/* * 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 #include #include #include #include #include #include #include #include "config.h" #include "datetime.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); 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"); #else puts("\t-F List all supported date and time formats"); puts("\t-h Show this help information"); puts("\t-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"); puts("License GPLv3+: GNU GPL version 3 or later " "."); 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); } 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; time_t arg; double dif; #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) < 0) { free(buf); return EXIT_FAILURE; } free(buf); #if defined(ENABLE_TESTS) && ENABLE_TESTS if (dbg == true) { dif = datetime_diff_epoch(arg); printf("%" PRId64 "\n", (int64_t) dif); 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; }