jot.c revision 1.22 1 /* $NetBSD: jot.c,v 1.22 2008/03/02 21:33:42 dsl Exp $ */
2
3 /*-
4 * Copyright (c) 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1993\n\
35 The Regents of the University of California. All rights reserved.\n");
36 #endif /* not lint */
37
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93";
41 #endif
42 __RCSID("$NetBSD: jot.c,v 1.22 2008/03/02 21:33:42 dsl Exp $");
43 #endif /* not lint */
44
45 /*
46 * jot - print sequential or random data
47 *
48 * Author: John Kunze, Office of Comp. Affairs, UCB
49 */
50
51 #include <ctype.h>
52 #include <err.h>
53 #include <limits.h>
54 #include <math.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <time.h>
59 #include <unistd.h>
60
61 #define REPS_DEF 100
62 #define BEGIN_DEF 1
63 #define ENDER_DEF 100
64 #define STEP_DEF 1
65
66 #define is_default(s) (strcmp((s), "-") == 0)
67
68 static double begin = BEGIN_DEF;
69 static double ender = ENDER_DEF;
70 static double step = STEP_DEF;
71 static long reps = REPS_DEF;
72 static int randomize;
73 static int boring;
74 static int prec = -1;
75 static int dox;
76 static int chardata;
77 static int nofinalnl;
78 static const char *sepstring = "\n";
79 static char format[BUFSIZ];
80
81 static void getargs(int, char *[]);
82 static void getformat(void);
83 static int getprec(char *);
84 static void putdata(double, long);
85 static void usage(void) __dead;
86
87 int
88 main(int argc, char *argv[])
89 {
90 double x;
91 long i;
92
93 getargs(argc, argv);
94 if (randomize) {
95 x = ender - begin;
96 if (dox == 0)
97 /*
98 * We are printing floating point, generate random
99 * number that include both supplied limits.
100 * Due to FP routing for display the low and high
101 * values are likely to occur half as often as all
102 * the others.
103 */
104 x /= (1u << 31) - 1.0;
105 else {
106 /*
107 * We are printing integers increase the range by
108 * one but ensure we never generate it.
109 * This makes all the integer values equally likely.
110 */
111 if (ender > begin)
112 x += 1.0;
113 else
114 x -= 1.0;
115 x /= (1u << 31);
116 }
117 srandom((unsigned long) step);
118 for (i = 1; i <= reps || reps == 0; i++)
119 putdata(random() * x + begin, reps - i);
120 } else {
121 /*
122 * If we are going to display as integer, add 0.5 here
123 * and use floor(x) later to get sane rounding.
124 */
125 x = begin;
126 if (dox)
127 x += 0.5;
128 for (i = 1; i <= reps || reps == 0; i++, x += step)
129 putdata(x, reps - i);
130 }
131 if (!nofinalnl)
132 putchar('\n');
133 exit(0);
134 }
135
136 static void
137 getargs(int argc, char *argv[])
138 {
139 unsigned int have = 0;
140 #define BEGIN 1
141 #define STEP 2 /* seed if -r */
142 #define REPS 4
143 #define ENDER 8
144 int n = 0;
145 long t;
146 char *ep;
147
148 for (;;) {
149 switch (getopt(argc, argv, "b:cnp:rs:w:")) {
150 default:
151 usage();
152 case -1:
153 break;
154 case 'c':
155 chardata = 1;
156 continue;
157 case 'n':
158 nofinalnl = 1;
159 continue;
160 case 'p':
161 prec = strtol(optarg, &ep, 0);
162 if (*ep != 0 || prec < 0)
163 errx(EXIT_FAILURE, "Bad precision value");
164 continue;
165 case 'r':
166 randomize = 1;
167 continue;
168 case 's':
169 sepstring = optarg;
170 continue;
171 case 'b':
172 boring = 1;
173 /* FALLTHROUGH */
174 case 'w':
175 strlcpy(format, optarg, sizeof(format));
176 continue;
177 }
178 break;
179 }
180 argc -= optind;
181 argv += optind;
182
183 switch (argc) { /* examine args right to left, falling thru cases */
184 case 4:
185 if (!is_default(argv[3])) {
186 step = strtod(argv[3], &ep);
187 if (*ep != 0)
188 errx(EXIT_FAILURE, "Bad step value: %s",
189 argv[3]);
190 have |= STEP;
191 }
192 case 3:
193 if (!is_default(argv[2])) {
194 if (!sscanf(argv[2], "%lf", &ender))
195 ender = argv[2][strlen(argv[2])-1];
196 have |= ENDER;
197 if (prec < 0)
198 n = getprec(argv[2]);
199 }
200 case 2:
201 if (!is_default(argv[1])) {
202 if (!sscanf(argv[1], "%lf", &begin))
203 begin = argv[1][strlen(argv[1])-1];
204 have |= BEGIN;
205 if (prec < 0)
206 prec = getprec(argv[1]);
207 if (n > prec) /* maximum precision */
208 prec = n;
209 }
210 case 1:
211 if (!is_default(argv[0])) {
212 reps = strtoul(argv[0], &ep, 0);
213 if (*ep != 0 || reps < 0)
214 errx(EXIT_FAILURE, "Bad reps value: %s",
215 argv[0]);
216 have |= REPS;
217 }
218 break;
219 case 0:
220 usage();
221 break;
222 default:
223 errx(EXIT_FAILURE,
224 "Too many arguments. What do you mean by %s?", argv[4]);
225 }
226 getformat();
227
228 if (prec == -1)
229 prec = 0;
230
231 if (randomize) {
232 /* 'step' is the seed here, use pseudo-random default */
233 if (!(have & STEP))
234 step = time(NULL) * getpid();
235 /* Take the default values for everything else */
236 return;
237 }
238
239 /*
240 * The loop we run uses begin/step/reps, so if we have been
241 * given an end value (ender) we must use it to replace the
242 * default values of the others.
243 * We will assume a begin of 0 and step of 1 if necessary.
244 */
245
246 switch (have) {
247
248 case ENDER | STEP:
249 case ENDER | STEP | BEGIN:
250 /* Calculate reps */
251 if (step == 0.0)
252 reps = 0; /* ie infinite */
253 else {
254 reps = (ender - begin + step) / step;
255 if (reps <= 0)
256 errx(EXIT_FAILURE, "Impossible stepsize");
257 }
258 break;
259
260 case REPS | ENDER:
261 case REPS | ENDER | STEP:
262 /* Calculate begin */
263 if (reps == 0)
264 errx(EXIT_FAILURE,
265 "Must specify begin if reps == 0");
266 begin = ender - reps * step + step;
267 break;
268
269 case REPS | BEGIN | ENDER:
270 /* Calculate step */
271 if (reps == 0)
272 errx(EXIT_FAILURE,
273 "Infinite sequences cannot be bounded");
274 if (reps == 1)
275 step = 0.0;
276 else
277 step = (ender - begin) / (reps - 1);
278 break;
279
280 case REPS | BEGIN | ENDER | STEP:
281 /* reps given and implied - take smaller */
282 if (step == 0.0)
283 break;
284 t = (ender - begin + step) / step;
285 if (t <= 0)
286 errx(EXIT_FAILURE,
287 "Impossible stepsize");
288 if (t < reps)
289 reps = t;
290 break;
291
292 default:
293 /* No values can be calculated, use defaults */
294 break;
295 }
296 }
297
298 static void
299 putdata(double x, long notlast)
300 {
301
302 if (boring) /* repeated word */
303 printf("%s", format);
304 else if (dox) /* scalar */
305 printf(format, (long)floor(x));
306 else /* real */
307 printf(format, x);
308 if (notlast != 0)
309 fputs(sepstring, stdout);
310 }
311
312 __dead static void
313 usage(void)
314 {
315 (void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] "
316 "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n",
317 getprogname());
318 exit(1);
319 }
320
321 static int
322 getprec(char *num_str)
323 {
324
325 num_str = strchr(num_str, '.');
326 if (num_str == NULL)
327 return 0;
328 return strspn(num_str + 1, "0123456789");
329 }
330
331 static void
332 getformat(void)
333 {
334 char *p;
335 size_t sz;
336
337 if (boring) /* no need to bother */
338 return;
339 for (p = format; *p; p++) { /* look for '%' */
340 if (*p == '%') {
341 if (*(p+1) != '%')
342 break;
343 p++; /* leave %% alone */
344 }
345 }
346 sz = sizeof(format) - strlen(format) - 1;
347 if (!*p) {
348 if (chardata || prec == 0) {
349 if (snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz)
350 errx(EXIT_FAILURE, "-w word too long");
351 dox = 1;
352 } else {
353 if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
354 errx(EXIT_FAILURE, "-w word too long");
355 }
356 } else if (!*(p+1)) {
357 if (sz <= 0)
358 errx(EXIT_FAILURE, "-w word too long");
359 strcat(format, "%"); /* cannot end in single '%' */
360 } else {
361 p++; /* skip leading % */
362 for(; *p && !isalpha((unsigned char)*p); p++) {
363 /* allow all valid printf(3) flags, but deny '*' */
364 if (!strchr("0123456789#-+. ", *p))
365 break;
366 }
367 /* Allow 'l' prefix, but no other. */
368 if (*p == 'l')
369 p++;
370 switch (*p) {
371 case 'f': case 'e': case 'g': case '%':
372 case 'E': case 'G':
373 break;
374 case 's':
375 errx(EXIT_FAILURE,
376 "cannot convert numeric data to strings");
377 break;
378 case 'd': case 'o': case 'x': case 'u':
379 case 'D': case 'O': case 'X': case 'U':
380 case 'c': case 'i':
381 dox = 1;
382 break;
383 default:
384 errx(EXIT_FAILURE, "unknown or invalid format `%s'",
385 format);
386 }
387 /* Need to check for trailing stuff to print */
388 for (; *p; p++) /* look for '%' */
389 if (*p == '%') {
390 if (*(p+1) != '%')
391 break;
392 p++; /* leave %% alone */
393 }
394 if (*p)
395 errx(EXIT_FAILURE, "unknown or invalid format `%s'",
396 format);
397 }
398 }
399