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