xref: /csrg-svn/bin/rcp/rcp.c (revision 23112)
121569Sdist /*
221569Sdist  * Copyright (c) 1983 Regents of the University of California.
321569Sdist  * All rights reserved.  The Berkeley software License Agreement
421569Sdist  * specifies the terms and conditions for redistribution.
521569Sdist  */
621569Sdist 
76440Swnj #ifndef lint
821569Sdist char copyright[] =
921569Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021569Sdist  All rights reserved.\n";
1121569Sdist #endif not lint
126440Swnj 
1321569Sdist #ifndef lint
14*23112Slepreau static char sccsid[] = "@(#)rcp.c	5.3 (Berkeley) 06/08/85";
1521569Sdist #endif not lint
1621569Sdist 
1712989Ssam /*
1812989Ssam  * rcp
1912989Ssam  */
206720Smckusick #include <sys/param.h>
216440Swnj #include <sys/stat.h>
2223101Slepreau #include <sys/time.h>
236440Swnj #include <sys/ioctl.h>
249199Ssam 
259199Ssam #include <netinet/in.h>
269199Ssam 
279199Ssam #include <stdio.h>
289199Ssam #include <signal.h>
296440Swnj #include <pwd.h>
306440Swnj #include <ctype.h>
3118026Sralph #include <netdb.h>
326440Swnj #include <errno.h>
3312989Ssam 
346440Swnj int	rem;
356440Swnj char	*colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf();
366440Swnj int	errs;
376440Swnj int	lostconn();
386440Swnj int	errno;
396440Swnj char	*sys_errlist[];
406440Swnj int	iamremote, targetshouldbedirectory;
416440Swnj int	iamrecursive;
4223101Slepreau int	pflag;
436440Swnj struct	passwd *pwd;
446440Swnj struct	passwd *getpwuid();
4518126Sralph int	userid;
4618126Sralph int	port;
476440Swnj 
4821254Smckusick struct buffer {
4921254Smckusick 	int	cnt;
5021254Smckusick 	char	*buf;
5121254Smckusick } *allocbuf();
5221254Smckusick 
536440Swnj /*VARARGS*/
546440Swnj int	error();
556440Swnj 
566440Swnj #define	ga()	 	(void) write(rem, "", 1)
576440Swnj 
586440Swnj main(argc, argv)
596440Swnj 	int argc;
606440Swnj 	char **argv;
616440Swnj {
626440Swnj 	char *targ, *host, *src;
6318631Sralph #ifndef NAMESERVER
6418631Sralph 	char *suser, *tuser;
6518631Sralph #else NAMESERVER
6618259Sralph 	char *suser, *tuser, *thost;
6718631Sralph #endif NAMESERVER
686440Swnj 	int i;
696440Swnj 	char buf[BUFSIZ], cmd[16];
7018126Sralph 	struct servent *sp;
7118026Sralph 
7218026Sralph 	sp = getservbyname("shell", "tcp");
7318026Sralph 	if (sp == NULL) {
7418026Sralph 		fprintf(stderr, "rcp: shell/tcp: unknown service\n");
7518026Sralph 		exit(1);
7618026Sralph 	}
7718126Sralph 	port = sp->s_port;
7818126Sralph 	pwd = getpwuid(userid = getuid());
796440Swnj 	if (pwd == 0) {
806440Swnj 		fprintf(stderr, "who are you?\n");
816440Swnj 		exit(1);
826440Swnj 	}
8323101Slepreau 
8423101Slepreau 	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
8523101Slepreau 		(*argv)++;
8623101Slepreau 		while (**argv) switch (*(*argv)++) {
8723101Slepreau 
8823101Slepreau 		    case 'r':
8923101Slepreau 			iamrecursive++;
9023101Slepreau 			break;
9123101Slepreau 
9223101Slepreau 		    case 'p':		/* preserve mtimes and atimes */
9323101Slepreau 			pflag++;
9423101Slepreau 			break;
9523101Slepreau 
9623101Slepreau 		    /* The rest of these are not for users. */
9723101Slepreau 		    case 'd':
9823101Slepreau 			targetshouldbedirectory = 1;
9923101Slepreau 			break;
10023101Slepreau 
10123101Slepreau 		    case 'f':		/* "from" */
10223101Slepreau 			iamremote = 1;
10323101Slepreau 			(void) response();
10423101Slepreau 			(void) setuid(userid);
10523101Slepreau 			source(--argc, ++argv);
10623101Slepreau 			exit(errs);
10723101Slepreau 
10823101Slepreau 		    case 't':		/* "to" */
10923101Slepreau 			iamremote = 1;
11023101Slepreau 			(void) setuid(userid);
11123101Slepreau 			sink(--argc, ++argv);
11223101Slepreau 			exit(errs);
11323101Slepreau 
11423101Slepreau 		    default:
11523101Slepreau 			fprintf(stderr,
116*23112Slepreau 		"Usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n");
11723101Slepreau 			exit(1);
11823101Slepreau 		}
1196440Swnj 	}
1206440Swnj 	rem = -1;
1216440Swnj 	if (argc > 2)
1226440Swnj 		targetshouldbedirectory = 1;
12323101Slepreau 	(void) sprintf(cmd, "rcp%s%s%s",
12423101Slepreau 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
12523101Slepreau 	    targetshouldbedirectory ? " -d" : "");
12623101Slepreau 	(void) signal(SIGPIPE, lostconn);
1276440Swnj 	targ = colon(argv[argc - 1]);
12823101Slepreau 	if (targ) {				/* ... to remote */
1296440Swnj 		*targ++ = 0;
13013542Ssam 		if (*targ == 0)
13113542Ssam 			targ = ".";
13218631Sralph #ifndef NAMESERVER
13318631Sralph 		tuser = rindex(argv[argc - 1], '.');
13418631Sralph 		if (tuser) {
13518631Sralph 			*tuser++ = 0;
13618631Sralph 			if (!okname(tuser))
13718631Sralph 				exit(1);
13818631Sralph 		} else
13918631Sralph 			tuser = pwd->pw_name;
14018631Sralph #else NAMESERVER
14118259Sralph 		thost = index(argv[argc - 1], '@');
14218259Sralph 		if (thost) {
14318259Sralph 			*thost++ = 0;
14418259Sralph 			tuser = argv[argc - 1];
14518259Sralph 			if (*tuser == '\0')
14618259Sralph 				tuser = pwd->pw_name;
14718259Sralph 			else if (!okname(tuser))
1486440Swnj 				exit(1);
14918259Sralph 		} else {
15018259Sralph 			thost = argv[argc - 1];
1516440Swnj 			tuser = pwd->pw_name;
15218259Sralph 		}
15318631Sralph #endif NAMESERVER
1546440Swnj 		for (i = 0; i < argc - 1; i++) {
1556440Swnj 			src = colon(argv[i]);
15623101Slepreau 			if (src) {		/* remote to remote */
1576440Swnj 				*src++ = 0;
15813542Ssam 				if (*src == 0)
15913738Ssam 					src = ".";
16018631Sralph #ifndef NAMESERVER
16118631Sralph 				suser = rindex(argv[i], '.');
16218631Sralph 				if (suser) {
16318631Sralph 					*suser++ = 0;
16418631Sralph 					if (!okname(suser))
16518631Sralph #else NAMESERVER
16618259Sralph 				host = index(argv[i], '@');
16718259Sralph 				if (host) {
16818259Sralph 					*host++ = 0;
16918259Sralph 					suser = argv[i];
17018259Sralph 					if (*suser == '\0')
17118259Sralph 						suser = pwd->pw_name;
17218259Sralph 					else if (!okname(suser))
17318631Sralph #endif NAMESERVER
1746440Swnj 						continue;
17518631Sralph #ifndef NAMESERVER
17618631Sralph 		(void) sprintf(buf, "rsh %s -l %s -n %s %s '%s.%s:%s'",
17718631Sralph 					    argv[i], suser, cmd, src,
17818631Sralph 					    argv[argc - 1], tuser, targ);
17918631Sralph 				} else
18018631Sralph 		(void) sprintf(buf, "rsh %s -n %s %s '%s.%s:%s'",
18118631Sralph 					    argv[i], cmd, src,
18218631Sralph 					    argv[argc - 1], tuser, targ);
18318631Sralph #else NAMESERVER
18418259Sralph 		(void) sprintf(buf, "rsh %s -l %s -n %s %s '%s@%s:%s'",
18518259Sralph 					    host, suser, cmd, src,
18618259Sralph 					    tuser, thost, targ);
1876440Swnj 				} else
18818259Sralph 		(void) sprintf(buf, "rsh %s -n %s %s '%s@%s:%s'",
18915898Sralph 					    argv[i], cmd, src,
19018259Sralph 					    tuser, thost, targ);
19118631Sralph #endif NAMESERVER
1926440Swnj 				(void) susystem(buf);
19323101Slepreau 			} else {		/* local to remote */
1946440Swnj 				if (rem == -1) {
1956440Swnj 					(void) sprintf(buf, "%s -t %s",
1966440Swnj 					    cmd, targ);
19718631Sralph #ifndef NAMESERVER
19818631Sralph 					host = argv[argc - 1];
19918631Sralph #else NAMESERVER
20018259Sralph 					host = thost;
20118631Sralph #endif NAMESERVER
20218126Sralph 					rem = rcmd(&host, port, pwd->pw_name, tuser,
2036440Swnj 					    buf, 0);
2046440Swnj 					if (rem < 0)
2056440Swnj 						exit(1);
2066440Swnj 					if (response() < 0)
2076440Swnj 						exit(1);
20818126Sralph 					(void) setuid(userid);
2096440Swnj 				}
2106440Swnj 				source(1, argv+i);
2116440Swnj 			}
2126440Swnj 		}
21323101Slepreau 	} else {				/* ... to local */
2146440Swnj 		if (targetshouldbedirectory)
2156440Swnj 			verifydir(argv[argc - 1]);
2166440Swnj 		for (i = 0; i < argc - 1; i++) {
2176440Swnj 			src = colon(argv[i]);
21823101Slepreau 			if (src == 0) {		/* local to local */
21923101Slepreau 				(void) sprintf(buf, "/bin/cp%s%s %s %s",
2206440Swnj 				    iamrecursive ? " -r" : "",
22123101Slepreau 				    pflag ? " -p" : "",
2226440Swnj 				    argv[i], argv[argc - 1]);
2236440Swnj 				(void) susystem(buf);
22423101Slepreau 			} else {		/* remote to local */
2256440Swnj 				*src++ = 0;
22613542Ssam 				if (*src == 0)
22713542Ssam 					src = ".";
22818631Sralph #ifndef NAMESERVER
22918631Sralph 				suser = rindex(argv[i], '.');
23018631Sralph 				if (suser) {
23118631Sralph 					*suser++ = 0;
23218631Sralph 					if (!okname(suser))
23318631Sralph #else NAMESERVER
23418259Sralph 				host = index(argv[i], '@');
23518259Sralph 				if (host) {
23618259Sralph 					*host++ = 0;
23718259Sralph 					suser = argv[i];
23818259Sralph 					if (*suser == '\0')
23918259Sralph 						suser = pwd->pw_name;
24018259Sralph 					else if (!okname(suser))
24118631Sralph #endif NAMESERVER
2426440Swnj 						continue;
24318631Sralph #ifndef NAMESERVER
24418631Sralph 				} else
24518631Sralph #else NAMESERVER
24618259Sralph 				} else {
24718259Sralph 					host = argv[i];
24818631Sralph #endif NAMESERVER
2496440Swnj 					suser = pwd->pw_name;
25018631Sralph #ifdef NAMESERVER
25118259Sralph 				}
25218631Sralph #endif NAMESERVER
2536440Swnj 				(void) sprintf(buf, "%s -f %s", cmd, src);
25418631Sralph #ifndef NAMESERVER
25518631Sralph 				host = argv[i];
25618631Sralph #endif NAMESERVER
25718126Sralph 				rem = rcmd(&host, port, pwd->pw_name, suser,
2586440Swnj 				    buf, 0);
2596440Swnj 				if (rem < 0)
26018259Sralph 					continue;
26118126Sralph 				(void) setreuid(0, userid);
2626440Swnj 				sink(1, argv+argc-1);
26318126Sralph 				(void) setreuid(userid, 0);
2646440Swnj 				(void) close(rem);
2656440Swnj 				rem = -1;
2666440Swnj 			}
2676440Swnj 		}
2686440Swnj 	}
2696440Swnj 	exit(errs);
2706440Swnj }
2716440Swnj 
2726440Swnj verifydir(cp)
2736440Swnj 	char *cp;
2746440Swnj {
2756440Swnj 	struct stat stb;
2766440Swnj 
27718126Sralph 	if (stat(cp, &stb) >= 0) {
27818126Sralph 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
27918126Sralph 			return;
28018126Sralph 		errno = ENOTDIR;
28118126Sralph 	}
2826440Swnj 	error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
2836440Swnj 	exit(1);
2846440Swnj }
2856440Swnj 
2866440Swnj char *
2876440Swnj colon(cp)
2886440Swnj 	char *cp;
2896440Swnj {
2906440Swnj 
2916440Swnj 	while (*cp) {
2926440Swnj 		if (*cp == ':')
2936440Swnj 			return (cp);
2946440Swnj 		if (*cp == '/')
2956440Swnj 			return (0);
2966440Swnj 		cp++;
2976440Swnj 	}
2986440Swnj 	return (0);
2996440Swnj }
3006440Swnj 
3016440Swnj okname(cp0)
3026440Swnj 	char *cp0;
3036440Swnj {
3046440Swnj 	register char *cp = cp0;
3056440Swnj 	register int c;
3066440Swnj 
3076440Swnj 	do {
3086440Swnj 		c = *cp;
3096440Swnj 		if (c & 0200)
3106440Swnj 			goto bad;
3116440Swnj 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
3126440Swnj 			goto bad;
3136440Swnj 		cp++;
3146440Swnj 	} while (*cp);
3156440Swnj 	return (1);
3166440Swnj bad:
3176440Swnj 	fprintf(stderr, "rcp: invalid user name %s\n", cp0);
3186440Swnj 	return (0);
3196440Swnj }
3206440Swnj 
32117999Sserge susystem(s)
32217999Sserge 	char *s;
3236440Swnj {
32417999Sserge 	int status, pid, w;
32517999Sserge 	register int (*istat)(), (*qstat)();
3266440Swnj 
32717999Sserge 	if ((pid = vfork()) == 0) {
32823101Slepreau 		(void) setuid(userid);
32917999Sserge 		execl("/bin/sh", "sh", "-c", s, (char *)0);
33017999Sserge 		_exit(127);
33117999Sserge 	}
33217999Sserge 	istat = signal(SIGINT, SIG_IGN);
33317999Sserge 	qstat = signal(SIGQUIT, SIG_IGN);
33417999Sserge 	while ((w = wait(&status)) != pid && w != -1)
33517999Sserge 		;
33617999Sserge 	if (w == -1)
33717999Sserge 		status = -1;
33823101Slepreau 	(void) signal(SIGINT, istat);
33923101Slepreau 	(void) signal(SIGQUIT, qstat);
34017999Sserge 	return (status);
3416440Swnj }
3426440Swnj 
3436440Swnj source(argc, argv)
3446440Swnj 	int argc;
3456440Swnj 	char **argv;
3466440Swnj {
3476440Swnj 	char *last, *name;
3486440Swnj 	struct stat stb;
34921254Smckusick 	static struct buffer buffer;
35021254Smckusick 	struct buffer *bp;
35121254Smckusick 	int x, sizerr, f, amt;
35221254Smckusick 	off_t i;
3536440Swnj 	char buf[BUFSIZ];
3546440Swnj 
3556440Swnj 	for (x = 0; x < argc; x++) {
3566440Swnj 		name = argv[x];
35718126Sralph 		if ((f = open(name, 0)) < 0) {
3586440Swnj 			error("rcp: %s: %s\n", name, sys_errlist[errno]);
3596440Swnj 			continue;
3606440Swnj 		}
3616440Swnj 		if (fstat(f, &stb) < 0)
3626440Swnj 			goto notreg;
3636440Swnj 		switch (stb.st_mode&S_IFMT) {
3646440Swnj 
3656440Swnj 		case S_IFREG:
3666440Swnj 			break;
3676440Swnj 
3686440Swnj 		case S_IFDIR:
3696440Swnj 			if (iamrecursive) {
3706440Swnj 				(void) close(f);
37123101Slepreau 				rsource(name, &stb);
3726440Swnj 				continue;
3736440Swnj 			}
3746440Swnj 			/* fall into ... */
3756440Swnj 		default:
3766440Swnj notreg:
3776440Swnj 			(void) close(f);
3786440Swnj 			error("rcp: %s: not a plain file\n", name);
3796440Swnj 			continue;
3806440Swnj 		}
3816440Swnj 		last = rindex(name, '/');
3826440Swnj 		if (last == 0)
3836440Swnj 			last = name;
3846440Swnj 		else
3856440Swnj 			last++;
38623101Slepreau 		if (pflag) {
38723101Slepreau 			/*
38823101Slepreau 			 * Make it compatible with possible future
38923101Slepreau 			 * versions expecting microseconds.
39023101Slepreau 			 */
39123101Slepreau 			(void) sprintf(buf, "T%ld 0 %ld 0\n",
39223101Slepreau 			    stb.st_mtime, stb.st_atime);
39323101Slepreau 			(void) write(rem, buf, strlen(buf));
39423101Slepreau 			if (response() < 0) {
39523101Slepreau 				(void) close(f);
39623101Slepreau 				continue;
39723101Slepreau 			}
39823101Slepreau 		}
39923101Slepreau 		(void) sprintf(buf, "C%04o %ld %s\n",
4006440Swnj 		    stb.st_mode&07777, stb.st_size, last);
4016440Swnj 		(void) write(rem, buf, strlen(buf));
4026440Swnj 		if (response() < 0) {
4036440Swnj 			(void) close(f);
4046440Swnj 			continue;
4056440Swnj 		}
40621254Smckusick 		if ((bp = allocbuf(&buffer, f, BUFSIZ)) < 0) {
40721254Smckusick 			(void) close(f);
40821254Smckusick 			continue;
40921254Smckusick 		}
4106440Swnj 		sizerr = 0;
41121254Smckusick 		for (i = 0; i < stb.st_size; i += bp->cnt) {
41221254Smckusick 			amt = bp->cnt;
4136440Swnj 			if (i + amt > stb.st_size)
4146440Swnj 				amt = stb.st_size - i;
41521254Smckusick 			if (sizerr == 0 && read(f, bp->buf, amt) != amt)
4166440Swnj 				sizerr = 1;
41721254Smckusick 			(void) write(rem, bp->buf, amt);
4186440Swnj 		}
4196440Swnj 		(void) close(f);
4206440Swnj 		if (sizerr == 0)
4216440Swnj 			ga();
4226440Swnj 		else
4236440Swnj 			error("rcp: %s: file changed size\n", name);
4246440Swnj 		(void) response();
4256440Swnj 	}
4266440Swnj }
4276440Swnj 
42813542Ssam #include <sys/dir.h>
4296440Swnj 
43023101Slepreau rsource(name, statp)
4316440Swnj 	char *name;
43223101Slepreau 	struct stat *statp;
4336440Swnj {
4346440Swnj 	DIR *d = opendir(name);
4356440Swnj 	char *last;
4366440Swnj 	struct direct *dp;
4376440Swnj 	char buf[BUFSIZ];
4386440Swnj 	char *bufv[1];
4396440Swnj 
4406440Swnj 	if (d == 0) {
44118126Sralph 		error("rcp: %s: %s\n", name, sys_errlist[errno]);
4426440Swnj 		return;
4436440Swnj 	}
4446440Swnj 	last = rindex(name, '/');
4456440Swnj 	if (last == 0)
4466440Swnj 		last = name;
4476440Swnj 	else
4486440Swnj 		last++;
44923101Slepreau 	if (pflag) {
45023101Slepreau 		(void) sprintf(buf, "T%ld 0 %ld 0\n",
45123101Slepreau 		    statp->st_mtime, statp->st_atime);
45223101Slepreau 		(void) write(rem, buf, strlen(buf));
45323101Slepreau 		if (response() < 0) {
45423101Slepreau 			closedir(d);
45523101Slepreau 			return;
45623101Slepreau 		}
45723101Slepreau 	}
45823101Slepreau 	(void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
4596440Swnj 	(void) write(rem, buf, strlen(buf));
4606440Swnj 	if (response() < 0) {
4616440Swnj 		closedir(d);
4626440Swnj 		return;
4636440Swnj 	}
4646440Swnj 	while (dp = readdir(d)) {
4656440Swnj 		if (dp->d_ino == 0)
4666440Swnj 			continue;
4676440Swnj 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
4686440Swnj 			continue;
4696440Swnj 		if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
4706440Swnj 			error("%s/%s: Name too long.\n", name, dp->d_name);
4716440Swnj 			continue;
4726440Swnj 		}
4736440Swnj 		(void) sprintf(buf, "%s/%s", name, dp->d_name);
4746440Swnj 		bufv[0] = buf;
4756440Swnj 		source(1, bufv);
4766440Swnj 	}
4776440Swnj 	closedir(d);
4786440Swnj 	(void) write(rem, "E\n", 2);
4796440Swnj 	(void) response();
4806440Swnj }
4816440Swnj 
4826440Swnj response()
4836440Swnj {
4846440Swnj 	char resp, c, rbuf[BUFSIZ], *cp = rbuf;
4856440Swnj 
4866440Swnj 	if (read(rem, &resp, 1) != 1)
4876440Swnj 		lostconn();
4886440Swnj 	switch (resp) {
4896440Swnj 
49023101Slepreau 	case 0:				/* ok */
4916440Swnj 		return (0);
4926440Swnj 
4936440Swnj 	default:
4946440Swnj 		*cp++ = resp;
4956440Swnj 		/* fall into... */
49623101Slepreau 	case 1:				/* error, followed by err msg */
49723101Slepreau 	case 2:				/* fatal error, "" */
4986440Swnj 		do {
4996440Swnj 			if (read(rem, &c, 1) != 1)
5006440Swnj 				lostconn();
5016440Swnj 			*cp++ = c;
5026440Swnj 		} while (cp < &rbuf[BUFSIZ] && c != '\n');
5036440Swnj 		if (iamremote == 0)
5046440Swnj 			(void) write(2, rbuf, cp - rbuf);
5056440Swnj 		errs++;
5066440Swnj 		if (resp == 1)
5076440Swnj 			return (-1);
5086440Swnj 		exit(1);
5096440Swnj 	}
5106440Swnj 	/*NOTREACHED*/
5116440Swnj }
5126440Swnj 
5136440Swnj lostconn()
5146440Swnj {
5156440Swnj 
5166440Swnj 	if (iamremote == 0)
5176440Swnj 		fprintf(stderr, "rcp: lost connection\n");
5186440Swnj 	exit(1);
5196440Swnj }
5206440Swnj 
5216440Swnj sink(argc, argv)
5226440Swnj 	int argc;
5236440Swnj 	char **argv;
5246440Swnj {
52521254Smckusick 	off_t i, j;
52621254Smckusick 	char *targ, *whopp, *cp;
52721254Smckusick 	int of, mode, wrerr, exists, first, count, amt, size;
52821254Smckusick 	struct buffer *bp;
52921254Smckusick 	static struct buffer buffer;
53021254Smckusick 	struct stat stb;
53121254Smckusick 	int targisdir = 0;
5326440Swnj 	int mask = umask(0);
5336440Swnj 	char *myargv[1];
53423101Slepreau 	char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
53523101Slepreau 	int setimes = 0;
53623101Slepreau 	struct timeval tv[2];
53723101Slepreau #define atime	tv[0]
53823101Slepreau #define mtime	tv[1]
53921254Smckusick #define	SCREWUP(str)	{ whopp = str; goto screwup; }
5406440Swnj 
541*23112Slepreau 	if (!pflag)
542*23112Slepreau 		(void) umask(mask);
54318126Sralph 	if (argc != 1) {
5446440Swnj 		error("rcp: ambiguous target\n");
5456440Swnj 		exit(1);
5466440Swnj 	}
5476440Swnj 	targ = *argv;
5486440Swnj 	if (targetshouldbedirectory)
5496440Swnj 		verifydir(targ);
5506440Swnj 	ga();
5516440Swnj 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
5526440Swnj 		targisdir = 1;
55314580Sralph 	for (first = 1; ; first = 0) {
5546440Swnj 		cp = cmdbuf;
5556440Swnj 		if (read(rem, cp, 1) <= 0)
5566440Swnj 			return;
5576440Swnj 		if (*cp++ == '\n')
5586440Swnj 			SCREWUP("unexpected '\\n'");
5596440Swnj 		do {
5606440Swnj 			if (read(rem, cp, 1) != 1)
5616440Swnj 				SCREWUP("lost connection");
5626440Swnj 		} while (*cp++ != '\n');
5636440Swnj 		*cp = 0;
5646440Swnj 		if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
5656440Swnj 			if (iamremote == 0)
56613542Ssam 				(void) write(2, cmdbuf+1, strlen(cmdbuf+1));
5676440Swnj 			if (cmdbuf[0] == '\02')
5686440Swnj 				exit(1);
5696440Swnj 			errs++;
5706440Swnj 			continue;
5716440Swnj 		}
5726440Swnj 		*--cp = 0;
5736440Swnj 		cp = cmdbuf;
5746440Swnj 		if (*cp == 'E') {
5756440Swnj 			ga();
5766440Swnj 			return;
5776440Swnj 		}
57823101Slepreau 
57923101Slepreau #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
58023101Slepreau 		if (*cp == 'T') {
58123101Slepreau 			setimes++;
58223101Slepreau 			cp++;
58323101Slepreau 			getnum(mtime.tv_sec);
58423101Slepreau 			if (*cp++ != ' ')
58523101Slepreau 				SCREWUP("mtime.sec not delimited");
58623101Slepreau 			getnum(mtime.tv_usec);
58723101Slepreau 			if (*cp++ != ' ')
58823101Slepreau 				SCREWUP("mtime.usec not delimited");
58923101Slepreau 			getnum(atime.tv_sec);
59023101Slepreau 			if (*cp++ != ' ')
59123101Slepreau 				SCREWUP("atime.sec not delimited");
59223101Slepreau 			getnum(atime.tv_usec);
59323101Slepreau 			if (*cp++ != '\0')
59423101Slepreau 				SCREWUP("atime.usec not delimited");
59523101Slepreau 			ga();
59623101Slepreau 			continue;
59723101Slepreau 		}
59814580Sralph 		if (*cp != 'C' && *cp != 'D') {
59914580Sralph 			/*
60014580Sralph 			 * Check for the case "rcp remote:foo\* local:bar".
60114580Sralph 			 * In this case, the line "No match." can be returned
60214580Sralph 			 * by the shell before the rcp command on the remote is
60314580Sralph 			 * executed so the ^Aerror_message convention isn't
60414580Sralph 			 * followed.
60514580Sralph 			 */
60614580Sralph 			if (first) {
60714580Sralph 				error("%s\n", cp);
60814580Sralph 				exit(1);
60914580Sralph 			}
6106440Swnj 			SCREWUP("expected control record");
61114580Sralph 		}
6126440Swnj 		cp++;
6136440Swnj 		mode = 0;
6146440Swnj 		for (; cp < cmdbuf+5; cp++) {
6156440Swnj 			if (*cp < '0' || *cp > '7')
6166440Swnj 				SCREWUP("bad mode");
6176440Swnj 			mode = (mode << 3) | (*cp - '0');
6186440Swnj 		}
6196440Swnj 		if (*cp++ != ' ')
6206440Swnj 			SCREWUP("mode not delimited");
6216440Swnj 		size = 0;
622*23112Slepreau 		while (isdigit(*cp))
6236440Swnj 			size = size * 10 + (*cp++ - '0');
6246440Swnj 		if (*cp++ != ' ')
6256440Swnj 			SCREWUP("size not delimited");
6266440Swnj 		if (targisdir)
6276440Swnj 			(void) sprintf(nambuf, "%s%s%s", targ,
6286440Swnj 			    *targ ? "/" : "", cp);
6296440Swnj 		else
6306440Swnj 			(void) strcpy(nambuf, targ);
6316440Swnj 		exists = stat(nambuf, &stb) == 0;
63218126Sralph 		if (cmdbuf[0] == 'D') {
63318126Sralph 			if (exists) {
6346440Swnj 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
6356440Swnj 					errno = ENOTDIR;
6366440Swnj 					goto bad;
6376440Swnj 				}
638*23112Slepreau 				if (pflag)
639*23112Slepreau 					(void) chmod(nambuf, mode);
64018126Sralph 			} else if (mkdir(nambuf, mode) < 0)
6416440Swnj 				goto bad;
6426440Swnj 			myargv[0] = nambuf;
6436440Swnj 			sink(1, myargv);
64423101Slepreau 			if (setimes) {
64523101Slepreau 				setimes = 0;
64623101Slepreau 				if (utimes(nambuf, tv) < 0)
64723101Slepreau 					error("rcp: can't set times on %s: %s\n",
64823101Slepreau 					    nambuf, sys_errlist[errno]);
64923101Slepreau 			}
6506440Swnj 			continue;
65118126Sralph 		}
65218126Sralph 		if ((of = creat(nambuf, mode)) < 0) {
6536440Swnj 	bad:
6546440Swnj 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
6556440Swnj 			continue;
6566440Swnj 		}
657*23112Slepreau 		if (exists && pflag)
658*23112Slepreau 			(void) fchmod(of, mode);
6596440Swnj 		ga();
66021254Smckusick 		if ((bp = allocbuf(&buffer, of, BUFSIZ)) < 0) {
66123101Slepreau 			(void) close(of);
66221254Smckusick 			continue;
66321254Smckusick 		}
66421254Smckusick 		cp = bp->buf;
66521254Smckusick 		count = 0;
6666440Swnj 		wrerr = 0;
6676440Swnj 		for (i = 0; i < size; i += BUFSIZ) {
66821254Smckusick 			amt = BUFSIZ;
6696440Swnj 			if (i + amt > size)
6706440Swnj 				amt = size - i;
67121254Smckusick 			count += amt;
6726440Swnj 			do {
67321254Smckusick 				j = read(rem, cp, amt);
6746440Swnj 				if (j <= 0)
6756440Swnj 					exit(1);
6766440Swnj 				amt -= j;
6776440Swnj 				cp += j;
6786440Swnj 			} while (amt > 0);
67921254Smckusick 			if (count == bp->cnt) {
68021254Smckusick 				if (wrerr == 0 &&
68121254Smckusick 				    write(of, bp->buf, count) != count)
68221254Smckusick 					wrerr++;
68321254Smckusick 				count = 0;
68421254Smckusick 				cp = bp->buf;
68521254Smckusick 			}
6866440Swnj 		}
68721254Smckusick 		if (count != 0 && wrerr == 0 &&
68821254Smckusick 		    write(of, bp->buf, count) != count)
68921254Smckusick 			wrerr++;
6906440Swnj 		(void) close(of);
6916440Swnj 		(void) response();
69223101Slepreau 		if (setimes) {
69323101Slepreau 			setimes = 0;
69423101Slepreau 			if (utimes(nambuf, tv) < 0)
69523101Slepreau 				error("rcp: can't set times on %s: %s\n",
69623101Slepreau 				    nambuf, sys_errlist[errno]);
69723101Slepreau 		}
6986440Swnj 		if (wrerr)
69923101Slepreau 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
7006440Swnj 		else
7016440Swnj 			ga();
7026440Swnj 	}
7036440Swnj screwup:
7046440Swnj 	error("rcp: protocol screwup: %s\n", whopp);
7056440Swnj 	exit(1);
7066440Swnj }
7076440Swnj 
70821254Smckusick struct buffer *
70921254Smckusick allocbuf(bp, fd, blksize)
71021254Smckusick 	struct buffer *bp;
71121254Smckusick 	int fd, blksize;
71221254Smckusick {
71321254Smckusick 	struct stat stb;
71421254Smckusick 	int size;
71521254Smckusick 
71621254Smckusick 	if (fstat(fd, &stb) < 0) {
71721254Smckusick 		error("rcp: fstat: %s\n", sys_errlist[errno]);
71821254Smckusick 		return ((struct buffer *)-1);
71921254Smckusick 	}
72021254Smckusick 	size = roundup(stb.st_blksize, blksize);
72121254Smckusick 	if (size == 0)
72221254Smckusick 		size = blksize;
72321254Smckusick 	if (bp->cnt < size) {
72421254Smckusick 		if (bp->buf != 0)
72521254Smckusick 			free(bp->buf);
72623101Slepreau 		bp->buf = (char *)malloc((unsigned) size);
72721254Smckusick 		if (bp->buf == 0) {
72821254Smckusick 			error("rcp: malloc: out of memory\n");
72921254Smckusick 			return ((struct buffer *)-1);
73021254Smckusick 		}
73121254Smckusick 	}
73221254Smckusick 	bp->cnt = size;
73321254Smckusick 	return (bp);
73421254Smckusick }
73521254Smckusick 
73623101Slepreau /*VARARGS1*/
7376440Swnj error(fmt, a1, a2, a3, a4, a5)
7386440Swnj 	char *fmt;
7396440Swnj 	int a1, a2, a3, a4, a5;
7406440Swnj {
7416440Swnj 	char buf[BUFSIZ], *cp = buf;
7426440Swnj 
7436440Swnj 	errs++;
7446440Swnj 	*cp++ = 1;
7456440Swnj 	(void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
7466440Swnj 	(void) write(rem, buf, strlen(buf));
7476440Swnj 	if (iamremote == 0)
7486440Swnj 		(void) write(2, buf+1, strlen(buf+1));
7496440Swnj }
750