diff options
author | Rob Landley <rob@landley.net> | 2004-11-25 07:21:47 +0000 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2004-11-25 07:21:47 +0000 |
commit | dcc286607c83723f2b05f91da0a6942999576106 (patch) | |
tree | e3c4b321b130d28af36ff5cf3bdc48771a405ba2 | |
parent | a8b98d63e76177dfc6aa1d5647783da200034b3d (diff) | |
download | busybox-w32-dcc286607c83723f2b05f91da0a6942999576106.tar.gz busybox-w32-dcc286607c83723f2b05f91da0a6942999576106.tar.bz2 busybox-w32-dcc286607c83723f2b05f91da0a6942999576106.zip |
Hiroshi found another bug. Currently sed's $ triggers at end of every file,
and with multiple files SuSv3 says it should only trigger at the end of the
LAST file.
The trivial fix I tried first broke if the last file is empty. Fixing this
properly required restructuring things to create a file list (actually a
FILE * list), and then processing it all in one go. (There's probably a
smaller way to do this, merging with append_list perhaps. But let's get
the behavior correct first.)
Note that editing files in place (-i) needs the _old_ behavior, with $
triggering at the end of each file.
Here's a test of all the things this patch fixed. gnu and busybox seds produce
the same results with this patch, and different without it.
echo -n -e "1one\n1two\n1three" > ../test1
echo -n > ../test2
echo -e "3one\n3two\n3three" > ../test3
sed -n "$ p" ../test1 ../test2 ../test3
sed -n "$ p" ../test1 ../test2
sed -i -n "$ p" ../test1 ../test2 ../test3
-rw-r--r-- | editors/sed.c | 74 |
1 files changed, 46 insertions, 28 deletions
diff --git a/editors/sed.c b/editors/sed.c index ae8d9832e..db364e3ee 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -34,7 +34,10 @@ | |||
34 | resulting sed_cmd_t structures are appended to a linked list | 34 | resulting sed_cmd_t structures are appended to a linked list |
35 | (sed_cmd_head/sed_cmd_tail). | 35 | (sed_cmd_head/sed_cmd_tail). |
36 | 36 | ||
37 | process_file() does actual sedding, reading data lines from an input FILE * | 37 | add_input_file() adds a FILE * to the list of input files. We need to |
38 | know them all ahead of time to find the last line for the $ match. | ||
39 | |||
40 | process_files() does actual sedding, reading data lines from each input FILE * | ||
38 | (which could be stdin) and applying the sed command list (sed_cmd_head) to | 41 | (which could be stdin) and applying the sed command list (sed_cmd_head) to |
39 | each of the resulting lines. | 42 | each of the resulting lines. |
40 | 43 | ||
@@ -116,6 +119,9 @@ static int be_quiet, in_place, regex_type; | |||
116 | FILE *nonstdout; | 119 | FILE *nonstdout; |
117 | char *outname,*hold_space; | 120 | char *outname,*hold_space; |
118 | 121 | ||
122 | /* List of input files */ | ||
123 | int input_file_count,current_input_file; | ||
124 | FILE **input_file_list; | ||
119 | 125 | ||
120 | static const char bad_format_in_subst[] = | 126 | static const char bad_format_in_subst[] = |
121 | "bad format in substitution expression"; | 127 | "bad format in substitution expression"; |
@@ -171,6 +177,9 @@ static void free_and_close_stuff(void) | |||
171 | } | 177 | } |
172 | 178 | ||
173 | if(hold_space) free(hold_space); | 179 | if(hold_space) free(hold_space); |
180 | |||
181 | while(current_input_file<input_file_count) | ||
182 | fclose(input_file_list[current_input_file++]); | ||
174 | } | 183 | } |
175 | #endif | 184 | #endif |
176 | 185 | ||
@@ -565,6 +574,8 @@ void add_cmd(char *cmdstr) | |||
565 | } | 574 | } |
566 | } | 575 | } |
567 | 576 | ||
577 | /* Append to a string, reallocating memory as necessary. */ | ||
578 | |||
568 | struct pipeline { | 579 | struct pipeline { |
569 | char *buf; /* Space to hold string */ | 580 | char *buf; /* Space to hold string */ |
570 | int idx; /* Space used */ | 581 | int idx; /* Space used */ |
@@ -718,20 +729,29 @@ static void flush_append(void) | |||
718 | append_head=append_tail=NULL; | 729 | append_head=append_tail=NULL; |
719 | } | 730 | } |
720 | 731 | ||
721 | /* Get next line of input, flushing append buffer and noting if we hit EOF | 732 | void add_input_file(FILE *file) |
722 | * without a newline on the last line. | 733 | { |
734 | input_file_list=xrealloc(input_file_list,(input_file_count+1)*sizeof(FILE *)); | ||
735 | input_file_list[input_file_count++]=file; | ||
736 | } | ||
737 | |||
738 | /* Get next line of input from input_file_list, flushing append buffer and | ||
739 | * noting if we ran out of files without a newline on the last line we read. | ||
723 | */ | 740 | */ |
724 | static char *get_next_line(FILE * file, int *no_newline) | 741 | static char *get_next_line(int *no_newline) |
725 | { | 742 | { |
726 | char *temp; | 743 | char *temp=NULL; |
727 | int len; | 744 | int len; |
728 | 745 | ||
729 | flush_append(); | 746 | flush_append(); |
730 | temp=bb_get_line_from_file(file); | 747 | while(current_input_file<input_file_count) { |
731 | if(temp) { | 748 | temp=bb_get_line_from_file(input_file_list[current_input_file]); |
732 | len=strlen(temp); | 749 | if(temp) { |
733 | if(len && temp[len-1]=='\n') temp[len-1]=0; | 750 | len=strlen(temp); |
734 | else *no_newline=1; | 751 | *no_newline=!(len && temp[len-1]=='\n'); |
752 | if(!*no_newline) temp[len-1]=0; | ||
753 | break; | ||
754 | } else fclose(input_file_list[current_input_file++]); | ||
735 | } | 755 | } |
736 | 756 | ||
737 | return temp; | 757 | return temp; |
@@ -757,15 +777,15 @@ static int puts_maybe_newline(char *s, FILE *file, int missing_newline, int no_n | |||
757 | 777 | ||
758 | #define sed_puts(s,n) missing_newline=puts_maybe_newline(s,nonstdout,missing_newline,n) | 778 | #define sed_puts(s,n) missing_newline=puts_maybe_newline(s,nonstdout,missing_newline,n) |
759 | 779 | ||
760 | static void process_file(FILE *file) | 780 | static void process_files(void) |
761 | { | 781 | { |
762 | char *pattern_space, *next_line; | 782 | char *pattern_space, *next_line; |
763 | static int linenum = 0, missing_newline=0; | 783 | int linenum = 0, missing_newline=0; |
764 | int no_newline,next_no_newline=0; | 784 | int no_newline,next_no_newline=0; |
765 | 785 | ||
766 | next_line = get_next_line(file,&next_no_newline); | 786 | next_line = get_next_line(&next_no_newline); |
767 | 787 | ||
768 | /* go through every line in the file */ | 788 | /* go through every line in each file */ |
769 | for(;;) { | 789 | for(;;) { |
770 | sed_cmd_t *sed_cmd; | 790 | sed_cmd_t *sed_cmd; |
771 | int substituted=0; | 791 | int substituted=0; |
@@ -775,7 +795,7 @@ static void process_file(FILE *file) | |||
775 | no_newline=next_no_newline; | 795 | no_newline=next_no_newline; |
776 | 796 | ||
777 | /* Read one line in advance so we can act on the last line, the '$' address */ | 797 | /* Read one line in advance so we can act on the last line, the '$' address */ |
778 | next_line = get_next_line(file,&next_no_newline); | 798 | next_line = get_next_line(&next_no_newline); |
779 | linenum++; | 799 | linenum++; |
780 | restart: | 800 | restart: |
781 | /* for every line, go through all the commands */ | 801 | /* for every line, go through all the commands */ |
@@ -944,7 +964,7 @@ restart: | |||
944 | free(pattern_space); | 964 | free(pattern_space); |
945 | pattern_space = next_line; | 965 | pattern_space = next_line; |
946 | no_newline=next_no_newline; | 966 | no_newline=next_no_newline; |
947 | next_line = get_next_line(file,&next_no_newline); | 967 | next_line = get_next_line(&next_no_newline); |
948 | linenum++; | 968 | linenum++; |
949 | break; | 969 | break; |
950 | } | 970 | } |
@@ -974,7 +994,7 @@ restart: | |||
974 | pattern_space[len]='\n'; | 994 | pattern_space[len]='\n'; |
975 | strcpy(pattern_space+len+1, next_line); | 995 | strcpy(pattern_space+len+1, next_line); |
976 | no_newline=next_no_newline; | 996 | no_newline=next_no_newline; |
977 | next_line = get_next_line(file,&next_no_newline); | 997 | next_line = get_next_line(&next_no_newline); |
978 | linenum++; | 998 | linenum++; |
979 | } | 999 | } |
980 | break; | 1000 | break; |
@@ -1150,8 +1170,7 @@ extern int sed_main(int argc, char **argv) | |||
1150 | } | 1170 | } |
1151 | } | 1171 | } |
1152 | 1172 | ||
1153 | /* if we didn't get a pattern from a -e and no command file was specified, | 1173 | /* if we didn't get a pattern from -e or -f, use argv[optind] */ |
1154 | * argv[optind] should be the pattern. no pattern, no worky */ | ||
1155 | if(getpat) { | 1174 | if(getpat) { |
1156 | if (argv[optind] == NULL) | 1175 | if (argv[optind] == NULL) |
1157 | bb_show_usage(); | 1176 | bb_show_usage(); |
@@ -1172,14 +1191,16 @@ extern int sed_main(int argc, char **argv) | |||
1172 | fprintf(stderr,"sed: Filename required for -i\n"); | 1191 | fprintf(stderr,"sed: Filename required for -i\n"); |
1173 | exit(1); | 1192 | exit(1); |
1174 | } | 1193 | } |
1175 | process_file(stdin); | 1194 | add_input_file(stdin); |
1195 | process_files(); | ||
1176 | } else { | 1196 | } else { |
1177 | int i; | 1197 | int i; |
1178 | FILE *file; | 1198 | FILE *file; |
1179 | 1199 | ||
1180 | for (i = optind; i < argc; i++) { | 1200 | for (i = optind; i < argc; i++) { |
1181 | if(!strcmp(argv[i], "-") && !in_place) { | 1201 | if(!strcmp(argv[i], "-") && !in_place) { |
1182 | process_file(stdin); | 1202 | add_input_file(stdin); |
1203 | process_files(); | ||
1183 | } else { | 1204 | } else { |
1184 | file = bb_wfopen(argv[i], "r"); | 1205 | file = bb_wfopen(argv[i], "r"); |
1185 | if (file) { | 1206 | if (file) { |
@@ -1187,30 +1208,27 @@ extern int sed_main(int argc, char **argv) | |||
1187 | struct stat statbuf; | 1208 | struct stat statbuf; |
1188 | outname=bb_xstrndup(argv[i],strlen(argv[i])+6); | 1209 | outname=bb_xstrndup(argv[i],strlen(argv[i])+6); |
1189 | strcat(outname,"XXXXXX"); | 1210 | strcat(outname,"XXXXXX"); |
1190 | /* Set permissions of output file */ | ||
1191 | fstat(fileno(file),&statbuf); | ||
1192 | mkstemp(outname); | 1211 | mkstemp(outname); |
1193 | nonstdout=bb_wfopen(outname,"w"); | 1212 | nonstdout=bb_wfopen(outname,"w"); |
1194 | /* Set permissions of output file */ | 1213 | /* Set permissions of output file */ |
1195 | fstat(fileno(file),&statbuf); | 1214 | fstat(fileno(file),&statbuf); |
1196 | fchmod(fileno(nonstdout),statbuf.st_mode); | 1215 | fchmod(fileno(nonstdout),statbuf.st_mode); |
1197 | atexit(cleanup_outname); | 1216 | atexit(cleanup_outname); |
1198 | } | 1217 | add_input_file(file); |
1199 | process_file(file); | 1218 | process_files(); |
1200 | fclose(file); | ||
1201 | if(in_place) { | ||
1202 | fclose(nonstdout); | 1219 | fclose(nonstdout); |
1203 | nonstdout=stdout; | 1220 | nonstdout=stdout; |
1204 | unlink(argv[i]); | 1221 | unlink(argv[i]); |
1205 | rename(outname,argv[i]); | 1222 | rename(outname,argv[i]); |
1206 | free(outname); | 1223 | free(outname); |
1207 | outname=0; | 1224 | outname=0; |
1208 | } | 1225 | } else add_input_file(file); |
1209 | } else { | 1226 | } else { |
1210 | status = EXIT_FAILURE; | 1227 | status = EXIT_FAILURE; |
1211 | } | 1228 | } |
1212 | } | 1229 | } |
1213 | } | 1230 | } |
1231 | if(input_file_count>current_input_file) process_files(); | ||
1214 | } | 1232 | } |
1215 | 1233 | ||
1216 | return status; | 1234 | return status; |