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.

Raspunde prin e-mail lui