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 <[email protected]>
---
 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
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to