logo

utils-std

Collection of commonly available Unix tools
commit: 9b58ebce2a60f546ee611ba5463734e2e2a649f5
parent 29b47ba7bcf132ab8d79686fb96e980df8f96eeb
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Fri, 26 Jul 2024 09:43:38 +0200

lib/iso_parse: Add `struct tm *` argument to avoid NULL pointers

Diffstat:

Mcmd/date.c63++++++++++++++++++++++++++-------------------------------------
Mcmd/touch.c13++++++++++---
Mlib/iso_parse.c45+++++++++++++++++----------------------------
Mlib/iso_parse.h2+-
4 files changed, 54 insertions(+), 69 deletions(-)

diff --git a/cmd/date.c b/cmd/date.c @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { char outstr[BUFSIZ]; - struct tm *tm; + struct tm tm; struct timespec tp = { .tv_sec = 0, .tv_nsec = 0, @@ -45,7 +45,6 @@ main(int argc, char *argv[]) int c; bool jflag = false; bool settime = false; - bool custom_datetime = false; errno = 0; setlocale(LC_ALL, ""); @@ -74,8 +73,7 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } - tm = iso_parse(optarg, &tp.tv_nsec, &errstr); - custom_datetime = true; + iso_parse(optarg, &tm, &tp.tv_nsec, &errstr); dflag = 1; if(errstr != NULL) { @@ -84,7 +82,7 @@ main(int argc, char *argv[]) } assert(errno == 0); - tp.tv_sec = mktime_tz(tm); + tp.tv_sec = mktime_tz(&tm); errno = 0; break; @@ -122,9 +120,26 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; + if(uflag) + { + if(gmtime_r(&tp.tv_sec, &tm) == NULL) + { + perror("date: gmtime_r"); + exit(EXIT_FAILURE); + } + } + else + { + if(localtime_r(&tp.tv_sec, &tm) == NULL) + { + perror("date: localtime_r"); + exit(EXIT_FAILURE); + } + } + if(argc > 0 && input_format != NULL) { - char *res = strptime(argv[0], input_format, tm); + char *res = strptime(argv[0], input_format, &tm); if(res == NULL) { @@ -135,10 +150,8 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } - custom_datetime = true; - assert(errno == 0); - tp.tv_sec = mktime_tz(tm); + tp.tv_sec = mktime_tz(&tm); tp.tv_nsec = 0; errno = 0; @@ -149,7 +162,7 @@ main(int argc, char *argv[]) if(input_format == NULL && argc > 0 && *argv && **argv != '+') { char *fmt = "%m%d%H%M"; - char *s = strptime(*argv, fmt, tm); + char *s = strptime(argv[0], fmt, &tm); if(s == NULL) { fprintf(stderr, "date: strptime(\"%s\", \"%s\", …) returned NULL\n", *argv, fmt); @@ -172,7 +185,7 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } - s = strptime(s, fmt, tm); + s = strptime(s, fmt, &tm); if(s == NULL) { fprintf(stderr, "date: strptime(\"%s\", \"%s\", …) returned NULL\n", *argv, fmt); @@ -180,10 +193,8 @@ main(int argc, char *argv[]) } } - custom_datetime = true; - assert(errno == 0); - tp.tv_sec = mktime(tm); + tp.tv_sec = mktime(&tm); tp.tv_nsec = 0; errno = 0; @@ -192,28 +203,6 @@ main(int argc, char *argv[]) settime = true; } - if(!custom_datetime) - { - if(uflag) - { - tm = gmtime(&tp.tv_sec); - if(tm == NULL) - { - perror("date: gmtime"); - exit(EXIT_FAILURE); - } - } - else - { - tm = localtime(&tp.tv_sec); - if(tm == NULL) - { - perror("date: localtime"); - exit(EXIT_FAILURE); - } - } - } - if(settime && !jflag) { if(clock_settime(CLOCK_REALTIME, &tp) != 0) @@ -236,7 +225,7 @@ main(int argc, char *argv[]) } errno = 0; - if(strftime(outstr, sizeof(outstr), format, tm) == 0 && errno != 0) + if(strftime(outstr, sizeof(outstr), format, &tm) == 0 && errno != 0) { perror("date: strftime"); exit(EXIT_FAILURE); diff --git a/cmd/touch.c b/cmd/touch.c @@ -158,14 +158,21 @@ main(int argc, char *argv[]) case 'd': { long nsec = 0; - struct tm *iso_res = iso_parse(optarg, &nsec, &errstr); + struct tm iso_res; + + char *s = iso_parse(optarg, &iso_res, &nsec, &errstr); if(errstr != NULL) { fprintf(stderr, "touch: iso_parse(\"%s\", …): %s\n", optarg, errstr); exit(EXIT_FAILURE); } - assert(iso_res != NULL); - target.tv_sec = mktime_tz(iso_res); + if(s == NULL) + { + fprintf(stderr, "touch: iso_parse(\"%s\", …) returned NULL\n", optarg); + exit(EXIT_FAILURE); + } + + target.tv_sec = mktime_tz(&iso_res); target.tv_nsec = nsec; break; } diff --git a/lib/iso_parse.c b/lib/iso_parse.c @@ -20,43 +20,32 @@ // Sets errstr on failure // YYYY-MM-DD[T ]hh:mm:SS([,\.]frac)?(Z|[+\-]hh:?mm)? -struct tm * -iso_parse(char *arg, long *nsec, char **errstr) +char * +iso_parse(char *arg, struct tm *time, long *nsec, char **errstr) { - // Need fractional seconds and GMT-offset - // struct timespec provides nanoseconds but not GMT-offset - // struct tm provides GMT-offset but not nanoseconds - static struct tm res = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 0, - .tm_hour = 0, - .tm_min = 0, - .tm_sec = 0, - .tm_isdst = -1, // unknown if DST is in effect - .tm_gmtoff = 0, - .tm_zone = NULL, - }; - // For Alpine's abuild compatibility if(arg[0] == '@') { arg++; assert(errno == 0); - time_t now = strtol(arg, NULL, 10); + char *endptr = NULL; + time_t now = strtol(arg, &endptr, 10); if(errno != 0) { *errstr = strerror(errno); errno = 0; return NULL; } + nsec = 0; - return gmtime(&now); + gmtime_r(&now, time); + + return endptr; } // No %F in POSIX - char *s = strptime(arg, "%Y-%m-%d", &res); + char *s = strptime(arg, "%Y-%m-%d", time); if(s == NULL) { @@ -72,7 +61,7 @@ iso_parse(char *arg, long *nsec, char **errstr) } s++; - s = strptime(s, "%H:%M:%S", &res); + s = strptime(s, "%H:%M:%S", time); if(s == NULL) { *errstr = "strptime(…, \"%H:%M:%S\", …) returned NULL"; @@ -117,8 +106,8 @@ iso_parse(char *arg, long *nsec, char **errstr) { if(s[0] == 'Z' && s[1] == '\0') { - res.tm_gmtoff = 0; - res.tm_zone = "UTC"; + time->tm_gmtoff = 0; + time->tm_zone = "UTC"; } else { @@ -137,7 +126,7 @@ iso_parse(char *arg, long *nsec, char **errstr) if(isdigit(o[0]) && isdigit(o[1])) { - res.tm_gmtoff = (o[0] - '0') * 36000 + (o[1] - '0') * 3600; + time->tm_gmtoff = (o[0] - '0') * 36000 + (o[1] - '0') * 3600; o += 2; } else @@ -150,7 +139,7 @@ iso_parse(char *arg, long *nsec, char **errstr) if(isdigit(o[0]) && isdigit(o[1])) { - res.tm_gmtoff += (o[0] - '0') * 600 + (o[1] - '0') * 60; + time->tm_gmtoff += (o[0] - '0') * 600 + (o[1] - '0') * 60; o += 2; } else @@ -159,7 +148,7 @@ iso_parse(char *arg, long *nsec, char **errstr) return NULL; } - if(neg) res.tm_gmtoff = -res.tm_gmtoff; + if(neg) time->tm_gmtoff = -time->tm_gmtoff; #ifndef TZNAME_MAX #define TZNAME_MAX _POSIX_TZNAME_MAX @@ -167,13 +156,13 @@ iso_parse(char *arg, long *nsec, char **errstr) static char offname[TZNAME_MAX + 1] = ""; assert(o - s < TZNAME_MAX); memcpy(offname, s, o - s); - res.tm_zone = offname; + time->tm_zone = offname; } } assert(errno == 0); - return &res; + return s; } // Because mktime() messes with tm_gmtoff yet doesn't applies it, even in POSIX.1-2024 diff --git a/lib/iso_parse.h b/lib/iso_parse.h @@ -6,7 +6,7 @@ // Sets errstr on failure // YYYY-MM-DD[T ]hh:mm:SS([,\.]frac)?(Z|[+\-]hh:?mm)? -extern struct tm *iso_parse(char *arg, long *nsec, char **errstr); +extern char *iso_parse(char *arg, struct tm *time, long *nsec, char **errstr); // Because mktime() messes with tm_gmtoff yet doesn't applies the offset // Returns (time_t)-1 on failure