X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/07290a455e008c37adc233554eff8ad468608161..f5e91c02ff4057002e480ad4933a1b9aa2496c40:/cc-hash.c diff --git a/cc-hash.c b/cc-hash.c index 9e23f79..00dcb39 100644 --- a/cc-hash.c +++ b/cc-hash.c @@ -32,11 +32,17 @@ #include "config.h" #include +#include #include #include #include +#include +#include + +#include #include +#include #include #include @@ -166,6 +172,18 @@ const encodeops *getencoding(const char *ename) /*----- File hashing ------------------------------------------------------*/ +enum { + FHETY_DIR, + FHETY_FILE +}; + +typedef struct fhent { + struct fhent *next; + unsigned ty; + struct fhent *sub; + char name[1]; +} fhdir; + /* --- @gethash@ --- * * * Arguments: @const char *name@ = pointer to name string @@ -193,6 +211,27 @@ const gchash *gethash(const char *name) return (gg); } +/* --- @describefile@ --- * + * + * Arguments: @const struct stat *st@ = pointer to file state + * + * Returns: A snappy one-word description of the file. + */ + +const char *describefile(const struct stat *st) +{ + switch (st->st_mode & S_IFMT) { + case S_IFBLK: return ("block-special"); + case S_IFCHR: return ("char-special"); + case S_IFIFO: return ("fifo"); + case S_IFREG: return ("file"); + case S_IFLNK: return ("symlink"); + case S_IFDIR: return ("directory"); + case S_IFSOCK: return ("socket"); + default: return ("unknown"); + } +} + /* --- @fhash_init@ ---* * * Arguments: @fhashstate *fh@ = pointer to fhash state to initialize @@ -205,7 +244,7 @@ const gchash *gethash(const char *name) */ void fhash_init(fhashstate *fh, const gchash *gch, unsigned f) - { fh->f = f; fh->gch = gch; } + { fh->f = f; fh->gch = gch; fh->ents = 0; } /* --- @fhash_free@ --- * * @@ -216,7 +255,19 @@ void fhash_init(fhashstate *fh, const gchash *gch, unsigned f) * Use: Frees an fhash state. */ -void fhash_free(fhashstate *fh) { return; } +static void freefhents(struct fhent *fhe) +{ + struct fhent *ffhe; + + for (; fhe; fhe = ffhe) { + ffhe = fhe->next; + freefhents(fhe->sub); + xfree(fhe); + } +} + +void fhash_free(fhashstate *fh) + { freefhents(fh->ents); } /* --- @fhash@ --- * * @@ -236,6 +287,9 @@ int fhash(fhashstate *fh, const char *file, void *buf) size_t sz; ghash *h; int rc = 0; + struct fhent *fhe, **ffhe; + const char *p, *q; + size_t n; fprogress ff; if (!file || strcmp(file, "-") == 0) @@ -247,6 +301,35 @@ int fhash(fhashstate *fh, const char *file, void *buf) if (fprogress_init(&ff, file, fp)) return (-1); } + if (fh->f & FHF_JUNK) { + p = file; + if (strncmp(p, "./", 2) == 0) p += 2; + q = p; + ffhe = &fh->ents; + for (;;) { + if (*q == '/' || *q == 0) { + n = q - p; + for (; *ffhe; ffhe = &(*ffhe)->next) { + fhe = *ffhe; + if (strncmp(p, fhe->name, n) == 0 && fhe->name[n] == 0) + goto found; + } + fhe = xmalloc(offsetof(struct fhent, name) + n + 1); + fhe->next = 0; + fhe->ty = *q == '/' ? FHETY_DIR : FHETY_FILE; + fhe->sub = 0; + *ffhe = fhe; + memcpy(fhe->name, p, n); fhe->name[n] = 0; + found: + if (!*q) break; + while (*++q == '/'); + p = q; + ffhe = &fhe->sub; + } else + q++; + } + } + h = GH_INIT(fh->gch); while ((sz = fread(fbuf, 1, sizeof(fbuf), fp)) > 0) { GH_HASH(h, fbuf, sz); @@ -260,6 +343,99 @@ int fhash(fhashstate *fh, const char *file, void *buf) return (rc); } +/* --- @fhash_junk@ --- * + * + * Arguments: @fhashstate *fh@ = pointer to fhash state + * @void (*func)(const char *, const struct stat *, void *)@ + * @void *p@ = pointer to pass to function + * + * Returns: Positive if any junk was found, negative on error, zero if + * everything was fine. + * + * Use: Reports junk files in any directories covered by the hash + * state. + */ + +struct fhjunk { + int (*func)(const char *, const struct stat *, void *); + void *p; + dstr *d; +}; + +static int fhjunk(struct fhjunk *fhj, struct fhent *ents) +{ + DIR *dp; + int rc = 0, rrc; + struct stat st; + struct dirent *d; + const char *dname; + size_t n = fhj->d->len; + struct fhent *fhe; + + dname = n ? fhj->d->buf : "."; + if ((dp = opendir(dname)) == 0) { + moan("failed to open directory `%s': %s", dname, strerror(errno)); + rc = -1; + goto subs; + } + if (n) { + dstr_putc(fhj->d, '/'); + n++; + } + while (errno = 0, (d = readdir(dp)) != 0) { + if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) + continue; + for (fhe = ents; fhe; fhe = fhe->next) { + if (strcmp(d->d_name, fhe->name) == 0) goto found; + } + fhj->d->len = n; + dstr_puts(fhj->d, d->d_name); + if (!lstat(fhj->d->buf, &st)) { + if (!rc) rc = 1; + rrc = fhj->func(fhj->d->buf, &st, fhj->p); + } else { + rc = -1; + rrc = fhj->func(fhj->d->buf, 0, fhj->p); + } + if (rrc < 0) rc = -1; + found:; + } + closedir(dp); + if (errno) { + moan("failed to read directory `%s': %s", dname, strerror(errno)); + rc = -1; + } + +subs: + for (fhe = ents; fhe; fhe = fhe->next) { + if (fhe->ty == FHETY_DIR) { + fhj->d->len = n; + dstr_puts(fhj->d, fhe->name); + rrc = fhjunk(fhj, fhe->sub); + if (rrc < 0) rc = -1; + else if (!rc) rc = rrc; + } + } + + return (rc); +} + +int fhash_junk(fhashstate *fh, + int (*func)(const char *, const struct stat *, void *), + void *p) +{ + dstr d = DSTR_INIT; + struct fhjunk fhj; + int rc; + + fhj.func = func; + fhj.p = p; + fhj.d = &d; + rc = fhjunk(&fhj, fh->ents); + dstr_destroy(&d); + return (rc); +} + /* --- @hfparse@ --- * * * Arguments: @hfpctx *hfp@ = pointer to the context structure