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