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