diff options
author | Ron Yorston <rmy@pobox.com> | 2021-07-17 10:35:24 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-07-17 10:35:24 +0100 |
commit | 36e9606c1d1edfbc9e4eba31b4c498dba361ac98 (patch) | |
tree | 492a436d5fba5a545e7155a115679e0669e9bed6 /editors/vi.c | |
parent | dcca23dc4cf9b51a1c2576360fea190e9b71204e (diff) | |
parent | dabbeeb79356eef78528acd55e1f143ae80372f7 (diff) | |
download | busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.tar.gz busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.tar.bz2 busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'editors/vi.c')
-rw-r--r-- | editors/vi.c | 159 |
1 files changed, 139 insertions, 20 deletions
diff --git a/editors/vi.c b/editors/vi.c index 89c567f10..a079be3fe 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -2444,12 +2444,14 @@ static char *char_search(char *p, const char *pat, int dir_and_range) | |||
2444 | char *q; | 2444 | char *q; |
2445 | int i, size, range, start; | 2445 | int i, size, range, start; |
2446 | 2446 | ||
2447 | re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; | 2447 | re_syntax_options = RE_SYNTAX_POSIX_BASIC & (~RE_DOT_NEWLINE); |
2448 | if (ignorecase) | 2448 | if (ignorecase) |
2449 | re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE; | 2449 | re_syntax_options |= RE_ICASE; |
2450 | 2450 | ||
2451 | memset(&preg, 0, sizeof(preg)); | 2451 | memset(&preg, 0, sizeof(preg)); |
2452 | err = re_compile_pattern(pat, strlen(pat), &preg); | 2452 | err = re_compile_pattern(pat, strlen(pat), &preg); |
2453 | preg.not_bol = p != text; | ||
2454 | preg.not_eol = p != end - 1; | ||
2453 | if (err != NULL) { | 2455 | if (err != NULL) { |
2454 | status_line_bold("bad search pattern '%s': %s", pat, err); | 2456 | status_line_bold("bad search pattern '%s': %s", pat, err); |
2455 | return p; | 2457 | return p; |
@@ -2741,6 +2743,75 @@ static char *expand_args(char *args) | |||
2741 | # endif | 2743 | # endif |
2742 | #endif /* FEATURE_VI_COLON */ | 2744 | #endif /* FEATURE_VI_COLON */ |
2743 | 2745 | ||
2746 | #if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
2747 | # define MAX_SUBPATTERN 10 // subpatterns \0 .. \9 | ||
2748 | |||
2749 | // Like strchr() but skipping backslash-escaped characters | ||
2750 | static char *strchr_backslash(const char *s, int c) | ||
2751 | { | ||
2752 | while (*s) { | ||
2753 | if (*s == c) | ||
2754 | return (char *)s; | ||
2755 | if (*s == '\\') | ||
2756 | if (*++s == '\0') | ||
2757 | break; | ||
2758 | s++; | ||
2759 | } | ||
2760 | return NULL; | ||
2761 | } | ||
2762 | |||
2763 | // If the return value is not NULL the caller should free R | ||
2764 | static char *regex_search(char *q, regex_t *preg, const char *Rorig, | ||
2765 | size_t *len_F, size_t *len_R, char **R) | ||
2766 | { | ||
2767 | regmatch_t regmatch[MAX_SUBPATTERN], *cur_match; | ||
2768 | char *found = NULL; | ||
2769 | const char *t; | ||
2770 | char *r; | ||
2771 | |||
2772 | regmatch[0].rm_so = 0; | ||
2773 | regmatch[0].rm_eo = end_line(q) - q; | ||
2774 | if (regexec(preg, q, MAX_SUBPATTERN, regmatch, REG_STARTEND) != 0) | ||
2775 | return found; | ||
2776 | |||
2777 | found = q + regmatch[0].rm_so; | ||
2778 | *len_F = regmatch[0].rm_eo - regmatch[0].rm_so; | ||
2779 | *R = NULL; | ||
2780 | |||
2781 | fill_result: | ||
2782 | // first pass calculates len_R, second fills R | ||
2783 | *len_R = 0; | ||
2784 | for (t = Rorig, r = *R; *t; t++) { | ||
2785 | size_t len = 1; // default is to copy one char from replace pattern | ||
2786 | const char *from = t; | ||
2787 | if (*t == '\\') { | ||
2788 | from = ++t; // skip backslash | ||
2789 | if (*t >= '0' && *t < '0' + MAX_SUBPATTERN) { | ||
2790 | cur_match = regmatch + (*t - '0'); | ||
2791 | if (cur_match->rm_so >= 0) { | ||
2792 | len = cur_match->rm_eo - cur_match->rm_so; | ||
2793 | from = q + cur_match->rm_so; | ||
2794 | } | ||
2795 | } | ||
2796 | } | ||
2797 | *len_R += len; | ||
2798 | if (*R) { | ||
2799 | memcpy(r, from, len); | ||
2800 | r += len; | ||
2801 | /* *r = '\0'; - xzalloc did it */ | ||
2802 | } | ||
2803 | } | ||
2804 | if (*R == NULL) { | ||
2805 | *R = xzalloc(*len_R + 1); | ||
2806 | goto fill_result; | ||
2807 | } | ||
2808 | |||
2809 | return found; | ||
2810 | } | ||
2811 | #else /* !ENABLE_FEATURE_VI_REGEX_SEARCH */ | ||
2812 | # define strchr_backslash(s, c) strchr(s, c) | ||
2813 | #endif /* ENABLE_FEATURE_VI_REGEX_SEARCH */ | ||
2814 | |||
2744 | // buf must be no longer than MAX_INPUT_LEN! | 2815 | // buf must be no longer than MAX_INPUT_LEN! |
2745 | static void colon(char *buf) | 2816 | static void colon(char *buf) |
2746 | { | 2817 | { |
@@ -3148,23 +3219,30 @@ static void colon(char *buf) | |||
3148 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | 3219 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS |
3149 | int last_line = 0, lines = 0; | 3220 | int last_line = 0, lines = 0; |
3150 | # endif | 3221 | # endif |
3222 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3223 | regex_t preg; | ||
3224 | int cflags; | ||
3225 | char *Rorig; | ||
3226 | # if ENABLE_FEATURE_VI_UNDO | ||
3227 | int undo = 0; | ||
3228 | # endif | ||
3229 | # endif | ||
3151 | 3230 | ||
3152 | // F points to the "find" pattern | 3231 | // F points to the "find" pattern |
3153 | // R points to the "replace" pattern | 3232 | // R points to the "replace" pattern |
3154 | // replace the cmd line delimiters "/" with NULs | 3233 | // replace the cmd line delimiters "/" with NULs |
3155 | c = buf[1]; // what is the delimiter | 3234 | c = buf[1]; // what is the delimiter |
3156 | F = buf + 2; // start of "find" | 3235 | F = buf + 2; // start of "find" |
3157 | R = strchr(F, c); // middle delimiter | 3236 | R = strchr_backslash(F, c); // middle delimiter |
3158 | if (!R) | 3237 | if (!R) |
3159 | goto colon_s_fail; | 3238 | goto colon_s_fail; |
3160 | len_F = R - F; | 3239 | len_F = R - F; |
3161 | *R++ = '\0'; // terminate "find" | 3240 | *R++ = '\0'; // terminate "find" |
3162 | flags = strchr(R, c); | 3241 | flags = strchr_backslash(R, c); |
3163 | if (flags) { | 3242 | if (flags) { |
3164 | *flags++ = '\0'; // terminate "replace" | 3243 | *flags++ = '\0'; // terminate "replace" |
3165 | gflag = *flags; | 3244 | gflag = *flags; |
3166 | } | 3245 | } |
3167 | len_R = strlen(R); | ||
3168 | 3246 | ||
3169 | if (len_F) { // save "find" as last search pattern | 3247 | if (len_F) { // save "find" as last search pattern |
3170 | free(last_search_pattern); | 3248 | free(last_search_pattern); |
@@ -3186,31 +3264,67 @@ static void colon(char *buf) | |||
3186 | b = e; | 3264 | b = e; |
3187 | } | 3265 | } |
3188 | 3266 | ||
3267 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3268 | Rorig = R; | ||
3269 | cflags = 0; | ||
3270 | if (ignorecase) | ||
3271 | cflags = REG_ICASE; | ||
3272 | memset(&preg, 0, sizeof(preg)); | ||
3273 | if (regcomp(&preg, F, cflags) != 0) { | ||
3274 | status_line(":s bad search pattern"); | ||
3275 | goto regex_search_end; | ||
3276 | } | ||
3277 | # else | ||
3278 | len_R = strlen(R); | ||
3279 | # endif | ||
3280 | |||
3189 | for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 | 3281 | for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 |
3190 | char *ls = q; // orig line start | 3282 | char *ls = q; // orig line start |
3191 | char *found; | 3283 | char *found; |
3192 | vc4: | 3284 | vc4: |
3285 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3286 | found = regex_search(q, &preg, Rorig, &len_F, &len_R, &R); | ||
3287 | # else | ||
3193 | found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find" | 3288 | found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find" |
3289 | # endif | ||
3194 | if (found) { | 3290 | if (found) { |
3195 | uintptr_t bias; | 3291 | uintptr_t bias; |
3196 | // we found the "find" pattern - delete it | 3292 | // we found the "find" pattern - delete it |
3197 | // For undo support, the first item should not be chained | 3293 | // For undo support, the first item should not be chained |
3198 | text_hole_delete(found, found + len_F - 1, | 3294 | // This needs to be handled differently depending on |
3199 | subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); | 3295 | // whether or not regex support is enabled. |
3200 | // can't do this above, no undo => no third argument | 3296 | # if ENABLE_FEATURE_VI_REGEX_SEARCH |
3201 | subs++; | 3297 | # define TEST_LEN_F len_F // len_F may be zero |
3202 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | 3298 | # define TEST_UNDO1 undo++ |
3203 | if (last_line != i) { | 3299 | # define TEST_UNDO2 undo++ |
3204 | last_line = i; | 3300 | # else |
3205 | ++lines; | 3301 | # define TEST_LEN_F 1 // len_F is never zero |
3302 | # define TEST_UNDO1 subs | ||
3303 | # define TEST_UNDO2 1 | ||
3304 | # endif | ||
3305 | if (TEST_LEN_F) // match can be empty, no delete needed | ||
3306 | text_hole_delete(found, found + len_F - 1, | ||
3307 | TEST_UNDO1 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO); | ||
3308 | if (len_R != 0) { // insert the "replace" pattern, if required | ||
3309 | bias = string_insert(found, R, | ||
3310 | TEST_UNDO2 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO); | ||
3311 | found += bias; | ||
3312 | ls += bias; | ||
3313 | //q += bias; - recalculated anyway | ||
3206 | } | 3314 | } |
3315 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3316 | free(R); | ||
3317 | # endif | ||
3318 | if (TEST_LEN_F || len_R != 0) { | ||
3319 | dot = ls; | ||
3320 | subs++; | ||
3321 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
3322 | if (last_line != i) { | ||
3323 | last_line = i; | ||
3324 | ++lines; | ||
3325 | } | ||
3207 | # endif | 3326 | # endif |
3208 | // insert the "replace" patern | 3327 | } |
3209 | bias = string_insert(found, R, ALLOW_UNDO_CHAIN); | ||
3210 | found += bias; | ||
3211 | ls += bias; | ||
3212 | dot = ls; | ||
3213 | //q += bias; - recalculated anyway | ||
3214 | // check for "global" :s/foo/bar/g | 3328 | // check for "global" :s/foo/bar/g |
3215 | if (gflag == 'g') { | 3329 | if (gflag == 'g') { |
3216 | if ((found + len_R) < end_line(ls)) { | 3330 | if ((found + len_R) < end_line(ls)) { |
@@ -3230,6 +3344,10 @@ static void colon(char *buf) | |||
3230 | status_line("%d substitutions on %d lines", subs, lines); | 3344 | status_line("%d substitutions on %d lines", subs, lines); |
3231 | # endif | 3345 | # endif |
3232 | } | 3346 | } |
3347 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3348 | regex_search_end: | ||
3349 | regfree(&preg); | ||
3350 | # endif | ||
3233 | # endif /* FEATURE_VI_SEARCH */ | 3351 | # endif /* FEATURE_VI_SEARCH */ |
3234 | } else if (strncmp(cmd, "version", i) == 0) { // show software version | 3352 | } else if (strncmp(cmd, "version", i) == 0) { // show software version |
3235 | status_line(BB_VER); | 3353 | status_line(BB_VER); |
@@ -3503,7 +3621,7 @@ static int find_range(char **start, char **stop, int cmd) | |||
3503 | // for non-change operations WS after NL is not part of word | 3621 | // for non-change operations WS after NL is not part of word |
3504 | if (cmd != 'c' && dot != t && *dot != '\n') | 3622 | if (cmd != 'c' && dot != t && *dot != '\n') |
3505 | dot = t; | 3623 | dot = t; |
3506 | } else if (strchr("GHL+-jk'\r\n", c)) { | 3624 | } else if (strchr("GHL+-gjk'\r\n", c)) { |
3507 | // these operate on whole lines | 3625 | // these operate on whole lines |
3508 | buftype = WHOLE; | 3626 | buftype = WHOLE; |
3509 | do_cmd(c); // execute movement cmd | 3627 | do_cmd(c); // execute movement cmd |
@@ -4093,6 +4211,7 @@ static void do_cmd(int c) | |||
4093 | buf[1] = (c1 >= 0 ? c1 : '*'); | 4211 | buf[1] = (c1 >= 0 ? c1 : '*'); |
4094 | buf[2] = '\0'; | 4212 | buf[2] = '\0'; |
4095 | not_implemented(buf); | 4213 | not_implemented(buf); |
4214 | cmd_error = TRUE; | ||
4096 | break; | 4215 | break; |
4097 | } | 4216 | } |
4098 | if (cmdcnt == 0) | 4217 | if (cmdcnt == 0) |