diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-17 12:59:33 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-17 12:59:33 +0200 |
commit | 91bc01c59b1be5c8a198f72078421e0b6e306a18 (patch) | |
tree | b5138b9f47f34be25b8ab5d25b121016e50c53f4 | |
parent | 894466cc5182a022051868ede316f378071020cd (diff) | |
download | busybox-w32-91bc01c59b1be5c8a198f72078421e0b6e306a18.tar.gz busybox-w32-91bc01c59b1be5c8a198f72078421e0b6e306a18.tar.bz2 busybox-w32-91bc01c59b1be5c8a198f72078421e0b6e306a18.zip |
mv: implement -t and -T
function old new delta
mv_main 496 585 +89
.rodata 103188 103242 +54
packed_usage 33549 33590 +41
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 184/0) Total: 184 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | coreutils/mv.c | 74 |
1 files changed, 50 insertions, 24 deletions
diff --git a/coreutils/mv.c b/coreutils/mv.c index f5ed9fcfc..eae2e99bb 100644 --- a/coreutils/mv.c +++ b/coreutils/mv.c | |||
@@ -23,13 +23,15 @@ | |||
23 | //kbuild:lib-$(CONFIG_MV) += mv.o | 23 | //kbuild:lib-$(CONFIG_MV) += mv.o |
24 | 24 | ||
25 | //usage:#define mv_trivial_usage | 25 | //usage:#define mv_trivial_usage |
26 | //usage: "[-fin] SOURCE DEST\n" | 26 | //usage: "[-finT] SOURCE DEST\n" |
27 | //usage: "or: mv [-fin] SOURCE... DIRECTORY" | 27 | //usage: "or: mv [-fin] SOURCE... { -t DIRECTORY | DIRECTORY }" |
28 | //usage:#define mv_full_usage "\n\n" | 28 | //usage:#define mv_full_usage "\n\n" |
29 | //usage: "Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n" | 29 | //usage: "Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n" |
30 | //usage: "\n -f Don't prompt before overwriting" | 30 | //usage: "\n -f Don't prompt before overwriting" |
31 | //usage: "\n -i Interactive, prompt before overwrite" | 31 | //usage: "\n -i Interactive, prompt before overwrite" |
32 | //usage: "\n -n Don't overwrite an existing file" | 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" | ||
33 | //usage: | 35 | //usage: |
34 | //usage:#define mv_example_usage | 36 | //usage:#define mv_example_usage |
35 | //usage: "$ mv /tmp/foo /bin/bar\n" | 37 | //usage: "$ mv /tmp/foo /bin/bar\n" |
@@ -40,7 +42,7 @@ | |||
40 | int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 42 | int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
41 | int mv_main(int argc, char **argv) | 43 | int mv_main(int argc, char **argv) |
42 | { | 44 | { |
43 | struct stat dest_stat; | 45 | struct stat statbuf; |
44 | const char *last; | 46 | const char *last; |
45 | const char *dest; | 47 | const char *dest; |
46 | unsigned flags; | 48 | unsigned flags; |
@@ -51,41 +53,66 @@ int mv_main(int argc, char **argv) | |||
51 | #define OPT_FORCE (1 << 0) | 53 | #define OPT_FORCE (1 << 0) |
52 | #define OPT_INTERACTIVE (1 << 1) | 54 | #define OPT_INTERACTIVE (1 << 1) |
53 | #define OPT_NOCLOBBER (1 << 2) | 55 | #define OPT_NOCLOBBER (1 << 2) |
54 | #define OPT_VERBOSE ((1 << 3) * ENABLE_FEATURE_VERBOSE) | 56 | #define OPT_DESTNOTDIR (1 << 3) |
55 | /* Need at least two arguments. | 57 | #define OPT_DESTDIR (1 << 4) |
56 | * If more than one of -f, -i, -n is specified , only the final one | 58 | #define OPT_VERBOSE ((1 << 5) * ENABLE_FEATURE_VERBOSE) |
57 | * takes effect (it unsets previous options). | ||
58 | */ | ||
59 | flags = getopt32long(argv, "^" | 59 | flags = getopt32long(argv, "^" |
60 | "finv" | 60 | "finTt:v" |
61 | "\0" | 61 | "\0" |
62 | "-2:f-in:i-fn:n-fi", | 62 | /* At least one argument. (Usually two+, but -t DIR can have only one) */ |
63 | "-1" | ||
64 | /* only the final one of -f, -i, -n takes effect */ | ||
65 | ":f-in:i-fn:n-fi" | ||
66 | /* -t and -T don't mix */ | ||
67 | ":t--T:T--t", | ||
63 | "interactive\0" No_argument "i" | 68 | "interactive\0" No_argument "i" |
64 | "force\0" No_argument "f" | 69 | "force\0" No_argument "f" |
65 | "no-clobber\0" No_argument "n" | 70 | "no-clobber\0" No_argument "n" |
71 | "no-target-directory\0" No_argument "T" | ||
72 | "target-directory\0" Required_argument "t" | ||
66 | IF_FEATURE_VERBOSE( | 73 | IF_FEATURE_VERBOSE( |
67 | "verbose\0" No_argument "v" | 74 | "verbose\0" No_argument "v", |
75 | &last | ||
68 | ) | 76 | ) |
69 | ); | 77 | ); |
70 | argc -= optind; | 78 | argc -= optind; |
71 | argv += optind; | 79 | argv += optind; |
72 | last = argv[argc - 1]; | ||
73 | 80 | ||
74 | if (argc == 2) { | 81 | if (!(flags & OPT_DESTDIR)) { |
75 | dest_exists = cp_mv_stat(last, &dest_stat); | 82 | last = argv[argc - 1]; |
76 | if (dest_exists < 0) { | 83 | if (argc < 2) |
77 | return EXIT_FAILURE; | 84 | bb_show_usage(); |
78 | } | 85 | if (argc != 2) { |
79 | 86 | if (flags & OPT_DESTNOTDIR) | |
80 | if (!(dest_exists & 2)) { /* last is not a directory */ | 87 | bb_show_usage(); |
81 | dest = last; | 88 | /* "mv A B C... DIR" - target must be dir */ |
82 | goto DO_MOVE; | 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 */ | ||
93 | return EXIT_FAILURE; | ||
94 | } | ||
95 | if (!(dest_exists & 2)) { | ||
96 | /* last is not a directory */ | ||
97 | dest = last; | ||
98 | goto DO_MOVE; | ||
99 | } | ||
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) */ | ||
105 | dest = last; | ||
106 | goto DO_MOVE; | ||
107 | } | ||
108 | /* else: fall through into "do { move SRC to DIR/SRC } while" loop */ | ||
83 | } | 109 | } |
84 | } | 110 | } |
111 | /* else: last is DIR from "t -DIR" */ | ||
85 | 112 | ||
86 | do { | 113 | do { |
87 | dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); | 114 | dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); |
88 | dest_exists = cp_mv_stat(dest, &dest_stat); | 115 | dest_exists = cp_mv_stat(dest, &statbuf); |
89 | if (dest_exists < 0) { | 116 | if (dest_exists < 0) { |
90 | goto RET_1; | 117 | goto RET_1; |
91 | } | 118 | } |
@@ -108,11 +135,10 @@ int mv_main(int argc, char **argv) | |||
108 | } | 135 | } |
109 | 136 | ||
110 | if (rename(*argv, dest) < 0) { | 137 | if (rename(*argv, dest) < 0) { |
111 | struct stat source_stat; | ||
112 | int source_exists; | 138 | int source_exists; |
113 | 139 | ||
114 | if (errno != EXDEV | 140 | if (errno != EXDEV |
115 | || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 | 141 | || (source_exists = cp_mv_stat2(*argv, &statbuf, lstat)) < 1 |
116 | ) { | 142 | ) { |
117 | bb_perror_msg("can't rename '%s'", *argv); | 143 | bb_perror_msg("can't rename '%s'", *argv); |
118 | } else { | 144 | } else { |