2 * html.c: implementation of html.h.
19 #define lenof(x) ( sizeof((x)) / sizeof(*(x)) )
25 size_t buflen
, bufsize
;
27 unsigned long long totalsize
, oldest
, newest
;
32 unsigned long long thresholds
[MAXCOLOUR
-1];
36 static void vhtprintf(struct html
*ctx
, char *fmt
, va_list ap
)
42 size
= vsnprintf(NULL
, 0, fmt
, ap2
);
45 if (ctx
->buflen
+ size
>= ctx
->bufsize
) {
46 ctx
->bufsize
= (ctx
->buflen
+ size
) * 3 / 2 + 1024;
47 ctx
->buf
= sresize(ctx
->buf
, ctx
->bufsize
, char);
49 size2
= vsnprintf(ctx
->buf
+ ctx
->buflen
, ctx
->bufsize
- ctx
->buflen
,
51 assert(size
== size2
);
55 static void htprintf(struct html
*ctx
, char *fmt
, ...)
59 vhtprintf(ctx
, fmt
, ap
);
63 static unsigned long long round_and_format_age(struct html
*ctx
,
64 unsigned long long age
,
65 char *buf
, int direction
)
69 unsigned long long ret
, newret
;
72 static const int minutes
[] = { 5, 10, 15, 30, 45 };
74 tm
= *localtime(&ctx
->now
);
75 ym
= tm
.tm_year
* 12 + tm
.tm_mon
;
80 for (i
= 0; i
< lenof(minutes
); i
++) {
81 newret
= ctx
->now
- minutes
[i
] * 60;
82 sprintf(newbuf
, "%d minutes", minutes
[i
]);
89 for (i
= 1; i
< 24; i
++) {
90 newret
= ctx
->now
- i
* (60*60);
91 sprintf(newbuf
, "%d hour%s", i
, i
==1 ?
"" : "s");
98 for (i
= 1; i
< 7; i
++) {
99 newret
= ctx
->now
- i
* (24*60*60);
100 sprintf(newbuf
, "%d day%s", i
, i
==1 ?
"" : "s");
107 for (i
= 1; i
< 4; i
++) {
108 newret
= ctx
->now
- i
* (7*24*60*60);
109 sprintf(newbuf
, "%d week%s", i
, i
==1 ?
"" : "s");
116 for (i
= 1; i
< 11; i
++) {
117 tm2
= tm
; /* structure copy */
118 tm2
.tm_year
= (ym
- i
) / 12;
119 tm2
.tm_mon
= (ym
- i
) % 12;
120 newret
= mktime(&tm2
);
121 sprintf(newbuf
, "%d month%s", i
, i
==1 ?
"" : "s");
129 tm2
= tm
; /* structure copy */
130 tm2
.tm_year
= (ym
- i
*12) / 12;
131 tm2
.tm_mon
= (ym
- i
*12) % 12;
132 newret
= mktime(&tm2
);
133 sprintf(newbuf
, "%d year%s", i
, i
==1 ?
"" : "s");
143 * Round toward newest, i.e. use the existing (buf,ret).
145 } else if (direction
< 0) {
147 * Round toward oldest, i.e. use (newbuf,newret);
155 if (ret
- age
> age
- newret
) {
163 static void get_indices(const void *t
, char *path
,
164 unsigned long *xi1
, unsigned long *xi2
)
166 size_t pathlen
= strlen(path
);
168 *xi1
= trie_before(t
, path
);
169 path
[pathlen
] = '\001';
170 path
[pathlen
+1] = '\0';
171 *xi2
= trie_before(t
, path
);
172 path
[pathlen
] = '\0';
175 static unsigned long long fetch_size(const void *t
, char *path
,
176 unsigned long long atime
)
178 unsigned long xi1
, xi2
;
180 get_indices(t
, path
, &xi1
, &xi2
);
182 return index_query(t
, xi2
, atime
) - index_query(t
, xi1
, atime
);
185 static void htescape(struct html
*ctx
, const char *s
, int n
, int italics
)
187 while (n
> 0 && *s
) {
188 unsigned char c
= (unsigned char)*s
++;
191 htprintf(ctx
, "&");
193 htprintf(ctx
, "<");
195 htprintf(ctx
, ">");
196 else if (c
>= ' ' && c
< '\177')
197 htprintf(ctx
, "%c", c
);
199 if (italics
) htprintf(ctx
, "<i>");
200 htprintf(ctx
, "[%02x]", c
);
201 if (italics
) htprintf(ctx
, "</i>");
208 static void begin_colour_bar(struct html
*ctx
)
210 htprintf(ctx
, "<table cellspacing=0 cellpadding=0"
211 " style=\"border:0\">\n<tr>\n");
214 static void add_to_colour_bar(struct html
*ctx
, int colour
, int pixels
)
219 if (colour
>= 0 && colour
< 256) /* red -> yellow fade */
220 r
= 255, g
= colour
, b
= 0;
221 else if (colour
>= 256 && colour
<= 511) /* yellow -> green fade */
222 r
= 511 - colour
, g
= 255, b
= 0;
223 else /* background grey */
227 /* no title text here */
228 } else if (colour
== 0) {
229 strcpy(buf
, "< ");
230 round_and_format_age(ctx
, ctx
->thresholds
[0], buf
+5, 0);
231 } else if (colour
== MAXCOLOUR
) {
232 strcpy(buf
, "> ");
233 round_and_format_age(ctx
, ctx
->thresholds
[MAXCOLOUR
-1], buf
+5, 0);
235 unsigned long long midrange
=
236 (ctx
->thresholds
[colour
] + ctx
->thresholds
[colour
+1]) / 2;
237 round_and_format_age(ctx
, midrange
, buf
, 0);
241 htprintf(ctx
, "<td style=\"width:%dpx; height:1em; "
242 "background-color:#%02x%02x%02x\"",
245 htprintf(ctx
, " title=\"%s\"", buf
);
246 htprintf(ctx
, "></td>\n");
250 static void end_colour_bar(struct html
*ctx
)
252 htprintf(ctx
, "</tr>\n</table>\n");
259 unsigned long long sizes
[MAXCOLOUR
+1];
262 int vec_compare(const void *av
, const void *bv
)
264 const struct vector
*a
= *(const struct vector
**)av
;
265 const struct vector
*b
= *(const struct vector
**)bv
;
267 if (a
->sizes
[MAXCOLOUR
] > b
->sizes
[MAXCOLOUR
])
269 else if (a
->sizes
[MAXCOLOUR
] < b
->sizes
[MAXCOLOUR
])
271 else if (a
->want_href
< b
->want_href
)
273 else if (a
->want_href
> b
->want_href
)
275 else if (a
->want_href
)
276 return strcmp(a
->name
, b
->name
);
277 else if (a
->index
< b
->index
)
279 else if (a
->index
> b
->index
)
284 static struct vector
*make_vector(struct html
*ctx
, char *path
,
285 int want_href
, char *name
)
287 unsigned long xi1
, xi2
;
288 struct vector
*vec
= snew(struct vector
);
291 vec
->want_href
= want_href
;
292 vec
->name
= name ?
dupstr(name
) : NULL
;
294 get_indices(ctx
->t
, path
, &xi1
, &xi2
);
298 for (i
= 0; i
<= MAXCOLOUR
; i
++) {
299 unsigned long long atime
;
303 atime
= ctx
->thresholds
[i
];
304 vec
->sizes
[i
] = fetch_size(ctx
->t
, path
, atime
);
310 static void print_heading(struct html
*ctx
, const char *title
)
312 htprintf(ctx
, "<tr style=\"padding: 0.2em; background-color:#e0e0e0\">\n"
313 "<td colspan=4 align=center>%s</td>\n</tr>\n", title
);
316 #define PIXEL_SIZE 600 /* FIXME: configurability? */
317 static void write_report_line(struct html
*ctx
, struct vector
*vec
)
319 unsigned long long size
, asize
, divisor
;
324 * A line with literally zero space usage should not be
325 * printed at all if it's a link to a subdirectory (since it
326 * probably means the whole thing was excluded by some
327 * --exclude-path wildcard). If it's [files] or the top-level
328 * line, though, we must always print _something_, and in that
329 * case we must fiddle about to prevent divisions by zero in
332 if (!vec
->sizes
[MAXCOLOUR
] && vec
->want_href
)
334 divisor
= ctx
->totalsize
;
340 * Find the total size of this subdirectory.
342 size
= vec
->sizes
[MAXCOLOUR
];
343 htprintf(ctx
, "<tr>\n"
344 "<td style=\"padding: 0.2em; text-align: right\">%lluMb</td>\n",
345 ((size
+ ((1<<11)-1)) >> 11)); /* convert to Mb, rounding up */
348 * Generate a colour bar.
350 htprintf(ctx
, "<td style=\"padding: 0.2em\">\n");
351 begin_colour_bar(ctx
);
353 for (i
= 0; i
<= MAXCOLOUR
; i
++) {
354 asize
= vec
->sizes
[i
];
355 newpix
= asize
* PIXEL_SIZE
/ divisor
;
356 add_to_colour_bar(ctx
, i
, newpix
- pix
);
359 add_to_colour_bar(ctx
, -1, PIXEL_SIZE
- pix
);
361 htprintf(ctx
, "</td>\n");
364 * Output size as a percentage of totalsize.
366 htprintf(ctx
, "<td style=\"padding: 0.2em; text-align: right\">"
367 "%.2f%%</td>\n", (double)size
/ divisor
* 100.0);
370 * Output a subdirectory marker.
372 htprintf(ctx
, "<td style=\"padding: 0.2em\">");
376 if (ctx
->format
&& vec
->want_href
) {
377 snprintf(ctx
->href
, ctx
->hreflen
, ctx
->format
, vec
->index
);
378 htprintf(ctx
, "<a href=\"%s\">", ctx
->href
);
381 htescape(ctx
, vec
->name
, strlen(vec
->name
), 1);
383 htprintf(ctx
, "</a>");
385 htprintf(ctx
, "</td>\n</tr>\n");
388 char *html_query(const void *t
, unsigned long index
, const char *format
)
390 struct html actx
, *ctx
= &actx
;
391 char *path
, *path2
, *p
, *q
, *href
;
392 char agebuf1
[80], agebuf2
[80];
393 size_t pathlen
, hreflen
;
394 unsigned long index2
;
396 struct vector
**vecs
;
398 unsigned long xi1
, xi2
, xj1
, xj2
;
400 if (index
>= trie_count(t
))
404 ctx
->buflen
= ctx
->bufsize
= 0;
406 ctx
->format
= format
;
407 htprintf(ctx
, "<html>\n");
409 path
= snewn(1+trie_maxpathlen(t
), char);
410 ctx
->path2
= path2
= snewn(1+trie_maxpathlen(t
), char);
412 hreflen
= strlen(format
) + 100;
413 href
= snewn(hreflen
, char);
418 ctx
->hreflen
= hreflen
;
424 htprintf(ctx
, "<head>\n");
425 trie_getpath(t
, index
, path
);
426 htprintf(ctx
, "<title>agedu: ");
427 htescape(ctx
, path
, strlen(path
), 0);
428 htprintf(ctx
, "</title>\n");
429 htprintf(ctx
, "</head>\n");
432 * Begin BODY section.
434 htprintf(ctx
, "<body>\n");
435 htprintf(ctx
, "<h3 align=center>Disk space breakdown by"
436 " last-access time</h3>\n");
439 * Show the pathname we're centred on, with hyperlinks to
440 * parent directories where available.
442 htprintf(ctx
, "<p align=center>\n<code>");
444 for (p
= strchr(path
, '/'); p
; p
= strchr(p
+1, '/')) {
447 * See if this path prefix exists in the trie. If so,
448 * generate a hyperlink.
451 index2
= trie_before(t
, path
);
452 trie_getpath(t
, index2
, path2
);
453 if (!strcmp(path
, path2
) && format
) {
454 snprintf(href
, hreflen
, format
, index2
);
455 htprintf(ctx
, "<a href=\"%s\">", href
);
459 htescape(ctx
, q
, p
- q
, 1);
462 htprintf(ctx
, "</a>");
465 htescape(ctx
, q
, strlen(q
), 1);
466 htprintf(ctx
, "</code>\n");
469 * Decide on the age limit of our colour coding, establish the
470 * colour thresholds, and write out a key.
472 ctx
->oldest
= index_order_stat(t
, 0.05); /* FIXME: configurability? */
473 ctx
->newest
= index_order_stat(t
, 1.0);
474 ctx
->now
= time(NULL
);
475 ctx
->oldest
= round_and_format_age(ctx
, ctx
->oldest
, agebuf1
, -1);
476 ctx
->newest
= round_and_format_age(ctx
, ctx
->newest
, agebuf2
, +1);
477 for (i
= 0; i
< MAXCOLOUR
-1; i
++) {
479 ctx
->oldest
+ (ctx
->newest
- ctx
->oldest
) * i
/ MAXCOLOUR
;
481 htprintf(ctx
, "<p align=center>Key to colour coding (mouse over for more detail):\n");
482 htprintf(ctx
, "<p align=center style=\"padding: 0; margin-top:0.4em; "
483 "margin-bottom:1em\"");
484 begin_colour_bar(ctx
);
485 htprintf(ctx
, "<td style=\"padding-right:1em\">%s</td>\n", agebuf1
);
486 for (i
= 0; i
< MAXCOLOUR
; i
++)
487 add_to_colour_bar(ctx
, i
, 1);
488 htprintf(ctx
, "<td style=\"padding-left:1em\">%s</td>\n", agebuf2
);
492 * Begin the main table.
494 htprintf(ctx
, "<p align=center>\n<table style=\"margin:0; border:0\">\n");
497 * Find the total size of our entire subdirectory. We'll use
498 * that as the scale for all the colour bars in this report.
500 ctx
->totalsize
= fetch_size(t
, path
, ULLONG_MAX
);
503 * Generate a report line for the whole subdirectory.
506 vecs
= snewn(vecsize
, struct vector
*);
508 vecs
[0] = make_vector(ctx
, path
, 0, NULL
);
509 print_heading(ctx
, "Overall");
510 write_report_line(ctx
, vecs
[0]);
513 * Now generate report lines for all its children, and the
514 * files contained in it.
516 print_heading(ctx
, "Subdirectories");
518 vecs
[0]->name
= dupstr("[files]");
519 get_indices(t
, path
, &xi1
, &xi2
);
521 pathlen
= strlen(path
);
523 trie_getpath(t
, xi1
, path2
);
524 get_indices(t
, ctx
->path2
, &xj1
, &xj2
);
527 continue; /* skip individual files */
528 if (nvecs
>= vecsize
) {
529 vecsize
= nvecs
* 3 / 2 + 64;
530 vecs
= sresize(vecs
, vecsize
, struct vector
*);
532 assert(strlen(path2
) > pathlen
);
533 vecs
[nvecs
] = make_vector(ctx
, path2
, 1, path2
+ pathlen
+ 1);
534 for (i
= 0; i
<= MAXCOLOUR
; i
++)
535 vecs
[0]->sizes
[i
] -= vecs
[nvecs
]->sizes
[i
];
539 qsort(vecs
, nvecs
, sizeof(vecs
[0]), vec_compare
);
541 for (i
= 0; i
< nvecs
; i
++)
542 write_report_line(ctx
, vecs
[i
]);
545 * Close the main table.
547 htprintf(ctx
, "</table>\n");
550 * Finish up and tidy up.
552 htprintf(ctx
, "</body>\n");
553 htprintf(ctx
, "</html>\n");
557 for (i
= 0; i
< nvecs
; i
++) {
558 sfree(vecs
[i
]->name
);