Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
bool single_minus_longopt = false)
{
add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt);
}
//! @brief POSIX add() (gnu==false).
void add(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, //
bool single_minus_longopt = false)
{
add(false, usage, argc, argv, min_abbr_len, single_minus_longopt);
}
//! @brief POSIX add() (gnu==false) with non-const argv.
void add(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, //
bool single_minus_longopt = false)
{
add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt);
}
private:
class CountOptionsAction;
};
/**
* @brief Checks argument vectors for validity and parses them into data
* structures that are easier to work with.
*
* @par Example:
* @code
* int main(int argc, char* argv[])
* {
* argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
* option::Stats stats(usage, argc, argv);
* option::Option options[stats.options_max], buffer[stats.buffer_max];
* option::Parser parse(usage, argc, argv, options, buffer);
*
* if (parse.error())
* return 1;
*
* if (options[HELP])
* ...
* @endcode
*/
class Parser
{
int op_count; //!< @internal @brief see optionsCount()
int nonop_count; //!< @internal @brief see nonOptionsCount()
const char** nonop_args; //!< @internal @brief see nonOptions()
bool err; //!< @internal @brief see error()
public:
/**
* @brief Creates a new Parser.
*/
Parser() :
op_count(0), nonop_count(0), nonop_args(0), err(false)
{
}
/**
* @brief Creates a new Parser and immediately parses the given argument vector.
* @copydetails parse()
*/
Parser(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[],
int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) :
op_count(0), nonop_count(0), nonop_args(0), err(false)
{
parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
}
//! @brief Parser(...) with non-const argv.
Parser(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[],
int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) :
op_count(0), nonop_count(0), nonop_args(0), err(false)
{
parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
}
//! @brief POSIX Parser(...) (gnu==false).
Parser(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], int min_abbr_len = 0,
bool single_minus_longopt = false, int bufmax = -1) :
op_count(0), nonop_count(0), nonop_args(0), err(false)
{
parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
}
//! @brief POSIX Parser(...) (gnu==false) with non-const argv.
Parser(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0,
bool single_minus_longopt = false, int bufmax = -1) :
op_count(0), nonop_count(0), nonop_args(0), err(false)
{
parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
}
/**
* @brief Parses the given argument vector.
*
* @param gnu if true, parse() will not stop at the first non-option argument. Instead it will
* reorder arguments so that all non-options are at the end. This is the default behaviour
* of GNU getopt() but is not conforming to POSIX. @n
* Note, that once the argument vector has been reordered, the @c gnu flag will have
* no further effect on this argument vector. So it is enough to pass @c gnu==true when
* creating Stats.
* @param usage Array of Descriptor objects that describe the options to support. The last entry
* of this array must have 0 in all fields.
* @param argc The number of elements from @c argv that are to be parsed. If you pass -1, the number
* will be determined automatically. In that case the @c argv list must end with a NULL
* pointer.
* @param argv The arguments to be parsed. If you pass -1 as @c argc the last pointer in the @c argv
* list must be NULL to mark the end.
* @param options Each entry is the first element of a linked list of Options. Each new option
* that is parsed will be appended to the list specified by that Option's
* Descriptor::index. If an entry is not yet used (i.e. the Option is invalid),
* it will be replaced rather than appended to. @n
* The minimum length of this array is the greatest Descriptor::index value that
* occurs in @c usage @e PLUS ONE.
* @param buffer Each argument that is successfully parsed (including unknown arguments, if they
* have a Descriptor whose CheckArg does not return @ref ARG_ILLEGAL) will be stored in this
* array. parse() scans the array for the first invalid entry and begins writing at that
* index. You can pass @c bufmax to limit the number of options stored.
* @param min_abbr_len Passing a value <code> min_abbr_len > 0 </code> enables abbreviated long
* options. The parser will match a prefix of a long option as if it was
* the full long option (e.g. @c --foob=10 will be interpreted as if it was
* @c --foobar=10 ), as long as the prefix has at least @c min_abbr_len characters
* (not counting the @c -- ) and is unambiguous.
* @n Be careful if combining @c min_abbr_len=1 with @c single_minus_longopt=true
* because the ambiguity check does not consider short options and abbreviated
* single minus long options will take precedence over short options.
* @param single_minus_longopt Passing @c true for this option allows long options to begin with
* a single minus. The double minus form will still be recognized. Note that
* single minus long options take precedence over short options and short option
* groups. E.g. @c -file would be interpreted as @c --file and not as
* <code> -f -i -l -e </code> (assuming a long option named @c "file" exists).
* @param bufmax The greatest index in the @c buffer[] array that parse() will write to is
* @c bufmax-1. If there are more options, they will be processed (in particular
* their CheckArg will be called) but not stored. @n
* If you used Stats::buffer_max to dimension this array, you can pass
* -1 (or not pass @c bufmax at all) which tells parse() that the buffer is
* "large enough".
* @attention
* Remember that @c options and @c buffer store Option @e objects, not pointers. Therefore it
* is not possible for the same object to be in both arrays. For those options that are found in
* both @c buffer[] and @c options[] the respective objects are independent copies. And only the
* objects in @c options[] are properly linked via Option::next() and Option::prev().
* You can iterate over @c buffer[] to
* process all options in the order they appear in the argument vector, but if you want access to
* the other Options with the same Descriptor::index, then you @e must access the linked list via
* @c options[]. You can get the linked list in options from a buffer object via something like
* @c options[buffer[i].index()].
*/
void parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[],
int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1);
//! @brief parse() with non-const argv.
void parse(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[],
int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1)
{
parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
}
//! @brief POSIX parse() (gnu==false).
void parse(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[],
int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1)
{
parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
}
//! @brief POSIX parse() (gnu==false) with non-const argv.
void parse(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0,
bool single_minus_longopt = false, int bufmax = -1)
{
parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
}
/**
* @brief Returns the number of valid Option objects in @c buffer[].
*
* @note
* @li The returned value always reflects the number of Options in the buffer[] array used for
* the most recent call to parse().
* @li The count (and the buffer[]) includes unknown options if they are collected
* (see Descriptor::longopt).
*/
int optionsCount()
{
return op_count;
}
/**
* @brief Returns the number of non-option arguments that remained at the end of the
* most recent parse() that actually encountered non-option arguments.
*
* @note
* A parse() that does not encounter non-option arguments will leave this value
* as well as nonOptions() undisturbed. This means you can feed the Parser a
* default argument vector that contains non-option arguments (e.g. a default filename).
* Then you feed it the actual arguments from the user. If the user has supplied at
* least one non-option argument, all of the non-option arguments from the default
* disappear and are replaced by the user's non-option arguments. However, if the
* user does not supply any non-option arguments the defaults will still be in
* effect.
*/
int nonOptionsCount()
{
return nonop_count;
}
/**
* @brief Returns a pointer to an array of non-option arguments (only valid
* if <code>nonOptionsCount() >0 </code>).
*
* @note
* @li parse() does not copy arguments, so this pointer points into the actual argument
* vector as passed to parse().
* @li As explained at nonOptionsCount() this pointer is only changed by parse() calls
* that actually encounter non-option arguments. A parse() call that encounters only
* options, will not change nonOptions().
*/
const char** nonOptions()
{
return nonop_args;
}
/**
* @brief Returns <b><code>nonOptions()[i]</code></b> (@e without checking if i is in range!).
*/
const char* nonOption(int i)
{
return nonOptions()[i];
}
/**
* @brief Returns @c true if an unrecoverable error occurred while parsing options.
*
* An illegal argument to an option (i.e. CheckArg returns @ref ARG_ILLEGAL) is an
* unrecoverable error that aborts the parse. Unknown options are only an error if
* their CheckArg function returns @ref ARG_ILLEGAL. Otherwise they are collected.
* In that case if you want to exit the program if either an illegal argument
* or an unknown option has been passed, use code like this
*
* @code
* if (parser.error() || options[UNKNOWN])
* exit(1);
* @endcode
*
*/
bool error()
{
return err;
}
private:
friend struct Stats;
class StoreOptionAction;
struct Action;
/**
* @internal
* @brief This is the core function that does all the parsing.
* @retval false iff an unrecoverable error occurred.
*/
static bool workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action,
bool single_minus_longopt, bool print_errors, int min_abbr_len);
/**
* @internal
* @brief Returns true iff @c st1 is a prefix of @c st2 and
* in case @c st2 is longer than @c st1, then
* the first additional character is '='.
*
* @par Examples:
* @code
* streq("foo", "foo=bar") == true
* streq("foo", "foobar") == false
* streq("foo", "foo") == true
* streq("foo=bar", "foo") == false
* @endcode
*/
static bool streq(const char* st1, const char* st2)
{
while (*st1 != 0)
if (*st1++ != *st2++)
return false;
return (*st2 == 0 || *st2 == '=');
}
/**
* @internal
* @brief Like streq() but handles abbreviations.
*
* Returns true iff @c st1 and @c st2 have a common
* prefix with the following properties:
* @li (if min > 0) its length is at least @c min characters or the same length as @c st1 (whichever is smaller).
* @li (if min <= 0) its length is the same as that of @c st1
* @li within @c st2 the character following the common prefix is either '=' or end-of-string.
*
* Examples:
* @code
* streqabbr("foo", "foo=bar",<anything>) == true
* streqabbr("foo", "fo=bar" , 2) == true
* streqabbr("foo", "fo" , 2) == true
* streqabbr("foo", "fo" , 0) == false
* streqabbr("foo", "f=bar" , 2) == false
* streqabbr("foo", "f" , 2) == false
* streqabbr("fo" , "foo=bar",<anything>) == false
* streqabbr("foo", "foobar" ,<anything>) == false
* streqabbr("foo", "fobar" ,<anything>) == false
* streqabbr("foo", "foo" ,<anything>) == true
* @endcode
*/
static bool streqabbr(const char* st1, const char* st2, long long min)
{
const char* st1start = st1;
while (*st1 != 0 && (*st1 == *st2))
{
++st1;
++st2;
}
return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '=');
}
/**
* @internal
* @brief Returns true iff character @c ch is contained in the string @c st.
*
* Returns @c true for @c ch==0 .
*/
static bool instr(char ch, const char* st)
{
while (*st != 0 && *st != ch)
++st;
return *st == ch;
}
/**
* @internal
* @brief Rotates <code>args[-count],...,args[-1],args[0]</code> to become
* <code>args[0],args[-count],...,args[-1]</code>.
*/
static void shift(const char** args, int count)
{
for (int i = 0; i > -count; --i)
{
const char* temp = args[i];
args[i] = args[i - 1];
args[i - 1] = temp;
}
}
};
/**
* @internal
* @brief Interface for actions Parser::workhorse() should perform for each Option it
* parses.
*/
struct Parser::Action
{
/**
* @brief Called by Parser::workhorse() for each Option that has been successfully
* parsed (including unknown
* options if they have a Descriptor whose Descriptor::check_arg does not return
* @ref ARG_ILLEGAL.
*
* Returns @c false iff a fatal error has occured and the parse should be aborted.
*/
virtual bool perform(Option&)
{
return true;
}
/**
* @brief Called by Parser::workhorse() after finishing the parse.
* @param numargs the number of non-option arguments remaining
* @param args pointer to the first remaining non-option argument (if numargs > 0).
*
* @return
* @c false iff a fatal error has occurred.
*/
virtual bool finished(int numargs, const char** args)
{
(void) numargs;
(void) args;
return true;
}
};
/**
* @internal
* @brief An Action to pass to Parser::workhorse() that will increment a counter for
* each parsed Option.
*/
class Stats::CountOptionsAction: public Parser::Action
{
unsigned* buffer_max;
public:
/**
* Creates a new CountOptionsAction that will increase @c *buffer_max_ for each
* parsed Option.
*/
CountOptionsAction(unsigned* buffer_max_) :
buffer_max(buffer_max_)
{
}
bool perform(Option&)
{
if (*buffer_max == 0x7fffffff)
return false; // overflow protection: don't accept number of options that doesn't fit signed int
++*buffer_max;
return true;
}
};
/**
* @internal
* @brief An Action to pass to Parser::workhorse() that will store each parsed Option in
* appropriate arrays (see Parser::parse()).
*/
class Parser::StoreOptionAction: public Parser::Action
{
Parser& parser;
Option* options;
Option* buffer;
int bufmax; //! Number of slots in @c buffer. @c -1 means "large enough".
public:
/**
* @brief Creates a new StoreOption action.
* @param parser_ the parser whose op_count should be updated.
* @param options_ each Option @c o is chained into the linked list @c options_[o.desc->index]
* @param buffer_ each Option is appended to this array as long as there's a free slot.
* @param bufmax_ number of slots in @c buffer_. @c -1 means "large enough".
*/
StoreOptionAction(Parser& parser_, Option options_[], Option buffer_[], int bufmax_) :
parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_)
{
// find first empty slot in buffer (if any)
int bufidx = 0;
while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx])
++bufidx;
// set parser's optionCount
parser.op_count = bufidx;
}
bool perform(Option& option)
{
if (bufmax < 0 || parser.op_count < bufmax)
{
if (parser.op_count == 0x7fffffff)
return false; // overflow protection: don't accept number of options that doesn't fit signed int
buffer[parser.op_count] = option;
int idx = buffer[parser.op_count].desc->index;
if (options[idx])
options[idx].append(buffer[parser.op_count]);
else
options[idx] = buffer[parser.op_count];
++parser.op_count;
}
return true; // NOTE: an option that is discarded because of a full buffer is not fatal
}
bool finished(int numargs, const char** args)
{
// only overwrite non-option argument list if there's at least 1
// new non-option argument. Otherwise we keep the old list. This
// makes it easy to use default non-option arguments.
if (numargs > 0)
{
parser.nonop_count = numargs;
parser.nonop_args = args;
}
return true;
}
};
inline void Parser::parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[],
Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax)
{
StoreOptionAction action(*this, options, buffer, bufmax);
err = !workhorse(gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len);
}
inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len,
bool single_minus_longopt)
{
// determine size of options array. This is the greatest index used in the usage + 1
int i = 0;
while (usage[i].shortopt != 0)
{
if (usage[i].index + 1 >= options_max)
options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel
++i;
}
CountOptionsAction action(&buffer_max);
Parser::workhorse(gnu, usage, argc, argv, action, single_minus_longopt, false, min_abbr_len);
}
inline bool Parser::workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action,
bool single_minus_longopt, bool print_errors, int min_abbr_len)
{
// protect against NULL pointer
if (args == 0)
numargs = 0;
int nonops = 0;
while (numargs != 0 && *args != 0)
{
const char* param = *args; // param can be --long-option, -srto or non-option argument
// in POSIX mode the first non-option argument terminates the option list
// a lone minus character is a non-option argument
if (param[0] != '-' || param[1] == 0)
{
if (gnu)
{
++nonops;
++args;
if (numargs > 0)
--numargs;
continue;
}
else
break;
}
// -- terminates the option list. The -- itself is skipped.
if (param[1] == '-' && param[2] == 0)
{
shift(args, nonops);
++args;
if (numargs > 0)
--numargs;
break;
}
bool handle_short_options;
const char* longopt_name;
if (param[1] == '-') // if --long-option
{
handle_short_options = false;
longopt_name = param + 2;
}
else
{
handle_short_options = true;
longopt_name = param + 1; //for testing a potential -long-option
}
bool try_single_minus_longopt = single_minus_longopt;
bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid?
do // loop over short options in group, for long options the body is executed only once
{
int idx;
const char* optarg;
/******************** long option **********************/
if (handle_short_options == false || try_single_minus_longopt)
{
idx = 0;
while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name))
++idx;
if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options
{
int i1 = 0;
while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len))
++i1;
if (usage[i1].longopt != 0)
{ // now test if the match is unambiguous by checking for another match
int i2 = i1 + 1;
while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len))
++i2;
if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx
idx = i1;
}
}
// if we found something, disable handle_short_options (only relevant if single_minus_longopt)
if (usage[idx].longopt != 0)
handle_short_options = false;
try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group
optarg = longopt_name;
while (*optarg != 0 && *optarg != '=')
++optarg;
if (*optarg == '=') // attached argument
++optarg;
else
// possibly detached argument
optarg = (have_more_args ? args[1] : 0);
}
/************************ short option ***********************************/
if (handle_short_options)
{
if (*++param == 0) // point at the 1st/next option character
break; // end of short option group
idx = 0;
while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt))
++idx;
if (param[1] == 0) // if the potential argument is separate
optarg = (have_more_args ? args[1] : 0);
else
// if the potential argument is attached
optarg = param + 1;
}
const Descriptor* descriptor = &usage[idx];
if (descriptor->shortopt == 0) /************** unknown option ********************/
{
// look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options
idx = 0;
while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0))
++idx;
descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]);
}
if (descriptor != 0)
{
Option option(descriptor, param, optarg);
switch (descriptor->check_arg(option, print_errors))
{
case ARG_ILLEGAL:
return false; // fatal
case ARG_OK:
// skip one element of the argument vector, if it's a separated argument
if (optarg != 0 && have_more_args && optarg == args[1])
{
shift(args, nonops);
if (numargs > 0)
--numargs;
++args;
}
// No further short options are possible after an argument
handle_short_options = false;
break;
case ARG_IGNORE:
case ARG_NONE:
option.arg = 0;
break;
}
if (!action.perform(option))
return false;
}
} while (handle_short_options);
shift(args, nonops);
++args;
if (numargs > 0)
--numargs;
} // while
if (numargs > 0 && *args == 0) // It's a bug in the caller if numargs is greater than the actual number
numargs = 0; // of arguments, but as a service to the user we fix this if we spot it.
if (numargs < 0) // if we don't know the number of remaining non-option arguments
{ // we need to count them
numargs = 0;
while (args[numargs] != 0)
++numargs;
}
return action.finished(numargs + nonops, args - nonops);
}
/**
* @internal
* @brief The implementation of option::printUsage().
*/
struct PrintUsageImplementation
{
/**
* @internal
* @brief Interface for Functors that write (part of) a string somewhere.
*/
struct IStringWriter
{
/**
* @brief Writes the given number of chars beginning at the given pointer somewhere.
*/
virtual void operator()(const char*, int)
{
}
};
/**
* @internal
* @brief Encapsulates a function with signature <code>func(string, size)</code> where
* string can be initialized with a const char* and size with an int.
*/
template<typename Function>
struct FunctionWriter: public IStringWriter
{
Function* write;
virtual void operator()(const char* str, int size)
{
(*write)(str, size);
}
FunctionWriter(Function* w) :
write(w)
{
}
};
/**
* @internal
* @brief Encapsulates a reference to an object with a <code>write(string, size)</code>
* method like that of @c std::ostream.
*/
template<typename OStream>
struct OStreamWriter: public IStringWriter
{
OStream& ostream;
virtual void operator()(const char* str, int size)
{
ostream.write(str, size);
}
OStreamWriter(OStream& o) :
ostream(o)
{
}
};
/**
* @internal
* @brief Like OStreamWriter but encapsulates a @c const reference, which is
* typically a temporary object of a user class.
*/
template<typename Temporary>
struct TemporaryWriter: public IStringWriter
{
const Temporary& userstream;
virtual void operator()(const char* str, int size)
{
userstream.write(str, size);
}
TemporaryWriter(const Temporary& u) :
userstream(u)
{
}
};
/**
* @internal
* @brief Encapsulates a function with the signature <code>func(fd, string, size)</code> (the
* signature of the @c write() system call)
* where fd can be initialized from an int, string from a const char* and size from an int.
*/
template<typename Syscall>
struct SyscallWriter: public IStringWriter
{
Syscall* write;
int fd;
virtual void operator()(const char* str, int size)
{
(*write)(fd, str, size);
}
SyscallWriter(Syscall* w, int f) :
write(w), fd(f)
{
}
};
/**
* @internal
* @brief Encapsulates a function with the same signature as @c std::fwrite().
*/
template<typename Function, typename Stream>
struct StreamWriter: public IStringWriter
{
Function* fwrite;
Stream* stream;
virtual void operator()(const char* str, int size)
{
(*fwrite)(str, size, 1, stream);
}
StreamWriter(Function* w, Stream* s) :
fwrite(w), stream(s)
{
}
};
/**
* @internal
* @brief Sets <code> i1 = max(i1, i2) </code>
*/
static void upmax(int& i1, int i2)
{
i1 = (i1 >= i2 ? i1 : i2);
}
/**
* @internal
* @brief Moves the "cursor" to column @c want_x assuming it is currently at column @c x
* and sets @c x=want_x .
* If <code> x > want_x </code>, a line break is output before indenting.
*
* @param write Spaces and possibly a line break are written via this functor to get
* the desired indentation @c want_x .
* @param[in,out] x the current indentation. Set to @c want_x by this method.
* @param want_x the desired indentation.
*/
static void indent(IStringWriter& write, int& x, int want_x)
{
int indent = want_x - x;
if (indent < 0)
{
write("\n", 1);
indent = want_x;
}
if (indent > 0)
{
char space = ' ';
for (int i = 0; i < indent; ++i)
write(&space, 1);
x = want_x;
}
}
/**
* @brief Returns true if ch is the unicode code point of a wide character.
*
* @note
* The following character ranges are treated as wide
* @code
* 1100..115F
* 2329..232A (just 2 characters!)
* 2E80..A4C6 except for 303F
* A960..A97C
* AC00..D7FB
* F900..FAFF
* FE10..FE6B
* FF01..FF60
* FFE0..FFE6
* 1B000......
* @endcode
*/
static bool isWideChar(unsigned ch)
{
if (ch == 0x303F)
return false;
return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6)
|| (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF)
|| (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6)
|| (0x1B000 <= ch));
}
/**
* @internal
* @brief Splits a @c Descriptor[] array into tables, rows, lines and columns and
* iterates over these components.
*
* The top-level organizational unit is the @e table.
* A table begins at a Descriptor with @c help!=NULL and extends up to
* a Descriptor with @c help==NULL.
*
* A table consists of @e rows. Due to line-wrapping and explicit breaks
* a row may take multiple lines on screen. Rows within the table are separated
* by \\n. They never cross Descriptor boundaries. This means a row ends either
* at \\n or the 0 at the end of the help string.
*
* A row consists of columns/cells. Columns/cells within a row are separated by \\t.
* Line breaks within a cell are marked by \\v.
*
* Rows in the same table need not have the same number of columns/cells. The
* extreme case are interjections, which are rows that contain neither \\t nor \\v.
* These are NOT treated specially by LinePartIterator, but they are treated
* specially by printUsage().
*
* LinePartIterator iterates through the usage at 3 levels: table, row and part.
* Tables and rows are as described above. A @e part is a line within a cell.
* LinePartIterator iterates through 1st parts of all cells, then through the 2nd
* parts of all cells (if any),... @n
* Example: The row <code> "1 \v 3 \t 2 \v 4" </code> has 2 cells/columns and 4 parts.
* The parts will be returned in the order 1, 2, 3, 4.
*
* It is possible that some cells have fewer parts than others. In this case
* LinePartIterator will "fill up" these cells with 0-length parts. IOW, LinePartIterator
* always returns the same number of parts for each column. Note that this is different
* from the way rows and columns are handled. LinePartIterator does @e not guarantee that
* the same number of columns will be returned for each row.
*
*/
class LinePartIterator
{
const Descriptor* tablestart; //!< The 1st descriptor of the current table.
const Descriptor* rowdesc; //!< The Descriptor that contains the current row.
const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help.
const char* ptr; //!< Ptr to current part within the current row.
int col; //!< Index of current column.
int len; //!< Length of the current part (that ptr points at) in BYTES
int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account).
int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs.
int line_in_block; //!< Line index within the current cell of the current part.
int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration.
bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell.
/**
* @brief Determines the byte and character lengths of the part at @ref ptr and
* stores them in @ref len and @ref screenlen respectively.
*/
void update_length()
{
screenlen = 0;
for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len)
{
++screenlen;
unsigned ch = (unsigned char) ptr[len];
if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte
{
// int __builtin_clz (unsigned int x)
// Returns the number of leading 0-bits in x, starting at the most significant bit
unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff);
ch = ch & mask; // mask out length bits, we don't verify their correctness
while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte
{
ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code
++len;
}
// ch is the decoded unicode code point
if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case
++screenlen;
}
}
}
public:
//! @brief Creates an iterator for @c usage.
LinePartIterator(const Descriptor usage[]) :
tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0),
target_line_in_block(0), hit_target_line(true)
{
}
/**
* @brief Moves iteration to the next table (if any). Has to be called once on a new
* LinePartIterator to move to the 1st table.
* @retval false if moving to next table failed because no further table exists.
*/
bool nextTable()
{
// If this is NOT the first time nextTable() is called after the constructor,
// then skip to the next table break (i.e. a Descriptor with help == 0)
if (rowdesc != 0)
{
while (tablestart->help != 0 && tablestart->shortopt != 0)
++tablestart;
}
// Find the next table after the break (if any)
while (tablestart->help == 0 && tablestart->shortopt != 0)
++tablestart;
restartTable();
return rowstart != 0;
}
/**
* @brief Reset iteration to the beginning of the current table.
*/
void restartTable()
{
rowdesc = tablestart;
rowstart = tablestart->help;
ptr = 0;
}
/**
* @brief Moves iteration to the next row (if any). Has to be called once after each call to
* @ref nextTable() to move to the 1st row of the table.
* @retval false if moving to next row failed because no further row exists.