1 /* $NetBSD: mv.c,v 1.44 2015/03/02 03:17:24 enami Exp $ */
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Ken Smith of The State University of New York at Buffalo.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/cdefs.h>
37 __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
38 The Regents of the University of California. All rights reserved.");
43 static char sccsid
[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94";
45 __RCSID("$NetBSD: mv.c,v 1.44 2015/03/02 03:17:24 enami Exp $");
49 #include <sys/param.h>
53 #include <sys/extattr.h>
66 #include "pathnames.h"
68 static int fflg
, iflg
, vflg
;
71 static int copy(char *, char *);
72 static int do_move(char *, char *);
73 static int fastcopy(char *, char *, struct stat
*);
74 __dead
static void usage(void);
77 main(int argc
, char *argv
[])
82 char path
[MAXPATHLEN
+ 1];
86 (void)setlocale(LC_ALL
, "");
88 while ((ch
= getopt(argc
, argv
, "ifv")) != -1)
110 stdin_ok
= isatty(STDIN_FILENO
);
113 * If the stat on the target fails or the target isn't a directory,
114 * try the move. More than 2 arguments is an error in this case.
116 if (stat(argv
[argc
- 1], &sb
) || !S_ISDIR(sb
.st_mode
)) {
119 exit(do_move(argv
[0], argv
[1]));
122 /* It's a directory, move each file into it. */
123 baselen
= strlcpy(path
, argv
[argc
- 1], sizeof(path
));
124 if (baselen
>= sizeof(path
))
125 errx(1, "%s: destination pathname too long", argv
[argc
- 1]);
126 endp
= &path
[baselen
];
127 if (!baselen
|| *(endp
- 1) != '/') {
131 for (rval
= 0; --argc
; ++argv
) {
132 p
= *argv
+ strlen(*argv
) - 1;
133 while (*p
== '/' && p
!= *argv
)
135 if ((p
= strrchr(*argv
, '/')) == NULL
)
140 if ((baselen
+ (len
= strlen(p
))) >= MAXPATHLEN
) {
141 warnx("%s: destination pathname too long", *argv
);
144 memmove(endp
, p
, len
+ 1);
145 if (do_move(*argv
, path
))
154 do_move(char *from
, char *to
)
160 * (1) If the destination path exists, the -f option is not specified
161 * and either of the following conditions are true:
163 * (a) The permissions of the destination path do not permit
164 * writing and the standard input is a terminal.
165 * (b) The -i option is specified.
167 * the mv utility shall write a prompt to standard error and
168 * read a line from standard input. If the response is not
169 * affirmative, mv shall do nothing more with the current
172 if (!fflg
&& !access(to
, F_OK
)) {
177 if (access(from
, F_OK
)) {
178 warn("rename %s", from
);
181 (void)fprintf(stderr
, "overwrite %s? ", to
);
182 } else if (stdin_ok
&& access(to
, W_OK
) && !stat(to
, &sb
)) {
183 if (access(from
, F_OK
)) {
184 warn("rename %s", from
);
187 strmode(sb
.st_mode
, modep
);
188 (void)fprintf(stderr
, "override %s%s%s/%s for %s? ",
189 modep
+ 1, modep
[9] == ' ' ? "" : " ",
190 user_from_uid(sb
.st_uid
, 0),
191 group_from_gid(sb
.st_gid
, 0), to
);
195 if ((ch
= getchar()) != EOF
&& ch
!= '\n') {
197 while ((ch2
= getchar()) != EOF
&& ch2
!= '\n')
200 if (ch
!= 'y' && ch
!= 'Y')
206 * (2) If rename() succeeds, mv shall do nothing more with the
207 * current source file. If it fails for any other reason than
208 * EXDEV, mv shall write a diagnostic message to the standard
209 * error and do nothing more with the current source file.
211 * (3) If the destination path exists, and it is a file of type
212 * directory and source_file is not a file of type directory,
213 * or it is a file not of type directory, and source file is
214 * a file of type directory, mv shall write a diagnostic
215 * message to standard error, and do nothing more with the
216 * current source file...
218 if (!rename(from
, to
)) {
220 printf("%s -> %s\n", from
, to
);
224 if (errno
!= EXDEV
) {
225 warn("rename %s to %s", from
, to
);
230 * (4) If the destination path exists, mv shall attempt to remove it.
231 * If this fails for any reason, mv shall write a diagnostic
232 * message to the standard error and do nothing more with the
233 * current source file...
235 if (!lstat(to
, &sb
)) {
236 if ((S_ISDIR(sb
.st_mode
)) ? rmdir(to
) : unlink(to
)) {
237 warn("can't remove %s", to
);
243 * (5) The file hierarchy rooted in source_file shall be duplicated
244 * as a file hierarchy rooted in the destination path...
246 if (lstat(from
, &sb
)) {
251 return (S_ISREG(sb
.st_mode
) ?
252 fastcopy(from
, to
, &sb
) : copy(from
, to
));
256 fastcopy(char *from
, char *to
, struct stat
*sbp
)
258 #if defined(__NetBSD__)
259 struct timespec ts
[2];
261 struct timeval tval
[2];
263 static blksize_t blen
;
265 int nread
, from_fd
, to_fd
;
267 if ((from_fd
= open(from
, O_RDONLY
, 0)) < 0) {
272 open(to
, O_CREAT
| O_TRUNC
| O_WRONLY
, sbp
->st_mode
)) < 0) {
274 (void)close(from_fd
);
277 if (!blen
&& !(bp
= malloc(blen
= sbp
->st_blksize
))) {
280 (void)close(from_fd
);
284 while ((nread
= read(from_fd
, bp
, blen
)) > 0)
285 if (write(to_fd
, bp
, nread
) != nread
) {
292 warn("%s: remove", to
);
293 (void)close(from_fd
);
298 #if !defined(__minix)
299 if (fcpxattr(from_fd
, to_fd
) == -1)
300 warn("%s: error copying extended attributes", to
);
301 #endif /* !defined(__minix) */
303 (void)close(from_fd
);
305 #if defined(__NetBSD__)
306 ts
[0] = sbp
->st_atimespec
;
307 ts
[1] = sbp
->st_mtimespec
;
309 TIMESPEC_TO_TIMEVAL(&tval
[0], &sbp
->st_atimespec
);
310 TIMESPEC_TO_TIMEVAL(&tval
[1], &sbp
->st_mtimespec
);
313 tval
[0].tv_sec
= sbp
->st_atime
;
314 tval
[1].tv_sec
= sbp
->st_mtime
;
319 if (utimes(to
, tval
))
321 #if defined(__NetBSD__)
322 if (futimens(to_fd
, ts
))
324 if (futimes(to_fd
, tval
))
327 warn("%s: set times", to
);
328 if (fchown(to_fd
, sbp
->st_uid
, sbp
->st_gid
)) {
330 warn("%s: set owner/group", to
);
331 sbp
->st_mode
&= ~(S_ISUID
| S_ISGID
);
333 if (fchmod(to_fd
, sbp
->st_mode
))
334 warn("%s: set mode", to
);
335 #if !defined(__minix)
336 if (fchflags(to_fd
, sbp
->st_flags
) && (errno
!= EOPNOTSUPP
))
337 warn("%s: set flags (was: 0%07o)", to
, sbp
->st_flags
);
338 #endif /* !defined(__minix) */
346 warn("%s: remove", from
);
351 printf("%s -> %s\n", from
, to
);
357 copy(char *from
, char *to
)
362 if ((pid
= vfork()) == 0) {
363 execl(_PATH_CP
, "mv", vflg
? "-PRpv" : "-PRp", "--", from
, to
, NULL
);
364 warn("%s", _PATH_CP
);
367 if (waitpid(pid
, &status
, 0) == -1) {
368 warn("%s: waitpid", _PATH_CP
);
371 if (!WIFEXITED(status
)) {
372 warnx("%s: did not terminate normally", _PATH_CP
);
375 if (WEXITSTATUS(status
)) {
376 warnx("%s: terminated with %d (non-zero) status",
377 _PATH_CP
, WEXITSTATUS(status
));
380 if (!(pid
= vfork())) {
381 execl(_PATH_RM
, "mv", "-rf", "--", from
, NULL
);
382 warn("%s", _PATH_RM
);
385 if (waitpid(pid
, &status
, 0) == -1) {
386 warn("%s: waitpid", _PATH_RM
);
389 if (!WIFEXITED(status
)) {
390 warnx("%s: did not terminate normally", _PATH_RM
);
393 if (WEXITSTATUS(status
)) {
394 warnx("%s: terminated with %d (non-zero) status",
395 _PATH_RM
, WEXITSTATUS(status
));
404 (void)fprintf(stderr
, "usage: %s [-fiv] source target\n"
405 " %s [-fiv] source ... directory\n", getprogname(),