static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p) { char *line = *line_p; unsigned match_count = 0; bool altered = 0; bool prev_match_empty = 1; bool tried_at_eol = 0; regex_t *current_regex; current_regex = sed_cmd->sub_match; /* Handle empty regex. */ if (!current_regex) { current_regex = G.previous_regex_ptr; if (!current_regex) bb_error_msg_and_die("no previous regexp"); } G.previous_regex_ptr = current_regex; /* Find the first match */ dbg("matching '%s'", line); if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0)) { dbg("no match"); return 0; } dbg("match"); /* Initialize temporary output buffer. */ G.pipeline.buf = xmalloc(PIPE_GROW); G.pipeline.len = PIPE_GROW; G.pipeline.idx = 0; /* Now loop through, substituting for matches */ do { int start = G.regmatch[0].rm_so; int end = G.regmatch[0].rm_eo; int i; match_count++; /* If we aren't interested in this match, output old line to * end of match and continue */ if (sed_cmd->which_match && (sed_cmd->which_match != match_count) ) { for (i = 0; i < end; i++) pipe_putc(*line++); /* Null match? Print one more char */ if (start == end && *line) pipe_putc(*line++); goto next; } /* Print everything before the match */ for (i = 0; i < start; i++) pipe_putc(line[i]); /* Then print the substitution string, * unless we just matched empty string after non-empty one. * Example: string "cccd", pattern "c*", repl "R": * result is "RdR", not "RRdR": first match "ccc", * second is "" before "d", third is "" after "d". * Second match is NOT replaced! */ if (prev_match_empty || start != 0 || start != end) { //dbg("%d %d %d", prev_match_empty, start, end); dbg("inserting replacement at %d in '%s'", start, line); do_subst_w_backrefs(line, sed_cmd->string); /* Flag that something has changed */ altered = 1; } else { dbg("NOT inserting replacement at %d in '%s'", start, line); } /* If matched string is empty (f.e. "c*" pattern), * copy verbatim one char after it before attempting more matches */ prev_match_empty = (start == end); if (prev_match_empty) { if (!line[end]) { tried_at_eol = 1; } else { pipe_putc(line[end]); end++; } } /* Advance past the match */ dbg("line += %d", end); line += end; /* if we're not doing this globally, get out now */ if (sed_cmd->which_match != 0) break; next: /* Exit if we are at EOL and already tried matching at it */ if (*line == '\0') { if (tried_at_eol) break; tried_at_eol = 1; } //maybe (end ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL? } while (regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH); /* Copy rest of string into output pipeline */ while (1) { char c = *line++; pipe_putc(c); if (c == '\0') break; } free(*line_p); *line_p = G.pipeline.buf; return altered; }
static int do_subst_command(sed_cmd_t *sed_cmd, char **line) { char *oldline = *line; int altered = 0; unsigned match_count = 0; regex_t *current_regex; /* Handle empty regex. */ if (sed_cmd->sub_match == NULL) { current_regex = G.previous_regex_ptr; if (!current_regex) bb_error_msg_and_die("no previous regexp"); } else G.previous_regex_ptr = current_regex = sed_cmd->sub_match; /* Find the first match */ if (REG_NOMATCH == regexec(current_regex, oldline, 10, G.regmatch, 0)) return 0; /* Initialize temporary output buffer. */ G.pipeline.buf = xmalloc(PIPE_GROW); G.pipeline.len = PIPE_GROW; G.pipeline.idx = 0; /* Now loop through, substituting for matches */ do { int i; /* Work around bug in glibc regexec, demonstrated by: echo " a.b" | busybox sed 's [^ .]* x g' The match_count check is so not to break echo "hi" | busybox sed 's/^/!/g' */ if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) { pipe_putc(*oldline++); continue; } match_count++; /* If we aren't interested in this match, output old line to end of match and continue */ if (sed_cmd->which_match && (sed_cmd->which_match != match_count) ) { for (i = 0; i < G.regmatch[0].rm_eo; i++) pipe_putc(*oldline++); continue; } /* print everything before the match */ for (i = 0; i < G.regmatch[0].rm_so; i++) pipe_putc(oldline[i]); /* then print the substitution string */ do_subst_w_backrefs(oldline, sed_cmd->string); /* advance past the match */ oldline += G.regmatch[0].rm_eo; /* flag that something has changed */ altered++; /* if we're not doing this globally, get out now */ if (sed_cmd->which_match) break; } while (*oldline && (regexec(current_regex, oldline, 10, G.regmatch, 0) != REG_NOMATCH)); /* Copy rest of string into output pipeline */ while (*oldline) pipe_putc(*oldline++); pipe_putc(0); free(*line); *line = G.pipeline.buf; return altered; }