From 50d117cb76883735faa8864c4978b5819cb167bd Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Tue, 22 Mar 2022 18:46:20 -0400 Subject: datetime, formats: Conform to strptime() space req --- diff --git a/TODO b/TODO index 56d1f3e..b8f4d77 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,3 @@ -spaces between specifiers in strptime() formats -- strftime()? - format printing: copy format to another buffer, skipping certain char - parsing: copy format one char at a time, changing placeholder to space test with other libc's (newlib? musl? fbsd? obsd?) `./@ 7:00 PM` doesn't work with musl strptime() test other times diff --git a/src/datetime.c b/src/datetime.c index 8f5051d..f7cdb89 100644 --- a/src/datetime.c +++ b/src/datetime.c @@ -43,6 +43,31 @@ static struct tm DATETIME_EPOCH_ = { }; 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) { tm->tm_year = INT_MIN; @@ -142,7 +167,7 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec) sec = *arg_sec; /* GCC is dumb. */ got = false; for (t = 0; t < sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0]) ; ++t) { - memcpy(fmt_buf, FORMATS_TIME[t], time_fmt_len + 1); + _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') { @@ -159,11 +184,11 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec) if (FORMATS_DATE[d][0] == '\0') { break; } - memcpy(fmt_buf, FORMATS_DATE[d], date_fmt_len + 1); + _datetime_buf_cpy_p(fmt_buf, FORMATS_DATE[d]); for (t = 0; t < sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0]); ++t) { - memcpy(fmt_buf + date_fmt_len, FORMATS_TIME[t], - time_fmt_len + 1); + _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') { @@ -178,14 +203,14 @@ datetime_parse(struct tm *now_tm, const char *input, time_t *arg_sec) } } for (t = 0; t < sizeof(FORMATS_TIME) / sizeof(FORMATS_TIME[0]); ++t) { - memcpy(fmt_buf, FORMATS_TIME[t], time_fmt_len + 1); + _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; } - memcpy(fmt_buf + time_fmt_len, FORMATS_DATE[d], - date_fmt_len + 1); + _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') { @@ -200,7 +225,7 @@ 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) { - memcpy(fmt_buf, FORMATS_MISC[d], misc_fmt_len + 1); + _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') { @@ -280,10 +305,12 @@ _datetime_strftime(const char *fmts[], const struct tm *tm, char **out, ++*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)); diff --git a/src/formats.h b/src/formats.h index f4caccd..262912e 100644 --- a/src/formats.h +++ b/src/formats.h @@ -25,6 +25,16 @@ * * 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[] = { @@ -220,10 +230,10 @@ static const char *FORMATS_TIME[] = { " %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 */ + " %I^%M^%S %p ", /* 70001 PM */ + " %H^%M^%S ", /* 190001 */ + " %I^%M %p ", /* 700 PM */ + " %H^%M ", /* 1900 */ }; static const char *FORMATS_MISC[] = { @@ -231,13 +241,13 @@ static const char *FORMATS_MISC[] = { " %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 ", }; -- cgit v0.9.1