xref: /plan9/sys/src/cmd/ssh1/scp.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
1*63afb9a5SDavid du Colombier #include <u.h>
2*63afb9a5SDavid du Colombier #include <libc.h>
3*63afb9a5SDavid du Colombier #include <ctype.h>
4*63afb9a5SDavid du Colombier 
5*63afb9a5SDavid du Colombier int
isatty(int fd)6*63afb9a5SDavid du Colombier isatty(int fd)
7*63afb9a5SDavid du Colombier {
8*63afb9a5SDavid du Colombier 	char buf[64];
9*63afb9a5SDavid du Colombier 
10*63afb9a5SDavid du Colombier 	buf[0] = '\0';
11*63afb9a5SDavid du Colombier 	fd2path(fd, buf, sizeof buf);
12*63afb9a5SDavid du Colombier 	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
13*63afb9a5SDavid du Colombier 		return 1;
14*63afb9a5SDavid du Colombier 	return 0;
15*63afb9a5SDavid du Colombier }
16*63afb9a5SDavid du Colombier 
17*63afb9a5SDavid du Colombier #define	OK	0x00
18*63afb9a5SDavid du Colombier #define	ERROR	0x01
19*63afb9a5SDavid du Colombier #define	FATAL	0x02
20*63afb9a5SDavid du Colombier 
21*63afb9a5SDavid du Colombier char	*progname;
22*63afb9a5SDavid du Colombier 
23*63afb9a5SDavid du Colombier int	dflag;
24*63afb9a5SDavid du Colombier int	fflag;
25*63afb9a5SDavid du Colombier int	iflag;
26*63afb9a5SDavid du Colombier int	pflag;
27*63afb9a5SDavid du Colombier int	rflag;
28*63afb9a5SDavid du Colombier int	tflag;
29*63afb9a5SDavid du Colombier int	vflag;
30*63afb9a5SDavid du Colombier 
31*63afb9a5SDavid du Colombier int	remote;
32*63afb9a5SDavid du Colombier 
33*63afb9a5SDavid du Colombier char	*exitflag = nil;
34*63afb9a5SDavid du Colombier 
35*63afb9a5SDavid du Colombier void	scperror(int, char*, ...);
36*63afb9a5SDavid du Colombier void	mustbedir(char*);
37*63afb9a5SDavid du Colombier void	receive(char*);
38*63afb9a5SDavid du Colombier char	*fileaftercolon(char*);
39*63afb9a5SDavid du Colombier void	destislocal(char *cmd, int argc, char *argv[], char *dest);
40*63afb9a5SDavid du Colombier void	destisremote(char *cmd, int argc, char *argv[], char *host, char *dest);
41*63afb9a5SDavid du Colombier int	remotessh(char *host, char *cmd);
42*63afb9a5SDavid du Colombier void	send(char*);
43*63afb9a5SDavid du Colombier void	senddir(char*, int, Dir*);
44*63afb9a5SDavid du Colombier int 	getresponse(void);
45*63afb9a5SDavid du Colombier 
46*63afb9a5SDavid du Colombier char	theuser[32];
47*63afb9a5SDavid du Colombier 
48*63afb9a5SDavid du Colombier char	ssh[] = "/bin/ssh";
49*63afb9a5SDavid du Colombier 
50*63afb9a5SDavid du Colombier int	remotefd0;
51*63afb9a5SDavid du Colombier int	remotefd1;
52*63afb9a5SDavid du Colombier 
53*63afb9a5SDavid du Colombier int
runcommand(char * cmd)54*63afb9a5SDavid du Colombier runcommand(char *cmd)
55*63afb9a5SDavid du Colombier {
56*63afb9a5SDavid du Colombier 	Waitmsg *w;
57*63afb9a5SDavid du Colombier 	int pid;
58*63afb9a5SDavid du Colombier 	char *argv[4];
59*63afb9a5SDavid du Colombier 
60*63afb9a5SDavid du Colombier 	if (cmd == nil)
61*63afb9a5SDavid du Colombier 		return -1;
62*63afb9a5SDavid du Colombier 	switch(pid = fork()){
63*63afb9a5SDavid du Colombier 	case -1:
64*63afb9a5SDavid du Colombier 		return -1;
65*63afb9a5SDavid du Colombier 	case 0:
66*63afb9a5SDavid du Colombier 		argv[0] = "rc";
67*63afb9a5SDavid du Colombier 		argv[1] = "-c";
68*63afb9a5SDavid du Colombier 		argv[2] = cmd;
69*63afb9a5SDavid du Colombier 		argv[3] = nil;
70*63afb9a5SDavid du Colombier 		exec("/bin/rc", argv);
71*63afb9a5SDavid du Colombier 		exits("exec failed");
72*63afb9a5SDavid du Colombier 	}
73*63afb9a5SDavid du Colombier 	for(;;){
74*63afb9a5SDavid du Colombier 		w = wait();
75*63afb9a5SDavid du Colombier 		if(w == nil)
76*63afb9a5SDavid du Colombier 			return -1;
77*63afb9a5SDavid du Colombier 		if(w->pid == pid)
78*63afb9a5SDavid du Colombier 			break;
79*63afb9a5SDavid du Colombier 		free(w);
80*63afb9a5SDavid du Colombier 	}
81*63afb9a5SDavid du Colombier 	if(w->msg[0]){
82*63afb9a5SDavid du Colombier 		free(w);
83*63afb9a5SDavid du Colombier 		return -1;
84*63afb9a5SDavid du Colombier 	}
85*63afb9a5SDavid du Colombier 	free(w);
86*63afb9a5SDavid du Colombier 	return 1;
87*63afb9a5SDavid du Colombier }
88*63afb9a5SDavid du Colombier 
89*63afb9a5SDavid du Colombier void
vprint(char * fmt,...)90*63afb9a5SDavid du Colombier vprint(char *fmt, ...)
91*63afb9a5SDavid du Colombier {
92*63afb9a5SDavid du Colombier 	char buf[1024];
93*63afb9a5SDavid du Colombier 	va_list arg;
94*63afb9a5SDavid du Colombier 	static char *name;
95*63afb9a5SDavid du Colombier 
96*63afb9a5SDavid du Colombier 	if(vflag == 0)
97*63afb9a5SDavid du Colombier 		return;
98*63afb9a5SDavid du Colombier 
99*63afb9a5SDavid du Colombier 	va_start(arg, fmt);
100*63afb9a5SDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
101*63afb9a5SDavid du Colombier 	va_end(arg);
102*63afb9a5SDavid du Colombier 
103*63afb9a5SDavid du Colombier 	if(name == nil){
104*63afb9a5SDavid du Colombier 		name = sysname();
105*63afb9a5SDavid du Colombier 		if(name == nil)
106*63afb9a5SDavid du Colombier 			name = "<unknown>";
107*63afb9a5SDavid du Colombier 	}
108*63afb9a5SDavid du Colombier 	fprint(2, "%s: %s\n", name, buf);
109*63afb9a5SDavid du Colombier }
110*63afb9a5SDavid du Colombier 
111*63afb9a5SDavid du Colombier void
usage(void)112*63afb9a5SDavid du Colombier usage(void)
113*63afb9a5SDavid du Colombier {
114*63afb9a5SDavid du Colombier 	fprint(2, "Usage: scp [-Iidfprtv] source ... destination\n");
115*63afb9a5SDavid du Colombier 	exits("usage");
116*63afb9a5SDavid du Colombier }
117*63afb9a5SDavid du Colombier 
118*63afb9a5SDavid du Colombier 
119*63afb9a5SDavid du Colombier #pragma	varargck	type	"F"	int
120*63afb9a5SDavid du Colombier #pragma	varargck	type	"V"	char*
121*63afb9a5SDavid du Colombier static int flag;
122*63afb9a5SDavid du Colombier 
123*63afb9a5SDavid du Colombier /* flag: if integer flag, take following char *value */
124*63afb9a5SDavid du Colombier int
flagfmt(Fmt * f)125*63afb9a5SDavid du Colombier flagfmt(Fmt *f)
126*63afb9a5SDavid du Colombier {
127*63afb9a5SDavid du Colombier 	flag = va_arg(f->args, int);
128*63afb9a5SDavid du Colombier 	return 0;
129*63afb9a5SDavid du Colombier }
130*63afb9a5SDavid du Colombier 
131*63afb9a5SDavid du Colombier /* flag: if previous integer flag, take char *value */
132*63afb9a5SDavid du Colombier int
valfmt(Fmt * f)133*63afb9a5SDavid du Colombier valfmt(Fmt *f)
134*63afb9a5SDavid du Colombier {
135*63afb9a5SDavid du Colombier 	char *value;
136*63afb9a5SDavid du Colombier 
137*63afb9a5SDavid du Colombier 	value = va_arg(f->args, char*);
138*63afb9a5SDavid du Colombier 	if(flag)
139*63afb9a5SDavid du Colombier 		return fmtprint(f, " %s", value);
140*63afb9a5SDavid du Colombier 	return 0;
141*63afb9a5SDavid du Colombier }
142*63afb9a5SDavid du Colombier 
143*63afb9a5SDavid du Colombier void
sendokresponse(void)144*63afb9a5SDavid du Colombier sendokresponse(void)
145*63afb9a5SDavid du Colombier {
146*63afb9a5SDavid du Colombier 	char ok = OK;
147*63afb9a5SDavid du Colombier 
148*63afb9a5SDavid du Colombier 	write(remotefd1, &ok, 1);
149*63afb9a5SDavid du Colombier }
150*63afb9a5SDavid du Colombier 
151*63afb9a5SDavid du Colombier void
main(int argc,char * argv[])152*63afb9a5SDavid du Colombier main(int argc, char *argv[])
153*63afb9a5SDavid du Colombier {
154*63afb9a5SDavid du Colombier 	int i, fd;
155*63afb9a5SDavid du Colombier 	char cmd[32];
156*63afb9a5SDavid du Colombier 	char *p;
157*63afb9a5SDavid du Colombier 
158*63afb9a5SDavid du Colombier 	progname = argv[0];
159*63afb9a5SDavid du Colombier 	fmtinstall('F', flagfmt);
160*63afb9a5SDavid du Colombier 	fmtinstall('V', valfmt);
161*63afb9a5SDavid du Colombier 	iflag = -1;
162*63afb9a5SDavid du Colombier 
163*63afb9a5SDavid du Colombier 	ARGBEGIN {
164*63afb9a5SDavid du Colombier 	case 'I':
165*63afb9a5SDavid du Colombier 		iflag = 0;
166*63afb9a5SDavid du Colombier 		break;
167*63afb9a5SDavid du Colombier 	case 'i':
168*63afb9a5SDavid du Colombier 		iflag = 1;
169*63afb9a5SDavid du Colombier 		break;
170*63afb9a5SDavid du Colombier 	case 'd':
171*63afb9a5SDavid du Colombier 		dflag++;
172*63afb9a5SDavid du Colombier 		break;
173*63afb9a5SDavid du Colombier 	case 'f':
174*63afb9a5SDavid du Colombier 		fflag++;
175*63afb9a5SDavid du Colombier 		remote++;
176*63afb9a5SDavid du Colombier 		break;
177*63afb9a5SDavid du Colombier 	case 'p':
178*63afb9a5SDavid du Colombier 		pflag++;
179*63afb9a5SDavid du Colombier 		break;
180*63afb9a5SDavid du Colombier 	case 'r':
181*63afb9a5SDavid du Colombier 		rflag++;
182*63afb9a5SDavid du Colombier 		break;
183*63afb9a5SDavid du Colombier 	case 't':
184*63afb9a5SDavid du Colombier 		tflag++;
185*63afb9a5SDavid du Colombier 		remote++;
186*63afb9a5SDavid du Colombier 		break;
187*63afb9a5SDavid du Colombier 	case 'v':
188*63afb9a5SDavid du Colombier 		vflag++;
189*63afb9a5SDavid du Colombier 		break;
190*63afb9a5SDavid du Colombier 	default:
191*63afb9a5SDavid du Colombier 		scperror(1, "unknown option %c", ARGC());
192*63afb9a5SDavid du Colombier 	} ARGEND
193*63afb9a5SDavid du Colombier 
194*63afb9a5SDavid du Colombier 	if(iflag == -1)
195*63afb9a5SDavid du Colombier 		iflag = isatty(0);
196*63afb9a5SDavid du Colombier 
197*63afb9a5SDavid du Colombier 	remotefd0 = 0;
198*63afb9a5SDavid du Colombier 	remotefd1 = 1;
199*63afb9a5SDavid du Colombier 
200*63afb9a5SDavid du Colombier 	if(fflag){
201*63afb9a5SDavid du Colombier 		getresponse();
202*63afb9a5SDavid du Colombier 		for(i=0; i<argc; i++)
203*63afb9a5SDavid du Colombier 			send(argv[i]);
204*63afb9a5SDavid du Colombier 		exits(0);
205*63afb9a5SDavid du Colombier 	}
206*63afb9a5SDavid du Colombier 	if(tflag){
207*63afb9a5SDavid du Colombier 		if(argc != 1)
208*63afb9a5SDavid du Colombier 			usage();
209*63afb9a5SDavid du Colombier 		receive(argv[0]);
210*63afb9a5SDavid du Colombier 		exits(0);
211*63afb9a5SDavid du Colombier 	}
212*63afb9a5SDavid du Colombier 
213*63afb9a5SDavid du Colombier 	if (argc < 2)
214*63afb9a5SDavid du Colombier 		usage();
215*63afb9a5SDavid du Colombier 	if (argc > 2)
216*63afb9a5SDavid du Colombier 		dflag = 1;
217*63afb9a5SDavid du Colombier 
218*63afb9a5SDavid du Colombier 	i = 0;
219*63afb9a5SDavid du Colombier 	fd = open("/dev/user", OREAD);
220*63afb9a5SDavid du Colombier 	if(fd >= 0){
221*63afb9a5SDavid du Colombier 		i = read(fd, theuser, sizeof theuser - 1);
222*63afb9a5SDavid du Colombier 		close(fd);
223*63afb9a5SDavid du Colombier 	}
224*63afb9a5SDavid du Colombier 	if(i <= 0)
225*63afb9a5SDavid du Colombier 		scperror(1, "can't read /dev/user: %r");
226*63afb9a5SDavid du Colombier 
227*63afb9a5SDavid du Colombier 	remotefd0 = -1;
228*63afb9a5SDavid du Colombier 	remotefd1 = -1;
229*63afb9a5SDavid du Colombier 
230*63afb9a5SDavid du Colombier 	snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V",
231*63afb9a5SDavid du Colombier 		dflag, "-d",
232*63afb9a5SDavid du Colombier 		pflag, "-p",
233*63afb9a5SDavid du Colombier 		rflag, "-r",
234*63afb9a5SDavid du Colombier 		vflag, "-v");
235*63afb9a5SDavid du Colombier 
236*63afb9a5SDavid du Colombier 	p = fileaftercolon(argv[argc-1]);
237*63afb9a5SDavid du Colombier 	if(p != nil)	/* send to remote machine. */
238*63afb9a5SDavid du Colombier 		destisremote(cmd, argc-1, argv, argv[argc-1], p);
239*63afb9a5SDavid du Colombier 	else{
240*63afb9a5SDavid du Colombier 		if(dflag)
241*63afb9a5SDavid du Colombier 			mustbedir(argv[argc-1]);
242*63afb9a5SDavid du Colombier 		destislocal(cmd, argc-1, argv, argv[argc-1]);
243*63afb9a5SDavid du Colombier 	}
244*63afb9a5SDavid du Colombier 
245*63afb9a5SDavid du Colombier 	exits(exitflag);
246*63afb9a5SDavid du Colombier }
247*63afb9a5SDavid du Colombier 
248*63afb9a5SDavid du Colombier void
destislocal(char * cmd,int argc,char * argv[],char * dst)249*63afb9a5SDavid du Colombier destislocal(char *cmd, int argc, char *argv[], char *dst)
250*63afb9a5SDavid du Colombier {
251*63afb9a5SDavid du Colombier 	int i;
252*63afb9a5SDavid du Colombier 	char *src;
253*63afb9a5SDavid du Colombier 	char buf[4096];
254*63afb9a5SDavid du Colombier 
255*63afb9a5SDavid du Colombier 	for(i = 0; i<argc; i++){
256*63afb9a5SDavid du Colombier 		src = fileaftercolon(argv[i]);
257*63afb9a5SDavid du Colombier 		if(src == nil){
258*63afb9a5SDavid du Colombier 			/* local file; no network */
259*63afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "exec cp%F%V%F%V %s %s",
260*63afb9a5SDavid du Colombier 				rflag, "-r",
261*63afb9a5SDavid du Colombier 				pflag, "-p",
262*63afb9a5SDavid du Colombier 				argv[i], dst);
263*63afb9a5SDavid du Colombier 	  		vprint("remotetolocal: %s", buf);
264*63afb9a5SDavid du Colombier 			if(runcommand(buf) < 0)
265*63afb9a5SDavid du Colombier 				exitflag = "local cp exec";
266*63afb9a5SDavid du Colombier 		}else{
267*63afb9a5SDavid du Colombier 			/* remote file; use network */
268*63afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "%s -f %s", cmd, src);
269*63afb9a5SDavid du Colombier 		  	if(remotessh(argv[i], buf) < 0)
270*63afb9a5SDavid du Colombier 				exitflag = "remote ssh exec";
271*63afb9a5SDavid du Colombier 			else{
272*63afb9a5SDavid du Colombier 				receive(dst);
273*63afb9a5SDavid du Colombier 				close(remotefd0);
274*63afb9a5SDavid du Colombier 				remotefd0 = -1;
275*63afb9a5SDavid du Colombier 				remotefd1 = -1;
276*63afb9a5SDavid du Colombier 			}
277*63afb9a5SDavid du Colombier 		}
278*63afb9a5SDavid du Colombier 	}
279*63afb9a5SDavid du Colombier }
280*63afb9a5SDavid du Colombier 
281*63afb9a5SDavid du Colombier void
destisremote(char * cmd,int argc,char * argv[],char * host,char * dest)282*63afb9a5SDavid du Colombier destisremote(char *cmd, int argc, char *argv[], char *host, char *dest)
283*63afb9a5SDavid du Colombier {
284*63afb9a5SDavid du Colombier 	int i;
285*63afb9a5SDavid du Colombier 	char *src;
286*63afb9a5SDavid du Colombier 	char buf[4096];
287*63afb9a5SDavid du Colombier 
288*63afb9a5SDavid du Colombier 	for(i = 0; i < argc; i++){
289*63afb9a5SDavid du Colombier 		vprint("remote destination: send %s to %s:%s", argv[i], host, dest);
290*63afb9a5SDavid du Colombier 		/* destination is remote, but source may be local */
291*63afb9a5SDavid du Colombier 		src = fileaftercolon(argv[i]);
292*63afb9a5SDavid du Colombier 		if(src != nil){
293*63afb9a5SDavid du Colombier 			/* remote to remote */
294*63afb9a5SDavid du Colombier 			snprint(buf, sizeof buf, "exec %s%F%V%F%V %s %s %s '%s:%s'",
295*63afb9a5SDavid du Colombier 				ssh,
296*63afb9a5SDavid du Colombier 				iflag, " -i",
297*63afb9a5SDavid du Colombier 				vflag, "-v",
298*63afb9a5SDavid du Colombier 				argv[i], cmd, src,
299*63afb9a5SDavid du Colombier 				host, dest);
300*63afb9a5SDavid du Colombier 			vprint("localtoremote: %s", buf);
301*63afb9a5SDavid du Colombier 			runcommand(buf);
302*63afb9a5SDavid du Colombier 		}else{
303*63afb9a5SDavid du Colombier 			/* local to remote */
304*63afb9a5SDavid du Colombier 			if(remotefd0 == -1){
305*63afb9a5SDavid du Colombier 				snprint(buf, sizeof buf, "%s -t %s", cmd, dest);
306*63afb9a5SDavid du Colombier 				if(remotessh(host, buf) < 0)
307*63afb9a5SDavid du Colombier 					exits("remotessh");
308*63afb9a5SDavid du Colombier 				if(getresponse() < 0)
309*63afb9a5SDavid du Colombier 					exits("bad response");
310*63afb9a5SDavid du Colombier 			}
311*63afb9a5SDavid du Colombier 			send(argv[i]);
312*63afb9a5SDavid du Colombier 		}
313*63afb9a5SDavid du Colombier 	}
314*63afb9a5SDavid du Colombier }
315*63afb9a5SDavid du Colombier 
316*63afb9a5SDavid du Colombier void
readhdr(char * p,int n)317*63afb9a5SDavid du Colombier readhdr(char *p, int n)
318*63afb9a5SDavid du Colombier {
319*63afb9a5SDavid du Colombier 	int i;
320*63afb9a5SDavid du Colombier 
321*63afb9a5SDavid du Colombier 	for(i=0; i<n; i++){
322*63afb9a5SDavid du Colombier 		if(read(remotefd0, &p[i], 1) != 1)
323*63afb9a5SDavid du Colombier 			break;
324*63afb9a5SDavid du Colombier 		if(p[i] == '\n'){
325*63afb9a5SDavid du Colombier 			p[i] = '\0';
326*63afb9a5SDavid du Colombier 			return;
327*63afb9a5SDavid du Colombier 		}
328*63afb9a5SDavid du Colombier 	}
329*63afb9a5SDavid du Colombier 	/* if at beginning, this is regular EOF */
330*63afb9a5SDavid du Colombier 	if(i == 0)
331*63afb9a5SDavid du Colombier 		exits(nil);
332*63afb9a5SDavid du Colombier 	scperror(1, "read error on receive header: %r");
333*63afb9a5SDavid du Colombier }
334*63afb9a5SDavid du Colombier 
335*63afb9a5SDavid du Colombier Dir *
receivedir(char * dir,int exists,Dir * d,int settimes,ulong atime,ulong mtime,ulong mode)336*63afb9a5SDavid du Colombier receivedir(char *dir, int exists, Dir *d, int settimes, ulong atime, ulong mtime, ulong mode)
337*63afb9a5SDavid du Colombier {
338*63afb9a5SDavid du Colombier 	Dir nd;
339*63afb9a5SDavid du Colombier 	int setmodes;
340*63afb9a5SDavid du Colombier 	int fd;
341*63afb9a5SDavid du Colombier 
342*63afb9a5SDavid du Colombier 	setmodes = pflag;
343*63afb9a5SDavid du Colombier 	if(exists){
344*63afb9a5SDavid du Colombier 		if(!(d->qid.type & QTDIR)) {
345*63afb9a5SDavid du Colombier 			scperror(0, "%s: protocol botch: directory requrest for non-directory", dir);
346*63afb9a5SDavid du Colombier 			return d;
347*63afb9a5SDavid du Colombier 		}
348*63afb9a5SDavid du Colombier 	}else{
349*63afb9a5SDavid du Colombier 		/* create it writeable; will fix later */
350*63afb9a5SDavid du Colombier 		setmodes = 1;
351*63afb9a5SDavid du Colombier 		fd = create(dir, OREAD, DMDIR|mode|0700);
352*63afb9a5SDavid du Colombier 		if (fd < 0){
353*63afb9a5SDavid du Colombier 			scperror(0, "%s: can't create: %r", dir);
354*63afb9a5SDavid du Colombier 			return d;
355*63afb9a5SDavid du Colombier 		}
356*63afb9a5SDavid du Colombier 		d = dirfstat(fd);
357*63afb9a5SDavid du Colombier 		close(fd);
358*63afb9a5SDavid du Colombier 		if(d == nil){
359*63afb9a5SDavid du Colombier 			scperror(0, "%s: can't stat: %r", dir);
360*63afb9a5SDavid du Colombier 			return d;
361*63afb9a5SDavid du Colombier 		}
362*63afb9a5SDavid du Colombier 	}
363*63afb9a5SDavid du Colombier 	receive(dir);
364*63afb9a5SDavid du Colombier 	if(settimes || setmodes){
365*63afb9a5SDavid du Colombier 		nulldir(&nd);
366*63afb9a5SDavid du Colombier 		if(settimes){
367*63afb9a5SDavid du Colombier 			nd.atime = atime;
368*63afb9a5SDavid du Colombier 			nd.mtime = mtime;
369*63afb9a5SDavid du Colombier 			d->atime = nd.atime;
370*63afb9a5SDavid du Colombier 			d->mtime = nd.mtime;
371*63afb9a5SDavid du Colombier 		}
372*63afb9a5SDavid du Colombier 		if(setmodes){
373*63afb9a5SDavid du Colombier 			nd.mode = DMDIR | (mode & 0777);
374*63afb9a5SDavid du Colombier 			d->mode = nd.mode;
375*63afb9a5SDavid du Colombier 		}
376*63afb9a5SDavid du Colombier 		if(dirwstat(dir, &nd) < 0){
377*63afb9a5SDavid du Colombier 			scperror(0, "can't wstat %s: %r", dir);
378*63afb9a5SDavid du Colombier 			free(d);
379*63afb9a5SDavid du Colombier 			return nil;
380*63afb9a5SDavid du Colombier 		}
381*63afb9a5SDavid du Colombier 	}
382*63afb9a5SDavid du Colombier 	return d;
383*63afb9a5SDavid du Colombier }
384*63afb9a5SDavid du Colombier 
385*63afb9a5SDavid du Colombier void
receive(char * dest)386*63afb9a5SDavid du Colombier receive(char *dest)
387*63afb9a5SDavid du Colombier {
388*63afb9a5SDavid du Colombier 	int isdir, settimes, mode;
389*63afb9a5SDavid du Colombier 	int exists, n, i, fd, m;
390*63afb9a5SDavid du Colombier 	int errors;
391*63afb9a5SDavid du Colombier 	ulong atime, mtime, size;
392*63afb9a5SDavid du Colombier 	char buf[8192], *p;
393*63afb9a5SDavid du Colombier 	char name[1024];
394*63afb9a5SDavid du Colombier 	Dir *d;
395*63afb9a5SDavid du Colombier 	Dir nd;
396*63afb9a5SDavid du Colombier 
397*63afb9a5SDavid du Colombier 	mtime = 0L;
398*63afb9a5SDavid du Colombier 	atime = 0L;
399*63afb9a5SDavid du Colombier 	settimes = 0;
400*63afb9a5SDavid du Colombier 	isdir = 0;
401*63afb9a5SDavid du Colombier 	if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) {
402*63afb9a5SDavid du Colombier 		isdir = 1;
403*63afb9a5SDavid du Colombier 	}
404*63afb9a5SDavid du Colombier 	if(dflag && !isdir)
405*63afb9a5SDavid du Colombier 		scperror(1, "%s: not a directory: %r", dest);
406*63afb9a5SDavid du Colombier 
407*63afb9a5SDavid du Colombier 	sendokresponse();
408*63afb9a5SDavid du Colombier 
409*63afb9a5SDavid du Colombier 	for (;;) {
410*63afb9a5SDavid du Colombier 		readhdr(buf, sizeof buf);
411*63afb9a5SDavid du Colombier 
412*63afb9a5SDavid du Colombier 		switch(buf[0]){
413*63afb9a5SDavid du Colombier 		case ERROR:
414*63afb9a5SDavid du Colombier 		case FATAL:
415*63afb9a5SDavid du Colombier 			if(!remote)
416*63afb9a5SDavid du Colombier 				fprint(2, "%s\n", buf+1);
417*63afb9a5SDavid du Colombier 			exitflag = "bad receive";
418*63afb9a5SDavid du Colombier 			if(buf[0] == FATAL)
419*63afb9a5SDavid du Colombier 				exits(exitflag);
420*63afb9a5SDavid du Colombier 			continue;
421*63afb9a5SDavid du Colombier 
422*63afb9a5SDavid du Colombier 		case 'E':
423*63afb9a5SDavid du Colombier 			sendokresponse();
424*63afb9a5SDavid du Colombier 			return;
425*63afb9a5SDavid du Colombier 
426*63afb9a5SDavid du Colombier 		case 'T':
427*63afb9a5SDavid du Colombier 			settimes = 1;
428*63afb9a5SDavid du Colombier 			p = buf + 1;
429*63afb9a5SDavid du Colombier 			mtime = strtol(p, &p, 10);
430*63afb9a5SDavid du Colombier 			if(*p++ != ' '){
431*63afb9a5SDavid du Colombier 		Badtime:
432*63afb9a5SDavid du Colombier 				scperror(1, "bad time format: %s", buf+1);
433*63afb9a5SDavid du Colombier 			}
434*63afb9a5SDavid du Colombier 			strtol(p, &p, 10);
435*63afb9a5SDavid du Colombier 			if(*p++ != ' ')
436*63afb9a5SDavid du Colombier 				goto Badtime;
437*63afb9a5SDavid du Colombier 			atime = strtol(p, &p, 10);
438*63afb9a5SDavid du Colombier 			if(*p++ != ' ')
439*63afb9a5SDavid du Colombier 				goto Badtime;
440*63afb9a5SDavid du Colombier 			strtol(p, &p, 10);
441*63afb9a5SDavid du Colombier 			if(*p++ != 0)
442*63afb9a5SDavid du Colombier 				goto Badtime;
443*63afb9a5SDavid du Colombier 
444*63afb9a5SDavid du Colombier 			sendokresponse();
445*63afb9a5SDavid du Colombier 			continue;
446*63afb9a5SDavid du Colombier 
447*63afb9a5SDavid du Colombier 		case 'D':
448*63afb9a5SDavid du Colombier 		case 'C':
449*63afb9a5SDavid du Colombier 			p = buf + 1;
450*63afb9a5SDavid du Colombier 			mode = strtol(p, &p, 8);
451*63afb9a5SDavid du Colombier 			if (*p++ != ' '){
452*63afb9a5SDavid du Colombier 		Badmode:
453*63afb9a5SDavid du Colombier 				scperror(1, "bad mode/size format: %s", buf+1);
454*63afb9a5SDavid du Colombier 			}
455*63afb9a5SDavid du Colombier 			size = strtoll(p, &p, 10);
456*63afb9a5SDavid du Colombier 			if(*p++ != ' ')
457*63afb9a5SDavid du Colombier 				goto Badmode;
458*63afb9a5SDavid du Colombier 
459*63afb9a5SDavid du Colombier 			if(isdir){
460*63afb9a5SDavid du Colombier 				if(dest[0] == '\0')
461*63afb9a5SDavid du Colombier 					snprint(name, sizeof name, "%s", p);
462*63afb9a5SDavid du Colombier 				else
463*63afb9a5SDavid du Colombier 					snprint(name, sizeof name, "%s/%s", dest, p);
464*63afb9a5SDavid du Colombier 			}else
465*63afb9a5SDavid du Colombier 				snprint(name, sizeof name, "%s", dest);
466*63afb9a5SDavid du Colombier 			if(strlen(name) > sizeof name-UTFmax)
467*63afb9a5SDavid du Colombier 				scperror(1, "file name too long: %s", dest);
468*63afb9a5SDavid du Colombier 
469*63afb9a5SDavid du Colombier 			exists = 1;
470*63afb9a5SDavid du Colombier 			free(d);
471*63afb9a5SDavid du Colombier 			if((d = dirstat(name)) == nil)
472*63afb9a5SDavid du Colombier 				exists = 0;
473*63afb9a5SDavid du Colombier 
474*63afb9a5SDavid du Colombier 			if(buf[0] == 'D'){
475*63afb9a5SDavid du Colombier 				vprint("receive directory %s", name);
476*63afb9a5SDavid du Colombier 				d = receivedir(name, exists, d, settimes, atime, mtime, mode);
477*63afb9a5SDavid du Colombier 				settimes = 0;
478*63afb9a5SDavid du Colombier 				continue;
479*63afb9a5SDavid du Colombier 			}
480*63afb9a5SDavid du Colombier 
481*63afb9a5SDavid du Colombier 			vprint("receive file %s by %s", name, getuser());
482*63afb9a5SDavid du Colombier 			fd = create(name, OWRITE, mode);
483*63afb9a5SDavid du Colombier 			if(fd < 0){
484*63afb9a5SDavid du Colombier 				scperror(0, "can't create %s: %r", name);
485*63afb9a5SDavid du Colombier 				continue;
486*63afb9a5SDavid du Colombier 			}
487*63afb9a5SDavid du Colombier 			sendokresponse();
488*63afb9a5SDavid du Colombier 
489*63afb9a5SDavid du Colombier 			/*
490*63afb9a5SDavid du Colombier 			 * Committed to receive size bytes
491*63afb9a5SDavid du Colombier 			 */
492*63afb9a5SDavid du Colombier 			errors = 0;
493*63afb9a5SDavid du Colombier 			for(i = 0; i < size; i += m){
494*63afb9a5SDavid du Colombier 				n = sizeof buf;
495*63afb9a5SDavid du Colombier 				if(n > size - i)
496*63afb9a5SDavid du Colombier 					n = size - i;
497*63afb9a5SDavid du Colombier 				m = readn(remotefd0, buf, n);
498*63afb9a5SDavid du Colombier 				if(m <= 0)
499*63afb9a5SDavid du Colombier 					scperror(1, "read error on connection: %r");
500*63afb9a5SDavid du Colombier 				if(errors == 0){
501*63afb9a5SDavid du Colombier 					n = write(fd, buf, m);
502*63afb9a5SDavid du Colombier 					if(n != m)
503*63afb9a5SDavid du Colombier 						errors = 1;
504*63afb9a5SDavid du Colombier 				}
505*63afb9a5SDavid du Colombier 			}
506*63afb9a5SDavid du Colombier 
507*63afb9a5SDavid du Colombier 			/* if file exists, modes could be wrong */
508*63afb9a5SDavid du Colombier 			if(errors)
509*63afb9a5SDavid du Colombier 				scperror(0, "%s: write error: %r", name);
510*63afb9a5SDavid du Colombier 			else if(settimes || (exists && (d->mode&0777) != (mode&0777))){
511*63afb9a5SDavid du Colombier 				nulldir(&nd);
512*63afb9a5SDavid du Colombier 				if(settimes){
513*63afb9a5SDavid du Colombier 					settimes = 0;
514*63afb9a5SDavid du Colombier 					nd.atime = atime;
515*63afb9a5SDavid du Colombier 					nd.mtime = mtime;
516*63afb9a5SDavid du Colombier 				}
517*63afb9a5SDavid du Colombier 				if(exists && (d->mode&0777) != (mode&0777))
518*63afb9a5SDavid du Colombier 					nd.mode = (d->mode & ~0777) | (mode&0777);
519*63afb9a5SDavid du Colombier 				if(dirwstat(name, &nd) < 0)
520*63afb9a5SDavid du Colombier 					scperror(0, "can't wstat %s: %r", name);
521*63afb9a5SDavid du Colombier 			}
522*63afb9a5SDavid du Colombier 			free(d);
523*63afb9a5SDavid du Colombier 			d = nil;
524*63afb9a5SDavid du Colombier 			close(fd);
525*63afb9a5SDavid du Colombier 			getresponse();
526*63afb9a5SDavid du Colombier 			if(errors)
527*63afb9a5SDavid du Colombier 				exits("write error");
528*63afb9a5SDavid du Colombier 			sendokresponse();
529*63afb9a5SDavid du Colombier 			break;
530*63afb9a5SDavid du Colombier 
531*63afb9a5SDavid du Colombier 		default:
532*63afb9a5SDavid du Colombier 			scperror(0, "unrecognized header type char %c", buf[0]);
533*63afb9a5SDavid du Colombier 			scperror(1, "input line: %s", buf);
534*63afb9a5SDavid du Colombier 		}
535*63afb9a5SDavid du Colombier 	}
536*63afb9a5SDavid du Colombier }
537*63afb9a5SDavid du Colombier 
538*63afb9a5SDavid du Colombier /*
539*63afb9a5SDavid du Colombier  * Lastelem is called when we have a Dir with the final element, but if the file
540*63afb9a5SDavid du Colombier  * has been bound, we want the original name that was used rather than
541*63afb9a5SDavid du Colombier  * the contents of the stat buffer, so do this lexically.
542*63afb9a5SDavid du Colombier  */
543*63afb9a5SDavid du Colombier char*
lastelem(char * file)544*63afb9a5SDavid du Colombier lastelem(char *file)
545*63afb9a5SDavid du Colombier {
546*63afb9a5SDavid du Colombier 	char *elem;
547*63afb9a5SDavid du Colombier 
548*63afb9a5SDavid du Colombier 	elem = strrchr(file, '/');
549*63afb9a5SDavid du Colombier 	if(elem == nil)
550*63afb9a5SDavid du Colombier 		return file;
551*63afb9a5SDavid du Colombier 	return elem+1;
552*63afb9a5SDavid du Colombier }
553*63afb9a5SDavid du Colombier 
554*63afb9a5SDavid du Colombier void
send(char * file)555*63afb9a5SDavid du Colombier send(char *file)
556*63afb9a5SDavid du Colombier {
557*63afb9a5SDavid du Colombier 	Dir *d;
558*63afb9a5SDavid du Colombier 	ulong i;
559*63afb9a5SDavid du Colombier 	int m, n, fd;
560*63afb9a5SDavid du Colombier 	char buf[8192];
561*63afb9a5SDavid du Colombier 
562*63afb9a5SDavid du Colombier 	if((fd = open(file, OREAD)) < 0){
563*63afb9a5SDavid du Colombier 		scperror(0, "can't open %s: %r", file);
564*63afb9a5SDavid du Colombier 		return;
565*63afb9a5SDavid du Colombier 	}
566*63afb9a5SDavid du Colombier 	if((d = dirfstat(fd)) == nil){
567*63afb9a5SDavid du Colombier 		scperror(0, "can't fstat %s: %r", file);
568*63afb9a5SDavid du Colombier 		goto Return;
569*63afb9a5SDavid du Colombier 	}
570*63afb9a5SDavid du Colombier 
571*63afb9a5SDavid du Colombier 	if(d->qid.type & QTDIR){
572*63afb9a5SDavid du Colombier 		if(rflag)
573*63afb9a5SDavid du Colombier 			senddir(file, fd, d);
574*63afb9a5SDavid du Colombier 		else
575*63afb9a5SDavid du Colombier 			scperror(0, "%s: is a directory", file);
576*63afb9a5SDavid du Colombier 		goto Return;
577*63afb9a5SDavid du Colombier 	}
578*63afb9a5SDavid du Colombier 
579*63afb9a5SDavid du Colombier 	if(pflag){
580*63afb9a5SDavid du Colombier 		fprint(remotefd1, "T%lud 0 %lud 0\n", d->mtime, d->atime);
581*63afb9a5SDavid du Colombier 		if(getresponse() < 0)
582*63afb9a5SDavid du Colombier 			goto Return;
583*63afb9a5SDavid du Colombier 	}
584*63afb9a5SDavid du Colombier 
585*63afb9a5SDavid du Colombier 	fprint(remotefd1, "C%.4luo %lld %s\n", d->mode&0777, d->length, lastelem(file));
586*63afb9a5SDavid du Colombier 	if(getresponse() < 0)
587*63afb9a5SDavid du Colombier 		goto Return;
588*63afb9a5SDavid du Colombier 
589*63afb9a5SDavid du Colombier 	/*
590*63afb9a5SDavid du Colombier 	 * We are now committed to send d.length bytes, regardless
591*63afb9a5SDavid du Colombier 	 */
592*63afb9a5SDavid du Colombier 	for(i=0; i<d->length; i+=m){
593*63afb9a5SDavid du Colombier 		n = sizeof buf;
594*63afb9a5SDavid du Colombier 		if(n > d->length - i)
595*63afb9a5SDavid du Colombier 			n = d->length - i;
596*63afb9a5SDavid du Colombier 		m = readn(fd, buf, n);
597*63afb9a5SDavid du Colombier 		if(m <= 0)
598*63afb9a5SDavid du Colombier 			break;
599*63afb9a5SDavid du Colombier 		write(remotefd1, buf, m);
600*63afb9a5SDavid du Colombier 	}
601*63afb9a5SDavid du Colombier 
602*63afb9a5SDavid du Colombier 	if(i == d->length)
603*63afb9a5SDavid du Colombier 		sendokresponse();
604*63afb9a5SDavid du Colombier 	else{
605*63afb9a5SDavid du Colombier 		/* continue to send gibberish up to d.length */
606*63afb9a5SDavid du Colombier 		for(; i<d->length; i+=n){
607*63afb9a5SDavid du Colombier 			n = sizeof buf;
608*63afb9a5SDavid du Colombier 			if(n > d->length - i)
609*63afb9a5SDavid du Colombier 				n = d->length - i;
610*63afb9a5SDavid du Colombier 			write(remotefd1, buf, n);
611*63afb9a5SDavid du Colombier 		}
612*63afb9a5SDavid du Colombier 		scperror(0, "%s: %r", file);
613*63afb9a5SDavid du Colombier 	}
614*63afb9a5SDavid du Colombier 
615*63afb9a5SDavid du Colombier 	getresponse();
616*63afb9a5SDavid du Colombier 
617*63afb9a5SDavid du Colombier     Return:
618*63afb9a5SDavid du Colombier 	free(d);
619*63afb9a5SDavid du Colombier 	close(fd);
620*63afb9a5SDavid du Colombier }
621*63afb9a5SDavid du Colombier 
622*63afb9a5SDavid du Colombier int
getresponse(void)623*63afb9a5SDavid du Colombier getresponse(void)
624*63afb9a5SDavid du Colombier {
625*63afb9a5SDavid du Colombier 	uchar first, byte, buf[256];
626*63afb9a5SDavid du Colombier 	int i;
627*63afb9a5SDavid du Colombier 
628*63afb9a5SDavid du Colombier 	if (read(remotefd0, &first, 1) != 1)
629*63afb9a5SDavid du Colombier 		scperror(1, "lost connection");
630*63afb9a5SDavid du Colombier 
631*63afb9a5SDavid du Colombier 	if(first == 0)
632*63afb9a5SDavid du Colombier 		return 0;
633*63afb9a5SDavid du Colombier 
634*63afb9a5SDavid du Colombier 	i = 0;
635*63afb9a5SDavid du Colombier 	if(first > FATAL){
636*63afb9a5SDavid du Colombier 		fprint(2, "scp: unexpected response character 0x%.2ux\n", first);
637*63afb9a5SDavid du Colombier 		buf[i++] = first;
638*63afb9a5SDavid du Colombier 	}
639*63afb9a5SDavid du Colombier 
640*63afb9a5SDavid du Colombier 	/* read error message up to newline */
641*63afb9a5SDavid du Colombier 	for(;;){
642*63afb9a5SDavid du Colombier 		if(read(remotefd0, &byte, 1) != 1)
643*63afb9a5SDavid du Colombier 			scperror(1, "response: dropped connection");
644*63afb9a5SDavid du Colombier 		if(byte == '\n')
645*63afb9a5SDavid du Colombier 			break;
646*63afb9a5SDavid du Colombier 		if(i < sizeof buf)
647*63afb9a5SDavid du Colombier 			buf[i++] = byte;
648*63afb9a5SDavid du Colombier 	}
649*63afb9a5SDavid du Colombier 
650*63afb9a5SDavid du Colombier 	exitflag = "bad response";
651*63afb9a5SDavid du Colombier 	if(!remote)
652*63afb9a5SDavid du Colombier 		fprint(2, "%.*s\n", utfnlen((char*)buf, i), (char*)buf);
653*63afb9a5SDavid du Colombier 
654*63afb9a5SDavid du Colombier 	if (first == ERROR)
655*63afb9a5SDavid du Colombier 		return -1;
656*63afb9a5SDavid du Colombier 	exits(exitflag);
657*63afb9a5SDavid du Colombier 	return 0;	/* not reached */
658*63afb9a5SDavid du Colombier }
659*63afb9a5SDavid du Colombier 
660*63afb9a5SDavid du Colombier void
senddir(char * name,int fd,Dir * dirp)661*63afb9a5SDavid du Colombier senddir(char *name, int fd, Dir *dirp)
662*63afb9a5SDavid du Colombier {
663*63afb9a5SDavid du Colombier 	Dir *d, *dir;
664*63afb9a5SDavid du Colombier 	int n;
665*63afb9a5SDavid du Colombier 	char file[256];
666*63afb9a5SDavid du Colombier 
667*63afb9a5SDavid du Colombier 	if(pflag){
668*63afb9a5SDavid du Colombier 		fprint(remotefd1, "T%lud 0 %lud 0\n", dirp->mtime, dirp->atime);
669*63afb9a5SDavid du Colombier 		if(getresponse() < 0)
670*63afb9a5SDavid du Colombier 			return;
671*63afb9a5SDavid du Colombier 	}
672*63afb9a5SDavid du Colombier 
673*63afb9a5SDavid du Colombier 	vprint("directory %s mode: D%.4lo %d %.1024s", name, dirp->mode&0777, 0, lastelem(name));
674*63afb9a5SDavid du Colombier 
675*63afb9a5SDavid du Colombier 	fprint(remotefd1, "D%.4lo %d %.1024s\n", dirp->mode&0777, 0, dirp->name);
676*63afb9a5SDavid du Colombier 	if(getresponse() < 0)
677*63afb9a5SDavid du Colombier 		return;
678*63afb9a5SDavid du Colombier 
679*63afb9a5SDavid du Colombier 	n = dirreadall(fd, &dir);
680*63afb9a5SDavid du Colombier 	for(d = dir; d < &dir[n]; d++){
681*63afb9a5SDavid du Colombier 		/* shouldn't happen with plan 9, but worth checking anyway */
682*63afb9a5SDavid du Colombier 		if(strcmp(d->name, ".")==0 || strcmp(d->name, "..")==0)
683*63afb9a5SDavid du Colombier 			continue;
684*63afb9a5SDavid du Colombier 		if(snprint(file, sizeof file, "%s/%s", name, d->name) > sizeof file-UTFmax){
685*63afb9a5SDavid du Colombier 			scperror(0, "%.20s.../%s: name too long; skipping file", file, d->name);
686*63afb9a5SDavid du Colombier 			continue;
687*63afb9a5SDavid du Colombier 		}
688*63afb9a5SDavid du Colombier 		send(file);
689*63afb9a5SDavid du Colombier 	}
690*63afb9a5SDavid du Colombier 	free(dir);
691*63afb9a5SDavid du Colombier 	fprint(remotefd1, "E\n");
692*63afb9a5SDavid du Colombier 	getresponse();
693*63afb9a5SDavid du Colombier }
694*63afb9a5SDavid du Colombier 
695*63afb9a5SDavid du Colombier int
remotessh(char * host,char * cmd)696*63afb9a5SDavid du Colombier remotessh(char *host, char *cmd)
697*63afb9a5SDavid du Colombier {
698*63afb9a5SDavid du Colombier 	int i, p[2];
699*63afb9a5SDavid du Colombier 	char *arg[32];
700*63afb9a5SDavid du Colombier 
701*63afb9a5SDavid du Colombier 	vprint("remotessh: %s: %s", host, cmd);
702*63afb9a5SDavid du Colombier 
703*63afb9a5SDavid du Colombier 	if(pipe(p) < 0)
704*63afb9a5SDavid du Colombier 		scperror(1, "pipe: %r");
705*63afb9a5SDavid du Colombier 
706*63afb9a5SDavid du Colombier 	switch(fork()){
707*63afb9a5SDavid du Colombier 	case -1:
708*63afb9a5SDavid du Colombier 		scperror(1, "fork: %r");
709*63afb9a5SDavid du Colombier 
710*63afb9a5SDavid du Colombier 	case 0:
711*63afb9a5SDavid du Colombier 		/* child */
712*63afb9a5SDavid du Colombier 		close(p[0]);
713*63afb9a5SDavid du Colombier 		dup(p[1], 0);
714*63afb9a5SDavid du Colombier 		dup(p[1], 1);
715*63afb9a5SDavid du Colombier 		for (i = 3; i < 100; i++)
716*63afb9a5SDavid du Colombier 			close(i);
717*63afb9a5SDavid du Colombier 
718*63afb9a5SDavid du Colombier 		i = 0;
719*63afb9a5SDavid du Colombier 		arg[i++] = ssh;
720*63afb9a5SDavid du Colombier 		arg[i++] = "-x";
721*63afb9a5SDavid du Colombier 		arg[i++] = "-a";
722*63afb9a5SDavid du Colombier 		arg[i++] = "-m";
723*63afb9a5SDavid du Colombier 		if(iflag)
724*63afb9a5SDavid du Colombier 			arg[i++] = "-i";
725*63afb9a5SDavid du Colombier 		if(vflag)
726*63afb9a5SDavid du Colombier 			arg[i++] = "-v";
727*63afb9a5SDavid du Colombier 		arg[i++] = host;
728*63afb9a5SDavid du Colombier 		arg[i++] = cmd;
729*63afb9a5SDavid du Colombier 		arg[i] = nil;
730*63afb9a5SDavid du Colombier 
731*63afb9a5SDavid du Colombier 		exec(ssh, arg);
732*63afb9a5SDavid du Colombier 		exits("exec failed");
733*63afb9a5SDavid du Colombier 
734*63afb9a5SDavid du Colombier 	default:
735*63afb9a5SDavid du Colombier 		/* parent */
736*63afb9a5SDavid du Colombier 		close(p[1]);
737*63afb9a5SDavid du Colombier 		remotefd0 = p[0];
738*63afb9a5SDavid du Colombier 		remotefd1 = p[0];
739*63afb9a5SDavid du Colombier 	}
740*63afb9a5SDavid du Colombier 	return 0;
741*63afb9a5SDavid du Colombier }
742*63afb9a5SDavid du Colombier 
743*63afb9a5SDavid du Colombier void
scperror(int exit,char * fmt,...)744*63afb9a5SDavid du Colombier scperror(int exit, char *fmt, ...)
745*63afb9a5SDavid du Colombier {
746*63afb9a5SDavid du Colombier 	char buf[2048];
747*63afb9a5SDavid du Colombier 	va_list arg;
748*63afb9a5SDavid du Colombier 
749*63afb9a5SDavid du Colombier 
750*63afb9a5SDavid du Colombier 	va_start(arg, fmt);
751*63afb9a5SDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
752*63afb9a5SDavid du Colombier 	va_end(arg);
753*63afb9a5SDavid du Colombier 
754*63afb9a5SDavid du Colombier 	fprint(remotefd1, "%cscp: %s\n", ERROR, buf);
755*63afb9a5SDavid du Colombier 
756*63afb9a5SDavid du Colombier 	if (!remote)
757*63afb9a5SDavid du Colombier 		fprint(2, "scp: %s\n", buf);
758*63afb9a5SDavid du Colombier 	exitflag = buf;
759*63afb9a5SDavid du Colombier 	if(exit)
760*63afb9a5SDavid du Colombier 		exits(exitflag);
761*63afb9a5SDavid du Colombier }
762*63afb9a5SDavid du Colombier 
763*63afb9a5SDavid du Colombier char *
fileaftercolon(char * file)764*63afb9a5SDavid du Colombier fileaftercolon(char *file)
765*63afb9a5SDavid du Colombier {
766*63afb9a5SDavid du Colombier 	char *c, *s;
767*63afb9a5SDavid du Colombier 
768*63afb9a5SDavid du Colombier 	c = utfrune(file, ':');
769*63afb9a5SDavid du Colombier 	if(c == nil)
770*63afb9a5SDavid du Colombier 		return nil;
771*63afb9a5SDavid du Colombier 
772*63afb9a5SDavid du Colombier 	/* colon must be in middle of name to be a separator */
773*63afb9a5SDavid du Colombier 	if(c == file)
774*63afb9a5SDavid du Colombier 		return nil;
775*63afb9a5SDavid du Colombier 
776*63afb9a5SDavid du Colombier 	/* does slash occur before colon? */
777*63afb9a5SDavid du Colombier 	s = utfrune(file, '/');
778*63afb9a5SDavid du Colombier 	if(s != nil && s < c)
779*63afb9a5SDavid du Colombier 		return nil;
780*63afb9a5SDavid du Colombier 
781*63afb9a5SDavid du Colombier 	*c++ = '\0';
782*63afb9a5SDavid du Colombier 	if(*c == '\0')
783*63afb9a5SDavid du Colombier 		return ".";
784*63afb9a5SDavid du Colombier 	return c;
785*63afb9a5SDavid du Colombier }
786*63afb9a5SDavid du Colombier 
787*63afb9a5SDavid du Colombier void
mustbedir(char * file)788*63afb9a5SDavid du Colombier mustbedir(char *file)
789*63afb9a5SDavid du Colombier {
790*63afb9a5SDavid du Colombier 	Dir *d;
791*63afb9a5SDavid du Colombier 
792*63afb9a5SDavid du Colombier 	if((d = dirstat(file)) == nil){
793*63afb9a5SDavid du Colombier 		scperror(1, "%s: %r", file);
794*63afb9a5SDavid du Colombier 		return;
795*63afb9a5SDavid du Colombier 	}
796*63afb9a5SDavid du Colombier 	if(!(d->qid.type & QTDIR))
797*63afb9a5SDavid du Colombier 		scperror(1, "%s: Not a directory", file);
798*63afb9a5SDavid du Colombier 	free(d);
799*63afb9a5SDavid du Colombier }
800