trangetest: switch to C implementation - numtools - perform numerical operation… | |
git clone git://src.adamsgaard.dk/numtools | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 7a3d0ac06b0b914fbd04bd19e76fc682fcb38a20 | |
parent 1fb885b94e493d9d36da5ddf5d2cd8fb8a99510f | |
Author: Anders Damsgaard <[email protected]> | |
Date: Tue, 31 Aug 2021 10:41:51 +0200 | |
rangetest: switch to C implementation | |
Diffstat: | |
M Makefile | 51 +++++++++++++++++++++++++----… | |
D rangetest | 75 -----------------------------… | |
A rangetest.c | 137 +++++++++++++++++++++++++++++… | |
3 files changed, 178 insertions(+), 85 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
t@@ -8,25 +8,53 @@ PREFIX = /usr/local | |
MANPREFIX = ${PREFIX}/man | |
DOCPREFIX = ${PREFIX}/share/doc/${NAME} | |
-SCRIPTS = \ | |
+SCRIPTS =\ | |
histpdf\ | |
max\ | |
mean\ | |
min\ | |
- rangetest\ | |
sum\ | |
- transpose | |
+ transpose\ | |
+ | |
+BIN =\ | |
+ rangetest\ | |
+ | |
+SRC = ${BIN:=.c} | |
-MAN1 = ${SCRIPTS:=.1} | |
-DOC = \ | |
+HDR =\ | |
+ arg.h\ | |
+ | |
+LIBS = -lm | |
+ | |
+_CFLAGS = ${CFLAGS} ${INCS} -DVERSION=\"${VERSION}\" | |
+_LDFLAGS = ${LDFLAGS} ${LIBS} | |
+_CPPFLAGS = ${CPPFLAGS} | |
+ | |
+MAN1 = ${BIN:=.1} ${SCRIPTS:=.1} | |
+DOC =\ | |
LICENSE\ | |
- README | |
+ README\ | |
+ | |
+all: ${BIN} | |
+ | |
+${BIN}: ${@:=.o} | |
+ | |
+OBJ = ${SRC:.c=.o} | |
+ | |
+${OBJ}: ${HDR} | |
+ | |
+.o: | |
+ ${CC} -o $@ $< ${_LDFLAGS} | |
+ | |
+.c.o: | |
+ ${CC} ${_CFLAGS} ${_CPPFLAGS} -o $@ -c $< | |
+ | |
install: | |
# installing executable files and scripts. | |
mkdir -p "${DESTDIR}${PREFIX}/bin" | |
cp -f ${SCRIPTS} "${DESTDIR}${PREFIX}/bin" | |
- for f in ${SCRIPTS}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done | |
+ for f in ${BIN} ${SCRIPTS}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"… | |
# installing documentation files. | |
mkdir -p "${DESTDIR}${DOCPREFIX}" | |
cp -f ${DOC} "${DESTDIR}${DOCPREFIX}" | |
t@@ -38,7 +66,7 @@ install: | |
uninstall: | |
# removing executable files and scripts. | |
- for f in ${SCRIPTS}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done | |
+ for f in ${BIN} ${SCRIPTS}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; do… | |
# removing example files. | |
for d in ${DOC}; do rm -f "${DESTDIR}${DOCPREFIX}/$$d"; done | |
-rmdir "${DESTDIR}${DOCPREFIX}" | |
t@@ -48,10 +76,13 @@ uninstall: | |
dist: | |
rm -rf "${NAME}-${VERSION}" | |
mkdir -p "${NAME}-${VERSION}" | |
- cp -rf ${MAN1} ${DOC} ${SCRIPTS} "${NAME}-${VERSION}" | |
+ cp -rf ${MAN1} ${DOC} ${BIN} ${SCRIPTS} "${NAME}-${VERSION}" | |
# make tarball | |
tar cf - "${NAME}-${VERSION}" | \ | |
gzip -c > "${NAME}-${VERSION}.tar.gz" | |
rm -rf "${NAME}-${VERSION}" | |
-.PHONY: install uninstall dist | |
+clean: | |
+ rm -f ${BIN} ${OBJ} | |
+ | |
+.PHONY: install uninstall dist clean | |
diff --git a/rangetest b/rangetest | |
t@@ -1,75 +0,0 @@ | |
-#!/usr/bin/awk -f | |
-# uses a binary search to run a "cmd" where the first occurence of | |
-# string @VAL@ is substituted for a value between "min_val" and | |
-# "max_val". Successful runs are reported on stdout, failed runs | |
-# on stderr. The "cmd" command must successfully run with either | |
-# "min_val" or "max_val". | |
- | |
-function die(s) { | |
- printf "error: %s\n", s > "/dev/stderr" | |
- exit 1 | |
-} | |
- | |
-function launch(cmd, val) { | |
- sub(/@VAL@/, val, cmd) | |
- if (system(cmd)) { | |
- printf "%g\n", val > "/dev/stderr" | |
- return 1 | |
- } else { | |
- printf "%g\n", val > "/dev/stdout" | |
- return 0 | |
- } | |
-} | |
- | |
-function binary_search(cmd, min, max, maxiter) { | |
- | |
- minfail = launch(cmd, min) | |
- maxfail = launch(cmd, max) | |
- | |
- if (minfail && maxfail) | |
- die("both min_val and max_val runs errored") | |
- | |
- if (!minfail && !maxfail) | |
- die("both min_val and max_val ran successfully") | |
- | |
- while (min <= max && iter < maxiter) { | |
- val = min + 0.5 * (max - min) | |
- | |
- if (launch(cmd, val)) { # the cmd fails | |
- if (maxfail) { | |
- max = val | |
- maxfail = 1 | |
- } else { | |
- min = val | |
- minfail = 1 | |
- } | |
- } else { # the cmd is ok | |
- if (maxfail) { | |
- min = val | |
- minfail = 0 | |
- } else { | |
- max = val | |
- maxfail = 0 | |
- } | |
- } | |
- iter++ | |
- } | |
-} | |
- | |
-BEGIN { | |
- | |
- if (ARGC != 4) | |
- die("usage: rangetest cmd min_val max_val") | |
- | |
- cmd = ARGV[1] | |
- min = ARGV[2] | |
- max = ARGV[3] | |
- | |
- if (!match(cmd, /@VAL@/)) | |
- die("@VAL@ not found in cmd") | |
- | |
- if (min >= max) | |
- die("min_val must be smaller than max_val") | |
- | |
- binary_search(cmd, min, max, 10) | |
-} | |
diff --git a/rangetest.c b/rangetest.c | |
t@@ -0,0 +1,137 @@ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <err.h> | |
+#include <limits.h> | |
+#include <string.h> | |
+ | |
+#include "arg.h" | |
+ | |
+#define VALUESTR "@VAL@" | |
+ | |
+char *argv0; | |
+ | |
+#ifdef NEED_STRLCPY /* OpenBSD implementation */ | |
+size_t | |
+strlcpy(char *dst, const char *src, size_t dsize) { | |
+ const char *osrc = src; | |
+ size_t nleft = dsize; | |
+ | |
+ if (nleft != 0) { | |
+ while (--nleft != 0) { | |
+ if ((*dst++= *src++) == '\0') | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (nleft == 0) { | |
+ if (dsize != 0) | |
+ *dst = '\0'; | |
+ while (*src++) | |
+ ; | |
+ } | |
+ | |
+ return(src - osrc - 1); | |
+} | |
+#endif /* NEED_STRLCPY */ | |
+ | |
+static void | |
+usage(void) | |
+{ | |
+ errx(1, "usage: %s [-h] [-n maxiter] cmd min_val max_val\n" | |
+ "where cmd must contain the string '" VALUESTR "'", argv0); | |
+} | |
+ | |
+static int | |
+launch(char *cmd, char *cmd0, double val) | |
+{ | |
+ char *c; | |
+ | |
+ if ((c = strstr(cmd0, VALUESTR)) == NULL) | |
+ errx(1, VALUESTR " not found in cmd"); | |
+ | |
+ if (strlcpy(cmd, cmd0, PATH_MAX) >= PATH_MAX) | |
+ err(1, "cmd too long"); | |
+ | |
+ sprintf(cmd + (c - cmd0), "%g%s >/dev/null", val, c + strnlen(VALUESTR… | |
+ if (system(cmd)) { | |
+ fprintf(stderr, "%g\n", val); | |
+ return 1; | |
+ } else { | |
+ printf("%g\n", val); | |
+ return 0; | |
+ } | |
+} | |
+ | |
+static void | |
+binary_search(char *cmd, char *cmd0, double minv, double maxv, int maxiter) | |
+{ | |
+ int minfail, maxfail; | |
+ int iter; | |
+ double val; | |
+ | |
+ minfail = launch(cmd, cmd0, minv); | |
+ maxfail = launch(cmd, cmd0, maxv); | |
+ | |
+ if (minfail && maxfail) | |
+ errx(2, "both min_val and max_val runs errored"); | |
+ | |
+ else if (!minfail && !maxfail) | |
+ errx(3, "both min_val and max_val ran successfully"); | |
+ | |
+ while (minv <= maxv && iter < maxiter) { | |
+ val = minv + 0.5 * (maxv - minv); | |
+ | |
+ if (launch(cmd, cmd0, val)) { | |
+ if (maxfail) { | |
+ maxv = val; | |
+ maxfail = 1; | |
+ } else { | |
+ minv = val; | |
+ minfail = 1; | |
+ } | |
+ } else { | |
+ if (maxfail) { | |
+ minv = val; | |
+ minfail = 0; | |
+ } else { | |
+ maxv = val; | |
+ maxfail = 0; | |
+ } | |
+ } | |
+ iter++; | |
+ } | |
+} | |
+ | |
+int | |
+main(int argc, char *argv[]) | |
+{ | |
+ int maxiter = 10; | |
+ double minv, maxv; | |
+ char cmd0[PATH_MAX] = "", cmd[PATH_MAX] = ""; | |
+ | |
+ ARGBEGIN { | |
+ case 'h': | |
+ usage(); | |
+ break; | |
+ case 'n': | |
+ maxiter = atoi(EARGF(usage())); | |
+ if (maxiter < 1) | |
+ errx(1, "maxiter (-n) must be positive"); | |
+ break; | |
+ default: | |
+ usage(); | |
+ } ARGEND; | |
+ | |
+ if (argc == 3) { | |
+ if (strlcpy(cmd0, argv[0], sizeof(cmd0)) >= sizeof(cmd0)) | |
+ err(1, "cmd too long"); | |
+ minv = atof(argv[1]); | |
+ maxv = atof(argv[2]); | |
+ if (minv >= maxv) | |
+ errx(1, "min_val must be smaller than max_val"); | |
+ binary_search(cmd, cmd0, minv, maxv, maxiter); | |
+ } else | |
+ usage(); | |
+ | |
+ return 0; | |
+} |