Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/editors/sed.c
- Timestamp:
- Dec 20, 2016, 4:07:32 PM (7 years ago)
- Location:
- branches/3.3
- Files:
-
- 1 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
branches/3.3/mindi-busybox/editors/sed.c
r3232 r3621 23 23 * resulting sed_cmd_t structures are appended to a linked list 24 24 * (G.sed_cmd_head/G.sed_cmd_tail). 25 *26 * add_input_file() adds a FILE* to the list of input files. We need to27 * know all input sources ahead of time to find the last line for the $ match.28 25 * 29 26 * process_files() does actual sedding, reading data lines from each input FILE* … … 57 54 * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html 58 55 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html 56 * http://sed.sourceforge.net/sedfaq3.html 59 57 */ 60 58 59 //config:config SED 60 //config: bool "sed" 61 //config: default y 62 //config: help 63 //config: sed is used to perform text transformations on a file 64 //config: or input from a pipeline. 65 66 //kbuild:lib-$(CONFIG_SED) += sed.o 67 68 //applet:IF_SED(APPLET(sed, BB_DIR_BIN, BB_SUID_DROP)) 69 61 70 //usage:#define sed_trivial_usage 62 //usage: "[-inr ] [-f FILE]... [-e CMD]... [FILE]...\n"63 //usage: "or: sed [-inr ] CMD [FILE]..."71 //usage: "[-inrE] [-f FILE]... [-e CMD]... [FILE]...\n" 72 //usage: "or: sed [-inrE] CMD [FILE]..." 64 73 //usage:#define sed_full_usage "\n\n" 65 74 //usage: " -e CMD Add CMD to sed commands to be executed" … … 68 77 //usage: "\n Optionally back files up, appending SFX" 69 78 //usage: "\n -n Suppress automatic printing of pattern space" 70 //usage: "\n -r Use extended regex syntax"79 //usage: "\n -r,-E Use extended regex syntax" 71 80 //usage: "\n" 72 81 //usage: "\nIf no -e or -f, the first non-option argument is the sed command string." … … 78 87 79 88 #include "libbb.h" 89 #include "common_bufsiz.h" 80 90 #include "xregex.h" 81 91 … … 102 112 int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ 103 113 int beg_line_orig; /* copy of the above, needed for -i */ 104 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ 105 106 FILE *sw_file; /* File (sw) command writes to, -1 for none. */ 114 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($). -2-N = +N */ 115 int end_line_orig; 116 117 FILE *sw_file; /* File (sw) command writes to, NULL for none. */ 107 118 char *string; /* Data string for (saicytb) commands. */ 108 119 … … 125 136 /* options */ 126 137 int be_quiet, regex_type; 138 127 139 FILE *nonstdout; 128 140 char *outname, *hold_space; 129 130 /* List of input files */ 131 int input_file_count, current_input_file; 132 FILE **input_file_list; 141 smallint exitcode; 142 143 /* list of input files */ 144 int current_input_file, last_input_file; 145 char **input_file_list; 146 FILE *current_fp; 133 147 134 148 regmatch_t regmatch[10]; … … 138 152 sed_cmd_t *sed_cmd_head, **sed_cmd_tail; 139 153 140 /* Linked list of append lines */154 /* linked list of append lines */ 141 155 llist_t *append_head; 142 156 … … 149 163 } pipeline; 150 164 } FIX_ALIASING; 151 #define G (*(struct globals*)&bb_common_bufsiz1) 152 struct BUG_G_too_big { 153 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; 154 }; 165 #define G (*(struct globals*)bb_common_bufsiz1) 155 166 #define INIT_G() do { \ 167 setup_common_bufsiz(); \ 168 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ 156 169 G.sed_cmd_tail = &G.sed_cmd_head; \ 157 170 } while (0) … … 169 182 170 183 if (sed_cmd->sw_file) 171 xprint_and_close_file(sed_cmd->sw_file);184 fclose(sed_cmd->sw_file); 172 185 173 186 if (sed_cmd->beg_match) { … … 190 203 free(G.hold_space); 191 204 192 while (G.current_input_file < G.input_file_count)193 fclose(G. input_file_list[G.current_input_file++]);205 if (G.current_fp) 206 fclose(G.current_fp); 194 207 } 195 208 #else … … 206 219 /* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */ 207 220 208 static void parse_escapes(char *dest, const char *string, int len, char from, char to) 209 { 221 static unsigned parse_escapes(char *dest, const char *string, int len, char from, char to) 222 { 223 char *d = dest; 210 224 int i = 0; 225 226 if (len == -1) 227 len = strlen(string); 211 228 212 229 while (i < len) { 213 230 if (string[i] == '\\') { 214 231 if (!to || string[i+1] == from) { 215 *dest++ = to ? to : string[i+1]; 232 if ((*d = to ? to : string[i+1]) == '\0') 233 return d - dest; 216 234 i += 2; 235 d++; 217 236 continue; 218 237 } 219 *dest++ = string[i++]; 220 } 221 /* TODO: is it safe wrt a string with trailing '\\' ? */ 222 *dest++ = string[i++]; 223 } 224 *dest = '\0'; 238 i++; /* skip backslash in string[] */ 239 *d++ = '\\'; 240 /* fall through: copy next char verbatim */ 241 } 242 if ((*d = string[i++]) == '\0') 243 return d - dest; 244 d++; 245 } 246 *d = '\0'; 247 return d - dest; 225 248 } 226 249 … … 233 256 /* GNU sed also recognizes \t and \r */ 234 257 for (s = "\nn\tt\rr"; *s; s += 2) { 235 parse_escapes(dest, string, len, s[1], s[0]);258 len = parse_escapes(dest, string, len, s[1], s[0]); 236 259 string = dest; 237 len = strlen(dest);238 260 } 239 261 return dest; … … 331 353 temp = copy_parsing_escapes(pos, next); 332 354 *regex = xzalloc(sizeof(regex_t)); 333 xregcomp(*regex, temp, G.regex_type |REG_NEWLINE);355 xregcomp(*regex, temp, G.regex_type); 334 356 free(temp); 335 357 /* Move position to next character after last delimiter */ … … 371 393 /* 372 394 * A substitution command should look something like this: 373 * s/match/replace/ #g Ipw395 * s/match/replace/ #giIpw 374 396 * || | ||| 375 397 * mandatory optional … … 385 407 386 408 sed_cmd->which_match = 1; 409 dbg("s flags:'%s'", substr + idx + 1); 387 410 while (substr[++idx]) { 411 dbg("s flag:'%c'", substr[idx]); 388 412 /* Parse match number */ 389 413 if (isdigit(substr[idx])) { … … 393 417 /* FIXME: error check? */ 394 418 sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10); 395 idx = pos - substr ;419 idx = pos - substr - 1; 396 420 } 397 421 continue; … … 414 438 case 'w': 415 439 { 416 char *temp; 417 idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp); 440 char *fname; 441 idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname); 442 sed_cmd->sw_file = xfopen_for_write(fname); 443 sed_cmd->sw_last_char = '\n'; 444 free(fname); 418 445 break; 419 446 } 420 447 /* Ignore case (gnu exension) */ 448 case 'i': 421 449 case 'I': 422 450 cflags |= REG_ICASE; … … 432 460 goto out; 433 461 default: 462 dbg("s bad flags:'%s'", substr + idx); 434 463 bb_error_msg_and_die("bad option in substitution expression"); 435 464 } … … 454 483 static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) 455 484 { 456 static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}";485 static const char cmd_letters[] ALIGN1 = "saicrw:btTydDgGhHlnNpPqx={}"; 457 486 enum { 458 487 IDX_s = 0, … … 485 514 IDX_nul 486 515 }; 487 struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; }; 488 489 unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters; 516 unsigned idx; 517 518 BUILD_BUG_ON(sizeof(cmd_letters)-1 != IDX_nul); 519 520 idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters; 490 521 491 522 /* handle (s)ubstitution command */ … … 495 526 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ 496 527 else if (idx <= IDX_c) { /* a,i,c */ 528 unsigned len; 529 497 530 if (idx < IDX_c) { /* a,i */ 498 531 if (sed_cmd->end_line || sed_cmd->end_match) … … 508 541 cmdstr++; 509 542 } 510 sed_cmd->string = xstrdup(cmdstr); 543 len = strlen(cmdstr); 544 sed_cmd->string = copy_parsing_escapes(cmdstr, len); 545 cmdstr += len; 511 546 /* "\anychar" -> "anychar" */ 512 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0'); 513 cmdstr += strlen(cmdstr); 547 parse_escapes(sed_cmd->string, sed_cmd->string, -1, '\0', '\0'); 514 548 } 515 549 /* handle file cmds: (r)ead */ … … 543 577 cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1; 544 578 /* \n already parsed, but \delimiter needs unescaping. */ 545 parse_escapes(match, match, strlen(match), i, i);546 parse_escapes(replace, replace, strlen(replace), i, i);579 parse_escapes(match, match, -1, i, i); 580 parse_escapes(replace, replace, -1, i, i); 547 581 548 582 sed_cmd->string = xzalloc((strlen(match) + 1) * 2); … … 626 660 627 661 cmdstr++; 628 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); 629 if (!idx) 662 if (*cmdstr == '+' && isdigit(cmdstr[1])) { 663 /* http://sed.sourceforge.net/sedfaq3.html#s3.3 664 * Under GNU sed 3.02+, ssed, and sed15+, <address2> 665 * may also be a notation of the form +num, 666 * indicating the next num lines after <address1> is 667 * matched. 668 * GNU sed 4.2.1 accepts even "+" (meaning "+0"). 669 * We don't (we check for isdigit, see above), think 670 * about the "+-3" case. 671 */ 672 char *end; 673 /* code is smaller compared to using &cmdstr here: */ 674 idx = strtol(cmdstr+1, &end, 10); 675 sed_cmd->end_line = -2 - idx; 676 cmdstr = end; 677 } else { 678 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); 679 cmdstr += idx; 680 idx--; /* if 0, trigger error check below */ 681 } 682 if (idx < 0) 630 683 bb_error_msg_and_die("no address after comma"); 631 cmdstr += idx;684 sed_cmd->end_line_orig = sed_cmd->end_line; 632 685 } 633 686 … … 649 702 sed_cmd->cmd = *cmdstr++; 650 703 cmdstr = parse_cmd_args(sed_cmd, cmdstr); 704 705 /* cmdstr now points past args. 706 * GNU sed requires a separator, if there are more commands, 707 * else it complains "char N: extra characters after command". 708 * Example: "sed 'p;d'". We also allow "sed 'pd'". 709 */ 651 710 652 711 /* Add the command to the command array */ … … 843 902 static void append(char *s) 844 903 { 845 llist_add_to_end(&G.append_head, xstrdup(s)); 846 } 847 848 static void flush_append(void) 849 { 850 char *data; 851 852 /* Output appended lines. */ 853 while ((data = (char *)llist_pop(&G.append_head))) { 854 fprintf(G.nonstdout, "%s\n", data); 855 free(data); 856 } 857 } 858 859 static void add_input_file(FILE *file) 860 { 861 G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); 862 G.input_file_list[G.input_file_count++] = file; 863 } 864 865 /* Get next line of input from G.input_file_list, flushing append buffer and 866 * noting if we ran out of files without a newline on the last line we read. 904 llist_add_to_end(&G.append_head, s); 905 } 906 907 /* Output line of text. */ 908 /* Note: 909 * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. 910 * Without them, we had this: 911 * echo -n thingy >z1 912 * echo -n again >z2 913 * >znull 914 * sed "s/i/z/" z1 z2 znull | hexdump -vC 915 * output: 916 * gnu sed 4.1.5: 917 * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| 918 * bbox: 919 * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| 867 920 */ 868 921 enum { … … 870 923 LAST_IS_NUL = 2, 871 924 }; 872 static char *get_next_line(char *gets_char) 925 static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) 926 { 927 char lpc = *last_puts_char; 928 929 /* Need to insert a '\n' between two files because first file's 930 * last line wasn't terminated? */ 931 if (lpc != '\n' && lpc != '\0') { 932 fputc('\n', file); 933 lpc = '\n'; 934 } 935 fputs(s, file); 936 937 /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ 938 if (s[0]) 939 lpc = 'x'; 940 941 /* had trailing '\0' and it was last char of file? */ 942 if (last_gets_char == LAST_IS_NUL) { 943 fputc('\0', file); 944 lpc = 'x'; /* */ 945 } else 946 /* had trailing '\n' or '\0'? */ 947 if (last_gets_char != NO_EOL_CHAR) { 948 fputc(last_gets_char, file); 949 lpc = last_gets_char; 950 } 951 952 if (ferror(file)) { 953 xfunc_error_retval = 4; /* It's what gnu sed exits with... */ 954 bb_error_msg_and_die(bb_msg_write_error); 955 } 956 *last_puts_char = lpc; 957 } 958 959 static void flush_append(char *last_puts_char) 960 { 961 char *data; 962 963 /* Output appended lines. */ 964 while ((data = (char *)llist_pop(&G.append_head)) != NULL) { 965 /* Append command does not respect "nonterminated-ness" 966 * of last line. Try this: 967 * $ echo -n "woot" | sed -e '/woot/a woo' - 968 * woot 969 * woo 970 * (both lines are terminated with \n) 971 * Therefore we do not propagate "last_gets_char" here, 972 * pass '\n' instead: 973 */ 974 puts_maybe_newline(data, G.nonstdout, last_puts_char, '\n'); 975 free(data); 976 } 977 } 978 979 /* Get next line of input from G.input_file_list, flushing append buffer and 980 * noting if we ran out of files without a newline on the last line we read. 981 */ 982 static char *get_next_line(char *gets_char, char *last_puts_char) 873 983 { 874 984 char *temp = NULL; … … 876 986 char gc; 877 987 878 flush_append( );988 flush_append(last_puts_char); 879 989 880 990 /* will be returned if last line in the file 881 991 * doesn't end with either '\n' or '\0' */ 882 992 gc = NO_EOL_CHAR; 883 while (G.current_input_file < G.input_file_count) { 884 FILE *fp = G.input_file_list[G.current_input_file]; 993 for (; G.current_input_file <= G.last_input_file; G.current_input_file++) { 994 FILE *fp = G.current_fp; 995 if (!fp) { 996 const char *path = G.input_file_list[G.current_input_file]; 997 fp = stdin; 998 if (path != bb_msg_standard_input) { 999 fp = fopen_or_warn(path, "r"); 1000 if (!fp) { 1001 G.exitcode = EXIT_FAILURE; 1002 continue; 1003 } 1004 } 1005 G.current_fp = fp; 1006 } 885 1007 /* Read line up to a newline or NUL byte, inclusive, 886 1008 * return malloc'ed char[]. length of the chunk read … … 913 1035 } 914 1036 /* Close this file and advance to next one */ 915 fclose (fp);916 G.current_ input_file++;1037 fclose_if_not_stdin(fp); 1038 G.current_fp = NULL; 917 1039 } 918 1040 *gets_char = gc; 919 1041 return temp; 920 }921 922 /* Output line of text. */923 /* Note:924 * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.925 * Without them, we had this:926 * echo -n thingy >z1927 * echo -n again >z2928 * >znull929 * sed "s/i/z/" z1 z2 znull | hexdump -vC930 * output:931 * gnu sed 4.1.5:932 * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn|933 * bbox:934 * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn|935 */936 static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)937 {938 char lpc = *last_puts_char;939 940 /* Need to insert a '\n' between two files because first file's941 * last line wasn't terminated? */942 if (lpc != '\n' && lpc != '\0') {943 fputc('\n', file);944 lpc = '\n';945 }946 fputs(s, file);947 948 /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */949 if (s[0])950 lpc = 'x';951 952 /* had trailing '\0' and it was last char of file? */953 if (last_gets_char == LAST_IS_NUL) {954 fputc('\0', file);955 lpc = 'x'; /* */956 } else957 /* had trailing '\n' or '\0'? */958 if (last_gets_char != NO_EOL_CHAR) {959 fputc(last_gets_char, file);960 lpc = last_gets_char;961 }962 963 if (ferror(file)) {964 xfunc_error_retval = 4; /* It's what gnu sed exits with... */965 bb_error_msg_and_die(bb_msg_write_error);966 }967 *last_puts_char = lpc;968 1042 } 969 1043 … … 990 1064 991 1065 /* Prime the pump */ 992 next_line = get_next_line(&next_gets_char );1066 next_line = get_next_line(&next_gets_char, &last_puts_char); 993 1067 994 1068 /* Go through every line in each file */ … … 1004 1078 /* Read one line in advance so we can act on the last line, 1005 1079 * the '$' address */ 1006 next_line = get_next_line(&next_gets_char );1080 next_line = get_next_line(&next_gets_char, &last_puts_char); 1007 1081 linenum++; 1008 1082 … … 1063 1137 1064 1138 if (matched) { 1139 if (sed_cmd->end_line <= -2) { 1140 /* address2 is +N, i.e. N lines from beg_line */ 1141 sed_cmd->end_line = linenum + (-sed_cmd->end_line - 2); 1142 } 1065 1143 /* once matched, "n,xxx" range is dead, disabling it */ 1066 1144 if (sed_cmd->beg_line > 0) { 1067 1145 sed_cmd->beg_line = -2; 1068 1146 } 1147 dbg("end1:%d", sed_cmd->end_line ? sed_cmd->end_line == -1 1148 ? !next_line : (sed_cmd->end_line <= linenum) 1149 : !sed_cmd->end_match); 1150 dbg("end2:%d", sed_cmd->end_match && old_matched 1151 && !regexec(sed_cmd->end_match,pattern_space, 0, NULL, 0)); 1069 1152 sed_cmd->in_match = !( 1070 1153 /* has the ending line come, or is this a single address command? */ … … 1176 1259 /* Append line to linked list to be printed later */ 1177 1260 case 'a': 1178 append( sed_cmd->string);1261 append(xstrdup(sed_cmd->string)); 1179 1262 break; 1180 1263 … … 1198 1281 if (rfile) { 1199 1282 char *line; 1200 1201 1283 while ((line = xmalloc_fgetline(rfile)) 1202 1284 != NULL) 1203 1285 append(line); 1204 xprint_and_close_file(rfile);1286 fclose(rfile); 1205 1287 } 1206 1288 … … 1223 1305 pattern_space = next_line; 1224 1306 last_gets_char = next_gets_char; 1225 next_line = get_next_line(&next_gets_char );1307 next_line = get_next_line(&next_gets_char, &last_puts_char); 1226 1308 substituted = 0; 1227 1309 linenum++; … … 1259 1341 strcpy(pattern_space + len+1, next_line); 1260 1342 last_gets_char = next_gets_char; 1261 next_line = get_next_line(&next_gets_char );1343 next_line = get_next_line(&next_gets_char, &last_puts_char); 1262 1344 linenum++; 1263 1345 break; … … 1363 1445 /* Delete and such jump here. */ 1364 1446 discard_line: 1365 flush_append( );1447 flush_append(&last_puts_char /*,last_gets_char*/); 1366 1448 free(pattern_space); 1367 1449 … … 1372 1454 * newlines. This counts as multiple command lines. 1373 1455 * However, newline can be escaped: 's/e/z\<newline>z/' 1374 * We check forthis.1456 * add_cmd() handles this. 1375 1457 */ 1376 1458 … … 1382 1464 do { 1383 1465 eol = strchr(cmdstr, '\n'); 1384 next: 1385 if (eol) { 1386 /* Count preceding slashes */ 1387 int slashes = 0; 1388 char *sl = eol; 1389 1390 while (sl != cmdstr && *--sl == '\\') 1391 slashes++; 1392 /* Odd number of preceding slashes - newline is escaped */ 1393 if (slashes & 1) { 1394 overlapping_strcpy(eol - 1, eol); 1395 eol = strchr(eol, '\n'); 1396 goto next; 1397 } 1466 if (eol) 1398 1467 *eol = '\0'; 1399 }1400 1468 add_cmd(cmdstr); 1401 1469 cmdstr = eol + 1; … … 1422 1490 #endif 1423 1491 1424 int status = EXIT_SUCCESS;1425 1426 1492 INIT_G(); 1427 1493 … … 1444 1510 1445 1511 /* -i must be first, to match OPT_in_place definition */ 1446 opt = getopt32(argv, "i::rne:f:", &opt_i, &opt_e, &opt_f, 1512 /* -E is a synonym of -r: 1513 * GNU sed 4.2.1 mentions it in neither --help 1514 * nor manpage, but does recognize it. 1515 */ 1516 opt = getopt32(argv, "i::rEne:f:", &opt_i, &opt_e, &opt_f, 1447 1517 &G.be_quiet); /* counter for -n */ 1448 1518 //argc -= optind; … … 1451 1521 atexit(cleanup_outname); 1452 1522 } 1453 if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r 1454 //if (opt & 0x4) G.be_quiet++; // -n 1523 if (opt & (2|4)) 1524 G.regex_type |= REG_EXTENDED; // -r or -E 1525 //if (opt & 8) 1526 // G.be_quiet++; // -n (implemented with a counter instead) 1455 1527 while (opt_e) { // -e 1456 1528 add_cmd_block(llist_pop(&opt_e)); … … 1459 1531 char *line; 1460 1532 FILE *cmdfile; 1461 cmdfile = xfopen_ for_read(llist_pop(&opt_f));1533 cmdfile = xfopen_stdin(llist_pop(&opt_f)); 1462 1534 while ((line = xmalloc_fgetline(cmdfile)) != NULL) { 1463 1535 add_cmd(line); 1464 1536 free(line); 1465 1537 } 1466 fclose (cmdfile);1538 fclose_if_not_stdin(cmdfile); 1467 1539 } 1468 1540 /* if we didn't get a pattern from -e or -f, use argv[0] */ 1469 if (!(opt & 0x 18)) {1541 if (!(opt & 0x30)) { 1470 1542 if (!*argv) 1471 1543 bb_show_usage(); … … 1481 1553 * files were specified or '-' was specified, take input from stdin. 1482 1554 * Otherwise, we process all the files specified. */ 1483 if (argv[0] == NULL) { 1555 G.input_file_list = argv; 1556 if (!argv[0]) { 1484 1557 if (opt & OPT_in_place) 1485 1558 bb_error_msg_and_die(bb_msg_requires_arg, "-i"); 1486 add_input_file(stdin); 1559 argv[0] = (char*)bb_msg_standard_input; 1560 /* G.last_input_file = 0; - already is */ 1487 1561 } else { 1488 int i;1489 1490 for ( i = 0; argv[i]; i++) {1562 goto start; 1563 1564 for (; *argv; argv++) { 1491 1565 struct stat statbuf; 1492 1566 int nonstdoutfd; 1493 FILE *file;1494 1567 sed_cmd_t *sed_cmd; 1495 1568 1496 if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { 1497 add_input_file(stdin); 1498 process_files(); 1569 G.last_input_file++; 1570 start: 1571 if (!(opt & OPT_in_place)) { 1572 if (LONE_DASH(*argv)) { 1573 *argv = (char*)bb_msg_standard_input; 1574 process_files(); 1575 } 1499 1576 continue; 1500 1577 } 1501 file = fopen_or_warn(argv[i], "r"); 1502 if (!file) { 1503 status = EXIT_FAILURE; 1578 1579 /* -i: process each FILE separately: */ 1580 1581 if (stat(*argv, &statbuf) != 0) { 1582 bb_simple_perror_msg(*argv); 1583 G.exitcode = EXIT_FAILURE; 1584 G.current_input_file++; 1504 1585 continue; 1505 1586 } 1506 add_input_file(file); 1507 if (!(opt & OPT_in_place)) { 1508 continue; 1509 } 1510 1511 /* -i: process each FILE separately: */ 1512 1513 G.outname = xasprintf("%sXXXXXX", argv[i]); 1587 G.outname = xasprintf("%sXXXXXX", *argv); 1514 1588 nonstdoutfd = xmkstemp(G.outname); 1515 1589 G.nonstdout = xfdopen_for_write(nonstdoutfd); 1516 1517 1590 /* Set permissions/owner of output file */ 1518 fstat(fileno(file), &statbuf);1519 1591 /* chmod'ing AFTER chown would preserve suid/sgid bits, 1520 1592 * but GNU sed 4.2.1 does not preserve them either */ … … 1527 1599 1528 1600 if (opt_i) { 1529 char *backupname = xasprintf("%s%s", argv[i], opt_i);1530 xrename( argv[i], backupname);1601 char *backupname = xasprintf("%s%s", *argv, opt_i); 1602 xrename(*argv, backupname); 1531 1603 free(backupname); 1532 1604 } 1533 /* else unlink( argv[i]); - rename below does this */1534 xrename(G.outname, argv[i]); //TODO: rollback backup on error?1605 /* else unlink(*argv); - rename below does this */ 1606 xrename(G.outname, *argv); //TODO: rollback backup on error? 1535 1607 free(G.outname); 1536 1608 G.outname = NULL; 1537 1609 1538 /* Re-enable disabled range matches */1610 /* Fix disabled range matches and mangled ",+N" ranges */ 1539 1611 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) { 1540 1612 sed_cmd->beg_line = sed_cmd->beg_line_orig; 1613 sed_cmd->end_line = sed_cmd->end_line_orig; 1541 1614 } 1542 1615 } 1543 1616 /* Here, to handle "sed 'cmds' nonexistent_file" case we did: 1544 * if (G.current_input_file >= G.input_file_count)1545 * return status;1617 * if (G.current_input_file[G.current_input_file] == NULL) 1618 * return G.exitcode; 1546 1619 * but it's not needed since process_files() works correctly 1547 1620 * in this case too. */ 1548 1621 } 1622 1549 1623 process_files(); 1550 1624 1551 return status;1552 } 1625 return G.exitcode; 1626 }
Note:
See TracChangeset
for help on using the changeset viewer.