1 /* $NetBSD: jot.c,v 1.25 2009/04/12 11:19:18 lukem 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.25 2009/04/12 11:19:18 lukem 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 case 3: 194 if (!is_default(argv[2])) { 195 if (!sscanf(argv[2], "%lf", &ender)) 196 ender = argv[2][strlen(argv[2])-1]; 197 have |= ENDER; 198 if (prec < 0) 199 n = getprec(argv[2]); 200 } 201 case 2: 202 if (!is_default(argv[1])) { 203 if (!sscanf(argv[1], "%lf", &begin)) 204 begin = argv[1][strlen(argv[1])-1]; 205 have |= BEGIN; 206 if (prec < 0) 207 prec = getprec(argv[1]); 208 if (n > prec) /* maximum precision */ 209 prec = n; 210 } 211 case 1: 212 if (!is_default(argv[0])) { 213 reps = strtoul(argv[0], &ep, 0); 214 if (*ep != 0 || reps < 0) 215 errx(EXIT_FAILURE, "Bad reps value: %s", 216 argv[0]); 217 have |= REPS; 218 } 219 break; 220 case 0: 221 usage(); 222 break; 223 default: 224 errx(EXIT_FAILURE, 225 "Too many arguments. What do you mean by %s?", argv[4]); 226 } 227 getformat(); 228 229 if (prec == -1) 230 prec = 0; 231 232 if (randomize) { 233 /* 'step' is the seed here, use pseudo-random default */ 234 if (!(have & STEP)) 235 step = time(NULL) * getpid(); 236 /* Take the default values for everything else */ 237 return; 238 } 239 240 /* 241 * The loop we run uses begin/step/reps, so if we have been 242 * given an end value (ender) we must use it to replace the 243 * default values of the others. 244 * We will assume a begin of 0 and step of 1 if necessary. 245 */ 246 247 switch (have) { 248 249 case ENDER | STEP: 250 case ENDER | STEP | BEGIN: 251 /* Calculate reps */ 252 if (step == 0.0) 253 reps = 0; /* ie infinite */ 254 else { 255 reps = (ender - begin + step) / step; 256 if (reps <= 0) 257 errx(EXIT_FAILURE, "Impossible stepsize"); 258 } 259 break; 260 261 case REPS | ENDER: 262 case REPS | ENDER | STEP: 263 /* Calculate begin */ 264 if (reps == 0) 265 errx(EXIT_FAILURE, 266 "Must specify begin if reps == 0"); 267 begin = ender - reps * step + step; 268 break; 269 270 case REPS | BEGIN | ENDER: 271 /* Calculate step */ 272 if (reps == 0) 273 errx(EXIT_FAILURE, 274 "Infinite sequences cannot be bounded"); 275 if (reps == 1) 276 step = 0.0; 277 else 278 step = (ender - begin) / (reps - 1); 279 break; 280 281 case REPS | BEGIN | ENDER | STEP: 282 /* reps given and implied - take smaller */ 283 if (step == 0.0) 284 break; 285 t = (ender - begin + step) / step; 286 if (t <= 0) 287 errx(EXIT_FAILURE, 288 "Impossible stepsize"); 289 if (t < reps) 290 reps = t; 291 break; 292 293 default: 294 /* No values can be calculated, use defaults */ 295 break; 296 } 297 } 298 299 static void 300 putdata(double x, long notlast) 301 { 302 303 if (boring) /* repeated word */ 304 printf("%s", format); 305 else if (dox) /* scalar */ 306 printf(format, (long)floor(x)); 307 else /* real */ 308 printf(format, x); 309 if (notlast != 0) 310 fputs(sepstring, stdout); 311 } 312 313 __dead static void 314 usage(void) 315 { 316 (void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] " 317 "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n", 318 getprogname()); 319 exit(1); 320 } 321 322 static int 323 getprec(char *num_str) 324 { 325 326 num_str = strchr(num_str, '.'); 327 if (num_str == NULL) 328 return 0; 329 return strspn(num_str + 1, "0123456789"); 330 } 331 332 static void 333 getformat(void) 334 { 335 char *p; 336 size_t sz; 337 338 if (boring) /* no need to bother */ 339 return; 340 for (p = format; *p; p++) { /* look for '%' */ 341 if (*p == '%') { 342 if (*(p+1) != '%') 343 break; 344 p++; /* leave %% alone */ 345 } 346 } 347 sz = sizeof(format) - strlen(format) - 1; 348 if (!*p) { 349 if (chardata || prec == 0) { 350 if ((size_t)snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz) 351 errx(EXIT_FAILURE, "-w word too long"); 352 dox = 1; 353 } else { 354 if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) 355 errx(EXIT_FAILURE, "-w word too long"); 356 } 357 } else if (!*(p+1)) { 358 if (sz <= 0) 359 errx(EXIT_FAILURE, "-w word too long"); 360 strcat(format, "%"); /* cannot end in single '%' */ 361 } else { 362 p++; /* skip leading % */ 363 for(; *p && !isalpha((unsigned char)*p); p++) { 364 /* allow all valid printf(3) flags, but deny '*' */ 365 if (!strchr("0123456789#-+. ", *p)) 366 break; 367 } 368 /* Allow 'l' prefix, but no other. */ 369 if (*p == 'l') 370 p++; 371 switch (*p) { 372 case 'f': case 'e': case 'g': case '%': 373 case 'E': case 'G': 374 break; 375 case 's': 376 errx(EXIT_FAILURE, 377 "cannot convert numeric data to strings"); 378 break; 379 case 'd': case 'o': case 'x': case 'u': 380 case 'D': case 'O': case 'X': case 'U': 381 case 'c': case 'i': 382 dox = 1; 383 break; 384 default: 385 errx(EXIT_FAILURE, "unknown or invalid format `%s'", 386 format); 387 } 388 /* Need to check for trailing stuff to print */ 389 for (; *p; p++) /* look for '%' */ 390 if (*p == '%') { 391 if (*(p+1) != '%') 392 break; 393 p++; /* leave %% alone */ 394 } 395 if (*p) 396 errx(EXIT_FAILURE, "unknown or invalid format `%s'", 397 format); 398 } 399 } 400