date: use strbufs in date-formatting functions

Many of the date functions write into fixed-size buffers. This is a minor pain, as we have to take special precautions, and frequently end up copying the result into a strbuf or heap-allocated buffer anyway (for which we sometimes use strcpy!). Let's instead teach parse_date, datestamp, etc to write to a strbuf. The obvious downside is that we might need to perform a heap allocation where we otherwise would not need to. However, it turns out that the only two new allocations required are: 1. In test-date.c, where we don't care about efficiency. 2. In determine_author_info, which is not performance critical (and where the use of a strbuf will help later refactoring). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Aug 27, 2014 at 03:57 UTC c33ddc2e33d51da9391a81206a1d9e4a92d97d10
6 files changed +45 -48
builtin/commit.c
+10 -10
@@ -520,19 +520,16 @@ static int sane_ident_split(struct ident_split *person)
520 return 1;
521 }
522
523 -static int parse_force_date(const char *in, char *out, int len)
523 +static int parse_force_date(const char *in, struct strbuf *out)
524 {
525 - if (len < 1)
526 - return -1;
527 - *out++ = '@';
528 - len--;
525 + strbuf_addch(out, '@');
526
530 - if (parse_date(in, out, len) < 0) {
527 + if (parse_date(in, out) < 0) {
528 int errors = 0;
529 unsigned long t = approxidate_careful(in, &errors);
530 if (errors)
531 return -1;
535 - snprintf(out, len, "%lu", t);
532 + strbuf_addf(out, "%lu", t);
533 }
534
535 return 0;
@@ -542,7 +539,7 @@ static void determine_author_info(struct strbuf *author_ident)
539 {
540 char *name, *email, *date;
541 struct ident_split author;
545 - char date_buf[64];
542 + struct strbuf date_buf = STRBUF_INIT;
543
544 name = getenv("GIT_AUTHOR_NAME");
545 email = getenv("GIT_AUTHOR_EMAIL");
@@ -588,9 +585,10 @@ static void determine_author_info(struct strbuf *author_ident)
585 }
586
587 if (force_date) {
591 - if (parse_force_date(force_date, date_buf, sizeof(date_buf)))
588 + strbuf_reset(&date_buf);
589 + if (parse_force_date(force_date, &date_buf))
590 die(_("invalid date format: %s"), force_date);
593 - date = date_buf;
591 + date = date_buf.buf;
592 }
593
594 strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
@@ -600,6 +598,8 @@ static void determine_author_info(struct strbuf *author_ident)
598 export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
599 export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
600 }
601 +
602 + strbuf_release(&date_buf);
603 }
604
605 static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
cache.h
+2 -2
@@ -1044,10 +1044,10 @@ enum date_mode {
1044 const char *show_date(unsigned long time, int timezone, enum date_mode mode);
1045 void show_date_relative(unsigned long time, int tz, const struct timeval *now,
1046 struct strbuf *timebuf);
1047 -int parse_date(const char *date, char *buf, int bufsize);
1047 +int parse_date(const char *date, struct strbuf *out);
1048 int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
1049 int parse_expiry_date(const char *date, unsigned long *timestamp);
1050 -void datestamp(char *buf, int bufsize);
1050 +void datestamp(struct strbuf *out);
1051 #define approxidate(s) approxidate_careful((s), NULL)
1052 unsigned long approxidate_careful(const char *, int *);
1053 unsigned long approxidate_relative(const char *date, const struct timeval *now);
date.c
+7 -6
@@ -605,7 +605,7 @@ static int match_tz(const char *date, int *offp)
605 return end - date;
606 }
607
608 -static int date_string(unsigned long date, int offset, char *buf, int len)
608 +static void date_string(unsigned long date, int offset, struct strbuf *buf)
609 {
610 int sign = '+';
611
@@ -613,7 +613,7 @@ static int date_string(unsigned long date, int offset, char *buf, int len)
613 offset = -offset;
614 sign = '-';
615 }
616 - return snprintf(buf, len, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
616 + strbuf_addf(buf, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
617 }
618
619 /*
@@ -735,13 +735,14 @@ int parse_expiry_date(const char *date, unsigned long *timestamp)
735 return errors;
736 }
737
738 -int parse_date(const char *date, char *result, int maxlen)
738 +int parse_date(const char *date, struct strbuf *result)
739 {
740 unsigned long timestamp;
741 int offset;
742 if (parse_date_basic(date, &timestamp, &offset))
743 return -1;
744 - return date_string(timestamp, offset, result, maxlen);
744 + date_string(timestamp, offset, result);
745 + return 0;
746 }
747
748 enum date_mode parse_date_format(const char *format)
@@ -766,7 +767,7 @@ enum date_mode parse_date_format(const char *format)
767 die("unknown date format %s", format);
768 }
769
769 -void datestamp(char *buf, int bufsize)
770 +void datestamp(struct strbuf *out)
771 {
772 time_t now;
773 int offset;
@@ -776,7 +777,7 @@ void datestamp(char *buf, int bufsize)
777 offset = tm_to_time_t(localtime(&now)) - now;
778 offset /= 60;
779
779 - date_string(now, offset, buf, bufsize);
780 + date_string(now, offset, out);
781 }
782
783 /*
fast-import.c
+9 -11
@@ -1971,7 +1971,7 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
1971 return 1;
1972 }
1973
1974 -static int validate_raw_date(const char *src, char *result, int maxlen)
1974 +static int validate_raw_date(const char *src, struct strbuf *result)
1975 {
1976 const char *orig_src = src;
1977 char *endp;
@@ -1989,11 +1989,10 @@ static int validate_raw_date(const char *src, char *result, int maxlen)
1989 return -1;
1990
1991 num = strtoul(src + 1, &endp, 10);
1992 - if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
1993 - 1400 < num)
1992 + if (errno || endp == src + 1 || *endp || 1400 < num)
1993 return -1;
1994
1996 - strcpy(result, orig_src);
1995 + strbuf_addstr(result, orig_src);
1996 return 0;
1997 }
1998
@@ -2001,7 +2000,7 @@ static char *parse_ident(const char *buf)
2000 {
2001 const char *ltgt;
2002 size_t name_len;
2004 - char *ident;
2003 + struct strbuf ident = STRBUF_INIT;
2004
2005 /* ensure there is a space delimiter even if there is no name */
2006 if (*buf == '<')
@@ -2020,26 +2019,25 @@ static char *parse_ident(const char *buf)
2019 die("Missing space after > in ident string: %s", buf);
2020 ltgt++;
2021 name_len = ltgt - buf;
2023 - ident = xmalloc(name_len + 24);
2024 - strncpy(ident, buf, name_len);
2022 + strbuf_add(&ident, buf, name_len);
2023
2024 switch (whenspec) {
2025 case WHENSPEC_RAW:
2028 - if (validate_raw_date(ltgt, ident + name_len, 24) < 0)
2026 + if (validate_raw_date(ltgt, &ident) < 0)
2027 die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
2028 break;
2029 case WHENSPEC_RFC2822:
2032 - if (parse_date(ltgt, ident + name_len, 24) < 0)
2030 + if (parse_date(ltgt, &ident) < 0)
2031 die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf);
2032 break;
2033 case WHENSPEC_NOW:
2034 if (strcmp("now", ltgt))
2035 die("Date in ident must be 'now': %s", buf);
2038 - datestamp(ident + name_len, 24);
2036 + datestamp(&ident);
2037 break;
2038 }
2039
2042 - return ident;
2040 + return strbuf_detach(&ident, NULL);
2041 }
2042
2043 static void parse_and_store_blob(
ident.c
+11 -15
@@ -9,7 +9,7 @@
9
10 static struct strbuf git_default_name = STRBUF_INIT;
11 static struct strbuf git_default_email = STRBUF_INIT;
12 -static char git_default_date[50];
12 +static struct strbuf git_default_date = STRBUF_INIT;
13
14 #define IDENT_NAME_GIVEN 01
15 #define IDENT_MAIL_GIVEN 02
@@ -129,9 +129,9 @@ const char *ident_default_email(void)
129
130 static const char *ident_default_date(void)
131 {
132 - if (!git_default_date[0])
133 - datestamp(git_default_date, sizeof(git_default_date));
134 - return git_default_date;
132 + if (!git_default_date.len)
133 + datestamp(&git_default_date);
134 + return git_default_date.buf;
135 }
136
137 static int crud(unsigned char c)
@@ -292,7 +292,6 @@ const char *fmt_ident(const char *name, const char *email,
292 const char *date_str, int flag)
293 {
294 static struct strbuf ident = STRBUF_INIT;
295 - char date[50];
295 int strict = (flag & IDENT_STRICT);
296 int want_date = !(flag & IDENT_NO_DATE);
297 int want_name = !(flag & IDENT_NO_NAME);
@@ -320,15 +319,6 @@ const char *fmt_ident(const char *name, const char *email,
319 die("unable to auto-detect email address (got '%s')", email);
320 }
321
323 - if (want_date) {
324 - if (date_str && date_str[0]) {
325 - if (parse_date(date_str, date, sizeof(date)) < 0)
326 - die("invalid date format: %s", date_str);
327 - }
328 - else
329 - strcpy(date, ident_default_date());
330 - }
331 -
322 strbuf_reset(&ident);
323 if (want_name) {
324 strbuf_addstr_without_crud(&ident, name);
@@ -339,8 +329,14 @@ const char *fmt_ident(const char *name, const char *email,
329 strbuf_addch(&ident, '>');
330 if (want_date) {
331 strbuf_addch(&ident, ' ');
342 - strbuf_addstr_without_crud(&ident, date);
332 + if (date_str && date_str[0]) {
333 + if (parse_date(date_str, &ident) < 0)
334 + die("invalid date format: %s", date_str);
335 + }
336 + else
337 + strbuf_addstr(&ident, ident_default_date());
338 }
339 +
340 return ident.buf;
341 }
342
test-date.c
+6 -4
@@ -19,19 +19,21 @@ static void show_dates(char **argv, struct timeval *now)
19
20 static void parse_dates(char **argv, struct timeval *now)
21 {
22 + struct strbuf result = STRBUF_INIT;
23 +
24 for (; *argv; argv++) {
23 - char result[100];
25 unsigned long t;
26 int tz;
27
27 - result[0] = 0;
28 - parse_date(*argv, result, sizeof(result));
29 - if (sscanf(result, "%lu %d", &t, &tz) == 2)
28 + strbuf_reset(&result);
29 + parse_date(*argv, &result);
30 + if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
31 printf("%s -> %s\n",
32 *argv, show_date(t, tz, DATE_ISO8601));
33 else
34 printf("%s -> bad\n", *argv);
35 }
36 + strbuf_release(&result);
37 }
38
39 static void parse_approxidate(char **argv, struct timeval *now)