diff options
-rw-r--r-- | archive-tar.c | 68 | ||||
-rw-r--r-- | builtin-apply.c | 254 | ||||
-rw-r--r-- | builtin-archive.c | 97 | ||||
-rw-r--r-- | builtin-blame.c | 34 | ||||
-rw-r--r-- | builtin-branch.c | 15 | ||||
-rw-r--r-- | builtin-checkout-index.c | 8 | ||||
-rw-r--r-- | builtin-commit-tree.c | 58 | ||||
-rw-r--r-- | builtin-fetch--tool.c | 22 | ||||
-rw-r--r-- | builtin-log.c | 12 | ||||
-rw-r--r-- | builtin-rerere.c | 55 | ||||
-rw-r--r-- | builtin-rev-list.c | 13 | ||||
-rw-r--r-- | builtin-show-branch.c | 13 | ||||
-rw-r--r-- | builtin-stripspace.c | 20 | ||||
-rw-r--r-- | builtin-tag.c | 78 | ||||
-rw-r--r-- | builtin-update-index.c | 13 | ||||
-rw-r--r-- | cache-tree.c | 57 | ||||
-rw-r--r-- | cache.h | 7 | ||||
-rw-r--r-- | commit.c | 416 | ||||
-rw-r--r-- | commit.h | 9 | ||||
-rw-r--r-- | convert.c | 413 | ||||
-rw-r--r-- | diff.c | 38 | ||||
-rw-r--r-- | entry.c | 10 | ||||
-rw-r--r-- | fast-import.c | 265 | ||||
-rw-r--r-- | fetch.c | 7 | ||||
-rw-r--r-- | git-compat-util.h | 17 | ||||
-rw-r--r-- | imap-send.c | 30 | ||||
-rw-r--r-- | interpolate.c | 20 | ||||
-rw-r--r-- | log-tree.c | 56 | ||||
-rw-r--r-- | mktag.c | 14 | ||||
-rw-r--r-- | mktree.c | 33 | ||||
-rw-r--r-- | sha1_file.c | 69 | ||||
-rw-r--r-- | strbuf.c | 170 | ||||
-rw-r--r-- | strbuf.h | 102 | ||||
-rwxr-xr-x | t/t5000-tar-tree.sh | 15 |
34 files changed, 1066 insertions, 1442 deletions
diff --git a/archive-tar.c b/archive-tar.c index c0d95dab0d..a87bc4b83e 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -3,7 +3,6 @@ */ #include "cache.h" #include "commit.h" -#include "strbuf.h" #include "tar.h" #include "builtin.h" #include "archive.h" @@ -79,19 +78,6 @@ static void write_trailer(void) } } -static void strbuf_append_string(struct strbuf *sb, const char *s) -{ - int slen = strlen(s); - int total = sb->len + slen; - if (total + 1 > sb->alloc) { - sb->buf = xrealloc(sb->buf, total + 1); - sb->alloc = total + 1; - } - memcpy(sb->buf + sb->len, s, slen); - sb->len = total; - sb->buf[total] = '\0'; -} - /* * pax extended header records have the format "%u %s=%s\n". %u contains * the size of the whole string (including the %u), the first %s is the @@ -101,26 +87,17 @@ static void strbuf_append_string(struct strbuf *sb, const char *s) static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword, const char *value, unsigned int valuelen) { - char *p; - int len, total, tmp; + int len, tmp; /* "%u %s=%s\n" */ len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1; for (tmp = len; tmp > 9; tmp /= 10) len++; - total = sb->len + len; - if (total > sb->alloc) { - sb->buf = xrealloc(sb->buf, total); - sb->alloc = total; - } - - p = sb->buf; - p += sprintf(p, "%u %s=", len, keyword); - memcpy(p, value, valuelen); - p += valuelen; - *p = '\n'; - sb->len = total; + strbuf_grow(sb, len); + strbuf_addf(sb, "%u %s=", len, keyword); + strbuf_add(sb, value, valuelen); + strbuf_addch(sb, '\n'); } static unsigned int ustar_header_chksum(const struct ustar_header *header) @@ -154,8 +131,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, struct strbuf ext_header; memset(&header, 0, sizeof(header)); - ext_header.buf = NULL; - ext_header.len = ext_header.alloc = 0; + strbuf_init(&ext_header, 0); if (!sha1) { *header.typeflag = TYPEFLAG_GLOBAL_HEADER; @@ -167,7 +143,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1)); } else { if (verbose) - fprintf(stderr, "%.*s\n", path->len, path->buf); + fprintf(stderr, "%.*s\n", (int)path->len, path->buf); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { *header.typeflag = TYPEFLAG_DIR; mode = (mode | 0777) & ~tar_umask; @@ -226,8 +202,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, if (ext_header.len > 0) { write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len); - free(ext_header.buf); } + strbuf_release(&ext_header); write_blocked(&header, sizeof(header)); if (S_ISREG(mode) && buffer && size > 0) write_blocked(buffer, size); @@ -236,11 +212,11 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, static void write_global_extended_header(const unsigned char *sha1) { struct strbuf ext_header; - ext_header.buf = NULL; - ext_header.len = ext_header.alloc = 0; + + strbuf_init(&ext_header, 0); strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40); write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len); - free(ext_header.buf); + strbuf_release(&ext_header); } static int git_tar_config(const char *var, const char *value) @@ -261,28 +237,18 @@ static int write_tar_entry(const unsigned char *sha1, const char *base, int baselen, const char *filename, unsigned mode, int stage) { - static struct strbuf path; + static struct strbuf path = STRBUF_INIT; int filenamelen = strlen(filename); void *buffer; enum object_type type; unsigned long size; - if (!path.alloc) { - path.buf = xmalloc(PATH_MAX); - path.alloc = PATH_MAX; - path.len = path.eof = 0; - } - if (path.alloc < baselen + filenamelen + 1) { - free(path.buf); - path.buf = xmalloc(baselen + filenamelen + 1); - path.alloc = baselen + filenamelen + 1; - } - memcpy(path.buf, base, baselen); - memcpy(path.buf + baselen, filename, filenamelen); - path.len = baselen + filenamelen; - path.buf[path.len] = '\0'; + strbuf_grow(&path, MAX(PATH_MAX, baselen + filenamelen + 1)); + strbuf_reset(&path); + strbuf_add(&path, base, baselen); + strbuf_add(&path, filename, filenamelen); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { - strbuf_append_string(&path, "/"); + strbuf_addch(&path, '/'); buffer = NULL; size = 0; } else { diff --git a/builtin-apply.c b/builtin-apply.c index 86d89a4a7e..f953c5b768 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -181,34 +181,21 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c static void *read_patch_file(int fd, unsigned long *sizep) { - unsigned long size = 0, alloc = CHUNKSIZE; - void *buffer = xmalloc(alloc); + struct strbuf buf; - for (;;) { - ssize_t nr = alloc - size; - if (nr < 1024) { - alloc += CHUNKSIZE; - buffer = xrealloc(buffer, alloc); - nr = alloc - size; - } - nr = xread(fd, (char *) buffer + size, nr); - if (!nr) - break; - if (nr < 0) - die("git-apply: read returned %s", strerror(errno)); - size += nr; - } - *sizep = size; + strbuf_init(&buf, 0); + if (strbuf_read(&buf, fd, 0) < 0) + die("git-apply: read returned %s", strerror(errno)); + *sizep = buf.len; /* * Make sure that we have some slop in the buffer * so that we can do speculative "memcmp" etc, and * see to it that it is NUL-filled. */ - if (alloc < size + SLOP) - buffer = xrealloc(buffer, size + SLOP); - memset((char *) buffer + size, 0, SLOP); - return buffer; + strbuf_grow(&buf, SLOP); + memset(buf.buf + buf.len, 0, SLOP); + return strbuf_detach(&buf); } static unsigned long linelen(const char *buffer, unsigned long size) @@ -1454,39 +1441,28 @@ static void show_stats(struct patch *patch) free(qname); } -static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p) +static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) { int fd; - unsigned long got; - unsigned long nsize; - char *nbuf; - unsigned long size = *size_p; - char *buf = *buf_p; switch (st->st_mode & S_IFMT) { case S_IFLNK: - return readlink(path, buf, size) != size; + strbuf_grow(buf, st->st_size); + if (readlink(path, buf->buf, st->st_size) != st->st_size) + return -1; + strbuf_setlen(buf, st->st_size); + return 0; case S_IFREG: fd = open(path, O_RDONLY); if (fd < 0) return error("unable to open %s", path); - got = 0; - for (;;) { - ssize_t ret = xread(fd, buf + got, size - got); - if (ret <= 0) - break; - got += ret; + if (strbuf_read(buf, fd, st->st_size) < 0) { + close(fd); + return -1; } close(fd); - nsize = got; - nbuf = convert_to_git(path, buf, &nsize); - if (nbuf) { - free(buf); - *buf_p = nbuf; - *alloc_p = nsize; - *size_p = nsize; - } - return got != size; + convert_to_git(path, buf->buf, buf->len, buf); + return 0; default: return -1; } @@ -1591,12 +1567,6 @@ static void remove_last_line(const char **rbuf, int *rsize) *rsize = offset + 1; } -struct buffer_desc { - char *buffer; - unsigned long size; - unsigned long alloc; -}; - static int apply_line(char *output, const char *patch, int plen) { /* plen is number of bytes to be copied from patch, @@ -1673,10 +1643,9 @@ static int apply_line(char *output, const char *patch, int plen) return output + plen - buf; } -static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof) +static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof) { int match_beginning, match_end; - char *buf = desc->buffer; const char *patch = frag->patch; int offset, size = frag->size; char *old = xmalloc(size); @@ -1787,24 +1756,17 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i lines = 0; pos = frag->newpos; for (;;) { - offset = find_offset(buf, desc->size, + offset = find_offset(buf->buf, buf->len, oldlines, oldsize, pos, &lines); - if (match_end && offset + oldsize != desc->size) + if (match_end && offset + oldsize != buf->len) offset = -1; if (match_beginning && offset) offset = -1; if (offset >= 0) { - int diff; - unsigned long size, alloc; - if (new_whitespace == strip_whitespace && - (desc->size - oldsize - offset == 0)) /* end of file? */ + (buf->len - oldsize - offset == 0)) /* end of file? */ newsize -= new_blank_lines_at_end; - diff = newsize - oldsize; - size = desc->size + diff; - alloc = desc->alloc; - /* Warn if it was necessary to reduce the number * of context lines. */ @@ -1814,19 +1776,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i " to apply fragment at %d\n", leading, trailing, pos + lines); - if (size > alloc) { - alloc = size + 8192; - desc->alloc = alloc; - buf = xrealloc(buf, alloc); - desc->buffer = buf; - } - desc->size = size; - memmove(buf + offset + newsize, - buf + offset + oldsize, - size - offset - newsize); - memcpy(buf + offset, newlines, newsize); + strbuf_splice(buf, offset, oldsize, newlines, newsize); offset = 0; - break; } @@ -1862,12 +1813,11 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i return offset; } -static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch) +static int apply_binary_fragment(struct strbuf *buf, struct patch *patch) { - unsigned long dst_size; struct fragment *fragment = patch->fragments; - void *data; - void *result; + unsigned long len; + void *dst; /* Binary patch is irreversible without the optional second hunk */ if (apply_in_reverse) { @@ -1878,29 +1828,24 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch) ? patch->new_name : patch->old_name); fragment = fragment->next; } - data = (void*) fragment->patch; switch (fragment->binary_patch_method) { case BINARY_DELTA_DEFLATED: - result = patch_delta(desc->buffer, desc->size, - data, - fragment->size, - &dst_size); - free(desc->buffer); - desc->buffer = result; - break; + dst = patch_delta(buf->buf, buf->len, fragment->patch, + fragment->size, &len); + if (!dst) + return -1; + /* XXX patch_delta NUL-terminates */ + strbuf_attach(buf, dst, len, len + 1); + return 0; case BINARY_LITERAL_DEFLATED: - free(desc->buffer); - desc->buffer = data; - dst_size = fragment->size; - break; + strbuf_reset(buf); + strbuf_add(buf, fragment->patch, fragment->size); + return 0; } - if (!desc->buffer) - return -1; - desc->size = desc->alloc = dst_size; - return 0; + return -1; } -static int apply_binary(struct buffer_desc *desc, struct patch *patch) +static int apply_binary(struct strbuf *buf, struct patch *patch) { const char *name = patch->old_name ? patch->old_name : patch->new_name; unsigned char sha1[20]; @@ -1919,7 +1864,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) /* See if the old one matches what the patch * applies to. */ - hash_sha1_file(desc->buffer, desc->size, blob_type, sha1); + hash_sha1_file(buf->buf, buf->len, blob_type, sha1); if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix)) return error("the patch applies to '%s' (%s), " "which does not match the " @@ -1928,16 +1873,14 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) } else { /* Otherwise, the old one must be empty. */ - if (desc->size) + if (buf->len) return error("the patch applies to an empty " "'%s' but it is not empty", name); } get_sha1_hex(patch->new_sha1_prefix, sha1); if (is_null_sha1(sha1)) { - free(desc->buffer); - desc->alloc = desc->size = 0; - desc->buffer = NULL; + strbuf_release(buf); return 0; /* deletion patch */ } @@ -1945,43 +1888,44 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) /* We already have the postimage */ enum object_type type; unsigned long size; + char *result; - free(desc->buffer); - desc->buffer = read_sha1_file(sha1, &type, &size); - if (!desc->buffer) + result = read_sha1_file(sha1, &type, &size); + if (!result) return error("the necessary postimage %s for " "'%s' cannot be read", patch->new_sha1_prefix, name); - desc->alloc = desc->size = size; - } - else { - /* We have verified desc matches the preimage; + /* XXX read_sha1_file NUL-terminates */ + strbuf_attach(buf, result, size, size + 1); + } else { + /* We have verified buf matches the preimage; * apply the patch data to it, which is stored * in the patch->fragments->{patch,size}. */ - if (apply_binary_fragment(desc, patch)) + if (apply_binary_fragment(buf, patch)) return error("binary patch does not apply to '%s'", name); /* verify that the result matches */ - hash_sha1_file(desc->buffer, desc->size, blob_type, sha1); + hash_sha1_file(buf->buf, buf->len, blob_type, sha1); if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix)) - return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1)); + return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", + name, patch->new_sha1_prefix, sha1_to_hex(sha1)); } return 0; } -static int apply_fragments(struct buffer_desc *desc, struct patch *patch) +static int apply_fragments(struct strbuf *buf, struct patch *patch) { struct fragment *frag = patch->fragments; const char *name = patch->old_name ? patch->old_name : patch->new_name; if (patch->is_binary) - return apply_binary(desc, patch); + return apply_binary(buf, patch); while (frag) { - if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) { + if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) { error("patch failed: %s:%ld", name, frag->oldpos); if (!apply_with_reject) return -1; @@ -1992,76 +1936,57 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch) return 0; } -static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p, - unsigned long *size_p) +static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) { if (!ce) return 0; if (S_ISGITLINK(ntohl(ce->ce_mode))) { - *buf_p = xmalloc(100); - *size_p = snprintf(*buf_p, 100, - "Subproject commit %s\n", sha1_to_hex(ce->sha1)); + strbuf_grow(buf, 100); + strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1)); } else { enum object_type type; - *buf_p = read_sha1_file(ce->sha1, &type, size_p); - if (!*buf_p) + unsigned long sz; + char *result; + + result = read_sha1_file(ce->sha1, &type, &sz); + if (!result) return -1; + /* XXX read_sha1_file NUL-terminates */ + strbuf_attach(buf, result, sz, sz + 1); } return 0; } static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) { - char *buf; - unsigned long size, alloc; - struct buffer_desc desc; + struct strbuf buf; - size = 0; - alloc = 0; - buf = NULL; + strbuf_init(&buf, 0); if (cached) { - if (read_file_or_gitlink(ce, &buf, &size)) + if (read_file_or_gitlink(ce, &buf)) return error("read of %s failed", patch->old_name); - alloc = size; } else if (patch->old_name) { if (S_ISGITLINK(patch->old_mode)) { - if (ce) - read_file_or_gitlink(ce, &buf, &size); - else { + if (ce) { + read_file_or_gitlink(ce, &buf); + } else { /* * There is no way to apply subproject * patch without looking at the index. */ patch->fragments = NULL; - size = 0; } - } - else { - size = xsize_t(st->st_size); - alloc = size + 8192; - buf = xmalloc(alloc); - if (read_old_data(st, patch->old_name, - &buf, &alloc, &size)) - return error("read of %s failed", - patch->old_name); + } else { + if (read_old_data(st, patch->old_name, &buf)) + return error("read of %s failed", patch->old_name); } } - desc.size = size; - desc.alloc = alloc; - desc.buffer = buf; - - if (apply_fragments(&desc, patch) < 0) + if (apply_fragments(&buf, patch) < 0) return -1; /* note with --reject this succeeds. */ - - /* NUL terminate the result */ - if (desc.alloc <= desc.size) - desc.buffer = xrealloc(desc.buffer, desc.size + 1); - desc.buffer[desc.size] = 0; - - patch->result = desc.buffer; - patch->resultsize = desc.size; + patch->result = buf.buf; + patch->resultsize = buf.len; if (0 < patch->is_delete && patch->resultsize) return error("removal patch leaves file contents"); @@ -2479,7 +2404,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) { int fd; - char *nbuf; + struct strbuf nbuf; if (S_ISGITLINK(mode)) { struct stat st; @@ -2498,23 +2423,16 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, if (fd < 0) return -1; - nbuf = convert_to_working_tree(path, buf, &size); - if (nbuf) - buf = nbuf; - - while (size) { - int written = xwrite(fd, buf, size); - if (written < 0) - die("writing file %s: %s", path, strerror(errno)); - if (!written) - die("out of space writing file %s", path); - buf += written; - size -= written; + strbuf_init(&nbuf, 0); + if (convert_to_working_tree(path, buf, size, &nbuf)) { + size = nbuf.len; + buf = nbuf.buf; } + write_or_die(fd, buf, size); + strbuf_release(&nbuf); + if (close(fd) < 0) die("closing file %s: %s", path, strerror(errno)); - if (nbuf) - free(nbuf); return 0; } diff --git a/builtin-archive.c b/builtin-archive.c index a90c65ce54..843a9e37bb 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -81,95 +81,80 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static void *format_subst(const struct commit *commit, const char *format, - unsigned long *sizep) +static void format_subst(const struct commit *commit, + const char *src, size_t len, + struct strbuf *buf) { - unsigned long len = *sizep, result_len = 0; - const char *a = format; - char *result = NULL; + char *to_free = NULL; + struct strbuf fmt; + if (src == buf->buf) + to_free = strbuf_detach(buf); + strbuf_init(&fmt, 0); for (;;) { const char *b, *c; - char *fmt, *formatted = NULL; - unsigned long a_len, fmt_len, formatted_len, allocated = 0; - b = memmem(a, len, "$Format:", 8); - if (!b || a + len < b + 9) + b = memmem(src, len, "$Format:", 8); + if (!b || src + len < b + 9) break; c = memchr(b + 8, '$', len - 8); if (!c) break; - a_len = b - a; - fmt_len = c - b - 8; - fmt = xmalloc(fmt_len + 1); - memcpy(fmt, b + 8, fmt_len); - fmt[fmt_len] = '\0'; - - formatted_len = format_commit_message(commit, fmt, &formatted, - &allocated); - free(fmt); - result = xrealloc(result, result_len + a_len + formatted_len); - memcpy(result + result_len, a, a_len); - memcpy(result + result_len + a_len, formatted, formatted_len); - result_len += a_len + formatted_len; - len -= c + 1 - a; - a = c + 1; - } + strbuf_reset(&fmt); + strbuf_add(&fmt, b + 8, c - b - 8); - if (result && len) { - result = xrealloc(result, result_len + len); - memcpy(result + result_len, a, len); - result_len += len; + strbuf_add(buf, src, b - src); + format_commit_message(commit, fmt.buf, buf); + len -= c + 1 - src; + src = c + 1; } - - *sizep = result_len; - - return result; + strbuf_add(buf, src, len); + strbuf_release(&fmt); + free(to_free); } -static void *convert_to_archive(const char *path, - const void *src, unsigned long *sizep, - const struct commit *commit) +static int convert_to_archive(const char *path, + const void *src, size_t len, + struct strbuf *buf, + const struct commit *commit) { static struct git_attr *attr_export_subst; struct git_attr_check check[1]; if (!commit) - return NULL; + return 0; - if (!attr_export_subst) - attr_export_subst = git_attr("export-subst", 12); + if (!attr_export_subst) + attr_export_subst = git_attr("export-subst", 12); check[0].attr = attr_export_subst; if (git_checkattr(path, ARRAY_SIZE(check), check)) - return NULL; + return 0; if (!ATTR_TRUE(check[0].value)) - return NULL; + return 0; - return format_subst(commit, src, sizep); + format_subst(commit, src, len, buf); + return 1; } void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, - unsigned long *size, + unsigned long *sizep, const struct commit *commit) { - void *buffer, *converted; + void *buffer; - buffer = read_sha1_file(sha1, type, size); + buffer = read_sha1_file(sha1, type, sizep); if (buffer && S_ISREG(mode)) { - converted = convert_to_working_tree(path, buffer, size); - if (converted) { - free(buffer); - buffer = converted; - } - - converted = convert_to_archive(path, buffer, size, commit); - if (converted) { - free(buffer); - buffer = converted; - } + struct strbuf buf; + + strbuf_init(&buf, 0); + strbuf_attach(&buf, buffer, *sizep, *sizep + 1); + convert_to_working_tree(path, buf.buf, buf.len, &buf); + convert_to_archive(path, buf.buf, buf.len, &buf, commit); + *sizep = buf.len; + buffer = buf.buf; } return buffer; diff --git a/builtin-blame.c b/builtin-blame.c index dc88a953a5..e364b6c6c0 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2001,11 +2001,10 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con struct commit *commit; struct origin *origin; unsigned char head_sha1[20]; - char *buf; + struct strbuf buf; const char *ident; int fd; time_t now; - unsigned long fin_size; int size, len; struct cache_entry *ce; unsigned mode; @@ -2023,9 +2022,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con origin = make_origin(commit, path); + strbuf_init(&buf, 0); if (!contents_from || strcmp("-", contents_from)) { struct stat st; const char *read_from; + unsigned long fin_size; if (contents_from) { if (stat(contents_from, &st) < 0) @@ -2038,19 +2039,19 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con read_from = path; } fin_size = xsize_t(st.st_size); - buf = xmalloc(fin_size+1); mode = canon_mode(st.st_mode); switch (st.st_mode & S_IFMT) { case S_IFREG: fd = open(read_from, O_RDONLY); if (fd < 0) die("cannot open %s", read_from); - if (read_in_full(fd, buf, fin_size) != fin_size) + if (strbuf_read(&buf, fd, 0) != xsize_t(st.st_size)) die("cannot read %s", read_from); break; case S_IFLNK: - if (readlink(read_from, buf, fin_size+1) != fin_size) + if (readlink(read_from, buf.buf, buf.alloc) != fin_size) die("cannot readlink %s", read_from); + buf.len = fin_size; break; default: die("unsupported file type %s", read_from); @@ -2059,26 +2060,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con else { /* Reading from stdin */ contents_from = "standard input"; - buf = NULL; - fin_size = 0; mode = 0; - while (1) { - ssize_t cnt = 8192; - buf = xrealloc(buf, fin_size + cnt); - cnt = xread(0, buf + fin_size, cnt); - if (cnt < 0) - die("read error %s from stdin", - strerror(errno)); - if (!cnt) - break; - fin_size += cnt; - } - buf = xrealloc(buf, fin_size + 1); + if (strbuf_read(&buf, 0, 0) < 0) + die("read error %s from stdin", strerror(errno)); } - buf[fin_size] = 0; - origin->file.ptr = buf; - origin->file.size = fin_size; - pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1); + origin->file.ptr = buf.buf; + origin->file.size = buf.len; + pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1); commit->util = origin; /* diff --git a/builtin-branch.c b/builtin-branch.c index 5f5c1823cb..3da8b55b8f 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -268,23 +268,22 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } if (verbose) { - char *subject = NULL; - unsigned long subject_len = 0; + struct strbuf subject; const char *sub = " **** invalid ref ****"; + strbuf_init(&subject, 0); + commit = lookup_commit(item->sha1); if (commit && !parse_commit(commit)) { - pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &subject, &subject_len, 0, - NULL, NULL, 0); - sub = subject; + pretty_print_commit(CMIT_FMT_ONELINE, commit, + &subject, 0, NULL, NULL, 0); + sub = subject.buf; } printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), maxwidth, item->name, branch_get_color(COLOR_BRANCH_RESET), find_unique_abbrev(item->sha1, abbrev), sub); - if (subject) - free(subject); + strbuf_release(&subject); } else { printf("%c %s%s%s\n", c, branch_get_color(color), item->name, branch_get_color(COLOR_BRANCH_RESET)); diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index 75377b9cab..a18ecc4bab 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -38,7 +38,6 @@ */ #include "builtin.h" #include "cache.h" -#include "strbuf.h" #include "quote.h" #include "cache-tree.h" @@ -274,13 +273,11 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) struct strbuf buf; if (all) die("git-checkout-index: don't mix '--all' and '--stdin'"); - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *path_name; const char *p; - - read_line(&buf, stdin, line_termination); - if (buf.eof) + if (strbuf_getline(&buf, stdin, line_termination) == EOF) break; if (line_termination && buf.buf[0] == '"') path_name = unquote_c_style(buf.buf, NULL); @@ -293,6 +290,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) if (path_name != buf.buf) free(path_name); } + strbuf_release(&buf); } if (all) diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index ccbcbe30da..88b0ab36eb 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -14,36 +14,6 @@ /* * FIXME! Share the code with "write-tree.c" */ -static void init_buffer(char **bufp, unsigned int *sizep) -{ - *bufp = xmalloc(BLOCKING); - *sizep = 0; -} - -static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...) -{ - char one_line[2048]; - va_list args; - int len; - unsigned long alloc, size, newsize; - char *buf; - - va_start(args, fmt); - len = vsnprintf(one_line, sizeof(one_line), fmt, args); - va_end(args); - size = *sizep; - newsize = size + len + 1; - alloc = (size + 32767) & ~32767; - buf = *bufp; - if (newsize > alloc) { - alloc = (newsize + 32767) & ~32767; - buf = xrealloc(buf, alloc); - *bufp = buf; - } - *sizep = newsize - 1; - memcpy(buf + size, one_line, len); -} - static void check_valid(unsigned char *sha1, enum object_type expect) { enum object_type type = sha1_object_info(sha1, NULL); @@ -87,9 +57,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) int parents = 0; unsigned char tree_sha1[20]; unsigned char commit_sha1[20]; - char comment[1000]; - char *buffer; - unsigned int size; + struct strbuf buffer; int encoding_is_utf8; git_config(git_default_config); @@ -118,8 +86,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) /* Not having i18n.commitencoding is the same as having utf-8 */ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding); - init_buffer(&buffer, &size); - add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1)); + strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */ + strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1)); /* * NOTE! This ordering means that the same exact tree merged with a @@ -127,26 +95,24 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) * if everything else stays the same. */ for (i = 0; i < parents; i++) - add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i])); + strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i])); /* Person/date information */ - add_buffer(&buffer, &size, "author %s\n", git_author_info(1)); - add_buffer(&buffer, &size, "committer %s\n", git_committer_info(1)); + strbuf_addf(&buffer, "author %s\n", git_author_info(1)); + strbuf_addf(&buffer, "committer %s\n", git_committer_info(1)); if (!encoding_is_utf8) - add_buffer(&buffer, &size, - "encoding %s\n", git_commit_encoding); - add_buffer(&buffer, &size, "\n"); + strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); + strbuf_addch(&buffer, '\n'); /* And add the comment */ - while (fgets(comment, sizeof(comment), stdin) != NULL) - add_buffer(&buffer, &size, "%s", comment); + if (strbuf_read(&buffer, 0, 0) < 0) + die("git-commit-tree: read returned %s", strerror(errno)); /* And check the encoding */ - buffer[size] = '\0'; - if (encoding_is_utf8 && !is_utf8(buffer)) + if (encoding_is_utf8 && !is_utf8(buffer.buf)) fprintf(stderr, commit_utf8_warn); - if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) { + if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) { printf("%s\n", sha1_to_hex(commit_sha1)); return 0; } diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 24c7e6f7db..514a3cc018 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -3,26 +3,14 @@ #include "refs.h" #include "commit.h" -#define CHUNK_SIZE 1024 - static char *get_stdin(void) { - size_t offset = 0; - char *data = xmalloc(CHUNK_SIZE); - - while (1) { - ssize_t cnt = xread(0, data + offset, CHUNK_SIZE); - if (cnt < 0) - die("error reading standard input: %s", - strerror(errno)); - if (cnt == 0) { - data[offset] = 0; - break; - } - offset += cnt; - data = xrealloc(data, offset + CHUNK_SIZE); + struct strbuf buf; + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 1024) < 0) { + die("error reading standard input: %s", strerror(errno)); } - return data; + return strbuf_detach(&buf); } static void show_new(enum object_type type, unsigned char *sha1_new) diff --git a/builtin-log.c b/builtin-log.c index c6cc3aef52..60819d15c5 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -792,13 +792,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) sign = '-'; if (verbose) { - char *buf = NULL; - unsigned long buflen = 0; - pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &buf, &buflen, 0, NULL, NULL, 0); + struct strbuf buf; + strbuf_init(&buf, 0); + pretty_print_commit(CMIT_FMT_ONELINE, commit, + &buf, 0, NULL, NULL, 0); printf("%c %s %s\n", sign, - sha1_to_hex(commit->object.sha1), buf); - free(buf); + sha1_to_hex(commit->object.sha1), buf.buf); + strbuf_release(&buf); } else { printf("%c %s\n", sign, diff --git a/builtin-rerere.c b/builtin-rerere.c index 29d057c98c..58288f61a3 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -66,41 +66,20 @@ static int write_rr(struct path_list *rr, int out_fd) return commit_lock_file(&write_lock); } -struct buffer { - char *ptr; - int nr, alloc; -}; - -static void append_line(struct buffer *buffer, const char *line) -{ - int len = strlen(line); - - if (buffer->nr + len > buffer->alloc) { - buffer->alloc = alloc_nr(buffer->nr + len); - buffer->ptr = xrealloc(buffer->ptr, buffer->alloc); - } - memcpy(buffer->ptr + buffer->nr, line, len); - buffer->nr += len; -} - -static void clear_buffer(struct buffer *buffer) -{ - free(buffer->ptr); - buffer->ptr = NULL; - buffer->nr = buffer->alloc = 0; -} - static int handle_file(const char *path, unsigned char *sha1, const char *output) { SHA_CTX ctx; char buf[1024]; int hunk = 0, hunk_no = 0; - struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 }; - struct buffer *one = &minus, *two = + + struct strbuf minus, plus; + struct strbuf *one = &minus, *two = + FILE *f = fopen(path, "r"); FILE *out; + strbuf_init(&minus, 0); + strbuf_init(&plus, 0); + if (!f) return error("Could not open %s", path); @@ -122,36 +101,36 @@ static int handle_file(const char *path, else if (!prefixcmp(buf, "=======")) hunk = 2; else if (!prefixcmp(buf, ">>>>>>> ")) { - int one_is_longer = (one->nr > two->nr); - int common_len = one_is_longer ? two->nr : one->nr; - int cmp = memcmp(one->ptr, two->ptr, common_len); + int one_is_longer = (one->len > two->len); + int common_len = one_is_longer ? two->len : one->len; + int cmp = memcmp(one->buf, two->buf, common_len); hunk_no++; hunk = 0; if ((cmp > 0) || ((cmp == 0) && one_is_longer)) { - struct buffer *swap = one; + struct strbuf *swap = one; one = two; two = swap; } if (out) { fputs("<<<<<<<\n", out); - fwrite(one->ptr, one->nr, 1, out); + fwrite(one->buf, one->len, 1, out); fputs("=======\n", out); - fwrite(two->ptr, two->nr, 1, out); + fwrite(two->buf, two->len, 1, out); fputs(">>>>>>>\n", out); } if (sha1) { - SHA1_Update(&ctx, one->ptr, one->nr); + SHA1_Update(&ctx, one->buf, one->len); SHA1_Update(&ctx, "\0", 1); - SHA1_Update(&ctx, two->ptr, two->nr); + SHA1_Update(&ctx, two->buf, two->len); SHA1_Update(&ctx, "\0", 1); } - clear_buffer(one); - clear_buffer(two); + strbuf_release(one); + strbuf_release(two); } else if (hunk == 1) - append_line(one, buf); + strbuf_addstr(one, buf); else if (hunk == 2) - append_line(two, buf); + strbuf_addstr(two, buf); else if (out) fputs(buf, out); } diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 899a31d09a..43b88fae29 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -80,13 +80,12 @@ static void show_commit(struct commit *commit) putchar('\n'); if (revs.verbose_header) { - char *buf = NULL; - unsigned long buflen = 0; - pretty_print_commit(revs.commit_format, commit, ~0, - &buf, &buflen, - revs.abbrev, NULL, NULL, revs.date_mode); - printf("%s%c", buf, hdr_termination); - free(buf); + struct strbuf buf; + strbuf_init(&buf, 0); + pretty_print_commit(revs.commit_format, commit, + &buf, revs.abbrev, NULL, NULL, revs.date_mode); + printf("%s%c", buf.buf, hdr_termination); + strbuf_release(&buf); } maybe_flush_or_die(stdout, "stdout"); if (commit->parents) { diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 4fa87f6081..07a0c2316b 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -259,16 +259,15 @@ static void join_revs(struct commit_list **list_p, static void show_one_commit(struct commit *commit, int no_name) { - char *pretty = NULL; + struct strbuf pretty; const char *pretty_str = "(unavailable)"; - unsigned long pretty_len = 0; struct commit_name *name = commit->util; + strbuf_init(&pretty, 0); if (commit->object.parsed) { - pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &pretty, &pretty_len, - 0, NULL, NULL, 0); - pretty_str = pretty; + pretty_print_commit(CMIT_FMT_ONELINE, commit, + &pretty, 0, NULL, NULL, 0); + pretty_str = pretty.buf; } if (!prefixcmp(pretty_str, "[PATCH] ")) pretty_str += 8; @@ -289,7 +288,7 @@ static void show_one_commit(struct commit *commit, int no_name) find_unique_abbrev(commit->object.sha1, 7)); } puts(pretty_str); - free(pretty); + strbuf_release(&pretty); } static char *ref_name[MAX_REVS + 1]; diff --git a/builtin-stripspace.c b/builtin-stripspace.c index 916355ca5d..1ce284710c 100644 --- a/builtin-stripspace.c +++ b/builtin-stripspace.c @@ -74,26 +74,22 @@ size_t stripspace(char *buffer, size_t length, int skip_comments) int cmd_stripspace(int argc, const char **argv, const char *prefix) { - char *buffer; - unsigned long size; + struct strbuf buf; int strip_comments = 0; if (argc > 1 && (!strcmp(argv[1], "-s") || !strcmp(argv[1], "--strip-comments"))) strip_comments = 1; - size = 1024; - buffer = xmalloc(size); - if (read_fd(0, &buffer, &size)) { - free(buffer); + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 1024) < 0) die("could not read the input"); - } - size = stripspace(buffer, size, strip_comments); - write_or_die(1, buffer, size); - if (size) - putc('\n', stdout); + strbuf_setlen(&buf, stripspace(buf.buf, buf.len, strip_comments)); + if (buf.len) + strbuf_addch(&buf, '\n'); - free(buffer); + write_or_die(1, buf.buf, buf.len); + strbuf_release(&buf); return 0; } diff --git a/builtin-tag.c b/builtin-tag.c index 3a9d2eea71..82ebda11b0 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -17,7 +17,7 @@ static const char builtin_tag_usage[] = static char signingkey[1000]; -static void launch_editor(const char *path, char **buffer, unsigned long *len) +static void launch_editor(const char *path, struct strbuf *buffer) { const char *editor, *terminal; struct child_process child; @@ -55,10 +55,8 @@ static void launch_editor(const char *path, char **buffer, unsigned long *len) fd = open(path, O_RDONLY); if (fd < 0) die("could not open '%s': %s", path, strerror(errno)); - if (read_fd(fd, buffer, len)) { - free(*buffer); - die("could not read message file '%s': %s", - path, strerror(errno)); + if (strbuf_read(buffer, fd, 0) < 0) { + die("could not read message file '%s': %s", path, strerror(errno)); } close(fd); } @@ -184,7 +182,7 @@ static int verify_tag(const char *name, const char *ref, return 0; } -static ssize_t do_sign(char *buffer, size_t size, size_t max) +static int do_sign(struct strbuf *buffer) { struct child_process gpg; const char *args[4]; @@ -216,22 +214,22 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max) if (start_command(&gpg)) return error("could not run gpg."); - if (write_in_full(gpg.in, buffer, size) != size) { + if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) { close(gpg.in); finish_command(&gpg); return error("gpg did not accept the tag data"); } close(gpg.in); gpg.close_in = 0; - len = read_in_full(gpg.out, buffer + size, max - size); + len = strbuf_read(buffer, gpg.out, 1024); if (finish_command(&gpg) || !len || len < 0) return error("gpg failed to sign the tag"); - if (len == max - size) + if (len < 0) return error("could not read the entire signature from gpg."); - return size + len; + return 0; } static const char tag_template[] = @@ -254,15 +252,13 @@ static int git_tag_config(const char *var, const char *value) return git_default_config(var, value); } -#define MAX_SIGNATURE_LENGTH 1024 -/* message must be NULL or allocated, it will be reallocated and freed */ static void create_tag(const unsigned char *object, const char *tag, - char *message, int sign, unsigned char *result) + struct strbuf *buf, int message, int sign, + unsigned char *result) { enum object_type type; - char header_buf[1024], *buffer = NULL; - int header_len, max_size; - unsigned long size = 0; + char header_buf[1024]; + int header_len; type = sha1_object_info(object, NULL); if (type <= OBJ_NONE) @@ -294,53 +290,40 @@ static void create_tag(const unsigned char *object, const char *tag, write_or_die(fd, tag_template, strlen(tag_template)); close(fd); - launch_editor(path, &buffer, &size); + launch_editor(path, buf); unlink(path); free(path); } - else { - buffer = message; - size = strlen(message); - } - size = stripspace(buffer, size, 1); + strbuf_setlen(buf, stripspace(buf->buf, buf->len, 1)); - if (!message && !size) + if (!message && !buf->len) die("no tag message?"); /* insert the header and add the '\n' if needed: */ - max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1; - buffer = xrealloc(buffer, max_size); - if (size) - buffer[size++] = '\n'; - memmove(buffer + header_len, buffer, size); - memcpy(buffer, header_buf, header_len); - size += header_len; - - if (sign) { - ssize_t r = do_sign(buffer, size, max_size); - if (r < 0) - die("unable to sign the tag"); - size = r; - } + if (buf->len) + strbuf_addch(buf, '\n'); + strbuf_insert(buf, 0, header_buf, header_len); - if (write_sha1_file(buffer, size, tag_type, result) < 0) + if (sign && do_sign(buf) < 0) + die("unable to sign the tag"); + if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0) die("unable to write tag file"); - free(buffer); } int cmd_tag(int argc, const char **argv, const char *prefix) { + struct strbuf buf; unsigned char object[20], prev[20]; - int annotate = 0, sign = 0, force = 0, lines = 0; - char *message = NULL; + int annotate = 0, sign = 0, force = 0, lines = 0, message = 0; char ref[PATH_MAX]; const char *object_ref, *tag; int i; struct ref_lock *lock; git_config(git_tag_config); + strbuf_init(&buf, 0); for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -376,11 +359,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("option -m needs an argument."); if (message) die("only one -F or -m option is allowed."); - message = xstrdup(argv[i]); + strbuf_addstr(&buf, argv[i]); + message = 1; continue; } if (!strcmp(arg, "-F")) { - unsigned long len; int fd; annotate = 1; @@ -398,12 +381,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("could not open '%s': %s", argv[i], strerror(errno)); } - len = 1024; - message = xmalloc(len); - if (read_fd(fd, &message, &len)) { - free(message); + if (strbuf_read(&buf, fd, 1024) < 0) { die("cannot read %s", argv[i]); } + message = 1; continue; } if (!strcmp(arg, "-u")) { @@ -451,7 +432,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("tag '%s' already exists", tag); if (annotate) - create_tag(object, tag, message, sign, object); + create_tag(object, tag, &buf, message, sign, object); lock = lock_any_ref_for_update(ref, prev, 0); if (!lock) @@ -459,5 +440,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (write_ref_sha1(lock, object, NULL) < 0) die("%s: cannot update the ref", ref); + strbuf_release(&buf); return 0; } diff --git a/builtin-update-index.c b/builtin-update-index.c index 55fb679d68..acd5ab5926 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -4,7 +4,6 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" -#include "strbuf.h" #include "quote.h" #include "cache-tree.h" #include "tree-walk.h" @@ -296,7 +295,7 @@ static void update_one(const char *path, const char *prefix, int prefix_length) static void read_index_info(int line_termination) { struct strbuf buf; - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *ptr, *tab; char *path_name; @@ -321,8 +320,7 @@ static void read_index_info(int line_termination) * This format is to put higher order stages into the * index file and matches git-ls-files --stage output. */ - read_line(&buf, stdin, line_termination); - if (buf.eof) + if (strbuf_getline(&buf, stdin, line_termination) == EOF) break; errno = 0; @@ -384,6 +382,7 @@ static void read_index_info(int line_termination) bad_line: die("malformed index info %s", buf.buf); } + strbuf_release(&buf); } static const char update_index_usage[] = @@ -707,12 +706,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) } if (read_from_stdin) { struct strbuf buf; - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *path_name; const char *p; - read_line(&buf, stdin, line_termination); - if (buf.eof) + if (strbuf_getline(&buf, stdin, line_termination) == EOF) break; if (line_termination && buf.buf[0] == '"') path_name = unquote_c_style(buf.buf, NULL); @@ -727,6 +725,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (path_name != buf.buf) free(path_name); } + strbuf_release(&buf); } finish: diff --git a/cache-tree.c b/cache-tree.c index 077f034369..5471844af6 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -235,8 +235,7 @@ static int update_one(struct cache_tree *it, int missing_ok, int dryrun) { - unsigned long size, offset; - char *buffer; + struct strbuf buffer; int i; if (0 <= it->entry_count && has_sha1_file(it->sha1)) @@ -293,9 +292,7 @@ static int update_one(struct cache_tree *it, /* * Then write out the tree object for this level. */ - size = 8192; - buffer = xmalloc(size); - offset = 0; + strbuf_init(&buffer, 8192); for (i = 0; i < entries; i++) { struct cache_entry *ce = cache[i]; @@ -332,15 +329,9 @@ static int update_one(struct cache_tree *it, if (!ce->ce_mode) continue; /* entry being removed */ - if (size < offset + entlen + 100) { - size = alloc_nr(offset + entlen + 100); - buffer = xrealloc(buffer, size); - } - offset += sprintf(buffer + offset, - "%o %.*s", mode, entlen, path + baselen); - buffer[offset++] = 0; - hashcpy((unsigned char*)buffer + offset, sha1); - offset += 20; + strbuf_grow(&buffer, entlen + 100); + strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); + strbuf_add(&buffer, sha1, 20); #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", @@ -349,10 +340,10 @@ static int update_one(struct cache_tree *it, } if (dryrun) - hash_sha1_file(buffer, offset, tree_type, it->sha1); + hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); else - write_sha1_file(buffer, offset, tree_type, it->sha1); - free(buffer); + write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); + strbuf_release(&buffer); it->entry_count = i; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", @@ -378,12 +369,10 @@ int cache_tree_update(struct cache_tree *it, return 0; } -static void *write_one(struct cache_tree *it, +static void write_one(struct cache_tree *it, char *path, int pathlen, - char *buffer, - unsigned long *size, - unsigned long *offset) + struct strbuf *buffer) { int i; @@ -393,13 +382,9 @@ static void *write_one(struct cache_tree *it, * tree-sha1 (missing if invalid) * subtree_nr "cache-tree" entries for subtrees. */ - if (*size < *offset + pathlen + 100) { - *size = alloc_nr(*offset + pathlen + 100); - buffer = xrealloc(buffer, *size); - } - *offset += sprintf(buffer + *offset, "%.*s%c%d %d\n", - pathlen, path, 0, - it->entry_count, it->subtree_nr); + strbuf_grow(buffer, pathlen + 100); + strbuf_add(buffer, path, pathlen); + strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr); #if DEBUG if (0 <= it->entry_count) @@ -412,8 +397,7 @@ static void *write_one(struct cache_tree *it, #endif if (0 <= it->entry_count) { - hashcpy((unsigned char*)buffer + *offset, it->sha1); - *offset += 20; + strbuf_add(buffer, it->sha1, 20); } for (i = 0; i < it->subtree_nr; i++) { struct cache_tree_sub *down = it->down[i]; @@ -423,21 +407,20 @@ static void *write_one(struct cache_tree *it, prev->name, prev->namelen) <= 0) die("fatal - unsorted cache subtree"); } - buffer = write_one(down->cache_tree, down->name, down->namelen, - buffer, size, offset); + write_one(down->cache_tree, down->name, down->namelen, buffer); } - return buffer; } void *cache_tree_write(struct cache_tree *root, unsigned long *size_p) { char path[PATH_MAX]; - unsigned long size = 8192; - char *buffer = xmalloc(size); + struct strbuf buffer; - *size_p = 0; path[0] = 0; - return write_one(root, path, 0, buffer, &size, size_p); + strbuf_init(&buffer, 0); + write_one(root, path, 0, &buffer); + *size_p = buffer.len; + return strbuf_detach(&buffer); } static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) @@ -2,6 +2,7 @@ #define CACHE_H #include "git-compat-util.h" +#include "strbuf.h" #include SHA1_HEADER #include <zlib.h> @@ -270,7 +271,6 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); -extern int read_fd(int fd, char **return_buf, unsigned long *return_size); extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); @@ -591,8 +591,9 @@ extern void trace_printf(const char *format, ...); extern void trace_argv_printf(const char **argv, int count, const char *format, ...); /* convert.c */ -extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep); -extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep); +/* returns 1 if *dst was used */ +extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst); +extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* diff.c */ extern int diff_auto_refresh_index; @@ -458,11 +458,11 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) /* * Generic support for pretty-printing the header */ -static int get_one_line(const char *msg, unsigned long len) +static int get_one_line(const char *msg) { int ret = 0; - while (len--) { + for (;;) { char c = *msg++; if (!c) break; @@ -485,31 +485,25 @@ static int is_rfc2047_special(char ch) return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); } -static int add_rfc2047(char *buf, const char *line, int len, +static void add_rfc2047(struct strbuf *sb, const char *line, int len, const char *encoding) { - char *bp = buf; - int i, needquote; - char q_encoding[128]; - const char *q_encoding_fmt = "=?%s?q?"; + int i, last; - for (i = needquote = 0; !needquote && i < len; i++) { + for (i = 0; i < len; i++) { int ch = line[i]; if (non_ascii(ch)) - needquote++; - if ((i + 1 < len) && - (ch == '=' && line[i+1] == '?')) - needquote++; + goto needquote; + if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) + goto needquote; } - if (!needquote) - return sprintf(buf, "%.*s", len, line); - - i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding); - if (sizeof(q_encoding) < i) - die("Insanely long encoding name %s", encoding); - memcpy(bp, q_encoding, i); - bp += i; - for (i = 0; i < len; i++) { + strbuf_add(sb, line, len); + return; + +needquote: + strbuf_grow(sb, len * 3 + strlen(encoding) + 100); + strbuf_addf(sb, "=?%s?q?", encoding); + for (i = last = 0; i < len; i++) { unsigned ch = line[i] & 0xFF; /* * We encode ' ' using '=20' even though rfc2047 @@ -518,40 +512,30 @@ static int add_rfc2047(char *buf, const char *line, int len, * leave the underscore in place. */ if (is_rfc2047_special(ch) || ch == ' ') { - sprintf(bp, "=%02X", ch); - bp += 3; + strbuf_add(sb, line + last, i - last); + strbuf_addf(sb, "=%02X", ch); + last = i + 1; } - else - *bp++ = ch; } - memcpy(bp, "?=", 2); - bp += 2; - return bp - buf; -} - -static unsigned long bound_rfc2047(unsigned long len, const char *encoding) -{ - /* upper bound of q encoded string of length 'len' */ - unsigned long elen = strlen(encoding); - - return len * 3 + elen + 100; + strbuf_add(sb, line + last, len - last); + strbuf_addstr(sb, "?="); } -static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, +static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding) { char *date; int namelen; unsigned long time; - int tz, ret; + int tz; const char *filler = " "; if (fmt == CMIT_FMT_ONELINE) - return 0; + return; date = strchr(line, '>'); if (!date) - return 0; + return; namelen = ++date - line; time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); @@ -560,42 +544,34 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, char *name_tail = strchr(line, '<'); int display_name_length; if (!name_tail) - return 0; + return; while (line < name_tail && isspace(name_tail[-1])) name_tail--; display_name_length = name_tail - line; filler = ""; - strcpy(buf, "From: "); - ret = strlen(buf); - ret += add_rfc2047(buf + ret, line, display_name_length, - encoding); - memcpy(buf + ret, name_tail, namelen - display_name_length); - ret += namelen - display_name_length; - buf[ret++] = '\n'; - } - else { - ret = sprintf(buf, "%s: %.*s%.*s\n", what, + strbuf_addstr(sb, "From: "); + add_rfc2047(sb, line, display_name_length, encoding); + strbuf_add(sb, name_tail, namelen - display_name_length); + strbuf_addch(sb, '\n'); + } else { + strbuf_addf(sb, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); } switch (fmt) { case CMIT_FMT_MEDIUM: - ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, dmode)); + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); break; case CMIT_FMT_EMAIL: - ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, DATE_RFC2822)); + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); break; case CMIT_FMT_FULLER: - ret += sprintf(buf + ret, "%sDate: %s\n", what, - show_date(time, tz, dmode)); + strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); break; default: /* notin' */ break; } - return ret; } static int is_empty_line(const char *line, int *len_p) @@ -607,16 +583,16 @@ static int is_empty_line(const char *line, int *len_p) return !len; } -static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev) +static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, + const struct commit *commit, int abbrev) { struct commit_list *parent = commit->parents; - int offset; if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || !parent || !parent->next) - return 0; + return; - offset = sprintf(buf, "Merge:"); + strbuf_addstr(sb, "Merge:"); while (parent) { struct commit *p = parent->item; @@ -629,10 +605,9 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; - offset += sprintf(buf + offset, " %s%s", hex, dots); + strbuf_addf(sb, " %s%s", hex, dots); } - buf[offset++] = '\n'; - return offset; + strbuf_addch(sb, '\n'); } static char *get_header(const struct commit *commit, const char *key) @@ -665,47 +640,34 @@ static char *get_header(const struct commit *commit, const char *key) static char *replace_encoding_header(char *buf, const char *encoding) { - char *encoding_header = strstr(buf, "\nencoding "); - char *header_end = strstr(buf, "\n\n"); - char *end_of_encoding_header; - int encoding_header_pos; - int encoding_header_len; - int new_len; - int need_len; - int buflen = strlen(buf) + 1; - - if (!header_end) - header_end = buf + buflen; - if (!encoding_header || encoding_header >= header_end) - return buf; - encoding_header++; - end_of_encoding_header = strchr(encoding_header, '\n'); - if (!end_of_encoding_header) + struct strbuf tmp; + size_t start, len; + char *cp = buf; + + /* guess if there is an encoding header before a \n\n */ + while (strncmp(cp, "encoding ", strlen("encoding "))) { + cp = strchr(cp, '\n'); + if (!cp || *++cp == '\n') + return buf; + } + start = cp - buf; + cp = strchr(cp, '\n'); + if (!cp) return buf; /* should not happen but be defensive */ - end_of_encoding_header++; - - encoding_header_len = end_of_encoding_header - encoding_header; - encoding_header_pos = encoding_header - buf; + len = cp + 1 - (buf + start); + strbuf_init(&tmp, 0); + strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); if (is_encoding_utf8(encoding)) { /* we have re-coded to UTF-8; drop the header */ - memmove(encoding_header, end_of_encoding_header, - buflen - (encoding_header_pos + encoding_header_len)); - return buf; - } - new_len = strlen(encoding); - need_len = new_len + strlen("encoding \n"); - if (encoding_header_len < need_len) { - buf = xrealloc(buf, buflen + (need_len - encoding_header_len)); - encoding_header = buf + encoding_header_pos; - end_of_encoding_header = encoding_header + encoding_header_len; + strbuf_splice(&tmp, start, len, NULL, 0); + } else { + /* just replaces XXXX in 'encoding XXXX\n' */ + strbuf_splice(&tmp, start + strlen("encoding "), + len - strlen("encoding \n"), + encoding, strlen(encoding)); } - memmove(end_of_encoding_header + (need_len - encoding_header_len), - end_of_encoding_header, - buflen - (encoding_header_pos + encoding_header_len)); - memcpy(encoding_header + 9, encoding, strlen(encoding)); - encoding_header[9 + new_len] = '\n'; - return buf; + return tmp.buf; } static char *logmsg_reencode(const struct commit *commit, @@ -787,8 +749,8 @@ static void fill_person(struct interp *table, const char *msg, int len) interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); } -long format_commit_message(const struct commit *commit, const void *format, - char **buf_p, unsigned long *space_p) +void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb) { struct interp table[] = { { "%H" }, /* commit hash */ @@ -841,6 +803,7 @@ long format_commit_message(const struct commit *commit, const void *format, }; struct commit_list *p; char parents[1024]; + unsigned long len; int i; enum { HEADER, SUBJECT, BODY } state; const char *msg = commit->buffer; @@ -921,21 +884,15 @@ long format_commit_message(const struct commit *commit, const void *format, if (!table[i].value) interp_set_entry(table, i, "<unknown>"); - do { - char *buf = *buf_p; - unsigned long space = *space_p; - - space = interpolate(buf, space, format, - table, ARRAY_SIZE(table)); - if (!space) - break; - buf = xrealloc(buf, space); - *buf_p = buf; - *space_p = space; - } while (1); + len = interpolate(sb->buf + sb->len, strbuf_avail(sb), + format, table, ARRAY_SIZE(table)); + if (len > strbuf_avail(sb)) { + strbuf_grow(sb, len); + interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, + format, table, ARRAY_SIZE(table)); + } + strbuf_setlen(sb, sb->len + len); interp_clear_table(table, ARRAY_SIZE(table)); - - return strlen(*buf_p); } static void pp_header(enum cmit_fmt fmt, @@ -944,34 +901,24 @@ static void pp_header(enum cmit_fmt fmt, const char *encoding, const struct commit *commit, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p) + struct strbuf *sb) { int parents_shown = 0; for (;;) { const char *line = *msg_p; - char *dst; - int linelen = get_one_line(*msg_p, *len_p); - unsigned long len; + int linelen = get_one_line(*msg_p); if (!linelen) return; *msg_p += linelen; - *len_p -= linelen; if (linelen == 1) /* End of header */ return; - ALLOC_GROW(*buf_p, linelen + *ofs_p + 20, *space_p); - dst = *buf_p + *ofs_p; - if (fmt == CMIT_FMT_RAW) { - memcpy(dst, line, linelen); - *ofs_p += linelen; + strbuf_add(sb, line, linelen); continue; } @@ -989,10 +936,8 @@ static void pp_header(enum cmit_fmt fmt, parent = parent->next, num++) ; /* with enough slop */ - num = *ofs_p + num * 50 + 20; - ALLOC_GROW(*buf_p, num, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_merge_info(fmt, dst, commit, abbrev); + strbuf_grow(sb, num * 50 + 20); + add_merge_info(fmt, sb, commit, abbrev); parents_shown = 1; } @@ -1002,129 +947,82 @@ static void pp_header(enum cmit_fmt fmt, * FULLER shows both authors and dates. */ if (!memcmp(line, "author ", 7)) { - len = linelen; - if (fmt == CMIT_FMT_EMAIL) - len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_user_info("Author", fmt, dst, - line + 7, dmode, encoding); + strbuf_grow(sb, linelen + 80); + add_user_info("Author", fmt, sb, line + 7, dmode, encoding); } - if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - len = linelen; - if (fmt == CMIT_FMT_EMAIL) - len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_user_info("Commit", fmt, dst, - line + 10, dmode, encoding); + strbuf_grow(sb, linelen + 80); + add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); } } } static void pp_title_line(enum cmit_fmt fmt, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p, - int indent, + struct strbuf *sb, const char *subject, const char *after_subject, const char *encoding, int plain_non_ascii) { - char *title; - unsigned long title_alloc, title_len; - unsigned long len; + struct strbuf title; + + strbuf_init(&title, 80); - title_len = 0; - title_alloc = 80; - title = xmalloc(title_alloc); for (;;) { const char *line = *msg_p; - int linelen = get_one_line(line, *len_p); - *msg_p += linelen; - *len_p -= linelen; + int linelen = get_one_line(line); + *msg_p += linelen; if (!linelen || is_empty_line(line, &linelen)) break; - if (title_alloc <= title_len + linelen + 2) { - title_alloc = title_len + linelen + 80; - title = xrealloc(title, title_alloc); - } - len = 0; - if (title_len) { + strbuf_grow(&title, linelen + 2); + if (title.len) { if (fmt == CMIT_FMT_EMAIL) { - len++; - title[title_len++] = '\n'; + strbuf_addch(&title, '\n'); } - len++; - title[title_len++] = ' '; + strbuf_addch(&title, ' '); } - memcpy(title + title_len, line, linelen); - title_len += linelen; + strbuf_add(&title, line, linelen); } - /* Enough slop for the MIME header and rfc2047 */ - len = bound_rfc2047(title_len, encoding)+ 1000; - if (subject) - len += strlen(subject); - if (after_subject) - len += strlen(after_subject); - if (encoding) - len += strlen(encoding); - ALLOC_GROW(*buf_p, title_len + *ofs_p + len, *space_p); - + strbuf_grow(sb, title.len + 1024); if (subject) { - len = strlen(subject); - memcpy(*buf_p + *ofs_p, subject, len); - *ofs_p += len; - *ofs_p += add_rfc2047(*buf_p + *ofs_p, - title, title_len, encoding); + strbuf_addstr(sb, subject); + add_rfc2047(sb, title.buf, title.len, encoding); } else { - memcpy(*buf_p + *ofs_p, title, title_len); - *ofs_p += title_len; + strbuf_addbuf(sb, &title); } - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_addch(sb, '\n'); + if (plain_non_ascii) { const char *header_fmt = "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=%s\n" "Content-Transfer-Encoding: 8bit\n"; - *ofs_p += snprintf(*buf_p + *ofs_p, - *space_p - *ofs_p, - header_fmt, encoding); + strbuf_addf(sb, header_fmt, encoding); } if (after_subject) { - len = strlen(after_subject); - memcpy(*buf_p + *ofs_p, after_subject, len); - *ofs_p += len; + strbuf_addstr(sb, after_subject); } - free(title); if (fmt == CMIT_FMT_EMAIL) { - ALLOC_GROW(*buf_p, *ofs_p + 20, *space_p); - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_addch(sb, '\n'); } + strbuf_release(&title); } static void pp_remainder(enum cmit_fmt fmt, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p, + struct strbuf *sb, int indent) { int first = 1; for (;;) { const char *line = *msg_p; - int linelen = get_one_line(line, *len_p); + int linelen = get_one_line(line); *msg_p += linelen; - *len_p -= linelen; if (!linelen) break; @@ -1137,36 +1035,32 @@ static void pp_remainder(enum cmit_fmt fmt, } first = 0; - ALLOC_GROW(*buf_p, *ofs_p + linelen + indent + 20, *space_p); + strbuf_grow(sb, linelen + indent + 20); if (indent) { - memset(*buf_p + *ofs_p, ' ', indent); - *ofs_p += indent; + memset(sb->buf + sb->len, ' ', indent); + strbuf_setlen(sb, sb->len + indent); } - memcpy(*buf_p + *ofs_p, line, linelen); - *ofs_p += linelen; - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_add(sb, line, linelen); + strbuf_addch(sb, '\n'); } } -unsigned long pretty_print_commit(enum cmit_fmt fmt, - const struct commit *commit, - unsigned long len, - char **buf_p, unsigned long *space_p, - int abbrev, const char *subject, - const char *after_subject, +void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb, int abbrev, + const char *subject, const char *after_subject, enum date_mode dmode) { - unsigned long offset = 0; unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; int plain_non_ascii = 0; char *reencoded; const char *encoding; - char *buf; - if (fmt == CMIT_FMT_USERFORMAT) - return format_commit_message(commit, user_format, buf_p, space_p); + if (fmt == CMIT_FMT_USERFORMAT) { + format_commit_message(commit, user_format, sb); + return; + } encoding = (git_log_output_encoding ? git_log_output_encoding @@ -1176,7 +1070,6 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, reencoded = logmsg_reencode(commit, encoding); if (reencoded) { msg = reencoded; - len = strlen(reencoded); } if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) @@ -1191,14 +1084,13 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, if (fmt == CMIT_FMT_EMAIL && !after_subject) { int i, ch, in_body; - for (in_body = i = 0; (ch = msg[i]) && i < len; i++) { + for (in_body = i = 0; (ch = msg[i]); i++) { if (!in_body) { /* author could be non 7-bit ASCII but * the log may be so; skip over the * header part first. */ - if (ch == '\n' && - i + 1 < len && msg[i+1] == '\n') + if (ch == '\n' && msg[i+1] == '\n') in_body = 1; } else if (non_ascii(ch)) { @@ -1208,59 +1100,44 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, } } - pp_header(fmt, abbrev, dmode, encoding, - commit, &msg, &len, - &offset, buf_p, space_p); + pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); if (fmt != CMIT_FMT_ONELINE && !subject) { - ALLOC_GROW(*buf_p, offset + 20, *space_p); - (*buf_p)[offset++] = '\n'; + strbuf_addch(sb, '\n'); } /* Skip excess blank lines at the beginning of body, if any... */ for (;;) { - int linelen = get_one_line(msg, len); + int linelen = get_one_line(msg); int ll = linelen; if (!linelen) break; if (!is_empty_line(msg, &ll)) break; msg += linelen; - len -= linelen; } /* These formats treat the title line specially. */ - if (fmt == CMIT_FMT_ONELINE - || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, &len, &offset, - buf_p, space_p, indent, - subject, after_subject, encoding, - plain_non_ascii); - - beginning_of_body = offset; - if (fmt != CMIT_FMT_ONELINE) - pp_remainder(fmt, &msg, &len, &offset, - buf_p, space_p, indent); - - while (offset && isspace((*buf_p)[offset-1])) - offset--; + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + pp_title_line(fmt, &msg, sb, subject, + after_subject, encoding, plain_non_ascii); - ALLOC_GROW(*buf_p, offset + 20, *space_p); - buf = *buf_p; + beginning_of_body = sb->len; + if (fmt != CMIT_FMT_ONELINE) + pp_remainder(fmt, &msg, sb, indent); + strbuf_rtrim(sb); /* Make sure there is an EOLN for the non-oneline case */ if (fmt != CMIT_FMT_ONELINE) - buf[offset++] = '\n'; + strbuf_addch(sb, '\n'); /* * The caller may append additional body text in e-mail * format. Make sure we did not strip the blank line * between the header and the body. */ - if (fmt == CMIT_FMT_EMAIL && offset <= beginning_of_body) - buf[offset++] = '\n'; - buf[offset] = '\0'; + if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + strbuf_addch(sb, '\n'); free(reencoded); - return offset; } struct commit *pop_commit(struct commit_list **stack) @@ -1339,12 +1216,12 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, next=next->next; } /* - * find the tips - * - * tips are nodes not reachable from any other node in the list - * - * the tips serve as a starting set for the work queue. - */ + * find the tips + * + * tips are nodes not reachable from any other node in the list + * + * the tips serve as a starting set for the work queue. + */ next=*list; insert = &work; while (next) { @@ -1371,9 +1248,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, if (pn) { /* * parents are only enqueued for emission - * when all their children have been emitted thereby - * guaranteeing topological order. - */ + * when all their children have been emitted thereby + * guaranteeing topological order. + */ pn->indegree--; if (!pn->indegree) { if (!lifo) @@ -1385,9 +1262,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, parents=parents->next; } /* - * work_item is a commit all of whose children - * have already been emitted. we can emit it now. - */ + * work_item is a commit all of whose children + * have already been emitted. we can emit it now. + */ *pptr = work_node->list_item; pptr = &(*pptr)->next; *pptr = NULL; @@ -1483,8 +1360,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) } struct commit_list *get_merge_bases(struct commit *one, - struct commit *two, - int cleanup) + struct commit *two, int cleanup) { struct commit_list *list; struct commit **rslt; @@ -3,6 +3,7 @@ #include "object.h" #include "tree.h" +#include "strbuf.h" #include "decorate.h" struct commit_list { @@ -61,8 +62,12 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); -extern long format_commit_message(const struct commit *commit, const void *template, char **buf_p, unsigned long *space_p); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode); +extern void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb); +extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, + struct strbuf *, + int abbrev, const char *subject, + const char *after_subject, enum date_mode); /** Removes the first commit from a list sorted by date, and adds all * of its parents. @@ -80,24 +80,19 @@ static int is_binary(unsigned long size, struct text_stat *stats) return 0; } -static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action) +static int crlf_to_git(const char *path, const char *src, size_t len, + struct strbuf *buf, int action) { - char *buffer, *dst; - unsigned long size, nsize; struct text_stat stats; + char *dst; - if ((action == CRLF_BINARY) || !auto_crlf) - return NULL; - - size = *sizep; - if (!size) - return NULL; - - gather_stats(src, size, &stats); + if ((action == CRLF_BINARY) || !auto_crlf || !len) + return 0; + gather_stats(src, len, &stats); /* No CR? Nothing to convert, regardless. */ if (!stats.cr) - return NULL; + return 0; if (action == CRLF_GUESS) { /* @@ -106,24 +101,17 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep * stuff? */ if (stats.cr != stats.crlf) - return NULL; + return 0; /* * And add some heuristics for binary vs text, of course... */ - if (is_binary(size, &stats)) - return NULL; + if (is_binary(len, &stats)) + return 0; } - /* - * Ok, allocate a new buffer, fill it in, and return it - * to let the caller know that we switched buffers. - */ - nsize = size - stats.crlf; - buffer = xmalloc(nsize); - *sizep = nsize; - - dst = buffer; + strbuf_grow(buf, len); + dst = buf->buf; if (action == CRLF_GUESS) { /* * If we guessed, we already know we rejected a file with @@ -134,71 +122,72 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep unsigned char c = *src++; if (c != '\r') *dst++ = c; - } while (--size); + } while (--len); } else { do { unsigned char c = *src++; - if (! (c == '\r' && (1 < size && *src == '\n'))) + if (! (c == '\r' && (1 < len && *src == '\n'))) *dst++ = c; - } while (--size); + } while (--len); } - - return buffer; + strbuf_setlen(buf, dst - buf->buf); + return 1; } -static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action) +static int crlf_to_worktree(const char *path, const char *src, size_t len, + struct strbuf *buf, int action) { - char *buffer, *dst; - unsigned long size, nsize; + char *to_free = NULL; struct text_stat stats; - unsigned char last; if ((action == CRLF_BINARY) || (action == CRLF_INPUT) || auto_crlf <= 0) - return NULL; + return 0; - size = *sizep; - if (!size) - return NULL; + if (!len) + return 0; - gather_stats(src, size, &stats); + gather_stats(src, len, &stats); /* No LF? Nothing to convert, regardless. */ if (!stats.lf) - return NULL; + return 0; /* Was it already in CRLF format? */ if (stats.lf == stats.crlf) - return NULL; + return 0; if (action == CRLF_GUESS) { /* If we have any bare CR characters, we're not going to touch it */ if (stats.cr != stats.crlf) - return NULL; + return 0; - if (is_binary(size, &stats)) - return NULL; + if (is_binary(len, &stats)) + return 0; } - /* - * Ok, allocate a new buffer, fill it in, and return it - * to let the caller know that we switched buffers. - */ - nsize = size + stats.lf - stats.crlf; - buffer = xmalloc(nsize); - *sizep = nsize; - last = 0; - - dst = buffer; - do { - unsigned char c = *src++; - if (c == '\n' && last != '\r') - *dst++ = '\r'; - *dst++ = c; - last = c; - } while (--size); - - return buffer; + /* are we "faking" in place editing ? */ + if (src == buf->buf) + to_free = strbuf_detach(buf); + + strbuf_grow(buf, len + stats.lf - stats.crlf); + for (;;) { + const char *nl = memchr(src, '\n', len); + if (!nl) + break; + if (nl > src && nl[-1] == '\r') { + strbuf_add(buf, src, nl + 1 - src); + } else { + strbuf_add(buf, src, nl - src); + strbuf_addstr(buf, "\r\n"); + } + len -= nl + 1 - src; + src = nl + 1; + } + strbuf_add(buf, src, len); + + free(to_free); + return 1; } static int filter_buffer(const char *path, const char *src, @@ -246,8 +235,8 @@ static int filter_buffer(const char *path, const char *src, return (write_err || status); } -static char *apply_filter(const char *path, const char *src, - unsigned long *sizep, const char *cmd) +static int apply_filter(const char *path, const char *src, size_t len, + struct strbuf *dst, const char *cmd) { /* * Create a pipeline to have the command filter the buffer's @@ -255,21 +244,19 @@ static char *apply_filter(const char *path, const char *src, * * (child --> cmd) --> us */ - const int SLOP = 4096; int pipe_feed[2]; - int status; - char *dst; - unsigned long dstsize, dstalloc; + int status, ret = 1; struct child_process child_process; + struct strbuf nbuf; if (!cmd) - return NULL; + return 0; memset(&child_process, 0, sizeof(child_process)); if (pipe(pipe_feed) < 0) { error("cannot create pipe to run external filter %s", cmd); - return NULL; + return 0; } fflush(NULL); @@ -278,54 +265,37 @@ static char *apply_filter(const char *path, const char *src, error("cannot fork to run external filter %s", cmd); close(pipe_feed[0]); close(pipe_feed[1]); - return NULL; + return 0; } if (!child_process.pid) { dup2(pipe_feed[1], 1); close(pipe_feed[0]); close(pipe_feed[1]); - exit(filter_buffer(path, src, *sizep, cmd)); + exit(filter_buffer(path, src, len, cmd)); } close(pipe_feed[1]); - dstalloc = *sizep; - dst = xmalloc(dstalloc); - dstsize = 0; - - while (1) { - ssize_t numread = xread(pipe_feed[0], dst + dstsize, - dstalloc - dstsize); - - if (numread <= 0) { - if (!numread) - break; - error("read from external filter %s failed", cmd); - free(dst); - dst = NULL; - break; - } - dstsize += numread; - if (dstalloc <= dstsize + SLOP) { - dstalloc = dstsize + SLOP; - dst = xrealloc(dst, dstalloc); - } + strbuf_init(&nbuf, 0); + if (strbuf_read(&nbuf, pipe_feed[0], len) < 0) { + error("read from external filter %s failed", cmd); + ret = 0; } if (close(pipe_feed[0])) { - error("read from external filter %s failed", cmd); - free(dst); - dst = NULL; + ret = error("read from external filter %s failed", cmd); + ret = 0; } - status = finish_command(&child_process); if (status) { - error("external filter %s failed %d", cmd, -status); - free(dst); - dst = NULL; + ret = error("external filter %s failed %d", cmd, -status); + ret = 0; } - if (dst) - *sizep = dstsize; - return dst; + if (ret) { + *dst = nbuf; + } else { + strbuf_release(&nbuf); + } + return ret; } static struct convert_driver { @@ -449,137 +419,104 @@ static int count_ident(const char *cp, unsigned long size) return cnt; } -static char *ident_to_git(const char *path, const char *src, unsigned long *sizep, int ident) +static int ident_to_git(const char *path, const char *src, size_t len, + struct strbuf *buf, int ident) { - int cnt; - unsigned long size; - char *dst, *buf; + char *dst, *dollar; - if (!ident) - return NULL; - size = *sizep; - cnt = count_ident(src, size); - if (!cnt) - return NULL; - buf = xmalloc(size); - - for (dst = buf; size; size--) { - char ch = *src++; - *dst++ = ch; - if ((ch == '$') && (3 <= size) && - !memcmp("Id:", src, 3)) { - unsigned long rem = size - 3; - const char *cp = src + 3; - do { - ch = *cp++; - if (ch == '$') - break; - rem--; - } while (rem); - if (!rem) - continue; + if (!ident || !count_ident(src, len)) + return 0; + + strbuf_grow(buf, len); + dst = buf->buf; + for (;;) { + dollar = memchr(src, '$', len); + if (!dollar) + break; + memcpy(dst, src, dollar + 1 - src); + dst += dollar + 1 - src; + len -= dollar + 1 - src; + src = dollar + 1; + + if (len > 3 && !memcmp(src, "Id:", 3)) { + dollar = memchr(src + 3, '$', len - 3); + if (!dollar) + break; memcpy(dst, "Id$", 3); dst += 3; - size -= (cp - src); - src = cp; + len -= dollar + 1 - src; + src = dollar + 1; } } - - *sizep = dst - buf; - return buf; + memcpy(dst, src, len); + strbuf_setlen(buf, dst + len - buf->buf); + return 1; } -static char *ident_to_worktree(const char *path, const char *src, unsigned long *sizep, int ident) +static int ident_to_worktree(const char *path, const char *src, size_t len, + struct strbuf *buf, int ident) { - int cnt; - unsigned long size; - char *dst, *buf; unsigned char sha1[20]; + char *to_free = NULL, *dollar; + int cnt; if (!ident) - return NULL; + return 0; - size = *sizep; - cnt = count_ident(src, size); + cnt = count_ident(src, len); if (!cnt) - return NULL; + return 0; - hash_sha1_file(src, size, "blob", sha1); - buf = xmalloc(size + cnt * 43); - - for (dst = buf; size; size--) { - const char *cp; - /* Fetch next source character, move the pointer on */ - char ch = *src++; - /* Copy the current character to the destination */ - *dst++ = ch; - /* If the current character is "$" or there are less than three - * remaining bytes or the two bytes following this one are not - * "Id", then simply read the next character */ - if ((ch != '$') || (size < 3) || memcmp("Id", src, 2)) - continue; - /* - * Here when - * - There are more than 2 bytes remaining - * - The current three bytes are "$Id" - * with - * - ch == "$" - * - src[0] == "I" - */ + /* are we "faking" in place editing ? */ + if (src == buf->buf) + to_free = strbuf_detach(buf); + hash_sha1_file(src, len, "blob", sha1); - /* - * It's possible that an expanded Id has crept its way into the - * repository, we cope with that by stripping the expansion out - */ - if (src[2] == ':') { - /* Expanded keywords have "$Id:" at the front */ + strbuf_grow(buf, len + cnt * 43); + for (;;) { + /* step 1: run to the next '$' */ + dollar = memchr(src, '$', len); + if (!dollar) + break; + strbuf_add(buf, src, dollar + 1 - src); + len -= dollar + 1 - src; + src = dollar + 1; - /* discard up to but not including the closing $ */ - unsigned long rem = size - 3; - /* Point at first byte after the ":" */ - cp = src + 3; - /* - * Throw away characters until either - * - we reach a "$" - * - we run out of bytes (rem == 0) - */ - do { - ch = *cp; - if (ch == '$') - break; - cp++; - rem--; - } while (rem); - /* If the above finished because it ran out of characters, then - * this is an incomplete keyword, so don't run the expansion */ - if (!rem) - continue; - } else if (src[2] == '$') - cp = src + 2; - else - /* Anything other than "$Id:XXX$" or $Id$ and we skip the - * expansion */ + /* step 2: does it looks like a bit like Id:xxx$ or Id$ ? */ + if (len < 3 || memcmp("Id", src, 2)) continue; - /* cp is now pointing at the last $ of the keyword */ - - memcpy(dst, "Id: ", 4); - dst += 4; - memcpy(dst, sha1_to_hex(sha1), 40); - dst += 40; - *dst++ = ' '; + /* step 3: skip over Id$ or Id:xxxxx$ */ + if (src[2] == '$') { + src += 3; + len -= 3; + } else if (src[2] == ':') { + /* + * It's possible that an expanded Id has crept its way into the + * repository, we cope with that by stripping the expansion out + */ + dollar = memchr(src + 3, '$', len - 3); + if (!dollar) { + /* incomplete keyword, no more '$', so just quit the loop */ + break; + } - /* Adjust for the characters we've discarded */ - size -= (cp - src); - src = cp; + len -= dollar + 1 - src; + src = dollar + 1; + } else { + /* it wasn't a "Id$" or "Id:xxxx$" */ + continue; + } - /* Copy the final "$" */ - *dst++ = *src++; - size--; + /* step 4: substitute */ + strbuf_addstr(buf, "Id: "); + strbuf_add(buf, sha1_to_hex(sha1), 40); + strbuf_addstr(buf, " $"); } + strbuf_add(buf, src, len); - *sizep = dst - buf; - return buf; + free(to_free); + return 1; } static int git_path_check_crlf(const char *path, struct git_attr_check *check) @@ -618,13 +555,12 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check) return !!ATTR_TRUE(value); } -char *convert_to_git(const char *path, const char *src, unsigned long *sizep) +int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst) { struct git_attr_check check[3]; int crlf = CRLF_GUESS; - int ident = 0; + int ident = 0, ret = 0; char *filter = NULL; - char *buf, *buf2; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { @@ -636,30 +572,25 @@ char *convert_to_git(const char *path, const char *src, unsigned long *sizep) filter = drv->clean; } - buf = apply_filter(path, src, sizep, filter); - - buf2 = crlf_to_git(path, buf ? buf : src, sizep, crlf); - if (buf2) { - free(buf); - buf = buf2; + ret |= apply_filter(path, src, len, dst, filter); + if (ret) { + src = dst->buf; + len = dst->len; } - - buf2 = ident_to_git(path, buf ? buf : src, sizep, ident); - if (buf2) { - free(buf); - buf = buf2; + ret |= crlf_to_git(path, src, len, dst, crlf); + if (ret) { + src = dst->buf; + len = dst->len; } - - return buf; + return ret | ident_to_git(path, src, len, dst, ident); } -char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep) +int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst) { struct git_attr_check check[3]; int crlf = CRLF_GUESS; - int ident = 0; + int ident = 0, ret = 0; char *filter = NULL; - char *buf, *buf2; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { @@ -671,19 +602,15 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long * filter = drv->smudge; } - buf = ident_to_worktree(path, src, sizep, ident); - - buf2 = crlf_to_worktree(path, buf ? buf : src, sizep, crlf); - if (buf2) { - free(buf); - buf = buf2; + ret |= ident_to_worktree(path, src, len, dst, ident); + if (ret) { + src = dst->buf; + len = dst->len; } - - buf2 = apply_filter(path, buf ? buf : src, sizep, filter); - if (buf2) { - free(buf); - buf = buf2; + ret |= crlf_to_worktree(path, src, len, dst, crlf); + if (ret) { + src = dst->buf; + len = dst->len; } - - return buf; + return ret | apply_filter(path, src, len, dst, filter); } @@ -1545,26 +1545,16 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int static int populate_from_stdin(struct diff_filespec *s) { -#define INCREMENT 1024 - char *buf; - unsigned long size; - ssize_t got; - - size = 0; - buf = NULL; - while (1) { - buf = xrealloc(buf, size + INCREMENT); - got = xread(0, buf + size, INCREMENT); - if (!got) - break; /* EOF */ - if (got < 0) - return error("error while reading from stdin %s", + struct strbuf buf; + + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 0) < 0) + return error("error while reading from stdin %s", strerror(errno)); - size += got; - } + s->should_munmap = 0; - s->data = buf; - s->size = size; + s->size = buf.len; + s->data = strbuf_detach(&buf); s->should_free = 1; return 0; } @@ -1609,10 +1599,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) if (!s->sha1_valid || reuse_worktree_file(s->path, s->sha1, 0)) { + struct strbuf buf; struct stat st; int fd; - char *buf; - unsigned long size; if (!strcmp(s->path, "-")) return populate_from_stdin(s); @@ -1653,13 +1642,12 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) /* * Convert from working tree format to canonical git format */ - size = s->size; - buf = convert_to_git(s->path, s->data, &size); - if (buf) { + strbuf_init(&buf, 0); + if (convert_to_git(s->path, s->data, s->size, &buf)) { munmap(s->data, s->size); s->should_munmap = 0; - s->data = buf; - s->size = size; + s->data = buf.buf; + s->size = buf.len; s->should_free = 1; } } @@ -104,7 +104,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout long wrote; switch (ntohl(ce->ce_mode) & S_IFMT) { - char *buf, *new; + char *new; + struct strbuf buf; unsigned long size; case S_IFREG: @@ -116,10 +117,11 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout /* * Convert from git internal format to working tree format */ - buf = convert_to_working_tree(ce->name, new, &size); - if (buf) { + strbuf_init(&buf, 0); + if (convert_to_working_tree(ce->name, new, size, &buf)) { free(new); - new = buf; + new = buf.buf; + size = buf.len; } if (to_tempfile) { diff --git a/fast-import.c b/fast-import.c index 078079d404..e2a4834706 100644 --- a/fast-import.c +++ b/fast-import.c @@ -149,7 +149,6 @@ Format of STDIN stream: #include "pack.h" #include "refs.h" #include "csum-file.h" -#include "strbuf.h" #include "quote.h" #define PACK_ID_BITS 16 @@ -183,11 +182,10 @@ struct mark_set struct last_object { - void *data; - unsigned long len; + struct strbuf data; uint32_t offset; unsigned int depth; - unsigned no_free:1; + unsigned no_swap : 1; }; struct mem_pool @@ -251,12 +249,6 @@ struct tag unsigned char sha1[20]; }; -struct dbuf -{ - void *buffer; - size_t capacity; -}; - struct hash_list { struct hash_list *next; @@ -317,15 +309,15 @@ static struct mark_set *marks; static const char* mark_file; /* Our last blob */ -static struct last_object last_blob; +static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 }; /* Tree management */ static unsigned int tree_entry_alloc = 1000; static void *avail_tree_entry; static unsigned int avail_tree_table_sz = 100; static struct avail_tree_content **avail_tree_table; -static struct dbuf old_tree; -static struct dbuf new_tree; +static struct strbuf old_tree = STRBUF_INIT; +static struct strbuf new_tree = STRBUF_INIT; /* Branch data */ static unsigned long max_active_branches = 5; @@ -340,14 +332,14 @@ static struct tag *last_tag; /* Input stream parsing */ static whenspec_type whenspec = WHENSPEC_RAW; -static struct strbuf command_buf; +static struct strbuf command_buf = STRBUF_INIT; static int unread_command_buf; static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL}; static struct recent_command *cmd_tail = &cmd_hist; static struct recent_command *rc_free; static unsigned int cmd_save = 100; static uintmax_t next_mark; -static struct dbuf new_data; +static struct strbuf new_data = STRBUF_INIT; static void write_branch_report(FILE *rpt, struct branch *b) { @@ -567,17 +559,6 @@ static char *pool_strdup(const char *s) return r; } -static void size_dbuf(struct dbuf *b, size_t maxlen) -{ - if (b->buffer) { - if (b->capacity >= maxlen) - return; - free(b->buffer); - } - b->capacity = ((maxlen / 1024) + 1) * 1024; - b->buffer = xmalloc(b->capacity); -} - static void insert_mark(uintmax_t idnum, struct object_entry *oe) { struct mark_set *s = marks; @@ -968,9 +949,7 @@ static void end_packfile(void) free(old_p); /* We can't carry a delta across packfiles. */ - free(last_blob.data); - last_blob.data = NULL; - last_blob.len = 0; + strbuf_release(&last_blob.data); last_blob.offset = 0; last_blob.depth = 0; } @@ -1006,8 +985,7 @@ static size_t encode_header( static int store_object( enum object_type type, - void *dat, - size_t datlen, + struct strbuf *dat, struct last_object *last, unsigned char *sha1out, uintmax_t mark) @@ -1021,10 +999,10 @@ static int store_object( z_stream s; hdrlen = sprintf((char*)hdr,"%s %lu", typename(type), - (unsigned long)datlen) + 1; + (unsigned long)dat->len) + 1; SHA1_Init(&c); SHA1_Update(&c, hdr, hdrlen); - SHA1_Update(&c, dat, datlen); + SHA1_Update(&c, dat->buf, dat->len); SHA1_Final(sha1, &c); if (sha1out) hashcpy(sha1out, sha1); @@ -1043,11 +1021,11 @@ static int store_object( return 1; } - if (last && last->data && last->depth < max_depth) { - delta = diff_delta(last->data, last->len, - dat, datlen, + if (last && last->data.buf && last->depth < max_depth) { + delta = diff_delta(last->data.buf, last->data.len, + dat->buf, dat->len, &deltalen, 0); - if (delta && deltalen >= datlen) { + if (delta && deltalen >= dat->len) { free(delta); delta = NULL; } @@ -1060,8 +1038,8 @@ static int store_object( s.next_in = delta; s.avail_in = deltalen; } else { - s.next_in = dat; - s.avail_in = datlen; + s.next_in = (void *)dat->buf; + s.avail_in = dat->len; } s.avail_out = deflateBound(&s, s.avail_in); s.next_out = out = xmalloc(s.avail_out); @@ -1084,8 +1062,8 @@ static int store_object( memset(&s, 0, sizeof(s)); deflateInit(&s, zlib_compression_level); - s.next_in = dat; - s.avail_in = datlen; + s.next_in = (void *)dat->buf; + s.avail_in = dat->len; s.avail_out = deflateBound(&s, s.avail_in); s.next_out = out = xrealloc(out, s.avail_out); while (deflate(&s, Z_FINISH) == Z_OK) @@ -1119,7 +1097,7 @@ static int store_object( } else { if (last) last->depth = 0; - hdrlen = encode_header(type, datlen, hdr); + hdrlen = encode_header(type, dat->len, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); pack_size += hdrlen; } @@ -1130,11 +1108,14 @@ static int store_object( free(out); free(delta); if (last) { - if (!last->no_free) - free(last->data); - last->data = dat; + if (last->no_swap) { + last->data = *dat; + } else { + struct strbuf tmp = *dat; + *dat = last->data; + last->data = tmp; + } last->offset = e->offset; - last->len = datlen; } return 0; } @@ -1230,14 +1211,10 @@ static int tecmp1 (const void *_a, const void *_b) b->name->str_dat, b->name->str_len, b->versions[1].mode); } -static void mktree(struct tree_content *t, - int v, - unsigned long *szp, - struct dbuf *b) +static void mktree(struct tree_content *t, int v, struct strbuf *b) { size_t maxlen = 0; unsigned int i; - char *c; if (!v) qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0); @@ -1249,28 +1226,23 @@ static void mktree(struct tree_content *t, maxlen += t->entries[i]->name->str_len + 34; } - size_dbuf(b, maxlen); - c = b->buffer; + strbuf_reset(b); + strbuf_grow(b, maxlen); for (i = 0; i < t->entry_count; i++) { struct tree_entry *e = t->entries[i]; if (!e->versions[v].mode) continue; - c += sprintf(c, "%o", (unsigned int)e->versions[v].mode); - *c++ = ' '; - strcpy(c, e->name->str_dat); - c += e->name->str_len + 1; - hashcpy((unsigned char*)c, e->versions[v].sha1); - c += 20; + strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode, + e->name->str_dat, '\0'); + strbuf_add(b, e->versions[v].sha1, 20); } - *szp = c - (char*)b->buffer; } static void store_tree(struct tree_entry *root) { struct tree_content *t = root->tree; unsigned int i, j, del; - unsigned long new_len; - struct last_object lo; + struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 }; struct object_entry *le; if (!is_null_sha1(root->versions[1].sha1)) @@ -1282,23 +1254,15 @@ static void store_tree(struct tree_entry *root) } le = find_object(root->versions[0].sha1); - if (!S_ISDIR(root->versions[0].mode) - || !le - || le->pack_id != pack_id) { - lo.data = NULL; - lo.depth = 0; - lo.no_free = 0; - } else { - mktree(t, 0, &lo.len, &old_tree); - lo.data = old_tree.buffer; + if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) { + mktree(t, 0, &old_tree); + lo.data = old_tree; lo.offset = le->offset; lo.depth = t->delta_depth; - lo.no_free = 1; } - mktree(t, 1, &new_len, &new_tree); - store_object(OBJ_TREE, new_tree.buffer, new_len, - &lo, root->versions[1].sha1, 0); + mktree(t, 1, &new_tree); + store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0); t->delta_depth = lo.depth; for (i = 0, j = 0, del = 0; i < t->entry_count; i++) { @@ -1585,20 +1549,25 @@ static void dump_marks(void) mark_file, strerror(errno)); } -static void read_next_command(void) +static int read_next_command(void) { + static int stdin_eof = 0; + + if (stdin_eof) { + unread_command_buf = 0; + return EOF; + } + do { if (unread_command_buf) { unread_command_buf = 0; - if (command_buf.eof) - return; } else { struct recent_command *rc; - command_buf.buf = NULL; - read_line(&command_buf, stdin, '\n'); - if (command_buf.eof) - return; + strbuf_detach(&command_buf); + stdin_eof = strbuf_getline(&command_buf, stdin, '\n'); + if (stdin_eof) + return EOF; rc = rc_free; if (rc) @@ -1617,6 +1586,8 @@ static void read_next_command(void) cmd_tail = rc; } } while (command_buf.buf[0] == '#'); + + return 0; } static void skip_optional_lf(void) @@ -1636,42 +1607,35 @@ static void cmd_mark(void) next_mark = 0; } -static void *cmd_data (size_t *size) +static void cmd_data(struct strbuf *sb) { - size_t length; - char *buffer; + strbuf_reset(sb); if (prefixcmp(command_buf.buf, "data ")) die("Expected 'data n' command, found: %s", command_buf.buf); if (!prefixcmp(command_buf.buf + 5, "<<")) { char *term = xstrdup(command_buf.buf + 5 + 2); - size_t sz = 8192, term_len = command_buf.len - 5 - 2; - length = 0; - buffer = xmalloc(sz); - command_buf.buf = NULL; + size_t term_len = command_buf.len - 5 - 2; + for (;;) { - read_line(&command_buf, stdin, '\n'); - if (command_buf.eof) + if (strbuf_getline(&command_buf, stdin, '\n') == EOF) die("EOF in data (terminator '%s' not found)", term); if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) break; - ALLOC_GROW(buffer, length + command_buf.len, sz); - memcpy(buffer + length, - command_buf.buf, - command_buf.len - 1); - length += command_buf.len - 1; - buffer[length++] = '\n'; + strbuf_addbuf(sb, &command_buf); + strbuf_addch(sb, '\n'); } free(term); } else { - size_t n = 0; + size_t n = 0, length; + length = strtoul(command_buf.buf + 5, NULL, 10); - buffer = xmalloc(length); + while (n < length) { - size_t s = fread(buffer + n, 1, length - n, stdin); + size_t s = strbuf_fread(sb, length - n, stdin); if (!s && feof(stdin)) die("EOF in data (%lu bytes remaining)", (unsigned long)(length - n)); @@ -1680,8 +1644,6 @@ static void *cmd_data (size_t *size) } skip_optional_lf(); - *size = length; - return buffer; } static int validate_raw_date(const char *src, char *result, int maxlen) @@ -1744,15 +1706,12 @@ static char *parse_ident(const char *buf) static void cmd_new_blob(void) { - size_t l; - void *d; + static struct strbuf buf = STRBUF_INIT; read_next_command(); cmd_mark(); - d = cmd_data(&l); - - if (store_object(OBJ_BLOB, d, l, &last_blob, NULL, next_mark)) - free(d); + cmd_data(&buf); + store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark); } static void unload_one_branch(void) @@ -1848,14 +1807,13 @@ static void file_change_m(struct branch *b) } if (inline_data) { - size_t l; - void *d; + static struct strbuf buf = STRBUF_INIT; + if (!p_uq) p = p_uq = xstrdup(p); read_next_command(); - d = cmd_data(&l); - if (store_object(OBJ_BLOB, d, l, &last_blob, sha1, 0)) - free(d); + cmd_data(&buf); + store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0); } else if (oe) { if (oe->type != OBJ_BLOB) die("Not a blob (actually a %s): %s", @@ -2062,9 +2020,8 @@ static struct hash_list *cmd_merge(unsigned int *count) static void cmd_new_commit(void) { + static struct strbuf msg = STRBUF_INIT; struct branch *b; - void *msg; - size_t msglen; char *sp; char *author = NULL; char *committer = NULL; @@ -2089,7 +2046,7 @@ static void cmd_new_commit(void) } if (!committer) die("Expected committer but didn't get one"); - msg = cmd_data(&msglen); + cmd_data(&msg); read_next_command(); cmd_from(b); merge_list = cmd_merge(&merge_count); @@ -2101,7 +2058,7 @@ static void cmd_new_commit(void) } /* file_change* */ - while (!command_buf.eof && command_buf.len > 1) { + while (command_buf.len > 0) { if (!prefixcmp(command_buf.buf, "M ")) file_change_m(b); else if (!prefixcmp(command_buf.buf, "D ")) @@ -2116,53 +2073,47 @@ static void cmd_new_commit(void) unread_command_buf = 1; break; } - read_next_command(); + if (read_next_command() == EOF) + break; } /* build the tree and the commit */ store_tree(&b->branch_tree); hashcpy(b->branch_tree.versions[0].sha1, b->branch_tree.versions[1].sha1); - size_dbuf(&new_data, 114 + msglen - + merge_count * 49 - + (author - ? strlen(author) + strlen(committer) - : 2 * strlen(committer))); - sp = new_data.buffer; - sp += sprintf(sp, "tree %s\n", + + strbuf_reset(&new_data); + strbuf_addf(&new_data, "tree %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1)); if (!is_null_sha1(b->sha1)) - sp += sprintf(sp, "parent %s\n", sha1_to_hex(b->sha1)); + strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1)); while (merge_list) { struct hash_list *next = merge_list->next; - sp += sprintf(sp, "parent %s\n", sha1_to_hex(merge_list->sha1)); + strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1)); free(merge_list); merge_list = next; } - sp += sprintf(sp, "author %s\n", author ? author : committer); - sp += sprintf(sp, "committer %s\n", committer); - *sp++ = '\n'; - memcpy(sp, msg, msglen); - sp += msglen; + strbuf_addf(&new_data, + "author %s\n" + "committer %s\n" + "\n", + author ? author : committer, committer); + strbuf_addbuf(&new_data, &msg); free(author); free(committer); - free(msg); - if (!store_object(OBJ_COMMIT, - new_data.buffer, sp - (char*)new_data.buffer, - NULL, b->sha1, next_mark)) + if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark)) b->pack_id = pack_id; b->last_commit = object_count_by_type[OBJ_COMMIT]; } static void cmd_new_tag(void) { + static struct strbuf msg = STRBUF_INIT; char *sp; const char *from; char *tagger; struct branch *s; - void *msg; - size_t msglen; struct tag *t; uintmax_t from_mark = 0; unsigned char sha1[20]; @@ -2213,24 +2164,21 @@ static void cmd_new_tag(void) /* tag payload/message */ read_next_command(); - msg = cmd_data(&msglen); + cmd_data(&msg); /* build the tag object */ - size_dbuf(&new_data, 67+strlen(t->name)+strlen(tagger)+msglen); - sp = new_data.buffer; - sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1)); - sp += sprintf(sp, "type %s\n", commit_type); - sp += sprintf(sp, "tag %s\n", t->name); - sp += sprintf(sp, "tagger %s\n", tagger); - *sp++ = '\n'; - memcpy(sp, msg, msglen); - sp += msglen; + strbuf_reset(&new_data); + strbuf_addf(&new_data, + "object %s\n" + "type %s\n" + "tag %s\n" + "tagger %s\n" + "\n", + sha1_to_hex(sha1), commit_type, t->name, tagger); + strbuf_addbuf(&new_data, &msg); free(tagger); - free(msg); - if (store_object(OBJ_TAG, new_data.buffer, - sp - (char*)new_data.buffer, - NULL, t->sha1, 0)) + if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0)) t->pack_id = MAX_PACK_ID; else t->pack_id = pack_id; @@ -2256,7 +2204,7 @@ static void cmd_reset_branch(void) else b = new_branch(sp); read_next_command(); - if (!cmd_from(b) && command_buf.len > 1) + if (!cmd_from(b) && command_buf.len > 0) unread_command_buf = 1; } @@ -2273,7 +2221,7 @@ static void cmd_checkpoint(void) static void cmd_progress(void) { - fwrite(command_buf.buf, 1, command_buf.len - 1, stdout); + fwrite(command_buf.buf, 1, command_buf.len, stdout); fputc('\n', stdout); fflush(stdout); skip_optional_lf(); @@ -2323,7 +2271,7 @@ int main(int argc, const char **argv) git_config(git_default_config); alloc_objects(object_entry_alloc); - strbuf_init(&command_buf); + strbuf_init(&command_buf, 0); atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*)); branch_table = xcalloc(branch_table_sz, sizeof(struct branch*)); avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*)); @@ -2381,11 +2329,8 @@ int main(int argc, const char **argv) prepare_packed_git(); start_packfile(); set_die_routine(die_nicely); - for (;;) { - read_next_command(); - if (command_buf.eof) - break; - else if (!strcmp("blob", command_buf.buf)) + while (read_next_command() != EOF) { + if (!strcmp("blob", command_buf.buf)) cmd_new_blob(); else if (!prefixcmp(command_buf.buf, "commit ")) cmd_new_commit(); @@ -6,7 +6,6 @@ #include "tag.h" #include "blob.h" #include "refs.h" -#include "strbuf.h" int get_tree = 0; int get_history = 0; @@ -218,13 +217,12 @@ int pull_targets_stdin(char ***target, const char ***write_ref) int targets = 0, targets_alloc = 0; struct strbuf buf; *target = NULL; *write_ref = NULL; - strbuf_init(&buf); + strbuf_init(&buf, 0); while (1) { char *rf_one = NULL; char *tg_one; - read_line(&buf, stdin, '\n'); - if (buf.eof) + if (strbuf_getline(&buf, stdin, '\n') == EOF) break; tg_one = buf.buf; rf_one = strchr(tg_one, '\t'); @@ -240,6 +238,7 @@ int pull_targets_stdin(char ***target, const char ***write_ref) (*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL; targets++; } + strbuf_release(&buf); return targets; } diff --git a/git-compat-util.h b/git-compat-util.h index 1bfbdeb94f..f23d934f66 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -211,19 +211,20 @@ static inline void *xmalloc(size_t size) return ret; } -static inline char *xstrndup(const char *str, size_t len) +static inline void *xmemdupz(const void *data, size_t len) { - char *p; - - p = memchr(str, '\0', len); - if (p) - len = p - str; - p = xmalloc(len + 1); - memcpy(p, str, len); + char *p = xmalloc(len + 1); + memcpy(p, data, len); p[len] = '\0'; return p; } +static inline char *xstrndup(const char *str, size_t len) +{ + char *p = memchr(str, '\0', len); + return xmemdupz(str, p ? p - str : len); +} + static inline void *xrealloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); diff --git a/imap-send.c b/imap-send.c index a5a0696084..86e4a0f6a0 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1160,28 +1160,18 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid ) static int read_message( FILE *f, msg_data_t *msg ) { - int len, r; + struct strbuf buf; - memset( msg, 0, sizeof *msg ); - len = CHUNKSIZE; - msg->data = xmalloc( len+1 ); - msg->data[0] = 0; - - while(!feof( f )) { - if (msg->len >= len) { - void *p; - len += CHUNKSIZE; - p = xrealloc(msg->data, len+1); - if (!p) - break; - msg->data = p; - } - r = fread( &msg->data[msg->len], 1, len - msg->len, f ); - if (r <= 0) + memset(msg, 0, sizeof(*msg)); + strbuf_init(&buf, 0); + + do { + if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0) break; - msg->len += r; - } - msg->data[msg->len] = 0; + } while (!feof(f)); + + msg->len = buf.len; + msg->data = strbuf_detach(&buf); return msg->len; } diff --git a/interpolate.c b/interpolate.c index 00826778fc..3de583238d 100644 --- a/interpolate.c +++ b/interpolate.c @@ -44,9 +44,8 @@ void interp_clear_table(struct interp *table, int ninterps) * { "%%", "%"}, * } * - * Returns 0 on a successful substitution pass that fits in result, - * Returns a number of bytes needed to hold the full substituted - * string otherwise. + * Returns the length of the substituted string (not including the final \0). + * Like with snprintf, if the result is >= reslen, then it overflowed. */ unsigned long interpolate(char *result, unsigned long reslen, @@ -61,8 +60,6 @@ unsigned long interpolate(char *result, unsigned long reslen, int i; char c; - memset(result, 0, reslen); - while ((c = *src)) { if (c == '%') { /* Try to match an interpolation string. */ @@ -78,9 +75,9 @@ unsigned long interpolate(char *result, unsigned long reslen, value = interps[i].value; valuelen = strlen(value); - if (newlen + valuelen + 1 < reslen) { + if (newlen + valuelen < reslen) { /* Substitute. */ - strncpy(dest, value, valuelen); + memcpy(dest, value, valuelen); dest += valuelen; } newlen += valuelen; @@ -95,8 +92,9 @@ unsigned long interpolate(char *result, unsigned long reslen, newlen++; } - if (newlen + 1 < reslen) - return 0; - else - return newlen + 2; + /* XXX: the previous loop always keep room for the ending NUL, + we just need to check if there was room for a NUL in the first place */ + if (reslen > 0) + *dest = '\0'; + return newlen; } diff --git a/log-tree.c b/log-tree.c index a6423718e7..3e5e6acfaf 100644 --- a/log-tree.c +++ b/log-tree.c @@ -79,25 +79,14 @@ static int detect_any_signoff(char *letter, int size) return seen_head && seen_name; } -static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p, - unsigned long at, const char *signoff) +static void append_signoff(struct strbuf *sb, const char *signoff) { static const char signed_off_by[] = "Signed-off-by: "; size_t signoff_len = strlen(signoff); int has_signoff = 0; char *cp; - char *buf; - unsigned long buf_sz; - - buf = *buf_p; - buf_sz = *buf_sz_p; - if (buf_sz <= at + strlen(signed_off_by) + signoff_len + 3) { - buf_sz += strlen(signed_off_by) + signoff_len + 3; - buf = xrealloc(buf, buf_sz); - *buf_p = buf; - *buf_sz_p = buf_sz; - } - cp = buf; + + cp = sb->buf; /* First see if we already have the sign-off by the signer */ while ((cp = strstr(cp, signed_off_by))) { @@ -105,29 +94,25 @@ static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p, has_signoff = 1; cp += strlen(signed_off_by); - if (cp + signoff_len >= buf + at) + if (cp + signoff_len >= sb->buf + sb->len) break; if (strncmp(cp, signoff, signoff_len)) continue; if (!isspace(cp[signoff_len])) continue; /* we already have him */ - return at; + return; } if (!has_signoff) - has_signoff = detect_any_signoff(buf, at); + has_signoff = detect_any_signoff(sb->buf, sb->len); if (!has_signoff) - buf[at++] = '\n'; - - strcpy(buf + at, signed_off_by); - at += strlen(signed_off_by); - strcpy(buf + at, signoff); - at += signoff_len; - buf[at++] = '\n'; - buf[at] = 0; - return at; + strbuf_addch(sb, '\n'); + + strbuf_addstr(sb, signed_off_by); + strbuf_add(sb, signoff, signoff_len); + strbuf_addch(sb, '\n'); } static unsigned int digits_in_number(unsigned int number) @@ -142,14 +127,12 @@ static unsigned int digits_in_number(unsigned int number) void show_log(struct rev_info *opt, const char *sep) { - char *msgbuf = NULL; - unsigned long msgbuf_len = 0; + struct strbuf msgbuf; struct log_info *log = opt->loginfo; struct commit *commit = log->commit, *parent = log->parent; int abbrev = opt->diffopt.abbrev; int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; - int len; const char *subject = NULL, *extra_headers = opt->extra_headers; opt->loginfo = NULL; @@ -288,18 +271,17 @@ void show_log(struct rev_info *opt, const char *sep) /* * And then the pretty-printed message itself */ - len = pretty_print_commit(opt->commit_format, commit, ~0u, - &msgbuf, &msgbuf_len, abbrev, subject, - extra_headers, opt->date_mode); + strbuf_init(&msgbuf, 0); + pretty_print_commit(opt->commit_format, commit, &msgbuf, + abbrev, subject, extra_headers, opt->date_mode); if (opt->add_signoff) - len = append_signoff(&msgbuf, &msgbuf_len, len, - opt->add_signoff); + append_signoff(&msgbuf, opt->add_signoff); if (opt->show_log_size) - printf("log size %i\n", len); + printf("log size %i\n", (int)msgbuf.len); - printf("%s%s%s", msgbuf, extra, sep); - free(msgbuf); + printf("%s%s%s", msgbuf.buf, extra, sep); + strbuf_release(&msgbuf); } int log_tree_diff_flush(struct rev_info *opt) @@ -111,8 +111,7 @@ static int verify_tag(char *buffer, unsigned long size) int main(int argc, char **argv) { - unsigned long size = 4096; - char *buffer = xmalloc(size); + struct strbuf buf; unsigned char result_sha1[20]; if (argc != 1) @@ -120,21 +119,20 @@ int main(int argc, char **argv) setup_git_directory(); - if (read_fd(0, &buffer, &size)) { - free(buffer); + strbuf_init(&buf, 0); + if (strbuf_read(&buf, 0, 4096) < 0) { die("could not read from stdin"); } /* Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger " */ - if (verify_tag(buffer, size) < 0) + if (verify_tag(buf.buf, buf.len) < 0) die("invalid tag signature file"); - if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0) + if (write_sha1_file(buf.buf, buf.len, tag_type, result_sha1) < 0) die("unable to write tag file"); - free(buffer); - + strbuf_release(&buf); printf("%s\n", sha1_to_hex(result_sha1)); return 0; } @@ -4,7 +4,6 @@ * Copyright (c) Junio C Hamano, 2006 */ #include "cache.h" -#include "strbuf.h" #include "quote.h" #include "tree.h" @@ -44,30 +43,22 @@ static int ent_compare(const void *a_, const void *b_) static void write_tree(unsigned char *sha1) { - char *buffer; - unsigned long size, offset; + struct strbuf buf; + size_t size; int i; qsort(entries, used, sizeof(*entries), ent_compare); for (size = i = 0; i < used; i++) size += 32 + entries[i]->len; - buffer = xmalloc(size); - offset = 0; + strbuf_init(&buf, size); for (i = 0; i < used; i++) { struct treeent *ent = entries[i]; - - if (offset + ent->len + 100 < size) { - size = alloc_nr(offset + ent->len + 100); - buffer = xrealloc(buffer, size); - } - offset += sprintf(buffer + offset, "%o ", ent->mode); - offset += sprintf(buffer + offset, "%s", ent->name); - buffer[offset++] = 0; - hashcpy((unsigned char*)buffer + offset, ent->sha1); - offset += 20; + strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); + strbuf_add(&buf, ent->sha1, 20); } - write_sha1_file(buffer, offset, tree_type, sha1); + + write_sha1_file(buf.buf, buf.len, tree_type, sha1); } static const char mktree_usage[] = "git-mktree [-z]"; @@ -90,18 +81,15 @@ int main(int ac, char **av) av++; } - strbuf_init(&sb); + strbuf_init(&sb, 0); while (1) { - int len; char *ptr, *ntr; unsigned mode; enum object_type type; char *path; - read_line(&sb, stdin, line_termination); - if (sb.eof) + if (strbuf_getline(&sb, stdin, line_termination) == EOF) break; - len = sb.len; ptr = sb.buf; /* Input is non-recursive ls-tree output format * mode SP type SP sha1 TAB name @@ -111,7 +99,7 @@ int main(int ac, char **av) die("input format error: %s", sb.buf); ptr = ntr + 1; /* type */ ntr = strchr(ptr, ' '); - if (!ntr || sb.buf + len <= ntr + 41 || + if (!ntr || sb.buf + sb.len <= ntr + 40 || ntr[41] != '\t' || get_sha1_hex(ntr + 1, sha1)) die("input format error: %s", sb.buf); @@ -132,6 +120,7 @@ int main(int ac, char **av) if (path != ntr) free(path); } + strbuf_release(&sb); write_tree(sha1); puts(sha1_to_hex(sha1)); exit(0); diff --git a/sha1_file.c b/sha1_file.c index 9978a58da6..59325d46be 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2302,68 +2302,25 @@ int has_sha1_file(const unsigned char *sha1) return find_sha1_file(sha1, &st) ? 1 : 0; } -/* - * reads from fd as long as possible into a supplied buffer of size bytes. - * If necessary the buffer's size is increased using realloc() - * - * returns 0 if anything went fine and -1 otherwise - * - * The buffer is always NUL-terminated, not including it in returned size. - * - * NOTE: both buf and size may change, but even when -1 is returned - * you still have to free() it yourself. - */ -int read_fd(int fd, char **return_buf, unsigned long *return_size) -{ - char *buf = *return_buf; - unsigned long size = *return_size; - ssize_t iret; - unsigned long off = 0; - - if (!buf || size <= 1) { - size = 1024; - buf = xrealloc(buf, size); - } - - do { - iret = xread(fd, buf + off, (size - 1) - off); - if (iret > 0) { - off += iret; - if (off == size - 1) { - size = alloc_nr(size); - buf = xrealloc(buf, size); - } - } - } while (iret > 0); - - buf[off] = '\0'; - - *return_buf = buf; - *return_size = off; - - if (iret < 0) - return -1; - return 0; -} - int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) { - unsigned long size = 4096; - char *buf = xmalloc(size); + struct strbuf buf; int ret; - if (read_fd(fd, &buf, &size)) { - free(buf); + strbuf_init(&buf, 0); + if (strbuf_read(&buf, fd, 4096) < 0) { + strbuf_release(&buf); return -1; } if (!type) type = blob_type; if (write_object) - ret = write_sha1_file(buf, size, type, sha1); + ret = write_sha1_file(buf.buf, buf.len, type, sha1); else - ret = hash_sha1_file(buf, size, type, sha1); - free(buf); + ret = hash_sha1_file(buf.buf, buf.len, type, sha1); + strbuf_release(&buf); + return ret; } @@ -2385,12 +2342,12 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, * Convert blobs to git internal format */ if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) { - unsigned long nsize = size; - char *nbuf = convert_to_git(path, buf, &nsize); - if (nbuf) { + struct strbuf nbuf; + strbuf_init(&nbuf, 0); + if (convert_to_git(path, buf, size, &nbuf)) { munmap(buf, size); - size = nsize; - buf = nbuf; + size = nbuf.len; + buf = nbuf.buf; re_allocated = 1; } } @@ -1,41 +1,167 @@ #include "cache.h" -#include "strbuf.h" -void strbuf_init(struct strbuf *sb) { - sb->buf = NULL; - sb->eof = sb->alloc = sb->len = 0; +void strbuf_init(struct strbuf *sb, size_t hint) +{ + memset(sb, 0, sizeof(*sb)); + if (hint) + strbuf_grow(sb, hint); } -static void strbuf_begin(struct strbuf *sb) { +void strbuf_release(struct strbuf *sb) +{ free(sb->buf); - strbuf_init(sb); + memset(sb, 0, sizeof(*sb)); } -static void inline strbuf_add(struct strbuf *sb, int ch) { - if (sb->alloc <= sb->len) { - sb->alloc = sb->alloc * 3 / 2 + 16; - sb->buf = xrealloc(sb->buf, sb->alloc); +void strbuf_reset(struct strbuf *sb) +{ + if (sb->len) + strbuf_setlen(sb, 0); +} + +char *strbuf_detach(struct strbuf *sb) +{ + char *res = sb->buf; + 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"); + ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); +} + +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_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) +{ + strbuf_grow(sb, len); + if (pos > sb->len) + die("`pos' is too far after the end of the buffer"); + memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos); + memcpy(sb->buf + pos, data, len); + strbuf_setlen(sb, sb->len + 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_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_addf(struct strbuf *sb, const char *fmt, ...) +{ + int len; + va_list ap; + + va_start(ap, fmt); + len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); + va_end(ap); + if (len < 0) { + len = 0; } - sb->buf[sb->len++] = ch; + 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); } -static void strbuf_end(struct strbuf *sb) { - strbuf_add(sb, 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; } -void read_line(struct strbuf *sb, FILE *fp, int term) { - int ch; - strbuf_begin(sb); - if (feof(fp)) { - sb->eof = 1; - return; +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 = xread(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_add(sb, ch); + strbuf_grow(sb, 1); + sb->buf[sb->len++] = ch; } if (ch == EOF && sb->len == 0) - sb->eof = 1; - strbuf_end(sb); + return EOF; + + sb->buf[sb->len] = '\0'; + return 0; } @@ -1,13 +1,105 @@ #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 legal to copy the ->buf pointer away. Though if you want to reuse + * the strbuf after that, setting ->buf to NULL isn't legal. + * `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 areay starting at sb->buf, and of length + * // sb_avail(sb) is all yours, and you are sure that sb_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 sb_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> + struct strbuf { - int alloc; - int len; - int eof; + size_t alloc; + size_t len; char *buf; }; -extern void strbuf_init(struct strbuf *); -extern void read_line(struct strbuf *, FILE *, int); +#define STRBUF_INIT { 0, 0, NULL } + +/*----- strbuf life cycle -----*/ +extern void strbuf_init(struct strbuf *, size_t); +extern void strbuf_release(struct strbuf *); +extern void strbuf_reset(struct strbuf *); +extern char *strbuf_detach(struct strbuf *); +extern void strbuf_attach(struct strbuf *, void *, size_t, size_t); + +/*----- strbuf size related -----*/ +static inline size_t strbuf_avail(struct strbuf *sb) { + return sb->alloc ? sb->alloc - sb->len - 1 : 0; +} +static inline void strbuf_setlen(struct strbuf *sb, size_t len) { + assert (len < sb->alloc); + sb->len = len; + sb->buf[len] = '\0'; +} + +extern void strbuf_grow(struct strbuf *, size_t); + +/*----- content related -----*/ +extern void strbuf_rtrim(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'; +} + +/* inserts after pos, or appends if pos >= sb->len */ +extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); + +/* 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, struct strbuf *sb2) { + strbuf_add(sb, sb2->buf, sb2->len); +} + +__attribute__((format(printf,2,3))) +extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); + +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 int strbuf_getline(struct strbuf *, FILE *, int); #endif /* STRBUF_H */ diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 42e28ab758..dca2067b2d 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -36,7 +36,8 @@ test_expect_success \ echo simple textfile >a/a && mkdir a/bin && cp /bin/sh a/bin && - printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile && + printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 && + printf "A not substituted O" >a/substfile2 && ln -s a a/l1 && (p=long_path_to_a_file && cd a && for depth in 1 2 3 4 5; do mkdir $p && cd $p; done && @@ -108,20 +109,22 @@ test_expect_success \ 'diff -r a c/prefix/a' test_expect_success \ - 'create an archive with a substfile' \ - 'echo substfile export-subst >a/.gitattributes && + 'create an archive with a substfiles' \ + 'echo "substfile?" export-subst >a/.gitattributes && git archive HEAD >f.tar && rm a/.gitattributes' test_expect_success \ - 'extract substfile' \ + 'extract substfiles' \ '(mkdir f && cd f && $TAR xf -) <f.tar' test_expect_success \ 'validate substfile contents' \ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ - >f/a/substfile.expected && - diff f/a/substfile.expected f/a/substfile' + >f/a/substfile1.expected && + diff f/a/substfile1.expected f/a/substfile1 && + diff a/substfile2 f/a/substfile2 +' test_expect_success \ 'git archive --format=zip' \ |