* (c) 2000 Straylight/Edgeware
*/
-/*----- Licensing notice --------------------------------------------------*
+/*----- Licensing notice --------------------------------------------------*
*
* This file is part of Catacomb.
*
* 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,
/*----- Header files ------------------------------------------------------*/
+#define _FILE_OFFSET_BITS 64
+
#include "config.h"
#include <assert.h>
#include <mLib/base64.h>
#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_oddhash 64u
#define f_escape 128u
#define f_oddenc 256u
+#define f_progress 512u
/*----- Encoding and decoding ---------------------------------------------*/
}
if (pp)
*pp = (char *)p;
- return (i);
+ return (i);
}
/* --- Base64 encoding --- */
/* --- Table --- */
-typedef struct encops {
+typedef struct encodeops {
const char *name;
void (*put)(const octet *, size_t, FILE *);
size_t (*get)(const char *, octet *, size_t, char **);
-} encops;
+} encodeops;
-static const encops enctab[] = {
+static const encodeops encodingtab[] = {
{ "hex", puthex, gethex },
{ "base64", putb64, getb64 },
{ "base32", putb32, getb32 },
{ 0, 0, 0 }
};
-static const encops *getenc(const char *ename)
+static const encodeops *getencoding(const char *ename)
{
- const encops *e;
+ const encodeops *e;
- for (e = enctab; e->name; e++) {
+ for (e = encodingtab; e->name; e++) {
if (strcmp(ename, e->name) == 0)
return (e);
}
* 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;
- char fbuf[BUFSIZ];
+ char fbuf[1024 * 128];
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;
- if (!file)
+ if (!file || strcmp(file, "-") == 0)
fp = stdin;
else if ((fp = fopen(file, f & f_binary ? "rb" : "r")) == 0)
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);
+ }
+
h = GH_INIT(gch);
- while ((sz = fread(fbuf, 1, sizeof(fbuf), fp)) > 0)
+ 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);
+ }
+ }
+ }
GH_DONE(h, buf);
GH_DESTROY(h);
+ if (f & f_progress) fprintf(stderr, "\r%78s\r", "");
e = ferror(fp);
if (file)
fclose(fp);
/*----- Guts --------------------------------------------------------------*/
static int checkhash(const char *file, unsigned f,
- const gchash *gch, const encops *e)
+ const gchash *gch, const encodeops *e)
{
int rc;
FILE *fp;
unsigned long n = 0, nfail = 0;
octet *buf = xmalloc(2 * gch->hashsz);
- if (!file)
+ if (!file || strcmp(file, "-") == 0)
fp = stdin;
else if ((fp = fopen(file, f & f_raw ? "r" : "rb")) == 0) {
moan("couldn't open `%s': %s", file, strerror(errno));
xfree(buf);
buf = xmalloc(2 * gch->hashsz);
} else if (strcmp(q, "encoding") == 0) {
- const encops *ee;
+ const encodeops *ee;
if ((q = str_getword(&p)) == 0)
continue;
- if ((ee = getenc(q)) == 0)
+ if ((ee = getencoding(q)) == 0)
continue;
e = ee;
} else if (strcmp(q, "escape") == 0)
}
static int dohash(const char *file, unsigned f,
- const gchash *gch, const encops *e)
+ const gchash *gch, const encodeops *e)
{
int rc = 0;
octet *p = xmalloc(gch->hashsz);
}
static int dofile(const char *file, unsigned f,
- const gchash *gch, const encops *e)
+ const gchash *gch, const encodeops *e)
{
return (f & f_check ? checkhash : dohash)(file, f, gch, e);
}
static int hashfiles(const char *file, unsigned f,
- const gchash *gch, const encops *e)
+ const gchash *gch, const encodeops *e)
{
FILE *fp;
dstr d = DSTR_INIT;
int rc = 0;
int rrc;
- if (!file)
+ if (!file || strcmp(file, "-") == 0)
fp = stdin;
else if ((fp = fopen(file, f & f_raw ? "r" : "rb")) == 0) {
moan("couldn't open `%s': %s", file, strerror(errno));
}
static int hashsum(const char *file, unsigned f,
- const gchash *gch, const encops *e)
+ const gchash *gch, const encodeops *e)
{
return (f & f_files ? hashfiles : dofile)(file, f, gch, e);
}
/*----- Main driver -------------------------------------------------------*/
-static void version(FILE *fp)
+void version(FILE *fp)
{
pquis(fp, "$, Catacomb version " VERSION "\n");
}
static void usage(FILE *fp)
{
- pquis(fp, "Usage: $ [-f0ebcv] [-a algorithm] [files...]\n");
+ pquis(fp, "Usage: $ [-f0ebcv] [-a ALGORITHM] [-E ENC] [FILES...]\n");
}
static void help(FILE *fp, const gchash *gch)
-h, --help Display this help message.\n\
-V, --version Display program's version number.\n\
-u, --usage Display a terse usage message.\n\
+-l, --list [ITEM...] Show known hash functions and/or encodings.\n\
\n\
-a, --algorithm=ALG Use the message digest algorithm ALG.\n\
-E, --encoding=ENC Represent hashes using encoding ENC.\n\
fprintf(fp, "The default message digest algorithm is %s.\n", gch->name);
}
+#define LISTS(LI) \
+ LI("Lists", list, listtab[i].name, listtab[i].name) \
+ LI("Hash functions", hash, ghashtab[i], ghashtab[i]->name) \
+ LI("Encodings", enc, encodingtab[i].name, encodingtab[i].name)
+
+MAKELISTTAB(listtab, LISTS)
+
int main(int argc, char *argv[])
{
unsigned f = 0;
const gchash *gch = 0;
- const encops *e = &enctab[0];
+ const encodeops *e = &encodingtab[0];
int rc;
/* --- Initialization --- */
{ "check", 0, 0, 'c' },
{ "binary", 0, 0, 'b' },
{ "verbose", 0, 0, 'v' },
+ { "progress", 0, 0, 'p' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "hVu a:E:l f0 ecbv", opts, 0, 0, 0);
+ int i = mdwopt(argc, argv, "hVu a:E:l f0 ecbvp", opts, 0, 0, 0);
if (i < 0)
break;
case 'u':
usage(stdout);
exit(0);
+ case 'l':
+ exit(displaylists(listtab, argv + optind));
case 'a':
if ((gch = gethash(optarg)) == 0)
die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
f |= f_oddhash;
break;
- case 'l': {
- unsigned j;
- printf("Algorithms: ");
- for (j = 0; ghashtab[j]; j++) {
- if (j) fputc(' ', stdout);
- printf("%s", ghashtab[j]->name);
- }
- fputc('\n', stdout);
- printf("Encodings: ");
- for (j = 0; enctab[j].name; j++) {
- if (j) fputc(' ', stdout);
- printf("%s", enctab[j].name);
- }
- fputc('\n', stdout);
- exit(0);
- } break;
case 'E':
- if ((e = getenc(optarg)) == 0)
+ if ((e = getencoding(optarg)) == 0)
die(EXIT_FAILURE, "unknown encoding `%s'", optarg);
f |= f_oddenc;
break;
case 'v':
f |= f_verbose;
break;
+ case 'p':
+ f |= f_progress;
+ break;
default:
f |= f_bogus;
break;
/* --- Generate output --- */
+ if (!(f & f_check) && (argc || (f & f_files))) {
+ if (f & f_oddhash) printf("#hash %s\n", gch->name);
+ if (f & f_oddenc) printf("#encoding %s\n", e->name);
+ if (f & f_escape) fputs("#escape\n", stdout);
+ }
if (!argc)
rc = hashsum(0, f, gch, e);
else {
int rrc;
rc = 0;
- if (!(f & f_check)) {
- if (f & f_oddhash) printf("#hash %s\n", gch->name);
- if (f & f_oddenc) printf("#encoding %s\n", e->name);
- if (f & f_escape) fputs("#escape\n", stdout);
- }
for (i = 0; i < argc; i++) {
if ((rrc = hashsum(argv[i], f, gch, e)) != 0)
rc = rrc;