{! /* * Copyright (c) 2022 Omar Polo * Copyright (c) 2016, 2019, 2020-2022 Tracey Emery * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "got_error.h" #include "got_object.h" #include "got_reference.h" #include "proc.h" #include "gotwebd.h" #include "tmpl.h" static int gotweb_render_blob_line(struct template *, const char *, size_t); static int gotweb_render_tree_item(struct template *, struct got_tree_entry *); static int blame_line(struct template *, const char *, struct blame_line *, int, int); static inline int gotweb_render_more(struct template *, int); static inline int diff_line(struct template *, char *); static inline int tag_item(struct template *, struct repo_tag *); static inline int branch(struct template *, struct got_reflist_entry *); static inline int rss_tag_item(struct template *, struct repo_tag *); static inline int rss_author(struct template *, char *); !} {{ define gotweb_render_page(struct template *tp, int (*body)(struct template *)) }} {! struct request *c = tp->tp_arg; struct server *srv = c->srv; struct querystring *qs = c->t->qs; struct gotweb_url u_path; const char *prfx = c->document_uri; const char *css = srv->custom_css; memset(&u_path, 0, sizeof(u_path)); u_path.index_page = -1; u_path.page = -1; u_path.action = SUMMARY; !} {{ srv->site_name }}
{{ render body(tp) }}
{{ if srv->show_site_owner }} {{ srv->site_owner }} {{ end }}
{{ end }} {{ define gotweb_render_error(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; !}
{{ if t->error }} {{ t->error->msg }} {{ else }} See daemon logs for details {{ end }}
{{ end }} {{ define gotweb_render_repo_table_hdr(struct template *tp) }} {! struct request *c = tp->tp_arg; struct server *srv = c->srv; !}
Project
{{ if srv->show_repo_description }}
Description
{{ end }} {{ if srv->show_repo_owner }}
Owner
{{ end }} {{ if srv->show_repo_age }}
Last Change
{{ end }}
{{ end }} {{ define gotweb_render_repo_fragment(struct template *tp, struct repo_dir *repo_dir) }} {! struct request *c = tp->tp_arg; struct server *srv = c->srv; struct gotweb_url summary = { .action = SUMMARY, .index_page = -1, .page = -1, .path = repo_dir->name, }, briefs = { .action = BRIEFS, .index_page = -1, .page = -1, .path = repo_dir->name, }, commits = { .action = COMMITS, .index_page = -1, .page = -1, .path = repo_dir->name, }, tags = { .action = TAGS, .index_page = -1, .page = -1, .path = repo_dir->name, }, tree = { .action = TREE, .index_page = -1, .page = -1, .path = repo_dir->name, }, rss = { .action = RSS, .index_page = -1, .page = -1, .path = repo_dir->name, }; !}
{{ if srv->show_repo_description }}
{{ repo_dir->description }}
{{ end }} {{ if srv->show_repo_owner }}
{{ repo_dir->owner }}
{{ end }} {{ if srv->show_repo_age }}
{{ render gotweb_render_age(tp, repo_dir->age, TM_DIFF) }}
{{ end }}
{{ end }} {{ define gotweb_render_briefs(struct template *tp) }} {! const struct got_error *error; struct request *c = tp->tp_arg; struct server *srv = c->srv; struct transport *t = c->t; struct querystring *qs = c->t->qs; struct repo_commit *rc; struct repo_dir *repo_dir = t->repo_dir; struct gotweb_url diff_url, tree_url; char *tmp; diff_url = (struct gotweb_url){ .action = DIFF, .index_page = -1, .page = -1, .path = repo_dir->name, .headref = qs->headref, }; tree_url = (struct gotweb_url){ .action = TREE, .index_page = -1, .page = -1, .path = repo_dir->name, .headref = qs->headref, }; if (qs->action == SUMMARY) { qs->action = BRIEFS; error = got_get_repo_commits(c, D_MAXSLCOMMDISP); } else error = got_get_repo_commits(c, srv->max_commits_display); if (error) return -1; !}
Commit Briefs
{{ tailq-foreach rc &t->repo_commits entry }} {! diff_url.commit = rc->commit_id; tree_url.commit = rc->commit_id; tmp = strchr(rc->committer, '<'); if (tmp) *tmp = '\0'; tmp = strchr(rc->commit_msg, '\n'); if (tmp) *tmp = '\0'; !}
{{ render gotweb_render_age(tp, rc->committer_time, TM_DIFF) }}
{{ rc->committer }}
{{ rc->commit_msg }} {{ if rc->refs_str }} {{ " " }} ({{ rc->refs_str }}) {{ end }}
{{ end }} {{ render gotweb_render_more(tp, BRIEFS) }}
{{ end }} {{ define gotweb_render_more(struct template *tp, int action) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct querystring *qs = t->qs; struct gotweb_url more = { .action = action, .index_page = -1, .path = qs->path, .commit = t->more_id, .headref = qs->headref, }; !} {{ if t->more_id }} {{ end }} {{ end }} {{ define gotweb_render_navs(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct gotweb_url prev, next; int have_prev, have_next; gotweb_get_navs(c, &prev, &have_prev, &next, &have_next); !}
{{ finally }} {! free(t->next_id); t->next_id = NULL; free(t->prev_id); t->prev_id = NULL; !} {{ end }} {{ define gotweb_render_commits(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct repo_dir *repo_dir = t->repo_dir; struct repo_commit *rc; struct gotweb_url diff, tree; diff = (struct gotweb_url){ .action = DIFF, .index_page = -1, .page = -1, .path = repo_dir->name, }; tree = (struct gotweb_url){ .action = TREE, .index_page = -1, .page = -1, .path = repo_dir->name, }; !}
Commits
{{ tailq-foreach rc &t->repo_commits entry }} {! diff.commit = rc->commit_id; tree.commit = rc->commit_id; !}
Commit:
{{ rc->commit_id }}
From:
{{ rc->author }}
{{ if strcmp(rc->committer, rc->author) != 0 }}
Via:
{{ rc->committer }}
{{ end }}
Date:
{{ render gotweb_render_age(tp, rc->committer_time, TM_LONG) }}
{{ "\n" }} {{ rc->commit_msg }}
{{ end }} {{ render gotweb_render_more(tp, COMMITS) }}
{{ end }} {{ define gotweb_render_blob(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct got_blob_object *blob = t->blob; struct repo_commit *rc = TAILQ_FIRST(&t->repo_commits); !}
Blob
Date:
{{ render gotweb_render_age(tp, rc->committer_time, TM_LONG) }}
Message:
{{ rc->commit_msg }}
      {{ render got_output_blob_by_lines(tp, blob, gotweb_render_blob_line) }}
    
{{ end }} {{ define gotweb_render_blob_line(struct template *tp, const char *line, size_t no) }} {! char lineno[16]; int r; r = snprintf(lineno, sizeof(lineno), "%zu", no); if (r < 0 || (size_t)r >= sizeof(lineno)) return -1; !}
{{ line }}
{{ end }} {{ define gotweb_render_tree(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct repo_commit *rc = TAILQ_FIRST(&t->repo_commits); !}
Tree
Tree:
{{ rc->tree_id }}
Date:
{{ render gotweb_render_age(tp, rc->committer_time, TM_LONG) }}
Message:
{{ rc->commit_msg }}
{{ render got_output_repo_tree(c, gotweb_render_tree_item) }}
{{ end }} {{ define gotweb_render_tree_item(struct template *tp, struct got_tree_entry *te) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct querystring *qs = t->qs; struct repo_commit *rc = TAILQ_FIRST(&t->repo_commits); const char *modestr = ""; const char *name; const char *folder; char *dir = NULL; mode_t mode; struct gotweb_url url = { .index_page = -1, .page = -1, .commit = rc->commit_id, .path = qs->path, }; name = got_tree_entry_get_name(te); mode = got_tree_entry_get_mode(te); folder = qs->folder ? qs->folder : ""; if (S_ISDIR(mode)) { if (asprintf(&dir, "%s/%s", folder, name) == -1) return (-1); url.action = TREE; url.folder = dir; } else { url.action = BLOB; url.folder = folder; url.file = name; } if (got_object_tree_entry_is_submodule(te)) modestr = "$"; else if (S_ISLNK(mode)) modestr = "@"; else if (S_ISDIR(mode)) modestr = "/"; else if (mode & S_IXUSR) modestr = "*"; !}
{{ if S_ISDIR(mode) }}
 
{{ else }}
{! url.action = COMMITS; !} commits {{ " | " }} {! url.action = BLAME; !} blame
{{ end }}
{{ finally }} {! free(dir); !} {{ end }} {{ define gotweb_render_tags(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct querystring *qs = t->qs; struct repo_tag *rt; int commit_found; commit_found = qs->commit == NULL; !}
Tags
{{ if t->tag_count == 0 }}
This repository contains no tags
{{ else }} {{ tailq-foreach rt &t->repo_tags entry }} {{ if commit_found || !strcmp(qs->commit, rt->commit_id) }} {! commit_found = 1; !} {{ render tag_item(tp, rt) }} {{ end }} {{ end }} {{ if t->next_id || t->prev_id }} {! qs->action = TAGS; !} {{ render gotweb_render_navs(tp) }} {{ end }} {{ end }}
{{ end }} {{ define tag_item(struct template *tp, struct repo_tag *rt) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct repo_dir *repo_dir = t->repo_dir; char *tag_name = rt->tag_name; char *msg = rt->tag_commit; char *nl; struct gotweb_url url = { .action = TAG, .index_page = -1, .page = -1, .path = repo_dir->name, .commit = rt->commit_id, }; if (strncmp(tag_name, "refs/tags/", 10) == 0) tag_name += 10; if (msg) { nl = strchr(msg, '\n'); if (nl) *nl = '\0'; } !}
{{ render gotweb_render_age(tp, rt->tagger_time, TM_DIFF) }}
{{ tag_name }}
{{ end }} {{ define gotweb_render_tag(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct repo_tag *rt; const char *tag_name; rt = TAILQ_LAST(&t->repo_tags, repo_tags_head); tag_name = rt->tag_name; if (strncmp(tag_name, "refs/", 5) == 0) tag_name += 5; !}
Tag
Commit:
{{ rt->commit_id }} {{ " " }} ({{ tag_name }})
Tagger:
{{ rt->tagger }}
Date:
{{ render gotweb_render_age(tp, rt->tagger_time, TM_LONG)}}
Message:
{{ rt->commit_msg }}
{{ "\n" }} {{ rt->tag_commit }}
{{ end }} {{ define gotweb_render_diff(struct template *tp) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; FILE *fp = t->fp; struct repo_commit *rc = TAILQ_FIRST(&t->repo_commits); char *line = NULL; size_t linesize = 0; ssize_t linelen; !}
Commit Diff
Commit:
{{ rc->commit_id }}
From:
{{ rc->author }}
{{ if strcmp(rc->committer, rc->author) != 0 }}
Via:
{{ rc->committer }}
{{ end }}
Date:
{{ render gotweb_render_age(tp, rc->committer_time, TM_LONG) }}
Message:
{{ rc->commit_msg }}
{{ "\n" }} {{ while (linelen = getline(&line, &linesize, fp)) != -1 }} {{ render diff_line(tp, line) }} {{ end }}
{{ finally }} {! free(line); !} {{ end }} {{ define diff_line(struct template *tp, char *line )}} {! const char *color = NULL; char *nl; if (!strncmp(line, "-", 1)) color = "diff_minus"; else if (!strncmp(line, "+", 1)) color = "diff_plus"; else if (!strncmp(line, "@@", 2)) color = "diff_chunk_header"; else if (!strncmp(line, "commit +", 8) || !strncmp(line, "commit -", 8) || !strncmp(line, "blob +", 6) || !strncmp(line, "blob -", 6) || !strncmp(line, "file +", 6) || !strncmp(line, "file -", 6)) color = "diff_meta"; else if (!strncmp(line, "from:", 5) || !strncmp(line, "via:", 4)) color = "diff_author"; else if (!strncmp(line, "date:", 5)) color = "diff_date"; nl = strchr(line, '\n'); if (nl) *nl = '\0'; !}
{{ line }}
{{ end }} {{ define gotweb_render_branches(struct template *tp, struct got_reflist_head *refs) }} {! struct got_reflist_entry *re; !}
Branches
{{ tailq-foreach re refs entry }} {{ if !got_ref_is_symbolic(re->ref) }} {{ render branch(tp, re) }} {{ end }} {{ end }}
{{ end }} {{ define branch(struct template *tp, struct got_reflist_entry *re) }} {! const struct got_error *err; struct request *c = tp->tp_arg; struct querystring *qs = c->t->qs; const char *refname; time_t age; struct gotweb_url url = { .action = SUMMARY, .index_page = -1, .page = -1, .path = qs->path, }; refname = got_ref_get_name(re->ref); err = got_get_repo_age(&age, c, refname); if (err) { log_warnx("%s: %s", __func__, err->msg); return -1; } if (strncmp(refname, "refs/heads/", 11) == 0) refname += 11; url.headref = refname; !}
{{ render gotweb_render_age(tp, age, TM_DIFF) }}
 
{{ end }} {{ define gotweb_render_summary(struct template *tp) }} {! struct request *c = tp->tp_arg; struct server *srv = c->srv; struct transport *t = c->t; struct got_reflist_head *refs = &t->refs; !}
{{ if srv->show_repo_description }}
Description:
{{ t->repo_dir->description }}
{{ end }} {{ if srv->show_repo_owner }}
Owner:
{{ t->repo_dir->owner }}
{{ end }} {{ if srv->show_repo_age }}
Last Change:
{{ render gotweb_render_age(tp, t->repo_dir->age, TM_DIFF) }}
{{ end }} {{ if srv->show_repo_cloneurl }}
Clone URL:
{{ t->repo_dir->url }}
{{ end }}
{{ render gotweb_render_briefs(tp) }} {{ render gotweb_render_tags(tp) }} {{ render gotweb_render_branches(tp, refs) }} {{ end }} {{ define gotweb_render_blame(struct template *tp) }} {! const struct got_error *err; struct request *c = tp->tp_arg; struct transport *t = c->t; struct repo_commit *rc = TAILQ_FIRST(&t->repo_commits); !}
Blame
Date:
{{ render gotweb_render_age(tp, rc->committer_time, TM_LONG) }}
Message:
{{ rc->commit_msg }}
{{ "\n" }} {! err = got_output_file_blame(c, &blame_line); if (err && err->code != GOT_ERR_CANCELLED) log_warnx("%s: got_output_file_blame: %s", __func__, err->msg); if (err) return (-1); !}
{{ end }} {{ define blame_line(struct template *tp, const char *line, struct blame_line *bline, int lprec, int lcur) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct repo_dir *repo_dir = t->repo_dir; char *committer, *s; struct gotweb_url url = { .action = DIFF, .index_page = -1, .page = -1, .path = repo_dir->name, .commit = bline->id_str, }; s = strchr(bline->committer, '<'); committer = s ? s + 1 : bline->committer; s = strchr(committer, '@'); if (s) *s = '\0'; !}
{{ printf "%.*d", lprec, lcur }}
{{ bline->datebuf }}
{{ printf "%.9s", committer }}
{{ line }}
{{ end }} {{ define gotweb_render_rss(struct template *tp) }} {! struct request *c = tp->tp_arg; struct server *srv = c->srv; struct transport *t = c->t; struct repo_dir *repo_dir = t->repo_dir; struct repo_tag *rt; struct gotweb_url summary = { .action = SUMMARY, .index_page = -1, .page = -1, .path = repo_dir->name, }; !} Tags of {{ repo_dir->name }} {{ if srv->show_repo_description }} {{ repo_dir->description }} {{ end }} {{ tailq-foreach rt &t->repo_tags entry }} {{ render rss_tag_item(tp, rt) }} {{ end }} {{ end }} {{ define rss_tag_item(struct template *tp, struct repo_tag *rt) }} {! struct request *c = tp->tp_arg; struct transport *t = c->t; struct repo_dir *repo_dir = t->repo_dir; char *tag_name = rt->tag_name; struct gotweb_url tag = { .action = TAG, .index_page = -1, .page = -1, .path = repo_dir->name, .commit = rt->commit_id, }; if (strncmp(tag_name, "refs/tags/", 10) == 0) tag_name += 10; !} {{ repo_dir->name }} {{" "}} {{ tag_name }} {{ rt->tag_commit }}]]> {{ render rss_author(tp, rt->tagger) }} {{ rt->commit_id }} {{ render gotweb_render_age(tp, rt->tagger_time, TM_RFC822) }} {{ end }} {{ define rss_author(struct template *tp, char *author) }} {! char *t, *mail; /* what to do if the author name contains a paren? */ if (strchr(author, '(') != NULL || strchr(author, ')') != NULL) return 0; t = strchr(author, '<'); if (t == NULL) return 0; *t = '\0'; mail = t+1; while (isspace((unsigned char)*--t)) *t = '\0'; t = strchr(mail, '>'); if (t == NULL) return 0; *t = '\0'; !} {{ mail }} {{" "}} ({{ author }}) {{ end }}