d129c68a14
Up to now jot would fail to generate the last character in the range or skew the integer distribution in a way that would generate the numbers in the range's limits with half the probability of the rest. This modification fixes the program, rather than documenting the strange behavior, as suggested in docs/54879. Also, correctly specify the range of random(3). PR: docs/54879 MFC after: 2 weeks
466 lines
11 KiB
C
466 lines
11 KiB
C
/*-
|
|
* Copyright (c) 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static const char copyright[] =
|
|
"@(#) Copyright (c) 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n";
|
|
#endif /* not lint */
|
|
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93";
|
|
#endif
|
|
#endif
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/*
|
|
* jot - print sequential or random data
|
|
*
|
|
* Author: John Kunze, Office of Comp. Affairs, UCB
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define REPS_DEF 100
|
|
#define BEGIN_DEF 1
|
|
#define ENDER_DEF 100
|
|
#define STEP_DEF 1
|
|
|
|
#define HAVE_STEP 1
|
|
#define HAVE_ENDER 2
|
|
#define HAVE_BEGIN 4
|
|
#define HAVE_REPS 8
|
|
|
|
#define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0)
|
|
|
|
double begin;
|
|
double ender;
|
|
double s;
|
|
long reps;
|
|
int randomize;
|
|
int infinity;
|
|
int boring;
|
|
int prec;
|
|
int longdata;
|
|
int intdata;
|
|
int chardata;
|
|
int nosign;
|
|
int nofinalnl;
|
|
const char *sepstring = "\n";
|
|
char format[BUFSIZ];
|
|
|
|
void getformat(void);
|
|
int getprec(char *);
|
|
int putdata(double, long);
|
|
static void usage(void);
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
double x, y;
|
|
long i;
|
|
unsigned int mask = 0;
|
|
int n = 0;
|
|
int ch;
|
|
bool use_random = false;
|
|
bool have_format = false;
|
|
double divisor;
|
|
|
|
while ((ch = getopt(argc, argv, "rb:w:cs:np:")) != -1)
|
|
switch (ch) {
|
|
case 'r':
|
|
randomize = 1;
|
|
break;
|
|
case 'c':
|
|
chardata = 1;
|
|
break;
|
|
case 'n':
|
|
nofinalnl = 1;
|
|
break;
|
|
case 'b':
|
|
boring = 1;
|
|
/* FALLTHROUGH */
|
|
case 'w':
|
|
if (strlcpy(format, optarg, sizeof(format)) >=
|
|
sizeof(format))
|
|
errx(1, "-%c word too long", ch);
|
|
have_format = true;
|
|
break;
|
|
case 's':
|
|
sepstring = optarg;
|
|
break;
|
|
case 'p':
|
|
prec = atoi(optarg);
|
|
if (prec <= 0)
|
|
errx(1, "bad precision value");
|
|
have_format = true;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
switch (argc) { /* examine args right to left, falling thru cases */
|
|
case 4:
|
|
if (!is_default(argv[3])) {
|
|
if (!sscanf(argv[3], "%lf", &s))
|
|
errx(1, "bad s value: %s", argv[3]);
|
|
mask |= HAVE_STEP;
|
|
if (randomize)
|
|
use_random = true;
|
|
}
|
|
/* FALLTHROUGH */
|
|
case 3:
|
|
if (!is_default(argv[2])) {
|
|
if (!sscanf(argv[2], "%lf", &ender))
|
|
ender = argv[2][strlen(argv[2])-1];
|
|
mask |= HAVE_ENDER;
|
|
if (!prec)
|
|
n = getprec(argv[2]);
|
|
}
|
|
/* FALLTHROUGH */
|
|
case 2:
|
|
if (!is_default(argv[1])) {
|
|
if (!sscanf(argv[1], "%lf", &begin))
|
|
begin = argv[1][strlen(argv[1])-1];
|
|
mask |= HAVE_BEGIN;
|
|
if (!prec)
|
|
prec = getprec(argv[1]);
|
|
if (n > prec) /* maximum precision */
|
|
prec = n;
|
|
}
|
|
/* FALLTHROUGH */
|
|
case 1:
|
|
if (!is_default(argv[0])) {
|
|
if (!sscanf(argv[0], "%ld", &reps))
|
|
errx(1, "bad reps value: %s", argv[0]);
|
|
mask |= HAVE_REPS;
|
|
}
|
|
break;
|
|
case 0:
|
|
usage();
|
|
default:
|
|
errx(1, "too many arguments. What do you mean by %s?",
|
|
argv[4]);
|
|
}
|
|
getformat();
|
|
while (mask) /* 4 bit mask has 1's where last 4 args were given */
|
|
switch (mask) { /* fill in the 0's by default or computation */
|
|
case HAVE_STEP:
|
|
case HAVE_ENDER:
|
|
case HAVE_ENDER | HAVE_STEP:
|
|
case HAVE_BEGIN:
|
|
case HAVE_BEGIN | HAVE_STEP:
|
|
case HAVE_BEGIN | HAVE_ENDER:
|
|
reps = REPS_DEF;
|
|
mask |= HAVE_REPS;
|
|
break;
|
|
case HAVE_BEGIN | HAVE_ENDER | HAVE_STEP:
|
|
if (randomize)
|
|
reps = REPS_DEF;
|
|
else if (s == 0.0)
|
|
reps = 0;
|
|
else
|
|
reps = (ender - begin + s) / s;
|
|
if (reps <= 0)
|
|
errx(1, "impossible stepsize");
|
|
mask = 0;
|
|
break;
|
|
case HAVE_REPS:
|
|
case HAVE_REPS | HAVE_STEP:
|
|
begin = BEGIN_DEF;
|
|
mask |= HAVE_BEGIN;
|
|
break;
|
|
case HAVE_REPS | HAVE_ENDER:
|
|
s = STEP_DEF;
|
|
mask = HAVE_REPS | HAVE_ENDER | HAVE_STEP;
|
|
break;
|
|
case HAVE_REPS | HAVE_ENDER | HAVE_STEP:
|
|
if (randomize)
|
|
begin = BEGIN_DEF;
|
|
else if (reps == 0)
|
|
errx(1, "must specify begin if reps == 0");
|
|
begin = ender - reps * s + s;
|
|
mask = 0;
|
|
break;
|
|
case HAVE_REPS | HAVE_BEGIN:
|
|
s = STEP_DEF;
|
|
mask = HAVE_REPS | HAVE_BEGIN | HAVE_STEP;
|
|
break;
|
|
case HAVE_REPS | HAVE_BEGIN | HAVE_STEP:
|
|
if (randomize)
|
|
ender = ENDER_DEF;
|
|
else
|
|
ender = begin + reps * s - s;
|
|
mask = 0;
|
|
break;
|
|
case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER:
|
|
if (reps == 0)
|
|
errx(1, "infinite sequences cannot be bounded");
|
|
else if (reps == 1)
|
|
s = 0.0;
|
|
else
|
|
s = (ender - begin) / (reps - 1);
|
|
mask = 0;
|
|
break;
|
|
case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER | HAVE_STEP:
|
|
/* if reps given and implied, */
|
|
if (!randomize && s != 0.0) {
|
|
long t = (ender - begin + s) / s;
|
|
if (t <= 0)
|
|
errx(1, "impossible stepsize");
|
|
if (t < reps) /* take lesser */
|
|
reps = t;
|
|
}
|
|
mask = 0;
|
|
break;
|
|
default:
|
|
errx(1, "bad mask");
|
|
}
|
|
if (reps == 0)
|
|
infinity = 1;
|
|
if (randomize) {
|
|
if (use_random) {
|
|
srandom((unsigned long)s);
|
|
divisor = (double)INT32_MAX + 1;
|
|
} else
|
|
divisor = (double)UINT32_MAX + 1;
|
|
|
|
/*
|
|
* Attempt to DWIM when the user has specified an
|
|
* integer range within that of the random number
|
|
* generator: distribute the numbers equally in
|
|
* the range [begin .. ender]. Jot's default %.0f
|
|
* format would make the appearance of the first and
|
|
* last specified value half as likely as the rest.
|
|
*/
|
|
if (!have_format && prec == 0 &&
|
|
begin >= 0 && begin < divisor &&
|
|
ender >= 0 && ender < divisor) {
|
|
ender += 1;
|
|
nosign = 1;
|
|
intdata = 1;
|
|
(void)strlcpy(format,
|
|
chardata ? "%c" : "%u", sizeof(format));
|
|
}
|
|
x = (ender - begin) * (ender > begin ? 1 : -1);
|
|
for (i = 1; i <= reps || infinity; i++) {
|
|
if (use_random)
|
|
y = random() / divisor;
|
|
else
|
|
y = arc4random() / divisor;
|
|
if (putdata(y * x + begin, reps - i))
|
|
errx(1, "range error in conversion");
|
|
}
|
|
} else
|
|
for (i = 1, x = begin; i <= reps || infinity; i++, x += s)
|
|
if (putdata(x, reps - i))
|
|
errx(1, "range error in conversion");
|
|
if (!nofinalnl)
|
|
putchar('\n');
|
|
exit(0);
|
|
}
|
|
|
|
int
|
|
putdata(double x, long int notlast)
|
|
{
|
|
|
|
if (boring)
|
|
printf("%s", format);
|
|
else if (longdata && nosign) {
|
|
if (x <= (double)ULONG_MAX && x >= (double)0)
|
|
printf(format, (unsigned long)x);
|
|
else
|
|
return (1);
|
|
} else if (longdata) {
|
|
if (x <= (double)LONG_MAX && x >= (double)LONG_MIN)
|
|
printf(format, (long)x);
|
|
else
|
|
return (1);
|
|
} else if (chardata || (intdata && !nosign)) {
|
|
if (x <= (double)INT_MAX && x >= (double)INT_MIN)
|
|
printf(format, (int)x);
|
|
else
|
|
return (1);
|
|
} else if (intdata) {
|
|
if (x <= (double)UINT_MAX && x >= (double)0)
|
|
printf(format, (unsigned int)x);
|
|
else
|
|
return (1);
|
|
|
|
} else
|
|
printf(format, x);
|
|
if (notlast != 0)
|
|
fputs(sepstring, stdout);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "%s\n%s\n",
|
|
"usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]",
|
|
" [reps [begin [end [s]]]]");
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
getprec(char *str)
|
|
{
|
|
char *p;
|
|
char *q;
|
|
|
|
for (p = str; *p; p++)
|
|
if (*p == '.')
|
|
break;
|
|
if (!*p)
|
|
return (0);
|
|
for (q = ++p; *p; p++)
|
|
if (!isdigit((unsigned char)*p))
|
|
break;
|
|
return (p - q);
|
|
}
|
|
|
|
void
|
|
getformat(void)
|
|
{
|
|
char *p, *p2;
|
|
int dot, hash, space, sign, numbers = 0;
|
|
size_t sz;
|
|
|
|
if (boring) /* no need to bother */
|
|
return;
|
|
for (p = format; *p; p++) /* look for '%' */
|
|
if (*p == '%' && *(p+1) != '%') /* leave %% alone */
|
|
break;
|
|
sz = sizeof(format) - strlen(format) - 1;
|
|
if (!*p && !chardata) {
|
|
if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
|
|
errx(1, "-w word too long");
|
|
} else if (!*p && chardata) {
|
|
if (strlcpy(p, "%c", sz) >= sz)
|
|
errx(1, "-w word too long");
|
|
intdata = 1;
|
|
} else if (!*(p+1)) {
|
|
if (sz <= 0)
|
|
errx(1, "-w word too long");
|
|
strcat(format, "%"); /* cannot end in single '%' */
|
|
} else {
|
|
/*
|
|
* Allow conversion format specifiers of the form
|
|
* %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of
|
|
* [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u}
|
|
*/
|
|
p2 = p++;
|
|
dot = hash = space = sign = numbers = 0;
|
|
while (!isalpha((unsigned char)*p)) {
|
|
if (isdigit((unsigned char)*p)) {
|
|
numbers++;
|
|
p++;
|
|
} else if ((*p == '#' && !(numbers|dot|sign|space|
|
|
hash++)) ||
|
|
(*p == ' ' && !(numbers|dot|space++)) ||
|
|
((*p == '+' || *p == '-') && !(numbers|dot|sign++))
|
|
|| (*p == '.' && !(dot++)))
|
|
p++;
|
|
else
|
|
goto fmt_broken;
|
|
}
|
|
if (*p == 'l') {
|
|
longdata = 1;
|
|
if (*++p == 'l') {
|
|
if (p[1] != '\0')
|
|
p++;
|
|
goto fmt_broken;
|
|
}
|
|
}
|
|
switch (*p) {
|
|
case 'o': case 'u': case 'x': case 'X':
|
|
intdata = nosign = 1;
|
|
break;
|
|
case 'd': case 'i':
|
|
intdata = 1;
|
|
break;
|
|
case 'D':
|
|
if (!longdata) {
|
|
intdata = 1;
|
|
break;
|
|
}
|
|
case 'O': case 'U':
|
|
if (!longdata) {
|
|
intdata = nosign = 1;
|
|
break;
|
|
}
|
|
case 'c':
|
|
if (!(intdata | longdata)) {
|
|
chardata = 1;
|
|
break;
|
|
}
|
|
case 'h': case 'n': case 'p': case 'q': case 's': case 'L':
|
|
case '$': case '*':
|
|
goto fmt_broken;
|
|
case 'f': case 'e': case 'g': case 'E': case 'G':
|
|
if (!longdata)
|
|
break;
|
|
/* FALLTHROUGH */
|
|
default:
|
|
fmt_broken:
|
|
*++p = '\0';
|
|
errx(1, "illegal or unsupported format '%s'", p2);
|
|
/* NOTREACHED */
|
|
}
|
|
while (*++p)
|
|
if (*p == '%' && *(p+1) && *(p+1) != '%')
|
|
errx(1, "too many conversions");
|
|
else if (*p == '%' && *(p+1) == '%')
|
|
p++;
|
|
else if (*p == '%' && !*(p+1)) {
|
|
strcat(format, "%");
|
|
break;
|
|
}
|
|
}
|
|
}
|