mirror of
https://github.com/reactos/reactos.git
synced 2024-09-05 10:17:16 +00:00
1342 lines
31 KiB
C
1342 lines
31 KiB
C
/* @(#)getargs.c 2.71 17/07/16 Copyright 1985, 1988, 1994-2017 J. Schilling */
|
|
#include <schily/mconfig.h>
|
|
#ifndef lint
|
|
static UConst char sccsid[] =
|
|
"@(#)getargs.c 2.71 17/07/16 Copyright 1985, 1988, 1994-2017 J. Schilling";
|
|
#endif
|
|
#define NEW
|
|
/*
|
|
* Copyright (c) 1985, 1988, 1994-2017 J. Schilling
|
|
*
|
|
* 1.3.88 Start implementation of release 2
|
|
*/
|
|
/*
|
|
* Parse arguments on a command line.
|
|
* Format string type specifier (appearing directly after flag name):
|
|
* '' BOOL size int set to TRUE (1)
|
|
* '%' Extended format, next char determines the type:
|
|
* '%0' BOOL with size modifier set to FALSE (0)
|
|
* '%1' BOOL with size modifier set to TRUE(1)
|
|
* '*' string
|
|
* '?' char
|
|
* '#' number
|
|
* '&' call function for any type flag
|
|
* '~' call function for BOOLEAN flag
|
|
* '+' inctype +++ NEU +++
|
|
*
|
|
* The format string 'f* ' may be used to disallow -ffoo for f*
|
|
* The same behavior is implemented for 'f# ', 'f? ' and 'f& '.
|
|
* The ' ' needs to immediately follow the format type specifier.
|
|
*
|
|
* The format string 'f*_' may be used to disallow -f foo for f*
|
|
* The same behavior is implemented for 'f#_', 'f?_' and 'f&_'.
|
|
* The '_' needs to immediately follow the format type specifier.
|
|
* This also allows to implement optional arguments to options.
|
|
* Note: 'f#_' is only implemented for orthogonality, -f will
|
|
* be converted to an integer value of 0.
|
|
*
|
|
* The '#', '+' and '%[01]' types may have size modifiers added:
|
|
* 'c'/'C' char
|
|
* 's'/'S' short
|
|
* 'i'/'I' int (default == no size modifier)
|
|
* 'l'/'L' long
|
|
* 'll'/'LL' long long
|
|
*/
|
|
/*
|
|
* The contents of this file are subject to the terms of the
|
|
* Common Development and Distribution License, Version 1.0 only
|
|
* (the "License"). You may not use this file except in compliance
|
|
* with the License.
|
|
*
|
|
* See the file CDDL.Schily.txt in this distribution for details.
|
|
* A copy of the CDDL is also available via the Internet at
|
|
* http://www.opensource.org/licenses/cddl1.txt
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
* file and include the License file CDDL.Schily.txt from this distribution.
|
|
*/
|
|
/* LINTLIBRARY */
|
|
#include <schily/mconfig.h>
|
|
#include <schily/standard.h>
|
|
#include <schily/utypes.h>
|
|
#include <schily/getargs.h>
|
|
#include <schily/varargs.h>
|
|
#include <schily/string.h>
|
|
#include <schily/schily.h>
|
|
#include <schily/ctype.h>
|
|
|
|
/*
|
|
* Various return values
|
|
*/
|
|
#define RETMAX 2 /* Max. value for getargerror() */
|
|
#define FLAGDELIM 2 /* "--" stopped flag processing */
|
|
#define NOTAFLAG 1 /* Not a flag type argument */
|
|
#define NOARGS 0 /* No more args */
|
|
#define BADFLAG (-1) /* Not a valid flag argument */
|
|
#define BADFMT (-2) /* Error in format string */
|
|
#define NOTAFILE (-3) /* Seems to be a flag type arg */
|
|
#define RETMIN (-3) /* Min. value for getargerror() */
|
|
|
|
LOCAL char *retnames[] = {
|
|
"NOTAFILE",
|
|
"BADFMT",
|
|
"BADFLAG",
|
|
"NOARGS",
|
|
"NOTAFLAG",
|
|
"FLAGDELIM",
|
|
};
|
|
#define RNAME(a) (retnames[(a)-RETMIN])
|
|
|
|
#define SCANONLY 0 /* Do not try to set argument values */
|
|
#define SETARGS 1 /* Set argument values from cmdline */
|
|
#define ARGVECTOR 2 /* Use vector instead of list interface */
|
|
#define NOEQUAL 4 /* -opt=val not allowed for -opt val */
|
|
|
|
LOCAL struct ga_props *_getprops __PR((struct ga_props *));
|
|
|
|
int _getargs __PR((int *, char *const **, void *,
|
|
int,
|
|
struct ga_props *,
|
|
va_list));
|
|
LOCAL int dofile __PR((int *, char *const **, const char **,
|
|
struct ga_props *));
|
|
LOCAL int doflag __PR((int *, char *const **, const char *,
|
|
void *, int, va_list));
|
|
LOCAL int dosflags __PR((const char *, void *,
|
|
int *, char *const **,
|
|
int, va_list));
|
|
LOCAL int checkfmt __PR((const char *));
|
|
LOCAL int checkeql __PR((const char *));
|
|
|
|
LOCAL va_list va_dummy;
|
|
|
|
LOCAL char fmtspecs[] = "#?*&~+%";
|
|
|
|
#define isfmtspec(c) (strchr(fmtspecs, c) != NULL)
|
|
|
|
LOCAL struct ga_props props_default = { 0, 0, sizeof (struct ga_props) };
|
|
LOCAL struct ga_props props_posix = { GAF_POSIX_DEFAULT, 0, sizeof (struct ga_props) };
|
|
|
|
EXPORT int
|
|
_getarginit(props, size, flags)
|
|
struct ga_props *props;
|
|
size_t size;
|
|
UInt32_t flags;
|
|
{
|
|
if (size > sizeof (struct ga_props))
|
|
return (-1);
|
|
|
|
/*
|
|
* GAF_POSIX may be used as an alias for the flags that currently
|
|
* define POSIX behavior.
|
|
*/
|
|
if (flags == GAF_POSIX)
|
|
flags = GAF_POSIX_DEFAULT;
|
|
|
|
props->ga_flags = flags;
|
|
props->ga_oflags = 0;
|
|
props->ga_size = size;
|
|
return (0);
|
|
}
|
|
|
|
LOCAL struct ga_props *
|
|
_getprops(props)
|
|
struct ga_props *props;
|
|
{
|
|
if (props == GA_NO_PROPS)
|
|
props = &props_default;
|
|
else if (props == GA_POSIX_PROPS)
|
|
props = &props_posix;
|
|
props->ga_oflags = 0;
|
|
/*
|
|
* GAF_POSIX may be used as an alias for the flags that currently
|
|
* define POSIX behavior.
|
|
*/
|
|
if (props->ga_flags == GAF_POSIX)
|
|
props->ga_flags = GAF_POSIX_DEFAULT;
|
|
|
|
return (props);
|
|
}
|
|
|
|
/*
|
|
* get flags until a non flag type argument is reached (old version)
|
|
*/
|
|
/* VARARGS3 */
|
|
#ifdef PROTOTYPES
|
|
EXPORT int
|
|
getargs(int *pac, char *const **pav, const char *fmt, ...)
|
|
#else
|
|
EXPORT int
|
|
getargs(pac, pav, fmt, va_alist)
|
|
int *pac;
|
|
char **pav[];
|
|
char *fmt;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
va_list args;
|
|
int ret;
|
|
|
|
#ifdef PROTOTYPES
|
|
va_start(args, fmt);
|
|
#else
|
|
va_start(args);
|
|
#endif
|
|
ret = _getargs(pac, pav, (void *)fmt, SETARGS, GA_NO_PROPS, args);
|
|
va_end(args);
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* get flags until a non flag type argument is reached (list version)
|
|
*/
|
|
/* VARARGS4 */
|
|
#ifdef PROTOTYPES
|
|
EXPORT int
|
|
getlargs(int *pac, char *const **pav, struct ga_props *props, const char *fmt, ...)
|
|
#else
|
|
EXPORT int
|
|
getlargs(pac, pav, props, fmt, va_alist)
|
|
int *pac;
|
|
char **pav[];
|
|
struct ga_props *props;
|
|
char *fmt;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
va_list args;
|
|
int ret;
|
|
|
|
#ifdef PROTOTYPES
|
|
va_start(args, fmt);
|
|
#else
|
|
va_start(args);
|
|
#endif
|
|
ret = _getargs(pac, pav, (void *)fmt, SETARGS, props, args);
|
|
va_end(args);
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* get flags until a non flag type argument is reached (vector version)
|
|
*/
|
|
EXPORT int
|
|
getvargs(pac, pav, props, vfmt)
|
|
int *pac;
|
|
char * const *pav[];
|
|
struct ga_props *props;
|
|
struct ga_flags *vfmt;
|
|
{
|
|
return (_getargs(pac, pav, vfmt, SETARGS | ARGVECTOR, props, va_dummy));
|
|
}
|
|
|
|
|
|
/*
|
|
* get all flags on the command line, do not stop on files (old version)
|
|
*/
|
|
/* VARARGS3 */
|
|
#ifdef PROTOTYPES
|
|
EXPORT int
|
|
getallargs(int *pac, char *const **pav, const char *fmt, ...)
|
|
#else
|
|
EXPORT int
|
|
getallargs(pac, pav, fmt, va_alist)
|
|
int *pac;
|
|
char **pav[];
|
|
char *fmt;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
va_list args;
|
|
int ret;
|
|
|
|
#ifdef PROTOTYPES
|
|
va_start(args, fmt);
|
|
#else
|
|
va_start(args);
|
|
#endif
|
|
for (; ; (*pac)--, (*pav)++) {
|
|
if ((ret = _getargs(pac, pav, (void *)fmt, SETARGS, GA_NO_PROPS, args)) < NOTAFLAG)
|
|
break;
|
|
}
|
|
va_end(args);
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* get all flags on the command line, do not stop on files (list version)
|
|
*/
|
|
/* VARARGS4 */
|
|
#ifdef PROTOTYPES
|
|
EXPORT int
|
|
getlallargs(int *pac, char *const **pav, struct ga_props *props, const char *fmt, ...)
|
|
#else
|
|
EXPORT int
|
|
getlallargs(pac, pav, props, fmt, va_alist)
|
|
int *pac;
|
|
char **pav[];
|
|
struct ga_props *props;
|
|
char *fmt;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
va_list args;
|
|
int ret;
|
|
|
|
#ifdef PROTOTYPES
|
|
va_start(args, fmt);
|
|
#else
|
|
va_start(args);
|
|
#endif
|
|
props = _getprops(props);
|
|
for (; ; (*pac)--, (*pav)++) {
|
|
if ((ret = _getargs(pac, pav, (void *)fmt, SETARGS, props, args)) < NOTAFLAG)
|
|
break;
|
|
/*
|
|
* The default is to parse all options on the command line and
|
|
* to let "--" only make the next argument a non-option.
|
|
*/
|
|
if (ret == FLAGDELIM && (props->ga_flags & GAF_DELIM_DASHDASH))
|
|
break;
|
|
}
|
|
va_end(args);
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* get all flags on the command line, do not stop on files (vector version)
|
|
*/
|
|
EXPORT int
|
|
getvallargs(pac, pav, props, vfmt)
|
|
int *pac;
|
|
char * const *pav[];
|
|
struct ga_props *props;
|
|
struct ga_flags *vfmt;
|
|
{
|
|
int ret;
|
|
|
|
props = _getprops(props);
|
|
for (; ; (*pac)--, (*pav)++) {
|
|
if ((ret = _getargs(pac, pav, vfmt, SETARGS | ARGVECTOR, props, va_dummy)) < NOTAFLAG)
|
|
break;
|
|
/*
|
|
* The default is to parse all options on the command line and
|
|
* to let "--" only make the next argument a non-option.
|
|
*/
|
|
if (ret == FLAGDELIM && (props->ga_flags & GAF_DELIM_DASHDASH))
|
|
break;
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* get next non flag type argument (i.e. a file) (old version)
|
|
* getfiles() is a dry run getargs()
|
|
*/
|
|
EXPORT int
|
|
getfiles(pac, pav, fmt)
|
|
int *pac;
|
|
char *const *pav[];
|
|
const char *fmt;
|
|
{
|
|
return (_getargs(pac, pav, (void *)fmt, SCANONLY, GA_NO_PROPS, va_dummy));
|
|
}
|
|
|
|
|
|
/*
|
|
* get next non flag type argument (i.e. a file) (list version)
|
|
* getlfiles() is a dry run getlargs()
|
|
*/
|
|
EXPORT int
|
|
getlfiles(pac, pav, props, fmt)
|
|
int *pac;
|
|
char *const *pav[];
|
|
struct ga_props *props;
|
|
const char *fmt;
|
|
{
|
|
return (_getargs(pac, pav, (void *)fmt, SCANONLY, props, va_dummy));
|
|
}
|
|
|
|
|
|
/*
|
|
* get next non flag type argument (i.e. a file) (vector version)
|
|
* getvfiles() is a dry run getvargs()
|
|
*/
|
|
EXPORT int
|
|
getvfiles(pac, pav, props, vfmt)
|
|
int *pac;
|
|
char *const *pav[];
|
|
struct ga_props *props;
|
|
struct ga_flags *vfmt;
|
|
{
|
|
return (_getargs(pac, pav, vfmt, SCANONLY | ARGVECTOR, props, va_dummy));
|
|
}
|
|
|
|
|
|
/*
|
|
* check args until the next non flag type argmument is reached
|
|
* *pac is decremented, *pav is incremented so that the
|
|
* non flag type argument is at *pav[0]
|
|
*
|
|
* return code:
|
|
* +2 FLAGDELIM "--" stopped flag processing
|
|
* +1 NOTAFLAG not a flag type argument (is a file)
|
|
* 0 NOARGS no more args
|
|
* -1 BADFLAG a non-matching flag type argument
|
|
* -2 BADFMT bad syntax in format string
|
|
*/
|
|
/* LOCAL int */
|
|
EXPORT int
|
|
_getargs(pac, pav, vfmt, flags, props, args)
|
|
register int *pac;
|
|
register char *const **pav;
|
|
void *vfmt;
|
|
int flags;
|
|
struct ga_props *props;
|
|
va_list args;
|
|
{
|
|
const char *argp;
|
|
int ret;
|
|
|
|
|
|
props = _getprops(props);
|
|
|
|
if (props->ga_flags & GAF_NO_EQUAL)
|
|
flags |= NOEQUAL;
|
|
|
|
for (; *pac > 0; (*pac)--, (*pav)++) {
|
|
argp = **pav;
|
|
|
|
ret = dofile(pac, pav, &argp, props);
|
|
|
|
if (ret != NOTAFILE)
|
|
return (ret);
|
|
|
|
ret = doflag(pac, pav, argp, vfmt, flags, args);
|
|
|
|
if (ret != NOTAFLAG)
|
|
return (ret);
|
|
}
|
|
return (NOARGS);
|
|
}
|
|
|
|
|
|
/*
|
|
* check if *pargp is a file type argument
|
|
*/
|
|
LOCAL int
|
|
dofile(pac, pav, pargp, props)
|
|
register int *pac;
|
|
register char *const **pav;
|
|
const char **pargp;
|
|
struct ga_props *props;
|
|
{
|
|
register const char *argp = *pargp;
|
|
|
|
|
|
if (argp[0] == '-') {
|
|
/*
|
|
* "-" is a special non flag type argument
|
|
* that usually means take stdin instead of a named file
|
|
*/
|
|
if (argp[1] == '\0')
|
|
return (NOTAFLAG);
|
|
/*
|
|
* "--" is a prefix to take the next argument
|
|
* as non flag type argument
|
|
* NOTE: POSIX requires "--" to indicate the end of the
|
|
* flags on the command line. Programs that like to be
|
|
* 100% POSIX compliant call only get[lv]args() once
|
|
* and then process the list of files from cav[0].
|
|
*/
|
|
if (argp[1] == '-' && argp[2] == '\0') {
|
|
if (--(*pac) > 0) {
|
|
(*pav)++;
|
|
return (FLAGDELIM);
|
|
} else {
|
|
return (NOARGS);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now check if it may be flag type argument at all.
|
|
* Flag type arguments begin with a '-', a '+' or contain a '='
|
|
* i.e. -flag +flag or flag=
|
|
* The behavior here may be controlled by props->ga_flags to
|
|
* allow getargs() to e.g. behave fully POSIX compliant.
|
|
*/
|
|
if (argp[0] != '-') {
|
|
if (argp[0] == '+' && (props->ga_flags & GAF_NO_PLUS) == 0)
|
|
return (NOTAFILE); /* This is a flag type arg */
|
|
|
|
/*
|
|
* If 'flag=value' is not allowed at all, speed things up
|
|
* and do not call checkeql() to check for '='.
|
|
*/
|
|
if (props->ga_flags & GAF_NO_EQUAL)
|
|
return (NOTAFLAG);
|
|
if (checkeql(argp) && (props->ga_flags & GAF_NEED_DASH) == 0)
|
|
return (NOTAFILE); /* This is a flag type arg */
|
|
return (NOTAFLAG);
|
|
}
|
|
return (NOTAFILE); /* This is a flag type arg */
|
|
}
|
|
|
|
|
|
/*
|
|
* compare argp with the format string
|
|
* if a match is found store the result a la scanf in one of the
|
|
* arguments pointed to in the va_list
|
|
*
|
|
* If (flags & SETARGS) == 0, only check arguments for getfiles()
|
|
* In case that (flags & SETARGS) == 0 or that (flags & ARGVECTOR) != 0,
|
|
* va_list may be a dummy argument.
|
|
*/
|
|
LOCAL int
|
|
doflag(pac, pav, argp, vfmt, flags, oargs)
|
|
int *pac;
|
|
char *const **pav;
|
|
register const char *argp;
|
|
void *vfmt;
|
|
int flags;
|
|
va_list oargs;
|
|
{
|
|
register const char *fmt = (const char *)vfmt;
|
|
struct ga_flags *flagp = vfmt;
|
|
const char *fmtp;
|
|
long val;
|
|
Llong llval;
|
|
int singlecharflag = 0;
|
|
BOOL isspec;
|
|
BOOL hasdash = FALSE;
|
|
BOOL doubledash = FALSE;
|
|
BOOL haseql = checkeql(argp);
|
|
const char *sargp;
|
|
const char *sfmt;
|
|
va_list args;
|
|
char *const *spav = *pav;
|
|
int spac = *pac;
|
|
void *curarg = (void *)0;
|
|
|
|
sfmt = fmt;
|
|
/*
|
|
* flags beginning with '-' don't have to include the '-' in
|
|
* the format string.
|
|
* flags beginning with '+' have to include it in the format string.
|
|
*/
|
|
if (argp[0] == '-') {
|
|
argp++;
|
|
hasdash = TRUE;
|
|
/*
|
|
* Implement legacy support for --longopt
|
|
* If we find a double dash, we do not look for combinations
|
|
* of boolean single char flags.
|
|
*/
|
|
if (argp[0] == '-') {
|
|
argp++;
|
|
doubledash = TRUE;
|
|
/*
|
|
* Allow -- only for long options.
|
|
*/
|
|
if (argp[1] == '\0') {
|
|
return (BADFLAG);
|
|
}
|
|
}
|
|
}
|
|
sargp = argp;
|
|
|
|
/*
|
|
* Initialize 'args' to the start of the argument list.
|
|
* I don't know any portable way to copy an arbitrary
|
|
* C object so I use a system-specific routine
|
|
* (probably a macro) from stdarg.h. (Remember that
|
|
* if va_list is an array, 'args' will be a pointer
|
|
* and '&args' won't be what I would need for memcpy.)
|
|
* It is a system requirement for SVr4 compatibility
|
|
* to be able to do this assgignement. If your system
|
|
* defines va_list to be an array but does not define
|
|
* va_copy() you are lost.
|
|
* This is needed to make sure, that 'oargs' will not
|
|
* be clobbered.
|
|
*/
|
|
va_copy(args, oargs);
|
|
|
|
if (flags & ARGVECTOR) {
|
|
sfmt = fmt = flagp->ga_format;
|
|
if (fmt == NULL)
|
|
sfmt = fmt = "";
|
|
if (flags & SETARGS)
|
|
curarg = flagp->ga_arg;
|
|
} else if (flags & SETARGS) {
|
|
curarg = va_arg(args, void *);
|
|
}
|
|
/*
|
|
* check if the first flag in format string is a singlechar flag
|
|
*/
|
|
again:
|
|
if (fmt[0] != '\0' &&
|
|
(fmt[1] == ',' || fmt[1] == '+' ||
|
|
fmt[1] == '~' || fmt[1] == '%' || fmt[1] == '\0'))
|
|
singlecharflag++;
|
|
/*
|
|
* check the whole format string for a match
|
|
*/
|
|
for (;;) {
|
|
for (; *fmt; fmt++, argp++) {
|
|
if (*fmt == '\\') {
|
|
/*
|
|
* Allow "#?*&+" to appear inside a flag.
|
|
* NOTE: they must be escaped by '\\' only
|
|
* inside the the format string.
|
|
*/
|
|
fmt++;
|
|
isspec = FALSE;
|
|
} else {
|
|
isspec = isfmtspec(*fmt);
|
|
}
|
|
/*
|
|
* If isspec is TRUE, the arg beeing checked starts
|
|
* like a valid flag. Argp now points to the rest.
|
|
*/
|
|
if (isspec) {
|
|
/*
|
|
* If *argp is '+' and we are on the
|
|
* beginning of the arg that is currently
|
|
* checked, this cannot be an inc type flag.
|
|
*/
|
|
if (*argp == '+' && argp == sargp)
|
|
continue;
|
|
/*
|
|
* skip over to arg of flag
|
|
*/
|
|
if (*argp == '=') {
|
|
if (flags & NOEQUAL)
|
|
return (BADFLAG);
|
|
argp++;
|
|
} else if (*argp != '\0' && haseql) {
|
|
/*
|
|
* Flag and arg are not separated by a
|
|
* space.
|
|
* Check here for:
|
|
* xxxxx=yyyyy match on '&'
|
|
* Checked before:
|
|
* abc=yyyyy match on 'abc&'
|
|
* or 'abc*'
|
|
* or 'abc#'
|
|
* We come here if 'argp' starts with
|
|
* the same sequence as a valid flag
|
|
* and contains an equal sign.
|
|
* We have tested before if the text
|
|
* before 'argp' matches exactly.
|
|
* At this point we have no exact match
|
|
* and we only allow to match
|
|
* the special pattern '&'.
|
|
* We need this e.g. for 'make'.
|
|
* We allow any flag type argument to
|
|
* match the format string "&" to set
|
|
* up a function that handles all odd
|
|
* stuff that getargs will not grok.
|
|
* In addition, to allow getargs to be
|
|
* used for CPP type flags we allow to
|
|
* match -Dabc=xyz on 'D&'. Note that
|
|
* Dabc=xyz will not match 'D&'.
|
|
*/
|
|
if ((!hasdash && argp != sargp) || *fmt != '&')
|
|
goto nextarg;
|
|
}
|
|
|
|
/*
|
|
* The format string 'f* ' may be used
|
|
* to disallow -ffoo for f*
|
|
*
|
|
* The same behavior is implemented for
|
|
* 'f# '. 'f? ' and 'f& '.
|
|
*/
|
|
if (!haseql && *argp != '\0' &&
|
|
(fmt[0] == '*' || fmt[0] == '#' ||
|
|
fmt[0] == '?' || fmt[0] == '&') &&
|
|
fmt[1] == ' ') {
|
|
goto nextarg;
|
|
}
|
|
|
|
/*
|
|
* *arpp == '\0' || !haseql
|
|
* We come here if 'argp' starts with
|
|
* the same sequence as a valid flag.
|
|
* This will match on the following args:
|
|
* -farg match on 'f*'
|
|
* -f12 match on 'f#'
|
|
* +12 match on '+#'
|
|
* -12 match on '#'
|
|
* and all args that are separated from
|
|
* their flags.
|
|
* In the switch statement below, we check
|
|
* if the text after 'argp' (if *argp != 0) or
|
|
* the next arg is a valid arg for this flag.
|
|
*/
|
|
break;
|
|
} else if (*fmt == *argp) {
|
|
if (argp[1] == '\0' &&
|
|
(fmt[1] == '\0' || fmt[1] == ',')) {
|
|
|
|
if (flags & SETARGS)
|
|
*((int *)curarg) = TRUE;
|
|
|
|
|
|
return (checkfmt(fmt)); /* XXX */
|
|
}
|
|
} else {
|
|
/*
|
|
* skip over to next format identifier
|
|
* & reset arg pointer
|
|
*/
|
|
nextarg:
|
|
while (*fmt != ',' && *fmt != '\0') {
|
|
/* function has extra arg on stack */
|
|
if ((*fmt == '&' || *fmt == '~') &&
|
|
(flags & (SETARGS|ARGVECTOR)) == SETARGS) {
|
|
curarg = va_arg(args, void *);
|
|
}
|
|
fmt++;
|
|
}
|
|
argp = sargp;
|
|
break;
|
|
}
|
|
}
|
|
switch (*fmt) {
|
|
|
|
case '\0':
|
|
/*
|
|
* Boolean type has been tested before.
|
|
*/
|
|
if (flags & ARGVECTOR) {
|
|
if (flagp[1].ga_format != NULL) {
|
|
flagp++;
|
|
sfmt = fmt = flagp->ga_format;
|
|
if (flags & SETARGS)
|
|
curarg = flagp->ga_arg;
|
|
argp = sargp;
|
|
goto again;
|
|
}
|
|
}
|
|
if (singlecharflag && !doubledash &&
|
|
(val = dosflags(sargp, vfmt, pac, pav,
|
|
flags & ~SETARGS,
|
|
va_dummy)) == BADFLAG) {
|
|
return (val);
|
|
}
|
|
if (singlecharflag && !doubledash &&
|
|
(val = dosflags(sargp, vfmt, pac, pav,
|
|
flags,
|
|
oargs)) != BADFLAG) {
|
|
return (val);
|
|
}
|
|
return (BADFLAG);
|
|
|
|
case ',':
|
|
fmt++;
|
|
if (fmt[0] == '\0') /* Should we allow "a,b,c,"? */
|
|
return (BADFMT);
|
|
if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0')
|
|
singlecharflag++;
|
|
if ((flags & (SETARGS|ARGVECTOR)) == SETARGS)
|
|
curarg = va_arg(args, void *);
|
|
continue;
|
|
|
|
case '*':
|
|
if (*argp == '\0' && fmt[1] != '_') {
|
|
if (*pac > 1) {
|
|
(*pac)--;
|
|
(*pav)++;
|
|
argp = **pav;
|
|
} else {
|
|
return (BADFLAG);
|
|
}
|
|
}
|
|
if (fmt[1] == '_') /* To disallow -f foo for f* */
|
|
fmt++;
|
|
else if (fmt[1] == ' ') /* To disallow -ffoo for f* */
|
|
fmt++;
|
|
|
|
if (flags & SETARGS)
|
|
*((const char **)curarg) = argp;
|
|
|
|
|
|
return (checkfmt(fmt));
|
|
|
|
case '?':
|
|
if (*argp == '\0' && fmt[1] != '_') {
|
|
if (*pac > 1) {
|
|
(*pac)--;
|
|
(*pav)++;
|
|
argp = **pav;
|
|
} else {
|
|
return (BADFLAG);
|
|
}
|
|
}
|
|
if (fmt[1] == '_') /* To disallow -f c for f? */
|
|
fmt++;
|
|
else if (fmt[1] == ' ') /* To disallow -fc for f? */
|
|
fmt++;
|
|
|
|
/*
|
|
* Allow -f '' to specify a nul character.
|
|
* If more than one char arg, it
|
|
* cannot be a character argument.
|
|
*/
|
|
if (argp[0] != '\0' && argp[1] != '\0')
|
|
goto nextchance;
|
|
|
|
if (flags & SETARGS)
|
|
*((char *)curarg) = *argp;
|
|
|
|
|
|
return (checkfmt(fmt));
|
|
|
|
case '+':
|
|
/*
|
|
* inc type is similar to boolean,
|
|
* there is no arg in argp to convert.
|
|
*/
|
|
if (*argp != '\0')
|
|
goto nextchance;
|
|
/*
|
|
* If *fmt is '+' and we are on the beginning
|
|
* of the format desciptor that is currently
|
|
* checked, this cannot be an inc type flag.
|
|
*/
|
|
if (fmt == sfmt || fmt[-1] == ',')
|
|
goto nextchance;
|
|
|
|
fmtp = fmt;
|
|
if (fmt[1] == 'l' || fmt[1] == 'L') {
|
|
if (fmt[2] == 'l' || fmt[2] == 'L') {
|
|
if (flags & SETARGS)
|
|
*((Llong *)curarg) += 1;
|
|
fmt += 2;
|
|
} else {
|
|
if (flags & SETARGS)
|
|
*((long *)curarg) += 1;
|
|
fmt++;
|
|
}
|
|
} else if (fmt[1] == 's' || fmt[1] == 'S') {
|
|
if (flags & SETARGS)
|
|
*((short *)curarg) += 1;
|
|
fmt++;
|
|
} else if (fmt[1] == 'c' || fmt[1] == 'C') {
|
|
if (flags & SETARGS)
|
|
*((char *)curarg) += 1;
|
|
fmt++;
|
|
} else {
|
|
if (fmt[1] == 'i' || fmt[1] == 'I')
|
|
fmt++;
|
|
if (flags & SETARGS)
|
|
*((int *)curarg) += 1;
|
|
}
|
|
|
|
|
|
return (checkfmt(fmt));
|
|
|
|
case '%':
|
|
/*
|
|
* inc type is similar to boolean,
|
|
* there is no arg in argp to convert.
|
|
*/
|
|
if (*argp != '\0')
|
|
goto nextchance;
|
|
|
|
fmt++;
|
|
if (*fmt == '1')
|
|
val = TRUE;
|
|
else if (*fmt == '0')
|
|
val = FALSE;
|
|
else
|
|
goto nextchance;
|
|
|
|
fmtp = fmt;
|
|
llval = (Llong)val;
|
|
if (fmt[1] == 'l' || fmt[1] == 'L') {
|
|
if (fmt[2] == 'l' || fmt[2] == 'L') {
|
|
if (flags & SETARGS)
|
|
*((Llong *)curarg) = llval;
|
|
fmt += 2;
|
|
} else {
|
|
if (flags & SETARGS)
|
|
*((long *)curarg) = val;
|
|
fmt++;
|
|
}
|
|
} else if (fmt[1] == 's' || fmt[1] == 'S') {
|
|
if (flags & SETARGS)
|
|
*((short *)curarg) = val;
|
|
fmt++;
|
|
} else if (fmt[1] == 'c' || fmt[1] == 'C') {
|
|
if (flags & SETARGS)
|
|
*((char *)curarg) = val;
|
|
fmt++;
|
|
} else {
|
|
if (fmt[1] == 'i' || fmt[1] == 'I')
|
|
fmt++;
|
|
if (flags & SETARGS)
|
|
*((int *)curarg) = val;
|
|
}
|
|
|
|
|
|
return (checkfmt(fmt));
|
|
|
|
case '#':
|
|
if (*argp == '\0' && fmt[1] != '_') {
|
|
if (*pac > 1) {
|
|
(*pac)--;
|
|
(*pav)++;
|
|
argp = **pav;
|
|
} else {
|
|
return (BADFLAG);
|
|
}
|
|
}
|
|
if (fmt[1] == '_') /* To disallow -f 123 for f# */
|
|
fmt++;
|
|
else if (fmt[1] == ' ') /* To disallow -f123 for f# */
|
|
fmt++;
|
|
|
|
if (*astoll(argp, &llval) != '\0') {
|
|
/*
|
|
* arg is not a valid number!
|
|
* go to next format in the format string
|
|
* and check if arg matches any other type
|
|
* in the format specs.
|
|
*/
|
|
nextchance:
|
|
while (*fmt != ',' && *fmt != '\0') {
|
|
if ((*fmt == '&' || *fmt == '~') &&
|
|
(flags & (SETARGS|ARGVECTOR)) == SETARGS) {
|
|
curarg = va_arg(args, void *);
|
|
}
|
|
fmt++;
|
|
}
|
|
argp = sargp;
|
|
*pac = spac;
|
|
*pav = spav;
|
|
continue;
|
|
}
|
|
fmtp = fmt;
|
|
val = (long)llval;
|
|
if (fmt[1] == 'l' || fmt[1] == 'L') {
|
|
if (fmt[2] == 'l' || fmt[2] == 'L') {
|
|
if (flags & SETARGS)
|
|
*((Llong *)curarg) = llval;
|
|
fmt += 2;
|
|
} else {
|
|
if (flags & SETARGS)
|
|
*((long *)curarg) = val;
|
|
fmt++;
|
|
}
|
|
} else if (fmt[1] == 's' || fmt[1] == 'S') {
|
|
if (flags & SETARGS)
|
|
*((short *)curarg) = (short)val;
|
|
fmt++;
|
|
} else if (fmt[1] == 'c' || fmt[1] == 'C') {
|
|
if (flags & SETARGS)
|
|
*((char *)curarg) = (char)val;
|
|
fmt++;
|
|
} else {
|
|
if (fmt[1] == 'i' || fmt[1] == 'I')
|
|
fmt++;
|
|
if (flags & SETARGS)
|
|
*((int *)curarg) = (int)val;
|
|
}
|
|
|
|
return (checkfmt(fmt));
|
|
|
|
case '~':
|
|
if (*argp != '\0')
|
|
goto nextchance;
|
|
if (haseql) {
|
|
return (BADFLAG);
|
|
}
|
|
goto callfunc;
|
|
|
|
case '&':
|
|
if (*argp == '\0' && fmt[1] != '_') {
|
|
if (*pac > 1) {
|
|
(*pac)--;
|
|
(*pav)++;
|
|
argp = **pav;
|
|
} else {
|
|
return (BADFLAG);
|
|
}
|
|
}
|
|
callfunc:
|
|
|
|
if (*fmt == '&' && fmt[1] == '_') /* To disallow -f foo for f& */
|
|
fmt++;
|
|
else if (fmt[1] == ' ') /* To disallow -ffoo for f& */
|
|
fmt++;
|
|
if ((val = checkfmt(fmt)) != NOTAFLAG)
|
|
return (val);
|
|
|
|
fmtp = sargp;
|
|
if (hasdash)
|
|
fmtp--;
|
|
if (doubledash)
|
|
fmtp--;
|
|
if ((flags & (SETARGS|ARGVECTOR)) == (SETARGS|ARGVECTOR)) {
|
|
int ret;
|
|
|
|
if (flagp->ga_funcp == NULL)
|
|
return (BADFMT);
|
|
|
|
ret = ((*flagp->ga_funcp) (argp, flagp->ga_arg,
|
|
pac, pav, fmtp));
|
|
if (ret != NOTAFILE)
|
|
return (ret);
|
|
fmt++;
|
|
} else
|
|
if (flags & SETARGS) {
|
|
int ret;
|
|
void *funarg = va_arg(args, void *);
|
|
|
|
if (curarg == NULL)
|
|
return (BADFMT);
|
|
ret = ((*(getpargfun)curarg) (argp, funarg,
|
|
pac, pav, fmtp));
|
|
if (ret != NOTAFILE)
|
|
return (ret);
|
|
fmt++;
|
|
} else {
|
|
return (val);
|
|
}
|
|
/*
|
|
* Called function returns NOTAFILE: try next format.
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* parse args for combined single char flags
|
|
*/
|
|
typedef struct {
|
|
void *curarg; /* The pointer to the arg to modify */
|
|
void *curfun; /* The pointer to the function to call */
|
|
char c; /* The single char flag character */
|
|
char type; /* The type of the single char flag */
|
|
char fmt; /* The format type of the single char flag */
|
|
char val; /* The value to assign for BOOL flags */
|
|
} sflags;
|
|
|
|
LOCAL int
|
|
dosflags(argp, vfmt, pac, pav, flags, oargs)
|
|
register const char *argp;
|
|
void *vfmt;
|
|
int *pac;
|
|
char *const **pav;
|
|
int flags;
|
|
va_list oargs;
|
|
{
|
|
register const char *fmt = (const char *)vfmt;
|
|
struct ga_flags *flagp = vfmt;
|
|
#define MAXSF 64
|
|
sflags sf[MAXSF];
|
|
char fl[256];
|
|
va_list args;
|
|
register sflags *rsf = sf;
|
|
register int nsf = 0;
|
|
register const char *p = argp;
|
|
register int i;
|
|
register void *curarg = (void *)0;
|
|
getpargfun curfun = 0;
|
|
char type;
|
|
|
|
/*
|
|
* Initialize 'args' to the start of the argument list.
|
|
* I don't know any portable way to copy an arbitrary
|
|
* C object so I use a system-specific routine
|
|
* (probably a macro) from stdarg.h. (Remember that
|
|
* if va_list is an array, 'args' will be a pointer
|
|
* and '&args' won't be what I would need for memcpy.)
|
|
* It is a system requirement for SVr4 compatibility
|
|
* to be able to do this assgignement. If your system
|
|
* defines va_list to be an array but does not define
|
|
* va_copy() you are lost.
|
|
* This is needed to make sure, that 'oargs' will not
|
|
* be clobbered.
|
|
*/
|
|
va_copy(args, oargs);
|
|
|
|
if (flags & ARGVECTOR) {
|
|
fmt = flagp->ga_format;
|
|
if (fmt == NULL)
|
|
fmt = "";
|
|
if (flags & SETARGS) {
|
|
curarg = flagp->ga_arg;
|
|
curfun = flagp->ga_funcp;
|
|
}
|
|
} else if (flags & SETARGS) {
|
|
/*
|
|
* We set curfun to curarg. We later get the real
|
|
* curarg in case that we see a function callback
|
|
* but we need curfun first in this case.
|
|
*/
|
|
curarg = va_arg(args, void *);
|
|
curfun = (getpargfun)curarg;
|
|
}
|
|
|
|
for (i = 0; i < sizeof (fl); i++) {
|
|
fl[i] = 0;
|
|
}
|
|
while (*p) {
|
|
for (i = 0; i < nsf; i++) {
|
|
if (rsf[i].c == *p)
|
|
break;
|
|
}
|
|
if (i >= MAXSF) {
|
|
va_end(args);
|
|
return (BADFLAG);
|
|
}
|
|
if (i == nsf) {
|
|
rsf[i].curarg = (void *)0;
|
|
rsf[i].curfun = (void *)0;
|
|
rsf[i].c = *p;
|
|
rsf[i].type = (char)-1;
|
|
rsf[i].fmt = '\0';
|
|
rsf[i].val = (char)TRUE;
|
|
nsf++;
|
|
}
|
|
fl[*p & 0xFF] = i;
|
|
p++;
|
|
}
|
|
|
|
again:
|
|
while (*fmt) {
|
|
if (!isfmtspec(*fmt) &&
|
|
(fmt[1] == ',' || fmt[1] == '+' ||
|
|
fmt[1] == '~' || fmt[1] == '%' || fmt[1] == '\0') &&
|
|
strchr(argp, *fmt)) {
|
|
for (i = 0; i < nsf; i++) {
|
|
if (rsf[i].c == *fmt) {
|
|
if (fmt[1] == '+') {
|
|
rsf[i].fmt = '+';
|
|
fmt++;
|
|
if (fmt[1] == ',' ||
|
|
fmt[1] == '\0') {
|
|
rsf[i].type = 'i';
|
|
} else if ((fmt[1] == 'l' ||
|
|
fmt[1] == 'L') &&
|
|
(fmt[2] == 'l' ||
|
|
fmt[2] == 'L')) {
|
|
/*
|
|
* Type 'Q'uad (ll)
|
|
*/
|
|
rsf[i].type = 'Q';
|
|
fmt++;
|
|
} else {
|
|
/*
|
|
* Type 'l','i','s','c'
|
|
*/
|
|
rsf[i].type = fmt[1];
|
|
}
|
|
} else if (fmt[1] == '%') {
|
|
fmt++;
|
|
rsf[i].fmt = '%';
|
|
if (fmt[1] == '0')
|
|
rsf[i].val = (char)FALSE;
|
|
else if (fmt[1] == '1')
|
|
rsf[i].val = (char)TRUE;
|
|
fmt++;
|
|
if (fmt[1] == ',' ||
|
|
fmt[1] == '\0') {
|
|
rsf[i].type = 'i';
|
|
} else if ((fmt[1] == 'l' ||
|
|
fmt[1] == 'L') &&
|
|
(fmt[2] == 'l' ||
|
|
fmt[2] == 'L')) {
|
|
/*
|
|
* Type 'Q'uad (ll)
|
|
*/
|
|
rsf[i].type = 'Q';
|
|
fmt++;
|
|
} else {
|
|
/*
|
|
* Type 'l','i','s','c'
|
|
*/
|
|
rsf[i].type = fmt[1];
|
|
}
|
|
} else if (fmt[1] == '~') {
|
|
/*
|
|
* Let fmt point to ',' to
|
|
* prevent to fetch the
|
|
* func arg twice.
|
|
*/
|
|
fmt += 2;
|
|
rsf[i].fmt = '~';
|
|
rsf[i].type = '~';
|
|
rsf[i].curfun = (void *)curfun;
|
|
if ((flags & (SETARGS|ARGVECTOR)) == SETARGS)
|
|
curarg = va_arg(args, void *);
|
|
} else {
|
|
/*
|
|
* ',' or '\0' for BOOL
|
|
*/
|
|
rsf[i].type = fmt[1];
|
|
}
|
|
rsf[i].curarg = curarg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (*fmt != ',' && *fmt != '\0') {
|
|
/*
|
|
* function has extra arg on stack. The code above
|
|
* prevents us from fetching this arg twice.
|
|
*/
|
|
if ((*fmt == '&' || *fmt == '~') &&
|
|
(flags & (SETARGS|ARGVECTOR)) == SETARGS) {
|
|
curarg = va_arg(args, void *);
|
|
}
|
|
fmt++;
|
|
}
|
|
if (*fmt != '\0')
|
|
fmt++;
|
|
else
|
|
break;
|
|
|
|
if ((flags & (SETARGS|ARGVECTOR)) == SETARGS) {
|
|
/*
|
|
* We set curfun to curarg. We later get the real
|
|
* curarg in case that we see a function callback
|
|
* but we need curfun first in this case.
|
|
*/
|
|
curarg = va_arg(args, void *);
|
|
curfun = (getpargfun)curarg;
|
|
}
|
|
}
|
|
if ((flags & ARGVECTOR) && flagp[1].ga_format != NULL) {
|
|
flagp++;
|
|
fmt = flagp->ga_format;
|
|
if (flags & SETARGS) {
|
|
curarg = flagp->ga_arg;
|
|
curfun = flagp->ga_funcp;
|
|
}
|
|
goto again;
|
|
}
|
|
|
|
for (p = argp; *p; p++) {
|
|
char tfmt;
|
|
|
|
i = fl[*p & 0xFF];
|
|
tfmt = rsf[i].fmt;
|
|
type = rsf[i].type;
|
|
if (type == (char)-1) {
|
|
return (BADFLAG);
|
|
}
|
|
|
|
if ((flags & SETARGS) &&
|
|
(rsf[i].curfun || rsf[i].curarg)) {
|
|
if (type == ',' || type == '\0') {
|
|
*((int *)rsf[i].curarg) = TRUE;
|
|
} else if (type == 'i' || type == 'I') {
|
|
if (tfmt == '+')
|
|
*((int *)rsf[i].curarg) += 1;
|
|
else
|
|
*((int *)rsf[i].curarg) = rsf[i].val;
|
|
} else if (type == 'l' || type == 'L') {
|
|
if (tfmt == '+')
|
|
*((long *)rsf[i].curarg) += 1;
|
|
else
|
|
*((long *)rsf[i].curarg) = rsf[i].val;
|
|
} else if (type == 'Q') {
|
|
if (tfmt == '+')
|
|
*((Llong *)rsf[i].curarg) += 1;
|
|
else
|
|
*((Llong *)rsf[i].curarg) = rsf[i].val;
|
|
} else if (type == 's' || type == 'S') {
|
|
if (tfmt == '+')
|
|
*((short *)rsf[i].curarg) += 1;
|
|
else
|
|
*((short *)rsf[i].curarg) = rsf[i].val;
|
|
} else if (type == 'c' || type == 'C') {
|
|
if (tfmt == '+')
|
|
*((char *)rsf[i].curarg) += 1;
|
|
else
|
|
*((char *)rsf[i].curarg) = rsf[i].val;
|
|
} else if (type == '~') {
|
|
int ret;
|
|
char cfmt[3];
|
|
|
|
cfmt[0] = '-';
|
|
cfmt[1] = rsf[i].c;
|
|
cfmt[2] = '\0';
|
|
|
|
if (rsf[i].curfun == NULL)
|
|
return (BADFMT);
|
|
ret = ((*(getpargfun)rsf[i].curfun) ("",
|
|
rsf[i].curarg,
|
|
pac, pav, cfmt));
|
|
return (ret);
|
|
} else {
|
|
return (BADFLAG);
|
|
}
|
|
}
|
|
}
|
|
return (NOTAFLAG);
|
|
}
|
|
|
|
/*
|
|
* If the next format character is a comma or the string delimiter,
|
|
* there are no invalid format specifiers. Return success.
|
|
* Otherwise raise the getarg_bad_format condition.
|
|
*/
|
|
LOCAL int
|
|
checkfmt(fmt)
|
|
const char *fmt;
|
|
{
|
|
char c;
|
|
|
|
c = *(++fmt); /* non constant expression */
|
|
|
|
|
|
if (c == ',' || c == '\0') {
|
|
return (NOTAFLAG);
|
|
} else {
|
|
raisecond("getarg_bad_format", (long)fmt);
|
|
return (BADFMT);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse the string as long as valid characters can be found.
|
|
* Valid flag identifiers are chosen from the set of
|
|
* alphanumeric characters, '-' and '_'.
|
|
* If the next character is an equal sign the string
|
|
* contains a valid flag identifier.
|
|
*/
|
|
LOCAL int
|
|
checkeql(str)
|
|
register const char *str;
|
|
{
|
|
register unsigned char c;
|
|
|
|
for (c = (unsigned char)*str;
|
|
isalnum(c) || c == '_' || c == '-' || c == '+';
|
|
c = *++str)
|
|
/* LINTED */
|
|
;
|
|
return (c == '=');
|
|
}
|
|
|
|
EXPORT char *
|
|
getargerror(err)
|
|
int err;
|
|
{
|
|
if (err < RETMIN || err > RETMAX)
|
|
return ("Illegal arg error");
|
|
return (RNAME(err));
|
|
}
|