argtable3.c 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. /*
  2. * SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. /*******************************************************************************
  7. * argtable3: Implements the main interfaces of the library
  8. *
  9. * This file is part of the argtable3 library.
  10. *
  11. * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
  12. * <sheitmann@users.sourceforge.net>
  13. * All rights reserved.
  14. *
  15. * Redistribution and use in source and binary forms, with or without
  16. * modification, are permitted provided that the following conditions are met:
  17. * * Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * * Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. * * Neither the name of STEWART HEITMANN nor the names of its contributors
  23. * may be used to endorse or promote products derived from this software
  24. * without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
  30. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  31. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  32. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  33. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. ******************************************************************************/
  37. #include "argtable3.h"
  38. #ifndef ARG_AMALGAMATION
  39. #include "argtable3_private.h"
  40. #if ARG_REPLACE_GETOPT == 1
  41. #include "arg_getopt.h"
  42. #else
  43. #include <getopt.h>
  44. #endif
  45. #else
  46. #if ARG_REPLACE_GETOPT == 0
  47. #include <getopt.h>
  48. #endif
  49. #endif
  50. #ifdef _WIN32
  51. #define WIN32_LEAN_AND_MEAN
  52. #include <windows.h>
  53. #undef WIN32_LEAN_AND_MEAN
  54. #endif
  55. #include <assert.h>
  56. #include <ctype.h>
  57. #include <limits.h>
  58. #include <stdlib.h>
  59. #include <string.h>
  60. static void arg_register_error(struct arg_end* end, void* parent, int error, const char* argval) {
  61. /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */
  62. if (end->count < end->hdr.maxcount) {
  63. end->error[end->count] = error;
  64. end->parent[end->count] = parent;
  65. end->argval[end->count] = argval;
  66. end->count++;
  67. } else {
  68. end->error[end->hdr.maxcount - 1] = ARG_ELIMIT;
  69. end->parent[end->hdr.maxcount - 1] = end;
  70. end->argval[end->hdr.maxcount - 1] = NULL;
  71. }
  72. }
  73. /*
  74. * Return index of first table entry with a matching short option
  75. * or -1 if no match was found.
  76. */
  77. static int find_shortoption(struct arg_hdr** table, char shortopt) {
  78. int tabindex;
  79. for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  80. if (table[tabindex]->shortopts && strchr(table[tabindex]->shortopts, shortopt))
  81. return tabindex;
  82. }
  83. return -1;
  84. }
  85. struct longoptions {
  86. int getoptval;
  87. int noptions;
  88. struct option* options;
  89. };
  90. #if 0
  91. static
  92. void dump_longoptions(struct longoptions * longoptions)
  93. {
  94. int i;
  95. printf("getoptval = %d\n", longoptions->getoptval);
  96. printf("noptions = %d\n", longoptions->noptions);
  97. for (i = 0; i < longoptions->noptions; i++)
  98. {
  99. printf("options[%d].name = \"%s\"\n",
  100. i,
  101. longoptions->options[i].name);
  102. printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg);
  103. printf("options[%d].flag = %p\n", i, longoptions->options[i].flag);
  104. printf("options[%d].val = %d\n", i, longoptions->options[i].val);
  105. }
  106. }
  107. #endif
  108. static struct longoptions* alloc_longoptions(struct arg_hdr** table) {
  109. struct longoptions* result;
  110. size_t nbytes;
  111. int noptions = 1;
  112. size_t longoptlen = 0;
  113. int tabindex;
  114. int option_index = 0;
  115. char* store;
  116. /*
  117. * Determine the total number of option structs required
  118. * by counting the number of comma separated long options
  119. * in all table entries and return the count in noptions.
  120. * note: noptions starts at 1 not 0 because we getoptlong
  121. * requires a NULL option entry to terminate the option array.
  122. * While we are at it, count the number of chars required
  123. * to store private copies of all the longoption strings
  124. * and return that count in logoptlen.
  125. */
  126. tabindex = 0;
  127. do {
  128. const char* longopts = table[tabindex]->longopts;
  129. longoptlen += (longopts ? strlen(longopts) : 0) + 1;
  130. while (longopts) {
  131. noptions++;
  132. longopts = strchr(longopts + 1, ',');
  133. }
  134. } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
  135. /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/
  136. /* allocate storage for return data structure as: */
  137. /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */
  138. nbytes = sizeof(struct longoptions) + sizeof(struct option) * (size_t)noptions + longoptlen;
  139. result = (struct longoptions*)xmalloc(nbytes);
  140. result->getoptval = 0;
  141. result->noptions = noptions;
  142. result->options = (struct option*)(result + 1);
  143. store = (char*)(result->options + noptions);
  144. for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  145. const char* longopts = table[tabindex]->longopts;
  146. while (longopts && *longopts) {
  147. char* storestart = store;
  148. /* copy progressive longopt strings into the store */
  149. while (*longopts != 0 && *longopts != ',')
  150. *store++ = *longopts++;
  151. *store++ = 0;
  152. if (*longopts == ',')
  153. longopts++;
  154. /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/
  155. result->options[option_index].name = storestart;
  156. result->options[option_index].flag = &(result->getoptval);
  157. result->options[option_index].val = tabindex;
  158. if (table[tabindex]->flag & ARG_HASOPTVALUE)
  159. result->options[option_index].has_arg = 2;
  160. else if (table[tabindex]->flag & ARG_HASVALUE)
  161. result->options[option_index].has_arg = 1;
  162. else
  163. result->options[option_index].has_arg = 0;
  164. option_index++;
  165. }
  166. }
  167. /* terminate the options array with a zero-filled entry */
  168. result->options[option_index].name = 0;
  169. result->options[option_index].has_arg = 0;
  170. result->options[option_index].flag = 0;
  171. result->options[option_index].val = 0;
  172. /*dump_longoptions(result);*/
  173. return result;
  174. }
  175. static char* alloc_shortoptions(struct arg_hdr** table) {
  176. char* result;
  177. size_t len = 2;
  178. int tabindex;
  179. char* res;
  180. /* determine the total number of option chars required */
  181. for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  182. struct arg_hdr* hdr = table[tabindex];
  183. len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0);
  184. }
  185. result = xmalloc(len);
  186. res = result;
  187. /* add a leading ':' so getopt return codes distinguish */
  188. /* unrecognised option and options missing argument values */
  189. *res++ = ':';
  190. for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  191. struct arg_hdr* hdr = table[tabindex];
  192. const char* shortopts = hdr->shortopts;
  193. while (shortopts && *shortopts) {
  194. *res++ = *shortopts++;
  195. if (hdr->flag & ARG_HASVALUE)
  196. *res++ = ':';
  197. if (hdr->flag & ARG_HASOPTVALUE)
  198. *res++ = ':';
  199. }
  200. }
  201. /* null terminate the string */
  202. *res = 0;
  203. /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/
  204. return result;
  205. }
  206. /* return index of the table terminator entry */
  207. static int arg_endindex(struct arg_hdr** table) {
  208. int tabindex = 0;
  209. while (!(table[tabindex]->flag & ARG_TERMINATOR))
  210. tabindex++;
  211. return tabindex;
  212. }
  213. static void arg_parse_tagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) {
  214. struct longoptions* longoptions;
  215. char* shortoptions;
  216. int copt;
  217. /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
  218. /* allocate short and long option arrays for the given opttable[]. */
  219. /* if the allocs fail then put an error msg in the last table entry. */
  220. longoptions = alloc_longoptions(table);
  221. shortoptions = alloc_shortoptions(table);
  222. /*dump_longoptions(longoptions);*/
  223. /* reset getopts internal option-index to zero, and disable error reporting */
  224. optind = 0;
  225. opterr = 0;
  226. /* fetch and process args using getopt_long */
  227. #ifdef ARG_LONG_ONLY
  228. while ((copt = getopt_long_only(argc, argv, shortoptions, longoptions->options, NULL)) != -1) {
  229. #else
  230. while ((copt = getopt_long(argc, argv, shortoptions, longoptions->options, NULL)) != -1) {
  231. #endif
  232. /*
  233. printf("optarg='%s'\n",optarg);
  234. printf("optind=%d\n",optind);
  235. printf("copt=%c\n",(char)copt);
  236. printf("optopt=%c (%d)\n",optopt, (int)(optopt));
  237. */
  238. switch (copt) {
  239. case 0: {
  240. int tabindex = longoptions->getoptval;
  241. void* parent = table[tabindex]->parent;
  242. /*printf("long option detected from argtable[%d]\n", tabindex);*/
  243. if (optarg && optarg[0] == 0 && (table[tabindex]->flag & ARG_HASVALUE)) {
  244. /* printf(": long option %s requires an argument\n",argv[optind-1]); */
  245. arg_register_error(endtable, endtable, ARG_EMISSARG, argv[optind - 1]);
  246. /* continue to scan the (empty) argument value to enforce argument count checking */
  247. }
  248. if (table[tabindex]->scanfn) {
  249. int errorcode = table[tabindex]->scanfn(parent, optarg);
  250. if (errorcode != 0)
  251. arg_register_error(endtable, parent, errorcode, optarg);
  252. }
  253. } break;
  254. case '?':
  255. /*
  256. * getopt_long() found an unrecognised short option.
  257. * if it was a short option its value is in optopt
  258. * if it was a long option then optopt=0
  259. */
  260. switch (optopt) {
  261. case 0:
  262. /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/
  263. arg_register_error(endtable, endtable, ARG_ELONGOPT, argv[optind - 1]);
  264. break;
  265. default:
  266. /*printf("?* unrecognised short option '%c'\n",optopt);*/
  267. arg_register_error(endtable, endtable, optopt, NULL);
  268. break;
  269. }
  270. break;
  271. case ':':
  272. /*
  273. * getopt_long() found an option with its argument missing.
  274. */
  275. /*printf(": option %s requires an argument\n",argv[optind-1]); */
  276. arg_register_error(endtable, endtable, ARG_EMISSARG, argv[optind - 1]);
  277. break;
  278. default: {
  279. /* getopt_long() found a valid short option */
  280. int tabindex = find_shortoption(table, (char)copt);
  281. /*printf("short option detected from argtable[%d]\n", tabindex);*/
  282. if (tabindex == -1) {
  283. /* should never get here - but handle it just in case */
  284. /*printf("unrecognised short option %d\n",copt);*/
  285. arg_register_error(endtable, endtable, copt, NULL);
  286. } else {
  287. if (table[tabindex]->scanfn) {
  288. void* parent = table[tabindex]->parent;
  289. int errorcode = table[tabindex]->scanfn(parent, optarg);
  290. if (errorcode != 0)
  291. arg_register_error(endtable, parent, errorcode, optarg);
  292. }
  293. }
  294. break;
  295. }
  296. }
  297. }
  298. xfree(shortoptions);
  299. xfree(longoptions);
  300. }
  301. static void arg_parse_untagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) {
  302. int tabindex = 0;
  303. int errorlast = 0;
  304. const char* optarglast = NULL;
  305. void* parentlast = NULL;
  306. /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
  307. while (!(table[tabindex]->flag & ARG_TERMINATOR)) {
  308. void* parent;
  309. int errorcode;
  310. /* if we have exhausted our argv[optind] entries then we have finished */
  311. if (optind >= argc) {
  312. /*printf("arg_parse_untagged(): argv[] exhausted\n");*/
  313. return;
  314. }
  315. /* skip table entries with non-null long or short options (they are not untagged entries) */
  316. if (table[tabindex]->longopts || table[tabindex]->shortopts) {
  317. /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/
  318. tabindex++;
  319. continue;
  320. }
  321. /* skip table entries with NULL scanfn */
  322. if (!(table[tabindex]->scanfn)) {
  323. /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/
  324. tabindex++;
  325. continue;
  326. }
  327. /* attempt to scan the current argv[optind] with the current */
  328. /* table[tabindex] entry. If it succeeds then keep it, otherwise */
  329. /* try again with the next table[] entry. */
  330. parent = table[tabindex]->parent;
  331. errorcode = table[tabindex]->scanfn(parent, argv[optind]);
  332. if (errorcode == 0) {
  333. /* success, move onto next argv[optind] but stay with same table[tabindex] */
  334. /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/
  335. optind++;
  336. /* clear the last tentative error */
  337. errorlast = 0;
  338. } else {
  339. /* failure, try same argv[optind] with next table[tabindex] entry */
  340. /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/
  341. tabindex++;
  342. /* remember this as a tentative error we may wish to reinstate later */
  343. errorlast = errorcode;
  344. optarglast = argv[optind];
  345. parentlast = parent;
  346. }
  347. }
  348. /* if a tenative error still remains at this point then register it as a proper error */
  349. if (errorlast) {
  350. arg_register_error(endtable, parentlast, errorlast, optarglast);
  351. optind++;
  352. }
  353. /* only get here when not all argv[] entries were consumed */
  354. /* register an error for each unused argv[] entry */
  355. while (optind < argc) {
  356. /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/
  357. arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]);
  358. }
  359. return;
  360. }
  361. static void arg_parse_check(struct arg_hdr** table, struct arg_end* endtable) {
  362. int tabindex = 0;
  363. /* printf("arg_parse_check()\n"); */
  364. do {
  365. if (table[tabindex]->checkfn) {
  366. void* parent = table[tabindex]->parent;
  367. int errorcode = table[tabindex]->checkfn(parent);
  368. if (errorcode != 0)
  369. arg_register_error(endtable, parent, errorcode, NULL);
  370. }
  371. } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
  372. }
  373. static void arg_reset(void** argtable) {
  374. struct arg_hdr** table = (struct arg_hdr**)argtable;
  375. int tabindex = 0;
  376. /*printf("arg_reset(%p)\n",argtable);*/
  377. do {
  378. if (table[tabindex]->resetfn)
  379. table[tabindex]->resetfn(table[tabindex]->parent);
  380. } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
  381. }
  382. int arg_parse(int argc, char** argv, void** argtable) {
  383. struct arg_hdr** table = (struct arg_hdr**)argtable;
  384. struct arg_end* endtable;
  385. int endindex;
  386. char** argvcopy = NULL;
  387. int i;
  388. /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/
  389. /* reset any argtable data from previous invocations */
  390. arg_reset(argtable);
  391. /* locate the first end-of-table marker within the array */
  392. endindex = arg_endindex(table);
  393. endtable = (struct arg_end*)table[endindex];
  394. /* Special case of argc==0. This can occur on Texas Instruments DSP. */
  395. /* Failure to trap this case results in an unwanted NULL result from */
  396. /* the malloc for argvcopy (next code block). */
  397. if (argc == 0) {
  398. /* We must still perform post-parse checks despite the absence of command line arguments */
  399. arg_parse_check(table, endtable);
  400. /* Now we are finished */
  401. return endtable->count;
  402. }
  403. argvcopy = (char**)xmalloc(sizeof(char*) * (size_t)(argc + 1));
  404. /*
  405. Fill in the local copy of argv[]. We need a local copy
  406. because getopt rearranges argv[] which adversely affects
  407. susbsequent parsing attempts.
  408. */
  409. for (i = 0; i < argc; i++)
  410. argvcopy[i] = argv[i];
  411. argvcopy[argc] = NULL;
  412. /* parse the command line (local copy) for tagged options */
  413. arg_parse_tagged(argc, argvcopy, table, endtable);
  414. /* parse the command line (local copy) for untagged options */
  415. arg_parse_untagged(argc, argvcopy, table, endtable);
  416. /* if no errors so far then perform post-parse checks otherwise dont bother */
  417. if (endtable->count == 0)
  418. arg_parse_check(table, endtable);
  419. /* release the local copt of argv[] */
  420. xfree(argvcopy);
  421. return endtable->count;
  422. }
  423. /*
  424. * Concatenate contents of src[] string onto *pdest[] string.
  425. * The *pdest pointer is altered to point to the end of the
  426. * target string and *pndest is decremented by the same number
  427. * of chars.
  428. * Does not append more than *pndest chars into *pdest[]
  429. * so as to prevent buffer overruns.
  430. * Its something like strncat() but more efficient for repeated
  431. * calls on the same destination string.
  432. * Example of use:
  433. * char dest[30] = "good"
  434. * size_t ndest = sizeof(dest);
  435. * char *pdest = dest;
  436. * arg_char(&pdest,"bye ",&ndest);
  437. * arg_char(&pdest,"cruel ",&ndest);
  438. * arg_char(&pdest,"world!",&ndest);
  439. * Results in:
  440. * dest[] == "goodbye cruel world!"
  441. * ndest == 10
  442. */
  443. static void arg_cat(char** pdest, const char* src, size_t* pndest) {
  444. char* dest = *pdest;
  445. char* end = dest + *pndest;
  446. /*locate null terminator of dest string */
  447. while (dest < end && *dest != 0)
  448. dest++;
  449. /* concat src string to dest string */
  450. while (dest < end && *src != 0)
  451. *dest++ = *src++;
  452. /* null terminate dest string */
  453. *dest = 0;
  454. /* update *pdest and *pndest */
  455. *pndest = (size_t)(end - dest);
  456. *pdest = dest;
  457. }
  458. static void arg_cat_option(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue) {
  459. if (shortopts) {
  460. char option[3];
  461. /* note: option array[] is initialiazed dynamically here to satisfy */
  462. /* a deficiency in the watcom compiler wrt static array initializers. */
  463. option[0] = '-';
  464. option[1] = shortopts[0];
  465. option[2] = 0;
  466. arg_cat(&dest, option, &ndest);
  467. if (datatype) {
  468. arg_cat(&dest, " ", &ndest);
  469. if (optvalue) {
  470. arg_cat(&dest, "[", &ndest);
  471. arg_cat(&dest, datatype, &ndest);
  472. arg_cat(&dest, "]", &ndest);
  473. } else
  474. arg_cat(&dest, datatype, &ndest);
  475. }
  476. } else if (longopts) {
  477. size_t ncspn;
  478. /* add "--" tag prefix */
  479. arg_cat(&dest, "--", &ndest);
  480. /* add comma separated option tag */
  481. ncspn = strcspn(longopts, ",");
  482. #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__))
  483. strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest);
  484. #else
  485. strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest);
  486. #endif
  487. if (datatype) {
  488. arg_cat(&dest, "=", &ndest);
  489. if (optvalue) {
  490. arg_cat(&dest, "[", &ndest);
  491. arg_cat(&dest, datatype, &ndest);
  492. arg_cat(&dest, "]", &ndest);
  493. } else
  494. arg_cat(&dest, datatype, &ndest);
  495. }
  496. } else if (datatype) {
  497. if (optvalue) {
  498. arg_cat(&dest, "[", &ndest);
  499. arg_cat(&dest, datatype, &ndest);
  500. arg_cat(&dest, "]", &ndest);
  501. } else
  502. arg_cat(&dest, datatype, &ndest);
  503. }
  504. }
  505. static void arg_cat_optionv(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue, const char* separator) {
  506. separator = separator ? separator : "";
  507. if (shortopts) {
  508. const char* c = shortopts;
  509. while (*c) {
  510. /* "-a|-b|-c" */
  511. char shortopt[3];
  512. /* note: shortopt array[] is initialiazed dynamically here to satisfy */
  513. /* a deficiency in the watcom compiler wrt static array initializers. */
  514. shortopt[0] = '-';
  515. shortopt[1] = *c;
  516. shortopt[2] = 0;
  517. arg_cat(&dest, shortopt, &ndest);
  518. if (*++c)
  519. arg_cat(&dest, separator, &ndest);
  520. }
  521. }
  522. /* put separator between long opts and short opts */
  523. if (shortopts && longopts)
  524. arg_cat(&dest, separator, &ndest);
  525. if (longopts) {
  526. const char* c = longopts;
  527. while (*c) {
  528. size_t ncspn;
  529. /* add "--" tag prefix */
  530. arg_cat(&dest, "--", &ndest);
  531. /* add comma separated option tag */
  532. ncspn = strcspn(c, ",");
  533. #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__))
  534. strncat_s(dest, ndest, c, (ncspn < ndest) ? ncspn : ndest);
  535. #else
  536. strncat(dest, c, (ncspn < ndest) ? ncspn : ndest);
  537. #endif
  538. c += ncspn;
  539. /* add given separator in place of comma */
  540. if (*c == ',') {
  541. arg_cat(&dest, separator, &ndest);
  542. c++;
  543. }
  544. }
  545. }
  546. if (datatype) {
  547. if (longopts)
  548. arg_cat(&dest, "=", &ndest);
  549. else if (shortopts)
  550. arg_cat(&dest, " ", &ndest);
  551. if (optvalue) {
  552. arg_cat(&dest, "[", &ndest);
  553. arg_cat(&dest, datatype, &ndest);
  554. arg_cat(&dest, "]", &ndest);
  555. } else
  556. arg_cat(&dest, datatype, &ndest);
  557. }
  558. }
  559. void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix) {
  560. char syntax[200] = "";
  561. suffix = suffix ? suffix : "";
  562. /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */
  563. arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, 0, "|");
  564. arg_dstr_cat(ds, syntax);
  565. arg_dstr_cat(ds, (char*)suffix);
  566. }
  567. /* this function should be deprecated because it doesn't consider optional argument values (ARG_HASOPTVALUE) */
  568. void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix) {
  569. arg_dstr_t ds = arg_dstr_create();
  570. arg_print_option_ds(ds, shortopts, longopts, datatype, suffix);
  571. fputs(arg_dstr_cstr(ds), fp);
  572. arg_dstr_destroy(ds);
  573. }
  574. /*
  575. * Print a GNU style [OPTION] string in which all short options that
  576. * do not take argument values are presented in abbreviated form, as
  577. * in: -xvfsd, or -xvf[sd], or [-xvsfd]
  578. */
  579. static void arg_print_gnuswitch_ds(arg_dstr_t ds, struct arg_hdr** table) {
  580. int tabindex;
  581. const char* format1 = " -%c";
  582. const char* format2 = " [-%c";
  583. const char* suffix = "";
  584. /* print all mandatory switches that are without argument values */
  585. for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  586. /* skip optional options */
  587. if (table[tabindex]->mincount < 1)
  588. continue;
  589. /* skip non-short options */
  590. if (table[tabindex]->shortopts == NULL)
  591. continue;
  592. /* skip options that take argument values */
  593. if (table[tabindex]->flag & ARG_HASVALUE)
  594. continue;
  595. /* print the short option (only the first short option char, ignore multiple choices)*/
  596. arg_dstr_catf(ds, format1, table[tabindex]->shortopts[0]);
  597. format1 = "%c";
  598. format2 = "[%c";
  599. }
  600. /* print all optional switches that are without argument values */
  601. for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  602. /* skip mandatory args */
  603. if (table[tabindex]->mincount > 0)
  604. continue;
  605. /* skip args without short options */
  606. if (table[tabindex]->shortopts == NULL)
  607. continue;
  608. /* skip args with values */
  609. if (table[tabindex]->flag & ARG_HASVALUE)
  610. continue;
  611. /* print first short option */
  612. arg_dstr_catf(ds, format2, table[tabindex]->shortopts[0]);
  613. format2 = "%c";
  614. suffix = "]";
  615. }
  616. arg_dstr_catf(ds, "%s", suffix);
  617. }
  618. void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix) {
  619. struct arg_hdr** table = (struct arg_hdr**)argtable;
  620. int i, tabindex;
  621. /* print GNU style [OPTION] string */
  622. arg_print_gnuswitch_ds(ds, table);
  623. /* print remaining options in abbreviated style */
  624. for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  625. char syntax[200] = "";
  626. const char *shortopts, *longopts, *datatype;
  627. /* skip short options without arg values (they were printed by arg_print_gnu_switch) */
  628. if (table[tabindex]->shortopts && !(table[tabindex]->flag & ARG_HASVALUE))
  629. continue;
  630. shortopts = table[tabindex]->shortopts;
  631. longopts = table[tabindex]->longopts;
  632. datatype = table[tabindex]->datatype;
  633. arg_cat_option(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE);
  634. if (strlen(syntax) > 0) {
  635. /* print mandatory instances of this option */
  636. for (i = 0; i < table[tabindex]->mincount; i++) {
  637. arg_dstr_cat(ds, " ");
  638. arg_dstr_cat(ds, syntax);
  639. }
  640. /* print optional instances enclosed in "[..]" */
  641. switch (table[tabindex]->maxcount - table[tabindex]->mincount) {
  642. case 0:
  643. break;
  644. case 1:
  645. arg_dstr_cat(ds, " [");
  646. arg_dstr_cat(ds, syntax);
  647. arg_dstr_cat(ds, "]");
  648. break;
  649. case 2:
  650. arg_dstr_cat(ds, " [");
  651. arg_dstr_cat(ds, syntax);
  652. arg_dstr_cat(ds, "]");
  653. arg_dstr_cat(ds, " [");
  654. arg_dstr_cat(ds, syntax);
  655. arg_dstr_cat(ds, "]");
  656. break;
  657. default:
  658. arg_dstr_cat(ds, " [");
  659. arg_dstr_cat(ds, syntax);
  660. arg_dstr_cat(ds, "]...");
  661. break;
  662. }
  663. }
  664. }
  665. if (suffix) {
  666. arg_dstr_cat(ds, (char*)suffix);
  667. }
  668. }
  669. void arg_print_syntax(FILE* fp, void** argtable, const char* suffix) {
  670. arg_dstr_t ds = arg_dstr_create();
  671. arg_print_syntax_ds(ds, argtable, suffix);
  672. fputs(arg_dstr_cstr(ds), fp);
  673. arg_dstr_destroy(ds);
  674. }
  675. void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix) {
  676. struct arg_hdr** table = (struct arg_hdr**)argtable;
  677. int i, tabindex;
  678. /* print remaining options in abbreviated style */
  679. for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  680. char syntax[200] = "";
  681. const char *shortopts, *longopts, *datatype;
  682. shortopts = table[tabindex]->shortopts;
  683. longopts = table[tabindex]->longopts;
  684. datatype = table[tabindex]->datatype;
  685. arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, "|");
  686. /* print mandatory options */
  687. for (i = 0; i < table[tabindex]->mincount; i++) {
  688. arg_dstr_cat(ds, " ");
  689. arg_dstr_cat(ds, syntax);
  690. }
  691. /* print optional args enclosed in "[..]" */
  692. switch (table[tabindex]->maxcount - table[tabindex]->mincount) {
  693. case 0:
  694. break;
  695. case 1:
  696. arg_dstr_cat(ds, " [");
  697. arg_dstr_cat(ds, syntax);
  698. arg_dstr_cat(ds, "]");
  699. break;
  700. case 2:
  701. arg_dstr_cat(ds, " [");
  702. arg_dstr_cat(ds, syntax);
  703. arg_dstr_cat(ds, "]");
  704. arg_dstr_cat(ds, " [");
  705. arg_dstr_cat(ds, syntax);
  706. arg_dstr_cat(ds, "]");
  707. break;
  708. default:
  709. arg_dstr_cat(ds, " [");
  710. arg_dstr_cat(ds, syntax);
  711. arg_dstr_cat(ds, "]...");
  712. break;
  713. }
  714. }
  715. if (suffix) {
  716. arg_dstr_cat(ds, (char*)suffix);
  717. }
  718. }
  719. void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix) {
  720. arg_dstr_t ds = arg_dstr_create();
  721. arg_print_syntaxv_ds(ds, argtable, suffix);
  722. fputs(arg_dstr_cstr(ds), fp);
  723. arg_dstr_destroy(ds);
  724. }
  725. void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format) {
  726. struct arg_hdr** table = (struct arg_hdr**)argtable;
  727. int tabindex;
  728. format = format ? format : " %-20s %s\n";
  729. for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  730. if (table[tabindex]->glossary) {
  731. char syntax[200] = "";
  732. const char* shortopts = table[tabindex]->shortopts;
  733. const char* longopts = table[tabindex]->longopts;
  734. const char* datatype = table[tabindex]->datatype;
  735. const char* glossary = table[tabindex]->glossary;
  736. arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, ", ");
  737. arg_dstr_catf(ds, format, syntax, glossary);
  738. }
  739. }
  740. }
  741. void arg_print_glossary(FILE* fp, void** argtable, const char* format) {
  742. arg_dstr_t ds = arg_dstr_create();
  743. arg_print_glossary_ds(ds, argtable, format);
  744. fputs(arg_dstr_cstr(ds), fp);
  745. arg_dstr_destroy(ds);
  746. }
  747. /**
  748. * Print a piece of text formatted, which means in a column with a
  749. * left and a right margin. The lines are wrapped at whitspaces next
  750. * to right margin. The function does not indent the first line, but
  751. * only the following ones.
  752. *
  753. * See description of arg_print_formatted below.
  754. */
  755. static void arg_print_formatted_ds(arg_dstr_t ds, const unsigned lmargin, const unsigned rmargin, const char* text) {
  756. const unsigned int textlen = (unsigned int)strlen(text);
  757. unsigned int line_start = 0;
  758. unsigned int line_end = textlen;
  759. const unsigned int colwidth = (rmargin - lmargin) + 1;
  760. assert(strlen(text) < UINT_MAX);
  761. /* Someone doesn't like us... */
  762. if (line_end < line_start) {
  763. arg_dstr_catf(ds, "%s\n", text);
  764. }
  765. while (line_end > line_start) {
  766. /* Eat leading white spaces. This is essential because while
  767. wrapping lines, there will often be a whitespace at beginning
  768. of line */
  769. while (isspace((int)(*(text + line_start)))) {
  770. line_start++;
  771. }
  772. /* Find last whitespace, that fits into line */
  773. if (line_end - line_start > colwidth) {
  774. line_end = line_start + colwidth;
  775. while ((line_end > line_start) && !isspace((int)(*(text + line_end)))) {
  776. line_end--;
  777. }
  778. /* Consume trailing spaces */
  779. while ((line_end > line_start) && isspace((int)(*(text + line_end)))) {
  780. line_end--;
  781. }
  782. /* Restore the last non-space character */
  783. line_end++;
  784. }
  785. /* Output line of text */
  786. while (line_start < line_end) {
  787. char c = *(text + line_start);
  788. arg_dstr_catc(ds, c);
  789. line_start++;
  790. }
  791. arg_dstr_cat(ds, "\n");
  792. /* Initialize another line */
  793. if (line_end < textlen) {
  794. unsigned i;
  795. for (i = 0; i < lmargin; i++) {
  796. arg_dstr_cat(ds, " ");
  797. }
  798. line_end = textlen;
  799. }
  800. } /* lines of text */
  801. }
  802. /**
  803. * Print a piece of text formatted, which means in a column with a
  804. * left and a right margin. The lines are wrapped at whitspaces next
  805. * to right margin. The function does not indent the first line, but
  806. * only the following ones.
  807. *
  808. * Example:
  809. * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." )
  810. * will result in the following output:
  811. *
  812. * Some
  813. * text
  814. * that
  815. * doesn'
  816. * t fit.
  817. *
  818. * Too long lines will be wrapped in the middle of a word.
  819. *
  820. * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." )
  821. * will result in the following output:
  822. *
  823. * Some
  824. * text
  825. * that
  826. * doesn'
  827. * t fit.
  828. *
  829. * As you see, the first line is not indented. This enables output of
  830. * lines, which start in a line where output already happened.
  831. *
  832. * Author: Uli Fouquet
  833. */
  834. void arg_print_formatted(FILE* fp, const unsigned lmargin, const unsigned rmargin, const char* text) {
  835. arg_dstr_t ds = arg_dstr_create();
  836. arg_print_formatted_ds(ds, lmargin, rmargin, text);
  837. fputs(arg_dstr_cstr(ds), fp);
  838. arg_dstr_destroy(ds);
  839. }
  840. /**
  841. * Prints the glossary in strict GNU format.
  842. * Differences to arg_print_glossary() are:
  843. * - wraps lines after 80 chars
  844. * - indents lines without shortops
  845. * - does not accept formatstrings
  846. *
  847. * Contributed by Uli Fouquet
  848. */
  849. void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable) {
  850. struct arg_hdr** table = (struct arg_hdr**)argtable;
  851. int tabindex;
  852. for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) {
  853. if (table[tabindex]->glossary) {
  854. char syntax[200] = "";
  855. const char* shortopts = table[tabindex]->shortopts;
  856. const char* longopts = table[tabindex]->longopts;
  857. const char* datatype = table[tabindex]->datatype;
  858. const char* glossary = table[tabindex]->glossary;
  859. if (!shortopts && longopts) {
  860. /* Indent trailing line by 4 spaces... */
  861. memset(syntax, ' ', 4);
  862. *(syntax + 4) = '\0';
  863. }
  864. arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, ", ");
  865. /* If syntax fits not into column, print glossary in new line... */
  866. if (strlen(syntax) > 25) {
  867. arg_dstr_catf(ds, " %-25s %s\n", syntax, "");
  868. *syntax = '\0';
  869. }
  870. arg_dstr_catf(ds, " %-25s ", syntax);
  871. arg_print_formatted_ds(ds, 28, 79, glossary);
  872. }
  873. } /* for each table entry */
  874. arg_dstr_cat(ds, "\n");
  875. }
  876. void arg_print_glossary_gnu(FILE* fp, void** argtable) {
  877. arg_dstr_t ds = arg_dstr_create();
  878. arg_print_glossary_gnu_ds(ds, argtable);
  879. fputs(arg_dstr_cstr(ds), fp);
  880. arg_dstr_destroy(ds);
  881. }
  882. /**
  883. * Checks the argtable[] array for NULL entries and returns 1
  884. * if any are found, zero otherwise.
  885. */
  886. int arg_nullcheck(void** argtable) {
  887. struct arg_hdr** table = (struct arg_hdr**)argtable;
  888. int tabindex;
  889. /*printf("arg_nullcheck(%p)\n",argtable);*/
  890. if (!table)
  891. return 1;
  892. tabindex = 0;
  893. do {
  894. /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/
  895. if (!table[tabindex])
  896. return 1;
  897. } while (!(table[tabindex++]->flag & ARG_TERMINATOR));
  898. return 0;
  899. }
  900. /*
  901. * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design.
  902. * The flaw results in memory leak in the (very rare) case that an intermediate
  903. * entry in the argtable array failed its memory allocation while others following
  904. * that entry were still allocated ok. Those subsequent allocations will not be
  905. * deallocated by arg_free().
  906. * Despite the unlikeliness of the problem occurring, and the even unlikelier event
  907. * that it has any deliterious effect, it is fixed regardless by replacing arg_free()
  908. * with the newer arg_freetable() function.
  909. * We still keep arg_free() for backwards compatibility.
  910. */
  911. void arg_free(void** argtable) {
  912. struct arg_hdr** table = (struct arg_hdr**)argtable;
  913. int tabindex = 0;
  914. int flag;
  915. /*printf("arg_free(%p)\n",argtable);*/
  916. do {
  917. /*
  918. if we encounter a NULL entry then somewhat incorrectly we presume
  919. we have come to the end of the array. It isnt strictly true because
  920. an intermediate entry could be NULL with other non-NULL entries to follow.
  921. The subsequent argtable entries would then not be freed as they should.
  922. */
  923. if (table[tabindex] == NULL)
  924. break;
  925. flag = table[tabindex]->flag;
  926. xfree(table[tabindex]);
  927. table[tabindex++] = NULL;
  928. } while (!(flag & ARG_TERMINATOR));
  929. }
  930. /* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */
  931. void arg_freetable(void** argtable, size_t n) {
  932. struct arg_hdr** table = (struct arg_hdr**)argtable;
  933. size_t tabindex = 0;
  934. /*printf("arg_freetable(%p)\n",argtable);*/
  935. for (tabindex = 0; tabindex < n; tabindex++) {
  936. if (table[tabindex] == NULL)
  937. continue;
  938. xfree(table[tabindex]);
  939. table[tabindex] = NULL;
  940. };
  941. }
  942. #ifdef _WIN32
  943. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
  944. return TRUE;
  945. UNREFERENCED_PARAMETER(hinstDLL);
  946. UNREFERENCED_PARAMETER(fdwReason);
  947. UNREFERENCED_PARAMETER(lpvReserved);
  948. }
  949. #endif