X-Git-Url: https://git.distorted.org.uk/~mdw/cfd/blobdiff_plain/379c3233ebd6c9ea6b29b9c0058db99ce5dc430e..657c75463f04c02c1fe70f54a49df7eb32111387:/mdwopt.c diff --git a/mdwopt.c b/mdwopt.c index 6814e99..fc76635 100644 --- a/mdwopt.c +++ b/mdwopt.c @@ -1,7 +1,5 @@ /* -*-c-*- * - * $Id: mdwopt.c,v 1.5 1999/05/19 20:23:59 mdw Exp $ - * * Options parsing, similar to GNU @getopt_long@ * * (c) 1996 Straylight/Edgeware @@ -27,55 +25,6 @@ * MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: mdwopt.c,v $ - * Revision 1.5 1999/05/19 20:23:59 mdw - * Change naming to match newer mLib conventions. - * - * 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 @@ -106,9 +55,96 @@ enum { 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 ---------------------------------------------------------*/ -/* --- @nextWord@ --- * +/* --- @nextword@ --- * * * Arguments: @int argc@ = number of command line options * @char *argv[]@ = pointer to command line options @@ -120,22 +156,12 @@ enum { * variable. */ -static char *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; } @@ -184,17 +210,17 @@ static void permute(char *const *argv, mdwopt_data *data) 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 */ + p++; + while (*p == '+') + p++; + while (*p == ':') p++; - while (*p == ':') /* And jump any `%|:|%' characters */ - p++; /* Just in case there are any */ } else return (p + 1); @@ -388,9 +414,10 @@ static const char *findOpt(int o, const char *shortopt, * * 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, @@ -399,15 +426,15 @@ 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 --- */ @@ -415,22 +442,22 @@ int mdwopt(int argc, char *const *argv, /* --- 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 */ + switch (shortopt[0]) { + case '-': data->order = ORD_RETURN; break; - case '+': /* `%|+|%' turns on POSIXness */ + case '+': data->order = ORD_POSIX; break; - case '!': /* `%|!|%' ignores POSIXness */ + case '!': data->order = ORD_PERMUTE; break; } @@ -472,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 } @@ -485,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; } } @@ -524,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 --- */ @@ -547,40 +574,40 @@ int mdwopt(int argc, char *const *argv, * There are some added little wrinkles, which we'll meet as we go. */ - for (;;) { /* Keep looping for a while */ - p = 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 & OPTF_NEGATION)) && p[1] != 0) { - if (strcmp(p, "--") == 0) { /* If this is the magic marker */ - 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 */ - 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 --- */ - permute(argv, data); /* Do any permuting necessary */ + permute(argv, data); /* --- Check for a numeric option --- * * @@ -607,63 +634,63 @@ int mdwopt(int argc, char *const *argv, if (((p[0] == '-' && p[1] == '-') || (flags & OPTF_NOSHORTS && !findOpt(p[1], shortopt, data))) && - (~flags & OPTF_NOLONGS)) /* Is this a long option? */ + (~flags & OPTF_NOLONGS)) { - int match = -1; /* Count matches as we go */ + int match = -1; - 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 (p[0] == '+') { + data->order |= ORD_NEGATE; + p++; + prefix = "+"; + } else if (p[1] == '-') { if ((flags & OPTF_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 */ + 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 & OPTF_NEGATION) && strncmp(p + 1, "no-", 3) == 0) { - p += 4; /* Find the text */ - prefix = "-no-"; /* Set the prefix */ - data->order |= ORD_NEGATE; /* Set negatedness flag */ + 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 */ + for (i = 0; longopts[i].name; i++) { if ((data->order & ORD_NEGATE) && (~longopts[i].has_arg & OPTF_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 */ + 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, @@ -672,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, @@ -695,11 +722,11 @@ int mdwopt(int argc, char *const *argv, } break; - case required_argument: - if (!p) { /* If no argument given */ - p = 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, @@ -712,7 +739,7 @@ int mdwopt(int argc, char *const *argv, } break; - case optional_argument: + case OPTF_ARGOPT: /* Who cares? */ break; } @@ -720,7 +747,7 @@ 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].flag) { if (longopts[match].has_arg & OPTF_SWITCH) { if (data->order & ORD_NEGATE) *longopts[match].flag &= ~longopts[match].val; @@ -732,7 +759,7 @@ int mdwopt(int argc, char *const *argv, else *longopts[match].flag = longopts[match].val; } - return (0); /* And return something */ + return (0); } else { if (data->order & ORD_NEGATE) return (longopts[match].val | OPTF_NEGATED); @@ -744,19 +771,19 @@ int mdwopt(int argc, char *const *argv, /* --- Do short options things --- */ else { - if (p[0] == '+') /* If starts with a `%|+|%' */ + if (p[0] == '+') data->order |= ORD_NEGATE; - data->list = p + 1; /* Omit leading `%|-|%'/`%|+|%' */ + 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 *) findOpt(i, shortopt, data); - if (!p) { /* No more options left */ + if (!p) { if (data->err) { fprintf(stderr, "%s: unknown option `%c%c'\n", data->prog, @@ -766,19 +793,19 @@ int mdwopt(int argc, char *const *argv, 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 = 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,