Skip to content

Commit

Permalink
approxidate_careful() reports errorneous date string
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Junio C Hamano committed Jan 26, 2010
1 parent 35eabd1 commit 93cfa7c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 10 deletions.
3 changes: 2 additions & 1 deletion cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,8 @@ const char *show_date_relative(unsigned long time, int tz,
size_t timebuf_size);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
#define approxidate(s) approxidate_careful((s), NULL)
unsigned long approxidate_careful(const char *, int *);
unsigned long approxidate_relative(const char *date, const struct timeval *now);
enum date_mode parse_date_format(const char *format);

Expand Down
43 changes: 35 additions & 8 deletions date.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,11 @@ static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec)
return n;
}

static void date_now(struct tm *tm, struct tm *now, int *num)
{
update_tm(tm, now, 0);
}

static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
update_tm(tm, now, 24*60*60);
Expand Down Expand Up @@ -770,6 +775,7 @@ static const struct special {
{ "PM", date_pm },
{ "AM", date_am },
{ "never", date_never },
{ "now", date_now },
{ NULL }
};

Expand All @@ -790,7 +796,7 @@ static const struct typelen {
{ NULL }
};

static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num)
static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
{
const struct typelen *tl;
const struct special *s;
Expand All @@ -804,6 +810,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
int match = match_string(date, month_names[i]);
if (match >= 3) {
tm->tm_mon = i;
*touched = 1;
return end;
}
}
Expand All @@ -812,6 +819,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
int len = strlen(s->name);
if (match_string(date, s->name) == len) {
s->fn(tm, now, num);
*touched = 1;
return end;
}
}
Expand All @@ -821,11 +829,14 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
int len = strlen(number_name[i]);
if (match_string(date, number_name[i]) == len) {
*num = i;
*touched = 1;
return end;
}
}
if (match_string(date, "last") == 4)
if (match_string(date, "last") == 4) {
*num = 1;
*touched = 1;
}
return end;
}

Expand All @@ -835,6 +846,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
if (match_string(date, tl->type) >= len-1) {
update_tm(tm, now, tl->length * *num);
*num = 0;
*touched = 1;
return end;
}
tl++;
Expand All @@ -852,6 +864,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
diff += 7*n;

update_tm(tm, now, diff * 24 * 60 * 60);
*touched = 1;
return end;
}
}
Expand All @@ -866,13 +879,15 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
tm->tm_year--;
}
tm->tm_mon = n;
*touched = 1;
return end;
}

if (match_string(date, "years") >= 4) {
update_tm(tm, now, 0); /* fill in date fields if needed */
tm->tm_year -= *num;
*num = 0;
*touched = 1;
return end;
}

Expand Down Expand Up @@ -929,9 +944,12 @@ static void pending_number(struct tm *tm, int *num)
}
}

static unsigned long approxidate_str(const char *date, const struct timeval *tv)
static unsigned long approxidate_str(const char *date,
const struct timeval *tv,
int *error_ret)
{
int number = 0;
int touched = 0;
struct tm tm, now;
time_t time_sec;

Expand All @@ -951,33 +969,42 @@ static unsigned long approxidate_str(const char *date, const struct timeval *tv)
if (isdigit(c)) {
pending_number(&tm, &number);
date = approxidate_digit(date-1, &tm, &number);
touched = 1;
continue;
}
if (isalpha(c))
date = approxidate_alpha(date-1, &tm, &now, &number);
date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
}
pending_number(&tm, &number);
if (!touched)
*error_ret = 1;
return update_tm(&tm, &now, 0);
}

unsigned long approxidate_relative(const char *date, const struct timeval *tv)
{
char buffer[50];
int errors = 0;

if (parse_date(date, buffer, sizeof(buffer)) > 0)
return strtoul(buffer, NULL, 0);

return approxidate_str(date, tv);
return approxidate_str(date, tv, &errors);
}

unsigned long approxidate(const char *date)
unsigned long approxidate_careful(const char *date, int *error_ret)
{
struct timeval tv;
char buffer[50];
int dummy = 0;
if (!error_ret)
error_ret = &dummy;

if (parse_date(date, buffer, sizeof(buffer)) > 0)
if (parse_date(date, buffer, sizeof(buffer)) > 0) {
*error_ret = 0;
return strtoul(buffer, NULL, 0);
}

gettimeofday(&tv, NULL);
return approxidate_str(date, &tv);
return approxidate_str(date, &tv, error_ret);
}
5 changes: 4 additions & 1 deletion sha1_name.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,11 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
} else if (0 <= nth)
at_time = 0;
else {
int errors = 0;
char *tmp = xstrndup(str + at + 2, reflog_len);
at_time = approxidate(tmp);
at_time = approxidate_careful(tmp, &errors);
if (errors)
die("Bogus timestamp '%s'", tmp);
free(tmp);
}
if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
Expand Down
45 changes: 45 additions & 0 deletions t/t0101-at-syntax.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/sh

test_description='various @{whatever} syntax tests'
. ./test-lib.sh

test_expect_success 'setup' '
test_commit one &&
test_commit two
'

check_at() {
echo "$2" >expect &&
git log -1 --format=%s "$1" >actual &&
test_cmp expect actual
}

test_expect_success '@{0} shows current' '
check_at @{0} two
'

test_expect_success '@{1} shows old' '
check_at @{1} one
'

test_expect_success '@{now} shows current' '
check_at @{now} two
'

test_expect_success '@{30.years.ago} shows old' '
check_at @{30.years.ago} one
'

test_expect_success 'silly approxidates work' '
check_at @{3.hot.dogs.and.30.years.ago} one
'

test_expect_success 'notice misspelled upstream' '
test_must_fail git log -1 --format=%s @{usptream}
'

test_expect_success 'complain about total nonsense' '
test_must_fail git log -1 --format=%s @{utter.bogosity}
'

test_done

0 comments on commit 93cfa7c

Please sign in to comment.