static int luaV_list_newindex (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); long n = (long) luaL_checkinteger(L, 2); listitem_T *li; if (l->lv_lock) luaL_error(L, "list is locked"); li = list_find(l, n); if (li == NULL) return 0; if (lua_isnil(L, 3)) /* remove? */ { vimlist_remove(l, li, li); clear_tv(&li->li_tv); vim_free(li); } else { typval_T v; luaV_totypval(L, 3, &v); clear_tv(&li->li_tv); copy_tv(&v, &li->li_tv); clear_tv(&v); } return 0; }
static int luaV_list_insert (lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); long pos = (long) luaL_optinteger(L, 3, 0); listitem_T *li = NULL; typval_T v; if (l->lv_lock) luaL_error(L, "list is locked"); if (pos < l->lv_len) { li = list_find(l, pos); if (li == NULL) luaL_error(L, "invalid position"); } lua_settop(L, 2); luaV_totypval(L, 2, &v); if (list_insert_tv(l, &v, li) == FAIL) { clear_tv(&v); luaL_error(L, "Failed to add item to list"); } clear_tv(&v); lua_settop(L, 1); return 1; }
/// Set a value in a dict. Objects are recursively expanded into their /// vimscript equivalents. Passing 'nil' as value deletes the key. /// /// @param dict The vimscript dict /// @param key The key /// @param value The new value /// @param[out] err Details of an error that may have occurred /// @return the old value, if any Object dict_set_value(dict_T *dict, String key, Object value, Error *err) { Object rv = OBJECT_INIT; if (dict->dv_lock) { set_api_error("Dictionary is locked", err); return rv; } if (key.size == 0) { set_api_error("Empty dictionary keys aren't allowed", err); return rv; } if (key.size > INT_MAX) { set_api_error("Key length is too high", err); return rv; } dictitem_T *di = dict_find(dict, (uint8_t *)key.data, (int)key.size); if (value.type == kObjectTypeNil) { // Delete the key if (di == NULL) { // Doesn't exist, fail set_api_error("Key doesn't exist", err); } else { // Return the old value rv = vim_to_object(&di->di_tv); // Delete the entry hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key); hash_remove(&dict->dv_hashtab, hi); dictitem_free(di); } } else { // Update the key typval_T tv; // Convert the object to a vimscript type in the temporary variable if (!object_to_vim(value, &tv, err)) { return rv; } if (di == NULL) { // Need to create an entry di = dictitem_alloc((uint8_t *) key.data); dict_add(dict, di); } else { // Return the old value clear_tv(&di->di_tv); } // Update the value copy_tv(&tv, &di->di_tv); // Clear the temporary variable clear_tv(&tv); } return rv; }
static int luaV_dict_newindex (lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); char_u *key = (char_u *) luaL_checkstring(L, 2); dictitem_T *di; if (d->dv_lock) luaL_error(L, "dict is locked"); di = dict_find(d, key, -1); if (di == NULL) /* non-existing key? */ { if (lua_isnil(L, 3)) return 0; di = dictitem_alloc(key); if (di == NULL) return 0; if (dict_add(d, di) == FAIL) { vim_free(di); return 0; } } else clear_tv(&di->di_tv); if (lua_isnil(L, 3)) /* remove? */ { hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key); hash_remove(&d->dv_hashtab, hi); dictitem_free(di); } else { typval_T v; luaV_totypval(L, 3, &v); copy_tv(&v, &di->di_tv); } return 0; }
/* * Free a Dictionary, including all non-container items it contains. * Ignores the reference count. */ static void dict_free_contents(dict_T *d) { int todo; hashitem_T *hi; dictitem_T *di; /* Lock the hashtab, we don't want it to resize while freeing items. */ hash_lock(&d->dv_hashtab); todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { /* Remove the item before deleting it, just in case there is * something recursive causing trouble. */ di = HI2DI(hi); hash_remove(&d->dv_hashtab, hi); clear_tv(&di->di_tv); vim_free(di); --todo; } } hash_clear(&d->dv_hashtab); }
/* * Free a dict item. Also clears the value. */ void dictitem_free(dictitem_T *item) { clear_tv(&item->di_tv); if (item->di_flags & DI_FLAGS_ALLOC) vim_free(item); }
/* * Allocate a variable for a List and fill it from "*arg". * Return OK or FAIL. */ int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) { list_T *l = NULL; typval_T tv; listitem_T *item; if (evaluate) { l = list_alloc(); if (l == NULL) return FAIL; } *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ goto failret; if (evaluate) { item = listitem_alloc(); if (item != NULL) { item->li_tv = tv; item->li_tv.v_lock = 0; list_append(l, item); } else clear_tv(&tv); } if (**arg == ']') break; if (**arg != ',') { EMSG2(_("E696: Missing comma in List: %s"), *arg); goto failret; } *arg = skipwhite(*arg + 1); } if (**arg != ']') { EMSG2(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) list_free(l); return FAIL; } *arg = skipwhite(*arg + 1); if (evaluate) rettv_list_set(rettv, l); return OK; }
static int json_decode_array(js_read_T *reader, typval_T *res, int options) { char_u *p; typval_T item; listitem_T *li; int ret; if (res != NULL && rettv_list_alloc(res) == FAIL) { res->v_type = VAR_SPECIAL; res->vval.v_number = VVAL_NONE; return FAIL; } ++reader->js_used; /* consume the '[' */ while (TRUE) { json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == NUL) return MAYBE; if (*p == ']') { ++reader->js_used; /* consume the ']' */ break; } ret = json_decode_item(reader, res == NULL ? NULL : &item, options); if (ret != OK) return ret; if (res != NULL) { li = listitem_alloc(); if (li == NULL) { clear_tv(&item); return FAIL; } li->li_tv = item; list_append(res->vval.v_list, li); } json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == ',') ++reader->js_used; else if (*p != ']') { if (*p == NUL) return MAYBE; return FAIL; } } return OK; }
static int luaV_list_add (lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); typval_T v; if (l->lv_lock) luaL_error(L, "list is locked"); lua_settop(L, 2); luaV_totypval(L, 2, &v); if (list_append_tv(l, &v) == FAIL) { clear_tv(&v); luaL_error(L, "Failed to add item to list"); } clear_tv(&v); lua_settop(L, 1); return 1; }
/// Call the given function with the given arguments stored in an array. /// /// @param fname Function to call /// @param args Functions arguments packed in an Array /// @param[out] err Details of an error that may have occurred /// @return Result of the function call Object vim_call_function(String fname, Array args, Error *err) { Object rv = OBJECT_INIT; if (args.size > MAX_FUNC_ARGS) { api_set_error(err, Validation, _("Function called with too many arguments.")); return rv; } // Convert the arguments in args from Object to typval_T values typval_T vim_args[MAX_FUNC_ARGS + 1]; size_t i = 0; // also used for freeing the variables for (; i < args.size; i++) { if (!object_to_vim(args.items[i], &vim_args[i], err)) { goto free_vim_args; } } try_start(); // Call the function typval_T rettv; int dummy; int r = call_func((char_u *) fname.data, (int) fname.size, &rettv, (int) args.size, vim_args, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, true, NULL); if (r == FAIL) { api_set_error(err, Exception, _("Error calling function.")); } if (!try_end(err)) { rv = vim_to_object(&rettv); } clear_tv(&rettv); free_vim_args: while (i > 0) { clear_tv(&vim_args[--i]); } return rv; }
/* * Free a list, including all non-container items it points to. * Ignores the reference count. */ static void list_free_contents(list_T *l) { listitem_T *item; for (item = l->lv_first; item != NULL; item = l->lv_first) { /* Remove the item before deleting it. */ l->lv_first = item->li_next; clear_tv(&item->li_tv); vim_free(item); } }
/* * Go over all entries in "d2" and add them to "d1". * When "action" is "error" then a duplicate key is an error. * When "action" is "force" then a duplicate key is overwritten. * Otherwise duplicate keys are ignored ("action" is "keep"). */ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) { dictitem_T *di1; hashitem_T *hi2; int todo; char_u *arg_errmsg = (char_u *)N_("extend() argument"); todo = (int)d2->dv_hashtab.ht_used; for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { if (!HASHITEM_EMPTY(hi2)) { --todo; di1 = dict_find(d1, hi2->hi_key, -1); if (d1->dv_scope != 0) { /* Disallow replacing a builtin function in l: and g:. * Check the key to be valid when adding to any scope. */ if (d1->dv_scope == VAR_DEF_SCOPE && HI2DI(hi2)->di_tv.v_type == VAR_FUNC && var_check_func_name(hi2->hi_key, di1 == NULL)) break; if (!valid_varname(hi2->hi_key)) break; } if (di1 == NULL) { di1 = dictitem_copy(HI2DI(hi2)); if (di1 != NULL && dict_add(d1, di1) == FAIL) dictitem_free(di1); } else if (*action == 'e') { EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); break; } else if (*action == 'f' && HI2DI(hi2) != di1) { if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) break; clear_tv(&di1->di_tv); copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); } } } }
/* * Decode one item and put it in "res". If "res" is NULL only advance. * Must already have skipped white space. * * Return FAIL for a decoding error (and give an error). * Return MAYBE for an incomplete message. */ static int json_decode_item(js_read_T *reader, typval_T *res, int options) { char_u *p; int len; int retval; garray_T stack; typval_T item; typval_T *cur_item; json_dec_item_T *top_item; char_u key_buf[NUMBUFLEN]; ga_init2(&stack, sizeof(json_dec_item_T), 100); cur_item = res; init_tv(&item); if (res != NULL) init_tv(res); fill_numbuflen(reader); p = reader->js_buf + reader->js_used; for (;;) { top_item = NULL; if (stack.ga_len > 0) { top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1; json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == NUL) { retval = MAYBE; if (top_item->jd_type == JSON_OBJECT) /* did get the key, clear it */ clear_tv(&top_item->jd_key_tv); goto theend; } if (top_item->jd_type == JSON_OBJECT_KEY || top_item->jd_type == JSON_ARRAY) { /* Check for end of object or array. */ if (*p == (top_item->jd_type == JSON_ARRAY ? ']' : '}')) { ++reader->js_used; /* consume the ']' or '}' */ --stack.ga_len; if (stack.ga_len == 0) { retval = OK; goto theend; } if (cur_item != NULL) cur_item = &top_item->jd_tv; goto item_end; } } } if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY && (options & JSON_JS) && reader->js_buf[reader->js_used] != '"' && reader->js_buf[reader->js_used] != '\'' && reader->js_buf[reader->js_used] != '[' && reader->js_buf[reader->js_used] != '{') { char_u *key; /* accept an object key that is not in quotes */ key = p = reader->js_buf + reader->js_used; while (*p != NUL && *p != ':' && *p > ' ') ++p; if (cur_item != NULL) { cur_item->v_type = VAR_STRING; cur_item->vval.v_string = vim_strnsave(key, (int)(p - key)); top_item->jd_key = cur_item->vval.v_string; } reader->js_used += (int)(p - key); } else { switch (*p) { case '[': /* start of array */ if (top_item && top_item->jd_type == JSON_OBJECT_KEY) { retval = FAIL; break; } if (ga_grow(&stack, 1) == FAIL) { retval = FAIL; break; } if (cur_item != NULL && rettv_list_alloc(cur_item) == FAIL) { cur_item->v_type = VAR_SPECIAL; cur_item->vval.v_number = VVAL_NONE; retval = FAIL; break; } ++reader->js_used; /* consume the '[' */ top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len; top_item->jd_type = JSON_ARRAY; ++stack.ga_len; if (cur_item != NULL) { top_item->jd_tv = *cur_item; cur_item = &item; } continue; case '{': /* start of object */ if (top_item && top_item->jd_type == JSON_OBJECT_KEY) { retval = FAIL; break; } if (ga_grow(&stack, 1) == FAIL) { retval = FAIL; break; } if (cur_item != NULL && rettv_dict_alloc(cur_item) == FAIL) { cur_item->v_type = VAR_SPECIAL; cur_item->vval.v_number = VVAL_NONE; retval = FAIL; break; } ++reader->js_used; /* consume the '{' */ top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len; top_item->jd_type = JSON_OBJECT_KEY; ++stack.ga_len; if (cur_item != NULL) { top_item->jd_tv = *cur_item; cur_item = &top_item->jd_key_tv; } continue; case '"': /* string */ retval = json_decode_string(reader, cur_item, *p); break; case '\'': if (options & JSON_JS) retval = json_decode_string(reader, cur_item, *p); else { EMSG(_(e_invarg)); retval = FAIL; } break; case ',': /* comma: empty item */ if ((options & JSON_JS) == 0) { EMSG(_(e_invarg)); retval = FAIL; break; } /* FALLTHROUGH */ case NUL: /* empty */ if (cur_item != NULL) { cur_item->v_type = VAR_SPECIAL; cur_item->vval.v_number = VVAL_NONE; } retval = OK; break; default: if (VIM_ISDIGIT(*p) || *p == '-') { #ifdef FEAT_FLOAT char_u *sp = p; if (*sp == '-') { ++sp; if (*sp == NUL) { retval = MAYBE; break; } if (!VIM_ISDIGIT(*sp)) { EMSG(_(e_invarg)); retval = FAIL; break; } } sp = skipdigits(sp); if (*sp == '.' || *sp == 'e' || *sp == 'E') { if (cur_item == NULL) { float_T f; len = string2float(p, &f); } else { cur_item->v_type = VAR_FLOAT; len = string2float(p, &cur_item->vval.v_float); } } else #endif { varnumber_T nr; vim_str2nr(reader->js_buf + reader->js_used, NULL, &len, 0, /* what */ &nr, NULL, 0); if (cur_item != NULL) { cur_item->v_type = VAR_NUMBER; cur_item->vval.v_number = nr; } } reader->js_used += len; retval = OK; break; } if (STRNICMP((char *)p, "false", 5) == 0) { reader->js_used += 5; if (cur_item != NULL) { cur_item->v_type = VAR_SPECIAL; cur_item->vval.v_number = VVAL_FALSE; } retval = OK; break; } if (STRNICMP((char *)p, "true", 4) == 0) { reader->js_used += 4; if (cur_item != NULL) { cur_item->v_type = VAR_SPECIAL; cur_item->vval.v_number = VVAL_TRUE; } retval = OK; break; } if (STRNICMP((char *)p, "null", 4) == 0) { reader->js_used += 4; if (cur_item != NULL) { cur_item->v_type = VAR_SPECIAL; cur_item->vval.v_number = VVAL_NULL; } retval = OK; break; } #ifdef FEAT_FLOAT if (STRNICMP((char *)p, "NaN", 3) == 0) { reader->js_used += 3; if (cur_item != NULL) { cur_item->v_type = VAR_FLOAT; cur_item->vval.v_float = NAN; } retval = OK; break; } if (STRNICMP((char *)p, "Infinity", 8) == 0) { reader->js_used += 8; if (cur_item != NULL) { cur_item->v_type = VAR_FLOAT; cur_item->vval.v_float = INFINITY; } retval = OK; break; } #endif /* check for truncated name */ len = (int)(reader->js_end - (reader->js_buf + reader->js_used)); if ( (len < 5 && STRNICMP((char *)p, "false", len) == 0) #ifdef FEAT_FLOAT || (len < 8 && STRNICMP((char *)p, "Infinity", len) == 0) || (len < 3 && STRNICMP((char *)p, "NaN", len) == 0) #endif || (len < 4 && (STRNICMP((char *)p, "true", len) == 0 || STRNICMP((char *)p, "null", len) == 0))) retval = MAYBE; else retval = FAIL; break; } /* We are finished when retval is FAIL or MAYBE and when at the * toplevel. */ if (retval == FAIL) break; if (retval == MAYBE || stack.ga_len == 0) goto theend; if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY && cur_item != NULL) { top_item->jd_key = get_tv_string_buf_chk(cur_item, key_buf); if (top_item->jd_key == NULL) { clear_tv(cur_item); EMSG(_(e_invarg)); retval = FAIL; goto theend; } } } item_end: top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1; switch (top_item->jd_type) { case JSON_ARRAY: if (res != NULL) { listitem_T *li = listitem_alloc(); if (li == NULL) { clear_tv(cur_item); retval = FAIL; goto theend; } li->li_tv = *cur_item; list_append(top_item->jd_tv.vval.v_list, li); } if (cur_item != NULL) cur_item = &item; json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == ',') ++reader->js_used; else if (*p != ']') { if (*p == NUL) retval = MAYBE; else { EMSG(_(e_invarg)); retval = FAIL; } goto theend; } break; case JSON_OBJECT_KEY: json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p != ':') { if (cur_item != NULL) clear_tv(cur_item); if (*p == NUL) retval = MAYBE; else { EMSG(_(e_invarg)); retval = FAIL; } goto theend; } ++reader->js_used; json_skip_white(reader); top_item->jd_type = JSON_OBJECT; if (cur_item != NULL) cur_item = &item; break; case JSON_OBJECT: if (cur_item != NULL && dict_find(top_item->jd_tv.vval.v_dict, top_item->jd_key, -1) != NULL) { EMSG2(_("E938: Duplicate key in JSON: \"%s\""), top_item->jd_key); clear_tv(&top_item->jd_key_tv); clear_tv(cur_item); retval = FAIL; goto theend; } if (cur_item != NULL) { dictitem_T *di = dictitem_alloc(top_item->jd_key); clear_tv(&top_item->jd_key_tv); if (di == NULL) { clear_tv(cur_item); retval = FAIL; goto theend; } di->di_tv = *cur_item; di->di_tv.v_lock = 0; if (dict_add(top_item->jd_tv.vval.v_dict, di) == FAIL) { dictitem_free(di); retval = FAIL; goto theend; } } json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == ',') ++reader->js_used; else if (*p != '}') { if (*p == NUL) retval = MAYBE; else { EMSG(_(e_invarg)); retval = FAIL; } goto theend; } top_item->jd_type = JSON_OBJECT_KEY; if (cur_item != NULL) cur_item = &top_item->jd_key_tv; break; } } /* Get here when parsing failed. */ if (res != NULL) { clear_tv(res); res->v_type = VAR_SPECIAL; res->vval.v_number = VVAL_NONE; } EMSG(_(e_invarg)); theend: ga_clear(&stack); return retval; }
static int json_decode_object(js_read_T *reader, typval_T *res) { char_u *p; typval_T tvkey; typval_T item; dictitem_T *di; char_u buf[NUMBUFLEN]; char_u *key = NULL; int ret; if (res != NULL && rettv_dict_alloc(res) == FAIL) { res->v_type = VAR_SPECIAL; res->vval.v_number = VVAL_NONE; return FAIL; } ++reader->js_used; /* consume the '{' */ while (TRUE) { json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == NUL) return MAYBE; if (*p == '}') { ++reader->js_used; /* consume the '}' */ break; } ret = json_decode_item(reader, res == NULL ? NULL : &tvkey); if (ret != OK) return ret; if (res != NULL) { key = get_tv_string_buf_chk(&tvkey, buf); if (key == NULL || *key == NUL) { clear_tv(&tvkey); return FAIL; } } json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p != ':') { if (res != NULL) clear_tv(&tvkey); if (*p == NUL) return MAYBE; return FAIL; } ++reader->js_used; json_skip_white(reader); ret = json_decode_item(reader, res == NULL ? NULL : &item); if (ret != OK) { if (res != NULL) clear_tv(&tvkey); return ret; } if (res != NULL) { di = dictitem_alloc(key); clear_tv(&tvkey); if (di == NULL) { clear_tv(&item); return FAIL; } di->di_tv = item; if (dict_add(res->vval.v_dict, di) == FAIL) { dictitem_free(di); return FAIL; } } json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == ',') ++reader->js_used; else if (*p != '}') { if (*p == NUL) return MAYBE; return FAIL; } } return OK; }
static int json_decode_object(js_read_T *reader, typval_T *res, int options) { char_u *p; typval_T tvkey; typval_T item; dictitem_T *di; char_u buf[NUMBUFLEN]; char_u *key = NULL; int ret; if (res != NULL && rettv_dict_alloc(res) == FAIL) { res->v_type = VAR_SPECIAL; res->vval.v_number = VVAL_NONE; return FAIL; } ++reader->js_used; /* consume the '{' */ while (TRUE) { json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == NUL) return MAYBE; if (*p == '}') { ++reader->js_used; /* consume the '}' */ break; } if ((options & JSON_JS) && reader->js_buf[reader->js_used] != '"') { /* accept a key that is not in quotes */ key = p = reader->js_buf + reader->js_used; while (*p != NUL && *p != ':' && *p > ' ') ++p; tvkey.v_type = VAR_STRING; tvkey.vval.v_string = vim_strnsave(key, (int)(p - key)); reader->js_used += (int)(p - key); key = tvkey.vval.v_string; } else { ret = json_decode_item(reader, res == NULL ? NULL : &tvkey, options); if (ret != OK) return ret; if (res != NULL) { key = get_tv_string_buf_chk(&tvkey, buf); if (key == NULL || *key == NUL) { clear_tv(&tvkey); return FAIL; } } } json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p != ':') { if (res != NULL) clear_tv(&tvkey); if (*p == NUL) return MAYBE; return FAIL; } ++reader->js_used; json_skip_white(reader); ret = json_decode_item(reader, res == NULL ? NULL : &item, options); if (ret != OK) { if (res != NULL) clear_tv(&tvkey); return ret; } if (res != NULL) { di = dictitem_alloc(key); clear_tv(&tvkey); if (di == NULL) { clear_tv(&item); return FAIL; } di->di_tv = item; if (dict_add(res->vval.v_dict, di) == FAIL) { dictitem_free(di); return FAIL; } } json_skip_white(reader); p = reader->js_buf + reader->js_used; if (*p == ',') ++reader->js_used; else if (*p != '}') { if (*p == NUL) return MAYBE; return FAIL; } } return OK; }
/* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. */ int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) { dict_T *d = NULL; typval_T tvkey; typval_T tv; char_u *key = NULL; dictitem_T *item; char_u *start = skipwhite(*arg + 1); char_u buf[NUMBUFLEN]; /* * First check if it's not a curly-braces thing: {expr}. * Must do this without evaluating, otherwise a function may be called * twice. Unfortunately this means we need to call eval1() twice for the * first item. * But {} is an empty Dictionary. */ if (*start != '}') { if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */ return FAIL; if (*start == '}') return NOTDONE; } if (evaluate) { d = dict_alloc(); if (d == NULL) return FAIL; } tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; *arg = skipwhite(*arg + 1); while (**arg != '}' && **arg != NUL) { if (eval1(arg, &tvkey, evaluate) == FAIL) /* recursive! */ goto failret; if (**arg != ':') { EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); clear_tv(&tvkey); goto failret; } if (evaluate) { key = get_tv_string_buf_chk(&tvkey, buf); if (key == NULL) { /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */ clear_tv(&tvkey); goto failret; } } *arg = skipwhite(*arg + 1); if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ { if (evaluate) clear_tv(&tvkey); goto failret; } if (evaluate) { item = dict_find(d, key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); clear_tv(&tvkey); clear_tv(&tv); goto failret; } item = dictitem_alloc(key); clear_tv(&tvkey); if (item != NULL) { item->di_tv = tv; item->di_tv.v_lock = 0; if (dict_add(d, item) == FAIL) dictitem_free(item); } } if (**arg == '}') break; if (**arg != ',') { EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg); goto failret; } *arg = skipwhite(*arg + 1); } if (**arg != '}') { EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); failret: if (evaluate) dict_free(d); return FAIL; } *arg = skipwhite(*arg + 1); if (evaluate) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = d; ++d->dv_refcount; } return OK; }
/* * Free a list item. Also clears the value. Does not notify watchers. */ void listitem_free(listitem_T *item) { clear_tv(&item->li_tv); vim_free(item); }