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