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