diff options
Diffstat (limited to 'librols/getargs.c')
-rw-r--r-- | librols/getargs.c | 838 |
1 files changed, 838 insertions, 0 deletions
diff --git a/librols/getargs.c b/librols/getargs.c new file mode 100644 index 0000000..76f1a88 --- /dev/null +++ b/librols/getargs.c @@ -0,0 +1,838 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)getargs.c 2.37 04/09/25 Copyright 1985, 1988, 1994-2003 J. Schilling */ +#define NEW +/* + * Copyright (c) 1985, 1988, 1994-2003 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 + * '!' BOOL with size modifier +++ NEU +++ (XXX nicht fertig) + * '*' string + * '?' char + * '#' number + * '&' call function + * '+' inctype +++ NEU +++ + * + * The '#' and '+' types may have size modifiers added: + * 'c'/'C' char + * 's'/'S' short + * 'i'/'I' int (default) + * 'l'/'L' long + * 'll'/'LL' long long + * + * The format string 'f* ' may be used to disallow -ffoo for f* + * + * XXX This is currently only implemented for the '*' format + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* LINTLIBRARY */ +#include <mconfig.h> +#include <standard.h> +#include <utypes.h> +#include <getargs.h> +#include <vadefs.h> +#include <strdefs.h> +#include <schily.h> +#include <ctype.h> + +#define NOARGS 0 /* No more args */ +#define NOTAFLAG 1 /* Not a flag type argument */ +#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 */ + + + int _getargs __PR((int *, char *const **, const char *, + BOOL, va_list)); +LOCAL int dofile __PR((int *, char *const **, const char **)); +LOCAL int doflag __PR((int *, char *const **, const char *, + const char *, BOOL, va_list)); +LOCAL int dosflags __PR((const char *, const char *, BOOL, va_list)); +LOCAL int checkfmt __PR((const char *)); +LOCAL int checkeql __PR((const char *)); + +LOCAL va_list va_dummy; + +LOCAL char fmtspecs[] = "#?*&+"; +/*LOCAL char fmtspecs[] = "#?*&+!";*/ + +#define isfmtspec(c) (strchr(fmtspecs, c) != NULL) + +/*--------------------------------------------------------------------------- +| +| get flags until a non flag type argument is reached +| ++---------------------------------------------------------------------------*/ +/* 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, fmt, TRUE, args); + va_end(args); + return (ret); +} + + +/*--------------------------------------------------------------------------- +| +| get all flags on the command line, do not stop on files +| ++---------------------------------------------------------------------------*/ +/* 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, fmt, TRUE, args)) != NOTAFLAG) + break; + } + va_end(args); + return (ret); +} + + +/*--------------------------------------------------------------------------- +| +| get next non flag type argument (i.e. a file) +| ++---------------------------------------------------------------------------*/ +EXPORT int +getfiles(pac, pav, fmt) + int *pac; + char *const *pav[]; + const char *fmt; +{ + return (_getargs(pac, pav, fmt, FALSE, 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: +| NOARGS no more args +| NOTAFLAG not a flag type argument +| BADFLAG a non-matching flag type argument +| BADFMT bad syntax in format string +| +| ++---------------------------------------------------------------------------*/ +/*LOCAL*/ int +_getargs(pac, pav, fmt, setargs, args) + register int *pac; + register char *const **pav; + const char *fmt; + BOOL setargs; + va_list args; +{ + const char *argp; + int ret; + + + for (; *pac > 0; (*pac)--, (*pav)++) { + argp = **pav; + + ret = dofile(pac, pav, &argp); + + if (ret != NOTAFILE) + return (ret); + + ret = doflag(pac, pav, argp, fmt, setargs, args); + + if (ret != NOTAFLAG) + return (ret); + } + return (NOARGS); +} + + +/*--------------------------------------------------------------------------- +| +| check if *pargp is a file type argument +| ++---------------------------------------------------------------------------*/ +LOCAL int +dofile(pac, pav, pargp) + register int *pac; + register char *const **pav; + const char **pargp; +{ + 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. But we are currently not + * Posix. + */ + if (argp[1] == '-' && argp[2] == '\0') { + if (--(*pac) > 0) { + (*pav)++; + return (NOTAFLAG); + } else { + return (NOARGS); + } + } + } + + /* + * now check if it may be flag type argument + * flag type arguments begin with a '-', a '+' or contain a '=' + * i.e. -flag +flag or flag= + */ + if (argp[0] != '-' && argp[0] != '+' && (!checkeql(argp))) + return (NOTAFLAG); + + return (NOTAFILE); +} + + +/*--------------------------------------------------------------------------- +| +| 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 setargs is FALSE, only check arguments for getfiles() +| in this case, va_list may be a dummy arg. +| ++---------------------------------------------------------------------------*/ +LOCAL int +doflag(pac, pav, argp, fmt, setargs, oargs) + int *pac; + char *const **pav; + register const char *argp; + register const char *fmt; + BOOL setargs; + va_list oargs; +{ + char argstr[2]; + 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 = fmt; + va_list args; + char *const *spav = *pav; + int spac = *pac; + void *curarg = (void *)0; + + /* + * 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 (setargs) + curarg = va_arg(args, void *); + + /* + * check if the first flag in format string is a singlechar flag + */ + if (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 == '=') { + 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* + * + * XXX This is currently only implemented for + * XXX the '*' format + */ + if (!haseql && *argp != '\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 (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 == '&' && setargs) + curarg = va_arg(args, void *); + fmt++; + } + argp = sargp; + break; + } + } + switch (*fmt) { + + case '\0': + /* + * Boolean type has been tested before. + */ + if (singlecharflag && !doubledash && + (val = dosflags(sargp, sfmt, setargs, oargs)) != + BADFLAG) + return (val); + + + return (BADFLAG); + + case ',': + fmt++; + if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0') + singlecharflag++; + if (setargs) + curarg = va_arg(args, void *); + continue; + + case '*': + if (*argp == '\0') { + if (*pac > 1) { + (*pac)--; + (*pav)++; + argp = **pav; + } else { + return (BADFLAG); + } + } + if (fmt[1] == ' ') /* To disallow -ffoo for f* */ + fmt++; + if (setargs) + *((const char **)curarg) = argp; + + + return (checkfmt(fmt)); + + case '?': + /* + * If more than one char arg, it + * cannot be a character argument. + */ + if (argp[1] != '\0') + goto nextchance; + if (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; + + if (fmt[1] == 'l' || fmt[1] == 'L') { + if (fmt[2] == 'l' || fmt[2] == 'L') { + if (setargs) + *((Llong *)curarg) += 1; + fmt += 2; + } else { + if (setargs) + *((long *)curarg) += 1; + fmt++; + } + } else if (fmt[1] == 's' || fmt[1] == 'S') { + if (setargs) + *((short *)curarg) += 1; + fmt++; + } else if (fmt[1] == 'c' || fmt[1] == 'C') { + if (setargs) + *((char *)curarg) += 1; + fmt++; + } else { + if (fmt[1] == 'i' || fmt[1] == 'I') + fmt++; + if (setargs) + *((int *)curarg) += 1; + } + + argstr[0] = *fmt; + argstr[1] = '\0'; + + return (checkfmt(fmt)); + + case '#': + if (*argp == '\0') { + if (*pac > 1) { + (*pac)--; + (*pav)++; + argp = **pav; + } else { + return (BADFLAG); + } + } + 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 == '&' && setargs) + curarg = va_arg(args, void *); + fmt++; + } + argp = sargp; + *pac = spac; + *pav = spav; + continue; + } + val = (long)llval; + if (fmt[1] == 'l' || fmt[1] == 'L') { + if (fmt[2] == 'l' || fmt[2] == 'L') { + if (setargs) + *((Llong *)curarg) = llval; + fmt += 2; + } else { + if (setargs) + *((long *)curarg) = val; + fmt++; + } + } else if (fmt[1] == 's' || fmt[1] == 'S') { + if (setargs) + *((short *)curarg) = (short)val; + fmt++; + } else if (fmt[1] == 'c' || fmt[1] == 'C') { + if (setargs) + *((char *)curarg) = (char)val; + fmt++; + } else { + if (fmt[1] == 'i' || fmt[1] == 'I') + fmt++; + if (setargs) + *((int *)curarg) = (int)val; + } + argstr[0] = *fmt; + argstr[1] = '\0'; + + return (checkfmt(fmt)); + + case '&': + if (*argp == '\0') { + if (*pac > 1) { + (*pac)--; + (*pav)++; + argp = **pav; + } else { + return (BADFLAG); + } + } + + if ((val = checkfmt(fmt)) != NOTAFLAG) + return (val); + + if (setargs) { + int ret; + void *funarg = va_arg(args, void *); + + ret = ((*(getargfun)curarg) (argp, funarg)); + 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 */ + short count; /* The number of times a sc flag appears */ + char c; /* The single char flag character */ + char type; /* The type of the single char flag */ +} sflags; + +LOCAL int +dosflags(argp, fmt, setargs, oargs) + register const char *argp; + register const char *fmt; + BOOL setargs; + va_list oargs; +{ +#define MAXSF 32 + sflags sf[MAXSF]; + va_list args; + register sflags *rsf = sf; + register int nsf = 0; + register const char *p = argp; + register int i; + register void *curarg = (void *)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 (setargs) + curarg = va_arg(args, void *); + + while (*p) { + for (i = 0; i < nsf; i++) { + if (rsf[i].c == *p) + break; + } + if (i >= MAXSF) + return (BADFLAG); + if (i == nsf) { + rsf[i].curarg = (void *)0; + rsf[i].count = 0; + rsf[i].c = *p; + rsf[i].type = (char)-1; + nsf++; + } + rsf[i].count++; + p++; + } + + while (*fmt) { + if (!isfmtspec(*fmt) && + (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0') && + strchr(argp, *fmt)) { + for (i = 0; i < nsf; i++) { + if (rsf[i].c == *fmt) { + if (fmt[1] == '+') { + 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 { + /* + * ',' or '\0' for BOOL + */ + rsf[i].type = fmt[1]; + } + rsf[i].curarg = curarg; + break; + } + } + } + while (*fmt != ',' && *fmt != '\0') { + /* function has extra arg on stack */ + if (*fmt == '&' && setargs) + curarg = va_arg(args, void *); + fmt++; + } + if (*fmt != '\0') + fmt++; + + if (setargs) + curarg = va_arg(args, void *); + } + for (i = 0; i < nsf; i++) { + type = rsf[i].type; + if (type == (char)-1) { + return (BADFLAG); + } + if (rsf[i].curarg) { + if (type == ',' || type == '\0') { + *((int *)rsf[i].curarg) = TRUE; + } else if (type == 'i' || type == 'I') { + *((int *)rsf[i].curarg) += rsf[i].count; + } else if (type == 'l' || type == 'L') { + *((long *)rsf[i].curarg) += rsf[i].count; + } else if (type == 'Q') { + *((Llong *)rsf[i].curarg) += rsf[i].count; + } else if (type == 's' || type == 'S') { + *((short *)rsf[i].curarg) += rsf[i].count; + } else if (type == 'c' || type == 'C') { + *((char *)rsf[i].curarg) += rsf[i].count; + } 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 = *str++) + /* LINTED */ + ; + return (c == '='); +} |