diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2022-01-04 19:42:36 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2022-01-04 19:42:36 +0100 |
commit | 286b33721d5f6afd615f752ea83bbd72658c6bb9 (patch) | |
tree | 93c9efb8b08a9674eca57c5ce1f30bdc6f54f9b3 | |
parent | ed2af2e82dbcfccb7392e9fbc3f837de1594c103 (diff) | |
download | busybox-w32-286b33721d5f6afd615f752ea83bbd72658c6bb9.tar.gz busybox-w32-286b33721d5f6afd615f752ea83bbd72658c6bb9.tar.bz2 busybox-w32-286b33721d5f6afd615f752ea83bbd72658c6bb9.zip |
sed: correctly handle 'w FILE' commands writing to the same file
function old new delta
sed_xfopen_w - 84 +84
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | editors/sed.c | 31 | ||||
-rwxr-xr-x | testsuite/sed.tests | 9 |
2 files changed, 38 insertions, 2 deletions
diff --git a/editors/sed.c b/editors/sed.c index e8c82ac63..48b0dbf67 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -97,6 +97,12 @@ enum { | |||
97 | OPT_in_place = 1 << 0, | 97 | OPT_in_place = 1 << 0, |
98 | }; | 98 | }; |
99 | 99 | ||
100 | struct sed_FILE { | ||
101 | struct sed_FILE *next; /* Next (linked list, NULL terminated) */ | ||
102 | const char *fname; | ||
103 | FILE *fp; | ||
104 | }; | ||
105 | |||
100 | /* Each sed command turns into one of these structures. */ | 106 | /* Each sed command turns into one of these structures. */ |
101 | typedef struct sed_cmd_s { | 107 | typedef struct sed_cmd_s { |
102 | /* Ordered by alignment requirements: currently 36 bytes on x86 */ | 108 | /* Ordered by alignment requirements: currently 36 bytes on x86 */ |
@@ -151,6 +157,11 @@ struct globals { | |||
151 | /* linked list of append lines */ | 157 | /* linked list of append lines */ |
152 | llist_t *append_head; | 158 | llist_t *append_head; |
153 | 159 | ||
160 | /* linked list of FILEs opened for 'w' and s///w'. | ||
161 | * Needed to handle duplicate fnames: sed '/a/w F;/b/w F' | ||
162 | */ | ||
163 | struct sed_FILE *FILE_head; | ||
164 | |||
154 | char *add_cmd_line; | 165 | char *add_cmd_line; |
155 | 166 | ||
156 | struct pipeline { | 167 | struct pipeline { |
@@ -211,6 +222,22 @@ static void sed_free_and_close_stuff(void) | |||
211 | void sed_free_and_close_stuff(void); | 222 | void sed_free_and_close_stuff(void); |
212 | #endif | 223 | #endif |
213 | 224 | ||
225 | static FILE *sed_xfopen_w(const char *fname) | ||
226 | { | ||
227 | struct sed_FILE **pp = &G.FILE_head; | ||
228 | struct sed_FILE *cur; | ||
229 | while ((cur = *pp) != NULL) { | ||
230 | if (strcmp(cur->fname, fname) == 0) | ||
231 | return cur->fp; | ||
232 | pp = &cur->next; | ||
233 | } | ||
234 | *pp = cur = xzalloc(sizeof(*cur)); | ||
235 | /*cur->next = NULL; - already is */ | ||
236 | cur->fname = xstrdup(fname); | ||
237 | cur->fp = xfopen_for_write(fname); | ||
238 | return cur->fp; | ||
239 | } | ||
240 | |||
214 | /* If something bad happens during -i operation, delete temp file */ | 241 | /* If something bad happens during -i operation, delete temp file */ |
215 | 242 | ||
216 | static void cleanup_outname(void) | 243 | static void cleanup_outname(void) |
@@ -446,7 +473,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) | |||
446 | { | 473 | { |
447 | char *fname; | 474 | char *fname; |
448 | idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname); | 475 | idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname); |
449 | sed_cmd->sw_file = xfopen_for_write(fname); | 476 | sed_cmd->sw_file = sed_xfopen_w(fname); |
450 | sed_cmd->sw_last_char = '\n'; | 477 | sed_cmd->sw_last_char = '\n'; |
451 | free(fname); | 478 | free(fname); |
452 | break; | 479 | break; |
@@ -561,7 +588,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) | |||
561 | } | 588 | } |
562 | cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); | 589 | cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); |
563 | if (sed_cmd->cmd == 'w') { | 590 | if (sed_cmd->cmd == 'w') { |
564 | sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); | 591 | sed_cmd->sw_file = sed_xfopen_w(sed_cmd->string); |
565 | sed_cmd->sw_last_char = '\n'; | 592 | sed_cmd->sw_last_char = '\n'; |
566 | } | 593 | } |
567 | } | 594 | } |
diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 2b78c9b12..e62b839f7 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests | |||
@@ -405,6 +405,15 @@ testing "sed ^ OR not^" \ | |||
405 | "" \ | 405 | "" \ |
406 | "abca\n" | 406 | "abca\n" |
407 | 407 | ||
408 | # This only works if file name is exactly the same. | ||
409 | # For example, w FILE; w ./FILE won't work. | ||
410 | testing "sed understands duplicate file name" \ | ||
411 | "sed -n -e '/a/w sed.output' -e '/c/w sed.output' 2>&1 && cat sed.output && rm sed.output" \ | ||
412 | "a\nc\n" \ | ||
413 | "" \ | ||
414 | "a\nb\nc\n" | ||
415 | |||
416 | |||
408 | # testing "description" "commands" "result" "infile" "stdin" | 417 | # testing "description" "commands" "result" "infile" "stdin" |
409 | 418 | ||
410 | exit $FAILCOUNT | 419 | exit $FAILCOUNT |