File Coverage

deps/libgit2/src/buffer.c
Criterion Covered Total %
statement 278 537 51.7
branch 213 594 35.8
condition n/a
subroutine n/a
pod n/a
total 491 1131 43.4


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