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 * 4. 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 30 #ifndef lint 31 static const char copyright[] = 32 "@(#) Copyright (c) 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34 #endif /* not lint */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93"; 39 #endif 40 #endif 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 43 44 /* 45 * jot - print sequential or random data 46 * 47 * Author: John Kunze, Office of Comp. Affairs, UCB 48 */ 49 50 #include <ctype.h> 51 #include <err.h> 52 #include <limits.h> 53 #include <stdio.h> 54 #include <stdint.h> 55 #include <stdlib.h> 56 #include <stdbool.h> 57 #include <string.h> 58 #include <time.h> 59 #include <unistd.h> 60 61 /* Defaults */ 62 #define REPS_DEF 100 63 #define BEGIN_DEF 1 64 #define ENDER_DEF 100 65 #define STEP_DEF 1 66 67 /* Flags of options that have been set */ 68 #define HAVE_STEP 1 69 #define HAVE_ENDER 2 70 #define HAVE_BEGIN 4 71 #define HAVE_REPS 8 72 73 #define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0) 74 75 static bool boring; 76 static int prec = -1; 77 static bool longdata; 78 static bool intdata; 79 static bool chardata; 80 static bool nosign; 81 static const char *sepstring = "\n"; 82 static char format[BUFSIZ]; 83 84 static void getformat(void); 85 static int getprec(const char *); 86 static int putdata(double, bool); 87 static void usage(void); 88 89 int 90 main(int argc, char **argv) 91 { 92 bool have_format = false; 93 bool infinity = false; 94 bool nofinalnl = false; 95 bool randomize = false; 96 bool use_random = false; 97 int ch; 98 int mask = 0; 99 int n = 0; 100 double begin = BEGIN_DEF; 101 double divisor; 102 double ender = ENDER_DEF; 103 double s = STEP_DEF; 104 double x, y; 105 long i; 106 long reps = REPS_DEF; 107 108 while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1) 109 switch (ch) { 110 case 'b': 111 boring = true; 112 /* FALLTHROUGH */ 113 case 'w': 114 if (strlcpy(format, optarg, sizeof(format)) >= 115 sizeof(format)) 116 errx(1, "-%c word too long", ch); 117 have_format = true; 118 break; 119 case 'c': 120 chardata = true; 121 break; 122 case 'n': 123 nofinalnl = true; 124 break; 125 case 'p': 126 prec = atoi(optarg); 127 if (prec < 0) 128 errx(1, "bad precision value"); 129 have_format = true; 130 break; 131 case 'r': 132 randomize = true; 133 break; 134 case 's': 135 sepstring = optarg; 136 break; 137 default: 138 usage(); 139 } 140 argc -= optind; 141 argv += optind; 142 143 switch (argc) { /* examine args right to left, falling thru cases */ 144 case 4: 145 if (!is_default(argv[3])) { 146 if (!sscanf(argv[3], "%lf", &s)) 147 errx(1, "bad s value: %s", argv[3]); 148 mask |= HAVE_STEP; 149 if (randomize) 150 use_random = true; 151 } 152 /* FALLTHROUGH */ 153 case 3: 154 if (!is_default(argv[2])) { 155 if (!sscanf(argv[2], "%lf", &ender)) 156 ender = argv[2][strlen(argv[2])-1]; 157 mask |= HAVE_ENDER; 158 if (prec < 0) 159 n = getprec(argv[2]); 160 } 161 /* FALLTHROUGH */ 162 case 2: 163 if (!is_default(argv[1])) { 164 if (!sscanf(argv[1], "%lf", &begin)) 165 begin = argv[1][strlen(argv[1])-1]; 166 mask |= HAVE_BEGIN; 167 if (prec < 0) 168 prec = getprec(argv[1]); 169 if (n > prec) /* maximum precision */ 170 prec = n; 171 } 172 /* FALLTHROUGH */ 173 case 1: 174 if (!is_default(argv[0])) { 175 if (!sscanf(argv[0], "%ld", &reps)) 176 errx(1, "bad reps value: %s", argv[0]); 177 mask |= HAVE_REPS; 178 } 179 break; 180 case 0: 181 usage(); 182 default: 183 errx(1, "too many arguments. What do you mean by %s?", 184 argv[4]); 185 } 186 getformat(); 187 188 if (prec == -1) 189 prec = 0; 190 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 if (begin <= ender) 288 ender += 1; 289 else 290 begin += 1; 291 nosign = true; 292 intdata = true; 293 (void)strlcpy(format, 294 chardata ? "%c" : "%u", sizeof(format)); 295 } 296 x = ender - begin; 297 for (i = 1; i <= reps || infinity; i++) { 298 if (use_random) 299 y = random() / divisor; 300 else 301 y = arc4random() / divisor; 302 if (putdata(y * x + begin, !(reps - i))) 303 errx(1, "range error in conversion"); 304 } 305 } else 306 for (i = 1, x = begin; i <= reps || infinity; i++, x += s) 307 if (putdata(x, !(reps - i))) 308 errx(1, "range error in conversion"); 309 if (!nofinalnl) 310 putchar('\n'); 311 exit(0); 312 } 313 314 /* 315 * Send x to stdout using the specified format. 316 * Last is true if this is the set's last value. 317 * Return 0 if OK, or a positive number if the number passed was 318 * outside the range specified by the various flags. 319 */ 320 static int 321 putdata(double x, bool last) 322 { 323 324 if (boring) 325 printf("%s", format); 326 else if (longdata && nosign) { 327 if (x <= (double)ULONG_MAX && x >= (double)0) 328 printf(format, (unsigned long)x); 329 else 330 return (1); 331 } else if (longdata) { 332 if (x <= (double)LONG_MAX && x >= (double)LONG_MIN) 333 printf(format, (long)x); 334 else 335 return (1); 336 } else if (chardata || (intdata && !nosign)) { 337 if (x <= (double)INT_MAX && x >= (double)INT_MIN) 338 printf(format, (int)x); 339 else 340 return (1); 341 } else if (intdata) { 342 if (x <= (double)UINT_MAX && x >= (double)0) 343 printf(format, (unsigned int)x); 344 else 345 return (1); 346 347 } else 348 printf(format, x); 349 if (!last) 350 fputs(sepstring, stdout); 351 352 return (0); 353 } 354 355 static void 356 usage(void) 357 { 358 fprintf(stderr, "%s\n%s\n", 359 "usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]", 360 " [reps [begin [end [s]]]]"); 361 exit(1); 362 } 363 364 /* 365 * Return the number of digits following the number's decimal point. 366 * Return 0 if no decimal point is found. 367 */ 368 static int 369 getprec(const char *str) 370 { 371 const char *p; 372 const char *q; 373 374 for (p = str; *p; p++) 375 if (*p == '.') 376 break; 377 if (!*p) 378 return (0); 379 for (q = ++p; *p; p++) 380 if (!isdigit((unsigned char)*p)) 381 break; 382 return (p - q); 383 } 384 385 /* 386 * Set format, intdata, chardata, longdata, and nosign 387 * based on the command line arguments. 388 */ 389 static void 390 getformat(void) 391 { 392 char *p, *p2; 393 int dot, hash, space, sign, numbers = 0; 394 size_t sz; 395 396 if (boring) /* no need to bother */ 397 return; 398 for (p = format; *p; p++) /* look for '%' */ 399 if (*p == '%') { 400 if (p[1] == '%') 401 p++; /* leave %% alone */ 402 else 403 break; 404 } 405 sz = sizeof(format) - strlen(format) - 1; 406 if (!*p && !chardata) { 407 if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) 408 errx(1, "-w word too long"); 409 } else if (!*p && chardata) { 410 if (strlcpy(p, "%c", sz) >= sz) 411 errx(1, "-w word too long"); 412 intdata = true; 413 } else if (!*(p+1)) { 414 if (sz <= 0) 415 errx(1, "-w word too long"); 416 strcat(format, "%"); /* cannot end in single '%' */ 417 } else { 418 /* 419 * Allow conversion format specifiers of the form 420 * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of 421 * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u} 422 */ 423 p2 = p++; 424 dot = hash = space = sign = numbers = 0; 425 while (!isalpha((unsigned char)*p)) { 426 if (isdigit((unsigned char)*p)) { 427 numbers++; 428 p++; 429 } else if ((*p == '#' && !(numbers|dot|sign|space| 430 hash++)) || 431 (*p == ' ' && !(numbers|dot|space++)) || 432 ((*p == '+' || *p == '-') && !(numbers|dot|sign++)) 433 || (*p == '.' && !(dot++))) 434 p++; 435 else 436 goto fmt_broken; 437 } 438 if (*p == 'l') { 439 longdata = true; 440 if (*++p == 'l') { 441 if (p[1] != '\0') 442 p++; 443 goto fmt_broken; 444 } 445 } 446 switch (*p) { 447 case 'o': case 'u': case 'x': case 'X': 448 intdata = nosign = true; 449 break; 450 case 'd': case 'i': 451 intdata = true; 452 break; 453 case 'D': 454 if (!longdata) { 455 intdata = true; 456 break; 457 } 458 case 'O': case 'U': 459 if (!longdata) { 460 intdata = nosign = true; 461 break; 462 } 463 case 'c': 464 if (!(intdata | longdata)) { 465 chardata = true; 466 break; 467 } 468 case 'h': case 'n': case 'p': case 'q': case 's': case 'L': 469 case '$': case '*': 470 goto fmt_broken; 471 case 'f': case 'e': case 'g': case 'E': case 'G': 472 if (!longdata) 473 break; 474 /* FALLTHROUGH */ 475 default: 476 fmt_broken: 477 *++p = '\0'; 478 errx(1, "illegal or unsupported format '%s'", p2); 479 /* NOTREACHED */ 480 } 481 while (*++p) 482 if (*p == '%' && *(p+1) && *(p+1) != '%') 483 errx(1, "too many conversions"); 484 else if (*p == '%' && *(p+1) == '%') 485 p++; 486 else if (*p == '%' && !*(p+1)) { 487 if (strlcat(format, "%", sizeof(format)) >= 488 sizeof(format)) 489 errx(1, "-w word too long"); 490 break; 491 } 492 } 493 } 494