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