chiark / gitweb /
Emit random numbers for statistical tests.
[catacomb] / rspit.c
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
82 typedef struct gen {
83   const char *name;
84   grand *(*seed)(unsigned /*i*/);
85   unsigned i;
86   const char *help;
87 } gen;
88
89 static gen generators[];
90
91 /*----- Miscellaneous static data -----------------------------------------*/
92
93 static FILE *outfp = stdout;
94 static size_t outsz = 0;
95
96 static int argc;
97 static char **argv;
98
99 static unsigned flags = 0;
100
101 enum {
102   f_progress = 1,
103   f_file = 2
104 };
105
106 /*----- Help options ------------------------------------------------------*/
107
108 static void usage(FILE *fp)
109 {
110   pquis(fp, "Usage: $ generator [options]\n");
111 }
112
113 static void version(FILE *fp)
114 {
115   pquis(fp, "$, Catacomb version " VERSION "\n");
116 }
117
118 static void help(FILE *fp)
119 {
120   version(fp);
121   fputc('\n', fp);
122   usage(fp);
123   pquis(fp, "\n\
124 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
125 The primary objective is to be able to generate streams of input for\n\
126 statistical tests, such as Diehard.\n\
127 \n\
128 Options are specific to the particular generator, although there's a\n\
129 common 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
148 static 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
168 static 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
175 static option_v optv = DA_INIT;
176 static 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
188 static 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
207 static 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
282 static 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
304 static 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
334 static 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
466 static 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
517 static 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
573 static 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
583 enum {
584 #define E(c, x, y, z) c
585   OFBTAB
586 #undef E
587 };
588
589 #undef OFBTAB
590
591 static 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
664 static 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
701 static 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
739 static grand *gen_opts(unsigned i)
740 {
741   while (opt() >= 0)
742     ;
743   return (0);
744 }
745
746 /*----- Generators table --------------------------------------------------*/
747
748 static 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
772 static gen optsg = { "options", gen_opts, 0,
773                      "This message shouldn't be printed." };
774
775 /*----- Main code ---------------------------------------------------------*/
776
777 int 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 -------------------------------------------------*/