diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/datetime.c | 319 | ||||
-rw-r--r-- | src/datetime.h | 3 | ||||
-rw-r--r-- | src/formats.h | 255 | ||||
-rw-r--r-- | src/i18n.h | 11 | ||||
-rw-r--r-- | src/local.mk | 4 | ||||
-rw-r--r-- | src/main.c | 192 |
6 files changed, 575 insertions, 209 deletions
diff --git a/src/datetime.c b/src/datetime.c index 079266b..72f0219 100644 --- a/src/datetime.c +++ b/src/datetime.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 P. J. McDermott + * Copyright (C) 2021, 2022 P. J. McDermott * * This file is part of @ * @@ -18,6 +18,9 @@ */ #define _XOPEN_SOURCE +/* glibc requires these for timegm(): */ +#define _DEFAULT_SOURCE +#define _BSD_SOURCE #include <ctype.h> #include <errno.h> @@ -28,80 +31,33 @@ #include <string.h> #include <time.h> #include "datetime.h" +#include "formats.h" +#include "i18n.h" -/* IMPORTANT: All of the format strings in each array must be padded with spaces - * to be of equal lengths */ -static const char *DATETIME_DATE_FMTS_[] = { - " ", /* */ - " %a ", /* Wed */ - " %Y-%m-%d ", /* 1969-12-31 */ - " %Y/%m/%d ", /* 1969/12/31 */ - " %m-%d-%Y ", /* 12-31-1969 */ - " %m-%d ", /* 12-31 */ - " %m/%d/%Y ", /* 12/31/1969 */ - " %m/%d ", /* 12/31 */ - " %e %b %Y ", /* 31 Dec 1969 */ - " %e %b ", /* 31 Dec */ - " %d-%b-%Y ", /* 31-Dec-1969 */ - " %d-%b ", /* 31-Dec */ - " %d/%b/%Y ", /* 31/Dec/1969 */ - " %d/%b ", /* 31/Dec */ - " %a %e %b %Y ", /* Wed 31 Dec 1969 */ - " %a %e %b ", /* Wed 31 Dec */ - " %a %d-%b-%Y ", /* Wed 31-Dec-1969 */ - " %a %d-%b ", /* Wed 31-Dec */ - " %a %d/%b/%Y ", /* Wed 31/Dec/1969 */ - " %a %d/%b ", /* Wed 31/Dec */ - " %a, %e %b %Y ", /* Wed, 31 Dec 1969 */ - " %a, %e %b ", /* Wed, 31 Dec */ - " %a, %d-%b-%Y ", /* Wed, 31-Dec-1969 */ - " %a, %d-%b ", /* Wed, 31-Dec */ - " %a, %d/%b/%Y ", /* Wed, 31/Dec/1969 */ - " %a, %d/%b ", /* Wed, 31/Dec */ - " %b %e, %Y ", /* Dec 31, 1969 */ - " %b %e ", /* Dec 31 */ - " %b-%d-%Y ", /* Dec-31-1969 */ - " %b-%d ", /* Dec-31 */ - " %b/%d/%Y ", /* Dec/31/1969 */ - " %b/%d ", /* Dec/31 */ - " %a %b %e, %Y ", /* Wed Dec 31, 1969 */ - " %a %b %e ", /* Wed Dec 31 */ - " %a %b-%d-%Y ", /* Wed Dec-31-1969 */ - " %a %b-%d ", /* Wed Dec-31 */ - " %a %b/%d/%Y ", /* Wed Dec/31/1969 */ - " %a %b/%d ", /* Wed Dec/31 */ - " %a, %b %e, %Y ", /* Wed, Dec 31, 1969 */ - " %a, %b %e ", /* Wed, Dec 31 */ - " %a, %b-%d-%Y ", /* Wed, Dec-31-1969 */ - " %a, %b-%d ", /* Wed, Dec-31 */ - " %a, %b/%d/%Y ", /* Wed, Dec/31/1969 */ - " %a, %b/%d ", /* Wed, Dec/31 */ -}; -static const char *DATETIME_TIME_FMTS_[] = { - " %I:%M:%S %p ", /* 7:00:01 PM */ - " %H:%M:%S ", /* 19:00:01 */ - " %I:%M %p ", /* 7:00 PM */ - " %H:%M ", /* 19:00 */ - " %I%M%S %p ", /* 70001 PM */ - " %H%M%S ", /* 190001 */ - " %I%M %p ", /* 700 PM */ - " %H%M ", /* 1900 */ -}; -static const char *DATETIME_MISC_FMTS_[] = { - /* ISO 8601: 1969-12-31 delimited by "T" */ - " %Y-%m-%dT%H:%M:%S ", - " %Y-%m-%dT%H:%M ", - /* ISO 8601: 19691231 delimited by "T" */ - " %Y%m%dT%H%M%S ", - " %Y%m%dT%H%M ", - /* 19691231 */ - " %Y%m%d %I%M%S %p ", - " %Y%m%d %H%M%S ", - " %Y%m%d %I%M %p ", - " %Y%m%d %H%M ", - /* Wed Dec 31 19:00:01 1969 */ - " %a %b %d %H:%M:%S %Y ", -}; +static void +_datetime_buf_cpy_p(char *dst, const char *src) +{ + for (; *src != '\0'; ++src, ++dst) { + if (*src == '^') { + *dst = ' '; + } else { + *dst = *src; + } + } + *dst = '\0'; +} + +static void +_datetime_buf_cpy_f(char *dst, const char *src) +{ + for (; *src != '\0'; ++src, ++dst) { + if (*src == '^') { + ++src; + } + *dst = *src; + } + *dst = '\0'; +} static void _datetime_reset_tm(struct tm *tm) @@ -117,58 +73,64 @@ _datetime_reset_tm(struct tm *tm) } static void -_datetime_normalize(struct tm *arg_tm, time_t *arg_sec) +_datetime_normalize(struct tm *now_tm, struct tm *arg_tm, time_t *arg_sec) { - time_t now_sec; - struct tm *now_tm; - int wday; + int days_in_mon[12] = {31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31}; + + if (now_tm->tm_year % 4 == 0 && (now_tm->tm_year % 100 != 0 || + now_tm->tm_year % 400 == 0)) { + days_in_mon[1] = 29; + } if (arg_tm->tm_sec == INT_MIN) { arg_tm->tm_sec = 0; } if (arg_tm->tm_mday == INT_MIN && arg_tm->tm_wday == INT_MIN) { /* No date specified; try today. */ - now_sec = time(NULL); - now_tm = localtime(&now_sec); arg_tm->tm_year = now_tm->tm_year; arg_tm->tm_mon = now_tm->tm_mon; arg_tm->tm_mday = now_tm->tm_mday; arg_tm->tm_wday = now_tm->tm_wday; *arg_sec = mktime(arg_tm); - if (*arg_sec <= mktime(now_tm)) { + if (difftime(*arg_sec, mktime(now_tm)) <= 0) { /* Specified time already happened today; use tomorrow. - * Adding the number of seconds in a day is a shortcut - * that ignores leap seconds. One better method would - * be to increment tm_mday % days in tm_mon, etc. */ - *arg_sec += 60 * 60 * 24; - now_tm = localtime(arg_sec); - arg_tm->tm_year = now_tm->tm_year; - arg_tm->tm_mon = now_tm->tm_mon; - arg_tm->tm_mday = now_tm->tm_mday; + */ + ++arg_tm->tm_mday; + arg_tm->tm_wday = (arg_tm->tm_wday + 1) % 7; + if (arg_tm->tm_mday > days_in_mon[arg_tm->tm_mon]) { + arg_tm->tm_mday = 1; + ++arg_tm->tm_mon; + if (arg_tm->tm_mon >= 12) { + arg_tm->tm_mon = 0; + ++arg_tm->tm_year; + } + } *arg_sec = mktime(arg_tm); } } else if (arg_tm->tm_mday == INT_MIN) { - /* Only a weekday specified; try tomorrow or next week. Uses - * same shortcut as above. */ - wday = arg_tm->tm_wday; - now_sec = time(NULL); - now_tm = localtime(&now_sec); + /* Only a weekday specified; try tomorrow or next week. */ arg_tm->tm_year = now_tm->tm_year; arg_tm->tm_mon = now_tm->tm_mon; arg_tm->tm_mday = now_tm->tm_mday; - arg_tm->tm_wday = now_tm->tm_wday; - if (wday <= now_tm->tm_wday) { - wday += 7; + if (arg_tm->tm_wday <= now_tm->tm_wday) { + arg_tm->tm_mday += 7; + } + arg_tm->tm_mday += arg_tm->tm_wday - now_tm->tm_wday; + if (arg_tm->tm_mday > days_in_mon[arg_tm->tm_mon]) { + arg_tm->tm_mday -= days_in_mon[arg_tm->tm_mon]; + ++arg_tm->tm_mon; + if (arg_tm->tm_mon >= 12) { + arg_tm->tm_mon = 0; + ++arg_tm->tm_year; + } } *arg_sec = mktime(arg_tm); - *arg_sec += (60 * 60 * 24) * (wday - now_tm->tm_wday); } else if (arg_tm->tm_year == INT_MIN) { /* No year specified; try this year. */ - now_sec = time(NULL); - now_tm = localtime(&now_sec); arg_tm->tm_year = now_tm->tm_year; *arg_sec = mktime(arg_tm); - if (*arg_sec <= mktime(now_tm)) { + if (difftime(*arg_sec, mktime(now_tm)) <= 0) { /* Specified time already happened this year; use next * year. */ ++arg_tm->tm_year; @@ -180,71 +142,118 @@ _datetime_normalize(struct tm *arg_tm, time_t *arg_sec) } int -datetime_parse(const char *input, time_t *arg_sec) +datetime_parse(struct tm *now_tm, const char *input, + struct tm *arg_tm, time_t *arg_sec) { int date_fmt_len; int time_fmt_len; + int misc_fmt_len; + int fmt_len; char *fmt_buf; size_t d; size_t t; char *end; - struct tm arg_tm; + time_t sec; + bool got; - date_fmt_len = strlen(DATETIME_DATE_FMTS_[0]); - time_fmt_len = strlen(DATETIME_TIME_FMTS_[0]); - fmt_buf = calloc(date_fmt_len + time_fmt_len + 1, sizeof(*fmt_buf)); + date_fmt_len = strlen(FORMATS_DATE[0]); + time_fmt_len = strlen(FORMATS_TIME[0]); + misc_fmt_len = strlen(FORMATS_MISC[0]); + fmt_len = date_fmt_len + time_fmt_len; + if (misc_fmt_len > fmt_len) { + fmt_len = misc_fmt_len; + } + fmt_buf = calloc(fmt_len + 1, sizeof(*fmt_buf)); if (fmt_buf == NULL) { - fprintf(stderr, "Failed to allocate buffer: %s\n", + fprintf(stderr, _("Failed to allocate buffer: %s\n"), strerror(errno)); return -1; } - for (d = 0; d < sizeof(DATETIME_DATE_FMTS_) / - sizeof(DATETIME_DATE_FMTS_[0]); ++d) { - memcpy(fmt_buf, DATETIME_DATE_FMTS_[d], date_fmt_len); - for (t = 0; t < sizeof(DATETIME_TIME_FMTS_) / - sizeof(DATETIME_TIME_FMTS_[0]); ++t) { - memcpy(fmt_buf + date_fmt_len, DATETIME_TIME_FMTS_[t], - time_fmt_len); - _datetime_reset_tm(&arg_tm); - end = strptime(input, fmt_buf, &arg_tm); - if (end != NULL && *end == '\0') { + sec = *arg_sec; /* GCC is dumb. */ + got = false; + for (t = 0; t < sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0]); ++t) { + _datetime_buf_cpy_p(fmt_buf, FORMATS_TIME[t]); + _datetime_reset_tm(arg_tm); + end = strptime(input, fmt_buf, arg_tm); + if (end != NULL && *end == '\0') { + _datetime_normalize(now_tm, arg_tm, arg_sec); + if (arg_tm->tm_year >= 0) { free(fmt_buf); - _datetime_normalize(&arg_tm, arg_sec); return 0; } + sec = *arg_sec; + got = true; } } - for (t = 0; t < sizeof(DATETIME_TIME_FMTS_) / - sizeof(DATETIME_TIME_FMTS_[0]); ++t) { - memcpy(fmt_buf, DATETIME_TIME_FMTS_[t], time_fmt_len); - for (d = 0; d < sizeof(DATETIME_DATE_FMTS_) / - sizeof(DATETIME_DATE_FMTS_[0]); ++d) { - memcpy(fmt_buf + time_fmt_len, DATETIME_DATE_FMTS_[d], - date_fmt_len); - _datetime_reset_tm(&arg_tm); - end = strptime(input, fmt_buf, &arg_tm); + for (d = 0; d < sizeof(FORMATS_DATE) / sizeof(FORMATS_DATE[0]); ++d) { + if (FORMATS_DATE[d][0] == '\0') { + break; + } + _datetime_buf_cpy_p(fmt_buf, FORMATS_DATE[d]); + for (t = 0; t < sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0]); + ++t) { + _datetime_buf_cpy_p(fmt_buf + date_fmt_len, + FORMATS_TIME[t]); + _datetime_reset_tm(arg_tm); + end = strptime(input, fmt_buf, arg_tm); if (end != NULL && *end == '\0') { - free(fmt_buf); - _datetime_normalize(&arg_tm, arg_sec); - return 0; + _datetime_normalize(now_tm, arg_tm, arg_sec); + if (arg_tm->tm_year >= 0) { + free(fmt_buf); + return 0; + } + sec = *arg_sec; + got = true; + } + } + } + for (t = 0; t < sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0]); ++t) { + _datetime_buf_cpy_p(fmt_buf, FORMATS_TIME[t]); + for (d = 0; d < sizeof(FORMATS_DATE) / sizeof(FORMATS_DATE[0]); + ++d) { + if (FORMATS_DATE[d][0] == '\0') { + break; + } + _datetime_buf_cpy_p(fmt_buf + time_fmt_len, + FORMATS_DATE[d]); + _datetime_reset_tm(arg_tm); + end = strptime(input, fmt_buf, arg_tm); + if (end != NULL && *end == '\0') { + _datetime_normalize(now_tm, arg_tm, arg_sec); + if (arg_tm->tm_year >= 0) { + free(fmt_buf); + return 0; + } + sec = *arg_sec; + got = true; } } } - for (d = 0; d < sizeof(DATETIME_MISC_FMTS_) / - sizeof(DATETIME_MISC_FMTS_[0]) ; ++d) { - _datetime_reset_tm(&arg_tm); - end = strptime(input, DATETIME_MISC_FMTS_[d], &arg_tm); + for (d = 0; d < sizeof(FORMATS_MISC) / sizeof(FORMATS_MISC[0]); ++d) { + _datetime_buf_cpy_p(fmt_buf, FORMATS_MISC[d]); + _datetime_reset_tm(arg_tm); + end = strptime(input, fmt_buf, arg_tm); if (end != NULL && *end == '\0') { - free(fmt_buf); - _datetime_normalize(&arg_tm, arg_sec); - return 0; + _datetime_normalize(now_tm, arg_tm, arg_sec); + if (arg_tm->tm_year >= 0) { + free(fmt_buf); + return 0; + } + sec = *arg_sec; + got = true; } } - free(fmt_buf); - fputs("Unknown date format\n", stderr); - return -1; + if (got == true) { + *arg_sec = sec; + free(fmt_buf); + return 0; + } else { + fputs(_("Unknown date format\n"), stderr); + free(fmt_buf); + return -1; + } } static void @@ -283,30 +292,36 @@ _datetime_strftime(const char *fmts[], const struct tm *tm, char **out, int resized; if (*i == 0) { - *buf_sz = strlen(fmts[0]); + *buf_sz = strlen(fmts[0]) + 1; *buf = calloc(*buf_sz, sizeof(**buf)); if (*buf == NULL) { - fprintf(stderr, "Failed to allocate buffer: %s\n", + fprintf(stderr, _("Failed to allocate buffer: %s\n"), strerror(errno)); return -1; } *out = calloc(*buf_sz, sizeof(**out)); if (*out == NULL) { - fprintf(stderr, "Failed to allocate buffer: %s\n", + fprintf(stderr, _("Failed to allocate buffer: %s\n"), strerror(errno)); return -1; } } + if (fmts[*i][0] == '\0') { + ++*i; + } + + _datetime_buf_cpy_f(*out, fmts[*i]); + resized = 0; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" - while (strftime(*buf, *buf_sz, fmts[*i], tm) == 0) { + while (strftime(*buf, *buf_sz, *out, tm) == 0) { #pragma GCC diagnostic pop ++*buf_sz; *buf = realloc(*buf, *buf_sz * sizeof(**buf)); if (*buf == NULL) { - fprintf(stderr, "Failed to allocate buffer: %s\n", + fprintf(stderr, _("Failed to allocate buffer: %s\n"), strerror(errno)); return -1; } @@ -315,7 +330,7 @@ _datetime_strftime(const char *fmts[], const struct tm *tm, char **out, if (resized > 0) { *out = realloc(*out, *buf_sz * sizeof(**out)); if (*out == NULL) { - fprintf(stderr, "Failed to allocate buffer: %s\n", + fprintf(stderr, _("Failed to allocate buffer: %s\n"), strerror(errno)); return -1; } @@ -331,7 +346,7 @@ int datetime_strftime_date(const struct tm *tm, char **out, size_t *buf_sz, char **buf, size_t *i) { - if (*i >= sizeof(DATETIME_DATE_FMTS_) / sizeof(DATETIME_DATE_FMTS_[0])){ + if (*i >= sizeof(FORMATS_DATE) / sizeof(FORMATS_DATE[0])) { if (*out != NULL) { free(*out); } @@ -341,14 +356,14 @@ datetime_strftime_date(const struct tm *tm, char **out, size_t *buf_sz, return 0; } - return _datetime_strftime(DATETIME_DATE_FMTS_, tm, out, buf_sz, buf, i); + return _datetime_strftime(FORMATS_DATE, tm, out, buf_sz, buf, i); } int datetime_strftime_time(const struct tm *tm, char **out, size_t *buf_sz, char **buf, size_t *i) { - if (*i >= sizeof(DATETIME_TIME_FMTS_) / sizeof(DATETIME_TIME_FMTS_[0])){ + if (*i >= sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0])) { if (*out != NULL) { free(*out); } @@ -358,14 +373,14 @@ datetime_strftime_time(const struct tm *tm, char **out, size_t *buf_sz, return 0; } - return _datetime_strftime(DATETIME_TIME_FMTS_, tm, out, buf_sz, buf, i); + return _datetime_strftime(FORMATS_TIME, tm, out, buf_sz, buf, i); } int datetime_strftime_misc(const struct tm *tm, char **out, size_t *buf_sz, char **buf, size_t *i) { - if (*i >= sizeof(DATETIME_MISC_FMTS_) / sizeof(DATETIME_MISC_FMTS_[0])){ + if (*i >= sizeof(FORMATS_MISC) / sizeof(FORMATS_MISC[0])) { if (*out != NULL) { free(*out); } @@ -375,5 +390,5 @@ datetime_strftime_misc(const struct tm *tm, char **out, size_t *buf_sz, return 0; } - return _datetime_strftime(DATETIME_MISC_FMTS_, tm, out, buf_sz, buf, i); + return _datetime_strftime(FORMATS_MISC, tm, out, buf_sz, buf, i); } diff --git a/src/datetime.h b/src/datetime.h index edff8f7..1094cf8 100644 --- a/src/datetime.h +++ b/src/datetime.h @@ -23,7 +23,8 @@ #include <time.h> int -datetime_parse(const char *input, time_t *arg_sec); +datetime_parse(struct tm *now_tm, const char *input, + struct tm *arg_tm, time_t *arg_sec); int datetime_strftime_date(const struct tm *tm, char **out, size_t *out_sz, diff --git a/src/formats.h b/src/formats.h new file mode 100644 index 0000000..c88f731 --- /dev/null +++ b/src/formats.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2021, 2022 P. J. McDermott + * + * This file is part of @ + * + * @ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * @ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with @. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef FORMATS_H_ +#define FORMATS_H_ + +/* + * IMPORTANT: + * + * All of the format strings in each array must be padded with spaces to be of + * equal lengths. + * + * Conversion specifications are used by both strptime() and strftime(). As + * required by the former function, there shall be "white-space or other non- + * alphanumeric characters between any two conversion specifications unless all + * of the adjacent conversion specifications convert a known, fixed number of + * characters." [POSIX] The "^" character may be used to separate conversion + * specifications that should be separated (by a space) when parsing and + * adjacent when formatting for printing. For example, " %I^%M %p " will be + * changed to " %I %M %p " when parsing and to " %I%M %p " when formatting for + * printing. + */ + +static const char *FORMATS_DATE[] = { + " %a ", /* Wed */ + " %a. ", /* Wed. */ + " %Y-%m-%d ", /* 1969-12-31 */ + " %Y/%m/%d ", /* 1969/12/31 */ + " %Y.%m.%d ", /* 1969.12.31 */ + " %m-%d-%Y ", /* 12-31-1969 */ + " %m-%d ", /* 12-31 */ + " %m/%d/%Y ", /* 12/31/1969 */ + " %m/%d ", /* 12/31 */ + " %m.%d.%Y ", /* 12.31.1969 */ + " %m.%d ", /* 12.31 */ + " %e %b %Y ", /* 31 Dec 1969 */ + " %e %b. %Y ", /* 31 Dec. 1969 */ + " %e %b ", /* 31 Dec */ + " %e %b. ", /* 31 Dec. */ + " %d-%b-%Y ", /* 31-Dec-1969 */ + " %d-%b ", /* 31-Dec */ + " %d/%b/%Y ", /* 31/Dec/1969 */ + " %d/%b ", /* 31/Dec */ + " %a %e %b %Y ", /* Wed 31 Dec 1969 */ + " %a %e %b. %Y ", /* Wed 31 Dec. 1969 */ + " %a %e %b ", /* Wed 31 Dec */ + " %a %e %b. ", /* Wed 31 Dec. */ + " %a %d-%b-%Y ", /* Wed 31-Dec-1969 */ + " %a %d-%b ", /* Wed 31-Dec */ + " %a %d/%b/%Y ", /* Wed 31/Dec/1969 */ + " %a %d/%b ", /* Wed 31/Dec */ + " %a. %e %b %Y ", /* Wed. 31 Dec 1969 */ + " %a. %e %b. %Y ", /* Wed. 31 Dec. 1969 */ + " %a. %e %b ", /* Wed. 31 Dec */ + " %a. %e %b. ", /* Wed. 31 Dec. */ + " %a. %d-%b-%Y ", /* Wed. 31-Dec-1969 */ + " %a. %d-%b ", /* Wed. 31-Dec */ + " %a. %d/%b/%Y ", /* Wed. 31/Dec/1969 */ + " %a. %d/%b ", /* Wed. 31/Dec */ + " %a, %e %b %Y ", /* Wed, 31 Dec 1969 */ + " %a, %e %b. %Y ", /* Wed, 31 Dec. 1969 */ + " %a, %e %b ", /* Wed, 31 Dec */ + " %a, %e %b. ", /* Wed, 31 Dec. */ + " %a, %d-%b-%Y ", /* Wed, 31-Dec-1969 */ + " %a, %d-%b ", /* Wed, 31-Dec */ + " %a, %d/%b/%Y ", /* Wed, 31/Dec/1969 */ + " %a, %d/%b ", /* Wed, 31/Dec */ + " %b %e, %Y ", /* Dec 31, 1969 */ + " %b. %e, %Y ", /* Dec. 31, 1969 */ + " %b %e ", /* Dec 31 */ + " %b. %e ", /* Dec. 31 */ + " %b-%d-%Y ", /* Dec-31-1969 */ + " %b-%d ", /* Dec-31 */ + " %b/%d/%Y ", /* Dec/31/1969 */ + " %b/%d ", /* Dec/31 */ + " %a %b %e, %Y ", /* Wed Dec 31, 1969 */ + " %a %b. %e, %Y ", /* Wed Dec. 31, 1969 */ + " %a %b %e ", /* Wed Dec 31 */ + " %a %b. %e ", /* Wed Dec. 31 */ + " %a %b-%d-%Y ", /* Wed Dec-31-1969 */ + " %a %b-%d ", /* Wed Dec-31 */ + " %a %b/%d/%Y ", /* Wed Dec/31/1969 */ + " %a %b/%d ", /* Wed Dec/31 */ + " %a. %b %e, %Y ", /* Wed. Dec 31, 1969 */ + " %a. %b. %e, %Y ", /* Wed. Dec. 31, 1969 */ + " %a. %b %e ", /* Wed. Dec 31 */ + " %a. %b. %e ", /* Wed. Dec. 31 */ + " %a. %b-%d-%Y ", /* Wed. Dec-31-1969 */ + " %a. %b-%d ", /* Wed. Dec-31 */ + " %a. %b/%d/%Y ", /* Wed. Dec/31/1969 */ + " %a. %b/%d ", /* Wed. Dec/31 */ + " %a, %b %e, %Y ", /* Wed, Dec 31, 1969 */ + " %a, %b. %e, %Y ", /* Wed, Dec. 31, 1969 */ + " %a, %b %e ", /* Wed, Dec 31 */ + " %a, %b. %e ", /* Wed, Dec. 31 */ + " %a, %b-%d-%Y ", /* Wed, Dec-31-1969 */ + " %a, %b-%d ", /* Wed, Dec-31 */ + " %a, %b/%d/%Y ", /* Wed, Dec/31/1969 */ + " %a, %b/%d ", /* Wed, Dec/31 */ + "", /* End of parsing formats */ + " %A ", /* Wednesday */ + " %A %e %b %Y ", /* Wednesday 31 Dec 1969 */ + " %A %e %b. %Y ", /* Wednesday 31 Dec. 1969 */ + " %A %e %b ", /* Wednesday 31 Dec */ + " %A %e %b. ", /* Wednesday 31 Dec. */ + " %A %d-%b-%Y ", /* Wednesday 31-Dec-1969 */ + " %A %d-%b ", /* Wednesday 31-Dec */ + " %A %d/%b/%Y ", /* Wednesday 31/Dec/1969 */ + " %A %d/%b ", /* Wednesday 31/Dec */ + " %A, %e %b %Y ", /* Wednesday, 31 Dec 1969 */ + " %A, %e %b. %Y ", /* Wednesday, 31 Dec. 1969 */ + " %A, %e %b ", /* Wednesday, 31 Dec */ + " %A, %e %b. ", /* Wednesday, 31 Dec. */ + " %A, %d-%b-%Y ", /* Wednesday, 31-Dec-1969 */ + " %A, %d-%b ", /* Wednesday, 31-Dec */ + " %A, %d/%b/%Y ", /* Wednesday, 31/Dec/1969 */ + " %A, %d/%b ", /* Wednesday, 31/Dec */ + " %A %b %e, %Y ", /* Wednesday Dec 31, 1969 */ + " %A %b. %e, %Y ", /* Wednesday Dec. 31, 1969 */ + " %A %b %e ", /* Wednesday Dec 31 */ + " %A %b. %e ", /* Wednesday Dec. 31 */ + " %A %b-%d-%Y ", /* Wednesday Dec-31-1969 */ + " %A %b-%d ", /* Wednesday Dec-31 */ + " %A %b/%d/%Y ", /* Wednesday Dec/31/1969 */ + " %A %b/%d ", /* Wednesday Dec/31 */ + " %A, %b %e, %Y ", /* Wednesday, Dec 31, 1969 */ + " %A, %b. %e, %Y ", /* Wednesday, Dec. 31, 1969 */ + " %A, %b %e ", /* Wednesday, Dec 31 */ + " %A, %b. %e ", /* Wednesday, Dec. 31 */ + " %A, %b-%d-%Y ", /* Wednesday, Dec-31-1969 */ + " %A, %b-%d ", /* Wednesday, Dec-31 */ + " %A, %b/%d/%Y ", /* Wednesday, Dec/31/1969 */ + " %A, %b/%d ", /* Wednesday, Dec/31 */ + " %e %B %Y ", /* 31 December 1969 */ + " %e %B ", /* 31 December */ + " %d-%B-%Y ", /* 31-December-1969 */ + " %d-%B ", /* 31-December */ + " %d/%B/%Y ", /* 31/December/1969 */ + " %d/%B ", /* 31/December */ + " %a %e %B %Y ", /* Wed 31 December 1969 */ + " %a %e %B ", /* Wed 31 December */ + " %a %d-%B-%Y ", /* Wed 31-December-1969 */ + " %a %d-%B ", /* Wed 31-December */ + " %a %d/%B/%Y ", /* Wed 31/December/1969 */ + " %a %d/%B ", /* Wed 31/December */ + " %a. %e %B %Y ", /* Wed. 31 December 1969 */ + " %a. %e %B ", /* Wed. 31 December */ + " %a. %d-%B-%Y ", /* Wed. 31-December-1969 */ + " %a. %d-%B ", /* Wed. 31-December */ + " %a. %d/%B/%Y ", /* Wed. 31/December/1969 */ + " %a. %d/%B ", /* Wed. 31/December */ + " %a, %e %B %Y ", /* Wed, 31 December 1969 */ + " %a, %e %B ", /* Wed, 31 December */ + " %a, %d-%B-%Y ", /* Wed, 31-December-1969 */ + " %a, %d-%B ", /* Wed, 31-December */ + " %a, %d/%B/%Y ", /* Wed, 31/December/1969 */ + " %a, %d/%B ", /* Wed, 31/December */ + " %B %e, %Y ", /* December 31, 1969 */ + " %B %e ", /* December 31 */ + " %B-%d-%Y ", /* December-31-1969 */ + " %B-%d ", /* December-31 */ + " %B/%d/%Y ", /* December/31/1969 */ + " %B/%d ", /* December/31 */ + " %a %B %e, %Y ", /* Wed December 31, 1969 */ + " %a %B %e ", /* Wed December 31 */ + " %a %B-%d-%Y ", /* Wed December-31-1969 */ + " %a %B-%d ", /* Wed December-31 */ + " %a %B/%d/%Y ", /* Wed December/31/1969 */ + " %a %B/%d ", /* Wed December/31 */ + " %a. %B %e, %Y ", /* Wed. December 31, 1969 */ + " %a. %B %e ", /* Wed. December 31 */ + " %a. %B-%d-%Y ", /* Wed. December-31-1969 */ + " %a. %B-%d ", /* Wed. December-31 */ + " %a. %B/%d/%Y ", /* Wed. December/31/1969 */ + " %a. %B/%d ", /* Wed. December/31 */ + " %a, %B %e, %Y ", /* Wed, December 31, 1969 */ + " %a, %B %e ", /* Wed, December 31 */ + " %a, %B-%d-%Y ", /* Wed, December-31-1969 */ + " %a, %B-%d ", /* Wed, December-31 */ + " %a, %B/%d/%Y ", /* Wed, December/31/1969 */ + " %a, %B/%d ", /* Wed, December/31 */ + " %A %e %B %Y ", /* Wednesday 31 December 1969 */ + " %A %e %B ", /* Wednesday 31 December */ + " %A %d-%B-%Y ", /* Wednesday 31-December-1969 */ + " %A %d-%B ", /* Wednesday 31-December */ + " %A %d/%B/%Y ", /* Wednesday 31/December/1969 */ + " %A %d/%B ", /* Wednesday 31/December */ + " %A, %e %B %Y ", /* Wednesday, 31 December 1969 */ + " %A, %e %B ", /* Wednesday, 31 December */ + " %A, %d-%B-%Y ", /* Wednesday, 31-December-1969 */ + " %A, %d-%B ", /* Wednesday, 31-December */ + " %A, %d/%B/%Y ", /* Wednesday, 31/December/1969 */ + " %A, %d/%B ", /* Wednesday, 31/December */ + " %A %B %e, %Y ", /* Wednesday December 31, 1969 */ + " %A %B %e ", /* Wednesday December 31 */ + " %A %B-%d-%Y ", /* Wednesday December-31-1969 */ + " %A %B-%d ", /* Wednesday December-31 */ + " %A %B/%d/%Y ", /* Wednesday December/31/1969 */ + " %A %B/%d ", /* Wednesday December/31 */ + " %A, %B %e, %Y ", /* Wednesday, December 31, 1969 */ + " %A, %B %e ", /* Wednesday, December 31 */ + " %A, %B-%d-%Y ", /* Wednesday, December-31-1969 */ + " %A, %B-%d ", /* Wednesday, December-31 */ + " %A, %B/%d/%Y ", /* Wednesday, December/31/1969 */ + " %A, %B/%d ", /* Wednesday, December/31 */ +}; + +static const char *FORMATS_TIME[] = { + " %I:%M:%S %p ", /* 7:00:01 PM */ + " %H:%M:%S ", /* 19:00:01 */ + " %I:%M %p ", /* 7:00 PM */ + " %H:%M ", /* 19:00 */ + " %I.%M.%S %p ", /* 7.00.01 PM */ + " %H.%M.%S ", /* 19.00.01 */ + " %I.%M %p ", /* 7.00 PM */ + " %H.%M ", /* 19.00 */ + " %I^%M^%S %p ", /* 70001 PM */ + " %H^%M^%S ", /* 190001 */ + " %I^%M %p ", /* 700 PM */ + " %H^%M ", /* 1900 */ +}; + +static const char *FORMATS_MISC[] = { + /* ISO 8601: 1969-12-31 delimited by "T" */ + " %Y-%m-%dT%H:%M:%S ", + " %Y-%m-%dT%H:%M ", + /* ISO 8601: 19691231 delimited by "T" */ + " %Y^%m^%dT%H^%M^%S ", + " %Y^%m^%dT%H^%M ", + /* 19691231 */ + " %Y^%m^%d %I^%M^%S %p ", + " %Y^%m^%d %H^%M^%S ", + " %Y^%m^%d %I^%M %p ", + " %Y^%m^%d %H^%M ", + /* Wed Dec 31 19:00:01 1969 */ + " %a %b %d %H:%M:%S %Y ", +}; + +#endif /* FORMATS_H_ */ diff --git a/src/i18n.h b/src/i18n.h new file mode 100644 index 0000000..4d8ee76 --- /dev/null +++ b/src/i18n.h @@ -0,0 +1,11 @@ +#ifdef ENABLE_NLS + +#include <libintl.h> + +#define _(msgid) gettext(msgid) + +#else + +#define _(msgid) (msgid) + +#endif diff --git a/src/local.mk b/src/local.mk index 0882580..1012945 100644 --- a/src/local.mk +++ b/src/local.mk @@ -1,4 +1,6 @@ -@_SOURCES += \ +atsign_SOURCES += \ %reldir%/datetime.c \ %reldir%/datetime.h \ + %reldir%/formats.h \ + %reldir%/i18n.h \ %reldir%/main.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 P. J. McDermott + * Copyright (C) 2021, 2022 P. J. McDermott * * This file is part of @ * @@ -17,6 +17,12 @@ * along with @. If not, see <http://www.gnu.org/licenses/>. */ +#include "config.h" + +#ifdef ENABLE_NLS +#include <locale.h> +#endif + #include <errno.h> #include <stdbool.h> #include <stdio.h> @@ -24,8 +30,8 @@ #include <string.h> #include <time.h> #include <unistd.h> -#include "config.h" #include "datetime.h" +#include "i18n.h" #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG #include <getopt.h> @@ -34,7 +40,7 @@ extern const char *PACKAGE_VERSION_GIT; #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG -struct option LONGOPTS_[] = { +static const struct option LONGOPTS_[] = { { .name = "list-formats", .has_arg = 0, @@ -59,63 +65,83 @@ struct option LONGOPTS_[] = { static void _print_usage(FILE *stream, const char *program_name) { - fprintf(stream, "Usage: %s [date]time\n", program_name); + fprintf(stream, _("Usage: %s [date]time\n"), program_name); } static void _print_help(const char *program_name) { _print_usage(stdout, program_name); - puts("Options:"); #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG - puts("\t-F, --list-formats List all supported date and time formats"); - puts("\t-h, --help Show this help information"); - puts("\t-V, --version Show version information"); + puts(_("Options:\n" + " -F, --list-formats List all supported date and time formats\n" + " -h, --help Show this help information\n" + " -V, --version Show version information")); #else - puts("\t-F List all supported date and time formats"); - puts("\t-h Show this help information"); - puts("\t-V Show version information"); + puts(_("Options:\n" + " -F List all supported date and time formats\n" + " -h Show this help information\n" + " -V Show version information")); #endif } -static void -_list_formats(void) +#if defined(ENABLE_TESTS) && ENABLE_TESTS +static struct tm * +_set_now(char buf[]) { - time_t tim; - struct tm *tm; - char *out; - size_t buf_sz; - char *buf; - size_t i; + struct tm tm; + time_t t; + struct tm *tm_p; - tim = time(NULL); - tm = localtime(&tim); + sscanf(buf, "%04d-%02d-%02d %02d:%02d:%02d", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + tm.tm_year -= 1900; + tm.tm_mon -= 1; + tm.tm_wday = -1; + tm.tm_yday = -1; + tm.tm_isdst = -1; - puts("Time formats:"); + t = mktime(&tm); + tm_p = localtime(&t); + + return tm_p; +} +#endif + +static void +_list_formats(const struct tm *tm) +{ + char *out; + size_t buf_sz; + char *buf; + size_t i; + + puts(_("Time formats:")); i = 0; while (datetime_strftime_time(tm, &out, &buf_sz, &buf, &i) > 0) { if (out[0] == '\0') { continue; } - printf(" * %s\n", out); + printf(_(" * %s\n"), out); } - puts("Date formats:"); + puts(_("Date formats:")); i = 0; while (datetime_strftime_date(tm, &out, &buf_sz, &buf, &i) > 0) { if (out[0] == '\0') { continue; } - printf(" * %s\n", out); + printf(_(" * %s\n"), out); } - puts("Additional formats:"); + puts(_("Additional formats:")); i = 0; while (datetime_strftime_misc(tm, &out, &buf_sz, &buf, &i) > 0) { if (out[0] == '\0') { continue; } - printf(" * %s\n", out); + printf(_(" * %s\n"), out); } } @@ -123,13 +149,12 @@ static void _print_version(void) { printf("@ (atsign) %s%s\n", PACKAGE_VERSION, PACKAGE_VERSION_GIT); - puts("Copyright (C) 2021 P. J. McDermott"); - puts("License GPLv3+: GNU GPL version 3 or later " - "<http://gnu.org/licenses/gpl.html>."); - puts("This is free software: you are free to change and redistribute " - "it."); - puts("There is NO WARRANTY, to the extent permitted by law.\n"); - printf("Please report bugs to <%s>.\n", PACKAGE_BUGREPORT); + puts(_("Copyright (C) 2021, 2022 P. J. McDermott\n" + "License GPLv3+: GNU GPL version 3 or later " + "<http://gnu.org/licenses/gpl.html>.\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n")); + printf(_("Please report bugs to <%s>.\n"), PACKAGE_BUGREPORT); } static char * @@ -148,7 +173,7 @@ _concat_args(int argc, char * const argv[]) buf = calloc(buf_l, sizeof(*buf)); if (buf == NULL) { - fprintf(stderr, "Failed to allocate buffer: %s\n", + fprintf(stderr, _("Failed to allocate buffer: %s\n"), strerror(errno)); return NULL; } @@ -168,37 +193,76 @@ _concat_args(int argc, char * const argv[]) int main(int argc, char * const argv[]) { - int opt; - char *buf; - time_t arg; - time_t now; - time_t dif; +#if defined(ENABLE_TESTS) && ENABLE_TESTS + bool dbg; +#endif + bool fmt; + int opt; + char *buf; + time_t now; + struct tm *now_tm; + struct tm arg_tm; + time_t arg; + double dif; +#ifdef ENABLE_NLS + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET + bind_textdomain_codeset(PACKAGE, "UTF-8"); +#endif + textdomain(PACKAGE); + setlocale(LC_ALL, ""); +#endif + +#if defined(ENABLE_TESTS) && ENABLE_TESTS + dbg = false; +#endif + fmt = false; optind = 1; opterr = 0; + now = time(NULL); + now_tm = localtime(&now); + #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG - while ((opt = getopt_long(argc, argv, "FhV", LONGOPTS_, NULL)) > 0) { +#if defined(ENABLE_TESTS) && ENABLE_TESTS + while ((opt = getopt_long(argc, argv, "FhVd:", LONGOPTS_, NULL)) > 0) { +#else + while ((opt = getopt_long(argc, argv, "FhV:", LONGOPTS_, NULL)) > 0) { +#endif +#else +#if defined(ENABLE_TESTS) && ENABLE_TESTS + while ((opt = getopt(argc, argv, "FhVd:")) > 0) { #else - while ((opt = getopt(argc, argv, "FhV")) > 0) { + while ((opt = getopt(argc, argv, "FhV:")) > 0) { +#endif #endif switch (opt) { case 'F': - _list_formats(); - return EXIT_SUCCESS; + fmt = true; + break; case 'h': _print_help(argv[0]); return EXIT_SUCCESS; case 'V': _print_version(); return EXIT_SUCCESS; +#if defined(ENABLE_TESTS) && ENABLE_TESTS + case 'd': + dbg = true; + now_tm = _set_now(optarg); + if (now_tm == NULL) { + return EXIT_FAILURE; + } + break; +#endif default: _print_usage(stderr, argv[0]); #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG - fprintf(stderr, "Try '%s --help' for more " - "information.\n", argv[0]); + fprintf(stderr, _("Try '%s --help' for more " + "information.\n"), argv[0]); #else - fprintf(stderr, "Try '%s -h' for more " - "information.\n", argv[0]); + fprintf(stderr, _("Try '%s -h' for more " + "information.\n"), argv[0]); #endif return EXIT_FAILURE; } @@ -206,34 +270,52 @@ main(int argc, char * const argv[]) argc -= optind; argv += optind; + if (fmt == true) { + _list_formats(now_tm); + return EXIT_SUCCESS; + } + buf = _concat_args(argc, argv); if (buf == NULL) { return EXIT_FAILURE; } - if (datetime_parse(buf, &arg) < 0) { + if (datetime_parse(now_tm, buf, &arg_tm, &arg) < 0) { free(buf); return EXIT_FAILURE; } free(buf); - time(&now); - dif = arg - now; +#if defined(ENABLE_TESTS) && ENABLE_TESTS + if (dbg == true) { + printf("%04d-%02d-%02d %02d:%02d:%02d\n", + arg_tm.tm_year + 1900, + arg_tm.tm_mon + 1, + arg_tm.tm_mday, + arg_tm.tm_hour, + arg_tm.tm_min, + arg_tm.tm_sec); + return EXIT_SUCCESS; + } +#endif + + now = time(NULL); + dif = difftime(arg, now); if (dif >= 1000 * 24 * 60 * 60) { - fputs("Date too far in the future\n", stderr); + fputs(_("Date too far in the future\n"), stderr); return EXIT_FAILURE; } setvbuf(stdout, NULL, _IONBF, 0); - while (arg > now) { + while (dif > 0) { printf("\r%03d:%02d:%02d:%02d", (int) dif / 60 / 60 / 24, (int) dif / 60 / 60 % 24, (int) dif / 60 % 60, (int) dif % 60); sleep(1); - time(&now); - dif = arg - now; + now = time(NULL); + dif = difftime(arg, now); } printf("\r000:00:00:00\n"); |