aboutsummaryrefslogtreecommitdiff
path: root/editors/sed.c
diff options
context:
space:
mode:
Diffstat (limited to 'editors/sed.c')
-rw-r--r--editors/sed.c79
1 files changed, 51 insertions, 28 deletions
diff --git a/editors/sed.c b/editors/sed.c
index 5c4e9cc3b..1552cf370 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -75,6 +75,13 @@
75#include "libbb.h" 75#include "libbb.h"
76#include "xregex.h" 76#include "xregex.h"
77 77
78#if 0
79# define dbg(...) bb_error_msg(__VA_ARGS__)
80#else
81# define dbg(...) ((void)0)
82#endif
83
84
78enum { 85enum {
79 OPT_in_place = 1 << 0, 86 OPT_in_place = 1 << 0,
80}; 87};
@@ -89,6 +96,7 @@ typedef struct sed_cmd_s {
89 regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */ 96 regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */
90 regex_t *sub_match; /* For 's/sub_match/string/' */ 97 regex_t *sub_match; /* For 's/sub_match/string/' */
91 int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ 98 int beg_line; /* 'sed 1p' 0 == apply commands to all lines */
99 int beg_line_orig; /* copy of the above, needed for -i */
92 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ 100 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
93 101
94 FILE *sw_file; /* File (sw) command writes to, -1 for none. */ 102 FILE *sw_file; /* File (sw) command writes to, -1 for none. */
@@ -123,7 +131,7 @@ struct globals {
123 regex_t *previous_regex_ptr; 131 regex_t *previous_regex_ptr;
124 132
125 /* linked list of sed commands */ 133 /* linked list of sed commands */
126 sed_cmd_t sed_cmd_head, *sed_cmd_tail; 134 sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
127 135
128 /* Linked list of append lines */ 136 /* Linked list of append lines */
129 llist_t *append_head; 137 llist_t *append_head;
@@ -148,7 +156,7 @@ struct BUG_G_too_big {
148#if ENABLE_FEATURE_CLEAN_UP 156#if ENABLE_FEATURE_CLEAN_UP
149static void sed_free_and_close_stuff(void) 157static void sed_free_and_close_stuff(void)
150{ 158{
151 sed_cmd_t *sed_cmd = G.sed_cmd_head.next; 159 sed_cmd_t *sed_cmd = G.sed_cmd_head;
152 160
153 llist_free(G.append_head, free); 161 llist_free(G.append_head, free);
154 162
@@ -599,6 +607,7 @@ static void add_cmd(const char *cmdstr)
599 607
600 /* first part (if present) is an address: either a '$', a number or a /regex/ */ 608 /* first part (if present) is an address: either a '$', a number or a /regex/ */
601 cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); 609 cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
610 sed_cmd->beg_line_orig = sed_cmd->beg_line;
602 611
603 /* second part (if present) will begin with a comma */ 612 /* second part (if present) will begin with a comma */
604 if (*cmdstr == ',') { 613 if (*cmdstr == ',') {
@@ -630,8 +639,8 @@ static void add_cmd(const char *cmdstr)
630 cmdstr = parse_cmd_args(sed_cmd, cmdstr); 639 cmdstr = parse_cmd_args(sed_cmd, cmdstr);
631 640
632 /* Add the command to the command array */ 641 /* Add the command to the command array */
633 G.sed_cmd_tail->next = sed_cmd; 642 *G.sed_cmd_tail = sed_cmd;
634 G.sed_cmd_tail = G.sed_cmd_tail->next; 643 G.sed_cmd_tail = &sed_cmd->next;
635 } 644 }
636 645
637 /* If we glued multiple lines together, free the memory. */ 646 /* If we glued multiple lines together, free the memory. */
@@ -777,7 +786,7 @@ static sed_cmd_t *branch_to(char *label)
777{ 786{
778 sed_cmd_t *sed_cmd; 787 sed_cmd_t *sed_cmd;
779 788
780 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { 789 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
781 if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) { 790 if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
782 return sed_cmd; 791 return sed_cmd;
783 } 792 }
@@ -953,24 +962,24 @@ static void process_files(void)
953 962
954 /* For every line, go through all the commands */ 963 /* For every line, go through all the commands */
955 restart: 964 restart:
956 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { 965 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
957 int old_matched, matched; 966 int old_matched, matched;
958 967
959 old_matched = sed_cmd->in_match; 968 old_matched = sed_cmd->in_match;
960 969
961 /* Determine if this command matches this line: */ 970 /* Determine if this command matches this line: */
962 971
963 //bb_error_msg("match1:%d", sed_cmd->in_match); 972 dbg("match1:%d", sed_cmd->in_match);
964 //bb_error_msg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line 973 dbg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
965 // && !sed_cmd->beg_match && !sed_cmd->end_match)); 974 && !sed_cmd->beg_match && !sed_cmd->end_match));
966 //bb_error_msg("match3:%d", (sed_cmd->beg_line > 0 975 dbg("match3:%d", (sed_cmd->beg_line > 0
967 // && (sed_cmd->end_line || sed_cmd->end_match 976 && (sed_cmd->end_line || sed_cmd->end_match
968 // ? (sed_cmd->beg_line <= linenum) 977 ? (sed_cmd->beg_line <= linenum)
969 // : (sed_cmd->beg_line == linenum) 978 : (sed_cmd->beg_line == linenum)
970 // ) 979 )
971 // ) 980 ));
972 //bb_error_msg("match4:%d", (beg_match(sed_cmd, pattern_space))); 981 dbg("match4:%d", (beg_match(sed_cmd, pattern_space)));
973 //bb_error_msg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL)); 982 dbg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
974 983
975 /* Are we continuing a previous multi-line match? */ 984 /* Are we continuing a previous multi-line match? */
976 sed_cmd->in_match = sed_cmd->in_match 985 sed_cmd->in_match = sed_cmd->in_match
@@ -981,7 +990,14 @@ static void process_files(void)
981 || (sed_cmd->beg_line > 0 990 || (sed_cmd->beg_line > 0
982 && (sed_cmd->end_line || sed_cmd->end_match 991 && (sed_cmd->end_line || sed_cmd->end_match
983 /* note: even if end is numeric and is < linenum too, 992 /* note: even if end is numeric and is < linenum too,
984 * GNU sed matches! We match too */ 993 * GNU sed matches! We match too, therefore we don't
994 * check here that linenum <= end.
995 * Example:
996 * printf '1\n2\n3\n4\n' | sed -n '1{N;N;d};1p;2,3p;3p;4p'
997 * first three input lines are deleted;
998 * 4th line is matched and printed
999 * by "2,3" (!) and by "4" ranges
1000 */
985 ? (sed_cmd->beg_line <= linenum) /* N,end */ 1001 ? (sed_cmd->beg_line <= linenum) /* N,end */
986 : (sed_cmd->beg_line == linenum) /* N */ 1002 : (sed_cmd->beg_line == linenum) /* N */
987 ) 1003 )
@@ -994,16 +1010,14 @@ static void process_files(void)
994 /* Snapshot the value */ 1010 /* Snapshot the value */
995 matched = sed_cmd->in_match; 1011 matched = sed_cmd->in_match;
996 1012
997 //bb_error_msg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d", 1013 dbg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
998 //sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum); 1014 sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
999 1015
1000 /* Is this line the end of the current match? */ 1016 /* Is this line the end of the current match? */
1001 1017
1002 if (matched) { 1018 if (matched) {
1003 /* once matched, "n,xxx" range is dead, disabling it */ 1019 /* once matched, "n,xxx" range is dead, disabling it */
1004 if (sed_cmd->beg_line > 0 1020 if (sed_cmd->beg_line > 0) {
1005 && !(option_mask32 & OPT_in_place) /* but not for -i */
1006 ) {
1007 sed_cmd->beg_line = -2; 1021 sed_cmd->beg_line = -2;
1008 } 1022 }
1009 sed_cmd->in_match = !( 1023 sed_cmd->in_match = !(
@@ -1017,7 +1031,8 @@ static void process_files(void)
1017 /* or does this line matches our last address regex */ 1031 /* or does this line matches our last address regex */
1018 || (sed_cmd->end_match && old_matched 1032 || (sed_cmd->end_match && old_matched
1019 && (regexec(sed_cmd->end_match, 1033 && (regexec(sed_cmd->end_match,
1020 pattern_space, 0, NULL, 0) == 0)) 1034 pattern_space, 0, NULL, 0) == 0)
1035 )
1021 ); 1036 );
1022 } 1037 }
1023 1038
@@ -1407,11 +1422,12 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1407 add_input_file(stdin); 1422 add_input_file(stdin);
1408 } else { 1423 } else {
1409 int i; 1424 int i;
1410 FILE *file;
1411 1425
1412 for (i = 0; argv[i]; i++) { 1426 for (i = 0; argv[i]; i++) {
1413 struct stat statbuf; 1427 struct stat statbuf;
1414 int nonstdoutfd; 1428 int nonstdoutfd;
1429 FILE *file;
1430 sed_cmd_t *sed_cmd;
1415 1431
1416 if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { 1432 if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
1417 add_input_file(stdin); 1433 add_input_file(stdin);
@@ -1423,11 +1439,13 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1423 status = EXIT_FAILURE; 1439 status = EXIT_FAILURE;
1424 continue; 1440 continue;
1425 } 1441 }
1442 add_input_file(file);
1426 if (!(opt & OPT_in_place)) { 1443 if (!(opt & OPT_in_place)) {
1427 add_input_file(file);
1428 continue; 1444 continue;
1429 } 1445 }
1430 1446
1447 /* -i: process each FILE separately: */
1448
1431 G.outname = xasprintf("%sXXXXXX", argv[i]); 1449 G.outname = xasprintf("%sXXXXXX", argv[i]);
1432 nonstdoutfd = xmkstemp(G.outname); 1450 nonstdoutfd = xmkstemp(G.outname);
1433 G.nonstdout = xfdopen_for_write(nonstdoutfd); 1451 G.nonstdout = xfdopen_for_write(nonstdoutfd);
@@ -1438,15 +1456,20 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1438 * but GNU sed 4.2.1 does not preserve them either */ 1456 * but GNU sed 4.2.1 does not preserve them either */
1439 fchmod(nonstdoutfd, statbuf.st_mode); 1457 fchmod(nonstdoutfd, statbuf.st_mode);
1440 fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid); 1458 fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
1441 add_input_file(file); 1459
1442 process_files(); 1460 process_files();
1443 fclose(G.nonstdout); 1461 fclose(G.nonstdout);
1444
1445 G.nonstdout = stdout; 1462 G.nonstdout = stdout;
1463
1446 /* unlink(argv[i]); */ 1464 /* unlink(argv[i]); */
1447 xrename(G.outname, argv[i]); 1465 xrename(G.outname, argv[i]);
1448 free(G.outname); 1466 free(G.outname);
1449 G.outname = NULL; 1467 G.outname = NULL;
1468
1469 /* Re-enable disabled range matches */
1470 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
1471 sed_cmd->beg_line = sed_cmd->beg_line_orig;
1472 }
1450 } 1473 }
1451 /* Here, to handle "sed 'cmds' nonexistent_file" case we did: 1474 /* Here, to handle "sed 'cmds' nonexistent_file" case we did:
1452 * if (G.current_input_file >= G.input_file_count) 1475 * if (G.current_input_file >= G.input_file_count)