aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2004-11-25 07:21:47 +0000
committerRob Landley <rob@landley.net>2004-11-25 07:21:47 +0000
commitdcc286607c83723f2b05f91da0a6942999576106 (patch)
treee3c4b321b130d28af36ff5cf3bdc48771a405ba2
parenta8b98d63e76177dfc6aa1d5647783da200034b3d (diff)
downloadbusybox-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.c74
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;
116FILE *nonstdout; 119FILE *nonstdout;
117char *outname,*hold_space; 120char *outname,*hold_space;
118 121
122/* List of input files */
123int input_file_count,current_input_file;
124FILE **input_file_list;
119 125
120static const char bad_format_in_subst[] = 126static 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
568struct pipeline { 579struct 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 732void 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 */
724static char *get_next_line(FILE * file, int *no_newline) 741static 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
760static void process_file(FILE *file) 780static 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++;
780restart: 800restart:
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;