xref: /csrg-svn/bin/mv/mv.c (revision 10047)
1*10047Ssam #ifndef lint
2*10047Ssam static	char *sccsid = "@(#)mv.c	4.7 (Berkeley) 83/01/01";
3*10047Ssam #endif
48839Smckusick 
51216Sbill /*
61216Sbill  * mv file1 file2
71216Sbill  */
8*10047Ssam #include <sys/param.h>
9*10047Ssam #include <sys/stat.h>
101216Sbill 
111216Sbill #include <stdio.h>
12*10047Ssam #include <dir.h>
13*10047Ssam #include <errno.h>
141216Sbill #include <signal.h>
151216Sbill 
161216Sbill #define	DELIM	'/'
171216Sbill #define MODEBITS 07777
181216Sbill 
19*10047Ssam #define	ISDIR(st)	(((st).st_mode&S_IFMT) == S_IFDIR)
20*10047Ssam #define	ISLNK(st)	(((st).st_mode&S_IFMT) == S_IFLNK)
21*10047Ssam #define	ISREG(st)	(((st).st_mode&S_IFMT) == S_IFREG)
22*10047Ssam #define	ISDEV(st) \
23*10047Ssam 	(((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
24*10047Ssam 
251216Sbill char	*sprintf();
261216Sbill char	*dname();
271216Sbill struct	stat s1, s2;
28*10047Ssam int	iflag = 0;	/* interactive mode */
29*10047Ssam int	fflag = 0;	/* force overwriting */
30*10047Ssam extern	unsigned errno;
311216Sbill 
321216Sbill main(argc, argv)
33*10047Ssam 	register char *argv[];
341216Sbill {
351216Sbill 	register i, r;
361931Sroot 	register char *arg;
371216Sbill 
381216Sbill 	if (argc < 2)
391216Sbill 		goto usage;
40*10047Ssam 	while (argc > 1 && *argv[1] == '-') {
411216Sbill 		argc--;
421931Sroot 		arg = *++argv;
431216Sbill 
441931Sroot 		/*
45*10047Ssam 		 * all files following a null option
46*10047Ssam 		 * are considered file names
471931Sroot 		 */
48*10047Ssam 		if (*(arg+1) == '\0')
49*10047Ssam 			break;
50*10047Ssam 		while (*++arg != '\0') switch (*arg) {
511931Sroot 
52*10047Ssam 		case 'i':
53*10047Ssam 			iflag++;
54*10047Ssam 			break;
551931Sroot 
56*10047Ssam 		case 'f':
57*10047Ssam 			fflag++;
58*10047Ssam 			break;
591216Sbill 
60*10047Ssam 		default:
61*10047Ssam 			goto usage;
62*10047Ssam 		}
631216Sbill 	}
641216Sbill 	if (argc < 3)
651216Sbill 		goto usage;
66*10047Ssam 	setuid(getuid());
67*10047Ssam 	r = 0;
68*10047Ssam 	if (argc > 3) {
69*10047Ssam 		register char *dest;
70*10047Ssam 
71*10047Ssam 		dest = argv[argc-1];
72*10047Ssam 		if (stat(dest, &s2) < 0 || !ISDIR(s2))
731216Sbill 			goto usage;
74*10047Ssam 		for (i = 1; i < argc-1; i++)
75*10047Ssam 			r |= movewithshortname(argv[i], dest);
76*10047Ssam 		exit(r);
771216Sbill 	}
78*10047Ssam 	if (stat(argv[2], &s2) >= 0 && ISDIR(s2)) {
79*10047Ssam 		struct stat s1;
80*10047Ssam 
81*10047Ssam 		if (stat(argv[1], &s1) >= 0 && ISDIR(s1))
82*10047Ssam 			r = move(argv[1], argv[2]);
83*10047Ssam 		else
84*10047Ssam 			r = movewithshortname(argv[1], argv[2]);
85*10047Ssam 	} else
86*10047Ssam 		r = move(argv[1], argv[2]);
87*10047Ssam 	exit(r);
88*10047Ssam 	/*NOTREACHED*/
891216Sbill usage:
90*10047Ssam 	fprintf(stderr,
91*10047Ssam "usage: mv [-if] f1 ... fn d1 (where `fn' is a file or directory)\n");
92*10047Ssam 	return (1);
931216Sbill }
941216Sbill 
95*10047Ssam movewithshortname(src, dest)
96*10047Ssam 	char *src, *dest;
97*10047Ssam {
98*10047Ssam 	register char *shortname;
99*10047Ssam 	char target[MAXPATHLEN + 1];
100*10047Ssam 
101*10047Ssam 	shortname = dname(src);
102*10047Ssam 	if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
103*10047Ssam 		error("%s/%s: pathname too long", dest,
104*10047Ssam 			shortname);
105*10047Ssam 		return (1);
106*10047Ssam 	}
107*10047Ssam 	sprintf(target, "%s/%s", dest, shortname);
108*10047Ssam 	return (move(src, target));
109*10047Ssam }
110*10047Ssam 
1111216Sbill move(source, target)
112*10047Ssam 	char *source, *target;
1131216Sbill {
1141216Sbill 
1158839Smckusick 	if (lstat(source, &s1) < 0) {
116*10047Ssam 		error("cannot access %s", source);
117*10047Ssam 		return (1);
1181216Sbill 	}
119*10047Ssam 	/*
120*10047Ssam 	 * First, try to rename source to destination.
121*10047Ssam 	 * The only reason we continue on failure is if
122*10047Ssam 	 * the move is on a nondirectory and not across
123*10047Ssam 	 * file systems.
124*10047Ssam 	 */
125*10047Ssam 	if (lstat(target, &s2) >= 0) {
126*10047Ssam 		if (iflag && !fflag && query("remove %s? ", target) == 0)
127*10047Ssam 			return (1);
128*10047Ssam 		if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
129*10047Ssam 			error("%s and %s are identical", source, target);
130*10047Ssam 			return (1);
1311216Sbill 		}
132*10047Ssam 		if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
133*10047Ssam 			if (query("override protection %o for %s? ",
134*10047Ssam 			  s2.st_mode & MODEBITS, target) == 0)
135*10047Ssam 				return (1);
1361216Sbill 		}
137*10047Ssam 		if (rename(source, target) >= 0)
138*10047Ssam 			return (0);
139*10047Ssam 		if (errno != EXDEV) {
140*10047Ssam 			Perror2(source, "rename");
141*10047Ssam 			return (1);
142*10047Ssam 		}
143*10047Ssam 		if (ISDIR(s1)) {
144*10047Ssam 			error("can't mv directories across file systems");
145*10047Ssam 			return (1);
146*10047Ssam 		}
147*10047Ssam 		if (unlink(target) < 0) {
148*10047Ssam 			error("cannot unlink %s", target);
149*10047Ssam 			return (1);
150*10047Ssam 		}
151*10047Ssam 	} else {
152*10047Ssam 		if (rename(source, target) >= 0)
153*10047Ssam 			return (0);
154*10047Ssam 		if (ISDIR(s1)) {
155*10047Ssam 			Perror2(source, "rename");
156*10047Ssam 			return (1);
157*10047Ssam 		}
1581216Sbill 	}
159*10047Ssam 	/*
160*10047Ssam 	 * File can't be renamed, try to recreate the symbolic
161*10047Ssam 	 * link or special device, or copy the file wholesale
162*10047Ssam 	 * between file systems.
163*10047Ssam 	 */
164*10047Ssam 	if (ISLNK(s1)) {
1658839Smckusick 		register m;
166*10047Ssam 		char symln[MAXPATHLEN];
1678839Smckusick 
1688839Smckusick 		if (readlink(source, symln, sizeof (symln)) < 0) {
169*10047Ssam 			Perror(source);
1708839Smckusick 			return (1);
1718839Smckusick 		}
1728839Smckusick 		m = umask(~(s1.st_mode & MODEBITS));
1738839Smckusick 		if (symlink(symln, target) < 0) {
174*10047Ssam 			Perror(target);
1758839Smckusick 			return (1);
1768839Smckusick 		}
177*10047Ssam 		(void) umask(m);
178*10047Ssam 		goto cleanup;
179*10047Ssam 	}
180*10047Ssam 	if (ISDEV(s1)) {
181*10047Ssam 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
182*10047Ssam 			Perror(target);
183*10047Ssam 			return (1);
184*10047Ssam 		}
185*10047Ssam 		(void) utime(target, &s1.st_atime);
186*10047Ssam 		goto cleanup;
187*10047Ssam 	}
188*10047Ssam 	if (ISREG(s1)) {
189*10047Ssam 		int i, c, status;
190*10047Ssam 
1911216Sbill 		i = fork();
1921216Sbill 		if (i == -1) {
193*10047Ssam 			error("try again");
194*10047Ssam 			return (1);
1951216Sbill 		}
1961216Sbill 		if (i == 0) {
1971216Sbill 			execl("/bin/cp", "cp", source, target, 0);
198*10047Ssam 			error("cannot exec /bin/cp");
1991216Sbill 			exit(1);
2001216Sbill 		}
2011216Sbill 		while ((c = wait(&status)) != i && c != -1)
2021216Sbill 			;
2031216Sbill 		if (status != 0)
204*10047Ssam 			return (1);
205*10047Ssam 		(void) utime(target, &s1.st_atime);
206*10047Ssam 		goto cleanup;
2071216Sbill 	}
208*10047Ssam 	error("%s: unknown file type %o", source, s1.st_mode);
209*10047Ssam 	return (1);
2101216Sbill 
211*10047Ssam cleanup:
2121216Sbill 	if (unlink(source) < 0) {
213*10047Ssam 		error("cannot unlink %s", source);
214*10047Ssam 		return (1);
2151216Sbill 	}
216*10047Ssam 	return (0);
2171216Sbill }
2181216Sbill 
219*10047Ssam /*VARARGS*/
220*10047Ssam query(prompt, a1, a2)
221*10047Ssam 	char *a1;
2221216Sbill {
223*10047Ssam 	register char i, c;
2241216Sbill 
225*10047Ssam 	fprintf(stderr, prompt, a1, a2);
226*10047Ssam 	i = c = getchar();
227*10047Ssam 	while (c != '\n' && c != EOF)
228*10047Ssam 		c = getchar();
229*10047Ssam 	return (i == 'y');
2301216Sbill }
2311216Sbill 
2321216Sbill char *
2331216Sbill dname(name)
234*10047Ssam 	register char *name;
2351216Sbill {
2361216Sbill 	register char *p;
2371216Sbill 
2381216Sbill 	p = name;
2391216Sbill 	while (*p)
2401216Sbill 		if (*p++ == DELIM && *p)
2411216Sbill 			name = p;
2421216Sbill 	return name;
2431216Sbill }
2441216Sbill 
245*10047Ssam /*VARARGS*/
246*10047Ssam error(fmt, a1, a2)
247*10047Ssam 	char *fmt;
2481216Sbill {
2491216Sbill 
250*10047Ssam 	fprintf(stderr, "mv: ");
251*10047Ssam 	fprintf(stderr, fmt, a1, a2);
252*10047Ssam 	fprintf(stderr, "\n");
253*10047Ssam }
2541216Sbill 
255*10047Ssam Perror(s)
256*10047Ssam 	char *s;
257*10047Ssam {
258*10047Ssam 	char buf[MAXPATHLEN + 10];
259*10047Ssam 
260*10047Ssam 	sprintf(buf, "mv: %s", s);
261*10047Ssam 	perror(buf);
2621216Sbill }
2631216Sbill 
264*10047Ssam Perror2(s1, s2)
265*10047Ssam 	char *s1, *s2;
2661216Sbill {
267*10047Ssam 	char buf[MAXPATHLEN + 20];
268*10047Ssam 
269*10047Ssam 	sprintf(buf, "mv: %s: %s", s1, s2);
270*10047Ssam 	perror(buf);
2711216Sbill }
272