[PATCH 2/2] sftp: Be a bit more clever about completions

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

 



There are commands (e.g. "get" or "put") that accept two
arguments, a local path and a remote path. However, the way
current completion is written doesn't take this distinction into
account and always completes remote or local paths.

By expanding CMD struct and "cmds" array this distinction can be
reflected and with small adjustment to completer code the correct
path can be completed.

Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx>
---
 sftp.c | 107 ++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 64 insertions(+), 43 deletions(-)

diff --git a/sftp.c b/sftp.c
index 1c529263..f234fba9 100644
--- a/sftp.c
+++ b/sftp.c
@@ -165,7 +165,8 @@ enum sftp_command {
 struct CMD {
 	const char *c;
 	const int n;
-	const int t;
+	const int t;	/* Type of completion for the first argument */
+	const int t2;	/* Type of completion for the optional second argument */
 };
 
 /* Type of completion */
@@ -174,42 +175,42 @@ struct CMD {
 #define LOCAL	2
 
 static const struct CMD cmds[] = {
-	{ "bye",	I_QUIT,		NOARGS	},
-	{ "cd",		I_CHDIR,	REMOTE	},
-	{ "chdir",	I_CHDIR,	REMOTE	},
-	{ "chgrp",	I_CHGRP,	REMOTE	},
-	{ "chmod",	I_CHMOD,	REMOTE	},
-	{ "chown",	I_CHOWN,	REMOTE	},
-	{ "df",		I_DF,		REMOTE	},
-	{ "dir",	I_LS,		REMOTE	},
-	{ "exit",	I_QUIT,		NOARGS	},
-	{ "get",	I_GET,		REMOTE	},
-	{ "help",	I_HELP,		NOARGS	},
-	{ "lcd",	I_LCHDIR,	LOCAL	},
-	{ "lchdir",	I_LCHDIR,	LOCAL	},
-	{ "lls",	I_LLS,		LOCAL	},
-	{ "lmkdir",	I_LMKDIR,	LOCAL	},
-	{ "ln",		I_LINK,		REMOTE	},
-	{ "lpwd",	I_LPWD,		LOCAL	},
-	{ "ls",		I_LS,		REMOTE	},
-	{ "lumask",	I_LUMASK,	NOARGS	},
-	{ "mkdir",	I_MKDIR,	REMOTE	},
-	{ "mget",	I_GET,		REMOTE	},
-	{ "mput",	I_PUT,		LOCAL	},
-	{ "progress",	I_PROGRESS,	NOARGS	},
-	{ "put",	I_PUT,		LOCAL	},
-	{ "pwd",	I_PWD,		REMOTE	},
-	{ "quit",	I_QUIT,		NOARGS	},
-	{ "reget",	I_REGET,	REMOTE	},
-	{ "rename",	I_RENAME,	REMOTE	},
-	{ "reput",	I_REPUT,	LOCAL	},
-	{ "rm",		I_RM,		REMOTE	},
-	{ "rmdir",	I_RMDIR,	REMOTE	},
-	{ "symlink",	I_SYMLINK,	REMOTE	},
-	{ "version",	I_VERSION,	NOARGS	},
-	{ "!",		I_SHELL,	NOARGS	},
-	{ "?",		I_HELP,		NOARGS	},
-	{ NULL,		-1,		-1	}
+	{ "bye",	I_QUIT,		NOARGS, 	NOARGS	},
+	{ "cd",		I_CHDIR,	REMOTE, 	NOARGS	},
+	{ "chdir",	I_CHDIR,	REMOTE, 	NOARGS	},
+	{ "chgrp",	I_CHGRP,	REMOTE, 	NOARGS	},
+	{ "chmod",	I_CHMOD,	REMOTE, 	NOARGS	},
+	{ "chown",	I_CHOWN,	REMOTE, 	NOARGS	},
+	{ "df",		I_DF,		REMOTE, 	NOARGS	},
+	{ "dir",	I_LS,		REMOTE, 	NOARGS	},
+	{ "exit",	I_QUIT,		NOARGS, 	NOARGS	},
+	{ "get",	I_GET,		REMOTE, 	LOCAL	},
+	{ "help",	I_HELP,		NOARGS, 	NOARGS	},
+	{ "lcd",	I_LCHDIR,	LOCAL,		NOARGS	},
+	{ "lchdir",	I_LCHDIR,	LOCAL,		NOARGS	},
+	{ "lls",	I_LLS,		LOCAL,		NOARGS	},
+	{ "lmkdir",	I_LMKDIR,	LOCAL,		NOARGS	},
+	{ "ln",		I_LINK,		REMOTE, 	REMOTE	},
+	{ "lpwd",	I_LPWD,		LOCAL,		NOARGS	},
+	{ "ls",		I_LS,		REMOTE,		NOARGS	},
+	{ "lumask",	I_LUMASK,	NOARGS,		NOARGS	},
+	{ "mkdir",	I_MKDIR,	REMOTE,		NOARGS	},
+	{ "mget",	I_GET,		REMOTE,		LOCAL	},
+	{ "mput",	I_PUT,		LOCAL,		REMOTE	},
+	{ "progress",	I_PROGRESS,	NOARGS,		NOARGS	},
+	{ "put",	I_PUT,		LOCAL,		REMOTE	},
+	{ "pwd",	I_PWD,		REMOTE, 	NOARGS	},
+	{ "quit",	I_QUIT,		NOARGS, 	NOARGS	},
+	{ "reget",	I_REGET,	REMOTE, 	LOCAL	},
+	{ "rename",	I_RENAME,	REMOTE, 	REMOTE	},
+	{ "reput",	I_REPUT,	LOCAL,		REMOTE	},
+	{ "rm",		I_RM,		REMOTE,		NOARGS	},
+	{ "rmdir",	I_RMDIR,	REMOTE,		NOARGS	},
+	{ "symlink",	I_SYMLINK,	REMOTE,		REMOTE	},
+	{ "version",	I_VERSION,	NOARGS, 	NOARGS	},
+	{ "!",		I_SHELL,	NOARGS, 	NOARGS	},
+	{ "?",		I_HELP,		NOARGS, 	NOARGS	},
+	{ NULL,		-1,		-1,		-1	}
 };
 
 /* ARGSUSED */
@@ -1904,19 +1905,25 @@ complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
 }
 
 /*
- * Determine whether a particular sftp command's arguments (if any)
- * represent local or remote files.
+ * Determine whether a particular sftp command's arguments (if any) represent
+ * local or remote files. The "cmdarg" argument specifies the actual argument
+ * and accepts values 1 or 2.
  */
 static int
-complete_is_remote(char *cmd) {
+complete_is_remote(char *cmd, int cmdarg) {
 	int i;
 
 	if (cmd == NULL)
 		return -1;
 
 	for (i = 0; cmds[i].c; i++) {
-		if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
-			return cmds[i].t;
+		if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
+			if (cmdarg == 1)
+				return cmds[i].t;
+			else if (cmdarg == 2)
+				return cmds[i].t2;
+			break;
+		}
 	}
 
 	return -1;
@@ -2096,12 +2103,26 @@ complete(EditLine *el, int ch)
 			ret = CC_REDISPLAY;
 	} else if (carg >= 1) {
 		/* Handle file parsing */
-		int remote = complete_is_remote(argv[0]);
+		int remote = 0;
+		int i = 0, cmdarg = 0;
 		char *filematch = NULL;
 
 		if (carg > 1 && line[cursor-1] != ' ')
 			filematch = argv[carg - 1];
 
+		for (i = 1; i < carg; i++) {
+			/* Skip flags */
+			if (argv[i][0] != '-')
+				cmdarg++;
+		}
+
+		/* If previous argument is complete, then offer completion on the next
+		 * one. */
+		if (line[cursor - 1] == ' ')
+			cmdarg++;
+
+		remote = complete_is_remote(argv[0], cmdarg);
+
 		if ((remote == REMOTE || remote == LOCAL) &&
 		    complete_match(el, complete_ctx->conn,
 		    *complete_ctx->remote_pathp, filematch,
-- 
2.34.1

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@xxxxxxxxxxx
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev



[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux