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 = -1; 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 = BEGIN_DEF; 105 double divisor; 106 double ender = ENDER_DEF; 107 double s = STEP_DEF; 108 double x, y; 109 long i; 110 long reps = REPS_DEF; 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 < 0) 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 < 0) 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 192 if (prec == -1) 193 prec = 0; 194 195 while (mask) /* 4 bit mask has 1's where last 4 args were given */ 196 switch (mask) { /* fill in the 0's by default or computation */ 197 case HAVE_STEP: 198 case HAVE_ENDER: 199 case HAVE_ENDER | HAVE_STEP: 200 case HAVE_BEGIN: 201 case HAVE_BEGIN | HAVE_STEP: 202 reps = REPS_DEF; 203 mask |= HAVE_REPS; 204 break; 205 case HAVE_BEGIN | HAVE_ENDER: 206 s = ender > begin ? 1 : -1; 207 mask |= HAVE_STEP; 208 break; 209 case HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: 210 if (randomize) 211 reps = REPS_DEF; 212 else if (s == 0.0) 213 reps = 0; 214 else 215 reps = (ender - begin + s) / s; 216 if (reps <= 0) 217 errx(1, "impossible stepsize"); 218 mask = 0; 219 break; 220 case HAVE_REPS: 221 case HAVE_REPS | HAVE_STEP: 222 begin = BEGIN_DEF; 223 mask |= HAVE_BEGIN; 224 break; 225 case HAVE_REPS | HAVE_ENDER: 226 s = STEP_DEF; 227 mask = HAVE_REPS | HAVE_ENDER | HAVE_STEP; 228 break; 229 case HAVE_REPS | HAVE_ENDER | HAVE_STEP: 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 HAVE_REPS | HAVE_BEGIN: 238 s = STEP_DEF; 239 mask = HAVE_REPS | HAVE_BEGIN | HAVE_STEP; 240 break; 241 case HAVE_REPS | HAVE_BEGIN | HAVE_STEP: 242 if (randomize) 243 ender = ENDER_DEF; 244 else 245 ender = begin + reps * s - s; 246 mask = 0; 247 break; 248 case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER: 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 HAVE_REPS | HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: 258 /* if reps given and implied, */ 259 if (!randomize && s != 0.0) { 260 long t = (ender - begin + s) / s; 261 if (t <= 0) 262 errx(1, "impossible stepsize"); 263 if (t < reps) /* take lesser */ 264 reps = t; 265 } 266 mask = 0; 267 break; 268 default: 269 errx(1, "bad mask"); 270 } 271 if (reps == 0) 272 infinity = true; 273 if (randomize) { 274 if (use_random) { 275 srandom((unsigned long)s); 276 divisor = (double)INT32_MAX + 1; 277 } else 278 divisor = (double)UINT32_MAX + 1; 279 280 /* 281 * Attempt to DWIM when the user has specified an 282 * integer range within that of the random number 283 * generator: distribute the numbers equally in 284 * the range [begin .. ender]. Jot's default %.0f 285 * format would make the appearance of the first and 286 * last specified value half as likely as the rest. 287 */ 288 if (!have_format && prec == 0 && 289 begin >= 0 && begin < divisor && 290 ender >= 0 && ender < divisor) { 291 if (begin <= ender) 292 ender += 1; 293 else 294 begin += 1; 295 nosign = true; 296 intdata = true; 297 (void)strlcpy(format, 298 chardata ? "%c" : "%u", sizeof(format)); 299 } 300 x = ender - begin; 301 for (i = 1; i <= reps || infinity; i++) { 302 if (use_random) 303 y = random() / divisor; 304 else 305 y = arc4random() / divisor; 306 if (putdata(y * x + begin, !(reps - i))) 307 errx(1, "range error in conversion"); 308 } 309 } else 310 for (i = 1, x = begin; i <= reps || infinity; i++, x += s) 311 if (putdata(x, !(reps - i))) 312 errx(1, "range error in conversion"); 313 if (!nofinalnl) 314 putchar('\n'); 315 exit(0); 316 } 317 318 /* 319 * Send x to stdout using the specified format. 320 * Last is true if this is the set's last value. 321 * Return 0 if OK, or a positive number if the number passed was 322 * outside the range specified by the various flags. 323 */ 324 static int 325 putdata(double x, bool last) 326 { 327 328 if (boring) 329 printf("%s", format); 330 else if (longdata && nosign) { 331 if (x <= (double)ULONG_MAX && x >= (double)0) 332 printf(format, (unsigned long)x); 333 else 334 return (1); 335 } else if (longdata) { 336 if (x <= (double)LONG_MAX && x >= (double)LONG_MIN) 337 printf(format, (long)x); 338 else 339 return (1); 340 } else if (chardata || (intdata && !nosign)) { 341 if (x <= (double)INT_MAX && x >= (double)INT_MIN) 342 printf(format, (int)x); 343 else 344 return (1); 345 } else if (intdata) { 346 if (x <= (double)UINT_MAX && x >= (double)0) 347 printf(format, (unsigned int)x); 348 else 349 return (1); 350 351 } else 352 printf(format, x); 353 if (!last) 354 fputs(sepstring, stdout); 355 356 return (0); 357 } 358 359 static void 360 usage(void) 361 { 362 fprintf(stderr, "%s\n%s\n", 363 "usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]", 364 " [reps [begin [end [s]]]]"); 365 exit(1); 366 } 367 368 /* 369 * Return the number of digits following the number's decimal point. 370 * Return 0 if no decimal point is found. 371 */ 372 static int 373 getprec(const char *str) 374 { 375 const char *p; 376 const char *q; 377 378 for (p = str; *p; p++) 379 if (*p == '.') 380 break; 381 if (!*p) 382 return (0); 383 for (q = ++p; *p; p++) 384 if (!isdigit((unsigned char)*p)) 385 break; 386 return (p - q); 387 } 388 389 /* 390 * Set format, intdata, chardata, longdata, and nosign 391 * based on the command line arguments. 392 */ 393 static void 394 getformat(void) 395 { 396 char *p, *p2; 397 int dot, hash, space, sign, numbers = 0; 398 size_t sz; 399 400 if (boring) /* no need to bother */ 401 return; 402 for (p = format; *p; p++) /* look for '%' */ 403 if (*p == '%') { 404 if (p[1] == '%') 405 p++; /* leave %% alone */ 406 else 407 break; 408 } 409 sz = sizeof(format) - strlen(format) - 1; 410 if (!*p && !chardata) { 411 if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) 412 errx(1, "-w word too long"); 413 } else if (!*p && chardata) { 414 if (strlcpy(p, "%c", sz) >= sz) 415 errx(1, "-w word too long"); 416 intdata = true; 417 } else if (!*(p+1)) { 418 if (sz <= 0) 419 errx(1, "-w word too long"); 420 strcat(format, "%"); /* cannot end in single '%' */ 421 } else { 422 /* 423 * Allow conversion format specifiers of the form 424 * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of 425 * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u} 426 */ 427 p2 = p++; 428 dot = hash = space = sign = numbers = 0; 429 while (!isalpha((unsigned char)*p)) { 430 if (isdigit((unsigned char)*p)) { 431 numbers++; 432 p++; 433 } else if ((*p == '#' && !(numbers|dot|sign|space| 434 hash++)) || 435 (*p == ' ' && !(numbers|dot|space++)) || 436 ((*p == '+' || *p == '-') && !(numbers|dot|sign++)) 437 || (*p == '.' && !(dot++))) 438 p++; 439 else 440 goto fmt_broken; 441 } 442 if (*p == 'l') { 443 longdata = true; 444 if (*++p == 'l') { 445 if (p[1] != '\0') 446 p++; 447 goto fmt_broken; 448 } 449 } 450 switch (*p) { 451 case 'o': case 'u': case 'x': case 'X': 452 intdata = nosign = true; 453 break; 454 case 'd': case 'i': 455 intdata = true; 456 break; 457 case 'D': 458 if (!longdata) { 459 intdata = true; 460 break; 461 } 462 case 'O': case 'U': 463 if (!longdata) { 464 intdata = nosign = true; 465 break; 466 } 467 case 'c': 468 if (!(intdata | longdata)) { 469 chardata = true; 470 break; 471 } 472 case 'h': case 'n': case 'p': case 'q': case 's': case 'L': 473 case '$': case '*': 474 goto fmt_broken; 475 case 'f': case 'e': case 'g': case 'E': case 'G': 476 if (!longdata) 477 break; 478 /* FALLTHROUGH */ 479 default: 480 fmt_broken: 481 *++p = '\0'; 482 errx(1, "illegal or unsupported format '%s'", p2); 483 /* NOTREACHED */ 484 } 485 while (*++p) 486 if (*p == '%' && *(p+1) && *(p+1) != '%') 487 errx(1, "too many conversions"); 488 else if (*p == '%' && *(p+1) == '%') 489 p++; 490 else if (*p == '%' && !*(p+1)) { 491 if (strlcat(format, "%", sizeof(format)) >= 492 sizeof(format)) 493 errx(1, "-w word too long"); 494 break; 495 } 496 } 497 } 498