summaryrefslogtreecommitdiffstats
path: root/src/blob.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 08:50:31 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 08:50:31 +0000
commitaed8ce9da277f5ecffe968b324f242c41c3b752a (patch)
treed2e538394cb7a8a7c42a4aac6ccf1a8e3256999b /src/blob.c
parentInitial commit. (diff)
downloadvim-aed8ce9da277f5ecffe968b324f242c41c3b752a.tar.xz
vim-aed8ce9da277f5ecffe968b324f242c41c3b752a.zip
Adding upstream version 2:9.0.1378.upstream/2%9.0.1378upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/blob.c')
-rw-r--r--src/blob.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/src/blob.c b/src/blob.c
new file mode 100644
index 0000000..bfc7723
--- /dev/null
+++ b/src/blob.c
@@ -0,0 +1,853 @@
+/* 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 = ALLOC_CLEAR_ONE_ID(blob_T, aid_blob_alloc);
+
+ 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;
+}
+
+ int
+blob_copy(blob_T *from, typval_T *to)
+{
+ int len;
+
+ to->v_type = VAR_BLOB;
+ to->v_lock = 0;
+ if (from == NULL)
+ {
+ to->vval.v_blob = NULL;
+ return OK;
+ }
+
+ if (rettv_blob_alloc(to) == FAIL)
+ return FAIL;
+
+ len = from->bv_ga.ga_len;
+ if (len > 0)
+ {
+ to->vval.v_blob->bv_ga.ga_data =
+ vim_memsave(from->bv_ga.ga_data, len);
+ if (to->vval.v_blob->bv_ga.ga_data == NULL)
+ len = 0;
+ }
+ to->vval.v_blob->bv_ga.ga_len = len;
+ to->vval.v_blob->bv_ga.ga_maxlen = len;
+
+ return OK;
+}
+
+ 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.
+ */
+ int
+blob_get(blob_T *b, int idx)
+{
+ return ((char_u*)b->bv_ga.ga_data)[idx];
+}
+
+/*
+ * Store one byte "byte" in blob "blob" at "idx".
+ * Caller must make sure that "idx" is valid.
+ */
+ void
+blob_set(blob_T *blob, int idx, int byte)
+{
+ ((char_u*)blob->bv_ga.ga_data)[idx] = byte;
+}
+
+/*
+ * Store one byte "byte" in blob "blob" at "idx".
+ * Append one byte if needed.
+ */
+ void
+blob_set_append(blob_T *blob, int idx, int byte)
+{
+ garray_T *gap = &blob->bv_ga;
+
+ // Allow for appending a byte. Setting a byte beyond
+ // the end is an error otherwise.
+ if (idx < gap->ga_len
+ || (idx == gap->ga_len && ga_grow(gap, 1) == OK))
+ {
+ blob_set(blob, idx, byte);
+ if (idx == gap->ga_len)
+ ++gap->ga_len;
+ }
+}
+
+/*
+ * Return TRUE when two blobs have exactly the same values.
+ */
+ int
+blob_equal(
+ blob_T *b1,
+ blob_T *b2)
+{
+ int i;
+ int len1 = blob_len(b1);
+ int len2 = blob_len(b2);
+
+ // empty and NULL are considered the same
+ if (len1 == 0 && len2 == 0)
+ return TRUE;
+ if (b1 == b2)
+ return TRUE;
+ if (len1 != len2)
+ 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".
+ * Caller has allocated a blob in "rettv".
+ * Return OK or FAIL.
+ */
+ int
+read_blob(FILE *fd, typval_T *rettv, off_T offset, off_T size_arg)
+{
+ blob_T *blob = rettv->vval.v_blob;
+ struct stat st;
+ int whence;
+ off_T size = size_arg;
+
+ if (fstat(fileno(fd), &st) < 0)
+ return FAIL; // can't read the file, error
+
+ if (offset >= 0)
+ {
+ // The size defaults to the whole file. If a size is given it is
+ // limited to not go past the end of the file.
+ if (size == -1 || (size > st.st_size - offset
+#ifdef S_ISCHR
+ && !S_ISCHR(st.st_mode)
+#endif
+ ))
+ // size may become negative, checked below
+ size = st.st_size - offset;
+ whence = SEEK_SET;
+ }
+ else
+ {
+ // limit the offset to not go before the start of the file
+ if (-offset > st.st_size
+#ifdef S_ISCHR
+ && !S_ISCHR(st.st_mode)
+#endif
+ )
+ offset = -st.st_size;
+ // Size defaults to reading until the end of the file.
+ if (size == -1 || size > -offset)
+ size = -offset;
+ whence = SEEK_END;
+ }
+ if (size <= 0)
+ return OK;
+ if (offset != 0 && vim_fseek(fd, offset, whence) != 0)
+ return OK;
+
+ if (ga_grow(&blob->bv_ga, (int)size) == FAIL)
+ return FAIL;
+ blob->bv_ga.ga_len = (int)size;
+ if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
+ < (size_t)blob->bv_ga.ga_len)
+ {
+ // An empty blob is returned on error.
+ blob_free(rettv->vval.v_blob);
+ rettv->vval.v_blob = NULL;
+ 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_error_while_writing));
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * Convert a blob to a readable form: "0z00112233.44556677.8899"
+ */
+ char_u *
+blob2string(blob_T *blob, char_u **tofree, char_u *numbuf)
+{
+ int i;
+ garray_T ga;
+
+ if (blob == NULL)
+ {
+ *tofree = NULL;
+ return (char_u *)"0z";
+ }
+
+ // Store bytes in the growarray.
+ ga_init2(&ga, 1, 4000);
+ ga_concat(&ga, (char_u *)"0z");
+ for (i = 0; i < blob_len(blob); i++)
+ {
+ if (i > 0 && (i & 3) == 0)
+ ga_concat(&ga, (char_u *)".");
+ vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", blob_get(blob, i));
+ ga_concat(&ga, numbuf);
+ }
+ ga_append(&ga, NUL); // append a NUL at the end
+ *tofree = ga.ga_data;
+ return *tofree;
+}
+
+/*
+ * Convert a string variable, in the format of blob2string(), to a blob.
+ * Return NULL when conversion failed.
+ */
+ blob_T *
+string2blob(char_u *str)
+{
+ blob_T *blob = blob_alloc();
+ char_u *s = str;
+
+ if (blob == NULL)
+ return NULL;
+ if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z'))
+ goto failed;
+ s += 2;
+ while (vim_isxdigit(*s))
+ {
+ if (!vim_isxdigit(s[1]))
+ goto failed;
+ ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1]));
+ s += 2;
+ if (*s == '.' && vim_isxdigit(s[1]))
+ ++s;
+ }
+ if (*skipwhite(s) != NUL)
+ goto failed; // text after final digit
+
+ ++blob->bv_refcount;
+ return blob;
+
+failed:
+ blob_free(blob);
+ return NULL;
+}
+
+/*
+ * Returns a slice of 'blob' from index 'n1' to 'n2' in 'rettv'. The length of
+ * the blob is 'len'. Returns an empty blob if the indexes are out of range.
+ */
+ static int
+blob_slice(
+ blob_T *blob,
+ long len,
+ varnumber_T n1,
+ varnumber_T n2,
+ int exclusive,
+ typval_T *rettv)
+{
+ if (n1 < 0)
+ {
+ n1 = len + n1;
+ if (n1 < 0)
+ n1 = 0;
+ }
+ if (n2 < 0)
+ n2 = len + n2;
+ else if (n2 >= len)
+ n2 = len - (exclusive ? 0 : 1);
+ if (exclusive)
+ --n2;
+ if (n1 >= len || n2 < 0 || n1 > n2)
+ {
+ clear_tv(rettv);
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = NULL;
+ }
+ else
+ {
+ blob_T *new_blob = blob_alloc();
+ long i;
+
+ if (new_blob != NULL)
+ {
+ if (ga_grow(&new_blob->bv_ga, n2 - n1 + 1) == FAIL)
+ {
+ blob_free(new_blob);
+ return FAIL;
+ }
+ new_blob->bv_ga.ga_len = n2 - n1 + 1;
+ for (i = n1; i <= n2; i++)
+ blob_set(new_blob, i - n1, blob_get(blob, i));
+
+ clear_tv(rettv);
+ rettv_blob_set(rettv, new_blob);
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * Return the byte value in 'blob' at index 'idx' in 'rettv'. If the index is
+ * too big or negative that is an error. The length of the blob is 'len'.
+ */
+ static int
+blob_index(
+ blob_T *blob,
+ int len,
+ varnumber_T idx,
+ typval_T *rettv)
+{
+ // The resulting variable is a byte value.
+ // If the index is too big or negative that is an error.
+ if (idx < 0)
+ idx = len + idx;
+ if (idx < len && idx >= 0)
+ {
+ int v = blob_get(blob, idx);
+
+ clear_tv(rettv);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = v;
+ }
+ else
+ {
+ semsg(_(e_blob_index_out_of_range_nr), idx);
+ return FAIL;
+ }
+
+ return OK;
+}
+
+ int
+blob_slice_or_index(
+ blob_T *blob,
+ int is_range,
+ varnumber_T n1,
+ varnumber_T n2,
+ int exclusive,
+ typval_T *rettv)
+{
+ long len = blob_len(blob);
+
+ if (is_range)
+ return blob_slice(blob, len, n1, n2, exclusive, rettv);
+ else
+ return blob_index(blob, len, n1, rettv);
+ return OK;
+}
+
+/*
+ * Check if "n1"- is a valid index for a blobl with length "bloblen".
+ */
+ int
+check_blob_index(long bloblen, varnumber_T n1, int quiet)
+{
+ if (n1 < 0 || n1 > bloblen)
+ {
+ if (!quiet)
+ semsg(_(e_blob_index_out_of_range_nr), n1);
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * Check if "n1"-"n2" is a valid range for a blob with length "bloblen".
+ */
+ int
+check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet)
+{
+ if (n2 < 0 || n2 >= bloblen || n2 < n1)
+ {
+ if (!quiet)
+ semsg(_(e_blob_index_out_of_range_nr), n2);
+ return FAIL;
+ }
+ return OK;
+}
+
+/*
+ * Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src".
+ * Caller must make sure "src" is a blob.
+ * Returns FAIL if the number of bytes does not match.
+ */
+ int
+blob_set_range(blob_T *dest, long n1, long n2, typval_T *src)
+{
+ int il, ir;
+
+ if (n2 - n1 + 1 != blob_len(src->vval.v_blob))
+ {
+ emsg(_(e_blob_value_does_not_have_right_number_of_bytes));
+ return FAIL;
+ }
+
+ ir = 0;
+ for (il = n1; il <= n2; il++)
+ blob_set(dest, il, blob_get(src->vval.v_blob, ir++));
+ return OK;
+}
+
+/*
+ * "add(blob, item)" function
+ */
+ void
+blob_add(typval_T *argvars, typval_T *rettv)
+{
+ blob_T *b = argvars[0].vval.v_blob;
+ int error = FALSE;
+ varnumber_T n;
+
+ if (b == NULL)
+ {
+ if (in_vim9script())
+ emsg(_(e_cannot_add_to_null_blob));
+ return;
+ }
+
+ if (value_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE))
+ return;
+
+ n = tv_get_number_chk(&argvars[1], &error);
+ if (error)
+ return;
+
+ ga_append(&b->bv_ga, (int)n);
+ copy_tv(&argvars[0], rettv);
+}
+
+/*
+ * "remove({blob}, {idx} [, {end}])" function
+ */
+ void
+blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
+{
+ blob_T *b = argvars[0].vval.v_blob;
+ blob_T *newblob;
+ int error = FALSE;
+ long idx;
+ long end;
+ int len;
+ char_u *p;
+
+ if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE))
+ return;
+
+ idx = (long)tv_get_number_chk(&argvars[1], &error);
+ if (error)
+ return;
+
+ len = blob_len(b);
+
+ if (idx < 0)
+ // count from the end
+ idx = len + idx;
+ if (idx < 0 || idx >= len)
+ {
+ semsg(_(e_blob_index_out_of_range_nr), 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;
+ return;
+ }
+
+ // Remove range of items, return blob 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)
+ {
+ semsg(_(e_blob_index_out_of_range_nr), end);
+ return;
+ }
+ newblob = blob_alloc();
+ if (newblob == NULL)
+ return;
+ newblob->bv_ga.ga_len = end - idx + 1;
+ if (ga_grow(&newblob->bv_ga, end - idx + 1) == FAIL)
+ {
+ vim_free(newblob);
+ return;
+ }
+ p = (char_u *)b->bv_ga.ga_data;
+ mch_memmove((char_u *)newblob->bv_ga.ga_data, p + idx,
+ (size_t)(end - idx + 1));
+ ++newblob->bv_refcount;
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = newblob;
+
+ if (len - end - 1 > 0)
+ mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
+ b->bv_ga.ga_len -= end - idx + 1;
+}
+
+/*
+ * Implementation of map() and filter() for a Blob. Apply "expr" to every
+ * number in Blob "blob_arg" and return the result in "rettv".
+ */
+ void
+blob_filter_map(
+ blob_T *blob_arg,
+ filtermap_T filtermap,
+ typval_T *expr,
+ typval_T *rettv)
+{
+ blob_T *b;
+ int i;
+ typval_T tv;
+ varnumber_T val;
+ blob_T *b_ret;
+ int idx = 0;
+ int rem;
+ typval_T newtv;
+ funccall_T *fc;
+
+ if (filtermap == FILTERMAP_MAPNEW)
+ {
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = NULL;
+ }
+ if ((b = blob_arg) == NULL)
+ return;
+
+ b_ret = b;
+ if (filtermap == FILTERMAP_MAPNEW)
+ {
+ if (blob_copy(b, rettv) == FAIL)
+ return;
+ b_ret = rettv->vval.v_blob;
+ }
+
+ // set_vim_var_nr() doesn't set the type
+ set_vim_var_type(VV_KEY, VAR_NUMBER);
+
+ // Create one funccal_T for all eval_expr_typval() calls.
+ fc = eval_expr_get_funccal(expr, &newtv);
+
+ for (i = 0; i < b->bv_ga.ga_len; i++)
+ {
+ tv.v_type = VAR_NUMBER;
+ val = blob_get(b, i);
+ tv.vval.v_number = val;
+ set_vim_var_nr(VV_KEY, idx);
+ if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL
+ || did_emsg)
+ break;
+ if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
+ {
+ clear_tv(&newtv);
+ emsg(_(e_invalid_operation_for_blob));
+ break;
+ }
+ if (filtermap != FILTERMAP_FILTER)
+ {
+ if (newtv.vval.v_number != val)
+ blob_set(b_ret, i, newtv.vval.v_number);
+ }
+ else if (rem)
+ {
+ char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
+
+ mch_memmove(p + i, p + i + 1,
+ (size_t)b->bv_ga.ga_len - i - 1);
+ --b->bv_ga.ga_len;
+ --i;
+ }
+ ++idx;
+ }
+
+ if (fc != NULL)
+ remove_funccal();
+}
+
+/*
+ * "insert(blob, {item} [, {idx}])" function
+ */
+ void
+blob_insert_func(typval_T *argvars, typval_T *rettv)
+{
+ blob_T *b = argvars[0].vval.v_blob;
+ long before = 0;
+ int error = FALSE;
+ int val, len;
+ char_u *p;
+
+ if (b == NULL)
+ {
+ if (in_vim9script())
+ emsg(_(e_cannot_add_to_null_blob));
+ return;
+ }
+
+ if (value_check_lock(b->bv_lock, (char_u *)N_("insert() argument"), TRUE))
+ return;
+
+ len = blob_len(b);
+ 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)
+ {
+ semsg(_(e_invalid_argument_str), tv_get_string(&argvars[2]));
+ return;
+ }
+ }
+ val = tv_get_number_chk(&argvars[1], &error);
+ if (error)
+ return;
+ if (val < 0 || val > 255)
+ {
+ semsg(_(e_invalid_argument_str), tv_get_string(&argvars[1]));
+ return;
+ }
+
+ if (ga_grow(&b->bv_ga, 1) == FAIL)
+ return;
+ p = (char_u *)b->bv_ga.ga_data;
+ mch_memmove(p + before + 1, p + before, (size_t)len - before);
+ *(p + before) = val;
+ ++b->bv_ga.ga_len;
+
+ copy_tv(&argvars[0], rettv);
+}
+
+/*
+ * Implementation of reduce() for Blob "argvars[0]" using the function "expr"
+ * starting with the optional initial value "argvars[2]" and return the result
+ * in "rettv".
+ */
+ void
+blob_reduce(
+ typval_T *argvars,
+ typval_T *expr,
+ typval_T *rettv)
+{
+ blob_T *b = argvars[0].vval.v_blob;
+ int called_emsg_start = called_emsg;
+ int r;
+ typval_T initial;
+ typval_T argv[3];
+ int i;
+
+ if (argvars[2].v_type == VAR_UNKNOWN)
+ {
+ if (b == NULL || b->bv_ga.ga_len == 0)
+ {
+ semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "Blob");
+ return;
+ }
+ initial.v_type = VAR_NUMBER;
+ initial.vval.v_number = blob_get(b, 0);
+ i = 1;
+ }
+ else if (check_for_number_arg(argvars, 2) == FAIL)
+ return;
+ else
+ {
+ initial = argvars[2];
+ i = 0;
+ }
+
+ copy_tv(&initial, rettv);
+ if (b == NULL)
+ return;
+
+ for ( ; i < b->bv_ga.ga_len; i++)
+ {
+ argv[0] = *rettv;
+ argv[1].v_type = VAR_NUMBER;
+ argv[1].vval.v_number = blob_get(b, i);
+
+ r = eval_expr_typval(expr, argv, 2, NULL, rettv);
+
+ clear_tv(&argv[0]);
+ if (r == FAIL || called_emsg != called_emsg_start)
+ return;
+ }
+}
+
+/*
+ * "reverse({blob})" function
+ */
+ void
+blob_reverse(blob_T *b, typval_T *rettv)
+{
+ 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);
+}
+
+/*
+ * blob2list() function
+ */
+ void
+f_blob2list(typval_T *argvars, typval_T *rettv)
+{
+ blob_T *blob;
+ list_T *l;
+ int i;
+
+ if (rettv_list_alloc(rettv) == FAIL)
+ return;
+
+ if (check_for_blob_arg(argvars, 0) == FAIL)
+ return;
+
+ blob = argvars->vval.v_blob;
+ l = rettv->vval.v_list;
+ for (i = 0; i < blob_len(blob); i++)
+ list_append_number(l, blob_get(blob, i));
+}
+
+/*
+ * list2blob() function
+ */
+ void
+f_list2blob(typval_T *argvars, typval_T *rettv)
+{
+ list_T *l;
+ listitem_T *li;
+ blob_T *blob;
+
+ if (rettv_blob_alloc(rettv) == FAIL)
+ return;
+ blob = rettv->vval.v_blob;
+
+ if (check_for_list_arg(argvars, 0) == FAIL)
+ return;
+
+ l = argvars->vval.v_list;
+ if (l == NULL)
+ return;
+
+ CHECK_LIST_MATERIALIZE(l);
+ FOR_ALL_LIST_ITEMS(l, li)
+ {
+ int error;
+ varnumber_T n;
+
+ error = FALSE;
+ n = tv_get_number_chk(&li->li_tv, &error);
+ if (error == TRUE || n < 0 || n > 255)
+ {
+ if (!error)
+ semsg(_(e_invalid_value_for_blob_nr), n);
+ ga_clear(&blob->bv_ga);
+ return;
+ }
+ ga_append(&blob->bv_ga, n);
+ }
+}
+
+#endif // defined(FEAT_EVAL)