xref: /original-bsd/bin/mv/mv.c (revision 410efcb1)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * 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 char copyright[] =
13 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
14  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)mv.c	5.13 (Berkeley) 04/29/93";
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, exitval, len;
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((char)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 (exitval = 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 			exitval = 1;
98 		} else {
99 			memmove(endp, p, len + 1);
100 			exitval |= do_move(*argv, path);
101 		}
102 	}
103 	exit(exitval);
104 }
105 
106 int
107 do_move(from, to)
108 	char *from, *to;
109 {
110 	struct stat sb;
111 	int ask, ch;
112 
113 	/*
114 	 * Check access.  If interactive and file exists, ask user if it
115 	 * should be replaced.  Otherwise if file exists but isn't writable
116 	 * make sure the user wants to clobber it.
117 	 */
118 	if (!fflg && !access(to, F_OK)) {
119 		ask = 0;
120 		if (iflg) {
121 			(void)fprintf(stderr, "overwrite %s? ", to);
122 			ask = 1;
123 		}
124 		else if (access(to, W_OK) && !stat(to, &sb)) {
125 			(void)fprintf(stderr, "override mode %o on %s? ",
126 			    sb.st_mode & 07777, to);
127 			ask = 1;
128 		}
129 		if (ask) {
130 			if ((ch = getchar()) != EOF && ch != '\n')
131 				while (getchar() != '\n');
132 			if (ch != 'y')
133 				return (0);
134 		}
135 	}
136 	if (!rename(from, to))
137 		return (0);
138 
139 	if (errno != EXDEV) {
140 		warn("rename %s to %s", from, to);
141 		return (1);
142 	}
143 
144 	/*
145 	 * If rename fails, and it's a regular file, do the copy internally;
146 	 * otherwise, use cp and rm.
147 	 */
148 	if (stat(from, &sb)) {
149 		warn("%s", from);
150 		return (1);
151 	}
152 	return (S_ISREG(sb.st_mode) ?
153 	    fastcopy(from, to, &sb) : copy(from, to));
154 }
155 
156 int
157 fastcopy(from, to, sbp)
158 	char *from, *to;
159 	struct stat *sbp;
160 {
161 	struct timeval tval[2];
162 	static u_int blen;
163 	static char *bp;
164 	register int nread, from_fd, to_fd;
165 
166 	if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
167 		warn("%s", from);
168 		return (1);
169 	}
170 	if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) {
171 		warn("%s", to);
172 		(void)close(from_fd);
173 		return (1);
174 	}
175 	if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
176 		warn(NULL);
177 		return (1);
178 	}
179 	while ((nread = read(from_fd, bp, blen)) > 0)
180 		if (write(to_fd, bp, nread) != nread) {
181 			warn("%s", to);
182 			goto err;
183 		}
184 	if (nread < 0) {
185 		warn("%s", from);
186 err:		if (unlink(to))
187 			warn("%s: remove", to);
188 		(void)close(from_fd);
189 		(void)close(to_fd);
190 		return (1);
191 	}
192 	(void)close(from_fd);
193 
194 	if (fchown(to_fd, sbp->st_uid, sbp->st_gid))
195 		warn("%s: set owner/group", to);
196 	if (fchmod(to_fd, sbp->st_mode))
197 		warn("%s: set mode", to);
198 
199 	tval[0].tv_sec = sbp->st_atime;
200 	tval[1].tv_sec = sbp->st_mtime;
201 	tval[0].tv_usec = tval[1].tv_usec = 0;
202 	if (utimes(to, tval))
203 		warn("%s: set times", to);
204 
205 	if (close(to_fd)) {
206 		warn("%s", to);
207 		return (1);
208 	}
209 
210 	if (unlink(from)) {
211 		warn("%s: remove", from);
212 		return (1);
213 	}
214 	return (0);
215 }
216 
217 int
218 copy(from, to)
219 	char *from, *to;
220 {
221 	int pid, status;
222 
223 	if (!(pid = vfork())) {
224 		execl(_PATH_CP, "mv", "-pR", from, to, NULL);
225 		warn("%s", _PATH_CP);
226 		_exit(1);
227 	}
228 	if (waitpid(pid, &status, 0) == -1) {
229 		warn("%s: waitpid", _PATH_CP);
230 		return (1);
231 	}
232 	if (!WIFEXITED(status)) {
233 		warn("%s: did not terminate normally", _PATH_CP);
234 		return (1);
235 	}
236 	if (WEXITSTATUS(status)) {
237 		warn("%s: terminated with %d (non-zero) status",
238 		    _PATH_CP, WEXITSTATUS(status));
239 		return (1);
240 	}
241 	if (!(pid = vfork())) {
242 		execl(_PATH_RM, "mv", "-rf", from, NULL);
243 		warn("%s", _PATH_RM);
244 		_exit(1);
245 	}
246 	if (waitpid(pid, &status, 0) == -1) {
247 		warn("%s: waitpid", _PATH_RM);
248 		return (1);
249 	}
250 	if (!WIFEXITED(status)) {
251 		warn("%s: did not terminate normally", _PATH_RM);
252 		return (1);
253 	}
254 	if (WEXITSTATUS(status)) {
255 		warn("%s: terminated with %d (non-zero) status",
256 		    _PATH_RM, WEXITSTATUS(status));
257 		return (1);
258 	}
259 	return (0);
260 }
261 
262 void
263 usage()
264 {
265 	(void)fprintf(stderr,
266 "usage: mv [-if] src target;\n   or: mv [-if] src1 ... srcN directory\n");
267 	exit(1);
268 }
269