summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/datetime.c130
-rw-r--r--src/datetime.h6
-rw-r--r--src/formats.h2
-rw-r--r--src/i18n.h11
-rw-r--r--src/local.mk3
-rw-r--r--src/main.c84
6 files changed, 131 insertions, 105 deletions
diff --git a/src/datetime.c b/src/datetime.c
index f7cdb89..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>
@@ -29,18 +32,7 @@
#include <time.h>
#include "datetime.h"
#include "formats.h"
-
-static struct tm DATETIME_EPOCH_ = {
- .tm_sec = 0,
- .tm_min = 0,
- .tm_hour = 0,
- .tm_mday = 1,
- .tm_mon = 0,
- .tm_year = 70,
- .tm_wday = 4,
- .tm_yday = 0,
- .tm_isdst = -1,
-};
+#include "i18n.h"
static void
_datetime_buf_cpy_p(char *dst, const char *src)
@@ -83,7 +75,13 @@ _datetime_reset_tm(struct tm *tm)
static void
_datetime_normalize(struct tm *now_tm, struct tm *arg_tm, time_t *arg_sec)
{
- 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;
@@ -95,36 +93,44 @@ _datetime_normalize(struct tm *now_tm, struct tm *arg_tm, time_t *arg_sec)
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;
+ /* 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. */
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;
@@ -136,7 +142,8 @@ _datetime_normalize(struct tm *now_tm, struct tm *arg_tm, time_t *arg_sec)
}
int
-datetime_parse(struct tm *now_tm, 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;
@@ -146,7 +153,6 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec)
size_t d;
size_t t;
char *end;
- struct tm arg_tm;
time_t sec;
bool got;
@@ -159,20 +165,20 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec)
}
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;
}
sec = *arg_sec; /* GCC is dumb. */
got = false;
- for (t = 0; t < sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0]) ; ++t) {
+ 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);
+ _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 (datetime_diff_epoch(*arg_sec) >= 0) {
+ _datetime_normalize(now_tm, arg_tm, arg_sec);
+ if (arg_tm->tm_year >= 0) {
free(fmt_buf);
return 0;
}
@@ -189,11 +195,11 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec)
++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);
+ _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 (datetime_diff_epoch(*arg_sec) >= 0) {
+ _datetime_normalize(now_tm, arg_tm, arg_sec);
+ if (arg_tm->tm_year >= 0) {
free(fmt_buf);
return 0;
}
@@ -211,11 +217,11 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec)
}
_datetime_buf_cpy_p(fmt_buf + time_fmt_len,
FORMATS_DATE[d]);
- _datetime_reset_tm(&arg_tm);
- end = strptime(input, fmt_buf, &arg_tm);
+ _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 (datetime_diff_epoch(*arg_sec) >= 0) {
+ _datetime_normalize(now_tm, arg_tm, arg_sec);
+ if (arg_tm->tm_year >= 0) {
free(fmt_buf);
return 0;
}
@@ -224,13 +230,13 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec)
}
}
}
- for (d = 0; d < sizeof(FORMATS_MISC) / sizeof(FORMATS_MISC[0]) ; ++d) {
+ 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);
+ _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 (datetime_diff_epoch(*arg_sec) >= 0) {
+ _datetime_normalize(now_tm, arg_tm, arg_sec);
+ if (arg_tm->tm_year >= 0) {
free(fmt_buf);
return 0;
}
@@ -244,7 +250,7 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec)
free(fmt_buf);
return 0;
} else {
- fputs("Unknown date format\n", stderr);
+ fputs(_("Unknown date format\n"), stderr);
free(fmt_buf);
return -1;
}
@@ -289,13 +295,13 @@ _datetime_strftime(const char *fmts[], const struct tm *tm, char **out,
*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;
}
@@ -315,7 +321,7 @@ _datetime_strftime(const char *fmts[], const struct tm *tm, char **out,
++*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;
}
@@ -324,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;
}
@@ -386,13 +392,3 @@ datetime_strftime_misc(const struct tm *tm, char **out, size_t *buf_sz,
return _datetime_strftime(FORMATS_MISC, tm, out, buf_sz, buf, i);
}
-
-double
-datetime_diff_epoch(time_t t)
-{
- time_t epoch;
-
- epoch = timegm(&DATETIME_EPOCH_);
-
- return difftime(t, epoch);
-}
diff --git a/src/datetime.h b/src/datetime.h
index e225b52..1094cf8 100644
--- a/src/datetime.h
+++ b/src/datetime.h
@@ -23,7 +23,8 @@
#include <time.h>
int
-datetime_parse(struct tm *now_tm, 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,
@@ -37,7 +38,4 @@ int
datetime_strftime_misc(const struct tm *tm, char **out, size_t *out_sz,
char **buf, size_t *i);
-double
-datetime_diff_epoch(time_t t);
-
#endif /* DATETIME_H_ */
diff --git a/src/formats.h b/src/formats.h
index 262912e..c88f731 100644
--- a/src/formats.h
+++ b/src/formats.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021-2022 P. J. McDermott
+ * Copyright (C) 2021, 2022 P. J. McDermott
*
* This file is part of @
*
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 9992269..1012945 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -1,5 +1,6 @@
-@_SOURCES += \
+atsign_SOURCES += \
%reldir%/datetime.c \
%reldir%/datetime.h \
%reldir%/formats.h \
+ %reldir%/i18n.h \
%reldir%/main.c
diff --git a/src/main.c b/src/main.c
index 01a0a4c..0ff76c9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -17,16 +17,21 @@
* along with @. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "config.h"
+
+#ifdef ENABLE_NLS
+#include <locale.h>
+#endif
+
#include <errno.h>
-#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#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>
@@ -60,22 +65,23 @@ static const 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
}
@@ -111,31 +117,31 @@ _list_formats(const struct tm *tm)
char *buf;
size_t i;
- puts("Time formats:");
+ 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);
}
}
@@ -143,13 +149,12 @@ static void
_print_version(void)
{
printf("@ (atsign) %s%s\n", PACKAGE_VERSION, PACKAGE_VERSION_GIT);
- puts("Copyright (C) 2021-2022 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 *
@@ -168,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;
}
@@ -196,9 +201,19 @@ main(int argc, char * const argv[])
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
@@ -243,11 +258,11 @@ main(int argc, char * const argv[])
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;
}
@@ -265,7 +280,7 @@ main(int argc, char * const argv[])
return EXIT_FAILURE;
}
- if (datetime_parse(now_tm, buf, &arg) < 0) {
+ if (datetime_parse(now_tm, buf, &arg_tm, &arg) < 0) {
free(buf);
return EXIT_FAILURE;
}
@@ -273,8 +288,13 @@ main(int argc, char * const argv[])
#if defined(ENABLE_TESTS) && ENABLE_TESTS
if (dbg == true) {
- dif = datetime_diff_epoch(arg);
- printf("%" PRId64 "\n", (int64_t) dif);
+ 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
@@ -282,7 +302,7 @@ main(int argc, char * const argv[])
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;
}