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 * 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 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 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 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 } 84 return ret; 85 } 86 87 void 88 blob_free(blob_T *b) 89 { 90 ga_clear(&b->bv_ga); 91 vim_free(b); 92 } 93 94 /* 95 * Unreference a blob: decrement the reference count and free it when it 96 * becomes zero. 97 */ 98 void 99 blob_unref(blob_T *b) 100 { 101 if (b != NULL && --b->bv_refcount <= 0) 102 blob_free(b); 103 } 104 105 /* 106 * Get the length of data. 107 */ 108 long 109 blob_len(blob_T *b) 110 { 111 if (b == NULL) 112 return 0L; 113 return b->bv_ga.ga_len; 114 } 115 116 /* 117 * Get byte "idx" in blob "b". 118 * Caller must check that "idx" is valid. 119 */ 120 int 121 blob_get(blob_T *b, int idx) 122 { 123 return ((char_u*)b->bv_ga.ga_data)[idx]; 124 } 125 126 /* 127 * Store one byte "c" in blob "b" at "idx". 128 * Caller must make sure that "idx" is valid. 129 */ 130 void 131 blob_set(blob_T *b, int idx, char_u c) 132 { 133 ((char_u*)b->bv_ga.ga_data)[idx] = c; 134 } 135 136 /* 137 * Return TRUE when two blobs have exactly the same values. 138 */ 139 int 140 blob_equal( 141 blob_T *b1, 142 blob_T *b2) 143 { 144 int i; 145 int len1 = blob_len(b1); 146 int len2 = blob_len(b2); 147 148 // empty and NULL are considered the same 149 if (len1 == 0 && len2 == 0) 150 return TRUE; 151 if (b1 == b2) 152 return TRUE; 153 if (len1 != len2) 154 return FALSE; 155 156 for (i = 0; i < b1->bv_ga.ga_len; i++) 157 if (blob_get(b1, i) != blob_get(b2, i)) return FALSE; 158 return TRUE; 159 } 160 161 /* 162 * Read "blob" from file "fd". 163 * Return OK or FAIL. 164 */ 165 int 166 read_blob(FILE *fd, blob_T *blob) 167 { 168 struct stat st; 169 170 if (fstat(fileno(fd), &st) < 0) 171 return FAIL; 172 if (ga_grow(&blob->bv_ga, st.st_size) == FAIL) 173 return FAIL; 174 blob->bv_ga.ga_len = st.st_size; 175 if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) 176 < (size_t)blob->bv_ga.ga_len) 177 return FAIL; 178 return OK; 179 } 180 181 /* 182 * Write "blob" to file "fd". 183 * Return OK or FAIL. 184 */ 185 int 186 write_blob(FILE *fd, blob_T *blob) 187 { 188 if (fwrite(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd) 189 < (size_t)blob->bv_ga.ga_len) 190 { 191 emsg(_(e_write)); 192 return FAIL; 193 } 194 return OK; 195 } 196 197 /* 198 * Convert a blob to a readable form: "0z00112233.44556677.8899" 199 */ 200 char_u * 201 blob2string(blob_T *blob, char_u **tofree, char_u *numbuf) 202 { 203 int i; 204 garray_T ga; 205 206 if (blob == NULL) 207 { 208 *tofree = NULL; 209 return (char_u *)"0z"; 210 } 211 212 // Store bytes in the growarray. 213 ga_init2(&ga, 1, 4000); 214 ga_concat(&ga, (char_u *)"0z"); 215 for (i = 0; i < blob_len(blob); i++) 216 { 217 if (i > 0 && (i & 3) == 0) 218 ga_concat(&ga, (char_u *)"."); 219 vim_snprintf((char *)numbuf, NUMBUFLEN, "%02X", (int)blob_get(blob, i)); 220 ga_concat(&ga, numbuf); 221 } 222 *tofree = ga.ga_data; 223 return *tofree; 224 } 225 226 /* 227 * Convert a string variable, in the format of blob2string(), to a blob. 228 * Return NULL when conversion failed. 229 */ 230 blob_T * 231 string2blob(char_u *str) 232 { 233 blob_T *blob = blob_alloc(); 234 char_u *s = str; 235 236 if (blob == NULL) 237 return NULL; 238 if (s[0] != '0' || (s[1] != 'z' && s[1] != 'Z')) 239 goto failed; 240 s += 2; 241 while (vim_isxdigit(*s)) 242 { 243 if (!vim_isxdigit(s[1])) 244 goto failed; 245 ga_append(&blob->bv_ga, (hex2nr(s[0]) << 4) + hex2nr(s[1])); 246 s += 2; 247 if (*s == '.' && vim_isxdigit(s[1])) 248 ++s; 249 } 250 if (*skipwhite(s) != NUL) 251 goto failed; // text after final digit 252 253 ++blob->bv_refcount; 254 return blob; 255 256 failed: 257 blob_free(blob); 258 return NULL; 259 } 260 261 /* 262 * "remove({blob})" function 263 */ 264 void 265 blob_remove(typval_T *argvars, typval_T *rettv) 266 { 267 int error = FALSE; 268 long idx = (long)tv_get_number_chk(&argvars[1], &error); 269 long end; 270 271 if (!error) 272 { 273 blob_T *b = argvars[0].vval.v_blob; 274 int len = blob_len(b); 275 char_u *p; 276 277 if (idx < 0) 278 // count from the end 279 idx = len + idx; 280 if (idx < 0 || idx >= len) 281 { 282 semsg(_(e_blobidx), idx); 283 return; 284 } 285 if (argvars[2].v_type == VAR_UNKNOWN) 286 { 287 // Remove one item, return its value. 288 p = (char_u *)b->bv_ga.ga_data; 289 rettv->vval.v_number = (varnumber_T) *(p + idx); 290 mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); 291 --b->bv_ga.ga_len; 292 } 293 else 294 { 295 blob_T *blob; 296 297 // Remove range of items, return list with values. 298 end = (long)tv_get_number_chk(&argvars[2], &error); 299 if (error) 300 return; 301 if (end < 0) 302 // count from the end 303 end = len + end; 304 if (end >= len || idx > end) 305 { 306 semsg(_(e_blobidx), end); 307 return; 308 } 309 blob = blob_alloc(); 310 if (blob == NULL) 311 return; 312 blob->bv_ga.ga_len = end - idx + 1; 313 if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) 314 { 315 vim_free(blob); 316 return; 317 } 318 p = (char_u *)b->bv_ga.ga_data; 319 mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, 320 (size_t)(end - idx + 1)); 321 ++blob->bv_refcount; 322 rettv->v_type = VAR_BLOB; 323 rettv->vval.v_blob = blob; 324 325 mch_memmove(p + idx, p + end + 1, (size_t)(len - end)); 326 b->bv_ga.ga_len -= end - idx + 1; 327 } 328 } 329 } 330 331 #endif // defined(FEAT_EVAL) 332