xref: /csrg-svn/bin/mv/mv.c (revision 30907)
119845Sdist /*
219845Sdist  * Copyright (c) 1980 Regents of the University of California.
319845Sdist  * All rights reserved.  The Berkeley software License Agreement
419845Sdist  * specifies the terms and conditions for redistribution.
519845Sdist  */
619845Sdist 
710047Ssam #ifndef lint
819845Sdist char copyright[] =
919845Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\
1019845Sdist  All rights reserved.\n";
1119845Sdist #endif not lint
128839Smckusick 
1319845Sdist #ifndef lint
14*30907Slepreau static char sccsid[] = "@(#)mv.c	5.4 (Berkeley) 04/13/87";
1519845Sdist #endif not lint
1619845Sdist 
171216Sbill /*
181216Sbill  * mv file1 file2
191216Sbill  */
2010047Ssam #include <sys/param.h>
2110047Ssam #include <sys/stat.h>
2222601Sserge #include <sys/time.h>
231216Sbill 
241216Sbill #include <stdio.h>
2513489Ssam #include <sys/dir.h>
2610047Ssam #include <errno.h>
271216Sbill #include <signal.h>
281216Sbill 
291216Sbill #define	DELIM	'/'
301216Sbill #define MODEBITS 07777
311216Sbill 
3210047Ssam #define	ISDIR(st)	(((st).st_mode&S_IFMT) == S_IFDIR)
3310047Ssam #define	ISLNK(st)	(((st).st_mode&S_IFMT) == S_IFLNK)
3410047Ssam #define	ISREG(st)	(((st).st_mode&S_IFMT) == S_IFREG)
3510047Ssam #define	ISDEV(st) \
3610047Ssam 	(((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
3710047Ssam 
381216Sbill char	*sprintf();
391216Sbill char	*dname();
401216Sbill struct	stat s1, s2;
4110047Ssam int	iflag = 0;	/* interactive mode */
4210047Ssam int	fflag = 0;	/* force overwriting */
4310047Ssam extern	unsigned errno;
441216Sbill 
451216Sbill main(argc, argv)
4610047Ssam 	register char *argv[];
471216Sbill {
481216Sbill 	register i, r;
491931Sroot 	register char *arg;
5010641Smckusick 	char *dest;
511216Sbill 
521216Sbill 	if (argc < 2)
531216Sbill 		goto usage;
5410047Ssam 	while (argc > 1 && *argv[1] == '-') {
551216Sbill 		argc--;
561931Sroot 		arg = *++argv;
571216Sbill 
581931Sroot 		/*
5910047Ssam 		 * all files following a null option
6010047Ssam 		 * are considered file names
611931Sroot 		 */
6210047Ssam 		if (*(arg+1) == '\0')
6310047Ssam 			break;
6410047Ssam 		while (*++arg != '\0') switch (*arg) {
651931Sroot 
6610047Ssam 		case 'i':
6710047Ssam 			iflag++;
6810047Ssam 			break;
691931Sroot 
7010047Ssam 		case 'f':
7110047Ssam 			fflag++;
7210047Ssam 			break;
731216Sbill 
7410047Ssam 		default:
7510047Ssam 			goto usage;
7610047Ssam 		}
771216Sbill 	}
781216Sbill 	if (argc < 3)
791216Sbill 		goto usage;
8010641Smckusick 	dest = argv[argc-1];
8110642Smckusick 	if (stat(dest, &s2) >= 0 && ISDIR(s2)) {
8210114Ssam 		r = 0;
8310047Ssam 		for (i = 1; i < argc-1; i++)
8410047Ssam 			r |= movewithshortname(argv[i], dest);
8510047Ssam 		exit(r);
861216Sbill 	}
8710641Smckusick 	if (argc > 3)
8810641Smckusick 		goto usage;
8910641Smckusick 	r = move(argv[1], argv[2]);
9010047Ssam 	exit(r);
9110047Ssam 	/*NOTREACHED*/
921216Sbill usage:
9310047Ssam 	fprintf(stderr,
9410642Smckusick "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n");
9510047Ssam 	return (1);
961216Sbill }
971216Sbill 
9810047Ssam movewithshortname(src, dest)
9910047Ssam 	char *src, *dest;
10010047Ssam {
10110047Ssam 	register char *shortname;
10210047Ssam 	char target[MAXPATHLEN + 1];
10310047Ssam 
10410047Ssam 	shortname = dname(src);
10510047Ssam 	if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
10610047Ssam 		error("%s/%s: pathname too long", dest,
10710047Ssam 			shortname);
10810047Ssam 		return (1);
10910047Ssam 	}
11010047Ssam 	sprintf(target, "%s/%s", dest, shortname);
11110047Ssam 	return (move(src, target));
11210047Ssam }
11310047Ssam 
1141216Sbill move(source, target)
11510047Ssam 	char *source, *target;
1161216Sbill {
11711642Ssam 	int targetexists;
1181216Sbill 
1198839Smckusick 	if (lstat(source, &s1) < 0) {
12022601Sserge 		Perror2(source, "Cannot access");
12110047Ssam 		return (1);
1221216Sbill 	}
12310047Ssam 	/*
12410047Ssam 	 * First, try to rename source to destination.
12510047Ssam 	 * The only reason we continue on failure is if
12610047Ssam 	 * the move is on a nondirectory and not across
12710047Ssam 	 * file systems.
12810047Ssam 	 */
12911642Ssam 	targetexists = lstat(target, &s2) >= 0;
13011642Ssam 	if (targetexists) {
13110047Ssam 		if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
13210047Ssam 			error("%s and %s are identical", source, target);
13310047Ssam 			return (1);
1341216Sbill 		}
13522601Sserge 		if (iflag && !fflag && isatty(fileno(stdin)) &&
13622601Sserge 		    query("remove %s? ", target) == 0)
13722601Sserge 			return (1);
13810047Ssam 		if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
13910047Ssam 			if (query("override protection %o for %s? ",
14010047Ssam 			  s2.st_mode & MODEBITS, target) == 0)
14110047Ssam 				return (1);
1421216Sbill 		}
1431216Sbill 	}
14411642Ssam 	if (rename(source, target) >= 0)
14511642Ssam 		return (0);
14611642Ssam 	if (errno != EXDEV) {
14728202Slepreau 		Perror2(errno == ENOENT && targetexists == 0 ? target : source,
14828202Slepreau 		    "rename");
14911642Ssam 		return (1);
15011642Ssam 	}
15111642Ssam 	if (ISDIR(s1)) {
15211642Ssam 		error("can't mv directories across file systems");
15311642Ssam 		return (1);
15411642Ssam 	}
15511642Ssam 	if (targetexists && unlink(target) < 0) {
15622601Sserge 		Perror2(target, "Cannot unlink");
15711642Ssam 		return (1);
15811642Ssam 	}
15910047Ssam 	/*
16010047Ssam 	 * File can't be renamed, try to recreate the symbolic
16110047Ssam 	 * link or special device, or copy the file wholesale
16210047Ssam 	 * between file systems.
16310047Ssam 	 */
16410047Ssam 	if (ISLNK(s1)) {
1658839Smckusick 		register m;
16622601Sserge 		char symln[MAXPATHLEN + 1];
1678839Smckusick 
16822601Sserge 		m = readlink(source, symln, sizeof (symln) - 1);
16922601Sserge 		if (m < 0) {
17010047Ssam 			Perror(source);
1718839Smckusick 			return (1);
1728839Smckusick 		}
17322601Sserge 		symln[m] = '\0';
17422601Sserge 
175*30907Slepreau 		(void) umask(~(s1.st_mode & MODEBITS));
1768839Smckusick 		if (symlink(symln, target) < 0) {
17710047Ssam 			Perror(target);
1788839Smckusick 			return (1);
1798839Smckusick 		}
18010047Ssam 		goto cleanup;
18110047Ssam 	}
182*30907Slepreau 	(void) umask(0);
18310047Ssam 	if (ISDEV(s1)) {
18422601Sserge 		struct timeval tv[2];
18510166Ssam 
18610047Ssam 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
18710047Ssam 			Perror(target);
18810047Ssam 			return (1);
18910047Ssam 		}
19022601Sserge 
19122601Sserge 		tv[0].tv_sec = s1.st_atime;
19222601Sserge 		tv[0].tv_usec = 0;
19322601Sserge 		tv[1].tv_sec = s1.st_mtime;
19422601Sserge 		tv[1].tv_usec = 0;
19522601Sserge 		(void) utimes(target, tv);
19610047Ssam 		goto cleanup;
19710047Ssam 	}
19810047Ssam 	if (ISREG(s1)) {
19922601Sserge 		register int fi, fo, n;
20022601Sserge 		struct timeval tv[2];
20122601Sserge 		char buf[MAXBSIZE];
20210047Ssam 
20322601Sserge 		fi = open(source, 0);
20422601Sserge 		if (fi < 0) {
20522601Sserge 			Perror(source);
20610047Ssam 			return (1);
2071216Sbill 		}
20822601Sserge 
20922601Sserge 		fo = creat(target, s1.st_mode & MODEBITS);
21022601Sserge 		if (fo < 0) {
21122601Sserge 			Perror(target);
21222601Sserge 			close(fi);
21322601Sserge 			return (1);
2141216Sbill 		}
21522601Sserge 
21622601Sserge 		for (;;) {
21722601Sserge 			n = read(fi, buf, sizeof buf);
21822601Sserge 			if (n == 0) {
21922601Sserge 				break;
22022601Sserge 			} else if (n < 0) {
22122601Sserge 				Perror2(source, "read");
22222601Sserge 				close(fi);
22322601Sserge 				close(fo);
22422601Sserge 				return (1);
22522601Sserge 			} else if (write(fo, buf, n) != n) {
22622601Sserge 				Perror2(target, "write");
22722601Sserge 				close(fi);
22822601Sserge 				close(fo);
22922601Sserge 				return (1);
23022601Sserge 			}
23122601Sserge 		}
23222601Sserge 
23322601Sserge 		close(fi);
23422601Sserge 		close(fo);
23522601Sserge 
23622601Sserge 		tv[0].tv_sec = s1.st_atime;
23722601Sserge 		tv[0].tv_usec = 0;
23822601Sserge 		tv[1].tv_sec = s1.st_mtime;
23922601Sserge 		tv[1].tv_usec = 0;
24022601Sserge 		(void) utimes(target, tv);
24110047Ssam 		goto cleanup;
2421216Sbill 	}
24310047Ssam 	error("%s: unknown file type %o", source, s1.st_mode);
24410047Ssam 	return (1);
2451216Sbill 
24610047Ssam cleanup:
2471216Sbill 	if (unlink(source) < 0) {
24822601Sserge 		Perror2(source, "Cannot unlink");
24910047Ssam 		return (1);
2501216Sbill 	}
25110047Ssam 	return (0);
2521216Sbill }
2531216Sbill 
25410047Ssam /*VARARGS*/
25510047Ssam query(prompt, a1, a2)
25610047Ssam 	char *a1;
2571216Sbill {
25822601Sserge 	register int i, c;
2591216Sbill 
26010047Ssam 	fprintf(stderr, prompt, a1, a2);
26110047Ssam 	i = c = getchar();
26210047Ssam 	while (c != '\n' && c != EOF)
26310047Ssam 		c = getchar();
26410047Ssam 	return (i == 'y');
2651216Sbill }
2661216Sbill 
2671216Sbill char *
2681216Sbill dname(name)
26910047Ssam 	register char *name;
2701216Sbill {
2711216Sbill 	register char *p;
2721216Sbill 
2731216Sbill 	p = name;
2741216Sbill 	while (*p)
2751216Sbill 		if (*p++ == DELIM && *p)
2761216Sbill 			name = p;
2771216Sbill 	return name;
2781216Sbill }
2791216Sbill 
28010047Ssam /*VARARGS*/
28110047Ssam error(fmt, a1, a2)
28210047Ssam 	char *fmt;
2831216Sbill {
2841216Sbill 
28510047Ssam 	fprintf(stderr, "mv: ");
28610047Ssam 	fprintf(stderr, fmt, a1, a2);
28710047Ssam 	fprintf(stderr, "\n");
28810047Ssam }
2891216Sbill 
29010047Ssam Perror(s)
29110047Ssam 	char *s;
29210047Ssam {
29310047Ssam 	char buf[MAXPATHLEN + 10];
29410047Ssam 
29510047Ssam 	sprintf(buf, "mv: %s", s);
29610047Ssam 	perror(buf);
2971216Sbill }
2981216Sbill 
29910047Ssam Perror2(s1, s2)
30010047Ssam 	char *s1, *s2;
3011216Sbill {
30210047Ssam 	char buf[MAXPATHLEN + 20];
30310047Ssam 
30410047Ssam 	sprintf(buf, "mv: %s: %s", s1, s2);
30510047Ssam 	perror(buf);
3061216Sbill }
307