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

Diff for /src/usr.bin/ssh/sftp.c between version 1.115 and 1.116

version 1.115, 2009/12/20 07:28:36 version 1.116, 2010/01/04 02:03:57
Line 74 
Line 74 
 /* I wish qsort() took a separate ctx for the comparison function...*/  /* I wish qsort() took a separate ctx for the comparison function...*/
 int sort_flag;  int sort_flag;
   
   /* Context used for commandline completion */
   struct complete_ctx {
           struct sftp_conn *conn;
           char **remote_pathp;
   };
   
 int remote_glob(struct sftp_conn *, const char *, int,  int remote_glob(struct sftp_conn *, const char *, int,
     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */      int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
   
Line 122 
Line 128 
 struct CMD {  struct CMD {
         const char *c;          const char *c;
         const int n;          const int n;
           const int t;
 };  };
   
   /* Type of completion */
   #define NOARGS  0
   #define REMOTE  1
   #define LOCAL   2
   
 static const struct CMD cmds[] = {  static const struct CMD cmds[] = {
         { "bye",        I_QUIT },          { "bye",        I_QUIT,         NOARGS  },
         { "cd",         I_CHDIR },          { "cd",         I_CHDIR,        REMOTE  },
         { "chdir",      I_CHDIR },          { "chdir",      I_CHDIR,        REMOTE  },
         { "chgrp",      I_CHGRP },          { "chgrp",      I_CHGRP,        REMOTE  },
         { "chmod",      I_CHMOD },          { "chmod",      I_CHMOD,        REMOTE  },
         { "chown",      I_CHOWN },          { "chown",      I_CHOWN,        REMOTE  },
         { "df",         I_DF },          { "df",         I_DF,           REMOTE  },
         { "dir",        I_LS },          { "dir",        I_LS,           REMOTE  },
         { "exit",       I_QUIT },          { "exit",       I_QUIT,         NOARGS  },
         { "get",        I_GET },          { "get",        I_GET,          REMOTE  },
         { "mget",       I_GET },          { "help",       I_HELP,         NOARGS  },
         { "help",       I_HELP },          { "lcd",        I_LCHDIR,       LOCAL   },
         { "lcd",        I_LCHDIR },          { "lchdir",     I_LCHDIR,       LOCAL   },
         { "lchdir",     I_LCHDIR },          { "lls",        I_LLS,          LOCAL   },
         { "lls",        I_LLS },          { "lmkdir",     I_LMKDIR,       LOCAL   },
         { "lmkdir",     I_LMKDIR },          { "ln",         I_SYMLINK,      REMOTE  },
         { "ln",         I_SYMLINK },          { "lpwd",       I_LPWD,         LOCAL   },
         { "lpwd",       I_LPWD },          { "ls",         I_LS,           REMOTE  },
         { "ls",         I_LS },          { "lumask",     I_LUMASK,       NOARGS  },
         { "lumask",     I_LUMASK },          { "mkdir",      I_MKDIR,        REMOTE  },
         { "mkdir",      I_MKDIR },          { "progress",   I_PROGRESS,     NOARGS  },
         { "progress",   I_PROGRESS },          { "put",        I_PUT,          LOCAL   },
         { "put",        I_PUT },          { "pwd",        I_PWD,          REMOTE  },
         { "mput",       I_PUT },          { "quit",       I_QUIT,         NOARGS  },
         { "pwd",        I_PWD },          { "rename",     I_RENAME,       REMOTE  },
         { "quit",       I_QUIT },          { "rm",         I_RM,           REMOTE  },
         { "rename",     I_RENAME },          { "rmdir",      I_RMDIR,        REMOTE  },
         { "rm",         I_RM },          { "symlink",    I_SYMLINK,      REMOTE  },
         { "rmdir",      I_RMDIR },          { "version",    I_VERSION,      NOARGS  },
         { "symlink",    I_SYMLINK },          { "!",          I_SHELL,        NOARGS  },
         { "version",    I_VERSION },          { "?",          I_HELP,         NOARGS  },
         { "!",          I_SHELL },          { NULL,         -1,             -1      }
         { "?",          I_HELP },  
         { NULL,                 -1}  
 };  };
   
 int interactive_loop(struct sftp_conn *, char *file1, char *file2);  int interactive_loop(struct sftp_conn *, char *file1, char *file2);
Line 909 
Line 919 
  * Split a string into an argument vector using sh(1)-style quoting,   * Split a string into an argument vector using sh(1)-style quoting,
  * comment and escaping rules, but with some tweaks to handle glob(3)   * comment and escaping rules, but with some tweaks to handle glob(3)
  * wildcards.   * wildcards.
    * The "sloppy" flag allows for recovery from missing terminating quote, for
    * use in parsing incomplete commandlines during tab autocompletion.
    *
  * Returns NULL on error or a NULL-terminated array of arguments.   * Returns NULL on error or a NULL-terminated array of arguments.
    *
    * If "lastquote" is not NULL, the quoting character used for the last
    * argument is placed in *lastquote ("\0", "'" or "\"").
    *
    * If "terminated" is not NULL, *terminated will be set to 1 when the
    * last argument's quote has been properly terminated or 0 otherwise.
    * This parameter is only of use if "sloppy" is set.
  */   */
 #define MAXARGS         128  #define MAXARGS         128
 #define MAXARGLEN       8192  #define MAXARGLEN       8192
 static char **  static char **
 makeargv(const char *arg, int *argcp)  makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
       u_int *terminated)
 {  {
         int argc, quot;          int argc, quot;
         size_t i, j;          size_t i, j;
Line 928 
Line 949 
                 error("string too long");                  error("string too long");
                 return NULL;                  return NULL;
         }          }
           if (terminated != NULL)
                   *terminated = 1;
           if (lastquote != NULL)
                   *lastquote = '\0';
         state = MA_START;          state = MA_START;
         i = j = 0;          i = j = 0;
         for (;;) {          for (;;) {
Line 944 
Line 969 
                         if (state == MA_START) {                          if (state == MA_START) {
                                 argv[argc] = argvs + j;                                  argv[argc] = argvs + j;
                                 state = q;                                  state = q;
                                   if (lastquote != NULL)
                                           *lastquote = arg[i];
                         } else if (state == MA_UNQUOTED)                          } else if (state == MA_UNQUOTED)
                                 state = q;                                  state = q;
                         else if (state == q)                          else if (state == q)
Line 980 
Line 1007 
                                 if (state == MA_START) {                                  if (state == MA_START) {
                                         argv[argc] = argvs + j;                                          argv[argc] = argvs + j;
                                         state = MA_UNQUOTED;                                          state = MA_UNQUOTED;
                                           if (lastquote != NULL)
                                                   *lastquote = '\0';
                                 }                                  }
                                 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||                                  if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
                                     arg[i + 1] == '*' || arg[i + 1] == '\\') {                                      arg[i + 1] == '*' || arg[i + 1] == '\\') {
Line 1005 
Line 1034 
                                 goto string_done;                                  goto string_done;
                 } else if (arg[i] == '\0') {                  } else if (arg[i] == '\0') {
                         if (state == MA_SQUOTE || state == MA_DQUOTE) {                          if (state == MA_SQUOTE || state == MA_DQUOTE) {
                                   if (sloppy) {
                                           state = MA_UNQUOTED;
                                           if (terminated != NULL)
                                                   *terminated = 0;
                                           goto string_done;
                                   }
                                 error("Unterminated quoted argument");                                  error("Unterminated quoted argument");
                                 return NULL;                                  return NULL;
                         }                          }
Line 1018 
Line 1053 
                         if (state == MA_START) {                          if (state == MA_START) {
                                 argv[argc] = argvs + j;                                  argv[argc] = argvs + j;
                                 state = MA_UNQUOTED;                                  state = MA_UNQUOTED;
                                   if (lastquote != NULL)
                                           *lastquote = '\0';
                         }                          }
                         if ((state == MA_SQUOTE || state == MA_DQUOTE) &&                          if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
                             (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {                              (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
Line 1040 
Line 1077 
 }  }
   
 static int  static int
 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag,  parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
     unsigned long *n_arg, char **path1, char **path2)      int *hflag, unsigned long *n_arg, char **path1, char **path2)
 {  {
         const char *cmd, *cp = *cpp;          const char *cmd, *cp = *cpp;
         char *cp2, **argv;          char *cp2, **argv;
Line 1063 
Line 1100 
                 cp++;                  cp++;
         }          }
   
         if ((argv = makeargv(cp, &argc)) == NULL)          if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
                 return -1;                  return -1;
   
         /* Figure out which command we have */          /* Figure out which command we have */
Line 1443 
Line 1480 
         return ("sftp> ");          return ("sftp> ");
 }  }
   
   /* Display entries in 'list' after skipping the first 'len' chars */
   static void
   complete_display(char **list, u_int len)
   {
           u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
           struct winsize ws;
           char *tmp;
   
           /* Count entries for sort and find longest */
           for (y = 0; list[y]; y++)
                   m = MAX(m, strlen(list[y]));
   
           if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
                   width = ws.ws_col;
   
           m = m > len ? m - len : 0;
           columns = width / (m + 2);
           columns = MAX(columns, 1);
           colspace = width / columns;
           colspace = MIN(colspace, width);
   
           printf("\n");
           m = 1;
           for (y = 0; list[y]; y++) {
                   llen = strlen(list[y]);
                   tmp = llen > len ? list[y] + len : "";
                   printf("%-*s", colspace, tmp);
                   if (m >= columns) {
                           printf("\n");
                           m = 1;
                   } else
                           m++;
           }
           printf("\n");
   }
   
   /*
    * Given a "list" of words that begin with a common prefix of "word",
    * attempt to find an autocompletion to extends "word" by the next
    * characters common to all entries in "list".
    */
   static char *
   complete_ambiguous(const char *word, char **list, size_t count)
   {
           if (word == NULL)
                   return NULL;
   
           if (count > 0) {
                   u_int y, matchlen = strlen(list[0]);
   
                   /* Find length of common stem */
                   for (y = 1; list[y]; y++) {
                           u_int x;
   
                           for (x = 0; x < matchlen; x++)
                                   if (list[0][x] != list[y][x])
                                           break;
   
                           matchlen = x;
                   }
   
                   if (matchlen > strlen(word)) {
                           char *tmp = xstrdup(list[0]);
   
                           tmp[matchlen] = NULL;
                           return tmp;
                   }
           }
   
           return xstrdup(word);
   }
   
   /* Autocomplete a sftp command */
   static int
   complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
       int terminated)
   {
           u_int y, count = 0, cmdlen, tmplen;
           char *tmp, **list, argterm[3];
           const LineInfo *lf;
   
           list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
   
           /* No command specified: display all available commands */
           if (cmd == NULL) {
                   for (y = 0; cmds[y].c; y++)
                           list[count++] = xstrdup(cmds[y].c);
   
                   list[count] = NULL;
                   complete_display(list, 0);
   
                   for (y = 0; list[y] != NULL; y++)
                           xfree(list[y]);
                   xfree(list);
                   return count;
           }
   
           /* Prepare subset of commands that start with "cmd" */
           cmdlen = strlen(cmd);
           for (y = 0; cmds[y].c; y++)  {
                   if (!strncasecmp(cmd, cmds[y].c, cmdlen))
                           list[count++] = xstrdup(cmds[y].c);
           }
           list[count] = NULL;
   
           if (count == 0)
                   return 0;
   
           /* Complete ambigious command */
           tmp = complete_ambiguous(cmd, list, count);
           if (count > 1)
                   complete_display(list, 0);
   
           for (y = 0; list[y]; y++)
                   xfree(list[y]);
           xfree(list);
   
           if (tmp != NULL) {
                   tmplen = strlen(tmp);
                   cmdlen = strlen(cmd);
                   /* If cmd may be extended then do so */
                   if (tmplen > cmdlen)
                           if (el_insertstr(el, tmp + cmdlen) == -1)
                                   fatal("el_insertstr failed.");
                   lf = el_line(el);
                   /* Terminate argument cleanly */
                   if (count == 1) {
                           y = 0;
                           if (!terminated)
                                   argterm[y++] = quote;
                           if (lastarg || *(lf->cursor) != ' ')
                                   argterm[y++] = ' ';
                           argterm[y] = '\0';
                           if (y > 0 && el_insertstr(el, argterm) == -1)
                                   fatal("el_insertstr failed.");
                   }
                   xfree(tmp);
           }
   
           return count;
   }
   
   /*
    * Determine whether a particular sftp command's arguments (if any)
    * represent local or remote files.
    */
   static int
   complete_is_remote(char *cmd) {
           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;
           }
   
           return -1;
   }
   
   /* Autocomplete a filename "file" */
   static int
   complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
       char *file, int remote, int lastarg, char quote, int terminated)
   {
           glob_t g;
           char *tmp, *tmp2, ins[3];
           u_int i, hadglob, pwdlen, len, tmplen, filelen;
           const LineInfo *lf;
   
           /* Glob from "file" location */
           if (file == NULL)
                   tmp = xstrdup("*");
           else
                   xasprintf(&tmp, "%s*", file);
   
           memset(&g, 0, sizeof(g));
           if (remote != LOCAL) {
                   tmp = make_absolute(tmp, remote_path);
                   remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
           } else
                   glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
   
           /* Determine length of pwd so we can trim completion display */
           for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
                   /* Terminate counting on first unescaped glob metacharacter */
                   if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
                           if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
                                   hadglob = 1;
                           break;
                   }
                   if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
                           tmplen++;
                   if (tmp[tmplen] == '/')
                           pwdlen = tmplen + 1;    /* track last seen '/' */
           }
           xfree(tmp);
   
           if (g.gl_matchc == 0)
                   goto out;
   
           if (g.gl_matchc > 1)
                   complete_display(g.gl_pathv, pwdlen);
   
           tmp = NULL;
           /* Don't try to extend globs */
           if (file == NULL || hadglob)
                   goto out;
   
           tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
           tmp = path_strip(tmp2, remote_path);
           xfree(tmp2);
   
           if (tmp == NULL)
                   goto out;
   
           tmplen = strlen(tmp);
           filelen = strlen(file);
   
           if (tmplen > filelen)  {
                   tmp2 = tmp + filelen;
                   len = strlen(tmp2);
                   /* quote argument on way out */
                   for (i = 0; i < len; i++) {
                           ins[0] = '\\';
                           ins[1] = tmp2[i];
                           ins[2] = '\0';
                           switch (tmp2[i]) {
                           case '\'':
                           case '"':
                           case '\\':
                           case '\t':
                           case ' ':
                                   if (quote == '\0' || tmp2[i] == quote) {
                                           if (el_insertstr(el, ins) == -1)
                                                   fatal("el_insertstr "
                                                       "failed.");
                                           break;
                                   }
                                   /* FALLTHROUGH */
                           default:
                                   if (el_insertstr(el, ins + 1) == -1)
                                           fatal("el_insertstr failed.");
                                   break;
                           }
                   }
           }
   
           lf = el_line(el);
           /*
            * XXX should we really extend here? the user may not be done if
            * the filename is a directory.
            */
           if (g.gl_matchc == 1) {
                   i = 0;
                   if (!terminated)
                           ins[i++] = quote;
                   if (lastarg || *(lf->cursor) != ' ')
                           ins[i++] = ' ';
                   ins[i] = '\0';
                   if (i > 0 && el_insertstr(el, ins) == -1)
                           fatal("el_insertstr failed.");
           }
           xfree(tmp);
   
    out:
           globfree(&g);
           return g.gl_matchc;
   }
   
   /* tab-completion hook function, called via libedit */
   static unsigned char
   complete(EditLine *el, int ch)
   {
           char **argv, *line, quote;
           u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
           const LineInfo *lf;
           struct complete_ctx *complete_ctx;
   
           lf = el_line(el);
           if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
                   fatal("%s: el_get failed", __func__);
   
           /* Figure out which argument the cursor points to */
           cursor = lf->cursor - lf->buffer;
           line = (char *)xmalloc(cursor + 1);
           memcpy(line, lf->buffer, cursor);
           line[cursor] = '\0';
           argv = makeargv(line, &carg, 1, &quote, &terminated);
           xfree(line);
   
           /* Get all the arguments on the line */
           len = lf->lastchar - lf->buffer;
           line = (char *)xmalloc(len + 1);
           memcpy(line, lf->buffer, len);
           line[len] = '\0';
           argv = makeargv(line, &argc, 1, NULL, NULL);
   
           /* Ensure cursor is at EOL or a argument boundary */
           if (line[cursor] != ' ' && line[cursor] != '\0' &&
               line[cursor] != '\n') {
                   xfree(line);
                   return ret;
           }
   
           if (carg == 0) {
                   /* Show all available commands */
                   complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
                   ret = CC_REDISPLAY;
           } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
                   /* Handle the command parsing */
                   if (complete_cmd_parse(el, argv[0], argc == carg,
                       quote, terminated) != 0)
                           ret = CC_REDISPLAY;
           } else if (carg >= 1) {
                   /* Handle file parsing */
                   int remote = complete_is_remote(argv[0]);
                   char *filematch = NULL;
   
                   if (carg > 1 && line[cursor-1] != ' ')
                           filematch = argv[carg - 1];
   
                   if (remote != 0 &&
                       complete_match(el, complete_ctx->conn,
                       *complete_ctx->remote_pathp, filematch,
                       remote, carg == argc, quote, terminated) != 0)
                           ret = CC_REDISPLAY;
           }
   
           xfree(line);
           return ret;
   }
   
 int  int
 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)  interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
 {  {
         char *pwd;          char *remote_path;
         char *dir = NULL;          char *dir = NULL;
         char cmd[2048];          char cmd[2048];
         int err, interactive;          int err, interactive;
Line 1454 
Line 1825 
         History *hl = NULL;          History *hl = NULL;
         HistEvent hev;          HistEvent hev;
         extern char *__progname;          extern char *__progname;
           struct complete_ctx complete_ctx;
   
         if (!batchmode && isatty(STDIN_FILENO)) {          if (!batchmode && isatty(STDIN_FILENO)) {
                 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)                  if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
Line 1468 
Line 1840 
                 el_set(el, EL_TERMINAL, NULL);                  el_set(el, EL_TERMINAL, NULL);
                 el_set(el, EL_SIGNAL, 1);                  el_set(el, EL_SIGNAL, 1);
                 el_source(el, NULL);                  el_source(el, NULL);
   
                   /* Tab Completion */
                   el_set(el, EL_ADDFN, "ftp-complete",
                       "Context senstive argument completion", complete);
                   complete_ctx.conn = conn;
                   complete_ctx.remote_pathp = &remote_path;
                   el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
                   el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
         }          }
   
         pwd = do_realpath(conn, ".");          remote_path = do_realpath(conn, ".");
         if (pwd == NULL)          if (remote_path == NULL)
                 fatal("Need cwd");                  fatal("Need cwd");
   
         if (file1 != NULL) {          if (file1 != NULL) {
                 dir = xstrdup(file1);                  dir = xstrdup(file1);
                 dir = make_absolute(dir, pwd);                  dir = make_absolute(dir, remote_path);
   
                 if (remote_is_dir(conn, dir) && file2 == NULL) {                  if (remote_is_dir(conn, dir) && file2 == NULL) {
                         printf("Changing to: %s\n", dir);                          printf("Changing to: %s\n", dir);
                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);                          snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
                         if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {                          if (parse_dispatch_command(conn, cmd,
                               &remote_path, 1) != 0) {
                                 xfree(dir);                                  xfree(dir);
                                 xfree(pwd);                                  xfree(remote_path);
                                 xfree(conn);                                  xfree(conn);
                                 return (-1);                                  return (-1);
                         }                          }
Line 1494 
Line 1875 
                                 snprintf(cmd, sizeof cmd, "get %s %s", dir,                                  snprintf(cmd, sizeof cmd, "get %s %s", dir,
                                     file2);                                      file2);
   
                         err = parse_dispatch_command(conn, cmd, &pwd, 1);                          err = parse_dispatch_command(conn, cmd,
                               &remote_path, 1);
                         xfree(dir);                          xfree(dir);
                         xfree(pwd);                          xfree(remote_path);
                         xfree(conn);                          xfree(conn);
                         return (err);                          return (err);
                 }                  }
Line 1530 
Line 1912 
                                         printf("\n");                                          printf("\n");
                         }                          }
                 } else {                  } else {
                         if ((line = el_gets(el, &count)) == NULL || count <= 0) {                          if ((line = el_gets(el, &count)) == NULL ||
                               count <= 0) {
                                 printf("\n");                                  printf("\n");
                                 break;                                  break;
                         }                          }
Line 1549 
Line 1932 
                 interrupted = 0;                  interrupted = 0;
                 signal(SIGINT, cmd_interrupt);                  signal(SIGINT, cmd_interrupt);
   
                 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);                  err = parse_dispatch_command(conn, cmd, &remote_path,
                       batchmode);
                 if (err != 0)                  if (err != 0)
                         break;                          break;
         }          }
         xfree(pwd);          xfree(remote_path);
         xfree(conn);          xfree(conn);
   
         if (el != NULL)          if (el != NULL)

Legend:
Removed from v.1.115  
changed lines
  Added in v.1.116