xref: /csrg-svn/bin/mv/mv.c (revision 33382)
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*33382Sbostic static char sccsid[] = "@(#)mv.c	5.6 (Berkeley) 01/21/88";
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>
23*33382Sbostic #include <sys/file.h>
241216Sbill 
251216Sbill #include <stdio.h>
2613489Ssam #include <sys/dir.h>
2710047Ssam #include <errno.h>
281216Sbill #include <signal.h>
291216Sbill 
301216Sbill #define	DELIM	'/'
311216Sbill #define MODEBITS 07777
321216Sbill 
3310047Ssam #define	ISDIR(st)	(((st).st_mode&S_IFMT) == S_IFDIR)
3410047Ssam #define	ISLNK(st)	(((st).st_mode&S_IFMT) == S_IFLNK)
3510047Ssam #define	ISREG(st)	(((st).st_mode&S_IFMT) == S_IFREG)
3610047Ssam #define	ISDEV(st) \
3710047Ssam 	(((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
3810047Ssam 
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 	}
11032415Sbostic 	(void)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 		}
135*33382Sbostic 		if (!fflag && isatty(fileno(stdin)))
136*33382Sbostic 			if (iflag) {
137*33382Sbostic 				if (!query("remove %s? ", target))
138*33382Sbostic 					return (1);
139*33382Sbostic 			}
140*33382Sbostic 			else if (access(target, W_OK) < 0 &&
141*33382Sbostic 			    !query("override protection %o for %s? ",
142*33382Sbostic 			    s2.st_mode & MODEBITS, target))
14310047Ssam 				return (1);
1441216Sbill 	}
14511642Ssam 	if (rename(source, target) >= 0)
14611642Ssam 		return (0);
14711642Ssam 	if (errno != EXDEV) {
14828202Slepreau 		Perror2(errno == ENOENT && targetexists == 0 ? target : source,
14928202Slepreau 		    "rename");
15011642Ssam 		return (1);
15111642Ssam 	}
15211642Ssam 	if (ISDIR(s1)) {
15311642Ssam 		error("can't mv directories across file systems");
15411642Ssam 		return (1);
15511642Ssam 	}
15611642Ssam 	if (targetexists && unlink(target) < 0) {
15722601Sserge 		Perror2(target, "Cannot unlink");
15811642Ssam 		return (1);
15911642Ssam 	}
16010047Ssam 	/*
16110047Ssam 	 * File can't be renamed, try to recreate the symbolic
16210047Ssam 	 * link or special device, or copy the file wholesale
16310047Ssam 	 * between file systems.
16410047Ssam 	 */
16510047Ssam 	if (ISLNK(s1)) {
1668839Smckusick 		register m;
16722601Sserge 		char symln[MAXPATHLEN + 1];
1688839Smckusick 
16922601Sserge 		m = readlink(source, symln, sizeof (symln) - 1);
17022601Sserge 		if (m < 0) {
17110047Ssam 			Perror(source);
1728839Smckusick 			return (1);
1738839Smckusick 		}
17422601Sserge 		symln[m] = '\0';
17522601Sserge 
17630907Slepreau 		(void) umask(~(s1.st_mode & MODEBITS));
1778839Smckusick 		if (symlink(symln, target) < 0) {
17810047Ssam 			Perror(target);
1798839Smckusick 			return (1);
1808839Smckusick 		}
18110047Ssam 		goto cleanup;
18210047Ssam 	}
18330907Slepreau 	(void) umask(0);
18410047Ssam 	if (ISDEV(s1)) {
18522601Sserge 		struct timeval tv[2];
18610166Ssam 
18710047Ssam 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
18810047Ssam 			Perror(target);
18910047Ssam 			return (1);
19010047Ssam 		}
19122601Sserge 
19222601Sserge 		tv[0].tv_sec = s1.st_atime;
19322601Sserge 		tv[0].tv_usec = 0;
19422601Sserge 		tv[1].tv_sec = s1.st_mtime;
19522601Sserge 		tv[1].tv_usec = 0;
19622601Sserge 		(void) utimes(target, tv);
19710047Ssam 		goto cleanup;
19810047Ssam 	}
19910047Ssam 	if (ISREG(s1)) {
20022601Sserge 		register int fi, fo, n;
20122601Sserge 		struct timeval tv[2];
20222601Sserge 		char buf[MAXBSIZE];
20310047Ssam 
20422601Sserge 		fi = open(source, 0);
20522601Sserge 		if (fi < 0) {
20622601Sserge 			Perror(source);
20710047Ssam 			return (1);
2081216Sbill 		}
20922601Sserge 
21022601Sserge 		fo = creat(target, s1.st_mode & MODEBITS);
21122601Sserge 		if (fo < 0) {
21222601Sserge 			Perror(target);
21322601Sserge 			close(fi);
21422601Sserge 			return (1);
2151216Sbill 		}
21622601Sserge 
21722601Sserge 		for (;;) {
21822601Sserge 			n = read(fi, buf, sizeof buf);
21922601Sserge 			if (n == 0) {
22022601Sserge 				break;
22122601Sserge 			} else if (n < 0) {
22222601Sserge 				Perror2(source, "read");
22322601Sserge 				close(fi);
22422601Sserge 				close(fo);
22522601Sserge 				return (1);
22622601Sserge 			} else if (write(fo, buf, n) != n) {
22722601Sserge 				Perror2(target, "write");
22822601Sserge 				close(fi);
22922601Sserge 				close(fo);
23022601Sserge 				return (1);
23122601Sserge 			}
23222601Sserge 		}
23322601Sserge 
23422601Sserge 		close(fi);
23522601Sserge 		close(fo);
23622601Sserge 
23722601Sserge 		tv[0].tv_sec = s1.st_atime;
23822601Sserge 		tv[0].tv_usec = 0;
23922601Sserge 		tv[1].tv_sec = s1.st_mtime;
24022601Sserge 		tv[1].tv_usec = 0;
24122601Sserge 		(void) utimes(target, tv);
24210047Ssam 		goto cleanup;
2431216Sbill 	}
24410047Ssam 	error("%s: unknown file type %o", source, s1.st_mode);
24510047Ssam 	return (1);
2461216Sbill 
24710047Ssam cleanup:
2481216Sbill 	if (unlink(source) < 0) {
24922601Sserge 		Perror2(source, "Cannot unlink");
25010047Ssam 		return (1);
2511216Sbill 	}
25210047Ssam 	return (0);
2531216Sbill }
2541216Sbill 
25510047Ssam /*VARARGS*/
25610047Ssam query(prompt, a1, a2)
25710047Ssam 	char *a1;
2581216Sbill {
25922601Sserge 	register int i, c;
2601216Sbill 
26110047Ssam 	fprintf(stderr, prompt, a1, a2);
26210047Ssam 	i = c = getchar();
26310047Ssam 	while (c != '\n' && c != EOF)
26410047Ssam 		c = getchar();
26510047Ssam 	return (i == 'y');
2661216Sbill }
2671216Sbill 
2681216Sbill char *
2691216Sbill dname(name)
27010047Ssam 	register char *name;
2711216Sbill {
2721216Sbill 	register char *p;
2731216Sbill 
2741216Sbill 	p = name;
2751216Sbill 	while (*p)
2761216Sbill 		if (*p++ == DELIM && *p)
2771216Sbill 			name = p;
2781216Sbill 	return name;
2791216Sbill }
2801216Sbill 
28110047Ssam /*VARARGS*/
28210047Ssam error(fmt, a1, a2)
28310047Ssam 	char *fmt;
2841216Sbill {
2851216Sbill 
28610047Ssam 	fprintf(stderr, "mv: ");
28710047Ssam 	fprintf(stderr, fmt, a1, a2);
28810047Ssam 	fprintf(stderr, "\n");
28910047Ssam }
2901216Sbill 
29110047Ssam Perror(s)
29210047Ssam 	char *s;
29310047Ssam {
29410047Ssam 	char buf[MAXPATHLEN + 10];
29532415Sbostic 
29632415Sbostic 	(void)sprintf(buf, "mv: %s", s);
29710047Ssam 	perror(buf);
2981216Sbill }
2991216Sbill 
30010047Ssam Perror2(s1, s2)
30110047Ssam 	char *s1, *s2;
3021216Sbill {
30310047Ssam 	char buf[MAXPATHLEN + 20];
30410047Ssam 
30532415Sbostic 	(void)sprintf(buf, "mv: %s: %s", s1, s2);
30610047Ssam 	perror(buf);
3071216Sbill }
308