diff --git a/main.c b/main.c index 5c9392f8..7e4cd1e9 100644 --- a/main.c +++ b/main.c @@ -548,7 +548,7 @@ main(int argc, char *const *argv) case 0: /* Child */ - slave_spawn(term.ptmx); + slave_spawn(term.ptmx, conf.shell); assert(false); break; diff --git a/slave.c b/slave.c index 676d4ce2..1bebc012 100644 --- a/slave.c +++ b/slave.c @@ -12,12 +12,95 @@ #define LOG_ENABLE_DBG 0 #include "log.h" +static bool +push_argv(char ***argv, size_t *size, char *arg, size_t *argc) +{ + if (arg != NULL && arg[0] == '%') + return true; + + if (*argc >= *size) { + size_t new_size = *size > 0 ? 2 * *size : 10; + char **new_argv = realloc(*argv, new_size * sizeof(new_argv[0])); + + if (new_argv == NULL) + return false; + + *argv = new_argv; + *size = new_size; + } + + (*argv)[(*argc)++] = arg; + return true; +} + +static bool +tokenize_cmdline(char *cmdline, char ***argv) +{ + *argv = NULL; + size_t argv_size = 0; + + bool first_token_is_quoted = cmdline[0] == '"' || cmdline[0] == '\''; + char delim = first_token_is_quoted ? cmdline[0] : ' '; + + char *p = first_token_is_quoted ? &cmdline[1] : &cmdline[0]; + + size_t idx = 0; + while (*p != '\0') { + char *end = strchr(p, delim); + if (end == NULL) { + if (delim != ' ') { + LOG_ERR("unterminated %s quote\n", delim == '"' ? "double" : "single"); + free(*argv); + return false; + } + + if (!push_argv(argv, &argv_size, p, &idx) || + !push_argv(argv, &argv_size, NULL, &idx)) + { + goto err; + } else + return true; + } + + *end = '\0'; + + if (!push_argv(argv, &argv_size, p, &idx)) + goto err; + + p = end + 1; + while (*p == delim) + p++; + + while (*p == ' ') + p++; + + if (*p == '"' || *p == '\'') { + delim = *p; + p++; + } else + delim = ' '; + } + + if (!push_argv(argv, &argv_size, NULL, &idx)) + goto err; + + return true; + +err: + free(*argv); + return false; +} + void -slave_spawn(int ptmx) +slave_spawn(int ptmx, char *cmd) { int pts = -1; const char *pts_name = ptsname(ptmx); + char **argv = NULL; + if (!tokenize_cmdline(cmd, &argv)) + goto err; + if (grantpt(ptmx) == -1) { LOG_ERRNO("failed to grantpt()"); goto err; @@ -52,9 +135,11 @@ slave_spawn(int ptmx) close(pts); pts = -1; - execl("/usr/bin/zsh", "/usr/bin/zsh", NULL); + execvp(argv[0], argv); err: + if (argv) + free(argv); if (pts != -1) close(pts); if (ptmx != -1) diff --git a/slave.h b/slave.h index 22ef14f3..05e25832 100644 --- a/slave.h +++ b/slave.h @@ -1,4 +1,4 @@ #pragma once #include -void slave_spawn(int ptmx); +void slave_spawn(int ptmx, char *cmd);