From d78f05e91bb3a87a73b1d3fad29362447ee8e1f6 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 26 Mar 2019 15:40:00 -0500 Subject: Make touch use xparsedate() (result: -t and -d the same, autodetects format), and fix tests to pass on host too. --- lib/lib.h | 2 +- lib/xwrap.c | 100 +++++++++++++++++++++-------------------------------- tests/date.test | 16 ++++----- tests/touch.test | 23 ++++++------ toys/posix/date.c | 2 +- toys/posix/touch.c | 62 ++++----------------------------- 6 files changed, 67 insertions(+), 138 deletions(-) diff --git a/lib/lib.h b/lib/lib.h index 174c1fd6..8e6514a8 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -182,7 +182,7 @@ char *xtzset(char *new); void xsignal_flags(int signal, void *handler, int flags); void xsignal(int signal, void *handler); time_t xvali_date(struct tm *tm, char *str); -void xparsedate(char *str, time_t *t, unsigned *nano); +void xparsedate(char *str, time_t *t, unsigned *nano, int endian); // lib.c void verror_msg(char *msg, int err, va_list va); diff --git a/lib/xwrap.c b/lib/xwrap.c index ee07fda2..a8214e57 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -958,14 +958,17 @@ time_t xvali_date(struct tm *tm, char *str) } // Parse date string (relative to current *t). Sets time_t and nanoseconds. -void xparsedate(char *str, time_t *t, unsigned *nano) +void xparsedate(char *str, time_t *t, unsigned *nano, int endian) { struct tm tm; time_t now = *t; - int len = 0, i; - // Formats with years must come first. - char *s = str, *p, *formats[] = {"%F %T", "%FT%T", "%F %H:%M", "%F", - "%H:%M:%S", "%H:%M"}; + int len = 0, i = 0; + // Formats with years must come first. Posix can't agree on whether 12 digits + // has year before (touch -t) or year after (date), so support both. + char *s = str, *p, *oldtz = 0, *formats[] = {"%Y-%m-%d %T", "%Y-%m-%dT%T", + "%H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d", "%H:%M", "%m%d%H%M", + endian ? "%m%d%H%M%y" : "%y%m%d%H%M", + endian ? "%m%d%H%M%C%y" : "%C%y%m%d%H%M"}; *nano = 0; @@ -983,72 +986,49 @@ void xparsedate(char *str, time_t *t, unsigned *nano) if (isdigit(*s)) *nano += *s++-'0'; } } - if (s[len]) goto bad_dates; *t = ll; + if (!s[len]) return; + xvali_date(0, str); + } - return; + // Trailing Z means UTC timezone, don't expect libc to know this. + // (Trimming it off here means it won't show up in error messages.) + if ((i = strlen(str)) && toupper(str[i-1])=='Z') { + str[--i] = 0; + oldtz = getenv("TZ"); + if (oldtz) oldtz = xstrdup(oldtz); + setenv("TZ", "UTC0", 1); } - // Is it one of the fancy formats? + // Try each format for (i = 0; i2) { + len = 0; + sscanf(p, "%2u%n", &tm.tm_sec, &len); + p += len; + } + // nanoseconds + for (len = 0; len<9; len++) { + *nano *= 10; + if (isdigit(*p)) *nano += *p++-'0'; + } + } - // Posix format? - sscanf(s, "%2u%2u%2u%2u%n", &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, - &tm.tm_min, &len); - if (len != 8) goto bad_dates; - s += len; - tm.tm_mon--; - - // If year specified, overwrite one we fetched earlier. - if (*s && *s != '.') { - unsigned year; - - len = 0; - sscanf(s, "%u%n", &year, &len); - if (len == 4) tm.tm_year = year - 1900; - else if (len != 2) goto bad_dates; - s += len; - - // 2 digit years, next 50 years are "future", last 50 years are "past". - // A "future" date in past is a century ahead. - // A non-future date in the future is a century behind. - if (len == 2) { - unsigned r1 = tm.tm_year % 100, r2 = (tm.tm_year + 50) % 100, - century = tm.tm_year - r1; - - if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) { - if (year < r1) year += 100; - } else if (year > r1) year -= 100; - tm.tm_year = year + century; + if (!*p) break; } } - // Fractional part? - if (*s == '.') { - len = 0; - sscanf(s, ".%2u%n", &tm.tm_sec, &len); - s += len; - for (len = 0; len<9; len++) { - *nano *= 10; - if (isdigit(*s)) *nano += *s++-'0'; - } - } else tm.tm_sec = 0; // Sanity check field ranges - *t = xvali_date(&tm, str); - - // Shouldn't be any trailing garbage. - if (!*s) return; + *t = xvali_date((i!=ARRAY_LEN(formats)) ? &tm : 0, str); -bad_dates: - // monkey died - xvali_date(0, str); + if (oldtz) setenv("TZ", oldtz, 1); + free(oldtz); } diff --git a/tests/date.test b/tests/date.test index a317d35b..5c490b02 100644 --- a/tests/date.test +++ b/tests/date.test @@ -9,26 +9,26 @@ tz=Europe/Berlin # Unix date parsing. -testing "-d @0" "TZ=$tz date -d @0 2>&1" "Thu Jan 1 01:00:00 CET 1970\n" "" "" +testing "-d @0" "TZ=$tz date -d @0" "Thu Jan 1 01:00:00 CET 1970\n" "" "" testing "-d @0x123 invalid" "TZ=$tz date -d @0x123 2>/dev/null || echo expected error" "expected error\n" "" "" # POSIX format with 2- and 4-digit years. # All SKIP_HOST=1 because coreutils rejects POSIX format dates supplied to -d. # These expected values are from running on the host without -d (not as root!). SKIP_HOST=1 testing "-d MMDDhhmm" \ - "TZ=$tz date -d 06021234 2>&1" "Sun Jun 2 12:34:00 CEST $(date +%Y)\n" "" "" + "TZ=$tz date -d 06021234" "Sun Jun 2 12:34:00 CEST $(date +%Y)\n" "" "" SKIP_HOST=1 testing "-d MMDDhhmmYY.SS" \ - "TZ=$tz date -d 1110143115.30 2>&1" "Tue Nov 10 14:31:30 CET 2015\n" "" "" + "TZ=$tz date -d 1110143115.30" "Tue Nov 10 14:31:30 CET 2015\n" "" "" # busybox thinks this is the year 603 (ISO time 0602-12-34 19:82 with out of range fields normalized). SKIP_HOST=1 testing "-d MMDDhhmmCCYY" \ - "TZ=$tz date -d 060212341982 2>&1" "Wed Jun 2 12:34:00 CEST 1982\n" "" "" + "TZ=$tz date -d 060212341982" "Wed Jun 2 12:34:00 CEST 1982\n" "" "" SKIP_HOST=1 testing "-d MMDDhhmmCCYY.SS" \ - "TZ=$tz date -d 111014312015.30 2>&1" "Tue Nov 10 14:31:30 CET 2015\n" "" "" + "TZ=$tz date -d 111014312015.30" "Tue Nov 10 14:31:30 CET 2015\n" "" "" # ISO date format. -testing "-d 1980-01-02" "TZ=$tz date -d 1980-01-02 2>&1" "Wed Jan 2 00:00:00 CET 1980\n" "" "" -testing "-d 1980-01-02 12:34" "TZ=$tz date -d '1980-01-02 12:34' 2>&1" "Wed Jan 2 12:34:00 CET 1980\n" "" "" -testing "-d 1980-01-02 12:34:56" "TZ=$tz date -d '1980-01-02 12:34:56' 2>&1" "Wed Jan 2 12:34:56 CET 1980\n" "" "" +testing "-d 1980-01-02" "TZ=$tz date -d 1980-01-02" "Wed Jan 2 00:00:00 CET 1980\n" "" "" +testing "-d 1980-01-02 12:34" "TZ=$tz date -d '1980-01-02 12:34'" "Wed Jan 2 12:34:00 CET 1980\n" "" "" +testing "-d 1980-01-02 12:34:56" "TZ=$tz date -d '1980-01-02 12:34:56'" "Wed Jan 2 12:34:56 CET 1980\n" "" "" # Reject Unix times without a leading @. testing "Unix time missing @" "TZ=$tz date 1438053157 2>/dev/null || echo no" \ diff --git a/tests/touch.test b/tests/touch.test index da6fe99a..b010f7d3 100644 --- a/tests/touch.test +++ b/tests/touch.test @@ -32,10 +32,10 @@ testing "-t seconds" \ "touch -t 201201231234.56 walrus && date -r walrus +%Y%m%d-%H%M%S.%N" \ "20120123-123456.000000000\n" "" "" -testing "-t -" "touch -t 200109081946.40 - > walrus && date -r walrus +%s" \ - "1000000000\n" "" "" +testing "-t -" "TZ=utc touch -t 200109090146.40 - > walrus && TZ=utc date -r walrus +%s" \ + "1000000000\n" "" "" -testing "-t nanoseconds" \ +SKIP_HOST=1 testing "-t nanoseconds" \ "touch -t 201201231234.56123456789 walrus && date -r walrus +%Y%m%d-%H%M%S.%N" \ "20120123-123456.123456789\n" "" "" @@ -66,13 +66,12 @@ testing "-t CCMMDDhhmm" \ testing "-a" "touch -t 197101020304 walrus && touch -t 197203040506 -a walrus && stat -c '%X %Y' walrus" \ - "68562360 31662240\n" "" "" -testing "-m" "touch -t 197101020304 walrus && - touch -t 197203040506 -m walrus && stat -c '%X %Y' walrus" \ - "31662240 68562360\n" "" "" -testing "-am" "touch -t 197101020304 walrus && - touch -t 197203040506 -am walrus && stat -c '%X %Y' walrus" \ - "68562360 68562360\n" "" "" - -#testing "-t" + "68555160 31655040\n" "" "" +testing "-m" "TZ=utc touch -t 197101020304 walrus && + TZ=utc touch -t 197203040506 -m walrus && TZ=utc stat -c '%X %Y' walrus" \ + "31633440 68533560\n" "" "" +testing "-am" "TZ=utc touch -t 197101020304 walrus && + TZ=utc touch -t 197203040506 -am walrus && TZ=utc stat -c '%X %Y' walrus" \ + "68533560 68533560\n" "" "" + rm walrus walrus2 diff --git a/toys/posix/date.c b/toys/posix/date.c index 685ac8bf..a1762854 100644 --- a/toys/posix/date.c +++ b/toys/posix/date.c @@ -77,7 +77,7 @@ static void parse_date(char *str, time_t *t) tzset(); } time(t); - xparsedate(str, t, &TT.nano); + xparsedate(str, t, &TT.nano, 1); if (new_tz) { if (old_tz) setenv("TZ", old_tz, 1); else unsetenv("TZ"); diff --git a/toys/posix/touch.c b/toys/posix/touch.c index 3775c8bc..5e33d7f7 100644 --- a/toys/posix/touch.c +++ b/toys/posix/touch.c @@ -41,67 +41,17 @@ void touch_main(void) // use current time if no -t or -d ts[0].tv_nsec = UTIME_NOW; - if (FLAG(t) || FLAG(d)) { - char *s, *date, **format; - struct tm tm; - int len = 0; - - // Initialize default values for time fields - ts->tv_sec = time(0); - ts->tv_nsec = 0; - - // List of search types - if (FLAG(d)) { - format = (char *[]){"%Y-%m-%dT%T", "%Y-%m-%d %T", 0}; - date = TT.d; - } else { - format = (char *[]){"%m%d%H%M", "%y%m%d%H%M", "%C%y%m%d%H%M", 0}; - date = TT.t; - } - - // Trailing Z means UTC timezone, don't expect libc to know this. - i = strlen(s = date); - if (i && toupper(date[i-1])=='Z') { - date[i-1] = 0; - setenv("TZ", "UTC0", 1); - } - while (*format) { - if (FLAG(t)) { - s = strchr(date, '.'); - if ((s ? s-date : strlen(date)) != strlen(*format)) { - format++; - continue; - } - } - localtime_r(&(ts->tv_sec), &tm); - // Adjusting for daylight savings time gives the wrong answer. - tm.tm_isdst = 0; - tm.tm_sec = 0; - s = strptime(date, *format++, &tm); - - // parse nanoseconds - if (s && *s=='.' && isdigit(s[1])) { - s++; - if (FLAG(t)) - if (1 == sscanf(s, "%2u%n", &(tm.tm_sec), &len)) s += len; - if (1 == sscanf(s, "%lu%n", &ts->tv_nsec, &len)) { - s += len; - if (ts->tv_nsec > 999999999) s = 0; - else while (len++ < 9) ts->tv_nsec *= 10; - } - } - if (s && !*s) break; - } + if (FLAG(t) || FLAG(d)) { + time_t t = time(0); + unsigned nano; - errno = 0; - ts->tv_sec = mktime(&tm); - if (!s || *s || ts->tv_sec == -1) perror_exit("bad '%s'", date); + xparsedate(TT.t ? TT.t : TT.d, &t, &nano, 0); + ts->tv_sec = t; + ts->tv_nsec = nano; } ts[1]=ts[0]; - // Set time from -r? - if (TT.r) { struct stat st; -- cgit v1.2.3