summaryrefslogtreecommitdiff
path: root/librols/getargs.c
diff options
context:
space:
mode:
Diffstat (limited to 'librols/getargs.c')
-rw-r--r--librols/getargs.c838
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 == '=');
+}