diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-01-28 23:26:15 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-01-28 23:26:15 +0000 |
commit | 938e84dc19a2ad67c3ee7260ed28ff0cefb2994b (patch) | |
tree | af1d14dc4df46508bdccc3f99d3aac622933b0f1 | |
parent | a963a468e159a20ca9817fc71c8ae27972d81342 (diff) | |
download | busybox-w32-938e84dc19a2ad67c3ee7260ed28ff0cefb2994b.tar.gz busybox-w32-938e84dc19a2ad67c3ee7260ed28ff0cefb2994b.tar.bz2 busybox-w32-938e84dc19a2ad67c3ee7260ed28ff0cefb2994b.zip |
fix all known regressions with sed and also make it simpler
git-svn-id: svn://busybox.net/trunk/busybox@17608 69ca8d6d-28ef-0310-b511-8ec308f3f277
-rw-r--r-- | editors/sed.c | 70 | ||||
-rwxr-xr-x | testsuite/sed.tests | 5 |
2 files changed, 47 insertions, 28 deletions
diff --git a/editors/sed.c b/editors/sed.c index 720d29aed..28ee698e4 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -82,7 +82,7 @@ typedef struct sed_cmd_s { | |||
82 | unsigned int in_match:1; /* Next line also included in match? */ | 82 | unsigned int in_match:1; /* Next line also included in match? */ |
83 | unsigned int sub_p:1; /* (s) print option */ | 83 | unsigned int sub_p:1; /* (s) print option */ |
84 | 84 | ||
85 | int last_char; /* Last line written by (sw) had no '\n' */ | 85 | // int sw_last_char; /* Last line written by (sw) had no '\n' */ |
86 | 86 | ||
87 | /* GENERAL FIELDS */ | 87 | /* GENERAL FIELDS */ |
88 | char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */ | 88 | char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */ |
@@ -717,6 +717,11 @@ static void add_input_file(FILE *file) | |||
717 | bbg.input_file_list[bbg.input_file_count++] = file; | 717 | bbg.input_file_list[bbg.input_file_count++] = file; |
718 | } | 718 | } |
719 | 719 | ||
720 | enum { | ||
721 | MASK_NO_EOL_CHAR = 0x100, | ||
722 | //MASK_FIRST_LINE = 0x200, | ||
723 | }; | ||
724 | |||
720 | /* Get next line of input from bbg.input_file_list, flushing append buffer and | 725 | /* Get next line of input from bbg.input_file_list, flushing append buffer and |
721 | * noting if we ran out of files without a newline on the last line we read. | 726 | * noting if we ran out of files without a newline on the last line we read. |
722 | */ | 727 | */ |
@@ -743,13 +748,13 @@ static char *get_next_line(int *last_char) | |||
743 | } | 748 | } |
744 | /* will be returned if last line in the file | 749 | /* will be returned if last line in the file |
745 | * doesn't end with either '\n' or '\0' */ | 750 | * doesn't end with either '\n' or '\0' */ |
746 | lc |= 0x100; | 751 | lc |= MASK_NO_EOL_CHAR; |
747 | break; | 752 | break; |
748 | } | 753 | } |
749 | /* Close this file and advance to next one */ | 754 | /* Close this file and advance to next one */ |
750 | fclose(bbg.input_file_list[bbg.current_input_file++]); | 755 | fclose(bbg.input_file_list[bbg.current_input_file++]); |
751 | /* "this is the first line from new input file" */ | 756 | /* "this is the first line from new input file" */ |
752 | lc |= 0x200; | 757 | //lc |= MASK_FIRST_LINE; |
753 | } | 758 | } |
754 | *last_char = lc; | 759 | *last_char = lc; |
755 | return temp; | 760 | return temp; |
@@ -757,7 +762,7 @@ static char *get_next_line(int *last_char) | |||
757 | 762 | ||
758 | /* Output line of text. */ | 763 | /* Output line of text. */ |
759 | /* Note: | 764 | /* Note: |
760 | * The tricks with 0x200 and last_puts_char are there to emulate gnu sed. | 765 | * The tricks with MASK_FIRST_LINE and last_puts_char are there to emulate gnu sed. |
761 | * Without them, we had this: | 766 | * Without them, we had this: |
762 | * echo -n thingy >z1 | 767 | * echo -n thingy >z1 |
763 | * echo -n again >z2 | 768 | * echo -n again >z2 |
@@ -769,14 +774,14 @@ static char *get_next_line(int *last_char) | |||
769 | * bbox: | 774 | * bbox: |
770 | * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| | 775 | * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| |
771 | */ | 776 | */ |
772 | 777 | static void puts_maybe_newline(char *s, FILE *file, int last_char) | |
773 | static int puts_maybe_newline(char *s, FILE *file, int prev_last_char, int last_char) | ||
774 | { | 778 | { |
775 | static char last_puts_char; | 779 | static char last_puts_char = '\n'; |
776 | 780 | ||
777 | /* Is this a first line from new file | 781 | /* Is this a first line from new file |
778 | * and old file didn't end with '\n'? */ | 782 | * and old file didn't end with '\n' or '\0'? */ |
779 | if ((last_char & 0x200) && last_puts_char != '\n') { | 783 | // if ((last_char & MASK_FIRST_LINE) && last_puts_char != '\n') { |
784 | if (last_puts_char != '\n' && last_puts_char != '\0') { | ||
780 | fputc('\n', file); | 785 | fputc('\n', file); |
781 | last_puts_char = '\n'; | 786 | last_puts_char = '\n'; |
782 | } | 787 | } |
@@ -784,7 +789,7 @@ static int puts_maybe_newline(char *s, FILE *file, int prev_last_char, int last_ | |||
784 | /* why 'x'? - just something which is not '\n' */ | 789 | /* why 'x'? - just something which is not '\n' */ |
785 | if (s[0]) | 790 | if (s[0]) |
786 | last_puts_char = 'x'; | 791 | last_puts_char = 'x'; |
787 | if (!(last_char & 0x100)) { /* had trailing '\n' or '\0'? */ | 792 | if (!(last_char & MASK_NO_EOL_CHAR)) { /* had trailing '\n' or '\0'? */ |
788 | last_char &= 0xff; | 793 | last_char &= 0xff; |
789 | fputc(last_char, file); | 794 | fputc(last_char, file); |
790 | last_puts_char = last_char; | 795 | last_puts_char = last_char; |
@@ -795,18 +800,18 @@ static int puts_maybe_newline(char *s, FILE *file, int prev_last_char, int last_ | |||
795 | bb_error_msg_and_die(bb_msg_write_error); | 800 | bb_error_msg_and_die(bb_msg_write_error); |
796 | } | 801 | } |
797 | 802 | ||
798 | return last_char; | 803 | /* Seems to be unused */ |
804 | /*return last_char;*/ | ||
799 | } | 805 | } |
800 | 806 | ||
801 | #define sed_puts(s, n) \ | 807 | #define sed_puts(s, n) (puts_maybe_newline(s, bbg.nonstdout, n)) |
802 | (prev_last_char = puts_maybe_newline(s, bbg.nonstdout, prev_last_char, n)) | ||
803 | 808 | ||
804 | /* Process all the lines in all the files */ | 809 | /* Process all the lines in all the files */ |
805 | 810 | ||
806 | static void process_files(void) | 811 | static void process_files(void) |
807 | { | 812 | { |
808 | char *pattern_space, *next_line; | 813 | char *pattern_space, *next_line; |
809 | int linenum = 0, prev_last_char = 0; | 814 | int linenum = 0; |
810 | int last_char, next_last_char = 0; | 815 | int last_char, next_last_char = 0; |
811 | sed_cmd_t *sed_cmd; | 816 | sed_cmd_t *sed_cmd; |
812 | int substituted; | 817 | int substituted; |
@@ -873,10 +878,13 @@ restart: | |||
873 | 878 | ||
874 | /* Skip blocks of commands we didn't match. */ | 879 | /* Skip blocks of commands we didn't match. */ |
875 | if (sed_cmd->cmd == '{') { | 880 | if (sed_cmd->cmd == '{') { |
876 | if (sed_cmd->invert ? matched : !matched) | 881 | if (sed_cmd->invert ? matched : !matched) { |
877 | while (sed_cmd && sed_cmd->cmd != '}') | 882 | while (sed_cmd->cmd != '}') { |
878 | sed_cmd = sed_cmd->next; | 883 | sed_cmd = sed_cmd->next; |
879 | if (!sed_cmd) bb_error_msg_and_die("unterminated {"); | 884 | if (!sed_cmd) |
885 | bb_error_msg_and_die("unterminated {"); | ||
886 | } | ||
887 | } | ||
880 | continue; | 888 | continue; |
881 | } | 889 | } |
882 | 890 | ||
@@ -902,7 +910,8 @@ restart: | |||
902 | 910 | ||
903 | if (tmp) { | 911 | if (tmp) { |
904 | *tmp = '\0'; | 912 | *tmp = '\0'; |
905 | sed_puts(pattern_space, 1); | 913 | /* TODO: explain why '\n' below */ |
914 | sed_puts(pattern_space, '\n'); | ||
906 | *tmp = '\n'; | 915 | *tmp = '\n'; |
907 | break; | 916 | break; |
908 | } | 917 | } |
@@ -911,7 +920,11 @@ restart: | |||
911 | 920 | ||
912 | /* Write the current pattern space to output */ | 921 | /* Write the current pattern space to output */ |
913 | case 'p': | 922 | case 'p': |
914 | sed_puts(pattern_space, last_char); | 923 | /* NB: we print this _before_ the last line |
924 | * (of current file) is printed. Even if | ||
925 | * that line is nonterminated, we print | ||
926 | * '\n' here (gnu sed does the same) */ | ||
927 | sed_puts(pattern_space, (last_char & 0x200) | '\n'); | ||
915 | break; | 928 | break; |
916 | /* Delete up through first newline */ | 929 | /* Delete up through first newline */ |
917 | case 'D': | 930 | case 'D': |
@@ -940,9 +953,9 @@ restart: | |||
940 | sed_puts(pattern_space, last_char); | 953 | sed_puts(pattern_space, last_char); |
941 | /* handle w option */ | 954 | /* handle w option */ |
942 | if (sed_cmd->file) | 955 | if (sed_cmd->file) |
943 | sed_cmd->last_char = puts_maybe_newline( | 956 | /*sed_cmd->sw_last_char =*/ puts_maybe_newline( |
944 | pattern_space, sed_cmd->file, | 957 | pattern_space, sed_cmd->file, |
945 | sed_cmd->last_char, last_char); | 958 | last_char); |
946 | break; | 959 | break; |
947 | 960 | ||
948 | /* Append line to linked list to be printed later */ | 961 | /* Append line to linked list to be printed later */ |
@@ -952,14 +965,14 @@ restart: | |||
952 | 965 | ||
953 | /* Insert text before this line */ | 966 | /* Insert text before this line */ |
954 | case 'i': | 967 | case 'i': |
955 | sed_puts(sed_cmd->string, 1); | 968 | sed_puts(sed_cmd->string, '\n'); |
956 | break; | 969 | break; |
957 | 970 | ||
958 | /* Cut and paste text (replace) */ | 971 | /* Cut and paste text (replace) */ |
959 | case 'c': | 972 | case 'c': |
960 | /* Only triggers on last line of a matching range. */ | 973 | /* Only triggers on last line of a matching range. */ |
961 | if (!sed_cmd->in_match) | 974 | if (!sed_cmd->in_match) |
962 | sed_puts(sed_cmd->string, 0); | 975 | sed_puts(sed_cmd->string, MASK_NO_EOL_CHAR); |
963 | goto discard_line; | 976 | goto discard_line; |
964 | 977 | ||
965 | /* Read file, append contents to output */ | 978 | /* Read file, append contents to output */ |
@@ -982,9 +995,9 @@ restart: | |||
982 | 995 | ||
983 | /* Write pattern space to file. */ | 996 | /* Write pattern space to file. */ |
984 | case 'w': | 997 | case 'w': |
985 | sed_cmd->last_char = puts_maybe_newline( | 998 | /*sed_cmd->sw_last_char =*/ puts_maybe_newline( |
986 | pattern_space, sed_cmd->file, | 999 | pattern_space, sed_cmd->file, |
987 | sed_cmd->last_char, last_char); | 1000 | last_char); |
988 | break; | 1001 | break; |
989 | 1002 | ||
990 | /* Read next line from input */ | 1003 | /* Read next line from input */ |
@@ -1123,13 +1136,14 @@ restart: | |||
1123 | /* | 1136 | /* |
1124 | * exit point from sedding... | 1137 | * exit point from sedding... |
1125 | */ | 1138 | */ |
1126 | discard_commands: | 1139 | discard_commands: |
1127 | /* we will print the line unless we were told to be quiet ('-n') | 1140 | /* we will print the line unless we were told to be quiet ('-n') |
1128 | or if the line was suppressed (ala 'd'elete) */ | 1141 | or if the line was suppressed (ala 'd'elete) */ |
1129 | if (!bbg.be_quiet) sed_puts(pattern_space, last_char); | 1142 | if (!bbg.be_quiet) |
1143 | sed_puts(pattern_space, last_char); | ||
1130 | 1144 | ||
1131 | /* Delete and such jump here. */ | 1145 | /* Delete and such jump here. */ |
1132 | discard_line: | 1146 | discard_line: |
1133 | flush_append(); | 1147 | flush_append(); |
1134 | free(pattern_space); | 1148 | free(pattern_space); |
1135 | 1149 | ||
diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 2a0d4eacf..a386f1723 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests | |||
@@ -146,6 +146,11 @@ testing "sed match EOF" "sed -e '"'$p'"'" "hello\nthere\nthere" "" \ | |||
146 | "hello\nthere" | 146 | "hello\nthere" |
147 | testing "sed match EOF two files" "sed -e '"'$p'"' input -" \ | 147 | testing "sed match EOF two files" "sed -e '"'$p'"' input -" \ |
148 | "one\ntwo\nthree\nfour\nfour" "one\ntwo" "three\nfour" | 148 | "one\ntwo\nthree\nfour\nfour" "one\ntwo" "three\nfour" |
149 | # sed match EOF inline: gnu sed 4.1.5 outputs this: | ||
150 | #00000000 6f 6e 65 0a 6f 6f 6b 0a 6f 6f 6b 0a 74 77 6f 0a |one.ook.ook.two.| | ||
151 | #00000010 0a 74 68 72 65 65 0a 6f 6f 6b 0a 6f 6f 6b 0a 66 |.three.ook.ook.f| | ||
152 | #00000020 6f 75 72 |our| | ||
153 | # which looks buggy to me. | ||
149 | echo -ne "three\nfour" > input2 | 154 | echo -ne "three\nfour" > input2 |
150 | testing "sed match EOF inline" \ | 155 | testing "sed match EOF inline" \ |
151 | "sed -e '"'$i ook'"' -i input input2 && cat input input2" \ | 156 | "sed -e '"'$i ook'"' -i input input2 && cat input input2" \ |