Advertisement
avp210159

argvopt.h argv[] parser like getopt_long/argp_parse

Nov 22nd, 2015
206
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 62.76 KB | None | 0 0
  1. /* avp 2015
  2.   av_getopt(), av_opt_help(), ... argvopt():  argv[] parser. TEMPLATE version
  3.  
  4.    Author
  5.    ------
  6.    Vasily Anishchenko (avp210159@mail.ru)
  7.  
  8.    Code is hereby licensed under the MIT Public License:
  9.  
  10.    Copyright (C) 2015 Vasily Anishchenko
  11.  
  12.    Permission is hereby granted, free of charge, to any person
  13.    obtaining a copy of this software and associated documentation
  14.    files (the "Software"), to deal in the Software without
  15.    restriction, including without limitation the rights to use,
  16.    copy, modify, merge, publish, distribute, sublicense, and/or sell
  17.    copies of the Software, and to permit persons to whom the
  18.    Software is furnished to do so, subject to the following
  19.    conditions:
  20.  
  21.    The above copyright notice and this permission notice shall be
  22.    included in all copies or substantial portions of the Software.
  23.  
  24.    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25.    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  26.    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27.    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  28.    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  29.    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  30.    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  31.    OTHER DEALINGS IN THE SOFTWARE.
  32.  
  33.    Данная лицензия разрешает, безвозмездно, лицам, получившим копию
  34.    данного программного обеспечения и сопутствующей документации
  35.    (в дальнейшем именуемыми "Программное Обеспечение"), использовать
  36.    Программное Обеспечение без ограничений, включая неограниченное право
  37.    на использование, копирование, изменение, объединение, публикацию,
  38.    распространение, сублицензирование и/или продажу копий Программного
  39.    Обеспечения, также как и лицам, которым предоставляется данное
  40.    Программное Обеспечение, при соблюдении следующих условий:
  41.  
  42.    Вышеупомянутый копирайт и данные условия должны быть включены
  43.    во все копии или значимые части данного Программного Обеспечения.
  44.  
  45.    ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ»,
  46.    БЕЗ ЛЮБОГО ВИДА ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ,
  47.    ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ,
  48.    СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И НЕНАРУШЕНИЯ ПРАВ.
  49.    НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ
  50.    ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ ИЛИ ДРУГИХ ТРЕБОВАНИЙ ПО
  51.    ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ
  52.    ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ
  53.    ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
  54.  
  55.  */
  56.  
  57. /*
  58.   Based on the known functions:  getopt_long() and argp_parse()
  59.  
  60.   Small example:
  61.  
  62. avp@avp-ubu1:examples$ gcc a-minitar.c -I.. && ./a.out --help
  63. Usage: (1) a.out -c,--create -f,--file=ARCHIVE [ -z -v,--verbose
  64.      -?,--help --usage -V,--version --Help-grp ]  FILE [FILE ...]
  65.     create archive
  66.   or : (2) a.out -t,--list [ -f,--file=ARCHIVE -z -v,--verbose
  67.      --wildcards[=TYPE] -?,--help --usage -V,--version --Help-grp ]
  68.       [FILE ...]
  69.     list archive
  70.   or : (3) a.out -x,--extract [ -f,--file=ARCHIVE -z -v,--verbose
  71.      --wildcards[=TYPE] -?,--help --usage -V,--version --Help-grp ]
  72.       [FILE ...]
  73.     extract files from archive
  74. command options (MUST use only one):
  75.   -c, --create                 create a new archive
  76.   -t, --list                   list the contents of an archive
  77.   -x, --extract                extract files from an archive
  78. other common options:
  79.   -f, --file ARCHIVE           use archive file or device ARCHIVE
  80.   -z                           try compress archive
  81.   -v, --verbose                verbosely list files processed
  82. list and extract specific options:
  83.       --wildcards[=TYPE]       use wildcards (default "Shell")
  84.  
  85.   -?, --help                   help text
  86.       --usage                  short reference
  87.   -V, --version                programm version
  88.       --Help-grp               help text (with option <groups-mask>)
  89.  
  90. Mandatory or optional arguments to long options are also mandatory or optional  for any corresponding short options.
  91.  
  92. Just little example of argvopt groups
  93. avp@avp-ubu1:examples$ cat a-minitar.c
  94. #include <argvopt.h>
  95.  
  96. struct user_opts {
  97.   char *archive;
  98.   char *wc_type;
  99.   int create, list, extract, verbose, compress;
  100. };
  101.  
  102. static int
  103. check_options (int key, char *arg, struct av_state *st)
  104. {
  105.   if (key != AV_KEY_FINI || st->tot_err)
  106.     return key;
  107.   struct user_opts *a = (typeof(a))st->glue;
  108.  
  109.   if (!(a->create | a->list | a->extract))
  110.     st->msg = strdup("You must specify one of the '-ctx' options");
  111.   else if (a->create) {
  112.     if (!a->archive)
  113.       st->msg = strdup("-f ARCHIVE required for --create option");
  114.     else if (!st->n_args)
  115.       st->msg = strdup("Cowardly refusing to create an empty archive");
  116.   }
  117.    
  118.   return st->msg ? AV_ANYERR : key;
  119. }
  120.  
  121. #define DEFWC "Shell"
  122.  
  123. static int
  124. wc_proc_opt (int key, char *arg, struct av_state *st, AV_DCLOPT_T *t)
  125. {
  126.   if (key == 1) {
  127.     if (!arg)
  128.       *(t->optarg_ptr) = (char *)DEFWC;
  129.     else if (!*arg)
  130.       *(t->optarg_ptr) = 0;
  131.   }
  132.   return key;
  133. }
  134.  
  135.  
  136. int
  137. main (int ac, char *av[])
  138. {
  139.   struct user_opts a = {0};
  140.   AV_DCLOPT_T dcl[] = {
  141.     {0, 0, "command options (MUST use only one):"},
  142.     {"create", 'c', "create a new archive", AV_MGRP1, &a.create},
  143.     {"list", 't', "list the contents of an archive", AV_MGRP2, &a.list},
  144.     {"extract", 'x', "extract files from an archive", AV_MGRP3, &a.extract},
  145.     {0, 0, "other common options:"},
  146.     {"file", 'f', "use archive file or device ARCHIVE\vARCHIVE",
  147.      AV_HASARG | AV_MND(1), 0, &a.archive},
  148.     {0, 'z', "try compress archive", 0, &a.compress},
  149.     {"verbose", 'v', "verbosely list files processed", 0, &a.verbose},
  150.     {0, 0, "list and extract specific options:"},
  151.     {"wildcards", 1, "use wildcards (default "_STR(DEFWC)")\vTYPE",
  152.      AV_OPTARG | AV_GRP2 | AV_GRP3, 0, &a.wc_type, wc_proc_opt},
  153.     {0, 0, " "},
  154.     {AV_HUV()},
  155.     {AV_HELP_GRP("")},
  156.     {0}
  157.   };
  158.   av_program_doc = "\vJust little example of argvopt groups";
  159.   av_args_doc = "\01 FILE [FILE ...]\n\tcreate archive"
  160.     "\02 [FILE ...]\n\tlist archive"
  161.     "\03 [FILE ...]\n\textract files from archive";
  162.   av_program_version = "minitar argvopt example 1.1";
  163.   av_wrap_pos = 58;
  164.   struct av_state *ap = argvopt(ac, av, dcl, 0,
  165.                 AV_ERRENDEXIT | AV_QUIET_EXIT,
  166.                 check_options, &a);
  167.   printf("Your choice: %s\n"
  168.      "archive: %s\n"
  169.      "verbose level: %d\n"
  170.      "with%s compression\n",
  171.      a.create ? "Create" : a.list ? "View" : "Extract",
  172.      a.archive ? : "< stdin",
  173.      a.verbose,
  174.      a.compress ? "" : "out");
  175.   if (a.wc_type) {
  176.     printf("use");
  177.     if (a.wc_type[0])
  178.       printf(" '%s'", a.wc_type);
  179.     puts(" wildcards in archive FILE names");
  180.   }
  181.   if (ap->n_args) {
  182.     printf("action performes for files:");
  183.     int i;
  184.     for (i = 0; i < ap->n_args || !puts(""); i++)
  185.       printf(" %s", ap->args[i]);
  186.   }
  187.  
  188.   return puts("End") == EOF;
  189. }
  190. avp@avp-ubu1:examples$
  191.  
  192.  
  193. */
  194.  
  195. /*
  196.   TODO
  197.  
  198. +-  check zopt
  199.   callbacks break loop -- args tail ?
  200.   fixed/variable chunks arrays
  201. +-  more user #ifdef's ?
  202. +-  test suit
  203.   doc
  204.  
  205.   DONE
  206. ++  move AV_LONGOPT_AMBIGUOUS AV_NOCOMPATOPT messages to av_errormsg
  207. ++  move compat check code to _av_getopt
  208. ++  move user global callback to av_getopt
  209. ++  change commom callback args to: key, arg, state (like in argp_parse())
  210. ++  move store args to av_getopt() before callbacks
  211. ++  no duplicates in .opts[], .eropts[], .args[] and order by index in argv[]
  212. ++  write check av[] type func() for callbacks
  213. ++  collect messages, do them available in user loop
  214.  
  215.   in LONGOPT_ONLY mode
  216. ++  ambigous -- stop reparsing av[i] as short opts, goto next
  217. ++  first unknown short opt -- stop parsing av[i], report - unrecogn for rest
  218.  
  219.  */
  220.  
  221. #ifndef _ARGVOPT_H
  222. #define _ARGVOPT_H
  223.  
  224. #include <stdio.h>
  225. #include <stdlib.h>
  226. #include <string.h>
  227. #include <ctype.h>
  228. #include <errno.h>
  229. #include <stdint.h>
  230. #include <limits.h>
  231. #include <stdarg.h>
  232.  
  233. #ifdef unix
  234. #include <sysexits.h>
  235. #else
  236. #define EX_USAGE 1
  237. #include <windows.h>
  238. #endif
  239.  
  240. #ifndef AV_DCLOPT_T
  241. #define AV_DCLOPT_T struct av_dclopt
  242. #define _AV_NEED_DCL
  243. struct av_dclopt; // forward declaration
  244. #endif
  245.  
  246. struct av_opt {
  247.   int optind,
  248.     optchr,
  249.     optlen,
  250.     dclindex,
  251.     argind,
  252.     argchr;
  253.   AV_DCLOPT_T *dcl;
  254.   char *arg;
  255. };
  256.  
  257. struct av_opterr {
  258.   char *errmsg;
  259.   int errcode;
  260.   struct av_opt opt;
  261. };
  262.  
  263. struct av_state;
  264. typedef  int (*ucallback_t)(int key, char *arg, struct av_state *state);
  265.  
  266. struct av_state {
  267.   // input fields
  268.   int argc;     // .argv[] length
  269.   char **argv;  // arguments vector
  270.   int flags;    // av_getopt() behavior
  271. // flags AV_STOP_NONOPT_ARG | getenv("POSIXLY_CORRECT") as +OPTS in getopt()
  272. #define AV_STOP_NONOPT_ARG 0x1  // return AV_END and copy remaining arguments
  273. #define AV_SILENT 0x2           // don't print error messages
  274. #define AV_CHECK_DCL 0x4        // for debug (if errors exit in _av_initcheck())
  275. #define AV_LONGOPT_ONLY 0x8     // --opt and -opt are the same (man getopt_long)
  276. #define AV_DIGIT_OPTION 0x10    // -1, -2.73e2 and similar are options too
  277. #define AV_PARSE_ARGV0 0x20     // argv[0] is argument too
  278. #define AV_ERREXIT 0x40         // argvopt() exit on first error
  279. #define AV_ERRBREAK 0x80        // argvopt() break parse on first error
  280. #define AV_ERRENDEXIT 0x100     // argvopt() exit after parsing if errors
  281. #define AV_QUIET_EXIT 0x200     // don't print usage messages on err exit
  282.  
  283.   AV_DCLOPT_T *dcl;       // current dcl[]
  284.   int dcl_itemsize;       // for external dcl[] functions purpose
  285.   void *glue;             // dcl[] user functions link area
  286.   ucallback_t u_callback; // user common callback
  287.   FILE *out;              // error messages stream (stderr if 0)
  288.  
  289.   // result fields
  290.   char **args;  // not option arguments list (dynamic, NULL - init indicated)
  291.   int *iargs;   // their indices
  292.   struct av_opt *opts;      // processed options ordered by optind,optchr
  293.   struct av_opterr *eropts; // error options ordered by optind,optchr
  294.   int n_args,   // current .args[] size
  295.     arg_tail,   // index in .args[] of not parsed .argv[] arguments
  296.     n_opts,     // number of processed options in .opts[]
  297.     n_err,      // number of error options in .eropts[]
  298.     tot_err,    // total errors counter with init, ... uvfun(AV_KEY_INI,...) and other
  299.     groups;     // bitmask of compatible options groups in .opts[]
  300.  
  301.   // current state of parsing
  302.   char *optarg, // parsed argument or 0
  303.     *optsrc;    // option field in .argv[i]  or 0
  304.    
  305.   int zopt,    // same as opt = av_getopt() result, but ... (see source code)
  306.   /*
  307.     we want that zopt is dcl[].key or 0 if returns ARG or AV_END (-1) if END
  308.     set AV_KEY_NONE at init
  309.    */
  310.     optind,   // .argv[optind] is next for parsing
  311.     optchr,   // offset of next char in argv[optind] (clear every step to next)
  312.     optlen,   // long option length (long option indicator)
  313.     optopt,   // 0 or short option value (may be unknown) or not changed
  314.     dclindex, // index in dcl_option[] if option found or -1
  315.     cur_argind, // index in argv[] of optarg or -1
  316.     cur_optind, // index in argv[] of option or -1
  317.     cur_optchr; // option character offset in .argv[.cur_optind] or -1
  318.   char *msg;    // all messages (errors) (separated by '\n')
  319.   int do_msg;   // callback control: 0 -- if rc < 0 av_getopt don't do message
  320. };
  321. // opt = av_getopt() returns av_dclopt.key or codes:
  322. #define AV_ARG 0
  323. #define AV_END -1
  324. #define _AV_DASHDASH -100
  325. #define _AV_OPTION -99
  326.  
  327. #define AV_LONGOPT_REQ_ARG -2  // option '--in' requires an argument
  328. #define AV_LONGOPT_NOTALLOW_ARG -3 // option '--help' doesn't allow an argument
  329. #define AV_LONGOPT_AMBIGUOUS -4    // option '--ar' is ambiguous; possibilities: '--arg' '--arg2' ...
  330. #define AV_NO_LONGOPT -5        // unrecognized option '--in'
  331. #define AV_NO_OPTION -6         // invalid option -- 'i'
  332. #define AV_OPTION_REQ_ARG -7    // option requires an argument -- 'i'
  333.  
  334. #define AV_NOCOMPATOPT -8       // option not compatible with other groups
  335.  
  336. #define AV_NOMEM -12
  337. #define AV_USAGE_ERROR -13      // fatal, nil state/dcl data
  338. #define AV_ARGV_ERROR -14       // fatal, bad ac/av[]
  339. #define AV_ANYERR -15           // some error callback return
  340. #define AV_BREAK -16            // not error callback break processing loop
  341.  
  342. #define AV_DEFVAR -20
  343. #define AV_DATAERR -21
  344.  
  345. #ifndef AV_NOUSEVAR
  346. // user function: may returns AV_END or AV_BREAK  for stop parsing
  347. typedef int (*optfunc_t)(int opt, char *arg,
  348.              struct av_state *state, AV_DCLOPT_T *thisdcl);
  349. #endif
  350. #define AV_KEY_SPEC  0x70000000 // first not user key
  351. #define AV_KEY_NONE  0x70000001
  352. #ifndef AV_USAGE_KEY
  353. #define AV_USAGE_KEY (AV_KEY_SPEC - 1) // used for --usage in AV_USAGE/AV_HUV
  354. #endif
  355. #ifndef AV_HELPG_KEY
  356. #define AV_HELPG_KEY (AV_KEY_SPEC - 2) // used for --Help-grp in AV_USAGE/AV_HUV
  357. #endif
  358. #ifndef AV_HELP_KEY
  359. #define AV_HELP_KEY ('?')           // used for --help in AV_USAGE/AV_HUV
  360. #endif
  361. #ifndef AV_VERSION_KEY
  362. #define AV_VERSION_KEY ('V')
  363. #endif
  364. #define AV_KEY_INIT  0x7fffffff
  365. #define AV_KEY_FINI  0x7ffffffe
  366.  
  367. #ifdef _AV_NEED_DCL
  368. // Minimal declaration. Used in parse/help argv[] functions
  369. // Should be first and continuous fields in any other declaration struct
  370. struct av_dclopt {
  371.   const char *name;   // long option
  372.   int key;            // short option if isgraph()
  373.                       // this field returns by av_getopt(), should be > 0
  374.   const char *descr;  // description text for help in format: TEXT\vARGNAME
  375.   int def_arg;        // HASARG + Groups + other up levels description bits
  376. #ifndef AV_NOUSEVAR   // set program variables to parsed options value
  377.   int *opt_counter;   // incr
  378.   char **optarg_ptr;  // optarg value
  379.   optfunc_t uvfun;    // function called after set variable
  380.   const void *arg1,   // 2 user arguments to function
  381.     *arg2;
  382.   char *processed;    // ptr to last successfully processed option in argv[]
  383. #endif
  384. };
  385. #undef _AV_NEED_DCL
  386. #endif // _AV_NEED_DCL
  387.  
  388. // struct dcl_option .def_arg  HASARG values
  389. #define AV_NOARG     0  // equal  no_argument from getopt.h
  390. #define AV_HASARG    1  // required_argument
  391. #define AV_OPTARG    2  // optional_argument
  392. #define AV_HASARG_MASK 0x3
  393.  
  394. /*
  395.   Option can belongs to one or more (now maximum 5) groups (numbers from 1 to 5)
  396.   Options that has no one common group are uncompatible.
  397.   Zero group option (option that is not in any group) is compatible with any
  398.   other options.
  399.   _av_getopt() checks options compatibility
  400.   av_help()/av_usage() groups compatible options together in Usage: lines
  401.  */
  402. #define _MAKEMASK(len,ofs) ( ((1 << (len)) - 1) << (ofs) )
  403. #define _GETMASKV(v,mask,ofs) (((v) & (mask)) >> (ofs))
  404. #define AV_GRP_OFS 3
  405. #define AV_GRP_LEN 5
  406. #define AV_GROUP_MASK _MAKEMASK(AV_GRP_LEN, AV_GRP_OFS)
  407. // macros for init  .def_arg (may be ored)
  408. #define AV_GRP(n) (1 << (AV_GRP_OFS + (((n) < AV_GRP_LEN) ? n : AV_GRP_LEN) - 1))
  409. #define AV_GRP1 AV_GRP(1)
  410. #define AV_GRP2 AV_GRP(2)
  411. #define AV_GRP3 AV_GRP(3)
  412. #define AV_GRP4 AV_GRP(4)
  413. #define AV_GRP5 AV_GRP(5)
  414. // macros for get groups value
  415. #define AV_VGROUPS(v) _GETMASKV((v), AV_GROUP_MASK, AV_GRP_OFS)
  416. #define AV_GROUPS(p) AV_VGROUPS((p)->def_arg)
  417.  
  418. // options mandatoring (now only for print) 5 bit (by groups -- allways==11111)
  419. #define AV_MND_OFS (AV_GRP_LEN + AV_GRP_OFS)
  420. #define AV_MND_MASK _MAKEMASK(AV_GRP_LEN, AV_MND_OFS)
  421. #define AV_VMANDAT(v) _GETMASKV((v), AV_MND_MASK, AV_MND_OFS)
  422. #define AV_MANDAT(p) AV_VMANDAT((p)->def_arg)
  423. #define AV_MND(n) (1 << (AV_MND_OFS + (((n) < AV_GRP_LEN) ? n : AV_GRP_LEN) - 1))
  424. #define AV_GMND(n) (AV_MND(n) | AV_GRP(n))
  425. #define AV_MNDTRY (((1 << AV_GRP_LEN) - 1) << AV_MND_OFS)
  426. #define AV_MGRP1 AV_GMND(1)
  427. #define AV_MGRP2 AV_GMND(2)
  428. #define AV_MGRP3 AV_GMND(3)
  429. #define AV_MGRP4 AV_GMND(4)
  430. #define AV_MGRP5 AV_GMND(5)
  431.  
  432. // typed value options
  433. #ifndef AV_NOUSEVAR
  434. #define AV_TYPE_LEN 10
  435. #define AV_TYPE_OFS  (AV_GRP_LEN + AV_MND_OFS)
  436. #define AV_TYPE_MASK _MAKEMASK(AV_TYPE_LEN, AV_TYPE_OFS)
  437. // .optarg_ptr types
  438. #define _AV_STR_T       0
  439. #define _AV_INT8_T      (0x1 << AV_TYPE_OFS)
  440. #define _AV_UINT8_T     (0x2 << AV_TYPE_OFS)
  441. #define _AV_INT16_T     (0x4 << AV_TYPE_OFS)
  442. #define _AV_UINT16_T    (0x8 << AV_TYPE_OFS)
  443. #define _AV_INT32_T     (0x10 << AV_TYPE_OFS)
  444. #define _AV_UINT32_T    (0x20 << AV_TYPE_OFS)
  445. #define _AV_INT64_T     (0x40 << AV_TYPE_OFS)
  446. #define _AV_UINT64_T    (0x80 << AV_TYPE_OFS)
  447. #define _AV_FLOAT_T     (0x100 << AV_TYPE_OFS)
  448. #define _AV_DOUBLE_T    (0x200 << AV_TYPE_OFS)
  449.  
  450. #define AV_STR       AV_HASARG
  451. #define AV_INT8      (_AV_INT8_T | AV_HASARG)
  452. #define AV_UINT8     (_AV_UINT8_T | AV_HASARG)
  453. #define AV_INT16     (_AV_INT16_T | AV_HASARG)
  454. #define AV_UINT16    (_AV_UINT16_T | AV_HASARG)
  455. #define AV_INT32     (_AV_INT32_T | AV_HASARG)
  456. #define AV_UINT32    (_AV_UINT32_T | AV_HASARG)
  457. #define AV_INT64     (_AV_INT64_T | AV_HASARG)
  458. #define AV_UINT64    (_AV_UINT64_T | AV_HASARG)
  459. #define AV_FLOAT     (_AV_FLOAT_T | AV_HASARG)
  460. #define AV_DOUBLE    (_AV_DOUBLE_T | AV_HASARG)
  461.  
  462. // array options
  463. #define AV_ISARRAY (1 << (AV_TYPE_LEN + AV_TYPE_OFS))
  464. #define AV_ARRAY (AV_HASARG | AV_ISARRAY)
  465. // array chunk (under costruction yet)
  466. #define AV_ARRAYS(n) (AV_ARRAY | ((n) << (AV_TYPE_LEN + AV_TYPE_OFS + 1)))
  467. #define AV_GETARRAYSZ(v) ((v) >> (AV_TYPE_LEN + AV_TYPE_OFS + 1))
  468.  
  469. #endif // AV_NOUSEVAR
  470.  
  471.  
  472. #define ISEMPTY_DCL(d) ({ typeof (d) _p = (d); \
  473.       _p ? (_p->name == 0 && _p->key == 0 && _p->descr == 0) : 1; })
  474.  
  475. #define __STR(t) #t
  476. #define _STR(t) __STR(t)
  477. #define NAME_DCLOPT_TYPE _STR(AV_DCLOPT_T)
  478.  
  479. #ifdef unix
  480. extern char *__progname;
  481. #else
  482. static const char *__progname = "???.exe";
  483. #endif
  484.  
  485. #ifndef AV_NOCODE
  486. // Smth like getopt_long() see: man 3 getopt
  487. // Returns av_dclopt.key or 0 (arg) or -1 (finish) or ERRCODE < -1
  488. // set user variables, call callbacks, make messages and so on
  489. static int av_getopt (struct av_state *st);
  490. // most internal argv parser, only returns code
  491. static int _av_getopt (struct av_state *st);
  492. // Smth like GNU argp_parse() see: http://www.gnu.org/software/libc/manual/html_node/Argp.html
  493. // Returns malloced struct av_state
  494. static struct av_state *
  495. argvopt (int ac, char *av[], AV_DCLOPT_T *dcl, FILE *out,
  496.      int flags, ucallback_t uf, void *glue);
  497. #define ARGP(ac, av, dcl, flags) argvopt(ac, av, dcl, 0, flags, 0, 0)
  498.  
  499. static void *av_state_free (struct av_state *st, int free_st);
  500.  
  501. static int av_match_names (char *avitem, AV_DCLOPT_T dcl[], int **res);
  502. static char *av_errormsg (struct av_state *st, int code);
  503.  
  504. static void
  505. av_opt_usage (AV_DCLOPT_T dcl[], const char *hmsg, const char *emsg, FILE *out);
  506. static int
  507. av_dcl_usage (int key, char *a, struct av_state *st, AV_DCLOPT_T *td);
  508. #ifndef AV_USAGE
  509. #define AV_USAGE(rc, ...) "usage", AV_USAGE_KEY, "short reference", 0, 0, (char **)rc, av_dcl_usage, ##__VA_ARGS__
  510. #endif
  511.  
  512. // full help (with usage()) without groups
  513. static void
  514. av_opt_help (AV_DCLOPT_T dcl[], const char *hmsg, const char *emsg, FILE *out);
  515. static int
  516. av_dcl_help (int key, char *a, struct av_state *st, AV_DCLOPT_T *td);
  517. #ifndef AV_HELP
  518. #define AV_HELP(rc, ...) \
  519.   "help",AV_HELP_KEY,"help text",0,0,(char **)rc,av_dcl_help,##__VA_ARGS__
  520. #endif
  521.  
  522. // full help (with usage()) with groups
  523. static int
  524. av_dcl_help1 (int key, char *a, struct av_state *st, AV_DCLOPT_T *td);
  525. #ifndef AV_HELP_GRP
  526. #define AV_HELP_GRP(rc, ...)                        \
  527.   "Help-grp",AV_HELPG_KEY,"help text (with option <groups-mask>)",0,0,(char **)rc,av_dcl_help1,##__VA_ARGS__
  528. #endif
  529.  
  530. // help without usage() and options groups info (only opt description list)
  531. static void
  532. av_opt_help2 (AV_DCLOPT_T *dcl, const char *beg_end_msg, const char *args_msg, FILE *out, int prigroups);
  533. static int
  534. av_dcl_help2 (int key, char *a, struct av_state *st, AV_DCLOPT_T *td);
  535.  
  536. // print version
  537. static int
  538. av_dcl_version (int key, char *a, struct av_state *st, AV_DCLOPT_T *td);
  539. #ifndef AV_VERSION
  540. #define AV_VERSION(rc) "version", AV_VERSION_KEY, "programm version", 0, 0, (char **)rc, av_dcl_version
  541. #endif
  542.  
  543. // dcl print help, usage & versin together
  544. #ifndef AV_HUV
  545. #define AV_HUV(...) AV_HELP("",##__VA_ARGS__)},{AV_USAGE("",##__VA_ARGS__)},{AV_VERSION("")
  546. #endif
  547. #ifndef AV_HUV_NOEX
  548. #define AV_HUV_NOEX(...) AV_HELP(0,##__VA_ARGS__)},{AV_USAGE(0,##__VA_ARGS__)},{AV_VERSION(0)
  549. #endif
  550.  
  551.  
  552. // for debug
  553. static void
  554. av_print_dcl (AV_DCLOPT_T *dp, FILE *out);
  555. static int
  556. av_check_dcl (AV_DCLOPT_T *dp, FILE *out); // for debug, exit if errors
  557.  
  558. static AV_DCLOPT_T *av_find_dcl (int key, struct av_state *st);
  559. static int av_addyna (char **pa, int *pn, const char *arg, int itemsize);
  560.  
  561.  
  562. // well, I think that isgraph() and other MUST be improved
  563. static inline int Isgraph (int c) {
  564.   return c > 0 && c < 256 ? isgraph(c) : 0;
  565. }
  566.  
  567.  
  568. #define DYNALIM 1024
  569. // add new item to dynamic array, returns array size or 0 if no memory
  570. static int
  571. av_addyna (char **pa, int *pn, const char *arg, int itemsize)
  572. {
  573.   // array capacity as f(size)
  574.   // 0 : 2+,  1 : 4+,  2 : 4,  3 : 8+,  4 : 8,  5 : 8,  6 : 8,  7 : 16+   ...
  575.   int size, old, n = *pn;
  576.   if (n < 0)
  577.     n = 0;
  578.   char *t = *pa;
  579.  
  580.   if (n < DYNALIM) {
  581.     size = 1 << (32 - __builtin_clz(n + 1));
  582.     old = n ? 1 << (32 - __builtin_clz(n)) : 1;
  583.   } else {
  584.     size = ((n + 1) & ~(DYNALIM - 1)) + DYNALIM;
  585.     old = (n & ~(DYNALIM - 1)) + DYNALIM;
  586.   }
  587.  
  588.   if (old != size )
  589.     if (!(*pa = (char *)realloc(*pa, size * itemsize))) {
  590.       *pa = t;
  591.       return 0;
  592.     }
  593.  
  594.   memcpy(*pa + n++ * itemsize, arg, itemsize);
  595.   memset(*pa + n * itemsize, 0, itemsize);
  596.   return *pn = n;
  597. }
  598. #undef DYNALIM
  599.  
  600. #ifndef unix
  601. static void _av_setwinprogname ()
  602. {
  603.   if (__progname[0] == '?') {
  604.     char pname[10240];
  605.     GetModuleFileName(0, pname, sizeof(pname) - 1);
  606.     char *p = strrchr(pname, '\\');
  607.     __progname = strdup(p ? p + 1 : pname);
  608.   }
  609. }
  610. #endif
  611.  
  612. #ifndef _GNU_SOURCE
  613. static int
  614. asprintf (char **ps, const char *fmt, ...)
  615. {
  616.   //   puts("my asprintf");
  617.   va_list ap;
  618.   va_start(ap, fmt);
  619.   int rc = vsnprintf(*ps, 0, fmt, ap);
  620.   va_end(ap);
  621.   if (rc >= 0) {
  622.     if ((*ps = (char *)malloc(rc + 2))) {
  623.       va_start(ap, fmt);
  624.       rc = vsnprintf(*ps, rc + 1, fmt, ap);
  625.       va_end(ap);
  626.     } else
  627.       rc = -1;
  628.   }
  629.  
  630.   return rc;
  631. }
  632. #endif
  633.  
  634. static int
  635. _av_addmsg (char **p, const char *fmt, ...)
  636. {
  637.   va_list ap;
  638.   va_start(ap, fmt);
  639.   int l = vsnprintf(*p, 0, fmt, ap);
  640.   va_end(ap);
  641.   if (l > 0) {
  642.     int ll = *p ? strlen(*p) : 0, pnl = ll;
  643.     if (pnl)
  644.       ll++;
  645.     if ((*p = (char *)realloc(*p, l + ll + 1))) {
  646.       if (pnl)
  647.     (*p)[pnl] = '\n';
  648.       va_start(ap, fmt);
  649.       vsnprintf((*p) + ll, l + 1, fmt, ap);
  650.       va_end(ap);
  651.     } else
  652.       l = -1;
  653.   }
  654.  
  655.   return l;
  656. }
  657.  
  658.  
  659.  
  660. // Called if found ARGUMENT or END, Returns code
  661. static inline int _av_clropt (int code, struct av_state *st) {
  662.   st->optchr = st->optlen = 0;
  663.   return st->zopt = code;
  664. }
  665.  
  666. // Modify av_state for processing next argv[]
  667. static inline int _av_nextargv (int code, struct av_state *st) {
  668.   st->optind++; st->optchr = 0;
  669.   return code;
  670. }
  671.  
  672. // Add arg to av_state.args[]
  673. static int _av_add_arg (struct av_state *st, int i) {
  674.   int rc = 1;
  675.  
  676.   if (i >= 0 && i < st->argc) {
  677.     int j, n = st->n_args, l;
  678.     for (j = n - 1; j > 0 && st->iargs[j] > i; j--);
  679.     if (!(n && st->iargs[j] == i)) {
  680.       if (j < 0 || st->iargs[j] < i)
  681.     j++;
  682.       rc = av_addyna((char **)&st->iargs, &n, (char *)&i, sizeof(int)) &&
  683.     av_addyna((char **)&st->args, &st->n_args,
  684.           (char *)&st->argv[i], sizeof(char *));
  685.       if (rc && (l = (n - 1 - j))) {
  686.     memmove((char *)(st->iargs + j + 1), (char *)(st->iargs + j),
  687.            l * sizeof(int));
  688.     memmove((char *)(st->args + j + 1), (char *)(st->args + j),
  689.            l * sizeof(char *));
  690.     st->iargs[j] = i;
  691.       }
  692.     }
  693.     st->args[j] = st->argv[i];
  694.   }
  695.   return rc;
  696. }
  697.  
  698. static inline int
  699. _av_cmp_optpos (void *o1, void *o2)
  700. {
  701.   struct av_opt *a1 = (typeof(a1))o1, *a2 = (typeof(a2))o2;
  702.  
  703.   return a1->optind < a2->optind ? -1 :
  704.     a1->optind > a2->optind ? 1 :
  705.     a1->optchr < a2->optchr ? -1 : a1->optchr != a2->optchr;
  706. }
  707.  
  708. static inline int
  709. _av_cmp_opterrpos (void *o1, void *o2)
  710. {
  711.   struct av_opterr *a1 = (typeof(a1))o1, *a2 = (typeof(a2))o2;
  712.  
  713.   return _av_cmp_optpos(&a1->opt, &a2->opt);
  714. }
  715.  
  716. // Add av_opt to av_state.opts[] (or av_opterr to av_state.eropts[])
  717. static int
  718. _av_store_opts (int *n_items, void **optarr_addr,
  719.         void *opt_item, int item_size, int (*fcmp)(void *o1, void *o2))
  720. {
  721.   int rc = 1, j, l;
  722.   char **a = (char **)optarr_addr;
  723.  
  724.   for (j = *n_items - 1; j > 0 && fcmp(*a + j * item_size, opt_item) > 0; j--);
  725.   if (!(*n_items && fcmp(*a + j * item_size, opt_item) == 0)) {
  726.     if (j < 0 || fcmp(*a + j * item_size, opt_item) < 0)
  727.       j++;
  728.     rc = av_addyna(a, n_items, (char *)opt_item, item_size);
  729.     if (rc && (l = (*n_items - 1 - j) * item_size))
  730.       memmove(*a + (j + 1) * item_size, *a + j * item_size, l);
  731.   }
  732.   memcpy(*a + j * item_size, (char *)opt_item, item_size);
  733.  
  734.   return rc;
  735. }
  736.  
  737. // Called if there are no more options, args only. Returns END.
  738. static int
  739. _av_copytail (struct av_state *st, int i)
  740. {
  741.   char **av = st->argv;
  742.  
  743.   if (st->arg_tail == -1 && i < st->argc && av[i]) {
  744.     st->arg_tail = st->n_args;
  745.     for (; i < st->argc && av[i]; i++)
  746.     if (!_av_add_arg(st, i))
  747.       return AV_NOMEM;
  748.   }
  749.  
  750.   return _av_clropt(AV_END, st);
  751. }
  752.  
  753. // Search argv[i] type
  754. static int
  755. _av_argtype (struct av_state *st, int i)
  756. {
  757.   if (i >= st->argc || !st->argv[i])
  758.     return AV_END;
  759.   if (strcmp(st->argv[i], "--") == 0)
  760.     return _AV_DASHDASH;
  761.   if (st->argv[i][0] != '-' || st->argv[i][1] == 0 // "-"
  762.     || strncmp(st->argv[i], "---", 3) == 0
  763.     // arguments like -1 or -2.73e+2 may be not option too
  764.     || (isdigit(st->argv[i][1]) && !(st->flags & AV_DIGIT_OPTION)))
  765.     return AV_ARG;
  766.  
  767.   return _AV_OPTION;
  768. }
  769.  
  770.  
  771. // Search current (using av_state) short option in dcl[]
  772. static int
  773. _av_findkey (AV_DCLOPT_T *dcl, struct av_state *st)
  774. {
  775.   st->dclindex = -1;
  776.   int i, c;
  777.   if (st->optsrc && Isgraph(c = st->optsrc[st->optchr]))
  778.     for (i = 0; !ISEMPTY_DCL(dcl + i); i++)
  779.       if ((dcl + i)->key == c)
  780.     return (st->dclindex = i), c;
  781.   return 0;
  782. }
  783.  
  784. // Search dcl[] by short key
  785. static AV_DCLOPT_T *
  786. av_find_dcl (int key, struct av_state *st)
  787. {
  788.   AV_DCLOPT_T *dcl = st->dcl;
  789.   int i;
  790.  
  791.   for (; !ISEMPTY_DCL(dcl); dcl++)
  792.     if (dcl->key == key)
  793.       return dcl;
  794.  
  795.   return 0;
  796. }
  797.  
  798.  
  799. #ifndef AV_NOUSEVAR
  800. // Make dcl option name in buf[] for error message. Returns this name
  801. static char *
  802. _av_dclname (AV_DCLOPT_T *d, char buf[])
  803. {
  804.   if (d->name)
  805.     sprintf(buf, "--%s", d->name);
  806.   else
  807.     sprintf(buf, "-%c", d->key);
  808.  
  809.   return buf;
  810. }
  811.  
  812. // Make dcl array error message for _av_initcheck() & _av_put_data()
  813. static int
  814. _av_check_arraydef (AV_DCLOPT_T *d, struct av_state *st)
  815. {
  816.   char msg[100];
  817.   int l = 0;
  818.  
  819.   if (d->def_arg & AV_ISARRAY) {
  820.     if ((d->def_arg & AV_HASARG_MASK) != AV_HASARG)
  821.       l = sprintf(msg, "array need only AV_HASARG");
  822.     if (!d->opt_counter)
  823.       l += sprintf(msg + l, "%s%s", l ? ", " : "", "array need counter");
  824.     if (!d->optarg_ptr)
  825.       l += sprintf(msg + l, "%s%s", l ? ", " : "", "array need optarg_ptr");
  826.  
  827.     if (l) {
  828.       char buf[d->name ? strlen(d->name) + 3 : 3];
  829.       st->do_msg = 0;
  830.       return (_av_addmsg(&st->msg, "option %s: dcl error - %s",
  831.              _av_dclname(d, buf), msg) < 0) ?
  832.     AV_NOMEM : AV_DEFVAR;
  833.     }
  834.   }
  835.  
  836.   return 0;
  837. }
  838.  
  839. // Sizeof AV_type
  840. static int
  841. _av_get_tsize (int type)
  842. {
  843.   static struct {
  844.     int type, size;
  845.   } ts[] = {{_AV_STR_T, sizeof(char *)},
  846.         {_AV_INT8_T, sizeof(int8_t)},
  847.         {_AV_UINT8_T, sizeof(uint8_t)},
  848.         {_AV_INT16_T, sizeof(int16_t)},
  849.         {_AV_UINT16_T, sizeof(uint16_t)},
  850.         {_AV_INT32_T, sizeof(int32_t)},
  851.         {_AV_UINT32_T, sizeof(uint32_t)},
  852.         {_AV_INT64_T, sizeof(int64_t)},
  853.         {_AV_UINT64_T, sizeof(uint64_t)},
  854.         {_AV_FLOAT_T, sizeof(float)},
  855.         {_AV_DOUBLE_T, sizeof(double)},
  856.         {0,0}};
  857.   int i;
  858.  
  859.   for (i = 0; ts[i].size; i++)
  860.     if (ts[i].type == type)
  861.       return ts[i].size;
  862.  
  863.   return 0;
  864. }
  865.  
  866. // Make dcl variable type error message for _av_put_data() & _av_initcheck()
  867. static int
  868. _av_type_error (AV_DCLOPT_T *d , struct av_state *st)
  869. {
  870.   char buf[d->name ? strlen(d->name) + 3 : 3];
  871.  
  872.   st->do_msg = 0;
  873.   return _av_addmsg(&st->msg, "option %s: dcl error - unknown type",
  874.             _av_dclname(d, buf)) < 0 ?
  875.     AV_NOMEM: AV_DEFVAR;
  876. }
  877.  
  878. // Make errno error message for _av_put_data()
  879. static int
  880. _av_data_error (AV_DCLOPT_T *d, const char *arg, int old, struct av_state *st)
  881. {
  882.   char buf[d->name ? strlen(d->name) + 3 : 3];
  883.   const char *msg = errno ? strerror(errno) : "invalid character";
  884.  
  885.   st->do_msg = 0;
  886.   return _av_addmsg(&st->msg, "option %s <%s>: data format or value error - %s",
  887.           _av_dclname(d, buf), arg, msg) < 0 ?
  888.     AV_NOMEM : (errno = old,  AV_DATAERR);
  889. }
  890.  
  891. #endif
  892.  
  893. // for dcl[] debug (duplicates key, ambiguous name)
  894. static int
  895. av_check_dcl (AV_DCLOPT_T *dcl, FILE *out)
  896. {
  897.   int i, j, err = 0;
  898.  
  899.   for (i = 0; !ISEMPTY_DCL(dcl + i + 1); i++) {
  900.     int c = dcl[i].key;
  901.     if (c < 0 || c >= AV_KEY_SPEC) {
  902.       err++;
  903.       fprintf(out, "invalid key value %d (0x%02x) in dcl[] %d\n", c, c, i);
  904.     }
  905.    
  906.     for (j = i + 1; !ISEMPTY_DCL(dcl + j); j++) {
  907.       if (c > 0 && c == dcl[j].key) {
  908.     fprintf(out, "key %d ('%c') duplicated in dcl[] %d and %d\n",
  909.         c, Isgraph(c) ? c : 0, i, j);
  910.     err++;
  911.       }
  912.       if (dcl[i].name && dcl[j].name && !strcmp(dcl[i].name, dcl[j].name)) {
  913.     fprintf(out, "name '%s' duplicated in dcl[] %d and %d\n",
  914.         dcl[i].name, i, j);
  915.     err++;
  916.       }
  917.     }
  918.   }
  919.  
  920.   return err;
  921. }
  922.  
  923.  
  924. static int
  925. _av_initcheck (struct av_state *st)
  926. {
  927.   int rc = 0, urc;
  928.  
  929. #ifndef unix
  930.   _av_setwinprogname();
  931. #endif
  932.  
  933.   if (!st || !st->dcl) {
  934.     fprintf(stderr, "%s: invalid usage (null state data)\n", __progname);
  935.     return AV_USAGE_ERROR;
  936.   }
  937.   st->zopt = AV_KEY_NONE;
  938.   st->optopt = 0;
  939.   if (!st->out)
  940.     st->out = stderr;
  941.   if (st->optind < 1) { // rescan
  942.     st->optind = !(st->flags & AV_PARSE_ARGV0);
  943.     st->optchr = 0;
  944.   }
  945.   // OLD...  st->msg = 0;    // error message from dcl[].uvfun() must be malloc
  946.   st->do_msg = 1;
  947.   st->optlen = 0;
  948.   st->dclindex = st->cur_argind = st->cur_optind = st->cur_optchr = -1;
  949.   st->optsrc = st->optarg = NULL;
  950.   if (st->optchr < 0)   // fix possible user error?
  951.     st->optchr = 0;
  952.  
  953.   if (!st->args)
  954.     st->msg = 0;
  955.   else
  956.     free(st->msg), st->msg = 0;
  957.  
  958.   if (st->argc < 1 || !st->argv)
  959.     return AV_ARGV_ERROR;
  960.  
  961.   if (!st->args) { // init
  962.     st->optind = !(st->flags & AV_PARSE_ARGV0);
  963.     st->flags |= (getenv("POSIXLY_CORRECT") ? AV_STOP_NONOPT_ARG : 0);
  964.     st->optchr = 0;
  965.     st->n_err = st->tot_err = st->n_opts = st->n_args = 0;
  966.     st->arg_tail = -1;
  967.     st->groups = -1; // all possible groups
  968.     st->args = (char **)calloc(1, sizeof(*st->args));
  969.     st->iargs = (int *)calloc(1, sizeof(*st->iargs));
  970.     st->opts = (typeof(st->opts))calloc(1, sizeof(*st->opts));
  971.     st->eropts = (typeof(st->eropts))calloc(1, sizeof(*st->eropts));
  972.      if (!st->args || !st->opts || !st->eropts)
  973.       return AV_NOMEM;
  974.  
  975.     AV_DCLOPT_T *dcl = st->dcl;
  976.     if ((st->flags & AV_CHECK_DCL) && av_check_dcl(dcl, st->out) > 0) {
  977.       fprintf(st->out, "%s: checkdcl() exit due to error\n", __progname);
  978.       exit(EX_USAGE);
  979.     }
  980.  
  981. #ifndef AV_NOUSEVAR
  982.     // check dcl[] array definition & types only up to first error
  983.     for (st->dclindex = 0; !ISEMPTY_DCL(dcl); dcl++, st->dclindex++) {
  984.       if ((urc = _av_check_arraydef(dcl, st)) < 0 && !rc)
  985.     rc = urc; // will return first error code
  986.       if (!_av_get_tsize(dcl->def_arg & AV_TYPE_MASK)) {
  987.     urc = _av_type_error(dcl, st);
  988.     if (!rc)
  989.       rc = urc;
  990.       }
  991.       if (dcl->def_arg & AV_ISARRAY) {
  992.     if (dcl->opt_counter)
  993.       *(dcl->opt_counter) = 0;
  994.     if (dcl->optarg_ptr)
  995.       *(dcl->optarg_ptr) = 0;
  996.       }
  997.       if (dcl->uvfun && (urc = dcl->uvfun(AV_KEY_INIT, 0, st, dcl)) < 0) {
  998.     st->tot_err++;
  999.     if (!rc)
  1000.       rc = urc;
  1001.       }
  1002.     }
  1003.     st->dclindex = -1;
  1004. #endif
  1005.  
  1006.     if (st->u_callback) {
  1007.       if ((urc = st->u_callback(AV_KEY_INIT, 0, st)) < 0) {
  1008.     st->tot_err++;
  1009.     if (!rc)
  1010.       rc = urc;
  1011.       }
  1012.     }
  1013.   }
  1014.  
  1015.   return rc;
  1016. }
  1017.  
  1018. // Check compatibility current option groups set  with all groups,
  1019. // if compatible modify all groups by current option groups
  1020. static int
  1021. _av_grcompat (int rc, struct av_state *st)
  1022. {
  1023.   if ((0 < rc && rc < AV_KEY_SPEC) && st->dclindex >= 0) {
  1024.     int curopt_grp = AV_GROUPS(st->dcl + st->dclindex);
  1025.  
  1026.     if (curopt_grp) {
  1027.       if (st->groups & curopt_grp)
  1028.     st->groups &= curopt_grp;
  1029.       else
  1030.     rc = AV_NOCOMPATOPT;
  1031.     }
  1032.   }
  1033.   return rc;
  1034. }
  1035.  
  1036. // Main internal parsing routine
  1037. // Returns av_dclopt.key or 0 (arg) or -1 (finish) or ERRCODE < -1
  1038. static int
  1039. _av_getopt (struct av_state *st)
  1040. {
  1041.   int ret = _av_initcheck(st);
  1042.   if (ret)
  1043.     return ret;
  1044.   char **av = st->argv;
  1045.   int ac = st->argc, has_arg, key;
  1046.   AV_DCLOPT_T *dcl = st->dcl, *pdcl = dcl;
  1047.  
  1048.   for (;;) { // check current argv parsing point
  1049.     if (st->optind < ac)
  1050.       st->optsrc = av[st->optind];
  1051.     switch (_av_argtype(st, st->optind)) {
  1052.     case _AV_DASHDASH:
  1053.       st->optind++;
  1054.     case AV_END:
  1055.       return _av_clropt(AV_END, st);   // no more arguments
  1056.     case AV_ARG:
  1057.       st->optarg = av[st->cur_argind = st->optind++];
  1058.       return _av_clropt(AV_ARG, st);
  1059.     }
  1060.  
  1061.     if (st->optchr < strlen(st->optsrc))
  1062.       break;
  1063.     _av_nextargv(st->zopt, st);
  1064.   }
  1065.  
  1066.   if (st->optchr == 0) { // check longopt
  1067.     st->cur_optchr = st->optchr = 1 + (av[st->optind][1] == '-');
  1068.     st->cur_optind = st->optind;
  1069.     // check first char is legal key and set st->dlcindex
  1070.     key = _av_findkey(dcl, st);
  1071.  
  1072.     char *optname = st->optsrc + st->optchr;
  1073.     if ((st->optlen = strcspn(optname, "="))
  1074.     && (st->optchr == 2 || st->flags & AV_LONGOPT_ONLY)) {
  1075.       // check for long option name
  1076.       char namebuf[st->optlen + 1];
  1077.       strncpy(namebuf, optname, st->optlen); namebuf[st->optlen] = 0;
  1078.  
  1079.       if (st->optchr == 2 // --option
  1080.       || !key         // first char not recognized as short option
  1081.       || st->optlen > 1) { // but we want to recognize valid -longoption
  1082.     int i, match = 0;
  1083.     for (i = 0, pdcl = dcl; !ISEMPTY_DCL(pdcl); i++, pdcl++) {
  1084.       if (pdcl->key > 0 // must, by reason of return code 0 is ARG
  1085.           && pdcl->name
  1086.           && strncmp(optname, pdcl->name, st->optlen) == 0) {
  1087.         if (pdcl->name[st->optlen] == 0) { // exactly
  1088.           st->dclindex = i;
  1089.           match = 1;
  1090.           break;
  1091.         }
  1092.         if (!match++)
  1093.           st->dclindex = i; // first of not exactly matching
  1094.       }
  1095.     }
  1096.  
  1097.     if (match == 1) {
  1098.       pdcl = dcl + st->dclindex;
  1099.       st->zopt = ret = pdcl->key;
  1100.       if ((has_arg = pdcl->def_arg & AV_HASARG_MASK)) {
  1101.         if (optname[st->optlen] == '=') {
  1102.           st->cur_argind = st->optind;
  1103.           st->optarg = optname + st->optlen + 1;
  1104.         } else if (!(has_arg & AV_OPTARG)) { // not OPTARG, mandatory ARG
  1105.           if (st->optind + 1 < ac && st->argv[st->optind + 1]) {
  1106.         st->cur_argind = st->optind + 1;
  1107.         st->optarg = av[st->optind++ + 1];
  1108.           } else
  1109.         ret = AV_LONGOPT_REQ_ARG;
  1110.         }
  1111.       } else if (optname[st->optlen] == '=')
  1112.         ret = AV_LONGOPT_NOTALLOW_ARG;
  1113.  
  1114.       _av_nextargv(st->zopt, st);
  1115.       return _av_grcompat(ret, st);
  1116.     }
  1117.  
  1118.     if (match > 1) //  BEFORE ./tt TEST  && (st->optchr == 2 || !key))
  1119.       return _av_nextargv(AV_LONGOPT_AMBIGUOUS, st);
  1120.  
  1121.     if (st->optchr == 2 || (st->dclindex < 0 && st->optlen > 1))
  1122.       // Here "--longopt" or "-longopt" not found in dcl[]
  1123.       // and '-l' not found as short option too.
  1124.       // Note, when '-l' found as short option, we out of this point
  1125.       //  and will try to parse "-longopt" as set of short options
  1126.       return _av_nextargv(AV_NO_LONGOPT, st);
  1127.       }
  1128.     }
  1129.   }
  1130.  
  1131.   ret = AV_NO_OPTION;
  1132.   st->optlen = 0; // short option
  1133.   st->optopt = st->optsrc[st->cur_optchr = st->optchr];
  1134.   st->cur_optind = st->optind;
  1135.   if ((key = _av_findkey(dcl, st))) {
  1136.     pdcl = dcl + st->dclindex;
  1137.     st->zopt = ret = pdcl->key;
  1138.     if ((has_arg = (pdcl->def_arg & AV_HASARG_MASK))) {
  1139.       if (st->optsrc[st->optchr + 1]) {
  1140.     // arg in the same argv, set data
  1141.     st->cur_argind = st->optind;
  1142.     st->optarg = st->optsrc + st->optchr + 1;
  1143.     _av_nextargv(ret, st); // will return pdcl->key
  1144.       } else if (!(has_arg & AV_OPTARG)) { // not OPTARG, mandatory ARG
  1145.     // data in the next argv and it required
  1146.     _av_nextargv(ret, st); // will return pdcl->key
  1147.     if (st->optind < ac && st->argv[st->optind]) {
  1148.       st->cur_argind = st->optind;
  1149.       st->optarg = av[st->optind++];
  1150.     } else
  1151.       ret = AV_OPTION_REQ_ARG;
  1152.       } else // optional_arg and data in the next argv[] (don't set it)
  1153.     st->optchr++;
  1154.     } else
  1155.       st->optchr++; // next step
  1156.   } else if (st->flags & AV_LONGOPT_ONLY)
  1157.     return _av_nextargv(AV_NO_LONGOPT, st);
  1158.   else
  1159.     st->optchr++; // here ret == AV_NO_OPTION  i.e. invalid option
  1160.  
  1161.   return _av_grcompat(ret, st);
  1162. }
  1163.  
  1164. /*
  1165.   Create array res[] (call malloc()) with indexes of av_dclopt[],
  1166.   for .name matches avitem (if there are no matches, array not created)
  1167.   avitem must be --longopt or -longopt (with dashes!!!)
  1168.   Returns array size
  1169.  */
  1170. static int
  1171. av_match_names (char *avitem, AV_DCLOPT_T *dcl, int **res)
  1172. {
  1173.   if (strlen(avitem) < 2 || *avitem != '-')
  1174.     return 0;
  1175.   char *optname = avitem + 1 + (avitem[1] == '-');
  1176.   int n = 0, match = 0, optlen = strcspn(optname, "=");
  1177.   *res = 0;
  1178.  
  1179.   for (n = 0; !ISEMPTY_DCL(dcl); dcl++, n++)
  1180.     if (dcl->name && dcl->name[0]
  1181.     && strncmp(optname, dcl->name, optlen) == 0)
  1182.       if (!av_addyna((char **)res, &match, (char *)&n, sizeof(n)))
  1183.     return -1;
  1184.  
  1185.   return match;
  1186. }
  1187.  
  1188. // returns message by code in malloc() memory
  1189. static char *
  1190. av_errormsg (struct av_state *st, int code)
  1191. {
  1192.   AV_DCLOPT_T *dcl = 0;
  1193.   if (!st || !(dcl = st->dcl))
  1194.     code = AV_USAGE_ERROR;
  1195.   char *msg = 0;
  1196.   int dup = 1;
  1197.  
  1198.   switch (code) {
  1199.   case AV_ARG:
  1200.     msg = (char *)"the argument is not option"; break;
  1201.   case AV_END:
  1202.     msg = (char *)"finish"; break;
  1203.   case AV_BREAK:
  1204.     msg = (char *)"break by callback"; break;
  1205.   case AV_ANYERR:
  1206.     msg = (char *)"some error by callback"; break;
  1207.   case AV_NOMEM:
  1208.     msg = (char *)"out of memory"; break;
  1209.   case AV_USAGE_ERROR:
  1210.     msg = (char *)"invalid usage (null state or dcl)"; break;
  1211.   case AV_ARGV_ERROR:
  1212.     msg = (char *)"wrong arguments list"; break;
  1213.   case AV_LONGOPT_REQ_ARG:
  1214.     if (dup = (asprintf(&msg, "option '--%s' requires an argument",
  1215.             (dcl + st->dclindex)->name) < 0))
  1216.       msg = 0;
  1217.     break;
  1218.   case AV_LONGOPT_NOTALLOW_ARG:
  1219.     if (dup = (asprintf(&msg, "option '--%s' doesn't allow an argument",
  1220.             (dcl + st->dclindex)->name) < 0))
  1221.       msg = 0;
  1222.     break;
  1223.   case AV_LONGOPT_AMBIGUOUS:
  1224.     {
  1225.       char namebuf[strlen(st->optsrc) + 1];
  1226.       strcpy(namebuf, st->optsrc);
  1227.       namebuf[strcspn(namebuf, "=")] = 0;
  1228.       int sz = asprintf(&msg, "option '%s' is ambiguous; possibilities:",
  1229.                 namebuf);
  1230.       if (sz < 0) {
  1231.     msg = 0; break;
  1232.       }
  1233.       int *amb = 0, n =  av_match_names(st->optsrc, dcl, &amb), i;
  1234.       if (n < 0) {
  1235.     msg = 0; break;
  1236.       }
  1237.       for (i = 0; i < n; i++) {
  1238.     if (!(msg = (char *)realloc(msg,
  1239.                     sz + strlen((dcl + amb[i])->name) + 6)))
  1240.       break;
  1241.     sz += sprintf(msg + sz, " '--%s'", (dcl + amb[i])->name);
  1242.       }
  1243.       if (n)
  1244.     free(amb);
  1245.       dup = 0;
  1246.     }
  1247.     break;
  1248.   case AV_NO_LONGOPT:
  1249.     {
  1250.       char namebuf[strlen(st->optsrc) + 1];
  1251.       if (st->optlen)
  1252.     strcpy(namebuf, st->optsrc);
  1253.       else {
  1254.     strcpy(namebuf + 1, st->optsrc + st->cur_optchr);
  1255.     namebuf[0] = '-';
  1256.       }
  1257.       namebuf[strcspn(namebuf, "=")] = 0;
  1258.  
  1259.       if (dup = (asprintf(&msg, "unrecognized option '%s'", namebuf) < 0))
  1260.     msg = 0;
  1261.     }
  1262.     break;
  1263.   case AV_NO_OPTION:
  1264.     if (dup = (asprintf(&msg, "invalid option -- '-%c'", st->optopt) < 0))
  1265.       msg = 0;
  1266.     break;
  1267.   case AV_OPTION_REQ_ARG:
  1268.     if (dup = (asprintf(&msg, "option '-%c' requires an argument",
  1269.             st->optsrc[st->cur_optchr]) < 0))
  1270.       msg = 0;
  1271.     break;
  1272.   case AV_NOCOMPATOPT:
  1273.     {
  1274.       char optbuf[2] = " ";
  1275.       if (!st->optlen)
  1276.     optbuf[0] = st->zopt;
  1277.       int sz = asprintf(&msg,
  1278.             "option '-%s%s' is not compatible with other options:",
  1279.             st->optlen ? "-" : "",
  1280.             st->optlen ? (dcl + st->dclindex)->name : optbuf),
  1281.     i;
  1282.       if (sz < 0) {
  1283.     msg = 0; break;
  1284.       }
  1285.       int curopt_grp = AV_GROUPS(dcl + st->dclindex);
  1286.       for (i = 0; i < st->n_opts; i++) {
  1287.     int grp = AV_GROUPS(dcl + st->opts[i].dclindex);
  1288.     if (grp && ((curopt_grp & grp) == 0)) {
  1289.       int l = st->opts[i].optlen;
  1290.       char opt[l ? l + 1 : 2];
  1291.       strncpy(opt, st->argv[st->opts[i].optind] + st->opts[i].optchr,
  1292.           l ? l : 1);
  1293.       opt[l ? l : 1] = 0;
  1294.       if (!(msg = (char *)realloc(msg, sz + l + 6)))
  1295.         break;
  1296.       sz += sprintf(msg + sz, " '%s%s'", l ? "--" : "-", opt);
  1297.     }
  1298.       }
  1299.       dup = 0;
  1300.     }
  1301.     break;
  1302.   case AV_DEFVAR:
  1303.     msg = (char *)"option declaration error"; break;
  1304.   case AV_DATAERR:
  1305.     msg = (char *)"option data format or value error"; break;
  1306.   default:
  1307.     if (dup = (asprintf(&msg, "Unknown error code %d", code) < 0))
  1308.       msg = 0;
  1309.   }
  1310.  
  1311.   return dup ? (msg ? strdup(msg) : 0) : msg;
  1312. }
  1313.  
  1314. #ifndef AV_NOUSEVAR
  1315. static inline int
  1316. _av_outof_urange (long long ll, int size)
  1317. {
  1318.   int shift = size * CHAR_BIT, sign = ll & (1 << (shift - 1));
  1319.   ll >>= shift;
  1320.  
  1321.   return (ll && ll != -1) || (ll == -1 && !sign);
  1322. }
  1323.  
  1324. static int
  1325. _av_check_range (long long ll, int type, int size)
  1326. {
  1327.   switch (type) {
  1328.   case _AV_INT8_T:
  1329.     if (ll < SCHAR_MIN || ll > SCHAR_MAX)
  1330.       errno = ERANGE;
  1331.     break;
  1332.   case _AV_INT16_T:
  1333.     if (ll < SHRT_MIN || ll > SHRT_MAX)
  1334.       errno = ERANGE;
  1335.     break;
  1336.   case _AV_INT32_T:
  1337.     if (ll < INT_MIN || ll > INT_MAX)
  1338.       errno = ERANGE;
  1339.     break;
  1340.   default:
  1341.     if (_av_outof_urange(ll, size))
  1342.       errno = ERANGE;
  1343.   }
  1344.   return errno ? 0 : 1;
  1345. }
  1346.  
  1347. static int
  1348. _av_put_data (int res, struct av_state *st)
  1349. {
  1350.   AV_DCLOPT_T *pdcl = st->dcl + st->dclindex;
  1351.   int rc,
  1352.     ok = 1,
  1353.     has_arg = pdcl->def_arg & AV_HASARG_MASK,
  1354.     type = pdcl->def_arg & AV_TYPE_MASK,
  1355.     tsize = _av_get_tsize(type);
  1356.   if (!tsize)
  1357.     return _av_type_error(pdcl, st);
  1358.   if ((rc = _av_check_arraydef(pdcl, st)))
  1359.     return rc;
  1360.  
  1361.   if (pdcl->opt_counter && !(pdcl->def_arg & AV_ISARRAY))
  1362.     (*pdcl->opt_counter)++;  // counter for ARRAY will incr in av_addyna() later
  1363.  
  1364.   if (pdcl->optarg_ptr && st->optarg && has_arg != AV_NOARG) {
  1365.     union {
  1366.       double d;
  1367.       float f;
  1368.       long long ll;
  1369.       char *p;
  1370.       int i;
  1371.       short s;
  1372.       char c;
  1373.     } val;
  1374.     long long ll;
  1375.     char *ep;
  1376.     int save_errno = errno;
  1377.     errno = 0;
  1378.     switch (type) {
  1379.     case _AV_STR_T:
  1380.       val.p = st->optarg; break;
  1381.     case _AV_DOUBLE_T: case _AV_FLOAT_T:
  1382.       if (type == AV_FLOAT)
  1383.     val.f = strtof(st->optarg, &ep);
  1384.       else
  1385.     val.d = strtod(st->optarg, &ep);
  1386.       if (*ep || errno)
  1387.     return _av_data_error(pdcl, st->optarg, save_errno, st);
  1388.       break;
  1389.     default:
  1390.       ll = strtoll(st->optarg, &ep, 0); val.ll = ll;
  1391.       if (*ep || errno)
  1392.     return _av_data_error(pdcl, st->optarg, save_errno, st);
  1393.       if (!_av_check_range(val.ll, type, tsize))
  1394.     return _av_data_error(pdcl, st->optarg, save_errno, st);
  1395.       switch (type) {
  1396.       case _AV_INT8_T: case _AV_UINT8_T: val.c = ll; break;
  1397.       case _AV_INT16_T: case _AV_UINT16_T: val.s = ll; break;
  1398.       case _AV_INT32_T: case _AV_UINT32_T: val.i = ll;
  1399.       }
  1400.     }
  1401.  
  1402.     if (pdcl->def_arg & AV_ISARRAY)
  1403.       ok = av_addyna(pdcl->optarg_ptr, pdcl->opt_counter,
  1404.              (char *)&val, tsize);
  1405.     else
  1406.       memcpy(pdcl->optarg_ptr, &val, tsize);
  1407.   }
  1408.  
  1409.   return ok ? res : AV_NOMEM;
  1410. }
  1411. #endif
  1412.  
  1413.  
  1414. static void
  1415. _av_set_avopt (struct av_opt *opt, struct av_state *st)
  1416. {
  1417.   opt->optind = st->cur_optind;
  1418.   opt->optchr = st->cur_optchr;
  1419.   opt->optlen = st->optlen;
  1420.   opt->dclindex = st->dclindex;
  1421.   opt->argind = st->cur_argind;
  1422.   opt->dcl = st->dcl;
  1423.   opt->argchr = (opt->arg = st->optarg) ?
  1424.     st->optarg - st->argv[st->cur_argind] : -1;
  1425. }
  1426.  
  1427. // Returns av_dclopt.key or 0 (arg) or -1 (finish) or ERRCODE < -1
  1428. static int
  1429. av_getopt (struct av_state *st)
  1430. {
  1431. #ifndef unix
  1432.   _av_setwinprogname();
  1433. #endif
  1434.   AV_DCLOPT_T *dcl;
  1435.  
  1436.   if (!st || !(dcl = st->dcl)) {
  1437.     fprintf(stderr, "%s: invalid usage (%s)\n",
  1438.         __progname, st ? "null dcl" : "null state");
  1439.     return AV_USAGE_ERROR;
  1440.   }
  1441.   st->dcl_itemsize = sizeof(*dcl);
  1442.  
  1443.   int ret = _av_getopt(st);
  1444.  
  1445.   if (ret == AV_ARG) {
  1446.     if (st->optind > 1 && (st->flags & AV_STOP_NONOPT_ARG))
  1447.       ret = _av_copytail(st, st->cur_argind);
  1448.     else if (!_av_add_arg(st, st->cur_argind))
  1449.       ret = AV_NOMEM;
  1450.   }
  1451.  
  1452. #ifndef AV_NOUSEVAR
  1453.   if (st->dclindex >= 0 && dcl[st->dclindex].key) {
  1454.     if (AV_ARG < ret && ret < AV_KEY_SPEC)
  1455.       ret = _av_put_data(ret, st); // *opt_counter, *optarg_ptr changes here
  1456.     if (dcl[st->dclindex].uvfun)
  1457.       ret = dcl[st->dclindex].uvfun(ret, st->optarg, st, dcl + st->dclindex);
  1458.   }
  1459. #endif
  1460.   if (st->u_callback)
  1461.     ret = st->u_callback(ret, st->optarg, st);
  1462.   if (ret == AV_END)
  1463.     ret = _av_copytail(st, st->optind); // AV_NOMEM possible
  1464.  
  1465.   struct av_opterr t;
  1466.   t.opt.optlen = -1; // indicator that t.opt is not fill yet
  1467.  
  1468.   //  char *msg = st->msg;    // any message from callback() must be malloc
  1469.   //  st->msg = 0;
  1470.  
  1471.   if (0 < ret && ret < AV_KEY_SPEC) {     //add opt
  1472.     _av_set_avopt(&t.opt, st);
  1473.     if (!_av_store_opts(&st->n_opts, (void **)&st->opts,
  1474.             (void **)&t.opt, sizeof(t.opt), _av_cmp_optpos))
  1475.       ret = AV_NOMEM;
  1476.   }
  1477.   if (ret < 0 && ret != AV_END && ret != AV_BREAK) { // add erropt & error msg
  1478.     st->tot_err++;
  1479.     if (t.opt.optlen == -1)
  1480.       _av_set_avopt(&t.opt, st);
  1481.     t.errcode = ret;
  1482.  
  1483.     if (st->do_msg) {
  1484.       // callback don't add own final meassage
  1485.       char *msg = av_errormsg(st, ret);
  1486.  
  1487.       if (!msg || _av_addmsg(&st->msg, "%s", msg) < 0)
  1488.     ret = AV_NOMEM;
  1489.       free(msg);
  1490.     }
  1491.  
  1492.     switch (ret) {
  1493.     case AV_NOMEM:
  1494.       fprintf(st->out, "%s: out of memory\n", __progname);
  1495.     case AV_ARGV_ERROR:
  1496.     case AV_USAGE_ERROR:
  1497.       break;
  1498.     default:
  1499.       if (t.opt.optind < 0)
  1500.     break;
  1501.       t.errmsg = strdup(st->msg ? st->msg : (char *)"no msg");
  1502.       if (!_av_store_opts(&st->n_err, (void **)&st->eropts,
  1503.                 (void **)&t, sizeof(t), _av_cmp_opterrpos)) {
  1504.     fprintf(st->out, "%s: out of memory\n", __progname);
  1505.     ret = AV_NOMEM;
  1506.       }
  1507.     }
  1508.   }
  1509.  
  1510. #ifndef AV_NOUSEVAR
  1511.   if (0 < ret && ret < AV_KEY_SPEC)
  1512.     (dcl + st->dclindex)->processed = st->optsrc; //argv[st->cur_optind];
  1513.   else if (ret == AV_END || ret == AV_BREAK)
  1514.     for (; !ISEMPTY_DCL(dcl); dcl++) {
  1515.       if (dcl->uvfun && dcl->uvfun(AV_KEY_FINI, 0, st, dcl) < 0)
  1516.     st->tot_err++;
  1517.     }
  1518. #endif
  1519.   if ((ret == AV_END || ret == AV_BREAK) && st->u_callback) {
  1520.     if (st->u_callback(AV_KEY_FINI, 0, st) < 0)
  1521.       st->tot_err++;
  1522.   }
  1523.  
  1524.   if (st->msg && !(st->flags & AV_SILENT)) {
  1525.     //    fprintf(st->out, "%s: %s\n", __progname, st->msg);
  1526.     int l = fprintf(st->out, "%s: ", __progname);
  1527.     char *t = st->msg, filler[l + 1], c;
  1528.     memset(filler, ' ', l); filler[l] = 0;
  1529.     while (c = *t++) {
  1530.       fputc(c, st->out);
  1531.       if (c == '\n')
  1532.     fputs(filler, st->out);
  1533.     }
  1534.     fputc('\n', st->out);
  1535.   }
  1536.  
  1537.   if (st->tot_err &&
  1538.       ((st->flags & AV_ERREXIT) ||
  1539.        ((ret == AV_END || ret == AV_BREAK) && (st->flags & AV_ERRENDEXIT)))) {
  1540.     if (!(st->flags & AV_SILENT)) {
  1541.       if (!(st->flags & AV_QUIET_EXIT)) {
  1542.     av_opt_usage(dcl, 0, 0, st->out);
  1543.     fprintf(st->out, "%s: exit due to error(s)\n", __progname);
  1544.       }
  1545.       fprintf(st->out, "Try `%s --help or --usage` for more information.\n",
  1546.           __progname);
  1547.     }
  1548.     exit(EX_USAGE);
  1549.   }
  1550.  
  1551.   return ret;
  1552. }
  1553.  
  1554. static struct av_state *
  1555. argvopt (int ac, char *av[], AV_DCLOPT_T *dcl,
  1556.      FILE *out, int flags, ucallback_t uf, void *glue)
  1557. {
  1558. #ifndef unix
  1559.   _av_setwinprogname();
  1560. #endif
  1561.   struct av_state st = {0}, *res;
  1562.   AV_DCLOPT_T dd[] = {{0}};
  1563.  
  1564.   st.dcl = dcl ? dcl : dd;
  1565.   st.argc = ac;
  1566.   st.argv = av;
  1567.   st.flags = flags;
  1568.   st.out = out;
  1569.   st.glue = glue;
  1570.   st.u_callback = uf;
  1571.  
  1572.   int opt;
  1573.   while ((opt = av_getopt(&st)) != AV_END)
  1574.     if (opt == AV_BREAK ||
  1575.     opt == AV_USAGE_ERROR ||
  1576.     opt == AV_ARGV_ERROR ||
  1577.     opt == AV_NOMEM ||
  1578.     (st.tot_err && (st.flags & AV_ERRBREAK)))
  1579.       break;
  1580.  
  1581.   st.dcl = dcl; // may restore NULL
  1582.   if ((res = (typeof(res))malloc(sizeof(*res))))
  1583.     memcpy(res, &st, sizeof(st));
  1584.   return res;
  1585. }
  1586.  
  1587. static void *
  1588. av_state_free (struct av_state *st, int free_st)
  1589. {
  1590.   if (st->args) {
  1591.     free(st->args);
  1592.     free(st->iargs);
  1593.     free(st->opts);
  1594.     int i;
  1595.     for (i = 0; i < st->n_err; i++)
  1596.       free(st->eropts[i].errmsg);
  1597.     free(st->eropts);
  1598.     free(st->msg);
  1599.   }
  1600.   if (free_st)
  1601.     free(st);
  1602.   else
  1603.     memset(st, 0, sizeof(*st));
  1604.  
  1605.   return 0;
  1606. }
  1607.  
  1608.  
  1609. /*
  1610.   help/usage
  1611.  */
  1612. static const char *av_program_doc;
  1613. static const char *av_program_usage_doc;
  1614. static const char *av_args_doc = "[ARG ...]";
  1615. static const char *av_program_version = "argvopt 1.1";
  1616. //static const char *av_exitcode;
  1617.  
  1618. #ifndef AV_WRAP_LINE
  1619. #define AV_WRAP_LINE 64
  1620. #endif
  1621. static int av_wrap_pos = AV_WRAP_LINE;
  1622. #define DEFAULT_ARGNAME "ARG"
  1623. #ifndef DESCR_POS
  1624. #define DESCR_POS 26
  1625. #endif
  1626.  
  1627. static const char *
  1628. _av_print_begin (const char *beg_end_msg, FILE *out)
  1629. {
  1630.   int c = 0;
  1631.  
  1632.   if (beg_end_msg)
  1633.     while ((c = *beg_end_msg++) && c != '\v')
  1634.       fputc(c, out);
  1635.   return c ? beg_end_msg : 0;
  1636. }
  1637.  
  1638. static char *
  1639. _av_get_argname (const char *descr, char *argname, int size)
  1640. {
  1641.   const char *p = DEFAULT_ARGNAME, *q = descr ? strchr(descr, '\v') : 0;
  1642.   char *a = argname;
  1643.  
  1644.   if (q)
  1645.     p = q + 1;
  1646.   while ((size-- > 1) && (*a++ = *p++));
  1647.   *a = 0;
  1648.  
  1649.   return argname;
  1650. }
  1651.  
  1652. static int
  1653. _av_print_description (int l, AV_DCLOPT_T *dcl, FILE *out, int prigroups)
  1654. {
  1655.   const char *p = dcl->descr;
  1656.  
  1657.   if (p && *p && *p != '\v') {
  1658.     if (l) {  // key or longoption or both was printed
  1659.       if (l > DESCR_POS - 1)
  1660.     fputc(' ', out), l++;
  1661.       for (; l < DESCR_POS; l++) // print pad spaces before description
  1662.     fputc(' ', out);
  1663.       int groups = prigroups ? AV_GROUPS(dcl) : 0;
  1664.       if (groups)
  1665.     l += fprintf(out, "<%02x> ", groups);
  1666.       else
  1667.     while (l++ < DESCR_POS + 5)
  1668.       fputc(' ', out);
  1669.     }
  1670.     while (*p && *p != '\v')  {
  1671.       l++;
  1672.       fputc(*p++, out);
  1673.     }
  1674.   }
  1675.   return l;
  1676. }
  1677.  
  1678. static int
  1679. _av_print_argname (int l, AV_DCLOPT_T *dcl, FILE *out)
  1680. {
  1681.   if (l && (dcl->def_arg & AV_HASARG_MASK)) {
  1682.     char argname[80];  // default = "ARG";
  1683.     int oa = dcl->def_arg & AV_OPTARG;
  1684.     l += fprintf(out, "%s%s%s",
  1685.          oa ? dcl->name ? "[=" : "[" : " ",
  1686.          _av_get_argname(dcl->descr, argname, sizeof(argname)),
  1687.          oa ? "]" : "");
  1688.   }
  1689.   return l;
  1690. }
  1691.  
  1692. static inline int _av_wrap_line (int print_pos, FILE *out) {
  1693.   if (print_pos > av_wrap_pos) {
  1694.     print_pos = 8;
  1695.     fputs("\n\t", out);
  1696.   }
  1697.   return print_pos;
  1698. }
  1699.  
  1700. static int
  1701. _av_priusgrp (AV_DCLOPT_T *dcl, long long gmask, int n_grp,
  1702.           int l, FILE *out, int mnd_fl, int *obl)
  1703. {
  1704.   int i, grps;
  1705.  
  1706.   // print options
  1707.   for (i = 0; !ISEMPTY_DCL(dcl); dcl++, i++) {
  1708.     if (gmask == ~0 || i > 63
  1709.     || !(grps = AV_GROUPS(dcl)) || (gmask & (1LL << i))) {
  1710.       int mnd = AV_MANDAT(dcl),
  1711.     is_mandatory = (mnd == AV_VMANDAT(AV_MNDTRY))
  1712.     || (n_grp ? (mnd & (1 << (n_grp - 1))) : mnd);
  1713.  
  1714.       if (mnd_fl != is_mandatory) {
  1715.       (*obl)++;
  1716.       continue;
  1717.       }
  1718.  
  1719.  
  1720.       int key = Isgraph(dcl->key) ? dcl->key : 0;
  1721.  
  1722.       l = _av_wrap_line(l, out);
  1723.       if (dcl->def_arg & AV_HASARG_MASK) {
  1724.     char argname[80];
  1725.     int oa = dcl->def_arg & AV_OPTARG;
  1726.    
  1727.     if (key)
  1728.       l += dcl->name ?
  1729.         fprintf(out, " -%c,", key) :
  1730.         fprintf(out, " -%c%s%s%s", key, oa ? "[" : " ",
  1731.             _av_get_argname(dcl->descr, argname, sizeof(argname)),
  1732.             oa ? "]" : "");
  1733.     if (dcl->name)
  1734.       l += fprintf(out, "%s--%s%s%s%s", key ? "" : " ",
  1735.                dcl->name, oa ? "[=" : "=",
  1736.                _av_get_argname(dcl->descr, argname, sizeof(argname)),
  1737.                oa ? "]" : "");
  1738.       } else {
  1739.     if (key)
  1740.       l += fprintf(out, " -%c%s", key, dcl->name ? "," : "");
  1741.     if (dcl->name)
  1742.       l += fprintf(out, "%s--%s", key ? "" : " ", dcl->name);
  1743.       }
  1744.     }
  1745.   }
  1746.  
  1747.   return l;
  1748. }
  1749.  
  1750.  
  1751.  
  1752. static void
  1753. _av_pri_usage_group (AV_DCLOPT_T *dcl, long long gmask, int n_grp,
  1754.        int l, const char *args_msg, FILE *out)
  1755. {
  1756.   int n_obl = 0;
  1757.  
  1758.   l = _av_priusgrp(dcl, gmask, n_grp, l, out, 1, &n_obl); // print mandatory
  1759.   if (n_obl) {
  1760.     fputs(" [", out);
  1761.     l = _av_priusgrp(dcl, gmask, n_grp, l + 2, out, 0, &n_obl) + 2;
  1762.     fputs(" ]", out);
  1763.   }
  1764.  
  1765.   if (args_msg) {
  1766.     fflush(out);
  1767.     if (l + strcspn(args_msg, "\n\001\002\003\004\005\006\007\008") > 79)
  1768.       fputs("\n\t", out);
  1769.     fputc(' ', out);
  1770.     while ((unsigned char)*args_msg > AV_GRP_LEN)
  1771.       fputc(*args_msg++, out);
  1772.   }
  1773.   fputc('\n', out);
  1774. }
  1775.  
  1776.  
  1777. static const char *
  1778. _av_getgroup_arg (const char *args_msg, int group)
  1779. {
  1780.   if (args_msg && group) {
  1781.     while (*args_msg && *args_msg != group)
  1782.       args_msg++;
  1783.     if (*args_msg)
  1784.       args_msg++;
  1785.   }
  1786.   return args_msg;
  1787. }
  1788.  
  1789. static void
  1790. _av_set_index_bitmask (long long bitmask[], int index, int grps)
  1791. {
  1792.   int j;
  1793.  
  1794.   if (grps) // this DCL in one or more groups
  1795.     for (j = 0; j < AV_GRP_LEN; j++)
  1796.       if (grps & (1 << j))
  1797.     bitmask[j] |= (1LL << index);
  1798. }
  1799.  
  1800. static void
  1801. _av_print_usage (AV_DCLOPT_T *dcl, const char *args_msg, FILE *out)
  1802. {
  1803. #ifndef unix
  1804.   _av_setwinprogname();
  1805. #endif
  1806.   long long groups[AV_GRP_LEN] = {0}; // bitmasks of DCL indexes by groups
  1807.   //    mandat[AV_GRP_LEN] = {0}; // the same for mandatory in group
  1808.   int i, j, n = 0, call = 0, grp;
  1809.  
  1810.   for (i = 0; i < 64 && !ISEMPTY_DCL(dcl + i); i++)
  1811.     _av_set_index_bitmask(groups, i, AV_GROUPS(dcl + i));
  1812.  
  1813.   // remove duplicates
  1814.   for (i = AV_GRP_LEN - 1; i >= 0; i--)
  1815.     if (groups[i]) {
  1816.       n++;
  1817.       for (j = 0; j < i; j++)
  1818.     if (groups[i] == groups[j]) {
  1819.       groups[i] = 0;
  1820.       n--;
  1821.       break;
  1822.     }
  1823.     }
  1824.  
  1825.   if (!n)
  1826.     groups[0] = ~0; // no one, so let all DCL belongs to first (special) group
  1827.  
  1828.   for (i = 0; i < AV_GRP_LEN; i++)
  1829.     if (groups[i]) {
  1830.       int n_grp = groups[i] == ~0 ? 0 : i + 1;
  1831.       _av_pri_usage_group(dcl, groups[i], n_grp,
  1832.          n > 1 ?
  1833.          fprintf(out,
  1834.              ++call == 1 ? "Usage: (%d) %s" : "  or : (%d) %s",
  1835.              n_grp, __progname) :
  1836.          fprintf(out, "Usage: %s", __progname),
  1837.         _av_getgroup_arg(args_msg, n_grp), out);
  1838.     }
  1839. }
  1840.  
  1841. static void
  1842. av_opt_usage (AV_DCLOPT_T *dcl,
  1843.           const char *beg_end_msg, const char *args_msg, FILE *out)
  1844. {
  1845.   if (!out)
  1846.     out = stderr;
  1847.   if (!beg_end_msg)
  1848.     beg_end_msg = av_program_usage_doc;
  1849.   if (!args_msg)
  1850.     args_msg = av_args_doc;
  1851.  
  1852.   beg_end_msg = _av_print_begin(beg_end_msg, out);
  1853.   _av_print_usage(dcl, args_msg, out);
  1854.   if (beg_end_msg) // print END part
  1855.     fprintf(out, "%s\n", beg_end_msg);
  1856. }
  1857.  
  1858. static void
  1859. _av_opt_help (AV_DCLOPT_T *dcl, const char *beg_end_msg, const char *args_msg,
  1860.           FILE *out, int priusage, int prigroups)
  1861. {
  1862.   if (!out)
  1863.     out = stderr;
  1864.   if (!beg_end_msg)
  1865.     beg_end_msg = av_program_doc;
  1866.   if (!args_msg)
  1867.     args_msg = av_args_doc;
  1868.  
  1869.   beg_end_msg = _av_print_begin(beg_end_msg, out);
  1870.   if (priusage)
  1871.     _av_print_usage(dcl, args_msg, out);
  1872.  
  1873.   int bla = 0;
  1874.   for (; !ISEMPTY_DCL(dcl); dcl++) {
  1875.     int l = Isgraph(dcl->key) ?
  1876.       fprintf(out, "  -%c%s", dcl->key, dcl->name ? "," : "") : 0;
  1877.     l += dcl->name ? fprintf(out, "%s --%s", l ? "" : "     ", dcl->name) : 0;
  1878.     if (_av_print_description(_av_print_argname(l, dcl, out),
  1879.                   dcl, out, prigroups))
  1880.       fputc('\n', out);
  1881.     if (Isgraph(dcl->key) && dcl->name && (dcl->def_arg & AV_HASARG_MASK))
  1882.       bla++;
  1883.   }
  1884.  
  1885.   if (bla)
  1886.     fprintf(out, "\n"
  1887.         "Mandatory or optional arguments to long options are also "
  1888.         "mandatory or optional  for any corresponding short options."
  1889.         "\n%s", beg_end_msg ? "\n" : "");
  1890.   if (beg_end_msg) // print END part
  1891.     fprintf(out, "%s\n", beg_end_msg);
  1892. }
  1893.  
  1894. static void
  1895. av_opt_help (AV_DCLOPT_T *dcl,
  1896.          const char *beg_end_msg, const char *args_msg, FILE *out)
  1897. {
  1898.   _av_opt_help(dcl, beg_end_msg, args_msg, out, 1 /* print usage */, 1);
  1899. }
  1900.  
  1901. static void
  1902. av_opt_help2 (AV_DCLOPT_T *dcl, const char *beg_end_msg, const char *args_msg,
  1903.           FILE *out, int prigroups)
  1904. {
  1905.   _av_opt_help(dcl, beg_end_msg, args_msg, out, 0 /* no usage */, prigroups);
  1906. }
  1907.  
  1908. #ifndef AV_NOUSEVAR
  1909. // dcl[] uvfun()s for usage & help
  1910. static int
  1911. av_dcl_usage (int key, char *a, struct av_state *st, AV_DCLOPT_T *td) {
  1912.   //  if (key > 0 && key != AV_KEY_INIT && key != AV_KEY_FINI) {
  1913.   if (td && td->key == key) {
  1914.     av_opt_usage(st->dcl, (char *)td->arg1, (char *)td->arg2, 0);
  1915.     if (td->optarg_ptr)
  1916.       exit(atoi((char *)td->optarg_ptr));
  1917.   }
  1918.   return key;
  1919. }
  1920.  
  1921. // full help
  1922. static int
  1923. av_dcl_help1 (int key, char *a, struct av_state *st, AV_DCLOPT_T *td) {
  1924.   //  if (key > 0 && key != AV_KEY_INIT && key != AV_KEY_FINI) {
  1925.   if (td && td->key == key) {
  1926.     av_opt_help(st->dcl, (char *)td->arg1, (char *)td->arg2, 0);
  1927.     if (td->optarg_ptr)
  1928.       exit(atoi((char *)td->optarg_ptr));
  1929.   }
  1930.   return key;
  1931. }
  1932.  
  1933. // full help without groups
  1934. static int
  1935. av_dcl_help (int key, char *a, struct av_state *st, AV_DCLOPT_T *td) {
  1936.   //  if (key > 0 && key != AV_KEY_INIT && key != AV_KEY_FINI) {
  1937.   if (td && td->key == key) {
  1938.     _av_opt_help(st->dcl, (char *)td->arg1, (char *)td->arg2,
  1939.          st->out, 1, 0);
  1940.     if (td->optarg_ptr)
  1941.       exit(atoi((char *)td->optarg_ptr));
  1942.   }
  1943.   return key;
  1944. }
  1945.  
  1946. // help without usage() and options groups info (opt description list)
  1947. static int
  1948. av_dcl_help2 (int key, char *a, struct av_state *st, AV_DCLOPT_T *td) {
  1949.   //  if (key > 0 && key != AV_KEY_INIT && key != AV_KEY_FINI) {
  1950.   if (td && td->key == key) {
  1951.     av_opt_help2(st->dcl, (char *)td->arg1, (char *)td->arg2, 0, 0);
  1952.     if (td->optarg_ptr)
  1953.       exit(atoi((char *)td->optarg_ptr));
  1954.   }
  1955.   return key;
  1956. }
  1957.  
  1958. // print version
  1959. static int
  1960. av_dcl_version (int key, char *a, struct av_state *st, AV_DCLOPT_T *td) {
  1961.   //  if (key > 0 && key != AV_KEY_INIT && key != AV_KEY_FINI) {
  1962.   if (td && td->key == key) {
  1963.     puts(av_program_version);
  1964.     if (td->optarg_ptr)
  1965.       exit(atoi((char *)td->optarg_ptr));
  1966.   }
  1967.   return key;
  1968. }
  1969.  
  1970. /*
  1971.   misc debug
  1972.  */
  1973. #include <signal.h>
  1974. #include <setjmp.h>
  1975. #include <fcntl.h>
  1976. #ifndef unix
  1977. #define sigjmp_buf jmp_buf
  1978. #define siglongjmp longjmp
  1979. #define sigsetjmp(buf,flag) setjmp(buf)
  1980. #endif
  1981. #ifdef __cplusplus
  1982.   static sigjmp_buf _av_is_accessible_jmp;
  1983.  
  1984.   static void _av_is_accessible_hdr (int sig) {
  1985.     siglongjmp(_av_is_accessible_jmp, 0);
  1986.   }
  1987. #endif
  1988.  
  1989. // check is addr readable/writable, returns 1 if so
  1990. static int
  1991. _av_is_mem_accessible (char *addr, int writable)
  1992. {
  1993.   if (addr == 0 || addr == (void *)-1LL)
  1994.     return 0;
  1995.   int rc = 1;
  1996. #ifndef __cplusplus
  1997.   sigjmp_buf _av_is_accessible_jmp;
  1998.  
  1999.   void _av_is_accessible_hdr (int sig) { siglongjmp(_av_is_accessible_jmp, 0); }
  2000. #endif
  2001.  
  2002.   void
  2003. #ifdef unix
  2004.     (* sigbus)(int) = signal(SIGBUS, _av_is_accessible_hdr),
  2005. #endif
  2006.     (* sigsegv)(int) = signal(SIGSEGV, _av_is_accessible_hdr);
  2007.  
  2008. #ifdef DEBUG
  2009. #ifdef unix
  2010.   if (sigbus == SIG_ERR) {
  2011.     fputs ("SIG_ERR signal SIGBUS\n", stderr);
  2012.     exit(1);
  2013.   }
  2014. #endif
  2015.   if (sigsegv == SIG_ERR) {
  2016.     fputs ("SIG_ERR signal SIGSEGV\n", stderr);
  2017.     exit(1);
  2018.   }
  2019. #endif
  2020.  
  2021.   if (sigsetjmp(_av_is_accessible_jmp, 1)) { // MANDATORY save sigmask
  2022.     rc = 0;
  2023.     errno = EINVAL;
  2024.   } else {
  2025.     char t = *addr;
  2026.     if (writable)
  2027.       *addr = t;
  2028.   }
  2029.  
  2030. #ifdef unix
  2031.   signal(SIGBUS, sigbus);
  2032. #endif
  2033.   signal(SIGSEGV, sigsegv);
  2034.  
  2035.   return rc;
  2036. }
  2037.  
  2038.  
  2039. static void
  2040. _av_print_value (void *ptr, int type, FILE *out)
  2041. {
  2042.   union {
  2043.     double d;
  2044.     float f;
  2045.     long long ll;
  2046.     char *p;
  2047.     int i;
  2048.     short s;
  2049.     char c;
  2050.   } *pv = (typeof(pv))ptr;
  2051.   int64_t ll = pv->ll;
  2052.   int unsign = 0;
  2053.  
  2054.   switch (type) {
  2055.   case _AV_STR_T:
  2056.     fprintf(out, "[%s]", _av_is_mem_accessible(pv->p, O_RDONLY) ?
  2057.        pv->p : pv->p ? "(N/A)" : "(null)");
  2058.     break;
  2059.   case _AV_DOUBLE_T:
  2060.     fprintf(out, "%.16g", pv->d);
  2061.     break;
  2062.   case _AV_FLOAT_T:
  2063.     fprintf(out, "%f", pv->f);
  2064.     break;
  2065.   default:
  2066.     switch (type) {
  2067.     case _AV_INT8_T:  ll = pv->c; break;
  2068.     case _AV_INT16_T: ll = pv->s; break;
  2069.     case _AV_INT32_T: ll = pv->i; break;
  2070.     case _AV_UINT8_T: unsign = 1; ll = (uint8_t)pv->c; break;
  2071.     case _AV_UINT16_T: unsign = 1; ll = (uint16_t)pv->s; break;
  2072.     case _AV_UINT32_T: unsign = 1; ll = (uint32_t)pv->i; break;
  2073.     case _AV_UINT64_T: unsign = 1;
  2074.     }
  2075.     if (unsign)
  2076.       fprintf(out, "%llu", (unsigned long long)ll);
  2077.     else
  2078.       fprintf(out, "%lld", (long long)ll);
  2079.   }
  2080. }
  2081.  
  2082. static void
  2083. av_print_dcl (AV_DCLOPT_T *dp, FILE *out)
  2084. {
  2085.   if (!out)
  2086.     out = stdout;
  2087.   for (; !ISEMPTY_DCL(dp); dp++) {
  2088.     if (dp->key <= 0)
  2089.       continue;
  2090.     if (dp->name)
  2091.       fprintf(out, "%s:\t%s", dp->name, strlen(dp->name) < 7 ? "\t" : "");
  2092.     else if (Isgraph(dp->key))
  2093.       fprintf(out, "'%c':\t\t", dp->key);
  2094.     else
  2095.       fprintf(out, "0x%04x:\t\t", dp->key);
  2096.    
  2097.     if (dp->processed)
  2098.       fprintf(out, "'%s'\t%s",
  2099.           dp->processed, strlen(dp->processed) < 7 ? "\t" : "");
  2100.     else
  2101.       fputs(" not used \t", out);
  2102.  
  2103.     if (_av_is_mem_accessible((char *)dp->opt_counter, O_RDONLY))
  2104.       fprintf(out, "cnt: %d\t", *dp->opt_counter);
  2105.     else
  2106.       fprintf(out, "      \t");
  2107.      
  2108.     if (dp->def_arg & AV_HASARG_MASK) {
  2109.       if (_av_is_mem_accessible((char *)dp->optarg_ptr, O_RDONLY)) {
  2110.     int type = dp->def_arg & AV_TYPE_MASK,
  2111.       vsize = _av_get_tsize(type), i;
  2112.     if (vsize)
  2113.       if (dp->def_arg & AV_ISARRAY) {
  2114.         if (_av_is_mem_accessible((char *)dp->opt_counter, O_RDONLY)) {
  2115.           char *ap = *dp->optarg_ptr;
  2116.           for (i = 0; i < *dp->opt_counter; i++) {
  2117.         _av_print_value((ap + i * vsize), type, out);
  2118.         fputc(' ', out);
  2119.           }
  2120.         } else
  2121.           fprintf(out, "ERR: no counter for array");
  2122.       } else
  2123.         _av_print_value(dp->optarg_ptr, type, out);
  2124.     else
  2125.       fprintf(out, "ERR: unknown type");
  2126.       } else
  2127.     fprintf(out, "[%s]",
  2128.         dp->optarg_ptr ? "<N/A>" : "ERR: no pointer to array");
  2129.     }
  2130.     fputc('\n', out);
  2131.   }
  2132. }
  2133.  
  2134. #endif // AV_NOUSEVAR
  2135.  
  2136. #endif // #ifndef AV_NOCODE
  2137. #endif //  _ARGVOPT_H
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement