date: factor out date parsing (in preparation for touch -d)

function                                             old     new   delta
parse_datestr                                          -     391    +391
sha512_process_block128                             1283    1310     +27
buffer_fill_and_print                                179     196     +17
nexpr                                                826     840     +14
unzip_main                                          1931    1939      +8
popstring                                            134     140      +6
qrealloc                                              33      36      +3
builtin_umask                                        121     123      +2
evalvar                                             1365    1363      -2
changepath                                           194     192      -2
do_compress                                         1698    1688     -10
hwclock_main                                         340     329     -11
cmdputs                                              414     402     -12
identify                                            4343    4329     -14
date_main                                           1186     687    -499
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 7/7 up/down: 468/-550)          Total: -82 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2009-07-18 03:40:35 +02:00
parent d23f64eba7
commit 73b71f381d
5 changed files with 126 additions and 84 deletions

View File

@ -10,8 +10,6 @@
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/ */
#include "libbb.h"
/* This 'date' command supports only 2 time setting formats, /* This 'date' command supports only 2 time setting formats,
all the GNU strftime stuff (its in libc, lets use it), all the GNU strftime stuff (its in libc, lets use it),
setting time using UTC and displaying it, as well as setting time using UTC and displaying it, as well as
@ -23,18 +21,51 @@
/* Default input handling to save surprising some people */ /* Default input handling to save surprising some people */
/* GNU coreutils 6.9 man page:
* date [OPTION]... [+FORMAT]
* date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
* -d, --date=STRING
* display time described by STRING, not `now'
* -f, --file=DATEFILE
* like --date once for each line of DATEFILE
* -r, --reference=FILE
* display the last modification time of FILE
* -R, --rfc-2822
* output date and time in RFC 2822 format.
* Example: Mon, 07 Aug 2006 12:34:56 -0600
* --rfc-3339=TIMESPEC
* output date and time in RFC 3339 format.
* TIMESPEC='date', 'seconds', or 'ns'
* Date and time components are separated by a single space:
* 2006-08-07 12:34:56-06:00
* -s, --set=STRING
* set time described by STRING
* -u, --utc, --universal
* print or set Coordinated Universal Time
*
* Busybox:
* long options are not supported
* -f is not supported
* -I seems to roughly match --rfc-3339, but -I has _optional_ param
* (thus "-I seconds" doesn't work, only "-Iseconds"),
* and does not support -Ins
* -D FMT is a bbox extension for _input_ conversion of -d DATE
*/
#include "libbb.h"
#define DATE_OPT_RFC2822 0x01 enum {
#define DATE_OPT_SET 0x02 OPT_RFC2822 = (1 << 0), /* R */
#define DATE_OPT_UTC 0x04 OPT_SET = (1 << 1), /* s */
#define DATE_OPT_DATE 0x08 OPT_UTC = (1 << 2), /* u */
#define DATE_OPT_REFERENCE 0x10 OPT_DATE = (1 << 3), /* d */
#define DATE_OPT_TIMESPEC 0x20 OPT_REFERENCE = (1 << 4), /* r */
#define DATE_OPT_HINT 0x40 OPT_TIMESPEC = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */
OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
};
static void maybe_set_utc(int opt) static void maybe_set_utc(int opt)
{ {
if (opt & DATE_OPT_UTC) if (opt & OPT_UTC)
putenv((char*)"TZ=UTC0"); putenv((char*)"TZ=UTC0");
} }
@ -60,7 +91,7 @@ int date_main(int argc UNUSED_PARAM, char **argv)
argv += optind; argv += optind;
maybe_set_utc(opt); maybe_set_utc(opt);
if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_TIMESPEC)) { if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_TIMESPEC)) {
ifmt = 0; /* default is date */ ifmt = 0; /* default is date */
if (isofmt_arg) { if (isofmt_arg) {
static const char isoformats[] ALIGN1 = static const char isoformats[] ALIGN1 =
@ -76,8 +107,8 @@ int date_main(int argc UNUSED_PARAM, char **argv)
fmt_dt2str = &argv[0][1]; /* Skip over the '+' */ fmt_dt2str = &argv[0][1]; /* Skip over the '+' */
argv++; argv++;
} }
if (!(opt & (DATE_OPT_SET | DATE_OPT_DATE))) { if (!(opt & (OPT_SET | OPT_DATE))) {
opt |= DATE_OPT_SET; opt |= OPT_SET;
date_str = argv[0]; /* can be NULL */ date_str = argv[0]; /* can be NULL */
if (date_str) if (date_str)
argv++; argv++;
@ -88,13 +119,14 @@ int date_main(int argc UNUSED_PARAM, char **argv)
/* Now we have parsed all the information except the date format /* Now we have parsed all the information except the date format
which depends on whether the clock is being set or read */ which depends on whether the clock is being set or read */
if (opt & DATE_OPT_REFERENCE) { if (opt & OPT_REFERENCE) {
struct stat statbuf; struct stat statbuf;
xstat(filename, &statbuf); xstat(filename, &statbuf);
tm = statbuf.st_mtime; tm = statbuf.st_mtime;
} else } else {
time(&tm); time(&tm);
memcpy(&tm_time, localtime(&tm), sizeof(tm_time)); }
localtime_r(&tm, &tm_time);
/* If date string is given, update tm_time, and maybe set date */ /* If date string is given, update tm_time, and maybe set date */
if (date_str != NULL) { if (date_str != NULL) {
@ -104,72 +136,14 @@ int date_main(int argc UNUSED_PARAM, char **argv)
tm_time.tm_hour = 0; tm_time.tm_hour = 0;
/* Process any date input to UNIX time since 1 Jan 1970 */ /* Process any date input to UNIX time since 1 Jan 1970 */
if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_HINT)) { if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_HINT)) {
if (strptime(date_str, fmt_str2dt, &tm_time) == NULL) if (strptime(date_str, fmt_str2dt, &tm_time) == NULL)
bb_error_msg_and_die(bb_msg_invalid_date, date_str); bb_error_msg_and_die(bb_msg_invalid_date, date_str);
} else { } else {
char end = '\0'; parse_datestr(date_str, &tm_time);
const char *last_colon = strrchr(date_str, ':');
if (last_colon != NULL) {
/* Parse input and assign appropriately to tm_time */
if (sscanf(date_str, "%u:%u%c",
&tm_time.tm_hour,
&tm_time.tm_min,
&end) >= 2) {
/* no adjustments needed */
} else if (sscanf(date_str, "%u.%u-%u:%u%c",
&tm_time.tm_mon, &tm_time.tm_mday,
&tm_time.tm_hour, &tm_time.tm_min,
&end) >= 4) {
/* Adjust dates from 1-12 to 0-11 */
tm_time.tm_mon -= 1;
} else if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &tm_time.tm_year,
&tm_time.tm_mon, &tm_time.tm_mday,
&tm_time.tm_hour, &tm_time.tm_min,
&end) >= 5) {
tm_time.tm_year -= 1900; /* Adjust years */
tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
} else if (sscanf(date_str, "%u-%u-%u %u:%u%c", &tm_time.tm_year,
&tm_time.tm_mon, &tm_time.tm_mday,
&tm_time.tm_hour, &tm_time.tm_min,
&end) >= 5) {
tm_time.tm_year -= 1900; /* Adjust years */
tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
//TODO: coreutils 6.9 also accepts "YYYY-MM-DD HH" (no minutes)
} else {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
if (end == ':') {
if (sscanf(last_colon + 1, "%u%c", &tm_time.tm_sec, &end) == 1)
end = '\0';
/* else end != NUL and we error out */
}
} else {
if (sscanf(date_str, "%2u%2u%2u%2u%u%c", &tm_time.tm_mon,
&tm_time.tm_mday, &tm_time.tm_hour, &tm_time.tm_min,
&tm_time.tm_year, &end) < 4)
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
/* correct for century - minor Y2K problem here? */
if (tm_time.tm_year >= 1900) {
tm_time.tm_year -= 1900;
}
/* adjust date */
tm_time.tm_mon -= 1;
if (end == '.') {
if (sscanf(strchr(date_str, '.') + 1, "%u%c",
&tm_time.tm_sec, &end) == 1)
end = '\0';
/* else end != NUL and we error out */
}
}
if (end != '\0') {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
} }
/* Correct any day of week and day of year etc. fields */ /* Correct any day of week and day of year etc. fields */
tm_time.tm_isdst = -1; /* Be sure to recheck dst. */ tm_time.tm_isdst = -1; /* Be sure to recheck dst */
tm = mktime(&tm_time); tm = mktime(&tm_time);
if (tm < 0) { if (tm < 0) {
bb_error_msg_and_die(bb_msg_invalid_date, date_str); bb_error_msg_and_die(bb_msg_invalid_date, date_str);
@ -177,7 +151,7 @@ int date_main(int argc UNUSED_PARAM, char **argv)
maybe_set_utc(opt); maybe_set_utc(opt);
/* if setting time, set it */ /* if setting time, set it */
if ((opt & DATE_OPT_SET) && stime(&tm) < 0) { if ((opt & OPT_SET) && stime(&tm) < 0) {
bb_perror_msg("cannot set date"); bb_perror_msg("cannot set date");
} }
} }
@ -207,9 +181,9 @@ int date_main(int argc UNUSED_PARAM, char **argv)
} }
format_utc: format_utc:
fmt_dt2str[i++] = '%'; fmt_dt2str[i++] = '%';
fmt_dt2str[i] = (opt & DATE_OPT_UTC) ? 'Z' : 'z'; fmt_dt2str[i] = (opt & OPT_UTC) ? 'Z' : 'z';
} }
} else if (opt & DATE_OPT_RFC2822) { } else if (opt & OPT_RFC2822) {
/* Undo busybox.c for date -R */ /* Undo busybox.c for date -R */
if (ENABLE_LOCALE_SUPPORT) if (ENABLE_LOCALE_SUPPORT)
setlocale(LC_TIME, "C"); setlocale(LC_TIME, "C");

View File

@ -437,6 +437,9 @@ struct BUG_too_small {
}; };
void parse_datestr(const char *date_str, struct tm *tm_time) FAST_FUNC;
int xsocket(int domain, int type, int protocol) FAST_FUNC; int xsocket(int domain, int type, int protocol) FAST_FUNC;
void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC; void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC;
void xlisten(int s, int backlog) FAST_FUNC; void xlisten(int s, int backlog) FAST_FUNC;

View File

@ -6,9 +6,71 @@
* *
* Licensed under GPL version 2, see file LICENSE in this tarball for details. * Licensed under GPL version 2, see file LICENSE in this tarball for details.
*/ */
#include "libbb.h" #include "libbb.h"
void FAST_FUNC parse_datestr(const char *date_str, struct tm *tm_time)
{
char end = '\0';
const char *last_colon = strrchr(date_str, ':');
if (last_colon != NULL) {
/* Parse input and assign appropriately to tm_time */
if (sscanf(date_str, "%u:%u%c",
&tm_time->tm_hour,
&tm_time->tm_min,
&end) >= 2) {
/* no adjustments needed */
} else if (sscanf(date_str, "%u.%u-%u:%u%c",
&tm_time->tm_mon, &tm_time->tm_mday,
&tm_time->tm_hour, &tm_time->tm_min,
&end) >= 4) {
/* Adjust dates from 1-12 to 0-11 */
tm_time->tm_mon -= 1;
} else if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &tm_time->tm_year,
&tm_time->tm_mon, &tm_time->tm_mday,
&tm_time->tm_hour, &tm_time->tm_min,
&end) >= 5) {
tm_time->tm_year -= 1900; /* Adjust years */
tm_time->tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
} else if (sscanf(date_str, "%u-%u-%u %u:%u%c", &tm_time->tm_year,
&tm_time->tm_mon, &tm_time->tm_mday,
&tm_time->tm_hour, &tm_time->tm_min,
&end) >= 5) {
tm_time->tm_year -= 1900; /* Adjust years */
tm_time->tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
//TODO: coreutils 6.9 also accepts "YYYY-MM-DD HH" (no minutes)
} else {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
if (end == ':') {
if (sscanf(last_colon + 1, "%u%c", &tm_time->tm_sec, &end) == 1)
end = '\0';
/* else end != NUL and we error out */
}
} else {
if (sscanf(date_str, "%2u%2u%2u%2u%u%c", &tm_time->tm_mon,
&tm_time->tm_mday, &tm_time->tm_hour, &tm_time->tm_min,
&tm_time->tm_year, &end) < 4)
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
/* correct for century - minor Y2K problem here? */
if (tm_time->tm_year >= 1900) {
tm_time->tm_year -= 1900;
}
/* adjust date */
tm_time->tm_mon -= 1;
if (end == '.') {
if (sscanf(strchr(date_str, '.') + 1, "%u%c",
&tm_time->tm_sec, &end) == 1)
end = '\0';
/* else end != NUL and we error out */
}
}
if (end != '\0') {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
}
#if ENABLE_MONOTONIC_SYSCALL #if ENABLE_MONOTONIC_SYSCALL
#include <sys/syscall.h> #include <sys/syscall.h>

View File

@ -470,9 +470,9 @@ prep() {
check() { check() {
eval $2 >t_actual 2>&1 eval $2 >t_actual 2>&1
if $ECHO -ne "$expected" | cmp - t_actual; then if $ECHO -ne "$expected" | cmp - t_actual; then
echo "$1: PASS" echo "PASS: $1"
else else
echo "$1: FAIL" echo "FAIL: $1"
fi fi
} }

View File

@ -36,7 +36,10 @@ static void write_rtc(time_t t, int utc)
struct tm tm; struct tm tm;
int rtc = rtc_xopen(&rtcname, O_WRONLY); int rtc = rtc_xopen(&rtcname, O_WRONLY);
tm = *(utc ? gmtime(&t) : localtime(&t)); if (utc)
gmtime_r(&t, &tm);
else
localtime_r(&t, &tm);
tm.tm_isdst = 0; tm.tm_isdst = 0;
xioctl(rtc, RTC_SET_TIME, &tm); xioctl(rtc, RTC_SET_TIME, &tm);