xref: /original-bsd/bin/mv/mv.c (revision 9087ff44)
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.2 (Berkeley) 06/07/85";
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	*sprintf();
39 char	*dname();
40 struct	stat s1, s2;
41 int	iflag = 0;	/* interactive mode */
42 int	fflag = 0;	/* force overwriting */
43 extern	unsigned errno;
44 
45 main(argc, argv)
46 	register char *argv[];
47 {
48 	register i, r;
49 	register char *arg;
50 	char *dest;
51 
52 	if (argc < 2)
53 		goto usage;
54 	while (argc > 1 && *argv[1] == '-') {
55 		argc--;
56 		arg = *++argv;
57 
58 		/*
59 		 * all files following a null option
60 		 * are considered file names
61 		 */
62 		if (*(arg+1) == '\0')
63 			break;
64 		while (*++arg != '\0') switch (*arg) {
65 
66 		case 'i':
67 			iflag++;
68 			break;
69 
70 		case 'f':
71 			fflag++;
72 			break;
73 
74 		default:
75 			goto usage;
76 		}
77 	}
78 	if (argc < 3)
79 		goto usage;
80 	dest = argv[argc-1];
81 	if (stat(dest, &s2) >= 0 && ISDIR(s2)) {
82 		r = 0;
83 		for (i = 1; i < argc-1; i++)
84 			r |= movewithshortname(argv[i], dest);
85 		exit(r);
86 	}
87 	if (argc > 3)
88 		goto usage;
89 	r = move(argv[1], argv[2]);
90 	exit(r);
91 	/*NOTREACHED*/
92 usage:
93 	fprintf(stderr,
94 "usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1 (`fn' is a file or directory)\n");
95 	return (1);
96 }
97 
98 movewithshortname(src, dest)
99 	char *src, *dest;
100 {
101 	register char *shortname;
102 	char target[MAXPATHLEN + 1];
103 
104 	shortname = dname(src);
105 	if (strlen(dest) + strlen(shortname) > MAXPATHLEN - 1) {
106 		error("%s/%s: pathname too long", dest,
107 			shortname);
108 		return (1);
109 	}
110 	sprintf(target, "%s/%s", dest, shortname);
111 	return (move(src, target));
112 }
113 
114 move(source, target)
115 	char *source, *target;
116 {
117 	int targetexists;
118 
119 	if (lstat(source, &s1) < 0) {
120 		Perror2(source, "Cannot access");
121 		return (1);
122 	}
123 	/*
124 	 * First, try to rename source to destination.
125 	 * The only reason we continue on failure is if
126 	 * the move is on a nondirectory and not across
127 	 * file systems.
128 	 */
129 	targetexists = lstat(target, &s2) >= 0;
130 	if (targetexists) {
131 		if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
132 			error("%s and %s are identical", source, target);
133 			return (1);
134 		}
135 		if (iflag && !fflag && isatty(fileno(stdin)) &&
136 		    query("remove %s? ", target) == 0)
137 			return (1);
138 		if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) {
139 			if (query("override protection %o for %s? ",
140 			  s2.st_mode & MODEBITS, target) == 0)
141 				return (1);
142 		}
143 	}
144 	if (rename(source, target) >= 0)
145 		return (0);
146 	if (errno != EXDEV) {
147 		Perror2(source, "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 		m = umask(~(s1.st_mode & MODEBITS));
175 		if (symlink(symln, target) < 0) {
176 			Perror(target);
177 			return (1);
178 		}
179 		(void) umask(m);
180 		goto cleanup;
181 	}
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 	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 	sprintf(buf, "mv: %s: %s", s1, s2);
304 	perror(buf);
305 }
306