xref: /csrg-svn/bin/mv/mv.c (revision 10166)
110047Ssam #ifndef lint
2*10166Ssam static	char *sccsid = "@(#)mv.c	4.9 (Berkeley) 83/01/05";
310047Ssam #endif
48839Smckusick 
51216Sbill /*
61216Sbill  * mv file1 file2
71216Sbill  */
810047Ssam #include <sys/param.h>
910047Ssam #include <sys/stat.h>
101216Sbill 
111216Sbill #include <stdio.h>
1210047Ssam #include <dir.h>
1310047Ssam #include <errno.h>
141216Sbill #include <signal.h>
151216Sbill 
161216Sbill #define	DELIM	'/'
171216Sbill #define MODEBITS 07777
181216Sbill 
1910047Ssam #define	ISDIR(st)	(((st).st_mode&S_IFMT) == S_IFDIR)
2010047Ssam #define	ISLNK(st)	(((st).st_mode&S_IFMT) == S_IFLNK)
2110047Ssam #define	ISREG(st)	(((st).st_mode&S_IFMT) == S_IFREG)
2210047Ssam #define	ISDEV(st) \
2310047Ssam 	(((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
2410047Ssam 
251216Sbill char	*sprintf();
261216Sbill char	*dname();
271216Sbill struct	stat s1, s2;
2810047Ssam int	iflag = 0;	/* interactive mode */
2910047Ssam int	fflag = 0;	/* force overwriting */
3010047Ssam extern	unsigned errno;
311216Sbill 
321216Sbill main(argc, argv)
3310047Ssam 	register char *argv[];
341216Sbill {
351216Sbill 	register i, r;
361931Sroot 	register char *arg;
371216Sbill 
381216Sbill 	if (argc < 2)
391216Sbill 		goto usage;
4010047Ssam 	while (argc > 1 && *argv[1] == '-') {
411216Sbill 		argc--;
421931Sroot 		arg = *++argv;
431216Sbill 
441931Sroot 		/*
4510047Ssam 		 * all files following a null option
4610047Ssam 		 * are considered file names
471931Sroot 		 */
4810047Ssam 		if (*(arg+1) == '\0')
4910047Ssam 			break;
5010047Ssam 		while (*++arg != '\0') switch (*arg) {
511931Sroot 
5210047Ssam 		case 'i':
5310047Ssam 			iflag++;
5410047Ssam 			break;
551931Sroot 
5610047Ssam 		case 'f':
5710047Ssam 			fflag++;
5810047Ssam 			break;
591216Sbill 
6010047Ssam 		default:
6110047Ssam 			goto usage;
6210047Ssam 		}
631216Sbill 	}
641216Sbill 	if (argc < 3)
651216Sbill 		goto usage;
6610047Ssam 	if (argc > 3) {
6710047Ssam 		register char *dest;
6810047Ssam 
6910047Ssam 		dest = argv[argc-1];
7010047Ssam 		if (stat(dest, &s2) < 0 || !ISDIR(s2))
711216Sbill 			goto usage;
7210114Ssam 		r = 0;
7310047Ssam 		for (i = 1; i < argc-1; i++)
7410047Ssam 			r |= movewithshortname(argv[i], dest);
7510047Ssam 		exit(r);
761216Sbill 	}
7710114Ssam 	if (lstat(argv[2], &s2) >= 0 && ISDIR(s2)) {
7810047Ssam 		struct stat s1;
7910047Ssam 
8010114Ssam 		if (lstat(argv[1], &s1) >= 0 && ISDIR(s1))
8110047Ssam 			r = move(argv[1], argv[2]);
8210047Ssam 		else
8310047Ssam 			r = movewithshortname(argv[1], argv[2]);
8410047Ssam 	} else
8510047Ssam 		r = move(argv[1], argv[2]);
8610047Ssam 	exit(r);
8710047Ssam 	/*NOTREACHED*/
881216Sbill usage:
8910047Ssam 	fprintf(stderr,
9010047Ssam "usage: mv [-if] f1 ... fn d1 (where `fn' is a file or directory)\n");
9110047Ssam 	return (1);
921216Sbill }
931216Sbill 
9410047Ssam movewithshortname(src, dest)
9510047Ssam 	char *src, *dest;
9610047Ssam {
9710047Ssam 	register char *shortname;
9810047Ssam 	char target[MAXPATHLEN + 1];
9910047Ssam 
10010047Ssam 	shortname = dname(src);
10110047Ssam 	if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
10210047Ssam 		error("%s/%s: pathname too long", dest,
10310047Ssam 			shortname);
10410047Ssam 		return (1);
10510047Ssam 	}
10610047Ssam 	sprintf(target, "%s/%s", dest, shortname);
10710047Ssam 	return (move(src, target));
10810047Ssam }
10910047Ssam 
1101216Sbill move(source, target)
11110047Ssam 	char *source, *target;
1121216Sbill {
1131216Sbill 
1148839Smckusick 	if (lstat(source, &s1) < 0) {
11510047Ssam 		error("cannot access %s", source);
11610047Ssam 		return (1);
1171216Sbill 	}
11810047Ssam 	/*
11910047Ssam 	 * First, try to rename source to destination.
12010047Ssam 	 * The only reason we continue on failure is if
12110047Ssam 	 * the move is on a nondirectory and not across
12210047Ssam 	 * file systems.
12310047Ssam 	 */
12410047Ssam 	if (lstat(target, &s2) >= 0) {
12510047Ssam 		if (iflag && !fflag && query("remove %s? ", target) == 0)
12610047Ssam 			return (1);
12710047Ssam 		if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
12810047Ssam 			error("%s and %s are identical", source, target);
12910047Ssam 			return (1);
1301216Sbill 		}
13110047Ssam 		if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
13210047Ssam 			if (query("override protection %o for %s? ",
13310047Ssam 			  s2.st_mode & MODEBITS, target) == 0)
13410047Ssam 				return (1);
1351216Sbill 		}
13610047Ssam 		if (rename(source, target) >= 0)
13710047Ssam 			return (0);
13810047Ssam 		if (errno != EXDEV) {
13910047Ssam 			Perror2(source, "rename");
14010047Ssam 			return (1);
14110047Ssam 		}
14210047Ssam 		if (ISDIR(s1)) {
14310047Ssam 			error("can't mv directories across file systems");
14410047Ssam 			return (1);
14510047Ssam 		}
14610047Ssam 		if (unlink(target) < 0) {
14710047Ssam 			error("cannot unlink %s", target);
14810047Ssam 			return (1);
14910047Ssam 		}
15010047Ssam 	} else {
15110047Ssam 		if (rename(source, target) >= 0)
15210047Ssam 			return (0);
15310047Ssam 		if (ISDIR(s1)) {
15410047Ssam 			Perror2(source, "rename");
15510047Ssam 			return (1);
15610047Ssam 		}
1571216Sbill 	}
15810047Ssam 	/*
15910047Ssam 	 * File can't be renamed, try to recreate the symbolic
16010047Ssam 	 * link or special device, or copy the file wholesale
16110047Ssam 	 * between file systems.
16210047Ssam 	 */
16310047Ssam 	if (ISLNK(s1)) {
1648839Smckusick 		register m;
16510047Ssam 		char symln[MAXPATHLEN];
1668839Smckusick 
1678839Smckusick 		if (readlink(source, symln, sizeof (symln)) < 0) {
16810047Ssam 			Perror(source);
1698839Smckusick 			return (1);
1708839Smckusick 		}
1718839Smckusick 		m = umask(~(s1.st_mode & MODEBITS));
1728839Smckusick 		if (symlink(symln, target) < 0) {
17310047Ssam 			Perror(target);
1748839Smckusick 			return (1);
1758839Smckusick 		}
17610047Ssam 		(void) umask(m);
17710047Ssam 		goto cleanup;
17810047Ssam 	}
17910047Ssam 	if (ISDEV(s1)) {
180*10166Ssam 		time_t tv[2];
181*10166Ssam 
18210047Ssam 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
18310047Ssam 			Perror(target);
18410047Ssam 			return (1);
18510047Ssam 		}
186*10166Ssam 		/* kludge prior to utimes */
187*10166Ssam 		tv[0] = s1.st_atime;
188*10166Ssam 		tv[1] = s1.st_mtime;
189*10166Ssam 		(void) utime(target, tv);
19010047Ssam 		goto cleanup;
19110047Ssam 	}
19210047Ssam 	if (ISREG(s1)) {
19310047Ssam 		int i, c, status;
194*10166Ssam 		time_t tv[2];
19510047Ssam 
1961216Sbill 		i = fork();
1971216Sbill 		if (i == -1) {
19810047Ssam 			error("try again");
19910047Ssam 			return (1);
2001216Sbill 		}
2011216Sbill 		if (i == 0) {
2021216Sbill 			execl("/bin/cp", "cp", source, target, 0);
20310047Ssam 			error("cannot exec /bin/cp");
2041216Sbill 			exit(1);
2051216Sbill 		}
2061216Sbill 		while ((c = wait(&status)) != i && c != -1)
2071216Sbill 			;
2081216Sbill 		if (status != 0)
20910047Ssam 			return (1);
210*10166Ssam 		/* kludge prior to utimes */
211*10166Ssam 		tv[0] = s1.st_atime;
212*10166Ssam 		tv[1] = s1.st_mtime;
213*10166Ssam 		(void) utime(target, tv);
21410047Ssam 		goto cleanup;
2151216Sbill 	}
21610047Ssam 	error("%s: unknown file type %o", source, s1.st_mode);
21710047Ssam 	return (1);
2181216Sbill 
21910047Ssam cleanup:
2201216Sbill 	if (unlink(source) < 0) {
22110047Ssam 		error("cannot unlink %s", source);
22210047Ssam 		return (1);
2231216Sbill 	}
22410047Ssam 	return (0);
2251216Sbill }
2261216Sbill 
22710047Ssam /*VARARGS*/
22810047Ssam query(prompt, a1, a2)
22910047Ssam 	char *a1;
2301216Sbill {
23110047Ssam 	register char i, c;
2321216Sbill 
23310047Ssam 	fprintf(stderr, prompt, a1, a2);
23410047Ssam 	i = c = getchar();
23510047Ssam 	while (c != '\n' && c != EOF)
23610047Ssam 		c = getchar();
23710047Ssam 	return (i == 'y');
2381216Sbill }
2391216Sbill 
2401216Sbill char *
2411216Sbill dname(name)
24210047Ssam 	register char *name;
2431216Sbill {
2441216Sbill 	register char *p;
2451216Sbill 
2461216Sbill 	p = name;
2471216Sbill 	while (*p)
2481216Sbill 		if (*p++ == DELIM && *p)
2491216Sbill 			name = p;
2501216Sbill 	return name;
2511216Sbill }
2521216Sbill 
25310047Ssam /*VARARGS*/
25410047Ssam error(fmt, a1, a2)
25510047Ssam 	char *fmt;
2561216Sbill {
2571216Sbill 
25810047Ssam 	fprintf(stderr, "mv: ");
25910047Ssam 	fprintf(stderr, fmt, a1, a2);
26010047Ssam 	fprintf(stderr, "\n");
26110047Ssam }
2621216Sbill 
26310047Ssam Perror(s)
26410047Ssam 	char *s;
26510047Ssam {
26610047Ssam 	char buf[MAXPATHLEN + 10];
26710047Ssam 
26810047Ssam 	sprintf(buf, "mv: %s", s);
26910047Ssam 	perror(buf);
2701216Sbill }
2711216Sbill 
27210047Ssam Perror2(s1, s2)
27310047Ssam 	char *s1, *s2;
2741216Sbill {
27510047Ssam 	char buf[MAXPATHLEN + 20];
27610047Ssam 
27710047Ssam 	sprintf(buf, "mv: %s: %s", s1, s2);
27810047Ssam 	perror(buf);
2791216Sbill }
280