/// 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 list_insert_tv(list_T *l, typval_T *tv, listitem_T *item) { listitem_T *ni = listitem_alloc(); if (ni == NULL) return FAIL; copy_tv(tv, &ni->li_tv); if (item == NULL) list_append(l, ni); else { ni->li_prev = item->li_prev; ni->li_next = item; if (item->li_prev == NULL) { l->lv_first = ni; ++l->lv_idx; } else { item->li_prev->li_next = ni; l->lv_idx_item = NULL; } item->li_prev = ni; ++l->lv_len; } return OK; }
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_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; }
/* * Make a copy of dict "d". Shallow if "deep" is FALSE. * The refcount of the new dict is set to 1. * See item_copy() for "copyID". * Returns NULL when out of memory. */ dict_T * dict_copy(dict_T *orig, int deep, int copyID) { dict_T *copy; dictitem_T *di; int todo; hashitem_T *hi; if (orig == NULL) return NULL; copy = dict_alloc(); if (copy != NULL) { if (copyID != 0) { orig->dv_copyID = copyID; orig->dv_copydict = copy; } todo = (int)orig->dv_hashtab.ht_used; for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) { if (!HASHITEM_EMPTY(hi)) { --todo; di = dictitem_alloc(hi->hi_key); if (di == NULL) break; if (deep) { if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep, copyID) == FAIL) { vim_free(di); break; } } else copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); if (dict_add(copy, di) == FAIL) { dictitem_free(di); break; } } } ++copy->dv_refcount; if (todo > 0) { dict_unref(copy); copy = NULL; } } return copy; }
/* * Insert typval_T "tv" in list "l" before "item". * If "item" is NULL append at the end. * Return FAIL when out of memory. */ int list_insert_tv(list_T *l, typval_T *tv, listitem_T *item) { listitem_T *ni = listitem_alloc(); if (ni == NULL) return FAIL; copy_tv(tv, &ni->li_tv); list_insert(l, ni, item); return OK; }
/* * Append typval_T "tv" to the end of list "l". * Return FAIL when out of memory. */ int list_append_tv(list_T *l, typval_T *tv) { listitem_T *li = listitem_alloc(); if (li == NULL) return FAIL; copy_tv(tv, &li->li_tv); list_append(l, li); return OK; }
/* * Make a copy of a Dictionary item. */ static dictitem_T * dictitem_copy(dictitem_T *org) { dictitem_T *di; di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + STRLEN(org->di_key))); if (di != NULL) { STRCPY(di->di_key, org->di_key); di->di_flags = DI_FLAGS_ALLOC; copy_tv(&org->di_tv, &di->di_tv); } return di; }
/* * Make a copy of list "orig". Shallow if "deep" is FALSE. * The refcount of the new list is set to 1. * See item_copy() for "copyID". * Returns NULL when out of memory. */ list_T * list_copy(list_T *orig, int deep, int copyID) { list_T *copy; listitem_T *item; listitem_T *ni; if (orig == NULL) return NULL; copy = list_alloc(); if (copy != NULL) { if (copyID != 0) { /* Do this before adding the items, because one of the items may * refer back to this list. */ orig->lv_copyID = copyID; orig->lv_copylist = copy; } for (item = orig->lv_first; item != NULL && !got_int; item = item->li_next) { ni = listitem_alloc(); if (ni == NULL) break; if (deep) { if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { vim_free(ni); break; } } else copy_tv(&item->li_tv, &ni->li_tv); list_append(copy, ni); } ++copy->lv_refcount; if (item != NULL) { list_unref(copy); copy = NULL; } } return copy; }
/* * 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); } } } }
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); listitem_T *li; if (l->lv_lock) luaL_error(L, "list is locked"); li = listitem_alloc(); if (li != NULL) { typval_T v; lua_settop(L, 2); luaV_totypval(L, 2, &v); copy_tv(&v, &li->li_tv); list_append(l, li); } lua_settop(L, 1); return 1; }
/* * Turn a dict into a list: * "what" == 0: list of keys * "what" == 1: list of values * "what" == 2: list of items */ void dict_list(typval_T *argvars, typval_T *rettv, int what) { list_T *l2; dictitem_T *di; hashitem_T *hi; listitem_T *li; listitem_T *li2; dict_T *d; int todo; if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); return; } if ((d = argvars[0].vval.v_dict) == NULL) return; if (rettv_list_alloc(rettv) == FAIL) return; todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { --todo; di = HI2DI(hi); li = listitem_alloc(); if (li == NULL) break; list_append(rettv->vval.v_list, li); if (what == 0) { /* keys() */ li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = vim_strsave(di->di_key); } else if (what == 1) { /* values() */ copy_tv(&di->di_tv, &li->li_tv); } else { /* items() */ l2 = list_alloc(); li->li_tv.v_type = VAR_LIST; li->li_tv.v_lock = 0; li->li_tv.vval.v_list = l2; if (l2 == NULL) break; ++l2->lv_refcount; li2 = listitem_alloc(); if (li2 == NULL) break; list_append(l2, li2); li2->li_tv.v_type = VAR_STRING; li2->li_tv.v_lock = 0; li2->li_tv.vval.v_string = vim_strsave(di->di_key); li2 = listitem_alloc(); if (li2 == NULL) break; list_append(l2, li2); copy_tv(&di->di_tv, &li2->li_tv); } } } }