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