Merge branch 'vendor/DIFFUTILS'
[dragonfly.git] / crypto / openssh / sftp.c
1 /* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm Exp $ */
2 /*
3  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #include "includes.h"
19
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
24 #endif
25 #include <sys/param.h>
26 #include <sys/socket.h>
27 #include <sys/wait.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
30 #endif
31
32 #include <ctype.h>
33 #include <errno.h>
34
35 #ifdef HAVE_PATHS_H
36 # include <paths.h>
37 #endif
38 #ifdef HAVE_LIBGEN_H
39 #include <libgen.h>
40 #endif
41 #ifdef USE_LIBEDIT
42 #include <histedit.h>
43 #else
44 typedef void EditLine;
45 #endif
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <stdarg.h>
52
53 #ifdef HAVE_UTIL_H
54 # include <util.h>
55 #endif
56
57 #ifdef HAVE_LIBUTIL_H
58 # include <libutil.h>
59 #endif
60
61 #include "xmalloc.h"
62 #include "log.h"
63 #include "pathnames.h"
64 #include "misc.h"
65
66 #include "sftp.h"
67 #include "buffer.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
70
71 #define DEFAULT_COPY_BUFLEN     32768   /* Size of buffer for up/download */
72 #define DEFAULT_NUM_REQUESTS    256     /* # concurrent outstanding requests */
73
74 /* File to read commands from */
75 FILE* infile;
76
77 /* Are we in batchfile mode? */
78 int batchmode = 0;
79
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
82
83 /* This is set to 0 if the progressmeter is not desired. */
84 int showprogress = 1;
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
92 /* SIGINT received during command processing */
93 volatile sig_atomic_t interrupted = 0;
94
95 /* I wish qsort() took a separate ctx for the comparison function...*/
96 int sort_flag;
97
98 /* Context used for commandline completion */
99 struct complete_ctx {
100         struct sftp_conn *conn;
101         char **remote_pathp;
102 };
103
104 int remote_glob(struct sftp_conn *, const char *, int,
105     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
106
107 extern char *__progname;
108
109 /* Separators for interactive commands */
110 #define WHITESPACE " \t\r\n"
111
112 /* ls flags */
113 #define LS_LONG_VIEW    0x0001  /* Full view ala ls -l */
114 #define LS_SHORT_VIEW   0x0002  /* Single row view ala ls -1 */
115 #define LS_NUMERIC_VIEW 0x0004  /* Long view with numeric uid/gid */
116 #define LS_NAME_SORT    0x0008  /* Sort by name (default) */
117 #define LS_TIME_SORT    0x0010  /* Sort by mtime */
118 #define LS_SIZE_SORT    0x0020  /* Sort by file size */
119 #define LS_REVERSE_SORT 0x0040  /* Reverse sort order */
120 #define LS_SHOW_ALL     0x0080  /* Don't skip filenames starting with '.' */
121 #define LS_SI_UNITS     0x0100  /* Display sizes as K, M, G, etc. */
122
123 #define VIEW_FLAGS      (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
124 #define SORT_FLAGS      (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
125
126 /* Commands for interactive mode */
127 #define I_CHDIR         1
128 #define I_CHGRP         2
129 #define I_CHMOD         3
130 #define I_CHOWN         4
131 #define I_DF            24
132 #define I_GET           5
133 #define I_HELP          6
134 #define I_LCHDIR        7
135 #define I_LLS           8
136 #define I_LMKDIR        9
137 #define I_LPWD          10
138 #define I_LS            11
139 #define I_LUMASK        12
140 #define I_MKDIR         13
141 #define I_PUT           14
142 #define I_PWD           15
143 #define I_QUIT          16
144 #define I_RENAME        17
145 #define I_RM            18
146 #define I_RMDIR         19
147 #define I_SHELL         20
148 #define I_SYMLINK       21
149 #define I_VERSION       22
150 #define I_PROGRESS      23
151
152 struct CMD {
153         const char *c;
154         const int n;
155         const int t;
156 };
157
158 /* Type of completion */
159 #define NOARGS  0
160 #define REMOTE  1
161 #define LOCAL   2
162
163 static const struct CMD cmds[] = {
164         { "bye",        I_QUIT,         NOARGS  },
165         { "cd",         I_CHDIR,        REMOTE  },
166         { "chdir",      I_CHDIR,        REMOTE  },
167         { "chgrp",      I_CHGRP,        REMOTE  },
168         { "chmod",      I_CHMOD,        REMOTE  },
169         { "chown",      I_CHOWN,        REMOTE  },
170         { "df",         I_DF,           REMOTE  },
171         { "dir",        I_LS,           REMOTE  },
172         { "exit",       I_QUIT,         NOARGS  },
173         { "get",        I_GET,          REMOTE  },
174         { "help",       I_HELP,         NOARGS  },
175         { "lcd",        I_LCHDIR,       LOCAL   },
176         { "lchdir",     I_LCHDIR,       LOCAL   },
177         { "lls",        I_LLS,          LOCAL   },
178         { "lmkdir",     I_LMKDIR,       LOCAL   },
179         { "ln",         I_SYMLINK,      REMOTE  },
180         { "lpwd",       I_LPWD,         LOCAL   },
181         { "ls",         I_LS,           REMOTE  },
182         { "lumask",     I_LUMASK,       NOARGS  },
183         { "mkdir",      I_MKDIR,        REMOTE  },
184         { "mget",       I_GET,          REMOTE  },
185         { "mput",       I_PUT,          LOCAL   },
186         { "progress",   I_PROGRESS,     NOARGS  },
187         { "put",        I_PUT,          LOCAL   },
188         { "pwd",        I_PWD,          REMOTE  },
189         { "quit",       I_QUIT,         NOARGS  },
190         { "rename",     I_RENAME,       REMOTE  },
191         { "rm",         I_RM,           REMOTE  },
192         { "rmdir",      I_RMDIR,        REMOTE  },
193         { "symlink",    I_SYMLINK,      REMOTE  },
194         { "version",    I_VERSION,      NOARGS  },
195         { "!",          I_SHELL,        NOARGS  },
196         { "?",          I_HELP,         NOARGS  },
197         { NULL,         -1,             -1      }
198 };
199
200 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
201
202 /* ARGSUSED */
203 static void
204 killchild(int signo)
205 {
206         if (sshpid > 1) {
207                 kill(sshpid, SIGTERM);
208                 waitpid(sshpid, NULL, 0);
209         }
210
211         _exit(1);
212 }
213
214 /* ARGSUSED */
215 static void
216 cmd_interrupt(int signo)
217 {
218         const char msg[] = "\rInterrupt  \n";
219         int olderrno = errno;
220
221         write(STDERR_FILENO, msg, sizeof(msg) - 1);
222         interrupted = 1;
223         errno = olderrno;
224 }
225
226 static void
227 help(void)
228 {
229         printf("Available commands:\n"
230             "bye                                Quit sftp\n"
231             "cd path                            Change remote directory to 'path'\n"
232             "chgrp grp path                     Change group of file 'path' to 'grp'\n"
233             "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
234             "chown own path                     Change owner of file 'path' to 'own'\n"
235             "df [-hi] [path]                    Display statistics for current directory or\n"
236             "                                   filesystem containing 'path'\n"
237             "exit                               Quit sftp\n"
238             "get [-Ppr] remote [local]          Download file\n"
239             "help                               Display this help text\n"
240             "lcd path                           Change local directory to 'path'\n"
241             "lls [ls-options [path]]            Display local directory listing\n"
242             "lmkdir path                        Create local directory\n"
243             "ln oldpath newpath                 Symlink remote file\n"
244             "lpwd                               Print local working directory\n"
245             "ls [-1afhlnrSt] [path]             Display remote directory listing\n"
246             "lumask umask                       Set local umask to 'umask'\n"
247             "mkdir path                         Create remote directory\n"
248             "progress                           Toggle display of progress meter\n"
249             "put [-Ppr] local [remote]          Upload file\n"
250             "pwd                                Display remote working directory\n"
251             "quit                               Quit sftp\n"
252             "rename oldpath newpath             Rename remote file\n"
253             "rm path                            Delete remote file\n"
254             "rmdir path                         Remove remote directory\n"
255             "symlink oldpath newpath            Symlink remote file\n"
256             "version                            Show SFTP version\n"
257             "!command                           Execute 'command' in local shell\n"
258             "!                                  Escape to local shell\n"
259             "?                                  Synonym for help\n");
260 }
261
262 static void
263 local_do_shell(const char *args)
264 {
265         int status;
266         char *shell;
267         pid_t pid;
268
269         if (!*args)
270                 args = NULL;
271
272         if ((shell = getenv("SHELL")) == NULL)
273                 shell = _PATH_BSHELL;
274
275         if ((pid = fork()) == -1)
276                 fatal("Couldn't fork: %s", strerror(errno));
277
278         if (pid == 0) {
279                 /* XXX: child has pipe fds to ssh subproc open - issue? */
280                 if (args) {
281                         debug3("Executing %s -c \"%s\"", shell, args);
282                         execl(shell, shell, "-c", args, (char *)NULL);
283                 } else {
284                         debug3("Executing %s", shell);
285                         execl(shell, shell, (char *)NULL);
286                 }
287                 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
288                     strerror(errno));
289                 _exit(1);
290         }
291         while (waitpid(pid, &status, 0) == -1)
292                 if (errno != EINTR)
293                         fatal("Couldn't wait for child: %s", strerror(errno));
294         if (!WIFEXITED(status))
295                 error("Shell exited abnormally");
296         else if (WEXITSTATUS(status))
297                 error("Shell exited with status %d", WEXITSTATUS(status));
298 }
299
300 static void
301 local_do_ls(const char *args)
302 {
303         if (!args || !*args)
304                 local_do_shell(_PATH_LS);
305         else {
306                 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
307                 char *buf = xmalloc(len);
308
309                 /* XXX: quoting - rip quoting code from ftp? */
310                 snprintf(buf, len, _PATH_LS " %s", args);
311                 local_do_shell(buf);
312                 xfree(buf);
313         }
314 }
315
316 /* Strip one path (usually the pwd) from the start of another */
317 static char *
318 path_strip(char *path, char *strip)
319 {
320         size_t len;
321
322         if (strip == NULL)
323                 return (xstrdup(path));
324
325         len = strlen(strip);
326         if (strncmp(path, strip, len) == 0) {
327                 if (strip[len - 1] != '/' && path[len] == '/')
328                         len++;
329                 return (xstrdup(path + len));
330         }
331
332         return (xstrdup(path));
333 }
334
335 static char *
336 make_absolute(char *p, char *pwd)
337 {
338         char *abs_str;
339
340         /* Derelativise */
341         if (p && p[0] != '/') {
342                 abs_str = path_append(pwd, p);
343                 xfree(p);
344                 return(abs_str);
345         } else
346                 return(p);
347 }
348
349 static int
350 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
351     int *rflag)
352 {
353         extern int opterr, optind, optopt, optreset;
354         int ch;
355
356         optind = optreset = 1;
357         opterr = 0;
358
359         *rflag = *pflag = 0;
360         while ((ch = getopt(argc, argv, "PpRr")) != -1) {
361                 switch (ch) {
362                 case 'p':
363                 case 'P':
364                         *pflag = 1;
365                         break;
366                 case 'r':
367                 case 'R':
368                         *rflag = 1;
369                         break;
370                 default:
371                         error("%s: Invalid flag -%c", cmd, optopt);
372                         return -1;
373                 }
374         }
375
376         return optind;
377 }
378
379 static int
380 parse_ls_flags(char **argv, int argc, int *lflag)
381 {
382         extern int opterr, optind, optopt, optreset;
383         int ch;
384
385         optind = optreset = 1;
386         opterr = 0;
387
388         *lflag = LS_NAME_SORT;
389         while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
390                 switch (ch) {
391                 case '1':
392                         *lflag &= ~VIEW_FLAGS;
393                         *lflag |= LS_SHORT_VIEW;
394                         break;
395                 case 'S':
396                         *lflag &= ~SORT_FLAGS;
397                         *lflag |= LS_SIZE_SORT;
398                         break;
399                 case 'a':
400                         *lflag |= LS_SHOW_ALL;
401                         break;
402                 case 'f':
403                         *lflag &= ~SORT_FLAGS;
404                         break;
405                 case 'h':
406                         *lflag |= LS_SI_UNITS;
407                         break;
408                 case 'l':
409                         *lflag &= ~LS_SHORT_VIEW;
410                         *lflag |= LS_LONG_VIEW;
411                         break;
412                 case 'n':
413                         *lflag &= ~LS_SHORT_VIEW;
414                         *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
415                         break;
416                 case 'r':
417                         *lflag |= LS_REVERSE_SORT;
418                         break;
419                 case 't':
420                         *lflag &= ~SORT_FLAGS;
421                         *lflag |= LS_TIME_SORT;
422                         break;
423                 default:
424                         error("ls: Invalid flag -%c", optopt);
425                         return -1;
426                 }
427         }
428
429         return optind;
430 }
431
432 static int
433 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
434 {
435         extern int opterr, optind, optopt, optreset;
436         int ch;
437
438         optind = optreset = 1;
439         opterr = 0;
440
441         *hflag = *iflag = 0;
442         while ((ch = getopt(argc, argv, "hi")) != -1) {
443                 switch (ch) {
444                 case 'h':
445                         *hflag = 1;
446                         break;
447                 case 'i':
448                         *iflag = 1;
449                         break;
450                 default:
451                         error("%s: Invalid flag -%c", cmd, optopt);
452                         return -1;
453                 }
454         }
455
456         return optind;
457 }
458
459 static int
460 is_dir(char *path)
461 {
462         struct stat sb;
463
464         /* XXX: report errors? */
465         if (stat(path, &sb) == -1)
466                 return(0);
467
468         return(S_ISDIR(sb.st_mode));
469 }
470
471 static int
472 remote_is_dir(struct sftp_conn *conn, char *path)
473 {
474         Attrib *a;
475
476         /* XXX: report errors? */
477         if ((a = do_stat(conn, path, 1)) == NULL)
478                 return(0);
479         if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
480                 return(0);
481         return(S_ISDIR(a->perm));
482 }
483
484 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
485 static int
486 pathname_is_dir(char *pathname)
487 {
488         size_t l = strlen(pathname);
489
490         return l > 0 && pathname[l - 1] == '/';
491 }
492
493 static int
494 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
495     int pflag, int rflag)
496 {
497         char *abs_src = NULL;
498         char *abs_dst = NULL;
499         glob_t g;
500         char *filename, *tmp=NULL;
501         int i, err = 0;
502
503         abs_src = xstrdup(src);
504         abs_src = make_absolute(abs_src, pwd);
505         memset(&g, 0, sizeof(g));
506
507         debug3("Looking up %s", abs_src);
508         if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
509                 error("File \"%s\" not found.", abs_src);
510                 err = -1;
511                 goto out;
512         }
513
514         /*
515          * If multiple matches then dst must be a directory or
516          * unspecified.
517          */
518         if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
519                 error("Multiple source paths, but destination "
520                     "\"%s\" is not a directory", dst);
521                 err = -1;
522                 goto out;
523         }
524
525         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
526                 tmp = xstrdup(g.gl_pathv[i]);
527                 if ((filename = basename(tmp)) == NULL) {
528                         error("basename %s: %s", tmp, strerror(errno));
529                         xfree(tmp);
530                         err = -1;
531                         goto out;
532                 }
533
534                 if (g.gl_matchc == 1 && dst) {
535                         if (is_dir(dst)) {
536                                 abs_dst = path_append(dst, filename);
537                         } else {
538                                 abs_dst = xstrdup(dst);
539                         }
540                 } else if (dst) {
541                         abs_dst = path_append(dst, filename);
542                 } else {
543                         abs_dst = xstrdup(filename);
544                 }
545                 xfree(tmp);
546
547                 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
548                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
549                         if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 
550                             pflag || global_pflag, 1) == -1)
551                                 err = -1;
552                 } else {
553                         if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
554                             pflag || global_pflag) == -1)
555                                 err = -1;
556                 }
557                 xfree(abs_dst);
558                 abs_dst = NULL;
559         }
560
561 out:
562         xfree(abs_src);
563         globfree(&g);
564         return(err);
565 }
566
567 static int
568 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
569     int pflag, int rflag)
570 {
571         char *tmp_dst = NULL;
572         char *abs_dst = NULL;
573         char *tmp = NULL, *filename = NULL;
574         glob_t g;
575         int err = 0;
576         int i, dst_is_dir = 1;
577         struct stat sb;
578
579         if (dst) {
580                 tmp_dst = xstrdup(dst);
581                 tmp_dst = make_absolute(tmp_dst, pwd);
582         }
583
584         memset(&g, 0, sizeof(g));
585         debug3("Looking up %s", src);
586         if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
587                 error("File \"%s\" not found.", src);
588                 err = -1;
589                 goto out;
590         }
591
592         /* If we aren't fetching to pwd then stash this status for later */
593         if (tmp_dst != NULL)
594                 dst_is_dir = remote_is_dir(conn, tmp_dst);
595
596         /* If multiple matches, dst may be directory or unspecified */
597         if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
598                 error("Multiple paths match, but destination "
599                     "\"%s\" is not a directory", tmp_dst);
600                 err = -1;
601                 goto out;
602         }
603
604         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
605                 if (stat(g.gl_pathv[i], &sb) == -1) {
606                         err = -1;
607                         error("stat %s: %s", g.gl_pathv[i], strerror(errno));
608                         continue;
609                 }
610                 
611                 tmp = xstrdup(g.gl_pathv[i]);
612                 if ((filename = basename(tmp)) == NULL) {
613                         error("basename %s: %s", tmp, strerror(errno));
614                         xfree(tmp);
615                         err = -1;
616                         goto out;
617                 }
618
619                 if (g.gl_matchc == 1 && tmp_dst) {
620                         /* If directory specified, append filename */
621                         if (dst_is_dir)
622                                 abs_dst = path_append(tmp_dst, filename);
623                         else
624                                 abs_dst = xstrdup(tmp_dst);
625                 } else if (tmp_dst) {
626                         abs_dst = path_append(tmp_dst, filename);
627                 } else {
628                         abs_dst = make_absolute(xstrdup(filename), pwd);
629                 }
630                 xfree(tmp);
631
632                 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
633                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
634                         if (upload_dir(conn, g.gl_pathv[i], abs_dst,
635                             pflag || global_pflag, 1) == -1)
636                                 err = -1;
637                 } else {
638                         if (do_upload(conn, g.gl_pathv[i], abs_dst,
639                             pflag || global_pflag) == -1)
640                                 err = -1;
641                 }
642         }
643
644 out:
645         if (abs_dst)
646                 xfree(abs_dst);
647         if (tmp_dst)
648                 xfree(tmp_dst);
649         globfree(&g);
650         return(err);
651 }
652
653 static int
654 sdirent_comp(const void *aa, const void *bb)
655 {
656         SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
657         SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
658         int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
659
660 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
661         if (sort_flag & LS_NAME_SORT)
662                 return (rmul * strcmp(a->filename, b->filename));
663         else if (sort_flag & LS_TIME_SORT)
664                 return (rmul * NCMP(a->a.mtime, b->a.mtime));
665         else if (sort_flag & LS_SIZE_SORT)
666                 return (rmul * NCMP(a->a.size, b->a.size));
667
668         fatal("Unknown ls sort type");
669 }
670
671 /* sftp ls.1 replacement for directories */
672 static int
673 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
674 {
675         int n;
676         u_int c = 1, colspace = 0, columns = 1;
677         SFTP_DIRENT **d;
678
679         if ((n = do_readdir(conn, path, &d)) != 0)
680                 return (n);
681
682         if (!(lflag & LS_SHORT_VIEW)) {
683                 u_int m = 0, width = 80;
684                 struct winsize ws;
685                 char *tmp;
686
687                 /* Count entries for sort and find longest filename */
688                 for (n = 0; d[n] != NULL; n++) {
689                         if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
690                                 m = MAX(m, strlen(d[n]->filename));
691                 }
692
693                 /* Add any subpath that also needs to be counted */
694                 tmp = path_strip(path, strip_path);
695                 m += strlen(tmp);
696                 xfree(tmp);
697
698                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
699                         width = ws.ws_col;
700
701                 columns = width / (m + 2);
702                 columns = MAX(columns, 1);
703                 colspace = width / columns;
704                 colspace = MIN(colspace, width);
705         }
706
707         if (lflag & SORT_FLAGS) {
708                 for (n = 0; d[n] != NULL; n++)
709                         ;       /* count entries */
710                 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
711                 qsort(d, n, sizeof(*d), sdirent_comp);
712         }
713
714         for (n = 0; d[n] != NULL && !interrupted; n++) {
715                 char *tmp, *fname;
716
717                 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
718                         continue;
719
720                 tmp = path_append(path, d[n]->filename);
721                 fname = path_strip(tmp, strip_path);
722                 xfree(tmp);
723
724                 if (lflag & LS_LONG_VIEW) {
725                         if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
726                                 char *lname;
727                                 struct stat sb;
728
729                                 memset(&sb, 0, sizeof(sb));
730                                 attrib_to_stat(&d[n]->a, &sb);
731                                 lname = ls_file(fname, &sb, 1,
732                                     (lflag & LS_SI_UNITS));
733                                 printf("%s\n", lname);
734                                 xfree(lname);
735                         } else
736                                 printf("%s\n", d[n]->longname);
737                 } else {
738                         printf("%-*s", colspace, fname);
739                         if (c >= columns) {
740                                 printf("\n");
741                                 c = 1;
742                         } else
743                                 c++;
744                 }
745
746                 xfree(fname);
747         }
748
749         if (!(lflag & LS_LONG_VIEW) && (c != 1))
750                 printf("\n");
751
752         free_sftp_dirents(d);
753         return (0);
754 }
755
756 /* sftp ls.1 replacement which handles path globs */
757 static int
758 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
759     int lflag)
760 {
761         glob_t g;
762         u_int i, c = 1, colspace = 0, columns = 1;
763         Attrib *a = NULL;
764
765         memset(&g, 0, sizeof(g));
766
767         if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
768             NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
769                 if (g.gl_pathc)
770                         globfree(&g);
771                 error("Can't ls: \"%s\" not found", path);
772                 return (-1);
773         }
774
775         if (interrupted)
776                 goto out;
777
778         /*
779          * If the glob returns a single match and it is a directory,
780          * then just list its contents.
781          */
782         if (g.gl_matchc == 1) {
783                 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
784                         globfree(&g);
785                         return (-1);
786                 }
787                 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
788                     S_ISDIR(a->perm)) {
789                         int err;
790
791                         err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
792                         globfree(&g);
793                         return (err);
794                 }
795         }
796
797         if (!(lflag & LS_SHORT_VIEW)) {
798                 u_int m = 0, width = 80;
799                 struct winsize ws;
800
801                 /* Count entries for sort and find longest filename */
802                 for (i = 0; g.gl_pathv[i]; i++)
803                         m = MAX(m, strlen(g.gl_pathv[i]));
804
805                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
806                         width = ws.ws_col;
807
808                 columns = width / (m + 2);
809                 columns = MAX(columns, 1);
810                 colspace = width / columns;
811         }
812
813         for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
814                 char *fname;
815
816                 fname = path_strip(g.gl_pathv[i], strip_path);
817
818                 if (lflag & LS_LONG_VIEW) {
819                         char *lname;
820                         struct stat sb;
821
822                         /*
823                          * XXX: this is slow - 1 roundtrip per path
824                          * A solution to this is to fork glob() and
825                          * build a sftp specific version which keeps the
826                          * attribs (which currently get thrown away)
827                          * that the server returns as well as the filenames.
828                          */
829                         memset(&sb, 0, sizeof(sb));
830                         if (a == NULL)
831                                 a = do_lstat(conn, g.gl_pathv[i], 1);
832                         if (a != NULL)
833                                 attrib_to_stat(a, &sb);
834                         lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS));
835                         printf("%s\n", lname);
836                         xfree(lname);
837                 } else {
838                         printf("%-*s", colspace, fname);
839                         if (c >= columns) {
840                                 printf("\n");
841                                 c = 1;
842                         } else
843                                 c++;
844                 }
845                 xfree(fname);
846         }
847
848         if (!(lflag & LS_LONG_VIEW) && (c != 1))
849                 printf("\n");
850
851  out:
852         if (g.gl_pathc)
853                 globfree(&g);
854
855         return (0);
856 }
857
858 static int
859 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
860 {
861         struct sftp_statvfs st;
862         char s_used[FMT_SCALED_STRSIZE];
863         char s_avail[FMT_SCALED_STRSIZE];
864         char s_root[FMT_SCALED_STRSIZE];
865         char s_total[FMT_SCALED_STRSIZE];
866         unsigned long long ffree;
867
868         if (do_statvfs(conn, path, &st, 1) == -1)
869                 return -1;
870         if (iflag) {
871                 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
872                 printf("     Inodes        Used       Avail      "
873                     "(root)    %%Capacity\n");
874                 printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
875                     (unsigned long long)st.f_files,
876                     (unsigned long long)(st.f_files - st.f_ffree),
877                     (unsigned long long)st.f_favail,
878                     (unsigned long long)st.f_ffree, ffree);
879         } else if (hflag) {
880                 strlcpy(s_used, "error", sizeof(s_used));
881                 strlcpy(s_avail, "error", sizeof(s_avail));
882                 strlcpy(s_root, "error", sizeof(s_root));
883                 strlcpy(s_total, "error", sizeof(s_total));
884                 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
885                 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
886                 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
887                 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
888                 printf("    Size     Used    Avail   (root)    %%Capacity\n");
889                 printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
890                     s_total, s_used, s_avail, s_root,
891                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
892                     st.f_blocks));
893         } else {
894                 printf("        Size         Used        Avail       "
895                     "(root)    %%Capacity\n");
896                 printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
897                     (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
898                     (unsigned long long)(st.f_frsize *
899                     (st.f_blocks - st.f_bfree) / 1024),
900                     (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
901                     (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
902                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
903                     st.f_blocks));
904         }
905         return 0;
906 }
907
908 /*
909  * Undo escaping of glob sequences in place. Used to undo extra escaping
910  * applied in makeargv() when the string is destined for a function that
911  * does not glob it.
912  */
913 static void
914 undo_glob_escape(char *s)
915 {
916         size_t i, j;
917
918         for (i = j = 0;;) {
919                 if (s[i] == '\0') {
920                         s[j] = '\0';
921                         return;
922                 }
923                 if (s[i] != '\\') {
924                         s[j++] = s[i++];
925                         continue;
926                 }
927                 /* s[i] == '\\' */
928                 ++i;
929                 switch (s[i]) {
930                 case '?':
931                 case '[':
932                 case '*':
933                 case '\\':
934                         s[j++] = s[i++];
935                         break;
936                 case '\0':
937                         s[j++] = '\\';
938                         s[j] = '\0';
939                         return;
940                 default:
941                         s[j++] = '\\';
942                         s[j++] = s[i++];
943                         break;
944                 }
945         }
946 }
947
948 /*
949  * Split a string into an argument vector using sh(1)-style quoting,
950  * comment and escaping rules, but with some tweaks to handle glob(3)
951  * wildcards.
952  * The "sloppy" flag allows for recovery from missing terminating quote, for
953  * use in parsing incomplete commandlines during tab autocompletion.
954  *
955  * Returns NULL on error or a NULL-terminated array of arguments.
956  *
957  * If "lastquote" is not NULL, the quoting character used for the last
958  * argument is placed in *lastquote ("\0", "'" or "\"").
959  * 
960  * If "terminated" is not NULL, *terminated will be set to 1 when the
961  * last argument's quote has been properly terminated or 0 otherwise.
962  * This parameter is only of use if "sloppy" is set.
963  */
964 #define MAXARGS         128
965 #define MAXARGLEN       8192
966 static char **
967 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
968     u_int *terminated)
969 {
970         int argc, quot;
971         size_t i, j;
972         static char argvs[MAXARGLEN];
973         static char *argv[MAXARGS + 1];
974         enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
975
976         *argcp = argc = 0;
977         if (strlen(arg) > sizeof(argvs) - 1) {
978  args_too_longs:
979                 error("string too long");
980                 return NULL;
981         }
982         if (terminated != NULL)
983                 *terminated = 1;
984         if (lastquote != NULL)
985                 *lastquote = '\0';
986         state = MA_START;
987         i = j = 0;
988         for (;;) {
989                 if (isspace(arg[i])) {
990                         if (state == MA_UNQUOTED) {
991                                 /* Terminate current argument */
992                                 argvs[j++] = '\0';
993                                 argc++;
994                                 state = MA_START;
995                         } else if (state != MA_START)
996                                 argvs[j++] = arg[i];
997                 } else if (arg[i] == '"' || arg[i] == '\'') {
998                         q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
999                         if (state == MA_START) {
1000                                 argv[argc] = argvs + j;
1001                                 state = q;
1002                                 if (lastquote != NULL)
1003                                         *lastquote = arg[i];
1004                         } else if (state == MA_UNQUOTED) 
1005                                 state = q;
1006                         else if (state == q)
1007                                 state = MA_UNQUOTED;
1008                         else
1009                                 argvs[j++] = arg[i];
1010                 } else if (arg[i] == '\\') {
1011                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
1012                                 quot = state == MA_SQUOTE ? '\'' : '"';
1013                                 /* Unescape quote we are in */
1014                                 /* XXX support \n and friends? */
1015                                 if (arg[i + 1] == quot) {
1016                                         i++;
1017                                         argvs[j++] = arg[i];
1018                                 } else if (arg[i + 1] == '?' ||
1019                                     arg[i + 1] == '[' || arg[i + 1] == '*') {
1020                                         /*
1021                                          * Special case for sftp: append
1022                                          * double-escaped glob sequence -
1023                                          * glob will undo one level of
1024                                          * escaping. NB. string can grow here.
1025                                          */
1026                                         if (j >= sizeof(argvs) - 5)
1027                                                 goto args_too_longs;
1028                                         argvs[j++] = '\\';
1029                                         argvs[j++] = arg[i++];
1030                                         argvs[j++] = '\\';
1031                                         argvs[j++] = arg[i];
1032                                 } else {
1033                                         argvs[j++] = arg[i++];
1034                                         argvs[j++] = arg[i];
1035                                 }
1036                         } else {
1037                                 if (state == MA_START) {
1038                                         argv[argc] = argvs + j;
1039                                         state = MA_UNQUOTED;
1040                                         if (lastquote != NULL)
1041                                                 *lastquote = '\0';
1042                                 }
1043                                 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1044                                     arg[i + 1] == '*' || arg[i + 1] == '\\') {
1045                                         /*
1046                                          * Special case for sftp: append
1047                                          * escaped glob sequence -
1048                                          * glob will undo one level of
1049                                          * escaping.
1050                                          */
1051                                         argvs[j++] = arg[i++];
1052                                         argvs[j++] = arg[i];
1053                                 } else {
1054                                         /* Unescape everything */
1055                                         /* XXX support \n and friends? */
1056                                         i++;
1057                                         argvs[j++] = arg[i];
1058                                 }
1059                         }
1060                 } else if (arg[i] == '#') {
1061                         if (state == MA_SQUOTE || state == MA_DQUOTE)
1062                                 argvs[j++] = arg[i];
1063                         else
1064                                 goto string_done;
1065                 } else if (arg[i] == '\0') {
1066                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
1067                                 if (sloppy) {
1068                                         state = MA_UNQUOTED;
1069                                         if (terminated != NULL)
1070                                                 *terminated = 0;
1071                                         goto string_done;
1072                                 }
1073                                 error("Unterminated quoted argument");
1074                                 return NULL;
1075                         }
1076  string_done:
1077                         if (state == MA_UNQUOTED) {
1078                                 argvs[j++] = '\0';
1079                                 argc++;
1080                         }
1081                         break;
1082                 } else {
1083                         if (state == MA_START) {
1084                                 argv[argc] = argvs + j;
1085                                 state = MA_UNQUOTED;
1086                                 if (lastquote != NULL)
1087                                         *lastquote = '\0';
1088                         }
1089                         if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1090                             (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1091                                 /*
1092                                  * Special case for sftp: escape quoted
1093                                  * glob(3) wildcards. NB. string can grow
1094                                  * here.
1095                                  */
1096                                 if (j >= sizeof(argvs) - 3)
1097                                         goto args_too_longs;
1098                                 argvs[j++] = '\\';
1099                                 argvs[j++] = arg[i];
1100                         } else
1101                                 argvs[j++] = arg[i];
1102                 }
1103                 i++;
1104         }
1105         *argcp = argc;
1106         return argv;
1107 }
1108
1109 static int
1110 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1111     int *hflag, unsigned long *n_arg, char **path1, char **path2)
1112 {
1113         const char *cmd, *cp = *cpp;
1114         char *cp2, **argv;
1115         int base = 0;
1116         long l;
1117         int i, cmdnum, optidx, argc;
1118
1119         /* Skip leading whitespace */
1120         cp = cp + strspn(cp, WHITESPACE);
1121
1122         /* Check for leading '-' (disable error processing) */
1123         *iflag = 0;
1124         if (*cp == '-') {
1125                 *iflag = 1;
1126                 cp++;
1127                 cp = cp + strspn(cp, WHITESPACE);
1128         }
1129
1130         /* Ignore blank lines and lines which begin with comment '#' char */
1131         if (*cp == '\0' || *cp == '#')
1132                 return (0);
1133
1134         if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1135                 return -1;
1136
1137         /* Figure out which command we have */
1138         for (i = 0; cmds[i].c != NULL; i++) {
1139                 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1140                         break;
1141         }
1142         cmdnum = cmds[i].n;
1143         cmd = cmds[i].c;
1144
1145         /* Special case */
1146         if (*cp == '!') {
1147                 cp++;
1148                 cmdnum = I_SHELL;
1149         } else if (cmdnum == -1) {
1150                 error("Invalid command.");
1151                 return -1;
1152         }
1153
1154         /* Get arguments and parse flags */
1155         *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1156         *path1 = *path2 = NULL;
1157         optidx = 1;
1158         switch (cmdnum) {
1159         case I_GET:
1160         case I_PUT:
1161                 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1162                         return -1;
1163                 /* Get first pathname (mandatory) */
1164                 if (argc - optidx < 1) {
1165                         error("You must specify at least one path after a "
1166                             "%s command.", cmd);
1167                         return -1;
1168                 }
1169                 *path1 = xstrdup(argv[optidx]);
1170                 /* Get second pathname (optional) */
1171                 if (argc - optidx > 1) {
1172                         *path2 = xstrdup(argv[optidx + 1]);
1173                         /* Destination is not globbed */
1174                         undo_glob_escape(*path2);
1175                 }
1176                 break;
1177         case I_RENAME:
1178         case I_SYMLINK:
1179                 if (argc - optidx < 2) {
1180                         error("You must specify two paths after a %s "
1181                             "command.", cmd);
1182                         return -1;
1183                 }
1184                 *path1 = xstrdup(argv[optidx]);
1185                 *path2 = xstrdup(argv[optidx + 1]);
1186                 /* Paths are not globbed */
1187                 undo_glob_escape(*path1);
1188                 undo_glob_escape(*path2);
1189                 break;
1190         case I_RM:
1191         case I_MKDIR:
1192         case I_RMDIR:
1193         case I_CHDIR:
1194         case I_LCHDIR:
1195         case I_LMKDIR:
1196                 /* Get pathname (mandatory) */
1197                 if (argc - optidx < 1) {
1198                         error("You must specify a path after a %s command.",
1199                             cmd);
1200                         return -1;
1201                 }
1202                 *path1 = xstrdup(argv[optidx]);
1203                 /* Only "rm" globs */
1204                 if (cmdnum != I_RM)
1205                         undo_glob_escape(*path1);
1206                 break;
1207         case I_DF:
1208                 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1209                     iflag)) == -1)
1210                         return -1;
1211                 /* Default to current directory if no path specified */
1212                 if (argc - optidx < 1)
1213                         *path1 = NULL;
1214                 else {
1215                         *path1 = xstrdup(argv[optidx]);
1216                         undo_glob_escape(*path1);
1217                 }
1218                 break;
1219         case I_LS:
1220                 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1221                         return(-1);
1222                 /* Path is optional */
1223                 if (argc - optidx > 0)
1224                         *path1 = xstrdup(argv[optidx]);
1225                 break;
1226         case I_LLS:
1227                 /* Skip ls command and following whitespace */
1228                 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1229         case I_SHELL:
1230                 /* Uses the rest of the line */
1231                 break;
1232         case I_LUMASK:
1233         case I_CHMOD:
1234                 base = 8;
1235         case I_CHOWN:
1236         case I_CHGRP:
1237                 /* Get numeric arg (mandatory) */
1238                 if (argc - optidx < 1)
1239                         goto need_num_arg;
1240                 errno = 0;
1241                 l = strtol(argv[optidx], &cp2, base);
1242                 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1243                     ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1244                     l < 0) {
1245  need_num_arg:
1246                         error("You must supply a numeric argument "
1247                             "to the %s command.", cmd);
1248                         return -1;
1249                 }
1250                 *n_arg = l;
1251                 if (cmdnum == I_LUMASK)
1252                         break;
1253                 /* Get pathname (mandatory) */
1254                 if (argc - optidx < 2) {
1255                         error("You must specify a path after a %s command.",
1256                             cmd);
1257                         return -1;
1258                 }
1259                 *path1 = xstrdup(argv[optidx + 1]);
1260                 break;
1261         case I_QUIT:
1262         case I_PWD:
1263         case I_LPWD:
1264         case I_HELP:
1265         case I_VERSION:
1266         case I_PROGRESS:
1267                 break;
1268         default:
1269                 fatal("Command not implemented");
1270         }
1271
1272         *cpp = cp;
1273         return(cmdnum);
1274 }
1275
1276 static int
1277 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1278     int err_abort)
1279 {
1280         char *path1, *path2, *tmp;
1281         int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1282         unsigned long n_arg = 0;
1283         Attrib a, *aa;
1284         char path_buf[MAXPATHLEN];
1285         int err = 0;
1286         glob_t g;
1287
1288         path1 = path2 = NULL;
1289         cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1290             &path1, &path2);
1291
1292         if (iflag != 0)
1293                 err_abort = 0;
1294
1295         memset(&g, 0, sizeof(g));
1296
1297         /* Perform command */
1298         switch (cmdnum) {
1299         case 0:
1300                 /* Blank line */
1301                 break;
1302         case -1:
1303                 /* Unrecognized command */
1304                 err = -1;
1305                 break;
1306         case I_GET:
1307                 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1308                 break;
1309         case I_PUT:
1310                 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1311                 break;
1312         case I_RENAME:
1313                 path1 = make_absolute(path1, *pwd);
1314                 path2 = make_absolute(path2, *pwd);
1315                 err = do_rename(conn, path1, path2);
1316                 break;
1317         case I_SYMLINK:
1318                 path2 = make_absolute(path2, *pwd);
1319                 err = do_symlink(conn, path1, path2);
1320                 break;
1321         case I_RM:
1322                 path1 = make_absolute(path1, *pwd);
1323                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1324                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1325                         printf("Removing %s\n", g.gl_pathv[i]);
1326                         err = do_rm(conn, g.gl_pathv[i]);
1327                         if (err != 0 && err_abort)
1328                                 break;
1329                 }
1330                 break;
1331         case I_MKDIR:
1332                 path1 = make_absolute(path1, *pwd);
1333                 attrib_clear(&a);
1334                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1335                 a.perm = 0777;
1336                 err = do_mkdir(conn, path1, &a, 1);
1337                 break;
1338         case I_RMDIR:
1339                 path1 = make_absolute(path1, *pwd);
1340                 err = do_rmdir(conn, path1);
1341                 break;
1342         case I_CHDIR:
1343                 path1 = make_absolute(path1, *pwd);
1344                 if ((tmp = do_realpath(conn, path1)) == NULL) {
1345                         err = 1;
1346                         break;
1347                 }
1348                 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1349                         xfree(tmp);
1350                         err = 1;
1351                         break;
1352                 }
1353                 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1354                         error("Can't change directory: Can't check target");
1355                         xfree(tmp);
1356                         err = 1;
1357                         break;
1358                 }
1359                 if (!S_ISDIR(aa->perm)) {
1360                         error("Can't change directory: \"%s\" is not "
1361                             "a directory", tmp);
1362                         xfree(tmp);
1363                         err = 1;
1364                         break;
1365                 }
1366                 xfree(*pwd);
1367                 *pwd = tmp;
1368                 break;
1369         case I_LS:
1370                 if (!path1) {
1371                         do_ls_dir(conn, *pwd, *pwd, lflag);
1372                         break;
1373                 }
1374
1375                 /* Strip pwd off beginning of non-absolute paths */
1376                 tmp = NULL;
1377                 if (*path1 != '/')
1378                         tmp = *pwd;
1379
1380                 path1 = make_absolute(path1, *pwd);
1381                 err = do_globbed_ls(conn, path1, tmp, lflag);
1382                 break;
1383         case I_DF:
1384                 /* Default to current directory if no path specified */
1385                 if (path1 == NULL)
1386                         path1 = xstrdup(*pwd);
1387                 path1 = make_absolute(path1, *pwd);
1388                 err = do_df(conn, path1, hflag, iflag);
1389                 break;
1390         case I_LCHDIR:
1391                 if (chdir(path1) == -1) {
1392                         error("Couldn't change local directory to "
1393                             "\"%s\": %s", path1, strerror(errno));
1394                         err = 1;
1395                 }
1396                 break;
1397         case I_LMKDIR:
1398                 if (mkdir(path1, 0777) == -1) {
1399                         error("Couldn't create local directory "
1400                             "\"%s\": %s", path1, strerror(errno));
1401                         err = 1;
1402                 }
1403                 break;
1404         case I_LLS:
1405                 local_do_ls(cmd);
1406                 break;
1407         case I_SHELL:
1408                 local_do_shell(cmd);
1409                 break;
1410         case I_LUMASK:
1411                 umask(n_arg);
1412                 printf("Local umask: %03lo\n", n_arg);
1413                 break;
1414         case I_CHMOD:
1415                 path1 = make_absolute(path1, *pwd);
1416                 attrib_clear(&a);
1417                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1418                 a.perm = n_arg;
1419                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1420                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1421                         printf("Changing mode on %s\n", g.gl_pathv[i]);
1422                         err = do_setstat(conn, g.gl_pathv[i], &a);
1423                         if (err != 0 && err_abort)
1424                                 break;
1425                 }
1426                 break;
1427         case I_CHOWN:
1428         case I_CHGRP:
1429                 path1 = make_absolute(path1, *pwd);
1430                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1431                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1432                         if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1433                                 if (err_abort) {
1434                                         err = -1;
1435                                         break;
1436                                 } else
1437                                         continue;
1438                         }
1439                         if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1440                                 error("Can't get current ownership of "
1441                                     "remote file \"%s\"", g.gl_pathv[i]);
1442                                 if (err_abort) {
1443                                         err = -1;
1444                                         break;
1445                                 } else
1446                                         continue;
1447                         }
1448                         aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1449                         if (cmdnum == I_CHOWN) {
1450                                 printf("Changing owner on %s\n", g.gl_pathv[i]);
1451                                 aa->uid = n_arg;
1452                         } else {
1453                                 printf("Changing group on %s\n", g.gl_pathv[i]);
1454                                 aa->gid = n_arg;
1455                         }
1456                         err = do_setstat(conn, g.gl_pathv[i], aa);
1457                         if (err != 0 && err_abort)
1458                                 break;
1459                 }
1460                 break;
1461         case I_PWD:
1462                 printf("Remote working directory: %s\n", *pwd);
1463                 break;
1464         case I_LPWD:
1465                 if (!getcwd(path_buf, sizeof(path_buf))) {
1466                         error("Couldn't get local cwd: %s", strerror(errno));
1467                         err = -1;
1468                         break;
1469                 }
1470                 printf("Local working directory: %s\n", path_buf);
1471                 break;
1472         case I_QUIT:
1473                 /* Processed below */
1474                 break;
1475         case I_HELP:
1476                 help();
1477                 break;
1478         case I_VERSION:
1479                 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1480                 break;
1481         case I_PROGRESS:
1482                 showprogress = !showprogress;
1483                 if (showprogress)
1484                         printf("Progress meter enabled\n");
1485                 else
1486                         printf("Progress meter disabled\n");
1487                 break;
1488         default:
1489                 fatal("%d is not implemented", cmdnum);
1490         }
1491
1492         if (g.gl_pathc)
1493                 globfree(&g);
1494         if (path1)
1495                 xfree(path1);
1496         if (path2)
1497                 xfree(path2);
1498
1499         /* If an unignored error occurs in batch mode we should abort. */
1500         if (err_abort && err != 0)
1501                 return (-1);
1502         else if (cmdnum == I_QUIT)
1503                 return (1);
1504
1505         return (0);
1506 }
1507
1508 #ifdef USE_LIBEDIT
1509 static char *
1510 prompt(EditLine *el)
1511 {
1512         return ("sftp> ");
1513 }
1514
1515 /* Display entries in 'list' after skipping the first 'len' chars */
1516 static void
1517 complete_display(char **list, u_int len)
1518 {
1519         u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1520         struct winsize ws;
1521         char *tmp;
1522
1523         /* Count entries for sort and find longest */
1524         for (y = 0; list[y]; y++) 
1525                 m = MAX(m, strlen(list[y]));
1526
1527         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1528                 width = ws.ws_col;
1529
1530         m = m > len ? m - len : 0;
1531         columns = width / (m + 2);
1532         columns = MAX(columns, 1);
1533         colspace = width / columns;
1534         colspace = MIN(colspace, width);
1535
1536         printf("\n");
1537         m = 1;
1538         for (y = 0; list[y]; y++) {
1539                 llen = strlen(list[y]);
1540                 tmp = llen > len ? list[y] + len : "";
1541                 printf("%-*s", colspace, tmp);
1542                 if (m >= columns) {
1543                         printf("\n");
1544                         m = 1;
1545                 } else
1546                         m++;
1547         }
1548         printf("\n");
1549 }
1550
1551 /*
1552  * Given a "list" of words that begin with a common prefix of "word",
1553  * attempt to find an autocompletion to extends "word" by the next
1554  * characters common to all entries in "list".
1555  */
1556 static char *
1557 complete_ambiguous(const char *word, char **list, size_t count)
1558 {
1559         if (word == NULL)
1560                 return NULL;
1561
1562         if (count > 0) {
1563                 u_int y, matchlen = strlen(list[0]);
1564
1565                 /* Find length of common stem */
1566                 for (y = 1; list[y]; y++) {
1567                         u_int x;
1568
1569                         for (x = 0; x < matchlen; x++) 
1570                                 if (list[0][x] != list[y][x]) 
1571                                         break;
1572
1573                         matchlen = x;
1574                 }
1575
1576                 if (matchlen > strlen(word)) {
1577                         char *tmp = xstrdup(list[0]);
1578
1579                         tmp[matchlen] = '\0';
1580                         return tmp;
1581                 }
1582         } 
1583
1584         return xstrdup(word);
1585 }
1586
1587 /* Autocomplete a sftp command */
1588 static int
1589 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1590     int terminated)
1591 {
1592         u_int y, count = 0, cmdlen, tmplen;
1593         char *tmp, **list, argterm[3];
1594         const LineInfo *lf;
1595
1596         list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1597
1598         /* No command specified: display all available commands */
1599         if (cmd == NULL) {
1600                 for (y = 0; cmds[y].c; y++)
1601                         list[count++] = xstrdup(cmds[y].c);
1602                 
1603                 list[count] = NULL;
1604                 complete_display(list, 0);
1605
1606                 for (y = 0; list[y] != NULL; y++)  
1607                         xfree(list[y]); 
1608                 xfree(list);
1609                 return count;
1610         }
1611
1612         /* Prepare subset of commands that start with "cmd" */
1613         cmdlen = strlen(cmd);
1614         for (y = 0; cmds[y].c; y++)  {
1615                 if (!strncasecmp(cmd, cmds[y].c, cmdlen)) 
1616                         list[count++] = xstrdup(cmds[y].c);
1617         }
1618         list[count] = NULL;
1619
1620         if (count == 0)
1621                 return 0;
1622
1623         /* Complete ambigious command */
1624         tmp = complete_ambiguous(cmd, list, count);
1625         if (count > 1)
1626                 complete_display(list, 0);
1627
1628         for (y = 0; list[y]; y++)  
1629                 xfree(list[y]); 
1630         xfree(list);
1631
1632         if (tmp != NULL) {
1633                 tmplen = strlen(tmp);
1634                 cmdlen = strlen(cmd);
1635                 /* If cmd may be extended then do so */
1636                 if (tmplen > cmdlen)
1637                         if (el_insertstr(el, tmp + cmdlen) == -1)
1638                                 fatal("el_insertstr failed.");
1639                 lf = el_line(el);
1640                 /* Terminate argument cleanly */
1641                 if (count == 1) {
1642                         y = 0;
1643                         if (!terminated)
1644                                 argterm[y++] = quote;
1645                         if (lastarg || *(lf->cursor) != ' ')
1646                                 argterm[y++] = ' ';
1647                         argterm[y] = '\0';
1648                         if (y > 0 && el_insertstr(el, argterm) == -1)
1649                                 fatal("el_insertstr failed.");
1650                 }
1651                 xfree(tmp);
1652         }
1653
1654         return count;
1655 }
1656
1657 /*
1658  * Determine whether a particular sftp command's arguments (if any)
1659  * represent local or remote files.
1660  */
1661 static int
1662 complete_is_remote(char *cmd) {
1663         int i;
1664
1665         if (cmd == NULL)
1666                 return -1;
1667
1668         for (i = 0; cmds[i].c; i++) {
1669                 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 
1670                         return cmds[i].t;
1671         }
1672
1673         return -1;
1674 }
1675
1676 /* Autocomplete a filename "file" */
1677 static int
1678 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1679     char *file, int remote, int lastarg, char quote, int terminated)
1680 {
1681         glob_t g;
1682         char *tmp, *tmp2, ins[3];
1683         u_int i, hadglob, pwdlen, len, tmplen, filelen;
1684         const LineInfo *lf;
1685         
1686         /* Glob from "file" location */
1687         if (file == NULL)
1688                 tmp = xstrdup("*");
1689         else
1690                 xasprintf(&tmp, "%s*", file);
1691
1692         memset(&g, 0, sizeof(g));
1693         if (remote != LOCAL) {
1694                 tmp = make_absolute(tmp, remote_path);
1695                 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1696         } else 
1697                 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1698         
1699         /* Determine length of pwd so we can trim completion display */
1700         for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1701                 /* Terminate counting on first unescaped glob metacharacter */
1702                 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1703                         if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1704                                 hadglob = 1;
1705                         break;
1706                 }
1707                 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1708                         tmplen++;
1709                 if (tmp[tmplen] == '/')
1710                         pwdlen = tmplen + 1;    /* track last seen '/' */
1711         }
1712         xfree(tmp);
1713
1714         if (g.gl_matchc == 0) 
1715                 goto out;
1716
1717         if (g.gl_matchc > 1)
1718                 complete_display(g.gl_pathv, pwdlen);
1719
1720         tmp = NULL;
1721         /* Don't try to extend globs */
1722         if (file == NULL || hadglob)
1723                 goto out;
1724
1725         tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1726         tmp = path_strip(tmp2, remote_path);
1727         xfree(tmp2);
1728
1729         if (tmp == NULL)
1730                 goto out;
1731
1732         tmplen = strlen(tmp);
1733         filelen = strlen(file);
1734
1735         if (tmplen > filelen)  {
1736                 tmp2 = tmp + filelen;
1737                 len = strlen(tmp2); 
1738                 /* quote argument on way out */
1739                 for (i = 0; i < len; i++) {
1740                         ins[0] = '\\';
1741                         ins[1] = tmp2[i];
1742                         ins[2] = '\0';
1743                         switch (tmp2[i]) {
1744                         case '\'':
1745                         case '"':
1746                         case '\\':
1747                         case '\t':
1748                         case ' ':
1749                                 if (quote == '\0' || tmp2[i] == quote) {
1750                                         if (el_insertstr(el, ins) == -1)
1751                                                 fatal("el_insertstr "
1752                                                     "failed.");
1753                                         break;
1754                                 }
1755                                 /* FALLTHROUGH */
1756                         default:
1757                                 if (el_insertstr(el, ins + 1) == -1)
1758                                         fatal("el_insertstr failed.");
1759                                 break;
1760                         }
1761                 }
1762         }
1763
1764         lf = el_line(el);
1765         if (g.gl_matchc == 1) {
1766                 i = 0;
1767                 if (!terminated)
1768                         ins[i++] = quote;
1769                 if (*(lf->cursor - 1) != '/' &&
1770                     (lastarg || *(lf->cursor) != ' '))
1771                         ins[i++] = ' ';
1772                 ins[i] = '\0';
1773                 if (i > 0 && el_insertstr(el, ins) == -1)
1774                         fatal("el_insertstr failed.");
1775         }
1776         xfree(tmp);
1777
1778  out:
1779         globfree(&g);
1780         return g.gl_matchc;
1781 }
1782
1783 /* tab-completion hook function, called via libedit */
1784 static unsigned char
1785 complete(EditLine *el, int ch)
1786 {
1787         char **argv, *line, quote; 
1788         u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1789         const LineInfo *lf;
1790         struct complete_ctx *complete_ctx;
1791
1792         lf = el_line(el);
1793         if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1794                 fatal("%s: el_get failed", __func__);
1795
1796         /* Figure out which argument the cursor points to */
1797         cursor = lf->cursor - lf->buffer;
1798         line = (char *)xmalloc(cursor + 1);
1799         memcpy(line, lf->buffer, cursor);
1800         line[cursor] = '\0';
1801         argv = makeargv(line, &carg, 1, &quote, &terminated);
1802         xfree(line);
1803
1804         /* Get all the arguments on the line */
1805         len = lf->lastchar - lf->buffer;
1806         line = (char *)xmalloc(len + 1);
1807         memcpy(line, lf->buffer, len);
1808         line[len] = '\0';
1809         argv = makeargv(line, &argc, 1, NULL, NULL);
1810
1811         /* Ensure cursor is at EOL or a argument boundary */
1812         if (line[cursor] != ' ' && line[cursor] != '\0' &&
1813             line[cursor] != '\n') {
1814                 xfree(line);
1815                 return ret;
1816         }
1817
1818         if (carg == 0) {
1819                 /* Show all available commands */
1820                 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1821                 ret = CC_REDISPLAY;
1822         } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
1823                 /* Handle the command parsing */
1824                 if (complete_cmd_parse(el, argv[0], argc == carg,
1825                     quote, terminated) != 0) 
1826                         ret = CC_REDISPLAY;
1827         } else if (carg >= 1) {
1828                 /* Handle file parsing */
1829                 int remote = complete_is_remote(argv[0]);
1830                 char *filematch = NULL;
1831
1832                 if (carg > 1 && line[cursor-1] != ' ')
1833                         filematch = argv[carg - 1];
1834
1835                 if (remote != 0 &&
1836                     complete_match(el, complete_ctx->conn,
1837                     *complete_ctx->remote_pathp, filematch,
1838                     remote, carg == argc, quote, terminated) != 0) 
1839                         ret = CC_REDISPLAY;
1840         }
1841
1842         xfree(line);    
1843         return ret;
1844 }
1845 #endif /* USE_LIBEDIT */
1846
1847 int
1848 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1849 {
1850         char *remote_path;
1851         char *dir = NULL;
1852         char cmd[2048];
1853         int err, interactive;
1854         EditLine *el = NULL;
1855 #ifdef USE_LIBEDIT
1856         History *hl = NULL;
1857         HistEvent hev;
1858         extern char *__progname;
1859         struct complete_ctx complete_ctx;
1860
1861         if (!batchmode && isatty(STDIN_FILENO)) {
1862                 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1863                         fatal("Couldn't initialise editline");
1864                 if ((hl = history_init()) == NULL)
1865                         fatal("Couldn't initialise editline history");
1866                 history(hl, &hev, H_SETSIZE, 100);
1867                 el_set(el, EL_HIST, history, hl);
1868
1869                 el_set(el, EL_PROMPT, prompt);
1870                 el_set(el, EL_EDITOR, "emacs");
1871                 el_set(el, EL_TERMINAL, NULL);
1872                 el_set(el, EL_SIGNAL, 1);
1873                 el_source(el, NULL);
1874
1875                 /* Tab Completion */
1876                 el_set(el, EL_ADDFN, "ftp-complete", 
1877                     "Context senstive argument completion", complete);
1878                 complete_ctx.conn = conn;
1879                 complete_ctx.remote_pathp = &remote_path;
1880                 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1881                 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1882         }
1883 #endif /* USE_LIBEDIT */
1884
1885         remote_path = do_realpath(conn, ".");
1886         if (remote_path == NULL)
1887                 fatal("Need cwd");
1888
1889         if (file1 != NULL) {
1890                 dir = xstrdup(file1);
1891                 dir = make_absolute(dir, remote_path);
1892
1893                 if (remote_is_dir(conn, dir) && file2 == NULL) {
1894                         printf("Changing to: %s\n", dir);
1895                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1896                         if (parse_dispatch_command(conn, cmd,
1897                             &remote_path, 1) != 0) {
1898                                 xfree(dir);
1899                                 xfree(remote_path);
1900                                 xfree(conn);
1901                                 return (-1);
1902                         }
1903                 } else {
1904                         if (file2 == NULL)
1905                                 snprintf(cmd, sizeof cmd, "get %s", dir);
1906                         else
1907                                 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1908                                     file2);
1909
1910                         err = parse_dispatch_command(conn, cmd,
1911                             &remote_path, 1);
1912                         xfree(dir);
1913                         xfree(remote_path);
1914                         xfree(conn);
1915                         return (err);
1916                 }
1917                 xfree(dir);
1918         }
1919
1920 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1921         setvbuf(stdout, NULL, _IOLBF, 0);
1922         setvbuf(infile, NULL, _IOLBF, 0);
1923 #else
1924         setlinebuf(stdout);
1925         setlinebuf(infile);
1926 #endif
1927
1928         interactive = !batchmode && isatty(STDIN_FILENO);
1929         err = 0;
1930         for (;;) {
1931                 char *cp;
1932
1933                 signal(SIGINT, SIG_IGN);
1934
1935                 if (el == NULL) {
1936                         if (interactive)
1937                                 printf("sftp> ");
1938                         if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1939                                 if (interactive)
1940                                         printf("\n");
1941                                 break;
1942                         }
1943                         if (!interactive) { /* Echo command */
1944                                 printf("sftp> %s", cmd);
1945                                 if (strlen(cmd) > 0 &&
1946                                     cmd[strlen(cmd) - 1] != '\n')
1947                                         printf("\n");
1948                         }
1949                 } else {
1950 #ifdef USE_LIBEDIT
1951                         const char *line;
1952                         int count = 0;
1953
1954                         if ((line = el_gets(el, &count)) == NULL ||
1955                             count <= 0) {
1956                                 printf("\n");
1957                                 break;
1958                         }
1959                         history(hl, &hev, H_ENTER, line);
1960                         if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1961                                 fprintf(stderr, "Error: input line too long\n");
1962                                 continue;
1963                         }
1964 #endif /* USE_LIBEDIT */
1965                 }
1966
1967                 cp = strrchr(cmd, '\n');
1968                 if (cp)
1969                         *cp = '\0';
1970
1971                 /* Handle user interrupts gracefully during commands */
1972                 interrupted = 0;
1973                 signal(SIGINT, cmd_interrupt);
1974
1975                 err = parse_dispatch_command(conn, cmd, &remote_path,
1976                     batchmode);
1977                 if (err != 0)
1978                         break;
1979         }
1980         xfree(remote_path);
1981         xfree(conn);
1982
1983 #ifdef USE_LIBEDIT
1984         if (el != NULL)
1985                 el_end(el);
1986 #endif /* USE_LIBEDIT */
1987
1988         /* err == 1 signifies normal "quit" exit */
1989         return (err >= 0 ? 0 : -1);
1990 }
1991
1992 static void
1993 connect_to_server(char *path, char **args, int *in, int *out)
1994 {
1995         int c_in, c_out;
1996
1997 #ifdef USE_PIPES
1998         int pin[2], pout[2];
1999
2000         if ((pipe(pin) == -1) || (pipe(pout) == -1))
2001                 fatal("pipe: %s", strerror(errno));
2002         *in = pin[0];
2003         *out = pout[1];
2004         c_in = pout[0];
2005         c_out = pin[1];
2006 #else /* USE_PIPES */
2007         int inout[2];
2008
2009         if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2010                 fatal("socketpair: %s", strerror(errno));
2011         *in = *out = inout[0];
2012         c_in = c_out = inout[1];
2013 #endif /* USE_PIPES */
2014
2015         if ((sshpid = fork()) == -1)
2016                 fatal("fork: %s", strerror(errno));
2017         else if (sshpid == 0) {
2018                 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2019                     (dup2(c_out, STDOUT_FILENO) == -1)) {
2020                         fprintf(stderr, "dup2: %s\n", strerror(errno));
2021                         _exit(1);
2022                 }
2023                 close(*in);
2024                 close(*out);
2025                 close(c_in);
2026                 close(c_out);
2027
2028                 /*
2029                  * The underlying ssh is in the same process group, so we must
2030                  * ignore SIGINT if we want to gracefully abort commands,
2031                  * otherwise the signal will make it to the ssh process and
2032                  * kill it too.  Contrawise, since sftp sends SIGTERMs to the
2033                  * underlying ssh, it must *not* ignore that signal.
2034                  */
2035                 signal(SIGINT, SIG_IGN);
2036                 signal(SIGTERM, SIG_DFL);
2037                 execvp(path, args);
2038                 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2039                 _exit(1);
2040         }
2041
2042         signal(SIGTERM, killchild);
2043         signal(SIGINT, killchild);
2044         signal(SIGHUP, killchild);
2045         close(c_in);
2046         close(c_out);
2047 }
2048
2049 static void
2050 usage(void)
2051 {
2052         extern char *__progname;
2053
2054         fprintf(stderr,
2055             "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2056             "          [-D sftp_server_path] [-F ssh_config] "
2057             "[-i identity_file]\n"
2058             "          [-o ssh_option] [-P port] [-R num_requests] "
2059             "[-S program]\n"
2060             "          [-s subsystem | sftp_server] host\n"
2061             "       %s [user@]host[:file ...]\n"
2062             "       %s [user@]host[:dir[/]]\n"
2063             "       %s -b batchfile [user@]host\n",
2064             __progname, __progname, __progname, __progname);
2065         exit(1);
2066 }
2067
2068 int
2069 main(int argc, char **argv)
2070 {
2071         int in, out, ch, err;
2072         char *host = NULL, *userhost, *cp, *file2 = NULL;
2073         int debug_level = 0, sshver = 2;
2074         char *file1 = NULL, *sftp_server = NULL;
2075         char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2076         LogLevel ll = SYSLOG_LEVEL_INFO;
2077         arglist args;
2078         extern int optind;
2079         extern char *optarg;
2080         struct sftp_conn *conn;
2081         size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2082         size_t num_requests = DEFAULT_NUM_REQUESTS;
2083
2084         /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2085         sanitise_stdfd();
2086
2087         __progname = ssh_get_progname(argv[0]);
2088         memset(&args, '\0', sizeof(args));
2089         args.list = NULL;
2090         addargs(&args, "%s", ssh_program);
2091         addargs(&args, "-oForwardX11 no");
2092         addargs(&args, "-oForwardAgent no");
2093         addargs(&args, "-oPermitLocalCommand no");
2094         addargs(&args, "-oClearAllForwardings yes");
2095
2096         ll = SYSLOG_LEVEL_INFO;
2097         infile = stdin;
2098
2099         while ((ch = getopt(argc, argv,
2100             "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2101                 switch (ch) {
2102                 /* Passed through to ssh(1) */
2103                 case '4':
2104                 case '6':
2105                 case 'C':
2106                         addargs(&args, "-%c", ch);
2107                         break;
2108                 /* Passed through to ssh(1) with argument */
2109                 case 'F':
2110                 case 'c':
2111                 case 'i':
2112                 case 'o':
2113                         addargs(&args, "-%c", ch);
2114                         addargs(&args, "%s", optarg);
2115                         break;
2116                 case 'q':
2117                         showprogress = 0;
2118                         addargs(&args, "-%c", ch);
2119                         break;
2120                 case 'P':
2121                         addargs(&args, "-oPort %s", optarg);
2122                         break;
2123                 case 'v':
2124                         if (debug_level < 3) {
2125                                 addargs(&args, "-v");
2126                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2127                         }
2128                         debug_level++;
2129                         break;
2130                 case '1':
2131                         sshver = 1;
2132                         if (sftp_server == NULL)
2133                                 sftp_server = _PATH_SFTP_SERVER;
2134                         break;
2135                 case '2':
2136                         sshver = 2;
2137                         break;
2138                 case 'B':
2139                         copy_buffer_len = strtol(optarg, &cp, 10);
2140                         if (copy_buffer_len == 0 || *cp != '\0')
2141                                 fatal("Invalid buffer size \"%s\"", optarg);
2142                         break;
2143                 case 'b':
2144                         if (batchmode)
2145                                 fatal("Batch file already specified.");
2146
2147                         /* Allow "-" as stdin */
2148                         if (strcmp(optarg, "-") != 0 &&
2149                             (infile = fopen(optarg, "r")) == NULL)
2150                                 fatal("%s (%s).", strerror(errno), optarg);
2151                         showprogress = 0;
2152                         batchmode = 1;
2153                         addargs(&args, "-obatchmode yes");
2154                         break;
2155                 case 'p':
2156                         global_pflag = 1;
2157                         break;
2158                 case 'D':
2159                         sftp_direct = optarg;
2160                         break;
2161                 case 'r':
2162                         global_rflag = 1;
2163                         break;
2164                 case 'R':
2165                         num_requests = strtol(optarg, &cp, 10);
2166                         if (num_requests == 0 || *cp != '\0')
2167                                 fatal("Invalid number of requests \"%s\"",
2168                                     optarg);
2169                         break;
2170                 case 's':
2171                         sftp_server = optarg;
2172                         break;
2173                 case 'S':
2174                         ssh_program = optarg;
2175                         replacearg(&args, 0, "%s", ssh_program);
2176                         break;
2177                 case 'h':
2178                 default:
2179                         usage();
2180                 }
2181         }
2182
2183         if (!isatty(STDERR_FILENO))
2184                 showprogress = 0;
2185
2186         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2187
2188         if (sftp_direct == NULL) {
2189                 if (optind == argc || argc > (optind + 2))
2190                         usage();
2191
2192                 userhost = xstrdup(argv[optind]);
2193                 file2 = argv[optind+1];
2194
2195                 if ((host = strrchr(userhost, '@')) == NULL)
2196                         host = userhost;
2197                 else {
2198                         *host++ = '\0';
2199                         if (!userhost[0]) {
2200                                 fprintf(stderr, "Missing username\n");
2201                                 usage();
2202                         }
2203                         addargs(&args, "-l");
2204                         addargs(&args, "%s", userhost);
2205                 }
2206
2207                 if ((cp = colon(host)) != NULL) {
2208                         *cp++ = '\0';
2209                         file1 = cp;
2210                 }
2211
2212                 host = cleanhostname(host);
2213                 if (!*host) {
2214                         fprintf(stderr, "Missing hostname\n");
2215                         usage();
2216                 }
2217
2218                 addargs(&args, "-oProtocol %d", sshver);
2219
2220                 /* no subsystem if the server-spec contains a '/' */
2221                 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2222                         addargs(&args, "-s");
2223
2224                 addargs(&args, "--");
2225                 addargs(&args, "%s", host);
2226                 addargs(&args, "%s", (sftp_server != NULL ?
2227                     sftp_server : "sftp"));
2228
2229                 connect_to_server(ssh_program, args.list, &in, &out);
2230         } else {
2231                 args.list = NULL;
2232                 addargs(&args, "sftp-server");
2233
2234                 connect_to_server(sftp_direct, args.list, &in, &out);
2235         }
2236         freeargs(&args);
2237
2238         conn = do_init(in, out, copy_buffer_len, num_requests);
2239         if (conn == NULL)
2240                 fatal("Couldn't initialise connection to server");
2241
2242         if (!batchmode) {
2243                 if (sftp_direct == NULL)
2244                         fprintf(stderr, "Connected to %s.\n", host);
2245                 else
2246                         fprintf(stderr, "Attached to %s.\n", sftp_direct);
2247         }
2248
2249         err = interactive_loop(conn, file1, file2);
2250
2251 #if !defined(USE_PIPES)
2252         shutdown(in, SHUT_RDWR);
2253         shutdown(out, SHUT_RDWR);
2254 #endif
2255
2256         close(in);
2257         close(out);
2258         if (batchmode)
2259                 fclose(infile);
2260
2261         while (waitpid(sshpid, NULL, 0) == -1)
2262                 if (errno != EINTR)
2263                         fatal("Couldn't wait for ssh process: %s",
2264                             strerror(errno));
2265
2266         exit(err == 0 ? 0 : 1);
2267 }