[PATCH 1/1] vi: further changes to colon addresses

Denys Vlasenko vda.linux at googlemail.com
Sun Aug 29 18:19:29 UTC 2021


Applied, thank you.

On Sun, Aug 29, 2021 at 3:56 PM Ron Yorston <rmy at pobox.com> wrote:
>
> Improved error messages:
>
> - specify when a search fails or a mark isn't set;
> - warn when line addresses are out of range or when a range of
>   lines is reversed.
>
> Addresses are limited to the number of lines in the file so a
> command like ':2000000000' (go to the two billionth line) no
> longer causes a long pause.
>
> Improved vi compatibility of '+' and '-' operators that aren't
> followed immediately by a number:
>
>    :4+++=       7
>    :3-2=        1
>    :3 - 2=      4 (yes, really!)
>
> In a command like ':,$' the empty address before the separator now
> correctly refers to the current line.  (The similar case ':1,' was
> already being handled.)
>
> And all with a tidy reduction in bloat (32-bit build):
>
> function                                             old     new   delta
> colon                                               4029    4069     +40
> .rodata                                            99348   99253     -95
> ------------------------------------------------------------------------------
> (add/remove: 0/0 grow/shrink: 1/1 up/down: 40/-95)            Total: -55 bytes
>
> Signed-off-by: Ron Yorston <rmy at pobox.com>
> ---
>  editors/vi.c | 132 ++++++++++++++++++++++++++++++++-------------------
>  1 file changed, 83 insertions(+), 49 deletions(-)
>
> diff --git a/editors/vi.c b/editors/vi.c
> index e4ba2b2b0..3dbe5b471 100644
> --- a/editors/vi.c
> +++ b/editors/vi.c
> @@ -2458,26 +2458,38 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
>
>  //----- The Colon commands -------------------------------------
>  #if ENABLE_FEATURE_VI_COLON
> -static char *get_one_address(char *p, int *result)     // get colon addr, if present
> +// Evaluate colon address expression.  Returns a pointer to the
> +// next character or NULL on error.  If 'result' contains a valid
> +// address 'valid' is TRUE.
> +static char *get_one_address(char *p, int *result, int *valid)
>  {
> -       int st, num, sign, addr, new_addr;
> +       int num, sign, addr, got_addr;
>  # if ENABLE_FEATURE_VI_YANKMARK || ENABLE_FEATURE_VI_SEARCH
>         char *q, c;
>  # endif
>         IF_FEATURE_VI_SEARCH(int dir;)
>
> -       addr = -1;                      // assume no addr
> +       got_addr = FALSE;
> +       addr = count_lines(text, dot);  // default to current line
>         sign = 0;
>         for (;;) {
> -               new_addr = -1;
>                 if (isblank(*p)) {
> +                       if (got_addr) {
> +                               addr += sign;
> +                               sign = 0;
> +                       }
> +                       p++;
> +               } else if (!got_addr && *p == '.') {    // the current line
>                         p++;
> -               } else if (*p == '.') { // the current line
> +                       //addr = count_lines(text, dot);
> +                       got_addr = TRUE;
> +               } else if (!got_addr && *p == '$') {    // the last line in file
>                         p++;
> -                       new_addr = count_lines(text, dot);
> +                       addr = count_lines(text, end - 1);
> +                       got_addr = TRUE;
>                 }
>  # if ENABLE_FEATURE_VI_YANKMARK
> -               else if (*p == '\'') {  // is this a mark addr
> +               else if (!got_addr && *p == '\'') {     // is this a mark addr
>                         p++;
>                         c = tolower(*p);
>                         p++;
> @@ -2487,13 +2499,16 @@ static char *get_one_address(char *p, int *result)      // get colon addr, if present
>                                 c = c - 'a';
>                                 q = mark[(unsigned char) c];
>                         }
> -                       if (q == NULL)  // is mark valid
> +                       if (q == NULL) {        // is mark valid
> +                               status_line_bold("Mark not set");
>                                 return NULL;
> -                       new_addr = count_lines(text, q);
> +                       }
> +                       addr = count_lines(text, q);
> +                       got_addr = TRUE;
>                 }
>  # endif
>  # if ENABLE_FEATURE_VI_SEARCH
> -               else if (*p == '/' || *p == '?') {      // a search pattern
> +               else if (!got_addr && (*p == '/' || *p == '?')) {       // a search pattern
>                         c = *p;
>                         q = strchrnul(p + 1, c);
>                         if (p + 1 != q) {
> @@ -2516,40 +2531,41 @@ static char *get_one_address(char *p, int *result)      // get colon addr, if present
>                                 // no match, continue from other end of file
>                                 q = char_search(dir > 0 ? text : end - 1,
>                                                                 last_search_pattern + 1, dir);
> -                               if (q == NULL)
> +                               if (q == NULL) {
> +                                       status_line_bold("Pattern not found");
>                                         return NULL;
> +                               }
>                         }
> -                       new_addr = count_lines(text, q);
> +                       addr = count_lines(text, q);
> +                       got_addr = TRUE;
>                 }
>  # endif
> -               else if (*p == '$') {   // the last line in file
> -                       p++;
> -                       new_addr = count_lines(text, end - 1);
> -               } else if (isdigit(*p)) {
> -                       sscanf(p, "%d%n", &num, &st);
> -                       p += st;
> -                       if (addr < 0) { // specific line number
> +               else if (isdigit(*p)) {
> +                       num = 0;
> +                       while (isdigit(*p))
> +                               num = num * 10 + *p++ -'0';
> +                       if (!got_addr) {        // specific line number
>                                 addr = num;
> +                               got_addr = TRUE;
>                         } else {        // offset from current addr
>                                 addr += sign >= 0 ? num : -num;
>                         }
>                         sign = 0;
>                 } else if (*p == '-' || *p == '+') {
> -                       sign = *p++ == '-' ? -1 : 1;
> -                       if (addr < 0) { // default address is dot
> -                               addr = count_lines(text, dot);
> +                       if (!got_addr) {        // default address is dot
> +                               //addr = count_lines(text, dot);
> +                               got_addr = TRUE;
> +                       } else {
> +                               addr += sign;
>                         }
> +                       sign = *p++ == '-' ? -1 : 1;
>                 } else {
>                         addr += sign;   // consume unused trailing sign
>                         break;
>                 }
> -               if (new_addr >= 0) {
> -                       if (addr >= 0)  // only one new address per expression
> -                               return NULL;
> -                       addr = new_addr;
> -               }
>         }
>         *result = addr;
> +       *valid = got_addr;
>         return p;
>  }
>
> @@ -2558,34 +2574,40 @@ static char *get_one_address(char *p, int *result)      // get colon addr, if present
>
>  // Read line addresses for a colon command.  The user can enter as
>  // many as they like but only the last two will be used.
> -static char *get_address(char *p, int *b, int *e)
> +static char *get_address(char *p, int *b, int *e, unsigned int *got)
>  {
>         int state = GET_ADDRESS;
> +       int valid;
> +       int addr;
>         char *save_dot = dot;
>
>         //----- get the address' i.e., 1,3   'a,'b  -----
>         for (;;) {
>                 if (isblank(*p)) {
>                         p++;
> -               } else if (*p == '%' && state == GET_ADDRESS) { // alias for 1,$
> +               } else if (state == GET_ADDRESS && *p == '%') { // alias for 1,$
>                         p++;
>                         *b = 1;
>                         *e = count_lines(text, end-1);
> +                       *got = 3;
> +                       state = GET_SEPARATOR;
> +               } else if (state == GET_ADDRESS) {
> +                       valid = FALSE;
> +                       p = get_one_address(p, &addr, &valid);
> +                       // Quit on error or if the address is invalid and isn't of
> +                       // the form ',$' or '1,' (in which case it defaults to dot).
> +                       if (p == NULL || !(valid || *p == ',' || *p == ';' || *got & 1))
> +                               break;
> +                       *b = *e;
> +                       *e = addr;
> +                       *got = (*got << 1) | 1;
>                         state = GET_SEPARATOR;
>                 } else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
>                         if (*p == ';')
>                                 dot = find_line(*e);
>                         p++;
> -                       *b = *e;
>                         state = GET_ADDRESS;
> -               } else if (state == GET_ADDRESS) {
> -                       p = get_one_address(p, e);
> -                       if (p == NULL)
> -                               break;
> -                       state = GET_SEPARATOR;
>                 } else {
> -                       if (state == GET_SEPARATOR && *b >= 0 && *e < 0)
> -                               *e = count_lines(text, dot);
>                         break;
>                 }
>         }
> @@ -2799,9 +2821,14 @@ static void colon(char *buf)
>         not_implemented(p);
>  #else
>
> +// check how many addresses we got
> +# define GOT_ADDRESS (got & 1)
> +# define GOT_RANGE ((got & 3) == 3)
> +
>         char c, *buf1, *q, *r;
>         char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
>         int i, l, li, b, e;
> +       unsigned int got;
>         int useforce;
>
>         // :3154        // if (-e line 3154) goto it  else stay put
> @@ -2828,14 +2855,13 @@ static void colon(char *buf)
>
>         li = i = 0;
>         b = e = -1;
> +       got = 0;
>         li = count_lines(text, end - 1);
>         fn = current_filename;
>
>         // look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
> -       buf1 = buf;
> -       buf = get_address(buf, &b, &e);
> +       buf = get_address(buf, &b, &e, &got);
>         if (buf == NULL) {
> -               status_line_bold("Bad address: %s", buf1);
>                 goto ret;
>         }
>
> @@ -2858,13 +2884,17 @@ static void colon(char *buf)
>         }
>         // assume the command will want a range, certain commands
>         // (read, substitute) need to adjust these assumptions
> -       if (e < 0) {
> +       if (!GOT_ADDRESS) {
>                 q = text;                       // no addr, use 1,$ for the range
>                 r = end - 1;
>         } else {
>                 // at least one addr was given, get its details
> +               if (e < 0 || e > li) {
> +                       status_line_bold("Invalid range");
> +                       goto ret;
> +               }
>                 q = r = find_line(e);
> -               if (b < 0) {
> +               if (!GOT_RANGE) {
>                         // if there is only one addr, then it's the line
>                         // number of the single line the user wants.
>                         // Reset the end pointer to the end of that line.
> @@ -2873,6 +2903,10 @@ static void colon(char *buf)
>                 } else {
>                         // we were given two addrs.  change the
>                         // start pointer to the addr given by user.
> +                       if (b < 0 || b > li || b > e) {
> +                               status_line_bold("Invalid range");
> +                               goto ret;
> +                       }
>                         q = find_line(b);       // what line is #b
>                         r = end_line(r);
>                         li = e - b + 1;
> @@ -2903,12 +2937,12 @@ static void colon(char *buf)
>         }
>  # endif
>         else if (cmd[0] == '=' && !cmd[1]) {    // where is the address
> -               if (e < 0) {    // no addr given- use defaults
> +               if (!GOT_ADDRESS) {     // no addr given- use defaults
>                         e = count_lines(text, dot);
>                 }
>                 status_line("%d", e);
>         } else if (strncmp(cmd, "delete", i) == 0) {    // delete lines
> -               if (e < 0) {    // no addr given- use defaults
> +               if (!GOT_ADDRESS) {     // no addr given- use defaults
>                         q = begin_line(dot);    // assume .,. for the range
>                         r = end_line(dot);
>                 }
> @@ -2980,7 +3014,7 @@ static void colon(char *buf)
>                 rawmode();
>                 Hit_Return();
>         } else if (strncmp(cmd, "list", i) == 0) {      // literal print line
> -               if (e < 0) {    // no addr given- use defaults
> +               if (!GOT_ADDRESS) {     // no addr given- use defaults
>                         q = begin_line(dot);    // assume .,. for the range
>                         r = end_line(dot);
>                 }
> @@ -3063,7 +3097,7 @@ static void colon(char *buf)
>                 if (e == 0) {   // user said ":0r foo"
>                         q = text;
>                 } else {        // read after given line or current line if none given
> -                       q = next_line(e > 0 ? find_line(e) : dot);
> +                       q = next_line(GOT_ADDRESS ? find_line(e) : dot);
>                         // read after last line
>                         if (q == end-1)
>                                 ++q;
> @@ -3184,11 +3218,11 @@ static void colon(char *buf)
>                         len_F = strlen(F);
>                 }
>
> -               if (e < 0) {    // no addr given
> +               if (!GOT_ADDRESS) {     // no addr given
>                         q = begin_line(dot);      // start with cur line
>                         r = end_line(dot);
>                         b = e = count_lines(text, q); // cur line number
> -               } else if (b < 0) {     // one addr given
> +               } else if (!GOT_RANGE) {        // one addr given
>                         b = e;
>                 }
>
> @@ -3359,7 +3393,7 @@ static void colon(char *buf)
>                 }
>  # if ENABLE_FEATURE_VI_YANKMARK
>         } else if (strncmp(cmd, "yank", i) == 0) {      // yank lines
> -               if (b < 0) {    // no addr given- use defaults
> +               if (!GOT_ADDRESS) {     // no addr given- use defaults
>                         q = begin_line(dot);    // assume .,. for the range
>                         r = end_line(dot);
>                 }
> --
> 2.31.1
>
> _______________________________________________
> busybox mailing list
> busybox at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox


More information about the busybox mailing list