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