# HG changeset patch # User vimboss # Date 1137798618 0 # Node ID 1c586ee8dd452d305bebf633d5b8c78ba1ab170d # Parent 7437be625546cef15a541285195eb9ea5ccea483 updated for version 7.0183 diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.0aa. Last change: 2006 Jan 13 +*eval.txt* For Vim version 7.0aa. Last change: 2006 Jan 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -28,6 +28,7 @@ 8. Exception handling |exception-handl 9. Examples |eval-examples| 10. No +eval feature |no-eval-feature| 11. The sandbox |eval-sandbox| +12. Textlock |textlock| {Vi does not have any of these commands} @@ -6830,5 +6831,33 @@ This is not guaranteed 100% secure, but option that may have been set from a modeline, e.g. 'foldexpr'. + *sandbox-option* +A few options contain an expression. When this expression is evaluated it may +have to be done in the sandbox to avoid trouble. But the sandbox is +restrictive, thus this only happens when the option was set from an insecure +location. Insecure in this context are: +- sourcing a .vimrc or .exrc in the current directlry +- while executing in the sandbox +- value coming from a modeline + +Note that when in the sandbox and saving an option value and restoring it, the +option will still be marked as it was set in the sandbox. + +============================================================================== +12. Textlock *textlock* + +In a few situations it is not allowed to change the text in the buffer, jump +to another window and some other things that might confuse or break what Vim +is currently doing. This mostly applies to things that happen when Vim is +actually doing something else. For example, evaluating the 'balloonexpr' may +happen any moment the mouse cursor is resting at some position. + +This is not allowed when the textlock is active: + - changing the buffer text + - jumping to another buffer or window + - editing another file + - closing a window or quitting Vim + - etc. + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 7.0aa. Last change: 2006 Jan 19 +*options.txt* For Vim version 7.0aa. Last change: 2006 Jan 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1037,6 +1037,12 @@ A jump table for the options with a shor Vim does not try to send a message to an external debugger (Netbeans or Sun Workshop). + The expression may be evaluated in the |sandbox|, see + |sandbox-option|. + + It is not allowed to change text or jump to another window while + evaluating 'balloonexpr' |textlock|. + To check whether line breaks in the balloon text work use this check: > if has("balloon_multiline") < @@ -2771,8 +2777,13 @@ A jump table for the options with a shor {not available when compiled without the |+folding| or |+eval| feature} The expression used for when 'foldmethod' is "expr". It is evaluated - for each line to obtain its fold level. See |fold-expr|. Also see - |eval-sandbox|. + for each line to obtain its fold level. See |fold-expr|. + + The expression may be evaluated in the |sandbox|, see + |sandbox-option|. + + It is not allowed to change text or jump to another window while + evaluating 'foldexpr' |textlock|. *'foldignore'* *'fdi'* 'foldignore' 'fdi' string (default: "#") @@ -2904,6 +2915,12 @@ A jump table for the options with a shor An expression which is used to specify the text displayed for a closed fold. See |fold-foldtext|. + The expression may be evaluated in the |sandbox|, see + |sandbox-option|. + + It is not allowed to change text or jump to another window while + evaluating 'foldtext' |textlock|. + *'formatoptions'* *'fo'* 'formatoptions' 'fo' string (Vim default: "tcq", Vi default: "vt") local to buffer @@ -3696,11 +3713,17 @@ A jump table for the options with a shor option to a file name. Mostly useful to change "." to "/" for Java: > :set includeexpr=substitute(v:fname,'\\.','/','g') < The "v:fname" variable will be set to the file name that was detected. - Evaluated in the |sandbox|. + Also used for the |gf| command if an unmodified file name can't be found. Allows doing "gf" on the name after an 'include' statement. Also used for ||. + The expression may be evaluated in the |sandbox|, see + |sandbox-option|. + + It is not allowed to change text or jump to another window while + evaluating 'includeexpr' |textlock|. + *'incsearch'* *'is'* *'noincsearch'* *'nois'* 'incsearch' 'is' boolean (default off) global @@ -3746,9 +3769,16 @@ A jump table for the options with a shor :set indentexpr=GetMyIndent() < Error messages will be suppressed, unless the 'debug' option contains "msg". - See |indent-expression|. Also see |eval-sandbox|. + See |indent-expression|. NOTE: This option is made empty when 'compatible' is set. + The expression may be evaluated in the |sandbox|, see + |sandbox-option|. + + It is not allowed to change text or jump to another window while + evaluating 'indentexpr' |textlock|. + + *'indentkeys'* *'indk'* 'indentkeys' 'indk' string (default "0{,0},:,0#,!^F,o,O,e") local to buffer @@ -6084,7 +6114,13 @@ A jump table for the options with a shor temporarily to that of the window (and buffer) whose statusline is currently being drawn. The expression will evaluate in this context. The variable "actual_curbuf" is set to the 'bufnr()' number of the - real current buffer. The expression is evaluated in the |sandbox|. + real current buffer. + + The 'statusline' option may be evaluated in the |sandbox|, see + |sandbox-option|. + + It is not allowed to change text or jump to another window while + evaluating 'statusline' |textlock|. If the statusline is not updated when you want it (e.g., after setting a variable that's used in an expression), you can force an update by diff --git a/runtime/doc/tags b/runtime/doc/tags --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -5349,6 +5349,7 @@ hebrew hebrew.txt /*hebrew* hebrew.txt hebrew.txt /*hebrew.txt* help various.txt /*help* help-context help.txt /*help-context* +help-tags tags 1 help-translated various.txt /*help-translated* help-xterm-window various.txt /*help-xterm-window* help.txt help.txt /*help.txt* @@ -6404,6 +6405,7 @@ s:netrw_line pi_netrw.txt /*s:netrw_line s:var eval.txt /*s:var* s change.txt /*s* sandbox eval.txt /*sandbox* +sandbox-option eval.txt /*sandbox-option* save-file editing.txt /*save-file* save-settings starting.txt /*save-settings* scheme.vim syntax.txt /*scheme.vim* @@ -6929,6 +6931,7 @@ tex-style syntax.txt /*tex-style* tex.vim syntax.txt /*tex.vim* text-objects motion.txt /*text-objects* text-objects-changed version5.txt /*text-objects-changed* +textlock eval.txt /*textlock* tf.vim syntax.txt /*tf.vim* this_session-variable eval.txt /*this_session-variable* throw-catch eval.txt /*throw-catch* diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 7.0aa. Last change: 2006 Jan 19 +*todo.txt* For Vim version 7.0aa. Last change: 2006 Jan 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -30,20 +30,6 @@ be worked on, but only if you sponsor Vi *known-bugs* -------------------- Known bugs and current work ----------------------- -Evaluate 'balloonexpr' in the sandbox only when it was set from an unsafe -place (e.g., modeline)? Patch from Sumner Hayes, Jan 12. Also use for other -options? - -":saveas asdf.c" should set 'filetype' to c when it's empty. Also for ":w -asdf.c" when it sets the buffer filename. - -When ":cclose" is used the buffer is not wiped out and is no longer recognized -as a quickfix buffer, thus it's not reused either. - -Patch to support lists and dicts for the Python interface. (G. Sumner Hayes, -Jan 12). Docs in a previous patch. -Use free_tv() instead of clear_tv() and vim_free(). - ccomplete: - When using page-up/page-down in menu it sometimes jumps more than a page. - When an option is set: In completion mode and the user types (identifier) @@ -71,9 +57,9 @@ ccomplete: Can't reproduce it right now... spelling: -- Include script to cleanup a .add file. (Antonio Colombo, Jan 9) -- suggestions for "macARONI" doesn't include "macaroni", they are all allcap. - suggestion for "KG" to "kg" when it's keepcase. +- Use runtime/cleanadd script to cleanup .add files. When to invoke it? + After deleting a word and some timestamp difference perhaps? +- suggestion for "KG" to "kg" when it's keepcase. - Autocommand event for when a spell file is missing. Allows making a plugin that fetches the file over internet. Pattern == language. - Using KEEPCASE flag still allows all-upper word, docs say it doesn't. diff --git a/runtime/doc/version7.txt b/runtime/doc/version7.txt --- a/runtime/doc/version7.txt +++ b/runtime/doc/version7.txt @@ -1,4 +1,4 @@ -*version7.txt* For Vim version 7.0aa. Last change: 2006 Jan 19 +*version7.txt* For Vim version 7.0aa. Last change: 2006 Jan 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -154,6 +154,9 @@ expr". "-=" and ".=" works in a similar With the |:profile| command you can find out where your function or script wastes its time. +In the Python interface vim.eval() also handles Dictionaries and Lists. +|python-eval| (G. Sumner Hayes) + Spell checking *new-spell* -------------- @@ -667,6 +670,15 @@ iterative mechanism. This avoids out-of allocated memory, running out of memory can always be detected. Allows matching more complex things, but Vim may seem to hang while doing that. +Previously some options were always evaluated in the |sandbox|. Now that only +happens when the option was set from a modeline or in secure mode. Applies to +'balloonexpr', 'foldexpr', 'foldtext' and 'includeexpr'. (Sumner Hayes) + +Some commands and expressions could have nasty side effects, such as using +CTRL-R = while editing a search pattern and the expression invokes a function +that jumps to another window. The |textlock| has been added to prevent this +from happening. + ":breakadd here" and ":breakdel here" can be used to set or delete a breakpoint at the cursor. @@ -925,6 +937,10 @@ and for supported autocommand events. (Y Allow using ":global" in the sandbox, it doesn't do anything harmful by itself. +":saveas asdf.c" will set 'filetype' to c when it's empty. Also for ":w +asdf.c" when it sets the filename for the buffer. + + ============================================================================== COMPILE TIME CHANGES *compile-changes-7* @@ -1528,8 +1544,8 @@ string, because it may cause trouble in When evaluating an expression for CTRL-R = on the command line it was possible to call a function that opens a new window, resulting in errors for -incremental search, and many other nasty things were possible. Now set -"cmdline_busy" and disallow changing the buffer or jumpting to another window +incremental search, and many other nasty things were possible. Now use the +|textlock| to disallow changing the buffer or jumping to another window to protect from unexpected behavior. Same for CTRL-\ e. "d(" deleted the character under the cursor, while the documentation specified @@ -1574,4 +1590,10 @@ When expanding a file name for a shell c !cmd foo" also escape characters that are special for the shell: "!;&()<>". +When the name of the buffer was set by a ":r fname" command |cpo-f| no +autocommands were triggered to notify about the change in the buffer list. + +In the quickfix buffer 'bufhidden' was set to "delete", which caused closing +the quickfix window to leave an unlisted "No Name" buffer behind every time. + vim:tw=78:ts=8:ft=help:norl: diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -291,7 +291,7 @@ edit(cmdchar, startln, count) #endif /* Don't allow changes in the buffer while editing the cmdline. The * caller of getcmdline() may get confused. */ - if (cmdline_busy) + if (textlock != 0) { EMSG(_(e_secure)); return FALSE; diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -420,8 +420,8 @@ static int list_extend __ARGS((list_T *l static int list_concat __ARGS((list_T *l1, list_T *l2, typval_T *tv)); static list_T *list_copy __ARGS((list_T *orig, int deep, int copyID)); static void list_remove __ARGS((list_T *l, listitem_T *item, listitem_T *item2)); -static char_u *list2string __ARGS((typval_T *tv)); -static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo)); +static char_u *list2string __ARGS((typval_T *tv, int copyID)); +static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo, int copyID)); static void set_ref_in_ht __ARGS((hashtab_T *ht, int copyID)); static void set_ref_in_list __ARGS((list_T *l, int copyID)); static void set_ref_in_item __ARGS((typval_T *tv, int copyID)); @@ -435,10 +435,10 @@ static dict_T *dict_copy __ARGS((dict_T static int dict_add __ARGS((dict_T *d, dictitem_T *item)); static long dict_len __ARGS((dict_T *d)); static dictitem_T *dict_find __ARGS((dict_T *d, char_u *key, int len)); -static char_u *dict2string __ARGS((typval_T *tv)); +static char_u *dict2string __ARGS((typval_T *tv, int copyID)); static int get_dict_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate)); -static char_u *echo_string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf)); -static char_u *tv2string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf)); +static char_u *echo_string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf, int copyID)); +static char_u *tv2string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf, int copyID)); static char_u *string_quote __ARGS((char_u *str, int function)); static int get_env_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate)); static int find_internal_func __ARGS((char_u *name)); @@ -1175,20 +1175,26 @@ eval_to_string(arg, nextcmd) } /* - * Call eval_to_string() with "sandbox" set and not using local variables. + * Call eval_to_string() without using current local variables and using + * textlock. When "use_sandbox" is TRUE use the sandbox. */ char_u * -eval_to_string_safe(arg, nextcmd) +eval_to_string_safe(arg, nextcmd, use_sandbox) char_u *arg; char_u **nextcmd; + int use_sandbox; { char_u *retval; void *save_funccalp; save_funccalp = save_funccal(); - ++sandbox; + if (use_sandbox) + ++sandbox; + ++textlock; retval = eval_to_string(arg, nextcmd); - --sandbox; + if (use_sandbox) + --sandbox; + --textlock; restore_funccal(save_funccalp); return retval; } @@ -1566,9 +1572,12 @@ eval_foldexpr(arg, cp) typval_T tv; int retval; char_u *s; + int use_sandbox = was_set_insecurely((char_u *)"foldexpr"); ++emsg_off; - ++sandbox; + if (use_sandbox) + ++sandbox; + ++textlock; *cp = NUL; if (eval0(arg, &tv, NULL, TRUE) == FAIL) retval = 0; @@ -1591,7 +1600,9 @@ eval_foldexpr(arg, cp) clear_tv(&tv); } --emsg_off; - --sandbox; + if (use_sandbox) + --sandbox; + --textlock; return retval; } @@ -1985,7 +1996,7 @@ list_arg_vars(eap, arg) int c; char_u *s; - s = echo_string(&tv, &tf, numbuf); + s = echo_string(&tv, &tf, numbuf, 0); c = *arg; *arg = NUL; list_one_var_a((char_u *)"", @@ -5310,6 +5321,18 @@ list_equal(l1, l2, ic) return item1 == NULL && item2 == NULL; } +#if defined(FEAT_PYTHON) || defined(PROTO) +/* + * Return the dictitem that an entry in a hashtable points to. + */ + dictitem_T * +dict_lookup(hi) + hashitem_T *hi; +{ + return HI2DI(hi); +} +#endif + /* * Return TRUE when two dictionaries have exactly the same key/values. */ @@ -5777,8 +5800,9 @@ list_remove(l, item, item2) * May return NULL. */ static char_u * -list2string(tv) +list2string(tv, copyID) typval_T *tv; + int copyID; { garray_T ga; @@ -5786,7 +5810,7 @@ list2string(tv) return NULL; ga_init2(&ga, (int)sizeof(char), 80); ga_append(&ga, '['); - if (list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE) == FAIL) + if (list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE, copyID) == FAIL) { vim_free(ga.ga_data); return NULL; @@ -5802,11 +5826,12 @@ list2string(tv) * Return FAIL or OK. */ static int -list_join(gap, l, sep, echo) +list_join(gap, l, sep, echo, copyID) garray_T *gap; list_T *l; char_u *sep; int echo; + int copyID; { int first = TRUE; char_u *tofree; @@ -5822,9 +5847,9 @@ list_join(gap, l, sep, echo) ga_concat(gap, sep); if (echo) - s = echo_string(&item->li_tv, &tofree, numbuf); - else - s = tv2string(&item->li_tv, &tofree, numbuf); + s = echo_string(&item->li_tv, &tofree, numbuf, copyID); + else + s = tv2string(&item->li_tv, &tofree, numbuf, copyID); if (s != NULL) ga_concat(gap, s); vim_free(tofree); @@ -6355,8 +6380,9 @@ get_dict_number(d, key) * May return NULL. */ static char_u * -dict2string(tv) +dict2string(tv, copyID) typval_T *tv; + int copyID; { garray_T ga; int first = TRUE; @@ -6391,7 +6417,7 @@ dict2string(tv) vim_free(tofree); } ga_concat(&ga, (char_u *)": "); - s = tv2string(&HI2DI(hi)->di_tv, &tofree, numbuf); + s = tv2string(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID); if (s != NULL) ga_concat(&ga, s); vim_free(tofree); @@ -6535,13 +6561,15 @@ failret: * If the memory is allocated "tofree" is set to it, otherwise NULL. * "numbuf" is used for a number. * Does not put quotes around strings, as ":echo" displays values. + * When "copyID" is not NULL replace recursive lists and dicts with "...". * May return NULL; */ static char_u * -echo_string(tv, tofree, numbuf) +echo_string(tv, tofree, numbuf, copyID) typval_T *tv; char_u **tofree; char_u *numbuf; + int copyID; { static int recurse = 0; char_u *r = NULL; @@ -6560,19 +6588,51 @@ echo_string(tv, tofree, numbuf) *tofree = NULL; r = tv->vval.v_string; break; + case VAR_LIST: - *tofree = list2string(tv); - r = *tofree; - break; + if (tv->vval.v_list == NULL) + { + *tofree = NULL; + r = NULL; + } + else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID) + { + *tofree = NULL; + r = (char_u *)"[...]"; + } + else + { + tv->vval.v_list->lv_copyID = copyID; + *tofree = list2string(tv, copyID); + r = *tofree; + } + break; + case VAR_DICT: - *tofree = dict2string(tv); - r = *tofree; - break; + if (tv->vval.v_dict == NULL) + { + *tofree = NULL; + r = NULL; + } + else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID) + { + *tofree = NULL; + r = (char_u *)"{...}"; + } + else + { + tv->vval.v_dict->dv_copyID = copyID; + *tofree = dict2string(tv, copyID); + r = *tofree; + } + break; + case VAR_STRING: case VAR_NUMBER: *tofree = NULL; r = get_tv_string_buf(tv, numbuf); break; + default: EMSG2(_(e_intern2), "echo_string()"); *tofree = NULL; @@ -6590,10 +6650,11 @@ echo_string(tv, tofree, numbuf) * May return NULL; */ static char_u * -tv2string(tv, tofree, numbuf) +tv2string(tv, tofree, numbuf, copyID) typval_T *tv; char_u **tofree; char_u *numbuf; + int copyID; { switch (tv->v_type) { @@ -6610,7 +6671,7 @@ tv2string(tv, tofree, numbuf) default: EMSG2(_(e_intern2), "tv2string()"); } - return echo_string(tv, tofree, numbuf); + return echo_string(tv, tofree, numbuf, copyID); } /* @@ -11302,7 +11363,7 @@ f_join(argvars, rettv) if (sep != NULL) { ga_init2(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, sep, TRUE); + list_join(&ga, argvars[0].vval.v_list, sep, TRUE, 0); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; } @@ -11695,7 +11756,7 @@ find_some_match(argvars, rettv, type) break; } vim_free(tofree); - str = echo_string(&li->li_tv, &tofree, strbuf); + str = echo_string(&li->li_tv, &tofree, strbuf,0); if (str == NULL) break; } @@ -13734,8 +13795,8 @@ item_compare(s1, s2) char_u numbuf1[NUMBUFLEN]; char_u numbuf2[NUMBUFLEN]; - p1 = tv2string(&(*(listitem_T **)s1)->li_tv, &tofree1, numbuf1); - p2 = tv2string(&(*(listitem_T **)s2)->li_tv, &tofree2, numbuf2); + p1 = tv2string(&(*(listitem_T **)s1)->li_tv, &tofree1, numbuf1, 0); + p2 = tv2string(&(*(listitem_T **)s2)->li_tv, &tofree2, numbuf2, 0); if (item_compare_ic) res = STRICMP(p1, p2); else @@ -14212,7 +14273,7 @@ f_string(argvars, rettv) char_u numbuf[NUMBUFLEN]; rettv->v_type = VAR_STRING; - rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf); + rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf, 0); if (tofree == NULL) rettv->vval.v_string = vim_strsave(rettv->vval.v_string); } @@ -16407,7 +16468,7 @@ list_one_var(v, prefix) char_u *s; char_u numbuf[NUMBUFLEN]; - s = echo_string(&v->di_tv, &tofree, numbuf); + s = echo_string(&v->di_tv, &tofree, numbuf, ++current_copyID); list_one_var_a(prefix, v->di_key, v->di_tv.v_type, s == NULL ? (char_u *)"" : s); vim_free(tofree); @@ -16782,7 +16843,7 @@ ex_echo(eap) } else if (eap->cmdidx == CMD_echo) msg_puts_attr((char_u *)" ", echo_attr); - p = echo_string(&rettv, &tofree, numbuf); + p = echo_string(&rettv, &tofree, numbuf, ++current_copyID); if (p != NULL) for ( ; *p != NUL && !got_int; ++p) { @@ -18499,7 +18560,7 @@ call_user_func(fp, argcount, argvars, re msg_outnum((long)argvars[i].vval.v_number); else { - trunc_string(tv2string(&argvars[i], &tofree, numbuf), + trunc_string(tv2string(&argvars[i], &tofree, numbuf, 0), buf, MSG_BUF_CLEN); msg_puts(buf); vim_free(tofree); @@ -18584,7 +18645,7 @@ call_user_func(fp, argcount, argvars, re /* The value may be very long. Skip the middle part, so that we * have some idea how it starts and ends. smsg() would always * truncate it at the end. */ - trunc_string(tv2string(fc.rettv, &tofree, numbuf), + trunc_string(tv2string(fc.rettv, &tofree, numbuf, 0), buf, MSG_BUF_CLEN); smsg((char_u *)_("%s returning %s"), sourcing_name, buf); vim_free(tofree); @@ -18806,7 +18867,7 @@ get_return_cmd(rettv) char_u numbuf[NUMBUFLEN]; if (rettv != NULL) - s = echo_string((typval_T *)rettv, &tofree, numbuf); + s = echo_string((typval_T *)rettv, &tofree, numbuf, 0); if (s == NULL) s = (char_u *)""; @@ -19076,7 +19137,7 @@ write_viminfo_varlist(fp) default: continue; } fprintf(fp, "!%s\t%s\t", this_var->di_key, s); - p = echo_string(&this_var->di_tv, &tofree, numbuf); + p = echo_string(&this_var->di_tv, &tofree, numbuf, 0); if (p != NULL) viminfo_writestring(fp, p); vim_free(tofree); diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -2583,11 +2583,19 @@ do_write(eap) retval = FAIL; goto theend; } + + /* If 'filetype' was empty try detecting it now. */ + if (*curbuf->b_p_ft == NUL) + { + (void)do_doautocmd((char_u *)"filetypedetect BufRead", TRUE); + do_modelines(FALSE); + } #endif } retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2, eap, eap->append, eap->forceit, TRUE, FALSE); + } theend: @@ -2861,7 +2869,7 @@ getfile(fnum, ffname, sfname, setpm, lnu int retval; char_u *free_me = NULL; - if (editing_cmdline()) + if (text_locked()) return 1; if (fnum == 0) diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -661,13 +661,13 @@ getcmdline(firstc, count, indent) restore_cmdline(&save_ccline); if (c == '=') { - /* Need to save and restore ccline. And set cmdline_busy + /* Need to save and restore ccline. And set "textlock" * to avoid nasty things like going to another buffer when * evaluating an expression. */ save_cmdline(&save_ccline); - ++cmdline_busy; + ++textlock; p = get_expr_line(); - --cmdline_busy; + --textlock; restore_cmdline(&save_ccline); if (p != NULL && realloc_cmdbuff((int)STRLEN(p) + 1) == OK) @@ -1875,17 +1875,18 @@ getcmdline_prompt(firstc, prompt, attr, #endif /* - * Return TRUE when the command line is being edited. That means the current - * buffer and window can't be changed. + * Return TRUE when the text must not be changed and we can't switch to + * another window or buffer. Used when editing the command line, evaluating + * 'balloonexpr', etc. */ int -editing_cmdline() +text_locked() { #ifdef FEAT_CMDWIN if (cmdwin_type != 0) return TRUE; #endif - return cmdline_busy; + return textlock != 0; } /* @@ -1893,7 +1894,7 @@ editing_cmdline() * window is open or editing the cmdline in another way. */ void -editing_cmdline_msg() +text_locked_msg() { #ifdef FEAT_CMDWIN if (cmdwin_type != 0) @@ -2814,12 +2815,12 @@ cmdline_paste(regname, literally) regname = may_get_selection(regname); #endif - /* Need to save and restore ccline. And set cmdline_busy to avoid nasty + /* Need to save and restore ccline. And set "textlock" to avoid nasty * things like going to another buffer when evaluating an expression. */ save_cmdline(&save_ccline); - ++cmdline_busy; + ++textlock; i = get_spec_reg(regname, &arg, &allocated, TRUE); - --cmdline_busy; + --textlock; restore_cmdline(&save_ccline); if (i) @@ -3837,8 +3838,8 @@ addstar(fname, len, context) /* Custom expansion takes care of special things, match * backslashes literally (perhaps also for other types?) */ - if ((context == EXPAND_USER_DEFINED || - context == EXPAND_USER_LIST) && fname[i] == '\\') + if ((context == EXPAND_USER_DEFINED + || context == EXPAND_USER_LIST) && fname[i] == '\\') new_len++; /* '\' becomes "\\" */ } retval = alloc(new_len); diff --git a/src/fold.c b/src/fold.c --- a/src/fold.c +++ b/src/fold.c @@ -1938,7 +1938,8 @@ get_foldtext(wp, lnum, lnume, foldinfo, curbuf = wp->w_buffer; ++emsg_off; - text = eval_to_string_safe(wp->w_p_fdt, NULL); + text = eval_to_string_safe(wp->w_p_fdt, NULL, + was_set_insecurely((char_u *)"foldtext")); --emsg_off; curwin = save_curwin; diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -86,7 +86,6 @@ EXTERN int clear_cmdline INIT(= FALSE); #if defined(FEAT_CRYPT) || defined(FEAT_EVAL) EXTERN int cmdline_star INIT(= FALSE); /* cmdline is crypted */ #endif -EXTERN int cmdline_busy INIT(= FALSE); /* editing the cmdline */ EXTERN int exec_from_reg INIT(= FALSE); /* executing register */ @@ -547,12 +546,16 @@ EXTERN int secure INIT(= FALSE); * allowed, e.g. when sourcing .exrc or .vimrc * in current directory */ +EXTERN int textlock INIT(= 0); + /* non-zero when changing text and jumping to + * another window or buffer is not allowed */ + #ifdef FEAT_EVAL # define HAVE_SANDBOX EXTERN int sandbox INIT(= 0); - /* non-zero when evaluating an expression in a - * "sandbox". Not allowed to change the - * buffer. */ + /* Non-zero when evaluating an expression in a + * "sandbox". Several things are not allowed + * then. */ #endif EXTERN int silent_mode INIT(= FALSE); @@ -1413,10 +1416,7 @@ EXTERN char_u e_invexprmsg[] INIT(= N_(" EXTERN char_u e_guarded[] INIT(= N_("E463: Region is guarded, cannot modify")); EXTERN char_u e_nbreadonly[] INIT(= N_("E744: NetBeans does not allow changes in read-only files")); #endif -#if defined(FEAT_INS_EXPAND) || defined(FEAT_EVAL) || defined(FEAT_SYN_HL) \ - || defined(PROTO) EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s")); -#endif EXTERN char_u e_maxmempat[] INIT(= N_("E363: pattern uses more memory than 'maxmempattern'")); EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer")); diff --git a/src/gui_beval.c b/src/gui_beval.c --- a/src/gui_beval.c +++ b/src/gui_beval.c @@ -23,6 +23,7 @@ general_beval_cb(beval, state) { win_T *wp; int col; + int use_sandbox; linenr_T lnum; char_u *text; static char_u *result = NULL; @@ -50,10 +51,17 @@ general_beval_cb(beval, state) set_vim_var_string(VV_BEVAL_TEXT, text, -1); vim_free(text); - ++sandbox; + use_sandbox = was_set_insecurely((char_u *)"balloonexpr"); + if (use_sandbox) + ++sandbox; + ++textlock; + vim_free(result); result = eval_to_string(p_bexpr, NULL); - --sandbox; + + if (use_sandbox) + --sandbox; + --textlock; set_vim_var_string(VV_BEVAL_TEXT, NULL, -1); if (result != NULL && result[0] != NUL) diff --git a/src/if_python.c b/src/if_python.c --- a/src/if_python.c +++ b/src/if_python.c @@ -1082,35 +1082,137 @@ VimCommand(PyObject *self, PyObject *arg return result; } +/* + * Function to translate a typval_T into a PyObject; this will recursively + * translate lists/dictionaries into their Python equivalents. + * + * The depth parameter is too avoid infinite recursion, set it to 1 when + * you call VimToPython. + */ + static PyObject * +VimToPython(typval_T *our_tv, int depth, PyObject *lookupDict) +{ + PyObject *result; + PyObject *newObj; + char ptrBuf[NUMBUFLEN]; + + /* Avoid infinite recursion */ + if (depth > 100) + { + Py_INCREF(Py_None); + result = Py_None; + return result; + } + + /* Check if we run into a recursive loop. The item must be in lookupDict + * then and we can use it again. */ + sprintf(ptrBuf, "%ld", (long)our_tv); + result = PyDict_GetItemString(lookupDict, ptrBuf); + if (result != NULL) + Py_INCREF(result); + else if (our_tv->v_type == VAR_STRING) + { + result = Py_BuildValue("s", our_tv->vval.v_string); + PyDict_SetItemString(lookupDict, ptrBuf, result); + } + else if (our_tv->v_type == VAR_NUMBER) + { + char buf[NUMBUFLEN]; + + /* For backwards compatibility numbers are stored as strings. */ + sprintf(buf, "%ld", (long)our_tv->vval.v_number); + result = Py_BuildValue("s", buf); + PyDict_SetItemString(lookupDict, ptrBuf, result); + } + else if (our_tv->v_type == VAR_LIST) + { + list_T *list = our_tv->vval.v_list; + listitem_T *curr; + + result = PyList_New(0); + PyDict_SetItemString(lookupDict, ptrBuf, result); + + if (list != NULL) + { + for (curr = list->lv_first; curr != NULL; curr = curr->li_next) + { + newObj = VimToPython(&curr->li_tv, depth + 1, lookupDict); + PyList_Append(result, newObj); + Py_DECREF(newObj); + } + } + } + else if (our_tv->v_type == VAR_DICT) + { + result = PyDict_New(); + PyDict_SetItemString(lookupDict, ptrBuf, result); + + if (our_tv->vval.v_dict != NULL) + { + hashtab_T *ht = &our_tv->vval.v_dict->dv_hashtab; + int todo = ht->ht_used; + hashitem_T *hi; + dictitem_T *di; + + for (hi = ht->ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + + di = dict_lookup(hi); + newObj = VimToPython(&di->di_tv, depth + 1, lookupDict); + PyDict_SetItemString(result, (char *)hi->hi_key, newObj); + Py_DECREF(newObj); + } + } + } + } + else + { + Py_INCREF(Py_None); + result = Py_None; + } + + return result; +} + /*ARGSUSED*/ static PyObject * VimEval(PyObject *self, PyObject *args) { #ifdef FEAT_EVAL char *expr; - char *str; + typval_T *our_tv; PyObject *result; + PyObject *lookup_dict; if (!PyArg_ParseTuple(args, "s", &expr)) return NULL; Py_BEGIN_ALLOW_THREADS Python_Lock_Vim(); - str = (char *)eval_to_string((char_u *)expr, NULL); + our_tv = eval_expr((char_u *)expr, NULL); + Python_Release_Vim(); Py_END_ALLOW_THREADS - if (str == NULL) + if (our_tv == NULL) { PyErr_SetVim(_("invalid expression")); return NULL; } - result = Py_BuildValue("s", str); + /* Convert the Vim type into a Python type. Create a dictionary that's + * used to check for recursive loops. */ + lookup_dict = PyDict_New(); + result = VimToPython(our_tv, 1, lookup_dict); + Py_DECREF(lookup_dict); + Py_BEGIN_ALLOW_THREADS Python_Lock_Vim(); - vim_free(str); + free_tv(our_tv); Python_Release_Vim(); Py_END_ALLOW_THREADS diff --git a/src/misc1.c b/src/misc1.c --- a/src/misc1.c +++ b/src/misc1.c @@ -7673,12 +7673,17 @@ get_expr_indent() int indent; pos_T pos; int save_State; + int use_sandbox = was_set_insecurely((char_u *)"indentexpr"); pos = curwin->w_cursor; set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum); - ++sandbox; + if (use_sandbox) + ++sandbox; + ++textlock; indent = eval_to_number(curbuf->b_p_inde); - --sandbox; + if (use_sandbox) + --sandbox; + --textlock; /* Restore the cursor position so that 'indentexpr' doesn't need to. * Pretend to be in Insert mode, allow cursor past end of line for "o" diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -317,6 +317,7 @@ struct vimoption #define P_GETTEXT 0x80000L/* expand default value with _() */ #define P_NOGLOB 0x100000L/* do not use local value for global vimrc */ #define P_NFNAME 0x200000L/* only normal file name chars allowed */ +#define P_INSECURE 0x400000L/* option was set from a modeline */ /* * options[] is initialized here. @@ -1460,7 +1461,7 @@ static struct vimoption {"magic", NULL, P_BOOL|P_VI_DEF, (char_u *)&p_magic, PV_NONE, {(char_u *)TRUE, (char_u *)0L}}, - {"makeef", "mef", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, + {"makeef", "mef", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, #ifdef FEAT_QUICKFIX (char_u *)&p_mef, PV_NONE, {(char_u *)"", (char_u *)0L} @@ -2632,6 +2633,7 @@ static char *(p_cot_values[]) = {"menu", static void set_option_default __ARGS((int, int opt_flags, int compatible)); static void set_options_default __ARGS((int opt_flags)); +static void did_set_option __ARGS((int opt_idx, int opt_flags, int new_value)); static char_u *illegal_char __ARGS((char_u *, int)); static int string_to_key __ARGS((char_u *arg)); #ifdef FEAT_CMDWIN @@ -3157,6 +3159,9 @@ set_option_default(opt_idx, opt_flags, c *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *(int *)varp; } + + /* the default value is not insecure */ + options[opt_idx].flags &= ~P_INSECURE; } #ifdef FEAT_EVAL @@ -3790,6 +3795,12 @@ do_set(arg, opt_flags) } } + /* Skip all options that are not window-local (used when showing + * an already loaded buffer in a window). */ + if ((opt_flags & OPT_WINONLY) + && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) + goto skip; + /* Disallow changing some options from modelines */ if ((opt_flags & OPT_MODELINE) && (flags & P_SECURE)) { @@ -3797,15 +3808,9 @@ do_set(arg, opt_flags) goto skip; } - /* Skip all options that are not window-local (used when showing - * an already loaded buffer in a window). */ - if ((opt_flags & OPT_WINONLY) - && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) - goto skip; - #ifdef HAVE_SANDBOX /* Disallow changing some options in the sandbox */ - if (sandbox > 0 && (flags & P_SECURE)) + if (sandbox != 0 && (flags & P_SECURE)) { errmsg = (char_u *)_(e_sandbox); goto skip; @@ -4343,8 +4348,10 @@ do_set(arg, opt_flags) redraw_all_later(CLEAR); } } + if (opt_idx >= 0) - options[opt_idx].flags |= P_WAS_SET; + did_set_option(opt_idx, opt_flags, + !prepending && !adding && !removing); } skip: @@ -4405,6 +4412,31 @@ theend: return OK; } +/* + * Call this when an option has been given a new value through a user command. + * Sets the P_WAS_SET flag and takes care of the P_INSECURE flag. + */ + static void +did_set_option(opt_idx, opt_flags, new_value) + int opt_idx; + int opt_flags; /* possibly with OPT_MODELINE */ + int new_value; /* value was replaced completely */ +{ + options[opt_idx].flags |= P_WAS_SET; + + /* When an option is set in the sandbox, from a modeline or in secure mode + * set the P_INSECURE flag. Otherwise, if a new value is stored reset the + * flag. */ + if (secure +#ifdef HAVE_SANDBOX + || sandbox != 0 +#endif + || (opt_flags & OPT_MODELINE)) + options[opt_idx].flags |= P_INSECURE; + else if (new_value) + options[opt_idx].flags &= ~P_INSECURE; +} + static char_u * illegal_char(errbuf, c) char_u *errbuf; @@ -4837,6 +4869,25 @@ set_term_option_alloced(p) return; /* cannot happen: didn't find it! */ } +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * Return TRUE when option "opt" was set from a modeline or in secure mode. + * Return FALSE when it wasn't. + * Return -1 for an unknown option. + */ + int +was_set_insecurely(opt) + char_u *opt; +{ + int idx = findoption(opt); + + if (idx >= 0) + return (options[idx].flags & P_INSECURE) != 0; + EMSG2(_(e_intern2), "was_set_insecurely()"); + return -1; +} +#endif + /* * Set a string option to a new value (without checking the effect). * The string is copied into allocated memory. @@ -4938,9 +4989,9 @@ set_string_option(opt_idx, value, opt_fl : opt_flags); oldval = *varp; *varp = s; - options[opt_idx].flags |= P_WAS_SET; - (void)did_set_string_option(opt_idx, varp, TRUE, oldval, NULL, - opt_flags); + if (did_set_string_option(opt_idx, varp, TRUE, oldval, NULL, + opt_flags) == NULL) + did_set_option(opt_idx, opt_flags, TRUE); } } @@ -6571,10 +6622,6 @@ set_bool_option(opt_idx, varp, value, op { int old_value = *(int *)varp; -#ifdef FEAT_GUI - need_mouse_correct = TRUE; -#endif - /* Disallow changing some options from secure mode */ if ((secure #ifdef HAVE_SANDBOX @@ -6589,6 +6636,10 @@ set_bool_option(opt_idx, varp, value, op options[opt_idx].scriptID = current_SID; #endif +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + /* May set global value for local option. */ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; @@ -7077,15 +7128,22 @@ set_num_option(opt_idx, varp, value, err long old_Columns = Columns; /* remember old Columns */ long *pp = (long *)varp; -#ifdef FEAT_GUI - need_mouse_correct = TRUE; -#endif + /* Disallow changing some options from secure mode. */ + if ((secure +#ifdef HAVE_SANDBOX + || sandbox != 0 +#endif + ) && (options[opt_idx].flags & P_SECURE)) + return e_secure; *pp = value; #ifdef FEAT_EVAL /* Remember where the option was set. */ options[opt_idx].scriptID = current_SID; #endif +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif if (curbuf->b_p_sw <= 0) { @@ -7690,10 +7748,12 @@ set_option_value(name, number, string, o #ifdef HAVE_SANDBOX /* Disallow changing some options in the sandbox */ if (sandbox > 0 && (flags & P_SECURE)) + { EMSG(_(e_sandbox)); - else -#endif - if (flags & P_STRING) + return; + } +#endif + if (flags & P_STRING) set_string_option(opt_idx, string, opt_flags); else { @@ -7704,7 +7764,8 @@ set_option_value(name, number, string, o (void)set_num_option(opt_idx, varp, number, NULL, 0, opt_flags); else - (void)set_bool_option(opt_idx, varp, (int)number, opt_flags); + (void)set_bool_option(opt_idx, varp, (int)number, + opt_flags); } } } diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -18,7 +18,7 @@ int eval_to_bool __ARGS((char_u *arg, in char_u *eval_to_string_skip __ARGS((char_u *arg, char_u **nextcmd, int skip)); int skip_expr __ARGS((char_u **pp)); char_u *eval_to_string __ARGS((char_u *arg, char_u **nextcmd)); -char_u *eval_to_string_safe __ARGS((char_u *arg, char_u **nextcmd)); +char_u *eval_to_string_safe __ARGS((char_u *arg, char_u **nextcmd, int use_sandbox)); int eval_to_number __ARGS((char_u *expr)); list_T *eval_spell_expr __ARGS((char_u *badword, char_u *expr)); int get_spellword __ARGS((list_T *list, char_u **pp)); @@ -43,6 +43,7 @@ int do_unlet __ARGS((char_u *name, int f void del_menutrans_vars __ARGS((void)); char_u *get_user_var_name __ARGS((expand_T *xp, int idx)); void list_unref __ARGS((list_T *l)); +dictitem_T *dict_lookup __ARGS((hashitem_T *hi)); int list_append_dict __ARGS((list_T *list, dict_T *dict)); int garbage_collect __ARGS((void)); dict_T *dict_alloc __ARGS((void)); diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -1880,7 +1880,7 @@ ex_copen(eap) set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); set_option_value((char_u *)"bt", 0L, (char_u *)"quickfix", OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, (char_u *)"delete", OPT_LOCAL); + set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); set_option_value((char_u *)"diff", 0L, (char_u *)"", OPT_LOCAL); } else if (buf != curbuf) diff --git a/src/undo.c b/src/undo.c --- a/src/undo.c +++ b/src/undo.c @@ -182,7 +182,7 @@ undo_allowed() /* Don't allow changes in the buffer while editing the cmdline. The * caller of getcmdline() may get confused. */ - if (cmdline_busy) + if (textlock != 0) { EMSG(_(e_secure)); return FALSE; diff --git a/src/version.h b/src/version.h --- 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 (2006 Jan 19)" -#define VIM_VERSION_LONG_DATE "VIM - Vi IMproved 7.0aa ALPHA (2006 Jan 19, compiled " +#define VIM_VERSION_LONG "VIM - Vi IMproved 7.0aa ALPHA (2006 Jan 20)" +#define VIM_VERSION_LONG_DATE "VIM - Vi IMproved 7.0aa ALPHA (2006 Jan 20, compiled "