logo

utils-std

Collection of commonly available Unix tools
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:

Mcmd/date.c8+++++++-
Mcmd/touch.c10+++++++++-
Mlib/iso_parse.c52+++++++++++++++++++++++++++++++++++++++++-----------
Mlib/iso_parse.h2+-
Mtest-cmd/touch4+++-
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() {