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-2015 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://www.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) 2015 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 0 : my_log_error (const char *fmt, ...)
105 : {
106 : va_list arg_ptr ;
107 :
108 0 : va_start (arg_ptr, fmt);
109 0 : fprintf (stderr, "%s: ", strusage (11));
110 0 : vfprintf (stderr, fmt, arg_ptr);
111 0 : va_end (arg_ptr);
112 0 : }
113 :
114 : static void
115 0 : my_log_bug (const char *fmt, ...)
116 : {
117 : va_list arg_ptr ;
118 :
119 0 : va_start (arg_ptr, fmt);
120 0 : fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11));
121 0 : vfprintf (stderr, fmt, arg_ptr);
122 0 : va_end (arg_ptr);
123 0 : abort ();
124 : }
125 :
126 : /* Return true if the native charset is utf-8. */
127 : static int
128 0 : is_native_utf8 (void)
129 : {
130 0 : return 1;
131 : }
132 :
133 : static char *
134 0 : my_trim_spaces (char *str)
135 : {
136 : char *string, *p, *mark;
137 :
138 0 : string = str;
139 : /* Find first non space character. */
140 0 : for (p=string; *p && isspace (*(unsigned char*)p) ; p++)
141 : ;
142 : /* Move characters. */
143 0 : for ((mark = NULL); (*string = *p); string++, p++)
144 0 : if (isspace (*(unsigned char*)p))
145 : {
146 0 : if (!mark)
147 0 : mark = string;
148 : }
149 : else
150 0 : mark = NULL;
151 0 : if (mark)
152 0 : *mark = '\0' ; /* Remove trailing spaces. */
153 :
154 0 : 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 0 : argparse_register_outfnc (int (*fnc)(int, const char *))
293 : {
294 0 : custom_outfnc = fnc;
295 0 : }
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 0 : writestrings (int is_error, const char *string, ...)
303 : {
304 : va_list arg_ptr;
305 : const char *s;
306 0 : int count = 0;
307 :
308 0 : if (string)
309 : {
310 0 : s = string;
311 0 : va_start (arg_ptr, string);
312 : do
313 : {
314 0 : if (custom_outfnc)
315 0 : custom_outfnc (is_error? 2:1, s);
316 : else
317 0 : fputs (s, is_error? stderr : stdout);
318 0 : count += strlen (s);
319 : }
320 0 : while ((s = va_arg (arg_ptr, const char *)));
321 0 : va_end (arg_ptr);
322 : }
323 0 : return count;
324 : }
325 :
326 :
327 : static void
328 0 : flushstrings (int is_error)
329 : {
330 0 : if (custom_outfnc)
331 0 : custom_outfnc (is_error? 2:1, NULL);
332 : else
333 0 : fflush (is_error? stderr : stdout);
334 0 : }
335 :
336 :
337 : static void
338 0 : initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
339 : {
340 0 : if( !(arg->flags & (1<<15)) )
341 : {
342 : /* Initialize this instance. */
343 0 : arg->internal.idx = 0;
344 0 : arg->internal.last = NULL;
345 0 : arg->internal.inarg = 0;
346 0 : arg->internal.stopped = 0;
347 0 : arg->internal.aliases = NULL;
348 0 : arg->internal.cur_alias = NULL;
349 0 : arg->internal.iio_list = NULL;
350 0 : arg->err = 0;
351 0 : arg->flags |= 1<<15; /* Mark as initialized. */
352 0 : if ( *arg->argc < 0 )
353 0 : log_bug ("invalid argument for arg_parse\n");
354 : }
355 :
356 :
357 0 : 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 0 : arg->r.ret_str = NULL;
412 0 : arg->r.ret_long = 0;
413 0 : }
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 0 : ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
527 : {
528 : IIO_ITEM_DEF item, tmpitem;
529 :
530 0 : for (item = arg->internal.iio_list; item; item = tmpitem)
531 : {
532 0 : tmpitem = item->next;
533 0 : xfree (item);
534 : }
535 0 : arg->internal.iio_list = NULL;
536 0 : }
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 0 : optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
563 : ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
564 : {
565 : int state, i, c;
566 0 : int idx=0;
567 : char keyword[100];
568 0 : char *buffer = NULL;
569 0 : size_t buflen = 0;
570 0 : int in_alias=0;
571 :
572 0 : if (!fp) /* Divert to to arg_parse() in this case. */
573 0 : return arg_parse (arg, opts);
574 :
575 0 : initialize (arg, filename, lineno);
576 :
577 : /* Find the next keyword. */
578 0 : state = i = 0;
579 : for (;;)
580 : {
581 0 : c = getc (fp);
582 0 : if (c == '\n' || c== EOF )
583 : {
584 0 : if ( c != EOF )
585 0 : ++*lineno;
586 0 : if (state == -1)
587 0 : break;
588 0 : else if (state == 2)
589 : {
590 0 : keyword[i] = 0;
591 0 : for (i=0; opts[i].short_opt; i++ )
592 : {
593 0 : if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
594 0 : break;
595 : }
596 0 : idx = i;
597 0 : arg->r_opt = opts[idx].short_opt;
598 0 : if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
599 : {
600 0 : state = i = 0;
601 0 : continue;
602 : }
603 0 : 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 0 : else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
622 0 : 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 0 : break;
629 : }
630 0 : 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 0 : else if (state == 4)
645 : {
646 : /* Has an argument. */
647 0 : 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 0 : else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
674 0 : arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
675 : else
676 : {
677 : char *p;
678 :
679 0 : if (!buffer)
680 : {
681 0 : keyword[i] = 0;
682 0 : buffer = xtrystrdup (keyword);
683 0 : if (!buffer)
684 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
685 : }
686 : else
687 0 : buffer[i] = 0;
688 :
689 0 : if (buffer)
690 : {
691 0 : trim_spaces (buffer);
692 0 : p = buffer;
693 0 : 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 0 : if (!set_opt_arg (arg, opts[idx].flags, p))
701 0 : xfree (buffer);
702 : }
703 : }
704 0 : break;
705 : }
706 0 : else if (c == EOF)
707 : {
708 0 : ignore_invalid_option_clear (arg);
709 0 : if (ferror (fp))
710 0 : arg->r_opt = ARGPARSE_READ_ERROR;
711 : else
712 0 : arg->r_opt = 0; /* EOF. */
713 0 : break;
714 : }
715 0 : state = 0;
716 0 : i = 0;
717 : }
718 0 : else if (state == -1)
719 : ; /* Skip. */
720 0 : else if (state == 0 && isascii (c) && isspace(c))
721 : ; /* Skip leading white space. */
722 0 : else if (state == 0 && c == '#' )
723 0 : state = 1; /* Start of a comment. */
724 0 : else if (state == 1)
725 : ; /* Skip comments. */
726 0 : else if (state == 2 && isascii (c) && isspace(c))
727 : {
728 : /* Check keyword. */
729 0 : keyword[i] = 0;
730 0 : for (i=0; opts[i].short_opt; i++ )
731 0 : if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
732 0 : break;
733 0 : idx = i;
734 0 : arg->r_opt = opts[idx].short_opt;
735 0 : if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
736 : {
737 0 : state = 1; /* Process like a comment. */
738 : }
739 0 : else if (!opts[idx].short_opt)
740 : {
741 0 : if (!strcmp (keyword, "alias"))
742 : {
743 0 : in_alias = 1;
744 0 : state = 3;
745 : }
746 0 : else if (!strcmp (keyword, "ignore-invalid-option"))
747 : {
748 0 : if (ignore_invalid_option_add (arg, fp))
749 : {
750 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
751 0 : break;
752 : }
753 0 : state = i = 0;
754 0 : ++*lineno;
755 : }
756 0 : else if (ignore_invalid_option_p (arg, keyword))
757 0 : state = 1; /* Process like a comment. */
758 : else
759 : {
760 0 : arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
761 : ? ARGPARSE_INVALID_COMMAND
762 0 : : ARGPARSE_INVALID_OPTION);
763 0 : state = -1; /* Skip rest of line and leave. */
764 : }
765 : }
766 : else
767 0 : state = 3;
768 : }
769 0 : else if (state == 3)
770 : {
771 : /* Skip leading spaces of the argument. */
772 0 : if (!isascii (c) || !isspace(c))
773 : {
774 0 : i = 0;
775 0 : keyword[i++] = c;
776 0 : state = 4;
777 : }
778 : }
779 0 : else if (state == 4)
780 : {
781 : /* Collect the argument. */
782 0 : if (buffer)
783 : {
784 0 : if (i < buflen-1)
785 0 : buffer[i++] = c;
786 : else
787 : {
788 : char *tmp;
789 0 : size_t tmplen = buflen + 50;
790 :
791 0 : tmp = xtryrealloc (buffer, tmplen);
792 0 : if (tmp)
793 : {
794 0 : buflen = tmplen;
795 0 : buffer = tmp;
796 0 : buffer[i++] = c;
797 : }
798 : else
799 : {
800 0 : xfree (buffer);
801 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
802 0 : break;
803 : }
804 : }
805 : }
806 0 : else if (i < DIM(keyword)-1)
807 0 : keyword[i++] = c;
808 : else
809 : {
810 0 : size_t tmplen = DIM(keyword) + 50;
811 0 : buffer = xtrymalloc (tmplen);
812 0 : if (buffer)
813 : {
814 0 : buflen = tmplen;
815 0 : memcpy(buffer, keyword, i);
816 0 : buffer[i++] = c;
817 : }
818 : else
819 : {
820 0 : arg->r_opt = ARGPARSE_OUT_OF_CORE;
821 0 : break;
822 : }
823 : }
824 : }
825 0 : else if (i >= DIM(keyword)-1)
826 : {
827 0 : arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
828 0 : state = -1; /* Skip rest of line and leave. */
829 : }
830 : else
831 : {
832 0 : keyword[i++] = c;
833 0 : state = 2;
834 : }
835 : }
836 :
837 0 : return arg->r_opt;
838 : }
839 :
840 :
841 :
842 : static int
843 0 : find_long_option( ARGPARSE_ARGS *arg,
844 : ARGPARSE_OPTS *opts, const char *keyword )
845 : {
846 : int i;
847 : size_t n;
848 :
849 : (void)arg;
850 :
851 : /* Would be better if we can do a binary search, but it is not
852 : possible to reorder our option table because we would mess
853 : up our help strings - What we can do is: Build a nice option
854 : lookup table when this function is first invoked */
855 0 : if( !*keyword )
856 0 : return -1;
857 0 : for(i=0; opts[i].short_opt; i++ )
858 0 : if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
859 0 : return i;
860 : #if 0
861 : {
862 : ALIAS_DEF a;
863 : /* see whether it is an alias */
864 : for( a = args->internal.aliases; a; a = a->next ) {
865 : if( !strcmp( a->name, keyword) ) {
866 : /* todo: must parse the alias here */
867 : args->internal.cur_alias = a;
868 : return -3; /* alias available */
869 : }
870 : }
871 : }
872 : #endif
873 : /* not found, see whether it is an abbreviation */
874 : /* aliases may not be abbreviated */
875 0 : n = strlen( keyword );
876 0 : for(i=0; opts[i].short_opt; i++ ) {
877 0 : if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
878 : int j;
879 0 : for(j=i+1; opts[j].short_opt; j++ ) {
880 0 : if( opts[j].long_opt
881 0 : && !strncmp( opts[j].long_opt, keyword, n ) )
882 0 : return -2; /* abbreviation is ambiguous */
883 : }
884 0 : return i;
885 : }
886 : }
887 0 : return -1; /* Not found. */
888 : }
889 :
890 : int
891 0 : arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
892 : {
893 : int idx;
894 : int argc;
895 : char **argv;
896 : char *s, *s2;
897 : int i;
898 0 : char string_with_x[] = "x";
899 :
900 0 : initialize( arg, NULL, NULL );
901 0 : argc = *arg->argc;
902 0 : argv = *arg->argv;
903 0 : idx = arg->internal.idx;
904 :
905 0 : if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
906 : {
907 : /* Skip the first argument. */
908 0 : argc--; argv++; idx++;
909 : }
910 :
911 : next_one:
912 0 : if (!argc)
913 : {
914 : /* No more args. */
915 0 : arg->r_opt = 0;
916 0 : goto leave; /* Ready. */
917 : }
918 :
919 0 : s = *argv;
920 0 : arg->internal.last = s;
921 :
922 0 : if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
923 : {
924 0 : arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */
925 0 : arg->r_type = 2;
926 0 : arg->r.ret_str = s;
927 0 : argc--; argv++; idx++; /* set to next one */
928 : }
929 0 : else if( arg->internal.stopped )
930 : {
931 0 : arg->r_opt = 0;
932 0 : goto leave; /* Ready. */
933 : }
934 0 : else if ( *s == '-' && s[1] == '-' )
935 0 : {
936 : /* Long option. */
937 : char *argpos;
938 :
939 0 : arg->internal.inarg = 0;
940 0 : if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
941 : {
942 : /* Stop option processing. */
943 0 : arg->internal.stopped = 1;
944 0 : arg->flags |= ARGPARSE_FLAG_STOP_SEEN;
945 0 : argc--; argv++; idx++;
946 0 : goto next_one;
947 : }
948 :
949 0 : argpos = strchr( s+2, '=' );
950 0 : if ( argpos )
951 0 : *argpos = 0;
952 0 : i = find_long_option ( arg, opts, s+2 );
953 0 : if ( argpos )
954 0 : *argpos = '=';
955 :
956 0 : if ( i < 0 && !strcmp ( "help", s+2) )
957 0 : show_help (opts, arg->flags);
958 0 : else if ( i < 0 && !strcmp ( "version", s+2) )
959 : {
960 0 : if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
961 : {
962 0 : show_version ();
963 0 : exit(0);
964 : }
965 : }
966 0 : else if ( i < 0 && !strcmp( "warranty", s+2))
967 : {
968 0 : writestrings (0, strusage (16), "\n", NULL);
969 0 : exit (0);
970 : }
971 0 : else if ( i < 0 && !strcmp( "dump-options", s+2) )
972 : {
973 0 : for (i=0; opts[i].short_opt; i++ )
974 : {
975 0 : if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
976 0 : writestrings (0, "--", opts[i].long_opt, "\n", NULL);
977 : }
978 0 : writestrings (0, "--dump-options\n--help\n--version\n--warranty\n",
979 : NULL);
980 0 : exit (0);
981 : }
982 :
983 0 : if ( i == -2 )
984 0 : arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
985 0 : else if ( i == -1 )
986 : {
987 0 : arg->r_opt = ARGPARSE_INVALID_OPTION;
988 0 : arg->r.ret_str = s+2;
989 : }
990 : else
991 0 : arg->r_opt = opts[i].short_opt;
992 0 : if ( i < 0 )
993 : ;
994 0 : else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
995 : {
996 0 : if ( argpos )
997 : {
998 0 : s2 = argpos+1;
999 0 : if ( !*s2 )
1000 0 : s2 = NULL;
1001 : }
1002 : else
1003 0 : s2 = argv[1];
1004 0 : if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1005 : {
1006 0 : arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */
1007 : }
1008 0 : else if ( !s2 )
1009 : {
1010 0 : arg->r_opt = ARGPARSE_MISSING_ARG;
1011 : }
1012 0 : else if ( !argpos && *s2 == '-'
1013 0 : && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1014 : {
1015 : /* The argument is optional and the next seems to be an
1016 : option. We do not check this possible option but
1017 : assume no argument */
1018 0 : arg->r_type = ARGPARSE_TYPE_NONE;
1019 : }
1020 : else
1021 : {
1022 0 : set_opt_arg (arg, opts[i].flags, s2);
1023 0 : if ( !argpos )
1024 : {
1025 0 : argc--; argv++; idx++; /* Skip one. */
1026 : }
1027 : }
1028 : }
1029 : else
1030 : {
1031 : /* Does not take an argument. */
1032 0 : if ( argpos )
1033 0 : arg->r_type = ARGPARSE_UNEXPECTED_ARG;
1034 : else
1035 0 : arg->r_type = 0;
1036 : }
1037 0 : argc--; argv++; idx++; /* Set to next one. */
1038 : }
1039 0 : else if ( (*s == '-' && s[1]) || arg->internal.inarg )
1040 0 : {
1041 : /* Short option. */
1042 0 : int dash_kludge = 0;
1043 :
1044 0 : i = 0;
1045 0 : if ( !arg->internal.inarg )
1046 : {
1047 0 : arg->internal.inarg++;
1048 0 : if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
1049 : {
1050 0 : for (i=0; opts[i].short_opt; i++ )
1051 0 : if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
1052 : {
1053 0 : dash_kludge = 1;
1054 0 : break;
1055 : }
1056 : }
1057 : }
1058 0 : s += arg->internal.inarg;
1059 :
1060 0 : if (!dash_kludge )
1061 : {
1062 0 : for (i=0; opts[i].short_opt; i++ )
1063 0 : if ( opts[i].short_opt == *s )
1064 0 : break;
1065 : }
1066 :
1067 0 : if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
1068 0 : show_help (opts, arg->flags);
1069 :
1070 0 : arg->r_opt = opts[i].short_opt;
1071 0 : if (!opts[i].short_opt )
1072 : {
1073 0 : arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
1074 0 : ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
1075 0 : arg->internal.inarg++; /* Point to the next arg. */
1076 0 : arg->r.ret_str = s;
1077 : }
1078 0 : else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
1079 : {
1080 0 : if ( s[1] && !dash_kludge )
1081 : {
1082 0 : s2 = s+1;
1083 0 : set_opt_arg (arg, opts[i].flags, s2);
1084 : }
1085 : else
1086 : {
1087 0 : s2 = argv[1];
1088 0 : if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1089 : {
1090 0 : arg->r_type = ARGPARSE_TYPE_NONE;
1091 : }
1092 0 : else if ( !s2 )
1093 : {
1094 0 : arg->r_opt = ARGPARSE_MISSING_ARG;
1095 : }
1096 0 : else if ( *s2 == '-' && s2[1]
1097 0 : && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1098 : {
1099 : /* The argument is optional and the next seems to
1100 : be an option. We do not check this possible
1101 : option but assume no argument. */
1102 0 : arg->r_type = ARGPARSE_TYPE_NONE;
1103 : }
1104 : else
1105 : {
1106 0 : set_opt_arg (arg, opts[i].flags, s2);
1107 0 : argc--; argv++; idx++; /* Skip one. */
1108 : }
1109 : }
1110 0 : s = string_with_x; /* This is so that !s[1] yields false. */
1111 : }
1112 : else
1113 : {
1114 : /* Does not take an argument. */
1115 0 : arg->r_type = ARGPARSE_TYPE_NONE;
1116 0 : arg->internal.inarg++; /* Point to the next arg. */
1117 : }
1118 0 : if ( !s[1] || dash_kludge )
1119 : {
1120 : /* No more concatenated short options. */
1121 0 : arg->internal.inarg = 0;
1122 0 : argc--; argv++; idx++;
1123 : }
1124 : }
1125 0 : else if ( arg->flags & ARGPARSE_FLAG_MIXED )
1126 : {
1127 0 : arg->r_opt = ARGPARSE_IS_ARG;
1128 0 : arg->r_type = 2;
1129 0 : arg->r.ret_str = s;
1130 0 : argc--; argv++; idx++; /* Set to next one. */
1131 : }
1132 : else
1133 : {
1134 0 : arg->internal.stopped = 1; /* Stop option processing. */
1135 0 : goto next_one;
1136 : }
1137 :
1138 : leave:
1139 0 : *arg->argc = argc;
1140 0 : *arg->argv = argv;
1141 0 : arg->internal.idx = idx;
1142 0 : return arg->r_opt;
1143 : }
1144 :
1145 :
1146 : /* Returns: -1 on error, 0 for an integer type and 1 for a non integer
1147 : type argument. */
1148 : static int
1149 0 : set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
1150 : {
1151 0 : int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
1152 : long l;
1153 :
1154 0 : switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
1155 : {
1156 : case ARGPARSE_TYPE_LONG:
1157 : case ARGPARSE_TYPE_INT:
1158 0 : errno = 0;
1159 0 : l = strtol (s, NULL, base);
1160 0 : if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
1161 : {
1162 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1163 0 : return -1;
1164 : }
1165 0 : if (arg->r_type == ARGPARSE_TYPE_LONG)
1166 0 : arg->r.ret_long = l;
1167 0 : else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
1168 : {
1169 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1170 0 : return -1;
1171 : }
1172 : else
1173 0 : arg->r.ret_int = (int)l;
1174 0 : return 0;
1175 :
1176 : case ARGPARSE_TYPE_ULONG:
1177 0 : while (isascii (*s) && isspace(*s))
1178 0 : s++;
1179 0 : if (*s == '-')
1180 : {
1181 0 : arg->r.ret_ulong = 0;
1182 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1183 0 : return -1;
1184 : }
1185 0 : errno = 0;
1186 0 : arg->r.ret_ulong = strtoul (s, NULL, base);
1187 0 : if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
1188 : {
1189 0 : arg->r_opt = ARGPARSE_INVALID_ARG;
1190 0 : return -1;
1191 : }
1192 0 : return 0;
1193 :
1194 : case ARGPARSE_TYPE_STRING:
1195 : default:
1196 0 : arg->r.ret_str = s;
1197 0 : return 1;
1198 : }
1199 : }
1200 :
1201 :
1202 : static size_t
1203 0 : long_opt_strlen( ARGPARSE_OPTS *o )
1204 : {
1205 0 : size_t n = strlen (o->long_opt);
1206 :
1207 0 : if ( o->description && *o->description == '|' )
1208 : {
1209 : const char *s;
1210 0 : int is_utf8 = is_native_utf8 ();
1211 :
1212 0 : s=o->description+1;
1213 0 : if ( *s != '=' )
1214 0 : n++;
1215 : /* For a (mostly) correct length calculation we exclude
1216 : continuation bytes (10xxxxxx) if we are on a native utf8
1217 : terminal. */
1218 0 : for (; *s && *s != '|'; s++ )
1219 0 : if ( is_utf8 && (*s&0xc0) != 0x80 )
1220 0 : n++;
1221 : }
1222 0 : return n;
1223 : }
1224 :
1225 :
1226 : /****************
1227 : * Print formatted help. The description string has some special
1228 : * meanings:
1229 : * - A description string which is "@" suppresses help output for
1230 : * this option
1231 : * - a description,ine which starts with a '@' and is followed by
1232 : * any other characters is printed as is; this may be used for examples
1233 : * ans such.
1234 : * - A description which starts with a '|' outputs the string between this
1235 : * bar and the next one as arguments of the long option.
1236 : */
1237 : static void
1238 0 : show_help (ARGPARSE_OPTS *opts, unsigned int flags)
1239 : {
1240 : const char *s;
1241 : char tmp[2];
1242 :
1243 0 : show_version ();
1244 0 : writestrings (0, "\n", NULL);
1245 0 : s = strusage (42);
1246 0 : if (s && *s == '1')
1247 : {
1248 0 : s = strusage (40);
1249 0 : writestrings (1, s, NULL);
1250 0 : if (*s && s[strlen(s)] != '\n')
1251 0 : writestrings (1, "\n", NULL);
1252 : }
1253 0 : s = strusage(41);
1254 0 : writestrings (0, s, "\n", NULL);
1255 0 : if ( opts[0].description )
1256 : {
1257 : /* Auto format the option description. */
1258 : int i,j, indent;
1259 :
1260 : /* Get max. length of long options. */
1261 0 : for (i=indent=0; opts[i].short_opt; i++ )
1262 : {
1263 0 : if ( opts[i].long_opt )
1264 0 : if ( !opts[i].description || *opts[i].description != '@' )
1265 0 : if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
1266 0 : indent = j;
1267 : }
1268 :
1269 : /* Example: " -v, --verbose Viele Sachen ausgeben" */
1270 0 : indent += 10;
1271 0 : if ( *opts[0].description != '@' )
1272 0 : writestrings (0, "Options:", "\n", NULL);
1273 0 : for (i=0; opts[i].short_opt; i++ )
1274 : {
1275 0 : s = map_static_macro_string (_( opts[i].description ));
1276 0 : if ( s && *s== '@' && !s[1] ) /* Hide this line. */
1277 0 : continue;
1278 0 : if ( s && *s == '@' ) /* Unindented comment only line. */
1279 : {
1280 0 : for (s++; *s; s++ )
1281 : {
1282 0 : if ( *s == '\n' )
1283 : {
1284 0 : if( s[1] )
1285 0 : writestrings (0, "\n", NULL);
1286 : }
1287 : else
1288 : {
1289 0 : tmp[0] = *s;
1290 0 : tmp[1] = 0;
1291 0 : writestrings (0, tmp, NULL);
1292 : }
1293 : }
1294 0 : writestrings (0, "\n", NULL);
1295 0 : continue;
1296 : }
1297 :
1298 0 : j = 3;
1299 0 : if ( opts[i].short_opt < 256 )
1300 : {
1301 0 : tmp[0] = opts[i].short_opt;
1302 0 : tmp[1] = 0;
1303 0 : writestrings (0, " -", tmp, NULL );
1304 0 : if ( !opts[i].long_opt )
1305 : {
1306 0 : if (s && *s == '|' )
1307 : {
1308 0 : writestrings (0, " ", NULL); j++;
1309 0 : for (s++ ; *s && *s != '|'; s++, j++ )
1310 : {
1311 0 : tmp[0] = *s;
1312 0 : tmp[1] = 0;
1313 0 : writestrings (0, tmp, NULL);
1314 : }
1315 0 : if ( *s )
1316 0 : s++;
1317 : }
1318 : }
1319 : }
1320 : else
1321 0 : writestrings (0, " ", NULL);
1322 0 : if ( opts[i].long_opt )
1323 : {
1324 0 : tmp[0] = opts[i].short_opt < 256?',':' ';
1325 0 : tmp[1] = 0;
1326 0 : j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
1327 0 : if (s && *s == '|' )
1328 : {
1329 0 : if ( *++s != '=' )
1330 : {
1331 0 : writestrings (0, " ", NULL);
1332 0 : j++;
1333 : }
1334 0 : for ( ; *s && *s != '|'; s++, j++ )
1335 : {
1336 0 : tmp[0] = *s;
1337 0 : tmp[1] = 0;
1338 0 : writestrings (0, tmp, NULL);
1339 : }
1340 0 : if ( *s )
1341 0 : s++;
1342 : }
1343 0 : writestrings (0, " ", NULL);
1344 0 : j += 3;
1345 : }
1346 0 : for (;j < indent; j++ )
1347 0 : writestrings (0, " ", NULL);
1348 0 : if ( s )
1349 : {
1350 0 : if ( *s && j > indent )
1351 : {
1352 0 : writestrings (0, "\n", NULL);
1353 0 : for (j=0;j < indent; j++ )
1354 0 : writestrings (0, " ", NULL);
1355 : }
1356 0 : for (; *s; s++ )
1357 : {
1358 0 : if ( *s == '\n' )
1359 : {
1360 0 : if ( s[1] )
1361 : {
1362 0 : writestrings (0, "\n", NULL);
1363 0 : for (j=0; j < indent; j++ )
1364 0 : writestrings (0, " ", NULL);
1365 : }
1366 : }
1367 : else
1368 : {
1369 0 : tmp[0] = *s;
1370 0 : tmp[1] = 0;
1371 0 : writestrings (0, tmp, NULL);
1372 : }
1373 : }
1374 : }
1375 0 : writestrings (0, "\n", NULL);
1376 : }
1377 0 : if ( (flags & ARGPARSE_FLAG_ONEDASH) )
1378 0 : writestrings (0, "\n(A single dash may be used "
1379 : "instead of the double ones)\n", NULL);
1380 : }
1381 0 : if ( (s=strusage(19)) )
1382 : {
1383 0 : writestrings (0, "\n", NULL);
1384 0 : writestrings (0, s, NULL);
1385 : }
1386 0 : flushstrings (0);
1387 0 : exit(0);
1388 : }
1389 :
1390 : static void
1391 0 : show_version ()
1392 : {
1393 : const char *s;
1394 : int i;
1395 :
1396 : /* Version line. */
1397 0 : writestrings (0, strusage (11), NULL);
1398 0 : if ((s=strusage (12)))
1399 0 : writestrings (0, " (", s, ")", NULL);
1400 0 : writestrings (0, " ", strusage (13), "\n", NULL);
1401 : /* Additional version lines. */
1402 0 : for (i=20; i < 30; i++)
1403 0 : if ((s=strusage (i)))
1404 0 : writestrings (0, s, "\n", NULL);
1405 : /* Copyright string. */
1406 0 : if ((s=strusage (14)))
1407 0 : writestrings (0, s, "\n", NULL);
1408 : /* Licence string. */
1409 0 : if( (s=strusage (10)) )
1410 0 : writestrings (0, s, "\n", NULL);
1411 : /* Copying conditions. */
1412 0 : if ( (s=strusage(15)) )
1413 0 : writestrings (0, s, NULL);
1414 : /* Thanks. */
1415 0 : if ((s=strusage(18)))
1416 0 : writestrings (0, s, NULL);
1417 : /* Additional program info. */
1418 0 : for (i=30; i < 40; i++ )
1419 0 : if ( (s=strusage (i)) )
1420 0 : writestrings (0, s, NULL);
1421 0 : flushstrings (0);
1422 0 : }
1423 :
1424 :
1425 : void
1426 0 : usage (int level)
1427 : {
1428 : const char *p;
1429 :
1430 0 : if (!level)
1431 : {
1432 0 : writestrings (1, strusage(11), " ", strusage(13), "; ",
1433 : strusage (14), "\n", NULL);
1434 0 : flushstrings (1);
1435 : }
1436 0 : else if (level == 1)
1437 : {
1438 0 : p = strusage (40);
1439 0 : writestrings (1, p, NULL);
1440 0 : if (*p && p[strlen(p)] != '\n')
1441 0 : writestrings (1, "\n", NULL);
1442 0 : exit (2);
1443 : }
1444 0 : else if (level == 2)
1445 : {
1446 0 : p = strusage (42);
1447 0 : if (p && *p == '1')
1448 : {
1449 0 : p = strusage (40);
1450 0 : writestrings (1, p, NULL);
1451 0 : if (*p && p[strlen(p)] != '\n')
1452 0 : writestrings (1, "\n", NULL);
1453 : }
1454 0 : writestrings (0, strusage(41), "\n", NULL);
1455 0 : exit (0);
1456 : }
1457 0 : }
1458 :
1459 : /* Level
1460 : * 0: Print copyright string to stderr
1461 : * 1: Print a short usage hint to stderr and terminate
1462 : * 2: Print a long usage hint to stdout and terminate
1463 : * 10: Return license info string
1464 : * 11: Return the name of the program
1465 : * 12: Return optional name of package which includes this program.
1466 : * 13: version string
1467 : * 14: copyright string
1468 : * 15: Short copying conditions (with LFs)
1469 : * 16: Long copying conditions (with LFs)
1470 : * 17: Optional printable OS name
1471 : * 18: Optional thanks list (with LFs)
1472 : * 19: Bug report info
1473 : *20..29: Additional lib version strings.
1474 : *30..39: Additional program info (with LFs)
1475 : * 40: short usage note (with LF)
1476 : * 41: long usage note (with LF)
1477 : * 42: Flag string:
1478 : * First char is '1':
1479 : * The short usage notes needs to be printed
1480 : * before the long usage note.
1481 : */
1482 : const char *
1483 0 : strusage( int level )
1484 : {
1485 0 : const char *p = strusage_handler? strusage_handler(level) : NULL;
1486 :
1487 0 : if ( p )
1488 0 : return map_static_macro_string (p);
1489 :
1490 0 : switch ( level )
1491 : {
1492 :
1493 : case 10:
1494 : #if ARGPARSE_GPL_VERSION == 3
1495 : p = ("License GPLv3+: GNU GPL version 3 or later "
1496 : "<http://gnu.org/licenses/gpl.html>");
1497 : #else
1498 0 : p = ("License GPLv2+: GNU GPL version 2 or later "
1499 : "<http://gnu.org/licenses/>");
1500 : #endif
1501 0 : break;
1502 0 : case 11: p = "foo"; break;
1503 0 : case 13: p = "0.0"; break;
1504 0 : case 14: p = ARGPARSE_CRIGHT_STR; break;
1505 0 : case 15: p =
1506 : "This is free software: you are free to change and redistribute it.\n"
1507 : "There is NO WARRANTY, to the extent permitted by law.\n";
1508 0 : break;
1509 0 : case 16: p =
1510 : "This is free software; you can redistribute it and/or modify\n"
1511 : "it under the terms of the GNU General Public License as published by\n"
1512 : "the Free Software Foundation; either version "
1513 : ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
1514 : " of the License, or\n"
1515 : "(at your option) any later version.\n\n"
1516 : "It is distributed in the hope that it will be useful,\n"
1517 : "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1518 : "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1519 : "GNU General Public License for more details.\n\n"
1520 : "You should have received a copy of the GNU General Public License\n"
1521 : "along with this software. If not, see <https://www.gnu.org/licenses/>.\n";
1522 0 : break;
1523 : case 40: /* short and long usage */
1524 0 : case 41: p = ""; break;
1525 : }
1526 :
1527 0 : return p;
1528 : }
1529 :
1530 :
1531 : /* Set the usage handler. This function is basically a constructor. */
1532 : void
1533 0 : set_strusage ( const char *(*f)( int ) )
1534 : {
1535 0 : strusage_handler = f;
1536 0 : }
1537 :
1538 :
1539 : #ifdef TEST
1540 : static struct {
1541 : int verbose;
1542 : int debug;
1543 : char *outfile;
1544 : char *crf;
1545 : int myopt;
1546 : int echo;
1547 : int a_long_one;
1548 : } opt;
1549 :
1550 : int
1551 : main(int argc, char **argv)
1552 : {
1553 : ARGPARSE_OPTS opts[] = {
1554 : ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
1555 : ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, "
1556 : "was wir eingegeben haben")),
1557 : ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
1558 : ARGPARSE_s_s('o', "output", 0 ),
1559 : ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
1560 : /* Note that on a non-utf8 terminal the ß might garble the output. */
1561 : ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
1562 : ARGPARSE_o_i('m', "my-option", 0),
1563 : ARGPARSE_s_n(500, "a-long-option", 0 ),
1564 : ARGPARSE_end()
1565 : };
1566 : ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL
1567 : | ARGPARSE_FLAG_MIXED
1568 : | ARGPARSE_FLAG_ONEDASH) };
1569 : int i;
1570 :
1571 : while (arg_parse (&pargs, opts))
1572 : {
1573 : switch (pargs.r_opt)
1574 : {
1575 : case ARGPARSE_IS_ARG :
1576 : printf ("arg='%s'\n", pargs.r.ret_str);
1577 : break;
1578 : case 'v': opt.verbose++; break;
1579 : case 'e': opt.echo++; break;
1580 : case 'd': opt.debug++; break;
1581 : case 'o': opt.outfile = pargs.r.ret_str; break;
1582 : case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1583 : case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1584 : case 500: opt.a_long_one++; break;
1585 : default : pargs.err = ARGPARSE_PRINT_WARNING; break;
1586 : }
1587 : }
1588 : for (i=0; i < argc; i++ )
1589 : printf ("%3d -> (%s)\n", i, argv[i] );
1590 : puts ("Options:");
1591 : if (opt.verbose)
1592 : printf (" verbose=%d\n", opt.verbose );
1593 : if (opt.debug)
1594 : printf (" debug=%d\n", opt.debug );
1595 : if (opt.outfile)
1596 : printf (" outfile='%s'\n", opt.outfile );
1597 : if (opt.crf)
1598 : printf (" crffile='%s'\n", opt.crf );
1599 : if (opt.myopt)
1600 : printf (" myopt=%d\n", opt.myopt );
1601 : if (opt.a_long_one)
1602 : printf (" a-long-one=%d\n", opt.a_long_one );
1603 : if (opt.echo)
1604 : printf (" echo=%d\n", opt.echo );
1605 :
1606 : return 0;
1607 : }
1608 : #endif /*TEST*/
1609 :
1610 : /**** bottom of file ****/
|