xref: /original-bsd/bin/mv/mv.c (revision 57f376f9)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)mv.c	5.5 (Berkeley) 10/22/87";
15 #endif not lint
16 
17 /*
18  * mv file1 file2
19  */
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 
24 #include <stdio.h>
25 #include <sys/dir.h>
26 #include <errno.h>
27 #include <signal.h>
28 
29 #define	DELIM	'/'
30 #define MODEBITS 07777
31 
32 #define	ISDIR(st)	(((st).st_mode&S_IFMT) == S_IFDIR)
33 #define	ISLNK(st)	(((st).st_mode&S_IFMT) == S_IFLNK)
34 #define	ISREG(st)	(((st).st_mode&S_IFMT) == S_IFREG)
35 #define	ISDEV(st) \
36 	(((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
37 
38 char	*dname();
39 struct	stat s1, s2;
40 int	iflag = 0;	/* interactive mode */
41 int	fflag = 0;	/* force overwriting */
42 extern	unsigned errno;
43 
44 main(argc, argv)
45 	register char *argv[];
46 {
47 	register i, r;
48 	register char *arg;
49 	char *dest;
50 
51 	if (argc < 2)
52 		goto usage;
53 	while (argc > 1 && *argv[1] == '-') {
54 		argc--;
55 		arg = *++argv;
56 
57 		/*
58 		 * all files following a null option
59 		 * are considered file names
60 		 */
61 		if (*(arg+1) == '\0')
62 			break;
63 		while (*++arg != '\0') switch (*arg) {
64 
65 		case 'i':
66 			iflag++;
67 			break;
68 
69 		case 'f':
70 			fflag++;
71 			break;
72 
73 		default:
74 			goto usage;
75 		}
76 	}
77 	if (argc < 3)
78 		goto usage;
79 	dest = argv[argc-1];
80 	if (stat(dest, &s2) >= 0 && ISDIR(s2)) {
81 		r = 0;
82 		for (i = 1; i < argc-1; i++)
83 			r |= movewithshortname(argv[i], dest);
84 		exit(r);
85 	}
86 	if (argc > 3)
87 		goto usage;
88 	r = move(argv[1], argv[2]);
89 	exit(r);
90 	/*NOTREACHED*/
91 usage:
92 	fprintf(stderr,
93 "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n");
94 	return (1);
95 }
96 
97 movewithshortname(src, dest)
98 	char *src, *dest;
99 {
100 	register char *shortname;
101 	char target[MAXPATHLEN + 1];
102 
103 	shortname = dname(src);
104 	if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
105 		error("%s/%s: pathname too long", dest,
106 			shortname);
107 		return (1);
108 	}
109 	(void)sprintf(target, "%s/%s", dest, shortname);
110 	return (move(src, target));
111 }
112 
113 move(source, target)
114 	char *source, *target;
115 {
116 	int targetexists;
117 
118 	if (lstat(source, &s1) < 0) {
119 		Perror2(source, "Cannot access");
120 		return (1);
121 	}
122 	/*
123 	 * First, try to rename source to destination.
124 	 * The only reason we continue on failure is if
125 	 * the move is on a nondirectory and not across
126 	 * file systems.
127 	 */
128 	targetexists = lstat(target, &s2) >= 0;
129 	if (targetexists) {
130 		if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
131 			error("%s and %s are identical", source, target);
132 			return (1);
133 		}
134 		if (iflag && !fflag && isatty(fileno(stdin)) &&
135 		    query("remove %s? ", target) == 0)
136 			return (1);
137 		if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
138 			if (query("override protection %o for %s? ",
139 			  s2.st_mode & MODEBITS, target) == 0)
140 				return (1);
141 		}
142 	}
143 	if (rename(source, target) >= 0)
144 		return (0);
145 	if (errno != EXDEV) {
146 		Perror2(errno == ENOENT && targetexists == 0 ? target : source,
147 		    "rename");
148 		return (1);
149 	}
150 	if (ISDIR(s1)) {
151 		error("can't mv directories across file systems");
152 		return (1);
153 	}
154 	if (targetexists && unlink(target) < 0) {
155 		Perror2(target, "Cannot unlink");
156 		return (1);
157 	}
158 	/*
159 	 * File can't be renamed, try to recreate the symbolic
160 	 * link or special device, or copy the file wholesale
161 	 * between file systems.
162 	 */
163 	if (ISLNK(s1)) {
164 		register m;
165 		char symln[MAXPATHLEN + 1];
166 
167 		m = readlink(source, symln, sizeof (symln) - 1);
168 		if (m < 0) {
169 			Perror(source);
170 			return (1);
171 		}
172 		symln[m] = '\0';
173 
174 		(void) umask(~(s1.st_mode & MODEBITS));
175 		if (symlink(symln, target) < 0) {
176 			Perror(target);
177 			return (1);
178 		}
179 		goto cleanup;
180 	}
181 	(void) umask(0);
182 	if (ISDEV(s1)) {
183 		struct timeval tv[2];
184 
185 		if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
186 			Perror(target);
187 			return (1);
188 		}
189 
190 		tv[0].tv_sec = s1.st_atime;
191 		tv[0].tv_usec = 0;
192 		tv[1].tv_sec = s1.st_mtime;
193 		tv[1].tv_usec = 0;
194 		(void) utimes(target, tv);
195 		goto cleanup;
196 	}
197 	if (ISREG(s1)) {
198 		register int fi, fo, n;
199 		struct timeval tv[2];
200 		char buf[MAXBSIZE];
201 
202 		fi = open(source, 0);
203 		if (fi < 0) {
204 			Perror(source);
205 			return (1);
206 		}
207 
208 		fo = creat(target, s1.st_mode & MODEBITS);
209 		if (fo < 0) {
210 			Perror(target);
211 			close(fi);
212 			return (1);
213 		}
214 
215 		for (;;) {
216 			n = read(fi, buf, sizeof buf);
217 			if (n == 0) {
218 				break;
219 			} else if (n < 0) {
220 				Perror2(source, "read");
221 				close(fi);
222 				close(fo);
223 				return (1);
224 			} else if (write(fo, buf, n) != n) {
225 				Perror2(target, "write");
226 				close(fi);
227 				close(fo);
228 				return (1);
229 			}
230 		}
231 
232 		close(fi);
233 		close(fo);
234 
235 		tv[0].tv_sec = s1.st_atime;
236 		tv[0].tv_usec = 0;
237 		tv[1].tv_sec = s1.st_mtime;
238 		tv[1].tv_usec = 0;
239 		(void) utimes(target, tv);
240 		goto cleanup;
241 	}
242 	error("%s: unknown file type %o", source, s1.st_mode);
243 	return (1);
244 
245 cleanup:
246 	if (unlink(source) < 0) {
247 		Perror2(source, "Cannot unlink");
248 		return (1);
249 	}
250 	return (0);
251 }
252 
253 /*VARARGS*/
254 query(prompt, a1, a2)
255 	char *a1;
256 {
257 	register int i, c;
258 
259 	fprintf(stderr, prompt, a1, a2);
260 	i = c = getchar();
261 	while (c != '\n' && c != EOF)
262 		c = getchar();
263 	return (i == 'y');
264 }
265 
266 char *
267 dname(name)
268 	register char *name;
269 {
270 	register char *p;
271 
272 	p = name;
273 	while (*p)
274 		if (*p++ == DELIM && *p)
275 			name = p;
276 	return name;
277 }
278 
279 /*VARARGS*/
280 error(fmt, a1, a2)
281 	char *fmt;
282 {
283 
284 	fprintf(stderr, "mv: ");
285 	fprintf(stderr, fmt, a1, a2);
286 	fprintf(stderr, "\n");
287 }
288 
289 Perror(s)
290 	char *s;
291 {
292 	char buf[MAXPATHLEN + 10];
293 
294 	(void)sprintf(buf, "mv: %s", s);
295 	perror(buf);
296 }
297 
298 Perror2(s1, s2)
299 	char *s1, *s2;
300 {
301 	char buf[MAXPATHLEN + 20];
302 
303 	(void)sprintf(buf, "mv: %s: %s", s1, s2);
304 	perror(buf);
305 }
306