vim-patch:9.0.0795: readblob() always reads the whole file

Problem:    readblob() always reads the whole file.
Solution:   Add arguments to read part of the file. (Ken Takata,
            closes vim/vim#11402)

11df3aeee5

Remove trailing whitespace in test as done in patch 9.0.1257.
Move the help for rand() before range().

Co-authored-by: K.Takata <kentkt@csc.jp>
This commit is contained in:
zeertzjq
2023-02-28 19:04:32 +08:00
parent 3b92776226
commit bfa0bc7df0
5 changed files with 110 additions and 42 deletions

View File

@@ -5859,27 +5859,57 @@ write_blob_error:
return false;
}
/// Read a blob from a file `fd`.
/// Read blob from file "fd".
/// Caller has allocated a blob in "rettv".
///
/// @param[in] fd File to read from.
/// @param[in,out] blob Blob to write to.
/// @param[in,out] rettv Blob to write to.
/// @param[in] offset Read the file from the specified offset.
/// @param[in] size Read the specified size, or -1 if no limit.
///
/// @return true on success, or false on failure.
bool read_blob(FILE *const fd, blob_T *const blob)
/// @return OK on success, or FAIL on failure.
int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg)
FUNC_ATTR_NONNULL_ALL
{
blob_T *const blob = rettv->vval.v_blob;
FileInfo file_info;
if (!os_fileinfo_fd(fileno(fd), &file_info)) {
return false;
return FAIL; // can't read the file, error
}
const int size = (int)os_fileinfo_size(&file_info);
ga_grow(&blob->bv_ga, size);
blob->bv_ga.ga_len = size;
int whence;
off_T size = size_arg;
if (offset >= 0) {
if (size == -1) {
// size may become negative, checked below
size = (off_T)os_fileinfo_size(&file_info) - offset;
}
whence = SEEK_SET;
} else {
if (size == -1) {
size = -offset;
}
whence = SEEK_END;
}
// Trying to read bytes that aren't there results in an empty blob, not an
// error.
if (size < 0 || size > (off_T)os_fileinfo_size(&file_info)) {
return OK;
}
if (vim_fseek(fd, offset, whence) != 0) {
return OK;
}
ga_grow(&blob->bv_ga, (int)size);
blob->bv_ga.ga_len = (int)size;
if (fread(blob->bv_ga.ga_data, 1, (size_t)blob->bv_ga.ga_len, fd)
< (size_t)blob->bv_ga.ga_len) {
return false;
// An empty blob is returned on error.
tv_blob_free(rettv->vval.v_blob);
rettv->vval.v_blob = NULL;
return FAIL;
}
return true;
return OK;
}
/// Saves a typval_T as a string.

View File

@@ -296,7 +296,7 @@ return {
perleval={args=1, base=1},
rand={args={0, 1}, base=1},
range={args={1, 3}, base=1},
readblob={args=1, base=1},
readblob={args={1, 3}, base=1},
readdir={args={1, 2}, base=1},
readfile={args={1, 3}, base=1},
reduce={args={2, 3}, base=1},

View File

@@ -5592,15 +5592,24 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
ptrdiff_t prevlen = 0; // length of data in prev
ptrdiff_t prevsize = 0; // size of prev buffer
int64_t maxline = MAXLNUM;
off_T offset = 0;
off_T size = -1;
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]);
if (always_blob) {
offset = (off_T)tv_get_number(&argvars[1]);
if (argvars[2].v_type != VAR_UNKNOWN) {
size = (off_T)tv_get_number(&argvars[2]);
}
} else {
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]);
}
}
}
@@ -5619,11 +5628,8 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
if (blob) {
tv_blob_alloc_ret(rettv);
if (!read_blob(fd, rettv->vval.v_blob)) {
if (read_blob(fd, rettv, offset, size) == FAIL) {
semsg(_(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;

View File

@@ -439,10 +439,29 @@ func Test_blob_read_write()
call writefile(b, 'Xblob')
VAR br = readfile('Xblob', 'B')
call assert_equal(b, br)
VAR br2 = readblob('Xblob')
call assert_equal(b, br2)
VAR br3 = readblob('Xblob', 1)
call assert_equal(b[1 :], br3)
VAR br4 = readblob('Xblob', 1, 2)
call assert_equal(b[1 : 2], br4)
VAR br5 = readblob('Xblob', -3)
call assert_equal(b[-3 :], br5)
VAR br6 = readblob('Xblob', -3, 2)
call assert_equal(b[-3 : -2], br6)
VAR br1e = readblob('Xblob', 10000)
call assert_equal(0z, br1e)
VAR br2e = readblob('Xblob', -10000)
call assert_equal(0z, br2e)
call delete('Xblob')
END
call CheckLegacyAndVim9Success(lines)
call assert_fails("call readblob('notexist')", 'E484:')
" TODO: How do we test for the E485 error?
" This was crashing when calling readfile() with a directory.
call assert_fails("call readfile('.', 'B')", 'E17: "." is a directory')
endfunc