commit: b5c35a2aac5fd23f1e1a112e7eda24579b8f8bc1
parent f5de5c3c6b3fda963433f8d02f7c61676ff012f4
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Wed, 1 Nov 2023 03:48:40 +0100
lib/iso_parse: Improve error handling with adding **errstr argument
Diffstat:
5 files changed, 61 insertions(+), 15 deletions(-)
diff --git a/cmd/date.c b/cmd/date.c
@@ -40,10 +40,16 @@ main(int argc, char *argv[])
while((c = getopt(argc, argv, ":d:uR")) != -1)
{
+ char *errstr = NULL;
switch(c)
{
case 'd': /* Custom datetime */
- now = iso_parse(optarg).tv_sec;
+ now = iso_parse(optarg, &errstr).tv_sec;
+ if(errstr != NULL)
+ {
+ fprintf(stderr, "date: iso_parse(\"%s\", …): %s\n", optarg, errstr);
+ exit(EXIT_FAILURE);
+ }
break;
case 'u': /* UTC timezone */
uflag++;
diff --git a/cmd/touch.c b/cmd/touch.c
@@ -13,6 +13,7 @@
#include <fcntl.h> /* open */
#include <stdbool.h> /* bool */
#include <stdio.h> /* perror */
+#include <stdlib.h> /* exit, EXIT_FAILURE */
#include <sys/stat.h> /* futimens, stat, utimensat */
#include <unistd.h> /* getopt, opt*, close */
@@ -32,6 +33,8 @@ main(int argc, char *argv[])
int c = 0;
while((c = getopt(argc, argv, ":achmr:t:d:")) != -1)
{
+ char *errstr = NULL;
+
switch(c)
{
case 'a':
@@ -56,7 +59,12 @@ main(int argc, char *argv[])
return 1;
break;
case 'd':
- target = iso_parse(optarg);
+ target = iso_parse(optarg, &errstr);
+ if(errstr != NULL)
+ {
+ fprintf(stderr, "touch: iso_parse(\"%s\", …): %s\n", optarg, errstr);
+ exit(EXIT_FAILURE);
+ }
break;
case ':':
fprintf(stderr, "touch: Error: Missing operand for option: '-%c'\n", optopt);
diff --git a/lib/iso_parse.c b/lib/iso_parse.c
@@ -9,13 +9,13 @@
#include <ctype.h> /* isdigit */
#include <errno.h> /* errno */
#include <stdio.h> /* perror, sscanf */
-#include <stdlib.h> /* exit */
+#include <stdlib.h> /* strtol */
#include <string.h> /* memset */
#include <time.h> /* strptime, tm */
-// Calls exit() on failure
+// Sets errstr on failure
struct timespec
-iso_parse(char *arg)
+iso_parse(char *arg, char **errstr)
{
// YYYY-MM-DD[T ]hh:mm:SS([,\.]frac)?Z?
// Dammit Unixes why no nanoseconds in `struct tm` nor `strptime`
@@ -30,8 +30,8 @@ iso_parse(char *arg)
time.tv_sec = strtol(arg, NULL, 10);
if(errno != 0)
{
- perror("strtol");
- exit(EXIT_FAILURE);
+ *errstr = strerror(errno);
+ return time;
}
return time;
}
@@ -42,10 +42,24 @@ iso_parse(char *arg)
// No %F in POSIX
char *s = strptime(arg, "%Y-%m-%d", &tm);
- if(s[0] != 'T' && s[0] != ' ') exit(EXIT_FAILURE);
+ if(s == NULL)
+ {
+ *errstr = "strptime(…, \"%Y-%m-%d\", …) returned NULL";
+ return time;
+ }
+ if(s[0] != 'T' && s[0] != ' ')
+ {
+ *errstr = "Couldn't find time-separator (T or space) after date (Y-m-d)";
+ return time;
+ }
s++;
s = strptime(s, "%H:%M:%S", &tm);
+ if(s == NULL)
+ {
+ *errstr = "strptime(…, \"%H:%M:%S\", …) returned NULL";
+ return time;
+ }
if(s[0] == ',' || s[0] == '.')
{
@@ -54,16 +68,32 @@ iso_parse(char *arg)
if(s[0] == ',') s[0] = '.';
- if(sscanf(s, "%10lf%n", &fraction, &parsed) < 1) exit(EXIT_FAILURE);
+ errno = 0;
+ if(sscanf(s, "%10lf%n", &fraction, &parsed) < 1)
+ {
+ if(errno == 0)
+ {
+ *errstr = "Failed to parse fractional seconds";
+ }
+ else
+ {
+ *errstr = strerror(errno);
+ }
+ return time;
+ }
time.tv_nsec = fraction * 1000000000;
s += parsed;
// too many digits
- if(isdigit(s[0])) exit(EXIT_FAILURE);
+ if(isdigit(s[0]))
+ {
+ *errstr = "Too many digits (> 10) for fractional seconds";
+ return time;
+ }
}
- if(s[0] == 'Z')
+ if(s != NULL && s[0] == 'Z')
{
tm.tm_gmtoff = 0;
tm.tm_zone = "UTC";
@@ -72,8 +102,8 @@ iso_parse(char *arg)
time.tv_sec = mktime(&tm);
if(time.tv_sec == (time_t)-1)
{
- perror("mktime");
- exit(EXIT_FAILURE);
+ *errstr = strerror(errno);
+ return time;
}
return time;
diff --git a/lib/iso_parse.h b/lib/iso_parse.h
@@ -3,4 +3,4 @@
// SPDX-License-Identifier: MPL-2.0
#include <time.h> /* struct timespec */
-extern struct timespec iso_parse(char *);
+extern struct timespec iso_parse(char *arg, char **errstr);
diff --git a/test-cmd/touch b/test-cmd/touch
@@ -173,7 +173,9 @@ optd_frac_body() {
atf_check -o 'match:^2003-06-02[T ]13:37:42.123456789 ?(Z|[\+\-]00:?00)$' ./stat_atime ./foo
atf_check -o 'match:^2003-06-02[T ]13:37:42.123456789 ?(Z|[\+\-]00:?00)$' ./stat_mtime ./foo
- atf_check -s 'exit:1' ../cmd/touch -d 2003-06-02T13:37:42.1234567890Z ./foo
+ atf_check -s 'exit:1' \
+ -e 'inline:touch: iso_parse("2003-06-02T13:37:42.1234567890Z", …): Too many digits (> 10) for fractional seconds\n' \
+ ../cmd/touch -d 2003-06-02T13:37:42.1234567890Z ./foo
}
atf_init_test_cases() {