patch 9.1.1024: blob2str/str2blob() do not support list of strings Commit: https://github.com/vim/vim/commit/a11b23c4d52aa704a95067085741a4d66146f92b Author: Yegappan Lakshmanan <yegap...@yahoo.com> Date: Thu Jan 16 19:16:42 2025 +0100
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 <yegap...@yahoo.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 9d56f5082..d60d61f0d 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -72,7 +72,7 @@ base64_encode({blob}) String base64 encode the bytes in {blob} bindtextdomain({package}, {path}) Bool bind text domain to specified path 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}) String put up a file 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} srand([{expr}]) List get seed for |rand()| state([{what}]) String current state of Vim -str2blob({string} [, {options}]) - Blob convert {string} into a Blob +str2blob({list} [, {options}]) + Blob convert list of strings into a Blob str2float({expr} [, {quoted}]) Float convert String to Float str2list({expr} [, {utf8}]) List convert each character of {expr} to ASCII/UTF-8 value @@ -1260,7 +1260,7 @@ base64_encode({blob}) *base64_encode()* " Encode the contents of a binary file echo base64_encode(readblob('somefile.bin')) " Encode a string - echo base64_encode(str2blob(somestr)) + echo base64_encode(str2blob([somestr])) < Can also be used as a |method|: > GetBinaryData()->base64_encode() @@ -1294,8 +1294,12 @@ blob2list({blob}) *blob2list()* blob2str({blob} [, {options}]) *blob2str()* - Return a String in the current 'encoding' by converting the - bytes in {blob} into characters. + Return a List of Strings in the current 'encoding' by + 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 used to decode the bytes in {blob}. @@ -1306,22 +1310,22 @@ blob2str({blob} [, {options}]) *blob2str()* encoding. The value is a |String|. See |encoding-names| for the supported values. *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}, - Returns an empty String if blob is empty. + Returns an empty List if blob is empty. See also |str2blob()| Examples: > - blob2str(0z6162) returns "ab" - blob2str(0zC2ABC2BB) returns "«»" - blob2str(0zABBB, {'encoding': 'latin1'}) returns "«»" + blob2str(0z6162) returns ["ab"] + blob2str(0zC2ABC2BB) returns ["«»"] + blob2str(0zABBB, {'encoding': 'latin1'}) returns ["«»"] < Can also be used as a |method|: > GetBlob()->blob2str() < - Return type: |String| + Return type: list<string> *browse()* @@ -10592,33 +10596,39 @@ state([{what}]) *state()* Return type: |String| -str2blob({string} [, {options}]) *str2blob()* - Return a Blob by converting the characters in {string} into - bytes. +str2blob({list} [, {options}]) *str2blob()* + Return a Blob by converting the characters in the List of + 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 - 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 items: - encoding Encode the characters in {string} using this - encoding. The value is a |String|. See - |encoding-names| for the supported values. + encoding Encode the characters using this encoding. + The value is a |String|. See |encoding-names| + for the supported values. An error is given and an empty blob is returned if the character encoding fails. - Returns an empty Blob if {string} is empty. + Returns an empty Blob if {list} is empty. See also |blob2str()| Examples: > - str2blob("ab") returns 0z6162 - str2blob("«»") returns 0zC2ABC2BB - str2blob("«»", {'encoding': 'latin1'}) returns 0zABBB + str2blob(["ab"]) returns 0z6162 + str2blob(["«»"]) returns 0zC2ABC2BB + str2blob(["a b"]) returns 0z610A62 + str2blob(readfile('myfile.txt')) + str2blob(["«»"], {'encoding': 'latin1'}) returns 0zABBB < Can also be used as a |method|: > - GetStr()->str2blob() + GetListOfStrings()->str2blob() < Return type: |Blob| diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index cb4225734..35aba0250 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -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 @@ -801,8 +801,8 @@ String manipulation: *string-functions* trim() trim characters from a string bindtextdomain() set message lookup translation base path gettext() lookup message translation - str2blob() convert a string into a blob - blob2str() convert a blob into a string + str2blob() convert a list of strings into a blob + blob2str() convert a blob into a list of strings List manipulation: *list-functions* get() get an item without error for wrong index diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index cae287684..ecdb555dc 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -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 @@ -41638,7 +41638,7 @@ Functions: ~ |base64_decode()| decode a base64 string into a blob |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 |diff()| diff two Lists of strings |filecopy()| copy a file {from} to {to} @@ -41654,7 +41654,7 @@ Functions: ~ |matchbufline()| all the matches of a pattern in a buffer |matchstrlist()| all the matches of a pattern in a List of strings |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: ~ diff --git a/src/evalfunc.c b/src/evalfunc.c index 71e3448ae..2b630cafc 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -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_number[] = {arg_list_number, arg_list_number}; 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_lnum[] = {arg_lnum, arg_lnum}; static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number}; @@ -2713,7 +2714,7 @@ static funcentry_T global_functions[] = ret_list_number, f_srand}, {"state", 0, 1, FEARG_1, arg1_string, 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}, {"str2float", 1, 2, FEARG_1, arg2_string_bool, ret_float, f_str2float}, diff --git a/src/strings.c b/src/strings.c index c26914d0d..a71ac9192 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1234,6 +1234,67 @@ convert_string(char_u *str, char_u *from, char_u *to) 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 * 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 f_blob2str(typval_T *argvars, typval_T *rettv) { - blob_T *blob; - char_u *str; - char_u *p; - int blen; + blob_T *blob; + int blen; + long idx; + int utf8_inuse = FALSE; if (check_for_blob_arg(argvars, 0) == FAIL || check_for_opt_dict_arg(argvars, 1) == FAIL) return; - blob = argvars->vval.v_blob; - blen = blob_len(blob); - - rettv->v_type = VAR_STRING; - - str = alloc(blen + 1); - if (str == NULL) + if (rettv_list_alloc(rettv) == FAIL) return; - for (int i = 0; i < blen; i++) - str[i] = (char_u)blob_get(blob, i); - str[blen] = NUL; + blob = argvars->vval.v_blob; + if (blob == NULL) + return; + blen = blob_len(blob); - p = str; + char_u *from_encoding = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { 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); if (enc != NULL) - { - 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); - } + from_encoding = enc_canonize(enc_skip(enc)); } } 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); - vim_free(p); - return; + converted_str = convert_string(str, from_encoding, p_enc); + vim_free(str); + 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) { blob_T *blob; - char_u *p; - size_t len; + list_T *list; + 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) return; @@ -1318,11 +1394,11 @@ f_str2blob(typval_T *argvars, typval_T *rettv) blob = rettv->vval.v_blob; - p = tv_get_string_chk(&argvars[0]); - if (p == NULL) + list = argvars[0].vval.v_list; + if (list == NULL) return; - int free_str = FALSE; + char_u *to_encoding = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { 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); if (enc != NULL) + to_encoding = enc_canonize(enc_skip(enc)); + } + } + + FOR_ALL_LIST_ITEMS(list, li) + { + if (li->li_tv.v_type != VAR_STRING) + continue; + + char_u *str = li->li_tv.vval.v_string; + + if (str == NULL) + continue; + + if (to_encoding != NULL) + { + str = convert_string(str, p_enc, to_encoding); + if (str == NULL) { - 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; + semsg(_(e_str_encoding_failed), "to", to_encoding); + goto done; } } - } - len = STRLEN(p); - for (size_t i = 0; i < len; i++) - ga_append(&blob->bv_ga, (int)p[i]); + 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); + } - if (free_str) - vim_free(p); +done: + if (to_encoding != NULL) + vim_free(to_encoding); } /* diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 9fc954c08..5e0fd7da7 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -4262,23 +4262,32 @@ endfunc " Tests for the str2blob() function func Test_str2blob() let lines =<< trim END - call assert_equal(0z, str2blob("")) - call assert_fails("call str2blob([])", 'E1174: String required for argument 1') - call assert_equal(0z6162, str2blob("ab")) - call assert_equal(0zC2ABC2BB, str2blob("«»")) - call assert_equal(0zC59DC59F, str2blob("ŝş")) - call assert_equal(0zE0AE85E0.AE87, str2blob("அஇ")) - call assert_equal(0zF09F81B0.F09F81B3, str2blob("🁰🁳")) - call assert_equal(0z616263, str2blob('abc', {})) - call assert_equal(0zABBB, 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') + call assert_equal(0z, str2blob([""])) + call assert_equal(0z, str2blob([])) + call assert_equal(0z, str2blob(test_null_list())) + call assert_equal(0z, str2blob([test_null_string(), test_null_string()])) + call assert_fails("call str2blob('')", 'E1211: List required for argument 1') + call assert_equal(0z61, str2blob(["a"])) + call assert_equal(0z6162, str2blob(["ab"])) + call assert_equal(0z610062, str2blob(["a b"])) + call assert_equal(0z61620A6364, str2blob(["ab", "cd"])) + call assert_equal(0z0A, str2blob(["", ""])) + + call assert_equal(0zC2ABC2BB, str2blob(["«»"])) + call assert_equal(0zC59DC59F, str2blob(["ŝş"])) + call assert_equal(0zE0AE85E0.AE87, str2blob(["அஇ"])) + call assert_equal(0zF09F81B0.F09F81B3, str2blob(["🁰🁳"])) + call assert_equal(0z616263, str2blob(['abc'], {})) + 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 call v9.CheckLegacyAndVim9Success(lines) endfunc @@ -4286,18 +4295,23 @@ endfunc " Tests for the blob2str() function func Test_blob2str() 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_equal("ab", blob2str(0z6162)) - call assert_equal("«»", blob2str(0zC2ABC2BB)) - call assert_equal("ŝş", blob2str(0zC59DC59F)) - 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'})) + call assert_equal(["ab"], blob2str(0z6162)) + call assert_equal(["a b"], blob2str(0z610062)) + call assert_equal(["ab", "cd"], blob2str(0z61620A6364)) + + call assert_equal(["«»"], blob2str(0zC2ABC2BB)) + call assert_equal(["ŝş"], blob2str(0zC59DC59F)) + 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 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(0zE0)", "E1515: Unable to convert from 'utf-8' encoding") call assert_fails("call blob2str(0zF0)", "E1515: Unable to convert from 'utf-8' encoding") diff --git a/src/version.c b/src/version.c index 92bba8500..b00221086 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1024, /**/ 1023, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1tYUd1-00GPWs-9n%40256bit.org.