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