diff options
Diffstat (limited to 'sftp.c')
-rw-r--r-- | sftp.c | 858 |
1 files changed, 640 insertions, 218 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker Exp $ */ | 1 | /* $OpenBSD: sftp.c,v 1.123 2010/01/27 19:21:39 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 | * |
@@ -35,6 +35,9 @@ | |||
35 | #ifdef HAVE_PATHS_H | 35 | #ifdef HAVE_PATHS_H |
36 | # include <paths.h> | 36 | # include <paths.h> |
37 | #endif | 37 | #endif |
38 | #ifdef HAVE_LIBGEN_H | ||
39 | #include <libgen.h> | ||
40 | #endif | ||
38 | #ifdef USE_LIBEDIT | 41 | #ifdef USE_LIBEDIT |
39 | #include <histedit.h> | 42 | #include <histedit.h> |
40 | #else | 43 | #else |
@@ -65,30 +68,39 @@ typedef void EditLine; | |||
65 | #include "sftp-common.h" | 68 | #include "sftp-common.h" |
66 | #include "sftp-client.h" | 69 | #include "sftp-client.h" |
67 | 70 | ||
71 | #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ | ||
72 | #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ | ||
73 | |||
68 | /* File to read commands from */ | 74 | /* File to read commands from */ |
69 | FILE* infile; | 75 | FILE* infile; |
70 | 76 | ||
71 | /* Are we in batchfile mode? */ | 77 | /* Are we in batchfile mode? */ |
72 | int batchmode = 0; | 78 | int batchmode = 0; |
73 | 79 | ||
74 | /* Size of buffer used when copying files */ | ||
75 | size_t copy_buffer_len = 32768; | ||
76 | |||
77 | /* Number of concurrent outstanding requests */ | ||
78 | size_t num_requests = 64; | ||
79 | |||
80 | /* PID of ssh transport process */ | 80 | /* PID of ssh transport process */ |
81 | static pid_t sshpid = -1; | 81 | static pid_t sshpid = -1; |
82 | 82 | ||
83 | /* This is set to 0 if the progressmeter is not desired. */ | 83 | /* This is set to 0 if the progressmeter is not desired. */ |
84 | int showprogress = 1; | 84 | int showprogress = 1; |
85 | 85 | ||
86 | /* When this option is set, we always recursively download/upload directories */ | ||
87 | int global_rflag = 0; | ||
88 | |||
89 | /* When this option is set, the file transfers will always preserve times */ | ||
90 | int global_pflag = 0; | ||
91 | |||
86 | /* SIGINT received during command processing */ | 92 | /* SIGINT received during command processing */ |
87 | volatile sig_atomic_t interrupted = 0; | 93 | volatile sig_atomic_t interrupted = 0; |
88 | 94 | ||
89 | /* I wish qsort() took a separate ctx for the comparison function...*/ | 95 | /* I wish qsort() took a separate ctx for the comparison function...*/ |
90 | int sort_flag; | 96 | int sort_flag; |
91 | 97 | ||
98 | /* Context used for commandline completion */ | ||
99 | struct complete_ctx { | ||
100 | struct sftp_conn *conn; | ||
101 | char **remote_pathp; | ||
102 | }; | ||
103 | |||
92 | int remote_glob(struct sftp_conn *, const char *, int, | 104 | int remote_glob(struct sftp_conn *, const char *, int, |
93 | int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ | 105 | int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ |
94 | 106 | ||
@@ -98,16 +110,17 @@ extern char *__progname; | |||
98 | #define WHITESPACE " \t\r\n" | 110 | #define WHITESPACE " \t\r\n" |
99 | 111 | ||
100 | /* ls flags */ | 112 | /* ls flags */ |
101 | #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ | 113 | #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ |
102 | #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ | 114 | #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ |
103 | #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ | 115 | #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ |
104 | #define LS_NAME_SORT 0x08 /* Sort by name (default) */ | 116 | #define LS_NAME_SORT 0x0008 /* Sort by name (default) */ |
105 | #define LS_TIME_SORT 0x10 /* Sort by mtime */ | 117 | #define LS_TIME_SORT 0x0010 /* Sort by mtime */ |
106 | #define LS_SIZE_SORT 0x20 /* Sort by file size */ | 118 | #define LS_SIZE_SORT 0x0020 /* Sort by file size */ |
107 | #define LS_REVERSE_SORT 0x40 /* Reverse sort order */ | 119 | #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ |
108 | #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ | 120 | #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ |
109 | 121 | #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ | |
110 | #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) | 122 | |
123 | #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) | ||
111 | #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) | 124 | #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) |
112 | 125 | ||
113 | /* Commands for interactive mode */ | 126 | /* Commands for interactive mode */ |
@@ -139,46 +152,50 @@ extern char *__progname; | |||
139 | struct CMD { | 152 | struct CMD { |
140 | const char *c; | 153 | const char *c; |
141 | const int n; | 154 | const int n; |
155 | const int t; | ||
142 | }; | 156 | }; |
143 | 157 | ||
158 | /* Type of completion */ | ||
159 | #define NOARGS 0 | ||
160 | #define REMOTE 1 | ||
161 | #define LOCAL 2 | ||
162 | |||
144 | static const struct CMD cmds[] = { | 163 | static const struct CMD cmds[] = { |
145 | { "bye", I_QUIT }, | 164 | { "bye", I_QUIT, NOARGS }, |
146 | { "cd", I_CHDIR }, | 165 | { "cd", I_CHDIR, REMOTE }, |
147 | { "chdir", I_CHDIR }, | 166 | { "chdir", I_CHDIR, REMOTE }, |
148 | { "chgrp", I_CHGRP }, | 167 | { "chgrp", I_CHGRP, REMOTE }, |
149 | { "chmod", I_CHMOD }, | 168 | { "chmod", I_CHMOD, REMOTE }, |
150 | { "chown", I_CHOWN }, | 169 | { "chown", I_CHOWN, REMOTE }, |
151 | { "df", I_DF }, | 170 | { "df", I_DF, REMOTE }, |
152 | { "dir", I_LS }, | 171 | { "dir", I_LS, REMOTE }, |
153 | { "exit", I_QUIT }, | 172 | { "exit", I_QUIT, NOARGS }, |
154 | { "get", I_GET }, | 173 | { "get", I_GET, REMOTE }, |
155 | { "mget", I_GET }, | 174 | { "help", I_HELP, NOARGS }, |
156 | { "help", I_HELP }, | 175 | { "lcd", I_LCHDIR, LOCAL }, |
157 | { "lcd", I_LCHDIR }, | 176 | { "lchdir", I_LCHDIR, LOCAL }, |
158 | { "lchdir", I_LCHDIR }, | 177 | { "lls", I_LLS, LOCAL }, |
159 | { "lls", I_LLS }, | 178 | { "lmkdir", I_LMKDIR, LOCAL }, |
160 | { "lmkdir", I_LMKDIR }, | 179 | { "ln", I_SYMLINK, REMOTE }, |
161 | { "ln", I_SYMLINK }, | 180 | { "lpwd", I_LPWD, LOCAL }, |
162 | { "lpwd", I_LPWD }, | 181 | { "ls", I_LS, REMOTE }, |
163 | { "ls", I_LS }, | 182 | { "lumask", I_LUMASK, NOARGS }, |
164 | { "lumask", I_LUMASK }, | 183 | { "mkdir", I_MKDIR, REMOTE }, |
165 | { "mkdir", I_MKDIR }, | 184 | { "progress", I_PROGRESS, NOARGS }, |
166 | { "progress", I_PROGRESS }, | 185 | { "put", I_PUT, LOCAL }, |
167 | { "put", I_PUT }, | 186 | { "pwd", I_PWD, REMOTE }, |
168 | { "mput", I_PUT }, | 187 | { "quit", I_QUIT, NOARGS }, |
169 | { "pwd", I_PWD }, | 188 | { "rename", I_RENAME, REMOTE }, |
170 | { "quit", I_QUIT }, | 189 | { "rm", I_RM, REMOTE }, |
171 | { "rename", I_RENAME }, | 190 | { "rmdir", I_RMDIR, REMOTE }, |
172 | { "rm", I_RM }, | 191 | { "symlink", I_SYMLINK, REMOTE }, |
173 | { "rmdir", I_RMDIR }, | 192 | { "version", I_VERSION, NOARGS }, |
174 | { "symlink", I_SYMLINK }, | 193 | { "!", I_SHELL, NOARGS }, |
175 | { "version", I_VERSION }, | 194 | { "?", I_HELP, NOARGS }, |
176 | { "!", I_SHELL }, | 195 | { NULL, -1, -1 } |
177 | { "?", I_HELP }, | ||
178 | { NULL, -1} | ||
179 | }; | 196 | }; |
180 | 197 | ||
181 | int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); | 198 | int interactive_loop(struct sftp_conn *, char *file1, char *file2); |
182 | 199 | ||
183 | /* ARGSUSED */ | 200 | /* ARGSUSED */ |
184 | static void | 201 | static void |
@@ -216,18 +233,18 @@ help(void) | |||
216 | "df [-hi] [path] Display statistics for current directory or\n" | 233 | "df [-hi] [path] Display statistics for current directory or\n" |
217 | " filesystem containing 'path'\n" | 234 | " filesystem containing 'path'\n" |
218 | "exit Quit sftp\n" | 235 | "exit Quit sftp\n" |
219 | "get [-P] remote-path [local-path] Download file\n" | 236 | "get [-Ppr] remote [local] Download file\n" |
220 | "help Display this help text\n" | 237 | "help Display this help text\n" |
221 | "lcd path Change local directory to 'path'\n" | 238 | "lcd path Change local directory to 'path'\n" |
222 | "lls [ls-options [path]] Display local directory listing\n" | 239 | "lls [ls-options [path]] Display local directory listing\n" |
223 | "lmkdir path Create local directory\n" | 240 | "lmkdir path Create local directory\n" |
224 | "ln oldpath newpath Symlink remote file\n" | 241 | "ln oldpath newpath Symlink remote file\n" |
225 | "lpwd Print local working directory\n" | 242 | "lpwd Print local working directory\n" |
226 | "ls [-1aflnrSt] [path] Display remote directory listing\n" | 243 | "ls [-1afhlnrSt] [path] Display remote directory listing\n" |
227 | "lumask umask Set local umask to 'umask'\n" | 244 | "lumask umask Set local umask to 'umask'\n" |
228 | "mkdir path Create remote directory\n" | 245 | "mkdir path Create remote directory\n" |
229 | "progress Toggle display of progress meter\n" | 246 | "progress Toggle display of progress meter\n" |
230 | "put [-P] local-path [remote-path] Upload file\n" | 247 | "put [-Ppr] local [remote] Upload file\n" |
231 | "pwd Display remote working directory\n" | 248 | "pwd Display remote working directory\n" |
232 | "quit Quit sftp\n" | 249 | "quit Quit sftp\n" |
233 | "rename oldpath newpath Rename remote file\n" | 250 | "rename oldpath newpath Rename remote file\n" |
@@ -314,21 +331,6 @@ path_strip(char *path, char *strip) | |||
314 | } | 331 | } |
315 | 332 | ||
316 | static char * | 333 | static char * |
317 | path_append(char *p1, char *p2) | ||
318 | { | ||
319 | char *ret; | ||
320 | size_t len = strlen(p1) + strlen(p2) + 2; | ||
321 | |||
322 | ret = xmalloc(len); | ||
323 | strlcpy(ret, p1, len); | ||
324 | if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') | ||
325 | strlcat(ret, "/", len); | ||
326 | strlcat(ret, p2, len); | ||
327 | |||
328 | return(ret); | ||
329 | } | ||
330 | |||
331 | static char * | ||
332 | make_absolute(char *p, char *pwd) | 334 | make_absolute(char *p, char *pwd) |
333 | { | 335 | { |
334 | char *abs_str; | 336 | char *abs_str; |
@@ -343,27 +345,8 @@ make_absolute(char *p, char *pwd) | |||
343 | } | 345 | } |
344 | 346 | ||
345 | static int | 347 | static int |
346 | infer_path(const char *p, char **ifp) | 348 | parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, |
347 | { | 349 | int *rflag) |
348 | char *cp; | ||
349 | |||
350 | cp = strrchr(p, '/'); | ||
351 | if (cp == NULL) { | ||
352 | *ifp = xstrdup(p); | ||
353 | return(0); | ||
354 | } | ||
355 | |||
356 | if (!cp[1]) { | ||
357 | error("Invalid path"); | ||
358 | return(-1); | ||
359 | } | ||
360 | |||
361 | *ifp = xstrdup(cp + 1); | ||
362 | return(0); | ||
363 | } | ||
364 | |||
365 | static int | ||
366 | parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) | ||
367 | { | 350 | { |
368 | extern int opterr, optind, optopt, optreset; | 351 | extern int opterr, optind, optopt, optreset; |
369 | int ch; | 352 | int ch; |
@@ -371,13 +354,17 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) | |||
371 | optind = optreset = 1; | 354 | optind = optreset = 1; |
372 | opterr = 0; | 355 | opterr = 0; |
373 | 356 | ||
374 | *pflag = 0; | 357 | *rflag = *pflag = 0; |
375 | while ((ch = getopt(argc, argv, "Pp")) != -1) { | 358 | while ((ch = getopt(argc, argv, "PpRr")) != -1) { |
376 | switch (ch) { | 359 | switch (ch) { |
377 | case 'p': | 360 | case 'p': |
378 | case 'P': | 361 | case 'P': |
379 | *pflag = 1; | 362 | *pflag = 1; |
380 | break; | 363 | break; |
364 | case 'r': | ||
365 | case 'R': | ||
366 | *rflag = 1; | ||
367 | break; | ||
381 | default: | 368 | default: |
382 | error("%s: Invalid flag -%c", cmd, optopt); | 369 | error("%s: Invalid flag -%c", cmd, optopt); |
383 | return -1; | 370 | return -1; |
@@ -397,7 +384,7 @@ parse_ls_flags(char **argv, int argc, int *lflag) | |||
397 | opterr = 0; | 384 | opterr = 0; |
398 | 385 | ||
399 | *lflag = LS_NAME_SORT; | 386 | *lflag = LS_NAME_SORT; |
400 | while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { | 387 | while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { |
401 | switch (ch) { | 388 | switch (ch) { |
402 | case '1': | 389 | case '1': |
403 | *lflag &= ~VIEW_FLAGS; | 390 | *lflag &= ~VIEW_FLAGS; |
@@ -413,12 +400,15 @@ parse_ls_flags(char **argv, int argc, int *lflag) | |||
413 | case 'f': | 400 | case 'f': |
414 | *lflag &= ~SORT_FLAGS; | 401 | *lflag &= ~SORT_FLAGS; |
415 | break; | 402 | break; |
403 | case 'h': | ||
404 | *lflag |= LS_SI_UNITS; | ||
405 | break; | ||
416 | case 'l': | 406 | case 'l': |
417 | *lflag &= ~VIEW_FLAGS; | 407 | *lflag &= ~LS_SHORT_VIEW; |
418 | *lflag |= LS_LONG_VIEW; | 408 | *lflag |= LS_LONG_VIEW; |
419 | break; | 409 | break; |
420 | case 'n': | 410 | case 'n': |
421 | *lflag &= ~VIEW_FLAGS; | 411 | *lflag &= ~LS_SHORT_VIEW; |
422 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; | 412 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; |
423 | break; | 413 | break; |
424 | case 'r': | 414 | case 'r': |
@@ -489,62 +479,79 @@ remote_is_dir(struct sftp_conn *conn, char *path) | |||
489 | return(S_ISDIR(a->perm)); | 479 | return(S_ISDIR(a->perm)); |
490 | } | 480 | } |
491 | 481 | ||
482 | /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ | ||
483 | static int | ||
484 | pathname_is_dir(char *pathname) | ||
485 | { | ||
486 | size_t l = strlen(pathname); | ||
487 | |||
488 | return l > 0 && pathname[l - 1] == '/'; | ||
489 | } | ||
490 | |||
492 | static int | 491 | static int |
493 | process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | 492 | process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, |
493 | int pflag, int rflag) | ||
494 | { | 494 | { |
495 | char *abs_src = NULL; | 495 | char *abs_src = NULL; |
496 | char *abs_dst = NULL; | 496 | char *abs_dst = NULL; |
497 | char *tmp; | ||
498 | glob_t g; | 497 | glob_t g; |
499 | int err = 0; | 498 | char *filename, *tmp=NULL; |
500 | int i; | 499 | int i, err = 0; |
501 | 500 | ||
502 | abs_src = xstrdup(src); | 501 | abs_src = xstrdup(src); |
503 | abs_src = make_absolute(abs_src, pwd); | 502 | abs_src = make_absolute(abs_src, pwd); |
504 | |||
505 | memset(&g, 0, sizeof(g)); | 503 | memset(&g, 0, sizeof(g)); |
504 | |||
506 | debug3("Looking up %s", abs_src); | 505 | debug3("Looking up %s", abs_src); |
507 | if (remote_glob(conn, abs_src, 0, NULL, &g)) { | 506 | if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) { |
508 | error("File \"%s\" not found.", abs_src); | 507 | error("File \"%s\" not found.", abs_src); |
509 | err = -1; | 508 | err = -1; |
510 | goto out; | 509 | goto out; |
511 | } | 510 | } |
512 | 511 | ||
513 | /* If multiple matches, dst must be a directory or unspecified */ | 512 | /* |
514 | if (g.gl_matchc > 1 && dst && !is_dir(dst)) { | 513 | * If multiple matches then dst must be a directory or |
515 | error("Multiple files match, but \"%s\" is not a directory", | 514 | * unspecified. |
516 | dst); | 515 | */ |
516 | if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { | ||
517 | error("Multiple source paths, but destination " | ||
518 | "\"%s\" is not a directory", dst); | ||
517 | err = -1; | 519 | err = -1; |
518 | goto out; | 520 | goto out; |
519 | } | 521 | } |
520 | 522 | ||
521 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { | 523 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { |
522 | if (infer_path(g.gl_pathv[i], &tmp)) { | 524 | tmp = xstrdup(g.gl_pathv[i]); |
525 | if ((filename = basename(tmp)) == NULL) { | ||
526 | error("basename %s: %s", tmp, strerror(errno)); | ||
527 | xfree(tmp); | ||
523 | err = -1; | 528 | err = -1; |
524 | goto out; | 529 | goto out; |
525 | } | 530 | } |
526 | 531 | ||
527 | if (g.gl_matchc == 1 && dst) { | 532 | if (g.gl_matchc == 1 && dst) { |
528 | /* If directory specified, append filename */ | ||
529 | xfree(tmp); | ||
530 | if (is_dir(dst)) { | 533 | if (is_dir(dst)) { |
531 | if (infer_path(g.gl_pathv[0], &tmp)) { | 534 | abs_dst = path_append(dst, filename); |
532 | err = 1; | 535 | } else { |
533 | goto out; | ||
534 | } | ||
535 | abs_dst = path_append(dst, tmp); | ||
536 | xfree(tmp); | ||
537 | } else | ||
538 | abs_dst = xstrdup(dst); | 536 | abs_dst = xstrdup(dst); |
537 | } | ||
539 | } else if (dst) { | 538 | } else if (dst) { |
540 | abs_dst = path_append(dst, tmp); | 539 | abs_dst = path_append(dst, filename); |
541 | xfree(tmp); | 540 | } else { |
542 | } else | 541 | abs_dst = xstrdup(filename); |
543 | abs_dst = tmp; | 542 | } |
543 | xfree(tmp); | ||
544 | 544 | ||
545 | printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); | 545 | printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); |
546 | if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) | 546 | if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { |
547 | err = -1; | 547 | if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, |
548 | pflag || global_pflag, 1) == -1) | ||
549 | err = -1; | ||
550 | } else { | ||
551 | if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, | ||
552 | pflag || global_pflag) == -1) | ||
553 | err = -1; | ||
554 | } | ||
548 | xfree(abs_dst); | 555 | xfree(abs_dst); |
549 | abs_dst = NULL; | 556 | abs_dst = NULL; |
550 | } | 557 | } |
@@ -556,14 +563,15 @@ out: | |||
556 | } | 563 | } |
557 | 564 | ||
558 | static int | 565 | static int |
559 | process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | 566 | process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, |
567 | int pflag, int rflag) | ||
560 | { | 568 | { |
561 | char *tmp_dst = NULL; | 569 | char *tmp_dst = NULL; |
562 | char *abs_dst = NULL; | 570 | char *abs_dst = NULL; |
563 | char *tmp; | 571 | char *tmp = NULL, *filename = NULL; |
564 | glob_t g; | 572 | glob_t g; |
565 | int err = 0; | 573 | int err = 0; |
566 | int i; | 574 | int i, dst_is_dir = 1; |
567 | struct stat sb; | 575 | struct stat sb; |
568 | 576 | ||
569 | if (dst) { | 577 | if (dst) { |
@@ -573,16 +581,20 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
573 | 581 | ||
574 | memset(&g, 0, sizeof(g)); | 582 | memset(&g, 0, sizeof(g)); |
575 | debug3("Looking up %s", src); | 583 | debug3("Looking up %s", src); |
576 | if (glob(src, GLOB_NOCHECK, NULL, &g)) { | 584 | if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { |
577 | error("File \"%s\" not found.", src); | 585 | error("File \"%s\" not found.", src); |
578 | err = -1; | 586 | err = -1; |
579 | goto out; | 587 | goto out; |
580 | } | 588 | } |
581 | 589 | ||
590 | /* If we aren't fetching to pwd then stash this status for later */ | ||
591 | if (tmp_dst != NULL) | ||
592 | dst_is_dir = remote_is_dir(conn, tmp_dst); | ||
593 | |||
582 | /* If multiple matches, dst may be directory or unspecified */ | 594 | /* If multiple matches, dst may be directory or unspecified */ |
583 | if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { | 595 | if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { |
584 | error("Multiple files match, but \"%s\" is not a directory", | 596 | error("Multiple paths match, but destination " |
585 | tmp_dst); | 597 | "\"%s\" is not a directory", tmp_dst); |
586 | err = -1; | 598 | err = -1; |
587 | goto out; | 599 | goto out; |
588 | } | 600 | } |
@@ -593,38 +605,38 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
593 | error("stat %s: %s", g.gl_pathv[i], strerror(errno)); | 605 | error("stat %s: %s", g.gl_pathv[i], strerror(errno)); |
594 | continue; | 606 | continue; |
595 | } | 607 | } |
596 | 608 | ||
597 | if (!S_ISREG(sb.st_mode)) { | 609 | tmp = xstrdup(g.gl_pathv[i]); |
598 | error("skipping non-regular file %s", | 610 | if ((filename = basename(tmp)) == NULL) { |
599 | g.gl_pathv[i]); | 611 | error("basename %s: %s", tmp, strerror(errno)); |
600 | continue; | 612 | xfree(tmp); |
601 | } | ||
602 | if (infer_path(g.gl_pathv[i], &tmp)) { | ||
603 | err = -1; | 613 | err = -1; |
604 | goto out; | 614 | goto out; |
605 | } | 615 | } |
606 | 616 | ||
607 | if (g.gl_matchc == 1 && tmp_dst) { | 617 | if (g.gl_matchc == 1 && tmp_dst) { |
608 | /* If directory specified, append filename */ | 618 | /* If directory specified, append filename */ |
609 | if (remote_is_dir(conn, tmp_dst)) { | 619 | if (dst_is_dir) |
610 | if (infer_path(g.gl_pathv[0], &tmp)) { | 620 | abs_dst = path_append(tmp_dst, filename); |
611 | err = 1; | 621 | else |
612 | goto out; | ||
613 | } | ||
614 | abs_dst = path_append(tmp_dst, tmp); | ||
615 | xfree(tmp); | ||
616 | } else | ||
617 | abs_dst = xstrdup(tmp_dst); | 622 | abs_dst = xstrdup(tmp_dst); |
618 | |||
619 | } else if (tmp_dst) { | 623 | } else if (tmp_dst) { |
620 | abs_dst = path_append(tmp_dst, tmp); | 624 | abs_dst = path_append(tmp_dst, filename); |
621 | xfree(tmp); | 625 | } else { |
622 | } else | 626 | abs_dst = make_absolute(xstrdup(filename), pwd); |
623 | abs_dst = make_absolute(tmp, pwd); | 627 | } |
628 | xfree(tmp); | ||
624 | 629 | ||
625 | printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); | 630 | printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); |
626 | if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) | 631 | if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { |
627 | err = -1; | 632 | if (upload_dir(conn, g.gl_pathv[i], abs_dst, |
633 | pflag || global_pflag, 1) == -1) | ||
634 | err = -1; | ||
635 | } else { | ||
636 | if (do_upload(conn, g.gl_pathv[i], abs_dst, | ||
637 | pflag || global_pflag) == -1) | ||
638 | err = -1; | ||
639 | } | ||
628 | } | 640 | } |
629 | 641 | ||
630 | out: | 642 | out: |
@@ -708,13 +720,14 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) | |||
708 | xfree(tmp); | 720 | xfree(tmp); |
709 | 721 | ||
710 | if (lflag & LS_LONG_VIEW) { | 722 | if (lflag & LS_LONG_VIEW) { |
711 | if (lflag & LS_NUMERIC_VIEW) { | 723 | if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) { |
712 | char *lname; | 724 | char *lname; |
713 | struct stat sb; | 725 | struct stat sb; |
714 | 726 | ||
715 | memset(&sb, 0, sizeof(sb)); | 727 | memset(&sb, 0, sizeof(sb)); |
716 | attrib_to_stat(&d[n]->a, &sb); | 728 | attrib_to_stat(&d[n]->a, &sb); |
717 | lname = ls_file(fname, &sb, 1); | 729 | lname = ls_file(fname, &sb, 1, |
730 | (lflag & LS_SI_UNITS)); | ||
718 | printf("%s\n", lname); | 731 | printf("%s\n", lname); |
719 | xfree(lname); | 732 | xfree(lname); |
720 | } else | 733 | } else |
@@ -816,7 +829,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
816 | a = do_lstat(conn, g.gl_pathv[i], 1); | 829 | a = do_lstat(conn, g.gl_pathv[i], 1); |
817 | if (a != NULL) | 830 | if (a != NULL) |
818 | attrib_to_stat(a, &sb); | 831 | attrib_to_stat(a, &sb); |
819 | lname = ls_file(fname, &sb, 1); | 832 | lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); |
820 | printf("%s\n", lname); | 833 | printf("%s\n", lname); |
821 | xfree(lname); | 834 | xfree(lname); |
822 | } else { | 835 | } else { |
@@ -848,19 +861,19 @@ do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) | |||
848 | char s_avail[FMT_SCALED_STRSIZE]; | 861 | char s_avail[FMT_SCALED_STRSIZE]; |
849 | char s_root[FMT_SCALED_STRSIZE]; | 862 | char s_root[FMT_SCALED_STRSIZE]; |
850 | char s_total[FMT_SCALED_STRSIZE]; | 863 | char s_total[FMT_SCALED_STRSIZE]; |
864 | unsigned long long ffree; | ||
851 | 865 | ||
852 | if (do_statvfs(conn, path, &st, 1) == -1) | 866 | if (do_statvfs(conn, path, &st, 1) == -1) |
853 | return -1; | 867 | return -1; |
854 | if (iflag) { | 868 | if (iflag) { |
869 | ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0; | ||
855 | printf(" Inodes Used Avail " | 870 | printf(" Inodes Used Avail " |
856 | "(root) %%Capacity\n"); | 871 | "(root) %%Capacity\n"); |
857 | printf("%11llu %11llu %11llu %11llu %3llu%%\n", | 872 | printf("%11llu %11llu %11llu %11llu %3llu%%\n", |
858 | (unsigned long long)st.f_files, | 873 | (unsigned long long)st.f_files, |
859 | (unsigned long long)(st.f_files - st.f_ffree), | 874 | (unsigned long long)(st.f_files - st.f_ffree), |
860 | (unsigned long long)st.f_favail, | 875 | (unsigned long long)st.f_favail, |
861 | (unsigned long long)st.f_ffree, | 876 | (unsigned long long)st.f_ffree, ffree); |
862 | (unsigned long long)(100 * (st.f_files - st.f_ffree) / | ||
863 | st.f_files)); | ||
864 | } else if (hflag) { | 877 | } else if (hflag) { |
865 | strlcpy(s_used, "error", sizeof(s_used)); | 878 | strlcpy(s_used, "error", sizeof(s_used)); |
866 | strlcpy(s_avail, "error", sizeof(s_avail)); | 879 | strlcpy(s_avail, "error", sizeof(s_avail)); |
@@ -934,12 +947,23 @@ undo_glob_escape(char *s) | |||
934 | * Split a string into an argument vector using sh(1)-style quoting, | 947 | * Split a string into an argument vector using sh(1)-style quoting, |
935 | * comment and escaping rules, but with some tweaks to handle glob(3) | 948 | * comment and escaping rules, but with some tweaks to handle glob(3) |
936 | * wildcards. | 949 | * wildcards. |
950 | * The "sloppy" flag allows for recovery from missing terminating quote, for | ||
951 | * use in parsing incomplete commandlines during tab autocompletion. | ||
952 | * | ||
937 | * Returns NULL on error or a NULL-terminated array of arguments. | 953 | * Returns NULL on error or a NULL-terminated array of arguments. |
954 | * | ||
955 | * If "lastquote" is not NULL, the quoting character used for the last | ||
956 | * argument is placed in *lastquote ("\0", "'" or "\""). | ||
957 | * | ||
958 | * If "terminated" is not NULL, *terminated will be set to 1 when the | ||
959 | * last argument's quote has been properly terminated or 0 otherwise. | ||
960 | * This parameter is only of use if "sloppy" is set. | ||
938 | */ | 961 | */ |
939 | #define MAXARGS 128 | 962 | #define MAXARGS 128 |
940 | #define MAXARGLEN 8192 | 963 | #define MAXARGLEN 8192 |
941 | static char ** | 964 | static char ** |
942 | makeargv(const char *arg, int *argcp) | 965 | makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, |
966 | u_int *terminated) | ||
943 | { | 967 | { |
944 | int argc, quot; | 968 | int argc, quot; |
945 | size_t i, j; | 969 | size_t i, j; |
@@ -953,6 +977,10 @@ makeargv(const char *arg, int *argcp) | |||
953 | error("string too long"); | 977 | error("string too long"); |
954 | return NULL; | 978 | return NULL; |
955 | } | 979 | } |
980 | if (terminated != NULL) | ||
981 | *terminated = 1; | ||
982 | if (lastquote != NULL) | ||
983 | *lastquote = '\0'; | ||
956 | state = MA_START; | 984 | state = MA_START; |
957 | i = j = 0; | 985 | i = j = 0; |
958 | for (;;) { | 986 | for (;;) { |
@@ -969,6 +997,8 @@ makeargv(const char *arg, int *argcp) | |||
969 | if (state == MA_START) { | 997 | if (state == MA_START) { |
970 | argv[argc] = argvs + j; | 998 | argv[argc] = argvs + j; |
971 | state = q; | 999 | state = q; |
1000 | if (lastquote != NULL) | ||
1001 | *lastquote = arg[i]; | ||
972 | } else if (state == MA_UNQUOTED) | 1002 | } else if (state == MA_UNQUOTED) |
973 | state = q; | 1003 | state = q; |
974 | else if (state == q) | 1004 | else if (state == q) |
@@ -1005,6 +1035,8 @@ makeargv(const char *arg, int *argcp) | |||
1005 | if (state == MA_START) { | 1035 | if (state == MA_START) { |
1006 | argv[argc] = argvs + j; | 1036 | argv[argc] = argvs + j; |
1007 | state = MA_UNQUOTED; | 1037 | state = MA_UNQUOTED; |
1038 | if (lastquote != NULL) | ||
1039 | *lastquote = '\0'; | ||
1008 | } | 1040 | } |
1009 | if (arg[i + 1] == '?' || arg[i + 1] == '[' || | 1041 | if (arg[i + 1] == '?' || arg[i + 1] == '[' || |
1010 | arg[i + 1] == '*' || arg[i + 1] == '\\') { | 1042 | arg[i + 1] == '*' || arg[i + 1] == '\\') { |
@@ -1030,6 +1062,12 @@ makeargv(const char *arg, int *argcp) | |||
1030 | goto string_done; | 1062 | goto string_done; |
1031 | } else if (arg[i] == '\0') { | 1063 | } else if (arg[i] == '\0') { |
1032 | if (state == MA_SQUOTE || state == MA_DQUOTE) { | 1064 | if (state == MA_SQUOTE || state == MA_DQUOTE) { |
1065 | if (sloppy) { | ||
1066 | state = MA_UNQUOTED; | ||
1067 | if (terminated != NULL) | ||
1068 | *terminated = 0; | ||
1069 | goto string_done; | ||
1070 | } | ||
1033 | error("Unterminated quoted argument"); | 1071 | error("Unterminated quoted argument"); |
1034 | return NULL; | 1072 | return NULL; |
1035 | } | 1073 | } |
@@ -1043,6 +1081,8 @@ makeargv(const char *arg, int *argcp) | |||
1043 | if (state == MA_START) { | 1081 | if (state == MA_START) { |
1044 | argv[argc] = argvs + j; | 1082 | argv[argc] = argvs + j; |
1045 | state = MA_UNQUOTED; | 1083 | state = MA_UNQUOTED; |
1084 | if (lastquote != NULL) | ||
1085 | *lastquote = '\0'; | ||
1046 | } | 1086 | } |
1047 | if ((state == MA_SQUOTE || state == MA_DQUOTE) && | 1087 | if ((state == MA_SQUOTE || state == MA_DQUOTE) && |
1048 | (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { | 1088 | (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { |
@@ -1065,8 +1105,8 @@ makeargv(const char *arg, int *argcp) | |||
1065 | } | 1105 | } |
1066 | 1106 | ||
1067 | static int | 1107 | static int |
1068 | parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, | 1108 | parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, |
1069 | unsigned long *n_arg, char **path1, char **path2) | 1109 | int *hflag, unsigned long *n_arg, char **path1, char **path2) |
1070 | { | 1110 | { |
1071 | const char *cmd, *cp = *cpp; | 1111 | const char *cmd, *cp = *cpp; |
1072 | char *cp2, **argv; | 1112 | char *cp2, **argv; |
@@ -1077,18 +1117,19 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, | |||
1077 | /* Skip leading whitespace */ | 1117 | /* Skip leading whitespace */ |
1078 | cp = cp + strspn(cp, WHITESPACE); | 1118 | cp = cp + strspn(cp, WHITESPACE); |
1079 | 1119 | ||
1080 | /* Ignore blank lines and lines which begin with comment '#' char */ | ||
1081 | if (*cp == '\0' || *cp == '#') | ||
1082 | return (0); | ||
1083 | |||
1084 | /* Check for leading '-' (disable error processing) */ | 1120 | /* Check for leading '-' (disable error processing) */ |
1085 | *iflag = 0; | 1121 | *iflag = 0; |
1086 | if (*cp == '-') { | 1122 | if (*cp == '-') { |
1087 | *iflag = 1; | 1123 | *iflag = 1; |
1088 | cp++; | 1124 | cp++; |
1125 | cp = cp + strspn(cp, WHITESPACE); | ||
1089 | } | 1126 | } |
1090 | 1127 | ||
1091 | if ((argv = makeargv(cp, &argc)) == NULL) | 1128 | /* Ignore blank lines and lines which begin with comment '#' char */ |
1129 | if (*cp == '\0' || *cp == '#') | ||
1130 | return (0); | ||
1131 | |||
1132 | if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) | ||
1092 | return -1; | 1133 | return -1; |
1093 | 1134 | ||
1094 | /* Figure out which command we have */ | 1135 | /* Figure out which command we have */ |
@@ -1109,13 +1150,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, | |||
1109 | } | 1150 | } |
1110 | 1151 | ||
1111 | /* Get arguments and parse flags */ | 1152 | /* Get arguments and parse flags */ |
1112 | *lflag = *pflag = *hflag = *n_arg = 0; | 1153 | *lflag = *pflag = *rflag = *hflag = *n_arg = 0; |
1113 | *path1 = *path2 = NULL; | 1154 | *path1 = *path2 = NULL; |
1114 | optidx = 1; | 1155 | optidx = 1; |
1115 | switch (cmdnum) { | 1156 | switch (cmdnum) { |
1116 | case I_GET: | 1157 | case I_GET: |
1117 | case I_PUT: | 1158 | case I_PUT: |
1118 | if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) | 1159 | if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1) |
1119 | return -1; | 1160 | return -1; |
1120 | /* Get first pathname (mandatory) */ | 1161 | /* Get first pathname (mandatory) */ |
1121 | if (argc - optidx < 1) { | 1162 | if (argc - optidx < 1) { |
@@ -1235,7 +1276,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1235 | int err_abort) | 1276 | int err_abort) |
1236 | { | 1277 | { |
1237 | char *path1, *path2, *tmp; | 1278 | char *path1, *path2, *tmp; |
1238 | int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; | 1279 | int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; |
1239 | unsigned long n_arg = 0; | 1280 | unsigned long n_arg = 0; |
1240 | Attrib a, *aa; | 1281 | Attrib a, *aa; |
1241 | char path_buf[MAXPATHLEN]; | 1282 | char path_buf[MAXPATHLEN]; |
@@ -1243,7 +1284,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1243 | glob_t g; | 1284 | glob_t g; |
1244 | 1285 | ||
1245 | path1 = path2 = NULL; | 1286 | path1 = path2 = NULL; |
1246 | cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, | 1287 | cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg, |
1247 | &path1, &path2); | 1288 | &path1, &path2); |
1248 | 1289 | ||
1249 | if (iflag != 0) | 1290 | if (iflag != 0) |
@@ -1261,10 +1302,10 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1261 | err = -1; | 1302 | err = -1; |
1262 | break; | 1303 | break; |
1263 | case I_GET: | 1304 | case I_GET: |
1264 | err = process_get(conn, path1, path2, *pwd, pflag); | 1305 | err = process_get(conn, path1, path2, *pwd, pflag, rflag); |
1265 | break; | 1306 | break; |
1266 | case I_PUT: | 1307 | case I_PUT: |
1267 | err = process_put(conn, path1, path2, *pwd, pflag); | 1308 | err = process_put(conn, path1, path2, *pwd, pflag, rflag); |
1268 | break; | 1309 | break; |
1269 | case I_RENAME: | 1310 | case I_RENAME: |
1270 | path1 = make_absolute(path1, *pwd); | 1311 | path1 = make_absolute(path1, *pwd); |
@@ -1290,7 +1331,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1290 | attrib_clear(&a); | 1331 | attrib_clear(&a); |
1291 | a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; | 1332 | a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; |
1292 | a.perm = 0777; | 1333 | a.perm = 0777; |
1293 | err = do_mkdir(conn, path1, &a); | 1334 | err = do_mkdir(conn, path1, &a, 1); |
1294 | break; | 1335 | break; |
1295 | case I_RMDIR: | 1336 | case I_RMDIR: |
1296 | path1 = make_absolute(path1, *pwd); | 1337 | path1 = make_absolute(path1, *pwd); |
@@ -1468,21 +1509,352 @@ prompt(EditLine *el) | |||
1468 | { | 1509 | { |
1469 | return ("sftp> "); | 1510 | return ("sftp> "); |
1470 | } | 1511 | } |
1471 | #endif | 1512 | |
1513 | /* Display entries in 'list' after skipping the first 'len' chars */ | ||
1514 | static void | ||
1515 | complete_display(char **list, u_int len) | ||
1516 | { | ||
1517 | u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; | ||
1518 | struct winsize ws; | ||
1519 | char *tmp; | ||
1520 | |||
1521 | /* Count entries for sort and find longest */ | ||
1522 | for (y = 0; list[y]; y++) | ||
1523 | m = MAX(m, strlen(list[y])); | ||
1524 | |||
1525 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) | ||
1526 | width = ws.ws_col; | ||
1527 | |||
1528 | m = m > len ? m - len : 0; | ||
1529 | columns = width / (m + 2); | ||
1530 | columns = MAX(columns, 1); | ||
1531 | colspace = width / columns; | ||
1532 | colspace = MIN(colspace, width); | ||
1533 | |||
1534 | printf("\n"); | ||
1535 | m = 1; | ||
1536 | for (y = 0; list[y]; y++) { | ||
1537 | llen = strlen(list[y]); | ||
1538 | tmp = llen > len ? list[y] + len : ""; | ||
1539 | printf("%-*s", colspace, tmp); | ||
1540 | if (m >= columns) { | ||
1541 | printf("\n"); | ||
1542 | m = 1; | ||
1543 | } else | ||
1544 | m++; | ||
1545 | } | ||
1546 | printf("\n"); | ||
1547 | } | ||
1548 | |||
1549 | /* | ||
1550 | * Given a "list" of words that begin with a common prefix of "word", | ||
1551 | * attempt to find an autocompletion to extends "word" by the next | ||
1552 | * characters common to all entries in "list". | ||
1553 | */ | ||
1554 | static char * | ||
1555 | complete_ambiguous(const char *word, char **list, size_t count) | ||
1556 | { | ||
1557 | if (word == NULL) | ||
1558 | return NULL; | ||
1559 | |||
1560 | if (count > 0) { | ||
1561 | u_int y, matchlen = strlen(list[0]); | ||
1562 | |||
1563 | /* Find length of common stem */ | ||
1564 | for (y = 1; list[y]; y++) { | ||
1565 | u_int x; | ||
1566 | |||
1567 | for (x = 0; x < matchlen; x++) | ||
1568 | if (list[0][x] != list[y][x]) | ||
1569 | break; | ||
1570 | |||
1571 | matchlen = x; | ||
1572 | } | ||
1573 | |||
1574 | if (matchlen > strlen(word)) { | ||
1575 | char *tmp = xstrdup(list[0]); | ||
1576 | |||
1577 | tmp[matchlen] = '\0'; | ||
1578 | return tmp; | ||
1579 | } | ||
1580 | } | ||
1581 | |||
1582 | return xstrdup(word); | ||
1583 | } | ||
1584 | |||
1585 | /* Autocomplete a sftp command */ | ||
1586 | static int | ||
1587 | complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, | ||
1588 | int terminated) | ||
1589 | { | ||
1590 | u_int y, count = 0, cmdlen, tmplen; | ||
1591 | char *tmp, **list, argterm[3]; | ||
1592 | const LineInfo *lf; | ||
1593 | |||
1594 | list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); | ||
1595 | |||
1596 | /* No command specified: display all available commands */ | ||
1597 | if (cmd == NULL) { | ||
1598 | for (y = 0; cmds[y].c; y++) | ||
1599 | list[count++] = xstrdup(cmds[y].c); | ||
1600 | |||
1601 | list[count] = NULL; | ||
1602 | complete_display(list, 0); | ||
1603 | |||
1604 | for (y = 0; list[y] != NULL; y++) | ||
1605 | xfree(list[y]); | ||
1606 | xfree(list); | ||
1607 | return count; | ||
1608 | } | ||
1609 | |||
1610 | /* Prepare subset of commands that start with "cmd" */ | ||
1611 | cmdlen = strlen(cmd); | ||
1612 | for (y = 0; cmds[y].c; y++) { | ||
1613 | if (!strncasecmp(cmd, cmds[y].c, cmdlen)) | ||
1614 | list[count++] = xstrdup(cmds[y].c); | ||
1615 | } | ||
1616 | list[count] = NULL; | ||
1617 | |||
1618 | if (count == 0) | ||
1619 | return 0; | ||
1620 | |||
1621 | /* Complete ambigious command */ | ||
1622 | tmp = complete_ambiguous(cmd, list, count); | ||
1623 | if (count > 1) | ||
1624 | complete_display(list, 0); | ||
1625 | |||
1626 | for (y = 0; list[y]; y++) | ||
1627 | xfree(list[y]); | ||
1628 | xfree(list); | ||
1629 | |||
1630 | if (tmp != NULL) { | ||
1631 | tmplen = strlen(tmp); | ||
1632 | cmdlen = strlen(cmd); | ||
1633 | /* If cmd may be extended then do so */ | ||
1634 | if (tmplen > cmdlen) | ||
1635 | if (el_insertstr(el, tmp + cmdlen) == -1) | ||
1636 | fatal("el_insertstr failed."); | ||
1637 | lf = el_line(el); | ||
1638 | /* Terminate argument cleanly */ | ||
1639 | if (count == 1) { | ||
1640 | y = 0; | ||
1641 | if (!terminated) | ||
1642 | argterm[y++] = quote; | ||
1643 | if (lastarg || *(lf->cursor) != ' ') | ||
1644 | argterm[y++] = ' '; | ||
1645 | argterm[y] = '\0'; | ||
1646 | if (y > 0 && el_insertstr(el, argterm) == -1) | ||
1647 | fatal("el_insertstr failed."); | ||
1648 | } | ||
1649 | xfree(tmp); | ||
1650 | } | ||
1651 | |||
1652 | return count; | ||
1653 | } | ||
1654 | |||
1655 | /* | ||
1656 | * Determine whether a particular sftp command's arguments (if any) | ||
1657 | * represent local or remote files. | ||
1658 | */ | ||
1659 | static int | ||
1660 | complete_is_remote(char *cmd) { | ||
1661 | int i; | ||
1662 | |||
1663 | if (cmd == NULL) | ||
1664 | return -1; | ||
1665 | |||
1666 | for (i = 0; cmds[i].c; i++) { | ||
1667 | if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) | ||
1668 | return cmds[i].t; | ||
1669 | } | ||
1670 | |||
1671 | return -1; | ||
1672 | } | ||
1673 | |||
1674 | /* Autocomplete a filename "file" */ | ||
1675 | static int | ||
1676 | complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, | ||
1677 | char *file, int remote, int lastarg, char quote, int terminated) | ||
1678 | { | ||
1679 | glob_t g; | ||
1680 | char *tmp, *tmp2, ins[3]; | ||
1681 | u_int i, hadglob, pwdlen, len, tmplen, filelen; | ||
1682 | const LineInfo *lf; | ||
1683 | |||
1684 | /* Glob from "file" location */ | ||
1685 | if (file == NULL) | ||
1686 | tmp = xstrdup("*"); | ||
1687 | else | ||
1688 | xasprintf(&tmp, "%s*", file); | ||
1689 | |||
1690 | memset(&g, 0, sizeof(g)); | ||
1691 | if (remote != LOCAL) { | ||
1692 | tmp = make_absolute(tmp, remote_path); | ||
1693 | remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); | ||
1694 | } else | ||
1695 | glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); | ||
1696 | |||
1697 | /* Determine length of pwd so we can trim completion display */ | ||
1698 | for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { | ||
1699 | /* Terminate counting on first unescaped glob metacharacter */ | ||
1700 | if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { | ||
1701 | if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') | ||
1702 | hadglob = 1; | ||
1703 | break; | ||
1704 | } | ||
1705 | if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') | ||
1706 | tmplen++; | ||
1707 | if (tmp[tmplen] == '/') | ||
1708 | pwdlen = tmplen + 1; /* track last seen '/' */ | ||
1709 | } | ||
1710 | xfree(tmp); | ||
1711 | |||
1712 | if (g.gl_matchc == 0) | ||
1713 | goto out; | ||
1714 | |||
1715 | if (g.gl_matchc > 1) | ||
1716 | complete_display(g.gl_pathv, pwdlen); | ||
1717 | |||
1718 | tmp = NULL; | ||
1719 | /* Don't try to extend globs */ | ||
1720 | if (file == NULL || hadglob) | ||
1721 | goto out; | ||
1722 | |||
1723 | tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); | ||
1724 | tmp = path_strip(tmp2, remote_path); | ||
1725 | xfree(tmp2); | ||
1726 | |||
1727 | if (tmp == NULL) | ||
1728 | goto out; | ||
1729 | |||
1730 | tmplen = strlen(tmp); | ||
1731 | filelen = strlen(file); | ||
1732 | |||
1733 | if (tmplen > filelen) { | ||
1734 | tmp2 = tmp + filelen; | ||
1735 | len = strlen(tmp2); | ||
1736 | /* quote argument on way out */ | ||
1737 | for (i = 0; i < len; i++) { | ||
1738 | ins[0] = '\\'; | ||
1739 | ins[1] = tmp2[i]; | ||
1740 | ins[2] = '\0'; | ||
1741 | switch (tmp2[i]) { | ||
1742 | case '\'': | ||
1743 | case '"': | ||
1744 | case '\\': | ||
1745 | case '\t': | ||
1746 | case ' ': | ||
1747 | if (quote == '\0' || tmp2[i] == quote) { | ||
1748 | if (el_insertstr(el, ins) == -1) | ||
1749 | fatal("el_insertstr " | ||
1750 | "failed."); | ||
1751 | break; | ||
1752 | } | ||
1753 | /* FALLTHROUGH */ | ||
1754 | default: | ||
1755 | if (el_insertstr(el, ins + 1) == -1) | ||
1756 | fatal("el_insertstr failed."); | ||
1757 | break; | ||
1758 | } | ||
1759 | } | ||
1760 | } | ||
1761 | |||
1762 | lf = el_line(el); | ||
1763 | if (g.gl_matchc == 1) { | ||
1764 | i = 0; | ||
1765 | if (!terminated) | ||
1766 | ins[i++] = quote; | ||
1767 | if (*(lf->cursor - 1) != '/' && | ||
1768 | (lastarg || *(lf->cursor) != ' ')) | ||
1769 | ins[i++] = ' '; | ||
1770 | ins[i] = '\0'; | ||
1771 | if (i > 0 && el_insertstr(el, ins) == -1) | ||
1772 | fatal("el_insertstr failed."); | ||
1773 | } | ||
1774 | xfree(tmp); | ||
1775 | |||
1776 | out: | ||
1777 | globfree(&g); | ||
1778 | return g.gl_matchc; | ||
1779 | } | ||
1780 | |||
1781 | /* tab-completion hook function, called via libedit */ | ||
1782 | static unsigned char | ||
1783 | complete(EditLine *el, int ch) | ||
1784 | { | ||
1785 | char **argv, *line, quote; | ||
1786 | u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; | ||
1787 | const LineInfo *lf; | ||
1788 | struct complete_ctx *complete_ctx; | ||
1789 | |||
1790 | lf = el_line(el); | ||
1791 | if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) | ||
1792 | fatal("%s: el_get failed", __func__); | ||
1793 | |||
1794 | /* Figure out which argument the cursor points to */ | ||
1795 | cursor = lf->cursor - lf->buffer; | ||
1796 | line = (char *)xmalloc(cursor + 1); | ||
1797 | memcpy(line, lf->buffer, cursor); | ||
1798 | line[cursor] = '\0'; | ||
1799 | argv = makeargv(line, &carg, 1, "e, &terminated); | ||
1800 | xfree(line); | ||
1801 | |||
1802 | /* Get all the arguments on the line */ | ||
1803 | len = lf->lastchar - lf->buffer; | ||
1804 | line = (char *)xmalloc(len + 1); | ||
1805 | memcpy(line, lf->buffer, len); | ||
1806 | line[len] = '\0'; | ||
1807 | argv = makeargv(line, &argc, 1, NULL, NULL); | ||
1808 | |||
1809 | /* Ensure cursor is at EOL or a argument boundary */ | ||
1810 | if (line[cursor] != ' ' && line[cursor] != '\0' && | ||
1811 | line[cursor] != '\n') { | ||
1812 | xfree(line); | ||
1813 | return ret; | ||
1814 | } | ||
1815 | |||
1816 | if (carg == 0) { | ||
1817 | /* Show all available commands */ | ||
1818 | complete_cmd_parse(el, NULL, argc == carg, '\0', 1); | ||
1819 | ret = CC_REDISPLAY; | ||
1820 | } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { | ||
1821 | /* Handle the command parsing */ | ||
1822 | if (complete_cmd_parse(el, argv[0], argc == carg, | ||
1823 | quote, terminated) != 0) | ||
1824 | ret = CC_REDISPLAY; | ||
1825 | } else if (carg >= 1) { | ||
1826 | /* Handle file parsing */ | ||
1827 | int remote = complete_is_remote(argv[0]); | ||
1828 | char *filematch = NULL; | ||
1829 | |||
1830 | if (carg > 1 && line[cursor-1] != ' ') | ||
1831 | filematch = argv[carg - 1]; | ||
1832 | |||
1833 | if (remote != 0 && | ||
1834 | complete_match(el, complete_ctx->conn, | ||
1835 | *complete_ctx->remote_pathp, filematch, | ||
1836 | remote, carg == argc, quote, terminated) != 0) | ||
1837 | ret = CC_REDISPLAY; | ||
1838 | } | ||
1839 | |||
1840 | xfree(line); | ||
1841 | return ret; | ||
1842 | } | ||
1843 | #endif /* USE_LIBEDIT */ | ||
1472 | 1844 | ||
1473 | int | 1845 | int |
1474 | interactive_loop(int fd_in, int fd_out, char *file1, char *file2) | 1846 | interactive_loop(struct sftp_conn *conn, char *file1, char *file2) |
1475 | { | 1847 | { |
1476 | char *pwd; | 1848 | char *remote_path; |
1477 | char *dir = NULL; | 1849 | char *dir = NULL; |
1478 | char cmd[2048]; | 1850 | char cmd[2048]; |
1479 | struct sftp_conn *conn; | ||
1480 | int err, interactive; | 1851 | int err, interactive; |
1481 | EditLine *el = NULL; | 1852 | EditLine *el = NULL; |
1482 | #ifdef USE_LIBEDIT | 1853 | #ifdef USE_LIBEDIT |
1483 | History *hl = NULL; | 1854 | History *hl = NULL; |
1484 | HistEvent hev; | 1855 | HistEvent hev; |
1485 | extern char *__progname; | 1856 | extern char *__progname; |
1857 | struct complete_ctx complete_ctx; | ||
1486 | 1858 | ||
1487 | if (!batchmode && isatty(STDIN_FILENO)) { | 1859 | if (!batchmode && isatty(STDIN_FILENO)) { |
1488 | if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) | 1860 | if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) |
@@ -1497,27 +1869,32 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) | |||
1497 | el_set(el, EL_TERMINAL, NULL); | 1869 | el_set(el, EL_TERMINAL, NULL); |
1498 | el_set(el, EL_SIGNAL, 1); | 1870 | el_set(el, EL_SIGNAL, 1); |
1499 | el_source(el, NULL); | 1871 | el_source(el, NULL); |
1872 | |||
1873 | /* Tab Completion */ | ||
1874 | el_set(el, EL_ADDFN, "ftp-complete", | ||
1875 | "Context senstive argument completion", complete); | ||
1876 | complete_ctx.conn = conn; | ||
1877 | complete_ctx.remote_pathp = &remote_path; | ||
1878 | el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); | ||
1879 | el_set(el, EL_BIND, "^I", "ftp-complete", NULL); | ||
1500 | } | 1880 | } |
1501 | #endif /* USE_LIBEDIT */ | 1881 | #endif /* USE_LIBEDIT */ |
1502 | 1882 | ||
1503 | conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); | 1883 | remote_path = do_realpath(conn, "."); |
1504 | if (conn == NULL) | 1884 | if (remote_path == NULL) |
1505 | fatal("Couldn't initialise connection to server"); | ||
1506 | |||
1507 | pwd = do_realpath(conn, "."); | ||
1508 | if (pwd == NULL) | ||
1509 | fatal("Need cwd"); | 1885 | fatal("Need cwd"); |
1510 | 1886 | ||
1511 | if (file1 != NULL) { | 1887 | if (file1 != NULL) { |
1512 | dir = xstrdup(file1); | 1888 | dir = xstrdup(file1); |
1513 | dir = make_absolute(dir, pwd); | 1889 | dir = make_absolute(dir, remote_path); |
1514 | 1890 | ||
1515 | if (remote_is_dir(conn, dir) && file2 == NULL) { | 1891 | if (remote_is_dir(conn, dir) && file2 == NULL) { |
1516 | printf("Changing to: %s\n", dir); | 1892 | printf("Changing to: %s\n", dir); |
1517 | snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); | 1893 | snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); |
1518 | if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { | 1894 | if (parse_dispatch_command(conn, cmd, |
1895 | &remote_path, 1) != 0) { | ||
1519 | xfree(dir); | 1896 | xfree(dir); |
1520 | xfree(pwd); | 1897 | xfree(remote_path); |
1521 | xfree(conn); | 1898 | xfree(conn); |
1522 | return (-1); | 1899 | return (-1); |
1523 | } | 1900 | } |
@@ -1528,9 +1905,10 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) | |||
1528 | snprintf(cmd, sizeof cmd, "get %s %s", dir, | 1905 | snprintf(cmd, sizeof cmd, "get %s %s", dir, |
1529 | file2); | 1906 | file2); |
1530 | 1907 | ||
1531 | err = parse_dispatch_command(conn, cmd, &pwd, 1); | 1908 | err = parse_dispatch_command(conn, cmd, |
1909 | &remote_path, 1); | ||
1532 | xfree(dir); | 1910 | xfree(dir); |
1533 | xfree(pwd); | 1911 | xfree(remote_path); |
1534 | xfree(conn); | 1912 | xfree(conn); |
1535 | return (err); | 1913 | return (err); |
1536 | } | 1914 | } |
@@ -1571,7 +1949,8 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) | |||
1571 | const char *line; | 1949 | const char *line; |
1572 | int count = 0; | 1950 | int count = 0; |
1573 | 1951 | ||
1574 | if ((line = el_gets(el, &count)) == NULL || count <= 0) { | 1952 | if ((line = el_gets(el, &count)) == NULL || |
1953 | count <= 0) { | ||
1575 | printf("\n"); | 1954 | printf("\n"); |
1576 | break; | 1955 | break; |
1577 | } | 1956 | } |
@@ -1591,11 +1970,12 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) | |||
1591 | interrupted = 0; | 1970 | interrupted = 0; |
1592 | signal(SIGINT, cmd_interrupt); | 1971 | signal(SIGINT, cmd_interrupt); |
1593 | 1972 | ||
1594 | err = parse_dispatch_command(conn, cmd, &pwd, batchmode); | 1973 | err = parse_dispatch_command(conn, cmd, &remote_path, |
1974 | batchmode); | ||
1595 | if (err != 0) | 1975 | if (err != 0) |
1596 | break; | 1976 | break; |
1597 | } | 1977 | } |
1598 | xfree(pwd); | 1978 | xfree(remote_path); |
1599 | xfree(conn); | 1979 | xfree(conn); |
1600 | 1980 | ||
1601 | #ifdef USE_LIBEDIT | 1981 | #ifdef USE_LIBEDIT |
@@ -1647,9 +2027,11 @@ connect_to_server(char *path, char **args, int *in, int *out) | |||
1647 | * The underlying ssh is in the same process group, so we must | 2027 | * The underlying ssh is in the same process group, so we must |
1648 | * ignore SIGINT if we want to gracefully abort commands, | 2028 | * ignore SIGINT if we want to gracefully abort commands, |
1649 | * otherwise the signal will make it to the ssh process and | 2029 | * otherwise the signal will make it to the ssh process and |
1650 | * kill it too | 2030 | * kill it too. Contrawise, since sftp sends SIGTERMs to the |
2031 | * underlying ssh, it must *not* ignore that signal. | ||
1651 | */ | 2032 | */ |
1652 | signal(SIGINT, SIG_IGN); | 2033 | signal(SIGINT, SIG_IGN); |
2034 | signal(SIGTERM, SIG_DFL); | ||
1653 | execvp(path, args); | 2035 | execvp(path, args); |
1654 | fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); | 2036 | fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); |
1655 | _exit(1); | 2037 | _exit(1); |
@@ -1668,12 +2050,16 @@ usage(void) | |||
1668 | extern char *__progname; | 2050 | extern char *__progname; |
1669 | 2051 | ||
1670 | fprintf(stderr, | 2052 | fprintf(stderr, |
1671 | "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" | 2053 | "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" |
1672 | " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" | 2054 | " [-D sftp_server_path] [-F ssh_config] " |
1673 | " [-S program] [-s subsystem | sftp_server] host\n" | 2055 | "[-i identity_file]\n" |
2056 | " [-o ssh_option] [-P port] [-R num_requests] " | ||
2057 | "[-S program]\n" | ||
2058 | " [-s subsystem | sftp_server] host\n" | ||
1674 | " %s [user@]host[:file ...]\n" | 2059 | " %s [user@]host[:file ...]\n" |
1675 | " %s [user@]host[:dir[/]]\n" | 2060 | " %s [user@]host[:dir[/]]\n" |
1676 | " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); | 2061 | " %s -b batchfile [user@]host\n", |
2062 | __progname, __progname, __progname, __progname); | ||
1677 | exit(1); | 2063 | exit(1); |
1678 | } | 2064 | } |
1679 | 2065 | ||
@@ -1681,7 +2067,7 @@ int | |||
1681 | main(int argc, char **argv) | 2067 | main(int argc, char **argv) |
1682 | { | 2068 | { |
1683 | int in, out, ch, err; | 2069 | int in, out, ch, err; |
1684 | char *host, *userhost, *cp, *file2 = NULL; | 2070 | char *host = NULL, *userhost, *cp, *file2 = NULL; |
1685 | int debug_level = 0, sshver = 2; | 2071 | int debug_level = 0, sshver = 2; |
1686 | char *file1 = NULL, *sftp_server = NULL; | 2072 | char *file1 = NULL, *sftp_server = NULL; |
1687 | char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; | 2073 | char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; |
@@ -1689,6 +2075,9 @@ main(int argc, char **argv) | |||
1689 | arglist args; | 2075 | arglist args; |
1690 | extern int optind; | 2076 | extern int optind; |
1691 | extern char *optarg; | 2077 | extern char *optarg; |
2078 | struct sftp_conn *conn; | ||
2079 | size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; | ||
2080 | size_t num_requests = DEFAULT_NUM_REQUESTS; | ||
1692 | 2081 | ||
1693 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | 2082 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
1694 | sanitise_stdfd(); | 2083 | sanitise_stdfd(); |
@@ -1705,10 +2094,29 @@ main(int argc, char **argv) | |||
1705 | ll = SYSLOG_LEVEL_INFO; | 2094 | ll = SYSLOG_LEVEL_INFO; |
1706 | infile = stdin; | 2095 | infile = stdin; |
1707 | 2096 | ||
1708 | while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { | 2097 | while ((ch = getopt(argc, argv, |
2098 | "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { | ||
1709 | switch (ch) { | 2099 | switch (ch) { |
2100 | /* Passed through to ssh(1) */ | ||
2101 | case '4': | ||
2102 | case '6': | ||
1710 | case 'C': | 2103 | case 'C': |
1711 | addargs(&args, "-C"); | 2104 | addargs(&args, "-%c", ch); |
2105 | break; | ||
2106 | /* Passed through to ssh(1) with argument */ | ||
2107 | case 'F': | ||
2108 | case 'c': | ||
2109 | case 'i': | ||
2110 | case 'o': | ||
2111 | addargs(&args, "-%c", ch); | ||
2112 | addargs(&args, "%s", optarg); | ||
2113 | break; | ||
2114 | case 'q': | ||
2115 | showprogress = 0; | ||
2116 | addargs(&args, "-%c", ch); | ||
2117 | break; | ||
2118 | case 'P': | ||
2119 | addargs(&args, "-oPort %s", optarg); | ||
1712 | break; | 2120 | break; |
1713 | case 'v': | 2121 | case 'v': |
1714 | if (debug_level < 3) { | 2122 | if (debug_level < 3) { |
@@ -1717,21 +2125,18 @@ main(int argc, char **argv) | |||
1717 | } | 2125 | } |
1718 | debug_level++; | 2126 | debug_level++; |
1719 | break; | 2127 | break; |
1720 | case 'F': | ||
1721 | case 'o': | ||
1722 | addargs(&args, "-%c%s", ch, optarg); | ||
1723 | break; | ||
1724 | case '1': | 2128 | case '1': |
1725 | sshver = 1; | 2129 | sshver = 1; |
1726 | if (sftp_server == NULL) | 2130 | if (sftp_server == NULL) |
1727 | sftp_server = _PATH_SFTP_SERVER; | 2131 | sftp_server = _PATH_SFTP_SERVER; |
1728 | break; | 2132 | break; |
1729 | case 's': | 2133 | case '2': |
1730 | sftp_server = optarg; | 2134 | sshver = 2; |
1731 | break; | 2135 | break; |
1732 | case 'S': | 2136 | case 'B': |
1733 | ssh_program = optarg; | 2137 | copy_buffer_len = strtol(optarg, &cp, 10); |
1734 | replacearg(&args, 0, "%s", ssh_program); | 2138 | if (copy_buffer_len == 0 || *cp != '\0') |
2139 | fatal("Invalid buffer size \"%s\"", optarg); | ||
1735 | break; | 2140 | break; |
1736 | case 'b': | 2141 | case 'b': |
1737 | if (batchmode) | 2142 | if (batchmode) |
@@ -1745,13 +2150,14 @@ main(int argc, char **argv) | |||
1745 | batchmode = 1; | 2150 | batchmode = 1; |
1746 | addargs(&args, "-obatchmode yes"); | 2151 | addargs(&args, "-obatchmode yes"); |
1747 | break; | 2152 | break; |
1748 | case 'P': | 2153 | case 'p': |
2154 | global_pflag = 1; | ||
2155 | break; | ||
2156 | case 'D': | ||
1749 | sftp_direct = optarg; | 2157 | sftp_direct = optarg; |
1750 | break; | 2158 | break; |
1751 | case 'B': | 2159 | case 'r': |
1752 | copy_buffer_len = strtol(optarg, &cp, 10); | 2160 | global_rflag = 1; |
1753 | if (copy_buffer_len == 0 || *cp != '\0') | ||
1754 | fatal("Invalid buffer size \"%s\"", optarg); | ||
1755 | break; | 2161 | break; |
1756 | case 'R': | 2162 | case 'R': |
1757 | num_requests = strtol(optarg, &cp, 10); | 2163 | num_requests = strtol(optarg, &cp, 10); |
@@ -1759,6 +2165,13 @@ main(int argc, char **argv) | |||
1759 | fatal("Invalid number of requests \"%s\"", | 2165 | fatal("Invalid number of requests \"%s\"", |
1760 | optarg); | 2166 | optarg); |
1761 | break; | 2167 | break; |
2168 | case 's': | ||
2169 | sftp_server = optarg; | ||
2170 | break; | ||
2171 | case 'S': | ||
2172 | ssh_program = optarg; | ||
2173 | replacearg(&args, 0, "%s", ssh_program); | ||
2174 | break; | ||
1762 | case 'h': | 2175 | case 'h': |
1763 | default: | 2176 | default: |
1764 | usage(); | 2177 | usage(); |
@@ -1785,7 +2198,8 @@ main(int argc, char **argv) | |||
1785 | fprintf(stderr, "Missing username\n"); | 2198 | fprintf(stderr, "Missing username\n"); |
1786 | usage(); | 2199 | usage(); |
1787 | } | 2200 | } |
1788 | addargs(&args, "-l%s", userhost); | 2201 | addargs(&args, "-l"); |
2202 | addargs(&args, "%s", userhost); | ||
1789 | } | 2203 | } |
1790 | 2204 | ||
1791 | if ((cp = colon(host)) != NULL) { | 2205 | if ((cp = colon(host)) != NULL) { |
@@ -1805,24 +2219,32 @@ main(int argc, char **argv) | |||
1805 | if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) | 2219 | if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) |
1806 | addargs(&args, "-s"); | 2220 | addargs(&args, "-s"); |
1807 | 2221 | ||
2222 | addargs(&args, "--"); | ||
1808 | addargs(&args, "%s", host); | 2223 | addargs(&args, "%s", host); |
1809 | addargs(&args, "%s", (sftp_server != NULL ? | 2224 | addargs(&args, "%s", (sftp_server != NULL ? |
1810 | sftp_server : "sftp")); | 2225 | sftp_server : "sftp")); |
1811 | 2226 | ||
1812 | if (!batchmode) | ||
1813 | fprintf(stderr, "Connecting to %s...\n", host); | ||
1814 | connect_to_server(ssh_program, args.list, &in, &out); | 2227 | connect_to_server(ssh_program, args.list, &in, &out); |
1815 | } else { | 2228 | } else { |
1816 | args.list = NULL; | 2229 | args.list = NULL; |
1817 | addargs(&args, "sftp-server"); | 2230 | addargs(&args, "sftp-server"); |
1818 | 2231 | ||
1819 | if (!batchmode) | ||
1820 | fprintf(stderr, "Attaching to %s...\n", sftp_direct); | ||
1821 | connect_to_server(sftp_direct, args.list, &in, &out); | 2232 | connect_to_server(sftp_direct, args.list, &in, &out); |
1822 | } | 2233 | } |
1823 | freeargs(&args); | 2234 | freeargs(&args); |
1824 | 2235 | ||
1825 | err = interactive_loop(in, out, file1, file2); | 2236 | conn = do_init(in, out, copy_buffer_len, num_requests); |
2237 | if (conn == NULL) | ||
2238 | fatal("Couldn't initialise connection to server"); | ||
2239 | |||
2240 | if (!batchmode) { | ||
2241 | if (sftp_direct == NULL) | ||
2242 | fprintf(stderr, "Connected to %s.\n", host); | ||
2243 | else | ||
2244 | fprintf(stderr, "Attached to %s.\n", sftp_direct); | ||
2245 | } | ||
2246 | |||
2247 | err = interactive_loop(conn, file1, file2); | ||
1826 | 2248 | ||
1827 | #if !defined(USE_PIPES) | 2249 | #if !defined(USE_PIPES) |
1828 | shutdown(in, SHUT_RDWR); | 2250 | shutdown(in, SHUT_RDWR); |