| 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; | |
| +} |