File Coverage

deps/libgit2/src/buffer.c
Criterion Covered Total %
statement 272 538 50.5
branch 215 598 35.9
condition n/a
subroutine n/a
pod n/a
total 487 1136 42.8


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