summaryrefslogtreecommitdiff
path: root/src/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer.c')
-rw-r--r--src/buffer.c1366
1 files changed, 0 insertions, 1366 deletions
diff --git a/src/buffer.c b/src/buffer.c
deleted file mode 100644
index fe087ea11..000000000
--- a/src/buffer.c
+++ /dev/null
@@ -1,1366 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "buffer.h"
-#include "posix.h"
-#include "git2/buffer.h"
-#include <ctype.h>
-
-/* Used as default value for git_buf->ptr so that people can always
- * assume ptr is non-NULL and zero terminated even for new git_bufs.
- */
-char git_buf__initbuf[1];
-
-char git_buf__oom[1];
-
-#define ENSURE_SIZE(b, d) \
- if ((b)->ptr == git_buf__oom || \
- ((d) > (b)->asize && git_buf_grow((b), (d)) < 0))\
- return -1;
-
-
-int git_buf_init(git_buf *buf, size_t initial_size)
-{
- buf->asize = 0;
- buf->size = 0;
- buf->ptr = git_buf__initbuf;
-
- ENSURE_SIZE(buf, initial_size);
-
- return 0;
-}
-
-int git_buf_try_grow(
- git_buf *buf, size_t target_size, bool mark_oom)
-{
- char *new_ptr;
- size_t new_size;
-
- if (buf->ptr == git_buf__oom)
- return -1;
-
- if (buf->asize == 0 && buf->size != 0) {
- git_error_set(GIT_ERROR_INVALID, "cannot grow a borrowed buffer");
- return GIT_EINVALID;
- }
-
- if (!target_size)
- target_size = buf->size;
-
- if (target_size <= buf->asize)
- return 0;
-
- if (buf->asize == 0) {
- new_size = target_size;
- new_ptr = NULL;
- } else {
- new_size = buf->asize;
- /*
- * Grow the allocated buffer by 1.5 to allow
- * re-use of memory holes resulting from the
- * realloc. If this is still too small, then just
- * use the target size.
- */
- if ((new_size = (new_size << 1) - (new_size >> 1)) < target_size)
- new_size = target_size;
- new_ptr = buf->ptr;
- }
-
- /* round allocation up to multiple of 8 */
- new_size = (new_size + 7) & ~7;
-
- if (new_size < buf->size) {
- if (mark_oom) {
- if (buf->ptr && buf->ptr != git_buf__initbuf)
- git__free(buf->ptr);
- buf->ptr = git_buf__oom;
- }
-
- git_error_set_oom();
- return -1;
- }
-
- new_ptr = git__realloc(new_ptr, new_size);
-
- if (!new_ptr) {
- if (mark_oom) {
- if (buf->ptr && (buf->ptr != git_buf__initbuf))
- git__free(buf->ptr);
- buf->ptr = git_buf__oom;
- }
- return -1;
- }
-
- buf->asize = new_size;
- buf->ptr = new_ptr;
-
- /* truncate the existing buffer size if necessary */
- if (buf->size >= buf->asize)
- buf->size = buf->asize - 1;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-int git_buf_grow(git_buf *buffer, size_t target_size)
-{
- return git_buf_try_grow(buffer, target_size, true);
-}
-
-int git_buf_grow_by(git_buf *buffer, size_t additional_size)
-{
- size_t newsize;
-
- if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
- buffer->ptr = git_buf__oom;
- return -1;
- }
-
- return git_buf_try_grow(buffer, newsize, true);
-}
-
-void git_buf_dispose(git_buf *buf)
-{
- if (!buf) return;
-
- if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
- git__free(buf->ptr);
-
- git_buf_init(buf, 0);
-}
-
-#ifndef GIT_DEPRECATE_HARD
-void git_buf_free(git_buf *buf)
-{
- git_buf_dispose(buf);
-}
-#endif
-
-int git_buf_sanitize(git_buf *buf)
-{
- if (buf->ptr == NULL) {
- GIT_ASSERT_ARG(buf->size == 0 && buf->asize == 0);
-
- buf->ptr = git_buf__initbuf;
- } else if (buf->asize > buf->size) {
- buf->ptr[buf->size] = '\0';
- }
-
- return 0;
-}
-
-void git_buf_clear(git_buf *buf)
-{
- buf->size = 0;
-
- if (!buf->ptr) {
- buf->ptr = git_buf__initbuf;
- buf->asize = 0;
- }
-
- if (buf->asize > 0)
- buf->ptr[0] = '\0';
-}
-
-int git_buf_set(git_buf *buf, const void *data, size_t len)
-{
- size_t alloclen;
-
- if (len == 0 || data == NULL) {
- git_buf_clear(buf);
- } else {
- if (data != buf->ptr) {
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
- ENSURE_SIZE(buf, alloclen);
- memmove(buf->ptr, data, len);
- }
-
- buf->size = len;
- if (buf->asize > buf->size)
- buf->ptr[buf->size] = '\0';
-
- }
- return 0;
-}
-
-int git_buf_sets(git_buf *buf, const char *string)
-{
- return git_buf_set(buf, string, string ? strlen(string) : 0);
-}
-
-int git_buf_putc(git_buf *buf, char c)
-{
- size_t new_size;
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
- ENSURE_SIZE(buf, new_size);
- buf->ptr[buf->size++] = c;
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-int git_buf_putcn(git_buf *buf, char c, size_t len)
-{
- size_t new_size;
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
- memset(buf->ptr + buf->size, c, len);
- buf->size += len;
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-int git_buf_put(git_buf *buf, const char *data, size_t len)
-{
- if (len) {
- size_t new_size;
-
- GIT_ASSERT_ARG(data);
-
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
- memmove(buf->ptr + buf->size, data, len);
- buf->size += len;
- buf->ptr[buf->size] = '\0';
- }
- return 0;
-}
-
-int git_buf_puts(git_buf *buf, const char *string)
-{
- GIT_ASSERT_ARG(string);
-
- return git_buf_put(buf, string, strlen(string));
-}
-
-static const char base64_encode[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
-{
- size_t extra = len % 3;
- uint8_t *write, a, b, c;
- const uint8_t *read = (const uint8_t *)data;
- size_t blocks = (len / 3) + !!extra, alloclen;
-
- GIT_ERROR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
- GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
-
- ENSURE_SIZE(buf, alloclen);
- write = (uint8_t *)&buf->ptr[buf->size];
-
- /* convert each run of 3 bytes into 4 output bytes */
- for (len -= extra; len > 0; len -= 3) {
- a = *read++;
- b = *read++;
- c = *read++;
-
- *write++ = base64_encode[a >> 2];
- *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
- *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
- *write++ = base64_encode[c & 0x3f];
- }
-
- if (extra > 0) {
- a = *read++;
- b = (extra > 1) ? *read++ : 0;
-
- *write++ = base64_encode[a >> 2];
- *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
- *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
- *write++ = '=';
- }
-
- buf->size = ((char *)write) - buf->ptr;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-/* The inverse of base64_encode */
-static const int8_t base64_decode[] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
- -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
-{
- size_t i;
- int8_t a, b, c, d;
- size_t orig_size = buf->size, new_size;
-
- if (len % 4) {
- git_error_set(GIT_ERROR_INVALID, "invalid base64 input");
- return -1;
- }
-
- GIT_ASSERT_ARG(len % 4 == 0);
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
-
- for (i = 0; i < len; i += 4) {
- if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
- (b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
- (c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
- (d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
- buf->size = orig_size;
- buf->ptr[buf->size] = '\0';
-
- git_error_set(GIT_ERROR_INVALID, "invalid base64 input");
- return -1;
- }
-
- buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
- buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
- buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
- }
-
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-static const char base85_encode[] =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
-
-int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
-{
- size_t blocks = (len / 4) + !!(len % 4), alloclen;
-
- GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
-
- ENSURE_SIZE(buf, alloclen);
-
- while (len) {
- uint32_t acc = 0;
- char b85[5];
- int i;
-
- for (i = 24; i >= 0; i -= 8) {
- uint8_t ch = *data++;
- acc |= (uint32_t)ch << i;
-
- if (--len == 0)
- break;
- }
-
- for (i = 4; i >= 0; i--) {
- int val = acc % 85;
- acc /= 85;
-
- b85[i] = base85_encode[val];
- }
-
- for (i = 0; i < 5; i++)
- buf->ptr[buf->size++] = b85[i];
- }
-
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-/* The inverse of base85_encode */
-static const int8_t base85_decode[] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 73, 74, 75, 76, 77,
- 78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80,
- 81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-int git_buf_decode_base85(
- git_buf *buf,
- const char *base85,
- size_t base85_len,
- size_t output_len)
-{
- size_t orig_size = buf->size, new_size;
-
- if (base85_len % 5 ||
- output_len > base85_len * 4 / 5) {
- git_error_set(GIT_ERROR_INVALID, "invalid base85 input");
- return -1;
- }
-
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size);
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
-
- while (output_len) {
- unsigned acc = 0;
- int de, cnt = 4;
- unsigned char ch;
- do {
- ch = *base85++;
- de = base85_decode[ch];
- if (--de < 0)
- goto on_error;
-
- acc = acc * 85 + de;
- } while (--cnt);
- ch = *base85++;
- de = base85_decode[ch];
- if (--de < 0)
- goto on_error;
-
- /* Detect overflow. */
- if (0xffffffff / 85 < acc ||
- 0xffffffff - de < (acc *= 85))
- goto on_error;
-
- acc += de;
-
- cnt = (output_len < 4) ? (int)output_len : 4;
- output_len -= cnt;
- do {
- acc = (acc << 8) | (acc >> 24);
- buf->ptr[buf->size++] = acc;
- } while (--cnt);
- }
-
- buf->ptr[buf->size] = 0;
-
- return 0;
-
-on_error:
- buf->size = orig_size;
- buf->ptr[buf->size] = '\0';
-
- git_error_set(GIT_ERROR_INVALID, "invalid base85 input");
- return -1;
-}
-
-#define HEX_DECODE(c) ((c | 32) % 39 - 9)
-
-int git_buf_decode_percent(
- git_buf *buf,
- const char *str,
- size_t str_len)
-{
- size_t str_pos, new_size;
-
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, str_len);
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
-
- for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) {
- if (str[str_pos] == '%' &&
- str_len > str_pos + 2 &&
- isxdigit(str[str_pos + 1]) &&
- isxdigit(str[str_pos + 2])) {
- buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) +
- HEX_DECODE(str[str_pos + 2]);
- str_pos += 2;
- } else {
- buf->ptr[buf->size] = str[str_pos];
- }
- }
-
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
-{
- size_t expected_size, new_size;
- int len;
-
- GIT_ERROR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
- GIT_ERROR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
- ENSURE_SIZE(buf, expected_size);
-
- while (1) {
- va_list args;
- va_copy(args, ap);
-
- len = p_vsnprintf(
- buf->ptr + buf->size,
- buf->asize - buf->size,
- format, args
- );
-
- va_end(args);
-
- if (len < 0) {
- git__free(buf->ptr);
- buf->ptr = git_buf__oom;
- return -1;
- }
-
- if ((size_t)len + 1 <= buf->asize - buf->size) {
- buf->size += len;
- break;
- }
-
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
- }
-
- return 0;
-}
-
-int git_buf_printf(git_buf *buf, const char *format, ...)
-{
- int r;
- va_list ap;
-
- va_start(ap, format);
- r = git_buf_vprintf(buf, format, ap);
- va_end(ap);
-
- return r;
-}
-
-int git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
-{
- size_t copylen;
-
- GIT_ASSERT_ARG(data);
- GIT_ASSERT_ARG(datasize);
- GIT_ASSERT_ARG(buf);
-
- data[0] = '\0';
-
- if (buf->size == 0 || buf->asize <= 0)
- return 0;
-
- copylen = buf->size;
- if (copylen > datasize - 1)
- copylen = datasize - 1;
- memmove(data, buf->ptr, copylen);
- data[copylen] = '\0';
-
- return 0;
-}
-
-void git_buf_consume_bytes(git_buf *buf, size_t len)
-{
- git_buf_consume(buf, buf->ptr + len);
-}
-
-void git_buf_consume(git_buf *buf, const char *end)
-{
- if (end > buf->ptr && end <= buf->ptr + buf->size) {
- size_t consumed = end - buf->ptr;
- memmove(buf->ptr, end, buf->size - consumed);
- buf->size -= consumed;
- buf->ptr[buf->size] = '\0';
- }
-}
-
-void git_buf_truncate(git_buf *buf, size_t len)
-{
- if (len >= buf->size)
- return;
-
- buf->size = len;
- if (buf->size < buf->asize)
- buf->ptr[buf->size] = '\0';
-}
-
-void git_buf_shorten(git_buf *buf, size_t amount)
-{
- if (buf->size > amount)
- git_buf_truncate(buf, buf->size - amount);
- else
- git_buf_clear(buf);
-}
-
-void git_buf_truncate_at_char(git_buf *buf, char separator)
-{
- ssize_t idx = git_buf_find(buf, separator);
- if (idx >= 0)
- git_buf_truncate(buf, (size_t)idx);
-}
-
-void git_buf_rtruncate_at_char(git_buf *buf, char separator)
-{
- ssize_t idx = git_buf_rfind_next(buf, separator);
- git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
-}
-
-void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
-{
- git_buf t = *buf_a;
- *buf_a = *buf_b;
- *buf_b = t;
-}
-
-char *git_buf_detach(git_buf *buf)
-{
- char *data = buf->ptr;
-
- if (buf->asize == 0 || buf->ptr == git_buf__oom)
- return NULL;
-
- git_buf_init(buf, 0);
-
- return data;
-}
-
-int git_buf_attach(git_buf *buf, char *ptr, size_t asize)
-{
- git_buf_dispose(buf);
-
- if (ptr) {
- buf->ptr = ptr;
- buf->size = strlen(ptr);
- if (asize)
- buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
- else /* pass 0 to fall back on strlen + 1 */
- buf->asize = buf->size + 1;
- }
-
- ENSURE_SIZE(buf, asize);
- return 0;
-}
-
-void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
-{
- if (git_buf_is_allocated(buf))
- git_buf_dispose(buf);
-
- if (!size) {
- git_buf_init(buf, 0);
- } else {
- buf->ptr = (char *)ptr;
- buf->asize = 0;
- buf->size = size;
- }
-}
-
-int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
-{
- va_list ap;
- int i;
- size_t total_size = 0, original_size = buf->size;
- char *out, *original = buf->ptr;
-
- if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
- ++total_size; /* space for initial separator */
-
- /* Make two passes to avoid multiple reallocation */
-
- va_start(ap, nbuf);
- for (i = 0; i < nbuf; ++i) {
- const char *segment;
- size_t segment_len;
-
- segment = va_arg(ap, const char *);
- if (!segment)
- continue;
-
- segment_len = strlen(segment);
-
- GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
-
- if (segment_len == 0 || segment[segment_len - 1] != separator)
- GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
- }
- va_end(ap);
-
- /* expand buffer if needed */
- if (total_size == 0)
- return 0;
-
- GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
- if (git_buf_grow_by(buf, total_size) < 0)
- return -1;
-
- out = buf->ptr + buf->size;
-
- /* append separator to existing buf if needed */
- if (buf->size > 0 && out[-1] != separator)
- *out++ = separator;
-
- va_start(ap, nbuf);
- for (i = 0; i < nbuf; ++i) {
- const char *segment;
- size_t segment_len;
-
- segment = va_arg(ap, const char *);
- if (!segment)
- continue;
-
- /* deal with join that references buffer's original content */
- if (segment >= original && segment < original + original_size) {
- size_t offset = (segment - original);
- segment = buf->ptr + offset;
- segment_len = original_size - offset;
- } else {
- segment_len = strlen(segment);
- }
-
- /* skip leading separators */
- if (out > buf->ptr && out[-1] == separator)
- while (segment_len > 0 && *segment == separator) {
- segment++;
- segment_len--;
- }
-
- /* copy over next buffer */
- if (segment_len > 0) {
- memmove(out, segment, segment_len);
- out += segment_len;
- }
-
- /* append trailing separator (except for last item) */
- if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
- *out++ = separator;
- }
- va_end(ap);
-
- /* set size based on num characters actually written */
- buf->size = out - buf->ptr;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-int git_buf_join(
- git_buf *buf,
- char separator,
- const char *str_a,
- const char *str_b)
-{
- size_t strlen_a = str_a ? strlen(str_a) : 0;
- size_t strlen_b = strlen(str_b);
- size_t alloc_len;
- int need_sep = 0;
- ssize_t offset_a = -1;
-
- /* not safe to have str_b point internally to the buffer */
- if (buf->size)
- GIT_ASSERT_ARG(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
-
- /* figure out if we need to insert a separator */
- if (separator && strlen_a) {
- while (*str_b == separator) { str_b++; strlen_b--; }
- if (str_a[strlen_a - 1] != separator)
- need_sep = 1;
- }
-
- /* str_a could be part of the buffer */
- if (buf->size && str_a >= buf->ptr && str_a < buf->ptr + buf->size)
- offset_a = str_a - buf->ptr;
-
- GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
- ENSURE_SIZE(buf, alloc_len);
-
- /* fix up internal pointers */
- if (offset_a >= 0)
- str_a = buf->ptr + offset_a;
-
- /* do the actual copying */
- if (offset_a != 0 && str_a)
- memmove(buf->ptr, str_a, strlen_a);
- if (need_sep)
- buf->ptr[strlen_a] = separator;
- memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
-
- buf->size = strlen_a + strlen_b + need_sep;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-int git_buf_join3(
- git_buf *buf,
- char separator,
- const char *str_a,
- const char *str_b,
- const char *str_c)
-{
- size_t len_a = strlen(str_a),
- len_b = strlen(str_b),
- len_c = strlen(str_c),
- len_total;
- int sep_a = 0, sep_b = 0;
- char *tgt;
-
- /* for this function, disallow pointers into the existing buffer */
- GIT_ASSERT(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
- GIT_ASSERT(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
- GIT_ASSERT(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
-
- if (separator) {
- if (len_a > 0) {
- while (*str_b == separator) { str_b++; len_b--; }
- sep_a = (str_a[len_a - 1] != separator);
- }
- if (len_a > 0 || len_b > 0)
- while (*str_c == separator) { str_c++; len_c--; }
- if (len_b > 0)
- sep_b = (str_b[len_b - 1] != separator);
- }
-
- GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
- GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
- GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
- GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
- GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
- ENSURE_SIZE(buf, len_total);
-
- tgt = buf->ptr;
-
- if (len_a) {
- memcpy(tgt, str_a, len_a);
- tgt += len_a;
- }
- if (sep_a)
- *tgt++ = separator;
- if (len_b) {
- memcpy(tgt, str_b, len_b);
- tgt += len_b;
- }
- if (sep_b)
- *tgt++ = separator;
- if (len_c)
- memcpy(tgt, str_c, len_c);
-
- buf->size = len_a + sep_a + len_b + sep_b + len_c;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-void git_buf_rtrim(git_buf *buf)
-{
- while (buf->size > 0) {
- if (!git__isspace(buf->ptr[buf->size - 1]))
- break;
-
- buf->size--;
- }
-
- if (buf->asize > buf->size)
- buf->ptr[buf->size] = '\0';
-}
-
-int git_buf_cmp(const git_buf *a, const git_buf *b)
-{
- int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
- return (result != 0) ? result :
- (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
-}
-
-int git_buf_splice(
- git_buf *buf,
- size_t where,
- size_t nb_to_remove,
- const char *data,
- size_t nb_to_insert)
-{
- char *splice_loc;
- size_t new_size, alloc_size;
-
- GIT_ASSERT(buf);
- GIT_ASSERT(where <= buf->size);
- GIT_ASSERT(nb_to_remove <= buf->size - where);
-
- splice_loc = buf->ptr + where;
-
- /* Ported from git.git
- * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
- */
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
- ENSURE_SIZE(buf, alloc_size);
-
- memmove(splice_loc + nb_to_insert,
- splice_loc + nb_to_remove,
- buf->size - where - nb_to_remove);
-
- memcpy(splice_loc, data, nb_to_insert);
-
- buf->size = new_size;
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-/* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
-int git_buf_quote(git_buf *buf)
-{
- const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
- git_buf quoted = GIT_BUF_INIT;
- size_t i = 0;
- bool quote = false;
- int error = 0;
-
- /* walk to the first char that needs quoting */
- if (buf->size && buf->ptr[0] == '!')
- quote = true;
-
- for (i = 0; !quote && i < buf->size; i++) {
- if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
- buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
- quote = true;
- break;
- }
- }
-
- if (!quote)
- goto done;
-
- git_buf_putc(&quoted, '"');
- git_buf_put(&quoted, buf->ptr, i);
-
- for (; i < buf->size; i++) {
- /* whitespace - use the map above, which is ordered by ascii value */
- if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
- git_buf_putc(&quoted, '\\');
- git_buf_putc(&quoted, whitespace[buf->ptr[i] - '\a']);
- }
-
- /* double quote and backslash must be escaped */
- else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
- git_buf_putc(&quoted, '\\');
- git_buf_putc(&quoted, buf->ptr[i]);
- }
-
- /* escape anything unprintable as octal */
- else if (buf->ptr[i] != ' ' &&
- (buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
- git_buf_printf(&quoted, "\\%03o", (unsigned char)buf->ptr[i]);
- }
-
- /* yay, printable! */
- else {
- git_buf_putc(&quoted, buf->ptr[i]);
- }
- }
-
- git_buf_putc(&quoted, '"');
-
- if (git_buf_oom(&quoted)) {
- error = -1;
- goto done;
- }
-
- git_buf_swap(&quoted, buf);
-
-done:
- git_buf_dispose(&quoted);
- return error;
-}
-
-/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
-int git_buf_unquote(git_buf *buf)
-{
- size_t i, j;
- char ch;
-
- git_buf_rtrim(buf);
-
- if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
- goto invalid;
-
- for (i = 0, j = 1; j < buf->size-1; i++, j++) {
- ch = buf->ptr[j];
-
- if (ch == '\\') {
- if (j == buf->size-2)
- goto invalid;
-
- ch = buf->ptr[++j];
-
- switch (ch) {
- /* \" or \\ simply copy the char in */
- case '"': case '\\':
- break;
-
- /* add the appropriate escaped char */
- case 'a': ch = '\a'; break;
- case 'b': ch = '\b'; break;
- case 'f': ch = '\f'; break;
- case 'n': ch = '\n'; break;
- case 'r': ch = '\r'; break;
- case 't': ch = '\t'; break;
- case 'v': ch = '\v'; break;
-
- /* \xyz digits convert to the char*/
- case '0': case '1': case '2': case '3':
- if (j == buf->size-3) {
- git_error_set(GIT_ERROR_INVALID,
- "truncated quoted character \\%c", ch);
- return -1;
- }
-
- if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
- buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
- git_error_set(GIT_ERROR_INVALID,
- "truncated quoted character \\%c%c%c",
- buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
- return -1;
- }
-
- ch = ((buf->ptr[j] - '0') << 6) |
- ((buf->ptr[j+1] - '0') << 3) |
- (buf->ptr[j+2] - '0');
- j += 2;
- break;
-
- default:
- git_error_set(GIT_ERROR_INVALID, "invalid quoted character \\%c", ch);
- return -1;
- }
- }
-
- buf->ptr[i] = ch;
- }
-
- buf->ptr[i] = '\0';
- buf->size = i;
-
- return 0;
-
-invalid:
- git_error_set(GIT_ERROR_INVALID, "invalid quoted line");
- return -1;
-}
-
-int git_buf_puts_escaped(
- git_buf *buf,
- const char *string,
- const char *esc_chars,
- const char *esc_with)
-{
- const char *scan;
- size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
-
- if (!string)
- return 0;
-
- for (scan = string; *scan; ) {
- /* count run of non-escaped characters */
- count = strcspn(scan, esc_chars);
- total += count;
- scan += count;
- /* count run of escaped characters */
- count = strspn(scan, esc_chars);
- total += count * (esc_len + 1);
- scan += count;
- }
-
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, total, 1);
- if (git_buf_grow_by(buf, alloclen) < 0)
- return -1;
-
- for (scan = string; *scan; ) {
- count = strcspn(scan, esc_chars);
-
- memmove(buf->ptr + buf->size, scan, count);
- scan += count;
- buf->size += count;
-
- for (count = strspn(scan, esc_chars); count > 0; --count) {
- /* copy escape sequence */
- memmove(buf->ptr + buf->size, esc_with, esc_len);
- buf->size += esc_len;
- /* copy character to be escaped */
- buf->ptr[buf->size] = *scan;
- buf->size++;
- scan++;
- }
- }
-
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-void git_buf_unescape(git_buf *buf)
-{
- buf->size = git__unescape(buf->ptr);
-}
-
-int git_buf_crlf_to_lf(git_buf *tgt, const git_buf *src)
-{
- const char *scan = src->ptr;
- const char *scan_end = src->ptr + src->size;
- const char *next = memchr(scan, '\r', src->size);
- size_t new_size;
- char *out;
-
- GIT_ASSERT(tgt != src);
-
- if (!next)
- return git_buf_set(tgt, src->ptr, src->size);
-
- /* reduce reallocs while in the loop */
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
- if (git_buf_grow(tgt, new_size) < 0)
- return -1;
-
- out = tgt->ptr;
- tgt->size = 0;
-
- /* Find the next \r and copy whole chunk up to there to tgt */
- for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
- if (next > scan) {
- size_t copylen = (size_t)(next - scan);
- memcpy(out, scan, copylen);
- out += copylen;
- }
-
- /* Do not drop \r unless it is followed by \n */
- if (next + 1 == scan_end || next[1] != '\n')
- *out++ = '\r';
- }
-
- /* Copy remaining input into dest */
- if (scan < scan_end) {
- size_t remaining = (size_t)(scan_end - scan);
- memcpy(out, scan, remaining);
- out += remaining;
- }
-
- tgt->size = (size_t)(out - tgt->ptr);
- tgt->ptr[tgt->size] = '\0';
-
- return 0;
-}
-
-int git_buf_lf_to_crlf(git_buf *tgt, const git_buf *src)
-{
- const char *start = src->ptr;
- const char *end = start + src->size;
- const char *scan = start;
- const char *next = memchr(scan, '\n', src->size);
- size_t alloclen;
-
- GIT_ASSERT(tgt != src);
-
- if (!next)
- return git_buf_set(tgt, src->ptr, src->size);
-
- /* attempt to reduce reallocs while in the loop */
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- if (git_buf_grow(tgt, alloclen) < 0)
- return -1;
- tgt->size = 0;
-
- for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
- size_t copylen = next - scan;
-
- /* if we find mixed line endings, carry on */
- if (copylen && next[-1] == '\r')
- copylen--;
-
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
- if (git_buf_grow_by(tgt, alloclen) < 0)
- return -1;
-
- if (copylen) {
- memcpy(tgt->ptr + tgt->size, scan, copylen);
- tgt->size += copylen;
- }
-
- tgt->ptr[tgt->size++] = '\r';
- tgt->ptr[tgt->size++] = '\n';
- }
-
- tgt->ptr[tgt->size] = '\0';
- return git_buf_put(tgt, scan, end - scan);
-}
-
-int git_buf_common_prefix(git_buf *buf, char *const *const strings, size_t count)
-{
- size_t i;
- const char *str, *pfx;
-
- git_buf_clear(buf);
-
- if (!strings || !count)
- return 0;
-
- /* initialize common prefix to first string */
- if (git_buf_sets(buf, strings[0]) < 0)
- return -1;
-
- /* go through the rest of the strings, truncating to shared prefix */
- for (i = 1; i < count; ++i) {
-
- for (str = strings[i], pfx = buf->ptr;
- *str && *str == *pfx;
- str++, pfx++)
- /* scanning */;
-
- git_buf_truncate(buf, pfx - buf->ptr);
-
- if (!buf->size)
- break;
- }
-
- return 0;
-}
-
-int git_buf_is_binary(const git_buf *buf)
-{
- const char *scan = buf->ptr, *end = buf->ptr + buf->size;
- git_buf_bom_t bom;
- int printable = 0, nonprintable = 0;
-
- scan += git_buf_detect_bom(&bom, buf);
-
- if (bom > GIT_BUF_BOM_UTF8)
- return 1;
-
- while (scan < end) {
- unsigned char c = *scan++;
-
- /* Printable characters are those above SPACE (0x1F) excluding DEL,
- * and including BS, ESC and FF.
- */
- if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014')
- printable++;
- else if (c == '\0')
- return true;
- else if (!git__isspace(c))
- nonprintable++;
- }
-
- return ((printable >> 7) < nonprintable);
-}
-
-int git_buf_contains_nul(const git_buf *buf)
-{
- return (memchr(buf->ptr, '\0', buf->size) != NULL);
-}
-
-int git_buf_detect_bom(git_buf_bom_t *bom, const git_buf *buf)
-{
- const char *ptr;
- size_t len;
-
- *bom = GIT_BUF_BOM_NONE;
- /* need at least 2 bytes to look for any BOM */
- if (buf->size < 2)
- return 0;
-
- ptr = buf->ptr;
- len = buf->size;
-
- switch (*ptr++) {
- case 0:
- if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
- *bom = GIT_BUF_BOM_UTF32_BE;
- return 4;
- }
- break;
- case '\xEF':
- if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
- *bom = GIT_BUF_BOM_UTF8;
- return 3;
- }
- break;
- case '\xFE':
- if (*ptr == '\xFF') {
- *bom = GIT_BUF_BOM_UTF16_BE;
- return 2;
- }
- break;
- case '\xFF':
- if (*ptr != '\xFE')
- break;
- if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
- *bom = GIT_BUF_BOM_UTF32_LE;
- return 4;
- } else {
- *bom = GIT_BUF_BOM_UTF16_LE;
- return 2;
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-bool git_buf_gather_text_stats(
- git_buf_text_stats *stats, const git_buf *buf, bool skip_bom)
-{
- const char *scan = buf->ptr, *end = buf->ptr + buf->size;
- int skip;
-
- memset(stats, 0, sizeof(*stats));
-
- /* BOM detection */
- skip = git_buf_detect_bom(&stats->bom, buf);
- if (skip_bom)
- scan += skip;
-
- /* Ignore EOF character */
- if (buf->size > 0 && end[-1] == '\032')
- end--;
-
- /* Counting loop */
- while (scan < end) {
- unsigned char c = *scan++;
-
- if (c > 0x1F && c != 0x7F)
- stats->printable++;
- else switch (c) {
- case '\0':
- stats->nul++;
- stats->nonprintable++;
- break;
- case '\n':
- stats->lf++;
- break;
- case '\r':
- stats->cr++;
- if (scan < end && *scan == '\n')
- stats->crlf++;
- break;
- case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
- stats->printable++;
- break;
- default:
- stats->nonprintable++;
- break;
- }
- }
-
- /* Treat files with a bare CR as binary */
- return (stats->cr != stats->crlf || stats->nul > 0 ||
- ((stats->printable >> 7) < stats->nonprintable));
-}