File Coverage

deps/libgit2/src/util/str.c
Criterion Covered Total %
statement 406 716 56.7
branch 285 746 38.2
condition n/a
subroutine n/a
pod n/a
total 691 1462 47.2


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              
8             #include "str.h"
9             #include "posix.h"
10             #include
11              
12             /* Used as default value for git_str->ptr so that people can always
13             * assume ptr is non-NULL and zero terminated even for new git_strs.
14             */
15             char git_str__initstr[1];
16              
17             char git_str__oom[1];
18              
19             #define ENSURE_SIZE(b, d) \
20             if ((b)->ptr == git_str__oom || \
21             ((d) > (b)->asize && git_str_grow((b), (d)) < 0))\
22             return -1;
23              
24              
25 36746           int git_str_init(git_str *buf, size_t initial_size)
26             {
27 36746           buf->asize = 0;
28 36746           buf->size = 0;
29 36746           buf->ptr = git_str__initstr;
30              
31 36746 50         ENSURE_SIZE(buf, initial_size);
    100          
    50          
32              
33 36746           return 0;
34             }
35              
36 30601           int git_str_try_grow(
37             git_str *buf, size_t target_size, bool mark_oom)
38             {
39             char *new_ptr;
40             size_t new_size;
41              
42 30601 50         if (buf->ptr == git_str__oom)
43 0           return -1;
44              
45 30601 100         if (buf->asize == 0 && buf->size != 0) {
    50          
46 0           git_error_set(GIT_ERROR_INVALID, "cannot grow a borrowed buffer");
47 0           return GIT_EINVALID;
48             }
49              
50 30601 100         if (!target_size)
51 2           target_size = buf->size;
52              
53 30601 100         if (target_size <= buf->asize)
54 646           return 0;
55              
56 29955 100         if (buf->asize == 0) {
57 23790           new_size = target_size;
58 23790           new_ptr = NULL;
59             } else {
60 6165           new_size = buf->asize;
61             /*
62             * Grow the allocated buffer by 1.5 to allow
63             * re-use of memory holes resulting from the
64             * realloc. If this is still too small, then just
65             * use the target size.
66             */
67 6165 100         if ((new_size = (new_size << 1) - (new_size >> 1)) < target_size)
68 1084           new_size = target_size;
69 6165           new_ptr = buf->ptr;
70             }
71              
72             /* round allocation up to multiple of 8 */
73 29955           new_size = (new_size + 7) & ~7;
74              
75 29955 50         if (new_size < buf->size) {
76 0 0         if (mark_oom) {
77 0 0         if (buf->ptr && buf->ptr != git_str__initstr)
    0          
78 0           git__free(buf->ptr);
79 0           buf->ptr = git_str__oom;
80             }
81              
82 0           git_error_set_oom();
83 0           return -1;
84             }
85              
86 29955           new_ptr = git__realloc(new_ptr, new_size);
87              
88 29955 50         if (!new_ptr) {
89 0 0         if (mark_oom) {
90 0 0         if (buf->ptr && (buf->ptr != git_str__initstr))
    0          
91 0           git__free(buf->ptr);
92 0           buf->ptr = git_str__oom;
93             }
94 0           return -1;
95             }
96              
97 29955           buf->asize = new_size;
98 29955           buf->ptr = new_ptr;
99              
100             /* truncate the existing buffer size if necessary */
101 29955 50         if (buf->size >= buf->asize)
102 0           buf->size = buf->asize - 1;
103 29955           buf->ptr[buf->size] = '\0';
104              
105 29955           return 0;
106             }
107              
108 29808           int git_str_grow(git_str *buffer, size_t target_size)
109             {
110 29808           return git_str_try_grow(buffer, target_size, true);
111             }
112              
113 205           int git_str_grow_by(git_str *buffer, size_t additional_size)
114             {
115             size_t newsize;
116              
117 205 50         if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
    50          
118 0           buffer->ptr = git_str__oom;
119 0           return -1;
120             }
121              
122 205           return git_str_try_grow(buffer, newsize, true);
123             }
124              
125 32783           void git_str_dispose(git_str *buf)
126             {
127 32783 100         if (!buf) return;
128              
129 32199 100         if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_str__oom)
    50          
    50          
130 20377           git__free(buf->ptr);
131              
132 32199           git_str_init(buf, 0);
133             }
134              
135 13736           void git_str_clear(git_str *buf)
136             {
137 13736           buf->size = 0;
138              
139 13736 100         if (!buf->ptr) {
140 613           buf->ptr = git_str__initstr;
141 613           buf->asize = 0;
142             }
143              
144 13736 100         if (buf->asize > 0)
145 8224           buf->ptr[0] = '\0';
146 13736           }
147              
148 7286           int git_str_set(git_str *buf, const void *data, size_t len)
149             {
150             size_t alloclen;
151              
152 7286 100         if (len == 0 || data == NULL) {
    50          
153 11           git_str_clear(buf);
154             } else {
155 7275 100         if (data != buf->ptr) {
156 6127 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
    50          
157 6127 50         ENSURE_SIZE(buf, alloclen);
    100          
    50          
158 6127           memmove(buf->ptr, data, len);
159             }
160              
161 7275           buf->size = len;
162 7275 50         if (buf->asize > buf->size)
163 7275           buf->ptr[buf->size] = '\0';
164              
165             }
166 7286           return 0;
167             }
168              
169 2941           int git_str_sets(git_str *buf, const char *string)
170             {
171 2941 50         return git_str_set(buf, string, string ? strlen(string) : 0);
172             }
173              
174 22350           int git_str_putc(git_str *buf, char c)
175             {
176             size_t new_size;
177 22350 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
    50          
178 22350 50         ENSURE_SIZE(buf, new_size);
    100          
    50          
179 22350           buf->ptr[buf->size++] = c;
180 22350           buf->ptr[buf->size] = '\0';
181 22350           return 0;
182             }
183              
184 16           int git_str_putcn(git_str *buf, char c, size_t len)
185             {
186             size_t new_size;
187 16 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
    50          
188 16 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
    50          
189 16 50         ENSURE_SIZE(buf, new_size);
    100          
    50          
190 16           memset(buf->ptr + buf->size, c, len);
191 16           buf->size += len;
192 16           buf->ptr[buf->size] = '\0';
193 16           return 0;
194             }
195              
196 13914           int git_str_put(git_str *buf, const char *data, size_t len)
197             {
198 13914 100         if (len) {
199             size_t new_size;
200              
201 13277 50         GIT_ASSERT_ARG(data);
202              
203 13277 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
    50          
204 13277 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
    50          
205 13277 50         ENSURE_SIZE(buf, new_size);
    100          
    50          
206 13277           memmove(buf->ptr + buf->size, data, len);
207 13277           buf->size += len;
208 13277           buf->ptr[buf->size] = '\0';
209             }
210 13914           return 0;
211             }
212              
213 7846           int git_str_puts(git_str *buf, const char *string)
214             {
215 7846 50         GIT_ASSERT_ARG(string);
216              
217 7846           return git_str_put(buf, string, strlen(string));
218             }
219              
220             static char hex_encode[] = "0123456789abcdef";
221              
222 187           int git_str_encode_hexstr(git_str *str, const char *data, size_t len)
223             {
224             size_t new_size, i;
225             char *s;
226              
227 187 50         GIT_ERROR_CHECK_ALLOC_MULTIPLY(&new_size, len, 2);
    50          
228 187 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
    50          
229              
230 187 50         if (git_str_grow_by(str, new_size) < 0)
231 0           return -1;
232              
233 187           s = str->ptr + str->size;
234              
235 1683 100         for (i = 0; i < len; i++) {
236 1496           *s++ = hex_encode[(data[i] & 0xf0) >> 4];
237 1496           *s++ = hex_encode[(data[i] & 0x0f)];
238             }
239              
240 187           str->size += (len * 2);
241 187           str->ptr[str->size] = '\0';
242              
243 187           return 0;
244             }
245              
246             static const char base64_encode[] =
247             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
248              
249 0           int git_str_encode_base64(git_str *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_str_decode_base64(git_str *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         GIT_ASSERT_ARG(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_str_encode_base85(git_str *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_str_decode_base85(
411             git_str *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_str_decode_percent(
475             git_str *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 5673           int git_str_vprintf(git_str *buf, const char *format, va_list ap)
503             {
504             size_t expected_size, new_size;
505             int len;
506              
507 5673 50         GIT_ERROR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
    50          
508 5673 50         GIT_ERROR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
    50          
509 5673 50         ENSURE_SIZE(buf, expected_size);
    100          
    50          
510              
511             while (1) {
512             va_list args;
513 5954           va_copy(args, ap);
514              
515 5954           len = p_vsnprintf(
516             buf->ptr + buf->size,
517             buf->asize - buf->size,
518             format, args
519             );
520              
521 5954           va_end(args);
522              
523 5954 50         if (len < 0) {
524 0           git__free(buf->ptr);
525 0           buf->ptr = git_str__oom;
526 5673           return -1;
527             }
528              
529 5954 100         if ((size_t)len + 1 <= buf->asize - buf->size) {
530 5673           buf->size += len;
531 5673           break;
532             }
533              
534 281 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
    50          
535 281 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
    50          
536 281 50         ENSURE_SIZE(buf, new_size);
    50          
    50          
537 281           }
538              
539 5673           return 0;
540             }
541              
542 1395           int git_str_printf(git_str *buf, const char *format, ...)
543             {
544             int r;
545             va_list ap;
546              
547 1395           va_start(ap, format);
548 1395           r = git_str_vprintf(buf, format, ap);
549 1395           va_end(ap);
550              
551 1395           return r;
552             }
553              
554 1104           int git_str_copy_cstr(char *data, size_t datasize, const git_str *buf)
555             {
556             size_t copylen;
557              
558 1104 50         GIT_ASSERT_ARG(data);
559 1104 50         GIT_ASSERT_ARG(datasize);
560 1104 50         GIT_ASSERT_ARG(buf);
561              
562 1104           data[0] = '\0';
563              
564 1104 50         if (buf->size == 0 || buf->asize <= 0)
    50          
565 0           return 0;
566              
567 1104           copylen = buf->size;
568 1104 50         if (copylen > datasize - 1)
569 0           copylen = datasize - 1;
570 1104           memmove(data, buf->ptr, copylen);
571 1104           data[copylen] = '\0';
572              
573 1104           return 0;
574             }
575              
576 0           void git_str_consume_bytes(git_str *buf, size_t len)
577             {
578 0           git_str_consume(buf, buf->ptr + len);
579 0           }
580              
581 0           void git_str_consume(git_str *buf, const char *end)
582             {
583 0 0         if (end > buf->ptr && end <= buf->ptr + buf->size) {
    0          
584 0           size_t consumed = end - buf->ptr;
585 0           memmove(buf->ptr, end, buf->size - consumed);
586 0           buf->size -= consumed;
587 0           buf->ptr[buf->size] = '\0';
588             }
589 0           }
590              
591 4905           void git_str_truncate(git_str *buf, size_t len)
592             {
593 4905 100         if (len >= buf->size)
594 1634           return;
595              
596 3271           buf->size = len;
597 3271 50         if (buf->size < buf->asize)
598 3271           buf->ptr[buf->size] = '\0';
599             }
600              
601 5           void git_str_shorten(git_str *buf, size_t amount)
602             {
603 5 50         if (buf->size > amount)
604 5           git_str_truncate(buf, buf->size - amount);
605             else
606 0           git_str_clear(buf);
607 5           }
608              
609 0           void git_str_truncate_at_char(git_str *buf, char separator)
610             {
611 0           ssize_t idx = git_str_find(buf, separator);
612 0 0         if (idx >= 0)
613 0           git_str_truncate(buf, (size_t)idx);
614 0           }
615              
616 176           void git_str_rtruncate_at_char(git_str *buf, char separator)
617             {
618 176           ssize_t idx = git_str_rfind_next(buf, separator);
619 176           git_str_truncate(buf, idx < 0 ? 0 : (size_t)idx);
620 176           }
621              
622 2398           void git_str_swap(git_str *str_a, git_str *str_b)
623             {
624 2398           git_str t = *str_a;
625 2398           *str_a = *str_b;
626 2398           *str_b = t;
627 2398           }
628              
629 2992           char *git_str_detach(git_str *buf)
630             {
631 2992           char *data = buf->ptr;
632              
633 2992 50         if (buf->asize == 0 || buf->ptr == git_str__oom)
    50          
634 0           return NULL;
635              
636 2992           git_str_init(buf, 0);
637              
638 2992           return data;
639             }
640              
641 65           int git_str_attach(git_str *buf, char *ptr, size_t asize)
642             {
643 65           git_str_dispose(buf);
644              
645 65 100         if (ptr) {
646 7           buf->ptr = ptr;
647 7           buf->size = strlen(ptr);
648 7 50         if (asize)
649 0 0         buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
650             else /* pass 0 to fall back on strlen + 1 */
651 7           buf->asize = buf->size + 1;
652             }
653              
654 65 50         ENSURE_SIZE(buf, asize);
    50          
    0          
655 65           return 0;
656             }
657              
658 107           void git_str_attach_notowned(git_str *buf, const char *ptr, size_t size)
659             {
660 107 50         if (git_str_is_allocated(buf))
661 0           git_str_dispose(buf);
662              
663 107 100         if (!size) {
664 29           git_str_init(buf, 0);
665             } else {
666 78           buf->ptr = (char *)ptr;
667 78           buf->asize = 0;
668 78           buf->size = size;
669             }
670 107           }
671              
672 0           int git_str_join_n(git_str *buf, char separator, int nbuf, ...)
673             {
674             va_list ap;
675             int i;
676 0           size_t total_size = 0, original_size = buf->size;
677 0           char *out, *original = buf->ptr;
678              
679 0 0         if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
    0          
680 0           ++total_size; /* space for initial separator */
681              
682             /* Make two passes to avoid multiple reallocation */
683              
684 0           va_start(ap, nbuf);
685 0 0         for (i = 0; i < nbuf; ++i) {
686             const char *segment;
687             size_t segment_len;
688              
689 0 0         segment = va_arg(ap, const char *);
690 0 0         if (!segment)
691 0           continue;
692              
693 0           segment_len = strlen(segment);
694              
695 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
    0          
696              
697 0 0         if (segment_len == 0 || segment[segment_len - 1] != separator)
    0          
698 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
    0          
699             }
700 0           va_end(ap);
701              
702             /* expand buffer if needed */
703 0 0         if (total_size == 0)
704 0           return 0;
705              
706 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
    0          
707 0 0         if (git_str_grow_by(buf, total_size) < 0)
708 0           return -1;
709              
710 0           out = buf->ptr + buf->size;
711              
712             /* append separator to existing buf if needed */
713 0 0         if (buf->size > 0 && out[-1] != separator)
    0          
714 0           *out++ = separator;
715              
716 0           va_start(ap, nbuf);
717 0 0         for (i = 0; i < nbuf; ++i) {
718             const char *segment;
719             size_t segment_len;
720              
721 0 0         segment = va_arg(ap, const char *);
722 0 0         if (!segment)
723 0           continue;
724              
725             /* deal with join that references buffer's original content */
726 0 0         if (segment >= original && segment < original + original_size) {
    0          
727 0           size_t offset = (segment - original);
728 0           segment = buf->ptr + offset;
729 0           segment_len = original_size - offset;
730             } else {
731 0           segment_len = strlen(segment);
732             }
733              
734             /* skip leading separators */
735 0 0         if (out > buf->ptr && out[-1] == separator)
    0          
736 0 0         while (segment_len > 0 && *segment == separator) {
    0          
737 0           segment++;
738 0           segment_len--;
739             }
740              
741             /* copy over next buffer */
742 0 0         if (segment_len > 0) {
743 0           memmove(out, segment, segment_len);
744 0           out += segment_len;
745             }
746              
747             /* append trailing separator (except for last item) */
748 0 0         if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
    0          
    0          
749 0           *out++ = separator;
750             }
751 0           va_end(ap);
752              
753             /* set size based on num characters actually written */
754 0           buf->size = out - buf->ptr;
755 0           buf->ptr[buf->size] = '\0';
756              
757 0           return 0;
758             }
759              
760 16352           int git_str_join(
761             git_str *buf,
762             char separator,
763             const char *str_a,
764             const char *str_b)
765             {
766 16352 100         size_t strlen_a = str_a ? strlen(str_a) : 0;
767 16352           size_t strlen_b = strlen(str_b);
768             size_t alloc_len;
769 16352           int need_sep = 0;
770 16352           ssize_t offset_a = -1;
771              
772             /* not safe to have str_b point internally to the buffer */
773 16352 100         if (buf->size)
774 5636 100         GIT_ASSERT_ARG(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
    50          
775              
776             /* figure out if we need to insert a separator */
777 16352 50         if (separator && strlen_a) {
    100          
778 15233 100         while (*str_b == separator) { str_b++; strlen_b--; }
779 15228 100         if (str_a[strlen_a - 1] != separator)
780 3087           need_sep = 1;
781             }
782              
783             /* str_a could be part of the buffer */
784 16352 100         if (buf->size && str_a >= buf->ptr && str_a < buf->ptr + buf->size)
    100          
    100          
785 3580           offset_a = str_a - buf->ptr;
786              
787 16352 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
    50          
788 16352 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
    50          
789 16352 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
    50          
790 16352 50         ENSURE_SIZE(buf, alloc_len);
    100          
    50          
791              
792             /* fix up internal pointers */
793 16352 100         if (offset_a >= 0)
794 3580           str_a = buf->ptr + offset_a;
795              
796             /* do the actual copying */
797 16352 100         if (offset_a != 0 && str_a)
    100          
798 12771           memmove(buf->ptr, str_a, strlen_a);
799 16352 100         if (need_sep)
800 3087           buf->ptr[strlen_a] = separator;
801 16352           memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
802              
803 16352           buf->size = strlen_a + strlen_b + need_sep;
804 16352           buf->ptr[buf->size] = '\0';
805              
806 16352           return 0;
807             }
808              
809 14           int git_str_join3(
810             git_str *buf,
811             char separator,
812             const char *str_a,
813             const char *str_b,
814             const char *str_c)
815             {
816 14           size_t len_a = strlen(str_a),
817 14           len_b = strlen(str_b),
818 14           len_c = strlen(str_c),
819             len_total;
820 14           int sep_a = 0, sep_b = 0;
821             char *tgt;
822              
823             /* for this function, disallow pointers into the existing buffer */
824 14 50         GIT_ASSERT(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
    0          
825 14 50         GIT_ASSERT(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
    0          
826 14 50         GIT_ASSERT(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
    0          
827              
828 14 50         if (separator) {
829 14 50         if (len_a > 0) {
830 14 50         while (*str_b == separator) { str_b++; len_b--; }
831 14           sep_a = (str_a[len_a - 1] != separator);
832             }
833 14 50         if (len_a > 0 || len_b > 0)
    0          
834 14 50         while (*str_c == separator) { str_c++; len_c--; }
835 14 100         if (len_b > 0)
836 10           sep_b = (str_b[len_b - 1] != separator);
837             }
838              
839 14 50         GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
    50          
840 14 50         GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
    50          
841 14 50         GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
    50          
842 14 50         GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
    50          
843 14 50         GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
    50          
844 14 50         ENSURE_SIZE(buf, len_total);
    50          
    50          
845              
846 14           tgt = buf->ptr;
847              
848 14 50         if (len_a) {
849 14           memcpy(tgt, str_a, len_a);
850 14           tgt += len_a;
851             }
852 14 50         if (sep_a)
853 0           *tgt++ = separator;
854 14 100         if (len_b) {
855 10           memcpy(tgt, str_b, len_b);
856 10           tgt += len_b;
857             }
858 14 100         if (sep_b)
859 7           *tgt++ = separator;
860 14 50         if (len_c)
861 14           memcpy(tgt, str_c, len_c);
862              
863 14           buf->size = len_a + sep_a + len_b + sep_b + len_c;
864 14           buf->ptr[buf->size] = '\0';
865              
866 14           return 0;
867             }
868              
869 766           void git_str_rtrim(git_str *buf)
870             {
871 1410 50         while (buf->size > 0) {
872 1410 100         if (!git__isspace(buf->ptr[buf->size - 1]))
873 766           break;
874              
875 644           buf->size--;
876             }
877              
878 766 50         if (buf->asize > buf->size)
879 766           buf->ptr[buf->size] = '\0';
880 766           }
881              
882 0           int git_str_cmp(const git_str *a, const git_str *b)
883             {
884 0           int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
885 0 0         return (result != 0) ? result :
886 0 0         (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
887             }
888              
889 0           int git_str_splice(
890             git_str *buf,
891             size_t where,
892             size_t nb_to_remove,
893             const char *data,
894             size_t nb_to_insert)
895             {
896             char *splice_loc;
897             size_t new_size, alloc_size;
898              
899 0 0         GIT_ASSERT(buf);
900 0 0         GIT_ASSERT(where <= buf->size);
901 0 0         GIT_ASSERT(nb_to_remove <= buf->size - where);
902              
903 0           splice_loc = buf->ptr + where;
904              
905             /* Ported from git.git
906             * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
907             */
908 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
    0          
909 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
    0          
910 0 0         ENSURE_SIZE(buf, alloc_size);
    0          
    0          
911              
912 0           memmove(splice_loc + nb_to_insert,
913             splice_loc + nb_to_remove,
914 0           buf->size - where - nb_to_remove);
915              
916 0           memcpy(splice_loc, data, nb_to_insert);
917              
918 0           buf->size = new_size;
919 0           buf->ptr[buf->size] = '\0';
920 0           return 0;
921             }
922              
923             /* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
924 50           int git_str_quote(git_str *buf)
925             {
926 50           const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
927 50           git_str quoted = GIT_STR_INIT;
928 50           size_t i = 0;
929 50           bool quote = false;
930 50           int error = 0;
931              
932             /* walk to the first char that needs quoting */
933 50 50         if (buf->size && buf->ptr[0] == '!')
    50          
934 0           quote = true;
935              
936 526 50         for (i = 0; !quote && i < buf->size; i++) {
    100          
937 476 50         if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
    50          
    50          
938 476 50         buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
939 0           quote = true;
940 0           break;
941             }
942             }
943              
944 50 50         if (!quote)
945 50           goto done;
946              
947 0           git_str_putc("ed, '"');
948 0           git_str_put("ed, buf->ptr, i);
949              
950 0 0         for (; i < buf->size; i++) {
951             /* whitespace - use the map above, which is ordered by ascii value */
952 0 0         if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
    0          
953 0           git_str_putc("ed, '\\');
954 0           git_str_putc("ed, whitespace[buf->ptr[i] - '\a']);
955             }
956              
957             /* double quote and backslash must be escaped */
958 0 0         else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
    0          
959 0           git_str_putc("ed, '\\');
960 0           git_str_putc("ed, buf->ptr[i]);
961             }
962              
963             /* escape anything unprintable as octal */
964 0 0         else if (buf->ptr[i] != ' ' &&
    0          
965 0 0         (buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
966 0           git_str_printf("ed, "\\%03o", (unsigned char)buf->ptr[i]);
967             }
968              
969             /* yay, printable! */
970             else {
971 0           git_str_putc("ed, buf->ptr[i]);
972             }
973             }
974              
975 0           git_str_putc("ed, '"');
976              
977 0 0         if (git_str_oom("ed)) {
978 0           error = -1;
979 0           goto done;
980             }
981              
982 0           git_str_swap("ed, buf);
983              
984             done:
985 50           git_str_dispose("ed);
986 50           return error;
987             }
988              
989             /* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
990 0           int git_str_unquote(git_str *buf)
991             {
992             size_t i, j;
993             char ch;
994              
995 0           git_str_rtrim(buf);
996              
997 0 0         if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
    0          
    0          
998             goto invalid;
999              
1000 0 0         for (i = 0, j = 1; j < buf->size-1; i++, j++) {
1001 0           ch = buf->ptr[j];
1002              
1003 0 0         if (ch == '\\') {
1004 0 0         if (j == buf->size-2)
1005 0           goto invalid;
1006              
1007 0           ch = buf->ptr[++j];
1008              
1009 0           switch (ch) {
1010             /* \" or \\ simply copy the char in */
1011             case '"': case '\\':
1012 0           break;
1013              
1014             /* add the appropriate escaped char */
1015 0           case 'a': ch = '\a'; break;
1016 0           case 'b': ch = '\b'; break;
1017 0           case 'f': ch = '\f'; break;
1018 0           case 'n': ch = '\n'; break;
1019 0           case 'r': ch = '\r'; break;
1020 0           case 't': ch = '\t'; break;
1021 0           case 'v': ch = '\v'; break;
1022              
1023             /* \xyz digits convert to the char*/
1024             case '0': case '1': case '2': case '3':
1025 0 0         if (j == buf->size-3) {
1026 0           git_error_set(GIT_ERROR_INVALID,
1027             "truncated quoted character \\%c", ch);
1028 0           return -1;
1029             }
1030              
1031 0 0         if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
    0          
    0          
1032 0 0         buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
1033 0           git_error_set(GIT_ERROR_INVALID,
1034             "truncated quoted character \\%c%c%c",
1035 0           buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
1036 0           return -1;
1037             }
1038              
1039 0           ch = ((buf->ptr[j] - '0') << 6) |
1040 0           ((buf->ptr[j+1] - '0') << 3) |
1041 0           (buf->ptr[j+2] - '0');
1042 0           j += 2;
1043 0           break;
1044              
1045             default:
1046 0           git_error_set(GIT_ERROR_INVALID, "invalid quoted character \\%c", ch);
1047 0           return -1;
1048             }
1049             }
1050              
1051 0           buf->ptr[i] = ch;
1052             }
1053              
1054 0           buf->ptr[i] = '\0';
1055 0           buf->size = i;
1056              
1057 0           return 0;
1058              
1059             invalid:
1060 0           git_error_set(GIT_ERROR_INVALID, "invalid quoted line");
1061 0           return -1;
1062             }
1063              
1064 3           int git_str_puts_escaped(
1065             git_str *buf,
1066             const char *string,
1067             const char *esc_chars,
1068             const char *esc_with)
1069             {
1070             const char *scan;
1071 3           size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
1072              
1073 3 50         if (!string)
1074 0           return 0;
1075              
1076 9 100         for (scan = string; *scan; ) {
1077             /* count run of non-escaped characters */
1078 6           count = strcspn(scan, esc_chars);
1079 6           total += count;
1080 6           scan += count;
1081             /* count run of escaped characters */
1082 6           count = strspn(scan, esc_chars);
1083 6           total += count * (esc_len + 1);
1084 6           scan += count;
1085             }
1086              
1087 3 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, total, 1);
    50          
1088 3 50         if (git_str_grow_by(buf, alloclen) < 0)
1089 0           return -1;
1090              
1091 9 100         for (scan = string; *scan; ) {
1092 6           count = strcspn(scan, esc_chars);
1093              
1094 6           memmove(buf->ptr + buf->size, scan, count);
1095 6           scan += count;
1096 6           buf->size += count;
1097              
1098 9 100         for (count = strspn(scan, esc_chars); count > 0; --count) {
1099             /* copy escape sequence */
1100 3           memmove(buf->ptr + buf->size, esc_with, esc_len);
1101 3           buf->size += esc_len;
1102             /* copy character to be escaped */
1103 3           buf->ptr[buf->size] = *scan;
1104 3           buf->size++;
1105 3           scan++;
1106             }
1107             }
1108              
1109 3           buf->ptr[buf->size] = '\0';
1110              
1111 3           return 0;
1112             }
1113              
1114 32           void git_str_unescape(git_str *buf)
1115             {
1116 32           buf->size = git__unescape(buf->ptr);
1117 32           }
1118              
1119 3           int git_str_crlf_to_lf(git_str *tgt, const git_str *src)
1120             {
1121 3           const char *scan = src->ptr;
1122 3           const char *scan_end = src->ptr + src->size;
1123 3           const char *next = memchr(scan, '\r', src->size);
1124             size_t new_size;
1125             char *out;
1126              
1127 3 50         GIT_ASSERT(tgt != src);
1128              
1129 3 50         if (!next)
1130 0           return git_str_set(tgt, src->ptr, src->size);
1131              
1132             /* reduce reallocs while in the loop */
1133 3 50         GIT_ERROR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
    50          
1134 3 50         if (git_str_grow(tgt, new_size) < 0)
1135 0           return -1;
1136              
1137 3           out = tgt->ptr;
1138 3           tgt->size = 0;
1139              
1140             /* Find the next \r and copy whole chunk up to there to tgt */
1141 6 100         for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
1142 3 50         if (next > scan) {
1143 3           size_t copylen = (size_t)(next - scan);
1144 3           memcpy(out, scan, copylen);
1145 3           out += copylen;
1146             }
1147              
1148             /* Do not drop \r unless it is followed by \n */
1149 3 50         if (next + 1 == scan_end || next[1] != '\n')
    50          
1150 0           *out++ = '\r';
1151             }
1152              
1153             /* Copy remaining input into dest */
1154 3 50         if (scan < scan_end) {
1155 3           size_t remaining = (size_t)(scan_end - scan);
1156 3           memcpy(out, scan, remaining);
1157 3           out += remaining;
1158             }
1159              
1160 3           tgt->size = (size_t)(out - tgt->ptr);
1161 3           tgt->ptr[tgt->size] = '\0';
1162              
1163 3           return 0;
1164             }
1165              
1166 3           int git_str_lf_to_crlf(git_str *tgt, const git_str *src)
1167             {
1168 3           const char *start = src->ptr;
1169 3           const char *end = start + src->size;
1170 3           const char *scan = start;
1171 3           const char *next = memchr(scan, '\n', src->size);
1172             size_t alloclen;
1173              
1174 3 50         GIT_ASSERT(tgt != src);
1175              
1176 3 50         if (!next)
1177 0           return git_str_set(tgt, src->ptr, src->size);
1178              
1179             /* attempt to reduce reallocs while in the loop */
1180 3 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
    50          
1181 3 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
    50          
1182 3 50         if (git_str_grow(tgt, alloclen) < 0)
1183 0           return -1;
1184 3           tgt->size = 0;
1185              
1186 6 100         for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
1187 3           size_t copylen = next - scan;
1188              
1189             /* if we find mixed line endings, carry on */
1190 3 50         if (copylen && next[-1] == '\r')
    50          
1191 0           copylen--;
1192              
1193 3 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
    50          
1194 3 50         if (git_str_grow_by(tgt, alloclen) < 0)
1195 0           return -1;
1196              
1197 3 50         if (copylen) {
1198 3           memcpy(tgt->ptr + tgt->size, scan, copylen);
1199 3           tgt->size += copylen;
1200             }
1201              
1202 3           tgt->ptr[tgt->size++] = '\r';
1203 3           tgt->ptr[tgt->size++] = '\n';
1204             }
1205              
1206 3           tgt->ptr[tgt->size] = '\0';
1207 3           return git_str_put(tgt, scan, end - scan);
1208             }
1209              
1210 36           int git_str_common_prefix(git_str *buf, char *const *const strings, size_t count)
1211             {
1212             size_t i;
1213             const char *str, *pfx;
1214              
1215 36           git_str_clear(buf);
1216              
1217 36 50         if (!strings || !count)
    50          
1218 0           return 0;
1219              
1220             /* initialize common prefix to first string */
1221 36 50         if (git_str_sets(buf, strings[0]) < 0)
1222 0           return -1;
1223              
1224             /* go through the rest of the strings, truncating to shared prefix */
1225 36 100         for (i = 1; i < count; ++i) {
1226              
1227 3 50         for (str = strings[i], pfx = buf->ptr;
1228 3 50         *str && *str == *pfx;
1229 0           str++, pfx++)
1230             /* scanning */;
1231              
1232 3           git_str_truncate(buf, pfx - buf->ptr);
1233              
1234 3 50         if (!buf->size)
1235 3           break;
1236             }
1237              
1238 36           return 0;
1239             }
1240              
1241 10           int git_str_is_binary(const git_str *buf)
1242             {
1243 10           const char *scan = buf->ptr, *end = buf->ptr + buf->size;
1244             git_str_bom_t bom;
1245 10           int printable = 0, nonprintable = 0;
1246              
1247 10           scan += git_str_detect_bom(&bom, buf);
1248              
1249 10 50         if (bom > GIT_STR_BOM_UTF8)
1250 0           return 1;
1251              
1252 289 100         while (scan < end) {
1253 279           unsigned char c = *scan++;
1254              
1255             /* Printable characters are those above SPACE (0x1F) excluding DEL,
1256             * and including BS, ESC and FF.
1257             */
1258 279 100         if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014')
    50          
    50          
    50          
    50          
1259 276           printable++;
1260 3 50         else if (c == '\0')
1261 0           return true;
1262 3 50         else if (!git__isspace(c))
1263 0           nonprintable++;
1264             }
1265              
1266 10           return ((printable >> 7) < nonprintable);
1267             }
1268              
1269 60           int git_str_contains_nul(const git_str *buf)
1270             {
1271 60           return (memchr(buf->ptr, '\0', buf->size) != NULL);
1272             }
1273              
1274 2750           int git_str_detect_bom(git_str_bom_t *bom, const git_str *buf)
1275             {
1276             const char *ptr;
1277             size_t len;
1278              
1279 2750           *bom = GIT_STR_BOM_NONE;
1280             /* need at least 2 bytes to look for any BOM */
1281 2750 100         if (buf->size < 2)
1282 2419           return 0;
1283              
1284 331           ptr = buf->ptr;
1285 331           len = buf->size;
1286              
1287 331           switch (*ptr++) {
1288             case 0:
1289 0 0         if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
    0          
    0          
    0          
1290 0           *bom = GIT_STR_BOM_UTF32_BE;
1291 0           return 4;
1292             }
1293 0           break;
1294             case '\xEF':
1295 0 0         if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
    0          
    0          
1296 0           *bom = GIT_STR_BOM_UTF8;
1297 0           return 3;
1298             }
1299 0           break;
1300             case '\xFE':
1301 0 0         if (*ptr == '\xFF') {
1302 0           *bom = GIT_STR_BOM_UTF16_BE;
1303 0           return 2;
1304             }
1305 0           break;
1306             case '\xFF':
1307 0 0         if (*ptr != '\xFE')
1308 0           break;
1309 0 0         if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
    0          
    0          
1310 0           *bom = GIT_STR_BOM_UTF32_LE;
1311 0           return 4;
1312             } else {
1313 0           *bom = GIT_STR_BOM_UTF16_LE;
1314 0           return 2;
1315             }
1316             break;
1317             default:
1318 331           break;
1319             }
1320              
1321 331           return 0;
1322             }
1323              
1324 63           bool git_str_gather_text_stats(
1325             git_str_text_stats *stats, const git_str *buf, bool skip_bom)
1326             {
1327 63           const char *scan = buf->ptr, *end = buf->ptr + buf->size;
1328             int skip;
1329              
1330 63           memset(stats, 0, sizeof(*stats));
1331              
1332             /* BOM detection */
1333 63           skip = git_str_detect_bom(&stats->bom, buf);
1334 63 50         if (skip_bom)
1335 0           scan += skip;
1336              
1337             /* Ignore EOF character */
1338 63 50         if (buf->size > 0 && end[-1] == '\032')
    50          
1339 0           end--;
1340              
1341             /* Counting loop */
1342 2003 100         while (scan < end) {
1343 1940           unsigned char c = *scan++;
1344              
1345 1940 100         if (c > 0x1F && c != 0x7F)
    50          
1346 1889           stats->printable++;
1347 51           else switch (c) {
1348             case '\0':
1349 0           stats->nul++;
1350 0           stats->nonprintable++;
1351 0           break;
1352             case '\n':
1353 48           stats->lf++;
1354 48           break;
1355             case '\r':
1356 3           stats->cr++;
1357 3 50         if (scan < end && *scan == '\n')
    50          
1358 3           stats->crlf++;
1359 3           break;
1360             case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
1361 0           stats->printable++;
1362 0           break;
1363             default:
1364 0           stats->nonprintable++;
1365 0           break;
1366             }
1367             }
1368              
1369             /* Treat files with a bare CR as binary */
1370 63 50         return (stats->cr != stats->crlf || stats->nul > 0 ||
    50          
    50          
1371 63           ((stats->printable >> 7) < stats->nonprintable));
1372             }