1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * blob.c: Blob support by Yasuhiro Matsumoto
12  */
13 
14 #include "vim.h"
15 
16 #if defined(FEAT_EVAL) || defined(PROTO)
17 
18 /*
19  * Allocate an empty blob.
20  * Caller should take care of the reference count.
21  */
22     blob_T *
blob_alloc(void)23 blob_alloc(void)
24 {
25     blob_T *blob = ALLOC_CLEAR_ONE(blob_T);
26 
27     if (blob != NULL)
28 	ga_init2(&blob->bv_ga, 1, 100);
29     return blob;
30 }
31 
32 /*
33  * Allocate an empty blob for a return value, with reference count set.
34  * Returns OK or FAIL.
35  */
36     int
rettv_blob_alloc(typval_T * rettv)37 rettv_blob_alloc(typval_T *rettv)
38 {
39     blob_T	*b = blob_alloc();
40 
41     if (b == NULL)
42 	return FAIL;
43 
44     rettv_blob_set(rettv, b);
45     return OK;
46 }
47 
48 /*
49  * Set a blob as the return value.
50  */
51     void
rettv_blob_set(typval_T * rettv,blob_T * b)52 rettv_blob_set(typval_T *rettv, blob_T *b)
53 {
54     rettv->v_type = VAR_BLOB;
55     rettv->vval.v_blob = b;
56     if (b != NULL)
57 	++b->bv_refcount;
58 }
59 
60     int
blob_copy(blob_T * from,typval_T * to)61 blob_copy(blob_T *from, typval_T *to)
62 {
63     int	    ret = OK;
64 
65     to->v_type = VAR_BLOB;
66     to->v_lock = 0;
67     if (from == NULL)
68 	to->vval.v_blob = NULL;
69     else if (rettv_blob_alloc(to) == FAIL)
70 	ret = FAIL;
71     else
72     {
73 	int  len = from->bv_ga.ga_len;
74 
75 	if (len > 0)
76 	{
77 	    to->vval.v_blob->bv_ga.ga_data =
78 					 vim_memsave(from->bv_ga.ga_data, len);
79 	    if (to->vval.v_blob->bv_ga.ga_data == NULL)
80 		len = 0;
81 	}
82 	to->vval.v_blob->bv_ga.ga_len = len;
83 	to->vval.v_blob->bv_ga.ga_maxlen = len;
84     }
85     return ret;
86 }
87 
88     void
blob_free(blob_T * b)89 blob_free(blob_T *b)
90 {
91     ga_clear(&b->bv_ga);
92     vim_free(b);
93 }
94 
95 /*
96  * Unreference a blob: decrement the reference count and free it when it
97  * becomes zero.
98  */
99     void
blob_unref(blob_T * b)100 blob_unref(blob_T *b)
101 {
102     if (b != NULL && --b->bv_refcount <= 0)
103 	blob_free(b);
104 }
105 
106 /*
107  * Get the length of data.
108  */
109     long
blob_len(blob_T * b)110 blob_len(blob_T *b)
111 {
112     if (b == NULL)
113 	return 0L;
114     return b->bv_ga.ga_len;
115 }
116 
117 /*
118  * Get byte "idx" in blob "b".
119  * Caller must check that "idx" is valid.
120  */
121     int
blob_get(blob_T * b,int idx)122 blob_get(blob_T *b, int idx)
123 {
124     return ((char_u*)b->bv_ga.ga_data)[idx];
125 }
126 
127 /*
128  * Store one byte "byte" in blob "blob" at "idx".
129  * Caller must make sure that "idx" is valid.
130  */
131     void
blob_set(blob_T * blob,int idx,int byte)132 blob_set(blob_T *blob, int idx, int byte)
133 {
134     ((char_u*)blob->bv_ga.ga_data)[idx] = byte;
135 }
136 
137 /*
138  * Store one byte "byte" in blob "blob" at "idx".
139  * Append one byte if needed.
140  */
141     void
blob_set_append(blob_T * blob,int idx,int byte)142 blob_set_append(blob_T *blob, int idx, int byte)
143 {
144     garray_T *gap = &blob->bv_ga;
145 
146     // Allow for appending a byte.  Setting a byte beyond
147     // the end is an error otherwise.
148     if (idx < gap->ga_len
149 	    || (idx == gap->ga_len && ga_grow(gap, 1) == OK))
150     {
151 	blob_set(blob, idx, byte);
152 	if (idx == gap->ga_len)
153 	    ++gap->ga_len;
154     }
155 }
156 
157 /*
158  * Return TRUE when two blobs have exactly the same values.
159  */
160     int
blob_equal(blob_T * b1,blob_T * b2)161 blob_equal(
162     blob_T	*b1,
163     blob_T	*b2)
164 {
165     int	    i;
166     int	    len1 = blob_len(b1);
167     int	    len2 = blob_len(b2);
168 
169     // empty and NULL are considered the same
170     if (len1 == 0 && len2 == 0)
171 	return TRUE;
172     if (b1 == b2)
173 	return TRUE;
174     if (len1 != len2)
175 	return FALSE;
176 
177     for (i = 0; i < b1->bv_ga.ga_len; i++)
178 	if (blob_get(b1, i) != blob_get(b2, i)) return FALSE;
179     return TRUE;
180 }
181 
182 /*
183  * Read "blob" from file "fd".
184  * Return OK or FAIL.
185  */
186     int
read_blob(FILE * fd,blob_T * blob)187 read_blob(FILE *fd, blob_T *blob)
188 {
189     struct stat	st;
190 
191     if (fstat(fileno(fd), &st) < 0)
192 	return FAIL;
193     if (ga_grow(&blob->bv_ga, st.st_size) == FAIL)
194 	return FAIL;
195     blob->bv_ga.ga_len = st.st_size;
196     if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
197 						  < (size_t)blob->bv_ga.ga_len)
198 	return FAIL;
199     return OK;
200 }
201 
202 /*
203  * Write "blob" to file "fd".
204  * Return OK or FAIL.
205  */
206     int
write_blob(FILE * fd,blob_T * blob)207 write_blob(FILE *fd, blob_T *blob)
208 {
209     if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
210 						  < (size_t)blob->bv_ga.ga_len)
211     {
212 	emsg(_(e_write));
213 	return FAIL;
214     }
215     return OK;
216 }
217 
218 /*
219  * Convert a blob to a readable form: "0z00112233.44556677.8899"
220  */
221     char_u *
blob2string(blob_T * blob,char_u ** tofree,char_u * numbuf)222 blob2string(blob_T *blob, char_u **tofree, char_u *numbuf)
223 {
224     int		i;
225     garray_T    ga;
226 
227     if (blob == NULL)
228     {
229 	*tofree = NULL;
230 	return (char_u *)"0z";
231     }
232 
233     // Store bytes in the growarray.
234     ga_init2(&ga, 1, 4000);
235     ga_concat(&ga, (char_u *)"0z");
236     for (i = 0; i < blob_len(blob); i++)
237     {
238 	if (i > 0 && (i & 3) == 0)
239 	    ga_concat(&ga, (char_u *)".");
240 	vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i));
241 	ga_concat(&ga, numbuf);
242     }
243     *tofree = ga.ga_data;
244     return *tofree;
245 }
246 
247 /*
248  * Convert a string variable, in the format of blob2string(), to a blob.
249  * Return NULL when conversion failed.
250  */
251     blob_T *
string2blob(char_u * str)252 string2blob(char_u *str)
253 {
254     blob_T  *blob = blob_alloc();
255     char_u  *s = str;
256 
257     if (blob == NULL)
258 	return NULL;
259     if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z'))
260 	goto failed;
261     s += 2;
262     while (vim_isxdigit(*s))
263     {
264 	if (!vim_isxdigit(s[1]))
265 	    goto failed;
266 	ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1]));
267 	s += 2;
268 	if (*s == '.' && vim_isxdigit(s[1]))
269 	    ++s;
270     }
271     if (*skipwhite(s) != NUL)
272 	goto failed;  // text after final digit
273 
274     ++blob->bv_refcount;
275     return blob;
276 
277 failed:
278     blob_free(blob);
279     return NULL;
280 }
281 
282     int
blob_slice_or_index(blob_T * blob,int is_range,varnumber_T n1,varnumber_T n2,int exclusive,typval_T * rettv)283 blob_slice_or_index(
284 	blob_T		*blob,
285 	int		is_range,
286 	varnumber_T	n1,
287 	varnumber_T	n2,
288 	int		exclusive,
289 	typval_T	*rettv)
290 {
291     long	    len = blob_len(blob);
292 
293     if (is_range)
294     {
295 	// The resulting variable is a sub-blob.  If the indexes
296 	// are out of range the result is empty.
297 	if (n1 < 0)
298 	{
299 	    n1 = len + n1;
300 	    if (n1 < 0)
301 		n1 = 0;
302 	}
303 	if (n2 < 0)
304 	    n2 = len + n2;
305 	else if (n2 >= len)
306 	    n2 = len - (exclusive ? 0 : 1);
307 	if (exclusive)
308 	    --n2;
309 	if (n1 >= len || n2 < 0 || n1 > n2)
310 	{
311 	    clear_tv(rettv);
312 	    rettv->v_type = VAR_BLOB;
313 	    rettv->vval.v_blob = NULL;
314 	}
315 	else
316 	{
317 	    blob_T  *new_blob = blob_alloc();
318 	    long    i;
319 
320 	    if (new_blob != NULL)
321 	    {
322 		if (ga_grow(&new_blob->bv_ga, n2 - n1 + 1) == FAIL)
323 		{
324 		    blob_free(new_blob);
325 		    return FAIL;
326 		}
327 		new_blob->bv_ga.ga_len = n2 - n1 + 1;
328 		for (i = n1; i <= n2; i++)
329 		    blob_set(new_blob, i - n1, blob_get(blob, i));
330 
331 		clear_tv(rettv);
332 		rettv_blob_set(rettv, new_blob);
333 	    }
334 	}
335     }
336     else
337     {
338 	// The resulting variable is a byte value.
339 	// If the index is too big or negative that is an error.
340 	if (n1 < 0)
341 	    n1 = len + n1;
342 	if (n1 < len && n1 >= 0)
343 	{
344 	    int v = blob_get(blob, n1);
345 
346 	    clear_tv(rettv);
347 	    rettv->v_type = VAR_NUMBER;
348 	    rettv->vval.v_number = v;
349 	}
350 	else
351 	{
352 	    semsg(_(e_blobidx), n1);
353 	    return FAIL;
354 	}
355     }
356     return OK;
357 }
358 
359 /*
360  * Check if "n1"- is a valid index for a blobl with length "bloblen".
361  */
362     int
check_blob_index(long bloblen,varnumber_T n1,int quiet)363 check_blob_index(long bloblen, varnumber_T n1, int quiet)
364 {
365     if (n1 < 0 || n1 > bloblen)
366     {
367 	if (!quiet)
368 	    semsg(_(e_blobidx), n1);
369 	return FAIL;
370     }
371     return OK;
372 }
373 
374 /*
375  * Check if "n1"-"n2" is a valid range for a blob with length "bloblen".
376  */
377     int
check_blob_range(long bloblen,varnumber_T n1,varnumber_T n2,int quiet)378 check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet)
379 {
380     if (n2 < 0 || n2 >= bloblen || n2 < n1)
381     {
382 	if (!quiet)
383 	    semsg(_(e_blobidx), n2);
384 	return FAIL;
385     }
386     return OK;
387 }
388 
389 /*
390  * Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src".
391  * Caller must make sure "src" is a blob.
392  * Returns FAIL if the number of bytes does not match.
393  */
394     int
blob_set_range(blob_T * dest,long n1,long n2,typval_T * src)395 blob_set_range(blob_T *dest, long n1, long n2, typval_T *src)
396 {
397     int	il, ir;
398 
399     if (n2 - n1 + 1 != blob_len(src->vval.v_blob))
400     {
401 	emsg(_("E972: Blob value does not have the right number of bytes"));
402 	return FAIL;
403     }
404 
405     ir = 0;
406     for (il = n1; il <= n2; il++)
407 	blob_set(dest, il, blob_get(src->vval.v_blob, ir++));
408     return OK;
409 }
410 
411 /*
412  * "remove({blob})" function
413  */
414     void
blob_remove(typval_T * argvars,typval_T * rettv,char_u * arg_errmsg)415 blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
416 {
417     blob_T	*b = argvars[0].vval.v_blob;
418     int		error = FALSE;
419     long	idx;
420     long	end;
421 
422     if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE))
423 	return;
424 
425     idx = (long)tv_get_number_chk(&argvars[1], &error);
426     if (!error)
427     {
428 	int	len = blob_len(b);
429 	char_u  *p;
430 
431 	if (idx < 0)
432 	    // count from the end
433 	    idx = len + idx;
434 	if (idx < 0 || idx >= len)
435 	{
436 	    semsg(_(e_blobidx), idx);
437 	    return;
438 	}
439 	if (argvars[2].v_type == VAR_UNKNOWN)
440 	{
441 	    // Remove one item, return its value.
442 	    p = (char_u *)b->bv_ga.ga_data;
443 	    rettv->vval.v_number = (varnumber_T) *(p + idx);
444 	    mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
445 	    --b->bv_ga.ga_len;
446 	}
447 	else
448 	{
449 	    blob_T  *blob;
450 
451 	    // Remove range of items, return blob with values.
452 	    end = (long)tv_get_number_chk(&argvars[2], &error);
453 	    if (error)
454 		return;
455 	    if (end < 0)
456 		// count from the end
457 		end = len + end;
458 	    if (end >= len || idx > end)
459 	    {
460 		semsg(_(e_blobidx), end);
461 		return;
462 	    }
463 	    blob = blob_alloc();
464 	    if (blob == NULL)
465 		return;
466 	    blob->bv_ga.ga_len = end - idx + 1;
467 	    if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
468 	    {
469 		vim_free(blob);
470 		return;
471 	    }
472 	    p = (char_u *)b->bv_ga.ga_data;
473 	    mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
474 						  (size_t)(end - idx + 1));
475 	    ++blob->bv_refcount;
476 	    rettv->v_type = VAR_BLOB;
477 	    rettv->vval.v_blob = blob;
478 
479 	    if (len - end - 1 > 0)
480 		mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
481 	    b->bv_ga.ga_len -= end - idx + 1;
482 	}
483     }
484 }
485 
486 /*
487  * blob2list() function
488  */
489     void
f_blob2list(typval_T * argvars,typval_T * rettv)490 f_blob2list(typval_T *argvars, typval_T *rettv)
491 {
492     blob_T	*blob;
493     list_T	*l;
494     int		i;
495 
496     if (rettv_list_alloc(rettv) == FAIL)
497 	return;
498 
499     if (check_for_blob_arg(argvars, 0) == FAIL)
500 	return;
501 
502     blob = argvars->vval.v_blob;
503     l = rettv->vval.v_list;
504     for (i = 0; i < blob_len(blob); i++)
505 	list_append_number(l, blob_get(blob, i));
506 }
507 
508 /*
509  * list2blob() function
510  */
511     void
f_list2blob(typval_T * argvars,typval_T * rettv)512 f_list2blob(typval_T *argvars, typval_T *rettv)
513 {
514     list_T	*l;
515     listitem_T	*li;
516     blob_T	*blob;
517 
518     if (rettv_blob_alloc(rettv) == FAIL)
519 	return;
520     blob = rettv->vval.v_blob;
521 
522     if (check_for_list_arg(argvars, 0) == FAIL)
523 	return;
524 
525     l = argvars->vval.v_list;
526     if (l == NULL)
527 	return;
528 
529     CHECK_LIST_MATERIALIZE(l);
530     FOR_ALL_LIST_ITEMS(l, li)
531     {
532 	int		error;
533 	varnumber_T	n;
534 
535 	error = FALSE;
536 	n = tv_get_number_chk(&li->li_tv, &error);
537 	if (error == TRUE || n < 0 || n > 255)
538 	{
539 	    if (!error)
540 		semsg(_(e_invalid_value_for_blob_nr), n);
541 	    ga_clear(&blob->bv_ga);
542 	    return;
543 	}
544 	ga_append(&blob->bv_ga, n);
545     }
546 }
547 
548 #endif // defined(FEAT_EVAL)
549