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