commit: 10e16fe669120d6980bfeae8a2cdbd02108cc9ca
parent 73076a09363d6471825b2740179aae55e6c0d414
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Sun, 24 Sep 2023 21:47:31 +0200
lib/strtodur: Extract from cmd/sleep
Diffstat:
12 files changed, 266 insertions(+), 126 deletions(-)
diff --git a/Kyuafile b/Kyuafile
@@ -5,3 +5,4 @@ syntax(2)
test_suite("utils-std")
include("test-cmd/Kyuafile")
+include("test-lib/Kyuafile")
diff --git a/Makefile b/Makefile
@@ -14,7 +14,7 @@ all: $(EXE)
$(CC) -std=c99 $(CFLAGS) -c -o $@ $<
.PHONY: check
-check: all
+check: all test-lib/strtodur
MALLOC_CHECK_=3 POSIX_ME_HARDER=1 POSIXLY_CORRECT=1 kyua test || (kyua report --verbose --results-filter=broken,failed; false)
MALLOC_CHECK_=3 POSIX_ME_HARDER=1 POSIXLY_CORRECT=1 $(CRAM) test-cmd/*.t
@@ -50,3 +50,10 @@ cmd/date: cmd/date.c lib/iso_parse.c Makefile
cmd/touch: cmd/touch.c lib/iso_parse.c Makefile
rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
$(CC) -std=c99 $(CFLAGS) -o $@ cmd/touch.c lib/iso_parse.c $(LDFLAGS)
+
+cmd/sleep: cmd/sleep.c lib/strtodur.c Makefile
+ rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
+ $(CC) -std=c99 $(CFLAGS) -o $@ cmd/sleep.c lib/strtodur.c $(LDFLAGS)
+
+test-lib/strtodur: test-lib/strtodur.c lib/strtodur.c Makefile
+ $(CC) -std=c99 $(CFLAGS) $(ATF_CFLAGS) -o $@ test-lib/strtodur.c lib/strtodur.c $(LDFLAGS) $(ATF_LIBS)
diff --git a/cmd/sleep.c b/cmd/sleep.c
@@ -3,104 +3,24 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include <errno.h> // errno
-#include <stdio.h> // fprintf, perror, sscanf
-#include <stdlib.h> // exit
-#include <time.h> // nanosleep, timespec
+#include "../lib/strtodur.h"
-// Maybe should be moved in ./lib with iso_parse
-static struct timespec
-strtodur(char *s)
-{
- struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
-
- if(s[0] == 0)
- {
- fprintf(stderr, "sleep: Got an empty string as duration\n");
- return dur;
- }
-
- int parsed = 0;
- if(s[0] != '.' && s[0] != ',')
- {
- errno = 0;
- int ret = sscanf(s, "%10ld%n", &dur.tv_sec, &parsed);
- if(ret < 1)
- {
- if(errno == 0)
- {
- fprintf(stderr, "sleep: Not a number: %s\n", s);
- }
- else
- {
- perror("sleep: sscanf");
- }
- exit(EXIT_FAILURE);
- }
-
- s += parsed;
-
- if(s[0] == 0) return dur;
- }
-
- if(s[0] == '.' || s[0] == ',')
- {
- float fraction = 0.0;
- if(s[1] == 0) return dur;
- if(s[0] == ',') s[0] = '.';
-
- parsed = 0;
- errno = 0;
- if(sscanf(s, "%10f%n", &fraction, &parsed) < 1)
- {
- if(errno == 0)
- {
- fprintf(stderr, "sleep: Decimal part is not a number: %s\n", s);
- }
- else
- {
- perror("sleep: sscanf");
- }
- exit(EXIT_FAILURE);
- }
-
- dur.tv_nsec = fraction * 1000000000;
- s += parsed;
- }
-
- if(s[0] == 0) return dur;
-
- if(s[1] != 0)
- {
- fprintf(stderr, "sleep: suffix '%s' is too long, should be only one character\n", s);
- exit(1);
- }
-
- switch(s[0])
- {
- case 's': // seconds
- break;
- case 'm': // minutes
- dur.tv_sec *= 60;
- break;
- case 'h': // hours
- dur.tv_sec *= 60 * 60;
- break;
- default:
- fprintf(stderr, "sleep: Unknown suffix %c\n", s[0]);
- exit(1);
- }
-
- return dur;
-}
+#include <errno.h> // errno
+#include <stdio.h> // fprintf, perror
+#include <time.h> // nanosleep
int
main(int argc, char *argv[])
{
struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+
for(int i = 1; i < argc; i++)
{
- struct timespec arg_dur = strtodur(argv[i]);
+ struct timespec arg_dur = {.tv_sec = 0, .tv_nsec = 0};
+ if(strtodur(argv[i], &arg_dur) < 0)
+ {
+ return 1;
+ }
dur.tv_sec += arg_dur.tv_sec;
dur.tv_nsec += arg_dur.tv_nsec;
@@ -112,12 +32,10 @@ main(int argc, char *argv[])
}
if(dur.tv_sec == 0 && dur.tv_nsec == 0)
{
- fprintf(stderr, "sleep: Got a duration of 0\n");
+ fprintf(stderr, "sleep: Got a total duration of 0\n");
return 1;
}
- //fprintf(stderr, "sleep: going to sleep for %ld.%09ld seconds\n", dur.tv_sec, dur.tv_nsec);
-
errno = 0;
if(nanosleep(&dur, &dur) < 0)
{
diff --git a/configure b/configure
@@ -199,11 +199,10 @@ fi
echo
-# pkg-config
-for dep in ${DEPS}
-do
- pkg_config_check --exists "$dep" || exit 1
-done
+if pkg_config_check --exists atf-c; then
+ ATF_CFLAGS=$("${PKGCONFIG}" --cflags atf-c)
+ ATF_LIBS=$("${PKGCONFIG}" --libs atf-c)
+fi
echo
@@ -232,6 +231,9 @@ REUSE = ${REUSE}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS}
+
+ATF_CFLAGS = ${ATF_CFLAGS}
+ATF_LIBS = ${ATF_LIBS}
EOF
is_ok
diff --git a/lib/strtodur.c b/lib/strtodur.c
@@ -0,0 +1,94 @@
+// utils-std: Collection of commonly available Unix tools
+// SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#include "strtodur.h"
+
+#include <errno.h> // errno
+#include <stdio.h> // fprintf, perror, sscanf
+#include <assert.h>
+
+int
+strtodur(char *s, struct timespec *dur)
+{
+ if(s == 0 || s[0] == 0)
+ {
+ fprintf(stderr, "strtodur: Warning: Got an empty string as duration\n");
+ return 0;
+ }
+
+ assert(dur);
+
+ int parsed = 0;
+ if(s[0] != '.' && s[0] != ',')
+ {
+ errno = 0;
+ int ret = sscanf(s, "%10ld%n", &dur->tv_sec, &parsed);
+ if(ret < 1)
+ {
+ if(errno == 0)
+ {
+ fprintf(stderr, "strtodur: Error: Not a number: %s\n", s);
+ }
+ else
+ {
+ perror("strtodur: Error with sscanf");
+ }
+ return -1;
+ }
+
+ s += parsed;
+
+ if(s[0] == 0) return 0;
+ }
+
+ if(s[0] == '.' || s[0] == ',')
+ {
+ float fraction = 0.0;
+ if(s[1] == 0) return 0;
+ if(s[0] == ',') s[0] = '.';
+
+ parsed = 0;
+ errno = 0;
+ if(sscanf(s, "%10f%n", &fraction, &parsed) < 1)
+ {
+ if(errno == 0)
+ {
+ fprintf(stderr, "strtodur: Error: Decimal part is not a number: %s\n", s);
+ }
+ else
+ {
+ perror("strtodur: Error with sscanf");
+ }
+ return -1;
+ }
+
+ dur->tv_nsec = fraction * 1000000000;
+ s += parsed;
+ }
+
+ if(s[0] == 0) return 0;
+
+ if(s[1] != 0)
+ {
+ fprintf(stderr, "strtodur: Error: suffix '%s' is too long, should be only one character\n", s);
+ return -1;
+ }
+
+ switch(s[0])
+ {
+ case 's': // seconds
+ break;
+ case 'm': // minutes
+ dur->tv_sec *= 60;
+ break;
+ case 'h': // hours
+ dur->tv_sec *= 60 * 60;
+ break;
+ default:
+ fprintf(stderr, "strtodur: Error: Unknown suffix %c\n", s[0]);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lib/strtodur.h b/lib/strtodur.h
@@ -0,0 +1,6 @@
+// utils-std: Collection of commonly available Unix tools
+// SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#include <time.h> // timespec
+int strtodur(char *s, struct timespec *dur);
diff --git a/test-cmd/Kyuafile b/test-cmd/Kyuafile
@@ -2,11 +2,9 @@
-- SPDX-License-Identifier: MPL-2.0
syntax(2)
-test_suite("utils")
+test_suite("utils-std commands")
-basedir = fs.dirname(fs.dirname(current_kyuafile()))
-
--- 9,$|LC_ALL=C.UTF-8 sort
+-- 7,$|LC_ALL=C.UTF-8 sort
atf_test_program{name="base64", timeout=1}
atf_test_program{name="basename", timeout=1}
atf_test_program{name="cat", timeout=1}
@@ -19,6 +17,7 @@ atf_test_program{name="id", timeout=1}
atf_test_program{name="link", timeout=1}
atf_test_program{name="pwd", timeout=1}
atf_test_program{name="seq", timeout=1}
+atf_test_program{name="sleep", timeout=3}
atf_test_program{name="strings", timeout=1}
atf_test_program{name="tee", timeout=1}
atf_test_program{name="touch", timeout=3}
diff --git a/test-cmd/foo b/test-cmd/foo
diff --git a/test-cmd/sleep.t b/test-cmd/sleep.t
@@ -1,23 +0,0 @@
-#!/usr/bin/env cram
-# SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
-# SPDX-License-Identifier: MPL-2.0
-
- $ cd $TESTDIR/../cmd
-
- $ ./sleep
- sleep: Got a duration of 0
- [1]
-
- $ ./sleep -f
- (sleep: Not a number: -f|sleep: sscanf: Invalid argument) (re)
- [1]
-
- $ ./sleep 0
- sleep: Got a duration of 0
- [1]
-
- $ ./sleep 1
-
- $ ./sleep .1
-
- $ ./sleep 1.
diff --git a/test-lib/Kyuafile b/test-lib/Kyuafile
@@ -0,0 +1,8 @@
+-- SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+-- SPDX-License-Identifier: MPL-2.0
+syntax(2)
+
+test_suite("utils-std libs")
+
+-- 7,$|LC_ALL=C.UTF-8 sort
+atf_test_program{name="strtodur"}
diff --git a/test-lib/strtodur b/test-lib/strtodur
Binary files differ.
diff --git a/test-lib/strtodur.c b/test-lib/strtodur.c
@@ -0,0 +1,128 @@
+// utils-std: Collection of commonly available Unix tools
+// SPDX-FileCopyrightText: 2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#define _POSIX_C_SOURCE 200809L
+#include <atf-c.h>
+#include <assert.h>
+#include <signal.h> // SIGABRT
+
+#include "../lib/strtodur.h"
+
+ATF_TC(empty);
+ATF_TC_HEAD(empty, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Empty string");
+}
+ATF_TC_BODY(empty, tc)
+{
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ ATF_CHECK_EQ(0, strtodur("", &dur));
+ ATF_CHECK_EQ(0, dur.tv_sec);
+ ATF_CHECK_EQ(0, dur.tv_nsec);
+}
+
+ATF_TC(null_str);
+ATF_TC_HEAD(null_str, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "NULL string");
+}
+ATF_TC_BODY(null_str, tc)
+{
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ ATF_CHECK_EQ(0, strtodur(NULL, &dur));
+ ATF_CHECK_EQ(0, dur.tv_sec);
+ ATF_CHECK_EQ(0, dur.tv_nsec);
+}
+
+ATF_TC(null_dur);
+ATF_TC_HEAD(null_dur, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "NULL *dur");
+}
+ATF_TC_BODY(null_dur, tc)
+{
+ atf_tc_expect_signal(SIGABRT, "Abort on receiving a NULL pointer on *dur argument");
+ ATF_CHECK_EQ(1, strtodur("1", NULL));
+}
+
+ATF_TC(one);
+ATF_TC_HEAD(one, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "1");
+}
+ATF_TC_BODY(one, tc)
+{
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ ATF_CHECK_EQ(0, strtodur("1", &dur));
+ ATF_CHECK_EQ(1, dur.tv_sec);
+ ATF_CHECK_EQ(0, dur.tv_nsec);
+}
+
+ATF_TC(pointone);
+ATF_TC_HEAD(pointone, tc)
+{
+ atf_tc_set_md_var(tc, "descr", ".1");
+}
+ATF_TC_BODY(pointone, tc)
+{
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ ATF_CHECK_EQ(0, strtodur(".1", &dur));
+ ATF_CHECK_EQ(0, dur.tv_sec);
+
+ int expect = 1000000000*0.1;
+ ATF_CHECK_EQ_MSG(expect, dur.tv_nsec, "dur.tv_nsec(%d) != %d", dur.tv_nsec, expect);
+}
+
+ATF_TC(onepoint);
+ATF_TC_HEAD(onepoint, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "1.");
+}
+ATF_TC_BODY(onepoint, tc)
+{
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ ATF_CHECK_EQ(0, strtodur("1.", &dur));
+ ATF_CHECK_EQ(1, dur.tv_sec);
+ ATF_CHECK_EQ(0, dur.tv_nsec);
+}
+
+ATF_TC(onecomma);
+ATF_TC_HEAD(onecomma, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "1,");
+}
+ATF_TC_BODY(onecomma, tc)
+{
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ ATF_CHECK_EQ(0, strtodur("1,", &dur));
+ ATF_CHECK_EQ(1, dur.tv_sec);
+ ATF_CHECK_EQ(0, dur.tv_nsec);
+}
+
+ATF_TC(comma);
+ATF_TC_HEAD(comma, tc)
+{
+ atf_tc_set_md_var(tc, "descr", ",");
+}
+ATF_TC_BODY(comma, tc)
+{
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ ATF_CHECK_EQ(0, strtodur(",", &dur));
+ ATF_CHECK_EQ(0, dur.tv_sec);
+ ATF_CHECK_EQ(0, dur.tv_nsec);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, empty);
+ ATF_TP_ADD_TC(tp, null_str);
+ ATF_TP_ADD_TC(tp, null_dur);
+ ATF_TP_ADD_TC(tp, one);
+ ATF_TP_ADD_TC(tp, pointone);
+ ATF_TP_ADD_TC(tp, onepoint);
+ ATF_TP_ADD_TC(tp, onecomma);
+ ATF_TP_ADD_TC(tp, comma);
+
+ return atf_no_error();
+}