From 6e5ea8d2a995b32bbc5972edc4f827b959f2702f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Jan 2019 22:47:31 +0100 Subject: patch 8.1.0735: cannot handle binary data Problem: Cannot handle binary data. Solution: Add the Blob type. (Yasuhiro Matsumoto, closes #3638) --- src/eval.c | 423 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 400 insertions(+), 23 deletions(-) (limited to 'src/eval.c') 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", + (int)blob_get(b, i)); + ga_concat(&ga, numbuf); + } + ga_append(&ga, ']'); + *tofree = ga.ga_data; + r = *tofree; + } + break; + case VAR_LIST: if (tv->vval.v_list == NULL) { @@ -6841,6 +7124,9 @@ free_tv(typval_T *varp) case VAR_PARTIAL: partial_unref(varp->vval.v_partial); break; + case VAR_BLOB: + blob_unref(varp->vval.v_blob); + break; case VAR_LIST: list_unref(varp->vval.v_list); break; @@ -6887,6 +7173,10 @@ clear_tv(typval_T *varp) partial_unref(varp->vval.v_partial); varp->vval.v_partial = NULL; break; + case VAR_BLOB: + blob_unref(varp->vval.v_blob); + varp->vval.v_blob = NULL; + break; case VAR_LIST: list_unref(varp->vval.v_list); varp->vval.v_list = NULL; @@ -6990,6 +7280,9 @@ tv_get_number_chk(typval_T *varp, int *denote) EMSG(_("E913: Using a Channel as a Number")); break; #endif + case VAR_BLOB: + EMSG(_("E974: Using a Blob as a Number")); + break; case VAR_UNKNOWN: internal_error("tv_get_number(UNKNOWN)"); break; @@ -7037,6 +7330,9 @@ tv_get_float(typval_T *varp) EMSG(_("E914: Using a Channel as a Float")); break; # endif + case VAR_BLOB: + EMSG(_("E975: Using a Blob as a Float")); + break; case VAR_UNKNOWN: internal_error("tv_get_float(UNKNOWN)"); break; @@ -7113,6 +7409,9 @@ tv_get_string_buf_chk(typval_T *varp, char_u *buf) case VAR_SPECIAL: STRCPY(buf, get_var_special_name(varp->vval.v_number)); return buf; + case VAR_BLOB: + EMSG(_("E976: using Blob as a String")); + break; case VAR_JOB: #ifdef FEAT_JOB_CHANNEL { @@ -7805,6 +8104,15 @@ copy_tv(typval_T *from, typval_T *to) ++to->vval.v_partial->pt_refcount; } break; + case VAR_BLOB: + if (from->vval.v_blob == NULL) + to->vval.v_blob = NULL; + else + { + to->vval.v_blob = from->vval.v_blob; + ++to->vval.v_blob->bv_refcount; + } + break; case VAR_LIST: if (from->vval.v_list == NULL) to->vval.v_list = NULL; @@ -7863,6 +8171,7 @@ item_copy( case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_BLOB: copy_tv(from, to); break; case VAR_LIST: @@ -8601,6 +8910,7 @@ read_viminfo_varlist(vir_T *virp, int writing) #endif case 'D': type = VAR_DICT; break; case 'L': type = VAR_LIST; break; + case 'B': type = VAR_BLOB; break; case 'X': type = VAR_SPECIAL; break; } @@ -8608,7 +8918,8 @@ read_viminfo_varlist(vir_T *virp, int writing) if (tab != NULL) { tv.v_type = type; - if (type == VAR_STRING || type == VAR_DICT || type == VAR_LIST) + if (type == VAR_STRING || type == VAR_DICT || + type == VAR_LIST || type == VAR_BLOB) tv.vval.v_string = viminfo_readstring(virp, (int)(tab - virp->vir_line + 1), TRUE); #ifdef FEAT_FLOAT @@ -8617,7 +8928,7 @@ read_viminfo_varlist(vir_T *virp, int writing) #endif else tv.vval.v_number = atol((char *)tab + 1); - if (type == VAR_DICT || type == VAR_LIST) + if (type == VAR_DICT || type == VAR_LIST || type == VAR_BLOB) { typval_T *etv = eval_expr(tv.vval.v_string, NULL); @@ -8640,7 +8951,8 @@ read_viminfo_varlist(vir_T *virp, int writing) if (tv.v_type == VAR_STRING) vim_free(tv.vval.v_string); - else if (tv.v_type == VAR_DICT || tv.v_type == VAR_LIST) + else if (tv.v_type == VAR_DICT || tv.v_type == VAR_LIST || + tv.v_type == VAR_BLOB) clear_tv(&tv); } } @@ -8684,6 +8996,7 @@ write_viminfo_varlist(FILE *fp) case VAR_FLOAT: s = "FLO"; break; case VAR_DICT: s = "DIC"; break; case VAR_LIST: s = "LIS"; break; + case VAR_BLOB: s = "BLO"; break; case VAR_SPECIAL: s = "XPL"; break; case VAR_UNKNOWN: @@ -9250,6 +9563,33 @@ typval_compare( * it means TRUE. */ n1 = (type == TYPE_NEQUAL); } + else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) + { + if (type_is) + { + n1 = (typ1->v_type == typ2->v_type + && typ1->vval.v_blob == typ2->vval.v_blob); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + else if (typ1->v_type != typ2->v_type + || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) + { + if (typ1->v_type != typ2->v_type) + EMSG(_("E977: Can only compare Blob with Blob")); + else + EMSG(_(e_invalblob)); + clear_tv(typ1); + return FAIL; + } + else + { + // Compare two Blobs for being equal or unequal. + n1 = blob_equal(typ1->vval.v_blob, typ2->vval.v_blob); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + } else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) { if (type_is) @@ -10278,6 +10618,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) dict_T *d = NULL; typval_T save_val; typval_T save_key; + blob_T *b = NULL; int rem; int todo; char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); @@ -10286,7 +10627,12 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) int save_did_emsg; int idx = 0; - if (argvars[0].v_type == VAR_LIST) + if (argvars[0].v_type == VAR_BLOB) + { + if ((b = argvars[0].vval.v_blob) == NULL) + return; + } + else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) @@ -10353,6 +10699,37 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) } hash_unlock(ht); } + else if (argvars[0].v_type == VAR_BLOB) + { + int i; + typval_T tv; + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + for (i = 0; i < b->bv_ga.ga_len; i++) + { + tv.v_type = VAR_NUMBER; + tv.vval.v_number = blob_get(b, i); + vimvars[VV_KEY].vv_nr = idx; + if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) + break; + if (tv.v_type != VAR_NUMBER) + { + EMSG(_(e_invalblob)); + return; + } + tv.v_type = VAR_NUMBER; + blob_set(b, i, tv.vval.v_number); + if (!map && rem) + { + char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; + + mch_memmove(p + idx, p + i + 1, + (size_t)b->bv_ga.ga_len - i - 1); + --b->bv_ga.ga_len; + --i; + } + } + } else { vimvars[VV_KEY].vv_type = VAR_NUMBER; -- cgit v1.2.3