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 #ifdef HAVE_STDLIB_H 32 #include <stdlib.h> 33 #endif 34 #include <ctype.h> 35 #include <string.h> 36 #include "argv_parse.h" 37 38 #define STATE_WHITESPACE 1 39 #define STATE_TOKEN 2 40 #define STATE_QUOTED 3 41 42 /* 43 * Returns 0 on success, -1 on failure. 44 */ 45 int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv) 46 { 47 int argc = 0, max_argc = 0; 48 char **argv, **new_argv, *buf, ch; 49 char *cp = 0, *outcp = 0; 50 int state = STATE_WHITESPACE; 51 52 buf = malloc(strlen(in_buf)+1); 53 if (!buf) 54 return -1; 55 56 max_argc = 0; argc = 0; argv = 0; 57 outcp = buf; 58 for (cp = in_buf; (ch = *cp); cp++) { 59 if (state == STATE_WHITESPACE) { 60 if (isspace((int) ch)) 61 continue; 62 /* Not whitespace, so start a new token */ 63 state = STATE_TOKEN; 64 if (argc >= max_argc) { 65 max_argc += 3; 66 new_argv = realloc(argv, 67 (max_argc+1)*sizeof(char *)); 68 if (!new_argv) { 69 free(argv); 70 free(buf); 71 return -1; 72 } 73 argv = new_argv; 74 } 75 argv[argc++] = outcp; 76 } 77 if (state == STATE_QUOTED) { 78 if (ch == '"') 79 state = STATE_TOKEN; 80 else 81 *outcp++ = ch; 82 continue; 83 } 84 /* Must be processing characters in a word */ 85 if (isspace((int) ch)) { 86 /* 87 * Terminate the current word and start 88 * looking for the beginning of the next word. 89 */ 90 *outcp++ = 0; 91 state = STATE_WHITESPACE; 92 continue; 93 } 94 if (ch == '"') { 95 state = STATE_QUOTED; 96 continue; 97 } 98 if (ch == '\\') { 99 ch = *++cp; 100 switch (ch) { 101 case '\0': 102 ch = '\\'; cp--; break; 103 case 'n': 104 ch = '\n'; break; 105 case 't': 106 ch = '\t'; break; 107 case 'b': 108 ch = '\b'; break; 109 } 110 } 111 *outcp++ = ch; 112 } 113 if (state != STATE_WHITESPACE) 114 *outcp++ = '\0'; 115 if (argv == 0) { 116 argv = malloc(sizeof(char *)); 117 free(buf); 118 } 119 argv[argc] = 0; 120 if (ret_argc) 121 *ret_argc = argc; 122 if (ret_argv) 123 *ret_argv = argv; 124 return 0; 125 } 126 127 void argv_free(char **argv) 128 { 129 free(*argv); 130 free(argv); 131 } 132 133 #ifdef DEBUG 134 /* 135 * For debugging 136 */ 137 138 #include <stdio.h> 139 140 int main(int argc, char **argv) 141 { 142 int ac, ret; 143 char **av, **cpp; 144 char buf[256]; 145 146 while (!feof(stdin)) { 147 if (fgets(buf, sizeof(buf), stdin) == NULL) 148 break; 149 ret = argv_parse(buf, &ac, &av); 150 if (ret != 0) { 151 printf("Argv_parse returned %d!\n", ret); 152 continue; 153 } 154 printf("Argv_parse returned %d arguments...\n", ac); 155 for (cpp = av; *cpp; cpp++) { 156 if (cpp != av) 157 printf(", "); 158 printf("'%s'", *cpp); 159 } 160 printf("\n"); 161 argv_free(av); 162 } 163 exit(0); 164 } 165 #endif /* DEBUG */ 166