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