diff options
Diffstat (limited to 'src/nvim/eval/funcs.c')
-rw-r--r-- | src/nvim/eval/funcs.c | 2108 |
1 files changed, 1333 insertions, 775 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 1ba31bfe68..5569d74413 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -47,6 +47,7 @@ #include "nvim/os/input.h" #include "nvim/os/shell.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" @@ -79,22 +80,23 @@ KHASH_MAP_INIT_STR(functions, VimLFuncDef) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/funcs.c.generated.h" -#ifdef _MSC_VER +# ifdef _MSC_VER // This prevents MSVC from replacing the functions with intrinsics, // and causing errors when trying to get their addresses in funcs.generated.h -#pragma function(ceil) -#pragma function(floor) -#endif +# pragma function(ceil) +# pragma function(floor) +# endif PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH -#include "funcs.generated.h" +# include "funcs.generated.h" PRAGMA_DIAG_POP PRAGMA_DIAG_POP #endif static char *e_listarg = N_("E686: Argument of %s must be a List"); +static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob"); static char *e_invalwindow = N_("E957: Invalid window number"); /// Dummy va_list for passing to vim_snprintf @@ -113,12 +115,14 @@ char_u *get_function_name(expand_T *xp, int idx) static int intidx = -1; char_u *name; - if (idx == 0) + if (idx == 0) { intidx = -1; + } if (intidx < 0) { name = get_user_func_name(xp, idx); if (name != NULL) { - if (*name != '<' && STRNCMP("g:", xp->xp_pattern, 2) == 0) { + if (*name != NUL && *name != '<' + && STRNCMP("g:", xp->xp_pattern, 2) == 0) { return cat_prefix_varname('g', name); } return name; @@ -152,12 +156,14 @@ char_u *get_expr_name(expand_T *xp, int idx) static int intidx = -1; char_u *name; - if (idx == 0) + if (idx == 0) { intidx = -1; + } if (intidx < 0) { name = get_function_name(xp, idx); - if (name != NULL) + if (name != NULL) { return name; + } } return get_user_var_name(xp, ++intidx); } @@ -174,6 +180,52 @@ const VimLFuncDef *find_internal_func(const char *const name) return find_internal_func_gperf(name, len); } +int call_internal_func(const char_u *const fname, const int argcount, typval_T *const argvars, + typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL) { + return ERROR_UNKNOWN; + } else if (argcount < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount > fdef->max_argc) { + return ERROR_TOOMANY; + } + argvars[argcount].v_type = VAR_UNKNOWN; + fdef->func(argvars, rettv, fdef->data); + return ERROR_NONE; +} + +/// Invoke a method for base->method(). +int call_internal_method(const char_u *const fname, const int argcount, typval_T *const argvars, + typval_T *const rettv, typval_T *const basetv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL) { + return ERROR_UNKNOWN; + } else if (fdef->base_arg == BASE_NONE) { + return ERROR_NOTMETHOD; + } else if (argcount + 1 < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount + 1 > fdef->max_argc) { + return ERROR_TOOMANY; + } + + typval_T argv[MAX_FUNC_ARGS + 1]; + const ptrdiff_t base_index + = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; + memcpy(argv, argvars, base_index * sizeof(typval_T)); + argv[base_index] = *basetv; + memcpy(argv + base_index + 1, argvars + base_index, + (argcount - base_index) * sizeof(typval_T)); + argv[argcount + 1].v_type = VAR_UNKNOWN; + + fdef->func(argv, rettv, fdef->data); + return ERROR_NONE; +} + /* * Return TRUE for a non-zero Number and a non-empty String. */ @@ -196,7 +248,7 @@ static int non_zero_arg(typval_T *argvars) static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - float_T (*function)(float_T) = (float_T (*)(float_T))fptr; + float_T (*function)(float_T) = (float_T (*)(float_T)) fptr; rettv->v_type = VAR_FLOAT; if (tv_get_float_chk(argvars, &f)) { @@ -273,8 +325,20 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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)) { + bool error = false; + const varnumber_T n = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + ga_append(&b->bv_ga, (int)n); + tv_copy(&argvars[0], rettv); + } + } } else { - EMSG(_(e_listreq)); + EMSG(_(e_listblobreq)); } } @@ -382,8 +446,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; int idx = tv_get_number_chk(&argvars[0], NULL); if (arglist != NULL && idx >= 0 && idx < argcount) { - rettv->vval.v_string = (char_u *)xstrdup( - (const char *)alist_name(&arglist[idx])); + rettv->vval.v_string = (char_u *)xstrdup((const char *)alist_name(&arglist[idx])); } else if (idx == -1) { get_arglist_as_rettv(arglist, argcount, rettv); } @@ -425,13 +488,13 @@ static void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "assert_report(msg) static void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - garray_T ga; + garray_T ga; - prepare_assert_error(&ga); - ga_concat(&ga, (const char_u *)tv_get_string(&argvars[0])); - assert_error(&ga); - ga_clear(&ga); - rettv->vval.v_number = 1; + prepare_assert_error(&ga); + ga_concat(&ga, (const char_u *)tv_get_string(&argvars[0])); + assert_error(&ga); + ga_clear(&ga); + rettv->vval.v_number = 1; } /// "assert_exception(string[, msg])" function @@ -515,11 +578,11 @@ static void f_browsedir(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static buf_T *find_buffer(typval_T *avar) { - buf_T *buf = NULL; + buf_T *buf = NULL; - if (avar->v_type == VAR_NUMBER) + 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) { + } 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" @@ -560,7 +623,7 @@ static void f_bufexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - buf_T *buf; + buf_T *buf; buf = find_buffer(&argvars[0]); rettv->vval.v_number = (buf != NULL && buf->b_p_bl); @@ -586,7 +649,7 @@ static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr) */ static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - buf_T *buf; + buf_T *buf; buf = find_buffer(&argvars[0]); rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); @@ -688,19 +751,23 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ buf_T *tv_get_buf(typval_T *tv, int curtab_only) { - char_u *name = tv->vval.v_string; + char_u *name = tv->vval.v_string; int save_magic; - char_u *save_cpo; - buf_T *buf; + char_u *save_cpo; + buf_T *buf; - if (tv->v_type == VAR_NUMBER) + if (tv->v_type == VAR_NUMBER) { return buflist_findnr((int)tv->vval.v_number); - if (tv->v_type != VAR_STRING) + } + if (tv->v_type != VAR_STRING) { return NULL; - if (name == NULL || *name == NUL) + } + if (name == NULL || *name == NUL) { return curbuf; - if (name[0] == '$' && name[1] == NUL) + } + if (name[0] == '$' && name[1] == NUL) { return lastbuf; + } // Ignore 'magic' and 'cpoptions' here to make scripts portable save_magic = p_magic; @@ -736,7 +803,7 @@ buf_T *tv_get_buf_from_arg(typval_T *const tv) FUNC_ATTR_NONNULL_ALL /// Get the buffer from "arg" and give an error and return NULL if it is not /// valid. -buf_T * get_buf_arg(typval_T *arg) +buf_T *get_buf_arg(typval_T *arg) { buf_T *buf; @@ -814,9 +881,9 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) } bool owned = false; - char_u *func; - partial_T *partial = NULL; - dict_T *selfdict = NULL; + char_u *func; + partial_T *partial = NULL; + dict_T *selfdict = NULL; if (argvars[0].v_type == VAR_FUNC) { func = argvars[0].vval.v_string; } else if (argvars[0].v_type == VAR_PARTIAL) { @@ -866,7 +933,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type != VAR_STRING - && argvars[1].v_type != VAR_UNKNOWN)) { + && argvars[1].v_type != VAR_UNKNOWN)) { EMSG(_(e_invarg)); return; } @@ -911,7 +978,17 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ptrdiff_t input_len = 0; - char *input = save_tv_as_string(&argvars[1], &input_len, false); + char *input = NULL; + if (argvars[1].v_type == VAR_BLOB) { + const blob_T *const b = argvars[1].vval.v_blob; + input_len = tv_blob_len(b); + if (input_len > 0) { + input = xmemdup(b->bv_ga.ga_data, input_len); + } + } else { + input = save_tv_as_string(&argvars[1], &input_len, false); + } + if (!input) { // Either the error has been handled by save_tv_as_string(), // or there is no input to send. @@ -936,8 +1013,7 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - rettv->vval.v_number = utf_ptr2char( - (const char_u *)tv_get_string(&argvars[0])); + rettv->vval.v_number = utf_ptr2char((const char_u *)tv_get_string(&argvars[0])); } // "charidx()" function @@ -1000,11 +1076,12 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_c_indent(); curwin->w_cursor = pos; - } else + } else { rettv->vval.v_number = -1; + } } -static win_T * get_optional_window(typval_T *argvars, int idx) +static win_T *get_optional_window(typval_T *argvars, int idx) { win_T *win = curwin; @@ -1036,7 +1113,7 @@ static void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) { colnr_T col = 0; - pos_T *fp; + pos_T *fp; int fnum = curbuf->b_fnum; fp = var2fpos(&argvars[0], FALSE, &fnum); @@ -1053,14 +1130,17 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) // 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_u *p = get_cursor_pos_ptr(); - if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p, - curwin->w_virtcol - curwin->w_cursor.coladd)) { + if (curwin->w_cursor.coladd + >= (colnr_T)win_chartabsize(curwin, p, + (curwin->w_virtcol + - curwin->w_cursor.coladd))) { int l; - if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) + if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) { col += l; + } } } } @@ -1078,10 +1158,11 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - /* Check for undo allowed here, because if something was already inserted - * the line was already saved for undo and this check isn't done. */ - if (!undo_allowed()) + // Check for undo allowed here, because if something was already inserted + // the line was already saved for undo and this check isn't done. + if (!undo_allowed(curbuf)) { return; + } if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_invarg)); @@ -1165,11 +1246,16 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) error = true; } else { switch (TOUPPER_ASC(*typestr)) { - case 'E': type = VIM_ERROR; break; - case 'Q': type = VIM_QUESTION; break; - case 'I': type = VIM_INFO; break; - case 'W': type = VIM_WARNING; break; - case 'G': type = VIM_GENERIC; break; + case 'E': + type = VIM_ERROR; break; + case 'Q': + type = VIM_QUESTION; break; + case 'I': + type = VIM_INFO; break; + case 'W': + type = VIM_WARNING; break; + case 'G': + type = VIM_GENERIC; break; } } } @@ -1181,8 +1267,8 @@ 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, false); + rettv->vval.v_number = do_dialog(type, NULL, (char_u *)message, (char_u *)buttons, def, NULL, + false); } } @@ -1232,8 +1318,8 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else if (argvars[0].v_type == VAR_LIST) { - listitem_T *li; - list_T *l; + listitem_T *li; + list_T *l; long idx; if ((l = argvars[0].vval.v_list) != NULL) { @@ -1248,8 +1334,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - if (error) + if (error) { li = NULL; + } } for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { @@ -1260,8 +1347,8 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT) { int todo; - dict_T *d; - hashitem_T *hi; + dict_T *d; + hashitem_T *hi; if ((d = argvars[0].vval.v_dict) != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) { @@ -1715,7 +1802,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); + rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars))); } /* @@ -1780,53 +1867,45 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool n = true; switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_FUNC: { - n = argvars[0].vval.v_string == NULL - || *argvars[0].vval.v_string == NUL; - break; - } - case VAR_PARTIAL: { + case VAR_STRING: + case VAR_FUNC: + n = argvars[0].vval.v_string == NULL + || *argvars[0].vval.v_string == NUL; + break; + case VAR_PARTIAL: + n = false; + break; + case VAR_NUMBER: + n = argvars[0].vval.v_number == 0; + break; + case VAR_FLOAT: + n = argvars[0].vval.v_float == 0.0; + break; + case VAR_LIST: + n = (tv_list_len(argvars[0].vval.v_list) == 0); + break; + case VAR_DICT: + n = (tv_dict_len(argvars[0].vval.v_dict) == 0); + break; + case VAR_BOOL: + switch (argvars[0].vval.v_bool) { + case kBoolVarTrue: n = false; break; - } - case VAR_NUMBER: { - n = argvars[0].vval.v_number == 0; - break; - } - case VAR_FLOAT: { - n = argvars[0].vval.v_float == 0.0; - break; - } - case VAR_LIST: { - n = (tv_list_len(argvars[0].vval.v_list) == 0); - break; - } - case VAR_DICT: { - n = (tv_dict_len(argvars[0].vval.v_dict) == 0); - break; - } - case VAR_BOOL: { - switch (argvars[0].vval.v_bool) { - case kBoolVarTrue: { - n = false; - break; - } - case kBoolVarFalse: { - n = true; - break; - } - } - break; - } - case VAR_SPECIAL: { - n = argvars[0].vval.v_special == kSpecialVarNull; - break; - } - case VAR_UNKNOWN: { - internal_error("f_empty(UNKNOWN)"); + case kBoolVarFalse: + n = true; break; } + break; + case VAR_SPECIAL: + n = argvars[0].vval.v_special == kSpecialVarNull; + break; + case VAR_BLOB: + n = (tv_blob_len(argvars[0].vval.v_blob) == 0); + break; + case VAR_UNKNOWN: + internal_error("f_empty(UNKNOWN)"); + break; } rettv->vval.v_number = n; @@ -1886,9 +1965,8 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char buf[NUMBUFLEN]; - rettv->vval.v_string = 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((const char_u *)tv_get_string(&argvars[0]), + (const char_u *)tv_get_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -1921,7 +1999,7 @@ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (expr_start != NULL && !aborting()) { EMSG2(_(e_invexpr2), expr_start); } - need_clr_eos = FALSE; + need_clr_eos = false; rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; } else if (*s != NUL) { @@ -1969,8 +2047,7 @@ static char_u *get_list_line(int c, void *cookie, int indent, bool do_concat) return (char_u *)(s == NULL ? NULL : xstrdup(s)); } -static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, - int arg_off) +static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int arg_off) { const int save_msg_silent = msg_silent; const int save_emsg_silent = emsg_silent; @@ -2076,7 +2153,7 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Update the status line if the cursor moved. if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) { - wp->w_redr_status = true; + wp->w_redr_status = true; } } } @@ -2144,7 +2221,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { size_t len; - char_u *errormsg; + char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; expand_T xpc; bool error = false; @@ -2443,8 +2520,9 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) if (*fname != NUL && !error) { do { - if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) + if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) { xfree(fresult); + } fresult = find_file_in_path_option(first ? (char_u *)fname : NULL, first ? strlen(fname) : 0, 0, first, path, @@ -2460,8 +2538,9 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); } - if (rettv->v_type == VAR_STRING) + if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = fresult; + } } @@ -2528,8 +2607,7 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_string = (char_u *)vim_strsave_fnameescape( - tv_get_string(&argvars[0]), false); + rettv->vval.v_string = (char_u *)vim_strsave_fnameescape(tv_get_string(&argvars[0]), false); rettv->v_type = VAR_STRING; } @@ -2617,14 +2695,14 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T foldstart; - linenr_T foldend; - char_u *dashes; - linenr_T lnum; - char_u *s; - char_u *r; - int len; - char *txt; + linenr_T foldstart; + linenr_T foldend; + char_u *dashes; + linenr_T lnum; + char_u *s; + char_u *r; + int len; + char *txt; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -2647,8 +2725,9 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) s = skipwhite(s + 2); if (*skipwhite(s) == NUL && lnum + 1 < foldend) { s = skipwhite(ml_get(lnum + 1)); - if (*s == '*') + if (*s == '*') { s = skipwhite(s + 1); + } } } unsigned long count = (unsigned long)(foldend - foldstart + 1); @@ -2671,7 +2750,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *text; + char_u *text; char_u buf[FOLD_TEXT_LEN]; static bool entered = false; @@ -2733,14 +2812,30 @@ static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - listitem_T *li; - list_T *l; - dictitem_T *di; - dict_T *d; - typval_T *tv = NULL; + listitem_T *li; + list_T *l; + dictitem_T *di; + dict_T *d; + typval_T *tv = NULL; bool what_is_dict = false; - if (argvars[0].v_type == VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + int idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + rettv->v_type = VAR_NUMBER; + if (idx < 0) { + idx = tv_blob_len(argvars[0].vval.v_blob) + idx; + } + if (idx < 0 || idx >= tv_blob_len(argvars[0].vval.v_blob)) { + rettv->vval.v_number = -1; + } else { + rettv->vval.v_number = tv_blob_get(argvars[0].vval.v_blob, idx); + tv = rettv; + } + } + } else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; @@ -2801,7 +2896,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else { - EMSG2(_(e_listdictarg), "get()"); + EMSG2(_(e_listdictblobarg), "get()"); } if (tv == NULL) { @@ -2880,11 +2975,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) * buffer. * If 'retlist' is TRUE, then the lines are returned as a Vim List. */ -static void get_buffer_lines(buf_T *buf, - linenr_T start, - linenr_T end, - int retlist, - typval_T *rettv) +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; @@ -3027,10 +3118,9 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "getchar()" function - */ -static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +// "getchar()" and "getcharstr()" functions +static void getchar_common(typval_T *argvars, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL { varnumber_T n; bool error = false; @@ -3097,6 +3187,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { i += utf_char2bytes(n, temp + i); } + assert(i < 10); temp[i++] = NUL; rettv->v_type = VAR_STRING; rettv->vval.v_string = vim_strsave(temp); @@ -3105,21 +3196,21 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) int row = mouse_row; int col = mouse_col; int grid = mouse_grid; - win_T *win; linenr_T lnum; - win_T *wp; + win_T *wp; int winnr = 1; if (row >= 0 && col >= 0) { - /* Find the window at the mouse coordinates and compute the - * text position. */ - win = mouse_find_win(&grid, &row, &col); + // Find the window at the mouse coordinates and compute the + // text position. + win_T *const win = mouse_find_win(&grid, &row, &col); if (win == NULL) { return; } (void)mouse_comp_pos(win, &row, &col, &lnum); - for (wp = firstwin; wp != win; wp = wp->w_next) + for (wp = firstwin; wp != win; wp = wp->w_next) { ++winnr; + } set_vim_var_nr(VV_MOUSE_WIN, winnr); set_vim_var_nr(VV_MOUSE_WINID, wp->handle); set_vim_var_nr(VV_MOUSE_LNUM, lnum); @@ -3129,6 +3220,32 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "getchar()" function +static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getchar_common(argvars, rettv); +} + +// "getcharstr()" function +static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getchar_common(argvars, rettv); + + if (rettv->v_type == VAR_NUMBER) { + char_u temp[7]; // mbyte-char: 6, NUL: 1 + const varnumber_T n = rettv->vval.v_number; + int i = 0; + + if (n != 0) { + i += utf_char2bytes(n, temp); + } + assert(i < 7); + temp[i++] = NUL; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(temp); + } +} + /* * "getcharmod()" function */ @@ -3192,11 +3309,11 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "getcompletion()" function static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat; - expand_T xpc; - bool filtered = false; - int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH - | WILD_NO_BEEP; + char_u *pat; + expand_T xpc; + bool filtered = false; + int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH + | WILD_NO_BEEP; if (argvars[1].v_type != VAR_STRING) { EMSG2(_(e_invarg2), "type must be a string"); @@ -3290,7 +3407,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *from = NULL; // The original string to copy tabpage_T *tp = curtab; // The tabpage to look at. - win_T *win = curwin; // The window to look at. + win_T *win = curwin; // The window to look at. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -3353,29 +3470,29 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) cwd = xmalloc(MAXPATHL); switch (scope) { - case kCdScopeWindow: - assert(win); - from = win->w_localdir; - if (from) { - break; - } - FALLTHROUGH; - case kCdScopeTab: - assert(tp); - from = tp->tp_localdir; - if (from) { - break; - } - FALLTHROUGH; - case kCdScopeGlobal: - if (globaldir) { // `globaldir` is not always set. - from = globaldir; - } else if (os_dirname(cwd, MAXPATHL) == FAIL) { // Get the OS CWD. - from = (char_u *)""; // Return empty string on failure. - } + case kCdScopeWindow: + assert(win); + from = win->w_localdir; + if (from) { + break; + } + FALLTHROUGH; + case kCdScopeTab: + assert(tp); + from = tp->tp_localdir; + if (from) { break; - case kCdScopeInvalid: // We should never get here - abort(); + } + FALLTHROUGH; + case kCdScopeGlobal: + if (globaldir) { // `globaldir` is not always set. + from = globaldir; + } else if (os_dirname(cwd, MAXPATHL) == FAIL) { // Get the OS CWD. + from = (char_u *)""; // Return empty string on failure. + } + break; + case kCdScopeInvalid: // We should never get here + abort(); } if (from) { @@ -3468,8 +3585,8 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *type = NULL; - char *t; + char_u *type = NULL; + char *t; const char *fname = tv_get_string(&argvars[0]); @@ -3596,7 +3713,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (cur->match.regprog == NULL) { // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; i++) { - llpos_T *llpos; + llpos_T *llpos; char buf[30]; // use 30 to avoid compiler warning llpos = &cur->pos.pos[i]; @@ -3633,6 +3750,59 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "getmousepos()" function +void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + dict_T *d; + win_T *wp; + int row = mouse_row; + int col = mouse_col; + int grid = mouse_grid; + varnumber_T winid = 0; + varnumber_T winrow = 0; + varnumber_T wincol = 0; + linenr_T line = 0; + varnumber_T column = 0; + + tv_dict_alloc_ret(rettv); + d = rettv->vval.v_dict; + + tv_dict_add_nr(d, S_LEN("screenrow"), (varnumber_T)mouse_row + 1); + tv_dict_add_nr(d, S_LEN("screencol"), (varnumber_T)mouse_col + 1); + + wp = mouse_find_win(&grid, &row, &col); + if (wp != NULL) { + int height = wp->w_height + wp->w_status_height; + // The height is adjusted by 1 when there is a bottom border. This is not + // necessary for a top border since `row` starts at -1 in that case. + if (row < height + wp->w_border_adj[2]) { + winid = wp->handle; + winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border + wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border + if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { + char_u *p; + int count; + + mouse_comp_pos(wp, &row, &col, &line); + + // limit to text length plus one + p = ml_get_buf(wp->w_buffer, line, false); + count = (int)STRLEN(p); + if (col > count) { + col = count; + } + + column = col + 1; + } + } + } + tv_dict_add_nr(d, S_LEN("winid"), winid); + tv_dict_add_nr(d, S_LEN("winrow"), winrow); + tv_dict_add_nr(d, S_LEN("wincol"), wincol); + tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)line); + tv_dict_add_nr(d, S_LEN("column"), column); +} + /* * "getpid()" function */ @@ -3657,12 +3827,10 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0)); - tv_list_append_number( - l, ((fp != NULL) + tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0)); - tv_list_append_number( - l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); + : (varnumber_T)0)); + tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { const int save_set_curswant = curwin->w_set_curswant; const colnr_T save_curswant = curwin->w_curswant; @@ -3867,18 +4035,18 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "gettagstack()" function static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *wp = curwin; // default is current window + win_T *wp = curwin; // default is current window - tv_dict_alloc_ret(rettv); + tv_dict_alloc_ret(rettv); - if (argvars[0].v_type != VAR_UNKNOWN) { - wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL) { - return; - } + if (argvars[0].v_type != VAR_UNKNOWN) { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + return; } + } - get_tagstack(wp, rettv->vval.v_dict); + get_tagstack(wp, rettv->vval.v_dict); } /// "getwininfo()" function @@ -3996,12 +4164,11 @@ static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) // // 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) +static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags) { - int dir; - int height = wp->w_height; - win_T *oldwin = curwin; + int dir; + int height = wp->w_height; + win_T *oldwin = curwin; if (wp == targetwin) { return; @@ -4037,9 +4204,9 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, // "win_splitmove()" function static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *wp; - win_T *targetwin; - int flags = 0, size = 0; + win_T *wp; + win_T *targetwin; + int flags = 0, size = 0; wp = find_win_by_nr_or_id(&argvars[0]); targetwin = find_win_by_nr_or_id(&argvars[1]); @@ -4053,8 +4220,8 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[2].v_type != VAR_UNKNOWN) { - dict_T *d; - dictitem_T *di; + dict_T *d; + dictitem_T *di; if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) { EMSG(_(e_invarg)); @@ -4133,11 +4300,12 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; - if (p_wic) + if (p_wic) { options += WILD_ICASE; + } if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne( - &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); + rettv->vval.v_string = ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, + WILD_ALL); } else { ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); @@ -4148,8 +4316,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ExpandCleanup(&xpc); } - } else + } else { rettv->vval.v_string = NULL; + } } /// "globpath()" function @@ -4327,6 +4496,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "user_commands", "vartabs", "vertsplit", + "vimscript-1", "virtualedit", "visual", "visualextra", @@ -4408,8 +4578,9 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_dictreq)); return; } - if (argvars[0].vval.v_dict == NULL) + if (argvars[0].vval.v_dict == NULL) { return; + } rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, tv_get_string(&argvars[1]), @@ -4440,7 +4611,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) }; tabpage_T *tp = curtab; // The tabpage to look at. - win_T *win = curwin; // The window to look at. + win_T *win = curwin; // The window to look at. rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -4499,20 +4670,20 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } switch (scope) { - case kCdScopeWindow: - assert(win); - rettv->vval.v_number = win->w_localdir ? 1 : 0; - break; - case kCdScopeTab: - assert(tp); - rettv->vval.v_number = tp->tp_localdir ? 1 : 0; - break; - case kCdScopeGlobal: - // The global scope never has a local directory - break; - case kCdScopeInvalid: - // We should never get here - abort(); + case kCdScopeWindow: + assert(win); + rettv->vval.v_number = win->w_localdir ? 1 : 0; + break; + case kCdScopeTab: + assert(tp); + rettv->vval.v_number = tp->tp_localdir ? 1 : 0; + break; + case kCdScopeGlobal: + // The global scope never has a local directory + break; + case kCdScopeInvalid: + // We should never get here + abort(); } } @@ -4635,8 +4806,7 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = syn_name2id( - (const char_u *)tv_get_string(&argvars[0])); + rettv->vval.v_number = syn_name2id((const char_u *)tv_get_string(&argvars[0])); } /* @@ -4644,8 +4814,7 @@ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = highlight_exists( - (const char_u *)tv_get_string(&argvars[0])); + rettv->vval.v_number = highlight_exists((const char_u *)tv_get_string(&argvars[0])); } /* @@ -4672,11 +4841,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_u *const from = enc_canonize(enc_skip((char_u *)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_u *const to = enc_canonize(enc_skip((char_u *)tv_get_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); @@ -4714,8 +4881,38 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool ic = false; rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) { - EMSG(_(e_listreq)); + if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + int start = 0; + + if (argvars[2].v_type != VAR_UNKNOWN) { + start = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + } + blob_T *const b = argvars[0].vval.v_blob; + if (b == NULL) { + return; + } + if (start < 0) { + start = tv_blob_len(b) + start; + if (start < 0) { + start = 0; + } + } + for (idx = start; idx < tv_blob_len(b); idx++) { + typval_T tv; + tv.v_type = VAR_NUMBER; + tv.vval.v_number = tv_blob_get(b, idx); + if (tv_equal(&tv, &argvars[1], ic, false)) { + rettv->vval.v_number = idx; + return; + } + } + return; + } else if (argvars[0].v_type != VAR_LIST) { + EMSG(_(e_listblobreq)); return; } list_T *const l = argvars[0].vval.v_list; @@ -4844,8 +5041,46 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; bool error = false; - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "insert()"); + 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_("insert() argument"), + TV_TRANSLATE)) { + return; + } + + long before = 0; + const int len = tv_blob_len(b); + + if (argvars[2].v_type != VAR_UNKNOWN) { + before = (long)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // type error; errmsg already given + } + if (before < 0 || before > len) { + EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + } + const int val = tv_get_number_chk(&argvars[1], &error); + if (error) { + return; + } + if (val < 0 || val > 255) { + EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + + ga_grow(&b->bv_ga, 1); + char_u *const p = (char_u *)b->bv_ga.ga_data; + memmove(p + before + 1, p + before, (size_t)len - before); + *(p + before) = val; + b->bv_ga.ga_len++; + + tv_copy(&argvars[0], rettv); + } else if (argvars[0].v_type != VAR_LIST) { + EMSG2(_(e_listblobarg), "insert()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), N_("insert() argument"), TV_TRANSLATE)) { long before = 0; @@ -4873,8 +5108,7 @@ 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, +static void f_interrupt(typval_T *argvars FUNC_ATTR_UNUSED, typval_T *rettv FUNC_ATTR_UNUSED, FunPtr fptr FUNC_ATTR_UNUSED) { got_int = true; @@ -4902,7 +5136,7 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) { lval_T lv; - dictitem_T *di; + dictitem_T *di; rettv->vval.v_number = -1; const char_u *const end = get_lval((char_u *)tv_get_string(&argvars[0]), @@ -4954,7 +5188,7 @@ static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_isnan(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT - && xisnan(argvars[0].vval.v_float); + && xisnan(argvars[0].vval.v_float); } /// "id()" function @@ -5062,9 +5296,7 @@ static const char *required_env_vars[] = { NULL }; -static dict_T *create_environment(const dictitem_T *job_env, - const bool clear_env, - const bool pty, +static dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty, const char * const pty_term_name) { dict_T * env = tv_dict_alloc(); @@ -5181,6 +5413,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool pty = false; bool clear_env = false; bool overlapped = false; + ChannelStdinMode stdin_mode = kChannelStdinPipe; CallbackReader on_stdout = CALLBACK_READER_INIT, on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; @@ -5195,6 +5428,17 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; overlapped = tv_dict_get_number(job_opts, "overlapped") != 0; + char *s = tv_dict_get_string(job_opts, "stdin", false); + if (s) { + if (!strncmp(s, "null", NUMBUFLEN)) { + stdin_mode = kChannelStdinNull; + } else if (!strncmp(s, "pipe", NUMBUFLEN)) { + // Nothing to do, default value + } else { + EMSG3(_(e_invargNval), "stdin", s); + } + } + if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); @@ -5251,8 +5495,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) env = create_environment(job_env, clear_env, pty, term_name); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, - rpc, overlapped, detach, cwd, width, height, - env, &rettv->vval.v_number); + rpc, overlapped, detach, stdin_mode, cwd, + width, height, env, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -5301,7 +5545,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } if (argvars[0].v_type != VAR_LIST || (argvars[1].v_type != VAR_NUMBER - && argvars[1].v_type != VAR_UNKNOWN)) { + && argvars[1].v_type != VAR_UNKNOWN)) { EMSG(_(e_invarg)); return; } @@ -5316,14 +5560,19 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER - || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) { + || !(chan = find_channel(TV_LIST_ITEM_TV(arg)->vval.v_number)) + || chan->streamtype != kChannelStreamProc) { + jobs[i] = NULL; // Invalid job. + } else if (process_is_stopped(&chan->stream.proc)) { + // Job is stopped but not fully destroyed. + // Ensure all callbacks on its event queue are executed. #15402 + process_wait(&chan->stream.proc, -1, NULL); jobs[i] = NULL; // Invalid job. } else { jobs[i] = chan; channel_incref(chan); if (chan->stream.proc.status < 0) { - // Process any pending events on the job's queue before temporarily - // replacing it. + // Flush any events in the job's queue before temporarily replacing it. multiqueue_process_events(chan->events); multiqueue_replace_parent(chan->events, waiting_jobs); } @@ -5482,29 +5731,27 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) { switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_NUMBER: { - rettv->vval.v_number = (varnumber_T)strlen( - tv_get_string(&argvars[0])); - break; - } - case VAR_LIST: { - rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); - break; - } - case VAR_DICT: { - rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); - break; - } - case VAR_UNKNOWN: - case VAR_BOOL: - case VAR_SPECIAL: - case VAR_FLOAT: - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E701: Invalid type for len()")); - break; - } + case VAR_STRING: + case VAR_NUMBER: + rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); + break; + case VAR_BLOB: + rettv->vval.v_number = tv_blob_len(argvars[0].vval.v_blob); + break; + case VAR_LIST: + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); + break; + case VAR_DICT: + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); + break; + case VAR_UNKNOWN: + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_FUNC: + EMSG(_("E701: Invalid type for len()")); + break; } } @@ -5549,7 +5796,7 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) } if (out_type == VAR_NUMBER) { - rettv->vval.v_number = (varnumber_T)int_out; + rettv->vval.v_number = (varnumber_T)int_out; } } @@ -5679,7 +5926,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) int mode; int abbr = FALSE; int get_dict = FALSE; - mapblock_T *mp; + mapblock_T *mp; int buffer_local; // Return empty string for failure. @@ -5721,11 +5968,9 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (*rhs == NUL) { rettv->vval.v_string = vim_strsave((char_u *)"<Nop>"); } else { - rettv->vval.v_string = (char_u *)str2special_save( - (char *)rhs, false, false); + rettv->vval.v_string = (char_u *)str2special_save((char *)rhs, false, false); } } - } else { tv_dict_alloc_ret(rettv); if (rhs != NULL) { @@ -5775,19 +6020,19 @@ static void f_mapcheck(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; - long len = 0; - char_u *expr = NULL; + char_u *str = NULL; + long len = 0; + char_u *expr = NULL; regmatch_T regmatch; - char_u *save_cpo; + char_u *save_cpo; long start = 0; long nth = 1; colnr_T startcol = 0; bool match = false; - list_T *l = NULL; - listitem_T *li = NULL; + list_T *l = NULL; + listitem_T *li = NULL; long idx = 0; - char_u *tofree = NULL; + char_u *tofree = NULL; // Make 'cpoptions' empty, the 'l' flag should not be used here. save_cpo = p_cpo; @@ -5795,30 +6040,26 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, rettv->vval.v_number = -1; switch (type) { - // matchlist(): return empty list when there are no matches. - case kSomeMatchList: { - tv_list_alloc_ret(rettv, kListLenMayKnow); - break; - } - // matchstrpos(): return ["", -1, -1, -1] - case kSomeMatchStrPos: { - tv_list_alloc_ret(rettv, 4); - tv_list_append_string(rettv->vval.v_list, "", 0); - tv_list_append_number(rettv->vval.v_list, -1); - tv_list_append_number(rettv->vval.v_list, -1); - tv_list_append_number(rettv->vval.v_list, -1); - break; - } - case kSomeMatchStr: { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - break; - } - case kSomeMatch: - case kSomeMatchEnd: { - // Do nothing: zero is default. - break; - } + // matchlist(): return empty list when there are no matches. + case kSomeMatchList: + tv_list_alloc_ret(rettv, kListLenMayKnow); + break; + // matchstrpos(): return ["", -1, -1, -1] + case kSomeMatchStrPos: + tv_list_alloc_ret(rettv, 4); + tv_list_append_string(rettv->vval.v_list, "", 0); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); + break; + case kSomeMatchStr: + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + break; + case kSomeMatch: + case kSomeMatchEnd: + // Do nothing: zero is default. + break; } if (argvars[0].v_type == VAR_LIST) { @@ -5851,10 +6092,12 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, } li = tv_list_find(l, idx); } else { - if (start < 0) + if (start < 0) { start = 0; - if (start > len) + } + if (start > len) { goto theend; + } // When "count" argument is there ignore matches before "start", // otherwise skip part of the string. Differs when pattern is "^" // or "\<". @@ -5894,10 +6137,12 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, match = vim_regexec_nl(®match, str, (colnr_T)startcol); - if (match && --nth <= 0) + if (match && --nth <= 0) { break; - if (l == NULL && !match) + } + if (l == NULL && !match) { break; + } // Advance to just after the match. if (l != NULL) { @@ -5907,74 +6152,70 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, startcol = (colnr_T)(regmatch.startp[0] + (*mb_ptr2len)(regmatch.startp[0]) - str); if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) { - match = false; - break; + match = false; + break; } } } if (match) { switch (type) { - case kSomeMatchStrPos: { - list_T *const ret_l = rettv->vval.v_list; - listitem_T *li1 = tv_list_first(ret_l); - listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); - listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); - listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); - 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(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) { - TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; - } - break; - } - case kSomeMatchList: { - // Return list with matched string and submatches. - for (int i = 0; i < NSUBEXP; i++) { - if (regmatch.endp[i] == NULL) { - tv_list_append_string(rettv->vval.v_list, NULL, 0); - } else { - tv_list_append_string(rettv->vval.v_list, - (const char *)regmatch.startp[i], - (regmatch.endp[i] - regmatch.startp[i])); - } - } - break; + case kSomeMatchStrPos: { + list_T *const ret_l = rettv->vval.v_list; + listitem_T *li1 = tv_list_first(ret_l); + listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); + listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); + listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); + 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(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) { + TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; } - case kSomeMatchStr: { - // Return matched string. - if (l != NULL) { - tv_copy(TV_LIST_ITEM_TV(li), rettv); + break; + } + case kSomeMatchList: + // Return list with matched string and submatches. + for (int i = 0; i < NSUBEXP; i++) { + if (regmatch.endp[i] == NULL) { + tv_list_append_string(rettv->vval.v_list, NULL, 0); } else { - rettv->vval.v_string = (char_u *)xmemdupz( - (const char *)regmatch.startp[0], - (size_t)(regmatch.endp[0] - regmatch.startp[0])); + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); } - break; } - case kSomeMatch: - case kSomeMatchEnd: { - if (l != NULL) { - rettv->vval.v_number = idx; + break; + case kSomeMatchStr: + // Return matched string. + if (l != NULL) { + tv_copy(TV_LIST_ITEM_TV(li), rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz((const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - + regmatch.startp[0])); + } + break; + case kSomeMatch: + case kSomeMatchEnd: + if (l != NULL) { + rettv->vval.v_number = idx; + } else { + if (type == kSomeMatch) { + rettv->vval.v_number = + (varnumber_T)(regmatch.startp[0] - str); } else { - if (type == kSomeMatch) { - 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)(str - expr); + rettv->vval.v_number = + (varnumber_T)(regmatch.endp[0] - str); } - break; + rettv->vval.v_number += (varnumber_T)(str - expr); } + break; } } vim_regfree(regmatch.regprog); @@ -6123,7 +6364,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *win = get_optional_window(argvars, 1); + win_T *win = get_optional_window(argvars, 1); if (win == NULL) { rettv->vval.v_number = -1; } else { @@ -6171,8 +6412,7 @@ static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// vval.v_number, type is not touched. Returns zero for /// empty lists/dictionaries. /// @param[in] domax Determines whether maximal or minimal value is desired. -static void max_min(const typval_T *const tv, typval_T *const rettv, - const bool domax) +static void max_min(const typval_T *const tv, typval_T *const rettv, const bool domax) FUNC_ATTR_NONNULL_ALL { bool error = false; @@ -6298,9 +6538,16 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); list_T *const list = argvars[0].vval.v_list; - msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); + msgpack_packer *packer; + if (argvars[1].v_type != VAR_UNKNOWN + && strequal(tv_get_string(&argvars[1]), "B")) { + tv_blob_alloc_ret(rettv); + packer = msgpack_packer_new(rettv->vval.v_blob, &encode_blob_write); + } else { + packer = msgpack_packer_new(tv_list_alloc_ret(rettv, kListLenMayKnow), + &encode_list_write); + } const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; @@ -6308,23 +6555,47 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER(list, li, { vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx); idx++; - if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { + if (encode_vim_to_msgpack(packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { break; } }); - msgpack_packer_free(lpacker); + msgpack_packer_free(packer); } -/// "msgpackparse" function -static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static int msgpackparse_convert_item(const msgpack_object data, const msgpack_unpack_return result, + list_T *const ret_list, const bool fail_if_incomplete) FUNC_ATTR_NONNULL_ALL { - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "msgpackparse()"); - return; + switch (result) { + case MSGPACK_UNPACK_PARSE_ERROR: + EMSG2(_(e_invarg2), "Failed to parse msgpack string"); + return FAIL; + case MSGPACK_UNPACK_NOMEM_ERROR: + EMSG(_(e_outofmem)); + return FAIL; + case MSGPACK_UNPACK_CONTINUE: + if (fail_if_incomplete) { + EMSG2(_(e_invarg2), "Incomplete msgpack string"); + return FAIL; + } + return NOTDONE; + case MSGPACK_UNPACK_SUCCESS: { + typval_T tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(data, &tv) == FAIL) { + EMSG2(_(e_invarg2), "Failed to convert msgpack string"); + return FAIL; + } + tv_list_append_owned_tv(ret_list, tv); + return OK; } - list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); - const list_T *const list = argvars[0].vval.v_list; + default: + abort(); + } +} + +static void msgpackparse_unpack_list(const list_T *const list, list_T *const ret_list) + FUNC_ATTR_NONNULL_ARG(2) +{ if (tv_list_len(list) == 0) { return; } @@ -6343,43 +6614,28 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) do { if (!msgpack_unpacker_reserve_buffer(unpacker, IOSIZE)) { EMSG(_(e_outofmem)); - goto f_msgpackparse_exit; + goto end; } size_t read_bytes; - const int rlret = encode_read_from_list( - &lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes); + const int rlret = encode_read_from_list(&lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, + &read_bytes); if (rlret == FAIL) { EMSG2(_(e_invarg2), "List item is not a string"); - goto f_msgpackparse_exit; + goto end; } msgpack_unpacker_buffer_consumed(unpacker, read_bytes); if (read_bytes == 0) { break; } while (unpacker->off < unpacker->used) { - const msgpack_unpack_return result = msgpack_unpacker_next(unpacker, - &unpacked); - if (result == MSGPACK_UNPACK_PARSE_ERROR) { - EMSG2(_(e_invarg2), "Failed to parse msgpack string"); - goto f_msgpackparse_exit; - } - if (result == MSGPACK_UNPACK_NOMEM_ERROR) { - EMSG(_(e_outofmem)); - goto f_msgpackparse_exit; - } - if (result == MSGPACK_UNPACK_SUCCESS) { - typval_T tv = { .v_type = VAR_UNKNOWN }; - if (msgpack_to_vim(unpacked.data, &tv) == FAIL) { - EMSG2(_(e_invarg2), "Failed to convert msgpack string"); - goto f_msgpackparse_exit; - } - tv_list_append_owned_tv(ret_list, tv); - } - if (result == MSGPACK_UNPACK_CONTINUE) { - if (rlret == OK) { - EMSG2(_(e_invarg2), "Incomplete msgpack string"); - } + const msgpack_unpack_return result + = msgpack_unpacker_next(unpacker, &unpacked); + const int conv_result = msgpackparse_convert_item(unpacked.data, result, + ret_list, rlret == OK); + if (conv_result == NOTDONE) { break; + } else if (conv_result == FAIL) { + goto end; } } if (rlret == OK) { @@ -6387,10 +6643,46 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } while (true); -f_msgpackparse_exit: - msgpack_unpacked_destroy(&unpacked); +end: msgpack_unpacker_free(unpacker); - return; + msgpack_unpacked_destroy(&unpacked); +} + +static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret_list) + FUNC_ATTR_NONNULL_ARG(2) +{ + const int len = tv_blob_len(blob); + if (len == 0) { + return; + } + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + for (size_t offset = 0; offset < (size_t)len;) { + const msgpack_unpack_return result + = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, len, &offset); + if (msgpackparse_convert_item(unpacked.data, result, ret_list, true) + != OK) { + break; + } + } + + msgpack_unpacked_destroy(&unpacked); +} + +/// "msgpackparse" function +static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) + FUNC_ATTR_NONNULL_ALL +{ + if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { + EMSG2(_(e_listblobarg), "msgpackparse()"); + return; + } + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + if (argvars[0].v_type == VAR_LIST) { + msgpackparse_unpack_list(argvars[0].vval.v_list, ret_list); + } else { + msgpackparse_unpack_blob(argvars[0].vval.v_blob, ret_list); + } } /* @@ -6525,53 +6817,51 @@ 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, FunPtr fptr) { - buf_T *buf; - Callback prompt_callback = { .type = kCallbackNone }; + buf_T *buf; + Callback prompt_callback = { .type = kCallbackNone }; - if (check_secure()) { - return; - } - buf = tv_get_buf(&argvars[0], false); - if (buf == NULL) { - return; - } + if (check_secure()) { + return; + } + buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + return; + } - if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) { - if (!callback_from_typval(&prompt_callback, &argvars[1])) { - return; - } + if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) { + if (!callback_from_typval(&prompt_callback, &argvars[1])) { + return; } + } - callback_free(&buf->b_prompt_callback); - buf->b_prompt_callback = prompt_callback; + callback_free(&buf->b_prompt_callback); + buf->b_prompt_callback = prompt_callback; } // "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, FunPtr fptr) { - buf_T *buf; - Callback interrupt_callback = { .type = kCallbackNone }; + buf_T *buf; + Callback interrupt_callback = { .type = kCallbackNone }; - if (check_secure()) { - return; - } - buf = tv_get_buf(&argvars[0], false); - if (buf == NULL) { - return; - } + if (check_secure()) { + return; + } + buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + return; + } - if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) { - if (!callback_from_typval(&interrupt_callback, &argvars[1])) { - return; - } + if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) { + if (!callback_from_typval(&interrupt_callback, &argvars[1])) { + return; } + } - callback_free(&buf->b_prompt_interrupt); - buf->b_prompt_interrupt= interrupt_callback; + callback_free(&buf->b_prompt_interrupt); + buf->b_prompt_interrupt= interrupt_callback; } /// "prompt_getprompt({buffer})" function @@ -6595,23 +6885,22 @@ void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // "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, FunPtr fptr) { - buf_T *buf; - const char_u *text; + buf_T *buf; + const char_u *text; - if (check_secure()) { - return; - } - buf = tv_get_buf(&argvars[0], false); - if (buf == NULL) { - return; - } + if (check_secure()) { + return; + } + buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + return; + } - text = (const char_u *)tv_get_string(&argvars[1]); - xfree(buf->b_prompt_text); - buf->b_prompt_text = vim_strsave(text); + text = (const char_u *)tv_get_string(&argvars[1]); + xfree(buf->b_prompt_text); + buf->b_prompt_text = vim_strsave(text); } // "pum_getpos()" function @@ -6626,8 +6915,9 @@ static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - if (pum_visible()) + if (pum_visible()) { rettv->vval.v_number = 1; + } } /* @@ -6801,11 +7091,12 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool binary = false; - FILE *fd; + bool blob = false; + FILE *fd; char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1 int io_size = sizeof(buf); int readlen; // size of last fread() - char_u *prev = NULL; // previously read bytes, if any + char_u *prev = NULL; // previously read bytes, if any long prevlen = 0; // length of data in prev long prevsize = 0; // size of prev buffer long maxline = MAXLNUM; @@ -6813,22 +7104,41 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { binary = true; + } else if (strcmp(tv_get_string(&argvars[1]), "B") == 0) { + blob = true; } if (argvars[2].v_type != VAR_UNKNOWN) { maxline = tv_get_number(&argvars[2]); } } - list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); - // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); + + if (os_isdir((const char_u *)fname)) { + EMSG2(_(e_isadir2), fname); + return; + } if (*fname == NUL || (fd = os_fopen(fname, READBIN)) == NULL) { EMSG2(_(e_notopen), *fname == NUL ? _("<empty>") : fname); return; } + if (blob) { + tv_blob_alloc_ret(rettv); + if (!read_blob(fd, rettv->vval.v_blob)) { + EMSG2(_(e_notread), fname); + // An empty blob is returned on error. + tv_blob_free(rettv->vval.v_blob); + rettv->vval.v_blob = NULL; + } + fclose(fd); + return; + } + + list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); + while (maxline < 0 || tv_list_len(l) < maxline) { readlen = (int)fread(buf, 1, io_size, fd); @@ -6843,7 +7153,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); p++) { if (*p == '\n' || readlen <= 0) { - char_u *s = NULL; + char_u *s = NULL; size_t len = p - start; // Finished a line. Remove CRs before NL. @@ -6893,16 +7203,17 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (*p == NUL) { *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. + // 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) { // 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] - : prevlen >= 1 ? prev[prevlen - 1] : NUL; + : prevlen >= 1 ? prev[prevlen - 1] : NUL; char_u back2 = p >= buf + 2 ? p[-2] - : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] - : prevlen >= 2 ? prev[prevlen - 2] : NUL; + : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] + : prevlen >= + 2 ? prev[prevlen - 2] : NUL; if (back2 == 0xef && back1 == 0xbb) { char_u *dest = p - 2; @@ -6920,8 +7231,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) // adjust_prevlen must be 1 or 2. dest = buf; } - if (readlen > p - buf + 1) + if (readlen > p - buf + 1) { memmove(dest, p + 1, readlen - (p - buf) - 1); + } readlen -= 3 - adjust_prevlen; prevlen -= adjust_prevlen; p = dest - 1; @@ -6940,9 +7252,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) * fragment of a line, so the first allocation is made * small, to avoid repeatedly 'allocing' large and * 'reallocing' small. */ - if (prevsize == 0) + if (prevsize == 0) { prevsize = (long)(p - start); - else { + } else { long grow50pc = (prevsize * 3) / 2; long growmin = (long)((p - start) * 2 + prevlen); prevsize = grow50pc > growmin ? grow50pc : growmin; @@ -6959,6 +7271,61 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) fclose(fd); } +/// "getreginfo()" function +static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *strregname; + if (argvars[0].v_type != VAR_UNKNOWN) { + strregname = tv_get_string_chk(&argvars[0]); + if (strregname == NULL) { + return; + } + } else { + strregname = (const char *)get_vim_var_str(VV_REG); + } + + int regname = (strregname == NULL ? '"' : *strregname); + if (regname == 0 || regname == '@') { + regname = '"'; + } + + tv_dict_alloc_ret(rettv); + dict_T *const dict = rettv->vval.v_dict; + + list_T *const list = get_reg_contents(regname, kGRegExprSrc | kGRegList); + if (list == NULL) { + return; + } + tv_dict_add_list(dict, S_LEN("regcontents"), list); + + char buf[NUMBUFLEN + 2]; + buf[0] = NUL; + buf[1] = NUL; + colnr_T reglen = 0; + switch (get_reg_type(regname, ®len)) { + case kMTLineWise: + buf[0] = 'V'; + break; + case kMTCharWise: + buf[0] = 'v'; + break; + case kMTBlockWise: + vim_snprintf(buf, sizeof(buf), "%c%d", Ctrl_V, reglen + 1); + break; + case kMTUnknown: + abort(); + } + tv_dict_add_str(dict, S_LEN("regtype"), buf); + + buf[0] = get_register_name(get_unname_register()); + buf[1] = NUL; + if (regname == '"') { + tv_dict_add_str(dict, S_LEN("points_to"), buf); + } else { + tv_dict_add_bool(dict, S_LEN("isunnamed"), regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); + } +} + // "reg_executing()" function static void f_reg_executing(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -7042,7 +7409,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) // results, if varnumber_T or proftime_T change, the union cast will need // to be revised. STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), - "type punning will produce incorrect results on this platform"); + "type punning will produce incorrect results on this platform"); tv_list_alloc_ret(rettv, 2); tv_list_append_number(rettv->vval.v_list, u.split.high); @@ -7067,13 +7434,13 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *item, *item2; - listitem_T *li; + list_T *l; + listitem_T *item, *item2; + listitem_T *li; long idx; long end; - dict_T *d; - dictitem_T *di; + dict_T *d; + dictitem_T *di; const char *const arg_errmsg = N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { @@ -7097,8 +7464,64 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } + } 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, arg_errmsg, TV_TRANSLATE)) { + return; + } + + bool error = false; + idx = (long)tv_get_number_chk(&argvars[1], &error); + + if (!error) { + const int len = tv_blob_len(b); + + if (idx < 0) { + // count from the end + idx = len + idx; + } + if (idx < 0 || idx >= len) { + EMSGN(_(e_blobidx), idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + char_u *const p = (char_u *)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--; + } else { + // Remove range of items, return blob with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (end < 0) { + // count from the end + end = len + end; + } + if (end >= len || idx > end) { + EMSGN(_(e_blobidx), end); + return; + } + blob_T *const blob = tv_blob_alloc(); + blob->bv_ga.ga_len = end - idx + 1; + ga_grow(&blob->bv_ga, 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)); + tv_blob_set_ret(rettv, blob); + + if (len - end - 1 > 0) { + memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); + } + b->bv_ga.ga_len -= end - idx + 1; + } + } } else if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listdictarg), "remove()"); + EMSG2(_(e_listdictblobarg), "remove()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), arg_errmsg, TV_TRANSLATE)) { bool error = false; @@ -7151,9 +7574,8 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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((const char_u *)tv_get_string(&argvars[0]), + (const char_u *)tv_get_string_buf(&argvars[1], buf)); } } @@ -7372,13 +7794,25 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "reverse()"); - } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("reverse() argument"), TV_TRANSLATE)) { - tv_list_reverse(l); - tv_list_set_ret(rettv, l); + 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); + tv_blob_set(b, i, tv_blob_get(b, len - i - 1)); + tv_blob_set(b, len - i - 1, tmp); + } + tv_blob_set_ret(rettv, b); + } else if (argvars[0].v_type != VAR_LIST) { + EMSG2(_(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)) { + tv_list_reverse(l); + tv_list_set_ret(rettv, l); + } } } @@ -7409,30 +7843,40 @@ static int get_search_arg(typval_T *varp, int *flagsp) } 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) { - emsgf(_(e_invarg2), flags); - dir = 0; - } else { - *flagsp |= mask; + 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) { + emsgf(_(e_invarg2), flags); + dir = 0; + } else { + *flagsp |= mask; + } } if (dir == 0) { break; @@ -7511,12 +7955,14 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia); if (subpatnum != FAIL) { - if (flags & SP_SUBPAT) + if (flags & SP_SUBPAT) { retval = subpatnum; - else + } else { retval = pos.lnum; - if (flags & SP_SETPCMARK) + } + if (flags & SP_SETPCMARK) { setpcmark(); + } curwin->w_cursor = pos; if (match_pos != NULL) { // Store the match cursor position @@ -7654,10 +8100,10 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) } msg_ext_set_kind("rpc_error"); if (name) { - emsgf_multiline("Error invoking '%s' on channel %"PRIu64" (%s):\n%s", + emsgf_multiline("Error invoking '%s' on channel %" PRIu64 " (%s):\n%s", method, chan_id, name, err.msg); } else { - emsgf_multiline("Error invoking '%s' on channel %"PRIu64":\n%s", + emsgf_multiline("Error invoking '%s' on channel %" PRIu64 ":\n%s", method, chan_id, err.msg); } @@ -7732,8 +8178,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, CALLBACK_NONE, - false, true, false, false, NULL, 0, 0, - NULL, &rettv->vval.v_number); + false, true, false, false, + kChannelStdinPipe, NULL, 0, 0, NULL, + &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -7769,9 +8216,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "screenattr()" function - */ +// "screenattr()" function static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int c; @@ -7789,9 +8234,7 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = c; } -/* - * "screenchar()" function - */ +// "screenchar()" function static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int c; @@ -7809,11 +8252,34 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = c; } -/* - * "screencol()" function - * - * First column is 1 to be consistent with virtcol(). - */ +// "screenchars()" function +static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + int row = tv_get_number_chk(&argvars[0], NULL) - 1; + int col = tv_get_number_chk(&argvars[1], NULL) - 1; + if (row < 0 || row >= default_grid.Rows + || col < 0 || col >= default_grid.Columns) { + tv_list_alloc_ret(rettv, 0); + return; + } + ScreenGrid *grid = &default_grid; + screenchar_adjust_grid(&grid, &row, &col); + int pcc[MAX_MCO]; + int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + col], pcc); + int composing_len = 0; + while (pcc[composing_len] != 0) { + composing_len++; + } + tv_list_alloc_ret(rettv, composing_len + 1); + tv_list_append_number(rettv->vval.v_list, c); + for (int i = 0; i < composing_len; i++) { + tv_list_append_number(rettv->vval.v_list, pcc[i]); + } +} + +// "screencol()" function +// +// First column is 1 to be consistent with virtcol(). static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = ui_current_col() + 1; @@ -7845,17 +8311,29 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("endcol"), ecol); } -/* - * "screenrow()" function - */ +// "screenrow()" function static void f_screenrow(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = ui_current_row() + 1; } -/* - * "search()" function - */ +// "screenstring()" function +static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_string = NULL; + rettv->v_type = VAR_STRING; + int row = tv_get_number_chk(&argvars[0], NULL) - 1; + int col = tv_get_number_chk(&argvars[1], NULL) - 1; + if (row < 0 || row >= default_grid.Rows + || col < 0 || col >= default_grid.Columns) { + return; + } + ScreenGrid *grid = &default_grid; + screenchar_adjust_grid(&grid, &row, &col); + rettv->vval.v_string = vim_strsave(grid->chars[grid->line_offset[row] + col]); +} + +// "search()" function static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int flags = 0; @@ -7958,9 +8436,8 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } } - retval = do_searchpair( - spat, mpat, epat, dir, skip, - flags, match_pos, lnum_stop, time_limit); + retval = do_searchpair(spat, mpat, epat, dir, skip, + flags, match_pos, lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -8001,22 +8478,19 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) * Used by searchpair(), see its documentation for the details. * Returns 0 or -1 for no match, */ -long -do_searchpair( - const char *spat, // start pattern - const char *mpat, // middle pattern - const char *epat, // end pattern - int dir, // BACKWARD or FORWARD - const typval_T *skip, // skip expression - int flags, // SP_SETPCMARK and other SP_ values - pos_T *match_pos, - linenr_T lnum_stop, // stop at this line if not zero - long time_limit // stop after this many msec -) +long do_searchpair(const char *spat, // start pattern + const char *mpat, // middle pattern + const char *epat, // end pattern + int dir, // BACKWARD or FORWARD + const typval_T *skip, // skip expression + int flags, // SP_SETPCMARK and other SP_ values + pos_T *match_pos, linenr_T lnum_stop, // stop at this line if not zero + long time_limit // stop after this many msec + ) FUNC_ATTR_NONNULL_ARG(1, 2, 3) { - char_u *save_cpo; - char_u *pat, *pat2 = NULL, *pat3 = NULL; + char_u *save_cpo; + char_u *pat, *pat2 = NULL, *pat3 = NULL; long retval = 0; pos_T pos; pos_T firstpos; @@ -8078,8 +8552,9 @@ do_searchpair( break; } - if (firstpos.lnum == 0) + if (firstpos.lnum == 0) { firstpos = pos; + } if (equalpos(pos, foundpos)) { // Found the same position again. Can happen with a pattern that // has "\zs" at the end and searching backwards. Advance one @@ -8108,8 +8583,9 @@ do_searchpair( retval = -1; break; } - if (r) + if (r) { continue; + } } if ((dir == BACKWARD && n == 3) || (dir == FORWARD && n == 2)) { @@ -8136,8 +8612,9 @@ do_searchpair( setpcmark(); } curwin->w_cursor = pos; - if (!(flags & SP_REPEAT)) + if (!(flags & SP_REPEAT)) { break; + } nest = 1; // search for next unmatched } } @@ -8269,16 +8746,16 @@ 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; + 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); - } + 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); + } } /* @@ -8329,8 +8806,8 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *d; - dictitem_T *di; + dict_T *d; + dictitem_T *di; if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -8510,7 +8987,7 @@ skip_args: */ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *win; + win_T *win; rettv->vval.v_number = -1; @@ -8632,7 +9109,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T pos; int fnum; - colnr_T curswant = -1; + colnr_T curswant = -1; rettv->vval.v_number = -1; const char *const name = tv_get_string_chk(argvars); @@ -8650,7 +9127,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } check_cursor(); rettv->vval.v_number = 0; - } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { + } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { rettv->vval.v_number = 0; @@ -8670,6 +9147,36 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_qf_ll_list(NULL, argvars, rettv); } +/// Translate a register type string to the yank type and block length +static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *const block_len) + FUNC_ATTR_NONNULL_ALL +{ + char_u *stropt = *pp; + switch (*stropt) { + case 'v': + case 'c': // character-wise selection + *yank_type = kMTCharWise; + break; + case 'V': + case 'l': // line-wise selection + *yank_type = kMTLineWise; + break; + case 'b': + case Ctrl_V: // block-wise selection + *yank_type = kMTBlockWise; + if (ascii_isdigit(stropt[1])) { + stropt++; + *block_len = getdigits_long(&stropt, false, 0) - 1; + stropt--; + } + break; + default: + return FAIL; + } + *pp = stropt; + return OK; +} + /* * "setreg()" function */ @@ -8694,45 +9201,75 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) regname = '"'; } + const typval_T *regcontents = NULL; + int pointreg = 0; + if (argvars[1].v_type == VAR_DICT) { + dict_T *const d = argvars[1].vval.v_dict; + + if (tv_dict_len(d) == 0) { + // Empty dict, clear the register (like setreg(0, [])) + char_u *lstval[2] = { NULL, NULL }; + write_reg_contents_lst(regname, lstval, false, kMTUnknown, -1); + return; + } + + dictitem_T *const di = tv_dict_find(d, "regcontents", -1); + if (di != NULL) { + regcontents = &di->di_tv; + } + + const char *stropt = tv_dict_get_string(d, "regtype", false); + if (stropt != NULL) { + const int ret = get_yank_type((char_u **)&stropt, &yank_type, &block_len); + + if (ret == FAIL || *(++stropt) != NUL) { + EMSG2(_(e_invargval), "value"); + return; + } + } + + if (regname == '"') { + stropt = tv_dict_get_string(d, "points_to", false); + if (stropt != NULL) { + pointreg = *stropt; + regname = pointreg; + } + } else if (tv_dict_get_number(d, "isunnamed")) { + pointreg = regname; + } + } else { + regcontents = &argvars[1]; + } + bool set_unnamed = false; if (argvars[2].v_type != VAR_UNKNOWN) { + if (yank_type != kMTUnknown) { + EMSG2(_(e_toomanyarg), "setreg"); + return; + } + const char *stropt = tv_get_string_chk(&argvars[2]); if (stropt == NULL) { return; // Type error. } for (; *stropt != NUL; stropt++) { switch (*stropt) { - case 'a': case 'A': { // append - append = true; - break; - } - case 'v': case 'c': { // character-wise selection - yank_type = kMTCharWise; - break; - } - case 'V': case 'l': { // line-wise selection - yank_type = kMTLineWise; - break; - } - case 'b': case Ctrl_V: { // block-wise selection - yank_type = kMTBlockWise; - if (ascii_isdigit(stropt[1])) { - stropt++; - block_len = getdigits_long((char_u **)&stropt, true, 0) - 1; - stropt--; - } - break; - } - case 'u': case '"': { // unnamed register - set_unnamed = true; - break; - } + case 'a': + case 'A': // append + append = true; + break; + case 'u': + case '"': // unnamed register + set_unnamed = true; + break; + default: + get_yank_type((char_u **)&stropt, &yank_type, &block_len); } } } - if (argvars[1].v_type == VAR_LIST) { - list_T *ll = argvars[1].vval.v_list; + if (regcontents != NULL && regcontents->v_type == VAR_LIST) { + list_T *const ll = regcontents->vval.v_list; // If the list is NULL handle like an empty list. const int len = tv_list_len(ll); @@ -8768,19 +9305,23 @@ free_lstval: xfree(*--curallocval); } xfree(lstval); - } else { - const char *strval = tv_get_string_chk(&argvars[1]); + } else if (regcontents != NULL) { + const char *const strval = tv_get_string_chk(regcontents); if (strval == NULL) { return; } write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), append, yank_type, block_len); } + if (pointreg != 0) { + get_yank_register(pointreg, YREG_YANK); + } rettv->vval.v_number = 0; if (set_unnamed) { // Discard the result. We already handle the error case. - if (op_reg_set_previous(regname)) { } + if (op_reg_set_previous(regname)) { + } } } @@ -8828,54 +9369,54 @@ static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "settagstack()" function static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - static char *e_invact2 = N_("E962: Invalid action: '%s'"); - win_T *wp; - dict_T *d; - int action = 'r'; + static char *e_invact2 = N_("E962: Invalid action: '%s'"); + win_T *wp; + dict_T *d; + int action = 'r'; - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - // first argument: window number or id - wp = find_win_by_nr_or_id(&argvars[0]); - if (wp == NULL) { - return; - } + // first argument: window number or id + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + return; + } - // second argument: dict with items to set in the tag stack - if (argvars[1].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - d = argvars[1].vval.v_dict; - if (d == NULL) { + // second argument: dict with items to set in the tag stack + if (argvars[1].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + d = argvars[1].vval.v_dict; + if (d == NULL) { + return; + } + + // third argument: action - 'a' for append and 'r' for replace. + // default is to replace the stack. + if (argvars[2].v_type == VAR_UNKNOWN) { + action = 'r'; + } else if (argvars[2].v_type == VAR_STRING) { + const char *actstr; + actstr = tv_get_string_chk(&argvars[2]); + if (actstr == NULL) { return; } - - // third argument: action - 'a' for append and 'r' for replace. - // default is to replace the stack. - if (argvars[2].v_type == VAR_UNKNOWN) { - action = 'r'; - } else if (argvars[2].v_type == VAR_STRING) { - const char *actstr; - actstr = tv_get_string_chk(&argvars[2]); - if (actstr == NULL) { - return; - } - if ((*actstr == 'r' || *actstr == 'a' || *actstr == 't') - && actstr[1] == NUL) { - action = *actstr; - } else { - EMSG2(_(e_invact2), actstr); - return; - } + if ((*actstr == 'r' || *actstr == 'a' || *actstr == 't') + && actstr[1] == NUL) { + action = *actstr; } else { - EMSG(_(e_stringreq)); - return; + EMSG2(_(e_invact2), actstr); + return; } + } else { + EMSG(_(e_stringreq)); + return; + } - if (set_tagstack(wp, d, action) == OK) { - rettv->vval.v_number = 0; - } + if (set_tagstack(wp, d, action) == OK) { + rettv->vval.v_number = 0; + } } /* @@ -8890,7 +9431,7 @@ static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *p = tv_get_string(&argvars[0]); - const char *hash = sha256_bytes((const uint8_t *)p, strlen(p) , NULL, 0); + const char *hash = sha256_bytes((const uint8_t *)p, strlen(p), NULL, 0); // make a copy of the hash (sha256_bytes returns a static buffer) rettv->vval.v_string = (char_u *)xstrdup(hash); @@ -8904,8 +9445,9 @@ static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const bool do_special = non_zero_arg(&argvars[1]); - rettv->vval.v_string = vim_strsave_shellescape( - (const char_u *)tv_get_string(&argvars[0]), do_special, do_special); + rettv->vval.v_string = vim_strsave_shellescape((const char_u *)tv_get_string( + &argvars[0]), do_special, + do_special); rettv->v_type = VAR_STRING; } @@ -8955,8 +9497,9 @@ static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = sign_define_from_dict( - name, argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL); + rettv->vval.v_number = sign_define_from_dict(name, + argvars[1].v_type == + VAR_DICT ? argvars[1].vval.v_dict : NULL); } /// "sign_getdefined()" function @@ -9088,8 +9631,8 @@ static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = sign_place_from_dict( - &argvars[0], &argvars[1], &argvars[2], &argvars[3], dict); + rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], &argvars[2], &argvars[3], + dict); } /// "sign_placelist()" function. Place multiple signs. @@ -9108,8 +9651,7 @@ static void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { sign_id = -1; if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) { - sign_id = sign_place_from_dict( - NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); + sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); } else { EMSG(_(e_dictreq)); } @@ -9378,7 +9920,6 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) int res; typval_T rettv; typval_T argv[3]; - int dummy; const char *func_name; partial_T *partial = sortinfo->item_compare_partial; @@ -9402,10 +9943,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func((const char_u *)func_name, - -1, - &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, - partial, sortinfo->item_compare_selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe); tv_clear(&argv[0]); tv_clear(&argv[1]); @@ -9445,7 +9987,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2) */ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { - ListSortItem *ptrs; + ListSortItem *ptrs; long len; long i; @@ -9632,7 +10174,7 @@ static void f_uniq(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, FunPtr fptr) FUNC_ATTR_NONNULL_ALL { proftime_T tm; @@ -9707,10 +10249,12 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_string(rettv->vval.v_list, word, len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" - : attr == HLF_SPR ? "rare" - : attr == HLF_SPL ? "local" - : attr == HLF_SPC ? "caps" - : NULL), -1); + : attr == HLF_SPR ? "rare" + : attr == HLF_SPL ? "local" + : attr == + HLF_SPC ? "caps" + : + NULL), -1); } /* @@ -9767,7 +10311,7 @@ f_spellsuggest_return: static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *save_cpo; + char_u *save_cpo; int match; colnr_T col = 0; bool keepempty = false; @@ -9904,7 +10448,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; varnumber_T n; - int what; + int what = 0; if (argvars[1].v_type != VAR_UNKNOWN) { base = tv_get_number(&argvars[1]); @@ -9912,6 +10456,9 @@ 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])) { + what |= STR2NR_QUOTE; + } } char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); @@ -9920,23 +10467,18 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) p = skipwhite(p + 1); } switch (base) { - case 2: { - what = STR2NR_BIN | STR2NR_FORCE; - break; - } - case 8: { - what = STR2NR_OCT | STR2NR_FORCE; - break; - } - case 16: { - what = STR2NR_HEX | STR2NR_FORCE; - break; - } - default: { - what = 0; - } + case 2: + what |= STR2NR_BIN | STR2NR_FORCE; + break; + case 8: + what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE; + break; + case 16: + what |= STR2NR_HEX | STR2NR_FORCE; + break; } - vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false); + // Text after the number is silently ignored. if (isneg) { rettv->vval.v_number = -n; } else { @@ -9967,7 +10509,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)")); } else { vimconv_T conv; - char_u *enc; + char_u *enc; conv.vc_type = CONV_NONE; enc = enc_locale(); @@ -10312,7 +10854,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0])); + rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0]), true); } /* @@ -10443,53 +10985,46 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *p = NULL; switch (TOLOWER_ASC(what[0])) { - case 'b': { - if (TOLOWER_ASC(what[1]) == 'g') { // bg[#] - p = highlight_color(id, what, modec); - } else { // bold - p = highlight_has_attr(id, HL_BOLD, modec); - } - break; - } - case 'f': { // fg[#] or font + case 'b': + if (TOLOWER_ASC(what[1]) == 'g') { // bg[#] p = highlight_color(id, what, modec); - break; - } - case 'i': { - if (TOLOWER_ASC(what[1]) == 'n') { // inverse - p = highlight_has_attr(id, HL_INVERSE, modec); - } else { // italic - p = highlight_has_attr(id, HL_ITALIC, modec); - } - break; + } else { // bold + p = highlight_has_attr(id, HL_BOLD, modec); } - case 'n': { // name - p = get_highlight_name_ext(NULL, id - 1, false); - break; - } - case 'r': { // reverse + break; + case 'f': // fg[#] or font + p = highlight_color(id, what, modec); + break; + case 'i': + if (TOLOWER_ASC(what[1]) == 'n') { // inverse p = highlight_has_attr(id, HL_INVERSE, modec); - break; + } else { // italic + p = highlight_has_attr(id, HL_ITALIC, modec); } - case 's': { - if (TOLOWER_ASC(what[1]) == 'p') { // sp[#] - p = highlight_color(id, what, modec); - } else if (TOLOWER_ASC(what[1]) == 't' - && TOLOWER_ASC(what[2]) == 'r') { // strikethrough - p = highlight_has_attr(id, HL_STRIKETHROUGH, modec); - } else { // standout - p = highlight_has_attr(id, HL_STANDOUT, modec); - } - break; + break; + case 'n': // name + p = get_highlight_name_ext(NULL, id - 1, false); + break; + case 'r': // reverse + p = highlight_has_attr(id, HL_INVERSE, modec); + break; + case 's': + if (TOLOWER_ASC(what[1]) == 'p') { // sp[#] + p = highlight_color(id, what, modec); + } else if (TOLOWER_ASC(what[1]) == 't' + && TOLOWER_ASC(what[2]) == 'r') { // strikethrough + p = highlight_has_attr(id, HL_STRIKETHROUGH, modec); + } else { // standout + p = highlight_has_attr(id, HL_STANDOUT, modec); } - case 'u': { - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline - p = highlight_has_attr(id, HL_UNDERLINE, modec); - } else { // undercurl - p = highlight_has_attr(id, HL_UNDERCURL, modec); - } - break; + break; + case 'u': + if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline + p = highlight_has_attr(id, HL_UNDERLINE, modec); + } else { // undercurl + p = highlight_has_attr(id, HL_UNDERCURL, modec); } + break; } rettv->v_type = VAR_STRING; @@ -10599,7 +11134,7 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *wp = NULL; + win_T *wp = NULL; if (argvars[0].v_type == VAR_UNKNOWN) { wp = firstwin; @@ -10652,9 +11187,9 @@ static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static int get_winnr(tabpage_T *tp, typval_T *argvar) { - win_T *twin; + win_T *twin; int nr = 1; - win_T *wp; + win_T *wp; twin = (tp == curtab) ? curwin : tp->tp_curwin; if (argvar->v_type != VAR_UNKNOWN) { @@ -10700,7 +11235,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) } } - if (nr > 0) + if (nr > 0) { for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin; wp != twin; wp = wp->w_next) { if (wp == NULL) { @@ -10710,6 +11245,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) } ++nr; } + } return nr; } @@ -10849,10 +11385,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) const bool rpc = false; const bool overlapped = false; const bool detach = false; + ChannelStdinMode stdin_mode = kChannelStdinPipe; uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, - pty, rpc, overlapped, detach, cwd, - term_width, curwin->w_height_inner, + pty, rpc, overlapped, detach, stdin_mode, + cwd, term_width, curwin->w_height_inner, env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; @@ -10891,8 +11428,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // "test_garbagecollect_now()" function -static void f_test_garbagecollect_now(typval_T *argvars, - typval_T *rettv, FunPtr fptr) +static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // This is dangerous, any Lists and Dicts used internally may be freed // while still in use. @@ -10900,9 +11436,7 @@ static void f_test_garbagecollect_now(typval_T *argvars, } // "test_write_list_log()" function -static void f_test_write_list_log(typval_T *const argvars, - typval_T *const rettv, - FunPtr fptr) +static void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr) { const char *const fname = tv_get_string_chk(&argvars[0]); if (fname == NULL) { @@ -10977,24 +11511,24 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } rettv->vval.v_number = - timer_start(tv_get_number(&argvars[0]), repeat, &callback); + timer_start(tv_get_number(&argvars[0]), repeat, &callback); } // "timer_stop(timerid)" function static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - if (argvars[0].v_type != VAR_NUMBER) { - EMSG(_(e_number_exp)); - return; - } + if (argvars[0].v_type != VAR_NUMBER) { + EMSG(_(e_number_exp)); + return; + } - timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0])); - if (timer == NULL) { - return; - } + timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0])); + if (timer == NULL) { + return; + } - timer_stop(timer); + timer_stop(timer); } static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr) @@ -11195,19 +11729,28 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n = -1; switch (argvars[0].v_type) { - case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; - case VAR_STRING: n = VAR_TYPE_STRING; break; - case VAR_PARTIAL: - case VAR_FUNC: n = VAR_TYPE_FUNC; break; - case VAR_LIST: n = VAR_TYPE_LIST; break; - case VAR_DICT: n = VAR_TYPE_DICT; break; - case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; - case VAR_BOOL: n = VAR_TYPE_BOOL; break; - case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break; - case VAR_UNKNOWN: { - internal_error("f_type(UNKNOWN)"); - break; - } + case VAR_NUMBER: + n = VAR_TYPE_NUMBER; break; + case VAR_STRING: + n = VAR_TYPE_STRING; break; + case VAR_PARTIAL: + case VAR_FUNC: + n = VAR_TYPE_FUNC; break; + case VAR_LIST: + n = VAR_TYPE_LIST; break; + case VAR_DICT: + n = VAR_TYPE_DICT; break; + case VAR_FLOAT: + n = VAR_TYPE_FLOAT; break; + case VAR_BOOL: + n = VAR_TYPE_BOOL; break; + case VAR_SPECIAL: + n = VAR_TYPE_SPECIAL; break; + case VAR_BLOB: + n = VAR_TYPE_BLOB; break; + case VAR_UNKNOWN: + internal_error("f_type(UNKNOWN)"); + break; } rettv->vval.v_number = n; } @@ -11267,7 +11810,7 @@ static void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { colnr_T vcol = 0; - pos_T *fp; + pos_T *fp; int fnum = curbuf->b_fnum; fp = var2fpos(&argvars[0], FALSE, &fnum); @@ -11352,6 +11895,9 @@ static void f_win_gettype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = vim_strsave((char_u *)"popup"); } else if (wp == curwin && cmdwin_type != 0) { rettv->vval.v_string = vim_strsave((char_u *)"command"); + } else if (bt_quickfix(wp->w_buffer)) { + rettv->vval.v_string = vim_strsave((char_u *)(wp->w_llist_ref != NULL ? + "loclist" : "quickfix")); } } @@ -11515,10 +12061,12 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) win_new_width(curwin, curwin->w_width); changed_window_setting(); - if (curwin->w_topline <= 0) + if (curwin->w_topline <= 0) { curwin->w_topline = 1; - if (curwin->w_topline > curbuf->b_ml.ml_line_count) + } + if (curwin->w_topline > curbuf->b_ml.ml_line_count) { curwin->w_topline = curbuf->b_ml.ml_line_count; + } check_topfill(curwin, true); } } @@ -11528,7 +12076,7 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *dict; + dict_T *dict; tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; @@ -11579,16 +12127,17 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "writefile()"); + if (argvars[0].v_type == VAR_LIST) { + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) { + return; + } + }); + } else if (argvars[0].v_type != VAR_BLOB) { + EMSG2(_(e_invarg2), + _("writefile() first argument must be a List or a Blob")); return; } - const list_T *const list = argvars[0].vval.v_list; - TV_LIST_ITER_CONST(list, li, { - if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) { - return; - } - }); bool binary = false; bool append = false; @@ -11600,15 +12149,18 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } for (const char *p = flags; *p; p++) { switch (*p) { - case 'b': { binary = true; break; } - case 'a': { append = true; break; } - case 's': { do_fsync = true; break; } - case 'S': { do_fsync = false; break; } - default: { - // Using %s, p and not %c, *p to preserve multibyte characters - emsgf(_("E5060: Unknown flag: %s"), p); - return; - } + case 'b': + binary = true; break; + case 'a': + append = true; break; + case 's': + do_fsync = true; break; + case 'S': + do_fsync = false; break; + default: + // Using %s, p and not %c, *p to preserve multibyte characters + emsgf(_("E5060: Unknown flag: %s"), p); + return; } } } @@ -11628,7 +12180,13 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsgf(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error)); } else { - if (write_list(&fp, list, binary)) { + bool write_ok; + if (argvars[0].v_type == VAR_BLOB) { + write_ok = write_blob(&fp, argvars[0].vval.v_blob); + } else { + write_ok = write_list(&fp, argvars[0].vval.v_list, binary); + } + if (write_ok) { rettv->vval.v_number = 0; } if ((error = file_close(&fp, do_fsync)) != 0) { |