summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c8
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt6
-rw-r--r--scan-tree.c8
-rw-r--r--shared.c2
-rw-r--r--ui-summary.c42
6 files changed, 43 insertions, 24 deletions
diff --git a/cgit.c b/cgit.c
index d6146e2..04cef23 100644
--- a/cgit.c
+++ b/cgit.c
@@ -27,121 +27,119 @@ void add_mimetype(const char *name, const char *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;
}
static void process_cached_repolist(const char *path);
void repo_config(struct cgit_repo *repo, const char *name, const char *value)
{
if (!strcmp(name, "name"))
repo->name = xstrdup(value);
else if (!strcmp(name, "clone-url"))
repo->clone_url = xstrdup(value);
else if (!strcmp(name, "desc"))
repo->desc = xstrdup(value);
else if (!strcmp(name, "owner"))
repo->owner = xstrdup(value);
else if (!strcmp(name, "defbranch"))
repo->defbranch = xstrdup(value);
else if (!strcmp(name, "snapshots"))
repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
else if (!strcmp(name, "enable-log-filecount"))
repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
else if (!strcmp(name, "enable-log-linecount"))
repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
else if (!strcmp(name, "enable-remote-branches"))
repo->enable_remote_branches = atoi(value);
else if (!strcmp(name, "enable-subject-links"))
repo->enable_subject_links = atoi(value);
else if (!strcmp(name, "max-stats"))
repo->max_stats = cgit_find_stats_period(value, NULL);
else if (!strcmp(name, "module-link"))
repo->module_link= xstrdup(value);
else if (!strcmp(name, "section"))
repo->section = xstrdup(value);
else if (!strcmp(name, "readme") && value != NULL) {
- char *colon;
- if (*value == '/' || ((colon = strchr(value, ':')) != NULL && colon != value && *(colon + 1) != '\0'))
- repo->readme = xstrdup(value);
- else
- repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
+ repo->readme = xstrdup(value);
} else if (ctx.cfg.enable_filter_overrides) {
if (!strcmp(name, "about-filter"))
repo->about_filter = new_filter(value, 0);
else if (!strcmp(name, "commit-filter"))
repo->commit_filter = new_filter(value, 0);
else if (!strcmp(name, "source-filter"))
repo->source_filter = new_filter(value, 1);
}
}
void config_cb(const char *name, const char *value)
{
if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
ctx.cfg.section = xstrdup(value);
else if (!strcmp(name, "repo.url"))
ctx.repo = cgit_add_repo(value);
else if (ctx.repo && !strcmp(name, "repo.path"))
ctx.repo->path = trim_end(value, '/');
else if (ctx.repo && !prefixcmp(name, "repo."))
repo_config(ctx.repo, name + 5, value);
+ else if (!strcmp(name, "readme"))
+ ctx.cfg.readme = xstrdup(value);
else if (!strcmp(name, "root-title"))
ctx.cfg.root_title = xstrdup(value);
else if (!strcmp(name, "root-desc"))
ctx.cfg.root_desc = xstrdup(value);
else if (!strcmp(name, "root-readme"))
ctx.cfg.root_readme = xstrdup(value);
else if (!strcmp(name, "css"))
ctx.cfg.css = xstrdup(value);
else if (!strcmp(name, "favicon"))
ctx.cfg.favicon = xstrdup(value);
else if (!strcmp(name, "footer"))
ctx.cfg.footer = xstrdup(value);
else if (!strcmp(name, "head-include"))
ctx.cfg.head_include = xstrdup(value);
else if (!strcmp(name, "header"))
ctx.cfg.header = xstrdup(value);
else if (!strcmp(name, "logo"))
ctx.cfg.logo = xstrdup(value);
else if (!strcmp(name, "index-header"))
ctx.cfg.index_header = xstrdup(value);
else if (!strcmp(name, "index-info"))
ctx.cfg.index_info = xstrdup(value);
else if (!strcmp(name, "logo-link"))
ctx.cfg.logo_link = xstrdup(value);
else if (!strcmp(name, "module-link"))
ctx.cfg.module_link = xstrdup(value);
else if (!strcmp(name, "virtual-root")) {
ctx.cfg.virtual_root = trim_end(value, '/');
if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
ctx.cfg.virtual_root = "";
} else if (!strcmp(name, "nocache"))
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"))
ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
else if (!strcmp(name, "enable-filter-overrides"))
ctx.cfg.enable_filter_overrides = atoi(value);
else if (!strcmp(name, "enable-gitweb-owner"))
ctx.cfg.enable_gitweb_owner = atoi(value);
else if (!strcmp(name, "enable-index-links"))
ctx.cfg.enable_index_links = atoi(value);
else if (!strcmp(name, "enable-log-filecount"))
ctx.cfg.enable_log_filecount = atoi(value);
else if (!strcmp(name, "enable-log-linecount"))
ctx.cfg.enable_log_linecount = atoi(value);
diff --git a/cgit.h b/cgit.h
index 4090cd4..72fb1c7 100644
--- a/cgit.h
+++ b/cgit.h
@@ -123,96 +123,97 @@ struct refinfo {
struct reflist {
struct refinfo **refs;
int alloc;
int count;
};
struct cgit_query {
int has_symref;
int has_sha1;
char *raw;
char *repo;
char *page;
char *search;
char *grep;
char *head;
char *sha1;
char *sha2;
char *path;
char *name;
char *mimetype;
char *url;
char *period;
int ofs;
int nohead;
char *sort;
int showmsg;
int ssdiff;
int show_all;
int context;
int ignorews;
char *vpath;
};
struct cgit_config {
char *agefile;
char *cache_root;
char *clone_prefix;
char *css;
char *favicon;
char *footer;
char *head_include;
char *header;
char *index_header;
char *index_info;
char *logo;
char *logo_link;
char *module_link;
char *project_list;
+ char *readme;
char *robots;
char *root_title;
char *root_desc;
char *root_readme;
char *script_name;
char *section;
char *virtual_root;
int cache_size;
int cache_dynamic_ttl;
int cache_max_create_time;
int cache_repo_ttl;
int cache_root_ttl;
int cache_scanrc_ttl;
int cache_static_ttl;
int embedded;
int enable_filter_overrides;
int enable_gitweb_owner;
int enable_index_links;
int enable_log_filecount;
int enable_log_linecount;
int enable_remote_branches;
int enable_subject_links;
int enable_tree_linenumbers;
int local_time;
int max_atom_items;
int max_repo_count;
int max_commit_count;
int max_lock_attempts;
int max_msg_len;
int max_repodesc_len;
int max_blob_size;
int max_stats;
int nocache;
int noplainemail;
int noheader;
int renamelimit;
int remove_suffix;
int snapshots;
int summary_branches;
int summary_log;
int summary_tags;
int ssdiff;
struct string_list mimetypes;
struct cgit_filter *about_filter;
struct cgit_filter *commit_filter;
struct cgit_filter *source_filter;
};
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index c643fae..187031a 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -189,96 +189,100 @@ max-message-length::
Specifies the maximum number of commit message characters to display in
"log" view. Default value: "80".
max-repo-count::
Specifies the number of entries to list per page on the repository
index page. Default value: "50".
max-repodesc-length::
Specifies the maximum number of repo description characters to display
on the repository index page. Default value: "80".
max-blob-size::
Specifies the maximum size of a blob to display HTML for in KBytes.
Default value: "0" (limit disabled).
max-stats::
Set the default maximum statistics period. Valid values are "week",
"month", "quarter" and "year". If unspecified, statistics are
disabled. Default value: none. See also: "repo.max-stats".
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
submodule is printed in a directory listing. The arguments for the
formatstring are the path and SHA1 of the submodule commit. Default
value: "./?repo=%s&page=commit&id=%s"
nocache::
If set to the value "1" caching will be disabled. This settings is
deprecated, and will not be honored starting with cgit-1.0. Default
value: "0".
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".
project-list::
A list of subdirectories inside of scan-path, relative to it, that
should loaded as git repositories. This must be defined prior to
scan-path. Default value: none. See also: scan-path.
+readme::
+ Text which will be used as default value for "repo.readme". Default
+ value: none.
+
remove-suffix::
If set to "1" and scan-path is enabled, if any repositories are found
with a suffix of ".git", this suffix will be removed for the url and
name. Default value: "0". See also: scan-path.
renamelimit::
Maximum number of files to consider when detecting renames. The value
"-1" uses the compiletime value in git (for further info, look at
`man git-diff`). Default value: "-1".
repo.group::
Legacy alias for "section". This option is deprecated and will not be
supported in cgit-1.0.
robots::
Text used as content for the "robots" meta-tag. Default value:
"index, nofollow".
root-desc::
Text printed below the heading on the repository index page. Default
value: "a fast webinterface for the git dscm".
root-readme::
The content of the file specified with this option will be included
verbatim below the "about" link on the repository index page. Default
value: none.
root-title::
Text printed as heading on the repository index page. Default value:
"Git Repository Browser".
scan-path::
A path which will be scanned for repositories. If caching is enabled,
the result will be cached as a cgitrc include-file in the cache
directory. If project-list has been defined prior to scan-path,
scan-path loads only the directories listed in the file pointed to by
project-list. Default value: none. See also: cache-scanrc-ttl,
project-list.
section::
The name of the current repository section - all repositories defined
after this option will inherit the current section name. Default value:
none.
side-by-side-diffs::
If set to "1" shows side-by-side diffs instead of unidiffs per
default. Default value: "0".
@@ -328,97 +332,97 @@ repo.clone-url::
repo.commit-filter::
Override the default commit-filter. Default value: none. See also:
"enable-filter-overrides".
repo.defbranch::
The name of the default branch for this repository. If no such branch
exists in the repository, the first branch name (when sorted) is used
as default instead. Default value: "master".
repo.desc::
The value to show as repository description. Default value: none.
repo.enable-log-filecount::
A flag which can be used to disable the global setting
`enable-log-filecount'. Default value: none.
repo.enable-log-linecount::
A flag which can be used to disable the global setting
`enable-log-linecount'. Default value: none.
repo.enable-remote-branches::
Flag which, when set to "1", will make cgit display remote branches
in the summary and refs views. Default value: <enable-remote-branches>.
repo.enable-subject-links::
A flag which can be used to override the global setting
`enable-subject-links'. Default value: none.
repo.max-stats::
Override the default maximum statistics period. Valid values are equal
to the values specified for the global "max-stats" setting. Default
value: none.
repo.name::
The value to show as repository name. Default value: <repo.url>.
repo.owner::
A value used to identify the owner of the repository. Default value:
none.
repo.path::
An absolute path to the repository directory. For non-bare repositories
this is the .git-directory. Default value: none.
repo.readme::
A path (relative to <repo.path>) which specifies a file to include
verbatim as the "About" page for this repo. You may also specify a
git refspec by head or by hash by prepending the refspec followed by
- a colon. For example, "master:docs/readme.mkd" Default value: none.
+ a colon. For example, "master:docs/readme.mkd" Default value: <readme>.
repo.snapshots::
A mask of allowed snapshot-formats for this repo, restricted by the
"snapshots" global setting. Default value: <snapshots>.
repo.section::
Override the current section name for this repository. Default value:
none.
repo.source-filter::
Override the default source-filter. Default value: none. See also:
"enable-filter-overrides".
repo.url::
The relative url used to access the repository. This must be the first
setting specified for each repo. Default value: none.
REPOSITORY-SPECIFIC CGITRC FILE
-------------------------------
When the option "scan-path" is used to auto-discover git repositories, cgit
will try to parse the file "cgitrc" within any found repository. Such a
repo-specific config file may contain any of the repo-specific options
described above, except "repo.url" and "repo.path". Additionally, the "filter"
options are only acknowledged in repo-specific config files when
"enable-filter-overrides" is set to "1".
Note: the "repo." prefix is dropped from the option names in repo-specific
config files, e.g. "repo.desc" becomes "desc".
EXAMPLE CGITRC FILE
-------------------
....
# Enable caching of up to 1000 output entriess
cache-size=1000
# Specify some default clone prefixes
clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
# Specify the css url
css=/css/cgit.css
# Show extra links for each repository on the index page
enable-index-links=1
diff --git a/scan-tree.c b/scan-tree.c
index e987824..780d405 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -65,99 +65,101 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
{
struct stat st;
struct passwd *pwd;
char *p;
size_t size;
if (stat(path, &st)) {
fprintf(stderr, "Error accessing %s: %s (%d)\n",
path, strerror(errno), errno);
return;
}
if (!stat(fmt("%s/noweb", path), &st))
return;
owner = NULL;
if (ctx.cfg.enable_gitweb_owner)
git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
if (base == path)
p = fmt("%s", path);
else
p = fmt("%s", path + strlen(base) + 1);
if (!strcmp(p + strlen(p) - 5, "/.git"))
p[strlen(p) - 5] = '\0';
repo = cgit_add_repo(xstrdup(p));
if (ctx.cfg.remove_suffix)
if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
*p = '\0';
repo->name = repo->url;
repo->path = xstrdup(path);
while (!owner) {
if ((pwd = getpwuid(st.st_uid)) == NULL) {
fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
path, strerror(errno), errno);
break;
}
if (pwd->pw_gecos)
if ((p = strchr(pwd->pw_gecos, ',')))
*p = '\0';
owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
}
repo->owner = owner;
p = fmt("%s/description", path);
if (!stat(p, &st))
readfile(p, &repo->desc, &size);
- p = fmt("%s/README.html", path);
- if (!stat(p, &st))
- repo->readme = "README.html";
+ if (!repo->readme) {
+ p = fmt("%s/README.html", path);
+ if (!stat(p, &st))
+ repo->readme = "README.html";
+ }
p = fmt("%s/cgitrc", path);
if (!stat(p, &st)) {
config_fn = fn;
parse_configfile(xstrdup(p), &repo_config);
}
}
static void scan_path(const char *base, const char *path, repo_config_fn fn)
{
DIR *dir;
struct dirent *ent;
char *buf;
struct stat st;
if (is_git_dir(path)) {
add_repo(base, path, fn);
return;
}
if (is_git_dir(fmt("%s/.git", path))) {
add_repo(base, fmt("%s/.git", path), fn);
return;
}
dir = opendir(path);
if (!dir) {
fprintf(stderr, "Error opening directory %s: %s (%d)\n",
path, strerror(errno), errno);
return;
}
while((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.') {
if (ent->d_name[1] == '\0')
continue;
if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
continue;
}
buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
if (!buf) {
fprintf(stderr, "Alloc error on %s: %s (%d)\n",
path, strerror(errno), errno);
exit(1);
}
sprintf(buf, "%s/%s", path, ent->d_name);
if (stat(buf, &st)) {
fprintf(stderr, "Error checking path %s: %s (%d)\n",
buf, strerror(errno), errno);
free(buf);
continue;
diff --git a/shared.c b/shared.c
index b42c2a2..72ac140 100644
--- a/shared.c
+++ b/shared.c
@@ -17,97 +17,97 @@ int chk_zero(int result, char *msg)
die("%s: %s", msg, strerror(errno));
return result;
}
int chk_positive(int result, char *msg)
{
if (result <= 0)
die("%s: %s", msg, strerror(errno));
return result;
}
int chk_non_negative(int result, char *msg)
{
if (result < 0)
die("%s: %s",msg, strerror(errno));
return result;
}
struct cgit_repo *cgit_add_repo(const char *url)
{
struct cgit_repo *ret;
if (++cgit_repolist.count > cgit_repolist.length) {
if (cgit_repolist.length == 0)
cgit_repolist.length = 8;
else
cgit_repolist.length *= 2;
cgit_repolist.repos = xrealloc(cgit_repolist.repos,
cgit_repolist.length *
sizeof(struct cgit_repo));
}
ret = &cgit_repolist.repos[cgit_repolist.count-1];
memset(ret, 0, sizeof(struct cgit_repo));
ret->url = trim_end(url, '/');
ret->name = ret->url;
ret->path = NULL;
ret->desc = "[no description]";
ret->owner = NULL;
ret->section = ctx.cfg.section;
ret->defbranch = "master";
ret->snapshots = ctx.cfg.snapshots;
ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
ret->enable_subject_links = ctx.cfg.enable_subject_links;
ret->max_stats = ctx.cfg.max_stats;
ret->module_link = ctx.cfg.module_link;
- ret->readme = NULL;
+ ret->readme = ctx.cfg.readme;
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;
}
struct cgit_repo *cgit_get_repoinfo(const char *url)
{
int i;
struct cgit_repo *repo;
for (i=0; i<cgit_repolist.count; i++) {
repo = &cgit_repolist.repos[i];
if (!strcmp(repo->url, url))
return repo;
}
return NULL;
}
void *cgit_free_commitinfo(struct commitinfo *info)
{
free(info->author);
free(info->author_email);
free(info->committer);
free(info->committer_email);
free(info->subject);
free(info->msg);
free(info->msg_encoding);
free(info);
return NULL;
}
char *trim_end(const char *str, char c)
{
int len;
char *s, *t;
if (str == NULL)
return NULL;
t = (char *)str;
len = strlen(t);
while(len > 0 && t[len - 1] == c)
len--;
if (len == 0)
return NULL;
diff --git a/ui-summary.c b/ui-summary.c
index 02f191e..b203bcc 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -25,86 +25,100 @@ static void print_url(char *base, char *suffix)
}
if (suffix && *suffix)
base = fmt("%s/%s", base, suffix);
html("<tr><td colspan='4'><a href='");
html_url_path(base);
html("'>");
html_txt(base);
html("</a></td></tr>\n");
}
static void print_urls(char *txt, char *suffix)
{
char *h = txt, *t, c;
while (h && *h) {
while (h && *h == ' ')
h++;
t = h;
while (t && *t && *t != ' ')
t++;
c = *t;
*t = 0;
print_url(h, suffix);
*t = c;
h = t;
}
}
void cgit_print_summary()
{
html("<table summary='repository info' class='list nowrap'>");
cgit_print_branches(ctx.cfg.summary_branches);
html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
cgit_print_tags(ctx.cfg.summary_tags);
if (ctx.cfg.summary_log > 0) {
html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
NULL, NULL, 0);
}
if (ctx.repo->clone_url)
print_urls(ctx.repo->clone_url, NULL);
else if (ctx.cfg.clone_prefix)
print_urls(ctx.cfg.clone_prefix, ctx.repo->url);
html("</table>");
}
void cgit_print_repo_readme(char *path)
{
- char *slash, *tmp, *colon, *ref = 0;
+ char *slash, *tmp, *colon, *ref;
- if (!ctx.repo->readme)
+ if (!ctx.repo->readme || !(*ctx.repo->readme))
return;
+ ref = NULL;
+
+ /* Check if the readme is tracked in the git repo. */
+ colon = strchr(ctx.repo->readme, ':');
+ if (colon && strlen(colon) > 1) {
+ *colon = '\0';
+ ref = ctx.repo->readme;
+ ctx.repo->readme = colon + 1;
+ if (!(*ctx.repo->readme))
+ return;
+ }
+
+ /* Prepend repo path to relative readme path unless tracked. */
+ if (!ref && *ctx.repo->readme != '/')
+ ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path,
+ ctx.repo->readme));
+
+ /* If a subpath is specified for the about page, make it relative
+ * to the directory containing the configured readme.
+ */
if (path) {
slash = strrchr(ctx.repo->readme, '/');
if (!slash) {
- slash = strchr(ctx.repo->readme, ':');
- if (!slash)
+ if (!colon)
return;
+ slash = colon;
}
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;
- colon = strchr(tmp, ':');
- if (colon && strlen(colon) > 1) {
- *colon = '\0';
- ref = tmp;
- tmp = colon + 1;
- while ((*tmp == '/' || *tmp == ':') && *tmp != '\0')
- ++tmp;
- if (!(*tmp))
- return;
- }
+
+ /* Print the calculated readme, either from the git repo or from the
+ * filesystem, while applying the about-filter.
+ */
html("<div id='summary'>");
if (ctx.repo->about_filter)
cgit_open_filter(ctx.repo->about_filter);
if (ref)
cgit_print_file(tmp, ref);
else
html_include(tmp);
if (ctx.repo->about_filter)
cgit_close_filter(ctx.repo->about_filter);
html("</div>");
}