jot.c revision 1.21 1 /* $NetBSD: jot.c,v 1.21 2008/02/29 22:43:48 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.21 2008/02/29 22:43:48 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, y;
91 long i;
92
93 getargs(argc, argv);
94 if (randomize) {
95 x = (ender + dox - begin) * (ender > begin ? 1 : -1);
96 srandom((unsigned long) step);
97 for (i = 1; i <= reps || reps == 0; i++) {
98 y = (double) random() / INT_MAX;
99 putdata(y * x + begin, reps - i);
100 }
101 } else {
102 for (i = 1, x = begin; i <= reps || reps == 0; i++, x += step)
103 putdata(x, reps - i);
104 }
105 if (!nofinalnl)
106 putchar('\n');
107 exit(0);
108 }
109
110 static void
111 getargs(int argc, char *argv[])
112 {
113 unsigned int have = 0;
114 #define BEGIN 1
115 #define STEP 2 /* seed if -r */
116 #define REPS 4
117 #define ENDER 8
118 int n = 0;
119 long t;
120 char *ep;
121
122 for (;;) {
123 switch (getopt(argc, argv, "b:cnp:rs:w:")) {
124 default:
125 usage();
126 case -1:
127 break;
128 case 'c':
129 chardata = 1;
130 continue;
131 case 'n':
132 nofinalnl = 1;
133 continue;
134 case 'p':
135 prec = strtol(optarg, &ep, 0);
136 if (*ep != 0 || prec < 0)
137 errx(EXIT_FAILURE, "Bad precision value");
138 continue;
139 case 'r':
140 randomize = 1;
141 continue;
142 case 's':
143 sepstring = optarg;
144 continue;
145 case 'b':
146 boring = 1;
147 /* FALLTHROUGH */
148 case 'w':
149 strlcpy(format, optarg, sizeof(format));
150 continue;
151 }
152 break;
153 }
154 argc -= optind;
155 argv += optind;
156
157 switch (argc) { /* examine args right to left, falling thru cases */
158 case 4:
159 if (!is_default(argv[3])) {
160 step = strtod(argv[3], &ep);
161 if (*ep != 0)
162 errx(EXIT_FAILURE, "Bad step value: %s",
163 argv[3]);
164 have |= STEP;
165 }
166 case 3:
167 if (!is_default(argv[2])) {
168 if (!sscanf(argv[2], "%lf", &ender))
169 ender = argv[2][strlen(argv[2])-1];
170 have |= ENDER;
171 if (prec < 0)
172 n = getprec(argv[2]);
173 }
174 case 2:
175 if (!is_default(argv[1])) {
176 if (!sscanf(argv[1], "%lf", &begin))
177 begin = argv[1][strlen(argv[1])-1];
178 have |= BEGIN;
179 if (prec < 0)
180 prec = getprec(argv[1]);
181 if (n > prec) /* maximum precision */
182 prec = n;
183 }
184 case 1:
185 if (!is_default(argv[0])) {
186 reps = strtoul(argv[0], &ep, 0);
187 if (*ep != 0 || reps < 0)
188 errx(EXIT_FAILURE, "Bad reps value: %s",
189 argv[0]);
190 have |= REPS;
191 }
192 break;
193 case 0:
194 usage();
195 break;
196 default:
197 errx(EXIT_FAILURE,
198 "Too many arguments. What do you mean by %s?", argv[4]);
199 }
200 getformat();
201
202 if (prec == -1)
203 prec = 0;
204
205 if (randomize) {
206 /* 'step' is the seed here, use pseudo-random default */
207 if (!(have & STEP))
208 step = time(NULL) * getpid();
209 /* Take the default values for everything else */
210 return;
211 }
212
213 /*
214 * The loop we run uses begin/step/reps, so if we have been
215 * given an end value (ender) we must use it to replace the
216 * default values of the others.
217 * We will assume a begin of 0 and step of 1 if necessary.
218 */
219
220 switch (have) {
221
222 case ENDER | STEP:
223 case ENDER | STEP | BEGIN:
224 /* Calculate reps */
225 if (step == 0.0)
226 reps = 0; /* ie infinite */
227 else {
228 reps = (ender - begin + step) / step;
229 if (reps <= 0)
230 errx(EXIT_FAILURE, "Impossible stepsize");
231 }
232 break;
233
234 case REPS | ENDER:
235 case REPS | ENDER | STEP:
236 /* Calculate begin */
237 if (reps == 0)
238 errx(EXIT_FAILURE,
239 "Must specify begin if reps == 0");
240 begin = ender - reps * step + step;
241 break;
242
243 case REPS | BEGIN | ENDER:
244 /* Calculate step */
245 if (reps == 0)
246 errx(EXIT_FAILURE,
247 "Infinite sequences cannot be bounded");
248 if (reps == 1)
249 step = 0.0;
250 else
251 step = (ender - begin) / (reps - 1);
252 break;
253
254 case REPS | BEGIN | ENDER | STEP:
255 /* reps given and implied - take smaller */
256 if (step == 0.0)
257 break;
258 t = (ender - begin + step) / step;
259 if (t <= 0)
260 errx(EXIT_FAILURE,
261 "Impossible stepsize");
262 if (t < reps)
263 reps = t;
264 break;
265
266 default:
267 /* No values can be calculated, use defaults */
268 break;
269 }
270 }
271
272 static void
273 putdata(double x, long notlast)
274 {
275 long d = floor(x);
276
277 if (boring) /* repeated word */
278 printf("%s", format);
279 else if (dox) /* scalar */
280 printf(format, d);
281 else /* real */
282 printf(format, x);
283 if (notlast != 0)
284 fputs(sepstring, stdout);
285 }
286
287 __dead static void
288 usage(void)
289 {
290 (void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] "
291 "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n",
292 getprogname());
293 exit(1);
294 }
295
296 static int
297 getprec(char *num_str)
298 {
299
300 num_str = strchr(num_str, '.');
301 if (num_str == NULL)
302 return 0;
303 return strspn(num_str + 1, "0123456789");
304 }
305
306 static void
307 getformat(void)
308 {
309 char *p;
310 size_t sz;
311
312 if (boring) /* no need to bother */
313 return;
314 for (p = format; *p; p++) { /* look for '%' */
315 if (*p == '%') {
316 if (*(p+1) != '%')
317 break;
318 p++; /* leave %% alone */
319 }
320 }
321 sz = sizeof(format) - strlen(format) - 1;
322 if (!*p) {
323 if (chardata || prec == 0) {
324 if (snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz)
325 errx(EXIT_FAILURE, "-w word too long");
326 dox = 1;
327 } else {
328 if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
329 errx(EXIT_FAILURE, "-w word too long");
330 }
331 } else if (!*(p+1)) {
332 if (sz <= 0)
333 errx(EXIT_FAILURE, "-w word too long");
334 strcat(format, "%"); /* cannot end in single '%' */
335 } else {
336 p++; /* skip leading % */
337 for(; *p && !isalpha((unsigned char)*p); p++) {
338 /* allow all valid printf(3) flags, but deny '*' */
339 if (!strchr("0123456789#-+. ", *p))
340 break;
341 }
342 /* Allow 'l' prefix, but no other. */
343 if (*p == 'l')
344 p++;
345 switch (*p) {
346 case 'f': case 'e': case 'g': case '%':
347 case 'E': case 'G':
348 break;
349 case 's':
350 errx(EXIT_FAILURE,
351 "cannot convert numeric data to strings");
352 break;
353 case 'd': case 'o': case 'x': case 'u':
354 case 'D': case 'O': case 'X': case 'U':
355 case 'c': case 'i':
356 dox = 1;
357 break;
358 default:
359 errx(EXIT_FAILURE, "unknown or invalid format `%s'",
360 format);
361 }
362 /* Need to check for trailing stuff to print */
363 for (; *p; p++) /* look for '%' */
364 if (*p == '%') {
365 if (*(p+1) != '%')
366 break;
367 p++; /* leave %% alone */
368 }
369 if (*p)
370 errx(EXIT_FAILURE, "unknown or invalid format `%s'",
371 format);
372 }
373 }
374