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:
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