Created
August 16, 2014 08:36
-
-
Save leeonix/19b28f783ebd9dc75a8c to your computer and use it in GitHub Desktop.
git's strbuf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <ctype.h> | |
#include <fcntl.h> | |
#include <stddef.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <malloc.h> | |
#include <errno.h> | |
#include <limits.h> | |
#include <io.h> | |
#include "strbuf.h" | |
#define alloc_nr(x) (((x)+16)*3/2) | |
/* | |
* Realloc the buffer pointed at by variable 'x' so that it can hold | |
* at least 'nr' entries; the number of entries currently allocated | |
* is 'alloc', using the standard growing factor alloc_nr() macro. | |
* | |
* DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'. | |
*/ | |
#define ALLOC_GROW(x, nr, alloc) \ | |
do { \ | |
if ((nr) > alloc) { \ | |
if (alloc_nr(alloc) < (nr)) \ | |
alloc = (nr); \ | |
else \ | |
alloc = alloc_nr(alloc); \ | |
x = xrealloc((x), alloc * sizeof(*(x))); \ | |
} \ | |
} while (0) | |
void die(const char *err, ...) | |
{ | |
char msg[4096]; | |
va_list params; | |
va_start(params, err); | |
vsnprintf(msg, sizeof(msg), err, params); | |
fprintf(stderr, "%s\n", msg); | |
va_end(params); | |
exit(1); | |
} | |
static void *xmalloc(size_t size) | |
{ | |
void *ret = malloc(size); | |
if (!ret && !size) | |
ret = malloc(1); | |
if (!ret) | |
die("Out of memory"); | |
return ret; | |
} | |
static void *xrealloc(void *ptr, size_t size) | |
{ | |
void *ret = realloc(ptr, size); | |
if (!ret && !size) | |
ret = malloc(1); | |
if (!ret) | |
die("Out of memory"); | |
return ret; | |
} | |
int starts_with(const char *str, const char *prefix) | |
{ | |
for (; ; str++, prefix++) | |
if (!*prefix) | |
return 1; | |
else if (*str != *prefix) | |
return 0; | |
} | |
int ends_with(const char *str, const char *suffix) | |
{ | |
int len = strlen(str), suflen = strlen(suffix); | |
if (len < suflen) | |
return 0; | |
else | |
return !strcmp(str + len - suflen, suffix); | |
} | |
/* | |
* Used as the default ->buf value, so that people can always assume | |
* buf is non NULL and ->buf is NUL terminated even for a freshly | |
* initialized strbuf. | |
*/ | |
char strbuf_slopbuf[1]; | |
void strbuf_init(struct strbuf *sb, size_t hint) | |
{ | |
sb->alloc = sb->len = 0; | |
sb->buf = strbuf_slopbuf; | |
if (hint) | |
strbuf_grow(sb, hint); | |
} | |
void strbuf_release(struct strbuf *sb) | |
{ | |
if (sb->alloc) { | |
free(sb->buf); | |
strbuf_init(sb, 0); | |
} | |
} | |
char *strbuf_detach(struct strbuf *sb, size_t *sz) | |
{ | |
char *res; | |
strbuf_grow(sb, 0); | |
res = sb->buf; | |
if (sz) | |
*sz = sb->len; | |
strbuf_init(sb, 0); | |
return res; | |
} | |
void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc) | |
{ | |
strbuf_release(sb); | |
sb->buf = buf; | |
sb->len = len; | |
sb->alloc = alloc; | |
strbuf_grow(sb, 0); | |
sb->buf[sb->len] = '\0'; | |
} | |
void strbuf_grow(struct strbuf *sb, size_t extra) | |
{ | |
int new_buf = !sb->alloc; | |
if (unsigned_add_overflows(extra, 1) || | |
unsigned_add_overflows(sb->len, extra + 1)) | |
die("you want to use way too much memory"); | |
if (new_buf) | |
sb->buf = NULL; | |
ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); | |
if (new_buf) | |
sb->buf[0] = '\0'; | |
} | |
void strbuf_trim(struct strbuf *sb) | |
{ | |
strbuf_rtrim(sb); | |
strbuf_ltrim(sb); | |
} | |
void strbuf_rtrim(struct strbuf *sb) | |
{ | |
while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) | |
sb->len--; | |
sb->buf[sb->len] = '\0'; | |
} | |
void strbuf_ltrim(struct strbuf *sb) | |
{ | |
char *b = sb->buf; | |
while (sb->len > 0 && isspace(*b)) { | |
b++; | |
sb->len--; | |
} | |
memmove(sb->buf, b, sb->len); | |
sb->buf[sb->len] = '\0'; | |
} | |
void strbuf_tolower(struct strbuf *sb) | |
{ | |
char *p = sb->buf, *end = sb->buf + sb->len; | |
for (; p < end; p++) | |
*p = tolower(*p); | |
} | |
struct strbuf **strbuf_split_buf(const char *str, size_t slen, | |
int terminator, int max) | |
{ | |
struct strbuf **ret = NULL; | |
size_t nr = 0, alloc = 0; | |
struct strbuf *t; | |
while (slen) { | |
int len = slen; | |
if (max <= 0 || nr + 1 < (size_t) max) { | |
const char *end = memchr(str, terminator, slen); | |
if (end) | |
len = end - str + 1; | |
} | |
t = xmalloc(sizeof(struct strbuf)); | |
strbuf_init(t, len); | |
strbuf_add(t, str, len); | |
ALLOC_GROW(ret, nr + 2, alloc); | |
ret[nr++] = t; | |
str += len; | |
slen -= len; | |
} | |
ALLOC_GROW(ret, nr + 1, alloc); /* In case string was empty */ | |
ret[nr] = NULL; | |
return ret; | |
} | |
void strbuf_list_free(struct strbuf **sbs) | |
{ | |
struct strbuf **s = sbs; | |
while (*s) { | |
strbuf_release(*s); | |
free(*s++); | |
} | |
free(sbs); | |
} | |
int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) | |
{ | |
int len = a->len < b->len ? a->len: b->len; | |
int cmp = memcmp(a->buf, b->buf, len); | |
if (cmp) | |
return cmp; | |
return a->len < b->len ? -1: a->len != b->len; | |
} | |
void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, | |
const void *data, size_t dlen) | |
{ | |
if (unsigned_add_overflows(pos, len)) | |
die("you want to use way too much memory"); | |
if (pos > sb->len) | |
die("`pos' is too far after the end of the buffer"); | |
if (pos + len > sb->len) | |
die("`pos + len' is too far after the end of the buffer"); | |
if (dlen >= len) | |
strbuf_grow(sb, dlen - len); | |
memmove(sb->buf + pos + dlen, | |
sb->buf + pos + len, | |
sb->len - pos - len); | |
memcpy(sb->buf + pos, data, dlen); | |
strbuf_setlen(sb, sb->len + dlen - len); | |
} | |
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) | |
{ | |
strbuf_splice(sb, pos, 0, data, len); | |
} | |
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) | |
{ | |
strbuf_splice(sb, pos, len, NULL, 0); | |
} | |
void strbuf_add(struct strbuf *sb, const void *data, size_t len) | |
{ | |
strbuf_grow(sb, len); | |
memcpy(sb->buf + sb->len, data, len); | |
strbuf_setlen(sb, sb->len + len); | |
} | |
void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) | |
{ | |
strbuf_grow(sb, len); | |
memcpy(sb->buf + sb->len, sb->buf + pos, len); | |
strbuf_setlen(sb, sb->len + len); | |
} | |
void strbuf_addf(struct strbuf *sb, const char *fmt, ...) | |
{ | |
va_list ap; | |
va_start(ap, fmt); | |
strbuf_vaddf(sb, fmt, ap); | |
va_end(ap); | |
} | |
static void add_lines(struct strbuf *out, | |
const char *prefix1, | |
const char *prefix2, | |
const char *buf, size_t size) | |
{ | |
while (size) { | |
const char *prefix; | |
const char *next = memchr(buf, '\n', size); | |
next = next ? (next + 1) : (buf + size); | |
prefix = (prefix2 && buf[0] == '\n') ? prefix2 : prefix1; | |
strbuf_addstr(out, prefix); | |
strbuf_add(out, buf, next - buf); | |
size -= next - buf; | |
buf = next; | |
} | |
strbuf_complete_line(out); | |
} | |
char comment_line_char = '#'; | |
void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size) | |
{ | |
static char prefix1[3]; | |
static char prefix2[2]; | |
if (prefix1[0] != comment_line_char) { | |
sprintf(prefix1, "%c ", comment_line_char); | |
sprintf(prefix2, "%c", comment_line_char); | |
} | |
add_lines(out, prefix1, prefix2, buf, size); | |
} | |
void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...) | |
{ | |
va_list params; | |
struct strbuf buf = STRBUF_INIT; | |
int incomplete_line = sb->len && sb->buf[sb->len - 1] != '\n'; | |
va_start(params, fmt); | |
strbuf_vaddf(&buf, fmt, params); | |
va_end(params); | |
strbuf_add_commented_lines(sb, buf.buf, buf.len); | |
if (incomplete_line) | |
sb->buf[--sb->len] = '\0'; | |
strbuf_release(&buf); | |
} | |
void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap) | |
{ | |
size_t len; | |
va_list cp; | |
if (!strbuf_avail(sb)) | |
strbuf_grow(sb, 64); | |
va_copy(cp, ap); | |
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, cp); | |
va_end(cp); | |
if (len < 0) | |
die("BUG: your vsnprintf is broken (returned %d)", len); | |
if (len > strbuf_avail(sb)) { | |
strbuf_grow(sb, len); | |
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | |
if (len > strbuf_avail(sb)) | |
die("BUG: your vsnprintf is broken (insatiable)"); | |
} | |
strbuf_setlen(sb, sb->len + len); | |
} | |
void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, | |
void *context) | |
{ | |
for (;;) { | |
const char *percent; | |
size_t consumed; | |
percent = (const char *) strchrnul(format, '%'); | |
strbuf_add(sb, format, percent - format); | |
if (!*percent) | |
break; | |
format = percent + 1; | |
if (*format == '%') { | |
strbuf_addch(sb, '%'); | |
format++; | |
continue; | |
} | |
consumed = fn(sb, format, context); | |
if (consumed) | |
format += consumed; | |
else | |
strbuf_addch(sb, '%'); | |
} | |
} | |
size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, | |
void *context) | |
{ | |
struct strbuf_expand_dict_entry *e = context; | |
size_t len; | |
for (; e->placeholder && (len = strlen(e->placeholder)); e++) { | |
if (!strncmp(placeholder, e->placeholder, len)) { | |
if (e->value) | |
strbuf_addstr(sb, e->value); | |
return len; | |
} | |
} | |
return 0; | |
} | |
void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src) | |
{ | |
int i, len = src->len; | |
for (i = 0; i < len; i++) { | |
if (src->buf[i] == '%') | |
strbuf_addch(dst, '%'); | |
strbuf_addch(dst, src->buf[i]); | |
} | |
} | |
void strbuf_add_lines(struct strbuf *out, const char *prefix, | |
const char *buf, size_t size) | |
{ | |
add_lines(out, prefix, NULL, buf, size); | |
} | |
void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s) | |
{ | |
while (*s) { | |
size_t len = strcspn(s, "\"<>&"); | |
strbuf_add(buf, s, len); | |
s += len; | |
switch (*s) { | |
case '"': | |
strbuf_addstr(buf, """); | |
break; | |
case '<': | |
strbuf_addstr(buf, "<"); | |
break; | |
case '>': | |
strbuf_addstr(buf, ">"); | |
break; | |
case '&': | |
strbuf_addstr(buf, "&"); | |
break; | |
case 0: | |
return; | |
} | |
s++; | |
} | |
} | |
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) | |
{ | |
size_t res; | |
size_t oldalloc = sb->alloc; | |
strbuf_grow(sb, size); | |
res = fread(sb->buf + sb->len, 1, size, f); | |
if (res > 0) | |
strbuf_setlen(sb, sb->len + res); | |
else if (oldalloc == 0) | |
strbuf_release(sb); | |
return res; | |
} | |
int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) | |
{ | |
int ch; | |
if (feof(fp)) | |
return EOF; | |
strbuf_reset(sb); | |
while ((ch = fgetc(fp)) != EOF) { | |
strbuf_grow(sb, 1); | |
sb->buf[sb->len++] = ch; | |
if (ch == term) | |
break; | |
} | |
if (ch == EOF && sb->len == 0) | |
return EOF; | |
sb->buf[sb->len] = '\0'; | |
return 0; | |
} | |
int strbuf_getline(struct strbuf *sb, FILE *fp, int term) | |
{ | |
if (strbuf_getwholeline(sb, fp, term)) | |
return EOF; | |
if (sb->buf[sb->len-1] == term) | |
strbuf_setlen(sb, sb->len-1); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* copy from git source | |
*/ | |
#ifndef STRBUF_H | |
#define STRBUF_H | |
#ifdef STRBUF_USEDLL | |
# ifdef STRBUF_EXPORTS | |
# define STRBUF_API __declspec(dllexport) | |
# else | |
# define STRBUF_API __declspec(dllimport) | |
# endif | |
#else | |
# define STRBUF_API extern | |
#endif | |
#define inline __inline | |
#define __inline__ __inline | |
typedef int ssize_t; | |
typedef long long intmax_t; | |
typedef unsigned long long uintmax_t; | |
#define INTMAX_MIN _I64_MIN | |
#define INTMAX_MAX _I64_MAX | |
#define UINTMAX_MAX _UI64_MAX | |
#define UINT32_MAX 0xffffffff /* 4294967295U */ | |
#define bitsizeof(x) (CHAR_BIT * sizeof(x)) | |
#define maximum_signed_value_of_type(a) \ | |
(INTMAX_MAX >> (bitsizeof(intmax_t) - bitsizeof(a))) | |
#define maximum_unsigned_value_of_type(a) \ | |
(UINTMAX_MAX >> (bitsizeof(uintmax_t) - bitsizeof(a))) | |
/* | |
* Signed integer overflow is undefined in C, so here's a helper macro | |
* to detect if the sum of two integers will overflow. | |
* | |
* Requires: a >= 0, typeof(a) equals typeof(b) | |
*/ | |
#define signed_add_overflows(a, b) \ | |
((b) > maximum_signed_value_of_type(a) - (a)) | |
#define unsigned_add_overflows(a, b) \ | |
((b) > maximum_unsigned_value_of_type(a) - (a)) | |
#ifndef va_copy | |
/* | |
* Since an obvious implementation of va_list would be to make it a | |
* pointer into the stack frame, a simple assignment will work on | |
* many systems. But let's try to be more portable. | |
*/ | |
# ifdef __va_copy | |
# define va_copy(dst, src) __va_copy(dst, src) | |
# else | |
# define va_copy(dst, src) ((dst) = (src)) | |
# endif | |
#endif | |
void die(const char *err, ...); | |
static inline char *strchrnul(const char *s, int c) | |
{ | |
while (*s && *s != c) | |
s++; | |
return (char *)s; | |
} | |
/* See Documentation/technical/api-strbuf.txt */ | |
STRBUF_API char strbuf_slopbuf[]; | |
struct strbuf { | |
size_t alloc; | |
size_t len; | |
char *buf; | |
}; | |
#define STRBUF_INIT { 0, 0, strbuf_slopbuf } | |
/*----- strbuf life cycle -----*/ | |
STRBUF_API void strbuf_init(struct strbuf *, size_t); | |
STRBUF_API void strbuf_release(struct strbuf *); | |
STRBUF_API char *strbuf_detach(struct strbuf *, size_t *); | |
STRBUF_API void strbuf_attach(struct strbuf *, void *, size_t, size_t); | |
static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) | |
{ | |
struct strbuf tmp = *a; | |
*a = *b; | |
*b = tmp; | |
} | |
/*----- strbuf size related -----*/ | |
static inline size_t strbuf_avail(const struct strbuf *sb) | |
{ | |
return sb->alloc ? sb->alloc - sb->len - 1 : 0; | |
} | |
STRBUF_API void strbuf_grow(struct strbuf *, size_t); | |
static inline void strbuf_setlen(struct strbuf *sb, size_t len) | |
{ | |
if (len > (sb->alloc ? sb->alloc - 1 : 0)) | |
die("BUG: strbuf_setlen() beyond buffer"); | |
sb->len = len; | |
sb->buf[len] = '\0'; | |
} | |
#define strbuf_reset(sb) strbuf_setlen(sb, 0) | |
/*----- content related -----*/ | |
STRBUF_API void strbuf_trim(struct strbuf *); | |
STRBUF_API void strbuf_rtrim(struct strbuf *); | |
STRBUF_API void strbuf_ltrim(struct strbuf *); | |
STRBUF_API void strbuf_tolower(struct strbuf *sb); | |
STRBUF_API int strbuf_cmp(const struct strbuf *, const struct strbuf *); | |
/* | |
* Split str (of length slen) at the specified terminator character. | |
* Return a null-terminated array of pointers to strbuf objects | |
* holding the substrings. The substrings include the terminator, | |
* except for the last substring, which might be unterminated if the | |
* original string did not end with a terminator. If max is positive, | |
* then split the string into at most max substrings (with the last | |
* substring containing everything following the (max-1)th terminator | |
* character). | |
* | |
* For lighter-weight alternatives, see string_list_split() and | |
* string_list_split_in_place(). | |
*/ | |
STRBUF_API struct strbuf **strbuf_split_buf(const char *, size_t, | |
int terminator, int max); | |
/* | |
* Split a NUL-terminated string at the specified terminator | |
* character. See strbuf_split_buf() for more information. | |
*/ | |
static inline struct strbuf **strbuf_split_str(const char *str, | |
int terminator, int max) | |
{ | |
return strbuf_split_buf(str, strlen(str), terminator, max); | |
} | |
/* | |
* Split a strbuf at the specified terminator character. See | |
* strbuf_split_buf() for more information. | |
*/ | |
static inline struct strbuf **strbuf_split_max(const struct strbuf *sb, | |
int terminator, int max) | |
{ | |
return strbuf_split_buf(sb->buf, sb->len, terminator, max); | |
} | |
/* | |
* Split a strbuf at the specified terminator character. See | |
* strbuf_split_buf() for more information. | |
*/ | |
static inline struct strbuf **strbuf_split(const struct strbuf *sb, | |
int terminator) | |
{ | |
return strbuf_split_max(sb, terminator, 0); | |
} | |
/* | |
* Free a NULL-terminated list of strbufs (for example, the return | |
* values of the strbuf_split*() functions). | |
*/ | |
STRBUF_API void strbuf_list_free(struct strbuf **); | |
/*----- add data in your buffer -----*/ | |
static inline void strbuf_addch(struct strbuf *sb, int c) | |
{ | |
strbuf_grow(sb, 1); | |
sb->buf[sb->len++] = c; | |
sb->buf[sb->len] = '\0'; | |
} | |
STRBUF_API void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); | |
STRBUF_API void strbuf_remove(struct strbuf *, size_t pos, size_t len); | |
/* splice pos..pos+len with given data */ | |
STRBUF_API void strbuf_splice(struct strbuf *, size_t pos, size_t len, | |
const void *, size_t); | |
STRBUF_API char comment_line_char; | |
STRBUF_API void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size); | |
STRBUF_API void strbuf_add(struct strbuf *, const void *, size_t); | |
static inline void strbuf_addstr(struct strbuf *sb, const char *s) | |
{ | |
strbuf_add(sb, s, strlen(s)); | |
} | |
static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) | |
{ | |
strbuf_grow(sb, sb2->len); | |
strbuf_add(sb, sb2->buf, sb2->len); | |
} | |
STRBUF_API void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); | |
typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); | |
STRBUF_API void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context); | |
struct strbuf_expand_dict_entry { | |
const char *placeholder; | |
const char *value; | |
}; | |
STRBUF_API size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context); | |
STRBUF_API void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src); | |
STRBUF_API void strbuf_addf(struct strbuf *sb, const char *fmt, ...); | |
STRBUF_API void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...); | |
STRBUF_API void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); | |
STRBUF_API void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size); | |
/* | |
* Append s to sb, with the characters '<', '>', '&' and '"' converted | |
* into XML entities. | |
*/ | |
STRBUF_API void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s); | |
static inline void strbuf_complete_line(struct strbuf *sb) | |
{ | |
if (sb->len && sb->buf[sb->len - 1] != '\n') | |
strbuf_addch(sb, '\n'); | |
} | |
STRBUF_API size_t strbuf_fread(struct strbuf *, size_t, FILE *); | |
STRBUF_API int strbuf_getwholeline(struct strbuf *, FILE *, int); | |
STRBUF_API int strbuf_getline(struct strbuf *, FILE *, int); | |
#endif /* STRBUF_H */ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment