Makefile.am: Ship `debian/compat'.
[cfd] / mdwopt.c
index 1717d2d..fc76635 100644 (file)
--- a/mdwopt.c
+++ b/mdwopt.c
@@ -1,7 +1,5 @@
 /* -*-c-*-
  *
- * $Id: mdwopt.c,v 1.7 1999/06/18 21:59:46 mdw Exp $
- *
  * Options parsing, similar to GNU @getopt_long@
  *
  * (c) 1996 Straylight/Edgeware
  * MA 02111-1307, USA.
  */
 
-/*----- Revision history --------------------------------------------------*
- *
- * $Log: mdwopt.c,v $
- * Revision 1.7  1999/06/18 21:59:46  mdw
- * Fix stupid bug which only read one word from environment variables.
- *
- * Revision 1.6  1999/05/20 23:00:42  mdw
- * Little formatting things.
- *
- * 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 <ctype.h>
@@ -112,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
@@ -126,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++;
-    if (*p)
-      *p++ = 0;
-    data->env = p;
-    if (p != q)
-      return (q);
-    data->env = 0;
+    char *p;
+    if ((p = qword(&data->env, STRF_QUOTE)) != 0)
+      return (p);
     data->ind = 1;
   }
 
@@ -190,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);
@@ -406,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 --- */
 
@@ -422,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;
     }
@@ -479,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
        }
@@ -492,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;
          }
        }
 
@@ -531,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 --- */
 
@@ -554,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 --- *
      *
@@ -614,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,
@@ -679,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,
@@ -702,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,
@@ -719,7 +739,7 @@ int mdwopt(int argc, char *const *argv,
          }
          break;
 
-       case optional_argument:
+       case OPTF_ARGOPT:
          /* Who cares? */
          break;
       }
@@ -727,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;
@@ -739,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);
@@ -751,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,
@@ -773,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,