From 6394bcf17925715db042cfb24f5886b1bed1dfc9 Mon Sep 17 00:00:00 2001 From: Jackie Huang Date: Thu, 31 Oct 2013 14:36:31 +0800 Subject: [PATCH] sed: fix "sed clusternewline" and "autoinsert newline" testcase Upstream-Status: Backport [busybox.net] Signed-off-by: Jackie Huang --- editors/sed.c | 135 ++++++++++++++++++++++++++-------------------------- testsuite/sed.tests | 4 -- 2 files changed, 68 insertions(+), 71 deletions(-) diff --git a/editors/sed.c b/editors/sed.c index f8ca5d3..98478b4 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -845,37 +845,79 @@ static void append(char *s) llist_add_to_end(&G.append_head, xstrdup(s)); } -static void flush_append(void) +/* Output line of text. */ +/* Note: + * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. + * Without them, we had this: + * echo -n thingy >z1 + * echo -n again >z2 + * >znull + * sed "s/i/z/" z1 z2 znull | hexdump -vC + * output: + * gnu sed 4.1.5: + * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| + * bbox: + * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| + */ +enum { + NO_EOL_CHAR = 1, + LAST_IS_NUL = 2, +}; +static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) +{ + char lpc = *last_puts_char; + + /* Need to insert a '\n' between two files because first file's + * last line wasn't terminated? */ + if (lpc != '\n' && lpc != '\0') { + fputc('\n', file); + lpc = '\n'; + } + fputs(s, file); + + /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ + if (s[0]) + lpc = 'x'; + + /* had trailing '\0' and it was last char of file? */ + if (last_gets_char == LAST_IS_NUL) { + fputc('\0', file); + lpc = 'x'; /* */ + } else + /* had trailing '\n' or '\0'? */ + if (last_gets_char != NO_EOL_CHAR) { + fputc(last_gets_char, file); + lpc = last_gets_char; + } + + if (ferror(file)) { + xfunc_error_retval = 4; /* It's what gnu sed exits with... */ + bb_error_msg_and_die(bb_msg_write_error); + } + *last_puts_char = lpc; +} + +static void flush_append(char *last_puts_char, char last_gets_char) { char *data; /* Output appended lines. */ while ((data = (char *)llist_pop(&G.append_head))) { - fprintf(G.nonstdout, "%s\n", data); + puts_maybe_newline(data, G.nonstdout, last_puts_char, last_gets_char); free(data); } } -static void add_input_file(FILE *file) -{ - G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); - G.input_file_list[G.input_file_count++] = file; -} - /* Get next line of input from G.input_file_list, flushing append buffer and * noting if we ran out of files without a newline on the last line we read. */ -enum { - NO_EOL_CHAR = 1, - LAST_IS_NUL = 2, -}; -static char *get_next_line(char *gets_char) +static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets_char) { char *temp = NULL; int len; char gc; - flush_append(); + flush_append(last_puts_char, last_gets_char); /* will be returned if last line in the file * doesn't end with either '\n' or '\0' */ @@ -919,54 +961,6 @@ static char *get_next_line(char *gets_char) return temp; } -/* Output line of text. */ -/* Note: - * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. - * Without them, we had this: - * echo -n thingy >z1 - * echo -n again >z2 - * >znull - * sed "s/i/z/" z1 z2 znull | hexdump -vC - * output: - * gnu sed 4.1.5: - * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| - * bbox: - * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| - */ -static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) -{ - char lpc = *last_puts_char; - - /* Need to insert a '\n' between two files because first file's - * last line wasn't terminated? */ - if (lpc != '\n' && lpc != '\0') { - fputc('\n', file); - lpc = '\n'; - } - fputs(s, file); - - /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ - if (s[0]) - lpc = 'x'; - - /* had trailing '\0' and it was last char of file? */ - if (last_gets_char == LAST_IS_NUL) { - fputc('\0', file); - lpc = 'x'; /* */ - } else - /* had trailing '\n' or '\0'? */ - if (last_gets_char != NO_EOL_CHAR) { - fputc(last_gets_char, file); - lpc = last_gets_char; - } - - if (ferror(file)) { - xfunc_error_retval = 4; /* It's what gnu sed exits with... */ - bb_error_msg_and_die(bb_msg_write_error); - } - *last_puts_char = lpc; -} - #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n)) static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space) @@ -989,7 +983,7 @@ static void process_files(void) int substituted; /* Prime the pump */ - next_line = get_next_line(&next_gets_char); + next_line = get_next_line(&next_gets_char, &last_puts_char, '\n' /*last_gets_char*/); /* Go through every line in each file */ again: @@ -1003,7 +997,7 @@ static void process_files(void) /* Read one line in advance so we can act on the last line, * the '$' address */ - next_line = get_next_line(&next_gets_char); + next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); linenum++; /* For every line, go through all the commands */ @@ -1176,6 +1170,7 @@ static void process_files(void) /* Append line to linked list to be printed later */ case 'a': append(sed_cmd->string); + last_gets_char = '\n'; break; /* Insert text before this line */ @@ -1222,7 +1217,7 @@ static void process_files(void) free(pattern_space); pattern_space = next_line; last_gets_char = next_gets_char; - next_line = get_next_line(&next_gets_char); + next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); substituted = 0; linenum++; break; @@ -1258,7 +1253,7 @@ static void process_files(void) pattern_space[len] = '\n'; strcpy(pattern_space + len+1, next_line); last_gets_char = next_gets_char; - next_line = get_next_line(&next_gets_char); + next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); linenum++; break; } @@ -1362,7 +1357,7 @@ static void process_files(void) /* Delete and such jump here. */ discard_line: - flush_append(); + flush_append(&last_puts_char, last_gets_char); free(pattern_space); goto again; @@ -1403,6 +1398,12 @@ static void add_cmd_block(char *cmdstr) free(sv); } +static void add_input_file(FILE *file) +{ + G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); + G.input_file_list[G.input_file_count++] = file; +} + int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int sed_main(int argc UNUSED_PARAM, char **argv) { diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 468565f..e26483c 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests @@ -135,10 +135,8 @@ testing "sed empty file plus cat" "sed -e 's/nohit//' input -" "one\ntwo" \ "" "one\ntwo" testing "sed cat plus empty file" "sed -e 's/nohit//' input -" "one\ntwo" \ "one\ntwo" "" -test x"$SKIP_KNOWN_BUGS" = x"" && { testing "sed append autoinserts newline" "sed -e '/woot/a woo' -" \ "woot\nwoo\n" "" "woot" -} testing "sed insert doesn't autoinsert newline" "sed -e '/woot/i woo' -" \ "woo\nwoot" "" "woot" testing "sed print autoinsert newlines" "sed -e 'p' -" "one\none" "" "one" @@ -154,11 +152,9 @@ testing "sed selective matches insert newline" \ testing "sed selective matches noinsert newline" \ "sed -ne 's/woo/bang/p' input -" "a bang\nb bang" "a woo\nb woo" \ "c no\nd no" -test x"$SKIP_KNOWN_BUGS" = x"" && { testing "sed clusternewline" \ "sed -e '/one/a 111' -e '/two/i 222' -e p input -" \ "one\none\n111\n222\ntwo\ntwo" "one" "two" -} testing "sed subst+write" \ "sed -e 's/i/z/' -e 'woutputw' input -; $ECHO -n X; cat outputw" \ "thzngy\nagaznXthzngy\nagazn" "thingy" "again" -- 1.8.3