aboutsummaryrefslogtreecommitdiff
path: root/editors/sed.c
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2009-06-30 19:19:37 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2009-06-30 19:19:37 +0200
commit8bca3e20b9c057e9144af27870ca3905f1e5d316 (patch)
treec5472b7e8dfdbd847810ba833d5f69e5de327ccc /editors/sed.c
parentf1fab0924285cca27903a1e4a4498c7205810742 (diff)
downloadbusybox-w32-8bca3e20b9c057e9144af27870ca3905f1e5d316.tar.gz
busybox-w32-8bca3e20b9c057e9144af27870ca3905f1e5d316.tar.bz2
busybox-w32-8bca3e20b9c057e9144af27870ca3905f1e5d316.zip
sed: fix handling of 1d;1,3p and 1d;1,REGEXp
function old new delta process_files 2084 2173 +89 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'editors/sed.c')
-rw-r--r--editors/sed.c476
1 files changed, 247 insertions, 229 deletions
diff --git a/editors/sed.c b/editors/sed.c
index eb31f7d2e..2127301d5 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -865,63 +865,80 @@ static void process_files(void)
865 /* Prime the pump */ 865 /* Prime the pump */
866 next_line = get_next_line(&next_gets_char); 866 next_line = get_next_line(&next_gets_char);
867 867
868 /* go through every line in each file */ 868 /* Go through every line in each file */
869 again: 869 again:
870 substituted = 0; 870 substituted = 0;
871 871
872 /* Advance to next line. Stop if out of lines. */ 872 /* Advance to next line. Stop if out of lines. */
873 pattern_space = next_line; 873 pattern_space = next_line;
874 if (!pattern_space) return; 874 if (!pattern_space)
875 return;
875 last_gets_char = next_gets_char; 876 last_gets_char = next_gets_char;
876 877
877 /* Read one line in advance so we can act on the last line, 878 /* Read one line in advance so we can act on the last line,
878 * the '$' address */ 879 * the '$' address */
879 next_line = get_next_line(&next_gets_char); 880 next_line = get_next_line(&next_gets_char);
880 linenum++; 881 linenum++;
882
883 /* For every line, go through all the commands */
881 restart: 884 restart:
882 /* for every line, go through all the commands */
883 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { 885 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
884 int old_matched, matched; 886 int old_matched, matched;
885 887
886 old_matched = sed_cmd->in_match; 888 old_matched = sed_cmd->in_match;
887 889
888 /* Determine if this command matches this line: */ 890 /* Determine if this command matches this line: */
889 891 /* Are we continuing a previous multi-line match? */
890 /* Are we continuing a previous multi-line match? */
891 sed_cmd->in_match = sed_cmd->in_match 892 sed_cmd->in_match = sed_cmd->in_match
892 /* Or is no range necessary? */ 893 /* Or is no range necessary? */
893 || (!sed_cmd->beg_line && !sed_cmd->end_line 894 || (!sed_cmd->beg_line && !sed_cmd->end_line
894 && !sed_cmd->beg_match && !sed_cmd->end_match) 895 && !sed_cmd->beg_match && !sed_cmd->end_match)
895 /* Or did we match the start of a numerical range? */ 896 /* Or did we match the start of a numerical range? */
896 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum)) 897 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum
898 /* "shadowed beginning" case: "1d;1,ENDp" - p still matches at line 2
899 * even though 1d skipped line 1 which is a start line for p */
900 || (sed_cmd->end_line && sed_cmd->beg_line < linenum && sed_cmd->end_line >= linenum)
901 || (sed_cmd->end_match && sed_cmd->beg_line < linenum)
902 )
903 )
897 /* Or does this line match our begin address regex? */ 904 /* Or does this line match our begin address regex? */
898 || (beg_match(sed_cmd, pattern_space)) 905 || (beg_match(sed_cmd, pattern_space))
899 /* Or did we match last line of input? */ 906 /* Or did we match last line of input? */
900 || (sed_cmd->beg_line == -1 && next_line == NULL); 907 || (sed_cmd->beg_line == -1 && next_line == NULL);
901 908
902 /* Snapshot the value */
903
904 matched = sed_cmd->in_match; 909 matched = sed_cmd->in_match;
905 910
906 /* Is this line the end of the current match? */ 911 //bb_error_msg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
912 //sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
907 913
914 /* Is this line the end of the current match? */
908 if (matched) { 915 if (matched) {
909 sed_cmd->in_match = !( 916 int n = (
910 /* has the ending line come, or is this a single address command? */ 917 /* has the ending line come, or is this a single address command? */
911 (sed_cmd->end_line ? 918 sed_cmd->end_line ?
912 sed_cmd->end_line == -1 ? 919 sed_cmd->end_line == -1 ?
913 !next_line 920 !next_line
914 : (sed_cmd->end_line <= linenum) 921 : (sed_cmd->end_line <= linenum)
915 : !sed_cmd->end_match 922 : !sed_cmd->end_match
916 ) 923 );
924 if (!n) {
917 /* or does this line matches our last address regex */ 925 /* or does this line matches our last address regex */
918 || (sed_cmd->end_match && old_matched 926 n = (sed_cmd->end_match
927 && old_matched
919 && (regexec(sed_cmd->end_match, 928 && (regexec(sed_cmd->end_match,
920 pattern_space, 0, NULL, 0) == 0)) 929 pattern_space, 0, NULL, 0) == 0)
921 ); 930 );
931 if (n && sed_cmd->beg_line > 0) {
932 /* Once matched, "n,regex" range is dead, disabling it */
933 regfree(sed_cmd->end_match);
934 free(sed_cmd->end_match);
935 sed_cmd->end_match = NULL;
936 }
937 }
938 sed_cmd->in_match = !n;
922 } 939 }
923 940
924 /* Skip blocks of commands we didn't match. */ 941 /* Skip blocks of commands we didn't match */
925 if (sed_cmd->cmd == '{') { 942 if (sed_cmd->cmd == '{') {
926 if (sed_cmd->invert ? matched : !matched) { 943 if (sed_cmd->invert ? matched : !matched) {
927 while (sed_cmd->cmd != '}') { 944 while (sed_cmd->cmd != '}') {
@@ -934,253 +951,254 @@ static void process_files(void)
934 } 951 }
935 952
936 /* Okay, so did this line match? */ 953 /* Okay, so did this line match? */
937 if (sed_cmd->invert ? !matched : matched) { 954 if (sed_cmd->invert ? matched : !matched)
938 /* Update last used regex in case a blank substitute BRE is found */ 955 continue; /* no */
939 if (sed_cmd->beg_match) {
940 G.previous_regex_ptr = sed_cmd->beg_match;
941 }
942 956
943 /* actual sedding */ 957 /* Update last used regex in case a blank substitute BRE is found */
944 switch (sed_cmd->cmd) { 958 if (sed_cmd->beg_match) {
959 G.previous_regex_ptr = sed_cmd->beg_match;
960 }
945 961
946 /* Print line number */ 962 /* actual sedding */
947 case '=': 963 switch (sed_cmd->cmd) {
948 fprintf(G.nonstdout, "%d\n", linenum);
949 break;
950 964
951 /* Write the current pattern space up to the first newline */ 965 /* Print line number */
952 case 'P': 966 case '=':
953 { 967 fprintf(G.nonstdout, "%d\n", linenum);
954 char *tmp = strchr(pattern_space, '\n'); 968 break;
955
956 if (tmp) {
957 *tmp = '\0';
958 /* TODO: explain why '\n' below */
959 sed_puts(pattern_space, '\n');
960 *tmp = '\n';
961 break;
962 }
963 /* Fall Through */
964 }
965 969
966 /* Write the current pattern space to output */ 970 /* Write the current pattern space up to the first newline */
967 case 'p': 971 case 'P':
968 /* NB: we print this _before_ the last line 972 {
969 * (of current file) is printed. Even if 973 char *tmp = strchr(pattern_space, '\n');
970 * that line is nonterminated, we print 974
971 * '\n' here (gnu sed does the same) */ 975 if (tmp) {
976 *tmp = '\0';
977 /* TODO: explain why '\n' below */
972 sed_puts(pattern_space, '\n'); 978 sed_puts(pattern_space, '\n');
979 *tmp = '\n';
973 break; 980 break;
974 /* Delete up through first newline */
975 case 'D':
976 {
977 char *tmp = strchr(pattern_space, '\n');
978
979 if (tmp) {
980 tmp = xstrdup(tmp+1);
981 free(pattern_space);
982 pattern_space = tmp;
983 goto restart;
984 }
985 } 981 }
986 /* discard this line. */ 982 /* Fall Through */
987 case 'd': 983 }
988 goto discard_line;
989 984
990 /* Substitute with regex */ 985 /* Write the current pattern space to output */
991 case 's': 986 case 'p':
992 if (!do_subst_command(sed_cmd, &pattern_space)) 987 /* NB: we print this _before_ the last line
993 break; 988 * (of current file) is printed. Even if
994 substituted |= 1; 989 * that line is nonterminated, we print
995 990 * '\n' here (gnu sed does the same) */
996 /* handle p option */ 991 sed_puts(pattern_space, '\n');
997 if (sed_cmd->sub_p) 992 break;
998 sed_puts(pattern_space, last_gets_char); 993 /* Delete up through first newline */
999 /* handle w option */ 994 case 'D':
1000 if (sed_cmd->sw_file) 995 {
1001 puts_maybe_newline( 996 char *tmp = strchr(pattern_space, '\n');
1002 pattern_space, sed_cmd->sw_file,
1003 &sed_cmd->sw_last_char, last_gets_char);
1004 break;
1005 997
1006 /* Append line to linked list to be printed later */ 998 if (tmp) {
1007 case 'a': 999 tmp = xstrdup(tmp+1);
1008 append(sed_cmd->string); 1000 free(pattern_space);
1009 break; 1001 pattern_space = tmp;
1002 goto restart;
1003 }
1004 }
1005 /* discard this line. */
1006 case 'd':
1007 goto discard_line;
1010 1008
1011 /* Insert text before this line */ 1009 /* Substitute with regex */
1012 case 'i': 1010 case 's':
1013 sed_puts(sed_cmd->string, '\n'); 1011 if (!do_subst_command(sed_cmd, &pattern_space))
1014 break; 1012 break;
1013 substituted |= 1;
1015 1014
1016 /* Cut and paste text (replace) */ 1015 /* handle p option */
1017 case 'c': 1016 if (sed_cmd->sub_p)
1018 /* Only triggers on last line of a matching range. */ 1017 sed_puts(pattern_space, last_gets_char);
1019 if (!sed_cmd->in_match) 1018 /* handle w option */
1020 sed_puts(sed_cmd->string, NO_EOL_CHAR); 1019 if (sed_cmd->sw_file)
1021 goto discard_line; 1020 puts_maybe_newline(
1021 pattern_space, sed_cmd->sw_file,
1022 &sed_cmd->sw_last_char, last_gets_char);
1023 break;
1022 1024
1023 /* Read file, append contents to output */ 1025 /* Append line to linked list to be printed later */
1024 case 'r': 1026 case 'a':
1025 { 1027 append(sed_cmd->string);
1026 FILE *rfile; 1028 break;
1027 1029
1028 rfile = fopen_for_read(sed_cmd->string); 1030 /* Insert text before this line */
1029 if (rfile) { 1031 case 'i':
1030 char *line; 1032 sed_puts(sed_cmd->string, '\n');
1033 break;
1031 1034
1032 while ((line = xmalloc_fgetline(rfile)) 1035 /* Cut and paste text (replace) */
1033 != NULL) 1036 case 'c':
1034 append(line); 1037 /* Only triggers on last line of a matching range. */
1035 xprint_and_close_file(rfile); 1038 if (!sed_cmd->in_match)
1036 } 1039 sed_puts(sed_cmd->string, NO_EOL_CHAR);
1040 goto discard_line;
1037 1041
1038 break; 1042 /* Read file, append contents to output */
1043 case 'r':
1044 {
1045 FILE *rfile;
1046
1047 rfile = fopen_for_read(sed_cmd->string);
1048 if (rfile) {
1049 char *line;
1050
1051 while ((line = xmalloc_fgetline(rfile))
1052 != NULL)
1053 append(line);
1054 xprint_and_close_file(rfile);
1039 } 1055 }
1040 1056
1041 /* Write pattern space to file. */ 1057 break;
1042 case 'w': 1058 }
1043 puts_maybe_newline(
1044 pattern_space, sed_cmd->sw_file,
1045 &sed_cmd->sw_last_char, last_gets_char);
1046 break;
1047 1059
1048 /* Read next line from input */ 1060 /* Write pattern space to file. */
1049 case 'n': 1061 case 'w':
1050 if (!G.be_quiet) 1062 puts_maybe_newline(
1051 sed_puts(pattern_space, last_gets_char); 1063 pattern_space, sed_cmd->sw_file,
1052 if (next_line) { 1064 &sed_cmd->sw_last_char, last_gets_char);
1053 free(pattern_space); 1065 break;
1054 pattern_space = next_line;
1055 last_gets_char = next_gets_char;
1056 next_line = get_next_line(&next_gets_char);
1057 substituted = 0;
1058 linenum++;
1059 break;
1060 }
1061 /* fall through */
1062 1066
1063 /* Quit. End of script, end of input. */ 1067 /* Read next line from input */
1064 case 'q': 1068 case 'n':
1065 /* Exit the outer while loop */ 1069 if (!G.be_quiet)
1066 free(next_line); 1070 sed_puts(pattern_space, last_gets_char);
1067 next_line = NULL; 1071 if (next_line) {
1068 goto discard_commands; 1072 free(pattern_space);
1069 1073 pattern_space = next_line;
1070 /* Append the next line to the current line */
1071 case 'N':
1072 {
1073 int len;
1074 /* If no next line, jump to end of script and exit. */
1075 if (next_line == NULL) {
1076 /* Jump to end of script and exit */
1077 free(next_line);
1078 next_line = NULL;
1079 goto discard_line;
1080 /* append next_line, read new next_line. */
1081 }
1082 len = strlen(pattern_space);
1083 pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);
1084 pattern_space[len] = '\n';
1085 strcpy(pattern_space + len+1, next_line);
1086 last_gets_char = next_gets_char; 1074 last_gets_char = next_gets_char;
1087 next_line = get_next_line(&next_gets_char); 1075 next_line = get_next_line(&next_gets_char);
1076 substituted = 0;
1088 linenum++; 1077 linenum++;
1089 break; 1078 break;
1090 } 1079 }
1080 /* fall through */
1091 1081
1092 /* Test/branch if substitution occurred */ 1082 /* Quit. End of script, end of input. */
1093 case 't': 1083 case 'q':
1094 if (!substituted) break; 1084 /* Exit the outer while loop */
1095 substituted = 0; 1085 free(next_line);
1096 /* Fall through */ 1086 next_line = NULL;
1097 /* Test/branch if substitution didn't occur */ 1087 goto discard_commands;
1098 case 'T':
1099 if (substituted) break;
1100 /* Fall through */
1101 /* Branch to label */
1102 case 'b':
1103 if (!sed_cmd->string) goto discard_commands;
1104 else sed_cmd = branch_to(sed_cmd->string);
1105 break;
1106 /* Transliterate characters */
1107 case 'y':
1108 {
1109 int i, j;
1110
1111 for (i = 0; pattern_space[i]; i++) {
1112 for (j = 0; sed_cmd->string[j]; j += 2) {
1113 if (pattern_space[i] == sed_cmd->string[j]) {
1114 pattern_space[i] = sed_cmd->string[j + 1];
1115 break;
1116 }
1117 }
1118 }
1119 1088
1120 break; 1089 /* Append the next line to the current line */
1090 case 'N':
1091 {
1092 int len;
1093 /* If no next line, jump to end of script and exit. */
1094 if (next_line == NULL) {
1095 /* Jump to end of script and exit */
1096 free(next_line);
1097 next_line = NULL;
1098 goto discard_line;
1099 /* append next_line, read new next_line. */
1121 } 1100 }
1122 case 'g': /* Replace pattern space with hold space */ 1101 len = strlen(pattern_space);
1123 free(pattern_space); 1102 pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);
1124 pattern_space = xstrdup(G.hold_space ? G.hold_space : ""); 1103 pattern_space[len] = '\n';
1125 break; 1104 strcpy(pattern_space + len+1, next_line);
1126 case 'G': /* Append newline and hold space to pattern space */ 1105 last_gets_char = next_gets_char;
1127 { 1106 next_line = get_next_line(&next_gets_char);
1128 int pattern_space_size = 2; 1107 linenum++;
1129 int hold_space_size = 0; 1108 break;
1130 1109 }
1131 if (pattern_space)
1132 pattern_space_size += strlen(pattern_space);
1133 if (G.hold_space)
1134 hold_space_size = strlen(G.hold_space);
1135 pattern_space = xrealloc(pattern_space,
1136 pattern_space_size + hold_space_size);
1137 if (pattern_space_size == 2)
1138 pattern_space[0] = 0;
1139 strcat(pattern_space, "\n");
1140 if (G.hold_space)
1141 strcat(pattern_space, G.hold_space);
1142 last_gets_char = '\n';
1143 1110
1144 break; 1111 /* Test/branch if substitution occurred */
1145 } 1112 case 't':
1146 case 'h': /* Replace hold space with pattern space */ 1113 if (!substituted) break;
1147 free(G.hold_space); 1114 substituted = 0;
1148 G.hold_space = xstrdup(pattern_space); 1115 /* Fall through */
1149 break; 1116 /* Test/branch if substitution didn't occur */
1150 case 'H': /* Append newline and pattern space to hold space */ 1117 case 'T':
1151 { 1118 if (substituted) break;
1152 int hold_space_size = 2; 1119 /* Fall through */
1153 int pattern_space_size = 0; 1120 /* Branch to label */
1154 1121 case 'b':
1155 if (G.hold_space) 1122 if (!sed_cmd->string) goto discard_commands;
1156 hold_space_size += strlen(G.hold_space); 1123 else sed_cmd = branch_to(sed_cmd->string);
1157 if (pattern_space) 1124 break;
1158 pattern_space_size = strlen(pattern_space); 1125 /* Transliterate characters */
1159 G.hold_space = xrealloc(G.hold_space, 1126 case 'y':
1160 hold_space_size + pattern_space_size); 1127 {
1161 1128 int i, j;
1162 if (hold_space_size == 2)
1163 *G.hold_space = 0;
1164 strcat(G.hold_space, "\n");
1165 if (pattern_space)
1166 strcat(G.hold_space, pattern_space);
1167 1129
1168 break; 1130 for (i = 0; pattern_space[i]; i++) {
1169 } 1131 for (j = 0; sed_cmd->string[j]; j += 2) {
1170 case 'x': /* Exchange hold and pattern space */ 1132 if (pattern_space[i] == sed_cmd->string[j]) {
1171 { 1133 pattern_space[i] = sed_cmd->string[j + 1];
1172 char *tmp = pattern_space; 1134 break;
1173 pattern_space = G.hold_space ? : xzalloc(1); 1135 }
1174 last_gets_char = '\n'; 1136 }
1175 G.hold_space = tmp;
1176 break;
1177 }
1178 } 1137 }
1138
1139 break;
1179 } 1140 }
1180 } 1141 case 'g': /* Replace pattern space with hold space */
1142 free(pattern_space);
1143 pattern_space = xstrdup(G.hold_space ? G.hold_space : "");
1144 break;
1145 case 'G': /* Append newline and hold space to pattern space */
1146 {
1147 int pattern_space_size = 2;
1148 int hold_space_size = 0;
1149
1150 if (pattern_space)
1151 pattern_space_size += strlen(pattern_space);
1152 if (G.hold_space)
1153 hold_space_size = strlen(G.hold_space);
1154 pattern_space = xrealloc(pattern_space,
1155 pattern_space_size + hold_space_size);
1156 if (pattern_space_size == 2)
1157 pattern_space[0] = 0;
1158 strcat(pattern_space, "\n");
1159 if (G.hold_space)
1160 strcat(pattern_space, G.hold_space);
1161 last_gets_char = '\n';
1162
1163 break;
1164 }
1165 case 'h': /* Replace hold space with pattern space */
1166 free(G.hold_space);
1167 G.hold_space = xstrdup(pattern_space);
1168 break;
1169 case 'H': /* Append newline and pattern space to hold space */
1170 {
1171 int hold_space_size = 2;
1172 int pattern_space_size = 0;
1173
1174 if (G.hold_space)
1175 hold_space_size += strlen(G.hold_space);
1176 if (pattern_space)
1177 pattern_space_size = strlen(pattern_space);
1178 G.hold_space = xrealloc(G.hold_space,
1179 hold_space_size + pattern_space_size);
1180
1181 if (hold_space_size == 2)
1182 *G.hold_space = 0;
1183 strcat(G.hold_space, "\n");
1184 if (pattern_space)
1185 strcat(G.hold_space, pattern_space);
1186
1187 break;
1188 }
1189 case 'x': /* Exchange hold and pattern space */
1190 {
1191 char *tmp = pattern_space;
1192 pattern_space = G.hold_space ? : xzalloc(1);
1193 last_gets_char = '\n';
1194 G.hold_space = tmp;
1195 break;
1196 }
1197 } /* switch */
1198 } /* for each cmd */
1181 1199
1182 /* 1200 /*
1183 * exit point from sedding... 1201 * Exit point from sedding...
1184 */ 1202 */
1185 discard_commands: 1203 discard_commands:
1186 /* we will print the line unless we were told to be quiet ('-n') 1204 /* we will print the line unless we were told to be quiet ('-n')