1 /* @(#)getargs.c 2.79 19/11/26 Copyright 1985, 1988, 1994-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)getargs.c 2.79 19/11/26 Copyright 1985, 1988, 1994-2019 J. Schilling";
6 #endif
7 #define NEW
8 /*
9 * Copyright (c) 1985, 1988, 1994-2019 J. Schilling
10 *
11 * 1.3.88 Start implementation of release 2
12 */
13 /*
14 * Parse arguments on a command line.
15 * Format string type specifier (appearing directly after flag name):
16 * '' BOOL size int set to TRUE (1)
17 * '%' Extended format, next char determines the type:
18 * '%0' BOOL with size modifier set to FALSE (0)
19 * '%1' BOOL with size modifier set to TRUE(1)
20 * '*' string
21 * '?' char
22 * '#' number
23 * '&' call function for any type flag
24 * '~' call function for BOOLEAN flag
25 * '+' inctype +++ NEU +++
26 *
27 * The format string 'f* ' may be used to disallow -ffoo for f*
28 * The same behavior is implemented for 'f# ', 'f? ' and 'f& '.
29 * The ' ' needs to immediately follow the format type specifier.
30 *
31 * The format string 'f*_' may be used to disallow -f foo for f*
32 * The same behavior is implemented for 'f#_', 'f?_' and 'f&_'.
33 * The '_' needs to immediately follow the format type specifier.
34 * This also allows to implement optional arguments to options.
35 * Note: 'f#_' is only implemented for orthogonality, -f will
36 * be converted to an integer value of 0.
37 *
38 * The '#', '+' and '%[01]' types may have size modifiers added:
39 * 'c'/'C' char
40 * 's'/'S' short
41 * 'i'/'I' int (default == no size modifier)
42 * 'l'/'L' long
43 * 'll'/'LL' long long
44 */
45 /*
46 * The contents of this file are subject to the terms of the
47 * Common Development and Distribution License, Version 1.0 only
48 * (the "License"). You may not use this file except in compliance
49 * with the License.
50 *
51 * See the file CDDL.Schily.txt in this distribution for details.
52 * A copy of the CDDL is also available via the Internet at
53 * http://www.opensource.org/licenses/cddl1.txt
54 *
55 * When distributing Covered Code, include this CDDL HEADER in each
56 * file and include the License file CDDL.Schily.txt from this distribution.
57 */
58 /* LINTLIBRARY */
59 #include <schily/mconfig.h>
60 #include <schily/standard.h>
61 #include <schily/utypes.h>
62 #include <schily/getargs.h>
63 #include <schily/varargs.h>
64 #include <schily/string.h>
65 #include <schily/schily.h>
66 #include <schily/ctype.h>
67
68 /*
69 * Various return values
70 */
71 #define RETMAX 2 /* Max. value for getargerror() */
72 #define FLAGDELIM 2 /* "--" stopped flag processing */
73 #define NOTAFLAG 1 /* Not a flag type argument */
74 #define NOARGS 0 /* No more args */
75 #define BADFLAG (-1) /* Not a valid flag argument */
76 #define BADFMT (-2) /* Error in format string */
77 #define NOTAFILE (-3) /* Seems to be a flag type arg */
78 #define RETMIN (-3) /* Min. value for getargerror() */
79
80 LOCAL char *retnames[] = {
81 "NOTAFILE",
82 "BADFMT",
83 "BADFLAG",
84 "NOARGS",
85 "NOTAFLAG",
86 "FLAGDELIM",
87 };
88 #define RNAME(a) (retnames[(a)-RETMIN])
89
90 #define SCANONLY 0 /* Do not try to set argument values */
91 #define SETARGS 1 /* Set argument values from cmdline */
92 #define CHECKARGS 2 /* To not update argv and argc */
93 #define ARGVECTOR 4 /* Use vector instead of list interface */
94 #define NOEQUAL 8 /* -opt=val not allowed for -opt val */
95 #define SINGLEARG 16 /* Last singlechar opt may have arg */
96
97 LOCAL struct ga_props *_getprops __PR((struct ga_props *));
98
99 int _getargs __PR((int *, char *const **, void *,
100 int,
101 struct ga_props *,
102 va_list));
103 LOCAL int dofile __PR((int *, char *const **, const char **,
104 struct ga_props *));
105 LOCAL int doflag __PR((int *, char *const **, const char *,
106 void *, int, va_list));
107 LOCAL int dosflags __PR((const char *, void *,
108 int *, char *const **,
109 int, va_list));
110 LOCAL int checkfmt __PR((const char *));
111 LOCAL int checkeql __PR((const char *));
112 LOCAL BOOL validfmt __PR((unsigned char c));
113
114 LOCAL va_list va_dummy;
115
116 LOCAL char fmtspecs[] = "#?*&~+%";
117
118 #define isfmtspec(c) (strchr(fmtspecs, c) != NULL)
119
120 LOCAL struct ga_props props_default = { 0, 0, sizeof (struct ga_props) };
121 LOCAL struct ga_props props_posix = { GAF_POSIX_DEFAULT, 0, sizeof (struct ga_props) };
122
123 EXPORT int
_getarginit(props,size,flags)124 _getarginit(props, size, flags)
125 struct ga_props *props;
126 size_t size;
127 UInt32_t flags;
128 {
129 if (size > sizeof (struct ga_props))
130 return (-1);
131
132 /*
133 * GAF_POSIX may be used as an alias for the flags that currently
134 * define POSIX behavior.
135 */
136 if (flags == GAF_POSIX)
137 flags = GAF_POSIX_DEFAULT;
138
139 props->ga_flags = flags;
140 props->ga_oflags = 0;
141 props->ga_size = size;
142 return (0);
143 }
144
145 LOCAL struct ga_props *
_getprops(props)146 _getprops(props)
147 struct ga_props *props;
148 {
149 if (props == GA_NO_PROPS)
150 props = &props_default;
151 else if (props == GA_POSIX_PROPS)
152 props = &props_posix;
153 props->ga_oflags = 0;
154 /*
155 * GAF_POSIX may be used as an alias for the flags that currently
156 * define POSIX behavior.
157 */
158 if (props->ga_flags == GAF_POSIX)
159 props->ga_flags = GAF_POSIX_DEFAULT;
160
161 return (props);
162 }
163
164 /*
165 * get flags until a non flag type argument is reached (old version)
166 */
167 /* VARARGS3 */
168 #ifdef PROTOTYPES
169 EXPORT int
getargs(int * pac,char * const ** pav,const char * fmt,...)170 getargs(int *pac, char *const **pav, const char *fmt, ...)
171 #else
172 EXPORT int
173 getargs(pac, pav, fmt, va_alist)
174 int *pac;
175 char **pav[];
176 char *fmt;
177 va_dcl
178 #endif
179 {
180 va_list args;
181 int ret;
182
183 #ifdef PROTOTYPES
184 va_start(args, fmt);
185 #else
186 va_start(args);
187 #endif
188 ret = _getargs(pac, pav, (void *)fmt, SETARGS, GA_NO_PROPS, args);
189 va_end(args);
190 return (ret);
191 }
192
193
194 /*
195 * get flags until a non flag type argument is reached (list version)
196 */
197 /* VARARGS4 */
198 #ifdef PROTOTYPES
199 EXPORT int
getlargs(int * pac,char * const ** pav,struct ga_props * props,const char * fmt,...)200 getlargs(int *pac, char *const **pav, struct ga_props *props, const char *fmt, ...)
201 #else
202 EXPORT int
203 getlargs(pac, pav, props, fmt, va_alist)
204 int *pac;
205 char **pav[];
206 struct ga_props *props;
207 char *fmt;
208 va_dcl
209 #endif
210 {
211 va_list args;
212 int ret;
213
214 #ifdef PROTOTYPES
215 va_start(args, fmt);
216 #else
217 va_start(args);
218 #endif
219 ret = _getargs(pac, pav, (void *)fmt, SETARGS, props, args);
220 va_end(args);
221 return (ret);
222 }
223
224
225 /*
226 * get flags until a non flag type argument is reached (vector version)
227 */
228 EXPORT int
getvargs(pac,pav,props,vfmt)229 getvargs(pac, pav, props, vfmt)
230 int *pac;
231 char * const *pav[];
232 struct ga_props *props;
233 struct ga_flags *vfmt;
234 {
235 return (_getargs(pac, pav, vfmt, SETARGS | ARGVECTOR, props, va_dummy));
236 }
237
238
239 /*
240 * get all flags on the command line, do not stop on files (old version)
241 */
242 /* VARARGS3 */
243 #ifdef PROTOTYPES
244 EXPORT int
getallargs(int * pac,char * const ** pav,const char * fmt,...)245 getallargs(int *pac, char *const **pav, const char *fmt, ...)
246 #else
247 EXPORT int
248 getallargs(pac, pav, fmt, va_alist)
249 int *pac;
250 char **pav[];
251 char *fmt;
252 va_dcl
253 #endif
254 {
255 va_list args;
256 int ret;
257
258 #ifdef PROTOTYPES
259 va_start(args, fmt);
260 #else
261 va_start(args);
262 #endif
263 for (; ; (*pac)--, (*pav)++) {
264 if ((ret = _getargs(pac, pav, (void *)fmt, SETARGS, GA_NO_PROPS, args)) < NOTAFLAG)
265 break;
266 }
267 va_end(args);
268 return (ret);
269 }
270
271
272 /*
273 * get all flags on the command line, do not stop on files (list version)
274 */
275 /* VARARGS4 */
276 #ifdef PROTOTYPES
277 EXPORT int
getlallargs(int * pac,char * const ** pav,struct ga_props * props,const char * fmt,...)278 getlallargs(int *pac, char *const **pav, struct ga_props *props, const char *fmt, ...)
279 #else
280 EXPORT int
281 getlallargs(pac, pav, props, fmt, va_alist)
282 int *pac;
283 char **pav[];
284 struct ga_props *props;
285 char *fmt;
286 va_dcl
287 #endif
288 {
289 va_list args;
290 int ret;
291
292 #ifdef PROTOTYPES
293 va_start(args, fmt);
294 #else
295 va_start(args);
296 #endif
297 props = _getprops(props);
298 for (; ; (*pac)--, (*pav)++) {
299 if ((ret = _getargs(pac, pav, (void *)fmt, SETARGS, props, args)) < NOTAFLAG)
300 break;
301 /*
302 * The default is to parse all options on the command line and
303 * to let "--" only make the next argument a non-option.
304 */
305 if (ret == FLAGDELIM && (props->ga_flags & GAF_DELIM_DASHDASH))
306 break;
307 }
308 va_end(args);
309 return (ret);
310 }
311
312
313 /*
314 * get all flags on the command line, do not stop on files (vector version)
315 */
316 EXPORT int
getvallargs(pac,pav,props,vfmt)317 getvallargs(pac, pav, props, vfmt)
318 int *pac;
319 char * const *pav[];
320 struct ga_props *props;
321 struct ga_flags *vfmt;
322 {
323 int ret;
324
325 props = _getprops(props);
326 for (; ; (*pac)--, (*pav)++) {
327 if ((ret = _getargs(pac, pav, vfmt, SETARGS | ARGVECTOR, props, va_dummy)) < NOTAFLAG)
328 break;
329 /*
330 * The default is to parse all options on the command line and
331 * to let "--" only make the next argument a non-option.
332 */
333 if (ret == FLAGDELIM && (props->ga_flags & GAF_DELIM_DASHDASH))
334 break;
335 }
336 return (ret);
337 }
338
339
340 /*
341 * get next non flag type argument (i.e. a file) (old version)
342 * getfiles() is a dry run getargs()
343 */
344 EXPORT int
getfiles(pac,pav,fmt)345 getfiles(pac, pav, fmt)
346 int *pac;
347 char *const *pav[];
348 const char *fmt;
349 {
350 return (_getargs(pac, pav, (void *)fmt, SCANONLY, GA_NO_PROPS, va_dummy));
351 }
352
353
354 /*
355 * get next non flag type argument (i.e. a file) (list version)
356 * getlfiles() is a dry run getlargs()
357 */
358 EXPORT int
getlfiles(pac,pav,props,fmt)359 getlfiles(pac, pav, props, fmt)
360 int *pac;
361 char *const *pav[];
362 struct ga_props *props;
363 const char *fmt;
364 {
365 return (_getargs(pac, pav, (void *)fmt, SCANONLY, props, va_dummy));
366 }
367
368
369 /*
370 * get next non flag type argument (i.e. a file) (vector version)
371 * getvfiles() is a dry run getvargs()
372 */
373 EXPORT int
getvfiles(pac,pav,props,vfmt)374 getvfiles(pac, pav, props, vfmt)
375 int *pac;
376 char *const *pav[];
377 struct ga_props *props;
378 struct ga_flags *vfmt;
379 {
380 return (_getargs(pac, pav, vfmt, SCANONLY | ARGVECTOR, props, va_dummy));
381 }
382
383
384 /*
385 * check args until the next non flag type argmument is reached
386 * *pac is decremented, *pav is incremented so that the
387 * non flag type argument is at *pav[0]
388 *
389 * return code:
390 * +2 FLAGDELIM "--" stopped flag processing
391 * +1 NOTAFLAG not a flag type argument (is a file)
392 * 0 NOARGS no more args
393 * -1 BADFLAG a non-matching flag type argument
394 * -2 BADFMT bad syntax in format string
395 */
396 /* LOCAL int */
397 EXPORT int
_getargs(pac,pav,vfmt,flags,props,args)398 _getargs(pac, pav, vfmt, flags, props, args)
399 register int *pac;
400 register char *const **pav;
401 void *vfmt;
402 int flags;
403 struct ga_props *props;
404 va_list args;
405 {
406 const char *argp;
407 int ret;
408
409
410 props = _getprops(props);
411
412 if (props->ga_flags & GAF_NO_EQUAL)
413 flags |= NOEQUAL;
414 if (props->ga_flags & GAF_SINGLEARG)
415 flags |= SINGLEARG;
416
417 for (; *pac > 0; (*pac)--, (*pav)++) {
418 argp = **pav;
419
420 ret = dofile(pac, pav, &argp, props);
421
422 if (ret != NOTAFILE)
423 return (ret);
424
425 ret = doflag(pac, pav, argp, vfmt, flags, args);
426
427 if (ret != NOTAFLAG)
428 return (ret);
429 }
430 return (NOARGS);
431 }
432
433
434 /*
435 * check if *pargp is a file type argument
436 */
437 LOCAL int
dofile(pac,pav,pargp,props)438 dofile(pac, pav, pargp, props)
439 register int *pac;
440 register char *const **pav;
441 const char **pargp;
442 struct ga_props *props;
443 {
444 register const char *argp = *pargp;
445
446
447 if (argp[0] == '-') {
448 /*
449 * "-" is a special non flag type argument
450 * that usually means take stdin instead of a named file
451 */
452 if (argp[1] == '\0')
453 return (NOTAFLAG);
454 /*
455 * "--" is a prefix to take the next argument
456 * as non flag type argument
457 * NOTE: POSIX requires "--" to indicate the end of the
458 * flags on the command line. Programs that like to be
459 * 100% POSIX compliant call only get[lv]args() once
460 * and then process the list of files from cav[0].
461 */
462 if (argp[1] == '-' && argp[2] == '\0') {
463 if (--(*pac) > 0) {
464 (*pav)++;
465 return (FLAGDELIM);
466 } else {
467 return (NOARGS);
468 }
469 }
470 }
471
472 /*
473 * Now check if it may be flag type argument at all.
474 * Flag type arguments begin with a '-', a '+' or contain a '='
475 * i.e. -flag +flag or flag=
476 * The behavior here may be controlled by props->ga_flags to
477 * allow getargs() to e.g. behave fully POSIX compliant.
478 */
479 if (argp[0] != '-') {
480 if (argp[0] == '+' && (props->ga_flags & GAF_NO_PLUS) == 0)
481 return (NOTAFILE); /* This is a flag type arg */
482
483 /*
484 * If 'flag=value' is not allowed at all, speed things up
485 * and do not call checkeql() to check for '='.
486 */
487 if (props->ga_flags & GAF_NO_EQUAL)
488 return (NOTAFLAG);
489 if (checkeql(argp) && (props->ga_flags & GAF_NEED_DASH) == 0)
490 return (NOTAFILE); /* This is a flag type arg */
491 return (NOTAFLAG);
492 }
493 return (NOTAFILE); /* This is a flag type arg */
494 }
495
496
497 /*
498 * compare argp with the format string
499 * if a match is found store the result a la scanf in one of the
500 * arguments pointed to in the va_list
501 *
502 * If (flags & SETARGS) == 0, only check arguments for getfiles()
503 * In case that (flags & SETARGS) == 0 or that (flags & ARGVECTOR) != 0,
504 * va_list may be a dummy argument.
505 */
506 LOCAL int
doflag(pac,pav,argp,vfmt,flags,oargs)507 doflag(pac, pav, argp, vfmt, flags, oargs)
508 int *pac;
509 char *const **pav;
510 register const char *argp;
511 void *vfmt;
512 int flags;
513 va_list oargs;
514 {
515 register const char *fmt = (const char *)vfmt;
516 struct ga_flags *flagp = vfmt;
517 const char *fmtp;
518 long val;
519 Llong llval;
520 int singlecharflag = 0;
521 BOOL isspec;
522 BOOL hasdash = FALSE;
523 BOOL doubledash = FALSE;
524 BOOL haseql = checkeql(argp);
525 const char *sargp;
526 const char *sfmt;
527 va_list args;
528 char *const *spav = *pav;
529 int spac = *pac;
530 void *curarg = (void *)0;
531
532 sfmt = fmt;
533 /*
534 * flags beginning with '-' don't have to include the '-' in
535 * the format string.
536 * flags beginning with '+' have to include it in the format string.
537 */
538 if (argp[0] == '-') {
539 argp++;
540 hasdash = TRUE;
541 /*
542 * Implement legacy support for --longopt
543 * If we find a double dash, we do not look for combinations
544 * of boolean single char flags.
545 */
546 if (argp[0] == '-') {
547 argp++;
548 doubledash = TRUE;
549 /*
550 * Allow -- only for long options.
551 */
552 if (argp[1] == '\0') {
553 return (BADFLAG);
554 }
555 }
556 }
557 sargp = argp;
558
559 /*
560 * Initialize 'args' to the start of the argument list.
561 * I don't know any portable way to copy an arbitrary
562 * C object so I use a system-specific routine
563 * (probably a macro) from stdarg.h. (Remember that
564 * if va_list is an array, 'args' will be a pointer
565 * and '&args' won't be what I would need for memcpy.)
566 * It is a system requirement for SVr4 compatibility
567 * to be able to do this assgignement. If your system
568 * defines va_list to be an array but does not define
569 * va_copy() you are lost.
570 * This is needed to make sure, that 'oargs' will not
571 * be clobbered.
572 */
573 va_copy(args, oargs);
574
575 if (flags & ARGVECTOR) {
576 sfmt = fmt = flagp->ga_format;
577 if (fmt == NULL)
578 sfmt = fmt = "";
579 if (flags & SETARGS)
580 curarg = flagp->ga_arg;
581 } else if (flags & SETARGS) {
582 curarg = va_arg(args, void *);
583 }
584 /*
585 * check if the first flag in format string is a singlechar flag
586 */
587 again:
588 if (fmt[0] != '\0' &&
589 (fmt[1] == ',' || fmt[1] == '+' ||
590 fmt[1] == '~' || fmt[1] == '%' || fmt[1] == '\0'))
591 singlecharflag++;
592 /*
593 * check the whole format string for a match
594 */
595 for (;;) {
596 for (; *fmt; fmt++, argp++) {
597 if (*fmt == '\\') {
598 /*
599 * Allow "#?*&+" to appear inside a flag.
600 * NOTE: they must be escaped by '\\' only
601 * inside the the format string.
602 * Only characters permitted by the
603 * function validfmt() are really
604 * accepted. So if we like to permit
605 * more than '+' in addition, we need
606 * to enhance validfmt() as well.
607 */
608 fmt++;
609 isspec = FALSE;
610 } else {
611 isspec = isfmtspec(*fmt);
612 }
613 /*
614 * If isspec is TRUE, the arg beeing checked starts
615 * like a valid flag. Argp now points to the rest.
616 */
617 if (isspec) {
618 /*
619 * If *argp is '+' and we are on the
620 * beginning of the arg that is currently
621 * checked, this cannot be an inc type flag.
622 */
623 if (*argp == '+' && argp == sargp)
624 continue;
625 /*
626 * skip over to arg of flag
627 */
628 if (*argp == '=') {
629 if (flags & NOEQUAL)
630 return (BADFLAG);
631 argp++;
632 } else if (*argp != '\0' && haseql) {
633 /*
634 * Flag and arg are not separated by a
635 * space.
636 * Check here for:
637 * xxxxx=yyyyy match on '&'
638 * Checked before:
639 * abc=yyyyy match on 'abc&'
640 * or 'abc*'
641 * or 'abc#'
642 * We come here if 'argp' starts with
643 * the same sequence as a valid flag
644 * and contains an equal sign.
645 * We have tested before if the text
646 * before 'argp' matches exactly.
647 * At this point we have no exact match
648 * and we only allow to match
649 * the special pattern '&'.
650 * We need this e.g. for 'make'.
651 * We allow any flag type argument to
652 * match the format string "&" to set
653 * up a function that handles all odd
654 * stuff that getargs will not grok.
655 * In addition, to allow getargs to be
656 * used for CPP type flags we allow to
657 * match -Dabc=xyz on 'D&'. Note that
658 * Dabc=xyz will not match 'D&'.
659 */
660 if ((!hasdash && argp != sargp) || *fmt != '&')
661 goto nextarg;
662 }
663
664 /*
665 * The format string 'f* ' may be used
666 * to disallow -ffoo for f*
667 *
668 * The same behavior is implemented for
669 * 'f# '. 'f? ' and 'f& '.
670 */
671 if (!haseql && *argp != '\0' &&
672 (fmt[0] == '*' || fmt[0] == '#' ||
673 fmt[0] == '?' || fmt[0] == '&') &&
674 fmt[1] == ' ') {
675 goto nextarg;
676 }
677
678 /*
679 * *arpp == '\0' || !haseql
680 * We come here if 'argp' starts with
681 * the same sequence as a valid flag.
682 * This will match on the following args:
683 * -farg match on 'f*'
684 * -f12 match on 'f#'
685 * +12 match on '+#'
686 * -12 match on '#'
687 * and all args that are separated from
688 * their flags.
689 * In the switch statement below, we check
690 * if the text after 'argp' (if *argp != 0) or
691 * the next arg is a valid arg for this flag.
692 */
693 break;
694 } else if (*fmt == *argp) {
695 unsigned char c;
696
697 if (argp[1] == '\0' &&
698 (fmt[1] == '\0' || fmt[1] == ',')) {
699
700 if (flags & SETARGS)
701 *((int *)curarg) = TRUE;
702
703
704 return (checkfmt(fmt)); /* XXX */
705 }
706 /*
707 * Check whether the current program argument
708 * contains characters that are not allowed in
709 * option names. Check current character.
710 */
711 c = *(unsigned char *)fmt;
712 if (c == ',' || !validfmt(c))
713 goto nextarg;
714 } else {
715 /*
716 * skip over to next format identifier
717 * & reset arg pointer
718 */
719 nextarg:
720 while (*fmt != ',' && *fmt != '\0') {
721 /* function has extra arg on stack */
722 if ((*fmt == '&' || *fmt == '~') &&
723 (flags & (SETARGS|ARGVECTOR)) == SETARGS) {
724 curarg = va_arg(args, void *);
725 }
726 fmt++;
727 }
728 argp = sargp;
729 break;
730 }
731 }
732 switch (*fmt) {
733
734 case '\0':
735 /*
736 * Boolean type has been tested before.
737 */
738 if (flags & ARGVECTOR) {
739 if (flagp[1].ga_format != NULL) {
740 flagp++;
741 sfmt = fmt = flagp->ga_format;
742 if (flags & SETARGS)
743 curarg = flagp->ga_arg;
744 argp = sargp;
745 goto again;
746 }
747 }
748 if (singlecharflag && !doubledash &&
749 (val = dosflags(sargp, vfmt, pac, pav,
750 CHECKARGS |
751 (flags & ~SETARGS),
752 va_dummy)) == BADFLAG) {
753 return (val);
754 }
755 if (singlecharflag && !doubledash &&
756 (val = dosflags(sargp, vfmt, pac, pav,
757 flags,
758 oargs)) != BADFLAG) {
759 return (val);
760 }
761 return (BADFLAG);
762
763 case ',':
764 fmt++;
765 if (fmt[0] == '\0') /* Should we allow "a,b,c,"? */
766 return (BADFMT);
767 if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0')
768 singlecharflag++;
769 if ((flags & (SETARGS|ARGVECTOR)) == SETARGS)
770 curarg = va_arg(args, void *);
771 continue;
772
773 case '*':
774 if (*argp == '\0' && fmt[1] != '_') {
775 if (*pac > 1) {
776 (*pac)--;
777 (*pav)++;
778 argp = **pav;
779 } else {
780 return (BADFLAG);
781 }
782 }
783 if (fmt[1] == '_') /* To disallow -f foo for f* */
784 fmt++;
785 else if (fmt[1] == ' ') /* To disallow -ffoo for f* */
786 fmt++;
787
788 if (flags & SETARGS)
789 *((const char **)curarg) = argp;
790
791
792 return (checkfmt(fmt));
793
794 case '?':
795 if (*argp == '\0' && fmt[1] != '_') {
796 if (*pac > 1) {
797 (*pac)--;
798 (*pav)++;
799 argp = **pav;
800 } else {
801 return (BADFLAG);
802 }
803 }
804 if (fmt[1] == '_') /* To disallow -f c for f? */
805 fmt++;
806 else if (fmt[1] == ' ') /* To disallow -fc for f? */
807 fmt++;
808
809 /*
810 * Allow -f '' to specify a nul character.
811 * If more than one char arg, it
812 * cannot be a character argument.
813 */
814 if (argp[0] != '\0' && argp[1] != '\0')
815 goto nextchance;
816
817 if (flags & SETARGS)
818 *((char *)curarg) = *argp;
819
820
821 return (checkfmt(fmt));
822
823 case '+':
824 /*
825 * inc type is similar to boolean,
826 * there is no arg in argp to convert.
827 */
828 if (*argp != '\0')
829 goto nextchance;
830 /*
831 * If *fmt is '+' and we are on the beginning
832 * of the format desciptor that is currently
833 * checked, this cannot be an inc type flag.
834 */
835 if (fmt == sfmt || fmt[-1] == ',')
836 goto nextchance;
837
838 fmtp = fmt;
839 if (fmt[1] == 'l' || fmt[1] == 'L') {
840 if (fmt[2] == 'l' || fmt[2] == 'L') {
841 if (flags & SETARGS)
842 *((Llong *)curarg) += 1;
843 fmt += 2;
844 } else {
845 if (flags & SETARGS)
846 *((long *)curarg) += 1;
847 fmt++;
848 }
849 } else if (fmt[1] == 's' || fmt[1] == 'S') {
850 if (flags & SETARGS)
851 *((short *)curarg) += 1;
852 fmt++;
853 } else if (fmt[1] == 'c' || fmt[1] == 'C') {
854 if (flags & SETARGS)
855 *((char *)curarg) += 1;
856 fmt++;
857 } else {
858 if (fmt[1] == 'i' || fmt[1] == 'I')
859 fmt++;
860 if (flags & SETARGS)
861 *((int *)curarg) += 1;
862 }
863
864
865 return (checkfmt(fmt));
866
867 case '%':
868 /*
869 * inc type is similar to boolean,
870 * there is no arg in argp to convert.
871 */
872 if (*argp != '\0')
873 goto nextchance;
874
875 fmt++;
876 if (*fmt >= '0' && *fmt <= '9')
877 val = *fmt - '0';
878 else
879 goto nextchance;
880
881 fmtp = fmt;
882 llval = (Llong)val;
883 if (fmt[1] == 'l' || fmt[1] == 'L') {
884 if (fmt[2] == 'l' || fmt[2] == 'L') {
885 if (flags & SETARGS)
886 *((Llong *)curarg) = llval;
887 fmt += 2;
888 } else {
889 if (flags & SETARGS)
890 *((long *)curarg) = val;
891 fmt++;
892 }
893 } else if (fmt[1] == 's' || fmt[1] == 'S') {
894 if (flags & SETARGS)
895 *((short *)curarg) = val;
896 fmt++;
897 } else if (fmt[1] == 'c' || fmt[1] == 'C') {
898 if (flags & SETARGS)
899 *((char *)curarg) = val;
900 fmt++;
901 } else {
902 if (fmt[1] == 'i' || fmt[1] == 'I')
903 fmt++;
904 if (flags & SETARGS)
905 *((int *)curarg) = val;
906 }
907
908
909 return (checkfmt(fmt));
910
911 case '#':
912 if (*argp == '\0' && fmt[1] != '_') {
913 if (*pac > 1) {
914 (*pac)--;
915 (*pav)++;
916 argp = **pav;
917 } else {
918 return (BADFLAG);
919 }
920 }
921 if (fmt[1] == '_') /* To disallow -f 123 for f# */
922 fmt++;
923 else if (fmt[1] == ' ') /* To disallow -f123 for f# */
924 fmt++;
925
926 if (*astoll(argp, &llval) != '\0') {
927 /*
928 * arg is not a valid number!
929 * go to next format in the format string
930 * and check if arg matches any other type
931 * in the format specs.
932 */
933 nextchance:
934 while (*fmt != ',' && *fmt != '\0') {
935 if ((*fmt == '&' || *fmt == '~') &&
936 (flags & (SETARGS|ARGVECTOR)) == SETARGS) {
937 curarg = va_arg(args, void *);
938 }
939 fmt++;
940 }
941 argp = sargp;
942 *pac = spac;
943 *pav = spav;
944 continue;
945 }
946 fmtp = fmt;
947 val = (long)llval;
948 if (fmt[1] == 'l' || fmt[1] == 'L') {
949 if (fmt[2] == 'l' || fmt[2] == 'L') {
950 if (flags & SETARGS)
951 *((Llong *)curarg) = llval;
952 fmt += 2;
953 } else {
954 if (flags & SETARGS)
955 *((long *)curarg) = val;
956 fmt++;
957 }
958 } else if (fmt[1] == 's' || fmt[1] == 'S') {
959 if (flags & SETARGS)
960 *((short *)curarg) = (short)val;
961 fmt++;
962 } else if (fmt[1] == 'c' || fmt[1] == 'C') {
963 if (flags & SETARGS)
964 *((char *)curarg) = (char)val;
965 fmt++;
966 } else {
967 if (fmt[1] == 'i' || fmt[1] == 'I')
968 fmt++;
969 if (flags & SETARGS)
970 *((int *)curarg) = (int)val;
971 }
972
973 return (checkfmt(fmt));
974
975 case '~':
976 if (*argp != '\0')
977 goto nextchance;
978 if (haseql) {
979 return (BADFLAG);
980 }
981 goto callfunc;
982
983 case '&':
984 if (*argp == '\0' && fmt[1] != '_') {
985 if (*pac > 1) {
986 (*pac)--;
987 (*pav)++;
988 argp = **pav;
989 } else {
990 return (BADFLAG);
991 }
992 }
993 callfunc:
994
995 if (*fmt == '&' && fmt[1] == '_') /* To disallow -f foo for f& */
996 fmt++;
997 else if (fmt[1] == ' ') /* To disallow -ffoo for f& */
998 fmt++;
999 if ((val = checkfmt(fmt)) != NOTAFLAG)
1000 return (val);
1001
1002 fmtp = sargp;
1003 if (hasdash)
1004 fmtp--;
1005 if (doubledash)
1006 fmtp--;
1007 if ((flags & (SETARGS|ARGVECTOR)) == (SETARGS|ARGVECTOR)) {
1008 int ret;
1009
1010 if (flagp->ga_funcp == NULL)
1011 return (BADFMT);
1012
1013 ret = ((*flagp->ga_funcp) (argp, flagp->ga_arg,
1014 pac, pav, fmtp));
1015 if (ret != NOTAFILE)
1016 return (ret);
1017 fmt++;
1018 } else
1019 if (flags & SETARGS) {
1020 int ret;
1021 void *funarg = va_arg(args, void *);
1022
1023 if (curarg == NULL)
1024 return (BADFMT);
1025 ret = ((*(getpargfun)curarg) (argp, funarg,
1026 pac, pav, fmtp));
1027 if (ret != NOTAFILE)
1028 return (ret);
1029 fmt++;
1030 } else {
1031 return (val);
1032 }
1033 /*
1034 * Called function returns NOTAFILE: try next format.
1035 */
1036 }
1037 }
1038 }
1039
1040
1041 /*
1042 * parse args for combined single char flags
1043 */
1044 typedef struct {
1045 void *curarg; /* The pointer to the arg to modify */
1046 void *curfun; /* The pointer to the function to call */
1047 char c; /* The single char flag character */
1048 char type; /* The type of the single char flag */
1049 char fmt; /* The format type of the single char flag */
1050 char val; /* The value to assign for BOOL flags */
1051 } sflags;
1052
1053 LOCAL int
dosflags(argp,vfmt,pac,pav,flags,oargs)1054 dosflags(argp, vfmt, pac, pav, flags, oargs)
1055 register const char *argp;
1056 void *vfmt;
1057 int *pac;
1058 char *const **pav;
1059 int flags;
1060 va_list oargs;
1061 {
1062 register const char *fmt = (const char *)vfmt;
1063 struct ga_flags *flagp = vfmt;
1064 #define MAXSF 64
1065 sflags sf[MAXSF];
1066 char fl[256];
1067 va_list args;
1068 register sflags *rsf = sf;
1069 register int nsf = 0;
1070 register const char *p = argp;
1071 register int i;
1072 register void *curarg = (void *)0;
1073 getpargfun curfun = 0;
1074 char type;
1075
1076 /*
1077 * Initialize 'args' to the start of the argument list.
1078 * I don't know any portable way to copy an arbitrary
1079 * C object so I use a system-specific routine
1080 * (probably a macro) from stdarg.h. (Remember that
1081 * if va_list is an array, 'args' will be a pointer
1082 * and '&args' won't be what I would need for memcpy.)
1083 * It is a system requirement for SVr4 compatibility
1084 * to be able to do this assgignement. If your system
1085 * defines va_list to be an array but does not define
1086 * va_copy() you are lost.
1087 * This is needed to make sure, that 'oargs' will not
1088 * be clobbered.
1089 */
1090 va_copy(args, oargs);
1091
1092 if (flags & ARGVECTOR) {
1093 fmt = flagp->ga_format;
1094 if (fmt == NULL)
1095 fmt = "";
1096 if (flags & SETARGS) {
1097 curarg = flagp->ga_arg;
1098 curfun = flagp->ga_funcp;
1099 }
1100 } else if (flags & SETARGS) {
1101 /*
1102 * We set curfun to curarg. We later get the real
1103 * curarg in case that we see a function callback
1104 * but we need curfun first in this case.
1105 */
1106 curarg = va_arg(args, void *);
1107 curfun = (getpargfun)curarg;
1108 }
1109
1110 for (i = 0; i < sizeof (fl); i++) {
1111 fl[i] = 0;
1112 }
1113 while (*p) {
1114 for (i = 0; i < nsf; i++) {
1115 if (rsf[i].c == *p)
1116 break;
1117 }
1118 if (i >= MAXSF) {
1119 va_end(args);
1120 return (BADFLAG);
1121 }
1122 if (i == nsf) {
1123 rsf[i].curarg = (void *)0;
1124 rsf[i].curfun = (void *)0;
1125 rsf[i].c = *p;
1126 rsf[i].type = (char)-1;
1127 rsf[i].fmt = '\0';
1128 rsf[i].val = (char)TRUE;
1129 nsf++;
1130 }
1131 fl[*p & 0xFF] = i;
1132 p++;
1133 }
1134
1135 again:
1136 while (*fmt) {
1137 if ((((flags & SINGLEARG) &&
1138 ((fmt[1] == '*' || fmt[1] == '?' ||
1139 fmt[1] == '&' || fmt[1] == '#'))) ||
1140 (!isfmtspec(*fmt) &&
1141 (fmt[1] == ',' || fmt[1] == '+' ||
1142 fmt[1] == '~' || fmt[1] == '%' || fmt[1] == '\0'))) &&
1143 strchr(argp, *fmt)) {
1144 for (i = 0; i < nsf; i++) {
1145 if (rsf[i].c == *fmt) {
1146 if ((fmt[1] == '+') ||
1147 (fmt[1] == '#' && (flags & SINGLEARG))) {
1148 rsf[i].fmt = fmt[1];
1149 fmt++;
1150 if (fmt[1] == ',' ||
1151 fmt[1] == '\0') {
1152 rsf[i].type = 'i';
1153 } else if ((fmt[1] == 'l' ||
1154 fmt[1] == 'L') &&
1155 (fmt[2] == 'l' ||
1156 fmt[2] == 'L')) {
1157 /*
1158 * Type 'Q'uad (ll)
1159 */
1160 rsf[i].type = 'Q';
1161 fmt++;
1162 } else {
1163 /*
1164 * Type 'l','i','s','c'
1165 */
1166 rsf[i].type = fmt[1];
1167 }
1168 } else if (fmt[1] == '%') {
1169 fmt++;
1170 rsf[i].fmt = '%';
1171
1172 if (fmt[1] >= '0' &&
1173 fmt[1] <= '9')
1174 rsf[i].val = fmt[1] - '0';
1175 fmt++;
1176 if (fmt[1] == ',' ||
1177 fmt[1] == '\0') {
1178 rsf[i].type = 'i';
1179 } else if ((fmt[1] == 'l' ||
1180 fmt[1] == 'L') &&
1181 (fmt[2] == 'l' ||
1182 fmt[2] == 'L')) {
1183 /*
1184 * Type 'Q'uad (ll)
1185 */
1186 rsf[i].type = 'Q';
1187 fmt++;
1188 } else {
1189 /*
1190 * Type 'l','i','s','c'
1191 */
1192 rsf[i].type = fmt[1];
1193 }
1194 } else if ((fmt[1] == '*' ||
1195 fmt[1] == '?') &&
1196 (flags & SINGLEARG)) {
1197 fmt++;
1198 rsf[i].fmt = fmt[0];
1199 rsf[i].type = fmt[0];
1200 if (fmt[1] == ' ' || fmt[1] == '_')
1201 rsf[i].type = fmt[1];
1202 } else if ((fmt[1] == '~') ||
1203 (fmt[1] == '&' && (flags & SINGLEARG))) {
1204 rsf[i].fmt = fmt[1];
1205 rsf[i].type = fmt[1];
1206 /*
1207 * Let fmt point to ',' to
1208 * prevent to fetch the
1209 * func arg twice.
1210 */
1211 fmt += 2;
1212 if ((rsf[i].fmt == '&') &&
1213 (fmt[0] == ' ' || fmt[0] == '_'))
1214 rsf[i].type = fmt[0];
1215 rsf[i].curfun = (void *)curfun;
1216 if ((flags & (SETARGS|ARGVECTOR)) == SETARGS)
1217 curarg = va_arg(args, void *);
1218 } else {
1219 /*
1220 * ',' or '\0' for BOOL
1221 */
1222 rsf[i].type = fmt[1];
1223 }
1224 rsf[i].curarg = curarg;
1225 break;
1226 }
1227 }
1228 }
1229 while (*fmt != ',' && *fmt != '\0') {
1230 /*
1231 * function has extra arg on stack. The code above
1232 * prevents us from fetching this arg twice.
1233 */
1234 if ((*fmt == '&' || *fmt == '~') &&
1235 (flags & (SETARGS|ARGVECTOR)) == SETARGS) {
1236 curarg = va_arg(args, void *);
1237 }
1238 fmt++;
1239 }
1240 if (*fmt != '\0')
1241 fmt++;
1242 else
1243 break;
1244
1245 if ((flags & (SETARGS|ARGVECTOR)) == SETARGS) {
1246 /*
1247 * We set curfun to curarg. We later get the real
1248 * curarg in case that we see a function callback
1249 * but we need curfun first in this case.
1250 */
1251 curarg = va_arg(args, void *);
1252 curfun = (getpargfun)curarg;
1253 }
1254 }
1255 if ((flags & ARGVECTOR) && flagp[1].ga_format != NULL) {
1256 flagp++;
1257 fmt = flagp->ga_format;
1258 if (flags & SETARGS) {
1259 curarg = flagp->ga_arg;
1260 curfun = flagp->ga_funcp;
1261 }
1262 goto again;
1263 }
1264
1265 for (p = argp; *p; p++) {
1266 char tfmt;
1267
1268 i = fl[*p & 0xFF];
1269 tfmt = rsf[i].fmt;
1270 type = rsf[i].type;
1271 if (type == (char)-1) {
1272 return (BADFLAG);
1273 }
1274
1275 if ((flags & SETARGS) == 0 &&
1276 (tfmt == '*' || tfmt == '?' || tfmt == '&' || tfmt == '#')) {
1277 /*
1278 * POSIX 12.2 Guideline 5 requires that any number of
1279 * options without argument may be followed by a
1280 * single option with parameter. So all characters
1281 * past this one belong to the argument of this option.
1282 */
1283 if (flags & CHECKARGS)
1284 break;
1285 if (*++p == '\0' && type != '_') {
1286 if (*pac > 1) {
1287 (*pac)--;
1288 (*pav)++;
1289 p = **pav;
1290 } else {
1291 return (BADFLAG);
1292 }
1293 }
1294 break;
1295 }
1296 if ((flags & SETARGS) &&
1297 (rsf[i].curfun || rsf[i].curarg)) {
1298 Llong llval = 0;
1299 long val = 0;
1300
1301 if (tfmt == '*' || tfmt == '?' ||
1302 tfmt == '&' || tfmt == '#') {
1303 /*
1304 * If type is '_', then -f ... results in an
1305 * empty argument. This is because we disallow
1306 * -f foo for f* in this case.
1307 */
1308 if (*++p == '\0' && type != '_') {
1309 if (*pac > 1) {
1310 (*pac)--;
1311 (*pav)++;
1312 p = **pav;
1313 } else {
1314 return (BADFLAG);
1315 }
1316 }
1317 }
1318
1319 if (tfmt == '#' && *astoll(p, &llval) != '\0') {
1320 return (BADFLAG);
1321 }
1322 val = (long)llval;
1323
1324 if (type == ',' || type == '\0') {
1325 *((int *)rsf[i].curarg) = TRUE;
1326 } else if (type == 'i' || type == 'I') {
1327 if (tfmt == '+')
1328 *((int *)rsf[i].curarg) += 1;
1329 else if (tfmt == '#')
1330 *((int *)rsf[i].curarg) = (int)val;
1331 else
1332 *((int *)rsf[i].curarg) = rsf[i].val;
1333 } else if (type == 'l' || type == 'L') {
1334 if (tfmt == '+')
1335 *((long *)rsf[i].curarg) += 1;
1336 else if (tfmt == '#')
1337 *((long *)rsf[i].curarg) = val;
1338 else
1339 *((long *)rsf[i].curarg) = rsf[i].val;
1340 } else if (type == 'Q') {
1341 if (tfmt == '+')
1342 *((Llong *)rsf[i].curarg) += 1;
1343 else if (tfmt == '#')
1344 *((Llong *)rsf[i].curarg) = llval;
1345 else
1346 *((Llong *)rsf[i].curarg) = rsf[i].val;
1347 } else if (type == 's' || type == 'S') {
1348 if (tfmt == '+')
1349 *((short *)rsf[i].curarg) += 1;
1350 else if (tfmt == '#')
1351 *((short *)rsf[i].curarg) = (short)val;
1352 else
1353 *((short *)rsf[i].curarg) = rsf[i].val;
1354 } else if (type == 'c' || type == 'C') {
1355 if (tfmt == '+')
1356 *((char *)rsf[i].curarg) += 1;
1357 else if (tfmt == '#')
1358 *((char *)rsf[i].curarg) = (char)val;
1359 else
1360 *((char *)rsf[i].curarg) = rsf[i].val;
1361 } else if (tfmt == '*' || tfmt == '?') {
1362 if (tfmt == '*')
1363 *((const char **)rsf[i].curarg) = p;
1364 else
1365 *((char *)rsf[i].curarg) = *p;
1366 break;
1367 } else if (tfmt == '~' || tfmt == '&') {
1368 int ret;
1369 char cfmt[3];
1370
1371 cfmt[0] = '-';
1372 cfmt[1] = rsf[i].c;
1373 cfmt[2] = '\0';
1374
1375 if (rsf[i].curfun == NULL)
1376 return (BADFMT);
1377 ret = ((*(getpargfun)rsf[i].curfun) (tfmt == '&'
1378 ?p:"",
1379 rsf[i].curarg,
1380 pac, pav, cfmt));
1381 if (ret != NOTAFLAG)
1382 return (ret);
1383 if (tfmt == '&')
1384 break;
1385 } else {
1386 return (BADFLAG);
1387 }
1388 if (tfmt == '#')
1389 break;
1390 }
1391 }
1392 return (NOTAFLAG);
1393 }
1394
1395 /*
1396 * If the next format character is a comma or the string delimiter,
1397 * there are no invalid format specifiers. Return success.
1398 * Otherwise raise the getarg_bad_format condition.
1399 */
1400 LOCAL int
checkfmt(fmt)1401 checkfmt(fmt)
1402 const char *fmt;
1403 {
1404 char c;
1405
1406 c = *(++fmt); /* non constant expression */
1407
1408
1409 if (c == ',' || c == '\0') {
1410 return (NOTAFLAG);
1411 } else {
1412 raisecond("getarg_bad_format", (long)fmt);
1413 return (BADFMT);
1414 }
1415 }
1416
1417 /*
1418 * Parse the string as long as valid characters can be found.
1419 * Valid flag identifiers are chosen from the set of
1420 * alphanumeric characters, '+', '-' and '_'.
1421 * If the next character is an equal sign the string
1422 * contains a valid flag identifier.
1423 */
1424 LOCAL int
checkeql(str)1425 checkeql(str)
1426 register const char *str;
1427 {
1428 register unsigned char c;
1429
1430 for (c = (unsigned char)*str;
1431 isalnum(c) || c == '_' || c == '-' || c == '+';
1432 c = *++str)
1433 /* LINTED */
1434 ;
1435 return (c == '=');
1436 }
1437
1438 /*
1439 * Check whether the argument is a valid character for an option name.
1440 */
1441 #ifdef PROTOTYPES
1442 LOCAL BOOL
validfmt(register unsigned char c)1443 validfmt(register unsigned char c)
1444 #else
1445 LOCAL BOOL
1446 validfmt(c)
1447 register unsigned char c;
1448 #endif
1449 {
1450
1451 if (isalnum(c) || c == '_' || c == '-' || c == '+')
1452 return (TRUE);
1453 return (FALSE);
1454 }
1455
1456 EXPORT char *
getargerror(err)1457 getargerror(err)
1458 int err;
1459 {
1460 if (err < RETMIN || err > RETMAX)
1461 return ("Illegal arg error");
1462 return (RNAME(err));
1463 }
1464