xref: /csrg-svn/bin/mv/mv.c (revision 19845)
1*19845Sdist /*
2*19845Sdist  * Copyright (c) 1980 Regents of the University of California.
3*19845Sdist  * All rights reserved.  The Berkeley software License Agreement
4*19845Sdist  * specifies the terms and conditions for redistribution.
5*19845Sdist  */
6*19845Sdist 
710047Ssam #ifndef lint
8*19845Sdist char copyright[] =
9*19845Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10*19845Sdist  All rights reserved.\n";
11*19845Sdist #endif not lint
128839Smckusick 
13*19845Sdist #ifndef lint
14*19845Sdist static char sccsid[] = "@(#)mv.c	5.1 (Berkeley) 04/30/85";
15*19845Sdist #endif not lint
16*19845Sdist 
171216Sbill /*
181216Sbill  * mv file1 file2
191216Sbill  */
2010047Ssam #include <sys/param.h>
2110047Ssam #include <sys/stat.h>
221216Sbill 
231216Sbill #include <stdio.h>
2413489Ssam #include <sys/dir.h>
2510047Ssam #include <errno.h>
261216Sbill #include <signal.h>
271216Sbill 
281216Sbill #define	DELIM	'/'
291216Sbill #define MODEBITS 07777
301216Sbill 
3110047Ssam #define	ISDIR(st)	(((st).st_mode&S_IFMT) == S_IFDIR)
3210047Ssam #define	ISLNK(st)	(((st).st_mode&S_IFMT) == S_IFLNK)
3310047Ssam #define	ISREG(st)	(((st).st_mode&S_IFMT) == S_IFREG)
3410047Ssam #define	ISDEV(st) \
3510047Ssam 	(((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
3610047Ssam 
371216Sbill char	*sprintf();
381216Sbill char	*dname();
391216Sbill struct	stat s1, s2;
4010047Ssam int	iflag = 0;	/* interactive mode */
4110047Ssam int	fflag = 0;	/* force overwriting */
4210047Ssam extern	unsigned errno;
431216Sbill 
441216Sbill main(argc, argv)
4510047Ssam 	register char *argv[];
461216Sbill {
471216Sbill 	register i, r;
481931Sroot 	register char *arg;
4910641Smckusick 	char *dest;
501216Sbill 
511216Sbill 	if (argc < 2)
521216Sbill 		goto usage;
5310047Ssam 	while (argc > 1 && *argv[1] == '-') {
541216Sbill 		argc--;
551931Sroot 		arg = *++argv;
561216Sbill 
571931Sroot 		/*
5810047Ssam 		 * all files following a null option
5910047Ssam 		 * are considered file names
601931Sroot 		 */
6110047Ssam 		if (*(arg+1) == '\0')
6210047Ssam 			break;
6310047Ssam 		while (*++arg != '\0') switch (*arg) {
641931Sroot 
6510047Ssam 		case 'i':
6610047Ssam 			iflag++;
6710047Ssam 			break;
681931Sroot 
6910047Ssam 		case 'f':
7010047Ssam 			fflag++;
7110047Ssam 			break;
721216Sbill 
7310047Ssam 		default:
7410047Ssam 			goto usage;
7510047Ssam 		}
761216Sbill 	}
771216Sbill 	if (argc < 3)
781216Sbill 		goto usage;
7910641Smckusick 	dest = argv[argc-1];
8010642Smckusick 	if (stat(dest, &s2) >= 0 && ISDIR(s2)) {
8110114Ssam 		r = 0;
8210047Ssam 		for (i = 1; i < argc-1; i++)
8310047Ssam 			r |= movewithshortname(argv[i], dest);
8410047Ssam 		exit(r);
851216Sbill 	}
8610641Smckusick 	if (argc > 3)
8710641Smckusick 		goto usage;
8810641Smckusick 	r = move(argv[1], argv[2]);
8910047Ssam 	exit(r);
9010047Ssam 	/*NOTREACHED*/
911216Sbill usage:
9210047Ssam 	fprintf(stderr,
9310642Smckusick "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n");
9410047Ssam 	return (1);
951216Sbill }
961216Sbill 
9710047Ssam movewithshortname(src, dest)
9810047Ssam 	char *src, *dest;
9910047Ssam {
10010047Ssam 	register char *shortname;
10110047Ssam 	char target[MAXPATHLEN + 1];
10210047Ssam 
10310047Ssam 	shortname = dname(src);
10410047Ssam 	if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
10510047Ssam 		error("%s/%s: pathname too long", dest,
10610047Ssam 			shortname);
10710047Ssam 		return (1);
10810047Ssam 	}
10910047Ssam 	sprintf(target, "%s/%s", dest, shortname);
11010047Ssam 	return (move(src, target));
11110047Ssam }
11210047Ssam 
1131216Sbill move(source, target)
11410047Ssam 	char *source, *target;
1151216Sbill {
11611642Ssam 	int targetexists;
1171216Sbill 
1188839Smckusick 	if (lstat(source, &s1) < 0) {
11910047Ssam 		error("cannot access %s", source);
12010047Ssam 		return (1);
1211216Sbill 	}
12210047Ssam 	/*
12310047Ssam 	 * First, try to rename source to destination.
12410047Ssam 	 * The only reason we continue on failure is if
12510047Ssam 	 * the move is on a nondirectory and not across
12610047Ssam 	 * file systems.
12710047Ssam 	 */
12811642Ssam 	targetexists = lstat(target, &s2) >= 0;
12911642Ssam 	if (targetexists) {
13010047Ssam 		if (iflag && !fflag && query("remove %s? ", target) == 0)
13110047Ssam 			return (1);
13210047Ssam 		if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
13310047Ssam 			error("%s and %s are identical", source, target);
13410047Ssam 			return (1);
1351216Sbill 		}
13610047Ssam 		if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
13710047Ssam 			if (query("override protection %o for %s? ",
13810047Ssam 			  s2.st_mode & MODEBITS, target) == 0)
13910047Ssam 				return (1);
1401216Sbill 		}
1411216Sbill 	}
14211642Ssam 	if (rename(source, target) >= 0)
14311642Ssam 		return (0);
14411642Ssam 	if (errno != EXDEV) {
14511642Ssam 		Perror2(source, "rename");
14611642Ssam 		return (1);
14711642Ssam 	}
14811642Ssam 	if (ISDIR(s1)) {
14911642Ssam 		error("can't mv directories across file systems");
15011642Ssam 		return (1);
15111642Ssam 	}
15211642Ssam 	if (targetexists && unlink(target) < 0) {
15311642Ssam 		error("cannot unlink %s", target);
15411642Ssam 		return (1);
15511642Ssam 	}
15610047Ssam 	/*
15710047Ssam 	 * File can't be renamed, try to recreate the symbolic
15810047Ssam 	 * link or special device, or copy the file wholesale
15910047Ssam 	 * between file systems.
16010047Ssam 	 */
16110047Ssam 	if (ISLNK(s1)) {
1628839Smckusick 		register m;
16310047Ssam 		char symln[MAXPATHLEN];
1648839Smckusick 
1658839Smckusick 		if (readlink(source, symln, sizeof (symln)) < 0) {
16610047Ssam 			Perror(source);
1678839Smckusick 			return (1);
1688839Smckusick 		}
1698839Smckusick 		m = umask(~(s1.st_mode & MODEBITS));
1708839Smckusick 		if (symlink(symln, target) < 0) {
17110047Ssam 			Perror(target);
1728839Smckusick 			return (1);
1738839Smckusick 		}
17410047Ssam 		(void) umask(m);
17510047Ssam 		goto cleanup;
17610047Ssam 	}
17710047Ssam 	if (ISDEV(s1)) {
17810166Ssam 		time_t tv[2];
17910166Ssam 
18010047Ssam 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
18110047Ssam 			Perror(target);
18210047Ssam 			return (1);
18310047Ssam 		}
18410166Ssam 		/* kludge prior to utimes */
18510166Ssam 		tv[0] = s1.st_atime;
18610166Ssam 		tv[1] = s1.st_mtime;
18710166Ssam 		(void) utime(target, tv);
18810047Ssam 		goto cleanup;
18910047Ssam 	}
19010047Ssam 	if (ISREG(s1)) {
19110047Ssam 		int i, c, status;
19210166Ssam 		time_t tv[2];
19310047Ssam 
1941216Sbill 		i = fork();
1951216Sbill 		if (i == -1) {
19610047Ssam 			error("try again");
19710047Ssam 			return (1);
1981216Sbill 		}
1991216Sbill 		if (i == 0) {
2001216Sbill 			execl("/bin/cp", "cp", source, target, 0);
20110047Ssam 			error("cannot exec /bin/cp");
2021216Sbill 			exit(1);
2031216Sbill 		}
2041216Sbill 		while ((c = wait(&status)) != i && c != -1)
2051216Sbill 			;
2061216Sbill 		if (status != 0)
20710047Ssam 			return (1);
20810166Ssam 		/* kludge prior to utimes */
20910166Ssam 		tv[0] = s1.st_atime;
21010166Ssam 		tv[1] = s1.st_mtime;
21110166Ssam 		(void) utime(target, tv);
21210047Ssam 		goto cleanup;
2131216Sbill 	}
21410047Ssam 	error("%s: unknown file type %o", source, s1.st_mode);
21510047Ssam 	return (1);
2161216Sbill 
21710047Ssam cleanup:
2181216Sbill 	if (unlink(source) < 0) {
21910047Ssam 		error("cannot unlink %s", source);
22010047Ssam 		return (1);
2211216Sbill 	}
22210047Ssam 	return (0);
2231216Sbill }
2241216Sbill 
22510047Ssam /*VARARGS*/
22610047Ssam query(prompt, a1, a2)
22710047Ssam 	char *a1;
2281216Sbill {
22910047Ssam 	register char i, c;
2301216Sbill 
23110047Ssam 	fprintf(stderr, prompt, a1, a2);
23210047Ssam 	i = c = getchar();
23310047Ssam 	while (c != '\n' && c != EOF)
23410047Ssam 		c = getchar();
23510047Ssam 	return (i == 'y');
2361216Sbill }
2371216Sbill 
2381216Sbill char *
2391216Sbill dname(name)
24010047Ssam 	register char *name;
2411216Sbill {
2421216Sbill 	register char *p;
2431216Sbill 
2441216Sbill 	p = name;
2451216Sbill 	while (*p)
2461216Sbill 		if (*p++ == DELIM && *p)
2471216Sbill 			name = p;
2481216Sbill 	return name;
2491216Sbill }
2501216Sbill 
25110047Ssam /*VARARGS*/
25210047Ssam error(fmt, a1, a2)
25310047Ssam 	char *fmt;
2541216Sbill {
2551216Sbill 
25610047Ssam 	fprintf(stderr, "mv: ");
25710047Ssam 	fprintf(stderr, fmt, a1, a2);
25810047Ssam 	fprintf(stderr, "\n");
25910047Ssam }
2601216Sbill 
26110047Ssam Perror(s)
26210047Ssam 	char *s;
26310047Ssam {
26410047Ssam 	char buf[MAXPATHLEN + 10];
26510047Ssam 
26610047Ssam 	sprintf(buf, "mv: %s", s);
26710047Ssam 	perror(buf);
2681216Sbill }
2691216Sbill 
27010047Ssam Perror2(s1, s2)
27110047Ssam 	char *s1, *s2;
2721216Sbill {
27310047Ssam 	char buf[MAXPATHLEN + 20];
27410047Ssam 
27510047Ssam 	sprintf(buf, "mv: %s: %s", s1, s2);
27610047Ssam 	perror(buf);
2771216Sbill }
278