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