1 /* $OpenBSD: jot.c,v 1.49 2019/06/27 18:03:36 deraadt 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 <ctype.h> 40 #include <err.h> 41 #include <limits.h> 42 #include <math.h> 43 #include <stdbool.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 1 51 #define BEGIN 2 52 #define ENDER 4 53 #define STEP 8 54 55 #define is_default(s) (strcmp((s), "-") == 0) 56 57 static long reps = 100; 58 static double begin = 1; 59 static double ender = 100; 60 static double step = 1; 61 62 static char *format = ""; 63 static char *sepstring = "\n"; 64 static int prec = -1; 65 static bool boring; 66 static bool chardata; 67 static bool finalnl = true; 68 static bool infinity; 69 static bool intdata; 70 static bool longdata; 71 static bool nosign; 72 static bool randomize; 73 74 static void getformat(void); 75 static int getprec(char *); 76 static int putdata(double, bool); 77 static void __dead usage(void); 78 79 int 80 main(int argc, char *argv[]) 81 { 82 double x; 83 double y; 84 long i; 85 unsigned int mask = 0; 86 int n = 0; 87 int ch; 88 const char *errstr; 89 90 if (pledge("stdio", NULL) == -1) 91 err(1, "pledge"); 92 93 while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1) { 94 switch (ch) { 95 case 'b': 96 boring = true; 97 format = optarg; 98 break; 99 case 'c': 100 chardata = true; 101 break; 102 case 'n': 103 finalnl = false; 104 break; 105 case 'p': 106 prec = strtonum(optarg, 0, INT_MAX, &errstr); 107 if (errstr != NULL) 108 errx(1, "bad precision value, %s: %s", errstr, 109 optarg); 110 break; 111 case 'r': 112 randomize = true; 113 break; 114 case 's': 115 sepstring = optarg; 116 break; 117 case 'w': 118 format = optarg; 119 break; 120 default: 121 usage(); 122 } 123 } 124 argc -= optind; 125 argv += optind; 126 127 switch (argc) { /* examine args right to left, falling thru cases */ 128 case 4: 129 if (!is_default(argv[3])) { 130 if (!sscanf(argv[3], "%lf", &step)) 131 errx(1, "Bad s value: %s", argv[3]); 132 mask |= STEP; 133 if (randomize) 134 warnx("random seeding not supported"); 135 } 136 case 3: 137 if (!is_default(argv[2])) { 138 if (!sscanf(argv[2], "%lf", &ender)) 139 ender = argv[2][strlen(argv[2])-1]; 140 mask |= ENDER; 141 if (prec == -1) 142 n = getprec(argv[2]); 143 } 144 case 2: 145 if (!is_default(argv[1])) { 146 if (!sscanf(argv[1], "%lf", &begin)) 147 begin = argv[1][strlen(argv[1])-1]; 148 mask |= BEGIN; 149 if (prec == -1) 150 prec = getprec(argv[1]); 151 if (n > prec) /* maximum precision */ 152 prec = n; 153 } 154 case 1: 155 if (!is_default(argv[0])) { 156 reps = strtonum(argv[0], 0, LONG_MAX, &errstr); 157 if (errstr != NULL) 158 errx(1, "Bad reps value, %s: %s", errstr, 159 argv[0]); 160 mask |= REPS; 161 if (reps == 0) 162 infinity = true; 163 if (prec == -1) 164 prec = 0; 165 } 166 case 0: 167 break; 168 default: 169 errx(1, "Too many arguments. What do you mean by %s?", 170 argv[4]); 171 } 172 173 if (!boring) 174 getformat(); 175 176 if (!randomize) { 177 /* 178 * Consolidate the values of reps, begin, ender, step: 179 * The formula ender - begin == (reps - 1) * step shows that any 180 * three determine the fourth (unless reps == 1 or step == 0). 181 * The manual states the following rules: 182 * 1. If four are specified, compare the given and the computed 183 * value of reps and take the smaller of the two. 184 * 2. If steps was omitted, it takes the default, unless both 185 * begin and ender were specified. 186 * 3. Assign defaults to omitted values for reps, begin, ender, 187 * from left to right. 188 */ 189 switch (mask) { /* Four cases involve both begin and ender. */ 190 case REPS | BEGIN | ENDER | STEP: 191 if (infinity) 192 errx(1, 193 "Can't specify end of infinite sequence"); 194 if (step != 0.0) { 195 long t = (ender - begin + step) / step; 196 if (t <= 0) 197 errx(1, "Impossible stepsize"); 198 if (t < reps) 199 reps = t; 200 } 201 break; 202 case REPS | BEGIN | ENDER: 203 if (infinity) 204 errx(1, 205 "Can't specify end of infinite sequence"); 206 if (reps == 1) 207 step = 0.0; 208 else 209 step = (ender - begin) / (reps - 1); 210 break; 211 case BEGIN | ENDER: 212 step = ender > begin ? 1 : -1; /* FreeBSD's behavior. */ 213 /* FALLTHROUGH */ 214 case BEGIN | ENDER | STEP: 215 if (step == 0.0) { 216 reps = 0; 217 infinity = true; 218 break; 219 } 220 reps = (ender - begin + step) / step; 221 if (reps <= 0) 222 errx(1, "Impossible stepsize"); 223 break; 224 case ENDER: /* Four cases involve only ender. */ 225 case ENDER | STEP: 226 case REPS | ENDER: 227 case REPS | ENDER | STEP: 228 if (infinity) 229 errx(1, 230 "Must specify start of infinite sequence"); 231 begin = ender - reps * step + step; 232 break; 233 default: 234 /* 235 * The remaining eight cases omit ender. We don't need 236 * to compute anything because only reps, begin, step 237 * are used for producing output below. Rules 2. and 3. 238 * together imply that ender will be set last. 239 */ 240 break; 241 } 242 243 for (i = 1, x = begin; i <= reps || infinity; i++, x += step) 244 if (putdata(x, reps == i && !infinity)) 245 errx(1, "range error in conversion: %f", x); 246 } else { /* Random output: use defaults for omitted values. */ 247 bool use_unif; 248 uint32_t pow10 = 1; 249 uint32_t uintx = 0; /* Initialized to make gcc happy. */ 250 251 if (prec > 9) /* pow(10, prec) > UINT32_MAX */ 252 errx(1, "requested precision too large"); 253 254 if (ender < begin) { 255 x = begin; 256 begin = ender; 257 ender = x; 258 } 259 x = ender - begin; 260 261 if (prec == 0 && (fmod(ender, 1) != 0 || fmod(begin, 1) != 0)) 262 use_unif = 0; 263 else { 264 while (prec-- > 0) 265 pow10 *= 10; 266 /* 267 * If pow10 * (ender - begin) is an integer, use 268 * arc4random_uniform(). 269 */ 270 use_unif = fmod(pow10 * (ender - begin), 1) == 0; 271 if (use_unif) { 272 uintx = pow10 * (ender - begin); 273 if (uintx >= UINT32_MAX) 274 errx(1, "requested range too large"); 275 uintx++; 276 } 277 } 278 279 for (i = 1; i <= reps || infinity; i++) { 280 double v; 281 282 if (use_unif) { 283 y = arc4random_uniform(uintx) / (double)pow10; 284 v = y + begin; 285 } else { 286 y = arc4random() / ((double)0xffffffff + 1); 287 v = y * x + begin; 288 } 289 if (putdata(v, reps == i && !infinity)) 290 errx(1, "range error in conversion: %f", v); 291 } 292 } 293 294 if (finalnl) 295 putchar('\n'); 296 297 return 0; 298 } 299 300 static int 301 putdata(double x, bool last) 302 { 303 if (boring) 304 printf("%s", format); 305 else if (longdata && nosign) { 306 if (x <= (double)ULONG_MAX && x >= 0.0) 307 printf(format, (unsigned long)x); 308 else 309 return 1; 310 } else if (longdata) { 311 if (x <= (double)LONG_MAX && x >= (double)LONG_MIN) 312 printf(format, (long)x); 313 else 314 return 1; 315 } else if (chardata || (intdata && !nosign)) { 316 if (x <= (double)INT_MAX && x >= (double)INT_MIN) 317 printf(format, (int)x); 318 else 319 return 1; 320 } else if (intdata) { 321 if (x <= (double)UINT_MAX && x >= 0.0) 322 printf(format, (unsigned int)x); 323 else 324 return 1; 325 } else 326 printf(format, x); 327 if (!last) 328 fputs(sepstring, stdout); 329 330 return 0; 331 } 332 333 static void __dead 334 usage(void) 335 { 336 (void)fprintf(stderr, "usage: jot [-cnr] [-b word] [-p precision] " 337 "[-s string] [-w word]\n" 338 " [reps [begin [end [s]]]]\n"); 339 exit(1); 340 } 341 342 static int 343 getprec(char *s) 344 { 345 if ((s = strchr(s, '.')) == NULL) 346 return 0; 347 return strspn(s + 1, "0123456789"); 348 } 349 350 static void 351 getformat(void) 352 { 353 char *p; 354 355 p = format; 356 while ((p = strchr(p, '%')) != NULL && p[1] == '%') 357 p += 2; 358 359 if (p == NULL && !chardata) { 360 if (asprintf(&format, "%s%%.%df", format, prec) == -1) 361 err(1, NULL); 362 } else if (p == NULL && chardata) { 363 if (asprintf(&format, "%s%%c", format) == -1) 364 err(1, NULL); 365 } else if (p[1] == '\0') { 366 /* cannot end in single '%' */ 367 if (asprintf(&format, "%s%%", format) == -1) 368 err(1, NULL); 369 } else { 370 /* 371 * Allow conversion format specifiers of the form 372 * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of 373 * [l]{d,i,o,u,x} or {f,e,g,F,E,G,d,o,x,D,O,U,X,c,u} 374 */ 375 char *fmt; 376 int dot, hash, space, sign, numbers; 377 378 fmt = p++; 379 dot = hash = space = sign = numbers = 0; 380 while (!isalpha((unsigned char)*p)) { 381 if (isdigit((unsigned char)*p)) { 382 numbers++; 383 p++; 384 } else if ((*p == '#' && !(numbers|dot|sign|space| 385 hash++)) || 386 (*p == ' ' && !(numbers|dot|space++)) || 387 ((*p == '+' || *p == '-') && !(numbers|dot|sign++)) 388 || (*p == '.' && !(dot++))) 389 p++; 390 else 391 goto fmt_broken; 392 } 393 if (*p == 'l') { 394 longdata = true; 395 if (*++p == 'l') { 396 p++; 397 goto fmt_broken; 398 } 399 } 400 switch (*p) { 401 case 'd': 402 case 'i': 403 intdata = true; 404 break; 405 case 'o': 406 case 'u': 407 case 'x': 408 case 'X': 409 intdata = nosign = true; 410 break; 411 case 'D': 412 if (longdata) 413 goto fmt_broken; 414 longdata = intdata = true; /* same as %ld */ 415 break; 416 case 'O': 417 case 'U': 418 if (longdata) 419 goto fmt_broken; 420 longdata = intdata = nosign = true; /* same as %l[ou] */ 421 break; 422 case 'c': 423 if (longdata) 424 goto fmt_broken; 425 chardata = true; 426 break; 427 case 'e': 428 case 'E': 429 case 'f': 430 case 'F': 431 case 'g': 432 case 'G': 433 if (longdata) 434 goto fmt_broken; 435 /* No cast needed for printing in putdata() */ 436 break; 437 default: 438 fmt_broken: 439 errx(1, "illegal or unsupported format '%.*s'", 440 (int)(p + 1 - fmt), fmt); 441 } 442 443 while ((p = strchr(p, '%')) != NULL && p[1] == '%') 444 p += 2; 445 446 if (p != NULL) { 447 if (p[1] != '\0') 448 errx(1, "too many conversions"); 449 /* cannot end in single '%' */ 450 if (asprintf(&format, "%s%%", format) == -1) 451 err(1, NULL); 452 } 453 } 454 } 455