aboutsummaryrefslogtreecommitdiff
path: root/editors/vi.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-07-17 10:35:24 +0100
committerRon Yorston <rmy@pobox.com>2021-07-17 10:35:24 +0100
commit36e9606c1d1edfbc9e4eba31b4c498dba361ac98 (patch)
tree492a436d5fba5a545e7155a115679e0669e9bed6 /editors/vi.c
parentdcca23dc4cf9b51a1c2576360fea190e9b71204e (diff)
parentdabbeeb79356eef78528acd55e1f143ae80372f7 (diff)
downloadbusybox-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.c159
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
2750static 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
2764static 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!
2745static void colon(char *buf) 2816static 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)