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