funcs.c (214990B)
1 #include <assert.h> 2 #include <float.h> 3 #include <inttypes.h> 4 #include <limits.h> 5 #include <math.h> 6 #include <signal.h> 7 #include <stdarg.h> 8 #include <stddef.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <time.h> 13 #include <uv.h> 14 15 #include "auto/config.h" 16 #include "klib/kvec.h" 17 #include "mpack/mpack_core.h" 18 #include "mpack/object.h" 19 #include "nvim/api/private/converter.h" 20 #include "nvim/api/private/defs.h" 21 #include "nvim/api/private/dispatch.h" 22 #include "nvim/api/private/helpers.h" 23 #include "nvim/api/vim.h" 24 #include "nvim/ascii_defs.h" 25 #include "nvim/assert_defs.h" 26 #include "nvim/autocmd.h" 27 #include "nvim/autocmd_defs.h" 28 #include "nvim/buffer.h" 29 #include "nvim/buffer_defs.h" 30 #include "nvim/channel.h" 31 #include "nvim/channel_defs.h" 32 #include "nvim/charset.h" 33 #include "nvim/cmdexpand.h" 34 #include "nvim/cmdexpand_defs.h" 35 #include "nvim/context.h" 36 #include "nvim/cursor.h" 37 #include "nvim/edit.h" 38 #include "nvim/errors.h" 39 #include "nvim/eval/buffer.h" 40 #include "nvim/eval/decode.h" 41 #include "nvim/eval/encode.h" 42 #include "nvim/eval/executor.h" 43 #include "nvim/eval/funcs.h" 44 #include "nvim/eval/typval.h" 45 #include "nvim/eval/typval_defs.h" 46 #include "nvim/eval/userfunc.h" 47 #include "nvim/eval/vars.h" 48 #include "nvim/eval/window.h" 49 #include "nvim/event/defs.h" 50 #include "nvim/event/loop.h" 51 #include "nvim/event/multiqueue.h" 52 #include "nvim/event/proc.h" 53 #include "nvim/event/time.h" 54 #include "nvim/ex_cmds.h" 55 #include "nvim/ex_cmds_defs.h" 56 #include "nvim/ex_docmd.h" 57 #include "nvim/ex_eval.h" 58 #include "nvim/ex_getln.h" 59 #include "nvim/garray.h" 60 #include "nvim/garray_defs.h" 61 #include "nvim/getchar.h" 62 #include "nvim/getchar_defs.h" 63 #include "nvim/gettext_defs.h" 64 #include "nvim/globals.h" 65 #include "nvim/grid.h" 66 #include "nvim/grid_defs.h" 67 #include "nvim/highlight_defs.h" 68 #include "nvim/highlight_group.h" 69 #include "nvim/indent.h" 70 #include "nvim/indent_c.h" 71 #include "nvim/input.h" 72 #include "nvim/insexpand.h" 73 #include "nvim/keycodes.h" 74 #include "nvim/lua/executor.h" 75 #include "nvim/macros_defs.h" 76 #include "nvim/main.h" 77 #include "nvim/mark.h" 78 #include "nvim/mark_defs.h" 79 #include "nvim/math.h" 80 #include "nvim/mbyte.h" 81 #include "nvim/mbyte_defs.h" 82 #include "nvim/memline.h" 83 #include "nvim/memory.h" 84 #include "nvim/memory_defs.h" 85 #include "nvim/menu.h" 86 #include "nvim/menu_defs.h" 87 #include "nvim/message.h" 88 #include "nvim/move.h" 89 #include "nvim/msgpack_rpc/channel.h" 90 #include "nvim/msgpack_rpc/channel_defs.h" 91 #include "nvim/msgpack_rpc/packer.h" 92 #include "nvim/msgpack_rpc/packer_defs.h" 93 #include "nvim/msgpack_rpc/server.h" 94 #include "nvim/normal.h" 95 #include "nvim/normal_defs.h" 96 #include "nvim/ops.h" 97 #include "nvim/option.h" 98 #include "nvim/option_defs.h" 99 #include "nvim/option_vars.h" 100 #include "nvim/optionstr.h" 101 #include "nvim/os/dl.h" 102 #include "nvim/os/fs.h" 103 #include "nvim/os/os.h" 104 #include "nvim/os/os_defs.h" 105 #include "nvim/os/pty_proc.h" 106 #include "nvim/os/shell.h" 107 #include "nvim/os/stdpaths_defs.h" 108 #include "nvim/os/time.h" 109 #include "nvim/path.h" 110 #include "nvim/plines.h" 111 #include "nvim/popupmenu.h" 112 #include "nvim/pos_defs.h" 113 #include "nvim/profile.h" 114 #include "nvim/regexp.h" 115 #include "nvim/regexp_defs.h" 116 #include "nvim/register.h" 117 #include "nvim/runtime.h" 118 #include "nvim/runtime_defs.h" 119 #include "nvim/search.h" 120 #include "nvim/sha256.h" 121 #include "nvim/spell.h" 122 #include "nvim/spellsuggest.h" 123 #include "nvim/state.h" 124 #include "nvim/state_defs.h" 125 #include "nvim/strings.h" 126 #include "nvim/syntax.h" 127 #include "nvim/tag.h" 128 #include "nvim/terminal.h" 129 #include "nvim/types_defs.h" 130 #include "nvim/ui.h" 131 #include "nvim/ui_compositor.h" 132 #include "nvim/version.h" 133 #include "nvim/vim_defs.h" 134 #include "nvim/window.h" 135 136 /// Describe data to return from find_some_match() 137 typedef enum { 138 kSomeMatch, ///< Data for match(). 139 kSomeMatchEnd, ///< Data for matchend(). 140 kSomeMatchList, ///< Data for matchlist(). 141 kSomeMatchStr, ///< Data for matchstr(). 142 kSomeMatchStrPos, ///< Data for matchstrpos(). 143 } SomeMatchType; 144 145 #include "eval/funcs.c.generated.h" 146 147 #ifdef _MSC_VER 148 // This prevents MSVC from replacing the functions with intrinsics, 149 // and causing errors when trying to get their addresses in funcs.generated.h 150 # pragma function(ceil) 151 # pragma function(floor) 152 #endif 153 154 PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES 155 PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH 156 #include "funcs.generated.h" 157 158 PRAGMA_DIAG_POP 159 PRAGMA_DIAG_POP 160 161 static const char *e_invalwindow = N_("E957: Invalid window number"); 162 static const char e_invalid_submatch_number_nr[] 163 = N_("E935: Invalid submatch number: %d"); 164 static const char e_string_list_or_blob_required[] 165 = N_("E1098: String, List or Blob required"); 166 static const char e_missing_function_argument[] 167 = N_("E1132: Missing function argument"); 168 169 /// Dummy va_list for passing to vim_snprintf 170 /// 171 /// Used because: 172 /// - passing a NULL pointer doesn't work when va_list isn't a pointer 173 /// - locally in the function results in a "used before set" warning 174 /// - using va_start() to initialize it gives "function with fixed args" error 175 static va_list dummy_ap; 176 177 /// Function given to ExpandGeneric() to obtain the list of internal 178 /// or user defined function names. 179 char *get_function_name(expand_T *xp, int idx) 180 { 181 static int intidx = -1; 182 183 if (idx == 0) { 184 intidx = -1; 185 } 186 if (intidx < 0) { 187 char *name = get_user_func_name(xp, idx); 188 if (name != NULL) { 189 if (*name != NUL && *name != '<' 190 && strncmp("g:", xp->xp_pattern, 2) == 0) { 191 return cat_prefix_varname('g', name); 192 } 193 return name; 194 } 195 } 196 197 const char *const key = functions[++intidx].name; 198 if (!key) { 199 return NULL; 200 } 201 const size_t key_len = strlen(key); 202 memcpy(IObuff, key, key_len); 203 IObuff[key_len] = '('; 204 if (functions[intidx].max_argc == 0) { 205 IObuff[key_len + 1] = ')'; 206 IObuff[key_len + 2] = NUL; 207 } else { 208 IObuff[key_len + 1] = NUL; 209 } 210 return IObuff; 211 } 212 213 /// Function given to ExpandGeneric() to obtain the list of internal or 214 /// user defined variable or function names. 215 char *get_expr_name(expand_T *xp, int idx) 216 { 217 static int intidx = -1; 218 219 if (idx == 0) { 220 intidx = -1; 221 } 222 if (intidx < 0) { 223 char *name = get_function_name(xp, idx); 224 if (name != NULL) { 225 return name; 226 } 227 } 228 return get_user_var_name(xp, ++intidx); 229 } 230 231 /// Find internal function in hash functions 232 /// 233 /// @param[in] name Name of the function. 234 /// 235 /// @return pointer to the function definition or NULL if not found. 236 const EvalFuncDef *find_internal_func(const char *const name) 237 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL 238 { 239 size_t len = strlen(name); 240 int index = find_internal_func_hash(name, len); 241 return index >= 0 ? &functions[index] : NULL; 242 } 243 244 /// Check the argument count to use for internal function "fdef". 245 /// @return -1 for failure, 0 if no method base accepted, 1 if method base is 246 /// first argument, 2 if method base is second argument, etc. 247 int check_internal_func(const EvalFuncDef *const fdef, const int argcount) 248 FUNC_ATTR_NONNULL_ALL 249 { 250 int res; 251 252 if (argcount < fdef->min_argc) { 253 res = FCERR_TOOFEW; 254 } else if (argcount > fdef->max_argc) { 255 res = FCERR_TOOMANY; 256 } else { 257 return fdef->base_arg; 258 } 259 260 const char *const name = fdef->name; 261 if (res == FCERR_TOOMANY) { 262 semsg(_(e_toomanyarg), name); 263 } else { 264 semsg(_(e_toofewarg), name); 265 } 266 return -1; 267 } 268 269 int call_internal_func(const char *const fname, const int argcount, typval_T *const argvars, 270 typval_T *const rettv) 271 FUNC_ATTR_NONNULL_ALL 272 { 273 const EvalFuncDef *const fdef = find_internal_func(fname); 274 if (fdef == NULL) { 275 return FCERR_UNKNOWN; 276 } else if (argcount < fdef->min_argc) { 277 return FCERR_TOOFEW; 278 } else if (argcount > fdef->max_argc) { 279 return FCERR_TOOMANY; 280 } 281 argvars[argcount].v_type = VAR_UNKNOWN; 282 fdef->func(argvars, rettv, fdef->data); 283 return FCERR_NONE; 284 } 285 286 /// Invoke a method for base->method(). 287 int call_internal_method(const char *const fname, const int argcount, typval_T *const argvars, 288 typval_T *const rettv, typval_T *const basetv) 289 FUNC_ATTR_NONNULL_ALL 290 { 291 const EvalFuncDef *const fdef = find_internal_func(fname); 292 if (fdef == NULL) { 293 return FCERR_UNKNOWN; 294 } else if (fdef->base_arg == BASE_NONE) { 295 return FCERR_NOTMETHOD; 296 } else if (argcount + 1 < fdef->min_argc) { 297 return FCERR_TOOFEW; 298 } else if (argcount + 1 > fdef->max_argc) { 299 return FCERR_TOOMANY; 300 } 301 302 typval_T argv[MAX_FUNC_ARGS + 1]; 303 const ptrdiff_t base_index = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; 304 if (argcount < base_index) { 305 return FCERR_TOOFEW; 306 } 307 memcpy(argv, argvars, (size_t)base_index * sizeof(typval_T)); 308 argv[base_index] = *basetv; 309 memcpy(argv + base_index + 1, argvars + base_index, 310 (size_t)(argcount - base_index) * sizeof(typval_T)); 311 argv[argcount + 1].v_type = VAR_UNKNOWN; 312 313 fdef->func(argv, rettv, fdef->data); 314 return FCERR_NONE; 315 } 316 317 /// @return true for a non-zero Number and a non-empty String. 318 static bool non_zero_arg(typval_T *argvars) 319 { 320 return ((argvars[0].v_type == VAR_NUMBER 321 && argvars[0].vval.v_number != 0) 322 || (argvars[0].v_type == VAR_BOOL 323 && argvars[0].vval.v_bool == kBoolVarTrue) 324 || (argvars[0].v_type == VAR_STRING 325 && argvars[0].vval.v_string != NULL 326 && *argvars[0].vval.v_string != NUL)); 327 } 328 329 /// Apply a floating point C function on a typval with one float_T. 330 /// 331 /// Some versions of glibc on i386 have an optimization that makes it harder to 332 /// call math functions indirectly from inside an inlined function, causing 333 /// compile-time errors. Avoid `inline` in that case. #3072 334 static void float_op_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 335 { 336 float_T f; 337 338 rettv->v_type = VAR_FLOAT; 339 if (tv_get_float_chk(argvars, &f)) { 340 rettv->vval.v_float = fptr.float_func(f); 341 } else { 342 rettv->vval.v_float = 0.0; 343 } 344 } 345 346 static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 347 { 348 if (check_secure()) { 349 return; 350 } 351 352 MsgpackRpcRequestHandler handler = *fptr.api_handler; 353 354 MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); 355 Arena arena = ARENA_EMPTY; 356 357 for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) { 358 ADD_C(args, vim_to_object(tv, &arena, false)); 359 } 360 361 Error err = ERROR_INIT; 362 Object result = handler.fn(VIML_INTERNAL_CALL, args, &arena, &err); 363 364 if (ERROR_SET(&err)) { 365 semsg_multiline("emsg", e_api_error, err.msg); 366 goto end; 367 } 368 369 object_to_vim_take_luaref(&result, rettv, true, &err); 370 371 end: 372 if (handler.ret_alloc) { 373 api_free_object(result); 374 } 375 arena_mem_free(arena_finish(&arena)); 376 api_clear_error(&err); 377 } 378 379 /// "abs(expr)" function 380 static void f_abs(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 381 { 382 if (argvars[0].v_type == VAR_FLOAT) { 383 float_op_wrapper(argvars, rettv, (EvalFuncData){ .float_func = &fabs }); 384 } else { 385 bool error = false; 386 387 varnumber_T n = tv_get_number_chk(&argvars[0], &error); 388 if (error) { 389 rettv->vval.v_number = -1; 390 } else if (n > 0) { 391 rettv->vval.v_number = n; 392 } else { 393 rettv->vval.v_number = -n; 394 } 395 } 396 } 397 398 /// "and(expr, expr)" function 399 static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 400 { 401 rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) 402 & tv_get_number_chk(&argvars[1], NULL); 403 } 404 405 /// "api_info()" function 406 static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 407 { 408 object_to_vim(api_metadata(), rettv, NULL); 409 } 410 411 /// "atan2()" function 412 static void f_atan2(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 413 { 414 float_T fx; 415 float_T fy; 416 417 rettv->v_type = VAR_FLOAT; 418 if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { 419 rettv->vval.v_float = atan2(fx, fy); 420 } else { 421 rettv->vval.v_float = 0.0; 422 } 423 } 424 425 /// Get buffer by number or pattern. 426 buf_T *tv_get_buf(typval_T *tv, int curtab_only) 427 { 428 if (tv->v_type == VAR_NUMBER) { 429 return buflist_findnr((int)tv->vval.v_number); 430 } 431 if (tv->v_type != VAR_STRING) { 432 return NULL; 433 } 434 435 char *name = tv->vval.v_string; 436 437 if (name == NULL || *name == NUL) { 438 return curbuf; 439 } 440 if (name[0] == '$' && name[1] == NUL) { 441 return lastbuf; 442 } 443 444 // Ignore 'magic' and 'cpoptions' here to make scripts portable 445 int save_magic = p_magic; 446 p_magic = true; 447 char *save_cpo = p_cpo; 448 p_cpo = empty_string_option; 449 450 buf_T *buf = buflist_findnr(buflist_findpat(name, name + strlen(name), 451 true, false, curtab_only)); 452 453 p_magic = save_magic; 454 p_cpo = save_cpo; 455 456 // If not found, try expanding the name, like done for bufexists(). 457 if (buf == NULL) { 458 buf = find_buffer(tv); 459 } 460 461 return buf; 462 } 463 464 /// Like tv_get_buf() but give an error message if the type is wrong. 465 buf_T *tv_get_buf_from_arg(typval_T *const tv) FUNC_ATTR_NONNULL_ALL 466 { 467 if (!tv_check_str_or_nr(tv)) { 468 return NULL; 469 } 470 emsg_off++; 471 buf_T *const buf = tv_get_buf(tv, false); 472 emsg_off--; 473 return buf; 474 } 475 476 /// Get the buffer from "arg" and give an error and return NULL if it is not 477 /// valid. 478 buf_T *get_buf_arg(typval_T *arg) 479 { 480 emsg_off++; 481 buf_T *buf = tv_get_buf(arg, false); 482 emsg_off--; 483 if (buf == NULL) { 484 semsg(_("E158: Invalid buffer name: %s"), tv_get_string(arg)); 485 } 486 return buf; 487 } 488 489 /// "byte2line(byte)" function 490 static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 491 { 492 int boff = (int)tv_get_number(&argvars[0]) - 1; 493 if (boff < 0) { 494 rettv->vval.v_number = -1; 495 } else { 496 rettv->vval.v_number = (varnumber_T)ml_find_line_or_offset(curbuf, 0, 497 &boff, false); 498 } 499 } 500 501 /// "call(func, arglist [, dict])" function 502 static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 503 { 504 if (tv_check_for_list_arg(argvars, 1) == FAIL) { 505 return; 506 } 507 if (argvars[1].vval.v_list == NULL) { 508 return; 509 } 510 511 bool owned = false; 512 char *func; 513 partial_T *partial = NULL; 514 if (argvars[0].v_type == VAR_FUNC) { 515 func = argvars[0].vval.v_string; 516 } else if (argvars[0].v_type == VAR_PARTIAL) { 517 partial = argvars[0].vval.v_partial; 518 func = partial_name(partial); 519 } else if (nlua_is_table_from_lua(&argvars[0])) { 520 // TODO(tjdevries): UnifiedCallback 521 func = nlua_register_table_as_callable(&argvars[0]); 522 owned = true; 523 } else { 524 func = (char *)tv_get_string(&argvars[0]); 525 } 526 527 if (func == NULL || *func == NUL) { 528 return; // type error, empty name or null function 529 } 530 char *tofree = NULL; 531 if (argvars[0].v_type == VAR_STRING) { 532 char *p = func; 533 tofree = trans_function_name(&p, false, TFN_INT|TFN_QUIET, NULL, NULL); 534 if (tofree == NULL) { 535 emsg_funcname(e_unknown_function_str, func); 536 return; 537 } 538 func = tofree; 539 } 540 541 dict_T *selfdict = NULL; 542 if (argvars[2].v_type != VAR_UNKNOWN) { 543 if (tv_check_for_dict_arg(argvars, 2) == FAIL) { 544 goto done; 545 } 546 selfdict = argvars[2].vval.v_dict; 547 } 548 549 func_call(func, &argvars[1], partial, selfdict, rettv); 550 551 done: 552 if (owned) { 553 func_unref(func); 554 } 555 xfree(tofree); 556 } 557 558 /// "changenr()" function 559 static void f_changenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 560 { 561 rettv->vval.v_number = curbuf->b_u_seq_cur; 562 } 563 564 /// "chanclose(id[, stream])" function 565 static void f_chanclose(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 566 { 567 rettv->v_type = VAR_NUMBER; 568 rettv->vval.v_number = 0; 569 570 if (check_secure()) { 571 return; 572 } 573 574 if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type != VAR_STRING 575 && argvars[1].v_type != VAR_UNKNOWN)) { 576 emsg(_(e_invarg)); 577 return; 578 } 579 580 ChannelPart part = kChannelPartAll; 581 if (argvars[1].v_type == VAR_STRING) { 582 char *stream = argvars[1].vval.v_string; 583 if (!strcmp(stream, "stdin")) { 584 part = kChannelPartStdin; 585 } else if (!strcmp(stream, "stdout")) { 586 part = kChannelPartStdout; 587 } else if (!strcmp(stream, "stderr")) { 588 part = kChannelPartStderr; 589 } else if (!strcmp(stream, "rpc")) { 590 part = kChannelPartRpc; 591 } else { 592 semsg(_("Invalid channel stream \"%s\""), stream); 593 return; 594 } 595 } 596 const char *error; 597 rettv->vval.v_number = channel_close((uint64_t)argvars[0].vval.v_number, part, &error); 598 if (!rettv->vval.v_number) { 599 emsg(error); 600 } 601 } 602 603 /// "chansend(id, data)" function 604 static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 605 { 606 rettv->v_type = VAR_NUMBER; 607 rettv->vval.v_number = 0; 608 609 if (check_secure()) { 610 return; 611 } 612 613 if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type == VAR_UNKNOWN) { 614 // First argument is the channel id and second is the data to write 615 emsg(_(e_invarg)); 616 return; 617 } 618 619 ptrdiff_t input_len = 0; 620 char *input = NULL; 621 uint64_t id = (uint64_t)argvars[0].vval.v_number; 622 #ifdef UNIX 623 bool crlf = false; 624 #else 625 Channel *chan = find_channel(id); 626 bool crlf = (chan != NULL && chan->term) ? true : false; 627 #endif 628 629 if (argvars[1].v_type == VAR_BLOB) { 630 const blob_T *const b = argvars[1].vval.v_blob; 631 input_len = tv_blob_len(b); 632 if (input_len > 0) { 633 input = xmemdup(b->bv_ga.ga_data, (size_t)input_len); 634 } 635 } else { 636 input = save_tv_as_string(&argvars[1], &input_len, false, crlf); 637 } 638 639 if (!input) { 640 // Either the error has been handled by save_tv_as_string(), 641 // or there is no input to send. 642 return; 643 } 644 const char *error = NULL; 645 rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error); 646 if (error) { 647 emsg(error); 648 } 649 } 650 651 /// "char2nr(string)" function 652 static void f_char2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 653 { 654 if (argvars[1].v_type != VAR_UNKNOWN) { 655 if (!tv_check_num(&argvars[1])) { 656 return; 657 } 658 } 659 660 rettv->vval.v_number = utf_ptr2char(tv_get_string(&argvars[0])); 661 } 662 663 /// Get the current cursor column and store it in 'rettv'. 664 /// 665 /// @return the character index of the column if 'charcol' is true, 666 /// otherwise the byte index of the column. 667 static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) 668 { 669 if (tv_check_for_string_or_list_arg(argvars, 0) == FAIL 670 || tv_check_for_opt_number_arg(argvars, 1) == FAIL) { 671 return; 672 } 673 674 win_T *wp = curwin; 675 676 if (argvars[1].v_type != VAR_UNKNOWN) { 677 // use the window specified in the second argument 678 tabpage_T *tp; 679 wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp); 680 if (wp == NULL || tp == NULL) { 681 return; 682 } 683 check_cursor(wp); 684 } 685 686 buf_T *bp = wp->w_buffer; 687 colnr_T col = 0; 688 int fnum = bp->b_fnum; 689 pos_T *fp = var2fpos(&argvars[0], false, &fnum, charcol, wp); 690 if (fp != NULL && fnum == bp->b_fnum) { 691 if (fp->col == MAXCOL) { 692 // '> can be MAXCOL, get the length of the line then 693 if (fp->lnum <= bp->b_ml.ml_line_count) { 694 col = ml_get_buf_len(bp, fp->lnum) + 1; 695 } else { 696 col = MAXCOL; 697 } 698 } else { 699 col = fp->col + 1; 700 // col(".") when the cursor is on the NUL at the end of the line 701 // because of "coladd" can be seen as an extra column. 702 if (virtual_active(wp) && fp == &wp->w_cursor) { 703 char *p = ml_get_buf(bp, wp->w_cursor.lnum) + wp->w_cursor.col; 704 if (wp->w_cursor.coladd >= 705 (colnr_T)win_chartabsize(wp, p, 706 wp->w_virtcol - wp->w_cursor.coladd)) { 707 int l; 708 if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) { 709 col += l; 710 } 711 } 712 } 713 } 714 } 715 rettv->vval.v_number = col; 716 } 717 718 /// "charcol()" function 719 static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 720 { 721 get_col(argvars, rettv, true); 722 } 723 724 win_T *get_optional_window(typval_T *argvars, int idx) 725 { 726 if (argvars[idx].v_type == VAR_UNKNOWN) { 727 return curwin; 728 } 729 730 win_T *win = find_win_by_nr_or_id(&argvars[idx]); 731 if (win == NULL) { 732 emsg(_(e_invalwindow)); 733 return NULL; 734 } 735 return win; 736 } 737 738 /// "col(string)" function 739 static void f_col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 740 { 741 get_col(argvars, rettv, false); 742 } 743 744 /// "confirm(message, buttons[, default [, type]])" function 745 static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 746 { 747 char buf[NUMBUFLEN]; 748 char buf2[NUMBUFLEN]; 749 const char *buttons = NULL; 750 int def = 1; 751 int type = VIM_GENERIC; 752 bool error = false; 753 754 const char *message = tv_get_string_chk(&argvars[0]); 755 if (message == NULL) { 756 error = true; 757 } 758 if (argvars[1].v_type != VAR_UNKNOWN) { 759 buttons = tv_get_string_buf_chk(&argvars[1], buf); 760 if (buttons == NULL) { 761 error = true; 762 } 763 if (argvars[2].v_type != VAR_UNKNOWN) { 764 def = (int)tv_get_number_chk(&argvars[2], &error); 765 if (argvars[3].v_type != VAR_UNKNOWN) { 766 const char *typestr = tv_get_string_buf_chk(&argvars[3], buf2); 767 if (typestr == NULL) { 768 error = true; 769 } else { 770 switch (TOUPPER_ASC(*typestr)) { 771 case 'E': 772 type = VIM_ERROR; break; 773 case 'Q': 774 type = VIM_QUESTION; break; 775 case 'I': 776 type = VIM_INFO; break; 777 case 'W': 778 type = VIM_WARNING; break; 779 case 'G': 780 type = VIM_GENERIC; break; 781 } 782 } 783 } 784 } 785 } 786 787 if (buttons == NULL || *buttons == NUL) { 788 buttons = _("&Ok"); 789 } 790 791 if (!error) { 792 rettv->vval.v_number = do_dialog(type, NULL, message, buttons, def, NULL, false); 793 } 794 } 795 796 /// "copy()" function 797 static void f_copy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 798 { 799 var_item_copy(NULL, &argvars[0], rettv, false, 0); 800 } 801 802 /// "ctxget([{index}])" function 803 static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 804 { 805 size_t index = 0; 806 if (argvars[0].v_type == VAR_NUMBER) { 807 index = (size_t)argvars[0].vval.v_number; 808 } else if (argvars[0].v_type != VAR_UNKNOWN) { 809 semsg(_(e_invarg2), "expected nothing or a Number as an argument"); 810 return; 811 } 812 813 Context *ctx = ctx_get(index); 814 if (ctx == NULL) { 815 semsg(_(e_invargNval), "index", "out of bounds"); 816 return; 817 } 818 819 Arena arena = ARENA_EMPTY; 820 Dict ctx_dict = ctx_to_dict(ctx, &arena); 821 Error err = ERROR_INIT; 822 object_to_vim(DICT_OBJ(ctx_dict), rettv, &err); 823 arena_mem_free(arena_finish(&arena)); 824 api_clear_error(&err); 825 } 826 827 /// "ctxpop()" function 828 static void f_ctxpop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 829 { 830 if (!ctx_restore(NULL, kCtxAll)) { 831 emsg(_("Context stack is empty")); 832 } 833 } 834 835 /// "ctxpush([{types}])" function 836 static void f_ctxpush(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 837 { 838 int types = kCtxAll; 839 if (argvars[0].v_type == VAR_LIST) { 840 types = 0; 841 TV_LIST_ITER(argvars[0].vval.v_list, li, { 842 typval_T *tv_li = TV_LIST_ITEM_TV(li); 843 if (tv_li->v_type == VAR_STRING) { 844 if (strequal(tv_li->vval.v_string, "regs")) { 845 types |= kCtxRegs; 846 } else if (strequal(tv_li->vval.v_string, "jumps")) { 847 types |= kCtxJumps; 848 } else if (strequal(tv_li->vval.v_string, "bufs")) { 849 types |= kCtxBufs; 850 } else if (strequal(tv_li->vval.v_string, "gvars")) { 851 types |= kCtxGVars; 852 } else if (strequal(tv_li->vval.v_string, "sfuncs")) { 853 types |= kCtxSFuncs; 854 } else if (strequal(tv_li->vval.v_string, "funcs")) { 855 types |= kCtxFuncs; 856 } 857 } 858 }); 859 } else if (argvars[0].v_type != VAR_UNKNOWN) { 860 semsg(_(e_invarg2), "expected nothing or a List as an argument"); 861 return; 862 } 863 ctx_save(NULL, types); 864 } 865 866 /// "ctxset({context}[, {index}])" function 867 static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 868 { 869 if (argvars[0].v_type != VAR_DICT) { 870 semsg(_(e_invarg2), "expected dictionary as first argument"); 871 return; 872 } 873 874 size_t index = 0; 875 if (argvars[1].v_type == VAR_NUMBER) { 876 index = (size_t)argvars[1].vval.v_number; 877 } else if (argvars[1].v_type != VAR_UNKNOWN) { 878 semsg(_(e_invarg2), "expected nothing or a Number as second argument"); 879 return; 880 } 881 882 Context *ctx = ctx_get(index); 883 if (ctx == NULL) { 884 semsg(_(e_invargNval), "index", "out of bounds"); 885 return; 886 } 887 888 const int save_did_emsg = did_emsg; 889 did_emsg = false; 890 891 Arena arena = ARENA_EMPTY; 892 Dict dict = vim_to_object(&argvars[0], &arena, true).data.dict; 893 Context tmp = CONTEXT_INIT; 894 Error err = ERROR_INIT; 895 ctx_from_dict(dict, &tmp, &err); 896 897 if (ERROR_SET(&err)) { 898 semsg("%s", err.msg); 899 ctx_free(&tmp); 900 } else { 901 ctx_free(ctx); 902 *ctx = tmp; 903 } 904 905 arena_mem_free(arena_finish(&arena)); 906 api_clear_error(&err); 907 did_emsg = save_did_emsg; 908 } 909 910 /// "ctxsize()" function 911 static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 912 { 913 rettv->v_type = VAR_NUMBER; 914 rettv->vval.v_number = (varnumber_T)ctx_size(); 915 } 916 917 /// Set the cursor position. 918 /// If "charcol" is true, then use the column number as a character offset. 919 /// Otherwise use the column number as a byte offset. 920 static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) 921 { 922 linenr_T lnum; 923 colnr_T col; 924 colnr_T coladd = 0; 925 bool set_curswant = true; 926 927 rettv->vval.v_number = -1; 928 if (argvars[0].v_type == VAR_LIST) { 929 pos_T pos; 930 colnr_T curswant = -1; 931 932 if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL) { 933 emsg(_(e_invarg)); 934 return; 935 } 936 937 lnum = pos.lnum; 938 col = pos.col; 939 coladd = pos.coladd; 940 if (curswant >= 0) { 941 curwin->w_curswant = curswant - 1; 942 set_curswant = false; 943 } 944 } else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING) 945 && (argvars[1].v_type == VAR_NUMBER || argvars[1].v_type == VAR_STRING)) { 946 lnum = tv_get_lnum(argvars); 947 if (lnum < 0) { 948 semsg(_(e_invarg2), tv_get_string(&argvars[0])); 949 } else if (lnum == 0) { 950 lnum = curwin->w_cursor.lnum; 951 } 952 col = (colnr_T)tv_get_number_chk(&argvars[1], NULL); 953 if (charcol) { 954 col = buf_charidx_to_byteidx(curbuf, lnum, (int)col) + 1; 955 } 956 if (argvars[2].v_type != VAR_UNKNOWN) { 957 coladd = (colnr_T)tv_get_number_chk(&argvars[2], NULL); 958 } 959 } else { 960 emsg(_(e_invarg)); 961 return; 962 } 963 if (lnum < 0 || col < 0 || coladd < 0) { 964 return; // type error; errmsg already given 965 } 966 if (lnum > 0) { 967 curwin->w_cursor.lnum = lnum; 968 } 969 if (col != MAXCOL && --col < 0) { 970 col = 0; 971 } 972 curwin->w_cursor.col = col; 973 curwin->w_cursor.coladd = coladd; 974 975 // Make sure the cursor is in a valid position. 976 check_cursor(curwin); 977 // Correct cursor for multi-byte character. 978 mb_adjust_cursor(); 979 980 curwin->w_set_curswant = set_curswant; 981 rettv->vval.v_number = 0; 982 } 983 984 /// "cursor(lnum, col)" function, or 985 /// "cursor(list)" 986 /// 987 /// Moves the cursor to the specified line and column. 988 /// 989 /// @return 0 when the position could be set, -1 otherwise. 990 static void f_cursor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 991 { 992 set_cursorpos(argvars, rettv, false); 993 } 994 995 /// "debugbreak()" function 996 static void f_debugbreak(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 997 { 998 rettv->vval.v_number = FAIL; 999 int pid = (int)tv_get_number(&argvars[0]); 1000 if (pid == 0) { 1001 emsg(_(e_invarg)); 1002 return; 1003 } 1004 1005 #ifdef MSWIN 1006 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); 1007 if (hProcess == NULL) { 1008 return; 1009 } 1010 1011 DebugBreakProcess(hProcess); 1012 CloseHandle(hProcess); 1013 rettv->vval.v_number = OK; 1014 #else 1015 uv_kill(pid, SIGINT); 1016 #endif 1017 } 1018 1019 /// "deepcopy()" function 1020 static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1021 { 1022 if (tv_check_for_opt_bool_arg(argvars, 1) == FAIL) { 1023 return; 1024 } 1025 1026 varnumber_T noref = 0; 1027 if (argvars[1].v_type != VAR_UNKNOWN) { 1028 noref = tv_get_bool_chk(&argvars[1], NULL); 1029 } 1030 1031 var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() : 0)); 1032 } 1033 1034 /// dictwatcheradd(dict, key, funcref) function 1035 static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1036 { 1037 if (check_secure()) { 1038 return; 1039 } 1040 1041 if (argvars[0].v_type != VAR_DICT) { 1042 semsg(_(e_invarg2), "dict"); 1043 return; 1044 } else if (argvars[0].vval.v_dict == NULL) { 1045 const char *const arg_errmsg = _("dictwatcheradd() argument"); 1046 const size_t arg_errmsg_len = strlen(arg_errmsg); 1047 semsg(_(e_cannot_change_readonly_variable_str), (int)arg_errmsg_len, arg_errmsg); 1048 return; 1049 } 1050 1051 if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { 1052 semsg(_(e_invarg2), "key"); 1053 return; 1054 } 1055 1056 const char *const key_pattern = tv_get_string_chk(argvars + 1); 1057 if (key_pattern == NULL) { 1058 return; 1059 } 1060 const size_t key_pattern_len = strlen(key_pattern); 1061 1062 Callback callback; 1063 if (!callback_from_typval(&callback, &argvars[2])) { 1064 semsg(_(e_invarg2), "funcref"); 1065 return; 1066 } 1067 1068 tv_dict_watcher_add(argvars[0].vval.v_dict, key_pattern, key_pattern_len, 1069 callback); 1070 } 1071 1072 /// dictwatcherdel(dict, key, funcref) function 1073 static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1074 { 1075 if (check_secure()) { 1076 return; 1077 } 1078 1079 if (argvars[0].v_type != VAR_DICT) { 1080 semsg(_(e_invarg2), "dict"); 1081 return; 1082 } 1083 1084 if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) { 1085 semsg(_(e_invarg2), "funcref"); 1086 return; 1087 } 1088 1089 const char *const key_pattern = tv_get_string_chk(argvars + 1); 1090 if (key_pattern == NULL) { 1091 return; 1092 } 1093 1094 Callback callback; 1095 if (!callback_from_typval(&callback, &argvars[2])) { 1096 return; 1097 } 1098 1099 if (!tv_dict_watcher_remove(argvars[0].vval.v_dict, key_pattern, 1100 strlen(key_pattern), callback)) { 1101 emsg("Couldn't find a watcher matching key and callback"); 1102 } 1103 1104 callback_free(&callback); 1105 } 1106 1107 /// "did_filetype()" function 1108 static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1109 { 1110 rettv->vval.v_number = curbuf->b_did_filetype; 1111 } 1112 1113 /// "empty({expr})" function 1114 static void f_empty(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1115 { 1116 bool n = true; 1117 1118 switch (argvars[0].v_type) { 1119 case VAR_STRING: 1120 case VAR_FUNC: 1121 n = argvars[0].vval.v_string == NULL 1122 || *argvars[0].vval.v_string == NUL; 1123 break; 1124 case VAR_PARTIAL: 1125 n = false; 1126 break; 1127 case VAR_NUMBER: 1128 n = argvars[0].vval.v_number == 0; 1129 break; 1130 case VAR_FLOAT: 1131 n = argvars[0].vval.v_float == 0.0; 1132 break; 1133 case VAR_LIST: 1134 n = (tv_list_len(argvars[0].vval.v_list) == 0); 1135 break; 1136 case VAR_DICT: 1137 n = (tv_dict_len(argvars[0].vval.v_dict) == 0); 1138 break; 1139 case VAR_BOOL: 1140 switch (argvars[0].vval.v_bool) { 1141 case kBoolVarTrue: 1142 n = false; 1143 break; 1144 case kBoolVarFalse: 1145 n = true; 1146 break; 1147 } 1148 break; 1149 case VAR_SPECIAL: 1150 n = argvars[0].vval.v_special == kSpecialVarNull; 1151 break; 1152 case VAR_BLOB: 1153 n = (tv_blob_len(argvars[0].vval.v_blob) == 0); 1154 break; 1155 case VAR_UNKNOWN: 1156 internal_error("f_empty(UNKNOWN)"); 1157 break; 1158 } 1159 1160 rettv->vval.v_number = n; 1161 } 1162 1163 /// "environ()" function 1164 static void f_environ(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1165 { 1166 tv_dict_alloc_ret(rettv); 1167 1168 size_t env_size = os_get_fullenv_size(); 1169 char **env = xmalloc(sizeof(*env) * (env_size + 1)); 1170 env[env_size] = NULL; 1171 1172 os_copy_fullenv(env, env_size); 1173 1174 for (ssize_t i = (ssize_t)env_size - 1; i >= 0; i--) { 1175 const char *str = env[i]; 1176 const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), 1177 '='); 1178 assert(end != NULL); 1179 ptrdiff_t len = end - str; 1180 assert(len > 0); 1181 const char *value = str + len + 1; 1182 1183 char c = env[i][len]; 1184 env[i][len] = NUL; 1185 1186 #ifdef MSWIN 1187 // Upper-case all the keys for Windows so we can detect duplicates 1188 char *const key = strcase_save(str, true); 1189 #else 1190 char *const key = xstrdup(str); 1191 #endif 1192 1193 env[i][len] = c; 1194 1195 if (tv_dict_find(rettv->vval.v_dict, key, len) != NULL) { 1196 // Since we're traversing from the end of the env block to the front, any 1197 // duplicate names encountered should be ignored. This preserves the 1198 // semantics of env vars defined later in the env block taking precedence. 1199 xfree(key); 1200 continue; 1201 } 1202 tv_dict_add_str(rettv->vval.v_dict, key, (size_t)len, value); 1203 xfree(key); 1204 } 1205 os_free_fullenv(env); 1206 } 1207 1208 /// "escape({string}, {chars})" function 1209 static void f_escape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1210 { 1211 char buf[NUMBUFLEN]; 1212 1213 rettv->vval.v_string = vim_strsave_escaped(tv_get_string(&argvars[0]), 1214 tv_get_string_buf(&argvars[1], buf)); 1215 rettv->v_type = VAR_STRING; 1216 } 1217 1218 /// "getenv()" function 1219 static void f_getenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1220 { 1221 char *p = vim_getenv(tv_get_string(&argvars[0])); 1222 1223 if (p == NULL) { 1224 rettv->v_type = VAR_SPECIAL; 1225 rettv->vval.v_special = kSpecialVarNull; 1226 return; 1227 } 1228 rettv->vval.v_string = p; 1229 rettv->v_type = VAR_STRING; 1230 } 1231 1232 /// "eval()" function 1233 static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1234 { 1235 const char *s = tv_get_string_chk(&argvars[0]); 1236 if (s != NULL) { 1237 s = skipwhite(s); 1238 } 1239 1240 const char *const expr_start = s; 1241 if (s == NULL || eval1((char **)&s, rettv, &EVALARG_EVALUATE) == FAIL) { 1242 if (expr_start != NULL && !aborting()) { 1243 semsg(_(e_invexpr2), expr_start); 1244 } 1245 need_clr_eos = false; 1246 rettv->v_type = VAR_NUMBER; 1247 rettv->vval.v_number = 0; 1248 } else if (*s != NUL) { 1249 semsg(_(e_trailing_arg), s); 1250 } 1251 } 1252 1253 /// "eventhandler()" function 1254 static void f_eventhandler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1255 { 1256 rettv->vval.v_number = vgetc_busy; 1257 } 1258 1259 typedef struct { 1260 const list_T *const l; 1261 const listitem_T *li; 1262 } GetListLineCookie; 1263 1264 static char *get_list_line(int c, void *cookie, int indent, bool do_concat) 1265 { 1266 GetListLineCookie *const p = (GetListLineCookie *)cookie; 1267 1268 const listitem_T *const item = p->li; 1269 if (item == NULL) { 1270 return NULL; 1271 } 1272 char buf[NUMBUFLEN]; 1273 const char *const s = tv_get_string_buf_chk(TV_LIST_ITEM_TV(item), buf); 1274 p->li = TV_LIST_ITEM_NEXT(p->l, item); 1275 return s == NULL ? NULL : xstrdup(s); 1276 } 1277 1278 void execute_common(typval_T *argvars, typval_T *rettv, int arg_off) 1279 { 1280 const int save_msg_silent = msg_silent; 1281 const int save_emsg_silent = emsg_silent; 1282 const bool save_emsg_noredir = emsg_noredir; 1283 const bool save_redir_off = redir_off; 1284 garray_T *const save_capture_ga = capture_ga; 1285 const int save_msg_col = msg_col; 1286 bool echo_output = false; 1287 1288 if (check_secure()) { 1289 return; 1290 } 1291 1292 if (argvars[arg_off + 1].v_type != VAR_UNKNOWN) { 1293 char buf[NUMBUFLEN]; 1294 const char *const s = tv_get_string_buf_chk(&argvars[arg_off + 1], buf); 1295 1296 if (s == NULL) { 1297 return; 1298 } 1299 if (*s == NUL) { 1300 echo_output = true; 1301 } 1302 if (strncmp(s, "silent", 6) == 0) { 1303 msg_silent++; 1304 } 1305 if (strcmp(s, "silent!") == 0) { 1306 emsg_silent = true; 1307 emsg_noredir = true; 1308 } 1309 } else { 1310 msg_silent++; 1311 } 1312 1313 garray_T capture_local; 1314 ga_init(&capture_local, (int)sizeof(char), 80); 1315 capture_ga = &capture_local; 1316 redir_off = false; 1317 if (!echo_output) { 1318 msg_col = 0; // prevent leading spaces 1319 } 1320 1321 if (argvars[arg_off].v_type != VAR_LIST) { 1322 do_cmdline_cmd(tv_get_string(&argvars[arg_off])); 1323 } else if (argvars[arg_off].vval.v_list != NULL) { 1324 list_T *const list = argvars[arg_off].vval.v_list; 1325 tv_list_ref(list); 1326 GetListLineCookie cookie = { 1327 .l = list, 1328 .li = tv_list_first(list), 1329 }; 1330 do_cmdline(NULL, get_list_line, (void *)&cookie, 1331 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); 1332 tv_list_unref(list); 1333 } 1334 msg_silent = save_msg_silent; 1335 emsg_silent = save_emsg_silent; 1336 emsg_noredir = save_emsg_noredir; 1337 redir_off = save_redir_off; 1338 // "silent reg" or "silent echo x" leaves msg_col somewhere in the line. 1339 if (echo_output) { 1340 // When not working silently: put it in column zero. A following 1341 // "echon" will overwrite the message, unavoidably. 1342 msg_col = 0; 1343 } else { 1344 // When working silently: Put it back where it was, since nothing 1345 // should have been written. 1346 msg_col = save_msg_col; 1347 } 1348 1349 ga_append(capture_ga, NUL); 1350 rettv->v_type = VAR_STRING; 1351 rettv->vval.v_string = capture_ga->ga_data; 1352 1353 capture_ga = save_capture_ga; 1354 } 1355 1356 /// "execute(command)" function 1357 static void f_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1358 { 1359 execute_common(argvars, rettv, 0); 1360 } 1361 1362 /// "exists()" function 1363 static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1364 { 1365 int n = false; 1366 1367 const char *p = tv_get_string(&argvars[0]); 1368 if (*p == '$') { // Environment variable. 1369 // First try "normal" environment variables (fast). 1370 if (os_env_exists(p + 1, false)) { 1371 n = true; 1372 } else { 1373 // Try expanding things like $VIM and ${HOME}. 1374 char *const exp = expand_env_save((char *)p); 1375 if (exp != NULL && *exp != '$') { 1376 n = true; 1377 } 1378 xfree(exp); 1379 } 1380 } else if (*p == '&' || *p == '+') { // Option. 1381 n = (eval_option(&p, NULL, true) == OK); 1382 if (*skipwhite(p) != NUL) { 1383 n = false; // Trailing garbage. 1384 } 1385 } else if (*p == '*') { // Internal or user defined function. 1386 n = strnequal(p, "*v:lua.", 7) ? nlua_func_exists(p + 7) : function_exists(p + 1, false); 1387 } else if (*p == ':') { 1388 n = cmd_exists(p + 1); 1389 } else if (*p == '#') { 1390 if (p[1] == '#') { 1391 n = autocmd_supported(p + 2); 1392 } else { 1393 n = au_exists(p + 1); 1394 } 1395 } else { // Internal variable. 1396 n = var_exists(p); 1397 } 1398 1399 rettv->vval.v_number = n; 1400 } 1401 1402 /// "expand()" function 1403 static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1404 { 1405 int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; 1406 bool error = false; 1407 #ifdef BACKSLASH_IN_FILENAME 1408 char *p_csl_save = p_csl; 1409 1410 // avoid using 'completeslash' here 1411 p_csl = empty_string_option; 1412 #endif 1413 1414 rettv->v_type = VAR_STRING; 1415 if (argvars[1].v_type != VAR_UNKNOWN 1416 && argvars[2].v_type != VAR_UNKNOWN 1417 && tv_get_number_chk(&argvars[2], &error) 1418 && !error) { 1419 tv_list_set_ret(rettv, NULL); 1420 } 1421 1422 const char *s = tv_get_string(&argvars[0]); 1423 if (*s == '%' || *s == '#' || *s == '<') { 1424 if (p_verbose == 0) { 1425 emsg_off++; 1426 } 1427 size_t len; 1428 const char *errormsg = NULL; 1429 char *result = eval_vars((char *)s, s, &len, NULL, &errormsg, NULL, false); 1430 if (p_verbose == 0) { 1431 emsg_off--; 1432 } else if (errormsg != NULL) { 1433 emsg(errormsg); 1434 } 1435 if (rettv->v_type == VAR_LIST) { 1436 tv_list_alloc_ret(rettv, (result != NULL)); 1437 if (result != NULL) { 1438 tv_list_append_string(rettv->vval.v_list, result, -1); 1439 } 1440 XFREE_CLEAR(result); 1441 } else { 1442 rettv->vval.v_string = result; 1443 } 1444 } else { 1445 // When the optional second argument is non-zero, don't remove matches 1446 // for 'wildignore' and don't put matches for 'suffixes' at the end. 1447 if (argvars[1].v_type != VAR_UNKNOWN 1448 && tv_get_number_chk(&argvars[1], &error)) { 1449 options |= WILD_KEEP_ALL; 1450 } 1451 if (!error) { 1452 expand_T xpc; 1453 ExpandInit(&xpc); 1454 xpc.xp_context = EXPAND_FILES; 1455 if (p_wic) { 1456 options += WILD_ICASE; 1457 } 1458 if (rettv->v_type == VAR_STRING) { 1459 rettv->vval.v_string = ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL); 1460 } else { 1461 ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL_KEEP); 1462 tv_list_alloc_ret(rettv, xpc.xp_numfiles); 1463 for (int i = 0; i < xpc.xp_numfiles; i++) { 1464 tv_list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); 1465 } 1466 ExpandCleanup(&xpc); 1467 } 1468 } else { 1469 rettv->vval.v_string = NULL; 1470 } 1471 } 1472 #ifdef BACKSLASH_IN_FILENAME 1473 p_csl = p_csl_save; 1474 #endif 1475 } 1476 1477 /// "menu_get(path [, modes])" function 1478 static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1479 { 1480 tv_list_alloc_ret(rettv, kListLenMayKnow); 1481 int modes = MENU_ALL_MODES; 1482 if (argvars[1].v_type == VAR_STRING) { 1483 const char *const strmodes = tv_get_string(&argvars[1]); 1484 modes = get_menu_cmd_modes(strmodes, false, NULL, NULL); 1485 } 1486 menu_get((char *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list); 1487 } 1488 1489 /// "expandcmd()" function 1490 /// Expand all the special characters in a command string. 1491 static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1492 { 1493 const char *errormsg = NULL; 1494 bool emsgoff = true; 1495 1496 if (argvars[1].v_type == VAR_DICT 1497 && tv_dict_get_bool(argvars[1].vval.v_dict, "errmsg", kBoolVarFalse)) { 1498 emsgoff = false; 1499 } 1500 1501 rettv->v_type = VAR_STRING; 1502 char *cmdstr = xstrdup(tv_get_string(&argvars[0])); 1503 1504 exarg_T eap = { 1505 .cmd = cmdstr, 1506 .arg = cmdstr, 1507 .usefilter = false, 1508 .nextcmd = NULL, 1509 .cmdidx = CMD_USER, 1510 }; 1511 eap.argt |= EX_NOSPC; 1512 1513 if (emsgoff) { 1514 emsg_off++; 1515 } 1516 if (expand_filename(&eap, &cmdstr, &errormsg) == FAIL) { 1517 if (!emsgoff && errormsg != NULL && *errormsg != NUL) { 1518 emsg(errormsg); 1519 } 1520 } 1521 if (emsgoff) { 1522 emsg_off--; 1523 } 1524 1525 rettv->vval.v_string = cmdstr; 1526 } 1527 1528 /// "flatten()" and "flattennew()" functions 1529 static void flatten_common(typval_T *argvars, typval_T *rettv, bool make_copy) 1530 { 1531 bool error = false; 1532 1533 if (argvars[0].v_type != VAR_LIST) { 1534 semsg(_(e_listarg), "flatten()"); 1535 return; 1536 } 1537 1538 int maxdepth; 1539 if (argvars[1].v_type == VAR_UNKNOWN) { 1540 maxdepth = 999999; 1541 } else { 1542 maxdepth = (int)tv_get_number_chk(&argvars[1], &error); 1543 if (error) { 1544 return; 1545 } 1546 if (maxdepth < 0) { 1547 emsg(_("E900: maxdepth must be non-negative number")); 1548 return; 1549 } 1550 } 1551 1552 list_T *list = argvars[0].vval.v_list; 1553 rettv->v_type = VAR_LIST; 1554 rettv->vval.v_list = list; 1555 if (list == NULL) { 1556 return; 1557 } 1558 1559 if (make_copy) { 1560 list = tv_list_copy(NULL, list, false, get_copyID()); 1561 rettv->vval.v_list = list; 1562 if (list == NULL) { 1563 return; 1564 } 1565 } else { 1566 if (value_check_lock(tv_list_locked(list), N_("flatten() argument"), TV_TRANSLATE)) { 1567 return; 1568 } 1569 tv_list_ref(list); 1570 } 1571 1572 tv_list_flatten(list, NULL, tv_list_len(list), maxdepth); 1573 } 1574 1575 /// "flatten(list[, {maxdepth}])" function 1576 static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1577 { 1578 flatten_common(argvars, rettv, false); 1579 } 1580 1581 /// "flattennew(list[, {maxdepth}])" function 1582 static void f_flattennew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1583 { 1584 flatten_common(argvars, rettv, true); 1585 } 1586 1587 /// "feedkeys()" function 1588 static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1589 { 1590 // This is not allowed in the sandbox. If the commands would still be 1591 // executed in the sandbox it would be OK, but it probably happens later, 1592 // when "sandbox" is no longer set. 1593 if (check_secure()) { 1594 return; 1595 } 1596 1597 const char *const keys = tv_get_string(&argvars[0]); 1598 char nbuf[NUMBUFLEN]; 1599 const char *flags = NULL; 1600 if (argvars[1].v_type != VAR_UNKNOWN) { 1601 flags = tv_get_string_buf(&argvars[1], nbuf); 1602 } 1603 1604 nvim_feedkeys(cstr_as_string(keys), 1605 cstr_as_string(flags), true); 1606 } 1607 1608 /// "float2nr({float})" function 1609 static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1610 { 1611 float_T f; 1612 1613 if (!tv_get_float_chk(argvars, &f)) { 1614 return; 1615 } 1616 1617 if (f <= (float_T)(-VARNUMBER_MAX) + DBL_EPSILON) { 1618 rettv->vval.v_number = -VARNUMBER_MAX; 1619 } else if (f >= (float_T)VARNUMBER_MAX - DBL_EPSILON) { 1620 rettv->vval.v_number = VARNUMBER_MAX; 1621 } else { 1622 rettv->vval.v_number = (varnumber_T)f; 1623 } 1624 } 1625 1626 /// "fmod()" function 1627 static void f_fmod(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1628 { 1629 float_T fx; 1630 float_T fy; 1631 1632 rettv->v_type = VAR_FLOAT; 1633 if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { 1634 rettv->vval.v_float = fmod(fx, fy); 1635 } else { 1636 rettv->vval.v_float = 0.0; 1637 } 1638 } 1639 1640 /// "fnameescape({string})" function 1641 static void f_fnameescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1642 { 1643 rettv->vval.v_string = vim_strsave_fnameescape(tv_get_string(&argvars[0]), VSE_NONE); 1644 rettv->v_type = VAR_STRING; 1645 } 1646 1647 /// "foreground()" function 1648 static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1649 { 1650 } 1651 1652 /// "function()" function 1653 /// "funcref()" function 1654 static void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) 1655 { 1656 char *s; 1657 char *name; 1658 bool use_string = false; 1659 partial_T *arg_pt = NULL; 1660 char *trans_name = NULL; 1661 1662 if (argvars[0].v_type == VAR_FUNC) { 1663 // function(MyFunc, [arg], dict) 1664 s = argvars[0].vval.v_string; 1665 } else if (argvars[0].v_type == VAR_PARTIAL 1666 && argvars[0].vval.v_partial != NULL) { 1667 // function(dict.MyFunc, [arg]) 1668 arg_pt = argvars[0].vval.v_partial; 1669 s = partial_name(arg_pt); 1670 // TODO(bfredl): do the entire nlua_is_table_from_lua dance 1671 } else { 1672 // function('MyFunc', [arg], dict) 1673 s = (char *)tv_get_string(&argvars[0]); 1674 use_string = true; 1675 } 1676 1677 if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { 1678 name = s; 1679 trans_name = save_function_name(&name, false, 1680 TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL); 1681 if (*name != NUL) { 1682 s = NULL; 1683 } 1684 } 1685 if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) 1686 || (is_funcref && trans_name == NULL)) { 1687 semsg(_(e_invarg2), (use_string ? tv_get_string(&argvars[0]) : s)); 1688 // Don't check an autoload name for existence here. 1689 } else if (trans_name != NULL 1690 && (is_funcref 1691 ? find_func(trans_name) == NULL 1692 : !translated_function_exists(trans_name))) { 1693 semsg(_("E700: Unknown function: %s"), s); 1694 } else { 1695 int dict_idx = 0; 1696 int arg_idx = 0; 1697 list_T *list = NULL; 1698 if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { 1699 // Expand s: and <SID> into <SNR>nr_, so that the function can 1700 // also be called from another script. Using trans_function_name() 1701 // would also work, but some plugins depend on the name being 1702 // printable text. 1703 name = get_scriptlocal_funcname(s); 1704 } else { 1705 name = xstrdup(s); 1706 } 1707 1708 if (argvars[1].v_type != VAR_UNKNOWN) { 1709 if (argvars[2].v_type != VAR_UNKNOWN) { 1710 // function(name, [args], dict) 1711 arg_idx = 1; 1712 dict_idx = 2; 1713 } else if (argvars[1].v_type == VAR_DICT) { 1714 // function(name, dict) 1715 dict_idx = 1; 1716 } else { 1717 // function(name, [args]) 1718 arg_idx = 1; 1719 } 1720 if (dict_idx > 0) { 1721 if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) { 1722 xfree(name); 1723 goto theend; 1724 } 1725 if (argvars[dict_idx].vval.v_dict == NULL) { 1726 dict_idx = 0; 1727 } 1728 } 1729 if (arg_idx > 0) { 1730 if (argvars[arg_idx].v_type != VAR_LIST) { 1731 emsg(_("E923: Second argument of function() must be " 1732 "a list or a dict")); 1733 xfree(name); 1734 goto theend; 1735 } 1736 list = argvars[arg_idx].vval.v_list; 1737 if (tv_list_len(list) == 0) { 1738 arg_idx = 0; 1739 } else if (tv_list_len(list) > MAX_FUNC_ARGS) { 1740 emsg_funcname(e_toomanyarg, s); 1741 xfree(name); 1742 goto theend; 1743 } 1744 } 1745 } 1746 if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { 1747 partial_T *const pt = xcalloc(1, sizeof(*pt)); 1748 1749 // result is a VAR_PARTIAL 1750 if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { 1751 const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); 1752 const int lv_len = tv_list_len(list); 1753 1754 pt->pt_argc = arg_len + lv_len; 1755 pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc); 1756 int i = 0; 1757 for (; i < arg_len; i++) { 1758 tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); 1759 } 1760 if (lv_len > 0) { 1761 TV_LIST_ITER(list, li, { 1762 tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); 1763 }); 1764 } 1765 } 1766 1767 // For "function(dict.func, [], dict)" and "func" is a partial 1768 // use "dict". That is backwards compatible. 1769 if (dict_idx > 0) { 1770 // The dict is bound explicitly, pt_auto is false 1771 pt->pt_dict = argvars[dict_idx].vval.v_dict; 1772 (pt->pt_dict->dv_refcount)++; 1773 } else if (arg_pt != NULL) { 1774 // If the dict was bound automatically the result is also 1775 // bound automatically. 1776 pt->pt_dict = arg_pt->pt_dict; 1777 pt->pt_auto = arg_pt->pt_auto; 1778 if (pt->pt_dict != NULL) { 1779 (pt->pt_dict->dv_refcount)++; 1780 } 1781 } 1782 1783 pt->pt_refcount = 1; 1784 if (arg_pt != NULL && arg_pt->pt_func != NULL) { 1785 pt->pt_func = arg_pt->pt_func; 1786 func_ptr_ref(pt->pt_func); 1787 xfree(name); 1788 } else if (is_funcref) { 1789 pt->pt_func = find_func(trans_name); 1790 func_ptr_ref(pt->pt_func); 1791 xfree(name); 1792 } else { 1793 pt->pt_name = name; 1794 func_ref(name); 1795 } 1796 1797 rettv->v_type = VAR_PARTIAL; 1798 rettv->vval.v_partial = pt; 1799 } else { 1800 // result is a VAR_FUNC 1801 rettv->v_type = VAR_FUNC; 1802 rettv->vval.v_string = name; 1803 func_ref(name); 1804 } 1805 } 1806 theend: 1807 xfree(trans_name); 1808 } 1809 1810 static void f_funcref(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1811 { 1812 common_function(argvars, rettv, true); 1813 } 1814 1815 static void f_function(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1816 { 1817 common_function(argvars, rettv, false); 1818 } 1819 1820 /// "garbagecollect()" function 1821 static void f_garbagecollect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1822 { 1823 // This is postponed until we are back at the toplevel, because we may be 1824 // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". 1825 want_garbage_collect = true; 1826 1827 if (argvars[0].v_type != VAR_UNKNOWN && tv_get_number(&argvars[0]) == 1) { 1828 garbage_collect_at_exit = true; 1829 } 1830 } 1831 1832 /// "get()" function 1833 static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1834 { 1835 typval_T *tv = NULL; 1836 bool what_is_dict = false; 1837 1838 if (argvars[0].v_type == VAR_BLOB) { 1839 bool error = false; 1840 int idx = (int)tv_get_number_chk(&argvars[1], &error); 1841 1842 if (!error) { 1843 rettv->v_type = VAR_NUMBER; 1844 if (idx < 0) { 1845 idx = tv_blob_len(argvars[0].vval.v_blob) + idx; 1846 } 1847 if (idx < 0 || idx >= tv_blob_len(argvars[0].vval.v_blob)) { 1848 rettv->vval.v_number = -1; 1849 } else { 1850 rettv->vval.v_number = tv_blob_get(argvars[0].vval.v_blob, idx); 1851 tv = rettv; 1852 } 1853 } 1854 } else if (argvars[0].v_type == VAR_LIST) { 1855 list_T *l = argvars[0].vval.v_list; 1856 if (l != NULL) { 1857 bool error = false; 1858 1859 listitem_T *li = tv_list_find(l, (int)tv_get_number_chk(&argvars[1], &error)); 1860 if (!error && li != NULL) { 1861 tv = TV_LIST_ITEM_TV(li); 1862 } 1863 } 1864 } else if (argvars[0].v_type == VAR_DICT) { 1865 dict_T *d = argvars[0].vval.v_dict; 1866 if (d != NULL) { 1867 dictitem_T *di = tv_dict_find(d, tv_get_string(&argvars[1]), -1); 1868 if (di != NULL) { 1869 tv = &di->di_tv; 1870 } 1871 } 1872 } else if (tv_is_func(argvars[0])) { 1873 partial_T *pt; 1874 partial_T fref_pt; 1875 1876 if (argvars[0].v_type == VAR_PARTIAL) { 1877 pt = argvars[0].vval.v_partial; 1878 } else { 1879 CLEAR_FIELD(fref_pt); 1880 fref_pt.pt_name = argvars[0].vval.v_string; 1881 pt = &fref_pt; 1882 } 1883 1884 if (pt != NULL) { 1885 const char *const what = tv_get_string(&argvars[1]); 1886 1887 if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) { 1888 const char *name = partial_name(pt); 1889 rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); 1890 assert(name != NULL); 1891 if (rettv->v_type == VAR_FUNC) { 1892 func_ref((char *)name); 1893 } 1894 if (*what == 'n' && pt->pt_name == NULL && pt->pt_func != NULL) { 1895 // use <SNR> instead of the byte code 1896 name = printable_func_name(pt->pt_func); 1897 } 1898 rettv->vval.v_string = xstrdup(name); 1899 } else if (strcmp(what, "dict") == 0) { 1900 what_is_dict = true; 1901 if (pt->pt_dict != NULL) { 1902 tv_dict_set_ret(rettv, pt->pt_dict); 1903 } 1904 } else if (strcmp(what, "args") == 0) { 1905 rettv->v_type = VAR_LIST; 1906 tv_list_alloc_ret(rettv, pt->pt_argc); 1907 for (int i = 0; i < pt->pt_argc; i++) { 1908 tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); 1909 } 1910 } else if (strcmp(what, "arity") == 0) { 1911 int required = 0; 1912 int optional = 0; 1913 bool varargs = false; 1914 const char *name = partial_name(pt); 1915 1916 get_func_arity(name, &required, &optional, &varargs); 1917 1918 rettv->v_type = VAR_DICT; 1919 tv_dict_alloc_ret(rettv); 1920 dict_T *dict = rettv->vval.v_dict; 1921 1922 // Take into account the arguments of the partial, if any. 1923 // Note that it is possible to supply more arguments than the function 1924 // accepts. 1925 if (pt->pt_argc >= required + optional) { 1926 required = optional = 0; 1927 } else if (pt->pt_argc > required) { 1928 optional -= pt->pt_argc - required; 1929 required = 0; 1930 } else { 1931 required -= pt->pt_argc; 1932 } 1933 1934 tv_dict_add_nr(dict, S_LEN("required"), required); 1935 tv_dict_add_nr(dict, S_LEN("optional"), optional); 1936 tv_dict_add_bool(dict, S_LEN("varargs"), varargs); 1937 } else { 1938 semsg(_(e_invarg2), what); 1939 } 1940 1941 // When {what} == "dict" and pt->pt_dict == NULL, evaluate the 1942 // third argument 1943 if (!what_is_dict) { 1944 return; 1945 } 1946 } 1947 } else { 1948 semsg(_(e_listdictblobarg), "get()"); 1949 } 1950 1951 if (tv == NULL) { 1952 if (argvars[2].v_type != VAR_UNKNOWN) { 1953 tv_copy(&argvars[2], rettv); 1954 } 1955 } else { 1956 tv_copy(tv, rettv); 1957 } 1958 } 1959 1960 /// "getchangelist()" function 1961 static void f_getchangelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 1962 { 1963 tv_list_alloc_ret(rettv, 2); 1964 1965 const buf_T *buf; 1966 if (argvars[0].v_type == VAR_UNKNOWN) { 1967 buf = curbuf; 1968 } else { 1969 vim_ignored = (int)tv_get_number(&argvars[0]); // issue errmsg if type error 1970 emsg_off++; 1971 buf = tv_get_buf(&argvars[0], false); 1972 emsg_off--; 1973 } 1974 if (buf == NULL) { 1975 return; 1976 } 1977 1978 list_T *const l = tv_list_alloc(buf->b_changelistlen); 1979 tv_list_append_list(rettv->vval.v_list, l); 1980 // The current window change list index tracks only the position for the 1981 // current buffer. For other buffers use the stored index for the current 1982 // window, or, if that's not available, the change list length. 1983 int changelistindex; 1984 if (buf == curwin->w_buffer) { 1985 changelistindex = curwin->w_changelistidx; 1986 } else { 1987 changelistindex = buf->b_changelistlen; 1988 1989 for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) { 1990 WinInfo *wip = kv_A(buf->b_wininfo, i); 1991 if (wip->wi_win == curwin) { 1992 changelistindex = wip->wi_changelistidx; 1993 break; 1994 } 1995 } 1996 } 1997 tv_list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex); 1998 1999 for (int i = 0; i < buf->b_changelistlen; i++) { 2000 if (buf->b_changelist[i].mark.lnum == 0) { 2001 continue; 2002 } 2003 dict_T *const d = tv_dict_alloc(); 2004 tv_list_append_dict(l, d); 2005 tv_dict_add_nr(d, S_LEN("lnum"), buf->b_changelist[i].mark.lnum); 2006 tv_dict_add_nr(d, S_LEN("col"), buf->b_changelist[i].mark.col); 2007 tv_dict_add_nr(d, S_LEN("coladd"), buf->b_changelist[i].mark.coladd); 2008 } 2009 } 2010 2011 static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool charcol) 2012 { 2013 pos_T *fp = NULL; 2014 pos_T pos; 2015 win_T *wp = curwin; 2016 int fnum = -1; 2017 2018 if (getcurpos) { 2019 if (argvars[0].v_type != VAR_UNKNOWN) { 2020 wp = find_win_by_nr_or_id(&argvars[0]); 2021 if (wp != NULL) { 2022 fp = &wp->w_cursor; 2023 } 2024 } else { 2025 fp = &curwin->w_cursor; 2026 } 2027 if (fp != NULL && charcol) { 2028 pos = *fp; 2029 pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col); 2030 fp = &pos; 2031 } 2032 } else { 2033 fp = var2fpos(&argvars[0], true, &fnum, charcol, curwin); 2034 } 2035 2036 list_T *const l = tv_list_alloc_ret(rettv, 4 + getcurpos); 2037 tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); 2038 tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0)); 2039 tv_list_append_number(l, ((fp != NULL) 2040 ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) 2041 : (varnumber_T)0)); 2042 tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); 2043 if (getcurpos) { 2044 const bool save_set_curswant = curwin->w_set_curswant; 2045 const colnr_T save_curswant = curwin->w_curswant; 2046 const colnr_T save_virtcol = curwin->w_virtcol; 2047 2048 if (wp == curwin) { 2049 update_curswant(); 2050 } 2051 tv_list_append_number(l, (wp == NULL) ? 0 : ((wp->w_curswant == MAXCOL) 2052 ? (varnumber_T)MAXCOL 2053 : (varnumber_T)wp->w_curswant + 1)); 2054 2055 // Do not change "curswant", as it is unexpected that a get 2056 // function has a side effect. 2057 if (wp == curwin && save_set_curswant) { 2058 curwin->w_set_curswant = save_set_curswant; 2059 curwin->w_curswant = save_curswant; 2060 curwin->w_virtcol = save_virtcol; 2061 curwin->w_valid &= ~VALID_VIRTCOL; 2062 } 2063 } 2064 } 2065 2066 /// "getcharpos()" function 2067 static void f_getcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2068 { 2069 getpos_both(argvars, rettv, false, true); 2070 } 2071 2072 /// "getcharsearch()" function 2073 static void f_getcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2074 { 2075 tv_dict_alloc_ret(rettv); 2076 2077 dict_T *dict = rettv->vval.v_dict; 2078 2079 tv_dict_add_str(dict, S_LEN("char"), last_csearch()); 2080 tv_dict_add_nr(dict, S_LEN("forward"), last_csearch_forward()); 2081 tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); 2082 } 2083 2084 /// "getfontname()" function 2085 static void f_getfontname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2086 { 2087 rettv->v_type = VAR_STRING; 2088 rettv->vval.v_string = NULL; 2089 } 2090 2091 /// "getjumplist()" function 2092 static void f_getjumplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2093 { 2094 tv_list_alloc_ret(rettv, kListLenMayKnow); 2095 win_T *const wp = find_tabwin(&argvars[0], &argvars[1]); 2096 if (wp == NULL) { 2097 return; 2098 } 2099 2100 cleanup_jumplist(wp, true); 2101 2102 list_T *const l = tv_list_alloc(wp->w_jumplistlen); 2103 tv_list_append_list(rettv->vval.v_list, l); 2104 tv_list_append_number(rettv->vval.v_list, wp->w_jumplistidx); 2105 2106 for (int i = 0; i < wp->w_jumplistlen; i++) { 2107 if (wp->w_jumplist[i].fmark.mark.lnum == 0) { 2108 continue; 2109 } 2110 dict_T *const d = tv_dict_alloc(); 2111 tv_list_append_dict(l, d); 2112 tv_dict_add_nr(d, S_LEN("lnum"), wp->w_jumplist[i].fmark.mark.lnum); 2113 tv_dict_add_nr(d, S_LEN("col"), wp->w_jumplist[i].fmark.mark.col); 2114 tv_dict_add_nr(d, S_LEN("coladd"), wp->w_jumplist[i].fmark.mark.coladd); 2115 tv_dict_add_nr(d, S_LEN("bufnr"), wp->w_jumplist[i].fmark.fnum); 2116 if (wp->w_jumplist[i].fname != NULL) { 2117 tv_dict_add_str(d, S_LEN("filename"), wp->w_jumplist[i].fname); 2118 } 2119 } 2120 } 2121 2122 /// "getmarklist()" function 2123 static void f_getmarklist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2124 { 2125 tv_list_alloc_ret(rettv, kListLenMayKnow); 2126 2127 if (argvars[0].v_type == VAR_UNKNOWN) { 2128 get_global_marks(rettv->vval.v_list); 2129 return; 2130 } 2131 2132 buf_T *buf = tv_get_buf(&argvars[0], false); 2133 if (buf == NULL) { 2134 return; 2135 } 2136 2137 get_buf_local_marks(buf, rettv->vval.v_list); 2138 } 2139 2140 /// "getpid()" function 2141 static void f_getpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2142 { 2143 rettv->vval.v_number = os_get_pid(); 2144 } 2145 2146 /// "getcurpos(string)" function 2147 static void f_getcurpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2148 { 2149 getpos_both(argvars, rettv, true, false); 2150 } 2151 2152 static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2153 { 2154 getpos_both(argvars, rettv, true, true); 2155 } 2156 2157 /// "getpos(string)" function 2158 static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2159 { 2160 getpos_both(argvars, rettv, false, false); 2161 } 2162 2163 /// Convert from block_def to string 2164 static String block_def2str(struct block_def *bd) 2165 { 2166 size_t size = (size_t)bd->startspaces + (size_t)bd->endspaces + (size_t)bd->textlen; 2167 String ret = { .data = xmalloc(size + 1) }; 2168 2169 memset(ret.data, ' ', (size_t)bd->startspaces); 2170 ret.size += (size_t)bd->startspaces; 2171 memmove(ret.data + ret.size, bd->textstart, (size_t)bd->textlen); 2172 ret.size += (size_t)bd->textlen; 2173 memset(ret.data + ret.size, ' ', (size_t)bd->endspaces); 2174 ret.size += (size_t)bd->endspaces; 2175 ret.data[ret.size] = NUL; 2176 2177 return ret; 2178 } 2179 2180 static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2, 2181 bool *const inclusive, MotionType *region_type, oparg_T *oap) 2182 FUNC_ATTR_NONNULL_ALL 2183 { 2184 tv_list_alloc_ret(rettv, kListLenMayKnow); 2185 2186 if (tv_check_for_list_arg(argvars, 0) == FAIL 2187 || tv_check_for_list_arg(argvars, 1) == FAIL 2188 || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { 2189 return FAIL; 2190 } 2191 2192 int fnum1 = -1; 2193 int fnum2 = -1; 2194 if (list2fpos(&argvars[0], p1, &fnum1, NULL, false) != OK 2195 || list2fpos(&argvars[1], p2, &fnum2, NULL, false) != OK 2196 || fnum1 != fnum2) { 2197 return FAIL; 2198 } 2199 2200 bool is_select_exclusive; 2201 char *type; 2202 char default_type[] = "v"; 2203 if (argvars[2].v_type == VAR_DICT) { 2204 is_select_exclusive = tv_dict_get_bool(argvars[2].vval.v_dict, "exclusive", 2205 *p_sel == 'e'); 2206 type = tv_dict_get_string(argvars[2].vval.v_dict, "type", false); 2207 if (type == NULL) { 2208 type = default_type; 2209 } 2210 } else { 2211 is_select_exclusive = *p_sel == 'e'; 2212 type = default_type; 2213 } 2214 2215 int block_width = 0; 2216 if (type[0] == 'v' && type[1] == NUL) { 2217 *region_type = kMTCharWise; 2218 } else if (type[0] == 'V' && type[1] == NUL) { 2219 *region_type = kMTLineWise; 2220 } else if (type[0] == Ctrl_V) { 2221 char *p = type + 1; 2222 if (*p != NUL && ((block_width = getdigits_int(&p, false, 0)) <= 0 || *p != NUL)) { 2223 semsg(_(e_invargNval), "type", type); 2224 return FAIL; 2225 } 2226 *region_type = kMTBlockWise; 2227 } else { 2228 semsg(_(e_invargNval), "type", type); 2229 return FAIL; 2230 } 2231 2232 buf_T *findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf; 2233 if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { 2234 emsg(_(e_buffer_is_not_loaded)); 2235 return FAIL; 2236 } 2237 2238 if (p1->lnum < 1 || p1->lnum > findbuf->b_ml.ml_line_count) { 2239 semsg(_(e_invalid_line_number_nr), p1->lnum); 2240 return FAIL; 2241 } 2242 if (p1->col == MAXCOL) { 2243 p1->col = ml_get_buf_len(findbuf, p1->lnum) + 1; 2244 } else if (p1->col < 1 || p1->col > ml_get_buf_len(findbuf, p1->lnum) + 1) { 2245 semsg(_(e_invalid_column_number_nr), p1->col); 2246 return FAIL; 2247 } 2248 2249 if (p2->lnum < 1 || p2->lnum > findbuf->b_ml.ml_line_count) { 2250 semsg(_(e_invalid_line_number_nr), p2->lnum); 2251 return FAIL; 2252 } 2253 if (p2->col == MAXCOL) { 2254 p2->col = ml_get_buf_len(findbuf, p2->lnum) + 1; 2255 } else if (p2->col < 1 || p2->col > ml_get_buf_len(findbuf, p2->lnum) + 1) { 2256 semsg(_(e_invalid_column_number_nr), p2->col); 2257 return FAIL; 2258 } 2259 2260 curbuf = findbuf; 2261 curwin->w_buffer = curbuf; 2262 virtual_op = virtual_active(curwin); 2263 2264 // NOTE: Adjustment is needed. 2265 p1->col--; 2266 p2->col--; 2267 2268 if (!lt(*p1, *p2)) { 2269 // swap position 2270 pos_T p = *p1; 2271 *p1 = *p2; 2272 *p2 = p; 2273 } 2274 2275 if (*region_type == kMTCharWise) { 2276 // Handle 'selection' == "exclusive". 2277 if (is_select_exclusive && !equalpos(*p1, *p2)) { 2278 // When backing up to previous line, inclusive becomes false. 2279 *inclusive = !unadjust_for_sel_inner(p2); 2280 } 2281 // If p2 is on NUL (end of line), inclusive becomes false. 2282 if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL) { 2283 *inclusive = false; 2284 } 2285 } else if (*region_type == kMTBlockWise) { 2286 colnr_T sc1, ec1, sc2, ec2; 2287 const bool lbr_saved = reset_lbr(); 2288 getvvcol(curwin, p1, &sc1, NULL, &ec1); 2289 getvvcol(curwin, p2, &sc2, NULL, &ec2); 2290 restore_lbr(lbr_saved); 2291 oap->motion_type = kMTBlockWise; 2292 oap->inclusive = true; 2293 oap->op_type = OP_NOP; 2294 oap->start = *p1; 2295 oap->end = *p2; 2296 oap->start_vcol = MIN(sc1, sc2); 2297 if (block_width > 0) { 2298 oap->end_vcol = oap->start_vcol + block_width - 1; 2299 } else if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) { 2300 oap->end_vcol = sc2 - 1; 2301 } else { 2302 oap->end_vcol = MAX(ec1, ec2); 2303 } 2304 } 2305 2306 // Include the trailing byte of a multi-byte char. 2307 int l = utfc_ptr2len(ml_get_pos(p2)); 2308 if (l > 1) { 2309 p2->col += l - 1; 2310 } 2311 2312 return OK; 2313 } 2314 2315 /// "getregion()" function 2316 static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2317 { 2318 buf_T *const save_curbuf = curbuf; 2319 const TriState save_virtual = virtual_op; 2320 2321 pos_T p1, p2; 2322 bool inclusive = true; 2323 MotionType region_type = kMTUnknown; 2324 oparg_T oa; 2325 2326 if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, ®ion_type, &oa) == FAIL) { 2327 return; 2328 } 2329 2330 for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { 2331 String akt = STRING_INIT; 2332 2333 if (region_type == kMTBlockWise) { 2334 struct block_def bd; 2335 block_prep(&oa, &bd, lnum, false); 2336 akt = block_def2str(&bd); 2337 } else if (region_type == kMTLineWise || (p1.lnum < lnum && lnum < p2.lnum)) { 2338 akt = cbuf_to_string(ml_get(lnum), (size_t)ml_get_len(lnum)); 2339 } else { 2340 struct block_def bd; 2341 charwise_block_prep(p1, p2, &bd, lnum, inclusive); 2342 akt = block_def2str(&bd); 2343 } 2344 2345 assert(akt.data != NULL); 2346 tv_list_append_allocated_string(rettv->vval.v_list, akt.data); 2347 } 2348 2349 // getregionpos() may change curbuf and virtual_op 2350 curbuf = save_curbuf; 2351 curwin->w_buffer = curbuf; 2352 virtual_op = save_virtual; 2353 } 2354 2355 static void add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2) 2356 { 2357 list_T *l1 = tv_list_alloc(2); 2358 tv_list_append_list(rettv->vval.v_list, l1); 2359 2360 list_T *l2 = tv_list_alloc(4); 2361 tv_list_append_list(l1, l2); 2362 2363 list_T *l3 = tv_list_alloc(4); 2364 tv_list_append_list(l1, l3); 2365 2366 tv_list_append_number(l2, curbuf->b_fnum); 2367 tv_list_append_number(l2, p1.lnum); 2368 tv_list_append_number(l2, p1.col); 2369 tv_list_append_number(l2, p1.coladd); 2370 2371 tv_list_append_number(l3, curbuf->b_fnum); 2372 tv_list_append_number(l3, p2.lnum); 2373 tv_list_append_number(l3, p2.col); 2374 tv_list_append_number(l3, p2.coladd); 2375 } 2376 2377 /// "getregionpos()" function 2378 static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2379 { 2380 buf_T *const save_curbuf = curbuf; 2381 const TriState save_virtual = virtual_op; 2382 2383 pos_T p1, p2; 2384 bool inclusive = true; 2385 MotionType region_type = kMTUnknown; 2386 bool allow_eol = false; 2387 oparg_T oa; 2388 2389 if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, ®ion_type, &oa) == FAIL) { 2390 return; 2391 } 2392 2393 if (argvars[2].v_type == VAR_DICT) { 2394 allow_eol = tv_dict_get_bool(argvars[2].vval.v_dict, "eol", false); 2395 } 2396 2397 for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { 2398 pos_T ret_p1, ret_p2; 2399 char *line = ml_get(lnum); 2400 colnr_T line_len = ml_get_len(lnum); 2401 2402 if (region_type == kMTLineWise) { 2403 ret_p1.col = 1; 2404 ret_p1.coladd = 0; 2405 ret_p2.col = MAXCOL; 2406 ret_p2.coladd = 0; 2407 } else { 2408 struct block_def bd; 2409 2410 if (region_type == kMTBlockWise) { 2411 block_prep(&oa, &bd, lnum, false); 2412 } else { 2413 charwise_block_prep(p1, p2, &bd, lnum, inclusive); 2414 } 2415 2416 if (bd.is_oneChar) { // selection entirely inside one char 2417 if (region_type == kMTBlockWise) { 2418 ret_p1.col = (colnr_T)(mb_prevptr(line, bd.textstart) - line) + 1; 2419 ret_p1.coladd = bd.start_char_vcols - (bd.start_vcol - oa.start_vcol); 2420 } else { 2421 ret_p1.col = p1.col + 1; 2422 ret_p1.coladd = p1.coladd; 2423 } 2424 } else if (region_type == kMTBlockWise && oa.start_vcol > bd.start_vcol) { 2425 // blockwise selection entirely beyond end of line 2426 ret_p1.col = MAXCOL; 2427 ret_p1.coladd = oa.start_vcol - bd.start_vcol; 2428 bd.is_oneChar = true; 2429 } else if (bd.startspaces > 0) { 2430 ret_p1.col = (colnr_T)(mb_prevptr(line, bd.textstart) - line) + 1; 2431 ret_p1.coladd = bd.start_char_vcols - bd.startspaces; 2432 } else { 2433 ret_p1.col = bd.textcol + 1; 2434 ret_p1.coladd = 0; 2435 } 2436 2437 if (bd.is_oneChar) { // selection entirely inside one char 2438 ret_p2.col = ret_p1.col; 2439 ret_p2.coladd = ret_p1.coladd + bd.startspaces + bd.endspaces; 2440 } else if (bd.endspaces > 0) { 2441 ret_p2.col = bd.textcol + bd.textlen + 1; 2442 ret_p2.coladd = bd.endspaces; 2443 } else { 2444 ret_p2.col = bd.textcol + bd.textlen; 2445 ret_p2.coladd = 0; 2446 } 2447 } 2448 2449 if (!allow_eol && ret_p1.col > line_len) { 2450 ret_p1.col = 0; 2451 ret_p1.coladd = 0; 2452 } else if (ret_p1.col > line_len + 1) { 2453 ret_p1.col = line_len + 1; 2454 } 2455 2456 if (!allow_eol && ret_p2.col > line_len) { 2457 ret_p2.col = ret_p1.col == 0 ? 0 : line_len; 2458 ret_p2.coladd = 0; 2459 } else if (ret_p2.col > line_len + 1) { 2460 ret_p2.col = line_len + 1; 2461 } 2462 2463 ret_p1.lnum = lnum; 2464 ret_p2.lnum = lnum; 2465 add_regionpos_range(rettv, ret_p1, ret_p2); 2466 } 2467 2468 // getregionpos() may change curbuf and virtual_op 2469 curbuf = save_curbuf; 2470 curwin->w_buffer = curbuf; 2471 virtual_op = save_virtual; 2472 } 2473 2474 /// Common between getreg(), getreginfo() and getregtype(): get the register 2475 /// name from the first argument. 2476 /// Returns zero on error. 2477 static int getreg_get_regname(typval_T *argvars) 2478 { 2479 const char *strregname; 2480 2481 if (argvars[0].v_type != VAR_UNKNOWN) { 2482 strregname = tv_get_string_chk(&argvars[0]); 2483 if (strregname == NULL) { // type error; errmsg already given 2484 return 0; 2485 } 2486 } else { 2487 // Default to v:register 2488 strregname = get_vim_var_str(VV_REG); 2489 } 2490 2491 return *strregname == 0 ? '"' : (uint8_t)(*strregname); 2492 } 2493 2494 /// "getreg()" function 2495 static void f_getreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2496 { 2497 int arg2 = false; 2498 bool return_list = false; 2499 2500 int regname = getreg_get_regname(argvars); 2501 if (regname == 0) { 2502 return; 2503 } 2504 2505 if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { 2506 bool error = false; 2507 arg2 = (int)tv_get_number_chk(&argvars[1], &error); 2508 if (!error && argvars[2].v_type != VAR_UNKNOWN) { 2509 return_list = (bool)tv_get_number_chk(&argvars[2], &error); 2510 } 2511 if (error) { 2512 return; 2513 } 2514 } 2515 2516 if (return_list) { 2517 rettv->v_type = VAR_LIST; 2518 rettv->vval.v_list = 2519 get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); 2520 if (rettv->vval.v_list == NULL) { 2521 rettv->vval.v_list = tv_list_alloc(0); 2522 } 2523 tv_list_ref(rettv->vval.v_list); 2524 } else { 2525 rettv->v_type = VAR_STRING; 2526 rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0); 2527 } 2528 } 2529 2530 /// "getregtype()" function 2531 static void f_getregtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2532 { 2533 // on error return an empty string 2534 rettv->v_type = VAR_STRING; 2535 rettv->vval.v_string = NULL; 2536 2537 int regname = getreg_get_regname(argvars); 2538 if (regname == 0) { 2539 return; 2540 } 2541 2542 colnr_T reglen = 0; 2543 char buf[NUMBUFLEN + 2]; 2544 MotionType reg_type = get_reg_type(regname, ®len); 2545 format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf)); 2546 2547 rettv->vval.v_string = xstrdup(buf); 2548 } 2549 2550 /// "gettagstack()" function 2551 static void f_gettagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2552 { 2553 win_T *wp = curwin; // default is current window 2554 2555 tv_dict_alloc_ret(rettv); 2556 2557 if (argvars[0].v_type != VAR_UNKNOWN) { 2558 wp = find_win_by_nr_or_id(&argvars[0]); 2559 if (wp == NULL) { 2560 return; 2561 } 2562 } 2563 2564 get_tagstack(wp, rettv->vval.v_dict); 2565 } 2566 2567 /// Dummy timer callback. Used by f_wait(). 2568 static void dummy_timer_due_cb(TimeWatcher *tw, void *data) 2569 { 2570 // If the main loop is closing, the condition won't be checked again. 2571 // Close the timer to avoid leaking resources. 2572 if (main_loop.closing) { 2573 time_watcher_stop(tw); 2574 time_watcher_close(tw, dummy_timer_close_cb); 2575 } 2576 } 2577 2578 /// Dummy timer close callback. Used by f_wait(). 2579 static void dummy_timer_close_cb(TimeWatcher *tw, void *data) 2580 { 2581 xfree(tw); 2582 } 2583 2584 /// "wait(timeout, condition[, interval])" function 2585 static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2586 { 2587 rettv->v_type = VAR_NUMBER; 2588 rettv->vval.v_number = -1; 2589 2590 if (argvars[0].v_type != VAR_NUMBER) { 2591 semsg(_(e_invargval), "1"); 2592 return; 2593 } 2594 if ((argvars[2].v_type != VAR_NUMBER && argvars[2].v_type != VAR_UNKNOWN) 2595 || (argvars[2].v_type == VAR_NUMBER && argvars[2].vval.v_number <= 0)) { 2596 semsg(_(e_invargval), "3"); 2597 return; 2598 } 2599 2600 int timeout = (int)argvars[0].vval.v_number; 2601 typval_T expr = argvars[1]; 2602 int interval = argvars[2].v_type == VAR_NUMBER 2603 ? (int)argvars[2].vval.v_number 2604 : 200; // Default. 2605 TimeWatcher *tw = xmalloc(sizeof(TimeWatcher)); 2606 2607 // Start dummy timer. 2608 time_watcher_init(&main_loop, tw, NULL); 2609 // Don't schedule the due callback, as that'll lead to two different types of events 2610 // on each interval, causing the condition to be checked twice. 2611 tw->events = NULL; 2612 time_watcher_start(tw, dummy_timer_due_cb, (uint64_t)interval, (uint64_t)interval); 2613 2614 typval_T argv = TV_INITIAL_VALUE; 2615 typval_T exprval = TV_INITIAL_VALUE; 2616 bool error = false; 2617 const int called_emsg_before = called_emsg; 2618 2619 // Flush screen updates before blocking. 2620 ui_flush(); 2621 2622 LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, timeout, 2623 eval_expr_typval(&expr, false, &argv, 0, &exprval) != OK 2624 || tv_get_number_chk(&exprval, &error) 2625 || called_emsg > called_emsg_before || error || got_int); 2626 2627 if (called_emsg > called_emsg_before || error) { 2628 rettv->vval.v_number = -3; 2629 } else if (got_int) { 2630 got_int = false; 2631 vgetc(); 2632 rettv->vval.v_number = -2; 2633 } else if (tv_get_number_chk(&exprval, &error)) { 2634 rettv->vval.v_number = 0; 2635 } 2636 2637 // Stop dummy timer 2638 time_watcher_stop(tw); 2639 time_watcher_close(tw, dummy_timer_close_cb); 2640 } 2641 2642 /// "gettext()" function 2643 static void f_gettext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2644 { 2645 if (tv_check_for_nonempty_string_arg(argvars, 0) == FAIL) { 2646 return; 2647 } 2648 2649 rettv->v_type = VAR_STRING; 2650 rettv->vval.v_string = xstrdup(_(argvars[0].vval.v_string)); 2651 } 2652 2653 /// "has()" function 2654 static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2655 { 2656 static const char *const has_list[] = { 2657 #if defined(BSD) && !defined(__APPLE__) && !defined(__GNU__) 2658 "bsd", 2659 #endif 2660 #ifdef __GNU__ 2661 "hurd", 2662 #endif 2663 #ifdef __linux__ 2664 "linux", 2665 #endif 2666 #ifdef SUN_SYSTEM 2667 "sun", 2668 #endif 2669 #ifdef UNIX 2670 "unix", 2671 #endif 2672 #ifdef MSWIN 2673 "win32", 2674 #endif 2675 #ifdef _WIN64 2676 "win64", 2677 #endif 2678 #ifndef CASE_INSENSITIVE_FILENAME 2679 "fname_case", 2680 #endif 2681 #ifdef HAVE_ACL 2682 "acl", 2683 #endif 2684 "autochdir", 2685 "arabic", 2686 "autocmd", 2687 "browsefilter", 2688 "byte_offset", 2689 "cindent", 2690 "cmdline_compl", 2691 "cmdline_hist", 2692 "cmdwin", 2693 "comments", 2694 "conceal", 2695 "cursorbind", 2696 "cursorshape", 2697 "dialog_con", 2698 "diff", 2699 "digraphs", 2700 "eval", // always present, of course! 2701 "ex_extra", 2702 "extra_search", 2703 "file_in_path", 2704 "filterpipe", 2705 "find_in_path", 2706 "float", 2707 "folding", 2708 #if defined(UNIX) 2709 "fork", 2710 #endif 2711 "gettext", 2712 "iconv", 2713 "insert_expand", 2714 "jumplist", 2715 "keymap", 2716 "lambda", 2717 "langmap", 2718 "libcall", 2719 "linebreak", 2720 "lispindent", 2721 "listcmds", 2722 "localmap", 2723 #ifdef __APPLE__ 2724 "mac", 2725 "macunix", 2726 "osx", 2727 "osxdarwin", 2728 #endif 2729 "menu", 2730 "mksession", 2731 "modify_fname", 2732 "mouse", 2733 "multi_byte", 2734 "multi_lang", 2735 "nanotime", 2736 "num64", 2737 "packages", 2738 "path_extra", 2739 "persistent_undo", 2740 "profile", 2741 "reltime", 2742 "quickfix", 2743 "rightleft", 2744 "scrollbind", 2745 "showcmd", 2746 "cmdline_info", 2747 "shada", 2748 "signs", 2749 "smartindent", 2750 "startuptime", 2751 "statusline", 2752 "spell", 2753 "syntax", 2754 #if !defined(UNIX) 2755 "system", 2756 #endif 2757 "tablineat", 2758 "tag_binary", 2759 "termguicolors", 2760 #ifdef HAVE_UNIBILIUM 2761 "terminfo", 2762 #endif 2763 "termresponse", 2764 "textobjects", 2765 "timers", 2766 "title", 2767 "user-commands", // was accidentally included in 5.4 2768 "user_commands", 2769 "vartabs", 2770 "vertsplit", 2771 "vimscript-1", 2772 "virtualedit", 2773 "visual", 2774 "visualextra", 2775 "vreplace", 2776 "wildignore", 2777 "wildmenu", 2778 "windows", 2779 "winaltkeys", 2780 "writebackup", 2781 #ifdef HAVE_XATTR 2782 "xattr", 2783 #endif 2784 "nvim", 2785 }; 2786 2787 bool x = false; 2788 bool n = false; 2789 const char *const name = tv_get_string(&argvars[0]); 2790 2791 // Fast-path: check features not in has_list[] first to avoid the full 2792 // linear scan for very common queries like has('patch-...'). 2793 if (STRNICMP(name, "patch", 5) == 0) { 2794 x = true; 2795 if (name[5] == '-' 2796 && strlen(name) >= 11 2797 && (name[6] >= '1' && name[6] <= '9')) { 2798 char *end; 2799 2800 // This works for patch-8.1.2, patch-9.0.3, patch-10.0.4, etc. 2801 // Not for patch-9.10.5. 2802 int major = (int)strtoul(name + 6, &end, 10); 2803 if (*end == '.' && ascii_isdigit(end[1]) 2804 && end[2] == '.' && ascii_isdigit(end[3])) { 2805 int minor = atoi(end + 1); 2806 2807 // Expect "patch-9.9.01234". 2808 n = has_vim_patch(atoi(end + 3), major * 100 + minor); 2809 } 2810 } else if (ascii_isdigit(name[5])) { 2811 n = has_vim_patch(atoi(name + 5), 0); 2812 } 2813 } else if (STRNICMP(name, "nvim-", 5) == 0) { 2814 x = true; 2815 // Expect "nvim-x.y.z" 2816 n = has_nvim_version(name + 5); 2817 } else if (STRICMP(name, "vim_starting") == 0) { 2818 x = true; 2819 n = (starting != 0); 2820 } else if (STRICMP(name, "ttyin") == 0) { 2821 x = true; 2822 n = stdin_isatty; 2823 } else if (STRICMP(name, "ttyout") == 0) { 2824 x = true; 2825 n = stdout_isatty; 2826 } else if (STRICMP(name, "multi_byte_encoding") == 0) { 2827 x = true; 2828 n = true; 2829 } else if (STRICMP(name, "gui_running") == 0) { 2830 x = true; 2831 n = ui_gui_attached(); 2832 } else if (STRICMP(name, "syntax_items") == 0) { 2833 x = true; 2834 n = syntax_present(curwin); 2835 } else if (STRICMP(name, "wsl") == 0) { 2836 x = true; 2837 n = has_wsl(); 2838 } 2839 2840 // Look up in has_list[] only if not already handled above. 2841 if (!x) { 2842 for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { 2843 if (STRICMP(name, has_list[i]) == 0) { 2844 x = true; 2845 n = true; 2846 break; 2847 } 2848 } 2849 } 2850 2851 if (!x) { 2852 // XXX: eval_has_provider() may shell out :( 2853 const int save_shell_error = (int)get_vim_var_nr(VV_SHELL_ERROR); 2854 2855 if (STRICMP(name, "clipboard_working") == 0) { 2856 n = eval_has_provider("clipboard", true); 2857 #ifdef UNIX 2858 } else if (STRICMP(name, "unnamedplus") == 0) { 2859 n = eval_has_provider("clipboard", true); 2860 #endif 2861 } else if (STRICMP(name, "pythonx") == 0) { 2862 n = eval_has_provider("python3", true); 2863 } else if (eval_has_provider(name, true)) { 2864 n = true; 2865 } 2866 2867 set_vim_var_nr(VV_SHELL_ERROR, save_shell_error); 2868 } 2869 2870 rettv->vval.v_number = n; 2871 } 2872 2873 static bool has_wsl(void) 2874 { 2875 static TriState has_wsl = kNone; 2876 if (has_wsl == kNone) { 2877 Error err = ERROR_INIT; 2878 Object o = NLUA_EXEC_STATIC("return vim.uv.os_uname()['release']:lower()" 2879 ":match('microsoft')", 2880 (Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err); 2881 assert(!ERROR_SET(&err)); 2882 has_wsl = LUARET_TRUTHY(o) ? kTrue : kFalse; 2883 } 2884 return has_wsl == kTrue; 2885 } 2886 2887 /// "highlightID(name)" function 2888 static void f_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2889 { 2890 rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0])); 2891 } 2892 2893 /// "highlight_exists()" function 2894 static void f_hlexists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2895 { 2896 rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0])); 2897 } 2898 2899 /// "hostname()" function 2900 static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2901 { 2902 char hostname[256]; 2903 2904 os_get_hostname(hostname, 256); 2905 rettv->v_type = VAR_STRING; 2906 rettv->vval.v_string = xstrdup(hostname); 2907 } 2908 2909 /// "index()" function 2910 static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 2911 { 2912 int idx = 0; 2913 bool ic = false; 2914 2915 rettv->vval.v_number = -1; 2916 if (argvars[0].v_type == VAR_BLOB) { 2917 bool error = false; 2918 int start = 0; 2919 2920 if (argvars[2].v_type != VAR_UNKNOWN) { 2921 start = (int)tv_get_number_chk(&argvars[2], &error); 2922 if (error) { 2923 return; 2924 } 2925 } 2926 blob_T *const b = argvars[0].vval.v_blob; 2927 if (b == NULL) { 2928 return; 2929 } 2930 if (start < 0) { 2931 start = tv_blob_len(b) + start; 2932 if (start < 0) { 2933 start = 0; 2934 } 2935 } 2936 for (idx = start; idx < tv_blob_len(b); idx++) { 2937 typval_T tv; 2938 tv.v_type = VAR_NUMBER; 2939 tv.vval.v_number = tv_blob_get(b, idx); 2940 if (tv_equal(&tv, &argvars[1], ic)) { 2941 rettv->vval.v_number = idx; 2942 return; 2943 } 2944 } 2945 return; 2946 } else if (argvars[0].v_type != VAR_LIST) { 2947 emsg(_(e_listblobreq)); 2948 return; 2949 } 2950 2951 list_T *const l = argvars[0].vval.v_list; 2952 if (l == NULL) { 2953 return; 2954 } 2955 2956 listitem_T *item = tv_list_first(l); 2957 if (argvars[2].v_type != VAR_UNKNOWN) { 2958 bool error = false; 2959 2960 // Start at specified item. 2961 idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error)); 2962 if (error || idx == -1) { 2963 item = NULL; 2964 } else { 2965 item = tv_list_find(l, idx); 2966 assert(item != NULL); 2967 } 2968 if (argvars[3].v_type != VAR_UNKNOWN) { 2969 ic = !!tv_get_number_chk(&argvars[3], &error); 2970 if (error) { 2971 item = NULL; 2972 } 2973 } 2974 } 2975 2976 for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { 2977 if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic)) { 2978 rettv->vval.v_number = idx; 2979 break; 2980 } 2981 } 2982 } 2983 2984 /// Evaluate "expr" with the v:key and v:val arguments and return the result. 2985 /// The expression is expected to return a boolean value. The caller should set 2986 /// the VV_KEY and VV_VAL vim variables before calling this function. 2987 static varnumber_T indexof_eval_expr(typval_T *expr) 2988 { 2989 typval_T argv[3]; 2990 argv[0] = *get_vim_var_tv(VV_KEY); 2991 argv[1] = *get_vim_var_tv(VV_VAL); 2992 typval_T newtv; 2993 newtv.v_type = VAR_UNKNOWN; 2994 2995 if (eval_expr_typval(expr, false, argv, 2, &newtv) == FAIL) { 2996 return false; 2997 } 2998 2999 bool error = false; 3000 varnumber_T found = tv_get_bool_chk(&newtv, &error); 3001 tv_clear(&newtv); 3002 3003 return error ? false : found; 3004 } 3005 3006 /// Evaluate "expr" for each byte in the Blob "b" starting with the byte at 3007 /// "startidx" and return the index of the byte where "expr" is TRUE. Returns 3008 /// -1 if "expr" doesn't evaluate to TRUE for any of the bytes. 3009 static varnumber_T indexof_blob(blob_T *b, varnumber_T startidx, typval_T *expr) 3010 { 3011 if (b == NULL) { 3012 return -1; 3013 } 3014 3015 if (startidx < 0) { 3016 // negative index: index from the last byte 3017 startidx = tv_blob_len(b) + startidx; 3018 if (startidx < 0) { 3019 startidx = 0; 3020 } 3021 } 3022 3023 set_vim_var_type(VV_KEY, VAR_NUMBER); 3024 set_vim_var_type(VV_VAL, VAR_NUMBER); 3025 3026 const int called_emsg_start = called_emsg; 3027 for (varnumber_T idx = startidx; idx < tv_blob_len(b); idx++) { 3028 set_vim_var_nr(VV_KEY, idx); 3029 set_vim_var_nr(VV_VAL, tv_blob_get(b, (int)idx)); 3030 3031 if (indexof_eval_expr(expr)) { 3032 return idx; 3033 } 3034 3035 if (called_emsg != called_emsg_start) { 3036 return -1; 3037 } 3038 } 3039 3040 return -1; 3041 } 3042 3043 /// Evaluate "expr" for each item in the List "l" starting with the item at 3044 /// "startidx" and return the index of the item where "expr" is TRUE. Returns 3045 /// -1 if "expr" doesn't evaluate to TRUE for any of the items. 3046 static varnumber_T indexof_list(list_T *l, varnumber_T startidx, typval_T *expr) 3047 { 3048 if (l == NULL) { 3049 return -1; 3050 } 3051 3052 listitem_T *item; 3053 varnumber_T idx = 0; 3054 if (startidx == 0) { 3055 item = tv_list_first(l); 3056 } else { 3057 // Start at specified item. 3058 idx = tv_list_uidx(l, (int)startidx); 3059 if (idx == -1) { 3060 item = NULL; 3061 } else { 3062 item = tv_list_find(l, (int)idx); 3063 assert(item != NULL); 3064 } 3065 } 3066 3067 set_vim_var_type(VV_KEY, VAR_NUMBER); 3068 3069 const int called_emsg_start = called_emsg; 3070 for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { 3071 set_vim_var_nr(VV_KEY, idx); 3072 tv_copy(TV_LIST_ITEM_TV(item), get_vim_var_tv(VV_VAL)); 3073 3074 bool found = indexof_eval_expr(expr); 3075 tv_clear(get_vim_var_tv(VV_VAL)); 3076 3077 if (found) { 3078 return idx; 3079 } 3080 3081 if (called_emsg != called_emsg_start) { 3082 return -1; 3083 } 3084 } 3085 3086 return -1; 3087 } 3088 3089 /// "indexof()" function 3090 static void f_indexof(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3091 { 3092 rettv->vval.v_number = -1; 3093 3094 if (tv_check_for_list_or_blob_arg(argvars, 0) == FAIL 3095 || tv_check_for_string_or_func_arg(argvars, 1) == FAIL 3096 || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { 3097 return; 3098 } 3099 3100 if ((argvars[1].v_type == VAR_STRING 3101 && (argvars[1].vval.v_string == NULL || *argvars[1].vval.v_string == NUL)) 3102 || (argvars[1].v_type == VAR_FUNC && argvars[1].vval.v_partial == NULL)) { 3103 return; 3104 } 3105 3106 varnumber_T startidx = 0; 3107 if (argvars[2].v_type == VAR_DICT) { 3108 startidx = tv_dict_get_number_def(argvars[2].vval.v_dict, "startidx", 0); 3109 } 3110 3111 typval_T save_val; 3112 typval_T save_key; 3113 prepare_vimvar(VV_VAL, &save_val); 3114 prepare_vimvar(VV_KEY, &save_key); 3115 3116 // We reset "did_emsg" to be able to detect whether an error occurred 3117 // during evaluation of the expression. 3118 const int save_did_emsg = did_emsg; 3119 did_emsg = false; 3120 3121 if (argvars[0].v_type == VAR_BLOB) { 3122 rettv->vval.v_number = indexof_blob(argvars[0].vval.v_blob, startidx, &argvars[1]); 3123 } else { 3124 rettv->vval.v_number = indexof_list(argvars[0].vval.v_list, startidx, &argvars[1]); 3125 } 3126 3127 restore_vimvar(VV_KEY, &save_key); 3128 restore_vimvar(VV_VAL, &save_val); 3129 did_emsg |= save_did_emsg; 3130 } 3131 3132 static bool inputsecret_flag = false; 3133 3134 /// "input()" function 3135 /// Also handles inputsecret() when inputsecret is set. 3136 static void f_input(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3137 { 3138 get_user_input(argvars, rettv, false, inputsecret_flag); 3139 } 3140 3141 /// "inputdialog()" function 3142 static void f_inputdialog(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3143 { 3144 get_user_input(argvars, rettv, true, inputsecret_flag); 3145 } 3146 3147 /// "inputlist()" function 3148 static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3149 { 3150 if (argvars[0].v_type != VAR_LIST) { 3151 semsg(_(e_listarg), "inputlist()"); 3152 return; 3153 } 3154 3155 msg_ext_set_kind("confirm"); 3156 msg_start(); 3157 msg_row = Rows - 1; // for when 'cmdheight' > 1 3158 lines_left = Rows; // avoid more prompt 3159 msg_scroll = true; 3160 msg_clr_eos(); 3161 3162 list_T *l = argvars[0].vval.v_list; 3163 TV_LIST_ITER_CONST(l, li, { 3164 msg_puts(tv_get_string(TV_LIST_ITEM_TV(li))); 3165 if (!ui_has(kUIMessages) || TV_LIST_ITEM_NEXT(l, li) != NULL) { 3166 msg_putchar('\n'); 3167 } 3168 }); 3169 3170 // Ask for choice. 3171 bool mouse_used = false; 3172 int selected = prompt_for_input(NULL, 0, false, &mouse_used); 3173 if (mouse_used) { 3174 selected = tv_list_len(l) - (cmdline_row - mouse_row); 3175 } 3176 3177 rettv->vval.v_number = selected; 3178 } 3179 3180 static garray_T ga_userinput = { 0, 0, sizeof(tasave_T), 4, NULL }; 3181 3182 /// "inputrestore()" function 3183 static void f_inputrestore(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3184 { 3185 if (!GA_EMPTY(&ga_userinput)) { 3186 ga_userinput.ga_len--; 3187 restore_typeahead((tasave_T *)(ga_userinput.ga_data) 3188 + ga_userinput.ga_len); 3189 // default return is zero == OK 3190 } else if (p_verbose > 1) { 3191 verb_msg(_("called inputrestore() more often than inputsave()")); 3192 rettv->vval.v_number = 1; // Failed 3193 } 3194 } 3195 3196 /// "inputsave()" function 3197 static void f_inputsave(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3198 { 3199 // Add an entry to the stack of typeahead storage. 3200 tasave_T *p = GA_APPEND_VIA_PTR(tasave_T, &ga_userinput); 3201 save_typeahead(p); 3202 } 3203 3204 /// "inputsecret()" function 3205 static void f_inputsecret(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3206 { 3207 cmdline_star++; 3208 inputsecret_flag = true; 3209 f_input(argvars, rettv, fptr); 3210 cmdline_star--; 3211 inputsecret_flag = false; 3212 } 3213 3214 /// "interrupt()" function 3215 static void f_interrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3216 { 3217 got_int = true; 3218 } 3219 3220 /// "invert(expr)" function 3221 static void f_invert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3222 { 3223 rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); 3224 } 3225 3226 /// "islocked()" function 3227 static void f_islocked(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3228 { 3229 lval_T lv; 3230 3231 rettv->vval.v_number = -1; 3232 const char *const end = get_lval((char *)tv_get_string(&argvars[0]), 3233 NULL, 3234 &lv, false, false, 3235 GLV_NO_AUTOLOAD|GLV_READ_ONLY, 3236 FNE_CHECK_START); 3237 if (end != NULL && lv.ll_name != NULL) { 3238 if (*end != NUL) { 3239 semsg(_(lv.ll_name_len == 0 ? e_invarg2 : e_trailing_arg), end); 3240 } else { 3241 if (lv.ll_tv == NULL) { 3242 dictitem_T *di = find_var(lv.ll_name, lv.ll_name_len, NULL, true); 3243 if (di != NULL) { 3244 // Consider a variable locked when: 3245 // 1. the variable itself is locked 3246 // 2. the value of the variable is locked. 3247 // 3. the List or Dict value is locked. 3248 rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK) 3249 || tv_islocked(&di->di_tv)); 3250 } 3251 } else if (lv.ll_range) { 3252 emsg(_("E786: Range not allowed")); 3253 } else if (lv.ll_newkey != NULL) { 3254 semsg(_(e_dictkey), lv.ll_newkey); 3255 } else if (lv.ll_list != NULL) { 3256 // List item. 3257 rettv->vval.v_number = tv_islocked(TV_LIST_ITEM_TV(lv.ll_li)); 3258 } else { 3259 // Dictionary item. 3260 rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv); 3261 } 3262 } 3263 } 3264 3265 clear_lval(&lv); 3266 } 3267 3268 /// "isinf()" function 3269 static void f_isinf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3270 { 3271 if (argvars[0].v_type == VAR_FLOAT 3272 && xisinf(argvars[0].vval.v_float)) { 3273 rettv->vval.v_number = argvars[0].vval.v_float > 0.0 ? 1 : -1; 3274 } 3275 } 3276 3277 /// "isnan()" function 3278 static void f_isnan(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3279 { 3280 rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT 3281 && xisnan(argvars[0].vval.v_float); 3282 } 3283 3284 /// "id()" function 3285 static void f_id(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3286 FUNC_ATTR_NONNULL_ALL 3287 { 3288 const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars); 3289 rettv->v_type = VAR_STRING; 3290 rettv->vval.v_string = xmalloc((size_t)len + 1); 3291 vim_vsnprintf_typval(rettv->vval.v_string, (size_t)len + 1, "%p", dummy_ap, argvars); 3292 } 3293 3294 /// "jobpid(id)" function 3295 static void f_jobpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3296 { 3297 rettv->v_type = VAR_NUMBER; 3298 rettv->vval.v_number = 0; 3299 3300 if (check_secure()) { 3301 return; 3302 } 3303 3304 if (argvars[0].v_type != VAR_NUMBER) { 3305 emsg(_(e_invarg)); 3306 return; 3307 } 3308 3309 Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); 3310 if (!data) { 3311 return; 3312 } 3313 3314 Proc *proc = &data->stream.proc; 3315 rettv->vval.v_number = proc->pid; 3316 } 3317 3318 /// "jobresize(job, width, height)" function 3319 static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3320 { 3321 rettv->v_type = VAR_NUMBER; 3322 rettv->vval.v_number = 0; 3323 3324 if (check_secure()) { 3325 return; 3326 } 3327 3328 if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_NUMBER 3329 || argvars[2].v_type != VAR_NUMBER) { 3330 // job id, width, height 3331 emsg(_(e_invarg)); 3332 return; 3333 } 3334 3335 Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); 3336 if (!data) { 3337 return; 3338 } 3339 3340 if (data->stream.proc.type != kProcTypePty) { 3341 emsg(_(e_channotpty)); 3342 return; 3343 } 3344 3345 pty_proc_resize(&data->stream.pty, (uint16_t)argvars[1].vval.v_number, 3346 (uint16_t)argvars[2].vval.v_number); 3347 rettv->vval.v_number = 1; 3348 } 3349 3350 static const char *pty_ignored_env_vars[] = { 3351 #ifndef MSWIN 3352 "COLUMNS", 3353 "LINES", 3354 "TERMCAP", 3355 "COLORFGBG", 3356 #endif 3357 "COLORTERM", 3358 // Nvim-owned env vars. #6764 3359 "VIM", 3360 "VIMRUNTIME", 3361 NULL 3362 }; 3363 3364 /// According to comments in src/win/process.c of libuv, Windows has a few 3365 /// "essential" environment variables. 3366 static const char *required_env_vars[] = { 3367 #ifdef MSWIN 3368 "HOMEDRIVE", 3369 "HOMEPATH", 3370 "LOGONSERVER", 3371 "PATH", 3372 "SYSTEMDRIVE", 3373 "SYSTEMROOT", 3374 "TEMP", 3375 "USERDOMAIN", 3376 "USERNAME", 3377 "USERPROFILE", 3378 "WINDIR", 3379 #endif 3380 NULL 3381 }; 3382 3383 dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty, 3384 const char * const pty_term_name) 3385 { 3386 dict_T *env = tv_dict_alloc(); 3387 3388 if (!clear_env) { 3389 typval_T temp_env = TV_INITIAL_VALUE; 3390 f_environ(NULL, &temp_env, (EvalFuncData){ .null = NULL }); 3391 tv_dict_extend(env, temp_env.vval.v_dict, "force"); 3392 tv_dict_free(temp_env.vval.v_dict); 3393 3394 if (pty) { 3395 // These env vars shouldn't propagate to the child process. #6764 3396 // Remove them here, then the user may decide to explicitly set them below. 3397 for (size_t i = 0; 3398 i < ARRAY_SIZE(pty_ignored_env_vars) && pty_ignored_env_vars[i]; 3399 i++) { 3400 dictitem_T *dv = tv_dict_find(env, pty_ignored_env_vars[i], -1); 3401 if (dv) { 3402 tv_dict_item_remove(env, dv); 3403 } 3404 } 3405 // Set COLORTERM to "truecolor" if termguicolors is set 3406 if (p_tgc) { 3407 tv_dict_add_str(env, S_LEN("COLORTERM"), "truecolor"); 3408 } 3409 } 3410 } 3411 3412 // For a pty, we need a sane $TERM set. We can't rely on nvim's environment, 3413 // because the child process is going to be communicating with nvim, not the 3414 // parent terminal. Set a sane default, but let the user override it in the 3415 // job's environment if they want. 3416 if (pty) { 3417 dictitem_T *dv = tv_dict_find(env, S_LEN("TERM")); 3418 if (dv) { 3419 tv_dict_item_remove(env, dv); 3420 } 3421 tv_dict_add_str(env, S_LEN("TERM"), pty_term_name); 3422 } 3423 3424 // Set $NVIM (in the child process) to v:servername. #3118 3425 char *nvim_addr = get_vim_var_str(VV_SEND_SERVER); 3426 if (nvim_addr[0] != NUL) { 3427 dictitem_T *dv = tv_dict_find(env, S_LEN("NVIM")); 3428 if (dv) { 3429 tv_dict_item_remove(env, dv); 3430 } 3431 tv_dict_add_str(env, S_LEN("NVIM"), nvim_addr); 3432 } 3433 3434 if (job_env) { 3435 #ifdef MSWIN 3436 TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, { 3437 // Always use upper-case keys for Windows so we detect duplicate keys 3438 char *const key = strcase_save(var->di_key, true); 3439 size_t len = strlen(key); 3440 dictitem_T *dv = tv_dict_find(env, key, len); 3441 if (dv) { 3442 tv_dict_item_remove(env, dv); 3443 } 3444 tv_dict_add_str(env, key, len, tv_get_string(&var->di_tv)); 3445 xfree(key); 3446 }); 3447 #else 3448 tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force"); 3449 #endif 3450 } 3451 3452 if (pty) { 3453 // Now that the custom environment is configured, we need to ensure certain 3454 // environment variables are present. 3455 for (size_t i = 0; 3456 i < ARRAY_SIZE(required_env_vars) && required_env_vars[i]; 3457 i++) { 3458 size_t len = strlen(required_env_vars[i]); 3459 dictitem_T *dv = tv_dict_find(env, required_env_vars[i], (ptrdiff_t)len); 3460 if (!dv) { 3461 char *env_var = os_getenv(required_env_vars[i]); 3462 if (env_var) { 3463 tv_dict_add_allocated_str(env, required_env_vars[i], len, env_var); 3464 } 3465 } 3466 } 3467 } 3468 3469 return env; 3470 } 3471 3472 /// "jobstart()" function 3473 void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3474 { 3475 rettv->v_type = VAR_NUMBER; 3476 rettv->vval.v_number = 0; 3477 3478 if (check_secure()) { 3479 return; 3480 } 3481 3482 const char *cmd; 3483 bool executable = true; 3484 char **argv = tv_to_argv(&argvars[0], &cmd, &executable); 3485 if (!argv) { 3486 rettv->vval.v_number = executable ? 0 : -1; 3487 return; // Did error message in tv_to_argv. 3488 } 3489 3490 if (argvars[1].v_type != VAR_DICT && argvars[1].v_type != VAR_UNKNOWN) { 3491 // Wrong argument types 3492 semsg(_(e_invarg2), "expected dictionary"); 3493 shell_free_argv(argv); 3494 return; 3495 } 3496 3497 dict_T *job_opts = NULL; 3498 bool detach = false; 3499 bool rpc = false; 3500 bool pty = false; 3501 bool term = false; 3502 bool clear_env = false; 3503 bool overlapped = false; 3504 ChannelStdinMode stdin_mode = kChannelStdinPipe; 3505 CallbackReader on_stdout = CALLBACK_READER_INIT; 3506 CallbackReader on_stderr = CALLBACK_READER_INIT; 3507 Callback on_exit = CALLBACK_NONE; 3508 char *cwd = NULL; 3509 dictitem_T *job_env = NULL; 3510 if (argvars[1].v_type == VAR_DICT) { 3511 job_opts = argvars[1].vval.v_dict; 3512 3513 detach = tv_dict_get_number(job_opts, "detach") != 0; 3514 rpc = tv_dict_get_number(job_opts, "rpc") != 0; 3515 term = tv_dict_get_number(job_opts, "term") != 0; 3516 pty = term || tv_dict_get_number(job_opts, "pty") != 0; 3517 clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; 3518 overlapped = tv_dict_get_number(job_opts, "overlapped") != 0; 3519 3520 char *s = tv_dict_get_string(job_opts, "stdin", false); 3521 if (s) { 3522 if (!strncmp(s, "null", NUMBUFLEN)) { 3523 stdin_mode = kChannelStdinNull; 3524 } else if (!strncmp(s, "pipe", NUMBUFLEN)) { 3525 // Nothing to do, default value 3526 } else { 3527 semsg(_(e_invargNval), "stdin", s); 3528 } 3529 } 3530 3531 dictitem_T *const job_term = tv_dict_find(job_opts, S_LEN("term")); 3532 if (job_term && VAR_BOOL != job_term->di_tv.v_type) { 3533 // Restrict "term" field to boolean, in case we want to allow buffer numbers in the future. 3534 semsg(_(e_invarg2), "'term' must be Boolean"); 3535 shell_free_argv(argv); 3536 return; 3537 } 3538 3539 if (pty && rpc) { 3540 semsg(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); 3541 shell_free_argv(argv); 3542 return; 3543 } 3544 3545 #ifdef MSWIN 3546 if (pty && overlapped) { 3547 semsg(_(e_invarg2), 3548 "job cannot have both 'pty' and 'overlapped' options set"); 3549 shell_free_argv(argv); 3550 return; 3551 } 3552 #endif 3553 3554 char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); 3555 if (new_cwd && *new_cwd != NUL) { 3556 cwd = new_cwd; 3557 // The new cwd must be a directory. 3558 if (!os_isdir(cwd)) { 3559 semsg(_(e_invarg2), "expected valid directory"); 3560 shell_free_argv(argv); 3561 return; 3562 } 3563 } 3564 3565 job_env = tv_dict_find(job_opts, S_LEN("env")); 3566 if (job_env && job_env->di_tv.v_type != VAR_DICT) { 3567 semsg(_(e_invarg2), "env"); 3568 shell_free_argv(argv); 3569 return; 3570 } 3571 3572 if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { 3573 shell_free_argv(argv); 3574 return; 3575 } 3576 } 3577 3578 uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); 3579 uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); 3580 char *term_name = NULL; 3581 3582 if (term) { 3583 if (text_locked()) { 3584 text_locked_msg(); 3585 shell_free_argv(argv); 3586 return; 3587 } 3588 if (curbuf->b_changed) { 3589 emsg(_("jobstart(...,{term=true}) requires unmodified buffer")); 3590 shell_free_argv(argv); 3591 return; 3592 } 3593 if (curbuf->terminal) { 3594 if (terminal_running(curbuf->terminal)) { 3595 semsg(_("Terminal already connected to buffer %d"), curbuf->handle); 3596 shell_free_argv(argv); 3597 return; 3598 } 3599 buf_close_terminal(curbuf); 3600 } 3601 assert(!rpc); 3602 term_name = "xterm-256color"; 3603 cwd = cwd ? cwd : "."; 3604 overlapped = false; 3605 detach = false; 3606 stdin_mode = kChannelStdinPipe; 3607 width = width ? width : (uint16_t)MAX(0, curwin->w_view_width - win_col_off(curwin)); 3608 height = height ? height : (uint16_t)curwin->w_view_height; 3609 } 3610 3611 if (pty) { 3612 // Deprecated TERM field is from before `env` option existed. 3613 term_name = term_name ? term_name : tv_dict_get_string(job_opts, "TERM", false); 3614 term_name = term_name ? term_name : "ansi"; 3615 } 3616 3617 dict_T *env = create_environment(job_env, clear_env, pty, term_name); 3618 Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit, pty, 3619 rpc, overlapped, detach, stdin_mode, cwd, 3620 width, height, env, &rettv->vval.v_number); 3621 if (!chan) { 3622 return; 3623 } else if (!term) { 3624 channel_create_event(chan, NULL); 3625 } else { 3626 if (rettv->vval.v_number <= 0) { 3627 return; 3628 } 3629 3630 const int pid = chan->stream.pty.proc.pid; 3631 buf_T *const buf = curbuf; 3632 3633 // Unset 'swapfile' to ensure no swapfile is created. 3634 buf->b_p_swf = false; 3635 // If the buffer isn't loaded, open a memfile here to avoid spurious autocommands 3636 // from open_buffer() when updating the terminal buffer later. 3637 if (buf->b_ml.ml_mfp == NULL && ml_open(buf) == FAIL) { 3638 // Internal error in ml_open(): stop the job. 3639 proc_stop(&chan->stream.proc); 3640 channel_decref(chan); 3641 return; 3642 } 3643 3644 channel_incref(chan); 3645 channel_terminal_alloc(buf, chan); 3646 3647 apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, buf); 3648 3649 if (chan->term == NULL || terminal_buf(chan->term) == 0) { 3650 goto term_done; // Terminal may be destroyed during autocommands. 3651 } 3652 3653 // "./…" => "/home/foo/…" 3654 vim_FullName(cwd, NameBuff, sizeof(NameBuff), false); 3655 // "/home/foo/…" => "~/…" 3656 size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true); 3657 // Trim slash. 3658 if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) { 3659 IObuff[len - 1] = NUL; 3660 } 3661 3662 if (len == 1 && IObuff[0] == '/') { 3663 // Avoid ambiguity in the URI when CWD is root directory. 3664 IObuff[1] = '.'; 3665 IObuff[2] = NUL; 3666 } 3667 3668 // Terminal URI: "term://$CWD//$PID:$CMD" 3669 snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s", IObuff, pid, cmd); 3670 3671 setfname(buf, NameBuff, NULL, true); 3672 apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, buf); 3673 3674 if (chan->term == NULL || terminal_buf(chan->term) == 0) { 3675 goto term_done; // Terminal may be destroyed during autocommands. 3676 } 3677 3678 Error err = ERROR_INIT; 3679 buf->b_locked++; 3680 // Set (deprecated) buffer-local vars (prefer 'channel' buffer-local option). 3681 dict_set_var(buf->b_vars, cstr_as_string("terminal_job_id"), 3682 INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err); 3683 api_clear_error(&err); 3684 dict_set_var(buf->b_vars, cstr_as_string("terminal_job_pid"), 3685 INTEGER_OBJ(pid), false, false, NULL, &err); 3686 api_clear_error(&err); 3687 buf->b_locked--; 3688 3689 if (chan->term == NULL || terminal_buf(chan->term) == 0) { 3690 goto term_done; // Terminal may be destroyed in dict watchers. 3691 } 3692 3693 terminal_open(&chan->term, buf); 3694 term_done: 3695 channel_create_event(chan, NULL); 3696 channel_decref(chan); 3697 } 3698 } 3699 3700 /// "jobstop()" function 3701 void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3702 { 3703 rettv->v_type = VAR_NUMBER; 3704 rettv->vval.v_number = 0; 3705 3706 if (check_secure()) { 3707 return; 3708 } 3709 3710 if (argvars[0].v_type != VAR_NUMBER) { 3711 // Only argument is the job id 3712 emsg(_(e_invarg)); 3713 return; 3714 } 3715 3716 Channel *data = find_job((uint64_t)argvars[0].vval.v_number, false); 3717 if (!data) { 3718 return; 3719 } 3720 3721 const char *error = NULL; 3722 if (data->is_rpc) { 3723 // Ignore return code, but show error later. 3724 channel_close(data->id, kChannelPartRpc, &error); 3725 } 3726 proc_stop(&data->stream.proc); 3727 rettv->vval.v_number = 1; 3728 if (error) { 3729 emsg(error); 3730 } 3731 } 3732 3733 /// "jobwait(ids[, timeout])" function 3734 static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3735 { 3736 rettv->v_type = VAR_NUMBER; 3737 rettv->vval.v_number = 0; 3738 3739 if (check_secure()) { 3740 return; 3741 } 3742 if (argvars[0].v_type != VAR_LIST || (argvars[1].v_type != VAR_NUMBER 3743 && argvars[1].v_type != VAR_UNKNOWN)) { 3744 emsg(_(e_invarg)); 3745 return; 3746 } 3747 3748 list_T *args = argvars[0].vval.v_list; 3749 Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs)); 3750 MultiQueue *waiting_jobs = multiqueue_new(loop_on_put, &main_loop); 3751 3752 // Validate, prepare jobs for waiting. 3753 int i = 0; 3754 TV_LIST_ITER_CONST(args, arg, { 3755 Channel *chan = NULL; 3756 if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER 3757 || !(chan = find_channel((uint64_t)TV_LIST_ITEM_TV(arg)->vval.v_number)) 3758 || chan->streamtype != kChannelStreamProc) { 3759 jobs[i] = NULL; // Invalid job. 3760 } else if (proc_is_stopped(&chan->stream.proc)) { 3761 // Job is stopped but not fully destroyed. 3762 // Ensure all callbacks on its event queue are executed. #15402 3763 proc_wait(&chan->stream.proc, -1, NULL); 3764 jobs[i] = NULL; // Invalid job. 3765 } else { 3766 jobs[i] = chan; 3767 channel_incref(chan); 3768 if (chan->stream.proc.status < 0) { 3769 // Flush any events in the job's queue before temporarily replacing it. 3770 multiqueue_process_events(chan->events); 3771 multiqueue_replace_parent(chan->events, waiting_jobs); 3772 } 3773 } 3774 i++; 3775 }); 3776 3777 int remaining = -1; 3778 uint64_t before = 0; 3779 if (argvars[1].v_type == VAR_NUMBER && argvars[1].vval.v_number >= 0) { 3780 remaining = (int)argvars[1].vval.v_number; 3781 before = os_hrtime(); 3782 } 3783 3784 // Only mark the UI as busy when jobwait() blocks 3785 const bool busy = remaining != 0; 3786 if (busy) { 3787 ui_busy_start(); 3788 ui_flush(); 3789 } 3790 3791 for (i = 0; i < tv_list_len(args); i++) { 3792 if (remaining == 0) { 3793 break; // Timeout. 3794 } 3795 if (jobs[i] == NULL) { 3796 continue; // Invalid job, will assign status=-3 below. 3797 } 3798 int status = proc_wait(&jobs[i]->stream.proc, remaining, 3799 waiting_jobs); 3800 if (status < 0) { 3801 break; // Interrupted (CTRL-C) or timeout, skip remaining jobs. 3802 } 3803 if (remaining > 0) { 3804 uint64_t now = os_hrtime(); 3805 remaining = MIN(0, remaining - (int)((now - before) / 1000000)); 3806 before = now; 3807 } 3808 } 3809 3810 list_T *const rv = tv_list_alloc(tv_list_len(args)); 3811 3812 // For each job: 3813 // * Restore its parent queue if the job is still alive. 3814 // * Append its status to the output list, or: 3815 // -3 for "invalid job id" 3816 // -2 for "interrupted" (user hit CTRL-C) 3817 // -1 for jobs that were skipped or timed out 3818 for (i = 0; i < tv_list_len(args); i++) { 3819 if (jobs[i] == NULL) { 3820 tv_list_append_number(rv, -3); 3821 continue; 3822 } 3823 multiqueue_process_events(jobs[i]->events); 3824 multiqueue_replace_parent(jobs[i]->events, main_loop.events); 3825 3826 tv_list_append_number(rv, jobs[i]->stream.proc.status); 3827 channel_decref(jobs[i]); 3828 } 3829 3830 multiqueue_free(waiting_jobs); 3831 xfree(jobs); 3832 if (busy) { 3833 ui_busy_stop(); 3834 } 3835 tv_list_ref(rv); 3836 rettv->v_type = VAR_LIST; 3837 rettv->vval.v_list = rv; 3838 } 3839 3840 /// json_decode() function 3841 static void f_json_decode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3842 { 3843 char numbuf[NUMBUFLEN]; 3844 const char *s = NULL; 3845 char *tofree = NULL; 3846 size_t len; 3847 if (argvars[0].v_type == VAR_LIST) { 3848 if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &tofree)) { 3849 emsg(_("E474: Failed to convert list to string")); 3850 return; 3851 } 3852 s = tofree; 3853 if (s == NULL) { 3854 assert(len == 0); 3855 s = ""; 3856 } 3857 } else { 3858 s = tv_get_string_buf_chk(&argvars[0], numbuf); 3859 if (s) { 3860 len = strlen(s); 3861 } else { 3862 return; 3863 } 3864 } 3865 if (json_decode_string(s, len, rettv) == FAIL) { 3866 semsg(_("E474: Failed to parse %.*s"), (int)len, s); 3867 rettv->v_type = VAR_NUMBER; 3868 rettv->vval.v_number = 0; 3869 } 3870 assert(rettv->v_type != VAR_UNKNOWN); 3871 xfree(tofree); 3872 } 3873 3874 /// json_encode() function 3875 static void f_json_encode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3876 { 3877 rettv->v_type = VAR_STRING; 3878 rettv->vval.v_string = encode_tv2json(&argvars[0], NULL); 3879 } 3880 3881 /// "keytrans()" function 3882 static void f_keytrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3883 { 3884 rettv->v_type = VAR_STRING; 3885 if (tv_check_for_string_arg(argvars, 0) == FAIL 3886 || argvars[0].vval.v_string == NULL) { 3887 return; 3888 } 3889 // Need to escape K_SPECIAL for mb_unescape(). 3890 char *escaped = vim_strsave_escape_ks(argvars[0].vval.v_string); 3891 rettv->vval.v_string = str2special_save(escaped, true, true); 3892 xfree(escaped); 3893 } 3894 3895 /// "len()" function 3896 static void f_len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3897 { 3898 switch (argvars[0].v_type) { 3899 case VAR_STRING: 3900 case VAR_NUMBER: 3901 rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); 3902 break; 3903 case VAR_BLOB: 3904 rettv->vval.v_number = tv_blob_len(argvars[0].vval.v_blob); 3905 break; 3906 case VAR_LIST: 3907 rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); 3908 break; 3909 case VAR_DICT: 3910 rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); 3911 break; 3912 case VAR_UNKNOWN: 3913 case VAR_BOOL: 3914 case VAR_SPECIAL: 3915 case VAR_FLOAT: 3916 case VAR_PARTIAL: 3917 case VAR_FUNC: 3918 emsg(_("E701: Invalid type for len()")); 3919 break; 3920 } 3921 } 3922 3923 static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) 3924 { 3925 rettv->v_type = (VarType)out_type; 3926 if (out_type != VAR_NUMBER) { 3927 rettv->vval.v_string = NULL; 3928 } 3929 3930 if (check_secure()) { 3931 return; 3932 } 3933 3934 // The first two args (libname and funcname) must be strings 3935 if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) { 3936 return; 3937 } 3938 3939 const char *libname = argvars[0].vval.v_string; 3940 const char *funcname = argvars[1].vval.v_string; 3941 3942 VarType in_type = argvars[2].v_type; 3943 3944 // input variables 3945 char *str_in = (in_type == VAR_STRING) ? argvars[2].vval.v_string : NULL; 3946 int int_in = (int)argvars[2].vval.v_number; 3947 3948 // output variables 3949 char **str_out = (out_type == VAR_STRING) ? &rettv->vval.v_string : NULL; 3950 int int_out = 0; 3951 3952 bool success = os_libcall(libname, funcname, 3953 str_in, int_in, 3954 str_out, &int_out); 3955 3956 if (!success) { 3957 semsg(_(e_libcall), funcname); 3958 return; 3959 } 3960 3961 if (out_type == VAR_NUMBER) { 3962 rettv->vval.v_number = (varnumber_T)int_out; 3963 } 3964 } 3965 3966 /// "libcall()" function 3967 static void f_libcall(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3968 { 3969 libcall_common(argvars, rettv, VAR_STRING); 3970 } 3971 3972 /// "libcallnr()" function 3973 static void f_libcallnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3974 { 3975 libcall_common(argvars, rettv, VAR_NUMBER); 3976 } 3977 3978 /// "line(string, [winid])" function 3979 static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3980 { 3981 linenr_T lnum = 0; 3982 pos_T *fp = NULL; 3983 int fnum; 3984 3985 if (argvars[1].v_type != VAR_UNKNOWN) { 3986 // use window specified in the second argument 3987 int id = (int)tv_get_number(&argvars[1]); 3988 tabpage_T *tp; 3989 win_T *wp = win_id2wp_tp(id, &tp); 3990 if (wp != NULL && tp != NULL) { 3991 // With 'splitkeep' != cursor and in diff mode, prevent that the 3992 // window scrolls and keep the topline. 3993 if (*p_spk != 'c' || (wp->w_p_diff && curwin->w_p_diff)) { 3994 skip_update_topline = true; 3995 } 3996 check_cursor(wp); 3997 fp = var2fpos(&argvars[0], true, &fnum, false, wp); 3998 skip_update_topline = false; 3999 } 4000 } else { 4001 // use current window 4002 fp = var2fpos(&argvars[0], true, &fnum, false, curwin); 4003 } 4004 4005 if (fp != NULL) { 4006 lnum = fp->lnum; 4007 } 4008 rettv->vval.v_number = lnum; 4009 } 4010 4011 /// "line2byte(lnum)" function 4012 static void f_line2byte(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4013 { 4014 const linenr_T lnum = tv_get_lnum(argvars); 4015 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { 4016 rettv->vval.v_number = -1; 4017 } else { 4018 rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL, false); 4019 } 4020 if (rettv->vval.v_number >= 0) { 4021 rettv->vval.v_number++; 4022 } 4023 } 4024 4025 /// "localtime()" function 4026 static void f_localtime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4027 { 4028 rettv->vval.v_number = (varnumber_T)time(NULL); 4029 } 4030 4031 /// luaeval() function implementation 4032 static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4033 FUNC_ATTR_NONNULL_ALL 4034 { 4035 const char *const str = tv_get_string_chk(&argvars[0]); 4036 if (str == NULL) { 4037 return; 4038 } 4039 4040 nlua_typval_eval(cstr_as_string(str), &argvars[1], rettv); 4041 } 4042 4043 static void find_some_match(typval_T *const argvars, typval_T *const rettv, 4044 const SomeMatchType type) 4045 { 4046 char *str = NULL; 4047 int64_t len = 0; 4048 char *expr = NULL; 4049 regmatch_T regmatch; 4050 int64_t start = 0; 4051 int64_t nth = 1; 4052 colnr_T startcol = 0; 4053 bool match = false; 4054 list_T *l = NULL; 4055 int idx = 0; 4056 char *tofree = NULL; 4057 4058 // Make 'cpoptions' empty, the 'l' flag should not be used here. 4059 char *save_cpo = p_cpo; 4060 p_cpo = empty_string_option; 4061 4062 rettv->vval.v_number = -1; 4063 switch (type) { 4064 // matchlist(): return empty list when there are no matches. 4065 case kSomeMatchList: 4066 tv_list_alloc_ret(rettv, kListLenMayKnow); 4067 break; 4068 // matchstrpos(): return ["", -1, -1, -1] 4069 case kSomeMatchStrPos: 4070 tv_list_alloc_ret(rettv, 4); 4071 tv_list_append_string(rettv->vval.v_list, "", 0); 4072 tv_list_append_number(rettv->vval.v_list, -1); 4073 tv_list_append_number(rettv->vval.v_list, -1); 4074 tv_list_append_number(rettv->vval.v_list, -1); 4075 break; 4076 case kSomeMatchStr: 4077 rettv->v_type = VAR_STRING; 4078 rettv->vval.v_string = NULL; 4079 break; 4080 case kSomeMatch: 4081 case kSomeMatchEnd: 4082 // Do nothing: zero is default. 4083 break; 4084 } 4085 4086 listitem_T *li = NULL; 4087 if (argvars[0].v_type == VAR_LIST) { 4088 if ((l = argvars[0].vval.v_list) == NULL) { 4089 goto theend; 4090 } 4091 li = tv_list_first(l); 4092 } else { 4093 expr = str = (char *)tv_get_string(&argvars[0]); 4094 len = (int64_t)strlen(str); 4095 } 4096 4097 char patbuf[NUMBUFLEN]; 4098 const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); 4099 if (pat == NULL) { 4100 goto theend; 4101 } 4102 4103 if (argvars[2].v_type != VAR_UNKNOWN) { 4104 bool error = false; 4105 4106 start = tv_get_number_chk(&argvars[2], &error); 4107 if (error) { 4108 goto theend; 4109 } 4110 if (l != NULL) { 4111 idx = tv_list_uidx(l, (int)start); 4112 if (idx == -1) { 4113 goto theend; 4114 } 4115 li = tv_list_find(l, idx); 4116 } else { 4117 if (start < 0) { 4118 start = 0; 4119 } 4120 if (start > len) { 4121 goto theend; 4122 } 4123 // When "count" argument is there ignore matches before "start", 4124 // otherwise skip part of the string. Differs when pattern is "^" 4125 // or "\<". 4126 if (argvars[3].v_type != VAR_UNKNOWN) { 4127 startcol = (colnr_T)start; 4128 } else { 4129 str += start; 4130 len -= start; 4131 } 4132 } 4133 4134 if (argvars[3].v_type != VAR_UNKNOWN) { 4135 nth = tv_get_number_chk(&argvars[3], &error); 4136 } 4137 if (error) { 4138 goto theend; 4139 } 4140 } 4141 4142 regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); 4143 if (regmatch.regprog != NULL) { 4144 regmatch.rm_ic = p_ic; 4145 4146 while (true) { 4147 if (l != NULL) { 4148 if (li == NULL) { 4149 match = false; 4150 break; 4151 } 4152 xfree(tofree); 4153 tofree = expr = str = encode_tv2echo(TV_LIST_ITEM_TV(li), NULL); 4154 if (str == NULL) { 4155 break; 4156 } 4157 } 4158 4159 match = vim_regexec_nl(®match, str, startcol); 4160 4161 if (match && --nth <= 0) { 4162 break; 4163 } 4164 if (l == NULL && !match) { 4165 break; 4166 } 4167 4168 // Advance to just after the match. 4169 if (l != NULL) { 4170 li = TV_LIST_ITEM_NEXT(l, li); 4171 idx++; 4172 } else { 4173 startcol = (colnr_T)(regmatch.startp[0] 4174 + utfc_ptr2len(regmatch.startp[0]) - str); 4175 if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) { 4176 match = false; 4177 break; 4178 } 4179 } 4180 } 4181 4182 if (match) { 4183 switch (type) { 4184 case kSomeMatchStrPos: { 4185 list_T *const ret_l = rettv->vval.v_list; 4186 listitem_T *li1 = tv_list_first(ret_l); 4187 listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); 4188 listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); 4189 listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); 4190 xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); 4191 4192 const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); 4193 TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz(regmatch.startp[0], rd); 4194 TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)(regmatch.startp[0] - expr); 4195 TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(regmatch.endp[0] - expr); 4196 if (l != NULL) { 4197 TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; 4198 } 4199 break; 4200 } 4201 case kSomeMatchList: 4202 // Return list with matched string and submatches. 4203 for (int i = 0; i < NSUBEXP; i++) { 4204 if (regmatch.endp[i] == NULL) { 4205 tv_list_append_string(rettv->vval.v_list, NULL, 0); 4206 } else { 4207 tv_list_append_string(rettv->vval.v_list, regmatch.startp[i], 4208 (regmatch.endp[i] - regmatch.startp[i])); 4209 } 4210 } 4211 break; 4212 case kSomeMatchStr: 4213 // Return matched string. 4214 if (l != NULL) { 4215 tv_copy(TV_LIST_ITEM_TV(li), rettv); 4216 } else { 4217 rettv->vval.v_string = xmemdupz(regmatch.startp[0], 4218 (size_t)(regmatch.endp[0] - 4219 regmatch.startp[0])); 4220 } 4221 break; 4222 case kSomeMatch: 4223 case kSomeMatchEnd: 4224 if (l != NULL) { 4225 rettv->vval.v_number = idx; 4226 } else { 4227 if (type == kSomeMatch) { 4228 rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); 4229 } else { 4230 rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str); 4231 } 4232 rettv->vval.v_number += (varnumber_T)(str - expr); 4233 } 4234 break; 4235 } 4236 } 4237 vim_regfree(regmatch.regprog); 4238 } 4239 4240 theend: 4241 if (type == kSomeMatchStrPos && l == NULL && rettv->vval.v_list != NULL) { 4242 // matchstrpos() without a list: drop the second item 4243 list_T *const ret_l = rettv->vval.v_list; 4244 tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l))); 4245 } 4246 4247 xfree(tofree); 4248 p_cpo = save_cpo; 4249 } 4250 4251 /// Return all the matches in string "str" for pattern "rmp". 4252 /// The matches are returned in the List "mlist". 4253 /// If "submatches" is true, then submatch information is also returned. 4254 /// "matchbuf" is true when called for matchbufline(). 4255 static void get_matches_in_str(const char *str, regmatch_T *rmp, list_T *mlist, int idx, 4256 bool submatches, bool matchbuf) 4257 { 4258 size_t len = strlen(str); 4259 int match = 0; 4260 colnr_T startidx = 0; 4261 4262 while (true) { 4263 match = vim_regexec_nl(rmp, str, startidx); 4264 if (!match) { 4265 break; 4266 } 4267 4268 dict_T *d = tv_dict_alloc(); 4269 tv_list_append_dict(mlist, d); 4270 4271 if (matchbuf) { 4272 tv_dict_add_nr(d, S_LEN("lnum"), idx); 4273 } else { 4274 tv_dict_add_nr(d, S_LEN("idx"), idx); 4275 } 4276 4277 tv_dict_add_nr(d, S_LEN("byteidx"), 4278 (colnr_T)(rmp->startp[0] - str)); 4279 4280 tv_dict_add_str_len(d, S_LEN("text"), rmp->startp[0], 4281 (int)(rmp->endp[0] - rmp->startp[0])); 4282 4283 if (submatches) { 4284 list_T *sml = tv_list_alloc(NSUBEXP - 1); 4285 4286 tv_dict_add_list(d, S_LEN("submatches"), sml); 4287 4288 // return a list with the submatches 4289 for (int i = 1; i < NSUBEXP; i++) { 4290 if (rmp->endp[i] == NULL) { 4291 tv_list_append_string(sml, "", 0); 4292 } else { 4293 tv_list_append_string(sml, rmp->startp[i], rmp->endp[i] - rmp->startp[i]); 4294 } 4295 } 4296 } 4297 startidx = (colnr_T)(rmp->endp[0] - str); 4298 if (startidx >= (colnr_T)len || str + startidx <= rmp->startp[0]) { 4299 break; 4300 } 4301 } 4302 } 4303 4304 /// "matchbufline()" function 4305 static void f_matchbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4306 { 4307 rettv->vval.v_number = -1; 4308 tv_list_alloc_ret(rettv, kListLenUnknown); 4309 list_T *retlist = rettv->vval.v_list; 4310 4311 if (tv_check_for_buffer_arg(argvars, 0) == FAIL 4312 || tv_check_for_string_arg(argvars, 1) == FAIL 4313 || tv_check_for_lnum_arg(argvars, 2) == FAIL 4314 || tv_check_for_lnum_arg(argvars, 3) == FAIL 4315 || tv_check_for_opt_dict_arg(argvars, 4) == FAIL) { 4316 return; 4317 } 4318 4319 const int prev_did_emsg = did_emsg; 4320 buf_T *buf = tv_get_buf(&argvars[0], false); 4321 if (buf == NULL) { 4322 if (did_emsg == prev_did_emsg) { 4323 semsg(_(e_invalid_buffer_name_str), tv_get_string(&argvars[0])); 4324 } 4325 return; 4326 } 4327 if (buf->b_ml.ml_mfp == NULL) { 4328 emsg(_(e_buffer_is_not_loaded)); 4329 return; 4330 } 4331 4332 char patbuf[NUMBUFLEN]; 4333 const char *pat = tv_get_string_buf(&argvars[1], patbuf); 4334 4335 const int did_emsg_before = did_emsg; 4336 linenr_T slnum = tv_get_lnum_buf(&argvars[2], buf); 4337 if (did_emsg > did_emsg_before) { 4338 return; 4339 } 4340 if (slnum < 1) { 4341 semsg(_(e_invargval), "lnum"); 4342 return; 4343 } 4344 4345 linenr_T elnum = tv_get_lnum_buf(&argvars[3], buf); 4346 if (did_emsg > did_emsg_before) { 4347 return; 4348 } 4349 if (elnum < 1 || elnum < slnum) { 4350 semsg(_(e_invargval), "end_lnum"); 4351 return; 4352 } 4353 4354 if (elnum > buf->b_ml.ml_line_count) { 4355 elnum = buf->b_ml.ml_line_count; 4356 } 4357 4358 bool submatches = false; 4359 if (argvars[4].v_type != VAR_UNKNOWN) { 4360 dict_T *d = argvars[4].vval.v_dict; 4361 if (d != NULL) { 4362 dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); 4363 if (di != NULL) { 4364 if (di->di_tv.v_type != VAR_BOOL) { 4365 semsg(_(e_invargval), "submatches"); 4366 return; 4367 } 4368 submatches = tv_get_bool(&di->di_tv); 4369 } 4370 } 4371 } 4372 4373 // Make 'cpoptions' empty, the 'l' flag should not be used here. 4374 char *const save_cpo = p_cpo; 4375 p_cpo = empty_string_option; 4376 4377 regmatch_T regmatch; 4378 regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); 4379 if (regmatch.regprog == NULL) { 4380 goto theend; 4381 } 4382 regmatch.rm_ic = p_ic; 4383 4384 while (slnum <= elnum) { 4385 const char *str = ml_get_buf(buf, slnum); 4386 get_matches_in_str(str, ®match, retlist, slnum, submatches, true); 4387 slnum++; 4388 } 4389 4390 vim_regfree(regmatch.regprog); 4391 4392 theend: 4393 p_cpo = save_cpo; 4394 } 4395 4396 /// "match()" function 4397 static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4398 { 4399 find_some_match(argvars, rettv, kSomeMatch); 4400 } 4401 4402 /// "matchend()" function 4403 static void f_matchend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4404 { 4405 find_some_match(argvars, rettv, kSomeMatchEnd); 4406 } 4407 4408 /// "matchlist()" function 4409 static void f_matchlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4410 { 4411 find_some_match(argvars, rettv, kSomeMatchList); 4412 } 4413 4414 /// "matchstr()" function 4415 static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4416 { 4417 find_some_match(argvars, rettv, kSomeMatchStr); 4418 } 4419 4420 /// "matchstrlist()" function 4421 static void f_matchstrlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4422 { 4423 rettv->vval.v_number = -1; 4424 tv_list_alloc_ret(rettv, kListLenUnknown); 4425 list_T *retlist = rettv->vval.v_list; 4426 4427 if (tv_check_for_list_arg(argvars, 0) == FAIL 4428 || tv_check_for_string_arg(argvars, 1) == FAIL 4429 || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { 4430 return; 4431 } 4432 4433 list_T *l = NULL; 4434 if ((l = argvars[0].vval.v_list) == NULL) { 4435 return; 4436 } 4437 4438 char patbuf[NUMBUFLEN]; 4439 const char *pat = tv_get_string_buf_chk(&argvars[1], patbuf); 4440 if (pat == NULL) { 4441 return; 4442 } 4443 4444 // Make 'cpoptions' empty, the 'l' flag should not be used here. 4445 char *const save_cpo = p_cpo; 4446 p_cpo = empty_string_option; 4447 4448 regmatch_T regmatch; 4449 regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); 4450 if (regmatch.regprog == NULL) { 4451 goto theend; 4452 } 4453 regmatch.rm_ic = p_ic; 4454 4455 bool submatches = false; 4456 if (argvars[2].v_type != VAR_UNKNOWN) { 4457 dict_T *d = argvars[2].vval.v_dict; 4458 if (d != NULL) { 4459 dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); 4460 if (di != NULL) { 4461 if (di->di_tv.v_type != VAR_BOOL) { 4462 semsg(_(e_invargval), "submatches"); 4463 goto cleanup; 4464 } 4465 submatches = tv_get_bool(&di->di_tv); 4466 } 4467 } 4468 } 4469 4470 int idx = 0; 4471 TV_LIST_ITER_CONST(l, li, { 4472 const typval_T *const li_tv = TV_LIST_ITEM_TV(li); 4473 if (li_tv->v_type == VAR_STRING && li_tv->vval.v_string != NULL) { 4474 const char *str = li_tv->vval.v_string; 4475 get_matches_in_str(str, ®match, retlist, idx, submatches, false); 4476 } 4477 idx++; 4478 }); 4479 4480 cleanup: 4481 vim_regfree(regmatch.regprog); 4482 4483 theend: 4484 p_cpo = save_cpo; 4485 } 4486 4487 /// "matchstrpos()" function 4488 static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4489 { 4490 find_some_match(argvars, rettv, kSomeMatchStrPos); 4491 } 4492 4493 /// Get maximal/minimal number value in a list or dictionary 4494 /// 4495 /// @param[in] tv List or dictionary to work with. If it contains something 4496 /// that is not an integer number (or cannot be coerced to 4497 /// it) error is given. 4498 /// @param[out] rettv Location where result will be saved. Only assigns 4499 /// vval.v_number, type is not touched. Returns zero for 4500 /// empty lists/dictionaries. 4501 /// @param[in] domax Determines whether maximal or minimal value is desired. 4502 static void max_min(const typval_T *const tv, typval_T *const rettv, const bool domax) 4503 FUNC_ATTR_NONNULL_ALL 4504 { 4505 bool error = false; 4506 4507 rettv->vval.v_number = 0; 4508 varnumber_T n = (domax ? VARNUMBER_MIN : VARNUMBER_MAX); 4509 if (tv->v_type == VAR_LIST) { 4510 if (tv_list_len(tv->vval.v_list) == 0) { 4511 return; 4512 } 4513 TV_LIST_ITER_CONST(tv->vval.v_list, li, { 4514 const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); 4515 if (error) { 4516 return; // type error; errmsg already given 4517 } 4518 if (domax ? i > n : i < n) { 4519 n = i; 4520 } 4521 }); 4522 } else if (tv->v_type == VAR_DICT) { 4523 if (tv_dict_len(tv->vval.v_dict) == 0) { 4524 return; 4525 } 4526 TV_DICT_ITER(tv->vval.v_dict, di, { 4527 const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); 4528 if (error) { 4529 return; // type error; errmsg already given 4530 } 4531 if (domax ? i > n : i < n) { 4532 n = i; 4533 } 4534 }); 4535 } else { 4536 semsg(_(e_listdictarg), domax ? "max()" : "min()"); 4537 return; 4538 } 4539 4540 rettv->vval.v_number = n; 4541 } 4542 4543 /// "max()" function 4544 static void f_max(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4545 { 4546 max_min(argvars, rettv, true); 4547 } 4548 4549 /// "min()" function 4550 static void f_min(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4551 { 4552 max_min(argvars, rettv, false); 4553 } 4554 4555 /// "mode()" function 4556 static void f_mode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4557 { 4558 char buf[MODE_MAX_LENGTH]; 4559 4560 get_mode(buf); 4561 4562 // Clear out the minor mode when the argument is not a non-zero number or 4563 // non-empty string. 4564 if (!non_zero_arg(&argvars[0])) { 4565 buf[1] = NUL; 4566 } 4567 4568 rettv->vval.v_string = xstrdup(buf); 4569 rettv->v_type = VAR_STRING; 4570 } 4571 4572 static void may_add_state_char(garray_T *gap, const char *include, uint8_t c) 4573 { 4574 if (include == NULL || vim_strchr(include, c) != NULL) { 4575 ga_append(gap, c); 4576 } 4577 } 4578 4579 /// "state()" function 4580 static void f_state(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4581 { 4582 garray_T ga; 4583 ga_init(&ga, 1, 20); 4584 const char *include = NULL; 4585 4586 if (argvars[0].v_type != VAR_UNKNOWN) { 4587 include = tv_get_string(&argvars[0]); 4588 } 4589 4590 if (!(stuff_empty() && typebuf.tb_len == 0 && !using_script())) { 4591 may_add_state_char(&ga, include, 'm'); 4592 } 4593 if (op_pending()) { 4594 may_add_state_char(&ga, include, 'o'); 4595 } 4596 if (autocmd_busy) { 4597 may_add_state_char(&ga, include, 'x'); 4598 } 4599 if (ins_compl_active()) { 4600 may_add_state_char(&ga, include, 'a'); 4601 } 4602 if (!get_was_safe_state()) { 4603 may_add_state_char(&ga, include, 'S'); 4604 } 4605 for (int i = 0; i < get_callback_depth() && i < 3; i++) { 4606 may_add_state_char(&ga, include, 'c'); 4607 } 4608 if (msg_scrolled > 0) { 4609 may_add_state_char(&ga, include, 's'); 4610 } 4611 4612 rettv->v_type = VAR_STRING; 4613 rettv->vval.v_string = ga.ga_data; 4614 } 4615 4616 /// "msgpackdump()" function 4617 static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4618 FUNC_ATTR_NONNULL_ALL 4619 { 4620 if (argvars[0].v_type != VAR_LIST) { 4621 semsg(_(e_listarg), "msgpackdump()"); 4622 return; 4623 } 4624 list_T *const list = argvars[0].vval.v_list; 4625 PackerBuffer packer = packer_string_buffer(); 4626 const char *const msg = _("msgpackdump() argument, index %i"); 4627 // Assume that translation will not take more then 4 times more space 4628 char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; 4629 int idx = 0; 4630 TV_LIST_ITER(list, li, { 4631 vim_snprintf(msgbuf, sizeof(msgbuf), msg, idx); 4632 idx++; 4633 if (encode_vim_to_msgpack(&packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { 4634 break; 4635 } 4636 }); 4637 String data = packer_take_string(&packer); 4638 if (argvars[1].v_type != VAR_UNKNOWN && strequal(tv_get_string(&argvars[1]), "B")) { 4639 blob_T *b = tv_blob_alloc_ret(rettv); 4640 b->bv_ga.ga_data = data.data; 4641 b->bv_ga.ga_len = (int)data.size; 4642 b->bv_ga.ga_maxlen = (int)(packer.endptr - packer.startptr); 4643 } else { 4644 encode_list_write(tv_list_alloc_ret(rettv, kListLenMayKnow), data.data, data.size); 4645 api_free_string(data); 4646 } 4647 } 4648 4649 static void emsg_mpack_error(int status) 4650 { 4651 switch (status) { 4652 case MPACK_ERROR: 4653 semsg(_(e_invarg2), "Failed to parse msgpack string"); 4654 break; 4655 4656 case MPACK_EOF: 4657 semsg(_(e_invarg2), "Incomplete msgpack string"); 4658 break; 4659 4660 case MPACK_NOMEM: 4661 semsg(_(e_invarg2), "object was too deep to unpack"); 4662 break; 4663 4664 default: 4665 break; 4666 } 4667 } 4668 4669 static void msgpackparse_unpack_list(const list_T *const list, list_T *const ret_list) 4670 FUNC_ATTR_NONNULL_ARG(2) 4671 { 4672 if (tv_list_len(list) == 0) { 4673 return; 4674 } 4675 if (TV_LIST_ITEM_TV(tv_list_first(list))->v_type != VAR_STRING) { 4676 semsg(_(e_invarg2), "List item is not a string"); 4677 return; 4678 } 4679 ListReaderState lrstate = encode_init_lrstate(list); 4680 char *buf = alloc_block(); 4681 size_t buf_size = 0; 4682 4683 typval_T cur_item = { .v_type = VAR_UNKNOWN }; 4684 mpack_parser_t parser; 4685 mpack_parser_init(&parser, 0); 4686 parser.data.p = &cur_item; 4687 4688 int status = MPACK_OK; 4689 while (true) { 4690 size_t read_bytes; 4691 const int rlret = encode_read_from_list(&lrstate, buf + buf_size, ARENA_BLOCK_SIZE - buf_size, 4692 &read_bytes); 4693 if (rlret == FAIL) { 4694 semsg(_(e_invarg2), "List item is not a string"); 4695 goto end; 4696 } 4697 buf_size += read_bytes; 4698 4699 const char *ptr = buf; 4700 while (buf_size) { 4701 status = mpack_parse_typval(&parser, &ptr, &buf_size); 4702 if (status == MPACK_OK) { 4703 tv_list_append_owned_tv(ret_list, cur_item); 4704 cur_item.v_type = VAR_UNKNOWN; 4705 } else { 4706 break; 4707 } 4708 } 4709 4710 if (rlret == OK) { 4711 break; 4712 } 4713 4714 if (status == MPACK_EOF) { 4715 // move remaining data to front of buffer 4716 if (buf_size && ptr > buf) { 4717 memmove(buf, ptr, buf_size); 4718 } 4719 } else if (status != MPACK_OK) { 4720 break; 4721 } 4722 } 4723 4724 if (status != MPACK_OK) { 4725 typval_parser_error_free(&parser); 4726 emsg_mpack_error(status); 4727 } 4728 4729 end: 4730 free_block(buf); 4731 } 4732 4733 static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret_list) 4734 FUNC_ATTR_NONNULL_ARG(2) 4735 { 4736 const int len = tv_blob_len(blob); 4737 if (len == 0) { 4738 return; 4739 } 4740 4741 const char *data = blob->bv_ga.ga_data; 4742 size_t remaining = (size_t)len; 4743 while (remaining) { 4744 typval_T tv; 4745 int status = unpack_typval(&data, &remaining, &tv); 4746 if (status != MPACK_OK) { 4747 emsg_mpack_error(status); 4748 return; 4749 } 4750 4751 tv_list_append_owned_tv(ret_list, tv); 4752 } 4753 } 4754 4755 /// "msgpackparse" function 4756 static void f_msgpackparse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4757 FUNC_ATTR_NONNULL_ALL 4758 { 4759 if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { 4760 semsg(_(e_listblobarg), "msgpackparse()"); 4761 return; 4762 } 4763 list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); 4764 if (argvars[0].v_type == VAR_LIST) { 4765 msgpackparse_unpack_list(argvars[0].vval.v_list, ret_list); 4766 } else { 4767 msgpackparse_unpack_blob(argvars[0].vval.v_blob, ret_list); 4768 } 4769 } 4770 4771 /// "nextnonblank()" function 4772 static void f_nextnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4773 { 4774 linenr_T lnum; 4775 4776 for (lnum = tv_get_lnum(argvars);; lnum++) { 4777 if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) { 4778 lnum = 0; 4779 break; 4780 } 4781 if (*skipwhite(ml_get(lnum)) != NUL) { 4782 break; 4783 } 4784 } 4785 rettv->vval.v_number = lnum; 4786 } 4787 4788 /// "nr2char()" function 4789 static void f_nr2char(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4790 { 4791 if (argvars[1].v_type != VAR_UNKNOWN) { 4792 if (!tv_check_num(&argvars[1])) { 4793 return; 4794 } 4795 } 4796 4797 bool error = false; 4798 const varnumber_T num = tv_get_number_chk(&argvars[0], &error); 4799 if (error) { 4800 return; 4801 } 4802 if (num < 0) { 4803 emsg(_("E5070: Character number must not be less than zero")); 4804 return; 4805 } 4806 if (num > INT_MAX) { 4807 semsg(_("E5071: Character number must not be greater than INT_MAX (%i)"), 4808 INT_MAX); 4809 return; 4810 } 4811 4812 char buf[MB_MAXCHAR]; 4813 const int len = utf_char2bytes((int)num, buf); 4814 4815 rettv->v_type = VAR_STRING; 4816 rettv->vval.v_string = xmemdupz(buf, (size_t)len); 4817 } 4818 4819 /// "or(expr, expr)" function 4820 static void f_or(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4821 { 4822 rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) 4823 | tv_get_number_chk(&argvars[1], NULL); 4824 } 4825 4826 /// "pow()" function 4827 static void f_pow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4828 { 4829 float_T fx; 4830 float_T fy; 4831 4832 rettv->v_type = VAR_FLOAT; 4833 if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { 4834 rettv->vval.v_float = pow(fx, fy); 4835 } else { 4836 rettv->vval.v_float = 0.0; 4837 } 4838 } 4839 4840 /// "prevnonblank()" function 4841 static void f_prevnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4842 { 4843 linenr_T lnum = tv_get_lnum(argvars); 4844 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { 4845 lnum = 0; 4846 } else { 4847 while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) { 4848 lnum--; 4849 } 4850 } 4851 rettv->vval.v_number = lnum; 4852 } 4853 4854 /// "printf()" function 4855 static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4856 { 4857 rettv->v_type = VAR_STRING; 4858 rettv->vval.v_string = NULL; 4859 { 4860 int saved_did_emsg = did_emsg; 4861 4862 // Get the required length, allocate the buffer and do it for real. 4863 did_emsg = false; 4864 char buf[NUMBUFLEN]; 4865 const char *fmt = tv_get_string_buf(&argvars[0], buf); 4866 int len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1); 4867 if (!did_emsg) { 4868 char *s = xmalloc((size_t)len + 1); 4869 rettv->vval.v_string = s; 4870 vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); 4871 } 4872 did_emsg |= saved_did_emsg; 4873 } 4874 } 4875 4876 /// "prompt_getprompt({buffer})" function 4877 static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4878 FUNC_ATTR_NONNULL_ALL 4879 { 4880 // return an empty string by default, e.g. it's not a prompt buffer 4881 rettv->v_type = VAR_STRING; 4882 rettv->vval.v_string = NULL; 4883 4884 buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); 4885 if (buf == NULL) { 4886 return; 4887 } 4888 4889 if (!bt_prompt(buf)) { 4890 return; 4891 } 4892 4893 rettv->vval.v_string = xstrdup(buf_prompt_text(buf)); 4894 } 4895 4896 /// "prompt_getinput({buffer})" function 4897 static void f_prompt_getinput(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4898 FUNC_ATTR_NONNULL_ALL 4899 { 4900 // return an empty string by default, e.g. it's not a prompt buffer 4901 rettv->v_type = VAR_STRING; 4902 rettv->vval.v_string = NULL; 4903 4904 buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); 4905 if (buf == NULL) { 4906 return; 4907 } 4908 4909 if (!bt_prompt(buf)) { 4910 return; 4911 } 4912 4913 rettv->vval.v_string = prompt_get_input(buf); 4914 } 4915 4916 /// "pum_getpos()" function 4917 static void f_pum_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4918 { 4919 tv_dict_alloc_ret(rettv); 4920 pum_set_event_info(rettv->vval.v_dict); 4921 } 4922 4923 /// "pumvisible()" function 4924 static void f_pumvisible(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4925 { 4926 if (pum_visible()) { 4927 rettv->vval.v_number = 1; 4928 } 4929 } 4930 4931 /// "py3eval()" and "pyxeval()" functions (always python3) 4932 static void f_py3eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4933 { 4934 script_host_eval("python3", argvars, rettv); 4935 } 4936 4937 static void init_srand(uint32_t *const x) 4938 FUNC_ATTR_NONNULL_ALL 4939 { 4940 union { 4941 uint32_t number; 4942 uint8_t bytes[sizeof(uint32_t)]; 4943 } buf; 4944 4945 if (uv_random(NULL, NULL, buf.bytes, sizeof(buf.bytes), 0, NULL) == 0) { 4946 *x = buf.number; 4947 return; 4948 } 4949 4950 // The system's random number generator doesn't work, 4951 // fall back to os_hrtime() XOR with process ID 4952 *x = (uint32_t)os_hrtime(); 4953 *x ^= (uint32_t)os_get_pid(); 4954 } 4955 4956 static inline uint32_t splitmix32(uint32_t *const x) 4957 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE 4958 { 4959 uint32_t z = (*x += 0x9e3779b9); 4960 z = (z ^ (z >> 16)) * 0x85ebca6b; 4961 z = (z ^ (z >> 13)) * 0xc2b2ae35; 4962 return z ^ (z >> 16); 4963 } 4964 4965 static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *const y, 4966 uint32_t *const z, uint32_t *const w) 4967 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE 4968 { 4969 #define ROTL(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) 4970 const uint32_t result = ROTL(*y * 5, 7) * 9; 4971 const uint32_t t = *y << 9; 4972 *z ^= *x; 4973 *w ^= *y; 4974 *y ^= *z; 4975 *x ^= *w; 4976 *z ^= t; 4977 *w = ROTL(*w, 11); 4978 #undef ROTL 4979 return result; 4980 } 4981 4982 /// "rand()" function 4983 static void f_rand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4984 { 4985 uint32_t result; 4986 4987 if (argvars[0].v_type == VAR_UNKNOWN) { 4988 static uint32_t gx, gy, gz, gw; 4989 static bool initialized = false; 4990 4991 // When no argument is given use the global seed list. 4992 if (!initialized) { 4993 // Initialize the global seed list. 4994 uint32_t x = 0; 4995 init_srand(&x); 4996 4997 gx = splitmix32(&x); 4998 gy = splitmix32(&x); 4999 gz = splitmix32(&x); 5000 gw = splitmix32(&x); 5001 initialized = true; 5002 } 5003 5004 result = shuffle_xoshiro128starstar(&gx, &gy, &gz, &gw); 5005 } else if (argvars[0].v_type == VAR_LIST) { 5006 list_T *const l = argvars[0].vval.v_list; 5007 if (tv_list_len(l) != 4) { 5008 goto theend; 5009 } 5010 5011 typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0)); 5012 typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1)); 5013 typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2)); 5014 typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3)); 5015 if (tvx->v_type != VAR_NUMBER) { 5016 goto theend; 5017 } 5018 if (tvy->v_type != VAR_NUMBER) { 5019 goto theend; 5020 } 5021 if (tvz->v_type != VAR_NUMBER) { 5022 goto theend; 5023 } 5024 if (tvw->v_type != VAR_NUMBER) { 5025 goto theend; 5026 } 5027 uint32_t x = (uint32_t)tvx->vval.v_number; 5028 uint32_t y = (uint32_t)tvy->vval.v_number; 5029 uint32_t z = (uint32_t)tvz->vval.v_number; 5030 uint32_t w = (uint32_t)tvw->vval.v_number; 5031 5032 result = shuffle_xoshiro128starstar(&x, &y, &z, &w); 5033 5034 tvx->vval.v_number = (varnumber_T)x; 5035 tvy->vval.v_number = (varnumber_T)y; 5036 tvz->vval.v_number = (varnumber_T)z; 5037 tvw->vval.v_number = (varnumber_T)w; 5038 } else { 5039 goto theend; 5040 } 5041 5042 rettv->v_type = VAR_NUMBER; 5043 rettv->vval.v_number = (varnumber_T)result; 5044 return; 5045 5046 theend: 5047 semsg(_(e_invarg2), tv_get_string(&argvars[0])); 5048 rettv->v_type = VAR_NUMBER; 5049 rettv->vval.v_number = -1; 5050 } 5051 5052 /// "srand()" function 5053 static void f_srand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5054 { 5055 uint32_t x = 0; 5056 5057 tv_list_alloc_ret(rettv, 4); 5058 if (argvars[0].v_type == VAR_UNKNOWN) { 5059 init_srand(&x); 5060 } else { 5061 bool error = false; 5062 x = (uint32_t)tv_get_number_chk(&argvars[0], &error); 5063 if (error) { 5064 return; 5065 } 5066 } 5067 5068 tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); 5069 tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); 5070 tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); 5071 tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); 5072 } 5073 5074 /// "perleval()" function 5075 static void f_perleval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5076 { 5077 script_host_eval("perl", argvars, rettv); 5078 } 5079 5080 /// "rubyeval()" function 5081 static void f_rubyeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5082 { 5083 script_host_eval("ruby", argvars, rettv); 5084 } 5085 5086 /// "range()" function 5087 static void f_range(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5088 { 5089 varnumber_T end; 5090 varnumber_T stride = 1; 5091 bool error = false; 5092 5093 varnumber_T start = tv_get_number_chk(&argvars[0], &error); 5094 if (argvars[1].v_type == VAR_UNKNOWN) { 5095 end = start - 1; 5096 start = 0; 5097 } else { 5098 end = tv_get_number_chk(&argvars[1], &error); 5099 if (argvars[2].v_type != VAR_UNKNOWN) { 5100 stride = tv_get_number_chk(&argvars[2], &error); 5101 } 5102 } 5103 5104 if (error) { 5105 return; // Type error; errmsg already given. 5106 } 5107 if (stride == 0) { 5108 emsg(_("E726: Stride is zero")); 5109 return; 5110 } 5111 if (stride > 0 ? end + 1 < start : end - 1 > start) { 5112 emsg(_("E727: Start past end")); 5113 return; 5114 } 5115 5116 tv_list_alloc_ret(rettv, (end - start) / stride); 5117 for (varnumber_T i = start; stride > 0 ? i <= end : i >= end; i += stride) { 5118 tv_list_append_number(rettv->vval.v_list, i); 5119 } 5120 } 5121 5122 /// "getreginfo()" function 5123 static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5124 { 5125 int regname = getreg_get_regname(argvars); 5126 if (regname == 0) { 5127 return; 5128 } 5129 5130 if (regname == '@') { 5131 regname = '"'; 5132 } 5133 5134 tv_dict_alloc_ret(rettv); 5135 dict_T *const dict = rettv->vval.v_dict; 5136 5137 list_T *const list = get_reg_contents(regname, kGRegExprSrc | kGRegList); 5138 if (list == NULL) { 5139 return; 5140 } 5141 tv_dict_add_list(dict, S_LEN("regcontents"), list); 5142 5143 char buf[NUMBUFLEN + 2]; 5144 buf[0] = NUL; 5145 buf[1] = NUL; 5146 colnr_T reglen = 0; 5147 switch (get_reg_type(regname, ®len)) { 5148 case kMTLineWise: 5149 buf[0] = 'V'; 5150 break; 5151 case kMTCharWise: 5152 buf[0] = 'v'; 5153 break; 5154 case kMTBlockWise: 5155 vim_snprintf(buf, sizeof(buf), "%c%d", Ctrl_V, reglen + 1); 5156 break; 5157 case kMTUnknown: 5158 abort(); 5159 } 5160 tv_dict_add_str(dict, S_LEN("regtype"), buf); 5161 5162 buf[0] = (char)get_register_name(get_unname_register()); 5163 buf[1] = NUL; 5164 if (regname == '"') { 5165 tv_dict_add_str(dict, S_LEN("points_to"), buf); 5166 } else { 5167 tv_dict_add_bool(dict, S_LEN("isunnamed"), 5168 regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); 5169 } 5170 } 5171 5172 static void return_register(int regname, typval_T *rettv) 5173 { 5174 char buf[2] = { (char)regname, 0 }; 5175 5176 rettv->v_type = VAR_STRING; 5177 rettv->vval.v_string = xstrdup(buf); 5178 } 5179 5180 /// "reg_executing()" function 5181 static void f_reg_executing(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5182 { 5183 return_register(reg_executing, rettv); 5184 } 5185 5186 /// "reg_recording()" function 5187 static void f_reg_recording(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5188 { 5189 return_register(reg_recording, rettv); 5190 } 5191 5192 static void f_reg_recorded(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5193 { 5194 return_register(reg_recorded, rettv); 5195 } 5196 5197 /// list2proftime - convert a List to proftime_T 5198 /// 5199 /// @param arg The input list, must be of type VAR_LIST and have 5200 /// exactly 2 items 5201 /// @param[out] tm The proftime_T representation of `arg` 5202 /// @return OK In case of success, FAIL in case of error 5203 static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL 5204 { 5205 if (arg->v_type != VAR_LIST || tv_list_len(arg->vval.v_list) != 2) { 5206 return FAIL; 5207 } 5208 5209 bool error = false; 5210 varnumber_T n1 = tv_list_find_nr(arg->vval.v_list, 0, &error); 5211 varnumber_T n2 = tv_list_find_nr(arg->vval.v_list, 1, &error); 5212 if (error) { 5213 return FAIL; 5214 } 5215 5216 // in f_reltime() we split up the 64-bit proftime_T into two 32-bit 5217 // values, now we combine them again. 5218 union { 5219 struct { int32_t low, high; } split; 5220 proftime_T prof; 5221 } u = { .split.high = (int32_t)n1, .split.low = (int32_t)n2 }; 5222 5223 *tm = u.prof; 5224 5225 return OK; 5226 } 5227 5228 /// f_reltime - return an item that represents a time value 5229 /// 5230 /// @param[out] rettv Without an argument it returns the current time. With 5231 /// one argument it returns the time passed since the argument. 5232 /// With two arguments it returns the time passed between 5233 /// the two arguments. 5234 static void f_reltime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5235 { 5236 proftime_T res; 5237 proftime_T start; 5238 5239 if (argvars[0].v_type == VAR_UNKNOWN) { 5240 // no arguments: get current time. 5241 res = profile_start(); 5242 } else if (argvars[1].v_type == VAR_UNKNOWN) { 5243 if (list2proftime(&argvars[0], &res) == FAIL) { 5244 return; 5245 } 5246 res = profile_end(res); 5247 } else { 5248 // two arguments: compute the difference. 5249 if (list2proftime(&argvars[0], &start) == FAIL 5250 || list2proftime(&argvars[1], &res) == FAIL) { 5251 return; 5252 } 5253 res = profile_sub(res, start); 5254 } 5255 5256 // we have to store the 64-bit proftime_T inside of a list of int's 5257 // (varnumber_T is defined as int). For all our supported platforms, int's 5258 // are at least 32-bits wide. So we'll use two 32-bit values to store it. 5259 union { 5260 struct { int32_t low, high; } split; 5261 proftime_T prof; 5262 } u = { .prof = res }; 5263 5264 // statically assert that the union type conv will provide the correct 5265 // results, if varnumber_T or proftime_T change, the union cast will need 5266 // to be revised. 5267 STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), 5268 "type punning will produce incorrect results on this platform"); 5269 5270 tv_list_alloc_ret(rettv, 2); 5271 tv_list_append_number(rettv->vval.v_list, u.split.high); 5272 tv_list_append_number(rettv->vval.v_list, u.split.low); 5273 } 5274 5275 /// "reltimestr()" function 5276 static void f_reltimestr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5277 FUNC_ATTR_NONNULL_ALL 5278 { 5279 proftime_T tm; 5280 5281 rettv->v_type = VAR_STRING; 5282 rettv->vval.v_string = NULL; 5283 if (list2proftime(&argvars[0], &tm) == OK) { 5284 rettv->vval.v_string = xstrdup(profile_msg(tm)); 5285 } 5286 } 5287 5288 /// "repeat()" function 5289 static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5290 { 5291 varnumber_T n = tv_get_number(&argvars[1]); 5292 if (argvars[0].v_type == VAR_LIST) { 5293 tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list)); 5294 while (n-- > 0) { 5295 tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); 5296 } 5297 } else if (argvars[0].v_type == VAR_BLOB) { 5298 tv_blob_alloc_ret(rettv); 5299 if (argvars[0].vval.v_blob == NULL || n <= 0) { 5300 return; 5301 } 5302 5303 const int slen = argvars[0].vval.v_blob->bv_ga.ga_len; 5304 const int len = (int)(slen * n); 5305 if (len <= 0) { 5306 return; 5307 } 5308 5309 ga_grow(&rettv->vval.v_blob->bv_ga, len); 5310 5311 rettv->vval.v_blob->bv_ga.ga_len = len; 5312 5313 int i; 5314 for (i = 0; i < slen; i++) { 5315 if (tv_blob_get(argvars[0].vval.v_blob, i) != 0) { 5316 break; 5317 } 5318 } 5319 5320 if (i == slen) { 5321 // No need to copy since all bytes are already zero 5322 return; 5323 } 5324 5325 for (i = 0; i < n; i++) { 5326 tv_blob_set_range(rettv->vval.v_blob, i * slen, (i + 1) * slen - 1, argvars); 5327 } 5328 } else { 5329 rettv->v_type = VAR_STRING; 5330 rettv->vval.v_string = NULL; 5331 if (n <= 0) { 5332 return; 5333 } 5334 5335 const char *const p = tv_get_string(&argvars[0]); 5336 5337 const size_t slen = strlen(p); 5338 if (slen == 0) { 5339 return; 5340 } 5341 const size_t len = slen * (size_t)n; 5342 // Detect overflow. 5343 if (len / (size_t)n != slen) { 5344 return; 5345 } 5346 5347 char *const r = xmallocz(len); 5348 for (varnumber_T i = 0; i < n; i++) { 5349 memmove(r + (size_t)i * slen, p, slen); 5350 } 5351 5352 rettv->vval.v_string = r; 5353 } 5354 } 5355 5356 /// Implementation of reduce() for list "argvars[0]", using the function "expr" 5357 /// starting with the optional initial value argvars[2] and return the result in 5358 /// "rettv". 5359 static void reduce_list(typval_T *argvars, typval_T *expr, typval_T *rettv) 5360 { 5361 list_T *const l = argvars[0].vval.v_list; 5362 const int called_emsg_start = called_emsg; 5363 5364 typval_T initial; 5365 const listitem_T *li = NULL; 5366 if (argvars[2].v_type == VAR_UNKNOWN) { 5367 if (tv_list_len(l) == 0) { 5368 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "List"); 5369 return; 5370 } 5371 const listitem_T *const first = tv_list_first(l); 5372 initial = *TV_LIST_ITEM_TV(first); 5373 li = TV_LIST_ITEM_NEXT(l, first); 5374 } else { 5375 initial = argvars[2]; 5376 li = tv_list_first(l); 5377 } 5378 5379 tv_copy(&initial, rettv); 5380 5381 if (l == NULL) { 5382 return; 5383 } 5384 5385 const VarLockStatus prev_locked = tv_list_locked(l); 5386 5387 tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here 5388 for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { 5389 typval_T argv[3]; 5390 argv[0] = *rettv; 5391 argv[1] = *TV_LIST_ITEM_TV(li); 5392 rettv->v_type = VAR_UNKNOWN; 5393 5394 const int r = eval_expr_typval(expr, true, argv, 2, rettv); 5395 5396 tv_clear(&argv[0]); 5397 if (r == FAIL || called_emsg != called_emsg_start) { 5398 break; 5399 } 5400 } 5401 tv_list_set_lock(l, prev_locked); 5402 } 5403 5404 /// Implementation of reduce() for String "argvars[0]" using the function "expr" 5405 /// starting with the optional initial value "argvars[2]" and return the result 5406 /// in "rettv". 5407 static void reduce_string(typval_T *argvars, typval_T *expr, typval_T *rettv) 5408 { 5409 const char *p = tv_get_string(&argvars[0]); 5410 int len; 5411 const int called_emsg_start = called_emsg; 5412 5413 if (argvars[2].v_type == VAR_UNKNOWN) { 5414 if (*p == NUL) { 5415 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "String"); 5416 return; 5417 } 5418 len = utfc_ptr2len(p); 5419 *rettv = (typval_T){ 5420 .v_type = VAR_STRING, 5421 .v_lock = VAR_UNLOCKED, 5422 .vval.v_string = xmemdupz(p, (size_t)len), 5423 }; 5424 p += len; 5425 } else if (tv_check_for_string_arg(argvars, 2) == FAIL) { 5426 return; 5427 } else { 5428 tv_copy(&argvars[2], rettv); 5429 } 5430 5431 for (; *p != NUL; p += len) { 5432 typval_T argv[3]; 5433 argv[0] = *rettv; 5434 len = utfc_ptr2len(p); 5435 argv[1] = (typval_T){ 5436 .v_type = VAR_STRING, 5437 .v_lock = VAR_UNLOCKED, 5438 .vval.v_string = xmemdupz(p, (size_t)len), 5439 }; 5440 5441 const int r = eval_expr_typval(expr, true, argv, 2, rettv); 5442 5443 tv_clear(&argv[0]); 5444 tv_clear(&argv[1]); 5445 if (r == FAIL || called_emsg != called_emsg_start) { 5446 break; 5447 } 5448 } 5449 } 5450 5451 /// Implementation of reduce() for Blob "argvars[0]" using the function "expr" 5452 /// starting with the optional initial value "argvars[2]" and return the result 5453 /// in "rettv". 5454 static void reduce_blob(typval_T *argvars, typval_T *expr, typval_T *rettv) 5455 { 5456 const blob_T *const b = argvars[0].vval.v_blob; 5457 const int called_emsg_start = called_emsg; 5458 5459 typval_T initial; 5460 int i; 5461 if (argvars[2].v_type == VAR_UNKNOWN) { 5462 if (tv_blob_len(b) == 0) { 5463 semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "Blob"); 5464 return; 5465 } 5466 initial = (typval_T){ 5467 .v_type = VAR_NUMBER, 5468 .v_lock = VAR_UNLOCKED, 5469 .vval.v_number = tv_blob_get(b, 0), 5470 }; 5471 i = 1; 5472 } else if (tv_check_for_number_arg(argvars, 2) == FAIL) { 5473 return; 5474 } else { 5475 initial = argvars[2]; 5476 i = 0; 5477 } 5478 5479 tv_copy(&initial, rettv); 5480 for (; i < tv_blob_len(b); i++) { 5481 typval_T argv[3]; 5482 argv[0] = *rettv; 5483 argv[1] = (typval_T){ 5484 .v_type = VAR_NUMBER, 5485 .v_lock = VAR_UNLOCKED, 5486 .vval.v_number = tv_blob_get(b, i), 5487 }; 5488 5489 const int r = eval_expr_typval(expr, true, argv, 2, rettv); 5490 5491 if (r == FAIL || called_emsg != called_emsg_start) { 5492 return; 5493 } 5494 } 5495 } 5496 5497 /// "reduce(list, { accumulator, element -> value } [, initial])" function 5498 /// "reduce(blob, { accumulator, element -> value } [, initial])" function 5499 /// "reduce(string, { accumulator, element -> value } [, initial])" function 5500 static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5501 { 5502 if (argvars[0].v_type != VAR_STRING 5503 && argvars[0].v_type != VAR_LIST 5504 && argvars[0].v_type != VAR_BLOB) { 5505 emsg(_(e_string_list_or_blob_required)); 5506 return; 5507 } 5508 5509 const char *func_name; 5510 if (argvars[1].v_type == VAR_FUNC) { 5511 func_name = argvars[1].vval.v_string; 5512 } else if (argvars[1].v_type == VAR_PARTIAL) { 5513 func_name = partial_name(argvars[1].vval.v_partial); 5514 } else { 5515 func_name = tv_get_string(&argvars[1]); 5516 } 5517 if (func_name == NULL || *func_name == NUL) { 5518 emsg(_(e_missing_function_argument)); 5519 return; 5520 } 5521 5522 if (argvars[0].v_type == VAR_LIST) { 5523 reduce_list(argvars, &argvars[1], rettv); 5524 } else if (argvars[0].v_type == VAR_STRING) { 5525 reduce_string(argvars, &argvars[1], rettv); 5526 } else { 5527 reduce_blob(argvars, &argvars[1], rettv); 5528 } 5529 } 5530 5531 #define SP_NOMOVE 0x01 ///< don't move cursor 5532 #define SP_REPEAT 0x02 ///< repeat to find outer pair 5533 #define SP_RETCOUNT 0x04 ///< return matchcount 5534 #define SP_SETPCMARK 0x08 ///< set previous context mark 5535 #define SP_START 0x10 ///< accept match at start position 5536 #define SP_SUBPAT 0x20 ///< return nr of matching sub-pattern 5537 #define SP_END 0x40 ///< leave cursor at end of match 5538 #define SP_COLUMN 0x80 ///< start at cursor column 5539 5540 /// Get flags for a search function. 5541 /// Possibly sets "p_ws". 5542 /// 5543 /// @return BACKWARD, FORWARD or zero (for an error). 5544 static int get_search_arg(typval_T *varp, int *flagsp) 5545 { 5546 int dir = FORWARD; 5547 5548 if (varp->v_type == VAR_UNKNOWN) { 5549 return FORWARD; 5550 } 5551 5552 char nbuf[NUMBUFLEN]; 5553 const char *flags = tv_get_string_buf_chk(varp, nbuf); 5554 if (flags == NULL) { 5555 return 0; // Type error; errmsg already given. 5556 } 5557 int mask; 5558 while (*flags != NUL) { 5559 switch (*flags) { 5560 case 'b': 5561 dir = BACKWARD; break; 5562 case 'w': 5563 p_ws = true; break; 5564 case 'W': 5565 p_ws = false; break; 5566 default: 5567 mask = 0; 5568 if (flagsp != NULL) { 5569 switch (*flags) { 5570 case 'c': 5571 mask = SP_START; break; 5572 case 'e': 5573 mask = SP_END; break; 5574 case 'm': 5575 mask = SP_RETCOUNT; break; 5576 case 'n': 5577 mask = SP_NOMOVE; break; 5578 case 'p': 5579 mask = SP_SUBPAT; break; 5580 case 'r': 5581 mask = SP_REPEAT; break; 5582 case 's': 5583 mask = SP_SETPCMARK; break; 5584 case 'z': 5585 mask = SP_COLUMN; break; 5586 } 5587 } 5588 if (mask == 0) { 5589 semsg(_(e_invarg2), flags); 5590 dir = 0; 5591 } else { 5592 *flagsp |= mask; 5593 } 5594 } 5595 if (dir == 0) { 5596 break; 5597 } 5598 flags++; 5599 } 5600 return dir; 5601 } 5602 5603 /// Shared by search() and searchpos() functions. 5604 static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) 5605 { 5606 bool save_p_ws = p_ws; 5607 int retval = 0; // default: FAIL 5608 linenr_T lnum_stop = 0; 5609 int64_t time_limit = 0; 5610 int options = SEARCH_KEEP; 5611 bool use_skip = false; 5612 5613 const char *const pat = tv_get_string(&argvars[0]); 5614 int dir = get_search_arg(&argvars[1], flagsp); // May set p_ws. 5615 if (dir == 0) { 5616 goto theend; 5617 } 5618 int flags = *flagsp; 5619 if (flags & SP_START) { 5620 options |= SEARCH_START; 5621 } 5622 if (flags & SP_END) { 5623 options |= SEARCH_END; 5624 } 5625 if (flags & SP_COLUMN) { 5626 options |= SEARCH_COL; 5627 } 5628 5629 // Optional arguments: line number to stop searching, timeout and skip. 5630 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { 5631 lnum_stop = (linenr_T)tv_get_number_chk(&argvars[2], NULL); 5632 if (lnum_stop < 0) { 5633 goto theend; 5634 } 5635 if (argvars[3].v_type != VAR_UNKNOWN) { 5636 time_limit = tv_get_number_chk(&argvars[3], NULL); 5637 if (time_limit < 0) { 5638 goto theend; 5639 } 5640 use_skip = eval_expr_valid_arg(&argvars[4]); 5641 } 5642 } 5643 5644 // Set the time limit, if there is one. 5645 proftime_T tm = profile_setlimit(time_limit); 5646 5647 // This function does not accept SP_REPEAT and SP_RETCOUNT flags. 5648 // Check to make sure only those flags are set. 5649 // Also, Only the SP_NOMOVE or the SP_SETPCMARK flag can be set. Both 5650 // flags cannot be set. Check for that condition also. 5651 if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0) 5652 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { 5653 semsg(_(e_invarg2), tv_get_string(&argvars[1])); 5654 goto theend; 5655 } 5656 5657 pos_T save_cursor; 5658 pos_T pos = save_cursor = curwin->w_cursor; 5659 pos_T firstpos = { 0 }; 5660 searchit_arg_T sia = { 5661 .sa_stop_lnum = lnum_stop, 5662 .sa_tm = &tm, 5663 }; 5664 5665 const size_t patlen = strlen(pat); 5666 int subpatnum; 5667 5668 // Repeat until {skip} returns false. 5669 while (true) { 5670 subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, (char *)pat, patlen, 1, 5671 options, RE_SEARCH, &sia); 5672 // finding the first match again means there is no match where {skip} 5673 // evaluates to zero. 5674 if (firstpos.lnum != 0 && equalpos(pos, firstpos)) { 5675 subpatnum = FAIL; 5676 } 5677 5678 if (subpatnum == FAIL || !use_skip) { 5679 // didn't find it or no skip argument 5680 break; 5681 } 5682 if (firstpos.lnum == 0) { 5683 firstpos = pos; 5684 } 5685 5686 // If the skip expression matches, ignore this match. 5687 { 5688 const pos_T save_pos = curwin->w_cursor; 5689 5690 curwin->w_cursor = pos; 5691 bool err = false; 5692 const bool do_skip = eval_expr_to_bool(&argvars[4], &err); 5693 curwin->w_cursor = save_pos; 5694 if (err) { 5695 // Evaluating {skip} caused an error, break here. 5696 subpatnum = FAIL; 5697 break; 5698 } 5699 if (!do_skip) { 5700 break; 5701 } 5702 } 5703 5704 // clear the start flag to avoid getting stuck here 5705 options &= ~SEARCH_START; 5706 } 5707 5708 if (subpatnum != FAIL) { 5709 if (flags & SP_SUBPAT) { 5710 retval = subpatnum; 5711 } else { 5712 retval = pos.lnum; 5713 } 5714 if (flags & SP_SETPCMARK) { 5715 setpcmark(); 5716 } 5717 curwin->w_cursor = pos; 5718 if (match_pos != NULL) { 5719 // Store the match cursor position 5720 match_pos->lnum = pos.lnum; 5721 match_pos->col = pos.col + 1; 5722 } 5723 // "/$" will put the cursor after the end of the line, may need to 5724 // correct that here 5725 check_cursor(curwin); 5726 } 5727 5728 // If 'n' flag is used: restore cursor position. 5729 if (flags & SP_NOMOVE) { 5730 curwin->w_cursor = save_cursor; 5731 } else { 5732 curwin->w_set_curswant = true; 5733 } 5734 theend: 5735 p_ws = save_p_ws; 5736 5737 return retval; 5738 } 5739 5740 /// "rpcnotify()" function 5741 static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5742 { 5743 rettv->v_type = VAR_NUMBER; 5744 rettv->vval.v_number = 0; 5745 5746 if (check_secure()) { 5747 return; 5748 } 5749 5750 if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number < 0) { 5751 semsg(_(e_invarg2), "Channel id must be a positive integer"); 5752 return; 5753 } 5754 5755 if (argvars[1].v_type != VAR_STRING) { 5756 semsg(_(e_invarg2), "Event type must be a string"); 5757 return; 5758 } 5759 5760 MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); 5761 Arena arena = ARENA_EMPTY; 5762 5763 for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { 5764 ADD_C(args, vim_to_object(tv, &arena, true)); 5765 } 5766 5767 bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number, 5768 tv_get_string(&argvars[1]), args); 5769 5770 arena_mem_free(arena_finish(&arena)); 5771 5772 if (!ok) { 5773 semsg(_(e_invarg2), "Channel doesn't exist"); 5774 return; 5775 } 5776 rettv->vval.v_number = 1; 5777 } 5778 5779 /// "rpcrequest()" function 5780 static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5781 { 5782 rettv->v_type = VAR_NUMBER; 5783 rettv->vval.v_number = 0; 5784 const int l_provider_call_nesting = provider_call_nesting; 5785 5786 if (check_secure()) { 5787 return; 5788 } 5789 5790 if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number <= 0) { 5791 semsg(_(e_invarg2), "Channel id must be a positive integer"); 5792 return; 5793 } 5794 5795 if (argvars[1].v_type != VAR_STRING) { 5796 semsg(_(e_invarg2), "Method name must be a string"); 5797 return; 5798 } 5799 5800 MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); 5801 Arena arena = ARENA_EMPTY; 5802 5803 for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { 5804 ADD_C(args, vim_to_object(tv, &arena, true)); 5805 } 5806 5807 sctx_T save_current_sctx; 5808 char *save_autocmd_fname, *save_autocmd_match; 5809 bool save_autocmd_fname_full; 5810 int save_autocmd_bufnr; 5811 funccal_entry_T funccal_entry; 5812 5813 if (l_provider_call_nesting) { 5814 // If this is called from a provider function, restore the scope 5815 // information of the caller. 5816 save_current_sctx = current_sctx; 5817 save_autocmd_fname = autocmd_fname; 5818 save_autocmd_match = autocmd_match; 5819 save_autocmd_fname_full = autocmd_fname_full; 5820 save_autocmd_bufnr = autocmd_bufnr; 5821 save_funccal(&funccal_entry); 5822 5823 current_sctx = provider_caller_scope.script_ctx; 5824 ga_grow(&exestack, 1); 5825 ((estack_T *)exestack.ga_data)[exestack.ga_len++] = provider_caller_scope.es_entry; 5826 autocmd_fname = provider_caller_scope.autocmd_fname; 5827 autocmd_match = provider_caller_scope.autocmd_match; 5828 autocmd_fname_full = provider_caller_scope.autocmd_fname_full; 5829 autocmd_bufnr = provider_caller_scope.autocmd_bufnr; 5830 set_current_funccal((funccall_T *)(provider_caller_scope.funccalp)); 5831 } 5832 5833 Error err = ERROR_INIT; 5834 5835 uint64_t chan_id = (uint64_t)argvars[0].vval.v_number; 5836 const char *method = tv_get_string(&argvars[1]); 5837 5838 ArenaMem res_mem = NULL; 5839 Object result = rpc_send_call(chan_id, method, args, &res_mem, &err); 5840 arena_mem_free(arena_finish(&arena)); 5841 5842 if (l_provider_call_nesting) { 5843 current_sctx = save_current_sctx; 5844 exestack.ga_len--; 5845 autocmd_fname = save_autocmd_fname; 5846 autocmd_match = save_autocmd_match; 5847 autocmd_fname_full = save_autocmd_fname_full; 5848 autocmd_bufnr = save_autocmd_bufnr; 5849 restore_funccal(); 5850 } 5851 5852 if (ERROR_SET(&err)) { 5853 const char *name = NULL; 5854 Channel *chan = find_channel(chan_id); 5855 if (chan) { 5856 name = get_client_info(chan, "name"); 5857 } 5858 if (name) { 5859 semsg_multiline("rpc_error", "Invoking '%s' on channel %" PRIu64 " (%s):\n%s", 5860 method, chan_id, name, err.msg); 5861 } else { 5862 semsg_multiline("rpc_error", "Invoking '%s' on channel %" PRIu64 ":\n%s", 5863 method, chan_id, err.msg); 5864 } 5865 5866 goto end; 5867 } 5868 5869 object_to_vim(result, rettv, &err); 5870 5871 end: 5872 arena_mem_free(res_mem); 5873 api_clear_error(&err); 5874 } 5875 5876 static void screenchar_adjust(ScreenGrid **grid, int *row, int *col) 5877 { 5878 // TODO(bfredl): this is a hack for legacy tests which use screenchar() 5879 // to check printed messages on the screen (but not floats etc 5880 // as these are not legacy features). If the compositor is refactored to 5881 // have its own buffer, this should just read from it instead. 5882 msg_scroll_flush(); 5883 5884 *grid = ui_comp_get_grid_at_coord(*row, *col); 5885 5886 // Make `row` and `col` relative to the grid 5887 *row -= (*grid)->comp_row; 5888 *col -= (*grid)->comp_col; 5889 } 5890 5891 /// "screenattr()" function 5892 static void f_screenattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5893 { 5894 int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; 5895 int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; 5896 5897 ScreenGrid *grid; 5898 screenchar_adjust(&grid, &row, &col); 5899 5900 int c; 5901 if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { 5902 c = -1; 5903 } else { 5904 c = grid->attrs[grid->line_offset[row] + (size_t)col]; 5905 } 5906 rettv->vval.v_number = c; 5907 } 5908 5909 /// "screenchar()" function 5910 static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5911 { 5912 int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; 5913 int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; 5914 5915 ScreenGrid *grid; 5916 screenchar_adjust(&grid, &row, &col); 5917 5918 rettv->vval.v_number = (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) 5919 ? -1 : schar_get_first_codepoint(grid_getchar(grid, row, col, NULL)); 5920 } 5921 5922 /// "screenchars()" function 5923 static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5924 { 5925 int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; 5926 int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; 5927 5928 ScreenGrid *grid; 5929 screenchar_adjust(&grid, &row, &col); 5930 5931 tv_list_alloc_ret(rettv, kListLenMayKnow); 5932 if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { 5933 return; 5934 } 5935 5936 char buf[MAX_SCHAR_SIZE + 1]; 5937 schar_get(buf, grid_getchar(grid, row, col, NULL)); 5938 5939 // schar values are already processed chars which are always NUL-terminated. 5940 // A single [0] is expected when char is NUL. 5941 size_t i = 0; 5942 do { 5943 int c = utf_ptr2char(buf + i); 5944 tv_list_append_number(rettv->vval.v_list, c); 5945 i += (size_t)utf_ptr2len(buf + i); 5946 } while (buf[i] != NUL); 5947 } 5948 5949 /// "screencol()" function 5950 /// 5951 /// First column is 1 to be consistent with virtcol(). 5952 static void f_screencol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5953 { 5954 rettv->vval.v_number = ui_current_col() + 1; 5955 } 5956 5957 /// "screenrow()" function 5958 static void f_screenrow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5959 { 5960 rettv->vval.v_number = ui_current_row() + 1; 5961 } 5962 5963 /// "screenstring()" function 5964 static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5965 { 5966 rettv->vval.v_string = NULL; 5967 rettv->v_type = VAR_STRING; 5968 5969 ScreenGrid *grid; 5970 int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; 5971 int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; 5972 5973 screenchar_adjust(&grid, &row, &col); 5974 5975 if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { 5976 return; 5977 } 5978 5979 char buf[MAX_SCHAR_SIZE + 1]; 5980 schar_get(buf, grid_getchar(grid, row, col, NULL)); 5981 rettv->vval.v_string = xstrdup(buf); 5982 } 5983 5984 /// "search()" function 5985 static void f_search(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5986 { 5987 int flags = 0; 5988 5989 rettv->vval.v_number = search_cmn(argvars, NULL, &flags); 5990 } 5991 5992 /// "searchdecl()" function 5993 static void f_searchdecl(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 5994 { 5995 int locally = 1; 5996 int thisblock = 0; 5997 bool error = false; 5998 5999 rettv->vval.v_number = 1; // default: FAIL 6000 6001 const char *const name = tv_get_string_chk(&argvars[0]); 6002 if (argvars[1].v_type != VAR_UNKNOWN) { 6003 locally = tv_get_number_chk(&argvars[1], &error) == 0; 6004 if (!error && argvars[2].v_type != VAR_UNKNOWN) { 6005 thisblock = tv_get_number_chk(&argvars[2], &error) != 0; 6006 } 6007 } 6008 if (!error && name != NULL) { 6009 rettv->vval.v_number = find_decl((char *)name, strlen(name), locally, 6010 thisblock, SEARCH_KEEP) == FAIL; 6011 } 6012 } 6013 6014 /// Used by searchpair() and searchpairpos() 6015 static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) 6016 { 6017 bool save_p_ws = p_ws; 6018 int flags = 0; 6019 int retval = 0; // default: FAIL 6020 linenr_T lnum_stop = 0; 6021 int64_t time_limit = 0; 6022 6023 // Get the three pattern arguments: start, middle, end. Will result in an 6024 // error if not a valid argument. 6025 char nbuf1[NUMBUFLEN]; 6026 char nbuf2[NUMBUFLEN]; 6027 const char *spat = tv_get_string_chk(&argvars[0]); 6028 const char *mpat = tv_get_string_buf_chk(&argvars[1], nbuf1); 6029 const char *epat = tv_get_string_buf_chk(&argvars[2], nbuf2); 6030 if (spat == NULL || mpat == NULL || epat == NULL) { 6031 goto theend; // Type error. 6032 } 6033 6034 // Handle the optional fourth argument: flags. 6035 int dir = get_search_arg(&argvars[3], &flags); // may set p_ws. 6036 if (dir == 0) { 6037 goto theend; 6038 } 6039 6040 // Don't accept SP_END or SP_SUBPAT. 6041 // Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. 6042 if ((flags & (SP_END | SP_SUBPAT)) != 0 6043 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { 6044 semsg(_(e_invarg2), tv_get_string(&argvars[3])); 6045 goto theend; 6046 } 6047 6048 // Using 'r' implies 'W', otherwise it doesn't work. 6049 if (flags & SP_REPEAT) { 6050 p_ws = false; 6051 } 6052 6053 // Optional fifth argument: skip expression. 6054 const typval_T *skip; 6055 if (argvars[3].v_type == VAR_UNKNOWN 6056 || argvars[4].v_type == VAR_UNKNOWN) { 6057 skip = NULL; 6058 } else { 6059 // Type is checked later. 6060 skip = &argvars[4]; 6061 6062 if (argvars[5].v_type != VAR_UNKNOWN) { 6063 lnum_stop = (linenr_T)tv_get_number_chk(&argvars[5], NULL); 6064 if (lnum_stop < 0) { 6065 semsg(_(e_invarg2), tv_get_string(&argvars[5])); 6066 goto theend; 6067 } 6068 if (argvars[6].v_type != VAR_UNKNOWN) { 6069 time_limit = tv_get_number_chk(&argvars[6], NULL); 6070 if (time_limit < 0) { 6071 semsg(_(e_invarg2), tv_get_string(&argvars[6])); 6072 goto theend; 6073 } 6074 } 6075 } 6076 } 6077 6078 retval = do_searchpair(spat, mpat, epat, dir, skip, 6079 flags, match_pos, lnum_stop, time_limit); 6080 6081 theend: 6082 p_ws = save_p_ws; 6083 6084 return retval; 6085 } 6086 6087 /// "searchpair()" function 6088 static void f_searchpair(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6089 { 6090 rettv->vval.v_number = searchpair_cmn(argvars, NULL); 6091 } 6092 6093 /// "searchpairpos()" function 6094 static void f_searchpairpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6095 { 6096 pos_T match_pos; 6097 int lnum = 0; 6098 int col = 0; 6099 6100 tv_list_alloc_ret(rettv, 2); 6101 6102 if (searchpair_cmn(argvars, &match_pos) > 0) { 6103 lnum = match_pos.lnum; 6104 col = match_pos.col; 6105 } 6106 6107 tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); 6108 tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); 6109 } 6110 6111 /// Search for a start/middle/end thing. 6112 /// Used by searchpair(), see its documentation for the details. 6113 /// 6114 /// @param spat start pattern 6115 /// @param mpat middle pattern 6116 /// @param epat end pattern 6117 /// @param dir BACKWARD or FORWARD 6118 /// @param skip skip expression 6119 /// @param flags SP_SETPCMARK and other SP_ values 6120 /// @param lnum_stop stop at this line if not zero 6121 /// @param time_limit stop after this many msec 6122 /// 6123 /// @returns 0 or -1 for no match, 6124 int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir, 6125 const typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, 6126 int64_t time_limit) 6127 FUNC_ATTR_NONNULL_ARG(1, 2, 3) 6128 { 6129 int retval = 0; 6130 int nest = 1; 6131 bool use_skip = false; 6132 int options = SEARCH_KEEP; 6133 6134 // Make 'cpoptions' empty, the 'l' flag should not be used here. 6135 char *save_cpo = p_cpo; 6136 p_cpo = empty_string_option; 6137 6138 // Set the time limit, if there is one. 6139 proftime_T tm = profile_setlimit(time_limit); 6140 6141 // Make two search patterns: start/end (pat2, for in nested pairs) and 6142 // start/middle/end (pat3, for the top pair). 6143 const size_t spatlen = strlen(spat); 6144 const size_t epatlen = strlen(epat); 6145 const size_t pat2size = spatlen + epatlen + 17; 6146 char *pat2 = xmalloc(pat2size); 6147 const size_t pat3size = spatlen + strlen(mpat) + epatlen + 25; 6148 char *pat3 = xmalloc(pat3size); 6149 int pat2len = snprintf(pat2, pat2size, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); 6150 int pat3len; 6151 if (*mpat == NUL) { 6152 STRCPY(pat3, pat2); 6153 pat3len = pat2len; 6154 } else { 6155 pat3len = snprintf(pat3, pat3size, 6156 "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat); 6157 } 6158 if (flags & SP_START) { 6159 options |= SEARCH_START; 6160 } 6161 6162 if (skip != NULL) { 6163 use_skip = eval_expr_valid_arg(skip); 6164 } 6165 6166 pos_T save_cursor = curwin->w_cursor; 6167 pos_T pos = curwin->w_cursor; 6168 pos_T firstpos; 6169 clearpos(&firstpos); 6170 pos_T foundpos; 6171 clearpos(&foundpos); 6172 char *pat = pat3; 6173 assert(pat3len >= 0); 6174 size_t patlen = (size_t)pat3len; 6175 while (true) { 6176 searchit_arg_T sia = { 6177 .sa_stop_lnum = lnum_stop, 6178 .sa_tm = &tm, 6179 }; 6180 6181 int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 1, 6182 options, RE_SEARCH, &sia); 6183 if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) { 6184 // didn't find it or found the first match again: FAIL 6185 break; 6186 } 6187 6188 if (firstpos.lnum == 0) { 6189 firstpos = pos; 6190 } 6191 if (equalpos(pos, foundpos)) { 6192 // Found the same position again. Can happen with a pattern that 6193 // has "\zs" at the end and searching backwards. Advance one 6194 // character and try again. 6195 if (dir == BACKWARD) { 6196 decl(&pos); 6197 } else { 6198 incl(&pos); 6199 } 6200 } 6201 foundpos = pos; 6202 6203 // clear the start flag to avoid getting stuck here 6204 options &= ~SEARCH_START; 6205 6206 // If the skip pattern matches, ignore this match. 6207 if (use_skip) { 6208 pos_T save_pos = curwin->w_cursor; 6209 curwin->w_cursor = pos; 6210 bool err = false; 6211 const bool r = eval_expr_to_bool(skip, &err); 6212 curwin->w_cursor = save_pos; 6213 if (err) { 6214 // Evaluating {skip} caused an error, break here. 6215 curwin->w_cursor = save_cursor; 6216 retval = -1; 6217 break; 6218 } 6219 if (r) { 6220 continue; 6221 } 6222 } 6223 6224 if ((dir == BACKWARD && n == 3) || (dir == FORWARD && n == 2)) { 6225 // Found end when searching backwards or start when searching 6226 // forward: nested pair. 6227 nest++; 6228 pat = pat2; // nested, don't search for middle 6229 } else { 6230 // Found end when searching forward or start when searching 6231 // backward: end of (nested) pair; or found middle in outer pair. 6232 if (--nest == 1) { 6233 pat = pat3; // outer level, search for middle 6234 } 6235 } 6236 6237 if (nest == 0) { 6238 // Found the match: return matchcount or line number. 6239 if (flags & SP_RETCOUNT) { 6240 retval++; 6241 } else { 6242 retval = pos.lnum; 6243 } 6244 if (flags & SP_SETPCMARK) { 6245 setpcmark(); 6246 } 6247 curwin->w_cursor = pos; 6248 if (!(flags & SP_REPEAT)) { 6249 break; 6250 } 6251 nest = 1; // search for next unmatched 6252 } 6253 } 6254 6255 if (match_pos != NULL) { 6256 // Store the match cursor position 6257 match_pos->lnum = curwin->w_cursor.lnum; 6258 match_pos->col = curwin->w_cursor.col + 1; 6259 } 6260 6261 // If 'n' flag is used or search failed: restore cursor position. 6262 if ((flags & SP_NOMOVE) || retval == 0) { 6263 curwin->w_cursor = save_cursor; 6264 } 6265 6266 xfree(pat2); 6267 xfree(pat3); 6268 if (p_cpo == empty_string_option) { 6269 p_cpo = save_cpo; 6270 } else { 6271 // Darn, evaluating the {skip} expression changed the value. 6272 // If it's still empty it was changed and restored, need to restore in 6273 // the complicated way. 6274 if (*p_cpo == NUL) { 6275 set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); 6276 } 6277 free_string_option(save_cpo); 6278 } 6279 6280 return retval; 6281 } 6282 6283 /// "searchpos()" function 6284 static void f_searchpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6285 { 6286 pos_T match_pos; 6287 int flags = 0; 6288 6289 const int n = search_cmn(argvars, &match_pos, &flags); 6290 6291 tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT))); 6292 6293 const int lnum = (n > 0 ? match_pos.lnum : 0); 6294 const int col = (n > 0 ? match_pos.col : 0); 6295 6296 tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); 6297 tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); 6298 if (flags & SP_SUBPAT) { 6299 tv_list_append_number(rettv->vval.v_list, (varnumber_T)n); 6300 } 6301 } 6302 6303 /// "serverlist()" function 6304 static void f_serverlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6305 { 6306 size_t n; 6307 char **addrs = server_address_list(&n); 6308 6309 Arena arena = ARENA_EMPTY; 6310 // Passed to vim._core.server.serverlist() to avoid duplicates 6311 Array addrs_arr = arena_array(&arena, n); 6312 6313 // Copy addrs into a linked list. 6314 list_T *const l = tv_list_alloc_ret(rettv, (ptrdiff_t)n); 6315 for (size_t i = 0; i < n; i++) { 6316 tv_list_append_allocated_string(l, addrs[i]); 6317 ADD_C(addrs_arr, CSTR_AS_OBJ(addrs[i])); 6318 } 6319 6320 if (!(argvars[0].v_type == VAR_DICT && tv_dict_get_bool(argvars[0].vval.v_dict, "peer", false))) { 6321 goto cleanup; 6322 } 6323 6324 MAXSIZE_TEMP_ARRAY(args, 1); 6325 ADD_C(args, ARRAY_OBJ(addrs_arr)); 6326 6327 Error err = ERROR_INIT; 6328 Object rv = NLUA_EXEC_STATIC("return require('vim._core.server').serverlist(...)", 6329 args, kRetObject, 6330 &arena, &err); 6331 6332 if (ERROR_SET(&err)) { 6333 ELOG("vim._core.serverlist failed: %s", err.msg); 6334 goto cleanup; 6335 } 6336 6337 for (size_t i = 0; i < rv.data.array.size; i++) { 6338 char *curr_server = rv.data.array.items[i].data.string.data; 6339 tv_list_append_string(l, curr_server, -1); 6340 } 6341 6342 cleanup: 6343 xfree(addrs); 6344 arena_mem_free(arena_finish(&arena)); 6345 } 6346 6347 /// "serverstart()" function 6348 static void f_serverstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6349 { 6350 rettv->v_type = VAR_STRING; 6351 rettv->vval.v_string = NULL; // Address of the new server 6352 6353 if (check_secure()) { 6354 return; 6355 } 6356 6357 char *address; 6358 // If the user supplied an address, use it, otherwise use a temp. 6359 if (argvars[0].v_type != VAR_UNKNOWN) { 6360 if (argvars[0].v_type != VAR_STRING) { 6361 emsg(_(e_invarg)); 6362 return; 6363 } 6364 address = xstrdup(tv_get_string(argvars)); 6365 } else { 6366 address = server_address_new(NULL); 6367 } 6368 6369 int result = server_start(address); 6370 xfree(address); 6371 6372 if (result != 0) { 6373 semsg("Failed to start server: %s", 6374 result > 0 ? "Unknown system error" : uv_strerror(result)); 6375 return; 6376 } 6377 6378 // Since it's possible server_start adjusted the given {address} (e.g., 6379 // "localhost:" will now have a port), return the final value to the user. 6380 size_t n; 6381 char **addrs = server_address_list(&n); 6382 rettv->vval.v_string = addrs[n - 1]; 6383 6384 n--; 6385 for (size_t i = 0; i < n; i++) { 6386 xfree(addrs[i]); 6387 } 6388 xfree(addrs); 6389 } 6390 6391 /// "serverstop()" function 6392 static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6393 { 6394 if (check_secure()) { 6395 return; 6396 } 6397 6398 if (argvars[0].v_type != VAR_STRING) { 6399 emsg(_(e_invarg)); 6400 return; 6401 } 6402 6403 rettv->v_type = VAR_NUMBER; 6404 rettv->vval.v_number = 0; 6405 if (argvars[0].vval.v_string) { 6406 bool rv = server_stop(argvars[0].vval.v_string); 6407 rettv->vval.v_number = (rv ? 1 : 0); 6408 } 6409 } 6410 6411 /// Set the cursor or mark position. 6412 /// If "charpos" is true, then use the column number as a character offset. 6413 /// Otherwise use the column number as a byte offset. 6414 static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) 6415 { 6416 colnr_T curswant = -1; 6417 6418 rettv->vval.v_number = -1; 6419 const char *const name = tv_get_string_chk(argvars); 6420 if (name == NULL) { 6421 return; 6422 } 6423 6424 pos_T pos; 6425 int fnum; 6426 if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) != OK) { 6427 return; 6428 } 6429 6430 if (pos.col != MAXCOL && --pos.col < 0) { 6431 pos.col = 0; 6432 } 6433 if (name[0] == '.' && name[1] == NUL) { 6434 // set cursor; "fnum" is ignored 6435 curwin->w_cursor = pos; 6436 if (curswant >= 0) { 6437 curwin->w_curswant = curswant - 1; 6438 curwin->w_set_curswant = false; 6439 } 6440 check_cursor(curwin); 6441 rettv->vval.v_number = 0; 6442 } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { 6443 // set mark 6444 if (setmark_pos((uint8_t)name[1], &pos, fnum, NULL) == OK) { 6445 rettv->vval.v_number = 0; 6446 } 6447 } else { 6448 emsg(_(e_invarg)); 6449 } 6450 } 6451 6452 /// "setcharpos()" function 6453 static void f_setcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6454 { 6455 set_position(argvars, rettv, true); 6456 } 6457 6458 static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6459 { 6460 if (tv_check_for_dict_arg(argvars, 0) == FAIL) { 6461 return; 6462 } 6463 6464 dict_T *d = argvars[0].vval.v_dict; 6465 if (d == NULL) { 6466 return; 6467 } 6468 6469 char *const csearch = tv_dict_get_string(d, "char", false); 6470 if (csearch != NULL) { 6471 int c = utf_ptr2char(csearch); 6472 set_last_csearch(c, csearch, utfc_ptr2len(csearch)); 6473 } 6474 6475 dictitem_T *di = tv_dict_find(d, S_LEN("forward")); 6476 if (di != NULL) { 6477 set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD); 6478 } 6479 6480 di = tv_dict_find(d, S_LEN("until")); 6481 if (di != NULL) { 6482 set_csearch_until(!!tv_get_number(&di->di_tv)); 6483 } 6484 } 6485 6486 /// "setcursorcharpos" function 6487 static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6488 { 6489 set_cursorpos(argvars, rettv, true); 6490 } 6491 6492 /// "setenv()" function 6493 static void f_setenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6494 { 6495 char namebuf[NUMBUFLEN]; 6496 char valbuf[NUMBUFLEN]; 6497 const char *name = tv_get_string_buf(&argvars[0], namebuf); 6498 6499 // setting an environment variable may be dangerous, e.g. you could 6500 // setenv GCONV_PATH=/tmp and then have iconv() unexpectedly call 6501 // a shell command using some shared library: 6502 if (check_secure()) { 6503 return; 6504 } 6505 6506 if (argvars[1].v_type == VAR_SPECIAL 6507 && argvars[1].vval.v_special == kSpecialVarNull) { 6508 vim_unsetenv_ext(name); 6509 } else { 6510 vim_setenv_ext(name, tv_get_string_buf(&argvars[1], valbuf)); 6511 } 6512 } 6513 6514 /// "setfperm({fname}, {mode})" function 6515 static void f_setfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6516 { 6517 rettv->vval.v_number = 0; 6518 6519 const char *const fname = tv_get_string_chk(&argvars[0]); 6520 if (fname == NULL) { 6521 return; 6522 } 6523 6524 char modebuf[NUMBUFLEN]; 6525 const char *const mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); 6526 if (mode_str == NULL) { 6527 return; 6528 } 6529 if (strlen(mode_str) != 9) { 6530 semsg(_(e_invarg2), mode_str); 6531 return; 6532 } 6533 6534 int mask = 1; 6535 int mode = 0; 6536 for (int i = 8; i >= 0; i--) { 6537 if (mode_str[i] != '-') { 6538 mode |= mask; 6539 } 6540 mask = mask << 1; 6541 } 6542 rettv->vval.v_number = os_setperm(fname, mode) == OK; 6543 } 6544 6545 /// "setpos()" function 6546 static void f_setpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6547 { 6548 set_position(argvars, rettv, false); 6549 } 6550 6551 /// Translate a register type string to the yank type and block length 6552 static int get_yank_type(char **const pp, MotionType *const yank_type, int *const block_len) 6553 FUNC_ATTR_NONNULL_ALL 6554 { 6555 char *stropt = *pp; 6556 switch (*stropt) { 6557 case 'v': 6558 case 'c': // character-wise selection 6559 *yank_type = kMTCharWise; 6560 break; 6561 case 'V': 6562 case 'l': // line-wise selection 6563 *yank_type = kMTLineWise; 6564 break; 6565 case 'b': 6566 case Ctrl_V: // block-wise selection 6567 *yank_type = kMTBlockWise; 6568 if (ascii_isdigit(stropt[1])) { 6569 stropt++; 6570 *block_len = getdigits_int(&stropt, false, 0) - 1; 6571 stropt--; 6572 } 6573 break; 6574 default: 6575 return FAIL; 6576 } 6577 *pp = stropt; 6578 return OK; 6579 } 6580 6581 /// "setreg()" function 6582 static void f_setreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6583 { 6584 bool append = false; 6585 6586 int block_len = -1; 6587 MotionType yank_type = kMTUnknown; 6588 6589 rettv->vval.v_number = 1; // FAIL is default. 6590 6591 const char *const strregname = tv_get_string_chk(argvars); 6592 if (strregname == NULL) { 6593 return; // Type error; errmsg already given. 6594 } 6595 char regname = *strregname; 6596 if (regname == 0 || regname == '@') { 6597 regname = '"'; 6598 } 6599 6600 const typval_T *regcontents = NULL; 6601 char pointreg = 0; 6602 if (argvars[1].v_type == VAR_DICT) { 6603 dict_T *const d = argvars[1].vval.v_dict; 6604 6605 if (tv_dict_len(d) == 0) { 6606 // Empty dict, clear the register (like setreg(0, [])) 6607 char *lstval[2] = { NULL, NULL }; 6608 write_reg_contents_lst(regname, lstval, false, kMTUnknown, -1); 6609 return; 6610 } 6611 6612 dictitem_T *const di = tv_dict_find(d, "regcontents", -1); 6613 if (di != NULL) { 6614 regcontents = &di->di_tv; 6615 } 6616 6617 const char *stropt = tv_dict_get_string(d, "regtype", false); 6618 if (stropt != NULL) { 6619 const int ret = get_yank_type((char **)&stropt, &yank_type, &block_len); 6620 6621 if (ret == FAIL || *(++stropt) != NUL) { 6622 semsg(_(e_invargval), "value"); 6623 return; 6624 } 6625 } 6626 6627 if (regname == '"') { 6628 stropt = tv_dict_get_string(d, "points_to", false); 6629 if (stropt != NULL) { 6630 pointreg = *stropt; 6631 regname = pointreg; 6632 } 6633 } else if (tv_dict_get_number(d, "isunnamed")) { 6634 pointreg = regname; 6635 } 6636 } else { 6637 regcontents = &argvars[1]; 6638 } 6639 6640 bool set_unnamed = false; 6641 if (argvars[2].v_type != VAR_UNKNOWN) { 6642 if (yank_type != kMTUnknown) { 6643 semsg(_(e_toomanyarg), "setreg"); 6644 return; 6645 } 6646 6647 const char *stropt = tv_get_string_chk(&argvars[2]); 6648 if (stropt == NULL) { 6649 return; // Type error. 6650 } 6651 for (; *stropt != NUL; stropt++) { 6652 switch (*stropt) { 6653 case 'a': 6654 case 'A': // append 6655 append = true; 6656 break; 6657 case 'u': 6658 case '"': // unnamed register 6659 set_unnamed = true; 6660 break; 6661 default: 6662 get_yank_type((char **)&stropt, &yank_type, &block_len); 6663 } 6664 } 6665 } 6666 6667 if (regcontents != NULL && regcontents->v_type == VAR_LIST) { 6668 list_T *const ll = regcontents->vval.v_list; 6669 // If the list is NULL handle like an empty list. 6670 const int len = tv_list_len(ll); 6671 6672 // First half: use for pointers to result lines; second half: use for 6673 // pointers to allocated copies. 6674 char **lstval = xmalloc(sizeof(char *) * (((size_t)len + 1) * 2)); 6675 const char **curval = (const char **)lstval; 6676 char **allocval = lstval + len + 2; 6677 char **curallocval = allocval; 6678 6679 TV_LIST_ITER_CONST(ll, li, { 6680 char buf[NUMBUFLEN]; 6681 *curval = tv_get_string_buf_chk(TV_LIST_ITEM_TV(li), buf); 6682 if (*curval == NULL) { 6683 goto free_lstval; 6684 } 6685 if (*curval == buf) { 6686 // Need to make a copy, 6687 // next tv_get_string_buf_chk() will overwrite the string. 6688 *curallocval = xstrdup(*curval); 6689 *curval = *curallocval; 6690 curallocval++; 6691 } 6692 curval++; 6693 }); 6694 *curval++ = NULL; 6695 6696 write_reg_contents_lst(regname, lstval, append, yank_type, (colnr_T)block_len); 6697 6698 free_lstval: 6699 while (curallocval > allocval) { 6700 xfree(*--curallocval); 6701 } 6702 xfree(lstval); 6703 } else if (regcontents != NULL) { 6704 const char *const strval = tv_get_string_chk(regcontents); 6705 if (strval == NULL) { 6706 return; 6707 } 6708 write_reg_contents_ex(regname, strval, (ssize_t)strlen(strval), 6709 append, yank_type, (colnr_T)block_len); 6710 } 6711 if (pointreg != 0) { 6712 get_yank_register(pointreg, YREG_YANK); 6713 } 6714 rettv->vval.v_number = 0; 6715 6716 if (set_unnamed) { 6717 // Discard the result. We already handle the error case. 6718 op_reg_set_previous(regname); 6719 } 6720 } 6721 6722 /// "settagstack()" function 6723 static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6724 { 6725 static const char *e_invact2 = N_("E962: Invalid action: '%s'"); 6726 char action = 'r'; 6727 6728 rettv->vval.v_number = -1; 6729 6730 // first argument: window number or id 6731 win_T *wp = find_win_by_nr_or_id(&argvars[0]); 6732 if (wp == NULL) { 6733 return; 6734 } 6735 6736 // second argument: dict with items to set in the tag stack 6737 if (tv_check_for_dict_arg(argvars, 1) == FAIL) { 6738 return; 6739 } 6740 dict_T *d = argvars[1].vval.v_dict; 6741 if (d == NULL) { 6742 return; 6743 } 6744 6745 // third argument: action - 'a' for append and 'r' for replace. 6746 // default is to replace the stack. 6747 if (argvars[2].v_type == VAR_UNKNOWN) { 6748 // action = 'r'; 6749 } else if (tv_check_for_string_arg(argvars, 2) == FAIL) { 6750 return; 6751 } else { 6752 const char *actstr; 6753 actstr = tv_get_string_chk(&argvars[2]); 6754 if (actstr == NULL) { 6755 return; 6756 } 6757 if ((*actstr == 'r' || *actstr == 'a' || *actstr == 't') 6758 && actstr[1] == NUL) { 6759 action = *actstr; 6760 } else { 6761 semsg(_(e_invact2), actstr); 6762 return; 6763 } 6764 } 6765 6766 if (set_tagstack(wp, d, action) == OK) { 6767 rettv->vval.v_number = 0; 6768 } 6769 } 6770 6771 /// "sha256({expr})" function 6772 static void f_sha256(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6773 { 6774 rettv->v_type = VAR_STRING; 6775 rettv->vval.v_string = NULL; 6776 6777 if (argvars[0].v_type == VAR_BLOB) { 6778 blob_T *blob = argvars[0].vval.v_blob; 6779 const uint8_t *p = blob != NULL ? (uint8_t *)blob->bv_ga.ga_data : (uint8_t *)""; 6780 int len = blob != NULL ? blob->bv_ga.ga_len : 0; 6781 rettv->vval.v_string = xstrdup(sha256_bytes(p, (size_t)len, NULL, 0)); 6782 } else { 6783 const char *p = tv_get_string(&argvars[0]); 6784 const char *hash = sha256_bytes((const uint8_t *)p, strlen(p), NULL, 0); 6785 rettv->vval.v_string = xstrdup(hash); 6786 } 6787 } 6788 6789 /// "shellescape({string})" function 6790 static void f_shellescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6791 { 6792 const bool do_special = non_zero_arg(&argvars[1]); 6793 6794 rettv->vval.v_string = 6795 vim_strsave_shellescape(tv_get_string(&argvars[0]), do_special, do_special); 6796 rettv->v_type = VAR_STRING; 6797 } 6798 6799 /// shiftwidth() function 6800 static void f_shiftwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6801 { 6802 rettv->vval.v_number = 0; 6803 6804 if (argvars[0].v_type != VAR_UNKNOWN) { 6805 colnr_T col = (colnr_T)tv_get_number_chk(argvars, NULL); 6806 if (col < 0) { 6807 return; // type error; errmsg already given 6808 } 6809 rettv->vval.v_number = get_sw_value_col(curbuf, col, false); 6810 return; 6811 } 6812 rettv->vval.v_number = get_sw_value(curbuf); 6813 } 6814 6815 /// "sockconnect()" function 6816 static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6817 { 6818 if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) { 6819 emsg(_(e_invarg)); 6820 return; 6821 } 6822 if (argvars[2].v_type != VAR_DICT && argvars[2].v_type != VAR_UNKNOWN) { 6823 // Wrong argument types 6824 semsg(_(e_invarg2), "expected dictionary"); 6825 return; 6826 } 6827 6828 const char *mode = tv_get_string(&argvars[0]); 6829 const char *address = tv_get_string(&argvars[1]); 6830 6831 bool tcp; 6832 if (strcmp(mode, "tcp") == 0) { 6833 tcp = true; 6834 } else if (strcmp(mode, "pipe") == 0) { 6835 tcp = false; 6836 } else { 6837 semsg(_(e_invarg2), "invalid mode"); 6838 return; 6839 } 6840 6841 bool rpc = false; 6842 CallbackReader on_data = CALLBACK_READER_INIT; 6843 if (argvars[2].v_type == VAR_DICT) { 6844 dict_T *opts = argvars[2].vval.v_dict; 6845 rpc = tv_dict_get_number(opts, "rpc") != 0; 6846 6847 if (!tv_dict_get_callback(opts, S_LEN("on_data"), &on_data.cb)) { 6848 return; 6849 } 6850 on_data.buffered = tv_dict_get_number(opts, "data_buffered"); 6851 if (on_data.buffered && on_data.cb.type == kCallbackNone) { 6852 on_data.self = opts; 6853 } 6854 } 6855 6856 const char *error = NULL; 6857 uint64_t id = channel_connect(tcp, address, rpc, on_data, 50, &error); 6858 6859 if (error) { 6860 semsg(_("connection failed: %s"), error); 6861 } 6862 6863 rettv->vval.v_number = (varnumber_T)id; 6864 rettv->v_type = VAR_NUMBER; 6865 } 6866 6867 /// "stdioopen()" function 6868 static void f_stdioopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6869 { 6870 if (argvars[0].v_type != VAR_DICT) { 6871 emsg(_(e_invarg)); 6872 return; 6873 } 6874 6875 CallbackReader on_stdin = CALLBACK_READER_INIT; 6876 dict_T *opts = argvars[0].vval.v_dict; 6877 bool rpc = tv_dict_get_number(opts, "rpc") != 0; 6878 6879 if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) { 6880 return; 6881 } 6882 if (!tv_dict_get_callback(opts, S_LEN("on_print"), &on_print)) { 6883 return; 6884 } 6885 6886 on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered"); 6887 if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) { 6888 on_stdin.self = opts; 6889 } 6890 6891 const char *error; 6892 uint64_t id = channel_from_stdio(rpc, on_stdin, &error); 6893 if (!id) { 6894 semsg(e_stdiochan2, error); 6895 } 6896 6897 rettv->vval.v_number = (varnumber_T)id; 6898 rettv->v_type = VAR_NUMBER; 6899 } 6900 6901 /// "reltimefloat()" function 6902 static void f_reltimefloat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6903 FUNC_ATTR_NONNULL_ALL 6904 { 6905 proftime_T tm; 6906 6907 rettv->v_type = VAR_FLOAT; 6908 rettv->vval.v_float = 0; 6909 if (list2proftime(&argvars[0], &tm) == OK) { 6910 rettv->vval.v_float = (float_T)profile_signed(tm) / 1000000000.0; 6911 } 6912 } 6913 6914 /// "soundfold({word})" function 6915 static void f_soundfold(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6916 { 6917 rettv->v_type = VAR_STRING; 6918 const char *const s = tv_get_string(&argvars[0]); 6919 rettv->vval.v_string = eval_soundfold(s); 6920 } 6921 6922 /// "spellbadword()" function 6923 static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6924 { 6925 const int wo_spell_save = curwin->w_p_spell; 6926 6927 if (!curwin->w_p_spell) { 6928 parse_spelllang(curwin); 6929 curwin->w_p_spell = true; 6930 } 6931 6932 if (*curwin->w_s->b_p_spl == NUL) { 6933 emsg(_(e_no_spell)); 6934 curwin->w_p_spell = wo_spell_save; 6935 return; 6936 } 6937 6938 const char *word = ""; 6939 hlf_T attr = HLF_COUNT; 6940 size_t len = 0; 6941 if (argvars[0].v_type == VAR_UNKNOWN) { 6942 // Find the start and length of the badly spelled word. 6943 len = spell_move_to(curwin, FORWARD, SMT_ALL, true, &attr); 6944 if (len != 0) { 6945 word = get_cursor_pos_ptr(); 6946 curwin->w_set_curswant = true; 6947 } 6948 } else if (*curbuf->b_s.b_p_spl != NUL) { 6949 const char *str = tv_get_string_chk(&argvars[0]); 6950 int capcol = -1; 6951 6952 if (str != NULL) { 6953 // Check the argument for spelling. 6954 while (*str != NUL) { 6955 len = spell_check(curwin, (char *)str, &attr, &capcol, false); 6956 if (attr != HLF_COUNT) { 6957 word = str; 6958 break; 6959 } 6960 str += len; 6961 capcol -= (int)len; 6962 len = 0; 6963 } 6964 } 6965 } 6966 curwin->w_p_spell = wo_spell_save; 6967 6968 assert(len <= INT_MAX); 6969 tv_list_alloc_ret(rettv, 2); 6970 tv_list_append_string(rettv->vval.v_list, word, (ssize_t)len); 6971 switch (attr) { 6972 case HLF_SPB: 6973 tv_list_append_string(rettv->vval.v_list, S_LEN("bad")); break; 6974 case HLF_SPR: 6975 tv_list_append_string(rettv->vval.v_list, S_LEN("rare")); break; 6976 case HLF_SPL: 6977 tv_list_append_string(rettv->vval.v_list, S_LEN("local")); break; 6978 case HLF_SPC: 6979 tv_list_append_string(rettv->vval.v_list, S_LEN("caps")); break; 6980 default: 6981 tv_list_append_string(rettv->vval.v_list, NULL, -1); break; 6982 } 6983 } 6984 6985 /// "spellsuggest()" function 6986 static void f_spellsuggest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 6987 { 6988 garray_T ga = GA_EMPTY_INIT_VALUE; 6989 const int wo_spell_save = curwin->w_p_spell; 6990 6991 if (!curwin->w_p_spell) { 6992 parse_spelllang(curwin); 6993 curwin->w_p_spell = true; 6994 } 6995 6996 if (*curwin->w_s->b_p_spl == NUL) { 6997 emsg(_(e_no_spell)); 6998 curwin->w_p_spell = wo_spell_save; 6999 return; 7000 } 7001 7002 int maxcount; 7003 bool need_capital = false; 7004 const char *const str = tv_get_string(&argvars[0]); 7005 if (argvars[1].v_type != VAR_UNKNOWN) { 7006 bool typeerr = false; 7007 maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); 7008 if (maxcount <= 0) { 7009 goto f_spellsuggest_return; 7010 } 7011 if (argvars[2].v_type != VAR_UNKNOWN) { 7012 need_capital = tv_get_number_chk(&argvars[2], &typeerr); 7013 if (typeerr) { 7014 goto f_spellsuggest_return; 7015 } 7016 } 7017 } else { 7018 maxcount = 25; 7019 } 7020 7021 spell_suggest_list(&ga, (char *)str, maxcount, need_capital, false); 7022 7023 f_spellsuggest_return: 7024 tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len); 7025 for (int i = 0; i < ga.ga_len; i++) { 7026 char *const p = ((char **)ga.ga_data)[i]; 7027 tv_list_append_allocated_string(rettv->vval.v_list, p); 7028 } 7029 ga_clear(&ga); 7030 curwin->w_p_spell = wo_spell_save; 7031 } 7032 7033 static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7034 { 7035 colnr_T col = 0; 7036 bool keepempty = false; 7037 bool typeerr = false; 7038 7039 // Make 'cpoptions' empty, the 'l' flag should not be used here. 7040 char *save_cpo = p_cpo; 7041 p_cpo = empty_string_option; 7042 7043 const char *str = tv_get_string(&argvars[0]); 7044 const char *pat = NULL; 7045 char patbuf[NUMBUFLEN]; 7046 if (argvars[1].v_type != VAR_UNKNOWN) { 7047 pat = tv_get_string_buf_chk(&argvars[1], patbuf); 7048 if (pat == NULL) { 7049 typeerr = true; 7050 } 7051 if (argvars[2].v_type != VAR_UNKNOWN) { 7052 keepempty = (bool)tv_get_bool_chk(&argvars[2], &typeerr); 7053 } 7054 } 7055 if (pat == NULL || *pat == NUL) { 7056 pat = "[\\x01- ]\\+"; 7057 } 7058 7059 tv_list_alloc_ret(rettv, kListLenMayKnow); 7060 7061 if (typeerr) { 7062 goto theend; 7063 } 7064 7065 regmatch_T regmatch = { 7066 .regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING), 7067 .startp = { NULL }, 7068 .endp = { NULL }, 7069 .rm_ic = false, 7070 }; 7071 if (regmatch.regprog != NULL) { 7072 while (*str != NUL || keepempty) { 7073 bool match; 7074 if (*str == NUL) { 7075 match = false; // Empty item at the end. 7076 } else { 7077 match = vim_regexec_nl(®match, str, col); 7078 } 7079 const char *end; 7080 if (match) { 7081 end = regmatch.startp[0]; 7082 } else { 7083 end = str + strlen(str); 7084 } 7085 if (keepempty || end > str || (tv_list_len(rettv->vval.v_list) > 0 7086 && *str != NUL 7087 && match 7088 && end < regmatch.endp[0])) { 7089 tv_list_append_string(rettv->vval.v_list, str, end - str); 7090 } 7091 if (!match) { 7092 break; 7093 } 7094 // Advance to just after the match. 7095 if (regmatch.endp[0] > str) { 7096 col = 0; 7097 } else { 7098 // Don't get stuck at the same match. 7099 col = utfc_ptr2len(regmatch.endp[0]); 7100 } 7101 str = regmatch.endp[0]; 7102 } 7103 7104 vim_regfree(regmatch.regprog); 7105 } 7106 7107 theend: 7108 p_cpo = save_cpo; 7109 } 7110 7111 /// "stdpath()" helper for list results 7112 static void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) 7113 FUNC_ATTR_NONNULL_ALL 7114 { 7115 list_T *const list = tv_list_alloc(kListLenShouldKnow); 7116 rettv->v_type = VAR_LIST; 7117 rettv->vval.v_list = list; 7118 tv_list_ref(list); 7119 char *const dirs = stdpaths_get_xdg_var(xdg); 7120 if (dirs == NULL) { 7121 return; 7122 } 7123 const void *iter = NULL; 7124 const char *appname = get_appname(false); 7125 do { 7126 size_t dir_len; 7127 const char *dir; 7128 iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len); 7129 if (dir != NULL && dir_len > 0) { 7130 char *dir_with_nvim = xmemdupz(dir, dir_len); 7131 dir_with_nvim = concat_fnames_realloc(dir_with_nvim, appname, true); 7132 tv_list_append_allocated_string(list, dir_with_nvim); 7133 } 7134 } while (iter != NULL); 7135 xfree(dirs); 7136 } 7137 7138 /// "stdpath(type)" function 7139 static void f_stdpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7140 { 7141 rettv->v_type = VAR_STRING; 7142 rettv->vval.v_string = NULL; 7143 7144 const char *const p = tv_get_string_chk(&argvars[0]); 7145 if (p == NULL) { 7146 return; // Type error; errmsg already given. 7147 } 7148 7149 if (strequal(p, "config")) { 7150 rettv->vval.v_string = get_xdg_home(kXDGConfigHome); 7151 } else if (strequal(p, "data")) { 7152 rettv->vval.v_string = get_xdg_home(kXDGDataHome); 7153 } else if (strequal(p, "cache")) { 7154 rettv->vval.v_string = get_xdg_home(kXDGCacheHome); 7155 } else if (strequal(p, "state")) { 7156 rettv->vval.v_string = get_xdg_home(kXDGStateHome); 7157 } else if (strequal(p, "log")) { 7158 rettv->vval.v_string = get_xdg_home(kXDGStateHome); 7159 } else if (strequal(p, "run")) { 7160 rettv->vval.v_string = stdpaths_get_xdg_var(kXDGRuntimeDir); 7161 } else if (strequal(p, "config_dirs")) { 7162 get_xdg_var_list(kXDGConfigDirs, rettv); 7163 } else if (strequal(p, "data_dirs")) { 7164 get_xdg_var_list(kXDGDataDirs, rettv); 7165 } else { 7166 semsg(_("E6100: \"%s\" is not a valid stdpath"), p); 7167 } 7168 } 7169 7170 /// "str2float()" function 7171 static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7172 { 7173 char *p = skipwhite(tv_get_string(&argvars[0])); 7174 bool isneg = (*p == '-'); 7175 7176 if (*p == '+' || *p == '-') { 7177 p = skipwhite(p + 1); 7178 } 7179 string2float(p, &rettv->vval.v_float); 7180 if (isneg) { 7181 rettv->vval.v_float *= -1; 7182 } 7183 rettv->v_type = VAR_FLOAT; 7184 } 7185 7186 /// "strftime({format}[, {time}])" function 7187 static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7188 { 7189 time_t seconds; 7190 7191 rettv->v_type = VAR_STRING; 7192 7193 char *p = (char *)tv_get_string(&argvars[0]); 7194 if (argvars[1].v_type == VAR_UNKNOWN) { 7195 seconds = time(NULL); 7196 } else { 7197 seconds = (time_t)tv_get_number(&argvars[1]); 7198 } 7199 7200 struct tm curtime; 7201 struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime); 7202 // MSVC returns NULL for an invalid value of seconds. 7203 if (curtime_ptr == NULL) { 7204 rettv->vval.v_string = xstrdup(_("(Invalid)")); 7205 return; 7206 } 7207 7208 vimconv_T conv; 7209 7210 conv.vc_type = CONV_NONE; 7211 char *enc = enc_locale(); 7212 convert_setup(&conv, p_enc, enc); 7213 if (conv.vc_type != CONV_NONE) { 7214 p = string_convert(&conv, p, NULL); 7215 } 7216 char result_buf[256]; 7217 if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) { 7218 result_buf[0] = NUL; 7219 } 7220 7221 if (conv.vc_type != CONV_NONE) { 7222 xfree(p); 7223 } 7224 convert_setup(&conv, enc, p_enc); 7225 if (conv.vc_type != CONV_NONE) { 7226 rettv->vval.v_string = string_convert(&conv, result_buf, NULL); 7227 } else { 7228 rettv->vval.v_string = xstrdup(result_buf); 7229 } 7230 7231 // Release conversion descriptors. 7232 convert_setup(&conv, NULL, NULL); 7233 xfree(enc); 7234 } 7235 7236 /// "strptime({format}, {timestring})" function 7237 static void f_strptime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7238 { 7239 char fmt_buf[NUMBUFLEN]; 7240 char str_buf[NUMBUFLEN]; 7241 7242 struct tm tmval = { 7243 .tm_isdst = -1, 7244 }; 7245 char *fmt = (char *)tv_get_string_buf(&argvars[0], fmt_buf); 7246 char *str = (char *)tv_get_string_buf(&argvars[1], str_buf); 7247 7248 vimconv_T conv = { 7249 .vc_type = CONV_NONE, 7250 }; 7251 char *enc = enc_locale(); 7252 convert_setup(&conv, p_enc, enc); 7253 if (conv.vc_type != CONV_NONE) { 7254 fmt = string_convert(&conv, fmt, NULL); 7255 } 7256 if (fmt == NULL 7257 || os_strptime(str, fmt, &tmval) == NULL 7258 || (rettv->vval.v_number = mktime(&tmval)) == -1) { 7259 rettv->vval.v_number = 0; 7260 } 7261 if (conv.vc_type != CONV_NONE) { 7262 xfree(fmt); 7263 } 7264 convert_setup(&conv, NULL, NULL); 7265 xfree(enc); 7266 } 7267 7268 /// "submatch()" function 7269 static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7270 { 7271 bool error = false; 7272 int no = (int)tv_get_number_chk(&argvars[0], &error); 7273 if (error) { 7274 return; 7275 } 7276 7277 if (no < 0 || no >= NSUBEXP) { 7278 semsg(_(e_invalid_submatch_number_nr), no); 7279 return; 7280 } 7281 int retList = 0; 7282 7283 if (argvars[1].v_type != VAR_UNKNOWN) { 7284 retList = (int)tv_get_number_chk(&argvars[1], &error); 7285 if (error) { 7286 return; 7287 } 7288 } 7289 7290 if (retList == 0) { 7291 rettv->v_type = VAR_STRING; 7292 rettv->vval.v_string = reg_submatch(no); 7293 } else { 7294 rettv->v_type = VAR_LIST; 7295 rettv->vval.v_list = reg_submatch_list(no); 7296 } 7297 } 7298 7299 /// "substitute()" function 7300 static void f_substitute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7301 { 7302 char patbuf[NUMBUFLEN]; 7303 char subbuf[NUMBUFLEN]; 7304 char flagsbuf[NUMBUFLEN]; 7305 7306 const char *const str = tv_get_string_chk(&argvars[0]); 7307 const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); 7308 const char *sub = NULL; 7309 const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); 7310 7311 typval_T *expr = NULL; 7312 if (tv_is_func(argvars[2])) { 7313 expr = &argvars[2]; 7314 } else { 7315 sub = tv_get_string_buf_chk(&argvars[2], subbuf); 7316 } 7317 7318 rettv->v_type = VAR_STRING; 7319 if (str == NULL || pat == NULL || (sub == NULL && expr == NULL) 7320 || flg == NULL) { 7321 rettv->vval.v_string = NULL; 7322 } else { 7323 rettv->vval.v_string = do_string_sub((char *)str, strlen(str), (char *)pat, 7324 (char *)sub, expr, (char *)flg, NULL); 7325 } 7326 } 7327 7328 /// "swapfilelist()" function 7329 static void f_swapfilelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7330 { 7331 tv_list_alloc_ret(rettv, kListLenUnknown); 7332 recover_names(NULL, false, rettv->vval.v_list, 0, NULL); 7333 } 7334 7335 /// "swapinfo(swap_filename)" function 7336 static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7337 { 7338 tv_dict_alloc_ret(rettv); 7339 swapfile_dict(tv_get_string(argvars), rettv->vval.v_dict); 7340 } 7341 7342 /// "swapname(expr)" function 7343 static void f_swapname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7344 { 7345 rettv->v_type = VAR_STRING; 7346 buf_T *buf = tv_get_buf(&argvars[0], false); 7347 if (buf == NULL 7348 || buf->b_ml.ml_mfp == NULL 7349 || buf->b_ml.ml_mfp->mf_fname == NULL) { 7350 rettv->vval.v_string = NULL; 7351 } else { 7352 rettv->vval.v_string = xstrdup(buf->b_ml.ml_mfp->mf_fname); 7353 } 7354 } 7355 7356 /// "synID(lnum, col, trans)" function 7357 static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7358 { 7359 // -1 on type error (both) 7360 const linenr_T lnum = tv_get_lnum(argvars); 7361 const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; 7362 7363 bool transerr = false; 7364 const int trans = (int)tv_get_number_chk(&argvars[2], &transerr); 7365 7366 int id = 0; 7367 if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count 7368 && col >= 0 && col < ml_get_len(lnum)) { 7369 id = syn_get_id(curwin, lnum, col, trans, NULL, false); 7370 } 7371 7372 rettv->vval.v_number = id; 7373 } 7374 7375 /// "synIDattr(id, what [, mode])" function 7376 static void f_synIDattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7377 { 7378 const int id = (int)tv_get_number(&argvars[0]); 7379 const char *const what = tv_get_string(&argvars[1]); 7380 int modec; 7381 if (argvars[2].v_type != VAR_UNKNOWN) { 7382 char modebuf[NUMBUFLEN]; 7383 const char *const mode = tv_get_string_buf(&argvars[2], modebuf); 7384 modec = TOLOWER_ASC(mode[0]); 7385 if (modec != 'c' && modec != 'g') { 7386 modec = 0; // Replace invalid with current. 7387 } 7388 } else if (ui_rgb_attached()) { 7389 modec = 'g'; 7390 } else { 7391 modec = 'c'; 7392 } 7393 7394 const char *p = NULL; 7395 switch (TOLOWER_ASC(what[0])) { 7396 case 'b': 7397 if (TOLOWER_ASC(what[1]) == 'g') { // bg[#] 7398 p = highlight_color(id, what, modec); 7399 } else if (TOLOWER_ASC(what[1]) == 'l') { // blink 7400 p = highlight_has_attr(id, HL_BLINK, modec); 7401 } else { // bold 7402 p = highlight_has_attr(id, HL_BOLD, modec); 7403 } 7404 break; 7405 case 'c': // conceal 7406 p = highlight_has_attr(id, HL_CONCEALED, modec); 7407 break; 7408 case 'd': // dim 7409 p = highlight_has_attr(id, HL_DIM, modec); 7410 break; 7411 case 'o': // overline 7412 p = highlight_has_attr(id, HL_OVERLINE, modec); 7413 break; 7414 case 'f': // fg[#] or font 7415 p = highlight_color(id, what, modec); 7416 break; 7417 case 'i': 7418 if (TOLOWER_ASC(what[1]) == 'n') { // inverse 7419 p = highlight_has_attr(id, HL_INVERSE, modec); 7420 } else { // italic 7421 p = highlight_has_attr(id, HL_ITALIC, modec); 7422 } 7423 break; 7424 case 'n': 7425 if (TOLOWER_ASC(what[1]) == 'o') { // nocombine 7426 p = highlight_has_attr(id, HL_NOCOMBINE, modec); 7427 } else { // name 7428 p = get_highlight_name_ext(NULL, id - 1, false); 7429 } 7430 break; 7431 case 'r': // reverse 7432 p = highlight_has_attr(id, HL_INVERSE, modec); 7433 break; 7434 case 's': 7435 if (TOLOWER_ASC(what[1]) == 'p') { // sp[#] 7436 p = highlight_color(id, what, modec); 7437 } else if (TOLOWER_ASC(what[1]) == 't' 7438 && TOLOWER_ASC(what[2]) == 'r') { // strikethrough 7439 p = highlight_has_attr(id, HL_STRIKETHROUGH, modec); 7440 } else { // standout 7441 p = highlight_has_attr(id, HL_STANDOUT, modec); 7442 } 7443 break; 7444 case 'u': 7445 if (strlen(what) >= 9) { 7446 if (TOLOWER_ASC(what[5]) == 'l') { 7447 // underline 7448 p = highlight_has_attr(id, HL_UNDERLINE, modec); 7449 } else if (TOLOWER_ASC(what[5]) != 'd') { 7450 // undercurl 7451 p = highlight_has_attr(id, HL_UNDERCURL, modec); 7452 } else if (TOLOWER_ASC(what[6]) != 'o') { 7453 // underdashed 7454 p = highlight_has_attr(id, HL_UNDERDASHED, modec); 7455 } else if (TOLOWER_ASC(what[7]) == 'u') { 7456 // underdouble 7457 p = highlight_has_attr(id, HL_UNDERDOUBLE, modec); 7458 } else { 7459 // underdotted 7460 p = highlight_has_attr(id, HL_UNDERDOTTED, modec); 7461 } 7462 } else { 7463 // ul 7464 p = highlight_color(id, what, modec); 7465 } 7466 break; 7467 } 7468 7469 rettv->v_type = VAR_STRING; 7470 rettv->vval.v_string = p == NULL ? NULL : xstrdup(p); 7471 } 7472 7473 /// "synIDtrans(id)" function 7474 static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7475 { 7476 int id = (int)tv_get_number(&argvars[0]); 7477 7478 if (id > 0) { 7479 id = syn_get_final_id(id); 7480 } else { 7481 id = 0; 7482 } 7483 7484 rettv->vval.v_number = id; 7485 } 7486 7487 /// "synconcealed(lnum, col)" function 7488 static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7489 { 7490 int syntax_flags = 0; 7491 int matchid = 0; 7492 char str[NUMBUFLEN]; 7493 7494 tv_list_set_ret(rettv, NULL); 7495 7496 // -1 on type error (both) 7497 const linenr_T lnum = tv_get_lnum(argvars); 7498 const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; 7499 7500 CLEAR_FIELD(str); 7501 7502 if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count 7503 && col >= 0 && col <= ml_get_len(lnum) && curwin->w_p_cole > 0) { 7504 syn_get_id(curwin, lnum, col, false, NULL, false); 7505 syntax_flags = get_syntax_info(&matchid); 7506 7507 // get the conceal character 7508 if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { 7509 schar_T cchar = schar_from_char(syn_get_sub_char()); 7510 if (cchar == NUL && curwin->w_p_cole == 1) { 7511 cchar = (curwin->w_p_lcs_chars.conceal == NUL) 7512 ? schar_from_ascii(' ') : curwin->w_p_lcs_chars.conceal; 7513 } 7514 if (cchar != NUL) { 7515 schar_get(str, cchar); 7516 } 7517 } 7518 } 7519 7520 tv_list_alloc_ret(rettv, 3); 7521 tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); 7522 // -1 to auto-determine strlen 7523 tv_list_append_string(rettv->vval.v_list, str, -1); 7524 tv_list_append_number(rettv->vval.v_list, matchid); 7525 } 7526 7527 /// "synstack(lnum, col)" function 7528 static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7529 { 7530 tv_list_set_ret(rettv, NULL); 7531 7532 // -1 on type error (both) 7533 const linenr_T lnum = tv_get_lnum(argvars); 7534 const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; 7535 7536 if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count 7537 && col >= 0 && col <= ml_get_len(lnum)) { 7538 tv_list_alloc_ret(rettv, kListLenMayKnow); 7539 syn_get_id(curwin, lnum, col, false, NULL, true); 7540 7541 int id; 7542 int i = 0; 7543 while ((id = syn_get_stack_item(i++)) >= 0) { 7544 tv_list_append_number(rettv->vval.v_list, id); 7545 } 7546 } 7547 } 7548 7549 /// "tabpagebuflist()" function 7550 static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7551 { 7552 win_T *wp = NULL; 7553 7554 if (argvars[0].v_type == VAR_UNKNOWN) { 7555 wp = firstwin; 7556 } else { 7557 tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); 7558 if (tp != NULL) { 7559 wp = (tp == curtab) ? firstwin : tp->tp_firstwin; 7560 } 7561 } 7562 if (wp != NULL) { 7563 tv_list_alloc_ret(rettv, kListLenMayKnow); 7564 while (wp != NULL) { 7565 tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); 7566 wp = wp->w_next; 7567 } 7568 } 7569 } 7570 7571 /// "tagfiles()" function 7572 static void f_tagfiles(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7573 { 7574 tv_list_alloc_ret(rettv, kListLenUnknown); 7575 char *fname = xmalloc(MAXPATHL); 7576 7577 bool first = true; 7578 tagname_T tn; 7579 while (get_tagfname(&tn, first, fname) == OK) { 7580 tv_list_append_string(rettv->vval.v_list, fname, -1); 7581 first = false; 7582 } 7583 7584 tagname_free(&tn); 7585 xfree(fname); 7586 } 7587 7588 /// "taglist()" function 7589 static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7590 { 7591 const char *const tag_pattern = tv_get_string(&argvars[0]); 7592 7593 rettv->vval.v_number = false; 7594 if (*tag_pattern == NUL) { 7595 return; 7596 } 7597 7598 const char *fname = NULL; 7599 if (argvars[1].v_type != VAR_UNKNOWN) { 7600 fname = tv_get_string(&argvars[1]); 7601 } 7602 get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), 7603 (char *)tag_pattern, (char *)fname); 7604 } 7605 7606 /// "timer_info([timer])" function 7607 static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7608 { 7609 tv_list_alloc_ret(rettv, kListLenUnknown); 7610 7611 if (tv_check_for_opt_number_arg(argvars, 0) == FAIL) { 7612 return; 7613 } 7614 7615 if (argvars[0].v_type != VAR_UNKNOWN) { 7616 timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0])); 7617 if (timer != NULL && (!timer->stopped || timer->refcount > 1)) { 7618 add_timer_info(rettv, timer); 7619 } 7620 } else { 7621 add_timer_info_all(rettv); 7622 } 7623 } 7624 7625 /// "timer_pause(timer, paused)" function 7626 static void f_timer_pause(typval_T *argvars, typval_T *unused, EvalFuncData fptr) 7627 { 7628 if (argvars[0].v_type != VAR_NUMBER) { 7629 emsg(_(e_number_exp)); 7630 return; 7631 } 7632 7633 int paused = (bool)tv_get_number(&argvars[1]); 7634 timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0])); 7635 if (timer != NULL) { 7636 if (!timer->paused && paused) { 7637 time_watcher_stop(&timer->tw); 7638 } else if (timer->paused && !paused) { 7639 time_watcher_start(&timer->tw, timer_due_cb, (uint64_t)timer->timeout, 7640 (uint64_t)timer->timeout); 7641 } 7642 timer->paused = paused; 7643 } 7644 } 7645 7646 /// "timer_start(timeout, callback, opts)" function 7647 static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7648 { 7649 int repeat = 1; 7650 7651 rettv->vval.v_number = -1; 7652 if (check_secure()) { 7653 return; 7654 } 7655 7656 if (argvars[2].v_type != VAR_UNKNOWN) { 7657 if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) { 7658 return; 7659 } 7660 dict_T *dict = argvars[2].vval.v_dict; 7661 dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); 7662 if (di != NULL) { 7663 repeat = (int)tv_get_number(&di->di_tv); 7664 if (repeat == 0) { 7665 repeat = 1; 7666 } 7667 } 7668 } 7669 7670 Callback callback; 7671 if (!callback_from_typval(&callback, &argvars[1])) { 7672 return; 7673 } 7674 rettv->vval.v_number = (varnumber_T)timer_start(tv_get_number(&argvars[0]), repeat, &callback); 7675 } 7676 7677 /// "timer_stop(timerid)" function 7678 static void f_timer_stop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7679 { 7680 if (tv_check_for_number_arg(argvars, 0) == FAIL) { 7681 return; 7682 } 7683 7684 timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0])); 7685 if (timer == NULL) { 7686 return; 7687 } 7688 7689 timer_stop(timer); 7690 } 7691 7692 static void f_timer_stopall(typval_T *argvars, typval_T *unused, EvalFuncData fptr) 7693 { 7694 timer_stop_all(); 7695 } 7696 7697 /// "type(expr)" function 7698 static void f_type(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7699 { 7700 int n = -1; 7701 7702 switch (argvars[0].v_type) { 7703 case VAR_NUMBER: 7704 n = VAR_TYPE_NUMBER; break; 7705 case VAR_STRING: 7706 n = VAR_TYPE_STRING; break; 7707 case VAR_PARTIAL: 7708 case VAR_FUNC: 7709 n = VAR_TYPE_FUNC; break; 7710 case VAR_LIST: 7711 n = VAR_TYPE_LIST; break; 7712 case VAR_DICT: 7713 n = VAR_TYPE_DICT; break; 7714 case VAR_FLOAT: 7715 n = VAR_TYPE_FLOAT; break; 7716 case VAR_BOOL: 7717 n = VAR_TYPE_BOOL; break; 7718 case VAR_SPECIAL: 7719 n = VAR_TYPE_SPECIAL; break; 7720 case VAR_BLOB: 7721 n = VAR_TYPE_BLOB; break; 7722 case VAR_UNKNOWN: 7723 internal_error("f_type(UNKNOWN)"); 7724 break; 7725 } 7726 rettv->vval.v_number = n; 7727 } 7728 7729 /// "virtcol({expr}, [, {list} [, {winid}]])" function 7730 static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7731 { 7732 colnr_T vcol_start = 0; 7733 colnr_T vcol_end = 0; 7734 win_T *wp = curwin; 7735 7736 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { 7737 // use the window specified in the third argument 7738 tabpage_T *tp; 7739 wp = win_id2wp_tp((int)tv_get_number(&argvars[2]), &tp); 7740 if (wp == NULL || tp == NULL) { 7741 goto theend; 7742 } 7743 check_cursor(wp); 7744 } 7745 7746 buf_T *bp = wp->w_buffer; 7747 int fnum = bp->b_fnum; 7748 pos_T *fp = var2fpos(&argvars[0], false, &fnum, false, wp); 7749 if (fp != NULL && fp->lnum <= bp->b_ml.ml_line_count 7750 && fnum == bp->b_fnum) { 7751 // Limit the column to a valid value, getvvcol() doesn't check. 7752 if (fp->col < 0) { 7753 fp->col = 0; 7754 } else { 7755 const colnr_T len = ml_get_buf_len(bp, fp->lnum); 7756 if (fp->col > len) { 7757 fp->col = len; 7758 } 7759 } 7760 getvvcol(wp, fp, &vcol_start, NULL, &vcol_end); 7761 vcol_start++; 7762 vcol_end++; 7763 } 7764 7765 theend: 7766 if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1])) { 7767 tv_list_alloc_ret(rettv, 2); 7768 tv_list_append_number(rettv->vval.v_list, vcol_start); 7769 tv_list_append_number(rettv->vval.v_list, vcol_end); 7770 } else { 7771 rettv->vval.v_number = vcol_end; 7772 } 7773 } 7774 7775 /// "visualmode()" function 7776 static void f_visualmode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7777 { 7778 char str[2]; 7779 7780 rettv->v_type = VAR_STRING; 7781 str[0] = (char)curbuf->b_visual_mode_eval; 7782 str[1] = NUL; 7783 rettv->vval.v_string = xstrdup(str); 7784 7785 // A non-zero number or non-empty string argument: reset mode. 7786 if (non_zero_arg(&argvars[0])) { 7787 curbuf->b_visual_mode_eval = NUL; 7788 } 7789 } 7790 7791 /// "wildmenumode()" function 7792 static void f_wildmenumode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7793 { 7794 if (wild_menu_showing || ((State & MODE_CMDLINE) && cmdline_pum_active())) { 7795 rettv->vval.v_number = 1; 7796 } 7797 } 7798 7799 /// "windowsversion()" function 7800 static void f_windowsversion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7801 { 7802 rettv->v_type = VAR_STRING; 7803 rettv->vval.v_string = xstrdup(windowsVersion); 7804 } 7805 7806 /// "wordcount()" function 7807 static void f_wordcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7808 { 7809 tv_dict_alloc_ret(rettv); 7810 cursor_pos_info(rettv->vval.v_dict); 7811 } 7812 7813 /// "xor(expr, expr)" function 7814 static void f_xor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 7815 { 7816 rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) 7817 ^ tv_get_number_chk(&argvars[1], NULL); 7818 }