diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/util-common.c | 112 | ||||
-rw-r--r-- | src/common/util-common.h | 3 |
2 files changed, 115 insertions, 0 deletions
diff --git a/src/common/util-common.c b/src/common/util-common.c index 3aa0eac9..fd91ecec 100644 --- a/src/common/util-common.c +++ b/src/common/util-common.c @@ -197,3 +197,115 @@ char *strndup(const char *s, size_t n) return (char *)memcpy(new, s, len); } #endif + +void wordsplit_free(char **ws) +{ + if(ws) { + char **c; + for(c = ws; *c; c++) { + free(*c); + } + free(ws); + } +} + +char **wordsplit(const char *str) +{ + const char *c = str, *end; + char **out = NULL, **outsave; + size_t count = 0; + + if(str == NULL) { + errno = EINVAL; + return NULL; + } + + for(c = str; isspace(*c); c++); + while(*c) { + size_t wordlen = 0; + + /* extend our array */ + outsave = out; + if((out = realloc(out, (count + 1) * sizeof(char*))) == NULL) { + out = outsave; + goto error; + } + + /* calculate word length and check for unbalanced quotes */ + for(end = c; *end && !isspace(*end); end++) { + if(*end == '\'' || *end == '"') { + char quote = *end; + while(*(++end) && *end != quote) { + if(*end == '\\' && *(end + 1) == quote) { + end++; + } + wordlen++; + } + if(*end != quote) { + errno = EINVAL; + goto error; + } + } else { + if(*end == '\\' && (end[1] == '\'' || end[1] == '"')) { + end++; /* skip the '\\' */ + } + wordlen++; + } + } + + if(wordlen == (size_t) (end - c)) { + /* no internal quotes or escapes, copy it the easy way */ + if((out[count++] = strndup(c, wordlen)) == NULL) { + goto error; + } + } else { + /* manually copy to remove quotes and escapes */ + char *dest = out[count++] = malloc(wordlen + 1); + if(dest == NULL) { goto error; } + while(c < end) { + if(*c == '\'' || *c == '"') { + char quote = *c; + /* we know there must be a matching end quote, + * no need to check for '\0' */ + for(c++; *c != quote; c++) { + if(*c == '\\' && *(c + 1) == quote) { + c++; + } + *(dest++) = *c; + } + c++; + } else { + if(*c == '\\' && (c[1] == '\'' || c[1] == '"')) { + c++; /* skip the '\\' */ + } + *(dest++) = *(c++); + } + } + *dest = '\0'; + } + + if(*end == '\0') { + break; + } else { + for(c = end + 1; isspace(*c); c++); + } + } + + outsave = out; + if((out = realloc(out, (count + 1) * sizeof(char*))) == NULL) { + out = outsave; + goto error; + } + + out[count++] = NULL; + + return out; + +error: + /* can't use wordsplit_free here because NULL has not been appended */ + while(count) { + free(out[--count]); + } + free(out); + return NULL; +} diff --git a/src/common/util-common.h b/src/common/util-common.h index 3434e1eb..7ec588cc 100644 --- a/src/common/util-common.h +++ b/src/common/util-common.h @@ -30,6 +30,9 @@ int llstat(char *path, struct stat *buf); char *safe_fgets(char *s, int size, FILE *stream); +void wordsplit_free(char **ws); +char **wordsplit(const char *str); + size_t strtrim(char *str); #ifndef HAVE_STRNDUP |