forked from public/pyceo
parent
235b223e4b
commit
f5a71b6b32
@ -0,0 +1,435 @@ |
||||
/*
|
||||
Copyright (c) 2005-2008 Junio C. Hamano et al. |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License (version 2) |
||||
as published by the Free Software Foundation. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software |
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <ctype.h> |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
|
||||
#include "strbuf.h" |
||||
#include "util.h" |
||||
|
||||
#define die fatal |
||||
|
||||
#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' 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) |
||||
|
||||
|
||||
static inline char *gitstrchrnul(const char *s, int c) |
||||
{ |
||||
while (*s && *s != c) |
||||
s++; |
||||
return (char *)s; |
||||
} |
||||
|
||||
|
||||
int prefixcmp(const char *str, const char *prefix) |
||||
{ |
||||
for (; ; str++, prefix++) |
||||
if (!*prefix) |
||||
return 0; |
||||
else if (*str != *prefix) |
||||
return (unsigned char)*prefix - (unsigned char)*str; |
||||
} |
||||
|
||||
/*
|
||||
* 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 = sb->alloc ? sb->buf : NULL; |
||||
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) |
||||
{ |
||||
if (sb->len + extra + 1 <= sb->len) |
||||
die("you want to use way too much memory"); |
||||
if (!sb->alloc) |
||||
sb->buf = NULL; |
||||
ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); |
||||
} |
||||
|
||||
void strbuf_trim(struct strbuf *sb) |
||||
{ |
||||
char *b = sb->buf; |
||||
while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) |
||||
sb->len--; |
||||
while (sb->len > 0 && isspace(*b)) { |
||||
b++; |
||||
sb->len--; |
||||
} |
||||
memmove(sb->buf, b, sb->len); |
||||
sb->buf[sb->len] = '\0'; |
||||
} |
||||
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) |
||||
{ |
||||
int i; |
||||
for (i = 0; i < sb->len; i++) |
||||
sb->buf[i] = tolower(sb->buf[i]); |
||||
} |
||||
|
||||
struct strbuf **strbuf_split(const struct strbuf *sb, int delim) |
||||
{ |
||||
int alloc = 2, pos = 0; |
||||
char *n, *p; |
||||
struct strbuf **ret; |
||||
struct strbuf *t; |
||||
|
||||
ret = xcalloc(alloc, sizeof(struct strbuf *)); |
||||
p = n = sb->buf; |
||||
while (n < sb->buf + sb->len) { |
||||
int len; |
||||
n = memchr(n, delim, sb->len - (n - sb->buf)); |
||||
if (pos + 1 >= alloc) { |
||||
alloc = alloc * 2; |
||||
ret = xrealloc(ret, sizeof(struct strbuf *) * alloc); |
||||
} |
||||
if (!n) |
||||
n = sb->buf + sb->len - 1; |
||||
len = n - p + 1; |
||||
t = xmalloc(sizeof(struct strbuf)); |
||||
strbuf_init(t, len); |
||||
strbuf_add(t, p, len); |
||||
ret[pos] = t; |
||||
ret[++pos] = NULL; |
||||
p = ++n; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
struct strbuf **strbuf_splitws(const struct strbuf *sb) |
||||
{ |
||||
int alloc = 2, pos = 0; |
||||
struct strbuf **ret; |
||||
int prev_ws = 1, ws; |
||||
int i, start = 0; |
||||
|
||||
ret = xcalloc(alloc, sizeof(struct strbuf *)); |
||||
|
||||
for (i = 0; i <= sb->len; i++) { |
||||
ws = !sb->buf[i] || isspace(sb->buf[i]); |
||||
if (prev_ws == ws) |
||||
continue; |
||||
if (!ws) { |
||||
start = i; |
||||
} else { |
||||
if (pos + 1 >= alloc) { |
||||
alloc = alloc * 2; |
||||
ret = xrealloc(ret, sizeof(struct strbuf *) * alloc); |
||||
} |
||||
ret[pos] = xmalloc(sizeof(struct strbuf)); |
||||
strbuf_init(ret[pos], i - start); |
||||
strbuf_add(ret[pos], &sb->buf[start], i - start); |
||||
pos++; |
||||
} |
||||
prev_ws = ws; |
||||
} |
||||
|
||||
ret[pos] = NULL; |
||||
return ret; |
||||
} |
||||
|
||||
void strbuf_list_free(struct strbuf **sbs) |
||||
{ |
||||
struct strbuf **s = sbs; |
||||
|
||||
while (*s) { |
||||
strbuf_release(*s); |
||||
free(*s++); |
||||
} |
||||
free(sbs); |
||||
} |
||||
|
||||
size_t strbuf_list_len(struct strbuf **sbs) |
||||
{ |
||||
size_t n = 0; |
||||
while (*sbs++) |
||||
n++; |
||||
return n; |
||||
} |
||||
|
||||
int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) |
||||
{ |
||||
int cmp; |
||||
if (a->len < b->len) { |
||||
cmp = memcmp(a->buf, b->buf, a->len); |
||||
return cmp ? cmp : -1; |
||||
} else { |
||||
cmp = memcmp(a->buf, b->buf, b->len); |
||||
return cmp ? cmp : a->len != b->len; |
||||
} |
||||
} |
||||
|
||||
void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, |
||||
const void *data, size_t dlen) |
||||
{ |
||||
if (pos + len < pos) |
||||
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, ...) |
||||
{ |
||||
int len; |
||||
va_list ap; |
||||
|
||||
if (!strbuf_avail(sb)) |
||||
strbuf_grow(sb, 64); |
||||
va_start(ap, fmt); |
||||
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); |
||||
va_end(ap); |
||||
if (len < 0) |
||||
die("your vsnprintf is broken"); |
||||
if (len > strbuf_avail(sb)) { |
||||
strbuf_grow(sb, len); |
||||
va_start(ap, fmt); |
||||
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); |
||||
va_end(ap); |
||||
if (len > strbuf_avail(sb)) { |
||||
die("this should not happen, your snprintf is broken"); |
||||
} |
||||
} |
||||
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 = gitstrchrnul(format, '%'); |
||||
strbuf_add(sb, format, percent - format); |
||||
if (!*percent) |
||||
break; |
||||
format = percent + 1; |
||||
|
||||
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; |
||||
} |
||||
|
||||
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) |
||||
{ |
||||
size_t res; |
||||
|
||||
strbuf_grow(sb, size); |
||||
res = fread(sb->buf + sb->len, 1, size, f); |
||||
if (res > 0) { |
||||
strbuf_setlen(sb, sb->len + res); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
ssize_t strbuf_write(struct strbuf *sb, int fd) { |
||||
ssize_t total = 0; |
||||
|
||||
while (total < sb->len) { |
||||
ssize_t cnt = write(fd, sb->buf + total, sb->len - total); |
||||
if (cnt <= 0) |
||||
return -1; |
||||
total += cnt; |
||||
} |
||||
|
||||
return total; |
||||
} |
||||
|
||||
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) |
||||
{ |
||||
size_t oldlen = sb->len; |
||||
|
||||
strbuf_grow(sb, hint ? hint : 8192); |
||||
for (;;) { |
||||
ssize_t cnt; |
||||
|
||||
cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); |
||||
if (cnt < 0) { |
||||
strbuf_setlen(sb, oldlen); |
||||
return -1; |
||||
} |
||||
if (!cnt) |
||||
break; |
||||
sb->len += cnt; |
||||
strbuf_grow(sb, 8192); |
||||
} |
||||
|
||||
sb->buf[sb->len] = '\0'; |
||||
return sb->len - oldlen; |
||||
} |
||||
|
||||
int strbuf_getline(struct strbuf *sb, FILE *fp, int term) |
||||
{ |
||||
int ch; |
||||
|
||||
strbuf_grow(sb, 0); |
||||
if (feof(fp)) |
||||
return EOF; |
||||
|
||||
strbuf_reset(sb); |
||||
while ((ch = fgetc(fp)) != EOF) { |
||||
if (ch == term) |
||||
break; |
||||
strbuf_grow(sb, 1); |
||||
sb->buf[sb->len++] = ch; |
||||
} |
||||
if (ch == EOF && sb->len == 0) |
||||
return EOF; |
||||
|
||||
sb->buf[sb->len] = '\0'; |
||||
return 0; |
||||
} |
||||
|
||||
int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) |
||||
{ |
||||
int fd, len; |
||||
|
||||
fd = open(path, O_RDONLY); |
||||
if (fd < 0) |
||||
return -1; |
||||
len = strbuf_read(sb, fd, hint); |
||||
close(fd); |
||||
if (len < 0) |
||||
return -1; |
||||
|
||||
return len; |
||||
} |
@ -0,0 +1,158 @@ |
||||
/*
|
||||
Copyright (c) 2005-2008 Junio C. Hamano et al. |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License (version 2) |
||||
as published by the Free Software Foundation. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software |
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#ifndef STRBUF_H |
||||
#define STRBUF_H |
||||
|
||||
/*
|
||||
* Strbuf's can be use in many ways: as a byte array, or to store arbitrary |
||||
* long, overflow safe strings. |
||||
* |
||||
* Strbufs has some invariants that are very important to keep in mind: |
||||
* |
||||
* 1. the ->buf member is always malloc-ed, hence strbuf's can be used to |
||||
* build complex strings/buffers whose final size isn't easily known. |
||||
* |
||||
* It is NOT legal to copy the ->buf pointer away. |
||||
* `strbuf_detach' is the operation that detachs a buffer from its shell |
||||
* while keeping the shell valid wrt its invariants. |
||||
* |
||||
* 2. the ->buf member is a byte array that has at least ->len + 1 bytes |
||||
* allocated. The extra byte is used to store a '\0', allowing the ->buf |
||||
* member to be a valid C-string. Every strbuf function ensure this |
||||
* invariant is preserved. |
||||
* |
||||
* Note that it is OK to "play" with the buffer directly if you work it |
||||
* that way: |
||||
* |
||||
* strbuf_grow(sb, SOME_SIZE); |
||||
* ... Here, the memory array starting at sb->buf, and of length |
||||
* ... strbuf_avail(sb) is all yours, and you are sure that |
||||
* ... strbuf_avail(sb) is at least SOME_SIZE. |
||||
* strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); |
||||
* |
||||
* Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb). |
||||
* |
||||
* Doing so is safe, though if it has to be done in many places, adding the |
||||
* missing API to the strbuf module is the way to go. |
||||
* |
||||
* XXX: do _not_ assume that the area that is yours is of size ->alloc - 1 |
||||
* even if it's true in the current implementation. Alloc is somehow a |
||||
* "private" member that should not be messed with. |
||||
*/ |
||||
|
||||
#include <assert.h> |
||||
#include <sys/types.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include <stdarg.h> |
||||
|
||||
extern char strbuf_slopbuf[]; |
||||
struct strbuf { |
||||
size_t alloc; |
||||
size_t len; |
||||
char *buf; |
||||
}; |
||||
|
||||
#define STRBUF_INIT { 0, 0, strbuf_slopbuf } |
||||
|
||||
/*----- strbuf life cycle -----*/ |
||||
extern void strbuf_init(struct strbuf *, size_t); |
||||
extern void strbuf_release(struct strbuf *); |
||||
extern char *strbuf_detach(struct strbuf *, size_t *); |
||||
extern 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; |
||||
} |
||||
|
||||
extern void strbuf_grow(struct strbuf *, size_t); |
||||
|
||||
static inline void strbuf_setlen(struct strbuf *sb, size_t len) { |
||||
if (!sb->alloc) |
||||
strbuf_grow(sb, 0); |
||||
assert(len < sb->alloc); |
||||
sb->len = len; |
||||
sb->buf[len] = '\0'; |
||||
} |
||||
#define strbuf_reset(sb) strbuf_setlen(sb, 0) |
||||
|
||||
/*----- content related -----*/ |
||||
extern void strbuf_trim(struct strbuf *); |
||||
extern void strbuf_rtrim(struct strbuf *); |
||||
extern void strbuf_ltrim(struct strbuf *); |
||||
extern int strbuf_cmp(const struct strbuf *, const struct strbuf *); |
||||
extern void strbuf_tolower(struct strbuf *); |
||||
|
||||
extern struct strbuf **strbuf_split(const struct strbuf *, int delim); |
||||
extern struct strbuf **strbuf_splitws(const struct strbuf *); |
||||
extern void strbuf_list_free(struct strbuf **); |
||||
extern size_t strbuf_list_len(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'; |
||||
} |
||||
|
||||
extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); |
||||
extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); |
||||
|
||||
/* splice pos..pos+len with given data */ |
||||
extern void strbuf_splice(struct strbuf *, size_t pos, size_t len, |
||||
const void *, size_t); |
||||
|
||||
extern 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_add(sb, sb2->buf, sb2->len); |
||||
} |
||||
extern 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); |
||||
extern 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; |
||||
}; |
||||
extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context); |
||||
|
||||
__attribute__((format(printf,2,3))) |
||||
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); |
||||
extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); |
||||
|
||||
extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); |
||||
/* XXX: if read fails, any partial read is undone */ |
||||
extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); |
||||
extern ssize_t strbuf_write(struct strbuf *sb, int fd); |
||||
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); |
||||
|
||||
extern int strbuf_getline(struct strbuf *, FILE *, int); |
||||
|
||||
extern void stripspace(struct strbuf *buf, int skip_comments); |
||||
extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); |
||||
|
||||
#endif /* STRBUF_H */ |
Loading…
Reference in new issue