logo

utils-std

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

MKyuafile1+
MMakefile9++++++++-
Mcmd/sleep.c104+++++++++----------------------------------------------------------------------
Mconfigure12+++++++-----
Alib/strtodur.c94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/strtodur.h6++++++
Mtest-cmd/Kyuafile7+++----
Dtest-cmd/foo0
Dtest-cmd/sleep.t23-----------------------
Atest-lib/Kyuafile8++++++++
Atest-lib/strtodur0
Atest-lib/strtodur.c128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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(); +}