/* * Parse command string. Returns -1 on error. If returning -1, cause is error * string, or NULL for empty command. */ int cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause) { size_t p; int ch, i, argc, rval; char **argv, *buf, *t; const char *whitespace, *equals; size_t len; argv = NULL; argc = 0; buf = NULL; len = 0; *cause = NULL; *cmdlist = NULL; rval = -1; p = 0; for (;;) { ch = cmd_string_getc(s, &p); switch (ch) { case '\'': if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) goto error; buf = xrealloc(buf, 1, len + strlen(t) + 1); strlcpy(buf + len, t, strlen(t) + 1); len += strlen(t); xfree(t); break; case '"': if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) goto error; buf = xrealloc(buf, 1, len + strlen(t) + 1); strlcpy(buf + len, t, strlen(t) + 1); len += strlen(t); xfree(t); break; case '$': if ((t = cmd_string_variable(s, &p)) == NULL) goto error; buf = xrealloc(buf, 1, len + strlen(t) + 1); strlcpy(buf + len, t, strlen(t) + 1); len += strlen(t); xfree(t); break; case '#': /* Comment: discard rest of line. */ while ((ch = cmd_string_getc(s, &p)) != EOF) ; /* FALLTHROUGH */ case EOF: case ' ': case '\t': if (buf != NULL) { buf = xrealloc(buf, 1, len + 1); buf[len] = '\0'; argv = xrealloc(argv, argc + 1, sizeof *argv); argv[argc++] = buf; buf = NULL; len = 0; } if (ch != EOF) break; while (argc != 0) { equals = strchr(argv[0], '='); whitespace = argv[0] + strcspn(argv[0], " \t"); if (equals == NULL || equals > whitespace) break; environ_put(&global_environ, argv[0]); argc--; memmove(argv, argv + 1, argc * (sizeof *argv)); } if (argc == 0) goto out; *cmdlist = cmd_list_parse(argc, argv, cause); if (*cmdlist == NULL) goto out; rval = 0; goto out; case '~': if (buf == NULL) { if ((t = cmd_string_expand_tilde(s, &p)) == NULL) goto error; buf = xrealloc(buf, 1, len + strlen(t) + 1); strlcpy(buf + len, t, strlen(t) + 1); len += strlen(t); xfree(t); break; } /* FALLTHROUGH */ default: if (len >= SIZE_MAX - 2) goto error; buf = xrealloc(buf, 1, len + 1); buf[len++] = ch; break; } } error: xasprintf(cause, "invalid or unknown command: %s", s); out: if (buf != NULL) xfree(buf); if (argv != NULL) { for (i = 0; i < argc; i++) xfree(argv[i]); xfree(argv); } return (rval); }
int cmd_string_split(const char *s, int *rargc, char ***rargv) { size_t p = 0; int ch, argc = 0, append = 0; char **argv = NULL, *buf = NULL, *t; const char *whitespace, *equals; size_t len = 0; for (;;) { ch = cmd_string_getc(s, &p); switch (ch) { case '\'': if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) goto error; cmd_string_copy(&buf, t, &len); break; case '"': if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) goto error; cmd_string_copy(&buf, t, &len); break; case '$': if ((t = cmd_string_variable(s, &p)) == NULL) goto error; cmd_string_copy(&buf, t, &len); break; case '#': /* Comment: discard rest of line. */ while ((ch = cmd_string_getc(s, &p)) != EOF) ; /* FALLTHROUGH */ case EOF: case ' ': case '\t': if (buf != NULL) { buf = xrealloc(buf, len + 1); buf[len] = '\0'; argv = xreallocarray(argv, argc + 1, sizeof *argv); argv[argc++] = buf; buf = NULL; len = 0; } if (ch != EOF) break; while (argc != 0) { equals = strchr(argv[0], '='); whitespace = argv[0] + strcspn(argv[0], " \t"); if (equals == NULL || equals > whitespace) break; environ_put(global_environ, argv[0]); argc--; memmove(argv, argv + 1, argc * (sizeof *argv)); } goto done; case '~': if (buf != NULL) { append = 1; break; } t = cmd_string_expand_tilde(s, &p); if (t == NULL) goto error; cmd_string_copy(&buf, t, &len); break; default: append = 1; break; } if (append) { if (len >= SIZE_MAX - 2) goto error; buf = xrealloc(buf, len + 1); buf[len++] = ch; } append = 0; } done: *rargc = argc; *rargv = argv; free(buf); return (0); error: if (argv != NULL) cmd_free_argv(argc, argv); free(buf); return (-1); }