static void dump_line(const char *pathname, const struct trie_file *tf)
{
const char *p;
- printf("%llu %llu ", tf->size, tf->atime);
+ if (printf("%llu %llu ", tf->size, tf->atime) < 0) goto error;
for (p = pathname; *p; p++) {
- if (*p >= ' ' && *p < 127 && *p != '%')
- putchar(*p);
- else
- printf("%%%02x", (unsigned char)*p);
+ if (*p >= ' ' && *p < 127 && *p != '%') {
+ if (putchar(*p) == EOF) goto error;
+ } else {
+ if (printf("%%%02x", (unsigned char)*p) < 0) goto error;
+ }
}
- putchar('\n');
+ if (putchar('\n') == EOF) goto error;
+ return;
+ error:
+ fatal("standard output: %s", strerror(errno));
}
static int gotdata(void *vctx, const char *pathname, const STRUCT_STAT *st)
if (ctx->usemtime || (ctx->fakeatimes && S_ISDIR(st->st_mode)))
file.atime = st->st_mtime;
else
- file.atime = st->st_atime;
+ file.atime = max(st->st_mtime, st->st_atime);
/*
* Filter based on wildcards.
}
static void text_query(const void *mappedfile, const char *querydir,
- time_t t, int depth)
+ time_t t, int showfiles, int depth, FILE *fp)
{
size_t maxpathlen;
char *pathbuf;
unsigned long xi1, xi2;
- unsigned long long s1, s2;
+ unsigned long long size;
maxpathlen = trie_maxpathlen(mappedfile);
pathbuf = snewn(maxpathlen + 1, char);
xi1 = trie_before(mappedfile, querydir);
xi2 = trie_before(mappedfile, pathbuf);
- if (xi2 - xi1 == 1)
+ if (!showfiles && xi2 - xi1 == 1)
return; /* file, or empty dir => no display */
/*
* Now do the lookups in the age index.
*/
- s1 = index_query(mappedfile, xi1, t);
- s2 = index_query(mappedfile, xi2, t);
+ if (xi2 - xi1 == 1) {
+ /*
+ * We are querying an individual file, so we should not
+ * depend on the index entries either side of the node,
+ * since they almost certainly don't both exist. Instead,
+ * just look up the file's size and atime in the main trie.
+ */
+ const struct trie_file *f = trie_getfile(mappedfile, xi1);
+ if (f->atime < t)
+ size = f->size;
+ else
+ size = 0;
+ } else {
+ unsigned long long s1, s2;
+ s1 = index_query(mappedfile, xi1, t);
+ s2 = index_query(mappedfile, xi2, t);
+ size = s2 - s1;
+ }
- if (s1 == s2)
+ if (size == 0)
return; /* no space taken up => no display */
- if (depth > 0) {
+ if (depth != 0) {
/*
* Now scan for first-level subdirectories and report
* those too.
*/
+ int newdepth = (depth > 0 ? depth - 1 : depth);
xi1++;
while (xi1 < xi2) {
trie_getpath(mappedfile, xi1, pathbuf);
- text_query(mappedfile, pathbuf, t, depth-1);
+ text_query(mappedfile, pathbuf, t, showfiles, newdepth, fp);
make_successor(pathbuf);
xi1 = trie_before(mappedfile, pathbuf);
}
}
/* Display in units of 1Kb */
- printf("%-11llu %s\n", (s2 - s1) / 1024, querydir);
+ fprintf(fp, "%-11llu %s\n", (size) / 1024, querydir);
}
/*
HELPOPT("[--scan,--load] fake atimes on directories") \
NOVAL(MTIME) LONG(mtime) \
HELPOPT("[--scan] use mtime instead of atime") \
- NOVAL(FULL) LONG(full_index) \
- HELPOPT("[--scan] index every file individually") \
+ NOVAL(SHOWFILES) LONG(files) \
+ HELPOPT("[--web,--html,--text] list individual files") \
VAL(AGERANGE) SHORT(r) LONG(age_range) LONG(range) LONG(ages) \
HELPARG("age[-age]") HELPOPT("[--web,--html] set limits of colour coding") \
+ VAL(OUTFILE) SHORT(o) LONG(output) \
+ HELPARG("filename") HELPOPT("[--html] specify output file or directory name") \
VAL(SERVERADDR) LONG(address) LONG(addr) LONG(server_address) \
LONG(server_addr) \
HELPARG("addr[:port]") HELPOPT("[--web] specify HTTP server address") \
HELPARG("filename") HELPOPT("[--web] read HTTP Basic user/pass from file") \
VAL(AUTHFD) LONG(auth_fd) \
HELPARG("fd") HELPOPT("[--web] read HTTP Basic user/pass from fd") \
- VAL(TQDEPTH) SHORT(d) LONG(depth) LONG(max_depth) LONG(maximum_depth) \
- HELPARG("levels") HELPOPT("[--text] recurse to this many levels") \
+ VAL(DEPTH) SHORT(d) LONG(depth) LONG(max_depth) LONG(maximum_depth) \
+ HELPARG("levels") HELPOPT("[--text,--html] recurse to this many levels") \
VAL(MINAGE) SHORT(a) LONG(age) LONG(min_age) LONG(minimum_age) \
HELPARG("age") HELPOPT("[--text] include only files older than this") \
HELPPFX("also") \
const char *httpserveraddr = NULL;
int httpserverport = 0;
const char *httpauthdata = NULL;
+ const char *outfile = NULL;
int auth = HTTPD_AUTH_MAGIC | HTTPD_AUTH_BASIC;
int progress = 1;
struct inclusion_exclusion *inex = NULL;
int ninex = 0, inexsize = 0;
int crossfs = 0;
- int tqdepth = 1;
+ int depth = -1, gotdepth = 0;
int fakediratimes = 1;
int mtime = 0;
- int fullindex = 0;
+ int showfiles = 0;
#ifdef DEBUG_MAD_OPTION_PARSING_MACROS
{
case OPT_NODIRATIME:
fakediratimes = 1;
break;
+ case OPT_SHOWFILES:
+ showfiles = 1;
+ break;
case OPT_MTIME:
mtime = 1;
break;
- case OPT_FULL:
- fullindex = 1;
- break;
case OPT_DATAFILE:
filename = optval;
break;
- case OPT_TQDEPTH:
- tqdepth = atoi(optval);
+ case OPT_DEPTH:
+ if (!strcasecmp(optval, "unlimited") ||
+ !strcasecmp(optval, "infinity") ||
+ !strcasecmp(optval, "infinite") ||
+ !strcasecmp(optval, "inf") ||
+ !strcasecmp(optval, "maximum") ||
+ !strcasecmp(optval, "max"))
+ depth = -1;
+ else
+ depth = atoi(optval);
+ gotdepth = 1;
+ break;
+ case OPT_OUTFILE:
+ outfile = optval;
break;
case OPT_MINAGE:
textcutoff = parse_age(now, optval);
ctx->progress = progress;
{
struct winsize ws;
- if (progress && ioctl(2, TIOCGWINSZ, &ws) == 0)
+ if (progress &&
+ ioctl(2, TIOCGWINSZ, &ws) == 0 &&
+ ws.ws_col > 0)
ctx->progwidth = ws.ws_col - 1;
else
ctx->progwidth = 79;
}
p++;
}
+ } else {
+ p++;
}
*q++ = c;
- p++;
}
*q = '\0';
triebuild_add(ctx->tb, buf, &tf);
}
if (mode != SCANDUMP) {
size_t maxpathlen;
+ size_t delta;
char *buf, *prevbuf;
count = triebuild_finish(ctx->tb);
return 1;
}
- printf("Built pathname index, %d entries, %llu bytes\n", count,
+ printf("Built pathname index, %d entries,"
+ " %llu bytes of index\n", count,
(unsigned long long)st.st_size);
- totalsize = index_compute_size(st.st_size, count);
+ totalsize = index_initial_size(st.st_size, count);
+ totalsize += totalsize / 10;
if (lseek(fd, totalsize-1, SEEK_SET) < 0) {
perror(PNAME ": lseek");
return 1;
}
- printf("Upper bound on index file size = %llu bytes\n",
- (unsigned long long)totalsize);
-
mappedfile = mmap(NULL, totalsize, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
if (!mappedfile) {
perror(PNAME ": mmap");
}
printf("Building index\n");
- ib = indexbuild_new(mappedfile, st.st_size, count);
+ ib = indexbuild_new(mappedfile, st.st_size, count, &delta);
maxpathlen = trie_maxpathlen(mappedfile);
buf = snewn(maxpathlen, char);
prevbuf = snewn(maxpathlen, char);
while (1) {
int i;
+ if (totalsize - indexbuild_realsize(ib) < delta) {
+ const void *oldfile = mappedfile;
+ ptrdiff_t diff;
+
+ /*
+ * Unmap the file, grow it, and remap it.
+ */
+ munmap(mappedfile, totalsize);
+
+ totalsize += delta;
+ totalsize += totalsize / 10;
+
+ if (lseek(fd, totalsize-1, SEEK_SET) < 0) {
+ perror(PNAME ": lseek");
+ return 1;
+ }
+ if (write(fd, "\0", 1) < 1) {
+ perror(PNAME ": write");
+ return 1;
+ }
+
+ mappedfile = mmap(NULL, totalsize, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
+ if (!mappedfile) {
+ perror(PNAME ": mmap");
+ return 1;
+ }
+
+ indexbuild_rebase(ib, mappedfile);
+ triewalk_rebase(tw, mappedfile);
+ diff = (const unsigned char *)mappedfile -
+ (const unsigned char *)oldfile;
+ if (prevtf)
+ prevtf = (const struct trie_file *)
+ (((const unsigned char *)prevtf) + diff);
+ if (tf)
+ tf = (const struct trie_file *)
+ (((const unsigned char *)tf) + diff);
+ }
+
/*
* Get the next file from the index. So we are
* currently holding, and have not yet
indexbuild_tag(ib);
break;
}
-
- /*
- * In full-index mode, index everything.
- */
- if (fullindex)
- indexbuild_tag(ib);
/*
* If prevbuf was a filename inside some
munmap(mappedfile, totalsize);
ftruncate(fd, realsize);
close(fd);
- printf("Actual index file size = %llu bytes\n",
+ printf("Final index file size = %llu bytes\n",
(unsigned long long)realsize);
}
} else if (mode == TEXT) {
if (pathlen > 0 && querydir[pathlen-1] == pathsep)
querydir[--pathlen] = '\0';
- text_query(mappedfile, querydir, textcutoff, tqdepth);
+ if (!gotdepth)
+ depth = 1; /* default for text mode */
+ if (outfile != NULL) {
+ FILE *fp = fopen(outfile, "w");
+ if (!fp) {
+ fprintf(stderr, "%s: %s: open: %s\n", PNAME,
+ outfile, strerror(errno));
+ return 1;
+ }
+ text_query(mappedfile, querydir, textcutoff, showfiles,
+ depth, fp);
+ fclose(fp);
+ } else {
+ text_query(mappedfile, querydir, textcutoff, showfiles,
+ depth, stdout);
+ }
munmap(mappedfile, totalsize);
} else if (mode == HTML) {
char *querydir = actions[action].arg;
- size_t pathlen;
+ size_t pathlen, maxpathlen;
+ char *pathbuf;
struct html_config cfg;
unsigned long xi;
char *html;
}
pathsep = trie_pathsep(mappedfile);
+ maxpathlen = trie_maxpathlen(mappedfile);
+ pathbuf = snewn(maxpathlen, char);
+
/*
* Trim trailing slash, just in case.
*/
querydir[--pathlen] = '\0';
xi = trie_before(mappedfile, querydir);
- cfg.format = NULL;
- cfg.autoage = htmlautoagerange;
- cfg.oldest = htmloldest;
- cfg.newest = htmlnewest;
- html = html_query(mappedfile, xi, &cfg);
- fputs(html, stdout);
+ if (xi >= trie_count(mappedfile) ||
+ (trie_getpath(mappedfile, xi, pathbuf),
+ strcmp(pathbuf, querydir))) {
+ fprintf(stderr, "%s: pathname '%s' does not exist in index\n"
+ "%*s(check it is spelled exactly as it is in the "
+ "index, including\n%*sany leading './')\n",
+ PNAME, querydir,
+ (int)(1+sizeof(PNAME)), "",
+ (int)(1+sizeof(PNAME)), "");
+ } else if (!index_has_root(mappedfile, xi)) {
+ fprintf(stderr, "%s: pathname '%s' is"
+ " a file, not a directory\n", PNAME, querydir);
+ } else if (!gotdepth) {
+ /*
+ * Single output file.
+ */
+ cfg.format = NULL;
+ cfg.rootpage = NULL;
+ cfg.autoage = htmlautoagerange;
+ cfg.oldest = htmloldest;
+ cfg.newest = htmlnewest;
+ cfg.showfiles = showfiles;
+ html = html_query(mappedfile, xi, &cfg, 0);
+ if (outfile != NULL) {
+ FILE *fp = fopen(outfile, "w");
+ if (!fp) {
+ fprintf(stderr, "%s: %s: open: %s\n", PNAME,
+ outfile, strerror(errno));
+ return 1;
+ } else if (fputs(html, fp) < 0) {
+ fprintf(stderr, "%s: %s: write: %s\n", PNAME,
+ outfile, strerror(errno));
+ fclose(fp);
+ return 1;
+ } else if (fclose(fp) < 0) {
+ fprintf(stderr, "%s: %s: fclose: %s\n", PNAME,
+ outfile, strerror(errno));
+ return 1;
+ }
+ } else {
+ fputs(html, stdout);
+ }
+ } else {
+ /*
+ * Multiple output files.
+ */
+ int dirlen = outfile ? 2+strlen(outfile) : 3;
+ char prefix[dirlen];
+ if (outfile)
+ snprintf(prefix, dirlen, "%s/", outfile);
+ else
+ snprintf(prefix, dirlen, "./");
+
+ unsigned long xi2;
+ make_successor(pathbuf);
+ xi2 = trie_before(mappedfile, pathbuf);
+
+ cfg.format = "%lu.html";
+ cfg.rootpage = "index.html";
+ cfg.autoage = htmlautoagerange;
+ cfg.oldest = htmloldest;
+ cfg.newest = htmlnewest;
+ cfg.showfiles = showfiles;
+ if (html_dump(mappedfile, xi, xi2, depth, &cfg, prefix))
+ return 1;
+ }
munmap(mappedfile, totalsize);
+ sfree(pathbuf);
} else if (mode == DUMP) {
size_t maxpathlen;
char *buf;
dcfg.port = httpserverport;
dcfg.basicauthdata = httpauthdata;
pcfg.format = NULL;
+ pcfg.rootpage = NULL;
pcfg.autoage = htmlautoagerange;
pcfg.oldest = htmloldest;
pcfg.newest = htmlnewest;
+ pcfg.showfiles = showfiles;
run_httpd(mappedfile, auth, &dcfg, &pcfg);
munmap(mappedfile, totalsize);
} else if (mode == REMOVE) {