Compare commits

...

16 Commits

Author SHA1 Message Date
Bram Moolenaar
e6640ad44e patch 8.0.1421: accessing invalid memory with overlong byte sequence
Problem:    Accessing invalid memory with overlong byte sequence.
Solution:   Check for NUL character. (test by Dominique Pelle, closes #2485)
2017-12-22 21:06:56 +01:00
Bram Moolenaar
3c09722600 patch 8.0.1420: accessing freed memory in vimgrep
Problem:    Accessing freed memory in vimgrep.
Solution:   Check that the quickfix list is still valid. (Yegappan Lakshmanan,
            closes #2474)
2017-12-21 20:54:49 +01:00
Bram Moolenaar
b73fa629d6 patch 8.0.1419: cursor column is not updated after ]s
Problem:    Cursor column is not updated after ]s. (Gary Johnson)
Solution:   Set the curswant flag.
2017-12-21 20:27:47 +01:00
Bram Moolenaar
ae6f865125 patch 8.0.1418: no test for expanding backticks
Problem:    No test for expanding backticks.
Solution:   Add a test. (Dominique Pelle, closes #2479)
2017-12-20 22:32:20 +01:00
Bram Moolenaar
1bd999f982 patch 8.0.1417: test doesn't search for a sentence
Problem:    Test doesn't search for a sentence. Still fails when searching for
            start of sentence. (Dominique Pelle)
Solution:   Add paren. Check for MAXCOL in dec().
2017-12-19 22:25:40 +01:00
Bram Moolenaar
8ada6aa929 patch 8.0.1416: crash when searching for a sentence
Problem:    Crash when searching for a sentence.
Solution:   Return NUL when getting character at MAXCOL. (closes #2468)
2017-12-19 21:23:21 +01:00
Bram Moolenaar
4ce46c2a6b patch 8.0.1415: warning for unused function without timers feature
Problem:    Warning for unused function without timers feature.
Solution:   Add #ifdef. (John Marriott)
2017-12-19 19:42:41 +01:00
Bram Moolenaar
14a4deb064 patch 8.0.1414: accessing freed memory in :lfile.
Problem:    Accessing freed memory in :lfile.
Solution:   Get the current window after executing autocommands. (Yegappan
            Lakshmanan, closes #2473)
2017-12-19 16:48:55 +01:00
Bram Moolenaar
aaf6e43b7a patch 8.0.1413: accessing freed memory in :cbuffer
Problem:    Accessing freed memory in :cbuffer.
Solution:   Get quickfix list after executing autocmds. (closes #2470)
2017-12-19 16:41:14 +01:00
Bram Moolenaar
1223744849 patch 8.0.1412: using free memory using setloclist()
Problem:    Using free memory using setloclist(). (Dominique Pelle)
Solution:   Mark location list context as still in use when needed. (Yegappan
            Lakshmanan, closes #2462)
2017-12-19 12:38:52 +01:00
Bram Moolenaar
2efb323e87 patch 8.0.1411: reading invalid memory with CTRL-W :
Problem:    Reading invalid memory with CTRL-W :.
Solution:   Correct the command characters. (closes #2469)
2017-12-19 12:27:23 +01:00
Bram Moolenaar
338e47fdfd patch 8.0.1410: hang when using count() with an empty string
Problem:    Hang when using count() with an empty string.
Solution:   Return zero for an empty string. (Dominique Pelle, closes #2465)
2017-12-19 11:55:26 +01:00
Bram Moolenaar
132f75255e patch 8.0.1409: buffer overflow in :tags command
Problem:    Buffer overflow in :tags command.
Solution:   Use vim_snprintf(). (Dominique Pelle, closes #2471, closes #2475)
            Add a test.
2017-12-19 10:49:34 +01:00
Bram Moolenaar
a0ca7d002d patch 8.0.1408: crash in setqflist()
Problem:    Crash in setqflist().
Solution:   Check for string to be NULL. (Dominique Pelle, closes #2464)
2017-12-19 10:22:19 +01:00
Bram Moolenaar
4af031dbc8 patch 8.0.1407: GUI: CursorHold may trigger before 'updatetime'
Problem:    GUI: CursorHold may trigger before 'updatetime' when using timers.
Solution:   Check that 'updatetime' has passed.
2017-12-19 10:02:43 +01:00
Bram Moolenaar
b254af312d patch 8.0.1406: difficult to track changes to a quickfix list
Problem:    Difficult to track changes to a quickfix list.
Solution:   Add a "changedtick" value. (Yegappan Lakshmanan, closes #2460)
2017-12-18 19:48:58 +01:00
19 changed files with 516 additions and 70 deletions

View File

@@ -1,4 +1,4 @@
*eval.txt* For Vim version 8.0. Last change: 2017 Dec 16
*eval.txt* For Vim version 8.0. Last change: 2017 Dec 19
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -3338,8 +3338,8 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()*
When {ic} is given and it's |TRUE| then case is ignored.
When {comp} is a string then the number of not overlapping
occurrences of {expr} is returned.
occurrences of {expr} is returned. Zero is returned when
{expr} is an empty string.
*cscope_connection()*
cscope_connection([{num} , {dbpath} [, {prepend}]])
@@ -4674,6 +4674,8 @@ getqflist([{what}]) *getqflist()*
If the optional {what} dictionary argument is supplied, then
returns only the items listed in {what} as a dictionary. The
following string items are supported in {what}:
changedtick get the total number of changes made
to the list
context get the context stored with |setqflist()|
efm errorformat to use when parsing "lines". If
not present, then the 'errorformat' option
@@ -4707,6 +4709,8 @@ getqflist([{what}]) *getqflist()*
"items" with the list of entries.
The returned dictionary contains the following entries:
changedtick total number of changes made to the
list |quickfix-changedtick|
context context information stored with |setqflist()|.
If not present, set to "".
id quickfix list ID |quickfix-ID|. If not

View File

@@ -64,6 +64,14 @@ When a window with a location list is split, the new window gets a copy of the
location list. When there are no longer any references to a location list,
the location list is destroyed.
*quickfix-changedtick*
Every quickfix and location list has a read-only changedtick variable that
tracks the total number of changes made to the list. Every time the quickfix
list is modified, this count is incremented. This can be used to perform an
action only when the list has changed. The getqflist() and getloclist()
functions can be used to query the current value of changedtick. You cannot
change the changedtick variable.
The following quickfix commands can be used. The location list commands are
similar to the quickfix commands, replacing the 'c' prefix in the quickfix
command with 'l'.

View File

@@ -2382,7 +2382,7 @@ f_count(typval_T *argvars, typval_T *rettv)
char_u *p = argvars[0].vval.v_string;
char_u *next;
if (!error && expr != NULL && p != NULL)
if (!error && expr != NULL && *expr != NUL && p != NULL)
{
if (ic)
{
@@ -11173,7 +11173,10 @@ f_spellbadword(typval_T *argvars UNUSED, typval_T *rettv)
/* Find the start and length of the badly spelled word. */
len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr);
if (len != 0)
{
word = ml_get_cursor();
curwin->w_set_curswant = TRUE;
}
}
else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL)
{

View File

@@ -4521,13 +4521,14 @@ get_address(
if (lnum != MAXLNUM)
curwin->w_cursor.lnum = lnum;
/*
* Start a forward search at the end of the line.
* Start a forward search at the end of the line (unless
* before the first line).
* Start a backward search at the start of the line.
* This makes sure we never match in the current
* line, and can match anywhere in the
* next/previous line.
*/
if (c == '/')
if (c == '/' && curwin->w_cursor.lnum > 0)
curwin->w_cursor.col = MAXCOL;
else
curwin->w_cursor.col = 0;

View File

@@ -2885,6 +2885,7 @@ gui_insert_lines(int row, int count)
}
}
#ifdef FEAT_TIMERS
/*
* Passed to ui_wait_for_chars_or_timer(), ignoring extra arguments.
*/
@@ -2896,6 +2897,7 @@ gui_wait_for_chars_3(
{
return gui_mch_wait_for_chars(wtime);
}
#endif
/*
* Returns OK if a character was found to be available within the given time,
@@ -2923,6 +2925,9 @@ gui_wait_for_chars_or_timer(long wtime)
gui_wait_for_chars(long wtime, int tb_change_cnt)
{
int retval;
#if defined(ELAPSED_FUNC) && defined(FEAT_AUTOCMD)
ELAPSED_TYPE start_tv;
#endif
#ifdef FEAT_MENU
/*
@@ -2952,6 +2957,10 @@ gui_wait_for_chars(long wtime, int tb_change_cnt)
return retval;
}
#if defined(ELAPSED_FUNC) && defined(FEAT_AUTOCMD)
ELAPSED_INIT(start_tv);
#endif
/*
* While we are waiting indefinitely for a character, blink the cursor.
*/
@@ -2966,7 +2975,11 @@ gui_wait_for_chars(long wtime, int tb_change_cnt)
if (gui_wait_for_chars_or_timer(p_ut) == OK)
retval = OK;
#ifdef FEAT_AUTOCMD
else if (trigger_cursorhold() && typebuf.tb_change_cnt == tb_change_cnt)
else if (trigger_cursorhold()
# ifdef ELAPSED_FUNC
&& ELAPSED_FUNC(start_tv) >= p_ut
# endif
&& typebuf.tb_change_cnt == tb_change_cnt)
{
char_u buf[3];

View File

@@ -2650,8 +2650,12 @@ del_lines(
int
gchar_pos(pos_T *pos)
{
char_u *ptr = ml_get_pos(pos);
char_u *ptr;
/* When searching columns is sometimes put at the end of a line. */
if (pos->col == MAXCOL)
return NUL;
ptr = ml_get_pos(pos);
#ifdef FEAT_MBYTE
if (has_mbyte)
return (*mb_ptr2char)(ptr);

View File

@@ -348,24 +348,29 @@ inc_cursor(void)
int
inc(pos_T *lp)
{
char_u *p = ml_get_pos(lp);
char_u *p;
if (*p != NUL) /* still within line, move to next char (may be NUL) */
/* when searching position may be set to end of a line */
if (lp->col != MAXCOL)
{
#ifdef FEAT_MBYTE
if (has_mbyte)
p = ml_get_pos(lp);
if (*p != NUL) /* still within line, move to next char (may be NUL) */
{
int l = (*mb_ptr2len)(p);
#ifdef FEAT_MBYTE
if (has_mbyte)
{
int l = (*mb_ptr2len)(p);
lp->col += l;
return ((p[l] != NUL) ? 0 : 2);
}
lp->col += l;
return ((p[l] != NUL) ? 0 : 2);
}
#endif
lp->col++;
lp->col++;
#ifdef FEAT_VIRTUALEDIT
lp->coladd = 0;
lp->coladd = 0;
#endif
return ((p[1] != NUL) ? 0 : 2);
return ((p[1] != NUL) ? 0 : 2);
}
}
if (lp->lnum != curbuf->b_ml.ml_line_count) /* there is a next line */
{
@@ -412,8 +417,21 @@ dec(pos_T *lp)
#ifdef FEAT_VIRTUALEDIT
lp->coladd = 0;
#endif
if (lp->col > 0) /* still within line */
if (lp->col == MAXCOL)
{
/* past end of line */
p = ml_get(lp->lnum);
lp->col = (colnr_T)STRLEN(p);
#ifdef FEAT_MBYTE
if (has_mbyte)
lp->col -= (*mb_head_off)(p, p + lp->col);
#endif
return 0;
}
if (lp->col > 0)
{
/* still within line */
lp->col--;
#ifdef FEAT_MBYTE
if (has_mbyte)
@@ -424,8 +442,10 @@ dec(pos_T *lp)
#endif
return 0;
}
if (lp->lnum > 1) /* there is a prior line */
if (lp->lnum > 1)
{
/* there is a prior line */
lp->lnum--;
p = ml_get(lp->lnum);
lp->col = (colnr_T)STRLEN(p);
@@ -435,7 +455,9 @@ dec(pos_T *lp)
#endif
return 1;
}
return -1; /* at start of file */
/* at start of file */
return -1;
}
/*
@@ -1600,11 +1622,17 @@ strup_save(char_u *orig)
char_u *s;
c = utf_ptr2char(p);
l = utf_ptr2len(p);
if (c == 0)
{
/* overlong sequence, use only the first byte */
c = *p;
l = 1;
}
uc = utf_toupper(c);
/* Reallocate string when byte count changes. This is rare,
* thus it's OK to do another malloc()/free(). */
l = utf_ptr2len(p);
newl = utf_char2len(uc);
if (newl != l)
{
@@ -1663,11 +1691,17 @@ strlow_save(char_u *orig)
char_u *s;
c = utf_ptr2char(p);
l = utf_ptr2len(p);
if (c == 0)
{
/* overlong sequence, use only the first byte */
c = *p;
l = 1;
}
lc = utf_tolower(c);
/* Reallocate string when byte count changes. This is rare,
* thus it's OK to do another malloc()/free(). */
l = utf_ptr2len(p);
newl = utf_char2len(lc);
if (newl != l)
{

View File

@@ -6814,6 +6814,8 @@ nv_brackets(cmdarg_T *cap)
clearopbeep(cap->oap);
break;
}
else
curwin->w_set_curswant = TRUE;
# ifdef FEAT_FOLDING
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
foldOpenCursor();
@@ -7850,8 +7852,12 @@ n_start_visual_mode(int c)
nv_window(cmdarg_T *cap)
{
if (cap->nchar == ':')
{
/* "CTRL-W :" is the same as typing ":"; useful in a terminal window */
cap->cmdchar = ':';
cap->nchar = NUL;
nv_colon(cap);
}
else if (!checkclearop(cap->oap))
do_window(cap->nchar, cap->count0, NUL); /* everything is in window.c */
}

View File

@@ -183,8 +183,15 @@ get_op_type(int char1, int char2)
if (char1 == 'g' && char2 == Ctrl_X) /* subtract */
return OP_NR_SUB;
for (i = 0; ; ++i)
{
if (opchars[i][0] == char1 && opchars[i][1] == char2)
break;
if (i == (int)(sizeof(opchars) / sizeof(char [3]) - 1))
{
internal_error("get_op_type()");
break;
}
}
return i;
}

View File

@@ -76,6 +76,7 @@ typedef struct qf_list_S
int qf_multiline;
int qf_multiignore;
int qf_multiscan;
long qf_changedtick;
} qf_list_T;
/*
@@ -143,6 +144,7 @@ static int qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *, char_u *);
static char_u *qf_push_dir(char_u *, struct dir_stack_T **, int is_file_stack);
static char_u *qf_pop_dir(struct dir_stack_T **);
static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *);
static int qflist_valid(win_T *wp, int_u qf_id);
static void qf_fmt_text(char_u *text, char_u *buf, int bufsize);
static void qf_clean_dir_stack(struct dir_stack_T **);
static int qf_win_pos_update(qf_info_T *qi, int old_qf_index);
@@ -176,6 +178,9 @@ static qf_info_T *ll_get_or_alloc_list(win_T *);
static char_u *qf_last_bufname = NULL;
static bufref_T qf_last_bufref = {NULL, 0, 0};
static char *e_loc_list_changed =
N_("E926: Current location list was changed");
/*
* Read the errorfile "efile" into memory, line by line, building the error
* list. Set the error list's title to qf_title.
@@ -1668,6 +1673,7 @@ copy_loclist(win_T *from, win_T *to)
/* Assign a new ID for the location list */
to_qfl->qf_id = ++last_qf_id;
to_qfl->qf_changedtick = 0L;
/* When no valid entries are present in the list, qf_ptr points to
* the first item in the list */
@@ -1925,6 +1931,29 @@ qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *filename)
return ds_ptr==NULL? NULL: ds_ptr->dirname;
}
/*
* Returns TRUE if a quickfix/location list with the given identifier exists.
*/
static int
qflist_valid (win_T *wp, int_u qf_id)
{
qf_info_T *qi = &ql_info;
int i;
if (wp != NULL)
{
qi = GET_LOC_LIST(wp); /* Location list */
if (qi == NULL)
return FALSE;
}
for (i = 0; i < qi->qf_listcount; ++i)
if (qi->qf_lists[i].qf_id == qf_id)
return TRUE;
return FALSE;
}
/*
* When loading a file from the quickfix, the auto commands may modify it.
* This may invalidate the current quickfix entry. This function checks
@@ -2341,14 +2370,28 @@ qf_jump_edit_buffer(
else
{
int old_qf_curlist = qi->qf_curlist;
int save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
retval = buflist_getfile(qf_ptr->qf_fnum,
(linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit);
if (qi != &ql_info && !win_valid_any_tab(oldwin))
if (qi != &ql_info)
{
EMSG(_("E924: Current window was closed"));
*abort = TRUE;
*opened_window = FALSE;
/*
* Location list. Check whether the associated window is still
* present and the list is still valid.
*/
if (!win_valid_any_tab(oldwin))
{
EMSG(_("E924: Current window was closed"));
*abort = TRUE;
*opened_window = FALSE;
}
else if (!qflist_valid(oldwin, save_qfid))
{
EMSG(_(e_loc_list_changed));
*abort = TRUE;
}
}
else if (old_qf_curlist != qi->qf_curlist
|| !is_qf_entry_present(qi, qf_ptr))
@@ -2356,7 +2399,7 @@ qf_jump_edit_buffer(
if (qi == &ql_info)
EMSG(_("E925: Current quickfix was changed"));
else
EMSG(_("E926: Current location list was changed"));
EMSG(_(e_loc_list_changed));
*abort = TRUE;
}
@@ -2965,6 +3008,7 @@ qf_free(qf_info_T *qi, int idx)
free_tv(qfl->qf_ctx);
qfl->qf_ctx = NULL;
qfl->qf_id = 0;
qfl->qf_changedtick = 0L;
}
/*
@@ -3604,6 +3648,12 @@ qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
KeyTyped = old_KeyTyped;
}
static void
qf_list_changed(qf_info_T *qi, int qf_idx)
{
qi->qf_lists[qf_idx].qf_changedtick++;
}
/*
* Return TRUE when using ":vimgrep" for ":grep".
*/
@@ -3713,6 +3763,8 @@ ex_make(exarg_T *eap)
*eap->cmdlinep, enc);
if (wp != NULL)
qi = GET_LOC_LIST(wp);
if (res >= 0 && qi != NULL)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD
if (au_name != NULL)
{
@@ -4054,13 +4106,10 @@ ex_cfile(exarg_T *eap)
qf_info_T *qi = &ql_info;
#ifdef FEAT_AUTOCMD
char_u *au_name = NULL;
int save_qfid;
#endif
int res;
if (eap->cmdidx == CMD_lfile || eap->cmdidx == CMD_lgetfile
|| eap->cmdidx == CMD_laddfile)
wp = curwin;
#ifdef FEAT_AUTOCMD
switch (eap->cmdidx)
{
@@ -4093,6 +4142,11 @@ ex_cfile(exarg_T *eap)
if (*eap->arg != NUL)
set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0);
if (eap->cmdidx == CMD_lfile
|| eap->cmdidx == CMD_lgetfile
|| eap->cmdidx == CMD_laddfile)
wp = curwin;
/*
* This function is used by the :cfile, :cgetfile and :caddfile
* commands.
@@ -4105,14 +4159,23 @@ ex_cfile(exarg_T *eap)
*/
res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
&& eap->cmdidx != CMD_laddfile), *eap->cmdlinep, enc);
if (wp != NULL)
qi = GET_LOC_LIST(wp);
if (res >= 0 && qi != NULL)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD
save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
/*
* Autocmd might have freed the quickfix/location list. Check whether it is
* still valid
*/
if (!qflist_valid(wp, save_qfid))
return;
#endif
if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile))
{
if (wp != NULL)
qi = GET_LOC_LIST(wp);
qf_jump(qi, 0, 0, eap->forceit); /* display first error */
}
}
@@ -4135,8 +4198,11 @@ ex_vimgrep(exarg_T *eap)
char_u *p;
int fi;
qf_info_T *qi = &ql_info;
int loclist_cmd = FALSE;
#ifdef FEAT_AUTOCMD
int_u save_qfid;
qfline_T *cur_qf_start;
win_T *wp;
#endif
long lnum;
buf_T *buf;
@@ -4190,6 +4256,7 @@ ex_vimgrep(exarg_T *eap)
qi = ll_get_or_alloc_list(curwin);
if (qi == NULL)
return;
loclist_cmd = TRUE;
}
if (eap->addr_count > 0)
@@ -4260,8 +4327,9 @@ ex_vimgrep(exarg_T *eap)
mch_dirname(dirname_start, MAXPATHL);
#ifdef FEAT_AUTOCMD
/* Remember the value of qf_start, so that we can check for autocommands
* changing the current quickfix list. */
/* Remember the current values of the quickfix list and qf_start, so that
* we can check for autocommands changing the current quickfix list. */
save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
#endif
@@ -4321,6 +4389,18 @@ ex_vimgrep(exarg_T *eap)
using_dummy = FALSE;
#ifdef FEAT_AUTOCMD
if (loclist_cmd)
{
/*
* Verify that the location list is still valid. An autocmd might
* have freed the location list.
*/
if (!qflist_valid(curwin, save_qfid))
{
EMSG(_(e_loc_list_changed));
goto theend;
}
}
if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start)
{
int idx;
@@ -4469,6 +4549,7 @@ ex_vimgrep(exarg_T *eap)
qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE;
qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start;
qi->qf_lists[qi->qf_curlist].qf_index = 1;
qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL);
@@ -4476,6 +4557,13 @@ ex_vimgrep(exarg_T *eap)
if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, TRUE, curbuf);
/*
* The QuickFixCmdPost autocmd may free the quickfix list. Check the list
* is still valid.
*/
wp = loclist_cmd ? curwin : NULL;
if (!qflist_valid(wp, save_qfid))
goto theend;
#endif
/* Jump to first match. */
@@ -4780,7 +4868,8 @@ enum {
QF_GETLIST_ID = 0x20,
QF_GETLIST_IDX = 0x40,
QF_GETLIST_SIZE = 0x80,
QF_GETLIST_ALL = 0xFF
QF_GETLIST_TICK = 0x100,
QF_GETLIST_ALL = 0x1FF
};
/*
@@ -4896,6 +4985,9 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
if (dict_find(what, (char_u *)"size", -1) != NULL)
flags |= QF_GETLIST_SIZE;
if (dict_find(what, (char_u *)"changedtick", -1) != NULL)
flags |= QF_GETLIST_TICK;
if (qi != NULL && qi->qf_listcount != 0)
{
qf_idx = qi->qf_curlist; /* default is the current list */
@@ -4912,8 +5004,9 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
qf_idx = -1;
}
}
else if ((di->di_tv.v_type == VAR_STRING)
&& (STRCMP(di->di_tv.vval.v_string, "$") == 0))
else if (di->di_tv.v_type == VAR_STRING
&& di->di_tv.vval.v_string != NULL
&& STRCMP(di->di_tv.vval.v_string, "$") == 0)
/* Get the last quickfix list number */
qf_idx = qi->qf_listcount - 1;
else
@@ -4963,6 +5056,8 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = dict_add_nr_str(retdict, "idx", 0L, NULL);
if ((status == OK) && (flags & QF_GETLIST_SIZE))
status = dict_add_nr_str(retdict, "size", 0L, NULL);
if ((status == OK) && (flags & QF_GETLIST_TICK))
status = dict_add_nr_str(retdict, "changedtick", 0L, NULL);
return status;
}
@@ -5035,6 +5130,10 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = dict_add_nr_str(retdict, "size",
qi->qf_lists[qf_idx].qf_count, NULL);
if ((status == OK) && (flags & QF_GETLIST_TICK))
status = dict_add_nr_str(retdict, "changedtick",
qi->qf_lists[qf_idx].qf_changedtick, NULL);
return status;
}
@@ -5202,7 +5301,8 @@ qf_set_properties(qf_info_T *qi, dict_T *what, int action, char_u *title)
newlist = FALSE; /* use the specified list */
}
else if (di->di_tv.v_type == VAR_STRING
&& STRCMP(di->di_tv.vval.v_string, "$") == 0)
&& di->di_tv.vval.v_string != NULL
&& STRCMP(di->di_tv.vval.v_string, "$") == 0)
{
if (qi->qf_listcount > 0)
qf_idx = qi->qf_listcount - 1;
@@ -5304,6 +5404,9 @@ qf_set_properties(qf_info_T *qi, dict_T *what, int action, char_u *title)
retval = OK;
}
if (retval == OK)
qf_list_changed(qi, qf_idx);
return retval;
}
@@ -5407,7 +5510,11 @@ set_errorlist(
else if (what != NULL)
retval = qf_set_properties(qi, what, action, title);
else
{
retval = qf_add_entries(qi, qi->qf_curlist, list, title, action);
if (retval == OK)
qf_list_changed(qi, qi->qf_curlist);
}
return retval;
}
@@ -5453,6 +5560,16 @@ set_ref_in_quickfix(int copyID)
if (abort)
return abort;
}
if (IS_LL_WINDOW(win) && (win->w_llist_ref->qf_refcount == 1))
{
/* In a location list window and none of the other windows is
* referring to this location list. Mark the location list
* context as still in use.
*/
abort = mark_quickfix_ctx(win->w_llist_ref, copyID);
if (abort)
return abort;
}
}
return abort;
@@ -5477,14 +5594,6 @@ ex_cbuffer(exarg_T *eap)
#endif
int res;
if (eap->cmdidx == CMD_lbuffer || eap->cmdidx == CMD_lgetbuffer
|| eap->cmdidx == CMD_laddbuffer)
{
qi = ll_get_or_alloc_list(curwin);
if (qi == NULL)
return;
}
#ifdef FEAT_AUTOCMD
switch (eap->cmdidx)
{
@@ -5506,6 +5615,16 @@ ex_cbuffer(exarg_T *eap)
}
#endif
/* Must come after autocommands. */
if (eap->cmdidx == CMD_lbuffer
|| eap->cmdidx == CMD_lgetbuffer
|| eap->cmdidx == CMD_laddbuffer)
{
qi = ll_get_or_alloc_list(curwin);
if (qi == NULL)
return;
}
if (*eap->arg == NUL)
buf = curbuf;
else if (*skipwhite(skipdigits(eap->arg)) == NUL)
@@ -5540,6 +5659,8 @@ ex_cbuffer(exarg_T *eap)
&& eap->cmdidx != CMD_laddbuffer),
eap->line1, eap->line2,
qf_title, NULL);
if (res >= 0)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD
if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
@@ -5567,14 +5688,6 @@ ex_cexpr(exarg_T *eap)
#endif
int res;
if (eap->cmdidx == CMD_lexpr || eap->cmdidx == CMD_lgetexpr
|| eap->cmdidx == CMD_laddexpr)
{
qi = ll_get_or_alloc_list(curwin);
if (qi == NULL)
return;
}
#ifdef FEAT_AUTOCMD
switch (eap->cmdidx)
{
@@ -5596,6 +5709,15 @@ ex_cexpr(exarg_T *eap)
}
#endif
if (eap->cmdidx == CMD_lexpr
|| eap->cmdidx == CMD_lgetexpr
|| eap->cmdidx == CMD_laddexpr)
{
qi = ll_get_or_alloc_list(curwin);
if (qi == NULL)
return;
}
/* Evaluate the expression. When the result is a string or a list we can
* use it to fill the errorlist. */
tv = eval_expr(eap->arg, NULL);
@@ -5609,6 +5731,8 @@ ex_cexpr(exarg_T *eap)
&& eap->cmdidx != CMD_laddexpr),
(linenr_T)0, (linenr_T)0, *eap->cmdlinep,
NULL);
if (res >= 0)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD
if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
@@ -5829,6 +5953,7 @@ ex_helpgrep(exarg_T *eap)
/* Darn, some plugin changed the value. */
free_string_option(save_cpo);
qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL);
#ifdef FEAT_AUTOCMD

View File

@@ -1130,7 +1130,7 @@ do_tags(exarg_T *eap UNUSED)
continue;
msg_putchar('\n');
sprintf((char *)IObuff, "%c%2d %2d %-15s %5ld ",
vim_snprintf((char *)IObuff, IOSIZE, "%c%2d %2d %-15s %5ld ",
i == tagstackidx ? '>' : ' ',
i + 1,
tagstack[i].cur_match + 1,

View File

@@ -268,6 +268,11 @@ func Test_tolower()
" Ⱥ (U+023A) and Ⱦ (U+023E) are the *only* code points to increase
" in length (2 to 3 bytes) when lowercased. So let's test them.
call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ"))
" This call to tolower with invalid utf8 sequence used to cause access to
" invalid memory.
call tolower("\xC0\x80\xC0")
call tolower("123\xC0\x80\xC0")
endfunc
func Test_toupper()
@@ -338,6 +343,11 @@ func Test_toupper()
call assert_equal("ZŹŻŽƵẐẔ", toupper("ZŹŻŽƵẐẔ"))
call assert_equal("Ⱥ Ⱦ", toupper("ⱥ ⱦ"))
" This call to toupper with invalid utf8 sequence used to cause access to
" invalid memory.
call toupper("\xC0\x80\xC0")
call toupper("123\xC0\x80\xC0")
endfunc
" Tests for the mode() function
@@ -692,6 +702,7 @@ func Test_count()
call assert_equal(0, count("foo", "O"))
call assert_equal(2, count("foo", "O", 1))
call assert_equal(2, count("fooooo", "oo"))
call assert_equal(0, count("foo", ""))
endfunc
func Test_changenr()

View File

@@ -404,6 +404,15 @@ func! Test_normal10_expand()
call assert_equal(expected[i], expand('<cexpr>'), 'i == ' . i)
endfor
if executable('echo')
" Test expand(`...`) i.e. backticks command expansion.
" MS-Windows has a trailing space.
call assert_match('^abcde *$', expand('`echo abcde`'))
endif
" Test expand(`=...`) i.e. backticks expression expansion
call assert_equal('5', expand('`=2+3`'))
" clean up
bw!
endfunc
@@ -1537,12 +1546,12 @@ fun! Test_normal29_brace()
\ 'the ''{'' flag is in ''cpoptions'' then ''{'' in the first column is used as a',
\ 'paragraph boundary |posix|.',
\ '{',
\ 'This is no paragaraph',
\ 'This is no paragraph',
\ 'unless the ''{'' is set',
\ 'in ''cpoptions''',
\ '}',
\ '.IP',
\ 'The nroff macros IP seperates a paragraph',
\ 'The nroff macros IP separates a paragraph',
\ 'That means, it must be a ''.''',
\ 'followed by IP',
\ '.LPIt does not matter, if afterwards some',
@@ -1557,7 +1566,7 @@ fun! Test_normal29_brace()
1
norm! 0d2}
call assert_equal(['.IP',
\ 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', 'followed by IP',
\ 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''', 'followed by IP',
\ '.LPIt does not matter, if afterwards some', 'more characters follow.', '.SHAlso section boundaries from the nroff',
\ 'macros terminate a paragraph. That means', 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))
norm! 0d}
@@ -1576,21 +1585,21 @@ fun! Test_normal29_brace()
set cpo+={
1
norm! 0d2}
call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
\ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''',
call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
\ '.IP', 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''',
\ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.',
\ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means',
\ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))
$
norm! d}
call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
\ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''',
call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
\ '.IP', 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''',
\ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.',
\ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means',
\ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))
norm! gg}
norm! d5}
call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$'))
call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$'))
" clean up
set cpo-={

View File

@@ -1428,6 +1428,11 @@ func XquickfixSetListWithAct(cchar)
call assert_fails("call g:Xsetlist(list1, 0)", 'E928:')
endfunc
func Test_setqflist_invalid_nr()
" The following command used to crash Vim
call setqflist([], ' ', {'nr' : $XXX_DOES_NOT_EXIST})
endfunc
func Test_quickfix_set_list_with_act()
call XquickfixSetListWithAct('c')
call XquickfixSetListWithAct('l')
@@ -2132,6 +2137,8 @@ func Test_Autocmd()
call delete('Xtest')
call delete('Xempty')
au! QuickFixCmdPre
au! QuickFixCmdPost
endfunc
func Test_Autocmd_Exception()
@@ -2896,7 +2903,8 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'size' : 0}).size)
call assert_equal('', g:Xgetlist({'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0}, g:Xgetlist({'all' : 0}))
call assert_equal(0, g:Xgetlist({'changedtick' : 0}).changedtick)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick': 0}, g:Xgetlist({'all' : 0}))
" Empty quickfix list
Xexpr ""
@@ -2908,6 +2916,7 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'size' : 0}).size)
call assert_notequal('', g:Xgetlist({'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
let qfid = g:Xgetlist({'id' : 0}).id
call g:Xsetlist([], 'f')
@@ -2921,7 +2930,8 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'id' : qfid, 'size' : 0}).size)
call assert_equal('', g:Xgetlist({'id' : qfid, 'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'id' : qfid, 'winid' : 0}).winid)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
call assert_equal(0, g:Xgetlist({'id' : qfid, 'changedtick' : 0}).changedtick)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
" Non-existing quickfix list number
call assert_equal('', g:Xgetlist({'nr' : 5, 'context' : 0}).context)
@@ -2932,10 +2942,139 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'nr' : 5, 'size' : 0}).size)
call assert_equal('', g:Xgetlist({'nr' : 5, 'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'nr' : 5, 'winid' : 0}).winid)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0}))
call assert_equal(0, g:Xgetlist({'nr' : 5, 'changedtick' : 0}).changedtick)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0}))
endfunc
func Test_getqflist()
call Xgetlist_empty_tests('c')
call Xgetlist_empty_tests('l')
endfunc
func Test_getqflist_invalid_nr()
" The following commands used to crash Vim
cexpr ""
call getqflist({'nr' : $XXX_DOES_NOT_EXIST_XXX})
" Cleanup
call setqflist([], 'r')
endfunc
" Tests for the quickfix/location list changedtick
func Xqftick_tests(cchar)
call s:setup_commands(a:cchar)
call g:Xsetlist([], 'f')
Xexpr "F1:10:Line10"
let qfid = g:Xgetlist({'id' : 0}).id
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
Xaddexpr "F2:20:Line20\nF2:21:Line21"
call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([], 'a', {'lines' : ["F3:30:Line30", "F3:31:Line31"]})
call assert_equal(3, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([], 'r', {'lines' : ["F4:40:Line40"]})
call assert_equal(4, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([], 'a', {'title' : 'New Title'})
call assert_equal(5, g:Xgetlist({'changedtick' : 0}).changedtick)
enew!
call append(0, ["F5:50:L50", "F6:60:L60"])
Xaddbuffer
call assert_equal(6, g:Xgetlist({'changedtick' : 0}).changedtick)
enew!
call g:Xsetlist([], 'a', {'context' : {'bus' : 'pci'}})
call assert_equal(7, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
\ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'a')
call assert_equal(8, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
\ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], ' ')
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
\ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'r')
call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
call writefile(["F8:80:L80", "F8:81:L81"], "Xone")
Xfile Xone
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
Xaddfile Xone
call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
" Test case for updating a non-current quickfix list
call g:Xsetlist([], 'f')
Xexpr "F1:1:L1"
Xexpr "F2:2:L2"
call g:Xsetlist([], 'a', {'nr' : 1, "lines" : ["F10:10:L10"]})
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
call assert_equal(2, g:Xgetlist({'nr' : 1, 'changedtick' : 0}).changedtick)
call delete("Xone")
endfunc
func Test_qf_tick()
call Xqftick_tests('c')
call Xqftick_tests('l')
endfunc
" The following test used to crash Vim.
" Open the location list window and close the regular window associated with
" the location list. When the garbage collection runs now, it incorrectly
" marks the location list context as not in use and frees the context.
func Test_ll_window_ctx()
call setloclist(0, [], 'f')
call setloclist(0, [], 'a', {'context' : []})
lopen | only
call test_garbagecollect_now()
echo getloclist(0, {'context' : 1}).context
enew | only
endfunc
" The following test used to crash vim
func Test_lfile_crash()
sp Xtest
au QuickFixCmdPre * bw
call assert_fails('lfile', 'E40')
au! QuickFixCmdPre
endfunc
" The following test used to crash vim
func Test_lbuffer_crash()
sv Xtest
augroup QF_Test
au!
au * * bw
augroup END
lbuffer
augroup QF_Test
au!
augroup END
endfunc
" The following test used to crash vim
func Test_lexpr_crash()
augroup QF_Test
au!
au * * call setloclist(0, [], 'f')
augroup END
lexpr ""
augroup QF_Test
au!
augroup END
enew | only
endfunc
" The following test used to crash Vim
func Test_lvimgrep_crash()
sv Xtest
augroup QF_Test
au!
au * * call setloclist(0, [], 'f')
augroup END
lvimgrep quickfix test_quickfix.vim
augroup QF_Test
au!
augroup END
enew | only
endfunc

View File

@@ -729,3 +729,12 @@ func Test_look_behind()
call search(getline("."))
bwipe!
endfunc
func Test_search_sentence()
new
" this used to cause a crash
call assert_fails("/\\%')", 'E486')
call assert_fails("/", 'E486')
/\%'(
/
endfunc

View File

@@ -28,6 +28,37 @@ func Test_wrap_search()
set nospell
endfunc
func Test_curswant()
new
call setline(1, ['Another plong line', 'abcdefghijklmnopq'])
set spell wrapscan
normal 0]s
call assert_equal('plong', expand('<cword>'))
normal j
call assert_equal(9, getcurpos()[2])
normal 0[s
call assert_equal('plong', expand('<cword>'))
normal j
call assert_equal(9, getcurpos()[2])
normal 0]S
call assert_equal('plong', expand('<cword>'))
normal j
call assert_equal(9, getcurpos()[2])
normal 0[S
call assert_equal('plong', expand('<cword>'))
normal j
call assert_equal(9, getcurpos()[2])
normal 1G0
call assert_equal('plong', spellbadword()[0])
normal j
call assert_equal(9, getcurpos()[2])
bwipe!
set nospell
endfunc
func Test_z_equal_on_invalid_utf8_word()
split
set spell

View File

@@ -1,4 +1,4 @@
" test 'taglist' function
" test 'taglist' function and :tags command
func Test_taglist()
call writefile([
@@ -56,3 +56,8 @@ func Test_taglist_ctags_etags()
call delete('Xtags')
endfunc
func Test_tags_too_long()
call assert_fails('tag ' . repeat('x', 1020), 'E426')
tags
endfunc

View File

@@ -467,4 +467,9 @@ func Test_window_contents()
call test_garbagecollect_now()
endfunc
func Test_window_colon_command()
" This was reading invalid memory.
exe "norm! v\<C-W>:\<C-U>echo v:version"
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -771,6 +771,38 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1421,
/**/
1420,
/**/
1419,
/**/
1418,
/**/
1417,
/**/
1416,
/**/
1415,
/**/
1414,
/**/
1413,
/**/
1412,
/**/
1411,
/**/
1410,
/**/
1409,
/**/
1408,
/**/
1407,
/**/
1406,
/**/
1405,
/**/