]> git.cameronkatri.com Git - cgit.git/blobdiff - ui-shared.c
tests: do not copy snapshots to /tmp/
[cgit.git] / ui-shared.c
index 03dcc0892626962300bb752b4a91083e23ce5041..151ac1797aa99351e63b9872d6f804eedda65431 100644 (file)
@@ -1,6 +1,6 @@
 /* ui-shared.c: common web output functions
  *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
  *
  * Licensed under GNU General Public License v2
  *   (see COPYING for full license text)
 #include "ui-shared.h"
 #include "cmd.h"
 #include "html.h"
+#include "version.h"
 
 static const char cgit_doctype[] =
-"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
-"  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
+"<!DOCTYPE html>\n";
 
 static char *http_date(time_t t)
 {
@@ -67,15 +67,49 @@ char *cgit_hosturl(void)
 
 char *cgit_currenturl(void)
 {
-       if (!ctx.qry.url)
-               return xstrdup(cgit_rooturl());
        const char *root = cgit_rooturl();
-       size_t len = strlen(root);
-       if (len && root[len - 1] == '/')
+
+       if (!ctx.qry.url)
+               return xstrdup(root);
+       if (root[0] && root[strlen(root) - 1] == '/')
                return fmtalloc("%s%s", root, ctx.qry.url);
        return fmtalloc("%s/%s", root, ctx.qry.url);
 }
 
+char *cgit_currentfullurl(void)
+{
+       const char *root = cgit_rooturl();
+       const char *orig_query = ctx.env.query_string ? ctx.env.query_string : "";
+       size_t len = strlen(orig_query);
+       char *query = xmalloc(len + 2), *start_url, *ret;
+
+       /* Remove all url=... parts from query string */
+       memcpy(query + 1, orig_query, len + 1);
+       query[0] = '?';
+       start_url = query;
+       while ((start_url = strstr(start_url, "url=")) != NULL) {
+               if (start_url[-1] == '?' || start_url[-1] == '&') {
+                       const char *end_url = strchr(start_url, '&');
+                       if (end_url)
+                               memmove(start_url, end_url + 1, strlen(end_url));
+                       else
+                               start_url[0] = '\0';
+               } else
+                       ++start_url;
+       }
+       if (!query[1])
+               query[0] = '\0';
+
+       if (!ctx.qry.url)
+               ret = fmtalloc("%s%s", root, query);
+       else if (root[0] && root[strlen(root) - 1] == '/')
+               ret = fmtalloc("%s%s%s", root, ctx.qry.url, query);
+       else
+               ret = fmtalloc("%s/%s%s", root, ctx.qry.url, query);
+       free(query);
+       return ret;
+}
+
 const char *cgit_rooturl(void)
 {
        if (ctx.cfg.virtual_root)
@@ -132,25 +166,38 @@ const char *cgit_repobasename(const char *reponame)
        static char rvbuf[1024];
        int p;
        const char *rv;
-       strncpy(rvbuf, reponame, sizeof(rvbuf));
-       if (rvbuf[sizeof(rvbuf)-1])
+       size_t len;
+
+       len = strlcpy(rvbuf, reponame, sizeof(rvbuf));
+       if (len >= sizeof(rvbuf))
                die("cgit_repobasename: truncated repository name '%s'", reponame);
-       p = strlen(rvbuf)-1;
+       p = len - 1;
        /* strip trailing slashes */
-       while (p && rvbuf[p] == '/') rvbuf[p--] = 0;
+       while (p && rvbuf[p] == '/')
+               rvbuf[p--] = '\0';
        /* strip trailing .git */
        if (p >= 3 && starts_with(&rvbuf[p-3], ".git")) {
-               p -= 3; rvbuf[p--] = 0;
+               p -= 3;
+               rvbuf[p--] = '\0';
        }
        /* strip more trailing slashes if any */
-       while ( p && rvbuf[p] == '/') rvbuf[p--] = 0;
+       while (p && rvbuf[p] == '/')
+               rvbuf[p--] = '\0';
        /* find last slash in the remaining string */
-       rv = strrchr(rvbuf,'/');
+       rv = strrchr(rvbuf, '/');
        if (rv)
                return ++rv;
        return rvbuf;
 }
 
+const char *cgit_snapshot_prefix(const struct cgit_repo *repo)
+{
+       if (repo->snapshot_prefix)
+               return repo->snapshot_prefix;
+
+       return cgit_repobasename(repo->url);
+}
+
 static void site_url(const char *page, const char *search, const char *sort, int ofs, int always_root)
 {
        char *delim = "?";
@@ -254,7 +301,7 @@ static char *repolink(const char *title, const char *class, const char *page,
                }
                delim = "&amp;";
        }
-       if (head && strcmp(head, ctx.repo->defbranch)) {
+       if (head && ctx.repo->defbranch && strcmp(head, ctx.repo->defbranch)) {
                html(delim);
                html("h=");
                html_url_arg(head);
@@ -304,6 +351,12 @@ void cgit_plain_link(const char *name, const char *title, const char *class,
        reporevlink("plain", name, title, class, head, rev, path);
 }
 
+void cgit_blame_link(const char *name, const char *title, const char *class,
+                    const char *head, const char *rev, const char *path)
+{
+       reporevlink("blame", 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,
@@ -347,16 +400,9 @@ void cgit_log_link(const char *name, const char *title, const char *class,
        html("</a>");
 }
 
-void cgit_commit_link(char *name, const char *title, const char *class,
+void cgit_commit_link(const char *name, const char *title, const char *class,
                      const char *head, const char *rev, const char *path)
 {
-       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);
@@ -387,9 +433,13 @@ void cgit_commit_link(char *name, const char *title, const char *class,
                html("follow=1");
        }
        html("'>");
-       if (name[0] != '\0')
-               html_txt(name);
-       else
+       if (name[0] != '\0') {
+               if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
+                       html_ntxt(name, ctx.cfg.max_msg_len - 3);
+                       html("...");
+               } else
+                       html_txt(name);
+       } else
                html_txt("(no commit message)");
        html("</a>");
 }
@@ -471,41 +521,45 @@ static void cgit_self_link(char *name, const char *title, const char *class)
        else if (!strcmp(ctx.qry.page, "summary"))
                cgit_summary_link(name, title, class, ctx.qry.head);
        else if (!strcmp(ctx.qry.page, "tag"))
-               cgit_tag_link(name, title, class, ctx.qry.has_sha1 ?
-                              ctx.qry.sha1 : ctx.qry.head);
+               cgit_tag_link(name, title, class, ctx.qry.has_oid ?
+                              ctx.qry.oid : ctx.qry.head);
        else if (!strcmp(ctx.qry.page, "tree"))
                cgit_tree_link(name, title, class, ctx.qry.head,
-                              ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+                              ctx.qry.has_oid ? ctx.qry.oid : NULL,
                               ctx.qry.path);
        else if (!strcmp(ctx.qry.page, "plain"))
                cgit_plain_link(name, title, class, ctx.qry.head,
-                               ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+                               ctx.qry.has_oid ? ctx.qry.oid : NULL,
+                               ctx.qry.path);
+       else if (!strcmp(ctx.qry.page, "blame"))
+               cgit_blame_link(name, title, class, ctx.qry.head,
+                               ctx.qry.has_oid ? ctx.qry.oid : NULL,
                                ctx.qry.path);
        else if (!strcmp(ctx.qry.page, "log"))
                cgit_log_link(name, title, class, ctx.qry.head,
-                             ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+                             ctx.qry.has_oid ? ctx.qry.oid : NULL,
                              ctx.qry.path, ctx.qry.ofs,
                              ctx.qry.grep, ctx.qry.search,
                              ctx.qry.showmsg, ctx.qry.follow);
        else if (!strcmp(ctx.qry.page, "commit"))
                cgit_commit_link(name, title, class, ctx.qry.head,
-                                ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+                                ctx.qry.has_oid ? ctx.qry.oid : NULL,
                                 ctx.qry.path);
        else if (!strcmp(ctx.qry.page, "patch"))
                cgit_patch_link(name, title, class, ctx.qry.head,
-                               ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+                               ctx.qry.has_oid ? ctx.qry.oid : NULL,
                                ctx.qry.path);
        else if (!strcmp(ctx.qry.page, "refs"))
                cgit_refs_link(name, title, class, ctx.qry.head,
-                              ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+                              ctx.qry.has_oid ? ctx.qry.oid : NULL,
                               ctx.qry.path);
        else if (!strcmp(ctx.qry.page, "snapshot"))
                cgit_snapshot_link(name, title, class, ctx.qry.head,
-                                  ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+                                  ctx.qry.has_oid ? ctx.qry.oid : NULL,
                                   ctx.qry.path);
        else if (!strcmp(ctx.qry.page, "diff"))
                cgit_diff_link(name, title, class, ctx.qry.head,
-                              ctx.qry.sha1, ctx.qry.sha2,
+                              ctx.qry.oid, ctx.qry.oid2,
                               ctx.qry.path);
        else if (!strcmp(ctx.qry.page, "stats"))
                cgit_stats_link(name, title, class, ctx.qry.head,
@@ -538,7 +592,7 @@ void cgit_object_link(struct object *obj)
                page = "tag";
        else
                page = "blob";
-       name = fmt("%s %s...", typename(obj->type), shortrev);
+       name = fmt("%s %s...", type_name(obj->type), shortrev);
        reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
 }
 
@@ -702,7 +756,6 @@ void cgit_redirect(const char *url, bool permanent)
        html("Location: ");
        html_url_path(url);
        html("\n\n");
-       exit(0);
 }
 
 static void print_rel_vcs_link(const char *url)
@@ -716,15 +769,16 @@ static void print_rel_vcs_link(const char *url)
 
 void cgit_print_docstart(void)
 {
+       char *host = cgit_hosturl();
+
        if (ctx.cfg.embedded) {
                if (ctx.cfg.header)
                        html_include(ctx.cfg.header);
                return;
        }
 
-       char *host = cgit_hosturl();
        html(cgit_doctype);
-       html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
+       html("<html lang='en'>\n");
        html("<head>\n");
        html("<title>");
        html_txt(ctx.page.title);
@@ -759,6 +813,8 @@ void cgit_print_docstart(void)
                cgit_add_clone_urls(print_rel_vcs_link);
        if (ctx.cfg.head_include)
                html_include(ctx.cfg.head_include);
+       if (ctx.repo && ctx.repo->extra_head_content)
+               html(ctx.repo->extra_head_content);
        html("</head>\n");
        html("<body>\n");
        if (ctx.cfg.header)
@@ -778,8 +834,8 @@ void cgit_print_docend(void)
        if (ctx.cfg.footer)
                html_include(ctx.cfg.footer);
        else {
-               htmlf("<div class='footer'>generated by <a href='http://git.zx2c4.com/cgit/about/'>cgit %s</a> at ",
-                       cgit_version);
+               htmlf("<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> "
+                       "(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string);
                html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)));
                html("</div>\n");
        }
@@ -793,13 +849,11 @@ void cgit_print_error_page(int code, const char *msg, const char *fmt, ...)
        ctx.page.expires = ctx.cfg.cache_dynamic_ttl;
        ctx.page.status = code;
        ctx.page.statusmsg = msg;
-       cgit_print_http_headers();
-       cgit_print_docstart();
-       cgit_print_pageheader();
+       cgit_print_layout_start();
        va_start(ap, fmt);
        cgit_vprint_error(fmt, ap);
        va_end(ap);
-       cgit_print_docend();
+       cgit_print_layout_end();
 }
 
 void cgit_print_layout_start(void)
@@ -864,10 +918,10 @@ void cgit_add_hidden_formfields(int incl_head, int incl_search,
            strcmp(ctx.qry.head, ctx.repo->defbranch))
                html_hidden("h", ctx.qry.head);
 
-       if (ctx.qry.sha1)
-               html_hidden("id", ctx.qry.sha1);
-       if (ctx.qry.sha2)
-               html_hidden("id2", ctx.qry.sha2);
+       if (ctx.qry.oid)
+               html_hidden("id", ctx.qry.oid);
+       if (ctx.qry.oid2)
+               html_hidden("id2", ctx.qry.oid2);
        if (ctx.qry.showmsg)
                html_hidden("showmsg", "1");
 
@@ -891,12 +945,13 @@ static void cgit_print_path_crumbs(char *path)
 {
        char *old_path = ctx.qry.path;
        char *p = path, *q, *end = path + strlen(path);
+       int levels = 0;
 
        ctx.qry.path = NULL;
        cgit_self_link("root", NULL, NULL);
        ctx.qry.path = p = path;
        while (p < end) {
-               if (!(q = strchr(p, '/')))
+               if (!(q = strchr(p, '/')) || levels > 15)
                        q = end;
                *q = '\0';
                html_txt("/");
@@ -904,6 +959,7 @@ static void cgit_print_path_crumbs(char *path)
                if (q < end)
                        *q = '/';
                p = q + 1;
+               ++levels;
        }
        ctx.qry.path = old_path;
 }
@@ -941,14 +997,14 @@ static void print_header(void)
                cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
                if (ctx.env.authenticated) {
                        html("</td><td class='form'>");
-                       html("<form method='get' action=''>\n");
+                       html("<form method='get'>\n");
                        cgit_add_hidden_formfields(0, 1, ctx.qry.page);
                        html("<select name='h' onchange='this.form.submit();'>\n");
                        for_each_branch_ref(print_branch_option, ctx.qry.head);
                        if (ctx.repo->enable_remote_branches)
                                for_each_remote_ref(print_branch_option, ctx.qry.head);
                        html("</select> ");
-                       html("<input type='submit' name='' value='switch'/>");
+                       html("<input type='submit' value='switch'/>");
                        html("</form>");
                }
        } else
@@ -963,8 +1019,6 @@ static void print_header(void)
        } else {
                if (ctx.cfg.root_desc)
                        html_txt(ctx.cfg.root_desc);
-               else if (ctx.cfg.index_info)
-                       html_include(ctx.cfg.index_info);
        }
        html("</td></tr></table>\n");
 }
@@ -984,19 +1038,28 @@ void cgit_print_pageheader(void)
                cgit_summary_link("summary", NULL, hc("summary"),
                                  ctx.qry.head);
                cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head,
-                              ctx.qry.sha1, NULL);
+                              ctx.qry.oid, NULL);
                cgit_log_link("log", NULL, hc("log"), ctx.qry.head,
                              NULL, ctx.qry.vpath, 0, NULL, NULL,
                              ctx.qry.showmsg, ctx.qry.follow);
-               cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
-                              ctx.qry.sha1, ctx.qry.vpath);
+               if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))
+                       cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,
+                                       ctx.qry.oid, ctx.qry.vpath);
+               else
+                       cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
+                                      ctx.qry.oid, ctx.qry.vpath);
                cgit_commit_link("commit", NULL, hc("commit"),
-                                ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath);
+                                ctx.qry.head, ctx.qry.oid, ctx.qry.vpath);
                cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
-                              ctx.qry.sha1, ctx.qry.sha2, ctx.qry.vpath);
+                              ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath);
                if (ctx.repo->max_stats)
                        cgit_stats_link("stats", NULL, hc("stats"),
                                        ctx.qry.head, ctx.qry.vpath);
+               if (ctx.repo->homepage) {
+                       html("<a href='");
+                       html_attr(ctx.repo->homepage);
+                       html("'>homepage</a>");
+               }
                html("</td><td class='form'>");
                html("<form class='right' method='get' action='");
                if (ctx.cfg.virtual_root) {
@@ -1013,7 +1076,7 @@ void cgit_print_pageheader(void)
                html_option("committer", "committer", ctx.qry.grep);
                html_option("range", "range", ctx.qry.grep);
                html("</select>\n");
-               html("<input class='txt' type='text' size='10' name='q' value='");
+               html("<input class='txt' type='search' size='10' name='q' value='");
                html_attr(ctx.qry.search);
                html("'/>\n");
                html("<input type='submit' value='search'/>\n");
@@ -1028,7 +1091,7 @@ void cgit_print_pageheader(void)
                html("<form method='get' action='");
                html_attr(currenturl);
                html("'>\n");
-               html("<input type='text' name='q' size='10' value='");
+               html("<input type='search' name='q' size='10' value='");
                html_attr(ctx.qry.search);
                html("'/>\n");
                html("<input type='submit' value='search'/>\n");
@@ -1036,7 +1099,7 @@ void cgit_print_pageheader(void)
                free(currenturl);
        }
        html("</td></tr></table>\n");
-       if (ctx.env.authenticated && ctx.qry.vpath) {
+       if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) {
                html("<div class='path'>");
                html("path: ");
                cgit_print_path_crumbs(ctx.qry.vpath);
@@ -1068,27 +1131,80 @@ void cgit_print_filemode(unsigned short mode)
        html_fileperm(mode);
 }
 
-void cgit_print_snapshot_links(const char *repo, const char *head,
-                              const char *hex, int snapshots)
+void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base,
+                                 const char *ref)
 {
-       const struct cgit_snapshot_format* f;
+       struct object_id oid;
+
+       /*
+        * Prettify snapshot names by stripping leading "v" or "V" if the tag
+        * name starts with {v,V}[0-9] and the prettify mapping is injective,
+        * i.e. each stripped tag can be inverted without ambiguities.
+        */
+       if (get_oid(fmt("refs/tags/%s", ref), &oid) == 0 &&
+           (ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]) &&
+           ((get_oid(fmt("refs/tags/%s", ref + 1), &oid) == 0) +
+            (get_oid(fmt("refs/tags/v%s", ref + 1), &oid) == 0) +
+            (get_oid(fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1))
+               ref++;
+
+       strbuf_addf(filename, "%s-%s", base, ref);
+}
+
+void cgit_print_snapshot_links(const struct cgit_repo *repo, const char *ref,
+                              const char *separator)
+{
+       const struct cgit_snapshot_format *f;
        struct strbuf filename = STRBUF_INIT;
+       const char *basename;
        size_t prefixlen;
-       unsigned char sha1[20];
 
-       if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 &&
-           (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1]))
-               hex++;
-       strbuf_addf(&filename, "%s-%s", cgit_repobasename(repo), hex);
+       basename = cgit_snapshot_prefix(repo);
+       if (starts_with(ref, basename))
+               strbuf_addstr(&filename, ref);
+       else
+               cgit_compose_snapshot_prefix(&filename, basename, ref);
+
        prefixlen = filename.len;
        for (f = cgit_snapshot_formats; f->suffix; f++) {
-               if (!(snapshots & f->bit))
+               if (!(repo->snapshots & cgit_snapshot_format_bit(f)))
                        continue;
                strbuf_setlen(&filename, prefixlen);
                strbuf_addstr(&filename, f->suffix);
                cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL,
                                   filename.buf);
-               html("<br/>");
+               if (cgit_snapshot_get_sig(ref, f)) {
+                       strbuf_addstr(&filename, ".asc");
+                       html(" (");
+                       cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
+                                          filename.buf);
+                       html(")");
+               } else if (starts_with(f->suffix, ".tar") && cgit_snapshot_get_sig(ref, &cgit_snapshot_formats[0])) {
+                       strbuf_setlen(&filename, strlen(filename.buf) - strlen(f->suffix));
+                       strbuf_addstr(&filename, ".tar.asc");
+                       html(" (");
+                       cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
+                                          filename.buf);
+                       html(")");
+               }
+               html(separator);
        }
        strbuf_release(&filename);
 }
+
+void cgit_set_title_from_path(const char *path)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char *slash, *last_slash;
+
+       if (!path)
+               return;
+
+       for (last_slash = path + strlen(path); (slash = memrchr(path, '/', last_slash - path)) != NULL; last_slash = slash) {
+               strbuf_add(&sb, slash + 1, last_slash - slash - 1);
+               strbuf_addstr(&sb, " \xc2\xab ");
+       }
+       strbuf_add(&sb, path, last_slash - path);
+       strbuf_addf(&sb, " - %s", ctx.page.title);
+       ctx.page.title = strbuf_detach(&sb, NULL);
+}