sed: correctly handle 'w FILE' commands writing to the same file
No binary (build failed)
—
(log yok)
| @@ -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'; | |
| } | |
| } | |
| @@ -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 | |