Blame src/buffer.c

Packit ae9e2a
/*
Packit ae9e2a
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit ae9e2a
 *
Packit ae9e2a
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit ae9e2a
 * a Linking Exception. For full terms see the included COPYING file.
Packit ae9e2a
 */
Packit ae9e2a
#include "buffer.h"
Packit ae9e2a
#include "posix.h"
Packit ae9e2a
#include "git2/buffer.h"
Packit ae9e2a
#include "buf_text.h"
Packit ae9e2a
#include <ctype.h>
Packit ae9e2a
Packit ae9e2a
/* Used as default value for git_buf->ptr so that people can always
Packit ae9e2a
 * assume ptr is non-NULL and zero terminated even for new git_bufs.
Packit ae9e2a
 */
Packit ae9e2a
char git_buf__initbuf[1];
Packit ae9e2a
Packit ae9e2a
char git_buf__oom[1];
Packit ae9e2a
Packit ae9e2a
#define ENSURE_SIZE(b, d) \
Packit ae9e2a
	if ((d) > (b)->asize && git_buf_grow((b), (d)) < 0)\
Packit ae9e2a
		return -1;
Packit ae9e2a
Packit ae9e2a
Packit ae9e2a
int git_buf_init(git_buf *buf, size_t initial_size)
Packit ae9e2a
{
Packit ae9e2a
	buf->asize = 0;
Packit ae9e2a
	buf->size = 0;
Packit ae9e2a
	buf->ptr = git_buf__initbuf;
Packit ae9e2a
Packit ae9e2a
	ENSURE_SIZE(buf, initial_size);
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_try_grow(
Packit ae9e2a
	git_buf *buf, size_t target_size, bool mark_oom)
Packit ae9e2a
{
Packit ae9e2a
	char *new_ptr;
Packit ae9e2a
	size_t new_size;
Packit ae9e2a
Packit ae9e2a
	if (buf->ptr == git_buf__oom)
Packit ae9e2a
		return -1;
Packit ae9e2a
Packit ae9e2a
	if (buf->asize == 0 && buf->size != 0) {
Packit ae9e2a
		giterr_set(GITERR_INVALID, "cannot grow a borrowed buffer");
Packit ae9e2a
		return GIT_EINVALID;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	if (!target_size)
Packit ae9e2a
		target_size = buf->size;
Packit ae9e2a
Packit ae9e2a
	if (target_size <= buf->asize)
Packit ae9e2a
		return 0;
Packit ae9e2a
Packit ae9e2a
	if (buf->asize == 0) {
Packit ae9e2a
		new_size = target_size;
Packit ae9e2a
		new_ptr = NULL;
Packit ae9e2a
	} else {
Packit ae9e2a
		new_size = buf->asize;
Packit ae9e2a
		new_ptr = buf->ptr;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	/* grow the buffer size by 1.5, until it's big enough
Packit ae9e2a
	 * to fit our target size */
Packit ae9e2a
	while (new_size < target_size)
Packit ae9e2a
		new_size = (new_size << 1) - (new_size >> 1);
Packit ae9e2a
Packit ae9e2a
	/* round allocation up to multiple of 8 */
Packit ae9e2a
	new_size = (new_size + 7) & ~7;
Packit ae9e2a
Packit ae9e2a
	if (new_size < buf->size) {
Packit ae9e2a
		if (mark_oom)
Packit ae9e2a
			buf->ptr = git_buf__oom;
Packit ae9e2a
Packit ae9e2a
		giterr_set_oom();
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	new_ptr = git__realloc(new_ptr, new_size);
Packit ae9e2a
Packit ae9e2a
	if (!new_ptr) {
Packit ae9e2a
		if (mark_oom) {
Packit ae9e2a
			if (buf->ptr && (buf->ptr != git_buf__initbuf))
Packit ae9e2a
				git__free(buf->ptr);
Packit ae9e2a
			buf->ptr = git_buf__oom;
Packit ae9e2a
		}
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	buf->asize = new_size;
Packit ae9e2a
	buf->ptr   = new_ptr;
Packit ae9e2a
Packit ae9e2a
	/* truncate the existing buffer size if necessary */
Packit ae9e2a
	if (buf->size >= buf->asize)
Packit ae9e2a
		buf->size = buf->asize - 1;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_grow(git_buf *buffer, size_t target_size)
Packit ae9e2a
{
Packit ae9e2a
	return git_buf_try_grow(buffer, target_size, true);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_grow_by(git_buf *buffer, size_t additional_size)
Packit ae9e2a
{
Packit ae9e2a
	size_t newsize;
Packit ae9e2a
Packit ae9e2a
	if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
Packit ae9e2a
		buffer->ptr = git_buf__oom;
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	return git_buf_try_grow(buffer, newsize, true);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_free(git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	if (!buf) return;
Packit ae9e2a
Packit ae9e2a
	if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
Packit ae9e2a
		git__free(buf->ptr);
Packit ae9e2a
Packit ae9e2a
	git_buf_init(buf, 0);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_sanitize(git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	if (buf->ptr == NULL) {
Packit ae9e2a
		assert(buf->size == 0 && buf->asize == 0);
Packit ae9e2a
		buf->ptr = git_buf__initbuf;
Packit ae9e2a
	} else if (buf->asize > buf->size)
Packit ae9e2a
		buf->ptr[buf->size] = '\0';
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_clear(git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	buf->size = 0;
Packit ae9e2a
Packit ae9e2a
	if (!buf->ptr) {
Packit ae9e2a
		buf->ptr = git_buf__initbuf;
Packit ae9e2a
		buf->asize = 0;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	if (buf->asize > 0)
Packit ae9e2a
		buf->ptr[0] = '\0';
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_set(git_buf *buf, const void *data, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	size_t alloclen;
Packit ae9e2a
Packit ae9e2a
	if (len == 0 || data == NULL) {
Packit ae9e2a
		git_buf_clear(buf);
Packit ae9e2a
	} else {
Packit ae9e2a
		if (data != buf->ptr) {
Packit ae9e2a
			GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
Packit ae9e2a
			ENSURE_SIZE(buf, alloclen);
Packit ae9e2a
			memmove(buf->ptr, data, len);
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		buf->size = len;
Packit ae9e2a
		if (buf->asize > buf->size)
Packit ae9e2a
			buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	}
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_is_binary(const git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	return git_buf_text_is_binary(buf);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_contains_nul(const git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	return git_buf_text_contains_nul(buf);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_sets(git_buf *buf, const char *string)
Packit ae9e2a
{
Packit ae9e2a
	return git_buf_set(buf, string, string ? strlen(string) : 0);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_putc(git_buf *buf, char c)
Packit ae9e2a
{
Packit ae9e2a
	size_t new_size;
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
Packit ae9e2a
	ENSURE_SIZE(buf, new_size);
Packit ae9e2a
	buf->ptr[buf->size++] = c;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_putcn(git_buf *buf, char c, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	size_t new_size;
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
Packit ae9e2a
	ENSURE_SIZE(buf, new_size);
Packit ae9e2a
	memset(buf->ptr + buf->size, c, len);
Packit ae9e2a
	buf->size += len;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_put(git_buf *buf, const char *data, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	if (len) {
Packit ae9e2a
		size_t new_size;
Packit ae9e2a
Packit ae9e2a
		assert(data);
Packit ae9e2a
		
Packit ae9e2a
		GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
Packit ae9e2a
		GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
Packit ae9e2a
		ENSURE_SIZE(buf, new_size);
Packit ae9e2a
		memmove(buf->ptr + buf->size, data, len);
Packit ae9e2a
		buf->size += len;
Packit ae9e2a
		buf->ptr[buf->size] = '\0';
Packit ae9e2a
	}
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_puts(git_buf *buf, const char *string)
Packit ae9e2a
{
Packit ae9e2a
	assert(string);
Packit ae9e2a
	return git_buf_put(buf, string, strlen(string));
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
static const char base64_encode[] =
Packit ae9e2a
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Packit ae9e2a
Packit ae9e2a
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	size_t extra = len % 3;
Packit ae9e2a
	uint8_t *write, a, b, c;
Packit ae9e2a
	const uint8_t *read = (const uint8_t *)data;
Packit ae9e2a
	size_t blocks = (len / 3) + !!extra, alloclen;
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
Packit ae9e2a
	GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
Packit ae9e2a
Packit ae9e2a
	ENSURE_SIZE(buf, alloclen);
Packit ae9e2a
	write = (uint8_t *)&buf->ptr[buf->size];
Packit ae9e2a
Packit ae9e2a
	/* convert each run of 3 bytes into 4 output bytes */
Packit ae9e2a
	for (len -= extra; len > 0; len -= 3) {
Packit ae9e2a
		a = *read++;
Packit ae9e2a
		b = *read++;
Packit ae9e2a
		c = *read++;
Packit ae9e2a
Packit ae9e2a
		*write++ = base64_encode[a >> 2];
Packit ae9e2a
		*write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
Packit ae9e2a
		*write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
Packit ae9e2a
		*write++ = base64_encode[c & 0x3f];
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	if (extra > 0) {
Packit ae9e2a
		a = *read++;
Packit ae9e2a
		b = (extra > 1) ? *read++ : 0;
Packit ae9e2a
Packit ae9e2a
		*write++ = base64_encode[a >> 2];
Packit ae9e2a
		*write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
Packit ae9e2a
		*write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
Packit ae9e2a
		*write++ = '=';
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	buf->size = ((char *)write) - buf->ptr;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
/* The inverse of base64_encode */
Packit ae9e2a
static const int8_t base64_decode[] = {
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
Packit ae9e2a
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,  0, -1, -1,
Packit ae9e2a
	-1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
Packit ae9e2a
	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
Packit ae9e2a
	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
Packit ae9e2a
};
Packit ae9e2a
Packit ae9e2a
int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	size_t i;
Packit ae9e2a
	int8_t a, b, c, d;
Packit ae9e2a
	size_t orig_size = buf->size, new_size;
Packit ae9e2a
Packit ae9e2a
	if (len % 4) {
Packit ae9e2a
		giterr_set(GITERR_INVALID, "invalid base64 input");
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	assert(len % 4 == 0);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
Packit ae9e2a
	ENSURE_SIZE(buf, new_size);
Packit ae9e2a
Packit ae9e2a
	for (i = 0; i < len; i += 4) {
Packit ae9e2a
		if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
Packit ae9e2a
			(b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
Packit ae9e2a
			(c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
Packit ae9e2a
			(d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
Packit ae9e2a
			buf->size = orig_size;
Packit ae9e2a
			buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
			giterr_set(GITERR_INVALID, "invalid base64 input");
Packit ae9e2a
			return -1;
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
Packit ae9e2a
		buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
Packit ae9e2a
		buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
static const char base85_encode[] =
Packit ae9e2a
	"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
Packit ae9e2a
Packit ae9e2a
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	size_t blocks = (len / 4) + !!(len % 4), alloclen;
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
Packit ae9e2a
Packit ae9e2a
	ENSURE_SIZE(buf, alloclen);
Packit ae9e2a
Packit ae9e2a
	while (len) {
Packit ae9e2a
		uint32_t acc = 0;
Packit ae9e2a
		char b85[5];
Packit ae9e2a
		int i;
Packit ae9e2a
Packit ae9e2a
		for (i = 24; i >= 0; i -= 8) {
Packit ae9e2a
			uint8_t ch = *data++;
Packit ae9e2a
			acc |= ch << i;
Packit ae9e2a
Packit ae9e2a
			if (--len == 0)
Packit ae9e2a
				break;
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		for (i = 4; i >= 0; i--) {
Packit ae9e2a
			int val = acc % 85;
Packit ae9e2a
			acc /= 85;
Packit ae9e2a
Packit ae9e2a
			b85[i] = base85_encode[val];
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		for (i = 0; i < 5; i++)
Packit ae9e2a
			buf->ptr[buf->size++] = b85[i];
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
/* The inverse of base85_encode */
Packit ae9e2a
static const int8_t base85_decode[] = {
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1,
Packit ae9e2a
	 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, -1, 73, 74, 75, 76, 77,
Packit ae9e2a
	78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
Packit ae9e2a
	26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80,
Packit ae9e2a
	81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
Packit ae9e2a
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Packit ae9e2a
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
Packit ae9e2a
};
Packit ae9e2a
Packit ae9e2a
int git_buf_decode_base85(
Packit ae9e2a
	git_buf *buf,
Packit ae9e2a
	const char *base85,
Packit ae9e2a
	size_t base85_len,
Packit ae9e2a
	size_t output_len)
Packit ae9e2a
{
Packit ae9e2a
	size_t orig_size = buf->size, new_size;
Packit ae9e2a
Packit ae9e2a
	if (base85_len % 5 ||
Packit ae9e2a
		output_len > base85_len * 4 / 5) {
Packit ae9e2a
		giterr_set(GITERR_INVALID, "invalid base85 input");
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
Packit ae9e2a
	ENSURE_SIZE(buf, new_size);
Packit ae9e2a
Packit ae9e2a
	while (output_len) {
Packit ae9e2a
		unsigned acc = 0;
Packit ae9e2a
		int de, cnt = 4;
Packit ae9e2a
		unsigned char ch;
Packit ae9e2a
		do {
Packit ae9e2a
			ch = *base85++;
Packit ae9e2a
			de = base85_decode[ch];
Packit ae9e2a
			if (--de < 0)
Packit ae9e2a
				goto on_error;
Packit ae9e2a
Packit ae9e2a
			acc = acc * 85 + de;
Packit ae9e2a
		} while (--cnt);
Packit ae9e2a
		ch = *base85++;
Packit ae9e2a
		de = base85_decode[ch];
Packit ae9e2a
		if (--de < 0)
Packit ae9e2a
			goto on_error;
Packit ae9e2a
Packit ae9e2a
		/* Detect overflow. */
Packit ae9e2a
		if (0xffffffff / 85 < acc ||
Packit ae9e2a
			0xffffffff - de < (acc *= 85))
Packit ae9e2a
			goto on_error;
Packit ae9e2a
Packit ae9e2a
		acc += de;
Packit ae9e2a
Packit ae9e2a
		cnt = (output_len < 4) ? output_len : 4;
Packit ae9e2a
		output_len -= cnt;
Packit ae9e2a
		do {
Packit ae9e2a
			acc = (acc << 8) | (acc >> 24);
Packit ae9e2a
			buf->ptr[buf->size++] = acc;
Packit ae9e2a
		} while (--cnt);
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	buf->ptr[buf->size] = 0;
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
Packit ae9e2a
on_error:
Packit ae9e2a
	buf->size = orig_size;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	giterr_set(GITERR_INVALID, "invalid base85 input");
Packit ae9e2a
	return -1;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
Packit ae9e2a
{
Packit ae9e2a
	size_t expected_size, new_size;
Packit ae9e2a
	int len;
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
Packit ae9e2a
	ENSURE_SIZE(buf, expected_size);
Packit ae9e2a
Packit ae9e2a
	while (1) {
Packit ae9e2a
		va_list args;
Packit ae9e2a
		va_copy(args, ap);
Packit ae9e2a
Packit ae9e2a
		len = p_vsnprintf(
Packit ae9e2a
			buf->ptr + buf->size,
Packit ae9e2a
			buf->asize - buf->size,
Packit ae9e2a
			format, args
Packit ae9e2a
		);
Packit ae9e2a
Packit ae9e2a
		va_end(args);
Packit ae9e2a
Packit ae9e2a
		if (len < 0) {
Packit ae9e2a
			git__free(buf->ptr);
Packit ae9e2a
			buf->ptr = git_buf__oom;
Packit ae9e2a
			return -1;
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		if ((size_t)len + 1 <= buf->asize - buf->size) {
Packit ae9e2a
			buf->size += len;
Packit ae9e2a
			break;
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
Packit ae9e2a
		GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
Packit ae9e2a
		ENSURE_SIZE(buf, new_size);
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_printf(git_buf *buf, const char *format, ...)
Packit ae9e2a
{
Packit ae9e2a
	int r;
Packit ae9e2a
	va_list ap;
Packit ae9e2a
Packit ae9e2a
	va_start(ap, format);
Packit ae9e2a
	r = git_buf_vprintf(buf, format, ap);
Packit ae9e2a
	va_end(ap);
Packit ae9e2a
Packit ae9e2a
	return r;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	size_t copylen;
Packit ae9e2a
Packit ae9e2a
	assert(data && datasize && buf);
Packit ae9e2a
Packit ae9e2a
	data[0] = '\0';
Packit ae9e2a
Packit ae9e2a
	if (buf->size == 0 || buf->asize <= 0)
Packit ae9e2a
		return;
Packit ae9e2a
Packit ae9e2a
	copylen = buf->size;
Packit ae9e2a
	if (copylen > datasize - 1)
Packit ae9e2a
		copylen = datasize - 1;
Packit ae9e2a
	memmove(data, buf->ptr, copylen);
Packit ae9e2a
	data[copylen] = '\0';
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_consume(git_buf *buf, const char *end)
Packit ae9e2a
{
Packit ae9e2a
	if (end > buf->ptr && end <= buf->ptr + buf->size) {
Packit ae9e2a
		size_t consumed = end - buf->ptr;
Packit ae9e2a
		memmove(buf->ptr, end, buf->size - consumed);
Packit ae9e2a
		buf->size -= consumed;
Packit ae9e2a
		buf->ptr[buf->size] = '\0';
Packit ae9e2a
	}
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_truncate(git_buf *buf, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	if (len >= buf->size)
Packit ae9e2a
		return;
Packit ae9e2a
Packit ae9e2a
	buf->size = len;
Packit ae9e2a
	if (buf->size < buf->asize)
Packit ae9e2a
		buf->ptr[buf->size] = '\0';
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_shorten(git_buf *buf, size_t amount)
Packit ae9e2a
{
Packit ae9e2a
	if (buf->size > amount)
Packit ae9e2a
		git_buf_truncate(buf, buf->size - amount);
Packit ae9e2a
	else
Packit ae9e2a
		git_buf_clear(buf);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
Packit ae9e2a
{
Packit ae9e2a
	ssize_t idx = git_buf_rfind_next(buf, separator);
Packit ae9e2a
	git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
Packit ae9e2a
{
Packit ae9e2a
	git_buf t = *buf_a;
Packit ae9e2a
	*buf_a = *buf_b;
Packit ae9e2a
	*buf_b = t;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
char *git_buf_detach(git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	char *data = buf->ptr;
Packit ae9e2a
Packit ae9e2a
	if (buf->asize == 0 || buf->ptr == git_buf__oom)
Packit ae9e2a
		return NULL;
Packit ae9e2a
Packit ae9e2a
	git_buf_init(buf, 0);
Packit ae9e2a
Packit ae9e2a
	return data;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_attach(git_buf *buf, char *ptr, size_t asize)
Packit ae9e2a
{
Packit ae9e2a
	git_buf_free(buf);
Packit ae9e2a
Packit ae9e2a
	if (ptr) {
Packit ae9e2a
		buf->ptr = ptr;
Packit ae9e2a
		buf->size = strlen(ptr);
Packit ae9e2a
		if (asize)
Packit ae9e2a
			buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
Packit ae9e2a
		else /* pass 0 to fall back on strlen + 1 */
Packit ae9e2a
			buf->asize = buf->size + 1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	ENSURE_SIZE(buf, asize);
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
Packit ae9e2a
{
Packit ae9e2a
	if (git_buf_is_allocated(buf))
Packit ae9e2a
		git_buf_free(buf);
Packit ae9e2a
Packit ae9e2a
	if (!size) {
Packit ae9e2a
		git_buf_init(buf, 0);
Packit ae9e2a
	} else {
Packit ae9e2a
		buf->ptr = (char *)ptr;
Packit ae9e2a
		buf->asize = 0;
Packit ae9e2a
		buf->size = size;
Packit ae9e2a
	}
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
Packit ae9e2a
{
Packit ae9e2a
	va_list ap;
Packit ae9e2a
	int i;
Packit ae9e2a
	size_t total_size = 0, original_size = buf->size;
Packit ae9e2a
	char *out, *original = buf->ptr;
Packit ae9e2a
Packit ae9e2a
	if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
Packit ae9e2a
		++total_size; /* space for initial separator */
Packit ae9e2a
Packit ae9e2a
	/* Make two passes to avoid multiple reallocation */
Packit ae9e2a
Packit ae9e2a
	va_start(ap, nbuf);
Packit ae9e2a
	for (i = 0; i < nbuf; ++i) {
Packit ae9e2a
		const char* segment;
Packit ae9e2a
		size_t segment_len;
Packit ae9e2a
Packit ae9e2a
		segment = va_arg(ap, const char *);
Packit ae9e2a
		if (!segment)
Packit ae9e2a
			continue;
Packit ae9e2a
Packit ae9e2a
		segment_len = strlen(segment);
Packit ae9e2a
Packit ae9e2a
		GITERR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
Packit ae9e2a
Packit ae9e2a
		if (segment_len == 0 || segment[segment_len - 1] != separator)
Packit ae9e2a
			GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
Packit ae9e2a
	}
Packit ae9e2a
	va_end(ap);
Packit ae9e2a
Packit ae9e2a
	/* expand buffer if needed */
Packit ae9e2a
	if (total_size == 0)
Packit ae9e2a
		return 0;
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
Packit ae9e2a
	if (git_buf_grow_by(buf, total_size) < 0)
Packit ae9e2a
		return -1;
Packit ae9e2a
Packit ae9e2a
	out = buf->ptr + buf->size;
Packit ae9e2a
Packit ae9e2a
	/* append separator to existing buf if needed */
Packit ae9e2a
	if (buf->size > 0 && out[-1] != separator)
Packit ae9e2a
		*out++ = separator;
Packit ae9e2a
Packit ae9e2a
	va_start(ap, nbuf);
Packit ae9e2a
	for (i = 0; i < nbuf; ++i) {
Packit ae9e2a
		const char* segment;
Packit ae9e2a
		size_t segment_len;
Packit ae9e2a
Packit ae9e2a
		segment = va_arg(ap, const char *);
Packit ae9e2a
		if (!segment)
Packit ae9e2a
			continue;
Packit ae9e2a
Packit ae9e2a
		/* deal with join that references buffer's original content */
Packit ae9e2a
		if (segment >= original && segment < original + original_size) {
Packit ae9e2a
			size_t offset = (segment - original);
Packit ae9e2a
			segment = buf->ptr + offset;
Packit ae9e2a
			segment_len = original_size - offset;
Packit ae9e2a
		} else {
Packit ae9e2a
			segment_len = strlen(segment);
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		/* skip leading separators */
Packit ae9e2a
		if (out > buf->ptr && out[-1] == separator)
Packit ae9e2a
			while (segment_len > 0 && *segment == separator) {
Packit ae9e2a
				segment++;
Packit ae9e2a
				segment_len--;
Packit ae9e2a
			}
Packit ae9e2a
Packit ae9e2a
		/* copy over next buffer */
Packit ae9e2a
		if (segment_len > 0) {
Packit ae9e2a
			memmove(out, segment, segment_len);
Packit ae9e2a
			out += segment_len;
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		/* append trailing separator (except for last item) */
Packit ae9e2a
		if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
Packit ae9e2a
			*out++ = separator;
Packit ae9e2a
	}
Packit ae9e2a
	va_end(ap);
Packit ae9e2a
Packit ae9e2a
	/* set size based on num characters actually written */
Packit ae9e2a
	buf->size = out - buf->ptr;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_join(
Packit ae9e2a
	git_buf *buf,
Packit ae9e2a
	char separator,
Packit ae9e2a
	const char *str_a,
Packit ae9e2a
	const char *str_b)
Packit ae9e2a
{
Packit ae9e2a
	size_t strlen_a = str_a ? strlen(str_a) : 0;
Packit ae9e2a
	size_t strlen_b = strlen(str_b);
Packit ae9e2a
	size_t alloc_len;
Packit ae9e2a
	int need_sep = 0;
Packit ae9e2a
	ssize_t offset_a = -1;
Packit ae9e2a
Packit ae9e2a
	/* not safe to have str_b point internally to the buffer */
Packit ae9e2a
	assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
Packit ae9e2a
Packit ae9e2a
	/* figure out if we need to insert a separator */
Packit ae9e2a
	if (separator && strlen_a) {
Packit ae9e2a
		while (*str_b == separator) { str_b++; strlen_b--; }
Packit ae9e2a
		if (str_a[strlen_a - 1] != separator)
Packit ae9e2a
			need_sep = 1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	/* str_a could be part of the buffer */
Packit ae9e2a
	if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
Packit ae9e2a
		offset_a = str_a - buf->ptr;
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
Packit ae9e2a
	ENSURE_SIZE(buf, alloc_len);
Packit ae9e2a
Packit ae9e2a
	/* fix up internal pointers */
Packit ae9e2a
	if (offset_a >= 0)
Packit ae9e2a
		str_a = buf->ptr + offset_a;
Packit ae9e2a
Packit ae9e2a
	/* do the actual copying */
Packit ae9e2a
	if (offset_a != 0 && str_a)
Packit ae9e2a
		memmove(buf->ptr, str_a, strlen_a);
Packit ae9e2a
	if (need_sep)
Packit ae9e2a
		buf->ptr[strlen_a] = separator;
Packit ae9e2a
	memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
Packit ae9e2a
Packit ae9e2a
	buf->size = strlen_a + strlen_b + need_sep;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_join3(
Packit ae9e2a
	git_buf *buf,
Packit ae9e2a
	char separator,
Packit ae9e2a
	const char *str_a,
Packit ae9e2a
	const char *str_b,
Packit ae9e2a
	const char *str_c)
Packit ae9e2a
{
Packit ae9e2a
	size_t len_a = strlen(str_a),
Packit ae9e2a
		len_b = strlen(str_b),
Packit ae9e2a
		len_c = strlen(str_c),
Packit ae9e2a
		len_total;
Packit ae9e2a
	int sep_a = 0, sep_b = 0;
Packit ae9e2a
	char *tgt;
Packit ae9e2a
Packit ae9e2a
	/* for this function, disallow pointers into the existing buffer */
Packit ae9e2a
	assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
Packit ae9e2a
	assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
Packit ae9e2a
	assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
Packit ae9e2a
Packit ae9e2a
	if (separator) {
Packit ae9e2a
		if (len_a > 0) {
Packit ae9e2a
			while (*str_b == separator) { str_b++; len_b--; }
Packit ae9e2a
			sep_a = (str_a[len_a - 1] != separator);
Packit ae9e2a
		}
Packit ae9e2a
		if (len_a > 0 || len_b > 0)
Packit ae9e2a
			while (*str_c == separator) { str_c++; len_c--; }
Packit ae9e2a
		if (len_b > 0)
Packit ae9e2a
			sep_b = (str_b[len_b - 1] != separator);
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
Packit ae9e2a
	ENSURE_SIZE(buf, len_total);
Packit ae9e2a
Packit ae9e2a
	tgt = buf->ptr;
Packit ae9e2a
Packit ae9e2a
	if (len_a) {
Packit ae9e2a
		memcpy(tgt, str_a, len_a);
Packit ae9e2a
		tgt += len_a;
Packit ae9e2a
	}
Packit ae9e2a
	if (sep_a)
Packit ae9e2a
		*tgt++ = separator;
Packit ae9e2a
	if (len_b) {
Packit ae9e2a
		memcpy(tgt, str_b, len_b);
Packit ae9e2a
		tgt += len_b;
Packit ae9e2a
	}
Packit ae9e2a
	if (sep_b)
Packit ae9e2a
		*tgt++ = separator;
Packit ae9e2a
	if (len_c)
Packit ae9e2a
		memcpy(tgt, str_c, len_c);
Packit ae9e2a
Packit ae9e2a
	buf->size = len_a + sep_a + len_b + sep_b + len_c;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void git_buf_rtrim(git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	while (buf->size > 0) {
Packit ae9e2a
		if (!git__isspace(buf->ptr[buf->size - 1]))
Packit ae9e2a
			break;
Packit ae9e2a
Packit ae9e2a
		buf->size--;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	if (buf->asize > buf->size)
Packit ae9e2a
		buf->ptr[buf->size] = '\0';
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_cmp(const git_buf *a, const git_buf *b)
Packit ae9e2a
{
Packit ae9e2a
	int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
Packit ae9e2a
	return (result != 0) ? result :
Packit ae9e2a
		(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_buf_splice(
Packit ae9e2a
	git_buf *buf,
Packit ae9e2a
	size_t where,
Packit ae9e2a
	size_t nb_to_remove,
Packit ae9e2a
	const char *data,
Packit ae9e2a
	size_t nb_to_insert)
Packit ae9e2a
{
Packit ae9e2a
	char *splice_loc;
Packit ae9e2a
	size_t new_size, alloc_size;
Packit ae9e2a
Packit ae9e2a
	assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
Packit ae9e2a
Packit ae9e2a
	splice_loc = buf->ptr + where;
Packit ae9e2a
Packit ae9e2a
	/* Ported from git.git
Packit ae9e2a
	 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
Packit ae9e2a
	 */
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
Packit ae9e2a
	GITERR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
Packit ae9e2a
	ENSURE_SIZE(buf, alloc_size);
Packit ae9e2a
Packit ae9e2a
	memmove(splice_loc + nb_to_insert,
Packit ae9e2a
		splice_loc + nb_to_remove,
Packit ae9e2a
		buf->size - where - nb_to_remove);
Packit ae9e2a
Packit ae9e2a
	memcpy(splice_loc, data, nb_to_insert);
Packit ae9e2a
Packit ae9e2a
	buf->size = new_size;
Packit ae9e2a
	buf->ptr[buf->size] = '\0';
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
/* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
Packit ae9e2a
int git_buf_quote(git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
Packit ae9e2a
	git_buf quoted = GIT_BUF_INIT;
Packit ae9e2a
	size_t i = 0;
Packit ae9e2a
	bool quote = false;
Packit ae9e2a
	int error = 0;
Packit ae9e2a
Packit ae9e2a
	/* walk to the first char that needs quoting */
Packit ae9e2a
	if (buf->size && buf->ptr[0] == '!')
Packit ae9e2a
		quote = true;
Packit ae9e2a
Packit ae9e2a
	for (i = 0; !quote && i < buf->size; i++) {
Packit ae9e2a
		if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
Packit ae9e2a
			buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
Packit ae9e2a
			quote = true;
Packit ae9e2a
			break;
Packit ae9e2a
		}
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	if (!quote)
Packit ae9e2a
		goto done;
Packit ae9e2a
Packit ae9e2a
	git_buf_putc(&quoted, '"');
Packit ae9e2a
	git_buf_put(&quoted, buf->ptr, i);
Packit ae9e2a
Packit ae9e2a
	for (; i < buf->size; i++) {
Packit ae9e2a
		/* whitespace - use the map above, which is ordered by ascii value */
Packit ae9e2a
		if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
Packit ae9e2a
			git_buf_putc(&quoted, '\\');
Packit ae9e2a
			git_buf_putc(&quoted, whitespace[buf->ptr[i] - '\a']);
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		/* double quote and backslash must be escaped */
Packit ae9e2a
		else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
Packit ae9e2a
			git_buf_putc(&quoted, '\\');
Packit ae9e2a
			git_buf_putc(&quoted, buf->ptr[i]);
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		/* escape anything unprintable as octal */
Packit ae9e2a
		else if (buf->ptr[i] != ' ' &&
Packit ae9e2a
				(buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
Packit ae9e2a
			git_buf_printf(&quoted, "\\%03o", (unsigned char)buf->ptr[i]);
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		/* yay, printable! */
Packit ae9e2a
		else {
Packit ae9e2a
			git_buf_putc(&quoted, buf->ptr[i]);
Packit ae9e2a
		}
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	git_buf_putc(&quoted, '"');
Packit ae9e2a
Packit ae9e2a
	if (git_buf_oom(&quoted)) {
Packit ae9e2a
		error = -1;
Packit ae9e2a
		goto done;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	git_buf_swap(&quoted, buf);
Packit ae9e2a
Packit ae9e2a
done:
Packit ae9e2a
	git_buf_free(&quoted);
Packit ae9e2a
	return error;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
Packit ae9e2a
int git_buf_unquote(git_buf *buf)
Packit ae9e2a
{
Packit ae9e2a
	size_t i, j;
Packit ae9e2a
	char ch;
Packit ae9e2a
Packit ae9e2a
	git_buf_rtrim(buf);
Packit ae9e2a
Packit ae9e2a
	if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
Packit ae9e2a
		goto invalid;
Packit ae9e2a
Packit ae9e2a
	for (i = 0, j = 1; j < buf->size-1; i++, j++) {
Packit ae9e2a
		ch = buf->ptr[j];
Packit ae9e2a
Packit ae9e2a
		if (ch == '\\') {
Packit ae9e2a
			if (j == buf->size-2)
Packit ae9e2a
				goto invalid;
Packit ae9e2a
Packit ae9e2a
			ch = buf->ptr[++j];
Packit ae9e2a
Packit ae9e2a
			switch (ch) {
Packit ae9e2a
			/* \" or \\ simply copy the char in */
Packit ae9e2a
			case '"': case '\\':
Packit ae9e2a
				break;
Packit ae9e2a
Packit ae9e2a
			/* add the appropriate escaped char */
Packit ae9e2a
			case 'a': ch = '\a'; break;
Packit ae9e2a
			case 'b': ch = '\b'; break;
Packit ae9e2a
			case 'f': ch = '\f'; break;
Packit ae9e2a
			case 'n': ch = '\n'; break;
Packit ae9e2a
			case 'r': ch = '\r'; break;
Packit ae9e2a
			case 't': ch = '\t'; break;
Packit ae9e2a
			case 'v': ch = '\v'; break;
Packit ae9e2a
Packit ae9e2a
			/* \xyz digits convert to the char*/
Packit ae9e2a
			case '0': case '1': case '2': case '3':
Packit ae9e2a
				if (j == buf->size-3) {
Packit ae9e2a
					giterr_set(GITERR_INVALID,
Packit ae9e2a
						"truncated quoted character \\%c", ch);
Packit ae9e2a
					return -1;
Packit ae9e2a
				}
Packit ae9e2a
Packit ae9e2a
				if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
Packit ae9e2a
					buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
Packit ae9e2a
					giterr_set(GITERR_INVALID,
Packit ae9e2a
						"truncated quoted character \\%c%c%c",
Packit ae9e2a
						buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
Packit ae9e2a
					return -1;
Packit ae9e2a
				}
Packit ae9e2a
Packit ae9e2a
				ch = ((buf->ptr[j] - '0') << 6) |
Packit ae9e2a
					((buf->ptr[j+1] - '0') << 3) |
Packit ae9e2a
					(buf->ptr[j+2] - '0');
Packit ae9e2a
				j += 2;
Packit ae9e2a
				break;
Packit ae9e2a
Packit ae9e2a
			default:
Packit ae9e2a
				giterr_set(GITERR_INVALID, "invalid quoted character \\%c", ch);
Packit ae9e2a
				return -1;
Packit ae9e2a
			}
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		buf->ptr[i] = ch;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	buf->ptr[i] = '\0';
Packit ae9e2a
	buf->size = i;
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
Packit ae9e2a
invalid:
Packit ae9e2a
	giterr_set(GITERR_INVALID, "invalid quoted line");
Packit ae9e2a
	return -1;
Packit ae9e2a
}