From: simon Date: Mon, 22 Feb 2010 19:03:08 +0000 (+0000) Subject: Make the existing -d (depth) option apply to the -H (static HTML X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/agedu/commitdiff_plain/00c5e40cdda24456519762da186538e34b81968a Make the existing -d (depth) option apply to the -H (static HTML report) mode, transforming its output from a single HTML file giving a report for one directory with no crosslinks to a collection of HTML files with crosslinks between them. I've also added a special 'max' value to -d (meaning no depth limit), which also works when used in text mode; I've added -o to specify an output file for both text and HTML report modes; and I've also documented -d, which I had previously forgotten to do (but nobody seems to have noticed :-). git-svn-id: svn://svn.tartarus.org/sgt/agedu@8880 cda61777-01e9-0310-a592-d414129be87e --- diff --git a/agedu.but b/agedu.but index 1494140..6e5e5b0 100644 --- a/agedu.but +++ b/agedu.but @@ -190,10 +190,10 @@ completely) and a username and password of your choice. \dd In this mode, \cw{agedu} generates a textual report on standard output, listing the disk usage in the specified directory and all -its subdirectories down to a fixed depth. By default that depth is +its subdirectories down to a given depth. By default that depth is 1, so that you see a report for \e{directory} itself and all of its -immediate subdirectories. You can configure a different depth using -\cw{-d}, described in the next section. +immediate subdirectories. You can configure a different depth (or no +depth limit) using \cw{-d}, described in the next section. \lcont{ @@ -257,9 +257,18 @@ for further detail.) \dd In this mode, \cw{agedu} will generate an HTML report of the disk usage in the specified directory and its immediate subdirectories, in the same form that it serves from its web server -in \cw{-w} mode. However, this time, a single HTML report will be -generated and simply written to standard output, with no hyperlinks -pointing to other similar pages. +in \cw{-w} mode. + +\lcont{ + +By default, a single HTML report will be generated and simply +written to standard output, with no hyperlinks pointing to other +similar pages. If you also specify the \cw{-d} option (see below), +\cw{agedu} will instead write out a collection of HTML files with +hyperlinks between them, and call the top-level file +\cw{index.html}. + +} \U OPTIONS @@ -467,6 +476,42 @@ the web server mode \cw{-w}, the stand-alone HTML generation mode files in each directory, instead of just giving a combined report for everything that's not in a subdirectory. +The following options affect the stand-alone HTML generation mode +\cw{-H} and the text report mode \cw{-t}. + +\dt \cw{-d} \e{depth} or \cw{--depth} \e{depth} + +\dd This option controls the maximum depth to which \cw{agedu} +recurses when generating a text or HTML report. + +\lcont{ + +In text mode, the default is 1, meaning that the report will include +the directory given on the command line and all of its immediate +subdirectories. A depth of two includes another level below that, +and so on; a depth of zero means \e{only} the directory on the +command line. + +In HTML mode, specifying this option switches \cw{agedu} from +writing out a single HTML file to writing out multiple files which +link to each other. A depth of 1 means \cw{agedu} will write out an +HTML file for the given directory and also one for each of its +immediate subdirectories. + +If you want \cw{agedu} to recurse as deeply as possible, give the +special word \cq{max} as an argument to \cw{-d}. + +} + +\dt \cw{-o} \e{filename} or \cw{--output} \e{filename} + +\dd This option is used to specify an output file for \cw{agedu} to +write its report to. In text mode or single-file HTML mode, the +argument is treated as the name of a file. In multiple-file HTML +mode, the argument is treated as the name of a directory: the +directory will be created if it does not already exist, and the +output HTML files will be created inside it. + The following options affect the web server mode \cw{-w}, and in one case also the stand-alone HTML generation mode \cw{-H}: diff --git a/agedu.c b/agedu.c index b253f18..463104f 100644 --- a/agedu.c +++ b/agedu.c @@ -163,7 +163,7 @@ static void scan_error(void *vctx, const char *fmt, ...) } static void text_query(const void *mappedfile, const char *querydir, - time_t t, int showfiles, int depth) + time_t t, int showfiles, int depth, FILE *fp) { size_t maxpathlen; char *pathbuf; @@ -211,22 +211,23 @@ static void text_query(const void *mappedfile, const char *querydir, 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, showfiles, 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", (size) / 1024, querydir); + fprintf(fp, "%-11llu %s\n", (size) / 1024, querydir); } /* @@ -344,6 +345,8 @@ static void text_query(const void *mappedfile, const char *querydir, 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") \ @@ -354,8 +357,8 @@ static void text_query(const void *mappedfile, const char *querydir, 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") \ @@ -496,12 +499,13 @@ int main(int argc, char **argv) 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; @@ -764,8 +768,20 @@ int main(int argc, char **argv) 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); @@ -1250,7 +1266,22 @@ int main(int argc, char **argv) if (pathlen > 0 && querydir[pathlen-1] == pathsep) querydir[--pathlen] = '\0'; - text_query(mappedfile, querydir, textcutoff, showfiles, 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) { @@ -1302,14 +1333,59 @@ int main(int argc, char **argv) } else if (!index_has_root(mappedfile, xi)) { fprintf(stderr, "%s: pathname '%s' is" " a file, not a directory\n", PNAME, querydir); - } else { + } 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); - fputs(html, stdout); + 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); @@ -1372,6 +1448,7 @@ int main(int argc, char **argv) dcfg.port = httpserverport; dcfg.basicauthdata = httpauthdata; pcfg.format = NULL; + pcfg.rootpage = NULL; pcfg.autoage = htmlautoagerange; pcfg.oldest = htmloldest; pcfg.newest = htmlnewest; diff --git a/html.c b/html.c index dacd68c..8293374 100644 --- a/html.c +++ b/html.c @@ -18,7 +18,7 @@ struct html { char *path2; char *href; size_t hreflen; - const char *format; + const char *format, *rootpage; unsigned long long thresholds[MAXCOLOUR]; char *titletexts[MAXCOLOUR+1]; time_t now; @@ -334,6 +334,16 @@ static void compute_display_size(unsigned long long size, *fmt = fmts[shift]; } +static void make_filename(char *buf, size_t buflen, + const char *format, const char *rootpage, + unsigned long index) +{ + if (index == 0 && rootpage) + snprintf(buf, buflen, "%s", rootpage); + else + snprintf(buf, buflen, format, index); +} + #define PIXEL_SIZE 600 /* FIXME: configurability? */ static void write_report_line(struct html *ctx, struct vector *vec) { @@ -399,7 +409,9 @@ static void write_report_line(struct html *ctx, struct vector *vec) int doing_href = 0; if (ctx->format && vec->want_href) { - snprintf(ctx->href, ctx->hreflen, ctx->format, vec->index); + make_filename(ctx->href, ctx->hreflen, + ctx->format, ctx->rootpage, + vec->index); htprintf(ctx, "", ctx->href); doing_href = 1; } @@ -427,7 +439,7 @@ int strcmptrailingpathsep(const char *a, const char *b) } char *html_query(const void *t, unsigned long index, - const struct html_config *cfg) + const struct html_config *cfg, int downlink) { struct html actx, *ctx = &actx; char *path, *path2, *p, *q, *href; @@ -446,6 +458,7 @@ char *html_query(const void *t, unsigned long index, ctx->buflen = ctx->bufsize = 0; ctx->t = t; ctx->format = cfg->format; + ctx->rootpage = cfg->rootpage; htprintf(ctx, "\n"); path = snewn(1+trie_maxpathlen(t), char); @@ -502,7 +515,7 @@ char *html_query(const void *t, unsigned long index, index2 = trie_before(t, path); trie_getpath(t, index2, path2); if (!strcmptrailingpathsep(path, path2) && cfg->format) { - snprintf(href, hreflen, cfg->format, index2); + make_filename(href, hreflen, cfg->format, cfg->rootpage, index2); if (!*href) /* special case that we understand */ strcpy(href, "./"); htprintf(ctx, "", href); @@ -611,7 +624,7 @@ char *html_query(const void *t, unsigned long index, vecs = sresize(vecs, vecsize, struct vector *); } assert(strlen(path2) > pathlen); - vecs[nvecs] = make_vector(ctx, path2, (xj2 - xj1 > 1), 0, + vecs[nvecs] = make_vector(ctx, path2, downlink && (xj2 - xj1 > 1), 0, path2 + subdirpos, 1); for (i = 0; i <= MAXCOLOUR; i++) vecs[0]->sizes[i] -= vecs[nvecs]->sizes[i]; @@ -644,3 +657,68 @@ char *html_query(const void *t, unsigned long index, return ctx->buf; } + +int html_dump(const void *t, unsigned long index, unsigned long endindex, + int maxdepth, const struct html_config *cfg, + const char *pathprefix) +{ + /* + * Determine the filename for this file. + */ + assert(cfg->format != NULL); + int prefixlen = strlen(pathprefix); + int fnmax = strlen(pathprefix) + strlen(cfg->format) + 100; + char filename[fnmax]; + strcpy(filename, pathprefix); + make_filename(filename + prefixlen, fnmax - prefixlen, + cfg->format, cfg->rootpage, index); + + /* + * Create the HTML itself. Don't write out downlinks from our + * deepest level. + */ + char *html = html_query(t, index, cfg, maxdepth != 0); + + /* + * Write it out. + */ + FILE *fp = fopen(filename, "w"); + if (!fp) { + fprintf(stderr, "%s: %s: open: %s\n", PNAME, + filename, strerror(errno)); + return 1; + } + if (fputs(html, fp) < 0) { + fprintf(stderr, "%s: %s: write: %s\n", PNAME, + filename, strerror(errno)); + fclose(fp); + return 1; + } + if (fclose(fp) < 0) { + fprintf(stderr, "%s: %s: fclose: %s\n", PNAME, + filename, strerror(errno)); + return 1; + } + + /* + * Recurse. + */ + if (maxdepth != 0) { + unsigned long subindex, subendindex; + int newdepth = (maxdepth > 0 ? maxdepth - 1 : maxdepth); + char path[1+trie_maxpathlen(t)]; + + index++; + while (index < endindex) { + trie_getpath(t, index, path); + get_indices(t, path, &subindex, &subendindex); + index = subendindex; + if (subendindex - subindex > 1) { + if (html_dump(t, subindex, subendindex, newdepth, + cfg, pathprefix)) + return 1; + } + } + } + return 0; +} diff --git a/html.h b/html.h index d99af0a..dc90c4b 100644 --- a/html.h +++ b/html.h @@ -14,6 +14,13 @@ struct html_config { const char *format; /* + * If "rootpage" is non-NULL, it overrides "format" to give a + * special name (e.g. "index.html") to the top-level page of the + * index. + */ + const char *rootpage; + + /* * Time stamps to assign to the extreme ends of the colour * scale. If "autoage" is true, they are ignored and the time * stamps are derived from the limits of the age data stored @@ -34,6 +41,34 @@ struct html_config { * against the pathname at a given index. Returns a dynamically * allocated piece of memory containing the entire HTML document, * as an ordinary C zero-terminated string. + * + * 'downlink' is TRUE if hyperlinks should be generated for + * subdirectories. (This can also be disabled by setting cfg->format + * to NULL, but that also disables the upward hyperlinks to parent + * directories. Setting cfg->format to non-NULL but downlink to NULL + * will generate uplinks but no downlinks.) */ char *html_query(const void *t, unsigned long index, - const struct html_config *cfg); + const struct html_config *cfg, int downlink); + +/* + * Recursively output a dump of lots of HTML files which crosslink + * to each other. cfg->format and cfg->rootpath will be used to + * generate the filenames for both the hyperlinks and the output + * file names; the file names will have "pathprefix" prepended to + * them before being opened. + * + * "index" and "endindex" point to the region of index file that + * should be generated by the dump, which must be a subdirectory. + * + * "maxdepth" limits the depth of recursion. Setting it to zero + * outputs only one page, 1 outputs the current directory and its + * immediate children but no further, and so on. Making it negative + * gives unlimited depth. + * + * Return value is 0 on success, or 1 if an error occurs during + * output. + */ +int html_dump(const void *t, unsigned long index, unsigned long endindex, + int maxdepth, const struct html_config *cfg, + const char *pathprefix); diff --git a/httpd.c b/httpd.c index e5ee270..90d4e42 100644 --- a/httpd.c +++ b/httpd.c @@ -277,7 +277,7 @@ char *got_data(struct connctx *ctx, char *data, int length, ret = http_error("404", "Not Found", NULL, "This is not a valid pathname index."); } else { - document = html_query(ctx->t, index, cfg); + document = html_query(ctx->t, index, cfg, 1); if (document) { ret = http_success("text/html", 1, document); sfree(document);