diff options
-rw-r--r-- | runtime/doc/eval.txt | 119 | ||||
-rw-r--r-- | runtime/doc/if_perl.txt | 3 | ||||
-rw-r--r-- | runtime/doc/if_ruby.txt | 4 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 1 | ||||
-rw-r--r-- | src/Make_mvc.mak | 4 | ||||
-rw-r--r-- | src/Makefile | 9 | ||||
-rw-r--r-- | src/blob.c | 167 | ||||
-rw-r--r-- | src/channel.c | 89 | ||||
-rw-r--r-- | src/eval.c | 423 | ||||
-rw-r--r-- | src/evalfunc.c | 282 | ||||
-rw-r--r-- | src/globals.h | 2 | ||||
-rw-r--r-- | src/if_perl.xs | 24 | ||||
-rw-r--r-- | src/if_py_both.h | 8 | ||||
-rw-r--r-- | src/if_python.c | 1 | ||||
-rw-r--r-- | src/if_python3.c | 5 | ||||
-rw-r--r-- | src/if_ruby.c | 28 | ||||
-rw-r--r-- | src/json.c | 21 | ||||
-rw-r--r-- | src/netbeans.c | 2 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/proto/blob.pro | 13 | ||||
-rw-r--r-- | src/proto/channel.pro | 4 | ||||
-rw-r--r-- | src/structs.h | 14 | ||||
-rw-r--r-- | src/testdir/Make_all.mak | 2 | ||||
-rw-r--r-- | src/testdir/test_blob.vim | 179 | ||||
-rw-r--r-- | src/testdir/test_channel.vim | 45 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim.h | 16 |
27 files changed, 1354 insertions, 114 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 86026239a9..b9c9ca42ad 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -72,6 +72,10 @@ Job Used for a job, see |job_start()|. *Job* *Jobs* 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,7 +128,8 @@ Note that " " and "0" are also non-empty strings, thus considered to be TRUE. 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 + *E974* *E975* *E976* +List, Dictionary, Funcref, Job, Channel and Blob types are not automatically converted. *E805* *E806* *E808* @@ -1017,6 +1022,12 @@ just above. Also see |sublist| below. Examples: > :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. @@ -1156,6 +1167,14 @@ of 'encoding'. 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-'* @@ -1911,6 +1930,8 @@ v:t_none Value of None type. Read-only. See: |type()| 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| @@ -2094,12 +2115,14 @@ ch_logfile({fname} [, {mode}]) none start logging channel activity 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}, {string} [, {options}]) - any send {string} over raw {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}]) @@ -2239,8 +2262,8 @@ hlID({name}) Number syntax ID of highlight group {name} 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 +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}]]) @@ -2249,7 +2272,7 @@ inputlist({textlist}) Number let the user pick from a choice list 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}] +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 @@ -2339,7 +2362,7 @@ py3eval({expr}) any evaluate |python3| expression pyxeval({expr}) any evaluate |python_x| expression range({expr} [, {max} [, {stride}]]) List items from {expr} to {max} -readfile({fname} [, {binary} [, {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 @@ -2554,8 +2577,8 @@ winrestview({dict}) none restore view of current window 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} +writefile({object}, {fname} [, {flags}]) + Number write |Blob| or |List| of lines to file xor({expr}, {expr}) Number bitwise XOR @@ -3199,6 +3222,11 @@ ch_read({handle} [, {options}]) *ch_read()* 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 @@ -3215,8 +3243,8 @@ ch_sendexpr({handle}, {expr} [, {options}]) *ch_sendexpr()* {only available when compiled with the |+channel| feature} -ch_sendraw({handle}, {string} [, {options}]) *ch_sendraw()* - Send {string} over {handle}. +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 @@ -5375,17 +5403,21 @@ indent({lnum}) The result is a Number, which is indent of line {lnum} in the 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. +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 {list}. + -1 is returned when {expr} is not found in {object}. Example: > :let idx = index(words, "the") :if index(numbers, 123) >= 0 @@ -5491,13 +5523,16 @@ inputsecret({prompt} [, {text}]) *inputsecret()* 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}. +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|. Examples: > + + 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)) @@ -5763,6 +5798,7 @@ json_encode({expr}) *json_encode()* 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" @@ -6947,16 +6983,18 @@ range({expr} [, {max} [, {stride}]]) *range()* range(2, 0) " error! < *readfile()* -readfile({fname} [, {binary} [, {max}]]) +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 {binary} contains "b" binary mode is used: + 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. @@ -7132,6 +7170,16 @@ remove({list}, {idx} [, {end}]) *remove()* 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") @@ -7172,9 +7220,11 @@ resolve({filename}) *resolve()* *E655* 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: > +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()* @@ -9518,6 +9568,7 @@ type({expr}) The result is a Number representing the type of {expr}. 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("") @@ -9865,14 +9916,17 @@ wordcount() *wordcount()* *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. +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") @@ -10575,7 +10629,10 @@ This does NOT work: > 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 diff --git a/runtime/doc/if_perl.txt b/runtime/doc/if_perl.txt index 26ea29e0db..cce3cdf5d9 100644 --- a/runtime/doc/if_perl.txt +++ b/runtime/doc/if_perl.txt @@ -191,6 +191,9 @@ VIM::Eval({expr}) Evaluates {expr} and returns (success, value) in list 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 diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt index b1404769f8..c9fc797eb1 100644 --- a/runtime/doc/if_ruby.txt +++ b/runtime/doc/if_ruby.txt @@ -110,6 +110,10 @@ Module Functions: 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 diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 759e185bca..7aef61ac33 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -696,6 +696,7 @@ CUIOBJ = $(OUTDIR)/iscygpty.o OBJ = \ $(OUTDIR)/arabic.o \ $(OUTDIR)/beval.o \ + $(OUTDIR)/blob.o \ $(OUTDIR)/blowfish.o \ $(OUTDIR)/buffer.o \ $(OUTDIR)/charset.o \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 0a92530114..a3c66819a5 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -701,6 +701,7 @@ INCL = vim.h alloc.h arabic.h ascii.h ex_cmds.h farsi.h feature.h globals.h \ OBJ = \ $(OUTDIR)\arabic.obj \ $(OUTDIR)\beval.obj \ + $(OUTDIR)\blob.obj \ $(OUTDIR)\blowfish.obj \ $(OUTDIR)\buffer.obj \ $(OUTDIR)\charset.obj \ @@ -1346,6 +1347,8 @@ $(OUTDIR)/arabic.obj: $(OUTDIR) arabic.c $(INCL) $(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,6 +1619,7 @@ auto: # End Custom Build proto.h: \ proto/arabic.pro \ + proto/blob.pro \ proto/blowfish.pro \ proto/buffer.pro \ proto/charset.pro \ diff --git a/src/Makefile b/src/Makefile index 0ea1503d87..cf409bd49a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1577,6 +1577,7 @@ include testdir/Make_all.mak BASIC_SRC = \ arabic.c \ beval.c \ + blob.c \ blowfish.c \ buffer.c \ charset.c \ @@ -1693,6 +1694,7 @@ OBJ_COMMON = \ objects/arabic.o \ objects/beval.o \ objects/buffer.o \ + objects/blob.o \ objects/blowfish.o \ objects/crypt.o \ objects/crypt_zip.o \ @@ -2943,6 +2945,9 @@ $(ALL_OBJ): objects/.dirstamp 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,6 +3400,10 @@ objects/beval.o: beval.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/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 \ diff --git a/src/blob.c b/src/blob.c new file mode 100644 index 0000000000..fc1d3f6248 --- /dev/null +++ b/src/blob.c @@ -0,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) */ diff --git a/src/channel.c b/src/channel.c index 6f5bf20234..e9615279a6 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1665,7 +1665,7 @@ channel_first_nl(readq_T *node) * Returns NULL if there is nothing. */ char_u * -channel_get(channel_T *channel, ch_part_T part) +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,6 +1673,8 @@ channel_get(channel_T *channel, ch_part_T part) 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,7 +1691,7 @@ channel_get(channel_T *channel, ch_part_T part) * Replaces NUL bytes with NL. */ static char_u * -channel_get_all(channel_T *channel, ch_part_T part) +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,7 +1701,7 @@ channel_get_all(channel_T *channel, ch_part_T part) /* 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); + return channel_get(channel, part, outlen); /* Concatenate everything into one buffer. */ for (node = head->rq_next; node != NULL; node = node->rq_next) @@ -1718,10 +1720,16 @@ channel_get_all(channel_T *channel, ch_part_T part) /* Free all buffers */ do { - p = channel_get(channel, part); + 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,7 +1901,7 @@ channel_fill(js_read_T *reader) { channel_T *channel = (channel_T *)reader->js_cookie; ch_part_T part = reader->js_cookie_arg; - char_u *next = channel_get(channel, part); + char_u *next = channel_get(channel, part, NULL); int keeplen; int addlen; char_u *p; @@ -1942,7 +1950,7 @@ channel_parse_json(channel_T *channel, ch_part_T part) if (channel_peek(channel, part) == NULL) return FALSE; - reader.js_buf = channel_get(channel, part); + reader.js_buf = channel_get(channel, part, NULL); reader.js_used = 0; reader.js_fill = channel_fill; reader.js_cookie = channel; @@ -2475,7 +2483,7 @@ drop_messages(channel_T *channel, ch_part_T part) { char_u *msg; - while ((msg = channel_get(channel, part)) != NULL) + while ((msg = channel_get(channel, part, NULL)) != NULL) { ch_log(channel, "Dropping message '%s'", (char *)msg); vim_free(msg); @@ -2639,7 +2647,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part) if (nl + 1 == buf + node->rq_buflen) { /* get the whole buffer, drop the NL */ - msg = channel_get(channel, part); + msg = channel_get(channel, part, NULL); *nl = NUL; } else @@ -2655,7 +2663,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part) /* 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); + msg = channel_get_all(channel, part, NULL); } if (msg == NULL) @@ -3007,7 +3015,7 @@ channel_clear_one(channel_T *channel, ch_part_T part) cbq_T *cb_head = &ch_part->ch_cb_head; while (channel_peek(channel, part) != NULL) - vim_free(channel_get(channel, part)); + vim_free(channel_get(channel, part, NULL)); while (cb_head->cq_next != NULL) { @@ -3381,7 +3389,8 @@ channel_read(channel_T *channel, ch_part_T part, char *func) * 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) +channel_read_block( + channel_T *channel, ch_part_T part, int timeout, int raw, int *outlen) { char_u *buf; char_u *msg; @@ -3422,9 +3431,9 @@ channel_read_block(channel_T *channel, ch_part_T part, int timeout, int raw) } /* We have a complete message now. */ - if (mode == MODE_RAW) + if (mode == MODE_RAW || outlen != NULL) { - msg = channel_get_all(channel, part); + msg = channel_get_all(channel, part, outlen); } else { @@ -3441,12 +3450,12 @@ channel_read_block(channel_T *channel, ch_part_T part, int timeout, int raw) if (nl == NULL) { /* must be a closed channel with missing NL */ - msg = channel_get(channel, part); + msg = channel_get(channel, part, NULL); } else if (nl + 1 == buf + node->rq_buflen) { /* get the whole buffer */ - msg = channel_get(channel, part); + msg = channel_get(channel, part, NULL); *nl = NUL; } else @@ -3554,7 +3563,7 @@ channel_read_json_block( * Common for ch_read() and ch_readraw(). */ void -common_channel_read(typval_T *argvars, typval_T *rettv, int raw) +common_channel_read(typval_T *argvars, typval_T *rettv, int raw, int blob) { channel_T *channel; ch_part_T part = PART_COUNT; @@ -3585,9 +3594,32 @@ common_channel_read(typval_T *argvars, typval_T *rettv, int raw) if (opt.jo_set & JO_TIMEOUT) timeout = opt.jo_timeout; - if (raw || mode == MODE_RAW || mode == MODE_NL) + 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); + timeout, raw, NULL); else { if (opt.jo_set & JO_ID) @@ -3905,6 +3937,7 @@ channel_send( send_common( typval_T *argvars, char_u *text, + int len, int id, int eval, jobopt_T *opt, @@ -3938,7 +3971,7 @@ send_common( opt->jo_callback, opt->jo_partial, id); } - if (channel_send(channel, part_send, text, (int)STRLEN(text), fun) == OK + if (channel_send(channel, part_send, text, len, fun) == OK && opt->jo_callback == NULL) return channel; return NULL; @@ -3982,7 +4015,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval) if (text == NULL) return; - channel = send_common(argvars, text, id, eval, &opt, + 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,6 +4047,7 @@ ch_raw_common(typval_T *argvars, typval_T *rettv, int eval) { char_u buf[NUMBUFLEN]; char_u *text; + int len; channel_T *channel; ch_part_T part_read; jobopt_T opt; @@ -4023,8 +4057,17 @@ ch_raw_common(typval_T *argvars, typval_T *rettv, int eval) 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, + 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,7 +4076,7 @@ ch_raw_common(typval_T *argvars, typval_T *rettv, int eval) else timeout = channel_get_timeout(channel, part_read); rettv->vval.v_string = channel_read_block(channel, part_read, - timeout, TRUE); + timeout, TRUE, NULL); } free_job_options(&opt); } diff --git a/src/eval.c b/src/eval.c index b6463d2e70..59bddb891a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -78,6 +78,8 @@ typedef struct 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,6 +189,7 @@ static struct vimvar {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,6 +205,7 @@ static struct vimvar #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,6 +342,7 @@ eval_init(void) 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,10 +1923,12 @@ get_lval( { 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->vval.v_dict != NULL) + && !(lp->ll_tv->v_type == VAR_BLOB + && |