diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Harden the page renderers
Diffstat (limited to '')
-rw-r--r--source/ui-stats.c56
1 file changed, 30 insertions, 26 deletions
diff --git a/source/ui-stats.c b/source/ui-stats.c
index a04b189..3960b14 100644
--- a/source/ui-stats.c
+++ b/source/ui-stats.c
@@ -17,11 +17,9 @@
#define DEFAULT_AUTHOR_ROWS 10
-/*
- * One author's share of the window. periods is keyed by the label of a
- * period, with the commit count stored in the util field itself rather than
- * behind another allocation.
- */
+// One author's share of the window. periods is keyed by the label of a period,
+// with the commit count stored in the util field itself rather than behind
+// another allocation.
struct authorstat {
long total;
struct string_list periods;
@@ -144,10 +142,8 @@ static char *pretty_year(struct tm *tm)
return cgit_fmt("%d", tm->tm_year + 1900);
}
-/*
- * The order runs from the finest window to the coarsest, because a repository
- * caps the page by storing an index into this table as its max-stats.
- */
+// The order runs from the finest window to the coarsest, because a repository
+// caps the page by storing an index into this table as its max-stats.
static const struct cgit_period periods[] = {
{'w', "week", 4, trunc_week, dec_week, inc_week, pretty_week},
{'m', "month", 4, trunc_month, dec_month, inc_month, pretty_month},
@@ -157,10 +153,13 @@ static const struct cgit_period periods[] = {
static void window_start(const struct cgit_period *period, struct tm *tm)
{
- time_t now;
+ // Read once, so the buckets and the column labels agree on the window
+ // even when the clock crosses a period boundary between them.
+ static time_t now;
int i;
- time(&now);
+ if (!now)
+ time(&now);
gmtime_r(&now, tm);
period->trunc(tm);
for (i = 1; i < period->count; i++)
@@ -175,7 +174,6 @@ static void add_commit(struct string_list *authors, struct commitinfo *info,
char *name, *label;
struct tm date;
time_t when;
- uintptr_t *count;
// A commit can lack an author header, so fall back rather than
// handing xstrdup a NULL.
@@ -194,11 +192,11 @@ static void add_commit(struct string_list *authors, struct commitinfo *info,
return;
period->trunc(&date);
label = xstrdup(period->pretty(&date));
+ // The pointer field holds the count itself.
bucket = string_list_insert(&stats->periods, label);
- count = (uintptr_t *)&bucket->util;
- if (*count)
+ if (bucket->util)
free(label);
- (*count)++;
+ bucket->util = (void *)((uintptr_t)bucket->util + 1);
stats->total++;
}
@@ -213,10 +211,11 @@ static struct string_list collect_stats(const struct cgit_period *period)
struct string_list authors;
struct rev_info rev;
struct commit *commit;
- // setup_revisions reads the entries after the double dash up to a
- // NULL, past the count, so the sentinel has to stay even when the
- // path fills the slot before it.
- const char *argv[] = {NULL, ctx.qry.head, NULL, NULL, NULL};
+ // setup_revisions reads these the way main does, skipping the first
+ // entry, and reads past the count up to a NULL after the double dash,
+ // so the sentinel has to stay even when the path fills the slot before
+ // it.
+ const char *argv[] = { NULL, ctx.qry.head, NULL, NULL, NULL };
int argc = 2;
time_t since;
struct tm tm;
@@ -235,16 +234,19 @@ static struct string_list collect_stats(const struct cgit_period *period)
rev.max_parents = 1;
rev.verbose_header = 1;
rev.show_root_diff = 0;
- // setup_revisions reads argv the way main does and ignores the first
- // entry, so the head to walk sits at argv[1].
setup_revisions(argc, argv, &rev, NULL);
// Prune the walk to the displayed window instead of traversing the
// whole history and discarding older commits. The check below still
// bounds the period edge exactly.
rev.max_age = since;
- prepare_revision_walk(&rev);
memset(&authors, 0, sizeof(authors));
- while ((commit = get_revision(&rev)) != NULL) {
+ // A failed setup leaves the walk holding freed commits, so it must
+ // not be read from.
+ if (prepare_revision_walk(&rev)) {
+ cgit_print_error_page(500, "Internal Server Error", "Unable to read the history");
+ return authors;
+ }
+ while ((commit = get_revision(&rev))) {
struct commitinfo *info = cgit_parse_commit(commit);
if ((time_t)info->committer_date >= since)
@@ -352,7 +354,7 @@ static void print_authors(struct string_list *authors, int max_rows, const struc
if (!bucket)
html("<td>0</td>");
else {
- htmlf("<td>%lu</td>", (uintptr_t)bucket->util);
+ htmlf("<td>%ju</td>", (uintmax_t)(uintptr_t)bucket->util);
total += (uintptr_t)bucket->util;
}
}
@@ -360,7 +362,8 @@ static void print_authors(struct string_list *authors, int max_rows, const struc
}
if (rows < authors->nr)
- print_summary_row(authors, rows, authors->nr - rows, "Others (%ld)", "left", "", "sum", labels);
+ print_summary_row(authors, rows, authors->nr - rows, "Others (%ld)", "left", "", "sum",
+ labels);
print_summary_row(authors, 0, authors->nr, "Total", "total", "sum", "sum", labels);
html("</table>\n");
@@ -380,7 +383,8 @@ static void print_options_form(const struct cgit_period *period, int top)
html("<tr><td class='label'>Period:</td>");
html("<td class='ctrl'><select name='period'>");
for (i = 0; i < choices; i++)
- html_option(cgit_fmt("%c", periods[i].code), periods[i].name, cgit_fmt("%c", period->code));
+ html_option(cgit_fmt("%c", periods[i].code), periods[i].name,
+ cgit_fmt("%c", period->code));
html("</select></td></tr>\n");
}
html("<tr><td class='label'>Authors:</td>");