diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2013-10-30 14:25:22 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2013-10-30 14:25:22 +0100 |
commit | c44539fcd32a0861f9f540253b5e914028821bd7 (patch) | |
tree | 39f189a6bfd6a6a9515227ccb14a849a894a3860 | |
parent | a82e32d9ce199db01aaea92ebcb39a23d90e5b85 (diff) | |
download | busybox-w32-c44539fcd32a0861f9f540253b5e914028821bd7.tar.gz busybox-w32-c44539fcd32a0861f9f540253b5e914028821bd7.tar.bz2 busybox-w32-c44539fcd32a0861f9f540253b5e914028821bd7.zip |
sed: fix "sed clusternewline" testcase
function old new delta
process_files 2197 2226 +29
flush_append 47 54 +7
get_next_line 184 189 +5
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 41/0) Total: 41 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | editors/sed.c | 134 | ||||
-rwxr-xr-x | testsuite/sed.tests | 2 |
2 files changed, 67 insertions, 69 deletions
diff --git a/editors/sed.c b/editors/sed.c index 5577f3582..971375884 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -851,37 +851,79 @@ static void append(char *s) | |||
851 | llist_add_to_end(&G.append_head, s); | 851 | llist_add_to_end(&G.append_head, s); |
852 | } | 852 | } |
853 | 853 | ||
854 | static void flush_append(void) | 854 | /* Output line of text. */ |
855 | /* Note: | ||
856 | * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. | ||
857 | * Without them, we had this: | ||
858 | * echo -n thingy >z1 | ||
859 | * echo -n again >z2 | ||
860 | * >znull | ||
861 | * sed "s/i/z/" z1 z2 znull | hexdump -vC | ||
862 | * output: | ||
863 | * gnu sed 4.1.5: | ||
864 | * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| | ||
865 | * bbox: | ||
866 | * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| | ||
867 | */ | ||
868 | enum { | ||
869 | NO_EOL_CHAR = 1, | ||
870 | LAST_IS_NUL = 2, | ||
871 | }; | ||
872 | static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) | ||
873 | { | ||
874 | char lpc = *last_puts_char; | ||
875 | |||
876 | /* Need to insert a '\n' between two files because first file's | ||
877 | * last line wasn't terminated? */ | ||
878 | if (lpc != '\n' && lpc != '\0') { | ||
879 | fputc('\n', file); | ||
880 | lpc = '\n'; | ||
881 | } | ||
882 | fputs(s, file); | ||
883 | |||
884 | /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ | ||
885 | if (s[0]) | ||
886 | lpc = 'x'; | ||
887 | |||
888 | /* had trailing '\0' and it was last char of file? */ | ||
889 | if (last_gets_char == LAST_IS_NUL) { | ||
890 | fputc('\0', file); | ||
891 | lpc = 'x'; /* */ | ||
892 | } else | ||
893 | /* had trailing '\n' or '\0'? */ | ||
894 | if (last_gets_char != NO_EOL_CHAR) { | ||
895 | fputc(last_gets_char, file); | ||
896 | lpc = last_gets_char; | ||
897 | } | ||
898 | |||
899 | if (ferror(file)) { | ||
900 | xfunc_error_retval = 4; /* It's what gnu sed exits with... */ | ||
901 | bb_error_msg_and_die(bb_msg_write_error); | ||
902 | } | ||
903 | *last_puts_char = lpc; | ||
904 | } | ||
905 | |||
906 | static void flush_append(char *last_puts_char, char last_gets_char) | ||
855 | { | 907 | { |
856 | char *data; | 908 | char *data; |
857 | 909 | ||
858 | /* Output appended lines. */ | 910 | /* Output appended lines. */ |
859 | while ((data = (char *)llist_pop(&G.append_head))) { | 911 | while ((data = (char *)llist_pop(&G.append_head))) { |
860 | fprintf(G.nonstdout, "%s\n", data); | 912 | puts_maybe_newline(data, G.nonstdout, last_puts_char, last_gets_char); |
861 | free(data); | 913 | free(data); |
862 | } | 914 | } |
863 | } | 915 | } |
864 | 916 | ||
865 | static void add_input_file(FILE *file) | ||
866 | { | ||
867 | G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); | ||
868 | G.input_file_list[G.input_file_count++] = file; | ||
869 | } | ||
870 | |||
871 | /* Get next line of input from G.input_file_list, flushing append buffer and | 917 | /* Get next line of input from G.input_file_list, flushing append buffer and |
872 | * noting if we ran out of files without a newline on the last line we read. | 918 | * noting if we ran out of files without a newline on the last line we read. |
873 | */ | 919 | */ |
874 | enum { | 920 | static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets_char) |
875 | NO_EOL_CHAR = 1, | ||
876 | LAST_IS_NUL = 2, | ||
877 | }; | ||
878 | static char *get_next_line(char *gets_char) | ||
879 | { | 921 | { |
880 | char *temp = NULL; | 922 | char *temp = NULL; |
881 | int len; | 923 | int len; |
882 | char gc; | 924 | char gc; |
883 | 925 | ||
884 | flush_append(); | 926 | flush_append(last_puts_char, last_gets_char); |
885 | 927 | ||
886 | /* will be returned if last line in the file | 928 | /* will be returned if last line in the file |
887 | * doesn't end with either '\n' or '\0' */ | 929 | * doesn't end with either '\n' or '\0' */ |
@@ -925,54 +967,6 @@ static char *get_next_line(char *gets_char) | |||
925 | return temp; | 967 | return temp; |
926 | } | 968 | } |
927 | 969 | ||
928 | /* Output line of text. */ | ||
929 | /* Note: | ||
930 | * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. | ||
931 | * Without them, we had this: | ||
932 | * echo -n thingy >z1 | ||
933 | * echo -n again >z2 | ||
934 | * >znull | ||
935 | * sed "s/i/z/" z1 z2 znull | hexdump -vC | ||
936 | * output: | ||
937 | * gnu sed 4.1.5: | ||
938 | * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| | ||
939 | * bbox: | ||
940 | * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| | ||
941 | */ | ||
942 | static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) | ||
943 | { | ||
944 | char lpc = *last_puts_char; | ||
945 | |||
946 | /* Need to insert a '\n' between two files because first file's | ||
947 | * last line wasn't terminated? */ | ||
948 | if (lpc != '\n' && lpc != '\0') { | ||
949 | fputc('\n', file); | ||
950 | lpc = '\n'; | ||
951 | } | ||
952 | fputs(s, file); | ||
953 | |||
954 | /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ | ||
955 | if (s[0]) | ||
956 | lpc = 'x'; | ||
957 | |||
958 | /* had trailing '\0' and it was last char of file? */ | ||
959 | if (last_gets_char == LAST_IS_NUL) { | ||
960 | fputc('\0', file); | ||
961 | lpc = 'x'; /* */ | ||
962 | } else | ||
963 | /* had trailing '\n' or '\0'? */ | ||
964 | if (last_gets_char != NO_EOL_CHAR) { | ||
965 | fputc(last_gets_char, file); | ||
966 | lpc = last_gets_char; | ||
967 | } | ||
968 | |||
969 | if (ferror(file)) { | ||
970 | xfunc_error_retval = 4; /* It's what gnu sed exits with... */ | ||
971 | bb_error_msg_and_die(bb_msg_write_error); | ||
972 | } | ||
973 | *last_puts_char = lpc; | ||
974 | } | ||
975 | |||
976 | #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n)) | 970 | #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n)) |
977 | 971 | ||
978 | static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space) | 972 | static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space) |
@@ -995,7 +989,7 @@ static void process_files(void) | |||
995 | int substituted; | 989 | int substituted; |
996 | 990 | ||
997 | /* Prime the pump */ | 991 | /* Prime the pump */ |
998 | next_line = get_next_line(&next_gets_char); | 992 | next_line = get_next_line(&next_gets_char, &last_puts_char, '\n' /*last_gets_char*/); |
999 | 993 | ||
1000 | /* Go through every line in each file */ | 994 | /* Go through every line in each file */ |
1001 | again: | 995 | again: |
@@ -1009,7 +1003,7 @@ static void process_files(void) | |||
1009 | 1003 | ||
1010 | /* Read one line in advance so we can act on the last line, | 1004 | /* Read one line in advance so we can act on the last line, |
1011 | * the '$' address */ | 1005 | * the '$' address */ |
1012 | next_line = get_next_line(&next_gets_char); | 1006 | next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); |
1013 | linenum++; | 1007 | linenum++; |
1014 | 1008 | ||
1015 | /* For every line, go through all the commands */ | 1009 | /* For every line, go through all the commands */ |
@@ -1227,7 +1221,7 @@ static void process_files(void) | |||
1227 | free(pattern_space); | 1221 | free(pattern_space); |
1228 | pattern_space = next_line; | 1222 | pattern_space = next_line; |
1229 | last_gets_char = next_gets_char; | 1223 | last_gets_char = next_gets_char; |
1230 | next_line = get_next_line(&next_gets_char); | 1224 | next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); |
1231 | substituted = 0; | 1225 | substituted = 0; |
1232 | linenum++; | 1226 | linenum++; |
1233 | break; | 1227 | break; |
@@ -1263,7 +1257,7 @@ static void process_files(void) | |||
1263 | pattern_space[len] = '\n'; | 1257 | pattern_space[len] = '\n'; |
1264 | strcpy(pattern_space + len+1, next_line); | 1258 | strcpy(pattern_space + len+1, next_line); |
1265 | last_gets_char = next_gets_char; | 1259 | last_gets_char = next_gets_char; |
1266 | next_line = get_next_line(&next_gets_char); | 1260 | next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char); |
1267 | linenum++; | 1261 | linenum++; |
1268 | break; | 1262 | break; |
1269 | } | 1263 | } |
@@ -1367,7 +1361,7 @@ static void process_files(void) | |||
1367 | 1361 | ||
1368 | /* Delete and such jump here. */ | 1362 | /* Delete and such jump here. */ |
1369 | discard_line: | 1363 | discard_line: |
1370 | flush_append(); | 1364 | flush_append(&last_puts_char, last_gets_char); |
1371 | free(pattern_space); | 1365 | free(pattern_space); |
1372 | 1366 | ||
1373 | goto again; | 1367 | goto again; |
@@ -1394,6 +1388,12 @@ static void add_cmd_block(char *cmdstr) | |||
1394 | free(sv); | 1388 | free(sv); |
1395 | } | 1389 | } |
1396 | 1390 | ||
1391 | static void add_input_file(FILE *file) | ||
1392 | { | ||
1393 | G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); | ||
1394 | G.input_file_list[G.input_file_count++] = file; | ||
1395 | } | ||
1396 | |||
1397 | int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1397 | int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1398 | int sed_main(int argc UNUSED_PARAM, char **argv) | 1398 | int sed_main(int argc UNUSED_PARAM, char **argv) |
1399 | { | 1399 | { |
diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 2af1e4c97..9494ac2de 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests | |||
@@ -154,11 +154,9 @@ testing "sed selective matches insert newline" \ | |||
154 | testing "sed selective matches noinsert newline" \ | 154 | testing "sed selective matches noinsert newline" \ |
155 | "sed -ne 's/woo/bang/p' input -" "a bang\nb bang" "a woo\nb woo" \ | 155 | "sed -ne 's/woo/bang/p' input -" "a bang\nb bang" "a woo\nb woo" \ |
156 | "c no\nd no" | 156 | "c no\nd no" |
157 | test x"$SKIP_KNOWN_BUGS" = x"" && { | ||
158 | testing "sed clusternewline" \ | 157 | testing "sed clusternewline" \ |
159 | "sed -e '/one/a 111' -e '/two/i 222' -e p input -" \ | 158 | "sed -e '/one/a 111' -e '/two/i 222' -e p input -" \ |
160 | "one\none\n111\n222\ntwo\ntwo" "one" "two" | 159 | "one\none\n111\n222\ntwo\ntwo" "one" "two" |
161 | } | ||
162 | testing "sed subst+write" \ | 160 | testing "sed subst+write" \ |
163 | "sed -e 's/i/z/' -e 'woutputw' input -; $ECHO -n X; cat outputw" \ | 161 | "sed -e 's/i/z/' -e 'woutputw' input -; $ECHO -n X; cat outputw" \ |
164 | "thzngy\nagaznXthzngy\nagazn" "thingy" "again" | 162 | "thzngy\nagaznXthzngy\nagazn" "thingy" "again" |