apps/system/vi/vi.c: Fix an infinite loop bug that can show up in some coases (plus a couple of other items).

This commit is contained in:
Ken Pettit 2019-01-22 08:51:32 -06:00 committed by Gregory Nutt
parent 6fb399fd65
commit 1a82551036

View File

@ -220,6 +220,7 @@ enum vi_cmdmode_key_e
KEY_CMDMODE_BEGINLINE = '0', /* Move cursor to start of current line */
KEY_CMDMODE_APPEND = 'a', /* Enter insertion mode after current character */
KEY_CMDMODE_WORDBACK = 'b', /* Scan to previous word */
KEY_CMDMODE_CHANGE = 'c', /* Delete text and enter insert mode */
KEY_CMDMODE_DEL_LINE = 'd', /* "dd" deletes a lines */
KEY_CMDMODE_FINDINLINE = 'f', /* Find within current line */
KEY_CMDMODE_GOTOTOP = 'g', /* Two of these sends cursor to the top */
@ -351,8 +352,9 @@ struct vi_s
uint8_t cmdlen; /* Length of the command in the scratch[] buffer */
bool modified; /* True: The file has modified */
bool error; /* True: There is an error message on the last line */
bool delarm; /* One more 'd' and the line(s) will be deleted */
bool yankarm; /* One more 'y' and the line(s) will be yanked */
bool delarm; /* Delete text arm flag */
bool chgarm; /* Change text arm flag */
bool yankarm; /* Yank text arm flag */
bool toparm; /* One more 'g' and the cursor moves to the top */
bool wqarm; /* One more 'Z' is the same as :wq */
bool fullredraw; /* True to draw all lines on screen */
@ -1453,6 +1455,7 @@ static void vi_setmode(FAR struct vi_s *vi, uint8_t mode, long value)
vi->delarm = false;
vi->yankarm = false;
vi->toparm = false;
vi->chgarm = false;
vi->wqarm = false;
vi->value = value;
vi->cmdlen = 0;
@ -1641,6 +1644,13 @@ static void vi_scrollcheck(FAR struct vi_s *vi)
int column;
int nlines;
/* Sanity test */
if (vi->curpos > vi->textsize)
{
vi->curpos = vi->textsize;
}
/* Get the text buffer offset to the beginning of the current line */
curline = vi_linebegin(vi, vi->curpos);
@ -2470,7 +2480,7 @@ static void vi_deltoeol(FAR struct vi_s *vi)
/* Yank and remove text from the buffer */
vi_yanktext(vi, start, end, true, true);
if (start != vi->textsize && vi->text[start] != '\n')
if (start > 0 && start != vi->textsize && vi->text[start - 1] != '\n')
{
vi->curpos = start-1;
}
@ -2589,6 +2599,7 @@ static void vi_yank(FAR struct vi_s *vi, bool del_after_yank)
off_t yank_end;
off_t textsize;
int pos_increment = 0;
bool empty_last_line = false;
/* Get the offset in the text buffer corresponding to the range of lines to
* be yanked
@ -2621,6 +2632,15 @@ static void vi_yank(FAR struct vi_s *vi, bool del_after_yank)
viinfo("start=%ld end=%ld\n", (long)start, (long)end);
/* Test if deleting last line with empty line above it */
if ((end > 0 && start == end && end == vi->textsize -1 &&
vi->text[end-1] == '\n') || (start > 1 && end + 1 ==
vi->textsize && vi->text[start-2] == '\n'))
{
empty_last_line = true;
}
vi_yanktext(vi, start, yank_end, 0, del_after_yank);
/* If the last line was yanked, then remove the '\n' on the
@ -2634,7 +2654,17 @@ static void vi_yank(FAR struct vi_s *vi, bool del_after_yank)
/* Place cursor at beginning of the line */
vi->curpos = vi_linebegin(vi, vi->curpos + pos_increment);
if (del_after_yank)
{
if (empty_last_line)
{
vi->curpos = vi->textsize;
}
else
{
vi->curpos = vi_linebegin(vi, vi->curpos + pos_increment);
}
}
}
/****************************************************************************
@ -2654,7 +2684,7 @@ static void vi_paste(FAR struct vi_s *vi, bool paste_before)
viinfo("curpos=%ld yankalloc=%d\n", (long)vi->curpos, (long)vi->yankalloc);
/* Make sure there is something to be yanked */
/* Make sure there is something to be pasted */
if (!vi->yank || vi->yanksize <= 0)
{
@ -2726,9 +2756,11 @@ static void vi_paste(FAR struct vi_s *vi, bool paste_before)
/* Test if pasting at end of file */
new_curpos = start;
if (start >= vi->textsize && vi->text[vi->textsize-1] != '\n')
if ((start >= vi->textsize && vi->text[vi->textsize-1] != '\n') ||
vi->curpos == vi->textsize)
{
off_t textsize = vi->textsize;
bool at_end = vi->curpos == vi->textsize;
vi->curpos = vi->textsize;
vi_insertch(vi, '\n');
@ -2737,7 +2769,7 @@ static void vi_paste(FAR struct vi_s *vi, bool paste_before)
/* Don't append the \n' in the yank buffer */
if (vi->text[textsize-1] != '\n')
if (vi->text[textsize-1] != '\n' || at_end)
{
size--;
}
@ -2748,7 +2780,7 @@ static void vi_paste(FAR struct vi_s *vi, bool paste_before)
else if (start >= vi->textsize)
{
start = vi->textsize;
new_curpos = start + size;
new_curpos = start;
vi->fullredraw = true;
}
@ -3031,9 +3063,9 @@ static void vi_gotonextword(FAR struct vi_s *vi)
vi->curpos = vi_findnextword(vi);
}
/* Test if yank or delete are armed */
/* Test if yank, delete or change are armed */
if (vi->yankarm || vi->delarm)
if (vi->yankarm || vi->delarm || vi->chgarm)
{
/* Rewind so we don't yank skipped whitespace */
@ -3083,14 +3115,25 @@ static void vi_gotonextword(FAR struct vi_s *vi)
{
/* Multi-line delete? */
vi->fullredraw = vi->delarm;
vi->fullredraw = vi->delarm || vi->chgarm;
}
/* Perform the yank */
end = vi->curpos + 1 == vi->textsize ? vi->curpos : vi->curpos - 1;
vi_yanktext(vi, start, end, 1, vi->delarm);
if (vi->delarm)
if (vi->chgarm)
{
end--;
}
/* Yank text if it isn't a single \n character */
if (!(start == end && vi->text[start] == '\n'))
{
vi_yanktext(vi, start, end, 1, vi->delarm | vi->chgarm);
}
if (vi->delarm | vi->chgarm)
{
/* Redraw line if text deleted */
@ -3104,14 +3147,22 @@ static void vi_gotonextword(FAR struct vi_s *vi)
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
/* Setup command repeat */
if (vi->delarm)
if (vi->delarm | vi->chgarm)
{
vi_saverepeat(vi, 'd');
vi_saverepeat(vi, vi->delarm ? 'd' : 'c');
vi_appendrepeat(vi, 'w');
}
#endif
/* If change text is armed, then enter insert mode */
if (vi->chgarm)
{
vi_setmode(vi, MODE_INSERT, 0);
}
vi->delarm = false;
vi->chgarm = false;
vi->yankarm = false;
}
}
@ -3506,6 +3557,13 @@ static void vi_cmd_mode(FAR struct vi_s *vi)
{
vi->yankarm = false;
}
/* Anything other than 'c' disarms line yanking */
if (ch != 'c')
{
vi->chgarm = false;
}
}
/* Anything other than'g' disarms goto top */
@ -3743,6 +3801,36 @@ static void vi_cmd_mode(FAR struct vi_s *vi)
}
break;
case KEY_CMDMODE_CHANGE:
{
if (vi->chgarm)
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
vi_appendrepeat(vi, ch);
#endif
vi_gotofirstnonwhite(vi);
vi_deltoeol(vi);
vi_setmode(vi, MODE_INSERT, 0);
if (vi->curpos == vi->textsize)
{
vi->curpos = vi_cursorright(vi, vi->curpos, 1) + 1;
}
else
{
vi->curpos = vi_cursorright(vi, vi->curpos, 1);
}
vi->chgarm = false;
}
else
{
vi->chgarm = true;
preserve = true;
}
}
break;
case KEY_CMDMODE_DELTOEOL: /* Delete to end of current line */
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
@ -4117,7 +4205,14 @@ static void vi_cmd_mode(FAR struct vi_s *vi)
case KEY_CMDMODE_MARK: /* Place a mark beginning at the current cursor position */
default:
{
VI_BEL(vi);
if (ch == -1)
{
continue;
}
else
{
VI_BEL(vi);
}
}
break;
}
@ -5186,12 +5281,12 @@ static void vi_findinline_mode(FAR struct vi_s *vi)
/* Test if yank or del armed */
if (vi->yankarm || vi->delarm)
if (vi->yankarm || vi->delarm || vi->chgarm)
{
/* Yank the text and possibly delete */
vi_yanktext(vi, vi->curpos, pos, 1, vi->delarm);
if (vi->delarm)
vi_yanktext(vi, vi->curpos, pos, 1, vi->delarm || vi->chgarm);
if (vi->delarm || vi->chgarm)
{
/* Redraw line if text deleted */
@ -5201,14 +5296,23 @@ static void vi_findinline_mode(FAR struct vi_s *vi)
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
/* Setup command repeat */
if (vi->delarm)
if (vi->delarm || vi->chgarm)
{
vi_saverepeat(vi, 'd');
vi_saverepeat(vi, vi->delarm ? 'd' : 'c');
vi_appendrepeat(vi, vi->tfind ? 't' : 'f');
vi_appendrepeat(vi, ch);
}
#endif
/* If change text is armed, then enter insert mode */
if (vi->chgarm)
{
vi->chgarm = false;
vi_setmode(vi, MODE_INSERT, 0);
return;
}
vi->delarm = false;
vi->yankarm = false;
}