diff options
author | Denys Vlasenko | 2022-01-04 19:42:36 +0100 |
---|---|---|
committer | Denys Vlasenko | 2022-01-04 19:42:36 +0100 |
commit | 286b33721d5f6afd615f752ea83bbd72658c6bb9 (patch) | |
tree | 93c9efb8b08a9674eca57c5ce1f30bdc6f54f9b3 | |
parent | ed2af2e82dbcfccb7392e9fbc3f837de1594c103 (diff) | |
download | busybox-286b33721d5f6afd615f752ea83bbd72658c6bb9.zip busybox-286b33721d5f6afd615f752ea83bbd72658c6bb9.tar.gz |
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 e8c82ac..48b0dbf 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -97,6 +97,12 @@ enum { OPT_in_place = 1 << 0, }; +struct sed_FILE { + struct sed_FILE *next; /* Next (linked list, NULL terminated) */ + const char *fname; + FILE *fp; +}; + /* Each sed command turns into one of these structures. */ typedef struct sed_cmd_s { /* Ordered by alignment requirements: currently 36 bytes on x86 */ @@ -151,6 +157,11 @@ struct globals { /* linked list of append lines */ llist_t *append_head; + /* linked list of FILEs opened for 'w' and s///w'. + * Needed to handle duplicate fnames: sed '/a/w F;/b/w F' + */ + struct sed_FILE *FILE_head; + char *add_cmd_line; struct pipeline { @@ -211,6 +222,22 @@ static void sed_free_and_close_stuff(void) void sed_free_and_close_stuff(void); #endif +static FILE *sed_xfopen_w(const char *fname) +{ + struct sed_FILE **pp = &G.FILE_head; + struct sed_FILE *cur; + while ((cur = *pp) != NULL) { + if (strcmp(cur->fname, fname) == 0) + return cur->fp; + pp = &cur->next; + } + *pp = cur = xzalloc(sizeof(*cur)); + /*cur->next = NULL; - already is */ + cur->fname = xstrdup(fname); + cur->fp = xfopen_for_write(fname); + return cur->fp; +} + /* If something bad happens during -i operation, delete temp file */ static void cleanup_outname(void) @@ -446,7 +473,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) { char *fname; idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname); - sed_cmd->sw_file = xfopen_for_write(fname); + sed_cmd->sw_file = sed_xfopen_w(fname); sed_cmd->sw_last_char = '\n'; free(fname); break; @@ -561,7 +588,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) } cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); if (sed_cmd->cmd == 'w') { - sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); + sed_cmd->sw_file = sed_xfopen_w(sed_cmd->string); sed_cmd->sw_last_char = '\n'; } } diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 2b78c9b..e62b839 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests @@ -405,6 +405,15 @@ testing "sed ^ OR not^" \ "" \ "abca\n" +# This only works if file name is exactly the same. +# For example, w FILE; w ./FILE won't work. +testing "sed understands duplicate file name" \ + "sed -n -e '/a/w sed.output' -e '/c/w sed.output' 2>&1 && cat sed.output && rm sed.output" \ + "a\nc\n" \ + "" \ + "a\nb\nc\n" + + # testing "description" "commands" "result" "infile" "stdin" exit $FAILCOUNT |