xref: /original-bsd/bin/mv/mv.c (revision 7e5c8007)
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Ken Smith of The State University of New York at Buffalo.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1989, 1993, 1994\n\
14 	The Regents of the University of California.  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)mv.c	8.2 (Berkeley) 04/02/94";
19 #endif /* not lint */
20 
21 #include <sys/param.h>
22 #include <sys/time.h>
23 #include <sys/wait.h>
24 #include <sys/stat.h>
25 
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "pathnames.h"
35 
36 int fflg, iflg;
37 
38 int	copy __P((char *, char *));
39 int	do_move __P((char *, char *));
40 int	fastcopy __P((char *, char *, struct stat *));
41 void	usage __P((void));
42 
43 int
44 main(argc, argv)
45 	int argc;
46 	char *argv[];
47 {
48 	register int baselen, len, rval;
49 	register char *p, *endp;
50 	struct stat sb;
51 	int ch;
52 	char path[MAXPATHLEN + 1];
53 
54 	while ((ch = getopt(argc, argv, "-if")) != EOF)
55 		switch (ch) {
56 		case 'i':
57 			iflg = 1;
58 			break;
59 		case 'f':
60 			fflg = 1;
61 			break;
62 		case '-':		/* Undocumented; for compatibility. */
63 			goto endarg;
64 		case '?':
65 		default:
66 			usage();
67 		}
68 endarg:	argc -= optind;
69 	argv += optind;
70 
71 	if (argc < 2)
72 		usage();
73 
74 	/*
75 	 * If the stat on the target fails or the target isn't a directory,
76 	 * try the move.  More than 2 arguments is an error in this case.
77 	 */
78 	if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
79 		if (argc > 2)
80 			usage();
81 		exit(do_move(argv[0], argv[1]));
82 	}
83 
84 	/* It's a directory, move each file into it. */
85 	(void)strcpy(path, argv[argc - 1]);
86 	baselen = strlen(path);
87 	endp = &path[baselen];
88 	*endp++ = '/';
89 	++baselen;
90 	for (rval = 0; --argc; ++argv) {
91 		if ((p = strrchr(*argv, '/')) == NULL)
92 			p = *argv;
93 		else
94 			++p;
95 		if ((baselen + (len = strlen(p))) >= MAXPATHLEN) {
96 			warnx("%s: destination pathname too long", *argv);
97 			rval = 1;
98 		} else {
99 			memmove(endp, p, len + 1);
100 			if (do_move(*argv, path))
101 				rval = 1;
102 		}
103 	}
104 	exit(rval);
105 }
106 
107 int
108 do_move(from, to)
109 	char *from, *to;
110 {
111 	struct stat sb;
112 	int ask, ch;
113 	char modep[15];
114 
115 	/*
116 	 * Check access.  If interactive and file exists, ask user if it
117 	 * should be replaced.  Otherwise if file exists but isn't writable
118 	 * make sure the user wants to clobber it.
119 	 */
120 	if (!fflg && !access(to, F_OK)) {
121 		ask = 0;
122 		if (iflg) {
123 			(void)fprintf(stderr, "overwrite %s? ", to);
124 			ask = 1;
125 		} else if (access(to, W_OK) && !stat(to, &sb)) {
126 			strmode(sb.st_mode, modep);
127 			(void)fprintf(stderr, "override %s%s%s/%s for %s? ",
128 			    modep + 1, modep[9] == ' ' ? "" : " ",
129 			    user_from_uid(sb.st_uid, 0),
130 			    group_from_gid(sb.st_gid, 0), to);
131 			ask = 1;
132 		}
133 		if (ask) {
134 			if ((ch = getchar()) != EOF && ch != '\n')
135 				while (getchar() != '\n');
136 			if (ch != 'y')
137 				return (0);
138 		}
139 	}
140 	if (!rename(from, to))
141 		return (0);
142 
143 	if (errno != EXDEV) {
144 		warn("rename %s to %s", from, to);
145 		return (1);
146 	}
147 
148 	/*
149 	 * If rename fails because we're trying to cross devices, and
150 	 * it's a regular file, do the copy internally; otherwise, use
151 	 * cp and rm.
152 	 */
153 	if (stat(from, &sb)) {
154 		warn("%s", from);
155 		return (1);
156 	}
157 	return (S_ISREG(sb.st_mode) ?
158 	    fastcopy(from, to, &sb) : copy(from, to));
159 }
160 
161 int
162 fastcopy(from, to, sbp)
163 	char *from, *to;
164 	struct stat *sbp;
165 {
166 	struct timeval tval[2];
167 	static u_int blen;
168 	static char *bp;
169 	register int nread, from_fd, to_fd;
170 
171 	if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
172 		warn("%s", from);
173 		return (1);
174 	}
175 	if ((to_fd =
176 	    open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) {
177 		warn("%s", to);
178 		(void)close(from_fd);
179 		return (1);
180 	}
181 	if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
182 		warn(NULL);
183 		return (1);
184 	}
185 	while ((nread = read(from_fd, bp, blen)) > 0)
186 		if (write(to_fd, bp, nread) != nread) {
187 			warn("%s", to);
188 			goto err;
189 		}
190 	if (nread < 0) {
191 		warn("%s", from);
192 err:		if (unlink(to))
193 			warn("%s: remove", to);
194 		(void)close(from_fd);
195 		(void)close(to_fd);
196 		return (1);
197 	}
198 	(void)close(from_fd);
199 
200 	if (fchown(to_fd, sbp->st_uid, sbp->st_gid))
201 		warn("%s: set owner/group", to);
202 	if (fchmod(to_fd, sbp->st_mode))
203 		warn("%s: set mode", to);
204 
205 	tval[0].tv_sec = sbp->st_atime;
206 	tval[1].tv_sec = sbp->st_mtime;
207 	tval[0].tv_usec = tval[1].tv_usec = 0;
208 	if (utimes(to, tval))
209 		warn("%s: set times", to);
210 
211 	if (close(to_fd)) {
212 		warn("%s", to);
213 		return (1);
214 	}
215 
216 	if (unlink(from)) {
217 		warn("%s: remove", from);
218 		return (1);
219 	}
220 	return (0);
221 }
222 
223 int
224 copy(from, to)
225 	char *from, *to;
226 {
227 	int pid, status;
228 
229 	if ((pid = vfork()) == 0) {
230 		execl(_PATH_CP, "mv", "-PRp", from, to, NULL);
231 		warn("%s", _PATH_CP);
232 		_exit(1);
233 	}
234 	if (waitpid(pid, &status, 0) == -1) {
235 		warn("%s: waitpid", _PATH_CP);
236 		return (1);
237 	}
238 	if (!WIFEXITED(status)) {
239 		warn("%s: did not terminate normally", _PATH_CP);
240 		return (1);
241 	}
242 	if (WEXITSTATUS(status)) {
243 		warn("%s: terminated with %d (non-zero) status",
244 		    _PATH_CP, WEXITSTATUS(status));
245 		return (1);
246 	}
247 	if (!(pid = vfork())) {
248 		execl(_PATH_RM, "mv", "-rf", from, NULL);
249 		warn("%s", _PATH_RM);
250 		_exit(1);
251 	}
252 	if (waitpid(pid, &status, 0) == -1) {
253 		warn("%s: waitpid", _PATH_RM);
254 		return (1);
255 	}
256 	if (!WIFEXITED(status)) {
257 		warn("%s: did not terminate normally", _PATH_RM);
258 		return (1);
259 	}
260 	if (WEXITSTATUS(status)) {
261 		warn("%s: terminated with %d (non-zero) status",
262 		    _PATH_RM, WEXITSTATUS(status));
263 		return (1);
264 	}
265 	return (0);
266 }
267 
268 void
269 usage()
270 {
271 	(void)fprintf(stderr,
272 "usage: mv [-if] src target;\n   or: mv [-if] src1 ... srcN directory\n");
273 	exit(1);
274 }
275