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