Home | History | Annotate | Line # | Download | only in dist
      1  1.38       rin /*	$NetBSD: sftp.c,v 1.44 2026/04/08 18:58:41 christos Exp $	*/
      2  1.44  christos /* $OpenBSD: sftp.c,v 1.250 2026/02/11 17:01:34 dtucker Exp $ */
      3  1.39  christos 
      4   1.1  christos /*
      5   1.1  christos  * Copyright (c) 2001-2004 Damien Miller <djm (at) openbsd.org>
      6   1.1  christos  *
      7   1.1  christos  * Permission to use, copy, modify, and distribute this software for any
      8   1.1  christos  * purpose with or without fee is hereby granted, provided that the above
      9   1.1  christos  * copyright notice and this permission notice appear in all copies.
     10   1.1  christos  *
     11   1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12   1.1  christos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13   1.1  christos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14   1.1  christos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15   1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16   1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17   1.1  christos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18   1.1  christos  */
     19   1.1  christos 
     20   1.2  christos #include "includes.h"
     21  1.38       rin __RCSID("$NetBSD: sftp.c,v 1.44 2026/04/08 18:58:41 christos Exp $");
     22  1.20  christos 
     23  1.14  christos #include <sys/param.h>	/* MIN MAX */
     24   1.1  christos #include <sys/types.h>
     25   1.1  christos #include <sys/ioctl.h>
     26   1.1  christos #include <sys/stat.h>
     27   1.1  christos #include <sys/socket.h>
     28   1.1  christos #include <sys/statvfs.h>
     29  1.44  christos #include <sys/wait.h>
     30   1.1  christos 
     31   1.1  christos #include <ctype.h>
     32   1.1  christos #include <errno.h>
     33   1.1  christos #include <glob.h>
     34   1.1  christos #include <histedit.h>
     35   1.1  christos #include <paths.h>
     36   1.4      adam #include <libgen.h>
     37  1.44  christos #include <limits.h>
     38  1.12  christos #include <locale.h>
     39   1.1  christos #include <signal.h>
     40  1.19  christos #include <stdarg.h>
     41   1.1  christos #include <stdlib.h>
     42   1.1  christos #include <stdio.h>
     43   1.1  christos #include <string.h>
     44   1.1  christos #include <unistd.h>
     45   1.1  christos #include <util.h>
     46   1.1  christos 
     47   1.1  christos #include "xmalloc.h"
     48   1.1  christos #include "log.h"
     49   1.1  christos #include "pathnames.h"
     50   1.1  christos #include "misc.h"
     51  1.19  christos #include "utf8.h"
     52   1.1  christos 
     53   1.1  christos #include "sftp.h"
     54  1.14  christos #include "ssherr.h"
     55  1.14  christos #include "sshbuf.h"
     56   1.1  christos #include "sftp-common.h"
     57   1.1  christos #include "sftp-client.h"
     58  1.36  christos #include "sftp-usergroup.h"
     59  1.36  christos 
     60   1.2  christos #include "fmt_scaled.h"
     61   1.1  christos 
     62   1.1  christos /* File to read commands from */
     63   1.1  christos FILE* infile;
     64   1.1  christos 
     65   1.1  christos /* Are we in batchfile mode? */
     66   1.1  christos int batchmode = 0;
     67   1.1  christos 
     68   1.1  christos /* PID of ssh transport process */
     69  1.24  christos static volatile pid_t sshpid = -1;
     70   1.1  christos 
     71  1.32  christos /* Suppress diagnostic messages */
     72  1.12  christos int quiet = 0;
     73  1.12  christos 
     74   1.1  christos /* This is set to 0 if the progressmeter is not desired. */
     75   1.1  christos int showprogress = 1;
     76   1.1  christos 
     77   1.4      adam /* When this option is set, we always recursively download/upload directories */
     78   1.4      adam int global_rflag = 0;
     79   1.4      adam 
     80  1.13  christos /* When this option is set, we resume download or upload if possible */
     81  1.12  christos int global_aflag = 0;
     82  1.12  christos 
     83   1.4      adam /* When this option is set, the file transfers will always preserve times */
     84   1.4      adam int global_pflag = 0;
     85   1.4      adam 
     86  1.13  christos /* When this option is set, transfers will have fsync() called on each file */
     87  1.13  christos int global_fflag = 0;
     88  1.13  christos 
     89   1.1  christos /* SIGINT received during command processing */
     90   1.1  christos volatile sig_atomic_t interrupted = 0;
     91   1.1  christos 
     92   1.1  christos /* I wish qsort() took a separate ctx for the comparison function...*/
     93   1.1  christos int sort_flag;
     94  1.22  christos glob_t *sort_glob;
     95   1.1  christos 
     96   1.4      adam /* Context used for commandline completion */
     97   1.4      adam struct complete_ctx {
     98   1.4      adam 	struct sftp_conn *conn;
     99   1.4      adam 	char **remote_pathp;
    100   1.4      adam };
    101   1.4      adam 
    102  1.39  christos int sftp_glob(struct sftp_conn *, const char *, int,
    103   1.1  christos     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
    104   1.1  christos 
    105   1.1  christos /* Separators for interactive commands */
    106   1.1  christos #define WHITESPACE " \t\r\n"
    107   1.1  christos 
    108   1.1  christos /* ls flags */
    109   1.4      adam #define LS_LONG_VIEW	0x0001	/* Full view ala ls -l */
    110   1.4      adam #define LS_SHORT_VIEW	0x0002	/* Single row view ala ls -1 */
    111   1.4      adam #define LS_NUMERIC_VIEW	0x0004	/* Long view with numeric uid/gid */
    112   1.4      adam #define LS_NAME_SORT	0x0008	/* Sort by name (default) */
    113   1.4      adam #define LS_TIME_SORT	0x0010	/* Sort by mtime */
    114   1.4      adam #define LS_SIZE_SORT	0x0020	/* Sort by file size */
    115   1.4      adam #define LS_REVERSE_SORT	0x0040	/* Reverse sort order */
    116   1.4      adam #define LS_SHOW_ALL	0x0080	/* Don't skip filenames starting with '.' */
    117   1.4      adam #define LS_SI_UNITS	0x0100	/* Display sizes as K, M, G, etc. */
    118   1.1  christos 
    119   1.4      adam #define VIEW_FLAGS	(LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
    120   1.1  christos #define SORT_FLAGS	(LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
    121   1.1  christos 
    122   1.1  christos /* Commands for interactive mode */
    123  1.13  christos enum sftp_command {
    124  1.13  christos 	I_CHDIR = 1,
    125  1.13  christos 	I_CHGRP,
    126  1.13  christos 	I_CHMOD,
    127  1.13  christos 	I_CHOWN,
    128  1.35  christos 	I_COPY,
    129  1.13  christos 	I_DF,
    130  1.13  christos 	I_GET,
    131  1.13  christos 	I_HELP,
    132  1.13  christos 	I_LCHDIR,
    133  1.13  christos 	I_LINK,
    134  1.13  christos 	I_LLS,
    135  1.13  christos 	I_LMKDIR,
    136  1.13  christos 	I_LPWD,
    137  1.13  christos 	I_LS,
    138  1.13  christos 	I_LUMASK,
    139  1.13  christos 	I_MKDIR,
    140  1.13  christos 	I_PUT,
    141  1.13  christos 	I_PWD,
    142  1.13  christos 	I_QUIT,
    143  1.13  christos 	I_REGET,
    144  1.13  christos 	I_RENAME,
    145  1.13  christos 	I_REPUT,
    146  1.13  christos 	I_RM,
    147  1.13  christos 	I_RMDIR,
    148  1.13  christos 	I_SHELL,
    149  1.13  christos 	I_SYMLINK,
    150  1.13  christos 	I_VERSION,
    151  1.13  christos 	I_PROGRESS,
    152  1.13  christos };
    153   1.1  christos 
    154   1.1  christos struct CMD {
    155   1.1  christos 	const char *c;
    156   1.1  christos 	const int n;
    157  1.36  christos 	const int t;	/* Completion type for the first argument */
    158  1.36  christos 	const int t2;	/* completion type for the optional second argument */
    159   1.1  christos };
    160   1.1  christos 
    161   1.4      adam /* Type of completion */
    162   1.4      adam #define NOARGS	0
    163   1.4      adam #define REMOTE	1
    164   1.4      adam #define LOCAL	2
    165   1.4      adam 
    166   1.1  christos static const struct CMD cmds[] = {
    167  1.40  christos 	{ "bye",	I_QUIT,		NOARGS,		NOARGS	},
    168  1.40  christos 	{ "cd",		I_CHDIR,	REMOTE,		NOARGS	},
    169  1.40  christos 	{ "chdir",	I_CHDIR,	REMOTE,		NOARGS	},
    170  1.40  christos 	{ "chgrp",	I_CHGRP,	REMOTE,		NOARGS	},
    171  1.40  christos 	{ "chmod",	I_CHMOD,	REMOTE,		NOARGS	},
    172  1.40  christos 	{ "chown",	I_CHOWN,	REMOTE,		NOARGS	},
    173  1.40  christos 	{ "copy",	I_COPY,		REMOTE,		LOCAL	},
    174  1.40  christos 	{ "cp",		I_COPY,		REMOTE,		LOCAL	},
    175  1.40  christos 	{ "df",		I_DF,		REMOTE,		NOARGS	},
    176  1.40  christos 	{ "dir",	I_LS,		REMOTE,		NOARGS	},
    177  1.40  christos 	{ "exit",	I_QUIT,		NOARGS,		NOARGS	},
    178  1.40  christos 	{ "get",	I_GET,		REMOTE,		LOCAL	},
    179  1.40  christos 	{ "help",	I_HELP,		NOARGS,		NOARGS	},
    180  1.36  christos 	{ "lcd",	I_LCHDIR,	LOCAL,		NOARGS	},
    181  1.36  christos 	{ "lchdir",	I_LCHDIR,	LOCAL,		NOARGS	},
    182  1.36  christos 	{ "lls",	I_LLS,		LOCAL,		NOARGS	},
    183  1.36  christos 	{ "lmkdir",	I_LMKDIR,	LOCAL,		NOARGS	},
    184  1.40  christos 	{ "ln",		I_LINK,		REMOTE,		REMOTE	},
    185  1.36  christos 	{ "lpwd",	I_LPWD,		LOCAL,		NOARGS	},
    186  1.36  christos 	{ "ls",		I_LS,		REMOTE,		NOARGS	},
    187  1.36  christos 	{ "lumask",	I_LUMASK,	NOARGS,		NOARGS	},
    188  1.36  christos 	{ "mkdir",	I_MKDIR,	REMOTE,		NOARGS	},
    189  1.36  christos 	{ "mget",	I_GET,		REMOTE,		LOCAL	},
    190  1.36  christos 	{ "mput",	I_PUT,		LOCAL,		REMOTE	},
    191  1.36  christos 	{ "progress",	I_PROGRESS,	NOARGS,		NOARGS	},
    192  1.36  christos 	{ "put",	I_PUT,		LOCAL,		REMOTE	},
    193  1.40  christos 	{ "pwd",	I_PWD,		REMOTE,		NOARGS	},
    194  1.40  christos 	{ "quit",	I_QUIT,		NOARGS,		NOARGS	},
    195  1.40  christos 	{ "reget",	I_REGET,	REMOTE,		LOCAL	},
    196  1.40  christos 	{ "rename",	I_RENAME,	REMOTE,		REMOTE	},
    197  1.36  christos 	{ "reput",	I_REPUT,	LOCAL,		REMOTE	},
    198  1.36  christos 	{ "rm",		I_RM,		REMOTE,		NOARGS	},
    199  1.36  christos 	{ "rmdir",	I_RMDIR,	REMOTE,		NOARGS	},
    200  1.36  christos 	{ "symlink",	I_SYMLINK,	REMOTE,		REMOTE	},
    201  1.40  christos 	{ "version",	I_VERSION,	NOARGS,		NOARGS	},
    202  1.40  christos 	{ "!",		I_SHELL,	NOARGS,		NOARGS	},
    203  1.40  christos 	{ "?",		I_HELP,		NOARGS,		NOARGS	},
    204  1.36  christos 	{ NULL,		-1,		-1,		-1	}
    205   1.1  christos };
    206   1.1  christos 
    207   1.1  christos /* ARGSUSED */
    208   1.8     joerg __dead static void
    209   1.1  christos killchild(int signo)
    210   1.1  christos {
    211  1.28  christos 	pid_t pid;
    212  1.28  christos 
    213  1.28  christos 	pid = sshpid;
    214  1.28  christos 	if (pid > 1) {
    215  1.28  christos 		kill(pid, SIGTERM);
    216  1.39  christos 		(void)waitpid(pid, NULL, 0);
    217   1.1  christos 	}
    218   1.1  christos 
    219   1.1  christos 	_exit(1);
    220   1.1  christos }
    221   1.1  christos 
    222   1.1  christos static void
    223  1.20  christos suspchild(int signo)
    224  1.20  christos {
    225  1.41  christos 	int save_errno = errno;
    226  1.20  christos 	if (sshpid > 1) {
    227  1.20  christos 		kill(sshpid, signo);
    228  1.20  christos 		while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
    229  1.20  christos 			continue;
    230  1.20  christos 	}
    231  1.20  christos 	kill(getpid(), SIGSTOP);
    232  1.41  christos 	errno = save_errno;
    233  1.20  christos }
    234  1.20  christos 
    235  1.20  christos static void
    236   1.1  christos cmd_interrupt(int signo)
    237   1.1  christos {
    238   1.1  christos 	const char msg[] = "\rInterrupt  \n";
    239   1.1  christos 	int olderrno = errno;
    240   1.1  christos 
    241  1.12  christos 	(void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
    242   1.1  christos 	interrupted = 1;
    243   1.1  christos 	errno = olderrno;
    244   1.1  christos }
    245   1.1  christos 
    246  1.33  christos static void
    247  1.33  christos read_interrupt(int signo)
    248  1.33  christos {
    249  1.33  christos 	interrupted = 1;
    250  1.33  christos }
    251  1.33  christos 
    252  1.24  christos static void
    253  1.24  christos sigchld_handler(int sig)
    254  1.24  christos {
    255  1.24  christos 	int save_errno = errno;
    256  1.24  christos 	pid_t pid;
    257  1.24  christos 	const char msg[] = "\rConnection closed.  \n";
    258  1.24  christos 
    259  1.24  christos 	/* Report if ssh transport process dies. */
    260  1.24  christos 	while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
    261  1.24  christos 		continue;
    262  1.24  christos 	if (pid == sshpid) {
    263  1.37  christos 		if (!quiet)
    264  1.37  christos 		    (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
    265  1.24  christos 		sshpid = -1;
    266  1.24  christos 	}
    267  1.24  christos 
    268  1.24  christos 	errno = save_errno;
    269  1.24  christos }
    270  1.24  christos 
    271   1.1  christos static void
    272   1.1  christos help(void)
    273   1.1  christos {
    274   1.1  christos 	printf("Available commands:\n"
    275   1.1  christos 	    "bye                                Quit sftp\n"
    276   1.1  christos 	    "cd path                            Change remote directory to 'path'\n"
    277  1.26  christos 	    "chgrp [-h] grp path                Change group of file 'path' to 'grp'\n"
    278  1.26  christos 	    "chmod [-h] mode path               Change permissions of file 'path' to 'mode'\n"
    279  1.26  christos 	    "chown [-h] own path                Change owner of file 'path' to 'own'\n"
    280  1.35  christos 	    "copy oldpath newpath               Copy remote file\n"
    281  1.35  christos 	    "cp oldpath newpath                 Copy remote file\n"
    282   1.1  christos 	    "df [-hi] [path]                    Display statistics for current directory or\n"
    283   1.1  christos 	    "                                   filesystem containing 'path'\n"
    284   1.1  christos 	    "exit                               Quit sftp\n"
    285  1.27  christos 	    "get [-afpR] remote [local]         Download file\n"
    286   1.1  christos 	    "help                               Display this help text\n"
    287   1.1  christos 	    "lcd path                           Change local directory to 'path'\n"
    288   1.1  christos 	    "lls [ls-options [path]]            Display local directory listing\n"
    289   1.1  christos 	    "lmkdir path                        Create local directory\n"
    290   1.7  christos 	    "ln [-s] oldpath newpath            Link remote file (-s for symlink)\n"
    291   1.1  christos 	    "lpwd                               Print local working directory\n"
    292   1.4      adam 	    "ls [-1afhlnrSt] [path]             Display remote directory listing\n"
    293   1.1  christos 	    "lumask umask                       Set local umask to 'umask'\n"
    294   1.1  christos 	    "mkdir path                         Create remote directory\n"
    295   1.1  christos 	    "progress                           Toggle display of progress meter\n"
    296  1.27  christos 	    "put [-afpR] local [remote]         Upload file\n"
    297   1.1  christos 	    "pwd                                Display remote working directory\n"
    298   1.1  christos 	    "quit                               Quit sftp\n"
    299  1.27  christos 	    "reget [-fpR] remote [local]        Resume download file\n"
    300   1.1  christos 	    "rename oldpath newpath             Rename remote file\n"
    301  1.27  christos 	    "reput [-fpR] local [remote]        Resume upload file\n"
    302   1.1  christos 	    "rm path                            Delete remote file\n"
    303   1.1  christos 	    "rmdir path                         Remove remote directory\n"
    304   1.1  christos 	    "symlink oldpath newpath            Symlink remote file\n"
    305   1.1  christos 	    "version                            Show SFTP version\n"
    306   1.1  christos 	    "!command                           Execute 'command' in local shell\n"
    307   1.1  christos 	    "!                                  Escape to local shell\n"
    308   1.1  christos 	    "?                                  Synonym for help\n");
    309   1.1  christos }
    310   1.1  christos 
    311   1.1  christos static void
    312   1.1  christos local_do_shell(const char *args)
    313   1.1  christos {
    314   1.1  christos 	int status;
    315   1.7  christos 	const char *shell;
    316   1.1  christos 	pid_t pid;
    317   1.1  christos 
    318   1.1  christos 	if (!*args)
    319   1.1  christos 		args = NULL;
    320   1.1  christos 
    321   1.7  christos 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
    322   1.1  christos 		shell = _PATH_BSHELL;
    323   1.1  christos 
    324   1.1  christos 	if ((pid = fork()) == -1)
    325   1.1  christos 		fatal("Couldn't fork: %s", strerror(errno));
    326   1.1  christos 
    327   1.1  christos 	if (pid == 0) {
    328   1.1  christos 		/* XXX: child has pipe fds to ssh subproc open - issue? */
    329   1.1  christos 		if (args) {
    330   1.1  christos 			debug3("Executing %s -c \"%s\"", shell, args);
    331   1.1  christos 			execl(shell, shell, "-c", args, (char *)NULL);
    332   1.1  christos 		} else {
    333   1.1  christos 			debug3("Executing %s", shell);
    334   1.1  christos 			execl(shell, shell, (char *)NULL);
    335   1.1  christos 		}
    336   1.1  christos 		fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
    337   1.1  christos 		    strerror(errno));
    338   1.1  christos 		_exit(1);
    339   1.1  christos 	}
    340   1.1  christos 	while (waitpid(pid, &status, 0) == -1)
    341   1.1  christos 		if (errno != EINTR)
    342   1.1  christos 			fatal("Couldn't wait for child: %s", strerror(errno));
    343   1.1  christos 	if (!WIFEXITED(status))
    344   1.1  christos 		error("Shell exited abnormally");
    345   1.1  christos 	else if (WEXITSTATUS(status))
    346   1.1  christos 		error("Shell exited with status %d", WEXITSTATUS(status));
    347   1.1  christos }
    348   1.1  christos 
    349   1.1  christos static void
    350   1.1  christos local_do_ls(const char *args)
    351   1.1  christos {
    352   1.1  christos 	if (!args || !*args)
    353   1.1  christos 		local_do_shell(_PATH_LS);
    354   1.1  christos 	else {
    355   1.1  christos 		int len = strlen(_PATH_LS " ") + strlen(args) + 1;
    356   1.1  christos 		char *buf = xmalloc(len);
    357   1.1  christos 
    358   1.1  christos 		/* XXX: quoting - rip quoting code from ftp? */
    359   1.1  christos 		snprintf(buf, len, _PATH_LS " %s", args);
    360   1.1  christos 		local_do_shell(buf);
    361  1.12  christos 		free(buf);
    362   1.1  christos 	}
    363   1.1  christos }
    364   1.1  christos 
    365   1.1  christos /* Strip one path (usually the pwd) from the start of another */
    366   1.1  christos static char *
    367  1.19  christos path_strip(const char *path, const char *strip)
    368   1.1  christos {
    369   1.1  christos 	size_t len;
    370   1.1  christos 
    371   1.1  christos 	if (strip == NULL)
    372   1.1  christos 		return (xstrdup(path));
    373   1.1  christos 
    374   1.1  christos 	len = strlen(strip);
    375   1.1  christos 	if (strncmp(path, strip, len) == 0) {
    376   1.1  christos 		if (strip[len - 1] != '/' && path[len] == '/')
    377   1.1  christos 			len++;
    378   1.1  christos 		return (xstrdup(path + len));
    379   1.1  christos 	}
    380   1.1  christos 
    381   1.1  christos 	return (xstrdup(path));
    382   1.1  christos }
    383   1.1  christos 
    384   1.1  christos static int
    385  1.12  christos parse_getput_flags(const char *cmd, char **argv, int argc,
    386  1.13  christos     int *aflag, int *fflag, int *pflag, int *rflag)
    387   1.1  christos {
    388   1.1  christos 	extern int opterr, optind, optopt, optreset;
    389   1.1  christos 	int ch;
    390   1.1  christos 
    391   1.1  christos 	optind = optreset = 1;
    392   1.1  christos 	opterr = 0;
    393   1.1  christos 
    394  1.13  christos 	*aflag = *fflag = *rflag = *pflag = 0;
    395  1.13  christos 	while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
    396   1.1  christos 		switch (ch) {
    397  1.12  christos 		case 'a':
    398  1.12  christos 			*aflag = 1;
    399  1.12  christos 			break;
    400  1.13  christos 		case 'f':
    401  1.13  christos 			*fflag = 1;
    402  1.13  christos 			break;
    403   1.1  christos 		case 'p':
    404   1.1  christos 		case 'P':
    405   1.1  christos 			*pflag = 1;
    406   1.1  christos 			break;
    407   1.4      adam 		case 'r':
    408   1.4      adam 		case 'R':
    409   1.4      adam 			*rflag = 1;
    410   1.4      adam 			break;
    411   1.1  christos 		default:
    412   1.1  christos 			error("%s: Invalid flag -%c", cmd, optopt);
    413   1.1  christos 			return -1;
    414   1.1  christos 		}
    415   1.1  christos 	}
    416   1.1  christos 
    417   1.1  christos 	return optind;
    418   1.1  christos }
    419   1.1  christos 
    420   1.1  christos static int
    421   1.7  christos parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
    422   1.7  christos {
    423   1.7  christos 	extern int opterr, optind, optopt, optreset;
    424   1.7  christos 	int ch;
    425   1.7  christos 
    426   1.7  christos 	optind = optreset = 1;
    427   1.7  christos 	opterr = 0;
    428   1.7  christos 
    429   1.7  christos 	*sflag = 0;
    430   1.7  christos 	while ((ch = getopt(argc, argv, "s")) != -1) {
    431   1.7  christos 		switch (ch) {
    432   1.7  christos 		case 's':
    433   1.7  christos 			*sflag = 1;
    434   1.7  christos 			break;
    435   1.7  christos 		default:
    436   1.7  christos 			error("%s: Invalid flag -%c", cmd, optopt);
    437   1.7  christos 			return -1;
    438   1.7  christos 		}
    439   1.7  christos 	}
    440   1.7  christos 
    441   1.7  christos 	return optind;
    442   1.7  christos }
    443   1.7  christos 
    444   1.7  christos static int
    445  1.13  christos parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
    446  1.13  christos {
    447  1.13  christos 	extern int opterr, optind, optopt, optreset;
    448  1.13  christos 	int ch;
    449  1.13  christos 
    450  1.13  christos 	optind = optreset = 1;
    451  1.13  christos 	opterr = 0;
    452  1.13  christos 
    453  1.13  christos 	*lflag = 0;
    454  1.13  christos 	while ((ch = getopt(argc, argv, "l")) != -1) {
    455  1.13  christos 		switch (ch) {
    456  1.13  christos 		case 'l':
    457  1.13  christos 			*lflag = 1;
    458  1.13  christos 			break;
    459  1.13  christos 		default:
    460  1.13  christos 			error("%s: Invalid flag -%c", cmd, optopt);
    461  1.13  christos 			return -1;
    462  1.13  christos 		}
    463  1.13  christos 	}
    464  1.13  christos 
    465  1.13  christos 	return optind;
    466  1.13  christos }
    467  1.13  christos 
    468  1.13  christos static int
    469   1.1  christos parse_ls_flags(char **argv, int argc, int *lflag)
    470   1.1  christos {
    471   1.1  christos 	extern int opterr, optind, optopt, optreset;
    472   1.1  christos 	int ch;
    473   1.1  christos 
    474   1.1  christos 	optind = optreset = 1;
    475   1.1  christos 	opterr = 0;
    476   1.1  christos 
    477   1.1  christos 	*lflag = LS_NAME_SORT;
    478   1.4      adam 	while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
    479   1.1  christos 		switch (ch) {
    480   1.1  christos 		case '1':
    481   1.1  christos 			*lflag &= ~VIEW_FLAGS;
    482   1.1  christos 			*lflag |= LS_SHORT_VIEW;
    483   1.1  christos 			break;
    484   1.1  christos 		case 'S':
    485   1.1  christos 			*lflag &= ~SORT_FLAGS;
    486   1.1  christos 			*lflag |= LS_SIZE_SORT;
    487   1.1  christos 			break;
    488   1.1  christos 		case 'a':
    489   1.1  christos 			*lflag |= LS_SHOW_ALL;
    490   1.1  christos 			break;
    491   1.1  christos 		case 'f':
    492   1.1  christos 			*lflag &= ~SORT_FLAGS;
    493   1.1  christos 			break;
    494   1.4      adam 		case 'h':
    495   1.4      adam 			*lflag |= LS_SI_UNITS;
    496   1.4      adam 			break;
    497   1.1  christos 		case 'l':
    498   1.4      adam 			*lflag &= ~LS_SHORT_VIEW;
    499   1.1  christos 			*lflag |= LS_LONG_VIEW;
    500   1.1  christos 			break;
    501   1.1  christos 		case 'n':
    502   1.4      adam 			*lflag &= ~LS_SHORT_VIEW;
    503   1.1  christos 			*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
    504   1.1  christos 			break;
    505   1.1  christos 		case 'r':
    506   1.1  christos 			*lflag |= LS_REVERSE_SORT;
    507   1.1  christos 			break;
    508   1.1  christos 		case 't':
    509   1.1  christos 			*lflag &= ~SORT_FLAGS;
    510   1.1  christos 			*lflag |= LS_TIME_SORT;
    511   1.1  christos 			break;
    512   1.1  christos 		default:
    513   1.1  christos 			error("ls: Invalid flag -%c", optopt);
    514   1.1  christos 			return -1;
    515   1.1  christos 		}
    516   1.1  christos 	}
    517   1.1  christos 
    518   1.1  christos 	return optind;
    519   1.1  christos }
    520   1.1  christos 
    521   1.1  christos static int
    522   1.1  christos parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
    523   1.1  christos {
    524   1.1  christos 	extern int opterr, optind, optopt, optreset;
    525   1.1  christos 	int ch;
    526   1.1  christos 
    527   1.1  christos 	optind = optreset = 1;
    528   1.1  christos 	opterr = 0;
    529   1.1  christos 
    530   1.1  christos 	*hflag = *iflag = 0;
    531   1.1  christos 	while ((ch = getopt(argc, argv, "hi")) != -1) {
    532   1.1  christos 		switch (ch) {
    533   1.1  christos 		case 'h':
    534   1.1  christos 			*hflag = 1;
    535   1.1  christos 			break;
    536   1.1  christos 		case 'i':
    537   1.1  christos 			*iflag = 1;
    538   1.1  christos 			break;
    539   1.1  christos 		default:
    540   1.1  christos 			error("%s: Invalid flag -%c", cmd, optopt);
    541   1.1  christos 			return -1;
    542   1.1  christos 		}
    543   1.1  christos 	}
    544   1.1  christos 
    545   1.1  christos 	return optind;
    546   1.1  christos }
    547   1.1  christos 
    548   1.1  christos static int
    549  1.26  christos parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
    550  1.26  christos {
    551  1.26  christos 	extern int opterr, optind, optopt, optreset;
    552  1.26  christos 	int ch;
    553  1.26  christos 
    554  1.26  christos 	optind = optreset = 1;
    555  1.26  christos 	opterr = 0;
    556  1.26  christos 
    557  1.26  christos 	*hflag = 0;
    558  1.26  christos 	while ((ch = getopt(argc, argv, "h")) != -1) {
    559  1.26  christos 		switch (ch) {
    560  1.26  christos 		case 'h':
    561  1.26  christos 			*hflag = 1;
    562  1.26  christos 			break;
    563  1.26  christos 		default:
    564  1.26  christos 			error("%s: Invalid flag -%c", cmd, optopt);
    565  1.26  christos 			return -1;
    566  1.26  christos 		}
    567  1.26  christos 	}
    568  1.26  christos 
    569  1.26  christos 	return optind;
    570  1.26  christos }
    571  1.26  christos 
    572  1.26  christos static int
    573  1.13  christos parse_no_flags(const char *cmd, char **argv, int argc)
    574  1.13  christos {
    575  1.13  christos 	extern int opterr, optind, optopt, optreset;
    576  1.13  christos 	int ch;
    577  1.13  christos 
    578  1.13  christos 	optind = optreset = 1;
    579  1.13  christos 	opterr = 0;
    580  1.13  christos 
    581  1.13  christos 	while ((ch = getopt(argc, argv, "")) != -1) {
    582  1.13  christos 		switch (ch) {
    583  1.13  christos 		default:
    584  1.13  christos 			error("%s: Invalid flag -%c", cmd, optopt);
    585  1.13  christos 			return -1;
    586  1.13  christos 		}
    587  1.13  christos 	}
    588  1.13  christos 
    589  1.13  christos 	return optind;
    590  1.13  christos }
    591  1.13  christos 
    592  1.36  christos static char *
    593  1.36  christos escape_glob(const char *s)
    594  1.36  christos {
    595  1.36  christos 	size_t i, o, len;
    596  1.36  christos 	char *ret;
    597  1.36  christos 
    598  1.36  christos 	len = strlen(s);
    599  1.36  christos 	ret = xcalloc(2, len + 1);
    600  1.36  christos 	for (i = o = 0; i < len; i++) {
    601  1.36  christos 		if (strchr("[]?*\\", s[i]) != NULL)
    602  1.36  christos 			ret[o++] = '\\';
    603  1.36  christos 		ret[o++] = s[i];
    604  1.36  christos 	}
    605  1.36  christos 	ret[o++] = '\0';
    606  1.36  christos 	return ret;
    607  1.36  christos }
    608  1.36  christos 
    609  1.39  christos /*
    610  1.39  christos  * Arg p must be dynamically allocated.  make_absolute will either return it
    611  1.39  christos  * or free it and allocate a new one.  Caller must free returned string.
    612  1.39  christos  */
    613  1.36  christos static char *
    614  1.39  christos make_absolute_pwd_glob(char *p, const char *pwd)
    615  1.36  christos {
    616  1.36  christos 	char *ret, *escpwd;
    617  1.36  christos 
    618  1.36  christos 	escpwd = escape_glob(pwd);
    619  1.36  christos 	if (p == NULL)
    620  1.36  christos 		return escpwd;
    621  1.39  christos 	ret = sftp_make_absolute(p, escpwd);
    622  1.36  christos 	free(escpwd);
    623  1.36  christos 	return ret;
    624  1.36  christos }
    625  1.36  christos 
    626  1.13  christos static int
    627  1.39  christos local_is_dir(const char *path)
    628  1.39  christos {
    629  1.39  christos 	struct stat sb;
    630  1.39  christos 
    631  1.39  christos 	if (stat(path, &sb) == -1)
    632  1.39  christos 		return 0;
    633  1.39  christos 	return S_ISDIR(sb.st_mode);
    634  1.39  christos }
    635  1.39  christos 
    636  1.39  christos static int
    637  1.19  christos process_get(struct sftp_conn *conn, const char *src, const char *dst,
    638  1.19  christos     const char *pwd, int pflag, int rflag, int resume, int fflag)
    639   1.1  christos {
    640  1.44  christos 	const char *filename;
    641  1.44  christos 	char *abs_src = NULL, *abs_dst = NULL, *tmp = NULL;
    642   1.1  christos 	glob_t g;
    643  1.13  christos 	int i, r, err = 0;
    644   1.1  christos 
    645  1.39  christos 	abs_src = make_absolute_pwd_glob(xstrdup(src), pwd);
    646   1.4      adam 	memset(&g, 0, sizeof(g));
    647   1.1  christos 
    648   1.1  christos 	debug3("Looking up %s", abs_src);
    649  1.39  christos 	if ((r = sftp_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
    650  1.13  christos 		if (r == GLOB_NOSPACE) {
    651  1.13  christos 			error("Too many matches for \"%s\".", abs_src);
    652  1.13  christos 		} else {
    653  1.13  christos 			error("File \"%s\" not found.", abs_src);
    654  1.13  christos 		}
    655   1.1  christos 		err = -1;
    656   1.1  christos 		goto out;
    657   1.1  christos 	}
    658   1.1  christos 
    659   1.4      adam 	/*
    660   1.4      adam 	 * If multiple matches then dst must be a directory or
    661   1.4      adam 	 * unspecified.
    662   1.4      adam 	 */
    663  1.31  christos 	if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) {
    664   1.4      adam 		error("Multiple source paths, but destination "
    665   1.4      adam 		    "\"%s\" is not a directory", dst);
    666   1.1  christos 		err = -1;
    667   1.1  christos 		goto out;
    668   1.1  christos 	}
    669   1.1  christos 
    670   1.1  christos 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
    671   1.4      adam 		tmp = xstrdup(g.gl_pathv[i]);
    672   1.4      adam 		if ((filename = basename(tmp)) == NULL) {
    673   1.4      adam 			error("basename %s: %s", tmp, strerror(errno));
    674  1.12  christos 			free(tmp);
    675   1.1  christos 			err = -1;
    676   1.1  christos 			goto out;
    677   1.1  christos 		}
    678   1.1  christos 
    679  1.44  christos 		/* Special handling for dest of '..' */
    680  1.44  christos 		if (strcmp(filename, "..") == 0)
    681  1.44  christos 			filename = "."; /* Download to dest, not dest/.. */
    682  1.44  christos 
    683   1.1  christos 		if (g.gl_matchc == 1 && dst) {
    684  1.31  christos 			if (local_is_dir(dst)) {
    685  1.39  christos 				abs_dst = sftp_path_append(dst, filename);
    686   1.4      adam 			} else {
    687   1.1  christos 				abs_dst = xstrdup(dst);
    688   1.4      adam 			}
    689   1.1  christos 		} else if (dst) {
    690  1.39  christos 			abs_dst = sftp_path_append(dst, filename);
    691   1.4      adam 		} else {
    692   1.4      adam 			abs_dst = xstrdup(filename);
    693   1.4      adam 		}
    694  1.12  christos 		free(tmp);
    695   1.1  christos 
    696  1.12  christos 		resume |= global_aflag;
    697  1.12  christos 		if (!quiet && resume)
    698  1.19  christos 			mprintf("Resuming %s to %s\n",
    699  1.19  christos 			    g.gl_pathv[i], abs_dst);
    700  1.12  christos 		else if (!quiet && !resume)
    701  1.19  christos 			mprintf("Fetching %s to %s\n",
    702  1.19  christos 			    g.gl_pathv[i], abs_dst);
    703  1.33  christos 		/* XXX follow link flag */
    704  1.39  christos 		if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
    705  1.39  christos 		    (rflag || global_rflag)) {
    706  1.39  christos 			if (sftp_download_dir(conn, g.gl_pathv[i], abs_dst,
    707  1.39  christos 			    NULL, pflag || global_pflag, 1, resume,
    708  1.36  christos 			    fflag || global_fflag, 0, 0) == -1)
    709   1.4      adam 				err = -1;
    710   1.4      adam 		} else {
    711  1.39  christos 			if (sftp_download(conn, g.gl_pathv[i], abs_dst, NULL,
    712  1.13  christos 			    pflag || global_pflag, resume,
    713  1.36  christos 			    fflag || global_fflag, 0) == -1)
    714   1.4      adam 				err = -1;
    715   1.4      adam 		}
    716  1.12  christos 		free(abs_dst);
    717   1.1  christos 		abs_dst = NULL;
    718   1.1  christos 	}
    719   1.1  christos 
    720   1.1  christos out:
    721  1.12  christos 	free(abs_src);
    722   1.1  christos 	globfree(&g);
    723   1.1  christos 	return(err);
    724   1.1  christos }
    725   1.1  christos 
    726   1.1  christos static int
    727  1.19  christos process_put(struct sftp_conn *conn, const char *src, const char *dst,
    728  1.19  christos     const char *pwd, int pflag, int rflag, int resume, int fflag)
    729   1.1  christos {
    730   1.1  christos 	char *tmp_dst = NULL;
    731   1.1  christos 	char *abs_dst = NULL;
    732  1.44  christos 	char *tmp = NULL;
    733  1.44  christos 	const char *filename = NULL;
    734   1.1  christos 	glob_t g;
    735   1.1  christos 	int err = 0;
    736   1.4      adam 	int i, dst_is_dir = 1;
    737   1.1  christos 	struct stat sb;
    738   1.1  christos 
    739   1.1  christos 	if (dst) {
    740   1.1  christos 		tmp_dst = xstrdup(dst);
    741  1.39  christos 		tmp_dst = sftp_make_absolute(tmp_dst, pwd);
    742   1.1  christos 	}
    743   1.1  christos 
    744   1.1  christos 	memset(&g, 0, sizeof(g));
    745   1.1  christos 	debug3("Looking up %s", src);
    746   1.4      adam 	if (glob(src, GLOB_NOCHECK | GLOB_LIMIT | GLOB_MARK, NULL, &g)) {
    747   1.1  christos 		error("File \"%s\" not found.", src);
    748   1.1  christos 		err = -1;
    749   1.1  christos 		goto out;
    750   1.1  christos 	}
    751   1.1  christos 
    752   1.4      adam 	/* If we aren't fetching to pwd then stash this status for later */
    753   1.4      adam 	if (tmp_dst != NULL)
    754  1.39  christos 		dst_is_dir = sftp_remote_is_dir(conn, tmp_dst);
    755   1.4      adam 
    756   1.1  christos 	/* If multiple matches, dst may be directory or unspecified */
    757   1.4      adam 	if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
    758   1.4      adam 		error("Multiple paths match, but destination "
    759   1.4      adam 		    "\"%s\" is not a directory", tmp_dst);
    760   1.1  christos 		err = -1;
    761   1.1  christos 		goto out;
    762   1.1  christos 	}
    763   1.1  christos 
    764   1.1  christos 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
    765   1.1  christos 		if (stat(g.gl_pathv[i], &sb) == -1) {
    766   1.1  christos 			err = -1;
    767   1.1  christos 			error("stat %s: %s", g.gl_pathv[i], strerror(errno));
    768   1.1  christos 			continue;
    769   1.1  christos 		}
    770  1.13  christos 
    771   1.4      adam 		tmp = xstrdup(g.gl_pathv[i]);
    772   1.4      adam 		if ((filename = basename(tmp)) == NULL) {
    773   1.4      adam 			error("basename %s: %s", tmp, strerror(errno));
    774  1.12  christos 			free(tmp);
    775   1.1  christos 			err = -1;
    776   1.1  christos 			goto out;
    777   1.1  christos 		}
    778  1.44  christos 		/* Special handling for source of '..' */
    779  1.44  christos 		if (strcmp(filename, "..") == 0)
    780  1.44  christos 			filename = "."; /* Upload to dest, not dest/.. */
    781   1.1  christos 
    782  1.39  christos 		free(abs_dst);
    783  1.39  christos 		abs_dst = NULL;
    784   1.1  christos 		if (g.gl_matchc == 1 && tmp_dst) {
    785   1.1  christos 			/* If directory specified, append filename */
    786   1.4      adam 			if (dst_is_dir)
    787  1.39  christos 				abs_dst = sftp_path_append(tmp_dst, filename);
    788   1.4      adam 			else
    789   1.1  christos 				abs_dst = xstrdup(tmp_dst);
    790   1.1  christos 		} else if (tmp_dst) {
    791  1.39  christos 			abs_dst = sftp_path_append(tmp_dst, filename);
    792   1.4      adam 		} else {
    793  1.39  christos 			abs_dst = sftp_make_absolute(xstrdup(filename), pwd);
    794   1.4      adam 		}
    795  1.12  christos 		free(tmp);
    796   1.1  christos 
    797  1.32  christos 		resume |= global_aflag;
    798  1.13  christos 		if (!quiet && resume)
    799  1.19  christos 			mprintf("Resuming upload of %s to %s\n",
    800  1.19  christos 			    g.gl_pathv[i], abs_dst);
    801  1.13  christos 		else if (!quiet && !resume)
    802  1.19  christos 			mprintf("Uploading %s to %s\n",
    803  1.19  christos 			    g.gl_pathv[i], abs_dst);
    804  1.33  christos 		/* XXX follow_link_flag */
    805  1.39  christos 		if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
    806  1.39  christos 		    (rflag || global_rflag)) {
    807  1.39  christos 			if (sftp_upload_dir(conn, g.gl_pathv[i], abs_dst,
    808  1.13  christos 			    pflag || global_pflag, 1, resume,
    809  1.36  christos 			    fflag || global_fflag, 0, 0) == -1)
    810   1.4      adam 				err = -1;
    811   1.4      adam 		} else {
    812  1.39  christos 			if (sftp_upload(conn, g.gl_pathv[i], abs_dst,
    813  1.13  christos 			    pflag || global_pflag, resume,
    814  1.36  christos 			    fflag || global_fflag, 0) == -1)
    815   1.4      adam 				err = -1;
    816   1.4      adam 		}
    817  1.15  christos 		free(abs_dst);
    818  1.15  christos 		abs_dst = NULL;
    819   1.1  christos 	}
    820   1.1  christos 
    821   1.1  christos out:
    822  1.12  christos 	free(abs_dst);
    823  1.12  christos 	free(tmp_dst);
    824   1.1  christos 	globfree(&g);
    825   1.1  christos 	return(err);
    826   1.1  christos }
    827   1.1  christos 
    828   1.1  christos static int
    829   1.1  christos sdirent_comp(const void *aa, const void *bb)
    830   1.1  christos {
    831   1.7  christos 	const SFTP_DIRENT *a = *(const SFTP_DIRENT * const *)aa;
    832   1.7  christos 	const SFTP_DIRENT *b = *(const SFTP_DIRENT * const *)bb;
    833   1.1  christos 	int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
    834   1.1  christos 
    835   1.1  christos #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
    836   1.1  christos 	if (sort_flag & LS_NAME_SORT)
    837   1.1  christos 		return (rmul * strcmp(a->filename, b->filename));
    838   1.1  christos 	else if (sort_flag & LS_TIME_SORT)
    839   1.1  christos 		return (rmul * NCMP(a->a.mtime, b->a.mtime));
    840   1.1  christos 	else if (sort_flag & LS_SIZE_SORT)
    841   1.1  christos 		return (rmul * NCMP(a->a.size, b->a.size));
    842   1.1  christos 
    843   1.1  christos 	fatal("Unknown ls sort type");
    844   1.2  christos 	/*NOTREACHED*/
    845   1.2  christos 	return 0;
    846   1.1  christos }
    847   1.1  christos 
    848   1.1  christos /* sftp ls.1 replacement for directories */
    849   1.1  christos static int
    850  1.19  christos do_ls_dir(struct sftp_conn *conn, const char *path,
    851  1.19  christos     const char *strip_path, int lflag)
    852   1.1  christos {
    853   1.1  christos 	int n;
    854   1.1  christos 	u_int c = 1, colspace = 0, columns = 1;
    855   1.1  christos 	SFTP_DIRENT **d;
    856   1.1  christos 
    857  1.39  christos 	if ((n = sftp_readdir(conn, path, &d)) != 0)
    858   1.1  christos 		return (n);
    859   1.1  christos 
    860   1.1  christos 	if (!(lflag & LS_SHORT_VIEW)) {
    861   1.1  christos 		u_int m = 0, width = 80;
    862   1.1  christos 		struct winsize ws;
    863   1.1  christos 		char *tmp;
    864   1.1  christos 
    865   1.1  christos 		/* Count entries for sort and find longest filename */
    866   1.1  christos 		for (n = 0; d[n] != NULL; n++) {
    867   1.1  christos 			if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
    868  1.20  christos 				m = MAXIMUM(m, strlen(d[n]->filename));
    869   1.1  christos 		}
    870   1.1  christos 
    871   1.1  christos 		/* Add any subpath that also needs to be counted */
    872   1.1  christos 		tmp = path_strip(path, strip_path);
    873   1.1  christos 		m += strlen(tmp);
    874  1.12  christos 		free(tmp);
    875   1.1  christos 
    876   1.1  christos 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
    877   1.1  christos 			width = ws.ws_col;
    878   1.1  christos 
    879   1.1  christos 		columns = width / (m + 2);
    880  1.20  christos 		columns = MAXIMUM(columns, 1);
    881   1.1  christos 		colspace = width / columns;
    882  1.20  christos 		colspace = MINIMUM(colspace, width);
    883   1.1  christos 	}
    884   1.1  christos 
    885   1.1  christos 	if (lflag & SORT_FLAGS) {
    886   1.1  christos 		for (n = 0; d[n] != NULL; n++)
    887   1.1  christos 			;	/* count entries */
    888   1.1  christos 		sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
    889   1.1  christos 		qsort(d, n, sizeof(*d), sdirent_comp);
    890   1.1  christos 	}
    891   1.1  christos 
    892  1.36  christos 	get_remote_user_groups_from_dirents(conn, d);
    893   1.1  christos 	for (n = 0; d[n] != NULL && !interrupted; n++) {
    894   1.1  christos 		char *tmp, *fname;
    895   1.1  christos 
    896   1.1  christos 		if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
    897   1.1  christos 			continue;
    898   1.1  christos 
    899  1.39  christos 		tmp = sftp_path_append(path, d[n]->filename);
    900   1.1  christos 		fname = path_strip(tmp, strip_path);
    901  1.12  christos 		free(tmp);
    902   1.1  christos 
    903   1.1  christos 		if (lflag & LS_LONG_VIEW) {
    904  1.36  christos 			if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 ||
    905  1.39  christos 			    sftp_can_get_users_groups_by_id(conn)) {
    906   1.1  christos 				char *lname;
    907   1.1  christos 				struct stat sb;
    908   1.1  christos 
    909   1.1  christos 				memset(&sb, 0, sizeof(sb));
    910   1.1  christos 				attrib_to_stat(&d[n]->a, &sb);
    911   1.4      adam 				lname = ls_file(fname, &sb, 1,
    912  1.36  christos 				    (lflag & LS_SI_UNITS),
    913  1.36  christos 				    ruser_name(sb.st_uid),
    914  1.36  christos 				    rgroup_name(sb.st_gid));
    915  1.19  christos 				mprintf("%s\n", lname);
    916  1.12  christos 				free(lname);
    917   1.1  christos 			} else
    918  1.19  christos 				mprintf("%s\n", d[n]->longname);
    919   1.1  christos 		} else {
    920  1.19  christos 			mprintf("%-*s", colspace, fname);
    921   1.1  christos 			if (c >= columns) {
    922   1.1  christos 				printf("\n");
    923   1.1  christos 				c = 1;
    924   1.1  christos 			} else
    925   1.1  christos 				c++;
    926   1.1  christos 		}
    927   1.1  christos 
    928  1.12  christos 		free(fname);
    929   1.1  christos 	}
    930   1.1  christos 
    931   1.1  christos 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
    932   1.1  christos 		printf("\n");
    933   1.1  christos 
    934  1.39  christos 	sftp_free_dirents(d);
    935   1.1  christos 	return (0);
    936   1.1  christos }
    937   1.1  christos 
    938  1.22  christos static int
    939  1.22  christos sglob_comp(const void *aa, const void *bb)
    940  1.22  christos {
    941  1.22  christos 	u_int a = *(const u_int *)aa;
    942  1.22  christos 	u_int b = *(const u_int *)bb;
    943  1.22  christos 	const char *ap = sort_glob->gl_pathv[a];
    944  1.22  christos 	const char *bp = sort_glob->gl_pathv[b];
    945  1.22  christos #if 0
    946  1.22  christos 	const struct stat *as = sort_glob->gl_statv[a];
    947  1.22  christos 	const struct stat *bs = sort_glob->gl_statv[b];
    948  1.22  christos #else
    949  1.22  christos 	struct stat ass, bss;
    950  1.22  christos 	const struct stat *as = &ass;
    951  1.22  christos 	const struct stat *bs = &bss;
    952  1.22  christos #endif
    953  1.22  christos 	int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
    954  1.22  christos 
    955  1.22  christos #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
    956  1.22  christos #if 1
    957  1.22  christos 	if (stat(ap, &ass) == -1 || stat(bp, &bss) == -1)
    958  1.22  christos 		return 0;
    959  1.22  christos #endif
    960  1.31  christos 	if (sort_flag & LS_NAME_SORT)
    961  1.31  christos 		return (rmul * strcmp(ap, bp));
    962  1.31  christos 	else if (sort_flag & LS_TIME_SORT) {
    963  1.31  christos 		if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==))
    964  1.31  christos 			return 0;
    965  1.31  christos 		return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ?
    966  1.31  christos 		    rmul : -rmul;
    967  1.31  christos 	} else if (sort_flag & LS_SIZE_SORT)
    968  1.22  christos 		return (rmul * NCMP(as->st_size, bs->st_size));
    969  1.22  christos 
    970  1.22  christos 	fatal("Unknown ls sort type");
    971  1.22  christos }
    972  1.22  christos 
    973   1.1  christos /* sftp ls.1 replacement which handles path globs */
    974   1.1  christos static int
    975  1.19  christos do_globbed_ls(struct sftp_conn *conn, const char *path,
    976  1.19  christos     const char *strip_path, int lflag)
    977   1.1  christos {
    978   1.7  christos 	char *fname, *lname;
    979   1.1  christos 	glob_t g;
    980  1.13  christos 	int err, r;
    981   1.7  christos 	struct winsize ws;
    982  1.22  christos 	u_int i, j, nentries, *indices = NULL, c = 1;
    983  1.22  christos 	u_int colspace = 0, columns = 1, m = 0, width = 80;
    984   1.7  christos 	struct stat *stp;
    985   1.7  christos #ifndef GLOB_KEEPSTAT
    986   1.7  christos 	struct stat st;
    987   1.7  christos #define GLOB_KEEPSTAT	0
    988   1.7  christos #endif
    989   1.1  christos 
    990   1.1  christos 	memset(&g, 0, sizeof(g));
    991   1.1  christos 
    992  1.39  christos 	if ((r = sftp_glob(conn, path,
    993   1.9  christos 	    GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
    994  1.13  christos 	    NULL, &g)) != 0 ||
    995   1.7  christos 	    (g.gl_pathc && !g.gl_matchc)) {
    996   1.1  christos 		if (g.gl_pathc)
    997   1.1  christos 			globfree(&g);
    998  1.13  christos 		if (r == GLOB_NOSPACE) {
    999  1.13  christos 			error("Can't ls: Too many matches for \"%s\"", path);
   1000  1.13  christos 		} else {
   1001  1.13  christos 			error("Can't ls: \"%s\" not found", path);
   1002  1.13  christos 		}
   1003   1.7  christos 		return -1;
   1004   1.1  christos 	}
   1005   1.1  christos 
   1006   1.1  christos 	if (interrupted)
   1007   1.1  christos 		goto out;
   1008   1.1  christos 
   1009   1.1  christos 	/*
   1010   1.1  christos 	 * If the glob returns a single match and it is a directory,
   1011   1.1  christos 	 * then just list its contents.
   1012   1.1  christos 	 */
   1013   1.7  christos 	if (g.gl_matchc == 1 &&
   1014   1.7  christos #if GLOB_KEEPSTAT != 0
   1015   1.7  christos 	    (stp = g.gl_statv[0]) != NULL &&
   1016   1.7  christos #else
   1017   1.7  christos 	    lstat(g.gl_pathv[0], stp = &st) != -1 &&
   1018   1.7  christos #endif
   1019   1.7  christos 	    S_ISDIR(stp->st_mode)) {
   1020   1.7  christos 		err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
   1021   1.7  christos 		globfree(&g);
   1022   1.7  christos 		return err;
   1023   1.7  christos 	}
   1024   1.1  christos 
   1025   1.7  christos 	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
   1026   1.7  christos 		width = ws.ws_col;
   1027   1.1  christos 
   1028   1.1  christos 	if (!(lflag & LS_SHORT_VIEW)) {
   1029   1.1  christos 		/* Count entries for sort and find longest filename */
   1030   1.1  christos 		for (i = 0; g.gl_pathv[i]; i++)
   1031  1.20  christos 			m = MAXIMUM(m, strlen(g.gl_pathv[i]));
   1032   1.1  christos 
   1033   1.1  christos 		columns = width / (m + 2);
   1034  1.20  christos 		columns = MAXIMUM(columns, 1);
   1035   1.1  christos 		colspace = width / columns;
   1036   1.1  christos 	}
   1037   1.1  christos 
   1038  1.22  christos 	/*
   1039  1.22  christos 	 * Sorting: rather than mess with the contents of glob_t, prepare
   1040  1.22  christos 	 * an array of indices into it and sort that. For the usual
   1041  1.22  christos 	 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
   1042  1.22  christos 	 */
   1043  1.22  christos 	for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
   1044  1.22  christos 		;	/* count entries */
   1045  1.37  christos 	indices = xcalloc(nentries, sizeof(*indices));
   1046  1.22  christos 	for (i = 0; i < nentries; i++)
   1047  1.22  christos 		indices[i] = i;
   1048  1.22  christos 
   1049  1.22  christos 	if (lflag & SORT_FLAGS) {
   1050  1.22  christos 		sort_glob = &g;
   1051  1.22  christos 		sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
   1052  1.22  christos 		qsort(indices, nentries, sizeof(*indices), sglob_comp);
   1053  1.22  christos 		sort_glob = NULL;
   1054  1.22  christos 	}
   1055  1.22  christos 
   1056  1.36  christos 	get_remote_user_groups_from_glob(conn, &g);
   1057  1.22  christos 	for (j = 0; j < nentries && !interrupted; j++) {
   1058  1.22  christos 		i = indices[j];
   1059   1.1  christos 		fname = path_strip(g.gl_pathv[i], strip_path);
   1060   1.1  christos 		if (lflag & LS_LONG_VIEW) {
   1061   1.7  christos #if GLOB_KEEPSTAT != 0
   1062   1.7  christos 			stp = g.gl_statv[i];
   1063   1.7  christos #else
   1064   1.7  christos 			if (lstat(g.gl_pathv[i], stp = &st) == -1)
   1065   1.7  christos 				stp = NULL;
   1066   1.7  christos #endif
   1067   1.7  christos 			if (stp == NULL) {
   1068   1.7  christos 				error("no stat information for %s", fname);
   1069  1.37  christos 				free(fname);
   1070   1.7  christos 				continue;
   1071   1.7  christos 			}
   1072  1.36  christos 			lname = ls_file(fname, stp, 1,
   1073  1.36  christos 			    (lflag & LS_SI_UNITS),
   1074  1.36  christos 			    ruser_name(stp->st_uid),
   1075  1.36  christos 			    rgroup_name(stp->st_gid));
   1076  1.19  christos 			mprintf("%s\n", lname);
   1077  1.12  christos 			free(lname);
   1078   1.1  christos 		} else {
   1079  1.19  christos 			mprintf("%-*s", colspace, fname);
   1080   1.1  christos 			if (c >= columns) {
   1081   1.1  christos 				printf("\n");
   1082   1.1  christos 				c = 1;
   1083   1.1  christos 			} else
   1084   1.1  christos 				c++;
   1085   1.1  christos 		}
   1086  1.12  christos 		free(fname);
   1087   1.1  christos 	}
   1088   1.1  christos 
   1089   1.1  christos 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
   1090   1.1  christos 		printf("\n");
   1091   1.1  christos 
   1092   1.1  christos  out:
   1093   1.1  christos 	if (g.gl_pathc)
   1094   1.1  christos 		globfree(&g);
   1095  1.22  christos 	free(indices);
   1096   1.1  christos 
   1097   1.7  christos 	return 0;
   1098   1.1  christos }
   1099   1.1  christos 
   1100   1.1  christos static int
   1101  1.19  christos do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
   1102   1.1  christos {
   1103   1.1  christos 	struct sftp_statvfs st;
   1104  1.21  christos 	char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
   1105  1.21  christos 	char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
   1106  1.21  christos 	char s_icapacity[16], s_dcapacity[16];
   1107   1.1  christos 
   1108  1.39  christos 	if (sftp_statvfs(conn, path, &st, 1) == -1)
   1109   1.1  christos 		return -1;
   1110  1.21  christos 	if (st.f_files == 0)
   1111  1.21  christos 		strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
   1112  1.21  christos 	else {
   1113  1.21  christos 		snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
   1114  1.21  christos 		    (unsigned long long)(100 * (st.f_files - st.f_ffree) /
   1115  1.21  christos 		    st.f_files));
   1116  1.21  christos 	}
   1117  1.21  christos 	if (st.f_blocks == 0)
   1118  1.21  christos 		strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
   1119  1.21  christos 	else {
   1120  1.21  christos 		snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
   1121  1.21  christos 		    (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
   1122  1.21  christos 		    st.f_blocks));
   1123  1.21  christos 	}
   1124   1.1  christos 	if (iflag) {
   1125   1.1  christos 		printf("     Inodes        Used       Avail      "
   1126   1.1  christos 		    "(root)    %%Capacity\n");
   1127  1.21  christos 		printf("%11llu %11llu %11llu %11llu         %s\n",
   1128   1.1  christos 		    (unsigned long long)st.f_files,
   1129   1.1  christos 		    (unsigned long long)(st.f_files - st.f_ffree),
   1130   1.1  christos 		    (unsigned long long)st.f_favail,
   1131  1.21  christos 		    (unsigned long long)st.f_ffree, s_icapacity);
   1132   1.1  christos 	} else if (hflag) {
   1133   1.1  christos 		strlcpy(s_used, "error", sizeof(s_used));
   1134   1.1  christos 		strlcpy(s_avail, "error", sizeof(s_avail));
   1135   1.1  christos 		strlcpy(s_root, "error", sizeof(s_root));
   1136   1.1  christos 		strlcpy(s_total, "error", sizeof(s_total));
   1137   1.1  christos 		fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
   1138   1.1  christos 		fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
   1139   1.1  christos 		fmt_scaled(st.f_bfree * st.f_frsize, s_root);
   1140   1.1  christos 		fmt_scaled(st.f_blocks * st.f_frsize, s_total);
   1141   1.1  christos 		printf("    Size     Used    Avail   (root)    %%Capacity\n");
   1142  1.21  christos 		printf("%7sB %7sB %7sB %7sB         %s\n",
   1143  1.21  christos 		    s_total, s_used, s_avail, s_root, s_dcapacity);
   1144   1.1  christos 	} else {
   1145   1.1  christos 		printf("        Size         Used        Avail       "
   1146   1.1  christos 		    "(root)    %%Capacity\n");
   1147  1.21  christos 		printf("%12llu %12llu %12llu %12llu         %s\n",
   1148   1.1  christos 		    (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
   1149   1.1  christos 		    (unsigned long long)(st.f_frsize *
   1150   1.1  christos 		    (st.f_blocks - st.f_bfree) / 1024),
   1151   1.1  christos 		    (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
   1152   1.1  christos 		    (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
   1153  1.21  christos 		    s_dcapacity);
   1154   1.1  christos 	}
   1155   1.1  christos 	return 0;
   1156   1.1  christos }
   1157   1.1  christos 
   1158   1.1  christos /*
   1159   1.1  christos  * Undo escaping of glob sequences in place. Used to undo extra escaping
   1160   1.1  christos  * applied in makeargv() when the string is destined for a function that
   1161   1.1  christos  * does not glob it.
   1162   1.1  christos  */
   1163   1.1  christos static void
   1164   1.1  christos undo_glob_escape(char *s)
   1165   1.1  christos {
   1166   1.1  christos 	size_t i, j;
   1167   1.1  christos 
   1168   1.1  christos 	for (i = j = 0;;) {
   1169   1.1  christos 		if (s[i] == '\0') {
   1170   1.1  christos 			s[j] = '\0';
   1171   1.1  christos 			return;
   1172   1.1  christos 		}
   1173   1.1  christos 		if (s[i] != '\\') {
   1174   1.1  christos 			s[j++] = s[i++];
   1175   1.1  christos 			continue;
   1176   1.1  christos 		}
   1177   1.1  christos 		/* s[i] == '\\' */
   1178   1.1  christos 		++i;
   1179   1.1  christos 		switch (s[i]) {
   1180   1.1  christos 		case '?':
   1181   1.1  christos 		case '[':
   1182   1.1  christos 		case '*':
   1183   1.1  christos 		case '\\':
   1184   1.1  christos 			s[j++] = s[i++];
   1185   1.1  christos 			break;
   1186   1.1  christos 		case '\0':
   1187   1.1  christos 			s[j++] = '\\';
   1188   1.1  christos 			s[j] = '\0';
   1189   1.1  christos 			return;
   1190   1.1  christos 		default:
   1191   1.1  christos 			s[j++] = '\\';
   1192   1.1  christos 			s[j++] = s[i++];
   1193   1.1  christos 			break;
   1194   1.1  christos 		}
   1195   1.1  christos 	}
   1196   1.1  christos }
   1197   1.1  christos 
   1198   1.1  christos /*
   1199   1.1  christos  * Split a string into an argument vector using sh(1)-style quoting,
   1200   1.1  christos  * comment and escaping rules, but with some tweaks to handle glob(3)
   1201   1.1  christos  * wildcards.
   1202   1.4      adam  * The "sloppy" flag allows for recovery from missing terminating quote, for
   1203   1.4      adam  * use in parsing incomplete commandlines during tab autocompletion.
   1204   1.4      adam  *
   1205   1.1  christos  * Returns NULL on error or a NULL-terminated array of arguments.
   1206   1.4      adam  *
   1207   1.4      adam  * If "lastquote" is not NULL, the quoting character used for the last
   1208   1.4      adam  * argument is placed in *lastquote ("\0", "'" or "\"").
   1209  1.13  christos  *
   1210   1.4      adam  * If "terminated" is not NULL, *terminated will be set to 1 when the
   1211   1.4      adam  * last argument's quote has been properly terminated or 0 otherwise.
   1212   1.4      adam  * This parameter is only of use if "sloppy" is set.
   1213   1.1  christos  */
   1214  1.31  christos #define MAXARGS		128
   1215   1.1  christos #define MAXARGLEN	8192
   1216   1.1  christos static char **
   1217   1.4      adam makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
   1218   1.4      adam     u_int *terminated)
   1219   1.1  christos {
   1220   1.1  christos 	int argc, quot;
   1221   1.1  christos 	size_t i, j;
   1222   1.1  christos 	static char argvs[MAXARGLEN];
   1223   1.1  christos 	static char *argv[MAXARGS + 1];
   1224   1.1  christos 	enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
   1225   1.1  christos 
   1226   1.1  christos 	*argcp = argc = 0;
   1227   1.1  christos 	if (strlen(arg) > sizeof(argvs) - 1) {
   1228   1.1  christos  args_too_longs:
   1229   1.1  christos 		error("string too long");
   1230   1.1  christos 		return NULL;
   1231   1.1  christos 	}
   1232   1.4      adam 	if (terminated != NULL)
   1233   1.4      adam 		*terminated = 1;
   1234   1.4      adam 	if (lastquote != NULL)
   1235   1.4      adam 		*lastquote = '\0';
   1236   1.1  christos 	state = MA_START;
   1237   1.1  christos 	i = j = 0;
   1238   1.1  christos 	for (;;) {
   1239  1.11  christos 		if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
   1240  1.11  christos 			error("Too many arguments.");
   1241  1.11  christos 			return NULL;
   1242  1.11  christos 		}
   1243   1.2  christos 		if (isspace((unsigned char)arg[i])) {
   1244   1.1  christos 			if (state == MA_UNQUOTED) {
   1245   1.1  christos 				/* Terminate current argument */
   1246   1.1  christos 				argvs[j++] = '\0';
   1247   1.1  christos 				argc++;
   1248   1.1  christos 				state = MA_START;
   1249   1.1  christos 			} else if (state != MA_START)
   1250   1.1  christos 				argvs[j++] = arg[i];
   1251   1.1  christos 		} else if (arg[i] == '"' || arg[i] == '\'') {
   1252   1.1  christos 			q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
   1253   1.1  christos 			if (state == MA_START) {
   1254   1.1  christos 				argv[argc] = argvs + j;
   1255   1.1  christos 				state = q;
   1256   1.4      adam 				if (lastquote != NULL)
   1257   1.4      adam 					*lastquote = arg[i];
   1258  1.13  christos 			} else if (state == MA_UNQUOTED)
   1259   1.1  christos 				state = q;
   1260   1.1  christos 			else if (state == q)
   1261   1.1  christos 				state = MA_UNQUOTED;
   1262   1.1  christos 			else
   1263   1.1  christos 				argvs[j++] = arg[i];
   1264   1.1  christos 		} else if (arg[i] == '\\') {
   1265   1.1  christos 			if (state == MA_SQUOTE || state == MA_DQUOTE) {
   1266   1.1  christos 				quot = state == MA_SQUOTE ? '\'' : '"';
   1267   1.1  christos 				/* Unescape quote we are in */
   1268   1.1  christos 				/* XXX support \n and friends? */
   1269   1.1  christos 				if (arg[i + 1] == quot) {
   1270   1.1  christos 					i++;
   1271   1.1  christos 					argvs[j++] = arg[i];
   1272   1.1  christos 				} else if (arg[i + 1] == '?' ||
   1273   1.1  christos 				    arg[i + 1] == '[' || arg[i + 1] == '*') {
   1274   1.1  christos 					/*
   1275   1.1  christos 					 * Special case for sftp: append
   1276   1.1  christos 					 * double-escaped glob sequence -
   1277   1.1  christos 					 * glob will undo one level of
   1278   1.1  christos 					 * escaping. NB. string can grow here.
   1279   1.1  christos 					 */
   1280   1.1  christos 					if (j >= sizeof(argvs) - 5)
   1281   1.1  christos 						goto args_too_longs;
   1282   1.1  christos 					argvs[j++] = '\\';
   1283   1.1  christos 					argvs[j++] = arg[i++];
   1284   1.1  christos 					argvs[j++] = '\\';
   1285   1.1  christos 					argvs[j++] = arg[i];
   1286   1.1  christos 				} else {
   1287   1.1  christos 					argvs[j++] = arg[i++];
   1288   1.1  christos 					argvs[j++] = arg[i];
   1289   1.1  christos 				}
   1290   1.1  christos 			} else {
   1291   1.1  christos 				if (state == MA_START) {
   1292   1.1  christos 					argv[argc] = argvs + j;
   1293   1.1  christos 					state = MA_UNQUOTED;
   1294   1.4      adam 					if (lastquote != NULL)
   1295   1.4      adam 						*lastquote = '\0';
   1296   1.1  christos 				}
   1297   1.1  christos 				if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
   1298   1.1  christos 				    arg[i + 1] == '*' || arg[i + 1] == '\\') {
   1299   1.1  christos 					/*
   1300   1.1  christos 					 * Special case for sftp: append
   1301   1.1  christos 					 * escaped glob sequence -
   1302   1.1  christos 					 * glob will undo one level of
   1303   1.1  christos 					 * escaping.
   1304   1.1  christos 					 */
   1305   1.1  christos 					argvs[j++] = arg[i++];
   1306   1.1  christos 					argvs[j++] = arg[i];
   1307   1.1  christos 				} else {
   1308   1.1  christos 					/* Unescape everything */
   1309   1.1  christos 					/* XXX support \n and friends? */
   1310   1.1  christos 					i++;
   1311   1.1  christos 					argvs[j++] = arg[i];
   1312   1.1  christos 				}
   1313   1.1  christos 			}
   1314   1.1  christos 		} else if (arg[i] == '#') {
   1315   1.1  christos 			if (state == MA_SQUOTE || state == MA_DQUOTE)
   1316   1.1  christos 				argvs[j++] = arg[i];
   1317   1.1  christos 			else
   1318   1.1  christos 				goto string_done;
   1319   1.1  christos 		} else if (arg[i] == '\0') {
   1320   1.1  christos 			if (state == MA_SQUOTE || state == MA_DQUOTE) {
   1321   1.4      adam 				if (sloppy) {
   1322   1.4      adam 					state = MA_UNQUOTED;
   1323   1.4      adam 					if (terminated != NULL)
   1324   1.4      adam 						*terminated = 0;
   1325   1.4      adam 					goto string_done;
   1326   1.4      adam 				}
   1327   1.1  christos 				error("Unterminated quoted argument");
   1328   1.1  christos 				return NULL;
   1329   1.1  christos 			}
   1330   1.1  christos  string_done:
   1331   1.1  christos 			if (state == MA_UNQUOTED) {
   1332   1.1  christos 				argvs[j++] = '\0';
   1333   1.1  christos 				argc++;
   1334   1.1  christos 			}
   1335   1.1  christos 			break;
   1336   1.1  christos 		} else {
   1337   1.1  christos 			if (state == MA_START) {
   1338   1.1  christos 				argv[argc] = argvs + j;
   1339   1.1  christos 				state = MA_UNQUOTED;
   1340   1.4      adam 				if (lastquote != NULL)
   1341   1.4      adam 					*lastquote = '\0';
   1342   1.1  christos 			}
   1343   1.1  christos 			if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
   1344   1.1  christos 			    (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
   1345   1.1  christos 				/*
   1346   1.1  christos 				 * Special case for sftp: escape quoted
   1347   1.1  christos 				 * glob(3) wildcards. NB. string can grow
   1348   1.1  christos 				 * here.
   1349   1.1  christos 				 */
   1350   1.1  christos 				if (j >= sizeof(argvs) - 3)
   1351   1.1  christos 					goto args_too_longs;
   1352   1.1  christos 				argvs[j++] = '\\';
   1353   1.1  christos 				argvs[j++] = arg[i];
   1354   1.1  christos 			} else
   1355   1.1  christos 				argvs[j++] = arg[i];
   1356   1.1  christos 		}
   1357   1.1  christos 		i++;
   1358   1.1  christos 	}
   1359   1.1  christos 	*argcp = argc;
   1360   1.1  christos 	return argv;
   1361   1.1  christos }
   1362   1.1  christos 
   1363   1.1  christos static int
   1364  1.26  christos parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
   1365  1.19  christos 	  int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
   1366  1.13  christos 	  int *rflag, int *sflag,
   1367  1.13  christos     unsigned long *n_arg, char **path1, char **path2)
   1368   1.1  christos {
   1369   1.1  christos 	const char *cmd, *cp = *cpp;
   1370   1.1  christos 	char *cp2, **argv;
   1371   1.1  christos 	int base = 0;
   1372  1.31  christos 	long long ll;
   1373  1.23  christos 	int path1_mandatory = 0, i, cmdnum, optidx, argc;
   1374   1.1  christos 
   1375   1.1  christos 	/* Skip leading whitespace */
   1376   1.1  christos 	cp = cp + strspn(cp, WHITESPACE);
   1377   1.1  christos 
   1378  1.26  christos 	/*
   1379  1.26  christos 	 * Check for leading '-' (disable error processing) and '@' (suppress
   1380  1.26  christos 	 * command echo)
   1381  1.26  christos 	 */
   1382  1.13  christos 	*ignore_errors = 0;
   1383  1.26  christos 	*disable_echo = 0;
   1384  1.26  christos 	for (;*cp != '\0'; cp++) {
   1385  1.26  christos 		if (*cp == '-') {
   1386  1.26  christos 			*ignore_errors = 1;
   1387  1.26  christos 		} else if (*cp == '@') {
   1388  1.26  christos 			*disable_echo = 1;
   1389  1.26  christos 		} else {
   1390  1.26  christos 			/* all other characters terminate prefix processing */
   1391  1.26  christos 			break;
   1392  1.26  christos 		}
   1393   1.1  christos 	}
   1394  1.26  christos 	cp = cp + strspn(cp, WHITESPACE);
   1395   1.1  christos 
   1396   1.4      adam 	/* Ignore blank lines and lines which begin with comment '#' char */
   1397   1.4      adam 	if (*cp == '\0' || *cp == '#')
   1398   1.4      adam 		return (0);
   1399   1.4      adam 
   1400   1.4      adam 	if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
   1401   1.1  christos 		return -1;
   1402   1.1  christos 
   1403   1.1  christos 	/* Figure out which command we have */
   1404   1.1  christos 	for (i = 0; cmds[i].c != NULL; i++) {
   1405  1.11  christos 		if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
   1406   1.1  christos 			break;
   1407   1.1  christos 	}
   1408   1.1  christos 	cmdnum = cmds[i].n;
   1409   1.1  christos 	cmd = cmds[i].c;
   1410   1.1  christos 
   1411   1.1  christos 	/* Special case */
   1412   1.1  christos 	if (*cp == '!') {
   1413   1.1  christos 		cp++;
   1414   1.1  christos 		cmdnum = I_SHELL;
   1415   1.1  christos 	} else if (cmdnum == -1) {
   1416   1.1  christos 		error("Invalid command.");
   1417   1.1  christos 		return -1;
   1418   1.1  christos 	}
   1419   1.1  christos 
   1420   1.1  christos 	/* Get arguments and parse flags */
   1421  1.13  christos 	*aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
   1422  1.13  christos 	*rflag = *sflag = 0;
   1423   1.1  christos 	*path1 = *path2 = NULL;
   1424   1.1  christos 	optidx = 1;
   1425   1.1  christos 	switch (cmdnum) {
   1426   1.1  christos 	case I_GET:
   1427  1.12  christos 	case I_REGET:
   1428  1.13  christos 	case I_REPUT:
   1429   1.1  christos 	case I_PUT:
   1430   1.7  christos 		if ((optidx = parse_getput_flags(cmd, argv, argc,
   1431  1.13  christos 		    aflag, fflag, pflag, rflag)) == -1)
   1432   1.1  christos 			return -1;
   1433   1.1  christos 		/* Get first pathname (mandatory) */
   1434   1.1  christos 		if (argc - optidx < 1) {
   1435   1.1  christos 			error("You must specify at least one path after a "
   1436   1.1  christos 			    "%s command.", cmd);
   1437   1.1  christos 			return -1;
   1438   1.1  christos 		}
   1439   1.1  christos 		*path1 = xstrdup(argv[optidx]);
   1440   1.1  christos 		/* Get second pathname (optional) */
   1441   1.1  christos 		if (argc - optidx > 1) {
   1442   1.1  christos 			*path2 = xstrdup(argv[optidx + 1]);
   1443   1.1  christos 			/* Destination is not globbed */
   1444   1.1  christos 			undo_glob_escape(*path2);
   1445   1.1  christos 		}
   1446   1.1  christos 		break;
   1447   1.7  christos 	case I_LINK:
   1448   1.7  christos 		if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
   1449   1.7  christos 			return -1;
   1450  1.13  christos 		goto parse_two_paths;
   1451  1.35  christos 	case I_COPY:
   1452  1.35  christos 		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
   1453  1.35  christos 			return -1;
   1454  1.35  christos 		goto parse_two_paths;
   1455  1.13  christos 	case I_RENAME:
   1456  1.13  christos 		if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
   1457  1.13  christos 			return -1;
   1458  1.13  christos 		goto parse_two_paths;
   1459   1.7  christos 	case I_SYMLINK:
   1460  1.13  christos 		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
   1461  1.13  christos 			return -1;
   1462  1.13  christos  parse_two_paths:
   1463   1.1  christos 		if (argc - optidx < 2) {
   1464   1.1  christos 			error("You must specify two paths after a %s "
   1465   1.1  christos 			    "command.", cmd);
   1466   1.1  christos 			return -1;
   1467   1.1  christos 		}
   1468   1.1  christos 		*path1 = xstrdup(argv[optidx]);
   1469   1.1  christos 		*path2 = xstrdup(argv[optidx + 1]);
   1470   1.1  christos 		/* Paths are not globbed */
   1471   1.1  christos 		undo_glob_escape(*path1);
   1472   1.1  christos 		undo_glob_escape(*path2);
   1473   1.1  christos 		break;
   1474   1.1  christos 	case I_RM:
   1475   1.1  christos 	case I_MKDIR:
   1476   1.1  christos 	case I_RMDIR:
   1477  1.23  christos 	case I_LMKDIR:
   1478  1.23  christos 		path1_mandatory = 1;
   1479  1.23  christos 		/* FALLTHROUGH */
   1480   1.1  christos 	case I_CHDIR:
   1481   1.1  christos 	case I_LCHDIR:
   1482  1.13  christos 		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
   1483  1.13  christos 			return -1;
   1484   1.1  christos 		/* Get pathname (mandatory) */
   1485   1.1  christos 		if (argc - optidx < 1) {
   1486  1.23  christos 			if (!path1_mandatory)
   1487  1.23  christos 				break; /* return a NULL path1 */
   1488   1.1  christos 			error("You must specify a path after a %s command.",
   1489   1.1  christos 			    cmd);
   1490   1.1  christos 			return -1;
   1491   1.1  christos 		}
   1492   1.1  christos 		*path1 = xstrdup(argv[optidx]);
   1493   1.1  christos 		/* Only "rm" globs */
   1494   1.1  christos 		if (cmdnum != I_RM)
   1495   1.1  christos 			undo_glob_escape(*path1);
   1496   1.1  christos 		break;
   1497   1.1  christos 	case I_DF:
   1498   1.1  christos 		if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
   1499   1.1  christos 		    iflag)) == -1)
   1500   1.1  christos 			return -1;
   1501   1.1  christos 		/* Default to current directory if no path specified */
   1502   1.1  christos 		if (argc - optidx < 1)
   1503   1.1  christos 			*path1 = NULL;
   1504   1.1  christos 		else {
   1505   1.1  christos 			*path1 = xstrdup(argv[optidx]);
   1506   1.1  christos 			undo_glob_escape(*path1);
   1507   1.1  christos 		}
   1508   1.1  christos 		break;
   1509   1.1  christos 	case I_LS:
   1510   1.1  christos 		if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
   1511   1.1  christos 			return(-1);
   1512   1.1  christos 		/* Path is optional */
   1513   1.1  christos 		if (argc - optidx > 0)
   1514   1.1  christos 			*path1 = xstrdup(argv[optidx]);
   1515   1.1  christos 		break;
   1516   1.1  christos 	case I_LLS:
   1517   1.1  christos 		/* Skip ls command and following whitespace */
   1518   1.1  christos 		cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
   1519   1.1  christos 	case I_SHELL:
   1520   1.1  christos 		/* Uses the rest of the line */
   1521   1.1  christos 		break;
   1522   1.1  christos 	case I_LUMASK:
   1523   1.1  christos 	case I_CHMOD:
   1524   1.1  christos 		base = 8;
   1525  1.26  christos 		/* FALLTHROUGH */
   1526   1.1  christos 	case I_CHOWN:
   1527   1.1  christos 	case I_CHGRP:
   1528  1.26  christos 		if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
   1529  1.13  christos 			return -1;
   1530   1.1  christos 		/* Get numeric arg (mandatory) */
   1531   1.1  christos 		if (argc - optidx < 1)
   1532   1.1  christos 			goto need_num_arg;
   1533   1.1  christos 		errno = 0;
   1534  1.31  christos 		ll = strtoll(argv[optidx], &cp2, base);
   1535   1.1  christos 		if (cp2 == argv[optidx] || *cp2 != '\0' ||
   1536  1.31  christos 		    ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) ||
   1537  1.31  christos 		    ll < 0 || ll > UINT32_MAX) {
   1538   1.1  christos  need_num_arg:
   1539   1.1  christos 			error("You must supply a numeric argument "
   1540   1.1  christos 			    "to the %s command.", cmd);
   1541   1.1  christos 			return -1;
   1542   1.1  christos 		}
   1543  1.31  christos 		*n_arg = ll;
   1544   1.1  christos 		if (cmdnum == I_LUMASK)
   1545   1.1  christos 			break;
   1546   1.1  christos 		/* Get pathname (mandatory) */
   1547   1.1  christos 		if (argc - optidx < 2) {
   1548   1.1  christos 			error("You must specify a path after a %s command.",
   1549   1.1  christos 			    cmd);
   1550   1.1  christos 			return -1;
   1551   1.1  christos 		}
   1552   1.1  christos 		*path1 = xstrdup(argv[optidx + 1]);
   1553   1.1  christos 		break;
   1554   1.1  christos 	case I_QUIT:
   1555   1.1  christos 	case I_PWD:
   1556   1.1  christos 	case I_LPWD:
   1557   1.1  christos 	case I_HELP:
   1558   1.1  christos 	case I_VERSION:
   1559   1.1  christos 	case I_PROGRESS:
   1560  1.13  christos 		if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
   1561  1.13  christos 			return -1;
   1562   1.1  christos 		break;
   1563   1.1  christos 	default:
   1564   1.1  christos 		fatal("Command not implemented");
   1565   1.1  christos 	}
   1566   1.1  christos 
   1567   1.1  christos 	*cpp = cp;
   1568   1.1  christos 	return(cmdnum);
   1569   1.1  christos }
   1570   1.1  christos 
   1571   1.1  christos static int
   1572   1.1  christos parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
   1573  1.26  christos     const char *startdir, int err_abort, int echo_command)
   1574   1.1  christos {
   1575  1.26  christos 	const char *ocmd = cmd;
   1576   1.1  christos 	char *path1, *path2, *tmp;
   1577  1.26  christos 	int ignore_errors = 0, disable_echo = 1;
   1578  1.26  christos 	int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
   1579  1.13  christos 	int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
   1580   1.7  christos 	int cmdnum, i;
   1581   1.1  christos 	unsigned long n_arg = 0;
   1582  1.39  christos 	Attrib a, aa;
   1583  1.14  christos 	char path_buf[PATH_MAX];
   1584   1.1  christos 	int err = 0;
   1585   1.1  christos 	glob_t g;
   1586   1.1  christos 
   1587   1.2  christos 	pflag = 0;	/* XXX gcc */
   1588   1.2  christos 	lflag = 0;	/* XXX gcc */
   1589   1.2  christos 	iflag = 0;	/* XXX gcc */
   1590   1.2  christos 	hflag = 0;	/* XXX gcc */
   1591   1.2  christos 	n_arg = 0;	/* XXX gcc */
   1592   1.2  christos 
   1593   1.1  christos 	path1 = path2 = NULL;
   1594  1.26  christos 	cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
   1595  1.26  christos 	    &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
   1596  1.26  christos 	    &path1, &path2);
   1597  1.13  christos 	if (ignore_errors != 0)
   1598   1.1  christos 		err_abort = 0;
   1599   1.1  christos 
   1600  1.26  christos 	if (echo_command && !disable_echo)
   1601  1.26  christos 		mprintf("sftp> %s\n", ocmd);
   1602  1.26  christos 
   1603   1.1  christos 	memset(&g, 0, sizeof(g));
   1604   1.1  christos 
   1605   1.1  christos 	/* Perform command */
   1606   1.1  christos 	switch (cmdnum) {
   1607   1.1  christos 	case 0:
   1608   1.1  christos 		/* Blank line */
   1609   1.1  christos 		break;
   1610   1.1  christos 	case -1:
   1611   1.1  christos 		/* Unrecognized command */
   1612   1.1  christos 		err = -1;
   1613   1.1  christos 		break;
   1614  1.12  christos 	case I_REGET:
   1615  1.12  christos 		aflag = 1;
   1616  1.12  christos 		/* FALLTHROUGH */
   1617   1.1  christos 	case I_GET:
   1618  1.12  christos 		err = process_get(conn, path1, path2, *pwd, pflag,
   1619  1.13  christos 		    rflag, aflag, fflag);
   1620   1.1  christos 		break;
   1621  1.13  christos 	case I_REPUT:
   1622  1.13  christos 		aflag = 1;
   1623  1.13  christos 		/* FALLTHROUGH */
   1624   1.1  christos 	case I_PUT:
   1625  1.13  christos 		err = process_put(conn, path1, path2, *pwd, pflag,
   1626  1.13  christos 		    rflag, aflag, fflag);
   1627   1.1  christos 		break;
   1628  1.35  christos 	case I_COPY:
   1629  1.39  christos 		path1 = sftp_make_absolute(path1, *pwd);
   1630  1.39  christos 		path2 = sftp_make_absolute(path2, *pwd);
   1631  1.39  christos 		err = sftp_copy(conn, path1, path2);
   1632  1.35  christos 		break;
   1633   1.1  christos 	case I_RENAME:
   1634  1.39  christos 		path1 = sftp_make_absolute(path1, *pwd);
   1635  1.39  christos 		path2 = sftp_make_absolute(path2, *pwd);
   1636  1.39  christos 		err = sftp_rename(conn, path1, path2, lflag);
   1637   1.1  christos 		break;
   1638   1.1  christos 	case I_SYMLINK:
   1639   1.7  christos 		sflag = 1;
   1640  1.26  christos 		/* FALLTHROUGH */
   1641   1.7  christos 	case I_LINK:
   1642  1.13  christos 		if (!sflag)
   1643  1.39  christos 			path1 = sftp_make_absolute(path1, *pwd);
   1644  1.39  christos 		path2 = sftp_make_absolute(path2, *pwd);
   1645  1.39  christos 		err = (sflag ? sftp_symlink : sftp_hardlink)(conn,
   1646  1.39  christos 		    path1, path2);
   1647   1.1  christos 		break;
   1648   1.1  christos 	case I_RM:
   1649  1.36  christos 		path1 = make_absolute_pwd_glob(path1, *pwd);
   1650  1.39  christos 		sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
   1651   1.1  christos 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
   1652  1.12  christos 			if (!quiet)
   1653  1.19  christos 				mprintf("Removing %s\n", g.gl_pathv[i]);
   1654  1.39  christos 			err = sftp_rm(conn, g.gl_pathv[i]);
   1655   1.1  christos 			if (err != 0 && err_abort)
   1656   1.1  christos 				break;
   1657   1.1  christos 		}
   1658   1.1  christos 		break;
   1659   1.1  christos 	case I_MKDIR:
   1660  1.39  christos 		path1 = sftp_make_absolute(path1, *pwd);
   1661   1.1  christos 		attrib_clear(&a);
   1662   1.1  christos 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
   1663   1.1  christos 		a.perm = 0777;
   1664  1.39  christos 		err = sftp_mkdir(conn, path1, &a, 1);
   1665   1.1  christos 		break;
   1666   1.1  christos 	case I_RMDIR:
   1667  1.39  christos 		path1 = sftp_make_absolute(path1, *pwd);
   1668  1.39  christos 		err = sftp_rmdir(conn, path1);
   1669   1.1  christos 		break;
   1670   1.1  christos 	case I_CHDIR:
   1671  1.23  christos 		if (path1 == NULL || *path1 == '\0')
   1672  1.23  christos 			path1 = xstrdup(startdir);
   1673  1.39  christos 		path1 = sftp_make_absolute(path1, *pwd);
   1674  1.39  christos 		if ((tmp = sftp_realpath(conn, path1)) == NULL) {
   1675   1.1  christos 			err = 1;
   1676   1.1  christos 			break;
   1677   1.1  christos 		}
   1678  1.39  christos 		if (sftp_stat(conn, tmp, 0, &aa) != 0) {
   1679  1.12  christos 			free(tmp);
   1680   1.1  christos 			err = 1;
   1681   1.1  christos 			break;
   1682   1.1  christos 		}
   1683  1.39  christos 		if (!(aa.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
   1684   1.1  christos 			error("Can't change directory: Can't check target");
   1685  1.12  christos 			free(tmp);
   1686   1.1  christos 			err = 1;
   1687   1.1  christos 			break;
   1688   1.1  christos 		}
   1689  1.39  christos 		if (!S_ISDIR(aa.perm)) {
   1690   1.1  christos 			error("Can't change directory: \"%s\" is not "
   1691   1.1  christos 			    "a directory", tmp);
   1692  1.12  christos 			free(tmp);
   1693   1.1  christos 			err = 1;
   1694   1.1  christos 			break;
   1695   1.1  christos 		}
   1696  1.12  christos 		free(*pwd);
   1697   1.1  christos 		*pwd = tmp;
   1698   1.1  christos 		break;
   1699   1.1  christos 	case I_LS:
   1700   1.1  christos 		if (!path1) {
   1701   1.4      adam 			do_ls_dir(conn, *pwd, *pwd, lflag);
   1702   1.1  christos 			break;
   1703   1.1  christos 		}
   1704   1.1  christos 
   1705   1.1  christos 		/* Strip pwd off beginning of non-absolute paths */
   1706   1.1  christos 		tmp = NULL;
   1707  1.26  christos 		if (!path_absolute(path1))
   1708   1.1  christos 			tmp = *pwd;
   1709   1.1  christos 
   1710  1.36  christos 		path1 = make_absolute_pwd_glob(path1, *pwd);
   1711   1.1  christos 		err = do_globbed_ls(conn, path1, tmp, lflag);
   1712   1.1  christos 		break;
   1713   1.1  christos 	case I_DF:
   1714   1.1  christos 		/* Default to current directory if no path specified */
   1715   1.1  christos 		if (path1 == NULL)
   1716   1.1  christos 			path1 = xstrdup(*pwd);
   1717  1.39  christos 		path1 = sftp_make_absolute(path1, *pwd);
   1718   1.1  christos 		err = do_df(conn, path1, hflag, iflag);
   1719   1.1  christos 		break;
   1720   1.1  christos 	case I_LCHDIR:
   1721  1.23  christos 		if (path1 == NULL || *path1 == '\0')
   1722  1.23  christos 			path1 = xstrdup("~");
   1723  1.14  christos 		tmp = tilde_expand_filename(path1, getuid());
   1724  1.14  christos 		free(path1);
   1725  1.14  christos 		path1 = tmp;
   1726   1.1  christos 		if (chdir(path1) == -1) {
   1727   1.1  christos 			error("Couldn't change local directory to "
   1728   1.1  christos 			    "\"%s\": %s", path1, strerror(errno));
   1729   1.1  christos 			err = 1;
   1730   1.1  christos 		}
   1731   1.1  christos 		break;
   1732   1.1  christos 	case I_LMKDIR:
   1733   1.1  christos 		if (mkdir(path1, 0777) == -1) {
   1734   1.1  christos 			error("Couldn't create local directory "
   1735   1.1  christos 			    "\"%s\": %s", path1, strerror(errno));
   1736   1.1  christos 			err = 1;
   1737   1.1  christos 		}
   1738   1.1  christos 		break;
   1739   1.1  christos 	case I_LLS:
   1740   1.1  christos 		local_do_ls(cmd);
   1741   1.1  christos 		break;
   1742   1.1  christos 	case I_SHELL:
   1743   1.1  christos 		local_do_shell(cmd);
   1744   1.1  christos 		break;
   1745   1.1  christos 	case I_LUMASK:
   1746   1.1  christos 		umask(n_arg);
   1747   1.1  christos 		printf("Local umask: %03lo\n", n_arg);
   1748   1.1  christos 		break;
   1749   1.1  christos 	case I_CHMOD:
   1750  1.36  christos 		path1 = make_absolute_pwd_glob(path1, *pwd);
   1751   1.1  christos 		attrib_clear(&a);
   1752   1.1  christos 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
   1753   1.1  christos 		a.perm = n_arg;
   1754  1.39  christos 		sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
   1755   1.1  christos 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
   1756  1.12  christos 			if (!quiet)
   1757  1.19  christos 				mprintf("Changing mode on %s\n",
   1758  1.19  christos 				    g.gl_pathv[i]);
   1759  1.39  christos 			err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
   1760  1.26  christos 			    g.gl_pathv[i], &a);
   1761   1.1  christos 			if (err != 0 && err_abort)
   1762   1.1  christos 				break;
   1763   1.1  christos 		}
   1764   1.1  christos 		break;
   1765   1.1  christos 	case I_CHOWN:
   1766   1.1  christos 	case I_CHGRP:
   1767  1.36  christos 		path1 = make_absolute_pwd_glob(path1, *pwd);
   1768  1.39  christos 		sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
   1769   1.1  christos 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
   1770  1.39  christos 			if ((hflag ? sftp_lstat : sftp_stat)(conn,
   1771  1.39  christos 			    g.gl_pathv[i], 0, &aa) != 0) {
   1772   1.1  christos 				if (err_abort) {
   1773   1.1  christos 					err = -1;
   1774   1.1  christos 					break;
   1775   1.1  christos 				} else
   1776   1.1  christos 					continue;
   1777   1.1  christos 			}
   1778  1.39  christos 			if (!(aa.flags & SSH2_FILEXFER_ATTR_UIDGID)) {
   1779   1.1  christos 				error("Can't get current ownership of "
   1780   1.1  christos 				    "remote file \"%s\"", g.gl_pathv[i]);
   1781   1.1  christos 				if (err_abort) {
   1782   1.1  christos 					err = -1;
   1783   1.1  christos 					break;
   1784   1.1  christos 				} else
   1785   1.1  christos 					continue;
   1786   1.1  christos 			}
   1787  1.39  christos 			aa.flags &= SSH2_FILEXFER_ATTR_UIDGID;
   1788   1.1  christos 			if (cmdnum == I_CHOWN) {
   1789  1.12  christos 				if (!quiet)
   1790  1.19  christos 					mprintf("Changing owner on %s\n",
   1791  1.12  christos 					    g.gl_pathv[i]);
   1792  1.39  christos 				aa.uid = n_arg;
   1793   1.1  christos 			} else {
   1794  1.12  christos 				if (!quiet)
   1795  1.19  christos 					mprintf("Changing group on %s\n",
   1796  1.12  christos 					    g.gl_pathv[i]);
   1797  1.39  christos 				aa.gid = n_arg;
   1798   1.1  christos 			}
   1799  1.39  christos 			err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
   1800  1.39  christos 			    g.gl_pathv[i], &aa);
   1801   1.1  christos 			if (err != 0 && err_abort)
   1802   1.1  christos 				break;
   1803   1.1  christos 		}
   1804   1.1  christos 		break;
   1805   1.1  christos 	case I_PWD:
   1806  1.19  christos 		mprintf("Remote working directory: %s\n", *pwd);
   1807   1.1  christos 		break;
   1808   1.1  christos 	case I_LPWD:
   1809   1.1  christos 		if (!getcwd(path_buf, sizeof(path_buf))) {
   1810   1.1  christos 			error("Couldn't get local cwd: %s", strerror(errno));
   1811   1.1  christos 			err = -1;
   1812   1.1  christos 			break;
   1813   1.1  christos 		}
   1814  1.19  christos 		mprintf("Local working directory: %s\n", path_buf);
   1815   1.1  christos 		break;
   1816   1.1  christos 	case I_QUIT:
   1817   1.1  christos 		/* Processed below */
   1818   1.1  christos 		break;
   1819   1.1  christos 	case I_HELP:
   1820   1.1  christos 		help();
   1821   1.1  christos 		break;
   1822   1.1  christos 	case I_VERSION:
   1823   1.1  christos 		printf("SFTP protocol version %u\n", sftp_proto_version(conn));
   1824   1.1  christos 		break;
   1825   1.1  christos 	case I_PROGRESS:
   1826   1.1  christos 		showprogress = !showprogress;
   1827   1.1  christos 		if (showprogress)
   1828   1.1  christos 			printf("Progress meter enabled\n");
   1829   1.1  christos 		else
   1830   1.1  christos 			printf("Progress meter disabled\n");
   1831   1.1  christos 		break;
   1832   1.1  christos 	default:
   1833   1.1  christos 		fatal("%d is not implemented", cmdnum);
   1834   1.1  christos 	}
   1835   1.1  christos 
   1836   1.1  christos 	if (g.gl_pathc)
   1837   1.1  christos 		globfree(&g);
   1838  1.12  christos 	free(path1);
   1839  1.12  christos 	free(path2);
   1840   1.1  christos 
   1841   1.1  christos 	/* If an unignored error occurs in batch mode we should abort. */
   1842   1.1  christos 	if (err_abort && err != 0)
   1843   1.1  christos 		return (-1);
   1844   1.1  christos 	else if (cmdnum == I_QUIT)
   1845   1.1  christos 		return (1);
   1846   1.1  christos 
   1847   1.1  christos 	return (0);
   1848   1.1  christos }
   1849   1.1  christos 
   1850   1.7  christos static const char *
   1851   1.1  christos prompt(EditLine *el)
   1852   1.1  christos {
   1853   1.1  christos 	return ("sftp> ");
   1854   1.1  christos }
   1855   1.1  christos 
   1856   1.4      adam /* Display entries in 'list' after skipping the first 'len' chars */
   1857   1.4      adam static void
   1858   1.4      adam complete_display(char **list, u_int len)
   1859   1.4      adam {
   1860   1.4      adam 	u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
   1861   1.4      adam 	struct winsize ws;
   1862   1.7  christos 	const char *tmp;
   1863   1.4      adam 
   1864   1.4      adam 	/* Count entries for sort and find longest */
   1865  1.13  christos 	for (y = 0; list[y]; y++)
   1866  1.20  christos 		m = MAXIMUM(m, strlen(list[y]));
   1867   1.4      adam 
   1868   1.4      adam 	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
   1869   1.4      adam 		width = ws.ws_col;
   1870   1.4      adam 
   1871   1.4      adam 	m = m > len ? m - len : 0;
   1872   1.4      adam 	columns = width / (m + 2);
   1873  1.20  christos 	columns = MAXIMUM(columns, 1);
   1874   1.4      adam 	colspace = width / columns;
   1875  1.20  christos 	colspace = MINIMUM(colspace, width);
   1876   1.4      adam 
   1877   1.4      adam 	printf("\n");
   1878   1.4      adam 	m = 1;
   1879   1.4      adam 	for (y = 0; list[y]; y++) {
   1880   1.4      adam 		llen = strlen(list[y]);
   1881   1.4      adam 		tmp = llen > len ? list[y] + len : "";
   1882  1.19  christos 		mprintf("%-*s", colspace, tmp);
   1883   1.4      adam 		if (m >= columns) {
   1884   1.4      adam 			printf("\n");
   1885   1.4      adam 			m = 1;
   1886   1.4      adam 		} else
   1887   1.4      adam 			m++;
   1888   1.4      adam 	}
   1889   1.4      adam 	printf("\n");
   1890   1.4      adam }
   1891   1.4      adam 
   1892   1.4      adam /*
   1893   1.4      adam  * Given a "list" of words that begin with a common prefix of "word",
   1894  1.43  christos  * attempt to find an autocompletion that extends "word" by the next
   1895   1.4      adam  * characters common to all entries in "list".
   1896   1.4      adam  */
   1897   1.4      adam static char *
   1898   1.4      adam complete_ambiguous(const char *word, char **list, size_t count)
   1899   1.4      adam {
   1900  1.44  christos 	size_t i, j, matchlen;
   1901  1.44  christos 	char *tmp;
   1902  1.44  christos 	int len;
   1903  1.44  christos 
   1904   1.4      adam 	if (word == NULL)
   1905   1.4      adam 		return NULL;
   1906   1.4      adam 
   1907  1.44  christos 	if (count == 0)
   1908  1.44  christos 		return xstrdup(word); /* no options to complete */
   1909   1.4      adam 
   1910  1.44  christos 	/* Find length of common stem across list */
   1911  1.44  christos 	matchlen = strlen(list[0]);
   1912  1.44  christos 	for (i = 1; i < count && list[i] != NULL; i++) {
   1913  1.44  christos 		for (j = 0; j < matchlen; j++)
   1914  1.44  christos 			if (list[0][j] != list[i][j])
   1915  1.44  christos 				break;
   1916  1.44  christos 		matchlen = j;
   1917  1.44  christos 	}
   1918   1.4      adam 
   1919  1.44  christos 	/*
   1920  1.44  christos 	 * Now check that the common stem doesn't finish in the middle of
   1921  1.44  christos 	 * a multibyte character.
   1922  1.44  christos 	 */
   1923  1.44  christos 	mblen(NULL, 0);
   1924  1.44  christos 	for (i = 0; i < matchlen;) {
   1925  1.44  christos 		len = mblen(list[0] + i, matchlen - i);
   1926  1.44  christos 		if (len <= 0 || i + (size_t)len > matchlen)
   1927  1.44  christos 			break;
   1928  1.44  christos 		i += (size_t)len;
   1929  1.44  christos 	}
   1930  1.44  christos 	/* If so, truncate */
   1931  1.44  christos 	if (i < matchlen)
   1932  1.44  christos 		matchlen = i;
   1933  1.44  christos 
   1934  1.44  christos 	if (matchlen > strlen(word)) {
   1935  1.44  christos 		tmp = xstrdup(list[0]);
   1936  1.44  christos 		tmp[matchlen] = '\0';
   1937  1.44  christos 		return tmp;
   1938  1.13  christos 	}
   1939   1.4      adam 
   1940   1.4      adam 	return xstrdup(word);
   1941   1.4      adam }
   1942   1.4      adam 
   1943   1.4      adam /* Autocomplete a sftp command */
   1944   1.4      adam static int
   1945   1.4      adam complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
   1946   1.4      adam     int terminated)
   1947   1.4      adam {
   1948   1.4      adam 	u_int y, count = 0, cmdlen, tmplen;
   1949   1.4      adam 	char *tmp, **list, argterm[3];
   1950   1.4      adam 	const LineInfo *lf;
   1951   1.4      adam 
   1952   1.4      adam 	list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
   1953   1.4      adam 
   1954   1.4      adam 	/* No command specified: display all available commands */
   1955   1.4      adam 	if (cmd == NULL) {
   1956   1.4      adam 		for (y = 0; cmds[y].c; y++)
   1957   1.4      adam 			list[count++] = xstrdup(cmds[y].c);
   1958  1.13  christos 
   1959   1.4      adam 		list[count] = NULL;
   1960   1.4      adam 		complete_display(list, 0);
   1961   1.4      adam 
   1962  1.13  christos 		for (y = 0; list[y] != NULL; y++)
   1963  1.13  christos 			free(list[y]);
   1964  1.12  christos 		free(list);
   1965   1.4      adam 		return count;
   1966   1.4      adam 	}
   1967   1.4      adam 
   1968   1.4      adam 	/* Prepare subset of commands that start with "cmd" */
   1969   1.4      adam 	cmdlen = strlen(cmd);
   1970   1.4      adam 	for (y = 0; cmds[y].c; y++)  {
   1971  1.13  christos 		if (!strncasecmp(cmd, cmds[y].c, cmdlen))
   1972   1.4      adam 			list[count++] = xstrdup(cmds[y].c);
   1973   1.4      adam 	}
   1974   1.4      adam 	list[count] = NULL;
   1975   1.4      adam 
   1976   1.9  christos 	if (count == 0) {
   1977  1.12  christos 		free(list);
   1978   1.4      adam 		return 0;
   1979   1.9  christos 	}
   1980   1.4      adam 
   1981  1.24  christos 	/* Complete ambiguous command */
   1982   1.4      adam 	tmp = complete_ambiguous(cmd, list, count);
   1983   1.4      adam 	if (count > 1)
   1984   1.4      adam 		complete_display(list, 0);
   1985   1.4      adam 
   1986  1.13  christos 	for (y = 0; list[y]; y++)
   1987  1.13  christos 		free(list[y]);
   1988  1.12  christos 	free(list);
   1989   1.4      adam 
   1990   1.4      adam 	if (tmp != NULL) {
   1991   1.4      adam 		tmplen = strlen(tmp);
   1992   1.4      adam 		cmdlen = strlen(cmd);
   1993   1.4      adam 		/* If cmd may be extended then do so */
   1994   1.4      adam 		if (tmplen > cmdlen)
   1995   1.4      adam 			if (el_insertstr(el, tmp + cmdlen) == -1)
   1996   1.4      adam 				fatal("el_insertstr failed.");
   1997   1.4      adam 		lf = el_line(el);
   1998   1.4      adam 		/* Terminate argument cleanly */
   1999   1.4      adam 		if (count == 1) {
   2000   1.4      adam 			y = 0;
   2001   1.4      adam 			if (!terminated)
   2002   1.4      adam 				argterm[y++] = quote;
   2003   1.4      adam 			if (lastarg || *(lf->cursor) != ' ')
   2004   1.4      adam 				argterm[y++] = ' ';
   2005   1.4      adam 			argterm[y] = '\0';
   2006   1.4      adam 			if (y > 0 && el_insertstr(el, argterm) == -1)
   2007   1.4      adam 				fatal("el_insertstr failed.");
   2008   1.4      adam 		}
   2009  1.12  christos 		free(tmp);
   2010   1.4      adam 	}
   2011   1.4      adam 
   2012   1.4      adam 	return count;
   2013   1.4      adam }
   2014   1.4      adam 
   2015   1.4      adam /*
   2016  1.36  christos  * Determine whether a particular sftp command's arguments (if any) represent
   2017  1.36  christos  * local or remote files. The "cmdarg" argument specifies the actual argument
   2018  1.36  christos  * and accepts values 1 or 2.
   2019   1.4      adam  */
   2020   1.4      adam static int
   2021  1.36  christos complete_is_remote(char *cmd, int cmdarg) {
   2022   1.4      adam 	int i;
   2023   1.4      adam 
   2024   1.4      adam 	if (cmd == NULL)
   2025   1.4      adam 		return -1;
   2026   1.4      adam 
   2027   1.4      adam 	for (i = 0; cmds[i].c; i++) {
   2028  1.36  christos 		if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
   2029  1.36  christos 			if (cmdarg == 1)
   2030  1.36  christos 				return cmds[i].t;
   2031  1.36  christos 			else if (cmdarg == 2)
   2032  1.36  christos 				return cmds[i].t2;
   2033  1.36  christos 			break;
   2034  1.36  christos 		}
   2035   1.4      adam 	}
   2036   1.4      adam 
   2037   1.4      adam 	return -1;
   2038   1.4      adam }
   2039   1.4      adam 
   2040   1.4      adam /* Autocomplete a filename "file" */
   2041   1.4      adam static int
   2042   1.4      adam complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
   2043   1.4      adam     char *file, int remote, int lastarg, char quote, int terminated)
   2044   1.4      adam {
   2045   1.4      adam 	glob_t g;
   2046  1.12  christos 	char *tmp, *tmp2, ins[8];
   2047  1.11  christos 	u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
   2048  1.12  christos 	int clen;
   2049   1.4      adam 	const LineInfo *lf;
   2050  1.13  christos 
   2051   1.4      adam 	/* Glob from "file" location */
   2052   1.4      adam 	if (file == NULL)
   2053   1.4      adam 		tmp = xstrdup("*");
   2054   1.4      adam 	else
   2055   1.4      adam 		xasprintf(&tmp, "%s*", file);
   2056   1.4      adam 
   2057  1.11  christos 	/* Check if the path is absolute. */
   2058  1.26  christos 	isabs = path_absolute(tmp);
   2059  1.11  christos 
   2060   1.4      adam 	memset(&g, 0, sizeof(g));
   2061   1.4      adam 	if (remote != LOCAL) {
   2062  1.39  christos 		tmp = make_absolute_pwd_glob(tmp, remote_path);
   2063  1.39  christos 		sftp_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
   2064  1.17  christos 	} else
   2065  1.39  christos 		(void)glob(tmp, GLOB_LIMIT|GLOB_DOOFFS|GLOB_MARK, NULL, &g);
   2066  1.38       rin 
   2067   1.4      adam 	/* Determine length of pwd so we can trim completion display */
   2068   1.4      adam 	for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
   2069   1.4      adam 		/* Terminate counting on first unescaped glob metacharacter */
   2070   1.4      adam 		if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
   2071   1.4      adam 			if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
   2072   1.4      adam 				hadglob = 1;
   2073   1.4      adam 			break;
   2074   1.4      adam 		}
   2075   1.4      adam 		if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
   2076   1.4      adam 			tmplen++;
   2077   1.4      adam 		if (tmp[tmplen] == '/')
   2078   1.4      adam 			pwdlen = tmplen + 1;	/* track last seen '/' */
   2079   1.4      adam 	}
   2080  1.12  christos 	free(tmp);
   2081  1.13  christos 	tmp = NULL;
   2082   1.4      adam 
   2083  1.13  christos 	if (g.gl_matchc == 0)
   2084   1.4      adam 		goto out;
   2085   1.4      adam 
   2086   1.4      adam 	if (g.gl_matchc > 1)
   2087   1.4      adam 		complete_display(g.gl_pathv, pwdlen);
   2088   1.4      adam 
   2089   1.4      adam 	/* Don't try to extend globs */
   2090   1.4      adam 	if (file == NULL || hadglob)
   2091   1.4      adam 		goto out;
   2092   1.4      adam 
   2093   1.4      adam 	tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
   2094  1.11  christos 	tmp = path_strip(tmp2, isabs ? NULL : remote_path);
   2095  1.12  christos 	free(tmp2);
   2096   1.4      adam 
   2097   1.4      adam 	if (tmp == NULL)
   2098   1.4      adam 		goto out;
   2099   1.4      adam 
   2100   1.4      adam 	tmplen = strlen(tmp);
   2101   1.4      adam 	filelen = strlen(file);
   2102   1.4      adam 
   2103  1.11  christos 	/* Count the number of escaped characters in the input string. */
   2104  1.11  christos 	cesc = isesc = 0;
   2105  1.11  christos 	for (i = 0; i < filelen; i++) {
   2106  1.11  christos 		if (!isesc && file[i] == '\\' && i + 1 < filelen){
   2107  1.11  christos 			isesc = 1;
   2108  1.11  christos 			cesc++;
   2109  1.11  christos 		} else
   2110  1.11  christos 			isesc = 0;
   2111  1.11  christos 	}
   2112  1.11  christos 
   2113  1.11  christos 	if (tmplen > (filelen - cesc)) {
   2114  1.11  christos 		tmp2 = tmp + filelen - cesc;
   2115  1.13  christos 		len = strlen(tmp2);
   2116   1.4      adam 		/* quote argument on way out */
   2117  1.44  christos 		mblen(NULL, 0);
   2118  1.12  christos 		for (i = 0; i < len; i += clen) {
   2119  1.12  christos 			if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
   2120  1.12  christos 			    (size_t)clen > sizeof(ins) - 2)
   2121  1.12  christos 				fatal("invalid multibyte character");
   2122   1.4      adam 			ins[0] = '\\';
   2123  1.12  christos 			memcpy(ins + 1, tmp2 + i, clen);
   2124  1.12  christos 			ins[clen + 1] = '\0';
   2125   1.4      adam 			switch (tmp2[i]) {
   2126   1.4      adam 			case '\'':
   2127   1.4      adam 			case '"':
   2128   1.4      adam 			case '\\':
   2129   1.4      adam 			case '\t':
   2130   1.7  christos 			case '[':
   2131   1.4      adam 			case ' ':
   2132  1.11  christos 			case '#':
   2133  1.11  christos 			case '*':
   2134   1.4      adam 				if (quote == '\0' || tmp2[i] == quote) {
   2135   1.4      adam 					if (el_insertstr(el, ins) == -1)
   2136   1.4      adam 						fatal("el_insertstr "
   2137   1.4      adam 						    "failed.");
   2138   1.4      adam 					break;
   2139   1.4      adam 				}
   2140   1.4      adam 				/* FALLTHROUGH */
   2141   1.4      adam 			default:
   2142   1.4      adam 				if (el_insertstr(el, ins + 1) == -1)
   2143   1.4      adam 					fatal("el_insertstr failed.");
   2144   1.4      adam 				break;
   2145   1.4      adam 			}
   2146   1.4      adam 		}
   2147   1.4      adam 	}
   2148   1.4      adam 
   2149   1.4      adam 	lf = el_line(el);
   2150   1.4      adam 	if (g.gl_matchc == 1) {
   2151   1.4      adam 		i = 0;
   2152  1.13  christos 		if (!terminated && quote != '\0')
   2153   1.4      adam 			ins[i++] = quote;
   2154   1.4      adam 		if (*(lf->cursor - 1) != '/' &&
   2155   1.4      adam 		    (lastarg || *(lf->cursor) != ' '))
   2156   1.4      adam 			ins[i++] = ' ';
   2157   1.4      adam 		ins[i] = '\0';
   2158   1.4      adam 		if (i > 0 && el_insertstr(el, ins) == -1)
   2159   1.4      adam 			fatal("el_insertstr failed.");
   2160   1.4      adam 	}
   2161  1.12  christos 	free(tmp);
   2162   1.4      adam 
   2163   1.4      adam  out:
   2164   1.4      adam 	globfree(&g);
   2165   1.4      adam 	return g.gl_matchc;
   2166   1.4      adam }
   2167   1.4      adam 
   2168   1.4      adam /* tab-completion hook function, called via libedit */
   2169   1.4      adam static unsigned char
   2170   1.4      adam complete(EditLine *el, int ch)
   2171   1.4      adam {
   2172  1.13  christos 	char **argv, *line, quote;
   2173  1.12  christos 	int argc, carg;
   2174  1.12  christos 	u_int cursor, len, terminated, ret = CC_ERROR;
   2175   1.4      adam 	const LineInfo *lf;
   2176   1.4      adam 	struct complete_ctx *complete_ctx;
   2177   1.4      adam 
   2178   1.4      adam 	lf = el_line(el);
   2179   1.5      adam 	if (el_get(el, EL_CLIENTDATA, &complete_ctx) != 0)
   2180  1.31  christos 		fatal_f("el_get failed");
   2181   1.4      adam 
   2182   1.4      adam 	/* Figure out which argument the cursor points to */
   2183   1.4      adam 	cursor = lf->cursor - lf->buffer;
   2184  1.16  christos 	line = xmalloc(cursor + 1);
   2185   1.4      adam 	memcpy(line, lf->buffer, cursor);
   2186   1.4      adam 	line[cursor] = '\0';
   2187   1.4      adam 	argv = makeargv(line, &carg, 1, &quote, &terminated);
   2188  1.12  christos 	free(line);
   2189   1.4      adam 
   2190   1.4      adam 	/* Get all the arguments on the line */
   2191   1.4      adam 	len = lf->lastchar - lf->buffer;
   2192  1.16  christos 	line = xmalloc(len + 1);
   2193   1.4      adam 	memcpy(line, lf->buffer, len);
   2194   1.4      adam 	line[len] = '\0';
   2195   1.4      adam 	argv = makeargv(line, &argc, 1, NULL, NULL);
   2196   1.4      adam 
   2197   1.4      adam 	/* Ensure cursor is at EOL or a argument boundary */
   2198   1.4      adam 	if (line[cursor] != ' ' && line[cursor] != '\0' &&
   2199   1.4      adam 	    line[cursor] != '\n') {
   2200  1.12  christos 		free(line);
   2201   1.4      adam 		return ret;
   2202   1.4      adam 	}
   2203   1.4      adam 
   2204   1.4      adam 	if (carg == 0) {
   2205   1.4      adam 		/* Show all available commands */
   2206   1.4      adam 		complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
   2207   1.4      adam 		ret = CC_REDISPLAY;
   2208   1.4      adam 	} else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
   2209   1.4      adam 		/* Handle the command parsing */
   2210   1.4      adam 		if (complete_cmd_parse(el, argv[0], argc == carg,
   2211  1.13  christos 		    quote, terminated) != 0)
   2212   1.4      adam 			ret = CC_REDISPLAY;
   2213   1.4      adam 	} else if (carg >= 1) {
   2214   1.4      adam 		/* Handle file parsing */
   2215  1.36  christos 		int remote = 0;
   2216  1.36  christos 		int i = 0, cmdarg = 0;
   2217   1.4      adam 		char *filematch = NULL;
   2218   1.4      adam 
   2219   1.4      adam 		if (carg > 1 && line[cursor-1] != ' ')
   2220   1.4      adam 			filematch = argv[carg - 1];
   2221   1.4      adam 
   2222  1.36  christos 		for (i = 1; i < carg; i++) {
   2223  1.36  christos 			/* Skip flags */
   2224  1.36  christos 			if (argv[i][0] != '-')
   2225  1.36  christos 				cmdarg++;
   2226  1.36  christos 		}
   2227  1.36  christos 
   2228  1.36  christos 		/*
   2229  1.36  christos 		 * If previous argument is complete, then offer completion
   2230  1.36  christos 		 * on the next one.
   2231  1.36  christos 		 */
   2232  1.36  christos 		if (line[cursor - 1] == ' ')
   2233  1.36  christos 			cmdarg++;
   2234  1.36  christos 
   2235  1.36  christos 		remote = complete_is_remote(argv[0], cmdarg);
   2236  1.36  christos 
   2237  1.36  christos 		if ((remote == REMOTE || remote == LOCAL) &&
   2238   1.4      adam 		    complete_match(el, complete_ctx->conn,
   2239   1.4      adam 		    *complete_ctx->remote_pathp, filematch,
   2240  1.13  christos 		    remote, carg == argc, quote, terminated) != 0)
   2241   1.4      adam 			ret = CC_REDISPLAY;
   2242   1.4      adam 	}
   2243   1.4      adam 
   2244  1.13  christos 	free(line);
   2245   1.4      adam 	return ret;
   2246   1.4      adam }
   2247   1.4      adam 
   2248  1.23  christos static int
   2249   1.7  christos interactive_loop(struct sftp_conn *conn, const char *file1, const char *file2)
   2250   1.1  christos {
   2251   1.4      adam 	char *remote_path;
   2252  1.23  christos 	char *dir = NULL, *startdir = NULL;
   2253   1.1  christos 	char cmd[2048];
   2254  1.44  christos 	const char *editor;
   2255   1.1  christos 	int err, interactive;
   2256   1.1  christos 	EditLine *el = NULL;
   2257   1.1  christos 	History *hl = NULL;
   2258   1.1  christos 	HistEvent hev;
   2259   1.1  christos 	extern char *__progname;
   2260   1.4      adam 	struct complete_ctx complete_ctx;
   2261   1.1  christos 
   2262   1.1  christos 	if (!batchmode && isatty(STDIN_FILENO)) {
   2263   1.1  christos 		if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
   2264   1.1  christos 			fatal("Couldn't initialise editline");
   2265   1.1  christos 		if ((hl = history_init()) == NULL)
   2266   1.1  christos 			fatal("Couldn't initialise editline history");
   2267   1.1  christos 		history(hl, &hev, H_SETSIZE, 100);
   2268   1.1  christos 		el_set(el, EL_HIST, history, hl);
   2269   1.1  christos 
   2270   1.1  christos 		el_set(el, EL_PROMPT, prompt);
   2271   1.1  christos 		el_set(el, EL_EDITOR, "emacs");
   2272   1.1  christos 		el_set(el, EL_TERMINAL, NULL);
   2273   1.1  christos 		el_set(el, EL_SIGNAL, 1);
   2274   1.1  christos 		el_source(el, NULL);
   2275   1.4      adam 
   2276   1.4      adam 		/* Tab Completion */
   2277  1.13  christos 		el_set(el, EL_ADDFN, "ftp-complete",
   2278   1.7  christos 		    "Context sensitive argument completion", complete);
   2279   1.4      adam 		complete_ctx.conn = conn;
   2280   1.4      adam 		complete_ctx.remote_pathp = &remote_path;
   2281   1.4      adam 		el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
   2282   1.4      adam 		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
   2283  1.13  christos 		/* enable ctrl-left-arrow and ctrl-right-arrow */
   2284  1.13  christos 		el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
   2285  1.27  christos 		el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
   2286  1.13  christos 		el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
   2287  1.13  christos 		el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
   2288  1.13  christos 		/* make ^w match ksh behaviour */
   2289  1.13  christos 		el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
   2290  1.44  christos 
   2291  1.44  christos 		/* el_source() may have changed EL_EDITOR to vi */
   2292  1.44  christos 		if (el_get(el, EL_EDITOR, &editor) == 0 && editor[0] == 'v')
   2293  1.44  christos 			el_set(el, EL_BIND, "^[", "vi-command-mode", NULL);
   2294   1.1  christos 	}
   2295   1.1  christos 
   2296  1.39  christos 	if ((remote_path = sftp_realpath(conn, ".")) == NULL)
   2297   1.1  christos 		fatal("Need cwd");
   2298  1.23  christos 	startdir = xstrdup(remote_path);
   2299   1.1  christos 
   2300   1.1  christos 	if (file1 != NULL) {
   2301   1.1  christos 		dir = xstrdup(file1);
   2302  1.39  christos 		dir = sftp_make_absolute(dir, remote_path);
   2303   1.1  christos 
   2304  1.39  christos 		if (sftp_remote_is_dir(conn, dir) && file2 == NULL) {
   2305  1.12  christos 			if (!quiet)
   2306  1.19  christos 				mprintf("Changing to: %s\n", dir);
   2307   1.1  christos 			snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
   2308   1.4      adam 			if (parse_dispatch_command(conn, cmd,
   2309  1.26  christos 			    &remote_path, startdir, 1, 0) != 0) {
   2310  1.12  christos 				free(dir);
   2311  1.23  christos 				free(startdir);
   2312  1.12  christos 				free(remote_path);
   2313  1.12  christos 				free(conn);
   2314   1.1  christos 				return (-1);
   2315   1.1  christos 			}
   2316   1.1  christos 		} else {
   2317  1.11  christos 			/* XXX this is wrong wrt quoting */
   2318  1.12  christos 			snprintf(cmd, sizeof cmd, "get%s %s%s%s",
   2319  1.12  christos 			    global_aflag ? " -a" : "", dir,
   2320  1.12  christos 			    file2 == NULL ? "" : " ",
   2321  1.12  christos 			    file2 == NULL ? "" : file2);
   2322   1.4      adam 			err = parse_dispatch_command(conn, cmd,
   2323  1.26  christos 			    &remote_path, startdir, 1, 0);
   2324  1.12  christos 			free(dir);
   2325  1.23  christos 			free(startdir);
   2326  1.12  christos 			free(remote_path);
   2327  1.12  christos 			free(conn);
   2328   1.1  christos 			return (err);
   2329   1.1  christos 		}
   2330  1.12  christos 		free(dir);
   2331   1.1  christos 	}
   2332   1.1  christos 
   2333  1.14  christos 	setvbuf(stdout, NULL, _IOLBF, 0);
   2334  1.14  christos 	setvbuf(infile, NULL, _IOLBF, 0);
   2335   1.1  christos 
   2336   1.1  christos 	interactive = !batchmode && isatty(STDIN_FILENO);
   2337   1.1  christos 	err = 0;
   2338   1.1  christos 	for (;;) {
   2339  1.34  christos 		struct sigaction sa;
   2340   1.1  christos 		const char *line;
   2341   1.1  christos 		int count = 0;
   2342   1.1  christos 
   2343  1.34  christos 		interrupted = 0;
   2344  1.34  christos 		memset(&sa, 0, sizeof(sa));
   2345  1.34  christos 		sa.sa_handler = interactive ? read_interrupt : killchild;
   2346  1.34  christos 		if (sigaction(SIGINT, &sa, NULL) == -1) {
   2347  1.34  christos 			debug3("sigaction(%s): %s", strsignal(SIGINT),
   2348  1.34  christos 			    strerror(errno));
   2349  1.34  christos 			break;
   2350  1.34  christos 		}
   2351   1.1  christos 		if (el == NULL) {
   2352  1.41  christos 			if (interactive) {
   2353   1.1  christos 				printf("sftp> ");
   2354  1.41  christos 				fflush(stdout);
   2355  1.41  christos 			}
   2356   1.1  christos 			if (fgets(cmd, sizeof(cmd), infile) == NULL) {
   2357   1.1  christos 				if (interactive)
   2358   1.1  christos 					printf("\n");
   2359  1.34  christos 				if (interrupted)
   2360  1.34  christos 					continue;
   2361   1.1  christos 				break;
   2362   1.1  christos 			}
   2363   1.1  christos 		} else {
   2364   1.4      adam 			if ((line = el_gets(el, &count)) == NULL ||
   2365   1.4      adam 			    count <= 0) {
   2366   1.1  christos 				printf("\n");
   2367  1.33  christos 				if (interrupted)
   2368  1.33  christos 					continue;
   2369   1.1  christos 				break;
   2370   1.1  christos 			}
   2371   1.1  christos 			history(hl, &hev, H_ENTER, line);
   2372   1.1  christos 			if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
   2373   1.1  christos 				fprintf(stderr, "Error: input line too long\n");
   2374   1.1  christos 				continue;
   2375   1.1  christos 			}
   2376   1.1  christos 		}
   2377   1.1  christos 
   2378  1.26  christos 		cmd[strcspn(cmd, "\n")] = '\0';
   2379   1.1  christos 
   2380   1.1  christos 		/* Handle user interrupts gracefully during commands */
   2381   1.1  christos 		interrupted = 0;
   2382  1.28  christos 		ssh_signal(SIGINT, cmd_interrupt);
   2383   1.1  christos 
   2384   1.4      adam 		err = parse_dispatch_command(conn, cmd, &remote_path,
   2385  1.26  christos 		    startdir, batchmode, !interactive && el == NULL);
   2386   1.1  christos 		if (err != 0)
   2387   1.1  christos 			break;
   2388   1.1  christos 	}
   2389  1.28  christos 	ssh_signal(SIGCHLD, SIG_DFL);
   2390  1.12  christos 	free(remote_path);
   2391  1.23  christos 	free(startdir);
   2392  1.12  christos 	free(conn);
   2393   1.1  christos 
   2394  1.43  christos 	if (hl != NULL)
   2395  1.43  christos 		history_end(hl);
   2396   1.1  christos 	if (el != NULL)
   2397   1.1  christos 		el_end(el);
   2398   1.1  christos 
   2399   1.1  christos 	/* err == 1 signifies normal "quit" exit */
   2400   1.1  christos 	return (err >= 0 ? 0 : -1);
   2401   1.1  christos }
   2402   1.1  christos 
   2403   1.1  christos static void
   2404   1.7  christos connect_to_server(const char *path, char **args, int *in, int *out)
   2405   1.1  christos {
   2406  1.35  christos 	int c_in, c_out, inout[2];
   2407   1.1  christos 
   2408   1.1  christos 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
   2409   1.1  christos 		fatal("socketpair: %s", strerror(errno));
   2410   1.1  christos 	*in = *out = inout[0];
   2411   1.1  christos 	c_in = c_out = inout[1];
   2412   1.1  christos 
   2413   1.1  christos 	if ((sshpid = fork()) == -1)
   2414   1.1  christos 		fatal("fork: %s", strerror(errno));
   2415   1.1  christos 	else if (sshpid == 0) {
   2416   1.1  christos 		if ((dup2(c_in, STDIN_FILENO) == -1) ||
   2417   1.1  christos 		    (dup2(c_out, STDOUT_FILENO) == -1)) {
   2418   1.1  christos 			fprintf(stderr, "dup2: %s\n", strerror(errno));
   2419   1.1  christos 			_exit(1);
   2420   1.1  christos 		}
   2421   1.1  christos 		close(*in);
   2422   1.1  christos 		close(*out);
   2423   1.1  christos 		close(c_in);
   2424   1.1  christos 		close(c_out);
   2425   1.1  christos 
   2426   1.1  christos 		/*
   2427   1.1  christos 		 * The underlying ssh is in the same process group, so we must
   2428   1.1  christos 		 * ignore SIGINT if we want to gracefully abort commands,
   2429   1.1  christos 		 * otherwise the signal will make it to the ssh process and
   2430   1.4      adam 		 * kill it too.  Contrawise, since sftp sends SIGTERMs to the
   2431   1.4      adam 		 * underlying ssh, it must *not* ignore that signal.
   2432   1.1  christos 		 */
   2433  1.28  christos 		ssh_signal(SIGINT, SIG_IGN);
   2434  1.28  christos 		ssh_signal(SIGTERM, SIG_DFL);
   2435   1.1  christos 		execvp(path, args);
   2436   1.1  christos 		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
   2437   1.1  christos 		_exit(1);
   2438   1.1  christos 	}
   2439   1.1  christos 
   2440  1.28  christos 	ssh_signal(SIGTERM, killchild);
   2441  1.28  christos 	ssh_signal(SIGINT, killchild);
   2442  1.28  christos 	ssh_signal(SIGHUP, killchild);
   2443  1.28  christos 	ssh_signal(SIGTSTP, suspchild);
   2444  1.28  christos 	ssh_signal(SIGTTIN, suspchild);
   2445  1.28  christos 	ssh_signal(SIGTTOU, suspchild);
   2446  1.28  christos 	ssh_signal(SIGCHLD, sigchld_handler);
   2447   1.1  christos 	close(c_in);
   2448   1.1  christos 	close(c_out);
   2449   1.1  christos }
   2450   1.1  christos 
   2451   1.8     joerg __dead static void
   2452   1.1  christos usage(void)
   2453   1.1  christos {
   2454   1.1  christos 	extern char *__progname;
   2455   1.1  christos 
   2456   1.1  christos 	fprintf(stderr,
   2457  1.30  christos 	    "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
   2458  1.36  christos 	    "          [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
   2459  1.26  christos 	    "          [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
   2460  1.26  christos 	    "          [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
   2461  1.37  christos 	    "          [-X sftp_option] destination\n",
   2462  1.23  christos 	    __progname);
   2463   1.1  christos 	exit(1);
   2464   1.1  christos }
   2465   1.1  christos 
   2466   1.1  christos int
   2467   1.1  christos main(int argc, char **argv)
   2468   1.1  christos {
   2469  1.36  christos 	int r, in, out, ch, err, tmp, port = -1, noisy = 0;
   2470  1.36  christos 	char *host = NULL, *user, *cp, **cpp, *file1 = NULL, *file2 = NULL;
   2471  1.29  christos 	int debug_level = 0;
   2472  1.31  christos 	const char *sftp_server = NULL;
   2473   1.7  christos 	const char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
   2474   1.7  christos 	const char *errstr;
   2475   1.1  christos 	LogLevel ll = SYSLOG_LEVEL_INFO;
   2476   1.1  christos 	arglist args;
   2477   1.1  christos 	extern int optind;
   2478   1.1  christos 	extern char *optarg;
   2479   1.4      adam 	struct sftp_conn *conn;
   2480  1.32  christos 	size_t copy_buffer_len = 0;
   2481  1.32  christos 	size_t num_requests = 0;
   2482  1.37  christos 	long long llv, limit_kbps = 0;
   2483   1.1  christos 
   2484   1.1  christos 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
   2485   1.1  christos 	sanitise_stdfd();
   2486  1.12  christos 	setlocale(LC_CTYPE, "");
   2487   1.1  christos 
   2488   1.1  christos 	memset(&args, '\0', sizeof(args));
   2489   1.1  christos 	args.list = NULL;
   2490   1.1  christos 	addargs(&args, "%s", ssh_program);
   2491   1.1  christos 	addargs(&args, "-oForwardX11 no");
   2492   1.1  christos 	addargs(&args, "-oPermitLocalCommand no");
   2493   1.1  christos 	addargs(&args, "-oClearAllForwardings yes");
   2494  1.42  christos 	addargs(&args, "-oControlMaster no");
   2495   1.1  christos 
   2496   1.1  christos 	ll = SYSLOG_LEVEL_INFO;
   2497   1.1  christos 	infile = stdin;
   2498   1.1  christos 
   2499   1.4      adam 	while ((ch = getopt(argc, argv,
   2500  1.37  christos 	    "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) {
   2501   1.1  christos 		switch (ch) {
   2502   1.4      adam 		/* Passed through to ssh(1) */
   2503  1.30  christos 		case 'A':
   2504   1.4      adam 		case '4':
   2505   1.4      adam 		case '6':
   2506   1.1  christos 		case 'C':
   2507   1.4      adam 			addargs(&args, "-%c", ch);
   2508   1.4      adam 			break;
   2509   1.4      adam 		/* Passed through to ssh(1) with argument */
   2510   1.4      adam 		case 'F':
   2511  1.26  christos 		case 'J':
   2512   1.4      adam 		case 'c':
   2513   1.4      adam 		case 'i':
   2514   1.4      adam 		case 'o':
   2515   1.4      adam 			addargs(&args, "-%c", ch);
   2516   1.4      adam 			addargs(&args, "%s", optarg);
   2517   1.4      adam 			break;
   2518   1.4      adam 		case 'q':
   2519  1.12  christos 			ll = SYSLOG_LEVEL_ERROR;
   2520  1.12  christos 			quiet = 1;
   2521   1.4      adam 			showprogress = 0;
   2522   1.4      adam 			addargs(&args, "-%c", ch);
   2523   1.4      adam 			break;
   2524   1.4      adam 		case 'P':
   2525  1.23  christos 			port = a2port(optarg);
   2526  1.23  christos 			if (port <= 0)
   2527  1.23  christos 				fatal("Bad port \"%s\"\n", optarg);
   2528   1.1  christos 			break;
   2529   1.1  christos 		case 'v':
   2530   1.1  christos 			if (debug_level < 3) {
   2531   1.1  christos 				addargs(&args, "-v");
   2532   1.1  christos 				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
   2533   1.1  christos 			}
   2534   1.1  christos 			debug_level++;
   2535   1.1  christos 			break;
   2536   1.1  christos 		case '1':
   2537  1.29  christos 			fatal("SSH protocol v.1 is no longer supported");
   2538   1.1  christos 			break;
   2539   1.4      adam 		case '2':
   2540  1.29  christos 			/* accept silently */
   2541   1.1  christos 			break;
   2542  1.12  christos 		case 'a':
   2543  1.12  christos 			global_aflag = 1;
   2544  1.12  christos 			break;
   2545   1.4      adam 		case 'B':
   2546   1.4      adam 			copy_buffer_len = strtol(optarg, &cp, 10);
   2547   1.4      adam 			if (copy_buffer_len == 0 || *cp != '\0')
   2548   1.4      adam 				fatal("Invalid buffer size \"%s\"", optarg);
   2549   1.1  christos 			break;
   2550   1.1  christos 		case 'b':
   2551   1.1  christos 			if (batchmode)
   2552   1.1  christos 				fatal("Batch file already specified.");
   2553   1.1  christos 
   2554   1.1  christos 			/* Allow "-" as stdin */
   2555   1.1  christos 			if (strcmp(optarg, "-") != 0 &&
   2556   1.1  christos 			    (infile = fopen(optarg, "r")) == NULL)
   2557   1.1  christos 				fatal("%s (%s).", strerror(errno), optarg);
   2558   1.1  christos 			showprogress = 0;
   2559  1.12  christos 			quiet = batchmode = 1;
   2560   1.1  christos 			addargs(&args, "-obatchmode yes");
   2561   1.1  christos 			break;
   2562  1.13  christos 		case 'f':
   2563  1.13  christos 			global_fflag = 1;
   2564  1.13  christos 			break;
   2565  1.29  christos 		case 'N':
   2566  1.29  christos 			noisy = 1; /* Used to clear quiet mode after getopt */
   2567  1.29  christos 			break;
   2568   1.4      adam 		case 'p':
   2569   1.4      adam 			global_pflag = 1;
   2570   1.4      adam 			break;
   2571   1.4      adam 		case 'D':
   2572   1.1  christos 			sftp_direct = optarg;
   2573   1.1  christos 			break;
   2574   1.7  christos 		case 'l':
   2575   1.7  christos 			limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
   2576   1.7  christos 			    &errstr);
   2577   1.7  christos 			if (errstr != NULL)
   2578   1.7  christos 				usage();
   2579   1.7  christos 			limit_kbps *= 1024; /* kbps */
   2580   1.7  christos 			break;
   2581   1.4      adam 		case 'r':
   2582   1.4      adam 			global_rflag = 1;
   2583   1.1  christos 			break;
   2584   1.1  christos 		case 'R':
   2585   1.1  christos 			num_requests = strtol(optarg, &cp, 10);
   2586   1.1  christos 			if (num_requests == 0 || *cp != '\0')
   2587   1.1  christos 				fatal("Invalid number of requests \"%s\"",
   2588   1.1  christos 				    optarg);
   2589   1.1  christos 			break;
   2590   1.4      adam 		case 's':
   2591   1.4      adam 			sftp_server = optarg;
   2592   1.4      adam 			break;
   2593   1.4      adam 		case 'S':
   2594   1.4      adam 			ssh_program = optarg;
   2595   1.4      adam 			replacearg(&args, 0, "%s", ssh_program);
   2596   1.4      adam 			break;
   2597  1.37  christos 		case 'X':
   2598  1.37  christos 			/* Please keep in sync with ssh.c -X */
   2599  1.37  christos 			if (strncmp(optarg, "buffer=", 7) == 0) {
   2600  1.37  christos 				r = scan_scaled(optarg + 7, &llv);
   2601  1.37  christos 				if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
   2602  1.37  christos 					r = -1;
   2603  1.37  christos 					errno = EINVAL;
   2604  1.37  christos 				}
   2605  1.37  christos 				if (r == -1) {
   2606  1.37  christos 					fatal("Invalid buffer size \"%s\": %s",
   2607  1.37  christos 					     optarg + 7, strerror(errno));
   2608  1.37  christos 				}
   2609  1.37  christos 				copy_buffer_len = (size_t)llv;
   2610  1.37  christos 			} else if (strncmp(optarg, "nrequests=", 10) == 0) {
   2611  1.37  christos 				llv = strtonum(optarg + 10, 1, 256 * 1024,
   2612  1.37  christos 				    &errstr);
   2613  1.37  christos 				if (errstr != NULL) {
   2614  1.37  christos 					fatal("Invalid number of requests "
   2615  1.37  christos 					    "\"%s\": %s", optarg + 10, errstr);
   2616  1.37  christos 				}
   2617  1.37  christos 				num_requests = (size_t)llv;
   2618  1.37  christos 			} else {
   2619  1.37  christos 				fatal("Invalid -X option");
   2620  1.37  christos 			}
   2621  1.37  christos 			break;
   2622   1.1  christos 		case 'h':
   2623   1.1  christos 		default:
   2624   1.1  christos 			usage();
   2625   1.1  christos 		}
   2626   1.1  christos 	}
   2627   1.1  christos 
   2628  1.30  christos 	/* Do this last because we want the user to be able to override it */
   2629  1.30  christos 	addargs(&args, "-oForwardAgent no");
   2630  1.30  christos 
   2631   1.1  christos 	if (!isatty(STDERR_FILENO))
   2632   1.1  christos 		showprogress = 0;
   2633   1.1  christos 
   2634  1.29  christos 	if (noisy)
   2635  1.29  christos 		quiet = 0;
   2636  1.29  christos 
   2637   1.1  christos 	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
   2638   1.1  christos 
   2639   1.1  christos 	if (sftp_direct == NULL) {
   2640   1.1  christos 		if (optind == argc || argc > (optind + 2))
   2641   1.1  christos 			usage();
   2642  1.23  christos 		argv += optind;
   2643   1.1  christos 
   2644  1.23  christos 		switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
   2645  1.23  christos 		case -1:
   2646  1.23  christos 			usage();
   2647  1.23  christos 			break;
   2648  1.23  christos 		case 0:
   2649  1.23  christos 			if (tmp != -1)
   2650  1.23  christos 				port = tmp;
   2651  1.23  christos 			break;
   2652  1.23  christos 		default:
   2653  1.27  christos 			/* Try with user, host and path. */
   2654  1.23  christos 			if (parse_user_host_path(*argv, &user, &host,
   2655  1.27  christos 			    &file1) == 0)
   2656  1.27  christos 				break;
   2657  1.27  christos 			/* Try with user and host. */
   2658  1.27  christos 			if (parse_user_host_port(*argv, &user, &host, NULL)
   2659  1.27  christos 			    == 0)
   2660  1.27  christos 				break;
   2661  1.27  christos 			/* Treat as a plain hostname. */
   2662  1.27  christos 			host = xstrdup(*argv);
   2663  1.27  christos 			host = cleanhostname(host);
   2664  1.23  christos 			break;
   2665   1.1  christos 		}
   2666  1.23  christos 		file2 = *(argv + 1);
   2667   1.1  christos 
   2668   1.1  christos 		if (!*host) {
   2669   1.1  christos 			fprintf(stderr, "Missing hostname\n");
   2670   1.1  christos 			usage();
   2671   1.1  christos 		}
   2672   1.1  christos 
   2673  1.23  christos 		if (port != -1)
   2674  1.23  christos 			addargs(&args, "-oPort %d", port);
   2675  1.23  christos 		if (user != NULL) {
   2676  1.23  christos 			addargs(&args, "-l");
   2677  1.23  christos 			addargs(&args, "%s", user);
   2678  1.23  christos 		}
   2679   1.1  christos 
   2680   1.1  christos 		/* no subsystem if the server-spec contains a '/' */
   2681   1.1  christos 		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
   2682   1.1  christos 			addargs(&args, "-s");
   2683   1.1  christos 
   2684   1.4      adam 		addargs(&args, "--");
   2685   1.1  christos 		addargs(&args, "%s", host);
   2686   1.1  christos 		addargs(&args, "%s", (sftp_server != NULL ?
   2687   1.1  christos 		    sftp_server : "sftp"));
   2688   1.1  christos 
   2689   1.1  christos 		connect_to_server(ssh_program, args.list, &in, &out);
   2690   1.1  christos 	} else {
   2691  1.36  christos 		if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
   2692  1.36  christos 			fatal_r(r, "Parse -D arguments");
   2693  1.43  christos 		if (cpp[0] == NULL)
   2694  1.36  christos 			fatal("No sftp server specified via -D");
   2695  1.36  christos 		connect_to_server(cpp[0], cpp, &in, &out);
   2696  1.36  christos 		argv_free(cpp, tmp);
   2697   1.1  christos 	}
   2698   1.1  christos 	freeargs(&args);
   2699   1.1  christos 
   2700  1.39  christos 	conn = sftp_init(in, out, copy_buffer_len, num_requests, limit_kbps);
   2701   1.4      adam 	if (conn == NULL)
   2702   1.4      adam 		fatal("Couldn't initialise connection to server");
   2703   1.4      adam 
   2704  1.12  christos 	if (!quiet) {
   2705   1.4      adam 		if (sftp_direct == NULL)
   2706   1.4      adam 			fprintf(stderr, "Connected to %s.\n", host);
   2707   1.4      adam 		else
   2708   1.4      adam 			fprintf(stderr, "Attached to %s.\n", sftp_direct);
   2709   1.4      adam 	}
   2710   1.4      adam 
   2711   1.4      adam 	err = interactive_loop(conn, file1, file2);
   2712   1.1  christos 
   2713   1.1  christos 	close(in);
   2714   1.1  christos 	close(out);
   2715   1.1  christos 	if (batchmode)
   2716   1.1  christos 		fclose(infile);
   2717   1.1  christos 
   2718  1.24  christos 	while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
   2719   1.1  christos 		if (errno != EINTR)
   2720   1.1  christos 			fatal("Couldn't wait for ssh process: %s",
   2721   1.1  christos 			    strerror(errno));
   2722   1.1  christos 
   2723   1.1  christos 	exit(err == 0 ? 0 : 1);
   2724   1.1  christos }
   2725