xref: /minix3/usr.bin/jot/jot.c (revision 075dbe55f35c411d440c66d581a9e79eed791d2f)
1 /*	$NetBSD: jot.c,v 1.25 2009/04/12 11:19:18 lukem 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
40 static char sccsid[] = "@(#)jot.c	8.1 (Berkeley) 6/6/93";
41 #endif
42 __RCSID("$NetBSD: jot.c,v 1.25 2009/04/12 11:19:18 lukem 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 
68 static double	begin = BEGIN_DEF;
69 static double	ender = ENDER_DEF;
70 static double	step = STEP_DEF;
71 static long	reps = REPS_DEF;
72 static int	randomize;
73 static int	boring;
74 static int	prec = -1;
75 static int	dox;
76 static int	chardata;
77 static int	nofinalnl;
78 static const char *sepstring = "\n";
79 static char	format[BUFSIZ];
80 
81 static void	getargs(int, char *[]);
82 static void	getformat(void);
83 static int	getprec(char *);
84 static void	putdata(double, long);
85 static void	usage(void) __dead;
86 
87 int
main(int argc,char * argv[])88 main(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 
137 static void
getargs(int argc,char * argv[])138 getargs(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 	case 3:
194 		if (!is_default(argv[2])) {
195 			if (!sscanf(argv[2], "%lf", &ender))
196 				ender = argv[2][strlen(argv[2])-1];
197 			have |= ENDER;
198 			if (prec < 0)
199 				n = getprec(argv[2]);
200 		}
201 	case 2:
202 		if (!is_default(argv[1])) {
203 			if (!sscanf(argv[1], "%lf", &begin))
204 				begin = argv[1][strlen(argv[1])-1];
205 			have |= BEGIN;
206 			if (prec < 0)
207 				prec = getprec(argv[1]);
208 			if (n > prec)		/* maximum precision */
209 				prec = n;
210 		}
211 	case 1:
212 		if (!is_default(argv[0])) {
213 			reps = strtoul(argv[0], &ep, 0);
214 			if (*ep != 0 || reps < 0)
215 				errx(EXIT_FAILURE, "Bad reps value:  %s",
216 				    argv[0]);
217 			have |= REPS;
218 		}
219 		break;
220 	case 0:
221 		usage();
222 		break;
223 	default:
224 		errx(EXIT_FAILURE,
225 		    "Too many arguments.  What do you mean by %s?", argv[4]);
226 	}
227 	getformat();
228 
229 	if (prec == -1)
230 		prec = 0;
231 
232 	if (randomize) {
233 		/* 'step' is the seed here, use pseudo-random default */
234 		if (!(have & STEP))
235 			step = time(NULL) * getpid();
236 		/* Take the default values for everything else */
237 		return;
238 	}
239 
240 	/*
241 	 * The loop we run uses begin/step/reps, so if we have been
242 	 * given an end value (ender) we must use it to replace the
243 	 * default values of the others.
244 	 * We will assume a begin of 0 and step of 1 if necessary.
245 	 */
246 
247 	switch (have) {
248 
249 	case ENDER | STEP:
250 	case ENDER | STEP | BEGIN:
251 		/* Calculate reps */
252 		if (step == 0.0)
253 			reps = 0;	/* ie infinite */
254 		else {
255 			reps = (ender - begin + step) / step;
256 			if (reps <= 0)
257 				errx(EXIT_FAILURE, "Impossible stepsize");
258 		}
259 		break;
260 
261 	case REPS | ENDER:
262 	case REPS | ENDER | STEP:
263 		/* Calculate begin */
264 		if (reps == 0)
265 			errx(EXIT_FAILURE,
266 			    "Must specify begin if reps == 0");
267 		begin = ender - reps * step + step;
268 		break;
269 
270 	case REPS | BEGIN | ENDER:
271 		/* Calculate step */
272 		if (reps == 0)
273 			errx(EXIT_FAILURE,
274 			    "Infinite sequences cannot be bounded");
275 		if (reps == 1)
276 			step = 0.0;
277 		else
278 			step = (ender - begin) / (reps - 1);
279 		break;
280 
281 	case REPS | BEGIN | ENDER | STEP:
282 		/* reps given and implied - take smaller */
283 		if (step == 0.0)
284 			break;
285 		t = (ender - begin + step) / step;
286 		if (t <= 0)
287 			errx(EXIT_FAILURE,
288 			    "Impossible stepsize");
289 		if (t < reps)
290 			reps = t;
291 		break;
292 
293 	default:
294 		/* No values can be calculated, use defaults */
295 		break;
296 	}
297 }
298 
299 static void
putdata(double x,long notlast)300 putdata(double x, long notlast)
301 {
302 
303 	if (boring)				/* repeated word */
304 		printf("%s", format);
305 	else if (dox)				/* scalar */
306 		printf(format, (long)floor(x));
307 	else					/* real */
308 		printf(format, x);
309 	if (notlast != 0)
310 		fputs(sepstring, stdout);
311 }
312 
313 __dead static void
usage(void)314 usage(void)
315 {
316 	(void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] "
317 	    "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n",
318 	    getprogname());
319 	exit(1);
320 }
321 
322 static int
getprec(char * num_str)323 getprec(char *num_str)
324 {
325 
326 	num_str = strchr(num_str, '.');
327 	if (num_str == NULL)
328 		return 0;
329 	return strspn(num_str + 1, "0123456789");
330 }
331 
332 static void
getformat(void)333 getformat(void)
334 {
335 	char	*p;
336 	size_t	sz;
337 
338 	if (boring)				/* no need to bother */
339 		return;
340 	for (p = format; *p; p++) {		/* look for '%' */
341 		if (*p == '%') {
342 			if (*(p+1) != '%')
343 				break;
344 			p++;		/* leave %% alone */
345 		}
346 	}
347 	sz = sizeof(format) - strlen(format) - 1;
348 	if (!*p) {
349 		if (chardata || prec == 0) {
350 			if ((size_t)snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz)
351 				errx(EXIT_FAILURE, "-w word too long");
352 			dox = 1;
353 		} else {
354 			if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
355 				errx(EXIT_FAILURE, "-w word too long");
356 		}
357 	} else if (!*(p+1)) {
358 		if (sz <= 0)
359 			errx(EXIT_FAILURE, "-w word too long");
360 		strcat(format, "%");		/* cannot end in single '%' */
361 	} else {
362 		p++;				/* skip leading % */
363 		for(; *p && !isalpha((unsigned char)*p); p++) {
364 			/* allow all valid printf(3) flags, but deny '*' */
365 			if (!strchr("0123456789#-+. ", *p))
366 				break;
367 		}
368 		/* Allow 'l' prefix, but no other. */
369 		if (*p == 'l')
370 			p++;
371 		switch (*p) {
372 		case 'f': case 'e': case 'g': case '%':
373 		case 'E': case 'G':
374 			break;
375 		case 's':
376 			errx(EXIT_FAILURE,
377 			    "cannot convert numeric data to strings");
378 			break;
379 		case 'd': case 'o': case 'x': case 'u':
380 		case 'D': case 'O': case 'X': case 'U':
381 		case 'c': case 'i':
382 			dox = 1;
383 			break;
384 		default:
385 			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
386 			    format);
387 		}
388 		/* Need to check for trailing stuff to print */
389 		for (; *p; p++)		/* look for '%' */
390 			if (*p == '%') {
391 				if (*(p+1) != '%')
392 					break;
393 				p++;		/* leave %% alone */
394 			}
395 		if (*p)
396 			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
397 			    format);
398 	}
399 }
400