Lift 16kB limit of log message output

Traditionally we had 16kB limit when formatting log messages for
output, because it was easier to arrange for the caller to have
a reasonably big buffer and pass it down without ever worrying
about reallocating.

This changes the calling convention of pretty_print_commit() to
lift this limit.  Instead of the buffer and remaining length, it
now takes a pointer to the pointer that points at the allocated
buffer, and another pointer to the location that stores the
allocated length, and reallocates the buffer as necessary.

To support the user format, the error return of interpolate()
needed to be changed.  It used to return a bool telling "Ok the
result fits", or "Sorry, I had to truncate it".  Now it returns
0 on success, and returns the size of the buffer it wants in
order to fit the whole result.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2007-06-11 00:34:54 -07:00
parent 90ac368afd
commit 80583c0ef6
9 changed files with 123 additions and 73 deletions

View File

@ -242,7 +242,6 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
char c; char c;
int color; int color;
struct commit *commit; struct commit *commit;
char subject[256];
switch (item->kind) { switch (item->kind) {
case REF_LOCAL_BRANCH: case REF_LOCAL_BRANCH:
@ -263,17 +262,23 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
} }
if (verbose) { if (verbose) {
char *subject = NULL;
unsigned long subject_len = 0;
const char *sub = " **** invalid ref ****";
commit = lookup_commit(item->sha1); commit = lookup_commit(item->sha1);
if (commit && !parse_commit(commit)) if (commit && !parse_commit(commit)) {
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
subject, sizeof(subject), 0, &subject, &subject_len, 0,
NULL, NULL, 0); NULL, NULL, 0);
else sub = subject;
strcpy(subject, " **** invalid ref ****"); }
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
maxwidth, item->name, maxwidth, item->name,
branch_get_color(COLOR_BRANCH_RESET), branch_get_color(COLOR_BRANCH_RESET),
find_unique_abbrev(item->sha1, abbrev), subject); find_unique_abbrev(item->sha1, abbrev), sub);
if (subject)
free(subject);
} else { } else {
printf("%c %s%s%s\n", c, branch_get_color(color), item->name, printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
branch_get_color(COLOR_BRANCH_RESET)); branch_get_color(COLOR_BRANCH_RESET));

View File

@ -742,11 +742,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
sign = '-'; sign = '-';
if (verbose) { if (verbose) {
static char buf[16384]; char *buf = NULL;
unsigned long buflen = 0;
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
buf, sizeof(buf), 0, NULL, NULL, 0); &buf, &buflen, 0, NULL, NULL, 0);
printf("%c %s %s\n", sign, printf("%c %s %s\n", sign,
sha1_to_hex(commit->object.sha1), buf); sha1_to_hex(commit->object.sha1), buf);
free(buf);
} }
else { else {
printf("%c %s\n", sign, printf("%c %s\n", sign,

View File

@ -92,11 +92,13 @@ static void show_commit(struct commit *commit)
putchar('\n'); putchar('\n');
if (revs.verbose_header) { if (revs.verbose_header) {
static char pretty_header[16384]; char *buf = NULL;
unsigned long buflen = 0;
pretty_print_commit(revs.commit_format, commit, ~0, pretty_print_commit(revs.commit_format, commit, ~0,
pretty_header, sizeof(pretty_header), &buf, &buflen,
revs.abbrev, NULL, NULL, revs.date_mode); revs.abbrev, NULL, NULL, revs.date_mode);
printf("%s%c", pretty_header, hdr_termination); printf("%s%c", buf, hdr_termination);
free(buf);
} }
fflush(stdout); fflush(stdout);
if (commit->parents) { if (commit->parents) {

View File

@ -259,17 +259,19 @@ static void join_revs(struct commit_list **list_p,
static void show_one_commit(struct commit *commit, int no_name) static void show_one_commit(struct commit *commit, int no_name)
{ {
char pretty[256], *cp; char *pretty = NULL;
const char *pretty_str = "(unavailable)";
unsigned long pretty_len = 0;
struct commit_name *name = commit->util; struct commit_name *name = commit->util;
if (commit->object.parsed)
if (commit->object.parsed) {
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
pretty, sizeof(pretty), 0, NULL, NULL, 0); &pretty, &pretty_len,
else 0, NULL, NULL, 0);
strcpy(pretty, "(unavailable)"); pretty_str = pretty;
if (!prefixcmp(pretty, "[PATCH] ")) }
cp = pretty + 8; if (!prefixcmp(pretty_str, "[PATCH] "))
else pretty_str += 8;
cp = pretty;
if (!no_name) { if (!no_name) {
if (name && name->head_name) { if (name && name->head_name) {
@ -286,7 +288,8 @@ static void show_one_commit(struct commit *commit, int no_name)
printf("[%s] ", printf("[%s] ",
find_unique_abbrev(commit->object.sha1, 7)); find_unique_abbrev(commit->object.sha1, 7));
} }
puts(cp); puts(pretty_str);
free(pretty);
} }
static char *ref_name[MAX_REVS + 1]; static char *ref_name[MAX_REVS + 1];

View File

@ -776,7 +776,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
} }
static long format_commit_message(const struct commit *commit, static long format_commit_message(const struct commit *commit,
const char *msg, char *buf, unsigned long space) const char *msg, char **buf_p, unsigned long *space_p)
{ {
struct interp table[] = { struct interp table[] = {
{ "%H" }, /* commit hash */ { "%H" }, /* commit hash */
@ -905,16 +905,27 @@ static long format_commit_message(const struct commit *commit,
if (!table[i].value) if (!table[i].value)
interp_set_entry(table, i, "<unknown>"); interp_set_entry(table, i, "<unknown>");
interpolate(buf, space, user_format, table, ARRAY_SIZE(table)); do {
char *buf = *buf_p;
unsigned long space = *space_p;
space = interpolate(buf, space, user_format,
table, ARRAY_SIZE(table));
if (!space)
break;
buf = xrealloc(buf, space);
*buf_p = buf;
*space_p = space;
} while (1);
interp_clear_table(table, ARRAY_SIZE(table)); interp_clear_table(table, ARRAY_SIZE(table));
return strlen(buf); return strlen(*buf_p);
} }
unsigned long pretty_print_commit(enum cmit_fmt fmt, unsigned long pretty_print_commit(enum cmit_fmt fmt,
const struct commit *commit, const struct commit *commit,
unsigned long len, unsigned long len,
char *buf, unsigned long space, char **buf_p, unsigned long *space_p,
int abbrev, const char *subject, int abbrev, const char *subject,
const char *after_subject, const char *after_subject,
enum date_mode dmode) enum date_mode dmode)
@ -927,9 +938,11 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
int plain_non_ascii = 0; int plain_non_ascii = 0;
char *reencoded; char *reencoded;
const char *encoding; const char *encoding;
char *buf;
unsigned long space, slop;
if (fmt == CMIT_FMT_USERFORMAT) if (fmt == CMIT_FMT_USERFORMAT)
return format_commit_message(commit, msg, buf, space); return format_commit_message(commit, msg, buf_p, space_p);
encoding = (git_log_output_encoding encoding = (git_log_output_encoding
? git_log_output_encoding ? git_log_output_encoding
@ -969,6 +982,26 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
} }
} }
space = *space_p;
buf = *buf_p;
/*
* We do not want to repeatedly realloc below, so
* preallocate with enough slop to hold MIME headers,
* "Subject: " prefix, etc.
*/
slop = 1000;
if (subject)
slop += strlen(subject);
if (after_subject)
slop += strlen(after_subject);
if (space < strlen(msg) + slop) {
space = strlen(msg) + slop;
buf = xrealloc(buf, space);
*space_p = space;
*buf_p = buf;
}
for (;;) { for (;;) {
const char *line = msg; const char *line = msg;
int linelen = get_one_line(msg, len); int linelen = get_one_line(msg, len);
@ -976,14 +1009,12 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
if (!linelen) if (!linelen)
break; break;
/* /* 20 would cover indent and leave us some slop */
* We want some slop for indentation and a possible
* final "...". Thus the "+ 20".
*/
if (offset + linelen + 20 > space) { if (offset + linelen + 20 > space) {
memcpy(buf + offset, " ...\n", 8); space = offset + linelen + 20;
offset += 8; buf = xrealloc(buf, space);
break; *buf_p = buf;
*space_p = space;
} }
msg += linelen; msg += linelen;

View File

@ -61,7 +61,7 @@ enum cmit_fmt {
}; };
extern enum cmit_fmt get_commit_format(const char *arg); extern enum cmit_fmt get_commit_format(const char *arg);
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode); 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);
/** Removes the first commit from a list sorted by date, and adds all /** Removes the first commit from a list sorted by date, and adds all
* of its parents. * of its parents.

View File

@ -44,33 +44,33 @@ void interp_clear_table(struct interp *table, int ninterps)
* { "%%", "%"}, * { "%%", "%"},
* } * }
* *
* Returns 1 on a successful substitution pass that fits in result, * Returns 0 on a successful substitution pass that fits in result,
* Returns 0 on a failed or overflowing substitution pass. * Returns a number of bytes needed to hold the full substituted
* string otherwise.
*/ */
int interpolate(char *result, int reslen, unsigned long interpolate(char *result, unsigned long reslen,
const char *orig, const char *orig,
const struct interp *interps, int ninterps) const struct interp *interps, int ninterps)
{ {
const char *src = orig; const char *src = orig;
char *dest = result; char *dest = result;
int newlen = 0; unsigned long newlen = 0;
const char *name, *value; const char *name, *value;
int namelen, valuelen; unsigned long namelen, valuelen;
int i; int i;
char c; char c;
memset(result, 0, reslen); memset(result, 0, reslen);
while ((c = *src) && newlen < reslen - 1) { while ((c = *src)) {
if (c == '%') { if (c == '%') {
/* Try to match an interpolation string. */ /* Try to match an interpolation string. */
for (i = 0; i < ninterps; i++) { for (i = 0; i < ninterps; i++) {
name = interps[i].name; name = interps[i].name;
namelen = strlen(name); namelen = strlen(name);
if (strncmp(src, name, namelen) == 0) { if (strncmp(src, name, namelen) == 0)
break; break;
}
} }
/* Check for valid interpolation. */ /* Check for valid interpolation. */
@ -78,29 +78,25 @@ int interpolate(char *result, int reslen,
value = interps[i].value; value = interps[i].value;
valuelen = strlen(value); valuelen = strlen(value);
if (newlen + valuelen < reslen - 1) { if (newlen + valuelen + 1 < reslen) {
/* Substitute. */ /* Substitute. */
strncpy(dest, value, valuelen); strncpy(dest, value, valuelen);
newlen += valuelen;
dest += valuelen; dest += valuelen;
src += namelen;
} else {
/* Something's not fitting. */
return 0;
} }
newlen += valuelen;
} else { src += namelen;
/* Skip bogus interpolation. */ continue;
*dest++ = *src++;
newlen++;
} }
} else {
/* Straight copy one non-interpolation character. */
*dest++ = *src++;
newlen++;
} }
/* Straight copy one non-interpolation character. */
if (newlen + 1 < reslen)
*dest++ = *src;
src++;
newlen++;
} }
return newlen < reslen - 1; if (newlen + 1 < reslen)
return 0;
else
return newlen + 2;
} }

View File

@ -19,8 +19,8 @@ struct interp {
extern void interp_set_entry(struct interp *table, int slot, const char *value); extern void interp_set_entry(struct interp *table, int slot, const char *value);
extern void interp_clear_table(struct interp *table, int ninterps); extern void interp_clear_table(struct interp *table, int ninterps);
extern int interpolate(char *result, int reslen, extern unsigned long interpolate(char *result, unsigned long reslen,
const char *orig, const char *orig,
const struct interp *interps, int ninterps); const struct interp *interps, int ninterps);
#endif /* INTERPOLATE_H */ #endif /* INTERPOLATE_H */

View File

@ -79,16 +79,25 @@ static int detect_any_signoff(char *letter, int size)
return seen_head && seen_name; return seen_head && seen_name;
} }
static int append_signoff(char *buf, int buf_sz, int at, const char *signoff) static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p,
unsigned long at, const char *signoff)
{ {
static const char signed_off_by[] = "Signed-off-by: "; static const char signed_off_by[] = "Signed-off-by: ";
int signoff_len = strlen(signoff); size_t signoff_len = strlen(signoff);
int has_signoff = 0; int has_signoff = 0;
char *cp = buf; char *cp;
char *buf;
unsigned long buf_sz;
/* Do we have enough space to add it? */ buf = *buf_p;
if (buf_sz - at <= strlen(signed_off_by) + signoff_len + 3) buf_sz = *buf_sz_p;
return at; 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;
/* First see if we already have the sign-off by the signer */ /* First see if we already have the sign-off by the signer */
while ((cp = strstr(cp, signed_off_by))) { while ((cp = strstr(cp, signed_off_by))) {
@ -133,7 +142,8 @@ static unsigned int digits_in_number(unsigned int number)
void show_log(struct rev_info *opt, const char *sep) void show_log(struct rev_info *opt, const char *sep)
{ {
static char this_header[16384]; char *msgbuf = NULL;
unsigned long msgbuf_len = 0;
struct log_info *log = opt->loginfo; struct log_info *log = opt->loginfo;
struct commit *commit = log->commit, *parent = log->parent; struct commit *commit = log->commit, *parent = log->parent;
int abbrev = opt->diffopt.abbrev; int abbrev = opt->diffopt.abbrev;
@ -278,14 +288,15 @@ void show_log(struct rev_info *opt, const char *sep)
/* /*
* And then the pretty-printed message itself * And then the pretty-printed message itself
*/ */
len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, len = pretty_print_commit(opt->commit_format, commit, ~0u,
sizeof(this_header), abbrev, subject, &msgbuf, &msgbuf_len, abbrev, subject,
extra_headers, opt->date_mode); extra_headers, opt->date_mode);
if (opt->add_signoff) if (opt->add_signoff)
len = append_signoff(this_header, sizeof(this_header), len, len = append_signoff(&msgbuf, &msgbuf_len, len,
opt->add_signoff); opt->add_signoff);
printf("%s%s%s", this_header, extra, sep); printf("%s%s%s", msgbuf, extra, sep);
free(msgbuf);
} }
int log_tree_diff_flush(struct rev_info *opt) int log_tree_diff_flush(struct rev_info *opt)