mv.c revision 1.4 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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #ifndef lint
38 char copyright[] =
39 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
40 All rights reserved.\n";
41 #endif /* not lint */
42
43 #ifndef lint
44 /*static char sccsid[] = "from: @(#)mv.c 5.11 (Berkeley) 4/3/91";*/
45 static char rcsid[] = "$Id: mv.c,v 1.4 1993/08/01 18:59:17 mycroft Exp $";
46 #endif /* not lint */
47
48 #include <sys/param.h>
49 #include <sys/time.h>
50 #include <sys/wait.h>
51 #include <sys/stat.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <unistd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include "pathnames.h"
59
60 int fflg, iflg;
61
62 main(argc, argv)
63 int argc;
64 char **argv;
65 {
66 extern char *optarg;
67 extern int optind;
68 register int baselen, exitval, len;
69 register char *p, *endp;
70 struct stat sb;
71 int ch;
72 char path[MAXPATHLEN + 1];
73
74 while (((ch = getopt(argc, argv, "-if")) != EOF))
75 switch((char)ch) {
76 case 'i':
77 iflg = 1;
78 break;
79 case 'f':
80 fflg = 1;
81 break;
82 case '-': /* undocumented; for compatibility */
83 goto endarg;
84 case '?':
85 default:
86 usage();
87 }
88 endarg: argc -= optind;
89 argv += optind;
90
91 if (argc < 2)
92 usage();
93
94 /*
95 * If the stat on the target fails or the target isn't a directory,
96 * try the move. More than 2 arguments is an error in this case.
97 */
98 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
99 if (argc > 2)
100 usage();
101 exit(do_move(argv[0], argv[1]));
102 }
103
104 /* It's a directory, move each file into it. */
105 (void)strcpy(path, argv[argc - 1]);
106 baselen = strlen(path);
107 endp = &path[baselen];
108 *endp++ = '/';
109 ++baselen;
110 for (exitval = 0; --argc; ++argv) {
111 if ((p = rindex(*argv, '/')) == NULL)
112 p = *argv;
113 else
114 ++p;
115 if ((baselen + (len = strlen(p))) >= MAXPATHLEN)
116 (void)fprintf(stderr,
117 "mv: %s: destination pathname too long\n", *argv);
118 else {
119 bcopy(p, endp, len + 1);
120 exitval |= do_move(*argv, path);
121 }
122 }
123 exit(exitval);
124 }
125
126 do_move(from, to)
127 char *from, *to;
128 {
129 struct stat sb;
130 int ask, ch;
131
132 /*
133 * Check access. If interactive and file exists, ask user if it
134 * should be replaced. Otherwise if file exists but isn't writable
135 * make sure the user wants to clobber it.
136 */
137 if (!fflg && !access(to, F_OK)) {
138 ask = 0;
139 if (iflg) {
140 (void)fprintf(stderr, "overwrite %s? ", to);
141 ask = 1;
142 }
143 else if (access(to, W_OK) && !stat(to, &sb)) {
144 (void)fprintf(stderr, "override mode %o on %s? ",
145 sb.st_mode & 07777, to);
146 ask = 1;
147 }
148 if (ask) {
149 if ((ch = getchar()) != EOF && ch != '\n')
150 while (getchar() != '\n');
151 if (ch != 'y')
152 return(0);
153 }
154 }
155 if (!rename(from, to))
156 return(0);
157
158 if (errno != EXDEV) {
159 (void)fprintf(stderr,
160 "mv: rename %s to %s: %s\n", from, to, strerror(errno));
161 return(1);
162 }
163
164 /*
165 * If rename fails, and it's a regular file, do the copy internally;
166 * otherwise, use cp and rm.
167 */
168 if (stat(from, &sb)) {
169 (void)fprintf(stderr, "mv: %s: %s\n", from, strerror(errno));
170 return(1);
171 }
172 return(S_ISREG(sb.st_mode) ?
173 fastcopy(from, to, &sb) : copy(from, to));
174 }
175
176 fastcopy(from, to, sbp)
177 char *from, *to;
178 struct stat *sbp;
179 {
180 struct timeval tval[2];
181 static u_int blen;
182 static char *bp;
183 register int nread, from_fd, to_fd;
184
185 if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
186 error(from);
187 return(1);
188 }
189 if ((to_fd = open(to, O_CREAT|O_TRUNC|O_WRONLY, sbp->st_mode)) < 0) {
190 error(to);
191 (void)close(from_fd);
192 return(1);
193 }
194 if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
195 error(NULL);
196 return(1);
197 }
198 while ((nread = read(from_fd, bp, blen)) > 0)
199 if (write(to_fd, bp, nread) != nread) {
200 error(to);
201 goto err;
202 }
203 if (nread < 0) {
204 error(from);
205 err: (void)unlink(to);
206 (void)close(from_fd);
207 (void)close(to_fd);
208 return(1);
209 }
210 (void)fchown(to_fd, sbp->st_uid, sbp->st_gid);
211 (void)fchmod(to_fd, sbp->st_mode);
212
213 (void)close(from_fd);
214 (void)close(to_fd);
215
216 tval[0].tv_sec = sbp->st_atime;
217 tval[1].tv_sec = sbp->st_mtime;
218 tval[0].tv_usec = tval[1].tv_usec = 0;
219 (void)utimes(to, tval);
220 (void)unlink(from);
221 return(0);
222 }
223
224 copy(from, to)
225 char *from, *to;
226 {
227 int pid, status;
228
229 if (!(pid = vfork())) {
230 execl(_PATH_CP, "mv", "-pr", from, to, NULL);
231 error(_PATH_CP);
232 _exit(1);
233 }
234 (void)waitpid(pid, &status, 0);
235 if (!WIFEXITED(status) || WEXITSTATUS(status))
236 return(1);
237 if (!(pid = vfork())) {
238 execl(_PATH_RM, "mv", "-rf", from, NULL);
239 error(_PATH_RM);
240 _exit(1);
241 }
242 (void)waitpid(pid, &status, 0);
243 return(!WIFEXITED(status) || WEXITSTATUS(status));
244 }
245
246 error(s)
247 char *s;
248 {
249 if (s)
250 (void)fprintf(stderr, "mv: %s: %s\n", s, strerror(errno));
251 else
252 (void)fprintf(stderr, "mv: %s\n", strerror(errno));
253 }
254
255 usage()
256 {
257 (void)fprintf(stderr,
258 "usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n");
259 exit(1);
260 }
261