mv.c revision 1556
1257453Sian/*
2257453Sian * Copyright (c) 1989, 1993, 1994
3257453Sian *	The Regents of the University of California.  All rights reserved.
4257453Sian *
5257453Sian * This code is derived from software contributed to Berkeley by
6257453Sian * Ken Smith of The State University of New York at Buffalo.
7257453Sian *
8257453Sian * Redistribution and use in source and binary forms, with or without
9257453Sian * modification, are permitted provided that the following conditions
10257453Sian * are met:
11257453Sian * 1. Redistributions of source code must retain the above copyright
12257453Sian *    notice, this list of conditions and the following disclaimer.
13257453Sian * 2. Redistributions in binary form must reproduce the above copyright
14257453Sian *    notice, this list of conditions and the following disclaimer in the
15257453Sian *    documentation and/or other materials provided with the distribution.
16257453Sian * 3. All advertising materials mentioning features or use of this software
17257453Sian *    must display the following acknowledgement:
18257453Sian *	This product includes software developed by the University of
19257453Sian *	California, Berkeley and its contributors.
20257453Sian * 4. Neither the name of the University nor the names of its contributors
21257453Sian *    may be used to endorse or promote products derived from this software
22257453Sian *    without specific prior written permission.
23257453Sian *
24257453Sian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25257453Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26257453Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27257453Sian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28257453Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29257453Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30257453Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31257453Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32283500Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33283500Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34283500Sian * SUCH DAMAGE.
35283500Sian */
36283500Sian
37283500Sian#ifndef lint
38283500Sianstatic char copyright[] =
39283500Sian"@(#) Copyright (c) 1989, 1993, 1994\n\
40283500Sian	The Regents of the University of California.  All rights reserved.\n";
41283500Sian#endif /* not lint */
42283500Sian
43283500Sian#ifndef lint
44283500Sianstatic char sccsid[] = "@(#)mv.c	8.2 (Berkeley) 4/2/94";
45283500Sian#endif /* not lint */
46283500Sian
47283500Sian#include <sys/param.h>
48283500Sian#include <sys/time.h>
49283500Sian#include <sys/wait.h>
50283500Sian#include <sys/stat.h>
51283500Sian
52266207Sian#include <err.h>
53266207Sian#include <errno.h>
54266207Sian#include <fcntl.h>
55266207Sian#include <stdio.h>
56266207Sian#include <stdlib.h>
57266207Sian#include <string.h>
58266207Sian#include <unistd.h>
59270076Sian
60266207Sian#include "pathnames.h"
61266207Sian
62266207Sianint fflg, iflg;
63266207Sian
64266207Sianint	copy __P((char *, char *));
65266207Sianint	do_move __P((char *, char *));
66266207Sianint	fastcopy __P((char *, char *, struct stat *));
67257453Sianvoid	usage __P((void));
68257453Sian
69257453Sianint
70main(argc, argv)
71	int argc;
72	char *argv[];
73{
74	register int baselen, len, rval;
75	register char *p, *endp;
76	struct stat sb;
77	int ch;
78	char path[MAXPATHLEN + 1];
79
80	while ((ch = getopt(argc, argv, "-if")) != EOF)
81		switch (ch) {
82		case 'i':
83			iflg = 1;
84			break;
85		case 'f':
86			fflg = 1;
87			break;
88		case '-':		/* Undocumented; for compatibility. */
89			goto endarg;
90		case '?':
91		default:
92			usage();
93		}
94endarg:	argc -= optind;
95	argv += optind;
96
97	if (argc < 2)
98		usage();
99
100	/*
101	 * If the stat on the target fails or the target isn't a directory,
102	 * try the move.  More than 2 arguments is an error in this case.
103	 */
104	if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
105		if (argc > 2)
106			usage();
107		exit(do_move(argv[0], argv[1]));
108	}
109
110	/* It's a directory, move each file into it. */
111	(void)strcpy(path, argv[argc - 1]);
112	baselen = strlen(path);
113	endp = &path[baselen];
114	*endp++ = '/';
115	++baselen;
116	for (rval = 0; --argc; ++argv) {
117		if ((p = strrchr(*argv, '/')) == NULL)
118			p = *argv;
119		else
120			++p;
121		if ((baselen + (len = strlen(p))) >= MAXPATHLEN) {
122			warnx("%s: destination pathname too long", *argv);
123			rval = 1;
124		} else {
125			memmove(endp, p, len + 1);
126			if (do_move(*argv, path))
127				rval = 1;
128		}
129	}
130	exit(rval);
131}
132
133int
134do_move(from, to)
135	char *from, *to;
136{
137	struct stat sb;
138	int ask, ch;
139	char modep[15];
140
141	/*
142	 * Check access.  If interactive and file exists, ask user if it
143	 * should be replaced.  Otherwise if file exists but isn't writable
144	 * make sure the user wants to clobber it.
145	 */
146	if (!fflg && !access(to, F_OK)) {
147		ask = 0;
148		if (iflg) {
149			(void)fprintf(stderr, "overwrite %s? ", to);
150			ask = 1;
151		} else if (access(to, W_OK) && !stat(to, &sb)) {
152			strmode(sb.st_mode, modep);
153			(void)fprintf(stderr, "override %s%s%s/%s for %s? ",
154			    modep + 1, modep[9] == ' ' ? "" : " ",
155			    user_from_uid(sb.st_uid, 0),
156			    group_from_gid(sb.st_gid, 0), to);
157			ask = 1;
158		}
159		if (ask) {
160			if ((ch = getchar()) != EOF && ch != '\n')
161				while (getchar() != '\n');
162			if (ch != 'y')
163				return (0);
164		}
165	}
166	if (!rename(from, to))
167		return (0);
168
169	if (errno != EXDEV) {
170		warn("rename %s to %s", from, to);
171		return (1);
172	}
173
174	/*
175	 * If rename fails because we're trying to cross devices, and
176	 * it's a regular file, do the copy internally; otherwise, use
177	 * cp and rm.
178	 */
179	if (stat(from, &sb)) {
180		warn("%s", from);
181		return (1);
182	}
183	return (S_ISREG(sb.st_mode) ?
184	    fastcopy(from, to, &sb) : copy(from, to));
185}
186
187int
188fastcopy(from, to, sbp)
189	char *from, *to;
190	struct stat *sbp;
191{
192	struct timeval tval[2];
193	static u_int blen;
194	static char *bp;
195	register int nread, from_fd, to_fd;
196
197	if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
198		warn("%s", from);
199		return (1);
200	}
201	if ((to_fd =
202	    open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) {
203		warn("%s", to);
204		(void)close(from_fd);
205		return (1);
206	}
207	if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
208		warn(NULL);
209		return (1);
210	}
211	while ((nread = read(from_fd, bp, blen)) > 0)
212		if (write(to_fd, bp, nread) != nread) {
213			warn("%s", to);
214			goto err;
215		}
216	if (nread < 0) {
217		warn("%s", from);
218err:		if (unlink(to))
219			warn("%s: remove", to);
220		(void)close(from_fd);
221		(void)close(to_fd);
222		return (1);
223	}
224	(void)close(from_fd);
225
226	if (fchown(to_fd, sbp->st_uid, sbp->st_gid))
227		warn("%s: set owner/group", to);
228	if (fchmod(to_fd, sbp->st_mode))
229		warn("%s: set mode", to);
230
231	tval[0].tv_sec = sbp->st_atime;
232	tval[1].tv_sec = sbp->st_mtime;
233	tval[0].tv_usec = tval[1].tv_usec = 0;
234	if (utimes(to, tval))
235		warn("%s: set times", to);
236
237	if (close(to_fd)) {
238		warn("%s", to);
239		return (1);
240	}
241
242	if (unlink(from)) {
243		warn("%s: remove", from);
244		return (1);
245	}
246	return (0);
247}
248
249int
250copy(from, to)
251	char *from, *to;
252{
253	int pid, status;
254
255	if ((pid = vfork()) == 0) {
256		execl(_PATH_CP, "mv", "-PRp", from, to, NULL);
257		warn("%s", _PATH_CP);
258		_exit(1);
259	}
260	if (waitpid(pid, &status, 0) == -1) {
261		warn("%s: waitpid", _PATH_CP);
262		return (1);
263	}
264	if (!WIFEXITED(status)) {
265		warn("%s: did not terminate normally", _PATH_CP);
266		return (1);
267	}
268	if (WEXITSTATUS(status)) {
269		warn("%s: terminated with %d (non-zero) status",
270		    _PATH_CP, WEXITSTATUS(status));
271		return (1);
272	}
273	if (!(pid = vfork())) {
274		execl(_PATH_RM, "mv", "-rf", from, NULL);
275		warn("%s", _PATH_RM);
276		_exit(1);
277	}
278	if (waitpid(pid, &status, 0) == -1) {
279		warn("%s: waitpid", _PATH_RM);
280		return (1);
281	}
282	if (!WIFEXITED(status)) {
283		warn("%s: did not terminate normally", _PATH_RM);
284		return (1);
285	}
286	if (WEXITSTATUS(status)) {
287		warn("%s: terminated with %d (non-zero) status",
288		    _PATH_RM, WEXITSTATUS(status));
289		return (1);
290	}
291	return (0);
292}
293
294void
295usage()
296{
297	(void)fprintf(stderr,
298"usage: mv [-if] src target;\n   or: mv [-if] src1 ... srcN directory\n");
299	exit(1);
300}
301