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