From 3af9746312c67c3d5d4c4aac7e60ab4bec80ae5d Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 16 Dec 2001 13:33:04 +0000 Subject: [PATCH] Add the `local' command set to PSFTP: lcd, lpwd, and ! to spawn a Windows command. git-svn-id: svn://svn.tartarus.org/sgt/putty@1501 cda61777-01e9-0310-a592-d414129be87e --- doc/psftp.but | 73 +++++++++++++++++-- psftp.c | 231 +++++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 233 insertions(+), 71 deletions(-) diff --git a/doc/psftp.but b/doc/psftp.but index 9d63a3e7..57ba0fa5 100644 --- a/doc/psftp.but +++ b/doc/psftp.but @@ -1,4 +1,4 @@ -\versionid $Id: psftp.but,v 1.2 2001/12/14 12:22:09 simon Exp $ +\versionid $Id: psftp.but,v 1.3 2001/12/16 13:33:04 simon Exp $ \C{psftp} Using PSFTP to transfer files securely @@ -159,6 +159,40 @@ Once you have started your PSFTP session, you will see a \c{psftp>} prompt. You can now type commands to perform file-transfer functions. This section lists all the available commands. +\S{psftp-quoting} General quoting rules for PSFTP commands + +Most PSFTP commands are considered by the PSFTP command interpreter +as a sequence of words, separated by spaces. For example, the +command \c{ren oldfilename newfilename} splits up into three words: +\c{ren} (the command name), \c{oldfilename} (the name of the file to +be renamed), and \c{newfilename} (the new name to give the file). + +Sometimes you will need to specify file names that \e{contain} +spaces. In order to do this, you can surround the file name with +double quotes. This works equally well for local file names and +remote file names: + +\c psftp> get "spacey file name.txt" "save it under this name.txt" + +The double quotes themselves will not appear as part of the file +names; they are removed by PSFTP and their only effect is to stop +the spaces inside them from acting as word separators. + +If you need to \e{use} a double quote (on some types of remote +system, such as Unix, you are allowed to use double quotes in file +names), you can do this by doubling it. This works both inside and +outside double quotes. For example, this command + +\c psftp> ren ""this"" "a file with ""quotes"" in it" + +will take a file whose current name is \c{"this"} (with a double +quote character at the beginning and the end) and rename it to a +file whose name is \c{a file with "quotes" in it}. + +(The one exception to the PSFTP quoting rules is the \c{!} command, +which passes its command line straight to Windows without splitting +it up into words at all. See \k{psftp-cmd-pling}.) + \S{psftp-cmd-open} The \c{open} command: start a session If you started PSFTP by double-clicking in the GUI, or just by @@ -198,11 +232,24 @@ remote working directory PSFTP maintains a notion of your \q{working directory} on the server. This is the default directory that other commands will operate on. For example, if you type \c{get filename.dat} then PSFTP -will look for \c{filename.dat} in your working directory on the -server. +will look for \c{filename.dat} in your remote working directory on +the server. -To change your working directory, use the \c{cd} command. To display -your current working directory, type \c{pwd}. +To change your remote working directory, use the \c{cd} command. To +display your current remote working directory, type \c{pwd}. + +\S{psftp-cmd-lcd} The \c{lcd} and \c{lpwd} commands: changing the +local working directory + +As well as having a working directory on the remote server, PSFTP +also has a working directory on your local machine (just like any +other Windows process). This is the default local directory that +other commands will operate on. For example, if you type \c{get +filename.dat} then PSFTP will save the resulting file as +\c{filename.dat} in your local working directory. + +To change your local working directory, use the \c{lcd} command. To +display your current local working directory, type \c{lpwd}. \S{psftp-cmd-get} The \c{get} command: fetch a file from the server @@ -363,6 +410,22 @@ name, and then the new file name: The \c{rename} and \c{mv} commands work exactly the same way as \c{ren}. +\S{psftp-cmd-pling} The \c{!} command: run a local Windows command + +You can run local Windows commands using the \c{!} command. This is +the only PSFTP command that is not subject to the command quoting +rules given in \k{psftp-quoting}. If any command line begins with +the \c{!} character, then the rest of the line will be passed +straight to Windows without further translation. + +For example, if you want to move an existing copy of a file out of +the way before downloading an updated version, you might type: + +\c psftp> !ren myfile.dat myfile.bak +\c psftp> get myfile.dat + +using the Windows \c{ren} command to rename files on your local PC. + \H{psftp-pubkey} Using public key authentication with PSFTP Like PuTTY, PSFTP can authenticate using a public key instead of a diff --git a/psftp.c b/psftp.c index 2923e732..ba740cea 100644 --- a/psftp.c +++ b/psftp.c @@ -904,6 +904,66 @@ static int sftp_cmd_open(struct sftp_command *cmd) return 1; } +static int sftp_cmd_lcd(struct sftp_command *cmd) +{ + char *currdir; + int len; + + if (cmd->nwords < 2) { + printf("lcd: expects a local directory name\n"); + return 0; + } + + if (!SetCurrentDirectory(cmd->words[1])) { + LPVOID message; + int i; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&message, 0, NULL); + i = strcspn((char *)message, "\n"); + printf("lcd: unable to change directory: %.*s\n", i, (LPCTSTR)message); + LocalFree(message); + return 0; + } + + currdir = smalloc(256); + len = GetCurrentDirectory(256, currdir); + if (len > 256) + currdir = srealloc(currdir, len); + GetCurrentDirectory(len, currdir); + printf("New local directory is %s\n", currdir); + sfree(currdir); + + return 1; +} + +static int sftp_cmd_lpwd(struct sftp_command *cmd) +{ + char *currdir; + int len; + + currdir = smalloc(256); + len = GetCurrentDirectory(256, currdir); + if (len > 256) + currdir = srealloc(currdir, len); + GetCurrentDirectory(len, currdir); + printf("Current local directory is %s\n", currdir); + sfree(currdir); + + return 1; +} + +static int sftp_cmd_pling(struct sftp_command *cmd) +{ + int exitcode; + + exitcode = system(cmd->words[1]); + return (exitcode == 0); +} + static int sftp_cmd_help(struct sftp_command *cmd); static struct sftp_cmd_lookup { @@ -920,6 +980,7 @@ static struct sftp_cmd_lookup { * `shorthelp' is the name of a primary command, which * contains the help that should double up for this command. */ + int listed; /* do we list this in primary help? */ char *shorthelp; char *longhelp; int (*obey) (struct sftp_command *); @@ -929,13 +990,19 @@ static struct sftp_cmd_lookup { * in ASCII order. */ { - "bye", "finish your SFTP session", + "!", TRUE, "run a local Windows command", + "\n" + " Runs a local Windows command. For example, \"!del myfile\".\n", + sftp_cmd_pling + }, + { + "bye", TRUE, "finish your SFTP session", "\n" " Terminates your SFTP session and quits the PSFTP program.\n", sftp_cmd_quit }, { - "cd", "change your remote working directory", + "cd", TRUE, "change your remote working directory", " [ ]\n" " Change the remote working directory for your SFTP session.\n" " If a new working directory is not supplied, you will be\n" @@ -943,7 +1010,7 @@ static struct sftp_cmd_lookup { sftp_cmd_cd }, { - "chmod", "change file permissions and modes", + "chmod", TRUE, "change file permissions and modes", " ( | ) \n" " Change the file permissions on a file or directory.\n" " can be any octal Unix permission specifier.\n" @@ -970,19 +1037,16 @@ static struct sftp_cmd_lookup { sftp_cmd_chmod }, { - "del", "delete a file", + "del", TRUE, "delete a file", " \n" " Delete a file.\n", sftp_cmd_rm }, { - "delete", "delete a file", - "\n" - " Delete a file.\n", - sftp_cmd_rm + "delete", FALSE, "del", NULL, sftp_cmd_rm }, { - "dir", "list contents of a remote directory", + "dir", TRUE, "list contents of a remote directory", " [ ]\n" " List the contents of a specified directory on the server.\n" " If is not given, the current working directory\n" @@ -990,10 +1054,10 @@ static struct sftp_cmd_lookup { sftp_cmd_ls }, { - "exit", "bye", NULL, sftp_cmd_quit + "exit", TRUE, "bye", NULL, sftp_cmd_quit }, { - "get", "download a file from the server to your local machine", + "get", TRUE, "download a file from the server to your local machine", " [ ]\n" " Downloads a file on the server and stores it locally under\n" " the same name, or under a different one if you supply the\n" @@ -1001,7 +1065,7 @@ static struct sftp_cmd_lookup { sftp_cmd_get }, { - "help", "give help", + "help", TRUE, "give help", " [ [ ... ] ]\n" " Give general help if no commands are specified.\n" " If one or more commands are specified, give specific help on\n" @@ -1009,24 +1073,38 @@ static struct sftp_cmd_lookup { sftp_cmd_help }, { - "ls", "dir", NULL, + "lcd", TRUE, "change local working directory", + " \n" + " Change the local working directory of the PSFTP program (the\n" + " default location where the \"get\" command will save files).\n", + sftp_cmd_lcd + }, + { + "lpwd", TRUE, "print local working directory", + "\n" + " Print the local working directory of the PSFTP program (the\n" + " default location where the \"get\" command will save files).\n", + sftp_cmd_lpwd + }, + { + "ls", TRUE, "dir", NULL, sftp_cmd_ls }, { - "mkdir", "create a directory on the remote server", + "mkdir", TRUE, "create a directory on the remote server", " \n" " Creates a directory with the given name on the server.\n", sftp_cmd_mkdir }, { - "mv", "move or rename a file on the remote server", + "mv", TRUE, "move or rename a file on the remote server", " \n" " Moves or renames the file on the server,\n" " so that it is accessible under the name .\n", sftp_cmd_mv }, { - "put", "upload a file from your local machine to the server", + "put", TRUE, "upload a file from your local machine to the server", " [ ]\n" " Uploads a file to the server and stores it there under\n" " the same name, or under a different one if you supply the\n" @@ -1034,7 +1112,7 @@ static struct sftp_cmd_lookup { sftp_cmd_put }, { - "open", "connect to a host", + "open", TRUE, "connect to a host", " [@]\n" " Establishes an SFTP connection to a given host. Only usable\n" " when you did not already specify a host name on the command\n" @@ -1042,17 +1120,17 @@ static struct sftp_cmd_lookup { sftp_cmd_open }, { - "pwd", "print your remote working directory", + "pwd", TRUE, "print your remote working directory", "\n" " Print the current remote working directory for your SFTP session.\n", sftp_cmd_pwd }, { - "quit", "bye", NULL, + "quit", TRUE, "bye", NULL, sftp_cmd_quit }, { - "reget", "continue downloading a file", + "reget", TRUE, "continue downloading a file", " [ ]\n" " Works exactly like the \"get\" command, but the local file\n" " must already exist. The download will begin at the end of the\n" @@ -1060,15 +1138,15 @@ static struct sftp_cmd_lookup { sftp_cmd_reget }, { - "ren", "mv", NULL, + "ren", TRUE, "mv", NULL, sftp_cmd_mv }, { - "rename", "mv", NULL, + "rename", FALSE, "mv", NULL, sftp_cmd_mv }, { - "reput", "continue uploading a file", + "reput", TRUE, "continue uploading a file", " [ ]\n" " Works exactly like the \"put\" command, but the remote file\n" " must already exist. The upload will begin at the end of the\n" @@ -1076,11 +1154,11 @@ static struct sftp_cmd_lookup { sftp_cmd_reput }, { - "rm", "del", NULL, + "rm", TRUE, "del", NULL, sftp_cmd_rm }, { - "rmdir", "remove a directory on the remote server", + "rmdir", TRUE, "remove a directory on the remote server", " \n" " Removes the directory with the given name on the server.\n" " The directory will not be removed unless it is empty.\n", @@ -1118,12 +1196,17 @@ static int sftp_cmd_help(struct sftp_command *cmd) int maxlen; maxlen = 0; for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) { - int len = strlen(sftp_lookup[i].name); + int len; + if (!sftp_lookup[i].listed) + continue; + len = strlen(sftp_lookup[i].name); if (maxlen < len) maxlen = len; } for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) { const struct sftp_cmd_lookup *lookup; + if (!sftp_lookup[i].listed) + continue; lookup = &sftp_lookup[i]; printf("%-*s", maxlen+2, lookup->name); if (lookup->longhelp == NULL) @@ -1199,49 +1282,65 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) printf("%s\n", line); } - /* - * Parse the command line into words. The syntax is: - * - double quotes are removed, but cause spaces within to be - * treated as non-separating. - * - a double-doublequote pair is a literal double quote, inside - * _or_ outside quotes. Like this: - * - * firstword "second word" "this has ""quotes"" in" sodoes""this"" - * - * becomes - * - * >firstword< - * >second word< - * >this has "quotes" in< - * >sodoes"this"< - */ p = line; - while (*p) { - /* skip whitespace */ - while (*p && (*p == ' ' || *p == '\t')) - p++; - /* mark start of word */ - q = r = p; /* q sits at start, r writes word */ - quoting = 0; + while (*p && (*p == ' ' || *p == '\t')) + p++; + + if (*p == '!') { + /* + * Special case: the ! command. This is always parsed as + * exactly two words: one containing the !, and the second + * containing everything else on the line. + */ + cmd->nwords = cmd->wordssize = 2; + cmd->words = srealloc(cmd->words, cmd->wordssize * sizeof(char *)); + cmd->words[0] = "!"; + cmd->words[1] = p+1; + } else { + + /* + * Parse the command line into words. The syntax is: + * - double quotes are removed, but cause spaces within to be + * treated as non-separating. + * - a double-doublequote pair is a literal double quote, inside + * _or_ outside quotes. Like this: + * + * firstword "second word" "this has ""quotes"" in" and""this"" + * + * becomes + * + * >firstword< + * >second word< + * >this has "quotes" in< + * >and"this"< + */ while (*p) { - if (!quoting && (*p == ' ' || *p == '\t')) - break; /* reached end of word */ - else if (*p == '"' && p[1] == '"') - p += 2, *r++ = '"'; /* a literal quote */ - else if (*p == '"') - p++, quoting = !quoting; - else - *r++ = *p++; - } - if (*p) - p++; /* skip over the whitespace */ - *r = '\0'; - if (cmd->nwords >= cmd->wordssize) { - cmd->wordssize = cmd->nwords + 16; - cmd->words = - srealloc(cmd->words, cmd->wordssize * sizeof(char *)); + /* skip whitespace */ + while (*p && (*p == ' ' || *p == '\t')) + p++; + /* mark start of word */ + q = r = p; /* q sits at start, r writes word */ + quoting = 0; + while (*p) { + if (!quoting && (*p == ' ' || *p == '\t')) + break; /* reached end of word */ + else if (*p == '"' && p[1] == '"') + p += 2, *r++ = '"'; /* a literal quote */ + else if (*p == '"') + p++, quoting = !quoting; + else + *r++ = *p++; + } + if (*p) + p++; /* skip over the whitespace */ + *r = '\0'; + if (cmd->nwords >= cmd->wordssize) { + cmd->wordssize = cmd->nwords + 16; + cmd->words = + srealloc(cmd->words, cmd->wordssize * sizeof(char *)); + } + cmd->words[cmd->nwords++] = q; } - cmd->words[cmd->nwords++] = q; } /* -- 2.11.0