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