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