xref: /freebsd/crypto/openssh/sftp.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /*
2  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "includes.h"
18 
19 RCSID("$OpenBSD: sftp.c,v 1.70 2006/01/31 10:19:02 djm Exp $");
20 
21 #ifdef USE_LIBEDIT
22 #include <histedit.h>
23 #else
24 typedef void EditLine;
25 #endif
26 
27 #include "buffer.h"
28 #include "xmalloc.h"
29 #include "log.h"
30 #include "pathnames.h"
31 #include "misc.h"
32 
33 #include "sftp.h"
34 #include "sftp-common.h"
35 #include "sftp-client.h"
36 
37 /* File to read commands from */
38 FILE* infile;
39 
40 /* Are we in batchfile mode? */
41 int batchmode = 0;
42 
43 /* Size of buffer used when copying files */
44 size_t copy_buffer_len = 32768;
45 
46 /* Number of concurrent outstanding requests */
47 size_t num_requests = 16;
48 
49 /* PID of ssh transport process */
50 static pid_t sshpid = -1;
51 
52 /* This is set to 0 if the progressmeter is not desired. */
53 int showprogress = 1;
54 
55 /* SIGINT received during command processing */
56 volatile sig_atomic_t interrupted = 0;
57 
58 /* I wish qsort() took a separate ctx for the comparison function...*/
59 int sort_flag;
60 
61 int remote_glob(struct sftp_conn *, const char *, int,
62     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
63 
64 extern char *__progname;
65 
66 /* Separators for interactive commands */
67 #define WHITESPACE " \t\r\n"
68 
69 /* ls flags */
70 #define LS_LONG_VIEW	0x01	/* Full view ala ls -l */
71 #define LS_SHORT_VIEW	0x02	/* Single row view ala ls -1 */
72 #define LS_NUMERIC_VIEW	0x04	/* Long view with numeric uid/gid */
73 #define LS_NAME_SORT	0x08	/* Sort by name (default) */
74 #define LS_TIME_SORT	0x10	/* Sort by mtime */
75 #define LS_SIZE_SORT	0x20	/* Sort by file size */
76 #define LS_REVERSE_SORT	0x40	/* Reverse sort order */
77 #define LS_SHOW_ALL	0x80	/* Don't skip filenames starting with '.' */
78 
79 #define VIEW_FLAGS	(LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
80 #define SORT_FLAGS	(LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
81 
82 /* Commands for interactive mode */
83 #define I_CHDIR		1
84 #define I_CHGRP		2
85 #define I_CHMOD		3
86 #define I_CHOWN		4
87 #define I_GET		5
88 #define I_HELP		6
89 #define I_LCHDIR	7
90 #define I_LLS		8
91 #define I_LMKDIR	9
92 #define I_LPWD		10
93 #define I_LS		11
94 #define I_LUMASK	12
95 #define I_MKDIR		13
96 #define I_PUT		14
97 #define I_PWD		15
98 #define I_QUIT		16
99 #define I_RENAME	17
100 #define I_RM		18
101 #define I_RMDIR		19
102 #define I_SHELL		20
103 #define I_SYMLINK	21
104 #define I_VERSION	22
105 #define I_PROGRESS	23
106 
107 struct CMD {
108 	const char *c;
109 	const int n;
110 };
111 
112 static const struct CMD cmds[] = {
113 	{ "bye",	I_QUIT },
114 	{ "cd",		I_CHDIR },
115 	{ "chdir",	I_CHDIR },
116 	{ "chgrp",	I_CHGRP },
117 	{ "chmod",	I_CHMOD },
118 	{ "chown",	I_CHOWN },
119 	{ "dir",	I_LS },
120 	{ "exit",	I_QUIT },
121 	{ "get",	I_GET },
122 	{ "mget",	I_GET },
123 	{ "help",	I_HELP },
124 	{ "lcd",	I_LCHDIR },
125 	{ "lchdir",	I_LCHDIR },
126 	{ "lls",	I_LLS },
127 	{ "lmkdir",	I_LMKDIR },
128 	{ "ln",		I_SYMLINK },
129 	{ "lpwd",	I_LPWD },
130 	{ "ls",		I_LS },
131 	{ "lumask",	I_LUMASK },
132 	{ "mkdir",	I_MKDIR },
133 	{ "progress",	I_PROGRESS },
134 	{ "put",	I_PUT },
135 	{ "mput",	I_PUT },
136 	{ "pwd",	I_PWD },
137 	{ "quit",	I_QUIT },
138 	{ "rename",	I_RENAME },
139 	{ "rm",		I_RM },
140 	{ "rmdir",	I_RMDIR },
141 	{ "symlink",	I_SYMLINK },
142 	{ "version",	I_VERSION },
143 	{ "!",		I_SHELL },
144 	{ "?",		I_HELP },
145 	{ NULL,			-1}
146 };
147 
148 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
149 
150 static void
151 killchild(int signo)
152 {
153 	if (sshpid > 1) {
154 		kill(sshpid, SIGTERM);
155 		waitpid(sshpid, NULL, 0);
156 	}
157 
158 	_exit(1);
159 }
160 
161 static void
162 cmd_interrupt(int signo)
163 {
164 	const char msg[] = "\rInterrupt  \n";
165 	int olderrno = errno;
166 
167 	write(STDERR_FILENO, msg, sizeof(msg) - 1);
168 	interrupted = 1;
169 	errno = olderrno;
170 }
171 
172 static void
173 help(void)
174 {
175 	printf("Available commands:\n");
176 	printf("cd path                       Change remote directory to 'path'\n");
177 	printf("lcd path                      Change local directory to 'path'\n");
178 	printf("chgrp grp path                Change group of file 'path' to 'grp'\n");
179 	printf("chmod mode path               Change permissions of file 'path' to 'mode'\n");
180 	printf("chown own path                Change owner of file 'path' to 'own'\n");
181 	printf("help                          Display this help text\n");
182 	printf("get remote-path [local-path]  Download file\n");
183 	printf("lls [ls-options [path]]       Display local directory listing\n");
184 	printf("ln oldpath newpath            Symlink remote file\n");
185 	printf("lmkdir path                   Create local directory\n");
186 	printf("lpwd                          Print local working directory\n");
187 	printf("ls [path]                     Display remote directory listing\n");
188 	printf("lumask umask                  Set local umask to 'umask'\n");
189 	printf("mkdir path                    Create remote directory\n");
190 	printf("progress                      Toggle display of progress meter\n");
191 	printf("put local-path [remote-path]  Upload file\n");
192 	printf("pwd                           Display remote working directory\n");
193 	printf("exit                          Quit sftp\n");
194 	printf("quit                          Quit sftp\n");
195 	printf("rename oldpath newpath        Rename remote file\n");
196 	printf("rmdir path                    Remove remote directory\n");
197 	printf("rm path                       Delete remote file\n");
198 	printf("symlink oldpath newpath       Symlink remote file\n");
199 	printf("version                       Show SFTP version\n");
200 	printf("!command                      Execute 'command' in local shell\n");
201 	printf("!                             Escape to local shell\n");
202 	printf("?                             Synonym for help\n");
203 }
204 
205 static void
206 local_do_shell(const char *args)
207 {
208 	int status;
209 	char *shell;
210 	pid_t pid;
211 
212 	if (!*args)
213 		args = NULL;
214 
215 	if ((shell = getenv("SHELL")) == NULL)
216 		shell = _PATH_BSHELL;
217 
218 	if ((pid = fork()) == -1)
219 		fatal("Couldn't fork: %s", strerror(errno));
220 
221 	if (pid == 0) {
222 		/* XXX: child has pipe fds to ssh subproc open - issue? */
223 		if (args) {
224 			debug3("Executing %s -c \"%s\"", shell, args);
225 			execl(shell, shell, "-c", args, (char *)NULL);
226 		} else {
227 			debug3("Executing %s", shell);
228 			execl(shell, shell, (char *)NULL);
229 		}
230 		fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
231 		    strerror(errno));
232 		_exit(1);
233 	}
234 	while (waitpid(pid, &status, 0) == -1)
235 		if (errno != EINTR)
236 			fatal("Couldn't wait for child: %s", strerror(errno));
237 	if (!WIFEXITED(status))
238 		error("Shell exited abormally");
239 	else if (WEXITSTATUS(status))
240 		error("Shell exited with status %d", WEXITSTATUS(status));
241 }
242 
243 static void
244 local_do_ls(const char *args)
245 {
246 	if (!args || !*args)
247 		local_do_shell(_PATH_LS);
248 	else {
249 		int len = strlen(_PATH_LS " ") + strlen(args) + 1;
250 		char *buf = xmalloc(len);
251 
252 		/* XXX: quoting - rip quoting code from ftp? */
253 		snprintf(buf, len, _PATH_LS " %s", args);
254 		local_do_shell(buf);
255 		xfree(buf);
256 	}
257 }
258 
259 /* Strip one path (usually the pwd) from the start of another */
260 static char *
261 path_strip(char *path, char *strip)
262 {
263 	size_t len;
264 
265 	if (strip == NULL)
266 		return (xstrdup(path));
267 
268 	len = strlen(strip);
269 	if (strncmp(path, strip, len) == 0) {
270 		if (strip[len - 1] != '/' && path[len] == '/')
271 			len++;
272 		return (xstrdup(path + len));
273 	}
274 
275 	return (xstrdup(path));
276 }
277 
278 static char *
279 path_append(char *p1, char *p2)
280 {
281 	char *ret;
282 	int len = strlen(p1) + strlen(p2) + 2;
283 
284 	ret = xmalloc(len);
285 	strlcpy(ret, p1, len);
286 	if (p1[strlen(p1) - 1] != '/')
287 		strlcat(ret, "/", len);
288 	strlcat(ret, p2, len);
289 
290 	return(ret);
291 }
292 
293 static char *
294 make_absolute(char *p, char *pwd)
295 {
296 	char *abs_str;
297 
298 	/* Derelativise */
299 	if (p && p[0] != '/') {
300 		abs_str = path_append(pwd, p);
301 		xfree(p);
302 		return(abs_str);
303 	} else
304 		return(p);
305 }
306 
307 static int
308 infer_path(const char *p, char **ifp)
309 {
310 	char *cp;
311 
312 	cp = strrchr(p, '/');
313 	if (cp == NULL) {
314 		*ifp = xstrdup(p);
315 		return(0);
316 	}
317 
318 	if (!cp[1]) {
319 		error("Invalid path");
320 		return(-1);
321 	}
322 
323 	*ifp = xstrdup(cp + 1);
324 	return(0);
325 }
326 
327 static int
328 parse_getput_flags(const char **cpp, int *pflag)
329 {
330 	const char *cp = *cpp;
331 
332 	/* Check for flags */
333 	if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
334 		switch (cp[1]) {
335 		case 'p':
336 		case 'P':
337 			*pflag = 1;
338 			break;
339 		default:
340 			error("Invalid flag -%c", cp[1]);
341 			return(-1);
342 		}
343 		cp += 2;
344 		*cpp = cp + strspn(cp, WHITESPACE);
345 	}
346 
347 	return(0);
348 }
349 
350 static int
351 parse_ls_flags(const char **cpp, int *lflag)
352 {
353 	const char *cp = *cpp;
354 
355 	/* Defaults */
356 	*lflag = LS_NAME_SORT;
357 
358 	/* Check for flags */
359 	if (cp++[0] == '-') {
360 		for (; strchr(WHITESPACE, *cp) == NULL; cp++) {
361 			switch (*cp) {
362 			case 'l':
363 				*lflag &= ~VIEW_FLAGS;
364 				*lflag |= LS_LONG_VIEW;
365 				break;
366 			case '1':
367 				*lflag &= ~VIEW_FLAGS;
368 				*lflag |= LS_SHORT_VIEW;
369 				break;
370 			case 'n':
371 				*lflag &= ~VIEW_FLAGS;
372 				*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
373 				break;
374 			case 'S':
375 				*lflag &= ~SORT_FLAGS;
376 				*lflag |= LS_SIZE_SORT;
377 				break;
378 			case 't':
379 				*lflag &= ~SORT_FLAGS;
380 				*lflag |= LS_TIME_SORT;
381 				break;
382 			case 'r':
383 				*lflag |= LS_REVERSE_SORT;
384 				break;
385 			case 'f':
386 				*lflag &= ~SORT_FLAGS;
387 				break;
388 			case 'a':
389 				*lflag |= LS_SHOW_ALL;
390 				break;
391 			default:
392 				error("Invalid flag -%c", *cp);
393 				return(-1);
394 			}
395 		}
396 		*cpp = cp + strspn(cp, WHITESPACE);
397 	}
398 
399 	return(0);
400 }
401 
402 static int
403 get_pathname(const char **cpp, char **path)
404 {
405 	const char *cp = *cpp, *end;
406 	char quot;
407 	u_int i, j;
408 
409 	cp += strspn(cp, WHITESPACE);
410 	if (!*cp) {
411 		*cpp = cp;
412 		*path = NULL;
413 		return (0);
414 	}
415 
416 	*path = xmalloc(strlen(cp) + 1);
417 
418 	/* Check for quoted filenames */
419 	if (*cp == '\"' || *cp == '\'') {
420 		quot = *cp++;
421 
422 		/* Search for terminating quote, unescape some chars */
423 		for (i = j = 0; i <= strlen(cp); i++) {
424 			if (cp[i] == quot) {	/* Found quote */
425 				i++;
426 				(*path)[j] = '\0';
427 				break;
428 			}
429 			if (cp[i] == '\0') {	/* End of string */
430 				error("Unterminated quote");
431 				goto fail;
432 			}
433 			if (cp[i] == '\\') {	/* Escaped characters */
434 				i++;
435 				if (cp[i] != '\'' && cp[i] != '\"' &&
436 				    cp[i] != '\\') {
437 					error("Bad escaped character '\\%c'",
438 					    cp[i]);
439 					goto fail;
440 				}
441 			}
442 			(*path)[j++] = cp[i];
443 		}
444 
445 		if (j == 0) {
446 			error("Empty quotes");
447 			goto fail;
448 		}
449 		*cpp = cp + i + strspn(cp + i, WHITESPACE);
450 	} else {
451 		/* Read to end of filename */
452 		end = strpbrk(cp, WHITESPACE);
453 		if (end == NULL)
454 			end = strchr(cp, '\0');
455 		*cpp = end + strspn(end, WHITESPACE);
456 
457 		memcpy(*path, cp, end - cp);
458 		(*path)[end - cp] = '\0';
459 	}
460 	return (0);
461 
462  fail:
463 	xfree(*path);
464 	*path = NULL;
465 	return (-1);
466 }
467 
468 static int
469 is_dir(char *path)
470 {
471 	struct stat sb;
472 
473 	/* XXX: report errors? */
474 	if (stat(path, &sb) == -1)
475 		return(0);
476 
477 	return(sb.st_mode & S_IFDIR);
478 }
479 
480 static int
481 is_reg(char *path)
482 {
483 	struct stat sb;
484 
485 	if (stat(path, &sb) == -1)
486 		fatal("stat %s: %s", path, strerror(errno));
487 
488 	return(S_ISREG(sb.st_mode));
489 }
490 
491 static int
492 remote_is_dir(struct sftp_conn *conn, char *path)
493 {
494 	Attrib *a;
495 
496 	/* XXX: report errors? */
497 	if ((a = do_stat(conn, path, 1)) == NULL)
498 		return(0);
499 	if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
500 		return(0);
501 	return(a->perm & S_IFDIR);
502 }
503 
504 static int
505 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
506 {
507 	char *abs_src = NULL;
508 	char *abs_dst = NULL;
509 	char *tmp;
510 	glob_t g;
511 	int err = 0;
512 	int i;
513 
514 	abs_src = xstrdup(src);
515 	abs_src = make_absolute(abs_src, pwd);
516 
517 	memset(&g, 0, sizeof(g));
518 	debug3("Looking up %s", abs_src);
519 	if (remote_glob(conn, abs_src, 0, NULL, &g)) {
520 		error("File \"%s\" not found.", abs_src);
521 		err = -1;
522 		goto out;
523 	}
524 
525 	/* If multiple matches, dst must be a directory or unspecified */
526 	if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
527 		error("Multiple files match, but \"%s\" is not a directory",
528 		    dst);
529 		err = -1;
530 		goto out;
531 	}
532 
533 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
534 		if (infer_path(g.gl_pathv[i], &tmp)) {
535 			err = -1;
536 			goto out;
537 		}
538 
539 		if (g.gl_matchc == 1 && dst) {
540 			/* If directory specified, append filename */
541 			if (is_dir(dst)) {
542 				if (infer_path(g.gl_pathv[0], &tmp)) {
543 					err = 1;
544 					goto out;
545 				}
546 				abs_dst = path_append(dst, tmp);
547 				xfree(tmp);
548 			} else
549 				abs_dst = xstrdup(dst);
550 		} else if (dst) {
551 			abs_dst = path_append(dst, tmp);
552 			xfree(tmp);
553 		} else
554 			abs_dst = tmp;
555 
556 		printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
557 		if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
558 			err = -1;
559 		xfree(abs_dst);
560 		abs_dst = NULL;
561 	}
562 
563 out:
564 	xfree(abs_src);
565 	if (abs_dst)
566 		xfree(abs_dst);
567 	globfree(&g);
568 	return(err);
569 }
570 
571 static int
572 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
573 {
574 	char *tmp_dst = NULL;
575 	char *abs_dst = NULL;
576 	char *tmp;
577 	glob_t g;
578 	int err = 0;
579 	int i;
580 
581 	if (dst) {
582 		tmp_dst = xstrdup(dst);
583 		tmp_dst = make_absolute(tmp_dst, pwd);
584 	}
585 
586 	memset(&g, 0, sizeof(g));
587 	debug3("Looking up %s", src);
588 	if (glob(src, 0, NULL, &g)) {
589 		error("File \"%s\" not found.", src);
590 		err = -1;
591 		goto out;
592 	}
593 
594 	/* If multiple matches, dst may be directory or unspecified */
595 	if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
596 		error("Multiple files match, but \"%s\" is not a directory",
597 		    tmp_dst);
598 		err = -1;
599 		goto out;
600 	}
601 
602 	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
603 		if (!is_reg(g.gl_pathv[i])) {
604 			error("skipping non-regular file %s",
605 			    g.gl_pathv[i]);
606 			continue;
607 		}
608 		if (infer_path(g.gl_pathv[i], &tmp)) {
609 			err = -1;
610 			goto out;
611 		}
612 
613 		if (g.gl_matchc == 1 && tmp_dst) {
614 			/* If directory specified, append filename */
615 			if (remote_is_dir(conn, tmp_dst)) {
616 				if (infer_path(g.gl_pathv[0], &tmp)) {
617 					err = 1;
618 					goto out;
619 				}
620 				abs_dst = path_append(tmp_dst, tmp);
621 				xfree(tmp);
622 			} else
623 				abs_dst = xstrdup(tmp_dst);
624 
625 		} else if (tmp_dst) {
626 			abs_dst = path_append(tmp_dst, tmp);
627 			xfree(tmp);
628 		} else
629 			abs_dst = make_absolute(tmp, pwd);
630 
631 		printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
632 		if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
633 			err = -1;
634 	}
635 
636 out:
637 	if (abs_dst)
638 		xfree(abs_dst);
639 	if (tmp_dst)
640 		xfree(tmp_dst);
641 	globfree(&g);
642 	return(err);
643 }
644 
645 static int
646 sdirent_comp(const void *aa, const void *bb)
647 {
648 	SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
649 	SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
650 	int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
651 
652 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
653 	if (sort_flag & LS_NAME_SORT)
654 		return (rmul * strcmp(a->filename, b->filename));
655 	else if (sort_flag & LS_TIME_SORT)
656 		return (rmul * NCMP(a->a.mtime, b->a.mtime));
657 	else if (sort_flag & LS_SIZE_SORT)
658 		return (rmul * NCMP(a->a.size, b->a.size));
659 
660 	fatal("Unknown ls sort type");
661 }
662 
663 /* sftp ls.1 replacement for directories */
664 static int
665 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
666 {
667 	int n;
668 	u_int c = 1, colspace = 0, columns = 1;
669 	SFTP_DIRENT **d;
670 
671 	if ((n = do_readdir(conn, path, &d)) != 0)
672 		return (n);
673 
674 	if (!(lflag & LS_SHORT_VIEW)) {
675 		u_int m = 0, width = 80;
676 		struct winsize ws;
677 		char *tmp;
678 
679 		/* Count entries for sort and find longest filename */
680 		for (n = 0; d[n] != NULL; n++) {
681 			if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
682 				m = MAX(m, strlen(d[n]->filename));
683 		}
684 
685 		/* Add any subpath that also needs to be counted */
686 		tmp = path_strip(path, strip_path);
687 		m += strlen(tmp);
688 		xfree(tmp);
689 
690 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
691 			width = ws.ws_col;
692 
693 		columns = width / (m + 2);
694 		columns = MAX(columns, 1);
695 		colspace = width / columns;
696 		colspace = MIN(colspace, width);
697 	}
698 
699 	if (lflag & SORT_FLAGS) {
700 		for (n = 0; d[n] != NULL; n++)
701 			;	/* count entries */
702 		sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
703 		qsort(d, n, sizeof(*d), sdirent_comp);
704 	}
705 
706 	for (n = 0; d[n] != NULL && !interrupted; n++) {
707 		char *tmp, *fname;
708 
709 		if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
710 			continue;
711 
712 		tmp = path_append(path, d[n]->filename);
713 		fname = path_strip(tmp, strip_path);
714 		xfree(tmp);
715 
716 		if (lflag & LS_LONG_VIEW) {
717 			if (lflag & LS_NUMERIC_VIEW) {
718 				char *lname;
719 				struct stat sb;
720 
721 				memset(&sb, 0, sizeof(sb));
722 				attrib_to_stat(&d[n]->a, &sb);
723 				lname = ls_file(fname, &sb, 1);
724 				printf("%s\n", lname);
725 				xfree(lname);
726 			} else
727 				printf("%s\n", d[n]->longname);
728 		} else {
729 			printf("%-*s", colspace, fname);
730 			if (c >= columns) {
731 				printf("\n");
732 				c = 1;
733 			} else
734 				c++;
735 		}
736 
737 		xfree(fname);
738 	}
739 
740 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
741 		printf("\n");
742 
743 	free_sftp_dirents(d);
744 	return (0);
745 }
746 
747 /* sftp ls.1 replacement which handles path globs */
748 static int
749 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
750     int lflag)
751 {
752 	glob_t g;
753 	u_int i, c = 1, colspace = 0, columns = 1;
754 	Attrib *a = NULL;
755 
756 	memset(&g, 0, sizeof(g));
757 
758 	if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
759 	    NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
760 		if (g.gl_pathc)
761 			globfree(&g);
762 		error("Can't ls: \"%s\" not found", path);
763 		return (-1);
764 	}
765 
766 	if (interrupted)
767 		goto out;
768 
769 	/*
770 	 * If the glob returns a single match and it is a directory,
771 	 * then just list its contents.
772 	 */
773 	if (g.gl_matchc == 1) {
774 		if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
775 			globfree(&g);
776 			return (-1);
777 		}
778 		if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
779 		    S_ISDIR(a->perm)) {
780 			int err;
781 
782 			err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
783 			globfree(&g);
784 			return (err);
785 		}
786 	}
787 
788 	if (!(lflag & LS_SHORT_VIEW)) {
789 		u_int m = 0, width = 80;
790 		struct winsize ws;
791 
792 		/* Count entries for sort and find longest filename */
793 		for (i = 0; g.gl_pathv[i]; i++)
794 			m = MAX(m, strlen(g.gl_pathv[i]));
795 
796 		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
797 			width = ws.ws_col;
798 
799 		columns = width / (m + 2);
800 		columns = MAX(columns, 1);
801 		colspace = width / columns;
802 	}
803 
804 	for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
805 		char *fname;
806 
807 		fname = path_strip(g.gl_pathv[i], strip_path);
808 
809 		if (lflag & LS_LONG_VIEW) {
810 			char *lname;
811 			struct stat sb;
812 
813 			/*
814 			 * XXX: this is slow - 1 roundtrip per path
815 			 * A solution to this is to fork glob() and
816 			 * build a sftp specific version which keeps the
817 			 * attribs (which currently get thrown away)
818 			 * that the server returns as well as the filenames.
819 			 */
820 			memset(&sb, 0, sizeof(sb));
821 			if (a == NULL)
822 				a = do_lstat(conn, g.gl_pathv[i], 1);
823 			if (a != NULL)
824 				attrib_to_stat(a, &sb);
825 			lname = ls_file(fname, &sb, 1);
826 			printf("%s\n", lname);
827 			xfree(lname);
828 		} else {
829 			printf("%-*s", colspace, fname);
830 			if (c >= columns) {
831 				printf("\n");
832 				c = 1;
833 			} else
834 				c++;
835 		}
836 		xfree(fname);
837 	}
838 
839 	if (!(lflag & LS_LONG_VIEW) && (c != 1))
840 		printf("\n");
841 
842  out:
843 	if (g.gl_pathc)
844 		globfree(&g);
845 
846 	return (0);
847 }
848 
849 static int
850 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
851     unsigned long *n_arg, char **path1, char **path2)
852 {
853 	const char *cmd, *cp = *cpp;
854 	char *cp2;
855 	int base = 0;
856 	long l;
857 	int i, cmdnum;
858 
859 	/* Skip leading whitespace */
860 	cp = cp + strspn(cp, WHITESPACE);
861 
862 	/* Ignore blank lines and lines which begin with comment '#' char */
863 	if (*cp == '\0' || *cp == '#')
864 		return (0);
865 
866 	/* Check for leading '-' (disable error processing) */
867 	*iflag = 0;
868 	if (*cp == '-') {
869 		*iflag = 1;
870 		cp++;
871 	}
872 
873 	/* Figure out which command we have */
874 	for (i = 0; cmds[i].c; i++) {
875 		int cmdlen = strlen(cmds[i].c);
876 
877 		/* Check for command followed by whitespace */
878 		if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
879 		    strchr(WHITESPACE, cp[cmdlen])) {
880 			cp += cmdlen;
881 			cp = cp + strspn(cp, WHITESPACE);
882 			break;
883 		}
884 	}
885 	cmdnum = cmds[i].n;
886 	cmd = cmds[i].c;
887 
888 	/* Special case */
889 	if (*cp == '!') {
890 		cp++;
891 		cmdnum = I_SHELL;
892 	} else if (cmdnum == -1) {
893 		error("Invalid command.");
894 		return (-1);
895 	}
896 
897 	/* Get arguments and parse flags */
898 	*lflag = *pflag = *n_arg = 0;
899 	*path1 = *path2 = NULL;
900 	switch (cmdnum) {
901 	case I_GET:
902 	case I_PUT:
903 		if (parse_getput_flags(&cp, pflag))
904 			return(-1);
905 		/* Get first pathname (mandatory) */
906 		if (get_pathname(&cp, path1))
907 			return(-1);
908 		if (*path1 == NULL) {
909 			error("You must specify at least one path after a "
910 			    "%s command.", cmd);
911 			return(-1);
912 		}
913 		/* Try to get second pathname (optional) */
914 		if (get_pathname(&cp, path2))
915 			return(-1);
916 		break;
917 	case I_RENAME:
918 	case I_SYMLINK:
919 		if (get_pathname(&cp, path1))
920 			return(-1);
921 		if (get_pathname(&cp, path2))
922 			return(-1);
923 		if (!*path1 || !*path2) {
924 			error("You must specify two paths after a %s "
925 			    "command.", cmd);
926 			return(-1);
927 		}
928 		break;
929 	case I_RM:
930 	case I_MKDIR:
931 	case I_RMDIR:
932 	case I_CHDIR:
933 	case I_LCHDIR:
934 	case I_LMKDIR:
935 		/* Get pathname (mandatory) */
936 		if (get_pathname(&cp, path1))
937 			return(-1);
938 		if (*path1 == NULL) {
939 			error("You must specify a path after a %s command.",
940 			    cmd);
941 			return(-1);
942 		}
943 		break;
944 	case I_LS:
945 		if (parse_ls_flags(&cp, lflag))
946 			return(-1);
947 		/* Path is optional */
948 		if (get_pathname(&cp, path1))
949 			return(-1);
950 		break;
951 	case I_LLS:
952 	case I_SHELL:
953 		/* Uses the rest of the line */
954 		break;
955 	case I_LUMASK:
956 		base = 8;
957 	case I_CHMOD:
958 		base = 8;
959 	case I_CHOWN:
960 	case I_CHGRP:
961 		/* Get numeric arg (mandatory) */
962 		l = strtol(cp, &cp2, base);
963 		if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
964 		    errno == ERANGE) || l < 0) {
965 			error("You must supply a numeric argument "
966 			    "to the %s command.", cmd);
967 			return(-1);
968 		}
969 		cp = cp2;
970 		*n_arg = l;
971 		if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
972 			break;
973 		if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
974 			error("You must supply a numeric argument "
975 			    "to the %s command.", cmd);
976 			return(-1);
977 		}
978 		cp += strspn(cp, WHITESPACE);
979 
980 		/* Get pathname (mandatory) */
981 		if (get_pathname(&cp, path1))
982 			return(-1);
983 		if (*path1 == NULL) {
984 			error("You must specify a path after a %s command.",
985 			    cmd);
986 			return(-1);
987 		}
988 		break;
989 	case I_QUIT:
990 	case I_PWD:
991 	case I_LPWD:
992 	case I_HELP:
993 	case I_VERSION:
994 	case I_PROGRESS:
995 		break;
996 	default:
997 		fatal("Command not implemented");
998 	}
999 
1000 	*cpp = cp;
1001 	return(cmdnum);
1002 }
1003 
1004 static int
1005 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1006     int err_abort)
1007 {
1008 	char *path1, *path2, *tmp;
1009 	int pflag, lflag, iflag, cmdnum, i;
1010 	unsigned long n_arg;
1011 	Attrib a, *aa;
1012 	char path_buf[MAXPATHLEN];
1013 	int err = 0;
1014 	glob_t g;
1015 
1016 	path1 = path2 = NULL;
1017 	cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg,
1018 	    &path1, &path2);
1019 
1020 	if (iflag != 0)
1021 		err_abort = 0;
1022 
1023 	memset(&g, 0, sizeof(g));
1024 
1025 	/* Perform command */
1026 	switch (cmdnum) {
1027 	case 0:
1028 		/* Blank line */
1029 		break;
1030 	case -1:
1031 		/* Unrecognized command */
1032 		err = -1;
1033 		break;
1034 	case I_GET:
1035 		err = process_get(conn, path1, path2, *pwd, pflag);
1036 		break;
1037 	case I_PUT:
1038 		err = process_put(conn, path1, path2, *pwd, pflag);
1039 		break;
1040 	case I_RENAME:
1041 		path1 = make_absolute(path1, *pwd);
1042 		path2 = make_absolute(path2, *pwd);
1043 		err = do_rename(conn, path1, path2);
1044 		break;
1045 	case I_SYMLINK:
1046 		path2 = make_absolute(path2, *pwd);
1047 		err = do_symlink(conn, path1, path2);
1048 		break;
1049 	case I_RM:
1050 		path1 = make_absolute(path1, *pwd);
1051 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1052 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1053 			printf("Removing %s\n", g.gl_pathv[i]);
1054 			err = do_rm(conn, g.gl_pathv[i]);
1055 			if (err != 0 && err_abort)
1056 				break;
1057 		}
1058 		break;
1059 	case I_MKDIR:
1060 		path1 = make_absolute(path1, *pwd);
1061 		attrib_clear(&a);
1062 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1063 		a.perm = 0777;
1064 		err = do_mkdir(conn, path1, &a);
1065 		break;
1066 	case I_RMDIR:
1067 		path1 = make_absolute(path1, *pwd);
1068 		err = do_rmdir(conn, path1);
1069 		break;
1070 	case I_CHDIR:
1071 		path1 = make_absolute(path1, *pwd);
1072 		if ((tmp = do_realpath(conn, path1)) == NULL) {
1073 			err = 1;
1074 			break;
1075 		}
1076 		if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1077 			xfree(tmp);
1078 			err = 1;
1079 			break;
1080 		}
1081 		if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1082 			error("Can't change directory: Can't check target");
1083 			xfree(tmp);
1084 			err = 1;
1085 			break;
1086 		}
1087 		if (!S_ISDIR(aa->perm)) {
1088 			error("Can't change directory: \"%s\" is not "
1089 			    "a directory", tmp);
1090 			xfree(tmp);
1091 			err = 1;
1092 			break;
1093 		}
1094 		xfree(*pwd);
1095 		*pwd = tmp;
1096 		break;
1097 	case I_LS:
1098 		if (!path1) {
1099 			do_globbed_ls(conn, *pwd, *pwd, lflag);
1100 			break;
1101 		}
1102 
1103 		/* Strip pwd off beginning of non-absolute paths */
1104 		tmp = NULL;
1105 		if (*path1 != '/')
1106 			tmp = *pwd;
1107 
1108 		path1 = make_absolute(path1, *pwd);
1109 		err = do_globbed_ls(conn, path1, tmp, lflag);
1110 		break;
1111 	case I_LCHDIR:
1112 		if (chdir(path1) == -1) {
1113 			error("Couldn't change local directory to "
1114 			    "\"%s\": %s", path1, strerror(errno));
1115 			err = 1;
1116 		}
1117 		break;
1118 	case I_LMKDIR:
1119 		if (mkdir(path1, 0777) == -1) {
1120 			error("Couldn't create local directory "
1121 			    "\"%s\": %s", path1, strerror(errno));
1122 			err = 1;
1123 		}
1124 		break;
1125 	case I_LLS:
1126 		local_do_ls(cmd);
1127 		break;
1128 	case I_SHELL:
1129 		local_do_shell(cmd);
1130 		break;
1131 	case I_LUMASK:
1132 		umask(n_arg);
1133 		printf("Local umask: %03lo\n", n_arg);
1134 		break;
1135 	case I_CHMOD:
1136 		path1 = make_absolute(path1, *pwd);
1137 		attrib_clear(&a);
1138 		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1139 		a.perm = n_arg;
1140 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1141 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1142 			printf("Changing mode on %s\n", g.gl_pathv[i]);
1143 			err = do_setstat(conn, g.gl_pathv[i], &a);
1144 			if (err != 0 && err_abort)
1145 				break;
1146 		}
1147 		break;
1148 	case I_CHOWN:
1149 	case I_CHGRP:
1150 		path1 = make_absolute(path1, *pwd);
1151 		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1152 		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1153 			if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1154 				if (err != 0 && err_abort)
1155 					break;
1156 				else
1157 					continue;
1158 			}
1159 			if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1160 				error("Can't get current ownership of "
1161 				    "remote file \"%s\"", g.gl_pathv[i]);
1162 				if (err != 0 && err_abort)
1163 					break;
1164 				else
1165 					continue;
1166 			}
1167 			aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1168 			if (cmdnum == I_CHOWN) {
1169 				printf("Changing owner on %s\n", g.gl_pathv[i]);
1170 				aa->uid = n_arg;
1171 			} else {
1172 				printf("Changing group on %s\n", g.gl_pathv[i]);
1173 				aa->gid = n_arg;
1174 			}
1175 			err = do_setstat(conn, g.gl_pathv[i], aa);
1176 			if (err != 0 && err_abort)
1177 				break;
1178 		}
1179 		break;
1180 	case I_PWD:
1181 		printf("Remote working directory: %s\n", *pwd);
1182 		break;
1183 	case I_LPWD:
1184 		if (!getcwd(path_buf, sizeof(path_buf))) {
1185 			error("Couldn't get local cwd: %s", strerror(errno));
1186 			err = -1;
1187 			break;
1188 		}
1189 		printf("Local working directory: %s\n", path_buf);
1190 		break;
1191 	case I_QUIT:
1192 		/* Processed below */
1193 		break;
1194 	case I_HELP:
1195 		help();
1196 		break;
1197 	case I_VERSION:
1198 		printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1199 		break;
1200 	case I_PROGRESS:
1201 		showprogress = !showprogress;
1202 		if (showprogress)
1203 			printf("Progress meter enabled\n");
1204 		else
1205 			printf("Progress meter disabled\n");
1206 		break;
1207 	default:
1208 		fatal("%d is not implemented", cmdnum);
1209 	}
1210 
1211 	if (g.gl_pathc)
1212 		globfree(&g);
1213 	if (path1)
1214 		xfree(path1);
1215 	if (path2)
1216 		xfree(path2);
1217 
1218 	/* If an unignored error occurs in batch mode we should abort. */
1219 	if (err_abort && err != 0)
1220 		return (-1);
1221 	else if (cmdnum == I_QUIT)
1222 		return (1);
1223 
1224 	return (0);
1225 }
1226 
1227 #ifdef USE_LIBEDIT
1228 static char *
1229 prompt(EditLine *el)
1230 {
1231 	return ("sftp> ");
1232 }
1233 #endif
1234 
1235 int
1236 interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1237 {
1238 	char *pwd;
1239 	char *dir = NULL;
1240 	char cmd[2048];
1241 	struct sftp_conn *conn;
1242 	int err, interactive;
1243 	EditLine *el = NULL;
1244 #ifdef USE_LIBEDIT
1245 	History *hl = NULL;
1246 	HistEvent hev;
1247 	extern char *__progname;
1248 
1249 	if (!batchmode && isatty(STDIN_FILENO)) {
1250 		if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1251 			fatal("Couldn't initialise editline");
1252 		if ((hl = history_init()) == NULL)
1253 			fatal("Couldn't initialise editline history");
1254 		history(hl, &hev, H_SETSIZE, 100);
1255 		el_set(el, EL_HIST, history, hl);
1256 
1257 		el_set(el, EL_PROMPT, prompt);
1258 		el_set(el, EL_EDITOR, "emacs");
1259 		el_set(el, EL_TERMINAL, NULL);
1260 		el_set(el, EL_SIGNAL, 1);
1261 		el_source(el, NULL);
1262 	}
1263 #endif /* USE_LIBEDIT */
1264 
1265 	conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1266 	if (conn == NULL)
1267 		fatal("Couldn't initialise connection to server");
1268 
1269 	pwd = do_realpath(conn, ".");
1270 	if (pwd == NULL)
1271 		fatal("Need cwd");
1272 
1273 	if (file1 != NULL) {
1274 		dir = xstrdup(file1);
1275 		dir = make_absolute(dir, pwd);
1276 
1277 		if (remote_is_dir(conn, dir) && file2 == NULL) {
1278 			printf("Changing to: %s\n", dir);
1279 			snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1280 			if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1281 				xfree(dir);
1282 				xfree(pwd);
1283 				return (-1);
1284 			}
1285 		} else {
1286 			if (file2 == NULL)
1287 				snprintf(cmd, sizeof cmd, "get %s", dir);
1288 			else
1289 				snprintf(cmd, sizeof cmd, "get %s %s", dir,
1290 				    file2);
1291 
1292 			err = parse_dispatch_command(conn, cmd, &pwd, 1);
1293 			xfree(dir);
1294 			xfree(pwd);
1295 			return (err);
1296 		}
1297 		xfree(dir);
1298 	}
1299 
1300 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1301 	setvbuf(stdout, NULL, _IOLBF, 0);
1302 	setvbuf(infile, NULL, _IOLBF, 0);
1303 #else
1304 	setlinebuf(stdout);
1305 	setlinebuf(infile);
1306 #endif
1307 
1308 	interactive = !batchmode && isatty(STDIN_FILENO);
1309 	err = 0;
1310 	for (;;) {
1311 		char *cp;
1312 
1313 		signal(SIGINT, SIG_IGN);
1314 
1315 		if (el == NULL) {
1316 			if (interactive)
1317 				printf("sftp> ");
1318 			if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1319 				if (interactive)
1320 					printf("\n");
1321 				break;
1322 			}
1323 			if (!interactive) { /* Echo command */
1324 				printf("sftp> %s", cmd);
1325 				if (strlen(cmd) > 0 &&
1326 				    cmd[strlen(cmd) - 1] != '\n')
1327 					printf("\n");
1328 			}
1329 		} else {
1330 #ifdef USE_LIBEDIT
1331 			const char *line;
1332 			int count = 0;
1333 
1334 			if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1335 				printf("\n");
1336  				break;
1337 			}
1338 			history(hl, &hev, H_ENTER, line);
1339 			if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1340 				fprintf(stderr, "Error: input line too long\n");
1341 				continue;
1342 			}
1343 #endif /* USE_LIBEDIT */
1344 		}
1345 
1346 		cp = strrchr(cmd, '\n');
1347 		if (cp)
1348 			*cp = '\0';
1349 
1350 		/* Handle user interrupts gracefully during commands */
1351 		interrupted = 0;
1352 		signal(SIGINT, cmd_interrupt);
1353 
1354 		err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1355 		if (err != 0)
1356 			break;
1357 	}
1358 	xfree(pwd);
1359 
1360 #ifdef USE_LIBEDIT
1361 	if (el != NULL)
1362 		el_end(el);
1363 #endif /* USE_LIBEDIT */
1364 
1365 	/* err == 1 signifies normal "quit" exit */
1366 	return (err >= 0 ? 0 : -1);
1367 }
1368 
1369 static void
1370 connect_to_server(char *path, char **args, int *in, int *out)
1371 {
1372 	int c_in, c_out;
1373 
1374 #ifdef USE_PIPES
1375 	int pin[2], pout[2];
1376 
1377 	if ((pipe(pin) == -1) || (pipe(pout) == -1))
1378 		fatal("pipe: %s", strerror(errno));
1379 	*in = pin[0];
1380 	*out = pout[1];
1381 	c_in = pout[0];
1382 	c_out = pin[1];
1383 #else /* USE_PIPES */
1384 	int inout[2];
1385 
1386 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1387 		fatal("socketpair: %s", strerror(errno));
1388 	*in = *out = inout[0];
1389 	c_in = c_out = inout[1];
1390 #endif /* USE_PIPES */
1391 
1392 	if ((sshpid = fork()) == -1)
1393 		fatal("fork: %s", strerror(errno));
1394 	else if (sshpid == 0) {
1395 		if ((dup2(c_in, STDIN_FILENO) == -1) ||
1396 		    (dup2(c_out, STDOUT_FILENO) == -1)) {
1397 			fprintf(stderr, "dup2: %s\n", strerror(errno));
1398 			_exit(1);
1399 		}
1400 		close(*in);
1401 		close(*out);
1402 		close(c_in);
1403 		close(c_out);
1404 
1405 		/*
1406 		 * The underlying ssh is in the same process group, so we must
1407 		 * ignore SIGINT if we want to gracefully abort commands,
1408 		 * otherwise the signal will make it to the ssh process and
1409 		 * kill it too
1410 		 */
1411 		signal(SIGINT, SIG_IGN);
1412 		execvp(path, args);
1413 		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1414 		_exit(1);
1415 	}
1416 
1417 	signal(SIGTERM, killchild);
1418 	signal(SIGINT, killchild);
1419 	signal(SIGHUP, killchild);
1420 	close(c_in);
1421 	close(c_out);
1422 }
1423 
1424 static void
1425 usage(void)
1426 {
1427 	extern char *__progname;
1428 
1429 	fprintf(stderr,
1430 	    "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
1431 	    "            [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
1432 	    "            [-S program] [-s subsystem | sftp_server] host\n"
1433 	    "       %s [[user@]host[:file [file]]]\n"
1434 	    "       %s [[user@]host[:dir[/]]]\n"
1435 	    "       %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
1436 	exit(1);
1437 }
1438 
1439 int
1440 main(int argc, char **argv)
1441 {
1442 	int in, out, ch, err;
1443 	char *host, *userhost, *cp, *file2 = NULL;
1444 	int debug_level = 0, sshver = 2;
1445 	char *file1 = NULL, *sftp_server = NULL;
1446 	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1447 	LogLevel ll = SYSLOG_LEVEL_INFO;
1448 	arglist args;
1449 	extern int optind;
1450 	extern char *optarg;
1451 
1452 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1453 	sanitise_stdfd();
1454 
1455 	__progname = ssh_get_progname(argv[0]);
1456 	memset(&args, '\0', sizeof(args));
1457 	args.list = NULL;
1458 	addargs(&args, ssh_program);
1459 	addargs(&args, "-oForwardX11 no");
1460 	addargs(&args, "-oForwardAgent no");
1461 	addargs(&args, "-oPermitLocalCommand no");
1462 	addargs(&args, "-oClearAllForwardings yes");
1463 
1464 	ll = SYSLOG_LEVEL_INFO;
1465 	infile = stdin;
1466 
1467 	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
1468 		switch (ch) {
1469 		case 'C':
1470 			addargs(&args, "-C");
1471 			break;
1472 		case 'v':
1473 			if (debug_level < 3) {
1474 				addargs(&args, "-v");
1475 				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1476 			}
1477 			debug_level++;
1478 			break;
1479 		case 'F':
1480 		case 'o':
1481 			addargs(&args, "-%c%s", ch, optarg);
1482 			break;
1483 		case '1':
1484 			sshver = 1;
1485 			if (sftp_server == NULL)
1486 				sftp_server = _PATH_SFTP_SERVER;
1487 			break;
1488 		case 's':
1489 			sftp_server = optarg;
1490 			break;
1491 		case 'S':
1492 			ssh_program = optarg;
1493 			replacearg(&args, 0, "%s", ssh_program);
1494 			break;
1495 		case 'b':
1496 			if (batchmode)
1497 				fatal("Batch file already specified.");
1498 
1499 			/* Allow "-" as stdin */
1500 			if (strcmp(optarg, "-") != 0 &&
1501 			    (infile = fopen(optarg, "r")) == NULL)
1502 				fatal("%s (%s).", strerror(errno), optarg);
1503 			showprogress = 0;
1504 			batchmode = 1;
1505 			addargs(&args, "-obatchmode yes");
1506 			break;
1507 		case 'P':
1508 			sftp_direct = optarg;
1509 			break;
1510 		case 'B':
1511 			copy_buffer_len = strtol(optarg, &cp, 10);
1512 			if (copy_buffer_len == 0 || *cp != '\0')
1513 				fatal("Invalid buffer size \"%s\"", optarg);
1514 			break;
1515 		case 'R':
1516 			num_requests = strtol(optarg, &cp, 10);
1517 			if (num_requests == 0 || *cp != '\0')
1518 				fatal("Invalid number of requests \"%s\"",
1519 				    optarg);
1520 			break;
1521 		case 'h':
1522 		default:
1523 			usage();
1524 		}
1525 	}
1526 
1527 	if (!isatty(STDERR_FILENO))
1528 		showprogress = 0;
1529 
1530 	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1531 
1532 	if (sftp_direct == NULL) {
1533 		if (optind == argc || argc > (optind + 2))
1534 			usage();
1535 
1536 		userhost = xstrdup(argv[optind]);
1537 		file2 = argv[optind+1];
1538 
1539 		if ((host = strrchr(userhost, '@')) == NULL)
1540 			host = userhost;
1541 		else {
1542 			*host++ = '\0';
1543 			if (!userhost[0]) {
1544 				fprintf(stderr, "Missing username\n");
1545 				usage();
1546 			}
1547 			addargs(&args, "-l%s",userhost);
1548 		}
1549 
1550 		if ((cp = colon(host)) != NULL) {
1551 			*cp++ = '\0';
1552 			file1 = cp;
1553 		}
1554 
1555 		host = cleanhostname(host);
1556 		if (!*host) {
1557 			fprintf(stderr, "Missing hostname\n");
1558 			usage();
1559 		}
1560 
1561 		addargs(&args, "-oProtocol %d", sshver);
1562 
1563 		/* no subsystem if the server-spec contains a '/' */
1564 		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1565 			addargs(&args, "-s");
1566 
1567 		addargs(&args, "%s", host);
1568 		addargs(&args, "%s", (sftp_server != NULL ?
1569 		    sftp_server : "sftp"));
1570 
1571 		if (!batchmode)
1572 			fprintf(stderr, "Connecting to %s...\n", host);
1573 		connect_to_server(ssh_program, args.list, &in, &out);
1574 	} else {
1575 		args.list = NULL;
1576 		addargs(&args, "sftp-server");
1577 
1578 		if (!batchmode)
1579 			fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1580 		connect_to_server(sftp_direct, args.list, &in, &out);
1581 	}
1582 	freeargs(&args);
1583 
1584 	err = interactive_loop(in, out, file1, file2);
1585 
1586 #if !defined(USE_PIPES)
1587 	shutdown(in, SHUT_RDWR);
1588 	shutdown(out, SHUT_RDWR);
1589 #endif
1590 
1591 	close(in);
1592 	close(out);
1593 	if (batchmode)
1594 		fclose(infile);
1595 
1596 	while (waitpid(sshpid, NULL, 0) == -1)
1597 		if (errno != EINTR)
1598 			fatal("Couldn't wait for ssh process: %s",
1599 			    strerror(errno));
1600 
1601 	exit(err == 0 ? 0 : 1);
1602 }
1603