[BACK]Return to sftp-int.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/Attic/sftp-int.c between version 1.22.2.2 and 1.22.2.3

version 1.22.2.2, 2001/02/19 17:19:23 version 1.22.2.3, 2001/03/21 19:46:29
Line 22 
Line 22 
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */   */
   
 /* XXX: finish implementation of all commands */  
 /* XXX: do fnmatch() instead of using raw pathname */  
 /* XXX: globbed ls */  /* XXX: globbed ls */
 /* XXX: recursive operations */  /* XXX: recursive operations */
   
 #include "includes.h"  #include "includes.h"
 RCSID("$OpenBSD$");  RCSID("$OpenBSD$");
   
   #include <glob.h>
   
 #include "buffer.h"  #include "buffer.h"
 #include "xmalloc.h"  #include "xmalloc.h"
 #include "log.h"  #include "log.h"
Line 37 
Line 37 
   
 #include "sftp.h"  #include "sftp.h"
 #include "sftp-common.h"  #include "sftp-common.h"
   #include "sftp-glob.h"
 #include "sftp-client.h"  #include "sftp-client.h"
 #include "sftp-int.h"  #include "sftp-int.h"
   
   /* File to read commands from */
   extern FILE *infile;
   
   /* Version of server we are speaking to */
   int version;
   
 /* Seperators for interactive commands */  /* Seperators for interactive commands */
 #define WHITESPACE " \t\r\n"  #define WHITESPACE " \t\r\n"
   
Line 64 
Line 71 
 #define I_RM            18  #define I_RM            18
 #define I_RMDIR         19  #define I_RMDIR         19
 #define I_SHELL         20  #define I_SHELL         20
   #define I_SYMLINK       21
   #define I_VERSION       22
   
 struct CMD {  struct CMD {
         const char *c;          const char *c;
Line 84 
Line 93 
         { "lchdir",     I_LCHDIR },          { "lchdir",     I_LCHDIR },
         { "lls",        I_LLS },          { "lls",        I_LLS },
         { "lmkdir",     I_LMKDIR },          { "lmkdir",     I_LMKDIR },
           { "ln",         I_SYMLINK },
         { "lpwd",       I_LPWD },          { "lpwd",       I_LPWD },
         { "ls",         I_LS },          { "ls",         I_LS },
         { "lumask",     I_LUMASK },          { "lumask",     I_LUMASK },
Line 94 
Line 104 
         { "rename",     I_RENAME },          { "rename",     I_RENAME },
         { "rm",         I_RM },          { "rm",         I_RM },
         { "rmdir",      I_RMDIR },          { "rmdir",      I_RMDIR },
           { "symlink",    I_SYMLINK },
           { "version",    I_VERSION },
         { "!",          I_SHELL },          { "!",          I_SHELL },
         { "?",          I_HELP },          { "?",          I_HELP },
         { NULL,                 -1}          { NULL,                 -1}
Line 111 
Line 123 
         printf("help                          Display this help text\n");          printf("help                          Display this help text\n");
         printf("get remote-path [local-path]  Download file\n");          printf("get remote-path [local-path]  Download file\n");
         printf("lls [ls-options [path]]       Display local directory listing\n");          printf("lls [ls-options [path]]       Display local directory listing\n");
           printf("ln oldpath newpath            Symlink remote file\n");
         printf("lmkdir path                   Create local directory\n");          printf("lmkdir path                   Create local directory\n");
         printf("lpwd                          Print local working directory\n");          printf("lpwd                          Print local working directory\n");
         printf("ls [path]                     Display remote directory listing\n");          printf("ls [path]                     Display remote directory listing\n");
Line 123 
Line 136 
         printf("rename oldpath newpath        Rename remote file\n");          printf("rename oldpath newpath        Rename remote file\n");
         printf("rmdir path                    Remove remote directory\n");          printf("rmdir path                    Remove remote directory\n");
         printf("rm path                       Delete remote file\n");          printf("rm path                       Delete remote file\n");
           printf("symlink oldpath newpath       Symlink remote file\n");
           printf("version                       Show SFTP version\n");
         printf("!command                      Execute 'command' in local shell\n");          printf("!command                      Execute 'command' in local shell\n");
         printf("!                             Escape to local shell\n");          printf("!                             Escape to local shell\n");
         printf("?                             Synonym for help\n");          printf("?                             Synonym for help\n");
Line 182 
Line 197 
 }  }
   
 char *  char *
   path_append(char *p1, char *p2)
   {
           char *ret;
           int len = strlen(p1) + strlen(p2) + 2;
   
           ret = xmalloc(len);
           strlcpy(ret, p1, len);
           strlcat(ret, "/", len);
           strlcat(ret, p2, len);
   
           return(ret);
   }
   
   char *
 make_absolute(char *p, char *pwd)  make_absolute(char *p, char *pwd)
 {  {
         char buf[2048];          char *abs;
   
         /* Derelativise */          /* Derelativise */
         if (p && p[0] != '/') {          if (p && p[0] != '/') {
                 snprintf(buf, sizeof(buf), "%s/%s", pwd, p);                  abs = path_append(pwd, p);
                 xfree(p);                  xfree(p);
                 p = xstrdup(buf);                  return(abs);
           } else
                   return(p);
   }
   
   int
   infer_path(const char *p, char **ifp)
   {
           char *cp;
   
           cp = strrchr(p, '/');
           if (cp == NULL) {
                   *ifp = xstrdup(p);
                   return(0);
         }          }
   
         return(p);          if (!cp[1]) {
                   error("Invalid path");
                   return(-1);
           }
   
           *ifp = xstrdup(cp + 1);
           return(0);
 }  }
   
 int  int
Line 236 
Line 284 
         /* Check for quoted filenames */          /* Check for quoted filenames */
         if (*cp == '\"' || *cp == '\'') {          if (*cp == '\"' || *cp == '\'') {
                 quot = *cp++;                  quot = *cp++;
   
                 end = strchr(cp, quot);                  end = strchr(cp, quot);
                 if (end == NULL) {                  if (end == NULL) {
                         error("Unterminated quote");                          error("Unterminated quote");
Line 268 
Line 316 
 }  }
   
 int  int
 infer_path(const char *p, char **ifp)  is_dir(char *path)
 {  {
         char *cp;          struct stat sb;
   
         debug("XXX: P = \"%s\"", p);          /* XXX: report errors? */
           if (stat(path, &sb) == -1)
                   return(0);
   
         cp = strrchr(p, '/');          return(sb.st_mode & S_IFDIR);
         if (cp == NULL) {  }
                 *ifp = xstrdup(p);  
   int
   remote_is_dir(int in, int out, char *path)
   {
           Attrib *a;
   
           /* XXX: report errors? */
           if ((a = do_stat(in, out, path, 1)) == NULL)
                 return(0);                  return(0);
           if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
                   return(0);
           return(a->perm & S_IFDIR);
   }
   
   int
   process_get(int in, int out, char *src, char *dst, char *pwd, int pflag)
   {
           char *abs_src = NULL;
           char *abs_dst = NULL;
           char *tmp;
           glob_t g;
           int err = 0;
           int i;
   
           abs_src = xstrdup(src);
           abs_src = make_absolute(abs_src, pwd);
   
           memset(&g, 0, sizeof(g));
           debug3("Looking up %s", abs_src);
           if (remote_glob(in, out, abs_src, 0, NULL, &g)) {
                   error("File \"%s\" not found.", abs_src);
                   err = -1;
                   goto out;
         }          }
   
         if (!cp[1]) {          /* Only one match, dst may be file, directory or unspecified */
                 error("Invalid path");          if (g.gl_pathv[0] && g.gl_matchc == 1) {
                 return(-1);                  if (dst) {
                           /* If directory specified, append filename */
                           if (is_dir(dst)) {
                                   if (infer_path(g.gl_pathv[0], &tmp)) {
                                           err = 1;
                                           goto out;
                                   }
                                   abs_dst = path_append(dst, tmp);
                                   xfree(tmp);
                           } else
                                   abs_dst = xstrdup(dst);
                   } else if (infer_path(g.gl_pathv[0], &abs_dst)) {
                           err = -1;
                           goto out;
                   }
                   printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst);
                   err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag);
                   goto out;
         }          }
   
         *ifp = xstrdup(cp + 1);          /* Multiple matches, dst may be directory or unspecified */
         return(0);          if (dst && !is_dir(dst)) {
                   error("Multiple files match, but \"%s\" is not a directory",
                       dst);
                   err = -1;
                   goto out;
           }
   
           for(i = 0; g.gl_pathv[i]; i++) {
                   if (infer_path(g.gl_pathv[i], &tmp)) {
                           err = -1;
                           goto out;
                   }
                   if (dst) {
                           abs_dst = path_append(dst, tmp);
                           xfree(tmp);
                   } else
                           abs_dst = tmp;
   
                   printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
                   if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag) == -1)
                           err = -1;
                   xfree(abs_dst);
                   abs_dst = NULL;
           }
   
   out:
           xfree(abs_src);
           if (abs_dst)
                   xfree(abs_dst);
           globfree(&g);
           return(err);
 }  }
   
 int  int
   process_put(int in, int out, char *src, char *dst, char *pwd, int pflag)
   {
           char *tmp_dst = NULL;
           char *abs_dst = NULL;
           char *tmp;
           glob_t g;
           int err = 0;
           int i;
   
           if (dst) {
                   tmp_dst = xstrdup(dst);
                   tmp_dst = make_absolute(tmp_dst, pwd);
           }
   
           memset(&g, 0, sizeof(g));
           debug3("Looking up %s", src);
           if (glob(src, 0, NULL, &g)) {
                   error("File \"%s\" not found.", src);
                   err = -1;
                   goto out;
           }
   
           /* Only one match, dst may be file, directory or unspecified */
           if (g.gl_pathv[0] && g.gl_matchc == 1) {
                   if (tmp_dst) {
                           /* If directory specified, append filename */
                           if (remote_is_dir(in, out, tmp_dst)) {
                                   if (infer_path(g.gl_pathv[0], &tmp)) {
                                           err = 1;
                                           goto out;
                                   }
                                   abs_dst = path_append(tmp_dst, tmp);
                                   xfree(tmp);
                           } else
                                   abs_dst = xstrdup(tmp_dst);
                   } else if (infer_path(g.gl_pathv[0], &abs_dst)) {
                           err = -1;
                           goto out;
                   }
                   printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst);
                   err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag);
                   goto out;
           }
   
           /* Multiple matches, dst may be directory or unspecified */
           if (tmp_dst && !remote_is_dir(in, out, tmp_dst)) {
                   error("Multiple files match, but \"%s\" is not a directory",
                       tmp_dst);
                   err = -1;
                   goto out;
           }
   
           for(i = 0; g.gl_pathv[i]; i++) {
                   if (infer_path(g.gl_pathv[i], &tmp)) {
                           err = -1;
                           goto out;
                   }
                   if (tmp_dst) {
                           abs_dst = path_append(tmp_dst, tmp);
                           xfree(tmp);
                   } else
                           abs_dst = make_absolute(tmp, pwd);
   
                   printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
                   if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag) == -1)
                           err = -1;
           }
   
   out:
           if (abs_dst)
                   xfree(abs_dst);
           if (tmp_dst)
                   xfree(tmp_dst);
           return(err);
   }
   
   int
 parse_args(const char **cpp, int *pflag, unsigned long *n_arg,  parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
     char **path1, char **path2)      char **path1, char **path2)
 {  {
Line 349 
Line 554 
                 /* Try to get second pathname (optional) */                  /* Try to get second pathname (optional) */
                 if (get_pathname(&cp, path2))                  if (get_pathname(&cp, path2))
                         return(-1);                          return(-1);
                 /* Otherwise try to guess it from first path */  
                 if (*path2 == NULL && infer_path(*path1, path2))  
                         return(-1);  
                 break;                  break;
         case I_RENAME:          case I_RENAME:
                 /* Get first pathname (mandatory) */          case I_SYMLINK:
                 if (get_pathname(&cp, path1))                  if (get_pathname(&cp, path1))
                         return(-1);                          return(-1);
                 if (get_pathname(&cp, path2))                  if (get_pathname(&cp, path2))
Line 427 
Line 629 
         case I_PWD:          case I_PWD:
         case I_LPWD:          case I_LPWD:
         case I_HELP:          case I_HELP:
           case I_VERSION:
                 break;                  break;
         default:          default:
                 fatal("Command not implemented");                  fatal("Command not implemented");
Line 440 
Line 643 
 parse_dispatch_command(int in, int out, const char *cmd, char **pwd)  parse_dispatch_command(int in, int out, const char *cmd, char **pwd)
 {  {
         char *path1, *path2, *tmp;          char *path1, *path2, *tmp;
         int pflag, cmdnum;          int pflag, cmdnum, i;
         unsigned long n_arg;          unsigned long n_arg;
         Attrib a, *aa;          Attrib a, *aa;
         char path_buf[PATH_MAX];          char path_buf[MAXPATHLEN];
           int err = 0;
           glob_t g;
   
         path1 = path2 = NULL;          path1 = path2 = NULL;
         cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2);          cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2);
   
           memset(&g, 0, sizeof(g));
   
         /* Perform command */          /* Perform command */
         switch (cmdnum) {          switch (cmdnum) {
         case -1:          case -1:
                 break;                  break;
         case I_GET:          case I_GET:
                 path1 = make_absolute(path1, *pwd);                  err = process_get(in, out, path1, path2, *pwd, pflag);
                 do_download(in, out, path1, path2, pflag);  
                 break;                  break;
         case I_PUT:          case I_PUT:
                 path2 = make_absolute(path2, *pwd);                  err = process_put(in, out, path1, path2, *pwd, pflag);
                 do_upload(in, out, path1, path2, pflag);                  break;
                 break;          case I_RENAME:
         case I_RENAME:  
                 path1 = make_absolute(path1, *pwd);                  path1 = make_absolute(path1, *pwd);
                 path2 = make_absolute(path2, *pwd);                  path2 = make_absolute(path2, *pwd);
                 do_rename(in, out, path1, path2);                  err = do_rename(in, out, path1, path2);
                 break;                  break;
           case I_SYMLINK:
                   if (version < 3) {
                           error("The server (version %d) does not support "
                               "this operation", version);
                           err = -1;
                   } else {
                           path2 = make_absolute(path2, *pwd);
                           err = do_symlink(in, out, path1, path2);
                   }
                   break;
         case I_RM:          case I_RM:
                 path1 = make_absolute(path1, *pwd);                  path1 = make_absolute(path1, *pwd);
                 do_rm(in, out, path1);                  remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                   for(i = 0; g.gl_pathv[i]; i++) {
                           printf("Removing %s\n", g.gl_pathv[i]);
                           if (do_rm(in, out, g.gl_pathv[i]) == -1)
                                   err = -1;
                   }
                 break;                  break;
         case I_MKDIR:          case I_MKDIR:
                 path1 = make_absolute(path1, *pwd);                  path1 = make_absolute(path1, *pwd);
                 attrib_clear(&a);                  attrib_clear(&a);
                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;                  a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
                 a.perm = 0777;                  a.perm = 0777;
                 do_mkdir(in, out, path1, &a);                  err = do_mkdir(in, out, path1, &a);
                 break;                  break;
         case I_RMDIR:          case I_RMDIR:
                 path1 = make_absolute(path1, *pwd);                  path1 = make_absolute(path1, *pwd);
                 do_rmdir(in, out, path1);                  err = do_rmdir(in, out, path1);
                 break;                  break;
         case I_CHDIR:          case I_CHDIR:
                 path1 = make_absolute(path1, *pwd);                  path1 = make_absolute(path1, *pwd);
                 if ((tmp = do_realpath(in, out, path1)) == NULL)                  if ((tmp = do_realpath(in, out, path1)) == NULL) {
                           err = 1;
                         break;                          break;
                 if ((aa = do_stat(in, out, tmp)) == NULL) {                  }
                   if ((aa = do_stat(in, out, tmp, 0)) == NULL) {
                         xfree(tmp);                          xfree(tmp);
                           err = 1;
                         break;                          break;
                 }                  }
                 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {                  if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
                         error("Can't change directory: Can't check target");                          error("Can't change directory: Can't check target");
                         xfree(tmp);                          xfree(tmp);
                           err = 1;
                         break;                          break;
                 }                  }
                 if (!S_ISDIR(aa->perm)) {                  if (!S_ISDIR(aa->perm)) {
                         error("Can't change directory: \"%s\" is not "                          error("Can't change directory: \"%s\" is not "
                             "a directory", tmp);                              "a directory", tmp);
                         xfree(tmp);                          xfree(tmp);
                           err = 1;
                         break;                          break;
                 }                  }
                 xfree(*pwd);                  xfree(*pwd);
Line 512 
Line 737 
                         break;                          break;
                 xfree(path1);                  xfree(path1);
                 path1 = tmp;                  path1 = tmp;
                 if ((aa = do_stat(in, out, path1)) == NULL)                  if ((aa = do_stat(in, out, path1, 0)) == NULL)
                         break;                          break;
                 if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&                  if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
                     !S_ISDIR(aa->perm)) {                      !S_ISDIR(aa->perm)) {
                         error("Can't ls: \"%s\" is not a directory", path1);                          error("Can't ls: \"%s\" is not a directory", path1);
                         break;                          break;
Line 522 
Line 747 
                 do_ls(in, out, path1);                  do_ls(in, out, path1);
                 break;                  break;
         case I_LCHDIR:          case I_LCHDIR:
                 if (chdir(path1) == -1)                  if (chdir(path1) == -1) {
                         error("Couldn't change local directory to "                          error("Couldn't change local directory to "
                             "\"%s\": %s", path1, strerror(errno));                              "\"%s\": %s", path1, strerror(errno));
                           err = 1;
                   }
                 break;                  break;
         case I_LMKDIR:          case I_LMKDIR:
                 if (mkdir(path1, 0777) == -1)                  if (mkdir(path1, 0777) == -1) {
                         error("Couldn't create local directory "                          error("Couldn't create local directory "
                             "\"%s\": %s", path1, strerror(errno));                              "\"%s\": %s", path1, strerror(errno));
                           err = 1;
                   }
                 break;                  break;
         case I_LLS:          case I_LLS:
                 local_do_ls(cmd);                  local_do_ls(cmd);
Line 546 
Line 775 
                 attrib_clear(&a);                  attrib_clear(&a);
                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;                  a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
                 a.perm = n_arg;                  a.perm = n_arg;
                 do_setstat(in, out, path1, &a);                  remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                   for(i = 0; g.gl_pathv[i]; i++) {
                           printf("Changing mode on %s\n", g.gl_pathv[i]);
                           do_setstat(in, out, g.gl_pathv[i], &a);
                   }
                 break;                  break;
         case I_CHOWN:          case I_CHOWN:
                 path1 = make_absolute(path1, *pwd);                  path1 = make_absolute(path1, *pwd);
                 if (!(aa = do_stat(in, out, path1)))                  remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                         break;                  for(i = 0; g.gl_pathv[i]; i++) {
                 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {                          if (!(aa = do_stat(in, out, g.gl_pathv[i], 0)))
                         error("Can't get current ownership of "                                  continue;
                             "remote file \"%s\"", path1);                          if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
                         break;                                  error("Can't get current ownership of "
                                       "remote file \"%s\"", g.gl_pathv[i]);
                                   continue;
                           }
                           printf("Changing owner on %s\n", g.gl_pathv[i]);
                           aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
                           aa->uid = n_arg;
                           do_setstat(in, out, g.gl_pathv[i], aa);
                 }                  }
                 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;  
                 aa->uid = n_arg;  
                 do_setstat(in, out, path1, aa);  
                 break;                  break;
         case I_CHGRP:          case I_CHGRP:
                 path1 = make_absolute(path1, *pwd);                  path1 = make_absolute(path1, *pwd);
                 if (!(aa = do_stat(in, out, path1)))                  remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g);
                         break;                  for(i = 0; g.gl_pathv[i]; i++) {
                 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {                          if (!(aa = do_stat(in, out, g.gl_pathv[i], 0)))
                         error("Can't get current ownership of "                                  continue;
                             "remote file \"%s\"", path1);                          if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
                         break;                                  error("Can't get current ownership of "
                                       "remote file \"%s\"", g.gl_pathv[i]);
                                   continue;
                           }
                           printf("Changing group on %s\n", g.gl_pathv[i]);
                           aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
                           aa->gid = n_arg;
                           do_setstat(in, out, g.gl_pathv[i], aa);
                 }                  }
                 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;  
                 aa->gid = n_arg;  
                 do_setstat(in, out, path1, aa);  
                 break;                  break;
         case I_PWD:          case I_PWD:
                 printf("Remote working directory: %s\n", *pwd);                  printf("Remote working directory: %s\n", *pwd);
                 break;                  break;
         case I_LPWD:          case I_LPWD:
                 if (!getcwd(path_buf, sizeof(path_buf)))                  if (!getcwd(path_buf, sizeof(path_buf)))
                         error("Couldn't get local cwd: %s\n",                          error("Couldn't get local cwd: %s",
                             strerror(errno));                              strerror(errno));
                 else                  else
                         printf("Local working directory: %s\n",                          printf("Local working directory: %s\n",
Line 590 
Line 831 
         case I_HELP:          case I_HELP:
                 help();                  help();
                 break;                  break;
           case I_VERSION:
                   printf("SFTP protocol version %d\n", version);
                   break;
         default:          default:
                 fatal("%d is not implemented", cmdnum);                  fatal("%d is not implemented", cmdnum);
         }          }
   
           if (g.gl_pathc)
                   globfree(&g);
         if (path1)          if (path1)
                 xfree(path1);                  xfree(path1);
         if (path2)          if (path2)
                 xfree(path2);                  xfree(path2);
   
           /* If an error occurs in batch mode we should abort. */
           if (infile != stdin && err > 0)
                   return -1;
   
         return(0);          return(0);
 }  }
   
Line 607 
Line 858 
         char *pwd;          char *pwd;
         char cmd[2048];          char cmd[2048];
   
           version = do_init(fd_in, fd_out);
           if (version == -1)
                   fatal("Couldn't initialise connection to server");
   
         pwd = do_realpath(fd_in, fd_out, ".");          pwd = do_realpath(fd_in, fd_out, ".");
         if (pwd == NULL)          if (pwd == NULL)
                 fatal("Need cwd");                  fatal("Need cwd");
   
         setvbuf(stdout, NULL, _IOLBF, 0);          setvbuf(stdout, NULL, _IOLBF, 0);
         setvbuf(stdin, NULL, _IOLBF, 0);          setvbuf(infile, NULL, _IOLBF, 0);
   
         for(;;) {          for(;;) {
                 char *cp;                  char *cp;
Line 620 
Line 875 
                 printf("sftp> ");                  printf("sftp> ");
   
                 /* XXX: use libedit */                  /* XXX: use libedit */
                 if (fgets(cmd, sizeof(cmd), stdin) == NULL) {                  if (fgets(cmd, sizeof(cmd), infile) == NULL) {
                         printf("\n");                          printf("\n");
                         break;                          break;
                 }                  } else if (infile != stdin) /* Bluff typing */
                           printf("%s", cmd);
   
                 cp = strrchr(cmd, '\n');                  cp = strrchr(cmd, '\n');
                 if (cp)                  if (cp)
                         *cp = '\0';                          *cp = '\0';
   
                 if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd))                  if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd))
                         break;                          break;
         }          }

Legend:
Removed from v.1.22.2.2  
changed lines
  Added in v.1.22.2.3