From e130ed9b3a982b36b75d4fbb225ad1fe8e16a7de Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Tue, 31 Aug 2021 20:54:28 -0400 Subject: Add option to list supported formats --- diff --git a/configure.ac b/configure.ac index cb2f4da..131540e 100644 --- a/configure.ac +++ b/configure.ac @@ -38,8 +38,8 @@ test -d "${srcdir}/.git" || CFLAGS="${save_CFLAGS}" funcs_missing=false AC_CHECK_FUNCS( [\ - calloc fprintf fputs free localtime mktime printf setvbuf \ - sleep strerror strlen strptime time + calloc fprintf fputs free isspace localtime mktime printf \ + realloc setvbuf sleep strerror strlen strftime strptime time ], [], [funcs_missing=true]) diff --git a/src/datetime.c b/src/datetime.c index 0056017..1a3bbba 100644 --- a/src/datetime.c +++ b/src/datetime.c @@ -19,8 +19,10 @@ #define _XOPEN_SOURCE +#include #include #include +#include #include #include #include @@ -87,16 +89,16 @@ static const char *DATETIME_TIME_FMTS_[] = { }; 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 ", + " %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 ", + " %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 ", + " %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 ", }; @@ -244,3 +246,109 @@ datetime_parse(const char *input, time_t *arg_sec) fputs("Unknown date format\n", stderr); return -1; } + +static void +_datetime_copy_trim_fmt(char *buf, const char *fmt) +{ + int buf_i; + int l; + bool was_space; + int i; + + buf_i = 0; + l = strlen(fmt); + was_space = true; + for (i = 0; i < l; ++i) { + if (isspace(fmt[i]) != 0) { + if (was_space == false) { + buf[buf_i++] = ' '; + was_space = true; + } + } else { + buf[buf_i++] = fmt[i]; + was_space = false; + } + } + if (buf_i > 0) { + buf[buf_i - 1] = '\0'; + } else { + buf[0] = '\0'; + } +} + +static int +_datetime_strftime(const char *fmts[], const struct tm *tm, char **out, + size_t *out_sz, char **buf, size_t *i) +{ + if (*buf == NULL) { + *buf = calloc(strlen(fmts[0]), sizeof(**buf)); + if (*buf == NULL) { + fprintf(stderr, "Failed to allocate buffer: %s\n", + strerror(errno)); + return -1; + } + } + if (*out == NULL) { + *out_sz = strlen(fmts[0]); + *out = calloc(*out_sz, sizeof(**out)); + if (*out == NULL) { + fprintf(stderr, "Failed to allocate buffer: %s\n", + strerror(errno)); + return -1; + } + } + + *buf[0] = '\0'; + while (*buf[0] == '\0') { + _datetime_copy_trim_fmt(*buf, fmts[*i]); + ++*i; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + while (strftime(*out, *out_sz, *buf, tm) == 0) { +#pragma GCC diagnostic pop + ++*out_sz; + *out = realloc(*out, *out_sz * sizeof(**out)); + if (*out == NULL) { + fprintf(stderr, "Failed to allocate buffer: %s\n", + strerror(errno)); + return -1; + } + } + + return 1; +} + +int +datetime_strftime_date(const struct tm *tm, char **out, size_t *out_sz, + char **buf, size_t *i) +{ + if (*i >= sizeof(DATETIME_DATE_FMTS_) / sizeof(DATETIME_DATE_FMTS_[0])){ + return 0; + } + + return _datetime_strftime(DATETIME_DATE_FMTS_, tm, out, out_sz, buf, i); +} + +int +datetime_strftime_time(const struct tm *tm, char **out, size_t *out_sz, + char **buf, size_t *i) +{ + if (*i >= sizeof(DATETIME_TIME_FMTS_) / sizeof(DATETIME_TIME_FMTS_[0])){ + return 0; + } + + return _datetime_strftime(DATETIME_TIME_FMTS_, tm, out, out_sz, buf, i); +} + +int +datetime_strftime_misc(const struct tm *tm, char **out, size_t *out_sz, + char **buf, size_t *i) +{ + if (*i >= sizeof(DATETIME_MISC_FMTS_) / sizeof(DATETIME_MISC_FMTS_[0])){ + return 0; + } + + return _datetime_strftime(DATETIME_MISC_FMTS_, tm, out, out_sz, buf, i); +} diff --git a/src/datetime.h b/src/datetime.h index 49a86a5..edff8f7 100644 --- a/src/datetime.h +++ b/src/datetime.h @@ -25,4 +25,16 @@ int datetime_parse(const char *input, time_t *arg_sec); +int +datetime_strftime_date(const struct tm *tm, char **out, size_t *out_sz, + char **buf, size_t *i); + +int +datetime_strftime_time(const struct tm *tm, char **out, size_t *out_sz, + char **buf, size_t *i); + +int +datetime_strftime_misc(const struct tm *tm, char **out, size_t *out_sz, + char **buf, size_t *i); + #endif /* DATETIME_H_ */ diff --git a/src/main.c b/src/main.c index 81096a5..f450281 100644 --- a/src/main.c +++ b/src/main.c @@ -36,6 +36,12 @@ extern const char *PACKAGE_VERSION_GIT; #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG struct option LONGOPTS_[] = { { + .name = "list-formats", + .has_arg = 0, + .flag = NULL, + .val = 'F', + }, + { .name = "help", .has_arg = 0, .flag = NULL, @@ -62,15 +68,76 @@ _print_help(const char *program_name) _print_usage(stdout, program_name); puts("Options:"); #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG - puts("\t-h, --help Show this help information"); - puts("\t-V, --version Show version information"); + 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 } static void +_list_formats(void) +{ + time_t tim; + struct tm *tm; + char *out; + size_t out_sz; + char *buf; + size_t i; + + tim = time(NULL); + tm = localtime(&tim); + + puts("Time formats:"); + out = NULL; + out_sz = 0; + buf = NULL; + i = 0; + while (datetime_strftime_time(tm, &out, &out_sz, &buf, &i) > 0) { + printf(" * %s\n", out); + } + if (out != NULL) { + free(out); + } + if (buf != NULL) { + free(buf); + } + + puts("Date formats:"); + out = NULL; + out_sz = 0; + buf = NULL; + i = 0; + while (datetime_strftime_date(tm, &out, &out_sz, &buf, &i) > 0) { + printf(" * %s\n", out); + } + if (out != NULL) { + free(out); + } + if (buf != NULL) { + free(buf); + } + + puts("Additional formats:"); + out = NULL; + out_sz = 0; + buf = NULL; + i = 0; + while (datetime_strftime_misc(tm, &out, &out_sz, &buf, &i) > 0) { + printf(" * %s\n", out); + } + if (out != NULL) { + free(out); + } + if (buf != NULL) { + free(buf); + } +} + +static void _print_version(void) { printf("@ (atsign) %s%s\n", PACKAGE_VERSION, PACKAGE_VERSION_GIT); @@ -128,11 +195,14 @@ main(int argc, char * const argv[]) optind = 1; opterr = 0; #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG - while ((opt = getopt_long(argc, argv, "hV", LONGOPTS_, NULL)) > 0) { + while ((opt = getopt_long(argc, argv, "FhV", LONGOPTS_, NULL)) > 0) { #else - while ((opt = getopt(argc, argv, "hV")) > 0) { + while ((opt = getopt(argc, argv, "FhV")) > 0) { #endif switch (opt) { + case 'F': + _list_formats(); + return EXIT_SUCCESS; case 'h': _print_help(argv[0]); return EXIT_SUCCESS; -- cgit v0.9.1