xref: /dragonfly/usr.bin/jot/jot.c (revision 91f1d6e1)
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 int	randomize;
58 int	infinity;
59 int	boring;
60 int	prec;
61 int	longdata;
62 int	intdata;
63 int	chardata;
64 int	nosign;
65 int	nofinalnl;
66 static const	char *sepstring = "\n";
67 static char	format[BUFSIZ];
68 
69 static void	getformat(void);
70 static int	getprec(const char *);
71 static int	putdata(double, long);
72 static void	usage(void) __dead2;
73 
74 int
75 main(int argc, char **argv)
76 {
77 	double	xd, yd;
78 	long	id;
79 	double	*x = &xd;
80 	double	*y = &yd;
81 	long	*i = &id;
82 	unsigned int	mask = 0;
83 	int	n = 0;
84 	int	ch;
85 	double	begin = BEGIN_DEF;
86 	double	ender = ENDER_DEF;
87 	double	s = STEP_DEF;
88 	long	reps = REPS_DEF;
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 		/* FALLTHROUGH */
131 	case 3:
132 		if (!is_default(argv[2])) {
133 			if (!sscanf(argv[2], "%lf", &ender))
134 				ender = argv[2][strlen(argv[2])-1];
135 			mask |= 02;
136 			if (!prec)
137 				n = getprec(argv[2]);
138 		}
139 		/* FALLTHROUGH */
140 	case 2:
141 		if (!is_default(argv[1])) {
142 			if (!sscanf(argv[1], "%lf", &begin))
143 				begin = argv[1][strlen(argv[1])-1];
144 			mask |= 04;
145 			if (!prec)
146 				prec = getprec(argv[1]);
147 			if (n > prec)		/* maximum precision */
148 				prec = n;
149 		}
150 		/* FALLTHROUGH */
151 	case 1:
152 		if (!is_default(argv[0])) {
153 			if (!sscanf(argv[0], "%ld", &reps))
154 				errx(1, "bad reps value: %s", argv[0]);
155 			mask |= 010;
156 		}
157 		break;
158 	case 0:
159 		usage();
160 	default:
161 		errx(1, "too many arguments.  What do you mean by %s?",
162 		    argv[4]);
163 	}
164 	getformat();
165 	while (mask)	/* 4 bit mask has 1's where last 4 args were given */
166 		switch (mask) {	/* fill in the 0's by default or computation */
167 		case 001:
168 			reps = REPS_DEF;
169 			mask = 011;
170 			break;
171 		case 002:
172 			reps = REPS_DEF;
173 			mask = 012;
174 			break;
175 		case 003:
176 			reps = REPS_DEF;
177 			mask = 013;
178 			break;
179 		case 004:
180 			reps = REPS_DEF;
181 			mask = 014;
182 			break;
183 		case 005:
184 			reps = REPS_DEF;
185 			mask = 015;
186 			break;
187 		case 006:
188 			reps = REPS_DEF;
189 			mask = 016;
190 			break;
191 		case 007:
192 			if (randomize) {
193 				reps = REPS_DEF;
194 				mask = 0;
195 				break;
196 			}
197 			if (s == 0.0) {
198 				reps = 0;
199 				mask = 0;
200 				break;
201 			}
202 			reps = (ender - begin + s) / s;
203 			if (reps <= 0)
204 				errx(1, "impossible stepsize");
205 			mask = 0;
206 			break;
207 		case 010:
208 			begin = BEGIN_DEF;
209 			mask = 014;
210 			break;
211 		case 011:
212 			begin = BEGIN_DEF;
213 			mask = 015;
214 			break;
215 		case 012:
216 			s = (randomize ? time(NULL) : STEP_DEF);
217 			mask = 013;
218 			break;
219 		case 013:
220 			if (randomize)
221 				begin = BEGIN_DEF;
222 			else if (reps == 0)
223 				errx(1, "must specify begin if reps == 0");
224 			begin = ender - reps * s + s;
225 			mask = 0;
226 			break;
227 		case 014:
228 			s = (randomize ? -1.0 : STEP_DEF);
229 			mask = 015;
230 			break;
231 		case 015:
232 			if (randomize)
233 				ender = ENDER_DEF;
234 			else
235 				ender = begin + reps * s - s;
236 			mask = 0;
237 			break;
238 		case 016:
239 			if (randomize)
240 				s = -1.0;
241 			else if (reps == 0)
242 				errx(1, "infinite sequences cannot be bounded");
243 			else if (reps == 1)
244 				s = 0.0;
245 			else
246 				s = (ender - begin) / (reps - 1);
247 			mask = 0;
248 			break;
249 		case 017:		/* if reps given and implied, */
250 			if (!randomize && s != 0.0) {
251 				long t = (ender - begin + s) / s;
252 				if (t <= 0)
253 					errx(1, "impossible stepsize");
254 				if (t < reps)		/* take lesser */
255 					reps = t;
256 			}
257 			mask = 0;
258 			break;
259 		default:
260 			errx(1, "bad mask");
261 		}
262 	if (reps == 0)
263 		infinity = 1;
264 	if (randomize) {
265 		*x = (ender - begin) * (ender > begin ? 1 : -1);
266 		for (*i = 1; *i <= reps || infinity; (*i)++) {
267 			*y = arc4random() / (double)0xffffffffU;
268 			if (putdata(*y * *x + begin, reps - *i))
269 				errx(1, "range error in conversion");
270 		}
271 	} else
272 		for (*i = 1, *x = begin; *i <= reps || infinity; (*i)++, *x += s)
273 			if (putdata(*x, reps - *i))
274 				errx(1, "range error in conversion");
275 	if (!nofinalnl)
276 		putchar('\n');
277 	exit(0);
278 }
279 
280 static int
281 putdata(double x, long notlast)
282 {
283 
284 	if (boring)
285 		printf("%s", format);
286 	else if (longdata && nosign) {
287 		if (x <= (double)ULONG_MAX && x >= (double)0)
288 			printf(format, (unsigned long)x);
289 		else
290 			return (1);
291 	} else if (longdata) {
292 		if (x <= (double)LONG_MAX && x >= (double)LONG_MIN)
293 			printf(format, (long)x);
294 		else
295 			return (1);
296 	} else if (chardata || (intdata && !nosign)) {
297 		if (x <= (double)INT_MAX && x >= (double)INT_MIN)
298 			printf(format, (int)x);
299 		else
300 			return (1);
301 	} else if (intdata) {
302 		if (x <= (double)UINT_MAX && x >= (double)0)
303 			printf(format, (unsigned int)x);
304 		else
305 			return (1);
306 
307 	} else
308 		printf(format, x);
309 	if (notlast != 0)
310 		fputs(sepstring, stdout);
311 
312 	return (0);
313 }
314 
315 static void
316 usage(void)
317 {
318 	fprintf(stderr, "%s\n%s\n",
319 	"usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]",
320 	"           [reps [begin [end [s]]]]");
321 	exit(1);
322 }
323 
324 static int
325 getprec(const char *str)
326 {
327 	const char	*p;
328 	const char	*q;
329 
330 	for (p = str; *p; p++)
331 		if (*p == '.')
332 			break;
333 	if (!*p)
334 		return (0);
335 	for (q = ++p; *p; p++)
336 		if (!isdigit(*p))
337 			break;
338 	return (p - q);
339 }
340 
341 static void
342 getformat(void)
343 {
344 	char	*p, *p2;
345 	int dot, hash, space, sign, numbers = 0;
346 	size_t sz;
347 
348 	if (boring)				/* no need to bother */
349 		return;
350 	for (p = format; *p; p++)		/* look for '%' */
351 		if (*p == '%' && *(p+1) != '%')	/* leave %% alone */
352 			break;
353 	sz = sizeof(format) - strlen(format) - 1;
354 	if (!*p && !chardata) {
355 		if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
356 			errx(1, "-w word too long");
357 	} else if (!*p && chardata) {
358 		if (strlcpy(p, "%c", sz) >= sz)
359 			errx(1, "-w word too long");
360 		intdata = 1;
361 	} else if (!*(p+1)) {
362 		if (sz <= 0)
363 			errx(1, "-w word too long");
364 		strcat(format, "%");		/* cannot end in single '%' */
365 	} else {
366 		/*
367 		 * Allow conversion format specifiers of the form
368 		 * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of
369 		 * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u}
370 		 */
371 		p2 = p++;
372 		dot = hash = space = sign = numbers = 0;
373 		while (!isalpha(*p)) {
374 			if (isdigit(*p)) {
375 				numbers++;
376 				p++;
377 			} else if ((*p == '#' && !(numbers|dot|sign|space|
378 			    hash++)) ||
379 			    (*p == ' ' && !(numbers|dot|space++)) ||
380 			    ((*p == '+' || *p == '-') && !(numbers|dot|sign++))
381 			    || (*p == '.' && !(dot++)))
382 				p++;
383 			else
384 				goto fmt_broken;
385 		}
386 		if (*p == 'l') {
387 			longdata = 1;
388 			if (*++p == 'l') {
389 				if (p[1] != '\0')
390 					p++;
391 				goto fmt_broken;
392 			}
393 		}
394 		switch (*p) {
395 		case 'o': case 'u': case 'x': case 'X':
396 			intdata = nosign = 1;
397 			break;
398 		case 'd': case 'i':
399 			intdata = 1;
400 			break;
401 		case 'D':
402 			if (longdata)
403 				goto fmt_broken;
404 			intdata = 1;
405 			break;
406 		case 'O': case 'U':
407 			if (longdata)
408 				goto fmt_broken;
409 			intdata = nosign = 1;
410 			break;
411 		case 'c':
412 			if (!(intdata | longdata)) {
413 				chardata = 1;
414 				break;
415 			}
416 		case 'h': case 'n': case 'p': case 'q': case 's': case 'L':
417 		case '$': case '*':
418 			goto fmt_broken;
419 		case 'f': case 'e': case 'g': case 'E': case 'G':
420 			if (!longdata)
421 				break;
422 			/* FALLTHROUGH */
423 		default:
424 fmt_broken:
425 			*++p = '\0';
426 			errx(1, "illegal or unsupported format '%s'", p2);
427 			/* NOTREACHED */
428 		}
429 		while (*++p)
430 			if (*p == '%' && *(p+1) && *(p+1) != '%')
431 				errx(1, "too many conversions");
432 			else if (*p == '%' && *(p+1) == '%')
433 				p++;
434 			else if (*p == '%' && !*(p+1)) {
435 				strcat(format, "%");
436 				break;
437 			}
438 	}
439 }
440