X-Git-Url: https://git.distorted.org.uk/~mdw/runlisp/blobdiff_plain/a1af6b508efcf1e6edb07cc4f471710739e9382c..60db9fabc6aca55ad76fc8aae5b01e61eac38715:/mdwopt.c diff --git a/mdwopt.c b/mdwopt.c deleted file mode 100644 index 631dae6..0000000 --- a/mdwopt.c +++ /dev/null @@ -1,827 +0,0 @@ -/* -*-c-*- - * - * $Id: mdwopt.c,v 1.10 2004/04/08 01:36:24 mdw Exp $ - * - * Options parsing, similar to GNU @getopt_long@ - * - * (c) 1996 Straylight/Edgeware - */ - -/*----- Licensing notice --------------------------------------------------* - * - * This file is part of many programs. - * - * `mdwopt' is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * `mdwopt' 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 Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with `mdwopt'; if not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/*----- External dependencies ---------------------------------------------*/ - -#include -#include -#include -#include - -#include "mdwopt.h" - -/*----- Configuration things ----------------------------------------------*/ - -#if defined(__riscos) -# define PATHSEP '.' -#elif defined(__OS2__) || defined(__MSDOS__) || defined(__WINNT__) -# define PATHSEP '\\' -#else /* Assume a sane filing system */ -# define PATHSEP '/' -#endif - -/*----- Global variables --------------------------------------------------*/ - -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 */ -}; - -/*----- 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@ --- * - * - * Arguments: @int argc@ = number of command line options - * @char *argv[]@ = pointer to command line options - * @mdwopt_data *data@ = pointer to persistent state - * - * Returns: Pointer to the next word to handle, or 0 - * - * Use: Extracts the next word from the command line or environment - * variable. - */ - -static char *nextword(int argc, char *const *argv, mdwopt_data *data) -{ - if (data->ind == -1) { - char *p; - if ((p = qword(&data->env, STRF_QUOTE)) != 0) - return (p); - data->ind = 1; - } - - if (data->next == argc) - return (0); - return (argv[data->next++]); -} - -/* --- @permute@ --- * - * - * Arguments: @char *argv[]@ = pointer to command line arguments - * @mdwopt_data *data@ = pointer to persistent data - * - * Returns: -- - * - * Use: Moves a command line option into the right place. - */ - -static void permute(char *const *argv, mdwopt_data *data) -{ - char **v = (char **)argv; - if (data->ind != -1) { - int i = data->next - 1; - char *p = v[i]; - while (i > data->ind) { - v[i] = v[i - 1]; - i--; - } - v[i] = p; - data->ind++; - } -} - -/* --- @findOpt@ --- * - * - * Arguments: @int o@ = which option to search for - * @const char *shortopt@ = short options string to search - * @mdwopt_data *data@ = pointer to persistant state - * - * Returns: Pointer to rest of short options string (including magic - * characters) - * - * Use: Looks up a short option in the given string. - */ - -static const char *findOpt(int o, const char *shortopt, - mdwopt_data *data) -{ - const char *p = shortopt; - for (;;) { - if (!*p) - return (0); - - if (o != *p || (p[1] != '+' && data->order & ORD_NEGATE)) { - p++; - while (*p == '+') - p++; - while (*p == ':') - p++; - } - else - return (p + 1); - } -} - -/* --- @mdwopt@ --- * - * - * Arguments: @int argc@ = number of command line arguments - * @char * const *argv@ = pointer to command line arguments - * @const char *shortopt@ = pointer to short options information - * @const struct option *longopts@ = pointer to long opts info - * @int *longind@ = where to store matched longopt - * @mdwopt_data *data@ = persistent state for the parser - * @int flags@ = various useful flags - * - * Returns: Value of option found next, or an error character, or - * @EOF@ for the last thing. - * - * Use: Reads options. The routine should be more-or-less compatible - * with standard getopts, although it provides many more - * features even than the standard GNU implementation. - * - * The precise manner of options parsing is determined by - * various flag settings, which are described below. By setting - * flag values appropriately, you can achieve behaviour very - * similar to most other getopt routines. - * - * - * How options parsing appears to users - * - * A command line consists of a number of `words' (which may - * contain spaces, according to various shell quoting - * conventions). A word may be an option, an argument to an - * option, or a non-option. An option begins with a special - * character, usually `%|-|%', although `%|+|%' is also used - * sometimes. As special exceptions, the word containing only a - * `%|-|%' is considered to be a non-option, since it usually - * represents standard input or output as a filename, and the - * word containing a double-dash `%|--|%' is used to mark all - * following words as being non-options regardless of their - * initial character. - * - * Traditionally, all words after the first non-option have been - * considered to be non-options automatically, so that options - * must be specified before filenames. However, this - * implementation can extract all the options from the command - * line regardless of their position. This can usually be - * disabled by setting one of the environment variables - * `%|POSIXLY_CORRECT|%' or `%|_POSIX_OPTION_ORDER|%'. - * - * There are two different styles of options: `short' and - * `long'. - * - * Short options are the sort which Unix has known for ages: an - * option is a single letter, preceded by a `%|-|%'. Short - * options can be joined together to save space (and possibly to - * make silly words): e.g., instead of giving options - * `%|-x -y|%', a user could write `%|-xy|%'. Some short - * options can have arguments, which appear after the option - * letter, either immediately following, or in the next `word' - * (so an option with an argument could be written as - * `%|-o foo|%' or as `%|-ofoo|%'). Note that options with - * optional arguments must be written in the second style. - * - * When a short option controls a flag setting, it is sometimes - * possible to explicitly turn the flag off, as well as turning - * it on, (usually to override default options). This is - * usually done by using a `%|+|%' instead of a `%|-|%' to - * introduce the option. - * - * Long options, as popularized by the GNU utilities, are given - * long-ish memorable names, preceded by a double-dash `%|--|%'. - * Since their names are more than a single character, long - * options can't be combined in the same way as short options. - * Arguments to long options may be given either in the same - * `word', separated from the option name by an equals sign, or - * in the following `word'. - * - * Long option names can be abbreviated if necessary, as long - * as the abbreviation is unique. This means that options can - * have sensible and memorable names but still not require much - * typing from an experienced user. - * - * Like short options, long options can control flag settings. - * The options to manipulate these settings come in pairs: an - * option of the form `%|--set-flag|%' might set the flag, while - * an option of the form `%|--no-set-flag|%' might clear it. - * - * It is usual for applications to provide both short and long - * options with identical behaviour. Some applications with - * lots of options may only provide long options (although they - * will often be only two or three characters long). In this - * case, long options can be preceded with a single `%|-|%' - * character, and negated by a `%|+|%' character. - * - * Finally, some (older) programs accept arguments of the form - * `%%@.{"-"}%%', to set some numerical parameter, - * typically a line count of some kind. - * - * - * How programs parse options - * - * An application parses its options by calling mdwopt - * repeatedly. Each time it is called, mdwopt returns a value - * describing the option just read, and stores information about - * the option in a data block. The value %$-1$% is returned - * when there are no more options to be read. The `%|?|%' - * character is returned when an error is encountered. - * - * Before starting to parse options, the value @data->ind@ must - * be set to 0 or 1. The value of @data->err@ can also be set, - * to choose whether errors are reported by mdwopt. - * - * The program's `@argc@' and `@argv@' arguments are passed to - * the options parser, so that it can read the command line. A - * flags word is also passed, allowing the program fine control - * over parsing. The flags are described above. - * - * Short options are described by a string, which once upon a - * time just contained the permitted option characters. Now the - * options string begins with a collection of flag characters, - * and various flag characters can be put after options - * characters to change their properties. - * - * If the first character of the short options string is - * `%|+|%', `%|-|%' or `%|!|%', the order in which options are - * read is modified, as follows: - * - * `%|+|%' forces the POSIX order to be used. As soon as a non- - * option is found, mdwopt returns %$-1$%. - * - * `%|-|%' makes mdwopt treat non-options as being `special' - * sorts of option. When a non-option word is found, the - * value 0 is returned, and the actual text of the word - * is stored as being the option's argument. - * - * `%|!|%' forces the default order to be used. The entire - * command line is scanned for options, which are - * returned in order. However, during this process, - * the options are moved in the @argv@ array, so that - * they appear before the non- options. - * - * A `%|:|%' character may be placed after the ordering flag (or - * at the very beginning if no ordering flag is given) which - * indicates that the character `%|:|%', rather than `%|?|%', - * should be returned if a missing argument error is detected. - * - * Each option in the string can be followed by a `%|+|%' sign, - * indicating that it can be negated, a `%|:|%' sign indicating - * that it requires an argument, or a `%|::|%' string, - * indicating an optional argument. Both `%|+|%' and `%|:|%' or - * `%|::|%' may be given, although the `%|+|%' must come first. - * - * If an option is found, the option character is returned to - * 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 @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 - * by an entry whose @name@ field is null. Each option has - * a flags word which, due to historical reasons, is called - * @has_arg@. This describes various properties of the option, - * such as what sort of argument it takes, and whether it can - * be negated. - * - * When mdwopt finds a long option, it looks the name up in the - * table. The index of the matching entry is stored in the - * @longind@ variable, passed to mdwopt (unless @longind@ is 0): - * a value of %$-1$% indicates that no long option was - * found. The behaviour is then dependent on the values in the - * 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 - * @OPTF_SWITCH@ is set, however, the value is combined with - * the existing value of the flags using a bitwise OR. If - * @OPTF_NEGATE@ is set, then the flag bit will be cleared if a - * matching negated long option is found. The value 0 is - * returned. - * - * If @flag@ is zero, the value in @val@ is returned by mdwopt, - * possibly with bit 8 set if the option was negated. - * - * Arguments for long options are stored in @data->arg@, as - * before. - * - * Numeric options, if enabled, cause the value `%|#|%' to be - * returned, and the numeric value to be stored in @data->opt@. - * - * If the flag @OPTF_ENVVAR@ is set on entry, options will be - * extracted from an environment variable whose name is built by - * 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.) - */ - -int mdwopt(int argc, char *const *argv, - const char *shortopt, - const struct option *longopts, int *longind, - mdwopt_data *data, int flags) -{ - /* --- Local variables --- */ - - char *p, *q, *r; - char *prefix; - int i; - char noarg = '?'; - - /* --- Sort out our data --- */ - - if (!data) - data = &mdwopt_global; - - /* --- See if this is the first time --- */ - - if (data->ind == 0 || (data->ind == 1 && ~flags & OPTF_NOPROGNAME)) { - - /* --- Sort out default returning order --- */ - - if (getenv("_POSIX_OPTION_ORDER") || - getenv("POSIXLY_CORRECT")) - data->order = ORD_POSIX; - else - data->order = ORD_PERMUTE; - - /* --- Now see what the caller actually wants --- */ - - switch (shortopt[0]) { - case '-': - data->order = ORD_RETURN; - break; - case '+': - data->order = ORD_POSIX; - break; - case '!': - data->order = ORD_PERMUTE; - break; - } - - /* --- Now decide on the program's name --- */ - - if (~flags & OPTF_NOPROGNAME) { - p = q = (char *)argv[0]; - while (*p) { - if (*p++ == PATHSEP) - q = p; - } - data->prog = q; - - data->ind = data->next = 1; - data->list = 0; - - /* --- See about environment variables --- * - * - * Be careful. The program may be setuid, and an attacker might have - * given us a long name in @argv[0]@. If the name is very long, don't - * support this option. - */ - - if (flags & OPTF_ENVVAR && strlen(data->prog) < 48) { - - char buf[64]; - - /* --- For RISC OS, support a different format --- * - * - * Acorn's RISC OS tends to put settings in variables named - * `App$Options' rather than `APP'. Under RISC OS, I'll support - * both methods, just to avoid confuddlement. - */ - -#ifdef __riscos - sprintf(buf, "%s$Options", data->prog); - p = getenv(buf); - if (!p) { -#endif - - p = buf; - q = data->prog; - while (*q) - *p++ = toupper(*q++); - *p++ = 0; - p = getenv(buf); - -#ifdef __riscos - } -#endif - - /* --- Copy the options string into a buffer --- */ - - 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 { - strcpy(q, p); - data->ind = -1; - data->env = data->estart = q; - } - } - - } - } - else - data->ind = data->next = 0; - } - - /* --- Do some initial bodgery --- * - * - * The @shortopt@ string can have some interesting characters at the - * beginning. We'll skip past them. - */ - - switch (shortopt[0]) { - case '+': - case '-': - case '!': - shortopt++; - break; - } - - if (shortopt[0] == ':') { - noarg = shortopt[0]; - shortopt++; - } - - if (longind) - *longind = -1; - data->opt = -1; - data->arg = 0; - - /* --- Now go off and search for an option --- */ - - if (!data->list || !*data->list) { - data->order &= 3; /* Clear negation flag */ - - /* --- Now we need to find the next option --- * - * - * Exactly how we do this depends on the settings of the order variable. - * 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 a `%|--|%' - * * We reach the end of the list - * - * There are some added little wrinkles, which we'll meet as we go. - */ - - 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) { - permute(argv, data); - return (EOF); - } - break; - } - - /* --- Figure out how to proceed --- */ - - switch (data->order & 3) { - case ORD_POSIX: - return (EOF); - break; - case ORD_PERMUTE: - break; - case ORD_RETURN: - permute(argv, data); - data->arg = p; - return (0); - } - } - - /* --- We found an option --- */ - - permute(argv, data); - - /* --- Check for a numeric option --- * - * - * We only check the first character (or the second if the first is a - * sign). This ought to be enough. - */ - - 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); - while (isspace((unsigned char)data->arg[0])) - data->arg++; - if (!data->arg[0]) - data->arg = 0; - return (p[0] == '-' ? '#' : '#' | OPTF_NEGATED); - } - } - - /* --- Check for a long option --- */ - - if (p[0] == '+') - data->order |= ORD_NEGATE; - - if (((p[0] == '-' && p[1] == '-') || - (flags & OPTF_NOSHORTS && !findOpt(p[1], shortopt, data))) && - (~flags & OPTF_NOLONGS)) - { - 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; - prefix = "--"; - } - } else { - if ((flags & OPTF_NEGATION) && strncmp(p + 1, "no-", 3) == 0) { - p += 4; - prefix = "-no-"; - data->order |= ORD_NEGATE; - } else { - p++; - prefix = "-"; - } - } - - 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) { - match = i; - break; - } else { - match = -1; - goto botched; - } - } - else if (*q != *r) - break; - q++, r++; - } - } - - botched: - if (match == -1) { - if (data->err) { - fprintf(stderr, "%s: unrecognized option `%s%s'\n", - data->prog, - prefix, p); - } - return ('?'); - } - - if (longind) - *longind = match; - - /* --- Handle argument behaviour --- */ - - while (*p != 0 && *p != '=') - p++; - p = (*p ? p + 1 : 0); - q = (char *) longopts[match].name; - - switch (longopts[match].has_arg & OPTF_ARG) { - case OPTF_NOARG: - if (p) { - if (data->err) { - fprintf(stderr, - "%s: option `%s%s' does not accept arguments\n", - data->prog, - prefix, q); - } - return ('?'); - } - break; - - case OPTF_ARGREQ: - if (!p) { - p = nextword(argc, argv, data); - - if (!p) { - if (data->err) { - fprintf(stderr, "%s: option `%s%s' requires an argument\n", - data->prog, - prefix, q); - } - return (noarg); - } - - permute(argv, data); - } - break; - - case OPTF_ARGOPT: - /* Who cares? */ - break; - } - data->arg = p; - - /* --- Do correct things now we have a match --- */ - - 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) - *longopts[match].flag = 0; - else - *longopts[match].flag = longopts[match].val; - } - return (0); - } else { - if (data->order & ORD_NEGATE) - return (longopts[match].val | OPTF_NEGATED); - else - return (longopts[match].val); - } - } - - /* --- Do short options things --- */ - - else { - if (p[0] == '+') - data->order |= ORD_NEGATE; - data->list = p + 1; - } - } - - /* --- Now process the short options --- */ - - i = *data->list++; - data->opt = i; - - 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 ? '+' : '-', - i); - } - return ('?'); - } - - data->opt = i; - - /* --- Sort out an argument, if we expect one --- */ - - 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); - if (!q) { - if (data->err) { - fprintf(stderr, "%s: option `%c%c' requires an argument\n", - data->prog, - data->order & ORD_NEGATE ? '+' : '-', - i); - } - return (noarg); - } - permute(argv, data); - } - - data->arg = q; - } - return ((data->order & ORD_NEGATE) ? i | OPTF_NEGATED : i); -} - -/*----- That's all, folks -------------------------------------------------*/ diff --git a/mdwopt.c b/mdwopt.c new file mode 120000 index 0000000..09bed29 --- /dev/null +++ b/mdwopt.c @@ -0,0 +1 @@ +.ext/cfd/src/mdwopt.c \ No newline at end of file