xref: /csrg-svn/bin/mv/mv.c (revision 34045)
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";
11*34045Sbostic #endif /* not lint */
128839Smckusick 
1319845Sdist #ifndef lint
14*34045Sbostic static char sccsid[] = "@(#)mv.c	5.7 (Berkeley) 04/21/88";
15*34045Sbostic #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>
2333382Sbostic #include <sys/file.h>
241216Sbill #include <stdio.h>
2510047Ssam #include <errno.h>
261216Sbill 
271216Sbill #define	DELIM	'/'
281216Sbill #define MODEBITS 07777
291216Sbill 
3010047Ssam #define	ISDIR(st)	(((st).st_mode&S_IFMT) == S_IFDIR)
3110047Ssam #define	ISLNK(st)	(((st).st_mode&S_IFMT) == S_IFLNK)
3210047Ssam #define	ISREG(st)	(((st).st_mode&S_IFMT) == S_IFREG)
3310047Ssam #define	ISDEV(st) \
3410047Ssam 	(((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
3510047Ssam 
361216Sbill char	*dname();
3710047Ssam int	iflag = 0;	/* interactive mode */
3810047Ssam int	fflag = 0;	/* force overwriting */
3910047Ssam extern	unsigned errno;
401216Sbill 
411216Sbill main(argc, argv)
42*34045Sbostic 	register int argc;
43*34045Sbostic 	register char **argv;
441216Sbill {
45*34045Sbostic 	extern int optind;
46*34045Sbostic 	struct stat st;
47*34045Sbostic 	int ch, r;
4810641Smckusick 	char *dest;
491216Sbill 
50*34045Sbostic 	while ((ch = getopt(argc, argv, "-fi")) != EOF)
51*34045Sbostic 		switch((char)ch) {
52*34045Sbostic 		case '-':
53*34045Sbostic 			goto endarg;
54*34045Sbostic 		case 'f':
55*34045Sbostic 			fflag++;
5610047Ssam 			break;
5710047Ssam 		case 'i':
5810047Ssam 			iflag++;
5910047Ssam 			break;
60*34045Sbostic 		case '?':
6110047Ssam 		default:
62*34045Sbostic 			usage();
6310047Ssam 		}
64*34045Sbostic endarg:	argv += optind;
65*34045Sbostic 	argc -= optind;
66*34045Sbostic 
67*34045Sbostic 	if (argc < 2)
68*34045Sbostic 		usage();
69*34045Sbostic 	dest = argv[argc - 1];
70*34045Sbostic 	if (stat(dest, &st) >= 0 && ISDIR(st)) {
71*34045Sbostic 		for (r = 0; --argc; ++argv)
72*34045Sbostic 			r |= movewithshortname(*argv, dest);
7310047Ssam 		exit(r);
741216Sbill 	}
75*34045Sbostic 	if (argc != 2)
76*34045Sbostic 		usage();
77*34045Sbostic 	r = move(argv[0], argv[1]);
7810047Ssam 	exit(r);
791216Sbill }
801216Sbill 
8110047Ssam movewithshortname(src, dest)
8210047Ssam 	char *src, *dest;
8310047Ssam {
8410047Ssam 	register char *shortname;
8510047Ssam 	char target[MAXPATHLEN + 1];
8610047Ssam 
8710047Ssam 	shortname = dname(src);
8810047Ssam 	if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
8910047Ssam 		error("%s/%s: pathname too long", dest,
9010047Ssam 			shortname);
9110047Ssam 		return (1);
9210047Ssam 	}
9332415Sbostic 	(void)sprintf(target, "%s/%s", dest, shortname);
9410047Ssam 	return (move(src, target));
9510047Ssam }
9610047Ssam 
971216Sbill move(source, target)
9810047Ssam 	char *source, *target;
991216Sbill {
10011642Ssam 	int targetexists;
101*34045Sbostic 	struct stat s1, s2;
1021216Sbill 
1038839Smckusick 	if (lstat(source, &s1) < 0) {
10422601Sserge 		Perror2(source, "Cannot access");
10510047Ssam 		return (1);
1061216Sbill 	}
10710047Ssam 	/*
10810047Ssam 	 * First, try to rename source to destination.
10910047Ssam 	 * The only reason we continue on failure is if
11010047Ssam 	 * the move is on a nondirectory and not across
11110047Ssam 	 * file systems.
11210047Ssam 	 */
11311642Ssam 	targetexists = lstat(target, &s2) >= 0;
11411642Ssam 	if (targetexists) {
11510047Ssam 		if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
11610047Ssam 			error("%s and %s are identical", source, target);
11710047Ssam 			return (1);
1181216Sbill 		}
11933382Sbostic 		if (!fflag && isatty(fileno(stdin)))
12033382Sbostic 			if (iflag) {
12133382Sbostic 				if (!query("remove %s? ", target))
12233382Sbostic 					return (1);
12333382Sbostic 			}
12433382Sbostic 			else if (access(target, W_OK) < 0 &&
12533382Sbostic 			    !query("override protection %o for %s? ",
12633382Sbostic 			    s2.st_mode & MODEBITS, target))
12710047Ssam 				return (1);
1281216Sbill 	}
12911642Ssam 	if (rename(source, target) >= 0)
13011642Ssam 		return (0);
13111642Ssam 	if (errno != EXDEV) {
13228202Slepreau 		Perror2(errno == ENOENT && targetexists == 0 ? target : source,
13328202Slepreau 		    "rename");
13411642Ssam 		return (1);
13511642Ssam 	}
13611642Ssam 	if (ISDIR(s1)) {
13711642Ssam 		error("can't mv directories across file systems");
13811642Ssam 		return (1);
13911642Ssam 	}
14011642Ssam 	if (targetexists && unlink(target) < 0) {
14122601Sserge 		Perror2(target, "Cannot unlink");
14211642Ssam 		return (1);
14311642Ssam 	}
14410047Ssam 	/*
14510047Ssam 	 * File can't be renamed, try to recreate the symbolic
14610047Ssam 	 * link or special device, or copy the file wholesale
14710047Ssam 	 * between file systems.
14810047Ssam 	 */
14910047Ssam 	if (ISLNK(s1)) {
1508839Smckusick 		register m;
15122601Sserge 		char symln[MAXPATHLEN + 1];
1528839Smckusick 
15322601Sserge 		m = readlink(source, symln, sizeof (symln) - 1);
15422601Sserge 		if (m < 0) {
15510047Ssam 			Perror(source);
1568839Smckusick 			return (1);
1578839Smckusick 		}
15822601Sserge 		symln[m] = '\0';
15922601Sserge 
16030907Slepreau 		(void) umask(~(s1.st_mode & MODEBITS));
1618839Smckusick 		if (symlink(symln, target) < 0) {
16210047Ssam 			Perror(target);
1638839Smckusick 			return (1);
1648839Smckusick 		}
16510047Ssam 		goto cleanup;
16610047Ssam 	}
16730907Slepreau 	(void) umask(0);
16810047Ssam 	if (ISDEV(s1)) {
16922601Sserge 		struct timeval tv[2];
17010166Ssam 
17110047Ssam 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
17210047Ssam 			Perror(target);
17310047Ssam 			return (1);
17410047Ssam 		}
17522601Sserge 
17622601Sserge 		tv[0].tv_sec = s1.st_atime;
17722601Sserge 		tv[0].tv_usec = 0;
17822601Sserge 		tv[1].tv_sec = s1.st_mtime;
17922601Sserge 		tv[1].tv_usec = 0;
18022601Sserge 		(void) utimes(target, tv);
18110047Ssam 		goto cleanup;
18210047Ssam 	}
18310047Ssam 	if (ISREG(s1)) {
18422601Sserge 		register int fi, fo, n;
18522601Sserge 		struct timeval tv[2];
18622601Sserge 		char buf[MAXBSIZE];
18710047Ssam 
18822601Sserge 		fi = open(source, 0);
18922601Sserge 		if (fi < 0) {
19022601Sserge 			Perror(source);
19110047Ssam 			return (1);
1921216Sbill 		}
19322601Sserge 
19422601Sserge 		fo = creat(target, s1.st_mode & MODEBITS);
19522601Sserge 		if (fo < 0) {
19622601Sserge 			Perror(target);
19722601Sserge 			close(fi);
19822601Sserge 			return (1);
1991216Sbill 		}
20022601Sserge 
20122601Sserge 		for (;;) {
20222601Sserge 			n = read(fi, buf, sizeof buf);
20322601Sserge 			if (n == 0) {
20422601Sserge 				break;
20522601Sserge 			} else if (n < 0) {
20622601Sserge 				Perror2(source, "read");
20722601Sserge 				close(fi);
20822601Sserge 				close(fo);
20922601Sserge 				return (1);
21022601Sserge 			} else if (write(fo, buf, n) != n) {
21122601Sserge 				Perror2(target, "write");
21222601Sserge 				close(fi);
21322601Sserge 				close(fo);
21422601Sserge 				return (1);
21522601Sserge 			}
21622601Sserge 		}
21722601Sserge 
21822601Sserge 		close(fi);
21922601Sserge 		close(fo);
22022601Sserge 
22122601Sserge 		tv[0].tv_sec = s1.st_atime;
22222601Sserge 		tv[0].tv_usec = 0;
22322601Sserge 		tv[1].tv_sec = s1.st_mtime;
22422601Sserge 		tv[1].tv_usec = 0;
22522601Sserge 		(void) utimes(target, tv);
22610047Ssam 		goto cleanup;
2271216Sbill 	}
22810047Ssam 	error("%s: unknown file type %o", source, s1.st_mode);
22910047Ssam 	return (1);
2301216Sbill 
23110047Ssam cleanup:
2321216Sbill 	if (unlink(source) < 0) {
23322601Sserge 		Perror2(source, "Cannot unlink");
23410047Ssam 		return (1);
2351216Sbill 	}
23610047Ssam 	return (0);
2371216Sbill }
2381216Sbill 
23910047Ssam /*VARARGS*/
24010047Ssam query(prompt, a1, a2)
24110047Ssam 	char *a1;
2421216Sbill {
24322601Sserge 	register int i, c;
2441216Sbill 
24510047Ssam 	fprintf(stderr, prompt, a1, a2);
24610047Ssam 	i = c = getchar();
24710047Ssam 	while (c != '\n' && c != EOF)
24810047Ssam 		c = getchar();
24910047Ssam 	return (i == 'y');
2501216Sbill }
2511216Sbill 
2521216Sbill char *
2531216Sbill dname(name)
25410047Ssam 	register char *name;
2551216Sbill {
2561216Sbill 	register char *p;
2571216Sbill 
2581216Sbill 	p = name;
2591216Sbill 	while (*p)
2601216Sbill 		if (*p++ == DELIM && *p)
2611216Sbill 			name = p;
2621216Sbill 	return name;
2631216Sbill }
2641216Sbill 
26510047Ssam /*VARARGS*/
26610047Ssam error(fmt, a1, a2)
26710047Ssam 	char *fmt;
2681216Sbill {
2691216Sbill 
27010047Ssam 	fprintf(stderr, "mv: ");
27110047Ssam 	fprintf(stderr, fmt, a1, a2);
27210047Ssam 	fprintf(stderr, "\n");
27310047Ssam }
2741216Sbill 
27510047Ssam Perror(s)
27610047Ssam 	char *s;
27710047Ssam {
27810047Ssam 	char buf[MAXPATHLEN + 10];
27932415Sbostic 
28032415Sbostic 	(void)sprintf(buf, "mv: %s", s);
28110047Ssam 	perror(buf);
2821216Sbill }
2831216Sbill 
28410047Ssam Perror2(s1, s2)
28510047Ssam 	char *s1, *s2;
2861216Sbill {
28710047Ssam 	char buf[MAXPATHLEN + 20];
28810047Ssam 
28932415Sbostic 	(void)sprintf(buf, "mv: %s: %s", s1, s2);
29010047Ssam 	perror(buf);
2911216Sbill }
292*34045Sbostic 
293*34045Sbostic usage()
294*34045Sbostic {
295*34045Sbostic 	fputs("usage: mv [-if] file1 file2 or mv [-if] file/directory ... directory\n", stderr);
296*34045Sbostic 	exit(1);
297*34045Sbostic }
298