commit 258495d0a32a728ba75d8569df12b7989d731646
parent 76946865659abbf6fb22ab476a2c6075aed14a99
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(-)
diff --git a/randnum.1 b/randnum.1
@@ -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
@@ -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.
@@ -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 ,
diff --git a/randnum.c b/randnum.c
@@ -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__
@@ -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;
}