New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RDY] Refactor writefile() and create more tests for it #6111
Conversation
According to the documentation fsync() may fail with EROFS or EINVAL if “file descriptor is bound to a special file which does not support synchronization” (e.g. /dev/stderr). This condition is completely valid in this case since main point of `file_fsync()` is dumping buffered input.
src/nvim/eval.c
Outdated
@@ -18153,17 +18178,25 @@ static void f_writefile(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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"binary mode" isn't relevant now
eq('a\0\0\0b', read_file(fname)) | ||
end) | ||
|
||
it('correctly overwrites file', function() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: "correctly" and "successfully" are redundant in test descriptions, it should be assumed that we (almost) always want correct behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I usually use such word because previously it did overwrite file/etc, but did this incorrectly: e.g. it is some kind of regression test.
break; | ||
const ptrdiff_t written = file_write(fp, "\n", 1); | ||
if (written < 0) { | ||
error = (int)written; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A previous error could be overwritten.
Previously it could attempt to write trailing newline before returning.
} | ||
if ((error = file_free(fp)) != 0) { | ||
emsgf(_("E80: Error when closing file %s: %s"), | ||
fname, os_strerror(error)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of fname
here looks like UB, because get_tv_string()
uses a static buffer for Number variables.
fname
is defined as a const char *
, but the buffer content could be changed by the previous call to write_list()
if the list contains a Number variable.
1. When calling writefile(list, fname, []) do not show error message twice. 2. Do not allow file name to be overwritten for writefile([1], 2). 3. Do not show “Can’t open file with an empty name” error after error like “using Float as a String” when type of the second argument is not correct. 4. Do not give multiple error messages and still continue for code like `writefile(["test", [], [], [], "tset"])`. Note that to fix 4. ideally I need tv_check_str_or_nr which is currently present in two PRs: neovim#6114 and neovim#5119. I would want to avoid copying this function into a yet another PR. Ref vim/vim#1476.
Should be ready now. |
Ping. |
if (write_list(fd, argvars[0].vval.v_list, binary) == false) { | ||
rettv->vval.v_number = -1; | ||
if (write_list(fp, argvars[0].vval.v_list, binary)) { | ||
rettv->vval.v_number = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be too early to signal a successful write. Flushing of the remaining RBuffer
could fail in file_free()
and writing to the file could be incomplete.
This way success/failure return from this function is more precise.
Looks like another random failure, restarting.
|
CI passed, should be ready again now. |
os.remove(fname) | ||
os.remove(dfname) | ||
lfs.rmdir(ddname) | ||
lfs.rmdir(dname) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
helpers.rmdir() is more robust, in particular for the Windows build. (Would allow omitting the os.remove calls above, too)
Part of #5119