diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-01-12 22:47:31 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-01-12 22:47:31 +0100 |
commit | 6e5ea8d2a995b32bbc5972edc4f827b959f2702f (patch) | |
tree | b1ad7d6a83f53220227122719d5eb97dd32ff1e6 /src | |
parent | e3c74d249ac36404d8af25f74baf335d143b30e3 (diff) |
patch 8.1.0735: cannot handle binary datav8.1.0735
Problem: Cannot handle binary data.
Solution: Add the Blob type. (Yasuhiro Matsumoto, closes #3638)
Diffstat (limited to 'src')
-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 |
24 files changed, 1259 insertions, 83 deletions
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 + && lp->ll_tv->vval.v_blob != NULL)) { if (!quiet) - EMSG(_("E689: Can only index a List or Dictionary")); + EMSG(_("E689: Can only index a List, Dictionary or Blob")); return NULL; } if (lp->ll_range) @@ -1974,11 +1981,14 @@ get_lval( clear_tv(&var1); return NULL; } - if (rettv != NULL && (rettv->v_type != VAR_LIST - || rettv->vval.v_list == 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 value")); + EMSG(_("E709: [:] requires a List or Blob value")); clear_tv(&var1); return NULL; } @@ -2097,6 +2107,33 @@ get_lval( 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,7 +2238,52 @@ set_var_lval( { cc = *endp; *endp = NUL; - if (op != NULL && *op != '=') + 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,6 +2434,20 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op) 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,6 +2547,7 @@ eval_for_line( char_u *expr; typval_T tv; list_T *l; + blob_T *b; *errp = TRUE; /* default: there is an error */ @@ -2476,24 +2573,38 @@ eval_for_line( *errp = FALSE; if (!skip) { - l = tv.vval.v_list; - if (tv.v_type != VAR_LIST) + if (tv.v_type == VAR_LIST) { - EMSG(_(e_listreq)); - clear_tv(&tv); + 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 (l == NULL) + else if (tv.v_type == VAR_BLOB) { - /* a null list is like an empty list: do nothing */ - clear_tv(&tv); + b = tv.vval.v_blob; + if (b == NULL) + clear_tv(&tv); + else + { + fi->fi_blob = b; + fi->fi_bi = 0; + } } 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; + EMSG(_(e_listreq)); + clear_tv(&tv); } } } @@ -2516,6 +2627,20 @@ next_for_item(void *fi_void, char_u *arg) 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,6 +3080,7 @@ item_lock(typval_T *tv, int deep, int lock) list_T *l; listitem_T *li; dict_T *d; + blob_T *b; hashitem_T *hi; int todo; @@ -2986,6 +3112,15 @@ item_lock(typval_T *tv, int deep, int lock) 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,7 +3744,8 @@ eval5(char_u **arg, typval_T *rettv, int evaluate) if (op != '+' && op != '-' && op != '.') break; - if ((op != '+' || rettv->v_type != VAR_LIST) + if ((op != '+' || (rettv->v_type != VAR_LIST + && rettv->v_type != VAR_BLOB)) #ifdef FEAT_FLOAT && (op == '.' || rettv->v_type != VAR_FLOAT) #endif @@ -3659,6 +3795,25 @@ eval5(char_u **arg, typval_T *rettv, int evaluate) 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,6 +4076,7 @@ eval6( /* * Handle sixth level expression: * number number constant + * 0zFFFFFFFF Blob constant * "string" string constant * 'string' literal string constant * &option-name option value @@ -4027,7 +4183,38 @@ eval7( } 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,6 +4450,7 @@ eval_index( { int empty1 = FALSE, empty2 = FALSE; typval_T var1, var2; + long i; long n1, n2 = 0; long len = -1; int range = FALSE; @@ -4297,6 +4485,7 @@ eval_index( case VAR_NUMBER: case VAR_LIST: case VAR_DICT: + case VAR_BLOB: break; } @@ -4439,6 +4628,67 @@ eval_index( 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,6 +5220,9 @@ tv_equal( --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,6 +5855,36 @@ echo_string_core( 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", + |