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
git_buf_init(git_buf * buf,size_t initial_size)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
git_buf_try_grow(git_buf * buf,size_t target_size,bool mark_oom)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
git_buf_grow(git_buf * buffer,size_t target_size)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
git_buf_grow_by(git_buf * buffer,size_t additional_size)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
git_buf_free(git_buf * buf)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
git_buf_sanitize(git_buf * buf)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
git_buf_clear(git_buf * buf)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
git_buf_set(git_buf * buf,const void * data,size_t len)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
git_buf_is_binary(const git_buf * buf)172 int git_buf_is_binary(const git_buf *buf)
173 {
174 return git_buf_text_is_binary(buf);
175 }
176
git_buf_contains_nul(const git_buf * buf)177 int git_buf_contains_nul(const git_buf *buf)
178 {
179 return git_buf_text_contains_nul(buf);
180 }
181
git_buf_sets(git_buf * buf,const char * string)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
git_buf_putc(git_buf * buf,char c)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
git_buf_putcn(git_buf * buf,char c,size_t len)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
git_buf_put(git_buf * buf,const char * data,size_t len)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
git_buf_puts(git_buf * buf,const char * string)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
git_buf_encode_base64(git_buf * buf,const char * data,size_t len)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
git_buf_decode_base64(git_buf * buf,const char * base64,size_t len)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
git_buf_encode_base85(git_buf * buf,const char * data,size_t len)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
git_buf_decode_base85(git_buf * buf,const char * base85,size_t base85_len,size_t output_len)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 #define HEX_DECODE(c) ((c | 32) % 39 - 9)
459
git_buf_decode_percent(git_buf * buf,const char * str,size_t str_len)460 int git_buf_decode_percent(
461 git_buf *buf,
462 const char *str,
463 size_t str_len)
464 {
465 size_t str_pos, new_size;
466
467 GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, str_len);
468 GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
469 ENSURE_SIZE(buf, new_size);
470
471 for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) {
472 if (str[str_pos] == '%' &&
473 str_len > str_pos + 2 &&
474 isxdigit(str[str_pos + 1]) &&
475 isxdigit(str[str_pos + 2])) {
476 buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) +
477 HEX_DECODE(str[str_pos + 2]);
478 str_pos += 2;
479 } else {
480 buf->ptr[buf->size] = str[str_pos];
481 }
482 }
483
484 buf->ptr[buf->size] = '\0';
485 return 0;
486 }
487
git_buf_vprintf(git_buf * buf,const char * format,va_list ap)488 int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
489 {
490 size_t expected_size, new_size;
491 int len;
492
493 GITERR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
494 GITERR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
495 ENSURE_SIZE(buf, expected_size);
496
497 while (1) {
498 va_list args;
499 va_copy(args, ap);
500
501 len = p_vsnprintf(
502 buf->ptr + buf->size,
503 buf->asize - buf->size,
504 format, args
505 );
506
507 va_end(args);
508
509 if (len < 0) {
510 git__free(buf->ptr);
511 buf->ptr = git_buf__oom;
512 return -1;
513 }
514
515 if ((size_t)len + 1 <= buf->asize - buf->size) {
516 buf->size += len;
517 break;
518 }
519
520 GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
521 GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
522 ENSURE_SIZE(buf, new_size);
523 }
524
525 return 0;
526 }
527
git_buf_printf(git_buf * buf,const char * format,...)528 int git_buf_printf(git_buf *buf, const char *format, ...)
529 {
530 int r;
531 va_list ap;
532
533 va_start(ap, format);
534 r = git_buf_vprintf(buf, format, ap);
535 va_end(ap);
536
537 return r;
538 }
539
git_buf_copy_cstr(char * data,size_t datasize,const git_buf * buf)540 void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
541 {
542 size_t copylen;
543
544 assert(data && datasize && buf);
545
546 data[0] = '\0';
547
548 if (buf->size == 0 || buf->asize <= 0)
549 return;
550
551 copylen = buf->size;
552 if (copylen > datasize - 1)
553 copylen = datasize - 1;
554 memmove(data, buf->ptr, copylen);
555 data[copylen] = '\0';
556 }
557
git_buf_consume(git_buf * buf,const char * end)558 void git_buf_consume(git_buf *buf, const char *end)
559 {
560 if (end > buf->ptr && end <= buf->ptr + buf->size) {
561 size_t consumed = end - buf->ptr;
562 memmove(buf->ptr, end, buf->size - consumed);
563 buf->size -= consumed;
564 buf->ptr[buf->size] = '\0';
565 }
566 }
567
git_buf_truncate(git_buf * buf,size_t len)568 void git_buf_truncate(git_buf *buf, size_t len)
569 {
570 if (len >= buf->size)
571 return;
572
573 buf->size = len;
574 if (buf->size < buf->asize)
575 buf->ptr[buf->size] = '\0';
576 }
577
git_buf_shorten(git_buf * buf,size_t amount)578 void git_buf_shorten(git_buf *buf, size_t amount)
579 {
580 if (buf->size > amount)
581 git_buf_truncate(buf, buf->size - amount);
582 else
583 git_buf_clear(buf);
584 }
585
git_buf_rtruncate_at_char(git_buf * buf,char separator)586 void git_buf_rtruncate_at_char(git_buf *buf, char separator)
587 {
588 ssize_t idx = git_buf_rfind_next(buf, separator);
589 git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
590 }
591
git_buf_swap(git_buf * buf_a,git_buf * buf_b)592 void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
593 {
594 git_buf t = *buf_a;
595 *buf_a = *buf_b;
596 *buf_b = t;
597 }
598
git_buf_detach(git_buf * buf)599 char *git_buf_detach(git_buf *buf)
600 {
601 char *data = buf->ptr;
602
603 if (buf->asize == 0 || buf->ptr == git_buf__oom)
604 return NULL;
605
606 git_buf_init(buf, 0);
607
608 return data;
609 }
610
git_buf_attach(git_buf * buf,char * ptr,size_t asize)611 int git_buf_attach(git_buf *buf, char *ptr, size_t asize)
612 {
613 git_buf_free(buf);
614
615 if (ptr) {
616 buf->ptr = ptr;
617 buf->size = strlen(ptr);
618 if (asize)
619 buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
620 else /* pass 0 to fall back on strlen + 1 */
621 buf->asize = buf->size + 1;
622 }
623
624 ENSURE_SIZE(buf, asize);
625 return 0;
626 }
627
git_buf_attach_notowned(git_buf * buf,const char * ptr,size_t size)628 void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
629 {
630 if (git_buf_is_allocated(buf))
631 git_buf_free(buf);
632
633 if (!size) {
634 git_buf_init(buf, 0);
635 } else {
636 buf->ptr = (char *)ptr;
637 buf->asize = 0;
638 buf->size = size;
639 }
640 }
641
git_buf_join_n(git_buf * buf,char separator,int nbuf,...)642 int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
643 {
644 va_list ap;
645 int i;
646 size_t total_size = 0, original_size = buf->size;
647 char *out, *original = buf->ptr;
648
649 if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
650 ++total_size; /* space for initial separator */
651
652 /* Make two passes to avoid multiple reallocation */
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 segment_len = strlen(segment);
664
665 GITERR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
666
667 if (segment_len == 0 || segment[segment_len - 1] != separator)
668 GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
669 }
670 va_end(ap);
671
672 /* expand buffer if needed */
673 if (total_size == 0)
674 return 0;
675
676 GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
677 if (git_buf_grow_by(buf, total_size) < 0)
678 return -1;
679
680 out = buf->ptr + buf->size;
681
682 /* append separator to existing buf if needed */
683 if (buf->size > 0 && out[-1] != separator)
684 *out++ = separator;
685
686 va_start(ap, nbuf);
687 for (i = 0; i < nbuf; ++i) {
688 const char* segment;
689 size_t segment_len;
690
691 segment = va_arg(ap, const char *);
692 if (!segment)
693 continue;
694
695 /* deal with join that references buffer's original content */
696 if (segment >= original && segment < original + original_size) {
697 size_t offset = (segment - original);
698 segment = buf->ptr + offset;
699 segment_len = original_size - offset;
700 } else {
701 segment_len = strlen(segment);
702 }
703
704 /* skip leading separators */
705 if (out > buf->ptr && out[-1] == separator)
706 while (segment_len > 0 && *segment == separator) {
707 segment++;
708 segment_len--;
709 }
710
711 /* copy over next buffer */
712 if (segment_len > 0) {
713 memmove(out, segment, segment_len);
714 out += segment_len;
715 }
716
717 /* append trailing separator (except for last item) */
718 if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
719 *out++ = separator;
720 }
721 va_end(ap);
722
723 /* set size based on num characters actually written */
724 buf->size = out - buf->ptr;
725 buf->ptr[buf->size] = '\0';
726
727 return 0;
728 }
729
git_buf_join(git_buf * buf,char separator,const char * str_a,const char * str_b)730 int git_buf_join(
731 git_buf *buf,
732 char separator,
733 const char *str_a,
734 const char *str_b)
735 {
736 size_t strlen_a = str_a ? strlen(str_a) : 0;
737 size_t strlen_b = strlen(str_b);
738 size_t alloc_len;
739 int need_sep = 0;
740 ssize_t offset_a = -1;
741
742 /* not safe to have str_b point internally to the buffer */
743 assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
744
745 /* figure out if we need to insert a separator */
746 if (separator && strlen_a) {
747 while (*str_b == separator) { str_b++; strlen_b--; }
748 if (str_a[strlen_a - 1] != separator)
749 need_sep = 1;
750 }
751
752 /* str_a could be part of the buffer */
753 if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
754 offset_a = str_a - buf->ptr;
755
756 GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
757 GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
758 GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
759 ENSURE_SIZE(buf, alloc_len);
760
761 /* fix up internal pointers */
762 if (offset_a >= 0)
763 str_a = buf->ptr + offset_a;
764
765 /* do the actual copying */
766 if (offset_a != 0 && str_a)
767 memmove(buf->ptr, str_a, strlen_a);
768 if (need_sep)
769 buf->ptr[strlen_a] = separator;
770 memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
771
772 buf->size = strlen_a + strlen_b + need_sep;
773 buf->ptr[buf->size] = '\0';
774
775 return 0;
776 }
777
git_buf_join3(git_buf * buf,char separator,const char * str_a,const char * str_b,const char * str_c)778 int git_buf_join3(
779 git_buf *buf,
780 char separator,
781 const char *str_a,
782 const char *str_b,
783 const char *str_c)
784 {
785 size_t len_a = strlen(str_a),
786 len_b = strlen(str_b),
787 len_c = strlen(str_c),
788 len_total;
789 int sep_a = 0, sep_b = 0;
790 char *tgt;
791
792 /* for this function, disallow pointers into the existing buffer */
793 assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
794 assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
795 assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
796
797 if (separator) {
798 if (len_a > 0) {
799 while (*str_b == separator) { str_b++; len_b--; }
800 sep_a = (str_a[len_a - 1] != separator);
801 }
802 if (len_a > 0 || len_b > 0)
803 while (*str_c == separator) { str_c++; len_c--; }
804 if (len_b > 0)
805 sep_b = (str_b[len_b - 1] != separator);
806 }
807
808 GITERR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
809 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
810 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
811 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
812 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
813 ENSURE_SIZE(buf, len_total);
814
815 tgt = buf->ptr;
816
817 if (len_a) {
818 memcpy(tgt, str_a, len_a);
819 tgt += len_a;
820 }
821 if (sep_a)
822 *tgt++ = separator;
823 if (len_b) {
824 memcpy(tgt, str_b, len_b);
825 tgt += len_b;
826 }
827 if (sep_b)
828 *tgt++ = separator;
829 if (len_c)
830 memcpy(tgt, str_c, len_c);
831
832 buf->size = len_a + sep_a + len_b + sep_b + len_c;
833 buf->ptr[buf->size] = '\0';
834
835 return 0;
836 }
837
git_buf_rtrim(git_buf * buf)838 void git_buf_rtrim(git_buf *buf)
839 {
840 while (buf->size > 0) {
841 if (!git__isspace(buf->ptr[buf->size - 1]))
842 break;
843
844 buf->size--;
845 }
846
847 if (buf->asize > buf->size)
848 buf->ptr[buf->size] = '\0';
849 }
850
git_buf_cmp(const git_buf * a,const git_buf * b)851 int git_buf_cmp(const git_buf *a, const git_buf *b)
852 {
853 int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
854 return (result != 0) ? result :
855 (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
856 }
857
git_buf_splice(git_buf * buf,size_t where,size_t nb_to_remove,const char * data,size_t nb_to_insert)858 int git_buf_splice(
859 git_buf *buf,
860 size_t where,
861 size_t nb_to_remove,
862 const char *data,
863 size_t nb_to_insert)
864 {
865 char *splice_loc;
866 size_t new_size, alloc_size;
867
868 assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
869
870 splice_loc = buf->ptr + where;
871
872 /* Ported from git.git
873 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
874 */
875 GITERR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
876 GITERR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
877 ENSURE_SIZE(buf, alloc_size);
878
879 memmove(splice_loc + nb_to_insert,
880 splice_loc + nb_to_remove,
881 buf->size - where - nb_to_remove);
882
883 memcpy(splice_loc, data, nb_to_insert);
884
885 buf->size = new_size;
886 buf->ptr[buf->size] = '\0';
887 return 0;
888 }
889
890 /* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
git_buf_quote(git_buf * buf)891 int git_buf_quote(git_buf *buf)
892 {
893 const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
894 git_buf quoted = GIT_BUF_INIT;
895 size_t i = 0;
896 bool quote = false;
897 int error = 0;
898
899 /* walk to the first char that needs quoting */
900 if (buf->size && buf->ptr[0] == '!')
901 quote = true;
902
903 for (i = 0; !quote && i < buf->size; i++) {
904 if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
905 buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
906 quote = true;
907 break;
908 }
909 }
910
911 if (!quote)
912 goto done;
913
914 git_buf_putc("ed, '"');
915 git_buf_put("ed, buf->ptr, i);
916
917 for (; i < buf->size; i++) {
918 /* whitespace - use the map above, which is ordered by ascii value */
919 if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
920 git_buf_putc("ed, '\\');
921 git_buf_putc("ed, whitespace[buf->ptr[i] - '\a']);
922 }
923
924 /* double quote and backslash must be escaped */
925 else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
926 git_buf_putc("ed, '\\');
927 git_buf_putc("ed, buf->ptr[i]);
928 }
929
930 /* escape anything unprintable as octal */
931 else if (buf->ptr[i] != ' ' &&
932 (buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
933 git_buf_printf("ed, "\\%03o", (unsigned char)buf->ptr[i]);
934 }
935
936 /* yay, printable! */
937 else {
938 git_buf_putc("ed, buf->ptr[i]);
939 }
940 }
941
942 git_buf_putc("ed, '"');
943
944 if (git_buf_oom("ed)) {
945 error = -1;
946 goto done;
947 }
948
949 git_buf_swap("ed, buf);
950
951 done:
952 git_buf_free("ed);
953 return error;
954 }
955
956 /* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
git_buf_unquote(git_buf * buf)957 int git_buf_unquote(git_buf *buf)
958 {
959 size_t i, j;
960 char ch;
961
962 git_buf_rtrim(buf);
963
964 if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
965 goto invalid;
966
967 for (i = 0, j = 1; j < buf->size-1; i++, j++) {
968 ch = buf->ptr[j];
969
970 if (ch == '\\') {
971 if (j == buf->size-2)
972 goto invalid;
973
974 ch = buf->ptr[++j];
975
976 switch (ch) {
977 /* \" or \\ simply copy the char in */
978 case '"': case '\\':
979 break;
980
981 /* add the appropriate escaped char */
982 case 'a': ch = '\a'; break;
983 case 'b': ch = '\b'; break;
984 case 'f': ch = '\f'; break;
985 case 'n': ch = '\n'; break;
986 case 'r': ch = '\r'; break;
987 case 't': ch = '\t'; break;
988 case 'v': ch = '\v'; break;
989
990 /* \xyz digits convert to the char*/
991 case '0': case '1': case '2': case '3':
992 if (j == buf->size-3) {
993 giterr_set(GITERR_INVALID,
994 "truncated quoted character \\%c", ch);
995 return -1;
996 }
997
998 if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
999 buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
1000 giterr_set(GITERR_INVALID,
1001 "truncated quoted character \\%c%c%c",
1002 buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
1003 return -1;
1004 }
1005
1006 ch = ((buf->ptr[j] - '0') << 6) |
1007 ((buf->ptr[j+1] - '0') << 3) |
1008 (buf->ptr[j+2] - '0');
1009 j += 2;
1010 break;
1011
1012 default:
1013 giterr_set(GITERR_INVALID, "invalid quoted character \\%c", ch);
1014 return -1;
1015 }
1016 }
1017
1018 buf->ptr[i] = ch;
1019 }
1020
1021 buf->ptr[i] = '\0';
1022 buf->size = i;
1023
1024 return 0;
1025
1026 invalid:
1027 giterr_set(GITERR_INVALID, "invalid quoted line");
1028 return -1;
1029 }
1030