summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c3
-rw-r--r--cgit.css6
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt4
m---------git0
-rw-r--r--ui-commit.c2
-rw-r--r--ui-log.c2
-rw-r--r--ui-plain.c3
-rw-r--r--ui-tree.c35
10 files changed, 37 insertions, 21 deletions
diff --git a/Makefile b/Makefile
index 1f9893a..dc9dffd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,171 +1,171 @@
1CGIT_VERSION = v0.8.2.1 1CGIT_VERSION = v0.8.2.1
2CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit 3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH) 4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
5CGIT_CONFIG = /etc/cgitrc 5CGIT_CONFIG = /etc/cgitrc
6CACHE_ROOT = /var/cache/cgit 6CACHE_ROOT = /var/cache/cgit
7SHA1_HEADER = <openssl/sha.h> 7SHA1_HEADER = <openssl/sha.h>
8GIT_VER = 1.6.3.4 8GIT_VER = 1.6.4.3
9GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 9GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
10INSTALL = install 10INSTALL = install
11 11
12# Define NO_STRCASESTR if you don't have strcasestr. 12# Define NO_STRCASESTR if you don't have strcasestr.
13# 13#
14# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin). 14# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin).
15# 15#
16 16
17#-include config.mak 17#-include config.mak
18 18
19# 19#
20# Platform specific tweaks 20# Platform specific tweaks
21# 21#
22 22
23uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 23uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
24uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') 24uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
25uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') 25uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
26 26
27ifeq ($(uname_O),Cygwin) 27ifeq ($(uname_O),Cygwin)
28 NO_STRCASESTR = YesPlease 28 NO_STRCASESTR = YesPlease
29 NEEDS_LIBICONV = YesPlease 29 NEEDS_LIBICONV = YesPlease
30endif 30endif
31 31
32# 32#
33# Let the user override the above settings. 33# Let the user override the above settings.
34# 34#
35-include cgit.conf 35-include cgit.conf
36 36
37# 37#
38# Define a way to invoke make in subdirs quietly, shamelessly ripped 38# Define a way to invoke make in subdirs quietly, shamelessly ripped
39# from git.git 39# from git.git
40# 40#
41QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir 41QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
42QUIET_SUBDIR1 = 42QUIET_SUBDIR1 =
43 43
44ifneq ($(findstring $(MAKEFLAGS),w),w) 44ifneq ($(findstring $(MAKEFLAGS),w),w)
45PRINT_DIR = --no-print-directory 45PRINT_DIR = --no-print-directory
46else # "make -w" 46else # "make -w"
47NO_SUBDIR = : 47NO_SUBDIR = :
48endif 48endif
49 49
50ifndef V 50ifndef V
51 QUIET_CC = @echo ' ' CC $@; 51 QUIET_CC = @echo ' ' CC $@;
52 QUIET_MM = @echo ' ' MM $@; 52 QUIET_MM = @echo ' ' MM $@;
53 QUIET_SUBDIR0 = +@subdir= 53 QUIET_SUBDIR0 = +@subdir=
54 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ 54 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
55 $(MAKE) $(PRINT_DIR) -C $$subdir 55 $(MAKE) $(PRINT_DIR) -C $$subdir
56endif 56endif
57 57
58# 58#
59# Define a pattern rule for automatic dependency building 59# Define a pattern rule for automatic dependency building
60# 60#
61%.d: %.c 61%.d: %.c
62 $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@ 62 $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@
63 63
64# 64#
65# Define a pattern rule for silent object building 65# Define a pattern rule for silent object building
66# 66#
67%.o: %.c 67%.o: %.c
68 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $< 68 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
69 69
70 70
71EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 71EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
72OBJECTS = 72OBJECTS =
73OBJECTS += cache.o 73OBJECTS += cache.o
74OBJECTS += cgit.o 74OBJECTS += cgit.o
75OBJECTS += cmd.o 75OBJECTS += cmd.o
76OBJECTS += configfile.o 76OBJECTS += configfile.o
77OBJECTS += html.o 77OBJECTS += html.o
78OBJECTS += parsing.o 78OBJECTS += parsing.o
79OBJECTS += scan-tree.o 79OBJECTS += scan-tree.o
80OBJECTS += shared.o 80OBJECTS += shared.o
81OBJECTS += ui-atom.o 81OBJECTS += ui-atom.o
82OBJECTS += ui-blob.o 82OBJECTS += ui-blob.o
83OBJECTS += ui-clone.o 83OBJECTS += ui-clone.o
84OBJECTS += ui-commit.o 84OBJECTS += ui-commit.o
85OBJECTS += ui-diff.o 85OBJECTS += ui-diff.o
86OBJECTS += ui-log.o 86OBJECTS += ui-log.o
87OBJECTS += ui-patch.o 87OBJECTS += ui-patch.o
88OBJECTS += ui-plain.o 88OBJECTS += ui-plain.o
89OBJECTS += ui-refs.o 89OBJECTS += ui-refs.o
90OBJECTS += ui-repolist.o 90OBJECTS += ui-repolist.o
91OBJECTS += ui-shared.o 91OBJECTS += ui-shared.o
92OBJECTS += ui-snapshot.o 92OBJECTS += ui-snapshot.o
93OBJECTS += ui-stats.o 93OBJECTS += ui-stats.o
94OBJECTS += ui-summary.o 94OBJECTS += ui-summary.o
95OBJECTS += ui-tag.o 95OBJECTS += ui-tag.o
96OBJECTS += ui-tree.o 96OBJECTS += ui-tree.o
97 97
98ifdef NEEDS_LIBICONV 98ifdef NEEDS_LIBICONV
99 EXTLIBS += -liconv 99 EXTLIBS += -liconv
100endif 100endif
101 101
102 102
103.PHONY: all libgit test install uninstall clean force-version get-git \ 103.PHONY: all libgit test install uninstall clean force-version get-git \
104 doc man-doc html-doc clean-doc 104 doc man-doc html-doc clean-doc
105 105
106all: cgit 106all: cgit
107 107
108VERSION: force-version 108VERSION: force-version
109 @./gen-version.sh "$(CGIT_VERSION)" 109 @./gen-version.sh "$(CGIT_VERSION)"
110-include VERSION 110-include VERSION
111 111
112 112
113CFLAGS += -g -Wall -Igit 113CFLAGS += -g -Wall -Igit
114CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 114CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
115CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 115CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
116CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 116CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
117CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 117CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
118CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 118CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
119 119
120ifdef NO_ICONV 120ifdef NO_ICONV
121 CFLAGS += -DNO_ICONV 121 CFLAGS += -DNO_ICONV
122endif 122endif
123ifdef NO_STRCASESTR 123ifdef NO_STRCASESTR
124 CFLAGS += -DNO_STRCASESTR 124 CFLAGS += -DNO_STRCASESTR
125endif 125endif
126 126
127cgit: $(OBJECTS) libgit 127cgit: $(OBJECTS) libgit
128 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS) 128 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
129 129
130cgit.o: VERSION 130cgit.o: VERSION
131 131
132-include $(OBJECTS:.o=.d) 132-include $(OBJECTS:.o=.d)
133 133
134libgit: 134libgit:
135 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 libgit.a 135 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 libgit.a
136 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 xdiff/lib.a 136 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 xdiff/lib.a
137 137
138test: all 138test: all
139 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all 139 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
140 140
141install: all 141install: all
142 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH) 142 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH)
143 $(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 143 $(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
144 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH) 144 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
145 $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css 145 $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
146 $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png 146 $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
147 147
148uninstall: 148uninstall:
149 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 149 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
150 rm -f $(CGIT_DATA_PATH)/cgit.css 150 rm -f $(CGIT_DATA_PATH)/cgit.css
151 rm -f $(CGIT_DATA_PATH)/cgit.png 151 rm -f $(CGIT_DATA_PATH)/cgit.png
152 152
153doc: man-doc html-doc pdf-doc 153doc: man-doc html-doc pdf-doc
154 154
155man-doc: cgitrc.5.txt 155man-doc: cgitrc.5.txt
156 a2x -f manpage cgitrc.5.txt 156 a2x -f manpage cgitrc.5.txt
157 157
158html-doc: cgitrc.5.txt 158html-doc: cgitrc.5.txt
159 a2x -f xhtml --stylesheet=cgit-doc.css cgitrc.5.txt 159 a2x -f xhtml --stylesheet=cgit-doc.css cgitrc.5.txt
160 160
161pdf-doc: cgitrc.5.txt 161pdf-doc: cgitrc.5.txt
162 a2x -f pdf cgitrc.5.txt 162 a2x -f pdf cgitrc.5.txt
163 163
164clean: clean-doc 164clean: clean-doc
165 rm -f cgit VERSION *.o *.d 165 rm -f cgit VERSION *.o *.d
166 166
167clean-doc: 167clean-doc:
168 rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo 168 rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
169 169
170get-git: 170get-git:
171 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git 171 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 3fcca2a..bd37788 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,454 +1,457 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cache.h" 10#include "cache.h"
11#include "cmd.h" 11#include "cmd.h"
12#include "configfile.h" 12#include "configfile.h"
13#include "html.h" 13#include "html.h"
14#include "ui-shared.h" 14#include "ui-shared.h"
15#include "ui-stats.h" 15#include "ui-stats.h"
16#include "scan-tree.h" 16#include "scan-tree.h"
17 17
18const char *cgit_version = CGIT_VERSION; 18const char *cgit_version = CGIT_VERSION;
19 19
20void add_mimetype(const char *name, const char *value) 20void add_mimetype(const char *name, const char *value)
21{ 21{
22 struct string_list_item *item; 22 struct string_list_item *item;
23 23
24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes); 24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);
25 item->util = xstrdup(value); 25 item->util = xstrdup(value);
26} 26}
27 27
28struct cgit_filter *new_filter(const char *cmd, int extra_args) 28struct cgit_filter *new_filter(const char *cmd, int extra_args)
29{ 29{
30 struct cgit_filter *f; 30 struct cgit_filter *f;
31 31
32 if (!cmd || !cmd[0]) 32 if (!cmd || !cmd[0])
33 return NULL; 33 return NULL;
34 34
35 f = xmalloc(sizeof(struct cgit_filter)); 35 f = xmalloc(sizeof(struct cgit_filter));
36 f->cmd = xstrdup(cmd); 36 f->cmd = xstrdup(cmd);
37 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 37 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
38 f->argv[0] = f->cmd; 38 f->argv[0] = f->cmd;
39 f->argv[1] = NULL; 39 f->argv[1] = NULL;
40 return f; 40 return f;
41} 41}
42 42
43static void process_cached_repolist(const char *path); 43static void process_cached_repolist(const char *path);
44 44
45void repo_config(struct cgit_repo *repo, const char *name, const char *value) 45void repo_config(struct cgit_repo *repo, const char *name, const char *value)
46{ 46{
47 if (!strcmp(name, "name")) 47 if (!strcmp(name, "name"))
48 repo->name = xstrdup(value); 48 repo->name = xstrdup(value);
49 else if (!strcmp(name, "clone-url")) 49 else if (!strcmp(name, "clone-url"))
50 repo->clone_url = xstrdup(value); 50 repo->clone_url = xstrdup(value);
51 else if (!strcmp(name, "desc")) 51 else if (!strcmp(name, "desc"))
52 repo->desc = xstrdup(value); 52 repo->desc = xstrdup(value);
53 else if (!strcmp(name, "owner")) 53 else if (!strcmp(name, "owner"))
54 repo->owner = xstrdup(value); 54 repo->owner = xstrdup(value);
55 else if (!strcmp(name, "defbranch")) 55 else if (!strcmp(name, "defbranch"))
56 repo->defbranch = xstrdup(value); 56 repo->defbranch = xstrdup(value);
57 else if (!strcmp(name, "snapshots")) 57 else if (!strcmp(name, "snapshots"))
58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
59 else if (!strcmp(name, "enable-log-filecount")) 59 else if (!strcmp(name, "enable-log-filecount"))
60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
61 else if (!strcmp(name, "enable-log-linecount")) 61 else if (!strcmp(name, "enable-log-linecount"))
62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
63 else if (!strcmp(name, "max-stats")) 63 else if (!strcmp(name, "max-stats"))
64 repo->max_stats = cgit_find_stats_period(value, NULL); 64 repo->max_stats = cgit_find_stats_period(value, NULL);
65 else if (!strcmp(name, "module-link")) 65 else if (!strcmp(name, "module-link"))
66 repo->module_link= xstrdup(value); 66 repo->module_link= xstrdup(value);
67 else if (!strcmp(name, "section")) 67 else if (!strcmp(name, "section"))
68 repo->section = xstrdup(value); 68 repo->section = xstrdup(value);
69 else if (!strcmp(name, "readme") && value != NULL) { 69 else if (!strcmp(name, "readme") && value != NULL) {
70 if (*value == '/') 70 if (*value == '/')
71 ctx.repo->readme = xstrdup(value); 71 ctx.repo->readme = xstrdup(value);
72 else 72 else
73 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 73 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
74 } else if (ctx.cfg.enable_filter_overrides) { 74 } else if (ctx.cfg.enable_filter_overrides) {
75 if (!strcmp(name, "about-filter")) 75 if (!strcmp(name, "about-filter"))
76 repo->about_filter = new_filter(value, 0); 76 repo->about_filter = new_filter(value, 0);
77 else if (!strcmp(name, "commit-filter")) 77 else if (!strcmp(name, "commit-filter"))
78 repo->commit_filter = new_filter(value, 0); 78 repo->commit_filter = new_filter(value, 0);
79 else if (!strcmp(name, "source-filter")) 79 else if (!strcmp(name, "source-filter"))
80 repo->source_filter = new_filter(value, 1); 80 repo->source_filter = new_filter(value, 1);
81 } 81 }
82} 82}
83 83
84void config_cb(const char *name, const char *value) 84void config_cb(const char *name, const char *value)
85{ 85{
86 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 86 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
87 ctx.cfg.section = xstrdup(value); 87 ctx.cfg.section = xstrdup(value);
88 else if (!strcmp(name, "repo.url")) 88 else if (!strcmp(name, "repo.url"))
89 ctx.repo = cgit_add_repo(value); 89 ctx.repo = cgit_add_repo(value);
90 else if (ctx.repo && !strcmp(name, "repo.path")) 90 else if (ctx.repo && !strcmp(name, "repo.path"))
91 ctx.repo->path = trim_end(value, '/'); 91 ctx.repo->path = trim_end(value, '/');
92 else if (ctx.repo && !prefixcmp(name, "repo.")) 92 else if (ctx.repo && !prefixcmp(name, "repo."))
93 repo_config(ctx.repo, name + 5, value); 93 repo_config(ctx.repo, name + 5, value);
94 else if (!strcmp(name, "root-title")) 94 else if (!strcmp(name, "root-title"))
95 ctx.cfg.root_title = xstrdup(value); 95 ctx.cfg.root_title = xstrdup(value);
96 else if (!strcmp(name, "root-desc")) 96 else if (!strcmp(name, "root-desc"))
97 ctx.cfg.root_desc = xstrdup(value); 97 ctx.cfg.root_desc = xstrdup(value);
98 else if (!strcmp(name, "root-readme")) 98 else if (!strcmp(name, "root-readme"))
99 ctx.cfg.root_readme = xstrdup(value); 99 ctx.cfg.root_readme = xstrdup(value);
100 else if (!strcmp(name, "css")) 100 else if (!strcmp(name, "css"))
101 ctx.cfg.css = xstrdup(value); 101 ctx.cfg.css = xstrdup(value);
102 else if (!strcmp(name, "favicon")) 102 else if (!strcmp(name, "favicon"))
103 ctx.cfg.favicon = xstrdup(value); 103 ctx.cfg.favicon = xstrdup(value);
104 else if (!strcmp(name, "footer")) 104 else if (!strcmp(name, "footer"))
105 ctx.cfg.footer = xstrdup(value); 105 ctx.cfg.footer = xstrdup(value);
106 else if (!strcmp(name, "head-include")) 106 else if (!strcmp(name, "head-include"))
107 ctx.cfg.head_include = xstrdup(value); 107 ctx.cfg.head_include = xstrdup(value);
108 else if (!strcmp(name, "header")) 108 else if (!strcmp(name, "header"))
109 ctx.cfg.header = xstrdup(value); 109 ctx.cfg.header = xstrdup(value);
110 else if (!strcmp(name, "logo")) 110 else if (!strcmp(name, "logo"))
111 ctx.cfg.logo = xstrdup(value); 111 ctx.cfg.logo = xstrdup(value);
112 else if (!strcmp(name, "index-header")) 112 else if (!strcmp(name, "index-header"))
113 ctx.cfg.index_header = xstrdup(value); 113 ctx.cfg.index_header = xstrdup(value);
114 else if (!strcmp(name, "index-info")) 114 else if (!strcmp(name, "index-info"))
115 ctx.cfg.index_info = xstrdup(value); 115 ctx.cfg.index_info = xstrdup(value);
116 else if (!strcmp(name, "logo-link")) 116 else if (!strcmp(name, "logo-link"))
117 ctx.cfg.logo_link = xstrdup(value); 117 ctx.cfg.logo_link = xstrdup(value);
118 else if (!strcmp(name, "module-link")) 118 else if (!strcmp(name, "module-link"))
119 ctx.cfg.module_link = xstrdup(value); 119 ctx.cfg.module_link = xstrdup(value);
120 else if (!strcmp(name, "virtual-root")) { 120 else if (!strcmp(name, "virtual-root")) {
121 ctx.cfg.virtual_root = trim_end(value, '/'); 121 ctx.cfg.virtual_root = trim_end(value, '/');
122 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 122 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
123 ctx.cfg.virtual_root = ""; 123 ctx.cfg.virtual_root = "";
124 } else if (!strcmp(name, "nocache")) 124 } else if (!strcmp(name, "nocache"))
125 ctx.cfg.nocache = atoi(value); 125 ctx.cfg.nocache = atoi(value);
126 else if (!strcmp(name, "noplainemail")) 126 else if (!strcmp(name, "noplainemail"))
127 ctx.cfg.noplainemail = atoi(value); 127 ctx.cfg.noplainemail = atoi(value);
128 else if (!strcmp(name, "noheader")) 128 else if (!strcmp(name, "noheader"))
129 ctx.cfg.noheader = atoi(value); 129 ctx.cfg.noheader = atoi(value);
130 else if (!strcmp(name, "snapshots")) 130 else if (!strcmp(name, "snapshots"))
131 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 131 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
132 else if (!strcmp(name, "enable-filter-overrides")) 132 else if (!strcmp(name, "enable-filter-overrides"))
133 ctx.cfg.enable_filter_overrides = atoi(value); 133 ctx.cfg.enable_filter_overrides = atoi(value);
134 else if (!strcmp(name, "enable-index-links")) 134 else if (!strcmp(name, "enable-index-links"))
135 ctx.cfg.enable_index_links = atoi(value); 135 ctx.cfg.enable_index_links = atoi(value);
136 else if (!strcmp(name, "enable-log-filecount")) 136 else if (!strcmp(name, "enable-log-filecount"))
137 ctx.cfg.enable_log_filecount = atoi(value); 137 ctx.cfg.enable_log_filecount = atoi(value);
138 else if (!strcmp(name, "enable-log-linecount")) 138 else if (!strcmp(name, "enable-log-linecount"))
139 ctx.cfg.enable_log_linecount = atoi(value); 139 ctx.cfg.enable_log_linecount = atoi(value);
140 else if (!strcmp(name, "enable-tree-linenumbers"))
141 ctx.cfg.enable_tree_linenumbers = atoi(value);
140 else if (!strcmp(name, "max-stats")) 142 else if (!strcmp(name, "max-stats"))
141 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 143 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
142 else if (!strcmp(name, "cache-size")) 144 else if (!strcmp(name, "cache-size"))
143 ctx.cfg.cache_size = atoi(value); 145 ctx.cfg.cache_size = atoi(value);
144 else if (!strcmp(name, "cache-root")) 146 else if (!strcmp(name, "cache-root"))
145 ctx.cfg.cache_root = xstrdup(value); 147 ctx.cfg.cache_root = xstrdup(value);
146 else if (!strcmp(name, "cache-root-ttl")) 148 else if (!strcmp(name, "cache-root-ttl"))
147 ctx.cfg.cache_root_ttl = atoi(value); 149 ctx.cfg.cache_root_ttl = atoi(value);
148 else if (!strcmp(name, "cache-repo-ttl")) 150 else if (!strcmp(name, "cache-repo-ttl"))
149 ctx.cfg.cache_repo_ttl = atoi(value); 151 ctx.cfg.cache_repo_ttl = atoi(value);
150 else if (!strcmp(name, "cache-scanrc-ttl")) 152 else if (!strcmp(name, "cache-scanrc-ttl"))
151 ctx.cfg.cache_scanrc_ttl = atoi(value); 153 ctx.cfg.cache_scanrc_ttl = atoi(value);
152 else if (!strcmp(name, "cache-static-ttl")) 154 else if (!strcmp(name, "cache-static-ttl"))
153 ctx.cfg.cache_static_ttl = atoi(value); 155 ctx.cfg.cache_static_ttl = atoi(value);
154 else if (!strcmp(name, "cache-dynamic-ttl")) 156 else if (!strcmp(name, "cache-dynamic-ttl"))
155 ctx.cfg.cache_dynamic_ttl = atoi(value); 157 ctx.cfg.cache_dynamic_ttl = atoi(value);
156 else if (!strcmp(name, "about-filter")) 158 else if (!strcmp(name, "about-filter"))
157 ctx.cfg.about_filter = new_filter(value, 0); 159 ctx.cfg.about_filter = new_filter(value, 0);
158 else if (!strcmp(name, "commit-filter")) 160 else if (!strcmp(name, "commit-filter"))
159 ctx.cfg.commit_filter = new_filter(value, 0); 161 ctx.cfg.commit_filter = new_filter(value, 0);
160 else if (!strcmp(name, "embedded")) 162 else if (!strcmp(name, "embedded"))
161 ctx.cfg.embedded = atoi(value); 163 ctx.cfg.embedded = atoi(value);
162 else if (!strcmp(name, "max-message-length")) 164 else if (!strcmp(name, "max-message-length"))
163 ctx.cfg.max_msg_len = atoi(value); 165 ctx.cfg.max_msg_len = atoi(value);
164 else if (!strcmp(name, "max-repodesc-length")) 166 else if (!strcmp(name, "max-repodesc-length"))
165 ctx.cfg.max_repodesc_len = atoi(value); 167 ctx.cfg.max_repodesc_len = atoi(value);
166 else if (!strcmp(name, "max-repo-count")) 168 else if (!strcmp(name, "max-repo-count"))
167 ctx.cfg.max_repo_count = atoi(value); 169 ctx.cfg.max_repo_count = atoi(value);
168 else if (!strcmp(name, "max-commit-count")) 170 else if (!strcmp(name, "max-commit-count"))
169 ctx.cfg.max_commit_count = atoi(value); 171 ctx.cfg.max_commit_count = atoi(value);
170 else if (!strcmp(name, "scan-path")) 172 else if (!strcmp(name, "scan-path"))
171 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 173 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
172 process_cached_repolist(value); 174 process_cached_repolist(value);
173 else 175 else
174 scan_tree(value, repo_config); 176 scan_tree(value, repo_config);
175 else if (!strcmp(name, "source-filter")) 177 else if (!strcmp(name, "source-filter"))
176 ctx.cfg.source_filter = new_filter(value, 1); 178 ctx.cfg.source_filter = new_filter(value, 1);
177 else if (!strcmp(name, "summary-log")) 179 else if (!strcmp(name, "summary-log"))
178 ctx.cfg.summary_log = atoi(value); 180 ctx.cfg.summary_log = atoi(value);
179 else if (!strcmp(name, "summary-branches")) 181 else if (!strcmp(name, "summary-branches"))
180 ctx.cfg.summary_branches = atoi(value); 182 ctx.cfg.summary_branches = atoi(value);
181 else if (!strcmp(name, "summary-tags")) 183 else if (!strcmp(name, "summary-tags"))
182 ctx.cfg.summary_tags = atoi(value); 184 ctx.cfg.summary_tags = atoi(value);
183 else if (!strcmp(name, "agefile")) 185 else if (!strcmp(name, "agefile"))
184 ctx.cfg.agefile = xstrdup(value); 186 ctx.cfg.agefile = xstrdup(value);
185 else if (!strcmp(name, "renamelimit")) 187 else if (!strcmp(name, "renamelimit"))
186 ctx.cfg.renamelimit = atoi(value); 188 ctx.cfg.renamelimit = atoi(value);
187 else if (!strcmp(name, "robots")) 189 else if (!strcmp(name, "robots"))
188 ctx.cfg.robots = xstrdup(value); 190 ctx.cfg.robots = xstrdup(value);
189 else if (!strcmp(name, "clone-prefix")) 191 else if (!strcmp(name, "clone-prefix"))
190 ctx.cfg.clone_prefix = xstrdup(value); 192 ctx.cfg.clone_prefix = xstrdup(value);
191 else if (!strcmp(name, "local-time")) 193 else if (!strcmp(name, "local-time"))
192 ctx.cfg.local_time = atoi(value); 194 ctx.cfg.local_time = atoi(value);
193 else if (!prefixcmp(name, "mimetype.")) 195 else if (!prefixcmp(name, "mimetype."))
194 add_mimetype(name + 9, value); 196 add_mimetype(name + 9, value);
195 else if (!strcmp(name, "include")) 197 else if (!strcmp(name, "include"))
196 parse_configfile(value, config_cb); 198 parse_configfile(value, config_cb);
197} 199}
198 200
199static void querystring_cb(const char *name, const char *value) 201static void querystring_cb(const char *name, const char *value)
200{ 202{
201 if (!value) 203 if (!value)
202 value = ""; 204 value = "";
203 205
204 if (!strcmp(name,"r")) { 206 if (!strcmp(name,"r")) {
205 ctx.qry.repo = xstrdup(value); 207 ctx.qry.repo = xstrdup(value);
206 ctx.repo = cgit_get_repoinfo(value); 208 ctx.repo = cgit_get_repoinfo(value);
207 } else if (!strcmp(name, "p")) { 209 } else if (!strcmp(name, "p")) {
208 ctx.qry.page = xstrdup(value); 210 ctx.qry.page = xstrdup(value);
209 } else if (!strcmp(name, "url")) { 211 } else if (!strcmp(name, "url")) {
210 ctx.qry.url = xstrdup(value); 212 ctx.qry.url = xstrdup(value);
211 cgit_parse_url(value); 213 cgit_parse_url(value);
212 } else if (!strcmp(name, "qt")) { 214 } else if (!strcmp(name, "qt")) {
213 ctx.qry.grep = xstrdup(value); 215 ctx.qry.grep = xstrdup(value);
214 } else if (!strcmp(name, "q")) { 216 } else if (!strcmp(name, "q")) {
215 ctx.qry.search = xstrdup(value); 217 ctx.qry.search = xstrdup(value);
216 } else if (!strcmp(name, "h")) { 218 } else if (!strcmp(name, "h")) {
217 ctx.qry.head = xstrdup(value); 219 ctx.qry.head = xstrdup(value);
218 ctx.qry.has_symref = 1; 220 ctx.qry.has_symref = 1;
219 } else if (!strcmp(name, "id")) { 221 } else if (!strcmp(name, "id")) {
220 ctx.qry.sha1 = xstrdup(value); 222 ctx.qry.sha1 = xstrdup(value);
221 ctx.qry.has_sha1 = 1; 223 ctx.qry.has_sha1 = 1;
222 } else if (!strcmp(name, "id2")) { 224 } else if (!strcmp(name, "id2")) {
223 ctx.qry.sha2 = xstrdup(value); 225 ctx.qry.sha2 = xstrdup(value);
224 ctx.qry.has_sha1 = 1; 226 ctx.qry.has_sha1 = 1;
225 } else if (!strcmp(name, "ofs")) { 227 } else if (!strcmp(name, "ofs")) {
226 ctx.qry.ofs = atoi(value); 228 ctx.qry.ofs = atoi(value);
227 } else if (!strcmp(name, "path")) { 229 } else if (!strcmp(name, "path")) {
228 ctx.qry.path = trim_end(value, '/'); 230 ctx.qry.path = trim_end(value, '/');
229 } else if (!strcmp(name, "name")) { 231 } else if (!strcmp(name, "name")) {
230 ctx.qry.name = xstrdup(value); 232 ctx.qry.name = xstrdup(value);
231 } else if (!strcmp(name, "mimetype")) { 233 } else if (!strcmp(name, "mimetype")) {
232 ctx.qry.mimetype = xstrdup(value); 234 ctx.qry.mimetype = xstrdup(value);
233 } else if (!strcmp(name, "s")){ 235 } else if (!strcmp(name, "s")){
234 ctx.qry.sort = xstrdup(value); 236 ctx.qry.sort = xstrdup(value);
235 } else if (!strcmp(name, "showmsg")) { 237 } else if (!strcmp(name, "showmsg")) {
236 ctx.qry.showmsg = atoi(value); 238 ctx.qry.showmsg = atoi(value);
237 } else if (!strcmp(name, "period")) { 239 } else if (!strcmp(name, "period")) {
238 ctx.qry.period = xstrdup(value); 240 ctx.qry.period = xstrdup(value);
239 } 241 }
240} 242}
241 243
242char *xstrdupn(const char *str) 244char *xstrdupn(const char *str)
243{ 245{
244 return (str ? xstrdup(str) : NULL); 246 return (str ? xstrdup(str) : NULL);
245} 247}
246 248
247static void prepare_context(struct cgit_context *ctx) 249static void prepare_context(struct cgit_context *ctx)
248{ 250{
249 memset(ctx, 0, sizeof(ctx)); 251 memset(ctx, 0, sizeof(ctx));
250 ctx->cfg.agefile = "info/web/last-modified"; 252 ctx->cfg.agefile = "info/web/last-modified";
251 ctx->cfg.nocache = 0; 253 ctx->cfg.nocache = 0;
252 ctx->cfg.cache_size = 0; 254 ctx->cfg.cache_size = 0;
253 ctx->cfg.cache_dynamic_ttl = 5; 255 ctx->cfg.cache_dynamic_ttl = 5;
254 ctx->cfg.cache_max_create_time = 5; 256 ctx->cfg.cache_max_create_time = 5;
255 ctx->cfg.cache_repo_ttl = 5; 257 ctx->cfg.cache_repo_ttl = 5;
256 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 258 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
257 ctx->cfg.cache_root_ttl = 5; 259 ctx->cfg.cache_root_ttl = 5;
258 ctx->cfg.cache_scanrc_ttl = 15; 260 ctx->cfg.cache_scanrc_ttl = 15;
259 ctx->cfg.cache_static_ttl = -1; 261 ctx->cfg.cache_static_ttl = -1;
260 ctx->cfg.css = "/cgit.css"; 262 ctx->cfg.css = "/cgit.css";
261 ctx->cfg.logo = "/cgit.png"; 263 ctx->cfg.logo = "/cgit.png";
262 ctx->cfg.local_time = 0; 264 ctx->cfg.local_time = 0;
265 ctx->cfg.enable_tree_linenumbers = 1;
263 ctx->cfg.max_repo_count = 50; 266 ctx->cfg.max_repo_count = 50;
264 ctx->cfg.max_commit_count = 50; 267 ctx->cfg.max_commit_count = 50;
265 ctx->cfg.max_lock_attempts = 5; 268 ctx->cfg.max_lock_attempts = 5;
266 ctx->cfg.max_msg_len = 80; 269 ctx->cfg.max_msg_len = 80;
267 ctx->cfg.max_repodesc_len = 80; 270 ctx->cfg.max_repodesc_len = 80;
268 ctx->cfg.max_stats = 0; 271 ctx->cfg.max_stats = 0;
269 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 272 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
270 ctx->cfg.renamelimit = -1; 273 ctx->cfg.renamelimit = -1;
271 ctx->cfg.robots = "index, nofollow"; 274 ctx->cfg.robots = "index, nofollow";
272 ctx->cfg.root_title = "Git repository browser"; 275 ctx->cfg.root_title = "Git repository browser";
273 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 276 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
274 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 277 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
275 ctx->cfg.section = ""; 278 ctx->cfg.section = "";
276 ctx->cfg.summary_branches = 10; 279 ctx->cfg.summary_branches = 10;
277 ctx->cfg.summary_log = 10; 280 ctx->cfg.summary_log = 10;
278 ctx->cfg.summary_tags = 10; 281 ctx->cfg.summary_tags = 10;
279 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 282 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
280 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 283 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
281 ctx->env.https = xstrdupn(getenv("HTTPS")); 284 ctx->env.https = xstrdupn(getenv("HTTPS"));
282 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 285 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
283 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 286 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
284 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 287 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
285 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 288 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
286 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 289 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
287 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 290 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
288 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 291 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
289 ctx->page.mimetype = "text/html"; 292 ctx->page.mimetype = "text/html";
290 ctx->page.charset = PAGE_ENCODING; 293 ctx->page.charset = PAGE_ENCODING;
291 ctx->page.filename = NULL; 294 ctx->page.filename = NULL;
292 ctx->page.size = 0; 295 ctx->page.size = 0;
293 ctx->page.modified = time(NULL); 296 ctx->page.modified = time(NULL);
294 ctx->page.expires = ctx->page.modified; 297 ctx->page.expires = ctx->page.modified;
295 ctx->page.etag = NULL; 298 ctx->page.etag = NULL;
296 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 299 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
297 if (ctx->env.script_name) 300 if (ctx->env.script_name)
298 ctx->cfg.script_name = ctx->env.script_name; 301 ctx->cfg.script_name = ctx->env.script_name;
299 if (ctx->env.query_string) 302 if (ctx->env.query_string)
300 ctx->qry.raw = ctx->env.query_string; 303 ctx->qry.raw = ctx->env.query_string;
301 if (!ctx->env.cgit_config) 304 if (!ctx->env.cgit_config)
302 ctx->env.cgit_config = CGIT_CONFIG; 305 ctx->env.cgit_config = CGIT_CONFIG;
303} 306}
304 307
305struct refmatch { 308struct refmatch {
306 char *req_ref; 309 char *req_ref;
307 char *first_ref; 310 char *first_ref;
308 int match; 311 int match;
309}; 312};
310 313
311int find_current_ref(const char *refname, const unsigned char *sha1, 314int find_current_ref(const char *refname, const unsigned char *sha1,
312 int flags, void *cb_data) 315 int flags, void *cb_data)
313{ 316{
314 struct refmatch *info; 317 struct refmatch *info;
315 318
316 info = (struct refmatch *)cb_data; 319 info = (struct refmatch *)cb_data;
317 if (!strcmp(refname, info->req_ref)) 320 if (!strcmp(refname, info->req_ref))
318 info->match = 1; 321 info->match = 1;
319 if (!info->first_ref) 322 if (!info->first_ref)
320 info->first_ref = xstrdup(refname); 323 info->first_ref = xstrdup(refname);
321 return info->match; 324 return info->match;
322} 325}
323 326
324char *find_default_branch(struct cgit_repo *repo) 327char *find_default_branch(struct cgit_repo *repo)
325{ 328{
326 struct refmatch info; 329 struct refmatch info;
327 char *ref; 330 char *ref;
328 331
329 info.req_ref = repo->defbranch; 332 info.req_ref = repo->defbranch;
330 info.first_ref = NULL; 333 info.first_ref = NULL;
331 info.match = 0; 334 info.match = 0;
332 for_each_branch_ref(find_current_ref, &info); 335 for_each_branch_ref(find_current_ref, &info);
333 if (info.match) 336 if (info.match)
334 ref = info.req_ref; 337 ref = info.req_ref;
335 else 338 else
336 ref = info.first_ref; 339 ref = info.first_ref;
337 if (ref) 340 if (ref)
338 ref = xstrdup(ref); 341 ref = xstrdup(ref);
339 return ref; 342 return ref;
340} 343}
341 344
342static int prepare_repo_cmd(struct cgit_context *ctx) 345static int prepare_repo_cmd(struct cgit_context *ctx)
343{ 346{
344 char *tmp; 347 char *tmp;
345 unsigned char sha1[20]; 348 unsigned char sha1[20];
346 int nongit = 0; 349 int nongit = 0;
347 350
348 setenv("GIT_DIR", ctx->repo->path, 1); 351 setenv("GIT_DIR", ctx->repo->path, 1);
349 setup_git_directory_gently(&nongit); 352 setup_git_directory_gently(&nongit);
350 if (nongit) { 353 if (nongit) {
351 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 354 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
352 "config error"); 355 "config error");
353 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 356 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
354 ctx->repo = NULL; 357 ctx->repo = NULL;
355 cgit_print_http_headers(ctx); 358 cgit_print_http_headers(ctx);
356 cgit_print_docstart(ctx); 359 cgit_print_docstart(ctx);
357 cgit_print_pageheader(ctx); 360 cgit_print_pageheader(ctx);
358 cgit_print_error(tmp); 361 cgit_print_error(tmp);
359 cgit_print_docend(); 362 cgit_print_docend();
360 return 1; 363 return 1;
361 } 364 }
362 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 365 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
363 366
364 if (!ctx->qry.head) { 367 if (!ctx->qry.head) {
365 ctx->qry.nohead = 1; 368 ctx->qry.nohead = 1;
366 ctx->qry.head = find_default_branch(ctx->repo); 369 ctx->qry.head = find_default_branch(ctx->repo);
367 ctx->repo->defbranch = ctx->qry.head; 370 ctx->repo->defbranch = ctx->qry.head;
368 } 371 }
369 372
370 if (!ctx->qry.head) { 373 if (!ctx->qry.head) {
371 cgit_print_http_headers(ctx); 374 cgit_print_http_headers(ctx);
372 cgit_print_docstart(ctx); 375 cgit_print_docstart(ctx);
373 cgit_print_pageheader(ctx); 376 cgit_print_pageheader(ctx);
374 cgit_print_error("Repository seems to be empty"); 377 cgit_print_error("Repository seems to be empty");
375 cgit_print_docend(); 378 cgit_print_docend();
376 return 1; 379 return 1;
377 } 380 }
378 381
379 if (get_sha1(ctx->qry.head, sha1)) { 382 if (get_sha1(ctx->qry.head, sha1)) {
380 tmp = xstrdup(ctx->qry.head); 383 tmp = xstrdup(ctx->qry.head);
381 ctx->qry.head = ctx->repo->defbranch; 384 ctx->qry.head = ctx->repo->defbranch;
382 ctx->page.status = 404; 385 ctx->page.status = 404;
383 ctx->page.statusmsg = "not found"; 386 ctx->page.statusmsg = "not found";
384 cgit_print_http_headers(ctx); 387 cgit_print_http_headers(ctx);
385 cgit_print_docstart(ctx); 388 cgit_print_docstart(ctx);
386 cgit_print_pageheader(ctx); 389 cgit_print_pageheader(ctx);
387 cgit_print_error(fmt("Invalid branch: %s", tmp)); 390 cgit_print_error(fmt("Invalid branch: %s", tmp));
388 cgit_print_docend(); 391 cgit_print_docend();
389 return 1; 392 return 1;
390 } 393 }
391 return 0; 394 return 0;
392} 395}
393 396
394static void process_request(void *cbdata) 397static void process_request(void *cbdata)
395{ 398{
396 struct cgit_context *ctx = cbdata; 399 struct cgit_context *ctx = cbdata;
397 struct cgit_cmd *cmd; 400 struct cgit_cmd *cmd;
398 401
399 cmd = cgit_get_cmd(ctx); 402 cmd = cgit_get_cmd(ctx);
400 if (!cmd) { 403 if (!cmd) {
401 ctx->page.title = "cgit error"; 404 ctx->page.title = "cgit error";
402 cgit_print_http_headers(ctx); 405 cgit_print_http_headers(ctx);
403 cgit_print_docstart(ctx); 406 cgit_print_docstart(ctx);
404 cgit_print_pageheader(ctx); 407 cgit_print_pageheader(ctx);
405 cgit_print_error("Invalid request"); 408 cgit_print_error("Invalid request");
406 cgit_print_docend(); 409 cgit_print_docend();
407 return; 410 return;
408 } 411 }
409 412
410 if (cmd->want_repo && !ctx->repo) { 413 if (cmd->want_repo && !ctx->repo) {
411 cgit_print_http_headers(ctx); 414 cgit_print_http_headers(ctx);
412 cgit_print_docstart(ctx); 415 cgit_print_docstart(ctx);
413 cgit_print_pageheader(ctx); 416 cgit_print_pageheader(ctx);
414 cgit_print_error(fmt("No repository selected")); 417 cgit_print_error(fmt("No repository selected"));
415 cgit_print_docend(); 418 cgit_print_docend();
416 return; 419 return;
417 } 420 }
418 421
419 if (ctx->repo && prepare_repo_cmd(ctx)) 422 if (ctx->repo && prepare_repo_cmd(ctx))
420 return; 423 return;
421 424
422 if (cmd->want_layout) { 425 if (cmd->want_layout) {
423 cgit_print_http_headers(ctx); 426 cgit_print_http_headers(ctx);
424 cgit_print_docstart(ctx); 427 cgit_print_docstart(ctx);
425 cgit_print_pageheader(ctx); 428 cgit_print_pageheader(ctx);
426 } 429 }
427 430
428 cmd->fn(ctx); 431 cmd->fn(ctx);
429 432
430 if (cmd->want_layout) 433 if (cmd->want_layout)
431 cgit_print_docend(); 434 cgit_print_docend();
432} 435}
433 436
434int cmp_repos(const void *a, const void *b) 437int cmp_repos(const void *a, const void *b)
435{ 438{
436 const struct cgit_repo *ra = a, *rb = b; 439 const struct cgit_repo *ra = a, *rb = b;
437 return strcmp(ra->url, rb->url); 440 return strcmp(ra->url, rb->url);
438} 441}
439 442
440char *build_snapshot_setting(int bitmap) 443char *build_snapshot_setting(int bitmap)
441{ 444{
442 const struct cgit_snapshot_format *f; 445 const struct cgit_snapshot_format *f;
443 char *result = xstrdup(""); 446 char *result = xstrdup("");
444 char *tmp; 447 char *tmp;
445 int len; 448 int len;
446 449
447 for (f = cgit_snapshot_formats; f->suffix; f++) { 450 for (f = cgit_snapshot_formats; f->suffix; f++) {
448 if (f->bit & bitmap) { 451 if (f->bit & bitmap) {
449 tmp = result; 452 tmp = result;
450 result = xstrdup(fmt("%s%s ", tmp, f->suffix)); 453 result = xstrdup(fmt("%s%s ", tmp, f->suffix));
451 free(tmp); 454 free(tmp);
452 } 455 }
453 } 456 }
454 len = strlen(result); 457 len = strlen(result);
diff --git a/cgit.css b/cgit.css
index 3c65114..c47ebc9 100644
--- a/cgit.css
+++ b/cgit.css
@@ -48,394 +48,394 @@ table#header td.form {
48 vertical-align: bottom; 48 vertical-align: bottom;
49 padding-right: 1em; 49 padding-right: 1em;
50 padding-bottom: 2px; 50 padding-bottom: 2px;
51 white-space: nowrap; 51 white-space: nowrap;
52} 52}
53 53
54table#header td.form form, 54table#header td.form form,
55table#header td.form input, 55table#header td.form input,
56table#header td.form select { 56table#header td.form select {
57 font-size: 90%; 57 font-size: 90%;
58} 58}
59 59
60table#header td.sub { 60table#header td.sub {
61 color: #777; 61 color: #777;
62 border-top: solid 1px #ccc; 62 border-top: solid 1px #ccc;
63 padding-left: 10px; 63 padding-left: 10px;
64} 64}
65 65
66table.tabs { 66table.tabs {
67 /* border-bottom: solid 2px #ccc; */ 67 /* border-bottom: solid 2px #ccc; */
68 border-collapse: collapse; 68 border-collapse: collapse;
69 margin-top: 2em; 69 margin-top: 2em;
70 margin-bottom: 0px; 70 margin-bottom: 0px;
71 width: 100%; 71 width: 100%;
72} 72}
73 73
74table.tabs td { 74table.tabs td {
75 padding: 0px 1em; 75 padding: 0px 1em;
76 vertical-align: bottom; 76 vertical-align: bottom;
77} 77}
78 78
79table.tabs td a { 79table.tabs td a {
80 padding: 2px 0.75em; 80 padding: 2px 0.75em;
81 color: #777; 81 color: #777;
82 font-size: 110%; 82 font-size: 110%;
83} 83}
84 84
85table.tabs td a.active { 85table.tabs td a.active {
86 color: #000; 86 color: #000;
87 background-color: #ccc; 87 background-color: #ccc;
88} 88}
89 89
90table.tabs td.form { 90table.tabs td.form {
91 text-align: right; 91 text-align: right;
92} 92}
93 93
94table.tabs td.form form { 94table.tabs td.form form {
95 padding-bottom: 2px; 95 padding-bottom: 2px;
96 font-size: 90%; 96 font-size: 90%;
97 white-space: nowrap; 97 white-space: nowrap;
98} 98}
99 99
100table.tabs td.form input, 100table.tabs td.form input,
101table.tabs td.form select { 101table.tabs td.form select {
102 font-size: 90%; 102 font-size: 90%;
103} 103}
104 104
105div.content { 105div.content {
106 margin: 0px; 106 margin: 0px;
107 padding: 2em; 107 padding: 2em;
108 border-top: solid 3px #ccc; 108 border-top: solid 3px #ccc;
109 border-bottom: solid 3px #ccc; 109 border-bottom: solid 3px #ccc;
110} 110}
111 111
112 112
113table.list { 113table.list {
114 width: 100%; 114 width: 100%;
115 border: none; 115 border: none;
116 border-collapse: collapse; 116 border-collapse: collapse;
117} 117}
118 118
119table.list tr { 119table.list tr {
120 background: white; 120 background: white;
121} 121}
122 122
123table.list tr.logheader { 123table.list tr.logheader {
124 background: #eee; 124 background: #eee;
125} 125}
126 126
127table.list tr:hover { 127table.list tr:hover {
128 background: #eee; 128 background: #eee;
129} 129}
130 130
131table.list tr.nohover:hover { 131table.list tr.nohover:hover {
132 background: white; 132 background: white;
133} 133}
134 134
135table.list th { 135table.list th {
136 font-weight: bold; 136 font-weight: bold;
137 /* color: #888; 137 /* color: #888;
138 border-top: dashed 1px #888; 138 border-top: dashed 1px #888;
139 border-bottom: dashed 1px #888; 139 border-bottom: dashed 1px #888;
140 */ 140 */
141 padding: 0.1em 0.5em 0.05em 0.5em; 141 padding: 0.1em 0.5em 0.05em 0.5em;
142 vertical-align: baseline; 142 vertical-align: baseline;
143} 143}
144 144
145table.list td { 145table.list td {
146 border: none; 146 border: none;
147 padding: 0.1em 0.5em 0.1em 0.5em; 147 padding: 0.1em 0.5em 0.1em 0.5em;
148} 148}
149 149
150table.list td.logsubject { 150table.list td.logsubject {
151 font-family: monospace; 151 font-family: monospace;
152 font-weight: bold; 152 font-weight: bold;
153} 153}
154 154
155table.list td.logmsg { 155table.list td.logmsg {
156 font-family: monospace; 156 font-family: monospace;
157 white-space: pre; 157 white-space: pre;
158 padding: 1em 0.5em 2em 0.5em; 158 padding: 1em 0.5em 2em 0.5em;
159} 159}
160 160
161table.list td a { 161table.list td a {
162 color: black; 162 color: black;
163} 163}
164 164
165table.list td a:hover { 165table.list td a:hover {
166 color: #00f; 166 color: #00f;
167} 167}
168 168
169img { 169img {
170 border: none; 170 border: none;
171} 171}
172 172
173input#switch-btn { 173input#switch-btn {
174 margin: 2px 0px 0px 0px; 174 margin: 2px 0px 0px 0px;
175} 175}
176 176
177td#sidebar input.txt { 177td#sidebar input.txt {
178 width: 100%; 178 width: 100%;
179 margin: 2px 0px 0px 0px; 179 margin: 2px 0px 0px 0px;
180} 180}
181 181
182table#grid { 182table#grid {
183 margin: 0px; 183 margin: 0px;
184} 184}
185 185
186td#content { 186td#content {
187 vertical-align: top; 187 vertical-align: top;
188 padding: 1em 2em 1em 1em; 188 padding: 1em 2em 1em 1em;
189 border: none; 189 border: none;
190} 190}
191 191
192div#summary { 192div#summary {
193 vertical-align: top; 193 vertical-align: top;
194 margin-bottom: 1em; 194 margin-bottom: 1em;
195} 195}
196 196
197table#downloads { 197table#downloads {
198 float: right; 198 float: right;
199 border-collapse: collapse; 199 border-collapse: collapse;
200 border: solid 1px #777; 200 border: solid 1px #777;
201 margin-left: 0.5em; 201 margin-left: 0.5em;
202 margin-bottom: 0.5em; 202 margin-bottom: 0.5em;
203} 203}
204 204
205table#downloads th { 205table#downloads th {
206 background-color: #ccc; 206 background-color: #ccc;
207} 207}
208 208
209div#blob { 209div#blob {
210 border: solid 1px black; 210 border: solid 1px black;
211} 211}
212 212
213div.error { 213div.error {
214 color: red; 214 color: red;
215 font-weight: bold; 215 font-weight: bold;
216 margin: 1em 2em; 216 margin: 1em 2em;
217} 217}
218 218
219a.ls-blob, a.ls-dir, a.ls-mod { 219a.ls-blob, a.ls-dir, a.ls-mod {
220 font-family: monospace; 220 font-family: monospace;
221} 221}
222 222
223td.ls-size { 223td.ls-size {
224 text-align: right; 224 text-align: right;
225 font-family: monospace; 225 font-family: monospace;
226 width: 10em; 226 width: 10em;
227} 227}
228 228
229td.ls-mode { 229td.ls-mode {
230 font-family: monospace; 230 font-family: monospace;
231 width: 10em; 231 width: 10em;
232} 232}
233 233
234table.blob { 234table.blob {
235 margin-top: 0.5em; 235 margin-top: 0.5em;
236 border-top: solid 1px black; 236 border-top: solid 1px black;
237} 237}
238 238
239table.blob td.lines { 239table.blob td.lines {
240 margin: 0; padding: 0; 240 margin: 0; padding: 0 0 0 0.5em;
241 vertical-align: top; 241 vertical-align: top;
242 color: black; 242 color: black;
243} 243}
244 244
245table.blob td.linenumbers { 245table.blob td.linenumbers {
246 margin: 0; padding: 0; 246 margin: 0; padding: 0 0.5em 0 0.5em;
247 vertical-align: top; 247 vertical-align: top;
248 text-align: right;
248 border-right: 1px solid gray; 249 border-right: 1px solid gray;
249 background-color: #eee;
250} 250}
251 251
252table.blob pre { 252table.blob pre {
253 padding: 0; margin: 0; 253 padding: 0; margin: 0;
254} 254}
255 255
256table.blob a.no { 256table.blob a.no {
257 color: gray; 257 color: gray;
258 text-align: right; 258 text-align: right;
259 text-decoration: none; 259 text-decoration: none;
260} 260}
261 261
262table.blob a.no a:hover { 262table.blob a.no a:hover {
263 color: black; 263 color: black;
264} 264}
265 265
266table.bin-blob { 266table.bin-blob {
267 margin-top: 0.5em; 267 margin-top: 0.5em;
268 border: solid 1px black; 268 border: solid 1px black;
269} 269}
270 270
271table.bin-blob th { 271table.bin-blob th {
272 font-family: monospace; 272 font-family: monospace;
273 white-space: pre; 273 white-space: pre;
274 border: solid 1px #777; 274 border: solid 1px #777;
275 padding: 0.5em 1em; 275 padding: 0.5em 1em;
276} 276}
277 277
278table.bin-blob td { 278table.bin-blob td {
279 font-family: monospace; 279 font-family: monospace;
280 white-space: pre; 280 white-space: pre;
281 border-left: solid 1px #777; 281 border-left: solid 1px #777;
282 padding: 0em 1em; 282 padding: 0em 1em;
283} 283}
284 284
285table.nowrap td { 285table.nowrap td {
286 white-space: nowrap; 286 white-space: nowrap;
287} 287}
288 288
289table.commit-info { 289table.commit-info {
290 border-collapse: collapse; 290 border-collapse: collapse;
291 margin-top: 1.5em; 291 margin-top: 1.5em;
292} 292}
293 293
294table.commit-info th { 294table.commit-info th {
295 text-align: left; 295 text-align: left;
296 font-weight: normal; 296 font-weight: normal;
297 padding: 0.1em 1em 0.1em 0.1em; 297 padding: 0.1em 1em 0.1em 0.1em;
298 vertical-align: top; 298 vertical-align: top;
299} 299}
300 300
301table.commit-info td { 301table.commit-info td {
302 font-weight: normal; 302 font-weight: normal;
303 padding: 0.1em 1em 0.1em 0.1em; 303 padding: 0.1em 1em 0.1em 0.1em;
304} 304}
305 305
306div.commit-subject { 306div.commit-subject {
307 font-weight: bold; 307 font-weight: bold;
308 font-size: 125%; 308 font-size: 125%;
309 margin: 1.5em 0em 0.5em 0em; 309 margin: 1.5em 0em 0.5em 0em;
310 padding: 0em; 310 padding: 0em;
311} 311}
312 312
313div.commit-msg { 313div.commit-msg {
314 white-space: pre; 314 white-space: pre;
315 font-family: monospace; 315 font-family: monospace;
316} 316}
317 317
318div.diffstat-header { 318div.diffstat-header {
319 font-weight: bold; 319 font-weight: bold;
320 padding-top: 1.5em; 320 padding-top: 1.5em;
321} 321}
322 322
323table.diffstat { 323table.diffstat {
324 border-collapse: collapse; 324 border-collapse: collapse;
325 border: solid 1px #aaa; 325 border: solid 1px #aaa;
326 background-color: #eee; 326 background-color: #eee;
327} 327}
328 328
329table.diffstat th { 329table.diffstat th {
330 font-weight: normal; 330 font-weight: normal;
331 text-align: left; 331 text-align: left;
332 text-decoration: underline; 332 text-decoration: underline;
333 padding: 0.1em 1em 0.1em 0.1em; 333 padding: 0.1em 1em 0.1em 0.1em;
334 font-size: 100%; 334 font-size: 100%;
335} 335}
336 336
337table.diffstat td { 337table.diffstat td {
338 padding: 0.2em 0.2em 0.1em 0.1em; 338 padding: 0.2em 0.2em 0.1em 0.1em;
339 font-size: 100%; 339 font-size: 100%;
340 border: none; 340 border: none;
341} 341}
342 342
343table.diffstat td.mode { 343table.diffstat td.mode {
344 white-space: nowrap; 344 white-space: nowrap;
345} 345}
346 346
347table.diffstat td span.modechange { 347table.diffstat td span.modechange {
348 padding-left: 1em; 348 padding-left: 1em;
349 color: red; 349 color: red;
350} 350}
351 351
352table.diffstat td.add a { 352table.diffstat td.add a {
353 color: green; 353 color: green;
354} 354}
355 355
356table.diffstat td.del a { 356table.diffstat td.del a {
357 color: red; 357 color: red;
358} 358}
359 359
360table.diffstat td.upd a { 360table.diffstat td.upd a {
361 color: blue; 361 color: blue;
362} 362}
363 363
364table.diffstat td.graph { 364table.diffstat td.graph {
365 width: 500px; 365 width: 500px;
366 vertical-align: middle; 366 vertical-align: middle;
367} 367}
368 368
369table.diffstat td.graph table { 369table.diffstat td.graph table {
370 border: none; 370 border: none;
371} 371}
372 372
373table.diffstat td.graph td { 373table.diffstat td.graph td {
374 padding: 0px; 374 padding: 0px;
375 border: 0px; 375 border: 0px;
376 height: 7pt; 376 height: 7pt;
377} 377}
378 378
379table.diffstat td.graph td.add { 379table.diffstat td.graph td.add {
380 background-color: #5c5; 380 background-color: #5c5;
381} 381}
382 382
383table.diffstat td.graph td.rem { 383table.diffstat td.graph td.rem {
384 background-color: #c55; 384 background-color: #c55;
385} 385}
386 386
387div.diffstat-summary { 387div.diffstat-summary {
388 color: #888; 388 color: #888;
389 padding-top: 0.5em; 389 padding-top: 0.5em;
390} 390}
391 391
392table.diff { 392table.diff {
393 width: 100%; 393 width: 100%;
394} 394}
395 395
396table.diff td { 396table.diff td {
397 font-family: monospace; 397 font-family: monospace;
398 white-space: pre; 398 white-space: pre;
399} 399}
400 400
401table.diff td div.head { 401table.diff td div.head {
402 font-weight: bold; 402 font-weight: bold;
403 margin-top: 1em; 403 margin-top: 1em;
404 color: black; 404 color: black;
405} 405}
406 406
407table.diff td div.hunk { 407table.diff td div.hunk {
408 color: #009; 408 color: #009;
409} 409}
410 410
411table.diff td div.add { 411table.diff td div.add {
412 color: green; 412 color: green;
413} 413}
414 414
415table.diff td div.del { 415table.diff td div.del {
416 color: red; 416 color: red;
417} 417}
418 418
419.sha1 { 419.sha1 {
420 font-family: monospace; 420 font-family: monospace;
421 font-size: 90%; 421 font-size: 90%;
422} 422}
423 423
424.left { 424.left {
425 text-align: left; 425 text-align: left;
426} 426}
427 427
428.right { 428.right {
429 text-align: right; 429 text-align: right;
430} 430}
431 431
432table.list td.reposection { 432table.list td.reposection {
433 font-style: italic; 433 font-style: italic;
434 color: #888; 434 color: #888;
435} 435}
436 436
437a.button { 437a.button {
438 font-size: 80%; 438 font-size: 80%;
439 padding: 0em 0.5em; 439 padding: 0em 0.5em;
440} 440}
441 441
diff --git a/cgit.h b/cgit.h
index ef109aa..6c6c460 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,293 +1,294 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <string-list.h> 18#include <string-list.h>
19#include <xdiff-interface.h> 19#include <xdiff-interface.h>
20#include <xdiff/xdiff.h> 20#include <xdiff/xdiff.h>
21#include <utf8.h> 21#include <utf8.h>
22 22
23 23
24/* 24/*
25 * Dateformats used on misc. pages 25 * Dateformats used on misc. pages
26 */ 26 */
27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
28#define FMT_SHORTDATE "%Y-%m-%d" 28#define FMT_SHORTDATE "%Y-%m-%d"
29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
30 30
31 31
32/* 32/*
33 * Limits used for relative dates 33 * Limits used for relative dates
34 */ 34 */
35#define TM_MIN 60 35#define TM_MIN 60
36#define TM_HOUR (TM_MIN * 60) 36#define TM_HOUR (TM_MIN * 60)
37#define TM_DAY (TM_HOUR * 24) 37#define TM_DAY (TM_HOUR * 24)
38#define TM_WEEK (TM_DAY * 7) 38#define TM_WEEK (TM_DAY * 7)
39#define TM_YEAR (TM_DAY * 365) 39#define TM_YEAR (TM_DAY * 365)
40#define TM_MONTH (TM_YEAR / 12.0) 40#define TM_MONTH (TM_YEAR / 12.0)
41 41
42 42
43/* 43/*
44 * Default encoding 44 * Default encoding
45 */ 45 */
46#define PAGE_ENCODING "UTF-8" 46#define PAGE_ENCODING "UTF-8"
47 47
48typedef void (*configfn)(const char *name, const char *value); 48typedef void (*configfn)(const char *name, const char *value);
49typedef void (*filepair_fn)(struct diff_filepair *pair); 49typedef void (*filepair_fn)(struct diff_filepair *pair);
50typedef void (*linediff_fn)(char *line, int len); 50typedef void (*linediff_fn)(char *line, int len);
51 51
52struct cgit_filter { 52struct cgit_filter {
53 char *cmd; 53 char *cmd;
54 char **argv; 54 char **argv;
55 int old_stdout; 55 int old_stdout;
56 int pipe_fh[2]; 56 int pipe_fh[2];
57 int pid; 57 int pid;
58 int exitstatus; 58 int exitstatus;
59}; 59};
60 60
61struct cgit_repo { 61struct cgit_repo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *module_link; 68 char *module_link;
69 char *readme; 69 char *readme;
70 char *section; 70 char *section;
71 char *clone_url; 71 char *clone_url;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75 int max_stats; 75 int max_stats;
76 time_t mtime; 76 time_t mtime;
77 struct cgit_filter *about_filter; 77 struct cgit_filter *about_filter;
78 struct cgit_filter *commit_filter; 78 struct cgit_filter *commit_filter;
79 struct cgit_filter *source_filter; 79 struct cgit_filter *source_filter;
80}; 80};
81 81
82typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 82typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
83 const char *value); 83 const char *value);
84 84
85struct cgit_repolist { 85struct cgit_repolist {
86 int length; 86 int length;
87 int count; 87 int count;
88 struct cgit_repo *repos; 88 struct cgit_repo *repos;
89}; 89};
90 90
91struct commitinfo { 91struct commitinfo {
92 struct commit *commit; 92 struct commit *commit;
93 char *author; 93 char *author;
94 char *author_email; 94 char *author_email;
95 unsigned long author_date; 95 unsigned long author_date;
96 char *committer; 96 char *committer;
97 char *committer_email; 97 char *committer_email;
98 unsigned long committer_date; 98 unsigned long committer_date;
99 char *subject; 99 char *subject;
100 char *msg; 100 char *msg;
101 char *msg_encoding; 101 char *msg_encoding;
102}; 102};
103 103
104struct taginfo { 104struct taginfo {
105 char *tagger; 105 char *tagger;
106 char *tagger_email; 106 char *tagger_email;
107 unsigned long tagger_date; 107 unsigned long tagger_date;
108 char *msg; 108 char *msg;
109}; 109};
110 110
111struct refinfo { 111struct refinfo {
112 const char *refname; 112 const char *refname;
113 struct object *object; 113 struct object *object;
114 union { 114 union {
115 struct taginfo *tag; 115 struct taginfo *tag;
116 struct commitinfo *commit; 116 struct commitinfo *commit;
117 }; 117 };
118}; 118};
119 119
120struct reflist { 120struct reflist {
121 struct refinfo **refs; 121 struct refinfo **refs;
122 int alloc; 122 int alloc;
123 int count; 123 int count;
124}; 124};
125 125
126struct cgit_query { 126struct cgit_query {
127 int has_symref; 127 int has_symref;
128 int has_sha1; 128 int has_sha1;
129 char *raw; 129 char *raw;
130 char *repo; 130 char *repo;
131 char *page; 131 char *page;
132 char *search; 132 char *search;
133 char *grep; 133 char *grep;
134 char *head; 134 char *head;
135 char *sha1; 135 char *sha1;
136 char *sha2; 136 char *sha2;
137 char *path; 137 char *path;
138 char *name; 138 char *name;
139 char *mimetype; 139 char *mimetype;
140 char *url; 140 char *url;
141 char *period; 141 char *period;
142 int ofs; 142 int ofs;
143 int nohead; 143 int nohead;
144 char *sort; 144 char *sort;
145 int showmsg; 145 int showmsg;
146}; 146};
147 147
148struct cgit_config { 148struct cgit_config {
149 char *agefile; 149 char *agefile;
150 char *cache_root; 150 char *cache_root;
151 char *clone_prefix; 151 char *clone_prefix;
152 char *css; 152 char *css;
153 char *favicon; 153 char *favicon;
154 char *footer; 154 char *footer;
155 char *head_include; 155 char *head_include;
156 char *header; 156 char *header;
157 char *index_header; 157 char *index_header;
158 char *index_info; 158 char *index_info;
159 char *logo; 159 char *logo;
160 char *logo_link; 160 char *logo_link;
161 char *module_link; 161 char *module_link;
162 char *robots; 162 char *robots;
163 char *root_title; 163 char *root_title;
164 char *root_desc; 164 char *root_desc;
165 char *root_readme; 165 char *root_readme;
166 char *script_name; 166 char *script_name;
167 char *section; 167 char *section;
168 char *virtual_root; 168 char *virtual_root;
169 int cache_size; 169 int cache_size;
170 int cache_dynamic_ttl; 170 int cache_dynamic_ttl;
171 int cache_max_create_time; 171 int cache_max_create_time;
172 int cache_repo_ttl; 172 int cache_repo_ttl;
173 int cache_root_ttl; 173 int cache_root_ttl;
174 int cache_scanrc_ttl; 174 int cache_scanrc_ttl;
175 int cache_static_ttl; 175 int cache_static_ttl;
176 int embedded; 176 int embedded;
177 int enable_filter_overrides; 177 int enable_filter_overrides;
178 int enable_index_links; 178 int enable_index_links;
179 int enable_log_filecount; 179 int enable_log_filecount;
180 int enable_log_linecount; 180 int enable_log_linecount;
181 int enable_tree_linenumbers;
181 int local_time; 182 int local_time;
182 int max_repo_count; 183 int max_repo_count;
183 int max_commit_count; 184 int max_commit_count;
184 int max_lock_attempts; 185 int max_lock_attempts;
185 int max_msg_len; 186 int max_msg_len;
186 int max_repodesc_len; 187 int max_repodesc_len;
187 int max_stats; 188 int max_stats;
188 int nocache; 189 int nocache;
189 int noplainemail; 190 int noplainemail;
190 int noheader; 191 int noheader;
191 int renamelimit; 192 int renamelimit;
192 int snapshots; 193 int snapshots;
193 int summary_branches; 194 int summary_branches;
194 int summary_log; 195 int summary_log;
195 int summary_tags; 196 int summary_tags;
196 struct string_list mimetypes; 197 struct string_list mimetypes;
197 struct cgit_filter *about_filter; 198 struct cgit_filter *about_filter;
198 struct cgit_filter *commit_filter; 199 struct cgit_filter *commit_filter;
199 struct cgit_filter *source_filter; 200 struct cgit_filter *source_filter;
200}; 201};
201 202
202struct cgit_page { 203struct cgit_page {
203 time_t modified; 204 time_t modified;
204 time_t expires; 205 time_t expires;
205 size_t size; 206 size_t size;
206 char *mimetype; 207 char *mimetype;
207 char *charset; 208 char *charset;
208 char *filename; 209 char *filename;
209 char *etag; 210 char *etag;
210 char *title; 211 char *title;
211 int status; 212 int status;
212 char *statusmsg; 213 char *statusmsg;
213}; 214};
214 215
215struct cgit_environment { 216struct cgit_environment {
216 char *cgit_config; 217 char *cgit_config;
217 char *http_host; 218 char *http_host;
218 char *https; 219 char *https;
219 char *no_http; 220 char *no_http;
220 char *path_info; 221 char *path_info;
221 char *query_string; 222 char *query_string;
222 char *request_method; 223 char *request_method;
223 char *script_name; 224 char *script_name;
224 char *server_name; 225 char *server_name;
225 char *server_port; 226 char *server_port;
226}; 227};
227 228
228struct cgit_context { 229struct cgit_context {
229 struct cgit_environment env; 230 struct cgit_environment env;
230 struct cgit_query qry; 231 struct cgit_query qry;
231 struct cgit_config cfg; 232 struct cgit_config cfg;
232 struct cgit_repo *repo; 233 struct cgit_repo *repo;
233 struct cgit_page page; 234 struct cgit_page page;
234}; 235};
235 236
236struct cgit_snapshot_format { 237struct cgit_snapshot_format {
237 const char *suffix; 238 const char *suffix;
238 const char *mimetype; 239 const char *mimetype;
239 write_archive_fn_t write_func; 240 write_archive_fn_t write_func;
240 int bit; 241 int bit;
241}; 242};
242 243
243extern const char *cgit_version; 244extern const char *cgit_version;
244 245
245extern struct cgit_repolist cgit_repolist; 246extern struct cgit_repolist cgit_repolist;
246extern struct cgit_context ctx; 247extern struct cgit_context ctx;
247extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 248extern const struct cgit_snapshot_format cgit_snapshot_formats[];
248 249
249extern struct cgit_repo *cgit_add_repo(const char *url); 250extern struct cgit_repo *cgit_add_repo(const char *url);
250extern struct cgit_repo *cgit_get_repoinfo(const char *url); 251extern struct cgit_repo *cgit_get_repoinfo(const char *url);
251extern void cgit_repo_config_cb(const char *name, const char *value); 252extern void cgit_repo_config_cb(const char *name, const char *value);
252 253
253extern int chk_zero(int result, char *msg); 254extern int chk_zero(int result, char *msg);
254extern int chk_positive(int result, char *msg); 255extern int chk_positive(int result, char *msg);
255extern int chk_non_negative(int result, char *msg); 256extern int chk_non_negative(int result, char *msg);
256 257
257extern char *trim_end(const char *str, char c); 258extern char *trim_end(const char *str, char c);
258extern char *strlpart(char *txt, int maxlen); 259extern char *strlpart(char *txt, int maxlen);
259extern char *strrpart(char *txt, int maxlen); 260extern char *strrpart(char *txt, int maxlen);
260 261
261extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 262extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
262extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 263extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
263 int flags, void *cb_data); 264 int flags, void *cb_data);
264 265
265extern void *cgit_free_commitinfo(struct commitinfo *info); 266extern void *cgit_free_commitinfo(struct commitinfo *info);
266 267
267extern int cgit_diff_files(const unsigned char *old_sha1, 268extern int cgit_diff_files(const unsigned char *old_sha1,
268 const unsigned char *new_sha1, 269 const unsigned char *new_sha1,
269 unsigned long *old_size, unsigned long *new_size, 270 unsigned long *old_size, unsigned long *new_size,
270 int *binary, linediff_fn fn); 271 int *binary, linediff_fn fn);
271 272
272extern void cgit_diff_tree(const unsigned char *old_sha1, 273extern void cgit_diff_tree(const unsigned char *old_sha1,
273 const unsigned char *new_sha1, 274 const unsigned char *new_sha1,
274 filepair_fn fn, const char *prefix); 275 filepair_fn fn, const char *prefix);
275 276
276extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 277extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
277 278
278extern char *fmt(const char *format,...); 279extern char *fmt(const char *format,...);
279 280
280extern struct commitinfo *cgit_parse_commit(struct commit *commit); 281extern struct commitinfo *cgit_parse_commit(struct commit *commit);
281extern struct taginfo *cgit_parse_tag(struct tag *tag); 282extern struct taginfo *cgit_parse_tag(struct tag *tag);
282extern void cgit_parse_url(const char *url); 283extern void cgit_parse_url(const char *url);
283 284
284extern const char *cgit_repobasename(const char *reponame); 285extern const char *cgit_repobasename(const char *reponame);
285 286
286extern int cgit_parse_snapshots_mask(const char *str); 287extern int cgit_parse_snapshots_mask(const char *str);
287 288
288extern int cgit_open_filter(struct cgit_filter *filter); 289extern int cgit_open_filter(struct cgit_filter *filter);
289extern int cgit_close_filter(struct cgit_filter *filter); 290extern int cgit_close_filter(struct cgit_filter *filter);
290 291
291extern int readfile(const char *path, char **buf, size_t *size); 292extern int readfile(const char *path, char **buf, size_t *size);
292 293
293#endif /* CGIT_H */ 294#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 617b7c3..4dc383d 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,301 +1,305 @@
1CGITRC(5) 1CGITRC(5)
2======== 2========
3 3
4 4
5NAME 5NAME
6---- 6----
7cgitrc - runtime configuration for cgit 7cgitrc - runtime configuration for cgit
8 8
9 9
10SYNOPSIS 10SYNOPSIS
11-------- 11--------
12Cgitrc contains all runtime settings for cgit, including the list of git 12Cgitrc contains all runtime settings for cgit, including the list of git
13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank 13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank
14lines, and lines starting with '#', are ignored. 14lines, and lines starting with '#', are ignored.
15 15
16 16
17LOCATION 17LOCATION
18-------- 18--------
19The default location of cgitrc, defined at compile time, is /etc/cgitrc. At 19The default location of cgitrc, defined at compile time, is /etc/cgitrc. At
20runtime, cgit will consult the environment variable CGIT_CONFIG and, if 20runtime, cgit will consult the environment variable CGIT_CONFIG and, if
21defined, use its value instead. 21defined, use its value instead.
22 22
23 23
24GLOBAL SETTINGS 24GLOBAL SETTINGS
25--------------- 25---------------
26about-filter:: 26about-filter::
27 Specifies a command which will be invoked to format the content of 27 Specifies a command which will be invoked to format the content of
28 about pages (both top-level and for each repository). The command will 28 about pages (both top-level and for each repository). The command will
29 get the content of the about-file on its STDIN, and the STDOUT from the 29 get the content of the about-file on its STDIN, and the STDOUT from the
30 command will be included verbatim on the about page. Default value: 30 command will be included verbatim on the about page. Default value:
31 none. 31 none.
32 32
33agefile:: 33agefile::
34 Specifies a path, relative to each repository path, which can be used 34 Specifies a path, relative to each repository path, which can be used
35 to specify the date and time of the youngest commit in the repository. 35 to specify the date and time of the youngest commit in the repository.
36 The first line in the file is used as input to the "parse_date" 36 The first line in the file is used as input to the "parse_date"
37 function in libgit. Recommended timestamp-format is "yyyy-mm-dd 37 function in libgit. Recommended timestamp-format is "yyyy-mm-dd
38 hh:mm:ss". Default value: "info/web/last-modified". 38 hh:mm:ss". Default value: "info/web/last-modified".
39 39
40cache-root:: 40cache-root::
41 Path used to store the cgit cache entries. Default value: 41 Path used to store the cgit cache entries. Default value:
42 "/var/cache/cgit". 42 "/var/cache/cgit".
43 43
44cache-dynamic-ttl:: 44cache-dynamic-ttl::
45 Number which specifies the time-to-live, in minutes, for the cached 45 Number which specifies the time-to-live, in minutes, for the cached
46 version of repository pages accessed without a fixed SHA1. Default 46 version of repository pages accessed without a fixed SHA1. Default
47 value: "5". 47 value: "5".
48 48
49cache-repo-ttl:: 49cache-repo-ttl::
50 Number which specifies the time-to-live, in minutes, for the cached 50 Number which specifies the time-to-live, in minutes, for the cached
51 version of the repository summary page. Default value: "5". 51 version of the repository summary page. Default value: "5".
52 52
53cache-root-ttl:: 53cache-root-ttl::
54 Number which specifies the time-to-live, in minutes, for the cached 54 Number which specifies the time-to-live, in minutes, for the cached
55 version of the repository index page. Default value: "5". 55 version of the repository index page. Default value: "5".
56 56
57cache-scanrc-ttl:: 57cache-scanrc-ttl::
58 Number which specifies the time-to-live, in minutes, for the result 58 Number which specifies the time-to-live, in minutes, for the result
59 of scanning a path for git repositories. Default value: "15". 59 of scanning a path for git repositories. Default value: "15".
60 60
61cache-size:: 61cache-size::
62 The maximum number of entries in the cgit cache. Default value: "0" 62 The maximum number of entries in the cgit cache. Default value: "0"
63 (i.e. caching is disabled). 63 (i.e. caching is disabled).
64 64
65cache-static-ttl:: 65cache-static-ttl::
66 Number which specifies the time-to-live, in minutes, for the cached 66 Number which specifies the time-to-live, in minutes, for the cached
67 version of repository pages accessed with a fixed SHA1. Default value: 67 version of repository pages accessed with a fixed SHA1. Default value:
68 "5". 68 "5".
69 69
70clone-prefix:: 70clone-prefix::
71 Space-separated list of common prefixes which, when combined with a 71 Space-separated list of common prefixes which, when combined with a
72 repository url, generates valid clone urls for the repository. This 72 repository url, generates valid clone urls for the repository. This
73 setting is only used if `repo.clone-url` is unspecified. Default value: 73 setting is only used if `repo.clone-url` is unspecified. Default value:
74 none. 74 none.
75 75
76commit-filter:: 76commit-filter::
77 Specifies a command which will be invoked to format commit messages. 77 Specifies a command which will be invoked to format commit messages.
78 The command will get the message on its STDIN, and the STDOUT from the 78 The command will get the message on its STDIN, and the STDOUT from the
79 command will be included verbatim as the commit message, i.e. this can 79 command will be included verbatim as the commit message, i.e. this can
80 be used to implement bugtracker integration. Default value: none. 80 be used to implement bugtracker integration. Default value: none.
81 81
82css:: 82css::
83 Url which specifies the css document to include in all cgit pages. 83 Url which specifies the css document to include in all cgit pages.
84 Default value: "/cgit.css". 84 Default value: "/cgit.css".
85 85
86embedded:: 86embedded::
87 Flag which, when set to "1", will make cgit generate a html fragment 87 Flag which, when set to "1", will make cgit generate a html fragment
88 suitable for embedding in other html pages. Default value: none. See 88 suitable for embedding in other html pages. Default value: none. See
89 also: "noheader". 89 also: "noheader".
90 90
91enable-filter-overrides:: 91enable-filter-overrides::
92 Flag which, when set to "1", allows all filter settings to be 92 Flag which, when set to "1", allows all filter settings to be
93 overridden in repository-specific cgitrc files. Default value: none. 93 overridden in repository-specific cgitrc files. Default value: none.
94 94
95enable-index-links:: 95enable-index-links::
96 Flag which, when set to "1", will make cgit generate extra links for 96 Flag which, when set to "1", will make cgit generate extra links for
97 each repo in the repository index (specifically, to the "summary", 97 each repo in the repository index (specifically, to the "summary",
98 "commit" and "tree" pages). Default value: "0". 98 "commit" and "tree" pages). Default value: "0".
99 99
100enable-log-filecount:: 100enable-log-filecount::
101 Flag which, when set to "1", will make cgit print the number of 101 Flag which, when set to "1", will make cgit print the number of
102 modified files for each commit on the repository log page. Default 102 modified files for each commit on the repository log page. Default
103 value: "0". 103 value: "0".
104 104
105enable-log-linecount:: 105enable-log-linecount::
106 Flag which, when set to "1", will make cgit print the number of added 106 Flag which, when set to "1", will make cgit print the number of added
107 and removed lines for each commit on the repository log page. Default 107 and removed lines for each commit on the repository log page. Default
108 value: "0". 108 value: "0".
109 109
110enable-tree-linenumbers::
111 Flag which, when set to "1", will make cgit generate linenumber links
112 for plaintext blobs printed in the tree view. Default value: "1".
113
110favicon:: 114favicon::
111 Url used as link to a shortcut icon for cgit. If specified, it is 115 Url used as link to a shortcut icon for cgit. If specified, it is
112 suggested to use the value "/favicon.ico" since certain browsers will 116 suggested to use the value "/favicon.ico" since certain browsers will
113 ignore other values. Default value: none. 117 ignore other values. Default value: none.
114 118
115footer:: 119footer::
116 The content of the file specified with this option will be included 120 The content of the file specified with this option will be included
117 verbatim at the bottom of all pages (i.e. it replaces the standard 121 verbatim at the bottom of all pages (i.e. it replaces the standard
118 "generated by..." message. Default value: none. 122 "generated by..." message. Default value: none.
119 123
120head-include:: 124head-include::
121 The content of the file specified with this option will be included 125 The content of the file specified with this option will be included
122 verbatim in the html HEAD section on all pages. Default value: none. 126 verbatim in the html HEAD section on all pages. Default value: none.
123 127
124header:: 128header::
125 The content of the file specified with this option will be included 129 The content of the file specified with this option will be included
126 verbatim at the top of all pages. Default value: none. 130 verbatim at the top of all pages. Default value: none.
127 131
128include:: 132include::
129 Name of a configfile to include before the rest of the current config- 133 Name of a configfile to include before the rest of the current config-
130 file is parsed. Default value: none. 134 file is parsed. Default value: none.
131 135
132index-header:: 136index-header::
133 The content of the file specified with this option will be included 137 The content of the file specified with this option will be included
134 verbatim above the repository index. This setting is deprecated, and 138 verbatim above the repository index. This setting is deprecated, and
135 will not be supported by cgit-1.0 (use root-readme instead). Default 139 will not be supported by cgit-1.0 (use root-readme instead). Default
136 value: none. 140 value: none.
137 141
138index-info:: 142index-info::
139 The content of the file specified with this option will be included 143 The content of the file specified with this option will be included
140 verbatim below the heading on the repository index page. This setting 144 verbatim below the heading on the repository index page. This setting
141 is deprecated, and will not be supported by cgit-1.0 (use root-desc 145 is deprecated, and will not be supported by cgit-1.0 (use root-desc
142 instead). Default value: none. 146 instead). Default value: none.
143 147
144local-time:: 148local-time::
145 Flag which, if set to "1", makes cgit print commit and tag times in the 149 Flag which, if set to "1", makes cgit print commit and tag times in the
146 servers timezone. Default value: "0". 150 servers timezone. Default value: "0".
147 151
148logo:: 152logo::
149 Url which specifies the source of an image which will be used as a logo 153 Url which specifies the source of an image which will be used as a logo
150 on all cgit pages. Default value: "/cgit.png". 154 on all cgit pages. Default value: "/cgit.png".
151 155
152logo-link:: 156logo-link::
153 Url loaded when clicking on the cgit logo image. If unspecified the 157 Url loaded when clicking on the cgit logo image. If unspecified the
154 calculated url of the repository index page will be used. Default 158 calculated url of the repository index page will be used. Default
155 value: none. 159 value: none.
156 160
157max-commit-count:: 161max-commit-count::
158 Specifies the number of entries to list per page in "log" view. Default 162 Specifies the number of entries to list per page in "log" view. Default
159 value: "50". 163 value: "50".
160 164
161max-message-length:: 165max-message-length::
162 Specifies the maximum number of commit message characters to display in 166 Specifies the maximum number of commit message characters to display in
163 "log" view. Default value: "80". 167 "log" view. Default value: "80".
164 168
165max-repo-count:: 169max-repo-count::
166 Specifies the number of entries to list per page on therepository 170 Specifies the number of entries to list per page on therepository
167 index page. Default value: "50". 171 index page. Default value: "50".
168 172
169max-repodesc-length:: 173max-repodesc-length::
170 Specifies the maximum number of repo description characters to display 174 Specifies the maximum number of repo description characters to display
171 on the repository index page. Default value: "80". 175 on the repository index page. Default value: "80".
172 176
173max-stats:: 177max-stats::
174 Set the default maximum statistics period. Valid values are "week", 178 Set the default maximum statistics period. Valid values are "week",
175 "month", "quarter" and "year". If unspecified, statistics are 179 "month", "quarter" and "year". If unspecified, statistics are
176 disabled. Default value: none. See also: "repo.max-stats". 180 disabled. Default value: none. See also: "repo.max-stats".
177 181
178mimetype.<ext>:: 182mimetype.<ext>::
179 Set the mimetype for the specified filename extension. This is used 183 Set the mimetype for the specified filename extension. This is used
180 by the `plain` command when returning blob content. 184 by the `plain` command when returning blob content.
181 185
182module-link:: 186module-link::
183 Text which will be used as the formatstring for a hyperlink when a 187 Text which will be used as the formatstring for a hyperlink when a
184 submodule is printed in a directory listing. The arguments for the 188 submodule is printed in a directory listing. The arguments for the
185 formatstring are the path and SHA1 of the submodule commit. Default 189 formatstring are the path and SHA1 of the submodule commit. Default
186 value: "./?repo=%s&page=commit&id=%s" 190 value: "./?repo=%s&page=commit&id=%s"
187 191
188nocache:: 192nocache::
189 If set to the value "1" caching will be disabled. This settings is 193 If set to the value "1" caching will be disabled. This settings is
190 deprecated, and will not be honored starting with cgit-1.0. Default 194 deprecated, and will not be honored starting with cgit-1.0. Default
191 value: "0". 195 value: "0".
192 196
193noplainemail:: 197noplainemail::
194 If set to "1" showing full author email adresses will be disabled. 198 If set to "1" showing full author email adresses will be disabled.
195 Default value: "0". 199 Default value: "0".
196 200
197noheader:: 201noheader::
198 Flag which, when set to "1", will make cgit omit the standard header 202 Flag which, when set to "1", will make cgit omit the standard header
199 on all pages. Default value: none. See also: "embedded". 203 on all pages. Default value: none. See also: "embedded".
200 204
201renamelimit:: 205renamelimit::
202 Maximum number of files to consider when detecting renames. The value 206 Maximum number of files to consider when detecting renames. The value
203 "-1" uses the compiletime value in git (for further info, look at 207 "-1" uses the compiletime value in git (for further info, look at
204 `man git-diff`). Default value: "-1". 208 `man git-diff`). Default value: "-1".
205 209
206repo.group:: 210repo.group::
207 Legacy alias for "section". This option is deprecated and will not be 211 Legacy alias for "section". This option is deprecated and will not be
208 supported in cgit-1.0. 212 supported in cgit-1.0.
209 213
210robots:: 214robots::
211 Text used as content for the "robots" meta-tag. Default value: 215 Text used as content for the "robots" meta-tag. Default value:
212 "index, nofollow". 216 "index, nofollow".
213 217
214root-desc:: 218root-desc::
215 Text printed below the heading on the repository index page. Default 219 Text printed below the heading on the repository index page. Default
216 value: "a fast webinterface for the git dscm". 220 value: "a fast webinterface for the git dscm".
217 221
218root-readme:: 222root-readme::
219 The content of the file specified with this option will be included 223 The content of the file specified with this option will be included
220 verbatim below the "about" link on the repository index page. Default 224 verbatim below the "about" link on the repository index page. Default
221 value: none. 225 value: none.
222 226
223root-title:: 227root-title::
224 Text printed as heading on the repository index page. Default value: 228 Text printed as heading on the repository index page. Default value:
225 "Git Repository Browser". 229 "Git Repository Browser".
226 230
227scan-path:: 231scan-path::
228 A path which will be scanned for repositories. If caching is enabled, 232 A path which will be scanned for repositories. If caching is enabled,
229 the result will be cached as a cgitrc include-file in the cache 233 the result will be cached as a cgitrc include-file in the cache
230 directory. Default value: none. See also: cache-scanrc-ttl. 234 directory. Default value: none. See also: cache-scanrc-ttl.
231 235
232section:: 236section::
233 The name of the current repository section - all repositories defined 237 The name of the current repository section - all repositories defined
234 after this option will inherit the current section name. Default value: 238 after this option will inherit the current section name. Default value:
235 none. 239 none.
236 240
237snapshots:: 241snapshots::
238 Text which specifies the default set of snapshot formats generated by 242 Text which specifies the default set of snapshot formats generated by
239 cgit. The value is a space-separated list of zero or more of the 243 cgit. The value is a space-separated list of zero or more of the
240 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 244 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
241 245
242source-filter:: 246source-filter::
243 Specifies a command which will be invoked to format plaintext blobs 247 Specifies a command which will be invoked to format plaintext blobs
244 in the tree view. The command will get the blob content on its STDIN 248 in the tree view. The command will get the blob content on its STDIN
245 and the name of the blob as its only command line argument. The STDOUT 249 and the name of the blob as its only command line argument. The STDOUT
246 from the command will be included verbatim as the blob contents, i.e. 250 from the command will be included verbatim as the blob contents, i.e.
247 this can be used to implement e.g. syntax highlighting. Default value: 251 this can be used to implement e.g. syntax highlighting. Default value:
248 none. 252 none.
249 253
250summary-branches:: 254summary-branches::
251 Specifies the number of branches to display in the repository "summary" 255 Specifies the number of branches to display in the repository "summary"
252 view. Default value: "10". 256 view. Default value: "10".
253 257
254summary-log:: 258summary-log::
255 Specifies the number of log entries to display in the repository 259 Specifies the number of log entries to display in the repository
256 "summary" view. Default value: "10". 260 "summary" view. Default value: "10".
257 261
258summary-tags:: 262summary-tags::
259 Specifies the number of tags to display in the repository "summary" 263 Specifies the number of tags to display in the repository "summary"
260 view. Default value: "10". 264 view. Default value: "10".
261 265
262virtual-root:: 266virtual-root::
263 Url which, if specified, will be used as root for all cgit links. It 267 Url which, if specified, will be used as root for all cgit links. It
264 will also cause cgit to generate 'virtual urls', i.e. urls like 268 will also cause cgit to generate 'virtual urls', i.e. urls like
265 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 269 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
266 value: none. 270 value: none.
267 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 271 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
268 same kind of virtual urls, so this option will probably be deprecated. 272 same kind of virtual urls, so this option will probably be deprecated.
269 273
270REPOSITORY SETTINGS 274REPOSITORY SETTINGS
271------------------- 275-------------------
272repo.about-filter:: 276repo.about-filter::
273 Override the default about-filter. Default value: none. See also: 277 Override the default about-filter. Default value: none. See also:
274 "enable-filter-overrides". 278 "enable-filter-overrides".
275 279
276repo.clone-url:: 280repo.clone-url::
277 A list of space-separated urls which can be used to clone this repo. 281 A list of space-separated urls which can be used to clone this repo.
278 Default value: none. 282 Default value: none.
279 283
280repo.commit-filter:: 284repo.commit-filter::
281 Override the default commit-filter. Default value: none. See also: 285 Override the default commit-filter. Default value: none. See also:
282 "enable-filter-overrides". 286 "enable-filter-overrides".
283 287
284repo.defbranch:: 288repo.defbranch::
285 The name of the default branch for this repository. If no such branch 289 The name of the default branch for this repository. If no such branch
286 exists in the repository, the first branch name (when sorted) is used 290 exists in the repository, the first branch name (when sorted) is used
287 as default instead. Default value: "master". 291 as default instead. Default value: "master".
288 292
289repo.desc:: 293repo.desc::
290 The value to show as repository description. Default value: none. 294 The value to show as repository description. Default value: none.
291 295
292repo.enable-log-filecount:: 296repo.enable-log-filecount::
293 A flag which can be used to disable the global setting 297 A flag which can be used to disable the global setting
294 `enable-log-filecount'. Default value: none. 298 `enable-log-filecount'. Default value: none.
295 299
296repo.enable-log-linecount:: 300repo.enable-log-linecount::
297 A flag which can be used to disable the global setting 301 A flag which can be used to disable the global setting
298 `enable-log-linecount'. Default value: none. 302 `enable-log-linecount'. Default value: none.
299 303
300repo.max-stats:: 304repo.max-stats::
301 Override the default maximum statistics period. Valid values are equal 305 Override the default maximum statistics period. Valid values are equal
diff --git a/git b/git
Subproject e276f018f2c1f0fc962fbe44a36708d1cdebada Subproject 7fb6bcff2dece2ff9fbc5ebfe526d9b2a7e764c
diff --git a/ui-commit.c b/ui-commit.c
index d6b73ee..f5b0ae5 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,118 +1,118 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12#include "ui-diff.h" 12#include "ui-diff.h"
13#include "ui-log.h" 13#include "ui-log.h"
14 14
15void cgit_print_commit(char *hex) 15void cgit_print_commit(char *hex)
16{ 16{
17 struct commit *commit, *parent; 17 struct commit *commit, *parent;
18 struct commitinfo *info; 18 struct commitinfo *info;
19 struct commit_list *p; 19 struct commit_list *p;
20 unsigned char sha1[20]; 20 unsigned char sha1[20];
21 char *tmp; 21 char *tmp;
22 int parents = 0; 22 int parents = 0;
23 23
24 if (!hex) 24 if (!hex)
25 hex = ctx.qry.head; 25 hex = ctx.qry.head;
26 26
27 if (get_sha1(hex, sha1)) { 27 if (get_sha1(hex, sha1)) {
28 cgit_print_error(fmt("Bad object id: %s", hex)); 28 cgit_print_error(fmt("Bad object id: %s", hex));
29 return; 29 return;
30 } 30 }
31 commit = lookup_commit_reference(sha1); 31 commit = lookup_commit_reference(sha1);
32 if (!commit) { 32 if (!commit) {
33 cgit_print_error(fmt("Bad commit reference: %s", hex)); 33 cgit_print_error(fmt("Bad commit reference: %s", hex));
34 return; 34 return;
35 } 35 }
36 info = cgit_parse_commit(commit); 36 info = cgit_parse_commit(commit);
37 37
38 load_ref_decorations(); 38 load_ref_decorations(DECORATE_FULL_REFS);
39 39
40 html("<table summary='commit info' class='commit-info'>\n"); 40 html("<table summary='commit info' class='commit-info'>\n");
41 html("<tr><th>author</th><td>"); 41 html("<tr><th>author</th><td>");
42 html_txt(info->author); 42 html_txt(info->author);
43 if (!ctx.cfg.noplainemail) { 43 if (!ctx.cfg.noplainemail) {
44 html(" "); 44 html(" ");
45 html_txt(info->author_email); 45 html_txt(info->author_email);
46 } 46 }
47 html("</td><td class='right'>"); 47 html("</td><td class='right'>");
48 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time); 48 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time);
49 html("</td></tr>\n"); 49 html("</td></tr>\n");
50 html("<tr><th>committer</th><td>"); 50 html("<tr><th>committer</th><td>");
51 html_txt(info->committer); 51 html_txt(info->committer);
52 if (!ctx.cfg.noplainemail) { 52 if (!ctx.cfg.noplainemail) {
53 html(" "); 53 html(" ");
54 html_txt(info->committer_email); 54 html_txt(info->committer_email);
55 } 55 }
56 html("</td><td class='right'>"); 56 html("</td><td class='right'>");
57 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time); 57 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time);
58 html("</td></tr>\n"); 58 html("</td></tr>\n");
59 html("<tr><th>commit</th><td colspan='2' class='sha1'>"); 59 html("<tr><th>commit</th><td colspan='2' class='sha1'>");
60 tmp = sha1_to_hex(commit->object.sha1); 60 tmp = sha1_to_hex(commit->object.sha1);
61 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp); 61 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp);
62 html(" ("); 62 html(" (");
63 cgit_patch_link("patch", NULL, NULL, NULL, tmp); 63 cgit_patch_link("patch", NULL, NULL, NULL, tmp);
64 html(")</td></tr>\n"); 64 html(")</td></tr>\n");
65 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 65 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
66 tmp = xstrdup(hex); 66 tmp = xstrdup(hex);
67 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, 67 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
68 ctx.qry.head, tmp, NULL); 68 ctx.qry.head, tmp, NULL);
69 html("</td></tr>\n"); 69 html("</td></tr>\n");
70 for (p = commit->parents; p ; p = p->next) { 70 for (p = commit->parents; p ; p = p->next) {
71 parent = lookup_commit_reference(p->item->object.sha1); 71 parent = lookup_commit_reference(p->item->object.sha1);
72 if (!parent) { 72 if (!parent) {
73 html("<tr><td colspan='3'>"); 73 html("<tr><td colspan='3'>");
74 cgit_print_error("Error reading parent commit"); 74 cgit_print_error("Error reading parent commit");
75 html("</td></tr>"); 75 html("</td></tr>");
76 continue; 76 continue;
77 } 77 }
78 html("<tr><th>parent</th>" 78 html("<tr><th>parent</th>"
79 "<td colspan='2' class='sha1'>"); 79 "<td colspan='2' class='sha1'>");
80 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 80 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
81 ctx.qry.head, sha1_to_hex(p->item->object.sha1)); 81 ctx.qry.head, sha1_to_hex(p->item->object.sha1));
82 html(" ("); 82 html(" (");
83 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, 83 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
84 sha1_to_hex(p->item->object.sha1), NULL); 84 sha1_to_hex(p->item->object.sha1), NULL);
85 html(")</td></tr>"); 85 html(")</td></tr>");
86 parents++; 86 parents++;
87 } 87 }
88 if (ctx.repo->snapshots) { 88 if (ctx.repo->snapshots) {
89 html("<tr><th>download</th><td colspan='2' class='sha1'>"); 89 html("<tr><th>download</th><td colspan='2' class='sha1'>");
90 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, 90 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
91 hex, ctx.repo->snapshots); 91 hex, ctx.repo->snapshots);
92 html("</td></tr>"); 92 html("</td></tr>");
93 } 93 }
94 html("</table>\n"); 94 html("</table>\n");
95 html("<div class='commit-subject'>"); 95 html("<div class='commit-subject'>");
96 if (ctx.repo->commit_filter) 96 if (ctx.repo->commit_filter)
97 cgit_open_filter(ctx.repo->commit_filter); 97 cgit_open_filter(ctx.repo->commit_filter);
98 html_txt(info->subject); 98 html_txt(info->subject);
99 if (ctx.repo->commit_filter) 99 if (ctx.repo->commit_filter)
100 cgit_close_filter(ctx.repo->commit_filter); 100 cgit_close_filter(ctx.repo->commit_filter);
101 show_commit_decorations(commit); 101 show_commit_decorations(commit);
102 html("</div>"); 102 html("</div>");
103 html("<div class='commit-msg'>"); 103 html("<div class='commit-msg'>");
104 if (ctx.repo->commit_filter) 104 if (ctx.repo->commit_filter)
105 cgit_open_filter(ctx.repo->commit_filter); 105 cgit_open_filter(ctx.repo->commit_filter);
106 html_txt(info->msg); 106 html_txt(info->msg);
107 if (ctx.repo->commit_filter) 107 if (ctx.repo->commit_filter)
108 cgit_close_filter(ctx.repo->commit_filter); 108 cgit_close_filter(ctx.repo->commit_filter);
109 html("</div>"); 109 html("</div>");
110 if (parents < 3) { 110 if (parents < 3) {
111 if (parents) 111 if (parents)
112 tmp = sha1_to_hex(commit->parents->item->object.sha1); 112 tmp = sha1_to_hex(commit->parents->item->object.sha1);
113 else 113 else
114 tmp = NULL; 114 tmp = NULL;
115 cgit_print_diff(ctx.qry.sha1, tmp, NULL); 115 cgit_print_diff(ctx.qry.sha1, tmp, NULL);
116 } 116 }
117 cgit_free_commitinfo(info); 117 cgit_free_commitinfo(info);
118} 118}
diff --git a/ui-log.c b/ui-log.c
index 0b37785..f3132c9 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,234 +1,234 @@
1/* ui-log.c: functions for log output 1/* ui-log.c: functions for log output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13int files, add_lines, rem_lines; 13int files, add_lines, rem_lines;
14 14
15void count_lines(char *line, int size) 15void count_lines(char *line, int size)
16{ 16{
17 if (size <= 0) 17 if (size <= 0)
18 return; 18 return;
19 19
20 if (line[0] == '+') 20 if (line[0] == '+')
21 add_lines++; 21 add_lines++;
22 22
23 else if (line[0] == '-') 23 else if (line[0] == '-')
24 rem_lines++; 24 rem_lines++;
25} 25}
26 26
27void inspect_files(struct diff_filepair *pair) 27void inspect_files(struct diff_filepair *pair)
28{ 28{
29 unsigned long old_size = 0; 29 unsigned long old_size = 0;
30 unsigned long new_size = 0; 30 unsigned long new_size = 0;
31 int binary = 0; 31 int binary = 0;
32 32
33 files++; 33 files++;
34 if (ctx.repo->enable_log_linecount) 34 if (ctx.repo->enable_log_linecount)
35 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 35 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
36 &new_size, &binary, count_lines); 36 &new_size, &binary, count_lines);
37} 37}
38 38
39void show_commit_decorations(struct commit *commit) 39void show_commit_decorations(struct commit *commit)
40{ 40{
41 struct name_decoration *deco; 41 struct name_decoration *deco;
42 static char buf[1024]; 42 static char buf[1024];
43 43
44 buf[sizeof(buf) - 1] = 0; 44 buf[sizeof(buf) - 1] = 0;
45 deco = lookup_decoration(&name_decoration, &commit->object); 45 deco = lookup_decoration(&name_decoration, &commit->object);
46 while (deco) { 46 while (deco) {
47 if (!prefixcmp(deco->name, "refs/heads/")) { 47 if (!prefixcmp(deco->name, "refs/heads/")) {
48 strncpy(buf, deco->name + 11, sizeof(buf) - 1); 48 strncpy(buf, deco->name + 11, sizeof(buf) - 1);
49 cgit_log_link(buf, NULL, "branch-deco", buf, NULL, NULL, 49 cgit_log_link(buf, NULL, "branch-deco", buf, NULL, NULL,
50 0, NULL, NULL, ctx.qry.showmsg); 50 0, NULL, NULL, ctx.qry.showmsg);
51 } 51 }
52 else if (!prefixcmp(deco->name, "tag: refs/tags/")) { 52 else if (!prefixcmp(deco->name, "tag: refs/tags/")) {
53 strncpy(buf, deco->name + 15, sizeof(buf) - 1); 53 strncpy(buf, deco->name + 15, sizeof(buf) - 1);
54 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf); 54 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
55 } 55 }
56 else if (!prefixcmp(deco->name, "refs/tags/")) { 56 else if (!prefixcmp(deco->name, "refs/tags/")) {
57 strncpy(buf, deco->name + 10, sizeof(buf) - 1); 57 strncpy(buf, deco->name + 10, sizeof(buf) - 1);
58 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf); 58 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
59 } 59 }
60 else if (!prefixcmp(deco->name, "refs/remotes/")) { 60 else if (!prefixcmp(deco->name, "refs/remotes/")) {
61 strncpy(buf, deco->name + 13, sizeof(buf) - 1); 61 strncpy(buf, deco->name + 13, sizeof(buf) - 1);
62 cgit_log_link(buf, NULL, "remote-deco", NULL, 62 cgit_log_link(buf, NULL, "remote-deco", NULL,
63 sha1_to_hex(commit->object.sha1), NULL, 63 sha1_to_hex(commit->object.sha1), NULL,
64 0, NULL, NULL, ctx.qry.showmsg); 64 0, NULL, NULL, ctx.qry.showmsg);
65 } 65 }
66 else { 66 else {
67 strncpy(buf, deco->name, sizeof(buf) - 1); 67 strncpy(buf, deco->name, sizeof(buf) - 1);
68 cgit_commit_link(buf, NULL, "deco", ctx.qry.head, 68 cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
69 sha1_to_hex(commit->object.sha1)); 69 sha1_to_hex(commit->object.sha1));
70 } 70 }
71 deco = deco->next; 71 deco = deco->next;
72 } 72 }
73} 73}
74 74
75void print_commit(struct commit *commit) 75void print_commit(struct commit *commit)
76{ 76{
77 struct commitinfo *info; 77 struct commitinfo *info;
78 char *tmp; 78 char *tmp;
79 int cols = 2; 79 int cols = 2;
80 80
81 info = cgit_parse_commit(commit); 81 info = cgit_parse_commit(commit);
82 htmlf("<tr%s><td>", 82 htmlf("<tr%s><td>",
83 ctx.qry.showmsg ? " class='logheader'" : ""); 83 ctx.qry.showmsg ? " class='logheader'" : "");
84 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); 84 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
85 tmp = cgit_pageurl(ctx.repo->url, "commit", tmp); 85 tmp = cgit_pageurl(ctx.repo->url, "commit", tmp);
86 html_link_open(tmp, NULL, NULL); 86 html_link_open(tmp, NULL, NULL);
87 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 87 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
88 html_link_close(); 88 html_link_close();
89 htmlf("</td><td%s>", 89 htmlf("</td><td%s>",
90 ctx.qry.showmsg ? " class='logsubject'" : ""); 90 ctx.qry.showmsg ? " class='logsubject'" : "");
91 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, 91 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
92 sha1_to_hex(commit->object.sha1)); 92 sha1_to_hex(commit->object.sha1));
93 show_commit_decorations(commit); 93 show_commit_decorations(commit);
94 html("</td><td>"); 94 html("</td><td>");
95 html_txt(info->author); 95 html_txt(info->author);
96 if (ctx.repo->enable_log_filecount) { 96 if (ctx.repo->enable_log_filecount) {
97 files = 0; 97 files = 0;
98 add_lines = 0; 98 add_lines = 0;
99 rem_lines = 0; 99 rem_lines = 0;
100 cgit_diff_commit(commit, inspect_files); 100 cgit_diff_commit(commit, inspect_files);
101 html("</td><td>"); 101 html("</td><td>");
102 htmlf("%d", files); 102 htmlf("%d", files);
103 if (ctx.repo->enable_log_linecount) { 103 if (ctx.repo->enable_log_linecount) {
104 html("</td><td>"); 104 html("</td><td>");
105 htmlf("-%d/+%d", rem_lines, add_lines); 105 htmlf("-%d/+%d", rem_lines, add_lines);
106 } 106 }
107 } 107 }
108 html("</td></tr>\n"); 108 html("</td></tr>\n");
109 if (ctx.qry.showmsg) { 109 if (ctx.qry.showmsg) {
110 if (ctx.repo->enable_log_filecount) { 110 if (ctx.repo->enable_log_filecount) {
111 cols++; 111 cols++;
112 if (ctx.repo->enable_log_linecount) 112 if (ctx.repo->enable_log_linecount)
113 cols++; 113 cols++;
114 } 114 }
115 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>", 115 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>",
116 cols); 116 cols);
117 html_txt(info->msg); 117 html_txt(info->msg);
118 html("</td></tr>\n"); 118 html("</td></tr>\n");
119 } 119 }
120 cgit_free_commitinfo(info); 120 cgit_free_commitinfo(info);
121} 121}
122 122
123static const char *disambiguate_ref(const char *ref) 123static const char *disambiguate_ref(const char *ref)
124{ 124{
125 unsigned char sha1[20]; 125 unsigned char sha1[20];
126 const char *longref; 126 const char *longref;
127 127
128 longref = fmt("refs/heads/%s", ref); 128 longref = fmt("refs/heads/%s", ref);
129 if (get_sha1(longref, sha1) == 0) 129 if (get_sha1(longref, sha1) == 0)
130 return longref; 130 return longref;
131 131
132 return ref; 132 return ref;
133} 133}
134 134
135void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, 135void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
136 char *path, int pager) 136 char *path, int pager)
137{ 137{
138 struct rev_info rev; 138 struct rev_info rev;
139 struct commit *commit; 139 struct commit *commit;
140 const char *argv[] = {NULL, NULL, NULL, NULL, NULL}; 140 const char *argv[] = {NULL, NULL, NULL, NULL, NULL};
141 int argc = 2; 141 int argc = 2;
142 int i, columns = 3; 142 int i, columns = 3;
143 143
144 if (!tip) 144 if (!tip)
145 tip = ctx.qry.head; 145 tip = ctx.qry.head;
146 146
147 argv[1] = disambiguate_ref(tip); 147 argv[1] = disambiguate_ref(tip);
148 148
149 if (grep && pattern && (!strcmp(grep, "grep") || 149 if (grep && pattern && (!strcmp(grep, "grep") ||
150 !strcmp(grep, "author") || 150 !strcmp(grep, "author") ||
151 !strcmp(grep, "committer"))) 151 !strcmp(grep, "committer")))
152 argv[argc++] = fmt("--%s=%s", grep, pattern); 152 argv[argc++] = fmt("--%s=%s", grep, pattern);
153 153
154 if (path) { 154 if (path) {
155 argv[argc++] = "--"; 155 argv[argc++] = "--";
156 argv[argc++] = path; 156 argv[argc++] = path;
157 } 157 }
158 init_revisions(&rev, NULL); 158 init_revisions(&rev, NULL);
159 rev.abbrev = DEFAULT_ABBREV; 159 rev.abbrev = DEFAULT_ABBREV;
160 rev.commit_format = CMIT_FMT_DEFAULT; 160 rev.commit_format = CMIT_FMT_DEFAULT;
161 rev.verbose_header = 1; 161 rev.verbose_header = 1;
162 rev.show_root_diff = 0; 162 rev.show_root_diff = 0;
163 setup_revisions(argc, argv, &rev, NULL); 163 setup_revisions(argc, argv, &rev, NULL);
164 load_ref_decorations(); 164 load_ref_decorations(DECORATE_FULL_REFS);
165 rev.show_decorations = 1; 165 rev.show_decorations = 1;
166 rev.grep_filter.regflags |= REG_ICASE; 166 rev.grep_filter.regflags |= REG_ICASE;
167 compile_grep_patterns(&rev.grep_filter); 167 compile_grep_patterns(&rev.grep_filter);
168 prepare_revision_walk(&rev); 168 prepare_revision_walk(&rev);
169 169
170 if (pager) 170 if (pager)
171 html("<table class='list nowrap'>"); 171 html("<table class='list nowrap'>");
172 172
173 html("<tr class='nohover'><th class='left'>Age</th>" 173 html("<tr class='nohover'><th class='left'>Age</th>"
174 "<th class='left'>Commit message"); 174 "<th class='left'>Commit message");
175 if (pager) { 175 if (pager) {
176 html(" ("); 176 html(" (");
177 cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL, 177 cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
178 NULL, ctx.qry.head, ctx.qry.sha1, 178 NULL, ctx.qry.head, ctx.qry.sha1,
179 ctx.qry.path, ctx.qry.ofs, ctx.qry.grep, 179 ctx.qry.path, ctx.qry.ofs, ctx.qry.grep,
180 ctx.qry.search, ctx.qry.showmsg ? 0 : 1); 180 ctx.qry.search, ctx.qry.showmsg ? 0 : 1);
181 html(")"); 181 html(")");
182 } 182 }
183 html("</th><th class='left'>Author</th>"); 183 html("</th><th class='left'>Author</th>");
184 if (ctx.repo->enable_log_filecount) { 184 if (ctx.repo->enable_log_filecount) {
185 html("<th class='left'>Files</th>"); 185 html("<th class='left'>Files</th>");
186 columns++; 186 columns++;
187 if (ctx.repo->enable_log_linecount) { 187 if (ctx.repo->enable_log_linecount) {
188 html("<th class='left'>Lines</th>"); 188 html("<th class='left'>Lines</th>");
189 columns++; 189 columns++;
190 } 190 }
191 } 191 }
192 html("</tr>\n"); 192 html("</tr>\n");
193 193
194 if (ofs<0) 194 if (ofs<0)
195 ofs = 0; 195 ofs = 0;
196 196
197 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { 197 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
198 free(commit->buffer); 198 free(commit->buffer);
199 commit->buffer = NULL; 199 commit->buffer = NULL;
200 free_commit_list(commit->parents); 200 free_commit_list(commit->parents);
201 commit->parents = NULL; 201 commit->parents = NULL;
202 } 202 }
203 203
204 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { 204 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
205 print_commit(commit); 205 print_commit(commit);
206 free(commit->buffer); 206 free(commit->buffer);
207 commit->buffer = NULL; 207 commit->buffer = NULL;
208 free_commit_list(commit->parents); 208 free_commit_list(commit->parents);
209 commit->parents = NULL; 209 commit->parents = NULL;
210 } 210 }
211 if (pager) { 211 if (pager) {
212 htmlf("</table><div class='pager'>", 212 htmlf("</table><div class='pager'>",
213 columns); 213 columns);
214 if (ofs > 0) { 214 if (ofs > 0) {
215 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, 215 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
216 ctx.qry.sha1, ctx.qry.path, 216 ctx.qry.sha1, ctx.qry.path,
217 ofs - cnt, ctx.qry.grep, 217 ofs - cnt, ctx.qry.grep,
218 ctx.qry.search, ctx.qry.showmsg); 218 ctx.qry.search, ctx.qry.showmsg);
219 html("&nbsp;"); 219 html("&nbsp;");
220 } 220 }
221 if ((commit = get_revision(&rev)) != NULL) { 221 if ((commit = get_revision(&rev)) != NULL) {
222 cgit_log_link("[next]", NULL, NULL, ctx.qry.head, 222 cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
223 ctx.qry.sha1, ctx.qry.path, 223 ctx.qry.sha1, ctx.qry.path,
224 ofs + cnt, ctx.qry.grep, 224 ofs + cnt, ctx.qry.grep,
225 ctx.qry.search, ctx.qry.showmsg); 225 ctx.qry.search, ctx.qry.showmsg);
226 } 226 }
227 html("</div>"); 227 html("</div>");
228 } else if ((commit = get_revision(&rev)) != NULL) { 228 } else if ((commit = get_revision(&rev)) != NULL) {
229 html("<tr class='nohover'><td colspan='3'>"); 229 html("<tr class='nohover'><td colspan='3'>");
230 cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, NULL, 0, 230 cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, NULL, 0,
231 NULL, NULL, ctx.qry.showmsg); 231 NULL, NULL, ctx.qry.showmsg);
232 html("</td></tr>\n"); 232 html("</td></tr>\n");
233 } 233 }
234} 234}
diff --git a/ui-plain.c b/ui-plain.c
index 27c6dae..a4ce077 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -1,93 +1,94 @@
1/* ui-plain.c: functions for output of plain blobs by path 1/* ui-plain.c: functions for output of plain blobs by path
2 * 2 *
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13char *curr_rev; 13char *curr_rev;
14char *match_path; 14char *match_path;
15int match; 15int match;
16 16
17static void print_object(const unsigned char *sha1, const char *path) 17static void print_object(const unsigned char *sha1, const char *path)
18{ 18{
19 enum object_type type; 19 enum object_type type;
20 char *buf, *ext; 20 char *buf, *ext;
21 unsigned long size; 21 unsigned long size;
22 struct string_list_item *mime; 22 struct string_list_item *mime;
23 23
24 type = sha1_object_info(sha1, &size); 24 type = sha1_object_info(sha1, &size);
25 if (type == OBJ_BAD) { 25 if (type == OBJ_BAD) {
26 html_status(404, "Not found", 0); 26 html_status(404, "Not found", 0);
27 return; 27 return;
28 } 28 }
29 29
30 buf = read_sha1_file(sha1, &type, &size); 30 buf = read_sha1_file(sha1, &type, &size);
31 if (!buf) { 31 if (!buf) {
32 html_status(404, "Not found", 0); 32 html_status(404, "Not found", 0);
33 return; 33 return;
34 } 34 }
35 ctx.page.mimetype = NULL; 35 ctx.page.mimetype = NULL;
36 ext = strrchr(path, '.'); 36 ext = strrchr(path, '.');
37 if (ext && *(++ext)) { 37 if (ext && *(++ext)) {
38 mime = string_list_lookup(ext, &ctx.cfg.mimetypes); 38 mime = string_list_lookup(ext, &ctx.cfg.mimetypes);
39 if (mime) 39 if (mime)
40 ctx.page.mimetype = (char *)mime->util; 40 ctx.page.mimetype = (char *)mime->util;
41 } 41 }
42 if (!ctx.page.mimetype) { 42 if (!ctx.page.mimetype) {
43 if (buffer_is_binary(buf, size)) 43 if (buffer_is_binary(buf, size))
44 ctx.page.mimetype = "application/octet-stream"; 44 ctx.page.mimetype = "application/octet-stream";
45 else 45 else
46 ctx.page.mimetype = "text/plain"; 46 ctx.page.mimetype = "text/plain";
47 } 47 }
48 ctx.page.filename = fmt("%s", path); 48 ctx.page.filename = fmt("%s", path);
49 ctx.page.size = size; 49 ctx.page.size = size;
50 ctx.page.etag = sha1_to_hex(sha1); 50 ctx.page.etag = sha1_to_hex(sha1);
51 cgit_print_http_headers(&ctx); 51 cgit_print_http_headers(&ctx);
52 html_raw(buf, size); 52 html_raw(buf, size);
53 match = 1; 53 match = 1;
54} 54}
55 55
56static int walk_tree(const unsigned char *sha1, const char *base, int baselen, 56static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
57 const char *pathname, unsigned mode, int stage, 57 const char *pathname, unsigned mode, int stage,
58 void *cbdata) 58 void *cbdata)
59{ 59{
60 if (S_ISDIR(mode)) 60 if (S_ISDIR(mode))
61 return READ_TREE_RECURSIVE; 61 return READ_TREE_RECURSIVE;
62 62
63 if (S_ISREG(mode)) 63 if (S_ISREG(mode) && !strncmp(base, match_path, baselen) &&
64 !strcmp(pathname, match_path + baselen))
64 print_object(sha1, pathname); 65 print_object(sha1, pathname);
65 66
66 return 0; 67 return 0;
67} 68}
68 69
69void cgit_print_plain(struct cgit_context *ctx) 70void cgit_print_plain(struct cgit_context *ctx)
70{ 71{
71 const char *rev = ctx->qry.sha1; 72 const char *rev = ctx->qry.sha1;
72 unsigned char sha1[20]; 73 unsigned char sha1[20];
73 struct commit *commit; 74 struct commit *commit;
74 const char *paths[] = {ctx->qry.path, NULL}; 75 const char *paths[] = {ctx->qry.path, NULL};
75 76
76 if (!rev) 77 if (!rev)
77 rev = ctx->qry.head; 78 rev = ctx->qry.head;
78 79
79 curr_rev = xstrdup(rev); 80 curr_rev = xstrdup(rev);
80 if (get_sha1(rev, sha1)) { 81 if (get_sha1(rev, sha1)) {
81 html_status(404, "Not found", 0); 82 html_status(404, "Not found", 0);
82 return; 83 return;
83 } 84 }
84 commit = lookup_commit_reference(sha1); 85 commit = lookup_commit_reference(sha1);
85 if (!commit || parse_commit(commit)) { 86 if (!commit || parse_commit(commit)) {
86 html_status(404, "Not found", 0); 87 html_status(404, "Not found", 0);
87 return; 88 return;
88 } 89 }
89 match_path = ctx->qry.path; 90 match_path = ctx->qry.path;
90 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); 91 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL);
91 if (!match) 92 if (!match)
92 html_status(404, "Not found", 0); 93 html_status(404, "Not found", 0);
93} 94}
diff --git a/ui-tree.c b/ui-tree.c
index c608754..f53ab64 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -1,239 +1,246 @@
1/* ui-tree.c: functions for tree output 1/* ui-tree.c: functions for tree output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include <ctype.h> 9#include <ctype.h>
10#include "cgit.h" 10#include "cgit.h"
11#include "html.h" 11#include "html.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13 13
14char *curr_rev; 14char *curr_rev;
15char *match_path; 15char *match_path;
16int header = 0; 16int header = 0;
17 17
18static void print_text_buffer(const char *name, char *buf, unsigned long size) 18static void print_text_buffer(const char *name, char *buf, unsigned long size)
19{ 19{
20 unsigned long lineno, idx; 20 unsigned long lineno, idx;
21 const char *numberfmt = 21 const char *numberfmt =
22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; 22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n";
23 23
24 html("<table summary='blob content' class='blob'>\n"); 24 html("<table summary='blob content' class='blob'>\n");
25
26 if (ctx.cfg.enable_tree_linenumbers) {
27 html("<tr><td class='linenumbers'><pre>");
28 idx = 0;
29 lineno = 0;
30
31 if (size) {
32 htmlf(numberfmt, ++lineno);
33 while(idx < size - 1) { // skip absolute last newline
34 if (buf[idx] == '\n')
35 htmlf(numberfmt, ++lineno);
36 idx++;
37 }
38 }
39 html("</pre></td>\n");
40 }
41 else {
42 html("<tr>\n");
43 }
44
25 if (ctx.repo->source_filter) { 45 if (ctx.repo->source_filter) {
26 html("<tr><td class='lines'><pre><code>"); 46 html("<td class='lines'><pre><code>");
27 ctx.repo->source_filter->argv[1] = xstrdup(name); 47 ctx.repo->source_filter->argv[1] = xstrdup(name);
28 cgit_open_filter(ctx.repo->source_filter); 48 cgit_open_filter(ctx.repo->source_filter);
29 write(STDOUT_FILENO, buf, size); 49 write(STDOUT_FILENO, buf, size);
30 cgit_close_filter(ctx.repo->source_filter); 50 cgit_close_filter(ctx.repo->source_filter);
31 html("</code></pre></td></tr></table>\n"); 51 html("</code></pre></td></tr></table>\n");
32 return; 52 return;
33 } 53 }
34 54
35 html("<tr><td class='linenumbers'><pre>");
36 idx = 0;
37 lineno = 0;
38
39 if (size) {
40 htmlf(numberfmt, ++lineno);
41 while(idx < size - 1) { // skip absolute last newline
42 if (buf[idx] == '\n')
43 htmlf(numberfmt, ++lineno);
44 idx++;
45 }
46 }
47 html("</pre></td>\n");
48 html("<td class='lines'><pre><code>"); 55 html("<td class='lines'><pre><code>");
49 html_txt(buf); 56 html_txt(buf);
50 html("</code></pre></td></tr></table>\n"); 57 html("</code></pre></td></tr></table>\n");
51} 58}
52 59
53#define ROWLEN 32 60#define ROWLEN 32
54 61
55static void print_binary_buffer(char *buf, unsigned long size) 62static void print_binary_buffer(char *buf, unsigned long size)
56{ 63{
57 unsigned long ofs, idx; 64 unsigned long ofs, idx;
58 static char ascii[ROWLEN + 1]; 65 static char ascii[ROWLEN + 1];
59 66
60 html("<table summary='blob content' class='bin-blob'>\n"); 67 html("<table summary='blob content' class='bin-blob'>\n");
61 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); 68 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>");
62 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { 69 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) {
63 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); 70 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs);
64 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 71 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
65 htmlf("%*s%02x", 72 htmlf("%*s%02x",
66 idx == 16 ? 4 : 1, "", 73 idx == 16 ? 4 : 1, "",
67 buf[idx] & 0xff); 74 buf[idx] & 0xff);
68 html(" </td><td class='hex'>"); 75 html(" </td><td class='hex'>");
69 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 76 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
70 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; 77 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.';
71 ascii[idx] = '\0'; 78 ascii[idx] = '\0';
72 html_txt(ascii); 79 html_txt(ascii);
73 html("</td></tr>\n"); 80 html("</td></tr>\n");
74 } 81 }
75 html("</table>\n"); 82 html("</table>\n");
76} 83}
77 84
78static void print_object(const unsigned char *sha1, char *path, const char *basename) 85static void print_object(const unsigned char *sha1, char *path, const char *basename)
79{ 86{
80 enum object_type type; 87 enum object_type type;
81 char *buf; 88 char *buf;
82 unsigned long size; 89 unsigned long size;
83 90
84 type = sha1_object_info(sha1, &size); 91 type = sha1_object_info(sha1, &size);
85 if (type == OBJ_BAD) { 92 if (type == OBJ_BAD) {
86 cgit_print_error(fmt("Bad object name: %s", 93 cgit_print_error(fmt("Bad object name: %s",
87 sha1_to_hex(sha1))); 94 sha1_to_hex(sha1)));
88 return; 95 return;
89 } 96 }
90 97
91 buf = read_sha1_file(sha1, &type, &size); 98 buf = read_sha1_file(sha1, &type, &size);
92 if (!buf) { 99 if (!buf) {
93 cgit_print_error(fmt("Error reading object %s", 100 cgit_print_error(fmt("Error reading object %s",
94 sha1_to_hex(sha1))); 101 sha1_to_hex(sha1)));
95 return; 102 return;
96 } 103 }
97 104
98 html(" ("); 105 html(" (");
99 cgit_plain_link("plain", NULL, NULL, ctx.qry.head, 106 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
100 curr_rev, path); 107 curr_rev, path);
101 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); 108 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1));
102 109
103 if (buffer_is_binary(buf, size)) 110 if (buffer_is_binary(buf, size))
104 print_binary_buffer(buf, size); 111 print_binary_buffer(buf, size);
105 else 112 else
106 print_text_buffer(basename, buf, size); 113 print_text_buffer(basename, buf, size);
107} 114}
108 115
109 116
110static int ls_item(const unsigned char *sha1, const char *base, int baselen, 117static int ls_item(const unsigned char *sha1, const char *base, int baselen,
111 const char *pathname, unsigned int mode, int stage, 118 const char *pathname, unsigned int mode, int stage,
112 void *cbdata) 119 void *cbdata)
113{ 120{
114 char *name; 121 char *name;
115 char *fullpath; 122 char *fullpath;
116 char *class; 123 char *class;
117 enum object_type type; 124 enum object_type type;
118 unsigned long size = 0; 125 unsigned long size = 0;
119 126
120 name = xstrdup(pathname); 127 name = xstrdup(pathname);
121 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 128 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
122 ctx.qry.path ? "/" : "", name); 129 ctx.qry.path ? "/" : "", name);
123 130
124 if (!S_ISGITLINK(mode)) { 131 if (!S_ISGITLINK(mode)) {
125 type = sha1_object_info(sha1, &size); 132 type = sha1_object_info(sha1, &size);
126 if (type == OBJ_BAD) { 133 if (type == OBJ_BAD) {
127 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 134 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
128 name, 135 name,
129 sha1_to_hex(sha1)); 136 sha1_to_hex(sha1));
130 return 0; 137 return 0;
131 } 138 }
132 } 139 }
133 140
134 html("<tr><td class='ls-mode'>"); 141 html("<tr><td class='ls-mode'>");
135 cgit_print_filemode(mode); 142 cgit_print_filemode(mode);
136 html("</td><td>"); 143 html("</td><td>");
137 if (S_ISGITLINK(mode)) { 144 if (S_ISGITLINK(mode)) {
138 htmlf("<a class='ls-mod' href='"); 145 htmlf("<a class='ls-mod' href='");
139 html_attr(fmt(ctx.repo->module_link, 146 html_attr(fmt(ctx.repo->module_link,
140 name, 147 name,
141 sha1_to_hex(sha1))); 148 sha1_to_hex(sha1)));
142 html("'>"); 149 html("'>");
143 html_txt(name); 150 html_txt(name);
144 html("</a>"); 151 html("</a>");
145 } else if (S_ISDIR(mode)) { 152 } else if (S_ISDIR(mode)) {
146 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, 153 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
147 curr_rev, fullpath); 154 curr_rev, fullpath);
148 } else { 155 } else {
149 class = strrchr(name, '.'); 156 class = strrchr(name, '.');
150 if (class != NULL) { 157 if (class != NULL) {
151 class = fmt("ls-blob %s", class + 1); 158 class = fmt("ls-blob %s", class + 1);
152 } else 159 } else
153 class = "ls-blob"; 160 class = "ls-blob";
154 cgit_tree_link(name, NULL, class, ctx.qry.head, 161 cgit_tree_link(name, NULL, class, ctx.qry.head,
155 curr_rev, fullpath); 162 curr_rev, fullpath);
156 } 163 }
157 htmlf("</td><td class='ls-size'>%li</td>", size); 164 htmlf("</td><td class='ls-size'>%li</td>", size);
158 165
159 html("<td>"); 166 html("<td>");
160 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, 167 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev,
161 fullpath, 0, NULL, NULL, ctx.qry.showmsg); 168 fullpath, 0, NULL, NULL, ctx.qry.showmsg);
162 if (ctx.repo->max_stats) 169 if (ctx.repo->max_stats)
163 cgit_stats_link("stats", NULL, "button", ctx.qry.head, 170 cgit_stats_link("stats", NULL, "button", ctx.qry.head,
164 fullpath); 171 fullpath);
165 html("</td></tr>\n"); 172 html("</td></tr>\n");
166 free(name); 173 free(name);
167 return 0; 174 return 0;
168} 175}
169 176
170static void ls_head() 177static void ls_head()
171{ 178{
172 html("<table summary='tree listing' class='list'>\n"); 179 html("<table summary='tree listing' class='list'>\n");
173 html("<tr class='nohover'>"); 180 html("<tr class='nohover'>");
174 html("<th class='left'>Mode</th>"); 181 html("<th class='left'>Mode</th>");
175 html("<th class='left'>Name</th>"); 182 html("<th class='left'>Name</th>");
176 html("<th class='right'>Size</th>"); 183 html("<th class='right'>Size</th>");
177 html("<th/>"); 184 html("<th/>");
178 html("</tr>\n"); 185 html("</tr>\n");
179 header = 1; 186 header = 1;
180} 187}
181 188
182static void ls_tail() 189static void ls_tail()
183{ 190{
184 if (!header) 191 if (!header)
185 return; 192 return;
186 html("</table>\n"); 193 html("</table>\n");
187 header = 0; 194 header = 0;
188} 195}
189 196
190static void ls_tree(const unsigned char *sha1, char *path) 197static void ls_tree(const unsigned char *sha1, char *path)
191{ 198{
192 struct tree *tree; 199 struct tree *tree;
193 200
194 tree = parse_tree_indirect(sha1); 201 tree = parse_tree_indirect(sha1);
195 if (!tree) { 202 if (!tree) {
196 cgit_print_error(fmt("Not a tree object: %s", 203 cgit_print_error(fmt("Not a tree object: %s",
197 sha1_to_hex(sha1))); 204 sha1_to_hex(sha1)));
198 return; 205 return;
199 } 206 }
200 207
201 ls_head(); 208 ls_head();
202 read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); 209 read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL);
203 ls_tail(); 210 ls_tail();
204} 211}
205 212
206 213
207static int walk_tree(const unsigned char *sha1, const char *base, int baselen, 214static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
208 const char *pathname, unsigned mode, int stage, 215 const char *pathname, unsigned mode, int stage,
209 void *cbdata) 216 void *cbdata)
210{ 217{
211 static int state; 218 static int state;
212 static char buffer[PATH_MAX]; 219 static char buffer[PATH_MAX];
213 char *url; 220 char *url;
214 221
215 if (state == 0) { 222 if (state == 0) {
216 memcpy(buffer, base, baselen); 223 memcpy(buffer, base, baselen);
217 strcpy(buffer+baselen, pathname); 224 strcpy(buffer+baselen, pathname);
218 url = cgit_pageurl(ctx.qry.repo, "tree", 225 url = cgit_pageurl(ctx.qry.repo, "tree",
219 fmt("h=%s&amp;path=%s", curr_rev, buffer)); 226 fmt("h=%s&amp;path=%s", curr_rev, buffer));
220 html("/"); 227 html("/");
221 cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, 228 cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head,
222 curr_rev, buffer); 229 curr_rev, buffer);
223 230
224 if (strcmp(match_path, buffer)) 231 if (strcmp(match_path, buffer))
225 return READ_TREE_RECURSIVE; 232 return READ_TREE_RECURSIVE;
226 233
227 if (S_ISDIR(mode)) { 234 if (S_ISDIR(mode)) {
228 state = 1; 235 state = 1;
229 ls_head(); 236 ls_head();
230 return READ_TREE_RECURSIVE; 237 return READ_TREE_RECURSIVE;
231 } else { 238 } else {
232 print_object(sha1, buffer, pathname); 239 print_object(sha1, buffer, pathname);
233 return 0; 240 return 0;
234 } 241 }
235 } 242 }
236 ls_item(sha1, base, baselen, pathname, mode, stage, NULL); 243 ls_item(sha1, base, baselen, pathname, mode, stage, NULL);
237 return 0; 244 return 0;
238} 245}
239 246