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 #endif 45 #include <sys/cdefs.h> 46 __FBSDID("$FreeBSD$"); 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 <stdint.h> 59 #include <stdlib.h> 60 #include <stdbool.h> 61 #include <string.h> 62 #include <time.h> 63 #include <unistd.h> 64 65 /* Defaults */ 66 #define REPS_DEF 100 67 #define BEGIN_DEF 1 68 #define ENDER_DEF 100 69 #define STEP_DEF 1 70 71 /* Flags of options that have been set */ 72 #define HAVE_STEP 1 73 #define HAVE_ENDER 2 74 #define HAVE_BEGIN 4 75 #define HAVE_REPS 8 76 77 #define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0) 78 79 static bool boring; 80 static int prec; 81 static bool longdata; 82 static bool intdata; 83 static bool chardata; 84 static bool nosign; 85 static const char *sepstring = "\n"; 86 static char format[BUFSIZ]; 87 88 static void getformat(void); 89 static int getprec(const char *); 90 static int putdata(double, bool); 91 static void usage(void); 92 93 int 94 main(int argc, char **argv) 95 { 96 bool have_format = false; 97 bool infinity = false; 98 bool nofinalnl = false; 99 bool randomize = false; 100 bool use_random = false; 101 int ch; 102 int mask = 0; 103 int n = 0; 104 double begin; 105 double divisor; 106 double ender; 107 double s; 108 double x, y; 109 long i; 110 long reps; 111 112 while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1) 113 switch (ch) { 114 case 'b': 115 boring = true; 116 /* FALLTHROUGH */ 117 case 'w': 118 if (strlcpy(format, optarg, sizeof(format)) >= 119 sizeof(format)) 120 errx(1, "-%c word too long", ch); 121 have_format = true; 122 break; 123 case 'c': 124 chardata = true; 125 break; 126 case 'n': 127 nofinalnl = true; 128 break; 129 case 'p': 130 prec = atoi(optarg); 131 if (prec <= 0) 132 errx(1, "bad precision value"); 133 have_format = true; 134 break; 135 case 'r': 136 randomize = true; 137 break; 138 case 's': 139 sepstring = optarg; 140 break; 141 default: 142 usage(); 143 } 144 argc -= optind; 145 argv += optind; 146 147 switch (argc) { /* examine args right to left, falling thru cases */ 148 case 4: 149 if (!is_default(argv[3])) { 150 if (!sscanf(argv[3], "%lf", &s)) 151 errx(1, "bad s value: %s", argv[3]); 152 mask |= HAVE_STEP; 153 if (randomize) 154 use_random = true; 155 } 156 /* FALLTHROUGH */ 157 case 3: 158 if (!is_default(argv[2])) { 159 if (!sscanf(argv[2], "%lf", &ender)) 160 ender = argv[2][strlen(argv[2])-1]; 161 mask |= HAVE_ENDER; 162 if (!prec) 163 n = getprec(argv[2]); 164 } 165 /* FALLTHROUGH */ 166 case 2: 167 if (!is_default(argv[1])) { 168 if (!sscanf(argv[1], "%lf", &begin)) 169 begin = argv[1][strlen(argv[1])-1]; 170 mask |= HAVE_BEGIN; 171 if (!prec) 172 prec = getprec(argv[1]); 173 if (n > prec) /* maximum precision */ 174 prec = n; 175 } 176 /* FALLTHROUGH */ 177 case 1: 178 if (!is_default(argv[0])) { 179 if (!sscanf(argv[0], "%ld", &reps)) 180 errx(1, "bad reps value: %s", argv[0]); 181 mask |= HAVE_REPS; 182 } 183 break; 184 case 0: 185 usage(); 186 default: 187 errx(1, "too many arguments. What do you mean by %s?", 188 argv[4]); 189 } 190 getformat(); 191 while (mask) /* 4 bit mask has 1's where last 4 args were given */ 192 switch (mask) { /* fill in the 0's by default or computation */ 193 case HAVE_STEP: 194 case HAVE_ENDER: 195 case HAVE_ENDER | HAVE_STEP: 196 case HAVE_BEGIN: 197 case HAVE_BEGIN | HAVE_STEP: 198 reps = REPS_DEF; 199 mask |= HAVE_REPS; 200 break; 201 case HAVE_BEGIN | HAVE_ENDER: 202 s = ender > begin ? 1 : -1; 203 mask |= HAVE_STEP; 204 break; 205 case HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: 206 if (randomize) 207 reps = REPS_DEF; 208 else if (s == 0.0) 209 reps = 0; 210 else 211 reps = (ender - begin + s) / s; 212 if (reps <= 0) 213 errx(1, "impossible stepsize"); 214 mask = 0; 215 break; 216 case HAVE_REPS: 217 case HAVE_REPS | HAVE_STEP: 218 begin = BEGIN_DEF; 219 mask |= HAVE_BEGIN; 220 break; 221 case HAVE_REPS | HAVE_ENDER: 222 s = STEP_DEF; 223 mask = HAVE_REPS | HAVE_ENDER | HAVE_STEP; 224 break; 225 case HAVE_REPS | HAVE_ENDER | HAVE_STEP: 226 if (randomize) 227 begin = BEGIN_DEF; 228 else if (reps == 0) 229 errx(1, "must specify begin if reps == 0"); 230 begin = ender - reps * s + s; 231 mask = 0; 232 break; 233 case HAVE_REPS | HAVE_BEGIN: 234 s = STEP_DEF; 235 mask = HAVE_REPS | HAVE_BEGIN | HAVE_STEP; 236 break; 237 case HAVE_REPS | HAVE_BEGIN | HAVE_STEP: 238 if (randomize) 239 ender = ENDER_DEF; 240 else 241 ender = begin + reps * s - s; 242 mask = 0; 243 break; 244 case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER: 245 if (reps == 0) 246 errx(1, "infinite sequences cannot be bounded"); 247 else if (reps == 1) 248 s = 0.0; 249 else 250 s = (ender - begin) / (reps - 1); 251 mask = 0; 252 break; 253 case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: 254 /* if reps given and implied, */ 255 if (!randomize && s != 0.0) { 256 long t = (ender - begin + s) / s; 257 if (t <= 0) 258 errx(1, "impossible stepsize"); 259 if (t < reps) /* take lesser */ 260 reps = t; 261 } 262 mask = 0; 263 break; 264 default: 265 errx(1, "bad mask"); 266 } 267 if (reps == 0) 268 infinity = true; 269 if (randomize) { 270 if (use_random) { 271 srandom((unsigned long)s); 272 divisor = (double)INT32_MAX + 1; 273 } else 274 divisor = (double)UINT32_MAX + 1; 275 276 /* 277 * Attempt to DWIM when the user has specified an 278 * integer range within that of the random number 279 * generator: distribute the numbers equally in 280 * the range [begin .. ender]. Jot's default %.0f 281 * format would make the appearance of the first and 282 * last specified value half as likely as the rest. 283 */ 284 if (!have_format && prec == 0 && 285 begin >= 0 && begin < divisor && 286 ender >= 0 && ender < divisor) { 287 ender += 1; 288 nosign = true; 289 intdata = true; 290 (void)strlcpy(format, 291 chardata ? "%c" : "%u", sizeof(format)); 292 } 293 x = (ender - begin) * (ender > begin ? 1 : -1); 294 for (i = 1; i <= reps || infinity; i++) { 295 if (use_random) 296 y = random() / divisor; 297 else 298 y = arc4random() / divisor; 299 if (putdata(y * x + begin, !(reps - i))) 300 errx(1, "range error in conversion"); 301 } 302 } else 303 for (i = 1, x = begin; i <= reps || infinity; i++, x += s) 304 if (putdata(x, !(reps - i))) 305 errx(1, "range error in conversion"); 306 if (!nofinalnl) 307 putchar('\n'); 308 exit(0); 309 } 310 311 /* 312 * Send x to stdout using the specified format. 313 * Last is true if this is the set's last value. 314 * Return 0 if OK, or a positive number if the number passed was 315 * outside the range specified by the various flags. 316 */ 317 static int 318 putdata(double x, bool last) 319 { 320 321 if (boring) 322 printf("%s", format); 323 else if (longdata && nosign) { 324 if (x <= (double)ULONG_MAX && x >= (double)0) 325 printf(format, (unsigned long)x); 326 else 327 return (1); 328 } else if (longdata) { 329 if (x <= (double)LONG_MAX && x >= (double)LONG_MIN) 330 printf(format, (long)x); 331 else 332 return (1); 333 } else if (chardata || (intdata && !nosign)) { 334 if (x <= (double)INT_MAX && x >= (double)INT_MIN) 335 printf(format, (int)x); 336 else 337 return (1); 338 } else if (intdata) { 339 if (x <= (double)UINT_MAX && x >= (double)0) 340 printf(format, (unsigned int)x); 341 else 342 return (1); 343 344 } else 345 printf(format, x); 346 if (!last) 347 fputs(sepstring, stdout); 348 349 return (0); 350 } 351 352 static void 353 usage(void) 354 { 355 fprintf(stderr, "%s\n%s\n", 356 "usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]", 357 " [reps [begin [end [s]]]]"); 358 exit(1); 359 } 360 361 /* 362 * Return the number of digits following the number's decimal point. 363 * Return 0 if no decimal point is found. 364 */ 365 static int 366 getprec(const char *str) 367 { 368 const char *p; 369 const char *q; 370 371 for (p = str; *p; p++) 372 if (*p == '.') 373 break; 374 if (!*p) 375 return (0); 376 for (q = ++p; *p; p++) 377 if (!isdigit((unsigned char)*p)) 378 break; 379 return (p - q); 380 } 381 382 /* 383 * Set format, intdata, chardata, longdata, and nosign 384 * based on the command line arguments. 385 */ 386 static void 387 getformat(void) 388 { 389 char *p, *p2; 390 int dot, hash, space, sign, numbers = 0; 391 size_t sz; 392 393 if (boring) /* no need to bother */ 394 return; 395 for (p = format; *p; p++) /* look for '%' */ 396 if (*p == '%') 397 if (p[1] == '%') 398 p++; /* leave %% alone */ 399 else 400 break; 401 sz = sizeof(format) - strlen(format) - 1; 402 if (!*p && !chardata) { 403 if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) 404 errx(1, "-w word too long"); 405 } else if (!*p && chardata) { 406 if (strlcpy(p, "%c", sz) >= sz) 407 errx(1, "-w word too long"); 408 intdata = true; 409 } else if (!*(p+1)) { 410 if (sz <= 0) 411 errx(1, "-w word too long"); 412 strcat(format, "%"); /* cannot end in single '%' */ 413 } else { 414 /* 415 * Allow conversion format specifiers of the form 416 * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of 417 * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u} 418 */ 419 p2 = p++; 420 dot = hash = space = sign = numbers = 0; 421 while (!isalpha((unsigned char)*p)) { 422 if (isdigit((unsigned char)*p)) { 423 numbers++; 424 p++; 425 } else if ((*p == '#' && !(numbers|dot|sign|space| 426 hash++)) || 427 (*p == ' ' && !(numbers|dot|space++)) || 428 ((*p == '+' || *p == '-') && !(numbers|dot|sign++)) 429 || (*p == '.' && !(dot++))) 430 p++; 431 else 432 goto fmt_broken; 433 } 434 if (*p == 'l') { 435 longdata = true; 436 if (*++p == 'l') { 437 if (p[1] != '\0') 438 p++; 439 goto fmt_broken; 440 } 441 } 442 switch (*p) { 443 case 'o': case 'u': case 'x': case 'X': 444 intdata = nosign = true; 445 break; 446 case 'd': case 'i': 447 intdata = true; 448 break; 449 case 'D': 450 if (!longdata) { 451 intdata = true; 452 break; 453 } 454 case 'O': case 'U': 455 if (!longdata) { 456 intdata = nosign = true; 457 break; 458 } 459 case 'c': 460 if (!(intdata | longdata)) { 461 chardata = true; 462 break; 463 } 464 case 'h': case 'n': case 'p': case 'q': case 's': case 'L': 465 case '$': case '*': 466 goto fmt_broken; 467 case 'f': case 'e': case 'g': case 'E': case 'G': 468 if (!longdata) 469 break; 470 /* FALLTHROUGH */ 471 default: 472 fmt_broken: 473 *++p = '\0'; 474 errx(1, "illegal or unsupported format '%s'", p2); 475 /* NOTREACHED */ 476 } 477 while (*++p) 478 if (*p == '%' && *(p+1) && *(p+1) != '%') 479 errx(1, "too many conversions"); 480 else if (*p == '%' && *(p+1) == '%') 481 p++; 482 else if (*p == '%' && !*(p+1)) { 483 if (strlcat(format, "%", sizeof(format)) >= 484 sizeof(format)) 485 errx(1, "-w word too long"); 486 break; 487 } 488 } 489 } 490