1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7 #include "buffer.h"
8 #include "posix.h"
9 #include "git2/buffer.h"
10 #include "buf_text.h"
11 #include <ctype.h>
12
13 /* Used as default value for git_buf->ptr so that people can always
14 * assume ptr is non-NULL and zero terminated even for new git_bufs.
15 */
16 char git_buf__initbuf[1];
17
18 char git_buf__oom[1];
19
20 #define ENSURE_SIZE(b, d) \
21 if ((b)->ptr == git_buf__oom || \
22 ((d) > (b)->asize && git_buf_grow((b), (d)) < 0))\
23 return -1;
24
25
git_buf_init(git_buf * buf,size_t initial_size)26 int git_buf_init(git_buf *buf, size_t initial_size)
27 {
28 buf->asize = 0;
29 buf->size = 0;
30 buf->ptr = git_buf__initbuf;
31
32 ENSURE_SIZE(buf, initial_size);
33
34 return 0;
35 }
36
git_buf_try_grow(git_buf * buf,size_t target_size,bool mark_oom)37 int git_buf_try_grow(
38 git_buf *buf, size_t target_size, bool mark_oom)
39 {
40 char *new_ptr;
41 size_t new_size;
42
43 if (buf->ptr == git_buf__oom)
44 return -1;
45
46 if (buf->asize == 0 && buf->size != 0) {
47 git_error_set(GIT_ERROR_INVALID, "cannot grow a borrowed buffer");
48 return GIT_EINVALID;
49 }
50
51 if (!target_size)
52 target_size = buf->size;
53
54 if (target_size <= buf->asize)
55 return 0;
56
57 if (buf->asize == 0) {
58 new_size = target_size;
59 new_ptr = NULL;
60 } else {
61 new_size = buf->asize;
62 /*
63 * Grow the allocated buffer by 1.5 to allow
64 * re-use of memory holes resulting from the
65 * realloc. If this is still too small, then just
66 * use the target size.
67 */
68 if ((new_size = (new_size << 1) - (new_size >> 1)) < target_size)
69 new_size = target_size;
70 new_ptr = buf->ptr;
71 }
72
73 /* round allocation up to multiple of 8 */
74 new_size = (new_size + 7) & ~7;
75
76 if (new_size < buf->size) {
77 if (mark_oom) {
78 if (buf->ptr && buf->ptr != git_buf__initbuf)
79 git__free(buf->ptr);
80 buf->ptr = git_buf__oom;
81 }
82
83 git_error_set_oom();
84 return -1;
85 }
86
87 new_ptr = git__realloc(new_ptr, new_size);
88
89 if (!new_ptr) {
90 if (mark_oom) {
91 if (buf->ptr && (buf->ptr != git_buf__initbuf))
92 git__free(buf->ptr);
93 buf->ptr = git_buf__oom;
94 }
95 return -1;
96 }
97
98 buf->asize = new_size;
99 buf->ptr = new_ptr;
100
101 /* truncate the existing buffer size if necessary */
102 if (buf->size >= buf->asize)
103 buf->size = buf->asize - 1;
104 buf->ptr[buf->size] = '\0';
105
106 return 0;
107 }
108
git_buf_grow(git_buf * buffer,size_t target_size)109 int git_buf_grow(git_buf *buffer, size_t target_size)
110 {
111 return git_buf_try_grow(buffer, target_size, true);
112 }
113
git_buf_grow_by(git_buf * buffer,size_t additional_size)114 int git_buf_grow_by(git_buf *buffer, size_t additional_size)
115 {
116 size_t newsize;
117
118 if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
119 buffer->ptr = git_buf__oom;
120 return -1;
121 }
122
123 return git_buf_try_grow(buffer, newsize, true);
124 }
125
git_buf_dispose(git_buf * buf)126 void git_buf_dispose(git_buf *buf)
127 {
128 if (!buf) return;
129
130 if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
131 git__free(buf->ptr);
132
133 git_buf_init(buf, 0);
134 }
135
git_buf_free(git_buf * buf)136 void git_buf_free(git_buf *buf)
137 {
138 git_buf_dispose(buf);
139 }
140
git_buf_sanitize(git_buf * buf)141 void git_buf_sanitize(git_buf *buf)
142 {
143 if (buf->ptr == NULL) {
144 assert(buf->size == 0 && buf->asize == 0);
145 buf->ptr = git_buf__initbuf;
146 } else if (buf->asize > buf->size)
147 buf->ptr[buf->size] = '\0';
148 }
149
git_buf_clear(git_buf * buf)150 void git_buf_clear(git_buf *buf)
151 {
152 buf->size = 0;
153
154 if (!buf->ptr) {
155 buf->ptr = git_buf__initbuf;
156 buf->asize = 0;
157 }
158
159 if (buf->asize > 0)
160 buf->ptr[0] = '\0';
161 }
162
git_buf_set(git_buf * buf,const void * data,size_t len)163 int git_buf_set(git_buf *buf, const void *data, size_t len)
164 {
165 size_t alloclen;
166
167 if (len == 0 || data == NULL) {
168 git_buf_clear(buf);
169 } else {
170 if (data != buf->ptr) {
171 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
172 ENSURE_SIZE(buf, alloclen);
173 memmove(buf->ptr, data, len);
174 }
175
176 buf->size = len;
177 if (buf->asize > buf->size)
178 buf->ptr[buf->size] = '\0';
179
180 }
181 return 0;
182 }
183
git_buf_is_binary(const git_buf * buf)184 int git_buf_is_binary(const git_buf *buf)
185 {
186 return git_buf_text_is_binary(buf);
187 }
188
git_buf_contains_nul(const git_buf * buf)189 int git_buf_contains_nul(const git_buf *buf)
190 {
191 return git_buf_text_contains_nul(buf);
192 }
193
git_buf_sets(git_buf * buf,const char * string)194 int git_buf_sets(git_buf *buf, const char *string)
195 {
196 return git_buf_set(buf, string, string ? strlen(string) : 0);
197 }
198
git_buf_putc(git_buf * buf,char c)199 int git_buf_putc(git_buf *buf, char c)
200 {
201 size_t new_size;
202 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
203 ENSURE_SIZE(buf, new_size);
204 buf->ptr[buf->size++] = c;
205 buf->ptr[buf->size] = '\0';
206 return 0;
207 }
208
git_buf_putcn(git_buf * buf,char c,size_t len)209 int git_buf_putcn(git_buf *buf, char c, size_t len)
210 {
211 size_t new_size;
212 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
213 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
214 ENSURE_SIZE(buf, new_size);
215 memset(buf->ptr + buf->size, c, len);
216 buf->size += len;
217 buf->ptr[buf->size] = '\0';
218 return 0;
219 }
220
git_buf_put(git_buf * buf,const char * data,size_t len)221 int git_buf_put(git_buf *buf, const char *data, size_t len)
222 {
223 if (len) {
224 size_t new_size;
225
226 assert(data);
227
228 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
229 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
230 ENSURE_SIZE(buf, new_size);
231 memmove(buf->ptr + buf->size, data, len);
232 buf->size += len;
233 buf->ptr[buf->size] = '\0';
234 }
235 return 0;
236 }
237
git_buf_puts(git_buf * buf,const char * string)238 int git_buf_puts(git_buf *buf, const char *string)
239 {
240 assert(string);
241 return git_buf_put(buf, string, strlen(string));
242 }
243
244 static const char base64_encode[] =
245 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
246
git_buf_encode_base64(git_buf * buf,const char * data,size_t len)247 int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
248 {
249 size_t extra = len % 3;
250 uint8_t *write, a, b, c;
251 const uint8_t *read = (const uint8_t *)data;
252 size_t blocks = (len / 3) + !!extra, alloclen;
253
254 GIT_ERROR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
255 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
256 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
257
258 ENSURE_SIZE(buf, alloclen);
259 write = (uint8_t *)&buf->ptr[buf->size];
260
261 /* convert each run of 3 bytes into 4 output bytes */
262 for (len -= extra; len > 0; len -= 3) {
263 a = *read++;
264 b = *read++;
265 c = *read++;
266
267 *write++ = base64_encode[a >> 2];
268 *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
269 *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
270 *write++ = base64_encode[c & 0x3f];
271 }
272
273 if (extra > 0) {
274 a = *read++;
275 b = (extra > 1) ? *read++ : 0;
276
277 *write++ = base64_encode[a >> 2];
278 *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
279 *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
280 *write++ = '=';
281 }
282
283 buf->size = ((char *)write) - buf->ptr;
284 buf->ptr[buf->size] = '\0';
285
286 return 0;
287 }
288
289 /* The inverse of base64_encode */
290 static const int8_t base64_decode[] = {
291 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
292 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
293 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
294 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
295 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
296 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
297 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
298 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
299 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
300 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
301 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
302 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
303 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
304 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
305 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
306 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
307 };
308
git_buf_decode_base64(git_buf * buf,const char * base64,size_t len)309 int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
310 {
311 size_t i;
312 int8_t a, b, c, d;
313 size_t orig_size = buf->size, new_size;
314
315 if (len % 4) {
316 git_error_set(GIT_ERROR_INVALID, "invalid base64 input");
317 return -1;
318 }
319
320 assert(len % 4 == 0);
321 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
322 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
323 ENSURE_SIZE(buf, new_size);
324
325 for (i = 0; i < len; i += 4) {
326 if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
327 (b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
328 (c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
329 (d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
330 buf->size = orig_size;
331 buf->ptr[buf->size] = '\0';
332
333 git_error_set(GIT_ERROR_INVALID, "invalid base64 input");
334 return -1;
335 }
336
337 buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
338 buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
339 buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
340 }
341
342 buf->ptr[buf->size] = '\0';
343 return 0;
344 }
345
346 static const char base85_encode[] =
347 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
348
git_buf_encode_base85(git_buf * buf,const char * data,size_t len)349 int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
350 {
351 size_t blocks = (len / 4) + !!(len % 4), alloclen;
352
353 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
354 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
355 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
356
357 ENSURE_SIZE(buf, alloclen);
358
359 while (len) {
360 uint32_t acc = 0;
361 char b85[5];
362 int i;
363
364 for (i = 24; i >= 0; i -= 8) {
365 uint8_t ch = *data++;
366 acc |= ch << i;
367
368 if (--len == 0)
369 break;
370 }
371
372 for (i = 4; i >= 0; i--) {
373 int val = acc % 85;
374 acc /= 85;
375
376 b85[i] = base85_encode[val];
377 }
378
379 for (i = 0; i < 5; i++)
380 buf->ptr[buf->size++] = b85[i];
381 }
382
383 buf->ptr[buf->size] = '\0';
384
385 return 0;
386 }
387
388 /* The inverse of base85_encode */
389 static const int8_t base85_decode[] = {
390 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
391 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
392 -1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1,
393 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 73, 74, 75, 76, 77,
394 78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
395 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80,
396 81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
397 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1,
398 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
399 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
400 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
401 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
402 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
403 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
404 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
405 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
406 };
407
git_buf_decode_base85(git_buf * buf,const char * base85,size_t base85_len,size_t output_len)408 int git_buf_decode_base85(
409 git_buf *buf,
410 const char *base85,
411 size_t base85_len,
412 size_t output_len)
413 {
414 size_t orig_size = buf->size, new_size;
415
416 if (base85_len % 5 ||
417 output_len > base85_len * 4 / 5) {
418 git_error_set(GIT_ERROR_INVALID, "invalid base85 input");
419 return -1;
420 }
421
422 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size);
423 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
424 ENSURE_SIZE(buf, new_size);
425
426 while (output_len) {
427 unsigned acc = 0;
428 int de, cnt = 4;
429 unsigned char ch;
430 do {
431 ch = *base85++;
432 de = base85_decode[ch];
433 if (--de < 0)
434 goto on_error;
435
436 acc = acc * 85 + de;
437 } while (--cnt);
438 ch = *base85++;
439 de = base85_decode[ch];
440 if (--de < 0)
441 goto on_error;
442
443 /* Detect overflow. */
444 if (0xffffffff / 85 < acc ||
445 0xffffffff - de < (acc *= 85))
446 goto on_error;
447
448 acc += de;
449
450 cnt = (output_len < 4) ? (int)output_len : 4;
451 output_len -= cnt;
452 do {
453 acc = (acc << 8) | (acc >> 24);
454 buf->ptr[buf->size++] = acc;
455 } while (--cnt);
456 }
457
458 buf->ptr[buf->size] = 0;
459
460 return 0;
461
462 on_error:
463 buf->size = orig_size;
464 buf->ptr[buf->size] = '\0';
465
466 git_error_set(GIT_ERROR_INVALID, "invalid base85 input");
467 return -1;
468 }
469
470 #define HEX_DECODE(c) ((c | 32) % 39 - 9)
471
git_buf_decode_percent(git_buf * buf,const char * str,size_t str_len)472 int git_buf_decode_percent(
473 git_buf *buf,
474 const char *str,
475 size_t str_len)
476 {
477 size_t str_pos, new_size;
478
479 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, str_len);
480 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
481 ENSURE_SIZE(buf, new_size);
482
483 for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) {
484 if (str[str_pos] == '%' &&
485 str_len > str_pos + 2 &&
486 isxdigit(str[str_pos + 1]) &&
487 isxdigit(str[str_pos + 2])) {
488 buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) +
489 HEX_DECODE(str[str_pos + 2]);
490 str_pos += 2;
491 } else {
492 buf->ptr[buf->size] = str[str_pos];
493 }
494 }
495
496 buf->ptr[buf->size] = '\0';
497 return 0;
498 }
499
git_buf_vprintf(git_buf * buf,const char * format,va_list ap)500 int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
501 {
502 size_t expected_size, new_size;
503 int len;
504
505 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
506 GIT_ERROR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
507 ENSURE_SIZE(buf, expected_size);
508
509 while (1) {
510 va_list args;
511 va_copy(args, ap);
512
513 len = p_vsnprintf(
514 buf->ptr + buf->size,
515 buf->asize - buf->size,
516 format, args
517 );
518
519 va_end(args);
520
521 if (len < 0) {
522 git__free(buf->ptr);
523 buf->ptr = git_buf__oom;
524 return -1;
525 }
526
527 if ((size_t)len + 1 <= buf->asize - buf->size) {
528 buf->size += len;
529 break;
530 }
531
532 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
533 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
534 ENSURE_SIZE(buf, new_size);
535 }
536
537 return 0;
538 }
539
git_buf_printf(git_buf * buf,const char * format,...)540 int git_buf_printf(git_buf *buf, const char *format, ...)
541 {
542 int r;
543 va_list ap;
544
545 va_start(ap, format);
546 r = git_buf_vprintf(buf, format, ap);
547 va_end(ap);
548
549 return r;
550 }
551
git_buf_copy_cstr(char * data,size_t datasize,const git_buf * buf)552 void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
553 {
554 size_t copylen;
555
556 assert(data && datasize && buf);
557
558 data[0] = '\0';
559
560 if (buf->size == 0 || buf->asize <= 0)
561 return;
562
563 copylen = buf->size;
564 if (copylen > datasize - 1)
565 copylen = datasize - 1;
566 memmove(data, buf->ptr, copylen);
567 data[copylen] = '\0';
568 }
569
git_buf_consume_bytes(git_buf * buf,size_t len)570 void git_buf_consume_bytes(git_buf *buf, size_t len)
571 {
572 git_buf_consume(buf, buf->ptr + len);
573 }
574
git_buf_consume(git_buf * buf,const char * end)575 void git_buf_consume(git_buf *buf, const char *end)
576 {
577 if (end > buf->ptr && end <= buf->ptr + buf->size) {
578 size_t consumed = end - buf->ptr;
579 memmove(buf->ptr, end, buf->size - consumed);
580 buf->size -= consumed;
581 buf->ptr[buf->size] = '\0';
582 }
583 }
584
git_buf_truncate(git_buf * buf,size_t len)585 void git_buf_truncate(git_buf *buf, size_t len)
586 {
587 if (len >= buf->size)
588 return;
589
590 buf->size = len;
591 if (buf->size < buf->asize)
592 buf->ptr[buf->size] = '\0';
593 }
594
git_buf_shorten(git_buf * buf,size_t amount)595 void git_buf_shorten(git_buf *buf, size_t amount)
596 {
597 if (buf->size > amount)
598 git_buf_truncate(buf, buf->size - amount);
599 else
600 git_buf_clear(buf);
601 }
602
git_buf_rtruncate_at_char(git_buf * buf,char separator)603 void git_buf_rtruncate_at_char(git_buf *buf, char separator)
604 {
605 ssize_t idx = git_buf_rfind_next(buf, separator);
606 git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
607 }
608
git_buf_swap(git_buf * buf_a,git_buf * buf_b)609 void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
610 {
611 git_buf t = *buf_a;
612 *buf_a = *buf_b;
613 *buf_b = t;
614 }
615
git_buf_detach(git_buf * buf)616 char *git_buf_detach(git_buf *buf)
617 {
618 char *data = buf->ptr;
619
620 if (buf->asize == 0 || buf->ptr == git_buf__oom)
621 return NULL;
622
623 git_buf_init(buf, 0);
624
625 return data;
626 }
627
git_buf_attach(git_buf * buf,char * ptr,size_t asize)628 int git_buf_attach(git_buf *buf, char *ptr, size_t asize)
629 {
630 git_buf_dispose(buf);
631
632 if (ptr) {
633 buf->ptr = ptr;
634 buf->size = strlen(ptr);
635 if (asize)
636 buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
637 else /* pass 0 to fall back on strlen + 1 */
638 buf->asize = buf->size + 1;
639 }
640
641 ENSURE_SIZE(buf, asize);
642 return 0;
643 }
644
git_buf_attach_notowned(git_buf * buf,const char * ptr,size_t size)645 void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
646 {
647 if (git_buf_is_allocated(buf))
648 git_buf_dispose(buf);
649
650 if (!size) {
651 git_buf_init(buf, 0);
652 } else {
653 buf->ptr = (char *)ptr;
654 buf->asize = 0;
655 buf->size = size;
656 }
657 }
658
git_buf_join_n(git_buf * buf,char separator,int nbuf,...)659 int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
660 {
661 va_list ap;
662 int i;
663 size_t total_size = 0, original_size = buf->size;
664 char *out, *original = buf->ptr;
665
666 if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
667 ++total_size; /* space for initial separator */
668
669 /* Make two passes to avoid multiple reallocation */
670
671 va_start(ap, nbuf);
672 for (i = 0; i < nbuf; ++i) {
673 const char* segment;
674 size_t segment_len;
675
676 segment = va_arg(ap, const char *);
677 if (!segment)
678 continue;
679
680 segment_len = strlen(segment);
681
682 GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
683
684 if (segment_len == 0 || segment[segment_len - 1] != separator)
685 GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
686 }
687 va_end(ap);
688
689 /* expand buffer if needed */
690 if (total_size == 0)
691 return 0;
692
693 GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
694 if (git_buf_grow_by(buf, total_size) < 0)
695 return -1;
696
697 out = buf->ptr + buf->size;
698
699 /* append separator to existing buf if needed */
700 if (buf->size > 0 && out[-1] != separator)
701 *out++ = separator;
702
703 va_start(ap, nbuf);
704 for (i = 0; i < nbuf; ++i) {
705 const char* segment;
706 size_t segment_len;
707
708 segment = va_arg(ap, const char *);
709 if (!segment)
710 continue;
711
712 /* deal with join that references buffer's original content */
713 if (segment >= original && segment < original + original_size) {
714 size_t offset = (segment - original);
715 segment = buf->ptr + offset;
716 segment_len = original_size - offset;
717 } else {
718 segment_len = strlen(segment);
719 }
720
721 /* skip leading separators */
722 if (out > buf->ptr && out[-1] == separator)
723 while (segment_len > 0 && *segment == separator) {
724 segment++;
725 segment_len--;
726 }
727
728 /* copy over next buffer */
729 if (segment_len > 0) {
730 memmove(out, segment, segment_len);
731 out += segment_len;
732 }
733
734 /* append trailing separator (except for last item) */
735 if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
736 *out++ = separator;
737 }
738 va_end(ap);
739
740 /* set size based on num characters actually written */
741 buf->size = out - buf->ptr;
742 buf->ptr[buf->size] = '\0';
743
744 return 0;
745 }
746
git_buf_join(git_buf * buf,char separator,const char * str_a,const char * str_b)747 int git_buf_join(
748 git_buf *buf,
749 char separator,
750 const char *str_a,
751 const char *str_b)
752 {
753 size_t strlen_a = str_a ? strlen(str_a) : 0;
754 size_t strlen_b = strlen(str_b);
755 size_t alloc_len;
756 int need_sep = 0;
757 ssize_t offset_a = -1;
758
759 /* not safe to have str_b point internally to the buffer */
760 assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
761
762 /* figure out if we need to insert a separator */
763 if (separator && strlen_a) {
764 while (*str_b == separator) { str_b++; strlen_b--; }
765 if (str_a[strlen_a - 1] != separator)
766 need_sep = 1;
767 }
768
769 /* str_a could be part of the buffer */
770 if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
771 offset_a = str_a - buf->ptr;
772
773 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
774 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
775 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
776 ENSURE_SIZE(buf, alloc_len);
777
778 /* fix up internal pointers */
779 if (offset_a >= 0)
780 str_a = buf->ptr + offset_a;
781
782 /* do the actual copying */
783 if (offset_a != 0 && str_a)
784 memmove(buf->ptr, str_a, strlen_a);
785 if (need_sep)
786 buf->ptr[strlen_a] = separator;
787 memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
788
789 buf->size = strlen_a + strlen_b + need_sep;
790 buf->ptr[buf->size] = '\0';
791
792 return 0;
793 }
794
git_buf_join3(git_buf * buf,char separator,const char * str_a,const char * str_b,const char * str_c)795 int git_buf_join3(
796 git_buf *buf,
797 char separator,
798 const char *str_a,
799 const char *str_b,
800 const char *str_c)
801 {
802 size_t len_a = strlen(str_a),
803 len_b = strlen(str_b),
804 len_c = strlen(str_c),
805 len_total;
806 int sep_a = 0, sep_b = 0;
807 char *tgt;
808
809 /* for this function, disallow pointers into the existing buffer */
810 assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
811 assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
812 assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
813
814 if (separator) {
815 if (len_a > 0) {
816 while (*str_b == separator) { str_b++; len_b--; }
817 sep_a = (str_a[len_a - 1] != separator);
818 }
819 if (len_a > 0 || len_b > 0)
820 while (*str_c == separator) { str_c++; len_c--; }
821 if (len_b > 0)
822 sep_b = (str_b[len_b - 1] != separator);
823 }
824
825 GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
826 GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
827 GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
828 GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
829 GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
830 ENSURE_SIZE(buf, len_total);
831
832 tgt = buf->ptr;
833
834 if (len_a) {
835 memcpy(tgt, str_a, len_a);
836 tgt += len_a;
837 }
838 if (sep_a)
839 *tgt++ = separator;
840 if (len_b) {
841 memcpy(tgt, str_b, len_b);
842 tgt += len_b;
843 }
844 if (sep_b)
845 *tgt++ = separator;
846 if (len_c)
847 memcpy(tgt, str_c, len_c);
848
849 buf->size = len_a + sep_a + len_b + sep_b + len_c;
850 buf->ptr[buf->size] = '\0';
851
852 return 0;
853 }
854
git_buf_rtrim(git_buf * buf)855 void git_buf_rtrim(git_buf *buf)
856 {
857 while (buf->size > 0) {
858 if (!git__isspace(buf->ptr[buf->size - 1]))
859 break;
860
861 buf->size--;
862 }
863
864 if (buf->asize > buf->size)
865 buf->ptr[buf->size] = '\0';
866 }
867
git_buf_cmp(const git_buf * a,const git_buf * b)868 int git_buf_cmp(const git_buf *a, const git_buf *b)
869 {
870 int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
871 return (result != 0) ? result :
872 (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
873 }
874
git_buf_splice(git_buf * buf,size_t where,size_t nb_to_remove,const char * data,size_t nb_to_insert)875 int git_buf_splice(
876 git_buf *buf,
877 size_t where,
878 size_t nb_to_remove,
879 const char *data,
880 size_t nb_to_insert)
881 {
882 char *splice_loc;
883 size_t new_size, alloc_size;
884
885 assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
886
887 splice_loc = buf->ptr + where;
888
889 /* Ported from git.git
890 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
891 */
892 GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
893 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
894 ENSURE_SIZE(buf, alloc_size);
895
896 memmove(splice_loc + nb_to_insert,
897 splice_loc + nb_to_remove,
898 buf->size - where - nb_to_remove);
899
900 memcpy(splice_loc, data, nb_to_insert);
901
902 buf->size = new_size;
903 buf->ptr[buf->size] = '\0';
904 return 0;
905 }
906
907 /* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
git_buf_quote(git_buf * buf)908 int git_buf_quote(git_buf *buf)
909 {
910 const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
911 git_buf quoted = GIT_BUF_INIT;
912 size_t i = 0;
913 bool quote = false;
914 int error = 0;
915
916 /* walk to the first char that needs quoting */
917 if (buf->size && buf->ptr[0] == '!')
918 quote = true;
919
920 for (i = 0; !quote && i < buf->size; i++) {
921 if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
922 buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
923 quote = true;
924 break;
925 }
926 }
927
928 if (!quote)
929 goto done;
930
931 git_buf_putc("ed, '"');
932 git_buf_put("ed, buf->ptr, i);
933
934 for (; i < buf->size; i++) {
935 /* whitespace - use the map above, which is ordered by ascii value */
936 if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
937 git_buf_putc("ed, '\\');
938 git_buf_putc("ed, whitespace[buf->ptr[i] - '\a']);
939 }
940
941 /* double quote and backslash must be escaped */
942 else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
943 git_buf_putc("ed, '\\');
944 git_buf_putc("ed, buf->ptr[i]);
945 }
946
947 /* escape anything unprintable as octal */
948 else if (buf->ptr[i] != ' ' &&
949 (buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
950 git_buf_printf("ed, "\\%03o", (unsigned char)buf->ptr[i]);
951 }
952
953 /* yay, printable! */
954 else {
955 git_buf_putc("ed, buf->ptr[i]);
956 }
957 }
958
959 git_buf_putc("ed, '"');
960
961 if (git_buf_oom("ed)) {
962 error = -1;
963 goto done;
964 }
965
966 git_buf_swap("ed, buf);
967
968 done:
969 git_buf_dispose("ed);
970 return error;
971 }
972
973 /* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
git_buf_unquote(git_buf * buf)974 int git_buf_unquote(git_buf *buf)
975 {
976 size_t i, j;
977 char ch;
978
979 git_buf_rtrim(buf);
980
981 if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
982 goto invalid;
983
984 for (i = 0, j = 1; j < buf->size-1; i++, j++) {
985 ch = buf->ptr[j];
986
987 if (ch == '\\') {
988 if (j == buf->size-2)
989 goto invalid;
990
991 ch = buf->ptr[++j];
992
993 switch (ch) {
994 /* \" or \\ simply copy the char in */
995 case '"': case '\\':
996 break;
997
998 /* add the appropriate escaped char */
999 case 'a': ch = '\a'; break;
1000 case 'b': ch = '\b'; break;
1001 case 'f': ch = '\f'; break;
1002 case 'n': ch = '\n'; break;
1003 case 'r': ch = '\r'; break;
1004 case 't': ch = '\t'; break;
1005 case 'v': ch = '\v'; break;
1006
1007 /* \xyz digits convert to the char*/
1008 case '0': case '1': case '2': case '3':
1009 if (j == buf->size-3) {
1010 git_error_set(GIT_ERROR_INVALID,
1011 "truncated quoted character \\%c", ch);
1012 return -1;
1013 }
1014
1015 if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
1016 buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
1017 git_error_set(GIT_ERROR_INVALID,
1018 "truncated quoted character \\%c%c%c",
1019 buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
1020 return -1;
1021 }
1022
1023 ch = ((buf->ptr[j] - '0') << 6) |
1024 ((buf->ptr[j+1] - '0') << 3) |
1025 (buf->ptr[j+2] - '0');
1026 j += 2;
1027 break;
1028
1029 default:
1030 git_error_set(GIT_ERROR_INVALID, "invalid quoted character \\%c", ch);
1031 return -1;
1032 }
1033 }
1034
1035 buf->ptr[i] = ch;
1036 }
1037
1038 buf->ptr[i] = '\0';
1039 buf->size = i;
1040
1041 return 0;
1042
1043 invalid:
1044 git_error_set(GIT_ERROR_INVALID, "invalid quoted line");
1045 return -1;
1046 }
1047