]>
git.proxmox.com Git - libgit2.git/blob - src/str.c
0d405bfda500f404591b5b05780c2a70c5909269
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
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.
15 char git_str__initstr
[1];
19 #define ENSURE_SIZE(b, d) \
20 if ((b)->ptr == git_str__oom || \
21 ((d) > (b)->asize && git_str_grow((b), (d)) < 0))\
25 int git_str_init(git_str
*buf
, size_t initial_size
)
29 buf
->ptr
= git_str__initstr
;
31 ENSURE_SIZE(buf
, initial_size
);
37 git_str
*buf
, size_t target_size
, bool mark_oom
)
42 if (buf
->ptr
== git_str__oom
)
45 if (buf
->asize
== 0 && buf
->size
!= 0) {
46 git_error_set(GIT_ERROR_INVALID
, "cannot grow a borrowed buffer");
51 target_size
= buf
->size
;
53 if (target_size
<= buf
->asize
)
56 if (buf
->asize
== 0) {
57 new_size
= target_size
;
60 new_size
= buf
->asize
;
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.
67 if ((new_size
= (new_size
<< 1) - (new_size
>> 1)) < target_size
)
68 new_size
= target_size
;
72 /* round allocation up to multiple of 8 */
73 new_size
= (new_size
+ 7) & ~7;
75 if (new_size
< buf
->size
) {
77 if (buf
->ptr
&& buf
->ptr
!= git_str__initstr
)
79 buf
->ptr
= git_str__oom
;
86 new_ptr
= git__realloc(new_ptr
, new_size
);
90 if (buf
->ptr
&& (buf
->ptr
!= git_str__initstr
))
92 buf
->ptr
= git_str__oom
;
97 buf
->asize
= new_size
;
100 /* truncate the existing buffer size if necessary */
101 if (buf
->size
>= buf
->asize
)
102 buf
->size
= buf
->asize
- 1;
103 buf
->ptr
[buf
->size
] = '\0';
108 int git_str_grow(git_str
*buffer
, size_t target_size
)
110 return git_str_try_grow(buffer
, target_size
, true);
113 int git_str_grow_by(git_str
*buffer
, size_t additional_size
)
117 if (GIT_ADD_SIZET_OVERFLOW(&newsize
, buffer
->size
, additional_size
)) {
118 buffer
->ptr
= git_str__oom
;
122 return git_str_try_grow(buffer
, newsize
, true);
125 void git_str_dispose(git_str
*buf
)
129 if (buf
->asize
> 0 && buf
->ptr
!= NULL
&& buf
->ptr
!= git_str__oom
)
132 git_str_init(buf
, 0);
135 void git_str_clear(git_str
*buf
)
140 buf
->ptr
= git_str__initstr
;
148 int git_str_set(git_str
*buf
, const void *data
, size_t len
)
152 if (len
== 0 || data
== NULL
) {
155 if (data
!= buf
->ptr
) {
156 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, len
, 1);
157 ENSURE_SIZE(buf
, alloclen
);
158 memmove(buf
->ptr
, data
, len
);
162 if (buf
->asize
> buf
->size
)
163 buf
->ptr
[buf
->size
] = '\0';
169 int git_str_sets(git_str
*buf
, const char *string
)
171 return git_str_set(buf
, string
, string
? strlen(string
) : 0);
174 int git_str_putc(git_str
*buf
, char c
)
177 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, buf
->size
, 2);
178 ENSURE_SIZE(buf
, new_size
);
179 buf
->ptr
[buf
->size
++] = c
;
180 buf
->ptr
[buf
->size
] = '\0';
184 int git_str_putcn(git_str
*buf
, char c
, size_t len
)
187 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, buf
->size
, len
);
188 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, new_size
, 1);
189 ENSURE_SIZE(buf
, new_size
);
190 memset(buf
->ptr
+ buf
->size
, c
, len
);
192 buf
->ptr
[buf
->size
] = '\0';
196 int git_str_put(git_str
*buf
, const char *data
, size_t len
)
201 GIT_ASSERT_ARG(data
);
203 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, buf
->size
, len
);
204 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, new_size
, 1);
205 ENSURE_SIZE(buf
, new_size
);
206 memmove(buf
->ptr
+ buf
->size
, data
, len
);
208 buf
->ptr
[buf
->size
] = '\0';
213 int git_str_puts(git_str
*buf
, const char *string
)
215 GIT_ASSERT_ARG(string
);
217 return git_str_put(buf
, string
, strlen(string
));
220 static char hex_encode
[] = "0123456789abcdef";
222 int git_str_encode_hexstr(git_str
*str
, const char *data
, size_t len
)
227 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&new_size
, len
, 2);
228 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, new_size
, 1);
230 if (git_str_grow_by(str
, new_size
) < 0)
233 s
= str
->ptr
+ str
->size
;
235 for (i
= 0; i
< len
; i
++) {
236 *s
++ = hex_encode
[(data
[i
] & 0xf0) >> 4];
237 *s
++ = hex_encode
[(data
[i
] & 0x0f)];
240 str
->size
+= (len
* 2);
241 str
->ptr
[str
->size
] = '\0';
246 static const char base64_encode
[] =
247 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
249 int git_str_encode_base64(git_str
*buf
, const char *data
, size_t len
)
251 size_t extra
= len
% 3;
252 uint8_t *write
, a
, b
, c
;
253 const uint8_t *read
= (const uint8_t *)data
;
254 size_t blocks
= (len
/ 3) + !!extra
, alloclen
;
256 GIT_ERROR_CHECK_ALLOC_ADD(&blocks
, blocks
, 1);
257 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen
, blocks
, 4);
258 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, buf
->size
);
260 ENSURE_SIZE(buf
, alloclen
);
261 write
= (uint8_t *)&buf
->ptr
[buf
->size
];
263 /* convert each run of 3 bytes into 4 output bytes */
264 for (len
-= extra
; len
> 0; len
-= 3) {
269 *write
++ = base64_encode
[a
>> 2];
270 *write
++ = base64_encode
[(a
& 0x03) << 4 | b
>> 4];
271 *write
++ = base64_encode
[(b
& 0x0f) << 2 | c
>> 6];
272 *write
++ = base64_encode
[c
& 0x3f];
277 b
= (extra
> 1) ? *read
++ : 0;
279 *write
++ = base64_encode
[a
>> 2];
280 *write
++ = base64_encode
[(a
& 0x03) << 4 | b
>> 4];
281 *write
++ = (extra
> 1) ? base64_encode
[(b
& 0x0f) << 2] : '=';
285 buf
->size
= ((char *)write
) - buf
->ptr
;
286 buf
->ptr
[buf
->size
] = '\0';
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
311 int git_str_decode_base64(git_str
*buf
, const char *base64
, size_t len
)
315 size_t orig_size
= buf
->size
, new_size
;
318 git_error_set(GIT_ERROR_INVALID
, "invalid base64 input");
322 GIT_ASSERT_ARG(len
% 4 == 0);
323 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, (len
/ 4 * 3), buf
->size
);
324 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, new_size
, 1);
325 ENSURE_SIZE(buf
, new_size
);
327 for (i
= 0; i
< len
; i
+= 4) {
328 if ((a
= base64_decode
[(unsigned char)base64
[i
]]) < 0 ||
329 (b
= base64_decode
[(unsigned char)base64
[i
+1]]) < 0 ||
330 (c
= base64_decode
[(unsigned char)base64
[i
+2]]) < 0 ||
331 (d
= base64_decode
[(unsigned char)base64
[i
+3]]) < 0) {
332 buf
->size
= orig_size
;
333 buf
->ptr
[buf
->size
] = '\0';
335 git_error_set(GIT_ERROR_INVALID
, "invalid base64 input");
339 buf
->ptr
[buf
->size
++] = ((a
<< 2) | (b
& 0x30) >> 4);
340 buf
->ptr
[buf
->size
++] = ((b
& 0x0f) << 4) | ((c
& 0x3c) >> 2);
341 buf
->ptr
[buf
->size
++] = (c
& 0x03) << 6 | (d
& 0x3f);
344 buf
->ptr
[buf
->size
] = '\0';
348 static const char base85_encode
[] =
349 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
351 int git_str_encode_base85(git_str
*buf
, const char *data
, size_t len
)
353 size_t blocks
= (len
/ 4) + !!(len
% 4), alloclen
;
355 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen
, blocks
, 5);
356 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, buf
->size
);
357 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, 1);
359 ENSURE_SIZE(buf
, alloclen
);
366 for (i
= 24; i
>= 0; i
-= 8) {
367 uint8_t ch
= *data
++;
368 acc
|= (uint32_t)ch
<< i
;
374 for (i
= 4; i
>= 0; i
--) {
378 b85
[i
] = base85_encode
[val
];
381 for (i
= 0; i
< 5; i
++)
382 buf
->ptr
[buf
->size
++] = b85
[i
];
385 buf
->ptr
[buf
->size
] = '\0';
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
410 int git_str_decode_base85(
416 size_t orig_size
= buf
->size
, new_size
;
418 if (base85_len
% 5 ||
419 output_len
> base85_len
* 4 / 5) {
420 git_error_set(GIT_ERROR_INVALID
, "invalid base85 input");
424 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, output_len
, buf
->size
);
425 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, new_size
, 1);
426 ENSURE_SIZE(buf
, new_size
);
434 de
= base85_decode
[ch
];
441 de
= base85_decode
[ch
];
445 /* Detect overflow. */
446 if (0xffffffff / 85 < acc
||
447 0xffffffff - de
< (acc
*= 85))
452 cnt
= (output_len
< 4) ? (int)output_len
: 4;
455 acc
= (acc
<< 8) | (acc
>> 24);
456 buf
->ptr
[buf
->size
++] = acc
;
460 buf
->ptr
[buf
->size
] = 0;
465 buf
->size
= orig_size
;
466 buf
->ptr
[buf
->size
] = '\0';
468 git_error_set(GIT_ERROR_INVALID
, "invalid base85 input");
472 #define HEX_DECODE(c) ((c | 32) % 39 - 9)
474 int git_str_decode_percent(
479 size_t str_pos
, new_size
;
481 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, buf
->size
, str_len
);
482 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, new_size
, 1);
483 ENSURE_SIZE(buf
, new_size
);
485 for (str_pos
= 0; str_pos
< str_len
; buf
->size
++, str_pos
++) {
486 if (str
[str_pos
] == '%' &&
487 str_len
> str_pos
+ 2 &&
488 isxdigit(str
[str_pos
+ 1]) &&
489 isxdigit(str
[str_pos
+ 2])) {
490 buf
->ptr
[buf
->size
] = (HEX_DECODE(str
[str_pos
+ 1]) << 4) +
491 HEX_DECODE(str
[str_pos
+ 2]);
494 buf
->ptr
[buf
->size
] = str
[str_pos
];
498 buf
->ptr
[buf
->size
] = '\0';
502 int git_str_vprintf(git_str
*buf
, const char *format
, va_list ap
)
504 size_t expected_size
, new_size
;
507 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&expected_size
, strlen(format
), 2);
508 GIT_ERROR_CHECK_ALLOC_ADD(&expected_size
, expected_size
, buf
->size
);
509 ENSURE_SIZE(buf
, expected_size
);
516 buf
->ptr
+ buf
->size
,
517 buf
->asize
- buf
->size
,
525 buf
->ptr
= git_str__oom
;
529 if ((size_t)len
+ 1 <= buf
->asize
- buf
->size
) {
534 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, buf
->size
, len
);
535 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, new_size
, 1);
536 ENSURE_SIZE(buf
, new_size
);
542 int git_str_printf(git_str
*buf
, const char *format
, ...)
547 va_start(ap
, format
);
548 r
= git_str_vprintf(buf
, format
, ap
);
554 int git_str_copy_cstr(char *data
, size_t datasize
, const git_str
*buf
)
558 GIT_ASSERT_ARG(data
);
559 GIT_ASSERT_ARG(datasize
);
564 if (buf
->size
== 0 || buf
->asize
<= 0)
568 if (copylen
> datasize
- 1)
569 copylen
= datasize
- 1;
570 memmove(data
, buf
->ptr
, copylen
);
571 data
[copylen
] = '\0';
576 void git_str_consume_bytes(git_str
*buf
, size_t len
)
578 git_str_consume(buf
, buf
->ptr
+ len
);
581 void git_str_consume(git_str
*buf
, const char *end
)
583 if (end
> buf
->ptr
&& end
<= buf
->ptr
+ buf
->size
) {
584 size_t consumed
= end
- buf
->ptr
;
585 memmove(buf
->ptr
, end
, buf
->size
- consumed
);
586 buf
->size
-= consumed
;
587 buf
->ptr
[buf
->size
] = '\0';
591 void git_str_truncate(git_str
*buf
, size_t len
)
593 if (len
>= buf
->size
)
597 if (buf
->size
< buf
->asize
)
598 buf
->ptr
[buf
->size
] = '\0';
601 void git_str_shorten(git_str
*buf
, size_t amount
)
603 if (buf
->size
> amount
)
604 git_str_truncate(buf
, buf
->size
- amount
);
609 void git_str_truncate_at_char(git_str
*buf
, char separator
)
611 ssize_t idx
= git_str_find(buf
, separator
);
613 git_str_truncate(buf
, (size_t)idx
);
616 void git_str_rtruncate_at_char(git_str
*buf
, char separator
)
618 ssize_t idx
= git_str_rfind_next(buf
, separator
);
619 git_str_truncate(buf
, idx
< 0 ? 0 : (size_t)idx
);
622 void git_str_swap(git_str
*str_a
, git_str
*str_b
)
629 char *git_str_detach(git_str
*buf
)
631 char *data
= buf
->ptr
;
633 if (buf
->asize
== 0 || buf
->ptr
== git_str__oom
)
636 git_str_init(buf
, 0);
641 int git_str_attach(git_str
*buf
, char *ptr
, size_t asize
)
643 git_str_dispose(buf
);
647 buf
->size
= strlen(ptr
);
649 buf
->asize
= (asize
< buf
->size
) ? buf
->size
+ 1 : asize
;
650 else /* pass 0 to fall back on strlen + 1 */
651 buf
->asize
= buf
->size
+ 1;
654 ENSURE_SIZE(buf
, asize
);
658 void git_str_attach_notowned(git_str
*buf
, const char *ptr
, size_t size
)
660 if (git_str_is_allocated(buf
))
661 git_str_dispose(buf
);
664 git_str_init(buf
, 0);
666 buf
->ptr
= (char *)ptr
;
672 int git_str_join_n(git_str
*buf
, char separator
, int nbuf
, ...)
676 size_t total_size
= 0, original_size
= buf
->size
;
677 char *out
, *original
= buf
->ptr
;
679 if (buf
->size
> 0 && buf
->ptr
[buf
->size
- 1] != separator
)
680 ++total_size
; /* space for initial separator */
682 /* Make two passes to avoid multiple reallocation */
685 for (i
= 0; i
< nbuf
; ++i
) {
689 segment
= va_arg(ap
, const char *);
693 segment_len
= strlen(segment
);
695 GIT_ERROR_CHECK_ALLOC_ADD(&total_size
, total_size
, segment_len
);
697 if (segment_len
== 0 || segment
[segment_len
- 1] != separator
)
698 GIT_ERROR_CHECK_ALLOC_ADD(&total_size
, total_size
, 1);
702 /* expand buffer if needed */
706 GIT_ERROR_CHECK_ALLOC_ADD(&total_size
, total_size
, 1);
707 if (git_str_grow_by(buf
, total_size
) < 0)
710 out
= buf
->ptr
+ buf
->size
;
712 /* append separator to existing buf if needed */
713 if (buf
->size
> 0 && out
[-1] != separator
)
717 for (i
= 0; i
< nbuf
; ++i
) {
721 segment
= va_arg(ap
, const char *);
725 /* deal with join that references buffer's original content */
726 if (segment
>= original
&& segment
< original
+ original_size
) {
727 size_t offset
= (segment
- original
);
728 segment
= buf
->ptr
+ offset
;
729 segment_len
= original_size
- offset
;
731 segment_len
= strlen(segment
);
734 /* skip leading separators */
735 if (out
> buf
->ptr
&& out
[-1] == separator
)
736 while (segment_len
> 0 && *segment
== separator
) {
741 /* copy over next buffer */
742 if (segment_len
> 0) {
743 memmove(out
, segment
, segment_len
);
747 /* append trailing separator (except for last item) */
748 if (i
< nbuf
- 1 && out
> buf
->ptr
&& out
[-1] != separator
)
753 /* set size based on num characters actually written */
754 buf
->size
= out
- buf
->ptr
;
755 buf
->ptr
[buf
->size
] = '\0';
766 size_t strlen_a
= str_a
? strlen(str_a
) : 0;
767 size_t strlen_b
= strlen(str_b
);
770 ssize_t offset_a
= -1;
772 /* not safe to have str_b point internally to the buffer */
774 GIT_ASSERT_ARG(str_b
< buf
->ptr
|| str_b
>= buf
->ptr
+ buf
->size
);
776 /* figure out if we need to insert a separator */
777 if (separator
&& strlen_a
) {
778 while (*str_b
== separator
) { str_b
++; strlen_b
--; }
779 if (str_a
[strlen_a
- 1] != separator
)
783 /* str_a could be part of the buffer */
784 if (buf
->size
&& str_a
>= buf
->ptr
&& str_a
< buf
->ptr
+ buf
->size
)
785 offset_a
= str_a
- buf
->ptr
;
787 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, strlen_a
, strlen_b
);
788 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, alloc_len
, need_sep
);
789 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, alloc_len
, 1);
790 ENSURE_SIZE(buf
, alloc_len
);
792 /* fix up internal pointers */
794 str_a
= buf
->ptr
+ offset_a
;
796 /* do the actual copying */
797 if (offset_a
!= 0 && str_a
)
798 memmove(buf
->ptr
, str_a
, strlen_a
);
800 buf
->ptr
[strlen_a
] = separator
;
801 memcpy(buf
->ptr
+ strlen_a
+ need_sep
, str_b
, strlen_b
);
803 buf
->size
= strlen_a
+ strlen_b
+ need_sep
;
804 buf
->ptr
[buf
->size
] = '\0';
816 size_t len_a
= strlen(str_a
),
817 len_b
= strlen(str_b
),
818 len_c
= strlen(str_c
),
820 int sep_a
= 0, sep_b
= 0;
823 /* for this function, disallow pointers into the existing buffer */
824 GIT_ASSERT(str_a
< buf
->ptr
|| str_a
>= buf
->ptr
+ buf
->size
);
825 GIT_ASSERT(str_b
< buf
->ptr
|| str_b
>= buf
->ptr
+ buf
->size
);
826 GIT_ASSERT(str_c
< buf
->ptr
|| str_c
>= buf
->ptr
+ buf
->size
);
830 while (*str_b
== separator
) { str_b
++; len_b
--; }
831 sep_a
= (str_a
[len_a
- 1] != separator
);
833 if (len_a
> 0 || len_b
> 0)
834 while (*str_c
== separator
) { str_c
++; len_c
--; }
836 sep_b
= (str_b
[len_b
- 1] != separator
);
839 GIT_ERROR_CHECK_ALLOC_ADD(&len_total
, len_a
, sep_a
);
840 GIT_ERROR_CHECK_ALLOC_ADD(&len_total
, len_total
, len_b
);
841 GIT_ERROR_CHECK_ALLOC_ADD(&len_total
, len_total
, sep_b
);
842 GIT_ERROR_CHECK_ALLOC_ADD(&len_total
, len_total
, len_c
);
843 GIT_ERROR_CHECK_ALLOC_ADD(&len_total
, len_total
, 1);
844 ENSURE_SIZE(buf
, len_total
);
849 memcpy(tgt
, str_a
, len_a
);
855 memcpy(tgt
, str_b
, len_b
);
861 memcpy(tgt
, str_c
, len_c
);
863 buf
->size
= len_a
+ sep_a
+ len_b
+ sep_b
+ len_c
;
864 buf
->ptr
[buf
->size
] = '\0';
869 void git_str_rtrim(git_str
*buf
)
871 while (buf
->size
> 0) {
872 if (!git__isspace(buf
->ptr
[buf
->size
- 1]))
878 if (buf
->asize
> buf
->size
)
879 buf
->ptr
[buf
->size
] = '\0';
882 int git_str_cmp(const git_str
*a
, const git_str
*b
)
884 int result
= memcmp(a
->ptr
, b
->ptr
, min(a
->size
, b
->size
));
885 return (result
!= 0) ? result
:
886 (a
->size
< b
->size
) ? -1 : (a
->size
> b
->size
) ? 1 : 0;
897 size_t new_size
, alloc_size
;
900 GIT_ASSERT(where
<= buf
->size
);
901 GIT_ASSERT(nb_to_remove
<= buf
->size
- where
);
903 splice_loc
= buf
->ptr
+ where
;
905 /* Ported from git.git
906 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
908 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, (buf
->size
- nb_to_remove
), nb_to_insert
);
909 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size
, new_size
, 1);
910 ENSURE_SIZE(buf
, alloc_size
);
912 memmove(splice_loc
+ nb_to_insert
,
913 splice_loc
+ nb_to_remove
,
914 buf
->size
- where
- nb_to_remove
);
916 memcpy(splice_loc
, data
, nb_to_insert
);
918 buf
->size
= new_size
;
919 buf
->ptr
[buf
->size
] = '\0';
923 /* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
924 int git_str_quote(git_str
*buf
)
926 const char whitespace
[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
927 git_str quoted
= GIT_STR_INIT
;
932 /* walk to the first char that needs quoting */
933 if (buf
->size
&& buf
->ptr
[0] == '!')
936 for (i
= 0; !quote
&& i
< buf
->size
; i
++) {
937 if (buf
->ptr
[i
] == '"' || buf
->ptr
[i
] == '\\' ||
938 buf
->ptr
[i
] < ' ' || buf
->ptr
[i
] > '~') {
947 git_str_putc("ed
, '"');
948 git_str_put("ed
, buf
->ptr
, i
);
950 for (; i
< buf
->size
; i
++) {
951 /* whitespace - use the map above, which is ordered by ascii value */
952 if (buf
->ptr
[i
] >= '\a' && buf
->ptr
[i
] <= '\r') {
953 git_str_putc("ed
, '\\');
954 git_str_putc("ed
, whitespace
[buf
->ptr
[i
] - '\a']);
957 /* double quote and backslash must be escaped */
958 else if (buf
->ptr
[i
] == '"' || buf
->ptr
[i
] == '\\') {
959 git_str_putc("ed
, '\\');
960 git_str_putc("ed
, buf
->ptr
[i
]);
963 /* escape anything unprintable as octal */
964 else if (buf
->ptr
[i
] != ' ' &&
965 (buf
->ptr
[i
] < '!' || buf
->ptr
[i
] > '~')) {
966 git_str_printf("ed
, "\\%03o", (unsigned char)buf
->ptr
[i
]);
969 /* yay, printable! */
971 git_str_putc("ed
, buf
->ptr
[i
]);
975 git_str_putc("ed
, '"');
977 if (git_str_oom("ed
)) {
982 git_str_swap("ed
, buf
);
985 git_str_dispose("ed
);
989 /* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
990 int git_str_unquote(git_str
*buf
)
997 if (buf
->size
< 2 || buf
->ptr
[0] != '"' || buf
->ptr
[buf
->size
-1] != '"')
1000 for (i
= 0, j
= 1; j
< buf
->size
-1; i
++, j
++) {
1004 if (j
== buf
->size
-2)
1010 /* \" or \\ simply copy the char in */
1011 case '"': case '\\':
1014 /* add the appropriate escaped char */
1015 case 'a': ch
= '\a'; break;
1016 case 'b': ch
= '\b'; break;
1017 case 'f': ch
= '\f'; break;
1018 case 'n': ch
= '\n'; break;
1019 case 'r': ch
= '\r'; break;
1020 case 't': ch
= '\t'; break;
1021 case 'v': ch
= '\v'; break;
1023 /* \xyz digits convert to the char*/
1024 case '0': case '1': case '2': case '3':
1025 if (j
== buf
->size
-3) {
1026 git_error_set(GIT_ERROR_INVALID
,
1027 "truncated quoted character \\%c", ch
);
1031 if (buf
->ptr
[j
+1] < '0' || buf
->ptr
[j
+1] > '7' ||
1032 buf
->ptr
[j
+2] < '0' || buf
->ptr
[j
+2] > '7') {
1033 git_error_set(GIT_ERROR_INVALID
,
1034 "truncated quoted character \\%c%c%c",
1035 buf
->ptr
[j
], buf
->ptr
[j
+1], buf
->ptr
[j
+2]);
1039 ch
= ((buf
->ptr
[j
] - '0') << 6) |
1040 ((buf
->ptr
[j
+1] - '0') << 3) |
1041 (buf
->ptr
[j
+2] - '0');
1046 git_error_set(GIT_ERROR_INVALID
, "invalid quoted character \\%c", ch
);
1060 git_error_set(GIT_ERROR_INVALID
, "invalid quoted line");
1064 int git_str_puts_escaped(
1067 const char *esc_chars
,
1068 const char *esc_with
)
1071 size_t total
= 0, esc_len
= strlen(esc_with
), count
, alloclen
;
1076 for (scan
= string
; *scan
; ) {
1077 /* count run of non-escaped characters */
1078 count
= strcspn(scan
, esc_chars
);
1081 /* count run of escaped characters */
1082 count
= strspn(scan
, esc_chars
);
1083 total
+= count
* (esc_len
+ 1);
1087 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, total
, 1);
1088 if (git_str_grow_by(buf
, alloclen
) < 0)
1091 for (scan
= string
; *scan
; ) {
1092 count
= strcspn(scan
, esc_chars
);
1094 memmove(buf
->ptr
+ buf
->size
, scan
, count
);
1098 for (count
= strspn(scan
, esc_chars
); count
> 0; --count
) {
1099 /* copy escape sequence */
1100 memmove(buf
->ptr
+ buf
->size
, esc_with
, esc_len
);
1101 buf
->size
+= esc_len
;
1102 /* copy character to be escaped */
1103 buf
->ptr
[buf
->size
] = *scan
;
1109 buf
->ptr
[buf
->size
] = '\0';
1114 void git_str_unescape(git_str
*buf
)
1116 buf
->size
= git__unescape(buf
->ptr
);
1119 int git_str_crlf_to_lf(git_str
*tgt
, const git_str
*src
)
1121 const char *scan
= src
->ptr
;
1122 const char *scan_end
= src
->ptr
+ src
->size
;
1123 const char *next
= memchr(scan
, '\r', src
->size
);
1127 GIT_ASSERT(tgt
!= src
);
1130 return git_str_set(tgt
, src
->ptr
, src
->size
);
1132 /* reduce reallocs while in the loop */
1133 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
, src
->size
, 1);
1134 if (git_str_grow(tgt
, new_size
) < 0)
1140 /* Find the next \r and copy whole chunk up to there to tgt */
1141 for (; next
; scan
= next
+ 1, next
= memchr(scan
, '\r', scan_end
- scan
)) {
1143 size_t copylen
= (size_t)(next
- scan
);
1144 memcpy(out
, scan
, copylen
);
1148 /* Do not drop \r unless it is followed by \n */
1149 if (next
+ 1 == scan_end
|| next
[1] != '\n')
1153 /* Copy remaining input into dest */
1154 if (scan
< scan_end
) {
1155 size_t remaining
= (size_t)(scan_end
- scan
);
1156 memcpy(out
, scan
, remaining
);
1160 tgt
->size
= (size_t)(out
- tgt
->ptr
);
1161 tgt
->ptr
[tgt
->size
] = '\0';
1166 int git_str_lf_to_crlf(git_str
*tgt
, const git_str
*src
)
1168 const char *start
= src
->ptr
;
1169 const char *end
= start
+ src
->size
;
1170 const char *scan
= start
;
1171 const char *next
= memchr(scan
, '\n', src
->size
);
1174 GIT_ASSERT(tgt
!= src
);
1177 return git_str_set(tgt
, src
->ptr
, src
->size
);
1179 /* attempt to reduce reallocs while in the loop */
1180 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, src
->size
, src
->size
>> 4);
1181 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, 1);
1182 if (git_str_grow(tgt
, alloclen
) < 0)
1186 for (; next
; scan
= next
+ 1, next
= memchr(scan
, '\n', end
- scan
)) {
1187 size_t copylen
= next
- scan
;
1189 /* if we find mixed line endings, carry on */
1190 if (copylen
&& next
[-1] == '\r')
1193 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, copylen
, 3);
1194 if (git_str_grow_by(tgt
, alloclen
) < 0)
1198 memcpy(tgt
->ptr
+ tgt
->size
, scan
, copylen
);
1199 tgt
->size
+= copylen
;
1202 tgt
->ptr
[tgt
->size
++] = '\r';
1203 tgt
->ptr
[tgt
->size
++] = '\n';
1206 tgt
->ptr
[tgt
->size
] = '\0';
1207 return git_str_put(tgt
, scan
, end
- scan
);
1210 int git_str_common_prefix(git_str
*buf
, char *const *const strings
, size_t count
)
1213 const char *str
, *pfx
;
1217 if (!strings
|| !count
)
1220 /* initialize common prefix to first string */
1221 if (git_str_sets(buf
, strings
[0]) < 0)
1224 /* go through the rest of the strings, truncating to shared prefix */
1225 for (i
= 1; i
< count
; ++i
) {
1227 for (str
= strings
[i
], pfx
= buf
->ptr
;
1228 *str
&& *str
== *pfx
;
1232 git_str_truncate(buf
, pfx
- buf
->ptr
);
1241 int git_str_is_binary(const git_str
*buf
)
1243 const char *scan
= buf
->ptr
, *end
= buf
->ptr
+ buf
->size
;
1245 int printable
= 0, nonprintable
= 0;
1247 scan
+= git_str_detect_bom(&bom
, buf
);
1249 if (bom
> GIT_STR_BOM_UTF8
)
1252 while (scan
< end
) {
1253 unsigned char c
= *scan
++;
1255 /* Printable characters are those above SPACE (0x1F) excluding DEL,
1256 * and including BS, ESC and FF.
1258 if ((c
> 0x1F && c
!= 127) || c
== '\b' || c
== '\033' || c
== '\014')
1262 else if (!git__isspace(c
))
1266 return ((printable
>> 7) < nonprintable
);
1269 int git_str_contains_nul(const git_str
*buf
)
1271 return (memchr(buf
->ptr
, '\0', buf
->size
) != NULL
);
1274 int git_str_detect_bom(git_str_bom_t
*bom
, const git_str
*buf
)
1279 *bom
= GIT_STR_BOM_NONE
;
1280 /* need at least 2 bytes to look for any BOM */
1289 if (len
>= 4 && ptr
[0] == 0 && ptr
[1] == '\xFE' && ptr
[2] == '\xFF') {
1290 *bom
= GIT_STR_BOM_UTF32_BE
;
1295 if (len
>= 3 && ptr
[0] == '\xBB' && ptr
[1] == '\xBF') {
1296 *bom
= GIT_STR_BOM_UTF8
;
1301 if (*ptr
== '\xFF') {
1302 *bom
= GIT_STR_BOM_UTF16_BE
;
1309 if (len
>= 4 && ptr
[1] == 0 && ptr
[2] == 0) {
1310 *bom
= GIT_STR_BOM_UTF32_LE
;
1313 *bom
= GIT_STR_BOM_UTF16_LE
;
1324 bool git_str_gather_text_stats(
1325 git_str_text_stats
*stats
, const git_str
*buf
, bool skip_bom
)
1327 const char *scan
= buf
->ptr
, *end
= buf
->ptr
+ buf
->size
;
1330 memset(stats
, 0, sizeof(*stats
));
1333 skip
= git_str_detect_bom(&stats
->bom
, buf
);
1337 /* Ignore EOF character */
1338 if (buf
->size
> 0 && end
[-1] == '\032')
1342 while (scan
< end
) {
1343 unsigned char c
= *scan
++;
1345 if (c
> 0x1F && c
!= 0x7F)
1350 stats
->nonprintable
++;
1357 if (scan
< end
&& *scan
== '\n')
1360 case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
1364 stats
->nonprintable
++;
1369 /* Treat files with a bare CR as binary */
1370 return (stats
->cr
!= stats
->crlf
|| stats
->nul
> 0 ||
1371 ((stats
->printable
>> 7) < stats
->nonprintable
));