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