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>
This commit is contained in:
parent
ed2af2e82d
commit
286b33721d
@ -97,6 +97,12 @@ enum {
|
|||||||
OPT_in_place = 1 << 0,
|
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. */
|
/* Each sed command turns into one of these structures. */
|
||||||
typedef struct sed_cmd_s {
|
typedef struct sed_cmd_s {
|
||||||
/* Ordered by alignment requirements: currently 36 bytes on x86 */
|
/* Ordered by alignment requirements: currently 36 bytes on x86 */
|
||||||
@ -151,6 +157,11 @@ struct globals {
|
|||||||
/* linked list of append lines */
|
/* linked list of append lines */
|
||||||
llist_t *append_head;
|
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;
|
char *add_cmd_line;
|
||||||
|
|
||||||
struct pipeline {
|
struct pipeline {
|
||||||
@ -211,6 +222,22 @@ static void sed_free_and_close_stuff(void)
|
|||||||
void sed_free_and_close_stuff(void);
|
void sed_free_and_close_stuff(void);
|
||||||
#endif
|
#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 */
|
/* If something bad happens during -i operation, delete temp file */
|
||||||
|
|
||||||
static void cleanup_outname(void)
|
static void cleanup_outname(void)
|
||||||
@ -446,7 +473,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
|
|||||||
{
|
{
|
||||||
char *fname;
|
char *fname;
|
||||||
idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &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';
|
sed_cmd->sw_last_char = '\n';
|
||||||
free(fname);
|
free(fname);
|
||||||
break;
|
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);
|
cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string);
|
||||||
if (sed_cmd->cmd == 'w') {
|
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';
|
sed_cmd->sw_last_char = '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,6 +405,15 @@ testing "sed ^ OR not^" \
|
|||||||
"" \
|
"" \
|
||||||
"abca\n"
|
"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"
|
# testing "description" "commands" "result" "infile" "stdin"
|
||||||
|
|
||||||
exit $FAILCOUNT
|
exit $FAILCOUNT
|
||||||
|
Loading…
Reference in New Issue
Block a user