aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/typval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval/typval.c')
-rw-r--r--src/nvim/eval/typval.c1302
1 files changed, 910 insertions, 392 deletions
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index c298064d86..069cdced34 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1,16 +1,14 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
#include <assert.h>
+#include <lauxlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
-#include "lauxlib.h"
-#include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/ascii_defs.h"
+#include "nvim/assert_defs.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
@@ -21,33 +19,82 @@
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/lib/queue.h"
#include "nvim/lua/executor.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/input.h"
-#include "nvim/pos.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/pos_defs.h"
+#include "nvim/strings.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
+
+/// struct storing information about current sort
+typedef struct {
+ int item_compare_ic;
+ bool item_compare_lc;
+ bool item_compare_numeric;
+ bool item_compare_numbers;
+ bool item_compare_float;
+ const char *item_compare_func;
+ partial_T *item_compare_partial;
+ dict_T *item_compare_selfdict;
+ bool item_compare_func_err;
+} sortinfo_T;
+
+/// Structure representing one list item, used for sort array.
+typedef struct {
+ listitem_T *item; ///< Sorted list item.
+ int idx; ///< Sorted list item index.
+} ListSortItem;
+
+typedef int (*ListSorter)(const void *, const void *);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.c.generated.h"
#endif
-static char e_string_required_for_argument_nr[]
+static const char e_variable_nested_too_deep_for_unlock[]
+ = N_("E743: Variable nested too deep for (un)lock");
+static const char e_using_invalid_value_as_string[]
+ = N_("E908: Using an invalid value as a String");
+static const char e_string_required_for_argument_nr[]
= N_("E1174: String required for argument %d");
-static char e_non_empty_string_required_for_argument_nr[]
+static const char e_non_empty_string_required_for_argument_nr[]
= N_("E1175: Non-empty string required for argument %d");
-static char e_number_required_for_argument_nr[]
+static const char e_dict_required_for_argument_nr[]
+ = N_("E1206: Dictionary required for argument %d");
+static const char e_number_required_for_argument_nr[]
= N_("E1210: Number required for argument %d");
-static char e_string_or_list_required_for_argument_nr[]
+static const char e_list_required_for_argument_nr[]
+ = N_("E1211: List required for argument %d");
+static const char e_bool_required_for_argument_nr[]
+ = N_("E1212: Bool required for argument %d");
+static const char e_float_or_number_required_for_argument_nr[]
+ = N_("E1219: Float or Number required for argument %d");
+static const char e_string_or_number_required_for_argument_nr[]
+ = N_("E1220: String or Number required for argument %d");
+static const char e_string_or_list_required_for_argument_nr[]
= N_("E1222: String or List required for argument %d");
+static const char e_list_or_blob_required_for_argument_nr[]
+ = N_("E1226: List or Blob required for argument %d");
+static const char e_blob_required_for_argument_nr[]
+ = N_("E1238: Blob required for argument %d");
+static const char e_invalid_value_for_blob_nr[]
+ = N_("E1239: Invalid value for blob: %d");
+static const char e_string_list_or_blob_required_for_argument_nr[]
+ = N_("E1252: String, List or Blob required for argument %d");
+static const char e_string_or_function_required_for_argument_nr[]
+ = N_("E1256: String or function required for argument %d");
+static const char e_non_null_dict_required_for_argument_nr[]
+ = N_("E1297: Non-NULL Dictionary required for argument %d");
bool tv_in_free_unref_items = false;
@@ -58,70 +105,6 @@ bool tv_in_free_unref_items = false;
const char *const tv_empty_string = "";
//{{{1 Lists
-//{{{2 List log
-#ifdef LOG_LIST_ACTIONS
-ListLog *list_log_first = NULL;
-ListLog *list_log_last = NULL;
-
-/// Write list log to the given file
-///
-/// @param[in] fname File to write log to. Will be appended to if already
-/// present.
-void list_write_log(const char *const fname)
- FUNC_ATTR_NONNULL_ALL
-{
- FileDescriptor fp;
- const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600);
- if (fo_ret != 0) {
- semsg(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret));
- return;
- }
- for (ListLog *chunk = list_log_first; chunk != NULL;) {
- for (size_t i = 0; i < chunk->size; i++) {
- char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2];
- // act : hex " c:" len "[]" "\n\0"
- const ListLogEntry entry = chunk->entries[i];
- const size_t snp_len = (size_t)snprintf(buf, sizeof(buf),
- "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR
- "\n",
- entry.action, entry.l, entry.len, entry.li1,
- entry.li2);
- assert(snp_len + 1 == sizeof(buf));
- const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len);
- if (fw_ret != (ptrdiff_t)snp_len) {
- assert(fw_ret < 0);
- if (i) {
- memmove(chunk->entries, chunk->entries + i,
- sizeof(chunk->entries[0]) * (chunk->size - i));
- chunk->size -= i;
- }
- semsg(_("E5143: Failed to write to file %s: %s"),
- fname, os_strerror((int)fw_ret));
- return;
- }
- }
- list_log_first = chunk->next;
- xfree(chunk);
- chunk = list_log_first;
- }
- const int fc_ret = file_close(&fp, true);
- if (fc_ret != 0) {
- semsg(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret));
- }
-}
-
-# ifdef EXITFREE
-/// Free list log
-void list_free_log(void)
-{
- for (ListLog *chunk = list_log_first; chunk != NULL;) {
- list_log_first = chunk->next;
- xfree(chunk);
- chunk = list_log_first;
- }
-}
-# endif
-#endif
//{{{2 List item
/// Allocate a list item
@@ -210,7 +193,7 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item)
/// Caller should take care of the reference count.
///
/// @param[in] len Expected number of items to be populated before list
-/// becomes accessible from VimL. It is still valid to
+/// becomes accessible from Vimscript. It is still valid to
/// underpopulate a list, value only controls how many elements
/// will be allocated in advance. Currently does nothing.
/// @see ListLenSpecials.
@@ -228,7 +211,6 @@ list_T *tv_list_alloc(const ptrdiff_t len)
list->lv_used_prev = NULL;
list->lv_used_next = gc_first_list;
gc_first_list = list;
- list_log(list, NULL, (void *)(uintptr_t)len, "alloc");
list->lua_table_ref = LUA_NOREF;
return list;
}
@@ -259,8 +241,6 @@ void tv_list_init_static10(staticList10_T *const sl)
li->li_prev = li - 1;
li->li_next = li + 1;
}
- list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1],
- "s10init");
#undef SL_SIZE
}
@@ -272,7 +252,6 @@ void tv_list_init_static(list_T *const l)
{
CLEAR_POINTER(l);
l->lv_refcount = DO_NOT_FREE_CNT;
- list_log(l, NULL, NULL, "sinit");
}
/// Free items contained in a list
@@ -281,7 +260,6 @@ void tv_list_init_static(list_T *const l)
void tv_list_free_contents(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, NULL, NULL, "freecont");
for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) {
// Remove the item before deleting it.
l->lv_first = item->li_next;
@@ -311,7 +289,6 @@ void tv_list_free_list(list_T *const l)
if (l->lv_used_next != NULL) {
l->lv_used_next->lv_used_prev = l->lv_used_prev;
}
- list_log(l, NULL, NULL, "freelist");
NLUA_CLEAR_REF(l->lua_table_ref);
xfree(l);
@@ -358,7 +335,6 @@ void tv_list_unref(list_T *const l)
void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "drop");
// Notify watchers.
for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) {
l->lv_len--;
@@ -376,14 +352,12 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *con
item->li_prev->li_next = item2->li_next;
}
l->lv_idx_item = NULL;
- list_log(l, l->lv_first, l->lv_last, "afterdrop");
}
/// Like tv_list_drop_items, but also frees all removed items
void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "remove");
tv_list_drop_items(l, item, item2);
for (listitem_T *li = item;;) {
tv_clear(TV_LIST_ITEM_TV(li));
@@ -407,7 +381,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con
list_T *const tgt_l, const int cnt)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "move");
tv_list_drop_items(l, item, item2);
item->li_prev = tgt_l->lv_last;
item2->li_next = NULL;
@@ -418,7 +391,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con
}
tgt_l->lv_last = item2;
tgt_l->lv_len += cnt;
- list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt");
}
/// Insert list item
@@ -446,11 +418,10 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, listitem_T *const ite
}
item->li_prev = ni;
l->lv_len++;
- list_log(l, ni, item, "insert");
}
}
-/// Insert VimL value into a list
+/// Insert Vimscript value into a list
///
/// @param[out] l List to insert to.
/// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an
@@ -472,7 +443,6 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, listitem_T *const it
void tv_list_append(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, NULL, "append");
if (l->lv_last == NULL) {
// empty list
l->lv_first = item;
@@ -487,7 +457,7 @@ void tv_list_append(list_T *const l, listitem_T *const item)
item->li_next = NULL;
}
-/// Append VimL value to the end of list
+/// Append Vimscript value to the end of list
///
/// @param[out] l List to append to.
/// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an
@@ -647,55 +617,166 @@ tv_list_copy_error:
return NULL;
}
-/// Flatten "list" in place to depth "maxdepth".
+/// Get the list item in "l" with index "n1". "n1" is adjusted if needed.
+/// Return NULL if there is no such item.
+listitem_T *tv_list_check_range_index_one(list_T *const l, int *const n1, const bool quiet)
+{
+ listitem_T *li = tv_list_find_index(l, n1);
+ if (li == NULL) {
+ if (!quiet) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n1));
+ }
+ return NULL;
+ }
+ return li;
+}
+
+/// Check that "n2" can be used as the second index in a range of list "l".
+/// If "n1" or "n2" is negative it is changed to the positive index.
+/// "li1" is the item for item "n1".
+/// Return OK or FAIL.
+int tv_list_check_range_index_two(list_T *const l, int *const n1, const listitem_T *const li1,
+ int *const n2, const bool quiet)
+{
+ if (*n2 < 0) {
+ listitem_T *ni = tv_list_find(l, *n2);
+ if (ni == NULL) {
+ if (!quiet) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2));
+ }
+ return FAIL;
+ }
+ *n2 = tv_list_idx_of_item(l, ni);
+ }
+
+ // Check that n2 isn't before n1.
+ if (*n1 < 0) {
+ *n1 = tv_list_idx_of_item(l, li1);
+ }
+ if (*n2 < *n1) {
+ if (!quiet) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2));
+ }
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Assign values from list "src" into a range of "dest".
+/// "idx1_arg" is the index of the first item in "dest" to be replaced.
+/// "idx2" is the index of last item to be replaced, but when "empty_idx2" is
+/// true then replace all items after "idx1".
+/// "op" is the operator, normally "=" but can be "+=" and the like.
+/// "varname" is used for error messages.
+/// Returns OK or FAIL.
+int tv_list_assign_range(list_T *const dest, list_T *const src, const int idx1_arg, const int idx2,
+ const bool empty_idx2, const char *const op, const char *const varname)
+{
+ int idx1 = idx1_arg;
+ listitem_T *const first_li = tv_list_find_index(dest, &idx1);
+ listitem_T *src_li;
+
+ // Check whether any of the list items is locked before making any changes.
+ int idx = idx1;
+ listitem_T *dest_li = first_li;
+ for (src_li = tv_list_first(src); src_li != NULL && dest_li != NULL;) {
+ if (value_check_lock(TV_LIST_ITEM_TV(dest_li)->v_lock, varname, TV_CSTRING)) {
+ return FAIL;
+ }
+ src_li = TV_LIST_ITEM_NEXT(src, src_li);
+ if (src_li == NULL || (!empty_idx2 && idx2 == idx)) {
+ break;
+ }
+ dest_li = TV_LIST_ITEM_NEXT(dest, dest_li);
+ idx++;
+ }
+
+ // Assign the List values to the list items.
+ idx = idx1;
+ dest_li = first_li;
+ for (src_li = tv_list_first(src); src_li != NULL;) {
+ assert(dest_li != NULL);
+ if (op != NULL && *op != '=') {
+ eexe_mod_op(TV_LIST_ITEM_TV(dest_li), TV_LIST_ITEM_TV(src_li), op);
+ } else {
+ tv_clear(TV_LIST_ITEM_TV(dest_li));
+ tv_copy(TV_LIST_ITEM_TV(src_li), TV_LIST_ITEM_TV(dest_li));
+ }
+ src_li = TV_LIST_ITEM_NEXT(src, src_li);
+ if (src_li == NULL || (!empty_idx2 && idx2 == idx)) {
+ break;
+ }
+ if (TV_LIST_ITEM_NEXT(dest, dest_li) == NULL) {
+ // Need to add an empty item.
+ tv_list_append_number(dest, 0);
+ // "dest_li" may have become invalid after append, don’t use it.
+ dest_li = tv_list_last(dest); // Valid again.
+ } else {
+ dest_li = TV_LIST_ITEM_NEXT(dest, dest_li);
+ }
+ idx++;
+ }
+ if (src_li != NULL) {
+ emsg(_("E710: List value has more items than target"));
+ return FAIL;
+ }
+ if (empty_idx2
+ ? (dest_li != NULL && TV_LIST_ITEM_NEXT(dest, dest_li) != NULL)
+ : idx != idx2) {
+ emsg(_("E711: List value has not enough items"));
+ return FAIL;
+ }
+ return OK;
+}
+
+/// Flatten up to "maxitems" in "list", starting at "first" to depth "maxdepth".
+/// When "first" is NULL use the first item.
/// Does nothing if "maxdepth" is 0.
///
/// @param[in,out] list List to flatten
/// @param[in] maxdepth Maximum depth that will be flattened
///
/// @return OK or FAIL
-int tv_list_flatten(list_T *list, long maxdepth)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
+void tv_list_flatten(list_T *list, listitem_T *first, int64_t maxitems, int64_t maxdepth)
+ FUNC_ATTR_NONNULL_ARG(1)
{
listitem_T *item;
- listitem_T *to_free;
- int n;
+ int done = 0;
if (maxdepth == 0) {
- return OK;
+ return;
}
- n = 0;
- item = list->lv_first;
- while (item != NULL) {
+ if (first == NULL) {
+ item = list->lv_first;
+ } else {
+ item = first;
+ }
+
+ while (item != NULL && done < maxitems) {
+ listitem_T *next = item->li_next;
+
fast_breakcheck();
if (got_int) {
- return FAIL;
+ return;
}
if (item->li_tv.v_type == VAR_LIST) {
- listitem_T *next = item->li_next;
+ list_T *itemlist = item->li_tv.vval.v_list;
tv_list_drop_items(list, item, item);
- tv_list_extend(list, item->li_tv.vval.v_list, next);
- tv_clear(&item->li_tv);
- to_free = item;
+ tv_list_extend(list, itemlist, next);
- if (item->li_prev == NULL) {
- item = list->lv_first;
- } else {
- item = item->li_prev->li_next;
+ if (maxdepth > 0) {
+ tv_list_flatten(list,
+ item->li_prev == NULL ? list->lv_first : item->li_prev->li_next,
+ itemlist->lv_len, maxdepth - 1);
}
- xfree(to_free);
-
- if (++n >= maxdepth) {
- n = 0;
- item = next;
- }
- } else {
- n = 0;
- item = item->li_next;
+ tv_clear(&item->li_tv);
+ xfree(item);
}
+
+ done++;
+ item = next;
}
- return OK;
}
/// Extend first list with the second
@@ -750,9 +831,67 @@ int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv)
return OK;
}
+static list_T *tv_list_slice(list_T *ol, varnumber_T n1, varnumber_T n2)
+{
+ list_T *l = tv_list_alloc(n2 - n1 + 1);
+ listitem_T *item = tv_list_find(ol, (int)n1);
+ for (; n1 <= n2; n1++) {
+ tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
+ item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item);
+ }
+ return l;
+}
+
+int tv_list_slice_or_index(list_T *list, bool range, varnumber_T n1_arg, varnumber_T n2_arg,
+ bool exclusive, typval_T *rettv, bool verbose)
+{
+ int len = tv_list_len(rettv->vval.v_list);
+ varnumber_T n1 = n1_arg;
+ varnumber_T n2 = n2_arg;
+
+ if (n1 < 0) {
+ n1 = len + n1;
+ }
+ if (n1 < 0 || n1 >= len) {
+ // For a range we allow invalid values and return an empty list.
+ // A list index out of range is an error.
+ if (!range) {
+ if (verbose) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)n1);
+ }
+ return FAIL;
+ }
+ n1 = len;
+ }
+ if (range) {
+ if (n2 < 0) {
+ n2 = len + n2;
+ } else if (n2 >= len) {
+ n2 = len - (exclusive ? 0 : 1);
+ }
+ if (exclusive) {
+ n2--;
+ }
+ if (n2 < 0 || n2 + 1 < n1) {
+ n2 = -1;
+ }
+ list_T *l = tv_list_slice(rettv->vval.v_list, n1, n2);
+ tv_clear(rettv);
+ tv_list_set_ret(rettv, l);
+ } else {
+ // copy the item to "var1" to avoid that freeing the list makes it
+ // invalid.
+ typval_T var1;
+ tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, (int)n1)), &var1);
+ tv_clear(rettv);
+ *rettv = var1;
+ }
+ return OK;
+}
+
typedef struct {
- char_u *s;
- char_u *tofree;
+ char *s;
+ char *tofree;
} Join;
/// Join list into a string, helper function
@@ -785,7 +924,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l, const char *con
sumlen += len;
Join *const p = GA_APPEND_VIA_PTR(Join, join_gap);
- p->tofree = p->s = (char_u *)s;
+ p->tofree = p->s = s;
line_breakcheck();
});
@@ -806,7 +945,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l, const char *con
const Join *const p = ((const Join *)join_gap->ga_data) + i;
if (p->s != NULL) {
- ga_concat(gap, (char *)p->s);
+ ga_concat(gap, p->s);
}
line_breakcheck();
}
@@ -886,8 +1025,8 @@ void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char buf[MB_MAXBYTES + 1];
TV_LIST_ITER_CONST(l, li, {
- buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL;
- ga_concat(&ga, (char *)buf);
+ buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), buf)] = NUL;
+ ga_concat(&ga, buf);
});
ga_append(&ga, NUL);
@@ -905,14 +1044,14 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
return;
}
- long idx = tv_get_number_chk(&argvars[1], &error);
+ int64_t idx = tv_get_number_chk(&argvars[1], &error);
listitem_T *item;
if (error) {
// Type error: do nothing, errmsg already given.
} else if ((item = tv_list_find(l, (int)idx)) == NULL) {
- semsg(_(e_listidx), (int64_t)idx);
+ semsg(_(e_list_index_out_of_range_nr), idx);
} else {
if (argvars[2].v_type == VAR_UNKNOWN) {
// Remove one item, return its value.
@@ -922,11 +1061,11 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
} else {
listitem_T *item2;
// Remove range of items, return list with values.
- long end = tv_get_number_chk(&argvars[2], &error);
+ int64_t end = tv_get_number_chk(&argvars[2], &error);
if (error) {
// Type error: do nothing.
} else if ((item2 = tv_list_find(l, (int)end)) == NULL) {
- semsg(_(e_listidx), (int64_t)end);
+ semsg(_(e_list_index_out_of_range_nr), end);
} else {
int cnt = 0;
@@ -948,18 +1087,6 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg)
}
}
-/// struct storing information about current sort
-typedef struct {
- int item_compare_ic;
- bool item_compare_lc;
- bool item_compare_numeric;
- bool item_compare_numbers;
- bool item_compare_float;
- const char *item_compare_func;
- partial_T *item_compare_partial;
- dict_T *item_compare_selfdict;
- bool item_compare_func_err;
-} sortinfo_T;
static sortinfo_T *sortinfo = NULL;
#define ITEM_COMPARE_FAIL 999
@@ -1027,12 +1154,11 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
if (sortinfo->item_compare_lc) {
res = strcoll(p1, p2);
} else {
- res = sortinfo->item_compare_ic ? STRICMP(p1, p2): strcmp(p1, p2);
+ res = sortinfo->item_compare_ic ? STRICMP(p1, p2) : strcmp(p1, p2);
}
} else {
- double n1, n2;
- n1 = strtod(p1, &p1);
- n2 = strtod(p2, &p2);
+ double n1 = strtod(p1, &p1);
+ double n2 = strtod(p2, &p2);
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
}
@@ -1062,8 +1188,6 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2)
static int item_compare2(const void *s1, const void *s2, bool keep_zero)
{
- ListSortItem *si1, *si2;
- int res;
typval_T rettv;
typval_T argv[3];
const char *func_name;
@@ -1074,13 +1198,13 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
return 0;
}
- si1 = (ListSortItem *)s1;
- si2 = (ListSortItem *)s2;
+ ListSortItem *si1 = (ListSortItem *)s1;
+ ListSortItem *si2 = (ListSortItem *)s2;
if (partial == NULL) {
func_name = sortinfo->item_compare_func;
} else {
- func_name = (const char *)partial_name(partial);
+ func_name = partial_name(partial);
}
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
@@ -1093,7 +1217,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
funcexe.fe_evaluate = true;
funcexe.fe_partial = partial;
funcexe.fe_selfdict = sortinfo->item_compare_selfdict;
- res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
+ int res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
tv_clear(&argv[0]);
tv_clear(&argv[1]);
@@ -1135,149 +1259,188 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
return item_compare2(s1, s2, false);
}
-/// "sort({list})" function
-static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
+/// sort() List "l"
+static void do_sort(list_T *l, sortinfo_T *info)
{
- ListSortItem *ptrs;
- long len;
- long i;
+ const int len = tv_list_len(l);
- // Pointer to current info struct used in compare function. Save and restore
- // the current one for nested calls.
- sortinfo_T info;
- sortinfo_T *old_sortinfo = sortinfo;
- sortinfo = &info;
+ // Make an array with each entry pointing to an item in the List.
+ ListSortItem *ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
- const char *const arg_errmsg = (sort
- ? N_("sort() argument")
- : N_("uniq() argument"));
+ // f_sort(): ptrs will be the list to sort
+ int i = 0;
+ TV_LIST_ITER(l, li, {
+ ptrs[i].item = li;
+ ptrs[i].idx = i;
+ i++;
+ });
- if (argvars[0].v_type != VAR_LIST) {
- semsg(_(e_listarg), sort ? "sort()" : "uniq()");
- } else {
- list_T *const l = argvars[0].vval.v_list;
- if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
- goto theend;
+ info->item_compare_func_err = false;
+ ListSorter item_compare_func = ((info->item_compare_func == NULL
+ && info->item_compare_partial == NULL)
+ ? item_compare_not_keeping_zero
+ : item_compare2_not_keeping_zero);
+
+ // Sort the array with item pointers.
+ qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
+ if (!info->item_compare_func_err) {
+ // Clear the list and append the items in the sorted order.
+ l->lv_first = NULL;
+ l->lv_last = NULL;
+ l->lv_idx_item = NULL;
+ l->lv_len = 0;
+ for (i = 0; i < len; i++) {
+ tv_list_append(l, ptrs[i].item);
}
- tv_list_set_ret(rettv, l);
+ }
+ if (info->item_compare_func_err) {
+ emsg(_("E702: Sort compare function failed"));
+ }
- len = tv_list_len(l);
- if (len <= 1) {
- goto theend; // short list sorts pretty quickly
- }
-
- info.item_compare_ic = false;
- info.item_compare_lc = false;
- info.item_compare_numeric = false;
- info.item_compare_numbers = false;
- info.item_compare_float = false;
- info.item_compare_func = NULL;
- info.item_compare_partial = NULL;
- info.item_compare_selfdict = NULL;
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- // optional second argument: {func}
- if (argvars[1].v_type == VAR_FUNC) {
- info.item_compare_func = (const char *)argvars[1].vval.v_string;
- } else if (argvars[1].v_type == VAR_PARTIAL) {
- info.item_compare_partial = argvars[1].vval.v_partial;
- } else {
- bool error = false;
+ xfree(ptrs);
+}
- i = tv_get_number_chk(&argvars[1], &error);
- if (error) {
- goto theend; // type error; errmsg already given
- }
- if (i == 1) {
- info.item_compare_ic = true;
- } else if (argvars[1].v_type != VAR_NUMBER) {
- info.item_compare_func = tv_get_string(&argvars[1]);
- } else if (i != 0) {
- emsg(_(e_invarg));
- goto theend;
- }
- if (info.item_compare_func != NULL) {
- if (*info.item_compare_func == NUL) {
- // empty string means default sort
- info.item_compare_func = NULL;
- } else if (strcmp(info.item_compare_func, "n") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_numeric = true;
- } else if (strcmp(info.item_compare_func, "N") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_numbers = true;
- } else if (strcmp(info.item_compare_func, "f") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_float = true;
- } else if (strcmp(info.item_compare_func, "i") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_ic = true;
- } else if (strcmp(info.item_compare_func, "l") == 0) {
- info.item_compare_func = NULL;
- info.item_compare_lc = true;
- }
- }
- }
+/// uniq() List "l"
+static void do_uniq(list_T *l, sortinfo_T *info)
+{
+ const int len = tv_list_len(l);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- // optional third argument: {dict}
- if (argvars[2].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
- goto theend;
- }
- info.item_compare_selfdict = argvars[2].vval.v_dict;
- }
- }
+ // Make an array with each entry pointing to an item in the List.
+ ListSortItem *ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
- // Make an array with each entry pointing to an item in the List.
- ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem)));
+ // f_uniq(): ptrs will be a stack of items to remove.
- if (sort) {
- info.item_compare_func_err = false;
- tv_list_item_sort(l, ptrs,
- ((info.item_compare_func == NULL
- && info.item_compare_partial == NULL)
- ? item_compare_not_keeping_zero
- : item_compare2_not_keeping_zero),
- &info.item_compare_func_err);
- if (info.item_compare_func_err) {
- emsg(_("E702: Sort compare function failed"));
- }
+ info->item_compare_func_err = false;
+ ListSorter item_compare_func = ((info->item_compare_func == NULL
+ && info->item_compare_partial == NULL)
+ ? item_compare_keeping_zero
+ : item_compare2_keeping_zero);
+
+ for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)); li != NULL;) {
+ listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
+ if (item_compare_func(&prev_li, &li) == 0) {
+ li = tv_list_item_remove(l, li);
} else {
- ListSorter item_compare_func_ptr;
+ li = TV_LIST_ITEM_NEXT(l, li);
+ }
+ if (info->item_compare_func_err) {
+ emsg(_("E882: Uniq compare function failed"));
+ break;
+ }
+ }
- // f_uniq(): ptrs will be a stack of items to remove.
- info.item_compare_func_err = false;
- if (info.item_compare_func != NULL
- || info.item_compare_partial != NULL) {
- item_compare_func_ptr = item_compare2_keeping_zero;
- } else {
- item_compare_func_ptr = item_compare_keeping_zero;
- }
+ xfree(ptrs);
+}
- for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
- ; li != NULL;) {
- listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
- if (item_compare_func_ptr(&prev_li, &li) == 0) {
- li = tv_list_item_remove(l, li);
- } else {
- li = TV_LIST_ITEM_NEXT(l, li);
- }
- if (info.item_compare_func_err) {
- emsg(_("E882: Uniq compare function failed"));
- break;
- }
+/// Parse the optional arguments to sort() and uniq() and return the values in "info".
+static int parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
+{
+ info->item_compare_ic = false;
+ info->item_compare_lc = false;
+ info->item_compare_numeric = false;
+ info->item_compare_numbers = false;
+ info->item_compare_float = false;
+ info->item_compare_func = NULL;
+ info->item_compare_partial = NULL;
+ info->item_compare_selfdict = NULL;
+
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ return OK;
+ }
+
+ // optional second argument: {func}
+ if (argvars[1].v_type == VAR_FUNC) {
+ info->item_compare_func = argvars[1].vval.v_string;
+ } else if (argvars[1].v_type == VAR_PARTIAL) {
+ info->item_compare_partial = argvars[1].vval.v_partial;
+ } else {
+ bool error = false;
+ int nr = (int)tv_get_number_chk(&argvars[1], &error);
+ if (error) {
+ return FAIL; // type error; errmsg already given
+ }
+ if (nr == 1) {
+ info->item_compare_ic = true;
+ } else if (argvars[1].v_type != VAR_NUMBER) {
+ info->item_compare_func = tv_get_string(&argvars[1]);
+ } else if (nr != 0) {
+ emsg(_(e_invarg));
+ return FAIL;
+ }
+ if (info->item_compare_func != NULL) {
+ if (*info->item_compare_func == NUL) {
+ // empty string means default sort
+ info->item_compare_func = NULL;
+ } else if (strcmp(info->item_compare_func, "n") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_numeric = true;
+ } else if (strcmp(info->item_compare_func, "N") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_numbers = true;
+ } else if (strcmp(info->item_compare_func, "f") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_float = true;
+ } else if (strcmp(info->item_compare_func, "i") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_ic = true;
+ } else if (strcmp(info->item_compare_func, "l") == 0) {
+ info->item_compare_func = NULL;
+ info->item_compare_lc = true;
}
}
+ }
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ // optional third argument: {dict}
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
+ return FAIL;
+ }
+ info->item_compare_selfdict = argvars[2].vval.v_dict;
+ }
+
+ return OK;
+}
- xfree(ptrs);
+/// "sort()" or "uniq()" function
+static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
+{
+ if (argvars[0].v_type != VAR_LIST) {
+ semsg(_(e_listarg), sort ? "sort()" : "uniq()");
+ return;
+ }
+
+ // Pointer to current info struct used in compare function. Save and restore
+ // the current one for nested calls.
+ sortinfo_T info;
+ sortinfo_T *old_sortinfo = sortinfo;
+ sortinfo = &info;
+
+ const char *const arg_errmsg = (sort ? N_("sort() argument") : N_("uniq() argument"));
+ list_T *const l = argvars[0].vval.v_list;
+ if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
+ goto theend;
+ }
+ tv_list_set_ret(rettv, l);
+
+ const int len = tv_list_len(l);
+ if (len <= 1) {
+ goto theend; // short list sorts pretty quickly
+ }
+ if (parse_sort_uniq_args(argvars, &info) == FAIL) {
+ goto theend;
+ }
+
+ if (sort) {
+ do_sort(l, &info);
+ } else {
+ do_uniq(l, &info);
}
theend:
sortinfo = old_sortinfo;
}
-/// "sort"({list})" function
+/// "sort({list})" function
void f_sort(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_sort_uniq(argvars, rettv, true);
@@ -1336,7 +1499,6 @@ void tv_list_reverse(list_T *const l)
if (tv_list_len(l) <= 1) {
return;
}
- list_log(l, NULL, NULL, "reverse");
#define SWAP(a, b) \
do { \
tmp = (a); \
@@ -1354,47 +1516,6 @@ void tv_list_reverse(list_T *const l)
l->lv_idx = l->lv_len - l->lv_idx - 1;
}
-// FIXME Add unit tests for tv_list_item_sort().
-
-/// Sort list using libc qsort
-///
-/// @param[in,out] l List to sort, will be sorted in-place.
-/// @param ptrs Preallocated array of items to sort, must have at least
-/// tv_list_len(l) entries. Should not be initialized.
-/// @param[in] item_compare_func Function used to compare list items.
-/// @param errp Location where information about whether error occurred is
-/// saved by item_compare_func. If boolean there appears to be
-/// true list will not be modified. Must be initialized to false
-/// by the caller.
-void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
- const ListSorter item_compare_func, const bool *errp)
- FUNC_ATTR_NONNULL_ARG(3, 4)
-{
- const int len = tv_list_len(l);
- if (len <= 1) {
- return;
- }
- list_log(l, NULL, NULL, "sort");
- int i = 0;
- TV_LIST_ITER(l, li, {
- ptrs[i].item = li;
- ptrs[i].idx = i;
- i++;
- });
- // Sort the array with item pointers.
- qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
- if (!(*errp)) {
- // Clear the list and append the items in the sorted order.
- l->lv_first = NULL;
- l->lv_last = NULL;
- l->lv_idx_item = NULL;
- l-&g