xref: /csrg-svn/bin/mv/mv.c (revision 32415)
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*32415Sbostic static char sccsid[] = "@(#)mv.c	5.5 (Berkeley) 10/22/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	*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 	}
109*32415Sbostic 	(void)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) {
11922601Sserge 		Perror2(source, "Cannot access");
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 (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
13110047Ssam 			error("%s and %s are identical", source, target);
13210047Ssam 			return (1);
1331216Sbill 		}
13422601Sserge 		if (iflag && !fflag && isatty(fileno(stdin)) &&
13522601Sserge 		    query("remove %s? ", target) == 0)
13622601Sserge 			return (1);
13710047Ssam 		if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
13810047Ssam 			if (query("override protection %o for %s? ",
13910047Ssam 			  s2.st_mode & MODEBITS, target) == 0)
14010047Ssam 				return (1);
1411216Sbill 		}
1421216Sbill 	}
14311642Ssam 	if (rename(source, target) >= 0)
14411642Ssam 		return (0);
14511642Ssam 	if (errno != EXDEV) {
14628202Slepreau 		Perror2(errno == ENOENT && targetexists == 0 ? target : source,
14728202Slepreau 		    "rename");
14811642Ssam 		return (1);
14911642Ssam 	}
15011642Ssam 	if (ISDIR(s1)) {
15111642Ssam 		error("can't mv directories across file systems");
15211642Ssam 		return (1);
15311642Ssam 	}
15411642Ssam 	if (targetexists && unlink(target) < 0) {
15522601Sserge 		Perror2(target, "Cannot unlink");
15611642Ssam 		return (1);
15711642Ssam 	}
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;
16522601Sserge 		char symln[MAXPATHLEN + 1];
1668839Smckusick 
16722601Sserge 		m = readlink(source, symln, sizeof (symln) - 1);
16822601Sserge 		if (m < 0) {
16910047Ssam 			Perror(source);
1708839Smckusick 			return (1);
1718839Smckusick 		}
17222601Sserge 		symln[m] = '\0';
17322601Sserge 
17430907Slepreau 		(void) umask(~(s1.st_mode & MODEBITS));
1758839Smckusick 		if (symlink(symln, target) < 0) {
17610047Ssam 			Perror(target);
1778839Smckusick 			return (1);
1788839Smckusick 		}
17910047Ssam 		goto cleanup;
18010047Ssam 	}
18130907Slepreau 	(void) umask(0);
18210047Ssam 	if (ISDEV(s1)) {
18322601Sserge 		struct timeval tv[2];
18410166Ssam 
18510047Ssam 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
18610047Ssam 			Perror(target);
18710047Ssam 			return (1);
18810047Ssam 		}
18922601Sserge 
19022601Sserge 		tv[0].tv_sec = s1.st_atime;
19122601Sserge 		tv[0].tv_usec = 0;
19222601Sserge 		tv[1].tv_sec = s1.st_mtime;
19322601Sserge 		tv[1].tv_usec = 0;
19422601Sserge 		(void) utimes(target, tv);
19510047Ssam 		goto cleanup;
19610047Ssam 	}
19710047Ssam 	if (ISREG(s1)) {
19822601Sserge 		register int fi, fo, n;
19922601Sserge 		struct timeval tv[2];
20022601Sserge 		char buf[MAXBSIZE];
20110047Ssam 
20222601Sserge 		fi = open(source, 0);
20322601Sserge 		if (fi < 0) {
20422601Sserge 			Perror(source);
20510047Ssam 			return (1);
2061216Sbill 		}
20722601Sserge 
20822601Sserge 		fo = creat(target, s1.st_mode & MODEBITS);
20922601Sserge 		if (fo < 0) {
21022601Sserge 			Perror(target);
21122601Sserge 			close(fi);
21222601Sserge 			return (1);
2131216Sbill 		}
21422601Sserge 
21522601Sserge 		for (;;) {
21622601Sserge 			n = read(fi, buf, sizeof buf);
21722601Sserge 			if (n == 0) {
21822601Sserge 				break;
21922601Sserge 			} else if (n < 0) {
22022601Sserge 				Perror2(source, "read");
22122601Sserge 				close(fi);
22222601Sserge 				close(fo);
22322601Sserge 				return (1);
22422601Sserge 			} else if (write(fo, buf, n) != n) {
22522601Sserge 				Perror2(target, "write");
22622601Sserge 				close(fi);
22722601Sserge 				close(fo);
22822601Sserge 				return (1);
22922601Sserge 			}
23022601Sserge 		}
23122601Sserge 
23222601Sserge 		close(fi);
23322601Sserge 		close(fo);
23422601Sserge 
23522601Sserge 		tv[0].tv_sec = s1.st_atime;
23622601Sserge 		tv[0].tv_usec = 0;
23722601Sserge 		tv[1].tv_sec = s1.st_mtime;
23822601Sserge 		tv[1].tv_usec = 0;
23922601Sserge 		(void) utimes(target, tv);
24010047Ssam 		goto cleanup;
2411216Sbill 	}
24210047Ssam 	error("%s: unknown file type %o", source, s1.st_mode);
24310047Ssam 	return (1);
2441216Sbill 
24510047Ssam cleanup:
2461216Sbill 	if (unlink(source) < 0) {
24722601Sserge 		Perror2(source, "Cannot unlink");
24810047Ssam 		return (1);
2491216Sbill 	}
25010047Ssam 	return (0);
2511216Sbill }
2521216Sbill 
25310047Ssam /*VARARGS*/
25410047Ssam query(prompt, a1, a2)
25510047Ssam 	char *a1;
2561216Sbill {
25722601Sserge 	register int i, c;
2581216Sbill 
25910047Ssam 	fprintf(stderr, prompt, a1, a2);
26010047Ssam 	i = c = getchar();
26110047Ssam 	while (c != '\n' && c != EOF)
26210047Ssam 		c = getchar();
26310047Ssam 	return (i == 'y');
2641216Sbill }
2651216Sbill 
2661216Sbill char *
2671216Sbill dname(name)
26810047Ssam 	register char *name;
2691216Sbill {
2701216Sbill 	register char *p;
2711216Sbill 
2721216Sbill 	p = name;
2731216Sbill 	while (*p)
2741216Sbill 		if (*p++ == DELIM && *p)
2751216Sbill 			name = p;
2761216Sbill 	return name;
2771216Sbill }
2781216Sbill 
27910047Ssam /*VARARGS*/
28010047Ssam error(fmt, a1, a2)
28110047Ssam 	char *fmt;
2821216Sbill {
2831216Sbill 
28410047Ssam 	fprintf(stderr, "mv: ");
28510047Ssam 	fprintf(stderr, fmt, a1, a2);
28610047Ssam 	fprintf(stderr, "\n");
28710047Ssam }
2881216Sbill 
28910047Ssam Perror(s)
29010047Ssam 	char *s;
29110047Ssam {
29210047Ssam 	char buf[MAXPATHLEN + 10];
293*32415Sbostic 
294*32415Sbostic 	(void)sprintf(buf, "mv: %s", s);
29510047Ssam 	perror(buf);
2961216Sbill }
2971216Sbill 
29810047Ssam Perror2(s1, s2)
29910047Ssam 	char *s1, *s2;
3001216Sbill {
30110047Ssam 	char buf[MAXPATHLEN + 20];
30210047Ssam 
303*32415Sbostic 	(void)sprintf(buf, "mv: %s: %s", s1, s2);
30410047Ssam 	perror(buf);
3051216Sbill }
306