1 /* vi: set sw=4 ts=4: */
3 * Mini mv implementation for busybox
5 * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
12 * Size reduction and improved error checking.
15 //config: bool "mv (10 kb)"
18 //config: mv is used to move or rename files or directories.
20 //applet:IF_MV(APPLET_NOEXEC(mv, mv, BB_DIR_BIN, BB_SUID_DROP, mv))
21 /* NOEXEC despite cases when it can be a "runner" (mv LARGE_DIR OTHER_FS) */
23 //kbuild:lib-$(CONFIG_MV) += mv.o
25 //usage:#define mv_trivial_usage
26 //usage: "[-finT] SOURCE DEST\n"
27 //usage: "or: mv [-fin] SOURCE... { -t DIRECTORY | DIRECTORY }"
28 //usage:#define mv_full_usage "\n\n"
29 //usage: "Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n"
30 //usage: "\n -f Don't prompt before overwriting"
31 //usage: "\n -i Interactive, prompt before overwrite"
32 //usage: "\n -n Don't overwrite an existing file"
33 //usage: "\n -T Refuse to move if DEST is a directory"
34 //usage: "\n -t DIR Move all SOURCEs into DIR"
36 //usage:#define mv_example_usage
37 //usage: "$ mv /tmp/foo /bin/bar\n"
40 #include "libcoreutils/coreutils.h"
42 int mv_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
43 int mv_main(int argc
, char **argv
)
53 #define OPT_FORCE (1 << 0)
54 #define OPT_INTERACTIVE (1 << 1)
55 #define OPT_NOCLOBBER (1 << 2)
56 #define OPT_DESTNOTDIR (1 << 3)
57 #define OPT_DESTDIR (1 << 4)
58 #define OPT_VERBOSE ((1 << 5) * ENABLE_FEATURE_VERBOSE)
59 flags
= getopt32long(argv
, "^"
62 /* At least one argument. (Usually two+, but -t DIR can have only one) */
64 /* only the final one of -f, -i, -n takes effect */
66 /* -t and -T don't mix */
68 "interactive\0" No_argument
"i"
69 "force\0" No_argument
"f"
70 "no-clobber\0" No_argument
"n"
71 "no-target-directory\0" No_argument
"T"
72 "target-directory\0" Required_argument
"t"
74 "verbose\0" No_argument
"v"
81 if (!(flags
& OPT_DESTDIR
)) {
82 last
= argv
[argc
- 1];
86 if (flags
& OPT_DESTNOTDIR
)
88 /* "mv A B C... DIR" - target must be dir */
89 } else /* argc == 2 */ {
90 /* "mv A B" - only case where target can be not a dir */
91 dest_exists
= cp_mv_stat(last
, &statbuf
);
92 if (dest_exists
< 0) { /* error other than ENOENT */
95 if (!(dest_exists
& 2)) {
96 /* last is not a directory */
100 /* last is a directory */
101 if (flags
& OPT_DESTNOTDIR
) {
102 if (stat(argv
[0], &statbuf
) == 0 && !S_ISDIR(statbuf
.st_mode
))
103 bb_error_msg_and_die("'%s' is a directory", last
);
104 /* "mv -T DIR1 DIR2" is allowed (renames a dir) */
108 /* else: fall through into "do { move SRC to DIR/SRC } while" loop */
111 /* else: last is DIR from "-t DIR" */
114 dest
= concat_path_file(last
, bb_get_last_path_component_strip(*argv
));
115 dest_exists
= cp_mv_stat(dest
, &statbuf
);
116 if (dest_exists
< 0) {
122 if (flags
& OPT_NOCLOBBER
)
124 if (!(flags
& OPT_FORCE
)
125 && ((access(dest
, W_OK
) < 0 && isatty(0))
126 || (flags
& OPT_INTERACTIVE
))
128 if (fprintf(stderr
, "mv: overwrite '%s'? ", dest
) < 0) {
129 goto RET_1
; /* Ouch! fprintf failed! */
131 if (!bb_ask_y_confirmation()) {
137 if (rename(*argv
, dest
) < 0) {
141 || (source_exists
= cp_mv_stat2(*argv
, &statbuf
, lstat
)) < 1
143 bb_perror_msg("can't rename '%s'", *argv
);
145 static const char fmt
[] ALIGN1
=
146 "can't overwrite %sdirectory with %sdirectory";
149 if (dest_exists
== 3) {
150 if (source_exists
!= 3) {
151 bb_error_msg(fmt
, "", "non-");
155 if (source_exists
== 3) {
156 bb_error_msg(fmt
, "non-", "");
160 if (unlink(dest
) < 0) {
161 bb_perror_msg("can't remove '%s'", dest
);
165 /* FILEUTILS_RECUR also prevents nasties like
166 * "read from device and write contents to dst"
167 * instead of "create same device node" */
168 copy_flag
= FILEUTILS_RECUR
| FILEUTILS_PRESERVE_STATUS
;
170 copy_flag
|= FILEUTILS_PRESERVE_SECURITY_CONTEXT
;
172 if ((copy_file(*argv
, dest
, copy_flag
) >= 0)
173 && (remove_file(*argv
, FILEUTILS_RECUR
| FILEUTILS_FORCE
) >= 0)
182 if (flags
& OPT_VERBOSE
) {
183 printf("'%s' -> '%s'\n", *argv
, dest
);
188 } while (*++argv
&& *argv
!= last
);