From cd6eca4375f46a35b93e2fea4b0428a23b451aa3 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sun, 22 Jan 2012 13:12:14 +0000 Subject: [PATCH] Catcrypt tools: Roll out progress indicator stuff from hashsum. Factor out the progress indication from hashsum, and introduce it into the other tools. --- Makefile.m4 | 2 +- catcrypt.1 | 18 ++++- catcrypt.c | 106 ++++++++++++++++++------- catsign.1 | 26 ++++++- catsign.c | 131 ++++++++++++++++++++++--------- cc-enc.c | 81 +++++++++++++------ cc-kem.c | 2 + cc-list.c | 2 + cc-progress.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cc-sig.c | 2 + cc-subcmd.c | 2 + cc.h | 73 +++++++++++++++++- cookie.c | 2 + dsig.1 | 10 ++- dsig.c | 38 +++++++-- hashsum.c | 95 +---------------------- keyutil.c | 2 + perftest.c | 2 + 18 files changed, 641 insertions(+), 197 deletions(-) create mode 100644 cc-progress.c diff --git a/Makefile.m4 b/Makefile.m4 index 6312e98..1ef5632 100644 --- a/Makefile.m4 +++ b/Makefile.m4 @@ -287,7 +287,7 @@ noinst_PROGRAMS = \ LDADD = libcatcrypt.a libcatacomb.la define(`LIBCAT_SRC', `cc.h getdate.h dnl - cc-sig.c cc-subcmd.c cc-enc.c cc-kem.c cc-list.c') + cc-sig.c cc-subcmd.c cc-enc.c cc-kem.c cc-list.c cc-progress.c') libcatcrypt_a_SOURCES = LIBCAT_SRC getdate.y patsubst(MP_BASE MP_SOURCES, `\.c\>', `.lo') dsig.o keyutil.o rspit.o \ diff --git a/catcrypt.1 b/catcrypt.1 index 72bfe12..6bace86 100644 --- a/catcrypt.1 +++ b/catcrypt.1 @@ -44,7 +44,7 @@ is one of: .RI [ item ...] .br .B encrypt -.RB [ \-aC ] +.RB [ \-apC ] .RB [ \-k .IR tag ] .RB [ \-f @@ -54,7 +54,7 @@ is one of: .RI [ file ] .br .B decrypt -.RB [ \-aqvC ] +.RB [ \-apqvC ] .RB [ \-f .IR format ] .RB [ \-o @@ -62,6 +62,7 @@ is one of: .RI [ file ] .br .B encode +.RB [ \-p ] .RB [ \-f .IR format ] .RB [ \-b @@ -71,6 +72,7 @@ is one of: .RI [ file ] .br .B decode +.RB [ \-p ] .RB [ \-f .IR format ] .RB [ \-b @@ -486,6 +488,9 @@ Use the key-encapsulation key named in the current keyring; the default key is .BR ccrypt . .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .BI "\-s, \-\-sign-key " tag Use the signature key named .I tag @@ -523,6 +528,9 @@ on if output is to stdout, but is always available as an option. Read input encoded according to .IR format . .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .B "\-v, \-\-verbose" Produce more verbose messages. See below for the messages produced during decryption. The default verbosity level is 1. (Currently this @@ -593,6 +601,9 @@ The input is read from the given on the command line, or from standard input if none is specified. Options provided are: .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .BI "\-f, \-\-format " format Produce output in .IR format . @@ -647,6 +658,9 @@ lines. Without this option, will start reading at the first plausible boundary string, and continue processing until it reaches the matching end boundary. .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .BI "\-o, \-\-output " file Write output to .I file diff --git a/catcrypt.c b/catcrypt.c index d10739d..08fb7e0 100644 --- a/catcrypt.c +++ b/catcrypt.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include @@ -126,10 +128,11 @@ err: static int encrypt(int argc, char *argv[]) { - const char *of = 0, *kn = "ccrypt", *skn = 0; + const char *fn, *of = 0, *kn = "ccrypt", *skn = 0; FILE *ofp = 0; FILE *fp = 0; const char *ef = "binary"; + fprogress ff; const char *err; int i; int en; @@ -153,6 +156,7 @@ static int encrypt(int argc, char *argv[]) #define f_bogus 1u #define f_nocheck 2u +#define f_progress 4u for (;;) { static const struct option opt[] = { @@ -162,10 +166,11 @@ static int encrypt(int argc, char *argv[]) { "armor", 0, 0, 'a' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { "nocheck", 0, 0, 'C' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "k:s:af:o:C", opt, 0, 0, 0); + i = mdwopt(argc, argv, "k:s:af:o:pC", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'k': kn = optarg; break; @@ -173,6 +178,7 @@ static int encrypt(int argc, char *argv[]) case 'a': ef = "pem"; break; case 'f': ef = optarg; break; case 'o': of = optarg; break; + case 'p': f |= f_progress; break; case 'C': f |= f_nocheck; break; default: f |= f_bogus; break; } @@ -190,16 +196,13 @@ static int encrypt(int argc, char *argv[]) if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); - if (optind == argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], "rb")) == 0) { + else if ((fp = fopen(fn, "rb")) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } if (!of || strcmp(of, "-") == 0) ofp = stdout; @@ -258,6 +261,13 @@ static int encrypt(int argc, char *argv[]) /* --- Now do the main crypto --- */ + if (f & f_progress) { + if (fprogress_init(&ff, fn, fp)) { + die(EXIT_FAILURE, "failed to initialize progress display: %s", + strerror(errno)); + } + } + assert(GC_CLASS(c)->blksz <= sizeof(bb)); dstr_ensure(&d, sizeof(bb) + GM_CLASS(m)->hashsz); seq = 0; @@ -272,6 +282,7 @@ static int encrypt(int argc, char *argv[]) } n = fread(bb, 1, chsz, fp); if (!n) break; + if (f & f_progress) fprogress_update(&ff, n); buf_init(&b, d.buf, d.sz); tag = buf_get(&b, GM_CLASS(m)->hashsz); ct = buf_get(&b, n); @@ -294,6 +305,7 @@ static int encrypt(int argc, char *argv[]) /* --- All done --- */ + if (f & f_progress) fprogress_done(&ff); e->ops->encdone(e); GM_DESTROY(m); GC_DESTROY(c); @@ -309,16 +321,18 @@ static int encrypt(int argc, char *argv[]) #undef f_bogus #undef f_nocheck +#undef f_progress } /*---- Decryption ---------------------------------------------------------*/ static int decrypt(int argc, char *argv[]) { - const char *of = 0; + const char *fn, *of = 0; FILE *ofp = 0, *rfp = 0; FILE *fp = 0; const char *ef = "binary"; + fprogress ff; int i; size_t n; dstr d = DSTR_INIT; @@ -344,6 +358,7 @@ static int decrypt(int argc, char *argv[]) #define f_bogus 1u #define f_buffer 2u #define f_nocheck 4u +#define f_progress 8u for (;;) { static const struct option opt[] = { @@ -355,9 +370,10 @@ static int decrypt(int argc, char *argv[]) { "nocheck", 0, 0, 'C' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "abf:o:qvC", opt, 0, 0, 0); + i = mdwopt(argc, argv, "abf:o:pqvC", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'a': ef = "pem"; break; @@ -367,6 +383,7 @@ static int decrypt(int argc, char *argv[]) case 'C': f |= f_nocheck; break; case 'f': ef = optarg; break; case 'o': of = optarg; break; + case 'p': f |= f_progress; break; default: f |= f_bogus; break; } } @@ -376,44 +393,55 @@ static int decrypt(int argc, char *argv[]) if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); - if (optind == argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) { + else if ((fp = fopen(fn, eo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0)) die(EXIT_FAILURE, "can't open keyring `%s'", keyring); e = initdec(eo, fp, checkbdry, "CATCRYPT ENCRYPTED MESSAGE"); + if (f & f_progress) { + if (fprogress_init(&ff, fn, fp)) { + die(EXIT_FAILURE, "failed to initialize progress display: %s", + strerror(errno)); + } + } + /* --- Read the header chunk --- */ chunk_read(e, &d, &b); + if (f & f_progress) + fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw); if (buf_getu32(&b, &id)) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL malformed header: missing keyid\n"); exit(EXIT_FAILURE); } if ((k = key_byid(&kf, id)) == 0) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL key id %08lx not found\n", (unsigned long)id); exit(EXIT_FAILURE); } if (BLEFT(&b)) { if (buf_getu32(&b, &id)) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL malformed header: missing signature keyid\n"); exit(EXIT_FAILURE); } if ((sk = key_byid(&kf, id)) == 0) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL key id %08lx not found\n", (unsigned long)id); exit(EXIT_FAILURE); } } if (BLEFT(&b)) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL malformed header: junk at end\n"); exit(EXIT_FAILURE); } @@ -434,7 +462,10 @@ static int decrypt(int argc, char *argv[]) /* --- Read the KEM chunk --- */ chunk_read(e, &d, &b); + if (f & f_progress) + fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw); if (setupkem(km, &d, &cx, &c, &m)) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL failed to decapsulate key\n"); exit(EXIT_FAILURE); } @@ -448,18 +479,24 @@ static int decrypt(int argc, char *argv[]) GC_ENCRYPT(cx, 0, d.buf, 1024); GH_HASH(s->h, d.buf, 1024); chunk_read(e, &d, &b); + if (f & f_progress) + fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw); if (s->ops->doit(s, &d)) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL signature verification failed\n"); exit(EXIT_FAILURE); } if (verb) { dstr_reset(&d); key_fulltag(sk, &d); + if (f & f_progress) fprogress_clear(&ff); printf("INFO good-signature %s\n", d.buf); } freesig(s); - } else if (verb) + } else if (verb) { + if (f & f_progress) fprogress_clear(&ff); printf("INFO no-signature\n"); + } /* --- Now decrypt the main body --- */ @@ -469,12 +506,15 @@ static int decrypt(int argc, char *argv[]) } if (!(f & f_buffer)) { if ((ofp = fopen(of, "wb")) == 0) { + if (f & f_progress) fprogress_done(&ff); die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", ofp, strerror(errno)); } rfp = ofp; - } else if ((rfp = tmpfile()) == 0) + } else if ((rfp = tmpfile()) == 0) { + if (f & f_progress) fprogress_done(&ff); die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno)); + } seq = 0; dstr_ensure(&d, GC_CLASS(c)->blksz); @@ -488,12 +528,16 @@ static int decrypt(int argc, char *argv[]) GH_HASHU32(h, seq); seq++; chunk_read(e, &d, &b); + if (f & f_progress) + fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw); if ((tag = buf_get(&b, GM_CLASS(m)->hashsz)) == 0) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL bad ciphertext chunk: no tag\n"); exit(EXIT_FAILURE); } GH_HASH(h, BCUR(&b), BLEFT(&b)); if (memcmp(tag, GH_DONE(h, 0), GM_CLASS(m)->hashsz) != 0) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL bad ciphertext chunk: authentication failure\n"); exit(EXIT_FAILURE); @@ -503,11 +547,13 @@ static int decrypt(int argc, char *argv[]) break; GC_DECRYPT(c, BCUR(&b), BCUR(&b), BLEFT(&b)); if (fwrite(BCUR(&b), 1, BLEFT(&b), rfp) != BLEFT(&b)) { + if (f & f_progress) fprogress_done(&ff); if (verb) printf("FAIL error writing output: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } + if (f & f_progress) fprogress_done(&ff); if (fflush(rfp) || ferror(rfp)) { if (verb) printf("FAIL error writing output: %s\n", strerror(errno)); exit(EXIT_FAILURE); @@ -518,15 +564,20 @@ static int decrypt(int argc, char *argv[]) of, strerror(errno)); } rewind(rfp); + if (f & f_progress) fprogress_init(&ff, "copying buffer", rfp); dstr_reset(&d); dstr_ensure(&d, 65536); if (verb && ofp == stdout) printf("DATA\n"); for (;;) { n = fread(d.buf, 1, d.sz, rfp); if (!n) break; - if (fwrite(d.buf, 1, n, ofp) < n) + if (f & f_progress) fprogress_update(&ff, n); + if (fwrite(d.buf, 1, n, ofp) < n) { + if (f & f_progress) fprogress_done(&ff); die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } } + if (f & f_progress) fprogress_done(&ff); if (ferror(rfp) || fclose(rfp)) die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno)); } @@ -549,6 +600,7 @@ static int decrypt(int argc, char *argv[]) #undef f_bogus #undef f_buffer #undef f_nocheck +#undef f_progress } /*----- Main code ---------------------------------------------------------*/ @@ -584,7 +636,7 @@ static cmd cmdtab[] = { CMD_ENCODE, CMD_DECODE, { "encrypt", encrypt, - "encrypt [-aC] [-k TAG] [-s TAG] [-f FORMAT]\n\t\ + "encrypt [-apC] [-k TAG] [-s TAG] [-f FORMAT]\n\t\ [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ @@ -593,20 +645,22 @@ Options:\n\ -k, --key=TAG Use public encryption key named by TAG.\n\ -s, --sign-key=TAG Use private signature key named by TAG.\n\ -o, --output=FILE Write output to FILE.\n\ +-p, --progress Show progress on large files.\n\ -C, --nocheck Don't check the public key.\n\ " }, { "decrypt", decrypt, - "decrypt [-abqvC] [-f FORMAT] [-o OUTPUT] [FILE]", "\ + "decrypt [-abpqvC] [-f FORMAT] [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ -a, --armour Same as `-f pem'.\n\ -b, --buffer Buffer output until we're sure we have it all.\n\ -f, --format=FORMAT Decode as FORMAT.\n\ -o, --output=FILE Write output to FILE.\n\ +-p, --progress Show progress on large files.\n\ -q, --quiet Produce fewer messages.\n\ -v, --verbose Produce more verbose messages.\n\ -C, --nocheck Don't check the private key.\n\ -" }, /* ' emacs is confused */ +" }, { 0, 0, 0 } }; diff --git a/catsign.1 b/catsign.1 index 93fe70d..190c631 100644 --- a/catsign.1 +++ b/catsign.1 @@ -44,7 +44,7 @@ is one of: .RI [ item ...] .br .B sign -.RB [ \-adtC ] +.RB [ \-adptC ] .RB [ \-k .IR tag ] .RB [ \-f @@ -54,7 +54,7 @@ is one of: .RI [ file ] .br .B verify -.RB [ \-aquvC ] +.RB [ \-apquvC ] .RB [ \-k .IR tag ] .RB [ \-f @@ -75,7 +75,7 @@ is one of: .RI [ file ] .br .B format -.RB [ \-auABDET ] +.RB [ \-apuABDET ] .RB [ \-f .IR format ] .RB [ \-F @@ -90,6 +90,7 @@ is one of: .RI [ message ]] .br .B encode +.RB [ \-p ] .RB [ \-f .IR format ] .RB [ \-b @@ -99,6 +100,7 @@ is one of: .RI [ file ] .br .B decode +.RB [ \-p ] .RB [ \-f .IR format ] .RB [ \-b @@ -416,6 +418,9 @@ Write output to .I file rather than to standard output. .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .B "\-t, \-\-text" Read and sign the input as text. This is the default. .TP @@ -459,6 +464,9 @@ Produce more verbose messages. See below for the messages produced during decryption. The default verbosity level is 1. (Currently this is the most verbose setting. This might not be the case always.) .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .B "\-q, \-\-quiet" Produce fewer messages. .TP @@ -564,6 +572,9 @@ is also accepted. Read input encoded according to .IR format . .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .B "\-u, \-\-utc" Show the datestamp in the signature in UTC rather than (your) local time. The synonym @@ -641,6 +652,9 @@ The variant spelling .B "\-\-armor" is also accepted. .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .BI "\-A, \-\-armour-out" Produce ASCII-armoured output. This is equivalent to specifying .BR "\-F pem" . @@ -712,6 +726,9 @@ at the bottom. The default is .BR MESSAGE . .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .BI "\-o, \-\-output " file Write output to .I file @@ -747,6 +764,9 @@ lines. Without this option, will start reading at the first plausible boundary string, and continue processing until it reaches the matching end boundary. .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .BI "\-o, \-\-output " file Write output to .I file diff --git a/catsign.c b/catsign.c index 8171404..3bbd7b2 100644 --- a/catsign.c +++ b/catsign.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include @@ -92,6 +94,7 @@ typedef struct sigmsg { #define F_BUFFER 256u #define F_UTC 512u #define F_NOCHECK 1024u +#define F_PROGRESS 2048u /*----- Chunk I/O ---------------------------------------------------------*/ @@ -132,6 +135,7 @@ typedef struct msgcanon { unsigned f; FILE *fp; enc *e; + fprogress ff; size_t (*read)(struct msgcanon *, void *); void (*write)(struct msgcanon *, const void *, size_t); void (*close)(struct msgcanon *); @@ -176,6 +180,7 @@ full: ungetc(ch, m->fp); done: m->f = f; + if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); return (n); } @@ -197,9 +202,18 @@ static void textwrite(msgcanon *m, const void *bp, size_t n) } static size_t binreadembed(msgcanon *m, void *bp) - { return (chunk_read(m->e, bp)); } +{ + size_t n = chunk_read(m->e, bp); + if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); + return (n); +} + static size_t binreaddetach(msgcanon *m, void *bp) - { return (fread(bp, 1, MSGBUFSZ - 1, m->fp)); } +{ + size_t n = fread(bp, 1, MSGBUFSZ - 1, m->fp); + if (m->f & F_PROGRESS) fprogress_update(&m->ff, n); + return (n); +} static void binwriteembed(msgcanon *m, const void *bp, size_t n) { chunk_write(m->e, bp, n); } @@ -208,18 +222,25 @@ static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; } static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn) { - m->f = F_DETACH | (f & F_BINARY); + m->f = F_DETACH | (f & (F_BINARY | F_PROGRESS)); if (!fn || strcmp(fn, "-") == 0) { m->fp = stdin; m->f |= F_NOCLOSE; } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0) die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno)); + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, fn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } m->read = (f & F_BINARY) ? binreaddetach : textread; } static void mcsetup_writenull(msgcanon *m) { m->write = nullwrite; } -static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) +static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, + const char *fn, const char *dfn) { enc *e = *ee; @@ -232,6 +253,12 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) *ee = 0; m->fp = e->fp; m->read = binreadembed; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, fn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } } else { m->read = binreaddetach; if (!dfn || strcmp(dfn, "-") == 0) @@ -240,6 +267,12 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno)); else m->f &= ~F_NOCLOSE; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, dfn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } } } else { m->read = textread; @@ -249,12 +282,26 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn) m->fp = e->fp; e->ops->destroy(e); *ee = 0; - } else if (!dfn || strcmp(dfn, "-") == 0) - m->fp = stdin; - else if ((m->fp = fopen(dfn, "r")) == 0) - die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno)); - else - m->f &= ~F_NOCLOSE; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, fn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } + } else { + if (!dfn || strcmp(dfn, "-") == 0) + m->fp = stdin; + else if ((m->fp = fopen(dfn, "r")) == 0) + die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno)); + else + m->f &= ~F_NOCLOSE; + if (m->f & F_PROGRESS) { + if (fprogress_init(&m->ff, dfn, m->fp)) { + die(EXIT_FAILURE, "failed to initialize progress indicator: %s", + strerror(errno)); + } + } + } } } @@ -293,6 +340,7 @@ static void mc_endread(msgcanon *m, const encops *eops, enc **ee) if (ferror(m->fp) || fclose(m->fp)) die(EXIT_FAILURE, "error closing message file: %s", strerror(errno)); } + if (m->f & F_PROGRESS) fprogress_done(&m->ff); } static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee) @@ -453,11 +501,12 @@ static int sign(int argc, char *argv[]) { "key", OPTF_ARGREQ, 0, 'k' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { "text", 0, 0, 't' }, { "nocheck", 0, 0, 'C' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "k:f:o:abdtC", opt, 0, 0, 0); + i = mdwopt(argc, argv, "k:f:o:abdptC", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'k': kn = optarg; break; @@ -468,6 +517,7 @@ static int sign(int argc, char *argv[]) case 'b': f |= F_BINARY; break; case 'd': f |= F_DETACH; break; case 'C': f |= F_NOCHECK; break; + case 'p': f |= F_PROGRESS; break; default: f |= F_BOGUS; break; } } @@ -574,12 +624,13 @@ static void vrfchoke(const char *m, void *p) static int verify(int argc, char *argv[]) { - const char *ef = "binary", *of = 0, *dfn = 0, *kn = 0, *err; + const char *ef = "binary", *of = 0, *fn, *dfn = 0, *kn = 0, *err; vrfctx v = { 0, 0, 1 }; key_file kf; key *k, *kk = 0; sigmsg s; FILE *fp, *ofp = 0, *rfp = 0; + fprogress ff; struct tm *tm; int i; char bb[MSGBUFSZ]; @@ -598,6 +649,7 @@ static int verify(int argc, char *argv[]) { "key", OPTF_ARGREQ, 0, 'k' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { "quiet", 0, 0, 'q' }, { "utc", 0, 0, 'u' }, { "fresh-time", 0, 0, 't' }, @@ -606,7 +658,7 @@ static int verify(int argc, char *argv[]) { "nocheck", 0, 0, 'C' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "k:f:o:abqt:uvC", opt, 0, 0, 0); + i = mdwopt(argc, argv, "k:f:o:abpqt:uvC", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'a': ef = "pem"; break; @@ -623,6 +675,7 @@ static int verify(int argc, char *argv[]) break; case 'q': if (v.verb > 0) v.verb--; break; case 'v': if (v.verb < 10) v.verb++; break; + case 'p': v.f |= F_PROGRESS; break; default: v.f |= F_BOGUS; break; } } @@ -632,16 +685,13 @@ static int verify(int argc, char *argv[]) if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); - if (optind >= argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) { + else if ((fp = fopen(fn, eo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0)) die(EXIT_FAILURE, "can't open keyring `%s'", keyring); @@ -692,7 +742,7 @@ static int verify(int argc, char *argv[]) sig_readsig(e, &s); if (optind < argc) dfn = argv[optind++]; - mcsetup_read(&mc_in, v.f, &e, dfn); + mcsetup_read(&mc_in, v.f, &e, fn, dfn); if (!of && (v.f & F_DETACH)) { rfp = ofp = 0; @@ -756,13 +806,18 @@ static int verify(int argc, char *argv[]) of, strerror(errno)); } rewind(rfp); + if (v.f & F_PROGRESS) fprogress_init(&ff, "copying buffer", rfp); if (v.verb && ofp == stdout) printf("DATA\n"); for (;;) { n = fread(bb, 1, sizeof(bb), rfp); if (!n) break; - if (fwrite(bb, 1, n, ofp) < n) + if (v.f & F_PROGRESS) fprogress_update(&ff, n); + if (fwrite(bb, 1, n, ofp) < n) { + if (v.f & F_PROGRESS) fprogress_done(&ff); die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } } + if (v.f & F_PROGRESS) fprogress_done(&ff); if (ferror(rfp) || fclose(rfp)) die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno)); } @@ -787,7 +842,7 @@ static int verify(int argc, char *argv[]) static int format(int argc, char *argv[]) { const char *ief = "binary", *oef = "binary"; - const char *dfn = 0, *of = 0, *mf = 0; + const char *fn, *dfn = 0, *of = 0, *mf = 0; sigmsg s; FILE *fp, *ofp = 0, *mfp = 0; int i; @@ -811,9 +866,10 @@ static int format(int argc, char *argv[]) { "format-out", OPTF_ARGREQ, 0, 'F' }, { "message", OPTF_ARGREQ, 0, 'm' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "f:F:m:o:aADE", opt, 0, 0, 0); + i = mdwopt(argc, argv, "f:F:m:o:apADE", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'a': ief = "pem"; break; @@ -824,6 +880,7 @@ static int format(int argc, char *argv[]) case 'E': f &= ~F_DETACH; fm |= F_DETACH; break; case 'm': mf = optarg; break; case 'o': of = optarg; break; + case 'p': f |= F_PROGRESS; break; default: f |= F_BOGUS; break; } } @@ -836,16 +893,13 @@ static int format(int argc, char *argv[]) if ((oeo = getenc(oef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", oef); - if (optind >= argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], ieo->rmode)) == 0) { + else if ((fp = fopen(fn, ieo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } if (optind < argc) dfn = argv[optind++]; @@ -859,7 +913,7 @@ static int format(int argc, char *argv[]) if (((s.f ^ v.f) & v.m) != 0) moan("boundary string inconsistent with contents (ignoring)"); - mcsetup_read(&mc_in, s.f, &ie, dfn); + mcsetup_read(&mc_in, s.f, &ie, fn, dfn); /* --- Prepare the output stuff --- */ @@ -1058,7 +1112,7 @@ static cmd cmdtab[] = { CMD_ENCODE, CMD_DECODE, { "sign", sign, - "sign [-adtC] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\ + "sign [-adptC] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ -a, --armour Same as `-f pem'.\n\ @@ -1067,11 +1121,12 @@ Options:\n\ -f, --format=FORMAT Encode as FORMAT.\n\ -k, --key=TAG Use public encryption key named by TAG.\n\ -o, --output=FILE Write output to FILE.\n\ +-p, --progress Show progress on large files.\n\ -t, --text Canonify input message as a text file.\n\ -C, --nocheck Don't check the private key.\n\ " }, { "verify", verify, - "verify [-abquvC] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\ + "verify [-abpquvC] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\ [FILE [MESSAGE]]", "\ Options:\n\ \n\ @@ -1080,6 +1135,7 @@ Options:\n\ -f, --format=FORMAT Decode as FORMAT.\n\ -k, --key=TAG Require that the message be signed by key TAG.\n\ -o, --output=FILE Write message to FILE.\n\ +-p, --progress Show progress on large files.\n\ -q, --quiet Produce fewer messages.\n\ -t, --freshtime=TIME Only accept signatures made after this time.\n\ -u, --utc Show dates in UTC rather than local time.\n\ @@ -1095,7 +1151,7 @@ Options:\n\ -u, --utc Show dates in UTC rather than local time.\n\ "}, { "format", format, - "format [-auADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\ + "format [-apuADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\ [FILE [MESSAGE]]", "\ Options:\n\ \n\ @@ -1107,6 +1163,7 @@ Options:\n\ -F, --format-out=FORMAT Encode output as FORMAT.\n\ -m, --message=FILE Write message to FILE.\n\ -o, --output=FILE Write new signature to FILE.\n\ +-p, --progress Show progress on large files.\n\ "}, { 0, 0, 0 } }; /* " Emacs seems confused. */ diff --git a/cc-enc.c b/cc-enc.c index c7354e6..2b8b0fd 100644 --- a/cc-enc.c +++ b/cc-enc.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include #include @@ -268,12 +270,12 @@ static void pem_destroy(enc *e) /* --- Encoder table --- */ const encops enctab[] = { - { "binary", "rb", "wb", + { "binary", "rb", "wb", 1, 1, bin_encinit, bin_decinit, bin_read, bin_write, bin_done, bin_done, bin_destroy }, - { "pem", "r", "w", + { "pem", "r", "w", 3, 4, pem_encinit, pem_decinit, pem_read, pem_write, pem_encdone, pem_decdone, @@ -358,11 +360,12 @@ void freeenc(enc *e) { e->ops->destroy(e); } int cmd_encode(int argc, char *argv[]) { - const char *of = 0; + const char *fn, *of = 0; FILE *ofp = 0; FILE *fp = 0; const char *ef = "binary"; const char *bd = "MESSAGE"; + fprogress ff; int i; size_t n; char buf[4096]; @@ -371,20 +374,23 @@ int cmd_encode(int argc, char *argv[]) enc *e; #define f_bogus 1u +#define f_progress 2u for (;;) { static const struct option opt[] = { { "format", OPTF_ARGREQ, 0, 'f' }, { "boundary", OPTF_ARGREQ, 0, 'b' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "f:b:o:", opt, 0, 0, 0); + i = mdwopt(argc, argv, "f:b:o:p", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'f': ef = optarg; break; case 'b': bd = optarg; break; case 'o': of = optarg; break; + case 'p': f |= f_progress; break; default: f |= f_bogus; break; } } @@ -394,16 +400,13 @@ int cmd_encode(int argc, char *argv[]) if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); - if (optind == argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], "rb")) == 0) { + else if ((fp = fopen(fn, "rb")) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } if (!of || strcmp(of, "-") == 0) ofp = stdout; @@ -414,25 +417,38 @@ int cmd_encode(int argc, char *argv[]) e = initenc(eo, ofp, bd); + if (f & f_progress) { + if (fprogress_init(&ff, fn, fp)) { + die(EXIT_FAILURE, "failed to initialize progress display: %s", + strerror(errno)); + } + } + do { n = fread(buf, 1, sizeof(buf), fp); - if (e->ops->write(e, buf, n)) + if (f & f_progress) fprogress_update(&ff, n); + if (e->ops->write(e, buf, n)) { + if (f & f_progress) fprogress_done(&ff); die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } } while (n == sizeof(buf)); + if (f & f_progress) fprogress_done(&ff); e->ops->encdone(e); freeenc(e); return (0); #undef f_bogus +#undef f_progress } int cmd_decode(int argc, char *argv[]) { - const char *of = 0; + const char *fn, *of = 0; FILE *ofp = 0; FILE *fp = 0; const char *ef = "binary"; const char *bd = 0; + fprogress ff; int i; char buf[4096]; unsigned f = 0; @@ -440,20 +456,23 @@ int cmd_decode(int argc, char *argv[]) enc *e; #define f_bogus 1u +#define f_progress 2u for (;;) { static const struct option opt[] = { { "format", OPTF_ARGREQ, 0, 'f' }, { "boundary", OPTF_ARGREQ, 0, 'b' }, { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "f:b:o:", opt, 0, 0, 0); + i = mdwopt(argc, argv, "f:b:o:p", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'f': ef = optarg; break; case 'b': bd = optarg; break; case 'o': of = optarg; break; + case 'p': f |= f_progress; break; default: f |= f_bogus; break; } } @@ -463,16 +482,13 @@ int cmd_decode(int argc, char *argv[]) if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); - if (optind == argc) + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { - fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) { + else if ((fp = fopen(fn, eo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; + fn, strerror(errno)); + } if (!of || strcmp(of, "-") == 0) ofp = stdout; @@ -483,17 +499,32 @@ int cmd_decode(int argc, char *argv[]) e = initdec(eo, fp, checkbdry, (/*unconst*/ void *)bd); + if (f & f_progress) { + if (fprogress_init(&ff, fn, fp)) { + die(EXIT_FAILURE, "failed to initialize progress display: %s", + strerror(errno)); + } + } + do { - if ((i = e->ops->read(e, buf, sizeof(buf))) < 0) + if ((i = e->ops->read(e, buf, sizeof(buf))) < 0) { + if (f & f_progress) fprogress_done(&ff); die(EXIT_FAILURE, "error reading input: %s", strerror(errno)); - if (fwrite(buf, 1, i, ofp) < i) + } + if (f & f_progress) + fprogress_update(&ff, i*e->ops->ncook/e->ops->nraw); + if (fwrite(buf, 1, i, ofp) < i) { + if (f & f_progress) fprogress_done(&ff); die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } } while (i == sizeof(buf)); e->ops->decdone(e); + if (f & f_progress) fprogress_done(&ff); freeenc(e); return (0); #undef f_bogus +#undef f_progress } /*----- That's all, folks -------------------------------------------------*/ diff --git a/cc-kem.c b/cc-kem.c index 28fcd4b..63199fc 100644 --- a/cc-kem.c +++ b/cc-kem.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include #include diff --git a/cc-list.c b/cc-list.c index 9c06cd0..b182ba4 100644 --- a/cc-list.c +++ b/cc-list.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include #include "cc.h" diff --git a/cc-progress.c b/cc-progress.c new file mode 100644 index 0000000..903e80e --- /dev/null +++ b/cc-progress.c @@ -0,0 +1,244 @@ +/* -*-c-*- + * + * Progress indicators for command-line tools + * + * (c) 2011 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#define _FILE_OFFSET_BITS 64 + +#include "config.h" + +#include "cc.h" + +#ifndef PATHSEP +# if defined(__riscos) +# define PATHSEP '.' +# elif defined(__unix) || defined(unix) +# define PATHSEP '/' +# else +# define PATHSEP '\\' +# endif +#endif + +/*----- Static data -------------------------------------------------------*/ + +static const char baton[] = "-\\|/"; + +/*----- Human-friendly unit printing --------------------------------------*/ + +struct unit { + const char *name; + int m; +}; + +/* --- @prhuman_time@ --- * + * + * Arguments: @FILE *fp@ = stream to print on + * @unsigned long n@ = time in seconds to print + * + * Returns: --- + * + * Use: Prints a time in some reasonable format. The time takes up + * PRHUMAN_TIMEWD character spaces. + */ + +#define PRHUMAN_TIMEWD 7 + +static void prhuman_time(FILE *fp, unsigned long n) +{ + const static struct unit utime[] = { + { "s", 60 }, { "m", 60 }, { "h", 24 }, { "d", 0 } + }; + + unsigned long m = 0; + const struct unit *u = utime; + + while (u[1].m && n > u[0].m*u[1].m) { n /= u->m; u++; } + m = n / u[1].m; n %= u[0].m; + if (m) fprintf(fp, "%3lu%s%02lu%s", m, u[1].name, n, u[0].name); + else fprintf(fp, " %2lu%s", n, u[0].name); +} + +/* --- @prhuman_data@ --- * + * + * Arguments: @FILE *fp@ = file to print on + * @off_t n@ = size to be printed + * + * Returns: --- + * + * Use: Prints a data size in some reasonable format. The data size + * takes up PRHUMAN_DATAWD character spaces. + */ + +#define PRHUMAN_DATAWD 7 + +static void prhuman_data(FILE *fp, off_t n) +{ + const static struct unit udata[] = { + { " ", 1024 }, { "k", 1024 }, { "M", 1024 }, { "G", 1024 }, + { "T", 1024 }, { "P", 1024 }, { "E", 1024 }, { "Z", 1024 }, + { "Y", 0 } + }; + + double x = n; + const struct unit *u = udata; + + while (u->m && x >= u->m) { x /= u->m; u++; } + fprintf(fp, "%6.1f%s", x, u->name); +} + +/*----- Main code ---------------------------------------------------------*/ + +#define BARWD 16 + +/* --- @fprogress_init@ --- * + * + * Arguments: @fprogress *f@ = progress context to be initialized + * @const char *name@ = file name string to show + * @FILE *fp@ = file we're reading from + * + * Returns: Zero on success, nonzero if the file's state is now broken. + * + * Use: Initializes a progress context. Nothing is actually + * displayed yet. + */ + +int fprogress_init(fprogress *f, const char *name, FILE *fp) +{ + const char *p; + off_t o, sz = -1; + size_t n; + + /* --- Set up the offset --- */ + + if ((o = ftello(fp)) >= 0 && + fseeko(fp, 0, SEEK_END) >= 0 && + (sz = ftello(fp), + fseeko(fp, o, SEEK_SET) < 0)) + return (-1); + if (o != -1 && sz != -1) sz -= o; + f->o = f->olast = 0; f->sz = sz; + + /* --- Set up the file name --- */ + + n = strlen(name); + if (n < sizeof(f->name)) + strcpy(f->name, name); + else if ((p = strchr(name + n - sizeof(f->name) + 4, PATHSEP)) != 0) + sprintf(f->name, "...%s", p); + else { + p = strrchr(name, PATHSEP); + if (!p) sprintf(f->name, "%.*s...", (int)sizeof(f->name) - 4, name); + else sprintf(f->name, "...%.*s...", (int)sizeof(f->name) - 7, p); + } + + /* --- Set up some other stuff --- */ + + f->start = f->last = time(0); + f->bp = baton; + + /* --- Done --- */ + + return (0); +} + +/* --- @fprogress_clear@ --- * + * + * Arguments: @fprogress *f@ = progress context + * + * Returns: --- + * + * Use: Clears the progress display from the screen. + */ + +void fprogress_clear(fprogress *f) +{ + fprintf(stderr, "\r%*s\r", + sizeof(f->name) + 2*PRHUMAN_DATAWD + PRHUMAN_TIMEWD + BARWD + 16, + ""); +} + +/* --- @fprogress_update@ --- * + * + * Arguments: @fprogress *f@ = progress context + * @size_t n@ = how much progress has been made + * + * Returns: --- + * + * Use: Maybe updates the display to show that some progress has been + * made. + */ + +void fprogress_update(fprogress *f, size_t sz) +{ + time_t now = time(0); + int i, n; + + /* --- See if there's anything to do --- */ + + f->o += sz; + if (difftime(now, f->last) < 1) return; + f->last = now; + + /* --- See if we're going to lose the ETA and percentage indicators --- */ + + if (f->olast < f->sz && f->o > f->sz) fprogress_clear(f); + f->olast = f->o; + + /* --- Do the initial display --- */ + + fprintf(stderr, "\r%-*s%c ", + (int)sizeof(f->name), f->name, + *f->bp++); + if (!*f->bp) f->bp = baton; + prhuman_data(stderr, f->o); + + /* --- More complicated display if we have a set size --- */ + + if (f->sz > f->o) { + fputc('/', stderr); + prhuman_data(stderr, f->sz); + fputs(" [", stderr); + n = (f->o*BARWD + f->sz/2)/f->sz; + for (i = 0; i < n; i++) fputc('.', stderr); + fprintf(stderr, "%*s] %3d%% ETA ", BARWD - n, "", + (int)((f->o*100 + 50)/f->sz)); + prhuman_time(stderr, difftime(now, f->start)*(f->sz - f->o)/f->o); + } +} + +/* --- @fprogress_done@ --- * + * + * Arguments: @fprogress *f@ = progress context + * + * Returns: --- + * + * Use: Clear up the progress context and removes any display. + */ + +void fprogress_done(fprogress *f) { fprogress_clear(f); } + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/cc-sig.c b/cc-sig.c index eecda46..54b58f3 100644 --- a/cc-sig.c +++ b/cc-sig.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include #include diff --git a/cc-subcmd.c b/cc-subcmd.c index b77a9c9..c8f8b51 100644 --- a/cc-subcmd.c +++ b/cc-subcmd.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include #include diff --git a/cc.h b/cc.h index 45e7eff..6f92978 100644 --- a/cc.h +++ b/cc.h @@ -36,8 +36,13 @@ /*----- Header files ------------------------------------------------------*/ +#if _FILE_OFFSET_BITS != 64 +# error "Must set _FILE_OFFSET_BITS to 64." +#endif + #include #include +#include #include @@ -48,6 +53,15 @@ /*----- Data structures ---------------------------------------------------*/ +/* --- Progress indicators --- */ + +typedef struct fprogress { + const char *bp; + off_t o, sz, olast; + time_t start, last; + char name[24]; +} fprogress; + /* --- Key encapsulation --- */ typedef struct kem { @@ -114,6 +128,7 @@ typedef struct enc { typedef struct encops { const char *name; const char *rmode, *wmode; + int nraw, ncook; enc *(*initenc)(FILE */*fp*/, const char */*msg*/); enc *(*initdec)(FILE */*fp*/, int (*/*func*/)(const char *, void *), void */*p*/); @@ -258,24 +273,26 @@ extern void freeenc(enc */*e*/); #define CMD_ENCODE { \ "encode", cmd_encode, \ - "encode [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", \ + "encode [-p] [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", \ "\ Options:\n\ \n\ -f, --format=FORMAT Encode to FORMAT.\n\ -b, --boundary=LABEL PEM boundary is LABEL.\n\ -o, --output=FILE Write output to FILE.\n\ +-p, --progress Show progress on large files.\n\ " } #define CMD_DECODE { \ "decode", cmd_decode, \ - "decode [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", \ + "decode [-p] [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", \ "\ Options:\n\ \n\ -f, --format=FORMAT Decode from FORMAT.\n\ -b, --boundary=LABEL PEM boundary is LABEL.\n\ -o, --output=FILE Write output to FILE.\n\ +-p, --progress Show progress on large files.\n\ " } extern int cmd_encode(int /*argc*/, char */*argv*/[]); @@ -387,6 +404,58 @@ const cmd *findcmd(const cmd */*cmds*/, const char */*name*/); extern void sc_help(const cmd */*cmds*/, FILE */*fp*/, char *const */*argv*/); +/*----- Progress indicators -----------------------------------------------*/ + +/* --- @fprogress_init@ --- * + * + * Arguments: @fprogress *f@ = progress context to be initialized + * @const char *name@ = file name string to show + * @FILE *fp@ = file we're reading from + * + * Returns: Zero on success, nonzero if the file's state is now broken. + * + * Use: Initializes a progress context. Nothing is actually + * displayed yet. + */ + +extern int fprogress_init(fprogress */*f*/, + const char */*name*/, FILE */*fp*/); + +/* --- @fprogress_update@ --- * + * + * Arguments: @fprogress *f@ = progress context + * @size_t n@ = how much progress has been made + * + * Returns: --- + * + * Use: Maybe updates the display to show that some progress has been + * made. + */ + +extern void fprogress_update(fprogress */*f*/, size_t /*n*/); + +/* --- @fprogress_clear@ --- * + * + * Arguments: @fprogress *f@ = progress context + * + * Returns: --- + * + * Use: Clears the progress display from the screen. + */ + +extern void fprogress_clear(fprogress */*f*/); + +/* --- @fprogress_done@ --- * + * + * Arguments: @fprogress *f@ = progress context + * + * Returns: --- + * + * Use: Clear up the progress context and removes any display. + */ + +extern void fprogress_done(fprogress */*f*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus diff --git a/cookie.c b/cookie.c index 3414695..8fa07da 100644 --- a/cookie.c +++ b/cookie.c @@ -28,6 +28,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include diff --git a/dsig.1 b/dsig.1 index edd1036..58cea8f 100644 --- a/dsig.1 +++ b/dsig.1 @@ -44,7 +44,7 @@ is one of: .RI [ item ...] .br .B sign -.RB [ \-0bqvC ] +.RB [ \-0bpqvC ] .RB [ \-c .IR comment ] .RB [ \-k @@ -59,7 +59,7 @@ is one of: .IR output ] .br .B verify -.RB [ \-qvC ] +.RB [ \-pqvC ] .RI [ file ] .SH DESCRIPTION The @@ -301,6 +301,9 @@ Writes as a comment in the output file. The comment's integrity is protected by the signature. .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .BI "\-f, \-\-file " name Read filenames from .I name @@ -366,6 +369,9 @@ Produce more informational output. The default verbosity level is 1. .B "\-q, \-\-quiet" Produce less information output. .TP +.BI "\-p, \-\-progress" +Write a progress meter to standard error while processing large files. +.TP .B "\-C, \-\-nocheck" Don't check the public key for validity. This makes verification go much faster, but at the risk of using a duff key, and potentially diff --git a/dsig.c b/dsig.c index c379e94..d09b675 100644 --- a/dsig.c +++ b/dsig.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include @@ -595,6 +597,7 @@ static const char *keyring = "keyring"; /* --- @fhash@ --- * * * Arguments: @const gchash *c@ = pointer to hash class + * @unsigned f@ = flags * @const char *file@ = file to hash * @void *b@ = pointer to output buffer * @@ -603,9 +606,12 @@ static const char *keyring = "keyring"; * Use: Hashes a file. */ -static int fhash(const gchash *c, const char *file, void *b) +#define FHF_PROGRESS 256u + +static int fhash(const gchash *c, unsigned f, const char *file, void *b) { FILE *fp = fopen(file, "rb"); + fprogress ff; ghash *h = GH_INIT(c); char buf[4096]; size_t sz; @@ -613,10 +619,16 @@ static int fhash(const gchash *c, const char *file, void *b) if (!fp) return (-1); - while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0) + if (f & FHF_PROGRESS) { + if (fprogress_init(&ff, file, fp)) return (-1); + } + while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0) { GH_HASH(h, buf, sz); + if (f & FHF_PROGRESS) fprogress_update(&ff, sz); + } if (ferror(fp)) rc = -1; + if (f & FHF_PROGRESS) fprogress_done(&ff); GH_DONE(h, b); GH_DESTROY(h); fclose(fp); @@ -677,6 +689,7 @@ static int sign(int argc, char *argv[]) { "null", 0, 0, '0' }, { "binary", 0, 0, 'b' }, { "verbose", 0, 0, 'v' }, + { "progress", 0, 0, 'p' }, { "quiet", 0, 0, 'q' }, { "comment", OPTF_ARGREQ, 0, 'c' }, { "file", OPTF_ARGREQ, 0, 'f' }, @@ -686,7 +699,7 @@ static int sign(int argc, char *argv[]) { "nocheck", OPTF_ARGREQ, 0, 'C' }, { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "+0vqbC" "c:" "f:o:" "k:e:", opts, 0, 0, 0); + int i = mdwopt(argc, argv, "+0vpqbC" "c:" "f:o:" "k:e:", opts, 0, 0, 0); if (i < 0) break; switch (i) { @@ -699,6 +712,9 @@ static int sign(int argc, char *argv[]) case 'v': verb++; break; + case 'p': + f |= FHF_PROGRESS; + break; case 'q': if (verb > 0) verb--; @@ -811,7 +827,7 @@ static int sign(int argc, char *argv[]) break; b.tag = T_FILE; DENSURE(&b.b, GH_CLASS(s->h)->hashsz); - if (fhash(GH_CLASS(s->h), b.d.buf, b.b.buf)) { + if (fhash(GH_CLASS(s->h), f, b.d.buf, b.b.buf)) { moan("Error reading `%s': %s", b.d.buf, strerror(errno)); f |= f_bogus; } else { @@ -897,17 +913,21 @@ static int verify(int argc, char *argv[]) for (;;) { static struct option opts[] = { { "verbose", 0, 0, 'v' }, + { "progress", 0, 0, 'p' }, { "quiet", 0, 0, 'q' }, { "nocheck", 0, 0, 'C' }, { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "+vqC", opts, 0, 0, 0); + int i = mdwopt(argc, argv, "+vpqC", opts, 0, 0, 0); if (i < 0) break; switch (i) { case 'v': verb++; break; + case 'p': + f |= FHF_PROGRESS; + break; case 'q': if (verb) verb--; @@ -1025,7 +1045,7 @@ static int verify(int argc, char *argv[]) case T_FILE: DRESET(&d); DENSURE(&d, GH_CLASS(s->h)->hashsz); - if (fhash(GH_CLASS(s->h), b.d.buf, d.buf)) { + if (fhash(GH_CLASS(s->h), f, b.d.buf, d.buf)) { if (verb > 1) { printf("BAD error reading file `%s': %s\n", b.d.buf, strerror(errno)); @@ -1109,7 +1129,7 @@ static cmd cmdtab[] = { { "help", cmd_help, "help [COMMAND...]" }, { "show", cmd_show, "show [ITEM...]" }, { "sign", sign, - "sign [-0bqvC] [-c COMMENT] [-k TAG] [-e EXPIRE]\n\t\ + "sign [-0bpqvC] [-c COMMENT] [-k TAG] [-e EXPIRE]\n\t\ [-f FILE] [-o OUTPUT]", "\ Options:\n\ @@ -1118,6 +1138,7 @@ Options:\n\ -b, --binary Produce a binary output file.\n\ -q, --quiet Produce fewer messages while working.\n\ -v, --verbose Produce more messages while working.\n\ +-p, --progress Show progress on large files.\n\ -C, --nocheck Don't check the private key.\n\ -c, --comment=COMMENT Include COMMENT in the output file.\n\ -f, --file=FILE Read filenames to hash from FILE.\n\ @@ -1126,11 +1147,12 @@ Options:\n\ -e, --expire=TIME The signature should expire after TIME.\n\ " }, { "verify", verify, - "verify [-qvC] [FILE]", "\ + "verify [-pqvC] [FILE]", "\ Options:\n\ \n\ -q, --quiet Produce fewer messages while working.\n\ -v, --verbose Produce more messages while working.\n\ +-p, --progress Show progress on large files.\n\ -C, --nocheck Don't check the public key.\n\ " }, { 0, 0, 0 } diff --git a/hashsum.c b/hashsum.c index d0ac31e..554b079 100644 --- a/hashsum.c +++ b/hashsum.c @@ -55,16 +55,6 @@ #include "ghash.h" #include "cc.h" -#ifndef PATHSEP -# if defined(__riscos) -# define PATHSEP '.' -# elif defined(__unix) || defined(unix) -# define PATHSEP '/' -# else -# define PATHSEP '\\' -# endif -#endif - /*----- Static variables --------------------------------------------------*/ #define f_binary 1u @@ -216,41 +206,6 @@ static const encodeops *getencoding(const char *ename) * Use: Hashes a file. */ -struct unit { - const char *name; - int m; -}; - -static void prhuman_time(FILE *fp, unsigned long n) -{ - const static struct unit utime[] = { - { "s", 60 }, { "m", 60 }, { "h", 24 }, { "d", 0 } - }; - - unsigned long m = 0; - const struct unit *u = utime; - - while (u[1].m && n > u[0].m*u[1].m) { n /= u->m; u++; } - m = n / u[1].m; n %= u[0].m; - if (m) fprintf(fp, "%3lu%s%02lu%s", m, u[1].name, n, u[0].name); - else fprintf(fp, " %2lu%s", n, u[0].name); -} - -static void prhuman_data(FILE *fp, off_t n) -{ - const static struct unit udata[] = { - { " ", 1024 }, { "k", 1024 }, { "M", 1024 }, { "G", 1024 }, - { "T", 1024 }, { "P", 1024 }, { "E", 1024 }, { "Z", 1024 }, - { "Y", 0 } - }; - - double x = n; - const struct unit *u = udata; - - while (u->m && x >= u->m) { x /= u->m; u++; } - fprintf(fp, "%6.1f%s", x, u->name); -} - static int fhash(const char *file, unsigned f, const gchash *gch, void *buf) { FILE *fp; @@ -258,13 +213,7 @@ static int fhash(const char *file, unsigned f, const gchash *gch, void *buf) size_t sz; ghash *h; int e; - off_t fsz = -1, fo; - const char *p; - dstr d = DSTR_INIT; - static char baton[] = "-\\|/"; - char *bp = baton; - time_t now, last, start; - int i, pc; + fprogress ff; if (!file || strcmp(file, "-") == 0) fp = stdin; @@ -272,53 +221,17 @@ static int fhash(const char *file, unsigned f, const gchash *gch, void *buf) return (-1); if (f & f_progress) { - if ((fo = ftello(fp)) >= 0 && - fseeko(fp, 0, SEEK_END) >= 0 && - (fsz = ftello(fp), - fseeko(fp, fo, SEEK_SET) < 0)) - return (-1); - if (fo != -1 && fsz != -1) fsz -= fo; - fo = 0; - sz = strlen(file); - if (sz < 24) - dstr_puts(&d, file); - else if ((p = strchr(file + sz - 20, PATHSEP)) != 0) { - dstr_puts(&d, "..."); dstr_puts(&d, p); - } else { - p = strrchr(file, PATHSEP); - if (!p) dstr_putf(&d, "%.20s...", file); - else dstr_putf(&d, "...%.17s...", p); - } - start = last = time(0); + if (fprogress_init(&ff, file, fp)) return (-1); } h = GH_INIT(gch); while ((sz = fread(fbuf, 1, sizeof(fbuf), fp)) > 0) { GH_HASH(h, fbuf, sz); - if (f & f_progress) { - fo += sz; - now = time(0); - if (difftime(now, last) < 1) continue; - last = now; - fprintf(stderr, "\r%-24s", d.buf); - fprintf(stderr, "%c ", *bp++); if (!*bp) bp = baton; - prhuman_data(stderr, fo); - if (fsz >= fo) { - fputc('/', stderr); - prhuman_data(stderr, fsz); - fputs(" [", stderr); - pc = (fo*16 + fsz/2)/fsz; - for (i = 0; i < pc; i++) fputc('.', stderr); - for (; i < 16; i++) fputc(' ', stderr); - fprintf(stderr, "] %3d%%", (int)((fo*100 + 50)/fsz)); - fprintf(stderr, " ETA "); - prhuman_time(stderr, difftime(now, start)*(fsz - fo)/fo); - } - } + if (f & f_progress) fprogress_update(&ff, sz); } GH_DONE(h, buf); GH_DESTROY(h); - if (f & f_progress) fprintf(stderr, "\r%78s\r", ""); + if (f & f_progress) fprogress_done(&ff); e = ferror(fp); if (file) fclose(fp); diff --git a/keyutil.c b/keyutil.c index 74f6409..cca075f 100644 --- a/keyutil.c +++ b/keyutil.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include diff --git a/perftest.c b/perftest.c index 3a0df7e..3c26937 100644 --- a/perftest.c +++ b/perftest.c @@ -29,6 +29,8 @@ /*----- Header files ------------------------------------------------------*/ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include -- 2.11.0