summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-04-08 19:29:21 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-04-08 19:29:21 (UTC)
commit23296ad648c0e2a9e3cf40a3de322b10ad25cce3 (patch) (side-by-side diff)
tree136493d8228b0ff4971feb06b0e8aee296367b00
parente2a44cf0923398396b7a321d5ce894ad3bf6f580 (diff)
parentc6f747649ace1a92ed5dfaae9cc1ea3affe0bf51 (diff)
downloadcgit-23296ad648c0e2a9e3cf40a3de322b10ad25cce3.zip
cgit-23296ad648c0e2a9e3cf40a3de322b10ad25cce3.tar.gz
cgit-23296ad648c0e2a9e3cf40a3de322b10ad25cce3.tar.bz2
Merge branch 'lh/cleanup'
* lh/cleanup: (21 commits) Reset ctx.repo to NULL when the config parser is finished Move cgit_parse_query() from parsing.c to html.c as http_parse_querystring() Move function for configfile parsing into configfile.[ch] Add cache.h Remove global and obsolete cgit_cmd Makefile: copy the QUIET constructs from the Makefile in git.git Move cgit_version from shared.c to cgit.c Makefile: autobuild dependency rules Initial Makefile cleanup Move non-generic functions from shared.c to cgit.c Add ui-shared.h Add separate header-files for each page/view Refactor snapshot support Add command dispatcher Remove obsolete cacheitem parameter to ui-functions Add struct cgit_page to cgit_context Introduce html.h Improve initialization of git directory Move cgit_repo into cgit_context Add all config variables into struct cgit_context ...
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitignore1
-rw-r--r--Makefile75
-rw-r--r--cache.c21
-rw-r--r--cache.h23
-rw-r--r--cgit.c391
-rw-r--r--cgit.h237
-rw-r--r--cmd.c112
-rw-r--r--cmd.h15
-rw-r--r--configfile.c87
-rw-r--r--configfile.h8
-rw-r--r--html.c95
-rw-r--r--html.h20
-rw-r--r--parsing.c146
-rw-r--r--shared.c257
-rw-r--r--ui-blob.c16
-rw-r--r--ui-blob.h6
-rw-r--r--ui-commit.c26
-rw-r--r--ui-commit.h6
-rw-r--r--ui-diff.c9
-rw-r--r--ui-diff.h7
-rw-r--r--ui-log.c32
-rw-r--r--ui-log.h7
-rw-r--r--ui-patch.c10
-rw-r--r--ui-patch.h6
-rw-r--r--ui-refs.c175
-rw-r--r--ui-refs.h8
-rw-r--r--ui-repolist.c54
-rw-r--r--ui-repolist.h6
-rw-r--r--ui-shared.c237
-rw-r--r--ui-shared.h36
-rw-r--r--ui-snapshot.c123
-rw-r--r--ui-snapshot.h8
-rw-r--r--ui-summary.c189
-rw-r--r--ui-summary.h6
-rw-r--r--ui-tag.c3
-rw-r--r--ui-tag.h6
-rw-r--r--ui-tree.c26
-rw-r--r--ui-tree.h6
38 files changed, 1379 insertions, 1117 deletions
diff --git a/.gitignore b/.gitignore
index aa36ff7..1e016e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Files I don't care to see in git-status/commit
cgit
cgit.conf
VERSION
*.o
+*.d
diff --git a/Makefile b/Makefile
index 8bf47f2..931c60e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,83 +1,136 @@
CGIT_VERSION = v0.7.2
CGIT_SCRIPT_NAME = cgit.cgi
CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
CGIT_CONFIG = /etc/cgitrc
CACHE_ROOT = /var/cache/cgit
SHA1_HEADER = <openssl/sha.h>
GIT_VER = 1.5.5.rc1
GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
#
# Let the user override the above settings.
#
-include cgit.conf
+#
+# Define a way to invoke make in subdirs quietly, shamelessly ripped
+# from git.git
+#
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1 =
-EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
-OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
- ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
- ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o ui-patch.o
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifndef V
+ QUIET_CC = @echo ' ' CC $@;
+ QUIET_MM = @echo ' ' MM $@;
+ QUIET_SUBDIR0 = +@subdir=
+ QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
+ $(MAKE) $(PRINT_DIR) -C $$subdir
+endif
+
+#
+# Define a pattern rule for automatic dependency building
+#
+%.d: %.c
+ $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@
+
+#
+# Define a pattern rule for silent object building
+#
+%.o: %.c
+ $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
+EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
+OBJECTS =
+OBJECTS += cache.o
+OBJECTS += cgit.o
+OBJECTS += cmd.o
+OBJECTS += configfile.o
+OBJECTS += html.o
+OBJECTS += parsing.o
+OBJECTS += shared.o
+OBJECTS += ui-blob.o
+OBJECTS += ui-commit.o
+OBJECTS += ui-diff.o
+OBJECTS += ui-log.o
+OBJECTS += ui-patch.o
+OBJECTS += ui-refs.o
+OBJECTS += ui-repolist.o
+OBJECTS += ui-shared.o
+OBJECTS += ui-snapshot.o
+OBJECTS += ui-summary.o
+OBJECTS += ui-tag.o
+OBJECTS += ui-tree.o
+
ifdef NEEDS_LIBICONV
EXTLIBS += -liconv
endif
.PHONY: all git test install clean distclean emptycache force-version get-git
all: cgit git
VERSION: force-version
@./gen-version.sh "$(CGIT_VERSION)"
-include VERSION
CFLAGS += -g -Wall -Igit
CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
-cgit: cgit.c $(OBJECTS)
- $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
+cgit: $(OBJECTS)
+ $(QUIET_CC)$(CC) $(CFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
+
+$(OBJECTS): git/xdiff/lib.a git/libgit.a
+
+cgit.o: VERSION
-$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
+-include $(OBJECTS:.o=.d)
git/xdiff/lib.a: | git
git/libgit.a: | git
git:
- cd git && $(MAKE) xdiff/lib.a
- cd git && $(MAKE) libgit.a
+ $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) xdiff/lib.a
+ $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) libgit.a
test: all
- $(MAKE) -C tests
+ $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
install: all
mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH)
install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css
install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png
uninstall:
rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
rm -f $(CGIT_SCRIPT_PATH)/cgit.css
rm -f $(CGIT_SCRIPT_PATH)/cgit.png
clean:
- rm -f cgit VERSION *.o
+ rm -f cgit VERSION *.o *.d
cd git && $(MAKE) clean
distclean: clean
git clean -d -x
cd git && git clean -d -x
emptycache:
rm -rf $(DESTDIR)$(CACHE_ROOT)/*
get-git:
curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cache.c b/cache.c
index 372e38d..89f7ecd 100644
--- a/cache.c
+++ b/cache.c
@@ -1,118 +1,119 @@
/* cache.c: cache management
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "cache.h"
const int NOLOCK = -1;
char *cache_safe_filename(const char *unsafe)
{
static char buf[4][PATH_MAX];
static int bufidx;
char *s;
char c;
bufidx++;
bufidx &= 3;
s = buf[bufidx];
while(unsafe && (c = *unsafe++) != 0) {
if (c == '/' || c == ' ' || c == '&' || c == '|' ||
c == '>' || c == '<' || c == '.')
c = '_';
*s++ = c;
}
*s = '\0';
return buf[bufidx];
}
int cache_exist(struct cacheitem *item)
{
if (stat(item->name, &item->st)) {
item->st.st_mtime = 0;
return 0;
}
return 1;
}
int cache_create_dirs()
{
char *path;
- path = fmt("%s", cgit_cache_root);
+ path = fmt("%s", ctx.cfg.cache_root);
if (mkdir(path, S_IRWXU) && errno!=EEXIST)
return 0;
- if (!cgit_repo)
+ if (!ctx.repo)
return 0;
- path = fmt("%s/%s", cgit_cache_root,
- cache_safe_filename(cgit_repo->url));
+ path = fmt("%s/%s", ctx.cfg.cache_root,
+ cache_safe_filename(ctx.repo->url));
if (mkdir(path, S_IRWXU) && errno!=EEXIST)
return 0;
- if (cgit_query_page) {
- path = fmt("%s/%s/%s", cgit_cache_root,
- cache_safe_filename(cgit_repo->url),
- cgit_query_page);
+ if (ctx.qry.page) {
+ path = fmt("%s/%s/%s", ctx.cfg.cache_root,
+ cache_safe_filename(ctx.repo->url),
+ ctx.qry.page);
if (mkdir(path, S_IRWXU) && errno!=EEXIST)
return 0;
}
return 1;
}
int cache_refill_overdue(const char *lockfile)
{
struct stat st;
if (stat(lockfile, &st))
return 0;
else
- return (time(NULL) - st.st_mtime > cgit_cache_max_create_time);
+ return (time(NULL) - st.st_mtime > ctx.cfg.cache_max_create_time);
}
int cache_lock(struct cacheitem *item)
{
int i = 0;
char *lockfile = xstrdup(fmt("%s.lock", item->name));
top:
- if (++i > cgit_max_lock_attempts)
+ if (++i > ctx.cfg.max_lock_attempts)
die("cache_lock: unable to lock %s: %s",
item->name, strerror(errno));
item->fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
if (item->fd == NOLOCK && errno == ENOENT && cache_create_dirs())
goto top;
if (item->fd == NOLOCK && errno == EEXIST &&
cache_refill_overdue(lockfile) && !unlink(lockfile))
goto top;
free(lockfile);
return (item->fd > 0);
}
int cache_unlock(struct cacheitem *item)
{
close(item->fd);
return (rename(fmt("%s.lock", item->name), item->name) == 0);
}
int cache_cancel_lock(struct cacheitem *item)
{
return (unlink(fmt("%s.lock", item->name)) == 0);
}
int cache_expired(struct cacheitem *item)
{
if (item->ttl < 0)
return 0;
return item->st.st_mtime + item->ttl * 60 < time(NULL);
diff --git a/cache.h b/cache.h
new file mode 100644
index 0000000..4dcbea3
--- a/dev/null
+++ b/cache.h
@@ -0,0 +1,23 @@
+/*
+ * Since git has it's own cache.h which we include,
+ * lets test on CGIT_CACHE_H to avoid confusion
+ */
+
+#ifndef CGIT_CACHE_H
+#define CGIT_CACHE_H
+
+struct cacheitem {
+ char *name;
+ struct stat st;
+ int ttl;
+ int fd;
+};
+
+extern char *cache_safe_filename(const char *unsafe);
+extern int cache_lock(struct cacheitem *item);
+extern int cache_unlock(struct cacheitem *item);
+extern int cache_cancel_lock(struct cacheitem *item);
+extern int cache_exist(struct cacheitem *item);
+extern int cache_expired(struct cacheitem *item);
+
+#endif /* CGIT_CACHE_H */
diff --git a/cgit.c b/cgit.c
index e8acc03..6ec763f 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,318 +1,459 @@
/* cgit.c: cgi for the git scm
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "cache.h"
+#include "cmd.h"
+#include "configfile.h"
+#include "html.h"
+#include "ui-shared.h"
+
+const char *cgit_version = CGIT_VERSION;
+
+void config_cb(const char *name, const char *value)
+{
+ if (!strcmp(name, "root-title"))
+ ctx.cfg.root_title = xstrdup(value);
+ else if (!strcmp(name, "css"))
+ ctx.cfg.css = 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, "snapshots"))
+ ctx.cfg.snapshots = cgit_parse_snapshots_mask(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);
+ else if (!strcmp(name, "cache-root"))
+ ctx.cfg.cache_root = xstrdup(value);
+ else if (!strcmp(name, "cache-root-ttl"))
+ ctx.cfg.cache_root_ttl = atoi(value);
+ else if (!strcmp(name, "cache-repo-ttl"))
+ ctx.cfg.cache_repo_ttl = atoi(value);
+ else if (!strcmp(name, "cache-static-ttl"))
+ ctx.cfg.cache_static_ttl = atoi(value);
+ else if (!strcmp(name, "cache-dynamic-ttl"))
+ ctx.cfg.cache_dynamic_ttl = atoi(value);
+ else if (!strcmp(name, "max-message-length"))
+ ctx.cfg.max_msg_len = atoi(value);
+ else if (!strcmp(name, "max-repodesc-length"))
+ ctx.cfg.max_repodesc_len = atoi(value);
+ else if (!strcmp(name, "max-commit-count"))
+ ctx.cfg.max_commit_count = atoi(value);
+ else if (!strcmp(name, "summary-log"))
+ ctx.cfg.summary_log = atoi(value);
+ else if (!strcmp(name, "summary-branches"))
+ ctx.cfg.summary_branches = atoi(value);
+ else if (!strcmp(name, "summary-tags"))
+ ctx.cfg.summary_tags = atoi(value);
+ else if (!strcmp(name, "agefile"))
+ ctx.cfg.agefile = xstrdup(value);
+ else if (!strcmp(name, "renamelimit"))
+ ctx.cfg.renamelimit = atoi(value);
+ else if (!strcmp(name, "robots"))
+ ctx.cfg.robots = xstrdup(value);
+ else if (!strcmp(name, "clone-prefix"))
+ ctx.cfg.clone_prefix = xstrdup(value);
+ else if (!strcmp(name, "repo.group"))
+ ctx.cfg.repo_group = xstrdup(value);
+ else if (!strcmp(name, "repo.url"))
+ ctx.repo = cgit_add_repo(value);
+ else if (!strcmp(name, "repo.name"))
+ ctx.repo->name = xstrdup(value);
+ else if (ctx.repo && !strcmp(name, "repo.path"))
+ ctx.repo->path = trim_end(value, '/');
+ else if (ctx.repo && !strcmp(name, "repo.clone-url"))
+ ctx.repo->clone_url = xstrdup(value);
+ else if (ctx.repo && !strcmp(name, "repo.desc"))
+ ctx.repo->desc = xstrdup(value);
+ else if (ctx.repo && !strcmp(name, "repo.owner"))
+ ctx.repo->owner = xstrdup(value);
+ else if (ctx.repo && !strcmp(name, "repo.defbranch"))
+ ctx.repo->defbranch = xstrdup(value);
+ else if (ctx.repo && !strcmp(name, "repo.snapshots"))
+ ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
+ else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
+ ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
+ else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
+ ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
+ else if (ctx.repo && !strcmp(name, "repo.module-link"))
+ ctx.repo->module_link= xstrdup(value);
+ else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
+ if (*value == '/')
+ ctx.repo->readme = xstrdup(value);
+ else
+ ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
+ } else if (!strcmp(name, "include"))
+ parse_configfile(value, config_cb);
+}
+
+static void querystring_cb(const char *name, const char *value)
+{
+ if (!strcmp(name,"r")) {
+ ctx.qry.repo = xstrdup(value);
+ ctx.repo = cgit_get_repoinfo(value);
+ } else if (!strcmp(name, "p")) {
+ ctx.qry.page = xstrdup(value);
+ } else if (!strcmp(name, "url")) {
+ cgit_parse_url(value);
+ } else if (!strcmp(name, "qt")) {
+ ctx.qry.grep = xstrdup(value);
+ } else if (!strcmp(name, "q")) {
+ ctx.qry.search = xstrdup(value);
+ } else if (!strcmp(name, "h")) {
+ ctx.qry.head = xstrdup(value);
+ ctx.qry.has_symref = 1;
+ } else if (!strcmp(name, "id")) {
+ ctx.qry.sha1 = xstrdup(value);
+ ctx.qry.has_sha1 = 1;
+ } else if (!strcmp(name, "id2")) {
+ ctx.qry.sha2 = xstrdup(value);
+ ctx.qry.has_sha1 = 1;
+ } else if (!strcmp(name, "ofs")) {
+ ctx.qry.ofs = atoi(value);
+ } else if (!strcmp(name, "path")) {
+ ctx.qry.path = trim_end(value, '/');
+ } else if (!strcmp(name, "name")) {
+ ctx.qry.name = xstrdup(value);
+ }
+}
+
+static void prepare_context(struct cgit_context *ctx)
+{
+ memset(ctx, 0, sizeof(ctx));
+ ctx->cfg.agefile = "info/web/last-modified";
+ ctx->cfg.cache_dynamic_ttl = 5;
+ ctx->cfg.cache_max_create_time = 5;
+ ctx->cfg.cache_repo_ttl = 5;
+ ctx->cfg.cache_root = CGIT_CACHE_ROOT;
+ ctx->cfg.cache_root_ttl = 5;
+ ctx->cfg.cache_static_ttl = -1;
+ ctx->cfg.css = "/cgit.css";
+ ctx->cfg.logo = "/git-logo.png";
+ ctx->cfg.max_commit_count = 50;
+ ctx->cfg.max_lock_attempts = 5;
+ ctx->cfg.max_msg_len = 60;
+ ctx->cfg.max_repodesc_len = 60;
+ ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
+ ctx->cfg.renamelimit = -1;
+ ctx->cfg.robots = "index, nofollow";
+ ctx->cfg.root_title = "Git repository browser";
+ ctx->cfg.script_name = CGIT_SCRIPT_NAME;
+ ctx->page.mimetype = "text/html";
+ ctx->page.charset = PAGE_ENCODING;
+ ctx->page.filename = NULL;
+}
static int cgit_prepare_cache(struct cacheitem *item)
{
- if (!cgit_repo && cgit_query_repo) {
- char *title = fmt("%s - %s", cgit_root_title, "Bad request");
- cgit_print_docstart(title, item);
- cgit_print_pageheader(title, 0);
- cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo));
+ if (!ctx.repo && ctx.qry.repo) {
+ ctx.page.title = fmt("%s - %s", ctx.cfg.root_title,
+ "Bad request");
+ cgit_print_http_headers(&ctx);
+ cgit_print_docstart(&ctx);
+ cgit_print_pageheader(&ctx);
+ cgit_print_error(fmt("Unknown repo: %s", ctx.qry.repo));
cgit_print_docend();
return 0;
}
- if (!cgit_repo) {
- item->name = xstrdup(fmt("%s/index.html", cgit_cache_root));
- item->ttl = cgit_cache_root_ttl;
+ if (!ctx.repo) {
+ item->name = xstrdup(fmt("%s/index.html", ctx.cfg.cache_root));
+ item->ttl = ctx.cfg.cache_root_ttl;
return 1;
}
- if (!cgit_cmd) {
- item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
- cache_safe_filename(cgit_repo->url),
- cache_safe_filename(cgit_querystring)));
- item->ttl = cgit_cache_repo_ttl;
+ if (!ctx.qry.page) {
+ item->name = xstrdup(fmt("%s/%s/index.%s.html", ctx.cfg.cache_root,
+ cache_safe_filename(ctx.repo->url),
+ cache_safe_filename(ctx.qry.raw)));
+ item->ttl = ctx.cfg.cache_repo_ttl;
} else {
- item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
- cache_safe_filename(cgit_repo->url),
- cgit_query_page,
- cache_safe_filename(cgit_querystring)));
- if (cgit_query_has_symref)
- item->ttl = cgit_cache_dynamic_ttl;
- else if (cgit_query_has_sha1)
- item->ttl = cgit_cache_static_ttl;
+ item->name = xstrdup(fmt("%s/%s/%s/%s.html", ctx.cfg.cache_root,
+ cache_safe_filename(ctx.repo->url),
+ ctx.qry.page,
+ cache_safe_filename(ctx.qry.raw)));
+ if (ctx.qry.has_symref)
+ item->ttl = ctx.cfg.cache_dynamic_ttl;
+ else if (ctx.qry.has_sha1)
+ item->ttl = ctx.cfg.cache_static_ttl;
else
- item->ttl = cgit_cache_repo_ttl;
+ item->ttl = ctx.cfg.cache_repo_ttl;
}
return 1;
}
struct refmatch {
char *req_ref;
char *first_ref;
int match;
};
int find_current_ref(const char *refname, const unsigned char *sha1,
int flags, void *cb_data)
{
struct refmatch *info;
info = (struct refmatch *)cb_data;
if (!strcmp(refname, info->req_ref))
info->match = 1;
if (!info->first_ref)
info->first_ref = xstrdup(refname);
return info->match;
}
-char *find_default_branch(struct repoinfo *repo)
+char *find_default_branch(struct cgit_repo *repo)
{
struct refmatch info;
info.req_ref = repo->defbranch;
info.first_ref = NULL;
info.match = 0;
for_each_branch_ref(find_current_ref, &info);
if (info.match)
return info.req_ref;
else
return info.first_ref;
}
-static void cgit_print_repo_page(struct cacheitem *item)
+static int prepare_repo_cmd(struct cgit_context *ctx)
{
- char *title, *tmp;
- int show_search;
+ char *tmp;
unsigned char sha1[20];
-
- if (chdir(cgit_repo->path)) {
- title = fmt("%s - %s", cgit_root_title, "Bad request");
- cgit_print_docstart(title, item);
- cgit_print_pageheader(title, 0);
- cgit_print_error(fmt("Unable to scan repository: %s",
- strerror(errno)));
+ int nongit = 0;
+
+ setenv("GIT_DIR", ctx->repo->path, 1);
+ setup_git_directory_gently(&nongit);
+ if (nongit) {
+ ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
+ "config error");
+ tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
+ ctx->repo = NULL;
+ cgit_print_http_headers(ctx);
+ cgit_print_docstart(ctx);
+ cgit_print_pageheader(ctx);
+ cgit_print_error(tmp);
cgit_print_docend();
- return;
+ return 1;
}
+ ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
- title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
- show_search = 0;
- setenv("GIT_DIR", cgit_repo->path, 1);
-
- if (!cgit_query_head) {
- cgit_query_head = xstrdup(find_default_branch(cgit_repo));
- cgit_repo->defbranch = cgit_query_head;
+ if (!ctx->qry.head) {
+ ctx->qry.head = xstrdup(find_default_branch(ctx->repo));
+ ctx->repo->defbranch = ctx->qry.head;
}
- if (!cgit_query_head) {
- cgit_print_docstart(title, item);
- cgit_print_pageheader(title, 0);
+ if (!ctx->qry.head) {
+ cgit_print_http_headers(ctx);
+ cgit_print_docstart(ctx);
+ cgit_print_pageheader(ctx);
cgit_print_error("Repository seems to be empty");
cgit_print_docend();
- return;
+ return 1;
}
- if (get_sha1(cgit_query_head, sha1)) {
- tmp = xstrdup(cgit_query_head);
- cgit_query_head = cgit_repo->defbranch;
- cgit_print_docstart(title, item);
- cgit_print_pageheader(title, 0);
+ if (get_sha1(ctx->qry.head, sha1)) {
+ tmp = xstrdup(ctx->qry.head);
+ ctx->qry.head = ctx->repo->defbranch;
+ cgit_print_http_headers(ctx);
+ cgit_print_docstart(ctx);
+ cgit_print_pageheader(ctx);
cgit_print_error(fmt("Invalid branch: %s", tmp));
cgit_print_docend();
- return;
+ return 1;
}
+ return 0;
+}
- if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
- cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1,
- cgit_repobasename(cgit_repo->url),
- cgit_query_path,
- cgit_repo->snapshots );
+static void process_request(struct cgit_context *ctx)
+{
+ struct cgit_cmd *cmd;
+
+ cmd = cgit_get_cmd(ctx);
+ if (!cmd) {
+ ctx->page.title = "cgit error";
+ ctx->repo = NULL;
+ cgit_print_http_headers(ctx);
+ cgit_print_docstart(ctx);
+ cgit_print_pageheader(ctx);
+ cgit_print_error("Invalid request");
+ cgit_print_docend();
return;
}
- if (cgit_cmd == CMD_PATCH) {
- cgit_print_patch(cgit_query_sha1, item);
+ if (cmd->want_repo && prepare_repo_cmd(ctx))
return;
- }
- if (cgit_cmd == CMD_BLOB) {
- cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
- return;
+ if (cmd->want_layout) {
+ cgit_print_http_headers(ctx);
+ cgit_print_docstart(ctx);
+ cgit_print_pageheader(ctx);
}
- show_search = (cgit_cmd == CMD_LOG);
- cgit_print_docstart(title, item);
- if (!cgit_cmd) {
- cgit_print_pageheader("summary", show_search);
- cgit_print_summary();
+ cmd->fn(ctx);
+
+ if (cmd->want_layout)
cgit_print_docend();
- return;
- }
+}
- cgit_print_pageheader(cgit_query_page, show_search);
-
- switch(cgit_cmd) {
- case CMD_LOG:
- cgit_print_log(cgit_query_sha1, cgit_query_ofs,
- cgit_max_commit_count, cgit_query_grep, cgit_query_search,
- cgit_query_path, 1);
- break;
- case CMD_TREE:
- cgit_print_tree(cgit_query_sha1, cgit_query_path);
- break;
- case CMD_COMMIT:
- cgit_print_commit(cgit_query_sha1);
- break;
- case CMD_REFS:
- cgit_print_refs();
- break;
- case CMD_TAG:
- cgit_print_tag(cgit_query_sha1);
- break;
- case CMD_DIFF:
- cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path);
- break;
- default:
- cgit_print_error("Invalid request");
- }
- cgit_print_docend();
+static long ttl_seconds(long ttl)
+{
+ if (ttl<0)
+ return 60 * 60 * 24 * 365;
+ else
+ return ttl * 60;
}
static void cgit_fill_cache(struct cacheitem *item, int use_cache)
{
- static char buf[PATH_MAX];
int stdout2;
- getcwd(buf, sizeof(buf));
- item->st.st_mtime = time(NULL);
-
if (use_cache) {
stdout2 = chk_positive(dup(STDOUT_FILENO),
"Preserving STDOUT");
chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
}
- if (cgit_repo)
- cgit_print_repo_page(item);
- else
- cgit_print_repolist(item);
+ ctx.page.modified = time(NULL);
+ ctx.page.expires = ctx.page.modified + ttl_seconds(item->ttl);
+ process_request(&ctx);
if (use_cache) {
chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
chk_positive(dup2(stdout2, STDOUT_FILENO),
"Restoring original STDOUT");
chk_zero(close(stdout2), "Closing temporary STDOUT");
}
-
- chdir(buf);
}
static void cgit_check_cache(struct cacheitem *item)
{
int i = 0;
top:
- if (++i > cgit_max_lock_attempts) {
+ if (++i > ctx.cfg.max_lock_attempts) {
die("cgit_refresh_cache: unable to lock %s: %s",
item->name, strerror(errno));
}
if (!cache_exist(item)) {
if (!cache_lock(item)) {
sleep(1);
goto top;
}
if (!cache_exist(item)) {
cgit_fill_cache(item, 1);
cache_unlock(item);
} else {
cache_cancel_lock(item);
}
} else if (cache_expired(item) && cache_lock(item)) {
if (cache_expired(item)) {
cgit_fill_cache(item, 1);
cache_unlock(item);
} else {
cache_cancel_lock(item);
}
}
}
static void cgit_print_cache(struct cacheitem *item)
{
static char buf[4096];
ssize_t i;
int fd = open(item->name, O_RDONLY);
if (fd<0)
die("Unable to open cached file %s", item->name);
while((i=read(fd, buf, sizeof(buf))) > 0)
write(STDOUT_FILENO, buf, i);
close(fd);
}
static void cgit_parse_args(int argc, const char **argv)
{
int i;
for (i = 1; i < argc; i++) {
if (!strncmp(argv[i], "--cache=", 8)) {
- cgit_cache_root = xstrdup(argv[i]+8);
+ ctx.cfg.cache_root = xstrdup(argv[i]+8);
}
if (!strcmp(argv[i], "--nocache")) {
- cgit_nocache = 1;
+ ctx.cfg.nocache = 1;
}
if (!strncmp(argv[i], "--query=", 8)) {
- cgit_querystring = xstrdup(argv[i]+8);
+ ctx.qry.raw = xstrdup(argv[i]+8);
}
if (!strncmp(argv[i], "--repo=", 7)) {
- cgit_query_repo = xstrdup(argv[i]+7);
+ ctx.qry.repo = xstrdup(argv[i]+7);
}
if (!strncmp(argv[i], "--page=", 7)) {
- cgit_query_page = xstrdup(argv[i]+7);
+ ctx.qry.page = xstrdup(argv[i]+7);
}
if (!strncmp(argv[i], "--head=", 7)) {
- cgit_query_head = xstrdup(argv[i]+7);
- cgit_query_has_symref = 1;
+ ctx.qry.head = xstrdup(argv[i]+7);
+ ctx.qry.has_symref = 1;
}
if (!strncmp(argv[i], "--sha1=", 7)) {
- cgit_query_sha1 = xstrdup(argv[i]+7);
- cgit_query_has_sha1 = 1;
+ ctx.qry.sha1 = xstrdup(argv[i]+7);
+ ctx.qry.has_sha1 = 1;
}
if (!strncmp(argv[i], "--ofs=", 6)) {
- cgit_query_ofs = atoi(argv[i]+6);
+ ctx.qry.ofs = atoi(argv[i]+6);
}
}
}
int main(int argc, const char **argv)
{
struct cacheitem item;
const char *cgit_config_env = getenv("CGIT_CONFIG");
- htmlfd = STDOUT_FILENO;
+ prepare_context(&ctx);
item.st.st_mtime = time(NULL);
cgit_repolist.length = 0;
cgit_repolist.count = 0;
cgit_repolist.repos = NULL;
- cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
- cgit_global_config_cb);
- cgit_repo = NULL;
+ parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
+ config_cb);
+ ctx.repo = NULL;
if (getenv("SCRIPT_NAME"))
- cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
+ ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
if (getenv("QUERY_STRING"))
- cgit_querystring = xstrdup(getenv("QUERY_STRING"));
+ ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
cgit_parse_args(argc, argv);
- cgit_parse_query(cgit_querystring, cgit_querystring_cb);
+ http_parse_querystring(ctx.qry.raw, querystring_cb);
if (!cgit_prepare_cache(&item))
return 0;
- if (cgit_nocache) {
+ if (ctx.cfg.nocache) {
cgit_fill_cache(&item, 0);
} else {
cgit_check_cache(&item);
cgit_print_cache(&item);
}
return 0;
}
diff --git a/cgit.h b/cgit.h
index 66c40b9..ee8c716 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,295 +1,224 @@
#ifndef CGIT_H
#define CGIT_H
#include <git-compat-util.h>
#include <cache.h>
#include <grep.h>
#include <object.h>
#include <tree.h>
#include <commit.h>
#include <tag.h>
#include <diff.h>
#include <diffcore.h>
#include <refs.h>
#include <revision.h>
#include <log-tree.h>
#include <archive.h>
#include <xdiff/xdiff.h>
#include <utf8.h>
/*
- * The valid cgit repo-commands
- */
-#define CMD_LOG 1
-#define CMD_COMMIT 2
-#define CMD_DIFF 3
-#define CMD_TREE 4
-#define CMD_BLOB 5
-#define CMD_SNAPSHOT 6
-#define CMD_TAG 7
-#define CMD_REFS 8
-#define CMD_PATCH 9
-
-/*
* Dateformats used on misc. pages
*/
#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
#define FMT_SHORTDATE "%Y-%m-%d"
/*
* Limits used for relative dates
*/
#define TM_MIN 60
#define TM_HOUR (TM_MIN * 60)
#define TM_DAY (TM_HOUR * 24)
#define TM_WEEK (TM_DAY * 7)
#define TM_YEAR (TM_DAY * 365)
#define TM_MONTH (TM_YEAR / 12.0)
/*
* Default encoding
*/
#define PAGE_ENCODING "UTF-8"
typedef void (*configfn)(const char *name, const char *value);
typedef void (*filepair_fn)(struct diff_filepair *pair);
typedef void (*linediff_fn)(char *line, int len);
-struct cacheitem {
- char *name;
- struct stat st;
- int ttl;
- int fd;
-};
-
-struct repoinfo {
+struct cgit_repo {
char *url;
char *name;
char *path;
char *desc;
char *owner;
char *defbranch;
char *group;
char *module_link;
char *readme;
char *clone_url;
int snapshots;
int enable_log_filecount;
int enable_log_linecount;
};
-struct repolist {
+struct cgit_repolist {
int length;
int count;
- struct repoinfo *repos;
+ struct cgit_repo *repos;
};
struct commitinfo {
struct commit *commit;
char *author;
char *author_email;
unsigned long author_date;
char *committer;
char *committer_email;
unsigned long committer_date;
char *subject;
char *msg;
char *msg_encoding;
};
struct taginfo {
char *tagger;
char *tagger_email;
int tagger_date;
char *msg;
};
struct refinfo {
const char *refname;
struct object *object;
union {
struct taginfo *tag;
struct commitinfo *commit;
};
};
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;
+ int ofs;
+};
+
+struct cgit_config {
+ char *agefile;
+ char *cache_root;
+ char *clone_prefix;
+ char *css;
+ char *index_header;
+ char *index_info;
+ char *logo;
+ char *logo_link;
+ char *module_link;
+ char *repo_group;
+ char *robots;
+ char *root_title;
+ char *script_name;
+ char *virtual_root;
+ int cache_dynamic_ttl;
+ int cache_max_create_time;
+ int cache_repo_ttl;
+ int cache_root_ttl;
+ int cache_static_ttl;
+ int enable_index_links;
+ int enable_log_filecount;
+ int enable_log_linecount;
+ int max_commit_count;
+ int max_lock_attempts;
+ int max_msg_len;
+ int max_repodesc_len;
+ int nocache;
+ int renamelimit;
+ int snapshots;
+ int summary_branches;
+ int summary_log;
+ int summary_tags;
+};
+
+struct cgit_page {
+ time_t modified;
+ time_t expires;
+ char *mimetype;
+ char *charset;
+ char *filename;
+ char *title;
+};
+
+struct cgit_context {
+ struct cgit_query qry;
+ struct cgit_config cfg;
+ struct cgit_repo *repo;
+ struct cgit_page page;
+};
+
+struct cgit_snapshot_format {
+ const char *suffix;
+ const char *mimetype;
+ write_archive_fn_t write_func;
+ int bit;
+};
+
extern const char *cgit_version;
-extern struct repolist cgit_repolist;
-extern struct repoinfo *cgit_repo;
-extern int cgit_cmd;
-
-extern char *cgit_root_title;
-extern char *cgit_css;
-extern char *cgit_logo;
-extern char *cgit_index_header;
-extern char *cgit_index_info;
-extern char *cgit_logo_link;
-extern char *cgit_module_link;
-extern char *cgit_agefile;
-extern char *cgit_virtual_root;
-extern char *cgit_script_name;
-extern char *cgit_cache_root;
-extern char *cgit_repo_group;
-extern char *cgit_robots;
-extern char *cgit_clone_prefix;
-
-extern int cgit_nocache;
-extern int cgit_snapshots;
-extern int cgit_enable_index_links;
-extern int cgit_enable_log_filecount;
-extern int cgit_enable_log_linecount;
-extern int cgit_max_lock_attempts;
-extern int cgit_cache_root_ttl;
-extern int cgit_cache_repo_ttl;
-extern int cgit_cache_dynamic_ttl;
-extern int cgit_cache_static_ttl;
-extern int cgit_cache_max_create_time;
-extern int cgit_summary_log;
-extern int cgit_summary_tags;
-extern int cgit_summary_branches;
-
-extern int cgit_max_msg_len;
-extern int cgit_max_repodesc_len;
-extern int cgit_max_commit_count;
-
-extern int cgit_query_has_symref;
-extern int cgit_query_has_sha1;
-
-extern char *cgit_querystring;
-extern char *cgit_query_repo;
-extern char *cgit_query_page;
-extern char *cgit_query_search;
-extern char *cgit_query_grep;
-extern char *cgit_query_head;
-extern char *cgit_query_sha1;
-extern char *cgit_query_sha2;
-extern char *cgit_query_path;
-extern char *cgit_query_name;
-extern int cgit_query_ofs;
-
-extern int htmlfd;
-
-extern int cgit_get_cmd_index(const char *cmd);
-extern struct repoinfo *cgit_get_repoinfo(const char *url);
-extern void cgit_global_config_cb(const char *name, const char *value);
+extern struct cgit_repolist cgit_repolist;
+extern struct cgit_context ctx;
+extern const struct cgit_snapshot_format cgit_snapshot_formats[];
+
+extern struct cgit_repo *cgit_add_repo(const char *url);
+extern struct cgit_repo *cgit_get_repoinfo(const char *url);
extern void cgit_repo_config_cb(const char *name, const char *value);
-extern void cgit_querystring_cb(const char *name, const char *value);
extern int chk_zero(int result, char *msg);
extern int chk_positive(int result, char *msg);
extern int chk_non_negative(int result, char *msg);
-extern int hextoint(char c);
extern char *trim_end(const char *str, char c);
extern char *strlpart(char *txt, int maxlen);
extern char *strrpart(char *txt, int maxlen);
extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
int flags, void *cb_data);
extern void *cgit_free_commitinfo(struct commitinfo *info);
extern int cgit_diff_files(const unsigned char *old_sha1,
const unsigned char *new_sha1,
linediff_fn fn);
extern void cgit_diff_tree(const unsigned char *old_sha1,
const unsigned char *new_sha1,
filepair_fn fn, const char *prefix);
extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
extern char *fmt(const char *format,...);
-extern void html(const char *txt);
-extern void htmlf(const char *format,...);
-extern void html_txt(char *txt);
-extern void html_ntxt(int len, char *txt);
-extern void html_attr(char *txt);
-extern void html_hidden(char *name, char *value);
-extern void html_option(char *value, char *text, char *selected_value);
-extern void html_link_open(char *url, char *title, char *class);
-extern void html_link_close(void);
-extern void html_filemode(unsigned short mode);
-extern int html_include(const char *filename);
-
-extern int cgit_read_config(const char *filename, configfn fn);
-extern int cgit_parse_query(char *txt, configfn fn);
extern struct commitinfo *cgit_parse_commit(struct commit *commit);
extern struct taginfo *cgit_parse_tag(struct tag *tag);
extern void cgit_parse_url(const char *url);
-extern char *cache_safe_filename(const char *unsafe);
-extern int cache_lock(struct cacheitem *item);
-extern int cache_unlock(struct cacheitem *item);
-extern int cache_cancel_lock(struct cacheitem *item);
-extern int cache_exist(struct cacheitem *item);
-extern int cache_expired(struct cacheitem *item);
-
-extern char *cgit_repourl(const char *reponame);
-extern char *cgit_fileurl(const char *reponame, const char *pagename,
- const char *filename, const char *query);
-extern char *cgit_pageurl(const char *reponame, const char *pagename,
- const char *query);
-
extern const char *cgit_repobasename(const char *reponame);
-extern void cgit_tree_link(char *name, char *title, char *class, char *head,
- char *rev, char *path);
-extern void cgit_log_link(char *name, char *title, char *class, char *head,
- char *rev, char *path, int ofs, char *grep,
- char *pattern);
-extern void cgit_commit_link(char *name, char *title, char *class, char *head,
- char *rev);
-extern void cgit_refs_link(char *name, char *title, char *class, char *head,
- char *rev, char *path);
-extern void cgit_snapshot_link(char *name, char *title, char *class,
- char *head, char *rev, char *archivename);
-extern void cgit_diff_link(char *name, char *title, char *class, char *head,
- char *new_rev, char *old_rev, char *path);
-
-extern void cgit_object_link(struct object *obj);
-
-extern void cgit_print_error(char *msg);
-extern void cgit_print_date(time_t secs, char *format);
-extern void cgit_print_age(time_t t, time_t max_relative, char *format);
-extern void cgit_print_docstart(char *title, struct cacheitem *item);
-extern void cgit_print_docend();
-extern void cgit_print_pageheader(char *title, int show_search);
-extern void cgit_print_snapshot_start(const char *mimetype,
- const char *filename,
- struct cacheitem *item);
-extern void cgit_print_branches(int maxcount);
-extern void cgit_print_tags(int maxcount);
-
-extern void cgit_print_repolist(struct cacheitem *item);
-extern void cgit_print_summary();
-extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
- char *pattern, char *path, int pager);
-extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
-extern void cgit_print_tree(const char *rev, char *path);
-extern void cgit_print_commit(char *hex);
-extern void cgit_print_refs();
-extern void cgit_print_tag(char *revname);
-extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix);
-extern void cgit_print_patch(char *hex, struct cacheitem *item);
-extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
- const char *hex, const char *prefix,
- const char *filename, int snapshot);
-extern void cgit_print_snapshot_links(const char *repo, const char *head,
- const char *hex, int snapshots);
extern int cgit_parse_snapshots_mask(const char *str);
#endif /* CGIT_H */
diff --git a/cmd.c b/cmd.c
new file mode 100644
index 0000000..e0eacbe
--- a/dev/null
+++ b/cmd.c
@@ -0,0 +1,112 @@
+/* cmd.c: the cgit command dispatcher
+ *
+ * Copyright (C) 2008 Lars Hjemli
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "cmd.h"
+#include "ui-blob.h"
+#include "ui-commit.h"
+#include "ui-diff.h"
+#include "ui-log.h"
+#include "ui-patch.h"
+#include "ui-refs.h"
+#include "ui-repolist.h"
+#include "ui-snapshot.h"
+#include "ui-summary.h"
+#include "ui-tag.h"
+#include "ui-tree.h"
+
+static void blob_fn(struct cgit_context *ctx)
+{
+ cgit_print_blob(ctx->qry.sha1, ctx->qry.path);
+}
+
+static void commit_fn(struct cgit_context *ctx)
+{
+ cgit_print_commit(ctx->qry.sha1);
+}
+
+static void diff_fn(struct cgit_context *ctx)
+{
+ cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
+}
+
+static void repolist_fn(struct cgit_context *ctx)
+{
+ cgit_print_repolist();
+}
+
+static void log_fn(struct cgit_context *ctx)
+{
+ cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
+ ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
+}
+
+static void patch_fn(struct cgit_context *ctx)
+{
+ cgit_print_patch(ctx->qry.sha1);
+}
+
+static void refs_fn(struct cgit_context *ctx)
+{
+ cgit_print_refs();
+}
+
+static void snapshot_fn(struct cgit_context *ctx)
+{
+ cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1,
+ cgit_repobasename(ctx->repo->url), ctx->qry.path,
+ ctx->repo->snapshots);
+}
+
+static void summary_fn(struct cgit_context *ctx)
+{
+ cgit_print_summary();
+}
+
+static void tag_fn(struct cgit_context *ctx)
+{
+ cgit_print_tag(ctx->qry.sha1);
+}
+
+static void tree_fn(struct cgit_context *ctx)
+{
+ cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
+}
+
+#define def_cmd(name, want_repo, want_layout) \
+ {#name, name##_fn, want_repo, want_layout}
+
+struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
+{
+ static struct cgit_cmd cmds[] = {
+ def_cmd(blob, 1, 0),
+ def_cmd(commit, 1, 1),
+ def_cmd(diff, 1, 1),
+ def_cmd(log, 1, 1),
+ def_cmd(patch, 1, 0),
+ def_cmd(refs, 1, 1),
+ def_cmd(repolist, 0, 0),
+ def_cmd(snapshot, 1, 0),
+ def_cmd(summary, 1, 1),
+ def_cmd(tag, 1, 1),
+ def_cmd(tree, 1, 1),
+ };
+ int i;
+
+ if (ctx->qry.page == NULL) {
+ if (ctx->repo)
+ ctx->qry.page = "summary";
+ else
+ ctx->qry.page = "repolist";
+ }
+
+ for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
+ if (!strcmp(ctx->qry.page, cmds[i].name))
+ return &cmds[i];
+ return NULL;
+}
diff --git a/cmd.h b/cmd.h
new file mode 100644
index 0000000..ec9e691
--- a/dev/null
+++ b/cmd.h
@@ -0,0 +1,15 @@
+#ifndef CMD_H
+#define CMD_H
+
+typedef void (*cgit_cmd_fn)(struct cgit_context *ctx);
+
+struct cgit_cmd {
+ const char *name;
+ cgit_cmd_fn fn;
+ unsigned int want_repo:1,
+ want_layout:1;
+};
+
+extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx);
+
+#endif /* CMD_H */
diff --git a/configfile.c b/configfile.c
new file mode 100644
index 0000000..4908058
--- a/dev/null
+++ b/configfile.c
@@ -0,0 +1,87 @@
+/* configfile.c: parsing of config files
+ *
+ * Copyright (C) 2008 Lars Hjemli
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include "configfile.h"
+
+int next_char(FILE *f)
+{
+ int c = fgetc(f);
+ if (c=='\r') {
+ c = fgetc(f);
+ if (c!='\n') {
+ ungetc(c, f);
+ c = '\r';
+ }
+ }
+ return c;
+}
+
+void skip_line(FILE *f)
+{
+ int c;
+
+ while((c=next_char(f)) && c!='\n' && c!=EOF)
+ ;
+}
+
+int read_config_line(FILE *f, char *line, const char **value, int bufsize)
+{
+ int i = 0, isname = 0;
+
+ *value = NULL;
+ while(i<bufsize-1) {
+ int c = next_char(f);
+ if (!isname && (c=='#' || c==';')) {
+ skip_line(f);
+ continue;
+ }
+ if (!isname && isspace(c))
+ continue;
+
+ if (c=='=' && !*value) {
+ line[i] = 0;
+ *value = &line[i+1];
+ } else if (c=='\n' && !isname) {
+ i = 0;
+ continue;
+ } else if (c=='\n' || c==EOF) {
+ line[i] = 0;
+ break;
+ } else {
+ line[i]=c;
+ }
+ isname = 1;
+ i++;
+ }
+ line[i+1] = 0;
+ return i;
+}
+
+int parse_configfile(const char *filename, configfile_value_fn fn)
+{
+ static int nesting;
+ int len;
+ char line[256];
+ const char *value;
+ FILE *f;
+
+ /* cancel deeply nested include-commands */
+ if (nesting > 8)
+ return -1;
+ if (!(f = fopen(filename, "r")))
+ return -1;
+ nesting++;
+ while((len = read_config_line(f, line, &value, sizeof(line))) > 0)
+ fn(line, value);
+ nesting--;
+ fclose(f);
+ return 0;
+}
+
diff --git a/configfile.h b/configfile.h
new file mode 100644
index 0000000..04235e5
--- a/dev/null
+++ b/configfile.h
@@ -0,0 +1,8 @@
+#ifndef CONFIGFILE_H
+#define CONFIGFILE_H
+
+typedef void (*configfile_value_fn)(const char *name, const char *value);
+
+extern int parse_configfile(const char *filename, configfile_value_fn fn);
+
+#endif /* CONFIGFILE_H */
diff --git a/html.c b/html.c
index 339bf00..937b5e7 100644
--- a/html.c
+++ b/html.c
@@ -1,57 +1,65 @@
/* html.c: helper functions for html output
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
-#include "cgit.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+int htmlfd = STDOUT_FILENO;
char *fmt(const char *format, ...)
{
static char buf[8][1024];
static int bufidx;
int len;
va_list args;
bufidx++;
bufidx &= 7;
va_start(args, format);
len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
va_end(args);
- if (len>sizeof(buf[bufidx]))
- die("[html.c] string truncated: %s", format);
+ if (len>sizeof(buf[bufidx])) {
+ fprintf(stderr, "[html.c] string truncated: %s\n", format);
+ exit(1);
+ }
return buf[bufidx];
}
void html(const char *txt)
{
write(htmlfd, txt, strlen(txt));
}
void htmlf(const char *format, ...)
{
static char buf[65536];
va_list args;
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
html(buf);
}
void html_txt(char *txt)
{
char *t = txt;
while(t && *t){
int c = *t;
if (c=='<' || c=='>' || c=='&') {
write(htmlfd, txt, t - txt);
if (c=='>')
html("&gt;");
else if (c=='<')
html("&lt;");
else if (c=='&')
html("&amp;");
@@ -121,64 +129,113 @@ void html_option(char *value, char *text, char *selected_value)
html("<option value='");
html_attr(value);
html("'");
if (selected_value && !strcmp(selected_value, value))
html(" selected='selected'");
html(">");
html_txt(text);
html("</option>\n");
}
void html_link_open(char *url, char *title, char *class)
{
html("<a href='");
html_attr(url);
if (title) {
html("' title='");
html_attr(title);
}
if (class) {
html("' class='");
html_attr(class);
}
html("'>");
}
void html_link_close(void)
{
html("</a>");
}
void html_fileperm(unsigned short mode)
{
- htmlf("%c%c%c", (mode & 4 ? 'r' : '-'),
+ htmlf("%c%c%c", (mode & 4 ? 'r' : '-'),
(mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-'));
}
-void html_filemode(unsigned short mode)
-{
- if (S_ISDIR(mode))
- html("d");
- else if (S_ISLNK(mode))
- html("l");
- else if (S_ISGITLINK(mode))
- html("m");
- else
- html("-");
- html_fileperm(mode >> 6);
- html_fileperm(mode >> 3);
- html_fileperm(mode);
-}
-
int html_include(const char *filename)
{
FILE *f;
char buf[4096];
size_t len;
if (!(f = fopen(filename, "r")))
return -1;
while((len = fread(buf, 1, 4096, f)) > 0)
write(htmlfd, buf, len);
fclose(f);
return 0;
}
+
+int hextoint(char c)
+{
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+ else if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+ else if (c >= '0' && c <= '9')
+ return c - '0';
+ else
+ return -1;
+}
+
+char *convert_query_hexchar(char *txt)
+{
+ int d1, d2;
+ if (strlen(txt) < 3) {
+ *txt = '\0';
+ return txt-1;
+ }
+ d1 = hextoint(*(txt+1));
+ d2 = hextoint(*(txt+2));
+ if (d1<0 || d2<0) {
+ strcpy(txt, txt+3);
+ return txt-1;
+ } else {
+ *txt = d1 * 16 + d2;
+ strcpy(txt+1, txt+3);
+ return txt;
+ }
+}
+
+int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value))
+{
+ char *t, *value = NULL, c;
+
+ if (!txt)
+ return 0;
+
+ t = txt = strdup(txt);
+ if (t == NULL) {
+ printf("Out of memory\n");
+ exit(1);
+ }
+ while((c=*t) != '\0') {
+ if (c=='=') {
+ *t = '\0';
+ value = t+1;
+ } else if (c=='+') {
+ *t = ' ';
+ } else if (c=='%') {
+ t = convert_query_hexchar(t);
+ } else if (c=='&') {
+ *t = '\0';
+ (*fn)(txt, value);
+ txt = t+1;
+ value = NULL;
+ }
+ t++;
+ }
+ if (t!=txt)
+ (*fn)(txt, value);
+ return 0;
+}
diff --git a/html.h b/html.h
new file mode 100644
index 0000000..e6fdc54
--- a/dev/null
+++ b/html.h
@@ -0,0 +1,20 @@
+#ifndef HTML_H
+#define HTML_H
+
+extern int htmlfd;
+
+extern void html(const char *txt);
+extern void htmlf(const char *format,...);
+extern void html_txt(char *txt);
+extern void html_ntxt(int len, char *txt);
+extern void html_attr(char *txt);
+extern void html_hidden(char *name, char *value);
+extern void html_option(char *value, char *text, char *selected_value);
+extern void html_link_open(char *url, char *title, char *class);
+extern void html_link_close(void);
+extern void html_fileperm(unsigned short mode);
+extern int html_include(const char *filename);
+
+extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value));
+
+#endif /* HTML_H */
diff --git a/parsing.c b/parsing.c
index 5093b8b..66e8b3d 100644
--- a/parsing.c
+++ b/parsing.c
@@ -1,206 +1,82 @@
/* config.c: parsing of config files
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
-int next_char(FILE *f)
-{
- int c = fgetc(f);
- if (c=='\r') {
- c = fgetc(f);
- if (c!='\n') {
- ungetc(c, f);
- c = '\r';
- }
- }
- return c;
-}
-
-void skip_line(FILE *f)
-{
- int c;
-
- while((c=next_char(f)) && c!='\n' && c!=EOF)
- ;
-}
-
-int read_config_line(FILE *f, char *line, const char **value, int bufsize)
-{
- int i = 0, isname = 0;
-
- *value = NULL;
- while(i<bufsize-1) {
- int c = next_char(f);
- if (!isname && (c=='#' || c==';')) {
- skip_line(f);
- continue;
- }
- if (!isname && isspace(c))
- continue;
-
- if (c=='=' && !*value) {
- line[i] = 0;
- *value = &line[i+1];
- } else if (c=='\n' && !isname) {
- i = 0;
- continue;
- } else if (c=='\n' || c==EOF) {
- line[i] = 0;
- break;
- } else {
- line[i]=c;
- }
- isname = 1;
- i++;
- }
- line[i+1] = 0;
- return i;
-}
-
-int cgit_read_config(const char *filename, configfn fn)
-{
- static int nesting;
- int len;
- char line[256];
- const char *value;
- FILE *f;
-
- /* cancel deeply nested include-commands */
- if (nesting > 8)
- return -1;
- if (!(f = fopen(filename, "r")))
- return -1;
- nesting++;
- while((len = read_config_line(f, line, &value, sizeof(line))) > 0)
- (*fn)(line, value);
- nesting--;
- fclose(f);
- return 0;
-}
-
-char *convert_query_hexchar(char *txt)
-{
- int d1, d2;
- if (strlen(txt) < 3) {
- *txt = '\0';
- return txt-1;
- }
- d1 = hextoint(*(txt+1));
- d2 = hextoint(*(txt+2));
- if (d1<0 || d2<0) {
- strcpy(txt, txt+3);
- return txt-1;
- } else {
- *txt = d1 * 16 + d2;
- strcpy(txt+1, txt+3);
- return txt;
- }
-}
-
-int cgit_parse_query(char *txt, configfn fn)
-{
- char *t, *value = NULL, c;
-
- if (!txt)
- return 0;
-
- t = txt = xstrdup(txt);
-
- while((c=*t) != '\0') {
- if (c=='=') {
- *t = '\0';
- value = t+1;
- } else if (c=='+') {
- *t = ' ';
- } else if (c=='%') {
- t = convert_query_hexchar(t);
- } else if (c=='&') {
- *t = '\0';
- (*fn)(txt, value);
- txt = t+1;
- value = NULL;
- }
- t++;
- }
- if (t!=txt)
- (*fn)(txt, value);
- return 0;
-}
-
/*
* url syntax: [repo ['/' cmd [ '/' path]]]
* repo: any valid repo url, may contain '/'
* cmd: log | commit | diff | tree | view | blob | snapshot
* path: any valid path, may contain '/'
*
*/
void cgit_parse_url(const char *url)
{
char *cmd, *p;
- cgit_repo = NULL;
+ ctx.repo = NULL;
if (!url || url[0] == '\0')
return;
- cgit_repo = cgit_get_repoinfo(url);
- if (cgit_repo) {
- cgit_query_repo = cgit_repo->url;
+ ctx.repo = cgit_get_repoinfo(url);
+ if (ctx.repo) {
+ ctx.qry.repo = ctx.repo->url;
return;
}
cmd = strchr(url, '/');
- while (!cgit_repo && cmd) {
+ while (!ctx.repo && cmd) {
cmd[0] = '\0';
- cgit_repo = cgit_get_repoinfo(url);
- if (cgit_repo == NULL) {
+ ctx.repo = cgit_get_repoinfo(url);
+ if (ctx.repo == NULL) {
cmd[0] = '/';
cmd = strchr(cmd + 1, '/');
continue;
}
- cgit_query_repo = cgit_repo->url;
+ ctx.qry.repo = ctx.repo->url;
p = strchr(cmd + 1, '/');
if (p) {
p[0] = '\0';
if (p[1])
- cgit_query_path = trim_end(p + 1, '/');
+ ctx.qry.path = trim_end(p + 1, '/');
}
- cgit_cmd = cgit_get_cmd_index(cmd + 1);
- cgit_query_page = xstrdup(cmd + 1);
+ if (cmd[1])
+ ctx.qry.page = xstrdup(cmd + 1);
return;
}
}
char *substr(const char *head, const char *tail)
{
char *buf;
buf = xmalloc(tail - head + 1);
strncpy(buf, head, tail - head);
buf[tail - head] = '\0';
return buf;
}
struct commitinfo *cgit_parse_commit(struct commit *commit)
{
struct commitinfo *ret;
char *p = commit->buffer, *t = commit->buffer;
ret = xmalloc(sizeof(*ret));
ret->commit = commit;
ret->author = NULL;
ret->author_email = NULL;
ret->committer = NULL;
ret->committer_email = NULL;
ret->subject = NULL;
ret->msg = NULL;
ret->msg_encoding = NULL;
if (p == NULL)
return ret;
diff --git a/shared.c b/shared.c
index f063894..f5875e4 100644
--- a/shared.c
+++ b/shared.c
@@ -1,327 +1,123 @@
/* shared.c: global vars + some callback functions
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
-struct repolist cgit_repolist;
-struct repoinfo *cgit_repo;
+struct cgit_repolist cgit_repolist;
+struct cgit_context ctx;
int cgit_cmd;
-const char *cgit_version = CGIT_VERSION;
-
-char *cgit_root_title = "Git repository browser";
-char *cgit_css = "/cgit.css";
-char *cgit_logo = "/git-logo.png";
-char *cgit_index_header = NULL;
-char *cgit_index_info = NULL;
-char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
-char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
-char *cgit_agefile = "info/web/last-modified";
-char *cgit_virtual_root = NULL;
-char *cgit_script_name = CGIT_SCRIPT_NAME;
-char *cgit_cache_root = CGIT_CACHE_ROOT;
-char *cgit_repo_group = NULL;
-char *cgit_robots = "index, nofollow";
-char *cgit_clone_prefix = NULL;
-
-int cgit_nocache = 0;
-int cgit_snapshots = 0;
-int cgit_enable_index_links = 0;
-int cgit_enable_log_filecount = 0;
-int cgit_enable_log_linecount = 0;
-int cgit_max_lock_attempts = 5;
-int cgit_cache_root_ttl = 5;
-int cgit_cache_repo_ttl = 5;
-int cgit_cache_dynamic_ttl = 5;
-int cgit_cache_static_ttl = -1;
-int cgit_cache_max_create_time = 5;
-int cgit_summary_log = 0;
-int cgit_summary_tags = 0;
-int cgit_summary_branches = 0;
-int cgit_renamelimit = -1;
-
-int cgit_max_msg_len = 60;
-int cgit_max_repodesc_len = 60;
-int cgit_max_commit_count = 50;
-
-int cgit_query_has_symref = 0;
-int cgit_query_has_sha1 = 0;
-
-char *cgit_querystring = NULL;
-char *cgit_query_repo = NULL;
-char *cgit_query_page = NULL;
-char *cgit_query_head = NULL;
-char *cgit_query_search = NULL;
-char *cgit_query_grep = NULL;
-char *cgit_query_sha1 = NULL;
-char *cgit_query_sha2 = NULL;
-char *cgit_query_path = NULL;
-char *cgit_query_name = NULL;
-int cgit_query_ofs = 0;
-
-int htmlfd = 0;
-
-
-int cgit_get_cmd_index(const char *cmd)
-{
- static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
- "snapshot", "tag", "refs", "patch", NULL};
- int i;
-
- for(i = 0; cmds[i]; i++)
- if (!strcmp(cmd, cmds[i]))
- return i + 1;
- return 0;
-}
-
int chk_zero(int result, char *msg)
{
if (result != 0)
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 repoinfo *add_repo(const char *url)
+struct cgit_repo *cgit_add_repo(const char *url)
{
- struct repoinfo *ret;
+ 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 repoinfo));
+ sizeof(struct cgit_repo));
}
ret = &cgit_repolist.repos[cgit_repolist.count-1];
ret->url = trim_end(url, '/');
ret->name = ret->url;
ret->path = NULL;
ret->desc = "[no description]";
ret->owner = NULL;
- ret->group = cgit_repo_group;
+ ret->group = ctx.cfg.repo_group;
ret->defbranch = "master";
- ret->snapshots = cgit_snapshots;
- ret->enable_log_filecount = cgit_enable_log_filecount;
- ret->enable_log_linecount = cgit_enable_log_linecount;
- ret->module_link = cgit_module_link;
+ ret->snapshots = ctx.cfg.snapshots;
+ ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
+ ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
+ ret->module_link = ctx.cfg.module_link;
ret->readme = NULL;
return ret;
}
-struct repoinfo *cgit_get_repoinfo(const char *url)
+struct cgit_repo *cgit_get_repoinfo(const char *url)
{
int i;
- struct repoinfo *repo;
+ 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_global_config_cb(const char *name, const char *value)
-{
- if (!strcmp(name, "root-title"))
- cgit_root_title = xstrdup(value);
- else if (!strcmp(name, "css"))
- cgit_css = xstrdup(value);
- else if (!strcmp(name, "logo"))
- cgit_logo = xstrdup(value);
- else if (!strcmp(name, "index-header"))
- cgit_index_header = xstrdup(value);
- else if (!strcmp(name, "index-info"))
- cgit_index_info = xstrdup(value);
- else if (!strcmp(name, "logo-link"))
- cgit_logo_link = xstrdup(value);
- else if (!strcmp(name, "module-link"))
- cgit_module_link = xstrdup(value);
- else if (!strcmp(name, "virtual-root")) {
- cgit_virtual_root = trim_end(value, '/');
- if (!cgit_virtual_root && (!strcmp(value, "/")))
- cgit_virtual_root = "";
- } else if (!strcmp(name, "nocache"))
- cgit_nocache = atoi(value);
- else if (!strcmp(name, "snapshots"))
- cgit_snapshots = cgit_parse_snapshots_mask(value);
- else if (!strcmp(name, "enable-index-links"))
- cgit_enable_index_links = atoi(value);
- else if (!strcmp(name, "enable-log-filecount"))
- cgit_enable_log_filecount = atoi(value);
- else if (!strcmp(name, "enable-log-linecount"))
- cgit_enable_log_linecount = atoi(value);
- else if (!strcmp(name, "cache-root"))
- cgit_cache_root = xstrdup(value);
- else if (!strcmp(name, "cache-root-ttl"))
- cgit_cache_root_ttl = atoi(value);
- else if (!strcmp(name, "cache-repo-ttl"))
- cgit_cache_repo_ttl = atoi(value);
- else if (!strcmp(name, "cache-static-ttl"))
- cgit_cache_static_ttl = atoi(value);
- else if (!strcmp(name, "cache-dynamic-ttl"))
- cgit_cache_dynamic_ttl = atoi(value);
- else if (!strcmp(name, "max-message-length"))
- cgit_max_msg_len = atoi(value);
- else if (!strcmp(name, "max-repodesc-length"))
- cgit_max_repodesc_len = atoi(value);
- else if (!strcmp(name, "max-commit-count"))
- cgit_max_commit_count = atoi(value);
- else if (!strcmp(name, "summary-log"))
- cgit_summary_log = atoi(value);
- else if (!strcmp(name, "summary-branches"))
- cgit_summary_branches = atoi(value);
- else if (!strcmp(name, "summary-tags"))
- cgit_summary_tags = atoi(value);
- else if (!strcmp(name, "agefile"))
- cgit_agefile = xstrdup(value);
- else if (!strcmp(name, "renamelimit"))
- cgit_renamelimit = atoi(value);
- else if (!strcmp(name, "robots"))
- cgit_robots = xstrdup(value);
- else if (!strcmp(name, "clone-prefix"))
- cgit_clone_prefix = xstrdup(value);
- else if (!strcmp(name, "repo.group"))
- cgit_repo_group = xstrdup(value);
- else if (!strcmp(name, "repo.url"))
- cgit_repo = add_repo(value);
- else if (!strcmp(name, "repo.name"))
- cgit_repo->name = xstrdup(value);
- else if (cgit_repo && !strcmp(name, "repo.path"))
- cgit_repo->path = trim_end(value, '/');
- else if (cgit_repo && !strcmp(name, "repo.clone-url"))
- cgit_repo->clone_url = xstrdup(value);
- else if (cgit_repo && !strcmp(name, "repo.desc"))
- cgit_repo->desc = xstrdup(value);
- else if (cgit_repo && !strcmp(name, "repo.owner"))
- cgit_repo->owner = xstrdup(value);
- else if (cgit_repo && !strcmp(name, "repo.defbranch"))
- cgit_repo->defbranch = xstrdup(value);
- else if (cgit_repo && !strcmp(name, "repo.snapshots"))
- cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
- else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
- cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
- else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
- cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
- else if (cgit_repo && !strcmp(name, "repo.module-link"))
- cgit_repo->module_link= xstrdup(value);
- else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
- if (*value == '/')
- cgit_repo->readme = xstrdup(value);
- else
- cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
- } else if (!strcmp(name, "include"))
- cgit_read_config(value, cgit_global_config_cb);
-}
-
-void cgit_querystring_cb(const char *name, const char *value)
-{
- if (!strcmp(name,"r")) {
- cgit_query_repo = xstrdup(value);
- cgit_repo = cgit_get_repoinfo(value);
- } else if (!strcmp(name, "p")) {
- cgit_query_page = xstrdup(value);
- cgit_cmd = cgit_get_cmd_index(value);
- } else if (!strcmp(name, "url")) {
- cgit_parse_url(value);
- } else if (!strcmp(name, "qt")) {
- cgit_query_grep = xstrdup(value);
- } else if (!strcmp(name, "q")) {
- cgit_query_search = xstrdup(value);
- } else if (!strcmp(name, "h")) {
- cgit_query_head = xstrdup(value);
- cgit_query_has_symref = 1;
- } else if (!strcmp(name, "id")) {
- cgit_query_sha1 = xstrdup(value);
- cgit_query_has_sha1 = 1;
- } else if (!strcmp(name, "id2")) {
- cgit_query_sha2 = xstrdup(value);
- cgit_query_has_sha1 = 1;
- } else if (!strcmp(name, "ofs")) {
- cgit_query_ofs = atoi(value);
- } else if (!strcmp(name, "path")) {
- cgit_query_path = trim_end(value, '/');
- } else if (!strcmp(name, "name")) {
- cgit_query_name = xstrdup(value);
- }
-}
-
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;
}
-int hextoint(char c)
-{
- if (c >= 'a' && c <= 'f')
- return 10 + c - 'a';
- else if (c >= 'A' && c <= 'F')
- return 10 + c - 'A';
- else if (c >= '0' && c <= '9')
- return c - '0';
- else
- return -1;
-}
-
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;
c = t[len];
t[len] = '\0';
s = xstrdup(t);
t[len] = c;
return s;
}
char *strlpart(char *txt, int maxlen)
{
char *result;
if (!txt)
return txt;
if (strlen(txt) <= maxlen)
return txt;
result = xmalloc(maxlen + 1);
@@ -462,60 +258,87 @@ int cgit_diff_files(const unsigned char *old_sha1,
const unsigned char *new_sha1,
linediff_fn fn)
{
mmfile_t file1, file2;
xpparam_t diff_params;
xdemitconf_t emit_params;
xdemitcb_t emit_cb;
if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
return 1;
diff_params.flags = XDF_NEED_MINIMAL;
emit_params.ctxlen = 3;
emit_params.flags = XDL_EMIT_FUNCNAMES;
emit_params.find_func = NULL;
emit_cb.outf = filediff_cb;
emit_cb.priv = fn;
xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
return 0;
}
void cgit_diff_tree(const unsigned char *old_sha1,
const unsigned char *new_sha1,
filepair_fn fn, const char *prefix)
{
struct diff_options opt;
int ret;
int prefixlen;
diff_setup(&opt);
opt.output_format = DIFF_FORMAT_CALLBACK;
opt.detect_rename = 1;
- opt.rename_limit = cgit_renamelimit;
+ opt.rename_limit = ctx.cfg.renamelimit;
DIFF_OPT_SET(&opt, RECURSIVE);
opt.format_callback = cgit_diff_tree_cb;
opt.format_callback_data = fn;
if (prefix) {
opt.nr_paths = 1;
opt.paths = &prefix;
prefixlen = strlen(prefix);
opt.pathlens = &prefixlen;
}
diff_setup_done(&opt);
if (old_sha1 && !is_null_sha1(old_sha1))
ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
else
ret = diff_root_tree_sha1(new_sha1, "", &opt);
diffcore_std(&opt);
diff_flush(&opt);
}
void cgit_diff_commit(struct commit *commit, filepair_fn fn)
{
unsigned char *old_sha1 = NULL;
if (commit->parents)
old_sha1 = commit->parents->item->object.sha1;
cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
}
+
+int cgit_parse_snapshots_mask(const char *str)
+{
+ const struct cgit_snapshot_format *f;
+ static const char *delim = " \t,:/|;";
+ int tl, sl, rv = 0;
+
+ /* favor legacy setting */
+ if(atoi(str))
+ return 1;
+ for(;;) {
+ str += strspn(str,delim);
+ tl = strcspn(str,delim);
+ if (!tl)
+ break;
+ for (f = cgit_snapshot_formats; f->suffix; f++) {
+ sl = strlen(f->suffix);
+ if((tl == sl && !strncmp(f->suffix, str, tl)) ||
+ (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) {
+ rv |= f->bit;
+ break;
+ }
+ }
+ str += tl;
+ }
+ return rv;
+}
diff --git a/ui-blob.c b/ui-blob.c
index f5b844b..11589db 100644
--- a/ui-blob.c
+++ b/ui-blob.c
@@ -1,31 +1,43 @@
+/* ui-blob.c: show blob content
+ *
+ * Copyright (C) 2008 Lars Hjemli
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
-void cgit_print_blob(struct cacheitem *item, const char *hex, char *path)
+void cgit_print_blob(const char *hex, char *path)
{
unsigned char sha1[20];
enum object_type type;
unsigned char *buf;
unsigned long size;
if (get_sha1_hex(hex, sha1)){
cgit_print_error(fmt("Bad hex value: %s", hex));
return;
}
type = sha1_object_info(sha1, &size);
if (type == OBJ_BAD) {
cgit_print_error(fmt("Bad object name: %s", hex));
return;
}
buf = read_sha1_file(sha1, &type, &size);
if (!buf) {
cgit_print_error(fmt("Error reading object %s", hex));
return;
}
buf[size] = '\0';
- cgit_print_snapshot_start("text/plain", path, item);
+ ctx.page.mimetype = "text/plain";
+ ctx.page.filename = path;
+ cgit_print_http_headers(&ctx);
write(htmlfd, buf, size);
}
diff --git a/ui-blob.h b/ui-blob.h
new file mode 100644
index 0000000..5a920a8
--- a/dev/null
+++ b/ui-blob.h
@@ -0,0 +1,6 @@
+#ifndef UI_BLOB_H
+#define UI_BLOB_H
+
+extern void cgit_print_blob(const char *hex, char *path);
+
+#endif /* UI_BLOB_H */
diff --git a/ui-commit.c b/ui-commit.c
index bd55a33..8019e36 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,110 +1,112 @@
/* ui-commit.c: generate commit view
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
static int files, slots;
static int total_adds, total_rems, max_changes;
static int lines_added, lines_removed;
static char *curr_rev;
static struct fileinfo {
char status;
unsigned char old_sha1[20];
unsigned char new_sha1[20];
unsigned short old_mode;
unsigned short new_mode;
char *old_path;
char *new_path;
unsigned int added;
unsigned int removed;
} *items;
void print_fileinfo(struct fileinfo *info)
{
char *class;
switch (info->status) {
case DIFF_STATUS_ADDED:
class = "add";
break;
case DIFF_STATUS_COPIED:
class = "cpy";
break;
case DIFF_STATUS_DELETED:
class = "del";
break;
case DIFF_STATUS_MODIFIED:
class = "upd";
break;
case DIFF_STATUS_RENAMED:
class = "mov";
break;
case DIFF_STATUS_TYPE_CHANGED:
class = "typ";
break;
case DIFF_STATUS_UNKNOWN:
class = "unk";
break;
case DIFF_STATUS_UNMERGED:
class = "stg";
break;
default:
die("bug: unhandled diff status %c", info->status);
}
html("<tr>");
htmlf("<td class='mode'>");
if (is_null_sha1(info->new_sha1)) {
- html_filemode(info->old_mode);
+ cgit_print_filemode(info->old_mode);
} else {
- html_filemode(info->new_mode);
+ cgit_print_filemode(info->new_mode);
}
if (info->old_mode != info->new_mode &&
!is_null_sha1(info->old_sha1) &&
!is_null_sha1(info->new_sha1)) {
html("<span class='modechange'>[");
- html_filemode(info->old_mode);
+ cgit_print_filemode(info->old_mode);
html("]</span>");
}
htmlf("</td><td class='%s'>", class);
- cgit_diff_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev,
+ cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, curr_rev,
NULL, info->new_path);
if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
htmlf(" (%s from %s)",
info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
info->old_path);
html("</td><td class='right'>");
htmlf("%d", info->added + info->removed);
html("</td><td class='graph'>");
htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
htmlf("<td class='add' style='width: %.1f%%;'/>",
info->added * 100.0 / max_changes);
htmlf("<td class='rem' style='width: %.1f%%;'/>",
info->removed * 100.0 / max_changes);
htmlf("<td class='none' style='width: %.1f%%;'/>",
(max_changes - info->removed - info->added) * 100.0 / max_changes);
html("</tr></table></td></tr>\n");
}
void cgit_count_diff_lines(char *line, int len)
{
if (line && (len > 0)) {
if (line[0] == '+')
lines_added++;
else if (line[0] == '-')
lines_removed++;
}
}
void inspect_filepair(struct diff_filepair *pair)
{
files++;
lines_added = 0;
@@ -114,113 +116,113 @@ void inspect_filepair(struct diff_filepair *pair)
if (slots == 0)
slots = 4;
else
slots = slots * 2;
items = xrealloc(items, slots * sizeof(struct fileinfo));
}
items[files-1].status = pair->status;
hashcpy(items[files-1].old_sha1, pair->one->sha1);
hashcpy(items[files-1].new_sha1, pair->two->sha1);
items[files-1].old_mode = pair->one->mode;
items[files-1].new_mode = pair->two->mode;
items[files-1].old_path = xstrdup(pair->one->path);
items[files-1].new_path = xstrdup(pair->two->path);
items[files-1].added = lines_added;
items[files-1].removed = lines_removed;
if (lines_added + lines_removed > max_changes)
max_changes = lines_added + lines_removed;
total_adds += lines_added;
total_rems += lines_removed;
}
void cgit_print_commit(char *hex)
{
struct commit *commit, *parent;
struct commitinfo *info;
struct commit_list *p;
unsigned char sha1[20];
char *tmp;
int i;
if (!hex)
- hex = cgit_query_head;
+ hex = ctx.qry.head;
curr_rev = hex;
if (get_sha1(hex, sha1)) {
cgit_print_error(fmt("Bad object id: %s", hex));
return;
}
commit = lookup_commit_reference(sha1);
if (!commit) {
cgit_print_error(fmt("Bad commit reference: %s", hex));
return;
}
info = cgit_parse_commit(commit);
html("<table summary='commit info' class='commit-info'>\n");
html("<tr><th>author</th><td>");
html_txt(info->author);
html(" ");
html_txt(info->author_email);
html("</td><td class='right'>");
cgit_print_date(info->author_date, FMT_LONGDATE);
html("</td></tr>\n");
html("<tr><th>committer</th><td>");
html_txt(info->committer);
html(" ");
html_txt(info->committer_email);
html("</td><td class='right'>");
cgit_print_date(info->committer_date, FMT_LONGDATE);
html("</td></tr>\n");
html("<tr><th>tree</th><td colspan='2' class='sha1'>");
tmp = xstrdup(hex);
cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
- cgit_query_head, tmp, NULL);
+ ctx.qry.head, tmp, NULL);
html("</td></tr>\n");
for (p = commit->parents; p ; p = p->next) {
parent = lookup_commit_reference(p->item->object.sha1);
if (!parent) {
html("<tr><td colspan='3'>");
cgit_print_error("Error reading parent commit");
html("</td></tr>");
continue;
}
html("<tr><th>parent</th>"
"<td colspan='2' class='sha1'>");
cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
- cgit_query_head, sha1_to_hex(p->item->object.sha1));
+ ctx.qry.head, sha1_to_hex(p->item->object.sha1));
html(" (");
- cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex,
+ cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
sha1_to_hex(p->item->object.sha1), NULL);
html(")</td></tr>");
}
- if (cgit_repo->snapshots) {
+ if (ctx.repo->snapshots) {
html("<tr><th>download</th><td colspan='2' class='sha1'>");
- cgit_print_snapshot_links(cgit_query_repo, cgit_query_head,
- hex, cgit_repo->snapshots);
+ cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
+ hex, ctx.repo->snapshots);
html("</td></tr>");
}
html("</table>\n");
html("<div class='commit-subject'>");
html_txt(info->subject);
html("</div>");
html("<div class='commit-msg'>");
html_txt(info->msg);
html("</div>");
if (!(commit->parents && commit->parents->next && commit->parents->next->next)) {
html("<div class='diffstat-header'>Diffstat</div>");
html("<table summary='diffstat' class='diffstat'>");
max_changes = 0;
cgit_diff_commit(commit, inspect_filepair);
for(i = 0; i<files; i++)
print_fileinfo(&items[i]);
html("</table>");
html("<div class='diffstat-summary'>");
htmlf("%d files changed, %d insertions, %d deletions (",
files, total_adds, total_rems);
- cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex,
+ cgit_diff_link("show diff", NULL, NULL, ctx.qry.head, hex,
NULL, NULL);
html(")</div>");
}
cgit_free_commitinfo(info);
}
diff --git a/ui-commit.h b/ui-commit.h
new file mode 100644
index 0000000..40bcb31
--- a/dev/null
+++ b/ui-commit.h
@@ -0,0 +1,6 @@
+#ifndef UI_COMMIT_H
+#define UI_COMMIT_H
+
+extern void cgit_print_commit(char *hex);
+
+#endif /* UI_COMMIT_H */
diff --git a/ui-diff.c b/ui-diff.c
index 4fcf852..2a22009 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -1,142 +1,143 @@
/* ui-diff.c: show diff between two blobs
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
-
+#include "html.h"
+#include "ui-shared.h"
unsigned char old_rev_sha1[20];
unsigned char new_rev_sha1[20];
/*
* print a single line returned from xdiff
*/
static void print_line(char *line, int len)
{
char *class = "ctx";
char c = line[len-1];
if (line[0] == '+')
class = "add";
else if (line[0] == '-')
class = "del";
else if (line[0] == '@')
class = "hunk";
htmlf("<div class='%s'>", class);
line[len-1] = '\0';
html_txt(line);
html("</div>");
line[len-1] = c;
}
static void header(unsigned char *sha1, char *path1, int mode1,
unsigned char *sha2, char *path2, int mode2)
{
char *abbrev1, *abbrev2;
int subproject;
subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
html("<div class='head'>");
html("diff --git a/");
html_txt(path1);
html(" b/");
html_txt(path2);
if (is_null_sha1(sha1))
path1 = "dev/null";
if (is_null_sha1(sha2))
path2 = "dev/null";
if (mode1 == 0)
htmlf("<br/>new file mode %.6o", mode2);
if (mode2 == 0)
htmlf("<br/>deleted file mode %.6o", mode1);
if (!subproject) {
abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
htmlf("<br/>index %s..%s", abbrev1, abbrev2);
free(abbrev1);
free(abbrev2);
if (mode1 != 0 && mode2 != 0) {
htmlf(" %.6o", mode1);
if (mode2 != mode1)
htmlf("..%.6o", mode2);
}
html("<br/>--- a/");
if (mode1 != 0)
- cgit_tree_link(path1, NULL, NULL, cgit_query_head,
+ cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
sha1_to_hex(old_rev_sha1), path1);
else
html_txt(path1);
html("<br/>+++ b/");
if (mode2 != 0)
- cgit_tree_link(path2, NULL, NULL, cgit_query_head,
+ cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
sha1_to_hex(new_rev_sha1), path2);
else
html_txt(path2);
}
html("</div>");
}
static void filepair_cb(struct diff_filepair *pair)
{
header(pair->one->sha1, pair->one->path, pair->one->mode,
pair->two->sha1, pair->two->path, pair->two->mode);
if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
if (S_ISGITLINK(pair->one->mode))
print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
if (S_ISGITLINK(pair->two->mode))
print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
return;
}
if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line))
cgit_print_error("Error running diff");
}
void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix)
{
enum object_type type;
unsigned long size;
struct commit *commit, *commit2;
if (!new_rev)
- new_rev = cgit_query_head;
+ new_rev = ctx.qry.head;
get_sha1(new_rev, new_rev_sha1);
type = sha1_object_info(new_rev_sha1, &size);
if (type == OBJ_BAD) {
cgit_print_error(fmt("Bad object name: %s", new_rev));
return;
}
if (type != OBJ_COMMIT) {
cgit_print_error(fmt("Unhandled object type: %s",
typename(type)));
return;
}
commit = lookup_commit_reference(new_rev_sha1);
if (!commit || parse_commit(commit))
cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1)));
if (old_rev)
get_sha1(old_rev, old_rev_sha1);
else if (commit->parents && commit->parents->item)
hashcpy(old_rev_sha1, commit->parents->item->object.sha1);
else
hashclr(old_rev_sha1);
if (!is_null_sha1(old_rev_sha1)) {
type = sha1_object_info(old_rev_sha1, &size);
if (type == OBJ_BAD) {
cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1)));
return;
}
commit2 = lookup_commit_reference(old_rev_sha1);
if (!commit2 || parse_commit(commit2))
cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1)));
diff --git a/ui-diff.h b/ui-diff.h
new file mode 100644
index 0000000..2307322
--- a/dev/null
+++ b/ui-diff.h
@@ -0,0 +1,7 @@
+#ifndef UI_DIFF_H
+#define UI_DIFF_H
+
+extern void cgit_print_diff(const char *new_hex, const char *old_hex,
+ const char *prefix);
+
+#endif /* UI_DIFF_H */
diff --git a/ui-log.c b/ui-log.c
index a41d2b2..60c9269 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,140 +1,142 @@
/* ui-log.c: functions for log output
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
int files, add_lines, rem_lines;
void count_lines(char *line, int size)
{
if (size <= 0)
return;
if (line[0] == '+')
add_lines++;
else if (line[0] == '-')
rem_lines++;
}
void inspect_files(struct diff_filepair *pair)
{
files++;
- if (cgit_repo->enable_log_linecount)
+ if (ctx.repo->enable_log_linecount)
cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines);
}
void print_commit(struct commit *commit)
{
struct commitinfo *info;
info = cgit_parse_commit(commit);
html("<tr><td>");
cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
html("</td><td>");
- cgit_commit_link(info->subject, NULL, NULL, cgit_query_head,
+ cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
sha1_to_hex(commit->object.sha1));
- if (cgit_repo->enable_log_filecount) {
+ if (ctx.repo->enable_log_filecount) {
files = 0;
add_lines = 0;
rem_lines = 0;
cgit_diff_commit(commit, inspect_files);
html("</td><td class='right'>");
htmlf("%d", files);
- if (cgit_repo->enable_log_linecount) {
+ if (ctx.repo->enable_log_linecount) {
html("</td><td class='right'>");
htmlf("-%d/+%d", rem_lines, add_lines);
}
}
html("</td><td>");
html_txt(info->author);
html("</td></tr>\n");
cgit_free_commitinfo(info);
}
void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager)
{
struct rev_info rev;
struct commit *commit;
const char *argv[] = {NULL, tip, NULL, NULL, NULL};
int argc = 2;
int i;
if (!tip)
- argv[1] = cgit_query_head;
+ argv[1] = ctx.qry.head;
if (grep && pattern && (!strcmp(grep, "grep") ||
!strcmp(grep, "author") ||
!strcmp(grep, "committer")))
argv[argc++] = fmt("--%s=%s", grep, pattern);
if (path) {
argv[argc++] = "--";
argv[argc++] = path;
}
init_revisions(&rev, NULL);
rev.abbrev = DEFAULT_ABBREV;
rev.commit_format = CMIT_FMT_DEFAULT;
rev.verbose_header = 1;
rev.show_root_diff = 0;
setup_revisions(argc, argv, &rev, NULL);
if (rev.grep_filter) {
rev.grep_filter->regflags |= REG_ICASE;
compile_grep_patterns(rev.grep_filter);
}
prepare_revision_walk(&rev);
html("<table summary='log' class='list nowrap'>");
html("<tr class='nohover'><th class='left'>Age</th>"
"<th class='left'>Message</th>");
- if (cgit_repo->enable_log_filecount) {
+ if (ctx.repo->enable_log_filecount) {
html("<th class='right'>Files</th>");
- if (cgit_repo->enable_log_linecount)
+ if (ctx.repo->enable_log_linecount)
html("<th class='right'>Lines</th>");
}
html("<th class='left'>Author</th></tr>\n");
if (ofs<0)
ofs = 0;
for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
free(commit->buffer);
commit->buffer = NULL;
free_commit_list(commit->parents);
commit->parents = NULL;
}
for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
print_commit(commit);
free(commit->buffer);
commit->buffer = NULL;
free_commit_list(commit->parents);
commit->parents = NULL;
}
html("</table>\n");
if (pager) {
html("<div class='pager'>");
if (ofs > 0) {
- cgit_log_link("[prev]", NULL, NULL, cgit_query_head,
- cgit_query_sha1, cgit_query_path,
- ofs - cnt, cgit_query_grep,
- cgit_query_search);
+ cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
+ ctx.qry.sha1, ctx.qry.path,
+ ofs - cnt, ctx.qry.grep,
+ ctx.qry.search);
html("&nbsp;");
}
if ((commit = get_revision(&rev)) != NULL) {
- cgit_log_link("[next]", NULL, NULL, cgit_query_head,
- cgit_query_sha1, cgit_query_path,
- ofs + cnt, cgit_query_grep,
- cgit_query_search);
+ cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
+ ctx.qry.sha1, ctx.qry.path,
+ ofs + cnt, ctx.qry.grep,
+ ctx.qry.search);
}
html("</div>");
}
}
diff --git a/ui-log.h b/ui-log.h
new file mode 100644
index 0000000..877e40e
--- a/dev/null
+++ b/ui-log.h
@@ -0,0 +1,7 @@
+#ifndef UI_LOG_H
+#define UI_LOG_H
+
+extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
+ char *pattern, char *path, int pager);
+
+#endif /* UI_LOG_H */
diff --git a/ui-patch.c b/ui-patch.c
index 7ee2c41..c1c4ce3 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -1,114 +1,118 @@
/* ui-patch.c: generate patch view
*
* Copyright (C) 2007 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
static void print_line(char *line, int len)
{
char c = line[len-1];
line[len-1] = '\0';
htmlf("%s\n", line);
line[len-1] = c;
}
static void header(unsigned char *sha1, char *path1, int mode1,
unsigned char *sha2, char *path2, int mode2)
{
char *abbrev1, *abbrev2;
int subproject;
subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
htmlf("diff --git a/%s b/%s\n", path1, path2);
if (is_null_sha1(sha1))
path1 = "dev/null";
if (is_null_sha1(sha2))
path2 = "dev/null";
if (mode1 == 0)
htmlf("new file mode %.6o\n", mode2);
if (mode2 == 0)
htmlf("deleted file mode %.6o\n", mode1);
if (!subproject) {
abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
htmlf("index %s..%s", abbrev1, abbrev2);
free(abbrev1);
free(abbrev2);
if (mode1 != 0 && mode2 != 0) {
htmlf(" %.6o", mode1);
if (mode2 != mode1)
htmlf("..%.6o", mode2);
}
htmlf("\n--- a/%s\n", path1);
htmlf("+++ b/%s\n", path2);
}
}
static void filepair_cb(struct diff_filepair *pair)
{
header(pair->one->sha1, pair->one->path, pair->one->mode,
pair->two->sha1, pair->two->path, pair->two->mode);
if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
if (S_ISGITLINK(pair->one->mode))
print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
if (S_ISGITLINK(pair->two->mode))
print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
return;
}
if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line))
html("Error running diff");
}
-void cgit_print_patch(char *hex, struct cacheitem *item)
+void cgit_print_patch(char *hex)
{
struct commit *commit;
struct commitinfo *info;
unsigned char sha1[20], old_sha1[20];
char *patchname;
if (!hex)
- hex = cgit_query_head;
+ hex = ctx.qry.head;
if (get_sha1(hex, sha1)) {
cgit_print_error(fmt("Bad object id: %s", hex));
return;
}
commit = lookup_commit_reference(sha1);
if (!commit) {
cgit_print_error(fmt("Bad commit reference: %s", hex));
return;
}
info = cgit_parse_commit(commit);
if (commit->parents && commit->parents->item)
hashcpy(old_sha1, commit->parents->item->object.sha1);
else
hashclr(old_sha1);
patchname = fmt("%s.patch", sha1_to_hex(sha1));
- cgit_print_snapshot_start("text/plain", patchname, item);
+ ctx.page.mimetype = "text/plain";
+ ctx.page.filename = patchname;
+ cgit_print_http_headers(&ctx);
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);
html("Date: ");
cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n");
htmlf("Subject: %s\n\n", info->subject);
if (info->msg && *info->msg) {
htmlf("%s", info->msg);
if (info->msg[strlen(info->msg) - 1] != '\n')
html("\n");
}
html("---\n");
cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL);
html("--\n");
htmlf("cgit %s\n", CGIT_VERSION);
cgit_free_commitinfo(info);
}
diff --git a/ui-patch.h b/ui-patch.h
new file mode 100644
index 0000000..9f68212
--- a/dev/null
+++ b/ui-patch.h
@@ -0,0 +1,6 @@
+#ifndef UI_PATCH_H
+#define UI_PATCH_H
+
+extern void cgit_print_patch(char *hex);
+
+#endif /* UI_PATCH_H */
diff --git a/ui-refs.c b/ui-refs.c
index 295f5ba..12533cd 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -1,30 +1,201 @@
/* ui-refs.c: browse symbolic refs
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
+static int header;
+static int cmp_age(int age1, int age2)
+{
+ if (age1 != 0 && age2 != 0)
+ return age2 - age1;
+
+ if (age1 == 0 && age2 == 0)
+ return 0;
+
+ if (age1 == 0)
+ return +1;
+
+ return -1;
+}
+
+static int cmp_ref_name(const void *a, const void *b)
+{
+ struct refinfo *r1 = *(struct refinfo **)a;
+ struct refinfo *r2 = *(struct refinfo **)b;
+
+ return strcmp(r1->refname, r2->refname);
+}
+
+static int cmp_branch_age(const void *a, const void *b)
+{
+ struct refinfo *r1 = *(struct refinfo **)a;
+ struct refinfo *r2 = *(struct refinfo **)b;
+
+ return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
+}
+
+static int cmp_tag_age(const void *a, const void *b)
+{
+ struct refinfo *r1 = *(struct refinfo **)a;
+ struct refinfo *r2 = *(struct refinfo **)b;
+
+ return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
+}
+
+static int print_branch(struct refinfo *ref)
+{
+ struct commitinfo *info = ref->commit;
+ char *name = (char *)ref->refname;
+
+ if (!info)
+ return 1;
+ html("<tr><td>");
+ cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL);
+ html("</td><td>");
+
+ if (ref->object->type == OBJ_COMMIT) {
+ cgit_print_age(info->commit->date, -1, NULL);
+ html("</td><td>");
+ html_txt(info->author);
+ html("</td><td>");
+ cgit_commit_link(info->subject, NULL, NULL, name, NULL);
+ } else {
+ html("</td><td></td><td>");
+ cgit_object_link(ref->object);
+ }
+ html("</td></tr>\n");
+ return 0;
+}
+
+static void print_tag_header()
+{
+ html("<tr class='nohover'><th class='left'>Tag</th>"
+ "<th class='left'>Age</th>"
+ "<th class='left'>Author</th>"
+ "<th class='left'>Reference</th></tr>\n");
+ header = 1;
+}
+
+static int print_tag(struct refinfo *ref)
+{
+ struct tag *tag;
+ struct taginfo *info;
+ char *url, *name = (char *)ref->refname;
+
+ if (ref->object->type == OBJ_TAG) {
+ tag = (struct tag *)ref->object;
+ info = ref->tag;
+ if (!tag || !info)
+ return 1;
+ html("<tr><td>");
+ url = cgit_pageurl(ctx.qry.repo, "tag",
+ fmt("id=%s", name));
+ html_link_open(url, NULL, NULL);
+ html_txt(name);
+ html_link_close();
+ html("</td><td>");
+ if (info->tagger_date > 0)
+ cgit_print_age(info->tagger_date, -1, NULL);
+ html("</td><td>");
+ if (info->tagger)
+ html(info->tagger);
+ html("</td><td>");
+ cgit_object_link(tag->tagged);
+ html("</td></tr>\n");
+ } else {
+ if (!header)
+ print_tag_header();
+ html("<tr><td>");
+ html_txt(name);
+ html("</td><td colspan='2'/><td>");
+ cgit_object_link(ref->object);
+ html("</td></tr>\n");
+ }
+ return 0;
+}
+
+static void print_refs_link(char *path)
+{
+ html("<tr class='nohover'><td colspan='4'>");
+ cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path);
+ html("</td></tr>");
+}
+
+void cgit_print_branches(int maxcount)
+{
+ struct reflist list;
+ int i;
+ html("<tr class='nohover'><th class='left'>Branch</th>"
+ "<th class='left'>Idle</th>"
+ "<th class='left'>Author</th>"
+ "<th class='left'>Head commit</th></tr>\n");
+
+ list.refs = NULL;
+ list.alloc = list.count = 0;
+ for_each_branch_ref(cgit_refs_cb, &list);
+
+ if (maxcount == 0 || maxcount > list.count)
+ maxcount = list.count;
+
+ if (maxcount < list.count) {
+ qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
+ qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
+ }
+
+ for(i=0; i<maxcount; i++)
+ print_branch(list.refs[i]);
+
+ if (maxcount < list.count)
+ print_refs_link("heads");
+}
+
+void cgit_print_tags(int maxcount)
+{
+ struct reflist list;
+ int i;
+
+ header = 0;
+ list.refs = NULL;
+ list.alloc = list.count = 0;
+ for_each_tag_ref(cgit_refs_cb, &list);
+ if (list.count == 0)
+ return;
+ qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
+ if (!maxcount)
+ maxcount = list.count;
+ else if (maxcount > list.count)
+ maxcount = list.count;
+ print_tag_header();
+ for(i=0; i<maxcount; i++)
+ print_tag(list.refs[i]);
+
+ if (maxcount < list.count)
+ print_refs_link("tags");
+}
void cgit_print_refs()
{
html("<table class='list nowrap'>");
- if (cgit_query_path && !strncmp(cgit_query_path, "heads", 5))
+ if (ctx.qry.path && !strncmp(ctx.qry.path, "heads", 5))
cgit_print_branches(0);
- else if (cgit_query_path && !strncmp(cgit_query_path, "tags", 4))
+ else if (ctx.qry.path && !strncmp(ctx.qry.path, "tags", 4))
cgit_print_tags(0);
else {
cgit_print_branches(0);
html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
cgit_print_tags(0);
}
html("</table>");
}
diff --git a/ui-refs.h b/ui-refs.h
new file mode 100644
index 0000000..b35c04a
--- a/dev/null
+++ b/ui-refs.h
@@ -0,0 +1,8 @@
+#ifndef UI_REFS_H
+#define UI_REFS_H
+
+extern void cgit_print_branches(int maxcount);
+extern void cgit_print_tags(int maxcount);
+extern void cgit_print_refs();
+
+#endif /* UI_REFS_H */
diff --git a/ui-repolist.c b/ui-repolist.c
index 3e97ca9..eeeaf3d 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -1,110 +1,114 @@
/* ui-repolist.c: functions for generating the repolist page
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
-#include "cgit.h"
#include <time.h>
+#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
time_t read_agefile(char *path)
{
FILE *f;
static char buf[64], buf2[64];
if (!(f = fopen(path, "r")))
return -1;
fgets(buf, sizeof(buf), f);
fclose(f);
if (parse_date(buf, buf2, sizeof(buf2)))
return strtoul(buf2, NULL, 10);
else
return 0;
}
-static void print_modtime(struct repoinfo *repo)
+static void print_modtime(struct cgit_repo *repo)
{
char *path;
struct stat s;
- path = fmt("%s/%s", repo->path, cgit_agefile);
+ path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
if (stat(path, &s) == 0) {
cgit_print_age(read_agefile(path), -1, NULL);
return;
}
path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
if (stat(path, &s) != 0)
return;
cgit_print_age(s.st_mtime, -1, NULL);
}
-void cgit_print_repolist(struct cacheitem *item)
+void cgit_print_repolist()
{
int i, columns = 4;
char *last_group = NULL;
- if (cgit_enable_index_links)
+ if (ctx.cfg.enable_index_links)
columns++;
- cgit_print_docstart(cgit_root_title, item);
- cgit_print_pageheader(cgit_root_title, 0);
+ ctx.page.title = ctx.cfg.root_title;
+ cgit_print_http_headers(&ctx);
+ cgit_print_docstart(&ctx);
+ cgit_print_pageheader(&ctx);
html("<table summary='repository list' class='list nowrap'>");
- if (cgit_index_header) {
+ if (ctx.cfg.index_header) {
htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>",
columns);
- html_include(cgit_index_header);
+ html_include(ctx.cfg.index_header);
html("</td></tr>");
}
html("<tr class='nohover'>"
"<th class='left'>Name</th>"
"<th class='left'>Description</th>"
"<th class='left'>Owner</th>"
"<th class='left'>Idle</th>");
- if (cgit_enable_index_links)
+ if (ctx.cfg.enable_index_links)
html("<th>Links</th>");
html("</tr>\n");
for (i=0; i<cgit_repolist.count; i++) {
- cgit_repo = &cgit_repolist.repos[i];
- if ((last_group == NULL && cgit_repo->group != NULL) ||
- (last_group != NULL && cgit_repo->group == NULL) ||
- (last_group != NULL && cgit_repo->group != NULL &&
- strcmp(cgit_repo->group, last_group))) {
+ ctx.repo = &cgit_repolist.repos[i];
+ if ((last_group == NULL && ctx.repo->group != NULL) ||
+ (last_group != NULL && ctx.repo->group == NULL) ||
+ (last_group != NULL && ctx.repo->group != NULL &&
+ strcmp(ctx.repo->group, last_group))) {
htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
columns);
- html_txt(cgit_repo->group);
+ html_txt(ctx.repo->group);
html("</td></tr>");
- last_group = cgit_repo->group;
+ last_group = ctx.repo->group;
}
htmlf("<tr><td class='%s'>",
- cgit_repo->group ? "sublevel-repo" : "toplevel-repo");
- html_link_open(cgit_repourl(cgit_repo->url), NULL, NULL);
- html_txt(cgit_repo->name);
+ ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
+ html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
+ html_txt(ctx.repo->name);
html_link_close();
html("</td><td>");
- html_ntxt(cgit_max_repodesc_len, cgit_repo->desc);
+ html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
html("</td><td>");
- html_txt(cgit_repo->owner);
+ html_txt(ctx.repo->owner);
html("</td><td>");
- print_modtime(cgit_repo);
+ print_modtime(ctx.repo);
html("</td>");
- if (cgit_enable_index_links) {
+ if (ctx.cfg.enable_index_links) {
html("<td>");
- html_link_open(cgit_repourl(cgit_repo->url),
+ html_link_open(cgit_repourl(ctx.repo->url),
NULL, "button");
html("summary</a>");
cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
0, NULL, NULL);
cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
html("</td>");
}
html("</tr>\n");
}
html("</table>");
cgit_print_docend();
}
diff --git a/ui-repolist.h b/ui-repolist.h
new file mode 100644
index 0000000..c23e5d2
--- a/dev/null
+++ b/ui-repolist.h
@@ -0,0 +1,6 @@
+#ifndef UI_REPOLIST_H
+#define UI_REPOLIST_H
+
+extern void cgit_print_repolist();
+
+#endif /* UI_REPOLIST_H */
diff --git a/ui-shared.c b/ui-shared.c
index 60aa2e3..aa65988 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,570 +1,591 @@
/* 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 "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", "Now", "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);
}
-static long ttl_seconds(long ttl)
-{
- if (ttl<0)
- return 60 * 60 * 24 * 365;
- else
- return ttl * 60;
-}
-
void cgit_print_error(char *msg)
{
html("<div class='error'>");
html_txt(msg);
html("</div>\n");
}
char *cgit_rooturl()
{
- if (cgit_virtual_root)
- return fmt("%s/", cgit_virtual_root);
+ if (ctx.cfg.virtual_root)
+ return fmt("%s/", ctx.cfg.virtual_root);
else
- return cgit_script_name;
+ return ctx.cfg.script_name;
}
char *cgit_repourl(const char *reponame)
{
- if (cgit_virtual_root) {
- return fmt("%s/%s/", cgit_virtual_root, 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 (cgit_virtual_root) {
- tmp = fmt("%s/%s/%s/%s", cgit_virtual_root, reponame,
+ 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 (!cgit_virtual_root)
- return cgit_script_name;
- else if (cgit_query_page)
- return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page);
- else if (cgit_query_repo)
- return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
+ 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/", cgit_virtual_root);
+ return fmt("%s/", ctx.cfg.virtual_root);
}
static char *repolink(char *title, char *class, char *page, char *head,
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 (cgit_virtual_root) {
- html_attr(cgit_virtual_root);
- if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
+ if (ctx.cfg.virtual_root) {
+ html_attr(ctx.cfg.virtual_root);
+ if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
html("/");
- html_attr(cgit_repo->url);
- if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
+ html_attr(ctx.repo->url);
+ if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
html("/");
if (page) {
html(page);
html("/");
if (path)
html_attr(path);
}
} else {
- html(cgit_script_name);
+ html(ctx.cfg.script_name);
html("?url=");
- html_attr(cgit_repo->url);
- if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
+ html_attr(ctx.repo->url);
+ if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
html("/");
if (page) {
html(page);
html("/");
if (path)
html_attr(path);
}
delim = "&amp;";
}
- if (head && strcmp(head, cgit_repo->defbranch)) {
+ if (head && strcmp(head, ctx.repo->defbranch)) {
html(delim);
html("h=");
html_attr(head);
delim = "&amp;";
}
return fmt("%s", delim);
}
static void reporevlink(char *page, char *name, char *title, char *class,
char *head, char *rev, char *path)
{
char *delim;
delim = repolink(title, class, page, head, path);
- if (rev && strcmp(rev, cgit_query_head)) {
+ if (rev && strcmp(rev, ctx.qry.head)) {
html(delim);
html("id=");
html_attr(rev);
}
html("'>");
html_txt(name);
html("</a>");
}
void cgit_tree_link(char *name, char *title, char *class, char *head,
char *rev, char *path)
{
reporevlink("tree", name, title, class, head, rev, path);
}
void cgit_log_link(char *name, char *title, char *class, char *head,
char *rev, char *path, int ofs, char *grep, char *pattern)
{
char *delim;
delim = repolink(title, class, "log", head, path);
- if (rev && strcmp(rev, cgit_query_head)) {
+ if (rev && strcmp(rev, ctx.qry.head)) {
html(delim);
html("id=");
html_attr(rev);
delim = "&";
}
if (grep && pattern) {
html(delim);
html("qt=");
html_attr(grep);
delim = "&";
html(delim);
html("q=");
html_attr(pattern);
}
if (ofs > 0) {
html(delim);
html("ofs=");
htmlf("%d", ofs);
}
html("'>");
html_txt(name);
html("</a>");
}
void cgit_commit_link(char *name, char *title, char *class, char *head,
char *rev)
{
- if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
- name[cgit_max_msg_len] = '\0';
- name[cgit_max_msg_len - 1] = '.';
- name[cgit_max_msg_len - 2] = '.';
- name[cgit_max_msg_len - 3] = '.';
+ 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] = '.';
}
reporevlink("commit", name, title, class, head, rev, NULL);
}
void cgit_refs_link(char *name, char *title, char *class, char *head,
char *rev, char *path)
{
reporevlink("refs", name, title, class, head, rev, path);
}
void cgit_snapshot_link(char *name, char *title, char *class, char *head,
char *rev, char *archivename)
{
reporevlink("snapshot", name, title, class, head, rev, archivename);
}
void cgit_diff_link(char *name, char *title, char *class, char *head,
char *new_rev, char *old_rev, char *path)
{
char *delim;
delim = repolink(title, class, "diff", head, path);
- if (new_rev && strcmp(new_rev, cgit_query_head)) {
+ if (new_rev && strcmp(new_rev, ctx.qry.head)) {
html(delim);
html("id=");
html_attr(new_rev);
delim = "&amp;";
}
if (old_rev) {
html(delim);
html("id2=");
html_attr(old_rev);
}
html("'>");
html_txt(name);
html("</a>");
}
void cgit_patch_link(char *name, char *title, char *class, char *head,
char *rev)
{
reporevlink("patch", name, title, class, head, rev, NULL);
}
void cgit_object_link(struct object *obj)
{
char *page, *arg, *url;
if (obj->type == OBJ_COMMIT) {
cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
- cgit_query_head, sha1_to_hex(obj->sha1));
+ ctx.qry.head, sha1_to_hex(obj->sha1));
return;
} else if (obj->type == OBJ_TREE) {
page = "tree";
arg = "id";
} else if (obj->type == OBJ_TAG) {
page = "tag";
arg = "id";
} else {
page = "blob";
arg = "id";
}
- url = cgit_pageurl(cgit_query_repo, page,
+ url = cgit_pageurl(ctx.qry.repo, page,
fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
html_link_open(url, NULL, NULL);
htmlf("%s %s", typename(obj->type),
sha1_to_hex(obj->sha1));
html_link_close();
}
void cgit_print_date(time_t secs, char *format)
{
char buf[64];
struct tm *time;
if (!secs)
return;
time = gmtime(&secs);
strftime(buf, sizeof(buf)-1, format, time);
html_txt(buf);
}
void cgit_print_age(time_t t, time_t max_relative, char *format)
{
time_t now, secs;
if (!t)
return;
time(&now);
secs = now - t;
if (secs > max_relative && max_relative >= 0) {
cgit_print_date(t, format);
return;
}
if (secs < TM_HOUR * 2) {
htmlf("<span class='age-mins'>%.0f min.</span>",
secs * 1.0 / TM_MIN);
return;
}
if (secs < TM_DAY * 2) {
htmlf("<span class='age-hours'>%.0f hours</span>",
secs * 1.0 / TM_HOUR);
return;
}
if (secs < TM_WEEK * 2) {
htmlf("<span class='age-days'>%.0f days</span>",
secs * 1.0 / TM_DAY);
return;
}
if (secs < TM_MONTH * 2) {
htmlf("<span class='age-weeks'>%.0f weeks</span>",
secs * 1.0 / TM_WEEK);
return;
}
if (secs < TM_YEAR * 2) {
htmlf("<span class='age-months'>%.0f months</span>",
secs * 1.0 / TM_MONTH);
return;
}
htmlf("<span class='age-years'>%.0f years</span>",
secs * 1.0 / TM_YEAR);
}
-void cgit_print_docstart(char *title, struct cacheitem *item)
+void cgit_print_http_headers(struct cgit_context *ctx)
{
- html("Content-Type: text/html; charset=" PAGE_ENCODING "\n");
- htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
- htmlf("Expires: %s\n", http_date(item->st.st_mtime +
- ttl_seconds(item->ttl)));
+ if (ctx->page.mimetype && ctx->page.charset)
+ htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
+ ctx->page.charset);
+ else if (ctx->page.mimetype)
+ htmlf("Content-Type: %s\n", ctx->page.mimetype);
+ if (ctx->page.filename)
+ htmlf("Content-Disposition: inline; filename=\"%s\"\n",
+ ctx->page.filename);
+ htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
+ htmlf("Expires: %s\n", http_date(ctx->page.expires));
html("\n");
+}
+
+void cgit_print_docstart(struct cgit_context *ctx)
+{
html(cgit_doctype);
html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
html("<head>\n");
html("<title>");
- html_txt(title);
+ html_txt(ctx->page.title);
html("</title>\n");
htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
- if (cgit_robots && *cgit_robots)
- htmlf("<meta name='robots' content='%s'/>\n", cgit_robots);
+ if (ctx->cfg.robots && *ctx->cfg.robots)
+ htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
html("<link rel='stylesheet' type='text/css' href='");
- html_attr(cgit_css);
+ html_attr(ctx->cfg.css);
html("'/>\n");
html("</head>\n");
html("<body>\n");
}
void cgit_print_docend()
{
html("</td>\n</tr>\n</table>\n</body>\n</html>\n");
}
int print_branch_option(const char *refname, const unsigned char *sha1,
int flags, void *cb_data)
{
char *name = (char *)refname;
- html_option(name, name, cgit_query_head);
+ html_option(name, name, ctx.qry.head);
return 0;
}
int print_archive_ref(const char *refname, const unsigned char *sha1,
int flags, void *cb_data)
{
struct tag *tag;
struct taginfo *info;
struct object *obj;
char buf[256], *url;
unsigned char fileid[20];
int *header = (int *)cb_data;
if (prefixcmp(refname, "refs/archives"))
return 0;
strncpy(buf, refname+14, sizeof(buf));
obj = parse_object(sha1);
if (!obj)
return 1;
if (obj->type == OBJ_TAG) {
tag = lookup_tag(sha1);
if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
return 0;
hashcpy(fileid, tag->tagged->sha1);
} else if (obj->type != OBJ_BLOB) {
return 0;
} else {
hashcpy(fileid, sha1);
}
if (!*header) {
html("<h1>download</h1>\n");
*header = 1;
}
- url = cgit_pageurl(cgit_query_repo, "blob",
+ url = cgit_pageurl(ctx.qry.repo, "blob",
fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
buf));
html_link_open(url, NULL, "menu");
html_txt(strlpart(buf, 20));
html_link_close();
return 0;
}
void add_hidden_formfields(int incl_head, int incl_search, char *page)
{
char *url;
- if (!cgit_virtual_root) {
- url = fmt("%s/%s", cgit_query_repo, page);
- if (cgit_query_path)
- url = fmt("%s/%s", url, cgit_query_path);
+ if (!ctx.cfg.virtual_root) {
+ url = fmt("%s/%s", ctx.qry.repo, page);
+ if (ctx.qry.path)
+ url = fmt("%s/%s", url, ctx.qry.path);
html_hidden("url", url);
}
- if (incl_head && strcmp(cgit_query_head, cgit_repo->defbranch))
- html_hidden("h", cgit_query_head);
+ if (incl_head && strcmp(ctx.qry.head, ctx.repo->defbranch))
+ html_hidden("h", ctx.qry.head);
- if (cgit_query_sha1)
- html_hidden("id", cgit_query_sha1);
- if (cgit_query_sha2)
- html_hidden("id2", cgit_query_sha2);
+ if (ctx.qry.sha1)
+ html_hidden("id", ctx.qry.sha1);
+ if (ctx.qry.sha2)
+ html_hidden("id2", ctx.qry.sha2);
if (incl_search) {
- if (cgit_query_grep)
- html_hidden("qt", cgit_query_grep);
- if (cgit_query_search)
- html_hidden("q", cgit_query_search);
+ if (ctx.qry.grep)
+ html_hidden("qt", ctx.qry.grep);
+ if (ctx.qry.search)
+ html_hidden("q", ctx.qry.search);
}
}
-void cgit_print_pageheader(char *title, int show_search)
+void cgit_print_pageheader(struct cgit_context *ctx)
{
static const char *default_info = "This is cgit, a fast webinterface for git repositories";
int header = 0;
char *url;
html("<table id='layout' summary=''>\n");
html("<tr><td id='sidebar'>\n");
html("<table class='sidebar' cellspacing='0' summary=''>\n");
html("<tr><td class='sidebar'>\n<a href='");
html_attr(cgit_rooturl());
htmlf("'><img src='%s' alt='cgit'/></a>\n",
- cgit_logo);
+ ctx->cfg.logo);
html("</td></tr>\n<tr><td class='sidebar'>\n");
- if (cgit_query_repo) {
+ if (ctx->repo) {
html("<h1 class='first'>");
- html_txt(strrpart(cgit_repo->name, 20));
+ html_txt(strrpart(ctx->repo->name, 20));
html("</h1>\n");
- html_txt(cgit_repo->desc);
- if (cgit_repo->owner) {
+ html_txt(ctx->repo->desc);
+ if (ctx->repo->owner) {
html("<h1>owner</h1>\n");
- html_txt(cgit_repo->owner);
+ html_txt(ctx->repo->owner);
}
html("<h1>navigate</h1>\n");
- reporevlink(NULL, "summary", NULL, "menu", cgit_query_head,
+ reporevlink(NULL, "summary", NULL, "menu", ctx->qry.head,
NULL, NULL);
- cgit_log_link("log", NULL, "menu", cgit_query_head, NULL, NULL,
+ cgit_log_link("log", NULL, "menu", ctx->qry.head, NULL, NULL,
0, NULL, NULL);
- cgit_tree_link("tree", NULL, "menu", cgit_query_head,
- cgit_query_sha1, NULL);
- cgit_commit_link("commit", NULL, "menu", cgit_query_head,
- cgit_query_sha1);
- cgit_diff_link("diff", NULL, "menu", cgit_query_head,
- cgit_query_sha1, cgit_query_sha2, NULL);
- cgit_patch_link("patch", NULL, "menu", cgit_query_head,
- cgit_query_sha1);
+ cgit_tree_link("tree", NULL, "menu", ctx->qry.head,
+ ctx->qry.sha1, NULL);
+ cgit_commit_link("commit", NULL, "menu", ctx->qry.head,
+ ctx->qry.sha1);
+ cgit_diff_link("diff", NULL, "menu", ctx->qry.head,
+ ctx->qry.sha1, ctx->qry.sha2, NULL);
+ cgit_patch_link("patch", NULL, "menu", ctx->qry.head,
+ ctx->qry.sha1);
for_each_ref(print_archive_ref, &header);
- if (cgit_repo->clone_url || cgit_clone_prefix) {
+ if (ctx->repo->clone_url || ctx->cfg.clone_prefix) {
html("<h1>clone</h1>\n");
- if (cgit_repo->clone_url)
- url = cgit_repo->clone_url;
+ if (ctx->repo->clone_url)
+ url = ctx->repo->clone_url;
else
- url = fmt("%s%s", cgit_clone_prefix,
- cgit_repo->url);
+ url = fmt("%s%s", ctx->cfg.clone_prefix,
+ ctx->repo->url);
html("<a class='menu' href='");
html_attr(url);
html("' title='");
html_attr(url);
html("'>\n");
html_txt(strrpart(url, 20));
html("</a>\n");
}
html("<h1>branch</h1>\n");
html("<form method='get' action=''>\n");
- add_hidden_formfields(0, 1, cgit_query_page);
+ add_hidden_formfields(0, 1, ctx->qry.page);
// html("<table summary='branch selector' class='grid'><tr><td id='branch-dropdown-cell'>");
html("<select name='h' onchange='this.form.submit();'>\n");
- for_each_branch_ref(print_branch_option, cgit_query_head);
+ for_each_branch_ref(print_branch_option, ctx->qry.head);
html("</select>\n");
// html("</td><td>");
html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n");
// html("</td></tr></table>");
html("</form>\n");
html("<h1>search</h1>\n");
html("<form method='get' action='");
- if (cgit_virtual_root)
- html_attr(cgit_fileurl(cgit_query_repo, "log",
- cgit_query_path, NULL));
+ if (ctx->cfg.virtual_root)
+ html_attr(cgit_fileurl(ctx->qry.repo, "log",
+ ctx->qry.path, NULL));
html("'>\n");
add_hidden_formfields(1, 0, "log");
html("<select name='qt'>\n");
- html_option("grep", "log msg", cgit_query_grep);
- html_option("author", "author", cgit_query_grep);
- html_option("committer", "committer", cgit_query_grep);
+ html_option("grep", "log msg", ctx->qry.grep);
+ html_option("author", "author", ctx->qry.grep);
+ html_option("committer", "committer", ctx->qry.grep);
html("</select>\n");
html("<input class='txt' type='text' name='q' value='");
- html_attr(cgit_query_search);
+ html_attr(ctx->qry.search);
html("'/>\n");
html("</form>\n");
} else {
- if (!cgit_index_info || html_include(cgit_index_info))
+ if (!ctx->cfg.index_info || html_include(ctx->cfg.index_info))
html(default_info);
}
html("</td></tr></table></td>\n");
html("<td id='content'>\n");
}
-
-void cgit_print_snapshot_start(const char *mimetype, const char *filename,
- struct cacheitem *item)
+void cgit_print_filemode(unsigned short mode)
{
- htmlf("Content-Type: %s\n", mimetype);
- htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
- htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
- htmlf("Expires: %s\n", http_date(item->st.st_mtime +
- ttl_seconds(item->ttl)));
- html("\n");
+ if (S_ISDIR(mode))
+ html("d");
+ else if (S_ISLNK(mode))
+ html("l");
+ else if (S_ISGITLINK(mode))
+ html("m");
+ else
+ html("-");
+ html_fileperm(mode >> 6);
+ html_fileperm(mode >> 3);
+ html_fileperm(mode);
}
-/* vim:set sw=8: */
+void cgit_print_snapshot_links(const char *repo, const char *head,
+ const char *hex, int snapshots)
+{
+ const struct cgit_snapshot_format* f;
+ char *filename;
+
+ for (f = cgit_snapshot_formats; f->suffix; f++) {
+ if (!(snapshots & f->bit))
+ continue;
+ filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
+ f->suffix);
+ cgit_snapshot_link(filename, NULL, NULL, (char *)head,
+ (char *)hex, filename);
+ html("<br/>");
+ }
+}
diff --git a/ui-shared.h b/ui-shared.h
new file mode 100644
index 0000000..94de884
--- a/dev/null
+++ b/ui-shared.h
@@ -0,0 +1,36 @@
+#ifndef UI_SHARED_H
+#define UI_SHARED_H
+
+extern char *cgit_repourl(const char *reponame);
+extern char *cgit_fileurl(const char *reponame, const char *pagename,
+ const char *filename, const char *query);
+extern char *cgit_pageurl(const char *reponame, const char *pagename,
+ const char *query);
+
+extern void cgit_tree_link(char *name, char *title, char *class, char *head,
+ char *rev, char *path);
+extern void cgit_log_link(char *name, char *title, char *class, char *head,
+ char *rev, char *path, int ofs, char *grep,
+ char *pattern);
+extern void cgit_commit_link(char *name, char *title, char *class, char *head,
+ char *rev);
+extern void cgit_refs_link(char *name, char *title, char *class, char *head,
+ char *rev, char *path);
+extern void cgit_snapshot_link(char *name, char *title, char *class,
+ char *head, char *rev, char *archivename);
+extern void cgit_diff_link(char *name, char *title, char *class, char *head,
+ char *new_rev, char *old_rev, char *path);
+extern void cgit_object_link(struct object *obj);
+
+extern void cgit_print_error(char *msg);
+extern void cgit_print_date(time_t secs, char *format);
+extern void cgit_print_age(time_t t, time_t max_relative, char *format);
+extern void cgit_print_http_headers(struct cgit_context *ctx);
+extern void cgit_print_docstart(struct cgit_context *ctx);
+extern void cgit_print_docend();
+extern void cgit_print_pageheader(struct cgit_context *ctx);
+extern void cgit_print_filemode(unsigned short mode);
+extern void cgit_print_snapshot_links(const char *repo, const char *head,
+ const char *hex, int snapshots);
+
+#endif /* UI_SHARED_H */
diff --git a/ui-snapshot.c b/ui-snapshot.c
index dfedd8f..966a140 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -1,157 +1,114 @@
/* ui-snapshot.c: generate snapshot of a commit
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
static int write_compressed_tar_archive(struct archiver_args *args,const char *filter)
{
int rw[2];
pid_t gzpid;
int stdout2;
int status;
int rv;
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");
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");
return rv;
}
static int write_tar_gzip_archive(struct archiver_args *args)
{
return write_compressed_tar_archive(args,"gzip");
}
static int write_tar_bzip2_archive(struct archiver_args *args)
{
return write_compressed_tar_archive(args,"bzip2");
}
-static const struct snapshot_archive_t {
- const char *suffix;
- const char *mimetype;
- write_archive_fn_t write_func;
- int bit;
-} snapshot_archives[] = {
+const struct cgit_snapshot_format cgit_snapshot_formats[] = {
{ ".zip", "application/x-zip", write_zip_archive, 0x1 },
{ ".tar.gz", "application/x-tar", write_tar_gzip_archive, 0x2 },
{ ".tar.bz2", "application/x-tar", write_tar_bzip2_archive, 0x4 },
- { ".tar", "application/x-tar", write_tar_archive, 0x8 }
+ { ".tar", "application/x-tar", write_tar_archive, 0x8 },
+ {}
};
-#define snapshot_archives_len (sizeof(snapshot_archives) / sizeof(*snapshot_archives))
-
-void cgit_print_snapshot(struct cacheitem *item, const char *head,
+static int make_snapshot(const struct cgit_snapshot_format *format,
const char *hex, const char *prefix,
- const char *filename, int snapshots)
+ const char *filename)
{
- const struct snapshot_archive_t* sat;
struct archiver_args args;
struct commit *commit;
unsigned char sha1[20];
- int f, sl, fnl = strlen(filename);
- for(f=0; f<snapshot_archives_len; f++) {
- sat = &snapshot_archives[f];
- if(!(snapshots & sat->bit))
- continue;
- sl = strlen(sat->suffix);
- if(fnl<sl || strcmp(&filename[fnl-sl],sat->suffix))
- continue;
- if (!hex)
- hex = head;
- if(get_sha1(hex, sha1)) {
- cgit_print_error(fmt("Bad object id: %s", hex));
- return;
- }
- commit = lookup_commit_reference(sha1);
- if(!commit) {
- cgit_print_error(fmt("Not a commit reference: %s", hex));
- return;;
- }
- memset(&args,0,sizeof(args));
- args.base = fmt("%s/", prefix);
- args.tree = commit->tree;
- args.time = commit->date;
- cgit_print_snapshot_start(sat->mimetype, filename, item);
- (*sat->write_func)(&args);
- return;
+ if(get_sha1(hex, sha1)) {
+ cgit_print_error(fmt("Bad object id: %s", hex));
+ return 1;
}
- cgit_print_error(fmt("Unsupported snapshot format: %s", filename));
-}
-
-void cgit_print_snapshot_links(const char *repo, const char *head,
- const char *hex, int snapshots)
-{
- const struct snapshot_archive_t* sat;
- char *filename;
- int f;
-
- for(f=0; f<snapshot_archives_len; f++) {
- sat = &snapshot_archives[f];
- if(!(snapshots & sat->bit))
- continue;
- filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
- sat->suffix);
- cgit_snapshot_link(filename, NULL, NULL, (char *)head,
- (char *)hex, filename);
- html("<br/>");
+ commit = lookup_commit_reference(sha1);
+ if(!commit) {
+ cgit_print_error(fmt("Not a commit reference: %s", hex));
+ return 1;
}
+ memset(&args, 0, sizeof(args));
+ args.base = fmt("%s/", prefix);
+ args.tree = commit->tree;
+ args.time = commit->date;
+ ctx.page.mimetype = xstrdup(format->mimetype);
+ ctx.page.filename = xstrdup(filename);
+ cgit_print_http_headers(&ctx);
+ format->write_func(&args);
+ return 0;
}
-int cgit_parse_snapshots_mask(const char *str)
+void cgit_print_snapshot(const char *head, const char *hex, const char *prefix,
+ const char *filename, int snapshots)
{
- const struct snapshot_archive_t* sat;
- static const char *delim = " \t,:/|;";
- int f, tl, sl, rv = 0;
-
- /* favor legacy setting */
- if(atoi(str))
- return 1;
- for(;;) {
- str += strspn(str,delim);
- tl = strcspn(str,delim);
- if(!tl)
- break;
- for(f=0; f<snapshot_archives_len; f++) {
- sat = &snapshot_archives[f];
- sl = strlen(sat->suffix);
- if((tl == sl && !strncmp(sat->suffix, str, tl)) ||
- (tl == sl-1 && !strncmp(sat->suffix+1, str, tl-1))) {
- rv |= sat->bit;
- break;
- }
- }
- str += tl;
+ const struct cgit_snapshot_format* f;
+ int sl, fnl;
+
+ fnl = strlen(filename);
+ if (!hex)
+ hex = head;
+ for (f = cgit_snapshot_formats; f->suffix; f++) {
+ if (!(snapshots & f->bit))
+ continue;
+ sl = strlen(f->suffix);
+ if(fnl < sl || strcmp(&filename[fnl-sl], f->suffix))
+ continue;
+ make_snapshot(f, hex, prefix, filename);
+ return;
}
- return rv;
+ cgit_print_error(fmt("Unsupported snapshot format: %s", filename));
}
-
-/* vim:set sw=8: */
diff --git a/ui-snapshot.h b/ui-snapshot.h
new file mode 100644
index 0000000..6e03761
--- a/dev/null
+++ b/ui-snapshot.h
@@ -0,0 +1,8 @@
+#ifndef UI_SNAPSHOT_H
+#define UI_SNAPSHOT_H
+
+extern void cgit_print_snapshot(const char *head, const char *hex,
+ const char *prefix, const char *filename,
+ int snapshot);
+
+#endif /* UI_SNAPSHOT_H */
diff --git a/ui-summary.c b/ui-summary.c
index b96414e..0b66b52 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -1,200 +1,31 @@
/* ui-summary.c: functions for generating repo summary page
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
-
-static int header;
-
-static int cmp_age(int age1, int age2)
-{
- if (age1 != 0 && age2 != 0)
- return age2 - age1;
-
- if (age1 == 0 && age2 == 0)
- return 0;
-
- if (age1 == 0)
- return +1;
-
- return -1;
-}
-
-static int cmp_ref_name(const void *a, const void *b)
-{
- struct refinfo *r1 = *(struct refinfo **)a;
- struct refinfo *r2 = *(struct refinfo **)b;
-
- return strcmp(r1->refname, r2->refname);
-}
-
-static int cmp_branch_age(const void *a, const void *b)
-{
- struct refinfo *r1 = *(struct refinfo **)a;
- struct refinfo *r2 = *(struct refinfo **)b;
-
- return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
-}
-
-static int cmp_tag_age(const void *a, const void *b)
-{
- struct refinfo *r1 = *(struct refinfo **)a;
- struct refinfo *r2 = *(struct refinfo **)b;
-
- return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
-}
-
-static int print_branch(struct refinfo *ref)
-{
- struct commitinfo *info = ref->commit;
- char *name = (char *)ref->refname;
-
- if (!info)
- return 1;
- html("<tr><td>");
- cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL);
- html("</td><td>");
-
- if (ref->object->type == OBJ_COMMIT) {
- cgit_print_age(info->commit->date, -1, NULL);
- html("</td><td>");
- html_txt(info->author);
- html("</td><td>");
- cgit_commit_link(info->subject, NULL, NULL, name, NULL);
- } else {
- html("</td><td></td><td>");
- cgit_object_link(ref->object);
- }
- html("</td></tr>\n");
- return 0;
-}
-
-static void print_tag_header()
-{
- html("<tr class='nohover'><th class='left'>Tag</th>"
- "<th class='left'>Age</th>"
- "<th class='left'>Author</th>"
- "<th class='left'>Reference</th></tr>\n");
- header = 1;
-}
-
-static int print_tag(struct refinfo *ref)
-{
- struct tag *tag;
- struct taginfo *info;
- char *url, *name = (char *)ref->refname;
-
- if (ref->object->type == OBJ_TAG) {
- tag = (struct tag *)ref->object;
- info = ref->tag;
- if (!tag || !info)
- return 1;
- html("<tr><td>");
- url = cgit_pageurl(cgit_query_repo, "tag",
- fmt("id=%s", name));
- html_link_open(url, NULL, NULL);
- html_txt(name);
- html_link_close();
- html("</td><td>");
- if (info->tagger_date > 0)
- cgit_print_age(info->tagger_date, -1, NULL);
- html("</td><td>");
- if (info->tagger)
- html(info->tagger);
- html("</td><td>");
- cgit_object_link(tag->tagged);
- html("</td></tr>\n");
- } else {
- if (!header)
- print_tag_header();
- html("<tr><td>");
- html_txt(name);
- html("</td><td colspan='2'/><td>");
- cgit_object_link(ref->object);
- html("</td></tr>\n");
- }
- return 0;
-}
-
-static void print_refs_link(char *path)
-{
- html("<tr class='nohover'><td colspan='4'>");
- cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path);
- html("</td></tr>");
-}
-
-void cgit_print_branches(int maxcount)
-{
- struct reflist list;
- int i;
-
- html("<tr class='nohover'><th class='left'>Branch</th>"
- "<th class='left'>Idle</th>"
- "<th class='left'>Author</th>"
- "<th class='left'>Head commit</th></tr>\n");
-
- list.refs = NULL;
- list.alloc = list.count = 0;
- for_each_branch_ref(cgit_refs_cb, &list);
-
- if (maxcount == 0 || maxcount > list.count)
- maxcount = list.count;
-
- if (maxcount < list.count) {
- qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
- qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
- }
-
- for(i=0; i<maxcount; i++)
- print_branch(list.refs[i]);
-
- if (maxcount < list.count)
- print_refs_link("heads");
-}
-
-void cgit_print_tags(int maxcount)
-{
- struct reflist list;
- int i;
-
- header = 0;
- list.refs = NULL;
- list.alloc = list.count = 0;
- for_each_tag_ref(cgit_refs_cb, &list);
- if (list.count == 0)
- return;
- qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
- if (!maxcount)
- maxcount = list.count;
- else if (maxcount > list.count)
- maxcount = list.count;
- print_tag_header();
- for(i=0; i<maxcount; i++)
- print_tag(list.refs[i]);
-
- if (maxcount < list.count)
- print_refs_link("tags");
-}
+#include "html.h"
+#include "ui-log.h"
+#include "ui-refs.h"
void cgit_print_summary()
{
- if (cgit_repo->readme) {
+ if (ctx.repo->readme) {
html("<div id='summary'>");
- html_include(cgit_repo->readme);
+ html_include(ctx.repo->readme);
html("</div>");
}
- if (cgit_summary_log > 0)
- cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL,
+ if (ctx.cfg.summary_log > 0)
+ cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
NULL, NULL, 0);
html("<table summary='repository info' class='list nowrap'>");
- if (cgit_summary_log > 0)
+ if (ctx.cfg.summary_log > 0)
html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
- cgit_print_branches(cgit_summary_branches);
+ cgit_print_branches(ctx.cfg.summary_branches);
html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
- cgit_print_tags(cgit_summary_tags);
+ cgit_print_tags(ctx.cfg.summary_tags);
html("</table>");
}
diff --git a/ui-summary.h b/ui-summary.h
new file mode 100644
index 0000000..37aedd2
--- a/dev/null
+++ b/ui-summary.h
@@ -0,0 +1,6 @@
+#ifndef UI_SUMMARY_H
+#define UI_SUMMARY_H
+
+extern void cgit_print_summary();
+
+#endif /* UI_SUMMARY_H */
diff --git a/ui-tag.c b/ui-tag.c
index 6d761f3..ab2c66d 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -1,42 +1,43 @@
/* ui-tag.c: display a tag
*
* Copyright (C) 2007 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
-
+#include "html.h"
+#include "ui-shared.h"
static void print_tag_content(char *buf)
{
char *p;
if (!buf)
return;
html("<div class='commit-subject'>");
p = strchr(buf, '\n');
if (p)
*p = '\0';
html_txt(buf);
html("</div>");
if (p) {
html("<div class='commit-msg'>");
html_txt(++p);
html("</div>");
}
}
void cgit_print_tag(char *revname)
{
unsigned char sha1[20];
struct object *obj;
struct tag *tag;
struct taginfo *info;
if (get_sha1(revname, sha1)) {
cgit_print_error(fmt("Bad tag reference: %s", revname));
return;
}
diff --git a/ui-tag.h b/ui-tag.h
new file mode 100644
index 0000000..d295cdc
--- a/dev/null
+++ b/ui-tag.h
@@ -0,0 +1,6 @@
+#ifndef UI_TAG_H
+#define UI_TAG_H
+
+extern void cgit_print_tag(char *revname);
+
+#endif /* UI_TAG_H */
diff --git a/ui-tree.c b/ui-tree.c
index c138877..9be3140 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -1,216 +1,218 @@
/* ui-tree.c: functions for tree output
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
char *curr_rev;
char *match_path;
int header = 0;
static void print_object(const unsigned char *sha1, char *path)
{
enum object_type type;
char *buf;
unsigned long size, lineno, start, idx;
const char *linefmt = "<tr><td class='no'><a id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a></td><td class='txt'>";
type = sha1_object_info(sha1, &size);
if (type == OBJ_BAD) {
cgit_print_error(fmt("Bad object name: %s",
sha1_to_hex(sha1)));
return;
}
buf = read_sha1_file(sha1, &type, &size);
if (!buf) {
cgit_print_error(fmt("Error reading object %s",
sha1_to_hex(sha1)));
return;
}
html(" blob: <a href='");
- html_attr(cgit_pageurl(cgit_query_repo, "blob", fmt("id=%s", sha1_to_hex(sha1))));
+ html_attr(cgit_pageurl(ctx.qry.repo, "blob", fmt("id=%s", sha1_to_hex(sha1))));
htmlf("'>%s</a>",sha1_to_hex(sha1));
html("<table summary='blob content' class='blob'>\n");
idx = 0;
start = 0;
lineno = 0;
while(idx < size) {
if (buf[idx] == '\n') {
buf[idx] = '\0';
htmlf(linefmt, ++lineno);
html_txt(buf + start);
html("</td></tr>\n");
start = idx + 1;
}
idx++;
}
htmlf(linefmt, ++lineno);
html_txt(buf + start);
html("</td></tr>\n");
html("</table>\n");
}
static int ls_item(const unsigned char *sha1, const char *base, int baselen,
const char *pathname, unsigned int mode, int stage)
{
char *name;
char *fullpath;
enum object_type type;
unsigned long size = 0;
name = xstrdup(pathname);
- fullpath = fmt("%s%s%s", cgit_query_path ? cgit_query_path : "",
- cgit_query_path ? "/" : "", name);
+ fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
+ ctx.qry.path ? "/" : "", name);
type = sha1_object_info(sha1, &size);
if (type == OBJ_BAD && !S_ISGITLINK(mode)) {
htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
name,
sha1_to_hex(sha1));
return 0;
}
html("<tr><td class='ls-mode'>");
- html_filemode(mode);
+ cgit_print_filemode(mode);
html("</td><td>");
if (S_ISGITLINK(mode)) {
htmlf("<a class='ls-mod' href='");
- html_attr(fmt(cgit_repo->module_link,
+ html_attr(fmt(ctx.repo->module_link,
name,
sha1_to_hex(sha1)));
html("'>");
html_txt(name);
html("</a>");
} else if (S_ISDIR(mode)) {
- cgit_tree_link(name, NULL, "ls-dir", cgit_query_head,
+ cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
curr_rev, fullpath);
} else {
- cgit_tree_link(name, NULL, "ls-blob", cgit_query_head,
+ cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head,
curr_rev, fullpath);
}
htmlf("</td><td class='ls-size'>%li</td>", size);
html("<td>");
- cgit_log_link("log", NULL, "button", cgit_query_head, curr_rev,
+ cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev,
fullpath, 0, NULL, NULL);
html("</td></tr>\n");
free(name);
return 0;
}
static void ls_head()
{
html("<table summary='tree listing' class='list'>\n");
html("<tr class='nohover'>");
html("<th class='left'>Mode</th>");
html("<th class='left'>Name</th>");
html("<th class='right'>Size</th>");
html("<th/>");
html("</tr>\n");
header = 1;
}
static void ls_tail()
{
if (!header)
return;
html("</table>\n");
header = 0;
}
static void ls_tree(const unsigned char *sha1, char *path)
{
struct tree *tree;
tree = parse_tree_indirect(sha1);
if (!tree) {
cgit_print_error(fmt("Not a tree object: %s",
sha1_to_hex(sha1)));
return;
}
ls_head();
read_tree_recursive(tree, "", 0, 1, NULL, ls_item);
ls_tail();
}
static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
const char *pathname, unsigned mode, int stage)
{
static int state;
static char buffer[PATH_MAX];
char *url;
if (state == 0) {
memcpy(buffer, base, baselen);
strcpy(buffer+baselen, pathname);
- url = cgit_pageurl(cgit_query_repo, "tree",
+ url = cgit_pageurl(ctx.qry.repo, "tree",
fmt("h=%s&amp;path=%s", curr_rev, buffer));
html("/");
- cgit_tree_link(xstrdup(pathname), NULL, NULL, cgit_query_head,
+ cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head,
curr_rev, buffer);
if (strcmp(match_path, buffer))
return READ_TREE_RECURSIVE;
if (S_ISDIR(mode)) {
state = 1;
ls_head();
return READ_TREE_RECURSIVE;
} else {
print_object(sha1, buffer);
return 0;
}
}
ls_item(sha1, base, baselen, pathname, mode, stage);
return 0;
}
/*
* Show a tree or a blob
* rev: the commit pointing at the root tree object
* path: path to tree or blob
*/
void cgit_print_tree(const char *rev, char *path)
{
unsigned char sha1[20];
struct commit *commit;
const char *paths[] = {path, NULL};
if (!rev)
- rev = cgit_query_head;
+ rev = ctx.qry.head;
curr_rev = xstrdup(rev);
if (get_sha1(rev, sha1)) {
cgit_print_error(fmt("Invalid revision name: %s", rev));
return;
}
commit = lookup_commit_reference(sha1);
if (!commit || parse_commit(commit)) {
cgit_print_error(fmt("Invalid commit reference: %s", rev));
return;
}
html("path: <a href='");
- html_attr(cgit_pageurl(cgit_query_repo, "tree", fmt("h=%s", rev)));
+ html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev)));
html("'>root</a>");
if (path == NULL) {
ls_tree(commit->tree->object.sha1, NULL);
return;
}
match_path = path;
read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree);
ls_tail();
}
diff --git a/ui-tree.h b/ui-tree.h
new file mode 100644
index 0000000..bbd34e3
--- a/dev/null
+++ b/ui-tree.h
@@ -0,0 +1,6 @@
+#ifndef UI_TREE_H
+#define UI_TREE_H
+
+extern void cgit_print_tree(const char *rev, char *path);
+
+#endif /* UI_TREE_H */