1 /* $NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil Exp $ */ 2 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 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93"; 41 #endif 42 __RCSID("$NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil Exp $"); 43 #endif /* not lint */ 44 45 /* 46 * jot - print sequential or random data 47 * 48 * Author: John Kunze, Office of Comp. Affairs, UCB 49 */ 50 51 #include <ctype.h> 52 #include <err.h> 53 #include <limits.h> 54 #include <math.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <time.h> 59 #include <unistd.h> 60 61 #define REPS_DEF 100 62 #define BEGIN_DEF 1 63 #define ENDER_DEF 100 64 #define STEP_DEF 1 65 66 #define is_default(s) (strcmp((s), "-") == 0) 67 68 static double begin = BEGIN_DEF; 69 static double ender = ENDER_DEF; 70 static double step = STEP_DEF; 71 static long reps = REPS_DEF; 72 static int randomize; 73 static int boring; 74 static int prec = -1; 75 static int dox; 76 static int chardata; 77 static int nofinalnl; 78 static const char *sepstring = "\n"; 79 static char format[BUFSIZ]; 80 81 static void getargs(int, char *[]); 82 static void getformat(void); 83 static int getprec(char *); 84 static void putdata(double, long); 85 static void usage(void) __dead; 86 87 int 88 main(int argc, char *argv[]) 89 { 90 double x; 91 long i; 92 93 getargs(argc, argv); 94 if (randomize) { 95 x = ender - begin; 96 if (x < 0) { 97 x = -x; 98 begin = ender; 99 } 100 if (dox == 0) 101 /* 102 * We are printing floating point, generate random 103 * number that include both supplied limits. 104 * Due to FP routing for display the low and high 105 * values are likely to occur half as often as all 106 * the others. 107 */ 108 x /= (1u << 31) - 1.0; 109 else { 110 /* 111 * We are printing integers increase the range by 112 * one but ensure we never generate it. 113 * This makes all the integer values equally likely. 114 */ 115 x += 1.0; 116 x /= (1u << 31); 117 } 118 srandom((unsigned long) step); 119 for (i = 1; i <= reps || reps == 0; i++) 120 putdata(random() * x + begin, reps - i); 121 } else { 122 /* 123 * If we are going to display as integer, add 0.5 here 124 * and use floor(x) later to get sane rounding. 125 */ 126 x = begin; 127 if (dox) 128 x += 0.5; 129 for (i = 1; i <= reps || reps == 0; i++, x += step) 130 putdata(x, reps - i); 131 } 132 if (!nofinalnl) 133 putchar('\n'); 134 exit(0); 135 } 136 137 static void 138 getargs(int argc, char *argv[]) 139 { 140 unsigned int have = 0; 141 #define BEGIN 1 142 #define STEP 2 /* seed if -r */ 143 #define REPS 4 144 #define ENDER 8 145 int n = 0; 146 long t; 147 char *ep; 148 149 for (;;) { 150 switch (getopt(argc, argv, "b:cnp:rs:w:")) { 151 default: 152 usage(); 153 case -1: 154 break; 155 case 'c': 156 chardata = 1; 157 continue; 158 case 'n': 159 nofinalnl = 1; 160 continue; 161 case 'p': 162 prec = strtol(optarg, &ep, 0); 163 if (*ep != 0 || prec < 0) 164 errx(EXIT_FAILURE, "Bad precision value"); 165 continue; 166 case 'r': 167 randomize = 1; 168 continue; 169 case 's': 170 sepstring = optarg; 171 continue; 172 case 'b': 173 boring = 1; 174 /* FALLTHROUGH */ 175 case 'w': 176 strlcpy(format, optarg, sizeof(format)); 177 continue; 178 } 179 break; 180 } 181 argc -= optind; 182 argv += optind; 183 184 switch (argc) { /* examine args right to left, falling thru cases */ 185 case 4: 186 if (!is_default(argv[3])) { 187 step = strtod(argv[3], &ep); 188 if (*ep != 0) 189 errx(EXIT_FAILURE, "Bad step value: %s", 190 argv[3]); 191 have |= STEP; 192 } 193 /* FALLTHROUGH */ 194 case 3: 195 if (!is_default(argv[2])) { 196 if (!sscanf(argv[2], "%lf", &ender)) 197 ender = argv[2][strlen(argv[2])-1]; 198 have |= ENDER; 199 if (prec < 0) 200 n = getprec(argv[2]); 201 } 202 /* FALLTHROUGH */ 203 case 2: 204 if (!is_default(argv[1])) { 205 if (!sscanf(argv[1], "%lf", &begin)) 206 begin = argv[1][strlen(argv[1])-1]; 207 have |= BEGIN; 208 if (prec < 0) 209 prec = getprec(argv[1]); 210 if (n > prec) /* maximum precision */ 211 prec = n; 212 } 213 /* FALLTHROUGH */ 214 case 1: 215 if (!is_default(argv[0])) { 216 reps = strtoul(argv[0], &ep, 0); 217 if (*ep != 0 || reps < 0) 218 errx(EXIT_FAILURE, "Bad reps value: %s", 219 argv[0]); 220 have |= REPS; 221 } 222 /* FALLTHROUGH */ 223 case 0: 224 break; 225 default: 226 errx(EXIT_FAILURE, 227 "Too many arguments. What do you mean by %s?", argv[4]); 228 } 229 230 if (prec == -1) 231 prec = 0; 232 233 getformat(); 234 235 if (randomize) { 236 /* 'step' is the seed here, use pseudo-random default */ 237 if (!(have & STEP)) 238 step = time(NULL) * getpid(); 239 /* Take the default values for everything else */ 240 return; 241 } 242 243 /* 244 * The loop we run uses begin/step/reps, so if we have been 245 * given an end value (ender) we must use it to replace the 246 * default values of the others. 247 * We will assume a begin of 0 and step of 1 if necessary. 248 */ 249 250 switch (have) { 251 252 case ENDER | STEP: 253 case ENDER | STEP | BEGIN: 254 /* Calculate reps */ 255 if (step == 0.0) 256 reps = 0; /* ie infinite */ 257 else { 258 reps = (ender - begin + step) / step; 259 if (reps <= 0) 260 errx(EXIT_FAILURE, "Impossible stepsize"); 261 } 262 break; 263 264 case REPS | ENDER: 265 case REPS | ENDER | STEP: 266 /* Calculate begin */ 267 if (reps == 0) 268 errx(EXIT_FAILURE, 269 "Must specify begin if reps == 0"); 270 begin = ender - reps * step + step; 271 break; 272 273 case REPS | BEGIN | ENDER: 274 /* Calculate step */ 275 if (reps == 0) 276 errx(EXIT_FAILURE, 277 "Infinite sequences cannot be bounded"); 278 if (reps == 1) 279 step = 0.0; 280 else 281 step = (ender - begin) / (reps - 1); 282 break; 283 284 case REPS | BEGIN | ENDER | STEP: 285 /* reps given and implied - take smaller */ 286 if (step == 0.0) 287 break; 288 t = (ender - begin + step) / step; 289 if (t <= 0) 290 errx(EXIT_FAILURE, 291 "Impossible stepsize"); 292 if (t < reps) 293 reps = t; 294 break; 295 296 default: 297 /* No values can be calculated, use defaults */ 298 break; 299 } 300 } 301 302 static void 303 putdata(double x, long notlast) 304 { 305 306 if (boring) /* repeated word */ 307 printf("%s", format); 308 else if (dox) /* scalar */ 309 printf(format, (long)floor(x)); 310 else /* real */ 311 printf(format, x); 312 if (notlast != 0) 313 fputs(sepstring, stdout); 314 } 315 316 __dead static void 317 usage(void) 318 { 319 (void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] " 320 "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n", 321 getprogname()); 322 exit(1); 323 } 324 325 static int 326 getprec(char *num_str) 327 { 328 329 num_str = strchr(num_str, '.'); 330 if (num_str == NULL) 331 return 0; 332 return strspn(num_str + 1, "0123456789"); 333 } 334 335 static void 336 getformat(void) 337 { 338 char *p; 339 size_t sz; 340 341 if (boring) /* no need to bother */ 342 return; 343 for (p = format; *p; p++) { /* look for '%' */ 344 if (*p == '%') { 345 if (*(p+1) != '%') 346 break; 347 p++; /* leave %% alone */ 348 } 349 } 350 sz = sizeof(format) - strlen(format) - 1; 351 if (!*p) { 352 if (chardata || prec == 0) { 353 if ((size_t)snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz) 354 errx(EXIT_FAILURE, "-w word too long"); 355 dox = 1; 356 } else { 357 if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) 358 errx(EXIT_FAILURE, "-w word too long"); 359 } 360 } else if (!*(p+1)) { 361 if (sz <= 0) 362 errx(EXIT_FAILURE, "-w word too long"); 363 strcat(format, "%"); /* cannot end in single '%' */ 364 } else { 365 p++; /* skip leading % */ 366 for(; *p && !isalpha((unsigned char)*p); p++) { 367 /* allow all valid printf(3) flags, but deny '*' */ 368 if (!strchr("0123456789#-+. ", *p)) 369 break; 370 } 371 /* Allow 'l' prefix, but no other. */ 372 if (*p == 'l') 373 p++; 374 switch (*p) { 375 case 'f': case 'e': case 'g': case '%': 376 case 'E': case 'G': 377 break; 378 case 's': 379 errx(EXIT_FAILURE, 380 "cannot convert numeric data to strings"); 381 break; 382 case 'd': case 'o': case 'x': case 'u': 383 case 'D': case 'O': case 'X': case 'U': 384 case 'c': case 'i': 385 dox = 1; 386 break; 387 default: 388 errx(EXIT_FAILURE, "unknown or invalid format `%s'", 389 format); 390 } 391 /* Need to check for trailing stuff to print */ 392 for (; *p; p++) /* look for '%' */ 393 if (*p == '%') { 394 if (*(p+1) != '%') 395 break; 396 p++; /* leave %% alone */ 397 } 398 if (*p) 399 errx(EXIT_FAILURE, "unknown or invalid format `%s'", 400 format); 401 } 402 } 403