xref: /csrg-svn/bin/mv/mv.c (revision 1931)
1*1931Sroot static char *sccsid = "@(#)mv.c	4.2 (Berkeley) 12/18/80";
21216Sbill /*
31216Sbill  * mv file1 file2
41216Sbill  */
51216Sbill 
61216Sbill #include <stdio.h>
71216Sbill #include <sys/types.h>
81216Sbill #include <sys/stat.h>
91216Sbill #include <sys/dir.h>
101216Sbill #include <signal.h>
111216Sbill 
121216Sbill #define	DOT	"."
131216Sbill #define	DOTDOT	".."
141216Sbill #define	DELIM	'/'
151216Sbill #define SDELIM "/"
161216Sbill #define	MAXN	100
171216Sbill #define MODEBITS 07777
181216Sbill #define ROOTINO 2
191216Sbill 
201216Sbill char	*pname();
211216Sbill char	*sprintf();
221216Sbill char	*dname();
231216Sbill struct	stat s1, s2;
241216Sbill int	iflag = 0;	/* interactive flag. If this flag is set,
251216Sbill 			 * the user is queried before files are
261216Sbill 			 * destroyed by cp.
271216Sbill 			 */
281216Sbill int	fflag = 0;	/* force flag. supercedes all restrictions */
291216Sbill 
301216Sbill main(argc, argv)
311216Sbill register char *argv[];
321216Sbill {
331216Sbill 	register i, r;
34*1931Sroot 	register char *arg;
351216Sbill 
361216Sbill 	/* get the flag(s) */
371216Sbill 
381216Sbill 	if (argc < 2)
391216Sbill 		goto usage;
40*1931Sroot 	while (argc>1 && *argv[1] == '-') {
411216Sbill 		argc--;
42*1931Sroot 		arg = *++argv;
431216Sbill 
44*1931Sroot 		/*
45*1931Sroot 		 *  all files following a null option are considered file names
46*1931Sroot 		 */
47*1931Sroot 		if (*(arg+1) == '\0') break;
48*1931Sroot 
49*1931Sroot 		while (*++arg != '\0')
50*1931Sroot 			switch (*arg) {
51*1931Sroot 
521216Sbill 			/* interactive mode */
531216Sbill 			case 'i':
541216Sbill 				iflag++;
551216Sbill 				break;
561216Sbill 
571216Sbill 			/* force moves */
581216Sbill 			case 'f':
591216Sbill 				fflag++;
601216Sbill 				break;
611216Sbill 
621216Sbill 			/* don't live with bad options */
631216Sbill 			default:
641216Sbill 				goto usage;
651216Sbill 			}
661216Sbill 	}
671216Sbill 	if (argc < 3)
681216Sbill 		goto usage;
691216Sbill 	if (stat(argv[1], &s1) < 0) {
701216Sbill 		fprintf(stderr, "mv: cannot access %s\n", argv[1]);
711216Sbill 		return(1);
721216Sbill 	}
731216Sbill 	if ((s1.st_mode & S_IFMT) == S_IFDIR) {
741216Sbill 		if (argc != 3)
751216Sbill 			goto usage;
761216Sbill 		return mvdir(argv[1], argv[2]);
771216Sbill 	}
781216Sbill 	setuid(getuid());
791216Sbill 	if (argc > 3)
801216Sbill 		if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR)
811216Sbill 			goto usage;
821216Sbill 	r = 0;
831216Sbill 	for (i=1; i<argc-1; i++)
841216Sbill 		r |= move(argv[i], argv[argc-1]);
851216Sbill 	return(r);
861216Sbill usage:
87*1931Sroot 	fprintf(stderr, "usage: mv [-if] f1 f2; or mv [-if] d1 d2; or mv [-if] f1 ... fn d1\n");
881216Sbill 	return(1);
891216Sbill }
901216Sbill 
911216Sbill move(source, target)
921216Sbill char *source, *target;
931216Sbill {
941216Sbill 	register c, i;
951216Sbill 	int	status;
961216Sbill 	char	buf[MAXN];
971216Sbill 
981216Sbill 	if (stat(source, &s1) < 0) {
991216Sbill 		fprintf(stderr, "mv: cannot access %s\n", source);
1001216Sbill 		return(1);
1011216Sbill 	}
1021216Sbill 	if ((s1.st_mode & S_IFMT) == S_IFDIR) {
1031216Sbill 		fprintf(stderr, "mv: directory rename only\n");
1041216Sbill 		return(1);
1051216Sbill 	}
1061216Sbill 	if (stat(target, &s2) >= 0) {
1071216Sbill 		if ((s2.st_mode & S_IFMT) == S_IFDIR) {
1081216Sbill 			sprintf(buf, "%s/%s", target, dname(source));
1091216Sbill 			target = buf;
1101216Sbill 		}
1111216Sbill 		if (stat(target, &s2) >= 0) {
1121216Sbill 			if ((s2.st_mode & S_IFMT) == S_IFDIR) {
1131216Sbill 				fprintf(stderr, "mv: %s is a directory\n", target);
1141216Sbill 				return(1);
1151216Sbill 			} else if (iflag && !fflag) {
1161216Sbill 				fprintf(stderr, "remove %s? ", target);
1171216Sbill 				i = c = getchar();
1181216Sbill 				while (c != '\n' && c != EOF)
1191216Sbill 					c = getchar();
1201216Sbill 				if (i != 'y')
1211216Sbill 					return(1);
1221216Sbill 			}
1231216Sbill 			if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) {
1241216Sbill 				fprintf(stderr, "mv: %s and %s are identical\n",
1251216Sbill 						source, target);
1261216Sbill 				return(1);
1271216Sbill 			}
1281216Sbill 			if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
1291216Sbill 				fprintf(stderr, "override protection %o for %s? ",
1301216Sbill 					s2.st_mode & MODEBITS, target);
1311216Sbill 				i = c = getchar();
1321216Sbill 				while (c != '\n' && c != EOF)
1331216Sbill 					c = getchar();
1341216Sbill 				if (i != 'y')
1351216Sbill 					return(1);
1361216Sbill 			}
1371216Sbill 			if (unlink(target) < 0) {
1381216Sbill 				fprintf(stderr, "mv: cannot unlink %s\n", target);
1391216Sbill 				return(1);
1401216Sbill 			}
1411216Sbill 		}
1421216Sbill 	}
1431216Sbill 	if (link(source, target) < 0) {
1441216Sbill 		i = fork();
1451216Sbill 		if (i == -1) {
1461216Sbill 			fprintf(stderr, "mv: try again\n");
1471216Sbill 			return(1);
1481216Sbill 		}
1491216Sbill 		if (i == 0) {
1501216Sbill 			execl("/bin/cp", "cp", source, target, 0);
1511216Sbill 			fprintf(stderr, "mv: cannot exec cp\n");
1521216Sbill 			exit(1);
1531216Sbill 		}
1541216Sbill 		while ((c = wait(&status)) != i && c != -1)
1551216Sbill 			;
1561216Sbill 		if (status != 0)
1571216Sbill 			return(1);
1581216Sbill 		utime(target, &s1.st_atime);
1591216Sbill 	}
1601216Sbill 	if (unlink(source) < 0) {
1611216Sbill 		fprintf(stderr, "mv: cannot unlink %s\n", source);
1621216Sbill 		return(1);
1631216Sbill 	}
1641216Sbill 	return(0);
1651216Sbill }
1661216Sbill 
1671216Sbill mvdir(source, target)
1681216Sbill char *source, *target;
1691216Sbill {
1701216Sbill 	register char *p;
1711216Sbill 	register i;
1721216Sbill 	char buf[MAXN];
1731216Sbill 	char c,cc;
1741216Sbill 
1751216Sbill 	if (stat(target, &s2) >= 0) {
1761216Sbill 		if ((s2.st_mode&S_IFMT) != S_IFDIR) {
1771216Sbill 			fprintf(stderr, "mv: %s exists\n", target);
1781216Sbill 			return(1);
1791216Sbill 		} else if (iflag && !fflag) {
1801216Sbill 			fprintf(stderr, "remove %s? ", target);
1811216Sbill 			cc = c = getchar();
1821216Sbill 			while (c != '\n' && c != EOF)
1831216Sbill 				c = getchar();
1841216Sbill 			if (cc != 'y')
1851216Sbill 				return(1);
1861216Sbill 		}
1871216Sbill 		if (strlen(target) > MAXN-DIRSIZ-2) {
1881216Sbill 			fprintf(stderr, "mv :target name too long\n");
1891216Sbill 			return(1);
1901216Sbill 		}
1911216Sbill 		strcpy(buf, target);
1921216Sbill 		target = buf;
1931216Sbill 		strcat(buf, SDELIM);
1941216Sbill 		strcat(buf, dname(source));
1951216Sbill 		if (stat(target, &s2) >= 0) {
1961216Sbill 			fprintf(stderr, "mv: %s exists\n", buf);
1971216Sbill 			return(1);
1981216Sbill 		}
1991216Sbill 	}
2001216Sbill 	if (strcmp(source, target) == 0) {
2011216Sbill 		fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n");
2021216Sbill 		return(1);
2031216Sbill 	}
2041216Sbill 	p = dname(source);
2051216Sbill 	if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') {
2061216Sbill 		fprintf(stderr, "mv: cannot rename %s\n", p);
2071216Sbill 		return(1);
2081216Sbill 	}
2091216Sbill 	if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) {
2101216Sbill 		fprintf(stderr, "mv: cannot locate parent\n");
2111216Sbill 		return(1);
2121216Sbill 	}
2131216Sbill 	if (access(pname(target), 2) < 0) {
2141216Sbill 		fprintf(stderr, "mv: no write access to %s\n", pname(target));
2151216Sbill 		return(1);
2161216Sbill 	}
2171216Sbill 	if (access(pname(source), 2) < 0) {
2181216Sbill 		fprintf(stderr, "mv: no write access to %s\n", pname(source));
2191216Sbill 		return(1);
2201216Sbill 	}
2211216Sbill 	if (access(source, 2) < 0) {
2221216Sbill 		fprintf(stderr, "mv: no write access to %s\n", source);
2231216Sbill 		return(1);
2241216Sbill 	}
2251216Sbill 	if (s1.st_dev != s2.st_dev) {
2261216Sbill 		fprintf(stderr, "mv: cannot move directories across devices\n");
2271216Sbill 		return(1);
2281216Sbill 	}
2291216Sbill 	if (s1.st_ino != s2.st_ino) {
2301216Sbill 		char dst[MAXN+5];
2311216Sbill 
2321216Sbill 		if (chkdot(source) || chkdot(target)) {
2331216Sbill 			fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT);
2341216Sbill 			return(1);
2351216Sbill 		}
2361216Sbill 		stat(source, &s1);
2371216Sbill 		if (check(pname(target), s1.st_ino))
2381216Sbill 			return(1);
2391216Sbill 		for (i = 1; i <= NSIG; i++)
2401216Sbill 			signal(i, SIG_IGN);
2411216Sbill 		if (link(source, target) < 0) {
2421216Sbill 			fprintf(stderr, "mv: cannot link %s to %s\n", target, source);
2431216Sbill 			return(1);
2441216Sbill 		}
2451216Sbill 		if (unlink(source) < 0) {
2461216Sbill 			fprintf(stderr, "mv: %s: cannot unlink\n", source);
2471216Sbill 			unlink(target);
2481216Sbill 			return(1);
2491216Sbill 		}
2501216Sbill 		strcat(dst, target);
2511216Sbill 		strcat(dst, "/");
2521216Sbill 		strcat(dst, DOTDOT);
2531216Sbill 		if (unlink(dst) < 0) {
2541216Sbill 			fprintf(stderr, "mv: %s: cannot unlink\n", dst);
2551216Sbill 			if (link(target, source) >= 0)
2561216Sbill 				unlink(target);
2571216Sbill 			return(1);
2581216Sbill 		}
2591216Sbill 		if (link(pname(target), dst) < 0) {
2601216Sbill 			fprintf(stderr, "mv: cannot link %s to %s\n",
2611216Sbill 				dst, pname(target));
2621216Sbill 			if (link(pname(source), dst) >= 0)
2631216Sbill 				if (link(target, source) >= 0)
2641216Sbill 					unlink(target);
2651216Sbill 			return(1);
2661216Sbill 		}
2671216Sbill 		return(0);
2681216Sbill 	}
2691216Sbill 	if (link(source, target) < 0) {
2701216Sbill 		fprintf(stderr, "mv: cannot link %s and %s\n",
2711216Sbill 			source, target);
2721216Sbill 		return(1);
2731216Sbill 	}
2741216Sbill 	if (unlink(source) < 0) {
2751216Sbill 		fprintf(stderr, "mv: ?? cannot unlink %s\n", source);
2761216Sbill 		return(1);
2771216Sbill 	}
2781216Sbill 	return(0);
2791216Sbill }
2801216Sbill 
2811216Sbill char *
2821216Sbill pname(name)
2831216Sbill register char *name;
2841216Sbill {
2851216Sbill 	register c;
2861216Sbill 	register char *p, *q;
2871216Sbill 	static	char buf[MAXN];
2881216Sbill 
2891216Sbill 	p = q = buf;
2901216Sbill 	while (c = *p++ = *name++)
2911216Sbill 		if (c == DELIM)
2921216Sbill 			q = p-1;
2931216Sbill 	if (q == buf && *q == DELIM)
2941216Sbill 		q++;
2951216Sbill 	*q = 0;
2961216Sbill 	return buf[0]? buf : DOT;
2971216Sbill }
2981216Sbill 
2991216Sbill char *
3001216Sbill dname(name)
3011216Sbill register char *name;
3021216Sbill {
3031216Sbill 	register char *p;
3041216Sbill 
3051216Sbill 	p = name;
3061216Sbill 	while (*p)
3071216Sbill 		if (*p++ == DELIM && *p)
3081216Sbill 			name = p;
3091216Sbill 	return name;
3101216Sbill }
3111216Sbill 
3121216Sbill check(spth, dinode)
3131216Sbill char *spth;
3141216Sbill ino_t dinode;
3151216Sbill {
3161216Sbill 	char nspth[MAXN];
3171216Sbill 	struct stat sbuf;
3181216Sbill 
3191216Sbill 	sbuf.st_ino = 0;
3201216Sbill 
3211216Sbill 	strcpy(nspth, spth);
3221216Sbill 	while (sbuf.st_ino != ROOTINO) {
3231216Sbill 		if (stat(nspth, &sbuf) < 0) {
3241216Sbill 			fprintf(stderr, "mv: cannot access %s\n", nspth);
3251216Sbill 			return(1);
3261216Sbill 		}
3271216Sbill 		if (sbuf.st_ino == dinode) {
3281216Sbill 			fprintf(stderr, "mv: cannot move a directory into itself\n");
3291216Sbill 			return(1);
3301216Sbill 		}
3311216Sbill 		if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) {
3321216Sbill 			fprintf(stderr, "mv: name too long\n");
3331216Sbill 			return(1);
3341216Sbill 		}
3351216Sbill 		strcat(nspth, SDELIM);
3361216Sbill 		strcat(nspth, DOTDOT);
3371216Sbill 	}
3381216Sbill 	return(0);
3391216Sbill }
3401216Sbill 
3411216Sbill chkdot(s)
3421216Sbill register char *s;
3431216Sbill {
3441216Sbill 	do {
3451216Sbill 		if (strcmp(dname(s), DOTDOT) == 0)
3461216Sbill 			return(1);
3471216Sbill 		s = pname(s);
3481216Sbill 	} while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0);
3491216Sbill 	return(0);
3501216Sbill }
351