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
|
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,29 @@ 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|.
|
||||||
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|.
|
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()|.
|
Also see |readfile()| and |writefile()|.
|
||||||
|
|
||||||
|
|
||||||
|
@ -5859,27 +5859,63 @@ 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;
|
||||||
|
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)
|
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,41 @@ 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)
|
||||||
|
|
||||||
|
#" 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')
|
call delete('Xblob')
|
||||||
END
|
END
|
||||||
call CheckLegacyAndVim9Success(lines)
|
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.
|
" 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