summaryrefslogtreecommitdiff
path: root/sftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp.c')
-rw-r--r--sftp.c576
1 files changed, 395 insertions, 181 deletions
diff --git a/sftp.c b/sftp.c
index f0d5dd557..e1aa49d0f 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.96 2007/01/03 04:09:15 stevesk Exp $ */ 1/* $OpenBSD: sftp.c,v 1.103 2008/07/13 22:16:03 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -25,7 +25,11 @@
25#include <sys/param.h> 25#include <sys/param.h>
26#include <sys/socket.h> 26#include <sys/socket.h>
27#include <sys/wait.h> 27#include <sys/wait.h>
28#ifdef HAVE_SYS_STATVFS_H
29#include <sys/statvfs.h>
30#endif
28 31
32#include <ctype.h>
29#include <errno.h> 33#include <errno.h>
30 34
31#ifdef HAVE_PATHS_H 35#ifdef HAVE_PATHS_H
@@ -43,6 +47,14 @@ typedef void EditLine;
43#include <unistd.h> 47#include <unistd.h>
44#include <stdarg.h> 48#include <stdarg.h>
45 49
50#ifdef HAVE_UTIL_H
51# include <util.h>
52#endif
53
54#ifdef HAVE_LIBUTIL_H
55# include <libutil.h>
56#endif
57
46#include "xmalloc.h" 58#include "xmalloc.h"
47#include "log.h" 59#include "log.h"
48#include "pathnames.h" 60#include "pathnames.h"
@@ -63,7 +75,7 @@ int batchmode = 0;
63size_t copy_buffer_len = 32768; 75size_t copy_buffer_len = 32768;
64 76
65/* Number of concurrent outstanding requests */ 77/* Number of concurrent outstanding requests */
66size_t num_requests = 16; 78size_t num_requests = 64;
67 79
68/* PID of ssh transport process */ 80/* PID of ssh transport process */
69static pid_t sshpid = -1; 81static pid_t sshpid = -1;
@@ -103,6 +115,7 @@ extern char *__progname;
103#define I_CHGRP 2 115#define I_CHGRP 2
104#define I_CHMOD 3 116#define I_CHMOD 3
105#define I_CHOWN 4 117#define I_CHOWN 4
118#define I_DF 24
106#define I_GET 5 119#define I_GET 5
107#define I_HELP 6 120#define I_HELP 6
108#define I_LCHDIR 7 121#define I_LCHDIR 7
@@ -135,6 +148,7 @@ static const struct CMD cmds[] = {
135 { "chgrp", I_CHGRP }, 148 { "chgrp", I_CHGRP },
136 { "chmod", I_CHMOD }, 149 { "chmod", I_CHMOD },
137 { "chown", I_CHOWN }, 150 { "chown", I_CHOWN },
151 { "df", I_DF },
138 { "dir", I_LS }, 152 { "dir", I_LS },
139 { "exit", I_QUIT }, 153 { "exit", I_QUIT },
140 { "get", I_GET }, 154 { "get", I_GET },
@@ -199,6 +213,8 @@ help(void)
199 printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 213 printf("chgrp grp path Change group of file 'path' to 'grp'\n");
200 printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 214 printf("chmod mode path Change permissions of file 'path' to 'mode'\n");
201 printf("chown own path Change owner of file 'path' to 'own'\n"); 215 printf("chown own path Change owner of file 'path' to 'own'\n");
216 printf("df [path] Display statistics for current directory or\n");
217 printf(" filesystem containing 'path'\n");
202 printf("help Display this help text\n"); 218 printf("help Display this help text\n");
203 printf("get remote-path [local-path] Download file\n"); 219 printf("get remote-path [local-path] Download file\n");
204 printf("lls [ls-options [path]] Display local directory listing\n"); 220 printf("lls [ls-options [path]] Display local directory listing\n");
@@ -346,144 +362,105 @@ infer_path(const char *p, char **ifp)
346} 362}
347 363
348static int 364static int
349parse_getput_flags(const char **cpp, int *pflag) 365parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
350{ 366{
351 const char *cp = *cpp; 367 extern int opterr, optind, optopt, optreset;
368 int ch;
352 369
353 /* Check for flags */ 370 optind = optreset = 1;
354 if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { 371 opterr = 0;
355 switch (cp[1]) { 372
373 *pflag = 0;
374 while ((ch = getopt(argc, argv, "Pp")) != -1) {
375 switch (ch) {
356 case 'p': 376 case 'p':
357 case 'P': 377 case 'P':
358 *pflag = 1; 378 *pflag = 1;
359 break; 379 break;
360 default: 380 default:
361 error("Invalid flag -%c", cp[1]); 381 error("%s: Invalid flag -%c", cmd, optopt);
362 return(-1); 382 return -1;
363 } 383 }
364 cp += 2;
365 *cpp = cp + strspn(cp, WHITESPACE);
366 } 384 }
367 385
368 return(0); 386 return optind;
369} 387}
370 388
371static int 389static int
372parse_ls_flags(const char **cpp, int *lflag) 390parse_ls_flags(char **argv, int argc, int *lflag)
373{ 391{
374 const char *cp = *cpp; 392 extern int opterr, optind, optopt, optreset;
393 int ch;
375 394
376 /* Defaults */ 395 optind = optreset = 1;
377 *lflag = LS_NAME_SORT; 396 opterr = 0;
378 397
379 /* Check for flags */ 398 *lflag = LS_NAME_SORT;
380 if (cp++[0] == '-') { 399 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
381 for (; strchr(WHITESPACE, *cp) == NULL; cp++) { 400 switch (ch) {
382 switch (*cp) { 401 case '1':
383 case 'l': 402 *lflag &= ~VIEW_FLAGS;
384 *lflag &= ~VIEW_FLAGS; 403 *lflag |= LS_SHORT_VIEW;
385 *lflag |= LS_LONG_VIEW; 404 break;
386 break; 405 case 'S':
387 case '1': 406 *lflag &= ~SORT_FLAGS;
388 *lflag &= ~VIEW_FLAGS; 407 *lflag |= LS_SIZE_SORT;
389 *lflag |= LS_SHORT_VIEW; 408 break;
390 break; 409 case 'a':
391 case 'n': 410 *lflag |= LS_SHOW_ALL;
392 *lflag &= ~VIEW_FLAGS; 411 break;
393 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 412 case 'f':
394 break; 413 *lflag &= ~SORT_FLAGS;
395 case 'S': 414 break;
396 *lflag &= ~SORT_FLAGS; 415 case 'l':
397 *lflag |= LS_SIZE_SORT; 416 *lflag &= ~VIEW_FLAGS;
398 break; 417 *lflag |= LS_LONG_VIEW;
399 case 't': 418 break;
400 *lflag &= ~SORT_FLAGS; 419 case 'n':
401 *lflag |= LS_TIME_SORT; 420 *lflag &= ~VIEW_FLAGS;
402 break; 421 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
403 case 'r': 422 break;
404 *lflag |= LS_REVERSE_SORT; 423 case 'r':
405 break; 424 *lflag |= LS_REVERSE_SORT;
406 case 'f': 425 break;
407 *lflag &= ~SORT_FLAGS; 426 case 't':
408 break; 427 *lflag &= ~SORT_FLAGS;
409 case 'a': 428 *lflag |= LS_TIME_SORT;
410 *lflag |= LS_SHOW_ALL; 429 break;
411 break; 430 default:
412 default: 431 error("ls: Invalid flag -%c", optopt);
413 error("Invalid flag -%c", *cp); 432 return -1;
414 return(-1);
415 }
416 } 433 }
417 *cpp = cp + strspn(cp, WHITESPACE);
418 } 434 }
419 435
420 return(0); 436 return optind;
421} 437}
422 438
423static int 439static int
424get_pathname(const char **cpp, char **path) 440parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
425{ 441{
426 const char *cp = *cpp, *end; 442 extern int opterr, optind, optopt, optreset;
427 char quot; 443 int ch;
428 u_int i, j;
429
430 cp += strspn(cp, WHITESPACE);
431 if (!*cp) {
432 *cpp = cp;
433 *path = NULL;
434 return (0);
435 }
436
437 *path = xmalloc(strlen(cp) + 1);
438
439 /* Check for quoted filenames */
440 if (*cp == '\"' || *cp == '\'') {
441 quot = *cp++;
442 444
443 /* Search for terminating quote, unescape some chars */ 445 optind = optreset = 1;
444 for (i = j = 0; i <= strlen(cp); i++) { 446 opterr = 0;
445 if (cp[i] == quot) { /* Found quote */
446 i++;
447 (*path)[j] = '\0';
448 break;
449 }
450 if (cp[i] == '\0') { /* End of string */
451 error("Unterminated quote");
452 goto fail;
453 }
454 if (cp[i] == '\\') { /* Escaped characters */
455 i++;
456 if (cp[i] != '\'' && cp[i] != '\"' &&
457 cp[i] != '\\') {
458 error("Bad escaped character '\\%c'",
459 cp[i]);
460 goto fail;
461 }
462 }
463 (*path)[j++] = cp[i];
464 }
465 447
466 if (j == 0) { 448 *hflag = *iflag = 0;
467 error("Empty quotes"); 449 while ((ch = getopt(argc, argv, "hi")) != -1) {
468 goto fail; 450 switch (ch) {
451 case 'h':
452 *hflag = 1;
453 break;
454 case 'i':
455 *iflag = 1;
456 break;
457 default:
458 error("%s: Invalid flag -%c", cmd, optopt);
459 return -1;
469 } 460 }
470 *cpp = cp + i + strspn(cp + i, WHITESPACE);
471 } else {
472 /* Read to end of filename */
473 end = strpbrk(cp, WHITESPACE);
474 if (end == NULL)
475 end = strchr(cp, '\0');
476 *cpp = end + strspn(end, WHITESPACE);
477
478 memcpy(*path, cp, end - cp);
479 (*path)[end - cp] = '\0';
480 } 461 }
481 return (0);
482 462
483 fail: 463 return optind;
484 xfree(*path);
485 *path = NULL;
486 return (-1);
487} 464}
488 465
489static int 466static int
@@ -499,17 +476,6 @@ is_dir(char *path)
499} 476}
500 477
501static int 478static int
502is_reg(char *path)
503{
504 struct stat sb;
505
506 if (stat(path, &sb) == -1)
507 fatal("stat %s: %s", path, strerror(errno));
508
509 return(S_ISREG(sb.st_mode));
510}
511
512static int
513remote_is_dir(struct sftp_conn *conn, char *path) 479remote_is_dir(struct sftp_conn *conn, char *path)
514{ 480{
515 Attrib *a; 481 Attrib *a;
@@ -597,6 +563,7 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
597 glob_t g; 563 glob_t g;
598 int err = 0; 564 int err = 0;
599 int i; 565 int i;
566 struct stat sb;
600 567
601 if (dst) { 568 if (dst) {
602 tmp_dst = xstrdup(dst); 569 tmp_dst = xstrdup(dst);
@@ -605,7 +572,7 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
605 572
606 memset(&g, 0, sizeof(g)); 573 memset(&g, 0, sizeof(g));
607 debug3("Looking up %s", src); 574 debug3("Looking up %s", src);
608 if (glob(src, 0, NULL, &g)) { 575 if (glob(src, GLOB_NOCHECK, NULL, &g)) {
609 error("File \"%s\" not found.", src); 576 error("File \"%s\" not found.", src);
610 err = -1; 577 err = -1;
611 goto out; 578 goto out;
@@ -620,7 +587,13 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
620 } 587 }
621 588
622 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 589 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
623 if (!is_reg(g.gl_pathv[i])) { 590 if (stat(g.gl_pathv[i], &sb) == -1) {
591 err = -1;
592 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
593 continue;
594 }
595
596 if (!S_ISREG(sb.st_mode)) {
624 error("skipping non-regular file %s", 597 error("skipping non-regular file %s",
625 g.gl_pathv[i]); 598 g.gl_pathv[i]);
626 continue; 599 continue;
@@ -867,14 +840,238 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
867} 840}
868 841
869static int 842static int
870parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, 843do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
844{
845 struct sftp_statvfs st;
846 char s_used[FMT_SCALED_STRSIZE];
847 char s_avail[FMT_SCALED_STRSIZE];
848 char s_root[FMT_SCALED_STRSIZE];
849 char s_total[FMT_SCALED_STRSIZE];
850
851 if (do_statvfs(conn, path, &st, 1) == -1)
852 return -1;
853 if (iflag) {
854 printf(" Inodes Used Avail "
855 "(root) %%Capacity\n");
856 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
857 (unsigned long long)st.f_files,
858 (unsigned long long)(st.f_files - st.f_ffree),
859 (unsigned long long)st.f_favail,
860 (unsigned long long)st.f_ffree,
861 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
862 st.f_files));
863 } else if (hflag) {
864 strlcpy(s_used, "error", sizeof(s_used));
865 strlcpy(s_avail, "error", sizeof(s_avail));
866 strlcpy(s_root, "error", sizeof(s_root));
867 strlcpy(s_total, "error", sizeof(s_total));
868 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
869 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
870 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
871 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
872 printf(" Size Used Avail (root) %%Capacity\n");
873 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
874 s_total, s_used, s_avail, s_root,
875 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
876 st.f_blocks));
877 } else {
878 printf(" Size Used Avail "
879 "(root) %%Capacity\n");
880 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
881 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
882 (unsigned long long)(st.f_frsize *
883 (st.f_blocks - st.f_bfree) / 1024),
884 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
885 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
886 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
887 st.f_blocks));
888 }
889 return 0;
890}
891
892/*
893 * Undo escaping of glob sequences in place. Used to undo extra escaping
894 * applied in makeargv() when the string is destined for a function that
895 * does not glob it.
896 */
897static void
898undo_glob_escape(char *s)
899{
900 size_t i, j;
901
902 for (i = j = 0;;) {
903 if (s[i] == '\0') {
904 s[j] = '\0';
905 return;
906 }
907 if (s[i] != '\\') {
908 s[j++] = s[i++];
909 continue;
910 }
911 /* s[i] == '\\' */
912 ++i;
913 switch (s[i]) {
914 case '?':
915 case '[':
916 case '*':
917 case '\\':
918 s[j++] = s[i++];
919 break;
920 case '\0':
921 s[j++] = '\\';
922 s[j] = '\0';
923 return;
924 default:
925 s[j++] = '\\';
926 s[j++] = s[i++];
927 break;
928 }
929 }
930}
931
932/*
933 * Split a string into an argument vector using sh(1)-style quoting,
934 * comment and escaping rules, but with some tweaks to handle glob(3)
935 * wildcards.
936 * Returns NULL on error or a NULL-terminated array of arguments.
937 */
938#define MAXARGS 128
939#define MAXARGLEN 8192
940static char **
941makeargv(const char *arg, int *argcp)
942{
943 int argc, quot;
944 size_t i, j;
945 static char argvs[MAXARGLEN];
946 static char *argv[MAXARGS + 1];
947 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
948
949 *argcp = argc = 0;
950 if (strlen(arg) > sizeof(argvs) - 1) {
951 args_too_longs:
952 error("string too long");
953 return NULL;
954 }
955 state = MA_START;
956 i = j = 0;
957 for (;;) {
958 if (isspace(arg[i])) {
959 if (state == MA_UNQUOTED) {
960 /* Terminate current argument */
961 argvs[j++] = '\0';
962 argc++;
963 state = MA_START;
964 } else if (state != MA_START)
965 argvs[j++] = arg[i];
966 } else if (arg[i] == '"' || arg[i] == '\'') {
967 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
968 if (state == MA_START) {
969 argv[argc] = argvs + j;
970 state = q;
971 } else if (state == MA_UNQUOTED)
972 state = q;
973 else if (state == q)
974 state = MA_UNQUOTED;
975 else
976 argvs[j++] = arg[i];
977 } else if (arg[i] == '\\') {
978 if (state == MA_SQUOTE || state == MA_DQUOTE) {
979 quot = state == MA_SQUOTE ? '\'' : '"';
980 /* Unescape quote we are in */
981 /* XXX support \n and friends? */
982 if (arg[i + 1] == quot) {
983 i++;
984 argvs[j++] = arg[i];
985 } else if (arg[i + 1] == '?' ||
986 arg[i + 1] == '[' || arg[i + 1] == '*') {
987 /*
988 * Special case for sftp: append
989 * double-escaped glob sequence -
990 * glob will undo one level of
991 * escaping. NB. string can grow here.
992 */
993 if (j >= sizeof(argvs) - 5)
994 goto args_too_longs;
995 argvs[j++] = '\\';
996 argvs[j++] = arg[i++];
997 argvs[j++] = '\\';
998 argvs[j++] = arg[i];
999 } else {
1000 argvs[j++] = arg[i++];
1001 argvs[j++] = arg[i];
1002 }
1003 } else {
1004 if (state == MA_START) {
1005 argv[argc] = argvs + j;
1006 state = MA_UNQUOTED;
1007 }
1008 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1009 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1010 /*
1011 * Special case for sftp: append
1012 * escaped glob sequence -
1013 * glob will undo one level of
1014 * escaping.
1015 */
1016 argvs[j++] = arg[i++];
1017 argvs[j++] = arg[i];
1018 } else {
1019 /* Unescape everything */
1020 /* XXX support \n and friends? */
1021 i++;
1022 argvs[j++] = arg[i];
1023 }
1024 }
1025 } else if (arg[i] == '#') {
1026 if (state == MA_SQUOTE || state == MA_DQUOTE)
1027 argvs[j++] = arg[i];
1028 else
1029 goto string_done;
1030 } else if (arg[i] == '\0') {
1031 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1032 error("Unterminated quoted argument");
1033 return NULL;
1034 }
1035 string_done:
1036 if (state == MA_UNQUOTED) {
1037 argvs[j++] = '\0';
1038 argc++;
1039 }
1040 break;
1041 } else {
1042 if (state == MA_START) {
1043 argv[argc] = argvs + j;
1044 state = MA_UNQUOTED;
1045 }
1046 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1047 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1048 /*
1049 * Special case for sftp: escape quoted
1050 * glob(3) wildcards. NB. string can grow
1051 * here.
1052 */
1053 if (j >= sizeof(argvs) - 3)
1054 goto args_too_longs;
1055 argvs[j++] = '\\';
1056 argvs[j++] = arg[i];
1057 } else
1058 argvs[j++] = arg[i];
1059 }
1060 i++;
1061 }
1062 *argcp = argc;
1063 return argv;
1064}
1065
1066static int
1067parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
871 unsigned long *n_arg, char **path1, char **path2) 1068 unsigned long *n_arg, char **path1, char **path2)
872{ 1069{
873 const char *cmd, *cp = *cpp; 1070 const char *cmd, *cp = *cpp;
874 char *cp2; 1071 char *cp2, **argv;
875 int base = 0; 1072 int base = 0;
876 long l; 1073 long l;
877 int i, cmdnum; 1074 int i, cmdnum, optidx, argc;
878 1075
879 /* Skip leading whitespace */ 1076 /* Skip leading whitespace */
880 cp = cp + strspn(cp, WHITESPACE); 1077 cp = cp + strspn(cp, WHITESPACE);
@@ -890,17 +1087,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
890 cp++; 1087 cp++;
891 } 1088 }
892 1089
1090 if ((argv = makeargv(cp, &argc)) == NULL)
1091 return -1;
1092
893 /* Figure out which command we have */ 1093 /* Figure out which command we have */
894 for (i = 0; cmds[i].c; i++) { 1094 for (i = 0; cmds[i].c != NULL; i++) {
895 int cmdlen = strlen(cmds[i].c); 1095 if (strcasecmp(cmds[i].c, argv[0]) == 0)
896
897 /* Check for command followed by whitespace */
898 if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
899 strchr(WHITESPACE, cp[cmdlen])) {
900 cp += cmdlen;
901 cp = cp + strspn(cp, WHITESPACE);
902 break; 1096 break;
903 }
904 } 1097 }
905 cmdnum = cmds[i].n; 1098 cmdnum = cmds[i].n;
906 cmd = cmds[i].c; 1099 cmd = cmds[i].c;
@@ -911,40 +1104,44 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
911 cmdnum = I_SHELL; 1104 cmdnum = I_SHELL;
912 } else if (cmdnum == -1) { 1105 } else if (cmdnum == -1) {
913 error("Invalid command."); 1106 error("Invalid command.");
914 return (-1); 1107 return -1;
915 } 1108 }
916 1109
917 /* Get arguments and parse flags */ 1110 /* Get arguments and parse flags */
918 *lflag = *pflag = *n_arg = 0; 1111 *lflag = *pflag = *hflag = *n_arg = 0;
919 *path1 = *path2 = NULL; 1112 *path1 = *path2 = NULL;
1113 optidx = 1;
920 switch (cmdnum) { 1114 switch (cmdnum) {
921 case I_GET: 1115 case I_GET:
922 case I_PUT: 1116 case I_PUT:
923 if (parse_getput_flags(&cp, pflag)) 1117 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
924 return(-1); 1118 return -1;
925 /* Get first pathname (mandatory) */ 1119 /* Get first pathname (mandatory) */
926 if (get_pathname(&cp, path1)) 1120 if (argc - optidx < 1) {
927 return(-1);
928 if (*path1 == NULL) {
929 error("You must specify at least one path after a " 1121 error("You must specify at least one path after a "
930 "%s command.", cmd); 1122 "%s command.", cmd);
931 return(-1); 1123 return -1;
1124 }
1125 *path1 = xstrdup(argv[optidx]);
1126 /* Get second pathname (optional) */
1127 if (argc - optidx > 1) {
1128 *path2 = xstrdup(argv[optidx + 1]);
1129 /* Destination is not globbed */
1130 undo_glob_escape(*path2);
932 } 1131 }
933 /* Try to get second pathname (optional) */
934 if (get_pathname(&cp, path2))
935 return(-1);
936 break; 1132 break;
937 case I_RENAME: 1133 case I_RENAME:
938 case I_SYMLINK: 1134 case I_SYMLINK:
939 if (get_pathname(&cp, path1)) 1135 if (argc - optidx < 2) {
940 return(-1);
941 if (get_pathname(&cp, path2))
942 return(-1);
943 if (!*path1 || !*path2) {
944 error("You must specify two paths after a %s " 1136 error("You must specify two paths after a %s "
945 "command.", cmd); 1137 "command.", cmd);
946 return(-1); 1138 return -1;
947 } 1139 }
1140 *path1 = xstrdup(argv[optidx]);
1141 *path2 = xstrdup(argv[optidx + 1]);
1142 /* Paths are not globbed */
1143 undo_glob_escape(*path1);
1144 undo_glob_escape(*path2);
948 break; 1145 break;
949 case I_RM: 1146 case I_RM:
950 case I_MKDIR: 1147 case I_MKDIR:
@@ -953,59 +1150,69 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
953 case I_LCHDIR: 1150 case I_LCHDIR:
954 case I_LMKDIR: 1151 case I_LMKDIR:
955 /* Get pathname (mandatory) */ 1152 /* Get pathname (mandatory) */
956 if (get_pathname(&cp, path1)) 1153 if (argc - optidx < 1) {
957 return(-1);
958 if (*path1 == NULL) {
959 error("You must specify a path after a %s command.", 1154 error("You must specify a path after a %s command.",
960 cmd); 1155 cmd);
961 return(-1); 1156 return -1;
1157 }
1158 *path1 = xstrdup(argv[optidx]);
1159 /* Only "rm" globs */
1160 if (cmdnum != I_RM)
1161 undo_glob_escape(*path1);
1162 break;
1163 case I_DF:
1164 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1165 iflag)) == -1)
1166 return -1;
1167 /* Default to current directory if no path specified */
1168 if (argc - optidx < 1)
1169 *path1 = NULL;
1170 else {
1171 *path1 = xstrdup(argv[optidx]);
1172 undo_glob_escape(*path1);
962 } 1173 }
963 break; 1174 break;
964 case I_LS: 1175 case I_LS:
965 if (parse_ls_flags(&cp, lflag)) 1176 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
966 return(-1); 1177 return(-1);
967 /* Path is optional */ 1178 /* Path is optional */
968 if (get_pathname(&cp, path1)) 1179 if (argc - optidx > 0)
969 return(-1); 1180 *path1 = xstrdup(argv[optidx]);
970 break; 1181 break;
971 case I_LLS: 1182 case I_LLS:
1183 /* Skip ls command and following whitespace */
1184 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
972 case I_SHELL: 1185 case I_SHELL:
973 /* Uses the rest of the line */ 1186 /* Uses the rest of the line */
974 break; 1187 break;
975 case I_LUMASK: 1188 case I_LUMASK:
976 base = 8;
977 case I_CHMOD: 1189 case I_CHMOD:
978 base = 8; 1190 base = 8;
979 case I_CHOWN: 1191 case I_CHOWN:
980 case I_CHGRP: 1192 case I_CHGRP:
981 /* Get numeric arg (mandatory) */ 1193 /* Get numeric arg (mandatory) */
1194 if (argc - optidx < 1)
1195 goto need_num_arg;
982 errno = 0; 1196 errno = 0;
983 l = strtol(cp, &cp2, base); 1197 l = strtol(argv[optidx], &cp2, base);
984 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 1198 if (cp2 == argv[optidx] || *cp2 != '\0' ||
985 errno == ERANGE) || l < 0) { 1199 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1200 l < 0) {
1201 need_num_arg:
986 error("You must supply a numeric argument " 1202 error("You must supply a numeric argument "
987 "to the %s command.", cmd); 1203 "to the %s command.", cmd);
988 return(-1); 1204 return -1;
989 } 1205 }
990 cp = cp2;
991 *n_arg = l; 1206 *n_arg = l;
992 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 1207 if (cmdnum == I_LUMASK)
993 break; 1208 break;
994 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
995 error("You must supply a numeric argument "
996 "to the %s command.", cmd);
997 return(-1);
998 }
999 cp += strspn(cp, WHITESPACE);
1000
1001 /* Get pathname (mandatory) */ 1209 /* Get pathname (mandatory) */
1002 if (get_pathname(&cp, path1)) 1210 if (argc - optidx < 2) {
1003 return(-1);
1004 if (*path1 == NULL) {
1005 error("You must specify a path after a %s command.", 1211 error("You must specify a path after a %s command.",
1006 cmd); 1212 cmd);
1007 return(-1); 1213 return -1;
1008 } 1214 }
1215 *path1 = xstrdup(argv[optidx + 1]);
1009 break; 1216 break;
1010 case I_QUIT: 1217 case I_QUIT:
1011 case I_PWD: 1218 case I_PWD:
@@ -1027,7 +1234,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1027 int err_abort) 1234 int err_abort)
1028{ 1235{
1029 char *path1, *path2, *tmp; 1236 char *path1, *path2, *tmp;
1030 int pflag, lflag, iflag, cmdnum, i; 1237 int pflag, lflag, iflag, hflag, cmdnum, i;
1031 unsigned long n_arg; 1238 unsigned long n_arg;
1032 Attrib a, *aa; 1239 Attrib a, *aa;
1033 char path_buf[MAXPATHLEN]; 1240 char path_buf[MAXPATHLEN];
@@ -1035,7 +1242,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1035 glob_t g; 1242 glob_t g;
1036 1243
1037 path1 = path2 = NULL; 1244 path1 = path2 = NULL;
1038 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 1245 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
1039 &path1, &path2); 1246 &path1, &path2);
1040 1247
1041 if (iflag != 0) 1248 if (iflag != 0)
@@ -1129,6 +1336,13 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1129 path1 = make_absolute(path1, *pwd); 1336 path1 = make_absolute(path1, *pwd);
1130 err = do_globbed_ls(conn, path1, tmp, lflag); 1337 err = do_globbed_ls(conn, path1, tmp, lflag);
1131 break; 1338 break;
1339 case I_DF:
1340 /* Default to current directory if no path specified */
1341 if (path1 == NULL)
1342 path1 = xstrdup(*pwd);
1343 path1 = make_absolute(path1, *pwd);
1344 err = do_df(conn, path1, hflag, iflag);
1345 break;
1132 case I_LCHDIR: 1346 case I_LCHDIR:
1133 if (chdir(path1) == -1) { 1347 if (chdir(path1) == -1) {
1134 error("Couldn't change local directory to " 1348 error("Couldn't change local directory to "