xref: /openbsd/usr.bin/jot/jot.c (revision 891d7ab6)
1 /*	$OpenBSD: jot.c,v 1.20 2009/10/27 23:59:39 deraadt Exp $	*/
2 /*	$NetBSD: jot.c,v 1.3 1994/12/02 20:29:43 pk Exp $	*/
3 
4 /*-
5  * Copyright (c) 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * jot - print sequential or random data
35  *
36  * Author:  John Kunze, Office of Comp. Affairs, UCB
37  */
38 
39 #include <err.h>
40 #include <stdbool.h>
41 #include <ctype.h>
42 #include <limits.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 #define	REPS_DEF	100
48 #define	BEGIN_DEF	1
49 #define	ENDER_DEF	100
50 #define	STEP_DEF	1
51 
52 #define	is_default(s)	(strcmp((s), "-") == 0)
53 
54 static double	begin;
55 static double	ender;
56 static double	s;
57 static long	reps;
58 static bool	randomize;
59 static bool	infinity;
60 static bool	boring;
61 static int	prec = -1;
62 static bool	intdata;
63 static bool	longdata;
64 static bool	chardata;
65 static bool	nosign;
66 static bool	finalnl = true;
67 static char	sepstring[BUFSIZ] = "\n";
68 static char	format[BUFSIZ];
69 
70 static void	getformat(void);
71 static int	getprec(char *);
72 static int	putdata(double, bool);
73 static void	usage(void);
74 
75 int
76 main(int argc, char *argv[])
77 {
78 	double		x;
79 	double		y;
80 	long		i;
81 	unsigned int	mask = 0;
82 	int		n = 0;
83 	int		ch;
84 	const	char	*errstr;
85 
86 	while ((ch = getopt(argc, argv, "rb:w:cs:np:")) != -1)
87 		switch (ch) {
88 		case 'r':
89 			randomize = true;
90 			break;
91 		case 'c':
92 			chardata = true;
93 			break;
94 		case 'n':
95 			finalnl = false;
96 			break;
97 		case 'b':
98 			boring = true;
99 			if (strlcpy(format, optarg, sizeof(format)) >=
100 			    sizeof(format))
101 				errx(1, "-b word too long");
102 			break;
103 		case 'w':
104 			if (strlcpy(format, optarg, sizeof(format)) >=
105 			    sizeof(format))
106 				errx(1, "-w word too long");
107 			break;
108 		case 's':
109 			if (strlcpy(sepstring, optarg, sizeof(sepstring)) >=
110 			    sizeof(sepstring))
111 				errx(1, "-s word too long");
112 			break;
113 		case 'p':
114 			prec = strtonum(optarg, 0, INT_MAX, &errstr);
115 			if (errstr != NULL)
116 				errx(1, "bad precision value, %s: %s", errstr,
117 					optarg);
118 			break;
119 		default:
120 			usage();
121 		}
122 	argc -= optind;
123 	argv += optind;
124 
125 	switch (argc) {	/* examine args right to left, falling thru cases */
126 	case 4:
127 		if (!is_default(argv[3])) {
128 			if (!sscanf(argv[3], "%lf", &s))
129 				errx(1, "Bad s value:  %s", argv[3]);
130 			mask |= 01;
131 			if (randomize)
132 				warnx("random seeding not supported");
133 		}
134 	case 3:
135 		if (!is_default(argv[2])) {
136 			if (!sscanf(argv[2], "%lf", &ender))
137 				ender = argv[2][strlen(argv[2])-1];
138 			mask |= 02;
139 			if (prec == -1)
140 				n = getprec(argv[2]);
141 		}
142 	case 2:
143 		if (!is_default(argv[1])) {
144 			if (!sscanf(argv[1], "%lf", &begin))
145 				begin = argv[1][strlen(argv[1])-1];
146 			mask |= 04;
147 			if (prec == -1)
148 				prec = getprec(argv[1]);
149 			if (n > prec)		/* maximum precision */
150 				prec = n;
151 		}
152 	case 1:
153 		if (!is_default(argv[0])) {
154 			if (!sscanf(argv[0], "%ld", &reps))
155 				errx(1, "Bad reps value:  %s", argv[0]);
156 			mask |= 010;
157 			if (prec == -1)
158 				prec = 0;
159 		}
160 		break;
161 	case 0:
162 		usage();
163 		break;
164 	default:
165 		errx(1, "Too many arguments.  What do you mean by %s?",
166 		    argv[4]);
167 	}
168 	getformat();
169 	while (mask)	/* 4 bit mask has 1's where last 4 args were given */
170 		switch (mask) {	/* fill in the 0's by default or computation */
171 		case 001:
172 			reps = REPS_DEF;
173 			mask = 011;
174 			break;
175 		case 002:
176 			reps = REPS_DEF;
177 			mask = 012;
178 			break;
179 		case 003:
180 			reps = REPS_DEF;
181 			mask = 013;
182 			break;
183 		case 004:
184 			reps = REPS_DEF;
185 			mask = 014;
186 			break;
187 		case 005:
188 			reps = REPS_DEF;
189 			mask = 015;
190 			break;
191 		case 006:
192 			reps = REPS_DEF;
193 			mask = 016;
194 			break;
195 		case 007:
196 			if (randomize) {
197 				reps = REPS_DEF;
198 				mask = 0;
199 				break;
200 			}
201 			if (s == 0.0) {
202 				reps = 0;
203 				mask = 0;
204 				break;
205 			}
206 			reps = (ender - begin + s) / s;
207 			if (reps <= 0)
208 				errx(1, "Impossible stepsize");
209 			mask = 0;
210 			break;
211 		case 010:
212 			begin = BEGIN_DEF;
213 			mask = 014;
214 			break;
215 		case 011:
216 			begin = BEGIN_DEF;
217 			mask = 015;
218 			break;
219 		case 012:
220 			s = STEP_DEF;
221 			mask = 013;
222 			break;
223 		case 013:
224 			if (randomize)
225 				begin = BEGIN_DEF;
226 			else if (reps == 0)
227 				errx(1, "Must specify begin if reps == 0");
228 			begin = ender - reps * s + s;
229 			mask = 0;
230 			break;
231 		case 014:
232 			s = STEP_DEF;
233 			mask = 015;
234 			break;
235 		case 015:
236 			if (randomize)
237 				ender = ENDER_DEF;
238 			else
239 				ender = begin + reps * s - s;
240 			mask = 0;
241 			break;
242 		case 016:
243 			if (reps == 0)
244 				errx(1, "Infinite sequences cannot be bounded");
245 			else if (reps == 1)
246 				s = 0.0;
247 			else
248 				s = (ender - begin) / (reps - 1);
249 			mask = 0;
250 			break;
251 		case 017:		/* if reps given and implied, */
252 			if (!randomize && s != 0.0) {
253 				long t = (ender - begin + s) / s;
254 				if (t <= 0)
255 					errx(1, "Impossible stepsize");
256 				if (t < reps)		/* take lesser */
257 					reps = t;
258 			}
259 			mask = 0;
260 			break;
261 		default:
262 			errx(1, "bad mask");
263 		}
264 	if (reps == 0)
265 		infinity = true;
266 	if (randomize) {
267 		x = (ender - begin) * (ender > begin ? 1 : -1);
268 		for (i = 1; i <= reps || infinity; i++) {
269 			double v;
270 			y = arc4random() / ((double)0xffffffff + 1);
271 			v = y * x + begin;
272 			if (putdata(v, reps == i && !infinity))
273 				errx(1, "range error in conversion: %f", v);
274 		}
275 	}
276 	else
277 		for (i = 1, x = begin; i <= reps || infinity; i++, x += s)
278 			if (putdata(x, reps == i && !infinity))
279 				errx(1, "range error in conversion: %f", x);
280 	if (finalnl)
281 		putchar('\n');
282 	exit(0);
283 }
284 
285 static int
286 putdata(double x, bool last)
287 {
288 	if (boring)
289 		printf("%s", format);
290 	else if (longdata && nosign) {
291 		if (x <= (double)ULONG_MAX && x >= 0.0)
292 			printf(format, (unsigned long)x);
293 		else
294 			return (1);
295 	} else if (longdata) {
296 		if (x <= (double)LONG_MAX && x >= (double)LONG_MIN)
297 			printf(format, (long)x);
298 		else
299 			return (1);
300 	} else if (chardata || (intdata && !nosign)) {
301 		if (x <= (double)INT_MAX && x >= (double)INT_MIN)
302 			printf(format, (int)x);
303 		else
304 			return (1);
305 	} else if (intdata) {
306 		if (x <= (double)UINT_MAX && x >= 0.0)
307 			printf(format, (unsigned int)x);
308 		else
309 			return (1);
310 	} else
311 		printf(format, x);
312 	if (!last)
313 		fputs(sepstring, stdout);
314 
315 	return (0);
316 }
317 
318 static void
319 usage(void)
320 {
321 	(void)fprintf(stderr, "usage: jot [-cnr] [-b word] [-p precision] "
322 	    "[-s string] [-w word]\n"
323 	    "	   [reps [begin [end [s]]]]\n");
324 	exit(1);
325 }
326 
327 static int
328 getprec(char *s)
329 {
330 	char	*p;
331 	char	*q;
332 
333 	for (p = s; *p != '\0'; p++)
334 		if (*p == '.')
335 			break;
336 	if (*p == '\0')
337 		return (0);
338 	for (q = ++p; *p != '\0'; p++)
339 		if (!isdigit(*p))
340 			break;
341 	return (p - q);
342 }
343 
344 static void
345 getformat(void)
346 {
347 	char	*p, *p2;
348 	int dot, hash, space, sign, numbers = 0;
349 	size_t sz;
350 
351 	if (boring)				/* no need to bother */
352 		return;
353 	for (p = format; *p != '\0'; p++)	/* look for '%' */
354 		if (*p == '%' && *(p+1) != '%')	/* leave %% alone */
355 			break;
356 	sz = sizeof(format) - strlen(format) - 1;
357 	if (*p == '\0' && !chardata) {
358 		int n;
359 
360 		n = snprintf(p, sz, "%%.%df", prec);
361 		if (n == -1 || n >= (int)sz)
362 			errx(1, "-w word too long");
363 	} else if (*p == '\0' && chardata) {
364 		if (strlcpy(p, "%c", sz) >= sz)
365 			errx(1, "-w word too long");
366 		intdata = true;
367 	} else if (*(p+1) == '\0') {
368 		if (sz <= 0)
369 			errx(1, "-w word too long");
370 		/* cannot end in single '%' */
371 		strlcat(format, "%", sizeof format);
372 	} else {
373 		/*
374 		 * Allow conversion format specifiers of the form
375 		 * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of
376 		 * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u}
377 		 */
378 		p2 = p++;
379 		dot = hash = space = sign = numbers = 0;
380 		while (!isalpha(*p)) {
381 			if (isdigit(*p)) {
382 				numbers++;
383 				p++;
384 			} else if ((*p == '#' && !(numbers|dot|sign|space|
385 			    hash++)) ||
386 			    (*p == ' ' && !(numbers|dot|space++)) ||
387 			    ((*p == '+' || *p == '-') && !(numbers|dot|sign++))
388 			    || (*p == '.' && !(dot++)))
389 				p++;
390 			else
391 				goto fmt_broken;
392 		}
393 		if (*p == 'l') {
394 			longdata = true;
395 			if (*++p == 'l') {
396 				if (p[1] != '\0')
397 					p++;
398 				goto fmt_broken;
399 			}
400 		}
401 		switch (*p) {
402 		case 'o': case 'u': case 'x': case 'X':
403 			intdata = nosign = true;
404 			break;
405 		case 'd': case 'i':
406 			intdata = true;
407 			break;
408 		case 'D':
409 			if (!longdata) {
410 				intdata = true;
411 				break;
412 			}
413 		case 'O': case 'U':
414 			if (!longdata) {
415 				intdata = nosign = true;
416 				break;
417 			}
418 		case 'c':
419 			if (!(intdata | longdata)) {
420 				chardata = true;
421 				break;
422 			}
423 		case 'h': case 'n': case 'p': case 'q': case 's': case 'L':
424 		case '$': case '*':
425 			goto fmt_broken;
426 		case 'f': case 'e': case 'g': case 'E': case 'G':
427 			if (!longdata)
428 				break;
429 			/* FALLTHROUGH */
430 		default:
431 fmt_broken:
432 			*++p = '\0';
433 			errx(1, "illegal or unsupported format '%s'", p2);
434 			/* NOTREACHED */
435 		}
436 		while (*++p != '\0')
437 			if (*p == '%' && *(p+1) != '\0' && *(p+1) != '%')
438 				errx(1, "too many conversions");
439 			else if (*p == '%' && *(p+1) == '%')
440 				p++;
441 			else if (*p == '%' && *(p+1) == '\0') {
442 				strlcat(format, "%", sizeof format);
443 				break;
444 			}
445 	}
446 }
447