aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/include/util.h18
-rw-r--r--lib/src/split_argv.c109
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;
}