commit 55e8c065589748eb51bc37751156a74c96051ed6 from: Omar Polo date: Wed Nov 23 21:08:45 2022 UTC kamiftp: implement shell-like word splitting way more useful than a dumb strsep(" ") commit - 9c9e60d1c17cfe7ba0561ee4c2561b67bf9d455e commit + 55e8c065589748eb51bc37751156a74c96051ed6 blob - f440e1a8a3df489a8069f6beaf66b4148d42ebcf blob + 1f3fe3b330c9607e0c96b8ed5b6eb5766cbbdda7 --- kamiftp/ftp.c +++ kamiftp/ftp.c @@ -1603,6 +1603,70 @@ excmd(int argc, const char **argv) log_warnx("unknown command %s", *argv); } +static int +parsecmd(char *cmd, char **argv, size_t len) +{ + int escape, quote; + int argc = 0; + + memset(argv, 0, sizeof(*argv) * len); + + while (argc < len) { + while (isspace((unsigned char)*cmd)) + cmd++; + if (*cmd == '\0') + break; + + argv[argc++] = cmd; + escape = quote = 0; + for (; *cmd != '\0'; ++cmd) { + if (escape) { + escape = 0; + continue; + } + if (*cmd == '\\') { + escape = 1; + memmove(cmd, cmd + 1, strlen(cmd)); + cmd--; + continue; + } + if (*cmd == quote) { + quote = 0; + memmove(cmd, cmd + 1, strlen(cmd)); + cmd--; + continue; + } + if (*cmd == '\'' || *cmd == '"') { + quote = *cmd; + memmove(cmd, cmd + 1, strlen(cmd)); + cmd--; + continue; + } + if (quote) + continue; + + if (isspace((unsigned char)*cmd)) + break; + } + + if (*cmd == '\0' && (escape || quote)) { + fprintf(stderr, "unterminated %s\n", + escape ? "escape" : "quote"); + return -1; + } + + if (*cmd == '\0') + break; + *cmd++ = '\0'; + } + + if (*cmd != '\0') { + fprintf(stderr, "too many arguments\n"); + return -1; + } + return argc; +} + static void cd_or_fetch(const char *path, const char *outfile) { @@ -1762,17 +1826,18 @@ main(int argc, char **argv) cd_or_fetch(path, outfile); for (;;) { - int argc = 0; + int argc; char *line, *argv[16] = {0}, **ap; if ((line = read_line("kamiftp> ")) == NULL) break; - for (argc = 0, ap = argv; ap < &argv[15] && - (*ap = strsep(&line, " \t")) != NULL;) { - if (**ap != '\0') - ap++, argc++; + if ((argc = parsecmd(line, argv, nitems(argv) - 1)) == -1) { + free(line); + continue; } + + argv[argc] = NULL; excmd(argc, (const char **)argv); if (bell) blob - c22fcc820b2e07e26fd86cddbbfb5c72d61925e3 blob + 7a2e955493ea2859b0cc85000c46269522c2afbe --- kamiftp/kamiftp.1 +++ kamiftp/kamiftp.1 @@ -70,6 +70,26 @@ or when names a directory. .El .Pp +.Nm +parses the input similarly to +.Xr sh . +Words are splitted on spaces, multiple spaces are collapsed into one. +Quoting with +.Sq ' +or +.Sq \&" +can be used to preserve spaces and other quotes. +.Sq \e +can be used to quote the following character. +No special meaning is given to +.Sq \e +sequences +.Po i.e.\& Sq \en +is just the +.Sq n +character +.Pc . +.Pp The following commands are recognized by .Nm : .Bl -tag -width Ds