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