Commit | Line | Data |
---|---|---|
bcf1365c | 1 | /* |
dfb144fc | 2 | * Copyright (c) 1989, 1993, 1994 |
59d11f9a | 3 | * The Regents of the University of California. All rights reserved. |
50da9e1a KB |
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 | * | |
27c71911 | 8 | * %sccs.include.redist.c% |
bcf1365c DF |
9 | */ |
10 | ||
11 | #ifndef lint | |
59d11f9a | 12 | static char copyright[] = |
dfb144fc | 13 | "@(#) Copyright (c) 1989, 1993, 1994\n\ |
59d11f9a | 14 | The Regents of the University of California. All rights reserved.\n"; |
8161a03a | 15 | #endif /* not lint */ |
bcf1365c | 16 | |
d53fe8dd | 17 | #ifndef lint |
dfb144fc | 18 | static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) %G%"; |
8161a03a | 19 | #endif /* not lint */ |
2a7e674d | 20 | |
d53fe8dd | 21 | #include <sys/param.h> |
465786e2 | 22 | #include <sys/time.h> |
50da9e1a KB |
23 | #include <sys/wait.h> |
24 | #include <sys/stat.h> | |
e96ee99d | 25 | |
2041d53d | 26 | #include <err.h> |
52b78a7d | 27 | #include <errno.h> |
e96ee99d | 28 | #include <fcntl.h> |
3880b4a9 | 29 | #include <stdio.h> |
52b78a7d | 30 | #include <stdlib.h> |
50da9e1a | 31 | #include <string.h> |
e96ee99d KB |
32 | #include <unistd.h> |
33 | ||
50da9e1a | 34 | #include "pathnames.h" |
3880b4a9 | 35 | |
50da9e1a | 36 | int fflg, iflg; |
3880b4a9 | 37 | |
e96ee99d KB |
38 | int copy __P((char *, char *)); |
39 | int do_move __P((char *, char *)); | |
e96ee99d KB |
40 | int fastcopy __P((char *, char *, struct stat *)); |
41 | void usage __P((void)); | |
42 | ||
43 | int | |
3880b4a9 | 44 | main(argc, argv) |
50da9e1a | 45 | int argc; |
e96ee99d | 46 | char *argv[]; |
3880b4a9 | 47 | { |
dfb144fc | 48 | register int baselen, len, rval; |
50da9e1a | 49 | register char *p, *endp; |
52b78a7d | 50 | struct stat sb; |
50da9e1a KB |
51 | int ch; |
52 | char path[MAXPATHLEN + 1]; | |
3880b4a9 | 53 | |
dfb144fc KB |
54 | while ((ch = getopt(argc, argv, "-if")) != EOF) |
55 | switch (ch) { | |
d53fe8dd | 56 | case 'i': |
52b78a7d | 57 | iflg = 1; |
50da9e1a KB |
58 | break; |
59 | case 'f': | |
52b78a7d | 60 | fflg = 1; |
d53fe8dd | 61 | break; |
e96ee99d | 62 | case '-': /* Undocumented; for compatibility. */ |
50da9e1a | 63 | goto endarg; |
8161a03a | 64 | case '?': |
d53fe8dd | 65 | default: |
8161a03a | 66 | usage(); |
d53fe8dd | 67 | } |
50da9e1a KB |
68 | endarg: argc -= optind; |
69 | argv += optind; | |
8161a03a KB |
70 | |
71 | if (argc < 2) | |
72 | usage(); | |
d53fe8dd | 73 | |
50da9e1a | 74 | /* |
52b78a7d KB |
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. | |
50da9e1a | 77 | */ |
52b78a7d | 78 | if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { |
50da9e1a KB |
79 | if (argc > 2) |
80 | usage(); | |
81 | exit(do_move(argv[0], argv[1])); | |
82 | } | |
d53fe8dd | 83 | |
52b78a7d | 84 | /* It's a directory, move each file into it. */ |
50da9e1a KB |
85 | (void)strcpy(path, argv[argc - 1]); |
86 | baselen = strlen(path); | |
87 | endp = &path[baselen]; | |
88 | *endp++ = '/'; | |
89 | ++baselen; | |
dfb144fc | 90 | for (rval = 0; --argc; ++argv) { |
2041d53d | 91 | if ((p = strrchr(*argv, '/')) == NULL) |
50da9e1a KB |
92 | p = *argv; |
93 | else | |
94 | ++p; | |
e96ee99d | 95 | if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { |
2041d53d | 96 | warnx("%s: destination pathname too long", *argv); |
dfb144fc | 97 | rval = 1; |
e96ee99d | 98 | } else { |
2041d53d | 99 | memmove(endp, p, len + 1); |
dfb144fc KB |
100 | if (do_move(*argv, path)) |
101 | rval = 1; | |
50da9e1a | 102 | } |
d53fe8dd | 103 | } |
dfb144fc | 104 | exit(rval); |
3880b4a9 BJ |
105 | } |
106 | ||
e96ee99d | 107 | int |
50da9e1a KB |
108 | do_move(from, to) |
109 | char *from, *to; | |
3880b4a9 | 110 | { |
52b78a7d | 111 | struct stat sb; |
50da9e1a | 112 | int ask, ch; |
dfb144fc | 113 | char modep[15]; |
3880b4a9 | 114 | |
d53fe8dd | 115 | /* |
52b78a7d | 116 | * Check access. If interactive and file exists, ask user if it |
50da9e1a KB |
117 | * should be replaced. Otherwise if file exists but isn't writable |
118 | * make sure the user wants to clobber it. | |
d53fe8dd | 119 | */ |
50da9e1a KB |
120 | if (!fflg && !access(to, F_OK)) { |
121 | ask = 0; | |
122 | if (iflg) { | |
123 | (void)fprintf(stderr, "overwrite %s? ", to); | |
124 | ask = 1; | |
dfb144fc KB |
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); | |
50da9e1a KB |
131 | ask = 1; |
132 | } | |
133 | if (ask) { | |
134 | if ((ch = getchar()) != EOF && ch != '\n') | |
135 | while (getchar() != '\n'); | |
136 | if (ch != 'y') | |
e96ee99d | 137 | return (0); |
3880b4a9 | 138 | } |
b07dfbb8 | 139 | } |
50da9e1a | 140 | if (!rename(from, to)) |
e96ee99d | 141 | return (0); |
52b78a7d | 142 | |
b07dfbb8 | 143 | if (errno != EXDEV) { |
2041d53d | 144 | warn("rename %s to %s", from, to); |
e96ee99d | 145 | return (1); |
3880b4a9 | 146 | } |
52b78a7d | 147 | |
d53fe8dd | 148 | /* |
dfb144fc KB |
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. | |
d53fe8dd | 152 | */ |
52b78a7d | 153 | if (stat(from, &sb)) { |
2041d53d | 154 | warn("%s", from); |
e96ee99d | 155 | return (1); |
d53fe8dd | 156 | } |
e96ee99d | 157 | return (S_ISREG(sb.st_mode) ? |
52b78a7d | 158 | fastcopy(from, to, &sb) : copy(from, to)); |
50da9e1a | 159 | } |
465786e2 | 160 | |
e96ee99d | 161 | int |
50da9e1a KB |
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; | |
50da9e1a KB |
170 | |
171 | if ((from_fd = open(from, O_RDONLY, 0)) < 0) { | |
2041d53d | 172 | warn("%s", from); |
e96ee99d | 173 | return (1); |
d53fe8dd | 174 | } |
dfb144fc KB |
175 | if ((to_fd = |
176 | open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { | |
2041d53d | 177 | warn("%s", to); |
50da9e1a | 178 | (void)close(from_fd); |
e96ee99d | 179 | return (1); |
3880b4a9 | 180 | } |
50da9e1a | 181 | if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { |
2041d53d | 182 | warn(NULL); |
e96ee99d | 183 | return (1); |
3880b4a9 | 184 | } |
50da9e1a KB |
185 | while ((nread = read(from_fd, bp, blen)) > 0) |
186 | if (write(to_fd, bp, nread) != nread) { | |
2041d53d | 187 | warn("%s", to); |
50da9e1a KB |
188 | goto err; |
189 | } | |
190 | if (nread < 0) { | |
2041d53d KB |
191 | warn("%s", from); |
192 | err: if (unlink(to)) | |
193 | warn("%s: remove", to); | |
50da9e1a KB |
194 | (void)close(from_fd); |
195 | (void)close(to_fd); | |
e96ee99d | 196 | return (1); |
50da9e1a | 197 | } |
50da9e1a | 198 | (void)close(from_fd); |
2041d53d KB |
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); | |
50da9e1a KB |
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; | |
2041d53d KB |
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 | } | |
e96ee99d | 220 | return (0); |
3880b4a9 BJ |
221 | } |
222 | ||
e96ee99d | 223 | int |
50da9e1a KB |
224 | copy(from, to) |
225 | char *from, *to; | |
3880b4a9 | 226 | { |
50da9e1a | 227 | int pid, status; |
d53fe8dd | 228 | |
dfb144fc KB |
229 | if ((pid = vfork()) == 0) { |
230 | execl(_PATH_CP, "mv", "-PRp", from, to, NULL); | |
2041d53d | 231 | warn("%s", _PATH_CP); |
50da9e1a KB |
232 | _exit(1); |
233 | } | |
2041d53d KB |
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); | |
e96ee99d | 240 | return (1); |
2041d53d KB |
241 | } |
242 | if (WEXITSTATUS(status)) { | |
243 | warn("%s: terminated with %d (non-zero) status", | |
244 | _PATH_CP, WEXITSTATUS(status)); | |
245 | return (1); | |
246 | } | |
50da9e1a | 247 | if (!(pid = vfork())) { |
79688ddf | 248 | execl(_PATH_RM, "mv", "-rf", from, NULL); |
2041d53d | 249 | warn("%s", _PATH_RM); |
50da9e1a KB |
250 | _exit(1); |
251 | } | |
2041d53d KB |
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); | |
52b78a7d KB |
266 | } |
267 | ||
e96ee99d | 268 | void |
8161a03a KB |
269 | usage() |
270 | { | |
50da9e1a KB |
271 | (void)fprintf(stderr, |
272 | "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); | |
8161a03a KB |
273 | exit(1); |
274 | } |