2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)getargs.c 2.37 04/09/25 Copyright 1985, 1988, 1994-2003 J. Schilling */
16 * Copyright (c) 1985, 1988, 1994-2003 J. Schilling
18 * 1.3.88 Start implementation of release 2
21 * Parse arguments on a command line.
22 * Format string type specifier (appearing directly after flag name):
24 * '!' BOOL with size modifier +++ NEU +++ (XXX nicht fertig)
29 * '+' inctype +++ NEU +++
31 * The '#' and '+' types may have size modifiers added:
34 * 'i'/'I' int (default)
38 * The format string 'f* ' may be used to disallow -ffoo for f*
40 * XXX This is currently only implemented for the '*' format
43 * This program is free software; you can redistribute it and/or modify
44 * it under the terms of the GNU General Public License version 2
45 * as published by the Free Software Foundation.
47 * This program is distributed in the hope that it will be useful,
48 * but WITHOUT ANY WARRANTY; without even the implied warranty of
49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50 * GNU General Public License for more details.
52 * You should have received a copy of the GNU General Public License along with
53 * this program; see the file COPYING. If not, write to the Free Software
54 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
66 #define NOARGS 0 /* No more args */
67 #define NOTAFLAG 1 /* Not a flag type argument */
68 #define BADFLAG (-1) /* Not a valid flag argument */
69 #define BADFMT (-2) /* Error in format string */
70 #define NOTAFILE (-3) /* Seems to be a flag type arg */
73 int _getargs __PR((int *, char *const **, const char *,
75 LOCAL int dofile __PR((int *, char *const **, const char **));
76 LOCAL int doflag __PR((int *, char *const **, const char *,
77 const char *, BOOL, va_list));
78 LOCAL int dosflags __PR((const char *, const char *, BOOL, va_list));
79 LOCAL int checkfmt __PR((const char *));
80 LOCAL int checkeql __PR((const char *));
82 LOCAL va_list va_dummy;
84 LOCAL char fmtspecs[] = "#?*&+";
85 /*LOCAL char fmtspecs[] = "#?*&+!";*/
87 #define isfmtspec(c) (strchr(fmtspecs, c) != NULL)
89 /*---------------------------------------------------------------------------
91 | get flags until a non flag type argument is reached
93 +---------------------------------------------------------------------------*/
97 getargs(int *pac, char *const **pav, const char *fmt, ...)
100 getargs(pac, pav, fmt, va_alist)
115 ret = _getargs(pac, pav, fmt, TRUE, args);
121 /*---------------------------------------------------------------------------
123 | get all flags on the command line, do not stop on files
125 +---------------------------------------------------------------------------*/
129 getallargs(int *pac, char *const **pav, const char *fmt, ...)
132 getallargs(pac, pav, fmt, va_alist)
147 for (; ; (*pac)--, (*pav)++) {
148 if ((ret = _getargs(pac, pav, fmt, TRUE, args)) != NOTAFLAG)
156 /*---------------------------------------------------------------------------
158 | get next non flag type argument (i.e. a file)
160 +---------------------------------------------------------------------------*/
162 getfiles(pac, pav, fmt)
167 return (_getargs(pac, pav, fmt, FALSE, va_dummy));
171 /*---------------------------------------------------------------------------
173 | check args until the next non flag type argmument is reached
174 | *pac is decremented, *pav is incremented so that the
175 | non flag type argument is at *pav[0]
178 | NOARGS no more args
179 | NOTAFLAG not a flag type argument
180 | BADFLAG a non-matching flag type argument
181 | BADFMT bad syntax in format string
184 +---------------------------------------------------------------------------*/
186 _getargs(pac, pav, fmt, setargs, args)
188 register char *const **pav;
197 for (; *pac > 0; (*pac)--, (*pav)++) {
200 ret = dofile(pac, pav, &argp);
205 ret = doflag(pac, pav, argp, fmt, setargs, args);
214 /*---------------------------------------------------------------------------
216 | check if *pargp is a file type argument
218 +---------------------------------------------------------------------------*/
220 dofile(pac, pav, pargp)
222 register char *const **pav;
225 register const char *argp = *pargp;
228 if (argp[0] == '-') {
230 * "-" is a special non flag type argument
231 * that usually means take stdin instead of a named file
236 * "--" is a prefix to take the next argument
237 * as non flag type argument
238 * NOTE: Posix requires "--" to indicate the end of the
239 * flags on the command line. But we are currently not
242 if (argp[1] == '-' && argp[2] == '\0') {
253 * now check if it may be flag type argument
254 * flag type arguments begin with a '-', a '+' or contain a '='
255 * i.e. -flag +flag or flag=
257 if (argp[0] != '-' && argp[0] != '+' && (!checkeql(argp)))
264 /*---------------------------------------------------------------------------
266 | compare argp with the format string
267 | if a match is found store the result a la scanf in one of the
268 | arguments pointed to in the va_list
270 | If setargs is FALSE, only check arguments for getfiles()
271 | in this case, va_list may be a dummy arg.
273 +---------------------------------------------------------------------------*/
275 doflag(pac, pav, argp, fmt, setargs, oargs)
278 register const char *argp;
279 register const char *fmt;
286 int singlecharflag = 0;
288 BOOL hasdash = FALSE;
289 BOOL doubledash = FALSE;
290 BOOL haseql = checkeql(argp);
292 const char *sfmt = fmt;
294 char *const *spav = *pav;
296 void *curarg = (void *)0;
299 * flags beginning with '-' don't have to include the '-' in
301 * flags beginning with '+' have to include it in the format string.
303 if (argp[0] == '-') {
307 * Implement legacy support for --longopt
308 * If we find a double dash, we do not look for combinations
309 * of boolean single char flags.
311 if (argp[0] == '-') {
315 * Allow -- only for long options.
317 if (argp[1] == '\0') {
325 * Initialize 'args' to the start of the argument list.
326 * I don't know any portable way to copy an arbitrary
327 * C object so I use a system-specific routine
328 * (probably a macro) from stdarg.h. (Remember that
329 * if va_list is an array, 'args' will be a pointer
330 * and '&args' won't be what I would need for memcpy.)
331 * It is a system requirement for SVr4 compatibility
332 * to be able to do this assgignement. If your system
333 * defines va_list to be an array but does not define
334 * va_copy() you are lost.
335 * This is needed to make sure, that 'oargs' will not
338 va_copy(args, oargs);
341 curarg = va_arg(args, void *);
344 * check if the first flag in format string is a singlechar flag
346 if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0')
349 * check the whole format string for a match
352 for (; *fmt; fmt++, argp++) {
355 * Allow "#?*&+" to appear inside a flag.
356 * NOTE: they must be escaped by '\\' only
357 * inside the the format string.
362 isspec = isfmtspec(*fmt);
365 * If isspec is TRUE, the arg beeing checked starts
366 * like a valid flag. Argp now points to the rest.
370 * If *argp is '+' and we are on the
371 * beginning of the arg that is currently
372 * checked, this cannot be an inc type flag.
374 if (*argp == '+' && argp == sargp)
377 * skip over to arg of flag
381 } else if (*argp != '\0' && haseql) {
383 * Flag and arg are not separated by a
386 * xxxxx=yyyyy match on '&'
388 * abc=yyyyy match on 'abc&'
391 * We come here if 'argp' starts with
392 * the same sequence as a valid flag
393 * and contains an equal sign.
394 * We have tested before if the text
395 * before 'argp' matches exactly.
396 * At this point we have no exact match
397 * and we only allow to match
398 * the special pattern '&'.
399 * We need this e.g. for 'make'.
400 * We allow any flag type argument to
401 * match the format string "&" to set
402 * up a function that handles all odd
403 * stuff that getargs will not grok.
404 * In addition, to allow getargs to be
405 * used for CPP type flags we allow to
406 * match -Dabc=xyz on 'D&'. Note that
407 * Dabc=xyz will not match 'D&'.
409 if ((!hasdash && argp != sargp) || *fmt != '&')
414 * The format string 'f* ' may be used
415 * to disallow -ffoo for f*
417 * XXX This is currently only implemented for
420 if (!haseql && *argp != '\0' && fmt[0] == '*' && fmt[1] == ' ')
423 * *arpp == '\0' || !haseql
424 * We come here if 'argp' starts with
425 * the same sequence as a valid flag.
426 * This will match on the following args:
427 * -farg match on 'f*'
431 * and all args that are separated from
433 * In the switch statement below, we check
434 * if the text after 'argp' (if *argp != 0) or
435 * the next arg is a valid arg for this flag.
438 } else if (*fmt == *argp) {
439 if (argp[1] == '\0' &&
440 (fmt[1] == '\0' || fmt[1] == ',')) {
443 *((int *)curarg) = TRUE;
446 return (checkfmt(fmt)); /* XXX */
450 * skip over to next format identifier
451 * & reset arg pointer
454 while (*fmt != ',' && *fmt != '\0') {
455 /* function has extra arg on stack */
456 if (*fmt == '&' && setargs)
457 curarg = va_arg(args, void *);
468 * Boolean type has been tested before.
470 if (singlecharflag && !doubledash &&
471 (val = dosflags(sargp, sfmt, setargs, oargs)) !=
480 if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0')
483 curarg = va_arg(args, void *);
496 if (fmt[1] == ' ') /* To disallow -ffoo for f* */
499 *((const char **)curarg) = argp;
502 return (checkfmt(fmt));
506 * If more than one char arg, it
507 * cannot be a character argument.
512 *((char *)curarg) = *argp;
515 return (checkfmt(fmt));
519 * inc type is similar to boolean,
520 * there is no arg in argp to convert.
525 * If *fmt is '+' and we are on the beginning
526 * of the format desciptor that is currently
527 * checked, this cannot be an inc type flag.
529 if (fmt == sfmt || fmt[-1] == ',')
532 if (fmt[1] == 'l' || fmt[1] == 'L') {
533 if (fmt[2] == 'l' || fmt[2] == 'L') {
535 *((Llong *)curarg) += 1;
539 *((long *)curarg) += 1;
542 } else if (fmt[1] == 's' || fmt[1] == 'S') {
544 *((short *)curarg) += 1;
546 } else if (fmt[1] == 'c' || fmt[1] == 'C') {
548 *((char *)curarg) += 1;
551 if (fmt[1] == 'i' || fmt[1] == 'I')
554 *((int *)curarg) += 1;
560 return (checkfmt(fmt));
572 if (*astoll(argp, &llval) != '\0') {
574 * arg is not a valid number!
575 * go to next format in the format string
576 * and check if arg matches any other type
577 * in the format specs.
580 while (*fmt != ',' && *fmt != '\0') {
581 if (*fmt == '&' && setargs)
582 curarg = va_arg(args, void *);
591 if (fmt[1] == 'l' || fmt[1] == 'L') {
592 if (fmt[2] == 'l' || fmt[2] == 'L') {
594 *((Llong *)curarg) = llval;
598 *((long *)curarg) = val;
601 } else if (fmt[1] == 's' || fmt[1] == 'S') {
603 *((short *)curarg) = (short)val;
605 } else if (fmt[1] == 'c' || fmt[1] == 'C') {
607 *((char *)curarg) = (char)val;
610 if (fmt[1] == 'i' || fmt[1] == 'I')
613 *((int *)curarg) = (int)val;
618 return (checkfmt(fmt));
631 if ((val = checkfmt(fmt)) != NOTAFLAG)
636 void *funarg = va_arg(args, void *);
638 ret = ((*(getargfun)curarg) (argp, funarg));
646 * Called function returns NOTAFILE: try next format.
653 /*---------------------------------------------------------------------------
655 | parse args for combined single char flags
657 +---------------------------------------------------------------------------*/
659 void *curarg; /* The pointer to the arg to modify */
660 short count; /* The number of times a sc flag appears */
661 char c; /* The single char flag character */
662 char type; /* The type of the single char flag */
666 dosflags(argp, fmt, setargs, oargs)
667 register const char *argp;
668 register const char *fmt;
675 register sflags *rsf = sf;
676 register int nsf = 0;
677 register const char *p = argp;
679 register void *curarg = (void *)0;
683 * Initialize 'args' to the start of the argument list.
684 * I don't know any portable way to copy an arbitrary
685 * C object so I use a system-specific routine
686 * (probably a macro) from stdarg.h. (Remember that
687 * if va_list is an array, 'args' will be a pointer
688 * and '&args' won't be what I would need for memcpy.)
689 * It is a system requirement for SVr4 compatibility
690 * to be able to do this assgignement. If your system
691 * defines va_list to be an array but does not define
692 * va_copy() you are lost.
693 * This is needed to make sure, that 'oargs' will not
696 va_copy(args, oargs);
699 curarg = va_arg(args, void *);
702 for (i = 0; i < nsf; i++) {
709 rsf[i].curarg = (void *)0;
712 rsf[i].type = (char)-1;
720 if (!isfmtspec(*fmt) &&
721 (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0') &&
722 strchr(argp, *fmt)) {
723 for (i = 0; i < nsf; i++) {
724 if (rsf[i].c == *fmt) {
730 } else if ((fmt[1] == 'l' ||
741 * Type 'l','i','s','c'
743 rsf[i].type = fmt[1];
747 * ',' or '\0' for BOOL
749 rsf[i].type = fmt[1];
751 rsf[i].curarg = curarg;
756 while (*fmt != ',' && *fmt != '\0') {
757 /* function has extra arg on stack */
758 if (*fmt == '&' && setargs)
759 curarg = va_arg(args, void *);
766 curarg = va_arg(args, void *);
768 for (i = 0; i < nsf; i++) {
770 if (type == (char)-1) {
774 if (type == ',' || type == '\0') {
775 *((int *)rsf[i].curarg) = TRUE;
776 } else if (type == 'i' || type == 'I') {
777 *((int *)rsf[i].curarg) += rsf[i].count;
778 } else if (type == 'l' || type == 'L') {
779 *((long *)rsf[i].curarg) += rsf[i].count;
780 } else if (type == 'Q') {
781 *((Llong *)rsf[i].curarg) += rsf[i].count;
782 } else if (type == 's' || type == 'S') {
783 *((short *)rsf[i].curarg) += rsf[i].count;
784 } else if (type == 'c' || type == 'C') {
785 *((char *)rsf[i].curarg) += rsf[i].count;
794 /*---------------------------------------------------------------------------
796 | If the next format character is a comma or the string delimiter,
797 | there are no invalid format specifiers. Return success.
798 | Otherwise raise the getarg_bad_format condition.
800 +---------------------------------------------------------------------------*/
807 c = *(++fmt); /* non constant expression */
810 if (c == ',' || c == '\0') {
813 raisecond("getarg_bad_format", (long)fmt);
818 /*---------------------------------------------------------------------------
820 | Parse the string as long as valid characters can be found.
821 | Valid flag identifiers are chosen from the set of
822 | alphanumeric characters, '-' and '_'.
823 | If the next character is an equal sign the string
824 | contains a valid flag identifier.
826 +---------------------------------------------------------------------------*/
829 register const char *str;
831 register unsigned char c;
833 for (c = (unsigned char)*str;
834 isalnum(c) || c == '_' || c == '-'; c = *str++)