+ char *delim;
+
+ delim = repolink(title, class, "log", head, path);
+ if (rev && strcmp(rev, ctx.qry.head)) {
+ html(delim);
+ html("id=");
+ html_url_arg(rev);
+ delim = "&";
+ }
+ if (grep && pattern) {
+ html(delim);
+ html("qt=");
+ html_url_arg(grep);
+ delim = "&";
+ html(delim);
+ html("q=");
+ html_url_arg(pattern);
+ }
+ if (ofs > 0) {
+ html(delim);
+ html("ofs=");
+ htmlf("%d", ofs);
+ delim = "&";
+ }
+ if (showmsg) {
+ html(delim);
+ html("showmsg=1");
+ }
+ html("'>");
+ html_txt(name);
+ html("</a>");
+}
+
+void cgit_commit_link(char *name, const char *title, const char *class,
+ const char *head, const char *rev, int toggle_ssdiff)
+{
+ if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
+ name[ctx.cfg.max_msg_len] = '\0';
+ name[ctx.cfg.max_msg_len - 1] = '.';
+ name[ctx.cfg.max_msg_len - 2] = '.';
+ name[ctx.cfg.max_msg_len - 3] = '.';
+ }
+
+ char *delim;
+
+ delim = repolink(title, class, "commit", head, NULL);
+ if (rev && strcmp(rev, ctx.qry.head)) {
+ html(delim);
+ html("id=");
+ html_url_arg(rev);
+ delim = "&";
+ }
+ if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
+ html(delim);
+ html("ss=1");
+ }
+ html("'>");
+ html_txt(name);
+ html("</a>");
+}
+
+void cgit_refs_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
+{
+ reporevlink("refs", name, title, class, head, rev, path);
+}
+
+void cgit_snapshot_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev,
+ const char *archivename)
+{
+ reporevlink("snapshot", name, title, class, head, rev, archivename);
+}
+
+void cgit_diff_link(const char *name, const char *title, const char *class,
+ const char *head, const char *new_rev, const char *old_rev,
+ const char *path, int toggle_ssdiff)
+{
+ char *delim;
+
+ delim = repolink(title, class, "diff", head, path);
+ if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) {
+ html(delim);
+ html("id=");
+ html_url_arg(new_rev);
+ delim = "&";
+ }
+ if (old_rev) {
+ html(delim);
+ html("id2=");
+ html_url_arg(old_rev);
+ delim = "&";
+ }
+ if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
+ html(delim);
+ html("ss=1");
+ }
+ html("'>");
+ html_txt(name);
+ html("</a>");
+}
+
+void cgit_patch_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
+{
+ reporevlink("patch", name, title, class, head, rev, path);
+}
+
+void cgit_stats_link(const char *name, const char *title, const char *class,
+ const char *head, const char *path)
+{
+ reporevlink("stats", name, title, class, head, NULL, path);
+}
+
+void cgit_self_link(char *name, const char *title, const char *class,
+ struct cgit_context *ctx)
+{
+ if (!strcmp(ctx->qry.page, "repolist"))
+ return cgit_index_link(name, title, class, ctx->qry.search,
+ ctx->qry.ofs);
+ else if (!strcmp(ctx->qry.page, "summary"))
+ return cgit_summary_link(name, title, class, ctx->qry.head);
+ else if (!strcmp(ctx->qry.page, "tag"))
+ return cgit_tag_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL);
+ else if (!strcmp(ctx->qry.page, "tree"))
+ return cgit_tree_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "plain"))
+ return cgit_plain_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "log"))
+ return cgit_log_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path, ctx->qry.ofs,
+ ctx->qry.grep, ctx->qry.search,
+ ctx->qry.showmsg);
+ else if (!strcmp(ctx->qry.page, "commit"))
+ return cgit_commit_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path, 0);
+ else if (!strcmp(ctx->qry.page, "patch"))
+ return cgit_patch_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "refs"))
+ return cgit_refs_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "snapshot"))
+ return cgit_snapshot_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "diff"))
+ return cgit_diff_link(name, title, class, ctx->qry.head,
+ ctx->qry.sha1, ctx->qry.sha2,
+ ctx->qry.path, 0);
+ else if (!strcmp(ctx->qry.page, "stats"))
+ return cgit_stats_link(name, title, class, ctx->qry.head,
+ ctx->qry.path);
+
+ /* Don't known how to make link for this page */
+ repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path);
+ html("><!-- cgit_self_link() doesn't know how to make link for page '");
+ html_txt(ctx->qry.page);
+ html("' -->");
+ html_txt(name);
+ html("</a>");
+}
+
+void cgit_object_link(struct object *obj)
+{
+ char *page, *shortrev, *fullrev, *name;
+
+ fullrev = sha1_to_hex(obj->sha1);
+ shortrev = xstrdup(fullrev);
+ shortrev[10] = '\0';
+ if (obj->type == OBJ_COMMIT) {
+ cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
+ ctx.qry.head, fullrev, 0);
+ return;
+ } else if (obj->type == OBJ_TREE)
+ page = "tree";
+ else if (obj->type == OBJ_TAG)
+ page = "tag";
+ else
+ page = "blob";
+ name = fmt("%s %s...", typename(obj->type), shortrev);
+ reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
+}
+
+void cgit_print_date(time_t secs, const char *format, int local_time)
+{
+ char buf[64];
+ struct tm *time;
+
+ if (!secs)
+ return;
+ if(local_time)
+ time = localtime(&secs);
+ else
+ time = gmtime(&secs);
+ strftime(buf, sizeof(buf)-1, format, time);
+ html_txt(buf);
+}
+
+void cgit_print_age(time_t t, time_t max_relative, const char *format)
+{
+ time_t now, secs;
+
+ if (!t)
+ return;
+ time(&now);
+ secs = now - t;
+
+ if (secs > max_relative && max_relative >= 0) {
+ cgit_print_date(t, format, ctx.cfg.local_time);
+ return;
+ }
+
+ if (secs < TM_HOUR * 2) {
+ htmlf("<span class='age-mins'>%.0f min.</span>",
+ secs * 1.0 / TM_MIN);
+ return;
+ }
+ if (secs < TM_DAY * 2) {
+ htmlf("<span class='age-hours'>%.0f hours</span>",
+ secs * 1.0 / TM_HOUR);
+ return;
+ }
+ if (secs < TM_WEEK * 2) {
+ htmlf("<span class='age-days'>%.0f days</span>",
+ secs * 1.0 / TM_DAY);
+ return;
+ }
+ if (secs < TM_MONTH * 2) {
+ htmlf("<span class='age-weeks'>%.0f weeks</span>",
+ secs * 1.0 / TM_WEEK);
+ return;
+ }
+ if (secs < TM_YEAR * 2) {
+ htmlf("<span class='age-months'>%.0f months</span>",
+ secs * 1.0 / TM_MONTH);
+ return;
+ }
+ htmlf("<span class='age-years'>%.0f years</span>",
+ secs * 1.0 / TM_YEAR);
+}
+
+void cgit_print_http_headers(struct cgit_context *ctx)
+{
+ if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1"))
+ return;
+
+ if (ctx->page.status)
+ htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg);
+ if (ctx->page.mimetype && ctx->page.charset)
+ htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
+ ctx->page.charset);
+ else if (ctx->page.mimetype)
+ htmlf("Content-Type: %s\n", ctx->page.mimetype);
+ if (ctx->page.size)
+ htmlf("Content-Length: %ld\n", ctx->page.size);
+ if (ctx->page.filename)
+ htmlf("Content-Disposition: inline; filename=\"%s\"\n",
+ ctx->page.filename);
+ htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
+ htmlf("Expires: %s\n", http_date(ctx->page.expires));
+ if (ctx->page.etag)
+ htmlf("ETag: \"%s\"\n", ctx->page.etag);