vi: fix incorrect memory access on brace matching. Closes 7256
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 15 Sep 2014 15:06:10 +0000 (17:06 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 15 Sep 2014 15:06:10 +0000 (17:06 +0200)
While at it, fix brace matching to actually show the match
(missed fflush was causing cursor positioning to be buffered);
shorten brace matching code; remove unused macro indirection
in indicate_error().

Custom linker script 'busybox_ldscript' found, using it
function                                             old     new   delta
indicate_error                                         -      61     +61
mysleep                                               43      56     +13
char_insert                                          483     486      +3
find_pair                                            167     124     -43
Indicate_Error                                        61       -     -61
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 2/1 up/down: 77/-104)           Total: -27 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
editors/vi.c

index 24a9f60a88ee6cc18534e53488bae5fe5b12aebe..70bdbab07b09c03bab33a00ccaece41ec05a0e00 100644 (file)
@@ -566,8 +566,7 @@ static void redraw(int);    // force a full screen refresh
 static char* format_line(char* /*, int*/);
 static void refresh(int);      // update the terminal from screen[]
 
-static void Indicate_Error(void);       // use flash or beep to indicate error
-#define indicate_error(c) Indicate_Error()
+static void indicate_error(void);       // use flash or beep to indicate error
 static void Hit_Return(void);
 
 #if ENABLE_FEATURE_VI_SEARCH
@@ -1840,11 +1839,11 @@ static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
 {
        if (p >= end && end > text) {
                p = end - 1;
-               indicate_error('1');
+               indicate_error();
        }
        if (p < text) {
                p = text;
-               indicate_error('2');
+               indicate_error();
        }
        return p;
 }
@@ -2023,16 +2022,9 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
                        p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED);  // shrink buffer 1 char
                }
        } else {
-#if ENABLE_FEATURE_VI_SETOPTS
                // insert a char into text[]
-               char *sp;               // "save p"
-#endif
-
                if (c == 13)
                        c = '\n';       // translate \r to \n
-#if ENABLE_FEATURE_VI_SETOPTS
-               sp = p;                 // remember addr of insert
-#endif
 #if ENABLE_FEATURE_VI_UNDO
 # if ENABLE_FEATURE_VI_UNDO_QUEUE
                if (c == '\n')
@@ -2056,8 +2048,8 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
 #endif /* ENABLE_FEATURE_VI_UNDO */
                p += 1 + stupid_insert(p, c);   // insert the char
 #if ENABLE_FEATURE_VI_SETOPTS
-               if (showmatch && strchr(")]}", *sp) != NULL) {
-                       showmatching(sp);
+               if (showmatch && strchr(")]}", c) != NULL) {
+                       showmatching(p - 1);
                }
                if (autoindent && c == '\n') {  // auto indent the new line
                        char *q;
@@ -2217,34 +2209,32 @@ static char *skip_thing(char *p, int linecnt, int dir, int type)
 }
 
 // find matching char of pair  ()  []  {}
+// will crash if c is not one of these
 static char *find_pair(char *p, const char c)
 {
-       char match, *q;
+       const char *braces = "()[]{}";
+       char match;
        int dir, level;
 
-       match = ')';
+       dir = strchr(braces, c) - braces;
+       dir ^= 1;
+       match = braces[dir];
+       dir = ((dir & 1) << 1) - 1; /* 1 for ([{, -1 for )\} */
+
+       // look for match, count levels of pairs  (( ))
        level = 1;
-       dir = 1;                        // assume forward
-       switch (c) {
-       case '(': match = ')'; break;
-       case '[': match = ']'; break;
-       case '{': match = '}'; break;
-       case ')': match = '('; dir = -1; break;
-       case ']': match = '['; dir = -1; break;
-       case '}': match = '{'; dir = -1; break;
-       }
-       for (q = p + dir; text <= q && q < end; q += dir) {
-               // look for match, count levels of pairs  (( ))
-               if (*q == c)
+       for (;;) {
+               p += dir;
+               if (p < text || p >= end)
+                       return NULL;
+               if (*p == c)
                        level++;        // increase pair levels
-               if (*q == match)
+               if (*p == match) {
                        level--;        // reduce pair level
-               if (level == 0)
-                       break;          // found matching pair
+                       if (level == 0)
+                               return p; // found matching pair
+               }
        }
-       if (level != 0)
-               q = NULL;               // indicate no match
-       return q;
 }
 
 #if ENABLE_FEATURE_VI_SETOPTS
@@ -2256,7 +2246,7 @@ static void showmatching(char *p)
        // we found half of a pair
        q = find_pair(p, *p);   // get loc of matching char
        if (q == NULL) {
-               indicate_error('3');    // no matching char
+               indicate_error();       // no matching char
        } else {
                // "q" now points to matching pair
                save_dot = dot; // remember where we are
@@ -2815,6 +2805,9 @@ static int mysleep(int hund)      // sleep for 'hund' 1/100 seconds or stdin ready
 {
        struct pollfd pfd[1];
 
+       if (hund != 0)
+               fflush_all();
+
        pfd[0].fd = STDIN_FILENO;
        pfd[0].events = POLLIN;
        return safe_poll(pfd, 1, hund*10) > 0;
@@ -3059,7 +3052,7 @@ static void flash(int h)
        redraw(TRUE);
 }
 
-static void Indicate_Error(void)
+static void indicate_error(void)
 {
 #if ENABLE_FEATURE_VI_CRASHME
        if (crashme > 0)
@@ -3602,7 +3595,7 @@ static void do_cmd(int c)
                break;
        case 27:                        // esc
                if (cmd_mode == 0)
-                       indicate_error(c);
+                       indicate_error();
                cmd_mode = 0;   // stop insrting
                undo_queue_commit();
                end_cmd_q();
@@ -3621,7 +3614,7 @@ static void do_cmd(int c)
                if ((unsigned)c1 <= 25) { // a-z?
                        YDreg = c1;
                } else {
-                       indicate_error(c);
+                       indicate_error();
                }
                break;
        case '\'':                      // '- goto a specific mark
@@ -3639,7 +3632,7 @@ static void do_cmd(int c)
                        dot_begin();    // go to B-o-l
                        dot_skip_over_ws();
                } else {
-                       indicate_error(c);
+                       indicate_error();
                }
                break;
        case 'm':                       // m- Mark a line
@@ -3652,7 +3645,7 @@ static void do_cmd(int c)
                        // remember the line
                        mark[c1] = dot;
                } else {
-                       indicate_error(c);
+                       indicate_error();
                }
                break;
        case 'P':                       // P- Put register before
@@ -3713,7 +3706,7 @@ static void do_cmd(int c)
                                // we found half of a pair
                                p = find_pair(q, *q);
                                if (p == NULL) {
-                                       indicate_error(c);
+                                       indicate_error();
                                } else {
                                        dot = p;
                                }
@@ -3721,7 +3714,7 @@ static void do_cmd(int c)
                        }
                }
                if (*q == '\n')
-                       indicate_error(c);
+                       indicate_error();
                break;
        case 'f':                       // f- forward to a user specified char
                last_forward_char = get_one_char();     // get the search char
@@ -4054,7 +4047,7 @@ static void do_cmd(int c)
                // ZZ means to save file (if necessary), then exit
                c1 = get_one_char();
                if (c1 != 'Z') {
-                       indicate_error(c);
+                       indicate_error();
                        break;
                }
                if (modified_count) {
@@ -4138,7 +4131,7 @@ static void do_cmd(int c)
                        // could not recognize object
                        c = c1 = 27;    // error-
                        ml = 0;
-                       indicate_error(c);
+                       indicate_error();
                }
                if (ml && whole) {
                        if (c == 'c') {