To: vim_dev@googlegroups.com Subject: Patch 8.1.0735 Fcc: outbox From: Bram Moolenaar 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 *)_("") : 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 ///