Home | History | Annotate | Download | only in support
      1 /*
      2  * argv_parse.c --- utility function for parsing a string into a
      3  * 	argc, argv array.
      4  *
      5  * This file defines a function argv_parse() which parsing a
      6  * passed-in string, handling double quotes and backslashes, and
      7  * creates an allocated argv vector which can be freed using the
      8  * argv_free() function.
      9  *
     10  * See argv_parse.h for the formal definition of the functions.
     11  *
     12  * Copyright 1999 by Theodore Ts'o.
     13  *
     14  * Permission to use, copy, modify, and distribute this software for
     15  * any purpose with or without fee is hereby granted, provided that
     16  * the above copyright notice and this permission notice appear in all
     17  * copies.  THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE
     18  * AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     19  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
     21  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
     22  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
     23  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
     24  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  (Isn't
     25  * it sick that the U.S. culture of lawsuit-happy lawyers requires
     26  * this kind of disclaimer?)
     27  *
     28  * Version 1.1, modified 2/27/1999
     29  */
     30 
     31 #include "config.h"
     32 #ifdef HAVE_STDLIB_H
     33 #include <stdlib.h>
     34 #endif
     35 #include <ctype.h>
     36 #include <string.h>
     37 #include "argv_parse.h"
     38 
     39 #define STATE_WHITESPACE	1
     40 #define STATE_TOKEN		2
     41 #define STATE_QUOTED		3
     42 
     43 /*
     44  * Returns 0 on success, -1 on failure.
     45  */
     46 int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv)
     47 {
     48 	int	argc = 0, max_argc = 0;
     49 	char 	**argv, **new_argv, *buf, ch;
     50 	char	*cp = 0, *outcp = 0;
     51 	int	state = STATE_WHITESPACE;
     52 
     53 	buf = malloc(strlen(in_buf)+1);
     54 	if (!buf)
     55 		return -1;
     56 
     57 	max_argc = 0; argc = 0; argv = 0;
     58 	outcp = buf;
     59 	for (cp = in_buf; (ch = *cp); cp++) {
     60 		if (state == STATE_WHITESPACE) {
     61 			if (isspace((int) ch))
     62 				continue;
     63 			/* Not whitespace, so start a new token */
     64 			state = STATE_TOKEN;
     65 			if (argc >= max_argc) {
     66 				max_argc += 3;
     67 				new_argv = realloc(argv,
     68 						  (max_argc+1)*sizeof(char *));
     69 				if (!new_argv) {
     70 					free(argv);
     71 					free(buf);
     72 					return -1;
     73 				}
     74 				argv = new_argv;
     75 			}
     76 			argv[argc++] = outcp;
     77 		}
     78 		if (state == STATE_QUOTED) {
     79 			if (ch == '"')
     80 				state = STATE_TOKEN;
     81 			else
     82 				*outcp++ = ch;
     83 			continue;
     84 		}
     85 		/* Must be processing characters in a word */
     86 		if (isspace((int) ch)) {
     87 			/*
     88 			 * Terminate the current word and start
     89 			 * looking for the beginning of the next word.
     90 			 */
     91 			*outcp++ = 0;
     92 			state = STATE_WHITESPACE;
     93 			continue;
     94 		}
     95 		if (ch == '"') {
     96 			state = STATE_QUOTED;
     97 			continue;
     98 		}
     99 		if (ch == '\\') {
    100 			ch = *++cp;
    101 			switch (ch) {
    102 			case '\0':
    103 				ch = '\\'; cp--; break;
    104 			case 'n':
    105 				ch = '\n'; break;
    106 			case 't':
    107 				ch = '\t'; break;
    108 			case 'b':
    109 				ch = '\b'; break;
    110 			}
    111 		}
    112 		*outcp++ = ch;
    113 	}
    114 	if (state != STATE_WHITESPACE)
    115 		*outcp++ = '\0';
    116 	if (argv == 0) {
    117 		argv = malloc(sizeof(char *));
    118 		free(buf);
    119 	}
    120 	argv[argc] = 0;
    121 	if (ret_argc)
    122 		*ret_argc = argc;
    123 	if (ret_argv)
    124 		*ret_argv = argv;
    125 	return 0;
    126 }
    127 
    128 void argv_free(char **argv)
    129 {
    130 	free(*argv);
    131 	free(argv);
    132 }
    133 
    134 #ifdef DEBUG
    135 /*
    136  * For debugging
    137  */
    138 
    139 #include <stdio.h>
    140 
    141 int main(int argc, char **argv)
    142 {
    143 	int	ac, ret;
    144 	char	**av, **cpp;
    145 	char	buf[256];
    146 
    147 	while (!feof(stdin)) {
    148 		if (fgets(buf, sizeof(buf), stdin) == NULL)
    149 			break;
    150 		ret = argv_parse(buf, &ac, &av);
    151 		if (ret != 0) {
    152 			printf("Argv_parse returned %d!\n", ret);
    153 			continue;
    154 		}
    155 		printf("Argv_parse returned %d arguments...\n", ac);
    156 		for (cpp = av; *cpp; cpp++) {
    157 			if (cpp != av)
    158 				printf(", ");
    159 			printf("'%s'", *cpp);
    160 		}
    161 		printf("\n");
    162 		argv_free(av);
    163 	}
    164 	exit(0);
    165 }
    166 #endif /* DEBUG */
    167