list.c (23782B)
1 // eval/list.c: List support and container (List, Dict, Blob) functions. 2 3 #include "nvim/errors.h" 4 #include "nvim/eval.h" 5 #include "nvim/eval/list.h" 6 #include "nvim/eval/typval.h" 7 #include "nvim/eval/vars.h" 8 #include "nvim/ex_docmd.h" 9 #include "nvim/garray.h" 10 #include "nvim/globals.h" 11 #include "nvim/mbyte.h" 12 #include "nvim/strings.h" 13 #include "nvim/vim_defs.h" 14 15 /// Enum used by filter(), map(), mapnew() and foreach() 16 typedef enum { 17 FILTERMAP_FILTER, 18 FILTERMAP_MAP, 19 FILTERMAP_MAPNEW, 20 FILTERMAP_FOREACH, 21 } filtermap_T; 22 23 #include "eval/list.c.generated.h" 24 25 static const char e_argument_of_str_must_be_list_string_or_dictionary[] 26 = N_("E706: Argument of %s must be a List, String or Dictionary"); 27 static const char e_argument_of_str_must_be_list_string_dictionary_or_blob[] 28 = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); 29 30 /// Handle one item for map(), filter(), foreach(). 31 /// Sets v:val to "tv". Caller must set v:key. 32 /// 33 /// @param tv original value 34 /// @param expr callback 35 /// @param newtv for map() an mapnew(): new value 36 /// @param remp for filter(): remove flag 37 static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filtermap, 38 typval_T *newtv, bool *remp) 39 FUNC_ATTR_NONNULL_ALL 40 { 41 typval_T argv[3]; 42 int retval = FAIL; 43 44 tv_copy(tv, get_vim_var_tv(VV_VAL)); 45 46 newtv->v_type = VAR_UNKNOWN; 47 if (filtermap == FILTERMAP_FOREACH && expr->v_type == VAR_STRING) { 48 // foreach() is not limited to an expression 49 do_cmdline_cmd(expr->vval.v_string); 50 if (!did_emsg) { 51 retval = OK; 52 } 53 goto theend; 54 } 55 56 argv[0] = *get_vim_var_tv(VV_KEY); 57 argv[1] = *get_vim_var_tv(VV_VAL); 58 if (eval_expr_typval(expr, false, argv, 2, newtv) == FAIL) { 59 goto theend; 60 } 61 if (filtermap == FILTERMAP_FILTER) { 62 bool error = false; 63 64 // filter(): when expr is zero remove the item 65 *remp = (tv_get_number_chk(newtv, &error) == 0); 66 tv_clear(newtv); 67 // On type error, nothing has been removed; return FAIL to stop the 68 // loop. The error message was given by tv_get_number_chk(). 69 if (error) { 70 goto theend; 71 } 72 } else if (filtermap == FILTERMAP_FOREACH) { 73 tv_clear(newtv); 74 } 75 retval = OK; 76 theend: 77 tv_clear(get_vim_var_tv(VV_VAL)); 78 return retval; 79 } 80 81 /// Implementation of map(), filter(), foreach() for a Dict. Apply "expr" to 82 /// every item in Dict "d" and return the result in "rettv". 83 static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name, 84 const char *arg_errmsg, typval_T *expr, typval_T *rettv) 85 { 86 if (filtermap == FILTERMAP_MAPNEW) { 87 rettv->v_type = VAR_DICT; 88 rettv->vval.v_dict = NULL; 89 } 90 if (d == NULL 91 || (filtermap == FILTERMAP_FILTER 92 && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { 93 return; 94 } 95 96 dict_T *d_ret = NULL; 97 98 if (filtermap == FILTERMAP_MAPNEW) { 99 tv_dict_alloc_ret(rettv); 100 d_ret = rettv->vval.v_dict; 101 } 102 103 const VarLockStatus prev_lock = d->dv_lock; 104 if (d->dv_lock == VAR_UNLOCKED) { 105 d->dv_lock = VAR_LOCKED; 106 } 107 hash_lock(&d->dv_hashtab); 108 TV_DICT_ITER(d, di, { 109 if (filtermap == FILTERMAP_MAP 110 && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) 111 || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { 112 break; 113 } 114 set_vim_var_string(VV_KEY, di->di_key, -1); 115 typval_T newtv; 116 bool rem; 117 int r = filter_map_one(&di->di_tv, expr, filtermap, &newtv, &rem); 118 tv_clear(get_vim_var_tv(VV_KEY)); 119 if (r == FAIL || did_emsg) { 120 tv_clear(&newtv); 121 break; 122 } 123 if (filtermap == FILTERMAP_MAP) { 124 // map(): replace the dict item value 125 tv_clear(&di->di_tv); 126 newtv.v_lock = VAR_UNLOCKED; 127 di->di_tv = newtv; 128 } else if (filtermap == FILTERMAP_MAPNEW) { 129 // mapnew(): add the item value to the new dict 130 r = tv_dict_add_tv(d_ret, di->di_key, strlen(di->di_key), &newtv); 131 tv_clear(&newtv); 132 if (r == FAIL) { 133 break; 134 } 135 } else if (filtermap == FILTERMAP_FILTER && rem) { 136 // filter(false): remove the item from the dict 137 if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) 138 || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { 139 break; 140 } 141 tv_dict_item_remove(d, di); 142 } 143 }); 144 hash_unlock(&d->dv_hashtab); 145 d->dv_lock = prev_lock; 146 } 147 148 /// Implementation of map(), filter(), foreach() for a Blob. 149 static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, 150 const char *arg_errmsg, typval_T *rettv) 151 { 152 if (filtermap == FILTERMAP_MAPNEW) { 153 rettv->v_type = VAR_BLOB; 154 rettv->vval.v_blob = NULL; 155 } 156 blob_T *b = blob_arg; 157 if (b == NULL 158 || (filtermap == FILTERMAP_FILTER 159 && value_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE))) { 160 return; 161 } 162 163 blob_T *b_ret = b; 164 165 if (filtermap == FILTERMAP_MAPNEW) { 166 tv_blob_copy(b, rettv); 167 b_ret = rettv->vval.v_blob; 168 } 169 170 // set_vim_var_nr() doesn't set the type 171 set_vim_var_type(VV_KEY, VAR_NUMBER); 172 173 const VarLockStatus prev_lock = b->bv_lock; 174 if (b->bv_lock == 0) { 175 b->bv_lock = VAR_LOCKED; 176 } 177 178 for (int i = 0, idx = 0; i < b->bv_ga.ga_len; i++) { 179 const varnumber_T val = tv_blob_get(b, i); 180 typval_T tv = { 181 .v_type = VAR_NUMBER, 182 .v_lock = VAR_UNLOCKED, 183 .vval.v_number = val, 184 }; 185 set_vim_var_nr(VV_KEY, idx); 186 typval_T newtv; 187 bool rem; 188 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL 189 || did_emsg) { 190 break; 191 } 192 if (filtermap != FILTERMAP_FOREACH) { 193 if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { 194 tv_clear(&newtv); 195 emsg(_(e_invalblob)); 196 break; 197 } 198 if (filtermap != FILTERMAP_FILTER) { 199 if (newtv.vval.v_number != val) { 200 tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); 201 } 202 } else if (rem) { 203 char *const p = (char *)blob_arg->bv_ga.ga_data; 204 memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); 205 b->bv_ga.ga_len--; 206 i--; 207 } 208 } 209 idx++; 210 } 211 212 b->bv_lock = prev_lock; 213 } 214 215 /// Implementation of map(), filter(), foreach() for a String. 216 static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr, 217 typval_T *rettv) 218 { 219 rettv->v_type = VAR_STRING; 220 rettv->vval.v_string = NULL; 221 222 // set_vim_var_nr() doesn't set the type 223 set_vim_var_type(VV_KEY, VAR_NUMBER); 224 225 garray_T ga; 226 ga_init(&ga, (int)sizeof(char), 80); 227 int len = 0; 228 int idx = 0; 229 for (const char *p = str; *p != NUL; p += len) { 230 len = utfc_ptr2len(p); 231 typval_T tv = { 232 .v_type = VAR_STRING, 233 .v_lock = VAR_UNLOCKED, 234 .vval.v_string = xmemdupz(p, (size_t)len), 235 }; 236 237 set_vim_var_nr(VV_KEY, idx); 238 typval_T newtv = { 239 .v_type = VAR_UNKNOWN, 240 }; 241 bool rem; 242 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL 243 || did_emsg) { 244 tv_clear(&newtv); 245 tv_clear(&tv); 246 break; 247 } 248 if (filtermap == FILTERMAP_MAP || filtermap == FILTERMAP_MAPNEW) { 249 if (newtv.v_type != VAR_STRING) { 250 tv_clear(&newtv); 251 tv_clear(&tv); 252 emsg(_(e_string_required)); 253 break; 254 } else { 255 ga_concat(&ga, newtv.vval.v_string); 256 } 257 } else if (filtermap == FILTERMAP_FOREACH || !rem) { 258 ga_concat(&ga, tv.vval.v_string); 259 } 260 261 tv_clear(&newtv); 262 tv_clear(&tv); 263 264 idx++; 265 } 266 ga_append(&ga, NUL); 267 rettv->vval.v_string = ga.ga_data; 268 } 269 270 /// Implementation of map(), filter(), foreach() for a List. Apply "expr" to 271 /// every item in List "l" and return the result in "rettv". 272 static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name, 273 const char *arg_errmsg, typval_T *expr, typval_T *rettv) 274 { 275 if (filtermap == FILTERMAP_MAPNEW) { 276 rettv->v_type = VAR_LIST; 277 rettv->vval.v_list = NULL; 278 } 279 if (l == NULL 280 || (filtermap == FILTERMAP_FILTER 281 && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { 282 return; 283 } 284 285 list_T *l_ret = NULL; 286 287 if (filtermap == FILTERMAP_MAPNEW) { 288 tv_list_alloc_ret(rettv, kListLenUnknown); 289 l_ret = rettv->vval.v_list; 290 } 291 // set_vim_var_nr() doesn't set the type 292 set_vim_var_type(VV_KEY, VAR_NUMBER); 293 294 const VarLockStatus prev_lock = tv_list_locked(l); 295 if (tv_list_locked(l) == VAR_UNLOCKED) { 296 tv_list_set_lock(l, VAR_LOCKED); 297 } 298 299 int idx = 0; 300 for (listitem_T *li = tv_list_first(l); li != NULL;) { 301 if (filtermap == FILTERMAP_MAP 302 && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, TV_TRANSLATE)) { 303 break; 304 } 305 set_vim_var_nr(VV_KEY, idx); 306 typval_T newtv; 307 bool rem; 308 if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL) { 309 break; 310 } 311 if (did_emsg) { 312 tv_clear(&newtv); 313 break; 314 } 315 if (filtermap == FILTERMAP_MAP) { 316 // map(): replace the list item value 317 tv_clear(TV_LIST_ITEM_TV(li)); 318 newtv.v_lock = VAR_UNLOCKED; 319 *TV_LIST_ITEM_TV(li) = newtv; 320 } else if (filtermap == FILTERMAP_MAPNEW) { 321 // mapnew(): append the list item value 322 tv_list_append_owned_tv(l_ret, newtv); 323 } 324 if (filtermap == FILTERMAP_FILTER && rem) { 325 li = tv_list_item_remove(l, li); 326 } else { 327 li = TV_LIST_ITEM_NEXT(l, li); 328 } 329 idx++; 330 } 331 332 tv_list_set_lock(l, prev_lock); 333 } 334 335 /// Implementation of map(), filter() and foreach(). 336 static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) 337 { 338 const char *const func_name = (filtermap == FILTERMAP_MAP 339 ? "map()" 340 : (filtermap == FILTERMAP_MAPNEW 341 ? "mapnew()" 342 : (filtermap == FILTERMAP_FILTER 343 ? "filter()" 344 : "foreach()"))); 345 const char *const arg_errmsg = (filtermap == FILTERMAP_MAP 346 ? N_("map() argument") 347 : (filtermap == FILTERMAP_MAPNEW 348 ? N_("mapnew() argument") 349 : (filtermap == FILTERMAP_FILTER 350 ? N_("filter() argument") 351 : N_("foreach() argument")))); 352 353 // map(), filter(), foreach() return the first argument, also on failure. 354 if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING) { 355 tv_copy(&argvars[0], rettv); 356 } 357 358 if (argvars[0].v_type != VAR_BLOB 359 && argvars[0].v_type != VAR_LIST 360 && argvars[0].v_type != VAR_DICT 361 && argvars[0].v_type != VAR_STRING) { 362 semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob), func_name); 363 return; 364 } 365 366 typval_T *expr = &argvars[1]; 367 // On type errors, the preceding call has already displayed an error 368 // message. Avoid a misleading error message for an empty string that 369 // was not passed as argument. 370 if (expr->v_type == VAR_UNKNOWN) { 371 return; 372 } 373 374 typval_T save_val; 375 typval_T save_key; 376 377 prepare_vimvar(VV_VAL, &save_val); 378 prepare_vimvar(VV_KEY, &save_key); 379 380 // We reset "did_emsg" to be able to detect whether an error 381 // occurred during evaluation of the expression. 382 int save_did_emsg = did_emsg; 383 did_emsg = false; 384 385 if (argvars[0].v_type == VAR_DICT) { 386 filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name, 387 arg_errmsg, expr, rettv); 388 } else if (argvars[0].v_type == VAR_BLOB) { 389 filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, arg_errmsg, rettv); 390 } else if (argvars[0].v_type == VAR_STRING) { 391 filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv); 392 } else { 393 assert(argvars[0].v_type == VAR_LIST); 394 filter_map_list(argvars[0].vval.v_list, filtermap, func_name, 395 arg_errmsg, expr, rettv); 396 } 397 398 restore_vimvar(VV_KEY, &save_key); 399 restore_vimvar(VV_VAL, &save_val); 400 401 did_emsg |= save_did_emsg; 402 } 403 404 /// "filter()" function 405 void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 406 { 407 filter_map(argvars, rettv, FILTERMAP_FILTER); 408 } 409 410 /// "map()" function 411 void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 412 { 413 filter_map(argvars, rettv, FILTERMAP_MAP); 414 } 415 416 /// "mapnew()" function 417 void f_mapnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 418 { 419 filter_map(argvars, rettv, FILTERMAP_MAPNEW); 420 } 421 422 /// "foreach()" function 423 void f_foreach(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 424 { 425 filter_map(argvars, rettv, FILTERMAP_FOREACH); 426 } 427 428 /// "add(list, item)" function 429 void f_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 430 { 431 rettv->vval.v_number = 1; // Default: failed. 432 if (argvars[0].v_type == VAR_LIST) { 433 list_T *const l = argvars[0].vval.v_list; 434 if (!value_check_lock(tv_list_locked(l), N_("add() argument"), 435 TV_TRANSLATE)) { 436 tv_list_append_tv(l, &argvars[1]); 437 tv_copy(&argvars[0], rettv); 438 } 439 } else if (argvars[0].v_type == VAR_BLOB) { 440 blob_T *const b = argvars[0].vval.v_blob; 441 if (b != NULL 442 && !value_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) { 443 bool error = false; 444 const varnumber_T n = tv_get_number_chk(&argvars[1], &error); 445 446 if (!error) { 447 ga_append(&b->bv_ga, (uint8_t)n); 448 tv_copy(&argvars[0], rettv); 449 } 450 } 451 } else { 452 emsg(_(e_listblobreq)); 453 } 454 } 455 456 /// Count the number of times "needle" occurs in string "haystack". 457 /// 458 /// @param ic ignore case 459 static varnumber_T count_string(const char *haystack, const char *needle, bool ic) 460 { 461 varnumber_T n = 0; 462 const char *p = haystack; 463 464 if (p == NULL || needle == NULL || *needle == NUL) { 465 return 0; 466 } 467 468 size_t needlelen = strlen(needle); 469 if (ic) { 470 while (*p != NUL) { 471 if (mb_strnicmp(p, needle, needlelen) == 0) { 472 n++; 473 p += needlelen; 474 } else { 475 MB_PTR_ADV(p); 476 } 477 } 478 } else { 479 const char *next; 480 while ((next = strstr(p, needle)) != NULL) { 481 n++; 482 p = next + needlelen; 483 } 484 } 485 486 return n; 487 } 488 489 /// Count the number of times item "needle" occurs in List "l" starting at index "idx". 490 /// 491 /// @param ic ignore case 492 static varnumber_T count_list(list_T *l, typval_T *needle, int64_t idx, bool ic) 493 { 494 if (tv_list_len(l) == 0) { 495 return 0; 496 } 497 498 listitem_T *li = tv_list_find(l, (int)idx); 499 if (li == NULL) { 500 semsg(_(e_list_index_out_of_range_nr), idx); 501 return 0; 502 } 503 504 varnumber_T n = 0; 505 506 for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { 507 if (tv_equal(TV_LIST_ITEM_TV(li), needle, ic)) { 508 n++; 509 } 510 } 511 512 return n; 513 } 514 515 /// Count the number of times item "needle" occurs in Dict "d". 516 /// 517 /// @param ic ignore case 518 static varnumber_T count_dict(dict_T *d, typval_T *needle, bool ic) 519 { 520 if (d == NULL) { 521 return 0; 522 } 523 524 varnumber_T n = 0; 525 526 TV_DICT_ITER(d, di, { 527 if (tv_equal(&di->di_tv, needle, ic)) { 528 n++; 529 } 530 }); 531 532 return n; 533 } 534 535 /// "count()" function 536 void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 537 { 538 varnumber_T n = 0; 539 int ic = 0; 540 bool error = false; 541 542 if (argvars[2].v_type != VAR_UNKNOWN) { 543 ic = (int)tv_get_number_chk(&argvars[2], &error); 544 } 545 546 if (!error && argvars[0].v_type == VAR_STRING) { 547 n = count_string(argvars[0].vval.v_string, tv_get_string_chk(&argvars[1]), ic); 548 } else if (!error && argvars[0].v_type == VAR_LIST) { 549 int64_t idx = 0; 550 if (argvars[2].v_type != VAR_UNKNOWN 551 && argvars[3].v_type != VAR_UNKNOWN) { 552 idx = (int64_t)tv_get_number_chk(&argvars[3], &error); 553 } 554 if (!error) { 555 n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic); 556 } 557 } else if (!error && argvars[0].v_type == VAR_DICT) { 558 dict_T *d = argvars[0].vval.v_dict; 559 560 if (d != NULL) { 561 if (argvars[2].v_type != VAR_UNKNOWN 562 && argvars[3].v_type != VAR_UNKNOWN) { 563 emsg(_(e_invarg)); 564 } else { 565 n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic); 566 } 567 } 568 } else if (!error) { 569 semsg(_(e_argument_of_str_must_be_list_string_or_dictionary), "count()"); 570 } 571 rettv->vval.v_number = n; 572 } 573 574 /// extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the 575 /// resulting Dict in "rettv". 576 /// 577 /// @param is_new true for extendnew() 578 static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv) 579 { 580 dict_T *d1 = argvars[0].vval.v_dict; 581 if (d1 == NULL) { 582 const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); 583 (void)locked; 584 assert(locked == true); 585 return; 586 } 587 dict_T *const d2 = argvars[1].vval.v_dict; 588 if (d2 == NULL) { 589 // Do nothing 590 tv_copy(&argvars[0], rettv); 591 return; 592 } 593 594 if (!is_new && value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { 595 return; 596 } 597 598 if (is_new) { 599 d1 = tv_dict_copy(NULL, d1, false, get_copyID()); 600 if (d1 == NULL) { 601 return; 602 } 603 } 604 605 const char *action = "force"; 606 // Check the third argument. 607 if (argvars[2].v_type != VAR_UNKNOWN) { 608 const char *const av[] = { "keep", "force", "error" }; 609 610 action = tv_get_string_chk(&argvars[2]); 611 if (action == NULL) { 612 if (is_new) { 613 tv_dict_unref(d1); 614 } 615 return; // Type error; error message already given. 616 } 617 size_t i; 618 for (i = 0; i < ARRAY_SIZE(av); i++) { 619 if (strcmp(action, av[i]) == 0) { 620 break; 621 } 622 } 623 if (i == 3) { 624 if (is_new) { 625 tv_dict_unref(d1); 626 } 627 semsg(_(e_invarg2), action); 628 return; 629 } 630 } 631 632 tv_dict_extend(d1, d2, action); 633 634 if (is_new) { 635 *rettv = (typval_T){ 636 .v_type = VAR_DICT, 637 .v_lock = VAR_UNLOCKED, 638 .vval.v_dict = d1, 639 }; 640 } else { 641 tv_copy(&argvars[0], rettv); 642 } 643 } 644 645 /// extend() a List. Append List argvars[1] to List argvars[0] before index 646 /// argvars[3] and return the resulting list in "rettv". 647 /// 648 /// @param is_new true for extendnew() 649 static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv) 650 { 651 bool error = false; 652 653 list_T *l1 = argvars[0].vval.v_list; 654 list_T *const l2 = argvars[1].vval.v_list; 655 656 if (!is_new && value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { 657 return; 658 } 659 660 if (is_new) { 661 l1 = tv_list_copy(NULL, l1, false, get_copyID()); 662 if (l1 == NULL) { 663 return; 664 } 665 } 666 667 listitem_T *item; 668 if (argvars[2].v_type != VAR_UNKNOWN) { 669 int before = (int)tv_get_number_chk(&argvars[2], &error); 670 if (error) { 671 return; // Type error; errmsg already given. 672 } 673 674 if (before == tv_list_len(l1)) { 675 item = NULL; 676 } else { 677 item = tv_list_find(l1, before); 678 if (item == NULL) { 679 semsg(_(e_list_index_out_of_range_nr), (int64_t)before); 680 return; 681 } 682 } 683 } else { 684 item = NULL; 685 } 686 tv_list_extend(l1, l2, item); 687 688 if (is_new) { 689 *rettv = (typval_T){ 690 .v_type = VAR_LIST, 691 .v_lock = VAR_UNLOCKED, 692 .vval.v_list = l1, 693 }; 694 } else { 695 tv_copy(&argvars[0], rettv); 696 } 697 } 698 699 /// "extend()" or "extendnew()" function. 700 /// 701 /// @param is_new true for extendnew() 702 static void extend(typval_T *argvars, typval_T *rettv, char *arg_errmsg, bool is_new) 703 { 704 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { 705 extend_list(argvars, arg_errmsg, is_new, rettv); 706 } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { 707 extend_dict(argvars, arg_errmsg, is_new, rettv); 708 } else { 709 semsg(_(e_listdictarg), is_new ? "extendnew()" : "extend()"); 710 } 711 } 712 713 /// "extend(list, list [, idx])" function 714 /// "extend(dict, dict [, action])" function 715 void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 716 { 717 char *errmsg = N_("extend() argument"); 718 extend(argvars, rettv, errmsg, false); 719 } 720 721 /// "extendnew(list, list [, idx])" function 722 /// "extendnew(dict, dict [, action])" function 723 void f_extendnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 724 { 725 char *errmsg = N_("extendnew() argument"); 726 extend(argvars, rettv, errmsg, true); 727 } 728 729 /// "insert()" function 730 void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 731 { 732 bool error = false; 733 734 if (argvars[0].v_type == VAR_BLOB) { 735 blob_T *const b = argvars[0].vval.v_blob; 736 737 if (b == NULL 738 || value_check_lock(b->bv_lock, N_("insert() argument"), 739 TV_TRANSLATE)) { 740 return; 741 } 742 743 int before = 0; 744 const int len = tv_blob_len(b); 745 746 if (argvars[2].v_type != VAR_UNKNOWN) { 747 before = (int)tv_get_number_chk(&argvars[2], &error); 748 if (error) { 749 return; // type error; errmsg already given 750 } 751 if (before < 0 || before > len) { 752 semsg(_(e_invarg2), tv_get_string(&argvars[2])); 753 return; 754 } 755 } 756 const int val = (int)tv_get_number_chk(&argvars[1], &error); 757 if (error) { 758 return; 759 } 760 if (val < 0 || val > 255) { 761 semsg(_(e_invarg2), tv_get_string(&argvars[1])); 762 return; 763 } 764 765 ga_grow(&b->bv_ga, 1); 766 uint8_t *const p = (uint8_t *)b->bv_ga.ga_data; 767 memmove(p + before + 1, p + before, (size_t)(len - before)); 768 *(p + before) = (uint8_t)val; 769 b->bv_ga.ga_len++; 770 771 tv_copy(&argvars[0], rettv); 772 } else if (argvars[0].v_type != VAR_LIST) { 773 semsg(_(e_listblobarg), "insert()"); 774 } else { 775 list_T *l = argvars[0].vval.v_list; 776 if (value_check_lock(tv_list_locked(l), N_("insert() argument"), TV_TRANSLATE)) { 777 return; 778 } 779 780 int64_t before = 0; 781 if (argvars[2].v_type != VAR_UNKNOWN) { 782 before = tv_get_number_chk(&argvars[2], &error); 783 } 784 if (error) { 785 // type error; errmsg already given 786 return; 787 } 788 789 listitem_T *item = NULL; 790 if (before != tv_list_len(l)) { 791 item = tv_list_find(l, (int)before); 792 if (item == NULL) { 793 semsg(_(e_list_index_out_of_range_nr), before); 794 l = NULL; 795 } 796 } 797 if (l != NULL) { 798 tv_list_insert_tv(l, &argvars[1], item); 799 tv_copy(&argvars[0], rettv); 800 } 801 } 802 } 803 804 /// "remove()" function 805 void f_remove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 806 { 807 const char *const arg_errmsg = N_("remove() argument"); 808 809 if (argvars[0].v_type == VAR_DICT) { 810 tv_dict_remove(argvars, rettv, arg_errmsg); 811 } else if (argvars[0].v_type == VAR_BLOB) { 812 tv_blob_remove(argvars, rettv, arg_errmsg); 813 } else if (argvars[0].v_type == VAR_LIST) { 814 tv_list_remove(argvars, rettv, arg_errmsg); 815 } else { 816 semsg(_(e_listdictblobarg), "remove()"); 817 } 818 } 819 820 /// "reverse({list})" function 821 void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 822 { 823 if (tv_check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL) { 824 return; 825 } 826 827 if (argvars[0].v_type == VAR_BLOB) { 828 blob_T *const b = argvars[0].vval.v_blob; 829 const int len = tv_blob_len(b); 830 831 for (int i = 0; i < len / 2; i++) { 832 const uint8_t tmp = tv_blob_get(b, i); 833 tv_blob_set(b, i, tv_blob_get(b, len - i - 1)); 834 tv_blob_set(b, len - i - 1, tmp); 835 } 836 tv_blob_set_ret(rettv, b); 837 } else if (argvars[0].v_type == VAR_STRING) { 838 rettv->v_type = VAR_STRING; 839 if (argvars[0].vval.v_string != NULL) { 840 rettv->vval.v_string = reverse_text(argvars[0].vval.v_string); 841 } else { 842 rettv->vval.v_string = NULL; 843 } 844 } else if (argvars[0].v_type == VAR_LIST) { 845 list_T *const l = argvars[0].vval.v_list; 846 if (!value_check_lock(tv_list_locked(l), N_("reverse() argument"), 847 TV_TRANSLATE)) { 848 tv_list_reverse(l); 849 tv_list_set_ret(rettv, l); 850 } 851 } 852 }