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