فهرست منبع

同步至 https://github.com/mirror/busybox/commit/a51d953b95a7cc6b40a6b3a5bfd95f3154acf5e2

Meco Man 4 سال پیش
والد
کامیت
d76dd05567
3فایلهای تغییر یافته به همراه274 افزوده شده و 122 حذف شده
  1. 268 117
      vi.c
  2. 1 1
      vi_utils.c
  3. 5 4
      vi_utils.h

+ 268 - 117
vi.c

@@ -146,6 +146,7 @@ struct globals {
 #define autoindent (0)
 #define expandtab  (0)
 #define err_method (0)
+#define ignorecase (0)
 #endif
 
 #if ENABLE_FEATURE_VI_READONLY
@@ -445,8 +446,7 @@ static void Hit_Return(void);
 static char *char_search(char *, const char *, int); // search for pattern starting at p
 #endif
 #if ENABLE_FEATURE_VI_COLON
-static char *get_one_address(char *, int *);    // get colon addr, if present
-static char *get_address(char *, int *, int *); // get two colon addrs, if present
+static char *get_address(char *p, int *b, int *e, unsigned int *got); // get colon addr, if present
 #endif
 static void colon(char *);  // execute the "colon" mode cmds
 #if ENABLE_FEATURE_VI_USE_SIGNALS
@@ -846,26 +846,38 @@ static void edit_file(char *fn)
 
 //----- 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++;
@@ -875,13 +887,16 @@ static char *get_one_address(char *p, int *result)  // get colon addr, if presen
                 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) {
@@ -900,39 +915,45 @@ static char *get_one_address(char *p, int *result)  // get colon addr, if presen
                 dir = ((unsigned)BACK << 1) | FULL;
             }
             q = char_search(q, last_search_pattern + 1, dir);
-            if (q == NULL)
-                return NULL;
-            new_addr = count_lines(text, q);
+            if (q == NULL) {
+                // no match, continue from other end of file
+                q = char_search(dir > 0 ? text : end - 1,
+                                last_search_pattern + 1, dir);
+                if (q == NULL) {
+                    status_line_bold("Pattern not found");
+                    return NULL;
+                }
+            }
+            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;
 }
 
@@ -941,34 +962,40 @@ static char *get_one_address(char *p, int *result)  // get colon addr, if presen
 
 // 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;
         }
     }
@@ -1052,6 +1079,75 @@ static char *expand_args(char *args)
 # endif
 #endif /* FEATURE_VI_COLON */
 
+#if ENABLE_FEATURE_VI_REGEX_SEARCH
+# define MAX_SUBPATTERN 10  // subpatterns \0 .. \9
+
+// Like strchr() but skipping backslash-escaped characters
+static char *strchr_backslash(const char *s, int c)
+{
+    while (*s) {
+        if (*s == c)
+            return (char *)s;
+        if (*s == '\\')
+            if (*++s == '\0')
+                break;
+        s++;
+    }
+    return NULL;
+}
+
+// If the return value is not NULL the caller should free R
+static char *regex_search(char *q, regex_t *preg, const char *Rorig,
+                size_t *len_F, size_t *len_R, char **R)
+{
+    regmatch_t regmatch[MAX_SUBPATTERN], *cur_match;
+    char *found = NULL;
+    const char *t;
+    char *r;
+
+    regmatch[0].rm_so = 0;
+    regmatch[0].rm_eo = end_line(q) - q;
+    if (regexec(preg, q, MAX_SUBPATTERN, regmatch, REG_STARTEND) != 0)
+        return found;
+
+    found = q + regmatch[0].rm_so;
+    *len_F = regmatch[0].rm_eo - regmatch[0].rm_so;
+    *R = NULL;
+
+ fill_result:
+    // first pass calculates len_R, second fills R
+    *len_R = 0;
+    for (t = Rorig, r = *R; *t; t++) {
+        size_t len = 1; // default is to copy one char from replace pattern
+        const char *from = t;
+        if (*t == '\\') {
+            from = ++t; // skip backslash
+            if (*t >= '0' && *t < '0' + MAX_SUBPATTERN) {
+                cur_match = regmatch + (*t - '0');
+                if (cur_match->rm_so >= 0) {
+                    len = cur_match->rm_eo - cur_match->rm_so;
+                    from = q + cur_match->rm_so;
+                }
+            }
+        }
+        *len_R += len;
+        if (*R) {
+            memcpy(r, from, len);
+            r += len;
+            /* *r = '\0'; - xzalloc did it */
+        }
+    }
+    if (*R == NULL) {
+        *R = vi_zalloc(*len_R + 1);
+        goto fill_result;
+    }
+
+    return found;
+}
+#else /* !ENABLE_FEATURE_VI_REGEX_SEARCH */
+# define strchr_backslash(s, c) strchr(s, c)
+#endif /* ENABLE_FEATURE_VI_REGEX_SEARCH */
+
 // buf must be no longer than MAX_INPUT_LEN!
 static void colon(char *buf)
 {
@@ -1111,9 +1207,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
@@ -1131,21 +1232,22 @@ static void colon(char *buf)
     // :!<cmd>  // run <cmd> then return
     //
 
-    if (!buf[0])
-        goto ret;
-    if (*buf == ':')
-        buf++;          // move past the ':'
+    while (*buf == ':')
+        buf++;          // move past leading colons
+    while (isblank(*buf))
+        buf++;          // move past leading blanks
+    if (!buf[0] || buf[0] == '"')
+        goto ret;       // ignore empty lines or those starting with '"'
 
     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;
     }
 
@@ -1168,13 +1270,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.
@@ -1183,6 +1289,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;
@@ -1213,12 +1323,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);
         }
@@ -1290,7 +1400,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);
         }
@@ -1373,7 +1483,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;
@@ -1455,6 +1565,14 @@ static void colon(char *buf)
         int subs = 0;   // number of substitutions
 #  if ENABLE_FEATURE_VI_VERBOSE_STATUS
         int last_line = 0, lines = 0;
+#  endif
+#  if ENABLE_FEATURE_VI_REGEX_SEARCH
+        regex_t preg;
+        int cflags;
+        char *Rorig;
+#   if ENABLE_FEATURE_VI_UNDO
+        int undo = 0;
+#   endif
 #  endif
 
         // F points to the "find" pattern
@@ -1462,51 +1580,98 @@ static void colon(char *buf)
         // replace the cmd line delimiters "/" with NULs
         c = buf[1]; // what is the delimiter
         F = buf + 2;    // start of "find"
-        R = strchr(F, c);   // middle delimiter
+        R = strchr_backslash(F, c);   // middle delimiter
         if (!R)
             goto colon_s_fail;
         len_F = R - F;
         *R++ = '\0';    // terminate "find"
-        flags = strchr(R, c);
+        flags = strchr_backslash(R, c);
         if (flags) {
             *flags++ = '\0';    // terminate "replace"
             gflag = *flags;
         }
-        len_R = strlen(R);
 
-        if (e < 0) {    // no addr given
+        if (len_F) {    // save "find" as last search pattern
+            vi_free(last_search_pattern);
+            last_search_pattern = vi_strdup(F - 1);
+            last_search_pattern[0] = '/';
+        } else if (last_search_pattern[1] == '\0') {
+            status_line_bold("No previous search");
+            goto ret;
+        } else {
+            F = last_search_pattern + 1;
+            len_F = strlen(F);
+        }
+
+        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;
         }
 
+#  if ENABLE_FEATURE_VI_REGEX_SEARCH
+        Rorig = R;
+        cflags = 0;
+        if (ignorecase)
+            cflags = REG_ICASE;
+        memset(&preg, 0, sizeof(preg));
+        if (regcomp(&preg, F, cflags) != 0) {
+            status_line(":s bad search pattern");
+            goto regex_search_end;
+        }
+#  else
+        len_R = strlen(R);
+#  endif
+
         for (i = b; i <= e; i++) {  // so, :20,23 s \0 find \0 replace \0
             char *ls = q;       // orig line start
             char *found;
  vc4:
+#  if ENABLE_FEATURE_VI_REGEX_SEARCH
+            found = regex_search(q, &preg, Rorig, &len_F, &len_R, &R);
+#  else
             found = char_search(q, F, (FORWARD << 1) | LIMITED);    // search cur line only for "find"
+#  endif
             if (found) {
                 uintptr_t bias;
                 // we found the "find" pattern - delete it
                 // For undo support, the first item should not be chained
-                text_hole_delete(found, found + len_F - 1,
-                            subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO);
-                // can't do this above, no undo => no third argument
-                subs++;
-#  if ENABLE_FEATURE_VI_VERBOSE_STATUS
-                if (last_line != i) {
-                    last_line = i;
-                    ++lines;
+                // This needs to be handled differently depending on
+                // whether or not regex support is enabled.
+#  if ENABLE_FEATURE_VI_REGEX_SEARCH
+#   define TEST_LEN_F len_F // len_F may be zero
+#   define TEST_UNDO1 undo++
+#   define TEST_UNDO2 undo++
+#  else
+#   define TEST_LEN_F 1     // len_F is never zero
+#   define TEST_UNDO1 subs
+#   define TEST_UNDO2 1
+#  endif
+                if (TEST_LEN_F) // match can be empty, no delete needed
+                    text_hole_delete(found, found + len_F - 1,
+                                TEST_UNDO1 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO);
+                if (len_R != 0) {   // insert the "replace" pattern, if required
+                    bias = string_insert(found, R,
+                                TEST_UNDO2 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO);
+                    found += bias;
+                    ls += bias;
+                    //q += bias; - recalculated anyway
                 }
+#  if ENABLE_FEATURE_VI_REGEX_SEARCH
+                vi_free(R);
+#  endif
+                if (TEST_LEN_F || len_R != 0) {
+                    dot = ls;
+                    subs++;
+#  if ENABLE_FEATURE_VI_VERBOSE_STATUS
+                    if (last_line != i) {
+                        last_line = i;
+                        ++lines;
+                    }
 #  endif
-                // insert the "replace" patern
-                bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
-                found += bias;
-                ls += bias;
-                dot = ls;
-                /*q += bias; - recalculated anyway */
+                }
                 // check for "global"  :s/foo/bar/g
                 if (gflag == 'g') {
                     if ((found + len_R) < end_line(ls)) {
@@ -1526,6 +1691,10 @@ static void colon(char *buf)
                 status_line("%d substitutions on %d lines", subs, lines);
 #  endif
         }
+#  if ENABLE_FEATURE_VI_REGEX_SEARCH
+ regex_search_end:
+        regfree(&preg);
+#  endif
 #endif /* FEATURE_VI_SEARCH */
     } else if (strncmp(cmd, "version", i) == 0) {  // show software version
         status_line(BB_VER " " BB_BT);
@@ -1609,7 +1778,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);
         }
@@ -2020,7 +2189,6 @@ static void new_screen(int ro, int co)
 }
 
 #if ENABLE_FEATURE_VI_SEARCH
-
 # if ENABLE_FEATURE_VI_REGEX_SEARCH
 
 // search for pattern starting at p
@@ -2029,16 +2197,16 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
     struct re_pattern_buffer preg;
     const char *err;
     char *q;
-    int i;
-    int size;
-    int range;
+    int i, size, range, start;
 
-    re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
+    re_syntax_options = RE_SYNTAX_POSIX_BASIC & (~RE_DOT_NEWLINE);
     if (ignorecase)
-        re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE;
+        re_syntax_options |= RE_ICASE;
 
     rt_memset(&preg, 0, sizeof(preg));
     err = re_compile_pattern(pat, strlen(pat), &preg);
+    preg.not_bol = p != text;
+    preg.not_eol = p != end - 1;
     if (err != NULL) {
         status_line_bold("bad search pattern '%s': %s", pat, err);
         return p;
@@ -2056,31 +2224,26 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
 
     // RANGE could be negative if we are searching backwards
     range = q - p;
-    q = p;
-    size = range;
     if (range < 0) {
-        size = -size;
-        q = p - size;
-        if (q < text)
-            q = text;
+        size = -range;
+        start = size;
+    } else {
+        size = range;
+        start = 0;
     }
+    q = p - start;
+    if (q < text)
+        q = text;
     // search for the compiled pattern, preg, in p[]
-    // range < 0: search backward
-    // range > 0: search forward
-    // 0 < start < size
+    // range < 0, start == size: search backward
+    // range > 0, start == 0: search forward
     // re_search() < 0: not found or error
     // re_search() >= 0: index of found pattern
     //           struct pattern   char     int   int    int    struct reg
     // re_search(*pattern_buffer, *string, size, start, range, *regs)
-    i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL);
+    i = re_search(&preg, q, size, start, range, /*struct re_registers*:*/ NULL);
     regfree(&preg);
-    if (i < 0)
-        return NULL;
-    if (dir_and_range > 0) // FORWARD?
-        p = p + i;
-    else
-        p = p - i;
-    return p;
+    return i < 0 ? NULL : q + i;
 }
 
 # else
@@ -2171,7 +2334,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
         cmdcnt = 0;
         end_cmd_q();    // stop adding to q
         last_status_cksum = 0;  // force status update
-        if ((p[-1] != '\n') && (dot > text)) {
+        if ((dot > text) && (p[-1] != '\n')) {
             p--;
         }
 #if ENABLE_FEATURE_VI_SETOPTS
@@ -2358,10 +2521,12 @@ static int find_range(char **start, char **stop, int cmd)
         // for non-change operations WS after NL is not part of word
         if (cmd != 'c' && dot != t && *dot != '\n')
             dot = t;
-    } else if (strchr("GHL+-jk'\r\n", c)) {
+    } else if (strchr("GHL+-gjk'\r\n", c)) {
         // these operate on whole lines
         buftype = WHOLE;
         do_cmd(c);      // execute movement cmd
+        if (cmd_error)
+            buftype = -1;
     } else if (c == ' ' || c == 'l') {
         // forward motion by character
         int tmpcnt = (cmdcnt ? cmdcnt : 1);
@@ -3436,21 +3601,14 @@ static void print_literal(char *buf, const char *s)
     char *d;
     unsigned char c;
 
-    buf[0] = '\0';
     if (!s[0])
         s = "(NULL)";
 
     d = buf;
     for (; *s; s++) {
-        int c_is_no_print;
-
         c = *s;
-        c_is_no_print = (c & 0x80) && !Isprint(c);
-        if (c_is_no_print) {
-            strcpy(d, ESC_NORM_TEXT);
-            d += sizeof(ESC_NORM_TEXT)-1;
-            c = '.';
-        }
+        if ((c & 0x80) && !Isprint(c))
+            c = '?';
         if (c < ' ' || c == 0x7f) {
             *d++ = '^';
             c |= '@'; /* 0x40 */
@@ -3459,14 +3617,6 @@ static void print_literal(char *buf, const char *s)
         }
         *d++ = c;
         *d = '\0';
-        if (c_is_no_print) {
-            strcpy(d, ESC_BOLD_TEXT);
-            d += sizeof(ESC_BOLD_TEXT)-1;
-        }
-        if (*s == '\n') {
-            *d++ = '$';
-            *d = '\0';
-        }
         if (d - buf > MAX_INPUT_LEN - 10) // paranoia
             break;
     }
@@ -4190,8 +4340,8 @@ static void do_cmd(int c)
 #endif
                     }
                 }
-            } else /* if (c == '>') */ {
-                // shift right -- add tab or tabstop spaces
+            } else if (/* c == '>' && */ p != end_line(p)) {
+                // shift right -- add tab or tabstop spaces on non-empty lines
                 char_insert(p, '\t', allow_undo);
             }
 #if ENABLE_FEATURE_VI_UNDO
@@ -4246,6 +4396,7 @@ static void do_cmd(int c)
             buf[1] = (c1 >= 0 ? c1 : '*');
             buf[2] = '\0';
             not_implemented(buf);
+            cmd_error = TRUE;
             break;
         }
         if (cmdcnt == 0)

+ 1 - 1
vi_utils.c

@@ -3,12 +3,12 @@
  */
 
 #include "vi_utils.h"
+#include <mem_sandbox.h>
 
 #define DBG_TAG "vi"
 #define DBG_LVL DBG_INFO
 #include <rtdbg.h>
 
-#include <rtconfig.h>
 #ifndef VI_SANDBOX_SIZE_KB
 #define VI_SANDBOX_SIZE_KB   20 /* KB */
 #endif

+ 5 - 4
vi_utils.h

@@ -6,7 +6,6 @@
 #define __VI_UTILS_H__
 
 #include <ctype.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stdint.h>
@@ -16,11 +15,12 @@
 #include <stddef.h>
 #include <string.h>
 #include <unistd.h>
-#include <dfs_posix.h>
-#include <dfs_poll.h>
+#include <poll.h>
 #include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
 
-#include <mem_sandbox.h>
+#include <rtthread.h>
 
 #define BB_VER "latest: 2021-06-17"
 #define BB_BT  "Busybox vi for RT-Thread"
@@ -372,6 +372,7 @@ void vi_free(void *ptr);
 void* vi_zalloc(size_t size);
 char *vi_strdup(const char *s);
 char *vi_strndup(const char *s, size_t n);
+
 int64_t read_key(int fd, char *buffer, int timeout);
 void *memrchr(const void* ptr, int ch, size_t pos);
 char* xasprintf(const char *format, ...);