From 9095101743b2606fb1d5f7a5a1216f22d2fb2b4a Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 18 Nov 2020 03:11:00 +0000 Subject: vim-patch:8.1.0735: cannot handle binary data Problem: Cannot handle binary data. Solution: Add the Blob type. (Yasuhiro Matsumoto, closes vim/vim#3638) https://github.com/vim/vim/commit/6e5ea8d2a995b32bbc5972edc4f827b959f2702f Nvim-specific Blob conversions are implemented in future commits. Refactor write_blob() to use a FileDescriptor, as f_writefile() was refactored to use one (does not apply to read_blob()). Use var_check_lock() in f_add() for Blobs from v8.1.0897. Add a modeline to test_blob.vim and fix some doc typos. Include if_perl.txt's VIM::Blob() documentation. Interestingly, this function already worked before this port, as it just returns a Blob string literal, not an actual Blob object. N/A patches for version.c: vim-patch:8.1.0741: viminfo with Blob is not tested Problem: Viminfo with Blob is not tested. Solution: Extend the viminfo test. Fix reading a blob. Fixed storing a special variable value. https://github.com/vim/vim/commit/8c8b8bb56c724cc1bfc3d8520eec33f2d399697c vim-patch:8.1.1022: may use NULL pointer when out of memory Problem: May use NULL pointer when out of memory. (Coverity) Solution: Check for blob_alloc() returning NULL. https://github.com/vim/vim/commit/e142a9467a7f6845a426d8db6efedf246d3c13ac --- src/nvim/eval/funcs.c | 218 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 190 insertions(+), 28 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 801b0f9d1c..dbb00da911 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -321,6 +321,13 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } + } else if (argvars[0].v_type == VAR_BLOB) { + blob_T *const b = argvars[0].vval.v_blob; + if (b != NULL + && !var_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) { + ga_append(&b->bv_ga, (char_u)tv_get_number(&argvars[1])); + tv_copy(&argvars[0], rettv); + } } else { EMSG(_(e_listreq)); } @@ -1874,6 +1881,10 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = argvars[0].vval.v_special == kSpecialVarNull; break; } + case VAR_BLOB: { + n = (tv_blob_len(argvars[0].vval.v_blob) == 0); + break; + } case VAR_UNKNOWN: { internal_error("f_empty(UNKNOWN)"); break; @@ -2791,7 +2802,19 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) typval_T *tv = NULL; bool what_is_dict = false; - if (argvars[0].v_type == VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + const int idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + rettv->v_type = VAR_NUMBER; + if (idx >= tv_blob_len(argvars[0].vval.v_blob)) { + EMSGN(_(e_blobidx), idx); + } else { + rettv->vval.v_number = tv_blob_get(argvars[0].vval.v_blob, idx); + } + } + } else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; @@ -4790,7 +4813,31 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool ic = false; rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + int start = 0; + + if (argvars[2].v_type != VAR_UNKNOWN) { + start = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + } + blob_T *const b = argvars[0].vval.v_blob; + if (b == NULL) { + return; + } + for (idx = start; idx < tv_blob_len(b); idx++) { + typval_T tv; + tv.v_type = VAR_NUMBER; + tv.vval.v_number = tv_blob_get(b, idx); + if (tv_equal(&tv, &argvars[1], ic, false)) { + rettv->vval.v_number = idx; + return; + } + } + return; + } else if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } @@ -4920,7 +4967,37 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; bool error = false; - if (argvars[0].v_type != VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + long before = 0; + const int len = tv_blob_len(argvars[0].vval.v_blob); + + if (argvars[2].v_type != VAR_UNKNOWN) { + before = (long)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // type error; errmsg already given + } + if (before < 0 || before > len) { + EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + } + const int val = tv_get_number_chk(&argvars[1], &error); + if (error) { + return; + } + if (val < 0 || val > 255) { + EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + + ga_grow(&argvars[0].vval.v_blob->bv_ga, 1); + char_u *const p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; + memmove(p + before + 1, p + before, (size_t)len - before); + *(p + before) = val; + argvars[0].vval.v_blob->bv_ga.ga_len++; + + tv_copy(&argvars[0], rettv); + } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), N_("insert() argument"), TV_TRANSLATE)) { @@ -5581,6 +5658,10 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_get_string(&argvars[0])); break; } + case VAR_BLOB: { + rettv->vval.v_number = tv_blob_len(argvars[0].vval.v_blob); + break; + } case VAR_LIST: { rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; @@ -6894,6 +6975,7 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool binary = false; + bool blob = false; FILE *fd; char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1 int io_size = sizeof(buf); @@ -6906,14 +6988,14 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { binary = true; + } else if (strcmp(tv_get_string(&argvars[1]), "B") == 0) { + blob = true; } if (argvars[2].v_type != VAR_UNKNOWN) { maxline = tv_get_number(&argvars[2]); } } - list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); - // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); @@ -6922,6 +7004,18 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + if (blob) { + tv_blob_alloc_ret(rettv); + if (!read_blob(fd, rettv->vval.v_blob)) { + EMSG("cannot read file"); + tv_blob_free(rettv->vval.v_blob); + } + fclose(fd); + return; + } + + list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); + while (maxline < 0 || tv_list_len(l) < maxline) { readlen = (int)fread(buf, 1, io_size, fd); @@ -7190,6 +7284,55 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } + } else if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + idx = (long)tv_get_number_chk(&argvars[1], &error); + + if (!error) { + blob_T *const b = argvars[0].vval.v_blob; + const int len = tv_blob_len(b); + + if (idx < 0) { + // count from the end + idx = len + idx; + } + if (idx < 0 || idx >= len) { + EMSGN(_(e_blobidx), idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + char_u *const p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T)(*(p + idx)); + memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); + b->bv_ga.ga_len--; + } else { + // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (end < 0) { + // count from the end + end = len + end; + } + if (end >= len || idx > end) { + EMSGN(_(e_blobidx), end); + return; + } + blob_T *const blob = tv_blob_alloc(); + blob->bv_ga.ga_len = end - idx + 1; + ga_grow(&blob->bv_ga, end - idx + 1); + + char_u *const p = (char_u *)b->bv_ga.ga_data; + memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + tv_blob_set_ret(rettv, blob); + + memmove(p + idx, p + end + 1, (size_t)(len - end)); + b->bv_ga.ga_len -= end - idx + 1; + } + } } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), @@ -7465,13 +7608,25 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - if (argvars[0].v_type != VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + blob_T *const b = argvars[0].vval.v_blob; + const int len = tv_blob_len(b); + + for (int i = 0; i < len / 2; i++) { + const char_u tmp = tv_blob_get(b, i); + tv_blob_set(b, i, tv_blob_get(b, len - i - 1)); + tv_blob_set(b, len - i - 1, tmp); + } + tv_blob_set_ret(rettv, b); + } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("reverse() argument"), TV_TRANSLATE)) { - tv_list_reverse(l); - tv_list_set_ret(rettv, l); + } else { + list_T *const l = argvars[0].vval.v_list; + if (!var_check_lock(tv_list_locked(l), N_("reverse() argument"), + TV_TRANSLATE)) { + tv_list_reverse(l); + tv_list_set_ret(rettv, l); + } } } @@ -11291,15 +11446,16 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n = -1; switch (argvars[0].v_type) { - case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; - case VAR_STRING: n = VAR_TYPE_STRING; break; + case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; + case VAR_STRING: n = VAR_TYPE_STRING; break; case VAR_PARTIAL: - case VAR_FUNC: n = VAR_TYPE_FUNC; break; - case VAR_LIST: n = VAR_TYPE_LIST; break; - case VAR_DICT: n = VAR_TYPE_DICT; break; - case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; - case VAR_BOOL: n = VAR_TYPE_BOOL; break; - case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break; + case VAR_FUNC: n = VAR_TYPE_FUNC; break; + case VAR_LIST: n = VAR_TYPE_LIST; break; + case VAR_DICT: n = VAR_TYPE_DICT; break; + case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; + case VAR_BOOL: n = VAR_TYPE_BOOL; break; + case VAR_SPECIAL: n = VAR_TYPE_SPECIAL; break; + case VAR_BLOB: n = VAR_TYPE_BLOB; break; case VAR_UNKNOWN: { internal_error("f_type(UNKNOWN)"); break; @@ -11678,16 +11834,16 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "writefile()"); + if (argvars[0].v_type == VAR_LIST) { + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) { + return; + } + }); + } else if (argvars[0].v_type != VAR_BLOB) { + EMSG2(_(e_invarg2), "writefile()"); return; } - const list_T *const list = argvars[0].vval.v_list; - TV_LIST_ITER_CONST(list, li, { - if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) { - return; - } - }); bool binary = false; bool append = false; @@ -11727,7 +11883,13 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsgf(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error)); } else { - if (write_list(&fp, list, binary)) { + bool write_ok; + if (argvars[0].v_type == VAR_BLOB) { + write_ok = write_blob(&fp, argvars[0].vval.v_blob); + } else { + write_ok = write_list(&fp, argvars[0].vval.v_list, binary); + } + if (write_ok) { rettv->vval.v_number = 0; } if ((error = file_close(&fp, do_fsync)) != 0) { -- cgit From d346ac536fbb7716e6a0d0f5ca7af9c39ec95de0 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 24 Nov 2020 21:37:52 +0000 Subject: vim-patch:8.1.0742: not all Blob operations are tested Problem: Not all Blob operations are tested. Solution: Add more testing for Blob. https://github.com/vim/vim/commit/05500ece6282407f9f7227aaf564e24147326863 Test_readfile_binary is already ported. --- src/nvim/eval/funcs.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index dbb00da911..3c25d76866 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -325,8 +325,13 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) blob_T *const b = argvars[0].vval.v_blob; if (b != NULL && !var_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) { - ga_append(&b->bv_ga, (char_u)tv_get_number(&argvars[1])); - tv_copy(&argvars[0], rettv); + bool error = false; + const varnumber_T n = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + ga_append(&b->bv_ga, (int)n); + tv_copy(&argvars[0], rettv); + } } } else { EMSG(_(e_listreq)); @@ -4827,6 +4832,12 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (b == NULL) { return; } + if (start < 0) { + start = tv_blob_len(b) + start; + if (start < 0) { + start = 0; + } + } for (idx = start; idx < tv_blob_len(b); idx++) { typval_T tv; tv.v_type = VAR_NUMBER; -- cgit From c1b8731ece81cdbc43fbb6050eaf12e84abdc4b0 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 25 Nov 2020 17:27:39 +0000 Subject: vim-patch:8.1.0755: error message for get() on a Blob with invalid index Problem: Error message for get() on a Blob with invalid index. Solution: Return an empty Blob, like get() on a List does. https://github.com/vim/vim/commit/2ea773b468a1143214c2f12b91ab5e1e7abb4a14 --- src/nvim/eval/funcs.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 3c25d76866..e804029187 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2809,14 +2809,18 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_BLOB) { bool error = false; - const int idx = tv_get_number_chk(&argvars[1], &error); + int idx = tv_get_number_chk(&argvars[1], &error); if (!error) { rettv->v_type = VAR_NUMBER; - if (idx >= tv_blob_len(argvars[0].vval.v_blob)) { - EMSGN(_(e_blobidx), idx); + if (idx < 0) { + idx = tv_blob_len(argvars[0].vval.v_blob) + idx; + } + if (idx < 0 || idx >= tv_blob_len(argvars[0].vval.v_blob)) { + rettv->vval.v_number = -1; } else { rettv->vval.v_number = tv_blob_get(argvars[0].vval.v_blob, idx); + tv = rettv; } } } else if (argvars[0].v_type == VAR_LIST) { -- cgit From c57132ec2a9c16facff7858c8610c3206398fe7e Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 2 Dec 2020 18:37:18 +0000 Subject: vim-patch:8.1.0793: incorrect error messages for functions that take a Blob Problem: Incorrect error messages for functions that now take a Blob argument. Solution: Adjust the error messages. (Dominique Pelle, closes vim/vim#3846) https://github.com/vim/vim/commit/0d17f0d1c09fa6db306336695ba646c21ea24909 --- src/nvim/eval/funcs.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e804029187..20affdf529 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -96,6 +96,7 @@ PRAGMA_DIAG_POP static char *e_listarg = N_("E686: Argument of %s must be a List"); +static char *e_listblobarg = N_("E898: Argument of %s must be a List or Blob"); static char *e_invalwindow = N_("E957: Invalid window number"); /// Dummy va_list for passing to vim_snprintf @@ -334,7 +335,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else { - EMSG(_(e_listreq)); + EMSG(_(e_listblobreq)); } } @@ -2884,7 +2885,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else { - EMSG2(_(e_listdictarg), "get()"); + EMSG2(_(e_listdictblobarg), "get()"); } if (tv == NULL) { @@ -4853,7 +4854,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) } return; } else if (argvars[0].v_type != VAR_LIST) { - EMSG(_(e_listreq)); + EMSG(_(e_listblobreq)); return; } list_T *const l = argvars[0].vval.v_list; @@ -5013,7 +5014,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_copy(&argvars[0], rettv); } else if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "insert()"); + EMSG2(_(e_listblobarg), "insert()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), N_("insert() argument"), TV_TRANSLATE)) { long before = 0; @@ -7349,7 +7350,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listdictarg), "remove()"); + EMSG2(_(e_listdictblobarg), "remove()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), arg_errmsg, TV_TRANSLATE)) { bool error = false; @@ -7634,7 +7635,7 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } tv_blob_set_ret(rettv, b); } else if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "reverse()"); + EMSG2(_(e_listblobarg), "reverse()"); } else { list_T *const l = argvars[0].vval.v_list; if (!var_check_lock(tv_list_locked(l), N_("reverse() argument"), -- cgit From e140eec441006d0a05abcba49c6d9a0482609216 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 2 Dec 2020 19:01:48 +0000 Subject: vim-patch:8.1.0797: error E898 is used twice Problem: Error E898 is used twice. Solution: Rename the Blob error to E899. (closes vim/vim#3853) https://github.com/vim/vim/commit/bf821bccf18453b01d25bee53e4954b02a5dd0e6 --- src/nvim/eval/funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 20affdf529..2d58ce1d04 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -96,7 +96,7 @@ PRAGMA_DIAG_POP static char *e_listarg = N_("E686: Argument of %s must be a List"); -static char *e_listblobarg = N_("E898: Argument of %s must be a List or Blob"); +static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob"); static char *e_invalwindow = N_("E957: Invalid window number"); /// Dummy va_list for passing to vim_snprintf -- cgit From ba34afb37849a8bdf5d601ced7f3c139a80cd396 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 2 Aug 2021 15:27:06 +0100 Subject: vim-patch:8.2.0404: writefile() error does not give a hint Problem: Writefile() error does not give a hint. Solution: Add remark about first argument. https://github.com/vim/vim/commit/18a2b87ca27c378a555b20f14a284d2ce3511427 --- src/nvim/eval/funcs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 2d58ce1d04..6dd7a92b61 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11857,7 +11857,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } }); } else if (argvars[0].v_type != VAR_BLOB) { - EMSG2(_(e_invarg2), "writefile()"); + EMSG2(_(e_invarg2), + _("writefile() first argument must be a List or a Blob")); return; } -- cgit From 9b5c9dbfa7074789dd77201ff7893690073e0f0a Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 25 Nov 2020 16:02:10 +0000 Subject: vim-patch:8.2.0521: crash when reading a blob fails Problem: Crash when reading a blob fails. Solution: Avoid keeping a pointer to a freed blob object. (Dominique Pelle, closes vim/vim#5890) Adjust error messages. https://github.com/vim/vim/commit/15352dc6ec43fd50cc3be4f4fd1ad74d5619da20 --- src/nvim/eval/funcs.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 6dd7a92b61..2b844af03f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7015,6 +7015,11 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); + + if (os_isdir((const char_u *)fname)) { + EMSG2(_(e_isadir2), fname); + return; + } if (*fname == NUL || (fd = os_fopen(fname, READBIN)) == NULL) { EMSG2(_(e_notopen), *fname == NUL ? _("") : fname); return; @@ -7023,8 +7028,10 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (blob) { tv_blob_alloc_ret(rettv); if (!read_blob(fd, rettv->vval.v_blob)) { - EMSG("cannot read file"); + EMSG2(_(e_notread), fname); + // An empty blob is returned on error. tv_blob_free(rettv->vval.v_blob); + rettv->vval.v_blob = NULL; } fclose(fd); return; -- cgit From 19232593ba495aa7ac6347b45a9a78d218e6e871 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 29 Jul 2021 22:32:22 +0100 Subject: fix(f_insert): partially port v8.2.0634 Fixes a crash in f_insert() when inserting into a NULL blob. Include blob-related test changes and some other simple changes. --- src/nvim/eval/funcs.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 2b844af03f..b3afa332f8 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4984,6 +4984,10 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[0].v_type == VAR_BLOB) { + if (argvars[0].vval.v_blob == NULL) { + return; + } + long before = 0; const int len = tv_blob_len(argvars[0].vval.v_blob); -- cgit From 3d6bb8b3fbe21d8a7a4ba975682eb9a4e5170859 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 29 Jul 2021 22:47:33 +0100 Subject: fix(f_remove): partially port v8.2.2779 Fixes remove() copying one extra byte after the end of a Blob's buffer. Can't be fully ported as the change is from blob_remove(), which hasn't been ported yet. --- src/nvim/eval/funcs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b3afa332f8..660c069e7f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7334,7 +7334,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); b->bv_ga.ga_len--; } else { - // Remove range of items, return list with values. + // Remove range of items, return blob with values. end = (long)tv_get_number_chk(&argvars[2], &error); if (error) { return; @@ -7356,7 +7356,9 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) (size_t)(end - idx + 1)); tv_blob_set_ret(rettv, blob); - memmove(p + idx, p + end + 1, (size_t)(len - end)); + if (len - end - 1 > 0) { + memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); + } b->bv_ga.ga_len -= end - idx + 1; } } -- cgit From e88961943b85434ceebff6a31089c635ac39cd66 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 1 Aug 2021 18:27:41 +0100 Subject: fix(eval): partially port v8.2.3284 These were issues that I found while porting that I fixed upstream. :^) Very little of the patch can be exactly ported as we're a bit behind on dependant patches (we also can't use the exact :for emsg, as we don't support iterating over Strings yet), so just translate the fixes as best as we can for now. Include latest relevant doc changes from: - v8.1.0815 - v8.2.2658 --- src/nvim/eval/funcs.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 660c069e7f..ec13aa7fbd 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4984,12 +4984,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[0].v_type == VAR_BLOB) { - if (argvars[0].vval.v_blob == NULL) { + blob_T *const b = argvars[0].vval.v_blob; + + if (b == NULL + || var_check_lock(b->bv_lock, N_("insert() argument"), + TV_TRANSLATE)) { return; } long before = 0; - const int len = tv_blob_len(argvars[0].vval.v_blob); + const int len = tv_blob_len(b); if (argvars[2].v_type != VAR_UNKNOWN) { before = (long)tv_get_number_chk(&argvars[2], &error); @@ -5010,11 +5014,11 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ga_grow(&argvars[0].vval.v_blob->bv_ga, 1); - char_u *const p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; + ga_grow(&b->bv_ga, 1); + char_u *const p = (char_u *)b->bv_ga.ga_data; memmove(p + before + 1, p + before, (size_t)len - before); *(p + before) = val; - argvars[0].vval.v_blob->bv_ga.ga_len++; + b->bv_ga.ga_len++; tv_copy(&argvars[0], rettv); } else if (argvars[0].v_type != VAR_LIST) { @@ -7312,11 +7316,16 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else if (argvars[0].v_type == VAR_BLOB) { + blob_T *const b = argvars[0].vval.v_blob; + + if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { + return; + } + bool error = false; idx = (long)tv_get_number_chk(&argvars[1], &error); if (!error) { - blob_T *const b = argvars[0].vval.v_blob; const int len = tv_blob_len(b); if (idx < 0) { -- cgit From 7e9ea083213c3eb195fbf206f12a3aac1fa29033 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 29 Jul 2021 12:26:13 +0100 Subject: feat(f_chansend): support Blob data argument --- src/nvim/eval/funcs.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index ec13aa7fbd..e83e0ac0cc 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -972,7 +972,17 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ptrdiff_t input_len = 0; - char *input = save_tv_as_string(&argvars[1], &input_len, false); + char *input = NULL; + if (argvars[1].v_type == VAR_BLOB) { + const blob_T *const b = argvars[1].vval.v_blob; + input_len = tv_blob_len(b); + if (input_len > 0) { + input = xmemdup(b->bv_ga.ga_data, input_len); + } + } else { + input = save_tv_as_string(&argvars[1], &input_len, false); + } + if (!input) { // Either the error has been handled by save_tv_as_string(), // or there is no input to send. -- cgit From 5fdf741f77c3b6ebed9b5cdc0e9d1043080beb3c Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 29 Jul 2021 14:05:55 +0100 Subject: feat(f_msgpackdump): support dumping to Blob --- src/nvim/eval/funcs.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e83e0ac0cc..a2ea74b19b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6506,9 +6506,16 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); list_T *const list = argvars[0].vval.v_list; - msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); + msgpack_packer *packer; + if (argvars[1].v_type != VAR_UNKNOWN + && strequal(tv_get_string(&argvars[1]), "B")) { + tv_blob_alloc_ret(rettv); + packer = msgpack_packer_new(rettv->vval.v_blob, &encode_blob_write); + } else { + packer = msgpack_packer_new(tv_list_alloc_ret(rettv, kListLenMayKnow), + &encode_list_write); + } const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; @@ -6516,11 +6523,11 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER(list, li, { vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx); idx++; - if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { + if (encode_vim_to_msgpack(packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { break; } }); - msgpack_packer_free(lpacker); + msgpack_packer_free(packer); } /// "msgpackparse" function -- cgit From e53b71627fb84025fb658a44ee5f90adf276cde7 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 29 Jul 2021 17:46:45 +0100 Subject: feat(f_msgpackparse): support parsing from Blob Note that it is not possible for msgpack_unpack_next() and msgpack_unpacker_next() to return MSGPACK_UNPACK_EXTRA_BYTES, so it should be fine to abort() on that. Lua 5.1 doesn't support string hex escapes (\xXX) like VimL does (though LuaJIT does), so convert them to decimal escapes (\DDD) in tests. --- src/nvim/eval/funcs.c | 117 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 34 deletions(-) (limited to 'src/nvim/eval/funcs.c') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index a2ea74b19b..fda33fdfdd 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6530,16 +6530,43 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) msgpack_packer_free(packer); } -/// "msgpackparse" function -static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static int msgpackparse_convert_item(const msgpack_object data, + const msgpack_unpack_return result, + list_T *const ret_list, + const bool fail_if_incomplete) FUNC_ATTR_NONNULL_ALL { - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "msgpackparse()"); - return; + switch (result) { + case MSGPACK_UNPACK_PARSE_ERROR: + EMSG2(_(e_invarg2), "Failed to parse msgpack string"); + return FAIL; + case MSGPACK_UNPACK_NOMEM_ERROR: + EMSG(_(e_outofmem)); + return FAIL; + case MSGPACK_UNPACK_CONTINUE: + if (fail_if_incomplete) { + EMSG2(_(e_invarg2), "Incomplete msgpack string"); + return FAIL; + } + return NOTDONE; + case MSGPACK_UNPACK_SUCCESS: { + typval_T tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(data, &tv) == FAIL) { + EMSG2(_(e_invarg2), "Failed to convert msgpack string"); + return FAIL; + } + tv_list_append_owned_tv(ret_list, tv); + return OK; + } + default: + abort(); } - list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); - const list_T *const list = argvars[0].vval.v_list; +} + +static void msgpackparse_unpack_list(const list_T *const list, + list_T *const ret_list) + FUNC_ATTR_NONNULL_ARG(2) +{ if (tv_list_len(list) == 0) { return; } @@ -6558,43 +6585,28 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) do { if (!msgpack_unpacker_reserve_buffer(unpacker, IOSIZE)) { EMSG(_(e_outofmem)); - goto f_msgpackparse_exit; + goto end; } size_t read_bytes; const int rlret = encode_read_from_list( &lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes); if (rlret == FAIL) { EMSG2(_(e_invarg2), "List item is not a string"); - goto f_msgpackparse_exit; + goto end; } msgpack_unpacker_buffer_consumed(unpacker, read_bytes); if (read_bytes == 0) { break; } while (unpacker->off < unpacker->used) { - const msgpack_unpack_return result = msgpack_unpacker_next(unpacker, - &unpacked); - if (result == MSGPACK_UNPACK_PARSE_ERROR) { - EMSG2(_(e_invarg2), "Failed to parse msgpack string"); - goto f_msgpackparse_exit; - } - if (result == MSGPACK_UNPACK_NOMEM_ERROR) { - EMSG(_(e_outofmem)); - goto f_msgpackparse_exit; - } - if (result == MSGPACK_UNPACK_SUCCESS) { - typval_T tv = { .v_type = VAR_UNKNOWN }; - if (msgpack_to_vim(unpacked.data, &tv) == FAIL) { - EMSG2(_(e_invarg2), "Failed to convert msgpack string"); - goto f_msgpackparse_exit; - } - tv_list_append_owned_tv(ret_list, tv); - } - if (result == MSGPACK_UNPACK_CONTINUE) { - if (rlret == OK) { - EMSG2(_(e_invarg2), "Incomplete msgpack string"); - } + const msgpack_unpack_return result + = msgpack_unpacker_next(unpacker, &unpacked); + const int conv_result = msgpackparse_convert_item(unpacked.data, result, + ret_list, rlret == OK); + if (conv_result == NOTDONE) { break; + } else if (conv_result == FAIL) { + goto end; } } if (rlret == OK) { @@ -6602,10 +6614,47 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } while (true); -f_msgpackparse_exit: - msgpack_unpacked_destroy(&unpacked); +end: msgpack_unpacker_free(unpacker); - return; + msgpack_unpacked_destroy(&unpacked); +} + +static void msgpackparse_unpack_blob(const blob_T *const blob, + list_T *const ret_list) + FUNC_ATTR_NONNULL_ARG(2) +{ + const int len = tv_blob_len(blob); + if (len == 0) { + return; + } + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + for (size_t offset = 0; offset < (size_t)len;) { + const msgpack_unpack_return result + = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, len, &offset); + if (msgpackparse_convert_item(unpacked.data, result, ret_list, true) + != OK) { + break; + } + } + + msgpack_unpacked_destroy(&unpacked); +} + +/// "msgpackparse" function +static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) + FUNC_ATTR_NONNULL_ALL +{ + if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { + EMSG2(_(e_listblobarg), "msgpackparse()"); + return; + } + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + if (argvars[0].v_type == VAR_LIST) { + msgpackparse_unpack_list(argvars[0].vval.v_list, ret_list); + } else { + msgpackparse_unpack_blob(argvars[0].vval.v_blob, ret_list); + } } /* -- cgit