]>
Commit | Line | Data |
---|---|---|
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 | 15 | char git_buf__initbuf[1]; |
309113c9 | 16 | |
a0d95962 | 17 | char 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 |
24 | void 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 | ||
7bf87ab6 | 34 | int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom) |
afeecf4f VM |
35 | { |
36 | char *new_ptr; | |
309113c9 | 37 | size_t new_size; |
afeecf4f | 38 | |
a0d95962 | 39 | if (buf->ptr == git_buf__oom) |
0d0fa7c3 | 40 | return -1; |
afeecf4f | 41 | |
13224ea4 | 42 | if (target_size <= buf->asize) |
0d0fa7c3 | 43 | return 0; |
8c74d22e | 44 | |
309113c9 RB |
45 | if (buf->asize == 0) { |
46 | new_size = target_size; | |
47 | new_ptr = NULL; | |
48 | } else { | |
13224ea4 | 49 | new_size = buf->asize; |
309113c9 RB |
50 | new_ptr = buf->ptr; |
51 | } | |
afeecf4f VM |
52 | |
53 | /* grow the buffer size by 1.5, until it's big enough | |
54 | * to fit our target size */ | |
309113c9 RB |
55 | while (new_size < target_size) |
56 | new_size = (new_size << 1) - (new_size >> 1); | |
afeecf4f | 57 | |
8c74d22e | 58 | /* round allocation up to multiple of 8 */ |
309113c9 | 59 | new_size = (new_size + 7) & ~7; |
8c74d22e | 60 | |
309113c9 | 61 | new_ptr = git__realloc(new_ptr, new_size); |
7bf87ab6 RB |
62 | |
63 | if (!new_ptr) { | |
64 | if (mark_oom) | |
65 | buf->ptr = git_buf__oom; | |
cb8a7961 | 66 | return -1; |
7bf87ab6 | 67 | } |
afeecf4f | 68 | |
309113c9 RB |
69 | buf->asize = new_size; |
70 | buf->ptr = new_ptr; | |
71 | ||
72 | /* truncate the existing buffer size if necessary */ | |
73 | if (buf->size >= buf->asize) | |
74 | buf->size = buf->asize - 1; | |
75 | buf->ptr[buf->size] = '\0'; | |
76 | ||
0d0fa7c3 | 77 | return 0; |
afeecf4f VM |
78 | } |
79 | ||
309113c9 RB |
80 | void git_buf_free(git_buf *buf) |
81 | { | |
82 | if (!buf) return; | |
83 | ||
a0d95962 | 84 | if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom) |
309113c9 RB |
85 | git__free(buf->ptr); |
86 | ||
87 | git_buf_init(buf, 0); | |
88 | } | |
89 | ||
90 | void git_buf_clear(git_buf *buf) | |
91 | { | |
92 | buf->size = 0; | |
93 | if (buf->asize > 0) | |
94 | buf->ptr[0] = '\0'; | |
95 | } | |
96 | ||
97769280 | 97 | int git_buf_set(git_buf *buf, const char *data, size_t len) |
8c74d22e RB |
98 | { |
99 | if (len == 0 || data == NULL) { | |
100 | git_buf_clear(buf); | |
101 | } else { | |
df743c7d RB |
102 | if (data != buf->ptr) { |
103 | ENSURE_SIZE(buf, len + 1); | |
104 | memmove(buf->ptr, data, len); | |
105 | } | |
8c74d22e | 106 | buf->size = len; |
c63728cd | 107 | buf->ptr[buf->size] = '\0'; |
8c74d22e | 108 | } |
0d0fa7c3 | 109 | return 0; |
8c74d22e RB |
110 | } |
111 | ||
97769280 | 112 | int git_buf_sets(git_buf *buf, const char *string) |
8c74d22e | 113 | { |
97769280 | 114 | return git_buf_set(buf, string, string ? strlen(string) : 0); |
8c74d22e RB |
115 | } |
116 | ||
97769280 | 117 | int git_buf_putc(git_buf *buf, char c) |
afeecf4f | 118 | { |
c63728cd | 119 | ENSURE_SIZE(buf, buf->size + 2); |
afeecf4f | 120 | buf->ptr[buf->size++] = c; |
c63728cd | 121 | buf->ptr[buf->size] = '\0'; |
0d0fa7c3 | 122 | return 0; |
afeecf4f VM |
123 | } |
124 | ||
97769280 | 125 | int git_buf_put(git_buf *buf, const char *data, size_t len) |
afeecf4f | 126 | { |
c63728cd | 127 | ENSURE_SIZE(buf, buf->size + len + 1); |
8c74d22e | 128 | memmove(buf->ptr + buf->size, data, len); |
afeecf4f | 129 | buf->size += len; |
c63728cd | 130 | buf->ptr[buf->size] = '\0'; |
0d0fa7c3 | 131 | return 0; |
afeecf4f VM |
132 | } |
133 | ||
97769280 | 134 | int git_buf_puts(git_buf *buf, const char *string) |
afeecf4f | 135 | { |
679b69c4 | 136 | assert(string); |
97769280 | 137 | return git_buf_put(buf, string, strlen(string)); |
afeecf4f VM |
138 | } |
139 | ||
2d3579be RB |
140 | static const char b64str[64] = |
141 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
142 | ||
143 | int git_buf_put_base64(git_buf *buf, const char *data, size_t len) | |
144 | { | |
145 | size_t extra = len % 3; | |
146 | uint8_t *write, a, b, c; | |
147 | const uint8_t *read = (const uint8_t *)data; | |
148 | ||
9d9288f4 | 149 | ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1); |
2d3579be RB |
150 | write = (uint8_t *)&buf->ptr[buf->size]; |
151 | ||
152 | /* convert each run of 3 bytes into 4 output bytes */ | |
153 | for (len -= extra; len > 0; len -= 3) { | |
154 | a = *read++; | |
155 | b = *read++; | |
156 | c = *read++; | |
157 | ||
158 | *write++ = b64str[a >> 2]; | |
159 | *write++ = b64str[(a & 0x03) << 4 | b >> 4]; | |
160 | *write++ = b64str[(b & 0x0f) << 2 | c >> 6]; | |
161 | *write++ = b64str[c & 0x3f]; | |
162 | } | |
163 | ||
164 | if (extra > 0) { | |
165 | a = *read++; | |
166 | b = (extra > 1) ? *read++ : 0; | |
167 | ||
168 | *write++ = b64str[a >> 2]; | |
169 | *write++ = b64str[(a & 0x03) << 4 | b >> 4]; | |
170 | *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '='; | |
171 | *write++ = '='; | |
172 | } | |
173 | ||
174 | buf->size = ((char *)write) - buf->ptr; | |
175 | buf->ptr[buf->size] = '\0'; | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
baaf1c47 | 180 | int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) |
afeecf4f VM |
181 | { |
182 | int len; | |
3f035860 | 183 | const size_t expected_size = buf->size + (strlen(format) * 2); |
afeecf4f | 184 | |
3f035860 | 185 | ENSURE_SIZE(buf, expected_size); |
afeecf4f VM |
186 | |
187 | while (1) { | |
baaf1c47 VM |
188 | va_list args; |
189 | va_copy(args, ap); | |
190 | ||
191 | len = p_vsnprintf( | |
192 | buf->ptr + buf->size, | |
193 | buf->asize - buf->size, | |
194 | format, args | |
195 | ); | |
afeecf4f VM |
196 | |
197 | if (len < 0) { | |
2bc8fa02 | 198 | git__free(buf->ptr); |
a0d95962 | 199 | buf->ptr = git_buf__oom; |
0d0fa7c3 | 200 | return -1; |
afeecf4f VM |
201 | } |
202 | ||
13224ea4 | 203 | if ((size_t)len + 1 <= buf->asize - buf->size) { |
afeecf4f | 204 | buf->size += len; |
97769280 | 205 | break; |
afeecf4f VM |
206 | } |
207 | ||
208 | ENSURE_SIZE(buf, buf->size + len + 1); | |
209 | } | |
97769280 | 210 | |
0d0fa7c3 | 211 | return 0; |
afeecf4f VM |
212 | } |
213 | ||
baaf1c47 VM |
214 | int git_buf_printf(git_buf *buf, const char *format, ...) |
215 | { | |
216 | int r; | |
217 | va_list ap; | |
218 | ||
219 | va_start(ap, format); | |
220 | r = git_buf_vprintf(buf, format, ap); | |
221 | va_end(ap); | |
222 | ||
223 | return r; | |
224 | } | |
225 | ||
97769280 | 226 | void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf) |
c63728cd RB |
227 | { |
228 | size_t copylen; | |
229 | ||
1dbcc9fc | 230 | assert(data && datasize && buf); |
c63728cd RB |
231 | |
232 | data[0] = '\0'; | |
233 | ||
234 | if (buf->size == 0 || buf->asize <= 0) | |
235 | return; | |
236 | ||
237 | copylen = buf->size; | |
238 | if (copylen > datasize - 1) | |
239 | copylen = datasize - 1; | |
240 | memmove(data, buf->ptr, copylen); | |
241 | data[copylen] = '\0'; | |
afeecf4f VM |
242 | } |
243 | ||
c7c30513 CMN |
244 | void git_buf_consume(git_buf *buf, const char *end) |
245 | { | |
8c74d22e RB |
246 | if (end > buf->ptr && end <= buf->ptr + buf->size) { |
247 | size_t consumed = end - buf->ptr; | |
248 | memmove(buf->ptr, end, buf->size - consumed); | |
249 | buf->size -= consumed; | |
c63728cd | 250 | buf->ptr[buf->size] = '\0'; |
8c74d22e RB |
251 | } |
252 | } | |
253 | ||
13224ea4 | 254 | void git_buf_truncate(git_buf *buf, size_t len) |
97769280 | 255 | { |
13224ea4 | 256 | if (len < buf->size) { |
97769280 RB |
257 | buf->size = len; |
258 | buf->ptr[buf->size] = '\0'; | |
259 | } | |
260 | } | |
261 | ||
b6c93aef RB |
262 | void git_buf_rtruncate_at_char(git_buf *buf, char separator) |
263 | { | |
deafee7b RB |
264 | ssize_t idx = git_buf_rfind_next(buf, separator); |
265 | git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx); | |
b6c93aef RB |
266 | } |
267 | ||
8c74d22e RB |
268 | void git_buf_swap(git_buf *buf_a, git_buf *buf_b) |
269 | { | |
270 | git_buf t = *buf_a; | |
271 | *buf_a = *buf_b; | |
272 | *buf_b = t; | |
c7c30513 | 273 | } |
8c74d22e | 274 | |
97769280 | 275 | char *git_buf_detach(git_buf *buf) |
8c74d22e | 276 | { |
309113c9 | 277 | char *data = buf->ptr; |
8c74d22e | 278 | |
a0d95962 | 279 | if (buf->asize == 0 || buf->ptr == git_buf__oom) |
8c74d22e RB |
280 | return NULL; |
281 | ||
309113c9 | 282 | git_buf_init(buf, 0); |
8c74d22e RB |
283 | |
284 | return data; | |
285 | } | |
286 | ||
13224ea4 | 287 | void git_buf_attach(git_buf *buf, char *ptr, size_t asize) |
8c74d22e | 288 | { |
97769280 RB |
289 | git_buf_free(buf); |
290 | ||
291 | if (ptr) { | |
292 | buf->ptr = ptr; | |
293 | buf->size = strlen(ptr); | |
294 | if (asize) | |
295 | buf->asize = (asize < buf->size) ? buf->size + 1 : asize; | |
296 | else /* pass 0 to fall back on strlen + 1 */ | |
297 | buf->asize = buf->size + 1; | |
298 | } else { | |
299 | git_buf_grow(buf, asize); | |
300 | } | |
301 | } | |
8c74d22e | 302 | |
97769280 RB |
303 | int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) |
304 | { | |
8c74d22e | 305 | va_list ap; |
0d0fa7c3 | 306 | int i; |
952f94c8 RB |
307 | size_t total_size = 0, original_size = buf->size; |
308 | char *out, *original = buf->ptr; | |
8c74d22e RB |
309 | |
310 | if (buf->size > 0 && buf->ptr[buf->size - 1] != separator) | |
311 | ++total_size; /* space for initial separator */ | |
312 | ||
97769280 RB |
313 | /* Make two passes to avoid multiple reallocation */ |
314 | ||
8c74d22e RB |
315 | va_start(ap, nbuf); |
316 | for (i = 0; i < nbuf; ++i) { | |
317 | const char* segment; | |
679b69c4 | 318 | size_t segment_len; |
8c74d22e RB |
319 | |
320 | segment = va_arg(ap, const char *); | |
321 | if (!segment) | |
322 | continue; | |
323 | ||
324 | segment_len = strlen(segment); | |
325 | total_size += segment_len; | |
326 | if (segment_len == 0 || segment[segment_len - 1] != separator) | |
327 | ++total_size; /* space for separator */ | |
328 | } | |
329 | va_end(ap); | |
330 | ||
97769280 | 331 | /* expand buffer if needed */ |
952f94c8 RB |
332 | if (total_size == 0) |
333 | return 0; | |
334 | if (git_buf_grow(buf, buf->size + total_size + 1) < 0) | |
0d0fa7c3 | 335 | return -1; |
8c74d22e RB |
336 | |
337 | out = buf->ptr + buf->size; | |
338 | ||
339 | /* append separator to existing buf if needed */ | |
340 | if (buf->size > 0 && out[-1] != separator) | |
341 | *out++ = separator; | |
342 | ||
343 | va_start(ap, nbuf); | |
344 | for (i = 0; i < nbuf; ++i) { | |
345 | const char* segment; | |
679b69c4 | 346 | size_t segment_len; |
8c74d22e RB |
347 | |
348 | segment = va_arg(ap, const char *); | |
349 | if (!segment) | |
350 | continue; | |
351 | ||
952f94c8 RB |
352 | /* deal with join that references buffer's original content */ |
353 | if (segment >= original && segment < original + original_size) { | |
354 | size_t offset = (segment - original); | |
355 | segment = buf->ptr + offset; | |
356 | segment_len = original_size - offset; | |
357 | } else { | |
358 | segment_len = strlen(segment); | |
359 | } | |
360 | ||
8c74d22e RB |
361 | /* skip leading separators */ |
362 | if (out > buf->ptr && out[-1] == separator) | |
952f94c8 RB |
363 | while (segment_len > 0 && *segment == separator) { |
364 | segment++; | |
365 | segment_len--; | |
366 | } | |
8c74d22e RB |
367 | |
368 | /* copy over next buffer */ | |
8c74d22e RB |
369 | if (segment_len > 0) { |
370 | memmove(out, segment, segment_len); | |
371 | out += segment_len; | |
372 | } | |
373 | ||
374 | /* append trailing separator (except for last item) */ | |
375 | if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator) | |
376 | *out++ = separator; | |
377 | } | |
378 | va_end(ap); | |
379 | ||
380 | /* set size based on num characters actually written */ | |
381 | buf->size = out - buf->ptr; | |
c63728cd | 382 | buf->ptr[buf->size] = '\0'; |
97769280 | 383 | |
0d0fa7c3 | 384 | return 0; |
8c74d22e RB |
385 | } |
386 | ||
97769280 | 387 | int git_buf_join( |
3aa294fd RB |
388 | git_buf *buf, |
389 | char separator, | |
390 | const char *str_a, | |
391 | const char *str_b) | |
392 | { | |
b6c93aef | 393 | size_t strlen_a = str_a ? strlen(str_a) : 0; |
969d588d RB |
394 | size_t strlen_b = strlen(str_b); |
395 | int need_sep = 0; | |
b5daae68 RB |
396 | ssize_t offset_a = -1; |
397 | ||
398 | /* not safe to have str_b point internally to the buffer */ | |
399 | assert(str_b < buf->ptr || str_b > buf->ptr + buf->size); | |
969d588d RB |
400 | |
401 | /* figure out if we need to insert a separator */ | |
402 | if (separator && strlen_a) { | |
403 | while (*str_b == separator) { str_b++; strlen_b--; } | |
404 | if (str_a[strlen_a - 1] != separator) | |
405 | need_sep = 1; | |
3aa294fd | 406 | } |
3aa294fd | 407 | |
b5daae68 RB |
408 | /* str_a could be part of the buffer */ |
409 | if (str_a >= buf->ptr && str_a < buf->ptr + buf->size) | |
410 | offset_a = str_a - buf->ptr; | |
411 | ||
0d0fa7c3 RB |
412 | if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0) |
413 | return -1; | |
3aa294fd | 414 | |
b5daae68 RB |
415 | /* fix up internal pointers */ |
416 | if (offset_a >= 0) | |
417 | str_a = buf->ptr + offset_a; | |
418 | ||
419 | /* do the actual copying */ | |
420 | if (offset_a != 0) | |
421 | memmove(buf->ptr, str_a, strlen_a); | |
969d588d RB |
422 | if (need_sep) |
423 | buf->ptr[strlen_a] = separator; | |
b5daae68 | 424 | memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b); |
3aa294fd | 425 | |
969d588d | 426 | buf->size = strlen_a + strlen_b + need_sep; |
c63728cd | 427 | buf->ptr[buf->size] = '\0'; |
97769280 | 428 | |
0d0fa7c3 | 429 | return 0; |
3aa294fd | 430 | } |
13224ea4 VM |
431 | |
432 | void git_buf_rtrim(git_buf *buf) | |
433 | { | |
434 | while (buf->size > 0) { | |
0f49200c | 435 | if (!git__isspace(buf->ptr[buf->size - 1])) |
13224ea4 VM |
436 | break; |
437 | ||
438 | buf->size--; | |
439 | } | |
eb8f90e5 VM |
440 | |
441 | buf->ptr[buf->size] = '\0'; | |
13224ea4 | 442 | } |
ce49c7a8 RB |
443 | |
444 | int git_buf_cmp(const git_buf *a, const git_buf *b) | |
445 | { | |
446 | int result = memcmp(a->ptr, b->ptr, min(a->size, b->size)); | |
447 | return (result != 0) ? result : | |
448 | (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0; | |
449 | } | |
41a82592 | 450 | |
3a14d3e2 | 451 | int git_buf_splice( |
452 | git_buf *buf, | |
453 | size_t where, | |
454 | size_t nb_to_remove, | |
455 | const char *data, | |
456 | size_t nb_to_insert) | |
457 | { | |
458 | assert(buf && | |
459 | where <= git_buf_len(buf) && | |
460 | where + nb_to_remove <= git_buf_len(buf)); | |
461 | ||
462 | /* Ported from git.git | |
463 | * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176 | |
464 | */ | |
465 | if (git_buf_grow(buf, git_buf_len(buf) + nb_to_insert - nb_to_remove) < 0) | |
466 | return -1; | |
467 | ||
468 | memmove(buf->ptr + where + nb_to_insert, | |
469 | buf->ptr + where + nb_to_remove, | |
470 | buf->size - where - nb_to_remove); | |
471 | ||
472 | memcpy(buf->ptr + where, data, nb_to_insert); | |
473 | ||
474 | buf->size = buf->size + nb_to_insert - nb_to_remove; | |
475 | buf->ptr[buf->size] = '\0'; | |
476 | return 0; | |
477 | } |