1
0
forked from aniani/vim

patch 9.1.1024: blob2str/str2blob() do not support list of strings

Problem:  blob2str/str2blob() do not support list of strings
          (after v9.1.1016)
Solution: Add support for using a list of strings (Yegappan Lakshmanan)

closes: #16459

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan
2025-01-16 19:16:42 +01:00
committed by Christian Brabandt
parent a234a46651
commit a11b23c4d5
7 changed files with 229 additions and 110 deletions

View File

@@ -72,7 +72,7 @@ base64_encode({blob}) String base64 encode the bytes in {blob}
bindtextdomain({package}, {path}) bindtextdomain({package}, {path})
Bool bind text domain to specified path Bool bind text domain to specified path
blob2list({blob}) List convert {blob} into a list of numbers blob2list({blob}) List convert {blob} into a list of numbers
blob2str({blob} [, {options}]) String convert {blob} into a String blob2str({blob} [, {options}]) String convert {blob} into a list of strings
browse({save}, {title}, {initdir}, {default}) browse({save}, {title}, {initdir}, {default})
String put up a file requester String put up a file requester
browsedir({title}, {initdir}) String put up a directory requester browsedir({title}, {initdir}) String put up a directory requester
@@ -610,8 +610,8 @@ split({expr} [, {pat} [, {keepempty}]])
sqrt({expr}) Float square root of {expr} sqrt({expr}) Float square root of {expr}
srand([{expr}]) List get seed for |rand()| srand([{expr}]) List get seed for |rand()|
state([{what}]) String current state of Vim state([{what}]) String current state of Vim
str2blob({string} [, {options}]) str2blob({list} [, {options}])
Blob convert {string} into a Blob Blob convert list of strings into a Blob
str2float({expr} [, {quoted}]) Float convert String to Float str2float({expr} [, {quoted}]) Float convert String to Float
str2list({expr} [, {utf8}]) List convert each character of {expr} to str2list({expr} [, {utf8}]) List convert each character of {expr} to
ASCII/UTF-8 value ASCII/UTF-8 value
@@ -1260,7 +1260,7 @@ base64_encode({blob}) *base64_encode()*
" Encode the contents of a binary file " Encode the contents of a binary file
echo base64_encode(readblob('somefile.bin')) echo base64_encode(readblob('somefile.bin'))
" Encode a string " Encode a string
echo base64_encode(str2blob(somestr)) echo base64_encode(str2blob([somestr]))
< <
Can also be used as a |method|: > Can also be used as a |method|: >
GetBinaryData()->base64_encode() GetBinaryData()->base64_encode()
@@ -1294,8 +1294,12 @@ blob2list({blob}) *blob2list()*
blob2str({blob} [, {options}]) *blob2str()* blob2str({blob} [, {options}]) *blob2str()*
Return a String in the current 'encoding' by converting the Return a List of Strings in the current 'encoding' by
bytes in {blob} into characters. converting the bytes in {blob} into characters.
Each <NL> byte in the blob is interpreted as the end of a
string and a new list item is added. Each <NUL> byte in the
blob is converted into a <NL> character.
If {options} is not supplied, the current 'encoding' value is If {options} is not supplied, the current 'encoding' value is
used to decode the bytes in {blob}. used to decode the bytes in {blob}.
@@ -1306,22 +1310,22 @@ blob2str({blob} [, {options}]) *blob2str()*
encoding. The value is a |String|. See encoding. The value is a |String|. See
|encoding-names| for the supported values. |encoding-names| for the supported values.
*E1515* *E1515*
An error is given and an empty string is returned if An error is given and an empty List is returned if
an invalid byte sequence is encountered in {blob}, an invalid byte sequence is encountered in {blob},
Returns an empty String if blob is empty. Returns an empty List if blob is empty.
See also |str2blob()| See also |str2blob()|
Examples: > Examples: >
blob2str(0z6162) returns "ab" blob2str(0z6162) returns ["ab"]
blob2str(0zC2ABC2BB) returns "«»" blob2str(0zC2ABC2BB) returns ["«»"]
blob2str(0zABBB, {'encoding': 'latin1'}) returns "«»" blob2str(0zABBB, {'encoding': 'latin1'}) returns ["«»"]
< <
Can also be used as a |method|: > Can also be used as a |method|: >
GetBlob()->blob2str() GetBlob()->blob2str()
< <
Return type: |String| Return type: list<string>
*browse()* *browse()*
@@ -10592,33 +10596,39 @@ state([{what}]) *state()*
Return type: |String| Return type: |String|
str2blob({string} [, {options}]) *str2blob()* str2blob({list} [, {options}]) *str2blob()*
Return a Blob by converting the characters in {string} into Return a Blob by converting the characters in the List of
bytes. strings in {list} into bytes.
A <NL> byte is added to the blob after each list item. A
newline character in the string is translated into a <NUL>
byte in the blob.
If {options} is not supplied, the current 'encoding' value is If {options} is not supplied, the current 'encoding' value is
used to convert the characters in {string} into bytes. used to convert the characters into bytes.
The argument {options} is a |Dict| and supports the following The argument {options} is a |Dict| and supports the following
items: items:
encoding Encode the characters in {string} using this encoding Encode the characters using this encoding.
encoding. The value is a |String|. See The value is a |String|. See |encoding-names|
|encoding-names| for the supported values. for the supported values.
An error is given and an empty blob is returned if the An error is given and an empty blob is returned if the
character encoding fails. character encoding fails.
Returns an empty Blob if {string} is empty. Returns an empty Blob if {list} is empty.
See also |blob2str()| See also |blob2str()|
Examples: > Examples: >
str2blob("ab") returns 0z6162 str2blob(["ab"]) returns 0z6162
str2blob("«»") returns 0zC2ABC2BB str2blob(["«»"]) returns 0zC2ABC2BB
str2blob("«»", {'encoding': 'latin1'}) returns 0zABBB str2blob(["a\nb"]) returns 0z610A62
str2blob(readfile('myfile.txt'))
str2blob(["«»"], {'encoding': 'latin1'}) returns 0zABBB
< <
Can also be used as a |method|: > Can also be used as a |method|: >
GetStr()->str2blob() GetListOfStrings()->str2blob()
< <
Return type: |Blob| Return type: |Blob|

View File

@@ -1,4 +1,4 @@
*usr_41.txt* For Vim version 9.1. Last change: 2025 Jan 14 *usr_41.txt* For Vim version 9.1. Last change: 2025 Jan 16
VIM USER MANUAL - by Bram Moolenaar VIM USER MANUAL - by Bram Moolenaar
@@ -801,8 +801,8 @@ String manipulation: *string-functions*
trim() trim characters from a string trim() trim characters from a string
bindtextdomain() set message lookup translation base path bindtextdomain() set message lookup translation base path
gettext() lookup message translation gettext() lookup message translation
str2blob() convert a string into a blob str2blob() convert a list of strings into a blob
blob2str() convert a blob into a string blob2str() convert a blob into a list of strings
List manipulation: *list-functions* List manipulation: *list-functions*
get() get an item without error for wrong index get() get an item without error for wrong index

View File

@@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.1. Last change: 2025 Jan 14 *version9.txt* For Vim version 9.1. Last change: 2025 Jan 16
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -41638,7 +41638,7 @@ Functions: ~
|base64_decode()| decode a base64 string into a blob |base64_decode()| decode a base64 string into a blob
|base64_encode()| encode a blob into a base64 string |base64_encode()| encode a blob into a base64 string
|blob2str()| convert a blob into a string |blob2str()| convert a blob into a List of strings
|bindtextdomain()| set message lookup translation base path |bindtextdomain()| set message lookup translation base path
|diff()| diff two Lists of strings |diff()| diff two Lists of strings
|filecopy()| copy a file {from} to {to} |filecopy()| copy a file {from} to {to}
@@ -41654,7 +41654,7 @@ Functions: ~
|matchbufline()| all the matches of a pattern in a buffer |matchbufline()| all the matches of a pattern in a buffer
|matchstrlist()| all the matches of a pattern in a List of strings |matchstrlist()| all the matches of a pattern in a List of strings
|popup_setbuf()| switch to a different buffer in a popup |popup_setbuf()| switch to a different buffer in a popup
|str2blob()| convert a string into a blob |str2blob()| convert a List of strings into a blob
Autocommands: ~ Autocommands: ~

View File

@@ -1129,6 +1129,7 @@ static argcheck_T arg2_list_any_number[] = {arg_list_any, arg_number};
static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string}; static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string};
static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number}; static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number};
static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool}; static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool};
static argcheck_T arg2_list_string_dict[] = {arg_list_string, arg_dict_any};
static argcheck_T arg2_listblobmod_item[] = {arg_list_or_blob_mod, arg_item_of_prev}; static argcheck_T arg2_listblobmod_item[] = {arg_list_or_blob_mod, arg_item_of_prev};
static argcheck_T arg2_lnum[] = {arg_lnum, arg_lnum}; static argcheck_T arg2_lnum[] = {arg_lnum, arg_lnum};
static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number}; static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number};
@@ -2713,7 +2714,7 @@ static funcentry_T global_functions[] =
ret_list_number, f_srand}, ret_list_number, f_srand},
{"state", 0, 1, FEARG_1, arg1_string, {"state", 0, 1, FEARG_1, arg1_string,
ret_string, f_state}, ret_string, f_state},
{"str2blob", 1, 2, FEARG_1, arg2_string_dict, {"str2blob", 1, 2, FEARG_1, arg2_list_string_dict,
ret_blob, f_str2blob}, ret_blob, f_str2blob},
{"str2float", 1, 2, FEARG_1, arg2_string_bool, {"str2float", 1, 2, FEARG_1, arg2_string_bool,
ret_float, f_str2float}, ret_float, f_str2float},

View File

@@ -1234,6 +1234,67 @@ convert_string(char_u *str, char_u *from, char_u *to)
return str; return str;
} }
/*
* Add the bytes from "str" to "blob".
*/
static void
blob_from_string(char_u *str, blob_T *blob)
{
size_t len = STRLEN(str);
for (size_t i = 0; i < len; i++)
{
int ch = str[i];
if (str[i] == NL)
// Translate newlines in the string to NUL character
ch = NUL;
ga_append(&blob->bv_ga, ch);
}
}
/*
* Return a string created from the bytes in blob starting at "start_idx".
* A NL character in the blob indicates end of string.
* A NUL character in the blob is translated to a NL.
* On return, "start_idx" points to next byte to process in blob.
*/
static char_u *
string_from_blob(blob_T *blob, long *start_idx)
{
garray_T str_ga;
long blen;
long idx;
ga_init2(&str_ga, sizeof(char), 80);
blen = blob_len(blob);
for (idx = *start_idx; idx < blen; idx++)
{
char_u byte = (char_u)blob_get(blob, idx);
if (byte == NL)
{
idx++;
break;
}
if (byte == NUL)
byte = NL;
ga_append(&str_ga, byte);
}
ga_append(&str_ga, NUL);
char_u *ret_str = vim_strsave(str_ga.ga_data);
*start_idx = idx;
ga_clear(&str_ga);
return ret_str;
}
/* /*
* "blob2str()" function * "blob2str()" function
* Converts a blob to a string, ensuring valid UTF-8 encoding. * Converts a blob to a string, ensuring valid UTF-8 encoding.
@@ -1241,29 +1302,24 @@ convert_string(char_u *str, char_u *from, char_u *to)
void void
f_blob2str(typval_T *argvars, typval_T *rettv) f_blob2str(typval_T *argvars, typval_T *rettv)
{ {
blob_T *blob; blob_T *blob;
char_u *str; int blen;
char_u *p; long idx;
int blen; int utf8_inuse = FALSE;
if (check_for_blob_arg(argvars, 0) == FAIL if (check_for_blob_arg(argvars, 0) == FAIL
|| check_for_opt_dict_arg(argvars, 1) == FAIL) || check_for_opt_dict_arg(argvars, 1) == FAIL)
return; return;
blob = argvars->vval.v_blob; if (rettv_list_alloc(rettv) == FAIL)
blen = blob_len(blob);
rettv->v_type = VAR_STRING;
str = alloc(blen + 1);
if (str == NULL)
return; return;
for (int i = 0; i < blen; i++) blob = argvars->vval.v_blob;
str[i] = (char_u)blob_get(blob, i); if (blob == NULL)
str[blen] = NUL; return;
blen = blob_len(blob);
p = str; char_u *from_encoding = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) if (argvars[1].v_type != VAR_UNKNOWN)
{ {
dict_T *d = argvars[1].vval.v_dict; dict_T *d = argvars[1].vval.v_dict;
@@ -1271,32 +1327,52 @@ f_blob2str(typval_T *argvars, typval_T *rettv)
{ {
char_u *enc = dict_get_string(d, "encoding", FALSE); char_u *enc = dict_get_string(d, "encoding", FALSE);
if (enc != NULL) if (enc != NULL)
{ from_encoding = enc_canonize(enc_skip(enc));
char_u *from = enc_canonize(enc_skip(enc));
p = convert_string(str, from, p_enc);
vim_free(str);
if (p == NULL)
{
semsg(_(e_str_encoding_failed), "from", from);
vim_free(from);
return;
}
vim_free(from);
}
} }
} }
if (STRCMP(p_enc, "utf-8") == 0 || STRCMP(p_enc, "utf8") == 0) if (STRCMP(p_enc, "utf-8") == 0 || STRCMP(p_enc, "utf8") == 0)
utf8_inuse = TRUE;
idx = 0;
while (idx < blen)
{ {
if (!utf_valid_string(p, NULL)) char_u *str;
char_u *converted_str;
str = string_from_blob(blob, &idx);
if (str == NULL)
break;
converted_str = str;
if (from_encoding != NULL)
{ {
semsg(_(e_str_encoding_failed), "from", p_enc); converted_str = convert_string(str, from_encoding, p_enc);
vim_free(p); vim_free(str);
return; if (converted_str == NULL)
{
semsg(_(e_str_encoding_failed), "from", from_encoding);
goto done;
}
} }
if (utf8_inuse)
{
if (!utf_valid_string(converted_str, NULL))
{
semsg(_(e_str_encoding_failed), "from", p_enc);
vim_free(converted_str);
goto done;
}
}
if (list_append_string(rettv->vval.v_list, converted_str, -1) == FAIL)
break;
vim_free(converted_str);
} }
rettv->vval.v_string = p; done:
vim_free(from_encoding);
} }
/* /*
@@ -1306,10 +1382,10 @@ f_blob2str(typval_T *argvars, typval_T *rettv)
f_str2blob(typval_T *argvars, typval_T *rettv) f_str2blob(typval_T *argvars, typval_T *rettv)
{ {
blob_T *blob; blob_T *blob;
char_u *p; list_T *list;
size_t len; listitem_T *li;
if (check_for_string_arg(argvars, 0) == FAIL if (check_for_list_arg(argvars, 0) == FAIL
|| check_for_opt_dict_arg(argvars, 1) == FAIL) || check_for_opt_dict_arg(argvars, 1) == FAIL)
return; return;
@@ -1318,11 +1394,11 @@ f_str2blob(typval_T *argvars, typval_T *rettv)
blob = rettv->vval.v_blob; blob = rettv->vval.v_blob;
p = tv_get_string_chk(&argvars[0]); list = argvars[0].vval.v_list;
if (p == NULL) if (list == NULL)
return; return;
int free_str = FALSE; char_u *to_encoding = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) if (argvars[1].v_type != VAR_UNKNOWN)
{ {
dict_T *d = argvars[1].vval.v_dict; dict_T *d = argvars[1].vval.v_dict;
@@ -1330,27 +1406,43 @@ f_str2blob(typval_T *argvars, typval_T *rettv)
{ {
char_u *enc = dict_get_string(d, "encoding", FALSE); char_u *enc = dict_get_string(d, "encoding", FALSE);
if (enc != NULL) if (enc != NULL)
{ to_encoding = enc_canonize(enc_skip(enc));
char_u *to = enc_canonize(enc_skip(enc));
p = convert_string(p, p_enc, to);
if (p == NULL)
{
semsg(_(e_str_encoding_failed), "to", to);
vim_free(to);
return;
}
vim_free(to);
free_str = TRUE;
}
} }
} }
len = STRLEN(p); FOR_ALL_LIST_ITEMS(list, li)
for (size_t i = 0; i < len; i++) {
ga_append(&blob->bv_ga, (int)p[i]); if (li->li_tv.v_type != VAR_STRING)
continue;
if (free_str) char_u *str = li->li_tv.vval.v_string;
vim_free(p);
if (str == NULL)
continue;
if (to_encoding != NULL)
{
str = convert_string(str, p_enc, to_encoding);
if (str == NULL)
{
semsg(_(e_str_encoding_failed), "to", to_encoding);
goto done;
}
}
if (li != list->lv_first)
// Each list string item is separated by a newline in the blob
ga_append(&blob->bv_ga, NL);
blob_from_string(str, blob);
if (to_encoding != NULL)
vim_free(str);
}
done:
if (to_encoding != NULL)
vim_free(to_encoding);
} }
/* /*

View File

@@ -4262,23 +4262,32 @@ endfunc
" Tests for the str2blob() function " Tests for the str2blob() function
func Test_str2blob() func Test_str2blob()
let lines =<< trim END let lines =<< trim END
call assert_equal(0z, str2blob("")) call assert_equal(0z, str2blob([""]))
call assert_fails("call str2blob([])", 'E1174: String required for argument 1') call assert_equal(0z, str2blob([]))
call assert_equal(0z6162, str2blob("ab")) call assert_equal(0z, str2blob(test_null_list()))
call assert_equal(0zC2ABC2BB, str2blob("«»")) call assert_equal(0z, str2blob([test_null_string(), test_null_string()]))
call assert_equal(0zC59DC59F, str2blob("ŝş")) call assert_fails("call str2blob('')", 'E1211: List required for argument 1')
call assert_equal(0zE0AE85E0.AE87, str2blob("அஇ")) call assert_equal(0z61, str2blob(["a"]))
call assert_equal(0zF09F81B0.F09F81B3, str2blob("🁰🁳")) call assert_equal(0z6162, str2blob(["ab"]))
call assert_equal(0z616263, str2blob('abc', {})) call assert_equal(0z610062, str2blob(["a\nb"]))
call assert_equal(0zABBB, str2blob('«»', {'encoding': 'latin1'})) call assert_equal(0z61620A6364, str2blob(["ab", "cd"]))
call assert_equal(0zC2ABC2BB, str2blob('«»', {'encoding': 'utf8'})) call assert_equal(0z0A, str2blob(["", ""]))
call assert_fails("call str2blob('abc', [])", 'E1206: Dictionary required for argument 2') call assert_equal(0zC2ABC2BB, str2blob(["«»"]))
call assert_fails("call str2blob('abc', {'encoding': []})", 'E730: Using a List as a String') call assert_equal(0zC59DC59F, str2blob(["ŝş"]))
call assert_fails("call str2blob('abc', {'encoding': 'ab12xy'})", 'E1515: Unable to convert to ''ab12xy'' encoding') call assert_equal(0zE0AE85E0.AE87, str2blob(["அஇ"]))
call assert_fails("call str2blob('ŝş', {'encoding': 'latin1'})", 'E1515: Unable to convert to ''latin1'' encoding') call assert_equal(0zF09F81B0.F09F81B3, str2blob(["🁰🁳"]))
call assert_fails("call str2blob('அஇ', {'encoding': 'latin1'})", 'E1515: Unable to convert to ''latin1'' encoding') call assert_equal(0z616263, str2blob(['abc'], {}))
call assert_fails("call str2blob('🁰🁳', {'encoding': 'latin1'})", 'E1515: Unable to convert to ''latin1'' encoding') call assert_equal(0zABBB, str2blob(['«»'], {'encoding': 'latin1'}))
call assert_equal(0zABBB0AABBB, str2blob(['«»', '«»'], {'encoding': 'latin1'}))
call assert_equal(0zC2ABC2BB, str2blob(['«»'], {'encoding': 'utf8'}))
call assert_fails("call str2blob(['abc'], [])", 'E1206: Dictionary required for argument 2')
call assert_fails("call str2blob(['abc'], {'encoding': []})", 'E730: Using a List as a String')
call assert_fails("call str2blob(['abc'], {'encoding': 'ab12xy'})", 'E1515: Unable to convert to ''ab12xy'' encoding')
call assert_fails("call str2blob(['ŝş'], {'encoding': 'latin1'})", 'E1515: Unable to convert to ''latin1'' encoding')
call assert_fails("call str2blob(['அஇ'], {'encoding': 'latin1'})", 'E1515: Unable to convert to ''latin1'' encoding')
call assert_fails("call str2blob(['🁰🁳'], {'encoding': 'latin1'})", 'E1515: Unable to convert to ''latin1'' encoding')
END END
call v9.CheckLegacyAndVim9Success(lines) call v9.CheckLegacyAndVim9Success(lines)
endfunc endfunc
@@ -4286,18 +4295,23 @@ endfunc
" Tests for the blob2str() function " Tests for the blob2str() function
func Test_blob2str() func Test_blob2str()
let lines =<< trim END let lines =<< trim END
call assert_equal("", blob2str(0z)) call assert_equal([], blob2str(0z))
call assert_equal([], blob2str(test_null_blob()))
call assert_fails("call blob2str([])", 'E1238: Blob required for argument 1') call assert_fails("call blob2str([])", 'E1238: Blob required for argument 1')
call assert_equal("ab", blob2str(0z6162)) call assert_equal(["ab"], blob2str(0z6162))
call assert_equal("«»", blob2str(0zC2ABC2BB)) call assert_equal(["a\nb"], blob2str(0z610062))
call assert_equal("ŝş", blob2str(0zC59DC59F)) call assert_equal(["ab", "cd"], blob2str(0z61620A6364))
call assert_equal("அஇ", blob2str(0zE0AE85E0.AE87))
call assert_equal("🁰🁳", blob2str(0zF09F81B0.F09F81B3)) call assert_equal(["«»"], blob2str(0zC2ABC2BB))
call assert_equal('«»', blob2str(0zABBB, {'encoding': 'latin1'})) call assert_equal(["ŝş"], blob2str(0zC59DC59F))
call assert_equal('«»', blob2str(0zC2ABC2BB, {'encoding': 'utf8'})) call assert_equal(["அஇ"], blob2str(0zE0AE85E0.AE87))
call assert_equal(["🁰🁳"], blob2str(0zF09F81B0.F09F81B3))
call assert_equal(['«»'], blob2str(0zABBB, {'encoding': 'latin1'}))
call assert_equal(['«»'], blob2str(0zC2ABC2BB, {'encoding': 'utf8'}))
#" Invalid encoding #" Invalid encoding
call assert_fails("call blob2str(0z80)", "E1515: Unable to convert from 'utf-8' encoding") call assert_fails("call blob2str(0z80)", "E1515: Unable to convert from 'utf-8' encoding")
call assert_fails("call blob2str(0z610A80)", "E1515: Unable to convert from 'utf-8' encoding")
call assert_fails("call blob2str(0zC0)", "E1515: Unable to convert from 'utf-8' encoding") call assert_fails("call blob2str(0zC0)", "E1515: Unable to convert from 'utf-8' encoding")
call assert_fails("call blob2str(0zE0)", "E1515: Unable to convert from 'utf-8' encoding") call assert_fails("call blob2str(0zE0)", "E1515: Unable to convert from 'utf-8' encoding")
call assert_fails("call blob2str(0zF0)", "E1515: Unable to convert from 'utf-8' encoding") call assert_fails("call blob2str(0zF0)", "E1515: Unable to convert from 'utf-8' encoding")

View File

@@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1024,
/**/ /**/
1023, 1023,
/**/ /**/