xref: /netbsd-src/usr.bin/jot/jot.c (revision 5dc82c03ce99695d67af1c2ddabb6cfdc55e56f3)
1*5dc82c03Skamil /*	$NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil Exp $	*/
26e0c4dedSjtc 
39636069cSjtc /*-
49636069cSjtc  * Copyright (c) 1993
59636069cSjtc  *	The Regents of the University of California.  All rights reserved.
69636069cSjtc  *
79636069cSjtc  * Redistribution and use in source and binary forms, with or without
89636069cSjtc  * modification, are permitted provided that the following conditions
99636069cSjtc  * are met:
109636069cSjtc  * 1. Redistributions of source code must retain the above copyright
119636069cSjtc  *    notice, this list of conditions and the following disclaimer.
129636069cSjtc  * 2. Redistributions in binary form must reproduce the above copyright
139636069cSjtc  *    notice, this list of conditions and the following disclaimer in the
149636069cSjtc  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
169636069cSjtc  *    may be used to endorse or promote products derived from this software
179636069cSjtc  *    without specific prior written permission.
189636069cSjtc  *
199636069cSjtc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209636069cSjtc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219636069cSjtc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229636069cSjtc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239636069cSjtc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249636069cSjtc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259636069cSjtc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269636069cSjtc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279636069cSjtc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289636069cSjtc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299636069cSjtc  * SUCH DAMAGE.
309636069cSjtc  */
319636069cSjtc 
32ac4e9aa7Slukem #include <sys/cdefs.h>
339636069cSjtc #ifndef lint
3498e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1993\
3598e5374cSlukem  The Regents of the University of California.  All rights reserved.");
369636069cSjtc #endif /* not lint */
379636069cSjtc 
389636069cSjtc #ifndef lint
396e0c4dedSjtc #if 0
409636069cSjtc static char sccsid[] = "@(#)jot.c	8.1 (Berkeley) 6/6/93";
416e0c4dedSjtc #endif
42*5dc82c03Skamil __RCSID("$NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil Exp $");
439636069cSjtc #endif /* not lint */
449636069cSjtc 
459636069cSjtc /*
469636069cSjtc  * jot - print sequential or random data
479636069cSjtc  *
489636069cSjtc  * Author:  John Kunze, Office of Comp. Affairs, UCB
499636069cSjtc  */
509636069cSjtc 
519636069cSjtc #include <ctype.h>
52ac4e9aa7Slukem #include <err.h>
539636069cSjtc #include <limits.h>
54d8817d5aSgarbled #include <math.h>
559636069cSjtc #include <stdio.h>
569636069cSjtc #include <stdlib.h>
579636069cSjtc #include <string.h>
589636069cSjtc #include <time.h>
593db0e47bSatatat #include <unistd.h>
609636069cSjtc 
619636069cSjtc #define	REPS_DEF	100
629636069cSjtc #define	BEGIN_DEF	1
639636069cSjtc #define	ENDER_DEF	100
649636069cSjtc #define	STEP_DEF	1
659636069cSjtc 
66507cbfd5Sjdolecek #define	is_default(s)	(strcmp((s), "-") == 0)
679636069cSjtc 
6893190b89Sdsl static double	begin = BEGIN_DEF;
6993190b89Sdsl static double	ender = ENDER_DEF;
7093190b89Sdsl static double	step = STEP_DEF;
7193190b89Sdsl static long	reps = REPS_DEF;
7293190b89Sdsl static int	randomize;
7393190b89Sdsl static int	boring;
7493190b89Sdsl static int	prec = -1;
7593190b89Sdsl static int	dox;
7693190b89Sdsl static int	chardata;
7793190b89Sdsl static int	nofinalnl;
7893190b89Sdsl static const char *sepstring = "\n";
7993190b89Sdsl static char	format[BUFSIZ];
809636069cSjtc 
81f127a329Sdsl static void	getargs(int, char *[]);
82f127a329Sdsl static void	getformat(void);
83f127a329Sdsl static int	getprec(char *);
84f127a329Sdsl static void	putdata(double, long);
85f127a329Sdsl static void	usage(void) __dead;
869636069cSjtc 
879636069cSjtc int
main(int argc,char * argv[])8810e955c2Sperry main(int argc, char *argv[])
899636069cSjtc {
901b00238dSdsl 	double	x;
91c4d8b28dSdsl 	long	i;
929636069cSjtc 
939636069cSjtc 	getargs(argc, argv);
949636069cSjtc 	if (randomize) {
951b00238dSdsl 		x = ender - begin;
969890613cSdsl 		if (x < 0) {
979890613cSdsl 			x = -x;
989890613cSdsl 			begin = ender;
999890613cSdsl 		}
1001b00238dSdsl 		if (dox == 0)
1011b00238dSdsl 			/*
1021b00238dSdsl 			 * We are printing floating point, generate random
1031b00238dSdsl 			 * number that include both supplied limits.
1041b00238dSdsl 			 * Due to FP routing for display the low and high
1051b00238dSdsl 			 * values are likely to occur half as often as all
1061b00238dSdsl 			 * the others.
1071b00238dSdsl 			 */
1081b00238dSdsl 			x /= (1u << 31) - 1.0;
1091b00238dSdsl 		else {
1101b00238dSdsl 			/*
1111b00238dSdsl 			 * We are printing integers increase the range by
1121b00238dSdsl 			 * one but ensure we never generate it.
1131b00238dSdsl 			 * This makes all the integer values equally likely.
1141b00238dSdsl 			 */
1151b00238dSdsl 			x += 1.0;
1161b00238dSdsl 			x /= (1u << 31);
1179636069cSjtc 		}
1181b00238dSdsl 		srandom((unsigned long) step);
1191b00238dSdsl 		for (i = 1; i <= reps || reps == 0; i++)
1201b00238dSdsl 			putdata(random() * x + begin, reps - i);
121f127a329Sdsl 	} else {
1221b00238dSdsl 		/*
1231b00238dSdsl 		 * If we are going to display as integer, add 0.5 here
1241b00238dSdsl 		 * and use floor(x) later to get sane rounding.
1251b00238dSdsl 		 */
1261b00238dSdsl 		x = begin;
1271b00238dSdsl 		if (dox)
1281b00238dSdsl 			x += 0.5;
1291b00238dSdsl 		for (i = 1; i <= reps || reps == 0; i++, x += step)
130c4d8b28dSdsl 			putdata(x, reps - i);
131f127a329Sdsl 	}
1329636069cSjtc 	if (!nofinalnl)
1339636069cSjtc 		putchar('\n');
1349636069cSjtc 	exit(0);
1359636069cSjtc }
1369636069cSjtc 
137f127a329Sdsl static void
getargs(int argc,char * argv[])13810e955c2Sperry getargs(int argc, char *argv[])
1399636069cSjtc {
14093190b89Sdsl 	unsigned int have = 0;
14193190b89Sdsl #define BEGIN	1
14293190b89Sdsl #define	STEP	2	/* seed if -r */
14393190b89Sdsl #define REPS	4
14493190b89Sdsl #define	ENDER	8
145ac4e9aa7Slukem 	int n = 0;
14693190b89Sdsl 	long t;
147f127a329Sdsl 	char *ep;
1489636069cSjtc 
149f127a329Sdsl 	for (;;) {
150f127a329Sdsl 		switch (getopt(argc, argv, "b:cnp:rs:w:")) {
151f127a329Sdsl 		default:
152f127a329Sdsl 			usage();
153f127a329Sdsl 		case -1:
1549636069cSjtc 			break;
1559636069cSjtc 		case 'c':
1569636069cSjtc 			chardata = 1;
157f127a329Sdsl 			continue;
1589636069cSjtc 		case 'n':
1599636069cSjtc 			nofinalnl = 1;
160f127a329Sdsl 			continue;
161f127a329Sdsl 		case 'p':
162f127a329Sdsl 			prec = strtol(optarg, &ep, 0);
163f127a329Sdsl 			if (*ep != 0 || prec < 0)
164f127a329Sdsl 				errx(EXIT_FAILURE, "Bad precision value");
165f127a329Sdsl 			continue;
166f127a329Sdsl 		case 'r':
167f127a329Sdsl 			randomize = 1;
168f127a329Sdsl 			continue;
169f127a329Sdsl 		case 's':
170f127a329Sdsl 			sepstring = optarg;
171f127a329Sdsl 			continue;
1729636069cSjtc 		case 'b':
1739636069cSjtc 			boring = 1;
174f127a329Sdsl 			/* FALLTHROUGH */
1759636069cSjtc 		case 'w':
176f127a329Sdsl 			strlcpy(format, optarg, sizeof(format));
177f127a329Sdsl 			continue;
1789636069cSjtc 		}
179f127a329Sdsl 		break;
180c4d8b28dSdsl 	}
181f127a329Sdsl 	argc -= optind;
182f127a329Sdsl 	argv += optind;
1839636069cSjtc 
184507cbfd5Sjdolecek 	switch (argc) {	/* examine args right to left, falling thru cases */
1859636069cSjtc 	case 4:
186507cbfd5Sjdolecek 		if (!is_default(argv[3])) {
18793190b89Sdsl 			step = strtod(argv[3], &ep);
18893190b89Sdsl 			if (*ep != 0)
189f127a329Sdsl 				errx(EXIT_FAILURE, "Bad step value:  %s",
190f127a329Sdsl 				    argv[3]);
19193190b89Sdsl 			have |= STEP;
1929636069cSjtc 		}
193fbffadb9Smrg 		/* FALLTHROUGH */
1949636069cSjtc 	case 3:
195507cbfd5Sjdolecek 		if (!is_default(argv[2])) {
196507cbfd5Sjdolecek 			if (!sscanf(argv[2], "%lf", &ender))
197507cbfd5Sjdolecek 				ender = argv[2][strlen(argv[2])-1];
19893190b89Sdsl 			have |= ENDER;
1999d6613d4Sdsl 			if (prec < 0)
200507cbfd5Sjdolecek 				n = getprec(argv[2]);
2019636069cSjtc 		}
202fbffadb9Smrg 		/* FALLTHROUGH */
2039636069cSjtc 	case 2:
204507cbfd5Sjdolecek 		if (!is_default(argv[1])) {
205507cbfd5Sjdolecek 			if (!sscanf(argv[1], "%lf", &begin))
206507cbfd5Sjdolecek 				begin = argv[1][strlen(argv[1])-1];
20793190b89Sdsl 			have |= BEGIN;
2089d6613d4Sdsl 			if (prec < 0)
209507cbfd5Sjdolecek 				prec = getprec(argv[1]);
2109636069cSjtc 			if (n > prec)		/* maximum precision */
2119636069cSjtc 				prec = n;
2129636069cSjtc 		}
213fbffadb9Smrg 		/* FALLTHROUGH */
2149636069cSjtc 	case 1:
215507cbfd5Sjdolecek 		if (!is_default(argv[0])) {
21693190b89Sdsl 			reps = strtoul(argv[0], &ep, 0);
21793190b89Sdsl 			if (*ep != 0 || reps < 0)
218f127a329Sdsl 				errx(EXIT_FAILURE, "Bad reps value:  %s",
219f127a329Sdsl 				    argv[0]);
22093190b89Sdsl 			have |= REPS;
2219636069cSjtc 		}
222fbffadb9Smrg 		/* FALLTHROUGH */
2239636069cSjtc 	case 0:
224507cbfd5Sjdolecek 		break;
2259636069cSjtc 	default:
226f127a329Sdsl 		errx(EXIT_FAILURE,
227f127a329Sdsl 		    "Too many arguments.  What do you mean by %s?", argv[4]);
2289636069cSjtc 	}
2299d6613d4Sdsl 
2309d6613d4Sdsl 	if (prec == -1)
2319d6613d4Sdsl 		prec = 0;
23293190b89Sdsl 
233*5dc82c03Skamil 	getformat();
234*5dc82c03Skamil 
23593190b89Sdsl 	if (randomize) {
23693190b89Sdsl 		/* 'step' is the seed here, use pseudo-random default */
23793190b89Sdsl 		if (!(have & STEP))
23893190b89Sdsl 			step = time(NULL) * getpid();
23993190b89Sdsl 		/* Take the default values for everything else */
24093190b89Sdsl 		return;
24193190b89Sdsl 	}
24293190b89Sdsl 
24393190b89Sdsl 	/*
24493190b89Sdsl 	 * The loop we run uses begin/step/reps, so if we have been
24593190b89Sdsl 	 * given an end value (ender) we must use it to replace the
24693190b89Sdsl 	 * default values of the others.
24793190b89Sdsl 	 * We will assume a begin of 0 and step of 1 if necessary.
24893190b89Sdsl 	 */
24993190b89Sdsl 
25093190b89Sdsl 	switch (have) {
25193190b89Sdsl 
25293190b89Sdsl 	case ENDER | STEP:
25393190b89Sdsl 	case ENDER | STEP | BEGIN:
25493190b89Sdsl 		/* Calculate reps */
25593190b89Sdsl 		if (step == 0.0)
25693190b89Sdsl 			reps = 0;	/* ie infinite */
25793190b89Sdsl 		else {
25893190b89Sdsl 			reps = (ender - begin + step) / step;
25993190b89Sdsl 			if (reps <= 0)
26093190b89Sdsl 				errx(EXIT_FAILURE, "Impossible stepsize");
26193190b89Sdsl 		}
26293190b89Sdsl 		break;
26393190b89Sdsl 
26493190b89Sdsl 	case REPS | ENDER:
26593190b89Sdsl 	case REPS | ENDER | STEP:
26693190b89Sdsl 		/* Calculate begin */
26793190b89Sdsl 		if (reps == 0)
26893190b89Sdsl 			errx(EXIT_FAILURE,
26993190b89Sdsl 			    "Must specify begin if reps == 0");
27093190b89Sdsl 		begin = ender - reps * step + step;
27193190b89Sdsl 		break;
27293190b89Sdsl 
27393190b89Sdsl 	case REPS | BEGIN | ENDER:
27493190b89Sdsl 		/* Calculate step */
27593190b89Sdsl 		if (reps == 0)
27693190b89Sdsl 			errx(EXIT_FAILURE,
27793190b89Sdsl 			    "Infinite sequences cannot be bounded");
27893190b89Sdsl 		if (reps == 1)
27993190b89Sdsl 			step = 0.0;
28093190b89Sdsl 		else
28193190b89Sdsl 			step = (ender - begin) / (reps - 1);
28293190b89Sdsl 		break;
28393190b89Sdsl 
28493190b89Sdsl 	case REPS | BEGIN | ENDER | STEP:
28593190b89Sdsl 		/* reps given and implied - take smaller */
28693190b89Sdsl 		if (step == 0.0)
28793190b89Sdsl 			break;
28893190b89Sdsl 		t = (ender - begin + step) / step;
28993190b89Sdsl 		if (t <= 0)
29093190b89Sdsl 			errx(EXIT_FAILURE,
29193190b89Sdsl 			    "Impossible stepsize");
29293190b89Sdsl 		if (t < reps)
29393190b89Sdsl 			reps = t;
29493190b89Sdsl 		break;
29593190b89Sdsl 
29693190b89Sdsl 	default:
29793190b89Sdsl 		/* No values can be calculated, use defaults */
29893190b89Sdsl 		break;
29993190b89Sdsl 	}
3009636069cSjtc }
3019636069cSjtc 
302f127a329Sdsl static void
putdata(double x,long notlast)30310e955c2Sperry putdata(double x, long notlast)
3049636069cSjtc {
3059636069cSjtc 
3069636069cSjtc 	if (boring)				/* repeated word */
30722e62219Spk 		printf("%s", format);
3089636069cSjtc 	else if (dox)				/* scalar */
3091b00238dSdsl 		printf(format, (long)floor(x));
3109636069cSjtc 	else					/* real */
3119636069cSjtc 		printf(format, x);
3129636069cSjtc 	if (notlast != 0)
3139636069cSjtc 		fputs(sepstring, stdout);
3149636069cSjtc }
3159636069cSjtc 
316f127a329Sdsl __dead static void
usage(void)317507cbfd5Sjdolecek usage(void)
3189636069cSjtc {
3192c12af6aSpeter 	(void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] "
320f127a329Sdsl 	    "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n",
321f127a329Sdsl 	    getprogname());
3229636069cSjtc 	exit(1);
3239636069cSjtc }
3249636069cSjtc 
325f127a329Sdsl static int
getprec(char * num_str)326f127a329Sdsl getprec(char *num_str)
3279636069cSjtc {
3289636069cSjtc 
329f127a329Sdsl 	num_str = strchr(num_str, '.');
330f127a329Sdsl 	if (num_str == NULL)
331f127a329Sdsl 		return 0;
332f127a329Sdsl 	return strspn(num_str + 1, "0123456789");
3339636069cSjtc }
3349636069cSjtc 
335f127a329Sdsl static void
getformat(void)33610e955c2Sperry getformat(void)
3379636069cSjtc {
338ac4e9aa7Slukem 	char	*p;
339507cbfd5Sjdolecek 	size_t	sz;
3409636069cSjtc 
3419636069cSjtc 	if (boring)				/* no need to bother */
3429636069cSjtc 		return;
343f127a329Sdsl 	for (p = format; *p; p++) {		/* look for '%' */
344507cbfd5Sjdolecek 		if (*p == '%') {
345507cbfd5Sjdolecek 			if (*(p+1) != '%')
3469636069cSjtc 				break;
347507cbfd5Sjdolecek 			p++;		/* leave %% alone */
348507cbfd5Sjdolecek 		}
349f127a329Sdsl 	}
350507cbfd5Sjdolecek 	sz = sizeof(format) - strlen(format) - 1;
351507cbfd5Sjdolecek 	if (!*p) {
352a357ac49Sdsl 		if (chardata || prec == 0) {
35339a84db3Slukem 			if ((size_t)snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz)
354f127a329Sdsl 				errx(EXIT_FAILURE, "-w word too long");
3559636069cSjtc 			dox = 1;
356618d6288Ssimonb 		} else {
357618d6288Ssimonb 			if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
358f127a329Sdsl 				errx(EXIT_FAILURE, "-w word too long");
3599636069cSjtc 		}
360507cbfd5Sjdolecek 	} else if (!*(p+1)) {
361507cbfd5Sjdolecek 		if (sz <= 0)
362f127a329Sdsl 			errx(EXIT_FAILURE, "-w word too long");
3639636069cSjtc 		strcat(format, "%");		/* cannot end in single '%' */
364507cbfd5Sjdolecek 	} else {
365507cbfd5Sjdolecek 		p++;				/* skip leading % */
366507cbfd5Sjdolecek 		for(; *p && !isalpha((unsigned char)*p); p++) {
367507cbfd5Sjdolecek 			/* allow all valid printf(3) flags, but deny '*' */
368507cbfd5Sjdolecek 			if (!strchr("0123456789#-+. ", *p))
369507cbfd5Sjdolecek 				break;
370507cbfd5Sjdolecek 		}
371507cbfd5Sjdolecek 		/* Allow 'l' prefix, but no other. */
372507cbfd5Sjdolecek 		if (*p == 'l')
373507cbfd5Sjdolecek 			p++;
3749636069cSjtc 		switch (*p) {
3759636069cSjtc 		case 'f': case 'e': case 'g': case '%':
376507cbfd5Sjdolecek 		case 'E': case 'G':
3779636069cSjtc 			break;
3789636069cSjtc 		case 's':
379f127a329Sdsl 			errx(EXIT_FAILURE,
380f127a329Sdsl 			    "cannot convert numeric data to strings");
3819636069cSjtc 			break;
382507cbfd5Sjdolecek 		case 'd': case 'o': case 'x': case 'u':
383507cbfd5Sjdolecek 		case 'D': case 'O': case 'X': case 'U':
384507cbfd5Sjdolecek 		case 'c': case 'i':
3859636069cSjtc 			dox = 1;
3869636069cSjtc 			break;
387507cbfd5Sjdolecek 		default:
388f127a329Sdsl 			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
389f127a329Sdsl 			    format);
3909636069cSjtc 		}
391507cbfd5Sjdolecek 		/* Need to check for trailing stuff to print */
392507cbfd5Sjdolecek 		for (; *p; p++)		/* look for '%' */
393507cbfd5Sjdolecek 			if (*p == '%') {
394507cbfd5Sjdolecek 				if (*(p+1) != '%')
395507cbfd5Sjdolecek 					break;
396507cbfd5Sjdolecek 				p++;		/* leave %% alone */
397507cbfd5Sjdolecek 			}
398507cbfd5Sjdolecek 		if (*p)
399f127a329Sdsl 			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
400f127a329Sdsl 			    format);
4019636069cSjtc 	}
4029636069cSjtc }
403