mirror of https://github.com/vim/vim.git
332 lines
6.2 KiB
C
332 lines
6.2 KiB
C
/* 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(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;
|
|
}
|
|
|
|
int
|
|
blob_copy(blob_T *from, typval_T *to)
|
|
{
|
|
int ret = OK;
|
|
|
|
to->v_type = VAR_BLOB;
|
|
to->v_lock = 0;
|
|
if (from == NULL)
|
|
to->vval.v_blob = NULL;
|
|
else if (rettv_blob_alloc(to) == FAIL)
|
|
ret = FAIL;
|
|
else
|
|
{
|
|
int 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;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
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 "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;
|
|
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".
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* 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", (int)blob_get(blob, i));
|
|
ga_concat(&ga, numbuf);
|
|
}
|
|
*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;
|
|
}
|
|
|
|
/*
|
|
* "remove({blob})" function
|
|
*/
|
|
void
|
|
blob_remove(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
int error = FALSE;
|
|
long idx = (long)tv_get_number_chk(&argvars[1], &error);
|
|
long end;
|
|
|
|
if (!error)
|
|
{
|
|
blob_T *b = argvars[0].vval.v_blob;
|
|
int len = blob_len(b);
|
|
char_u *p;
|
|
|
|
if (idx < 0)
|
|
// count from the end
|
|
idx = len + idx;
|
|
if (idx < 0 || idx >= len)
|
|
{
|
|
semsg(_(e_blobidx), 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;
|
|
}
|
|
else
|
|
{
|
|
blob_T *blob;
|
|
|
|
// Remove range of items, return list 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_blobidx), end);
|
|
return;
|
|
}
|
|
blob = blob_alloc();
|
|
if (blob == NULL)
|
|
return;
|
|
blob->bv_ga.ga_len = end - idx + 1;
|
|
if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
|
|
{
|
|
vim_free(blob);
|
|
return;
|
|
}
|
|
p = (char_u *)b->bv_ga.ga_data;
|
|
mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
|
|
(size_t)(end - idx + 1));
|
|
++blob->bv_refcount;
|
|
rettv->v_type = VAR_BLOB;
|
|
rettv->vval.v_blob = blob;
|
|
|
|
mch_memmove(p + idx, p + end + 1, (size_t)(len - end));
|
|
b->bv_ga.ga_len -= end - idx + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // defined(FEAT_EVAL)
|