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>
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 71e3448..2b630ca 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1129,6 +1129,7 @@
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 @@
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 c26914d..a71ac91 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -1235,35 +1235,91 @@
}
/*
+ * 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.
*/
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 @@
{
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_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 @@
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 @@
{
char_u *enc = dict_get_string(d, "encoding", FALSE);
if (enc != 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;
- }
+ to_encoding = enc_canonize(enc_skip(enc));
}
}
- len = STRLEN(p);
- for (size_t i = 0; i < len; i++)
- ga_append(&blob->bv_ga, (int)p[i]);
+ FOR_ALL_LIST_ITEMS(list, li)
+ {
+ if (li->li_tv.v_type != VAR_STRING)
+ continue;
- if (free_str)
- vim_free(p);
+ 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)
+ {
+ 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);
}
/*
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index 9fc954c..5e0fd7d 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -4262,23 +4262,32 @@
" 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_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\nb"]))
+ call assert_equal(0z61620A6364, str2blob(["ab", "cd"]))
+ call assert_equal(0z0A, str2blob(["", ""]))
- 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(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 @@
" 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\nb"], 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 92bba85..b002210 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1024,
+/**/
1023,
/**/
1022,