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