Commit | Line | Data |
---|---|---|
bcf1365c | 1 | /* |
50da9e1a KB |
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 | * | |
27c71911 | 8 | * %sccs.include.redist.c% |
bcf1365c DF |
9 | */ |
10 | ||
11 | #ifndef lint | |
12 | char copyright[] = | |
50da9e1a | 13 | "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ |
bcf1365c | 14 | All rights reserved.\n"; |
8161a03a | 15 | #endif /* not lint */ |
bcf1365c | 16 | |
d53fe8dd | 17 | #ifndef lint |
79688ddf | 18 | static char sccsid[] = "@(#)mv.c 5.11 (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> | |
52b78a7d KB |
25 | #include <fcntl.h> |
26 | #include <errno.h> | |
27 | #include <unistd.h> | |
3880b4a9 | 28 | #include <stdio.h> |
52b78a7d | 29 | #include <stdlib.h> |
50da9e1a KB |
30 | #include <string.h> |
31 | #include "pathnames.h" | |
3880b4a9 | 32 | |
50da9e1a | 33 | int fflg, iflg; |
3880b4a9 BJ |
34 | |
35 | main(argc, argv) | |
50da9e1a KB |
36 | int argc; |
37 | char **argv; | |
3880b4a9 | 38 | { |
50da9e1a | 39 | extern char *optarg; |
8161a03a | 40 | extern int optind; |
50da9e1a KB |
41 | register int baselen, exitval, len; |
42 | register char *p, *endp; | |
52b78a7d | 43 | struct stat sb; |
50da9e1a KB |
44 | int ch; |
45 | char path[MAXPATHLEN + 1]; | |
3880b4a9 | 46 | |
50da9e1a | 47 | while (((ch = getopt(argc, argv, "-if")) != EOF)) |
8161a03a | 48 | switch((char)ch) { |
d53fe8dd | 49 | case 'i': |
52b78a7d | 50 | iflg = 1; |
50da9e1a KB |
51 | break; |
52 | case 'f': | |
52b78a7d | 53 | fflg = 1; |
d53fe8dd | 54 | break; |
50da9e1a KB |
55 | case '-': /* undocumented; for compatibility */ |
56 | goto endarg; | |
8161a03a | 57 | case '?': |
d53fe8dd | 58 | default: |
8161a03a | 59 | usage(); |
d53fe8dd | 60 | } |
50da9e1a KB |
61 | endarg: argc -= optind; |
62 | argv += optind; | |
8161a03a KB |
63 | |
64 | if (argc < 2) | |
65 | usage(); | |
d53fe8dd | 66 | |
50da9e1a | 67 | /* |
52b78a7d KB |
68 | * If the stat on the target fails or the target isn't a directory, |
69 | * try the move. More than 2 arguments is an error in this case. | |
50da9e1a | 70 | */ |
52b78a7d | 71 | if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { |
50da9e1a KB |
72 | if (argc > 2) |
73 | usage(); | |
74 | exit(do_move(argv[0], argv[1])); | |
75 | } | |
d53fe8dd | 76 | |
52b78a7d | 77 | /* It's a directory, move each file into it. */ |
50da9e1a KB |
78 | (void)strcpy(path, argv[argc - 1]); |
79 | baselen = strlen(path); | |
80 | endp = &path[baselen]; | |
81 | *endp++ = '/'; | |
82 | ++baselen; | |
83 | for (exitval = 0; --argc; ++argv) { | |
84 | if ((p = rindex(*argv, '/')) == NULL) | |
85 | p = *argv; | |
86 | else | |
87 | ++p; | |
88 | if ((baselen + (len = strlen(p))) >= MAXPATHLEN) | |
89 | (void)fprintf(stderr, | |
90 | "mv: %s: destination pathname too long\n", *argv); | |
91 | else { | |
92 | bcopy(p, endp, len + 1); | |
93 | exitval |= do_move(*argv, path); | |
94 | } | |
d53fe8dd | 95 | } |
50da9e1a | 96 | exit(exitval); |
3880b4a9 BJ |
97 | } |
98 | ||
50da9e1a KB |
99 | do_move(from, to) |
100 | char *from, *to; | |
3880b4a9 | 101 | { |
52b78a7d | 102 | struct stat sb; |
50da9e1a | 103 | int ask, ch; |
3880b4a9 | 104 | |
d53fe8dd | 105 | /* |
52b78a7d | 106 | * Check access. If interactive and file exists, ask user if it |
50da9e1a KB |
107 | * should be replaced. Otherwise if file exists but isn't writable |
108 | * make sure the user wants to clobber it. | |
d53fe8dd | 109 | */ |
50da9e1a KB |
110 | if (!fflg && !access(to, F_OK)) { |
111 | ask = 0; | |
112 | if (iflg) { | |
113 | (void)fprintf(stderr, "overwrite %s? ", to); | |
114 | ask = 1; | |
115 | } | |
52b78a7d | 116 | else if (access(to, W_OK) && !stat(to, &sb)) { |
50da9e1a | 117 | (void)fprintf(stderr, "override mode %o on %s? ", |
52b78a7d | 118 | sb.st_mode & 07777, to); |
50da9e1a KB |
119 | ask = 1; |
120 | } | |
121 | if (ask) { | |
122 | if ((ch = getchar()) != EOF && ch != '\n') | |
123 | while (getchar() != '\n'); | |
124 | if (ch != 'y') | |
125 | return(0); | |
3880b4a9 | 126 | } |
b07dfbb8 | 127 | } |
50da9e1a KB |
128 | if (!rename(from, to)) |
129 | return(0); | |
52b78a7d | 130 | |
b07dfbb8 | 131 | if (errno != EXDEV) { |
50da9e1a KB |
132 | (void)fprintf(stderr, |
133 | "mv: rename %s to %s: %s\n", from, to, strerror(errno)); | |
134 | return(1); | |
3880b4a9 | 135 | } |
52b78a7d | 136 | |
d53fe8dd | 137 | /* |
52b78a7d KB |
138 | * If rename fails, and it's a regular file, do the copy internally; |
139 | * otherwise, use cp and rm. | |
d53fe8dd | 140 | */ |
52b78a7d KB |
141 | if (stat(from, &sb)) { |
142 | (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno)); | |
50da9e1a | 143 | return(1); |
d53fe8dd | 144 | } |
52b78a7d KB |
145 | return(S_ISREG(sb.st_mode) ? |
146 | fastcopy(from, to, &sb) : copy(from, to)); | |
50da9e1a | 147 | } |
465786e2 | 148 | |
50da9e1a KB |
149 | fastcopy(from, to, sbp) |
150 | char *from, *to; | |
151 | struct stat *sbp; | |
152 | { | |
153 | struct timeval tval[2]; | |
154 | static u_int blen; | |
155 | static char *bp; | |
156 | register int nread, from_fd, to_fd; | |
50da9e1a KB |
157 | |
158 | if ((from_fd = open(from, O_RDONLY, 0)) < 0) { | |
52b78a7d | 159 | error(from); |
50da9e1a | 160 | return(1); |
d53fe8dd | 161 | } |
52b78a7d KB |
162 | if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) { |
163 | error(to); | |
50da9e1a KB |
164 | (void)close(from_fd); |
165 | return(1); | |
3880b4a9 | 166 | } |
50da9e1a | 167 | if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { |
52b78a7d | 168 | error(NULL); |
50da9e1a | 169 | return(1); |
3880b4a9 | 170 | } |
50da9e1a KB |
171 | while ((nread = read(from_fd, bp, blen)) > 0) |
172 | if (write(to_fd, bp, nread) != nread) { | |
52b78a7d | 173 | error(to); |
50da9e1a KB |
174 | goto err; |
175 | } | |
176 | if (nread < 0) { | |
52b78a7d | 177 | error(from); |
50da9e1a KB |
178 | err: (void)unlink(to); |
179 | (void)close(from_fd); | |
180 | (void)close(to_fd); | |
181 | return(1); | |
182 | } | |
183 | (void)fchown(to_fd, sbp->st_uid, sbp->st_gid); | |
184 | (void)fchmod(to_fd, sbp->st_mode); | |
185 | ||
186 | (void)close(from_fd); | |
187 | (void)close(to_fd); | |
188 | ||
189 | tval[0].tv_sec = sbp->st_atime; | |
190 | tval[1].tv_sec = sbp->st_mtime; | |
191 | tval[0].tv_usec = tval[1].tv_usec = 0; | |
192 | (void)utimes(to, tval); | |
193 | (void)unlink(from); | |
194 | return(0); | |
3880b4a9 BJ |
195 | } |
196 | ||
50da9e1a KB |
197 | copy(from, to) |
198 | char *from, *to; | |
3880b4a9 | 199 | { |
50da9e1a | 200 | int pid, status; |
d53fe8dd | 201 | |
50da9e1a | 202 | if (!(pid = vfork())) { |
79688ddf | 203 | execl(_PATH_CP, "mv", "-pr", from, to, NULL); |
52b78a7d | 204 | error(_PATH_CP); |
50da9e1a KB |
205 | _exit(1); |
206 | } | |
207 | (void)waitpid(pid, &status, 0); | |
208 | if (!WIFEXITED(status) || WEXITSTATUS(status)) | |
209 | return(1); | |
210 | if (!(pid = vfork())) { | |
79688ddf | 211 | execl(_PATH_RM, "mv", "-rf", from, NULL); |
52b78a7d | 212 | error(_PATH_RM); |
50da9e1a KB |
213 | _exit(1); |
214 | } | |
215 | (void)waitpid(pid, &status, 0); | |
216 | return(!WIFEXITED(status) || WEXITSTATUS(status)); | |
3880b4a9 | 217 | } |
8161a03a | 218 | |
52b78a7d KB |
219 | error(s) |
220 | char *s; | |
221 | { | |
222 | if (s) | |
223 | (void)fprintf(stderr, "mv: %s: %s\n", s, strerror(errno)); | |
224 | else | |
225 | (void)fprintf(stderr, "mv: %s\n", strerror(errno)); | |
226 | } | |
227 | ||
8161a03a KB |
228 | usage() |
229 | { | |
50da9e1a KB |
230 | (void)fprintf(stderr, |
231 | "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); | |
8161a03a KB |
232 | exit(1); |
233 | } |