summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-01-12 22:47:31 +0100
committerBram Moolenaar <Bram@vim.org>2019-01-12 22:47:31 +0100
commit6e5ea8d2a995b32bbc5972edc4f827b959f2702f (patch)
treeb1ad7d6a83f53220227122719d5eb97dd32ff1e6 /src
parente3c74d249ac36404d8af25f74baf335d143b30e3 (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.mak1
-rw-r--r--src/Make_mvc.mak4
-rw-r--r--src/Makefile9
-rw-r--r--src/blob.c167
-rw-r--r--src/channel.c89
-rw-r--r--src/eval.c423
-rw-r--r--src/evalfunc.c282
-rw-r--r--src/globals.h2
-rw-r--r--src/if_perl.xs24
-rw-r--r--src/if_py_both.h8
-rw-r--r--src/if_python.c1
-rw-r--r--src/if_python3.c5
-rw-r--r--src/if_ruby.c28
-rw-r--r--src/json.c21
-rw-r--r--src/netbeans.c2
-rw-r--r--src/proto.h1
-rw-r--r--src/proto/blob.pro13
-rw-r--r--src/proto/channel.pro4
-rw-r--r--src/structs.h14
-rw-r--r--src/testdir/Make_all.mak2
-rw-r--r--src/testdir/test_blob.vim179
-rw-r--r--src/testdir/test_channel.vim45
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h16
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",
+