main netmisc / jot / jot.c
  1/*	$NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil Exp $	*/
  2
  3/*-
  4 * Copyright (c) 1993
  5 *	The Regents of the University of California.  All rights reserved.
  6 *
  7 * Redistribution and use in source and binary forms, with or without
  8 * modification, are permitted provided that the following conditions
  9 * are met:
 10 * 1. Redistributions of source code must retain the above copyright
 11 *    notice, this list of conditions and the following disclaimer.
 12 * 2. Redistributions in binary form must reproduce the above copyright
 13 *    notice, this list of conditions and the following disclaimer in the
 14 *    documentation and/or other materials provided with the distribution.
 15 * 3. Neither the name of the University nor the names of its contributors
 16 *    may be used to endorse or promote products derived from this software
 17 *    without specific prior written permission.
 18 *
 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 29 * SUCH DAMAGE.
 30 */
 31
 32#include <sys/cdefs.h>
 33#ifndef lint
 34__COPYRIGHT("@(#) Copyright (c) 1993\
 35 The Regents of the University of California.  All rights reserved.");
 36#endif /* not lint */
 37
 38#ifndef lint
 39#if 0
 40static char sccsid[] = "@(#)jot.c	8.1 (Berkeley) 6/6/93";
 41#endif
 42__RCSID("$NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil Exp $");
 43#endif /* not lint */
 44
 45/*
 46 * jot - print sequential or random data
 47 *
 48 * Author:  John Kunze, Office of Comp. Affairs, UCB
 49 */
 50
 51#include <ctype.h>
 52#include <err.h>
 53#include <limits.h>
 54#include <math.h>
 55#include <stdio.h>
 56#include <stdlib.h>
 57#include <string.h>
 58#include <time.h>
 59#include <unistd.h>
 60
 61#define	REPS_DEF	100
 62#define	BEGIN_DEF	1
 63#define	ENDER_DEF	100
 64#define	STEP_DEF	1
 65
 66#define	is_default(s)	(strcmp((s), "-") == 0)
 67
 68static double	begin = BEGIN_DEF;
 69static double	ender = ENDER_DEF;
 70static double	step = STEP_DEF;
 71static long	reps = REPS_DEF;
 72static int	randomize;
 73static int	boring;
 74static int	prec = -1;
 75static int	dox;
 76static int	chardata;
 77static int	nofinalnl;
 78static const char *sepstring = "\n";
 79static char	format[BUFSIZ];
 80
 81static void	getargs(int, char *[]);
 82static void	getformat(void);
 83static int	getprec(char *);
 84static void	putdata(double, long);
 85static void	usage(void) __dead;
 86
 87int
 88main(int argc, char *argv[])
 89{
 90	double	x;
 91	long	i;
 92
 93	getargs(argc, argv);
 94	if (randomize) {
 95		x = ender - begin;
 96		if (x < 0) {
 97			x = -x;
 98			begin = ender;
 99		}
100		if (dox == 0)
101			/*
102			 * We are printing floating point, generate random
103			 * number that include both supplied limits.
104			 * Due to FP routing for display the low and high
105			 * values are likely to occur half as often as all
106			 * the others.
107			 */
108			x /= (1u << 31) - 1.0;
109		else {
110			/*
111			 * We are printing integers increase the range by
112			 * one but ensure we never generate it.
113			 * This makes all the integer values equally likely.
114			 */
115			x += 1.0;
116			x /= (1u << 31);
117		}
118		srandom((unsigned long) step);
119		for (i = 1; i <= reps || reps == 0; i++)
120			putdata(random() * x + begin, reps - i);
121	} else {
122		/*
123		 * If we are going to display as integer, add 0.5 here
124		 * and use floor(x) later to get sane rounding.
125		 */
126		x = begin;
127		if (dox)
128			x += 0.5;
129		for (i = 1; i <= reps || reps == 0; i++, x += step)
130			putdata(x, reps - i);
131	}
132	if (!nofinalnl)
133		putchar('\n');
134	exit(0);
135}
136
137static void
138getargs(int argc, char *argv[])
139{
140	unsigned int have = 0;
141#define BEGIN	1
142#define	STEP	2	/* seed if -r */
143#define REPS	4
144#define	ENDER	8
145	int n = 0;
146	long t;
147	char *ep;
148
149	for (;;) {
150		switch (getopt(argc, argv, "b:cnp:rs:w:")) {
151		default:
152			usage();
153		case -1:
154			break;
155		case 'c':
156			chardata = 1;
157			continue;
158		case 'n':
159			nofinalnl = 1;
160			continue;
161		case 'p':
162			prec = strtol(optarg, &ep, 0);
163			if (*ep != 0 || prec < 0)
164				errx(EXIT_FAILURE, "Bad precision value");
165			continue;
166		case 'r':
167			randomize = 1;
168			continue;
169		case 's':
170			sepstring = optarg;
171			continue;
172		case 'b':
173			boring = 1;
174			/* FALLTHROUGH */
175		case 'w':
176			strlcpy(format, optarg, sizeof(format));
177			continue;
178		}
179		break;
180	}
181	argc -= optind;
182	argv += optind;
183
184	switch (argc) {	/* examine args right to left, falling thru cases */
185	case 4:
186		if (!is_default(argv[3])) {
187			step = strtod(argv[3], &ep);
188			if (*ep != 0)
189				errx(EXIT_FAILURE, "Bad step value:  %s",
190				    argv[3]);
191			have |= STEP;
192		}
193		/* FALLTHROUGH */
194	case 3:
195		if (!is_default(argv[2])) {
196			if (!sscanf(argv[2], "%lf", &ender))
197				ender = argv[2][strlen(argv[2])-1];
198			have |= ENDER;
199			if (prec < 0)
200				n = getprec(argv[2]);
201		}
202		/* FALLTHROUGH */
203	case 2:
204		if (!is_default(argv[1])) {
205			if (!sscanf(argv[1], "%lf", &begin))
206				begin = argv[1][strlen(argv[1])-1];
207			have |= BEGIN;
208			if (prec < 0)
209				prec = getprec(argv[1]);
210			if (n > prec)		/* maximum precision */
211				prec = n;
212		}
213		/* FALLTHROUGH */
214	case 1:
215		if (!is_default(argv[0])) {
216			reps = strtoul(argv[0], &ep, 0);
217			if (*ep != 0 || reps < 0)
218				errx(EXIT_FAILURE, "Bad reps value:  %s",
219				    argv[0]);
220			have |= REPS;
221		}
222		/* FALLTHROUGH */
223	case 0:
224		break;
225	default:
226		errx(EXIT_FAILURE,
227		    "Too many arguments.  What do you mean by %s?", argv[4]);
228	}
229
230	if (prec == -1)
231		prec = 0;
232
233	getformat();
234
235	if (randomize) {
236		/* 'step' is the seed here, use pseudo-random default */
237		if (!(have & STEP))
238			step = time(NULL) * getpid();
239		/* Take the default values for everything else */
240		return;
241	}
242
243	/*
244	 * The loop we run uses begin/step/reps, so if we have been
245	 * given an end value (ender) we must use it to replace the
246	 * default values of the others.
247	 * We will assume a begin of 0 and step of 1 if necessary.
248	 */
249
250	switch (have) {
251
252	case ENDER | STEP:
253	case ENDER | STEP | BEGIN:
254		/* Calculate reps */
255		if (step == 0.0)
256			reps = 0;	/* ie infinite */
257		else {
258			reps = (ender - begin + step) / step;
259			if (reps <= 0)
260				errx(EXIT_FAILURE, "Impossible stepsize");
261		}
262		break;
263
264	case REPS | ENDER:
265	case REPS | ENDER | STEP:
266		/* Calculate begin */
267		if (reps == 0)
268			errx(EXIT_FAILURE,
269			    "Must specify begin if reps == 0");
270		begin = ender - reps * step + step;
271		break;
272
273	case REPS | BEGIN | ENDER:
274		/* Calculate step */
275		if (reps == 0)
276			errx(EXIT_FAILURE,
277			    "Infinite sequences cannot be bounded");
278		if (reps == 1)
279			step = 0.0;
280		else
281			step = (ender - begin) / (reps - 1);
282		break;
283
284	case REPS | BEGIN | ENDER | STEP:
285		/* reps given and implied - take smaller */
286		if (step == 0.0)
287			break;
288		t = (ender - begin + step) / step;
289		if (t <= 0)
290			errx(EXIT_FAILURE,
291			    "Impossible stepsize");
292		if (t < reps)
293			reps = t;
294		break;
295
296	default:
297		/* No values can be calculated, use defaults */
298		break;
299	}
300}
301
302static void
303putdata(double x, long notlast)
304{
305
306	if (boring)				/* repeated word */
307		printf("%s", format);
308	else if (dox)				/* scalar */
309		printf(format, (long)floor(x));
310	else					/* real */
311		printf(format, x);
312	if (notlast != 0)
313		fputs(sepstring, stdout);
314}
315
316__dead static void
317usage(void)
318{
319	(void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] "
320	    "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n",
321	    getprogname());
322	exit(1);
323}
324
325static int
326getprec(char *num_str)
327{
328
329	num_str = strchr(num_str, '.');
330	if (num_str == NULL)
331		return 0;
332	return strspn(num_str + 1, "0123456789");
333}
334
335static void
336getformat(void)
337{
338	char	*p;
339	size_t	sz;
340
341	if (boring)				/* no need to bother */
342		return;
343	for (p = format; *p; p++) {		/* look for '%' */
344		if (*p == '%') {
345			if (*(p+1) != '%')
346				break;
347			p++;		/* leave %% alone */
348		}
349	}
350	sz = sizeof(format) - strlen(format) - 1;
351	if (!*p) {
352		if (chardata || prec == 0) {
353			if ((size_t)snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz)
354				errx(EXIT_FAILURE, "-w word too long");
355			dox = 1;
356		} else {
357			if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
358				errx(EXIT_FAILURE, "-w word too long");
359		}
360	} else if (!*(p+1)) {
361		if (sz <= 0)
362			errx(EXIT_FAILURE, "-w word too long");
363		strcat(format, "%");		/* cannot end in single '%' */
364	} else {
365		p++;				/* skip leading % */
366		for(; *p && !isalpha((unsigned char)*p); p++) {
367			/* allow all valid printf(3) flags, but deny '*' */
368			if (!strchr("0123456789#-+. ", *p))
369				break;
370		}
371		/* Allow 'l' prefix, but no other. */
372		if (*p == 'l')
373			p++;
374		switch (*p) {
375		case 'f': case 'e': case 'g': case '%':
376		case 'E': case 'G':
377			break;
378		case 's':
379			errx(EXIT_FAILURE,
380			    "cannot convert numeric data to strings");
381			break;
382		case 'd': case 'o': case 'x': case 'u':
383		case 'D': case 'O': case 'X': case 'U':
384		case 'c': case 'i':
385			dox = 1;
386			break;
387		default:
388			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
389			    format);
390		}
391		/* Need to check for trailing stuff to print */
392		for (; *p; p++)		/* look for '%' */
393			if (*p == '%') {
394				if (*(p+1) != '%')
395					break;
396				p++;		/* leave %% alone */
397			}
398		if (*p)
399			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
400			    format);
401	}
402}