diff options
-rw-r--r-- | editors/patch.c | 35 | ||||
-rw-r--r-- | include/usage.h | 15 | ||||
-rwxr-xr-x | testsuite/patch.tests | 47 |
3 files changed, 82 insertions, 15 deletions
diff --git a/editors/patch.c b/editors/patch.c index 4a9715144..580ee147c 100644 --- a/editors/patch.c +++ b/editors/patch.c | |||
@@ -78,12 +78,23 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
78 | enum { | 78 | enum { |
79 | OPT_R = (1 << 2), | 79 | OPT_R = (1 << 2), |
80 | OPT_N = (1 << 3), | 80 | OPT_N = (1 << 3), |
81 | OPT_dry_run = (1 << 4) * ENABLE_LONG_OPTS, | ||
81 | }; | 82 | }; |
82 | 83 | ||
83 | xfunc_error_retval = 2; | 84 | xfunc_error_retval = 2; |
84 | { | 85 | { |
85 | const char *p = "-1"; | 86 | const char *p = "-1"; |
86 | const char *i = "-"; /* compat */ | 87 | const char *i = "-"; /* compat */ |
88 | #if ENABLE_LONG_OPTS | ||
89 | static const char patch_longopts[] ALIGN1 = | ||
90 | "strip\0" Required_argument "p" | ||
91 | "input\0" Required_argument "i" | ||
92 | "reverse\0" No_argument "R" | ||
93 | "forward\0" No_argument "N" | ||
94 | "dry-run\0" No_argument "\xff" | ||
95 | ; | ||
96 | applet_long_options = patch_longopts; | ||
97 | #endif | ||
87 | opt = getopt32(argv, "p:i:RN", &p, &i); | 98 | opt = getopt32(argv, "p:i:RN", &p, &i); |
88 | if (opt & OPT_R) | 99 | if (opt & OPT_R) |
89 | plus = '-'; | 100 | plus = '-'; |
@@ -97,7 +108,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
97 | FILE *dst_stream; | 108 | FILE *dst_stream; |
98 | //char *old_filename; | 109 | //char *old_filename; |
99 | char *new_filename; | 110 | char *new_filename; |
100 | char *backup_filename; | 111 | char *backup_filename = NULL; |
101 | unsigned src_cur_line = 1; | 112 | unsigned src_cur_line = 1; |
102 | unsigned dst_cur_line = 0; | 113 | unsigned dst_cur_line = 0; |
103 | unsigned dst_beg_line; | 114 | unsigned dst_beg_line; |
@@ -131,16 +142,21 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
131 | bb_make_directory(new_filename, -1, FILEUTILS_RECUR); | 142 | bb_make_directory(new_filename, -1, FILEUTILS_RECUR); |
132 | *slash = '/'; | 143 | *slash = '/'; |
133 | } | 144 | } |
134 | backup_filename = NULL; | ||
135 | src_stream = NULL; | 145 | src_stream = NULL; |
136 | saved_stat.st_mode = 0644; | 146 | saved_stat.st_mode = 0644; |
137 | } else { | 147 | } else if (!(opt & OPT_dry_run)) { |
138 | backup_filename = xasprintf("%s.orig", new_filename); | 148 | backup_filename = xasprintf("%s.orig", new_filename); |
139 | xrename(new_filename, backup_filename); | 149 | xrename(new_filename, backup_filename); |
140 | src_stream = xfopen_for_read(backup_filename); | 150 | src_stream = xfopen_for_read(backup_filename); |
151 | } else | ||
152 | src_stream = xfopen_for_read(new_filename); | ||
153 | |||
154 | if (opt & OPT_dry_run) { | ||
155 | dst_stream = xfopen_for_write("/dev/null"); | ||
156 | } else { | ||
157 | dst_stream = xfopen_for_write(new_filename); | ||
158 | fchmod(fileno(dst_stream), saved_stat.st_mode); | ||
141 | } | 159 | } |
142 | dst_stream = xfopen_for_write(new_filename); | ||
143 | fchmod(fileno(dst_stream), saved_stat.st_mode); | ||
144 | 160 | ||
145 | printf("patching file %s\n", new_filename); | 161 | printf("patching file %s\n", new_filename); |
146 | 162 | ||
@@ -189,6 +205,11 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
189 | patch_line = xmalloc_fgets(patch_file); | 205 | patch_line = xmalloc_fgets(patch_file); |
190 | if (patch_line == NULL) | 206 | if (patch_line == NULL) |
191 | break; /* EOF */ | 207 | break; /* EOF */ |
208 | if (!*patch_line) { | ||
209 | /* whitespace-damaged patch with "" lines */ | ||
210 | free(patch_line); | ||
211 | patch_line = xstrdup(" "); | ||
212 | } | ||
192 | if ((*patch_line != '-') && (*patch_line != '+') | 213 | if ((*patch_line != '-') && (*patch_line != '+') |
193 | && (*patch_line != ' ') | 214 | && (*patch_line != ' ') |
194 | ) { | 215 | ) { |
@@ -244,7 +265,9 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
244 | if (backup_filename) { | 265 | if (backup_filename) { |
245 | unlink(backup_filename); | 266 | unlink(backup_filename); |
246 | } | 267 | } |
247 | if ((dst_cur_line == 0) || (dst_beg_line == 0)) { | 268 | if (!(opt & OPT_dry_run) |
269 | && ((dst_cur_line == 0) || (dst_beg_line == 0)) | ||
270 | ) { | ||
248 | /* The new patched file is empty, remove it */ | 271 | /* The new patched file is empty, remove it */ |
249 | xunlink(new_filename); | 272 | xunlink(new_filename); |
250 | // /* old_filename and new_filename may be the same file */ | 273 | // /* old_filename and new_filename may be the same file */ |
diff --git a/include/usage.h b/include/usage.h index d03ec2295..85561c558 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -898,7 +898,7 @@ | |||
898 | "\n -d Daemonize" \ | 898 | "\n -d Daemonize" \ |
899 | 899 | ||
900 | #define dos2unix_trivial_usage \ | 900 | #define dos2unix_trivial_usage \ |
901 | "[OPTION] [FILE]" | 901 | "[OPTIONS] [FILE]" |
902 | #define dos2unix_full_usage "\n\n" \ | 902 | #define dos2unix_full_usage "\n\n" \ |
903 | "Convert FILE in-place from DOS to Unix format.\n" \ | 903 | "Convert FILE in-place from DOS to Unix format.\n" \ |
904 | "When no file is given, use stdin/stdout.\n" \ | 904 | "When no file is given, use stdin/stdout.\n" \ |
@@ -907,7 +907,7 @@ | |||
907 | "\n -d unix2dos" \ | 907 | "\n -d unix2dos" \ |
908 | 908 | ||
909 | #define unix2dos_trivial_usage \ | 909 | #define unix2dos_trivial_usage \ |
910 | "[OPTION] [FILE]" | 910 | "[OPTIONS] [FILE]" |
911 | #define unix2dos_full_usage "\n\n" \ | 911 | #define unix2dos_full_usage "\n\n" \ |
912 | "Convert FILE in-place from Unix to DOS format.\n" \ | 912 | "Convert FILE in-place from Unix to DOS format.\n" \ |
913 | "When no file is given, use stdin/stdout.\n" \ | 913 | "When no file is given, use stdin/stdout.\n" \ |
@@ -3250,12 +3250,21 @@ | |||
3250 | ) | 3250 | ) |
3251 | 3251 | ||
3252 | #define patch_trivial_usage \ | 3252 | #define patch_trivial_usage \ |
3253 | "[-p NUM] [-i DIFF] [-R] [-N]" | 3253 | "[OPTIONS] [ORIGFILE [PATCHFILE]]" |
3254 | #define patch_full_usage "\n\n" \ | 3254 | #define patch_full_usage "\n\n" \ |
3255 | IF_LONG_OPTS( \ | ||
3256 | " -p,--strip NUM Strip NUM leading components from file names" \ | ||
3257 | "\n -i,--input DIFF Read DIFF instead of stdin" \ | ||
3258 | "\n -R,--reverse Reverse patch" \ | ||
3259 | "\n -N,--forward Ignore already applied patches" \ | ||
3260 | "\n --dry-run Don't actually change files" \ | ||
3261 | ) \ | ||
3262 | IF_NOT_LONG_OPTS( \ | ||
3255 | " -p NUM Strip NUM leading components from file names" \ | 3263 | " -p NUM Strip NUM leading components from file names" \ |
3256 | "\n -i DIFF Read DIFF instead of stdin" \ | 3264 | "\n -i DIFF Read DIFF instead of stdin" \ |
3257 | "\n -R Reverse patch" \ | 3265 | "\n -R Reverse patch" \ |
3258 | "\n -N Ignore already applied patches" \ | 3266 | "\n -N Ignore already applied patches" \ |
3267 | ) | ||
3259 | 3268 | ||
3260 | #define patch_example_usage \ | 3269 | #define patch_example_usage \ |
3261 | "$ patch -p1 < example.diff\n" \ | 3270 | "$ patch -p1 < example.diff\n" \ |
diff --git a/testsuite/patch.tests b/testsuite/patch.tests index cfe69b76a..178048d2a 100755 --- a/testsuite/patch.tests +++ b/testsuite/patch.tests | |||
@@ -7,7 +7,7 @@ | |||
7 | # testing "test name" "options" "expected result" "file input" "stdin" | 7 | # testing "test name" "options" "expected result" "file input" "stdin" |
8 | 8 | ||
9 | testing "patch with old_file == new_file" \ | 9 | testing "patch with old_file == new_file" \ |
10 | "patch; echo $?; cat input" \ | 10 | 'patch; echo $?; cat input' \ |
11 | "\ | 11 | "\ |
12 | patching file input | 12 | patching file input |
13 | 0 | 13 | 0 |
@@ -15,7 +15,10 @@ qwe | |||
15 | asd | 15 | asd |
16 | zxc | 16 | zxc |
17 | " \ | 17 | " \ |
18 | "qwe\nzxc\n" \ | 18 | "\ |
19 | qwe | ||
20 | zxc | ||
21 | " \ | ||
19 | "\ | 22 | "\ |
20 | --- input Jan 01 01:01:01 2000 | 23 | --- input Jan 01 01:01:01 2000 |
21 | +++ input Jan 01 01:01:01 2000 | 24 | +++ input Jan 01 01:01:01 2000 |
@@ -26,7 +29,7 @@ zxc | |||
26 | " \ | 29 | " \ |
27 | 30 | ||
28 | testing "patch with nonexistent old_file" \ | 31 | testing "patch with nonexistent old_file" \ |
29 | "patch; echo $?; cat input" \ | 32 | 'patch; echo $?; cat input' \ |
30 | "\ | 33 | "\ |
31 | patching file input | 34 | patching file input |
32 | 0 | 35 | 0 |
@@ -34,7 +37,10 @@ qwe | |||
34 | asd | 37 | asd |
35 | zxc | 38 | zxc |
36 | " \ | 39 | " \ |
37 | "qwe\nzxc\n" \ | 40 | "\ |
41 | qwe | ||
42 | zxc | ||
43 | " \ | ||
38 | "\ | 44 | "\ |
39 | --- input.doesnt_exist Jan 01 01:01:01 2000 | 45 | --- input.doesnt_exist Jan 01 01:01:01 2000 |
40 | +++ input Jan 01 01:01:01 2000 | 46 | +++ input Jan 01 01:01:01 2000 |
@@ -45,14 +51,18 @@ zxc | |||
45 | " \ | 51 | " \ |
46 | 52 | ||
47 | testing "patch -R with nonexistent old_file" \ | 53 | testing "patch -R with nonexistent old_file" \ |
48 | "patch -R; echo $?; cat input" \ | 54 | 'patch -R; echo $?; cat input' \ |
49 | "\ | 55 | "\ |
50 | patching file input | 56 | patching file input |
51 | 0 | 57 | 0 |
52 | qwe | 58 | qwe |
53 | zxc | 59 | zxc |
54 | " \ | 60 | " \ |
55 | "qwe\nasd\nzxc\n" \ | 61 | "\ |
62 | qwe | ||
63 | asd | ||
64 | zxc | ||
65 | " \ | ||
56 | "\ | 66 | "\ |
57 | --- input.doesnt_exist Jan 01 01:01:01 2000 | 67 | --- input.doesnt_exist Jan 01 01:01:01 2000 |
58 | +++ input Jan 01 01:01:01 2000 | 68 | +++ input Jan 01 01:01:01 2000 |
@@ -62,4 +72,29 @@ zxc | |||
62 | zxc | 72 | zxc |
63 | " \ | 73 | " \ |
64 | 74 | ||
75 | testing "patch detects already applied hunk" \ | ||
76 | 'patch 2>&1; echo $?; cat input' \ | ||
77 | "\ | ||
78 | patching file input | ||
79 | patch: hunk #1 FAILED at 1 | ||
80 | patch: 1 out of 1 hunk FAILED | ||
81 | 1 | ||
82 | abc | ||
83 | def | ||
84 | 123 | ||
85 | " \ | ||
86 | "\ | ||
87 | abc | ||
88 | def | ||
89 | 123 | ||
90 | " \ | ||
91 | "\ | ||
92 | --- input.old Jan 01 01:01:01 2000 | ||
93 | +++ input Jan 01 01:01:01 2000 | ||
94 | @@ -1,2 +1,3 @@ | ||
95 | abc | ||
96 | +def | ||
97 | 123 | ||
98 | " \ | ||
99 | |||
65 | exit $FAILCOUNT | 100 | exit $FAILCOUNT |