]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1016: Not possible to convert string2blob and blob2string v9.1.1016
authorYegappan Lakshmanan <yegappan@yahoo.com>
Tue, 14 Jan 2025 16:29:42 +0000 (17:29 +0100)
committerChristian Brabandt <cb@256bit.org>
Tue, 14 Jan 2025 16:29:42 +0000 (17:29 +0100)
Problem:  Not possible to convert string2blob and blob2string
Solution: add support for the blob2str() and str2blob() functions

closes: #16373

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
12 files changed:
runtime/doc/builtin.txt
runtime/doc/tags
runtime/doc/usr_41.txt
runtime/doc/version9.txt
src/errors.h
src/evalfunc.c
src/mbyte.c
src/proto/mbyte.pro
src/proto/strings.pro
src/strings.c
src/testdir/test_functions.vim
src/version.c

index a34c63aebc4634d1b2ad27cf20ba9148ba7f949f..10970bc51a18b62cdb93136858c6674f3622779e 100644 (file)
@@ -1,4 +1,4 @@
-*builtin.txt*  For Vim version 9.1.  Last change: 2025 Jan 06
+*builtin.txt*  For Vim version 9.1.  Last change: 2025 Jan 14
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -72,6 +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
 browse({save}, {title}, {initdir}, {default})
                                String  put up a file requester
 browsedir({title}, {initdir})  String  put up a directory requester
@@ -609,6 +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
 str2float({expr} [, {quoted}]) Float   convert String to Float
 str2list({expr} [, {utf8}])    List    convert each character of {expr} to
                                        ASCII/UTF-8 value
@@ -1289,6 +1292,38 @@ blob2list({blob})                                        *blob2list()*
 <
                Return type: list<any> or list<number>
 
+
+blob2str({blob} [, {options}])                         *blob2str()*
+               Return a String in the current 'encoding' by converting the
+               bytes in {blob} into characters.
+
+               If {options} is not supplied, the current 'encoding' value is
+               used to decode the bytes in {blob}.
+
+               The argument {options} is a |Dict| and supports the following
+               items:
+                   encoding    Decode the bytes in {blob} using this
+                               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 invalid byte sequence is encountered in {blob},
+
+               Returns an empty String if blob is empty.
+
+               See also |str2blob()|
+
+               Examples: >
+                       blob2str(0z6162)        returns "ab"
+                       blob2str(0zC2ABC2BB)    returns "«»"
+                       blob2str(0zABBB, {'encoding': 'latin1'})  returns "«»"
+<
+               Can also be used as a |method|: >
+                       GetBlob()->blob2str()
+<
+               Return type: |String|
+
+
                                                        *browse()*
 browse({save}, {title}, {initdir}, {default})
                Put up a file requester.  This only works when "has("browse")"
@@ -10556,6 +10591,36 @@ state([{what}])                                                *state()*
                Return type: |String|
 
 
+str2blob({string} [, {options}])                               *str2blob()*
+               Return a Blob by converting the characters in {string} into
+               bytes.
+
+               If {options} is not supplied, the current 'encoding' value is
+               used to convert the characters in {string} 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.
+
+               An error is given and an empty blob is returned if the
+               character encoding fails.
+
+               Returns an empty Blob if {string} is empty.
+
+               See also |blob2str()|
+
+               Examples: >
+                       str2blob("ab")          returns 0z6162
+                       str2blob("«»")                returns 0zC2ABC2BB
+                       str2blob("«»", {'encoding': 'latin1'})        returns 0zABBB
+<
+               Can also be used as a |method|: >
+                       GetStr()->str2blob()
+<
+               Return type: |Blob|
+
 str2float({string} [, {quoted}])                               *str2float()*
                Convert String {string} to a Float.  This mostly works the
                same as when using a floating point number in an expression,
index 05c21261476cbb6e82b913fe7e6cb203423ccaf1..fbc8545ec8e51239d9e5a316988d719348957d2c 100644 (file)
@@ -4585,6 +4585,7 @@ E1511     options.txt     /*E1511*
 E1512  options.txt     /*E1512*
 E1513  message.txt     /*E1513*
 E1514  options.txt     /*E1514*
+E1515  builtin.txt     /*E1515*
 E152   helphelp.txt    /*E152*
 E153   helphelp.txt    /*E153*
 E154   helphelp.txt    /*E154*
@@ -6192,6 +6193,7 @@ blob-index        eval.txt        /*blob-index*
 blob-literal   eval.txt        /*blob-literal*
 blob-modification      eval.txt        /*blob-modification*
 blob2list()    builtin.txt     /*blob2list()*
+blob2str()     builtin.txt     /*blob2str()*
 blockwise-examples     visual.txt      /*blockwise-examples*
 blockwise-operators    visual.txt      /*blockwise-operators*
 blockwise-put  change.txt      /*blockwise-put*
@@ -10253,6 +10255,7 @@ status-line     windows.txt     /*status-line*
 statusmsg-variable     eval.txt        /*statusmsg-variable*
 stl-%! options.txt     /*stl-%!*
 stl-%{ options.txt     /*stl-%{*
+str2blob()     builtin.txt     /*str2blob()*
 str2float()    builtin.txt     /*str2float()*
 str2list()     builtin.txt     /*str2list()*
 str2nr()       builtin.txt     /*str2nr()*
index ded30e35d46f98e1c01d7fcc69491b1a0a7b4c99..cb42257341f03536b07b33747321cb54fd81618f 100644 (file)
@@ -1,4 +1,4 @@
-*usr_41.txt*   For Vim version 9.1.  Last change: 2025 Jan 02
+*usr_41.txt*   For Vim version 9.1.  Last change: 2025 Jan 14
 
                     VIM USER MANUAL - by Bram Moolenaar
 
@@ -801,6 +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
 
 List manipulation:                                     *list-functions*
        get()                   get an item without error for wrong index
index e212230e5bbdcc558475ba90c754b4cb5c7abfa4..cae287684d72c29ed9d72a14077ef73a9104d6d5 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2025 Jan 12
+*version9.txt*  For Vim version 9.1.  Last change: 2025 Jan 14
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41638,6 +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
 |bindtextdomain()|     set message lookup translation base path
 |diff()|               diff two Lists of strings
 |filecopy()|           copy a file {from} to {to}
@@ -41653,6 +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
 
 
 Autocommands: ~
index 2811a3275c2adca991329efcbd0293ddb2179e88..6bab8265084457f5f40830e795f41894063d0b07 100644 (file)
@@ -3657,3 +3657,5 @@ EXTERN char e_winfixbuf_cannot_go_to_buffer[]
        INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
 EXTERN char e_invalid_return_type_from_findfunc[]
        INIT(= N_("E1514: 'findfunc' did not return a List type"));
+EXTERN char e_str_encoding_failed[]
+       INIT(= N_("E1515: Unable to convert %s '%s' encoding"));
index ef30792ad195f00cbbd55356347be0d98c9f3eb1..63142523c53d636ef6f3ad7b08942f62be488da0 100644 (file)
@@ -1151,6 +1151,7 @@ static argcheck_T arg2_string_number[] = {arg_string, arg_number};
 static argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict_any};
 static argcheck_T arg2_string_or_list_number[] = {arg_string_or_list_any, arg_number};
 static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr};
+static argcheck_T arg2_blob_dict[] = {arg_blob, arg_dict_any};
 static argcheck_T arg3_any_list_dict[] = {arg_any, arg_list_any, arg_dict_any};
 static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum};
 static argcheck_T arg3_buffer_number_number[] = {arg_buffer, arg_number, arg_number};
@@ -1844,6 +1845,8 @@ static funcentry_T global_functions[] =
                        ret_bool,           f_bindtextdomain},
     {"blob2list",      1, 1, FEARG_1,      arg1_blob,
                        ret_list_number,    f_blob2list},
+    {"blob2str",       1, 2, FEARG_1,      arg2_blob_dict,
+                       ret_string,         f_blob2str},
     {"browse",         4, 4, 0,            arg4_browse,
                        ret_string,         f_browse},
     {"browsedir",      2, 2, 0,            arg2_string,
@@ -2710,6 +2713,8 @@ 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,
+                       ret_blob,           f_str2blob},
     {"str2float",      1, 2, FEARG_1,      arg2_string_bool,
                        ret_float,          f_str2float},
     {"str2list",       1, 2, FEARG_1,      arg2_string_bool,
index 4a7eada25ae6bbefc7da10124a96468585b594da..157018266676c7d5d6ab14f1f4b196020afe7545 100644 (file)
@@ -2106,6 +2106,17 @@ utf_byte2len(int b)
     return utf8len_tab[b];
 }
 
+/*
+ * Return length of UTF-8 character, obtained from the first byte.
+ * "b" must be between 0 and 255!
+ * Returns 0 for an invalid first byte value.
+ */
+    int
+utf_byte2len_zero(int b)
+{
+    return utf8len_tab_zero[b];
+}
+
 /*
  * Get the length of UTF-8 byte sequence "p[size]".  Does not include any
  * following composing characters.
index bb976e3bf812b65835689ee5f2c07bb7cc389c1a..7061bab84a2159949fb36844e894494ca94400f5 100644 (file)
@@ -31,6 +31,7 @@ int utfc_ptr2char_len(char_u *p, int *pcc, int maxlen);
 int utfc_char2bytes(int off, char_u *buf);
 int utf_ptr2len(char_u *p);
 int utf_byte2len(int b);
+int utf_byte2len_zero(int b);
 int utf_ptr2len_len(char_u *p, int size);
 int utfc_ptr2len(char_u *p);
 int utfc_ptr2len_len(char_u *p, int size);
index c25555f1cdba8037f8d73df082ba9126e5030aba..ddc53cce0c0055f25ed8c60fd3e1508c680dc17b 100644 (file)
@@ -31,6 +31,8 @@ void string_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv);
 void f_byteidx(typval_T *argvars, typval_T *rettv);
 void f_byteidxcomp(typval_T *argvars, typval_T *rettv);
 void f_charidx(typval_T *argvars, typval_T *rettv);
+void f_blob2str(typval_T *argvars, typval_T *rettv);
+void f_str2blob(typval_T *argvars, typval_T *rettv);
 void f_str2list(typval_T *argvars, typval_T *rettv);
 void f_str2nr(typval_T *argvars, typval_T *rettv);
 void f_strgetchar(typval_T *argvars, typval_T *rettv);
index 54ac17873f2258d0ee14128c98e51aac3193f14b..c26914d0d049317413dfa196c8833d543e6d3509 100644 (file)
@@ -1213,6 +1213,146 @@ f_charidx(typval_T *argvars, typval_T *rettv)
     rettv->vval.v_number = len > 0 ? len - 1 : 0;
 }
 
+/*
+ * Convert the string "str", from encoding "from" to encoding "to".
+ */
+    static char_u *
+convert_string(char_u *str, char_u *from, char_u *to)
+{
+    vimconv_T  vimconv;
+
+    vimconv.vc_type = CONV_NONE;
+    if (convert_setup(&vimconv, from, to) == FAIL)
+       return NULL;
+    vimconv.vc_fail = TRUE;
+    if (vimconv.vc_type == CONV_NONE)
+       str = vim_strsave(str);
+    else
+       str = string_convert(&vimconv, str, NULL);
+    convert_setup(&vimconv, NULL, NULL);
+
+    return 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;
+
+    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)
+       return;
+
+    for (int i = 0; i < blen; i++)
+       str[i] = (char_u)blob_get(blob, i);
+    str[blen] = NUL;
+
+    p = str;
+    if (argvars[1].v_type != VAR_UNKNOWN)
+    {
+       dict_T *d = argvars[1].vval.v_dict;
+       if (d != NULL)
+       {
+           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);
+           }
+       }
+    }
+
+    if (STRCMP(p_enc, "utf-8") == 0 || STRCMP(p_enc, "utf8") == 0)
+    {
+       if (!utf_valid_string(p, NULL))
+       {
+           semsg(_(e_str_encoding_failed), "from", p_enc);
+           vim_free(p);
+           return;
+       }
+    }
+
+    rettv->vval.v_string = p;
+}
+
+/*
+ * "str2blob()" function
+ */
+    void
+f_str2blob(typval_T *argvars, typval_T *rettv)
+{
+    blob_T     *blob;
+    char_u     *p;
+    size_t     len;
+
+    if (check_for_string_arg(argvars, 0) == FAIL
+           || check_for_opt_dict_arg(argvars, 1) == FAIL)
+       return;
+
+    if (rettv_blob_alloc(rettv) == FAIL)
+       return;
+
+    blob = rettv->vval.v_blob;
+
+    p = tv_get_string_chk(&argvars[0]);
+    if (p == NULL)
+       return;
+
+    int free_str = FALSE;
+    if (argvars[1].v_type != VAR_UNKNOWN)
+    {
+       dict_T *d = argvars[1].vval.v_dict;
+       if (d != NULL)
+       {
+           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;
+           }
+       }
+    }
+
+    len = STRLEN(p);
+    for (size_t i = 0; i < len; i++)
+       ga_append(&blob->bv_ga, (int)p[i]);
+
+    if (free_str)
+       vim_free(p);
+}
+
 /*
  * "str2list()" function
  */
index f80754f0a7e996407aa4487bf2d8e8b9c3aa7ec7..8b0af9101596ed4554ff664a0251177a34bb43eb 100644 (file)
@@ -4257,4 +4257,73 @@ func Test_base64_encoding()
   call v9.CheckLegacyAndVim9Success(lines)
 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')
+  END
+  call v9.CheckLegacyAndVim9Success(lines)
+endfunc
+
+" Tests for the blob2str() function
+func Test_blob2str()
+  let lines =<< trim END
+    call assert_equal("", blob2str(0z))
+    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'}))
+
+    #" Invalid encoding
+    call assert_fails("call blob2str(0z80)", "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")
+
+    call assert_fails("call blob2str(0z6180)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0z61C0)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0z61E0)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0z61F0)", "E1515: Unable to convert from 'utf-8' encoding")
+
+    call assert_fails("call blob2str(0zC0C0)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0z61C0C0)", "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(0zE080)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0zE080C0)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0z61E080C0)", "E1515: Unable to convert from 'utf-8' encoding")
+
+    call assert_fails("call blob2str(0zF08080C0)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0z61F08080C0)", "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(0zF080)", "E1515: Unable to convert from 'utf-8' encoding")
+    call assert_fails("call blob2str(0zF08080)", "E1515: Unable to convert from 'utf-8' encoding")
+
+    call assert_fails("call blob2str(0z6162, [])", 'E1206: Dictionary required for argument 2')
+    call assert_fails("call blob2str(0z6162, {'encoding': []})", 'E730: Using a List as a String')
+    call assert_fails("call blob2str(0z6162, {'encoding': 'ab12xy'})", 'E1515: Unable to convert from ''ab12xy'' encoding')
+  END
+  call v9.CheckLegacyAndVim9Success(lines)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 96a51da8020022040deb42ed52d92a9e16bad4ec..13ffc5d4e88154925b2838cedf0b0cb29036ce54 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1016,
 /**/
     1015,
 /**/