X-Git-Url: https://git.distorted.org.uk/~mdw/cfd/blobdiff_plain/9cecacb1b693c7cfd95f67748544132bd6171868..657c75463f04c02c1fe70f54a49df7eb32111387:/mdwopt.c diff --git a/mdwopt.c b/mdwopt.c index 33f41a7..fc76635 100644 --- a/mdwopt.c +++ b/mdwopt.c @@ -1,7 +1,5 @@ /* -*-c-*- * - * $Id: mdwopt.c,v 1.4 1999/05/15 10:25:38 mdw Exp $ - * * Options parsing, similar to GNU @getopt_long@ * * (c) 1996 Straylight/Edgeware @@ -27,52 +25,6 @@ * MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: mdwopt.c,v $ - * Revision 1.4 1999/05/15 10:25:38 mdw - * Fix copyright information. - * - * Revision 1.3 1999/05/14 18:51:42 mdw - * Reformat the LGPL notice slightly. - * - * Revision 1.2 1999/05/13 22:57:23 mdw - * Change `-ise' to `-ize' throughout. - * - * Revision 1.1.1.1 1999/05/05 19:23:47 mdw - * New import. The old CVS repository was lost in a disk disaster. - * - * --- Previous lives --- - * - * %Log: mdwopt.c,v % - * Revision 1.7 1997/09/11 09:19:11 mdw - * (mo__nextWord): Arrrgh. Don't free the environment variable buffer! - * People are still using it! - * - * Revision 1.6 1997/09/11 09:05:54 mdw - * (mo__nextWord): Fix bug which returns too many words from environment - * variables. - * - * Revision 1.5 1997/08/09 20:27:59 mdw - * Fix spelling of `Licensing'. - * - * Revision 1.4 1997/07/29 21:11:35 mdw - * Reformatted. Fixed buffer overflow when dealing with environment - * variables. Included NT in list of daft operating systems with `\' as a - * path separator. Fixed address of the FSF. - * - * Revision 1.3 1997/02/26 00:41:10 mdw - * Added GPL notice to the top. Slight formatting changes. - * - * Revision 1.2 1996/10/28 13:12:13 mdw - * Fixed calls to ctype.h routines. Arguments are cast to unsigned char - * to avoid invoking undefined behaviour caused by signedness of chars. - * - * Revision 1.1 1996/09/24 18:01:28 mdw - * Initial revision - * - */ - /*----- External dependencies ---------------------------------------------*/ #include @@ -97,15 +49,102 @@ mdwopt_data mdwopt_global = {0, 0, 0, 1, 0, 0, 0, 0, 0}; enum { - ord__permute = 0, /* Permute the options (default) */ - ord__return = 1, /* Return non-option things */ - ord__posix = 2, /* Do POSIX-type hacking */ - ord__negate = 4 /* Magic negate-next-thing flag */ + ORD_PERMUTE = 0, /* Permute the options (default) */ + ORD_RETURN = 1, /* Return non-option things */ + ORD_POSIX = 2, /* Do POSIX-type hacking */ + ORD_NEGATE = 4 /* Magic negate-next-thing flag */ }; +/*----- Word splitting ----------------------------------------------------*/ + +#ifdef BUILDING_MLIB +# include "str.h" +# define qword str_qword +#else + +/* --- @qword@ --- * + * + * Arguments: @char **pp@ = address of pointer into string + * @unsigned f@ = various flags + * + * Returns: Pointer to the next space-separated possibly-quoted word from + * the string, or null. + * + * Use: Fetches the next word from a string. If the flag + * @STRF_QUOTE@ is set, the `\' character acts as an escape, and + * single and double quotes protect whitespace. + */ + +#define STRF_QUOTE 1u + +static char *qword(char **pp, unsigned f) +{ + char *p = *pp, *q, *qq; + int st = 0, pst = 0; + + /* --- Preliminaries --- */ + + if (!p) + return (0); + while (isspace((unsigned char)*p)) + p++; + if (!*p) { + *pp = 0; + return (0); + } + + /* --- Main work --- */ + + for (q = qq = p; *q; q++) { + switch (st) { + case '\\': + *qq++ = *q; + st = pst; + break; + case '\'': + case '\"': + if (*q == st) + st = pst = 0; + else if (*q == '\\') + st = '\\'; + else + *qq++ = *q; + break; + default: + if (isspace((unsigned char)*q)) { + do q++; while (*q && isspace((unsigned char)*q)); + goto done; + } else if (!(f & STRF_QUOTE)) + goto stdchar; + switch (*q) { + case '\\': + st = '\\'; + break; + case '\'': + case '\"': + st = pst = *q; + break; + default: + stdchar: + *qq++ = *q; + break; + } + } + } + + /* --- Finished --- */ + +done: + *pp = *q ? q : 0; + *qq++ = 0; + return (p); +} + +#endif + /*----- Main code ---------------------------------------------------------*/ -/* --- @mo__nextWord@ --- * +/* --- @nextword@ --- * * * Arguments: @int argc@ = number of command line options * @char *argv[]@ = pointer to command line options @@ -117,22 +156,12 @@ enum { * variable. */ -static char *mo__nextWord(int argc, char *const *argv, mdwopt_data *data) +static char *nextword(int argc, char *const *argv, mdwopt_data *data) { if (data->ind == -1) { - char *p = data->env; - char *q; - while (isspace((unsigned char)*p)) - p++; - q = p; - while (*p && !isspace((unsigned char)*p)) - p++; - data->env = p; - if (*p) - *p++ = 0; - if (p != q) - return (q); - data->env = 0; + char *p; + if ((p = qword(&data->env, STRF_QUOTE)) != 0) + return (p); data->ind = 1; } @@ -141,7 +170,7 @@ static char *mo__nextWord(int argc, char *const *argv, mdwopt_data *data) return (argv[data->next++]); } -/* --- @mo__permute@ --- * +/* --- @permute@ --- * * * Arguments: @char *argv[]@ = pointer to command line arguments * @mdwopt_data *data@ = pointer to persistent data @@ -151,7 +180,7 @@ static char *mo__nextWord(int argc, char *const *argv, mdwopt_data *data) * Use: Moves a command line option into the right place. */ -static void mo__permute(char *const *argv, mdwopt_data *data) +static void permute(char *const *argv, mdwopt_data *data) { char **v = (char **)argv; if (data->ind != -1) { @@ -166,7 +195,7 @@ static void mo__permute(char *const *argv, mdwopt_data *data) } } -/* --- @mo__findOpt@ --- * +/* --- @findOpt@ --- * * * Arguments: @int o@ = which option to search for * @const char *shortopt@ = short options string to search @@ -178,20 +207,20 @@ static void mo__permute(char *const *argv, mdwopt_data *data) * Use: Looks up a short option in the given string. */ -static const char *mo__findOpt(int o, const char *shortopt, +static const char *findOpt(int o, const char *shortopt, mdwopt_data *data) { - const char *p = shortopt; /* Point to short opts table */ + const char *p = shortopt; for (;;) { - if (!*p) /* No more options left */ + if (!*p) return (0); - if (o != *p || (p[1] != '+' && data->order & ord__negate)) { - p++; /* Skip this option entry */ - while (*p == '+') /* Jump a `%|+|%' sign */ + if (o != *p || (p[1] != '+' && data->order & ORD_NEGATE)) { + p++; + while (*p == '+') + p++; + while (*p == ':') p++; - while (*p == ':') /* And jump any `%|:|%' characters */ - p++; /* Just in case there are any */ } else return (p + 1); @@ -350,7 +379,7 @@ static const char *mo__findOpt(int o, const char *shortopt, * the caller. A pointer to an argument is stored in * @data->arg@, or @NULL@ is stored if there was no argument. * If a negated option was found, the option character is - * returned ORred with @gFlag_negated@ (bit 8 set). + * returned ORred with @OPTF_NEGATED@ (bit 8 set). * * Long options are described in a table. Each entry in the * table is of type @struct option@, and the table is terminated @@ -368,9 +397,9 @@ static const char *mo__findOpt(int o, const char *shortopt, * table entry. If @flag@ is nonzero, it points to an integer * to be modified by mdwopt. Usually the value in the @val@ * field is simply stored in the @flag@ variable. If the flag - * @gFlag_switch@ is set, however, the value is combined with + * @OPTF_SWITCH@ is set, however, the value is combined with * the existing value of the flags using a bitwise OR. If - * @gFlag_negate@ is set, then the flag bit will be cleared if a + * @OPTF_NEGATE@ is set, then the flag bit will be cleared if a * matching negated long option is found. The value 0 is * returned. * @@ -383,11 +412,12 @@ static const char *mo__findOpt(int o, const char *shortopt, * Numeric options, if enabled, cause the value `%|#|%' to be * returned, and the numeric value to be stored in @data->opt@. * - * If the flag @gFlag_envVar@ is set on entry, options will be + * If the flag @OPTF_ENVVAR@ is set on entry, options will be * extracted from an environment variable whose name is built by - * capitalising all the letters of the program's name. (This + * capitalizing all the letters of the program's name. (This * allows a user to have different default settings for a - * program, by calling it through different symbolic links.) */ + * program, by calling it through different symbolic links.) + */ int mdwopt(int argc, char *const *argv, const char *shortopt, @@ -396,45 +426,45 @@ int mdwopt(int argc, char *const *argv, { /* --- Local variables --- */ - char *p, *q, *r; /* Some useful things to have */ - char *prefix; /* Prefix from this option */ - int i; /* Always useful */ - char noarg = '?'; /* Standard missing-arg char */ + char *p, *q, *r; + char *prefix; + int i; + char noarg = '?'; /* --- Sort out our data --- */ - if (!data) /* If default data requested */ - data = &mdwopt_global; /* Then use the global stuff */ + if (!data) + data = &mdwopt_global; /* --- See if this is the first time --- */ - if (data->ind == 0 || (data->ind == 1 && ~flags & gFlag_noProgName)) { + if (data->ind == 0 || (data->ind == 1 && ~flags & OPTF_NOPROGNAME)) { /* --- Sort out default returning order --- */ - if (getenv("_POSIX_OPTION_ORDER") || /* Examine environment for opts */ - getenv("POSIXLY_CORRECT")) /* To see if we disable features */ - data->order = ord__posix; /* If set, use POSIX ordering */ + if (getenv("_POSIX_OPTION_ORDER") || + getenv("POSIXLY_CORRECT")) + data->order = ORD_POSIX; else - data->order = ord__permute; /* Otherwise mangle the options */ + data->order = ORD_PERMUTE; /* --- Now see what the caller actually wants --- */ - switch (shortopt[0]) { /* Look at the first character */ - case '-': /* `%|-|%' turns on in-orderness */ - data->order = ord__return; + switch (shortopt[0]) { + case '-': + data->order = ORD_RETURN; break; - case '+': /* `%|+|%' turns on POSIXness */ - data->order = ord__posix; + case '+': + data->order = ORD_POSIX; break; - case '!': /* `%|!|%' ignores POSIXness */ - data->order = ord__permute; + case '!': + data->order = ORD_PERMUTE; break; } /* --- Now decide on the program's name --- */ - if (~flags & gFlag_noProgName) { + if (~flags & OPTF_NOPROGNAME) { p = q = (char *)argv[0]; while (*p) { if (*p++ == PATHSEP) @@ -452,7 +482,7 @@ int mdwopt(int argc, char *const *argv, * support this option. */ - if (flags & gFlag_envVar && strlen(data->prog) < 48) { + if (flags & OPTF_ENVVAR && strlen(data->prog) < 48) { char buf[64]; @@ -469,12 +499,12 @@ int mdwopt(int argc, char *const *argv, if (!p) { #endif - p = buf; /* Point to a buffer */ - q = data->prog; /* Point to program name */ - while (*q) /* While characters left here */ - *p++ = toupper(*q++); /* Copy and uppercase */ - *p++ = 0; /* Terminate my copy of this */ - p = getenv(buf); /* Get the value of the variable */ + p = buf; + q = data->prog; + while (*q) + *p++ = toupper(*q++); + *p++ = 0; + p = getenv(buf); #ifdef __riscos } @@ -482,17 +512,17 @@ int mdwopt(int argc, char *const *argv, /* --- Copy the options string into a buffer --- */ - if (p) { /* If it is defined */ - q = malloc(strlen(p) + 1); /* Allocate space for a copy */ - if (!q) { /* If that failed */ - fprintf(stderr, /* Report a nice error */ + if (p) { + q = malloc(strlen(p) + 1); + if (!q) { + fprintf(stderr, "%s: Not enough memory to read settings in " "environment variable\n", data->prog); - } else { /* Otherwise */ - strcpy(q, p); /* Copy the text over */ - data->ind = -1; /* Mark that we're parsing envvar */ - data->env = data->estart = q; /* And store the pointer away */ + } else { + strcpy(q, p); + data->ind = -1; + data->env = data->estart = q; } } @@ -521,10 +551,10 @@ int mdwopt(int argc, char *const *argv, shortopt++; } - if (longind) /* Allow longind to be null */ - *longind = -1; /* Clear this to avoid confusion */ - data->opt = -1; /* And this too */ - data->arg = 0; /* No option set up here */ + if (longind) + *longind = -1; + data->opt = -1; + data->arg = 0; /* --- Now go off and search for an option --- */ @@ -537,47 +567,47 @@ int mdwopt(int argc, char *const *argv, * We identify options as being things starting with `%|-|%', and which * aren't equal to `%|-|%' or `%|--|%'. We'll look for options until: * - * * We find something which isn't an option AND @order == ord__posix@ + * * We find something which isn't an option AND @order == ORD_POSIX@ * * We find a `%|--|%' * * We reach the end of the list * * There are some added little wrinkles, which we'll meet as we go. */ - for (;;) { /* Keep looping for a while */ - p = mo__nextWord(argc, argv, data); /* Get the next word out */ - if (!p) /* If there's no next word */ - return (EOF); /* There's no more now */ + for (;;) { + p = nextword(argc, argv, data); + if (!p) + return (EOF); /* --- See if we've found an option --- */ - if ((p[0] == '-' || (p[0] == '+' && flags & gFlag_negation)) && + if ((p[0] == '-' || (p[0] == '+' && flags & OPTF_NEGATION)) && p[1] != 0) { - if (strcmp(p, "--") == 0) { /* If this is the magic marker */ - mo__permute(argv, data); /* Stow the magic marker item */ - return (EOF); /* There's nothing else to do */ + if (strcmp(p, "--") == 0) { + permute(argv, data); + return (EOF); } - break; /* We've found something! */ + break; } /* --- Figure out how to proceed --- */ switch (data->order & 3) { - case ord__posix: /* POSIX option order */ - return (EOF); /* This is easy */ + case ORD_POSIX: + return (EOF); break; - case ord__permute: /* Permute the option order */ + case ORD_PERMUTE: break; - case ord__return: /* Return each argument */ - mo__permute(argv, data); /* Insert word in same place */ - data->arg = p; /* Point to the argument */ - return (0); /* Return the value */ + case ORD_RETURN: + permute(argv, data); + data->arg = p; + return (0); } } /* --- We found an option --- */ - mo__permute(argv, data); /* Do any permuting necessary */ + permute(argv, data); /* --- Check for a numeric option --- * * @@ -585,7 +615,7 @@ int mdwopt(int argc, char *const *argv, * sign). This ought to be enough. */ - if (flags & gFlag_numbers && (p[0] == '-' || flags & gFlag_negNumber)) { + if (flags & OPTF_NUMBERS && (p[0] == '-' || flags & OPTF_NEGNUMBER)) { if (((p[1] == '+' || p[1] == '-') && isdigit((unsigned char)p[2])) || isdigit((unsigned char)p[1])) { data->opt = strtol(p + 1, &data->arg, 10); @@ -593,74 +623,74 @@ int mdwopt(int argc, char *const *argv, data->arg++; if (!data->arg[0]) data->arg = 0; - return (p[0] == '-' ? '#' : '#' | gFlag_negated); + return (p[0] == '-' ? '#' : '#' | OPTF_NEGATED); } } /* --- Check for a long option --- */ if (p[0] == '+') - data->order |= ord__negate; + data->order |= ORD_NEGATE; if (((p[0] == '-' && p[1] == '-') || - (flags & gFlag_noShorts && !mo__findOpt(p[1], shortopt, data))) && - (~flags & gFlag_noLongs)) /* Is this a long option? */ + (flags & OPTF_NOSHORTS && !findOpt(p[1], shortopt, data))) && + (~flags & OPTF_NOLONGS)) { - int match = -1; /* Count matches as we go */ - - if (p[0] == '+') { /* If it's negated */ - data->order |= ord__negate; /* Set the negate flag */ - p++; /* Point to the main text */ - prefix = "+"; /* Set the prefix string up */ - } else if (p[1] == '-') { /* If this is a `%|--|%' option */ - if ((flags & gFlag_negation) && strncmp(p + 2, "no-", 3) == 0) { - p += 5; /* Point to main text */ - prefix = "--no-"; /* And set the prefix */ - data->order |= ord__negate; /* Set the negatedness flag */ + int match = -1; + + if (p[0] == '+') { + data->order |= ORD_NEGATE; + p++; + prefix = "+"; + } else if (p[1] == '-') { + if ((flags & OPTF_NEGATION) && strncmp(p + 2, "no-", 3) == 0) { + p += 5; + prefix = "--no-"; + data->order |= ORD_NEGATE; } else { - p += 2; /* Point to the main text */ - prefix = "--"; /* Remember the prefix string */ + p += 2; + prefix = "--"; } } else { - if ((flags & gFlag_negation) && strncmp(p + 1, "no-", 3) == 0) { - p += 4; /* Find the text */ - prefix = "-no-"; /* Set the prefix */ - data->order |= ord__negate; /* Set negatedness flag */ + if ((flags & OPTF_NEGATION) && strncmp(p + 1, "no-", 3) == 0) { + p += 4; + prefix = "-no-"; + data->order |= ORD_NEGATE; } else { - p++; /* Otherwise find the text */ - prefix = "-"; /* And remember the prefix */ + p++; + prefix = "-"; } } - for (i = 0; longopts[i].name; i++) { /* Loop through the options */ - if ((data->order & ord__negate) && - (~longopts[i].has_arg & gFlag_negate)) - continue; /* If neg and opt doesn't allow */ - - r = (char *) longopts[i].name; /* Point to the name string */ - q = p; /* Point to the string start */ - for (;;) { /* Do a loop here */ - if (*q == 0 || *q == '=') { /* End of the option string? */ - if (*r == 0) { /* If end of other string */ - match = i; /* This is the match */ - goto botched; /* And exit the loop now */ + for (i = 0; longopts[i].name; i++) { + if ((data->order & ORD_NEGATE) && + (~longopts[i].has_arg & OPTF_NEGATE)) + continue; + + r = (char *) longopts[i].name; + q = p; + for (;;) { + if (*q == 0 || *q == '=') { + if (*r == 0) { + match = i; + goto botched; } - if (match == -1) { /* If no match currently */ - match = i; /* Then this is it, here */ - break; /* Stop looking now */ + if (match == -1) { + match = i; + break; } else { - match = -1; /* Else it's ambiguous */ - goto botched; /* So give up right now */ + match = -1; + goto botched; } } - else if (*q != *r) /* Otherwise if mismatch */ - break; /* Abort this loop */ - q++, r++; /* Increment the counters */ + else if (*q != *r) + break; + q++, r++; } } botched: - if (match == -1) { /* If we couldn't find a match */ + if (match == -1) { if (data->err) { fprintf(stderr, "%s: unrecognized option `%s%s'\n", data->prog, @@ -669,18 +699,18 @@ int mdwopt(int argc, char *const *argv, return ('?'); } - if (longind) /* Allow longind to be null */ - *longind = match; /* Store the match away */ + if (longind) + *longind = match; /* --- Handle argument behaviour --- */ - while (*p != 0 && *p != '=') /* Find the argument string */ + while (*p != 0 && *p != '=') p++; - p = (*p ? p + 1 : 0); /* Sort out argument presence */ - q = (char *) longopts[match].name; /* Remember the name here */ + p = (*p ? p + 1 : 0); + q = (char *) longopts[match].name; - switch (longopts[match].has_arg & 3) { - case no_argument: + switch (longopts[match].has_arg & OPTF_ARG) { + case OPTF_NOARG: if (p) { if (data->err) { fprintf(stderr, @@ -692,11 +722,11 @@ int mdwopt(int argc, char *const *argv, } break; - case required_argument: - if (!p) { /* If no argument given */ - p = mo__nextWord(argc, argv, data); + case OPTF_ARGREQ: + if (!p) { + p = nextword(argc, argv, data); - if (!p) { /* If no more arguments */ + if (!p) { if (data->err) { fprintf(stderr, "%s: option `%s%s' requires an argument\n", data->prog, @@ -705,11 +735,11 @@ int mdwopt(int argc, char *const *argv, return (noarg); } - mo__permute(argv, data); + permute(argv, data); } break; - case optional_argument: + case OPTF_ARGOPT: /* Who cares? */ break; } @@ -717,22 +747,22 @@ int mdwopt(int argc, char *const *argv, /* --- Do correct things now we have a match --- */ - if (longopts[match].flag) { /* If he has a @flag@ argument */ - if (longopts[match].has_arg & gFlag_switch) { - if (data->order & ord__negate) + if (longopts[match].flag) { + if (longopts[match].has_arg & OPTF_SWITCH) { + if (data->order & ORD_NEGATE) *longopts[match].flag &= ~longopts[match].val; else *longopts[match].flag |= longopts[match].val; } else { - if (data->order & ord__negate) + if (data->order & ORD_NEGATE) *longopts[match].flag = 0; else *longopts[match].flag = longopts[match].val; } - return (0); /* And return something */ + return (0); } else { - if (data->order & ord__negate) - return (longopts[match].val | gFlag_negated); + if (data->order & ORD_NEGATE) + return (longopts[match].val | OPTF_NEGATED); else return (longopts[match].val); } @@ -741,55 +771,55 @@ int mdwopt(int argc, char *const *argv, /* --- Do short options things --- */ else { - if (p[0] == '+') /* If starts with a `%|+|%' */ - data->order |= ord__negate; - data->list = p + 1; /* Omit leading `%|-|%'/`%|+|%' */ + if (p[0] == '+') + data->order |= ORD_NEGATE; + data->list = p + 1; } } /* --- Now process the short options --- */ - i = *data->list++; /* Get the next option letter */ - data->opt = i; /* Store this away nicely */ + i = *data->list++; + data->opt = i; - p = (char *) mo__findOpt(i, shortopt, data); - if (!p) { /* No more options left */ + p = (char *) findOpt(i, shortopt, data); + if (!p) { if (data->err) { fprintf(stderr, "%s: unknown option `%c%c'\n", data->prog, - data->order & ord__negate ? '+' : '-', + data->order & ORD_NEGATE ? '+' : '-', i); } return ('?'); } - data->opt = i; /* Store this for the caller */ + data->opt = i; /* --- Sort out an argument, if we expect one --- */ - if (p[0] == ':') { /* If we expect an option */ - q = (data->list[0] ? data->list : 0); /* If argument expected, use it */ - data->list = 0; /* Kill the remaining options */ - if (p[1] != ':' && !q) { /* If no arg, and not optional */ + if (p[0] == ':') { + q = (data->list[0] ? data->list : 0); + data->list = 0; + if (p[1] != ':' && !q) { /* --- Same code as before --- */ - q = mo__nextWord(argc, argv, data); /* Read the next word */ - if (!q) { /* If no more arguments */ + q = nextword(argc, argv, data); + if (!q) { if (data->err) { fprintf(stderr, "%s: option `%c%c' requires an argument\n", data->prog, - data->order & ord__negate ? '+' : '-', + data->order & ORD_NEGATE ? '+' : '-', i); } return (noarg); } - mo__permute(argv, data); + permute(argv, data); } data->arg = q; } - return ((data->order & ord__negate) ? i | gFlag_negated : i); + return ((data->order & ORD_NEGATE) ? i | OPTF_NEGATED : i); } /*----- That's all, folks -------------------------------------------------*/