Edit

kc3-lang/md4c/md2html/cmdline.c

Branch :

  • Show log

    Commit

  • Author : Martin Mitas
    Date : 2016-10-03 20:17:15
    Hash : efed58af
    Message : Initial commit.

  • md2html/cmdline.c
  • /* cmdline.c: a reentrant version of getopt(). Written 2006 by Brian
     * Raiter. This code is in the public domain.
     */
    
    #include	<stdio.h>
    #include	<stdlib.h>
    #include	<string.h>
    #include	<ctype.h>
    #include	"cmdline.h"
    
    #define	docallback(opt, val) \
    	    do { if ((r = callback(opt, val, data)) != 0) return r; } while (0)
    
    /* Parse the given cmdline arguments.
     */
    int readoptions(option const* list, int argc, char **argv,
    		int (*callback)(int, char const*, void*), void *data)
    {
        char		argstring[] = "--";
        option const       *opt;
        char const	       *val;
        char const	       *p;
        int			stop = 0;
        int			argi, len, r;
    
        if (!list || !callback)
    	return -1;
    
        for (argi = 1 ; argi < argc ; ++argi)
        {
    	/* First, check for "--", which forces all remaining arguments
    	 * to be treated as non-options.
    	 */
    	if (!stop && argv[argi][0] == '-' && argv[argi][1] == '-'
    					  && argv[argi][2] == '\0') {
    	    stop = 1;
    	    continue;
    	}
    
    	/* Arguments that do not begin with '-' (or are only "-") are
    	 * not options.
    	 */
    	if (stop || argv[argi][0] != '-' || argv[argi][1] == '\0') {
    	    docallback(0, argv[argi]);
    	    continue;
    	}
    
    	if (argv[argi][1] == '-')
    	{
    	    /* Arguments that begin with a double-dash are long
    	     * options.
    	     */
    	    p = argv[argi] + 2;
    	    val = strchr(p, '=');
    	    if (val)
    		len = val++ - p;
    	    else
    		len = strlen(p);
    
    	    /* Is it on the list of valid options? If so, does it
    	     * expect a parameter?
    	     */
    	    for (opt = list ; opt->optval ; ++opt)
    		if (opt->name && !strncmp(p, opt->name, len)
    			      && !opt->name[len])
    		    break;
    	    if (!opt->optval) {
    		docallback('?', argv[argi]);
    	    } else if (!val && opt->arg == 1) {
    		docallback(':', argv[argi]);
    	    } else if (val && opt->arg == 0) {
    		docallback('=', argv[argi]);
    	    } else {
    		docallback(opt->optval, val);
    	    }
    	}
    	else
    	{
    	    /* Arguments that begin with a single dash contain one or
    	     * more short options. Each character in the argument is
    	     * examined in turn, unless a parameter consumes the rest
    	     * of the argument (or possibly even the following
    	     * argument).
    	     */
    	    for (p = argv[argi] + 1 ; *p ; ++p) {
    		for (opt = list ; opt->optval ; ++opt)
    		    if (opt->chname == *p)
    			break;
    		if (!opt->optval) {
    		    argstring[1] = *p;
    		    docallback('?', argstring);
    		    continue;
    		} else if (opt->arg == 0) {
    		    docallback(opt->optval, NULL);
    		    continue;
    		} else if (p[1]) {
    		    docallback(opt->optval, p + 1);
    		    break;
    		} else if (argi + 1 < argc && strcmp(argv[argi + 1], "--")) {
    		    ++argi;
    		    docallback(opt->optval, argv[argi]);
    		    break;
    		} else if (opt->arg == 2) {
    		    docallback(opt->optval, NULL);
    		    continue;
    		} else {
    		    argstring[1] = *p;
    		    docallback(':', argstring);
    		    break;
    		}
    	    }
    	}
        }
        return 0;
    }
    
    /* Verify that str points to an ASCII zero or one (optionally with
     * whitespace) and return the value present, or -1 if str's contents
     * are anything else.
     */
    static int readboolvalue(char const *str)
    {
        char	d;
    
        while (isspace(*str))
    	++str;
        if (!*str)
    	return -1;
        d = *str++;
        while (isspace(*str))
    	++str;
        if (*str)
    	return -1;
        if (d == '0')
    	return 0;
        else if (d == '1')
    	return 1;
        else
    	return -1;
    }
    
    /* Parse a configuration file.
     */
    int readcfgfile(option const* list, FILE *fp,
    		int (*callback)(int, char const*, void*), void *data)
    {
        char		buf[1024];
        option const       *opt;
        char	       *name, *val, *p;
        int			len, f, r;
    
        while (fgets(buf, sizeof buf, fp) != NULL)
        {
    	/* Strip off the trailing newline and any leading whitespace.
    	 * If the line begins with a hash sign, skip it entirely.
    	 */
    	len = strlen(buf);
    	if (len && buf[len - 1] == '\n')
    	    buf[--len] = '\0';
    	for (p = buf ; isspace(*p) ; ++p) ;
    	if (!*p || *p == '#')
    	    continue;
    
    	/* Find the end of the option's name and the beginning of the
    	 * parameter, if any.
    	 */
    	for (name = p ; *p && *p != '=' && !isspace(*p) ; ++p) ;
    	len = p - name;
    	for ( ; *p == '=' || isspace(*p) ; ++p) ;
    	val = p;
    
    	/* Is it on the list of valid options? Does it take a
    	 * full parameter, or just an optional boolean?
    	 */
    	for (opt = list ; opt->optval ; ++opt)
    	    if (opt->name && !strncmp(name, opt->name, len)
    			  && !opt->name[len])
    		    break;
    	if (!opt->optval) {
    	    docallback('?', name);
    	} else if (!*val && opt->arg == 1) {
    	    docallback(':', name);
    	} else if (*val && opt->arg == 0) {
    	    f = readboolvalue(val);
    	    if (f < 0)
    		docallback('=', name);
    	    else if (f == 1)
    		docallback(opt->optval, NULL);
    	} else {
    	    docallback(opt->optval, val);
    	}
        }
        return ferror(fp) ? -1 : 0;
    }
    
    /* Turn a string containing a cmdline into an argc-argv pair.
     */
    int makecmdline(char const *cmdline, int *argcp, char ***argvp)
    {
        char      **argv;
        int		argc;
        char const *s;
        int		n, quoted;
    
        if (!cmdline)
    	return 0;
    
        /* Calcuate argc by counting the number of "clumps" of non-spaces.
         */
        for (s = cmdline ; isspace(*s) ; ++s) ;
        if (!*s) {
    	*argcp = 1;
    	if (argvp) {
    	    *argvp = malloc(2 * sizeof(char*));
    	    if (!*argvp)
    		return 0;
    	    (*argvp)[0] = NULL;
    	    (*argvp)[1] = NULL;
    	}
    	return 1;
        }
        for (argc = 2, quoted = 0 ; *s ; ++s) {
    	if (quoted == '"') {
    	    if (*s == '"')
    		quoted = 0;
    	    else if (*s == '\\' && s[1])
    		++s;
    	} else if (quoted == '\'') {
    	    if (*s == '\'')
    		quoted = 0;
    	} else {
    	    if (isspace(*s)) {
    		for ( ; isspace(s[1]) ; ++s) ;
    		if (!s[1])
    		    break;
    		++argc;
    	    } else if (*s == '"' || *s == '\'') {
    		quoted = *s;
    	    }
    	}
        }
    
        *argcp = argc;
        if (!argvp)
    	return 1;
    
        /* Allocate space for all the arguments and their pointers.
         */
        argv = malloc((argc + 1) * sizeof(char*) + strlen(cmdline) + 1);
        *argvp = argv;
        if (!argv)
    	return 0;
        argv[0] = NULL;
        argv[1] = (char*)(argv + argc + 1);
    
        /* Copy the string into the allocated memory immediately after the
         * argv array. Where spaces immediately follows a nonspace,
         * replace it with a \0. Where a nonspace immediately follows
         * spaces, store a pointer to it. (Except, of course, when the
         * space-nonspace transitions occur within quotes.)
         */
        for (s = cmdline ; isspace(*s) ; ++s) ;
        for (argc = 1, n = 0, quoted = 0 ; *s ; ++s) {
    	if (quoted == '"') {
    	    if (*s == '"') {
    		quoted = 0;
    	    } else {
    		if (*s == '\\' && s[1])
    		    ++s;
    		argv[argc][n++] = *s;
    	    }
    	} else if (quoted == '\'') {
    	    if (*s == '\'')
    		quoted = 0;
    	    else
    		argv[argc][n++] = *s;
    	} else {
    	    if (isspace(*s)) {
    		argv[argc][n] = '\0';
    		for ( ; isspace(s[1]) ; ++s) ;
    		if (!s[1])
    		    break;
    		argv[argc + 1] = argv[argc] + n + 1;
    		++argc;
    		n = 0;
    	    } else {
    		if (*s == '"' || *s == '\'')
    		    quoted = *s;
    		else
    		    argv[argc][n++] = *s;
    	    }
    	}
        }
        argv[argc + 1] = NULL;
        return 1;
    }