mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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:
parent
3b92776226
commit
bfa0bc7df0
@ -359,7 +359,8 @@ pyxeval({expr}) any evaluate |python_x| expression
|
|||||||
rand([{expr}]) Number get pseudo-random number
|
rand([{expr}]) Number get pseudo-random number
|
||||||
range({expr} [, {max} [, {stride}]])
|
range({expr} [, {max} [, {stride}]])
|
||||||
List items from {expr} to {max}
|
List items from {expr} to {max}
|
||||||
readblob({fname}) Blob read a |Blob| from {fname}
|
readblob({fname} [, {offset} [, {size}]])
|
||||||
|
Blob read a |Blob| from {fname}
|
||||||
readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
|
readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
|
||||||
readfile({fname} [, {type} [, {max}]])
|
readfile({fname} [, {type} [, {max}]])
|
||||||
List get list of lines from file {fname}
|
List get list of lines from file {fname}
|
||||||
@ -6110,6 +6111,25 @@ pyxeval({expr}) *pyxeval()*
|
|||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
GetExpr()->pyxeval()
|
GetExpr()->pyxeval()
|
||||||
<
|
<
|
||||||
|
rand([{expr}]) *rand()*
|
||||||
|
Return a pseudo-random Number generated with an xoshiro128**
|
||||||
|
algorithm using seed {expr}. The returned number is 32 bits,
|
||||||
|
also on 64 bits systems, for consistency.
|
||||||
|
{expr} can be initialized by |srand()| and will be updated by
|
||||||
|
rand(). If {expr} is omitted, an internal seed value is used
|
||||||
|
and updated.
|
||||||
|
Returns -1 if {expr} is invalid.
|
||||||
|
|
||||||
|
Examples: >
|
||||||
|
:echo rand()
|
||||||
|
:let seed = srand()
|
||||||
|
:echo rand(seed)
|
||||||
|
:echo rand(seed) % 16 " random number 0 - 15
|
||||||
|
<
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
seed->rand()
|
||||||
|
<
|
||||||
|
|
||||||
*E726* *E727*
|
*E726* *E727*
|
||||||
range({expr} [, {max} [, {stride}]]) *range()*
|
range({expr} [, {max} [, {stride}]]) *range()*
|
||||||
Returns a |List| with Numbers:
|
Returns a |List| with Numbers:
|
||||||
@ -6132,29 +6152,22 @@ range({expr} [, {max} [, {stride}]]) *range()*
|
|||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
GetExpr()->range()
|
GetExpr()->range()
|
||||||
<
|
<
|
||||||
rand([{expr}]) *rand()*
|
|
||||||
Return a pseudo-random Number generated with an xoshiro128**
|
|
||||||
algorithm using seed {expr}. The returned number is 32 bits,
|
|
||||||
also on 64 bits systems, for consistency.
|
|
||||||
{expr} can be initialized by |srand()| and will be updated by
|
|
||||||
rand(). If {expr} is omitted, an internal seed value is used
|
|
||||||
and updated.
|
|
||||||
Returns -1 if {expr} is invalid.
|
|
||||||
|
|
||||||
Examples: >
|
readblob({fname} [, {offset} [, {size}]]) *readblob()*
|
||||||
:echo rand()
|
|
||||||
:let seed = srand()
|
|
||||||
:echo rand(seed)
|
|
||||||
:echo rand(seed) % 16 " random number 0 - 15
|
|
||||||
<
|
|
||||||
Can also be used as a |method|: >
|
|
||||||
seed->rand()
|
|
||||||
<
|
|
||||||
|
|
||||||
readblob({fname}) *readblob()*
|
|
||||||
Read file {fname} in binary mode and return a |Blob|.
|
Read file {fname} in binary mode and return a |Blob|.
|
||||||
|
If {offset} is specified, read the file from the specified
|
||||||
|
offset. If it is a negative value, it is used as an offset
|
||||||
|
from the end of the file. E.g., to read the last 12 bytes: >
|
||||||
|
readblob('file.bin', -12)
|
||||||
|
< If {size} is specified, only the specified size will be read.
|
||||||
|
E.g. to read the first 100 bytes of a file: >
|
||||||
|
readblob('file.bin', 0, 100)
|
||||||
|
< If {size} is -1 or omitted, the whole data starting from
|
||||||
|
{offset} will be read.
|
||||||
When the file can't be opened an error message is given and
|
When the file can't be opened an error message is given and
|
||||||
the result is an empty |Blob|.
|
the result is an empty |Blob|.
|
||||||
|
When trying to read bytes beyond the end of the file the
|
||||||
|
result is an empty blob.
|
||||||
Also see |readfile()| and |writefile()|.
|
Also see |readfile()| and |writefile()|.
|
||||||
|
|
||||||
|
|
||||||
|
@ -5859,27 +5859,57 @@ write_blob_error:
|
|||||||
return false;
|
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] 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.
|
/// @return OK on success, or FAIL on failure.
|
||||||
bool read_blob(FILE *const fd, blob_T *const blob)
|
int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
|
blob_T *const blob = rettv->vval.v_blob;
|
||||||
FileInfo file_info;
|
FileInfo file_info;
|
||||||
if (!os_fileinfo_fd(fileno(fd), &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);
|
int whence;
|
||||||
blob->bv_ga.ga_len = size;
|
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)
|
if (fread(blob->bv_ga.ga_data, 1, (size_t)blob->bv_ga.ga_len, fd)
|
||||||
< (size_t)blob->bv_ga.ga_len) {
|
< (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.
|
/// Saves a typval_T as a string.
|
||||||
|
@ -296,7 +296,7 @@ return {
|
|||||||
perleval={args=1, base=1},
|
perleval={args=1, base=1},
|
||||||
rand={args={0, 1}, base=1},
|
rand={args={0, 1}, base=1},
|
||||||
range={args={1, 3}, 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},
|
readdir={args={1, 2}, base=1},
|
||||||
readfile={args={1, 3}, base=1},
|
readfile={args={1, 3}, base=1},
|
||||||
reduce={args={2, 3}, base=1},
|
reduce={args={2, 3}, base=1},
|
||||||
|
@ -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 prevlen = 0; // length of data in prev
|
||||||
ptrdiff_t prevsize = 0; // size of prev buffer
|
ptrdiff_t prevsize = 0; // size of prev buffer
|
||||||
int64_t maxline = MAXLNUM;
|
int64_t maxline = MAXLNUM;
|
||||||
|
off_T offset = 0;
|
||||||
|
off_T size = -1;
|
||||||
|
|
||||||
if (argvars[1].v_type != VAR_UNKNOWN) {
|
if (argvars[1].v_type != VAR_UNKNOWN) {
|
||||||
if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
|
if (always_blob) {
|
||||||
binary = true;
|
offset = (off_T)tv_get_number(&argvars[1]);
|
||||||
} else if (strcmp(tv_get_string(&argvars[1]), "B") == 0) {
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
blob = true;
|
size = (off_T)tv_get_number(&argvars[2]);
|
||||||
}
|
}
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
} else {
|
||||||
maxline = tv_get_number(&argvars[2]);
|
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) {
|
if (blob) {
|
||||||
tv_blob_alloc_ret(rettv);
|
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);
|
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);
|
fclose(fd);
|
||||||
return;
|
return;
|
||||||
|
@ -439,10 +439,29 @@ func Test_blob_read_write()
|
|||||||
call writefile(b, 'Xblob')
|
call writefile(b, 'Xblob')
|
||||||
VAR br = readfile('Xblob', 'B')
|
VAR br = readfile('Xblob', 'B')
|
||||||
call assert_equal(b, br)
|
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')
|
call delete('Xblob')
|
||||||
END
|
END
|
||||||
call CheckLegacyAndVim9Success(lines)
|
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.
|
" This was crashing when calling readfile() with a directory.
|
||||||
call assert_fails("call readfile('.', 'B')", 'E17: "." is a directory')
|
call assert_fails("call readfile('.', 'B')", 'E17: "." is a directory')
|
||||||
endfunc
|
endfunc
|
||||||
|
Loading…
Reference in New Issue
Block a user