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