+ if (ctx.env.http_host)
+ return ctx.env.http_host;
+ if (!ctx.env.server_name)
+ return NULL;
+ if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
+ return ctx.env.server_name;
+ return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port));
+}
+
+char *cgit_rooturl()
+{
+ if (ctx.cfg.virtual_root)
+ return fmt("%s/", ctx.cfg.virtual_root);
+ else
+ return ctx.cfg.script_name;
+}
+
+char *cgit_repourl(const char *reponame)
+{
+ if (ctx.cfg.virtual_root) {
+ return fmt("%s/%s/", ctx.cfg.virtual_root, reponame);
+ } else {
+ return fmt("?r=%s", reponame);
+ }
+}
+
+char *cgit_fileurl(const char *reponame, const char *pagename,
+ const char *filename, const char *query)
+{
+ char *tmp;
+ char *delim;
+
+ if (ctx.cfg.virtual_root) {
+ tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame,
+ pagename, (filename ? filename:""));
+ delim = "?";
+ } else {
+ tmp = fmt("?url=%s/%s/%s", reponame, pagename,
+ (filename ? filename : ""));
+ delim = "&";
+ }
+ if (query)
+ tmp = fmt("%s%s%s", tmp, delim, query);
+ return tmp;
+}
+
+char *cgit_pageurl(const char *reponame, const char *pagename,
+ const char *query)
+{
+ return cgit_fileurl(reponame,pagename,0,query);
+}
+
+const char *cgit_repobasename(const char *reponame)
+{
+ /* I assume we don't need to store more than one repo basename */
+ static char rvbuf[1024];
+ int p;
+ const char *rv;
+ strncpy(rvbuf,reponame,sizeof(rvbuf));
+ if(rvbuf[sizeof(rvbuf)-1])
+ die("cgit_repobasename: truncated repository name '%s'", reponame);
+ p = strlen(rvbuf)-1;
+ /* strip trailing slashes */
+ while(p && rvbuf[p]=='/') rvbuf[p--]=0;
+ /* strip trailing .git */
+ if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
+ p -= 3; rvbuf[p--] = 0;
+ }
+ /* strip more trailing slashes if any */
+ while( p && rvbuf[p]=='/') rvbuf[p--]=0;
+ /* find last slash in the remaining string */
+ rv = strrchr(rvbuf,'/');
+ if(rv)
+ return ++rv;
+ return rvbuf;
+}
+
+char *cgit_currurl()
+{
+ if (!ctx.cfg.virtual_root)
+ return ctx.cfg.script_name;
+ else if (ctx.qry.page)
+ return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page);
+ else if (ctx.qry.repo)
+ return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo);
+ else
+ return fmt("%s/", ctx.cfg.virtual_root);
+}
+
+static void site_url(const char *page, const char *search, const char *sort, int ofs)
+{
+ char *delim = "?";
+
+ if (ctx.cfg.virtual_root) {
+ html_attr(ctx.cfg.virtual_root);
+ if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
+ html("/");
+ } else
+ html(ctx.cfg.script_name);
+
+ if (page) {
+ htmlf("?p=%s", page);
+ delim = "&";
+ }
+ if (search) {
+ html(delim);
+ html("q=");
+ html_attr(search);
+ delim = "&";
+ }
+ if (sort) {
+ html(delim);
+ html("s=");
+ html_attr(sort);
+ delim = "&";
+ }
+ if (ofs) {
+ html(delim);
+ htmlf("ofs=%d", ofs);
+ }
+}
+
+static void site_link(const char *page, const char *name, const char *title,
+ const char *class, const char *search, const char *sort, int ofs)
+{
+ html("<a");
+ if (title) {
+ html(" title='");
+ html_attr(title);
+ html("'");
+ }
+ if (class) {
+ html(" class='");
+ html_attr(class);
+ html("'");
+ }
+ html(" href='");
+ site_url(page, search, sort, ofs);
+ html("'>");
+ html_txt(name);
+ html("</a>");
+}
+
+void cgit_index_link(const char *name, const char *title, const char *class,
+ const char *pattern, const char *sort, int ofs)
+{
+ site_link(NULL, name, title, class, pattern, sort, ofs);
+}
+
+static char *repolink(const char *title, const char *class, const char *page,
+ const char *head, const char *path)
+{
+ char *delim = "?";
+
+ html("<a");
+ if (title) {
+ html(" title='");
+ html_attr(title);
+ html("'");
+ }
+ if (class) {
+ html(" class='");
+ html_attr(class);
+ html("'");
+ }
+ html(" href='");
+ if (ctx.cfg.virtual_root) {
+ html_url_path(ctx.cfg.virtual_root);
+ if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
+ html("/");
+ html_url_path(ctx.repo->url);
+ if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
+ html("/");
+ if (page) {
+ html_url_path(page);
+ html("/");
+ if (path)
+ html_url_path(path);
+ }
+ } else {
+ html(ctx.cfg.script_name);
+ html("?url=");
+ html_url_arg(ctx.repo->url);
+ if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
+ html("/");
+ if (page) {
+ html_url_arg(page);
+ html("/");
+ if (path)
+ html_url_arg(path);
+ }
+ delim = "&";
+ }
+ if (head && strcmp(head, ctx.repo->defbranch)) {
+ html(delim);
+ html("h=");
+ html_url_arg(head);
+ delim = "&";
+ }
+ return fmt("%s", delim);
+}
+
+static void reporevlink(const char *page, const char *name, const char *title,
+ const char *class, const char *head, const char *rev,
+ const char *path)
+{
+ char *delim;
+
+ delim = repolink(title, class, page, head, path);
+ if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) {
+ html(delim);
+ html("id=");
+ html_url_arg(rev);
+ }
+ html("'>");
+ html_txt(name);
+ html("</a>");
+}
+
+void cgit_summary_link(const char *name, const char *title, const char *class,
+ const char *head)
+{
+ reporevlink(NULL, name, title, class, head, NULL, NULL);
+}
+
+void cgit_tag_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev)
+{
+ reporevlink("tag", name, title, class, head, rev, NULL);
+}
+
+void cgit_tree_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
+{
+ reporevlink("tree", name, title, class, head, rev, path);
+}
+
+void cgit_plain_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
+{
+ reporevlink("plain", name, title, class, head, rev, path);
+}
+
+void cgit_log_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path,
+ int ofs, const char *grep, const char *pattern, int showmsg)
+{
+ char *delim;
+
+ delim = repolink(title, class, "log", head, path);
+ if (rev && ctx.qry.head && 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, const char *path,
+ 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, path);
+ if (rev && ctx.qry.head && 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");
+ delim = "&";
+ }
+ if (ctx.qry.context > 0 && ctx.qry.context != 3) {
+ html(delim);
+ html("context=");
+ htmlf("%d", ctx.qry.context);
+ delim = "&";
+ }
+ if (ctx.qry.ignorews) {
+ html(delim);
+ html("ignorews=1");
+ delim = "&";
+ }
+ html("'>");
+ if (name[0] != '\0')
+ html_txt(name);
+ else
+ html_txt("(no commit message)");
+ 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");
+ delim = "&";
+ }
+ if (ctx.qry.context > 0 && ctx.qry.context != 3) {
+ html(delim);
+ html("context=");
+ htmlf("%d", ctx.qry.context);
+ delim = "&";
+ }
+ if (ctx.qry.ignorews) {
+ html(delim);
+ html("ignorews=1");
+ delim = "&";
+ }
+ 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.sort,
+ 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, NULL, 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);
+}
+
+struct string_list_item *lookup_path(struct string_list *list,
+ const char *path)
+{
+ struct string_list_item *item;
+
+ while (path && path[0]) {
+ if ((item = string_list_lookup(list, path)))
+ return item;
+ if (!(path = strchr(path, '/')))
+ break;
+ path++;
+ }
+ return NULL;