Line data Source code
1 : /* [argparse.c wk 17.06.97] Argument Parser for option handling
2 : * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc.
3 : * Copyright (C) 1997-2001, 2006-2008, 2013-2016 Werner Koch
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * GnuPG is free software; you can redistribute it and/or modify it
8 : * under the terms of either
9 : *
10 : * - the GNU Lesser General Public License as published by the Free
11 : * Software Foundation; either version 3 of the License, or (at
12 : * your option) any later version.
13 : *
14 : * or
15 : *
16 : * - the GNU General Public License as published by the Free
17 : * Software Foundation; either version 2 of the License, or (at
18 : * your option) any later version.
19 : *
20 : * or both in parallel, as here.
21 : *
22 : * GnuPG is distributed in the hope that it will be useful, but
23 : * WITHOUT ANY WARRANTY; without even the implied warranty of
24 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 : * General Public License for more details.
26 : *
27 : * You should have received a copies of the GNU General Public License
28 : * and the GNU Lesser General Public License along with this program;
29 : * if not, see <https://gnu.org/licenses/>.
30 : */
31 :
32 : /* This file may be used as part of GnuPG or standalone. A GnuPG
33 : build is detected by the presence of the macro GNUPG_MAJOR_VERSION.
34 : Some feature are only availalbe in the GnuPG build mode.
35 : */
36 :
37 : #ifdef HAVE_CONFIG_H
38 : #include <config.h>
39 : #endif
40 :
41 : #include <stdio.h>
42 : #include <stdlib.h>
43 : #include <ctype.h>
44 : #include <string.h>
45 : #include <stdarg.h>
46 : #include <limits.h>
47 : #include <errno.h>
48 :
49 : #ifdef GNUPG_MAJOR_VERSION
50 : # include "util.h"
51 : # include "common-defs.h"
52 : # include "i18n.h"
53 : # include "mischelp.h"
54 : # include "stringhelp.h"
55 : # include "logging.h"
56 : # include "utf8conv.h"
57 : #endif /*GNUPG_MAJOR_VERSION*/
58 :
59 : #include "argparse.h"
60 :
61 : /* GnuPG uses GPLv3+ but a standalone version of this defaults to
62 : GPLv2+ because that is the license of this file. Change this if
63 : you include it in a program which uses GPLv3. If you don't want to
64 : set a a copyright string for your usage() you may also hardcode it
65 : here. */
66 : #ifndef GNUPG_MAJOR_VERSION
67 :
68 : # define ARGPARSE_GPL_VERSION 2
69 : # define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME"
70 :
71 : #else /* Used by GnuPG */
72 :
73 : # define ARGPARSE_GPL_VERSION 3
74 : # define ARGPARSE_CRIGHT_STR "Copyright (C) 2016 Free Software Foundation, Inc."
75 :
76 : #endif /*GNUPG_MAJOR_VERSION*/
77 :
78 : /* Replacements for standalone builds. */
79 : #ifndef GNUPG_MAJOR_VERSION
80 : # ifndef _
81 : # define _(a) (a)
82 : # endif
83 : # ifndef DIM
84 : # define DIM(v) (sizeof(v)/sizeof((v)[0]))
85 : # endif
86 : # define xtrymalloc(a) malloc ((a))
87 : # define xtryrealloc(a,b) realloc ((a), (b))
88 : # define xtrystrdup(a) strdup ((a))
89 : # define xfree(a) free ((a))
90 : # define log_error my_log_error
91 : # define log_bug my_log_bug
92 : # define trim_spaces(a) my_trim_spaces ((a))
93 : # define map_static_macro_string(a) (a)
94 : #endif /*!GNUPG_MAJOR_VERSION*/
95 :
96 :
97 : #define ARGPARSE_STR(v) #v
98 : #define ARGPARSE_STR2(v) ARGPARSE_STR(v)
99 :
100 :
101 : /* Replacements for standalone builds. */
102 : #ifndef GNUPG_MAJOR_VERSION
103 : static void
104 : my_log_error (const char *fmt, ...)
105 : {
106 : va_list arg_ptr ;
107 :
108 : va_start (arg_ptr, fmt);
109 : fprintf (stderr, "%s: ", strusage (11));
110 : vfprintf (stderr, fmt, arg_ptr);
111 : va_end (arg_ptr);
112 : }
113 :
114 : static void
115 : my_log_bug (const char *fmt, ...)
116 : {
117 : va_list arg_ptr ;
118 :
119 : va_start (arg_ptr, fmt);
120 : fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11));
121 : vfprintf (stderr, fmt, arg_ptr);
122 : va_end (arg_ptr);
123 : abort ();
124 : }
125 :
126 : /* Return true if the native charset is utf-8. */
127 : static int
128 : is_native_utf8 (void)
129 : {
130 : return 1;
131 : }
132 :
133 : static char *
134 : my_trim_spaces (char *str)
135 : {
136 : char *string, *p, *mark;
137 :
138 : string = str;
139 : /* Find first non space character. */
140 : for (p=string; *p && isspace (*(unsigned char*)p) ; p++)
141 : ;
142 : /* Move characters. */
143 : for ((mark = NULL); (*string = *p); string++, p++)
144 : if (isspace (*(unsigned char*)p))
145 : {
146 : if (!mark)
147 : mark = string;
148 : }
149 : else
150 : mark = NULL;
151 : if (mark)
152 : *mark = '\0' ; /* Remove trailing spaces. */
153 :
154 : return str ;
155 : }
156 :
157 : #endif /*!GNUPG_MAJOR_VERSION*/
158 :
159 :
160 :
161 : /*********************************
162 : * @Summary arg_parse
163 : * #include "argparse.h"
164 : *
165 : * typedef struct {
166 : * char *argc; pointer to argc (value subject to change)
167 : * char ***argv; pointer to argv (value subject to change)
168 : * unsigned flags; Global flags (DO NOT CHANGE)
169 : * int err; print error about last option
170 : * 1 = warning, 2 = abort
171 : * int r_opt; return option
172 : * int r_type; type of return value (0 = no argument found)
173 : * union {
174 : * int ret_int;
175 : * long ret_long
176 : * ulong ret_ulong;
177 : * char *ret_str;
178 : * } r; Return values
179 : * struct {
180 : * int idx;
181 : * const char *last;
182 : * void *aliases;
183 : * } internal; DO NOT CHANGE
184 : * } ARGPARSE_ARGS;
185 : *
186 : * typedef struct {
187 : * int short_opt;
188 : * const char *long_opt;
189 : * unsigned flags;
190 : * } ARGPARSE_OPTS;
191 : *
192 : * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
193 : *
194 : * @Description
195 : * This is my replacement for getopt(). See the example for a typical usage.
196 : * Global flags are:
197 : * Bit 0 : Do not remove options form argv
198 : * Bit 1 : Do not stop at last option but return other args
199 : * with r_opt set to -1.
200 : * Bit 2 : Assume options and real args are mixed.
201 : * Bit 3 : Do not use -- to stop option processing.
202 : * Bit 4 : Do not skip the first arg.
203 : * Bit 5 : allow usage of long option with only one dash
204 : * Bit 6 : ignore --version
205 : * all other bits must be set to zero, this value is modified by the
206 : * function, so assume this is write only.
207 : * Local flags (for each option):
208 : * Bit 2-0 : 0 = does not take an argument
209 : * 1 = takes int argument
210 : * 2 = takes string argument
211 : * 3 = takes long argument
212 : * 4 = takes ulong argument
213 : * Bit 3 : argument is optional (r_type will the be set to 0)
214 : * Bit 4 : allow 0x etc. prefixed values.
215 : * Bit 6 : Ignore this option
216 : * Bit 7 : This is a command and not an option
217 : * You stop the option processing by setting opts to NULL, the function will
218 : * then return 0.
219 : * @Return Value
220 : * Returns the args.r_opt or 0 if ready
221 : * r_opt may be -2/-7 to indicate an unknown option/command.
222 : * @See Also
223 : * ArgExpand
224 : * @Notes
225 : * You do not need to process the options 'h', '--help' or '--version'
226 : * because this function includes standard help processing; but if you
227 : * specify '-h', '--help' or '--version' you have to do it yourself.
228 : * The option '--' stops argument processing; if bit 1 is set the function
229 : * continues to return normal arguments.
230 : * To process float args or unsigned args you must use a string args and do
231 : * the conversion yourself.
232 : * @Example
233 : *
234 : * ARGPARSE_OPTS opts[] = {
235 : * { 'v', "verbose", 0 },
236 : * { 'd', "debug", 0 },
237 : * { 'o', "output", 2 },
238 : * { 'c', "cross-ref", 2|8 },
239 : * { 'm', "my-option", 1|8 },
240 : * { 300, "ignored-long-option, ARGPARSE_OP_IGNORE},
241 : * { 500, "have-no-short-option-for-this-long-option", 0 },
242 : * {0} };
243 : * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
244 : *
245 : * while( ArgParse( &pargs, &opts) ) {
246 : * switch( pargs.r_opt ) {
247 : * case 'v': opt.verbose++; break;
248 : * case 'd': opt.debug++; break;
249 : * case 'o': opt.outfile = pargs.r.ret_str; break;
250 : * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
251 : * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
252 : * case 500: opt.a_long_one++; break
253 : * default : pargs.err = 1; break; -- force warning output --
254 : * }
255 : * }
256 : * if( argc > 1 )
257 : * log_fatal( "Too many args");
258 : *
259 : */
260 :
261 : typedef struct alias_def_s *ALIAS_DEF;
262 : struct alias_def_s {
263 : ALIAS_DEF next;
264 : char *name; /* malloced buffer with name, \0, value */
265 : const char *value; /* ptr into name */
266 : };
267 :
268 :
269 : /* Object to store the names for the --ignore-invalid-option option.
270 : This is a simple linked list. */
271 : typedef struct iio_item_def_s *IIO_ITEM_DEF;
272 : struct iio_item_def_s
273 : {
274 : IIO_ITEM_DEF next;
275 : char name[1]; /* String with the long option name. */
276 : };
277 :
278 : static const char *(*strusage_handler)( int ) = NULL;
279 : static int (*custom_outfnc) (int, const char *);
280 :
281 : static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
282 : static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
283 : static void show_version(void);
284 : static int writestrings (int is_error, const char *string, ...)
285 : #if __GNUC__ >= 4
286 : __attribute__ ((sentinel(0)))
287 : #endif
288 : ;
289 :
290 :
291 : void
292 2340 : argparse_register_outfnc (int (*fnc)(int, const char *))
293 : {
294 2340 : custom_outfnc = fnc;
295 2340 : }
296 :
297 :
298 : /* Write STRING and all following const char * arguments either to
299 : stdout or, if IS_ERROR is set, to stderr. The list of strings must
300 : be terminated by a NULL. */
301 : static int
302 47413 : writestrings (int is_error, const char *string, ...)
303 : {
304 : va_list arg_ptr;
305 : const char *s;
306 47413 : int count = 0;
307 :
308 47413 : if (string)
309 : {
310 47413 : s = string;
311 47413 : va_start (arg_ptr, string);
312 : do
313 : {
314 136654 : if (custom_outfnc)
315 136654 : custom_outfnc (is_error? 2:1, s);
316 : else
317 0 : fputs (s, is_error? stderr : stdout);
318 136654 : count += strlen (s);
319 : }
320 136654 : while ((s = va_arg (arg_ptr, const char *)));
321 47413 : va_end (arg_ptr);
322 : }
323 47413 : return count;
324 : }
325 :
326 :
327 : static void
328 9 : flushstrings (int is_error)
329 : {
330 9 : if (custom_outfnc)
331 9 : custom_outfnc (is_error? 2:1, NULL);
332 : else
333 0 : fflush (is_error? stderr : stdout);
334 9 : }
335 :
336 :
337 : static void
338 37828 : initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
339 : {
340 37828 : if( !(arg->flags & (1<<15)) )
341 : {
342 : /* Initialize this instance. */
343 4101 : arg->internal.idx = 0;
344 4101 : arg->internal.last = NULL;
345 4101 : arg->internal.inarg = 0;
346 4101 : arg->internal.stopped = 0;
347 4101 : arg->internal.aliases = NULL;
348 4101 : arg->internal.cur_alias = NULL;
349 4101 : arg->internal.iio_list = NULL;
350 4101 : arg->err = 0;
351 4101 : arg->flags |= 1<<15; /* Mark as initialized. */
352 4101 : if ( *arg->argc < 0 )
353 0 : log_bug ("invalid argument for arg_parse\n");
354 : }
355 :
356 :
357 37828 : if (arg->err)
358 : {
359 : /* Last option was erroneous. */
360 : const char *s;
361 :
362 0 : if (filename)
363 : {
364 0 : if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
365 0 : s = _("argument not expected");
366 0 : else if ( arg->r_opt == ARGPARSE_READ_ERROR )
367 0 : s = _("read error");
368 0 : else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG )
369 0 : s = _("keyword too long");
370 0 : else if ( arg->r_opt == ARGPARSE_MISSING_ARG )
371 0 : s = _("missing argument");
372 0 : else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
373 0 : s = _("invalid argument");
374 0 : else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
375 0 : s = _("invalid command");
376 0 : else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS )
377 0 : s = _("invalid alias definition");
378 0 : else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
379 0 : s = _("out of core");
380 : else
381 0 : s = _("invalid option");
382 0 : log_error ("%s:%u: %s\n", filename, *lineno, s);
383 : }
384 : else
385 : {
386 0 : s = arg->internal.last? arg->internal.last:"[??]";
387 :
388 0 : if ( arg->r_opt == ARGPARSE_MISSING_ARG )
389 0 : log_error (_("missing argument for option \"%.50s\"\n"), s);
390 0 : else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
391 0 : log_error (_("invalid argument for option \"%.50s\"\n"), s);
392 0 : else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
393 0 : log_error (_("option \"%.50s\" does not expect an argument\n"), s);
394 0 : else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
395 0 : log_error (_("invalid command \"%.50s\"\n"), s);
396 0 : else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
397 0 : log_error (_("option \"%.50s\" is ambiguous\n"), s);
398 0 : else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND )
399 0 : log_error (_("command \"%.50s\" is ambiguous\n"),s );
400 0 : else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
401 0 : log_error ("%s\n", _("out of core\n"));
402 : else
403 0 : log_error (_("invalid option \"%.50s\"\n"), s);
404 : }
405 0 : if (arg->err != ARGPARSE_PRINT_WARNING)
406 0 : exit (2);
407 0 : arg->err = 0;
408 : }
409 :
410 : /* Zero out the return value union. */
411 37828 : arg->r.ret_str = NULL;
412 37828 : arg->r.ret_long = 0;
413 37828 : }
414 :
415 :
416 : static void
417 0 : store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
418 : {
419 : /* TODO: replace this dummy function with a rea one
420 : * and fix the probelms IRIX has with (ALIAS_DEV)arg..
421 : * used as lvalue
422 : */
423 : (void)arg;
424 : (void)name;
425 : (void)value;
426 : #if 0
427 : ALIAS_DEF a = xmalloc( sizeof *a );
428 : a->name = name;
429 : a->value = value;
430 : a->next = (ALIAS_DEF)arg->internal.aliases;
431 : (ALIAS_DEF)arg->internal.aliases = a;
432 : #endif
433 0 : }
434 :
435 :
436 : /* Return true if KEYWORD is in the ignore-invalid-option list. */
437 : static int
438 0 : ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword)
439 : {
440 0 : IIO_ITEM_DEF item = arg->internal.iio_list;
441 :
442 0 : for (; item; item = item->next)
443 0 : if (!strcmp (item->name, keyword))
444 0 : return 1;
445 0 : return 0;
446 : }
447 :
448 :
449 : /* Add the keywords up to the next LF to the list of to be ignored
450 : options. After returning FP will either be at EOF or the next
451 : character read wll be the first of a new line. The function
452 : returns 0 on success or true on malloc failure. */
453 : static int
454 0 : ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
455 : {
456 : IIO_ITEM_DEF item;
457 : int c;
458 : char name[100];
459 0 : int namelen = 0;
460 0 : int ready = 0;
461 0 : enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS;
462 :
463 0 : while (!ready)
464 : {
465 0 : c = getc (fp);
466 0 : if (c == '\n')
467 0 : ready = 1;
468 0 : else if (c == EOF)
469 : {
470 0 : c = '\n';
471 0 : ready = 1;
472 : }
473 : again:
474 0 : switch (state)
475 : {
476 : case skipWS:
477 0 : if (!isascii (c) || !isspace(c))
478 : {
479 0 : namelen = 0;
480 0 : state = collectNAME;
481 0 : goto again;
482 : }
483 0 : break;
484 :
485 : case collectNAME:
486 0 : if (isspace (c))
487 : {
488 0 : state = addNAME;
489 0 : goto again;
490 : }
491 0 : else if (namelen < DIM(name)-1)
492 0 : name[namelen++] = c;
493 : else /* Too long. */
494 0 : state = skipNAME;
495 0 : break;
496 :
497 : case skipNAME:
498 0 : if (isspace (c))
499 : {
500 0 : state = skipWS;
501 0 : goto again;
502 : }
503 0 : break;
504 :
505 : case addNAME:
506 0 : name[namelen] = 0;
507 0 : if (!ignore_invalid_option_p (arg, name))
508 : {
509 0 : item = xtrymalloc (sizeof *item + namelen);
510 0 : if (!item)
511 0 : return 1;
512 0 : strcpy (item->name, name);
513 0 : item->next = (IIO_ITEM_DEF)arg->internal.iio_list;
514 0 : arg->internal.iio_list = item;
515 : }
516 0 : state = skipWS;
517 0 : goto again;
518 : }
519 : }
520 0 : return 0;
521 : }
522 :
523 :
524 : /* Clear the entire ignore-invalid-option list. */
525 : static void
526 1732 : ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
527 : {
528 : IIO_ITEM_DEF item, tmpitem;
529 :
530 1732 : for (item = arg->internal.iio_list; item; item = tmpitem)
531 : {
532 0 : tmpitem = item->next;
533 0 : xfree (item);
534 : }
535 1732 : arg->internal.iio_list = NULL;
536 1732 : }
537 :
538 :
539 :
540 : /****************
541 : * Get options from a file.
542 : * Lines starting with '#' are comment lines.
543 : * Syntax is simply a keyword and the argument.
544 : * Valid keywords are all keywords from the long_opt list without
545 : * the leading dashes. The special keywords "help", "warranty" and "version"
546 : * are not valid here.
547 : * The special keyword "alias" may be used to store alias definitions,
548 : * which are later expanded like long options.
549 : * The option
550 : * ignore-invalid-option OPTIONNAMEs
551 : * is recognized and updates a list of option which should be ignored if they
552 : * are not defined.
553 : * Caller must free returned strings.
554 : * If called with FP set to NULL command line args are parse instead.
555 : *
556 : * Q: Should we allow the syntax
557 : * keyword = value
558 : * and accept for boolean options a value of 1/0, yes/no or true/false?
559 : * Note: Abbreviation of options is here not allowed.
560 : */
561 : int
562 25959 : optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
563 : ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
564 : {
565 : int state, i, c;
566 25959 : int idx=0;
567 : char keyword[100];
568 25959 : char *buffer = NULL;
569 25959 : size_t buflen = 0;
570 25959 : int in_alias=0;
571 :
572 25959 : if (!fp) /* Divert to to arg_parse() in this case. */
573 11869 : return arg_parse (arg, opts);
574 :
575 14090 : initialize (arg, filename, lineno);
576 :
577 : /* Find the next keyword. */
578 14090 : state = i = 0;
579 : for (;;)
580 : {
581 605131 : c = getc (fp);
582 605131 : if (c == '\n' || c== EOF )
583 : {
584 23090 : if ( c != EOF )
585 21358 : ++*lineno;
586 23090 : if (state == -1)
587 0 : break;
588 23090 : else if (state == 2)
589 : {
590 8373 : keyword[i] = 0;
591 1796658 : for (i=0; opts[i].short_opt; i++ )
592 : {
593 1796658 : if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
594 8373 : break;
595 : }
596 8373 : idx = i;
597 8373 : arg->r_opt = opts[idx].short_opt;
598 8373 : if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
599 : {
600 0 : state = i = 0;
601 0 : continue;
602 : }
603 8373 : else if (!opts[idx].short_opt )
604 : {
605 0 : if (!strcmp (keyword, "ignore-invalid-option"))
606 : {
607 : /* No argument - ignore this meta option. */
608 0 : state = i = 0;
609 0 : continue;
610 : }
611 0 : else if (ignore_invalid_option_p (arg, keyword))
612 : {
613 : /* This invalid option is in the iio list. */
614 0 : state = i = 0;
615 0 : continue;
616 : }
617 0 : arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
618 : ? ARGPARSE_INVALID_COMMAND
619 0 : : ARGPARSE_INVALID_OPTION);
620 : }
621 8373 : else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
622 8373 : arg->r_type = 0; /* Does not take an arg. */
623 0 : else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
624 0 : arg->r_type = 0; /* Arg is optional. */
625 : else
626 0 : arg->r_opt = ARGPARSE_MISSING_ARG;
627 :
628 8373 : break;
629 : }
630 14717 : else if (state == 3)
631 : {
632 : /* No argument found. */
633 0 : if (in_alias)
634 0 : arg->r_opt = ARGPARSE_MISSING_ARG;
635 0 : else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
636 0 : arg->r_type = 0; /* Does not take an arg. */
637 0 : else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
638 0 : arg->r_type = 0; /* No optional argument. */
639 : else
640 0 : arg->r_opt = ARGPARSE_MISSING_ARG;
641 :
642 0 : break;
643 : }
644 14717 : else if (state == 4)
645 : {
646 : /* Has an argument. */
647 3985 : if (in_alias)
648 : {
649 0 : if (!buffer)
650 0 : arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
651 : else
652 : {
653 : char *p;
654 :
655 0 : buffer[i] = 0;
656 0 : p = strpbrk (buffer, " \t");
657 0 : if (p)
658 : {
659 0 : *p++ = 0;
660 0 : trim_spaces (p);
661 : }
662 0 : if (!p || !*p)
663 : {
664 0 : xfree (buffer);
665 0 : arg->r_opt = ARGPARSE_INVALID_ALIAS;
666 : }
667 : else
668 : {
669 0 : store_alias (arg, buffer, p);
670 : }
671 : }
672 : }
673 3985 : else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
674 0 : arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
675 : else
676 : {
677 : char *p;
678 :
679 3985 : if (!buffer)
680 : {
681 3985 : keyword[i] = 0;
682 3985 : buffer = xtrystrdup (keyword);
683 3985 : if (!buffer)
684 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
685 : }
686 : else
687 0 : buffer[i] = 0;
688 :
689 3985 : if (buffer)
690 : {
691 3985 : trim_spaces (buffer);
692 3985 : p = buffer;
693 3985 : if (*p == '"')
694 : {
695 : /* Remove quotes. */
696 0 : p++;
697 0 : if (*p && p[strlen(p)-1] == '\"' )
698 0 : p[strlen(p)-1] = 0;
699 : }
700 3985 : if (!set_opt_arg (arg, opts[idx].flags, p))
701 0 : xfree (buffer);
702 : else
703 3985 : gpgrt_annotate_leaked_object (buffer);
704 : }
705 : }
706 3985 : break;
707 : }
708 10732 : else if (c == EOF)
709 : {
710 1732 : ignore_invalid_option_clear (arg);
711 1732 : if (ferror (fp))
712 0 : arg->r_opt = ARGPARSE_READ_ERROR;
713 : else
714 1732 : arg->r_opt = 0; /* EOF. */
715 1732 : break;
716 : }
717 9000 : state = 0;
718 9000 : i = 0;
719 : }
720 582041 : else if (state == -1)
721 : ; /* Skip. */
722 582041 : else if (state == 0 && isascii (c) && isspace(c))
723 : ; /* Skip leading white space. */
724 582041 : else if (state == 0 && c == '#' )
725 7125 : state = 1; /* Start of a comment. */
726 574916 : else if (state == 1)
727 : ; /* Skip comments. */
728 369791 : else if (state == 2 && isascii (c) && isspace(c))
729 : {
730 : /* Check keyword. */
731 3985 : keyword[i] = 0;
732 948774 : for (i=0; opts[i].short_opt; i++ )
733 948774 : if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
734 3985 : break;
735 3985 : idx = i;
736 3985 : arg->r_opt = opts[idx].short_opt;
737 7970 : if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
738 : {
739 0 : state = 1; /* Process like a comment. */
740 : }
741 3985 : else if (!opts[idx].short_opt)
742 : {
743 0 : if (!strcmp (keyword, "alias"))
744 : {
745 0 : in_alias = 1;
746 0 : state = 3;
747 : }
748 0 : else if (!strcmp (keyword, "ignore-invalid-option"))
749 : {
750 0 : if (ignore_invalid_option_add (arg, fp))
751 : {
752 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
753 0 : break;
754 : }
755 0 : state = i = 0;
756 0 : ++*lineno;
757 : }
758 0 : else if (ignore_invalid_option_p (arg, keyword))
759 0 : state = 1; /* Process like a comment. */
760 : else
761 : {
762 0 : arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
763 : ? ARGPARSE_INVALID_COMMAND
764 0 : : ARGPARSE_INVALID_OPTION);
765 0 : state = -1; /* Skip rest of line and leave. */
766 : }
767 : }
768 : else
769 3985 : state = 3;
770 : }
771 365806 : else if (state == 3)
772 : {
773 : /* Skip leading spaces of the argument. */
774 4360 : if (!isascii (c) || !isspace(c))
775 : {
776 3985 : i = 0;
777 3985 : keyword[i++] = c;
778 3985 : state = 4;
779 : }
780 : }
781 361446 : else if (state == 4)
782 : {
783 : /* Collect the argument. */
784 185275 : if (buffer)
785 : {
786 0 : if (i < buflen-1)
787 0 : buffer[i++] = c;
788 : else
789 : {
790 : char *tmp;
791 0 : size_t tmplen = buflen + 50;
792 :
793 0 : tmp = xtryrealloc (buffer, tmplen);
794 0 : if (tmp)
795 : {
796 0 : buflen = tmplen;
797 0 : buffer = tmp;
798 0 : buffer[i++] = c;
799 : }
800 : else
801 : {
802 0 : xfree (buffer);
803 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
804 0 : break;
805 : }
806 : }
807 : }
808 185275 : else if (i < DIM(keyword)-1)
809 185275 : keyword[i++] = c;
810 : else
811 : {
812 0 : size_t tmplen = DIM(keyword) + 50;
813 0 : buffer = xtrymalloc (tmplen);
814 0 : if (buffer)
815 : {
816 0 : buflen = tmplen;
817 0 : memcpy(buffer, keyword, i);
818 0 : buffer[i++] = c;
819 : }
820 : else
821 : {
822 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
823 0 : break;
824 : }
825 : }
826 : }
827 176171 : else if (i >= DIM(keyword)-1)
828 : {
829 0 : arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
830 0 : state = -1; /* Skip rest of line and leave. */
831 : }
832 : else
833 : {
834 176171 : keyword[i++] = c;
835 176171 : state = 2;
836 : }
837 591041 : }
838 :
839 14090 : return arg->r_opt;
840 : }
841 :
842 :
843 :
844 : static int
845 18930 : find_long_option( ARGPARSE_ARGS *arg,
846 : ARGPARSE_OPTS *opts, const char *keyword )
847 : {
848 : int i;
849 : size_t n;
850 :
851 : (void)arg;
852 :
853 : /* Would be better if we can do a binary search, but it is not
854 : possible to reorder our option table because we would mess
855 : up our help strings - What we can do is: Build a nice option
856 : lookup table when this function is first invoked */
857 18930 : if( !*keyword )
858 0 : return -1;
859 2960437 : for(i=0; opts[i].short_opt; i++ )
860 2960289 : if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
861 18782 : return i;
862 : #if 0
863 : {
864 : ALIAS_DEF a;
865 : /* see whether it is an alias */
866 : for( a = args->internal.aliases; a; a = a->next ) {
867 : if( !strcmp( a->name, keyword) ) {
868 : /* todo: must parse the alias here */
869 : args->internal.cur_alias = a;
870 : return -3; /* alias available */
871 : }
872 : }
873 : }
874 : #endif
875 : /* not found, see whether it is an abbreviation */
876 : /* aliases may not be abbreviated */
877 148 : n = strlen( keyword );
878 46986 : for(i=0; opts[i].short_opt; i++ ) {
879 46856 : if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
880 : int j;
881 5826 : for(j=i+1; opts[j].short_opt; j++ ) {
882 5808 : if( opts[j].long_opt
883 5724 : && !strncmp( opts[j].long_opt, keyword, n ) )
884 0 : return -2; /* abbreviation is ambiguous */
885 : }
886 18 : return i;
887 : }
888 : }
889 130 : return -1; /* Not found. */
890 : }
891 :
892 : int
893 23738 : arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
894 : {
895 : int idx;
896 : int argc;
897 : char **argv;
898 : char *s, *s2;
899 : int i;
900 :
901 23738 : initialize( arg, NULL, NULL );
902 23738 : argc = *arg->argc;
903 23738 : argv = *arg->argv;
904 23738 : idx = arg->internal.idx;
905 :
906 23738 : if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
907 : {
908 : /* Skip the first argument. */
909 4093 : argc--; argv++; idx++;
910 : }
911 :
912 : next_one:
913 27443 : if (!argc)
914 : {
915 : /* No more args. */
916 267 : arg->r_opt = 0;
917 267 : goto leave; /* Ready. */
918 : }
919 :
920 27176 : s = *argv;
921 27176 : arg->internal.last = s;
922 :
923 27176 : if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
924 : {
925 0 : arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */
926 0 : arg->r_type = 2;
927 0 : arg->r.ret_str = s;
928 0 : argc--; argv++; idx++; /* set to next one */
929 : }
930 27176 : else if( arg->internal.stopped )
931 : {
932 3705 : arg->r_opt = 0;
933 3705 : goto leave; /* Ready. */
934 : }
935 23471 : else if ( *s == '-' && s[1] == '-' )
936 18801 : {
937 : /* Long option. */
938 : char *argpos;
939 :
940 18930 : arg->internal.inarg = 0;
941 18930 : if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
942 : {
943 : /* Stop option processing. */
944 0 : arg->internal.stopped = 1;
945 0 : arg->flags |= ARGPARSE_FLAG_STOP_SEEN;
946 0 : argc--; argv++; idx++;
947 0 : goto next_one;
948 : }
949 :
950 18930 : argpos = strchr( s+2, '=' );
951 18930 : if ( argpos )
952 1267 : *argpos = 0;
953 18930 : i = find_long_option ( arg, opts, s+2 );
954 18930 : if ( argpos )
955 1267 : *argpos = '=';
956 :
957 18930 : if ( i < 0 && !strcmp ( "help", s+2) )
958 4 : show_help (opts, arg->flags);
959 18926 : else if ( i < 0 && !strcmp ( "version", s+2) )
960 : {
961 3 : if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
962 : {
963 1 : show_version ();
964 1 : exit(0);
965 : }
966 : }
967 18924 : else if ( i < 0 && !strcmp( "warranty", s+2))
968 : {
969 0 : writestrings (0, strusage (16), "\n", NULL);
970 0 : exit (0);
971 : }
972 18924 : else if ( i < 0 && !strcmp( "dump-options", s+2) )
973 : {
974 45384 : for (i=0; opts[i].short_opt; i++ )
975 : {
976 45260 : if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
977 44516 : writestrings (0, "--", opts[i].long_opt, "\n", NULL);
978 : }
979 124 : writestrings (0, "--dump-options\n--help\n--version\n--warranty\n",
980 : NULL);
981 124 : exit (0);
982 : }
983 :
984 18801 : if ( i == -2 )
985 0 : arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
986 18801 : else if ( i == -1 )
987 : {
988 1 : arg->r_opt = ARGPARSE_INVALID_OPTION;
989 1 : arg->r.ret_str = s+2;
990 : }
991 : else
992 18800 : arg->r_opt = opts[i].short_opt;
993 18801 : if ( i < 0 )
994 : ;
995 18800 : else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
996 : {
997 6302 : if ( argpos )
998 : {
999 1267 : s2 = argpos+1;
1000 1267 : if ( !*s2 )
1001 0 : s2 = NULL;
1002 : }
1003 : else
1004 5035 : s2 = argv[1];
1005 6302 : if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1006 : {
1007 0 : arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */
1008 : }
1009 6302 : else if ( !s2 )
1010 : {
1011 0 : arg->r_opt = ARGPARSE_MISSING_ARG;
1012 : }
1013 6302 : else if ( !argpos && *s2 == '-'
1014 206 : && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1015 : {
1016 : /* The argument is optional and the next seems to be an
1017 : option. We do not check this possible option but
1018 : assume no argument */
1019 0 : arg->r_type = ARGPARSE_TYPE_NONE;
1020 : }
1021 : else
1022 : {
1023 6302 : set_opt_arg (arg, opts[i].flags, s2);
1024 6302 : if ( !argpos )
1025 : {
1026 5035 : argc--; argv++; idx++; /* Skip one. */
1027 : }
1028 : }
1029 : }
1030 : else
1031 : {
1032 : /* Does not take an argument. */
1033 12498 : if ( argpos )
1034 0 : arg->r_type = ARGPARSE_UNEXPECTED_ARG;
1035 : else
1036 12498 : arg->r_type = 0;
1037 : }
1038 18801 : argc--; argv++; idx++; /* Set to next one. */
1039 : }
1040 4541 : else if ( (*s == '-' && s[1]) || arg->internal.inarg )
1041 836 : {
1042 : /* Short option. */
1043 836 : int dash_kludge = 0;
1044 :
1045 836 : i = 0;
1046 836 : if ( !arg->internal.inarg )
1047 : {
1048 654 : arg->internal.inarg++;
1049 654 : if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
1050 : {
1051 0 : for (i=0; opts[i].short_opt; i++ )
1052 0 : if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
1053 : {
1054 0 : dash_kludge = 1;
1055 0 : break;
1056 : }
1057 : }
1058 : }
1059 836 : s += arg->internal.inarg;
1060 :
1061 836 : if (!dash_kludge )
1062 : {
1063 14818 : for (i=0; opts[i].short_opt; i++ )
1064 14818 : if ( opts[i].short_opt == *s )
1065 836 : break;
1066 : }
1067 :
1068 836 : if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
1069 0 : show_help (opts, arg->flags);
1070 :
1071 836 : arg->r_opt = opts[i].short_opt;
1072 836 : if (!opts[i].short_opt )
1073 : {
1074 0 : arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
1075 0 : ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
1076 0 : arg->internal.inarg++; /* Point to the next arg. */
1077 0 : arg->r.ret_str = s;
1078 : }
1079 836 : else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
1080 : {
1081 50 : if ( s[1] && !dash_kludge )
1082 : {
1083 0 : s2 = s+1;
1084 0 : set_opt_arg (arg, opts[i].flags, s2);
1085 : }
1086 : else
1087 : {
1088 50 : s2 = argv[1];
1089 50 : if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1090 : {
1091 0 : arg->r_type = ARGPARSE_TYPE_NONE;
1092 : }
1093 50 : else if ( !s2 )
1094 : {
1095 0 : arg->r_opt = ARGPARSE_MISSING_ARG;
1096 : }
1097 50 : else if ( *s2 == '-' && s2[1]
1098 0 : && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1099 : {
1100 : /* The argument is optional and the next seems to
1101 : be an option. We do not check this possible
1102 : option but assume no argument. */
1103 0 : arg->r_type = ARGPARSE_TYPE_NONE;
1104 : }
1105 : else
1106 : {
1107 50 : set_opt_arg (arg, opts[i].flags, s2);
1108 50 : argc--; argv++; idx++; /* Skip one. */
1109 : }
1110 : }
1111 50 : s = "x"; /* This is so that !s[1] yields false. */
1112 : }
1113 : else
1114 : {
1115 : /* Does not take an argument. */
1116 786 : arg->r_type = ARGPARSE_TYPE_NONE;
1117 786 : arg->internal.inarg++; /* Point to the next arg. */
1118 : }
1119 836 : if ( !s[1] || dash_kludge )
1120 : {
1121 : /* No more concatenated short options. */
1122 654 : arg->internal.inarg = 0;
1123 654 : argc--; argv++; idx++;
1124 : }
1125 : }
1126 3705 : else if ( arg->flags & ARGPARSE_FLAG_MIXED )
1127 : {
1128 0 : arg->r_opt = ARGPARSE_IS_ARG;
1129 0 : arg->r_type = 2;
1130 0 : arg->r.ret_str = s;
1131 0 : argc--; argv++; idx++; /* Set to next one. */
1132 : }
1133 : else
1134 : {
1135 3705 : arg->internal.stopped = 1; /* Stop option processing. */
1136 3705 : goto next_one;
1137 : }
1138 :
1139 : leave:
1140 23609 : *arg->argc = argc;
1141 23609 : *arg->argv = argv;
1142 23609 : arg->internal.idx = idx;
1143 23609 : return arg->r_opt;
1144 : }
1145 :
1146 :
1147 : /* Returns: -1 on error, 0 for an integer type and 1 for a non integer
1148 : type argument. */
1149 : static int
1150 10337 : set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
1151 : {
1152 10337 : int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
1153 : long l;
1154 :
1155 10337 : switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
1156 : {
1157 : case ARGPARSE_TYPE_LONG:
1158 : case ARGPARSE_TYPE_INT:
1159 1918 : errno = 0;
1160 1918 : l = strtol (s, NULL, base);
1161 1918 : if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
1162 : {
1163 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1164 0 : return -1;
1165 : }
1166 1918 : if (arg->r_type == ARGPARSE_TYPE_LONG)
1167 0 : arg->r.ret_long = l;
1168 1918 : else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
1169 : {
1170 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1171 0 : return -1;
1172 : }
1173 : else
1174 1918 : arg->r.ret_int = (int)l;
1175 1918 : return 0;
1176 :
1177 : case ARGPARSE_TYPE_ULONG:
1178 0 : while (isascii (*s) && isspace(*s))
1179 0 : s++;
1180 0 : if (*s == '-')
1181 : {
1182 0 : arg->r.ret_ulong = 0;
1183 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1184 0 : return -1;
1185 : }
1186 0 : errno = 0;
1187 0 : arg->r.ret_ulong = strtoul (s, NULL, base);
1188 0 : if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
1189 : {
1190 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1191 0 : return -1;
1192 : }
1193 0 : return 0;
1194 :
1195 : case ARGPARSE_TYPE_STRING:
1196 : default:
1197 8419 : arg->r.ret_str = s;
1198 8419 : return 1;
1199 : }
1200 : }
1201 :
1202 :
1203 : static size_t
1204 68 : long_opt_strlen( ARGPARSE_OPTS *o )
1205 : {
1206 68 : size_t n = strlen (o->long_opt);
1207 :
1208 68 : if ( o->description && *o->description == '|' )
1209 : {
1210 : const char *s;
1211 20 : int is_utf8 = is_native_utf8 ();
1212 :
1213 20 : s=o->description+1;
1214 20 : if ( *s != '=' )
1215 20 : n++;
1216 : /* For a (mostly) correct length calculation we exclude
1217 : continuation bytes (10xxxxxx) if we are on a native utf8
1218 : terminal. */
1219 144 : for (; *s && *s != '|'; s++ )
1220 124 : if ( is_utf8 && (*s&0xc0) != 0x80 )
1221 0 : n++;
1222 : }
1223 68 : return n;
1224 : }
1225 :
1226 :
1227 : /****************
1228 : * Print formatted help. The description string has some special
1229 : * meanings:
1230 : * - A description string which is "@" suppresses help output for
1231 : * this option
1232 : * - a description,ine which starts with a '@' and is followed by
1233 : * any other characters is printed as is; this may be used for examples
1234 : * ans such.
1235 : * - A description which starts with a '|' outputs the string between this
1236 : * bar and the next one as arguments of the long option.
1237 : */
1238 : static void
1239 4 : show_help (ARGPARSE_OPTS *opts, unsigned int flags)
1240 : {
1241 : const char *s;
1242 : char tmp[2];
1243 :
1244 4 : show_version ();
1245 4 : writestrings (0, "\n", NULL);
1246 4 : s = strusage (42);
1247 4 : if (s && *s == '1')
1248 : {
1249 0 : s = strusage (40);
1250 0 : writestrings (1, s, NULL);
1251 0 : if (*s && s[strlen(s)] != '\n')
1252 0 : writestrings (1, "\n", NULL);
1253 : }
1254 4 : s = strusage(41);
1255 4 : writestrings (0, s, "\n", NULL);
1256 4 : if ( opts[0].description )
1257 : {
1258 : /* Auto format the option description. */
1259 : int i,j, indent;
1260 :
1261 : /* Get max. length of long options. */
1262 108 : for (i=indent=0; opts[i].short_opt; i++ )
1263 : {
1264 104 : if ( opts[i].long_opt )
1265 92 : if ( !opts[i].description || *opts[i].description != '@' )
1266 68 : if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
1267 12 : indent = j;
1268 : }
1269 :
1270 : /* Example: " -v, --verbose Viele Sachen ausgeben" */
1271 4 : indent += 10;
1272 4 : if ( *opts[0].description != '@' )
1273 0 : writestrings (0, "Options:", "\n", NULL);
1274 108 : for (i=0; opts[i].short_opt; i++ )
1275 : {
1276 104 : s = map_static_macro_string (_( opts[i].description ));
1277 104 : if ( s && *s== '@' && !s[1] ) /* Hide this line. */
1278 24 : continue;
1279 80 : if ( s && *s == '@' ) /* Unindented comment only line. */
1280 : {
1281 160 : for (s++; *s; s++ )
1282 : {
1283 148 : if ( *s == '\n' )
1284 : {
1285 20 : if( s[1] )
1286 20 : writestrings (0, "\n", NULL);
1287 : }
1288 : else
1289 : {
1290 128 : tmp[0] = *s;
1291 128 : tmp[1] = 0;
1292 128 : writestrings (0, tmp, NULL);
1293 : }
1294 : }
1295 12 : writestrings (0, "\n", NULL);
1296 12 : continue;
1297 : }
1298 :
1299 68 : j = 3;
1300 68 : if ( opts[i].short_opt < 256 )
1301 : {
1302 48 : tmp[0] = opts[i].short_opt;
1303 48 : tmp[1] = 0;
1304 48 : writestrings (0, " -", tmp, NULL );
1305 48 : if ( !opts[i].long_opt )
1306 : {
1307 0 : if (s && *s == '|' )
1308 : {
1309 0 : writestrings (0, " ", NULL); j++;
1310 0 : for (s++ ; *s && *s != '|'; s++, j++ )
1311 : {
1312 0 : tmp[0] = *s;
1313 0 : tmp[1] = 0;
1314 0 : writestrings (0, tmp, NULL);
1315 : }
1316 0 : if ( *s )
1317 0 : s++;
1318 : }
1319 : }
1320 : }
1321 : else
1322 20 : writestrings (0, " ", NULL);
1323 68 : if ( opts[i].long_opt )
1324 : {
1325 68 : tmp[0] = opts[i].short_opt < 256?',':' ';
1326 68 : tmp[1] = 0;
1327 68 : j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
1328 68 : if (s && *s == '|' )
1329 : {
1330 20 : if ( *++s != '=' )
1331 : {
1332 20 : writestrings (0, " ", NULL);
1333 20 : j++;
1334 : }
1335 144 : for ( ; *s && *s != '|'; s++, j++ )
1336 : {
1337 124 : tmp[0] = *s;
1338 124 : tmp[1] = 0;
1339 124 : writestrings (0, tmp, NULL);
1340 : }
1341 20 : if ( *s )
1342 20 : s++;
1343 : }
1344 68 : writestrings (0, " ", NULL);
1345 68 : j += 3;
1346 : }
1347 304 : for (;j < indent; j++ )
1348 236 : writestrings (0, " ", NULL);
1349 68 : if ( s )
1350 : {
1351 68 : if ( *s && j > indent )
1352 : {
1353 16 : writestrings (0, "\n", NULL);
1354 368 : for (j=0;j < indent; j++ )
1355 352 : writestrings (0, " ", NULL);
1356 : }
1357 1612 : for (; *s; s++ )
1358 : {
1359 1544 : if ( *s == '\n' )
1360 : {
1361 0 : if ( s[1] )
1362 : {
1363 0 : writestrings (0, "\n", NULL);
1364 0 : for (j=0; j < indent; j++ )
1365 0 : writestrings (0, " ", NULL);
1366 : }
1367 : }
1368 : else
1369 : {
1370 1544 : tmp[0] = *s;
1371 1544 : tmp[1] = 0;
1372 1544 : writestrings (0, tmp, NULL);
1373 : }
1374 : }
1375 : }
1376 68 : writestrings (0, "\n", NULL);
1377 : }
1378 4 : if ( (flags & ARGPARSE_FLAG_ONEDASH) )
1379 0 : writestrings (0, "\n(A single dash may be used "
1380 : "instead of the double ones)\n", NULL);
1381 : }
1382 4 : if ( (s=strusage(19)) )
1383 : {
1384 4 : writestrings (0, "\n", NULL);
1385 4 : writestrings (0, s, NULL);
1386 : }
1387 4 : flushstrings (0);
1388 4 : exit(0);
1389 : }
1390 :
1391 : static void
1392 5 : show_version ()
1393 : {
1394 : const char *s;
1395 : int i;
1396 :
1397 : /* Version line. */
1398 5 : writestrings (0, strusage (11), NULL);
1399 5 : if ((s=strusage (12)))
1400 0 : writestrings (0, " (", s, ")", NULL);
1401 5 : writestrings (0, " ", strusage (13), "\n", NULL);
1402 : /* Additional version lines. */
1403 55 : for (i=20; i < 30; i++)
1404 50 : if ((s=strusage (i)))
1405 1 : writestrings (0, s, "\n", NULL);
1406 : /* Copyright string. */
1407 5 : if ((s=strusage (14)))
1408 5 : writestrings (0, s, "\n", NULL);
1409 : /* Licence string. */
1410 5 : if( (s=strusage (10)) )
1411 5 : writestrings (0, s, "\n", NULL);
1412 : /* Copying conditions. */
1413 5 : if ( (s=strusage(15)) )
1414 5 : writestrings (0, s, NULL);
1415 : /* Thanks. */
1416 5 : if ((s=strusage(18)))
1417 0 : writestrings (0, s, NULL);
1418 : /* Additional program info. */
1419 55 : for (i=30; i < 40; i++ )
1420 50 : if ( (s=strusage (i)) )
1421 7 : writestrings (0, s, NULL);
1422 5 : flushstrings (0);
1423 5 : }
1424 :
1425 :
1426 : void
1427 0 : usage (int level)
1428 : {
1429 : const char *p;
1430 :
1431 0 : if (!level)
1432 : {
1433 0 : writestrings (1, strusage(11), " ", strusage(13), "; ",
1434 : strusage (14), "\n", NULL);
1435 0 : flushstrings (1);
1436 : }
1437 0 : else if (level == 1)
1438 : {
1439 0 : p = strusage (40);
1440 0 : writestrings (1, p, NULL);
1441 0 : if (*p && p[strlen(p)] != '\n')
1442 0 : writestrings (1, "\n", NULL);
1443 0 : exit (2);
1444 : }
1445 0 : else if (level == 2)
1446 : {
1447 0 : p = strusage (42);
1448 0 : if (p && *p == '1')
1449 : {
1450 0 : p = strusage (40);
1451 0 : writestrings (1, p, NULL);
1452 0 : if (*p && p[strlen(p)] != '\n')
1453 0 : writestrings (1, "\n", NULL);
1454 : }
1455 0 : writestrings (0, strusage(41), "\n", NULL);
1456 0 : exit (0);
1457 : }
1458 0 : }
1459 :
1460 : /* Level
1461 : * 0: Print copyright string to stderr
1462 : * 1: Print a short usage hint to stderr and terminate
1463 : * 2: Print a long usage hint to stdout and terminate
1464 : * 10: Return license info string
1465 : * 11: Return the name of the program
1466 : * 12: Return optional name of package which includes this program.
1467 : * 13: version string
1468 : * 14: copyright string
1469 : * 15: Short copying conditions (with LFs)
1470 : * 16: Long copying conditions (with LFs)
1471 : * 17: Optional printable OS name
1472 : * 18: Optional thanks list (with LFs)
1473 : * 19: Bug report info
1474 : *20..29: Additional lib version strings.
1475 : *30..39: Additional program info (with LFs)
1476 : * 40: short usage note (with LF)
1477 : * 41: long usage note (with LF)
1478 : * 42: Flag string:
1479 : * First char is '1':
1480 : * The short usage notes needs to be printed
1481 : * before the long usage note.
1482 : */
1483 : const char *
1484 686 : strusage( int level )
1485 : {
1486 686 : const char *p = strusage_handler? strusage_handler(level) : NULL;
1487 :
1488 686 : if ( p )
1489 565 : return map_static_macro_string (p);
1490 :
1491 121 : switch ( level )
1492 : {
1493 :
1494 : case 10:
1495 : #if ARGPARSE_GPL_VERSION == 3
1496 5 : p = ("License GPLv3+: GNU GPL version 3 or later "
1497 : "<https://gnu.org/licenses/gpl.html>");
1498 : #else
1499 : p = ("License GPLv2+: GNU GPL version 2 or later "
1500 : "<https://gnu.org/licenses/>");
1501 : #endif
1502 5 : break;
1503 0 : case 11: p = "foo"; break;
1504 0 : case 13: p = "0.0"; break;
1505 5 : case 14: p = ARGPARSE_CRIGHT_STR; break;
1506 5 : case 15: p =
1507 : "This is free software: you are free to change and redistribute it.\n"
1508 : "There is NO WARRANTY, to the extent permitted by law.\n";
1509 5 : break;
1510 0 : case 16: p =
1511 : "This is free software; you can redistribute it and/or modify\n"
1512 : "it under the terms of the GNU General Public License as published by\n"
1513 : "the Free Software Foundation; either version "
1514 : ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
1515 : " of the License, or\n"
1516 : "(at your option) any later version.\n\n"
1517 : "It is distributed in the hope that it will be useful,\n"
1518 : "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1519 : "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1520 : "GNU General Public License for more details.\n\n"
1521 : "You should have received a copy of the GNU General Public License\n"
1522 : "along with this software. If not, see <https://gnu.org/licenses/>.\n";
1523 0 : break;
1524 : case 40: /* short and long usage */
1525 0 : case 41: p = ""; break;
1526 : }
1527 :
1528 121 : return p;
1529 : }
1530 :
1531 :
1532 : /* Set the usage handler. This function is basically a constructor. */
1533 : void
1534 2339 : set_strusage ( const char *(*f)( int ) )
1535 : {
1536 2339 : strusage_handler = f;
1537 2339 : }
1538 :
1539 :
1540 : #ifdef TEST
1541 : static struct {
1542 : int verbose;
1543 : int debug;
1544 : char *outfile;
1545 : char *crf;
1546 : int myopt;
1547 : int echo;
1548 : int a_long_one;
1549 : } opt;
1550 :
1551 : int
1552 : main(int argc, char **argv)
1553 : {
1554 : ARGPARSE_OPTS opts[] = {
1555 : ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
1556 : ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, "
1557 : "was wir eingegeben haben")),
1558 : ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
1559 : ARGPARSE_s_s('o', "output", 0 ),
1560 : ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
1561 : /* Note that on a non-utf8 terminal the ß might garble the output. */
1562 : ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
1563 : ARGPARSE_o_i('m', "my-option", 0),
1564 : ARGPARSE_s_n(500, "a-long-option", 0 ),
1565 : ARGPARSE_end()
1566 : };
1567 : ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL
1568 : | ARGPARSE_FLAG_MIXED
1569 : | ARGPARSE_FLAG_ONEDASH) };
1570 : int i;
1571 :
1572 : while (arg_parse (&pargs, opts))
1573 : {
1574 : switch (pargs.r_opt)
1575 : {
1576 : case ARGPARSE_IS_ARG :
1577 : printf ("arg='%s'\n", pargs.r.ret_str);
1578 : break;
1579 : case 'v': opt.verbose++; break;
1580 : case 'e': opt.echo++; break;
1581 : case 'd': opt.debug++; break;
1582 : case 'o': opt.outfile = pargs.r.ret_str; break;
1583 : case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1584 : case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1585 : case 500: opt.a_long_one++; break;
1586 : default : pargs.err = ARGPARSE_PRINT_WARNING; break;
1587 : }
1588 : }
1589 : for (i=0; i < argc; i++ )
1590 : printf ("%3d -> (%s)\n", i, argv[i] );
1591 : puts ("Options:");
1592 : if (opt.verbose)
1593 : printf (" verbose=%d\n", opt.verbose );
1594 : if (opt.debug)
1595 : printf (" debug=%d\n", opt.debug );
1596 : if (opt.outfile)
1597 : printf (" outfile='%s'\n", opt.outfile );
1598 : if (opt.crf)
1599 : printf (" crffile='%s'\n", opt.crf );
1600 : if (opt.myopt)
1601 : printf (" myopt=%d\n", opt.myopt );
1602 : if (opt.a_long_one)
1603 : printf (" a-long-one=%d\n", opt.a_long_one );
1604 : if (opt.echo)
1605 : printf (" echo=%d\n", opt.echo );
1606 :
1607 : return 0;
1608 : }
1609 : #endif /*TEST*/
1610 :
1611 : /**** bottom of file ****/
|