diff options
-rw-r--r-- | lib/include/util.h | 18 | ||||
-rw-r--r-- | lib/src/split_argv.c | 109 |
2 files changed, 75 insertions, 52 deletions
diff --git a/lib/include/util.h b/lib/include/util.h index c13b942..d824b22 100644 --- a/lib/include/util.h +++ b/lib/include/util.h @@ -100,13 +100,25 @@ int rdline(rdline_t *t); int unescape(char *src); /* + Replace spaces in 'str' with null bytes. Tread strings (started and + terminated with double-quotes which can be escaped) as a single block. + Such strings are run through unescap(). All elements are tightly + packed together and the function returns the number of consecutive + argument strings that are now inside 'str'. + + Returns a negative value if unescape() fails, a string is not + termianted or two such strings touch each other without a white + space in between. +*/ +int pack_argv(char *str); + +/* Split a space seperated string into a sequence of null-terminated strings. Return a NULL terminated array of strings pointing to the start of each sub string. - If a double quote is encountered, the entire string up to to the next, - unescaped double quite is interpreted as a single sub string and - fed through the unescape function. + It basically runs pack_argv on 'str' and then constructs the argv + vector from that, with each entry pointing into 'str'. The returned array must be freed with free(). */ diff --git a/lib/src/split_argv.c b/lib/src/split_argv.c index 9beec5c..b95720d 100644 --- a/lib/src/split_argv.c +++ b/lib/src/split_argv.c @@ -16,78 +16,89 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <stdlib.h> +#include <string.h> #include <ctype.h> #include <errno.h> +#include <stdio.h> #include "util.h" -char **split_argv(char *str) +int pack_argv(char *str) { - size_t i = 0, cap = 0, new_cap; - char **argv = NULL, **new; - char *ptr; + char *dst, *start; + int count = 0; - ptr = str; + dst = str; for (;;) { - if (*ptr == ' ') { - ++ptr; - continue; - } - - if (i == cap) { - new_cap = cap ? cap * 2 : 16; - new = realloc(argv, sizeof(argv[0]) * new_cap); - - if (new == NULL) { - free(argv); - errno = ENOMEM; - return NULL; - } - - cap = new_cap; - argv = new; - } + while (*str == ' ') + ++str; - if (*ptr == '\0') { - argv[i++] = NULL; + if (*str == '\0') break; - } - - argv[i++] = ptr; - if (*ptr == '"') { - ++ptr; - while (*ptr != '\0' && *ptr != '"') { - if (ptr[0] == '\\' && ptr[1] != '\0') - ++ptr; + if (*str == '"') { + start = dst; + *(dst++) = *(str++); - ++ptr; + while (*str != '"') { + if (*str == '\0') + goto fail_str; + if (str[0] == '\\' && str[1] != '\0') + *(dst++) = *(str++); + *(dst++) = *(str++); } - if (*ptr == '"') - ++ptr; + *(dst++) = *(str++); - if (*ptr == ' ') { - *(ptr++) = '\0'; - } else if (*ptr != '\0') { + if (*str != ' ' && *str != '\0') goto fail_str; - } + if (*str == ' ') + ++str; - if (unescape(argv[i - 1])) + *(dst++) = '\0'; + + if (unescape(start)) goto fail_str; - } else { - while (*ptr != '\0' && *ptr != ' ') - ++ptr; - if (*ptr == ' ') - *(ptr++) = '\0'; + dst = start + strlen(start) + 1; + } else { + while (*str != '\0' && *str != ' ') + *(dst++) = *(str++); + if (*str == ' ') { + ++str; + *(dst++) = '\0'; + } } + + ++count; } - return argv; + *dst = '\0'; + return count; fail_str: - free(argv); errno = EINVAL; - return NULL; + return -1; +} + +char **split_argv(char *str) +{ + char **argv = NULL; + int i, count; + + count = pack_argv(str); + if (count <= 0) + return NULL; + + argv = malloc(sizeof(argv[0]) * (count + 1)); + if (argv == NULL) + return NULL; + + for (i = 0; i < count; ++i) { + argv[i] = str; + str += strlen(str) + 1; + } + + argv[i] = NULL; + return argv; } |