11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3127423Scharnierstatic const char copyright[] = 321590Srgrimes"@(#) Copyright (c) 1993\n\ 331590Srgrimes The Regents of the University of California. All rights reserved.\n"; 341590Srgrimes#endif /* not lint */ 351590Srgrimes 361590Srgrimes#ifndef lint 3727423Scharnier#if 0 381590Srgrimesstatic char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93"; 3927423Scharnier#endif 4098254Sjmallett#endif 4198254Sjmallett#include <sys/cdefs.h> 4298254Sjmallett__FBSDID("$FreeBSD$"); 4398254Sjmallett 441590Srgrimes/* 451590Srgrimes * jot - print sequential or random data 461590Srgrimes * 471590Srgrimes * Author: John Kunze, Office of Comp. Affairs, UCB 481590Srgrimes */ 491590Srgrimes 501590Srgrimes#include <ctype.h> 5127423Scharnier#include <err.h> 521590Srgrimes#include <limits.h> 531590Srgrimes#include <stdio.h> 5499457Smike#include <stdint.h> 551590Srgrimes#include <stdlib.h> 56164026Sdds#include <stdbool.h> 571590Srgrimes#include <string.h> 58200462Sdelphij#include <time.h> 5923511Sache#include <unistd.h> 601590Srgrimes 61164046Sdds/* Defaults */ 621590Srgrimes#define REPS_DEF 100 631590Srgrimes#define BEGIN_DEF 1 641590Srgrimes#define ENDER_DEF 100 651590Srgrimes#define STEP_DEF 1 661590Srgrimes 67164046Sdds/* Flags of options that have been set */ 68164021Sdds#define HAVE_STEP 1 69164021Sdds#define HAVE_ENDER 2 70164021Sdds#define HAVE_BEGIN 4 71164021Sdds#define HAVE_REPS 8 72164021Sdds 73164028Sdds#define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0) 741590Srgrimes 75164046Sddsstatic bool boring; 76208728Sbrianstatic int prec = -1; 77164046Sddsstatic bool longdata; 78164046Sddsstatic bool intdata; 79164046Sddsstatic bool chardata; 80164046Sddsstatic bool nosign; 81164046Sddsstatic const char *sepstring = "\n"; 82164046Sddsstatic char format[BUFSIZ]; 831590Srgrimes 84164046Sddsstatic void getformat(void); 85164046Sddsstatic int getprec(const char *); 86164046Sddsstatic int putdata(double, bool); 8792920Simpstatic void usage(void); 881590Srgrimes 891590Srgrimesint 9098254Sjmallettmain(int argc, char **argv) 911590Srgrimes{ 92164046Sdds bool have_format = false; 93164046Sdds bool infinity = false; 94164046Sdds bool nofinalnl = false; 95164046Sdds bool randomize = false; 96164046Sdds bool use_random = false; 97164046Sdds int ch; 98164046Sdds int mask = 0; 99164046Sdds int n = 0; 100195443Sbrian double begin = BEGIN_DEF; 101164046Sdds double divisor; 102195443Sbrian double ender = ENDER_DEF; 103195443Sbrian double s = STEP_DEF; 104164025Sdds double x, y; 105164025Sdds long i; 106195443Sbrian long reps = REPS_DEF; 1071590Srgrimes 108164046Sdds while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1) 10998254Sjmallett switch (ch) { 1101590Srgrimes case 'b': 111164046Sdds boring = true; 11277276Sdd /* FALLTHROUGH */ 1131590Srgrimes case 'w': 11477276Sdd if (strlcpy(format, optarg, sizeof(format)) >= 11577276Sdd sizeof(format)) 11677276Sdd errx(1, "-%c word too long", ch); 117164035Sdds have_format = true; 1181590Srgrimes break; 119164046Sdds case 'c': 120164046Sdds chardata = true; 1211590Srgrimes break; 122164046Sdds case 'n': 123164046Sdds nofinalnl = true; 124164046Sdds break; 1251590Srgrimes case 'p': 12677276Sdd prec = atoi(optarg); 127208728Sbrian if (prec < 0) 12827423Scharnier errx(1, "bad precision value"); 129164035Sdds have_format = true; 1301590Srgrimes break; 131164046Sdds case 'r': 132164046Sdds randomize = true; 133164046Sdds break; 134164046Sdds case 's': 135164046Sdds sepstring = optarg; 136164046Sdds break; 1371590Srgrimes default: 13827423Scharnier usage(); 1391590Srgrimes } 14077276Sdd argc -= optind; 14177276Sdd argv += optind; 1421590Srgrimes 14377276Sdd switch (argc) { /* examine args right to left, falling thru cases */ 1441590Srgrimes case 4: 14577276Sdd if (!is_default(argv[3])) { 14677276Sdd if (!sscanf(argv[3], "%lf", &s)) 14777276Sdd errx(1, "bad s value: %s", argv[3]); 148164021Sdds mask |= HAVE_STEP; 149164026Sdds if (randomize) 150164035Sdds use_random = true; 1511590Srgrimes } 152164021Sdds /* FALLTHROUGH */ 1531590Srgrimes case 3: 15477276Sdd if (!is_default(argv[2])) { 15577276Sdd if (!sscanf(argv[2], "%lf", &ender)) 15677276Sdd ender = argv[2][strlen(argv[2])-1]; 157164021Sdds mask |= HAVE_ENDER; 158208728Sbrian if (prec < 0) 15977276Sdd n = getprec(argv[2]); 1601590Srgrimes } 161164021Sdds /* FALLTHROUGH */ 1621590Srgrimes case 2: 16377276Sdd if (!is_default(argv[1])) { 16477276Sdd if (!sscanf(argv[1], "%lf", &begin)) 16577276Sdd begin = argv[1][strlen(argv[1])-1]; 166164021Sdds mask |= HAVE_BEGIN; 167208728Sbrian if (prec < 0) 16877276Sdd prec = getprec(argv[1]); 1691590Srgrimes if (n > prec) /* maximum precision */ 1701590Srgrimes prec = n; 1711590Srgrimes } 172164021Sdds /* FALLTHROUGH */ 1731590Srgrimes case 1: 17477276Sdd if (!is_default(argv[0])) { 17577276Sdd if (!sscanf(argv[0], "%ld", &reps)) 17677276Sdd errx(1, "bad reps value: %s", argv[0]); 177164021Sdds mask |= HAVE_REPS; 1781590Srgrimes } 1791590Srgrimes break; 1801590Srgrimes case 0: 18127423Scharnier usage(); 1821590Srgrimes default: 18377276Sdd errx(1, "too many arguments. What do you mean by %s?", 18477276Sdd argv[4]); 1851590Srgrimes } 1861590Srgrimes getformat(); 187208728Sbrian 188208728Sbrian if (prec == -1) 189208728Sbrian prec = 0; 190208728Sbrian 1911590Srgrimes while (mask) /* 4 bit mask has 1's where last 4 args were given */ 1921590Srgrimes switch (mask) { /* fill in the 0's by default or computation */ 193164021Sdds case HAVE_STEP: 194164021Sdds case HAVE_ENDER: 195164021Sdds case HAVE_ENDER | HAVE_STEP: 196164021Sdds case HAVE_BEGIN: 197164021Sdds case HAVE_BEGIN | HAVE_STEP: 1981590Srgrimes reps = REPS_DEF; 199164023Sdds mask |= HAVE_REPS; 2001590Srgrimes break; 201164043Sdds case HAVE_BEGIN | HAVE_ENDER: 202164043Sdds s = ender > begin ? 1 : -1; 203164043Sdds mask |= HAVE_STEP; 204164043Sdds break; 205164021Sdds case HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: 206164023Sdds if (randomize) 2071590Srgrimes reps = REPS_DEF; 208164023Sdds else if (s == 0.0) 2091590Srgrimes reps = 0; 210164023Sdds else 211164023Sdds reps = (ender - begin + s) / s; 2121590Srgrimes if (reps <= 0) 21327423Scharnier errx(1, "impossible stepsize"); 2141590Srgrimes mask = 0; 2151590Srgrimes break; 216164021Sdds case HAVE_REPS: 217164021Sdds case HAVE_REPS | HAVE_STEP: 2181590Srgrimes begin = BEGIN_DEF; 219164023Sdds mask |= HAVE_BEGIN; 2201590Srgrimes break; 221164021Sdds case HAVE_REPS | HAVE_ENDER: 222164026Sdds s = STEP_DEF; 223164021Sdds mask = HAVE_REPS | HAVE_ENDER | HAVE_STEP; 2241590Srgrimes break; 225164021Sdds case HAVE_REPS | HAVE_ENDER | HAVE_STEP: 2261590Srgrimes if (randomize) 2271590Srgrimes begin = BEGIN_DEF; 2281590Srgrimes else if (reps == 0) 22927423Scharnier errx(1, "must specify begin if reps == 0"); 23077276Sdd begin = ender - reps * s + s; 2311590Srgrimes mask = 0; 2321590Srgrimes break; 233164021Sdds case HAVE_REPS | HAVE_BEGIN: 234164026Sdds s = STEP_DEF; 235164021Sdds mask = HAVE_REPS | HAVE_BEGIN | HAVE_STEP; 2361590Srgrimes break; 237164021Sdds case HAVE_REPS | HAVE_BEGIN | HAVE_STEP: 2381590Srgrimes if (randomize) 2391590Srgrimes ender = ENDER_DEF; 2401590Srgrimes else 2411590Srgrimes ender = begin + reps * s - s; 2421590Srgrimes mask = 0; 2431590Srgrimes break; 244164021Sdds case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER: 245164026Sdds if (reps == 0) 24627423Scharnier errx(1, "infinite sequences cannot be bounded"); 2471590Srgrimes else if (reps == 1) 2481590Srgrimes s = 0.0; 2491590Srgrimes else 2501590Srgrimes s = (ender - begin) / (reps - 1); 2511590Srgrimes mask = 0; 2521590Srgrimes break; 253164021Sdds case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: 254164021Sdds /* if reps given and implied, */ 2551590Srgrimes if (!randomize && s != 0.0) { 2561590Srgrimes long t = (ender - begin + s) / s; 2571590Srgrimes if (t <= 0) 25827423Scharnier errx(1, "impossible stepsize"); 2591590Srgrimes if (t < reps) /* take lesser */ 2601590Srgrimes reps = t; 2611590Srgrimes } 2621590Srgrimes mask = 0; 2631590Srgrimes break; 2641590Srgrimes default: 26527423Scharnier errx(1, "bad mask"); 2661590Srgrimes } 2671590Srgrimes if (reps == 0) 268164046Sdds infinity = true; 26977276Sdd if (randomize) { 270164035Sdds if (use_random) { 271164035Sdds srandom((unsigned long)s); 272164035Sdds divisor = (double)INT32_MAX + 1; 273164035Sdds } else 274164035Sdds divisor = (double)UINT32_MAX + 1; 275164035Sdds 276164035Sdds /* 277164035Sdds * Attempt to DWIM when the user has specified an 278164035Sdds * integer range within that of the random number 279164035Sdds * generator: distribute the numbers equally in 280164035Sdds * the range [begin .. ender]. Jot's default %.0f 281164035Sdds * format would make the appearance of the first and 282164035Sdds * last specified value half as likely as the rest. 283164035Sdds */ 284164035Sdds if (!have_format && prec == 0 && 285164035Sdds begin >= 0 && begin < divisor && 286164035Sdds ender >= 0 && ender < divisor) { 287208728Sbrian if (begin <= ender) 288208728Sbrian ender += 1; 289208728Sbrian else 290208728Sbrian begin += 1; 291164046Sdds nosign = true; 292164046Sdds intdata = true; 293164035Sdds (void)strlcpy(format, 294164035Sdds chardata ? "%c" : "%u", sizeof(format)); 295164035Sdds } 296208728Sbrian x = ender - begin; 297164025Sdds for (i = 1; i <= reps || infinity; i++) { 298164035Sdds if (use_random) 299164035Sdds y = random() / divisor; 300164026Sdds else 301164035Sdds y = arc4random() / divisor; 302164046Sdds if (putdata(y * x + begin, !(reps - i))) 30377276Sdd errx(1, "range error in conversion"); 30477276Sdd } 30577276Sdd } else 306164025Sdds for (i = 1, x = begin; i <= reps || infinity; i++, x += s) 307164046Sdds if (putdata(x, !(reps - i))) 30877276Sdd errx(1, "range error in conversion"); 30977276Sdd if (!nofinalnl) 31077276Sdd putchar('\n'); 31177276Sdd exit(0); 3121590Srgrimes} 3131590Srgrimes 314164046Sdds/* 315164046Sdds * Send x to stdout using the specified format. 316164046Sdds * Last is true if this is the set's last value. 317164046Sdds * Return 0 if OK, or a positive number if the number passed was 318164046Sdds * outside the range specified by the various flags. 319164046Sdds */ 320164046Sddsstatic int 321164046Sddsputdata(double x, bool last) 3221590Srgrimes{ 3231590Srgrimes 32455515Ssheldonh if (boring) 32562871Skris printf("%s", format); 32655515Ssheldonh else if (longdata && nosign) { 32755515Ssheldonh if (x <= (double)ULONG_MAX && x >= (double)0) 32855515Ssheldonh printf(format, (unsigned long)x); 32955515Ssheldonh else 33055515Ssheldonh return (1); 33155515Ssheldonh } else if (longdata) { 33255515Ssheldonh if (x <= (double)LONG_MAX && x >= (double)LONG_MIN) 33355515Ssheldonh printf(format, (long)x); 33455515Ssheldonh else 33555515Ssheldonh return (1); 33655515Ssheldonh } else if (chardata || (intdata && !nosign)) { 33755515Ssheldonh if (x <= (double)INT_MAX && x >= (double)INT_MIN) 33855515Ssheldonh printf(format, (int)x); 33955515Ssheldonh else 34055515Ssheldonh return (1); 34155515Ssheldonh } else if (intdata) { 34255515Ssheldonh if (x <= (double)UINT_MAX && x >= (double)0) 34355515Ssheldonh printf(format, (unsigned int)x); 34455515Ssheldonh else 34555515Ssheldonh return (1); 34655515Ssheldonh 34755515Ssheldonh } else 3481590Srgrimes printf(format, x); 349164046Sdds if (!last) 3501590Srgrimes fputs(sepstring, stdout); 35155515Ssheldonh 35255515Ssheldonh return (0); 3531590Srgrimes} 3541590Srgrimes 35527423Scharnierstatic void 35698254Sjmallettusage(void) 3571590Srgrimes{ 35827423Scharnier fprintf(stderr, "%s\n%s\n", 35930908Scharnier "usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]", 36030908Scharnier " [reps [begin [end [s]]]]"); 3611590Srgrimes exit(1); 3621590Srgrimes} 3631590Srgrimes 364164046Sdds/* 365164046Sdds * Return the number of digits following the number's decimal point. 366164046Sdds * Return 0 if no decimal point is found. 367164046Sdds */ 368164046Sddsstatic int 369164046Sddsgetprec(const char *str) 3701590Srgrimes{ 371164046Sdds const char *p; 372164046Sdds const char *q; 3731590Srgrimes 37477287Sdd for (p = str; *p; p++) 3751590Srgrimes if (*p == '.') 3761590Srgrimes break; 3771590Srgrimes if (!*p) 3781590Srgrimes return (0); 3791590Srgrimes for (q = ++p; *p; p++) 380132240Stjr if (!isdigit((unsigned char)*p)) 3811590Srgrimes break; 3821590Srgrimes return (p - q); 3831590Srgrimes} 3841590Srgrimes 385164046Sdds/* 386164046Sdds * Set format, intdata, chardata, longdata, and nosign 387164046Sdds * based on the command line arguments. 388164046Sdds */ 389164046Sddsstatic void 39098254Sjmallettgetformat(void) 3911590Srgrimes{ 39277287Sdd char *p, *p2; 39355515Ssheldonh int dot, hash, space, sign, numbers = 0; 39477276Sdd size_t sz; 3951590Srgrimes 3961590Srgrimes if (boring) /* no need to bother */ 3971590Srgrimes return; 3981590Srgrimes for (p = format; *p; p++) /* look for '%' */ 399165029Sdelphij if (*p == '%') { 400164851Sdds if (p[1] == '%') 401164851Sdds p++; /* leave %% alone */ 402164851Sdds else 403164851Sdds break; 404165029Sdelphij } 40577276Sdd sz = sizeof(format) - strlen(format) - 1; 40677276Sdd if (!*p && !chardata) { 40777276Sdd if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) 40877276Sdd errx(1, "-w word too long"); 40977276Sdd } else if (!*p && chardata) { 41077276Sdd if (strlcpy(p, "%c", sz) >= sz) 41177276Sdd errx(1, "-w word too long"); 412164046Sdds intdata = true; 41377276Sdd } else if (!*(p+1)) { 41477276Sdd if (sz <= 0) 41577276Sdd errx(1, "-w word too long"); 4161590Srgrimes strcat(format, "%"); /* cannot end in single '%' */ 41777276Sdd } else { 41848995Ssheldonh /* 41948995Ssheldonh * Allow conversion format specifiers of the form 42048995Ssheldonh * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of 42148995Ssheldonh * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u} 42248995Ssheldonh */ 42377287Sdd p2 = p++; 42448995Ssheldonh dot = hash = space = sign = numbers = 0; 425132240Stjr while (!isalpha((unsigned char)*p)) { 426132240Stjr if (isdigit((unsigned char)*p)) { 42748995Ssheldonh numbers++; 42848995Ssheldonh p++; 42948995Ssheldonh } else if ((*p == '#' && !(numbers|dot|sign|space| 43048995Ssheldonh hash++)) || 43148995Ssheldonh (*p == ' ' && !(numbers|dot|space++)) || 43248995Ssheldonh ((*p == '+' || *p == '-') && !(numbers|dot|sign++)) 43348995Ssheldonh || (*p == '.' && !(dot++))) 43448995Ssheldonh p++; 43548995Ssheldonh else 43655515Ssheldonh goto fmt_broken; 43748995Ssheldonh } 43855515Ssheldonh if (*p == 'l') { 439164046Sdds longdata = true; 44055515Ssheldonh if (*++p == 'l') { 44155515Ssheldonh if (p[1] != '\0') 44255515Ssheldonh p++; 44355515Ssheldonh goto fmt_broken; 44455515Ssheldonh } 44555515Ssheldonh } 44648995Ssheldonh switch (*p) { 44748995Ssheldonh case 'o': case 'u': case 'x': case 'X': 448164046Sdds intdata = nosign = true; 4491590Srgrimes break; 45048995Ssheldonh case 'd': case 'i': 451164046Sdds intdata = true; 45248995Ssheldonh break; 45348995Ssheldonh case 'D': 45455515Ssheldonh if (!longdata) { 455164046Sdds intdata = true; 45648995Ssheldonh break; 45748995Ssheldonh } 45848995Ssheldonh case 'O': case 'U': 45955515Ssheldonh if (!longdata) { 460164046Sdds intdata = nosign = true; 46148995Ssheldonh break; 46248995Ssheldonh } 46348995Ssheldonh case 'c': 46455515Ssheldonh if (!(intdata | longdata)) { 465164046Sdds chardata = true; 46648995Ssheldonh break; 46748995Ssheldonh } 46855515Ssheldonh case 'h': case 'n': case 'p': case 'q': case 's': case 'L': 46948995Ssheldonh case '$': case '*': 47055515Ssheldonh goto fmt_broken; 47148995Ssheldonh case 'f': case 'e': case 'g': case 'E': case 'G': 47255515Ssheldonh if (!longdata) 47348995Ssheldonh break; 47448995Ssheldonh /* FALLTHROUGH */ 4751590Srgrimes default: 47655515Ssheldonhfmt_broken: 47748995Ssheldonh *++p = '\0'; 47877287Sdd errx(1, "illegal or unsupported format '%s'", p2); 47948995Ssheldonh /* NOTREACHED */ 4801590Srgrimes } 48148995Ssheldonh while (*++p) 48248995Ssheldonh if (*p == '%' && *(p+1) && *(p+1) != '%') 48348995Ssheldonh errx(1, "too many conversions"); 48448995Ssheldonh else if (*p == '%' && *(p+1) == '%') 48548995Ssheldonh p++; 48648995Ssheldonh else if (*p == '%' && !*(p+1)) { 487164852Sdds if (strlcat(format, "%", sizeof(format)) >= 488164852Sdds sizeof(format)) 489164852Sdds errx(1, "-w word too long"); 49048995Ssheldonh break; 49148995Ssheldonh } 4921590Srgrimes } 4931590Srgrimes} 494