@matheusbsbs / matheus-bagliano / commits / 93cfa7c7a8
Fork 0

approxidate_careful() reports errorneous date string

For a long time, the time based reflog syntax (e.g. master@{yesterday}) didn't complain when the "human readable" timestamp was misspelled, as the underlying mechanism tried to be as lenient as possible. The funny thing was that parsing of "@{now}" even relied on the fact that anything not recognized by the machinery returned the current timestamp. Introduce approxidate_careful() that takes an optional pointer to an integer, that gets assigned 1 when the input does not make sense as a timestamp. As I am too lazy to fix all the callers that use approxidate(), most of the callers do not take advantage of the error checking, but convert the code to parse reflog to use it as a demonstration. Tests are mostly from Jeff King. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jan 26, 2010 at 11:58 UTC 93cfa7c7a85efbdb00daade4ad0afc11bd2fdf37
4 files changed +86 -10
cache.h
+2 -1
@@ -740,7 +740,8 @@ const char *show_date_relative(unsigned long time, int tz,
740 size_t timebuf_size);
741 int parse_date(const char *date, char *buf, int bufsize);
742 void datestamp(char *buf, int bufsize);
743 -unsigned long approxidate(const char *);
743 +#define approxidate(s) approxidate_careful((s), NULL)
744 +unsigned long approxidate_careful(const char *, int *);
745 unsigned long approxidate_relative(const char *date, const struct timeval *now);
746 enum date_mode parse_date_format(const char *format);
747
date.c
+35 -8
@@ -696,6 +696,11 @@ static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec)
696 return n;
697 }
698
699 +static void date_now(struct tm *tm, struct tm *now, int *num)
700 +{
701 + update_tm(tm, now, 0);
702 +}
703 +
704 static void date_yesterday(struct tm *tm, struct tm *now, int *num)
705 {
706 update_tm(tm, now, 24*60*60);
@@ -770,6 +775,7 @@ static const struct special {
775 { "PM", date_pm },
776 { "AM", date_am },
777 { "never", date_never },
778 + { "now", date_now },
779 { NULL }
780 };
781
@@ -790,7 +796,7 @@ static const struct typelen {
796 { NULL }
797 };
798
793 -static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num)
799 +static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
800 {
801 const struct typelen *tl;
802 const struct special *s;
@@ -804,6 +810,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
810 int match = match_string(date, month_names[i]);
811 if (match >= 3) {
812 tm->tm_mon = i;
813 + *touched = 1;
814 return end;
815 }
816 }
@@ -812,6 +819,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
819 int len = strlen(s->name);
820 if (match_string(date, s->name) == len) {
821 s->fn(tm, now, num);
822 + *touched = 1;
823 return end;
824 }
825 }
@@ -821,11 +829,14 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
829 int len = strlen(number_name[i]);
830 if (match_string(date, number_name[i]) == len) {
831 *num = i;
832 + *touched = 1;
833 return end;
834 }
835 }
827 - if (match_string(date, "last") == 4)
836 + if (match_string(date, "last") == 4) {
837 *num = 1;
838 + *touched = 1;
839 + }
840 return end;
841 }
842
@@ -835,6 +846,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
846 if (match_string(date, tl->type) >= len-1) {
847 update_tm(tm, now, tl->length * *num);
848 *num = 0;
849 + *touched = 1;
850 return end;
851 }
852 tl++;
@@ -852,6 +864,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
864 diff += 7*n;
865
866 update_tm(tm, now, diff * 24 * 60 * 60);
867 + *touched = 1;
868 return end;
869 }
870 }
@@ -866,6 +879,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
879 tm->tm_year--;
880 }
881 tm->tm_mon = n;
882 + *touched = 1;
883 return end;
884 }
885
@@ -873,6 +887,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
887 update_tm(tm, now, 0); /* fill in date fields if needed */
888 tm->tm_year -= *num;
889 *num = 0;
890 + *touched = 1;
891 return end;
892 }
893
@@ -929,9 +944,12 @@ static void pending_number(struct tm *tm, int *num)
944 }
945 }
946
932 -static unsigned long approxidate_str(const char *date, const struct timeval *tv)
947 +static unsigned long approxidate_str(const char *date,
948 + const struct timeval *tv,
949 + int *error_ret)
950 {
951 int number = 0;
952 + int touched = 0;
953 struct tm tm, now;
954 time_t time_sec;
955
@@ -951,33 +969,42 @@ static unsigned long approxidate_str(const char *date, const struct timeval *tv)
969 if (isdigit(c)) {
970 pending_number(&tm, &number);
971 date = approxidate_digit(date-1, &tm, &number);
972 + touched = 1;
973 continue;
974 }
975 if (isalpha(c))
957 - date = approxidate_alpha(date-1, &tm, &now, &number);
976 + date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
977 }
978 pending_number(&tm, &number);
979 + if (!touched)
980 + *error_ret = 1;
981 return update_tm(&tm, &now, 0);
982 }
983
984 unsigned long approxidate_relative(const char *date, const struct timeval *tv)
985 {
986 char buffer[50];
987 + int errors = 0;
988
989 if (parse_date(date, buffer, sizeof(buffer)) > 0)
990 return strtoul(buffer, NULL, 0);
991
970 - return approxidate_str(date, tv);
992 + return approxidate_str(date, tv, &errors);
993 }
994
973 -unsigned long approxidate(const char *date)
995 +unsigned long approxidate_careful(const char *date, int *error_ret)
996 {
997 struct timeval tv;
998 char buffer[50];
999 + int dummy = 0;
1000 + if (!error_ret)
1001 + error_ret = &dummy;
1002
978 - if (parse_date(date, buffer, sizeof(buffer)) > 0)
1003 + if (parse_date(date, buffer, sizeof(buffer)) > 0) {
1004 + *error_ret = 0;
1005 return strtoul(buffer, NULL, 0);
1006 + }
1007
1008 gettimeofday(&tv, NULL);
982 - return approxidate_str(date, &tv);
1009 + return approxidate_str(date, &tv, error_ret);
1010 }
sha1_name.c
+4 -1
@@ -395,8 +395,11 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
395 } else if (0 <= nth)
396 at_time = 0;
397 else {
398 + int errors = 0;
399 char *tmp = xstrndup(str + at + 2, reflog_len);
399 - at_time = approxidate(tmp);
400 + at_time = approxidate_careful(tmp, &errors);
401 + if (errors)
402 + die("Bogus timestamp '%s'", tmp);
403 free(tmp);
404 }
405 if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
t/t0101-at-syntax.sh new
+45
@@ -0,0 +1,45 @@
1 +#!/bin/sh
2 +
3 +test_description='various @{whatever} syntax tests'
4 +. ./test-lib.sh
5 +
6 +test_expect_success 'setup' '
7 + test_commit one &&
8 + test_commit two
9 +'
10 +
11 +check_at() {
12 + echo "$2" >expect &&
13 + git log -1 --format=%s "$1" >actual &&
14 + test_cmp expect actual
15 +}
16 +
17 +test_expect_success '@{0} shows current' '
18 + check_at @{0} two
19 +'
20 +
21 +test_expect_success '@{1} shows old' '
22 + check_at @{1} one
23 +'
24 +
25 +test_expect_success '@{now} shows current' '
26 + check_at @{now} two
27 +'
28 +
29 +test_expect_success '@{30.years.ago} shows old' '
30 + check_at @{30.years.ago} one
31 +'
32 +
33 +test_expect_success 'silly approxidates work' '
34 + check_at @{3.hot.dogs.and.30.years.ago} one
35 +'
36 +
37 +test_expect_success 'notice misspelled upstream' '
38 + test_must_fail git log -1 --format=%s @{usptream}
39 +'
40 +
41 +test_expect_success 'complain about total nonsense' '
42 + test_must_fail git log -1 --format=%s @{utter.bogosity}
43 +'
44 +
45 +test_done