summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2005-06-13 22:28:56 +0000
committerBram Moolenaar <Bram@vim.org>2005-06-13 22:28:56 +0000
commit9ba0eb850c0f4c94df3b7f7461610bf0b073f712 (patch)
tree11638af8ad8ecdfd337a6db15914b2e2cdff3aea /src
parentbac97eb8ae6b067466cab0481cac2f25b335ffe7 (diff)
downloadvim-git-9ba0eb850c0f4c94df3b7f7461610bf0b073f712.tar.gz
updated for version 7.0084v7.0084
Diffstat (limited to 'src')
-rw-r--r--src/Make_mvc.mak15
-rw-r--r--src/eval.c1108
-rw-r--r--src/ex_cmds.c4
-rw-r--r--src/ex_docmd.c1
-rw-r--r--src/ex_getln.c8
-rw-r--r--src/gui_w32.c13
-rw-r--r--src/gui_w48.c23
-rw-r--r--src/if_cscope.c2
-rw-r--r--src/if_python.c38
-rw-r--r--src/misc1.c21
-rw-r--r--src/misc2.c102
-rw-r--r--src/normal.c7
-rw-r--r--src/option.c30
-rw-r--r--src/os_mswin.c18
-rw-r--r--src/os_w32exe.c1
-rw-r--r--src/os_win32.c14
-rw-r--r--src/proto/misc1.pro1
-rw-r--r--src/proto/misc2.pro1
-rw-r--r--src/proto/spell.pro3
-rw-r--r--src/spell.c2291
-rw-r--r--src/tag.c11
-rw-r--r--src/ui.c1
-rw-r--r--src/version.h4
23 files changed, 3156 insertions, 561 deletions
diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak
index 4d4ea225e..eb5aa7e9e 100644
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -110,7 +110,8 @@
TARGETOS = BOTH
-# Select one of eight object code directories, depends on GUI, OLE and DEBUG.
+# Select one of eight object code directories, depends on GUI, OLE, DEBUG and
+# interfaces.
# If you change something else, do "make clean" first!
!if "$(GUI)" == "yes"
OBJDIR = .\ObjG
@@ -120,6 +121,18 @@ OBJDIR = .\ObjC
!if "$(OLE)" == "yes"
OBJDIR = $(OBJDIR)O
!endif
+!ifdef PERL
+OBJDIR = $(OBJDIR)L
+!endif
+!ifdef PYTHON
+OBJDIR = $(OBJDIR)Y
+!endif
+!ifdef TCL
+OBJDIR = $(OBJDIR)T
+!endif
+!ifdef RUBY
+OBJDIR = $(OBJDIR)R
+!endif
!ifdef MZSCHEME
OBJDIR = $(OBJDIR)Z
!endif
diff --git a/src/eval.c b/src/eval.c
index aa3935034..913f8ed0f 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -264,12 +264,11 @@ typedef struct
#define VV_RO 2 /* read-only */
#define VV_RO_SBX 4 /* read-only in the sandbox */
-#define VV_NAME(s, t) s, sizeof(s) - 1, {{t}}, {0}
+#define VV_NAME(s, t) s, {{t}}, {0}
static struct vimvar
{
char *vv_name; /* name of variable, without v: */
- int vv_len; /* length of name */
dictitem_T vv_di; /* value and name for key */
char vv_filler[16]; /* space for LONGEST name below!!! */
char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */
@@ -594,9 +593,12 @@ static void free_tv __ARGS((typval_T *varp));
static void clear_tv __ARGS((typval_T *varp));
static void init_tv __ARGS((typval_T *varp));
static long get_tv_number __ARGS((typval_T *varp));
+static long get_tv_number_chk __ARGS((typval_T *varp, int *denote));
static linenr_T get_tv_lnum __ARGS((typval_T *argvars));
static char_u *get_tv_string __ARGS((typval_T *varp));
+static char_u *get_tv_string_chk __ARGS((typval_T *varp));
static char_u *get_tv_string_buf __ARGS((typval_T *varp, char_u *buf));
+static char_u *get_tv_string_buf_chk __ARGS((typval_T *varp, char_u *buf));
static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp));
static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, char_u *varname, int writing));
static hashtab_T *find_var_ht __ARGS((char_u *name, char_u **varname));
@@ -1023,7 +1025,7 @@ eval_to_bool(arg, error, nextcmd, skip)
*error = FALSE;
if (!skip)
{
- retval = (get_tv_number(&tv) != 0);
+ retval = (get_tv_number_chk(&tv, error) != 0);
clear_tv(&tv);
}
}
@@ -1137,7 +1139,7 @@ eval_to_number(expr)
retval = -1;
else
{
- retval = get_tv_number(&rettv);
+ retval = get_tv_number_chk(&rettv, NULL);
clear_tv(&rettv);
}
--emsg_off;
@@ -1775,8 +1777,8 @@ ex_let_one(arg, tv, copy, endchars, op)
{
c1 = name[len];
name[len] = NUL;
- p = get_tv_string(tv);
- if (op != NULL && *op == '.')
+ p = get_tv_string_chk(tv);
+ if (p != NULL && op != NULL && *op == '.')
{
int mustfree = FALSE;
char_u *s = vim_getenv(name, &mustfree);
@@ -1789,15 +1791,18 @@ ex_let_one(arg, tv, copy, endchars, op)
}
}
if (p != NULL)
+ {
vim_setenv(name, p);
- if (STRICMP(name, "HOME") == 0)
- init_homedir();
- else if (didset_vim && STRICMP(name, "VIM") == 0)
- didset_vim = FALSE;
- else if (didset_vimruntime && STRICMP(name, "VIMRUNTIME") == 0)
- didset_vimruntime = FALSE;
+ if (STRICMP(name, "HOME") == 0)
+ init_homedir();
+ else if (didset_vim && STRICMP(name, "VIM") == 0)
+ didset_vim = FALSE;
+ else if (didset_vimruntime
+ && STRICMP(name, "VIMRUNTIME") == 0)
+ didset_vimruntime = FALSE;
+ arg_end = arg;
+ }
name[len] = c1;
- arg_end = arg;
vim_free(tofree);
}
}
@@ -1827,8 +1832,8 @@ ex_let_one(arg, tv, copy, endchars, op)
*p = NUL;
n = get_tv_number(tv);
- s = get_tv_string(tv);
- if (op != NULL && *op != '=')
+ s = get_tv_string_chk(tv); /* != NULL if number or string */
+ if (s != NULL && op != NULL && *op != '=')
{
opt_type = get_option_value(arg, &numval,
&stringval, opt_flags);
@@ -1852,9 +1857,12 @@ ex_let_one(arg, tv, copy, endchars, op)
}
}
}
- set_option_value(arg, n, s, opt_flags);
+ if (s != NULL)
+ {
+ set_option_value(arg, n, s, opt_flags);
+ arg_end = p;
+ }
*p = c1;
- arg_end = p;
vim_free(stringval);
}
}
@@ -1875,8 +1883,8 @@ ex_let_one(arg, tv, copy, endchars, op)
char_u *tofree = NULL;
char_u *s;
- p = get_tv_string(tv);
- if (op != NULL && *op == '.')
+ p = get_tv_string_chk(tv);
+ if (p != NULL && op != NULL && *op == '.')
{
s = get_reg_contents(*arg == '@' ? '"' : *arg, FALSE, FALSE);
if (s != NULL)
@@ -1886,8 +1894,10 @@ ex_let_one(arg, tv, copy, endchars, op)
}
}
if (p != NULL)
+ {
write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
- arg_end = arg + 1;
+ arg_end = arg + 1;
+ }
vim_free(tofree);
}
}
@@ -2070,6 +2080,12 @@ get_lval(name, rettv, lp, unlet, skip, quiet, fne_flags)
empty1 = FALSE;
if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */
return NULL;
+ if (get_tv_string_chk(&var1) == NULL)
+ {
+ /* not a number or string */
+ clear_tv(&var1);
+ return NULL;
+ }
}
/* Optionally get the second index [ :expr]. */
@@ -2104,6 +2120,14 @@ get_lval(name, rettv, lp, unlet, skip, quiet, fne_flags)
clear_tv(&var1);
return NULL;
}
+ if (get_tv_string_chk(&var2) == NULL)
+ {
+ /* not a number or string */
+ if (!empty1)
+ clear_tv(&var1);
+ clear_tv(&var2);
+ return NULL;
+ }
}
lp->ll_range = TRUE;
}
@@ -2130,7 +2154,7 @@ get_lval(name, rettv, lp, unlet, skip, quiet, fne_flags)
if (len == -1)
{
/* "[key]": get key from "var1" */
- key = get_tv_string(&var1);
+ key = get_tv_string(&var1); /* is number or string */
if (*key == NUL)
{
if (!quiet)
@@ -2176,7 +2200,7 @@ get_lval(name, rettv, lp, unlet, skip, quiet, fne_flags)
lp->ll_n1 = 0;
else
{
- lp->ll_n1 = get_tv_number(&var1);
+ lp->ll_n1 = get_tv_number(&var1); /* is number or string */
clear_tv(&var1);
}
lp->ll_dict = NULL;
@@ -2199,7 +2223,7 @@ get_lval(name, rettv, lp, unlet, skip, quiet, fne_flags)
*/
if (lp->ll_range && !lp->ll_empty2)
{
- lp->ll_n2 = get_tv_number(&var2);
+ lp->ll_n2 = get_tv_number(&var2); /* is number or string */
clear_tv(&var2);
if (lp->ll_n2 < 0)
{
@@ -3340,9 +3364,13 @@ eval1(arg, rettv, evaluate)
result = FALSE;
if (evaluate)
{
- if (get_tv_number(rettv) != 0)
+ int error = FALSE;
+
+ if (get_tv_number_chk(rettv, &error) != 0)
result = TRUE;
clear_tv(rettv);
+ if (error)
+ return FAIL;
}
/*
@@ -3398,6 +3426,7 @@ eval2(arg, rettv, evaluate)
typval_T var2;
long result;
int first;
+ int error = FALSE;
/*
* Get the first variable.
@@ -3414,9 +3443,11 @@ eval2(arg, rettv, evaluate)
{
if (evaluate && first)
{
- if (get_tv_number(rettv) != 0)
+ if (get_tv_number_chk(rettv, &error) != 0)
result = TRUE;
clear_tv(rettv);
+ if (error)
+ return FAIL;
first = FALSE;
}
@@ -3432,9 +3463,11 @@ eval2(arg, rettv, evaluate)
*/
if (evaluate && !result)
{
- if (get_tv_number(&var2) != 0)
+ if (get_tv_number_chk(&var2, &error) != 0)
result = TRUE;
clear_tv(&var2);
+ if (error)
+ return FAIL;
}
if (evaluate)
{
@@ -3464,6 +3497,7 @@ eval3(arg, rettv, evaluate)
typval_T var2;
long result;
int first;
+ int error = FALSE;
/*
* Get the first variable.
@@ -3480,9 +3514,11 @@ eval3(arg, rettv, evaluate)
{
if (evaluate && first)
{
- if (get_tv_number(rettv) == 0)
+ if (get_tv_number_chk(rettv, &error) == 0)
result = FALSE;
clear_tv(rettv);
+ if (error)
+ return FAIL;
first = FALSE;
}
@@ -3498,9 +3534,11 @@ eval3(arg, rettv, evaluate)
*/
if (evaluate && result)
{
- if (get_tv_number(&var2) == 0)
+ if (get_tv_number_chk(&var2, &error) == 0)
result = FALSE;
clear_tv(&var2);
+ if (error)
+ return FAIL;
}
if (evaluate)
{
@@ -3832,6 +3870,22 @@ eval5(arg, rettv, evaluate)
if (op != '+' && op != '-' && op != '.')
break;
+ if (op != '+' || rettv->v_type != VAR_LIST)
+ {
+ /* For "list + ...", an illegal use of the first operand as
+ * a number cannot be determined before evaluating the 2nd
+ * operand: if this is also a list, all is ok.
+ * For "something . ...", "something - ..." or "non-list + ...",
+ * we know that the first operand needs to be a string or number
+ * without evaluating the 2nd operand. So check before to avoid
+ * side effects after an error. */
+ if (evaluate && get_tv_string_chk(rettv) == NULL)
+ {
+ clear_tv(rettv);
+ return FAIL;
+ }
+ }
+
/*
* Get the second variable.
*/
@@ -3849,8 +3903,14 @@ eval5(arg, rettv, evaluate)
*/
if (op == '.')
{
- s1 = get_tv_string_buf(rettv, buf1);
- s2 = get_tv_string_buf(&var2, buf2);
+ s1 = get_tv_string_buf(rettv, buf1); /* already checked */
+ s2 = get_tv_string_buf_chk(&var2, buf2);
+ if (s2 == NULL) /* type error ? */
+ {
+ clear_tv(rettv);
+ clear_tv(&var2);
+ return FAIL;
+ }
p = concat_str(s1, s2);
clear_tv(rettv);
rettv->v_type = VAR_STRING;
@@ -3872,8 +3932,24 @@ eval5(arg, rettv, evaluate)
}
else
{
- n1 = get_tv_number(rettv);
- n2 = get_tv_number(&var2);
+ int error = FALSE;
+
+ n1 = get_tv_number_chk(rettv, &error);
+ if (error)
+ {
+ /* This can only happen for "list + non-list".
+ * For "non-list + ..." or "something - ...", we returned
+ * before evaluating the 2nd operand. */
+ clear_tv(rettv);
+ return FAIL;
+ }
+ n2 = get_tv_number_chk(&var2, &error);
+ if (error)
+ {
+ clear_tv(rettv);
+ clear_tv(&var2);
+ return FAIL;
+ }
clear_tv(rettv);
if (op == '+')
n1 = n1 + n2;
@@ -3908,6 +3984,7 @@ eval6(arg, rettv, evaluate)
typval_T var2;
int op;
long n1, n2;
+ int error = FALSE;
/*
* Get the first variable.
@@ -3926,8 +4003,10 @@ eval6(arg, rettv, evaluate)
if (evaluate)
{
- n1 = get_tv_number(rettv);
+ n1 = get_tv_number_chk(rettv, &error);
clear_tv(rettv);
+ if (error)
+ return FAIL;
}
else
n1 = 0;
@@ -3941,8 +4020,10 @@ eval6(arg, rettv, evaluate)
if (evaluate)
{
- n2 = get_tv_number(&var2);
+ n2 = get_tv_number_chk(&var2, &error);
clear_tv(&var2);
+ if (error)
+ return FAIL;
/*
* Compute the result.
@@ -4175,18 +4256,28 @@ eval7(arg, rettv, evaluate)
*/
if (ret == OK && evaluate && end_leader > start_leader)
{
- val = get_tv_number(rettv);
- while (end_leader > start_leader)
+ int error = FALSE;
+
+ val = get_tv_number_chk(rettv, &error);
+ if (error)
{
- --end_leader;
- if (*end_leader == '!')
- val = !val;
- else if (*end_leader == '-')
- val = -val;
+ clear_tv(rettv);
+ ret = FAIL;
+ }
+ else
+ {
+ while (end_leader > start_leader)
+ {
+ --end_leader;
+ if (*end_leader == '!')
+ val = !val;
+ else if (*end_leader == '-')
+ val = -val;
+ }
+ clear_tv(rettv);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = val;
}
- clear_tv(rettv);
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = val;
}
return ret;
@@ -4243,6 +4334,12 @@ eval_index(arg, rettv, evaluate, verbose)
empty1 = TRUE;
else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */
return FAIL;
+ else if (evaluate && get_tv_string_chk(&var1) == NULL)
+ {
+ /* not a number or string */
+ clear_tv(&var1);
+ return FAIL;
+ }
/*
* Get the second variable from inside the [:].
@@ -4255,7 +4352,16 @@ eval_index(arg, rettv, evaluate, verbose)
empty2 = TRUE;
else if (eval1(arg, &var2, evaluate) == FAIL) /* recursive! */
{
- clear_tv(&var1);
+ if (!empty1)
+ clear_tv(&var1);
+ return FAIL;
+ }
+ else if (evaluate && get_tv_string_chk(&var2) == NULL)
+ {
+ /* not a number or string */
+ if (!empty1)
+ clear_tv(&var1);
+ clear_tv(&var2);
return FAIL;
}
}
@@ -4928,6 +5034,7 @@ tv_equal(tv1, tv2, ic)
int ic; /* ignore case */
{
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+ char_u *s1, *s2;
if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
{
@@ -4963,12 +5070,13 @@ tv_equal(tv1, tv2, ic)
if (get_tv_number(tv1) != get_tv_number(tv2))
return FALSE;
}
- else if (!ic && STRCMP(get_tv_string_buf(tv1, buf1),
- get_tv_string_buf(tv2, buf2)) != 0)
- return FALSE;
- else if (ic && STRICMP(get_tv_string_buf(tv1, buf1),
- get_tv_string_buf(tv2, buf2)) != 0)
- return FALSE;
+ else
+ {
+ s1 = get_tv_string_buf(tv1, buf1);
+ s2 = get_tv_string_buf(tv2, buf2);
+ if ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) != 0)
+ return FALSE;
+ }
return TRUE;
}
@@ -5828,10 +5936,12 @@ get_dict_tv(arg, rettv, evaluate)
clear_tv(&tvkey);
goto failret;
}
- key = get_tv_string_buf(&tvkey, buf);
- if (*key == NUL)
+ key = get_tv_string_buf_chk(&tvkey, buf);
+ if (key == NULL || *key == NUL)
{
- EMSG(_(e_emptykey));
+ /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
+ if (key != NULL)
+ EMSG(_(e_emptykey));
clear_tv(&tvkey);
goto failret;
}
@@ -6731,12 +6841,12 @@ f_append(argvars, rettv)
typval_T *rettv;
{
long lnum;
+ char_u *line;
list_T *l = NULL;
listitem_T *li = NULL;
typval_T *tv;
long added = 0;
- rettv->vval.v_number = 1; /* Default: Failed */
lnum = get_tv_lnum(argvars);
if (lnum >= 0
&& lnum <= curbuf->b_ml.ml_line_count
@@ -6749,6 +6859,7 @@ f_append(argvars, rettv)
return;
li = l->lv_first;
}
+ rettv->vval.v_number = 0; /* Default: Success */
for (;;)
{
if (l == NULL)
@@ -6757,7 +6868,13 @@ f_append(argvars, rettv)
break; /* end of list */
else
tv = &li->li_tv; /* append item from list */
- ml_append(lnum + added, get_tv_string(tv), (colnr_T)0, FALSE);
+ line = get_tv_string_chk(tv);
+ if (line == NULL) /* type error */
+ {
+ rettv->vval.v_number = 1; /* Failed */
+ break;
+ }
+ ml_append(lnum + added, line, (colnr_T)0, FALSE);
++added;
if (l == NULL)
break;
@@ -6767,8 +6884,9 @@ f_append(argvars, rettv)
appended_lines_mark(lnum, added);
if (curwin->w_cursor.lnum > lnum)
curwin->w_cursor.lnum += added;
- rettv->vval.v_number = 0; /* Success */
}
+ else
+ rettv->vval.v_number = 1; /* Failed */
}
/*
@@ -6805,7 +6923,7 @@ f_argv(argvars, rettv)
{
int idx;
- idx = get_tv_number(&argvars[0]);
+ idx = get_tv_number_chk(&argvars[0], NULL);
if (idx >= 0 && idx < ARGCOUNT)
rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx]));
else
@@ -6829,13 +6947,17 @@ f_browse(argvars, rettv)
char_u *defname;
char_u buf[NUMBUFLEN];
char_u buf2[NUMBUFLEN];
+ int error = FALSE;
- save = get_tv_number(&argvars[0]);
- title = get_tv_string(&argvars[1]);
- initdir = get_tv_string_buf(&argvars[2], buf);
- defname = get_tv_string_buf(&argvars[3], buf2);
+ save = get_tv_number_chk(&argvars[0], &error);
+ title = get_tv_string_chk(&argvars[1]);
+ initdir = get_tv_string_buf_chk(&argvars[2], buf);
+ defname = get_tv_string_buf_chk(&argvars[3], buf2);
- rettv->vval.v_string =
+ if (error || title == NULL || initdir == NULL || defname == NULL)
+ rettv->vval.v_string = NULL;
+ else
+ rettv->vval.v_string =
do_browse(save ? BROWSE_SAVE : 0,
title, defname, NULL, initdir, NULL, curbuf);
#else
@@ -6858,10 +6980,13 @@ f_browsedir(argvars, rettv)
char_u *initdir;
char_u buf[NUMBUFLEN];
- title = get_tv_string(&argvars[0]);
- initdir = get_tv_string_buf(&argvars[1], buf);
+ title = get_tv_string_chk(&argvars[0]);
+ initdir = get_tv_string_buf_chk(&argvars[1], buf);
- rettv->vval.v_string = do_browse(BROWSE_DIR,
+ if (title == NULL || initdir == NULL)
+ rettv->vval.v_string = NULL;
+ else
+ rettv->vval.v_string = do_browse(BROWSE_DIR,
title, NULL, NULL, initdir, NULL, curbuf);
#else
rettv->vval.v_string = NULL;
@@ -6994,6 +7119,7 @@ f_bufname(argvars, rettv)
{
buf_T *buf;
+ (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0]);
rettv->v_type = VAR_STRING;
@@ -7014,6 +7140,7 @@ f_bufnr(argvars, rettv)
{
buf_T *buf;
+ (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0]);
if (buf != NULL)
@@ -7037,6 +7164,7 @@ f_bufwinnr(argvars, rettv)
#endif
buf_T *buf;
+ (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
++emsg_off;
buf = get_buf_tv(&argvars[0]);
#ifdef FEAT_WINDOWS
@@ -7067,7 +7195,7 @@ f_byte2line(argvars, rettv)
#else
long boff = 0;
- boff = get_tv_number(&argvars[0]) - 1;
+ boff = get_tv_number(&argvars[0]) - 1; /* boff gets -1 on type error */
if (boff < 0)
rettv->vval.v_number = -1;
else
@@ -7091,10 +7219,10 @@ f_byteidx(argvars, rettv)
char_u *str;
long idx;
- str = get_tv_string(&argvars[0]);
- idx = get_tv_number(&argvars[1]);
+ str = get_tv_string_chk(&argvars[0]);
+ idx = get_tv_number_chk(&argvars[1], NULL);
rettv->vval.v_number = -1;
- if (idx < 0)
+ if (str == NULL || idx < 0)
return;
#ifdef FEAT_MBYTE
@@ -7140,6 +7268,8 @@ f_call(argvars, rettv)
func = argvars[0].vval.v_string;
else
func = get_tv_string(&argvars[0]);
+ if (*func == NUL)
+ return; /* type error or empty name */
if (argvars[2].v_type != VAR_UNKNOWN)
{
@@ -7185,8 +7315,7 @@ f_char2nr(argvars, rettv)
{
#ifdef FEAT_MBYTE
if (has_mbyte)
- rettv->vval.v_number =
- (*mb_ptr2char)(get_tv_string(&argvars[0]));
+ rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0]));
else
#endif
rettv->vval.v_number = get_tv_string(&argvars[0])[0];
@@ -7285,26 +7414,35 @@ f_confirm(argvars, rettv)
char_u buf2[NUMBUFLEN];
int def = 1;
int type = VIM_GENERIC;
- int c;
+ char_u *typestr;
+ int error = FALSE;
- message = get_tv_string(&argvars[0]);
+ message = get_tv_string_chk(&argvars[0]);
+ if (message == NULL)
+ error = TRUE;
if (argvars[1].v_type != VAR_UNKNOWN)
{
- buttons = get_tv_string_buf(&argvars[1], buf);
+ buttons = get_tv_string_buf_chk(&argvars[1], buf);
+ if (buttons == NULL)
+ error = TRUE;
if (argvars[2].v_type != VAR_UNKNOWN)
{
- def = get_tv_number(&argvars[2]);
+ def = get_tv_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN)
{
- /* avoid that TOUPPER_ASC calls get_tv_string_buf() twice */
- c = *get_tv_string_buf(&argvars[3], buf2);
- switch (TOUPPER_ASC(c))
+ typestr = get_tv_string_buf_chk(&argvars[3], buf2);
+ if (typestr == NULL)
+ error = TRUE;
+ else
{
- case 'E': type = VIM_ERROR; break;
- case 'Q': type = VIM_QUESTION; break;
- case 'I': type = VIM_INFO; break;
- case 'W': type = VIM_WARNING; break;
- case 'G': type = VIM_GENERIC; break;
+ switch (TOUPPER_ASC(*typestr))
+ {
+ case 'E': type = VIM_ERROR; break;
+ case 'Q': type = VIM_QUESTION; break;
+ case 'I': type = VIM_INFO; break;
+ case 'W': type = VIM_WARNING; break;
+ case 'G': type = VIM_GENERIC; break;
+ }
}
}
}
@@ -7313,7 +7451,10 @@ f_confirm(argvars, rettv)
if (buttons == NULL || *buttons == NUL)
buttons = (char_u *)_("&Ok");
- rettv->vval.v_number = do_dialog(type, NULL, message, buttons,
+ if (error)
+ rettv->vval.v_number = 0;
+ else
+ rettv->vval.v_number = do_dialog(type, NULL, message, buttons,
def, NULL);
#else
rettv->vval.v_number = 0;
@@ -7353,14 +7494,21 @@ f_count(argvars, rettv)
li = l->lv_first;
if (argvars[2].v_type != VAR_UNKNOWN)
{
- ic = get_tv_number(&argvars[2]);
+ int error = FALSE;
+
+ ic = get_tv_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN)
{
- idx = get_tv_number(&argvars[3]);
- li = list_find(l, idx);
- if (li == NULL)
- EMSGN(_(e_listidx), idx);
+ idx = get_tv_number_chk(&argvars[3], &error);
+ if (!error)
+ {
+ li = list_find(l, idx);
+ if (li == NULL)
+ EMSGN(_(e_listidx), idx);
+ }
}
+ if (error)
+ li = NULL;
}
for ( ; li != NULL; li = li->li_next)
@@ -7376,14 +7524,16 @@ f_count(argvars, rettv)
if ((d = argvars[0].vval.v_dict) != NULL)
{
+ int error = FALSE;
+
if (argvars[2].v_type != VAR_UNKNOWN)
{
- ic = get_tv_number(&argvars[2]);
+ ic = get_tv_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN)
EMSG(_(e_invarg));
}
- todo = d->dv_hashtab.ht_used;
+ todo = error ? 0 : d->dv_hashtab.ht_used;
for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
{
if (!HASHITEM_EMPTY(hi))
@@ -7446,9 +7596,11 @@ f_cursor(argvars, rettv)
long line, col;
line = get_tv_lnum(argvars);
+ col = get_tv_number_chk(&argvars[1], NULL);
+ if (line < 0 || col < 0)
+ return; /* type error; errmsg already given */
if (line > 0)
curwin->w_cursor.lnum = line;
- col = get_tv_number(&argvars[1]);
if (col > 0)
curwin->w_cursor.col = col - 1;
#ifdef FEAT_VIRTUALEDIT
@@ -7478,7 +7630,7 @@ f_deepcopy(argvars, rettv)
int noref = 0;
if (argvars[1].v_type != VAR_UNKNOWN)
- noref = get_tv_number(&argvars[1]);
+ noref = get_tv_number_chk(&argvars[1], NULL);
if (noref < 0 || noref > 1)
EMSG(_(e_invarg));
else
@@ -7549,6 +7701,8 @@ f_diff_hlID(argvars, rettv)
int filler_lines;
int col;
+ if (lnum < 0) /* ignore type error in {lnum} arg */
+ lnum = 0;
if (lnum != prev_lnum
|| changedtick != curbuf->b_changedtick
|| fnum != curbuf->b_fnum)
@@ -7578,7 +7732,7 @@ f_diff_hlID(argvars, rettv)
if (hlID == HLF_CHD || hlID == HLF_TXD)
{
- col = get_tv_number(&argvars[1]) - 1;
+ col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */
if (col >= change_start && col <= change_end)
hlID = HLF_TXD; /* changed text */
else
@@ -7650,11 +7804,15 @@ f_eval(argvars, rettv)
{
char_u *s;
- s = get_tv_string(&argvars[0]);
- s = skipwhite(s);
+ s = get_tv_string_chk(&argvars[0]);
+ if (s != NULL)
+ s = skipwhite(s);
- if (eval1(&s, rettv, TRUE) == FAIL)
+ if (s == NULL || eval1(&s, rettv, TRUE) == FAIL)
+ {
+ rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
+ }
else if (*s != NUL)
EMSG(_(e_trailing));
}
@@ -7772,6 +7930,7 @@ f_expand(argvars, rettv)
char_u *errormsg;
int flags = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND;
expand_T xpc;
+ int error = FALSE;
rettv->v_type = VAR_STRING;
s = get_tv_string(&argvars[0]);
@@ -7785,12 +7944,18 @@ f_expand(argvars, rettv)
{
/* When the optional second argument is non-zero, don't remove matches
* for 'suffixes' and 'wildignore' */
- if (argvars[1].v_type != VAR_UNKNOWN && get_tv_number(&argvars[1]))
+ if (argvars[1].v_type != VAR_UNKNOWN
+ && get_tv_number_chk(&argvars[1], &error))
flags |= WILD_KEEP_ALL;
- ExpandInit(&xpc);
- xpc.xp_context = EXPAND_FILES;
- rettv->vval.v_string = ExpandOne(&xpc, s, NULL, flags, WILD_ALL);
- ExpandCleanup(&xpc);
+ if (!error)
+ {
+ ExpandInit(&xpc);
+ xpc.xp_context = EXPAND_FILES;
+ rettv->vval.v_string = ExpandOne(&xpc, s, NULL, flags, WILD_ALL);
+ ExpandCleanup(&xpc);
+ }
+ else
+ rettv->vval.v_string = NULL;
}
}
@@ -7809,6 +7974,7 @@ f_extend(argvars, rettv)
list_T *l1, *l2;
listitem_T *item;
long before;
+ int error = FALSE;
l1 = argvars[0].vval.v_list;
l2 = argvars[1].vval.v_list;
@@ -7817,7 +7983,10 @@ f_extend(argvars, rettv)
{
if (argvars[2].v_type != VAR_UNKNOWN)
{
- before = get_tv_number(&argvars[2]);
+ before = get_tv_number_chk(&argvars[2], &error);
+ if (error)
+ return; /* type error; errmsg already given */
+
if (before == l1->lv_len)
item = NULL;
else
@@ -7857,7 +8026,9 @@ f_extend(argvars, rettv)
{
static char *(av[]) = {"keep", "force", "error"};
- action = get_tv_string(&argvars[2]);
+ action = get_tv_string_chk(&argvars[2]);
+ if (action == NULL)
+ return; /* type error; errmsg already given */
for (i = 0; i < 3; ++i)
if (STRCMP(action, av[i]) == 0)
break;
@@ -7963,22 +8134,30 @@ findfilendir(argvars, rettv, dir)
if (argvars[1].v_type != VAR_UNKNOWN)
{
- p = get_tv_string_buf(&argvars[1], pathbuf);
- if (*p != NUL)
- path = p;
+ p = get_tv_string_buf_chk(&argvars[1], pathbuf);
+ if (p == NULL)
+ count = -1; /* error */
+ else
+ {
+ if (*p != NUL)
+ path = p;
- if (argvars[2].v_type != VAR_UNKNOWN)
- count = get_tv_number(&argvars[2]);
+ if (argvars[2].v_type != VAR_UNKNOWN)
+ count = get_tv_number_chk(&argvars[2], NULL); /* -1: error */
+ }
}
- do
+ if (*fname != NUL && count >= 0)
{
- vim_free(fresult);
- fresult = find_file_in_path_option(first ? fname : NULL,
- first ? (int)STRLEN(fname) : 0,
- 0, first, path, dir, NULL);
- first = FALSE;
- } while (--count > 0 && fresult != NULL);
+ do
+ {
+ vim_free(fresult);
+ fresult = find_file_in_path_option(first ? fname : NULL,
+ first ? (int)STRLEN(fname) : 0,
+ 0, first, path, dir, NULL);
+ first = FALSE;
+ } while (--count > 0 && fresult != NULL);
+ }
rettv->vval.v_string = fresult;
#else
@@ -8073,53 +8252,60 @@ filter_map(argvars, rettv, map)
return;
}
- prepare_vimvar(VV_VAL, &save_val);
- expr = skipwhite(get_tv_string_buf(&argvars[1], buf));
-
- if (argvars[0].v_type == VAR_DICT)
+ expr = get_tv_string_buf_chk(&argvars[1], buf);
+ /* On type errors, the preceding call has already displayed an error
+ * message. Avoid a misleading error message for an empty string that
+ * was not passed as argument. */
+ if (expr != NULL)
{
- prepare_vimvar(VV_KEY, &save_key);
- vimvars[VV_KEY].vv_type = VAR_STRING;
+ prepare_vimvar(VV_VAL, &save_val);
+ expr = skipwhite(expr);
- ht = &d->dv_hashtab;
- hash_lock(ht);
- todo = ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi)
+ if (argvars[0].v_type == VAR_DICT)
{
- if (!HASHITEM_EMPTY(hi))
+ prepare_vimvar(VV_KEY, &save_key);
+ vimvars[VV_KEY].vv_type = VAR_STRING;
+
+ ht = &d->dv_hashtab;
+ hash_lock(ht);
+ todo = ht->ht_used;
+ for (hi = ht->ht_array; todo > 0; ++hi)
{
- --todo;
- di = HI2DI(hi);
- if (tv_check_lock(di->di_tv.v_lock, msg))
+ if (!HASHITEM_EMPTY(hi))
+ {
+ --todo;
+ di = HI2DI(hi);
+ if (tv_check_lock(di->di_tv.v_lock, msg))
+ break;
+ vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
+ if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL)
+ break;
+ if (!map && rem)
+ dictitem_remove(d, di);
+ clear_tv(&vimvars[VV_KEY].vv_tv);
+ }
+ }
+ hash_unlock(ht);
+
+ restore_vimvar(VV_KEY, &save_key);
+ }
+ else
+ {
+ for (li = l->lv_first; li != NULL; li = nli)
+ {
+ if (tv_check_lock(li->li_tv.v_lock, msg))
break;
- vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
- if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL)
+ nli = li->li_next;
+ if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL)
break;
if (!map && rem)
- dictitem_remove(d, di);
- clear_tv(&vimvars[VV_KEY].vv_tv);
+ listitem_remove(l, li);
}
}
- hash_unlock(ht);
- restore_vimvar(VV_KEY, &save_key);
- }
- else
- {
- for (li = l->lv_first; li != NULL; li = nli)
- {
- if (tv_check_lock(li->li_tv.v_lock, msg))
- break;
- nli = li->li_next;
- if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL)
- break;
- if (!map && rem)
- listitem_remove(l, li);
- }
+ restore_vimvar(VV_VAL, &save_val);
}
- restore_vimvar(VV_VAL, &save_val);
-
copy_tv(&argvars[0], rettv);
}
@@ -8150,9 +8336,15 @@ filter_map_one(tv, expr, map, remp)
}
else
{
+ int error = FALSE;
+
/* filter(): when expr is zero remove the item */
- *remp = (get_tv_number(&rettv) == 0);
+ *remp = (get_tv_number_chk(&rettv, &error) == 0);
clear_tv(&rettv);
+ /* On type error, nothing has been removed; return FAIL to stop the
+ * loop. The error message was given by get_tv_number_chk(). */
+ if (error)
+ return FAIL;
}
clear_tv(&vimvars[VV_VAL].vv_tv);
return OK;
@@ -8206,11 +8398,15 @@ f_fnamemodify(argvars, rettv)
char_u *fbuf = NULL;
char_u buf[NUMBUFLEN];
- fname = get_tv_string(&argvars[0]);
- mods = get_tv_string_buf(&argvars[1], buf);
- len = (int)STRLEN(fname);
-
- (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len);
+ fname = get_tv_string_chk(&argvars[0]);
+ mods = get_tv_string_buf_chk(&argvars[1], buf);
+ if (fname == NULL || mods == NULL)
+ fname = NULL;
+ else
+ {
+ len = (int)STRLEN(fname);
+ (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len);
+ }
rettv->v_type = VAR_STRING;
if (fname == NULL)
@@ -8381,6 +8577,9 @@ f_foldtextresult(argvars, rettv)
rettv->vval.v_string = NULL;
#ifdef FEAT_FOLDING
lnum = get_tv_lnum(argvars);
+ /* treat illegal types and illegal string values for {lnum} the same */
+ if (lnum < 0)
+ lnum = 0;
fold_count = foldedCount(curwin, lnum, &foldinfo);
if (fold_count > 0)
{
@@ -8455,8 +8654,10 @@ f_get(argvars, rettv)
{
if ((l = argvars[0].vval.v_list) != NULL)
{
- li = list_find(l, get_tv_number(&argvars[1]));
- if (li != NULL)
+ int error = FALSE;
+
+ li = list_find(l, get_tv_number_chk(&argvars[1], &error));
+ if (!error && li != NULL)
tv = &li->li_tv;
}
}
@@ -8496,9 +8697,10 @@ f_getbufvar(argvars, rettv)
char_u *varname;
dictitem_T *v;
+ (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
+ varname = get_tv_string_chk(&argvars[1]);
++emsg_off;
buf = get_buf_tv(&argvars[0]);
- varname = get_tv_string(&argvars[1]);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -8518,6 +8720,11 @@ f_getbufvar(argvars, rettv)
}
else
{
+ if (*varname == NUL)
+ /* let getbufvar({nr}, "") return the "b:" dictionary. The
+ * scope prefix before the NUL byte is required by
+ * find_var_in_ht(). */
+ varname = (char_u *)"b:" + 2;
/* look up the variable */
v = find_var_in_ht(&buf->b_vars.dv_hashtab, varname, FALSE);
if (v != NULL)
@@ -8537,17 +8744,18 @@ f_getchar(argvars, rettv)
typval_T *rettv;
{
varnumber_T n;
+ int error = FALSE;
++no_mapping;
++allow_keys;
if (argvars[0].v_type == VAR_UNKNOWN)
/* getchar(): blocking wait. */
n = safe_vgetc();
- else if (get_tv_number(&argvars[0]) == 1)
+ else if (get_tv_number_chk(&argvars[0], &error) == 1)
/* getchar(1): only check if char avail */
n = vpeekc();
- else if (vpeekc() == NUL)
- /* getchar(0) and no char avail: return zero */
+ else if (error || vpeekc() == NUL)
+ /* illegal argument or getchar(0) and no char avail: return zero */
n = 0;
else
/* getchar(0) and char avail: return char */
@@ -8858,8 +9066,9 @@ f_getline(argvars, rettv)
listitem_T *li;
lnum = get_tv_lnum(argvars);
-
- if (argvars[1].v_type == VAR_UNKNOWN)
+ if (lnum < 0)
+ rettv->vval.v_number = 0; /* failure; error message already given */
+ else if (argvars[1].v_type == VAR_UNKNOWN)
{
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
p = ml_get(lnum);
@@ -8874,7 +9083,8 @@ f_getline(argvars, rettv)
end = get_tv_lnum(&argvars[1]);
if (end < lnum)
{
- EMSG(_(e_invrange));
+ if (end >= 0) /* else: error message already given */
+ EMSG(_(e_invrange));
rettv->vval.v_number = 0;
}
else
@@ -8945,12 +9155,14 @@ f_getreg(argvars, rettv)
char_u *strregname;
int regname;
int arg2 = FALSE;
+ int error = FALSE;
if (argvars[0].v_type != VAR_UNKNOWN)
{
- strregname = get_tv_string(&argvars[0]);
+ strregname = get_tv_string_chk(&argvars[0]);
+ error = strregname == NULL;
if (argvars[1].v_type != VAR_UNKNOWN)
- arg2 = get_tv_number(&argvars[1]);
+ arg2 = get_tv_number_chk(&argvars[1], &error);
}
else
strregname = vimvars[VV_REG].vv_str;
@@ -8959,7 +9171,8 @@ f_getreg(argvars, rettv)
regname = '"';
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = get_reg_contents(regname, TRUE, arg2);
+ rettv->vval.v_string = error ? NULL :
+ get_reg_contents(regname, TRUE, arg2);
}
/*
@@ -8976,7 +9189,15 @@ f_getregtype(argvars, rettv)
long reglen = 0;
if (argvars[0].v_type != VAR_UNKNOWN)
- strregname = get_tv_string(&argvars[0]);
+ {
+ strregname = get_tv_string_chk(&argvars[0]);
+ if (strregname == NULL) /* type error; errmsg already given */
+ {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ return;
+ }
+ }
else
/* Default to v:register */
strregname = vimvars[VV_REG].vv_str;
@@ -9056,9 +9277,9 @@ f_getwinvar(argvars, rettv)
char_u *varname;
dictitem_T *v;
- ++emsg_off;
win = find_win_by_nr(&argvars[0]);
- varname = get_tv_string(&argvars[1]);
+ varname = get_tv_string_chk(&argvars[1]);
+ ++emsg_off;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -9081,6 +9302,11 @@ f_getwinvar(argvars, rettv)
}
else
{
+ if (*varname == NUL)
+ /* let getwinvar({nr}, "") return the "w:" dictionary. The
+ * scope prefix before the NUL byte is required by
+ * find_var_in_ht(). */
+ varname = (char_u *)"w:" + 2;
/* look up the variable */
v = find_var_in_ht(&win->w_vars.dv_hashtab, varname, FALSE);
if (v != NULL)
@@ -9118,10 +9344,13 @@ f_globpath(argvars, rettv)
typval_T *rettv;
{
char_u buf1[NUMBUFLEN];
+ char_u *file = get_tv_string_buf_chk(&argvars[1], buf1);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = globpath(get_tv_string(&argvars[0]),
- get_tv_string_buf(&argvars[1], buf1));
+ if (file == NULL)
+ rettv->vval.v_string = NULL;
+ else
+ rettv->vval.v_string = globpath(get_tv_string(&argvars[0]), file);
}
/*
@@ -9697,7 +9926,8 @@ f_histadd(argvars, rettv)
if (check_restricted() || check_secure())
return;
#ifdef FEAT_CMDHIST
- histype = get_histtype(get_tv_string(&argvars[0]));
+ str = get_tv_string_chk(&argvars[0]); /* NULL on type error */
+ histype = str != NULL ? get_histtype(str) : -1;
if (histype >= 0)
{
str = get_tv_string_buf(&argvars[1], buf);
@@ -9723,17 +9953,21 @@ f_histdel(argvars, rettv)
#ifdef FEAT_CMDHIST
int n;
char_u buf[NUMBUFLEN];
+ char_u *str;
- if (argvars[1].v_type == VAR_UNKNOWN)
+ str = get_tv_string_chk(&argvars[0]); /* NULL on type error */
+ if (str == NULL)
+ n = 0;
+ else if (argvars[1].v_type == VAR_UNKNOWN)
/* only one argument: clear entire history */
- n = clr_history(get_histtype(get_tv_string(&argvars[0])));
+ n = clr_history(get_histtype(str));
else if (argvars[1].v_type == VAR_NUMBER)
/* index given: remove that entry */
- n = del_history_idx(get_histtype(get_tv_string(&argvars[0])),
+ n = del_history_idx(get_histtype(str),
(int)get_tv_number(&argvars[1]));
else
/* string given: remove all matching entries */
- n = del_history_entry(get_histtype(get_tv_string(&argvars[0])),
+ n = del_history_entry(get_histtype(str),
get_tv_string_buf(&argvars[1], buf));
rettv->vval.v_number = n;
#else
@@ -9753,13 +9987,21 @@ f_histget(argvars, rettv)
#ifdef FEAT_CMDHIST
int type;
int idx;
+ char_u *str;
- type = get_histtype(get_tv_string(&argvars[0]));
- if (argvars[1].v_type == VAR_UNKNOWN)
- idx = get_history_idx(type);
+ str = get_tv_string_chk(&argvars[0]); /* NULL on type error */
+ if (str == NULL)
+ rettv->vval.v_string = NULL;
else
- idx = (int)get_tv_number(&argvars[1]);
- rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
+ {
+ type = get_histtype(str);
+ if (argvars[1].v_type == VAR_UNKNOWN)
+ idx = get_history_idx(type);
+ else
+ idx = (int)get_tv_number_chk(&argvars[1], NULL);
+ /* -1 on type error */
+ rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
+ }
#else
rettv->vval.v_string = NULL;
#endif
@@ -9778,7 +10020,9 @@ f_histnr(argvars, rettv)
int i;
#ifdef FEAT_CMDHIST
- i = get_histtype(get_tv_string(&argvars[0]));
+ char_u *history = get_tv_string_chk(&argvars[0]);
+
+ i = history == NULL ? HIST_CMD - 1 : get_histtype(history);
if (i >= HIST_CMD && i < HIST_COUNT)
i = get_history_idx(i);
else
@@ -9905,12 +10149,16 @@ f_index(argvars, rettv)
item = l->lv_first;
if (argvars[2].v_type != VAR_UNKNOWN)
{
+ int error = FALSE;
+
/* Start at specified item. Use the cached index that list_find()
* sets, so that a negative number also works. */
- item = list_find(l, get_tv_number(&argvars[2]));
+ item = list_find(l, get_tv_number_chk(&argvars[2], &error));
idx = l->lv_idx;
if (argvars[3].v_type != VAR_UNKNOWN)
- ic = get_tv_number(&argvars[3]);
+ ic = get_tv_number_chk(&argvars[3], &error);
+ if (error)
+ item = NULL;
}
for ( ; item != NULL; item = item->li_next, ++idx)
@@ -9933,11 +10181,12 @@ f_input(argvars, rettv)
typval_T *argvars;
typval_T *rettv;
{
- char_u *prompt = get_tv_string(&argvars[0]);
+ char_u *prompt = get_tv_string_chk(&argvars[0]);
char_u *p = NULL;
int c;
char_u buf[NUMBUFLEN];
int cmd_silent_save = cmd_silent;
+ char_u *defstr = (char_u *)"";
rettv->v_type = VAR_STRING;
@@ -9971,17 +10220,22 @@ f_input(argvars, rettv)
*p = c;
}
cmdline_row = msg_row;
- }
- if (argvars[1].v_type != VAR_UNKNOWN)
- stuffReadbuffSpec(get_tv_string_buf(&argvars[1], buf));
+ if (argvars[1].v_type != VAR_UNKNOWN)
+ {
+ defstr = get_tv_string_buf_chk(&argvars[1], buf);
+ if (defstr != NULL)
+ stuffReadbuffSpec(defstr);
+ }
- rettv->vval.v_string =
+ if (defstr != NULL)
+ rettv->vval.v_string =
getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr);
- /* since the user typed this, no need to wait for return */
- need_wait_return = FALSE;
- msg_didout = FALSE;
+ /* since the user typed this, no need to wait for return */
+ need_wait_return = FALSE;
+ msg_didout = FALSE;
+ }
cmd_silent = cmd_silent_save;
}
@@ -9999,21 +10253,25 @@ f_inputdialog(argvars, rettv)
{
char_u *message;
char_u buf[NUMBUFLEN];
+ char_u *defstr = (char_u *)"";
- message = get_tv_string(&argvars[0]);
- if (argvars[1].v_type != VAR_UNKNOWN)
+ message = get_tv_string_chk(&argvars[0]);
+ if (argvars[1].v_type != VAR_UNKNOWN
+ && (defstr = get_tv_string_buf_chk(&argvars[1], buf)) != NULL)
{
- STRNCPY(IObuff, get_tv_string_buf(&argvars[1], buf), IOSIZE);
+ STRNCPY(IObuff, defstr, IOSIZE);
IObuff[IOSIZE - 1] = NUL;
}
else
IObuff[0] = NUL;
- if (do_dialog(VIM_QUESTION, NULL, message, (char_u *)_("&OK\n&Cancel"),
- 1, IObuff) == 1)
+ if (message != NULL && defstr != NULL
+ && do_dialog(VIM_QUESTION, NULL, message,
+ (char_u *)_("&OK\n&Cancel"), 1, IObuff) == 1)
rettv->vval.v_string = vim_strsave(IObuff);
else
{
- if (argvars[1].v_type != VAR_UNKNOWN
+ if (message != NULL && defstr != NULL
+ && argvars[1].v_type != VAR_UNKNOWN
&& argvars[2].v_type != VAR_UNKNOWN)
rettv->vval.v_string = vim_strsave(
get_tv_string_buf(&argvars[2], buf));
@@ -10099,6 +10357,7 @@ f_insert(argvars, rettv)
long before = 0;
listitem_T *item;
list_T *l;
+ int error = FALSE;
rettv->vval.v_number = 0;
if (argvars[0].v_type != VAR_LIST)
@@ -10107,7 +10366,9 @@ f_insert(argvars, rettv)
&& !tv_check_lock(l->lv_lock, (char_u *)"insert()"))
{
if (argvars[2].v_type != VAR_UNKNOWN)
- before = get_tv_number(&argvars[2]);
+ before = get_tv_number_chk(&argvars[2], &error);
+ if (error)
+ return; /* type error; errmsg already given */
if (before == l->lv_len)
item = NULL;
@@ -10339,14 +10600,19 @@ f_join(argvars, rettv)
if (argvars[1].v_type == VAR_UNKNOWN)
sep = (char_u *)" ";
else
- sep = get_tv_string(&argvars[1]);
-
- ga_init2(&ga, (int)sizeof(char), 80);
- list_join(&ga, argvars[0].vval.v_list, sep, TRUE);
- ga_append(&ga, NUL);
+ sep = get_tv_string_chk(&argvars[1]);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)ga.ga_data;
+
+ if (sep != NULL)
+ {
+ ga_init2(&ga, (int)sizeof(char), 80);
+ list_join(&ga, argvars[0].vval.v_list, sep, TRUE);
+ ga_append(&ga, NUL);
+ rettv->vval.v_string = (char_u *)ga.ga_data;
+ }
+ else
+ rettv->vval.v_string = NULL;
}
/*
@@ -10577,9 +10843,12 @@ get_maparg(argvars, rettv, exact)
return;
if (argvars[1].v_type != VAR_UNKNOWN)
- which = get_tv_string_buf(&argvars[1], buf);
+ which = get_tv_string_buf_chk(&argvars[1], buf);
else
which = (char_u *)"";
+ if (which == NULL)
+ return;
+
mode = get_map_mode(&which, 0);
keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE);
@@ -10683,11 +10952,17 @@ find_some_match(argvars, rettv, type)
else
expr = str = get_tv_string(&argvars[0]);
- pat = get_tv_string_buf(&argvars[1], patbuf);
+ pat = get_tv_string_buf_chk(&argvars[1], patbuf);
+ if (pat == NULL)
+ goto theend;
if (argvars[2].v_type != VAR_UNKNOWN)
{
- start = get_tv_number(&argvars[2]);
+ int error = FALSE;
+
+ start = get_tv_number_chk(&argvars[2], &error);
+ if (error)
+ goto theend;
if (l != NULL)
{
li = list_find(l, start);
@@ -10705,7 +10980,9 @@ find_some_match(argvars, rettv, type)
}
if (argvars[3].v_type != VAR_UNKNOWN)
- nth = get_tv_number(&argvars[3]);
+ nth = get_tv_number_chk(&argvars[3], &error);
+ if (error)
+ goto theend;
}
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
@@ -10856,6 +11133,7 @@ max_min(argvars, rettv, domax)
{
long n = 0;
long i;
+ int error = FALSE;
if (argvars[0].v_type == VAR_LIST)
{
@@ -10868,13 +11146,13 @@ max_min(argvars, rettv, domax)
li = l->lv_first;
if (li != NULL)
{
- n = get_tv_number(&li->li_tv);
+ n = get_tv_number_chk(&li->li_tv, &error);
while (1)
{
li = li->li_next;
if (li == NULL)
break;
- i = get_tv_number(&li->li_tv);
+ i = get_tv_number_chk(&li->li_tv, &error);
if (domax ? i > n : i < n)
n = i;
}
@@ -10897,7 +11175,7 @@ max_min(argvars, rettv, domax)
if (!HASHITEM_EMPTY(hi))
{
--todo;
- i = get_tv_number(&HI2DI(hi)->di_tv);
+ i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error);
if (first)
{
n = i;
@@ -10911,7 +11189,7 @@ max_min(argvars, rettv, domax)
}
else
EMSG(_(e_listdictarg));
- rettv->vval.v_number = n;
+ rettv->vval.v_number = error ? 0 : n;
}
/*
@@ -10990,11 +11268,11 @@ f_mkdir(argvars, rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
{
if (argvars[2].v_type != VAR_UNKNOWN)
- prot = get_tv_number(&argvars[2]);
- if (STRCMP(get_tv_string(&argvars[1]), "p") == 0)
+ prot = get_tv_number_chk(&argvars[2], NULL);
+ if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0)
mkdir_recurse(dir, prot);
}
- rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
+ rettv->vval.v_number = prot != -1 ? vim_mkdir_emsg(dir, prot) : 0;
}
#endif
@@ -11050,7 +11328,7 @@ f_nextnonblank(argvars, rettv)
for (lnum = get_tv_lnum(argvars); ; ++lnum)
{
- if (lnum > curbuf->b_ml.ml_line_count)
+ if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count)
{
lnum = 0;
break;
@@ -11117,8 +11395,9 @@ f_range(argvars, rettv)
long i;
list_T *l;
listitem_T *li;
+ int error = FALSE;
- start = get_tv_number(&argvars[0]);
+ start = get_tv_number_chk(&argvars[0], &error);
if (argvars[1].v_type == VAR_UNKNOWN)
{
end = start - 1;
@@ -11126,12 +11405,14 @@ f_range(argvars, rettv)
}
else
{
- end = get_tv_number(&argvars[1]);
+ end = get_tv_number_chk(&argvars[1], &error);
if (argvars[2].v_type != VAR_UNKNOWN)
- stride = get_tv_number(&argvars[2]);
+ stride = get_tv_number_chk(&argvars[2], &error);
}
rettv->vval.v_number = 0;
+ if (error)
+ return; /* type error; errmsg already given */
if (stride == 0)
EMSG(_("E726: Stride is zero"));
else if (stride > 0 ? end < start : end > start)
@@ -11375,7 +11656,9 @@ remote_common(argvars, rettv, expr)
return;
# endif
- server_name = get_tv_string(&argvars[0]);
+ server_name = get_tv_string_chk(&argvars[0]);
+ if (server_name == NULL)
+ return; /* type error; errmsg already given */
keys = get_tv_string_buf(&argvars[1], buf);
# ifdef WIN32
if (serverSendToVim(server_name, keys, &r, &w, expr, TRUE) < 0)
@@ -11397,11 +11680,14 @@ remote_common(argvars, rettv, expr)
{
dictitem_T v;
char_u str[30];
+ char_u *idvar;
sprintf((char *)str, "0x%x", (unsigned int)w);
v.di_tv.v_type = VAR_STRING;
v.di_tv.vval.v_string = vim_strsave(str);
- set_var(get_tv_string(&argvars[2]), &v.di_tv, FALSE);
+ idvar = get_tv_string_chk(&argvars[2]);
+ if (idvar != NULL)
+ set_var(idvar, &v.di_tv, FALSE);
vim_free(v.di_tv.vval.v_string);
}
}
@@ -11436,7 +11722,12 @@ f_remote_foreground(argvars, rettv)
#ifdef FEAT_CLIENTSERVER
# ifdef WIN32
/* On Win32 it's done in this application. */
- serverForeground(get_tv_string(&argvars[0]));
+ {
+ char_u *server_name = get_tv_string_chk(&argvars[0]);
+
+ if (server_name != NULL)
+ serverForeground(server_name);
+ }
# else
/* Send a foreground() expression to the server. */
argvars[1].v_type = VAR_STRING;
@@ -11460,14 +11751,21 @@ f_remote_peek(argvars, rettv)
# ifdef WIN32
int n = 0;
# endif
+ char_u *serverid;
if (check_restricted() || check_secure())
{
rettv->vval.v_number = -1;
return;
}
+ serverid = get_tv_string_chk(&argvars[0]);
+ if (serverid == NULL)
+ {
+ rettv->vval.v_number = -1;
+ return; /* type error; errmsg already given */
+ }
# ifdef WIN32
- sscanf(get_tv_string(&argvars[0]), "%x", &n);
+ sscanf(serverid, "%x", &n);
if (n == 0)
rettv->vval.v_number = -1;
else
@@ -11481,14 +11779,18 @@ f_remote_peek(argvars, rettv)
return;
rettv->vval.v_number = serverPeekReply(X_DISPLAY,
- serverStrToWin(get_tv_string(&argvars[0])), &s);
+ serverStrToWin(serverid), &s);
# endif
if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0)
{
+ char_u *retvar;
+
v.di_tv.v_type = VAR_STRING;
v.di_tv.vval.v_string = vim_strsave(s);
- set_var(get_tv_string(&argvars[1]), &v.di_tv, FALSE);
+ retvar = get_tv_string_chk(&argvars[1]);
+ if (retvar != NULL)
+ set_var(retvar, &v.di_tv, FALSE);
vim_free(v.di_tv.vval.v_string);
}
#else
@@ -11505,19 +11807,21 @@ f_remote_read(argvars, rettv)
char_u *r = NULL;
#ifdef FEAT_CLIENTSERVER
- if (!check_restricted() && !check_secure())
+ char_u *serverid = get_tv_string_chk(&argvars[0]);
+
+ if (serverid != NULL && !check_restricted() && !check_secure())
{
# ifdef WIN32
/* The server's HWND is encoded in the 'id' parameter */
int n = 0;
- sscanf(get_tv_string(&argvars[0]), "%x", &n);
+ sscanf(serverid, "%x", &n);
if (n != 0)
r = serverGetReply((HWND)n, FALSE, TRUE, TRUE);
if (r == NULL)
# else
if (check_connection() == FAIL || serverReadReply(X_DISPLAY,
- serverStrToWin(get_tv_string(&argvars[0])), &r, FALSE) < 0)
+ serverStrToWin(serverid), &r, FALSE) < 0)
# endif
EMSG(_("E277: Unable to read a server reply"));
}
@@ -11567,15 +11871,18 @@ f_remove(argvars, rettv)
else if ((d = argvars[0].vval.v_dict) != NULL
&& !tv_check_lock(d->dv_lock, (char_u *)"remove()"))
{
- key = get_tv_string(&argvars[1]);
- di = dict_find(d, key, -1);
- if (di == NULL)
- EMSG2(_(e_dictkey), key);
- else
+ key = get_tv_string_chk(&argvars[1]);
+ if (key != NULL)
{
- *rettv = di->di_tv;
- init_tv(&di->di_tv);
- dictitem_remove(d, di);
+ di = dict_find(d, key, -1);
+ if (di == NULL)
+ EMSG2(_(e_dictkey), key);
+ else
+ {
+ *rettv = di->di_tv;
+ init_tv(&di->di_tv);
+ dictitem_remove(d, di);
+ }
}
}
}
@@ -11584,9 +11891,12 @@ f_remove(argvars, rettv)
else if ((l = argvars[0].vval.v_list) != NULL
&& !tv_check_lock(l->lv_lock, (char_u *)"remove()"))
{
- idx = get_tv_number(&argvars[1]);
- item = list_find(l, idx);
- if (item == NULL)
+ int error = FALSE;
+
+ idx = get_tv_number_chk(&argvars[1], &error);
+ if (error)
+ ; /* type error: do nothing, errmsg already given */
+ else if ((item = list_find(l, idx)) == NULL)
EMSGN(_(e_listidx), idx);
else
{
@@ -11600,9 +11910,10 @@ f_remove(argvars, rettv)
else
{
/* Remove range of items, return list with values. */
- end = get_tv_number(&argvars[2]);
- item2 = list_find(l, end);
- if (item2 == NULL)
+ end = get_tv_number_chk(&argvars[2], &error);
+ if (error)
+ ; /* type error: do nothing */
+ else if ((item2 = list_find(l, end)) == NULL)
EMSGN(_(e_listidx), end);
else
{
@@ -11964,7 +12275,9 @@ get_search_arg(varp, flagsp)
if (varp->v_type != VAR_UNKNOWN)
{
- flags = get_tv_string_buf(varp, nbuf);
+ flags = get_tv_string_buf_chk(varp, nbuf);
+ if (flags == NULL)
+ return 0; /* type error; errmsg already given */
while (*flags != NUL)
{
switch (*flags)
@@ -12051,7 +12364,7 @@ f_searchpair(argvars, rettv)
{
char_u *spat, *mpat, *epat;
char_u *skip;
- char_u *pat, *pat2, *pat3;
+ char_u *pat, *pat2 = NULL, *pat3 = NULL;
pos_T pos;
pos_T firstpos;
pos_T foundpos;
@@ -12076,9 +12389,11 @@ f_searchpair(argvars, rettv)
p_cpo = (char_u *)"";
/* Get the three pattern arguments: start, middle, end. */
- spat = get_tv_string(&argvars[0]);
- mpat = get_tv_string_buf(&argvars[1], nbuf1);
- epat = get_tv_string_buf(&argvars[2], nbuf2);
+ spat = get_tv_string_chk(&argvars[0]);
+ mpat = get_tv_string_buf_chk(&argvars[1], nbuf1);
+ epat = get_tv_string_buf_chk(&argvars[2], nbuf2);
+ if (spat == NULL || mpat == NULL || epat == NULL)
+ goto theend; /* type error */
/* Make two search patterns: start/end (pat2, for in nested pairs) and
* start/middle/end (pat3, for the top pair). */
@@ -12103,7 +12418,9 @@ f_searchpair(argvars, rettv)
|| argvars[4].v_type == VAR_UNKNOWN)
skip = (char_u *)"";
else
- skip = get_tv_string_buf(&argvars[4], nbuf3);
+ skip = get_tv_string_buf_chk(&argvars[4], nbuf3);
+ if (skip == NULL)
+ goto theend; /* type error */
save_cursor = curwin->w_cursor;
pos = curwin->w_cursor;
@@ -12198,10 +12515,12 @@ f_server2client(argvars, rettv)
{
#ifdef FEAT_CLIENTSERVER
char_u buf[NUMBUFLEN];
- char_u *server = get_tv_string(&argvars[0]);
- char_u *reply = get_tv_string_buf(&argvars[1], buf);
+ char_u *server = get_tv_string_chk(&argvars[0]);
+ char_u *reply = get_tv_string_buf_chk(&argvars[1], buf);
rettv->vval.v_number = -1;
+ if (server == NULL || reply == NULL)
+ return;
if (check_restricted() || check_secure())
return;
# ifdef FEAT_X11
@@ -12260,11 +12579,14 @@ f_setbufvar(argvars, rettv)
typval_T *varp;
char_u nbuf[NUMBUFLEN];
+ rettv->vval.v_number = 0;
+
if (check_restricted() || check_secure())
return;
+ (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
+ varname = get_tv_string_chk(&argvars[1]);
++emsg_off;
buf = get_buf_tv(&argvars[0]);
- varname = get_tv_string(&argvars[1]);
varp = &argvars[2];
if (buf != NULL && varname != NULL && varp != NULL)
@@ -12279,9 +12601,17 @@ f_setbufvar(argvars, rettv)
if (*varname == '&')
{
+ long numval;
+ char_u *strval;
+ int error = FALSE;
+
++varname;
- set_option_value(varname, get_tv_number(varp),
- get_tv_string_buf(varp, nbuf), OPT_LOCAL);
+ --emsg_off;
+ numval = get_tv_number_chk(varp, &error);
+ strval = get_tv_string_buf_chk(varp, nbuf);
+ ++emsg_off;
+ if (!error && strval != NULL)
+ set_option_value(varname, numval, strval, OPT_LOCAL);
}
else
{
@@ -12313,8 +12643,10 @@ f_setcmdpos(argvars, rettv)
typval_T *argvars;
typval_T *rettv;
{
- rettv->vval.v_number = set_cmdline_pos(
- (int)get_tv_number(&argvars[0]) - 1);
+ int pos = (int)get_tv_number(&argvars[0]) - 1;
+
+ if (pos >= 0)
+ rettv->vval.v_number = set_cmdline_pos(pos);
}
/*
@@ -12339,7 +12671,7 @@ f_setline(argvars, rettv)
li = l->lv_first;
}
else
- line = get_tv_string(&argvars[1]);
+ line = get_tv_string_chk(&argvars[1]);
rettv->vval.v_number = 0; /* OK */
for (;;)
@@ -12349,12 +12681,12 @@ f_setline(argvars, rettv)
/* list argument, get next string */
if (li == NULL)
break;
- line = get_tv_string(&li->li_tv);
+ line = get_tv_string_chk(&li->li_tv);
li = li->li_next;
}
rettv->vval.v_number = 1; /* FAIL */
- if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1)
+ if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1)
break;
if (lnum <= curbuf->b_ml.ml_line_count)
{
@@ -12406,7 +12738,9 @@ f_setqflist(argvars, rettv)
if (argvars[1].v_type == VAR_STRING)
{
- act = get_tv_string(&argvars[1]);
+ act = get_tv_string_chk(&argvars[1]);
+ if (act == NULL)
+ return; /* type error; errmsg already given */
if (*act == 'a' || *act == 'r')
action = *act;
}
@@ -12428,6 +12762,7 @@ f_setreg(argvars, rettv)
int regname;
char_u *strregname;
char_u *stropt;
+ char_u *strval;
int append;
char_u yank_type;
long block_len;
@@ -12436,10 +12771,12 @@ f_setreg(argvars, rettv)
yank_type = MAUTO;
append = FALSE;
- strregname = get_tv_string(argvars);
+ strregname = get_tv_string_chk(argvars);
rettv->vval.v_number = 1; /* FAIL is default */
- regname = (strregname == NULL ? '"' : *strregname);
+ if (strregname == NULL)
+ return; /* type error; errmsg already given */
+ regname = *strregname;
if (regname == 0 || regname == '@')
regname = '"';
else if (regname == '=')
@@ -12447,7 +12784,10 @@ f_setreg(argvars, rettv)
if (argvars[2].v_type != VAR_UNKNOWN)
{
- for (stropt = get_tv_string(&argvars[2]); *stropt != NUL; ++stropt)
+ stropt = get_tv_string_chk(&argvars[2]);
+ if (stropt == NULL)
+ return; /* type error */
+ for (; *stropt != NUL; ++stropt)
switch (*stropt)
{
case 'a': case 'A': /* append */
@@ -12473,7 +12813,9 @@ f_setreg(argvars, rettv)
}
}
- write_reg_contents_ex(regname, get_tv_string(&argvars[1]), -1,
+ strval = get_tv_string_chk(&argvars[1]);
+ if (strval != NULL)
+ write_reg_contents_ex(regname, strval, -1,
append, yank_type, block_len);
rettv->vval.v_number = 0;
}
@@ -12496,11 +12838,13 @@ f_setwinvar(argvars, rettv)
typval_T *varp;
char_u nbuf[NUMBUFLEN];
+ rettv->vval.v_number = 0;
+
if (check_restricted() || check_secure())
return;
- ++emsg_off;
win = find_win_by_nr(&argvars[0]);
- varname = get_tv_string(&argvars[1]);
+ varname = get_tv_string_chk(&argvars[1]);
+ ++emsg_off;
varp = &argvars[2];
if (win != NULL && varname != NULL && varp != NULL)
@@ -12514,9 +12858,17 @@ f_setwinvar(argvars, rettv)
if (*varname == '&')
{
+ long numval;
+ char_u *strval;
+ int error = FALSE;
+
++varname;
- set_option_value(varname, get_tv_number(varp),
- get_tv_string_buf(varp, nbuf), OPT_LOCAL);
+ --emsg_off;
+ numval = get_tv_number_chk(varp, &error);
+ strval = get_tv_string_buf_chk(varp, nbuf);
+ ++emsg_off;
+ if (!error && strval != NULL)
+ set_option_value(varname, numval, strval, OPT_LOCAL);
}
else
{
@@ -12572,6 +12924,7 @@ static int
static int item_compare_ic;
static char_u *item_compare_func;
+static int item_compare_func_err;
#define ITEM_COMPARE_FAIL 999
/*
@@ -12615,6 +12968,10 @@ item_compare2(s1, s2)
typval_T argv[2];
int dummy;
+ /* shortcut after failure in previous call; compare all items equal */
+ if (item_compare_func_err)
+ return 0;
+
/* copy the values. This is needed to be able to set v_lock to VAR_FIXED
* in the copy without changing the original list items. */
copy_tv(&(*(listitem_T **)s1)->li_tv, &argv[0]);
@@ -12629,7 +12986,10 @@ item_compare2(s1, s2)
if (res == FAIL)
res = ITEM_COMPARE_FAIL;
else
- res = get_tv_number(&rettv);
+ /* return value has wrong type */
+ res = get_tv_number_chk(&rettv, &item_compare_func_err);
+ if (item_compare_func_err)
+ res = ITEM_COMPARE_FAIL;
clear_tv(&rettv);
return res;
}
@@ -12669,10 +13029,14 @@ f_sort(argvars, rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
{
if (argvars[1].v_type == VAR_FUNC)
- item_compare_func = argvars[0].vval.v_string;
+ item_compare_func = argvars[1].vval.v_string;
else
{
- i = get_tv_number(&argvars[1]);
+ int error = FALSE;
+
+ i = get_tv_number_chk(&argvars[1], &error);
+ if (error)
+ return; /* type error; errmsg already given */
if (i == 1)
item_compare_ic = TRUE;
else
@@ -12688,6 +13052,7 @@ f_sort(argvars, rettv)
for (li = l->lv_first; li != NULL; li = li->li_next)
ptrs[i++] = li;
+ item_compare_func_err = FALSE;
/* test the compare function */
if (item_compare_func != NULL
&& item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
@@ -12699,11 +13064,14 @@ f_sort(argvars, rettv)
qsort((void *)ptrs, (size_t)len, sizeof(listitem_T *),
item_compare_func == NULL ? item_compare : item_compare2);
- /* Clear the List and append the items in the sorted order. */
- l->lv_first = l->lv_last = NULL;
- l->lv_len = 0;
- for (i = 0; i < len; ++i)
- list_append(l, ptrs[i]);
+ if (!item_compare_func_err)
+ {
+ /* Clear the List and append the items in the sorted order. */
+ l->lv_first = l->lv_last = NULL;
+ l->lv_len = 0;
+ for (i = 0; i < len; ++i)
+ list_append(l, ptrs[i]);
+ }
}
vim_free(ptrs);
@@ -12726,6 +13094,7 @@ f_split(argvars, rettv)
list_T *l;
colnr_T col = 0;
int keepempty = FALSE;
+ int typeerr = FALSE;
/* Make 'cpoptions' empty, the 'l' flag should not be used here. */
save_cpo = p_cpo;
@@ -12734,9 +13103,11 @@ f_split(argvars, rettv)
str = get_tv_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN)
{
- pat = get_tv_string_buf(&argvars[1], patbuf);
+ pat = get_tv_string_buf_chk(&argvars[1], patbuf);
+ if (pat == NULL)
+ typeerr = TRUE;
if (argvars[2].v_type != VAR_UNKNOWN)
- keepempty = get_tv_number(&argvars[2]);
+ keepempty = get_tv_number_chk(&argvars[2], &typeerr);
}
if (pat == NULL || *pat == NUL)
pat = (char_u *)"[\\x01- ]\\+";
@@ -12747,6 +13118,8 @@ f_split(argvars, rettv)
rettv->v_type = VAR_LIST;
rettv->vval.v_list = l;
++l->lv_refcount;
+ if (typeerr)
+ return;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL)
@@ -12873,14 +13246,18 @@ f_stridx(argvars, rettv)
char_u *pos;
int start_idx;
- needle = get_tv_string(&argvars[1]);
- save_haystack = haystack = get_tv_string_buf(&argvars[0], buf);
+ needle = get_tv_string_chk(&argvars[1]);
+ save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf);
rettv->vval.v_number = -1;
+ if (needle == NULL || haystack == NULL)
+ return; /* type error; errmsg already given */
if (argvars[2].v_type != VAR_UNKNOWN)
{
- start_idx = get_tv_number(&argvars[2]);
- if (start_idx >= (int)STRLEN(haystack))
+ int error = FALSE;
+
+ start_idx = get_tv_number_chk(&argvars[2], &error);
+ if (error || start_idx >= (int)STRLEN(haystack))
return;
if (start_idx >= 0)
haystack += start_idx;
@@ -12932,12 +13309,15 @@ f_strpart(argvars, rettv)
int n;
int len;
int slen;
+ int error = FALSE;
p = get_tv_string(&argvars[0]);
slen = (int)STRLEN(p);
- n = get_tv_number(&argvars[1]);
- if (argvars[2].v_type != VAR_UNKNOWN)
+ n = get_tv_number_chk(&argvars[1], &error);
+ if (error)
+ len = 0;
+ else if (argvars[2].v_type != VAR_UNKNOWN)
len = get_tv_number(&argvars[2]);
else
len = slen - n; /* default len: all bytes that are available. */
@@ -12977,19 +13357,19 @@ f_strridx(argvars, rettv)
char_u *lastmatch = NULL;
int haystack_len, end_idx;
- needle = get_tv_string(&argvars[1]);
- haystack = get_tv_string_buf(&argvars[0], buf);
+ needle = get_tv_string_chk(&argvars[1]);
+ haystack = get_tv_string_buf_chk(&argvars[0], buf);
haystack_len = STRLEN(haystack);
+
+ rettv->vval.v_number = -1;
+ if (needle == NULL || haystack == NULL)
+ return; /* type error; errmsg already given */
if (argvars[2].v_type != VAR_UNKNOWN)
{
/* Third argument: upper limit for index */
- end_idx = get_tv_number(&argvars[2]);
+ end_idx = get_tv_number_chk(&argvars[2], NULL);
if (end_idx < 0)
- {
- /* can never find a match */
- rettv->vval.v_number = -1;
- return;
- }
+ return; /* can never find a match */
}
else
end_idx = haystack_len;
@@ -13037,7 +13417,8 @@ f_submatch(argvars, rettv)
typval_T *rettv;
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = reg_submatch((int)get_tv_number(&argvars[0]));
+ rettv->vval.v_string =
+ reg_submatch((int)get_tv_number_chk(&argvars[0], NULL));
}
/*
@@ -13052,12 +13433,16 @@ f_substitute(argvars, rettv)
char_u subbuf[NUMBUFLEN];
char_u flagsbuf[NUMBUFLEN];
+ char_u *str = get_tv_string_chk(&argvars[0]);
+ char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf);
+ char_u *sub = get_tv_string_buf_chk(&argvars[2], subbuf);
+ char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf);
+
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = do_string_sub(
- get_tv_string(&argvars[0]),
- get_tv_string_buf(&argvars[1], patbuf),
- get_tv_string_buf(&argvars[2], subbuf),
- get_tv_string_buf(&argvars[3], flagsbuf));
+ if (str == NULL || pat == NULL || sub == NULL || flg == NULL)
+ rettv->vval.v_string = NULL;
+ else
+ rettv->vval.v_string = do_string_sub(str, pat, sub, flg);
}
/*
@@ -13074,12 +13459,13 @@ f_synID(argvars, rettv)
long lnum;
long col;
int trans;
+ int transerr;
- lnum = get_tv_lnum(argvars);
- col = get_tv_number(&argvars[1]) - 1;
- trans = get_tv_number(&argvars[2]);
+ lnum = get_tv_lnum(argvars); /* -1 on type error */
+ col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */
+ trans = get_tv_number_chk(&argvars[2], &transerr);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
+ if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
&& col >= 0 && col < (long)STRLEN(ml_get(lnum)))
id = syn_get_id(lnum, (colnr_T)col, trans, NULL);
#endif
@@ -13236,7 +13622,9 @@ f_system(argvars, rettv)
EMSG2(_(e_notopen), infile);
goto done;
}
- p = get_tv_string_buf(&argvars[1], buf);
+ p = get_tv_string_buf_chk(&argvars[1], buf);
+ if (p == NULL)
+ goto done; /* type error; errmsg already given */
if (fwrite(p, STRLEN(p), 1, fd) != 1)
err = TRUE;
if (fclose(fd) != 0)
@@ -13305,6 +13693,8 @@ f_taglist(argvars, rettv)
tag_pattern = get_tv_string(&argvars[0]);
rettv->vval.v_number = FALSE;
+ if (*tag_pattern == NUL)
+ return;
l = list_alloc();
if (l != NULL)
@@ -13407,39 +13797,8 @@ f_toupper(argvars, rettv)
typval_T *argvars;
typval_T *rettv;
{
- char_u *p;
-
- p = vim_strsave(get_tv_string(&argvars[0]));
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = p;
-
- if (p != NULL)
- while (*p != NUL)
- {
-#ifdef FEAT_MBYTE
- int l;
-
- if (enc_utf8)
- {
- int c, uc;
-
- c = utf_ptr2char(p);
- uc = utf_toupper(c);
- l = utf_ptr2len_check(p);
- /* TODO: reallocate string when byte count changes. */
- if (utf_char2len(uc) == l)
- utf_char2bytes(uc, p);
- p += l;
- }
- else if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
- p += l; /* skip multi-byte character */
- else
-#endif
- {
- *p = TOUPPER_LOC(*p); /* note that toupper() can be a macro */
- p++;
- }
- }
+ rettv->vval.v_string = strup_save(get_tv_string(&argvars[0]));
}
/*
@@ -13468,12 +13827,14 @@ f_tr(argvars, rettv)
garray_T ga;
instr = get_tv_string(&argvars[0]);
- fromstr = get_tv_string_buf(&argvars[1], buf);
- tostr = get_tv_string_buf(&argvars[2], buf2);
+ fromstr = get_tv_string_buf_chk(&argvars[1], buf);
+ tostr = get_tv_string_buf_chk(&argvars[2], buf2);
/* Default return value: empty string. */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+ if (fromstr == NULL || tostr == NULL)
+ return; /* type error; errmsg already given */
ga_init2(&ga, (int)sizeof(char), 80);
#ifdef FEAT_MBYTE
@@ -13718,8 +14079,10 @@ f_winnr(argvars, rettv)
if (argvars[0].v_type != VAR_UNKNOWN)
{
- arg = get_tv_string(&argvars[0]);
- if (STRCMP(arg, "$") == 0)
+ arg = get_tv_string_chk(&argvars[0]);
+ if (arg == NULL)
+ nr = 0; /* type error; errmsg already given */
+ else if (STRCMP(arg, "$") == 0)
twin = lastwin;
else if (STRCMP(arg, "#") == 0)
{
@@ -13806,9 +14169,11 @@ find_win_by_nr(vp)
#endif
int nr;
- nr = get_tv_number(vp);
+ nr = get_tv_number_chk(vp, NULL);
#ifdef FEAT_WINDOWS
+ if (nr < 0)
+ return NULL;
if (nr == 0)
return curwin;
@@ -13906,7 +14271,9 @@ var2fpos(varp, lnum)
static pos_T pos;
pos_T *pp;
- name = get_tv_string(varp);
+ name = get_tv_string_chk(varp);
+ if (name == NULL)
+ return NULL;
if (name[0] == '.') /* cursor */
return &curwin->w_cursor;
if (name[0] == '\'') /* mark */
@@ -14621,18 +14988,31 @@ init_tv(varp)
/*
* Get the number value of a variable.
* If it is a String variable, uses vim_str2nr().
+ * For incompatible types, return 0.
+ * get_tv_number_chk() is similar to get_tv_number(), but informs the
+ * caller of incompatible types: it sets *denote to TRUE if "denote"
+ * is not NULL or returns -1 otherwise.
*/
static long
get_tv_number(varp)
typval_T *varp;
{
+ int error = FALSE;
+
+ return get_tv_number_chk(varp, &error); /* return 0L on error */
+}
+
+ static long
+get_tv_number_chk(varp, denote)
+ typval_T *varp;
+ int *denote;
+{
long n = 0L;
switch (varp->v_type)
{
case VAR_NUMBER:
- n = (long)(varp->vval.v_number);
- break;
+ return (long)(varp->vval.v_number);
case VAR_FUNC:
EMSG(_("E703: Using a Funcref as a number"));
break;
@@ -14640,7 +15020,7 @@ get_tv_number(varp)
if (varp->vval.v_string != NULL)
vim_str2nr(varp->vval.v_string, NULL, NULL,
TRUE, TRUE, &n, NULL);
- break;
+ return n;
case VAR_LIST:
EMSG(_("E745: Using a List as a number"));
break;
@@ -14651,11 +15031,16 @@ get_tv_number(varp)
EMSG2(_(e_intern2), "get_tv_number()");
break;
}
+ if (denote == NULL) /* useful for values that must be unsigned */
+ n = -1;
+ else
+ *denote = TRUE;
return n;
}
/*
* Get the lnum from the first argument. Also accepts ".", "$", etc.
+ * Returns -1 on error.
*/
static linenr_T
get_tv_lnum(argvars)
@@ -14664,7 +15049,7 @@ get_tv_lnum(argvars)
typval_T rettv;
linenr_T lnum;
- lnum = get_tv_number(&argvars[0]);
+ lnum = get_tv_number_chk(&argvars[0], NULL);
if (lnum == 0) /* no valid number, try using line() */
{
rettv.v_type = VAR_NUMBER;
@@ -14682,6 +15067,8 @@ get_tv_lnum(argvars)
* get_tv_string_buf() uses a given buffer.
* If the String variable has never been set, return an empty string.
* Never returns NULL;
+ * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return
+ * NULL on error.
*/
static char_u *
get_tv_string(varp)
@@ -14697,6 +15084,25 @@ get_tv_string_buf(varp, buf)
typval_T *varp;
char_u *buf;
{
+ char_u *res = get_tv_string_buf_chk(varp, buf);
+
+ return res != NULL ? res : (char_u *)"";
+}
+
+ static char_u *
+get_tv_string_chk(varp)
+ typval_T *varp;
+{
+ static char_u mybuf[NUMBUFLEN];
+
+ return get_tv_string_buf_chk(varp, mybuf);
+}
+
+ static char_u *
+get_tv_string_buf_chk(varp, buf)
+ typval_T *varp;
+ char_u *buf;
+{
switch (varp->v_type)
{
case VAR_NUMBER:
@@ -14714,12 +15120,12 @@ get_tv_string_buf(varp, buf)
case VAR_STRING:
if (varp->vval.v_string != NULL)
return varp->vval.v_string;
- break;
+ return (char_u *)"";
default:
EMSG2(_(e_intern2), "get_tv_string_buf()");
break;
}
- return (char_u *)"";
+ return NULL;
}
/*
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 9bef62db4..834614566 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -5349,13 +5349,13 @@ find_help_tags(arg, num_matches, matches, keep_lang)
int i;
static char *(mtable[]) = {"*", "g*", "[*", "]*", ":*",
"/*", "/\\*", "\"*", "/\\(\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??",
+ "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
"/\\?", "/\\z(\\)", "\\=", ":s\\=",
"[count]", "[quotex]", "[range]",
"[pattern]", "\\|", "\\%$"};
static char *(rtable[]) = {"star", "gstar", "[star", "]star", ":star",
"/star", "/\\\\star", "quotestar", "/\\\\(\\\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??",
+ "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
"/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=",
"\\[count]", "\\[quotex]", "\\[range]",
"\\[pattern]", "\\\\bar", "/\\\\%\\$"};
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 09e12bcc0..fbb0f5a78 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -8139,6 +8139,7 @@ theend:
#if ((defined(FEAT_SESSION) || defined(FEAT_EVAL)) && defined(vim_mkdir)) \
|| defined(PROTO)
+/*ARGSUSED*/
int
vim_mkdir_emsg(name, prot)
char_u *name;
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 5d04ad301..51b2f251c 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2997,10 +2997,10 @@ nextwild(xp, type, options)
v = OK;
if (v == OK)
{
- vim_strncpy(&ccline.cmdbuff[ccline.cmdpos + difflen],
- &ccline.cmdbuff[ccline.cmdpos],
- ccline.cmdlen - ccline.cmdpos + 1);
- STRNCPY(&ccline.cmdbuff[i], p2, STRLEN(p2));
+ mch_memmove(&ccline.cmdbuff[ccline.cmdpos + difflen],
+ &ccline.cmdbuff[ccline.cmdpos],
+ (size_t)(ccline.cmdlen - ccline.cmdpos + 1));
+ mch_memmove(&ccline.cmdbuff[i], p2, STRLEN(p2));
ccline.cmdlen += difflen;
ccline.cmdpos += difflen;
}
diff --git a/src/gui_w32.c b/src/gui_w32.c
index 8d0a4cef0..8046a8df0 100644
--- a/src/gui_w32.c
+++ b/src/gui_w32.c
@@ -556,7 +556,6 @@ _OnWindowPosChanged(
static int
_DuringSizing(
- HWND hwnd,
UINT fwSide,
LPRECT lprc)
{
@@ -681,7 +680,7 @@ _WndProc(
#endif
case WM_SIZING: /* HANDLE_MSG doesn't seem to handle this one */
- return _DuringSizing(hwnd, (UINT)wParam, (LPRECT)lParam);
+ return _DuringSizing((UINT)wParam, (LPRECT)lParam);
case WM_MOUSEWHEEL:
_OnMouseWheel(hwnd, HIWORD(wParam));
@@ -842,12 +841,14 @@ gui_mch_set_parent(char *title)
}
}
+#ifndef FEAT_OLE
static void
ole_error(char *arg)
{
EMSG2(_("E243: Argument not supported: \"-%s\"; Use the OLE version."),
arg);
}
+#endif
/*
* Parse the GUI related command-line arguments. Any arguments used are
@@ -1260,6 +1261,7 @@ get_work_area(RECT *spi_rect)
/*
* Set the size of the window to the given width and height in pixels.
*/
+/*ARGSUSED*/
void
gui_mch_set_shellsize(int width, int height,
int min_width, int min_height, int base_width, int base_height)
@@ -2045,7 +2047,7 @@ gui_mch_draw_string(
{
int x;
int offset;
- const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
+ static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
y = FILL_Y(row + 1) - 1;
for (x = FILL_X(col); x < FILL_X(col + len); ++x)
@@ -2451,6 +2453,7 @@ gui_mch_menu_grey(
* pressed, return that button's ID - IDCANCEL (2), which is the button's
* number.
*/
+/*ARGSUSED*/
static LRESULT CALLBACK
dialog_callback(
HWND hwnd,
@@ -4037,6 +4040,7 @@ delete_tooltip(beval)
DestroyWindow(beval->balloon);
}
+/*ARGSUSED*/
static VOID CALLBACK
BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
HWND hwnd;
@@ -4078,6 +4082,7 @@ BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
}
}
+/*ARGSUSED*/
void
gui_mch_disable_beval_area(beval)
BalloonEval *beval;
@@ -4087,6 +4092,7 @@ gui_mch_disable_beval_area(beval)
// TRACE0("gui_mch_disable_beval_area }}}");
}
+/*ARGSUSED*/
void
gui_mch_enable_beval_area(beval)
BalloonEval *beval;
@@ -4162,6 +4168,7 @@ gui_mch_create_beval_area(target, mesg, mesgCB, clientData)
return beval;
}
+/*ARGSUSED*/
static void
Handle_WM_Notify(hwnd, pnmh)
HWND hwnd;
diff --git a/src/gui_w48.c b/src/gui_w48.c
index 4729f9b1d..be2354681 100644
--- a/src/gui_w48.c
+++ b/src/gui_w48.c
@@ -314,7 +314,7 @@ static LOGFONT norm_logfont;
static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
#endif
-#ifdef DEBUG
+#ifdef DEBUG_PRINT_ERROR
/*
* Print out the last Windows error message
*/
@@ -330,7 +330,7 @@ print_windows_error(void)
TRACE1("Error: %s\n", lpMsgBuf);
LocalFree(lpMsgBuf);
}
-#endif /* DEBUG */
+#endif
/*
* Cursor blink functions.
@@ -445,6 +445,7 @@ gui_mch_start_blink(void)
* Call-back routines.
*/
+/*ARGSUSED*/
static VOID CALLBACK
_OnTimer(
HWND hwnd,
@@ -467,6 +468,7 @@ _OnTimer(
s_wait_timer = 0;
}
+/*ARGSUSED*/
static void
_OnDeadChar(
HWND hwnd,
@@ -555,6 +557,7 @@ char_to_string(int ch, char_u *string, int slen)
/*
* Key hit, add it to the input buffer.
*/
+/*ARGSUSED*/
static void
_OnChar(
HWND hwnd,
@@ -577,6 +580,7 @@ _OnChar(
/*
* Alt-Key hit, add it to the input buffer.
*/
+/*ARGSUSED*/
static void
_OnSysChar(
HWND hwnd,
@@ -656,6 +660,7 @@ _OnMouseEvent(
gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
}
+/*ARGSUSED*/
static void
_OnMouseButtonDown(
HWND hwnd,
@@ -756,6 +761,7 @@ _OnMouseButtonDown(
}
}
+/*ARGSUSED*/
static void
_OnMouseMoveOrRelease(
HWND hwnd,
@@ -832,6 +838,7 @@ gui_mswin_find_menu(
return pMenu;
}
+/*ARGSUSED*/
static void
_OnMenu(
HWND hwnd,
@@ -1255,6 +1262,7 @@ gui_mch_get_font(
* Return the name of font "font" in allocated memory.
* Don't know how to get the actual name, thus use the provided name.
*/
+/*ARGSUSED*/
char_u *
gui_mch_get_fontname(font, name)
GuiFont font;
@@ -1937,6 +1945,7 @@ gui_mch_enable_menu(int flag)
#endif
}
+/*ARGSUSED*/
void
gui_mch_set_menu_pos(
int x,
@@ -2291,6 +2300,7 @@ _OnEndSession(void)
* Get this message when the user clicks on the cross in the top right corner
* of a Windows95 window.
*/
+/*ARGSUSED*/
static void
_OnClose(
HWND hwnd)
@@ -2344,6 +2354,7 @@ _OnPaint(
}
}
+/*ARGSUSED*/
static void
_OnSize(
HWND hwnd,
@@ -2588,6 +2599,7 @@ gui_mch_insert_lines(
}
+/*ARGSUSED*/
void
gui_mch_exit(int rc)
{
@@ -2657,6 +2669,7 @@ logfont2name(LOGFONT lf)
* Initialise vim to use the font with the given name.
* Return FAIL if the font could not be loaded, OK otherwise.
*/
+/*ARGSUSED*/
int
gui_mch_init_font(char_u *font_name, int fontset)
{
@@ -2761,6 +2774,7 @@ gui_mch_newfont()
/*
* Set the window title
*/
+/*ARGSUSED*/
void
gui_mch_settitle(
char_u *title,
@@ -3162,6 +3176,7 @@ gui_mch_browse(
}
#endif /* FEAT_BROWSE */
+/*ARGSUSED*/
static void
_OnDropFiles(
HWND hwnd,
@@ -3229,6 +3244,7 @@ _OnDropFiles(
#endif
}
+/*ARGSUSED*/
static int
_OnScroll(
HWND hwnd,
@@ -3361,6 +3377,7 @@ _OnScroll(
* Return pointer to buffer in "tofree".
* Returns zero when out of memory.
*/
+/*ARGSUSED*/
int
get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
{
@@ -3473,7 +3490,7 @@ get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
}
}
- if (pnew != NUL)
+ if (pnew != NULL)
*pnew++ = NUL;
while (*p == ' ' || *p == '\t')
++p; /* advance until a non-space */
diff --git a/src/if_cscope.c b/src/if_cscope.c
index 7163c8a03..7653308f1 100644
--- a/src/if_cscope.c
+++ b/src/if_cscope.c
@@ -1226,11 +1226,13 @@ GetWin32Error()
return msg;
}
#endif
+
/*
* PRIVATE: cs_insert_filelist
*
* insert a new cscope database filename into the filelist
*/
+/*ARGSUSED*/
static int
cs_insert_filelist(fname, ppath, flags, sb)
char *fname;
diff --git a/src/if_python.c b/src/if_python.c
index b799047e9..8aca83518 100644
--- a/src/if_python.c
+++ b/src/if_python.c
@@ -384,10 +384,13 @@ static int initialised = 0;
#if PYTHON_API_VERSION < 1007 /* Python 1.4 */
typedef PyObject PyThreadState;
-#endif /* Python 1.4 */
+#endif
-#ifndef PY_CAN_RECURSE
+#ifdef PY_CAN_RECURSE
+static PyGILState_STATE pygilstate = PyGILState_UNLOCKED;
+#else
static PyThreadState *saved_python_thread = NULL;
+#endif
/*
* Suspend a thread of the Python interpreter, other threads are allowed to
@@ -396,7 +399,11 @@ static PyThreadState *saved_python_thread = NULL;
static void
Python_SaveThread(void)
{
+#ifdef PY_CAN_RECURSE
+ PyGILState_Release(pygilstate);
+#else
saved_python_thread = PyEval_SaveThread();
+#endif
}
/*
@@ -406,10 +413,13 @@ Python_SaveThread(void)
static void
Python_RestoreThread(void)
{
+#ifdef PY_CAN_RECURSE
+ pygilstate = PyGILState_Ensure();
+#else
PyEval_RestoreThread(saved_python_thread);
saved_python_thread = NULL;
-}
#endif
+}
/*
* obtain a lock on the Vim data structures
@@ -430,11 +440,17 @@ python_end()
{
#ifdef DYNAMIC_PYTHON
if (hinstPython && Py_IsInitialized())
+ {
+ Python_RestoreThread(); /* enter python */
Py_Finalize();
+ }
end_dynamic_python();
#else
if (Py_IsInitialized())
+ {
+ Python_RestoreThread(); /* enter python */
Py_Finalize();
+ }
#endif
}
@@ -470,11 +486,7 @@ Python_Init(void)
goto fail;
/* the first python thread is vim's, release the lock */
-#ifdef PY_CAN_RECURSE
- PyEval_SaveThread();
-#else
Python_SaveThread();
-#endif
initialised = 1;
}
@@ -497,9 +509,7 @@ fail:
static void
DoPythonCommand(exarg_T *eap, const char *cmd)
{
-#ifdef PY_CAN_RECURSE
- PyGILState_STATE pygilstate;
-#else
+#ifndef PY_CAN_RECURSE
static int recursive = 0;
#endif
#if defined(MACOS) && !defined(MACOS_X_UNIX)
@@ -544,19 +554,11 @@ DoPythonCommand(exarg_T *eap, const char *cmd)
}
#endif
-#ifdef PY_CAN_RECURSE
- pygilstate = PyGILState_Ensure();
-#else
Python_RestoreThread(); /* enter python */
-#endif
PyRun_SimpleString((char *)(cmd));
-#ifdef PY_CAN_RECURSE
- PyGILState_Release(pygilstate);
-#else
Python_SaveThread(); /* leave python */
-#endif
#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
if (saved_locale != NULL)
diff --git a/src/misc1.c b/src/misc1.c
index d4dabe634..7209a1ae2 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -3137,6 +3137,27 @@ get_number(colon)
return n;
}
+/*
+ * Ask the user to enter a number.
+ */
+ int
+prompt_for_number()
+{
+ int i;
+
+ /* When using ":silent" assume that <CR> was entered. */
+ MSG_PUTS(_("Choice number (<Enter> cancels): "));
+ i = get_number(TRUE);
+ if (KeyTyped) /* don't call wait_return() now */
+ {
+ msg_putchar('\n');
+ cmdline_row = msg_row - 1;
+ need_wait_return = FALSE;
+ msg_didany = FALSE;
+ }
+ return i;
+}
+
void
msgmore(n)
long n;
diff --git a/src/misc2.c b/src/misc2.c
index b3ab1f241..6775932c8 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -1082,6 +1082,69 @@ vim_strup(p)
}
}
+#if defined(FEAT_EVAL) || defined(FEAT_SYN_HL) || defined(PROTO)
+/*
+ * Make string "s" all upper-case and return it in allocated memory.
+ * Handles multi-byte characters as well as possible.
+ * Returns NULL when out of memory.
+ */
+ char_u *
+strup_save(orig)
+ char_u *orig;
+{
+ char_u *p;
+ char_u *res;
+
+ res = p = vim_strsave(orig);
+
+ if (res != NULL)
+ while (*p != NUL)
+ {
+# ifdef FEAT_MBYTE
+ int l;
+
+ if (enc_utf8)
+ {
+ int c, uc;
+ int nl;
+ char_u *s;
+
+ c = utf_ptr2char(p);
+ 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_check(p);
+ nl = utf_char2len(uc);
+ if (nl != l)
+ {
+ s = alloc((unsigned)STRLEN(res) + 1 + nl - l);
+ if (s == NULL)
+ break;
+ mch_memmove(s, res, p - res);
+ STRCPY(s + (p - res) + nl, p + l);
+ p = s + (p - res);
+ vim_free(res);
+ res = s;
+ }
+
+ utf_char2bytes(uc, p);
+ p += nl;
+ }
+ else if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
+ p += l; /* skip multi-byte character */
+ else
+# endif
+ {
+ *p = TOUPPER_LOC(*p); /* note that toupper() can be a macro */
+ p++;
+ }
+ }
+
+ return res;
+}
+#endif
+
/*
* copy a space a number of times
*/
@@ -1131,43 +1194,16 @@ del_trailing_spaces(ptr)
}
/*
- * This is here because strncpy() does not guarantee successful results when
- * the to and from strings overlap. It is only currently called from
- * nextwild() which copies part of the command line to another part of the
- * command line. This produced garbage when expanding files etc in the middle
- * of the command line (on my terminal, anyway) -- webb.
- * Note: strncpy() pads the remainder of the buffer with NUL bytes,
- * vim_strncpy() doesn't do that.
+ * Like strncpy(), but always terminate the result with one NUL.
*/
void
vim_strncpy(to, from, len)
- char_u *to;
- char_u *from;
- int len;
+ char_u *to;
+ char_u *from;
+ int len;
{
- int i;
-
- if (to <= from)
- {
- while (len-- && *from)
- *to++ = *from++;
- if (len >= 0)
- *to = *from; /* Copy NUL */
- }
- else
- {
- for (i = 0; i < len; i++)
- {
- to++;
- if (*from++ == NUL)
- {
- i++;
- break;
- }
- }
- for (; i > 0; i--)
- *--to = *--from;
- }
+ STRNCPY(to, from, len);
+ to[len] = NUL;
}
/*
diff --git a/src/normal.c b/src/normal.c
index e4bd3964d..fa5e355e7 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -4685,6 +4685,11 @@ dozet:
spell_add_word(ptr, len, nchar == 'w');
}
break;
+
+ case '?': /* "z?": suggestions for a badly spelled word */
+ if (!checkclearopq(cap->oap))
+ spell_suggest();
+ break;
#endif
default: clearopbeep(cap->oap);
@@ -6106,7 +6111,7 @@ nv_brackets(cap)
setpcmark();
for (n = 0; n < cap->count1; ++n)
if (spell_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
- cap->nchar == 's' ? TRUE : FALSE) == FAIL)
+ cap->nchar == 's' ? TRUE : FALSE, FALSE) == FAIL)
{
clearopbeep(cap->oap);
break;
diff --git a/src/option.c b/src/option.c
index 14f8ade78..c0ea094f6 100644
--- a/src/option.c
+++ b/src/option.c
@@ -5683,20 +5683,32 @@ did_set_string_option(opt_idx, varp, new_value_alloced, oldval, errbuf,
#endif
#ifdef FEAT_SYN_HL
- /* When 'spelllang' is set and there is a window for this buffer in which
- * 'spell' is set load the wordlists. */
- else if (varp == &(curbuf->b_p_spl))
+ /* When 'spelllang' or 'spellfile' is set and there is a window for this
+ * buffer in which 'spell' is set load the wordlists. */
+ else if (varp == &(curbuf->b_p_spl) || varp == &(curbuf->b_p_spf))
{
win_T *wp;
+ int l;
- FOR_ALL_WINDOWS(wp)
- if (wp->w_buffer == curbuf && wp->w_p_spell)
- {
- errmsg = did_set_spelllang(curbuf);
+ if (varp == &(curbuf->b_p_spf))
+ {
+ l = STRLEN(curbuf->b_p_spf);
+ if (l > 0 && (l < 4 || STRCMP(curbuf->b_p_spf + l - 4,
+ ".add") != 0))
+ errmsg = e_invarg;
+ }
+
+ if (errmsg == NULL)
+ {
+ FOR_ALL_WINDOWS(wp)
+ if (wp->w_buffer == curbuf && wp->w_p_spell)
+ {
+ errmsg = did_set_spelllang(curbuf);
# ifdef FEAT_WINDOWS
- break;
+ break;
# endif
- }
+ }
+ }
}
#endif
diff --git a/src/os_mswin.c b/src/os_mswin.c
index 8a47dd16f..b0c137fca 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -332,6 +332,7 @@ mch_settitle(
* 2: Just restore icon (which we don't have)
* 3: Restore title and icon (which we don't have)
*/
+/*ARGSUSED*/
void
mch_restore_title(
int which)
@@ -370,6 +371,7 @@ mch_can_restore_icon()
* When 'shellslash' set do it the other way around.
* Return OK or FAIL.
*/
+/*ARGSUSED*/
int
mch_FullName(
char_u *fname,
@@ -525,6 +527,7 @@ vim_stat(const char *name, struct stat *stp)
}
#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
+/*ARGSUSED*/
void
mch_settmode(int tmode)
{
@@ -701,6 +704,7 @@ mch_chdir(char *path)
* Switching off termcap mode is only allowed when Columns is 80, otherwise a
* crash may result. It's always allowed on NT or when running the GUI.
*/
+/*ARGSUSED*/
int
can_end_termcap_mode(
int give_msg)
@@ -732,6 +736,7 @@ mch_char_avail()
/*
* set screen mode, always fails.
*/
+/*ARGSUSED*/
int
mch_screenmode(
char_u *arg)
@@ -1028,6 +1033,7 @@ typedef struct
/*
* Make vim the owner of the current selection. Return OK upon success.
*/
+/*ARGSUSED*/
int
clip_mch_own_selection(VimClipboard *cbd)
{
@@ -1041,6 +1047,7 @@ clip_mch_own_selection(VimClipboard *cbd)
/*
* Make vim NOT the owner of the current selection.
*/
+/*ARGSUSED*/
void
clip_mch_lose_selection(VimClipboard *cbd)
{
@@ -1228,7 +1235,6 @@ clip_mch_request_selection(VimClipboard *cbd)
#ifdef FEAT_MBYTE
HGLOBAL rawh = NULL;
#endif
- char_u *hMemStr = NULL;
int str_size = 0;
int maxlen;
size_t n;
@@ -1327,7 +1333,7 @@ clip_mch_request_selection(VimClipboard *cbd)
{
if ((hMem = GetClipboardData(CF_TEXT)) != NULL)
{
- str = hMemStr = (char_u *)GlobalLock(hMem);
+ str = (char_u *)GlobalLock(hMem);
/* The length is either what our metadata says or the strlen().
* But limit it to the GlobalSize() for safety. */
@@ -1587,6 +1593,7 @@ clip_mch_set_selection(VimClipboard *cbd)
/*
* Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
*/
+/*ARGSUSED*/
void
DumpPutS(
const char *psz)
@@ -1736,6 +1743,7 @@ swap_me(COLORREF colorref)
return colorref;
}
+/*ARGSUSED*/
static BOOL CALLBACK
PrintDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -1798,6 +1806,7 @@ PrintDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
return FALSE;
}
+/*ARGSUSED*/
static BOOL CALLBACK
AbortProc(HDC hdcPrn, int iCode)
{
@@ -2247,6 +2256,7 @@ mch_print_begin(prt_settings_T *psettings)
return (ret > 0);
}
+/*ARGSUSED*/
void
mch_print_end(prt_settings_T *psettings)
{
@@ -2935,8 +2945,7 @@ typedef struct
HWND server; /* server window */
char_u *reply; /* reply string */
int expr_result; /* 0 for REPLY, 1 for RESULT 2 for error */
-}
-reply_T;
+} reply_T;
static garray_T reply_list = {0, 0, sizeof(reply_T), 5, 0};
@@ -3186,6 +3195,7 @@ points_to_pixels(char_u *str, char_u **end, int vertical, int pprinter_dc)
return pixels;
}
+/*ARGSUSED*/
static int CALLBACK
font_enumproc(
ENUMLOGFONT *elf,
diff --git a/src/os_w32exe.c b/src/os_w32exe.c
index ac9bf3c4a..8c48fb338 100644
--- a/src/os_w32exe.c
+++ b/src/os_w32exe.c
@@ -38,6 +38,7 @@ void _cdecl SaveInst(HINSTANCE hInst);
void (_cdecl *pSaveInst)(HINSTANCE);
#endif
+/*ARGSUSED*/
int WINAPI
WinMain(
HINSTANCE hInstance,
diff --git a/src/os_win32.c b/src/os_win32.c
index a55f46c19..82a3ce0f9 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -300,8 +300,7 @@ dyn_libintl_init(char *libname)
}
/* The bind_textdomain_codeset() function is optional. */
- (FARPROC)dyn_libintl_bind_textdomain_codeset =
- (FARPROC)GetProcAddress(hLibintlDLL,
+ dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
"bind_textdomain_codeset");
if (dyn_libintl_bind_textdomain_codeset == NULL)
dyn_libintl_bind_textdomain_codeset =
@@ -322,18 +321,21 @@ dyn_libintl_end()
dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
}
+/*ARGSUSED*/
static char *
null_libintl_gettext(const char *msgid)
{
return (char*)msgid;
}
+/*ARGSUSED*/
static char *
null_libintl_bindtextdomain(const char *domainname, const char *dirname)
{
return NULL;
}
+/*ARGSUSED*/
static char *
null_libintl_bind_textdomain_codeset(const char *domainname,
const char *codeset)
@@ -341,6 +343,7 @@ null_libintl_bind_textdomain_codeset(const char *domainname,
return NULL;
}
+/*ARGSUSED*/
static char *
null_libintl_textdomain(const char *domainname)
{
@@ -446,6 +449,7 @@ mch_windows95(void)
static int old_num_windows;
static int num_windows;
+/*ARGSUSED*/
static BOOL CALLBACK
win32ssynch_cb(HWND hwnd, LPARAM lparam)
{
@@ -762,6 +766,7 @@ decode_key_event(
* For the GUI the mouse handling is in gui_w32.c.
*/
# ifdef FEAT_GUI_W32
+/*ARGSUSED*/
void
mch_setmouse(int on)
{
@@ -2211,6 +2216,7 @@ mch_exit(int r)
/*
* Do we have an interactive window?
*/
+/*ARGSUSED*/
int
mch_check_win(
int argc,
@@ -3444,6 +3450,7 @@ termcap_mode_end(void)
#ifdef FEAT_GUI_W32
+/*ARGSUSED*/
void
mch_write(
char_u *s,
@@ -4099,6 +4106,7 @@ mch_write(
/*
* Delay for half a second.
*/
+/*ARGSUSED*/
void
mch_delay(
long msec,
@@ -4716,7 +4724,7 @@ copy_infostreams(char_u *from, char_u *to)
/* Advance to the next stream. We might try seeking too far,
* but BackupSeek() doesn't skip over stream borders, thus
* that's OK. */
- (void)BackupSeek(sh, sid.Size.LowPart, sid.Size.u.HighPart,
+ (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
&lo, &hi, &context);
}
diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
index 5a42c13d1..c2ae831fc 100644
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -42,6 +42,7 @@ void change_warning __ARGS((int col));
int ask_yesno __ARGS((char_u *str, int direct));
int get_keystroke __ARGS((void));
int get_number __ARGS((int colon));
+int prompt_for_number __ARGS((void));
void msgmore __ARGS((long n));
void beep_flush __ARGS((void));
void vim_beep __ARGS((void));
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index 365f2ea95..38d50a985 100644
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -31,6 +31,7 @@ char_u *vim_strsave_escaped_ext __ARGS((char_u *string, char_u *esc_chars, int c
char_u *vim_strsave_up __ARGS((char_u *string));
char_u *vim_strnsave_up __ARGS((char_u *string, int len));
void vim_strup __ARGS((char_u *p));
+char_u *strup_save __ARGS((char_u *orig));
void copy_spaces __ARGS((char_u *ptr, size_t count));
void copy_chars __ARGS((char_u *ptr, size_t count, int c));
void del_trailing_spaces __ARGS((char_u *ptr));
diff --git a/src/proto/spell.pro b/src/proto/spell.pro
index 71d6dbc1e..c932b1f71 100644
--- a/src/proto/spell.pro
+++ b/src/proto/spell.pro
@@ -1,6 +1,6 @@
/* spell.c */
int spell_check __ARGS((win_T *wp, char_u *ptr, int *attrp));
-int spell_move_to __ARGS((int dir, int allwords));
+int spell_move_to __ARGS((int dir, int allwords, int curline));
char_u *did_set_spelllang __ARGS((buf_T *buf));
void spell_reload __ARGS((void));
void put_bytes __ARGS((FILE *fd, long_u nr, int len));
@@ -8,4 +8,5 @@ void ex_mkspell __ARGS((exarg_T *eap));
void ex_spell __ARGS((exarg_T *eap));
void spell_add_word __ARGS((char_u *word, int len, int bad));
void init_spell_chartab __ARGS((void));
+void spell_suggest __ARGS((void));
/* vim: set ft=c : */
diff --git a/src/spell.c b/src/spell.c
index cbe2f9fc6..2b37f632f 100644
--- a/src/spell.c
+++ b/src/spell.c
@@ -29,12 +29,18 @@
*/
/*
+ * Use this to let the score depend in how much a suggestion sounds like the
+ * bad word. It's quite slow and doesn't make the sorting much better....
+ * #define SOUNDFOLD_SCORE
+ */
+
+/*
* Vim spell file format: <HEADER> <SUGGEST> <LWORDTREE> <KWORDTREE>
*
* <HEADER>: <fileID> <regioncnt> <regionname> ...
* <charflagslen> <charflags> <fcharslen> <fchars>
*
- * <fileID> 10 bytes "VIMspell05"
+ * <fileID> 10 bytes "VIMspell06"
* <regioncnt> 1 byte number of regions following (8 supported)
* <regionname> 2 bytes Region name: ca, au, etc. Lower case.
* First <regionname> is region 1.
@@ -47,11 +53,41 @@
* <fchars> N bytes Folded characters, first one is for character 128.
*
*
- * <SUGGEST> : <suggestlen> <more> ...
+ * <SUGGEST> : <repcount> <rep> ...
+ * <salflags> <salcount> <sal> ...
+ * <maplen> <mapstr>
+ *
+ * <repcount> 2 bytes number of <rep> items, MSB first.
+ *
+ * <rep> : <repfromlen> <repfrom> <reptolen> <repto>
+ *
+ * <repfromlen> 1 byte length of <repfrom>
+ *
+ * <repfrom> N bytes "from" part of replacement
+ *
+ * <reptolen> 1 byte length of <repto>
+ *
+ * <repto> N bytes "to" part of replacement
+ *
+ * <salflags> 1 byte flags for soundsalike conversion:
+ * SAL_F0LLOWUP
+ * SAL_COLLAPSE
+ * SAL_REM_ACCENTS
+ *
+ * <sal> : <salfromlen> <salfrom> <saltolen> <salto>
+ *
+ * <salfromlen> 1 byte length of <salfrom>
*
- * <suggestlen> 4 bytes Length of <SUGGEST> in bytes, excluding
- * <suggestlen>. MSB first.
- * <more> To be defined.
+ * <salfrom> N bytes "from" part of soundsalike
+ *
+ * <saltolen> 1 byte length of <salto>
+ *
+ * <salto> N bytes "to" part of soundsalike
+ *
+ * <maplen> 2 bytes length of <mapstr>, MSB first
+ *
+ * <mapstr> N bytes String with sequences of similar characters,
+ * separated by slashes.
*
*
* <LWORDTREE>: <wordtree>
@@ -90,9 +126,7 @@
*
* <KWORDTREE>: <wordtree>
*
- *
* All text characters are in 'encoding', but stored as single bytes.
- * The region name is ASCII.
*/
#if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64)
@@ -107,7 +141,9 @@
# include <fcntl.h>
#endif
-#define MAXWLEN 250 /* assume max. word len is this many bytes */
+#define MAXWLEN 250 /* Assume max. word len is this many bytes.
+ Some places assume a word length fits in a
+ byte, thus it can't be above 255. */
/* Flags used for a word. */
#define WF_REGION 0x01 /* region byte follows */
@@ -115,21 +151,23 @@
#define WF_ALLCAP 0x04 /* word must be all capitals */
#define WF_RARE 0x08 /* rare word */
#define WF_BANNED 0x10 /* bad word */
+#define WF_KEEPCAP 0x80 /* keep-case word */
-#define WF_KEEPCAP 0x100 /* keep-case word (not stored in file) */
+#define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP)
#define BY_NOFLAGS 0 /* end of word without flags or region */
#define BY_FLAGS 1 /* end of word, flag byte follows */
#define BY_INDEX 2 /* child is shared, index follows */
#define BY_SPECIAL BY_INDEX /* hightest special byte value */
-/* Info from "REP" entries in ".aff" file used in af_rep.
- * TODO: This is not used yet. Either use it or remove it. */
-typedef struct repentry_S
+/* Info from "REP" and "SAL" entries in ".aff" file used in si_rep, sl_rep,
+ * si_sal and sl_sal.
+ * One replacement: from "ft_from" to "ft_to". */
+typedef struct fromto_S
{
- char_u *re_from;
- char_u *re_to;
-} repentry_T;
+ char_u *ft_from;
+ char_u *ft_to;
+} fromto_T;
/*
* Structure used to store words and other info for one language, loaded from
@@ -152,22 +190,34 @@ struct slang_S
slang_T *sl_next; /* next language */
char_u *sl_name; /* language name "en", "en.rare", "nl", etc. */
char_u *sl_fname; /* name of .spl file */
- int sl_add; /* TRUE if it's an addition. */
+ int sl_add; /* TRUE if it's a .add file. */
char_u *sl_fbyts; /* case-folded word bytes */
int *sl_fidxs; /* case-folded word indexes */
char_u *sl_kbyts; /* keep-case word bytes */
int *sl_kidxs; /* keep-case word indexes */
- char_u *sl_try; /* "TRY" from .aff file TODO: not used */
- garray_T sl_rep; /* list of repentry_T entries from REP lines
- * TODO not used */
char_u sl_regions[17]; /* table with up to 8 region names plus NUL */
- int sl_error; /* error while loading */
+
+ garray_T sl_rep; /* list of fromto_T entries from REP lines */
+ short sl_rep_first[256]; /* indexes where byte first appears, -1 if
+ there is none */
+ garray_T sl_sal; /* list of fromto_T entries from SAL lines */
+ short sl_sal_first[256]; /* indexes where byte first appears, -1 if
+ there is none */
+ int sl_followup; /* SAL followup */
+ int sl_collapse; /* SAL collapse_result */
+ int sl_rem_accents; /* SAL remove_accents */
+ char_u *sl_map; /* string with similar chars from MAP lines */
};
/* First language that is loaded, start of the linked list of loaded
* languages. */
static slang_T *first_lang = NULL;
+/* Flags used in .spl file for soundsalike flags. */
+#define SAL_F0LLOWUP 1
+#define SAL_COLLAPSE 2
+#define SAL_REM_ACCENTS 4
+
/*
* Structure used in "b_langp", filled from 'spelllang'.
*/
@@ -188,16 +238,71 @@ typedef struct langp_S
#define SP_LOCAL 2
#define SP_BAD 3
-#define VIMSPELLMAGIC "VIMspell05" /* string at start of Vim spell file */
+#define VIMSPELLMAGIC "VIMspell06" /* string at start of Vim spell file */
#define VIMSPELLMAGICL 10
/*
+ * Information used when looking for suggestions.
+ */
+typedef struct suginfo_S
+{
+ garray_T su_ga; /* suggestions, contains "suggest_T" */
+ int su_maxscore; /* maximum score for adding to su_ga */
+ int su_icase; /* accept words with wrong case */
+ int su_icase_add; /* add matches while ignoring case */
+ char_u *su_badptr; /* start of bad word in line */
+ int su_badlen; /* length of detected bad word in line */
+ char_u su_badword[MAXWLEN]; /* bad word truncated at su_badlen */
+ char_u su_fbadword[MAXWLEN]; /* su_badword case-folded */
+ hashtab_T su_banned; /* table with banned words */
+#ifdef SOUNDFOLD_SCORE
+ slang_T *su_slang; /* currently used slang_T */
+ char_u su_salword[MAXWLEN]; /* soundfolded badword */
+#endif
+} suginfo_T;
+
+/* One word suggestion. Used in "si_ga". */
+typedef struct suggest_S
+{
+ char_u *st_word; /* suggested word, allocated string */
+ int st_orglen; /* length of replaced text */
+ int st_score; /* lower is better */
+} suggest_T;
+
+#define SUG(sup, i) (((suggest_T *)(sup)->su_ga.ga_data)[i])
+
+/* Number of suggestions displayed. */
+#define SUG_PROMPT_COUNT ((int)Rows - 2)
+
+/* Threshold for sorting and cleaning up suggestions. */
+#define SUG_CLEANUP_COUNT (SUG_PROMPT_COUNT + 50)
+
+/* score for various changes */
+#define SCORE_SPLIT 99 /* split bad word */
+#define SCORE_ICASE 52 /* slightly different case */
+#define SCORE_ALLCAP 120 /* need all-cap case */
+#define SCORE_REGION 70 /* word is for different region */
+#define SCORE_RARE 180 /* rare word */
+
+/* score for edit distance */
+#define SCORE_SWAP 90 /* swap two characters */
+#define SCORE_SWAP3 110 /* swap two characters in three */
+#define SCORE_REP 87 /* REP replacement */
+#define SCORE_SUBST 93 /* substitute a character */
+#define SCORE_SIMILAR 33 /* substitute a similar character */
+#define SCORE_DEL 96 /* delete a character */
+#define SCORE_INS 94 /* insert a character */
+
+#define SCORE_MAXINIT 350 /* Initial maximum score: higher == slower.
+ * 350 allows for about three changes. */
+#define SCORE_MAXMAX 999999 /* accept any score */
+
+/*
* Structure to store info for word matching.
*/
typedef struct matchinf_S
{
langp_T *mi_lp; /* info for language and region */
- slang_T *mi_slang; /* info for the language */
/* pointers to original text to be checked */
char_u *mi_word; /* start of word being checked */
@@ -248,23 +353,56 @@ static int set_spell_finish __ARGS((spelltab_T *new_st));
# define SPELL_ISWORDP(p) (spelltab.st_isw[*(p)])
#endif
+/*
+ * Struct to keep the state at each level in spell_try_change().
+ */
+typedef struct trystate_S
+{
+ int ts_state; /* state at this level, STATE_ */
+ int ts_score; /* score */
+ int ts_curi; /* index in list of child nodes */
+ int ts_fidx; /* index in fword[], case-folded bad word */
+ int ts_fidxtry; /* ts_fidx at which bytes may be changed */
+ int ts_twordlen; /* valid length of tword[] */
+ int ts_arridx; /* index in tree array, start of node */
+ char_u ts_save_prewordlen; /* saved "prewordlen" */
+ int ts_save_splitoff; /* su_splitoff saved here */
+ int ts_save_badflags; /* badflags saved here */
+} trystate_T;
+
static slang_T *slang_alloc __ARGS((char_u *lang));
static void slang_free __ARGS((slang_T *lp));
static void slang_clear __ARGS((slang_T *lp));
static void find_word __ARGS((matchinf_T *mip, int keepcap));
+static int spell_valid_case __ARGS((int origflags, int treeflags));
static void spell_load_lang __ARGS((char_u *lang));
static char_u *spell_enc __ARGS((void));
static void spell_load_cb __ARGS((char_u *fname, void *cookie));
-static void spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp));
+static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent));
static int read_tree __ARGS((FILE *fd, char_u *byts, int *idxs, int maxidx, int startidx));
static int find_region __ARGS((char_u *rp, char_u *region));
static int captype __ARGS((char_u *word, char_u *end));
-static void spell_reload_one __ARGS((char_u *fname));
+static void spell_reload_one __ARGS((char_u *fname, int added_word));
static int set_spell_charflags __ARGS((char_u *flags, int cnt, char_u *upp));
static int set_spell_chartab __ARGS((char_u *fol, char_u *low, char_u *upp));
static void write_spell_chartab __ARGS((FILE *fd));
static int spell_isupper __ARGS((int c));
static int spell_casefold __ARGS((char_u *p, int len, char_u *buf, int buflen));
+static void onecap_copy __ARGS((char_u *word, int len, char_u *wcopy, int upper));
+static void spell_try_change __ARGS((suginfo_T *su));
+static int try_deeper __ARGS((suginfo_T *su, trystate_T *stack, int depth, int score_add));
+static void find_keepcap_word __ARGS((slang_T *slang, char_u *fword, char_u *kword));
+static void spell_try_soundalike __ARGS((suginfo_T *su));
+static void make_case_word __ARGS((char_u *fword, char_u *cword, int flags));
+static int similar_chars __ARGS((slang_T *slang, int c1, int c2));
+static void add_suggestion __ARGS((suginfo_T *su, char_u *goodword, int use_score));
+static void add_banned __ARGS((suginfo_T *su, char_u *word));
+static int was_banned __ARGS((suginfo_T *su, char_u *word));
+static void free_banned __ARGS((suginfo_T *su));
+static void cleanup_suggestions __ARGS((suginfo_T *su));
+static void spell_soundfold __ARGS((slang_T *slang, char_u *inword, char_u *res));
+static int spell_edit_score __ARGS((char_u *badword, char_u *goodword));
+
static char *e_format = N_("E759: Format error in spell file");
@@ -274,6 +412,10 @@ static char *e_format = N_("E759: Format error in spell file");
* "*attrp" is set to the attributes for a badly spelled word. For a non-word
* or when it's OK it remains unchanged.
* This must only be called when 'spelllang' is not empty.
+ *
+ * "sug" is normally NULL. When looking for suggestions it points to
+ * suginfo_T. It's passed as a void pointer to keep the struct local.
+ *
* Returns the length of the word in bytes, also when it's OK, so that the
* caller can skip over the word.
*/
@@ -305,6 +447,7 @@ spell_check(wp, ptr, attrp)
/* Find the end of the word. */
mi.mi_word = ptr;
mi.mi_fend = ptr;
+
if (SPELL_ISWORDP(mi.mi_fend))
{
/* Make case-folded copy of the characters until the next non-word
@@ -313,18 +456,15 @@ spell_check(wp, ptr, attrp)
{
mb_ptr_adv(mi.mi_fend);
} while (*mi.mi_fend != NUL && SPELL_ISWORDP(mi.mi_fend));
-
- /* Check the caps type of the word. */
- mi.mi_capflags = captype(ptr, mi.mi_fend);
}
- else
- /* No word characters, caps type is always zero. */
- mi.mi_capflags = 0;
/* We always use the characters up to the next non-word character,
* also for bad words. */
mi.mi_end = mi.mi_fend;
- mi.mi_cend = mi.mi_fend;
+
+ /* Check caps type later. */
+ mi.mi_capflags = 0;
+ mi.mi_cend = NULL;
/* Include one non-word character so that we can check for the
* word end. */
@@ -349,7 +489,6 @@ spell_check(wp, ptr, attrp)
/* Check for a matching word in case-folded words. */
find_word(&mi, FALSE);
- /* Try keep-case words. */
find_word(&mi, TRUE);
}
@@ -563,17 +702,13 @@ find_word(mip, keepcap)
/* Check that the word is in the required case. */
if (mip->mi_cend != mip->mi_word + wlen)
{
- /* mi_capflags was set for a different word
- * length, need to do it again. */
+ /* mi_capflags was set for a different word length, need
+ * to do it again. */
mip->mi_cend = mip->mi_word + wlen;
- mip->mi_capflags = captype(mip->mi_word,
- mip->mi_cend);
+ mip->mi_capflags = captype(mip->mi_word, mip->mi_cend);
}
- valid = (mip->mi_capflags == WF_ALLCAP
- || ((flags & WF_ALLCAP) == 0
- && ((flags & WF_ONECAP) == 0
- || mip->mi_capflags == WF_ONECAP)));
+ valid = spell_valid_case(mip->mi_capflags, flags);
}
if (valid)
@@ -617,15 +752,31 @@ find_word(mip, keepcap)
}
}
+/*
+ * Check case flags for a word. Return TRUE if the word has the requested
+ * case.
+ */
+ static int
+spell_valid_case(origflags, treeflags)
+ int origflags; /* flags for the checked word. */
+ int treeflags; /* flags for the word in the spell tree */
+{
+ return (origflags == WF_ALLCAP
+ || ((treeflags & (WF_ALLCAP | WF_KEEPCAP)) == 0
+ && ((treeflags & WF_ONECAP) == 0 || origflags == WF_ONECAP)));
+}
+
/*
* Move to next spell error.
+ * "curline" is TRUE for "z?": find word under/after cursor in the same line.
* Return OK if found, FAIL otherwise.
*/
int
-spell_move_to(dir, allwords)
+spell_move_to(dir, allwords, curline)
int dir; /* FORWARD or BACKWARD */
int allwords; /* TRUE for "[s" and "]s" */
+ int curline;
{
linenr_T lnum;
pos_T found_pos;
@@ -680,7 +831,8 @@ spell_move_to(dir, allwords)
if (dir == BACKWARD
|| lnum > curwin->w_cursor.lnum
|| (lnum == curwin->w_cursor.lnum
- && (colnr_T)(p - line)
+ && (colnr_T)(curline ? p - line + len
+ : p - line)
> curwin->w_cursor.col))
{
if (has_syntax)
@@ -722,6 +874,9 @@ spell_move_to(dir, allwords)
break;
}
+ if (curline)
+ return FAIL; /* only check cursor line */
+
/* Advance to next line. */
if (dir == BACKWARD)
{
@@ -819,7 +974,8 @@ slang_alloc(lang)
if (lp != NULL)
{
lp->sl_name = vim_strsave(lang);
- ga_init2(&lp->sl_rep, sizeof(repentry_T), 4);
+ ga_init2(&lp->sl_rep, sizeof(fromto_T), 10);
+ ga_init2(&lp->sl_sal, sizeof(fromto_T), 10);
}
return lp;
}
@@ -844,6 +1000,10 @@ slang_free(lp)
slang_clear(lp)
slang_T *lp;
{
+ garray_T *gap;
+ fromto_T *ftp;
+ int round;
+
vim_free(lp->sl_fbyts);
lp->sl_fbyts = NULL;
vim_free(lp->sl_kbyts);
@@ -852,9 +1012,21 @@ slang_clear(lp)
lp->sl_fidxs = NULL;
vim_free(lp->sl_kidxs);
lp->sl_kidxs = NULL;
- ga_clear(&lp->sl_rep);
- vim_free(lp->sl_try);
- lp->sl_try = NULL;
+
+ for (round = 1; round <= 2; ++round)
+ {
+ gap = round == 1 ? &lp->sl_rep : &lp->sl_sal;
+ while (gap->ga_len > 0)
+ {
+ ftp = &((fromto_T *)gap->ga_data)[--gap->ga_len];
+ vim_free(ftp->ft_from);
+ vim_free(ftp->ft_to);
+ }
+ ga_clear(gap);
+ }
+
+ vim_free(lp->sl_map);
+ lp->sl_map = NULL;
}
/*
@@ -866,7 +1038,7 @@ spell_load_cb(fname, cookie)
char_u *fname;
void *cookie; /* points to the language name */
{
- spell_load_file(fname, (char_u *)cookie, NULL);
+ (void)spell_load_file(fname, (char_u *)cookie, NULL, FALSE);
}
/*
@@ -877,12 +1049,14 @@ spell_load_cb(fname, cookie)
* the language name, "old_lp" is NULL. Will allocate an slang_T.
* - To reload a spell file that was changed. "lang" is NULL and "old_lp"
* points to the existing slang_T.
+ * Returns the slang_T the spell file was loaded into. NULL for error.
*/
- static void
-spell_load_file(fname, lang, old_lp)
+ static slang_T *
+spell_load_file(fname, lang, old_lp, silent)
char_u *fname;
char_u *lang;
slang_T *old_lp;
+ int silent; /* no error if file doesn't exist */
{
FILE *fd;
char_u buf[MAXWLEN + 1];
@@ -895,11 +1069,22 @@ spell_load_file(fname, lang, old_lp)
int cnt, ccnt;
char_u *fol;
slang_T *lp = NULL;
+ garray_T *gap;
+ fromto_T *ftp;
+ int rr;
+ short *first;
fd = mch_fopen((char *)fname, "r");
if (fd == NULL)
{
- EMSG2(_(e_notopen), fname);
+ if (!silent)
+ EMSG2(_(e_notopen), fname);
+ else if (p_verbose > 2)
+ {
+ verbose_enter();
+ smsg((char_u *)e_notopen, fname);
+ verbose_leave();
+ }
goto endFAIL;
}
if (p_verbose > 2)
@@ -1000,12 +1185,88 @@ formerr:
goto formerr;
}
- /* <SUGGEST> : <suggestlen> <more> ... */
- /* TODO, just skip this for now */
- i = (getc(fd) << 24) + (getc(fd) << 16) + (getc(fd) << 8) + getc(fd);
- while (i-- > 0)
- if (getc(fd) == EOF) /* <suggestlen> */
- goto truncerr;
+ /* <SUGGEST> : <repcount> <rep> ...
+ * <salflags> <salcount> <sal> ...
+ * <maplen> <mapstr> */
+ for (round = 1; round <= 2; ++round)
+ {
+ if (round == 1)
+ {
+ gap = &lp->sl_rep;
+ first = lp->sl_rep_first;
+ }
+ else
+ {
+ gap = &lp->sl_sal;
+ first = lp->sl_sal_first;
+
+ i = getc(fd); /* <salflags> */
+ if (i & SAL_F0LLOWUP)
+ lp->sl_followup = TRUE;
+ if (i & SAL_COLLAPSE)
+ lp->sl_collapse = TRUE;
+ if (i & SAL_REM_ACCENTS)
+ lp->sl_rem_accents = TRUE;
+ }
+
+ cnt = (getc(fd) << 8) + getc(fd); /* <repcount> or <salcount> */
+ if (cnt < 0)
+ goto formerr;
+
+ if (ga_grow(gap, cnt) == FAIL)
+ goto endFAIL;
+ for (; gap->ga_len < cnt; ++gap->ga_len)
+ {
+ /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */
+ /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */
+ ftp = &((fromto_T *)gap->ga_data)[gap->ga_len];
+ for (rr = 1; rr <= 2; ++rr)
+ {
+ ccnt = getc(fd);
+ if (ccnt < 0)
+ {
+ if (rr == 2)
+ vim_free(ftp->ft_from);
+ goto formerr;
+ }
+ if ((p = alloc(ccnt + 1)) == NULL)
+ {
+ if (rr == 2)
+ vim_free(ftp->ft_from);
+ goto endFAIL;
+ }
+ for (i = 0; i < ccnt; ++i)
+ p[i] = getc(fd); /* <repfrom> or <salfrom> */
+ p[i] = NUL;
+ if (rr == 1)
+ ftp->ft_from = p;
+ else
+ ftp->ft_to = p;
+ }
+ }
+
+ /* Fill the first-index table. */
+ for (i = 0; i < 256; ++i)
+ first[i] = -1;
+ for (i = 0; i < gap->ga_len; ++i)
+ {
+ ftp = &((fromto_T *)gap->ga_data)[i];
+ if (first[*ftp->ft_from] == -1)
+ first[*ftp->ft_from] = i;
+ }
+ }
+
+ cnt = (getc(fd) << 8) + getc(fd); /* <maplen> */
+ if (cnt < 0)
+ goto formerr;
+ p = alloc(cnt + 1);
+ if (p == NULL)
+ goto endFAIL;
+ for (i = 0; i < cnt; ++i)
+ p[i] = getc(fd); /* <mapstr> */
+ p[i] = NUL;
+ lp->sl_map = p;
+
/* round 1: <LWORDTREE>
* round 2: <KWORDTREE> */
@@ -1063,13 +1324,18 @@ endFAIL:
/* truncating the name signals the error to spell_load_lang() */
*lang = NUL;
if (lp != NULL && old_lp == NULL)
+ {
slang_free(lp);
+ lp = NULL;
+ }
endOK:
if (fd != NULL)
fclose(fd);
sourcing_name = save_sourcing_name;
sourcing_lnum = save_sourcing_lnum;
+
+ return lp;
}
/*
@@ -1177,9 +1443,18 @@ did_set_spelllang(buf)
slang_T *lp;
int c;
char_u lbuf[MAXWLEN + 1];
+ char_u spf_name[MAXPATHL];
+ int did_spf = FALSE;
ga_init2(&ga, sizeof(langp_T), 2);
+ /* Get the name of the .spl file associated with 'spellfile'. */
+ if (*buf->b_p_spf == NUL)
+ did_spf = TRUE;
+ else
+ vim_snprintf((char *)spf_name, sizeof(spf_name), "%s.spl",
+ buf->b_p_spf);
+
/* loop over comma separated languages. */
for (lang = buf->b_p_spl; *lang != NUL; lang = e)
{
@@ -1206,8 +1481,7 @@ did_set_spelllang(buf)
if (lp == NULL)
{
/* Not found, load the language. */
- STRNCPY(lbuf, lang, e - lang);
- lbuf[e - lang] = NUL;
+ vim_strncpy(lbuf, lang, e - lang);
if (region != NULL)
mch_memmove(lbuf + 2, lbuf + 5, e - lang - 4);
spell_load_lang(lbuf);
@@ -1247,12 +1521,38 @@ did_set_spelllang(buf)
LANGP_ENTRY(ga, ga.ga_len)->lp_slang = lp;
LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask;
++ga.ga_len;
+
+ /* Check if this is the 'spellfile' spell file. */
+ if (fullpathcmp(spf_name, lp->sl_fname, FALSE) == FPC_SAME)
+ did_spf = TRUE;
}
if (*e == ',')
++e;
}
+ /*
+ * Make sure the 'spellfile' file is loaded. It may be in 'runtimepath',
+ * then it's probably loaded above already. Otherwise load it here.
+ */
+ if (!did_spf)
+ {
+ for (lp = first_lang; lp != NULL; lp = lp->sl_next)
+ if (fullpathcmp(spf_name, lp->sl_fname, FALSE) == FPC_SAME)
+ break;
+ if (lp == NULL)
+ {
+ vim_strncpy(lbuf, gettail(spf_name), 2);
+ lp = spell_load_file(spf_name, lbuf, NULL, TRUE);
+ }
+ if (lp != NULL && ga_grow(&ga, 1) == OK)
+ {
+ LANGP_ENTRY(ga, ga.ga_len)->lp_slang = lp;
+ LANGP_ENTRY(ga, ga.ga_len)->lp_region = REGION_ALL;
+ ++ga.ga_len;
+ }
+ }
+
/* Add a NULL entry to mark the end of the list. */
if (ga_grow(&ga, 1) == FAIL)
{
@@ -1292,7 +1592,7 @@ find_region(rp, region)
}
/*
- * Return type of word:
+ * Return case type of word:
* w word 0
* Word WF_ONECAP
* W WORD WF_ALLCAP
@@ -1301,7 +1601,7 @@ find_region(rp, region)
static int
captype(word, end)
char_u *word;
- char_u *end;
+ char_u *end; /* When NULL use up to NUL byte. */
{
char_u *p;
int c;
@@ -1311,7 +1611,7 @@ captype(word, end)
/* find first letter */
for (p = word; !SPELL_ISWORDP(p); mb_ptr_adv(p))
- if (p >= end)
+ if (end == NULL ? *p == NUL : p >= end)
return 0; /* only non-word characters, illegal word */
#ifdef FEAT_MBYTE
if (has_mbyte)
@@ -1325,7 +1625,7 @@ captype(word, end)
* Need to check all letters to find a word with mixed upper/lower.
* But a word with an upper char only at start is a ONECAP.
*/
- for ( ; p < end; mb_ptr_adv(p))
+ for ( ; end == NULL ? *p != NUL : p < end; mb_ptr_adv(p))
if (SPELL_ISWORDP(p))
{
#ifdef FEAT_MBYTE
@@ -1402,18 +1702,26 @@ spell_reload()
* Reload the spell file "fname" if it's loaded.
*/
static void
-spell_reload_one(fname)
+spell_reload_one(fname, added_word)
char_u *fname;
+ int added_word; /* invoked through "zg" */
{
slang_T *lp;
+ int didit = FALSE;
for (lp = first_lang; lp != NULL; lp = lp->sl_next)
if (fullpathcmp(fname, lp->sl_fname, FALSE) == FPC_SAME)
{
slang_clear(lp);
- spell_load_file(fname, NULL, lp);
+ (void)spell_load_file(fname, NULL, lp, FALSE);
redraw_all_later(NOT_VALID);
+ didit = TRUE;
}
+
+ /* When "zg" was used and the file wasn't loaded yet, should redo
+ * 'spelllang' to get it loaded. */
+ if (added_word && !didit)
+ did_set_spelllang(curbuf);
}
@@ -1429,12 +1737,10 @@ spell_reload_one(fname)
typedef struct afffile_S
{
char_u *af_enc; /* "SET", normalized, alloc'ed string or NULL */
- char_u *af_try; /* "TRY" line in "af_enc" encoding */
int af_rar; /* RAR ID for rare word */
int af_kep; /* KEP ID for keep-case word */
hashtab_T af_pref; /* hashtable for prefixes, affheader_T */
hashtab_T af_suff; /* hashtable for suffixes, affheader_T */
- garray_T af_rep; /* list of repentry_T entries from REP lines */
} afffile_T;
typedef struct affentry_S affentry_T;
@@ -1510,9 +1816,18 @@ typedef struct spellinfo_S
int si_region_count; /* number of regions supported (1 when there
are no regions) */
char_u si_region_name[16]; /* region names (if count > 1) */
+
+ garray_T si_rep; /* list of fromto_T entries from REP lines */
+ garray_T si_sal; /* list of fromto_T entries from SAL lines */
+ int si_followup; /* soundsalike: ? */
+ int si_collapse; /* soundsalike: ? */
+ int si_rem_accents; /* soundsalike: remove accents */
+ garray_T si_map; /* MAP info concatenated */
} spellinfo_T;
static afffile_T *spell_read_aff __ARGS((char_u *fname, spellinfo_T *spin));
+static void add_fromto __ARGS((spellinfo_T *spin, garray_T *gap, char_u *from, char_u *to));
+static int sal_to_bool __ARGS((char_u *s));
static int has_non_ascii __ARGS((char_u *s));
static void spell_free_aff __ARGS((afffile_T *aff));
static int spell_read_dic __ARGS((char_u *fname, spellinfo_T *spin, afffile_T *affile));
@@ -1529,11 +1844,11 @@ static int node_compress __ARGS((wordnode_T *node, hashtab_T *ht, int *tot));
static int node_equal __ARGS((wordnode_T *n1, wordnode_T *n2));
static void write_vim_spell __ARGS((char_u *fname, spellinfo_T *spin));
static int put_tree __ARGS((FILE *fd, wordnode_T *node, int index, int regionmask));
-static void mkspell __ARGS((int fcount, char_u **fnames, int ascii, int overwrite, int verbose));
+static void mkspell __ARGS((int fcount, char_u **fnames, int ascii, int overwrite, int added_word));
static void init_spellfile __ARGS((void));
/*
- * Read an affix ".aff" file.
+ * Read the affix file "fname".
* Returns an afffile_T, NULL for complete failure.
*/
static afffile_T *
@@ -1557,6 +1872,10 @@ spell_read_aff(fname, spin)
char_u *fol = NULL;
char_u *upp = NULL;
static char *e_affname = N_("Affix name too long in %s line %d: %s");
+ int do_rep;
+ int do_sal;
+ int do_map;
+ int found_map = FALSE;
/*
* Open the file.
@@ -1578,6 +1897,15 @@ spell_read_aff(fname, spin)
verbose_leave();
}
+ /* Only do REP lines when not done in another .aff file already. */
+ do_rep = spin->si_rep.ga_len == 0;
+
+ /* Only do SAL lines when not done in another .aff file already. */
+ do_sal = spin->si_sal.ga_len == 0;
+
+ /* Only do MAP lines when not done in another .aff file already. */
+ do_map = spin->si_map.ga_len == 0;
+
/*
* Allocate and init the afffile_T structure.
*/
@@ -1586,7 +1914,6 @@ spell_read_aff(fname, spin)
return NULL;
hash_init(&aff->af_pref);
hash_init(&aff->af_suff);
- ga_init2(&aff->af_rep, (int)sizeof(repentry_T), 20);
/*
* Read all the lines in the file one by one.
@@ -1660,12 +1987,11 @@ spell_read_aff(fname, spin)
}
else if (STRCMP(items[0], "NOSPLITSUGS") == 0 && itemcnt == 1)
{
- /* ignored */
+ /* ignored, we always split */
}
- else if (STRCMP(items[0], "TRY") == 0 && itemcnt == 2
- && aff->af_try == NULL)
+ else if (STRCMP(items[0], "TRY") == 0 && itemcnt == 2)
{
- aff->af_try = getroom_save(&spin->si_blocks, items[1]);
+ /* ignored, we look in the tree for what chars may appear */
}
else if (STRCMP(items[0], "RAR") == 0 && itemcnt == 2
&& aff->af_rar == 0)
@@ -1784,18 +2110,55 @@ spell_read_aff(fname, spin)
upp = vim_strsave(items[1]);
}
else if (STRCMP(items[0], "REP") == 0 && itemcnt == 2)
+ {
/* Ignore REP count */;
+ if (!isdigit(*items[1]))
+ smsg((char_u *)_("Expected REP count in %s line %d"),
+ fname, lnum);
+ }
else if (STRCMP(items[0], "REP") == 0 && itemcnt == 3)
{
- repentry_T *rp;
-
/* REP item */
- if (ga_grow(&aff->af_rep, 1) == FAIL)
- break;
- rp = ((repentry_T *)aff->af_rep.ga_data) + aff->af_rep.ga_len;
- rp->re_from = getroom_save(&spin->si_blocks, items[1]);
- rp->re_to = getroom_save(&spin->si_blocks, items[2]);
- ++aff->af_rep.ga_len;
+ if (do_rep)
+ add_fromto(spin, &spin->si_rep, items[1], items[2]);
+ }
+ else if (STRCMP(items[0], "MAP") == 0 && itemcnt == 2)
+ {
+ /* MAP item or count */
+ if (!found_map)
+ {
+ /* First line contains the count. */
+ found_map = TRUE;
+ if (!isdigit(*items[1]))
+ smsg((char_u *)_("Expected MAP count in %s line %d"),
+ fname, lnum);
+ }
+ else if (do_map)
+ {
+ /* We simply concatenate all the MAP strings, separated by
+ * slashes. */
+ ga_concat(&spin->si_map, items[1]);
+ ga_append(&spin->si_map, '/');
+ }
+ }
+ else if (STRCMP(items[0], "SAL") == 0 && itemcnt == 3)
+ {
+ if (do_sal)
+ {
+ /* SAL item (sounds-a-like)
+ * Either one of the known keys or a from-to pair. */
+ if (STRCMP(items[1], "followup") == 0)
+ spin->si_followup = sal_to_bool(items[2]);
+ else if (STRCMP(items[1], "collapse_result") == 0)
+ spin->si_collapse = sal_to_bool(items[2]);
+ else if (STRCMP(items[1], "remove_accents") == 0)
+ spin->si_rem_accents = sal_to_bool(items[2]);
+ else
+ /* when "to" is "_" it means empty */
+ add_fromto(spin, &spin->si_sal, items[1],
+ STRCMP(items[2], "_") == 0 ? (char_u *)""
+ : items[2]);
+ }
}
else
smsg((char_u *)_("Unrecognized item in %s line %d: %s"),
@@ -1834,6 +2197,41 @@ spell_read_aff(fname, spin)
}
/*
+ * Add a from-to item to "gap". Used for REP and SAL items.
+ * They are stored case-folded.
+ */
+ static void
+add_fromto(spin, gap, from, to)
+ spellinfo_T *spin;
+ garray_T *gap;
+ char_u *from;
+ char_u *to;
+{
+ fromto_T *ftp;
+ char_u word[MAXWLEN];
+
+ if (ga_grow(gap, 1) == OK)
+ {
+ ftp = ((fromto_T *)gap->ga_data) + gap->ga_len;
+ (void)spell_casefold(from, STRLEN(from), word, MAXWLEN);
+ ftp->ft_from = getroom_save(&spin->si_blocks, word);
+ (void)spell_casefold(to, STRLEN(to), word, MAXWLEN);
+ ftp->ft_to = getroom_save(&spin->si_blocks, word);
+ ++gap->ga_len;
+ }
+}
+
+/*
+ * Convert a boolean argument in a SAL line to TRUE or FALSE;
+ */
+ static int
+sal_to_bool(s)
+ char_u *s;
+{
+ return STRCMP(s, "1") == 0 || STRCMP(s, "true") == 0;
+}
+
+/*
* Return TRUE if string "s" contains a non-ASCII character (128 or higher).
* When "s" is NULL FALSE is returned.
*/
@@ -1885,7 +2283,6 @@ spell_free_aff(aff)
hash_clear(&aff->af_pref);
hash_clear(&aff->af_suff);
- ga_clear(&aff->af_rep);
}
/*
@@ -2490,15 +2887,9 @@ store_word(word, spin, flags, region)
char_u foldword[MAXWLEN];
int res;
- if (flags & WF_KEEPCAP)
- res = OK; /* keep-case specified, don't add as fold-case */
- else
- {
- (void)spell_casefold(word, len, foldword, MAXWLEN);
- res = tree_add_word(foldword, spin->si_foldroot,
- (ct == WF_KEEPCAP ? WF_ALLCAP : ct) | flags,
- region, &spin->si_blocks);
- }
+ (void)spell_casefold(word, len, foldword, MAXWLEN);
+ res = tree_add_word(foldword, spin->si_foldroot, ct | flags,
+ region, &spin->si_blocks);
if (res == OK && (ct == WF_KEEPCAP || flags & WF_KEEPCAP))
res = tree_add_word(word, spin->si_keeproot, flags,
@@ -2731,6 +3122,29 @@ put_bytes(fd, nr, len)
putc((int)(nr >> (i * 8)), fd);
}
+static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+rep_compare __ARGS((const void *s1, const void *s2));
+
+/*
+ * Function given to qsort() to sort the REP items on "from" string.
+ */
+ static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+rep_compare(s1, s2)
+ const void *s1;
+ const void *s2;
+{
+ fromto_T *p1 = (fromto_T *)s1;
+ fromto_T *p2 = (fromto_T *)s2;
+
+ return STRCMP(p1->ft_from, p2->ft_from);
+}
+
/*
* Write the Vim spell file "fname".
*/
@@ -2744,6 +3158,12 @@ write_vim_spell(fname, spin)
int round;
wordnode_T *tree;
int nodecount;
+ int i;
+ int l;
+ garray_T *gap;
+ fromto_T *ftp;
+ char_u *p;
+ int rr;
fd = mch_fopen((char *)fname, "w");
if (fd == NULL)
@@ -2773,11 +3193,15 @@ write_vim_spell(fname, spin)
regionmask = 0;
}
- /* Write the table with character flags and table for case folding.
+ /*
+ * Write the table with character flags and table for case folding.
* <charflagslen> <charflags> <fcharlen> <fchars>
* Skip this for ASCII, the table may conflict with the one used for
- * 'encoding'. */
- if (spin->si_ascii)
+ * 'encoding'.
+ * Also skip this for an .add.spl file, the main spell file must contain
+ * the table (avoids that it conflicts). File is shorter too.
+ */
+ if (spin->si_ascii || spin->si_add)
{
putc(0, fd);
putc(0, fd);
@@ -2786,16 +3210,56 @@ write_vim_spell(fname, spin)
else
write_spell_chartab(fd);
+ /* Sort the REP items. */
+ qsort(spin->si_rep.ga_data, (size_t)spin->si_rep.ga_len,
+ sizeof(fromto_T), rep_compare);
- /* <SUGGEST> : <suggestlen> <more> ...
- * TODO. Only write a zero length for now. */
- put_bytes(fd, 0L, 4); /* <suggestlen> */
+ /* <SUGGEST> : <repcount> <rep> ...
+ * <salflags> <salcount> <sal> ...
+ * <maplen> <mapstr> */
+ for (round = 1; round <= 2; ++round)
+ {
+ if (round == 1)
+ gap = &spin->si_rep;
+ else
+ {
+ gap = &spin->si_sal;
+
+ i = 0;
+ if (spin->si_followup)
+ i |= SAL_F0LLOWUP;
+ if (spin->si_collapse)
+ i |= SAL_COLLAPSE;
+ if (spin->si_rem_accents)
+ i |= SAL_REM_ACCENTS;
+ putc(i, fd); /* <salflags> */
+ }
- spin->si_memtot = 0;
+ put_bytes(fd, (long_u)gap->ga_len, 2); /* <repcount> or <salcount> */
+ for (i = 0; i < gap->ga_len; ++i)
+ {
+ /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */
+ /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */
+ ftp = &((fromto_T *)gap->ga_data)[i];
+ for (rr = 1; rr <= 2; ++rr)
+ {
+ p = rr == 1 ? ftp->ft_from : ftp->ft_to;
+ l = STRLEN(p);
+ putc(l, fd);
+ fwrite(p, l, (size_t)1, fd);
+ }
+ }
+ }
+
+ put_bytes(fd, (long_u)spin->si_map.ga_len, 2); /* <maplen> */
+ if (spin->si_map.ga_len > 0) /* <mapstr> */
+ fwrite(spin->si_map.ga_data, (size_t)spin->si_map.ga_len,
+ (size_t)1, fd);
/*
* <LWORDTREE> <KWORDTREE>
*/
+ spin->si_memtot = 0;
for (round = 1; round <= 2; ++round)
{
tree = (round == 1) ? spin->si_foldroot : spin->si_keeproot;
@@ -2941,7 +3405,7 @@ ex_mkspell(eap)
/* Expand all the remaining arguments (e.g., $VIMRUNTIME). */
if (get_arglist_exp(arg, &fcount, &fnames) == OK)
{
- mkspell(fcount, fnames, ascii, eap->forceit, TRUE);
+ mkspell(fcount, fnames, ascii, eap->forceit, FALSE);
FreeWild(fcount, fnames);
}
}
@@ -2954,12 +3418,12 @@ ex_mkspell(eap)
* and ".spl" is appended to make the output file name.
*/
static void
-mkspell(fcount, fnames, ascii, overwrite, verbose)
+mkspell(fcount, fnames, ascii, overwrite, added_word)
int fcount;
char_u **fnames;
int ascii; /* -ascii argument given */
int overwrite; /* overwrite existing output file */
- int verbose; /* give progress messages */
+ int added_word; /* invoked through "zg" */
{
char_u fname[MAXPATHL];
char_u wfname[MAXPATHL];
@@ -2973,8 +3437,13 @@ mkspell(fcount, fnames, ascii, overwrite, verbose)
spellinfo_T spin;
vim_memset(&spin, 0, sizeof(spin));
- spin.si_verbose = verbose;
+ spin.si_verbose = !added_word;
spin.si_ascii = ascii;
+ spin.si_followup = TRUE;
+ spin.si_rem_accents = TRUE;
+ ga_init2(&spin.si_rep, (int)sizeof(fromto_T), 20);
+ ga_init2(&spin.si_sal, (int)sizeof(fromto_T), 20);
+ ga_init2(&spin.si_map, (int)sizeof(char_u), 100);
/* default: fnames[0] is output file, following are input files */
innames = &fnames[1];
@@ -2994,8 +3463,7 @@ mkspell(fcount, fnames, ascii, overwrite, verbose)
else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0)
{
/* Name ends in ".spl", use as the file name. */
- STRNCPY(wfname, fnames[0], sizeof(wfname));
- wfname[sizeof(wfname) - 1] = NUL;
+ vim_strncpy(wfname, fnames[0], sizeof(wfname) - 1);
}
else
/* Name should be language, make the file name from it. */
@@ -3119,13 +3587,13 @@ mkspell(fcount, fnames, ascii, overwrite, verbose)
/*
* Combine tails in the tree.
*/
- if (verbose || p_verbose > 2)
+ if (!added_word || p_verbose > 2)
{
- if (!verbose)
+ if (added_word)
verbose_enter();
MSG(_("Compressing word tree..."));
out_flush();
- if (!verbose)
+ if (added_word)
verbose_leave();
}
wordtree_compress(spin.si_foldroot, &spin);
@@ -3137,36 +3605,39 @@ mkspell(fcount, fnames, ascii, overwrite, verbose)
/*
* Write the info in the spell file.
*/
- if (verbose || p_verbose > 2)
+ if (!added_word || p_verbose > 2)
{
- if (!verbose)
+ if (added_word)
verbose_enter();
smsg((char_u *)_("Writing spell file %s..."), wfname);
out_flush();
- if (!verbose)
+ if (added_word)
verbose_leave();
}
write_vim_spell(wfname, &spin);
- if (verbose || p_verbose > 2)
+ if (!added_word || p_verbose > 2)
{
- if (!verbose)
+ if (added_word)
verbose_enter();
MSG(_("Done!"));
smsg((char_u *)_("Estimated runtime memory use: %d bytes"),
spin.si_memtot);
out_flush();
- if (!verbose)
+ if (added_word)
verbose_leave();
}
/* If the file is loaded need to reload it. */
- spell_reload_one(wfname);
+ spell_reload_one(wfname, added_word);
}
/* Free the allocated memory. */
free_blocks(spin.si_blocks);
+ ga_clear(&spin.si_rep);
+ ga_clear(&spin.si_sal);
+ ga_clear(&spin.si_map);
/* Free the .aff file structures. */
for (i = 0; i < incount; ++i)
@@ -3202,7 +3673,7 @@ spell_add_word(word, len, bad)
if (*curbuf->b_p_spf == NUL)
init_spellfile();
if (*curbuf->b_p_spf == NUL)
- EMSG(_("E999: 'spellfile' is not set"));
+ EMSG(_("E764: 'spellfile' is not set"));
else
{
/* Check that the user isn't editing the .add file somewhere. */
@@ -3225,11 +3696,13 @@ spell_add_word(word, len, bad)
fclose(fd);
/* Update the .add.spl file. */
- mkspell(1, &curbuf->b_p_spf, FALSE, TRUE, FALSE);
+ mkspell(1, &curbuf->b_p_spf, FALSE, TRUE, TRUE);
/* If the .add file is edited somewhere, reload it. */
if (buf != NULL)
buf_reload(buf);
+
+ redraw_all_later(NOT_VALID);
}
}
}
@@ -3615,5 +4088,1591 @@ spell_casefold(p, len, buf, buflen)
return OK;
}
+/*
+ * "z?": Find badly spelled word under or after the cursor.
+ * Give suggestions for the properly spelled word.
+ * This is based on the mechanisms of Aspell, but completely reimplemented.
+ */
+ void
+spell_suggest()
+{
+ char_u *line;
+ pos_T prev_cursor = curwin->w_cursor;
+ int attr;
+ char_u wcopy[MAXWLEN + 2];
+ char_u *p;
+ int i;
+ int c;
+ suginfo_T sug;
+ suggest_T *stp;
+
+ /*
+ * Find the start of the badly spelled word.
+ */
+ if (spell_move_to(FORWARD, TRUE, TRUE) == FAIL)
+ {
+ beep_flush();
+ return;
+ }
+
+ /*
+ * Set the info in "sug".
+ */
+ vim_memset(&sug, 0, sizeof(sug));
+ ga_init2(&sug.su_ga, (int)sizeof(suggest_T), 10);
+ hash_init(&sug.su_banned);
+ line = ml_get_curline();
+ sug.su_badptr = line + curwin->w_cursor.col;
+ sug.su_badlen = spell_check(curwin, sug.su_badptr, &attr);
+ if (sug.su_badlen >= MAXWLEN)
+ sug.su_badlen = MAXWLEN - 1; /* just in case */
+ vim_strncpy(sug.su_badword, sug.su_badptr, sug.su_badlen);
+ (void)spell_casefold(sug.su_badptr, sug.su_badlen,
+ sug.su_fbadword, MAXWLEN);
+
+ /* Ban the bad word itself. It may appear in another region. */
+ add_banned(&sug, sug.su_badword);
+
+ /*
+ * 1. Try inserting/deleting/swapping/changing a letter, use REP entries
+ * from the .aff file and inserting a space (split the word).
+ */
+ /* Set a maximum score to limit the combination of operations that is
+ * tried. */
+ sug.su_maxscore = SCORE_MAXINIT;
+ spell_try_change(&sug);
+ cleanup_suggestions(&sug);
+
+ /*
+ * 2. Try finding sound-a-like words.
+ */
+ /* Allow a higher score if we don't have many suggestions yet. */
+ if (sug.su_maxscore == SCORE_MAXINIT)
+ sug.su_maxscore = SCORE_MAXMAX;
+ spell_try_soundalike(&sug);
+
+ /* When CTRL-C was hit while searching do show the results. */
+ if (got_int)
+ {
+ (void)vgetc();
+ got_int = FALSE;
+ }
+
+ if (sug.su_ga.ga_len == 0)
+ MSG(_("Sorry, no suggestions"));
+ else
+ {
+ /* Cleanup, sort the suggestions and truncate at SUG_PROMPT_COUNT. */
+ cleanup_suggestions(&sug);
+
+ /* List the suggestions. */
+ msg_start();
+ vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"),
+ sug.su_badlen, sug.su_badptr);
+ msg_puts(IObuff);
+ msg_clr_eos();
+ msg_putchar('\n');
+ msg_scroll = TRUE;
+ for (i = 0; i < sug.su_ga.ga_len; ++i)
+ {
+ stp = &SUG(&sug, i);
+
+ /* The suggested word may replace only part of the bad word, add
+ * the not replaced part. */
+ STRCPY(wcopy, stp->st_word);
+ if (sug.su_badlen > stp->st_orglen)
+ vim_strncpy(wcopy + STRLEN(wcopy),
+ sug.su_badptr + stp->st_orglen,
+ sug.su_badlen - stp->st_orglen);
+ /* TODO: remove score */
+ vim_snprintf((char *)IObuff, IOSIZE, _("%2d \"%s\" (%d)"),
+ i + 1, wcopy, stp->st_score);
+ msg_puts(IObuff);
+ lines_left = 3; /* avoid more prompt */
+ msg_putchar('\n');
+ }
+
+ /* Ask for choice. */
+ i = prompt_for_number();
+ if (i > 0 && i <= sug.su_ga.ga_len && u_save_cursor())
+ {
+ /* Replace the word. */
+ stp = &SUG(&sug, i - 1);
+ p = alloc(STRLEN(line) - stp->st_orglen + STRLEN(stp->st_word) + 1);
+ if (p != NULL)
+ {
+ c = sug.su_badptr - line;
+ mch_memmove(p, line, c);
+ STRCPY(p + c, stp->st_word);
+ STRCAT(p, sug.su_badptr + stp->st_orglen);
+ ml_replace(curwin->w_cursor.lnum, p, FALSE);
+ curwin->w_cursor.col = c;
+ changed_bytes(curwin->w_cursor.lnum, c);
+ }
+ }
+ else
+ curwin->w_cursor = prev_cursor;
+ }
+
+ /* Free the suggestions. */
+ for (i = 0; i < sug.su_ga.ga_len; ++i)
+ vim_free(SUG(&sug, i).st_word);
+ ga_clear(&sug.su_ga);
+
+ /* Free the banned words. */
+ free_banned(&sug);
+}
+
+/*
+ * Make a copy of "word[len]", with the first letter upper or lower cased,
+ * to "wcopy[MAXWLEN]".
+ */
+ static void
+onecap_copy(word, len, wcopy, upper)
+ char_u *word;
+ int len;
+ char_u *wcopy;
+ int upper; /* TRUE: first letter made upper case */
+{
+ char_u *p;
+ int c;
+ int l;
+
+ p = word;
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ c = mb_ptr2char_adv(&p);
+ else
+#endif
+ c = *p++;
+ if (upper)
+ c = MB_TOUPPER(c);
+ else
+ c = MB_TOLOWER(c);
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ l = mb_char2bytes(c, wcopy);
+ else
+#endif
+ {
+ l = 1;
+ wcopy[0] = c;
+ }
+ vim_strncpy(wcopy + l, p, len - (p - word));
+}
+
+/*
+ * Make a copy of "word[len]" with all the letters upper cased into
+ * "wcopy[MAXWLEN]".
+ */
+ static void
+allcap_copy(word, wcopy)
+ char_u *word;
+ char_u *wcopy;
+{
+ char_u *s;
+ char_u *d;
+ int c;
+
+ d = wcopy;
+ for (s = word; *s != NUL; )
+ {
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ c = mb_ptr2char_adv(&s);
+ else
+#endif
+ c = *s++;
+
+ c = MB_TOUPPER(c); /* TODO: use spell toupper */
+
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ if (d - wcopy >= MAXWLEN - MB_MAXBYTES)
+ break;
+ d += mb_char2bytes(c, d);
+ }
+ else
+#endif
+ {
+ if (d - wcopy >= MAXWLEN - 1)
+ break;
+ *d++ = c;
+ }
+ }
+ *d = NUL;
+}
+
+/*
+ * Try finding suggestions by adding/removing/swapping letters.
+ */
+ static void
+spell_try_change(su)
+ suginfo_T *su;
+{
+ char_u fword[MAXWLEN]; /* copy of the bad word, case-folded */
+ char_u tword[MAXWLEN]; /* good word collected so far */
+ trystate_T stack[MAXWLEN];
+ char_u preword[MAXWLEN * 3]; /* word found with proper case (appended
+ * to for word split) */
+ char_u prewordlen = 0; /* length of word in "preword" */
+ int splitoff = 0; /* index in tword after last split */
+ trystate_T *sp;
+ int newscore;
+ langp_T *lp;
+ char_u *byts;
+ int *idxs;
+ int depth;
+ int c;
+ int n;
+ int flags;
+ int badflags;
+ garray_T *gap;
+ int arridx;
+ int len;
+ char_u *p;
+ fromto_T *ftp;
+ int fl, tl;
+
+ /* get caps flags for bad word */
+ badflags = captype(su->su_badptr, su->su_badptr + su->su_badlen);
+
+ /* We make a copy of the case-folded bad word, so that we can modify it
+ * to find matches (esp. REP items). */
+ STRCPY(fword, su->su_fbadword);
+
+ /*
+ * At each node in the tree these states are tried:
+ */
+#define STATE_START 0 /* At start of node, check if word may end or
+ * split word. */
+#define STATE_SPLITUNDO 1 /* Undo word split. */
+#define STATE_ENDNUL 2 /* Past NUL bytes at start of the node. */
+#define STATE_PLAIN 3 /* Use each byte of the node. */
+#define STATE_DEL 4 /* Delete a byte from the bad word. */
+#define STATE_INS 5 /* Insert a byte in the bad word. */
+#define STATE_SWAP 6 /* Swap two bytes. */
+#define STATE_SWAP3A 7 /* Swap two bytes over three. */
+#define STATE_ROT3L 8 /* Rotate three bytes left */
+#define STATE_ROT3R 9 /* Rotate three bytes right */
+#define STATE_ROT_UNDO 10 /* undo rotating */
+#define STATE_REP_INI 11 /* Prepare for using REP items. */
+#define STATE_REP 12 /* Use matching REP items from the .aff file. */
+#define STATE_REP_UNDO 13 /* Undo a REP item replacement. */
+#define STATE_FINAL 99 /* End of this node. */
+
+
+ for (lp = LANGP_ENTRY(curwin->w_buffer->b_langp, 0);
+ lp->lp_slang != NULL; ++lp)
+ {
+#ifdef SOUNDFOLD_SCORE
+ su->su_slang = lp->lp_slang;
+ if (lp->lp_slang->sl_sal.ga_len > 0)
+ /* soundfold the bad word */
+ spell_soundfold(lp->lp_slang, su->su_fbadword, su->su_salword);
+#endif
+
+ /*
+ * Go through the whole case-fold tree, try changes at each node.
+ * "tword[]" contains the word collected from nodes in the tree.
+ * "fword[]" the word we are trying to match with (initially the bad
+ * word).
+ */
+ byts = lp->lp_slang->sl_fbyts;
+ idxs = lp->lp_slang->sl_fidxs;
+
+ depth = 0;
+ stack[0].ts_state = STATE_START;
+ stack[0].ts_score = 0;
+ stack[0].ts_curi = 1;
+ stack[0].ts_fidx = 0;
+ stack[0].ts_fidxtry = 0;
+ stack[0].ts_twordlen = 0;
+ stack[0].ts_arridx = 0;
+
+ while (depth >= 0 && !got_int)
+ {
+ sp = &stack[depth];
+ switch (sp->ts_state)
+ {
+ case STATE_START:
+ /*
+ * Start of node: Deal with NUL bytes, which means
+ * tword[] may end here.
+ */
+ arridx = sp->ts_arridx; /* current node in the tree */
+ len = byts[arridx]; /* bytes in this node */
+ arridx += sp->ts_curi; /* index of current byte */
+
+ if (sp->ts_curi > len || (c = byts[arridx]) != 0)
+ {
+ /* Past bytes in node and/or past NUL bytes. */
+ sp->ts_state = STATE_ENDNUL;
+ break;
+ }
+
+ /*
+ * End of word in tree.
+ */
+ ++sp->ts_curi; /* eat one NUL byte */
+
+ flags = idxs[arridx];
+
+ /*
+ * Form the word with proper case in preword.
+ * If there is a word from a previous split, append.
+ */
+ tword[sp->ts_twordlen] = NUL;
+ if (flags & WF_KEEPCAP)
+ /* Must find the word in the keep-case tree. */
+ find_keepcap_word(lp->lp_slang, tword + splitoff,
+ preword + prewordlen);
+ else
+ /* Include badflags: if the badword is onecap or allcap
+ * use that for the goodword too. */
+ make_case_word(tword + splitoff,
+ preword + prewordlen, flags | badflags);
+
+ /* Don't use a banned word. It may appear again as a good
+ * word, thus remember it. */
+ if (flags & WF_BANNED)
+ {
+ add_banned(su, preword + prewordlen);
+ break;
+ }
+ if (was_banned(su, preword + prewordlen))
+ break;
+
+ newscore = 0;
+ if ((flags & WF_REGION)
+ && (((unsigned)flags >> 8) & lp->lp_region) == 0)
+ newscore += SCORE_REGION;
+ if (flags & WF_RARE)
+ newscore += SCORE_RARE;
+
+ if (!spell_valid_case(badflags,
+ captype(preword + prewordlen, NULL)))
+ newscore += SCORE_ICASE;
+
+ if (fword[sp->ts_fidx] == 0)
+ {
+ /* The badword also ends: add suggestions, */
+ add_suggestion(su, preword, sp->ts_score + newscore);
+ }
+ else if (sp->ts_fidx >= sp->ts_fidxtry)
+ {
+ /* The word in the tree ends but the badword
+ * continues: try inserting a space and check that a valid
+ * words starts at fword[sp->ts_fidx]. */
+ if (try_deeper(su, stack, depth, newscore + SCORE_SPLIT))
+ {
+ /* Save things to be restored at STATE_SPLITUNDO. */
+ sp->ts_save_prewordlen = prewordlen;
+ sp->ts_save_badflags = badflags;
+ sp->ts_save_splitoff = splitoff;
+
+ /* Append a space to preword. */
+ STRCAT(preword, " ");
+ prewordlen = STRLEN(preword);
+ splitoff = sp->ts_twordlen;
+ /* TODO: when case-folding changed the number of bytes
+ * this doesn't work... */
+ badflags = captype(su->su_badptr + sp->ts_fidx,
+ su->su_badptr + su->su_badlen);
+
+ sp->ts_state = STATE_SPLITUNDO;
+ ++depth;
+ /* Restart at top of the tree. */
+ stack[depth].ts_arridx = 0;
+ }
+ }
+ break;
+
+ case STATE_SPLITUNDO:
+ /* Fixup the changes done for word split. */
+ badflags = sp->ts_save_badflags;
+ splitoff = sp->ts_save_splitoff;
+ prewordlen = sp->ts_save_prewordlen;
+
+ /* Continue looking for NUL bytes. */
+ sp->ts_state = STATE_START;
+ break;
+
+ case STATE_ENDNUL:
+ /* Past the NUL bytes in the node. */
+ if (fword[sp->ts_fidx] == 0)
+ {
+ /* The badword ends, can't use the bytes in this node. */
+ sp->ts_state = STATE_DEL;
+ break;
+ }
+ sp->ts_state = STATE_PLAIN;
+ /*FALLTHROUGH*/
+
+ case STATE_PLAIN:
+ /*
+ * Go over all possible bytes at this node, add each to
+ * tword[] and use child node. "ts_curi" is the index.
+ */
+ arridx = sp->ts_arridx;
+ if (sp->ts_curi > byts[arridx])
+ {
+ /* Done all bytes at this node, do next state. When still
+ * at already changed bytes skip the other tricks. */
+ if (sp->ts_fidx >= sp->ts_fidxtry)
+ sp->ts_state = STATE_DEL;
+ else
+ sp->ts_state = STATE_FINAL;
+ }
+ else
+ {
+ arridx += sp->ts_curi++;
+ c = byts[arridx];
+
+ /* Normal byte, go one level deeper. If it's not equal to
+ * the byte in the bad word adjust the score. But don't
+ * even try when the byte was already changed. */
+ if (c == fword[sp->ts_fidx])
+ newscore = 0;
+ /* TODO: multi-byte characters */
+ else if (lp->lp_slang->sl_map != NULL
+ && similar_chars(lp->lp_slang,
+ c, fword[sp->ts_fidx]))
+ newscore = SCORE_SIMILAR;
+ else
+ newscore = SCORE_SUBST;
+ if ((newscore == 0 || sp->ts_fidx >= sp->ts_fidxtry)
+ && try_deeper(su, stack, depth, newscore))
+ {
+ ++depth;
+ ++stack[depth].ts_fidx;
+ tword[stack[depth].ts_twordlen++] = c;
+ stack[depth].ts_arridx = idxs[arridx];
+ }
+ }
+ break;
+
+ case STATE_DEL:
+ /* Try skipping one byte in the bad word (delete it). */
+ sp->ts_state = STATE_INS;
+ sp->ts_curi = 1;
+ if (fword[sp->ts_fidx] != NUL
+ && try_deeper(su, stack, depth, SCORE_DEL))
+ {
+ ++depth;
+ ++stack[depth].ts_fidx;
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ case STATE_INS:
+ /* Insert one byte. Do this for each possible bytes at this
+ * node. */
+ n = sp->ts_arridx;
+ if (sp->ts_curi > byts[n])
+ {
+ /* Done all bytes at this node, do next state. */
+ sp->ts_state = STATE_SWAP;
+ sp->ts_curi = 1;
+ }
+ else
+ {
+ /* Do one more byte at this node. */
+ n += sp->ts_curi++;
+ c = byts[n];
+ if (c != 0 && try_deeper(su, stack, depth, SCORE_INS))
+ {
+ ++depth;
+ tword[stack[depth].ts_twordlen++] = c;
+ stack[depth].ts_arridx = idxs[n];
+ }
+ }
+ break;
+
+ case STATE_SWAP:
+ /* Swap two bytes: "12" -> "21". This means looking for the
+ * following byte at the current node and the current byte at
+ * its child node. We change "fword" here, it's changed back
+ * afterwards. TODO: should swap characters instead of bytes.
+ * */
+ c = fword[sp->ts_fidx];
+ if (c != NUL && fword[sp->ts_fidx + 1] != NUL
+ && try_deeper(su, stack, depth, SCORE_SWAP))
+ {
+ sp->ts_state = STATE_SWAP3A;
+ ++depth;
+ fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+ fword[sp->ts_fidx + 1] = c;
+ stack[depth].ts_fidxtry = sp->ts_fidx + 2;
+ }
+ else
+ /* If this swap doesn't work then SWAP3 won't either. */
+ sp->ts_state = STATE_REP_INI;
+ break;
+
+ case STATE_SWAP3A:
+ /* First undo the STATE_SWAP swap: "21" -> "12". */
+ c = fword[sp->ts_fidx];
+ fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+ fword[sp->ts_fidx + 1] = c;
+
+ /* Swap two bytes, skipping one: "123" -> "321". We change
+ * "fword" here, it's changed back afterwards. TODO: should
+ * swap characters instead of bytes. */
+ c = fword[sp->ts_fidx];
+ if (c != NUL && fword[sp->ts_fidx + 1] != NUL
+ && fword[sp->ts_fidx + 2] != NUL
+ && try_deeper(su, stack, depth, SCORE_SWAP3))
+ {
+ sp->ts_state = STATE_ROT3L;
+ ++depth;
+ fword[sp->ts_fidx] = fword[sp->ts_fidx + 2];
+ fword[sp->ts_fidx + 2] = c;
+ stack[depth].ts_fidxtry = sp->ts_fidx + 3;
+ }
+ else
+ sp->ts_state = STATE_REP_INI;
+ break;
+
+ case STATE_ROT3L:
+ /* First undo STATE_SWAP3A: "321" -> "123" */
+ c = fword[sp->ts_fidx];
+ fword[sp->ts_fidx] = fword[sp->ts_fidx + 2];
+ fword[sp->ts_fidx + 2] = c;
+
+ /* Rotate three bytes left: "123" -> "231". We change
+ * "fword" here, it's changed back afterwards. TODO: should
+ * swap characters instead of bytes. */
+ if (try_deeper(su, stack, depth, SCORE_SWAP3))
+ {
+ sp->ts_state = STATE_ROT3R;
+ ++depth;
+ c = fword[sp->ts_fidx];
+ fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+ fword[sp->ts_fidx + 1] = fword[sp->ts_fidx + 2];
+ fword[sp->ts_fidx + 2] = c;
+ stack[depth].ts_fidxtry = sp->ts_fidx + 3;
+ }
+ else
+ sp->ts_state = STATE_REP_INI;
+ break;
+
+ case STATE_ROT3R:
+ /* First undo STATE_ROT3L: "231" -> "123" */
+ c = fword[sp->ts_fidx + 2];
+ fword[sp->ts_fidx + 2] = fword[sp->ts_fidx + 1];
+ fword[sp->ts_fidx + 1] = fword[sp->ts_fidx];
+ fword[sp->ts_fidx] = c;
+
+ /* Rotate three bytes right: "123" -> "312". We change
+ * "fword" here, it's changed back afterwards. TODO: should
+ * swap characters instead of bytes. */
+ if (try_deeper(su, stack, depth, SCORE_SWAP3))
+ {
+ sp->ts_state = STATE_ROT_UNDO;
+ ++depth;
+ c = fword[sp->ts_fidx + 2];
+ fword[sp->ts_fidx + 2] = fword[sp->ts_fidx + 1];
+ fword[sp->ts_fidx + 1] = fword[sp->ts_fidx];
+ fword[sp->ts_fidx] = c;
+ stack[depth].ts_fidxtry = sp->ts_fidx + 3;
+ }
+ else
+ sp->ts_state = STATE_REP_INI;
+ break;
+
+ case STATE_ROT_UNDO:
+ /* Undo STATE_ROT3R: "312" -> "123" */
+ c = fword[sp->ts_fidx];
+ fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+ fword[sp->ts_fidx + 1] = fword[sp->ts_fidx + 2];
+ fword[sp->ts_fidx + 2] = c;
+ /*FALLTHROUGH*/
+
+ case STATE_REP_INI:
+ /* Check if matching with REP items from the .aff file would
+ * work. Quickly skip if there are no REP items or the score
+ * is going to be too high anyway. */
+ gap = &lp->lp_slang->sl_rep;
+ if (gap->ga_len == 0
+ || sp->ts_score + SCORE_REP >= su->su_maxscore)
+ {
+ sp->ts_state = STATE_FINAL;
+ break;
+ }
+
+ /* Use the first byte to quickly find the first entry that
+ * matches. If the index is -1 there is none. */
+ sp->ts_curi = lp->lp_slang->sl_rep_first[fword[sp->ts_fidx]];
+ if (sp->ts_curi < 0)
+ {
+ sp->ts_state = STATE_FINAL;
+ break;
+ }
+
+ sp->ts_state = STATE_REP;
+ /*FALLTHROUGH*/
+
+ case STATE_REP:
+ /* Try matching with REP items from the .aff file. For each
+ * match replace the charactes and check if the resulting word
+ * is valid. */
+ p = fword + sp->ts_fidx;
+
+ gap = &lp->lp_slang->sl_rep;
+ while (sp->ts_curi < gap->ga_len)
+ {
+ ftp = (fromto_T *)gap->ga_data + sp->ts_curi++;
+ if (*ftp->ft_from != *p)
+ {
+ /* past possible matching entries */
+ sp->ts_curi = gap->ga_len;
+ break;
+ }
+ if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0
+ && try_deeper(su, stack, depth, SCORE_REP))
+ {
+ /* Need to undo this afterwards. */
+ sp->ts_state = STATE_REP_UNDO;
+
+ /* Change the "from" to the "to" string. */
+ ++depth;
+ fl = STRLEN(ftp->ft_from);
+ tl = STRLEN(ftp->ft_to);
+ if (fl != tl)
+ mch_memmove(p + tl, p + fl, STRLEN(p + fl) + 1);
+ mch_memmove(p, ftp->ft_to, tl);
+ stack[depth].ts_fidxtry = sp->ts_fidx + tl;
+ break;
+ }
+ }
+
+ if (sp->ts_curi >= gap->ga_len)
+ /* No (more) matches. */
+ sp->ts_state = STATE_FINAL;
+
+ break;
+
+ case STATE_REP_UNDO:
+ /* Undo a REP replacement and continue with the next one. */
+ ftp = (fromto_T *)lp->lp_slang->sl_rep.ga_data
+ + sp->ts_curi - 1;
+ fl = STRLEN(ftp->ft_from);
+ tl = STRLEN(ftp->ft_to);
+ p = fword + sp->ts_fidx;
+ if (fl != tl)
+ mch_memmove(p + fl, p + tl, STRLEN(p + tl) + 1);
+ mch_memmove(p, ftp->ft_from, fl);
+ sp->ts_state = STATE_REP;
+ break;
+
+ default:
+ /* Did all possible states at this level, go up one level. */
+ --depth;
+ }
+
+ line_breakcheck();
+ }
+ }
+}
+
+/*
+ * Try going one level deeper in the tree.
+ */
+ static int
+try_deeper(su, stack, depth, score_add)
+ suginfo_T *su;
+ trystate_T *stack;
+ int depth;
+ int score_add;
+{
+ int newscore;
+
+ /* Refuse to go deeper if the scrore is getting too big. */
+ newscore = stack[depth].ts_score + score_add;
+ if (newscore >= su->su_maxscore)
+ return FALSE;
+
+ stack[depth + 1].ts_state = STATE_START;
+ stack[depth + 1].ts_score = newscore;
+ stack[depth + 1].ts_curi = 1; /* start just after length byte */
+ stack[depth + 1].ts_fidx = stack[depth].ts_fidx;
+ stack[depth + 1].ts_fidxtry = stack[depth].ts_fidxtry;
+ stack[depth + 1].ts_twordlen = stack[depth].ts_twordlen;
+ stack[depth + 1].ts_arridx = stack[depth].ts_arridx;
+ return TRUE;
+}
+
+/*
+ * "fword" is a good word with case folded. Find the matching keep-case
+ * words and put it in "kword".
+ * Theoretically there could be several keep-case words that result in the
+ * same case-folded word, but we only find one...
+ */
+ static void
+find_keepcap_word(slang, fword, kword)
+ slang_T *slang;
+ char_u *fword;
+ char_u *kword;
+{
+ char_u uword[MAXWLEN]; /* "fword" in upper-case */
+ int depth;
+ int tryidx;
+
+ /* The following arrays are used at each depth in the tree. */
+ int arridx[MAXWLEN];
+ int round[MAXWLEN];
+ int fwordidx[MAXWLEN];
+ int uwordidx[MAXWLEN];
+ int kwordlen[MAXWLEN];
+
+ int flen, ulen;
+ int l;
+ int len;
+ int c;
+ unsigned lo, hi, m;
+ char_u *p;
+ char_u *byts = slang->sl_kbyts; /* array with bytes of the words */
+ int *idxs = slang->sl_kidxs; /* array with indexes */
+
+ if (byts == NULL)
+ {
+ /* array is empty: "cannot happen" */
+ *kword = NUL;
+ return;
+ }
+
+ /* Make an all-cap version of "fword". */
+ allcap_copy(fword, uword);
+
+ /*
+ * Each character needs to be tried both case-folded and upper-case.
+ * All this gets very complicated if we keep in mind that changing case
+ * may change the byte length of a multi-byte character...
+ */
+ depth = 0;
+ arridx[0] = 0;
+ round[0] = 0;
+ fwordidx[0] = 0;
+ uwordidx[0] = 0;
+ kwordlen[0] = 0;
+ while (depth >= 0)
+ {
+ if (fword[fwordidx[depth]] == NUL)
+ {
+ /* We are at the end of "fword". If the tree allows a word to end
+ * here we have found a match. */
+ if (byts[arridx[depth] + 1] == 0)
+ {
+ kword[kwordlen[depth]] = NUL;
+ return;
+ }
+
+ /* kword is getting too long, continue one level up */
+ --depth;
+ }
+ else if (++round[depth] > 2)
+ {
+ /* tried both fold-case and upper-case character, continue one
+ * level up */
+ --depth;
+ }
+ else
+ {
+ /*
+ * round[depth] == 1: Try using the folded-case character.
+ * round[depth] == 2: Try using the upper-case character.
+ */
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ flen = mb_ptr2len_check(fword + fwordidx[depth]);
+ ulen = mb_ptr2len_check(uword + uwordidx[depth]);
+ }
+ else
+#endif
+ ulen = flen = 1;
+ if (round[depth] == 1)
+ {
+ p = fword + fwordidx[depth];
+ l = flen;
+ }
+ else
+ {
+ p = uword + uwordidx[depth];
+ l = ulen;
+ }
+
+ for (tryidx = arridx[depth]; l > 0; --l)
+ {
+ /* Perform a binary search in the list of accepted bytes. */
+ len = byts[tryidx++];
+ c = *p++;
+ lo = tryidx;
+ hi = tryidx + len - 1;
+ while (lo < hi)
+ {
+ m = (lo + hi) / 2;
+ if (byts[m] > c)
+ hi = m - 1;
+ else if (byts[m] < c)
+ lo = m + 1;
+ else
+ {
+ lo = hi = m;
+ break;
+ }
+ }
+
+ /* Stop if there is no matching byte. */
+ if (hi < lo || byts[lo] != c)
+ break;
+
+ /* Continue at the child (if there is one). */
+ tryidx = idxs[lo];
+ }
+
+ if (l == 0)
+ {
+ /*
+ * Found the matching char. Copy it to "kword" and go a
+ * level deeper.
+ */
+ if (round[depth] == 1)
+ {
+ STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth],
+ flen);
+ kwordlen[depth + 1] = kwordlen[depth] + flen;
+ }
+ else
+ {
+ STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth],
+ ulen);
+ kwordlen[depth + 1] = kwordlen[depth] + ulen;
+ }
+ fwordidx[depth + 1] = fwordidx[depth] + flen;
+ uwordidx[depth + 1] = uwordidx[depth] + ulen;
+
+ ++depth;
+ arridx[depth] = tryidx;
+ round[depth] = 0;
+ }
+ }
+ }
+
+ /* Didn't find it: "cannot happen". */
+ *kword = NUL;
+}
+
+/*
+ * Find suggestions by comparing the word in a sound-a-like form.
+ */
+ static void
+spell_try_soundalike(su)
+ suginfo_T *su;
+{
+ char_u salword[MAXWLEN];
+ char_u tword[MAXWLEN];
+ char_u tfword[MAXWLEN];
+ char_u tsalword[MAXWLEN];
+ int arridx[MAXWLEN];
+ int curi[MAXWLEN];
+ langp_T *lp;
+ char_u *byts;
+ int *idxs;
+ int depth;
+ int c;
+ int n;
+ int round;
+ int flags;
+
+ for (lp = LANGP_ENTRY(curwin->w_buffer->b_langp, 0);
+ lp->lp_slang != NULL; ++lp)
+ {
+ if (lp->lp_slang->sl_sal.ga_len > 0)
+ {
+ /* soundfold the bad word */
+ spell_soundfold(lp->lp_slang, su->su_fbadword, salword);
+
+ /*
+ * Go through the whole tree, soundfold each word and compare.
+ * round 1: use the case-folded tree.
+ * round 2: use the keep-case tree.
+ */
+ for (round = 1; round <= 2; ++round)
+ {
+ if (round == 1)
+ {
+ byts = lp->lp_slang->sl_fbyts;
+ idxs = lp->lp_slang->sl_fidxs;
+ }
+ else
+ {
+ byts = lp->lp_slang->sl_kbyts;
+ idxs = lp->lp_slang->sl_kidxs;
+ }
+
+ depth = 0;
+ arridx[0] = 0;
+ curi[0] = 1;
+ while (depth >= 0 && !got_int)
+ {
+ if (curi[depth] > byts[arridx[depth]])
+ /* Done all bytes at this node, go up one level. */
+ --depth;
+ else
+ {
+ /* Do one more byte at this node. */
+ n = arridx[depth] + curi[depth];
+ ++curi[depth];
+ c = byts[n];
+ if (c == 0)
+ {
+ /* End of word, deal with the word. */
+ flags = idxs[n];
+ if (round == 2 || (flags & WF_KEEPCAP) == 0)
+ {
+ tword[depth] = NUL;
+ if (round == 1)
+ spell_soundfold(lp->lp_slang,
+ tword, tsalword);
+ else
+ {
+ /* In keep-case tree need to case-fold the
+ * word. */
+ (void)spell_casefold(tword, depth,
+ tfword, MAXWLEN);
+ spell_soundfold(lp->lp_slang,
+ tfword, tsalword);
+ }
+
+ /* TODO: also compare with small changes
+ * (insert char, swap char, etc.) */
+ if (STRCMP(salword, tsalword) == 0)
+ {
+ if (round == 1 && flags != 0)
+ {
+ char_u cword[MAXWLEN];
+
+ make_case_word(tword, cword, flags);
+ add_suggestion(su, cword, 0);
+ }
+ else
+ add_suggestion(su, tword, 0);
+ }
+ }
+
+ /* Skip over other NUL bytes. */
+ while (byts[n + 1] == 0)
+ {
+ ++n;
+ ++curi[depth];
+ }
+ }
+ else
+ {
+ /* Normal char, go one level deeper. */
+ tword[depth++] = c;
+ arridx[depth] = idxs[n];
+ curi[depth] = 1;
+ }
+ }
+ }
+ line_breakcheck();
+ }
+ }
+ }
+}
+
+/*
+ * Copy "fword" to "cword", fixing according to "flags".
+ */
+ static void
+make_case_word(fword, cword, flags)
+ char_u *fword;
+ char_u *cword;
+ int flags;
+{
+ if (flags & WF_ALLCAP)
+ /* Make it all upper-case */
+ allcap_copy(fword, cword);
+ else if (flags & WF_ONECAP)
+ /* Make the first letter upper-case */
+ onecap_copy(fword, STRLEN(fword), cword, TRUE);
+ else
+ /* Use goodword as-is. */
+ STRCPY(cword, fword);
+}
+
+/*
+ * Return TRUE if "c1" and "c2" are similar characters according to the MAP
+ * lines in the .aff file.
+ */
+ static int
+similar_chars(slang, c1, c2)
+ slang_T *slang;
+ int c1;
+ int c2;
+{
+ char_u *p1;
+ char_u *p2;
+
+ /* The similar characters are stored separated with slashes:
+ * "aaa/bbb/ccc/". Search for each character and if the next slash is the
+ * same one they are in the same MAP entry. */
+ p1 = vim_strchr(slang->sl_map, c1);
+ if (p1 == NULL)
+ return FALSE;
+ p2 = vim_strchr(slang->sl_map, c2);
+ if (p2 == NULL)
+ return FALSE;
+ return vim_strchr(p1, '/') == vim_strchr(p2, '/');
+}
+
+/*
+ * Add a suggestion to the list of suggestions.
+ * Do not add a duplicate suggestion or suggestions with a bad score.
+ * When "use_score" is not zero it's used, otherwise the score is computed
+ * with spell_edit_score().
+ */
+ static void
+add_suggestion(su, goodword, use_score)
+ suginfo_T *su;
+ char_u *goodword;
+ int use_score;
+{
+ suggest_T *stp;
+ int score;
+ int i;
+#ifdef SOUNDFOLD_SCORE
+ char_u fword[MAXWLEN];
+ char_u salword[MAXWLEN];
+#endif
+
+ /* Check that the word wasn't banned. */
+ if (was_banned(su, goodword))
+ return;
+
+ /* Compute the score and add the suggestion if it's good enough. */
+ if (use_score != 0)
+ score = use_score;
+ else
+ score = spell_edit_score(su->su_badword, goodword);
+
+ if (score <= su->su_maxscore)
+ {
+#ifdef SOUNDFOLD_SCORE
+ /* Add to the score when the word sounds differently.
+ * This is slow... */
+ if (su->su_slang->sl_sal.ga_len > 0)
+ {
+ (void)spell_casefold(goodword, STRLEN(goodword), fword, MAXWLEN);
+ spell_soundfold(su->su_slang, fword, salword);
+ score += spell_edit_score(su->su_salword, salword);
+ }
+#endif
+
+ /* Check if the word is already there. */
+ stp = &SUG(su, 0);
+ for (i = su->su_ga.ga_len - 1; i >= 0; --i)
+ if (STRCMP(stp[i].st_word, goodword) == 0)
+ {
+ /* Found it. Remember the lowest score. */
+ if (stp[i].st_score > score)
+ stp[i].st_score = score;
+ break;
+ }
+
+ if (i < 0 && ga_grow(&su->su_ga, 1) == OK)
+ {
+ /* Add a suggestion. */
+ stp = &SUG(su, su->su_ga.ga_len);
+ stp->st_word = vim_strsave(goodword);
+ if (stp->st_word != NULL)
+ {
+ stp->st_score = score;
+ stp->st_orglen = su->su_badlen;
+ ++su->su_ga.ga_len;
+
+ /* If we have too many suggestions now, sort the list and keep
+ * the best suggestions. */
+ if (su->su_ga.ga_len > SUG_CLEANUP_COUNT)
+ cleanup_suggestions(su);
+ }
+ }
+ }
+}
+
+/*
+ * Add a word to be banned.
+ */
+ static void
+add_banned(su, word)
+ suginfo_T *su;
+ char_u *word;
+{
+ char_u *s = vim_strsave(word);
+ hash_T hash;
+ hashitem_T *hi;
+
+ if (s != NULL)
+ {
+ hash = hash_hash(s);
+ hi = hash_lookup(&su->su_banned, s, hash);
+ if (HASHITEM_EMPTY(hi))
+ hash_add_item(&su->su_banned, hi, s, hash);
+ }
+}
+
+/*
+ * Return TRUE if a word appears in the list of banned words.
+ */
+ static int
+was_banned(su, word)
+ suginfo_T *su;
+ char_u *word;
+{
+ return !HASHITEM_EMPTY(hash_find(&su->su_banned, word));
+}
+
+/*
+ * Free the banned words in "su".
+ */
+ static void
+free_banned(su)
+ suginfo_T *su;
+{
+ int todo;
+ hashitem_T *hi;
+
+ todo = su->su_banned.ht_used;
+ for (hi = su->su_banned.ht_array; todo > 0; ++hi)
+ {
+ if (!HASHITEM_EMPTY(hi))
+ {
+ vim_free(hi->hi_key);
+ --todo;
+ }
+ }
+ hash_clear(&su->su_banned);
+}
+
+static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+sug_compare __ARGS((const void *s1, const void *s2));
+
+/*
+ * Function given to qsort() to sort the suggestions on st_score.
+ */
+ static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+sug_compare(s1, s2)
+ const void *s1;
+ const void *s2;
+{
+ suggest_T *p1 = (suggest_T *)s1;
+ suggest_T *p2 = (suggest_T *)s2;
+
+ return p1->st_score - p2->st_score;
+}
+
+/*
+ * Cleanup the suggestions:
+ * - Sort on score.
+ * - Remove words that won't be displayed.
+ */
+ static void
+cleanup_suggestions(su)
+ suginfo_T *su;
+{
+ suggest_T *stp = &SUG(su, 0);
+ int i;
+
+ /* Sort the list. */
+ qsort(su->su_ga.ga_data, (size_t)su->su_ga.ga_len,
+ sizeof(suggest_T), sug_compare);
+
+ /* Truncate the list to the number of suggestions that will be displayed. */
+ if (su->su_ga.ga_len > SUG_PROMPT_COUNT)
+ {
+ for (i = SUG_PROMPT_COUNT; i < su->su_ga.ga_len; ++i)
+ vim_free(stp[i].st_word);
+ su->su_ga.ga_len = SUG_PROMPT_COUNT;
+ su->su_maxscore = stp[SUG_PROMPT_COUNT - 1].st_score;
+ }
+}
+
+/*
+ * Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]".
+ */
+ static void
+spell_soundfold(slang, inword, res)
+ slang_T *slang;
+ char_u *inword;
+ char_u *res;
+{
+ fromto_T *ftp;
+ char_u word[MAXWLEN];
+#ifdef FEAT_MBYTE
+ int l;
+#endif
+ char_u *s;
+ char_u *t;
+ int i, j, z;
+ int n, k = 0;
+ int z0;
+ int k0;
+ int n0;
+ int c;
+ int pri;
+ int p0 = -333;
+ int c0;
+
+ /* Remove accents, if wanted.
+ * We actually remove all non-word characters. */
+ if (slang->sl_rem_accents)
+ {
+ t = word;
+ for (s = inword; *s != NUL; )
+ {
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ l = mb_ptr2len_check(s);
+ if (SPELL_ISWORDP(s))
+ {
+ mch_memmove(t, s, l);
+ t += l;
+ }
+ s += l;
+ }
+ else
+#endif
+ {
+ if (SPELL_ISWORDP(s))
+ *t++ = *s;
+ ++s;
+ }
+ }
+ *t = NUL;
+ }
+ else
+ STRCPY(word, inword);
+
+ ftp = (fromto_T *)slang->sl_sal.ga_data;
+
+ /*
+ * This comes from Aspell phonet.cpp. Converted from C++ to C.
+ * TODO: support for multi-byte chars.
+ */
+ i = j = z = 0;
+ while ((c = word[i]) != NUL)
+ {
+ n = slang->sl_sal_first[c];
+ z0 = 0;
+
+ if (n >= 0)
+ {
+ /* check all rules for the same letter */
+ while (ftp[n].ft_from[0] == c)
+ {
+ /* check whole string */
+ k = 1; /* number of found letters */
+ pri = 5; /* default priority */
+ s = ftp[n].ft_from;
+ s++; /* important for (see below) "*(s-1)" */
+
+ /* Skip over normal letters that match with the word. */
+ while (*s != NUL && word[i + k] == *s
+ && !vim_isdigit(*s) && strchr("(-<^$", *s) == NULL)
+ {
+ k++;
+ s++;
+ }
+
+ if (*s == '(')
+ {
+ /* check alternate letters in "(..)" */
+ for (t = s + 1; *t != ')' && *t != NUL; ++t)
+ if (*t == word[i + k])
+ {
+ /* match */
+ ++k;
+ for (s = t + 1; *s != NUL; ++s)
+ if (*s == ')')
+ {
+ ++s;
+ break;
+ }
+ break;
+ }
+ }
+
+ p0 = *s;
+ k0 = k;
+ while (*s == '-' && k > 1)
+ {
+ k--;
+ s++;
+ }
+ if (*s == '<')
+ s++;
+ if (vim_isdigit(*s))
+ {
+ /* determine priority */
+ pri = *s - '0';
+ s++;
+ }
+ if (*s == '^' && *(s + 1) == '^')
+ s++;
+
+ if (*s == NUL
+ || (*s == '^'
+ && (i == 0 || !SPELL_ISWORDP(word + i - 1))
+ && (*(s + 1) != '$'
+ || (!SPELL_ISWORDP(word + i + k0))))
+ || (*s == '$' && i > 0
+ && SPELL_ISWORDP(word + i - 1)
+ && (!SPELL_ISWORDP(word + i + k0))))
+ {
+ /* search for followup rules, if: */
+ /* followup and k > 1 and NO '-' in searchstring */
+ c0 = word[i + k - 1];
+ n0 = slang->sl_sal_first[c0];
+
+ if (slang->sl_followup && k > 1 && n0 >= 0
+ && p0 != '-' && word[i + k] != NUL)
+ {
+ /* test follow-up rule for "word[i + k]" */
+ while (ftp[n0].ft_from[0] == c0)
+ {
+
+ /* check whole string */
+ k0 = k;
+ p0 = 5;
+ s = ftp[n0].ft_from;
+ s++;
+ while (*s != NUL && word[i+k0] == *s
+ && !vim_isdigit(*s)
+ && strchr("(-<^$",*s) == NULL)
+ {
+ k0++;
+ s++;
+ }
+ if (*s == '(')
+ {
+ /* check alternate letters in "(..)" */
+ for (t = s + 1; *t != ')' && *t != NUL; ++t)
+ if (*t == word[i + k0])
+ {
+ /* match */
+ ++k0;
+ for (s = t + 1; *s != NUL; ++s)
+ if (*s == ')')
+ {
+ ++s;
+ break;
+ }
+ break;
+ }
+ }
+ while (*s == '-')
+ {
+ /* "k0" gets NOT reduced */
+ /* because "if (k0 == k)" */
+ s++;
+ }
+ if (*s == '<')
+ s++;
+ if (vim_isdigit(*s))
+ {
+ p0 = *s - '0';
+ s++;
+ }
+
+ if (*s == NUL
+ /* *s == '^' cuts */
+ || (*s == '$'
+ && !SPELL_ISWORDP(word + i + k0)))
+ {
+ if (k0 == k)
+ {
+ /* this is just a piece of the string */
+ ++n0;
+ continue;
+ }
+
+ if (p0 < pri)
+ {
+ /* priority too low */
+ ++n0;
+ continue;
+ }
+ /* rule fits; stop search */
+ break;
+ }
+ ++n0;
+ }
+
+ if (p0 >= pri && ftp[n0].ft_from[0] == c0)
+ {
+ ++n;
+ continue;
+ }
+ }
+
+ /* replace string */
+ s = ftp[n].ft_to;
+ p0 = (ftp[n].ft_from[0] != NUL
+ && vim_strchr(ftp[n].ft_from + 1,
+ '<') != NULL) ? 1 : 0;
+ if (p0 == 1 && z == 0)
+ {
+ /* rule with '<' is used */
+ if (j > 0 && *s != NUL
+ && (res[j - 1] == c || res[j - 1] == *s))
+ j--;
+ z0 = 1;
+ z = 1;
+ k0 = 0;
+ while (*s != NUL && word[i+k0] != NUL)
+ {
+ word[i + k0] = *s;
+ k0++;
+ s++;
+ }
+ if (k > k0)
+ mch_memmove(word + i + k0, word + i + k,
+ STRLEN(word + i + k) + 1);
+
+ /* new "actual letter" */
+ c = word[i];
+ }
+ else
+ {
+ /* no '<' rule used */
+ i += k - 1;
+ z = 0;
+ while (*s != NUL && s[1] != NUL && j < MAXWLEN)
+ {
+ if (j == 0 || res[j - 1] != *s)
+ {
+ res[j] = *s;
+ j++;
+ }
+ s++;
+ }
+ /* new "actual letter" */
+ c = *s;
+ if (ftp[n].ft_from[0] != NUL
+ && strstr((char *)ftp[n].ft_from + 1,
+ "^^") != NULL)
+ {
+ if (c != NUL)
+ {
+ res[j] = c;
+ j++;
+ }
+ mch_memmove(word, word + i + 1,
+ STRLEN(word + i + 1) + 1);
+ i = 0;
+ z0 = 1;
+ }
+ }
+ break;
+ }
+ ++n;
+ }
+ }
+
+ if (z0 == 0)
+ {
+ if (k && !p0 && j < MAXWLEN && c != NUL
+ && (!slang->sl_collapse || j == 0 || res[j - 1] != c))
+ {
+ /* condense only double letters */
+ res[j] = c;
+ j++;
+ }
+
+ i++;
+ z = 0;
+ k = 0;
+ }
+ }
+
+ res[j] = NUL;
+}
+
+/*
+ * Compute the "edit distance" to turn "badword" into "goodword". The less
+ * deletes/inserts/swaps are required the lower the score.
+ * The algorithm comes from Aspell editdist.cpp, edit_distance().
+ * TODO: make this work with multi-byte chars.
+ */
+ static int
+spell_edit_score(badword, goodword)
+ char_u *badword;
+ char_u *goodword;
+{
+ int *cnt;
+ int badlen, goodlen;
+ int j, i;
+ int t;
+ int bc, gc;
+
+ /* We use "cnt" as an array: CNT(badword_idx, goodword_idx). */
+#define CNT(a, b) cnt[(a) + (b) * (badlen + 1)]
+ badlen = STRLEN(badword) + 1;
+ goodlen = STRLEN(goodword) + 1;
+ cnt = (int *)lalloc((long_u)(sizeof(int) * (badlen + 1) * (goodlen + 1)),
+ TRUE);
+ if (cnt == 0)
+ return 0;
+
+ CNT(0, 0) = 0;
+ for (j = 1; j <= goodlen; ++j)
+ CNT(0, j) = CNT(0, j - 1) + SCORE_DEL;
+
+ for (i = 1; i <= badlen; ++i)
+ {
+ CNT(i, 0) = CNT(i - 1, 0) + SCORE_INS;
+ for (j = 1; j <= goodlen; ++j)
+ {
+ bc = badword[i - 1];
+ gc = goodword[j - 1];
+ if (bc == gc)
+ CNT(i, j) = CNT(i - 1, j - 1);
+ else
+ {
+ /* Use a better score when there is only a case difference. */
+ if (spelltab.st_fold[bc] == spelltab.st_fold[gc])
+ CNT(i, j) = SCORE_ICASE + CNT(i - 1, j - 1);
+ else
+ CNT(i, j) = SCORE_SUBST + CNT(i - 1, j - 1);
+
+ if (i > 1 && j > 1 && bc == goodword[j - 2]
+ && badword[i - 2] == gc)
+ {
+ t = SCORE_SWAP + CNT(i - 2, j - 2);
+ if (t < CNT(i, j))
+ CNT(i, j) = t;
+ }
+ t = SCORE_DEL + CNT(i - 1, j);
+ if (t < CNT(i, j))
+ CNT(i, j) = t;
+ t = SCORE_INS + CNT(i, j - 1);
+ if (t < CNT(i, j))
+ CNT(i, j) = t;
+ }
+ }
+ }
+ return CNT(badlen - 1, goodlen - 1);
+}
#endif /* FEAT_SYN_HL */
diff --git a/src/tag.c b/src/tag.c
index 2cad49580..e94322d6a 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -740,17 +740,8 @@ do_tag(tag, type, count, forceit, verbose)
{
/*
* Ask to select a tag from the list.
- * When using ":silent" assume that <CR> was entered.
*/
- MSG_PUTS(_("Enter nr of choice (<CR> to abort): "));
- i = get_number(TRUE);
- if (KeyTyped) /* don't call wait_return() now */
- {
- msg_putchar('\n');
- cmdline_row = msg_row - 1;
- need_wait_return = FALSE;
- msg_didany = FALSE;
- }
+ i = prompt_for_number();
if (i <= 0 || i > num_matches || got_int)
{
/* no valid choice: don't change anything */
diff --git a/src/ui.c b/src/ui.c
index 89b5cd689..3fe99c5e4 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -1680,6 +1680,7 @@ read_from_input_buf(buf, maxlen)
return (int)maxlen;
}
+/*ARGSUSED*/
void
fill_input_buf(exit_on_error)
int exit_on_error;
diff --git a/src/version.h b/src/version.h
index c97e07806..94ec0ea7c 100644
--- a/src/version.h
+++ b/src/version.h
@@ -36,5 +36,5 @@
#define VIM_VERSION_NODOT "vim70aa"
#define VIM_VERSION_SHORT "7.0aa"
#define VIM_VERSION_MEDIUM "7.0aa ALPHA"
-#define VIM_VERSION_LONG "VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 8)"
-#define VIM_VERSION_LONG_DATE "VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 8, compiled "
+#define VIM_VERSION_LONG "VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 13)"
+#define VIM_VERSION_LONG_DATE "VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 13, compiled "