summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitignore5
-rw-r--r--Makefile21
-rw-r--r--cgit-doc.css3
-rw-r--r--cgit.c87
-rw-r--r--cgit.css2
-rw-r--r--cgit.h41
-rw-r--r--cgitrc.5.txt187
-rw-r--r--cmd.c2
-rwxr-xr-xfilters/commit-links.sh12
-rwxr-xr-xfilters/syntax-highlighting.sh39
m---------git0
-rw-r--r--shared.c38
-rw-r--r--ui-atom.c8
-rw-r--r--ui-blob.c8
-rw-r--r--ui-commit.c20
-rw-r--r--ui-log.c4
-rw-r--r--ui-patch.c6
-rw-r--r--ui-plain.c18
-rw-r--r--ui-refs.c19
-rw-r--r--ui-repolist.c9
-rw-r--r--ui-shared.c81
-rw-r--r--ui-shared.h1
-rw-r--r--ui-snapshot.c35
-rw-r--r--ui-summary.c28
-rw-r--r--ui-summary.h2
-rw-r--r--ui-tag.c2
-rw-r--r--ui-tree.c26
27 files changed, 553 insertions, 151 deletions
diff --git a/.gitignore b/.gitignore
index 1e016e5..487728b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,2 +4,7 @@ cgit.conf
VERSION
+cgitrc.5
+cgitrc.5.fo
+cgitrc.5.html
+cgitrc.5.pdf
+cgitrc.5.xml
*.o
diff --git a/Makefile b/Makefile
index 33c606d..1f9893a 100644
--- a/Makefile
+++ b/Makefile
@@ -7,3 +7,3 @@ CACHE_ROOT = /var/cache/cgit
SHA1_HEADER = <openssl/sha.h>
-GIT_VER = 1.6.1.1
+GIT_VER = 1.6.3.4
GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
@@ -102,3 +102,4 @@ endif
-.PHONY: all libgit test install uninstall clean force-version get-git
+.PHONY: all libgit test install uninstall clean force-version get-git \
+ doc man-doc html-doc clean-doc
@@ -151,5 +152,19 @@ uninstall:
-clean:
+doc: man-doc html-doc pdf-doc
+
+man-doc: cgitrc.5.txt
+ a2x -f manpage cgitrc.5.txt
+
+html-doc: cgitrc.5.txt
+ a2x -f xhtml --stylesheet=cgit-doc.css cgitrc.5.txt
+
+pdf-doc: cgitrc.5.txt
+ a2x -f pdf cgitrc.5.txt
+
+clean: clean-doc
rm -f cgit VERSION *.o *.d
+clean-doc:
+ rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
+
get-git:
diff --git a/cgit-doc.css b/cgit-doc.css
new file mode 100644
index 0000000..5a399b6
--- a/dev/null
+++ b/cgit-doc.css
@@ -0,0 +1,3 @@
+div.variablelist dt {
+ margin-top: 1em;
+}
diff --git a/cgit.c b/cgit.c
index 5301840..5816f3d 100644
--- a/cgit.c
+++ b/cgit.c
@@ -19,2 +19,25 @@ const char *cgit_version = CGIT_VERSION;
+void add_mimetype(const char *name, const char *value)
+{
+ struct string_list_item *item;
+
+ item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);
+ item->util = xstrdup(value);
+}
+
+struct cgit_filter *new_filter(const char *cmd, int extra_args)
+{
+ struct cgit_filter *f;
+
+ if (!cmd || !cmd[0])
+ return NULL;
+
+ f = xmalloc(sizeof(struct cgit_filter));
+ f->cmd = xstrdup(cmd);
+ f->argv = xmalloc((2 + extra_args) * sizeof(char *));
+ f->argv[0] = f->cmd;
+ f->argv[1] = NULL;
+ return f;
+}
+
void config_cb(const char *name, const char *value)
@@ -33,2 +56,4 @@ void config_cb(const char *name, const char *value)
ctx.cfg.footer = xstrdup(value);
+ else if (!strcmp(name, "head-include"))
+ ctx.cfg.head_include = xstrdup(value);
else if (!strcmp(name, "header"))
@@ -51,2 +76,6 @@ void config_cb(const char *name, const char *value)
ctx.cfg.nocache = atoi(value);
+ else if (!strcmp(name, "noplainemail"))
+ ctx.cfg.noplainemail = atoi(value);
+ else if (!strcmp(name, "noheader"))
+ ctx.cfg.noheader = atoi(value);
else if (!strcmp(name, "snapshots"))
@@ -73,2 +102,8 @@ void config_cb(const char *name, const char *value)
ctx.cfg.cache_dynamic_ttl = atoi(value);
+ else if (!strcmp(name, "about-filter"))
+ ctx.cfg.about_filter = new_filter(value, 0);
+ else if (!strcmp(name, "commit-filter"))
+ ctx.cfg.commit_filter = new_filter(value, 0);
+ else if (!strcmp(name, "embedded"))
+ ctx.cfg.embedded = atoi(value);
else if (!strcmp(name, "max-message-length"))
@@ -81,2 +116,4 @@ void config_cb(const char *name, const char *value)
ctx.cfg.max_commit_count = atoi(value);
+ else if (!strcmp(name, "source-filter"))
+ ctx.cfg.source_filter = new_filter(value, 1);
else if (!strcmp(name, "summary-log"))
@@ -97,2 +134,4 @@ void config_cb(const char *name, const char *value)
ctx.cfg.local_time = atoi(value);
+ else if (!prefixcmp(name, "mimetype."))
+ add_mimetype(name + 9, value);
else if (!strcmp(name, "repo.group"))
@@ -123,2 +162,8 @@ void config_cb(const char *name, const char *value)
ctx.repo->module_link= xstrdup(value);
+ else if (ctx.repo && !strcmp(name, "repo.about-filter"))
+ ctx.repo->about_filter = new_filter(value, 0);
+ else if (ctx.repo && !strcmp(name, "repo.commit-filter"))
+ ctx.repo->commit_filter = new_filter(value, 0);
+ else if (ctx.repo && !strcmp(name, "repo.source-filter"))
+ ctx.repo->source_filter = new_filter(value, 1);
else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
@@ -175,2 +220,7 @@ static void querystring_cb(const char *name, const char *value)
+char *xstrdupn(const char *str)
+{
+ return (str ? xstrdup(str) : NULL);
+}
+
static void prepare_context(struct cgit_context *ctx)
@@ -188,3 +238,3 @@ static void prepare_context(struct cgit_context *ctx)
ctx->cfg.css = "/cgit.css";
- ctx->cfg.logo = "/git-logo.png";
+ ctx->cfg.logo = "/cgit.png";
ctx->cfg.local_time = 0;
@@ -205,2 +255,12 @@ static void prepare_context(struct cgit_context *ctx)
ctx->cfg.summary_tags = 10;
+ ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
+ ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
+ ctx->env.https = xstrdupn(getenv("HTTPS"));
+ ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
+ ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
+ ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
+ ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
+ ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
+ ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
+ ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
ctx->page.mimetype = "text/html";
@@ -211,2 +271,10 @@ static void prepare_context(struct cgit_context *ctx)
ctx->page.expires = ctx->page.modified;
+ ctx->page.etag = NULL;
+ memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
+ if (ctx->env.script_name)
+ ctx->cfg.script_name = ctx->env.script_name;
+ if (ctx->env.query_string)
+ ctx->qry.raw = ctx->env.query_string;
+ if (!ctx->env.cgit_config)
+ ctx->env.cgit_config = CGIT_CONFIG;
}
@@ -290,2 +358,4 @@ static int prepare_repo_cmd(struct cgit_context *ctx)
ctx->qry.head = ctx->repo->defbranch;
+ ctx->page.status = 404;
+ ctx->page.statusmsg = "not found";
cgit_print_http_headers(ctx);
@@ -381,2 +451,5 @@ static void cgit_parse_args(int argc, const char **argv)
}
+ if (!strcmp(argv[i], "--nohttp")) {
+ ctx.env.no_http = "1";
+ }
if (!strncmp(argv[i], "--query=", 8)) {
@@ -433,3 +506,2 @@ int main(int argc, const char **argv)
{
- const char *cgit_config_env = getenv("CGIT_CONFIG");
const char *path;
@@ -443,9 +515,4 @@ int main(int argc, const char **argv)
- if (getenv("SCRIPT_NAME"))
- ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
- if (getenv("QUERY_STRING"))
- ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
cgit_parse_args(argc, argv);
- parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
- config_cb);
+ parse_configfile(ctx.env.cgit_config, config_cb);
ctx.repo = NULL;
@@ -464,3 +531,3 @@ int main(int argc, const char **argv)
*/
- path = getenv("PATH_INFO");
+ path = ctx.env.path_info;
if (!ctx.qry.url && path) {
@@ -480,2 +547,4 @@ int main(int argc, const char **argv)
ctx.page.expires += ttl*60;
+ if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
+ ctx.cfg.nocache = 1;
if (ctx.cfg.nocache)
diff --git a/cgit.css b/cgit.css
index adfc8ae..e3b32e7 100644
--- a/cgit.css
+++ b/cgit.css
@@ -157,3 +157,3 @@ table.list td.logmsg {
white-space: pre;
- padding: 1em 0em 2em 0em;
+ padding: 1em 0.5em 2em 0.5em;
}
diff --git a/cgit.h b/cgit.h
index 5f7af51..d90ccdc 100644
--- a/cgit.h
+++ b/cgit.h
@@ -17,2 +17,3 @@
#include <archive.h>
+#include <string-list.h>
#include <xdiff-interface.h>
@@ -50,2 +51,11 @@ typedef void (*linediff_fn)(char *line, int len);
+struct cgit_filter {
+ char *cmd;
+ char **argv;
+ int old_stdout;
+ int pipe_fh[2];
+ int pid;
+ int exitstatus;
+};
+
struct cgit_repo {
@@ -66,2 +76,5 @@ struct cgit_repo {
time_t mtime;
+ struct cgit_filter *about_filter;
+ struct cgit_filter *commit_filter;
+ struct cgit_filter *source_filter;
};
@@ -138,2 +151,3 @@ struct cgit_config {
char *footer;
+ char *head_include;
char *header;
@@ -157,2 +171,3 @@ struct cgit_config {
int cache_static_ttl;
+ int embedded;
int enable_index_links;
@@ -168,2 +183,4 @@ struct cgit_config {
int nocache;
+ int noplainemail;
+ int noheader;
int renamelimit;
@@ -173,2 +190,6 @@ struct cgit_config {
int summary_tags;
+ struct string_list mimetypes;
+ struct cgit_filter *about_filter;
+ struct cgit_filter *commit_filter;
+ struct cgit_filter *source_filter;
};
@@ -182,3 +203,19 @@ struct cgit_page {
char *filename;
+ char *etag;
char *title;
+ int status;
+ char *statusmsg;
+};
+
+struct cgit_environment {
+ char *cgit_config;
+ char *http_host;
+ char *https;
+ char *no_http;
+ char *path_info;
+ char *query_string;
+ char *request_method;
+ char *script_name;
+ char *server_name;
+ char *server_port;
};
@@ -186,2 +223,3 @@ struct cgit_page {
struct cgit_context {
+ struct cgit_environment env;
struct cgit_query qry;
@@ -244,2 +282,5 @@ extern int cgit_parse_snapshots_mask(const char *str);
+extern int cgit_open_filter(struct cgit_filter *filter);
+extern int cgit_close_filter(struct cgit_filter *filter);
+
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index fd299ae..3c35b02 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,3 +1,3 @@
-CGITRC
-======
+CGITRC(5)
+========
@@ -6,7 +6,7 @@ NAME
----
- cgitrc - runtime configuration for cgit
+cgitrc - runtime configuration for cgit
-DESCRIPTION
------------
+SYNOPSIS
+--------
Cgitrc contains all runtime settings for cgit, including the list of git
@@ -16,5 +16,19 @@ lines, and lines starting with '#', are ignored.
+LOCATION
+--------
+The default location of cgitrc, defined at compile time, is /etc/cgitrc. At
+runtime, cgit will consult the environment variable CGIT_CONFIG and, if
+defined, use its value instead.
+
+
GLOBAL SETTINGS
---------------
-agefile
+about-filter::
+ Specifies a command which will be invoked to format the content of
+ about pages (both top-level and for each repository). The command will
+ get the content of the about-file on its STDIN, and the STDOUT from the
+ command will be included verbatim on the about page. Default value:
+ none.
+
+agefile::
Specifies a path, relative to each repository path, which can be used
@@ -25,3 +39,3 @@ agefile
-cache-root
+cache-root::
Path used to store the cgit cache entries. Default value:
@@ -29,3 +43,3 @@ cache-root
-cache-dynamic-ttl
+cache-dynamic-ttl::
Number which specifies the time-to-live, in minutes, for the cached
@@ -34,3 +48,3 @@ cache-dynamic-ttl
-cache-repo-ttl
+cache-repo-ttl::
Number which specifies the time-to-live, in minutes, for the cached
@@ -38,3 +52,3 @@ cache-repo-ttl
-cache-root-ttl
+cache-root-ttl::
Number which specifies the time-to-live, in minutes, for the cached
@@ -42,3 +56,3 @@ cache-root-ttl
-cache-size
+cache-size::
The maximum number of entries in the cgit cache. Default value: "0"
@@ -46,3 +60,3 @@ cache-size
-cache-static-ttl
+cache-static-ttl::
Number which specifies the time-to-live, in minutes, for the cached
@@ -51,3 +65,3 @@ cache-static-ttl
-clone-prefix
+clone-prefix::
Space-separated list of common prefixes which, when combined with a
@@ -57,3 +71,9 @@ clone-prefix
-css
+commit-filter::
+ Specifies a command which will be invoked to format commit messages.
+ The command will get the message on its STDIN, and the STDOUT from the
+ command will be included verbatim as the commit message, i.e. this can
+ be used to implement bugtracker integration. Default value: none.
+
+css::
Url which specifies the css document to include in all cgit pages.
@@ -61,3 +81,8 @@ css
-enable-index-links
+embedded::
+ Flag which, when set to "1", will make cgit generate a html fragment
+ suitable for embedding in other html pages. Default value: none. See
+ also: "noheader".
+
+enable-index-links::
Flag which, when set to "1", will make cgit generate extra links for
@@ -66,3 +91,3 @@ enable-index-links
-enable-log-filecount
+enable-log-filecount::
Flag which, when set to "1", will make cgit print the number of
@@ -71,3 +96,3 @@ enable-log-filecount
-enable-log-linecount
+enable-log-linecount::
Flag which, when set to "1", will make cgit print the number of added
@@ -76,3 +101,3 @@ enable-log-linecount
-favicon
+favicon::
Url used as link to a shortcut icon for cgit. If specified, it is
@@ -81,3 +106,3 @@ favicon
-footer
+footer::
The content of the file specified with this option will be included
@@ -86,3 +111,7 @@ footer
-header
+head-include::
+ The content of the file specified with this option will be included
+ verbatim in the html HEAD section on all pages. Default value: none.
+
+header::
The content of the file specified with this option will be included
@@ -90,3 +119,3 @@ header
-include
+include::
Name of a configfile to include before the rest of the current config-
@@ -94,3 +123,3 @@ include
-index-header
+index-header::
The content of the file specified with this option will be included
@@ -100,3 +129,3 @@ index-header
-index-info
+index-info::
The content of the file specified with this option will be included
@@ -106,3 +135,3 @@ index-info
-local-time
+local-time::
Flag which, if set to "1", makes cgit print commit and tag times in the
@@ -110,7 +139,7 @@ local-time
-logo
+logo::
Url which specifies the source of an image which will be used as a logo
- on all cgit pages.
+ on all cgit pages. Default value: "/cgit.png".
-logo-link
+logo-link::
Url loaded when clicking on the cgit logo image. If unspecified the
@@ -119,3 +148,3 @@ logo-link
-max-commit-count
+max-commit-count::
Specifies the number of entries to list per page in "log" view. Default
@@ -123,3 +152,3 @@ max-commit-count
-max-message-length
+max-message-length::
Specifies the maximum number of commit message characters to display in
@@ -127,3 +156,3 @@ max-message-length
-max-repo-count
+max-repo-count::
Specifies the number of entries to list per page on the repository
@@ -131,3 +160,3 @@ max-repo-count
-max-repodesc-length
+max-repodesc-length::
Specifies the maximum number of repo description characters to display
@@ -135,3 +164,3 @@ max-repodesc-length
-max-stats
+max-stats::
Set the default maximum statistics period. Valid values are "week",
@@ -140,3 +169,7 @@ max-stats
-module-link
+mimetype.<ext>::
+ Set the mimetype for the specified filename extension. This is used
+ by the `plain` command when returning blob content.
+
+module-link::
Text which will be used as the formatstring for a hyperlink when a
@@ -146,3 +179,3 @@ module-link
-nocache
+nocache::
If set to the value "1" caching will be disabled. This settings is
@@ -151,3 +184,11 @@ nocache
-renamelimit
+noplainemail::
+ If set to "1" showing full author email adresses will be disabled.
+ Default value: "0".
+
+noheader::
+ Flag which, when set to "1", will make cgit omit the standard header
+ on all pages. Default value: none. See also: "embedded".
+
+renamelimit::
Maximum number of files to consider when detecting renames. The value
@@ -156,3 +197,3 @@ renamelimit
-repo.group
+repo.group::
A value for the current repository group, which all repositories
@@ -160,3 +201,3 @@ repo.group
-robots
+robots::
Text used as content for the "robots" meta-tag. Default value:
@@ -164,3 +205,3 @@ robots
-root-desc
+root-desc::
Text printed below the heading on the repository index page. Default
@@ -168,3 +209,3 @@ root-desc
-root-readme:
+root-readme::
The content of the file specified with this option will be included
@@ -173,3 +214,3 @@ root-readme:
-root-title
+root-title::
Text printed as heading on the repository index page. Default value:
@@ -177,3 +218,3 @@ root-title
-snapshots
+snapshots::
Text which specifies the default (and allowed) set of snapshot formats
@@ -187,3 +228,11 @@ snapshots
-summary-branches
+source-filter::
+ Specifies a command which will be invoked to format plaintext blobs
+ in the tree view. The command will get the blob content on its STDIN
+ and the name of the blob as its only command line argument. The STDOUT
+ from the command will be included verbatim as the blob contents, i.e.
+ this can be used to implement e.g. syntax highlighting. Default value:
+ none.
+
+summary-branches::
Specifies the number of branches to display in the repository "summary"
@@ -191,3 +240,3 @@ summary-branches
-summary-log
+summary-log::
Specifies the number of log entries to display in the repository
@@ -195,3 +244,3 @@ summary-log
-summary-tags
+summary-tags::
Specifies the number of tags to display in the repository "summary"
@@ -199,3 +248,3 @@ summary-tags
-virtual-root
+virtual-root::
Url which, if specified, will be used as root for all cgit links. It
@@ -209,3 +258,6 @@ REPOSITORY SETTINGS
-------------------
-repo.clone-url
+repo.about-filter::
+ Override the default about-filter. Default value: <about-filter>.
+
+repo.clone-url::
A list of space-separated urls which can be used to clone this repo.
@@ -213,3 +265,6 @@ repo.clone-url
-repo.defbranch
+repo.commit-filter::
+ Override the default commit-filter. Default value: <commit-filter>.
+
+repo.defbranch::
The name of the default branch for this repository. If no such branch
@@ -218,6 +273,6 @@ repo.defbranch
-repo.desc
+repo.desc::
The value to show as repository description. Default value: none.
-repo.enable-log-filecount
+repo.enable-log-filecount::
A flag which can be used to disable the global setting
@@ -225,3 +280,3 @@ repo.enable-log-filecount
-repo.enable-log-linecount
+repo.enable-log-linecount::
A flag which can be used to disable the global setting
@@ -229,3 +284,3 @@ repo.enable-log-linecount
-repo.max-stats
+repo.max-stats::
Override the default maximum statistics period. Valid values are equal
@@ -234,6 +289,6 @@ repo.max-stats
-repo.name
+repo.name::
The value to show as repository name. Default value: <repo.url>.
-repo.owner
+repo.owner::
A value used to identify the owner of the repository. Default value:
@@ -241,3 +296,3 @@ repo.owner
-repo.path
+repo.path::
An absolute path to the repository directory. For non-bare repositories
@@ -245,3 +300,3 @@ repo.path
-repo.readme
+repo.readme::
A path (relative to <repo.path>) which specifies a file to include
@@ -249,3 +304,3 @@ repo.readme
-repo.snapshots
+repo.snapshots::
A mask of allowed snapshot-formats for this repo, restricted by the
@@ -253,3 +308,6 @@ repo.snapshots
-repo.url
+repo.source-filter::
+ Override the default source-filter. Default value: <source-filter>.
+
+repo.url::
The relative url used to access the repository. This must be the first
@@ -261,2 +319,3 @@ EXAMPLE CGITRC FILE
+....
# Enable caching of up to 1000 output entriess
@@ -313,2 +372,15 @@ snapshots=tar.gz tar.bz2 zip
##
+## List of common mimetypes
+##
+
+mimetype.git=image/git
+mimetype.html=text/html
+mimetype.jpg=image/jpeg
+mimetype.jpeg=image/jpeg
+mimetype.pdf=application/pdf
+mimetype.png=image/png
+mimetype.svg=image/svg+xml
+
+
+##
## List of repositories.
@@ -370,2 +442,3 @@ repo.enable-log-linecount=0
repo.max-stats=month
+....
diff --git a/cmd.c b/cmd.c
index cf97da7..766f903 100644
--- a/cmd.c
+++ b/cmd.c
@@ -41,3 +41,3 @@ static void about_fn(struct cgit_context *ctx)
if (ctx->repo)
- cgit_print_repo_readme();
+ cgit_print_repo_readme(ctx->qry.path);
else
diff --git a/filters/commit-links.sh b/filters/commit-links.sh
new file mode 100755
index 0000000..165a533
--- a/dev/null
+++ b/filters/commit-links.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+# This script can be used to generate links in commit messages - the first
+# sed expression generates links to commits referenced by their SHA1, while
+# the second expression generates links to a fictional bugtracker.
+#
+# To use this script, refer to this file with either the commit-filter or the
+# repo.commit-filter options in cgitrc.
+
+sed -re '
+s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g
+s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g
+'
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
new file mode 100755
index 0000000..999ad0c
--- a/dev/null
+++ b/filters/syntax-highlighting.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+# This script can be used to implement syntax highlighting in the cgit
+# tree-view by refering to this file with the source-filter or repo.source-
+# filter options in cgitrc.
+#
+# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
+# highlighting, so you'll probably want something like the following included
+# in your css file (generated by highlight 2.4.8 and adapted for cgit):
+#
+# table.blob .num { color:#2928ff; }
+# table.blob .esc { color:#ff00ff; }
+# table.blob .str { color:#ff0000; }
+# table.blob .dstr { color:#818100; }
+# table.blob .slc { color:#838183; font-style:italic; }
+# table.blob .com { color:#838183; font-style:italic; }
+# table.blob .dir { color:#008200; }
+# table.blob .sym { color:#000000; }
+# table.blob .kwa { color:#000000; font-weight:bold; }
+# table.blob .kwb { color:#830000; }
+# table.blob .kwc { color:#000000; font-weight:bold; }
+# table.blob .kwd { color:#010181; }
+
+case "$1" in
+ *.c)
+ highlight -f -I -X -S c
+ ;;
+ *.h)
+ highlight -f -I -X -S c
+ ;;
+ *.sh)
+ highlight -f -I -X -S sh
+ ;;
+ *.css)
+ highlight -f -I -X -S css
+ ;;
+ *)
+ highlight -f -I -X -S txt
+ ;;
+esac
diff --git a/git b/git
-Subproject 5c415311f743ccb11a50f350ff1c385778f049d
+Subproject e276f018f2c1f0fc962fbe44a36708d1cdebada
diff --git a/shared.c b/shared.c
index cce0af4..911a55a 100644
--- a/shared.c
+++ b/shared.c
@@ -64,2 +64,5 @@ struct cgit_repo *cgit_add_repo(const char *url)
ret->mtime = -1;
+ ret->about_filter = ctx.cfg.about_filter;
+ ret->commit_filter = ctx.cfg.commit_filter;
+ ret->source_filter = ctx.cfg.source_filter;
return ret;
@@ -357 +360,36 @@ int cgit_parse_snapshots_mask(const char *str)
}
+
+int cgit_open_filter(struct cgit_filter *filter)
+{
+
+ filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
+ "Unable to duplicate STDOUT");
+ chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
+ filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
+ if (filter->pid == 0) {
+ close(filter->pipe_fh[1]);
+ chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
+ "Unable to use pipe as STDIN");
+ execvp(filter->cmd, filter->argv);
+ die("Unable to exec subprocess %s: %s (%d)", filter->cmd,
+ strerror(errno), errno);
+ }
+ close(filter->pipe_fh[0]);
+ chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
+ "Unable to use pipe as STDOUT");
+ close(filter->pipe_fh[1]);
+ return 0;
+}
+
+int cgit_close_filter(struct cgit_filter *filter)
+{
+ chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
+ "Unable to restore STDOUT");
+ close(filter->old_stdout);
+ if (filter->pid < 0)
+ return 0;
+ waitpid(filter->pid, &filter->exitstatus, 0);
+ if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus))
+ return 0;
+ die("Subprocess %s exited abnormally", filter->cmd);
+}
diff --git a/ui-atom.c b/ui-atom.c
index a6ea3ee..808b2d0 100644
--- a/ui-atom.c
+++ b/ui-atom.c
@@ -34,3 +34,3 @@ void add_entry(struct commit *commit, char *host)
}
- if (info->author_email) {
+ if (info->author_email && !ctx.cfg.noplainemail) {
mail = xstrdup(info->author_email);
@@ -54,3 +54,4 @@ void add_entry(struct commit *commit, char *host)
if (host) {
- html("<link rel='alternate' type='text/html' href='http://");
+ html("<link rel='alternate' type='text/html' href='");
+ html(cgit_httpscheme());
html_attr(host);
@@ -115,3 +116,4 @@ void cgit_print_atom(char *tip, char *path, int max_count)
if (host) {
- html("<link rel='alternate' type='text/html' href='http://");
+ html("<link rel='alternate' type='text/html' href='");
+ html(cgit_httpscheme());
html_attr(host);
diff --git a/ui-blob.c b/ui-blob.c
index 3cda03d..2ccd31d 100644
--- a/ui-blob.c
+++ b/ui-blob.c
@@ -29,3 +29,3 @@ void cgit_print_blob(const char *hex, char *path, const char *head)
enum object_type type;
- unsigned char *buf;
+ char *buf;
unsigned long size;
@@ -69,2 +69,8 @@ void cgit_print_blob(const char *hex, char *path, const char *head)
ctx.page.mimetype = ctx.qry.mimetype;
+ if (!ctx.page.mimetype) {
+ if (buffer_is_binary(buf, size))
+ ctx.page.mimetype = "application/octet-stream";
+ else
+ ctx.page.mimetype = "text/plain";
+ }
ctx.page.filename = path;
diff --git a/ui-commit.c b/ui-commit.c
index 41ce70e..d6b73ee 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -42,4 +42,6 @@ void cgit_print_commit(char *hex)
html_txt(info->author);
- html(" ");
- html_txt(info->author_email);
+ if (!ctx.cfg.noplainemail) {
+ html(" ");
+ html_txt(info->author_email);
+ }
html("</td><td class='right'>");
@@ -49,4 +51,6 @@ void cgit_print_commit(char *hex)
html_txt(info->committer);
- html(" ");
- html_txt(info->committer_email);
+ if (!ctx.cfg.noplainemail) {
+ html(" ");
+ html_txt(info->committer_email);
+ }
html("</td><td class='right'>");
@@ -91,3 +95,7 @@ void cgit_print_commit(char *hex)
html("<div class='commit-subject'>");
+ if (ctx.repo->commit_filter)
+ cgit_open_filter(ctx.repo->commit_filter);
html_txt(info->subject);
+ if (ctx.repo->commit_filter)
+ cgit_close_filter(ctx.repo->commit_filter);
show_commit_decorations(commit);
@@ -95,3 +103,7 @@ void cgit_print_commit(char *hex)
html("<div class='commit-msg'>");
+ if (ctx.repo->commit_filter)
+ cgit_open_filter(ctx.repo->commit_filter);
html_txt(info->msg);
+ if (ctx.repo->commit_filter)
+ cgit_close_filter(ctx.repo->commit_filter);
html("</div>");
diff --git a/ui-log.c b/ui-log.c
index ba2ab03..0b37785 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -55,2 +55,6 @@ void show_commit_decorations(struct commit *commit)
}
+ else if (!prefixcmp(deco->name, "refs/tags/")) {
+ strncpy(buf, deco->name + 10, sizeof(buf) - 1);
+ cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
+ }
else if (!prefixcmp(deco->name, "refs/remotes/")) {
diff --git a/ui-patch.c b/ui-patch.c
index 5d665d3..2a8f7a5 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -110,3 +110,7 @@ void cgit_print_patch(char *hex)
htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
- htmlf("From: %s %s\n", info->author, info->author_email);
+ htmlf("From: %s", info->author);
+ if (!ctx.cfg.noplainemail) {
+ htmlf(" %s", info->author_email);
+ }
+ html("\n");
html("Date: ");
diff --git a/ui-plain.c b/ui-plain.c
index 5addd9e..27c6dae 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -19,4 +19,5 @@ static void print_object(const unsigned char *sha1, const char *path)
enum object_type type;
- char *buf;
+ char *buf, *ext;
unsigned long size;
+ struct string_list_item *mime;
@@ -33,5 +34,18 @@ static void print_object(const unsigned char *sha1, const char *path)
}
- ctx.page.mimetype = "text/plain";
+ ctx.page.mimetype = NULL;
+ ext = strrchr(path, '.');
+ if (ext && *(++ext)) {
+ mime = string_list_lookup(ext, &ctx.cfg.mimetypes);
+ if (mime)
+ ctx.page.mimetype = (char *)mime->util;
+ }
+ if (!ctx.page.mimetype) {
+ if (buffer_is_binary(buf, size))
+ ctx.page.mimetype = "application/octet-stream";
+ else
+ ctx.page.mimetype = "text/plain";
+ }
ctx.page.filename = fmt("%s", path);
ctx.page.size = size;
+ ctx.page.etag = sha1_to_hex(sha1);
cgit_print_http_headers(&ctx);
diff --git a/ui-refs.c b/ui-refs.c
index 25da00a..d3b4f6e 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -48,4 +48,15 @@ static int cmp_tag_age(const void *a, const void *b)
struct refinfo *r2 = *(struct refinfo **)b;
+ int r1date, r2date;
- return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
+ if (r1->object->type != OBJ_COMMIT)
+ r1date = r1->tag->tagger_date;
+ else
+ r1date = r1->commit->committer_date;
+
+ if (r2->object->type != OBJ_COMMIT)
+ r2date = r2->tag->tagger_date;
+ else
+ r2date = r2->commit->committer_date;
+
+ return cmp_age(r1date, r2date);
}
@@ -147,2 +158,8 @@ static int print_tag(struct refinfo *ref)
cgit_object_link(ref->object);
+ html("</td><td>");
+ if (ref->object->type == OBJ_COMMIT)
+ html(ref->commit->author);
+ html("</td><td colspan='2'>");
+ if (ref->object->type == OBJ_COMMIT)
+ cgit_print_age(ref->commit->commit->date, -1, NULL);
html("</td></tr>\n");
diff --git a/ui-repolist.c b/ui-repolist.c
index 2c13d50..25f076f 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -275,4 +275,9 @@ void cgit_print_site_readme()
{
- if (ctx.cfg.root_readme)
- html_include(ctx.cfg.root_readme);
+ if (!ctx.cfg.root_readme)
+ return;
+ if (ctx.cfg.about_filter)
+ cgit_open_filter(ctx.cfg.about_filter);
+ html_include(ctx.cfg.root_readme);
+ if (ctx.cfg.about_filter)
+ cgit_close_filter(ctx.cfg.about_filter);
}
diff --git a/ui-shared.c b/ui-shared.c
index 40060ba..07d5dd4 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -36,20 +36,19 @@ void cgit_print_error(char *msg)
-char *cgit_hosturl()
+char *cgit_httpscheme()
{
- char *host, *port;
+ if (ctx.env.https && !strcmp(ctx.env.https, "on"))
+ return "https://";
+ else
+ return "http://";
+}
- host = getenv("HTTP_HOST");
- if (host) {
- host = xstrdup(host);
- } else {
- host = getenv("SERVER_NAME");
- if (!host)
- return NULL;
- port = getenv("SERVER_PORT");
- if (port && atoi(port) != 80)
- host = xstrdup(fmt("%s:%d", host, atoi(port)));
- else
- host = xstrdup(host);
- }
- return host;
+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));
}
@@ -458,2 +457,7 @@ 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)
@@ -470,3 +474,7 @@ void cgit_print_http_headers(struct cgit_context *ctx)
htmlf("Expires: %s\n", http_date(ctx->page.expires));
+ if (ctx->page.etag)
+ htmlf("ETag: \"%s\"\n", ctx->page.etag);
html("\n");
+ if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD"))
+ exit(0);
}
@@ -475,2 +483,8 @@ void cgit_print_docstart(struct cgit_context *ctx)
{
+ if (ctx->cfg.embedded) {
+ if (ctx->cfg.header)
+ html_include(ctx->cfg.header);
+ return;
+ }
+
char *host = cgit_hosturl();
@@ -494,3 +508,4 @@ void cgit_print_docstart(struct cgit_context *ctx)
if (host && ctx->repo) {
- html("<link rel='alternate' title='Atom feed' href='http://");
+ html("<link rel='alternate' title='Atom feed' href='");
+ html(cgit_httpscheme());
html_attr(cgit_hosturl());
@@ -498,4 +513,6 @@ void cgit_print_docstart(struct cgit_context *ctx)
fmt("h=%s", ctx->qry.head)));
- html("' type='application/atom+xml'/>");
+ html("' type='application/atom+xml'/>\n");
}
+ if (ctx->cfg.head_include)
+ html_include(ctx->cfg.head_include);
html("</head>\n");
@@ -508,3 +525,9 @@ void cgit_print_docend()
{
- html("</div>");
+ html("</div> <!-- class=content -->\n");
+ if (ctx.cfg.embedded) {
+ html("</div> <!-- id=cgit -->\n");
+ if (ctx.cfg.footer)
+ html_include(ctx.cfg.footer);
+ return;
+ }
if (ctx.cfg.footer)
@@ -517,2 +540,3 @@ void cgit_print_docend()
}
+ html("</div> <!-- id=cgit -->\n");
html("</body>\n</html>\n");
@@ -604,9 +628,4 @@ char *hc(struct cgit_cmd *cmd, const char *page)
-void cgit_print_pageheader(struct cgit_context *ctx)
+static void print_header(struct cgit_context *ctx)
{
- struct cgit_cmd *cmd = cgit_get_cmd(ctx);
-
- if (!cmd && ctx->repo)
- fallback_cmd = "summary";
-
html("<table id='header'>\n");
@@ -654,2 +673,14 @@ void cgit_print_pageheader(struct cgit_context *ctx)
html("</td></tr></table>\n");
+}
+
+void cgit_print_pageheader(struct cgit_context *ctx)
+{
+ struct cgit_cmd *cmd = cgit_get_cmd(ctx);
+
+ if (!cmd && ctx->repo)
+ fallback_cmd = "summary";
+
+ html("<div id='cgit'>");
+ if (!ctx->cfg.noheader)
+ print_header(ctx);
diff --git a/ui-shared.h b/ui-shared.h
index 5a3821f..bff4826 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -3,2 +3,3 @@
+extern char *cgit_httpscheme();
extern char *cgit_hosturl();
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 5372f5d..4136b3e 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -14,33 +14,12 @@ static int write_compressed_tar_archive(struct archiver_args *args,const char *f
{
- int rw[2];
- pid_t gzpid;
- int stdout2;
- int status;
int rv;
+ struct cgit_filter f;
- stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing");
- chk_zero(pipe(rw), "Opening pipe from compressor subprocess");
- gzpid = chk_non_negative(fork(), "Forking compressor subprocess");
- if(gzpid==0) {
- /* child */
- chk_zero(close(rw[1]), "Closing write end of pipe in child");
- chk_zero(close(STDIN_FILENO), "Closing STDIN");
- chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin");
- execlp(filter,filter,NULL);
- _exit(-1);
- }
- /* parent */
- chk_zero(close(rw[0]), "Closing read end of pipe");
- chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor");
-
+ f.cmd = xstrdup(filter);
+ f.argv = malloc(2 * sizeof(char *));
+ f.argv[0] = f.cmd;
+ f.argv[1] = NULL;
+ cgit_open_filter(&f);
rv = write_tar_archive(args);
-
- chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor");
- chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT");
- chk_zero(close(stdout2), "Closing uncompressed STDOUT");
- chk_zero(close(rw[1]), "Closing write end of pipe in parent");
- chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process");
- if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) )
- cgit_print_error("Failed to compress archive");
-
+ cgit_close_filter(&f);
return rv;
diff --git a/ui-summary.c b/ui-summary.c
index ede4a62..a2c018e 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -68,9 +68,25 @@ void cgit_print_summary()
-void cgit_print_repo_readme()
+void cgit_print_repo_readme(char *path)
{
- if (ctx.repo->readme) {
- html("<div id='summary'>");
- html_include(ctx.repo->readme);
- html("</div>");
- }
+ char *slash, *tmp;
+
+ if (!ctx.repo->readme)
+ return;
+
+ if (path) {
+ slash = strrchr(ctx.repo->readme, '/');
+ if (!slash)
+ return;
+ tmp = xmalloc(slash - ctx.repo->readme + 1 + strlen(path) + 1);
+ strncpy(tmp, ctx.repo->readme, slash - ctx.repo->readme + 1);
+ strcpy(tmp + (slash - ctx.repo->readme + 1), path);
+ } else
+ tmp = ctx.repo->readme;
+ html("<div id='summary'>");
+ if (ctx.repo->about_filter)
+ cgit_open_filter(ctx.repo->about_filter);
+ html_include(tmp);
+ if (ctx.repo->about_filter)
+ cgit_close_filter(ctx.repo->about_filter);
+ html("</div>");
}
diff --git a/ui-summary.h b/ui-summary.h
index 3e13039..c01f560 100644
--- a/ui-summary.h
+++ b/ui-summary.h
@@ -4,3 +4,3 @@
extern void cgit_print_summary();
-extern void cgit_print_repo_readme();
+extern void cgit_print_repo_readme(char *path);
diff --git a/ui-tag.c b/ui-tag.c
index 8c263ab..c2d72af 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -69,3 +69,3 @@ void cgit_print_tag(char *revname)
html_txt(info->tagger);
- if (info->tagger_email) {
+ if (info->tagger_email && !ctx.cfg.noplainemail) {
html(" ");
diff --git a/ui-tree.c b/ui-tree.c
index 553dbaa..c608754 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -17,3 +17,3 @@ int header = 0;
-static void print_text_buffer(char *buf, unsigned long size)
+static void print_text_buffer(const char *name, char *buf, unsigned long size)
{
@@ -24,2 +24,12 @@ static void print_text_buffer(char *buf, unsigned long size)
html("<table summary='blob content' class='blob'>\n");
+ if (ctx.repo->source_filter) {
+ html("<tr><td class='lines'><pre><code>");
+ ctx.repo->source_filter->argv[1] = xstrdup(name);
+ cgit_open_filter(ctx.repo->source_filter);
+ write(STDOUT_FILENO, buf, size);
+ cgit_close_filter(ctx.repo->source_filter);
+ html("</code></pre></td></tr></table>\n");
+ return;
+ }
+
html("<tr><td class='linenumbers'><pre>");
@@ -67,3 +77,3 @@ static void print_binary_buffer(char *buf, unsigned long size)
-static void print_object(const unsigned char *sha1, char *path)
+static void print_object(const unsigned char *sha1, char *path, const char *basename)
{
@@ -95,3 +105,3 @@ static void print_object(const unsigned char *sha1, char *path)
else
- print_text_buffer(buf, size);
+ print_text_buffer(basename, buf, size);
}
@@ -105,2 +115,3 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen,
char *fullpath;
+ char *class;
enum object_type type;
@@ -137,3 +148,8 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen,
} else {
- cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head,
+ class = strrchr(name, '.');
+ if (class != NULL) {
+ class = fmt("ls-blob %s", class + 1);
+ } else
+ class = "ls-blob";
+ cgit_tree_link(name, NULL, class, ctx.qry.head,
curr_rev, fullpath);
@@ -215,3 +231,3 @@ static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
} else {
- print_object(sha1, buffer);
+ print_object(sha1, buffer, pathname);
return 0;