trandnum(1): use getopt(2) and safe formatting specs - 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 258495d0a32a728ba75d8569df12b7989d731646
 (DIR) parent 76946865659abbf6fb22ab476a2c6075aed14a99
 (HTM) Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Wed, 11 May 2022 11:58:44 +0200
       
       randnum(1): use getopt(2) and safe formatting specs
       
       Diffstat:
         M randnum.1                           |      43 +++++++++++++++----------------
         M randnum.c                           |      78 +++++++++++++++++++------------
       
       2 files changed, 68 insertions(+), 53 deletions(-)
       ---
 (DIR) diff --git a/randnum.1 b/randnum.1
       t@@ -6,9 +6,10 @@
        .Nd produces random numbers in a range
        .Sh SYNOPSIS
        .Nm
       -.Op Fl f Ar fmtstr
       -.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 s Ar seed
        .Oo
        .Op Ar min_val
       t@@ -40,19 +41,20 @@ within the same microsecond will produce the same result.
        .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.
       -.It Fl n Ar num
       +.It Fl d Ar delimstr
       +Separate output values by
       +.Ar delimstr .
       +The default delimiter is newlines.
       +.It Fl n
       +Do not print a newline after the final value.
       +.It Fl N num
        Number of random points to generate.
       -The default is 1.
       +.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 Ar seed
        Seed the pseudo-random number generator with this value, which is used
        to generate reproducable binning.
       t@@ -63,13 +65,10 @@ Due to the randomness, your output may differ:
        .Dl $ randnum
        .Dl 0.38385568287140259
        .Pp
       -Generate five points in the range [-10;10[ and print with three significant digits:
       -.Dl $ randnum -n 5 -f '%.3g' -- -10 10
       -.Dl -4.16
       -.Dl -3.36
       -.Dl -5.8
       -.Dl -2.31
       -.Dl 4.4
       +Generate five points in the range [-10;10[ and print with three
       +significant digits seperated by spaces:
       +.Dl $ randnum -N 5 -p 3 -d ' ' -- -10 10
       +.Dl -5.52 -5.5 -3.88 3.49 -3.11
        .Sh SEE ALSO
        .Xr randcounts 1 ,
        .Xr range 1 ,
 (DIR) diff --git a/randnum.c b/randnum.c
       t@@ -4,59 +4,72 @@
        #include <limits.h>
        #include <string.h>
        #include <math.h>
       +#include <unistd.h>
        #include <sys/time.h>
        
       -#include "arg.h"
        #include "util.h"
        
       -char *argv0;
       -
        static void
        usage(void)
        {
       -        errx(1, "usage: %s [-f fmtstr] [-h] [-n num] [-s seed] "
       -                "[[min_val] max_val]\n", argv0);
       +        errx(1, "usage: randnum [-d delimstr] [-n] [-N num] "
       +                "[-p prec] [-s seed] "
       +                "[[min_val] max_val]\n");
        }
        
        int
        main(int argc, char *argv[])
        {
       -        int i, ret, s = 0;
       +        int i, ret, ch, s = 0, prec = 17, finalnl = 1;
                long j, seed, n = 1;
                double val, minv = 0.0, maxv = 1.0;
       -        char fmtstr[PATH_MAX] = "%.17g";
       +        char *delimstr = "\n";
       +        const char *errstr;
                struct timeval t1;
        
                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 'n':
       -                n = atoi(EARGF(usage()));
       -                break;
       -        case 's':
       -                s = 1;
       -                seed = atol(EARGF(usage()));
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       +        while ((ch = getopt(argc, argv, "d:nN:p: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 'p':
       +                        prec = strtonum(optarg, -10, 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 > 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]);
        
                if (s)
        #ifdef __OpenBSD__
       t@@ -70,9 +83,12 @@ main(int argc, char *argv[])
        #endif
        
                for (i = 0; i < n; i++) {
       -                printf(fmtstr, drand48() * (maxv - minv) + minv);
       -                putchar('\n');
       +                printf("%.*g", prec, drand48() * (maxv - minv) + minv);
       +                if (i < n - 1)
       +                        fputs(delimstr, stdout);
                }
       +        if (finalnl)
       +                putchar('\n');
        
                return 0;
        }