/* ui-shared.c: common web output functions * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" #include "cmd.h" #include "html.h" 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"; static char *http_date(time_t t) { static char day[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static char month[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; struct tm *tm = gmtime(&t); return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); } void cgit_print_error(const char *msg) { html("<div class='error'>"); html_txt(msg); html("</div>\n"); } char *cgit_httpscheme() { if (ctx.env.https && !strcmp(ctx.env.https, "on")) return "https://"; else return "http://"; } char *cgit_hosturl() { 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, 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 (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, 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, ofs); html("'>"); html_txt(name); html("</a>"); } void cgit_index_link(const char *name, const char *title, const char *class, const char *pattern, int ofs) { site_link(NULL, name, title, class, pattern, 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 && strcmp(rev, ctx.qry.head)) { html(delim); html("id="); html_url_arg(rev); delim = "&"; } if (grep && pattern) { html(delim); html("qt="); html_u<style>pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span><span class="c1"># This file should be sourced by all test-scripts</span> <span class="c1">#</span> <span class="c1"># Main functions:</span> <span class="c1"># prepare_tests(description) - setup for testing, i.e. create repos+config</span> <span class="c1"># run_test(description, script) - run one test, i.e. eval script</span> <span class="c1">#</span> <span class="c1"># Helper functions</span> <span class="c1"># cgit_query(querystring) - call cgit with the specified querystring</span> <span class="c1"># cgit_url(url) - call cgit with the specified virtual url</span> <span class="c1">#</span> <span class="c1"># Example script:</span> <span class="c1">#</span> <span class="c1"># . setup.sh</span> <span class="c1"># prepare_tests "html validation"</span> <span class="c1"># run_test 'repo index' 'cgit_url "/" | tidy -e'</span> <span class="c1"># run_test 'repo summary' 'cgit_url "/foo" | tidy -e'</span> mkrepo<span class="o">()</span><span class="w"> </span><span class="o">{</span> <span class="w"> </span><span class="nv">name</span><span class="o">=</span><span class="nv">$1</span> <span class="w"> </span><span class="nv">count</span><span class="o">=</span><span class="nv">$2</span> <span class="w"> </span><span class="nv">dir</span><span class="o">=</span><span class="nv">$PWD</span> <span class="w"> </span><span class="nb">test</span><span class="w"> </span>-d<span class="w"> </span><span class="nv">$name</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="k">return</span> <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">"Creating testrepo %s\n"</span><span class="w"> </span><span class="nv">$name</span> <span class="w"> </span>mkdir<span class="w"> </span>-p<span class="w"> </span><span class="nv">$name</span> <span class="w"> </span><span class="nb">cd</span><span class="w"> </span><span class="nv">$name</span> <span class="w"> </span>git<span class="w"> </span>init <span class="w"> </span><span class="nv">n</span><span class="o">=</span><span class="m">1</span> <span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="nb">test</span><span class="w"> </span><span class="nv">$n</span><span class="w"> </span>-le<span class="w"> </span><span class="nv">$count</span> <span class="w"> </span><span class="k">do</span> <span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="nv">$n</span><span class="w"> </span>>file-<span class="nv">$n</span> <span class="w"> </span>git<span class="w"> </span>add<span class="w"> </span>file-<span class="nv">$n</span> <span class="w"> </span>git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"commit </span><span class="nv">$n</span><span class="s2">"</span> <span class="w"> </span><span class="nv">n</span><span class="o">=</span><span class="k">$(</span>expr<span class="w"> </span><span class="nv">$n</span><span class="w"> </span>+<span class="w"> </span><span class="m">1</span><span class="k">)</span> <span class="w"> </span><span class="k">done</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nb">test</span><span class="w"> </span><span class="s2">"</span><span class="nv">$3</span><span class="s2">"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"testplus"</span> <span class="w"> </span><span class="k">then</span> <span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"hello"</span><span class="w"> </span>>a+b <span class="w"> </span>git<span class="w"> </span>add<span class="w"> </span>a+b <span class="w"> </span>git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"add a+b"</span> <span class="w"> </span>git<span class="w"> </span>branch<span class="w"> </span><span class="s2">"1+2"</span> <span class="w"> </span><span class="k">fi</span> <span class="w"> </span><span class="nb">cd</span><span class="w"> </span><span class="nv">$dir</span> <span class="o">}</span> setup_repos<span class="o">()</span> <span class="o">{</span> <span class="w"> </span>rm<span class="w"> </span>-rf<span class="w"> </span>trash/cache <span class="w"> </span>mkdir<span class="w"> </span>-p<span class="w"> </span>trash/cache <span class="w"> </span>mkrepo<span class="w"> </span>trash/repos/foo<span class="w"> </span><span class="m">5</span><span class="w"> </span>>/dev/null <span class="w"> </span>mkrepo<span class="w"> </span>trash/repos/bar<span class="w"> </span><span class="m">50</span><span class="w"> </span>>/dev/null <span class="w"> </span>mkrepo<span class="w"> </span>trash/repos/foo+bar<span class="w"> </span><span class="m">10</span><span class="w"> </span>testplus<span class="w"> </span>>/dev/null <span class="w"> </span>cat<span class="w"> </span>>trash/cgitrc<span class="w"> </span><span class="s"><<EOF</span> <span class="s">virtual-root=/</span> <span class="s">cache-root=$PWD/trash/cache</span> <span class="s">cache-size=1021</span> <span class="s">snapshots=tar.gz tar.bz zip</span> <span class="s">enable-log-filecount=1</span> <span class="s">enable-log-linecount=1</span> <span class="s">summary-log=5</span> <span class="s">summary-branches=5</span> <span class="s">summary-tags=5</span> <span class="s">repo.url=foo</span> <span class="s">repo.path=$PWD/trash/repos/foo/.git</span> <span class="s"># Do not specify a description for this repo, as it then will be assigned</span> <span class="s"># the constant value "[no description]" (which actually used to cause a</span> <span class="s"># segfault).</span> <span class="s">repo.url=bar</span> <span class="s">repo.path=$PWD/trash/repos/bar/.git</span> <span class="s">repo.desc=the bar repo</span> <span class="s">repo.url=foo+bar</span> <span class="s">repo.path=$PWD/trash/repos/foo+bar/.git</span> <span class="s">repo.desc=the foo+bar repo</span> <span class="s">EOF</span> <span class="o">}</span> prepare_tests<span class="o">()</span> <span class="o">{</span> <span class="w"> </span>setup_repos <span class="w"> </span>rm<span class="w"> </span>-f<span class="w"> </span>test-output.log<span class="w"> </span><span class="m">2</span>>/dev/null <span class="w"> </span><span class="nv">test_count</span><span class="o">=</span><span class="m">0</span> <span class="w"> </span><span class="nv">test_failed</span><span class="o">=</span><span class="m">0</span> <span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"[</span><span class="nv">$0</span><span class="s2">]"</span><span class="w"> </span><span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span><span class="w"> </span>>test-output.log <span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span><span class="w"> </span><span class="s2">"(</span><span class="nv">$0</span><span class="s2">)"</span> <span class="o">}</span> tests_done<span class="o">()</span> <span class="o">{</span> <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">"\n"</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nb">test</span><span class="w"> </span><span class="nv">$test_failed</span><span class="w"> </span>-gt<span class="w"> </span><span class="m">0</span> <span class="w"> </span><span class="k">then</span> <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">"test: *** %s failure(s), logfile=%s\n"</span><span class="w"> </span><span class="se">\</span> <span class="w"> </span><span class="nv">$test_failed</span><span class="w"> </span><span class="s2">"</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span><span class="s2">/test-output.log"</span> <span class="w"> </span><span class="nb">false</span> <span class="w"> </span><span class="k">fi</span> <span class="o">}</span> run_test<span class="o">()</span> <span class="o">{</span> <span class="w"> </span><span class="nv">desc</span><span class="o">=</span><span class="nv">$1</span> <span class="w"> </span><span class="nv">script</span><span class="o">=</span><span class="nv">$2</span> <span class="w"> </span><span class="nv">test_count</span><span class="o">=</span><span class="k">$(</span>expr<span class="w"> </span><span class="nv">$test_count</span><span class="w"> </span>+<span class="w"> </span><span class="m">1</span><span class="k">)</span> <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">"\ntest %d: name='%s'\n"</span><span class="w"> </span><span class="nv">$test_count</span><span class="w"> </span><span class="s2">"</span><span class="nv">$desc</span><span class="s2">"</span><span class="w"> </span>>>test-output.log <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">"test %d: eval='%s'\n"</span><span class="w"> </span><span class="nv">$test_count</span><span class="w"> </span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span><span class="w"> </span>>>test-output.log <span class="w"> </span><span class="nb">eval</span><span class="w"> </span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span><span class="w"> </span>>>test-output.log<span class="w"> </span><span class="m">2</span>>>test-output.log <span class="w"> </span><span class="nv">res</span><span class="o">=</span><span class="nv">$?</span> <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">"test %d: exitcode=%d\n"</span><span class="w"> </span><span class="nv">$test_count</span><span class="w"> </span><span class="nv">$res</span><span class="w"> </span>>>test-output.log <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nb">test</span><span class="w"> </span><span class="nv">$res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span> <span class="w"> </span><span class="k">then</span> <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">" %2d) %-60s [ok]\n"</span><span class="w"> </span><span class="nv">$test_count</span><span class="w"> </span><span class="s2">"</span><span class="nv">$desc</span><span class="s2">"</span> <span class="w"> </span><span class="k">else</span> <span class="w"> </span><span class="nv">test_failed</span><span class="o">=</span><span class="k">$(</span>expr<span class="w"> </span><span class="nv">$test_failed</span><span class="w"> </span>+<span class="w"> </span><span class="m">1</span><span class="k">)</span> <span class="w"> </span><span class="nb">printf</span><span class="w"> </span><span class="s2">" %2d) %-60s [failed]\n"</span><span class="w"> </span><span class="nv">$test_count</span><span class="w"> </span><span class="s2">"</span><span class="nv">$desc</span><span class="s2">"</span> <span cla