]> git.proxmox.com Git - libgit2.git/blame - src/buffer.c
Merge pull request #968 from arrbee/diff-support-typechange
[libgit2.git] / src / buffer.c
CommitLineData
bb742ede 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
bb742ede
VM
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 */
afeecf4f
VM
7#include "buffer.h"
8#include "posix.h"
9#include <stdarg.h>
13224ea4 10#include <ctype.h>
afeecf4f 11
309113c9
RB
12/* Used as default value for git_buf->ptr so that people can always
13 * assume ptr is non-NULL and zero terminated even for new git_bufs.
14 */
a0d95962 15char git_buf__initbuf[1];
309113c9 16
a0d95962 17char git_buf__oom[1];
13224ea4 18
afeecf4f 19#define ENSURE_SIZE(b, d) \
0d0fa7c3
RB
20 if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
21 return -1;
97769280 22
afeecf4f 23
309113c9
RB
24void git_buf_init(git_buf *buf, size_t initial_size)
25{
26 buf->asize = 0;
27 buf->size = 0;
a0d95962 28 buf->ptr = git_buf__initbuf;
309113c9
RB
29
30 if (initial_size)
31 git_buf_grow(buf, initial_size);
32}
33
afeecf4f 34int git_buf_grow(git_buf *buf, size_t target_size)
97769280
RB
35{
36 int error = git_buf_try_grow(buf, target_size);
0d0fa7c3 37 if (error != 0)
a0d95962 38 buf->ptr = git_buf__oom;
97769280
RB
39 return error;
40}
41
42int git_buf_try_grow(git_buf *buf, size_t target_size)
afeecf4f
VM
43{
44 char *new_ptr;
309113c9 45 size_t new_size;
afeecf4f 46
a0d95962 47 if (buf->ptr == git_buf__oom)
0d0fa7c3 48 return -1;
afeecf4f 49
13224ea4 50 if (target_size <= buf->asize)
0d0fa7c3 51 return 0;
8c74d22e 52
309113c9
RB
53 if (buf->asize == 0) {
54 new_size = target_size;
55 new_ptr = NULL;
56 } else {
13224ea4 57 new_size = buf->asize;
309113c9
RB
58 new_ptr = buf->ptr;
59 }
afeecf4f
VM
60
61 /* grow the buffer size by 1.5, until it's big enough
62 * to fit our target size */
309113c9
RB
63 while (new_size < target_size)
64 new_size = (new_size << 1) - (new_size >> 1);
afeecf4f 65
8c74d22e 66 /* round allocation up to multiple of 8 */
309113c9 67 new_size = (new_size + 7) & ~7;
8c74d22e 68
309113c9 69 new_ptr = git__realloc(new_ptr, new_size);
97769280 70 if (!new_ptr)
cb8a7961 71 return -1;
afeecf4f 72
309113c9
RB
73 buf->asize = new_size;
74 buf->ptr = new_ptr;
75
76 /* truncate the existing buffer size if necessary */
77 if (buf->size >= buf->asize)
78 buf->size = buf->asize - 1;
79 buf->ptr[buf->size] = '\0';
80
0d0fa7c3 81 return 0;
afeecf4f
VM
82}
83
309113c9
RB
84void git_buf_free(git_buf *buf)
85{
86 if (!buf) return;
87
a0d95962 88 if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
309113c9
RB
89 git__free(buf->ptr);
90
91 git_buf_init(buf, 0);
92}
93
94void git_buf_clear(git_buf *buf)
95{
96 buf->size = 0;
97 if (buf->asize > 0)
98 buf->ptr[0] = '\0';
99}
100
97769280 101int git_buf_set(git_buf *buf, const char *data, size_t len)
8c74d22e
RB
102{
103 if (len == 0 || data == NULL) {
104 git_buf_clear(buf);
105 } else {
df743c7d
RB
106 if (data != buf->ptr) {
107 ENSURE_SIZE(buf, len + 1);
108 memmove(buf->ptr, data, len);
109 }
8c74d22e 110 buf->size = len;
c63728cd 111 buf->ptr[buf->size] = '\0';
8c74d22e 112 }
0d0fa7c3 113 return 0;
8c74d22e
RB
114}
115
97769280 116int git_buf_sets(git_buf *buf, const char *string)
8c74d22e 117{
97769280 118 return git_buf_set(buf, string, string ? strlen(string) : 0);
8c74d22e
RB
119}
120
97769280 121int git_buf_putc(git_buf *buf, char c)
afeecf4f 122{
c63728cd 123 ENSURE_SIZE(buf, buf->size + 2);
afeecf4f 124 buf->ptr[buf->size++] = c;
c63728cd 125 buf->ptr[buf->size] = '\0';
0d0fa7c3 126 return 0;
afeecf4f
VM
127}
128
97769280 129int git_buf_put(git_buf *buf, const char *data, size_t len)
afeecf4f 130{
c63728cd 131 ENSURE_SIZE(buf, buf->size + len + 1);
8c74d22e 132 memmove(buf->ptr + buf->size, data, len);
afeecf4f 133 buf->size += len;
c63728cd 134 buf->ptr[buf->size] = '\0';
0d0fa7c3 135 return 0;
afeecf4f
VM
136}
137
97769280 138int git_buf_puts(git_buf *buf, const char *string)
afeecf4f 139{
679b69c4 140 assert(string);
97769280 141 return git_buf_put(buf, string, strlen(string));
afeecf4f
VM
142}
143
039fc406
RB
144int git_buf_puts_escaped(
145 git_buf *buf, const char *string, const char *esc_chars, const char *esc_with)
146{
0c8858de
RB
147 const char *scan;
148 size_t total = 0, esc_len = strlen(esc_with), count;
039fc406 149
0c8858de
RB
150 if (!string)
151 return 0;
152
153 for (scan = string; *scan; ) {
154 /* count run of non-escaped characters */
155 count = strcspn(scan, esc_chars);
156 total += count;
157 scan += count;
158 /* count run of escaped characters */
159 count = strspn(scan, esc_chars);
160 total += count * (esc_len + 1);
161 scan += count;
039fc406
RB
162 }
163
164 ENSURE_SIZE(buf, buf->size + total + 1);
165
166 for (scan = string; *scan; ) {
0c8858de 167 count = strcspn(scan, esc_chars);
039fc406
RB
168
169 memmove(buf->ptr + buf->size, scan, count);
170 scan += count;
171 buf->size += count;
172
0c8858de
RB
173 for (count = strspn(scan, esc_chars); count > 0; --count) {
174 /* copy escape sequence */
175 memmove(buf->ptr + buf->size, esc_with, esc_len);
176 buf->size += esc_len;
177 /* copy character to be escaped */
178 buf->ptr[buf->size] = *scan;
179 buf->size++;
180 scan++;
039fc406
RB
181 }
182 }
183
54e29b93
RB
184 buf->ptr[buf->size] = '\0';
185
039fc406
RB
186 return 0;
187}
188
2d3579be
RB
189static const char b64str[64] =
190 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
191
192int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
193{
194 size_t extra = len % 3;
195 uint8_t *write, a, b, c;
196 const uint8_t *read = (const uint8_t *)data;
197
9d9288f4 198 ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1);
2d3579be
RB
199 write = (uint8_t *)&buf->ptr[buf->size];
200
201 /* convert each run of 3 bytes into 4 output bytes */
202 for (len -= extra; len > 0; len -= 3) {
203 a = *read++;
204 b = *read++;
205 c = *read++;
206
207 *write++ = b64str[a >> 2];
208 *write++ = b64str[(a & 0x03) << 4 | b >> 4];
209 *write++ = b64str[(b & 0x0f) << 2 | c >> 6];
210 *write++ = b64str[c & 0x3f];
211 }
212
213 if (extra > 0) {
214 a = *read++;
215 b = (extra > 1) ? *read++ : 0;
216
217 *write++ = b64str[a >> 2];
218 *write++ = b64str[(a & 0x03) << 4 | b >> 4];
219 *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
220 *write++ = '=';
221 }
222
223 buf->size = ((char *)write) - buf->ptr;
224 buf->ptr[buf->size] = '\0';
225
226 return 0;
227}
228
baaf1c47 229int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
afeecf4f
VM
230{
231 int len;
3f035860 232 const size_t expected_size = buf->size + (strlen(format) * 2);
afeecf4f 233
3f035860 234 ENSURE_SIZE(buf, expected_size);
afeecf4f
VM
235
236 while (1) {
baaf1c47
VM
237 va_list args;
238 va_copy(args, ap);
239
240 len = p_vsnprintf(
241 buf->ptr + buf->size,
242 buf->asize - buf->size,
243 format, args
244 );
afeecf4f
VM
245
246 if (len < 0) {
2bc8fa02 247 git__free(buf->ptr);
a0d95962 248 buf->ptr = git_buf__oom;
0d0fa7c3 249 return -1;
afeecf4f
VM
250 }
251
13224ea4 252 if ((size_t)len + 1 <= buf->asize - buf->size) {
afeecf4f 253 buf->size += len;
97769280 254 break;
afeecf4f
VM
255 }
256
257 ENSURE_SIZE(buf, buf->size + len + 1);
258 }
97769280 259
0d0fa7c3 260 return 0;
afeecf4f
VM
261}
262
baaf1c47
VM
263int git_buf_printf(git_buf *buf, const char *format, ...)
264{
265 int r;
266 va_list ap;
267
268 va_start(ap, format);
269 r = git_buf_vprintf(buf, format, ap);
270 va_end(ap);
271
272 return r;
273}
274
97769280 275void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
c63728cd
RB
276{
277 size_t copylen;
278
1dbcc9fc 279 assert(data && datasize && buf);
c63728cd
RB
280
281 data[0] = '\0';
282
283 if (buf->size == 0 || buf->asize <= 0)
284 return;
285
286 copylen = buf->size;
287 if (copylen > datasize - 1)
288 copylen = datasize - 1;
289 memmove(data, buf->ptr, copylen);
290 data[copylen] = '\0';
afeecf4f
VM
291}
292
c7c30513
CMN
293void git_buf_consume(git_buf *buf, const char *end)
294{
8c74d22e
RB
295 if (end > buf->ptr && end <= buf->ptr + buf->size) {
296 size_t consumed = end - buf->ptr;
297 memmove(buf->ptr, end, buf->size - consumed);
298 buf->size -= consumed;
c63728cd 299 buf->ptr[buf->size] = '\0';
8c74d22e
RB
300 }
301}
302
13224ea4 303void git_buf_truncate(git_buf *buf, size_t len)
97769280 304{
13224ea4 305 if (len < buf->size) {
97769280
RB
306 buf->size = len;
307 buf->ptr[buf->size] = '\0';
308 }
309}
310
b6c93aef
RB
311void git_buf_rtruncate_at_char(git_buf *buf, char separator)
312{
deafee7b
RB
313 ssize_t idx = git_buf_rfind_next(buf, separator);
314 git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
b6c93aef
RB
315}
316
8c74d22e
RB
317void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
318{
319 git_buf t = *buf_a;
320 *buf_a = *buf_b;
321 *buf_b = t;
c7c30513 322}
8c74d22e 323
97769280 324char *git_buf_detach(git_buf *buf)
8c74d22e 325{
309113c9 326 char *data = buf->ptr;
8c74d22e 327
a0d95962 328 if (buf->asize == 0 || buf->ptr == git_buf__oom)
8c74d22e
RB
329 return NULL;
330
309113c9 331 git_buf_init(buf, 0);
8c74d22e
RB
332
333 return data;
334}
335
13224ea4 336void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
8c74d22e 337{
97769280
RB
338 git_buf_free(buf);
339
340 if (ptr) {
341 buf->ptr = ptr;
342 buf->size = strlen(ptr);
343 if (asize)
344 buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
345 else /* pass 0 to fall back on strlen + 1 */
346 buf->asize = buf->size + 1;
347 } else {
348 git_buf_grow(buf, asize);
349 }
350}
8c74d22e 351
97769280
RB
352int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
353{
8c74d22e 354 va_list ap;
0d0fa7c3 355 int i;
952f94c8
RB
356 size_t total_size = 0, original_size = buf->size;
357 char *out, *original = buf->ptr;
8c74d22e
RB
358
359 if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
360 ++total_size; /* space for initial separator */
361
97769280
RB
362 /* Make two passes to avoid multiple reallocation */
363
8c74d22e
RB
364 va_start(ap, nbuf);
365 for (i = 0; i < nbuf; ++i) {
366 const char* segment;
679b69c4 367 size_t segment_len;
8c74d22e
RB
368
369 segment = va_arg(ap, const char *);
370 if (!segment)
371 continue;
372
373 segment_len = strlen(segment);
374 total_size += segment_len;
375 if (segment_len == 0 || segment[segment_len - 1] != separator)
376 ++total_size; /* space for separator */
377 }
378 va_end(ap);
379
97769280 380 /* expand buffer if needed */
952f94c8
RB
381 if (total_size == 0)
382 return 0;
383 if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
0d0fa7c3 384 return -1;
8c74d22e
RB
385
386 out = buf->ptr + buf->size;
387
388 /* append separator to existing buf if needed */
389 if (buf->size > 0 && out[-1] != separator)
390 *out++ = separator;
391
392 va_start(ap, nbuf);
393 for (i = 0; i < nbuf; ++i) {
394 const char* segment;
679b69c4 395 size_t segment_len;
8c74d22e
RB
396
397 segment = va_arg(ap, const char *);
398 if (!segment)
399 continue;
400
952f94c8
RB
401 /* deal with join that references buffer's original content */
402 if (segment >= original && segment < original + original_size) {
403 size_t offset = (segment - original);
404 segment = buf->ptr + offset;
405 segment_len = original_size - offset;
406 } else {
407 segment_len = strlen(segment);
408 }
409
8c74d22e
RB
410 /* skip leading separators */
411 if (out > buf->ptr && out[-1] == separator)
952f94c8
RB
412 while (segment_len > 0 && *segment == separator) {
413 segment++;
414 segment_len--;
415 }
8c74d22e
RB
416
417 /* copy over next buffer */
8c74d22e
RB
418 if (segment_len > 0) {
419 memmove(out, segment, segment_len);
420 out += segment_len;
421 }
422
423 /* append trailing separator (except for last item) */
424 if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
425 *out++ = separator;
426 }
427 va_end(ap);
428
429 /* set size based on num characters actually written */
430 buf->size = out - buf->ptr;
c63728cd 431 buf->ptr[buf->size] = '\0';
97769280 432
0d0fa7c3 433 return 0;
8c74d22e
RB
434}
435
97769280 436int git_buf_join(
3aa294fd
RB
437 git_buf *buf,
438 char separator,
439 const char *str_a,
440 const char *str_b)
441{
b6c93aef 442 size_t strlen_a = str_a ? strlen(str_a) : 0;
969d588d
RB
443 size_t strlen_b = strlen(str_b);
444 int need_sep = 0;
b5daae68
RB
445 ssize_t offset_a = -1;
446
447 /* not safe to have str_b point internally to the buffer */
448 assert(str_b < buf->ptr || str_b > buf->ptr + buf->size);
969d588d
RB
449
450 /* figure out if we need to insert a separator */
451 if (separator && strlen_a) {
452 while (*str_b == separator) { str_b++; strlen_b--; }
453 if (str_a[strlen_a - 1] != separator)
454 need_sep = 1;
3aa294fd 455 }
3aa294fd 456
b5daae68
RB
457 /* str_a could be part of the buffer */
458 if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
459 offset_a = str_a - buf->ptr;
460
0d0fa7c3
RB
461 if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
462 return -1;
3aa294fd 463
b5daae68
RB
464 /* fix up internal pointers */
465 if (offset_a >= 0)
466 str_a = buf->ptr + offset_a;
467
468 /* do the actual copying */
469 if (offset_a != 0)
470 memmove(buf->ptr, str_a, strlen_a);
969d588d
RB
471 if (need_sep)
472 buf->ptr[strlen_a] = separator;
b5daae68 473 memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
3aa294fd 474
969d588d 475 buf->size = strlen_a + strlen_b + need_sep;
c63728cd 476 buf->ptr[buf->size] = '\0';
97769280 477
0d0fa7c3 478 return 0;
3aa294fd 479}
13224ea4
VM
480
481void git_buf_rtrim(git_buf *buf)
482{
483 while (buf->size > 0) {
0f49200c 484 if (!git__isspace(buf->ptr[buf->size - 1]))
13224ea4
VM
485 break;
486
487 buf->size--;
488 }
eb8f90e5
VM
489
490 buf->ptr[buf->size] = '\0';
13224ea4 491}
ce49c7a8
RB
492
493int git_buf_cmp(const git_buf *a, const git_buf *b)
494{
495 int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
496 return (result != 0) ? result :
497 (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
498}
41a82592
RB
499
500int git_buf_common_prefix(git_buf *buf, const git_strarray *strings)
501{
502 size_t i;
503 const char *str, *pfx;
504
505 git_buf_clear(buf);
506
507 if (!strings || !strings->count)
508 return 0;
509
2c833917 510 /* initialize common prefix to first string */
41a82592
RB
511 if (git_buf_sets(buf, strings->strings[0]) < 0)
512 return -1;
513
2c833917 514 /* go through the rest of the strings, truncating to shared prefix */
41a82592 515 for (i = 1; i < strings->count; ++i) {
2c833917 516
41a82592
RB
517 for (str = strings->strings[i], pfx = buf->ptr;
518 *str && *str == *pfx; str++, pfx++)
519 /* scanning */;
520
521 git_buf_truncate(buf, pfx - buf->ptr);
522
523 if (!buf->size)
524 break;
525 }
526
527 return 0;
528}
b59c73d3
RB
529
530bool git_buf_is_binary(const git_buf *buf)
531{
a0d95962
RB
532 size_t i;
533 int printable = 0, nonprintable = 0;
b59c73d3
RB
534
535 for (i = 0; i < buf->size; i++) {
536 unsigned char c = buf->ptr[i];
a0d95962 537 if (c > 0x1F && c < 0x7F)
b59c73d3
RB
538 printable++;
539 else if (c == '\0')
540 return true;
541 else if (!git__isspace(c))
542 nonprintable++;
543 }
544
545 return ((printable >> 7) < nonprintable);
546}
547
02a0d651 548void git_buf_unescape(git_buf *buf)
549{
550 buf->size = git__unescape(buf->ptr);
551}