💾 Archived View for gmi.noulin.net › gitRepositories › md2html › file › cmdline.c.gmi captured on 2023-01-29 at 13:24:34. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

md2html

Log

Files

Refs

README

cmdline.c (6934B)

     1 /* cmdline.c: a reentrant version of getopt(). Written 2006 by Brian
     2  * Raiter. This code is in the public domain.
     3  */
     4 
     5 #include        <stdio.h>
     6 #include        <stdlib.h>
     7 #include        <string.h>
     8 #include        <ctype.h>
     9 #include        "cmdline.h"
    10 
    11 #define        docallback(opt, val) \
    12             do { if ((r = callback(opt, val, data)) != 0) return r; } while (0)
    13 
    14 /* Parse the given cmdline arguments.
    15  */
    16 int readoptions(option const* list, int argc, char **argv,
    17                 int (*callback)(int, char const*, void*), void *data)
    18 {
    19     char                argstring[] = "--";
    20     option const       *opt;
    21     char const               *val;
    22     char const               *p;
    23     int                        stop = 0;
    24     int                        argi, len, r;
    25 
    26     if (!list || !callback)
    27         return -1;
    28 
    29     for (argi = 1 ; argi < argc ; ++argi)
    30     {
    31         /* First, check for "--", which forces all remaining arguments
    32          * to be treated as non-options.
    33          */
    34         if (!stop && argv[argi][0] == '-' && argv[argi][1] == '-'
    35                                           && argv[argi][2] == '\0') {
    36             stop = 1;
    37             continue;
    38         }
    39 
    40         /* Arguments that do not begin with '-' (or are only "-") are
    41          * not options.
    42          */
    43         if (stop || argv[argi][0] != '-' || argv[argi][1] == '\0') {
    44             docallback(0, argv[argi]);
    45             continue;
    46         }
    47 
    48         if (argv[argi][1] == '-')
    49         {
    50             /* Arguments that begin with a double-dash are long
    51              * options.
    52              */
    53             p = argv[argi] + 2;
    54             val = strchr(p, '=');
    55             if (val)
    56                 len = val++ - p;
    57             else
    58                 len = strlen(p);
    59 
    60             /* Is it on the list of valid options? If so, does it
    61              * expect a parameter?
    62              */
    63             for (opt = list ; opt->optval ; ++opt)
    64                 if (opt->name && !strncmp(p, opt->name, len)
    65                               && !opt->name[len])
    66                     break;
    67             if (!opt->optval) {
    68                 docallback('?', argv[argi]);
    69             } else if (!val && opt->arg == 1) {
    70                 docallback(':', argv[argi]);
    71             } else if (val && opt->arg == 0) {
    72                 docallback('=', argv[argi]);
    73             } else {
    74                 docallback(opt->optval, val);
    75             }
    76         }
    77         else
    78         {
    79             /* Arguments that begin with a single dash contain one or
    80              * more short options. Each character in the argument is
    81              * examined in turn, unless a parameter consumes the rest
    82              * of the argument (or possibly even the following
    83              * argument).
    84              */
    85             for (p = argv[argi] + 1 ; *p ; ++p) {
    86                 for (opt = list ; opt->optval ; ++opt)
    87                     if (opt->chname == *p)
    88                         break;
    89                 if (!opt->optval) {
    90                     argstring[1] = *p;
    91                     docallback('?', argstring);
    92                     continue;
    93                 } else if (opt->arg == 0) {
    94                     docallback(opt->optval, NULL);
    95                     continue;
    96                 } else if (p[1]) {
    97                     docallback(opt->optval, p + 1);
    98                     break;
    99                 } else if (argi + 1 < argc && strcmp(argv[argi + 1], "--")) {
   100                     ++argi;
   101                     docallback(opt->optval, argv[argi]);
   102                     break;
   103                 } else if (opt->arg == 2) {
   104                     docallback(opt->optval, NULL);
   105                     continue;
   106                 } else {
   107                     argstring[1] = *p;
   108                     docallback(':', argstring);
   109                     break;
   110                 }
   111             }
   112         }
   113     }
   114     return 0;
   115 }
   116 
   117 /* Verify that str points to an ASCII zero or one (optionally with
   118  * whitespace) and return the value present, or -1 if str's contents
   119  * are anything else.
   120  */
   121 static int readboolvalue(char const *str)
   122 {
   123     char        d;
   124 
   125     while (isspace(*str))
   126         ++str;
   127     if (!*str)
   128         return -1;
   129     d = *str++;
   130     while (isspace(*str))
   131         ++str;
   132     if (*str)
   133         return -1;
   134     if (d == '0')
   135         return 0;
   136     else if (d == '1')
   137         return 1;
   138     else
   139         return -1;
   140 }
   141 
   142 /* Parse a configuration file.
   143  */
   144 int readcfgfile(option const* list, FILE *fp,
   145                 int (*callback)(int, char const*, void*), void *data)
   146 {
   147     char                buf[1024];
   148     option const       *opt;
   149     char               *name, *val, *p;
   150     int                        len, f, r;
   151 
   152     while (fgets(buf, sizeof buf, fp) != NULL)
   153     {
   154         /* Strip off the trailing newline and any leading whitespace.
   155          * If the line begins with a hash sign, skip it entirely.
   156          */
   157         len = strlen(buf);
   158         if (len && buf[len - 1] == '\n')
   159             buf[--len] = '\0';
   160         for (p = buf ; isspace(*p) ; ++p) ;
   161         if (!*p || *p == '#')
   162             continue;
   163 
   164         /* Find the end of the option's name and the beginning of the
   165          * parameter, if any.
   166          */
   167         for (name = p ; *p && *p != '=' && !isspace(*p) ; ++p) ;
   168         len = p - name;
   169         for ( ; *p == '=' || isspace(*p) ; ++p) ;
   170         val = p;
   171 
   172         /* Is it on the list of valid options? Does it take a
   173          * full parameter, or just an optional boolean?
   174          */
   175         for (opt = list ; opt->optval ; ++opt)
   176             if (opt->name && !strncmp(name, opt->name, len)
   177                           && !opt->name[len])
   178                     break;
   179         if (!opt->optval) {
   180             docallback('?', name);
   181         } else if (!*val && opt->arg == 1) {
   182             docallback(':', name);
   183         } else if (*val && opt->arg == 0) {
   184             f = readboolvalue(val);
   185             if (f < 0)
   186                 docallback('=', name);
   187             else if (f == 1)
   188                 docallback(opt->optval, NULL);
   189         } else {
   190             docallback(opt->optval, val);
   191         }
   192     }
   193     return ferror(fp) ? -1 : 0;
   194 }
   195 
   196 /* Turn a string containing a cmdline into an argc-argv pair.
   197  */
   198 int makecmdline(char const *cmdline, int *argcp, char ***argvp)
   199 {
   200     char      **argv;
   201     int                argc;
   202     char const *s;
   203     int                n, quoted;
   204 
   205     if (!cmdline)
   206         return 0;
   207 
   208     /* Calcuate argc by counting the number of "clumps" of non-spaces.
   209      */
   210     for (s = cmdline ; isspace(*s) ; ++s) ;
   211     if (!*s) {
   212         *argcp = 1;
   213         if (argvp) {
   214             *argvp = malloc(2 * sizeof(char*));
   215             if (!*argvp)
   216                 return 0;
   217             (*argvp)[0] = NULL;
   218             (*argvp)[1] = NULL;
   219         }
   220         return 1;
   221     }
   222     for (argc = 2, quoted = 0 ; *s ; ++s) {
   223         if (quoted == '"') {
   224             if (*s == '"')
   225                 quoted = 0;
   226             else if (*s == '\\' && s[1])
   227                 ++s;
   228         } else if (quoted == '\'') {
   229             if (*s == '\'')
   230                 quoted = 0;
   231         } else {
   232             if (isspace(*s)) {
   233                 for ( ; isspace(s[1]) ; ++s) ;
   234                 if (!s[1])
   235                     break;
   236                 ++argc;
   237             } else if (*s == '"' || *s == '\'') {
   238                 quoted = *s;
   239             }
   240         }
   241     }
   242 
   243     *argcp = argc;
   244     if (!argvp)
   245         return 1;
   246 
   247     /* Allocate space for all the arguments and their pointers.
   248      */
   249     argv = malloc((argc + 1) * sizeof(char*) + strlen(cmdline) + 1);
   250     *argvp = argv;
   251     if (!argv)
   252         return 0;
   253     argv[0] = NULL;
   254     argv[1] = (char*)(argv + argc + 1);
   255 
   256     /* Copy the string into the allocated memory immediately after the
   257      * argv array. Where spaces immediately follows a nonspace,
   258      * replace it with a \0. Where a nonspace immediately follows
   259      * spaces, store a pointer to it. (Except, of course, when the
   260      * space-nonspace transitions occur within quotes.)
   261      */
   262     for (s = cmdline ; isspace(*s) ; ++s) ;
   263     for (argc = 1, n = 0, quoted = 0 ; *s ; ++s) {
   264         if (quoted == '"') {
   265             if (*s == '"') {
   266                 quoted = 0;
   267             } else {
   268                 if (*s == '\\' && s[1])
   269                     ++s;
   270                 argv[argc][n++] = *s;
   271             }
   272         } else if (quoted == '\'') {
   273             if (*s == '\'')
   274                 quoted = 0;
   275             else
   276                 argv[argc][n++] = *s;
   277         } else {
   278             if (isspace(*s)) {
   279                 argv[argc][n] = '\0';
   280                 for ( ; isspace(s[1]) ; ++s) ;
   281                 if (!s[1])
   282                     break;
   283                 argv[argc + 1] = argv[argc] + n + 1;
   284                 ++argc;
   285                 n = 0;
   286             } else {
   287                 if (*s == '"' || *s == '\'')
   288                     quoted = *s;
   289                 else
   290                     argv[argc][n++] = *s;
   291             }
   292         }
   293     }
   294     argv[argc + 1] = NULL;
   295     return 1;
   296 }