diff options
Diffstat (limited to 'src/nvim/eval')
-rw-r--r-- | src/nvim/eval/buffer.c | 734 | ||||
-rw-r--r-- | src/nvim/eval/buffer.h | 10 | ||||
-rw-r--r-- | src/nvim/eval/decode.c | 76 | ||||
-rw-r--r-- | src/nvim/eval/encode.c | 59 | ||||
-rw-r--r-- | src/nvim/eval/encode.h | 16 | ||||
-rw-r--r-- | src/nvim/eval/executor.c | 21 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 2710 | ||||
-rw-r--r-- | src/nvim/eval/funcs.h | 22 | ||||
-rw-r--r-- | src/nvim/eval/gc.c | 5 | ||||
-rw-r--r-- | src/nvim/eval/gc.h | 1 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 289 | ||||
-rw-r--r-- | src/nvim/eval/typval.h | 469 | ||||
-rw-r--r-- | src/nvim/eval/typval_defs.h | 399 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.c.h | 32 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.h | 4 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 1043 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.h | 85 | ||||
-rw-r--r-- | src/nvim/eval/vars.c | 346 | ||||
-rw-r--r-- | src/nvim/eval/vars.h | 2 | ||||
-rw-r--r-- | src/nvim/eval/window.c | 954 | ||||
-rw-r--r-- | src/nvim/eval/window.h | 78 |
21 files changed, 4259 insertions, 3096 deletions
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c new file mode 100644 index 0000000000..2f37d1ba2e --- /dev/null +++ b/src/nvim/eval/buffer.c @@ -0,0 +1,734 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// eval/buffer.c: Buffer related builtin functions + +#include <stdbool.h> +#include <string.h> + +#include "nvim/ascii.h" +#include "nvim/autocmd.h" +#include "nvim/buffer.h" +#include "nvim/buffer_defs.h" +#include "nvim/change.h" +#include "nvim/cursor.h" +#include "nvim/eval.h" +#include "nvim/eval/buffer.h" +#include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/globals.h" +#include "nvim/macros.h" +#include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/move.h" +#include "nvim/path.h" +#include "nvim/pos.h" +#include "nvim/sign.h" +#include "nvim/types.h" +#include "nvim/undo.h" +#include "nvim/vim.h" + +typedef struct { + win_T *cob_curwin_save; + aco_save_T cob_aco; + int cob_using_aco; + int cob_save_VIsual_active; +} cob_T; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/buffer.c.generated.h" +#endif + +/// Find a buffer by number or exact name. +buf_T *find_buffer(typval_T *avar) +{ + buf_T *buf = NULL; + + if (avar->v_type == VAR_NUMBER) { + buf = buflist_findnr((int)avar->vval.v_number); + } else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) { + buf = buflist_findname_exp(avar->vval.v_string); + if (buf == NULL) { + // No full path name match, try a match with a URL or a "nofile" + // buffer, these don't use the full path. + FOR_ALL_BUFFERS(bp) { + if (bp->b_fname != NULL + && (path_with_url(bp->b_fname) || bt_nofilename(bp)) + && strcmp(bp->b_fname, avar->vval.v_string) == 0) { + buf = bp; + break; + } + } + } + } + return buf; +} + +/// If there is a window for "curbuf", make it the current window. +static void find_win_for_curbuf(void) +{ + wininfo_T *wip; + + // The b_wininfo list should have the windows that recently contained the + // buffer, going over this is faster than going over all the windows. + // Do check the buffer is still there. + FOR_ALL_BUF_WININFO(curbuf, wip) { + if (wip->wi_win != NULL && wip->wi_win->w_buffer == curbuf) { + curwin = wip->wi_win; + break; + } + } +} + +/// Used before making a change in "buf", which is not the current one: Make +/// "buf" the current buffer and find a window for this buffer, so that side +/// effects are done correctly (e.g., adjusting marks). +/// +/// Information is saved in "cob" and MUST be restored by calling +/// change_other_buffer_restore(). +static void change_other_buffer_prepare(cob_T *cob, buf_T *buf) +{ + CLEAR_POINTER(cob); + + // Set "curbuf" to the buffer being changed. Then make sure there is a + // window for it to handle any side effects. + cob->cob_save_VIsual_active = VIsual_active; + VIsual_active = false; + cob->cob_curwin_save = curwin; + curbuf = buf; + find_win_for_curbuf(); // simplest: find existing window for "buf" + + if (curwin->w_buffer != buf) { + // No existing window for this buffer. It is dangerous to have + // curwin->w_buffer differ from "curbuf", use the autocmd window. + curbuf = curwin->w_buffer; + aucmd_prepbuf(&cob->cob_aco, buf); + cob->cob_using_aco = true; + } +} + +static void change_other_buffer_restore(cob_T *cob) +{ + if (cob->cob_using_aco) { + aucmd_restbuf(&cob->cob_aco); + } else { + curwin = cob->cob_curwin_save; + curbuf = curwin->w_buffer; + } + VIsual_active = cob->cob_save_VIsual_active; +} + +/// Set line or list of lines in buffer "buf" to "lines". +/// Any type is allowed and converted to a string. +static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_T *lines, + typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(4, 5) +{ + linenr_T lnum = lnum_arg + (append ? 1 : 0); + long added = 0; + + // When using the current buffer ml_mfp will be set if needed. Useful when + // setline() is used on startup. For other buffers the buffer must be + // loaded. + const bool is_curbuf = buf == curbuf; + if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) { + rettv->vval.v_number = 1; // FAIL + return; + } + + // After this don't use "return", goto "cleanup"! + cob_T cob; + if (!is_curbuf) { + // set "curbuf" to "buf" and find a window for this buffer + change_other_buffer_prepare(&cob, buf); + } + + linenr_T append_lnum; + if (append) { + // appendbufline() uses the line number below which we insert + append_lnum = lnum - 1; + } else { + // setbufline() uses the line number above which we insert, we only + // append if it's below the last line + append_lnum = curbuf->b_ml.ml_line_count; + } + + list_T *l = NULL; + listitem_T *li = NULL; + char *line = NULL; + if (lines->v_type == VAR_LIST) { + l = lines->vval.v_list; + if (l == NULL || tv_list_len(l) == 0) { + // set proper return code + if (lnum > curbuf->b_ml.ml_line_count) { + rettv->vval.v_number = 1; // FAIL + } + goto cleanup; + } + li = tv_list_first(l); + } else { + line = typval_tostring(lines, false); + } + + // Default result is zero == OK. + for (;;) { + if (lines->v_type == VAR_LIST) { + // List argument, get next string. + if (li == NULL) { + break; + } + xfree(line); + line = typval_tostring(TV_LIST_ITEM_TV(li), false); + li = TV_LIST_ITEM_NEXT(l, li); + } + + rettv->vval.v_number = 1; // FAIL + if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) { + break; + } + + // When coming here from Insert mode, sync undo, so that this can be + // undone separately from what was previously inserted. + if (u_sync_once == 2) { + u_sync_once = 1; // notify that u_sync() was called + u_sync(true); + } + + if (!append && lnum <= curbuf->b_ml.ml_line_count) { + // Existing line, replace it. + int old_len = (int)strlen(ml_get(lnum)); + if (u_savesub(lnum) == OK + && ml_replace(lnum, line, true) == OK) { + inserted_bytes(lnum, 0, old_len, (int)strlen(line)); + if (is_curbuf && lnum == curwin->w_cursor.lnum) { + check_cursor_col(); + } + rettv->vval.v_number = 0; // OK + } + } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { + // append the line. + added++; + if (ml_append(lnum - 1, line, 0, false) == OK) { + rettv->vval.v_number = 0; // OK + } + } + + if (l == NULL) { // only one string argument + break; + } + lnum++; + } + xfree(line); + + if (added > 0) { + appended_lines_mark(append_lnum, added); + + // Only adjust the cursor for buffers other than the current, unless it + // is the current window. For curbuf and other windows it has been done + // in mark_adjust_internal(). + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf + && (wp->w_buffer != curbuf || wp == curwin) + && wp->w_cursor.lnum > append_lnum) { + wp->w_cursor.lnum += (linenr_T)added; + } + } + check_cursor_col(); + update_topline(curwin); + } + +cleanup: + if (!is_curbuf) { + change_other_buffer_restore(&cob); + } +} + +/// "append(lnum, string/list)" function +void f_append(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const int did_emsg_before = did_emsg; + const linenr_T lnum = tv_get_lnum(&argvars[0]); + if (did_emsg == did_emsg_before) { + set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv); + } +} + +/// Set or append lines to a buffer. +static void buf_set_append_line(typval_T *argvars, typval_T *rettv, bool append) +{ + const int did_emsg_before = did_emsg; + buf_T *const buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + rettv->vval.v_number = 1; // FAIL + } else { + const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); + if (did_emsg == did_emsg_before) { + set_buffer_lines(buf, lnum, append, &argvars[2], rettv); + } + } +} + +/// "appendbufline(buf, lnum, string/list)" function +void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_set_append_line(argvars, rettv, true); +} + +/// "bufadd(expr)" function +void f_bufadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + char *name = (char *)tv_get_string(&argvars[0]); + + rettv->vval.v_number = buflist_add(*name == NUL ? NULL : name, 0); +} + +/// "bufexists(expr)" function +void f_bufexists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL); +} + +/// "buflisted(expr)" function +void f_buflisted(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_T *buf; + + buf = find_buffer(&argvars[0]); + rettv->vval.v_number = (buf != NULL && buf->b_p_bl); +} + +/// "bufload(expr)" function +void f_bufload(typval_T *argvars, typval_T *unused, EvalFuncData fptr) +{ + buf_T *buf = get_buf_arg(&argvars[0]); + + if (buf != NULL) { + buffer_ensure_loaded(buf); + } +} + +/// "bufloaded(expr)" function +void f_bufloaded(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_T *buf; + + buf = find_buffer(&argvars[0]); + rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); +} + +/// "bufname(expr)" function +void f_bufname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const buf_T *buf; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (argvars[0].v_type == VAR_UNKNOWN) { + buf = curbuf; + } else { + buf = tv_get_buf_from_arg(&argvars[0]); + } + if (buf != NULL && buf->b_fname != NULL) { + rettv->vval.v_string = xstrdup(buf->b_fname); + } +} + +/// "bufnr(expr)" function +void f_bufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const buf_T *buf; + bool error = false; + + rettv->vval.v_number = -1; + + if (argvars[0].v_type == VAR_UNKNOWN) { + buf = curbuf; + } else { + // Don't use tv_get_buf_from_arg(); we continue if the buffer wasn't found + // and the second argument isn't zero, but we want to return early if the + // first argument isn't a string or number so only one error is shown. + if (!tv_check_str_or_nr(&argvars[0])) { + return; + } + emsg_off++; + buf = tv_get_buf(&argvars[0], false); + emsg_off--; + } + + // If the buffer isn't found and the second argument is not zero create a + // new buffer. + const char *name; + if (buf == NULL + && argvars[1].v_type != VAR_UNKNOWN + && tv_get_number_chk(&argvars[1], &error) != 0 + && !error + && (name = tv_get_string_chk(&argvars[0])) != NULL) { + buf = buflist_new((char *)name, NULL, 1, 0); + } + + if (buf != NULL) { + rettv->vval.v_number = buf->b_fnum; + } +} + +static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr) +{ + const buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); + if (buf == NULL) { // no need to search if invalid arg or buffer not found + rettv->vval.v_number = -1; + return; + } + + int winnr = 0; + int winid; + bool found_buf = false; + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + winnr++; + if (wp->w_buffer == buf) { + found_buf = true; + winid = wp->handle; + break; + } + } + rettv->vval.v_number = (found_buf ? (get_nr ? winnr : winid) : -1); +} + +/// "bufwinid(nr)" function +void f_bufwinid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_win_common(argvars, rettv, false); +} + +/// "bufwinnr(nr)" function +void f_bufwinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_win_common(argvars, rettv, true); +} + +/// "deletebufline()" function +void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const int did_emsg_before = did_emsg; + rettv->vval.v_number = 1; // FAIL by default + buf_T *const buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + return; + } + + linenr_T last; + const linenr_T first = tv_get_lnum_buf(&argvars[1], buf); + if (did_emsg > did_emsg_before) { + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + last = tv_get_lnum_buf(&argvars[2], buf); + } else { + last = first; + } + + if (buf->b_ml.ml_mfp == NULL || first < 1 + || first > buf->b_ml.ml_line_count || last < first) { + return; + } + + // After this don't use "return", goto "cleanup"! + const bool is_curbuf = buf == curbuf; + cob_T cob; + if (!is_curbuf) { + // set "curbuf" to "buf" and find a window for this buffer + change_other_buffer_prepare(&cob, buf); + } + + if (last > curbuf->b_ml.ml_line_count) { + last = curbuf->b_ml.ml_line_count; + } + const long count = last - first + 1; + + // When coming here from Insert mode, sync undo, so that this can be + // undone separately from what was previously inserted. + if (u_sync_once == 2) { + u_sync_once = 1; // notify that u_sync() was called + u_sync(true); + } + + if (u_save(first - 1, last + 1) == FAIL) { + goto cleanup; + } + + for (linenr_T lnum = first; lnum <= last; lnum++) { + ml_delete(first, true); + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + if (wp->w_cursor.lnum > last) { + wp->w_cursor.lnum -= (linenr_T)count; + } else if (wp->w_cursor.lnum > first) { + wp->w_cursor.lnum = first; + } + if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) { + wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; + } + } + } + check_cursor_col(); + deleted_lines_mark(first, count); + rettv->vval.v_number = 0; // OK + +cleanup: + if (!is_curbuf) { + change_other_buffer_restore(&cob); + } +} + +/// @return buffer options, variables and other attributes in a dictionary. +static dict_T *get_buffer_info(buf_T *buf) +{ + dict_T *const dict = tv_dict_alloc(); + + tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); + tv_dict_add_str(dict, S_LEN("name"), + buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); + tv_dict_add_nr(dict, S_LEN("lnum"), + buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count); + tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); + tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); + tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); + tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf)); + tv_dict_add_nr(dict, S_LEN("hidden"), + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); + + // Get a reference to buffer variables + tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); + + // List of windows displaying this buffer + list_T *const windows = tv_list_alloc(kListLenMayKnow); + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + tv_list_append_number(windows, (varnumber_T)wp->handle); + } + } + tv_dict_add_list(dict, S_LEN("windows"), windows); + + if (buf->b_signlist != NULL) { + // List of signs placed in this buffer + tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); + } + + tv_dict_add_nr(dict, S_LEN("lastused"), buf->b_last_used); + + return dict; +} + +/// "getbufinfo()" function +void f_getbufinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_T *argbuf = NULL; + bool filtered = false; + bool sel_buflisted = false; + bool sel_bufloaded = false; + bool sel_bufmodified = false; + + tv_list_alloc_ret(rettv, kListLenMayKnow); + + // List of all the buffers or selected buffers + if (argvars[0].v_type == VAR_DICT) { + dict_T *sel_d = argvars[0].vval.v_dict; + + if (sel_d != NULL) { + dictitem_T *di; + + filtered = true; + + di = tv_dict_find(sel_d, S_LEN("buflisted")); + if (di != NULL && tv_get_number(&di->di_tv)) { + sel_buflisted = true; + } + + di = tv_dict_find(sel_d, S_LEN("bufloaded")); + if (di != NULL && tv_get_number(&di->di_tv)) { + sel_bufloaded = true; + } + di = tv_dict_find(sel_d, S_LEN("bufmodified")); + if (di != NULL && tv_get_number(&di->di_tv)) { + sel_bufmodified = true; + } + } + } else if (argvars[0].v_type != VAR_UNKNOWN) { + // Information about one buffer. Argument specifies the buffer + argbuf = tv_get_buf_from_arg(&argvars[0]); + if (argbuf == NULL) { + return; + } + } + + // Return information about all the buffers or a specified buffer + FOR_ALL_BUFFERS(buf) { + if (argbuf != NULL && argbuf != buf) { + continue; + } + if (filtered && ((sel_bufloaded && buf->b_ml.ml_mfp == NULL) + || (sel_buflisted && !buf->b_p_bl) + || (sel_bufmodified && !buf->b_changed))) { + continue; + } + + dict_T *const d = get_buffer_info(buf); + tv_list_append_dict(rettv->vval.v_list, d); + if (argbuf != NULL) { + return; + } + } +} + +/// Get line or list of lines from buffer "buf" into "rettv". +/// +/// @param retlist if true, then the lines are returned as a Vim List. +/// +/// @return range (from start to end) of lines in rettv from the specified +/// buffer. +static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) +{ + rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); + rettv->vval.v_string = NULL; + + if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) { + if (retlist) { + tv_list_alloc_ret(rettv, 0); + } + return; + } + + if (retlist) { + if (start < 1) { + start = 1; + } + if (end > buf->b_ml.ml_line_count) { + end = buf->b_ml.ml_line_count; + } + tv_list_alloc_ret(rettv, end - start + 1); + while (start <= end) { + tv_list_append_string(rettv->vval.v_list, + (const char *)ml_get_buf(buf, start++, false), -1); + } + } else { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count) + ? xstrdup(ml_get_buf(buf, start, false)) : NULL); + } +} + +/// @param retlist true: "getbufline()" function +/// false: "getbufoneline()" function +static void getbufline(typval_T *argvars, typval_T *rettv, bool retlist) +{ + const int did_emsg_before = did_emsg; + buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); + const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); + if (did_emsg > did_emsg_before) { + return; + } + const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN + ? lnum + : tv_get_lnum_buf(&argvars[2], buf)); + + get_buffer_lines(buf, lnum, end, retlist, rettv); +} + +/// "getbufline()" function +void f_getbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + getbufline(argvars, rettv, true); +} + +/// "getbufoneline()" function +void f_getbufoneline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + getbufline(argvars, rettv, false); +} + +/// "getline(lnum, [end])" function +void f_getline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + linenr_T end; + bool retlist; + + const linenr_T lnum = tv_get_lnum(argvars); + if (argvars[1].v_type == VAR_UNKNOWN) { + end = lnum; + retlist = false; + } else { + end = tv_get_lnum(&argvars[1]); + retlist = true; + } + + get_buffer_lines(curbuf, lnum, end, retlist, rettv); +} + +/// "setbufline()" function +void f_setbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_set_append_line(argvars, rettv, false); +} + +/// "setline()" function +void f_setline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const int did_emsg_before = did_emsg; + linenr_T lnum = tv_get_lnum(&argvars[0]); + if (did_emsg == did_emsg_before) { + set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv); + } +} + +/// Make "buf" the current buffer. +/// +/// restore_buffer() MUST be called to undo. +/// No autocommands will be executed. Use aucmd_prepbuf() if there are any. +void switch_buffer(bufref_T *save_curbuf, buf_T *buf) +{ + block_autocmds(); + set_bufref(save_curbuf, curbuf); + curbuf->b_nwindows--; + curbuf = buf; + curwin->w_buffer = buf; + curbuf->b_nwindows++; +} + +/// Restore the current buffer after using switch_buffer(). +void restore_buffer(bufref_T *save_curbuf) +{ + unblock_autocmds(); + // Check for valid buffer, just in case. + if (bufref_valid(save_curbuf)) { + curbuf->b_nwindows--; + curwin->w_buffer = save_curbuf->br_buf; + curbuf = save_curbuf->br_buf; + curbuf->b_nwindows++; + } +} + +/// Find a window for buffer "buf". +/// If found true is returned and "wp" and "tp" are set to +/// the window and tabpage. +/// If not found, false is returned. +/// +/// @param buf buffer to find a window for +/// @param[out] wp stores the found window +/// @param[out] tp stores the found tabpage +/// +/// @return true if a window was found for the buffer. +bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) +{ + *wp = NULL; + *tp = NULL; + FOR_ALL_TAB_WINDOWS(tp2, wp2) { + if (wp2->w_buffer == buf) { + *tp = tp2; + *wp = wp2; + return true; + } + } + return false; +} diff --git a/src/nvim/eval/buffer.h b/src/nvim/eval/buffer.h new file mode 100644 index 0000000000..4a2f8f9e94 --- /dev/null +++ b/src/nvim/eval/buffer.h @@ -0,0 +1,10 @@ +#ifndef NVIM_EVAL_BUFFER_H +#define NVIM_EVAL_BUFFER_H + +#include "nvim/buffer_defs.h" +#include "nvim/eval/typval_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/buffer.h.generated.h" +#endif +#endif // NVIM_EVAL_BUFFER_H diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 7b975ce775..cd1479f150 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -1,20 +1,31 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -#include <msgpack.h> +#include <assert.h> +#include <msgpack/object.h> +#include <stdbool.h> #include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "klib/kvec.h" #include "nvim/ascii.h" -#include "nvim/charset.h" // vim_str2nr +#include "nvim/charset.h" #include "nvim/eval.h" #include "nvim/eval/decode.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" -#include "nvim/globals.h" -#include "nvim/lib/kvec.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/hashtab.h" #include "nvim/macros.h" +#include "nvim/mbyte.h" +#include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/vim.h" // OK, FAIL +#include "nvim/types.h" +#include "nvim/vim.h" /// Helper structure for container_struct typedef struct { @@ -271,11 +282,9 @@ typval_T decode_string(const char *const s, const size_t len, const TriState has list_T *const list = tv_list_alloc(kListLenMayKnow); tv_list_ref(list); create_special_dict(&tv, kMPString, - ((typval_T){ - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); + (typval_T){ .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list } }); const int elw_ret = encode_list_write((void *)list, s, len); if (s_allocated) { xfree((void *)s); @@ -286,13 +295,12 @@ typval_T decode_string(const char *const s, const size_t len, const TriState has } } return tv; - } else { - return (typval_T) { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval = { .v_string = ((s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) }, - }; } + return (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval = { .v_string = ((s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) }, + }; } /// Parse JSON double-quoted string @@ -368,7 +376,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, goto parse_json_string_fail; } } else { - uint8_t p_byte = (uint8_t)*p; + uint8_t p_byte = (uint8_t)(*p); // unescaped = %x20-21 / %x23-5B / %x5D-10FFFF if (p_byte < 0x20) { semsg(_("E474: ASCII control characters cannot be present " @@ -430,7 +438,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, const char ubuf[] = { t[1], t[2], t[3], t[4] }; t += 4; uvarnumber_T ch; - vim_str2nr((char_u *)ubuf, NULL, NULL, + vim_str2nr(ubuf, NULL, NULL, STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true); if (ch == 0) { hasnul = true; @@ -469,7 +477,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, ['r'] = CAR, ['f'] = FF, }; - *str_end++ = escapes[(int)*t]; + *str_end++ = escapes[(int)(*t)]; break; } default: @@ -600,7 +608,7 @@ parse_json_number_check: // Convert integer varnumber_T nr; int num_len; - vim_str2nr((char_u *)s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true); + vim_str2nr(s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true); if ((int)exp_num_len != num_len) { semsg(_("E685: internal error: while converting number \"%.*s\" " "to integer vim_str2nr consumed %i bytes in place of %zu"), @@ -838,12 +846,10 @@ json_decode_string_cycle_start: .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, }; - kv_push(container_stack, ((ContainerStackItem) { - .stack_index = kv_size(stack), - .s = p, - .container = tv, - .special_val = NULL, - })); + kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = NULL })); kv_push(stack, OBJ(tv, false, didcomma, didcolon)); break; } @@ -862,12 +868,10 @@ json_decode_string_cycle_start: .vval = { .v_dict = dict }, }; } - kv_push(container_stack, ((ContainerStackItem) { - .stack_index = kv_size(stack), - .s = p, - .container = tv, - .special_val = val_list, - })); + kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack), + .s = p, + .container = tv, + .special_val = val_list })); kv_push(stack, OBJ(tv, false, didcomma, didcolon)); break; } @@ -1089,11 +1093,9 @@ msgpack_to_vim_generic_map: {} tv_list_append_number(list, mobj.via.ext.type); list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow); tv_list_append_list(list, ext_val_list); - create_special_dict(rettv, kMPExt, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); + create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list } })); if (encode_list_write((void *)ext_val_list, mobj.via.ext.ptr, mobj.via.ext.size) == -1) { return FAIL; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index bb514fba47..c2f1eae8af 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -10,23 +10,28 @@ #include <assert.h> #include <inttypes.h> #include <math.h> -#include <msgpack.h> +#include <stdbool.h> #include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include "klib/kvec.h" +#include "msgpack/pack.h" #include "nvim/ascii.h" -#include "nvim/buffer_defs.h" -#include "nvim/charset.h" // vim_isprintc() #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/garray.h" -#include "nvim/lib/kvec.h" +#include "nvim/gettext.h" +#include "nvim/hashtab.h" #include "nvim/macros.h" #include "nvim/math.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/strings.h" +#include "nvim/types.h" #include "nvim/vim.h" // For _() const char *const encode_bool_var_names[] = { @@ -131,35 +136,35 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, typval_T key_tv = { .v_type = VAR_STRING, .vval = { .v_string = - (char *)(v.data.d.hi == - NULL ? v.data.d.dict->dv_hashtab.ht_array : (v.data.d.hi - - 1))->hi_key }, + (v.data.d.hi == + NULL ? v.data.d.dict->dv_hashtab.ht_array : (v.data.d.hi - + 1))->hi_key }, }; char *const key = encode_tv2string(&key_tv, NULL); - vim_snprintf((char *)IObuff, IOSIZE, key_msg, key); + vim_snprintf(IObuff, IOSIZE, key_msg, key); xfree(key); - ga_concat(&msg_ga, (char *)IObuff); + ga_concat(&msg_ga, IObuff); break; } case kMPConvPairs: case kMPConvList: { const int idx = (v.data.l.li == tv_list_first(v.data.l.list) - ? 0 - : (v.data.l.li == NULL - ? tv_list_len(v.data.l.list) - 1 - : (int)tv_list_idx_of_item(v.data.l.list, - TV_LIST_ITEM_PREV(v.data.l.list, - v.data.l.li)))); + ? 0 + : (v.data.l.li == NULL + ? tv_list_len(v.data.l.list) - 1 + : (int)tv_list_idx_of_item(v.data.l.list, + TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)))); const listitem_T *const li = (v.data.l.li == NULL - ? tv_list_last(v.data.l.list) - : TV_LIST_ITEM_PREV(v.data.l.list, - v.data.l.li)); + ? tv_list_last(v.data.l.list) + : TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)); if (v.type == kMPConvList || li == NULL || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) { - vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx); - ga_concat(&msg_ga, (char *)IObuff); + vim_snprintf(IObuff, IOSIZE, idx_msg, idx); + ga_concat(&msg_ga, IObuff); } else { assert(li != NULL); listitem_T *const first_item = @@ -167,9 +172,9 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, assert(first_item != NULL); typval_T key_tv = *TV_LIST_ITEM_TV(first_item); char *const key = encode_tv2echo(&key_tv, NULL); - vim_snprintf((char *)IObuff, IOSIZE, key_pair_msg, key, idx); + vim_snprintf(IObuff, IOSIZE, key_pair_msg, key, idx); xfree(key); - ga_concat(&msg_ga, (char *)IObuff); + ga_concat(&msg_ga, IObuff); } break; } @@ -188,8 +193,8 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, break; case kMPConvPartialList: { const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1; - vim_snprintf((char *)IObuff, IOSIZE, partial_arg_i_msg, idx); - ga_concat(&msg_ga, (char *)IObuff); + vim_snprintf(IObuff, IOSIZE, partial_arg_i_msg, idx); + ga_concat(&msg_ga, IObuff); break; } } @@ -219,7 +224,7 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, cha } len++; if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) { - len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string); + len += strlen(TV_LIST_ITEM_TV(li)->vval.v_string); } }); if (len) { @@ -281,7 +286,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s state->offset = 0; state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL ? 0 - : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string)); + : strlen(TV_LIST_ITEM_TV(state->li)->vval.v_string)); } } *read_bytes = nbuf; @@ -304,7 +309,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s if (buf_[i_] == '\'') { \ ga_append(gap, '\''); \ } \ - ga_append(gap, buf_[i_]); \ + ga_append(gap, (uint8_t)buf_[i_]); \ } \ ga_append(gap, '\''); \ } \ diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 8755ff48ac..41e7614fc0 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -2,11 +2,15 @@ #define NVIM_EVAL_ENCODE_H #include <msgpack.h> +#include <msgpack/pack.h> #include <stddef.h> +#include <string.h> #include "nvim/eval.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/garray.h" -#include "nvim/vim.h" // For STRLEN +#include "nvim/vim.h" /// Convert VimL value to msgpack string /// @@ -15,9 +19,7 @@ /// @param[in] objname Object name, used for error message. /// /// @return OK in case of success, FAIL otherwise. -int encode_vim_to_msgpack(msgpack_packer *const packer, - typval_T *const tv, - const char *const objname); +int encode_vim_to_msgpack(msgpack_packer *packer, typval_T *tv, const char *objname); /// Convert VimL value to :echo output /// @@ -26,9 +28,7 @@ int encode_vim_to_msgpack(msgpack_packer *const packer, /// @param[in] objname Object name, used for error message. /// /// @return OK in case of success, FAIL otherwise. -int encode_vim_to_echo(garray_T *const packer, - typval_T *const tv, - const char *const objname); +int encode_vim_to_echo(garray_T *packer, typval_T *tv, const char *objname); /// Structure defining state for read_from_list() typedef struct { @@ -48,7 +48,7 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list) .offset = 0, .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL ? 0 - : STRLEN(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)), + : strlen(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)), }; } diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index b461456a3a..9caea2fef1 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -1,15 +1,23 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include <inttypes.h> +#include <stdlib.h> + #include "nvim/eval.h" #include "nvim/eval/executor.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/message.h" +#include "nvim/strings.h" +#include "nvim/types.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "eval/executor.c.generated.h" +# include "eval/executor.c.generated.h" // IWYU pragma: export #endif char *e_listidx = N_("E684: list index out of range: %" PRId64); @@ -43,7 +51,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons blob_T *const b1 = tv1->vval.v_blob; blob_T *const b2 = tv2->vval.v_blob; for (int i = 0; i < tv_blob_len(b2); i++) { - ga_append(&b1->bv_ga, (char)tv_blob_get(b2, i)); + ga_append(&b1->bv_ga, tv_blob_get(b2, i)); } } return OK; @@ -61,7 +69,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons if (tv2->v_type == VAR_LIST) { break; } - if (vim_strchr("+-*/%", *op) != NULL) { + if (vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { // nr += nr or nr -= nr, nr *= nr, nr /= nr, nr %= nr varnumber_T n = tv_get_number(tv1); if (tv2->v_type == VAR_FLOAT) { @@ -108,8 +116,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons const char *tvs = tv_get_string(tv1); char numbuf[NUMBUFLEN]; char *const s = - (char *)concat_str((const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2, - numbuf)); + concat_str(tvs, tv_get_string_buf(tv2, numbuf)); tv_clear(tv1); tv1->v_type = VAR_STRING; tv1->vval.v_string = s; @@ -123,8 +130,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons break; } const float_T f = (tv2->v_type == VAR_FLOAT - ? tv2->vval.v_float - : (float_T)tv_get_number(tv2)); + ? tv2->vval.v_float + : (float_T)tv_get_number(tv2)); switch (*op) { case '+': tv1->vval.v_float += f; break; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 9f851653eb..a6dc41d0f3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1,27 +1,44 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include <assert.h> +#include <fcntl.h> #include <float.h> +#include <inttypes.h> +#include <limits.h> #include <math.h> - +#include <msgpack/object.h> +#include <msgpack/pack.h> +#include <msgpack/unpack.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <uv.h> + +#include "auto/config.h" #include "nvim/api/private/converter.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" -#include "nvim/arglist.h" #include "nvim/ascii.h" #include "nvim/assert.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" -#include "nvim/change.h" +#include "nvim/buffer_defs.h" #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" -#include "nvim/cmdhist.h" #include "nvim/context.h" #include "nvim/cursor.h" #include "nvim/diff.h" -#include "nvim/digraph.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/buffer.h" #include "nvim/eval/decode.h" #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" @@ -29,55 +46,72 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/eval/window.h" +#include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" +#include "nvim/event/process.h" +#include "nvim/event/time.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" #include "nvim/fileio.h" -#include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/gettext.h" #include "nvim/globals.h" +#include "nvim/grid_defs.h" +#include "nvim/hashtab.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" -#include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/input.h" -#include "nvim/insexpand.h" +#include "nvim/keycodes.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" -#include "nvim/mapping.h" +#include "nvim/main.h" #include "nvim/mark.h" -#include "nvim/match.h" #include "nvim/math.h" +#include "nvim/mbyte.h" +#include "nvim/memfile_defs.h" #include "nvim/memline.h" +#include "nvim/memory.h" #include "nvim/menu.h" +#include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/server.h" +#include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/optionstr.h" #include "nvim/os/dl.h" +#include "nvim/os/fileio.h" +#include "nvim/os/fs_defs.h" +#include "nvim/os/os.h" +#include "nvim/os/pty_process.h" #include "nvim/os/shell.h" +#include "nvim/os/stdpaths_defs.h" +#include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/plines.h" #include "nvim/popupmenu.h" +#include "nvim/pos.h" #include "nvim/profile.h" -#include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/runtime.h" -#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/sha256.h" -#include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/spellsuggest.h" #include "nvim/state.h" +#include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/testing.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/version.h" @@ -106,6 +140,7 @@ typedef enum { PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH # include "funcs.generated.h" + PRAGMA_DIAG_POP PRAGMA_DIAG_POP #endif @@ -113,6 +148,8 @@ PRAGMA_DIAG_POP static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob"); static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value"); +static char e_using_number_as_bool_nr[] + = N_("E1023: Using a Number as a Bool: %d"); /// Dummy va_list for passing to vim_snprintf /// @@ -132,13 +169,13 @@ char *get_function_name(expand_T *xp, int idx) intidx = -1; } if (intidx < 0) { - char_u *name = (char_u *)get_user_func_name(xp, idx); + char *name = get_user_func_name(xp, idx); if (name != NULL) { if (*name != NUL && *name != '<' - && STRNCMP("g:", xp->xp_pattern, 2) == 0) { - return cat_prefix_varname('g', (char *)name); + && strncmp("g:", xp->xp_pattern, 2) == 0) { + return cat_prefix_varname('g', name); } - return (char *)name; + return name; } } @@ -155,7 +192,7 @@ char *get_function_name(expand_T *xp, int idx) } else { IObuff[key_len + 1] = NUL; } - return (char *)IObuff; + return IObuff; } /// Function given to ExpandGeneric() to obtain the list of internal or @@ -168,9 +205,9 @@ char *get_expr_name(expand_T *xp, int idx) intidx = -1; } if (intidx < 0) { - char_u *name = (char_u *)get_function_name(xp, idx); + char *name = get_function_name(xp, idx); if (name != NULL) { - return (char *)name; + return name; } } return get_user_var_name(xp, ++intidx); @@ -189,37 +226,37 @@ const EvalFuncDef *find_internal_func(const char *const name) return index >= 0 ? &functions[index] : NULL; } -int call_internal_func(const char_u *const fname, const int argcount, typval_T *const argvars, +int call_internal_func(const char *const fname, const int argcount, typval_T *const argvars, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL { - const EvalFuncDef *const fdef = find_internal_func((const char *)fname); + const EvalFuncDef *const fdef = find_internal_func(fname); if (fdef == NULL) { - return ERROR_UNKNOWN; + return FCERR_UNKNOWN; } else if (argcount < fdef->min_argc) { - return ERROR_TOOFEW; + return FCERR_TOOFEW; } else if (argcount > fdef->max_argc) { - return ERROR_TOOMANY; + return FCERR_TOOMANY; } argvars[argcount].v_type = VAR_UNKNOWN; fdef->func(argvars, rettv, fdef->data); - return ERROR_NONE; + return FCERR_NONE; } /// Invoke a method for base->method(). -int call_internal_method(const char_u *const fname, const int argcount, typval_T *const argvars, +int call_internal_method(const char *const fname, const int argcount, typval_T *const argvars, typval_T *const rettv, typval_T *const basetv) FUNC_ATTR_NONNULL_ALL { - const EvalFuncDef *const fdef = find_internal_func((const char *)fname); + const EvalFuncDef *const fdef = find_internal_func(fname); if (fdef == NULL) { - return ERROR_UNKNOWN; + return FCERR_UNKNOWN; } else if (fdef->base_arg == BASE_NONE) { - return ERROR_NOTMETHOD; + return FCERR_NOTMETHOD; } else if (argcount + 1 < fdef->min_argc) { - return ERROR_TOOFEW; + return FCERR_TOOFEW; } else if (argcount + 1 > fdef->max_argc) { - return ERROR_TOOMANY; + return FCERR_TOOMANY; } typval_T argv[MAX_FUNC_ARGS + 1]; @@ -231,10 +268,10 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T argv[argcount + 1].v_type = VAR_UNKNOWN; fdef->func(argv, rettv, fdef->data); - return ERROR_NONE; + return FCERR_NONE; } -/// @return TRUE for a non-zero Number and a non-empty String. +/// @return true for a non-zero Number and a non-empty String. static int non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER @@ -251,26 +288,25 @@ static int non_zero_arg(typval_T *argvars) /// Some versions of glibc on i386 have an optimization that makes it harder to /// call math functions indirectly from inside an inlined function, causing /// compile-time errors. Avoid `inline` in that case. #3072 -static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void float_op_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { float_T f; - float_T (*function)(float_T) = (float_T (*)(float_T)) fptr; rettv->v_type = VAR_FLOAT; if (tv_get_float_chk(argvars, &f)) { - rettv->vval.v_float = function(f); + rettv->vval.v_float = fptr.float_func(f); } else { rettv->vval.v_float = 0.0; } } -static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure()) { return; } - ApiDispatchWrapper fn = (ApiDispatchWrapper)fptr; + MsgpackRpcRequestHandler handler = *fptr.api_handler; Array args = ARRAY_DICT_INIT; @@ -279,7 +315,8 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) } Error err = ERROR_INIT; - Object result = fn(VIML_INTERNAL_CALL, args, &err); + Arena res_arena = ARENA_EMPTY; + Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err); if (ERROR_SET(&err)) { semsg_multiline((const char *)e_api_error, err.msg); @@ -292,15 +329,19 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) end: api_free_array(args); - api_free_object(result); + if (handler.arena_return) { + arena_mem_free(arena_finish(&res_arena)); + } else { + api_free_object(result); + } api_clear_error(&err); } /// "abs(expr)" function -static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_abs(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type == VAR_FLOAT) { - float_op_wrapper(argvars, rettv, (FunPtr)&fabs); + float_op_wrapper(argvars, rettv, (EvalFuncData){ .float_func = &fabs }); } else { bool error = false; @@ -316,25 +357,25 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "add(list, item)" function -static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = 1; // Default: failed. if (argvars[0].v_type == VAR_LIST) { list_T *const l = argvars[0].vval.v_list; - if (!var_check_lock(tv_list_locked(l), N_("add() argument"), - TV_TRANSLATE)) { + if (!value_check_lock(tv_list_locked(l), N_("add() argument"), + TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } } else if (argvars[0].v_type == VAR_BLOB) { blob_T *const b = argvars[0].vval.v_blob; if (b != NULL - && !var_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) { + && !value_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) { bool error = false; const varnumber_T n = tv_get_number_chk(&argvars[1], &error); if (!error) { - ga_append(&b->bv_ga, (char)n); + ga_append(&b->bv_ga, (uint8_t)n); tv_copy(&argvars[0], rettv); } } @@ -344,42 +385,21 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "and(expr, expr)" function -static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) & tv_get_number_chk(&argvars[1], NULL); } /// "api_info()" function -static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { Dictionary metadata = api_metadata(); (void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL); - api_free_dictionary(metadata); -} - -/// "append(lnum, string/list)" function -static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const linenr_T lnum = tv_get_lnum(&argvars[0]); - - set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv); -} - -/// "appendbufline(buf, lnum, string/list)" function -static void f_appendbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_T *const buf = tv_get_buf(&argvars[0], false); - if (buf == NULL) { - rettv->vval.v_number = 1; // FAIL - } else { - const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); - set_buffer_lines(buf, lnum, true, &argvars[2], rettv); - } } /// "atan2()" function -static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_atan2(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { float_T fx; float_T fy; @@ -393,176 +413,16 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "browse(save, title, initdir, default)" function -static void f_browse(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_browse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; } /// "browsedir(title, initdir)" function -static void f_browsedir(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - f_browse(argvars, rettv, NULL); -} - -/// Find a buffer by number or exact name. -static buf_T *find_buffer(typval_T *avar) -{ - buf_T *buf = NULL; - - if (avar->v_type == VAR_NUMBER) { - buf = buflist_findnr((int)avar->vval.v_number); - } else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) { - buf = buflist_findname_exp(avar->vval.v_string); - if (buf == NULL) { - // No full path name match, try a match with a URL or a "nofile" - // buffer, these don't use the full path. - FOR_ALL_BUFFERS(bp) { - if (bp->b_fname != NULL - && (path_with_url(bp->b_fname) || bt_nofilename(bp)) - && STRCMP(bp->b_fname, avar->vval.v_string) == 0) { - buf = bp; - break; - } - } - } - } - return buf; -} - -/// "bufadd(expr)" function -static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - char_u *name = (char_u *)tv_get_string(&argvars[0]); - - rettv->vval.v_number = buflist_add(*name == NUL ? NULL : (char *)name, 0); -} - -/// "bufexists(expr)" function -static void f_bufexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL); -} - -/// "buflisted(expr)" function -static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_T *buf; - - buf = find_buffer(&argvars[0]); - rettv->vval.v_number = (buf != NULL && buf->b_p_bl); -} - -/// "bufload(expr)" function -static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr) -{ - buf_T *buf = get_buf_arg(&argvars[0]); - - if (buf != NULL && buf->b_ml.ml_mfp == NULL) { - aco_save_T aco; - - aucmd_prepbuf(&aco, buf); - swap_exists_action = SEA_NONE; - open_buffer(false, NULL, 0); - aucmd_restbuf(&aco); - } -} - -/// "bufloaded(expr)" function -static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_browsedir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - buf_T *buf; - - buf = find_buffer(&argvars[0]); - rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); -} - -/// "bufname(expr)" function -static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const buf_T *buf; - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (argvars[0].v_type == VAR_UNKNOWN) { - buf = curbuf; - } else { - buf = tv_get_buf_from_arg(&argvars[0]); - } - if (buf != NULL && buf->b_fname != NULL) { - rettv->vval.v_string = xstrdup(buf->b_fname); - } -} - -/// "bufnr(expr)" function -static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const buf_T *buf; - bool error = false; - - rettv->vval.v_number = -1; - - if (argvars[0].v_type == VAR_UNKNOWN) { - buf = curbuf; - } else { - // Don't use tv_get_buf_from_arg(); we continue if the buffer wasn't found - // and the second argument isn't zero, but we want to return early if the - // first argument isn't a string or number so only one error is shown. - if (!tv_check_str_or_nr(&argvars[0])) { - return; - } - emsg_off++; - buf = tv_get_buf(&argvars[0], false); - emsg_off--; - } - - // If the buffer isn't found and the second argument is not zero create a - // new buffer. - const char *name; - if (buf == NULL - && argvars[1].v_type != VAR_UNKNOWN - && tv_get_number_chk(&argvars[1], &error) != 0 - && !error - && (name = tv_get_string_chk(&argvars[0])) != NULL) { - buf = buflist_new((char *)name, NULL, 1, 0); - } - - if (buf != NULL) { - rettv->vval.v_number = buf->b_fnum; - } -} - -static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr) -{ - const buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); - if (buf == NULL) { // no need to search if invalid arg or buffer not found - rettv->vval.v_number = -1; - return; - } - - int winnr = 0; - int winid; - bool found_buf = false; - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - winnr++; - if (wp->w_buffer == buf) { - found_buf = true; - winid = wp->handle; - break; - } - } - rettv->vval.v_number = (found_buf ? (get_nr ? winnr : winid) : -1); -} - -/// "bufwinid(nr)" function -static void f_bufwinid(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_win_common(argvars, rettv, false); -} - -/// "bufwinnr(nr)" function -static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_win_common(argvars, rettv, true); + f_browse(argvars, rettv, fptr); } /// Get buffer by number or pattern. @@ -575,7 +435,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only) return NULL; } - char_u *name = (char_u *)tv->vval.v_string; + char *name = tv->vval.v_string; if (name == NULL || *name == NUL) { return curbuf; @@ -588,9 +448,9 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only) int save_magic = p_magic; p_magic = true; char *save_cpo = p_cpo; - p_cpo = ""; + p_cpo = empty_option; - buf_T *buf = buflist_findnr(buflist_findpat((char *)name, (char *)name + STRLEN(name), + buf_T *buf = buflist_findnr(buflist_findpat(name, name + strlen(name), true, false, curtab_only)); p_magic = save_magic; @@ -630,7 +490,7 @@ buf_T *get_buf_arg(typval_T *arg) } /// "byte2line(byte)" function -static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { long boff = tv_get_number(&argvars[0]) - 1; if (boff < 0) { @@ -665,19 +525,19 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) } /// "byteidx()" function -static void f_byteidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { byteidx(argvars, rettv, false); } /// "byteidxcomp()" function -static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { byteidx(argvars, rettv, true); } /// "call(func, arglist [, dict])" function -static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[1].v_type != VAR_LIST) { emsg(_(e_listreq)); @@ -688,19 +548,19 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) } bool owned = false; - char_u *func; + char *func; partial_T *partial = NULL; if (argvars[0].v_type == VAR_FUNC) { - func = (char_u *)argvars[0].vval.v_string; + func = argvars[0].vval.v_string; } else if (argvars[0].v_type == VAR_PARTIAL) { partial = argvars[0].vval.v_partial; - func = (char_u *)partial_name(partial); + func = partial_name(partial); } else if (nlua_is_table_from_lua(&argvars[0])) { // TODO(tjdevries): UnifiedCallback func = nlua_register_table_as_callable(&argvars[0]); owned = true; } else { - func = (char_u *)tv_get_string(&argvars[0]); + func = (char *)tv_get_string(&argvars[0]); } if (*func == NUL) { @@ -726,13 +586,13 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "changenr()" function -static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_changenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = curbuf->b_u_seq_cur; } /// "chanclose(id[, stream])" function -static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_chanclose(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -771,7 +631,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "chansend(id, data)" function -static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -788,6 +648,14 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) ptrdiff_t input_len = 0; char *input = NULL; + uint64_t id = (uint64_t)argvars[0].vval.v_number; +#ifdef UNIX + bool crlf = false; +#else + Channel *chan = find_channel(id); + bool crlf = (chan != NULL && chan->term) ? true: false; +#endif + if (argvars[1].v_type == VAR_BLOB) { const blob_T *const b = argvars[1].vval.v_blob; input_len = tv_blob_len(b); @@ -795,7 +663,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) input = xmemdup(b->bv_ga.ga_data, (size_t)input_len); } } else { - input = save_tv_as_string(&argvars[1], &input_len, false); + input = save_tv_as_string(&argvars[1], &input_len, false, crlf); } if (!input) { @@ -803,7 +671,6 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) // or there is no input to send. return; } - uint64_t id = (uint64_t)argvars[0].vval.v_number; const char *error = NULL; rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error); if (error) { @@ -812,7 +679,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "char2nr(string)" function -static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_char2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[1].v_type != VAR_UNKNOWN) { if (!tv_check_num(&argvars[1])) { @@ -829,15 +696,38 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// otherwise the byte index of the column. static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) { + if (tv_check_for_string_or_list_arg(argvars, 0) == FAIL + || tv_check_for_opt_number_arg(argvars, 1) == FAIL) { + return; + } + + switchwin_T switchwin; + bool winchanged = false; + + if (argvars[1].v_type != VAR_UNKNOWN) { + // use the window specified in the second argument + tabpage_T *tp; + win_T *wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp); + if (wp == NULL || tp == NULL) { + return; + } + + if (switch_win_noblock(&switchwin, wp, tp, true) != OK) { + return; + } + + check_cursor(); + winchanged = true; + } + colnr_T col = 0; int fnum = curbuf->b_fnum; - pos_T *fp = var2fpos(&argvars[0], false, &fnum, charcol); if (fp != NULL && fnum == curbuf->b_fnum) { if (fp->col == MAXCOL) { // '> can be MAXCOL, get the length of the line then if (fp->lnum <= curbuf->b_ml.ml_line_count) { - col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1; + col = (colnr_T)strlen(ml_get(fp->lnum)) + 1; } else { col = MAXCOL; } @@ -846,11 +736,12 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) // col(".") when the cursor is on the NUL at the end of the line // because of "coladd" can be seen as an extra column. if (virtual_active() && fp == &curwin->w_cursor) { - char_u *p = get_cursor_pos_ptr(); + char *p = get_cursor_pos_ptr(); if (curwin->w_cursor.coladd >= - (colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - curwin->w_cursor.coladd)) { + (colnr_T)win_chartabsize(curwin, p, + curwin->w_virtcol - curwin->w_cursor.coladd)) { int l; - if (*p != NUL && p[(l = utfc_ptr2len((char *)p))] == NUL) { + if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) { col += l; } } @@ -858,23 +749,28 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) } } rettv->vval.v_number = col; + + if (winchanged) { + restore_win_noblock(&switchwin, true); + } } /// "charcol()" function -static void f_charcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { get_col(argvars, rettv, true); } /// "charidx()" function -static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_NUMBER || (argvars[2].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_NUMBER)) { + && argvars[2].v_type != VAR_NUMBER + && argvars[2].v_type != VAR_BOOL)) { emsg(_(e_invarg)); return; } @@ -889,7 +785,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) countcc = (int)tv_get_number(&argvars[2]); } if (countcc < 0 || countcc > 1) { - emsg(_(e_invarg)); + semsg(_(e_using_number_as_bool_nr), countcc); return; } @@ -913,7 +809,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "chdir(dir)" function -static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -925,12 +821,12 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // Return the current directory - char_u *cwd = xmalloc(MAXPATHL); + char *cwd = xmalloc(MAXPATHL); if (os_dirname(cwd, MAXPATHL) != FAIL) { #ifdef BACKSLASH_IN_FILENAME slash_adjust(cwd); #endif - rettv->vval.v_string = (char *)vim_strsave(cwd); + rettv->vval.v_string = xstrdup(cwd); } xfree(cwd); @@ -948,7 +844,7 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "cindent(lnum)" function -static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { pos_T pos = curwin->w_cursor; linenr_T lnum = tv_get_lnum(argvars); @@ -963,26 +859,26 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) win_T *get_optional_window(typval_T *argvars, int idx) { - win_T *win = curwin; + if (argvars[idx].v_type == VAR_UNKNOWN) { + return curwin; + } - if (argvars[idx].v_type != VAR_UNKNOWN) { - win = find_win_by_nr_or_id(&argvars[idx]); - if (win == NULL) { - emsg(_(e_invalwindow)); - return NULL; - } + win_T *win = find_win_by_nr_or_id(&argvars[idx]); + if (win == NULL) { + emsg(_(e_invalwindow)); + return NULL; } return win; } /// "col(string)" function -static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { get_col(argvars, rettv, false); } /// "confirm(message, buttons[, default [, type]])" function -static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char buf[NUMBUFLEN]; char buf2[NUMBUFLEN]; @@ -1029,19 +925,19 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!error) { - rettv->vval.v_number = do_dialog(type, NULL, (char_u *)message, (char_u *)buttons, def, NULL, + rettv->vval.v_number = do_dialog(type, NULL, (char *)message, (char *)buttons, def, NULL, false); } } /// "copy()" function -static void f_copy(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_copy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { var_item_copy(NULL, &argvars[0], rettv, false, 0); } /// "count()" function -static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { long n = 0; int ic = 0; @@ -1052,15 +948,15 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type == VAR_STRING) { - const char_u *expr = (char_u *)tv_get_string_chk(&argvars[1]); - const char_u *p = (char_u *)argvars[0].vval.v_string; + const char *expr = tv_get_string_chk(&argvars[1]); + const char *p = argvars[0].vval.v_string; if (!error && expr != NULL && *expr != NUL && p != NULL) { if (ic) { - const size_t len = STRLEN(expr); + const size_t len = strlen(expr); while (*p != NUL) { - if (mb_strnicmp(p, expr, len) == 0) { + if (mb_strnicmp((char *)p, (char *)expr, len) == 0) { n++; p += len; } else { @@ -1068,10 +964,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else { - char_u *next; - while ((next = (char_u *)strstr((char *)p, (char *)expr)) != NULL) { + char *next; + while ((next = strstr((char *)p, (char *)expr)) != NULL) { n++; - p = next + STRLEN(expr); + p = next + strlen(expr); } } } @@ -1127,31 +1023,8 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = n; } -/// "cscope_connection([{num} , {dbpath} [, {prepend}]])" function -/// -/// Checks the existence of a cscope connection. -static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int num = 0; - const char *dbpath = NULL; - const char *prepend = NULL; - char buf[NUMBUFLEN]; - - if (argvars[0].v_type != VAR_UNKNOWN - && argvars[1].v_type != VAR_UNKNOWN) { - num = (int)tv_get_number(&argvars[0]); - dbpath = tv_get_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) { - prepend = tv_get_string_buf(&argvars[2], buf); - } - } - - rettv->vval.v_number = cs_connection(num, (char_u *)dbpath, - (char_u *)prepend); -} - /// "ctxget([{index}])" function -static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { size_t index = 0; if (argvars[0].v_type == VAR_NUMBER) { @@ -1175,7 +1048,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "ctxpop()" function -static void f_ctxpop(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_ctxpop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (!ctx_restore(NULL, kCtxAll)) { emsg(_("Context stack is empty")); @@ -1183,7 +1056,7 @@ static void f_ctxpop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "ctxpush([{types}])" function -static void f_ctxpush(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_ctxpush(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int types = kCtxAll; if (argvars[0].v_type == VAR_LIST) { @@ -1214,7 +1087,7 @@ static void f_ctxpush(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "ctxset({context}[, {index}])" function -static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_DICT) { semsg(_(e_invarg2), "expected dictionary as first argument"); @@ -1254,7 +1127,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "ctxsize()" function -static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = (varnumber_T)ctx_size(); @@ -1265,7 +1138,7 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// Otherwise use the column number as a byte offset. static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) { - long line, col; + long lnum, col; long coladd = 0; bool set_curswant = true; @@ -1279,7 +1152,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) return; } - line = pos.lnum; + lnum = pos.lnum; col = pos.col; coladd = pos.coladd; if (curswant >= 0) { @@ -1288,10 +1161,15 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) } } else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING) && (argvars[1].v_type == VAR_NUMBER || argvars[1].v_type == VAR_STRING)) { - line = tv_get_lnum(argvars); + lnum = tv_get_lnum(argvars); + if (lnum < 0) { + semsg(_(e_invarg2), tv_get_string(&argvars[0])); + } else if (lnum == 0) { + lnum = curwin->w_cursor.lnum; + } col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) { - col = buf_charidx_to_byteidx(curbuf, (linenr_T)line, (int)col) + 1; + col = buf_charidx_to_byteidx(curbuf, (linenr_T)lnum, (int)col) + 1; } if (argvars[2].v_type != VAR_UNKNOWN) { coladd = (long)tv_get_number_chk(&argvars[2], NULL); @@ -1300,11 +1178,11 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) emsg(_(e_invarg)); return; } - if (line < 0 || col < 0 || coladd < 0) { + if (lnum < 0 || col < 0 || coladd < 0) { return; // type error; errmsg already given } - if (line > 0) { - curwin->w_cursor.lnum = (linenr_T)line; + if (lnum > 0) { + curwin->w_cursor.lnum = (linenr_T)lnum; } if (col > 0) { curwin->w_cursor.col = (colnr_T)col - 1; @@ -1326,43 +1204,45 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) /// Moves the cursor to the specified line and column. /// /// @return 0 when the position could be set, -1 otherwise. -static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_cursor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { set_cursorpos(argvars, rettv, false); } /// "debugbreak()" function -static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_debugbreak(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = FAIL; int pid = (int)tv_get_number(&argvars[0]); if (pid == 0) { emsg(_(e_invarg)); - } else { -#ifdef WIN32 - HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + return; + } - if (hProcess != NULL) { - DebugBreakProcess(hProcess); - CloseHandle(hProcess); - rettv->vval.v_number = OK; - } +#ifdef MSWIN + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + if (hProcess == NULL) { + return; + } + + DebugBreakProcess(hProcess); + CloseHandle(hProcess); + rettv->vval.v_number = OK; #else - uv_kill(pid, SIGINT); + uv_kill(pid, SIGINT); #endif - } } /// "deepcopy()" function -static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int noref = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - noref = (int)tv_get_number_chk(&argvars[1], NULL); + noref = (int)tv_get_bool_chk(&argvars[1], NULL); } if (noref < 0 || noref > 1) { - emsg(_(e_invarg)); + semsg(_(e_using_number_as_bool_nr), noref); } else { var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() @@ -1371,7 +1251,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "delete()" function -static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_delete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; if (check_secure()) { @@ -1407,7 +1287,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// dictwatcheradd(dict, key, funcref) function -static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure()) { return; @@ -1445,7 +1325,7 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// dictwatcherdel(dict, key, funcref) function -static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure()) { return; @@ -1479,96 +1359,20 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) callback_free(&callback); } -/// "deletebufline()" function -static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_T *const buf = tv_get_buf(&argvars[0], false); - if (buf == NULL) { - rettv->vval.v_number = 1; // FAIL - return; - } - const bool is_curbuf = buf == curbuf; - const bool save_VIsual_active = VIsual_active; - - linenr_T last; - const linenr_T first = tv_get_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) { - last = tv_get_lnum_buf(&argvars[2], buf); - } else { - last = first; - } - - if (buf->b_ml.ml_mfp == NULL || first < 1 - || first > buf->b_ml.ml_line_count || last < first) { - rettv->vval.v_number = 1; // FAIL - return; - } - - buf_T *curbuf_save = NULL; - win_T *curwin_save = NULL; - if (!is_curbuf) { - VIsual_active = false; - curbuf_save = curbuf; - curwin_save = curwin; - curbuf = buf; - find_win_for_curbuf(); - } - if (last > curbuf->b_ml.ml_line_count) { - last = curbuf->b_ml.ml_line_count; - } - const long count = last - first + 1; - - // When coming here from Insert mode, sync undo, so that this can be - // undone separately from what was previously inserted. - if (u_sync_once == 2) { - u_sync_once = 1; // notify that u_sync() was called - u_sync(true); - } - - if (u_save(first - 1, last + 1) == FAIL) { - rettv->vval.v_number = 1; // FAIL - } else { - for (linenr_T lnum = first; lnum <= last; lnum++) { - ml_delete(first, true); - } - - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (wp->w_buffer == buf) { - if (wp->w_cursor.lnum > last) { - wp->w_cursor.lnum -= (linenr_T)count; - } else if (wp->w_cursor.lnum > first) { - wp->w_cursor.lnum = first; - } - if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) { - wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; - } - } - } - check_cursor_col(); - deleted_lines_mark(first, count); - } - - if (!is_curbuf) { - curbuf = curbuf_save; - curwin = curwin_save; - VIsual_active = save_VIsual_active; - } -} - /// "did_filetype()" function -static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = did_filetype; } /// "diff_filler()" function -static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_diff_filler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars))); } /// "diff_hlID()" function -static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; @@ -1585,9 +1389,10 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) || changedtick != buf_get_changedtick(curbuf) || fnum != curbuf->b_fnum) { // New line, buffer, change: need to get the values. - int filler_lines = diff_check(curwin, lnum); - if (filler_lines < 0) { - if (filler_lines == -1) { + int linestatus = 0; + int filler_lines = diff_check_with_linestatus(curwin, lnum, &linestatus); + if (filler_lines < 0 || linestatus < 0) { + if (filler_lines == -1 || linestatus == -1) { change_start = MAXCOL; change_end = -1; if (diff_find_change(curwin, lnum, &change_start, &change_end)) { @@ -1614,11 +1419,11 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlID = HLF_CHD; // Changed line. } } - rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)(hlID + 1); + rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (hlID + 1); } /// "empty({expr})" function -static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_empty(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { bool n = true; @@ -1668,7 +1473,7 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "environ()" function -static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_environ(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_alloc_ret(rettv); @@ -1690,7 +1495,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) char c = env[i][len]; env[i][len] = NUL; -#ifdef WIN32 +#ifdef MSWIN // Upper-case all the keys for Windows so we can detect duplicates char *const key = strcase_save(str, true); #else @@ -1713,32 +1518,31 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "escape({string}, {chars})" function -static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_escape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char buf[NUMBUFLEN]; - rettv->vval.v_string = (char *)vim_strsave_escaped((const char_u *)tv_get_string(&argvars[0]), - (const char_u *)tv_get_string_buf(&argvars[1], - buf)); + rettv->vval.v_string = vim_strsave_escaped(tv_get_string(&argvars[0]), + tv_get_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } /// "getenv()" function -static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - char_u *p = (char_u *)vim_getenv(tv_get_string(&argvars[0])); + char *p = vim_getenv(tv_get_string(&argvars[0])); if (p == NULL) { rettv->v_type = VAR_SPECIAL; rettv->vval.v_special = kSpecialVarNull; return; } - rettv->vval.v_string = (char *)p; + rettv->vval.v_string = p; rettv->v_type = VAR_STRING; } /// "eval()" function -static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *s = tv_get_string_chk(&argvars[0]); if (s != NULL) { @@ -1759,15 +1563,15 @@ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "eventhandler()" function -static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_eventhandler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = vgetc_busy; } /// "executable()" function -static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_executable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - if (tv_check_for_string(&argvars[0]) == FAIL) { + if (tv_check_for_string_arg(argvars, 0) == FAIL) { return; } @@ -1794,7 +1598,7 @@ static char *get_list_line(int c, void *cookie, int indent, bool do_concat) return s == NULL ? NULL : xstrdup(s); } -static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int arg_off) +void execute_common(typval_T *argvars, typval_T *rettv, int arg_off) { const int save_msg_silent = msg_silent; const int save_emsg_silent = emsg_silent; @@ -1873,30 +1677,15 @@ static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int } /// "execute(command)" function -static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - execute_common(argvars, rettv, fptr, 0); -} - -/// "win_execute(win_id, command)" function -static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - // Return an empty string if something fails. - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - int id = (int)tv_get_number(argvars); - tabpage_T *tp; - win_T *wp = win_id2wp_tp(id, &tp); - if (wp != NULL && tp != NULL) { - WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, fptr, 1)); - } + execute_common(argvars, rettv, 0); } /// "exepath()" function -static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - if (tv_check_for_nonempty_string(&argvars[0]) == FAIL) { + if (tv_check_for_nonempty_string_arg(argvars, 0) == FAIL) { return; } @@ -1906,7 +1695,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) #ifdef BACKSLASH_IN_FILENAME if (path != NULL) { - slash_adjust((char_u *)path); + slash_adjust(path); } #endif @@ -1915,7 +1704,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "exists()" function -static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int n = false; @@ -1955,13 +1744,12 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "expand()" function -static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - char *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; bool error = false; #ifdef BACKSLASH_IN_FILENAME - char_u *p_csl_save = p_csl; + char *p_csl_save = p_csl; // avoid using 'completeslash' here p_csl = empty_option; @@ -1977,10 +1765,17 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *s = tv_get_string(&argvars[0]); if (*s == '%' || *s == '#' || *s == '<') { - emsg_off++; + if (p_verbose == 0) { + emsg_off++; + } size_t len; - char_u *result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); - emsg_off--; + char *errormsg = NULL; + char *result = eval_vars((char *)s, s, &len, NULL, &errormsg, NULL, false); + if (p_verbose == 0) { + emsg_off--; + } else if (errormsg != NULL) { + emsg(errormsg); + } if (rettv->v_type == VAR_LIST) { tv_list_alloc_ret(rettv, (result != NULL)); if (result != NULL) { @@ -1988,7 +1783,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) } XFREE_CLEAR(result); } else { - rettv->vval.v_string = (char *)result; + rettv->vval.v_string = result; } } else { // When the optional second argument is non-zero, don't remove matches @@ -2005,10 +1800,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) options += WILD_ICASE; } if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = (char *)ExpandOne(&xpc, (char_u *)s, NULL, options, - WILD_ALL); + rettv->vval.v_string = ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL); } else { - ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL_KEEP); tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, @@ -2026,7 +1820,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "menu_get(path [, modes])" function -static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, kListLenMayKnow); int modes = MENU_ALL_MODES; @@ -2039,9 +1833,15 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "expandcmd()" function /// Expand all the special characters in a command string. -static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char *errormsg = NULL; + bool emsgoff = true; + + if (argvars[1].v_type == VAR_DICT + && tv_dict_get_bool(argvars[1].vval.v_dict, "errmsg", kBoolVarFalse)) { + emsgoff = false; + } rettv->v_type = VAR_STRING; char *cmdstr = xstrdup(tv_get_string(&argvars[0])); @@ -2055,15 +1855,23 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) }; eap.argt |= EX_NOSPC; - emsg_off++; - expand_filename(&eap, &cmdstr, &errormsg); - emsg_off--; + if (emsgoff) { + emsg_off++; + } + if (expand_filename(&eap, &cmdstr, &errormsg) == FAIL) { + if (!emsgoff && errormsg != NULL && *errormsg != NUL) { + emsg(errormsg); + } + } + if (emsgoff) { + emsg_off--; + } rettv->vval.v_string = cmdstr; } /// "flatten(list[, {maxdepth}])" function -static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { bool error = false; @@ -2088,9 +1896,9 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *list = argvars[0].vval.v_list; if (list != NULL - && !var_check_lock(tv_list_locked(list), - N_("flatten() argument"), - TV_TRANSLATE) + && !value_check_lock(tv_list_locked(list), + N_("flatten() argument"), + TV_TRANSLATE) && tv_list_flatten(list, maxdepth) == OK) { tv_copy(&argvars[0], rettv); } @@ -2098,7 +1906,7 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "extend(list, list [, idx])" function /// "extend(dict, dict [, action])" function -static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const arg_errmsg = N_("extend() argument"); @@ -2107,7 +1915,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (!var_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + if (!value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { long before = (long)tv_get_number_chk(&argvars[2], &error); @@ -2136,13 +1944,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *const d1 = argvars[0].vval.v_dict; dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { - const bool locked = var_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); + const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); } else if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (!var_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + } else if (!value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { @@ -2174,7 +1982,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "feedkeys()" function -static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // This is not allowed in the sandbox. If the commands would still be // executed in the sandbox it would be OK, but it probably happens later, @@ -2195,17 +2003,17 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "filereadable()" function -static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_filereadable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const p = tv_get_string(&argvars[0]); rettv->vval.v_number = - (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p)); + (*p && !os_isdir(p) && os_file_is_readable(p)); } /// @return 0 for not writable /// 1 for writable file /// 2 for a dir which we have rights to write into. -static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_filewritable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *filename = tv_get_string(&argvars[0]); rettv->vval.v_number = os_file_is_writable(filename); @@ -2213,8 +2021,8 @@ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fresult = NULL; - char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; + char *fresult = NULL; + char *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; int count = 1; bool first = true; bool error = false; @@ -2231,7 +2039,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) error = true; } else { if (*p != NUL) { - path = (char_u *)p; + path = (char *)p; } if (argvars[2].v_type != VAR_UNKNOWN) { @@ -2249,12 +2057,12 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) { xfree(fresult); } - fresult = find_file_in_path_option(first ? (char_u *)fname : NULL, + fresult = find_file_in_path_option(first ? (char *)fname : NULL, first ? strlen(fname) : 0, 0, first, path, - find_what, (char_u *)curbuf->b_ffname, + find_what, curbuf->b_ffname, (find_what == FINDFILE_DIR - ? (char_u *)"" + ? "" : curbuf->b_p_sua)); first = false; @@ -2265,46 +2073,48 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = (char *)fresult; + rettv->vval.v_string = fresult; } } /// "filter()" function -static void f_filter(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { filter_map(argvars, rettv, false); } /// "finddir({fname}[, {path}[, {count}]])" function -static void f_finddir(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_finddir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { findfilendir(argvars, rettv, FINDFILE_DIR); } /// "findfile({fname}[, {path}[, {count}]])" function -static void f_findfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_findfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { findfilendir(argvars, rettv, FINDFILE_FILE); } /// "float2nr({float})" function -static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { float_T f; - if (tv_get_float_chk(argvars, &f)) { - if (f <= (float_T) - VARNUMBER_MAX + DBL_EPSILON) { - rettv->vval.v_number = -VARNUMBER_MAX; - } else if (f >= (float_T)VARNUMBER_MAX - DBL_EPSILON) { - rettv->vval.v_number = VARNUMBER_MAX; - } else { - rettv->vval.v_number = (varnumber_T)f; - } + if (!tv_get_float_chk(argvars, &f)) { + return; + } + + if (f <= (float_T) - VARNUMBER_MAX + DBL_EPSILON) { + rettv->vval.v_number = -VARNUMBER_MAX; + } else if (f >= (float_T)VARNUMBER_MAX - DBL_EPSILON) { + rettv->vval.v_number = VARNUMBER_MAX; + } else { + rettv->vval.v_number = (varnumber_T)f; } } /// "fmod()" function -static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_fmod(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { float_T fx; float_T fy; @@ -2318,16 +2128,16 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "fnameescape({string})" function -static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_fnameescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_string = vim_strsave_fnameescape(tv_get_string(&argvars[0]), VSE_NONE); rettv->v_type = VAR_STRING; } /// "fnamemodify({fname}, {mods})" function -static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - char_u *fbuf = NULL; + char *fbuf = NULL; size_t len = 0; char buf[NUMBUFLEN]; const char *fname = tv_get_string_chk(&argvars[0]); @@ -2339,7 +2149,7 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*mods != NUL) { size_t usedlen = 0; (void)modify_fname((char *)mods, false, &usedlen, - (char **)&fname, (char **)&fbuf, &len); + (char **)&fname, &fbuf, &len); } } @@ -2353,21 +2163,21 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "foreground()" function -static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) {} -static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_funcref(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - common_function(argvars, rettv, true, fptr); + common_function(argvars, rettv, true); } -static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_function(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - common_function(argvars, rettv, false, fptr); + common_function(argvars, rettv, false); } /// "garbagecollect()" function -static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_garbagecollect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // This is postponed until we are back at the toplevel, because we may be // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". @@ -2379,7 +2189,7 @@ static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "get()" function -static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { typval_T *tv = NULL; bool what_is_dict = false; @@ -2426,7 +2236,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) pt = argvars[0].vval.v_partial; } else { CLEAR_FIELD(fref_pt); - fref_pt.pt_name = (char_u *)argvars[0].vval.v_string; + fref_pt.pt_name = argvars[0].vval.v_string; pt = &fref_pt; } @@ -2434,13 +2244,17 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const what = tv_get_string(&argvars[1]); if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) { + const char *name = partial_name(pt); rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); - const char *const n = (const char *)partial_name(pt); - assert(n != NULL); - rettv->vval.v_string = xstrdup(n); + assert(name != NULL); if (rettv->v_type == VAR_FUNC) { - func_ref((char_u *)rettv->vval.v_string); + func_ref((char *)name); + } + if (*what == 'n' && pt->pt_name == NULL && pt->pt_func != NULL) { + // use <SNR> instead of the byte code + name = printable_func_name(pt->pt_func); } + rettv->vval.v_string = xstrdup(name); } else if (strcmp(what, "dict") == 0) { what_is_dict = true; if (pt->pt_dict != NULL) { @@ -2475,120 +2289,8 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "getbufinfo()" function -static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_T *argbuf = NULL; - bool filtered = false; - bool sel_buflisted = false; - bool sel_bufloaded = false; - bool sel_bufmodified = false; - - tv_list_alloc_ret(rettv, kListLenMayKnow); - - // List of all the buffers or selected buffers - if (argvars[0].v_type == VAR_DICT) { - dict_T *sel_d = argvars[0].vval.v_dict; - - if (sel_d != NULL) { - dictitem_T *di; - - filtered = true; - - di = tv_dict_find(sel_d, S_LEN("buflisted")); - if (di != NULL && tv_get_number(&di->di_tv)) { - sel_buflisted = true; - } - - di = tv_dict_find(sel_d, S_LEN("bufloaded")); - if (di != NULL && tv_get_number(&di->di_tv)) { - sel_bufloaded = true; - } - di = tv_dict_find(sel_d, S_LEN("bufmodified")); - if (di != NULL && tv_get_number(&di->di_tv)) { - sel_bufmodified = true; - } - } - } else if (argvars[0].v_type != VAR_UNKNOWN) { - // Information about one buffer. Argument specifies the buffer - argbuf = tv_get_buf_from_arg(&argvars[0]); - if (argbuf == NULL) { - return; - } - } - - // Return information about all the buffers or a specified buffer - FOR_ALL_BUFFERS(buf) { - if (argbuf != NULL && argbuf != buf) { - continue; - } - if (filtered && ((sel_bufloaded && buf->b_ml.ml_mfp == NULL) - || (sel_buflisted && !buf->b_p_bl) - || (sel_bufmodified && !buf->b_changed))) { - continue; - } - - dict_T *const d = get_buffer_info(buf); - tv_list_append_dict(rettv->vval.v_list, d); - if (argbuf != NULL) { - return; - } - } -} - -/// Get line or list of lines from buffer "buf" into "rettv". -/// -/// @param retlist if TRUE, then the lines are returned as a Vim List. -/// -/// @return range (from start to end) of lines in rettv from the specified -/// buffer. -static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) -{ - rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); - rettv->vval.v_string = NULL; - - if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) { - if (retlist) { - tv_list_alloc_ret(rettv, 0); - } - return; - } - - if (retlist) { - if (start < 1) { - start = 1; - } - if (end > buf->b_ml.ml_line_count) { - end = buf->b_ml.ml_line_count; - } - tv_list_alloc_ret(rettv, end - start + 1); - while (start <= end) { - tv_list_append_string(rettv->vval.v_list, - (const char *)ml_get_buf(buf, start++, false), -1); - } - } else { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = - (char *)((start >= 1 && start <= buf->b_ml.ml_line_count) - ? vim_strsave(ml_get_buf(buf, start, false)) : NULL); - } -} - -/// "getbufline()" function -static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); - - const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); - const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN - ? lnum - : tv_get_lnum_buf(&argvars[2], buf)); - - get_buffer_lines(buf, lnum, end, true, rettv); -} - /// "getchangelist()" function -static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getchangelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, 2); @@ -2693,13 +2395,13 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool } /// "getcharpos()" function -static void f_getcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { getpos_both(argvars, rettv, false, true); } /// "getcharsearch()" function -static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_alloc_ret(rettv); @@ -2710,49 +2412,6 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); } -/// "getcmdcompltype()" function -static void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char *)get_cmdline_completion(); -} - -/// "getcmdline()" function -static void f_getcmdline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char *)get_cmdline_str(); -} - -/// "getcmdpos()" function -static void f_getcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = get_cmdline_pos() + 1; -} - -/// "getcmdscreenpos()" function -static void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = get_cmdline_screen_pos() + 1; -} - -/// "getcmdtype()" function -static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = (char)get_cmdline_type(); -} - -/// "getcmdwintype()" function -static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = (char)cmdwin_type; -} - /// `getcwd([{win}[, {tab}]])` function /// /// Every scope not specified implies the currently selected scope object. @@ -2762,7 +2421,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @pre An argument may not be -1 if preceding arguments are not all -1. /// /// @post The return value will be a string. -static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // Possible scope of working directory to return. CdScope scope = kCdScopeInvalid; @@ -2857,13 +2516,13 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } FALLTHROUGH; // In global directory, just need to get OS CWD. case kCdScopeInvalid: // If called without any arguments, get OS CWD. - if (os_dirname((char_u *)cwd, MAXPATHL) == FAIL) { + if (os_dirname(cwd, MAXPATHL) == FAIL) { from = ""; // Return empty string on failure. } } if (from) { - STRLCPY(cwd, from, MAXPATHL); + xstrlcpy(cwd, from, MAXPATHL); } rettv->vval.v_string = xstrdup(cwd); @@ -2875,17 +2534,17 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "getfontname()" function -static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getfontname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; } /// "getfperm({fname})" function -static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char *perm = NULL; - char_u flags[] = "rwx"; + char flags[] = "rwx"; const char *filename = tv_get_string(&argvars[0]); int32_t file_perm = os_getperm(filename); @@ -2893,7 +2552,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { if (file_perm & (1 << (8 - i))) { - perm[i] = (char)flags[i % 3]; + perm[i] = flags[i % 3]; } } } @@ -2902,7 +2561,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "getfsize({fname})" function -static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getfsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *fname = tv_get_string(&argvars[0]); @@ -2911,7 +2570,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) FileInfo file_info; if (os_fileinfo(fname, &file_info)) { uint64_t filesize = os_fileinfo_size(&file_info); - if (os_isdir((const char_u *)fname)) { + if (os_isdir(fname)) { rettv->vval.v_number = 0; } else { rettv->vval.v_number = (varnumber_T)filesize; @@ -2927,7 +2586,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "getftime({fname})" function -static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *fname = tv_get_string(&argvars[0]); @@ -2940,9 +2599,9 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "getftype({fname})" function -static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getftype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - char_u *type = NULL; + char *type = NULL; char *t; const char *fname = tv_get_string(&argvars[0]); @@ -2968,13 +2627,13 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { t = "other"; } - type = vim_strsave((char_u *)t); + type = xstrdup(t); } - rettv->vval.v_string = (char *)type; + rettv->vval.v_string = type; } /// "getjumplist()" function -static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getjumplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, kListLenMayKnow); win_T *const wp = find_tabwin(&argvars[0], &argvars[1]); @@ -3004,26 +2663,8 @@ static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "getline(lnum, [end])" function -static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - linenr_T end; - bool retlist; - - const linenr_T lnum = tv_get_lnum(argvars); - if (argvars[1].v_type == VAR_UNKNOWN) { - end = lnum; - retlist = false; - } else { - end = tv_get_lnum(&argvars[1]); - retlist = true; - } - - get_buffer_lines(curbuf, lnum, end, retlist, rettv); -} - /// "getmarklist()" function -static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getmarklist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, kListLenMayKnow); @@ -3041,7 +2682,7 @@ static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "getmousepos()" function -static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getmousepos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int row = mouse_row; int col = mouse_col; @@ -3082,24 +2723,24 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "getpid()" function -static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = os_get_pid(); } /// "getcurpos(string)" function -static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getcurpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { getpos_both(argvars, rettv, true, false); } -static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { getpos_both(argvars, rettv, true, true); } /// "getpos(string)" function -static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { getpos_both(argvars, rettv, false, false); } @@ -3109,23 +2750,23 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// Returns zero on error. static int getreg_get_regname(typval_T *argvars) { - const char_u *strregname; + const char *strregname; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = (const char_u *)tv_get_string_chk(&argvars[0]); + strregname = tv_get_string_chk(&argvars[0]); if (strregname == NULL) { // type error; errmsg already given return 0; } } else { // Default to v:register - strregname = (char_u *)get_vim_var_str(VV_REG); + strregname = get_vim_var_str(VV_REG); } - return *strregname == 0 ? '"' : *strregname; + return *strregname == 0 ? '"' : (uint8_t)(*strregname); } /// "getreg()" function -static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int arg2 = false; bool return_list = false; @@ -3161,7 +2802,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "getregtype()" function -static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getregtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // on error return an empty string rettv->v_type = VAR_STRING; @@ -3180,40 +2821,8 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = xstrdup(buf); } -/// "gettabinfo()" function -static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tabpage_T *tparg = NULL; - - tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN - ? 1 - : kListLenMayKnow)); - - if (argvars[0].v_type != VAR_UNKNOWN) { - // Information about one tab page - tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - if (tparg == NULL) { - return; - } - } - - // Get information about a specific tab page or all tab pages - int tpnr = 0; - FOR_ALL_TABS(tp) { - tpnr++; - if (tparg != NULL && tp != tparg) { - continue; - } - dict_T *const d = get_tabpage_info(tp, tpnr); - tv_list_append_dict(rettv->vval.v_list, d); - if (tparg != NULL) { - return; - } - } -} - /// "gettagstack()" function -static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_gettagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { win_T *wp = curwin; // default is current window @@ -3229,41 +2838,6 @@ static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_tagstack(wp, rettv->vval.v_dict); } -/// "getwininfo()" function -static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *wparg = NULL; - - tv_list_alloc_ret(rettv, kListLenMayKnow); - - if (argvars[0].v_type != VAR_UNKNOWN) { - wparg = win_id2wp((int)tv_get_number(&argvars[0])); - if (wparg == NULL) { - return; - } - } - - // Collect information about either all the windows across all the tab - // pages or one particular window. - int16_t tabnr = 0; - FOR_ALL_TABS(tp) { - tabnr++; - int16_t winnr = 0; - FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - winnr++; - if (wparg != NULL && wp != wparg) { - continue; - } - dict_T *const d = get_win_info(wp, tabnr, winnr); - tv_list_append_dict(rettv->vval.v_list, d); - if (wparg != NULL) { - // found information about a specific window - return; - } - } - } -} - /// Dummy timer callback. Used by f_wait(). static void dummy_timer_due_cb(TimeWatcher *tw, void *data) {} @@ -3275,7 +2849,7 @@ static void dummy_timer_close_cb(TimeWatcher *tw, void *data) } /// "wait(timeout, condition[, interval])" function -static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; @@ -3328,113 +2902,8 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) time_watcher_close(tw, dummy_timer_close_cb); } -/// "win_screenpos()" function -static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_list_alloc_ret(rettv, 2); - const win_T *const wp = find_win_by_nr_or_id(&argvars[0]); - tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1); - tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); -} - -/// Move the window wp into a new split of targetwin in a given direction -static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags) -{ - int height = wp->w_height; - win_T *oldwin = curwin; - - if (wp == targetwin || wp == aucmd_win) { - return; - } - - // Jump to the target window - if (curwin != targetwin) { - win_goto(targetwin); - } - - // Remove the old window and frame from the tree of frames - int dir; - (void)winframe_remove(wp, &dir, NULL); - win_remove(wp, NULL); - last_status(false); // may need to remove last status line - (void)win_comp_pos(); // recompute window positions - - // Split a window on the desired side and put the old window there - (void)win_split_ins(size, flags, wp, dir); - - // If splitting horizontally, try to preserve height - if (size == 0 && !(flags & WSP_VERT)) { - win_setheight_win(height, wp); - if (p_ea) { - win_equal(wp, true, 'v'); - } - } - - if (oldwin != curwin) { - win_goto(oldwin); - } -} - -/// "win_splitmove()" function -static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *wp = find_win_by_nr_or_id(&argvars[0]); - win_T *targetwin = find_win_by_nr_or_id(&argvars[1]); - - if (wp == NULL || targetwin == NULL || wp == targetwin - || !win_valid(wp) || !win_valid(targetwin) - || win_valid_floating(wp) || win_valid_floating(targetwin)) { - emsg(_(e_invalwindow)); - rettv->vval.v_number = -1; - return; - } - - int flags = 0, size = 0; - - if (argvars[2].v_type != VAR_UNKNOWN) { - dict_T *d; - dictitem_T *di; - - if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) { - emsg(_(e_invarg)); - return; - } - - d = argvars[2].vval.v_dict; - if (tv_dict_get_number(d, "vertical")) { - flags |= WSP_VERT; - } - if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) { - flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE; - } - size = (int)tv_dict_get_number(d, "size"); - } - - win_move_into_split(wp, targetwin, size, flags); -} - -/// "getwinpos({timeout})" function -static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_list_alloc_ret(rettv, 2); - tv_list_append_number(rettv->vval.v_list, -1); - tv_list_append_number(rettv->vval.v_list, -1); -} - -/// "getwinposx()" function -static void f_getwinposx(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = -1; -} - -/// "getwinposy()" function -static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = -1; -} - /// "glob()" function -static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int options = WILD_SILENT|WILD_USE_NL; expand_T xpc; @@ -3464,11 +2933,11 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) options += WILD_ICASE; } if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = (char *)ExpandOne(&xpc, (char_u *) - tv_get_string(&argvars[0]), NULL, options, - WILD_ALL); + rettv->vval.v_string = ExpandOne(&xpc, (char *) + tv_get_string(&argvars[0]), NULL, options, + WILD_ALL); } else { - ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, + ExpandOne(&xpc, (char *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { @@ -3483,7 +2952,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "globpath()" function -static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath. bool error = false; @@ -3513,8 +2982,8 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const file = tv_get_string_buf_chk(&argvars[1], buf1); if (file != NULL && !error) { garray_T ga; - ga_init(&ga, (int)sizeof(char_u *), 10); - globpath((char *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags); + ga_init(&ga, (int)sizeof(char *), 10); + globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -3533,7 +3002,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "glob2regpat()" function -static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error @@ -3541,8 +3010,21 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (pat == NULL) ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, false); } +/// "gettext()" function +static void f_gettext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + if (argvars[0].v_type != VAR_STRING + || argvars[0].vval.v_string == NULL + || *argvars[0].vval.v_string == NUL) { + semsg(_(e_invarg2), tv_get_string(&argvars[0])); + } else { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string)); + } +} + /// "has()" function -static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { static const char *const has_list[] = { #if defined(BSD) && !defined(__APPLE__) @@ -3557,7 +3039,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) #ifdef UNIX "unix", #endif -#if defined(WIN32) +#ifdef MSWIN "win32", #endif #ifdef _WIN64 @@ -3580,7 +3062,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "cmdwin", "comments", "conceal", - "cscope", "cursorbind", "cursorshape", #ifdef DEBUG @@ -3601,9 +3082,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "fork", #endif "gettext", -#if defined(HAVE_ICONV) "iconv", -#endif "insert_expand", "jumplist", "keymap", @@ -3631,8 +3110,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "packages", "path_extra", "persistent_undo", - "postscript", - "printer", "profile", "pythonx", "reltime", @@ -3649,7 +3126,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "spell", "syntax", #if !defined(UNIX) - "system", // TODO(SplinterOfChaos): This IS defined for UNIX! + "system", #endif "tablineat", "tag_binary", @@ -3765,7 +3242,7 @@ static bool has_wsl(void) /// @pre An argument may not be -1 if preceding arguments are not all -1. /// /// @post The return value will be either the number `1` or `0`. -static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_haslocaldir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // Possible scope of working directory to return. CdScope scope = kCdScopeInvalid; @@ -3855,29 +3332,29 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "highlightID(name)" function -static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0])); } /// "highlight_exists()" function -static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_hlexists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0])); } /// "hostname()" function -static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char hostname[256]; os_get_hostname(hostname, 256); rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char *)vim_strsave((char_u *)hostname); + rettv->vval.v_string = xstrdup(hostname); } /// iconv() function -static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { vimconv_T vimconv; @@ -3886,9 +3363,9 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const str = tv_get_string(&argvars[0]); char buf1[NUMBUFLEN]; - char_u *const from = enc_canonize(enc_skip((char_u *)tv_get_string_buf(&argvars[1], buf1))); + char *const from = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[1], buf1))); char buf2[NUMBUFLEN]; - char_u *const to = enc_canonize(enc_skip((char_u *)tv_get_string_buf(&argvars[2], buf2))); + char *const to = enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); @@ -3896,7 +3373,7 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (vimconv.vc_type == CONV_NONE) { rettv->vval.v_string = xstrdup(str); } else { - rettv->vval.v_string = (char *)string_convert(&vimconv, (char_u *)str, NULL); + rettv->vval.v_string = string_convert(&vimconv, (char *)str, NULL); } convert_setup(&vimconv, NULL, NULL); @@ -3905,7 +3382,7 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "indent()" function -static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { @@ -3916,7 +3393,7 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "index()" function -static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { long idx = 0; bool ic = false; @@ -3956,33 +3433,36 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_listblobreq)); return; } + list_T *const l = argvars[0].vval.v_list; - if (l != NULL) { - listitem_T *item = tv_list_first(l); - if (argvars[2].v_type != VAR_UNKNOWN) { - bool error = false; + if (l == NULL) { + return; + } - // Start at specified item. - idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error)); - if (error || idx == -1) { + listitem_T *item = tv_list_first(l); + if (argvars[2].v_type != VAR_UNKNOWN) { + bool error = false; + + // Start at specified item. + idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error)); + if (error || idx == -1) { + item = NULL; + } else { + item = tv_list_find(l, (int)idx); + assert(item != NULL); + } + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = !!tv_get_number_chk(&argvars[3], &error); + if (error) { item = NULL; - } else { - item = tv_list_find(l, (int)idx); - assert(item != NULL); - } - if (argvars[3].v_type != VAR_UNKNOWN) { - ic = !!tv_get_number_chk(&argvars[3], &error); - if (error) { - item = NULL; - } } } + } - for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { - if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) { - rettv->vval.v_number = idx; - break; - } + for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { + if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) { + rettv->vval.v_number = idx; + break; } } } @@ -3991,19 +3471,19 @@ static bool inputsecret_flag = false; /// "input()" function /// Also handles inputsecret() when inputsecret is set. -static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_input(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { get_user_input(argvars, rettv, false, inputsecret_flag); } /// "inputdialog()" function -static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_inputdialog(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { get_user_input(argvars, rettv, true, inputsecret_flag); } /// "inputlist()" function -static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_LIST) { semsg(_(e_listarg), "inputlist()"); @@ -4034,7 +3514,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static garray_T ga_userinput = { 0, 0, sizeof(tasave_T), 4, NULL }; /// "inputrestore()" function -static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_inputrestore(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (!GA_EMPTY(&ga_userinput)) { ga_userinput.ga_len--; @@ -4048,7 +3528,7 @@ static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "inputsave()" function -static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_inputsave(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // Add an entry to the stack of typeahead storage. tasave_T *p = GA_APPEND_VIA_PTR(tasave_T, &ga_userinput); @@ -4056,17 +3536,17 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "inputsecret()" function -static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_inputsecret(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { cmdline_star++; inputsecret_flag = true; - f_input(argvars, rettv, NULL); + f_input(argvars, rettv, fptr); cmdline_star--; inputsecret_flag = false; } /// "insert()" function -static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { list_T *l; bool error = false; @@ -4075,8 +3555,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) blob_T *const b = argvars[0].vval.v_blob; if (b == NULL - || var_check_lock(b->bv_lock, N_("insert() argument"), - TV_TRANSLATE)) { + || value_check_lock(b->bv_lock, N_("insert() argument"), + TV_TRANSLATE)) { return; } @@ -4103,16 +3583,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ga_grow(&b->bv_ga, 1); - char_u *const p = (char_u *)b->bv_ga.ga_data; + uint8_t *const p = (uint8_t *)b->bv_ga.ga_data; memmove(p + before + 1, p + before, (size_t)(len - before)); - *(p + before) = (char_u)val; + *(p + before) = (uint8_t)val; b->bv_ga.ga_len++; tv_copy(&argvars[0], rettv); } else if (argvars[0].v_type != VAR_LIST) { semsg(_(e_listblobarg), "insert()"); - } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("insert() argument"), TV_TRANSLATE)) { + } else if (!value_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("insert() argument"), TV_TRANSLATE)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -4139,34 +3619,34 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "interrupt()" function static void f_interrupt(typval_T *argvars FUNC_ATTR_UNUSED, typval_T *rettv FUNC_ATTR_UNUSED, - FunPtr fptr FUNC_ATTR_UNUSED) + EvalFuncData fptr FUNC_ATTR_UNUSED) { got_int = true; } /// "invert(expr)" function -static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_invert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); } /// "isdirectory()" function -static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_isdirectory(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0])); + rettv->vval.v_number = os_isdir(tv_get_string(&argvars[0])); } /// "islocked()" function -static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_islocked(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { lval_T lv; rettv->vval.v_number = -1; - const char_u *const end = (char_u *)get_lval((char *)tv_get_string(&argvars[0]), - NULL, - &lv, false, false, - GLV_NO_AUTOLOAD|GLV_READ_ONLY, - FNE_CHECK_START); + const char *const end = get_lval((char *)tv_get_string(&argvars[0]), + NULL, + &lv, false, false, + GLV_NO_AUTOLOAD|GLV_READ_ONLY, + FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { if (*end != NUL) { semsg(_(e_trailing_arg), end); @@ -4199,7 +3679,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "isinf()" function -static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_isinf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type == VAR_FLOAT && xisinf(argvars[0].vval.v_float)) { @@ -4208,14 +3688,14 @@ static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "isnan()" function -static void f_isnan(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_isnan(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT && xisnan(argvars[0].vval.v_float); } /// "id()" function -static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_id(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL { const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars); @@ -4225,7 +3705,7 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "jobpid(id)" function -static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_jobpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -4249,7 +3729,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "jobresize(job, width, height)" function -static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -4281,7 +3761,7 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } static const char *ignored_env_vars[] = { -#ifndef WIN32 +#ifndef MSWIN "COLUMNS", "LINES", "TERMCAP", @@ -4293,7 +3773,7 @@ static const char *ignored_env_vars[] = { /// According to comments in src/win/process.c of libuv, Windows has a few /// "essential" environment variables. static const char *required_env_vars[] = { -#ifdef WIN32 +#ifdef MSWIN "HOMEDRIVE", "HOMEPATH", "LOGONSERVER", @@ -4316,7 +3796,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en if (!clear_env) { typval_T temp_env = TV_INITIAL_VALUE; - f_environ(NULL, &temp_env, NULL); + f_environ(NULL, &temp_env, (EvalFuncData){ .nullptr = NULL }); tv_dict_extend(env, temp_env.vval.v_dict, "force"); tv_dict_free(temp_env.vval.v_dict); @@ -4332,7 +3812,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en tv_dict_item_remove(env, dv); } } -#ifndef WIN32 +#ifndef MSWIN // Set COLORTERM to "truecolor" if termguicolors is set and 256 // otherwise, but only if it was set in the parent terminal at all dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM")); @@ -4367,7 +3847,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en } if (job_env) { -#ifdef WIN32 +#ifdef MSWIN TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, { // Always use upper-case keys for Windows so we detect duplicate keys char *const key = strcase_save((const char *)var->di_key, true); @@ -4405,7 +3885,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en } /// "jobstart()" function -static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -4467,7 +3947,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } -#ifdef WIN32 +#ifdef MSWIN if (pty && overlapped) { semsg(_(e_invarg2), "job cannot have both 'pty' and 'overlapped' options set"); @@ -4480,7 +3960,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((const char_u *)cwd)) { + if (!os_isdir(cwd)) { semsg(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -4525,7 +4005,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "jobstop()" function -static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -4558,7 +4038,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "jobwait(ids[, timeout])" function -static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -4657,7 +4137,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// json_decode() function -static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_json_decode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char numbuf[NUMBUFLEN]; const char *s = NULL; @@ -4691,14 +4171,28 @@ static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// json_encode() function -static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_json_encode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = encode_tv2json(&argvars[0], NULL); } +/// "keytrans()" function +static void f_keytrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + if (tv_check_for_string_arg(argvars, 0) == FAIL + || argvars[0].vval.v_string == NULL) { + return; + } + // Need to escape K_SPECIAL for mb_unescape(). + char *escaped = vim_strsave_escape_ks(argvars[0].vval.v_string); + rettv->vval.v_string = str2special_save(escaped, true, true); + xfree(escaped); +} + /// "last_buffer_nr()" function. -static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int n = 0; @@ -4712,7 +4206,7 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "len()" function -static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { switch (argvars[0].v_type) { case VAR_STRING: @@ -4783,19 +4277,19 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) } /// "libcall()" function -static void f_libcall(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_libcall(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { libcall_common(argvars, rettv, VAR_STRING); } /// "libcallnr()" function -static void f_libcallnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_libcallnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { libcall_common(argvars, rettv, VAR_NUMBER); } /// "line(string, [winid])" function -static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { linenr_T lnum = 0; pos_T *fp = NULL; @@ -4826,7 +4320,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "line2byte(lnum)" function -static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_line2byte(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const linenr_T lnum = tv_get_lnum(argvars); if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { @@ -4840,7 +4334,7 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "lispindent(lnum)" function -static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_lispindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const pos_T pos = curwin->w_cursor; const linenr_T lnum = tv_get_lnum(argvars); @@ -4854,13 +4348,13 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "localtime()" function -static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_localtime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = (varnumber_T)time(NULL); } /// luaeval() function implementation -static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL { const char *const str = tv_get_string_chk(&argvars[0]); @@ -4872,7 +4366,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "map()" function -static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { filter_map(argvars, rettv, true); } @@ -4880,9 +4374,9 @@ static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void find_some_match(typval_T *const argvars, typval_T *const rettv, const SomeMatchType type) { - char_u *str = NULL; + char *str = NULL; long len = 0; - char_u *expr = NULL; + char *expr = NULL; regmatch_T regmatch; long start = 0; long nth = 1; @@ -4890,11 +4384,11 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, bool match = false; list_T *l = NULL; long idx = 0; - char_u *tofree = NULL; + char *tofree = NULL; // Make 'cpoptions' empty, the 'l' flag should not be used here. char *save_cpo = p_cpo; - p_cpo = ""; + p_cpo = empty_option; rettv->vval.v_number = -1; switch (type) { @@ -4927,8 +4421,8 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, } li = tv_list_first(l); } else { - expr = str = (char_u *)tv_get_string(&argvars[0]); - len = (long)STRLEN(str); + expr = str = (char *)tv_get_string(&argvars[0]); + len = (long)strlen(str); } char patbuf[NUMBUFLEN]; @@ -4987,8 +4481,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, break; } xfree(tofree); - tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li), - NULL); + tofree = expr = str = encode_tv2echo(TV_LIST_ITEM_TV(li), NULL); if (str == NULL) { break; } @@ -5009,7 +4502,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, idx++; } else { startcol = (colnr_T)(regmatch.startp[0] - + utfc_ptr2len((char *)regmatch.startp[0]) - str); + + utfc_ptr2len(regmatch.startp[0]) - str); if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) { match = false; break; @@ -5028,7 +4521,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); - TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz((const char *)regmatch.startp[0], rd); + TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz(regmatch.startp[0], rd); TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)(regmatch.startp[0] - expr); TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(regmatch.endp[0] - expr); if (l != NULL) { @@ -5064,11 +4557,9 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, rettv->vval.v_number = idx; } else { if (type == kSomeMatch) { - rettv->vval.v_number = - (varnumber_T)(regmatch.startp[0] - str); + rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); } else { - rettv->vval.v_number = - (varnumber_T)(regmatch.endp[0] - str); + rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str); } rettv->vval.v_number += (varnumber_T)(str - expr); } @@ -5090,31 +4581,31 @@ theend: } /// "match()" function -static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { find_some_match(argvars, rettv, kSomeMatch); } /// "matchend()" function -static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_matchend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { find_some_match(argvars, rettv, kSomeMatchEnd); } /// "matchlist()" function -static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_matchlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { find_some_match(argvars, rettv, kSomeMatchList); } /// "matchstr()" function -static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { find_some_match(argvars, rettv, kSomeMatchStr); } /// "matchstrpos()" function -static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { find_some_match(argvars, rettv, kSomeMatchStrPos); } @@ -5142,7 +4633,7 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool TV_LIST_ITER_CONST(tv->vval.v_list, li, { const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); if (error) { - return; + return; // type error; errmsg already given } if (domax ? i > n : i < n) { n = i; @@ -5155,7 +4646,7 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool TV_DICT_ITER(tv->vval.v_dict, di, { const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); if (error) { - return; + return; // type error; errmsg already given } if (domax ? i > n : i < n) { n = i; @@ -5165,23 +4656,24 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool semsg(_(e_listdictarg), domax ? "max()" : "min()"); return; } + rettv->vval.v_number = n; } /// "max()" function -static void f_max(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_max(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { max_min(argvars, rettv, true); } /// "min()" function -static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_min(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { max_min(argvars, rettv, false); } /// "mkdir()" function -static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int prot = 0755; // -V536 @@ -5216,17 +4708,16 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(failed_dir); rettv->vval.v_number = FAIL; return; - } else { - rettv->vval.v_number = OK; - return; } + rettv->vval.v_number = OK; + return; } } rettv->vval.v_number = vim_mkdir_emsg(dir, prot); } /// "mode()" function -static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_mode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char buf[MODE_MAX_LENGTH]; @@ -5243,7 +4734,7 @@ static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "msgpackdump()" function -static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL { if (argvars[0].v_type != VAR_LIST) { @@ -5382,7 +4873,7 @@ static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret } /// "msgpackparse" function -static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_msgpackparse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL { if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { @@ -5398,7 +4889,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "nextnonblank()" function -static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_nextnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { linenr_T lnum; @@ -5407,7 +4898,7 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) lnum = 0; break; } - if (*skipwhite((char *)ml_get(lnum)) != NUL) { + if (*skipwhite(ml_get(lnum)) != NUL) { break; } } @@ -5415,7 +4906,7 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "nr2char()" function -static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_nr2char(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[1].v_type != VAR_UNKNOWN) { if (!tv_check_num(&argvars[1])) { @@ -5446,14 +4937,14 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "or(expr, expr)" function -static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_or(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) | tv_get_number_chk(&argvars[1], NULL); } /// "pathshorten()" function -static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_pathshorten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int trim_len = 1; @@ -5465,17 +4956,17 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; - const char_u *p = (char_u *)tv_get_string_chk(&argvars[0]); + const char *p = tv_get_string_chk(&argvars[0]); if (p == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = (char *)vim_strsave(p); - shorten_dir_len((char_u *)rettv->vval.v_string, trim_len); + rettv->vval.v_string = xstrdup(p); + shorten_dir_len(rettv->vval.v_string, trim_len); } } /// "pow()" function -static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_pow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { float_T fx; float_T fy; @@ -5489,13 +4980,13 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "prevnonblank()" function -static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_prevnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { linenr_T lnum = tv_get_lnum(argvars); if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; } else { - while (lnum >= 1 && *skipwhite((char *)ml_get(lnum)) == NUL) { + while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) { lnum--; } } @@ -5503,7 +4994,7 @@ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "printf()" function -static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -5525,7 +5016,7 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "prompt_setcallback({buffer}, {callback})" function -static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { Callback prompt_callback = { .type = kCallbackNone }; @@ -5548,7 +5039,7 @@ static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr } /// "prompt_setinterrupt({buffer}, {callback})" function -static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { Callback interrupt_callback = { .type = kCallbackNone }; @@ -5571,7 +5062,7 @@ static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fpt } /// "prompt_getprompt({buffer})" function -static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL { // return an empty string by default, e.g. it's not a prompt buffer @@ -5587,11 +5078,11 @@ static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_string = (char *)vim_strsave(buf_prompt_text(buf)); + rettv->vval.v_string = xstrdup(buf_prompt_text(buf)); } /// "prompt_setprompt({buffer}, {text})" function -static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure()) { return; @@ -5607,14 +5098,14 @@ static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "pum_getpos()" function -static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_pum_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_alloc_ret(rettv); pum_set_event_info(rettv->vval.v_dict); } /// "pumvisible()" function -static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_pumvisible(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (pum_visible()) { rettv->vval.v_number = 1; @@ -5622,7 +5113,7 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "py3eval()" and "pyxeval()" functions (always python3) -static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_py3eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { script_host_eval("python3", argvars, rettv); } @@ -5657,10 +5148,11 @@ static void init_srand(uint32_t *const x) } } if (dev_urandom_state != OK) { - // Reading /dev/urandom doesn't work, fall back to time(). + // Reading /dev/urandom doesn't work, fall back to os_hrtime() XOR with process ID #endif // uncrustify:off - *x = (uint32_t)time(NULL); + *x = (uint32_t)os_hrtime(); + *x ^= (uint32_t)os_get_pid(); #ifndef MSWIN } #endif @@ -5694,7 +5186,7 @@ static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *c } /// "rand()" function -static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_rand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { uint32_t result; @@ -5705,7 +5197,7 @@ static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) // When no argument is given use the global seed list. if (!initialized) { // Initialize the global seed list. - uint32_t x; + uint32_t x = 0; init_srand(&x); gx = splitmix32(&x); @@ -5764,7 +5256,7 @@ theend: } /// "srand()" function -static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_srand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { uint32_t x = 0; @@ -5786,19 +5278,19 @@ static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "perleval()" function -static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_perleval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { script_host_eval("perl", argvars, rettv); } /// "rubyeval()" function -static void f_rubyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_rubyeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { script_host_eval("ruby", argvars, rettv); } /// "range()" function -static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_range(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { varnumber_T end; varnumber_T stride = 1; @@ -5820,13 +5312,16 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (stride == 0) { emsg(_("E726: Stride is zero")); - } else if (stride > 0 ? end + 1 < start : end - 1 > start) { + return; + } + if (stride > 0 ? end + 1 < start : end - 1 > start) { emsg(_("E727: Start past end")); - } else { - tv_list_alloc_ret(rettv, (end - start) / stride); - for (varnumber_T i = start; stride > 0 ? i <= end : i >= end; i += stride) { - tv_list_append_number(rettv->vval.v_list, i); - } + return; + } + + tv_list_alloc_ret(rettv, (end - start) / stride); + for (varnumber_T i = start; stride > 0 ? i <= end : i >= end; i += stride) { + tv_list_append_number(rettv->vval.v_list, i); } } @@ -5868,7 +5363,7 @@ theend: } /// "readdir()" function -static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_readdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, kListLenUnknown); @@ -5885,17 +5380,17 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_clear_strings(&ga); } -/// "readfile()" function -static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "readfile()" or "readblob()" function +static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_blob) { bool binary = false; - bool blob = false; + bool blob = always_blob; FILE *fd; - char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1 + char buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1 int io_size = sizeof(buf); - char_u *prev = NULL; // previously read bytes, if any - long prevlen = 0; // length of data in prev - long prevsize = 0; // size of prev buffer + char *prev = NULL; // previously read bytes, if any + ptrdiff_t prevlen = 0; // length of data in prev + ptrdiff_t prevsize = 0; // size of prev buffer long maxline = MAXLNUM; if (argvars[1].v_type != VAR_UNKNOWN) { @@ -5913,7 +5408,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); - if (os_isdir((const char_u *)fname)) { + if (os_isdir(fname)) { semsg(_(e_isadir2), fname); return; } @@ -5944,13 +5439,13 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) // - an incomplete line gets written // - a "binary" file gets an empty line at the end if it ends in a // newline. - char_u *p; // Position in buf. - char_u *start; // Start of current line. + char *p; // Position in buf. + char *start; // Start of current line. for (p = buf, start = buf; p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); p++) { - if (*p == '\n' || readlen <= 0) { - char_u *s = NULL; + if (readlen <= 0 || *p == '\n') { + char *s = NULL; size_t len = (size_t)(p - start); // Finished a line. Remove CRs before NL. @@ -5967,7 +5462,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (prevlen == 0) { assert(len < INT_MAX); - s = vim_strnsave(start, len); + s = xstrnsave(start, len); } else { // Change "prev" buffer to be the right size. This way // the bytes are only copied once, and very long lines are @@ -5982,7 +5477,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_owned_tv(l, (typval_T) { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, - .vval.v_string = (char *)s, + .vval.v_string = s, }); start = p + 1; // Step over newline. @@ -6002,18 +5497,18 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) *p = '\n'; // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this // when finding the BF and check the previous two bytes. - } else if (*p == 0xbf && !binary) { + } else if ((uint8_t)(*p) == 0xbf && !binary) { // Find the two bytes before the 0xbf. If p is at buf, or buf + 1, // these may be in the "prev" string. - char_u back1 = p >= buf + 1 ? p[-1] + char back1 = p >= buf + 1 ? p[-1] : prevlen >= 1 ? prev[prevlen - 1] : NUL; - char_u back2 = p >= buf + 2 ? p[-2] + char back2 = p >= buf + 2 ? p[-2] : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] : prevlen >= - 2 ? prev[prevlen - 2] : NUL; + 2 ? prev[prevlen - 2] : NUL; - if (back2 == 0xef && back1 == 0xbb) { - char_u *dest = p - 2; + if ((uint8_t)back2 == 0xef && (uint8_t)back1 == 0xbb) { + char *dest = p - 2; // Usually a BOM is at the beginning of a file, and so at // the beginning of a line; then we can just step over it. @@ -6024,8 +5519,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) int adjust_prevlen = 0; if (dest < buf) { // -V782 - adjust_prevlen = (int)(buf - dest); // -V782 // adjust_prevlen must be 1 or 2. + adjust_prevlen = (int)(buf - dest); // -V782 dest = buf; } if (readlen > p - buf + 1) { @@ -6050,17 +5545,17 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) // small, to avoid repeatedly 'allocing' large and // 'reallocing' small. if (prevsize == 0) { - prevsize = (long)(p - start); + prevsize = p - start; } else { - long grow50pc = (prevsize * 3) / 2; - long growmin = (long)((p - start) * 2 + prevlen); + ptrdiff_t grow50pc = (prevsize * 3) / 2; + ptrdiff_t growmin = (p - start) * 2 + prevlen; prevsize = grow50pc > growmin ? grow50pc : growmin; } prev = xrealloc(prev, (size_t)prevsize); } // Add the line part to end of "prev". memmove(prev + prevlen, start, (size_t)(p - start)); - prevlen += (long)(p - start); + prevlen += p - start; } } // while @@ -6068,8 +5563,20 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) fclose(fd); } +/// "readblob()" function +static void f_readblob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + read_file_or_blob(argvars, rettv, true); +} + +/// "readfile()" function +static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + read_file_or_blob(argvars, rettv, false); +} + /// "getreginfo()" function -static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int regname = getreg_get_regname(argvars); if (regname == 0) { @@ -6119,18 +5626,18 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "reg_executing()" function -static void f_reg_executing(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reg_executing(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { return_register(reg_executing, rettv); } /// "reg_recording()" function -static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reg_recording(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { return_register(reg_recording, rettv); } -static void f_reg_recorded(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reg_recorded(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { return_register(reg_recorded, rettv); } @@ -6172,7 +5679,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL /// one argument it returns the time passed since the argument. /// With two arguments it returns the time passed between /// the two arguments. -static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reltime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { proftime_T res; proftime_T start; @@ -6214,7 +5721,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "reltimestr()" function -static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reltimestr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL { proftime_T tm; @@ -6227,7 +5734,7 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "remove()" function -static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_remove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const arg_errmsg = N_("remove() argument"); @@ -6243,19 +5750,19 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "rename({from}, {to})" function -static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_rename(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure()) { rettv->vval.v_number = -1; } else { char buf[NUMBUFLEN]; - rettv->vval.v_number = vim_rename((const char_u *)tv_get_string(&argvars[0]), - (const char_u *)tv_get_string_buf(&argvars[1], buf)); + rettv->vval.v_number = vim_rename(tv_get_string(&argvars[0]), + tv_get_string_buf(&argvars[1], buf)); } } /// "repeat()" function -static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { @@ -6292,18 +5799,18 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "resolve()" function -static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; const char *fname = tv_get_string(&argvars[0]); -#ifdef WIN32 +#ifdef MSWIN char *v = os_resolve_shortcut(fname); if (v == NULL) { if (os_is_reparse_point_include(fname)) { v = os_realpath(fname, v); } } - rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v); + rettv->vval.v_string = (v == NULL ? xstrdup(fname) : v); #else # ifdef HAVE_READLINK { @@ -6365,7 +5872,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*q != NUL) { cpy = remain; remain = (remain - ? (char *)concat_str((char_u *)q - 1, (char_u *)remain) + ? concat_str(q - 1, remain) : xstrdup(q - 1)); xfree(cpy); q[-1] = NUL; @@ -6377,7 +5884,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q[-1] = NUL; q = path_tail(p); } - if (q > p && !path_is_absolute((const char_u *)buf)) { + if (q > p && !path_is_absolute(buf)) { // Symlink is relative to directory of argument. Replace the // symlink with the resolved name in the same directory. const size_t p_len = strlen(p); @@ -6424,7 +5931,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) && (p[2] == NUL || vim_ispathsep(p[2])))))) { // Prepend "./". - cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p); + cpy = concat_str("./", p); xfree(p); p = cpy; } else if (!is_relative_to_current) { @@ -6457,18 +5964,18 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) # endif #endif - simplify_filename((char_u *)rettv->vval.v_string); + simplify_filename(rettv->vval.v_string); } /// "reverse({list})" function -static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type == VAR_BLOB) { blob_T *const b = argvars[0].vval.v_blob; const int len = tv_blob_len(b); for (int i = 0; i < len / 2; i++) { - const char_u tmp = tv_blob_get(b, i); + const uint8_t tmp = tv_blob_get(b, i); tv_blob_set(b, i, tv_blob_get(b, len - i - 1)); tv_blob_set(b, len - i - 1, tmp); } @@ -6477,8 +5984,8 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) semsg(_(e_listblobarg), "reverse()"); } else { list_T *const l = argvars[0].vval.v_list; - if (!var_check_lock(tv_list_locked(l), N_("reverse() argument"), - TV_TRANSLATE)) { + if (!value_check_lock(tv_list_locked(l), N_("reverse() argument"), + TV_TRANSLATE)) { tv_list_reverse(l); tv_list_set_ret(rettv, l); } @@ -6486,7 +5993,7 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "reduce(list, { accumulator, element -> value } [, initial])" function -static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { emsg(_(e_listblobreq)); @@ -6508,8 +6015,8 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) } funcexe_T funcexe = FUNCEXE_INIT; - funcexe.evaluate = true; - funcexe.partial = partial; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; typval_T initial; typval_T argv[3]; @@ -6598,55 +6105,57 @@ static int get_search_arg(typval_T *varp, int *flagsp) { int dir = FORWARD; - if (varp->v_type != VAR_UNKNOWN) { - char nbuf[NUMBUFLEN]; - const char *flags = tv_get_string_buf_chk(varp, nbuf); - if (flags == NULL) { - return 0; // Type error; errmsg already given. - } - int mask; - while (*flags != NUL) { - switch (*flags) { - case 'b': - dir = BACKWARD; break; - case 'w': - p_ws = true; break; - case 'W': - p_ws = false; break; - default: - mask = 0; - if (flagsp != NULL) { - switch (*flags) { - case 'c': - mask = SP_START; break; - case 'e': - mask = SP_END; break; - case 'm': - mask = SP_RETCOUNT; break; - case 'n': - mask = SP_NOMOVE; break; - case 'p': - mask = SP_SUBPAT; break; - case 'r': - mask = SP_REPEAT; break; - case 's': - mask = SP_SETPCMARK; break; - case 'z': - mask = SP_COLUMN; break; - } - } - if (mask == 0) { - semsg(_(e_invarg2), flags); - dir = 0; - } else { - *flagsp |= mask; + if (varp->v_type == VAR_UNKNOWN) { + return FORWARD; + } + + char nbuf[NUMBUFLEN]; + const char *flags = tv_get_string_buf_chk(varp, nbuf); + if (flags == NULL) { + return 0; // Type error; errmsg already given. + } + int mask; + while (*flags != NUL) { + switch (*flags) { + case 'b': + dir = BACKWARD; break; + case 'w': + p_ws = true; break; + case 'W': + p_ws = false; break; + default: + mask = 0; + if (flagsp != NULL) { + switch (*flags) { + case 'c': + mask = SP_START; break; + case 'e': + mask = SP_END; break; + case 'm': + mask = SP_RETCOUNT; break; + case 'n': + mask = SP_NOMOVE; break; + case 'p': + mask = SP_SUBPAT; break; + case 'r': + mask = SP_REPEAT; break; + case 's': + mask = SP_SETPCMARK; break; + case 'z': + mask = SP_COLUMN; break; } } - if (dir == 0) { - break; + if (mask == 0) { + semsg(_(e_invarg2), flags); + dir = 0; + } else { + *flagsp |= mask; } - flags++; } + if (dir == 0) { + break; + } + flags++; } return dir; } @@ -6718,7 +6227,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) // Repeat until {skip} returns false. for (;;) { subpatnum - = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia); + = searchit(curwin, curbuf, &pos, NULL, dir, (char *)pat, 1, options, RE_SEARCH, &sia); // finding the first match again means there is no match where {skip} // evaluates to zero. if (firstpos.lnum != 0 && equalpos(pos, firstpos)) { @@ -6729,7 +6238,9 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) // didn't find it or no skip argument break; } - firstpos = pos; + if (firstpos.lnum == 0) { + firstpos = pos; + } // If the skip expression matches, ignore this match. { @@ -6786,7 +6297,7 @@ theend: } /// "rpcnotify()" function -static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -6811,17 +6322,20 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) ADD(args, vim_to_object(tv)); } - if (!rpc_send_event((uint64_t)argvars[0].vval.v_number, - tv_get_string(&argvars[1]), args)) { + bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number, + tv_get_string(&argvars[1]), args); + + api_free_array(args); + + if (!ok) { semsg(_(e_invarg2), "Channel doesn't exist"); return; } - rettv->vval.v_number = 1; } /// "rpcrequest()" function -static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -6910,12 +6424,12 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) } end: - arena_mem_free(res_mem, NULL); + arena_mem_free(res_mem); api_clear_error(&err); } /// "rpcstart()" function (DEPRECATED) -static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -6955,7 +6469,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Allocate extra memory for the argument vector and the NULL pointer int argvl = argsl + 2; - char **argv = xmalloc(sizeof(char_u *) * (size_t)argvl); + char **argv = xmalloc(sizeof(char *) * (size_t)argvl); // Copy program name argv[0] = xstrdup(argvars[0].vval.v_string); @@ -6982,7 +6496,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "rpcstop()" function -static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -7000,7 +6514,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) // if called with a job, stop it, else closes the channel uint64_t id = (uint64_t)argvars[0].vval.v_number; if (find_job(id, false)) { - f_jobstop(argvars, rettv, NULL); + f_jobstop(argvars, rettv, fptr); } else { const char *error; rettv->vval.v_number = @@ -7012,7 +6526,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "screenattr()" function -static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_screenattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; @@ -7030,7 +6544,7 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "screenchar()" function -static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; @@ -7048,7 +6562,7 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "screenchars()" function -static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; @@ -7061,7 +6575,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } int pcc[MAX_MCO]; - int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + (size_t)col], pcc); + int c = utfc_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col], pcc); int composing_len = 0; while (pcc[composing_len] != 0) { composing_len++; @@ -7076,45 +6590,19 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "screencol()" function /// /// First column is 1 to be consistent with virtcol(). -static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_screencol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = ui_current_col() + 1; } -/// "screenpos({winid}, {lnum}, {col})" function -static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_dict_alloc_ret(rettv); - dict_T *dict = rettv->vval.v_dict; - - win_T *wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL) { - return; - } - - pos_T pos = { - .lnum = (linenr_T)tv_get_number(&argvars[1]), - .col = (colnr_T)tv_get_number(&argvars[2]) - 1, - .coladd = 0 - }; - int row = 0; - int scol = 0, ccol = 0, ecol = 0; - textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false); - - tv_dict_add_nr(dict, S_LEN("row"), row); - tv_dict_add_nr(dict, S_LEN("col"), scol); - tv_dict_add_nr(dict, S_LEN("curscol"), ccol); - tv_dict_add_nr(dict, S_LEN("endcol"), ecol); -} - /// "screenrow()" function -static void f_screenrow(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_screenrow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = ui_current_row() + 1; } /// "screenstring()" function -static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; @@ -7129,11 +6617,11 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + (size_t)col]); + rettv->vval.v_string = xstrdup((char *)grid->chars[grid->line_offset[row] + (size_t)col]); } /// "search()" function -static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_search(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int flags = 0; @@ -7141,7 +6629,7 @@ static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "searchdecl()" function -static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_searchdecl(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int locally = 1; int thisblock = 0; @@ -7157,7 +6645,7 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } if (!error && name != NULL) { - rettv->vval.v_number = find_decl((char_u *)name, strlen(name), locally, + rettv->vval.v_number = find_decl((char *)name, strlen(name), locally, thisblock, SEARCH_KEEP) == FAIL; } } @@ -7236,13 +6724,13 @@ theend: } /// "searchpair()" function -static void f_searchpair(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_searchpair(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = searchpair_cmn(argvars, NULL); } /// "searchpairpos()" function -static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_searchpairpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { pos_T match_pos; int lnum = 0; @@ -7284,7 +6772,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir // Make 'cpoptions' empty, the 'l' flag should not be used here. char *save_cpo = p_cpo; - p_cpo = (char *)empty_option; + p_cpo = empty_option; // Set the time limit, if there is one. proftime_T tm = profile_setlimit(time_limit); @@ -7292,14 +6780,14 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir // Make two search patterns: start/end (pat2, for in nested pairs) and // start/middle/end (pat3, for the top pair). const size_t pat2_len = strlen(spat) + strlen(epat) + 17; - char_u *pat2 = xmalloc(pat2_len); + char *pat2 = xmalloc(pat2_len); const size_t pat3_len = strlen(spat) + strlen(mpat) + strlen(epat) + 25; - char_u *pat3 = xmalloc(pat3_len); - snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + char *pat3 = xmalloc(pat3_len); + snprintf(pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); if (*mpat == NUL) { STRCPY(pat3, pat2); } else { - snprintf((char *)pat3, pat3_len, + snprintf(pat3, pat3_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat); } if (flags & SP_START) { @@ -7316,7 +6804,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir clearpos(&firstpos); pos_T foundpos; clearpos(&foundpos); - char_u *pat = pat3; + char *pat = pat3; for (;;) { searchit_arg_T sia = { .sa_stop_lnum = lnum_stop, @@ -7410,18 +6898,23 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir xfree(pat2); xfree(pat3); - if ((char_u *)p_cpo == empty_option) { + if (p_cpo == empty_option) { p_cpo = save_cpo; } else { // Darn, evaluating the {skip} expression changed the value. - free_string_option((char_u *)save_cpo); + // If it's still empty it was changed and restored, need to restore in + // the complicated way. + if (*p_cpo == NUL) { + set_option_value_give_err("cpo", 0L, save_cpo, 0); + } + free_string_option(save_cpo); } return retval; } /// "searchpos()" function -static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_searchpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { pos_T match_pos; int flags = 0; @@ -7441,7 +6934,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "serverlist()" function -static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_serverlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { size_t n; char **addrs = server_address_list(&n); @@ -7455,7 +6948,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "serverstart()" function -static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_serverstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; // Address of the new server @@ -7470,9 +6963,8 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_STRING) { emsg(_(e_invarg)); return; - } else { - address = xstrdup(tv_get_string(argvars)); } + address = xstrdup(tv_get_string(argvars)); } else { address = server_address_new(NULL); } @@ -7500,7 +6992,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "serverstop()" function -static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure()) { return; @@ -7519,23 +7011,8 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setbufline()" function -static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - linenr_T lnum; - buf_T *buf; - - buf = tv_get_buf(&argvars[0], false); - if (buf == NULL) { - rettv->vval.v_number = 1; // FAIL - } else { - lnum = tv_get_lnum_buf(&argvars[1], buf); - set_buffer_lines(buf, lnum, false, &argvars[2], rettv); - } -} - /// Set the cursor or mark position. -/// If 'charpos' is TRUE, then use the column number as a character offset. +/// If 'charpos' is true, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) { @@ -7543,41 +7020,45 @@ static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) rettv->vval.v_number = -1; const char *const name = tv_get_string_chk(argvars); - if (name != NULL) { - pos_T pos; - int fnum; - if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK) { - if (pos.col != MAXCOL && --pos.col < 0) { - pos.col = 0; - } - if (name[0] == '.' && name[1] == NUL) { - // set cursor; "fnum" is ignored - curwin->w_cursor = pos; - if (curswant >= 0) { - curwin->w_curswant = curswant - 1; - curwin->w_set_curswant = false; - } - check_cursor(); - rettv->vval.v_number = 0; - } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { - // set mark - if (setmark_pos((uint8_t)name[1], &pos, fnum, NULL) == OK) { - rettv->vval.v_number = 0; - } - } else { - emsg(_(e_invarg)); - } + if (name == NULL) { + return; + } + + pos_T pos; + int fnum; + if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) != OK) { + return; + } + + if (pos.col != MAXCOL && --pos.col < 0) { + pos.col = 0; + } + if (name[0] == '.' && name[1] == NUL) { + // set cursor; "fnum" is ignored + curwin->w_cursor = pos; + if (curswant >= 0) { + curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = false; + } + check_cursor(); + rettv->vval.v_number = 0; + } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { + // set mark + if (setmark_pos((uint8_t)name[1], &pos, fnum, NULL) == OK) { + rettv->vval.v_number = 0; } + } else { + emsg(_(e_invarg)); } } /// "setcharpos()" function -static void f_setcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_setcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { set_position(argvars, rettv, true); } -static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_DICT) { emsg(_(e_dictreq)); @@ -7585,44 +7066,36 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dict_T *d = argvars[0].vval.v_dict; - if (d != NULL) { - char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false); - if (csearch != NULL) { - int pcc[MAX_MCO]; - const int c = utfc_ptr2char(csearch, pcc); - set_last_csearch(c, csearch, utfc_ptr2len((char *)csearch)); - } - - dictitem_T *di = tv_dict_find(d, S_LEN("forward")); - if (di != NULL) { - set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD); - } + if (d == NULL) { + return; + } - di = tv_dict_find(d, S_LEN("until")); - if (di != NULL) { - set_csearch_until(!!tv_get_number(&di->di_tv)); - } + char *const csearch = tv_dict_get_string(d, "char", false); + if (csearch != NULL) { + int pcc[MAX_MCO]; + const int c = utfc_ptr2char(csearch, pcc); + set_last_csearch(c, csearch, utfc_ptr2len(csearch)); } -} -/// "setcmdpos()" function -static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const int pos = (int)tv_get_number(&argvars[0]) - 1; + dictitem_T *di = tv_dict_find(d, S_LEN("forward")); + if (di != NULL) { + set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD); + } - if (pos >= 0) { - rettv->vval.v_number = set_cmdline_pos(pos); + di = tv_dict_find(d, S_LEN("until")); + if (di != NULL) { + set_csearch_until(!!tv_get_number(&di->di_tv)); } } /// "setcursorcharpos" function -static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { set_cursorpos(argvars, rettv, true); } /// "setenv()" function -static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_setenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char namebuf[NUMBUFLEN]; char valbuf[NUMBUFLEN]; @@ -7637,7 +7110,7 @@ static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "setfperm({fname}, {mode})" function -static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_setfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = 0; @@ -7667,15 +7140,8 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = os_setperm(fname, mode) == OK; } -/// "setline()" function -static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - linenr_T lnum = tv_get_lnum(&argvars[0]); - set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv); -} - /// "setpos()" function -static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_setpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { set_position(argvars, rettv, false); } @@ -7711,7 +7177,7 @@ static int get_yank_type(char **const pp, MotionType *const yank_type, long *con } /// "setreg()" function -static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_setreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { bool append = false; @@ -7837,7 +7303,7 @@ free_lstval: if (strval == NULL) { return; } - write_reg_contents_ex(regname, (const char_u *)strval, (ssize_t)STRLEN(strval), + write_reg_contents_ex(regname, strval, (ssize_t)strlen(strval), append, yank_type, (colnr_T)block_len); } if (pointreg != 0) { @@ -7852,7 +7318,7 @@ free_lstval: } /// "settagstack()" function -static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { static char *e_invact2 = N_("E962: Invalid action: '%s'"); char action = 'r'; @@ -7903,7 +7369,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// f_sha256 - sha256({string}) function -static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_sha256(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *p = tv_get_string(&argvars[0]); const char *hash = sha256_bytes((const uint8_t *)p, strlen(p), NULL, 0); @@ -7914,18 +7380,17 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "shellescape({string})" function -static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_shellescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const bool do_special = non_zero_arg(&argvars[1]); rettv->vval.v_string = - (char *)vim_strsave_shellescape((const char_u *)tv_get_string(&argvars[0]), do_special, - do_special); + vim_strsave_shellescape(tv_get_string(&argvars[0]), do_special, do_special); rettv->v_type = VAR_STRING; } /// shiftwidth() function -static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_shiftwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = 0; @@ -7941,16 +7406,16 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "simplify()" function -static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_simplify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const p = tv_get_string(&argvars[0]); rettv->vval.v_string = xstrdup(p); - simplify_filename((char_u *)rettv->vval.v_string); // Simplify in place. + simplify_filename(rettv->vval.v_string); // Simplify in place. rettv->v_type = VAR_STRING; } /// "sockconnect()" function -static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) { emsg(_(e_invarg)); @@ -8002,7 +7467,7 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "stdioopen()" function -static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_stdioopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_DICT) { emsg(_(e_invarg)); @@ -8036,7 +7501,7 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "reltimefloat()" function -static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_reltimefloat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL { proftime_T tm; @@ -8049,7 +7514,7 @@ static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "soundfold({word})" function -static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_soundfold(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; const char *const s = tv_get_string(&argvars[0]); @@ -8057,7 +7522,7 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "spellbadword()" function -static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const int wo_spell_save = curwin->w_p_spell; @@ -8079,7 +7544,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Find the start and length of the badly spelled word. len = spell_move_to(curwin, FORWARD, true, true, &attr); if (len != 0) { - word = (char *)get_cursor_pos_ptr(); + word = get_cursor_pos_ptr(); curwin->w_set_curswant = true; } } else if (*curbuf->b_s.b_p_spl != NUL) { @@ -8089,7 +7554,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (str != NULL) { // Check the argument for spelling. while (*str != NUL) { - len = spell_check(curwin, (char_u *)str, &attr, &capcol, false); + len = spell_check(curwin, (char *)str, &attr, &capcol, false); if (attr != HLF_COUNT) { word = str; break; @@ -8113,7 +7578,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "spellsuggest()" function -static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_spellsuggest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { garray_T ga = GA_EMPTY_INIT_VALUE; const int wo_spell_save = curwin->w_p_spell; @@ -8148,7 +7613,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) maxcount = 25; } - spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); + spell_suggest_list(&ga, (char *)str, maxcount, need_capital, false); f_spellsuggest_return: tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len); @@ -8160,7 +7625,7 @@ f_spellsuggest_return: curwin->w_p_spell = wo_spell_save; } -static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { colnr_T col = 0; bool keepempty = false; @@ -8168,7 +7633,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Make 'cpoptions' empty, the 'l' flag should not be used here. char *save_cpo = p_cpo; - p_cpo = ""; + p_cpo = empty_option; const char *str = tv_get_string(&argvars[0]); const char *pat = NULL; @@ -8179,7 +7644,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) typeerr = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); + keepempty = (bool)tv_get_bool_chk(&argvars[2], &typeerr); } } if (pat == NULL || *pat == NUL) { @@ -8204,7 +7669,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*str == NUL) { match = false; // Empty item at the end. } else { - match = vim_regexec_nl(®match, (char_u *)str, col); + match = vim_regexec_nl(®match, (char *)str, col); } const char *end; if (match) { @@ -8222,11 +7687,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } // Advance to just after the match. - if (regmatch.endp[0] > (char_u *)str) { + if (regmatch.endp[0] > str) { col = 0; } else { // Don't get stuck at the same match. - col = utfc_ptr2len((char *)regmatch.endp[0]); + col = utfc_ptr2len(regmatch.endp[0]); } str = (const char *)regmatch.endp[0]; } @@ -8239,7 +7704,7 @@ theend: } /// "stdpath(type)" function -static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_stdpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -8271,7 +7736,7 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "str2float()" function -static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char *p = skipwhite(tv_get_string(&argvars[0])); bool isneg = (*p == '-'); @@ -8287,18 +7752,18 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "str2list()" function -static void f_str2list(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_str2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, kListLenUnknown); - const char_u *p = (const char_u *)tv_get_string(&argvars[0]); + const char *p = tv_get_string(&argvars[0]); - for (; *p != NUL; p += utf_ptr2len((char *)p)) { - tv_list_append_number(rettv->vval.v_list, utf_ptr2char((char *)p)); + for (; *p != NUL; p += utf_ptr2len(p)) { + tv_list_append_number(rettv->vval.v_list, utf_ptr2char(p)); } } /// "str2nr()" function -static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int base = 10; int what = 0; @@ -8309,15 +7774,15 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } - if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) { + if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) { what |= STR2NR_QUOTE; } } - char_u *p = (char_u *)skipwhite(tv_get_string(&argvars[0])); + char *p = skipwhite(tv_get_string(&argvars[0])); bool isneg = (*p == '-'); if (*p == '+' || *p == '-') { - p = (char_u *)skipwhite((char *)p + 1); + p = skipwhite(p + 1); } switch (base) { case 2: @@ -8341,7 +7806,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "strftime({format}[, {time}])" function -static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { time_t seconds; @@ -8361,18 +7826,15 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = xstrdup(_("(Invalid)")); } else { vimconv_T conv; - char_u *enc; conv.vc_type = CONV_NONE; - enc = enc_locale(); + char *enc = enc_locale(); convert_setup(&conv, p_enc, enc); if (conv.vc_type != CONV_NONE) { - p = (char *)string_convert(&conv, (char_u *)p, NULL); + p = string_convert(&conv, p, NULL); } char result_buf[256]; - if (p != NULL) { - (void)strftime(result_buf, sizeof(result_buf), p, curtime_ptr); - } else { + if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) { result_buf[0] = NUL; } @@ -8381,7 +7843,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } convert_setup(&conv, enc, p_enc); if (conv.vc_type != CONV_NONE) { - rettv->vval.v_string = (char *)string_convert(&conv, (char_u *)result_buf, NULL); + rettv->vval.v_string = string_convert(&conv, result_buf, NULL); } else { rettv->vval.v_string = xstrdup(result_buf); } @@ -8393,7 +7855,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "strgetchar()" function -static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; @@ -8407,7 +7869,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - const size_t len = STRLEN(str); + const size_t len = strlen(str); size_t byteidx = 0; while (charidx >= 0 && byteidx < len) { @@ -8421,7 +7883,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "stridx()" function -static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_stridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; @@ -8453,43 +7915,55 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "string()" function -static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_string(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = encode_tv2string(&argvars[0], NULL); } /// "strlen()" function -static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); } -/// "strchars()" function -static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc) { const char *s = tv_get_string(&argvars[0]); - int skipcc = 0; varnumber_T len = 0; - int (*func_mb_ptr2char_adv)(const char_u **pp); + int (*func_mb_ptr2char_adv)(const char **pp); + + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) { + func_mb_ptr2char_adv(&s); + len++; + } + rettv->vval.v_number = len; +} + +/// "strcharlen()" function +static void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + strchar_common(argvars, rettv, true); +} + +/// "strchars()" function +static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + int skipcc = false; if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = (int)tv_get_number_chk(&argvars[1], NULL); + skipcc = (int)tv_get_bool(&argvars[1]); } if (skipcc < 0 || skipcc > 1) { - emsg(_(e_invarg)); + semsg(_(e_using_number_as_bool_nr), skipcc); } else { - func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; - while (*s != NUL) { - func_mb_ptr2char_adv((const char_u **)&s); - len++; - } - rettv->vval.v_number = len; + strchar_common(argvars, rettv, skipcc); } } /// "strdisplaywidth()" function -static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const s = tv_get_string(&argvars[0]); int col = 0; @@ -8498,11 +7972,11 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) col = (int)tv_get_number(&argvars[1]); } - rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); + rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col); } /// "strwidth()" function -static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const s = tv_get_string(&argvars[0]); @@ -8510,10 +7984,10 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "strcharpart()" function -static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const p = tv_get_string(&argvars[0]); - const size_t slen = STRLEN(p); + const size_t slen = strlen(p); int nbyte = 0; bool error = false; @@ -8564,7 +8038,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "strpart()" function -static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { bool error = false; @@ -8610,7 +8084,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "strptime({format}, {timestring})" function -static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strptime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char fmt_buf[NUMBUFLEN]; char str_buf[NUMBUFLEN]; @@ -8624,10 +8098,10 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr) vimconv_T conv = { .vc_type = CONV_NONE, }; - char_u *enc = enc_locale(); + char *enc = enc_locale(); convert_setup(&conv, p_enc, enc); if (conv.vc_type != CONV_NONE) { - fmt = (char *)string_convert(&conv, (char_u *)fmt, NULL); + fmt = string_convert(&conv, fmt, NULL); } if (fmt == NULL || os_strptime(str, fmt, &tmval) == NULL @@ -8642,7 +8116,7 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "strridx()" function -static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char buf[NUMBUFLEN]; const char *const needle = tv_get_string_chk(&argvars[1]); @@ -8653,7 +8127,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; // Type error; errmsg already given. } - const size_t haystack_len = STRLEN(haystack); + const size_t haystack_len = strlen(haystack); ptrdiff_t end_idx; if (argvars[2].v_type != VAR_UNKNOWN) { // Third argument: upper limit for index. @@ -8685,14 +8159,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "strtrans()" function -static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true); } /// "submatch()" function -static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { bool error = false; int no = (int)tv_get_number_chk(&argvars[0], &error); @@ -8715,7 +8189,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (retList == 0) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char *)reg_submatch(no); + rettv->vval.v_string = reg_submatch(no); } else { rettv->v_type = VAR_LIST; rettv->vval.v_list = reg_submatch_list(no); @@ -8723,7 +8197,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "substitute()" function -static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_substitute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char patbuf[NUMBUFLEN]; char subbuf[NUMBUFLEN]; @@ -8752,14 +8226,14 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "swapinfo(swap_filename)" function -static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_alloc_ret(rettv); get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); } /// "swapname(expr)" function -static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_swapname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; buf_T *buf = tv_get_buf(&argvars[0], false); @@ -8768,12 +8242,12 @@ static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr) || buf->b_ml.ml_mfp->mf_fname == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = (char *)vim_strsave(buf->b_ml.ml_mfp->mf_fname); + rettv->vval.v_string = xstrdup(buf->b_ml.ml_mfp->mf_fname); } } /// "synID(lnum, col, trans)" function -static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); @@ -8784,7 +8258,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && (size_t)col < STRLEN(ml_get(lnum))) { + && col >= 0 && (size_t)col < strlen(ml_get(lnum))) { id = syn_get_id(curwin, lnum, col, trans, NULL, false); } @@ -8792,7 +8266,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "synIDattr(id, what [, mode])" function -static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_synIDattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const int id = (int)tv_get_number(&argvars[0]); const char *const what = tv_get_string(&argvars[1]); @@ -8850,7 +8324,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } break; case 'u': - if (STRLEN(what) >= 9) { + if (strlen(what) >= 9) { if (TOLOWER_ASC(what[5]) == 'l') { // underline p = highlight_has_attr(id, HL_UNDERLINE, modec); @@ -8879,7 +8353,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "synIDtrans(id)" function -static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int id = (int)tv_get_number(&argvars[0]); @@ -8893,12 +8367,12 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "synconcealed(lnum, col)" function -static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int syntax_flags = 0; int cchar; int matchid = 0; - char_u str[NUMBUFLEN]; + char str[NUMBUFLEN]; tv_list_set_ret(rettv, NULL); @@ -8909,7 +8383,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) CLEAR_FIELD(str); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { + && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); @@ -8922,7 +8396,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) : curwin->w_p_lcs_chars.conceal; } if (cchar != NUL) { - utf_char2bytes(cchar, (char *)str); + utf_char2bytes(cchar, str); } } } @@ -8930,12 +8404,12 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv, 3); tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen - tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); + tv_list_append_string(rettv->vval.v_list, str, -1); tv_list_append_number(rettv->vval.v_list, matchid); } /// "synstack(lnum, col)" function -static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_set_ret(rettv, NULL); @@ -8946,7 +8420,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && (size_t)col <= STRLEN(ml_get(lnum))) { + && (size_t)col <= strlen(ml_get(lnum))) { tv_list_alloc_ret(rettv, kListLenMayKnow); (void)syn_get_id(curwin, lnum, col, false, NULL, true); @@ -8959,18 +8433,18 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// f_system - the VimL system() function -static void f_system(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_system(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { get_system_output_as_rettv(argvars, rettv, false); } -static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_systemlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { get_system_output_as_rettv(argvars, rettv, true); } /// "tabpagebuflist()" function -static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { win_T *wp = NULL; @@ -8991,114 +8465,15 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "tabpagenr()" function -static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int nr = 1; - - if (argvars[0].v_type != VAR_UNKNOWN) { - const char *const arg = tv_get_string_chk(&argvars[0]); - nr = 0; - if (arg != NULL) { - if (strcmp(arg, "$") == 0) { - nr = tabpage_index(NULL) - 1; - } else if (strcmp(arg, "#") == 0) { - nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0; - } else { - semsg(_(e_invexpr2), arg); - } - } - } else { - nr = tabpage_index(curtab); - } - rettv->vval.v_number = nr; -} - -/// Common code for tabpagewinnr() and winnr(). -static int get_winnr(tabpage_T *tp, typval_T *argvar) -{ - int nr = 1; - - win_T *twin = (tp == curtab) ? curwin : tp->tp_curwin; - if (argvar->v_type != VAR_UNKNOWN) { - bool invalid_arg = false; - const char *const arg = tv_get_string_chk(argvar); - if (arg == NULL) { - nr = 0; // Type error; errmsg already given. - } else if (strcmp(arg, "$") == 0) { - twin = (tp == curtab) ? lastwin : tp->tp_lastwin; - } else if (strcmp(arg, "#") == 0) { - twin = (tp == curtab) ? prevwin : tp->tp_prevwin; - if (twin == NULL) { - nr = 0; - } - } else { - // Extract the window count (if specified). e.g. winnr('3j') - char *endp; - long count = strtol((char *)arg, &endp, 10); - if (count <= 0) { - // if count is not specified, default to 1 - count = 1; - } - if (endp != NULL && *endp != '\0') { - if (strequal(endp, "j")) { - twin = win_vert_neighbor(tp, twin, false, count); - } else if (strequal(endp, "k")) { - twin = win_vert_neighbor(tp, twin, true, count); - } else if (strequal(endp, "h")) { - twin = win_horz_neighbor(tp, twin, true, count); - } else if (strequal(endp, "l")) { - twin = win_horz_neighbor(tp, twin, false, count); - } else { - invalid_arg = true; - } - } else { - invalid_arg = true; - } - } - - if (invalid_arg) { - semsg(_(e_invexpr2), arg); - nr = 0; - } - } - - if (nr > 0) { - for (win_T *wp = (tp == curtab) ? firstwin : tp->tp_firstwin; - wp != twin; wp = wp->w_next) { - if (wp == NULL) { - // didn't find it in this tabpage - nr = 0; - break; - } - nr++; - } - } - return nr; -} - -/// "tabpagewinnr()" function -static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int nr = 1; - tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); - if (tp == NULL) { - nr = 0; - } else { - nr = get_winnr(tp, &argvars[1]); - } - rettv->vval.v_number = nr; -} - /// "tagfiles()" function -static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_tagfiles(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, kListLenUnknown); char *fname = xmalloc(MAXPATHL); bool first = true; tagname_T tn; - while (get_tagfname(&tn, first, (char_u *)fname) == OK) { + while (get_tagfname(&tn, first, fname) == OK) { tv_list_append_string(rettv->vval.v_list, fname, -1); first = false; } @@ -9108,7 +8483,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "taglist()" function -static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const tag_pattern = tv_get_string(&argvars[0]); @@ -9122,18 +8497,18 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) fname = tv_get_string(&argvars[1]); } (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), - (char_u *)tag_pattern, (char_u *)fname); + (char *)tag_pattern, (char *)fname); } /// "tempname()" function -static void f_tempname(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_tempname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char *)vim_tempname(); + rettv->vval.v_string = vim_tempname(); } /// "termopen(cmd[, cwd])" function -static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure()) { return; @@ -9176,7 +8551,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((const char_u *)cwd)) { + if (!os_isdir(cwd)) { semsg(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -9216,9 +8591,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) int pid = chan->stream.pty.process.pid; // "./…" => "/home/foo/…" - vim_FullName(cwd, (char *)NameBuff, sizeof(NameBuff), false); + vim_FullName(cwd, NameBuff, sizeof(NameBuff), false); // "/home/foo/…" => "~/…" - size_t len = home_replace(NULL, (char *)NameBuff, (char *)IObuff, sizeof(IObuff), true); + size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true); // Trim slash. if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) { IObuff[len - 1] = '\0'; @@ -9231,14 +8606,14 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // Terminal URI: "term://$CWD//$PID:$CMD" - snprintf((char *)NameBuff, sizeof(NameBuff), "term://%s//%d:%s", - (char *)IObuff, pid, cmd); + snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s", + IObuff, pid, cmd); // at this point the buffer has no terminal instance associated yet, so unset // the 'swapfile' option to ensure no swap file will be created curbuf->b_p_swf = false; apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - (void)setfname(curbuf, (char *)NameBuff, NULL, true); + (void)setfname(curbuf, NameBuff, NULL, true); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); // Save the job id and pid in b:terminal_job_{id,pid} @@ -9256,7 +8631,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "timer_info([timer])" function -static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_UNKNOWN) { if (argvars[0].v_type != VAR_NUMBER) { @@ -9274,7 +8649,7 @@ static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "timer_pause(timer, paused)" function -static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) +static void f_timer_pause(typval_T *argvars, typval_T *unused, EvalFuncData fptr) { if (argvars[0].v_type != VAR_NUMBER) { emsg(_(e_number_exp)); @@ -9294,7 +8669,7 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) } /// "timer_start(timeout, callback, opts)" function -static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int repeat = 1; @@ -9326,7 +8701,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "timer_stop(timerid)" function -static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_timer_stop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_NUMBER) { emsg(_(e_number_exp)); @@ -9341,27 +8716,27 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer_stop(timer); } -static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr) +static void f_timer_stopall(typval_T *argvars, typval_T *unused, EvalFuncData fptr) { timer_stop_all(); } /// "tolower(string)" function -static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false); } /// "toupper(string)" function -static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_toupper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true); } /// "tr(string, fromstr, tostr)" function -static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char buf[NUMBUFLEN]; char buf2[NUMBUFLEN]; @@ -9389,7 +8764,7 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int fromlen; for (const char *p = fromstr; *p != NUL; p += fromlen) { fromlen = utfc_ptr2len(p); - if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { + if (fromlen == inlen && strncmp(in_str, p, (size_t)inlen) == 0) { int tolen; for (p = tostr; *p != NUL; p += tolen) { tolen = utfc_ptr2len(p); @@ -9440,14 +8815,14 @@ error: } /// "trim({expr})" function -static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { char buf1[NUMBUFLEN]; char buf2[NUMBUFLEN]; - const char_u *head = (const char_u *)tv_get_string_buf_chk(&argvars[0], buf1); - const char_u *mask = NULL; - const char_u *prev; - const char_u *p; + const char *head = tv_get_string_buf_chk(&argvars[0], buf1); + const char *mask = NULL; + const char *prev; + const char *p; int dir = 0; rettv->v_type = VAR_STRING; @@ -9456,8 +8831,13 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING) { + semsg(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + if (argvars[1].v_type == VAR_STRING) { - mask = (const char_u *)tv_get_string_buf_chk(&argvars[1], buf2); + mask = tv_get_string_buf_chk(&argvars[1], buf2); if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; // leading or trailing characters to trim @@ -9495,7 +8875,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - const char_u *tail = head + STRLEN(head); + const char *tail = head + strlen(head); if (dir == 0 || dir == 2) { // Trim trailing characters for (; tail > head; tail = prev) { @@ -9518,11 +8898,11 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - rettv->vval.v_string = (char *)vim_strnsave(head, (size_t)(tail - head)); + rettv->vval.v_string = xstrnsave(head, (size_t)(tail - head)); } /// "type(expr)" function -static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_type(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int n = -1; @@ -9554,7 +8934,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "undofile(name)" function -static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_undofile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; const char *const fname = tv_get_string(&argvars[0]); @@ -9573,7 +8953,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "undotree()" function -static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_alloc_ret(rettv); @@ -9591,7 +8971,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "virtcol(string)" function -static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { colnr_T vcol = 0; int fnum = curbuf->b_fnum; @@ -9603,7 +8983,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (fp->col < 0) { fp->col = 0; } else { - const size_t len = STRLEN(ml_get(fp->lnum)); + const size_t len = strlen(ml_get(fp->lnum)); if (fp->col > (colnr_T)len) { fp->col = (colnr_T)len; } @@ -9616,14 +8996,14 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "visualmode()" function -static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_visualmode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - char_u str[2]; + char str[2]; rettv->v_type = VAR_STRING; - str[0] = (char_u)curbuf->b_visual_mode_eval; + str[0] = (char)curbuf->b_visual_mode_eval; str[1] = NUL; - rettv->vval.v_string = (char *)vim_strsave(str); + rettv->vval.v_string = xstrdup(str); // A non-zero number or non-empty string argument: reset mode. if (non_zero_arg(&argvars[0])) { @@ -9632,287 +9012,29 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "wildmenumode()" function -static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_wildmenumode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (wild_menu_showing || ((State & MODE_CMDLINE) && cmdline_pum_active())) { rettv->vval.v_number = 1; } } -/// "win_findbuf()" function -static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_list_alloc_ret(rettv, kListLenMayKnow); - win_findbuf(argvars, rettv->vval.v_list); -} - -/// "win_getid()" function -static void f_win_getid(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = win_getid(argvars); -} - -/// "win_gettype(nr)" function -static void f_win_gettype(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *wp = curwin; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (argvars[0].v_type != VAR_UNKNOWN) { - wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL) { - rettv->vval.v_string = (char *)vim_strsave((char_u *)"unknown"); - return; - } - } - if (wp == aucmd_win) { - rettv->vval.v_string = xstrdup("autocmd"); - } else if (wp->w_p_pvw) { - rettv->vval.v_string = xstrdup("preview"); - } else if (wp->w_floating) { - rettv->vval.v_string = xstrdup("popup"); - } else if (wp == curwin && cmdwin_type != 0) { - rettv->vval.v_string = xstrdup("command"); - } else if (bt_quickfix(wp->w_buffer)) { - rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix")); - } -} - -/// "win_gotoid()" function -static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = win_gotoid(argvars); -} - -/// "win_id2tabwin()" function -static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_id2tabwin(argvars, rettv); -} - -/// "win_id2win()" function -static void f_win_id2win(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = win_id2win(argvars); -} - -/// "win_move_separator()" function -static void f_win_move_separator(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = false; - - win_T *wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL || wp->w_floating) { - return; - } - - int offset = (int)tv_get_number(&argvars[1]); - win_drag_vsep_line(wp, offset); - rettv->vval.v_number = true; -} - -/// "win_move_statusline()" function -static void f_win_move_statusline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *wp; - int offset; - - rettv->vval.v_number = false; - - wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL || wp->w_floating) { - return; - } - - offset = (int)tv_get_number(&argvars[1]); - win_drag_status_line(wp, offset); - rettv->vval.v_number = true; -} - -/// "winbufnr(nr)" function -static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL) { - rettv->vval.v_number = -1; - } else { - rettv->vval.v_number = wp->w_buffer->b_fnum; - } -} - -/// "wincol()" function -static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - validate_cursor(); - rettv->vval.v_number = curwin->w_wcol + 1; -} - -/// "winheight(nr)" function -static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL) { - rettv->vval.v_number = -1; - } else { - rettv->vval.v_number = wp->w_height; - } -} - -/// "winlayout()" function -static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tabpage_T *tp; - - tv_list_alloc_ret(rettv, 2); - - if (argvars[0].v_type == VAR_UNKNOWN) { - tp = curtab; - } else { - tp = find_tabpage((int)tv_get_number(&argvars[0])); - if (tp == NULL) { - return; - } - } - - get_framelayout(tp->tp_topframe, rettv->vval.v_list, true); -} - -/// "winline()" function -static void f_winline(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - validate_cursor(); - rettv->vval.v_number = curwin->w_wrow + 1; -} - -/// "winnr()" function -static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = get_winnr(curtab, &argvars[0]); -} - -/// "winrestcmd()" function -static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - char_u buf[50]; - - garray_T ga; - ga_init(&ga, (int)sizeof(char), 70); - - // Do this twice to handle some window layouts properly. - for (int i = 0; i < 2; i++) { - int winnr = 1; - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - snprintf((char *)buf, sizeof(buf), "%dresize %d|", winnr, - wp->w_height); - ga_concat(&ga, (char *)buf); - snprintf((char *)buf, sizeof(buf), "vert %dresize %d|", winnr, - wp->w_width); - ga_concat(&ga, (char *)buf); - winnr++; - } - } - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; - rettv->v_type = VAR_STRING; -} - -/// "winrestview()" function -static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_T *dict = argvars[0].vval.v_dict; - - if (argvars[0].v_type != VAR_DICT || dict == NULL) { - emsg(_(e_invarg)); - } else { - dictitem_T *di; - if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { - curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv); - } - if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { - curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv); - } - if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { - curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv); - } - if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { - curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv); - curwin->w_set_curswant = false; - } - if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { - set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv)); - } - if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { - curwin->w_topfill = (int)tv_get_number(&di->di_tv); - } - if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { - curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv); - } - if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { - curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv); - } - - check_cursor(); - win_new_height(curwin, curwin->w_height); - win_new_width(curwin, curwin->w_width); - changed_window_setting(); - - if (curwin->w_topline <= 0) { - curwin->w_topline = 1; - } - if (curwin->w_topline > curbuf->b_ml.ml_line_count) { - curwin->w_topline = curbuf->b_ml.ml_line_count; - } - check_topfill(curwin, true); - } -} - -/// "winsaveview()" function -static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_dict_alloc_ret(rettv); - dict_T *dict = rettv->vval.v_dict; - - tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum); - tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col); - tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd); - update_curswant(); - tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant); - - tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline); - tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill); - tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol); - tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); -} - -/// "winwidth(nr)" function -static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL) { - rettv->vval.v_number = -1; - } else { - rettv->vval.v_number = wp->w_width; - } -} - /// "windowsversion()" function -static void f_windowsversion(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_windowsversion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = xstrdup(windowsVersion); } /// "wordcount()" function -static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_wordcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_alloc_ret(rettv); cursor_pos_info(rettv->vval.v_dict); } /// "writefile()" function -static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; @@ -9935,6 +9057,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; bool do_fsync = !!p_fs; + bool mkdir_p = false; if (argvars[2].v_type != VAR_UNKNOWN) { const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { @@ -9950,6 +9073,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_fsync = true; break; case 'S': do_fsync = false; break; + case 'p': + mkdir_p = true; break; default: // Using %s, p and not %c, *p to preserve multibyte characters semsg(_("E5060: Unknown flag: %s"), p); @@ -9969,6 +9094,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_("E482: Can't open file with an empty name")); } else if ((error = file_open(&fp, fname, ((append ? kFileAppend : kFileTruncate) + | (mkdir_p ? kFileMkDir : kFileCreate) | kFileCreate), 0666)) != 0) { semsg(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error)); @@ -9990,7 +9116,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "xor(expr, expr)" function -static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_xor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) ^ tv_get_number_chk(&argvars[1], NULL); diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 583ee0e75e..1ae031a952 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -1,11 +1,17 @@ #ifndef NVIM_EVAL_FUNCS_H #define NVIM_EVAL_FUNCS_H +#include <stdbool.h> +#include <stdint.h> + +#include "nvim/api/private/dispatch.h" #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/types.h" /// Prototype of C function that implements VimL function -typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); +typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, EvalFuncData data); /// Special flags for base_arg @see EvalFuncDef #define BASE_NONE 0 ///< Not a method (no base argument). @@ -13,13 +19,13 @@ typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); /// Structure holding VimL function definition typedef struct { - char *name; ///< Name of the function. - uint8_t min_argc; ///< Minimal number of arguments. - uint8_t max_argc; ///< Maximal number of arguments. - uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST. - bool fast; ///< Can be run in |api-fast| events - VimLFunc func; ///< Function implementation. - FunPtr data; ///< Userdata for function implementation. + char *name; ///< Name of the function. + uint8_t min_argc; ///< Minimal number of arguments. + uint8_t max_argc; ///< Maximal number of arguments. + uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST. + bool fast; ///< Can be run in |api-fast| events + VimLFunc func; ///< Function implementation. + EvalFuncData data; ///< Userdata for function implementation. } EvalFuncDef; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c index 633e6abacf..6a54c4ddc1 100644 --- a/src/nvim/eval/gc.c +++ b/src/nvim/eval/gc.c @@ -1,11 +1,12 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include <stddef.h> + #include "nvim/eval/gc.h" -#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "eval/gc.c.generated.h" +# include "eval/gc.c.generated.h" // IWYU pragma: export #endif /// Head of list of all dictionaries diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h index c2e862e469..3185750c3b 100644 --- a/src/nvim/eval/gc.h +++ b/src/nvim/eval/gc.h @@ -2,6 +2,7 @@ #define NVIM_EVAL_GC_H #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" extern dict_T *gc_first_dict; extern list_T *gc_first_list; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8822bb0491..05b4737206 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -28,9 +28,9 @@ #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/os/fileio.h" #include "nvim/os/input.h" #include "nvim/pos.h" #include "nvim/types.h" @@ -40,6 +40,15 @@ # include "eval/typval.c.generated.h" #endif +static char e_string_required_for_argument_nr[] + = N_("E1174: String required for argument %d"); +static char e_non_empty_string_required_for_argument_nr[] + = N_("E1175: Non-empty string required for argument %d"); +static char e_number_required_for_argument_nr[] + = N_("E1210: Number required for argument %d"); +static char e_string_or_list_required_for_argument_nr[] + = N_("E1222: String or List required for argument %d"); + bool tv_in_free_unref_items = false; // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead @@ -317,10 +326,12 @@ void tv_list_free_list(list_T *const l) void tv_list_free(list_T *const l) FUNC_ATTR_NONNULL_ALL { - if (!tv_in_free_unref_items) { - tv_list_free_contents(l); - tv_list_free_list(l); + if (tv_in_free_unref_items) { + return; } + + tv_list_free_contents(l); + tv_list_free_list(l); } /// Unreference a list @@ -831,7 +842,7 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) } /// "join()" function -void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_join(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_LIST) { emsg(_(e_listreq)); @@ -855,7 +866,7 @@ void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "list2str()" function -void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { garray_T ga; @@ -889,8 +900,8 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) list_T *l; bool error = false; - if (var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - arg_errmsg, TV_TRANSLATE)) { + if (value_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { return; } @@ -1016,7 +1027,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) if (sortinfo->item_compare_lc) { res = strcoll(p1, p2); } else { - res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); + res = sortinfo->item_compare_ic ? STRICMP(p1, p2): strcmp(p1, p2); } } else { double n1, n2; @@ -1079,15 +1090,17 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this funcexe_T funcexe = FUNCEXE_INIT; - funcexe.evaluate = true; - funcexe.partial = partial; - funcexe.selfdict = sortinfo->item_compare_selfdict; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; + funcexe.fe_selfdict = sortinfo->item_compare_selfdict; res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); tv_clear(&argv[0]); tv_clear(&argv[1]); if (res == FAIL) { + // XXX: ITEM_COMPARE_FAIL is unused res = ITEM_COMPARE_FAIL; + sortinfo->item_compare_func_err = true; } else { res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); if (res > 0) { @@ -1143,7 +1156,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) semsg(_(e_listarg), sort ? "sort()" : "uniq()"); } else { list_T *const l = argvars[0].vval.v_list; - if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { goto theend; } tv_list_set_ret(rettv, l); @@ -1242,20 +1255,18 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) item_compare_func_ptr = item_compare_keeping_zero; } - int idx = 0; for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) ; li != NULL;) { listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); if (item_compare_func_ptr(&prev_li, &li) == 0) { - if (info.item_compare_func_err) { // -V547 - emsg(_("E882: Uniq compare function failed")); - break; - } li = tv_list_item_remove(l, li); } else { - idx++; li = TV_LIST_ITEM_NEXT(l, li); } + if (info.item_compare_func_err) { + emsg(_("E882: Uniq compare function failed")); + break; + } } } @@ -1267,13 +1278,13 @@ theend: } /// "sort"({list})" function -void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_sort(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { do_sort_uniq(argvars, rettv, true); } /// "uniq({list})" function -void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_uniq(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { do_sort_uniq(argvars, rettv, false); } @@ -1356,7 +1367,7 @@ void tv_list_reverse(list_T *const l) /// true list will not be modified. Must be initialized to false /// by the caller. void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, - const ListSorter item_compare_func, bool *errp) + const ListSorter item_compare_func, const bool *errp) FUNC_ATTR_NONNULL_ARG(3, 4) { const int len = tv_list_len(l); @@ -1568,7 +1579,7 @@ bool tv_callback_equal(const Callback *cb1, const Callback *cb2) } switch (cb1->type) { case kCallbackFuncref: - return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; + return strcmp(cb1->data.funcref, cb2->data.funcref) == 0; case kCallbackPartial: // FIXME: this is inconsistent with tv_equal but is needed for precision // maybe change dictwatcheradd to return a watcher id instead? @@ -1588,7 +1599,7 @@ void callback_free(Callback *callback) { switch (callback->type) { case kCallbackFuncref: - func_unref((char_u *)callback->data.funcref); + func_unref(callback->data.funcref); xfree(callback->data.funcref); break; case kCallbackPartial: @@ -1617,7 +1628,7 @@ void callback_put(Callback *cb, typval_T *tv) case kCallbackFuncref: tv->v_type = VAR_FUNC; tv->vval.v_string = xstrdup(cb->data.funcref); - func_ref((char_u *)cb->data.funcref); + func_ref(cb->data.funcref); break; case kCallbackLua: // TODO(tjdevries): Unified Callback. @@ -1643,7 +1654,7 @@ void callback_copy(Callback *dest, Callback *src) break; case kCallbackFuncref: dest->data.funcref = xstrdup(src->data.funcref); - func_ref((char_u *)src->data.funcref); + func_ref(src->data.funcref); break; case kCallbackLua: dest->data.luaref = api_new_luaref(src->data.luaref); @@ -1740,9 +1751,8 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) const size_t len = watcher->key_pattern_len; if (len && watcher->key_pattern[len - 1] == '*') { return strncmp(key, watcher->key_pattern, len - 1) == 0; - } else { - return strcmp(key, watcher->key_pattern) == 0; } + return strcmp(key, watcher->key_pattern) == 0; } /// Send a change notification to all dictionary watchers that match given key @@ -1774,7 +1784,7 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key, typval_T tv_dict_add(argv[2].vval.v_dict, v); } - if (oldtv) { + if (oldtv && oldtv->v_type != VAR_UNKNOWN) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); tv_copy(oldtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); @@ -1834,6 +1844,7 @@ dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) di->di_key[key_len] = NUL; di->di_flags = DI_FLAGS_ALLOC; di->di_tv.v_lock = VAR_UNLOCKED; + di->di_tv.v_type = VAR_UNKNOWN; return di; } @@ -2053,9 +2064,24 @@ int tv_dict_get_tv(dict_T *d, const char *const key, typval_T *rettv) varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { + return tv_dict_get_number_def(d, key, 0); +} + +/// Get a number item from a dictionary. +/// +/// Returns "def" if the entry doesn't exist. +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Key to find in dictionary. +/// @param[in] def Default value. +/// +/// @return Dictionary item. +varnumber_T tv_dict_get_number_def(const dict_T *const d, const char *const key, const int def) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { - return 0; + return def; } return tv_get_number(&di->di_tv); } @@ -2074,7 +2100,7 @@ char **tv_dict_to_env(dict_T *denv) TV_DICT_ITER(denv, var, { const char *str = tv_get_string(&var->di_tv); assert(str); - size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1; + size_t len = strlen((char *)var->di_key) + strlen(str) + strlen("=") + 1; env[i] = xmalloc(len); snprintf(env[i], len, "%s=%s", (char *)var->di_key, str); i++; @@ -2183,6 +2209,15 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_ return res; } +/// Check for adding a function to g: or l:. +/// If the name is wrong give an error message and return true. +int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name) +{ + return (d == &globvardict || &d->dv_hashtab == get_funccal_local_ht()) + && tv_is_func(*tv) + && var_wrong_func_name(name, true); +} + //{{{2 dict_add* /// Add item to dictionary @@ -2194,7 +2229,10 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_ int tv_dict_add(dict_T *const d, dictitem_T *const item) FUNC_ATTR_NONNULL_ALL { - return hash_add(&d->dv_hashtab, item->di_key); + if (tv_dict_wrong_func_name(d, &item->di_tv, (const char *)item->di_key)) { + return FAIL; + } + return hash_add(&d->dv_hashtab, (char *)item->di_key); } /// Add a list entry to dictionary @@ -2410,10 +2448,10 @@ void tv_dict_clear(dict_T *const d) /// /// @param d1 Dictionary to extend. /// @param[in] d2 Dictionary to extend with. -/// @param[in] action "error", "force", "keep": -/// +/// @param[in] action "error", "force", "move", "keep": /// e*, including "error": duplicate key gives an error. /// f*, including "force": duplicate d2 keys override d1. +/// m*, including "move": move items instead of copying. /// other, including "keep": duplicate d2 keys ignored. void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action) FUNC_ATTR_NONNULL_ALL @@ -2422,27 +2460,33 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action const char *const arg_errmsg = _("extend() argument"); const size_t arg_errmsg_len = strlen(arg_errmsg); - TV_DICT_ITER(d2, di2, { + if (*action == 'm') { + hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove() + } + + HASHTAB_ITER(&d2->dv_hashtab, hi2, { + dictitem_T *const di2 = TV_DICT_HI2DI(hi2); dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1); - if (d1->dv_scope != VAR_NO_SCOPE) { - // Disallow replacing a builtin function in l: and g:. - // Check the key to be valid when adding to any scope. - if (d1->dv_scope == VAR_DEF_SCOPE - && tv_is_func(di2->di_tv) - && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { - break; - } - if (!valid_varname((const char *)di2->di_key)) { - break; - } + // Check the key to be valid when adding to any scope. + if (d1->dv_scope != VAR_NO_SCOPE && !valid_varname((const char *)di2->di_key)) { + break; } if (di1 == NULL) { - dictitem_T *const new_di = tv_dict_item_copy(di2); - if (tv_dict_add(d1, new_di) == FAIL) { - tv_dict_item_free(new_di); - } else if (watched) { - tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, - NULL); + if (*action == 'm') { + // Cheap way to move a dict item from "d2" to "d1". + // If dict_add() fails then "d2" won't be empty. + dictitem_T *const new_di = di2; + if (tv_dict_add(d1, new_di) == OK) { + hash_remove(&d2->dv_hashtab, hi2); + tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL); + } + } else { + dictitem_T *const new_di = tv_dict_item_copy(di2); + if (tv_dict_add(d1, new_di) == FAIL) { + tv_dict_item_free(new_di); + } else if (watched) { + tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL); + } } } else if (*action == 'e') { semsg(_("E737: Key already exists: %s"), di2->di_key); @@ -2450,10 +2494,14 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action } else if (*action == 'f' && di2 != di1) { typval_T oldtv; - if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { break; } + // Disallow replacing a builtin function. + if (tv_dict_wrong_func_name(d1, &di2->di_tv, (const char *)di2->di_key)) { + break; + } if (watched) { tv_copy(&di1->di_tv, &oldtv); @@ -2469,6 +2517,10 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action } } }); + + if (*action == 'm') { + hash_unlock(&d2->dv_hashtab); + } } /// Compare two dictionaries @@ -2483,10 +2535,14 @@ bool tv_dict_equal(dict_T *const d1, dict_T *const d2, const bool ic, const bool if (d1 == d2) { return true; } - if (d1 == NULL || d2 == NULL) { + if (tv_dict_len(d1) != tv_dict_len(d2)) { return false; } - if (tv_dict_len(d1) != tv_dict_len(d2)) { + if (tv_dict_len(d1) == 0) { + // empty and NULL dicts are considered equal + return true; + } + if (d1 == NULL || d2 == NULL) { return false; } @@ -2532,8 +2588,8 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, dict_T *const orig, const bool if (conv == NULL || conv->vc_type == CONV_NONE) { new_di = tv_dict_item_alloc((const char *)di->di_key); } else { - size_t len = STRLEN(di->di_key); - char *const key = (char *)string_convert(conv, di->di_key, &len); + size_t len = strlen((char *)di->di_key); + char *const key = (char *)string_convert(conv, (char *)di->di_key, &len); if (key == NULL) { new_di = tv_dict_item_alloc_len((const char *)di->di_key, len); } else { @@ -2654,7 +2710,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) { blob_T *const b = argvars[0].vval.v_blob; - if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { + if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { return; } @@ -2674,7 +2730,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) } if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - char_u *const p = (char_u *)b->bv_ga.ga_data; + uint8_t *const p = (uint8_t *)b->bv_ga.ga_data; rettv->vval.v_number = (varnumber_T)(*(p + idx)); memmove(p + idx, p + idx + 1, (size_t)(len - idx - 1)); b->bv_ga.ga_len--; @@ -2696,9 +2752,8 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) blob->bv_ga.ga_len = (int)(end - idx + 1); ga_grow(&blob->bv_ga, (int)(end - idx + 1)); - char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove((char_u *)blob->bv_ga.ga_data, p + idx, - (size_t)(end - idx + 1)); + uint8_t *const p = (uint8_t *)b->bv_ga.ga_data; + memmove(blob->bv_ga.ga_data, p + idx, (size_t)(end - idx + 1)); tv_blob_set_ret(rettv, blob); if (len - end - 1 > 0) { @@ -2777,7 +2832,7 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi switch (what) { case kDictListKeys: tv_item.v_type = VAR_STRING; - tv_item.vval.v_string = (char *)vim_strsave(di->di_key); + tv_item.vval.v_string = xstrdup((char *)di->di_key); break; case kDictListValues: tv_copy(&di->di_tv, &tv_item); @@ -2806,25 +2861,25 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi } /// "items(dict)" function -void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_list(argvars, rettv, 2); } /// "keys()" function -void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_keys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_list(argvars, rettv, 0); } /// "values(dict)" function -void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_list(argvars, rettv, 1); } /// "has_key()" function -void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_has_key(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_DICT) { emsg(_(e_dictreq)); @@ -2846,7 +2901,7 @@ void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) if (argvars[2].v_type != VAR_UNKNOWN) { semsg(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { + && !value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { dictitem_T *di = tv_dict_find(d, key, -1); @@ -2951,7 +3006,7 @@ void tv_blob_copy(typval_T *const from, typval_T *const to) (tv)->v_lock = VAR_UNLOCKED; \ } while (0) -static inline int _nothing_conv_func_start(typval_T *const tv, char_u *const fun) +static inline int _nothing_conv_func_start(typval_T *const tv, char *const fun) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) { tv->v_lock = VAR_UNLOCKED; @@ -3114,6 +3169,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic #define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const #define TYPVAL_ENCODE_FIRST_ARG_NAME ignored #include "nvim/eval/typval_encode.c.h" + #undef TYPVAL_ENCODE_SCOPE #undef TYPVAL_ENCODE_NAME #undef TYPVAL_ENCODE_FIRST_ARG_TYPE @@ -3178,7 +3234,7 @@ void tv_free(typval_T *tv) partial_unref(tv->vval.v_partial); break; case VAR_FUNC: - func_unref((char_u *)tv->vval.v_string); + func_unref(tv->vval.v_string); FALLTHROUGH; case VAR_STRING: xfree(tv->vval.v_string); @@ -3231,7 +3287,7 @@ void tv_copy(const typval_T *const from, typval_T *const to) if (from->vval.v_string != NULL) { to->vval.v_string = xstrdup(from->vval.v_string); if (from->v_type == VAR_FUNC) { - func_ref((char_u *)to->vval.v_string); + func_ref(to->vval.v_string); } } break; @@ -3403,12 +3459,12 @@ bool tv_check_lock(const typval_T *tv, const char *name, size_t name_len) default: break; } - return var_check_lock(tv->v_lock, name, name_len) - || (lock != VAR_UNLOCKED && var_check_lock(lock, name, name_len)); + return value_check_lock(tv->v_lock, name, name_len) + || (lock != VAR_UNLOCKED && value_check_lock(lock, name, name_len)); } -/// @return true if variable "name" is locked (immutable) -bool var_check_lock(VarLockStatus lock, const char *name, size_t name_len) +/// @return true if variable "name" has a locked (immutable) value +bool value_check_lock(VarLockStatus lock, const char *name, size_t name_len) { const char *error_message = NULL; switch (lock) { @@ -3583,13 +3639,13 @@ bool tv_check_str_or_nr(const typval_T *const tv) #define FUNC_ERROR "E703: Using a Funcref as a Number" static const char *const num_errors[] = { - [VAR_PARTIAL]=N_(FUNC_ERROR), - [VAR_FUNC]=N_(FUNC_ERROR), - [VAR_LIST]=N_("E745: Using a List as a Number"), - [VAR_DICT]=N_("E728: Using a Dictionary as a Number"), - [VAR_FLOAT]=N_("E805: Using a Float as a Number"), - [VAR_BLOB]=N_("E974: Using a Blob as a Number"), - [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"), + [VAR_PARTIAL]= N_(FUNC_ERROR), + [VAR_FUNC]= N_(FUNC_ERROR), + [VAR_LIST]= N_("E745: Using a List as a Number"), + [VAR_DICT]= N_("E728: Using a Dictionary as a Number"), + [VAR_FLOAT]= N_("E805: Using a Float as a Number"), + [VAR_BLOB]= N_("E974: Using a Blob as a Number"), + [VAR_UNKNOWN]= N_("E685: using an invalid value as a Number"), }; #undef FUNC_ERROR @@ -3628,13 +3684,13 @@ bool tv_check_num(const typval_T *const tv) #define FUNC_ERROR "E729: using Funcref as a String" static const char *const str_errors[] = { - [VAR_PARTIAL]=N_(FUNC_ERROR), - [VAR_FUNC]=N_(FUNC_ERROR), - [VAR_LIST]=N_("E730: using List as a String"), - [VAR_DICT]=N_("E731: using Dictionary as a String"), - [VAR_FLOAT]=((const char *)e_float_as_string), - [VAR_BLOB]=N_("E976: using Blob as a String"), - [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"), + [VAR_PARTIAL]= N_(FUNC_ERROR), + [VAR_FUNC]= N_(FUNC_ERROR), + [VAR_LIST]= N_("E730: using List as a String"), + [VAR_DICT]= N_("E731: using Dictionary as a String"), + [VAR_FLOAT]= ((const char *)e_float_as_string), + [VAR_BLOB]= N_("E976: using Blob as a String"), + [VAR_UNKNOWN]= N_("E908: using an invalid value as a String"), }; #undef FUNC_ERROR @@ -3717,8 +3773,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_STRING: { varnumber_T n = 0; if (tv->vval.v_string != NULL) { - vim_str2nr((char_u *)tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, - false); + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, false); } return n; } @@ -3746,9 +3801,11 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) linenr_T tv_get_lnum(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + const int did_emsg_before = did_emsg; linenr_T lnum = (linenr_T)tv_get_number_chk(tv, NULL); - if (lnum == 0) { // No valid number, try using same function as line() does. + if (lnum <= 0 && did_emsg_before == did_emsg && tv->v_type != VAR_NUMBER) { int fnum; + // No valid number, try using same function as line() does. pos_T *const fp = var2fpos(tv, true, &fnum, false); if (fp != NULL) { lnum = fp->lnum; @@ -3801,26 +3858,56 @@ float_T tv_get_float(const typval_T *const tv) return 0; } -// Give an error and return FAIL unless "tv" is a string. -int tv_check_for_string(const typval_T *const tv) +/// Give an error and return FAIL unless "args[idx]" is a string. +int tv_check_for_string_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (args[idx].v_type != VAR_STRING) { + semsg(_(e_string_required_for_argument_nr), idx + 1); + return FAIL; + } + return OK; +} + +/// Give an error and return FAIL unless "args[idx]" is a non-empty string. +int tv_check_for_nonempty_string_arg(const typval_T *const args, const int idx) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { - if (tv->v_type != VAR_STRING) { - emsg(_(e_stringreq)); + if (tv_check_for_string_arg(args, idx) == FAIL) { + return FAIL; + } + if (args[idx].vval.v_string == NULL || *args[idx].vval.v_string == NUL) { + semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1); return FAIL; } return OK; } -// Give an error and return FAIL unless "tv" is a non-empty string. -int tv_check_for_nonempty_string(const typval_T *const tv) +/// Give an error and return FAIL unless "args[idx]" is a number. +int tv_check_for_number_arg(const typval_T *const args, const int idx) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { - if (tv_check_for_string(tv) == FAIL) { + if (args[idx].v_type != VAR_NUMBER) { + semsg(_(e_number_required_for_argument_nr), idx + 1); return FAIL; } - if (tv->vval.v_string == NULL || *tv->vval.v_string == NUL) { - emsg(_(e_non_empty_string_required)); + return OK; +} + +/// Check for an optional number argument at "idx" +int tv_check_for_opt_number_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return (args[idx].v_type == VAR_UNKNOWN + || tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL; +} + +/// Give an error and return FAIL unless "args[idx]" is a string or a list. +int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_LIST) { + semsg(_(e_string_or_list_required_for_argument_nr), idx + 1); return FAIL; } return OK; diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index c4bc9f603b..3f59cd3547 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -2,11 +2,12 @@ #define NVIM_EVAL_TYPVAL_H #include <assert.h> -#include <inttypes.h> #include <stdbool.h> #include <stddef.h> +#include <stdint.h> #include <string.h> +#include "nvim/eval/typval_defs.h" #include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/gettext.h" @@ -15,405 +16,10 @@ #include "nvim/macros.h" #include "nvim/mbyte_defs.h" #include "nvim/message.h" -#include "nvim/pos.h" // for linenr_T #include "nvim/types.h" -#ifdef LOG_LIST_ACTIONS -# include "nvim/memory.h" -#endif - -/// Type used for VimL VAR_NUMBER values -typedef int64_t varnumber_T; -typedef uint64_t uvarnumber_T; - -/// Type used for VimL VAR_FLOAT values -typedef double float_T; - -/// Refcount for dict or list that should not be freed -enum { DO_NOT_FREE_CNT = (INT_MAX / 2), }; - -/// Additional values for tv_list_alloc() len argument -enum ListLenSpecials { - /// List length is not known in advance - /// - /// To be used when there is neither a way to know how many elements will be - /// needed nor are any educated guesses. - kListLenUnknown = -1, - /// List length *should* be known, but is actually not - /// - /// All occurrences of this value should be eventually removed. This is for - /// the case when the only reason why list length is not known is that it - /// would be hard to code without refactoring, but refactoring is needed. - kListLenShouldKnow = -2, - /// List length may be known in advance, but it requires too much effort - /// - /// To be used when it looks impractical to determine list length. - kListLenMayKnow = -3, -}; - -/// Maximal possible value of varnumber_T variable -#define VARNUMBER_MAX INT64_MAX -#define UVARNUMBER_MAX UINT64_MAX - -/// Minimal possible value of varnumber_T variable -#define VARNUMBER_MIN INT64_MIN - -/// %d printf format specifier for varnumber_T -#define PRIdVARNUMBER PRId64 - -typedef struct listvar_S list_T; -typedef struct dictvar_S dict_T; -typedef struct partial_S partial_T; -typedef struct blobvar_S blob_T; - -typedef struct ufunc ufunc_T; - -typedef enum { - kCallbackNone = 0, - kCallbackFuncref, - kCallbackPartial, - kCallbackLua, -} CallbackType; - -typedef struct { - union { - char *funcref; - partial_T *partial; - LuaRef luaref; - } data; - CallbackType type; -} Callback; - -#define CALLBACK_INIT { .type = kCallbackNone } -#define CALLBACK_NONE ((Callback)CALLBACK_INIT) - -/// Structure holding dictionary watcher -typedef struct dict_watcher { - Callback callback; - char *key_pattern; - size_t key_pattern_len; - QUEUE node; - bool busy; // prevent recursion if the dict is changed in the callback - bool needs_free; -} DictWatcher; - -/// Bool variable values -typedef enum { - kBoolVarFalse, ///< v:false - kBoolVarTrue, ///< v:true -} BoolVarValue; - -/// Special variable values -typedef enum { - kSpecialVarNull, ///< v:null -} SpecialVarValue; - -/// Variable lock status for typval_T.v_lock -typedef enum { - VAR_UNLOCKED = 0, ///< Not locked. - VAR_LOCKED = 1, ///< User lock, can be unlocked. - VAR_FIXED = 2, ///< Locked forever. -} VarLockStatus; - -/// VimL variable types, for use in typval_T.v_type -typedef enum { - VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. - VAR_NUMBER, ///< Number, .v_number is used. - VAR_STRING, ///< String, .v_string is used. - VAR_FUNC, ///< Function reference, .v_string is used as function name. - VAR_LIST, ///< List, .v_list is used. - VAR_DICT, ///< Dictionary, .v_dict is used. - VAR_FLOAT, ///< Floating-point value, .v_float is used. - VAR_BOOL, ///< true, false - VAR_SPECIAL, ///< Special value (null), .v_special - ///< is used. - VAR_PARTIAL, ///< Partial, .v_partial is used. - VAR_BLOB, ///< Blob, .v_blob is used. -} VarType; - -/// Structure that holds an internal variable value -typedef struct { - VarType v_type; ///< Variable type. - VarLockStatus v_lock; ///< Variable lock status. - union typval_vval_union { - varnumber_T v_number; ///< Number, for VAR_NUMBER. - BoolVarValue v_bool; ///< Bool value, for VAR_BOOL - SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. - float_T v_float; ///< Floating-point number, for VAR_FLOAT. - char *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. - list_T *v_list; ///< List for VAR_LIST, can be NULL. - dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. - partial_T *v_partial; ///< Closure: function with args. - blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL. - } vval; ///< Actual value. -} typval_T; - -/// Values for (struct dictvar_S).dv_scope -typedef enum { - VAR_NO_SCOPE = 0, ///< Not a scope dictionary. - VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …). - VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix - ///< (l:, g:). -} ScopeType; - -/// Structure to hold an item of a list -typedef struct listitem_S listitem_T; - -struct listitem_S { - listitem_T *li_next; ///< Next item in list. - listitem_T *li_prev; ///< Previous item in list. - typval_T li_tv; ///< Item value. -}; - -/// Structure used by those that are using an item in a list -typedef struct listwatch_S listwatch_T; - -struct listwatch_S { - listitem_T *lw_item; ///< Item being watched. - listwatch_T *lw_next; ///< Next watcher. -}; - -/// Structure to hold info about a list -/// Order of members is optimized to reduce padding. -struct listvar_S { - listitem_T *lv_first; ///< First item, NULL if none. - listitem_T *lv_last; ///< Last item, NULL if none. - listwatch_T *lv_watch; ///< First watcher, NULL if none. - listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". - list_T *lv_copylist; ///< Copied list used by deepcopy(). - list_T *lv_used_next; ///< next list in used lists list. - list_T *lv_used_prev; ///< Previous list in used lists list. - int lv_refcount; ///< Reference count. - int lv_len; ///< Number of items. - int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. - int lv_copyID; ///< ID used by deepcopy(). - VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. - - LuaRef lua_table_ref; -}; - -// Static list with 10 items. Use tv_list_init_static10() to initialize. -typedef struct { - list_T sl_list; // must be first - listitem_T sl_items[10]; -} staticList10_T; - -#define TV_LIST_STATIC10_INIT { \ - .sl_list = { \ - .lv_first = NULL, \ - .lv_last = NULL, \ - .lv_refcount = 0, \ - .lv_len = 0, \ - .lv_watch = NULL, \ - .lv_idx_item = NULL, \ - .lv_lock = VAR_FIXED, \ - .lv_used_next = NULL, \ - .lv_used_prev = NULL, \ - }, \ -} - -#define TV_DICTITEM_STRUCT(...) \ - struct { \ - typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ - uint8_t di_flags; /* Flags. */ \ - char_u di_key[__VA_ARGS__]; /* Key value. */ \ - } - -/// Structure to hold a scope dictionary -/// -/// @warning Must be compatible with dictitem_T. -/// -/// For use in find_var_in_ht to pretend that it found dictionary item when it -/// finds scope dictionary. -typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem; - -/// Structure to hold an item of a Dictionary -/// -/// @warning Must be compatible with ScopeDictDictItem. -/// -/// Also used for a variable. -typedef TV_DICTITEM_STRUCT() dictitem_T; - -/// Flags for dictitem_T.di_flags -typedef enum { - DI_FLAGS_RO = 1, ///< Read-only value - DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox - DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d. - DI_FLAGS_LOCK = 8, ///< Locked value. - DI_FLAGS_ALLOC = 16, ///< Separately allocated. -} DictItemFlags; - -/// Structure representing a Dictionary -struct dictvar_S { - VarLockStatus dv_lock; ///< Whole dictionary lock status. - ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if - ///< dictionary represents a scope (i.e. g:, l: …). - int dv_refcount; ///< Reference count. - int dv_copyID; ///< ID used when recursivery traversing a value. - hashtab_T dv_hashtab; ///< Hashtab containing all items. - dict_T *dv_copydict; ///< Copied dict used by deepcopy(). - dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. - dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. - QUEUE watchers; ///< Dictionary key watchers set by user code. - - LuaRef lua_table_ref; -}; - -/// Structure to hold info about a Blob -struct blobvar_S { - garray_T bv_ga; ///< Growarray with the data. - int bv_refcount; ///< Reference count. - VarLockStatus bv_lock; ///< VAR_UNLOCKED, VAR_LOCKED, VAR_FIXED. -}; - -/// Type used for script ID -typedef int scid_T; -/// Format argument for scid_T -#define PRIdSCID "d" - -// SCript ConteXt (SCTX): identifies a script line. -// When sourcing a script "sc_lnum" is zero, "sourcing_lnum" is the current -// line number. When executing a user function "sc_lnum" is the line where the -// function was defined, "sourcing_lnum" is the line number inside the -// function. When stored with a function, mapping, option, etc. "sc_lnum" is -// the line number in the script "sc_sid". -typedef struct { - scid_T sc_sid; // script ID - int sc_seq; // sourcing sequence number - linenr_T sc_lnum; // line number -} sctx_T; - -/// Maximum number of function arguments -#define MAX_FUNC_ARGS 20 -/// Short variable name length -#define VAR_SHORT_LEN 20 -/// Number of fixed variables used for arguments -#define FIXVAR_CNT 12 - -/// Callback interface for C function reference> -/// Used for managing functions that were registered with |register_cfunc| -typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state); // NOLINT -/// Callback to clear cfunc_T and any associated state. -typedef void (*cfunc_free_T)(void *state); - -// Structure to hold info for a function that is currently being executed. -typedef struct funccall_S funccall_T; - -struct funccall_S { - ufunc_T *func; ///< Function being called. - int linenr; ///< Next line to be executed. - int returned; ///< ":return" used. - /// Fixed variables for arguments. - TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT]; - dict_T l_vars; ///< l: local function variables. - ScopeDictDictItem l_vars_var; ///< Variable for l: scope. - dict_T l_avars; ///< a: argument variables. - ScopeDictDictItem l_avars_var; ///< Variable for a: scope. - list_T l_varlist; ///< List for a:000. - listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000. - typval_T *rettv; ///< Return value. - linenr_T breakpoint; ///< Next line with breakpoint or zero. - int dbg_tick; ///< Debug_tick when breakpoint was set. - int level; ///< Top nesting level of executed function. - proftime_T prof_child; ///< Time spent in a child. - funccall_T *caller; ///< Calling function or NULL; or next funccal in - ///< list pointed to by previous_funccal. - int fc_refcount; ///< Number of user functions that reference this funccall. - int fc_copyID; ///< CopyID used for garbage collection. - garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func". -}; - -/// Structure to hold info for a user function. -struct ufunc { - int uf_varargs; ///< variable nr of arguments - int uf_flags; - int uf_calls; ///< nr of active calls - bool uf_cleared; ///< func_clear() was already called - garray_T uf_args; ///< arguments - garray_T uf_def_args; ///< default argument expressions - garray_T uf_lines; ///< function lines - int uf_profiling; ///< true when func is being profiled - int uf_prof_initialized; - // Managing cfuncs - cfunc_T uf_cb; ///< C function extension callback - cfunc_free_T uf_cb_free; ///< C function extension free callback - void *uf_cb_state; ///< State of C function extension. - // Profiling the function as a whole. - int uf_tm_count; ///< nr of calls - proftime_T uf_tm_total; ///< time spent in function + children - proftime_T uf_tm_self; ///< time spent in function itself - proftime_T uf_tm_children; ///< time spent in children this call - // Profiling the function per line. - int *uf_tml_count; ///< nr of times line was executed - proftime_T *uf_tml_total; ///< time spent in a line + children - proftime_T *uf_tml_self; ///< time spent in a line itself - proftime_T uf_tml_start; ///< start time for current line - proftime_T uf_tml_children; ///< time spent in children for this line - proftime_T uf_tml_wait; ///< start wait time for current line - int uf_tml_idx; ///< index of line being timed; -1 if none - int uf_tml_execed; ///< line being timed was executed - sctx_T uf_script_ctx; ///< SCTX where function was defined, - ///< used for s: variables - int uf_refcount; ///< reference count, see func_name_refcount() - funccall_T *uf_scoped; ///< l: local variables for closure - char_u *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with - ///< "<SNR>" as a string, otherwise NULL - char_u uf_name[]; ///< Name of function (actual size equals name); - ///< can start with <SNR>123_ - ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR) -}; - -struct partial_S { - int pt_refcount; ///< Reference count. - char_u *pt_name; ///< Function name; when NULL use pt_func->name. - ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with - ///< pt_name. - bool pt_auto; ///< When true the partial was created by using dict.member - ///< in handle_subscript(). - int pt_argc; ///< Number of arguments. - typval_T *pt_argv; ///< Arguments in allocated array. - dict_T *pt_dict; ///< Dict for "self". -}; - -/// Structure used for explicit stack while garbage collecting hash tables -typedef struct ht_stack_S { - hashtab_T *ht; - struct ht_stack_S *prev; -} ht_stack_T; - -/// Structure used for explicit stack while garbage collecting lists -typedef struct list_stack_S { - list_T *list; - struct list_stack_S *prev; -} list_stack_T; - -/// Structure representing one list item, used for sort array. -typedef struct { - listitem_T *item; ///< Sorted list item. - int idx; ///< Sorted list item index. -} ListSortItem; - -typedef int (*ListSorter)(const void *, const void *); #ifdef LOG_LIST_ACTIONS - -/// List actions log entry -typedef struct { - uintptr_t l; ///< List log entry belongs to. - uintptr_t li1; ///< First list item log entry belongs to, if applicable. - uintptr_t li2; ///< Second list item log entry belongs to, if applicable. - int len; ///< List length when log entry was created. - const char *action; ///< Logged action. -} ListLogEntry; - -typedef struct list_log ListLog; - -/// List actions log -struct list_log { - ListLog *next; ///< Next chunk or NULL. - size_t capacity; ///< Number of entries in current chunk. - size_t size; ///< Current chunk size. - ListLogEntry entries[]; ///< Actual log entries. -}; +# include "nvim/memory.h" extern ListLog *list_log_first; ///< First list log chunk, NULL if missing extern ListLog *list_log_last; ///< Last list log chunk @@ -442,10 +48,8 @@ static inline ListLog *list_log_new(const size_t size) return ret; } -static inline void list_log(const list_T *const l, - const listitem_T *const li1, - const listitem_T *const li2, - const char *const action) +static inline void list_log(const list_T *const l, const listitem_T *const li1, + const listitem_T *const li2, const char *const action) REAL_FATTR_ALWAYS_INLINE; /// Add new entry to log @@ -489,7 +93,7 @@ static inline void list_log(const list_T *const l, const listitem_T *const li1, #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline void tv_list_ref(list_T *const l) +static inline void tv_list_ref(list_T *l) REAL_FATTR_ALWAYS_INLINE; /// Increase reference count for a given list @@ -505,7 +109,7 @@ static inline void tv_list_ref(list_T *const l) l->lv_refcount++; } -static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) +static inline void tv_list_set_ret(typval_T *tv, list_T *l) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a list as the return value. Increments the reference count. @@ -519,7 +123,7 @@ static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) tv_list_ref(l); } -static inline VarLockStatus tv_list_locked(const list_T *const l) +static inline VarLockStatus tv_list_locked(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get list lock status @@ -562,7 +166,7 @@ static inline void tv_list_set_copyid(list_T *const l, const int copyid) l->lv_copyID = copyid; } -static inline int tv_list_len(const list_T *const l) +static inline int tv_list_len(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list @@ -577,7 +181,7 @@ static inline int tv_list_len(const list_T *const l) return l->lv_len; } -static inline int tv_list_copyid(const list_T *const l) +static inline int tv_list_copyid(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get list copyID @@ -590,7 +194,7 @@ static inline int tv_list_copyid(const list_T *const l) return l->lv_copyID; } -static inline list_T *tv_list_latest_copy(const list_T *const l) +static inline list_T *tv_list_latest_copy(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get latest list copy @@ -605,7 +209,7 @@ static inline list_T *tv_list_latest_copy(const list_T *const l) return l->lv_copylist; } -static inline int tv_list_uidx(const list_T *const l, int n) +static inline int tv_list_uidx(const list_T *l, int n) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Normalize index: that is, return either -1 or non-negative index @@ -628,7 +232,7 @@ static inline int tv_list_uidx(const list_T *const l, int n) return n; } -static inline bool tv_list_has_watchers(const list_T *const l) +static inline bool tv_list_has_watchers(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Check whether list has watchers @@ -643,7 +247,7 @@ static inline bool tv_list_has_watchers(const list_T *const l) return l && l->lv_watch; } -static inline listitem_T *tv_list_first(const list_T *const l) +static inline listitem_T *tv_list_first(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get first list item @@ -661,7 +265,7 @@ static inline listitem_T *tv_list_first(const list_T *const l) return l->lv_first; } -static inline listitem_T *tv_list_last(const list_T *const l) +static inline listitem_T *tv_list_last(const list_T *l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get last list item @@ -679,7 +283,7 @@ static inline listitem_T *tv_list_last(const list_T *const l) return l->lv_last; } -static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d) +static inline void tv_dict_set_ret(typval_T *tv, dict_T *d) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a dictionary as the return value @@ -695,7 +299,7 @@ static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d) } } -static inline long tv_dict_len(const dict_T *const d) +static inline long tv_dict_len(const dict_T *d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a Dictionary @@ -709,7 +313,7 @@ static inline long tv_dict_len(const dict_T *const d) return (long)d->dv_hashtab.ht_used; } -static inline bool tv_dict_is_watched(const dict_T *const d) +static inline bool tv_dict_is_watched(const dict_T *d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Check if dictionary is watched @@ -722,7 +326,7 @@ static inline bool tv_dict_is_watched(const dict_T *const d) return d && !QUEUE_EMPTY(&d->watchers); } -static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) +static inline void tv_blob_set_ret(typval_T *tv, blob_T *b) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a blob as the return value. @@ -740,7 +344,7 @@ static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) } } -static inline int tv_blob_len(const blob_T *const b) +static inline int tv_blob_len(const blob_T *b) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the length of the data in the blob, in bytes. @@ -754,7 +358,7 @@ static inline int tv_blob_len(const blob_T *const b) return b->bv_ga.ga_len; } -static inline char_u tv_blob_get(const blob_T *const b, int idx) +static inline uint8_t tv_blob_get(const blob_T *b, int idx) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; /// Get the byte at index `idx` in the blob. @@ -763,12 +367,12 @@ static inline char_u tv_blob_get(const blob_T *const b, int idx) /// @param[in] idx Index in a blob. Must be valid. /// /// @return Byte value at the given index. -static inline char_u tv_blob_get(const blob_T *const b, int idx) +static inline uint8_t tv_blob_get(const blob_T *const b, int idx) { - return ((char_u *)b->bv_ga.ga_data)[idx]; + return ((uint8_t *)b->bv_ga.ga_data)[idx]; } -static inline void tv_blob_set(blob_T *const b, int idx, char_u c) +static inline void tv_blob_set(blob_T *b, int idx, uint8_t c) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; /// Store the byte `c` at index `idx` in the blob. @@ -776,9 +380,9 @@ static inline void tv_blob_set(blob_T *const b, int idx, char_u c) /// @param[in] b Blob to index. Cannot be NULL. /// @param[in] idx Index in a blob. Must be valid. /// @param[in] c Value to store. -static inline void tv_blob_set(blob_T *const b, int idx, char_u c) +static inline void tv_blob_set(blob_T *const b, int idx, uint8_t c) { - ((char_u *)b->bv_ga.ga_data)[idx] = c; + ((uint8_t *)b->bv_ga.ga_data)[idx] = c; } /// Initialize VimL object @@ -793,12 +397,6 @@ static inline void tv_init(typval_T *const tv) } } -#define TV_INITIAL_VALUE \ - ((typval_T) { \ - .v_type = VAR_UNKNOWN, \ - .v_lock = VAR_UNLOCKED, \ - }) - /// Empty string /// /// Needed for hack which allows not allocating empty string and still not @@ -836,7 +434,7 @@ extern bool tv_in_free_unref_items; /// @param li Name of the variable with current listitem_T entry. /// @param code Cycle body. #define TV_LIST_ITER(l, li, code) \ - _TV_LIST_ITER_MOD( , l, li, code) + _TV_LIST_ITER_MOD( , l, li, code) // NOLINT(whitespace/parens) /// Iterate over a list /// @@ -893,13 +491,9 @@ extern bool tv_in_free_unref_items; } \ }) -static inline bool tv_get_float_chk(const typval_T *const tv, - float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *tv, float_T *ret_f) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; -// FIXME circular dependency, cannot import message.h. -bool semsg(const char *const fmt, ...); - /// Get the float value /// /// Raises an error if object is not number or floating-point. @@ -935,7 +529,7 @@ static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) return QUEUE_DATA(q, DictWatcher, node); } -static inline bool tv_is_func(const typval_T tv) +static inline bool tv_is_func(typval_T tv) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; /// Check whether given typval_T contains a function @@ -970,4 +564,9 @@ EXTERN const size_t kTVTranslate INIT(= TV_TRANSLATE); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif + +#define tv_get_bool tv_get_number +#define tv_get_bool_chk tv_get_number_chk +#define tv_dict_get_bool tv_dict_get_number_def + #endif // NVIM_EVAL_TYPVAL_H diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h new file mode 100644 index 0000000000..4615198441 --- /dev/null +++ b/src/nvim/eval/typval_defs.h @@ -0,0 +1,399 @@ +#ifndef NVIM_EVAL_TYPVAL_DEFS_H +#define NVIM_EVAL_TYPVAL_DEFS_H + +#include <inttypes.h> +#include <limits.h> + +#include "nvim/garray.h" +#include "nvim/hashtab.h" +#include "nvim/lib/queue.h" +#include "nvim/pos.h" +#include "nvim/types.h" + +/// Type used for VimL VAR_NUMBER values +typedef int64_t varnumber_T; +typedef uint64_t uvarnumber_T; + +/// Refcount for dict or list that should not be freed +enum { DO_NOT_FREE_CNT = (INT_MAX / 2), }; + +/// Additional values for tv_list_alloc() len argument +enum ListLenSpecials { + /// List length is not known in advance + /// + /// To be used when there is neither a way to know how many elements will be + /// needed nor are any educated guesses. + kListLenUnknown = -1, + /// List length *should* be known, but is actually not + /// + /// All occurrences of this value should be eventually removed. This is for + /// the case when the only reason why list length is not known is that it + /// would be hard to code without refactoring, but refactoring is needed. + kListLenShouldKnow = -2, + /// List length may be known in advance, but it requires too much effort + /// + /// To be used when it looks impractical to determine list length. + kListLenMayKnow = -3, +}; + +/// Maximal possible value of varnumber_T variable +#define VARNUMBER_MAX INT64_MAX +#define UVARNUMBER_MAX UINT64_MAX + +/// Minimal possible value of varnumber_T variable +#define VARNUMBER_MIN INT64_MIN + +/// %d printf format specifier for varnumber_T +#define PRIdVARNUMBER PRId64 + +typedef struct listvar_S list_T; +typedef struct dictvar_S dict_T; +typedef struct partial_S partial_T; +typedef struct blobvar_S blob_T; + +typedef struct ufunc ufunc_T; + +typedef enum { + kCallbackNone = 0, + kCallbackFuncref, + kCallbackPartial, + kCallbackLua, +} CallbackType; + +typedef struct { + union { + char *funcref; + partial_T *partial; + LuaRef luaref; + } data; + CallbackType type; +} Callback; + +#define CALLBACK_INIT { .type = kCallbackNone } +#define CALLBACK_NONE ((Callback)CALLBACK_INIT) + +/// Structure holding dictionary watcher +typedef struct dict_watcher { + Callback callback; + char *key_pattern; + size_t key_pattern_len; + QUEUE node; + bool busy; // prevent recursion if the dict is changed in the callback + bool needs_free; +} DictWatcher; + +/// Bool variable values +typedef enum { + kBoolVarFalse, ///< v:false + kBoolVarTrue, ///< v:true +} BoolVarValue; + +/// Special variable values +typedef enum { + kSpecialVarNull, ///< v:null +} SpecialVarValue; + +/// Variable lock status for typval_T.v_lock +typedef enum { + VAR_UNLOCKED = 0, ///< Not locked. + VAR_LOCKED = 1, ///< User lock, can be unlocked. + VAR_FIXED = 2, ///< Locked forever. +} VarLockStatus; + +/// VimL variable types, for use in typval_T.v_type +typedef enum { + VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. + VAR_NUMBER, ///< Number, .v_number is used. + VAR_STRING, ///< String, .v_string is used. + VAR_FUNC, ///< Function reference, .v_string is used as function name. + VAR_LIST, ///< List, .v_list is used. + VAR_DICT, ///< Dictionary, .v_dict is used. + VAR_FLOAT, ///< Floating-point value, .v_float is used. + VAR_BOOL, ///< true, false + VAR_SPECIAL, ///< Special value (null), .v_special is used. + VAR_PARTIAL, ///< Partial, .v_partial is used. + VAR_BLOB, ///< Blob, .v_blob is used. +} VarType; + +/// Structure that holds an internal variable value +typedef struct { + VarType v_type; ///< Variable type. + VarLockStatus v_lock; ///< Variable lock status. + union typval_vval_union { + varnumber_T v_number; ///< Number, for VAR_NUMBER. + BoolVarValue v_bool; ///< Bool value, for VAR_BOOL + SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. + float_T v_float; ///< Floating-point number, for VAR_FLOAT. + char *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. + list_T *v_list; ///< List for VAR_LIST, can be NULL. + dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. + partial_T *v_partial; ///< Closure: function with args. + blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL. + } vval; ///< Actual value. +} typval_T; + +#define TV_INITIAL_VALUE \ + ((typval_T) { \ + .v_type = VAR_UNKNOWN, \ + .v_lock = VAR_UNLOCKED, \ + }) + +/// Values for (struct dictvar_S).dv_scope +typedef enum { + VAR_NO_SCOPE = 0, ///< Not a scope dictionary. + VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …). + VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix + ///< (l:, g:). +} ScopeType; + +/// Structure to hold an item of a list +typedef struct listitem_S listitem_T; + +struct listitem_S { + listitem_T *li_next; ///< Next item in list. + listitem_T *li_prev; ///< Previous item in list. + typval_T li_tv; ///< Item value. +}; + +/// Structure used by those that are using an item in a list +typedef struct listwatch_S listwatch_T; + +struct listwatch_S { + listitem_T *lw_item; ///< Item being watched. + listwatch_T *lw_next; ///< Next watcher. +}; + +/// Structure to hold info about a list +/// Order of members is optimized to reduce padding. +struct listvar_S { + listitem_T *lv_first; ///< First item, NULL if none. + listitem_T *lv_last; ///< Last item, NULL if none. + listwatch_T *lv_watch; ///< First watcher, NULL if none. + listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". + list_T *lv_copylist; ///< Copied list used by deepcopy(). + list_T *lv_used_next; ///< next list in used lists list. + list_T *lv_used_prev; ///< Previous list in used lists list. + int lv_refcount; ///< Reference count. + int lv_len; ///< Number of items. + int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. + int lv_copyID; ///< ID used by deepcopy(). + VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. + + LuaRef lua_table_ref; +}; + +/// Static list with 10 items. Use tv_list_init_static10() to initialize. +typedef struct { + list_T sl_list; // must be first + listitem_T sl_items[10]; +} staticList10_T; + +#define TV_LIST_STATIC10_INIT { \ + .sl_list = { \ + .lv_first = NULL, \ + .lv_last = NULL, \ + .lv_refcount = 0, \ + .lv_len = 0, \ + .lv_watch = NULL, \ + .lv_idx_item = NULL, \ + .lv_lock = VAR_FIXED, \ + .lv_used_next = NULL, \ + .lv_used_prev = NULL, \ + }, \ +} + +#define TV_DICTITEM_STRUCT(...) \ + struct { \ + typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ + uint8_t di_flags; /* Flags. */ \ + char_u di_key[__VA_ARGS__]; /* Key value. */ /* NOLINT(runtime/arrays)*/ \ + } + +/// Structure to hold a scope dictionary +/// +/// @warning Must be compatible with dictitem_T. +/// +/// For use in find_var_in_ht to pretend that it found dictionary item when it +/// finds scope dictionary. +typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem; + +/// Structure to hold an item of a Dictionary +/// +/// @warning Must be compatible with ScopeDictDictItem. +/// +/// Also used for a variable. +typedef TV_DICTITEM_STRUCT() dictitem_T; + +/// Flags for dictitem_T.di_flags +typedef enum { + DI_FLAGS_RO = 1, ///< Read-only value + DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox + DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d. + DI_FLAGS_LOCK = 8, ///< Locked value. + DI_FLAGS_ALLOC = 16, ///< Separately allocated. +} DictItemFlags; + +/// Structure representing a Dictionary +struct dictvar_S { + VarLockStatus dv_lock; ///< Whole dictionary lock status. + ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if + ///< dictionary represents a scope (i.e. g:, l: …). + int dv_refcount; ///< Reference count. + int dv_copyID; ///< ID used when recursivery traversing a value. + hashtab_T dv_hashtab; ///< Hashtab containing all items. + dict_T *dv_copydict; ///< Copied dict used by deepcopy(). + dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. + dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. + QUEUE watchers; ///< Dictionary key watchers set by user code. + + LuaRef lua_table_ref; +}; + +/// Structure to hold info about a Blob +struct blobvar_S { + garray_T bv_ga; ///< Growarray with the data. + int bv_refcount; ///< Reference count. + VarLockStatus bv_lock; ///< VAR_UNLOCKED, VAR_LOCKED, VAR_FIXED. +}; + +/// Type used for script ID +typedef int scid_T; +/// Format argument for scid_T +#define PRIdSCID "d" + +/// SCript ConteXt (SCTX): identifies a script line. +/// When sourcing a script "sc_lnum" is zero, "sourcing_lnum" is the current +/// line number. When executing a user function "sc_lnum" is the line where the +/// function was defined, "sourcing_lnum" is the line number inside the +/// function. When stored with a function, mapping, option, etc. "sc_lnum" is +/// the line number in the script "sc_sid". +typedef struct { + scid_T sc_sid; ///< script ID + int sc_seq; ///< sourcing sequence number + linenr_T sc_lnum; ///< line number +} sctx_T; + +/// Maximum number of function arguments +enum { MAX_FUNC_ARGS = 20, }; +/// Short variable name length +enum { VAR_SHORT_LEN = 20, }; +/// Number of fixed variables used for arguments +enum { FIXVAR_CNT = 12, }; + +/// Structure to hold info for a function that is currently being executed. +typedef struct funccall_S funccall_T; + +struct funccall_S { + ufunc_T *func; ///< Function being called. + int linenr; ///< Next line to be executed. + int returned; ///< ":return" used. + /// Fixed variables for arguments. + TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT]; + dict_T l_vars; ///< l: local function variables. + ScopeDictDictItem l_vars_var; ///< Variable for l: scope. + dict_T l_avars; ///< a: argument variables. + ScopeDictDictItem l_avars_var; ///< Variable for a: scope. + list_T l_varlist; ///< List for a:000. + listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000. + typval_T *rettv; ///< Return value. + linenr_T breakpoint; ///< Next line with breakpoint or zero. + int dbg_tick; ///< debug_tick when breakpoint was set. + int level; ///< Top nesting level of executed function. + proftime_T prof_child; ///< Time spent in a child. + funccall_T *caller; ///< Calling function or NULL; or next funccal in + ///< list pointed to by previous_funccal. + int fc_refcount; ///< Number of user functions that reference this funccall. + int fc_copyID; ///< CopyID used for garbage collection. + garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func". +}; + +/// Structure to hold info for a user function. +struct ufunc { + int uf_varargs; ///< variable nr of arguments + int uf_flags; + int uf_calls; ///< nr of active calls + bool uf_cleared; ///< func_clear() was already called + garray_T uf_args; ///< arguments + garray_T uf_def_args; ///< default argument expressions + garray_T uf_lines; ///< function lines + int uf_profiling; ///< true when func is being profiled + int uf_prof_initialized; + LuaRef uf_luaref; ///< lua callback, used if (uf_flags & FC_LUAREF) + // Profiling the function as a whole. + int uf_tm_count; ///< nr of calls + proftime_T uf_tm_total; ///< time spent in function + children + proftime_T uf_tm_self; ///< time spent in function itself + proftime_T uf_tm_children; ///< time spent in children this call + // Profiling the function per line. + int *uf_tml_count; ///< nr of times line was executed + proftime_T *uf_tml_total; ///< time spent in a line + children + proftime_T *uf_tml_self; ///< time spent in a line itself + proftime_T uf_tml_start; ///< start time for current line + proftime_T uf_tml_children; ///< time spent in children for this line + proftime_T uf_tml_wait; ///< start wait time for current line + int uf_tml_idx; ///< index of line being timed; -1 if none + int uf_tml_execed; ///< line being timed was executed + sctx_T uf_script_ctx; ///< SCTX where function was defined, + ///< used for s: variables + int uf_refcount; ///< reference count, see func_name_refcount() + funccall_T *uf_scoped; ///< l: local variables for closure + char *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with + ///< "<SNR>" as a string, otherwise NULL + char uf_name[]; ///< Name of function (actual size equals name); + ///< can start with <SNR>123_ + ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR) +}; + +struct partial_S { + int pt_refcount; ///< Reference count. + char *pt_name; ///< Function name; when NULL use pt_func->name. + ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with pt_name. + bool pt_auto; ///< When true the partial was created by using dict.member + ///< in handle_subscript(). + int pt_argc; ///< Number of arguments. + typval_T *pt_argv; ///< Arguments in allocated array. + dict_T *pt_dict; ///< Dict for "self". +}; + +/// Structure used for explicit stack while garbage collecting hash tables +typedef struct ht_stack_S { + hashtab_T *ht; + struct ht_stack_S *prev; +} ht_stack_T; + +/// Structure used for explicit stack while garbage collecting lists +typedef struct list_stack_S { + list_T *list; + struct list_stack_S *prev; +} list_stack_T; + +/// Structure representing one list item, used for sort array. +typedef struct { + listitem_T *item; ///< Sorted list item. + int idx; ///< Sorted list item index. +} ListSortItem; + +typedef int (*ListSorter)(const void *, const void *); + +#ifdef LOG_LIST_ACTIONS +/// List actions log entry +typedef struct { + uintptr_t l; ///< List log entry belongs to. + uintptr_t li1; ///< First list item log entry belongs to, if applicable. + uintptr_t li2; ///< Second list item log entry belongs to, if applicable. + int len; ///< List length when log entry was created. + const char *action; ///< Logged action. +} ListLogEntry; + +typedef struct list_log ListLog; + +/// List actions log +struct list_log { + ListLog *next; ///< Next chunk or NULL. + size_t capacity; ///< Number of entries in current chunk. + size_t size; ///< Current chunk size. + ListLogEntry entries[]; ///< Actual log entries. +}; +#endif + +#endif // NVIM_EVAL_TYPVAL_DEFS_H diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 73b36b8611..6d29286a58 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -250,7 +250,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/func_attr.h" -#include "nvim/lib/kvec.h" +#include "klib/kvec.h" // -V::1063 @@ -266,7 +266,7 @@ static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( const MPConvStack *const mpstack, const int copyID, const MPConvStackValType conv_type, const char *const objname) -REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT + REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; /// Function for checking whether container references itself @@ -301,7 +301,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv, typval_T *const tv, const int copyID, const char *const objname) -REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT; + REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT; /// Convert single value /// @@ -339,7 +339,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( tv_blob_len(tv->vval.v_blob)); break; case VAR_FUNC: - TYPVAL_ENCODE_CONV_FUNC_START(tv, (char_u *)tv->vval.v_string); + TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string); TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0); TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1); TYPVAL_ENCODE_CONV_FUNC_END(tv); @@ -347,7 +347,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( case VAR_PARTIAL: { partial_T *const pt = tv->vval.v_partial; (void)pt; - TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : (char_u *)partial_name(pt))); // -V547 + TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : partial_name(pt))); // -V547 _mp_push(*mpstack, ((MPConvStackVal) { // -V779 .type = kMPConvPartial, .tv = tv, @@ -358,7 +358,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .pt = tv->vval.v_partial, }, }, - })); + })); break; } case VAR_LIST: { @@ -381,7 +381,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .li = tv_list_first(tv->vval.v_list), }, }, - })); + })); TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack)); break; } @@ -459,8 +459,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const listitem_T *const highest_bits_li = ( TV_LIST_ITEM_NEXT(val_list, sign_li)); if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER - || ((highest_bits - = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number) + || ((highest_bits = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number) < 0)) { goto _convert_one_value_regular_dict; } @@ -536,7 +535,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .li = tv_list_first(val_di->di_tv.vval.v_list), }, }, - })); + })); break; } case kMPMap: { @@ -571,7 +570,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .li = tv_list_first(val_list), }, }, - })); + })); break; } case kMPExt: { @@ -581,8 +580,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2 || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type != VAR_NUMBER) - || ((type - = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number) + || ((type = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number) > INT8_MAX) || type < INT8_MIN || (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type @@ -622,7 +620,7 @@ _convert_one_value_regular_dict: {} .todo = tv->vval.v_dict->dv_hashtab.ht_used, }, }, - })); + })); TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict, _mp_last(*mpstack)); break; @@ -640,7 +638,7 @@ typval_encode_stop_converting_one_item: TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE( TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, typval_T *const tv, const char *const objname) -REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT; + REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT; /// Convert the whole typval /// @@ -758,7 +756,7 @@ typval_encode_stop_converting_one_item: .todo = (size_t)pt->pt_argc, }, }, - })); + })); } break; case kMPConvPartialSelf: { @@ -797,7 +795,7 @@ typval_encode_stop_converting_one_item: .todo = dict->dv_hashtab.ht_used, }, }, - })); + })); TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict, _mp_last(mpstack)); } else { diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index ed70ba87ec..2f19144da3 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -10,9 +10,9 @@ #include <stddef.h> #include <string.h> +#include "klib/kvec.h" #include "nvim/eval/typval.h" #include "nvim/func_attr.h" -#include "nvim/lib/kvec.h" /// Type of the stack entry typedef enum { @@ -71,7 +71,7 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack; #define _mp_pop kv_pop #define _mp_last kv_last -static inline size_t tv_strlen(const typval_T *const tv) +static inline size_t tv_strlen(const typval_T *tv) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index c46cb6ba5d..22c5b1954d 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -3,45 +3,52 @@ // User defined function support +#include <assert.h> +#include <ctype.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lauxlib.h" #include "nvim/ascii.h" +#include "nvim/autocmd.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/debugger.h" -#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" -#include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/insexpand.h" +#include "nvim/keycodes.h" #include "nvim/lua/executor.h" +#include "nvim/macros.h" +#include "nvim/mbyte.h" +#include "nvim/memline_defs.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/option_defs.h" #include "nvim/os/input.h" +#include "nvim/path.h" #include "nvim/profile.h" #include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/search.h" +#include "nvim/strings.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" -// flags used in uf_flags -#define FC_ABORT 0x01 // abort function on error -#define FC_RANGE 0x02 // function accepts range -#define FC_DICT 0x04 // Dict function, uses "self" -#define FC_CLOSURE 0x08 // closure, uses outer scope variables -#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0 -#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0 -#define FC_SANDBOX 0x40 // function defined in the sandbox -#define FC_DEAD 0x80 // function kept only for reference to dfunc -#define FC_EXPORT 0x100 // "export def Func()" -#define FC_NOARGS 0x200 // no a: variables in lambda -#define FC_VIM9 0x400 // defined in vim9 script file -#define FC_CFUNC 0x800 // C function extension - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/userfunc.c.generated.h" #endif @@ -62,6 +69,8 @@ static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace static char *e_funcdict = N_("E717: Dictionary entry already exists"); static char *e_funcref = N_("E718: Funcref required"); static char *e_nofunc = N_("E130: Unknown function: %s"); +static char e_no_white_space_allowed_before_str_str[] + = N_("E1068: No white space allowed before '%s': %s"); void func_init(void) { @@ -75,7 +84,7 @@ hashtab_T *func_tbl_get(void) } /// Get function arguments. -static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int *varargs, +static int get_function_args(char **argp, char endchar, garray_T *newargs, int *varargs, garray_T *default_args, bool skip) { bool mustend = false; @@ -85,10 +94,10 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int int i; if (newargs != NULL) { - ga_init(newargs, (int)sizeof(char_u *), 3); + ga_init(newargs, (int)sizeof(char *), 3); } if (default_args != NULL) { - ga_init(default_args, (int)sizeof(char_u *), 3); + ga_init(default_args, (int)sizeof(char *), 3); } if (varargs != NULL) { @@ -97,7 +106,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int // Isolate the arguments: "arg1, arg2, ...)" bool any_default = false; - while (*p != (char)endchar) { + while (*p != endchar) { if (p[0] == '.' && p[1] == '.' && p[2] == '.') { if (varargs != NULL) { *varargs = true; @@ -109,9 +118,9 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int while (ASCII_ISALNUM(*p) || *p == '_') { p++; } - if (arg == p || isdigit(*arg) - || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0) - || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) { + if (arg == p || isdigit((uint8_t)(*arg)) + || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0) + || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) { if (!skip) { semsg(_("E125: Illegal argument: %s"), arg); } @@ -125,7 +134,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int // Check for duplicate argument name. for (i = 0; i < newargs->ga_len; i++) { - if (STRCMP(((char **)(newargs->ga_data))[i], arg) == 0) { + if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) { semsg(_("E853: Duplicate argument name: %s"), arg); xfree(arg); goto err_ret; @@ -142,18 +151,18 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int any_default = true; p = skipwhite(p) + 1; p = skipwhite(p); - char_u *expr = (char_u *)p; + char *expr = p; if (eval1(&p, &rettv, false) != FAIL) { ga_grow(default_args, 1); // trim trailing whitespace - while (p > (char *)expr && ascii_iswhite(p[-1])) { + while (p > expr && ascii_iswhite(p[-1])) { p--; } c = (char_u)(*p); *p = NUL; - expr = vim_strsave(expr); - ((char **)(default_args->ga_data))[default_args->ga_len] = (char *)expr; + expr = xstrdup(expr); + ((char **)(default_args->ga_data))[default_args->ga_len] = expr; default_args->ga_len++; *p = (char)c; } else { @@ -163,6 +172,15 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int emsg(_("E989: Non-default argument follows default argument")); mustend = true; } + + if (ascii_iswhite(*p) && *skipwhite(p) == ',') { + // Be tolerant when skipping + if (!skip) { + semsg(_(e_no_white_space_allowed_before_str_str), ",", p); + goto err_ret; + } + p = skipwhite(p); + } if (*p == ',') { p++; } else { @@ -170,14 +188,14 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int } } p = skipwhite(p); - if (mustend && *p != (char)endchar) { + if (mustend && *p != endchar) { if (!skip) { semsg(_(e_invarg2), *argp); } break; } } - if (*p != (char)endchar) { + if (*p != endchar) { goto err_ret; } p++; // skip "endchar" @@ -211,21 +229,21 @@ static void register_closure(ufunc_T *fp) } /// @return a name for a lambda. Returned in static memory. -char_u *get_lambda_name(void) +char *get_lambda_name(void) { - static char_u name[30]; + static char name[30]; static int lambda_no = 0; - snprintf((char *)name, sizeof(name), "<lambda>%d", ++lambda_no); + snprintf(name, sizeof(name), "<lambda>%d", ++lambda_no); return name; } -static void set_ufunc_name(ufunc_T *fp, char_u *name) +static void set_ufunc_name(ufunc_T *fp, char *name) { STRCPY(fp->uf_name, name); - if (name[0] == K_SPECIAL) { - fp->uf_name_exp = xmalloc(STRLEN(name) + 3); + if ((uint8_t)name[0] == K_SPECIAL) { + fp->uf_name_exp = xmalloc(strlen(name) + 3); STRCPY(fp->uf_name_exp, "<SNR>"); STRCAT(fp->uf_name_exp, fp->uf_name + 3); } @@ -242,13 +260,13 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) partial_T *pt = NULL; int varargs; int ret; - char_u *start = (char_u *)skipwhite(*arg + 1); - char_u *s, *e; + char *start = skipwhite(*arg + 1); + char *s, *e; bool *old_eval_lavars = eval_lavars_used; bool eval_lavars = false; // First, check if this is a lambda expression. "->" must exists. - ret = get_function_args((char **)&start, '-', NULL, NULL, NULL, true); + ret = get_function_args(&start, '-', NULL, NULL, NULL, true); if (ret == FAIL || *start != '>') { return NOTDONE; } @@ -272,38 +290,39 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) // Get the start and the end of the expression. *arg = skipwhite((*arg) + 1); - s = (char_u *)(*arg); + s = *arg; ret = skip_expr(arg); if (ret == FAIL) { goto errret; } - e = (char_u *)(*arg); + e = *arg; *arg = skipwhite(*arg); if (**arg != '}') { + semsg(_("E451: Expected }: %s"), *arg); goto errret; } (*arg)++; if (evaluate) { int flags = 0; - char_u *p; + char *p; garray_T newlines; - char_u *name = get_lambda_name(); + char *name = get_lambda_name(); - fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); + fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1); pt = xcalloc(1, sizeof(partial_T)); - ga_init(&newlines, (int)sizeof(char_u *), 1); + ga_init(&newlines, (int)sizeof(char *), 1); ga_grow(&newlines, 1); // Add "return " before the expression. size_t len = (size_t)(7 + e - s + 1); - p = (char_u *)xmalloc(len); - ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p; + p = xmalloc(len); + ((char **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); - STRLCPY(p + 7, s, e - s + 1); - if (strstr((char *)p + 7, "a:") == NULL) { + xstrlcpy(p + 7, s, (size_t)(e - s) + 1); + if (strstr(p + 7, "a:") == NULL) { // No a: variables are used for sure. flags |= FC_NOARGS; } @@ -312,7 +331,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) set_ufunc_name(fp, name); hash_add(&func_hashtab, UF2HIKEY(fp)); fp->uf_args = newargs; - ga_init(&fp->uf_def_args, (int)sizeof(char_u *), 1); + ga_init(&fp->uf_def_args, (int)sizeof(char *), 1); fp->uf_lines = newlines; if (current_funccal != NULL && eval_lavars) { flags |= FC_CLOSURE; @@ -365,7 +384,7 @@ errret: /// was not found. /// /// @return name of the function. -char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload) +char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload) FUNC_ATTR_NONNULL_ARG(1, 2) { if (partialp != NULL) { @@ -376,10 +395,10 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp, if (v != NULL && v->di_tv.v_type == VAR_FUNC) { if (v->di_tv.vval.v_string == NULL) { // just in case *lenp = 0; - return (char_u *)""; + return ""; } - *lenp = (int)STRLEN(v->di_tv.vval.v_string); - return (char_u *)v->di_tv.vval.v_string; + *lenp = (int)strlen(v->di_tv.vval.v_string); + return v->di_tv.vval.v_string; } if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) { @@ -387,31 +406,31 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp, if (pt == NULL) { // just in case *lenp = 0; - return (char_u *)""; + return ""; } if (partialp != NULL) { *partialp = pt; } - char_u *s = (char_u *)partial_name(pt); - *lenp = (int)STRLEN(s); + char *s = partial_name(pt); + *lenp = (int)strlen(s); return s; } - return (char_u *)name; + return (char *)name; } /// Give an error message with a function name. Handle <SNR> things. /// /// @param ermsg must be passed without translation (use N_() instead of _()). /// @param name function name -void emsg_funcname(char *ermsg, const char_u *name) +void emsg_funcname(char *ermsg, const char *name) { - char_u *p; + char *p; - if (*name == K_SPECIAL) { - p = concat_str((char_u *)"<SNR>", name + 3); + if ((uint8_t)(*name) == K_SPECIAL) { + p = concat_str("<SNR>", name + 3); } else { - p = (char_u *)name; + p = (char *)name; } semsg(_(ermsg), p); @@ -429,7 +448,7 @@ void emsg_funcname(char *ermsg, const char_u *name) /// @param funcexe various values /// /// @return OK or FAIL. -int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe) +int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe) { char *argp; int ret = OK; @@ -439,12 +458,12 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex // Get the arguments. argp = *arg; while (argcount < MAX_FUNC_ARGS - - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) { + - (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc)) { argp = skipwhite(argp + 1); // skip the '(' or ',' if (*argp == ')' || *argp == ',' || *argp == NUL) { break; } - if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) { + if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) { ret = FAIL; break; } @@ -463,7 +482,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex int i = 0; if (get_vim_var_nr(VV_TESTING)) { - // Prepare for calling garbagecollect_for_testing(), need to know + // Prepare for calling test_garbagecollect_now(), need to know // what variables are used on the call stack. if (funcargs.ga_itemsize == 0) { ga_init(&funcargs, (int)sizeof(typval_T *), 50); @@ -473,7 +492,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i]; } } - ret = call_func((char *)name, len, rettv, argcount, argvars, funcexe); + ret = call_func(name, len, rettv, argcount, argvars, funcexe); funcargs.ga_len -= i; } else if (!aborting()) { @@ -525,48 +544,47 @@ static inline bool eval_fname_sid(const char *const name) /// /// @return transformed name: either `fname_buf` or a pointer to an allocated /// memory. -static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf, - char_u **const tofree, int *const error) +static char *fname_trans_sid(const char *const name, char *const fname_buf, char **const tofree, + int *const error) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *fname; - const int llen = eval_fname_script((const char *)name); - if (llen > 0) { - fname_buf[0] = K_SPECIAL; - fname_buf[1] = KS_EXTRA; - fname_buf[2] = KE_SNR; - int i = 3; - if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:" - if (current_sctx.sc_sid <= 0) { - *error = ERROR_SCRIPT; - } else { - snprintf((char *)fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_", - (int64_t)current_sctx.sc_sid); - i = (int)STRLEN(fname_buf); - } - } - if ((size_t)i + STRLEN(name + llen) < FLEN_FIXED) { - STRCPY(fname_buf + i, name + llen); - fname = fname_buf; + const int llen = eval_fname_script(name); + if (llen == 0) { + return (char *)name; // no prefix + } + + fname_buf[0] = (char)K_SPECIAL; + fname_buf[1] = (char)KS_EXTRA; + fname_buf[2] = KE_SNR; + int i = 3; + if (eval_fname_sid(name)) { // "<SID>" or "s:" + if (current_sctx.sc_sid <= 0) { + *error = FCERR_SCRIPT; } else { - fname = xmalloc((size_t)i + STRLEN(name + llen) + 1); - *tofree = fname; - memmove(fname, fname_buf, (size_t)i); - STRCPY(fname + i, name + llen); + snprintf(fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_", + (int64_t)current_sctx.sc_sid); + i = (int)strlen(fname_buf); } + } + char *fname; + if ((size_t)i + strlen(name + llen) < FLEN_FIXED) { + STRCPY(fname_buf + i, name + llen); + fname = fname_buf; } else { - fname = (char_u *)name; + fname = xmalloc((size_t)i + strlen(name + llen) + 1); + *tofree = fname; + memmove(fname, fname_buf, (size_t)i); + STRCPY(fname + i, name + llen); } - return fname; } /// Find a function by name, return pointer to it in ufuncs. /// /// @return NULL for unknown function. -ufunc_T *find_func(const char_u *name) +ufunc_T *find_func(const char *name) { - hashitem_T *hi = hash_find(&func_hashtab, (char *)name); + hashitem_T *hi = hash_find(&func_hashtab, name); if (!HASHITEM_EMPTY(hi)) { return HI2UF(hi); } @@ -576,9 +594,9 @@ ufunc_T *find_func(const char_u *name) /// Copy the function name of "fp" to buffer "buf". /// "buf" must be able to hold the function name plus three bytes. /// Takes care of script-local function names. -static void cat_func_name(char_u *buf, ufunc_T *fp) +static void cat_func_name(char *buf, ufunc_T *fp) { - if (fp->uf_name[0] == K_SPECIAL) { + if ((uint8_t)fp->uf_name[0] == K_SPECIAL) { STRCPY(buf, "<SNR>"); STRCAT(buf, fp->uf_name + 3); } else { @@ -593,7 +611,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) STRCPY(v->di_key, name); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - tv_dict_add(dp, v); + hash_add(&dp->dv_hashtab, (char *)v->di_key); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; @@ -758,9 +776,9 @@ static void func_clear_items(ufunc_T *fp) ga_clear_strings(&(fp->uf_lines)); XFREE_CLEAR(fp->uf_name_exp); - if (fp->uf_cb_free != NULL) { - fp->uf_cb_free(fp->uf_cb_state); - fp->uf_cb_free = NULL; + if (fp->uf_flags & FC_LUAREF) { + api_free_luaref(fp->uf_luaref); + fp->uf_luaref = LUA_NOREF; } XFREE_CLEAR(fp->uf_tml_count); @@ -828,7 +846,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett int fixvar_idx = 0; // index in fixvar[] int ai; bool islambda = false; - char_u numbuf[NUMBUFLEN]; + char numbuf[NUMBUFLEN]; char *name; typval_T *tv_to_free[MAX_FUNC_ARGS]; int tv_to_free_len = 0; @@ -852,7 +870,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett saveRedobuff(&save_redo); did_save_redo = true; } - ++fp->uf_calls; + fp->uf_calls++; // check for CTRL-C hit line_breakcheck(); // prepare the funccall_T structure @@ -863,14 +881,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett fc->rettv = rettv; fc->level = ex_nesting_level; // Check if this function has a breakpoint. - fc->breakpoint = dbg_find_breakpoint(false, fp->uf_name, (linenr_T)0); + fc->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, (linenr_T)0); fc->dbg_tick = debug_tick; // Set up fields for closure. ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1); func_ptr_ref(fp); - if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) { + if (strncmp(fp->uf_name, "<lambda>", 8) == 0) { islambda = true; } @@ -889,11 +907,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett STRCPY(name, "self"); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - tv_dict_add(&fc->l_vars, v); + hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = VAR_UNLOCKED; v->di_tv.vval.v_dict = selfdict; - ++selfdict->dv_refcount; + selfdict->dv_refcount++; } // Init a: variables, unless none found (in lambda). @@ -915,7 +933,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett STRCPY(name, "000"); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - tv_dict_add(&fc->l_avars, v); + hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; @@ -967,14 +985,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett break; } // "..." argument a:1, a:2, etc. - snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1); - name = (char *)numbuf; + snprintf(numbuf, sizeof(numbuf), "%d", ai + 1); + name = numbuf; } - if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) { + if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) { v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { - v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); + v = xmalloc(sizeof(dictitem_T) + strlen(name)); v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; } STRCPY(v->di_key, name); @@ -993,16 +1011,16 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. tv_copy(&v->di_tv, &v->di_tv); - tv_dict_add(&fc->l_vars, v); + hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key); } else { - tv_dict_add(&fc->l_avars, v); + hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key); } if (ai >= 0 && ai < MAX_FUNC_ARGS) { listitem_T *li = &fc->l_listitems[ai]; *TV_LIST_ITEM_TV(li) = argvars[i]; - TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED; + TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED; tv_list_append(&fc->l_varlist, li); } } @@ -1058,7 +1076,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett bool func_not_yet_profiling_but_should = do_profiling_yes - && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL); + && !fp->uf_profiling && has_profiling(false, (char *)fp->uf_name, NULL); if (func_not_yet_profiling_but_should) { started_profiling = true; @@ -1071,7 +1089,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett || (fc->caller != NULL && fc->caller->func->uf_profiling)); if (func_or_func_caller_profiling) { - ++fp->uf_tm_count; + fp->uf_tm_count++; call_start = profile_start(); fp->uf_tm_children = profile_zero(); } @@ -1083,7 +1101,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett const sctx_T save_current_sctx = current_sctx; current_sctx = fp->uf_script_ctx; save_did_emsg = did_emsg; - did_emsg = FALSE; + did_emsg = false; if (default_arg_err && (fp->uf_flags & FC_ABORT)) { did_emsg = true; @@ -1208,9 +1226,37 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett /// For the first we only count the name stored in func_hashtab as a reference, /// using function() does not count as a reference, because the function is /// looked up by name. -static bool func_name_refcount(char_u *name) +static bool func_name_refcount(const char *name) +{ + return isdigit((uint8_t)(*name)) || *name == '<'; +} + +/// Call a user function after checking the arguments. +static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, + funcexe_T *funcexe, dict_T *selfdict) + FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5) { - return isdigit(*name) || *name == '<'; + if (fp->uf_flags & FC_LUAREF) { + return typval_exec_lua_callable(fp->uf_luaref, argcount, argvars, rettv); + } + + if ((fp->uf_flags & FC_RANGE) && funcexe->fe_doesrange != NULL) { + *funcexe->fe_doesrange = true; + } + int error; + if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) { + error = FCERR_TOOFEW; + } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) { + error = FCERR_TOOMANY; + } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) { + error = FCERR_DICT; + } else { + // Call the user function. + call_user_func(fp, argcount, argvars, rettv, funcexe->fe_firstline, funcexe->fe_lastline, + (fp->uf_flags & FC_DICT) ? selfdict : NULL); + error = FCERR_NONE; + } + return error; } static funccal_entry_T *funccal_stack = NULL; @@ -1252,7 +1298,7 @@ void free_all_functions(void) ufunc_T *fp; uint64_t skipped = 0; uint64_t todo = 1; - uint64_t used; + int changed; // Clean up the current_funccal chain and the funccal stack. while (current_funccal != NULL) { @@ -1276,9 +1322,9 @@ void free_all_functions(void) if (func_name_refcount(fp->uf_name)) { skipped++; } else { - used = func_hashtab.ht_used; + changed = func_hashtab.ht_changed; func_clear(fp, true); - if (used != func_hashtab.ht_used) { + if (changed != func_hashtab.ht_changed) { skipped = 0; break; } @@ -1322,10 +1368,10 @@ void free_all_functions(void) /// @param[in] len length of "name", or -1 for NUL terminated. /// /// @return true if "name" looks like a builtin function name: starts with a -/// lower case letter and doesn't contain AUTOLOAD_CHAR. +/// lower case letter and doesn't contain AUTOLOAD_CHAR or ':'. static bool builtin_function(const char *name, int len) { - if (!ASCII_ISLOWER(name[0])) { + if (!ASCII_ISLOWER(name[0]) || name[1] == ':') { return false; } @@ -1336,7 +1382,7 @@ static bool builtin_function(const char *name, int len) return p == NULL; } -int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv) +int func_call(char *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv) { typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; @@ -1353,12 +1399,12 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict }); funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = true; - funcexe.partial = partial; - funcexe.selfdict = selfdict; - r = call_func((char *)name, -1, rettv, argc, argv, &funcexe); + funcexe.fe_firstline = curwin->w_cursor.lnum; + funcexe.fe_lastline = curwin->w_cursor.lnum; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; + funcexe.fe_selfdict = selfdict; + r = call_func(name, -1, rettv, argc, argv, &funcexe); func_call_skip_call: // Free the arguments. @@ -1369,35 +1415,50 @@ func_call_skip_call: return r; } +/// call the 'callback' function and return the result as a number. +/// Returns -2 when calling the function fails. Uses argv[0] to argv[argc - 1] +/// for the function arguments. argv[argc] should have type VAR_UNKNOWN. +/// +/// @param argcount number of "argvars" +/// @param argvars vars for arguments, must have "argcount" PLUS ONE elements! +varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argvars) +{ + typval_T rettv; + if (!callback_call(callback, argcount, argvars, &rettv)) { + return -2; + } + + varnumber_T retval = tv_get_number_chk(&rettv, NULL); + tv_clear(&rettv); + return retval; +} + /// Give an error message for the result of a function. /// Nothing if "error" is FCERR_NONE. -static void user_func_error(int error, const char_u *name) +static void user_func_error(int error, const char *name) FUNC_ATTR_NONNULL_ALL { switch (error) { - case ERROR_UNKNOWN: + case FCERR_UNKNOWN: emsg_funcname(N_("E117: Unknown function: %s"), name); break; - case ERROR_NOTMETHOD: + case FCERR_NOTMETHOD: emsg_funcname(N_("E276: Cannot use function as a method: %s"), name); break; - case ERROR_DELETED: + case FCERR_DELETED: emsg_funcname(N_("E933: Function was deleted: %s"), name); break; - case ERROR_TOOMANY: + case FCERR_TOOMANY: emsg_funcname(_(e_toomanyarg), name); break; - case ERROR_TOOFEW: - emsg_funcname(N_("E119: Not enough arguments for function: %s"), - name); + case FCERR_TOOFEW: + emsg_funcname(N_("E119: Not enough arguments for function: %s"), name); break; - case ERROR_SCRIPT: - emsg_funcname(N_("E120: Using <SID> not in a script context: %s"), - name); + case FCERR_SCRIPT: + emsg_funcname(N_("E120: Using <SID> not in a script context: %s"), name); break; - case ERROR_DICT: - emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), - name); + case FCERR_DICT: + emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), name); break; } } @@ -1435,27 +1496,27 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6) { int ret = FAIL; - int error = ERROR_NONE; + int error = FCERR_NONE; ufunc_T *fp = NULL; - char_u fname_buf[FLEN_FIXED + 1]; - char_u *tofree = NULL; - char_u *fname = NULL; - char_u *name = NULL; + char fname_buf[FLEN_FIXED + 1]; + char *tofree = NULL; + char *fname = NULL; + char *name = NULL; int argcount = argcount_in; typval_T *argvars = argvars_in; - dict_T *selfdict = funcexe->selfdict; + dict_T *selfdict = funcexe->fe_selfdict; typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or - // "funcexe->basetv" is not NULL + // "funcexe->fe_basetv" is not NULL int argv_clear = 0; int argv_base = 0; - partial_T *partial = funcexe->partial; + partial_T *partial = funcexe->fe_partial; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) // even when call_func() returns FAIL. rettv->v_type = VAR_UNKNOWN; if (len <= 0) { - len = (int)STRLEN(funcname); + len = (int)strlen(funcname); } if (partial != NULL) { fp = partial->pt_func; @@ -1463,12 +1524,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t if (fp == NULL) { // Make a copy of the name, if it comes from a funcref variable it could // be changed or deleted in the called function. - name = vim_strnsave((char_u *)funcname, (size_t)len); + name = xstrnsave(funcname, (size_t)len); fname = fname_trans_sid(name, fname_buf, &tofree, &error); } - if (funcexe->doesrange != NULL) { - *funcexe->doesrange = false; + if (funcexe->fe_doesrange != NULL) { + *funcexe->fe_doesrange = false; } if (partial != NULL) { @@ -1478,10 +1539,10 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) { selfdict = partial->pt_dict; } - if (error == ERROR_NONE && partial->pt_argc > 0) { + if (error == FCERR_NONE && partial->pt_argc > 0) { for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) { if (argv_clear + argcount_in >= MAX_FUNC_ARGS) { - error = ERROR_TOOMANY; + error = FCERR_TOOMANY; goto theend; } tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]); @@ -1494,8 +1555,8 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t } } - if (error == ERROR_NONE && funcexe->evaluate) { - char_u *rfname = fname; + if (error == FCERR_NONE && funcexe->fe_evaluate) { + char *rfname = fname; // Ignore "g:" before a function name. if (fp == NULL && fname[0] == 'g' && fname[1] == ':') { @@ -1504,12 +1565,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t rettv->v_type = VAR_NUMBER; // default rettv is number zero rettv->vval.v_number = 0; - error = ERROR_UNKNOWN; + error = FCERR_UNKNOWN; if (is_luafunc(partial)) { if (len > 0) { - error = ERROR_NONE; - argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); + error = FCERR_NONE; + argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base); nlua_typval_call(funcname, (size_t)len, argvars, argcount, rettv); } else { // v:lua was called directly; show its name in the emsg @@ -1524,71 +1585,50 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t // Trigger FuncUndefined event, may load the function. if (fp == NULL - && apply_autocmds(EVENT_FUNCUNDEFINED, (char *)rfname, (char *)rfname, true, NULL) + && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, true, NULL) && !aborting()) { // executed an autocommand, search for the function again fp = find_func(rfname); } // Try loading a package. - if (fp == NULL && script_autoload((const char *)rfname, STRLEN(rfname), + if (fp == NULL && script_autoload((const char *)rfname, strlen(rfname), true) && !aborting()) { // Loaded a package, search for the function again. fp = find_func(rfname); } if (fp != NULL && (fp->uf_flags & FC_DELETED)) { - error = ERROR_DELETED; - } else if (fp != NULL && (fp->uf_flags & FC_CFUNC)) { - cfunc_T cb = fp->uf_cb; - error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state); + error = FCERR_DELETED; } else if (fp != NULL) { - if (funcexe->argv_func != NULL) { + if (funcexe->fe_argv_func != NULL) { // postponed filling in the arguments, do it now - argcount = funcexe->argv_func(argcount, argvars, argv_clear, - fp->uf_args.ga_len); + argcount = funcexe->fe_argv_func(argcount, argvars, argv_clear, fp); } - argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); + argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base); - if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { - *funcexe->doesrange = true; - } - if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) { - error = ERROR_TOOFEW; - } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) { - error = ERROR_TOOMANY; - } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) { - error = ERROR_DICT; - } else { - // Call the user function. - call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, - funcexe->lastline, - (fp->uf_flags & FC_DICT) ? selfdict : NULL); - error = ERROR_NONE; - } + error = call_user_func_check(fp, argcount, argvars, rettv, funcexe, selfdict); } - } else if (funcexe->basetv != NULL) { + } else if (funcexe->fe_basetv != NULL) { // expr->method(): Find the method name in the table, call its // implementation with the base as one of the arguments. error = call_internal_method(fname, argcount, argvars, rettv, - funcexe->basetv); + funcexe->fe_basetv); } else { // Find the function name in the table, call its implementation. error = call_internal_func(fname, argcount, argvars, rettv); } - /* - * The function call (or "FuncUndefined" autocommand sequence) might - * have been aborted by an error, an interrupt, or an explicitly thrown - * exception that has not been caught so far. This situation can be - * tested for by calling aborting(). For an error in an internal - * function or for the "E132" error in call_user_func(), however, the - * throw point at which the "force_abort" flag (temporarily reset by - * emsg()) is normally updated has not been reached yet. We need to - * update that flag first to make aborting() reliable. - */ + // The function call (or "FuncUndefined" autocommand sequence) might + // have been aborted by an error, an interrupt, or an explicitly thrown + // exception that has not been caught so far. This situation can be + // tested for by calling aborting(). For an error in an internal + // function or for the "E132" error in call_user_func(), however, the + // throw point at which the "force_abort" flag (temporarily reset by + // emsg()) is normally updated has not been reached yet. We need to + // update that flag first to make aborting() reliable. update_force_abort(); } - if (error == ERROR_NONE) { + if (error == FCERR_NONE) { ret = OK; } @@ -1596,7 +1636,7 @@ theend: // Report an error unless the argument evaluation or function call has been // cancelled due to an aborting error, an interrupt, or an exception. if (!aborting()) { - user_func_error(error, (name != NULL) ? name : (char_u *)funcname); + user_func_error(error, (name != NULL) ? name : funcname); } // clear the copies made from the partial @@ -1610,6 +1650,11 @@ theend: return ret; } +char *printable_func_name(ufunc_T *fp) +{ + return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name; +} + /// List the head of the function: "name(arg1, arg2)". /// /// @param[in] fp Function pointer. @@ -1679,12 +1724,12 @@ static void list_func_head(ufunc_T *fp, int indent, bool force) /// @param partial return: partial of a FuncRef /// /// @return the function name in allocated memory, or NULL for failure. -char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial) +char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial) FUNC_ATTR_NONNULL_ARG(1) { - char_u *name = NULL; - const char_u *start; - const char_u *end; + char *name = NULL; + const char *start; + const char *end; int lead; int len; lval_T lv; @@ -1692,7 +1737,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa if (fdp != NULL) { CLEAR_POINTER(fdp); } - start = (char_u *)(*pp); + start = *pp; // Check for hard coded <SNR>: already translated function ID (from a user // command). @@ -1700,19 +1745,19 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa && (*pp)[2] == KE_SNR) { *pp += 3; len = get_id_len((const char **)pp) + 3; - return (char_u *)xmemdupz(start, (size_t)len); + return xmemdupz(start, (size_t)len); } // A name starting with "<SID>" or "<SNR>" is local to a script. But // don't skip over "s:", get_lval() needs it for "s:dict.func". - lead = eval_fname_script((const char *)start); + lead = eval_fname_script(start); if (lead > 2) { start += lead; } // Note that TFN_ flags use the same values as GLV_ flags. - end = (char_u *)get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY, - lead > 2 ? 0 : FNE_CHECK_START); + end = get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY, + lead > 2 ? 0 : FNE_CHECK_START); if (end == start) { if (!skip) { emsg(_("E129: Function name required")); @@ -1720,11 +1765,9 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa goto theend; } if (end == NULL || (lv.ll_tv != NULL && (lead > 2 || lv.ll_range))) { - /* - * Report an invalid expression in braces, unless the expression - * evaluation has been cancelled due to an aborting error, an - * interrupt, or an exception. - */ + // Report an invalid expression in braces, unless the expression + // evaluation has been cancelled due to an aborting error, an + // interrupt, or an exception. if (!aborting()) { if (end != NULL) { semsg(_(e_invarg2), start); @@ -1738,17 +1781,17 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa if (lv.ll_tv != NULL) { if (fdp != NULL) { fdp->fd_dict = lv.ll_dict; - fdp->fd_newkey = (char_u *)lv.ll_newkey; + fdp->fd_newkey = lv.ll_newkey; lv.ll_newkey = NULL; fdp->fd_di = lv.ll_di; } if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { - name = vim_strsave((char_u *)lv.ll_tv->vval.v_string); + name = xstrdup(lv.ll_tv->vval.v_string); *pp = (char *)end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { if (is_luafunc(lv.ll_tv->vval.v_partial) && *end == '.') { - len = check_luafunc_name((const char *)end + 1, true); + len = check_luafunc_name(end + 1, true); if (len == 0) { semsg(e_invexpr2, "v:lua"); goto theend; @@ -1757,7 +1800,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa memcpy(name, end + 1, (size_t)len); *pp = (char *)end + 1 + len; } else { - name = vim_strsave((char_u *)partial_name(lv.ll_tv->vval.v_partial)); + name = xstrdup(partial_name(lv.ll_tv->vval.v_partial)); *pp = (char *)end; } if (partial != NULL) { @@ -1791,22 +1834,21 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa name = NULL; } } else if (!(flags & TFN_NO_DEREF)) { - len = (int)(end - (char_u *)(*pp)); - name = deref_func_name((const char *)(*pp), &len, partial, - flags & TFN_NO_AUTOLOAD); - if (name == (char_u *)(*pp)) { + len = (int)(end - *pp); + name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD); + if (name == *pp) { name = NULL; } } if (name != NULL) { - name = vim_strsave(name); + name = xstrdup(name); *pp = (char *)end; - if (STRNCMP(name, "<SNR>", 5) == 0) { + if (strncmp(name, "<SNR>", 5) == 0) { // Change "<SNR>" to the byte sequence. - name[0] = K_SPECIAL; - name[1] = KS_EXTRA; + name[0] = (char)K_SPECIAL; + name[1] = (char)KS_EXTRA; name[2] = KE_SNR; - memmove(name + 3, name + 5, strlen((char *)name + 5) + 1); + memmove(name + 3, name + 5, strlen(name + 5) + 1); } goto theend; } @@ -1828,7 +1870,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa lv.ll_name += 2; lv.ll_name_len -= 2; } - len = (int)((const char *)end - lv.ll_name); + len = (int)(end - lv.ll_name); } size_t sid_buf_len = 0; @@ -1859,7 +1901,7 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa } if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) { - char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len); + char *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len); if (cp != NULL && cp < end) { semsg(_("E884: Function name cannot contain a colon: %s"), start); @@ -1869,8 +1911,8 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa name = xmalloc((size_t)len + (size_t)lead + 1); if (!skip && lead > 0) { - name[0] = K_SPECIAL; - name[1] = KS_EXTRA; + name[0] = (char)K_SPECIAL; + name[1] = (char)KS_EXTRA; name[2] = KE_SNR; if (sid_buf_len > 0) { // If it's "<SID>" memcpy(name + 3, sid_buf, sid_buf_len); @@ -1885,18 +1927,105 @@ theend: return name; } +/// If the "funcname" starts with "s:" or "<SID>", then expands it to the +/// current script ID and returns the expanded function name. The caller should +/// free the returned name. If not called from a script context or the function +/// name doesn't start with these prefixes, then returns NULL. +/// This doesn't check whether the script-local function exists or not. +char *get_scriptlocal_funcname(char *funcname) +{ + if (funcname == NULL) { + return NULL; + } + + if (strncmp(funcname, "s:", 2) != 0 + && strncmp(funcname, "<SID>", 5) != 0) { + // The function name is not a script-local function name + return NULL; + } + + if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) { + emsg(_(e_usingsid)); + return NULL; + } + + char sid_buf[25]; + // Expand s: and <SID> prefix into <SNR>nr_<name> + snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", + (int64_t)current_sctx.sc_sid); + const int off = *funcname == 's' ? 2 : 5; + char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1); + STRCPY(newname, sid_buf); + STRCAT(newname, funcname + off); + + return newname; +} + +/// Call trans_function_name(), except that a lambda is returned as-is. +/// Returns the name in allocated memory. +char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi) +{ + char *p = *name; + char *saved; + + if (strncmp(p, "<lambda>", 8) == 0) { + p += 8; + (void)getdigits(&p, false, 0); + saved = xstrndup(*name, (size_t)(p - *name)); + if (fudi != NULL) { + CLEAR_POINTER(fudi); + } + } else { + saved = trans_function_name(&p, skip, flags, fudi, NULL); + } + *name = p; + return saved; +} + +#define MAX_FUNC_NESTING 50 + +/// List functions. +/// +/// @param regmatch When NULL, all of them. +/// Otherwise functions matching "regmatch". +static void list_functions(regmatch_T *regmatch) +{ + const int changed = func_hashtab.ht_changed; + size_t todo = func_hashtab.ht_used; + const hashitem_T *const ht_array = func_hashtab.ht_array; + + for (const hashitem_T *hi = ht_array; todo > 0 && !got_int; hi++) { + if (!HASHITEM_EMPTY(hi)) { + ufunc_T *fp = HI2UF(hi); + todo--; + if ((fp->uf_flags & FC_DEAD) == 0 + && (regmatch == NULL + ? (!message_filtered((char *)fp->uf_name) + && !func_name_refcount(fp->uf_name)) + : (!isdigit((uint8_t)(*fp->uf_name)) + && vim_regexec(regmatch, (char *)fp->uf_name, 0)))) { + list_func_head(fp, false, false); + if (changed != func_hashtab.ht_changed) { + emsg(_("E454: function list was modified")); + return; + } + } + } + } +} + /// ":function" void ex_function(exarg_T *eap) { - char_u *theline; - char_u *line_to_free = NULL; - char_u c; + char *theline; + char *line_to_free = NULL; + char c; int saved_did_emsg; bool saved_wait_return = need_wait_return; - char_u *name = NULL; - char_u *p; - char_u *arg; - char_u *line_arg = NULL; + char *name = NULL; + char *p; + char *arg; + char *line_arg = NULL; garray_T newargs; garray_T default_args; garray_T newlines; @@ -1911,44 +2040,27 @@ void ex_function(exarg_T *eap) static int func_nr = 0; // number for nameless function int paren; hashtab_T *ht; - int todo; hashitem_T *hi; linenr_T sourcing_lnum_off; linenr_T sourcing_lnum_top; bool is_heredoc = false; - char_u *skip_until = NULL; - char_u *heredoc_trimmed = NULL; + char *skip_until = NULL; + char *heredoc_trimmed = NULL; bool show_block = false; bool do_concat = true; - /* - * ":function" without argument: list functions. - */ + // ":function" without argument: list functions. if (ends_excmd(*eap->arg)) { if (!eap->skip) { - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - fp = HI2UF(hi); - if (message_filtered(fp->uf_name)) { - continue; - } - if (!func_name_refcount(fp->uf_name)) { - list_func_head(fp, false, false); - } - } - } + list_functions(NULL); } - eap->nextcmd = (char *)check_nextcmd((char_u *)eap->arg); + eap->nextcmd = check_nextcmd(eap->arg); return; } - /* - * ":function /pat": list functions matching pattern. - */ + // ":function /pat": list functions matching pattern. if (*eap->arg == '/') { - p = skip_regexp((char_u *)eap->arg + 1, '/', true, NULL); + p = skip_regexp(eap->arg + 1, '/', true); if (!eap->skip) { regmatch_T regmatch; @@ -1958,25 +2070,14 @@ void ex_function(exarg_T *eap) *p = c; if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; - - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - fp = HI2UF(hi); - if (!isdigit(*fp->uf_name) - && vim_regexec(®match, (char *)fp->uf_name, 0)) { - list_func_head(fp, false, false); - } - } - } + list_functions(®match); vim_regfree(regmatch.regprog); } } if (*p == '/') { p++; } - eap->nextcmd = (char *)check_nextcmd(p); + eap->nextcmd = check_nextcmd(p); return; } @@ -1994,30 +2095,27 @@ void ex_function(exarg_T *eap) // "fudi.fd_di" set, "fudi.fd_newkey" == NULL // s:func script-local function name // g:func global function name, same as "func" - p = (char_u *)eap->arg; - name = trans_function_name((char **)&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL); - paren = (vim_strchr((char *)p, '(') != NULL); + p = eap->arg; + name = save_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi); + paren = (vim_strchr(p, '(') != NULL); if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) { - /* - * Return on an invalid expression in braces, unless the expression - * evaluation has been cancelled due to an aborting error, an - * interrupt, or an exception. - */ + // Return on an invalid expression in braces, unless the expression + // evaluation has been cancelled due to an aborting error, an + // interrupt, or an exception. if (!aborting()) { if (fudi.fd_newkey != NULL) { semsg(_(e_dictkey), fudi.fd_newkey); } xfree(fudi.fd_newkey); return; - } else { - eap->skip = TRUE; } + eap->skip = true; } // An error in a function call during evaluation of an expression in magic // braces should not cause the function not to be defined. saved_did_emsg = did_emsg; - did_emsg = FALSE; + did_emsg = false; // // ":function func" with only function name: list function. @@ -2026,11 +2124,11 @@ void ex_function(exarg_T *eap) // - exclude line numbers from function body // if (!paren) { - if (!ends_excmd(*skipwhite((char *)p))) { + if (!ends_excmd(*skipwhite(p))) { semsg(_(e_trailing_arg), p); goto ret_free; } - eap->nextcmd = (char *)check_nextcmd(p); + eap->nextcmd = check_nextcmd(p); if (eap->nextcmd != NULL) { *p = NUL; } @@ -2052,7 +2150,7 @@ void ex_function(exarg_T *eap) msg_putchar(' '); } } - msg_prt_line((char_u *)FUNCLINE(fp, j), false); + msg_prt_line(FUNCLINE(fp, j), false); ui_flush(); // show a line at a time os_breakcheck(); } @@ -2067,24 +2165,22 @@ void ex_function(exarg_T *eap) goto ret_free; } - /* - * ":function name(arg1, arg2)" Define function. - */ - p = (char_u *)skipwhite((char *)p); + // ":function name(arg1, arg2)" Define function. + p = skipwhite(p); if (*p != '(') { if (!eap->skip) { semsg(_("E124: Missing '(': %s"), eap->arg); goto ret_free; } // attempt to continue by skipping some text - if (vim_strchr((char *)p, '(') != NULL) { - p = (char_u *)vim_strchr((char *)p, '('); + if (vim_strchr(p, '(') != NULL) { + p = vim_strchr(p, '('); } } - p = (char_u *)skipwhite((char *)p + 1); + p = skipwhite(p + 1); - ga_init(&newargs, (int)sizeof(char_u *), 3); - ga_init(&newlines, (int)sizeof(char_u *), 3); + ga_init(&newargs, (int)sizeof(char *), 3); + ga_init(&newlines, (int)sizeof(char *), 3); if (!eap->skip) { // Check the name of the function. Unless it's a dictionary function @@ -2095,7 +2191,7 @@ void ex_function(exarg_T *eap) arg = fudi.fd_newkey; } if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) { - int j = (*arg == K_SPECIAL) ? 3 : 0; + int j = ((uint8_t)(*arg) == K_SPECIAL) ? 3 : 0; while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) { j++; } @@ -2109,7 +2205,7 @@ void ex_function(exarg_T *eap) } } - if (get_function_args((char **)&p, ')', &newargs, &varargs, + if (get_function_args(&p, ')', &newargs, &varargs, &default_args, eap->skip) == FAIL) { goto errret_2; } @@ -2121,23 +2217,22 @@ void ex_function(exarg_T *eap) // find extra arguments "range", "dict", "abort" and "closure" for (;;) { - p = (char_u *)skipwhite((char *)p); - if (STRNCMP(p, "range", 5) == 0) { + p = skipwhite(p); + if (strncmp(p, "range", 5) == 0) { flags |= FC_RANGE; p += 5; - } else if (STRNCMP(p, "dict", 4) == 0) { + } else if (strncmp(p, "dict", 4) == 0) { flags |= FC_DICT; p += 4; - } else if (STRNCMP(p, "abort", 5) == 0) { + } else if (strncmp(p, "abort", 5) == 0) { flags |= FC_ABORT; p += 5; - } else if (STRNCMP(p, "closure", 7) == 0) { + } else if (strncmp(p, "closure", 7) == 0) { flags |= FC_CLOSURE; p += 7; if (current_funccal == NULL) { - emsg_funcname(N_ - ("E932: Closure function should not be at top level: %s"), - name == NULL ? (char_u *)"" : name); + emsg_funcname(N_("E932: Closure function should not be at top level: %s"), + name == NULL ? "" : name); goto erret; } } else { @@ -2153,9 +2248,7 @@ void ex_function(exarg_T *eap) semsg(_(e_trailing_arg), p); } - /* - * Read the body of the function, until ":endfunction" is found. - */ + // Read the body of the function, until ":endfunction" is found. if (KeyTyped) { // Check if the function already exists, don't let the user type the // whole function before telling him it doesn't work! For a script we @@ -2193,9 +2286,9 @@ void ex_function(exarg_T *eap) if (line_arg != NULL) { // Use eap->arg, split up in parts by line breaks. theline = line_arg; - p = (char_u *)vim_strchr((char *)theline, '\n'); + p = vim_strchr(theline, '\n'); if (p == NULL) { - line_arg += STRLEN(line_arg); + line_arg += strlen(line_arg); } else { *p = NUL; line_arg = p + 1; @@ -2205,7 +2298,7 @@ void ex_function(exarg_T *eap) if (eap->getline == NULL) { theline = getcmdline(':', 0L, indent, do_concat); } else { - theline = (char_u *)eap->getline(':', eap->cookie, indent, do_concat); + theline = eap->getline(':', eap->cookie, indent, do_concat); } line_to_free = theline; } @@ -2235,18 +2328,18 @@ void ex_function(exarg_T *eap) // * ":python <<EOF" and "EOF" // * ":let {var-name} =<< [trim] {marker}" and "{marker}" if (heredoc_trimmed == NULL - || (is_heredoc && (char_u *)skipwhite((char *)theline) == theline) - || STRNCMP(theline, heredoc_trimmed, - STRLEN(heredoc_trimmed)) == 0) { + || (is_heredoc && skipwhite(theline) == theline) + || strncmp(theline, heredoc_trimmed, + strlen(heredoc_trimmed)) == 0) { if (heredoc_trimmed == NULL) { p = theline; } else if (is_heredoc) { - p = (char_u *)skipwhite((char *)theline) == theline - ? theline : theline + STRLEN(heredoc_trimmed); + p = skipwhite(theline) == theline + ? theline : theline + strlen(heredoc_trimmed); } else { - p = theline + STRLEN(heredoc_trimmed); + p = theline + strlen(heredoc_trimmed); } - if (STRCMP(p, skip_until) == 0) { + if (strcmp(p, skip_until) == 0) { XFREE_CLEAR(skip_until); XFREE_CLEAR(heredoc_trimmed); do_concat = true; @@ -2258,27 +2351,26 @@ void ex_function(exarg_T *eap) for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {} // Check for "endfunction". - if (checkforcmd((char **)&p, "endfunction", 4) && nesting-- == 0) { + if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) { if (*p == '!') { p++; } - char_u *nextcmd = NULL; + char *nextcmd = NULL; if (*p == '|') { nextcmd = p + 1; - } else if (line_arg != NULL && *skipwhite((char *)line_arg) != NUL) { + } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) { nextcmd = line_arg; } else if (*p != NUL && *p != '"' && p_verbose > 0) { - give_warning2((char_u *)_("W22: Text found after :endfunction: %s"), - p, true); + give_warning2(_("W22: Text found after :endfunction: %s"), p, true); } if (nextcmd != NULL) { // Another command follows. If the line came from "eap" we // can simply point into it, otherwise we need to change // "eap->cmdlinep". - eap->nextcmd = (char *)nextcmd; + eap->nextcmd = nextcmd; if (line_to_free != NULL) { xfree(*eap->cmdlinep); - *eap->cmdlinep = (char *)line_to_free; + *eap->cmdlinep = line_to_free; line_to_free = NULL; } } @@ -2287,46 +2379,50 @@ void ex_function(exarg_T *eap) // Increase indent inside "if", "while", "for" and "try", decrease // at "end". - if (indent > 2 && STRNCMP(p, "end", 3) == 0) { + if (indent > 2 && strncmp(p, "end", 3) == 0) { indent -= 2; - } else if (STRNCMP(p, "if", 2) == 0 - || STRNCMP(p, "wh", 2) == 0 - || STRNCMP(p, "for", 3) == 0 - || STRNCMP(p, "try", 3) == 0) { + } else if (strncmp(p, "if", 2) == 0 + || strncmp(p, "wh", 2) == 0 + || strncmp(p, "for", 3) == 0 + || strncmp(p, "try", 3) == 0) { indent += 2; } // Check for defining a function inside this function. - if (checkforcmd((char **)&p, "function", 2)) { + if (checkforcmd(&p, "function", 2)) { if (*p == '!') { - p = (char_u *)skipwhite((char *)p + 1); + p = skipwhite(p + 1); } p += eval_fname_script((const char *)p); - xfree(trans_function_name((char **)&p, true, 0, NULL, NULL)); - if (*skipwhite((char *)p) == '(') { - nesting++; - indent += 2; + xfree(trans_function_name(&p, true, 0, NULL, NULL)); + if (*skipwhite(p) == '(') { + if (nesting == MAX_FUNC_NESTING - 1) { + emsg(_("E1058: function nesting too deep")); + } else { + nesting++; + indent += 2; + } } } // Check for ":append", ":change", ":insert". - p = (char_u *)skip_range((char *)p, NULL); + p = skip_range(p, NULL); if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) || (p[0] == 'c' && (!ASCII_ISALPHA(p[1]) || (p[1] == 'h' && (!ASCII_ISALPHA(p[2]) || (p[2] == 'a' - && (STRNCMP(&p[3], "nge", 3) != 0 + && (strncmp(&p[3], "nge", 3) != 0 || !ASCII_ISALPHA(p[6]))))))) || (p[0] == 'i' && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n' && (!ASCII_ISALPHA(p[2]) || (p[2] == 's')))))) { - skip_until = vim_strsave((char_u *)"."); + skip_until = xstrdup("."); } // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc. - arg = (char_u *)skipwhite((char *)skiptowhite(p)); + arg = skipwhite(skiptowhite(p)); if (arg[0] == '<' && arg[1] == '<' && ((p[0] == 'p' && p[1] == 'y' && (!ASCII_ISALNUM(p[2]) || p[2] == 't' @@ -2343,22 +2439,22 @@ void ex_function(exarg_T *eap) || (p[0] == 'm' && p[1] == 'z' && (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) { // ":python <<" continues until a dot, like ":append" - p = (char_u *)skipwhite((char *)arg + 2); + p = skipwhite(arg + 2); if (*p == NUL) { - skip_until = vim_strsave((char_u *)"."); + skip_until = xstrdup("."); } else { - skip_until = vim_strsave(p); + skip_until = xstrdup(p); } } // Check for ":let v =<< [trim] EOF" // and ":let [a, b] =<< [trim] EOF" - arg = (char_u *)skipwhite((char *)skiptowhite(p)); + arg = skipwhite(skiptowhite(p)); if (*arg == '[') { - arg = (char_u *)vim_strchr((char *)arg, ']'); + arg = vim_strchr(arg, ']'); } if (arg != NULL) { - arg = (char_u *)skipwhite((char *)skiptowhite(arg)); + arg = skipwhite(skiptowhite(arg)); if (arg[0] == '=' && arg[1] == '<' && arg[2] == '<' @@ -2366,14 +2462,13 @@ void ex_function(exarg_T *eap) && p[1] == 'e' && (!ASCII_ISALNUM(p[2]) || (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) { - p = (char_u *)skipwhite((char *)arg + 3); - if (STRNCMP(p, "trim", 4) == 0) { + p = skipwhite(arg + 3); + if (strncmp(p, "trim", 4) == 0) { // Ignore leading white space. - p = (char_u *)skipwhite((char *)p + 4); - heredoc_trimmed = - vim_strnsave(theline, (size_t)((char_u *)skipwhite((char *)theline) - theline)); + p = skipwhite(p + 4); + heredoc_trimmed = xstrnsave(theline, (size_t)(skipwhite(theline) - theline)); } - skip_until = vim_strnsave(p, (size_t)(skiptowhite(p) - p)); + skip_until = xstrnsave(p, (size_t)(skiptowhite(p) - p)); do_concat = false; is_heredoc = true; } @@ -2386,8 +2481,8 @@ void ex_function(exarg_T *eap) // Copy the line to newly allocated memory. get_one_sourceline() // allocates 250 bytes per line, this saves 80% on average. The cost // is an extra alloc/free. - p = vim_strsave(theline); - ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p; + p = xstrdup(theline); + ((char **)(newlines.ga_data))[newlines.ga_len++] = p; // Add NULL lines for continuation lines, so that the line count is // equal to the index in the growarray. @@ -2407,14 +2502,11 @@ void ex_function(exarg_T *eap) goto erret; } - /* - * If there are no errors, add the function - */ + // If there are no errors, add the function if (fudi.fd_dict == NULL) { - v = find_var((const char *)name, STRLEN(name), &ht, false); + v = find_var((const char *)name, strlen(name), &ht, false); if (v != NULL && v->di_tv.v_type == VAR_FUNC) { - emsg_funcname(N_("E707: Function name conflicts with variable: %s"), - name); + emsg_funcname(N_("E707: Function name conflicts with variable: %s"), name); goto erret; } @@ -2429,8 +2521,7 @@ void ex_function(exarg_T *eap) goto erret; } if (fp->uf_calls > 0) { - emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), - name); + emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name); goto erret; } if (fp->uf_refcount > 1) { @@ -2441,7 +2532,7 @@ void ex_function(exarg_T *eap) fp = NULL; overwrite = true; } else { - char_u *exp_name = fp->uf_name_exp; + char *exp_name = fp->uf_name_exp; // redefine existing function, keep the expanded name XFREE_CLEAR(name); fp->uf_name_exp = NULL; @@ -2460,13 +2551,13 @@ void ex_function(exarg_T *eap) goto erret; } if (fudi.fd_di == NULL) { - if (var_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg, - TV_CSTRING)) { + if (value_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg, + TV_CSTRING)) { // Can't add a function to a locked dictionary goto erret; } - } else if (var_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg, - TV_CSTRING)) { + } else if (value_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg, + TV_CSTRING)) { // Can't change an existing function if it is locked goto erret; } @@ -2474,23 +2565,23 @@ void ex_function(exarg_T *eap) // Give the function a sequential number. Can only be used with a // Funcref! xfree(name); - sprintf(numbuf, "%d", ++func_nr); - name = vim_strsave((char_u *)numbuf); + sprintf(numbuf, "%d", ++func_nr); // NOLINT(runtime/printf) + name = xstrdup(numbuf); } if (fp == NULL) { - if (fudi.fd_dict == NULL && vim_strchr((char *)name, AUTOLOAD_CHAR) != NULL) { + if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) { int slen, plen; - char_u *scriptname; + char *scriptname; // Check that the autoload name matches the script name. int j = FAIL; if (SOURCING_NAME != NULL) { - scriptname = (char_u *)autoload_name((const char *)name, STRLEN(name)); - p = (char_u *)vim_strchr((char *)scriptname, '/'); - plen = (int)STRLEN(p); - slen = (int)STRLEN(SOURCING_NAME); - if (slen > plen && FNAMECMP(p, SOURCING_NAME + slen - plen) == 0) { + scriptname = autoload_name(name, strlen(name)); + p = vim_strchr(scriptname, '/'); + plen = (int)strlen(p); + slen = (int)strlen(SOURCING_NAME); + if (slen > plen && path_fnamecmp(p, SOURCING_NAME + slen - plen) == 0) { j = OK; } xfree(scriptname); @@ -2502,7 +2593,7 @@ void ex_function(exarg_T *eap) } } - fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); + fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1); if (fudi.fd_dict != NULL) { if (fudi.fd_di == NULL) { @@ -2518,7 +2609,7 @@ void ex_function(exarg_T *eap) tv_clear(&fudi.fd_di->di_tv); } fudi.fd_di->di_tv.v_type = VAR_FUNC; - fudi.fd_di->di_tv.vval.v_string = (char *)vim_strsave(name); + fudi.fd_di->di_tv.vval.v_string = xstrdup(name); // behave like "dict" was used flags |= FC_DICT; @@ -2527,8 +2618,8 @@ void ex_function(exarg_T *eap) // insert the new function in the function list set_ufunc_name(fp, name); if (overwrite) { - hi = hash_find(&func_hashtab, (char *)name); - hi->hi_key = UF2HIKEY(fp); + hi = hash_find(&func_hashtab, name); + hi->hi_key = (char *)UF2HIKEY(fp); } else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) { xfree(fp); goto erret; @@ -2584,8 +2675,8 @@ int eval_fname_script(const char *const p) // Use mb_strnicmp() because in Turkish comparing the "I" may not work with // the standard library function. if (p[0] == '<' - && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0 - || mb_strnicmp((char_u *)p + 1, (char_u *)"SNR>", 4) == 0)) { + && (mb_strnicmp(p + 1, "SID>", 4) == 0 + || mb_strnicmp(p + 1, "SNR>", 4) == 0)) { return 5; } if (p[0] == 's' && p[1] == ':') { @@ -2599,7 +2690,7 @@ bool translated_function_exists(const char *name) if (builtin_function(name, -1)) { return find_internal_func((char *)name) != NULL; } - return find_func((const char_u *)name) != NULL; + return find_func(name) != NULL; } /// Check whether function with the given name exists @@ -2610,15 +2701,15 @@ bool translated_function_exists(const char *name) /// @return true if it exists, false otherwise. bool function_exists(const char *const name, bool no_deref) { - const char_u *nm = (const char_u *)name; + const char *nm = name; bool n = false; int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD; if (no_deref) { flag |= TFN_NO_DEREF; } - char *const p = (char *)trans_function_name((char **)&nm, false, flag, NULL, NULL); - nm = (char_u *)skipwhite((char *)nm); + char *const p = trans_function_name((char **)&nm, false, flag, NULL, NULL); + nm = skipwhite(nm); // Only accept "funcname", "funcname ", "funcname (..." and // "funcname(...", not "funcname!...". @@ -2634,15 +2725,17 @@ bool function_exists(const char *const name, bool no_deref) char *get_user_func_name(expand_T *xp, int idx) { static size_t done; + static int changed; static hashitem_T *hi; ufunc_T *fp; if (idx == 0) { done = 0; hi = func_hashtab.ht_array; + changed = func_hashtab.ht_changed; } assert(hi); - if (done < func_hashtab.ht_used) { + if (changed == func_hashtab.ht_changed && done < func_hashtab.ht_used) { if (done++ > 0) { hi++; } @@ -2652,11 +2745,11 @@ char *get_user_func_name(expand_T *xp, int idx) fp = HI2UF(hi); if ((fp->uf_flags & FC_DICT) - || STRNCMP(fp->uf_name, "<lambda>", 8) == 0) { + || strncmp(fp->uf_name, "<lambda>", 8) == 0) { return ""; // don't show dict and lambda functions } - if (STRLEN(fp->uf_name) + 4 >= IOSIZE) { + if (strlen(fp->uf_name) + 4 >= IOSIZE) { return (char *)fp->uf_name; // Prevent overflow. } @@ -2667,7 +2760,7 @@ char *get_user_func_name(expand_T *xp, int idx) STRCAT(IObuff, ")"); } } - return (char *)IObuff; + return IObuff; } return NULL; } @@ -2676,12 +2769,12 @@ char *get_user_func_name(expand_T *xp, int idx) void ex_delfunction(exarg_T *eap) { ufunc_T *fp = NULL; - char_u *p; - char_u *name; + char *p; + char *name; funcdict_T fudi; - p = (char_u *)eap->arg; - name = trans_function_name((char **)&p, eap->skip, 0, &fudi, NULL); + p = eap->arg; + name = trans_function_name(&p, eap->skip, 0, &fudi, NULL); xfree(fudi.fd_newkey); if (name == NULL) { if (fudi.fd_dict != NULL && !eap->skip) { @@ -2689,16 +2782,23 @@ void ex_delfunction(exarg_T *eap) } return; } - if (!ends_excmd(*skipwhite((char *)p))) { + if (!ends_excmd(*skipwhite(p))) { xfree(name); semsg(_(e_trailing_arg), p); return; } - eap->nextcmd = (char *)check_nextcmd(p); + eap->nextcmd = check_nextcmd(p); if (eap->nextcmd != NULL) { *p = NUL; } + if (isdigit((uint8_t)(*name)) && fudi.fd_dict == NULL) { + if (!eap->skip) { + semsg(_(e_invarg2), eap->arg); + } + xfree(name); + return; + } if (!eap->skip) { fp = find_func(name); } @@ -2749,7 +2849,7 @@ void ex_delfunction(exarg_T *eap) /// Unreference a Function: decrement the reference count and free it when it /// becomes zero. -void func_unref(char_u *name) +void func_unref(char *name) { ufunc_T *fp = NULL; @@ -2758,7 +2858,7 @@ void func_unref(char_u *name) } fp = find_func(name); - if (fp == NULL && isdigit(*name)) { + if (fp == NULL && isdigit((uint8_t)(*name))) { #ifdef EXITFREE if (!entered_free_all_mem) { internal_error("func_unref()"); @@ -2791,7 +2891,7 @@ void func_ptr_unref(ufunc_T *fp) } /// Count a reference to a Function. -void func_ref(char_u *name) +void func_ref(char *name) { ufunc_T *fp; @@ -2801,7 +2901,7 @@ void func_ref(char_u *name) fp = find_func(name); if (fp != NULL) { (fp->uf_refcount)++; - } else if (isdigit(*name)) { + } else if (isdigit((uint8_t)(*name))) { // Only give an error for a numbered function. // Fail silently, when named or lambda function isn't found. internal_error("func_ref()"); @@ -2845,9 +2945,9 @@ static int can_free_funccal(funccall_T *fc, int copyID) /// ":return [expr]" void ex_return(exarg_T *eap) { - char_u *arg = (char_u *)eap->arg; + char *arg = eap->arg; typval_T rettv; - int returning = FALSE; + int returning = false; if (current_funccal == NULL) { emsg(_("E133: :return not inside a function")); @@ -2860,7 +2960,7 @@ void ex_return(exarg_T *eap) eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') - && eval0((char *)arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) { + && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) { if (!eap->skip) { returning = do_return(eap, false, true, &rettv); } else { @@ -2883,7 +2983,7 @@ void ex_return(exarg_T *eap) if (returning) { eap->nextcmd = NULL; } else if (eap->nextcmd == NULL) { // no argument - eap->nextcmd = (char *)check_nextcmd(arg); + eap->nextcmd = check_nextcmd(arg); } if (eap->skip) { @@ -2891,15 +2991,13 @@ void ex_return(exarg_T *eap) } } -// TODO(ZyX-I): move to eval/ex_cmds - /// ":1,25call func(arg1, arg2)" function call. void ex_call(exarg_T *eap) { - char_u *arg = (char_u *)eap->arg; - char_u *startarg; - char_u *name; - char_u *tofree; + char *arg = eap->arg; + char *startarg; + char *name; + char *tofree; int len; typval_T rettv; linenr_T lnum; @@ -2920,7 +3018,7 @@ void ex_call(exarg_T *eap) return; } - tofree = trans_function_name((char **)&arg, false, TFN_INT, &fudi, &partial); + tofree = trans_function_name(&arg, false, TFN_INT, &fudi, &partial); if (fudi.fd_newkey != NULL) { // Still need to give an error message for missing key. semsg(_(e_dictkey), fudi.fd_newkey); @@ -2939,13 +3037,12 @@ void ex_call(exarg_T *eap) // If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its // contents. For VAR_PARTIAL get its partial, unless we already have one // from trans_function_name(). - len = (int)STRLEN(tofree); - name = deref_func_name((const char *)tofree, &len, - partial != NULL ? NULL : &partial, false); + len = (int)strlen(tofree); + name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial, false); // Skip white space to allow ":call func ()". Not good, but required for // backward compatibility. - startarg = (char_u *)skipwhite((char *)arg); + startarg = skipwhite(arg); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { @@ -2969,13 +3066,13 @@ void ex_call(exarg_T *eap) arg = startarg; funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = eap->line1; - funcexe.lastline = eap->line2; - funcexe.doesrange = &doesrange; - funcexe.evaluate = true; - funcexe.partial = partial; - funcexe.selfdict = fudi.fd_dict; - if (get_func_tv(name, -1, &rettv, (char **)&arg, &funcexe) == FAIL) { + funcexe.fe_firstline = eap->line1; + funcexe.fe_lastline = eap->line2; + funcexe.fe_doesrange = &doesrange; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; + funcexe.fe_selfdict = fudi.fd_dict; + if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) { failed = true; break; } @@ -3004,7 +3101,7 @@ void ex_call(exarg_T *eap) // When inside :try we need to check for following "| catch" or "| endtry". // Not when there was an error, but do check if an exception was thrown. - if ((!aborting() || current_exception != NULL) && (!failed || eap->cstack->cs_trylevel > 0)) { + if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 0)) { // Check for trailing illegal characters and a following command. if (!ends_excmd(*arg)) { if (!failed && !aborting()) { @@ -3012,7 +3109,7 @@ void ex_call(exarg_T *eap) semsg(_(e_trailing_arg), arg); } } else { - eap->nextcmd = (char *)check_nextcmd(arg); + eap->nextcmd = check_nextcmd(arg); } } @@ -3029,8 +3126,8 @@ end: /// @param is_cmd set when called due to a ":return" command. /// @param rettv may point to a typval_T with the return rettv. /// -/// @return TRUE when the return can be carried out, -/// FALSE when the return gets pending. +/// @return true when the return can be carried out, +/// false when the return gets pending. int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) { int idx; @@ -3080,7 +3177,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) } report_make_pending(CSTP_RETURN, rettv); } else { - current_funccal->returned = TRUE; + current_funccal->returned = true; // If the return is carried out now, store the return value. For // a return immediately after reanimation, the value is already @@ -3099,25 +3196,25 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) /// Generate a return command for producing the value of "rettv". The result /// is an allocated string. Used by report_pending() for verbose messages. -char_u *get_return_cmd(void *rettv) +char *get_return_cmd(void *rettv) { - char_u *s = NULL; - char_u *tofree = NULL; + char *s = NULL; + char *tofree = NULL; if (rettv != NULL) { - tofree = s = (char_u *)encode_tv2echo((typval_T *)rettv, NULL); + tofree = s = encode_tv2echo((typval_T *)rettv, NULL); } if (s == NULL) { - s = (char_u *)""; + s = ""; } STRCPY(IObuff, ":return "); - STRLCPY(IObuff + 8, s, IOSIZE - 8); - if (STRLEN(s) + 8 >= IOSIZE) { + xstrlcpy(IObuff + 8, s, IOSIZE - 8); + if (strlen(s) + 8 >= IOSIZE) { STRCPY(IObuff + IOSIZE - 4, "..."); } xfree(tofree); - return vim_strsave(IObuff); + return xstrdup(IObuff); } /// Get next function line. @@ -3128,12 +3225,12 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat) { funccall_T *fcp = (funccall_T *)cookie; ufunc_T *fp = fcp->func; - char_u *retval; + char *retval; garray_T *gap; // growarray with function lines // If breakpoints have been added/deleted need to check for it. if (fcp->dbg_tick != debug_tick) { - fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM); + fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM); fcp->dbg_tick = debug_tick; } if (do_profiling == PROF_YES) { @@ -3153,7 +3250,7 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat) if (fcp->linenr >= gap->ga_len) { retval = NULL; } else { - retval = (char_u *)xstrdup(((char **)(gap->ga_data))[fcp->linenr++]); + retval = xstrdup(((char **)(gap->ga_data))[fcp->linenr++]); SOURCING_LNUM = fcp->linenr; if (do_profiling == PROF_YES) { func_line_start(cookie); @@ -3163,16 +3260,16 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat) // Did we encounter a breakpoint? if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM) { - dbg_breakpoint(fp->uf_name, SOURCING_LNUM); + dbg_breakpoint((char *)fp->uf_name, SOURCING_LNUM); // Find next breakpoint. - fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM); + fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM); fcp->dbg_tick = debug_tick; } - return (char *)retval; + return retval; } -/// @return TRUE if the currently active function should be ended, because a +/// @return true if the currently active function should be ended, because a /// return was encountered or an error occurred. Used inside a ":while". int func_has_ended(void *cookie) { @@ -3184,7 +3281,7 @@ int func_has_ended(void *cookie) || fcp->returned; } -/// @return TRUE if cookie indicates a function which "abort"s on errors. +/// @return true if cookie indicates a function which "abort"s on errors. int func_has_abort(void *cookie) { return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT; @@ -3194,17 +3291,17 @@ int func_has_abort(void *cookie) /// Changes "rettv" in-place. void make_partial(dict_T *const selfdict, typval_T *const rettv) { - char_u *fname; - char_u *tofree = NULL; + char *fname; + char *tofree = NULL; ufunc_T *fp; - char_u fname_buf[FLEN_FIXED + 1]; + char fname_buf[FLEN_FIXED + 1]; int error; if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) { fp = rettv->vval.v_partial->pt_func; } else { fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING - ? (char_u *)rettv->vval.v_string + ? rettv->vval.v_string : rettv->vval.v_partial->pt_name; // Translate "s:func" to the stored function name. fname = fname_trans_sid(fname, fname_buf, &tofree, &error); @@ -3221,7 +3318,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv) pt->pt_auto = true; if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) { // Just a function: Take over the function name and use selfdict. - pt->pt_name = (char_u *)rettv->vval.v_string; + pt->pt_name = rettv->vval.v_string; } else { partial_T *ret_pt = rettv->vval.v_partial; int i; @@ -3230,7 +3327,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv) // args. Can't take over name or args, the partial might // be referenced elsewhere. if (ret_pt->pt_name != NULL) { - pt->pt_name = vim_strsave(ret_pt->pt_name); + pt->pt_name = xstrdup(ret_pt->pt_name); func_ref(pt->pt_name); } else { pt->pt_func = ret_pt->pt_func; @@ -3252,7 +3349,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv) } /// @return the name of the executed function. -char_u *func_name(void *cookie) +char *func_name(void *cookie) { return ((funccall_T *)cookie)->func->uf_name; } @@ -3275,7 +3372,7 @@ int func_level(void *cookie) return ((funccall_T *)cookie)->level; } -/// @return TRUE when a function was ended by a ":return" command. +/// @return true when a function was ended by a ":return" command. int current_func_returned(void) { return current_funccal->returned; @@ -3539,14 +3636,14 @@ bool set_ref_in_func_args(int copyID) /// "ht_stack" is used to add hashtabs to be marked. Can be NULL. /// /// @return true if setting references failed somehow. -bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) +bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID) { ufunc_T *fp = fp_in; funccall_T *fc; - int error = ERROR_NONE; - char_u fname_buf[FLEN_FIXED + 1]; - char_u *tofree = NULL; - char_u *fname; + int error = FCERR_NONE; + char fname_buf[FLEN_FIXED + 1]; + char *tofree = NULL; + char *fname; bool abort = false; if (name == NULL && fp_in == NULL) { return false; @@ -3565,20 +3662,18 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) return abort; } -/// Registers a C extension user function. -char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state) +/// Registers a luaref as a lambda. +char *register_luafunc(LuaRef ref) { - char_u *name = get_lambda_name(); - ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); + char *name = get_lambda_name(); + ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1); fp->uf_refcount = 1; fp->uf_varargs = true; - fp->uf_flags = FC_CFUNC; + fp->uf_flags = FC_LUAREF; fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; - fp->uf_cb = cb; - fp->uf_cb_free = cb_free; - fp->uf_cb_state = state; + fp->uf_luaref = ref; STRCPY(fp->uf_name, name); hash_add(&func_hashtab, UF2HIKEY(fp)); diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 4b7007aae9..c8583f232c 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -1,18 +1,42 @@ #ifndef NVIM_EVAL_USERFUNC_H #define NVIM_EVAL_USERFUNC_H +#include <stdbool.h> +#include <stddef.h> + #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" +#include "nvim/garray.h" +#include "nvim/hashtab.h" +#include "nvim/pos.h" +#include "nvim/types.h" + +struct funccal_entry; // From user function to hashitem and back. #define UF2HIKEY(fp) ((fp)->uf_name) #define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name))) #define HI2UF(hi) HIKEY2UF((hi)->hi_key) -///< Structure used by trans_function_name() +// flags used in uf_flags +#define FC_ABORT 0x01 // abort function on error +#define FC_RANGE 0x02 // function accepts range +#define FC_DICT 0x04 // Dict function, uses "self" +#define FC_CLOSURE 0x08 // closure, uses outer scope variables +#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0 +#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0 +#define FC_SANDBOX 0x40 // function defined in the sandbox +#define FC_DEAD 0x80 // function kept only for reference to dfunc +#define FC_EXPORT 0x100 // "export def Func()" +#define FC_NOARGS 0x200 // no a: variables in lambda +#define FC_VIM9 0x400 // defined in vim9 script file +#define FC_LUAREF 0x800 // luaref callback + +/// Structure used by trans_function_name() typedef struct { dict_T *fd_dict; ///< Dictionary used. - char_u *fd_newkey; ///< New key in "dict" in allocated memory. + char *fd_newkey; ///< New key in "dict" in allocated memory. dictitem_T *fd_di; ///< Dictionary item used. } funcdict_T; @@ -24,44 +48,43 @@ struct funccal_entry { /// errors for when calling a function typedef enum { - ERROR_UNKNOWN = 0, - ERROR_TOOMANY, - ERROR_TOOFEW, - ERROR_SCRIPT, - ERROR_DICT, - ERROR_NONE, - ERROR_OTHER, - ERROR_BOTH, - ERROR_DELETED, - ERROR_NOTMETHOD, + FCERR_UNKNOWN = 0, + FCERR_TOOMANY = 1, + FCERR_TOOFEW = 2, + FCERR_SCRIPT = 3, + FCERR_DICT = 4, + FCERR_NONE = 5, + FCERR_OTHER = 6, + FCERR_DELETED = 7, + FCERR_NOTMETHOD = 8, ///< function cannot be used as a method } FnameTransError; /// Used in funcexe_T. Returns the new argcount. -typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip, - int called_func_argcount); +typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int partial_argcount, + ufunc_T *called_func); /// Structure passed between functions dealing with function call execution. typedef struct { - ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only - ///< when the invoked function uses them - linenr_T firstline; ///< first line of range - linenr_T lastline; ///< last line of range - bool *doesrange; ///< [out] if not NULL: function handled range - bool evaluate; ///< actually evaluate expressions - partial_T *partial; ///< for extra arguments - dict_T *selfdict; ///< Dictionary for "self" - typval_T *basetv; ///< base for base->method() + ArgvFunc fe_argv_func; ///< when not NULL, can be used to fill in arguments only + ///< when the invoked function uses them + linenr_T fe_firstline; ///< first line of range + linenr_T fe_lastline; ///< last line of range + bool *fe_doesrange; ///< [out] if not NULL: function handled range + bool fe_evaluate; ///< actually evaluate expressions + partial_T *fe_partial; ///< for extra arguments + dict_T *fe_selfdict; ///< Dictionary for "self" + typval_T *fe_basetv; ///< base for base->method() } funcexe_T; #define FUNCEXE_INIT (funcexe_T) { \ - .argv_func = NULL, \ - .firstline = 0, \ - .lastline = 0, \ - .doesrange = NULL, \ - .evaluate = false, \ - .partial = NULL, \ - .selfdict = NULL, \ - .basetv = NULL, \ + .fe_argv_func = NULL, \ + .fe_firstline = 0, \ + .fe_lastline = 0, \ + .fe_doesrange = NULL, \ + .fe_evaluate = false, \ + .fe_partial = NULL, \ + .fe_selfdict = NULL, \ + .fe_basetv = NULL, \ } #define FUNCARG(fp, j) ((char **)(fp->uf_args.ga_data))[j] diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index b38849730a..3e593151fc 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -3,24 +3,43 @@ // eval/vars.c: functions for dealing with variables +#include <assert.h> +#include <ctype.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + #include "nvim/ascii.h" #include "nvim/autocmd.h" -#include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/eval/window.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/hashtab.h" +#include "nvim/macros.h" +#include "nvim/memory.h" +#include "nvim/message.h" #include "nvim/ops.h" #include "nvim/option.h" -#include "nvim/screen.h" +#include "nvim/os/os.h" #include "nvim/search.h" +#include "nvim/strings.h" +#include "nvim/types.h" +#include "nvim/vim.h" #include "nvim/window.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -62,7 +81,7 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) // Check for the optional 'trim' word before the marker cmd = skipwhite(cmd); - if (STRNCMP(cmd, "trim", 4) == 0 + if (strncmp(cmd, "trim", 4) == 0 && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { cmd = skipwhite(cmd + 4); @@ -81,13 +100,13 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) // The marker is the next word. if (*cmd != NUL && *cmd != '"') { marker = skipwhite(cmd); - p = (char *)skiptowhite((char_u *)marker); + p = skiptowhite(marker); if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { semsg(_(e_trailing_arg), p); return NULL; } *p = NUL; - if (islower(*marker)) { + if (islower((uint8_t)(*marker))) { emsg(_("E221: Marker cannot start with lower case letter")); return NULL; } @@ -110,10 +129,10 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) // with "trim": skip the indent matching the :let line to find the // marker if (marker_indent_len > 0 - && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { + && strncmp(theline, *eap->cmdlinep, (size_t)marker_indent_len) == 0) { mi = marker_indent_len; } - if (STRCMP(marker, theline + mi) == 0) { + if (strcmp(marker, theline + mi) == 0) { xfree(theline); break; } @@ -189,8 +208,8 @@ static void ex_let_const(exarg_T *eap, const bool is_const) argend--; } expr = skipwhite(argend); - if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL - && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { + if (*expr != '=' && !((vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL + && expr[1] == '=') || strncmp(expr, "..=", 3) == 0)) { // ":let" without "=": list variables if (*arg == '[') { emsg(_(e_invarg)); @@ -207,7 +226,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const) list_func_vars(&first); list_vim_vars(&first); } - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + eap->nextcmd = check_nextcmd(arg); } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { // HERE document list_T *l = heredoc_get(eap, expr + 3); @@ -225,17 +244,19 @@ static void ex_let_const(exarg_T *eap, const bool is_const) op[0] = '='; op[1] = NUL; if (*expr != '=') { - if (vim_strchr("+-*/%.", *expr) != NULL) { + if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) { op[0] = *expr; // +=, -=, *=, /=, %= or .= if (expr[0] == '.' && expr[1] == '.') { // ..= expr++; } } - expr = skipwhite(expr + 2); + expr += 2; } else { - expr = skipwhite(expr + 1); + expr += 1; } + expr = skipwhite(expr); + if (eap->skip) { emsg_skip++; } @@ -378,9 +399,8 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) } } return p + 1; - } else { - return skip_var_one((char *)arg); } + return skip_var_one((char *)arg); } /// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, @@ -413,7 +433,7 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs // apply :filter /pat/ to variable name xstrlcpy(buf, prefix, IOSIZE); xstrlcat(buf, (char *)di->di_key, IOSIZE); - if (message_filtered((char_u *)buf)) { + if (message_filtered(buf)) { continue; } @@ -539,8 +559,6 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) return arg; } -// TODO(ZyX-I): move to eval/ex_cmds - /// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value /// /// @param[in] arg Start of the variable name. @@ -558,8 +576,6 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo { char *arg_end = NULL; int len; - int opt_flags; - char *tofree = NULL; // ":let $VAR = expr": Set environment variable. if (*arg == '$') { @@ -574,20 +590,20 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo if (len == 0) { semsg(_(e_invarg2), name - 1); } else { - if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { semsg(_(e_letwrong), op); } else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg)) == NULL) { + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) { emsg(_(e_letunexp)); } else if (!check_secure()) { + char *tofree = NULL; const char c1 = name[len]; name[len] = NUL; const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { char *s = vim_getenv(name); - if (s != NULL) { - tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); + tofree = concat_str(s, p); p = (const char *)tofree; xfree(s); } @@ -609,10 +625,11 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo return NULL; } // Find the end of the name. - char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); + int scope; + char *const p = (char *)find_option_end((const char **)&arg, &scope); if (p == NULL || (endchars != NULL - && vim_strchr(endchars, *skipwhite(p)) == NULL)) { + && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { emsg(_(e_letunexp)); } else { varnumber_T n = 0; @@ -621,11 +638,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo char *stringval = NULL; const char *s = NULL; bool failed = false; + uint32_t opt_p_flags; + char *tofree = NULL; const char c1 = *p; *p = NUL; - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope); if (opt_type == gov_bool || opt_type == gov_number || opt_type == gov_hidden_bool @@ -634,8 +653,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo n = (long)tv_get_number(tv); } - // Avoid setting a string option to the text "v:false" or similar. - if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) { + // If the option can be set to a function reference or a lambda + // and the passed value is a function reference, then convert it to + // the name (string) of the function reference. + s = tofree = encode_tv2string(tv, NULL); + } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + // Avoid setting a string option to the text "v:false" or similar. s = tv_get_string_chk(tv); } @@ -663,8 +687,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo } else if (opt_type == gov_string && stringval != NULL && s != NULL) { // string char *const oldstringval = stringval; - stringval = (char *)concat_str((const char_u *)stringval, - (const char_u *)s); + stringval = concat_str(stringval, s); xfree(oldstringval); s = stringval; } @@ -673,7 +696,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo if (!failed) { if (opt_type != gov_string || s != NULL) { - char *err = set_option_value(arg, n, s, opt_flags); + char *err = set_option_value(arg, n, s, scope); arg_end = p; if (err != NULL) { emsg(_(err)); @@ -684,6 +707,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo } *p = c1; xfree(stringval); + xfree(tofree); } // ":let @r = expr": Set register contents. } else if (*arg == '@') { @@ -692,10 +716,10 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo return NULL; } arg++; - if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { semsg(_(e_letwrong), op); } else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) { + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { emsg(_(e_letunexp)); } else { char *s; @@ -705,14 +729,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo if (p != NULL && op != NULL && *op == '.') { s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); if (s != NULL) { - ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); + ptofree = concat_str(s, p); p = (const char *)ptofree; xfree(s); } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, - (const char_u *)p, (ssize_t)STRLEN(p), false); + write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); arg_end = arg + 1; } xfree(ptofree); @@ -724,7 +747,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { + if (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL) { emsg(_(e_letunexp)); } else { set_var_lval(&lv, p, tv, copy, is_const, op); @@ -745,8 +768,6 @@ void ex_unlet(exarg_T *eap) ex_unletlock(eap, eap->arg, 0, do_unlet_var); } -// TODO(ZyX-I): move to eval/ex_cmds - /// ":lockvar" and ":unlockvar" commands void ex_lockvar(exarg_T *eap) { @@ -763,8 +784,6 @@ void ex_lockvar(exarg_T *eap) ex_unletlock(eap, arg, deep, do_lock_var); } -// TODO(ZyX-I): move to eval/ex_cmds - /// Common parsing logic for :unlet, :lockvar and :unlockvar. /// /// Invokes `callback` afterwards if successful and `eap->skip == false`. @@ -791,6 +810,7 @@ static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_ca semsg(_(e_invarg2), arg - 1); return; } + assert(*lv.ll_name == '$'); // suppress clang "Uninitialized argument value" if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) { error = true; } @@ -825,11 +845,9 @@ static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_ca arg = skipwhite(name_end); } while (!ends_excmd(*arg)); - eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + eap->nextcmd = check_nextcmd(arg); } -// TODO(ZyX-I): move to eval/ex_cmds - /// Unlet a variable indicated by `lp`. /// /// @param[in] lp The lvalue. @@ -846,7 +864,7 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ int cc; if (lp->ll_tv == NULL) { - cc = (char_u)(*name_end); + cc = (uint8_t)(*name_end); *name_end = NUL; // Environment variable, normal name or expanded name. @@ -859,13 +877,13 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ } else if ((lp->ll_list != NULL // ll_list is not NULL when lvalue is not in a list, NULL lists // yield E689. - && var_check_lock(tv_list_locked(lp->ll_list), - lp->ll_name, - lp->ll_name_len)) + && value_check_lock(tv_list_locked(lp->ll_list), + lp->ll_name, + lp->ll_name_len)) || (lp->ll_dict != NULL - && var_check_lock(lp->ll_dict->dv_lock, - lp->ll_name, - lp->ll_name_len))) { + && value_check_lock(lp->ll_dict->dv_lock, + lp->ll_name, + lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { assert(lp->ll_list != NULL); @@ -874,18 +892,17 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ listitem_T *last_li = first_li; for (;;) { listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, - lp->ll_name, - lp->ll_name_len)) { + if (value_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + lp->ll_name, + lp->ll_name_len)) { return false; } lp->ll_li = li; lp->ll_n1++; if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { break; - } else { - last_li = lp->ll_li; } + last_li = lp->ll_li; } tv_list_remove_items(lp->ll_list, first_li, last_li); } else { @@ -920,8 +937,6 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ return ret; } -// TODO(ZyX-I): move to eval/ex_cmds - /// unlet a variable /// /// @param[in] name Variable name to unlet. @@ -961,11 +976,11 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit) dictitem_T *const di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, name, TV_CSTRING) || var_check_ro(di->di_flags, name, TV_CSTRING) - || var_check_lock(d->dv_lock, name, TV_CSTRING)) { + || value_check_lock(d->dv_lock, name, TV_CSTRING)) { return FAIL; } - if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { + if (value_check_lock(d->dv_lock, name, TV_CSTRING)) { return FAIL; } @@ -992,8 +1007,6 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit) return FAIL; } -// TODO(ZyX-I): move to eval/ex_cmds - /// Lock or unlock variable indicated by `lp`. /// /// Locks if `eap->cmdidx == CMD_lockvar`, unlocks otherwise. @@ -1010,10 +1023,6 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap bool lock = eap->cmdidx == CMD_lockvar; int ret = OK; - if (deep == 0) { // Nothing to do. - return OK; - } - if (lp->ll_tv == NULL) { if (*lp->ll_name == '$') { semsg(_(e_lock_unlock), lp->ll_name); @@ -1037,9 +1046,13 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap } else { di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK); } - tv_item_lock(&di->di_tv, deep, lock, false); + if (deep != 0) { + tv_item_lock(&di->di_tv, deep, lock, false); + } } } + } else if (deep == 0) { + // nothing to do } else if (lp->ll_range) { listitem_T *li = lp->ll_li; @@ -1099,7 +1112,7 @@ int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, boo /// NULL when it doesn't exist. /// /// @see tv_get_string() for how long the pointer remains valid. -char_u *get_var_value(const char *const name) +char *get_var_value(const char *const name) { dictitem_T *v; @@ -1107,7 +1120,7 @@ char_u *get_var_value(const char *const name) if (v == NULL) { return NULL; } - return (char_u *)tv_get_string(&v->di_tv); + return (char *)tv_get_string(&v->di_tv); } /// Clean up a list of internal variables. @@ -1118,7 +1131,7 @@ void vars_clear(hashtab_T *ht) vars_clear_ext(ht, true); } -/// Like vars_clear(), but only free the value if "free_val" is TRUE. +/// Like vars_clear(), but only free the value if "free_val" is true. void vars_clear_ext(hashtab_T *ht, int free_val) { int todo; @@ -1162,7 +1175,7 @@ void delete_var(hashtab_T *ht, hashitem_T *hi) static void list_one_var(dictitem_T *v, const char *prefix, int *first) { char *const s = encode_tv2echo(&v->di_tv, NULL); - list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)STRLEN(v->di_key), + list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)strlen((char *)v->di_key), v->di_tv.v_type, (s == NULL ? "" : s), first); xfree(s); } @@ -1258,7 +1271,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, v = find_var_in_scoped_ht(name, name_len, true); } - if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { + if (tv_is_func(*tv) && var_wrong_func_name(name, v == NULL)) { return; } @@ -1269,12 +1282,18 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, return; } - // existing variable, need to clear the value + // Check in this order for backwards compatibility: + // - Whether the variable is read-only + // - Whether the variable value is locked + // - Whether the variable is locked if (var_check_ro(v->di_flags, name, name_len) - || var_check_lock(v->di_tv.v_lock, name, name_len)) { + || value_check_lock(v->di_tv.v_lock, name, name_len) + || var_check_lock(v->di_flags, name, name_len)) { return; } + // existing variable, need to clear the value + // Handle setting internal v: variables separately where needed to // prevent changing the type. if (is_vimvarht(ht)) { @@ -1300,7 +1319,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); } else if (strcmp(varname, "hlsearch") == 0) { no_hlsearch = !v->di_tv.vval.v_number; - redraw_all_later(SOME_VALID); + redraw_all_later(UPD_SOME_VALID); } return; } else if (v->di_tv.v_type != tv->v_type) { @@ -1330,7 +1349,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); - if (tv_dict_add(dict, v) == FAIL) { + if (hash_add(ht, (char *)v->di_key) == FAIL) { xfree(v); return; } @@ -1349,12 +1368,8 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, } if (watched) { - if (oldtv.v_type == VAR_UNKNOWN) { - tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); - } else { - tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); - tv_clear(&oldtv); - } + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_clear(&oldtv); } if (is_const) { @@ -1409,6 +1424,26 @@ bool var_check_ro(const int flags, const char *name, size_t name_len) return true; } +/// Return true if di_flags "flags" indicates variable "name" is locked. +/// Also give an error message. +bool var_check_lock(const int flags, const char *name, size_t name_len) +{ + if (!(flags & DI_FLAGS_LOCK)) { + return false; + } + + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + + semsg(_("E1122: Variable is locked: %*s"), (int)name_len, name); + + return true; +} + /// Check whether variable is fixed (DI_FLAGS_FIX) /// /// Also gives an error message. @@ -1443,37 +1478,34 @@ bool var_check_fixed(const int flags, const char *name, size_t name_len) return false; } -// TODO(ZyX-I): move to eval/expressions - /// Check if name is a valid name to assign funcref to /// /// @param[in] name Possible function/funcref name. /// @param[in] new_var True if it is a name for a variable. /// -/// @return false in case of error, true in case of success. Also gives an +/// @return false in case of success, true in case of failure. Also gives an /// error message if appropriate. -bool var_check_func_name(const char *const name, const bool new_var) +bool var_wrong_func_name(const char *const name, const bool new_var) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { // Allow for w: b: s: and t:. - if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') - ? name[2] : name[0])) { + // Allow autoload variable. + if (!(vim_strchr("wbst", (uint8_t)name[0]) != NULL && name[1] == ':') + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0]) + && vim_strchr(name, '#') == NULL) { semsg(_("E704: Funcref variable name must start with a capital: %s"), name); - return false; + return true; } // Don't allow hiding a function. When "v" is not NULL we might be // assigning another function to the same var, the type is checked // below. if (new_var && function_exists(name, false)) { semsg(_("E705: Variable name conflicts with existing function: %s"), name); - return false; + return true; } - return true; + return false; } -// TODO(ZyX-I): move to eval/expressions - /// Check if a variable name is valid /// /// @param[in] varname Variable name to check. @@ -1627,7 +1659,7 @@ static void set_option_from_tv(const char *varname, typval_T *varp) strval = tv_get_string_buf_chk(varp, nbuf); } if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); + set_option_value_give_err(varname, numval, strval, OPT_LOCAL); } } @@ -1648,25 +1680,27 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) const char *varname = tv_get_string_chk(&argvars[off + 1]); typval_T *varp = &argvars[off + 2]; - if (win != NULL && varname != NULL && varp != NULL) { - bool need_switch_win = !(tp == curtab && win == curwin); - switchwin_T switchwin; - if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { - if (*varname == '&') { - set_option_from_tv(varname + 1, varp); - } else { - const size_t varname_len = strlen(varname); - char *const winvarname = xmalloc(varname_len + 3); - memcpy(winvarname, "w:", 2); - memcpy(winvarname + 2, varname, varname_len + 1); - set_var(winvarname, varname_len + 2, varp, true); - xfree(winvarname); - } - } - if (need_switch_win) { - restore_win(&switchwin, true); + if (win == NULL || varname == NULL) { + return; + } + + bool need_switch_win = !(tp == curtab && win == curwin); + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { + if (*varname == '&') { + set_option_from_tv(varname + 1, varp); + } else { + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); + set_var(winvarname, varname_len + 2, varp, true); + xfree(winvarname); } } + if (need_switch_win) { + restore_win(&switchwin, true); + } } bool var_exists(const char *var) @@ -1702,7 +1736,7 @@ bool var_exists(const char *var) } /// "gettabvar()" function -void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_gettabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const varname = tv_get_string_chk(&argvars[1]); tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); @@ -1716,19 +1750,19 @@ void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "gettabwinvar()" function -void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_gettabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { getwinvar(argvars, rettv, 1); } /// "getwinvar()" function -void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_getwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { getwinvar(argvars, rettv, 0); } /// "getbufvar()" function -void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_getbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const varname = tv_get_string_chk(&argvars[1]); buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); @@ -1737,7 +1771,7 @@ void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "settabvar()" function -void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = 0; @@ -1749,38 +1783,40 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const varname = tv_get_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; - if (varname != NULL && tp != NULL) { - tabpage_T *const save_curtab = curtab; - goto_tabpage_tp(tp, false, false); + if (varname == NULL || tp == NULL) { + return; + } - const size_t varname_len = strlen(varname); - char *const tabvarname = xmalloc(varname_len + 3); - memcpy(tabvarname, "t:", 2); - memcpy(tabvarname + 2, varname, varname_len + 1); - set_var(tabvarname, varname_len + 2, varp, true); - xfree(tabvarname); - - // Restore current tabpage. - if (valid_tabpage(save_curtab)) { - goto_tabpage_tp(save_curtab, false, false); - } + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); + + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varname_len + 2, varp, true); + xfree(tabvarname); + + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); } } /// "settabwinvar()" function -void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_settabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { setwinvar(argvars, rettv, 1); } /// "setwinvar()" function -void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_setwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { setwinvar(argvars, rettv, 0); } /// "setbufvar()" function -void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_setbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (check_secure() || !tv_check_str_or_nr(&argvars[0])) { @@ -1790,27 +1826,29 @@ void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf_T *const buf = tv_get_buf(&argvars[0], false); typval_T *varp = &argvars[2]; - if (buf != NULL && varname != NULL) { - if (*varname == '&') { - aco_save_T aco; + if (buf == NULL || varname == NULL) { + return; + } - // set curbuf to be our buf, temporarily - aucmd_prepbuf(&aco, buf); + if (*varname == '&') { + aco_save_T aco; - set_option_from_tv(varname + 1, varp); + // Set curbuf to be our buf, temporarily. + aucmd_prepbuf(&aco, buf); - // reset notion of buffer - aucmd_restbuf(&aco); - } else { - const size_t varname_len = STRLEN(varname); - char *const bufvarname = xmalloc(varname_len + 3); - buf_T *const save_curbuf = curbuf; - curbuf = buf; - memcpy(bufvarname, "b:", 2); - memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varname_len + 2, varp, true); - xfree(bufvarname); - curbuf = save_curbuf; - } + set_option_from_tv(varname + 1, varp); + + // reset notion of buffer + aucmd_restbuf(&aco); + } else { + const size_t varname_len = strlen(varname); + char *const bufvarname = xmalloc(varname_len + 3); + buf_T *const save_curbuf = curbuf; + curbuf = buf; + memcpy(bufvarname, "b:", 2); + memcpy(bufvarname + 2, varname, varname_len + 1); + set_var(bufvarname, varname_len + 2, varp, true); + xfree(bufvarname); + curbuf = save_curbuf; } } diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h index 73efc4938a..b87c9d62cb 100644 --- a/src/nvim/eval/vars.h +++ b/src/nvim/eval/vars.h @@ -1,7 +1,7 @@ #ifndef NVIM_EVAL_VARS_H #define NVIM_EVAL_VARS_H -#include "nvim/ex_cmds_defs.h" // For exarg_T +#include "nvim/ex_cmds_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/vars.h.generated.h" diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c new file mode 100644 index 0000000000..f58a0c488a --- /dev/null +++ b/src/nvim/eval/window.c @@ -0,0 +1,954 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// eval/window.c: Window related builtin functions + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "nvim/ascii.h" +#include "nvim/autocmd.h" +#include "nvim/buffer.h" +#include "nvim/buffer_defs.h" +#include "nvim/cursor.h" +#include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/eval/window.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/macros.h" +#include "nvim/memline_defs.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/move.h" +#include "nvim/option_defs.h" +#include "nvim/pos.h" +#include "nvim/types.h" +#include "nvim/vim.h" +#include "nvim/window.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/window.c.generated.h" +#endif + +static char *e_invalwindow = N_("E957: Invalid window number"); +static char e_cannot_resize_window_in_another_tab_page[] + = N_("E1308: Cannot resize a window in another tab page"); + +static int win_getid(typval_T *argvars) +{ + if (argvars[0].v_type == VAR_UNKNOWN) { + return curwin->handle; + } + int winnr = (int)tv_get_number(&argvars[0]); + win_T *wp; + if (winnr <= 0) { + return 0; + } + + if (argvars[1].v_type == VAR_UNKNOWN) { + wp = firstwin; + } else { + tabpage_T *tp = NULL; + int tabnr = (int)tv_get_number(&argvars[1]); + FOR_ALL_TABS(tp2) { + if (--tabnr == 0) { + tp = tp2; + break; + } + } + if (tp == NULL) { + return -1; + } + if (tp == curtab) { + wp = firstwin; + } else { + wp = tp->tp_firstwin; + } + } + for (; wp != NULL; wp = wp->w_next) { + if (--winnr == 0) { + return wp->handle; + } + } + return 0; +} + +static void win_id2tabwin(typval_T *const argvars, typval_T *const rettv) +{ + handle_T id = (handle_T)tv_get_number(&argvars[0]); + + int winnr = 1; + int tabnr = 1; + win_get_tabwin(id, &tabnr, &winnr); + + list_T *const list = tv_list_alloc_ret(rettv, 2); + tv_list_append_number(list, tabnr); + tv_list_append_number(list, winnr); +} + +win_T *win_id2wp(int id) +{ + return win_id2wp_tp(id, NULL); +} + +/// Return the window and tab pointer of window "id". +win_T *win_id2wp_tp(int id, tabpage_T **tpp) +{ + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->handle == id) { + if (tpp != NULL) { + *tpp = tp; + } + return wp; + } + } + + return NULL; +} + +static int win_id2win(typval_T *argvars) +{ + int nr = 1; + int id = (int)tv_get_number(&argvars[0]); + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->handle == id) { + return nr; + } + nr++; + } + return 0; +} + +void win_findbuf(typval_T *argvars, list_T *list) +{ + int bufnr = (int)tv_get_number(&argvars[0]); + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer->b_fnum == bufnr) { + tv_list_append_number(list, wp->handle); + } + } +} + +/// Find window specified by "vp" in tabpage "tp". +/// +/// @param tp NULL for current tab page +/// @return current window if "vp" is number zero. +/// NULL if not found. +win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr < 0) { + return NULL; + } + + if (nr == 0) { + return curwin; + } + + // This method accepts NULL as an alias for curtab. + if (tp == NULL) { + tp = curtab; + } + + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (nr >= LOWEST_WIN_ID) { + if (wp->handle == nr) { + return wp; + } + } else if (--nr <= 0) { + return wp; + } + } + return NULL; +} + +/// Find a window: When using a Window ID in any tab page, when using a number +/// in the current tab page. +win_T *find_win_by_nr_or_id(typval_T *vp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr >= LOWEST_WIN_ID) { + return win_id2wp((int)tv_get_number(vp)); + } + + return find_win_by_nr(vp, NULL); +} + +/// Find window specified by "wvp" in tabpage "tvp". +win_T *find_tabwin(typval_T *wvp, typval_T *tvp) +{ + win_T *wp = NULL; + tabpage_T *tp = NULL; + + if (wvp->v_type != VAR_UNKNOWN) { + if (tvp->v_type != VAR_UNKNOWN) { + long n = tv_get_number(tvp); + if (n >= 0) { + tp = find_tabpage((int)n); + } + } else { + tp = curtab; + } + + if (tp != NULL) { + wp = find_win_by_nr(wvp, tp); + } + } else { + wp = curwin; + } + + return wp; +} + +/// Get the layout of the given tab page for winlayout(). +static void get_framelayout(const frame_T *fr, list_T *l, bool outer) +{ + if (fr == NULL) { + return; + } + + list_T *fr_list; + if (outer) { + // outermost call from f_winlayout() + fr_list = l; + } else { + fr_list = tv_list_alloc(2); + tv_list_append_list(l, fr_list); + } + + if (fr->fr_layout == FR_LEAF) { + if (fr->fr_win != NULL) { + tv_list_append_string(fr_list, "leaf", -1); + tv_list_append_number(fr_list, fr->fr_win->handle); + } + } else { + tv_list_append_string(fr_list, fr->fr_layout == FR_ROW ? "row" : "col", -1); + + list_T *const win_list = tv_list_alloc(kListLenUnknown); + tv_list_append_list(fr_list, win_list); + const frame_T *child = fr->fr_child; + while (child != NULL) { + get_framelayout(child, win_list, false); + child = child->fr_next; + } + } +} + +/// Common code for tabpagewinnr() and winnr(). +static int get_winnr(tabpage_T *tp, typval_T *argvar) +{ + int nr = 1; + + win_T *twin = (tp == curtab) ? curwin : tp->tp_curwin; + if (argvar->v_type != VAR_UNKNOWN) { + bool invalid_arg = false; + const char *const arg = tv_get_string_chk(argvar); + if (arg == NULL) { + nr = 0; // Type error; errmsg already given. + } else if (strcmp(arg, "$") == 0) { + twin = (tp == curtab) ? lastwin : tp->tp_lastwin; + } else if (strcmp(arg, "#") == 0) { + twin = (tp == curtab) ? prevwin : tp->tp_prevwin; + if (twin == NULL) { + nr = 0; + } + } else { + // Extract the window count (if specified). e.g. winnr('3j') + char *endp; + long count = strtol((char *)arg, &endp, 10); + if (count <= 0) { + // if count is not specified, default to 1 + count = 1; + } + if (endp != NULL && *endp != '\0') { + if (strequal(endp, "j")) { + twin = win_vert_neighbor(tp, twin, false, count); + } else if (strequal(endp, "k")) { + twin = win_vert_neighbor(tp, twin, true, count); + } else if (strequal(endp, "h")) { + twin = win_horz_neighbor(tp, twin, true, count); + } else if (strequal(endp, "l")) { + twin = win_horz_neighbor(tp, twin, false, count); + } else { + invalid_arg = true; + } + } else { + invalid_arg = true; + } + } + + if (invalid_arg) { + semsg(_(e_invexpr2), arg); + nr = 0; + } + } + + if (nr <= 0) { + return 0; + } + + for (win_T *wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + wp != twin; wp = wp->w_next) { + if (wp == NULL) { + // didn't find it in this tabpage + nr = 0; + break; + } + nr++; + } + return nr; +} + +/// @return information about a window as a dictionary. +static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) +{ + dict_T *const dict = tv_dict_alloc(); + + // make sure w_botline is valid + validate_botline(wp); + + tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); + tv_dict_add_nr(dict, S_LEN("winnr"), winnr); + tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); + tv_dict_add_nr(dict, S_LEN("height"), wp->w_height_inner); + tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1); + tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline); + tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1); + tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height); + tv_dict_add_nr(dict, S_LEN("width"), wp->w_width_inner); + tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); + tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); + tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp)); + tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("loclist"), + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); + + // Add a reference to window variables + tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); + + return dict; +} + +/// @return information (variables, options, etc.) about a tab page +/// as a dictionary. +static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) +{ + dict_T *const dict = tv_dict_alloc(); + + tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); + + list_T *const l = tv_list_alloc(kListLenMayKnow); + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + tv_list_append_number(l, (varnumber_T)wp->handle); + } + tv_dict_add_list(dict, S_LEN("windows"), l); + + // Make a reference to tabpage variables + tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); + + return dict; +} + +/// "gettabinfo()" function +void f_gettabinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tabpage_T *tparg = NULL; + + tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN + ? 1 + : kListLenMayKnow)); + + if (argvars[0].v_type != VAR_UNKNOWN) { + // Information about one tab page + tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + if (tparg == NULL) { + return; + } + } + + // Get information about a specific tab page or all tab pages + int tpnr = 0; + FOR_ALL_TABS(tp) { + tpnr++; + if (tparg != NULL && tp != tparg) { + continue; + } + dict_T *const d = get_tabpage_info(tp, tpnr); + tv_list_append_dict(rettv->vval.v_list, d); + if (tparg != NULL) { + return; + } + } +} + +/// "getwininfo()" function +void f_getwininfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_T *wparg = NULL; + + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (argvars[0].v_type != VAR_UNKNOWN) { + wparg = win_id2wp((int)tv_get_number(&argvars[0])); + if (wparg == NULL) { + return; + } + } + + // Collect information about either all the windows across all the tab + // pages or one particular window. + int16_t tabnr = 0; + FOR_ALL_TABS(tp) { + tabnr++; + int16_t winnr = 0; + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + winnr++; + if (wparg != NULL && wp != wparg) { + continue; + } + dict_T *const d = get_win_info(wp, tabnr, winnr); + tv_list_append_dict(rettv->vval.v_list, d); + if (wparg != NULL) { + // found information about a specific window + return; + } + } + } +} + +/// "getwinpos({timeout})" function +void f_getwinpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, 2); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); +} + +/// "getwinposx()" function +void f_getwinposx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; +} + +/// "getwinposy()" function +void f_getwinposy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; +} + +/// "tabpagenr()" function +void f_tabpagenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + int nr = 1; + + if (argvars[0].v_type != VAR_UNKNOWN) { + const char *const arg = tv_get_string_chk(&argvars[0]); + nr = 0; + if (arg != NULL) { + if (strcmp(arg, "$") == 0) { + nr = tabpage_index(NULL) - 1; + } else if (strcmp(arg, "#") == 0) { + nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0; + } else { + semsg(_(e_invexpr2), arg); + } + } + } else { + nr = tabpage_index(curtab); + } + rettv->vval.v_number = nr; +} + +/// "tabpagewinnr()" function +void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + int nr = 1; + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp == NULL) { + nr = 0; + } else { + nr = get_winnr(tp, &argvars[1]); + } + rettv->vval.v_number = nr; +} + +/// "win_execute(win_id, command)" function +void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + // Return an empty string if something fails. + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + int id = (int)tv_get_number(argvars); + tabpage_T *tp; + win_T *wp = win_id2wp_tp(id, &tp); + if (wp == NULL || tp == NULL) { + return; + } + + WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, 1)); +} + +/// "win_findbuf()" function +void f_win_findbuf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + win_findbuf(argvars, rettv->vval.v_list); +} + +/// "win_getid()" function +void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = win_getid(argvars); +} + +/// "win_gotoid()" function +void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + int id = (int)tv_get_number(&argvars[0]); + + if (cmdwin_type != 0) { + emsg(_(e_cmdwin)); + return; + } + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->handle == id) { + goto_tabpage_win(tp, wp); + rettv->vval.v_number = 1; + return; + } + } +} + +/// "win_id2tabwin()" function +void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_id2tabwin(argvars, rettv); +} + +/// "win_id2win()" function +void f_win_id2win(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = win_id2win(argvars); +} + +/// "win_move_separator()" function +void f_win_move_separator(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = false; + + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL || wp->w_floating) { + return; + } + if (!win_valid(wp)) { + emsg(_(e_cannot_resize_window_in_another_tab_page)); + return; + } + + int offset = (int)tv_get_number(&argvars[1]); + win_drag_vsep_line(wp, offset); + rettv->vval.v_number = true; +} + +/// "win_move_statusline()" function +void f_win_move_statusline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_T *wp; + int offset; + + rettv->vval.v_number = false; + + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL || wp->w_floating) { + return; + } + if (!win_valid(wp)) { + emsg(_(e_cannot_resize_window_in_another_tab_page)); + return; + } + + offset = (int)tv_get_number(&argvars[1]); + win_drag_status_line(wp, offset); + rettv->vval.v_number = true; +} + +/// "win_screenpos()" function +void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, 2); + const win_T *const wp = find_win_by_nr_or_id(&argvars[0]); + tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1); + tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); +} + +/// Move the window wp into a new split of targetwin in a given direction +static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags) +{ + int height = wp->w_height; + win_T *oldwin = curwin; + + if (wp == targetwin || is_aucmd_win(wp)) { + return; + } + + // Jump to the target window + if (curwin != targetwin) { + win_goto(targetwin); + } + + // Remove the old window and frame from the tree of frames + int dir; + (void)winframe_remove(wp, &dir, NULL); + win_remove(wp, NULL); + last_status(false); // may need to remove last status line + (void)win_comp_pos(); // recompute window positions + + // Split a window on the desired side and put the old window there + (void)win_split_ins(size, flags, wp, dir); + + // If splitting horizontally, try to preserve height + if (size == 0 && !(flags & WSP_VERT)) { + win_setheight_win(height, wp); + if (p_ea) { + win_equal(wp, true, 'v'); + } + } + + if (oldwin != curwin) { + win_goto(oldwin); + } +} + +/// "win_splitmove()" function +void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + win_T *targetwin = find_win_by_nr_or_id(&argvars[1]); + + if (wp == NULL || targetwin == NULL || wp == targetwin + || !win_valid(wp) || !win_valid(targetwin) + || win_valid_floating(wp) || win_valid_floating(targetwin)) { + emsg(_(e_invalwindow)); + rettv->vval.v_number = -1; + return; + } + + int flags = 0, size = 0; + + if (argvars[2].v_type != VAR_UNKNOWN) { + dict_T *d; + dictitem_T *di; + + if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) { + emsg(_(e_invarg)); + return; + } + + d = argvars[2].vval.v_dict; + if (tv_dict_get_number(d, "vertical")) { + flags |= WSP_VERT; + } + if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) { + flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE; + } + size = (int)tv_dict_get_number(d, "size"); + } + + win_move_into_split(wp, targetwin, size, flags); +} + +/// "win_gettype(nr)" function +void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_T *wp = curwin; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (argvars[0].v_type != VAR_UNKNOWN) { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + rettv->vval.v_string = xstrdup("unknown"); + return; + } + } + if (is_aucmd_win(wp)) { + rettv->vval.v_string = xstrdup("autocmd"); + } else if (wp->w_p_pvw) { + rettv->vval.v_string = xstrdup("preview"); + } else if (wp->w_floating) { + rettv->vval.v_string = xstrdup("popup"); + } else if (wp == curwin && cmdwin_type != 0) { + rettv->vval.v_string = xstrdup("command"); + } else if (bt_quickfix(wp->w_buffer)) { + rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix")); + } +} + +/// "getcmdwintype()" function +void f_getcmdwintype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + rettv->vval.v_string = xmallocz(1); + rettv->vval.v_string[0] = (char)cmdwin_type; +} + +/// "winbufnr(nr)" function +void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + rettv->vval.v_number = -1; + } else { + rettv->vval.v_number = wp->w_buffer->b_fnum; + } +} + +/// "wincol()" function +void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + validate_cursor(); + rettv->vval.v_number = curwin->w_wcol + 1; +} + +/// "winheight(nr)" function +void f_winheight(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + rettv->vval.v_number = -1; + } else { + rettv->vval.v_number = wp->w_height_inner; + } +} + +/// "winlayout()" function +void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tabpage_T *tp; + + tv_list_alloc_ret(rettv, 2); + + if (argvars[0].v_type == VAR_UNKNOWN) { + tp = curtab; + } else { + tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp == NULL) { + return; + } + } + + get_framelayout(tp->tp_topframe, rettv->vval.v_list, true); +} + +/// "winline()" function +void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + validate_cursor(); + rettv->vval.v_number = curwin->w_wrow + 1; +} + +/// "winnr()" function +void f_winnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = get_winnr(curtab, &argvars[0]); +} + +/// "winrestcmd()" function +void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + char buf[50]; + + garray_T ga; + ga_init(&ga, (int)sizeof(char), 70); + + // Do this twice to handle some window layouts properly. + for (int i = 0; i < 2; i++) { + int winnr = 1; + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + snprintf(buf, sizeof(buf), "%dresize %d|", winnr, + wp->w_height); + ga_concat(&ga, buf); + snprintf(buf, sizeof(buf), "vert %dresize %d|", winnr, + wp->w_width); + ga_concat(&ga, buf); + winnr++; + } + } + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; + rettv->v_type = VAR_STRING; +} + +/// "winrestview()" function +void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + dict_T *dict = argvars[0].vval.v_dict; + + if (argvars[0].v_type != VAR_DICT || dict == NULL) { + emsg(_(e_invarg)); + } else { + dictitem_T *di; + if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { + curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv); + } + if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { + curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv); + } + if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { + curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv); + } + if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { + curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv); + curwin->w_set_curswant = false; + } + if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { + set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv)); + } + if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { + curwin->w_topfill = (int)tv_get_number(&di->di_tv); + } + if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { + curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv); + } + if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { + curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv); + } + + check_cursor(); + win_new_height(curwin, curwin->w_height); + win_new_width(curwin, curwin->w_width); + changed_window_setting(); + + if (curwin->w_topline <= 0) { + curwin->w_topline = 1; + } + if (curwin->w_topline > curbuf->b_ml.ml_line_count) { + curwin->w_topline = curbuf->b_ml.ml_line_count; + } + check_topfill(curwin, true); + } +} + +/// "winsaveview()" function +void f_winsaveview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_dict_alloc_ret(rettv); + dict_T *dict = rettv->vval.v_dict; + + tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum); + tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col); + tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd); + update_curswant(); + tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant); + + tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline); + tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill); + tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol); + tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); +} + +/// "winwidth(nr)" function +void f_winwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + rettv->vval.v_number = -1; + } else { + rettv->vval.v_number = wp->w_width_inner; + } +} + +/// Set "win" to be the curwin and "tp" to be the current tab page. +/// restore_win() MUST be called to undo, also when FAIL is returned. +/// No autocommands will be executed until restore_win() is called. +/// +/// @param no_display if true the display won't be affected, no redraw is +/// triggered, another tabpage access is limited. +/// +/// @return FAIL if switching to "win" failed. +int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display) +{ + block_autocmds(); + return switch_win_noblock(switchwin, win, tp, no_display); +} + +// As switch_win() but without blocking autocommands. +int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display) +{ + CLEAR_POINTER(switchwin); + switchwin->sw_curwin = curwin; + if (win == curwin) { + switchwin->sw_same_win = true; + } else { + // Disable Visual selection, because redrawing may fail. + switchwin->sw_visual_active = VIsual_active; + VIsual_active = false; + } + + if (tp != NULL) { + switchwin->sw_curtab = curtab; + if (no_display) { + curtab->tp_firstwin = firstwin; + curtab->tp_lastwin = lastwin; + curtab = tp; + firstwin = curtab->tp_firstwin; + lastwin = curtab->tp_lastwin; + } else { + goto_tabpage_tp(tp, false, false); + } + } + if (!win_valid(win)) { + return FAIL; + } + curwin = win; + curbuf = curwin->w_buffer; + return OK; +} + +// Restore current tabpage and window saved by switch_win(), if still valid. +// When "no_display" is true the display won't be affected, no redraw is +// triggered. +void restore_win(switchwin_T *switchwin, bool no_display) +{ + restore_win_noblock(switchwin, no_display); + unblock_autocmds(); +} + +// As restore_win() but without unblocking autocommands. +void restore_win_noblock(switchwin_T *switchwin, bool no_display) +{ + if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) { + if (no_display) { + curtab->tp_firstwin = firstwin; + curtab->tp_lastwin = lastwin; + curtab = switchwin->sw_curtab; + firstwin = curtab->tp_firstwin; + lastwin = curtab->tp_lastwin; + } else { + goto_tabpage_tp(switchwin->sw_curtab, false, false); + } + } + + if (!switchwin->sw_same_win) { + VIsual_active = switchwin->sw_visual_active; + } + + if (win_valid(switchwin->sw_curwin)) { + curwin = switchwin->sw_curwin; + curbuf = curwin->w_buffer; + } +} diff --git a/src/nvim/eval/window.h b/src/nvim/eval/window.h new file mode 100644 index 0000000000..995f0a55a9 --- /dev/null +++ b/src/nvim/eval/window.h @@ -0,0 +1,78 @@ +#ifndef NVIM_EVAL_WINDOW_H +#define NVIM_EVAL_WINDOW_H + +#include <stdbool.h> +#include <string.h> + +#include "nvim/buffer.h" +#include "nvim/buffer_defs.h" +#include "nvim/cursor.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/globals.h" +#include "nvim/mark.h" +#include "nvim/option_defs.h" +#include "nvim/os/os.h" +#include "nvim/pos.h" +#include "nvim/vim.h" +#include "nvim/window.h" + +/// Structure used by switch_win() to pass values to restore_win() +typedef struct { + win_T *sw_curwin; + tabpage_T *sw_curtab; + bool sw_same_win; ///< VIsual_active was not reset + bool sw_visual_active; +} switchwin_T; + +/// Execute a block of code in the context of window `wp` in tabpage `tp`. +/// Ensures the status line is redrawn and cursor position is valid if it is moved. +#define WIN_EXECUTE(wp, tp, block) \ + do { \ + win_T *const wp_ = (wp); \ + const pos_T curpos_ = wp_->w_cursor; \ + char cwd_[MAXPATHL]; \ + char autocwd_[MAXPATHL]; \ + bool apply_acd_ = false; \ + int cwd_status_ = FAIL; \ + /* Getting and setting directory can be slow on some systems, only do */ \ + /* this when the current or target window/tab have a local directory or */ \ + /* 'acd' is set. */ \ + if (curwin != wp \ + && (curwin->w_localdir != NULL || wp->w_localdir != NULL \ + || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \ + || p_acd)) { \ + cwd_status_ = os_dirname(cwd_, MAXPATHL); \ + } \ + /* If 'acd' is set, check we are using that directory. If yes, then */ \ + /* apply 'acd' afterwards, otherwise restore the current directory. */ \ + if (cwd_status_ == OK && p_acd) { \ + do_autochdir(); \ + apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && strcmp(cwd_, autocwd_) == 0; \ + } \ + switchwin_T switchwin_; \ + if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \ + check_cursor(); \ + block; \ + } \ + restore_win_noblock(&switchwin_, true); \ + if (apply_acd_) { \ + do_autochdir(); \ + } else if (cwd_status_ == OK) { \ + os_chdir(cwd_); \ + } \ + /* Update the status line if the cursor moved. */ \ + if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \ + wp_->w_redr_status = true; \ + } \ + /* In case the command moved the cursor or changed the Visual area, */ \ + /* check it is valid. */ \ + check_cursor(); \ + if (VIsual_active) { \ + check_pos(curbuf, &VIsual); \ + } \ + } while (false) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/window.h.generated.h" +#endif +#endif // NVIM_EVAL_WINDOW_H |