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