diff options
Diffstat (limited to 'data/vim/patches/8.1.0735')
-rw-r--r-- | data/vim/patches/8.1.0735 | 3392 |
1 files changed, 3392 insertions, 0 deletions
diff --git a/data/vim/patches/8.1.0735 b/data/vim/patches/8.1.0735 new file mode 100644 index 000000000..945f91e9e --- /dev/null +++ b/data/vim/patches/8.1.0735 @@ -0,0 +1,3392 @@ +To: vim_dev@googlegroups.com +Subject: Patch 8.1.0735 +Fcc: outbox +From: Bram Moolenaar <Bram@moolenaar.net> +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +------------ + +Patch 8.1.0735 +Problem: Cannot handle binary data. +Solution: Add the Blob type. (Yasuhiro Matsumoto, closes #3638) +Files: runtime/doc/eval.txt, runtime/doc/if_perl.txt, + runtime/doc/if_ruby.txt, src/Make_cyg_ming.mak, src/Make_mvc.mak, + src/Makefile, src/blob.c, src/channel.c, src/eval.c, + src/evalfunc.c, src/if_perl.xs, src/if_py_both.h, src/if_python.c, + src/if_python3.c, src/if_ruby.c, src/json.c, src/netbeans.c, + src/proto.h, src/proto/blob.pro, src/proto/channel.pro, + src/structs.h, src/testdir/Make_all.mak, src/vim.h, src/globals.h, + src/testdir/test_blob.vim, src/testdir/test_channel.vim + + +*** ../vim-8.1.0734/runtime/doc/eval.txt 2019-01-12 14:24:22.627597552 +0100 +--- runtime/doc/eval.txt 2019-01-12 22:41:51.485097473 +0100 +*************** +*** 72,77 **** +--- 72,81 ---- + + Channel Used for a channel, see |ch_open()|. *Channel* *Channels* + ++ Blob Binary Large Object. Stores any sequence of bytes. *Blob* ++ Example: 0zFF00ED015DAF ++ 0z is an empty Blob. ++ + The Number and String types are converted automatically, depending on how they + are used. + +*************** +*** 124,130 **** + A List, Dictionary or Float is not a Number or String, thus evaluate to FALSE. + + *E745* *E728* *E703* *E729* *E730* *E731* *E908* *E910* *E913* +! List, Dictionary, Funcref, Job and Channel types are not automatically + converted. + + *E805* *E806* *E808* +--- 128,135 ---- + A List, Dictionary or Float is not a Number or String, thus evaluate to FALSE. + + *E745* *E728* *E703* *E729* *E730* *E731* *E908* *E910* *E913* +! *E974* *E975* *E976* +! List, Dictionary, Funcref, Job, Channel and Blob types are not automatically + converted. + + *E805* *E806* *E808* +*************** +*** 1010,1015 **** +--- 1022,1033 ---- + :let l = mylist[4:4] " List with one item + :let l = mylist[:] " shallow copy of a List + ++ If expr8 is a |Blob| this results in a new |Blob| with the bytes in the ++ indexes expr1a and expr1b, inclusive. Examples: > ++ :let b = 0zDEADBEEF ++ :let bs = b[1:2] " 0zADBE ++ :let bs = b[] " copy ov 0zDEADBEEF ++ + Using expr8[expr1] or expr8[expr1a : expr1b] on a |Funcref| results in an + error. + +*************** +*** 1145,1150 **** +--- 1167,1180 ---- + Note that "\000" and "\x00" force the end of the string. + + ++ blob-literal *blob-literal* *E973* *E977* *E978* ++ ------------ ++ ++ Hexadecimal starting with 0z or 0Z, with an arbitrary number of bytes. ++ The sequence must be an even number of hex characters. Example: > ++ :let b = 0zFF00ED015DAF ++ ++ + literal-string *literal-string* *E115* + --------------- + 'string' string constant *expr-'* +*************** +*** 1898,1903 **** +--- 1930,1937 ---- + v:t_number Value of Number type. Read-only. See: |type()| + *v:t_string* *t_string-variable* + v:t_string Value of String type. Read-only. See: |type()| ++ *v:t_blob* *t_blob-variable* ++ v:t_blob Value of Blob type. Read-only. See: |type()| + + *v:termresponse* *termresponse-variable* + v:termresponse The escape sequence returned by the terminal for the |t_RV| +*************** +*** 2080,2091 **** + ch_open({address} [, {options}]) + Channel open a channel to {address} + ch_read({handle} [, {options}]) String read from {handle} + ch_readraw({handle} [, {options}]) + String read raw from {handle} + ch_sendexpr({handle}, {expr} [, {options}]) + any send {expr} over JSON {handle} +! ch_sendraw({handle}, {string} [, {options}]) +! any send {string} over raw {handle} + ch_setoptions({handle}, {options}) + none set options for {handle} + ch_status({handle} [, {options}]) +--- 2115,2128 ---- + ch_open({address} [, {options}]) + Channel open a channel to {address} + ch_read({handle} [, {options}]) String read from {handle} ++ ch_readblob({handle} [, {options}]) ++ Blob read Blob from {handle} + ch_readraw({handle} [, {options}]) + String read raw from {handle} + ch_sendexpr({handle}, {expr} [, {options}]) + any send {expr} over JSON {handle} +! ch_sendraw({handle}, {expr} [, {options}]) +! any send {expr} over raw {handle} + ch_setoptions({handle}, {options}) + none set options for {handle} + ch_status({handle} [, {options}]) +*************** +*** 2225,2232 **** + hostname() String name of the machine Vim is running on + iconv({expr}, {from}, {to}) String convert encoding of {expr} + indent({lnum}) Number indent of line {lnum} +! index({list}, {expr} [, {start} [, {ic}]]) +! Number index in {list} where {expr} appears + input({prompt} [, {text} [, {completion}]]) + String get input from the user + inputdialog({prompt} [, {text} [, {completion}]]) +--- 2262,2269 ---- + hostname() String name of the machine Vim is running on + iconv({expr}, {from}, {to}) String convert encoding of {expr} + indent({lnum}) Number indent of line {lnum} +! index({object}, {expr} [, {start} [, {ic}]]) +! Number index in {object} where {expr} appears + input({prompt} [, {text} [, {completion}]]) + String get input from the user + inputdialog({prompt} [, {text} [, {completion}]]) +*************** +*** 2235,2241 **** + inputrestore() Number restore typeahead + inputsave() Number save and clear typeahead + inputsecret({prompt} [, {text}]) String like input() but hiding the text +! insert({list}, {item} [, {idx}]) List insert {item} in {list} [before {idx}] + invert({expr}) Number bitwise invert + isdirectory({directory}) Number |TRUE| if {directory} is a directory + islocked({expr}) Number |TRUE| if {expr} is locked +--- 2272,2278 ---- + inputrestore() Number restore typeahead + inputsave() Number save and clear typeahead + inputsecret({prompt} [, {text}]) String like input() but hiding the text +! insert({object}, {item} [, {idx}]) List insert {item} in {object} [before {idx}] + invert({expr}) Number bitwise invert + isdirectory({directory}) Number |TRUE| if {directory} is a directory + islocked({expr}) Number |TRUE| if {expr} is locked +*************** +*** 2325,2331 **** + pyxeval({expr}) any evaluate |python_x| expression + range({expr} [, {max} [, {stride}]]) + List items from {expr} to {max} +! readfile({fname} [, {binary} [, {max}]]) + List get list of lines from file {fname} + reg_executing() String get the executing register name + reg_recording() String get the recording register name +--- 2362,2368 ---- + pyxeval({expr}) any evaluate |python_x| expression + range({expr} [, {max} [, {stride}]]) + List items from {expr} to {max} +! readfile({fname} [, {type} [, {max}]]) + List get list of lines from file {fname} + reg_executing() String get the executing register name + reg_recording() String get the recording register name +*************** +*** 2541,2548 **** + winsaveview() Dict save view of current window + winwidth({nr}) Number width of window {nr} + wordcount() Dict get byte/char/word statistics +! writefile({list}, {fname} [, {flags}]) +! Number write list of lines to file {fname} + xor({expr}, {expr}) Number bitwise XOR + + +--- 2577,2584 ---- + winsaveview() Dict save view of current window + winwidth({nr}) Number width of window {nr} + wordcount() Dict get byte/char/word statistics +! writefile({object}, {fname} [, {flags}]) +! Number write |Blob| or |List| of lines to file + xor({expr}, {expr}) Number bitwise XOR + + +*************** +*** 3185,3190 **** +--- 3222,3232 ---- + See |channel-more|. + {only available when compiled with the |+channel| feature} + ++ ch_readblob({handle} [, {options}]) *ch_readblob()* ++ Like ch_read() but reads binary data and returns a Blob. ++ See |channel-more|. ++ {only available when compiled with the |+channel| feature} ++ + ch_readraw({handle} [, {options}]) *ch_readraw()* + Like ch_read() but for a JS and JSON channel does not decode + the message. For a NL channel it does not block waiting for +*************** +*** 3201,3208 **** + + {only available when compiled with the |+channel| feature} + +! ch_sendraw({handle}, {string} [, {options}]) *ch_sendraw()* +! Send {string} over {handle}. + Works like |ch_sendexpr()|, but does not encode the request or + decode the response. The caller is responsible for the + correct contents. Also does not add a newline for a channel +--- 3243,3250 ---- + + {only available when compiled with the |+channel| feature} + +! ch_sendraw({handle}, {expr} [, {options}]) *ch_sendraw()* +! Send string or Blob {expr} over {handle}. + Works like |ch_sendexpr()|, but does not encode the request or + decode the response. The caller is responsible for the + correct contents. Also does not add a newline for a channel +*************** +*** 5355,5371 **** + When {lnum} is invalid -1 is returned. + + +! index({list}, {expr} [, {start} [, {ic}]]) *index()* +! Return the lowest index in |List| {list} where the item has a +! value equal to {expr}. There is no automatic conversion, so +! the String "4" is different from the Number 4. And the number +! 4 is different from the Float 4.0. The value of 'ignorecase' +! is not used here, case always matters. + If {start} is given then start looking at the item with index + {start} (may be negative for an item relative to the end). + When {ic} is given and it is |TRUE|, ignore case. Otherwise + case must match. +! -1 is returned when {expr} is not found in {list}. + Example: > + :let idx = index(words, "the") + :if index(numbers, 123) >= 0 +--- 5403,5423 ---- + When {lnum} is invalid -1 is returned. + + +! index({object}, {expr} [, {start} [, {ic}]]) *index()* +! If {object} is a |List| return the lowest index where the item +! has a value equal to {expr}. There is no automatic +! conversion, so the String "4" is different from the Number 4. +! And the number 4 is different from the Float 4.0. The value +! of 'ignorecase' is not used here, case always matters. +! +! If {object} is |Blob| return the lowest index where the byte +! value is equal to {expr}. +! + If {start} is given then start looking at the item with index + {start} (may be negative for an item relative to the end). + When {ic} is given and it is |TRUE|, ignore case. Otherwise + case must match. +! -1 is returned when {expr} is not found in {object}. + Example: > + :let idx = index(words, "the") + :if index(numbers, 123) >= 0 +*************** +*** 5471,5483 **** + typed on the command-line in response to the issued prompt. + NOTE: Command-line completion is not supported. + +! insert({list}, {item} [, {idx}]) *insert()* +! Insert {item} at the start of |List| {list}. + If {idx} is specified insert {item} before the item with index + {idx}. If {idx} is zero it goes before the first item, just + like omitting {idx}. A negative {idx} is also possible, see + |list-index|. -1 inserts just before the last item. +! Returns the resulting |List|. Examples: > + :let mylist = insert([2, 3, 5], 1) + :call insert(mylist, 4, -1) + :call insert(mylist, 6, len(mylist)) +--- 5523,5538 ---- + typed on the command-line in response to the issued prompt. + NOTE: Command-line completion is not supported. + +! insert({object}, {item} [, {idx}]) *insert()* +! When {object} is a |List| or a |Blob| insert {item} at the start +! of it. +! + If {idx} is specified insert {item} before the item with index + {idx}. If {idx} is zero it goes before the first item, just + like omitting {idx}. A negative {idx} is also possible, see + |list-index|. -1 inserts just before the last item. +! +! Returns the resulting |List| or |Blob|. Examples: > + :let mylist = insert([2, 3, 5], 1) + :call insert(mylist, 4, -1) + :call insert(mylist, 6, len(mylist)) +*************** +*** 5743,5748 **** +--- 5798,5804 ---- + used recursively: [] + Dict as an object (possibly null); when + used recursively: {} ++ Blob as an array of the individual bytes + v:false "false" + v:true "true" + v:none "null" +*************** +*** 6923,6938 **** + range(2, 0) " error! + < + *readfile()* +! readfile({fname} [, {binary} [, {max}]]) + Read file {fname} and return a |List|, each line of the file + as an item. Lines are broken at NL characters. Macintosh + files separated with CR will result in a single long line + (unless a NL appears somewhere). + All NUL characters are replaced with a NL character. +! When {binary} contains "b" binary mode is used: + - When the last line ends in a NL an extra empty list item is + added. + - No CR characters are removed. + Otherwise: + - CR characters that appear before a NL are removed. + - Whether the last line ends in a NL or not does not matter. +--- 6983,7000 ---- + range(2, 0) " error! + < + *readfile()* +! readfile({fname} [, {type} [, {max}]]) + Read file {fname} and return a |List|, each line of the file + as an item. Lines are broken at NL characters. Macintosh + files separated with CR will result in a single long line + (unless a NL appears somewhere). + All NUL characters are replaced with a NL character. +! When {type} contains "b" binary mode is used: + - When the last line ends in a NL an extra empty list item is + added. + - No CR characters are removed. ++ When {type} contains "B" a |Blob| is returned with the binary ++ data of the file unmodified. + Otherwise: + - CR characters that appear before a NL are removed. + - Whether the last line ends in a NL or not does not matter. +*************** +*** 7108,7113 **** +--- 7170,7185 ---- + Example: > + :echo "last item: " . remove(mylist, -1) + :call remove(mylist, 0, 9) ++ remove({blob}, {idx} [, {end}]) ++ Without {end}: Remove the byte at {idx} from |Blob| {blob} and ++ return the byte. ++ With {end}: Remove bytes from {idx} to {end} (inclusive) and ++ return a |Blob| with these bytes. When {idx} points to the same ++ byte as {end} a |Blob| with one byte is returned. When {end} ++ points to a byte before {idx} this is an error. ++ Example: > ++ :echo "last byte: " . remove(myblob, -1) ++ :call remove(mylist, 0, 9) + remove({dict}, {key}) + Remove the entry from {dict} with key {key}. Example: > + :echo "removed " . remove(dict, "one") +*************** +*** 7148,7156 **** + path name) and also keeps a trailing path separator. + + *reverse()* +! reverse({list}) Reverse the order of items in {list} in-place. Returns +! {list}. +! If you want a list to remain unmodified make a copy first: > + :let revlist = reverse(copy(mylist)) + + round({expr}) *round()* +--- 7220,7230 ---- + path name) and also keeps a trailing path separator. + + *reverse()* +! reverse({object}) +! Reverse the order of items in {object} in-place. +! {object} can be a |List| or a |Blob|. +! Returns {object}. +! If you want an object to remain unmodified make a copy first: > + :let revlist = reverse(copy(mylist)) + + round({expr}) *round()* +*************** +*** 9490,9495 **** +--- 9568,9574 ---- + None 7 |v:t_none| (v:null and v:none) + Job 8 |v:t_job| + Channel 9 |v:t_channel| ++ Blob 10 |v:t_blob| + For backward compatibility, this method can be used: > + :if type(myvar) == type(0) + :if type(myvar) == type("") +*************** +*** 9837,9850 **** + + + *writefile()* +! writefile({list}, {fname} [, {flags}]) +! Write |List| {list} to file {fname}. Each list item is +! separated with a NL. Each list item must be a String or +! Number. + When {flags} contains "b" then binary mode is used: There will + not be a NL after the last list item. An empty item at the + end does cause the last line in the file to end in a NL. + + When {flags} contains "a" then append mode is used, lines are + appended to the file: > + :call writefile(["foo"], "event.log", "a") +--- 9916,9932 ---- + + + *writefile()* +! writefile({object}, {fname} [, {flags}]) +! When {object} is a |List| write it to file {fname}. Each list +! item is separated with a NL. Each list item must be a String +! or Number. + When {flags} contains "b" then binary mode is used: There will + not be a NL after the last list item. An empty item at the + end does cause the last line in the file to end in a NL. + ++ When {object} is a |Blob| write the bytes to file {fname} ++ unmodified. ++ + When {flags} contains "a" then append mode is used, lines are + appended to the file: > + :call writefile(["foo"], "event.log", "a") +*************** +*** 10546,10552 **** + This cannot be used to set a byte in a String. You + can do that like this: > + :let var = var[0:2] . 'X' . var[4:] +! < + *E711* *E719* + :let {var-name}[{idx1}:{idx2}] = {expr1} *E708* *E709* *E710* + Set a sequence of items in a |List| to the result of +--- 10629,10638 ---- + This cannot be used to set a byte in a String. You + can do that like this: > + :let var = var[0:2] . 'X' . var[4:] +! < When {var-name} is a |Blob| then {idx} can be the +! length of the blob, in which case one byte is +! appended. +! + *E711* *E719* + :let {var-name}[{idx1}:{idx2}] = {expr1} *E708* *E709* *E710* + Set a sequence of items in a |List| to the result of +*** ../vim-8.1.0734/runtime/doc/if_perl.txt 2018-05-17 13:41:41.000000000 +0200 +--- runtime/doc/if_perl.txt 2019-01-12 18:52:14.238595885 +0100 +*************** +*** 190,195 **** +--- 191,199 ---- + A |List| is turned into a string by joining the items + and inserting line breaks. + ++ *perl-Blob* ++ VIM::Blob({expr}) Return Blob literal string 0zXXXX from scalar value. ++ + *perl-SetHeight* + Window->SetHeight({height}) + Sets the Window height to {height}, within screen +*** ../vim-8.1.0734/runtime/doc/if_ruby.txt 2018-07-28 17:29:15.757096343 +0200 +--- runtime/doc/if_ruby.txt 2019-01-12 18:52:14.238595885 +0100 +*************** +*** 110,115 **** +--- 110,119 ---- + Vim::message({msg}) + Displays the message {msg}. + ++ *ruby-blob* ++ Vim::blob({arg}) ++ Return Blob literal string from {arg}. ++ + *ruby-set_option* + Vim::set_option({arg}) + Sets a vim option. {arg} can be any argument that the ":set" command +*** ../vim-8.1.0734/src/Make_cyg_ming.mak 2019-01-12 16:10:47.415360504 +0100 +--- src/Make_cyg_ming.mak 2019-01-12 18:52:14.238595885 +0100 +*************** +*** 696,701 **** +--- 696,702 ---- + OBJ = \ + $(OUTDIR)/arabic.o \ + $(OUTDIR)/beval.o \ ++ $(OUTDIR)/blob.o \ + $(OUTDIR)/blowfish.o \ + $(OUTDIR)/buffer.o \ + $(OUTDIR)/charset.o \ +*** ../vim-8.1.0734/src/Make_mvc.mak 2019-01-12 16:10:47.415360504 +0100 +--- src/Make_mvc.mak 2019-01-12 18:52:14.238595885 +0100 +*************** +*** 701,706 **** +--- 701,707 ---- + OBJ = \ + $(OUTDIR)\arabic.obj \ + $(OUTDIR)\beval.obj \ ++ $(OUTDIR)\blob.obj \ + $(OUTDIR)\blowfish.obj \ + $(OUTDIR)\buffer.obj \ + $(OUTDIR)\charset.obj \ +*************** +*** 1346,1351 **** +--- 1347,1354 ---- + + $(OUTDIR)/beval.obj: $(OUTDIR) beval.c $(INCL) + ++ $(OUTDIR)/blob.obj: $(OUTDIR) blob.c $(INCL) ++ + $(OUTDIR)/blowfish.obj: $(OUTDIR) blowfish.c $(INCL) + + $(OUTDIR)/buffer.obj: $(OUTDIR) buffer.c $(INCL) +*************** +*** 1616,1621 **** +--- 1619,1625 ---- + # End Custom Build + proto.h: \ + proto/arabic.pro \ ++ proto/blob.pro \ + proto/blowfish.pro \ + proto/buffer.pro \ + proto/charset.pro \ +*** ../vim-8.1.0734/src/Makefile 2019-01-12 16:10:47.415360504 +0100 +--- src/Makefile 2019-01-12 18:52:14.238595885 +0100 +*************** +*** 1577,1582 **** +--- 1579,1585 ---- + BASIC_SRC = \ + arabic.c \ + beval.c \ ++ blob.c \ + blowfish.c \ + buffer.c \ + charset.c \ +*************** +*** 1693,1698 **** +--- 1696,1702 ---- + objects/arabic.o \ + objects/beval.o \ + objects/buffer.o \ ++ objects/blob.o \ + objects/blowfish.o \ + objects/crypt.o \ + objects/crypt_zip.o \ +*************** +*** 2943,2948 **** +--- 2947,2955 ---- + objects/arabic.o: arabic.c + $(CCC) -o $@ arabic.c + ++ objects/blob.o: blob.c ++ $(CCC) -o $@ blob.c ++ + objects/blowfish.o: blowfish.c + $(CCC) -o $@ blowfish.c + +*************** +*** 3395,3400 **** +--- 3402,3411 ---- + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h ++ objects/blob.o: blob.c vim.h protodef.h auto/config.h feature.h os_unix.h \ ++ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ ++ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ ++ proto.h globals.h farsi.h arabic.h + objects/blowfish.o: blowfish.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ +*** ../vim-8.1.0734/src/blob.c 2019-01-12 22:40:58.041219177 +0100 +--- src/blob.c 2019-01-12 20:30:51.295186522 +0100 +*************** +*** 0 **** +--- 1,167 ---- ++ /* vi:set ts=8 sts=4 sw=4 noet: ++ * ++ * VIM - Vi IMproved by Bram Moolenaar ++ * ++ * Do ":help uganda" in Vim to read copying and usage conditions. ++ * Do ":help credits" in Vim to see a list of people who contributed. ++ * See README.txt for an overview of the Vim source code. ++ */ ++ ++ /* ++ * blob.c: Blob support by Yasuhiro Matsumoto ++ */ ++ ++ #include "vim.h" ++ ++ #if defined(FEAT_EVAL) || defined(PROTO) ++ ++ /* ++ * Allocate an empty blob. ++ * Caller should take care of the reference count. ++ */ ++ blob_T * ++ blob_alloc(void) ++ { ++ blob_T *blob = (blob_T *)alloc_clear(sizeof(blob_T)); ++ ++ if (blob != NULL) ++ ga_init2(&blob->bv_ga, 1, 100); ++ return blob; ++ } ++ ++ /* ++ * Allocate an empty blob for a return value, with reference count set. ++ * Returns OK or FAIL. ++ */ ++ int ++ rettv_blob_alloc(typval_T *rettv) ++ { ++ blob_T *b = blob_alloc(); ++ ++ if (b == NULL) ++ return FAIL; ++ ++ rettv_blob_set(rettv, b); ++ return OK; ++ } ++ ++ /* ++ * Set a blob as the return value. ++ */ ++ void ++ rettv_blob_set(typval_T *rettv, blob_T *b) ++ { ++ rettv->v_type = VAR_BLOB; ++ rettv->vval.v_blob = b; ++ if (b != NULL) ++ ++b->bv_refcount; ++ } ++ ++ void ++ blob_free(blob_T *b) ++ { ++ ga_clear(&b->bv_ga); ++ vim_free(b); ++ } ++ ++ /* ++ * Unreference a blob: decrement the reference count and free it when it ++ * becomes zero. ++ */ ++ void ++ blob_unref(blob_T *b) ++ { ++ if (b != NULL && --b->bv_refcount <= 0) ++ blob_free(b); ++ } ++ ++ /* ++ * Get the length of data. ++ */ ++ long ++ blob_len(blob_T *b) ++ { ++ if (b == NULL) ++ return 0L; ++ return b->bv_ga.ga_len; ++ } ++ ++ /* ++ * Get byte "idx" in blob "b". ++ * Caller must check that "idx" is valid. ++ */ ++ char_u ++ blob_get(blob_T *b, int idx) ++ { ++ return ((char_u*)b->bv_ga.ga_data)[idx]; ++ } ++ ++ /* ++ * Store one byte "c" in blob "b" at "idx". ++ * Caller must make sure that "idx" is valid. ++ */ ++ void ++ blob_set(blob_T *b, int idx, char_u c) ++ { ++ ((char_u*)b->bv_ga.ga_data)[idx] = c; ++ } ++ ++ /* ++ * Return TRUE when two blobs have exactly the same values. ++ */ ++ int ++ blob_equal( ++ blob_T *b1, ++ blob_T *b2) ++ { ++ int i; ++ ++ if (b1 == NULL || b2 == NULL) ++ return FALSE; ++ if (b1 == b2) ++ return TRUE; ++ if (blob_len(b1) != blob_len(b2)) ++ return FALSE; ++ ++ for (i = 0; i < b1->bv_ga.ga_len; i++) ++ if (blob_get(b1, i) != blob_get(b2, i)) return FALSE; ++ return TRUE; ++ } ++ ++ /* ++ * Read "blob" from file "fd". ++ * Return OK or FAIL. ++ */ ++ int ++ read_blob(FILE *fd, blob_T *blob) ++ { ++ struct stat st; ++ ++ if (fstat(fileno(fd), &st) < 0) ++ return FAIL; ++ if (ga_grow(&blob->bv_ga, st.st_size) == FAIL) ++ return FAIL; ++ blob->bv_ga.ga_len = st.st_size; ++ if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) ++ < (size_t)blob->bv_ga.ga_len) ++ return FAIL; ++ return OK; ++ } ++ ++ /* ++ * Write "blob" to file "fd". ++ * Return OK or FAIL. ++ */ ++ int ++ write_blob(FILE *fd, blob_T *blob) ++ { ++ if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) ++ < (size_t)blob->bv_ga.ga_len) ++ { ++ EMSG(_(e_write)); ++ return FAIL; ++ } ++ return OK; ++ } ++ ++ #endif /* defined(FEAT_EVAL) */ +*** ../vim-8.1.0734/src/channel.c 2019-01-05 00:02:52.045705776 +0100 +--- src/channel.c 2019-01-12 20:58:35.124077325 +0100 +*************** +*** 1665,1671 **** + * Returns NULL if there is nothing. + */ + char_u * +! channel_get(channel_T *channel, ch_part_T part) + { + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; +--- 1665,1671 ---- + * Returns NULL if there is nothing. + */ + char_u * +! channel_get(channel_T *channel, ch_part_T part, int *outlen) + { + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; +*************** +*** 1673,1678 **** +--- 1673,1680 ---- + + if (node == NULL) + return NULL; ++ if (outlen != NULL) ++ *outlen += node->rq_buflen; + /* dispose of the node but keep the buffer */ + p = node->rq_buffer; + head->rq_next = node->rq_next; +*************** +*** 1689,1695 **** + * Replaces NUL bytes with NL. + */ + static char_u * +! channel_get_all(channel_T *channel, ch_part_T part) + { + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; +--- 1691,1697 ---- + * Replaces NUL bytes with NL. + */ + static char_u * +! channel_get_all(channel_T *channel, ch_part_T part, int *outlen) + { + readq_T *head = &channel->ch_part[part].ch_head; + readq_T *node = head->rq_next; +*************** +*** 1699,1705 **** + + /* If there is only one buffer just get that one. */ + if (head->rq_next == NULL || head->rq_next->rq_next == NULL) +! return channel_get(channel, part); + + /* Concatenate everything into one buffer. */ + for (node = head->rq_next; node != NULL; node = node->rq_next) +--- 1701,1707 ---- + + /* If there is only one buffer just get that one. */ + if (head->rq_next == NULL || head->rq_next->rq_next == NULL) +! return channel_get(channel, part, outlen); + + /* Concatenate everything into one buffer. */ + for (node = head->rq_next; node != NULL; node = node->rq_next) +*************** +*** 1718,1727 **** + /* Free all buffers */ + do + { +! p = channel_get(channel, part); + vim_free(p); + } while (p != NULL); + + /* turn all NUL into NL */ + while (len > 0) + { +--- 1720,1735 ---- + /* Free all buffers */ + do + { +! p = channel_get(channel, part, NULL); + vim_free(p); + } while (p != NULL); + ++ if (outlen != NULL) ++ { ++ *outlen += len; ++ return res; ++ } ++ + /* turn all NUL into NL */ + while (len > 0) + { +*************** +*** 1893,1899 **** + { + channel_T *channel = (channel_T *)reader->js_cookie; + ch_part_T part = reader->js_cookie_arg; +! char_u *next = channel_get(channel, part); + int keeplen; + int addlen; + char_u *p; +--- 1901,1907 ---- + { + channel_T *channel = (channel_T *)reader->js_cookie; + ch_part_T part = reader->js_cookie_arg; +! char_u *next = channel_get(channel, part, NULL); + int keeplen; + int addlen; + char_u *p; +*************** +*** 1942,1948 **** + if (channel_peek(channel, part) == NULL) + return FALSE; + +! reader.js_buf = channel_get(channel, part); + reader.js_used = 0; + reader.js_fill = channel_fill; + reader.js_cookie = channel; +--- 1950,1956 ---- + if (channel_peek(channel, part) == NULL) + return FALSE; + +! reader.js_buf = channel_get(channel, part, NULL); + reader.js_used = 0; + reader.js_fill = channel_fill; + reader.js_cookie = channel; +*************** +*** 2475,2481 **** + { + char_u *msg; + +! while ((msg = channel_get(channel, part)) != NULL) + { + ch_log(channel, "Dropping message '%s'", (char *)msg); + vim_free(msg); +--- 2483,2489 ---- + { + char_u *msg; + +! while ((msg = channel_get(channel, part, NULL)) != NULL) + { + ch_log(channel, "Dropping message '%s'", (char *)msg); + vim_free(msg); +*************** +*** 2639,2645 **** + if (nl + 1 == buf + node->rq_buflen) + { + /* get the whole buffer, drop the NL */ +! msg = channel_get(channel, part); + *nl = NUL; + } + else +--- 2647,2653 ---- + if (nl + 1 == buf + node->rq_buflen) + { + /* get the whole buffer, drop the NL */ +! msg = channel_get(channel, part, NULL); + *nl = NUL; + } + else +*************** +*** 2655,2661 **** + /* For a raw channel we don't know where the message ends, just + * get everything we have. + * Convert NUL to NL, the internal representation. */ +! msg = channel_get_all(channel, part); + } + + if (msg == NULL) +--- 2663,2669 ---- + /* For a raw channel we don't know where the message ends, just + * get everything we have. + * Convert NUL to NL, the internal representation. */ +! msg = channel_get_all(channel, part, NULL); + } + + if (msg == NULL) +*************** +*** 3007,3013 **** + cbq_T *cb_head = &ch_part->ch_cb_head; + + while (channel_peek(channel, part) != NULL) +! vim_free(channel_get(channel, part)); + + while (cb_head->cq_next != NULL) + { +--- 3015,3021 ---- + cbq_T *cb_head = &ch_part->ch_cb_head; + + while (channel_peek(channel, part) != NULL) +! vim_free(channel_get(channel, part, NULL)); + + while (cb_head->cq_next != NULL) + { +*************** +*** 3381,3387 **** + * Returns NULL in case of error or timeout. + */ + static char_u * +! channel_read_block(channel_T *channel, ch_part_T part, int timeout, int raw) + { + char_u *buf; + char_u *msg; +--- 3389,3396 ---- + * Returns NULL in case of error or timeout. + */ + static char_u * +! channel_read_block( +! channel_T *channel, ch_part_T part, int timeout, int raw, int *outlen) + { + char_u *buf; + char_u *msg; +*************** +*** 3422,3430 **** + } + + /* We have a complete message now. */ +! if (mode == MODE_RAW) + { +! msg = channel_get_all(channel, part); + } + else + { +--- 3431,3439 ---- + } + + /* We have a complete message now. */ +! if (mode == MODE_RAW || outlen != NULL) + { +! msg = channel_get_all(channel, part, outlen); + } + else + { +*************** +*** 3441,3452 **** + if (nl == NULL) + { + /* must be a closed channel with missing NL */ +! msg = channel_get(channel, part); + } + else if (nl + 1 == buf + node->rq_buflen) + { + /* get the whole buffer */ +! msg = channel_get(channel, part); + *nl = NUL; + } + else +--- 3450,3461 ---- + if (nl == NULL) + { + /* must be a closed channel with missing NL */ +! msg = channel_get(channel, part, NULL); + } + else if (nl + 1 == buf + node->rq_buflen) + { + /* get the whole buffer */ +! msg = channel_get(channel, part, NULL); + *nl = NUL; + } + else +*************** +*** 3554,3560 **** + * Common for ch_read() and ch_readraw(). + */ + void +! common_channel_read(typval_T *argvars, typval_T *rettv, int raw) + { + channel_T *channel; + ch_part_T part = PART_COUNT; +--- 3563,3569 ---- + * Common for ch_read() and ch_readraw(). + */ + void +! common_channel_read(typval_T *argvars, typval_T *rettv, int raw, int blob) + { + channel_T *channel; + ch_part_T part = PART_COUNT; +*************** +*** 3585,3593 **** + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + +! if (raw || mode == MODE_RAW || mode == MODE_NL) + rettv->vval.v_string = channel_read_block(channel, part, +! timeout, raw); + else + { + if (opt.jo_set & JO_ID) +--- 3594,3625 ---- + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + +! if (blob) +! { +! int outlen = 0; +! char_u *p = channel_read_block(channel, part, +! timeout, TRUE, &outlen); +! if (p != NULL) +! { +! blob_T *b = blob_alloc(); +! +! if (b != NULL) +! { +! b->bv_ga.ga_len = outlen; +! if (ga_grow(&b->bv_ga, outlen) == FAIL) +! blob_free(b); +! else +! { +! memcpy(b->bv_ga.ga_data, p, outlen); +! rettv_blob_set(rettv, b); +! } +! } +! vim_free(p); +! } +! } +! else if (raw || mode == MODE_RAW || mode == MODE_NL) + rettv->vval.v_string = channel_read_block(channel, part, +! timeout, raw, NULL); + else + { + if (opt.jo_set & JO_ID) +*************** +*** 3905,3910 **** +--- 3937,3943 ---- + send_common( + typval_T *argvars, + char_u *text, ++ int len, + int id, + int eval, + jobopt_T *opt, +*************** +*** 3938,3944 **** + opt->jo_callback, opt->jo_partial, id); + } + +! if (channel_send(channel, part_send, text, (int)STRLEN(text), fun) == OK + && opt->jo_callback == NULL) + return channel; + return NULL; +--- 3971,3977 ---- + opt->jo_callback, opt->jo_partial, id); + } + +! if (channel_send(channel, part_send, text, len, fun) == OK + && opt->jo_callback == NULL) + return channel; + return NULL; +*************** +*** 3982,3988 **** + if (text == NULL) + return; + +! channel = send_common(argvars, text, id, eval, &opt, + eval ? "ch_evalexpr" : "ch_sendexpr", &part_read); + vim_free(text); + if (channel != NULL && eval) +--- 4015,4021 ---- + if (text == NULL) + return; + +! channel = send_common(argvars, text, (int)STRLEN(text), id, eval, &opt, + eval ? "ch_evalexpr" : "ch_sendexpr", &part_read); + vim_free(text); + if (channel != NULL && eval) +*************** +*** 4014,4019 **** +--- 4047,4053 ---- + { + char_u buf[NUMBUFLEN]; + char_u *text; ++ int len; + channel_T *channel; + ch_part_T part_read; + jobopt_T opt; +*************** +*** 4023,4030 **** + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + +! text = tv_get_string_buf(&argvars[1], buf); +! channel = send_common(argvars, text, 0, eval, &opt, + eval ? "ch_evalraw" : "ch_sendraw", &part_read); + if (channel != NULL && eval) + { +--- 4057,4073 ---- + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + +! if (argvars[1].v_type == VAR_BLOB) +! { +! text = argvars[1].vval.v_blob->bv_ga.ga_data; +! len = argvars[1].vval.v_blob->bv_ga.ga_len; +! } +! else +! { +! text = tv_get_string_buf(&argvars[1], buf); +! len = STRLEN(text); +! } +! channel = send_common(argvars, text, len, 0, eval, &opt, + eval ? "ch_evalraw" : "ch_sendraw", &part_read); + if (channel != NULL && eval) + { +*************** +*** 4033,4039 **** + else + timeout = channel_get_timeout(channel, part_read); + rettv->vval.v_string = channel_read_block(channel, part_read, +! timeout, TRUE); + } + free_job_options(&opt); + } +--- 4076,4082 ---- + else + timeout = channel_get_timeout(channel, part_read); + rettv->vval.v_string = channel_read_block(channel, part_read, +! timeout, TRUE, NULL); + } + free_job_options(&opt); + } +*** ../vim-8.1.0734/src/eval.c 2018-12-22 13:27:59.115503998 +0100 +--- src/eval.c 2019-01-12 22:02:53.262933357 +0100 +*************** +*** 78,83 **** +--- 78,85 ---- + int fi_varcount; /* nr of variables in the list */ + listwatch_T fi_lw; /* keep an eye on the item used. */ + list_T *fi_list; /* list being used */ ++ int fi_bi; /* index of blob */ ++ blob_T *fi_blob; /* blob being used */ + } forinfo_T; + + +*************** +*** 187,192 **** +--- 189,195 ---- + {VV_NAME("t_none", VAR_NUMBER), VV_RO}, + {VV_NAME("t_job", VAR_NUMBER), VV_RO}, + {VV_NAME("t_channel", VAR_NUMBER), VV_RO}, ++ {VV_NAME("t_blob", VAR_NUMBER), VV_RO}, + {VV_NAME("termrfgresp", VAR_STRING), VV_RO}, + {VV_NAME("termrbgresp", VAR_STRING), VV_RO}, + {VV_NAME("termu7resp", VAR_STRING), VV_RO}, +*************** +*** 202,207 **** +--- 205,211 ---- + #define vv_str vv_di.di_tv.vval.v_string + #define vv_list vv_di.di_tv.vval.v_list + #define vv_dict vv_di.di_tv.vval.v_dict ++ #define vv_blob vv_di.di_tv.vval.v_blob + #define vv_tv vv_di.di_tv + + static dictitem_T vimvars_var; /* variable used for v: */ +*************** +*** 338,343 **** +--- 342,348 ---- + set_vim_var_nr(VV_TYPE_NONE, VAR_TYPE_NONE); + set_vim_var_nr(VV_TYPE_JOB, VAR_TYPE_JOB); + set_vim_var_nr(VV_TYPE_CHANNEL, VAR_TYPE_CHANNEL); ++ set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB); + + set_reg_var(0); /* default for v:register is not 0 but '"' */ + +*************** +*** 1918,1927 **** + { + if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) + && !(lp->ll_tv->v_type == VAR_DICT +! && lp->ll_tv->vval.v_dict != NULL)) + { + if (!quiet) +! EMSG(_("E689: Can only index a List or Dictionary")); + return NULL; + } + if (lp->ll_range) +--- 1923,1934 ---- + { + if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) + && !(lp->ll_tv->v_type == VAR_DICT +! && lp->ll_tv->vval.v_dict != NULL) +! && !(lp->ll_tv->v_type == VAR_BLOB +! && lp->ll_tv->vval.v_blob != NULL)) + { + if (!quiet) +! EMSG(_("E689: Can only index a List, Dictionary or Blob")); + return NULL; + } + if (lp->ll_range) +*************** +*** 1974,1984 **** + clear_tv(&var1); + return NULL; + } +! if (rettv != NULL && (rettv->v_type != VAR_LIST +! || rettv->vval.v_list == NULL)) + { + if (!quiet) +! EMSG(_("E709: [:] requires a List value")); + clear_tv(&var1); + return NULL; + } +--- 1981,1994 ---- + clear_tv(&var1); + return NULL; + } +! if (rettv != NULL +! && !(rettv->v_type == VAR_LIST +! || rettv->vval.v_list != NULL) +! && !(rettv->v_type == VAR_BLOB +! || rettv->vval.v_blob != NULL)) + { + if (!quiet) +! EMSG(_("E709: [:] requires a List or Blob value")); + clear_tv(&var1); + return NULL; + } +*************** +*** 2097,2102 **** +--- 2107,2139 ---- + clear_tv(&var1); + lp->ll_tv = &lp->ll_di->di_tv; + } ++ else if (lp->ll_tv->v_type == VAR_BLOB) ++ { ++ /* ++ * Get the number and item for the only or first index of the List. ++ */ ++ if (empty1) ++ lp->ll_n1 = 0; ++ else ++ // is number or string ++ lp->ll_n1 = (long)tv_get_number(&var1); ++ clear_tv(&var1); ++ ++ if (lp->ll_n1 < 0 ++ || lp->ll_n1 > blob_len(lp->ll_tv->vval.v_blob)) ++ { ++ if (!quiet) ++ EMSGN(_(e_listidx), lp->ll_n1); ++ return NULL; ++ } ++ if (lp->ll_range && !lp->ll_empty2) ++ { ++ lp->ll_n2 = (long)tv_get_number(&var2); ++ clear_tv(&var2); ++ } ++ lp->ll_blob = lp->ll_tv->vval.v_blob; ++ lp->ll_tv = NULL; ++ } + else + { + /* +*************** +*** 2201,2207 **** + { + cc = *endp; + *endp = NUL; +! if (op != NULL && *op != '=') + { + typval_T tv; + +--- 2238,2289 ---- + { + cc = *endp; + *endp = NUL; +! if (lp->ll_blob != NULL) +! { +! int error = FALSE, val; +! if (op != NULL && *op != '=') +! { +! EMSG2(_(e_letwrong), op); +! return; +! } +! +! if (lp->ll_range && rettv->v_type == VAR_BLOB) +! { +! int i; +! +! if (blob_len(rettv->vval.v_blob) != blob_len(lp->ll_blob)) +! { +! EMSG(_("E972: Blob value has more items than target")); +! return; +! } +! +! for (i = lp->ll_n1; i <= lp->ll_n2; i++) +! blob_set(lp->ll_blob, i, +! blob_get(rettv->vval.v_blob, i)); +! } +! else +! { +! val = (int)tv_get_number_chk(rettv, &error); +! if (!error) +! { +! garray_T *gap = &lp->ll_blob->bv_ga; +! +! // Allow for appending a byte. Setting a byte beyond +! // the end is an error otherwise. +! if (lp->ll_n1 < gap->ga_len +! || (lp->ll_n1 == gap->ga_len +! && ga_grow(&lp->ll_blob->bv_ga, 1) == OK)) +! { +! blob_set(lp->ll_blob, lp->ll_n1, val); +! if (lp->ll_n1 == gap->ga_len) +! ++gap->ga_len; +! } +! else +! EMSG(_(e_invrange)); +! } +! } +! } +! else if (op != NULL && *op != '=') + { + typval_T tv; + +*************** +*** 2352,2357 **** +--- 2434,2453 ---- + case VAR_CHANNEL: + break; + ++ case VAR_BLOB: ++ if (*op != '+' || tv2->v_type != VAR_BLOB) ++ break; ++ // BLOB += BLOB ++ if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL) ++ { ++ blob_T *b1 = tv1->vval.v_blob; ++ blob_T *b2 = tv2->vval.v_blob; ++ int i, len = blob_len(b2); ++ for (i = 0; i < len; i++) ++ ga_append(&b1->bv_ga, blob_get(b2, i)); ++ } ++ return OK; ++ + case VAR_LIST: + if (*op != '+' || tv2->v_type != VAR_LIST) + break; +*************** +*** 2451,2456 **** +--- 2547,2553 ---- + char_u *expr; + typval_T tv; + list_T *l; ++ blob_T *b; + + *errp = TRUE; /* default: there is an error */ + +*************** +*** 2476,2499 **** + *errp = FALSE; + if (!skip) + { +! l = tv.vval.v_list; +! if (tv.v_type != VAR_LIST) + { +! EMSG(_(e_listreq)); +! clear_tv(&tv); + } +! else if (l == NULL) + { +! /* a null list is like an empty list: do nothing */ +! clear_tv(&tv); + } + else + { +! /* No need to increment the refcount, it's already set for the +! * list being used in "tv". */ +! fi->fi_list = l; +! list_add_watch(l, &fi->fi_lw); +! fi->fi_lw.lw_item = l->lv_first; + } + } + } +--- 2573,2610 ---- + *errp = FALSE; + if (!skip) + { +! if (tv.v_type == VAR_LIST) + { +! l = tv.vval.v_list; +! if (l == NULL) +! { +! // a null list is like an empty list: do nothing +! clear_tv(&tv); +! } +! else +! { +! // No need to increment the refcount, it's already set for +! // the list being used in "tv". +! fi->fi_list = l; +! list_add_watch(l, &fi->fi_lw); +! fi->fi_lw.lw_item = l->lv_first; +! } + } +! else if (tv.v_type == VAR_BLOB) + { +! b = tv.vval.v_blob; +! if (b == NULL) +! clear_tv(&tv); +! else +! { +! fi->fi_blob = b; +! fi->fi_bi = 0; +! } + } + else + { +! EMSG(_(e_listreq)); +! clear_tv(&tv); + } + } + } +*************** +*** 2516,2521 **** +--- 2627,2646 ---- + int result; + listitem_T *item; + ++ if (fi->fi_blob != NULL) ++ { ++ typval_T tv; ++ ++ if (fi->fi_bi >= blob_len(fi->fi_blob)) ++ return FALSE; ++ tv.v_type = VAR_NUMBER; ++ tv.v_lock = VAR_FIXED; ++ tv.vval.v_number = blob_get(fi->fi_blob, fi->fi_bi); ++ ++fi->fi_bi; ++ return ex_let_vars(arg, &tv, TRUE, ++ fi->fi_semicolon, fi->fi_varcount, NULL) == OK; ++ } ++ + item = fi->fi_lw.lw_item; + if (item == NULL) + result = FALSE; +*************** +*** 2955,2960 **** +--- 3080,3086 ---- + list_T *l; + listitem_T *li; + dict_T *d; ++ blob_T *b; + hashitem_T *hi; + int todo; + +*************** +*** 2986,2991 **** +--- 3112,3126 ---- + case VAR_CHANNEL: + break; + ++ case VAR_BLOB: ++ if ((b = tv->vval.v_blob) != NULL) ++ { ++ if (lock) ++ b->bv_lock |= VAR_LOCKED; ++ else ++ b->bv_lock &= ~VAR_LOCKED; ++ } ++ break; + case VAR_LIST: + if ((l = tv->vval.v_list) != NULL) + { +*************** +*** 3609,3615 **** + if (op != '+' && op != '-' && op != '.') + break; + +! if ((op != '+' || rettv->v_type != VAR_LIST) + #ifdef FEAT_FLOAT + && (op == '.' || rettv->v_type != VAR_FLOAT) + #endif +--- 3744,3751 ---- + if (op != '+' && op != '-' && op != '.') + break; + +! if ((op != '+' || (rettv->v_type != VAR_LIST +! && rettv->v_type != VAR_BLOB)) + #ifdef FEAT_FLOAT + && (op == '.' || rettv->v_type != VAR_FLOAT) + #endif +*************** +*** 3659,3664 **** +--- 3795,3819 ---- + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p; + } ++ else if (op == '+' && rettv->v_type == VAR_BLOB ++ && var2.v_type == VAR_BLOB) ++ { ++ blob_T *b1 = rettv->vval.v_blob; ++ blob_T *b2 = var2.vval.v_blob; ++ blob_T *b = blob_alloc(); ++ int i; ++ ++ if (b != NULL) ++ { ++ for (i = 0; i < blob_len(b1); i++) ++ ga_append(&b->bv_ga, blob_get(b1, i)); ++ for (i = 0; i < blob_len(b2); i++) ++ ga_append(&b->bv_ga, blob_get(b2, i)); ++ ++ clear_tv(rettv); ++ rettv_blob_set(rettv, b); ++ } ++ } + else if (op == '+' && rettv->v_type == VAR_LIST + && var2.v_type == VAR_LIST) + { +*************** +*** 3921,3926 **** +--- 4076,4082 ---- + /* + * Handle sixth level expression: + * number number constant ++ * 0zFFFFFFFF Blob constant + * "string" string constant + * 'string' literal string constant + * &option-name option value +*************** +*** 4027,4033 **** +--- 4183,4220 ---- + } + else + #endif ++ if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z')) + { ++ char_u *bp; ++ blob_T *blob; ++ ++ // Blob constant: 0z0123456789abcdef ++ if (evaluate) ++ blob = blob_alloc(); ++ for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2) ++ { ++ if (!vim_isxdigit(bp[1])) ++ { ++ EMSG(_("E973: Blob literal should have an even number of hex characters'")); ++ vim_free(blob); ++ ret = FAIL; ++ break; ++ } ++ if (blob != NULL) ++ ga_append(&blob->bv_ga, ++ (hex2nr(*bp) << 4) + hex2nr(*(bp+1))); ++ } ++ if (blob != NULL) ++ { ++ ++blob->bv_refcount; ++ rettv->v_type = VAR_BLOB; ++ rettv->vval.v_blob = blob; ++ } ++ *arg = bp; ++ } ++ else ++ { ++ // decimal, hex or octal number + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0); + *arg += len; + if (evaluate) +*************** +*** 4263,4268 **** +--- 4450,4456 ---- + { + int empty1 = FALSE, empty2 = FALSE; + typval_T var1, var2; ++ long i; + long n1, n2 = 0; + long len = -1; + int range = FALSE; +*************** +*** 4297,4302 **** +--- 4485,4491 ---- + case VAR_NUMBER: + case VAR_LIST: + case VAR_DICT: ++ case VAR_BLOB: + break; + } + +*************** +*** 4439,4444 **** +--- 4628,4694 ---- + rettv->vval.v_string = s; + break; + ++ case VAR_BLOB: ++ len = blob_len(rettv->vval.v_blob); ++ if (range) ++ { ++ // The resulting variable is a substring. If the indexes ++ // are out of range the result is empty. ++ if (n1 < 0) ++ { ++ n1 = len + n1; ++ if (n1 < 0) ++ n1 = 0; ++ } ++ if (n2 < 0) ++ n2 = len + n2; ++ else if (n2 >= len) ++ n2 = len - 1; ++ if (n1 >= len || n2 < 0 || n1 > n2) ++ { ++ clear_tv(rettv); ++ rettv->v_type = VAR_BLOB; ++ rettv->vval.v_blob = NULL; ++ } ++ else ++ { ++ blob_T *blob = blob_alloc(); ++ ++ if (blob != NULL) ++ { ++ if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL) ++ { ++ blob_free(blob); ++ return FAIL; ++ } ++ blob->bv_ga.ga_len = n2 - n1 + 1; ++ for (i = n1; i <= n2; i++) ++ blob_set(blob, i - n1, ++ blob_get(rettv->vval.v_blob, i)); ++ ++ clear_tv(rettv); ++ rettv_blob_set(rettv, blob); ++ } ++ } ++ } ++ else ++ { ++ // The resulting variable is a string of a single ++ // character. If the index is too big or negative the ++ // result is empty. ++ if (n1 < len && n1 >= 0) ++ { ++ int v = (int)blob_get(rettv->vval.v_blob, n1); ++ ++ clear_tv(rettv); ++ rettv->v_type = VAR_NUMBER; ++ rettv->vval.v_number = v; ++ } ++ else ++ EMSGN(_(e_blobidx), n1); ++ } ++ break; ++ + case VAR_LIST: + len = list_len(rettv->vval.v_list); + if (n1 < 0) +*************** +*** 4970,4975 **** +--- 5220,5228 ---- + --recursive_cnt; + return r; + ++ case VAR_BLOB: ++ return blob_equal(tv1->vval.v_blob, tv2->vval.v_blob); ++ + case VAR_NUMBER: + return tv1->vval.v_number == tv2->vval.v_number; + +*************** +*** 5602,5607 **** +--- 5855,5890 ---- + break; + } + ++ case VAR_BLOB: ++ if (tv->vval.v_blob == NULL) ++ { ++ *tofree = NULL; ++ r = (char_u *)"[]"; ++ } ++ else ++ { ++ blob_T *b; ++ int i; ++ garray_T ga; ++ ++ // Store bytes in the growarray. ++ ga_init2(&ga, 1, 4000); ++ b = tv->vval.v_blob; ++ ga_append(&ga, '['); ++ for (i = 0; i < blob_len(b); i++) ++ { ++ if (i > 0) ++ ga_concat(&ga, (char_u *)","); ++ vim_snprintf((char *)numbuf, NUMBUFLEN, "0x%02X", ++ (int)blob_get(b, i)); ++ ga_concat(&ga, numbuf); ++ } ++ ga_append(&ga, ']'); ++ *tofree = ga.ga_data; ++ r = *tofree; ++ } ++ break; ++ + case VAR_LIST: + if (tv->vval.v_list == NULL) + { +*************** +*** 6841,6846 **** +--- 7124,7132 ---- + case VAR_PARTIAL: + partial_unref(varp->vval.v_partial); + break; ++ case VAR_BLOB: ++ blob_unref(varp->vval.v_blob); ++ break; + case VAR_LIST: + list_unref(varp->vval.v_list); + break; +*************** +*** 6887,6892 **** +--- 7173,7182 ---- + partial_unref(varp->vval.v_partial); + varp->vval.v_partial = NULL; + break; ++ case VAR_BLOB: ++ blob_unref(varp->vval.v_blob); ++ varp->vval.v_blob = NULL; ++ break; + case VAR_LIST: + list_unref(varp->vval.v_list); + varp->vval.v_list = NULL; +*************** +*** 6990,6995 **** +--- 7280,7288 ---- + EMSG(_("E913: Using a Channel as a Number")); + break; + #endif ++ case VAR_BLOB: ++ EMSG(_("E974: Using a Blob as a Number")); ++ break; + case VAR_UNKNOWN: + internal_error("tv_get_number(UNKNOWN)"); + break; +*************** +*** 7037,7042 **** +--- 7330,7338 ---- + EMSG(_("E914: Using a Channel as a Float")); + break; + # endif ++ case VAR_BLOB: ++ EMSG(_("E975: Using a Blob as a Float")); ++ break; + case VAR_UNKNOWN: + internal_error("tv_get_float(UNKNOWN)"); + break; +*************** +*** 7113,7118 **** +--- 7409,7417 ---- + case VAR_SPECIAL: + STRCPY(buf, get_var_special_name(varp->vval.v_number)); + return buf; ++ case VAR_BLOB: ++ EMSG(_("E976: using Blob as a String")); ++ break; + case VAR_JOB: + #ifdef FEAT_JOB_CHANNEL + { +*************** +*** 7805,7810 **** +--- 8104,8118 ---- + ++to->vval.v_partial->pt_refcount; + } + break; ++ case VAR_BLOB: ++ if (from->vval.v_blob == NULL) ++ to->vval.v_blob = NULL; ++ else ++ { ++ to->vval.v_blob = from->vval.v_blob; ++ ++to->vval.v_blob->bv_refcount; ++ } ++ break; + case VAR_LIST: + if (from->vval.v_list == NULL) + to->vval.v_list = NULL; +*************** +*** 7863,7868 **** +--- 8171,8177 ---- + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: ++ case VAR_BLOB: + copy_tv(from, to); + break; + case VAR_LIST: +*************** +*** 8601,8606 **** +--- 8910,8916 ---- + #endif + case 'D': type = VAR_DICT; break; + case 'L': type = VAR_LIST; break; ++ case 'B': type = VAR_BLOB; break; + case 'X': type = VAR_SPECIAL; break; + } + +*************** +*** 8608,8614 **** + if (tab != NULL) + { + tv.v_type = type; +! if (type == VAR_STRING || type == VAR_DICT || type == VAR_LIST) + tv.vval.v_string = viminfo_readstring(virp, + (int)(tab - virp->vir_line + 1), TRUE); + #ifdef FEAT_FLOAT +--- 8918,8925 ---- + if (tab != NULL) + { + tv.v_type = type; +! if (type == VAR_STRING || type == VAR_DICT || +! type == VAR_LIST || type == VAR_BLOB) + tv.vval.v_string = viminfo_readstring(virp, + (int)(tab - virp->vir_line + 1), TRUE); + #ifdef FEAT_FLOAT +*************** +*** 8617,8623 **** + #endif + else + tv.vval.v_number = atol((char *)tab + 1); +! if (type == VAR_DICT || type == VAR_LIST) + { + typval_T *etv = eval_expr(tv.vval.v_string, NULL); + +--- 8928,8934 ---- + #endif + else + tv.vval.v_number = atol((char *)tab + 1); +! if (type == VAR_DICT || type == VAR_LIST || type == VAR_BLOB) + { + typval_T *etv = eval_expr(tv.vval.v_string, NULL); + +*************** +*** 8640,8646 **** + + if (tv.v_type == VAR_STRING) + vim_free(tv.vval.v_string); +! else if (tv.v_type == VAR_DICT || tv.v_type == VAR_LIST) + clear_tv(&tv); + } + } +--- 8951,8958 ---- + + if (tv.v_type == VAR_STRING) + vim_free(tv.vval.v_string); +! else if (tv.v_type == VAR_DICT || tv.v_type == VAR_LIST || +! tv.v_type == VAR_BLOB) + clear_tv(&tv); + } + } +*************** +*** 8684,8689 **** +--- 8996,9002 ---- + case VAR_FLOAT: s = "FLO"; break; + case VAR_DICT: s = "DIC"; break; + case VAR_LIST: s = "LIS"; break; ++ case VAR_BLOB: s = "BLO"; break; + case VAR_SPECIAL: s = "XPL"; break; + + case VAR_UNKNOWN: +*************** +*** 9250,9255 **** +--- 9563,9595 ---- + * it means TRUE. */ + n1 = (type == TYPE_NEQUAL); + } ++ else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) ++ { ++ if (type_is) ++ { ++ n1 = (typ1->v_type == typ2->v_type ++ && typ1->vval.v_blob == typ2->vval.v_blob); ++ if (type == TYPE_NEQUAL) ++ n1 = !n1; ++ } ++ else if (typ1->v_type != typ2->v_type ++ || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) ++ { ++ if (typ1->v_type != typ2->v_type) ++ EMSG(_("E977: Can only compare Blob with Blob")); ++ else ++ EMSG(_(e_invalblob)); ++ clear_tv(typ1); ++ return FAIL; ++ } ++ else ++ { ++ // Compare two Blobs for being equal or unequal. ++ n1 = blob_equal(typ1->vval.v_blob, typ2->vval.v_blob); ++ if (type == TYPE_NEQUAL) ++ n1 = !n1; ++ } ++ } + else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) + { + if (type_is) +*************** +*** 10278,10283 **** +--- 10618,10624 ---- + dict_T *d = NULL; + typval_T save_val; + typval_T save_key; ++ blob_T *b = NULL; + int rem; + int todo; + char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); +*************** +*** 10286,10292 **** + int save_did_emsg; + int idx = 0; + +! if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) == NULL + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) +--- 10627,10638 ---- + int save_did_emsg; + int idx = 0; + +! if (argvars[0].v_type == VAR_BLOB) +! { +! if ((b = argvars[0].vval.v_blob) == NULL) +! return; +! } +! else if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) == NULL + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) +*************** +*** 10353,10358 **** +--- 10699,10735 ---- + } + hash_unlock(ht); + } ++ else if (argvars[0].v_type == VAR_BLOB) ++ { ++ int i; ++ typval_T tv; ++ ++ vimvars[VV_KEY].vv_type = VAR_NUMBER; ++ for (i = 0; i < b->bv_ga.ga_len; i++) ++ { ++ tv.v_type = VAR_NUMBER; ++ tv.vval.v_number = blob_get(b, i); ++ vimvars[VV_KEY].vv_nr = idx; ++ if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) ++ break; ++ if (tv.v_type != VAR_NUMBER) ++ { ++ EMSG(_(e_invalblob)); ++ return; ++ } ++ tv.v_type = VAR_NUMBER; ++ blob_set(b, i, tv.vval.v_number); ++ if (!map && rem) ++ { ++ char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; ++ ++ mch_memmove(p + idx, p + i + 1, ++ (size_t)b->bv_ga.ga_len - i - 1); ++ --b->bv_ga.ga_len; ++ --i; ++ } ++ } ++ } + else + { + vimvars[VV_KEY].vv_type = VAR_NUMBER; +*** ../vim-8.1.0734/src/evalfunc.c 2019-01-12 13:50:27.712026891 +0100 +--- src/evalfunc.c 2019-01-12 22:21:55.631440482 +0100 +*************** +*** 96,101 **** +--- 96,102 ---- + static void f_ch_logfile(typval_T *argvars, typval_T *rettv); + static void f_ch_open(typval_T *argvars, typval_T *rettv); + static void f_ch_read(typval_T *argvars, typval_T *rettv); ++ static void f_ch_readblob(typval_T *argvars, typval_T *rettv); + static void f_ch_readraw(typval_T *argvars, typval_T *rettv); + static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv); + static void f_ch_sendraw(typval_T *argvars, typval_T *rettv); +*************** +*** 570,575 **** +--- 571,577 ---- + {"ch_logfile", 1, 2, f_ch_logfile}, + {"ch_open", 1, 2, f_ch_open}, + {"ch_read", 1, 2, f_ch_read}, ++ {"ch_readblob", 1, 2, f_ch_readblob}, + {"ch_readraw", 1, 2, f_ch_readraw}, + {"ch_sendexpr", 2, 3, f_ch_sendexpr}, + {"ch_sendraw", 2, 3, f_ch_sendraw}, +*************** +*** 1237,1242 **** +--- 1239,1245 ---- + f_add(typval_T *argvars, typval_T *rettv) + { + list_T *l; ++ blob_T *b; + + rettv->vval.v_number = 1; /* Default: Failed */ + if (argvars[0].v_type == VAR_LIST) +*************** +*** 1247,1252 **** +--- 1250,1265 ---- + && list_append_tv(l, &argvars[1]) == OK) + copy_tv(&argvars[0], rettv); + } ++ else if (argvars[0].v_type == VAR_BLOB) ++ { ++ if ((b = argvars[0].vval.v_blob) != NULL ++ && !tv_check_lock(b->bv_lock, ++ (char_u *)N_("add() argument"), TRUE)) ++ { ++ ga_append(&b->bv_ga, (char_u)tv_get_number(&argvars[1])); ++ copy_tv(&argvars[0], rettv); ++ } ++ } + else + EMSG(_(e_listreq)); + } +*************** +*** 2309,2315 **** + static void + f_ch_read(typval_T *argvars, typval_T *rettv) + { +! common_channel_read(argvars, rettv, FALSE); + } + + /* +--- 2322,2337 ---- + static void + f_ch_read(typval_T *argvars, typval_T *rettv) + { +! common_channel_read(argvars, rettv, FALSE, FALSE); +! } +! +! /* +! * "ch_readblob()" function +! */ +! static void +! f_ch_readblob(typval_T *argvars, typval_T *rettv) +! { +! common_channel_read(argvars, rettv, TRUE, TRUE); + } + + /* +*************** +*** 2318,2324 **** + static void + f_ch_readraw(typval_T *argvars, typval_T *rettv) + { +! common_channel_read(argvars, rettv, TRUE); + } + + /* +--- 2340,2346 ---- + static void + f_ch_readraw(typval_T *argvars, typval_T *rettv) + { +! common_channel_read(argvars, rettv, TRUE, FALSE); + } + + /* +*************** +*** 3170,3175 **** +--- 3192,3203 ---- + n = argvars[0].vval.v_number != VVAL_TRUE; + break; + ++ case VAR_BLOB: ++ n = argvars[0].vval.v_blob == NULL ++ || argvars[0].vval.v_blob->bv_ga.ga_data == NULL ++ || argvars[0].vval.v_blob->bv_ga.ga_len == 0; ++ break; ++ + case VAR_JOB: + #ifdef FEAT_JOB_CHANNEL + n = argvars[0].vval.v_job == NULL +*************** +*** 4365,4371 **** + dict_T *d; + typval_T *tv = NULL; + +! if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) != NULL) + { +--- 4393,4413 ---- + dict_T *d; + typval_T *tv = NULL; + +! if (argvars[0].v_type == VAR_BLOB) +! { +! int error = FALSE; +! int idx = tv_get_number_chk(&argvars[1], &error); +! +! if (!error) +! { +! rettv->v_type = VAR_NUMBER; +! if (idx >= blob_len(argvars[0].vval.v_blob)) +! EMSGN(_(e_blobidx), idx); +! else +! rettv->vval.v_number = blob_get(argvars[0].vval.v_blob, idx); +! } +! } +! else if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) != NULL) + { +*************** +*** 6965,6987 **** + { + list_T *l; + listitem_T *item; + long idx = 0; + int ic = FALSE; + + rettv->vval.v_number = -1; +! if (argvars[0].v_type != VAR_LIST) + { + EMSG(_(e_listreq)); + return; + } + l = argvars[0].vval.v_list; + if (l != NULL) + { + item = l->lv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { +- int error = FALSE; +- + /* Start at specified item. Use the cached index that list_find() + * sets, so that a negative number also works. */ + item = list_find(l, (long)tv_get_number_chk(&argvars[2], &error)); +--- 7007,7056 ---- + { + list_T *l; + listitem_T *item; ++ blob_T *b; + long idx = 0; + int ic = FALSE; ++ int error = FALSE; + + rettv->vval.v_number = -1; +! if (argvars[0].v_type == VAR_BLOB) +! { +! typval_T tv; +! int start = 0; +! +! if (argvars[2].v_type != VAR_UNKNOWN) +! { +! start = tv_get_number_chk(&argvars[2], &error); +! if (error) +! return; +! } +! b = argvars[0].vval.v_blob; +! if (b == NULL) +! return; +! for (idx = start; idx < blob_len(b); ++idx) +! { +! tv.v_type = VAR_NUMBER; +! tv.vval.v_number = blob_get(b, idx); +! if (tv_equal(&tv, &argvars[1], ic, FALSE)) +! { +! rettv->vval.v_number = idx; +! return; +! } +! } +! return; +! } +! else if (argvars[0].v_type != VAR_LIST) + { + EMSG(_(e_listreq)); + return; + } ++ + l = argvars[0].vval.v_list; + if (l != NULL) + { + item = l->lv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* Start at specified item. Use the cached index that list_find() + * sets, so that a negative number also works. */ + item = list_find(l, (long)tv_get_number_chk(&argvars[2], &error)); +*************** +*** 7160,7169 **** + list_T *l; + int error = FALSE; + +! if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listarg), "insert()"); +! else if ((l = argvars[0].vval.v_list) != NULL +! && !tv_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE)) + { + if (argvars[2].v_type != VAR_UNKNOWN) + before = (long)tv_get_number_chk(&argvars[2], &error); +--- 7229,7273 ---- + list_T *l; + int error = FALSE; + +! if (argvars[0].v_type == VAR_BLOB) +! { +! int val, len; +! char_u *p; +! +! len = blob_len(argvars[0].vval.v_blob); +! if (argvars[2].v_type != VAR_UNKNOWN) +! { +! before = (long)tv_get_number_chk(&argvars[2], &error); +! if (error) +! return; // type error; errmsg already given +! if (before < 0 || before > len) +! { +! EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); +! return; +! } +! } +! val = tv_get_number_chk(&argvars[1], &error); +! if (error) +! return; +! if (val < 0 || val > 255) +! { +! EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); +! return; +! } +! +! if (ga_grow(&argvars[0].vval.v_blob->bv_ga, 1) == FAIL) +! return; +! p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; +! mch_memmove(p + before + 1, p + before, (size_t)len - before); +! *(p + before) = val; +! ++argvars[0].vval.v_blob->bv_ga.ga_len; +! +! copy_tv(&argvars[0], rettv); +! } +! else if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listarg), "insert()"); +! else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, +! (char_u *)N_("insert() argument"), TRUE)) + { + if (argvars[2].v_type != VAR_UNKNOWN) + before = (long)tv_get_number_chk(&argvars[2], &error); +*************** +*** 7527,7532 **** +--- 7631,7639 ---- + rettv->vval.v_number = (varnumber_T)STRLEN( + tv_get_string(&argvars[0])); + break; ++ case VAR_BLOB: ++ rettv->vval.v_number = blob_len(argvars[0].vval.v_blob); ++ break; + case VAR_LIST: + rettv->vval.v_number = list_len(argvars[0].vval.v_list); + break; +*************** +*** 8926,8931 **** +--- 9033,9039 ---- + f_readfile(typval_T *argvars, typval_T *rettv) + { + int binary = FALSE; ++ int blob = FALSE; + int failed = FALSE; + char_u *fname; + FILE *fd; +*************** +*** 8944,8955 **** + { + if (STRCMP(tv_get_string(&argvars[1]), "b") == 0) + binary = TRUE; + if (argvars[2].v_type != VAR_UNKNOWN) + maxline = (long)tv_get_number(&argvars[2]); + } + +! if (rettv_list_alloc(rettv) == FAIL) +! return; + + /* Always open the file in binary mode, library functions have a mind of + * their own about CR-LF conversion. */ +--- 9052,9074 ---- + { + if (STRCMP(tv_get_string(&argvars[1]), "b") == 0) + binary = TRUE; ++ if (STRCMP(tv_get_string(&argvars[1]), "B") == 0) ++ blob = TRUE; ++ + if (argvars[2].v_type != VAR_UNKNOWN) + maxline = (long)tv_get_number(&argvars[2]); + } + +! if (blob) +! { +! if (rettv_blob_alloc(rettv) == FAIL) +! return; +! } +! else +! { +! if (rettv_list_alloc(rettv) == FAIL) +! return; +! } + + /* Always open the file in binary mode, library functions have a mind of + * their own about CR-LF conversion. */ +*************** +*** 8960,8965 **** +--- 9079,9095 ---- + return; + } + ++ if (blob) ++ { ++ if (read_blob(fd, rettv->vval.v_blob) == FAIL) ++ { ++ EMSG("cannot read file"); ++ blob_free(rettv->vval.v_blob); ++ } ++ fclose(fd); ++ return; ++ } ++ + while (cnt < maxline || maxline < 0) + { + readlen = (int)fread(buf, 1, io_size, fd); +*************** +*** 9555,9560 **** +--- 9685,9691 ---- + dict_T *d; + dictitem_T *di; + char_u *arg_errmsg = (char_u *)N_("remove() argument"); ++ int error = FALSE; + + if (argvars[0].v_type == VAR_DICT) + { +*************** +*** 9579,9594 **** + } + } + } + else if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listdictarg), "remove()"); + else if ((l = argvars[0].vval.v_list) != NULL +! && !tv_check_lock(l->lv_lock, arg_errmsg, TRUE)) + { +- int error = FALSE; +- + idx = (long)tv_get_number_chk(&argvars[1], &error); + if (error) +! ; /* type error: do nothing, errmsg already given */ + else if ((item = list_find(l, idx)) == NULL) + EMSGN(_(e_listidx), idx); + else +--- 9710,9785 ---- + } + } + } ++ else if (argvars[0].v_type == VAR_BLOB) ++ { ++ idx = (long)tv_get_number_chk(&argvars[1], &error); ++ if (!error) ++ { ++ blob_T *b = argvars[0].vval.v_blob; ++ int len = blob_len(b); ++ char_u *p; ++ ++ if (idx < 0) ++ // count from the end ++ idx = len + idx; ++ if (idx < 0 || idx >= len) ++ { ++ EMSGN(_(e_blobidx), idx); ++ return; ++ } ++ if (argvars[2].v_type == VAR_UNKNOWN) ++ { ++ // Remove one item, return its value. ++ p = (char_u *)b->bv_ga.ga_data; ++ rettv->vval.v_number = (varnumber_T) *(p + idx); ++ mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); ++ --b->bv_ga.ga_len; ++ } ++ else ++ { ++ blob_T *blob; ++ ++ // Remove range of items, return list with values. ++ end = (long)tv_get_number_chk(&argvars[2], &error); ++ if (error) ++ return; ++ if (end < 0) ++ // count from the end ++ end = len + end; ++ if (end >= len || idx > end) ++ { ++ EMSGN(_(e_blobidx), end); ++ return; ++ } ++ blob = blob_alloc(); ++ if (blob == NULL) ++ return; ++ blob->bv_ga.ga_len = end - idx + 1; ++ if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) ++ { ++ vim_free(blob); ++ return; ++ } ++ p = (char_u *)b->bv_ga.ga_data; ++ mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, ++ (size_t)(end - idx + 1)); ++ ++blob->bv_refcount; ++ rettv->v_type = VAR_BLOB; ++ rettv->vval.v_blob = blob; ++ ++ mch_memmove(p + idx, p + end + 1, (size_t)(len - end)); ++ b->bv_ga.ga_len -= end - idx + 1; ++ } ++ } ++ } + else if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listdictarg), "remove()"); + else if ((l = argvars[0].vval.v_list) != NULL +! && !tv_check_lock(l->lv_lock, arg_errmsg, TRUE)) + { + idx = (long)tv_get_number_chk(&argvars[1], &error); + if (error) +! ; // type error: do nothing, errmsg already given + else if ((item = list_find(l, idx)) == NULL) + EMSGN(_(e_listidx), idx); + else +*************** +*** 9602,9611 **** + } + else + { +! /* Remove range of items, return list with values. */ + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) +! ; /* type error: do nothing */ + else if ((item2 = list_find(l, end)) == NULL) + EMSGN(_(e_listidx), end); + else +--- 9793,9802 ---- + } + else + { +! // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) +! ; // type error: do nothing + else if ((item2 = list_find(l, end)) == NULL) + EMSGN(_(e_listidx), end); + else +*************** +*** 9912,9917 **** +--- 10103,10124 ---- + list_T *l; + listitem_T *li, *ni; + ++ if (argvars[0].v_type == VAR_BLOB) ++ { ++ blob_T *b = argvars[0].vval.v_blob; ++ int i, len = blob_len(b); ++ ++ for (i = 0; i < len / 2; i++) ++ { ++ int tmp = blob_get(b, i); ++ ++ blob_set(b, i, blob_get(b, len - i - 1)); ++ blob_set(b, len - i - 1, tmp); ++ } ++ rettv_blob_set(rettv, b); ++ return; ++ } ++ + if (argvars[0].v_type != VAR_LIST) + EMSG2(_(e_listarg), "reverse()"); + else if ((l = argvars[0].vval.v_list) != NULL +*************** +*** 14198,14203 **** +--- 14405,14411 ---- + break; + case VAR_JOB: n = VAR_TYPE_JOB; break; + case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break; ++ case VAR_BLOB: n = VAR_TYPE_BLOB; break; + case VAR_UNKNOWN: + internal_error("f_type(UNKNOWN)"); + n = -1; +*************** +*** 14556,14578 **** + FILE *fd; + int ret = 0; + listitem_T *li; +! list_T *list; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) + return; + +! if (argvars[0].v_type != VAR_LIST) + { +! EMSG2(_(e_listarg), "writefile()"); +! return; + } +! list = argvars[0].vval.v_list; +! if (list == NULL) +! return; +! for (li = list->lv_first; li != NULL; li = li->li_next) +! if (tv_get_string_chk(&li->li_tv) == NULL) + return; + + if (argvars[2].v_type != VAR_UNKNOWN) + { +--- 14764,14796 ---- + FILE *fd; + int ret = 0; + listitem_T *li; +! list_T *list = NULL; +! blob_T *blob = NULL; + + rettv->vval.v_number = -1; + if (check_restricted() || check_secure()) + return; + +! if (argvars[0].v_type == VAR_LIST) + { +! list = argvars[0].vval.v_list; +! if (list == NULL) +! return; +! for (li = list->lv_first; li != NULL; li = li->li_next) +! if (tv_get_string_chk(&li->li_tv) == NULL) +! return; + } +! else if (argvars[0].v_type == VAR_BLOB) +! { +! blob = argvars[0].vval.v_blob; +! if (blob == NULL) + return; ++ } ++ else ++ { ++ EMSG2(_(e_invarg2), "writefile()"); ++ return; ++ } + + if (argvars[2].v_type != VAR_UNKNOWN) + { +*************** +*** 14604,14609 **** +--- 14822,14839 ---- + EMSG2(_(e_notcreate), *fname == NUL ? (char_u *)_("<empty>") : fname); + ret = -1; + } ++ else if (blob) ++ { ++ if (write_blob(fd, blob) == FAIL) ++ ret = -1; ++ #ifdef HAVE_FSYNC ++ else if (do_fsync) ++ // Ignore the error, the user wouldn't know what to do about it. ++ // May happen for a device. ++ vim_ignored = fsync(fileno(fd)); ++ #endif ++ fclose(fd); ++ } + else + { + if (write_list(fd, list, binary) == FAIL) +*** ../vim-8.1.0734/src/if_perl.xs 2018-09-21 14:01:23.148405740 +0200 +--- src/if_perl.xs 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 236,241 **** +--- 236,242 ---- + # else + # define Perl_sv_2pv dll_Perl_sv_2pv + # endif ++ # define Perl_sv_2pvbyte dll_Perl_sv_2pvbyte + # define Perl_sv_bless dll_Perl_sv_bless + # if (PERL_REVISION == 5) && (PERL_VERSION >= 8) + # define Perl_sv_catpvn_flags dll_Perl_sv_catpvn_flags +*************** +*** 388,393 **** +--- 389,395 ---- + # else + static char* (*Perl_sv_2pv)(pTHX_ SV*, STRLEN*); + # endif ++ static char* (*Perl_sv_2pvbyte)(pTHX_ SV*, STRLEN*); + static SV* (*Perl_sv_bless)(pTHX_ SV*, HV*); + # if (PERL_REVISION == 5) && (PERL_VERSION >= 8) + static void (*Perl_sv_catpvn_flags)(pTHX_ SV* , const char*, STRLEN, I32); +*************** +*** 543,548 **** +--- 545,551 ---- + # else + {"Perl_sv_2pv", (PERL_PROC*)&Perl_sv_2pv}, + # endif ++ {"Perl_sv_2pvbyte", (PERL_PROC*)&Perl_sv_2pvbyte}, + # ifdef PERL589_OR_LATER + {"Perl_sv_2iv_flags", (PERL_PROC*)&Perl_sv_2iv_flags}, + {"Perl_newXS_flags", (PERL_PROC*)&Perl_newXS_flags}, +*************** +*** 1556,1561 **** +--- 1559,1585 ---- + vim_free(value); + } + ++ SV* ++ Blob(SV* sv) ++ PREINIT: ++ STRLEN len; ++ char *s; ++ int i; ++ char buf[3]; ++ SV* newsv; ++ ++ CODE: ++ s = SvPVbyte(sv, len); ++ newsv = newSVpv("0z", 2); ++ for (i = 0; i < len; i++) ++ { ++ sprintf(buf, "%02X", s[i]); ++ sv_catpvn(newsv, buf, 2); ++ } ++ RETVAL = newsv; ++ OUTPUT: ++ RETVAL ++ + void + Buffers(...) + +*** ../vim-8.1.0734/src/if_py_both.h 2018-12-23 13:36:36.671194499 +0100 +--- src/if_py_both.h 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 867,872 **** +--- 867,876 ---- + } + return ret; + } ++ else if (our_tv->v_type == VAR_BLOB) ++ ret = PyBytes_FromStringAndSize( ++ (char*) our_tv->vval.v_blob->bv_ga.ga_data, ++ (Py_ssize_t) our_tv->vval.v_blob->bv_ga.ga_len); + else + { + Py_INCREF(Py_None); +*************** +*** 6394,6399 **** +--- 6398,6407 ---- + tv->vval.v_partial->pt_argc, argv, + tv->vval.v_partial->pt_dict, + tv->vval.v_partial->pt_auto); ++ case VAR_BLOB: ++ return PyBytes_FromStringAndSize( ++ (char*) tv->vval.v_blob->bv_ga.ga_data, ++ (Py_ssize_t) tv->vval.v_blob->bv_ga.ga_len); + case VAR_UNKNOWN: + case VAR_CHANNEL: + case VAR_JOB: +*** ../vim-8.1.0734/src/if_python.c 2018-03-29 18:08:42.000000000 +0200 +--- src/if_python.c 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 1575,1580 **** +--- 1575,1581 ---- + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: ++ case VAR_BLOB: + break; + } + } +*** ../vim-8.1.0734/src/if_python3.c 2018-09-30 21:43:17.195693290 +0200 +--- src/if_python3.c 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 232,237 **** +--- 232,239 ---- + # endif + # undef PyBytes_FromString + # define PyBytes_FromString py3_PyBytes_FromString ++ # undef PyBytes_FromStringAndSize ++ # define PyBytes_FromStringAndSize py3_PyBytes_FromStringAndSize + # define PyFloat_FromDouble py3_PyFloat_FromDouble + # define PyFloat_AsDouble py3_PyFloat_AsDouble + # define PyObject_GenericGetAttr py3_PyObject_GenericGetAttr +*************** +*** 394,399 **** +--- 396,402 ---- + static char* (*py3_PyBytes_AsString)(PyObject *bytes); + static int (*py3_PyBytes_AsStringAndSize)(PyObject *bytes, char **buffer, Py_ssize_t *length); + static PyObject* (*py3_PyBytes_FromString)(char *str); ++ static PyObject* (*py3_PyBytes_FromStringAndSize)(char *str, Py_ssize_t length); + static PyObject* (*py3_PyFloat_FromDouble)(double num); + static double (*py3_PyFloat_AsDouble)(PyObject *); + static PyObject* (*py3_PyObject_GenericGetAttr)(PyObject *obj, PyObject *name); +*************** +*** 559,564 **** +--- 562,568 ---- + {"PyBytes_AsString", (PYTHON_PROC*)&py3_PyBytes_AsString}, + {"PyBytes_AsStringAndSize", (PYTHON_PROC*)&py3_PyBytes_AsStringAndSize}, + {"PyBytes_FromString", (PYTHON_PROC*)&py3_PyBytes_FromString}, ++ {"PyBytes_FromStringAndSize", (PYTHON_PROC*)&py3_PyBytes_FromStringAndSize}, + {"PyFloat_FromDouble", (PYTHON_PROC*)&py3_PyFloat_FromDouble}, + {"PyFloat_AsDouble", (PYTHON_PROC*)&py3_PyFloat_AsDouble}, + {"PyObject_GenericGetAttr", (PYTHON_PROC*)&py3_PyObject_GenericGetAttr}, +*************** +*** 1680,1685 **** +--- 1684,1690 ---- + case VAR_SPECIAL: + case VAR_JOB: + case VAR_CHANNEL: ++ case VAR_BLOB: + break; + } + } +*** ../vim-8.1.0734/src/if_ruby.c 2019-01-08 20:29:29.339909743 +0100 +--- src/if_ruby.c 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 51,56 **** +--- 51,57 ---- + # define rb_cFloat (*dll_rb_cFloat) + # endif + # define rb_cNilClass (*dll_rb_cNilClass) ++ # define rb_cString (*dll_rb_cString) + # define rb_cSymbol (*dll_rb_cSymbol) + # define rb_cTrueClass (*dll_rb_cTrueClass) + # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 18 +*************** +*** 219,224 **** +--- 220,226 ---- + */ + # define rb_assoc_new dll_rb_assoc_new + # define rb_cObject (*dll_rb_cObject) ++ # define rb_class_new_instance dll_rb_class_new_instance + # define rb_check_type dll_rb_check_type + # ifdef USE_TYPEDDATA + # define rb_check_typeddata dll_rb_check_typeddata +*************** +*** 365,372 **** +--- 367,376 ---- + # endif + VALUE *dll_rb_cNilClass; + static VALUE *dll_rb_cObject; ++ VALUE *dll_rb_cString; + VALUE *dll_rb_cSymbol; + VALUE *dll_rb_cTrueClass; ++ static VALUE (*dll_rb_class_new_instance) (int,VALUE*,VALUE); + static void (*dll_rb_check_type) (VALUE,int); + # ifdef USE_TYPEDDATA + static void *(*dll_rb_check_typeddata) (VALUE,const rb_data_type_t *); +*************** +*** 579,586 **** +--- 583,592 ---- + # endif + {"rb_cNilClass", (RUBY_PROC*)&dll_rb_cNilClass}, + {"rb_cObject", (RUBY_PROC*)&dll_rb_cObject}, ++ {"rb_cString", (RUBY_PROC*)&dll_rb_cString}, + {"rb_cSymbol", (RUBY_PROC*)&dll_rb_cSymbol}, + {"rb_cTrueClass", (RUBY_PROC*)&dll_rb_cTrueClass}, ++ {"rb_class_new_instance", (RUBY_PROC*)&dll_rb_class_new_instance}, + {"rb_check_type", (RUBY_PROC*)&dll_rb_check_type}, + # ifdef USE_TYPEDDATA + {"rb_check_typeddata", (RUBY_PROC*)&dll_rb_check_typeddata}, +*************** +*** 1164,1170 **** + result = Qtrue; + else if (tv->vval.v_number == VVAL_FALSE) + result = Qfalse; +! } /* else return Qnil; */ + + return result; + } +--- 1170,1182 ---- + result = Qtrue; + else if (tv->vval.v_number == VVAL_FALSE) + result = Qfalse; +! } +! else if (tv->v_type == VAR_BLOB) +! { +! result = rb_str_new(tv->vval.v_blob->bv_ga.ga_data, +! tv->vval.v_blob->bv_ga.ga_len); +! } +! /* else return Qnil; */ + + return result; + } +*************** +*** 1242,1247 **** +--- 1254,1272 ---- + return buf; + } + ++ static VALUE vim_blob(VALUE self UNUSED, VALUE str) ++ { ++ VALUE result = rb_str_new("0z", 2); ++ char buf[4]; ++ int i; ++ for (i = 0; i < RSTRING_LEN(str); i++) ++ { ++ sprintf(buf, "%02X", RSTRING_PTR(str)[i]); ++ rb_str_concat(result, rb_str_new_cstr(buf)); ++ } ++ return result; ++ } ++ + static VALUE buffer_s_current(void) + { + return buffer_new(curbuf); +*************** +*** 1662,1667 **** +--- 1687,1693 ---- + rb_define_module_function(mVIM, "set_option", vim_set_option, 1); + rb_define_module_function(mVIM, "command", vim_command, 1); + rb_define_module_function(mVIM, "evaluate", vim_evaluate, 1); ++ rb_define_module_function(mVIM, "blob", vim_blob, 1); + + eDeletedBufferError = rb_define_class_under(mVIM, "DeletedBufferError", + rb_eStandardError); +*** ../vim-8.1.0734/src/json.c 2019-01-12 14:24:22.627597552 +0100 +--- src/json.c 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 195,202 **** +--- 195,204 ---- + { + char_u numbuf[NUMBUFLEN]; + char_u *res; ++ blob_T *b; + list_T *l; + dict_T *d; ++ int i; + + switch (val->v_type) + { +*************** +*** 233,238 **** +--- 235,259 ---- + EMSG(_(e_invarg)); + return FAIL; + ++ case VAR_BLOB: ++ b = val->vval.v_blob; ++ if (b == NULL || b->bv_ga.ga_len == 0) ++ ga_concat(gap, (char_u *)"[]"); ++ else ++ { ++ ga_append(gap, '['); ++ for (i = 0; i < b->bv_ga.ga_len; i++) ++ { ++ if (i > 0) ++ ga_concat(gap, (char_u *)","); ++ vim_snprintf((char *)numbuf, NUMBUFLEN, "%d", ++ (int)blob_get(b, i)); ++ ga_concat(gap, numbuf); ++ } ++ ga_append(gap, ']'); ++ } ++ break; ++ + case VAR_LIST: + l = val->vval.v_list; + if (l == NULL) +*** ../vim-8.1.0734/src/netbeans.c 2018-12-29 18:53:07.843607433 +0100 +--- src/netbeans.c 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 404,410 **** + if (*p == NUL) + { + own_node = TRUE; +! buffer = channel_get(nb_channel, PART_SOCK); + /* "node" is now invalid! */ + } + else +--- 404,410 ---- + if (*p == NUL) + { + own_node = TRUE; +! buffer = channel_get(nb_channel, PART_SOCK, NULL); + /* "node" is now invalid! */ + } + else +*** ../vim-8.1.0734/src/proto.h 2019-01-01 13:20:05.940711222 +0100 +--- src/proto.h 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 88,93 **** +--- 88,94 ---- + # include "hashtab.pro" + # include "json.pro" + # include "list.pro" ++ # include "blob.pro" + # include "main.pro" + # include "mark.pro" + # include "memfile.pro" +*** ../vim-8.1.0734/src/proto/blob.pro 2019-01-12 22:40:58.105219042 +0100 +--- src/proto/blob.pro 2019-01-12 20:31:31.262919660 +0100 +*************** +*** 0 **** +--- 1,13 ---- ++ /* blob.c */ ++ blob_T *blob_alloc(void); ++ int rettv_blob_alloc(typval_T *rettv); ++ void rettv_blob_set(typval_T *rettv, blob_T *b); ++ void blob_free(blob_T *b); ++ void blob_unref(blob_T *b); ++ long blob_len(blob_T *b); ++ char_u blob_get(blob_T *b, int idx); ++ void blob_set(blob_T *b, int idx, char_u c); ++ int blob_equal(blob_T *b1, blob_T *b2); ++ int read_blob(FILE *fd, blob_T *blob); ++ int write_blob(FILE *fd, blob_T *blob); ++ /* vim: set ft=c : */ +*** ../vim-8.1.0734/src/proto/channel.pro 2018-12-14 21:31:58.008319718 +0100 +--- src/proto/channel.pro 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 18,24 **** + void channel_write_new_lines(buf_T *buf); + readq_T *channel_peek(channel_T *channel, ch_part_T part); + char_u *channel_first_nl(readq_T *node); +! char_u *channel_get(channel_T *channel, ch_part_T part); + void channel_consume(channel_T *channel, ch_part_T part, int len); + int channel_collapse(channel_T *channel, ch_part_T part, int want_nl); + int channel_can_write_to(channel_T *channel); +--- 18,24 ---- + void channel_write_new_lines(buf_T *buf); + readq_T *channel_peek(channel_T *channel, ch_part_T part); + char_u *channel_first_nl(readq_T *node); +! char_u *channel_get(channel_T *channel, ch_part_T part, int *outlen); + void channel_consume(channel_T *channel, ch_part_T part, int len); + int channel_collapse(channel_T *channel, ch_part_T part, int want_nl); + int channel_can_write_to(channel_T *channel); +*************** +*** 30,36 **** + void channel_close_in(channel_T *channel); + void channel_clear(channel_T *channel); + void channel_free_all(void); +! void common_channel_read(typval_T *argvars, typval_T *rettv, int raw); + channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp); + void channel_handle_events(int only_keep_open); + int channel_any_keep_open(void); +--- 30,36 ---- + void channel_close_in(channel_T *channel); + void channel_clear(channel_T *channel); + void channel_free_all(void); +! void common_channel_read(typval_T *argvars, typval_T *rettv, int raw, int blob); + channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp); + void channel_handle_events(int only_keep_open); + int channel_any_keep_open(void); +*** ../vim-8.1.0734/src/structs.h 2019-01-04 15:09:52.918373097 +0100 +--- src/structs.h 2019-01-12 18:56:15.668991755 +0100 +*************** +*** 1251,1256 **** +--- 1251,1257 ---- + typedef struct listvar_S list_T; + typedef struct dictvar_S dict_T; + typedef struct partial_S partial_T; ++ typedef struct blobvar_S blob_T; + + typedef struct jobvar_S job_T; + typedef struct readq_S readq_T; +*************** +*** 1272,1277 **** +--- 1273,1279 ---- + VAR_SPECIAL, // "v_number" is used + VAR_JOB, // "v_job" is used + VAR_CHANNEL, // "v_channel" is used ++ VAR_BLOB, // "v_blob" is used + } vartype_T; + + /* +*************** +*** 1295,1300 **** +--- 1297,1303 ---- + job_T *v_job; /* job value (can be NULL!) */ + channel_T *v_channel; /* channel value (can be NULL!) */ + #endif ++ blob_T *v_blob; /* blob value (can be NULL!) */ + } vval; + } typval_T; + +*************** +*** 1401,1406 **** +--- 1404,1419 ---- + dict_T *dv_used_prev; /* previous dict in used dicts list */ + }; + ++ /* ++ * Structure to hold info about a blob. ++ */ ++ struct blobvar_S ++ { ++ garray_T bv_ga; // growarray with the data ++ int bv_refcount; // reference count ++ char bv_lock; // zero, VAR_LOCKED, VAR_FIXED ++ }; ++ + #if defined(FEAT_EVAL) || defined(PROTO) + typedef struct funccall_S funccall_T; + +*************** +*** 3526,3531 **** +--- 3539,3545 ---- + dict_T *ll_dict; /* The Dictionary or NULL */ + dictitem_T *ll_di; /* The dictitem or NULL */ + char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ ++ blob_T *ll_blob; /* The Blob or NULL */ + } lval_T; + + /* Structure used to save the current state. Used when executing Normal mode +*** ../vim-8.1.0734/src/testdir/Make_all.mak 2019-01-12 13:25:42.633479785 +0100 +--- src/testdir/Make_all.mak 2019-01-12 18:54:17.729776157 +0100 +*************** +*** 73,78 **** +--- 73,79 ---- + test_backspace_opt \ + test_backup \ + test_behave \ ++ test_blob \ + test_blockedit \ + test_breakindent \ + test_bufline \ +*************** +*** 283,288 **** +--- 284,290 ---- + test_autocmd.res \ + test_autoload.res \ + test_backspace_opt.res \ ++ test_blob.res \ + test_blockedit.res \ + test_breakindent.res \ + test_bufwintabinfo.res \ +*** ../vim-8.1.0734/src/vim.h 2019-01-12 13:25:42.633479785 +0100 +--- src/vim.h 2019-01-12 18:52:14.242595860 +0100 +*************** +*** 1994,2006 **** + #define VV_TYPE_NONE 78 + #define VV_TYPE_JOB 79 + #define VV_TYPE_CHANNEL 80 +! #define VV_TERMRFGRESP 81 +! #define VV_TERMRBGRESP 82 +! #define VV_TERMU7RESP 83 +! #define VV_TERMSTYLERESP 84 +! #define VV_TERMBLINKRESP 85 +! #define VV_EVENT 86 +! #define VV_LEN 87 /* number of v: vars */ + + /* used for v_number in VAR_SPECIAL */ + #define VVAL_FALSE 0L +--- 1994,2007 ---- + #define VV_TYPE_NONE 78 + #define VV_TYPE_JOB 79 + #define VV_TYPE_CHANNEL 80 +! #define VV_TYPE_BLOB 81 +! #define VV_TERMRFGRESP 82 +! #define VV_TERMRBGRESP 83 +! #define VV_TERMU7RESP 84 +! #define VV_TERMSTYLERESP 85 +! #define VV_TERMBLINKRESP 86 +! #define VV_EVENT 87 +! #define VV_LEN 88 /* number of v: vars */ + + /* used for v_number in VAR_SPECIAL */ + #define VVAL_FALSE 0L +*************** +*** 2019,2024 **** +--- 2020,2026 ---- + #define VAR_TYPE_NONE 7 + #define VAR_TYPE_JOB 8 + #define VAR_TYPE_CHANNEL 9 ++ #define VAR_TYPE_BLOB 10 + + #ifdef FEAT_CLIPBOARD + +*** ../vim-8.1.0734/src/globals.h 2019-01-06 13:10:46.324499127 +0100 +--- src/globals.h 2019-01-12 19:44:10.389888679 +0100 +*************** +*** 1524,1529 **** +--- 1524,1531 ---- + EXTERN char_u e_emptykey[] INIT(= N_("E713: Cannot use empty key for Dictionary")); + EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); + EXTERN char_u e_listidx[] INIT(= N_("E684: list index out of range: %ld")); ++ EXTERN char_u e_blobidx[] INIT(= N_("E979: Blob index out of range: %ld")); ++ EXTERN char_u e_invalblob[] INIT(= N_("E978: Invalid operation for Blob")); + EXTERN char_u e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); + EXTERN char_u e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s")); + EXTERN char_u e_listreq[] INIT(= N_("E714: List required")); +*** ../vim-8.1.0734/src/testdir/test_blob.vim 2019-01-12 22:40:58.129218991 +0100 +--- src/testdir/test_blob.vim 2019-01-12 22:24:32.278412344 +0100 +*************** +*** 0 **** +--- 1,179 ---- ++ " Tests for the Blob types ++ ++ func TearDown() ++ " Run garbage collection after every test ++ call test_garbagecollect_now() ++ endfunc ++ ++ " Tests for Blob type ++ ++ " Blob creation from constant ++ func Test_blob_create() ++ let b = 0zDEADBEEF ++ call assert_equal(v:t_blob, type(b)) ++ call assert_equal(4, len(b)) ++ call assert_equal(0xDE, b[0]) ++ call assert_equal(0xAD, b[1]) ++ call assert_equal(0xBE, b[2]) ++ call assert_equal(0xEF, b[3]) ++ call assert_fails('let x = b[4]') ++ ++ call assert_equal(0xDE, get(b, 0)) ++ call assert_equal(0xEF, get(b, 3)) ++ call assert_fails('let x = get(b, 4)') ++ endfunc ++ ++ " assignment to a blob ++ func Test_blob_assign() ++ let b = 0zDEADBEEF ++ let b2 = b[1:2] ++ call assert_equal(0zADBE, b2) ++ ++ let bcopy = b[:] ++ call assert_equal(b, bcopy) ++ call assert_false(b is bcopy) ++ endfunc ++ ++ func Test_blob_to_string() ++ let b = 0zDEADBEEF ++ call assert_equal('[0xDE,0xAD,0xBE,0xEF]', string(b)) ++ call remove(b, 0, 3) ++ call assert_equal('[]', string(b)) ++ endfunc ++ ++ func Test_blob_compare() ++ let b1 = 0z0011 ++ let b2 = 0z1100 ++ call assert_false(b1 == b2) ++ call assert_true(b1 != b2) ++ call assert_true(b1 == 0z0011) ++ ++ call assert_false(b1 is b2) ++ let b2 = b1 ++ call assert_true(b1 is b2) ++ ++ call assert_fails('let x = b1 > b2') ++ call assert_fails('let x = b1 < b2') ++ call assert_fails('let x = b1 - b2') ++ call assert_fails('let x = b1 / b2') ++ call assert_fails('let x = b1 * b2') ++ endfunc ++ ++ " test for range assign ++ func Test_blob_range_assign() ++ let b = 0z00 ++ let b[1] = 0x11 ++ let b[2] = 0x22 ++ call assert_equal(0z001122, b) ++ call assert_fails('let b[4] = 0x33') ++ endfunc ++ ++ func Test_blob_for_loop() ++ let blob = 0z00010203 ++ let i = 0 ++ for byte in blob ++ call assert_equal(i, byte) ++ let i += 1 ++ endfor ++ ++ let blob = 0z00 ++ call remove(blob, 0) ++ call assert_equal(0, len(blob)) ++ for byte in blob ++ call assert_error('loop over empty blob') ++ endfor ++ endfunc ++ ++ func Test_blob_concatenate() ++ let b = 0z0011 ++ let b += 0z2233 ++ call assert_equal(0z00112233, b) ++ ++ call assert_fails('let b += "a"') ++ call assert_fails('let b += 88') ++ ++ let b = 0zDEAD + 0zBEEF ++ call assert_equal(0zDEADBEEF, b) ++ endfunc ++ ++ " Test removing items in blob ++ func Test_blob_func_remove() ++ " Test removing 1 element ++ let b = 0zDEADBEEF ++ call assert_equal(0xDE, remove(b, 0)) ++ call assert_equal(0zADBEEF, b) ++ ++ let b = 0zDEADBEEF ++ call assert_equal(0xEF, remove(b, -1)) ++ call assert_equal(0zDEADBE, b) ++ ++ let b = 0zDEADBEEF ++ call assert_equal(0xAD, remove(b, 1)) ++ call assert_equal(0zDEBEEF, b) ++ ++ " Test removing range of element(s) ++ let b = 0zDEADBEEF ++ call assert_equal(0zBE, remove(b, 2, 2)) ++ call assert_equal(0zDEADEF, b) ++ ++ let b = 0zDEADBEEF ++ call assert_equal(0zADBE, remove(b, 1, 2)) ++ call assert_equal(0zDEEF, b) ++ ++ " Test invalid cases ++ let b = 0zDEADBEEF ++ call assert_fails("call remove(b, 5)", 'E979:') ++ call assert_fails("call remove(b, 1, 5)", 'E979:') ++ call assert_fails("call remove(b, 3, 2)", 'E979:') ++ call assert_fails("call remove(1, 0)", 'E712:') ++ call assert_fails("call remove(b, b)", 'E974:') ++ endfunc ++ ++ func Test_blob_read_write() ++ let b = 0zDEADBEEF ++ call writefile(b, 'Xblob') ++ let br = readfile('Xblob', 'B') ++ call assert_equal(b, br) ++ call delete('Xblob') ++ endfunc ++ ++ " filter() item in blob ++ func Test_blob_filter() ++ let b = 0zDEADBEEF ++ call filter(b, 'v:val != 0xEF') ++ call assert_equal(0zDEADBE, b) ++ endfunc ++ ++ " map() item in blob ++ func Test_blob_map() ++ let b = 0zDEADBEEF ++ call map(b, 'v:val + 1') ++ call assert_equal(0zDFAEBFF0, b) ++ endfunc ++ ++ func Test_blob_index() ++ call assert_equal(2, index(0zDEADBEEF, 0xBE)) ++ call assert_equal(-1, index(0zDEADBEEF, 0)) ++ endfunc ++ ++ func Test_blob_insert() ++ let b = 0zDEADBEEF ++ call insert(b, 0x33) ++ call assert_equal(0z33DEADBEEF, b) ++ ++ let b = 0zDEADBEEF ++ call insert(b, 0x33, 2) ++ call assert_equal(0zDEAD33BEEF, b) ++ endfunc ++ ++ func Test_blob_reverse() ++ call assert_equal(0zEFBEADDE, reverse(0zDEADBEEF)) ++ call assert_equal(0zBEADDE, reverse(0zDEADBE)) ++ call assert_equal(0zADDE, reverse(0zDEAD)) ++ call assert_equal(0zDE, reverse(0zDE)) ++ endfunc ++ ++ func Test_blob_json_encode() ++ call assert_equal('[222,173,190,239]', json_encode(0zDEADBEEF)) ++ call assert_equal('[]', json_encode(0z)) ++ endfunc +*** ../vim-8.1.0734/src/testdir/test_channel.vim 2019-01-09 22:24:46.568161097 +0100 +--- src/testdir/test_channel.vim 2019-01-12 21:27:24.348531499 +0100 +*************** +*** 516,521 **** +--- 516,566 ---- + call assert_equal(1, found) + endfunc + ++ func Test_raw_pipe_blob() ++ if !has('job') ++ return ++ endif ++ call ch_log('Test_raw_pipe_blob()') ++ " Add a dummy close callback to avoid that messages are dropped when calling ++ " ch_canread(). ++ " Also test the non-blocking option. ++ let job = job_start(s:python . " test_channel_pipe.py", ++ \ {'mode': 'raw', 'drop': 'never', 'noblock': 1}) ++ call assert_equal(v:t_job, type(job)) ++ call assert_equal("run", job_status(job)) ++ ++ call assert_equal("open", ch_status(job)) ++ call assert_equal("open", ch_status(job), {"part": "out"}) ++ ++ try ++ " Create a blob with the echo command and write it. ++ let blob = 0z00 ++ let cmd = "echo something\n" ++ for i in range(0, len(cmd) - 1) ++ let blob[i] = char2nr(cmd[i]) ++ endfor ++ call assert_equal(len(cmd), len(blob)) ++ call ch_sendraw(job, blob) ++ ++ " Read a blob with the reply. ++ let msg = ch_readblob(job) ++ let expected = 'something' ++ for i in range(0, len(expected) - 1) ++ call assert_equal(char2nr(expected[i]), msg[i]) ++ endfor ++ ++ let reply = ch_evalraw(job, "quit\n", {'timeout': 100}) ++ call assert_equal("Goodbye!\n", substitute(reply, "\r", "", 'g')) ++ finally ++ call job_stop(job) ++ endtry ++ ++ let g:Ch_job = job ++ call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))}) ++ let info = job_info(job) ++ call assert_equal("dead", info.status) ++ endfunc ++ + func Test_nl_pipe() + if !has('job') + return +*** ../vim-8.1.0734/src/version.c 2019-01-12 16:29:26.327986331 +0100 +--- src/version.c 2019-01-12 18:48:53.379924473 +0100 +*************** +*** 797,798 **** +--- 797,800 ---- + { /* Add new patch number below this line */ ++ /**/ ++ 735, + /**/ + +-- +hundred-and-one symptoms of being an internet addict: +180. You maintain more than six e-mail addresses. + + /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ +/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ +\\\ an exciting new programming language -- http://www.Zimbu.org /// + \\\ help me help AIDS victims -- http://ICCF-Holland.org /// |