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
main(argc,argv)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
do_move(from,to)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
fastcopy(from,to,sbp)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
copy(from,to)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
usage()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