2 * html.c: implementation of html.h.
15 size_t buflen
, bufsize
;
17 unsigned long long totalsize
, oldest
, newest
;
21 const char *uriformat
;
22 unsigned long long thresholds
[MAXCOLOUR
];
23 char *titletexts
[MAXCOLOUR
+1];
27 static void vhtprintf(struct html
*ctx
, const char *fmt
, va_list ap
)
35 * Some C libraries (Solaris, I'm looking at you) don't like
36 * an output buffer size of zero in vsnprintf, but will return
37 * sensible values given any non-zero buffer size. Hence, we
38 * use testbuf to gauge the length of the string.
40 size
= vsnprintf(testbuf
, 1, fmt
, ap2
);
43 if (ctx
->buflen
+ size
>= ctx
->bufsize
) {
44 ctx
->bufsize
= (ctx
->buflen
+ size
) * 3 / 2 + 1024;
45 ctx
->buf
= sresize(ctx
->buf
, ctx
->bufsize
, char);
47 size2
= vsnprintf(ctx
->buf
+ ctx
->buflen
, ctx
->bufsize
- ctx
->buflen
,
49 assert(size
== size2
);
53 static void htprintf(struct html
*ctx
, const char *fmt
, ...)
57 vhtprintf(ctx
, fmt
, ap
);
61 static unsigned long long round_and_format_age(struct html
*ctx
,
62 unsigned long long age
,
63 char *buf
, int direction
)
67 unsigned long long ret
, newret
;
70 static const int minutes
[] = { 5, 10, 15, 30, 45 };
72 tm
= *localtime(&ctx
->now
);
73 ym
= tm
.tm_year
* 12 + tm
.tm_mon
;
78 for (i
= 0; i
< lenof(minutes
); i
++) {
79 newret
= ctx
->now
- minutes
[i
] * 60;
80 sprintf(newbuf
, "%d minutes", minutes
[i
]);
87 for (i
= 1; i
< 24; i
++) {
88 newret
= ctx
->now
- i
* (60*60);
89 sprintf(newbuf
, "%d hour%s", i
, i
==1 ?
"" : "s");
96 for (i
= 1; i
< 7; i
++) {
97 newret
= ctx
->now
- i
* (24*60*60);
98 sprintf(newbuf
, "%d day%s", i
, i
==1 ?
"" : "s");
105 for (i
= 1; i
< 4; i
++) {
106 newret
= ctx
->now
- i
* (7*24*60*60);
107 sprintf(newbuf
, "%d week%s", i
, i
==1 ?
"" : "s");
114 for (i
= 1; i
< 11; i
++) {
115 tm2
= tm
; /* structure copy */
116 tm2
.tm_year
= (ym
- i
) / 12;
117 tm2
.tm_mon
= (ym
- i
) % 12;
118 newret
= mktime(&tm2
);
119 sprintf(newbuf
, "%d month%s", i
, i
==1 ?
"" : "s");
127 tm2
= tm
; /* structure copy */
128 tm2
.tm_year
= (ym
- i
*12) / 12;
129 tm2
.tm_mon
= (ym
- i
*12) % 12;
130 newret
= mktime(&tm2
);
131 sprintf(newbuf
, "%d year%s", i
, i
==1 ?
"" : "s");
141 * Round toward newest, i.e. use the existing (buf,ret).
143 } else if (direction
< 0) {
145 * Round toward oldest, i.e. use (newbuf,newret);
153 if (ret
- age
> age
- newret
) {
161 static void get_indices(const void *t
, char *path
,
162 unsigned long *xi1
, unsigned long *xi2
)
164 size_t pathlen
= strlen(path
);
165 int c1
= path
[pathlen
], c2
= (pathlen
> 0 ? path
[pathlen
-1] : 0);
167 *xi1
= trie_before(t
, path
);
168 make_successor(path
);
169 *xi2
= trie_before(t
, path
);
172 path
[pathlen
-1] = c2
;
175 static unsigned long long fetch_size(const void *t
,
176 unsigned long xi1
, unsigned long xi2
,
177 unsigned long long atime
)
179 if (xi2
- xi1
== 1) {
181 * We are querying an individual file, so we should not
182 * depend on the index entries either side of the node,
183 * since they almost certainly don't both exist. Instead,
184 * just look up the file's size and atime in the main trie.
186 const struct trie_file
*f
= trie_getfile(t
, xi1
);
187 if (f
->atime
< atime
)
192 return index_query(t
, xi2
, atime
) - index_query(t
, xi1
, atime
);
196 static void htescape(struct html
*ctx
, const char *s
, int n
, int italics
)
198 while (n
> 0 && *s
) {
199 unsigned char c
= (unsigned char)*s
++;
202 htprintf(ctx
, "&");
204 htprintf(ctx
, "<");
206 htprintf(ctx
, ">");
207 else if (c
>= ' ' && c
< '\177')
208 htprintf(ctx
, "%c", c
);
210 if (italics
) htprintf(ctx
, "<i>");
211 htprintf(ctx
, "[%02x]", c
);
212 if (italics
) htprintf(ctx
, "</i>");
219 static void begin_colour_bar(struct html
*ctx
)
221 htprintf(ctx
, "<table cellspacing=0 cellpadding=0"
222 " style=\"border:0\">\n<tr>\n");
225 static void add_to_colour_bar(struct html
*ctx
, int colour
, int pixels
)
229 if (colour
>= 0 && colour
< 256) /* red -> yellow fade */
230 r
= 255, g
= colour
, b
= 0;
231 else if (colour
>= 256 && colour
<= 511) /* yellow -> green fade */
232 r
= 511 - colour
, g
= 255, b
= 0;
233 else /* background grey */
237 htprintf(ctx
, "<td style=\"width:%dpx; height:1em; "
238 "background-color:#%02x%02x%02x\"",
241 htprintf(ctx
, " title=\"%s\"", ctx
->titletexts
[colour
]);
242 htprintf(ctx
, "></td>\n");
246 static void end_colour_bar(struct html
*ctx
)
248 htprintf(ctx
, "</tr>\n</table>\n");
252 int want_href
, essential
;
254 int literal
; /* should the name be formatted in fixed-pitch? */
256 unsigned long long sizes
[MAXCOLOUR
+1];
259 int vec_compare(const void *av
, const void *bv
)
261 const struct vector
*a
= *(const struct vector
**)av
;
262 const struct vector
*b
= *(const struct vector
**)bv
;
264 if (a
->sizes
[MAXCOLOUR
] > b
->sizes
[MAXCOLOUR
])
266 else if (a
->sizes
[MAXCOLOUR
] < b
->sizes
[MAXCOLOUR
])
268 else if (a
->want_href
< b
->want_href
)
270 else if (a
->want_href
> b
->want_href
)
272 else if (a
->want_href
)
273 return strcmp(a
->name
, b
->name
);
274 else if (a
->index
< b
->index
)
276 else if (a
->index
> b
->index
)
278 else if (a
->essential
< b
->essential
)
280 else if (a
->essential
> b
->essential
)
285 static struct vector
*make_vector(struct html
*ctx
, char *path
,
286 int want_href
, int essential
,
287 char *name
, int literal
)
289 unsigned long xi1
, xi2
;
290 struct vector
*vec
= snew(struct vector
);
293 vec
->want_href
= want_href
;
294 vec
->essential
= essential
;
295 vec
->name
= name ?
dupstr(name
) : NULL
;
296 vec
->literal
= literal
;
298 get_indices(ctx
->t
, path
, &xi1
, &xi2
);
302 for (i
= 0; i
<= MAXCOLOUR
; i
++) {
303 unsigned long long atime
;
307 atime
= ctx
->thresholds
[i
];
308 vec
->sizes
[i
] = fetch_size(ctx
->t
, xi1
, xi2
, atime
);
314 static void print_heading(struct html
*ctx
, const char *title
)
316 htprintf(ctx
, "<tr style=\"padding: 0.2em; background-color:#e0e0e0\">\n"
317 "<td colspan=4 align=center>%s</td>\n</tr>\n", title
);
320 static void compute_display_size(unsigned long long size
,
321 const char **fmt
, double *display_size
)
323 static const char *const fmts
[] = {
324 "%g b", "%g Kb", "%#.1f Mb", "%#.1f Gb", "%#.1f Tb",
325 "%#.1f Pb", "%#.1f Eb", "%#.1f Zb", "%#.1f Yb"
328 unsigned long long tmpsize
;
333 while (tmpsize
>= 1024 && shift
< lenof(fmts
)-1) {
335 denominator
*= 1024.0;
338 *display_size
= size
/ denominator
;
342 struct format_option
{
343 const char *prefix
, *suffix
; /* may include '%%' */
344 int prefixlen
, suffixlen
; /* does not count '%%' */
345 char fmttype
; /* 0 for none, or 'n' or 'p' */
346 int translate_pathsep
; /* pathsep rendered as '/'? */
347 int shorten_path
; /* omit common prefix? */
351 * Gets the next format option from a format string. Advances '*fmt'
352 * past it, or sets it to NULL if nothing is left.
354 struct format_option
get_format_option(const char **fmt
)
356 struct format_option ret
;
359 * Scan for prefix of format.
366 * No formatting directive, and this is the last option.
373 } else if (**fmt
== '%') {
374 if ((*fmt
)[1] == '%') {
375 (*fmt
) += 2; /* just advance one extra */
377 } else if ((*fmt
)[1] == '|') {
379 * No formatting directive.
384 (*fmt
) += 2; /* advance to start of next option */
390 (*fmt
)++; /* normal character */
396 * Interpret formatting directive with flags.
399 ret
.translate_pathsep
= ret
.shorten_path
= 1;
404 ret
.translate_pathsep
= 0;
405 } else if (c
== '-') {
406 ret
.shorten_path
= 0;
408 assert(c
== 'n' || c
== 'p');
422 * This is the last option.
426 } else if (**fmt
!= '%') {
427 (*fmt
)++; /* normal character */
430 if ((*fmt
)[1] == '%') {
431 (*fmt
) += 2; /* just advance one extra */
434 assert((*fmt
)[1] == '|');
435 (*fmt
) += 2; /* advance to start of next option */
442 char *format_string_inner(const char *fmt
, int nescape
,
443 unsigned long index
, const void *t
)
446 char *ret
= NULL
, *p
= NULL
;
447 char *path
= NULL
, *q
= NULL
;
448 char pathsep
= trie_pathsep(t
);
449 int maxpathlen
= trie_maxpathlen(t
);
453 struct format_option opt
= get_format_option(&fmt
);
454 if (index
&& !opt
.fmttype
)
455 continue; /* option is only good for the root, which this isn't */
457 maxlen
= opt
.prefixlen
+ opt
.suffixlen
+ 1;
458 switch (opt
.fmttype
) {
460 maxlen
+= 40; /* generous length for an integer */
463 maxlen
+= 3*maxpathlen
; /* might have to escape everything */
466 ret
= snewn(maxlen
, char);
468 while (opt
.prefixlen
-- > 0) {
469 if ((*p
++ = *opt
.prefix
++) == '%')
472 switch (opt
.fmttype
) {
474 p
+= sprintf(p
, "%lu", index
);
477 path
= snewn(1+trie_maxpathlen(t
), char);
478 if (opt
.shorten_path
) {
479 trie_getpath(t
, 0, path
);
480 q
= path
+ strlen(path
);
481 trie_getpath(t
, index
, path
);
485 trie_getpath(t
, index
, path
);
491 if (c
== pathsep
&& opt
.translate_pathsep
) {
494 } else if (charindex
< nescape
||
495 (!isalnum((unsigned char)c
) &&
496 ((charindex
== 0 && c
=='.') ||
497 !strchr("-.@_", c
)))) {
498 p
+= sprintf(p
, "=%02X", (unsigned char)c
);
508 while (opt
.suffixlen
-- > 0) {
509 if ((*p
++ = *opt
.suffix
++) == '%')
513 assert(p
- ret
< maxlen
);
516 assert(!"Getting here implies an incomplete set of formats");
519 int parse_path(const void *t
, const char *path
,
520 const char *fmt
, unsigned long *index
)
522 int len
= strlen(path
);
526 char pathsep
= trie_pathsep(t
);
529 struct format_option opt
= get_format_option(&fmt
);
532 * Check prefix and suffix.
534 midlen
= len
- opt
.prefixlen
- opt
.suffixlen
;
536 continue; /* prefix and suffix don't even fit */
539 while (opt
.prefixlen
> 0) {
540 char c
= *opt
.prefix
++;
548 if (opt
.prefixlen
> 0)
549 continue; /* prefix didn't match */
551 q
= path
+ len
- opt
.suffixlen
;
552 while (opt
.suffixlen
> 0) {
553 char c
= *opt
.suffix
++;
561 if (opt
.suffixlen
> 0)
562 continue; /* suffix didn't match */
565 * Check the data in between. p points at it, and it's midlen
568 if (opt
.fmttype
== '\0') {
571 * Successful match against a root format.
576 } else if (opt
.fmttype
== 'n') {
579 if (*p
>= '0' && *p
<= '9')
580 *index
= *index
* 10 + (*p
- '0');
588 * Successful match against a numeric format.
593 assert(opt
.fmttype
== 'p');
595 int maxoutlen
= trie_maxpathlen(t
) + 1;
596 int maxinlen
= midlen
+ 1;
597 char triepath
[maxinlen
+maxoutlen
];
599 if (opt
.shorten_path
) {
600 trie_getpath(t
, 0, triepath
);
601 r
= triepath
+ strlen(triepath
);
602 if (r
> triepath
&& r
[-1] != pathsep
)
609 if (*p
== '/' && opt
.translate_pathsep
) {
613 } else if (*p
== '=') {
615 * We intentionally do not check whether the
616 * escaped character _should_ have been escaped
617 * according to the rules in html_format_path.
619 * All clients of this parsing function, after a
620 * successful parse, call html_format_path to find
621 * the canonical URI for the same index and return
622 * an HTTP redirect if the provided URI was not
623 * exactly equal to that canonical form. This is
624 * critical when the correction involves adding or
625 * removing a trailing slash (because then
626 * relative hrefs on the generated page can be
627 * computed with respect to the canonical URI
628 * instead of having to remember what the actual
629 * URI was), but also has the useful effect that
630 * if a user attempts to type in (guess) a URI by
631 * hand they don't have to remember the escaping
632 * rules - as long as they type _something_ that
633 * this code can parse into a recognisable
634 * pathname, it will be automatically 301ed into
635 * the canonical form.
638 !isxdigit((unsigned char)p
[1]) ||
639 !isxdigit((unsigned char)p
[2]))
640 break; /* faulty escape encoding */
646 sscanf(x
, "%x", &cval
);
657 continue; /* something went wrong in that loop */
658 assert(r
- triepath
< maxinlen
+maxoutlen
);
661 unsigned long gotidx
= trie_before(t
, triepath
);
662 if (gotidx
>= trie_count(t
))
663 continue; /* index out of range */
664 char retpath
[1+maxoutlen
];
665 trie_getpath(t
, gotidx
, retpath
);
666 if (strcmp(triepath
, retpath
))
667 continue; /* exact path not found in trie */
668 if (!index_has_root(t
, gotidx
))
669 continue; /* path is not a directory */
672 * Successful path-based match.
679 return 0; /* no match from any format option */
682 char *format_string(const char *fmt
, unsigned long index
, const void *t
)
684 unsigned long indexout
, parseret
;
686 const char *stepfmt
= fmt
;
690 * Format the string using whichever format option first works.
692 ret
= format_string_inner(fmt
, 0, index
, t
);
695 * Now re-_parse_ the string, to see if it gives the same index
696 * back. It might not, if a pathname is valid in two formats: for
697 * instance, if you use '-H -d max' to generate a static HTML dump
698 * from scanning a directory which has a subdir called 'index',
699 * you might well find that the top-level file wants to be called
700 * index.html and so does the one for that subdir.
702 * We fix this by formatting the string again with more and more
703 * characters escaped, so that the non-root 'index.html' becomes
704 * (e.g.) '=69ndex.html', or '=69=6edex.html' if that doesn't
708 struct format_option opt
= get_format_option(&stepfmt
);
711 * Parse the pathname and see if it gives the right index.
713 int parseret
= parse_path(t
, ret
, fmt
, &indexout
);
714 assert(parseret
!= 0);
715 if (indexout
== index
)
716 break; /* path now parses successfully */
719 * If not, try formatting it again.
721 char *new = format_string_inner(fmt
, ++nescape
, index
, t
);
722 assert(strcmp(new, ret
)); /* if nescape gets too big, give up */
730 char *html_format_path(const void *t
, const struct html_config
*cfg
,
733 return format_string(cfg
->uriformat
, index
, t
);
736 int html_parse_path(const void *t
, const char *path
,
737 const struct html_config
*cfg
, unsigned long *index
)
739 return parse_path(t
, path
, cfg
->uriformat
, index
);
742 char *make_href(const char *source
, const char *target
)
745 * We insist that both source and target URIs start with a /, or
746 * else we won't be reliably able to construct relative hrefs
747 * between them (e.g. because we've got a suffix on the end of
748 * some CGI pathname that this function doesn't know the final
751 assert(*source
== '/');
752 assert(*target
== '/');
755 * Find the last / in source. Everything up to but not including
756 * that is the directory to which the output href will be
757 * relative. We enforce by assertion that there must be a /
758 * somewhere in source, or else we can't construct a relative href
761 const char *sourceend
= strrchr(source
, '/');
762 assert(sourceend
!= NULL
);
765 * See how far the target URI agrees with the source one, up to
766 * and including that /.
768 const char *s
= source
, *t
= target
;
769 while (s
<= sourceend
&& *s
== *t
)
773 * We're only interested in agreement of complete path components,
774 * so back off until we're sitting just after a shared /.
776 while (s
> source
&& s
[-1] != '/')
781 * Now we need some number of levels of "../" to get from source
782 * to here, and then we just replicate the rest of 'target'.
785 while (s
<= sourceend
) {
790 int len
= 3*levels
+ strlen(t
);
792 /* One last special case: if target has no tail _and_ we
793 * haven't written out any "../". */
796 char *ret
= snewn(len
+1, char);
798 while (levels
-- > 0) {
808 #define PIXEL_SIZE 600 /* FIXME: configurability? */
809 static void write_report_line(struct html
*ctx
, struct vector
*vec
)
811 unsigned long long size
, asize
, divisor
;
815 const char *unitsfmt
;
818 * A line with literally zero space usage should not be
819 * printed at all if it's a link to a subdirectory (since it
820 * probably means the whole thing was excluded by some
821 * --exclude-path wildcard). If it's [files] or the top-level
822 * line, though, we must always print _something_, and in that
823 * case we must fiddle about to prevent divisions by zero in
826 if (!vec
->sizes
[MAXCOLOUR
] && !vec
->essential
)
828 divisor
= ctx
->totalsize
;
834 * Find the total size of this subdirectory.
836 size
= vec
->sizes
[MAXCOLOUR
];
837 compute_display_size(size
, &unitsfmt
, &display_size
);
838 htprintf(ctx
, "<tr>\n"
839 "<td style=\"padding: 0.2em; text-align: right\">");
840 htprintf(ctx
, unitsfmt
, display_size
);
841 htprintf(ctx
, "</td>\n");
844 * Generate a colour bar.
846 htprintf(ctx
, "<td style=\"padding: 0.2em\">\n");
847 begin_colour_bar(ctx
);
849 for (i
= 0; i
<= MAXCOLOUR
; i
++) {
850 asize
= vec
->sizes
[i
];
851 newpix
= asize
* PIXEL_SIZE
/ divisor
;
852 add_to_colour_bar(ctx
, i
, newpix
- pix
);
855 add_to_colour_bar(ctx
, -1, PIXEL_SIZE
- pix
);
857 htprintf(ctx
, "</td>\n");
860 * Output size as a percentage of totalsize.
862 htprintf(ctx
, "<td style=\"padding: 0.2em; text-align: right\">"
863 "%.2f%%</td>\n", (double)size
/ divisor
* 100.0);
866 * Output a subdirectory marker.
868 htprintf(ctx
, "<td style=\"padding: 0.2em\">");
872 if (ctx
->uriformat
&& vec
->want_href
) {
873 char *targeturi
= format_string(ctx
->uriformat
, vec
->index
,
875 char *link
= make_href(ctx
->oururi
, targeturi
);
876 htprintf(ctx
, "<a href=\"%s\">", link
);
882 htprintf(ctx
, "<code>");
883 htescape(ctx
, vec
->name
, strlen(vec
->name
), 1);
885 htprintf(ctx
, "</code>");
887 htprintf(ctx
, "</a>");
889 htprintf(ctx
, "</td>\n</tr>\n");
892 int strcmptrailingpathsep(const char *a
, const char *b
)
894 while (*a
== *b
&& *a
)
897 if ((*a
== pathsep
&& !a
[1] && !*b
) ||
898 (*b
== pathsep
&& !b
[1] && !*a
))
901 return (int)(unsigned char)*a
- (int)(unsigned char)*b
;
904 char *html_query(const void *t
, unsigned long index
,
905 const struct html_config
*cfg
, int downlink
)
907 struct html actx
, *ctx
= &actx
;
908 char *path
, *path2
, *p
, *q
;
909 char agebuf1
[80], agebuf2
[80];
910 size_t pathlen
, subdirpos
;
911 unsigned long index2
;
913 struct vector
**vecs
;
915 unsigned long xi1
, xi2
, xj1
, xj2
;
917 if (index
>= trie_count(t
))
921 ctx
->buflen
= ctx
->bufsize
= 0;
923 ctx
->uriformat
= cfg
->uriformat
;
924 htprintf(ctx
, "<html>\n");
926 path
= snewn(1+trie_maxpathlen(t
), char);
927 ctx
->path2
= path2
= snewn(1+trie_maxpathlen(t
), char);
929 ctx
->oururi
= format_string(cfg
->uriformat
, index
, t
);
936 htprintf(ctx
, "<head>\n");
937 trie_getpath(t
, index
, path
);
938 htprintf(ctx
, "<title>");
939 htescape(ctx
, cfg
->html_title
, strlen(cfg
->html_title
), 0);
941 htescape(ctx
, path
, strlen(path
), 0);
942 htprintf(ctx
, "</title>\n");
943 htprintf(ctx
, "</head>\n");
946 * Begin BODY section.
948 htprintf(ctx
, "<body>\n");
949 htprintf(ctx
, "<h3 align=center>Disk space breakdown by"
950 " last-access time</h3>\n");
953 * Show the pathname we're centred on, with hyperlinks to
954 * parent directories where available.
956 htprintf(ctx
, "<p align=center>\n<code>");
958 for (p
= strchr(path
, pathsep
); p
&& p
[1]; p
= strchr(p
, pathsep
)) {
963 * See if this path prefix exists in the trie. If so,
964 * generate a hyperlink.
967 if (p
== path
) /* special case for "/" at start */
974 index2
= trie_before(t
, path
);
975 trie_getpath(t
, index2
, path2
);
976 if (!strcmptrailingpathsep(path
, path2
) && cfg
->uriformat
) {
977 char *targeturi
= format_string(cfg
->uriformat
, index2
, t
);
978 char *link
= make_href(ctx
->oururi
, targeturi
);
979 htprintf(ctx
, "<a href=\"%s\">", link
);
985 htescape(ctx
, q
, zp
- q
, 1);
987 htprintf(ctx
, "</a>");
988 htescape(ctx
, zp
, p
- zp
, 1);
991 htescape(ctx
, q
, strlen(q
), 1);
992 htprintf(ctx
, "</code>\n");
995 * Decide on the age limit of our colour coding, establish the
996 * colour thresholds, and write out a key.
998 ctx
->now
= time(NULL
);
1000 ctx
->oldest
= index_order_stat(t
, 0.05);
1001 ctx
->newest
= index_order_stat(t
, 1.0);
1002 ctx
->oldest
= round_and_format_age(ctx
, ctx
->oldest
, agebuf1
, -1);
1003 ctx
->newest
= round_and_format_age(ctx
, ctx
->newest
, agebuf2
, +1);
1005 ctx
->oldest
= cfg
->oldest
;
1006 ctx
->newest
= cfg
->newest
;
1007 ctx
->oldest
= round_and_format_age(ctx
, ctx
->oldest
, agebuf1
, 0);
1008 ctx
->newest
= round_and_format_age(ctx
, ctx
->newest
, agebuf2
, 0);
1010 for (i
= 0; i
< MAXCOLOUR
; i
++) {
1011 ctx
->thresholds
[i
] =
1012 ctx
->oldest
+ (ctx
->newest
- ctx
->oldest
) * i
/ (MAXCOLOUR
-1);
1014 for (i
= 0; i
<= MAXCOLOUR
; i
++) {
1018 strcpy(buf
, "> ");
1019 round_and_format_age(ctx
, ctx
->thresholds
[0], buf
+5, 0);
1020 } else if (i
== MAXCOLOUR
) {
1021 strcpy(buf
, "< ");
1022 round_and_format_age(ctx
, ctx
->thresholds
[MAXCOLOUR
-1], buf
+5, 0);
1024 unsigned long long midrange
=
1025 (ctx
->thresholds
[i
-1] + ctx
->thresholds
[i
]) / 2;
1026 round_and_format_age(ctx
, midrange
, buf
, 0);
1029 ctx
->titletexts
[i
] = dupstr(buf
);
1031 htprintf(ctx
, "<p align=center>Key to colour coding (mouse over for more detail):\n");
1032 htprintf(ctx
, "<p align=center style=\"padding: 0; margin-top:0.4em; "
1033 "margin-bottom:1em\">");
1034 begin_colour_bar(ctx
);
1035 htprintf(ctx
, "<td style=\"padding-right:1em\">%s</td>\n", agebuf1
);
1036 for (i
= 0; i
< MAXCOLOUR
; i
++)
1037 add_to_colour_bar(ctx
, i
, 1);
1038 htprintf(ctx
, "<td style=\"padding-left:1em\">%s</td>\n", agebuf2
);
1039 end_colour_bar(ctx
);
1042 * Begin the main table.
1044 htprintf(ctx
, "<p align=center>\n<table style=\"margin:0; border:0\">\n");
1047 * Find the total size of our entire subdirectory. We'll use
1048 * that as the scale for all the colour bars in this report.
1050 get_indices(t
, path
, &xi1
, &xi2
);
1051 ctx
->totalsize
= fetch_size(t
, xi1
, xi2
, ULLONG_MAX
);
1054 * Generate a report line for the whole subdirectory.
1057 vecs
= snewn(vecsize
, struct vector
*);
1059 vecs
[0] = make_vector(ctx
, path
, 0, 1, NULL
, 0);
1060 print_heading(ctx
, "Overall");
1061 write_report_line(ctx
, vecs
[0]);
1064 * Now generate report lines for all its children, and the
1065 * files contained in it.
1067 print_heading(ctx
, "Subdirectories");
1069 vecs
[0]->name
= dupstr("[files]");
1070 get_indices(t
, path
, &xi1
, &xi2
);
1072 pathlen
= strlen(path
);
1073 subdirpos
= pathlen
+ 1;
1074 if (pathlen
> 0 && path
[pathlen
-1] == pathsep
)
1077 trie_getpath(t
, xi1
, path2
);
1078 get_indices(t
, ctx
->path2
, &xj1
, &xj2
);
1080 if (!cfg
->showfiles
&& xj2
- xj1
<= 1)
1081 continue; /* skip individual files */
1082 if (nvecs
>= vecsize
) {
1083 vecsize
= nvecs
* 3 / 2 + 64;
1084 vecs
= sresize(vecs
, vecsize
, struct vector
*);
1086 assert(strlen(path2
) > pathlen
);
1087 vecs
[nvecs
] = make_vector(ctx
, path2
, downlink
&& (xj2
- xj1
> 1), 0,
1088 path2
+ subdirpos
, 1);
1089 for (i
= 0; i
<= MAXCOLOUR
; i
++)
1090 vecs
[0]->sizes
[i
] -= vecs
[nvecs
]->sizes
[i
];
1094 qsort(vecs
, nvecs
, sizeof(vecs
[0]), vec_compare
);
1096 for (i
= 0; i
< nvecs
; i
++)
1097 write_report_line(ctx
, vecs
[i
]);
1100 * Close the main table.
1102 htprintf(ctx
, "</table>\n");
1105 * Finish up and tidy up.
1107 htprintf(ctx
, "</body>\n");
1108 htprintf(ctx
, "</html>\n");
1112 for (i
= 0; i
< nvecs
; i
++) {
1113 sfree(vecs
[i
]->name
);
1121 int html_dump(const void *t
, unsigned long index
, unsigned long endindex
,
1122 int maxdepth
, const struct html_config
*cfg
,
1123 const char *pathprefix
)
1126 * Determine the filename for this file.
1128 assert(cfg
->fileformat
!= NULL
);
1129 char *filename
= format_string(cfg
->fileformat
, index
, t
);
1130 char *path
= dupfmt("%s%s", pathprefix
, filename
);
1134 * Create the HTML itself. Don't write out downlinks from our
1137 char *html
= html_query(t
, index
, cfg
, maxdepth
!= 0);
1142 FILE *fp
= fopen(path
, "w");
1144 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, path
, strerror(errno
));
1147 if (fputs(html
, fp
) < 0) {
1148 fprintf(stderr
, "%s: %s: write: %s\n", PNAME
, path
, strerror(errno
));
1152 if (fclose(fp
) < 0) {
1153 fprintf(stderr
, "%s: %s: fclose: %s\n", PNAME
, path
, strerror(errno
));
1161 if (maxdepth
!= 0) {
1162 unsigned long subindex
, subendindex
;
1163 int newdepth
= (maxdepth
> 0 ? maxdepth
- 1 : maxdepth
);
1164 char rpath
[1+trie_maxpathlen(t
)];
1167 while (index
< endindex
) {
1168 trie_getpath(t
, index
, rpath
);
1169 get_indices(t
, rpath
, &subindex
, &subendindex
);
1170 index
= subendindex
;
1171 if (subendindex
- subindex
> 1) {
1172 if (html_dump(t
, subindex
, subendindex
, newdepth
,