md2html

Markdown to HTML converter
git clone https://noulin.net/git/md2html.git
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 }