tuse safe formatting parameters and option parsing - numtools - perform numerical operations on vectors and matrices in unix pipes
 (HTM) git clone git://src.adamsgaard.dk/numtools
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 8787e852ca96ee9c18d4ed1a45016996e464940a
 (DIR) parent d206d176dbb5704fda2d5cc33010f54b8f36ff38
 (HTM) Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Wed, 11 May 2022 14:57:18 +0200
       
       use safe formatting parameters and option parsing
       
       Diffstat:
         M max.1                               |      29 ++++++++++++++++-------------
         M max.c                               |      45 ++++++++++++++++++-------------
         M mean.1                              |      29 ++++++++++++++++-------------
         M mean.c                              |      45 ++++++++++++++++++-------------
         M min.1                               |      29 ++++++++++++++++-------------
         M min.c                               |      45 ++++++++++++++++++-------------
         M randcounts.1                        |      38 +++++++++++++++++++++++--------
         M randcounts.c                        |      98 ++++++++++++++++++-------------
         M randnum.c                           |       8 ++++----
         M range.1                             |      61 ++++++++++++++++---------------
         M range.c                             |     107 ++++++++++++++++++-------------
         M rangetest.c                         |      40 +++++++++++++++----------------
         M stddev.1                            |      27 +++++++++++++++------------
         M stddev.c                            |      52 ++++++++++++++++++-------------
         M stdvar.1                            |      29 ++++++++++++++++-------------
         M stdvar.c                            |      52 ++++++++++++++++++-------------
         M sum.1                               |      29 ++++++++++++++++-------------
         M sum.c                               |      45 ++++++++++++++++++-------------
         M transpose.1                         |       6 +++---
         M util.c                              |       9 ++++-----
         M util.h                              |       2 +-
       
       21 files changed, 469 insertions(+), 356 deletions(-)
       ---
 (DIR) diff --git a/max.1 b/max.1
       t@@ -6,8 +6,9 @@
        .Nd returns the maximum value for each column
        .Sh SYNOPSIS
        .Nm
       -.Op Fl f Ar fmtstr
       -.Op Fl h
       +.Op Fl d Ar delimstr
       +.Op Fl n
       +.Op Fl p Ar prec
        .Sh DESCRIPTION
        .Nm
        returns the maximum numerical value for each column in standard
       t@@ -17,20 +18,22 @@ the same number of fields.
        .Pp
        The options are as follows:
        .Bl -tag -width Ds
       -.It Fl f Ar fmtstr
       -Formatting string to use as documented in
       -.Xr printf 3 .
       -When including a format specifier (%..), only use forms that are
       -compatible with
       -.Vt double
       -types.
       -The default format string is '%.17g'.
       -.It Fl h
       -Show usage information and exit.
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is tab characters.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .El
        .Sh EXAMPLES
        .Dl $ printf '1\et2\et3\en4\et5\et6\en' | max
       -.Dl 4        5        6
       +.Dl 4    5    6
        .Sh SEE ALSO
        .Xr mean 1 ,
        .Xr min 1 ,
 (DIR) diff --git a/max.c b/max.c
       t@@ -4,40 +4,45 @@
        #include <unistd.h>
        #include <limits.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-f fmtstr] [-h] ", argv0);
       +        errx(1, "usage: max [-d delimstr] [-n] [-p prec]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int ret;
       +        int ch, prec = 17, finalnl = 1;
                size_t i = 0, nf = 0, nr = 0, linesize = 0;
       -        char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g";
       +        char *line = NULL, *data = NULL, *delimstr = "\t";
       +        const char *errstr;
                double val, *vals = NULL;
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'f':
       -                ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage()));
       -                if (ret < 0 || (size_t)ret >= sizeof(fmtstr))
       -                        errx(1, "%s: could not write fmtstr", __func__);
       -                break;
       -        case 'h':
       -                usage();
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       +        while ((ch = getopt(argc, argv, "d:np:")) != -1) {
       +                switch (ch) {
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, 0, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        argc -= optind;
       +        /*argv += optind;*/
                if (argc > 0)
                        usage();
        
       t@@ -54,7 +59,9 @@ main(int argc, char *argv[])
                        }
                        nr++;
                }
       -        printfarr(fmtstr, vals, nf);
       +        printfarr(delimstr, prec, vals, nf);
       +        if (finalnl)
       +                putchar('\n');
        
                free(line);
                free(vals);
 (DIR) diff --git a/mean.1 b/mean.1
       t@@ -6,8 +6,9 @@
        .Nd returns the average value for each column
        .Sh SYNOPSIS
        .Nm
       -.Op Fl f Ar fmtstr
       -.Op Fl h
       +.Op Fl d Ar delimstr
       +.Op Fl n
       +.Op Fl p Ar prec
        .Sh DESCRIPTION
        .Nm
        returns the mean numerical value for each column in standard input.
       t@@ -16,20 +17,22 @@ number of fields.
        .Pp
        The options are as follows:
        .Bl -tag -width Ds
       -.It Fl f Ar fmtstr
       -Formatting string to use as documented in
       -.Xr printf 3 .
       -When including a format specifier (%..), only use forms that are
       -compatible with
       -.Vt double
       -types.
       -The default format string is '%.17g'.
       -.It Fl h
       -Show usage information and exit.
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is tab characters.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .El
        .Sh EXAMPLES
        .Dl $ printf '1\et2\et3\en4\et5\et6\en' | mean
       -.Dl 2.5        3.5        4.5
       +.Dl 2.5    3.5    4.5
        .Sh SEE ALSO
        .Xr max 1 ,
        .Xr min 1 ,
 (DIR) diff --git a/mean.c b/mean.c
       t@@ -4,40 +4,45 @@
        #include <unistd.h>
        #include <limits.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-f fmtstr] [-h] ", argv0);
       +        errx(1, "usage: mean [-d delimstr] [-n] [-p prec]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int ret;
       +        int ch, prec = 17, finalnl = 1;
                size_t i = 0, nf = 0, nr = 0, linesize = 0;
       -        char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g";
       +        char *line = NULL, *data = NULL, *delimstr = "\t";
       +        const char *errstr;
                double val, *vals = NULL;
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'f':
       -                ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage()));
       -                if (ret < 0 || (size_t)ret >= sizeof(fmtstr))
       -                        errx(1, "%s: could not write fmtstr", __func__);
       -                break;
       -        case 'h':
       -                usage();
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       +        while ((ch = getopt(argc, argv, "d:np:")) != -1) {
       +                switch (ch) {
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, 0, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        argc -= optind;
       +        /*argv += optind;*/
                if (argc > 0)
                        usage();
        
       t@@ -57,7 +62,9 @@ main(int argc, char *argv[])
                }
                for (i = 0; i < nf; i++)
                        vals[i] /= (double)nr;
       -        printfarr(fmtstr, vals, nf);
       +        printfarr(delimstr, prec, vals, nf);
       +        if (finalnl)
       +                putchar('\n');
        
                free(line);
                free(vals);
 (DIR) diff --git a/min.1 b/min.1
       t@@ -6,8 +6,9 @@
        .Nd returns the minimum value for each column
        .Sh SYNOPSIS
        .Nm
       -.Op Fl f Ar fmtstr
       -.Op Fl h
       +.Op Fl d Ar delimstr
       +.Op Fl n
       +.Op Fl p Ar prec
        .Sh DESCRIPTION
        .Nm
        returns the minimum numerical value for each column in standard
       t@@ -17,20 +18,22 @@ number of fields.
        .Pp
        The options are as follows:
        .Bl -tag -width Ds
       -.It Fl f Ar fmtstr
       -Formatting string to use as documented in
       -.Xr printf 3 .
       -When including a format specifier (%..), only use forms that are
       -compatible with
       -.Vt double
       -types.
       -The default format string is '%.17g'.
       -.It Fl h
       -Show usage information and exit.
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is tab characters.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .El
        .Sh EXAMPLES
        .Dl $ printf '1\et2\et3\en4\et5\et6\en' | min
       -.Dl 1        2        3
       +.Dl 1    2    3
        .Sh SEE ALSO
        .Xr max 1 ,
        .Xr mean 1 ,
 (DIR) diff --git a/min.c b/min.c
       t@@ -4,40 +4,45 @@
        #include <unistd.h>
        #include <limits.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-f fmtstr] [-h] ", argv0);
       +        errx(1, "usage: max [-d delimstr] [-n] [-p prec]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int ret;
       +        int ch, prec = 17, finalnl = 1;
                size_t i = 0, nf = 0, nr = 0, linesize = 0;
       -        char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g";
       +        char *line = NULL, *data = NULL, *delimstr = "\t";
       +        const char *errstr;
                double val, *vals = NULL;
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'f':
       -                ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage()));
       -                if (ret < 0 || (size_t)ret >= sizeof(fmtstr))
       -                        errx(1, "%s: could not write fmtstr", __func__);
       -                break;
       -        case 'h':
       -                usage();
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       +        while ((ch = getopt(argc, argv, "d:np:")) != -1) {
       +                switch (ch) {
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, -10, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        argc -= optind;
       +        /*argv += optind;*/
                if (argc > 0)
                        usage();
        
       t@@ -54,7 +59,9 @@ main(int argc, char *argv[])
                        }
                        nr++;
                }
       -        printfarr(fmtstr, vals, nf);
       +        printfarr(delimstr, prec, vals, nf);
       +        if (finalnl)
       +                putchar('\n');
        
                free(line);
                free(vals);
 (DIR) diff --git a/randcounts.1 b/randcounts.1
       t@@ -6,8 +6,10 @@
        .Nd produces random counts in weighted bins
        .Sh SYNOPSIS
        .Nm
       -.Op Fl h
       -.Op Fl n Ar num
       +.Op Fl d Ar delimstr
       +.Op Fl n
       +.Op Fl N Ar num
       +.Op Fl p Ar prec
        .Op Fl r Ar repeats
        .Op Fl R
        .Op Fl s Ar seed
       t@@ -31,15 +33,28 @@ Output consists of the number of points per bin, in tab-separated format.
        .Pp
        The options are as follows:
        .Bl -tag -width Ds
       -.It Fl h
       -Show usage information.
       -.It Fl n Ar num
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is tab characters.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl N Ar num
        Number of points to place in the bins.
        The default is 1.
        .It Fl r Ar repeats
        Repeat the binning several times, with one realization per line of output.
        .It Fl R
        Show the output as ratios (in the range [0;1]) instead of counts.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +This option only applies if called with the
       +.Fl R
       +option.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .It Fl s Ar seed
        Seed the pseudo-random number generator with this value, which is used
        to generate reproducable binning.
       t@@ -48,15 +63,18 @@ to generate reproducable binning.
        Put one point in four bins with equal probability (25%).
        Due to the randomness, your output may differ:
        .Dl $ randcounts 0.25 0.25 0.25 0.25
       -.Dl 0        1        0        0
       +.Dl 0   1   0   0
        .Pp
       -Put 100 points in two bins with 75% and 25% probability, respectively:
       -.Dl $ randcounts -n 100 0.75 0.25
       +Put 100 points in two bins with 75% and 25% probability, respectively,
       +and print the count ratios for each bin:
       +.Dl $ randcounts -N 100 -R 0.75 0.25
       +.Dl 0.72999999999999998        0.27000000000000002
        .Pp
       -Put 100 points in three equal bins 1000 times, and calculate the average bin sizes with
       +Put 100 points in three equal bins 1000 times, and calculate the average
       +bin sizes with
        .Xr mean 1 :
        .Dl $ randcounts -n 100 -r 1000 0.333 0.333 0.334 | mean
       -.Dl 33.067        32.82        34.113
       +.Dl 33.067   32.82   34.113
        .Sh SEE ALSO
        .Xr mean 1 ,
        .Xr range 1 ,
 (DIR) diff --git a/randcounts.c b/randcounts.c
       t@@ -5,68 +5,85 @@
        #include <string.h>
        #include <math.h>
        #include <sys/time.h>
       +#include <unistd.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-h] [-n num] [-r repeats] [-R] [-s seed] "
       -                "weight1 [weight2 ...]\n", argv0);
       +        errx(1, "usage: randcounts [-d delimstr] [-n] [-N num] [-p prec]"
       +                "[-r repeats] [-R] [-s seed] weight1 [weight2 ...]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int i, s = 0, N;
       +        int i, ch, nbins, prec = 17, finalnl = 1, s = 0;
                long j, seed, *counts = NULL, n = 1, r, repeats = 1, ratio = 0;
                double val = 0.0, weightsum = 0.0, *weights = NULL;
       +        char *delimstr = "\t";
       +        const char *errstr;
                struct timeval t1;
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'h':
       -                usage();
       -                break;
       -        case 'n':
       -                n = atol(EARGF(usage()));
       -                break;
       -        case 'r':
       -                repeats = atol(EARGF(usage()));
       -                break;
       -        case 'R':
       -                ratio = 1;
       -                break;
       -        case 's':
       -                s = 1;
       -                seed = atol(EARGF(usage()));
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       -
       +        while ((ch = getopt(argc, argv, "d:nN:r:Rp:s:")) != -1) {
       +                switch (ch) {
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'N':
       +                        n = strtonum(optarg, 0, LONG_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad num value, %s: %s", errstr, optarg);
       +                        break;
       +                case 'r':
       +                        repeats = strtonum(optarg, 0, LONG_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad repeats value, %s: %s", errstr, optarg);
       +                        break;
       +                case 'R':
       +                        ratio = 1;
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, 0, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                case 's':
       +                        seed = strtonum(optarg, 0, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad seed value, %s: %s", errstr, optarg);
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        argc -= optind;
       +        argv += optind;
                if (argc < 1)
                        usage();
        
       -        N = argc;
       -        if (!(weights = calloc(N, sizeof(double))) ||
       -            !(counts = calloc(N, sizeof(long))))
       +        nbins = argc;
       +        if (!(weights = calloc(nbins, sizeof(double))) ||
       +            !(counts = calloc(nbins, sizeof(long))))
                        err(1, "calloc");
        
       -        for (i = 0; i < N; i++) {
       -                weights[i] = atof(argv[i]);
       +        for (i = 0; i < nbins; i++) {
       +                if (!sscanf(argv[i], "%lf", &weights[i]))
       +                        errx(1, "bad weight value: %s", argv[i]);
                        if (weights[i] <= 0.0)
                                errx(1, "weight %d is not positive (%g)", i, weights[i]);
                        if (weights[i] > 1.0)
                                errx(1, "weight %d is greater than 1 (%g)", i, weights[i]);
                }
        
       -        for (i = 0; i < N; i++)
       +        for (i = 0; i < nbins; i++)
                        weightsum += weights[i];
                if (fabs(weightsum - 1.0) > 1e-3)
                        errx(1, "weights do not sum to 1 (%g)", weightsum);
       t@@ -83,12 +100,12 @@ main(int argc, char *argv[])
        #endif
        
                for (r = 0; r < repeats; r++) {
       -                for (i = 0; i < N; i++)
       +                for (i = 0; i < nbins; i++)
                                counts[i] = 0;
                        for (j = 0; j < n; j++) {
                                val = drand48();
                                weightsum = 0.0;
       -                        for (i = 0; i < N; i++) {
       +                        for (i = 0; i < nbins; i++) {
                                        weightsum += weights[i];
                                        if (val <= weightsum) {
                                                counts[i]++;
       t@@ -96,15 +113,16 @@ main(int argc, char *argv[])
                                        }
                                }
                        }
       -                for (i = 0; i < N; i++) {
       +                for (i = 0; i < nbins; i++) {
                                if (ratio)
       -                                printf("%.17g", (double)counts[i] / n);
       +                                printf("%.*g", prec, (double)counts[i] / n);
                                else
                                        printf("%ld", counts[i]);
       -                        if (i < N - 1)
       -                                putchar('\t');
       +                        if (i < nbins - 1)
       +                                fputs(delimstr, stdout);
                                else
       -                                putchar('\n');
       +                                if (finalnl)
       +                                        putchar('\n');
                        }
                }
        
 (DIR) diff --git a/randnum.c b/randnum.c
       t@@ -20,9 +20,9 @@ usage(void)
        int
        main(int argc, char *argv[])
        {
       -        int i, ret, ch, s = 0, prec = 17, finalnl = 1;
       -        long j, seed, n = 1;
       -        double val, minv = 0.0, maxv = 1.0;
       +        int i, ch, s = 0, prec = 17, finalnl = 1;
       +        long seed, n = 1;
       +        double minv = 0.0, maxv = 1.0;
                char *delimstr = "\n";
                const char *errstr;
                struct timeval t1;
       t@@ -44,7 +44,7 @@ main(int argc, char *argv[])
                                        errx(1, "bad num value, %s: %s", errstr, optarg);
                                break;
                        case 'p':
       -                        prec = strtonum(optarg, -10, INT_MAX, &errstr);
       +                        prec = strtonum(optarg, 0, INT_MAX, &errstr);
                                if (errstr != NULL)
                                        errx(1, "bad precision value, %s: %s", errstr, optarg);
                                break;
 (DIR) diff --git a/range.1 b/range.1
       t@@ -7,11 +7,12 @@
        .Sh SYNOPSIS
        .Nm
        .Op Fl b
       +.Op Fl d Ar delimstr
        .Op Fl e
       -.Op Fl f Ar fmtstr
       -.Op Fl h
        .Op Fl l
       -.Op Fl n Ar num
       +.Op Fl n
       +.Op Fl N Ar num
       +.Op Fl p Ar prec
        .Op Fl s
        .Oo
        .Op Ar min_val
       t@@ -45,6 +46,10 @@ as the first value, making it a half-open interval
        .Ar max_val ],
        or an entirely open interval when combined with
        .Op Ar e .
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is newlines.
        .It Fl e
        Do not include
        .Ar max_val
       t@@ -53,54 +58,50 @@ as the last value, making it a half-open interval
        .Ar max_val [,
        or an entirely open interval when combined with
        .Op Ar b .
       -.It Fl f Ar fmtstr
       -Formatting string to use as documented in
       -.Xr printf 3 .
       -When including a format specifier (%..), only use forms that are
       -compatible with
       -.Vt double
       -types.
       -The default format string is '%.17g'.
       -.It Fl h
       -Show usage information.
        .It Fl l
        Produce output with even intervals in logarithmic space between 10^min_val
        and 10^max_val.
       -.It Fl n Ar num
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl N Ar num
        Number of values to produce within the specified range.
        The default is 10.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .It Fl s
       -Print the spacing between numbers and exit.
       +Print the numerical spacing between numbers and exit.
        .El
        .Sh EXAMPLES
        Generate four equally-spaced numbers in the closed default range [0;1]:
       -.Dl $ range -n 4 -f '%.17g\en' 0 1
       +.Dl $ range -N 4
        .Dl 0
        .Dl 0.33333333333333331
        .Dl 0.66666666666666663
        .Dl 1
        .Pp
        Generate four numbers in the range ]0;1[:
       -.Dl $ range -b -e -n 4 0 1
       -.Dl 0.2
       -.Dl 0.4
       -.Dl 0.6
       -.Dl 0.8
       +.Dl $ range -be -N 4
       +.Dl 0.20000000000000001
       +.Dl 0.40000000000000002
       +.Dl 0.60000000000000009
       +.Dl 0.80000000000000004
        .Pp
       -Repeat and modify a string three times:
       -.Dl $ range -n 3 -f 'The best number is %.0g' 1 3
       -.Dl The best number is 1
       -.Dl The best number is 2
       -.Dl The best number is 3
       +Generate three space-separated numbers:
       +.Dl $ range -d' ' -N 3 1 3
       +.Dl 1 2 3
        .Pp
        Generate three numbers evenly distributed in logspace from 10^0 to 10^2:
       -.Dl $ range -l -n 3 0 2
       +.Dl $ range -l -N 3 0 2
        .Dl 1
        .Dl 10
        .Dl 100
        .Pp
        Generate three numbers in the range [-2;-1]:
       -.Dl $ range -n 3 -- -2 -1
       +.Dl $ range -N 3 -- -2 -1
        .Dl -2
        .Dl -1.5
        .Dl -1
       t@@ -108,7 +109,7 @@ Generate three numbers in the range [-2;-1]:
        .Xr max 1 ,
        .Xr mean 1 ,
        .Xr min 1 ,
       -.Xr rangetest 1 ,
       -.Xr sum 1
       +.Xr randnum 1 ,
       +.Xr rangetest 1
        .Sh AUTHORS
        .An Anders Damsgaard Aq Mt anders@adamsgaard.dk
 (DIR) diff --git a/range.c b/range.c
       t@@ -4,78 +4,95 @@
        #include <limits.h>
        #include <string.h>
        #include <math.h>
       +#include <unistd.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-b] [-e] [-f fmtstr] [-h] [-l] [-n num] [-s] "
       -                "[[min_val] max_val]\n", argv0);
       +        errx(1, "usage: range [-b] [-d delimstr] [-e] [-l] [-n] [-N num] "
       +             "[-p prec] [-s] [[min_val] max_val]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int i, ret, n = 10, logrange = 0, openstart = 0, openend = 0,
       -                reportdx = 0;
       -        double minv = 0.0, maxv = 1.0, dx;
       -        char fmtstr[PATH_MAX] = "%.17g";
       +        int i, j, ch, n = 10, logrange = 0, openstart = 0, openend = 0,
       +                prec = 17, finalnl = 1, reportdx = 0;
       +        double minv = 0.0, maxv = 1.0, dx, val;
       +        const char *errstr;
       +        char *delimstr = "\n";
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'b':
       -                openstart = 1;
       -                break;
       -        case 'e':
       -                openend = 1;
       -                break;
       -        case 'f':
       -                ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage()));
       -                if (ret < 0 || (size_t)ret >= sizeof(fmtstr))
       -                        errx(1, "%s: could not write fmtstr", __func__);
       -                break;
       -        case 'h':
       -                usage();
       -                break;
       -        case 'l':
       -                logrange = 1;
       -                break;
       -        case 'n':
       -                n = atoi(EARGF(usage()));
       -                break;
       -        case 's':
       -                reportdx = 1;
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       -
       +        while ((ch = getopt(argc, argv, "bd:elnN:p:s")) != -1) {
       +                switch (ch) {
       +                case 'b':
       +                        openstart = 1;
       +                        break;
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'e':
       +                        openend = 1;
       +                        break;
       +                case 'l':
       +                        logrange = 1;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'N':
       +                        n = strtonum(optarg, 0, LONG_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad num value, %s: %s", errstr, optarg);
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, -10, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                case 's':
       +                        reportdx = 1;
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        argc -= optind;
       +        argv += optind;
                if (argc > 2)
                        usage();
                else if (argc == 2) {
       -                minv = atof(argv[0]);
       -                maxv = atof(argv[1]);
       +                if (!sscanf(argv[0], "%lf", &minv))
       +                        errx(1, "bad minv value: %s", argv[0]);
       +                if (!sscanf(argv[1], "%lf", &maxv))
       +                        errx(1, "bad maxv value: %s", argv[1]);
                } else if (argc == 1)
       -                maxv = atof(argv[0]);
       +                if (!sscanf(argv[0], "%lf", &maxv))
       +                        errx(1, "bad maxv value: %s", argv[0]);
        
                dx = (maxv - minv) / (n - 1 + openend + openstart);
                if (reportdx) {
       -                printf(fmtstr, dx);
       +                printf("%.*g", prec, dx);
       +                if (finalnl)
       +                        putchar('\n');
                        return 0;
                }
       -        for (i = 0 + openstart; i < n + openstart; i++) {
       +        for (i = 0; i < n; i++) {
       +                j = i + openstart;
                        if (logrange)
       -                        printf(fmtstr, pow(10, minv + i * dx));
       +                        val = pow(10, minv + j * dx);
                        else
       -                        printf(fmtstr, minv + i * dx);
       -                putchar('\n');
       +                        val = minv + j * dx;
       +                printf("%.*g", prec, val);
       +                if (i < n - 1)
       +                        fputs(delimstr, stdout);
                }
       +        if (finalnl)
       +                putchar('\n');
        
                return 0;
        }
 (DIR) diff --git a/rangetest.c b/rangetest.c
       t@@ -3,19 +3,17 @@
        #include <err.h>
        #include <limits.h>
        #include <string.h>
       +#include <unistd.h>
        
       -#include "arg.h"
        #include "util.h"
        
        #define VALUESTR "@VAL@"
        
       -char *argv0;
       -
        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);
       +        errx(1, "usage: rangetest [-n maxiter] cmd min_val max_val\n"
       +                "where cmd must contain the string '" VALUESTR "'");
        }
        
        static int
       t@@ -41,8 +39,7 @@ launch(char *cmd, char *cmd0, double val)
        static void
        binary_search(char *cmd, char *cmd0, double minv, double maxv, int maxiter)
        {
       -        int minfail, maxfail;
       -        int iter = 0;
       +        int minfail, maxfail, iter = 0;
                double val;
        
                minfail = launch(cmd, cmd0, minv);
       t@@ -76,23 +73,24 @@ binary_search(char *cmd, char *cmd0, double minv, double maxv, int maxiter)
        int
        main(int argc, char *argv[])
        {
       -        int maxiter = 10;
       +        int ch, maxiter = 10;
                double minv, maxv;
                char cmd0[PATH_MAX] = "", cmd[PATH_MAX] = "";
       +        const char *errstr;
        
       -        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;
       -
       +        while ((ch = getopt(argc, argv, "N:")) != -1) {
       +                switch (ch) {
       +                case 'N':
       +                        maxiter = strtonum(optarg, 0, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad maxiter value, %s: %s", errstr, optarg);
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        argc -= optind;
       +        argv += optind;
                if (argc == 3) {
                        if (strlcpy(cmd0, argv[0], sizeof(cmd0)) >= sizeof(cmd0))
                                err(1, "cmd too long");
 (DIR) diff --git a/stddev.1 b/stddev.1
       t@@ -6,8 +6,9 @@
        .Nd returns the standard deviation for each input column
        .Sh SYNOPSIS
        .Nm
       -.Op Fl f Ar fmtstr
       -.Op Fl h
       +.Op Fl d Ar delimstr
       +.Op Fl n
       +.Op Fl p Ar prec
        .Op Fl u
        .Sh DESCRIPTION
        .Nm
       t@@ -19,16 +20,18 @@ The output is always in full double precision.
        .Pp
        The options are as follows:
        .Bl -tag -width Ds
       -.It Fl f Ar fmtstr
       -Formatting string to use as documented in
       -.Xr printf 3 .
       -When including a format specifier (%..), only use forms that are
       -compatible with
       -.Vt double
       -types.
       -The default format string is '%.17g'.
       -.It Fl h
       -Show usage information and exit.
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is tab characters.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .It Fl u
        Return the uncorrected sample standard deviation instead.
        .El
 (DIR) diff --git a/stddev.c b/stddev.c
       t@@ -4,44 +4,50 @@
        #include <unistd.h>
        #include <math.h>
        #include <limits.h>
       +#include <unistd.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-f fmtstr] [-h] [-u]\n", argv0);
       +        errx(1, "usage: stddev [-d delimstr] [-n] [-p prec] [-u]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int ret;
       +        int ch, prec = 17, finalnl = 1;
                size_t i, j, nf = 0, nr = 0, correction = 1;
                double *means = NULL, *stdvals = NULL, **vals = NULL;
       -        char fmtstr[PATH_MAX] = "%.17g";
       +        const char *errstr;
       +        char *delimstr = "\t";
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'f':
       -                ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage()));
       -                if (ret < 0 || (size_t)ret >= sizeof(fmtstr))
       -                        errx(1, "%s: could not write fmtstr", __func__);
       -                break;
       -        case 'h':
       -                usage();
       -                break;
       -        case 'u':
       -                correction = 0;
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       +        while ((ch = getopt(argc, argv, "d:np:u")) != -1) {
       +                switch (ch) {
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, -10, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                case 'u':
       +                        correction = 0;
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        /*argc -= optind;*/
       +        /*argv += optind;*/
        
                nr = fscanmatrix(stdin, &vals, &nf);
        
       t@@ -63,7 +69,9 @@ main(int argc, char *argv[])
                        stdvals[i] = sqrt(stdvals[i] / ((double)(nr - correction)));
                }
        
       -        printfarr(fmtstr, stdvals, nf);
       +        printfarr(delimstr, prec, stdvals, nf);
       +        if (finalnl)
       +                putchar('\n');
        
                free(means);
                free(stdvals);
 (DIR) diff --git a/stdvar.1 b/stdvar.1
       t@@ -6,8 +6,9 @@
        .Nd returns the standard variance for each input column
        .Sh SYNOPSIS
        .Nm
       -.Op Fl f Ar fmtstr
       -.Op Fl h
       +.Op Fl d Ar delimstr
       +.Op Fl n
       +.Op Fl p Ar prec
        .Op Fl u
        .Sh DESCRIPTION
        .Nm
       t@@ -19,18 +20,20 @@ The output is always in full double precision.
        .Pp
        The options are as follows:
        .Bl -tag -width Ds
       -.It Fl f Ar fmtstr
       -Formatting string to use as documented in
       -.Xr printf 3 .
       -When including a format specifier (%..), only use forms that are
       -compatible with
       -.Vt double
       -types.
       -The default format string is '%.17g'.
       -.It Fl h
       -Show usage information and exit.
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is tab characters.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .It Fl u
       -Return the uncorrected sample standard variance instead.
       +Return the uncorrected sample standard deviation instead.
        .El
        .Sh EXAMPLES
        Compute the corrected standard variance for some input numbers:
 (DIR) diff --git a/stdvar.c b/stdvar.c
       t@@ -4,44 +4,50 @@
        #include <unistd.h>
        #include <math.h>
        #include <limits.h>
       +#include <unistd.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-f fmtstr] [-h] [-u]\n", argv0);
       +        errx(1, "usage: stdvar [-d delimstr] [-n] [-p prec] [-u]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int ret;
       +        int ch, prec = 17, finalnl = 1;
                size_t i, j, nf = 0, nr = 0, correction = 1;
                double *means = NULL, *stdvars = NULL, **vals = NULL;
       -        char fmtstr[PATH_MAX] = "%.17g";
       +        const char *errstr;
       +        char *delimstr = "\t";
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'f':
       -                ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage()));
       -                if (ret < 0 || (size_t)ret >= sizeof(fmtstr))
       -                        errx(1, "%s: could not write fmtstr", __func__);
       -                break;
       -        case 'h':
       -                usage();
       -                break;
       -        case 'u':
       -                correction = 0;
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       +        while ((ch = getopt(argc, argv, "d:np:u")) != -1) {
       +                switch (ch) {
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, -10, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                case 'u':
       +                        correction = 0;
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        /*argc -= optind;*/
       +        /*argv += optind;*/
        
                nr = fscanmatrix(stdin, &vals, &nf);
        
       t@@ -63,7 +69,9 @@ main(int argc, char *argv[])
                        stdvars[i] /= (double)(nr - correction);
                }
        
       -        printfarr(fmtstr, stdvars, nf);
       +        printfarr(delimstr, prec, stdvars, nf);
       +        if (finalnl)
       +                putchar('\n');
        
                free(means);
                free(stdvars);
 (DIR) diff --git a/sum.1 b/sum.1
       t@@ -6,8 +6,9 @@
        .Nd returns the sum for each column
        .Sh SYNOPSIS
        .Nm
       -.Op Fl f Ar fmtstr
       -.Op Fl h
       +.Op Fl d Ar delimstr
       +.Op Fl n
       +.Op Fl p Ar prec
        .Sh DESCRIPTION
        .Nm
        returns the numerical sum for each column in standard input.
       t@@ -16,20 +17,22 @@ same number of fields.
        .Pp
        The options are as follows:
        .Bl -tag -width Ds
       -.It Fl f Ar fmtstr
       -Formatting string to use as documented in
       -.Xr printf 3 .
       -When including a format specifier (%..), only use forms that are
       -compatible with
       -.Vt double
       -types.
       -The default format string is '%.17g'.
       -.It Fl h
       -Show usage information and exit.
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is tab characters.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl p prec
       +Print the output values with
       +.Ar prec
       +digits of precision.
       +By default, the output is printed with 17 digits of precision, which is
       +full double precision on 64-bit systems.
        .El
        .Sh EXAMPLES
        .Dl $ printf '1\et2\et3\en4\et5\et6\en' | sum
       -.Dl 5        7        9
       +.Dl 5   7   9
        .Sh SEE ALSO
        .Xr max 1 ,
        .Xr mean 1 ,
 (DIR) diff --git a/sum.c b/sum.c
       t@@ -4,40 +4,45 @@
        #include <unistd.h>
        #include <limits.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-f fmtstr] [-h] ", argv0);
       +        errx(1, "usage: sum [-d delimstr] [-n] [-p prec]");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int ret;
       +        int ch, prec = 17, finalnl = 1;
                size_t i = 0, nf = 0, nr = 0, linesize = 0;
       -        char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g";
       +        char *line = NULL, *data = NULL, *delimstr = "\t";
       +        const char *errstr;
                double val, *vals = NULL;
        
                if (pledge("stdio", NULL) == -1)
                        err(2, "pledge");
        
       -        ARGBEGIN {
       -        case 'f':
       -                ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage()));
       -                if (ret < 0 || (size_t)ret >= sizeof(fmtstr))
       -                        errx(1, "%s: could not write fmtstr", __func__);
       -                break;
       -        case 'h':
       -                usage();
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       +        while ((ch = getopt(argc, argv, "d:np:")) != -1) {
       +                switch (ch) {
       +                case 'd':
       +                        delimstr = optarg;
       +                        break;
       +                case 'n':
       +                        finalnl = 0;
       +                        break;
       +                case 'p':
       +                        prec = strtonum(optarg, 0, INT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "bad precision value, %s: %s", errstr, optarg);
       +                        break;
       +                default:
       +                        usage();
       +                }
       +        }
       +        argc -= optind;
       +        /*argv += optind;*/
                if (argc > 0)
                        usage();
        
       t@@ -55,7 +60,9 @@ main(int argc, char *argv[])
                        }
                        nr++;
                }
       -        printfarr(fmtstr, vals, nf);
       +        printfarr(delimstr, prec, vals, nf);
       +        if (finalnl)
       +                putchar('\n');
        
                free(line);
                free(vals);
 (DIR) diff --git a/transpose.1 b/transpose.1
       t@@ -16,9 +16,9 @@ Input fields must be tab-separated and each line must contain the
        same number of fields.
        .Sh EXAMPLES
        .Dl $ printf '1\et2\et3\en4\et5\et6\en' | transpose
       -.Dl 1        4
       -.Dl 2        5
       -.Dl 3        6
       +.Dl 1   4
       +.Dl 2   5
       +.Dl 3   6
        .Sh SEE ALSO
        .Xr max 1 ,
        .Xr mean 1 ,
 (DIR) diff --git a/util.c b/util.c
       t@@ -45,16 +45,15 @@ printarr(double *arr, size_t len)
        }
        
        void
       -printfarr(char *fmtstr, double *arr, size_t len)
       +printfarr(char *delimstr, int prec, double *arr, size_t len)
        {
                size_t i;
        
                for (i = 0; i < len; i++) {
       -                printf(fmtstr, arr[i]);
       -                if (i < len)
       -                        printf(DELIMSTR);
       +                printf("%.*g", prec, arr[i]);
       +                if (i < len - 1)
       +                        fputs(delimstr, stdout);
                }
       -        putchar('\n');
        }
        
        size_t
 (DIR) diff --git a/util.h b/util.h
       t@@ -26,7 +26,7 @@ void * xreallocarray(void *m, size_t n, size_t s);
        size_t allocarr(double **arr, const char *str, size_t maxlen);
        int scannextval(char **str, double *val);
        void printarr(double *arr, size_t len);
       -void printfarr(char *fmtstr, double *arr, size_t len);
       +void printfarr(char *delimstr, int prec, double *arr, size_t len);
        size_t fscanmatrix(FILE *stream, double ***arr, size_t *nf);
        
        #endif