3 * $Id: rspit.c,v 1.4 2000/06/17 12:08:28 mdw Exp $
5 * Spit out random numbers
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Catacomb.
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.
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.
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,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.4 2000/06/17 12:08:28 mdw
34 * Restructure handling of cipher-based generators. Add counter-mode
35 * ciphers and MGF-1 hash functions. Add FIPS 140-1 and Maurer's tests.
37 * Revision 1.3 2000/02/12 18:21:03 mdw
38 * Overhaul of key management (again).
40 * Revision 1.2 1999/12/22 15:59:51 mdw
41 * New prime-search system. Read BBS keys from key files.
43 * Revision 1.1 1999/12/10 23:29:13 mdw
44 * Emit random numbers for statistical tests.
48 /*----- Header files ------------------------------------------------------*/
65 #include <mLib/darray.h>
66 #include <mLib/dstr.h>
67 #include <mLib/mdwopt.h>
68 #include <mLib/quis.h>
69 #include <mLib/report.h>
92 #include "blowfish-ofb.h"
93 #include "twofish-ofb.h"
95 #include "cast128-ofb.h"
96 #include "cast256-ofb.h"
97 #include "rijndael-ofb.h"
98 #include "serpent-ofb.h"
100 #include "des-counter.h"
101 #include "des3-counter.h"
102 #include "rc2-counter.h"
103 #include "rc5-counter.h"
104 #include "blowfish-counter.h"
105 #include "twofish-counter.h"
106 #include "idea-counter.h"
107 #include "cast128-counter.h"
108 #include "cast256-counter.h"
109 #include "rijndael-counter.h"
110 #include "serpent-counter.h"
115 #include "rmd160-mgf.h"
119 /*----- Data structures ---------------------------------------------------*/
123 grand
*(*seed
)(unsigned /*i*/);
128 static gen generators
[];
135 E(BLOWFISH, blowfish) \
136 E(TWOFISH, twofish) \
138 E(CAST128, cast128) \
139 E(CAST256, cast256) \
140 E(RIJNDAEL, rijndael) \
149 #define E(PRE, pre) CIPHER_##PRE,
150 enum { CIPHERS CIPHER__bogus
};
153 #define E(PRE, pre) HASH_##PRE,
154 enum { HASHES HASH__bogus
};
160 grand
*(*ofb
)(const void */
*k*/
, size_t /*sz*/);
161 grand
*(*counter
)(const void */
*k*/
, size_t /*sz*/);
163 #define E(PRE, pre) \
164 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
172 grand
*(*mgf
)(const void */
*k*/
, size_t /*sz*/);
174 #define E(PRE, pre) \
175 { &pre, pre##_mgfkeysz, pre##_mgfrand },
180 /*----- Miscellaneous static data -----------------------------------------*/
182 static FILE *outfp
= stdout
;
183 static size_t outsz
= 0;
188 static unsigned flags
= 0;
197 /*----- Help options ------------------------------------------------------*/
199 static void usage(FILE *fp
)
201 pquis(fp
, "Usage: $ generator [options]\n");
204 static void version(FILE *fp
)
206 pquis(fp
, "$, Catacomb version " VERSION
"\n");
209 static void help(FILE *fp
)
215 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
216 The primary objective is to be able to generate streams of input for\n\
217 statistical tests, such as Diehard.\n\
219 Options are specific to the particular generator, although there's a\n\
222 -h, --help Display this help message.\n\
223 -v, --version Display the program's version number.\n\
224 -u, --usage Display a useless usage message.\n\
226 -l, --list Show a list of the supported generators, with\n\
228 -f, --fipstest Run the FIPS 140-1 randomness test.\n\
229 -m, --maurer Run Maurer's universal statistical test.\n\
230 -o, --output FILE Write output to FILE, not stdout.\n\
231 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
232 -p, --progress Show a little progress meter (on stderr).\n\
234 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
235 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
239 /*----- Main options parser -----------------------------------------------*/
241 static struct option opts
[] = {
243 /* --- Standard GNU help options --- */
245 { "help", 0, 0, 'h' },
246 { "version", 0, 0, 'v' },
247 { "usage", 0, 0, 'u' },
249 /* --- Other useful things --- */
251 { "list", 0, 0, 'l' },
252 { "fipstest", 0, 0, 'f' },
253 { "maurer", 0, 0, 'm' },
254 { "output", OPTF_ARGREQ
, 0, 'o' },
255 { "size", OPTF_ARGREQ
, 0, 'z' },
256 { "progress", 0, 0, 'p' },
258 /* --- End of main table --- */
263 static const char *sopts
= "hvu lfmo:z:p";
266 DA_DECL(option_v
, struct option
);
270 static option_v optv
= DA_INIT
;
271 static dstr optd
= DSTR_INIT
;
273 /* --- @addopts@ --- *
275 * Arguments: @const char *s@ = pointer to short options
276 * @struct option *l@ = pointer to long options
280 * Use: Adds a collection of options to the table.
283 static void addopts(const char *s
, struct option
*l
)
289 DA_PUSH(&optv
, *l
++);
297 * Returns: Next option from argument array.
299 * Use: Fetches options, handling the standard ones.
305 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
318 puts("Generators supported:");
319 for (g
= generators
; g
->name
; g
++)
320 printf(" %s %s\n", g
->name
, g
->help
);
331 die(EXIT_FAILURE
, "already set an output file");
332 if (strcmp(optarg
, "-") == 0)
335 outfp
= fopen(optarg
, "w");
337 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
338 optarg
, strerror(errno
));
345 outsz
= strtoul(optarg
, &p
, 0);
347 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
349 case 'G': case 'g': outsz
*= 1024;
350 case 'M': case 'm': outsz
*= 1024;
351 case 'K': case 'k': outsz
*= 1024;
355 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
359 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
370 /*----- Manglers for seed strings -----------------------------------------*/
374 * Arguments: @const char *p@ = pointer to input string
375 * @char **end@ = where the end goes
376 * @dstr *d@ = output buffer
380 * Use: Transforms a hex string into a chunk of binary data.
383 static void unhex(const char *p
, char **end
, dstr
*d
)
385 while (p
[0] && p
[1]) {
386 int x
= p
[0], y
= p
[1];
387 if ('0' <= x
&& x
<= '9') x
-= '0';
388 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
389 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
391 if ('0' <= y
&& y
<= '9') y
-= '0';
392 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
393 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
395 DPUTC(d
, (x
<< 4) + y
);
401 /* --- Generate a key --- */
403 static void textkey(dstr
*d
, const char *p
, const octet
*ksz
)
405 size_t sz
= strlen(p
);
408 die(EXIT_FAILURE
, "zero-length key string");
409 if (keysz(sz
, ksz
) != sz
)
413 rmd160_mgfinit(&g
, p
, sz
);
416 rmd160_mgfencrypt(&g
, 0, d
->buf
, sz
);
419 assert(((void)"I can't seem to choose a good key size",
420 keysz(d
->len
, ksz
) == d
->len
));
423 static void hexkey(dstr
*d
, const char *p
, const octet
*ksz
)
426 unhex(optarg
, &q
, d
);
428 die(EXIT_FAILURE
, "bad hex key `%s'", p
);
429 if (keysz(d
->len
, ksz
) != d
->len
)
430 die(EXIT_FAILURE
, "bad key length");
433 static void randkey(dstr
*d
, const octet
*ksz
)
435 size_t sz
= keysz(0, ksz
);
437 rand_get(RAND_GLOBAL
, d
->buf
, sz
);
441 /*----- Generators --------------------------------------------------------*/
443 /* --- Blum-Blum-Shub strong generator --- */
445 static grand
*gen_bbs(unsigned i
)
447 /* --- Default modulus --- *
449 * The factors of this number are
451 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
452 * @7754313537966036459299022912838407755462506416274551744201653277@
453 * @313130311731673973886822067@
455 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
456 * @5374320073594018817245784145742769603334292182227671519041431067@
457 * @61344781426317516045890159@
459 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
460 * common factors. They were found using this program, with random
463 * I hope that, by publishing these factors, I'll dissuade people from
464 * actually using this modulus in an attempt to attain real security. The
465 * program is quite quick at finding Blum numbers, so there's no excuse for
466 * not generating your own.
470 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
472 /* --- Other things --- */
476 unsigned bits
= 1024;
479 const char *kfile
= 0, *id
= 0, *ktype
= 0;
481 /* --- Parse options --- */
483 static struct option opts
[] = {
484 { "modulus", OPTF_ARGREQ
, 0, 'M' },
485 { "generate", 0, 0, 'g' },
486 { "seed", OPTF_ARGREQ
, 0, 's' },
487 { "bits", OPTF_ARGREQ
, 0, 'b' },
488 { "show", 0, 0, 'S' },
489 { "keyring", OPTF_ARGREQ
, 0, 'k' },
490 { "id", OPTF_ARGREQ
, 0, 'i' },
491 { "type", OPTF_ARGREQ
, 0, 't' },
495 addopts("M:gs:b:Sk:i:t:", opts
);
512 bits
= strtoul(optarg
, 0, 0);
514 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
536 /* --- Generate a modulus if one is requested --- */
540 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
541 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
542 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
543 /* Unfortunately I don't know how to test for a Blum integer */
544 } else if (kfile
|| id
|| ktype
) {
549 /* --- Open the key file --- */
553 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
554 die(EXIT_FAILURE
, "error opening key file `%s': %s",
555 kfile
, strerror(errno
));
558 /* --- Find the key --- */
561 if ((kk
= key_bytag(&kf
, id
)) == 0)
562 die(EXIT_FAILURE
, "key `%s' not found", id
);
566 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
567 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
570 /* --- Read the key data --- */
572 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
573 die(EXIT_FAILURE
, "key is not structured");
574 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
575 die(EXIT_FAILURE
, "key has no subkey `n'");
576 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
577 die(EXIT_FAILURE
, "incomatible subkey encoding");
578 m
= MP_COPY(kd
->u
.m
);
583 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
584 (flags
& f_progress
) ? pgen_ev
: 0, 0))
585 die(EXIT_FAILURE
, "modulus generation failed");
589 fputs("p = ", stderr
);
590 mp_writefile(bp
.p
, stderr
, 10);
591 fputs("\nq = ", stderr
);
592 mp_writefile(bp
.q
, stderr
, 10);
593 fputs("\nn = ", stderr
);
594 mp_writefile(bp
.n
, stderr
, 10);
602 /* --- Set up a seed --- */
605 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
608 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
610 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
622 /* --- Catacomb's random number generator --- */
624 static grand
*gen_rand(unsigned i
)
626 grand
*r
= rand_create();
629 static struct option opts
[] = {
630 { "key", OPTF_ARGREQ
, 0, 'k' },
631 { "text", OPTF_ARGREQ
, 0, 't' },
632 { "hex", OPTF_ARGREQ
, 0, 'H' },
636 addopts("k:t:H:n", opts
);
638 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
639 r
->ops
->misc(r
, RAND_SEED
, 160);
648 textkey(&d
, optarg
, rmd160_mackeysz
);
649 r
->ops
->misc(r
, RAND_KEY
, d
.buf
, d
.len
);
652 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
656 hexkey(&d
, optarg
, rmd160_mackeysz
);
657 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
666 /* --- RC4 output --- */
668 static grand
*gen_rc4(unsigned i
)
673 static struct option opts
[] = {
674 { "key", OPTF_ARGREQ
, 0, 'k' },
675 { "hex", OPTF_ARGREQ
, 0, 'H' },
679 addopts("k:H:", opts
);
688 textkey(&d
, optarg
, rc4_keysz
);
692 hexkey(&d
, optarg
, rc4_keysz
);
700 randkey(&d
, rc4_keysz
);
701 r
= rc4_rand(d
.buf
, d
.len
);
706 /* --- SEAL output --- */
708 static grand
*gen_seal(unsigned i
)
714 static struct option opts
[] = {
715 { "key", OPTF_ARGREQ
, 0, 'k' },
716 { "hex", OPTF_ARGREQ
, 0, 'H' },
717 { "sequence", OPTF_ARGREQ
, 0, 'n' },
721 addopts("k:H:n:", opts
);
730 textkey(&d
, optarg
, seal_keysz
);
734 hexkey(&d
, optarg
, seal_keysz
);
738 n
= strtoul(optarg
, &p
, 0);
740 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
748 randkey(&d
, seal_keysz
);
749 r
= seal_rand(d
.buf
, d
.len
, n
);
754 /* --- Output feedback generators --- */
756 static grand
*gen_ofb(unsigned i
)
762 static struct option opts
[] = {
763 { "key", OPTF_ARGREQ
, 0, 'k' },
764 { "hex", OPTF_ARGREQ
, 0, 'H' },
765 { "iv", OPTF_ARGREQ
, 0, 'i' },
769 addopts("k:H:i:", opts
);
778 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
782 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
786 unhex(optarg
, &p
, &iv
);
788 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
796 randkey(&d
, ciphertab
[i
].keysz
);
797 r
= ciphertab
[i
].ofb(d
.buf
, d
.len
);
799 if (iv
.len
!= ciphertab
[i
].blksz
) {
800 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
801 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
803 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
811 /* --- Counter generators --- */
813 static grand
*gen_counter(unsigned i
)
819 static struct option opts
[] = {
820 { "key", OPTF_ARGREQ
, 0, 'k' },
821 { "hex", OPTF_ARGREQ
, 0, 'H' },
822 { "iv", OPTF_ARGREQ
, 0, 'i' },
826 addopts("k:H:i:", opts
);
835 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
839 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
843 unhex(optarg
, &p
, &iv
);
845 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
853 randkey(&d
, ciphertab
[i
].keysz
);
854 r
= ciphertab
[i
].counter(d
.buf
, d
.len
);
856 if (iv
.len
!= ciphertab
[i
].blksz
) {
857 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
858 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
860 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
868 /* --- Mask generators --- */
870 static grand
*gen_mgf(unsigned i
)
876 static struct option opts
[] = {
877 { "key", OPTF_ARGREQ
, 0, 'k' },
878 { "hex", OPTF_ARGREQ
, 0, 'H' },
879 { "index", OPTF_ARGREQ
, 0, 'i' },
883 addopts("k:H:i:", opts
);
892 textkey(&d
, optarg
, hashtab
[i
].keysz
);
896 hexkey(&d
, optarg
, hashtab
[i
].keysz
);
900 c
= strtoul(optarg
, &p
, 0);
902 die(EXIT_FAILURE
, "bad index `%s'", optarg
);
910 randkey(&d
, hashtab
[i
].keysz
);
912 r
= hashtab
[i
].mgf(d
.buf
, d
.len
);
914 r
->ops
->misc(r
, GRAND_SEEDUINT32
, c
);
920 /* --- Fibonacci generator --- */
922 static grand
*gen_fib(unsigned i
)
929 static struct option opts
[] = {
930 { "seed", OPTF_ARGREQ
, 0, 's' },
942 s
= strtoul(optarg
, &p
, 0);
944 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
951 r
= fibrand_create(s
);
953 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
957 /* --- LC generator --- */
959 static grand
*gen_lc(unsigned i
)
965 static struct option opts
[] = {
966 { "seed", OPTF_ARGREQ
, 0, 's' },
978 s
= strtoul(optarg
, &p
, 0);
980 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
989 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
990 while (s
== LCRAND_FIXEDPT
);
992 return (lcrand_create(s
));
995 /* --- Basic options parser -- can't generate output --- */
997 static grand
*gen_opts(unsigned i
)
1004 /*----- Generators table --------------------------------------------------*/
1006 static gen generators
[] = {
1007 { "fibonacci", gen_fib
, 0,
1011 #define E(PRE, pre) \
1012 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
1013 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1016 #define E(PRE, pre) \
1017 { #pre "-counter", gen_counter, CIPHER_##PRE, \
1018 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1021 #define E(PRE, pre) \
1022 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1023 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1026 { "rc4", gen_rc4
, 0,
1027 "[-k KEY-PHRASE] [-H HEX-KEY]" },
1028 { "seal", gen_seal
, 0,
1029 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
1030 { "rand", gen_rand
, 0,
1031 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1032 { "bbs", gen_bbs
, 0,
1033 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
1038 static gen optsg
= { "options", gen_opts
, 0,
1039 "This message shouldn't be printed." };
1041 /*----- Main code ---------------------------------------------------------*/
1043 int main(int ac
, char *av
[])
1047 unsigned percent
= 0;
1050 static char baton
[] = "-\\|/";
1053 /* --- Initialize mLib --- */
1058 /* --- Set up the main Catacomb generator --- */
1060 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1061 rand_seed(RAND_GLOBAL
, 160);
1063 /* --- Initialize the options table --- */
1065 addopts(sopts
, opts
);
1069 /* --- Read the generator out of the first argument --- */
1071 if (argc
> 1 && *argv
[1] != '-') {
1072 const char *arg
= av
[1];
1073 size_t sz
= strlen(arg
);
1077 for (gg
= generators
; gg
->name
; gg
++) {
1078 if (strncmp(arg
, gg
->name
, sz
) == 0) {
1079 if (gg
->name
[sz
] == 0) {
1083 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
1089 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
1094 /* --- Get a generic random number generator --- */
1097 if (!r
|| optind
!= ac
- 1) {
1102 /* --- Do the FIPS test --- */
1104 if (flags
& f_fips
) {
1105 octet buf
[FIPSTEST_BUFSZ
];
1108 r
->ops
->fill(r
, buf
, sizeof(buf
));
1110 if (rc
& FIPSTEST_MONOBIT
)
1111 moan("failed monobit test");
1112 if (rc
& FIPSTEST_POKER
)
1113 moan("failed poker test");
1114 if (rc
& FIPSTEST_RUNS
)
1115 moan("failed runs test");
1116 if (rc
& FIPSTEST_LONGRUNS
)
1117 moan("failed long runs test");
1118 if (!rc
&& (flags
& f_progress
))
1119 puts("test passed");
1120 return (rc ? EXIT_FAILURE
: 0);
1123 /* --- Do Maurer's test --- */
1125 if (flags
& f_maurer
) {
1126 octet buf
[250 * 1024];
1129 unsigned f
= 0, jj
= 0;
1132 static struct { double x
; const char *sig
; } sigtab
[] = {
1140 r
->ops
->fill(r
, buf
, sizeof(buf
));
1141 for (i
= 5; i
< 8; i
++) {
1142 double z
= maurer(buf
, sizeof(buf
), i
+ 1);
1143 double zz
= fabs(z
);
1146 for (j
= 0; sigtab
[j
].sig
; j
++) {
1147 if (zz
> sigtab
[j
].x
) {
1148 if (zz
> fabs(maxz
)) {
1154 moan("failed, bits = %u, sig = %s, Z_u = %g",
1155 i
+ 1, sigtab
[j
].sig
, z
);
1159 if (flags
& f_progress
)
1160 printf("bits = %u, Z_u = %g\n", i
+ 1, z
);
1166 /* --- Make sure we don't write to the terminal --- */
1169 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
1170 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
1173 /* --- Spit out random data --- */
1177 if (flags
& f_progress
) {
1178 char *errbuf
= xmalloc(BUFSIZ
);
1179 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
1181 fprintf(stderr
, "[%*s] 0%% 0\r[/\b", 50, "");
1183 fputs("[ ] 0\r[/\b", stderr
);
1188 signal(SIGPIPE
, SIG_IGN
);
1193 size_t sz
= sizeof(buf
);
1195 /* --- Emit a bufferful (or less) of data --- */
1198 if (sz
> outsz
- kb
)
1201 r
->ops
->fill(r
, buf
, sz
);
1202 if (fwrite(buf
, 1, sz
, outfp
) != sz
) {
1203 if (flags
& f_progress
)
1204 fputc('\n', stderr
);
1205 die(EXIT_FAILURE
, "error writing data: %s", strerror(errno
));
1209 /* --- Update the display --- */
1211 if (flags
& f_progress
) {
1219 if (difftime(t
, last
) > 1.0) {
1223 fputs(" ] ", stderr
);
1225 unsigned pc
= kb
* 100.0 / outsz
;
1226 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
1230 for (; percent
< (pc
& ~1); percent
+= 2)
1233 for (; pc
< 100; pc
+= 2)
1235 fprintf(stderr
, "] %3i%% ", percent
);
1242 char *suff
= " KMG";
1243 while (q
> 8192 && suff
[1]) {
1247 fprintf(stderr
, "%4i%c\r[", q
, *suff
);
1250 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
1259 if (percent
< 100) {
1260 putc(*bp
++, stderr
);
1268 /* --- Terminate the loop --- */
1270 if (outsz
&& kb
>= outsz
)
1277 if (flags
& f_progress
)
1278 fputc('\n', stderr
);
1282 /*----- That's all, folks -------------------------------------------------*/