mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #22453 from zeertzjq/vim-9.0.0795
vim-patch:9.0.{0795,0803,0810}: readblob() offset and size
This commit is contained in:
commit
3f381f4d04
@ -359,7 +359,8 @@ pyxeval({expr}) any evaluate |python_x| expression
|
||||
rand([{expr}]) Number get pseudo-random number
|
||||
range({expr} [, {max} [, {stride}]])
|
||||
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}
|
||||
readfile({fname} [, {type} [, {max}]])
|
||||
List get list of lines from file {fname}
|
||||
@ -6110,6 +6111,25 @@ pyxeval({expr}) *pyxeval()*
|
||||
Can also be used as a |method|: >
|
||||
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*
|
||||
range({expr} [, {max} [, {stride}]]) *range()*
|
||||
Returns a |List| with Numbers:
|
||||
@ -6132,29 +6152,29 @@ range({expr} [, {max} [, {stride}]]) *range()*
|
||||
Can also be used as a |method|: >
|
||||
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: >
|
||||
: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()*
|
||||
readblob({fname} [, {offset} [, {size}]]) *readblob()*
|
||||
Read file {fname} in binary mode and return a |Blob|.
|
||||
When the file can't be opened an error message is given and
|
||||
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.
|
||||
This can be also used to read the data from a character device
|
||||
on Unix when {size} is explicitly set. Only if the device
|
||||
supports seeking {offset} can be used. Otherwise it should be
|
||||
zero. E.g. to read 10 bytes from a serial console: >
|
||||
readblob('/dev/ttyS0', 0, 10)
|
||||
< When the file can't be opened an error message is given and
|
||||
the result is an empty |Blob|.
|
||||
When the offset is beyond the end of the file the result is an
|
||||
empty blob.
|
||||
When trying to read more bytes than are available the result
|
||||
is truncated.
|
||||
Also see |readfile()| and |writefile()|.
|
||||
|
||||
|
||||
|
@ -5859,27 +5859,63 @@ 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;
|
||||
const off_T file_size = (off_T)os_fileinfo_size(&file_info);
|
||||
if (offset >= 0) {
|
||||
// The size defaults to the whole file. If a size is given it is
|
||||
// limited to not go past the end of the file.
|
||||
if (size == -1 || (size > file_size - offset && !S_ISCHR(file_info.stat.st_mode))) {
|
||||
// size may become negative, checked below
|
||||
size = (off_T)os_fileinfo_size(&file_info) - offset;
|
||||
}
|
||||
whence = SEEK_SET;
|
||||
} else {
|
||||
// limit the offset to not go before the start of the file
|
||||
if (-offset > file_size && !S_ISCHR(file_info.stat.st_mode)) {
|
||||
offset = -file_size;
|
||||
}
|
||||
// Size defaults to reading until the end of the file.
|
||||
if (size == -1 || size > -offset) {
|
||||
size = -offset;
|
||||
}
|
||||
whence = SEEK_END;
|
||||
}
|
||||
if (size <= 0) {
|
||||
return OK;
|
||||
}
|
||||
if (offset != 0 && 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.
|
||||
|
@ -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},
|
||||
|
@ -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;
|
||||
|
@ -439,10 +439,41 @@ 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)
|
||||
|
||||
#" reading past end of file, empty result
|
||||
VAR br1e = readblob('Xblob', 10000)
|
||||
call assert_equal(0z, br1e)
|
||||
|
||||
#" reading too much, result is truncated
|
||||
VAR blong = readblob('Xblob', -1000)
|
||||
call assert_equal(b, blong)
|
||||
LET blong = readblob('Xblob', -10, 8)
|
||||
call assert_equal(b, blong)
|
||||
LET blong = readblob('Xblob', 0, 10)
|
||||
call assert_equal(b, blong)
|
||||
|
||||
call delete('Xblob')
|
||||
END
|
||||
call CheckLegacyAndVim9Success(lines)
|
||||
|
||||
if filereadable('/dev/random')
|
||||
let b = readblob('/dev/random', 0, 10)
|
||||
call assert_equal(10, len(b))
|
||||
endif
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user