jot.c revision 164851
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 * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 3527423Scharnierstatic const char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1993\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 381590Srgrimes#endif /* not lint */ 391590Srgrimes 401590Srgrimes#ifndef lint 4127423Scharnier#if 0 421590Srgrimesstatic char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93"; 4327423Scharnier#endif 4498254Sjmallett#endif 4598254Sjmallett#include <sys/cdefs.h> 4698254Sjmallett__FBSDID("$FreeBSD: head/usr.bin/jot/jot.c 164851 2006-12-03 17:05:04Z dds $"); 4798254Sjmallett 481590Srgrimes/* 491590Srgrimes * jot - print sequential or random data 501590Srgrimes * 511590Srgrimes * Author: John Kunze, Office of Comp. Affairs, UCB 521590Srgrimes */ 531590Srgrimes 541590Srgrimes#include <ctype.h> 5527423Scharnier#include <err.h> 561590Srgrimes#include <limits.h> 571590Srgrimes#include <stdio.h> 5899457Smike#include <stdint.h> 591590Srgrimes#include <stdlib.h> 60164026Sdds#include <stdbool.h> 611590Srgrimes#include <string.h> 621590Srgrimes#include <time.h> 6323511Sache#include <unistd.h> 641590Srgrimes 65164046Sdds/* Defaults */ 661590Srgrimes#define REPS_DEF 100 671590Srgrimes#define BEGIN_DEF 1 681590Srgrimes#define ENDER_DEF 100 691590Srgrimes#define STEP_DEF 1 701590Srgrimes 71164046Sdds/* Flags of options that have been set */ 72164021Sdds#define HAVE_STEP 1 73164021Sdds#define HAVE_ENDER 2 74164021Sdds#define HAVE_BEGIN 4 75164021Sdds#define HAVE_REPS 8 76164021Sdds 77164028Sdds#define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0) 781590Srgrimes 79164046Sddsstatic bool boring; 80164046Sddsstatic int prec; 81164046Sddsstatic bool longdata; 82164046Sddsstatic bool intdata; 83164046Sddsstatic bool chardata; 84164046Sddsstatic bool nosign; 85164046Sddsstatic const char *sepstring = "\n"; 86164046Sddsstatic char format[BUFSIZ]; 871590Srgrimes 88164046Sddsstatic void getformat(void); 89164046Sddsstatic int getprec(const char *); 90164046Sddsstatic int putdata(double, bool); 9192920Simpstatic void usage(void); 921590Srgrimes 931590Srgrimesint 9498254Sjmallettmain(int argc, char **argv) 951590Srgrimes{ 96164046Sdds bool have_format = false; 97164046Sdds bool infinity = false; 98164046Sdds bool nofinalnl = false; 99164046Sdds bool randomize = false; 100164046Sdds bool use_random = false; 101164046Sdds int ch; 102164046Sdds int mask = 0; 103164046Sdds int n = 0; 104164046Sdds double begin; 105164046Sdds double divisor; 106164046Sdds double ender; 107164046Sdds double s; 108164025Sdds double x, y; 109164025Sdds long i; 110164046Sdds long reps; 1111590Srgrimes 112164046Sdds while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1) 11398254Sjmallett switch (ch) { 1141590Srgrimes case 'b': 115164046Sdds boring = true; 11677276Sdd /* FALLTHROUGH */ 1171590Srgrimes case 'w': 11877276Sdd if (strlcpy(format, optarg, sizeof(format)) >= 11977276Sdd sizeof(format)) 12077276Sdd errx(1, "-%c word too long", ch); 121164035Sdds have_format = true; 1221590Srgrimes break; 123164046Sdds case 'c': 124164046Sdds chardata = true; 1251590Srgrimes break; 126164046Sdds case 'n': 127164046Sdds nofinalnl = true; 128164046Sdds break; 1291590Srgrimes case 'p': 13077276Sdd prec = atoi(optarg); 1311590Srgrimes if (prec <= 0) 13227423Scharnier errx(1, "bad precision value"); 133164035Sdds have_format = true; 1341590Srgrimes break; 135164046Sdds case 'r': 136164046Sdds randomize = true; 137164046Sdds break; 138164046Sdds case 's': 139164046Sdds sepstring = optarg; 140164046Sdds break; 1411590Srgrimes default: 14227423Scharnier usage(); 1431590Srgrimes } 14477276Sdd argc -= optind; 14577276Sdd argv += optind; 1461590Srgrimes 14777276Sdd switch (argc) { /* examine args right to left, falling thru cases */ 1481590Srgrimes case 4: 14977276Sdd if (!is_default(argv[3])) { 15077276Sdd if (!sscanf(argv[3], "%lf", &s)) 15177276Sdd errx(1, "bad s value: %s", argv[3]); 152164021Sdds mask |= HAVE_STEP; 153164026Sdds if (randomize) 154164035Sdds use_random = true; 1551590Srgrimes } 156164021Sdds /* FALLTHROUGH */ 1571590Srgrimes case 3: 15877276Sdd if (!is_default(argv[2])) { 15977276Sdd if (!sscanf(argv[2], "%lf", &ender)) 16077276Sdd ender = argv[2][strlen(argv[2])-1]; 161164021Sdds mask |= HAVE_ENDER; 1621590Srgrimes if (!prec) 16377276Sdd n = getprec(argv[2]); 1641590Srgrimes } 165164021Sdds /* FALLTHROUGH */ 1661590Srgrimes case 2: 16777276Sdd if (!is_default(argv[1])) { 16877276Sdd if (!sscanf(argv[1], "%lf", &begin)) 16977276Sdd begin = argv[1][strlen(argv[1])-1]; 170164021Sdds mask |= HAVE_BEGIN; 1711590Srgrimes if (!prec) 17277276Sdd prec = getprec(argv[1]); 1731590Srgrimes if (n > prec) /* maximum precision */ 1741590Srgrimes prec = n; 1751590Srgrimes } 176164021Sdds /* FALLTHROUGH */ 1771590Srgrimes case 1: 17877276Sdd if (!is_default(argv[0])) { 17977276Sdd if (!sscanf(argv[0], "%ld", &reps)) 18077276Sdd errx(1, "bad reps value: %s", argv[0]); 181164021Sdds mask |= HAVE_REPS; 1821590Srgrimes } 1831590Srgrimes break; 1841590Srgrimes case 0: 18527423Scharnier usage(); 1861590Srgrimes default: 18777276Sdd errx(1, "too many arguments. What do you mean by %s?", 18877276Sdd argv[4]); 1891590Srgrimes } 1901590Srgrimes getformat(); 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) { 287164035Sdds ender += 1; 288164046Sdds nosign = true; 289164046Sdds intdata = true; 290164035Sdds (void)strlcpy(format, 291164035Sdds chardata ? "%c" : "%u", sizeof(format)); 292164035Sdds } 293164025Sdds x = (ender - begin) * (ender > begin ? 1 : -1); 294164025Sdds for (i = 1; i <= reps || infinity; i++) { 295164035Sdds if (use_random) 296164035Sdds y = random() / divisor; 297164026Sdds else 298164035Sdds y = arc4random() / divisor; 299164046Sdds if (putdata(y * x + begin, !(reps - i))) 30077276Sdd errx(1, "range error in conversion"); 30177276Sdd } 30277276Sdd } else 303164025Sdds for (i = 1, x = begin; i <= reps || infinity; i++, x += s) 304164046Sdds if (putdata(x, !(reps - i))) 30577276Sdd errx(1, "range error in conversion"); 30677276Sdd if (!nofinalnl) 30777276Sdd putchar('\n'); 30877276Sdd exit(0); 3091590Srgrimes} 3101590Srgrimes 311164046Sdds/* 312164046Sdds * Send x to stdout using the specified format. 313164046Sdds * Last is true if this is the set's last value. 314164046Sdds * Return 0 if OK, or a positive number if the number passed was 315164046Sdds * outside the range specified by the various flags. 316164046Sdds */ 317164046Sddsstatic int 318164046Sddsputdata(double x, bool last) 3191590Srgrimes{ 3201590Srgrimes 32155515Ssheldonh if (boring) 32262871Skris printf("%s", format); 32355515Ssheldonh else if (longdata && nosign) { 32455515Ssheldonh if (x <= (double)ULONG_MAX && x >= (double)0) 32555515Ssheldonh printf(format, (unsigned long)x); 32655515Ssheldonh else 32755515Ssheldonh return (1); 32855515Ssheldonh } else if (longdata) { 32955515Ssheldonh if (x <= (double)LONG_MAX && x >= (double)LONG_MIN) 33055515Ssheldonh printf(format, (long)x); 33155515Ssheldonh else 33255515Ssheldonh return (1); 33355515Ssheldonh } else if (chardata || (intdata && !nosign)) { 33455515Ssheldonh if (x <= (double)INT_MAX && x >= (double)INT_MIN) 33555515Ssheldonh printf(format, (int)x); 33655515Ssheldonh else 33755515Ssheldonh return (1); 33855515Ssheldonh } else if (intdata) { 33955515Ssheldonh if (x <= (double)UINT_MAX && x >= (double)0) 34055515Ssheldonh printf(format, (unsigned int)x); 34155515Ssheldonh else 34255515Ssheldonh return (1); 34355515Ssheldonh 34455515Ssheldonh } else 3451590Srgrimes printf(format, x); 346164046Sdds if (!last) 3471590Srgrimes fputs(sepstring, stdout); 34855515Ssheldonh 34955515Ssheldonh return (0); 3501590Srgrimes} 3511590Srgrimes 35227423Scharnierstatic void 35398254Sjmallettusage(void) 3541590Srgrimes{ 35527423Scharnier fprintf(stderr, "%s\n%s\n", 35630908Scharnier "usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]", 35730908Scharnier " [reps [begin [end [s]]]]"); 3581590Srgrimes exit(1); 3591590Srgrimes} 3601590Srgrimes 361164046Sdds/* 362164046Sdds * Return the number of digits following the number's decimal point. 363164046Sdds * Return 0 if no decimal point is found. 364164046Sdds */ 365164046Sddsstatic int 366164046Sddsgetprec(const char *str) 3671590Srgrimes{ 368164046Sdds const char *p; 369164046Sdds const char *q; 3701590Srgrimes 37177287Sdd for (p = str; *p; p++) 3721590Srgrimes if (*p == '.') 3731590Srgrimes break; 3741590Srgrimes if (!*p) 3751590Srgrimes return (0); 3761590Srgrimes for (q = ++p; *p; p++) 377132240Stjr if (!isdigit((unsigned char)*p)) 3781590Srgrimes break; 3791590Srgrimes return (p - q); 3801590Srgrimes} 3811590Srgrimes 382164046Sdds/* 383164046Sdds * Set format, intdata, chardata, longdata, and nosign 384164046Sdds * based on the command line arguments. 385164046Sdds */ 386164046Sddsstatic void 38798254Sjmallettgetformat(void) 3881590Srgrimes{ 38977287Sdd char *p, *p2; 39055515Ssheldonh int dot, hash, space, sign, numbers = 0; 39177276Sdd size_t sz; 3921590Srgrimes 3931590Srgrimes if (boring) /* no need to bother */ 3941590Srgrimes return; 3951590Srgrimes for (p = format; *p; p++) /* look for '%' */ 396164851Sdds if (*p == '%') 397164851Sdds if (p[1] == '%') 398164851Sdds p++; /* leave %% alone */ 399164851Sdds else 400164851Sdds break; 40177276Sdd sz = sizeof(format) - strlen(format) - 1; 40277276Sdd if (!*p && !chardata) { 40377276Sdd if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) 40477276Sdd errx(1, "-w word too long"); 40577276Sdd } else if (!*p && chardata) { 40677276Sdd if (strlcpy(p, "%c", sz) >= sz) 40777276Sdd errx(1, "-w word too long"); 408164046Sdds intdata = true; 40977276Sdd } else if (!*(p+1)) { 41077276Sdd if (sz <= 0) 41177276Sdd errx(1, "-w word too long"); 4121590Srgrimes strcat(format, "%"); /* cannot end in single '%' */ 41377276Sdd } else { 41448995Ssheldonh /* 41548995Ssheldonh * Allow conversion format specifiers of the form 41648995Ssheldonh * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of 41748995Ssheldonh * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u} 41848995Ssheldonh */ 41977287Sdd p2 = p++; 42048995Ssheldonh dot = hash = space = sign = numbers = 0; 421132240Stjr while (!isalpha((unsigned char)*p)) { 422132240Stjr if (isdigit((unsigned char)*p)) { 42348995Ssheldonh numbers++; 42448995Ssheldonh p++; 42548995Ssheldonh } else if ((*p == '#' && !(numbers|dot|sign|space| 42648995Ssheldonh hash++)) || 42748995Ssheldonh (*p == ' ' && !(numbers|dot|space++)) || 42848995Ssheldonh ((*p == '+' || *p == '-') && !(numbers|dot|sign++)) 42948995Ssheldonh || (*p == '.' && !(dot++))) 43048995Ssheldonh p++; 43148995Ssheldonh else 43255515Ssheldonh goto fmt_broken; 43348995Ssheldonh } 43455515Ssheldonh if (*p == 'l') { 435164046Sdds longdata = true; 43655515Ssheldonh if (*++p == 'l') { 43755515Ssheldonh if (p[1] != '\0') 43855515Ssheldonh p++; 43955515Ssheldonh goto fmt_broken; 44055515Ssheldonh } 44155515Ssheldonh } 44248995Ssheldonh switch (*p) { 44348995Ssheldonh case 'o': case 'u': case 'x': case 'X': 444164046Sdds intdata = nosign = true; 4451590Srgrimes break; 44648995Ssheldonh case 'd': case 'i': 447164046Sdds intdata = true; 44848995Ssheldonh break; 44948995Ssheldonh case 'D': 45055515Ssheldonh if (!longdata) { 451164046Sdds intdata = true; 45248995Ssheldonh break; 45348995Ssheldonh } 45448995Ssheldonh case 'O': case 'U': 45555515Ssheldonh if (!longdata) { 456164046Sdds intdata = nosign = true; 45748995Ssheldonh break; 45848995Ssheldonh } 45948995Ssheldonh case 'c': 46055515Ssheldonh if (!(intdata | longdata)) { 461164046Sdds chardata = true; 46248995Ssheldonh break; 46348995Ssheldonh } 46455515Ssheldonh case 'h': case 'n': case 'p': case 'q': case 's': case 'L': 46548995Ssheldonh case '$': case '*': 46655515Ssheldonh goto fmt_broken; 46748995Ssheldonh case 'f': case 'e': case 'g': case 'E': case 'G': 46855515Ssheldonh if (!longdata) 46948995Ssheldonh break; 47048995Ssheldonh /* FALLTHROUGH */ 4711590Srgrimes default: 47255515Ssheldonhfmt_broken: 47348995Ssheldonh *++p = '\0'; 47477287Sdd errx(1, "illegal or unsupported format '%s'", p2); 47548995Ssheldonh /* NOTREACHED */ 4761590Srgrimes } 47748995Ssheldonh while (*++p) 47848995Ssheldonh if (*p == '%' && *(p+1) && *(p+1) != '%') 47948995Ssheldonh errx(1, "too many conversions"); 48048995Ssheldonh else if (*p == '%' && *(p+1) == '%') 48148995Ssheldonh p++; 48248995Ssheldonh else if (*p == '%' && !*(p+1)) { 48348995Ssheldonh strcat(format, "%"); 48448995Ssheldonh break; 48548995Ssheldonh } 4861590Srgrimes } 4871590Srgrimes} 488