/*
* 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 .
*/
#include "config.h"
#ifdef ENABLE_NLS
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include "datetime.h"
#include "i18n.h"
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
#include
#endif
extern const char *PACKAGE_VERSION_GIT;
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
static const struct option LONGOPTS_[] = {
{
.name = "list-formats",
.has_arg = 0,
.flag = NULL,
.val = 'F',
},
{
.name = "help",
.has_arg = 0,
.flag = NULL,
.val = 'h',
},
{
.name = "version",
.has_arg = 0,
.flag = NULL,
.val = 'V',
},
};
#endif
static void
_print_usage(FILE *stream, const char *program_name)
{
fprintf(stream, _("Usage: %s [date]time\n"), program_name);
}
static void
_print_help(const char *program_name)
{
_print_usage(stdout, program_name);
#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
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(_("Options:\n"
" -F List all supported date and time formats\n"
" -h Show this help information\n"
" -V Show version information"));
#endif
}
#if defined(ENABLE_TESTS) && ENABLE_TESTS
static struct tm *
_set_now(char buf[])
{
struct tm tm;
time_t t;
struct tm *tm_p;
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;
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);
}
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);
}
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);
}
}
static void
_print_version(void)
{
printf("@ (atsign) %s%s\n", PACKAGE_VERSION, PACKAGE_VERSION_GIT);
puts(_("Copyright (C) 2021, 2022 P. J. McDermott\n"
"License GPLv3+: GNU GPL version 3 or later "
".\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 *
_concat_args(int argc, char * const argv[])
{
int buf_l;
int i;
char *buf;
int buf_i;
int j;
buf_l = 0;
for (i = 0; i < argc; ++i) {
buf_l += strlen(argv[i]) + 1;
}
buf = calloc(buf_l, sizeof(*buf));
if (buf == NULL) {
fprintf(stderr, _("Failed to allocate buffer: %s\n"),
strerror(errno));
return NULL;
}
buf_i = 0;
for (i = 0; i < argc; ++i) {
for (j = 0; argv[i][j] != '\0'; ++j) {
buf[buf_i++] = argv[i][j];
}
buf[buf_i++] = ' ';
}
buf[--buf_i] = '\0';
return buf;
}
int
main(int argc, char * const argv[])
{
#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
#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) {
#endif
#endif
switch (opt) {
case 'F':
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]);
#else
fprintf(stderr, _("Try '%s -h' for more "
"information.\n"), argv[0]);
#endif
return EXIT_FAILURE;
}
}
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(now_tm, buf, &arg_tm, &arg) < 0) {
free(buf);
return EXIT_FAILURE;
}
free(buf);
#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);
return EXIT_FAILURE;
}
setvbuf(stdout, NULL, _IONBF, 0);
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);
now = time(NULL);
dif = difftime(arg, now);
}
printf("\r000:00:00:00\n");
return EXIT_SUCCESS;
}