summaryrefslogtreecommitdiff
path: root/sftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp.c')
-rw-r--r--sftp.c858
1 files changed, 640 insertions, 218 deletions
diff --git a/sftp.c b/sftp.c
index 66bd111b1..d65d4ec62 100644
--- a/sftp.c
+++ b/sftp.c
@@ -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 */
69FILE* infile; 75FILE* infile;
70 76
71/* Are we in batchfile mode? */ 77/* Are we in batchfile mode? */
72int batchmode = 0; 78int batchmode = 0;
73 79
74/* Size of buffer used when copying files */
75size_t copy_buffer_len = 32768;
76
77/* Number of concurrent outstanding requests */
78size_t num_requests = 64;
79
80/* PID of ssh transport process */ 80/* PID of ssh transport process */
81static pid_t sshpid = -1; 81static 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. */
84int showprogress = 1; 84int showprogress = 1;
85 85
86/* When this option is set, we always recursively download/upload directories */
87int global_rflag = 0;
88
89/* When this option is set, the file transfers will always preserve times */
90int global_pflag = 0;
91
86/* SIGINT received during command processing */ 92/* SIGINT received during command processing */
87volatile sig_atomic_t interrupted = 0; 93volatile 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...*/
90int sort_flag; 96int sort_flag;
91 97
98/* Context used for commandline completion */
99struct complete_ctx {
100 struct sftp_conn *conn;
101 char **remote_pathp;
102};
103
92int remote_glob(struct sftp_conn *, const char *, int, 104int 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;
139struct CMD { 152struct 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
144static const struct CMD cmds[] = { 163static 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
181int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 198int interactive_loop(struct sftp_conn *, char *file1, char *file2);
182 199
183/* ARGSUSED */ 200/* ARGSUSED */
184static void 201static 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
316static char * 333static char *
317path_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
331static char *
332make_absolute(char *p, char *pwd) 334make_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
345static int 347static int
346infer_path(const char *p, char **ifp) 348parse_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
365static int
366parse_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 */
483static int
484pathname_is_dir(char *pathname)
485{
486 size_t l = strlen(pathname);
487
488 return l > 0 && pathname[l - 1] == '/';
489}
490
492static int 491static int
493process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 492process_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
558static int 565static int
559process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 566process_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
630out: 642out:
@@ -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
941static char ** 964static char **
942makeargv(const char *arg, int *argcp) 965makeargv(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
1067static int 1107static int
1068parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, 1108parse_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 */
1514static void
1515complete_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 */
1554static char *
1555complete_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 */
1586static int
1587complete_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 */
1659static int
1660complete_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" */
1675static int
1676complete_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 */
1782static unsigned char
1783complete(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, &quote, &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
1473int 1845int
1474interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1846interactive_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
1681main(int argc, char **argv) 2067main(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);