Emit random numbers for statistical tests.
[u/mdw/catacomb] / rspit.c
CommitLineData
ee33470f 1/* -*-c-*-
2 *
3 * $Id: rspit.c,v 1.1 1999/12/10 23:29:13 mdw Exp $
4 *
5 * Spit out random numbers
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: rspit.c,v $
33 * Revision 1.1 1999/12/10 23:29:13 mdw
34 * Emit random numbers for statistical tests.
35 *
36 */
37
38/*----- Header files ------------------------------------------------------*/
39
40#include "config.h"
41
42#include <errno.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.h>
48
49#ifndef PORTABLE
50# include <unistd.h>
51#endif
52
53#include <mLib/darray.h>
54#include <mLib/dstr.h>
55#include <mLib/mdwopt.h>
56#include <mLib/quis.h>
57#include <mLib/report.h>
58#include <mLib/sub.h>
59
60#include "grand.h"
61
62#include "lcrand.h"
63#include "fibrand.h"
64#include "rand.h"
65#include "noise.h"
66
67#include "bbs.h"
68#include "mprand.h"
69
70#include "rc4.h"
71
72#include "des-ofb.h"
73#include "des3-ofb.h"
74#include "rc5-ofb.h"
75#include "blowfish-ofb.h"
76#include "idea-ofb.h"
77
78#include "rmd160.h"
79
80/*----- Data structures ---------------------------------------------------*/
81
82typedef struct gen {
83 const char *name;
84 grand *(*seed)(unsigned /*i*/);
85 unsigned i;
86 const char *help;
87} gen;
88
89static gen generators[];
90
91/*----- Miscellaneous static data -----------------------------------------*/
92
93static FILE *outfp = stdout;
94static size_t outsz = 0;
95
96static int argc;
97static char **argv;
98
99static unsigned flags = 0;
100
101enum {
102 f_progress = 1,
103 f_file = 2
104};
105
106/*----- Help options ------------------------------------------------------*/
107
108static void usage(FILE *fp)
109{
110 pquis(fp, "Usage: $ generator [options]\n");
111}
112
113static void version(FILE *fp)
114{
115 pquis(fp, "$, Catacomb version " VERSION "\n");
116}
117
118static void help(FILE *fp)
119{
120 version(fp);
121 fputc('\n', fp);
122 usage(fp);
123 pquis(fp, "\n\
124Emits a stream of random bytes suitable for, well, all sorts of things.\n\
125The primary objective is to be able to generate streams of input for\n\
126statistical tests, such as Diehard.\n\
127\n\
128Options are specific to the particular generator, although there's a\n\
129common core set:\n\
130\n\
131-h, --help Display this help message.\n\
132-v, --version Display the program's version number.\n\
133-u, --usage Display a useless usage message.\n\
134\n\
135-l, --list Show a list of the supported generators, with\n\
136 their options.\n\
137-o, --output FILE Write output to FILE, not stdout.\n\
138-z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
139-p, --progress Show a little progress meter (on stderr).\n\
140\n\
141(A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
142`k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
143");
144}
145
146/*----- Main options parser -----------------------------------------------*/
147
148static struct option opts[] = {
149
150 /* --- Standard GNU help options --- */
151
152 { "help", 0, 0, 'h' },
153 { "version", 0, 0, 'v' },
154 { "usage", 0, 0, 'u' },
155
156 /* --- Other useful things --- */
157
158 { "list", 0, 0, 'l' },
159 { "output", OPTF_ARGREQ, 0, 'o' },
160 { "size", OPTF_ARGREQ, 0, 'z' },
161 { "progress", 0, 0, 'p' },
162
163 /* --- End of main table --- */
164
165 { 0, 0, 0, 0 }
166};
167
168static const char *sopts = "hvu lo:z:p";
169
170#ifndef OPTION_V
171 DA_DECL(option_v, struct option);
172# define OPTION_V
173#endif
174
175static option_v optv = DA_INIT;
176static dstr optd = DSTR_INIT;
177
178/* --- @addopts@ --- *
179 *
180 * Arguments: @const char *s@ = pointer to short options
181 * @struct option *l@ = pointer to long options
182 *
183 * Returns: ---
184 *
185 * Use: Adds a collection of options to the table.
186 */
187
188static void addopts(const char *s, struct option *l)
189{
190 dstr_puts(&optd, s);
191 if (DA_LEN(&optv))
192 DA_SHRINK(&optv, 1);
193 while (l->name)
194 DA_PUSH(&optv, *l++);
195 DA_PUSH(&optv, *l);
196}
197
198/* --- @opt@ --- *
199 *
200 * Arguments: ---
201 *
202 * Returns: Next option from argument array.
203 *
204 * Use: Fetches options, handling the standard ones.
205 */
206
207static int opt(void)
208{
209 for (;;) {
210 int i = mdwopt(argc, argv, optd.buf, DA(&optv), 0, 0, 0);
211 switch (i) {
212 case 'h':
213 help(stdout);
214 exit(0);
215 case 'v':
216 version(stdout);
217 exit(0);
218 case 'u':
219 usage(stdout);
220 exit(0);
221 case 'l': {
222 gen *g;
223 puts("Generators supported:");
224 for (g = generators; g->name; g++)
225 printf(" %s %s\n", g->name, g->help);
226 exit(0);
227 } break;
228 case 'o':
229 if (flags & f_file)
230 die(EXIT_FAILURE, "already set an output file");
231 if (strcmp(optarg, "-") == 0)
232 outfp = stdout;
233 else {
234 outfp = fopen(optarg, "w");
235 if (!outfp) {
236 die(EXIT_FAILURE, "couldn't open output file `%s': %s",
237 strerror(errno));
238 }
239 }
240 flags |= f_file;
241 break;
242 case 'z': {
243 char *p;
244 outsz = strtoul(optarg, &p, 0);
245 if (!outsz)
246 die(EXIT_FAILURE, "bad number `%s'", optarg);
247 switch (*p) {
248 case 'G': case 'g': outsz *= 1024;
249 case 'M': case 'm': outsz *= 1024;
250 case 'K': case 'k': outsz *= 1024;
251 case 0:
252 break;
253 default:
254 die(EXIT_FAILURE, "bad suffix `%s'", p);
255 break;
256 }
257 if (*p && p[1] != 0)
258 die(EXIT_FAILURE, "bad suffix `%s'", p);
259 } break;
260 case 'p':
261 flags |= f_progress;
262 break;
263 default:
264 return (i);
265 }
266 }
267}
268
269/*----- Manglers for seed strings -----------------------------------------*/
270
271/* --- @unhex@ --- *
272 *
273 * Arguments: @const char *p@ = pointer to input string
274 * @char **end@ = where the end goes
275 * @dstr *d@ = output buffer
276 *
277 * Returns: ---
278 *
279 * Use: Transforms a hex string into a chunk of binary data.
280 */
281
282static void unhex(const char *p, char **end, dstr *d)
283{
284 while (p[0] && p[1]) {
285 int x = p[0], y = p[1];
286 if ('0' <= x && x <= '9') x -= '0';
287 else if ('A' <= x && x <= 'F') x -= 'A' - 10;
288 else if ('a' <= x && x <= 'f') x -= 'a' - 10;
289 else x = 0;
290 if ('0' <= y && y <= '9') y -= '0';
291 else if ('A' <= y && y <= 'F') y -= 'A' - 10;
292 else if ('a' <= y && y <= 'f') y -= 'a' - 10;
293 else y = 0;
294 DPUTC(d, (x << 4) + y);
295 p += 2;
296 }
297 *end = (char *)p;
298}
299
300/*----- Generators --------------------------------------------------------*/
301
302/* --- Blum-Blum-Shub strong generator --- */
303
304static int bbsev(int ev, mp *m, void *p)
305{
306 switch (ev) {
307 case BBSEV_FINDP:
308 fputs("Searching for p: ", stderr);
309 fflush(stderr);
310 break;
311 case BBSEV_FINDQ:
312 fputs("Searching for q: ", stderr);
313 fflush(stderr);
314 break;
315 case BBSEV_FAILP:
316 case BBSEV_FAILQ:
317 fputc('.', stderr);
318 fflush(stderr);
319 break;
320 case BBSEV_PASSP:
321 case BBSEV_PASSQ:
322 fputc('+', stderr);
323 fflush(stderr);
324 break;
325 case BBSEV_GOODP:
326 case BBSEV_GOODQ:
327 fputc('\n', stderr);
328 fflush(stderr);
329 break;
330 }
331 return (0);
332}
333
334static grand *gen_bbs(unsigned i)
335{
336 /* --- Default modulus --- *
337 *
338 * The factors of this number are
339 *
340 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
341 * @7754313537966036459299022912838407755462506416274551744201653277@
342 * @313130311731673973886822067@
343 *
344 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
345 * @5374320073594018817245784145742769603334292182227671519041431067@
346 * @61344781426317516045890159@
347 *
348 * Both %$p$% and %$q$% are prime; %$(p - 1)/2%$ and %$(q - 1)/2$% have no
349 * common factors. They were found using this program, with random
350 * starting points.
351 *
352 * I hope that, by publishing these factors, I'll dissuade people from
353 * actually using this modulus in attempt to actually attain real
354 * security. The program is quite quick at finding Blum numbers, so
355 * there's no excuse for not generating your own.
356 */
357
358 const char *mt =
359 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
360
361 /* --- Other things --- */
362
363 grand *r;
364 const char *xt = 0;
365 unsigned bits = 512;
366 mp *m, *x;
367 unsigned show = 0;
368
369 /* --- Parse options --- */
370
371 static struct option opts[] = {
372 { "modulus", OPTF_ARGREQ, 0, 'm' },
373 { "generate", 0, 0, 'g' },
374 { "seed", OPTF_ARGREQ, 0, 's' },
375 { "bits", OPTF_ARGREQ, 0, 'b' },
376 { "show", 0, 0, 'S' },
377 { 0, 0, 0, 0 }
378 };
379
380 addopts("m:gs:b:S", opts);
381
382 for (;;) {
383 int o = opt();
384 if (o < 0)
385 break;
386 switch (o) {
387 case 'm':
388 mt = optarg;
389 break;
390 case 'g':
391 mt = 0;
392 break;
393 case 's':
394 xt = optarg;
395 break;
396 case 'b':
397 bits = strtoul(optarg, 0, 0);
398 if (bits == 0)
399 die(EXIT_FAILURE, "bad number of bits `%s'", optarg);
400 break;
401 case 'S':
402 show = 1;
403 break;
404 default:
405 return (0);
406 }
407 }
408
409 /* --- Generate a modulus if one is requested --- */
410
411 if (mt) {
412 char *p;
413 m = mp_readstring(MP_NEW, mt, &p, 0);
414 if (*p)
415 die(EXIT_FAILURE, "bad modulus `%s'", mt);
416 /* Unfortunately I don't know how to test for a Blum integer */
417 } else {
418 mp *p = mprand(MP_NEW, bits / 2, &rand_global, 3);
419 mp *q = mprand(MP_NEW, bits - bits / 2, &rand_global, 3);
420 bbs_params bp;
421 int err;
422
423 if ((err = bbs_gen(&bp, p, q, 0,
424 (flags & f_progress) ? bbsev : 0, 0)) != 0)
425 die(EXIT_FAILURE, "modulus generation failed (reason = %i)", err);
426 m = bp.n;
427
428 if (show) {
429 fputs("p = ", stderr);
430 mp_writefile(bp.p, stderr, 10);
431 fputs("\nq = ", stderr);
432 mp_writefile(bp.q, stderr, 10);
433 fputs("\nn = ", stderr);
434 mp_writefile(bp.n, stderr, 10);
435 fputc('\n', stderr);
436 }
437
438 mp_drop(p);
439 mp_drop(q);
440 mp_drop(bp.p);
441 mp_drop(bp.q);
442 }
443
444 /* --- Set up a seed --- */
445
446 if (!xt)
447 x = mprand(MP_NEW, mp_bits(m) - 1, &rand_global, 1);
448 else {
449 char *p;
450 x = mp_readstring(MP_NEW, xt, &p, 0);
451 if (*p)
452 die(EXIT_FAILURE, "bad modulus `%s'", xt);
453 }
454
455 /* --- Right --- */
456
457 r = bbs_rand(m, x);
458
459 mp_drop(m);
460 mp_drop(x);
461 return (r);
462}
463
464/* --- Catacomb's random number generator --- */
465
466static grand *gen_rand(unsigned i)
467{
468 grand *r = rand_create();
469 dstr d = DSTR_INIT;
470
471 static struct option opts[] = {
472 { "key", OPTF_ARGREQ, 0, 'k' },
473 { "text", OPTF_ARGREQ, 0, 't' },
474 { "hex", OPTF_ARGREQ, 0, 'H' },
475 { "noise", 0, 0, 'n' },
476 { 0, 0, 0, 0 }
477 };
478
479 addopts("k:t:H:n", opts);
480
481 for (;;) {
482 int o = opt();
483 if (o < 0)
484 break;
485 switch (o) {
486 case 'k': {
487 rmd160_ctx c;
488 octet hash[RMD160_HASHSZ];
489 rmd160_init(&c);
490 rmd160_hash(&c, optarg, strlen(optarg));
491 rmd160_done(&c, hash);
492 r->ops->misc(r, RAND_KEY, hash, sizeof(hash));
493 } break;
494 case 't':
495 r->ops->misc(r, GRAND_SEEDBLOCK, optarg, strlen(optarg));
496 break;
497 case 'H': {
498 char *p;
499 DRESET(&d);
500 unhex(optarg, &p, &d);
501 if (*p)
502 die(EXIT_FAILURE, "bad hex key `%s'", optarg);
503 r->ops->misc(r, GRAND_SEEDBLOCK, d.buf, d.len);
504 } break;
505 case 'n':
506 r->ops->misc(r, RAND_NOISESRC, &noise_source);
507 break;
508 }
509 }
510
511 dstr_destroy(&d);
512 return (r);
513}
514
515/* --- RC4 output --- */
516
517static grand *gen_rc4(unsigned i)
518{
519 grand *r;
520 dstr d = DSTR_INIT;
521
522 static struct option opts[] = {
523 { "key", OPTF_ARGREQ, 0, 'k' },
524 { "hex", OPTF_ARGREQ, 0, 'H' },
525 { 0, 0, 0, 0 }
526 };
527
528 addopts("k:H:", opts);
529
530 for (;;) {
531 int o = opt();
532 if (o < 0)
533 break;
534 switch (o) {
535 case 'k': {
536 rmd160_ctx c;
537 dstr_ensure(&d, RMD160_HASHSZ);
538 rmd160_init(&c);
539 rmd160_hash(&c, optarg, strlen(optarg));
540 rmd160_done(&c, d.buf);
541 d.len += RMD160_HASHSZ;
542 } break;
543 case 'H': {
544 char *p;
545 unhex(optarg, &p, &d);
546 if (*p)
547 die(EXIT_FAILURE, "bad hex key `%s'", optarg);
548 } break;
549 default:
550 return (0);
551 }
552 }
553
554 if (!d.len) {
555 dstr_ensure(&d, 16);
556 d.len = 16;
557 rand_getgood(RAND_GLOBAL, d.buf, d.len);
558 }
559 r = rc4_rand(d.buf, d.len);
560 dstr_destroy(&d);
561 return (r);
562}
563
564/* --- Output feedback generators --- */
565
566#define OFBTAB \
567 E(OFB_DES, DES_KEYSZ, DES_BLKSZ, des_ofbrand), \
568 E(OFB_DES3, DES3_KEYSZ, DES3_BLKSZ, des3_ofbrand), \
569 E(OFB_RC5, RC5_KEYSZ, RC5_BLKSZ, rc5_ofbrand), \
570 E(OFB_BLOWFISH, BLOWFISH_KEYSZ, BLOWFISH_BLKSZ, blowfish_ofbrand), \
571 E(OFB_IDEA, IDEA_KEYSZ, IDEA_BLKSZ, idea_ofbrand)
572
573static struct {
574 size_t keysz;
575 size_t blksz;
576 grand *(*rand)(const void */*k*/, size_t /*sz*/);
577} ofbtab[] = {
578#define E(c, x, y, z) { x, y, z }
579 OFBTAB
580#undef E
581};
582
583enum {
584#define E(c, x, y, z) c
585 OFBTAB
586#undef E
587};
588
589#undef OFBTAB
590
591static grand *gen_ofb(unsigned i)
592{
593 grand *r;
594 dstr d = DSTR_INIT;
595 dstr iv = DSTR_INIT;
596
597 static struct option opts[] = {
598 { "key", OPTF_ARGREQ, 0, 'k' },
599 { "hex", OPTF_ARGREQ, 0, 'H' },
600 { "iv", OPTF_ARGREQ, 0, 'i' },
601 { 0, 0, 0, 0 }
602 };
603
604 addopts("k:H:i:", opts);
605
606 for (;;) {
607 int o = opt();
608 if (o < 0)
609 break;
610 switch (o) {
611 case 'k': {
612 rmd160_ctx c;
613 dstr_ensure(&d, RMD160_HASHSZ);
614 rmd160_init(&c);
615 rmd160_hash(&c, optarg, strlen(optarg));
616 rmd160_done(&c, d.buf);
617 d.len += RMD160_HASHSZ;
618 } break;
619 case 'H': {
620 char *p;
621 unhex(optarg, &p, &d);
622 if (*p)
623 die(EXIT_FAILURE, "bad hex key `%s'", optarg);
624 } break;
625 case 'i': {
626 char *p;
627 unhex(optarg, &p, &iv);
628 if (*p)
629 die(EXIT_FAILURE, "bad hex IV `%s'", optarg);
630 } break;
631 default:
632 return (0);
633 }
634 }
635
636 if (!d.len) {
637 size_t n = ofbtab[i].keysz;
638 if (!n)
639 n = 16;
640 dstr_ensure(&d, n);
641 d.len = n;
642 rand_getgood(RAND_GLOBAL, d.buf, d.len);
643 }
644
645 while (d.len < ofbtab[i].keysz)
646 DPUTD(&d, &d);
647 if (ofbtab[i].keysz && d.len > ofbtab[i].keysz)
648 d.len = ofbtab[i].keysz;
649
650 r = ofbtab[i].rand(d.buf, d.len);
651 if (iv.len) {
652 while (iv.len < ofbtab[i].blksz)
653 DPUTD(&iv, &iv);
654 r->ops->misc(r, GRAND_SEEDBLOCK, iv.buf);
655 }
656
657 dstr_destroy(&d);
658 dstr_destroy(&iv);
659 return (r);
660}
661
662/* --- Fibonacci generator --- */
663
664static grand *gen_fib(unsigned i)
665{
666 grand *r;
667 uint32 s = 0;
668 char *p;
669 unsigned set = 0;
670
671 static struct option opts[] = {
672 { "seed", OPTF_ARGREQ, 0, 's' },
673 { 0, 0, 0, 0 }
674 };
675
676 addopts("s:", opts);
677
678 for (;;) {
679 int o = opt();
680 if (o < 0)
681 break;
682 switch (o) {
683 case 's':
684 s = strtoul(optarg, &p, 0);
685 if (*p)
686 die(EXIT_FAILURE, "bad integer `%s'", optarg);
687 set = 1;
688 break;
689 default:
690 return (0);
691 }
692 }
693 r = fibrand_create(s);
694 if (!set)
695 r->ops->misc(r, GRAND_SEEDRAND, &rand_global);
696 return (r);
697}
698
699/* --- LC generator --- */
700
701static grand *gen_lc(unsigned i)
702{
703 uint32 s = 0;
704 char *p;
705 unsigned set = 0;
706
707 static struct option opts[] = {
708 { "seed", OPTF_ARGREQ, 0, 's' },
709 { 0, 0, 0, 0 }
710 };
711
712 addopts("s:", opts);
713
714 for (;;) {
715 int o = opt();
716 if (o < 0)
717 break;
718 switch (o) {
719 case 's':
720 s = strtoul(optarg, &p, 0);
721 if (*p)
722 die(EXIT_FAILURE, "bad integer `%s'", optarg);
723 set = 1;
724 break;
725 default:
726 return (0);
727 }
728 }
729 if (!set) {
730 do
731 s = rand_global.ops->range(&rand_global, LCRAND_P);
732 while (s == LCRAND_FIXEDPT);
733 }
734 return (lcrand_create(s));
735}
736
737/* --- Basic options parser -- can't generate output --- */
738
739static grand *gen_opts(unsigned i)
740{
741 while (opt() >= 0)
742 ;
743 return (0);
744}
745
746/*----- Generators table --------------------------------------------------*/
747
748static gen generators[] = {
749 { "fibonacci", gen_fib, 0,
750 "[-s SEED]" },
751 { "lc", gen_lc, 0,
752 "[-s SEED]" },
753 { "des-ofb", gen_ofb, OFB_DES,
754 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
755 { "3des-ofb", gen_ofb, OFB_DES3,
756 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
757 { "rc5-ofb", gen_ofb, OFB_RC5,
758 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
759 { "blowfish-ofb", gen_ofb, OFB_BLOWFISH,
760 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
761 { "idea-ofb", gen_ofb, OFB_IDEA,
762 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
763 { "rc4", gen_rc4, 0,
764 "[-k KEY-PHRASE] [-H HEX-KEY]" },
765 { "rand", gen_rand, 0,
766 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
767 { "bbs", gen_bbs, 0,
768 "[-gS] [-s SEED] [-m MODULUS] [-b BITS" },
769 { 0, 0, 0, 0 },
770};
771
772static gen optsg = { "options", gen_opts, 0,
773 "This message shouldn't be printed." };
774
775/*----- Main code ---------------------------------------------------------*/
776
777int main(int ac, char *av[])
778{
779 gen *g = &optsg;
780 grand *r;
781 unsigned percent = -1;
782 size_t kb = 0;
783 time_t last;
784 static char baton[] = "|/-\\";
785 char *bp;
786
787 /* --- Initialize mLib --- */
788
789 ego(av[0]);
790 sub_init();
791
792 /* --- Set up the main Catacomb generator --- */
793
794 rand_noisesrc(RAND_GLOBAL, &noise_source);
795
796 /* --- Initialize the options table --- */
797
798 addopts(sopts, opts);
799 argc = ac;
800 argv = av;
801
802 /* --- Read the generator out of the first argument --- */
803
804 if (argc > 1 && *argv[1] != '-') {
805 const char *arg = av[1];
806 size_t sz = strlen(arg);
807 gen *gg;
808
809 g = 0;
810 for (gg = generators; gg->name; gg++) {
811 if (strncmp(arg, gg->name, sz) == 0) {
812 if (gg->name[sz] == 0) {
813 g = gg;
814 break;
815 } else if (g)
816 die(EXIT_FAILURE, "ambiguous generator name `%s'", arg);
817 else
818 g = gg;
819 }
820 }
821 if (!g)
822 die(EXIT_FAILURE, "unknown generator name `%s'", arg);
823 argc--;
824 argv++;
825 }
826
827 /* --- Get a generic random number generator --- */
828
829 r = g->seed(g->i);
830 if (!r || optind != ac - 1) {
831 usage(stderr);
832 exit(EXIT_FAILURE);
833 }
834
835#ifndef PORTABLE
836 if (!(flags & f_file) && isatty(STDOUT_FILENO))
837 die(EXIT_FAILURE, "writing output to a terminal is a bad idea");
838#endif
839
840 /* --- Spit out random data --- */
841
842 last = time(0);
843 bp = baton;
844 if (flags & f_progress) {
845 char *errbuf = xmalloc(BUFSIZ);
846 setvbuf(stderr, errbuf, _IOLBF, BUFSIZ);
847 fputc('[', stderr);
848 fflush(stderr);
849 }
850
851#ifdef SIGPIPE
852 signal(SIGPIPE, SIG_IGN);
853#endif
854
855 for (;;) {
856 octet buf[BUFSIZ];
857 size_t sz = sizeof(buf);
858
859 /* --- Emit a bufferful (or less) of data --- */
860
861 if (outsz) {
862 if (sz > outsz - kb)
863 sz = outsz - kb;
864 }
865 r->ops->fill(r, buf, sz);
866 if (fwrite(buf, 1, sz, outfp) != sz) {
867 if (flags & f_progress)
868 fputc('\n', stderr);
869 die(EXIT_FAILURE, "error writing data: %s", strerror(errno));
870 }
871 kb += sz;
872
873 /* --- Update the display --- */
874
875 if (flags & f_progress) {
876 time_t t = time(0);
877 unsigned up = 0;
878
879 if (percent > 100)
880 up = 1;
881
882 if (!outsz) {
883 if (difftime(t, last) > 1.0) {
884 up = 1;
885 }
886 if (up)
887 fputs(" ] ", stderr);
888 } else {
889 unsigned pc = kb * 100.0 / outsz;
890 if (pc > percent || percent > 100 || difftime(t, last) > 1.0) {
891 if (percent > 100)
892 percent = 0;
893 percent &= ~1;
894 for (; percent < (pc & ~1); percent += 2)
895 putc('.', stderr);
896 percent = pc;
897 for (; pc < 100; pc += 2)
898 putc(' ', stderr);
899 fprintf(stderr, "] %3i%% ", percent);
900 up = 1;
901 }
902 }
903
904 if (up) {
905 size_t q = kb;
906 char *suff = " KMG";
907 while (q > 8192 && suff[1]) {
908 q >>= 10;
909 suff++;
910 }
911 fprintf(stderr, "%4i%c\r[", q, *suff);
912 if (outsz) {
913 unsigned pc;
914 for (pc = 0; pc < (percent & ~1); pc += 2)
915 putc('.', stderr);
916 }
917 last = t;
918 }
919
920 if (percent > 100)
921 percent = 0;
922
923 if (percent < 100) {
924 putc(*bp++, stderr);
925 putc('\b', stderr);
926 if (!*bp)
927 bp = baton;
928 }
929 fflush(stderr);
930 }
931
932 /* --- Terminate the loop --- */
933
934 if (outsz && kb >= outsz)
935 break;
936 }
937
938 /* --- Done --- */
939
940 r->ops->destroy(r);
941 if (flags & f_progress)
942 fputc('\n', stderr);
943 return (0);
944}
945
946/*----- That's all, folks -------------------------------------------------*/