Commit Diff


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