summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
authorBarrett Ruth <62671086+barrettruth@users.noreply.github.com>2026-04-22 13:36:43 -0400
committerGitHub <noreply@github.com>2026-04-22 13:36:43 -0400
commitfb6aeaba2d3a38c7febd0a39cabd89685de11b9d (patch)
tree438f983b11642e476800f422651de39040db007d /src
parent496374e9513a5cbd0fd37fbd95d07d6306fa432d (diff)
feat(eval): treat Lua string as "blob" in writefile() #39098
Problem: vim.fn.writefile() treats Lua strings as Vimscript strings instead of a "binary clean" string. Solution: Treat Lua-originated strings as blob data.
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval/fs.c23
-rw-r--r--src/nvim/lua/executor.c2
2 files changed, 21 insertions, 4 deletions
diff --git a/src/nvim/eval/fs.c b/src/nvim/eval/fs.c
index 1235d78783..7e4dc1801b 100644
--- a/src/nvim/eval/fs.c
+++ b/src/nvim/eval/fs.c
@@ -42,6 +42,7 @@
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/pos_defs.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -1716,13 +1717,12 @@ write_list_error:
/// @param[in] blob Blob to write.
///
/// @return true on success, or false on failure.
-static bool write_blob(FileDescriptor *const fp, const blob_T *const blob)
+static bool write_data(FileDescriptor *const fp, const char *const data, const size_t len)
FUNC_ATTR_NONNULL_ARG(1)
{
int error = 0;
- const int len = tv_blob_len(blob);
if (len > 0) {
- const ptrdiff_t written = file_write(fp, blob->bv_ga.ga_data, (size_t)len);
+ const ptrdiff_t written = file_write(fp, data, len);
if (written < (ptrdiff_t)len) {
error = (int)written;
goto write_blob_error;
@@ -1738,6 +1738,18 @@ write_blob_error:
return false;
}
+static bool write_blob(FileDescriptor *const fp, const blob_T *const blob)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ return write_data(fp, blob->bv_ga.ga_data, (size_t)tv_blob_len(blob));
+}
+
+static bool write_string(FileDescriptor *const fp, const char *const data)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ return write_data(fp, data, strlen(data));
+}
+
/// "writefile()" function
void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -1753,7 +1765,8 @@ void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
});
- } else if (argvars[0].v_type != VAR_BLOB) {
+ } else if (argvars[0].v_type != VAR_BLOB
+ && !(argvars[0].v_type == VAR_STRING && script_is_lua(current_sctx.sc_sid))) {
semsg(_(e_invarg2),
_("writefile() first argument must be a List or a Blob"));
return;
@@ -1823,6 +1836,8 @@ void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool write_ok;
if (argvars[0].v_type == VAR_BLOB) {
write_ok = write_blob(&fp, argvars[0].vval.v_blob);
+ } else if (argvars[0].v_type == VAR_STRING) {
+ write_ok = write_string(&fp, argvars[0].vval.v_string);
} else {
write_ok = write_list(&fp, argvars[0].vval.v_list, binary);
}
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 1771a01565..02a95d5f6d 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1366,12 +1366,14 @@ int nlua_call(lua_State *lstate)
funcexe.fe_firstline = curwin->w_cursor.lnum;
funcexe.fe_lastline = curwin->w_cursor.lnum;
funcexe.fe_evaluate = true;
+ const sctx_T save_current_sctx = api_set_sctx(LUA_INTERNAL_CALL);
TRY_WRAP(&err, {
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
// (TRY_WRAP) to capture abort-causing non-exception errors.
(void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
});
+ current_sctx = save_current_sctx;
if (!ERROR_SET(&err)) {
nlua_push_typval(lstate, &rettv, 0);