commit 7a3d0ac06b0b914fbd04bd19e76fc682fcb38a20
parent 1fb885b94e493d9d36da5ddf5d2cd8fb8a99510f
Author: Anders Damsgaard <anders@adamsgaard.dk>
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
@@ -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"; done
# installing documentation files.
mkdir -p "${DESTDIR}${DOCPREFIX}"
cp -f ${DOC} "${DESTDIR}${DOCPREFIX}"
@@ -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"; done
# removing example files.
for d in ${DOC}; do rm -f "${DESTDIR}${DOCPREFIX}/$$d"; done
-rmdir "${DESTDIR}${DOCPREFIX}"
@@ -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
@@ -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
@@ -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, sizeof(cmd)));
+ 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;
+}