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(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 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_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);
}
p++;
}
+ } else {
+ p++;
}
*q++ = c;
- p++;
}
*q = '\0';
triebuild_add(ctx->tb, buf, &tf);
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) {
+ if (mkdir(outfile, 0777) < 0 && errno != EEXIST) {
+ fprintf(stderr, "%s: %s: mkdir: %s\n", PNAME,
+ outfile, strerror(errno));
+ return 1;
+ }
+ 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) {