summaryrefslogtreecommitdiffabout
authorJohan Herland <johan@herland.net>2010-06-09 23:09:26 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2010-06-19 08:40:22 (UTC)
commit0ff143df7043b7dd87c31c50fa875bc96d1a7779 (patch) (unidiff)
treed1625ba2aafed1ddfebc429921abdbb0d692042f
parent0e34c6d1ef32ea8f69019272fe72dbf2aeaba392 (diff)
downloadcgit-0ff143df7043b7dd87c31c50fa875bc96d1a7779.zip
cgit-0ff143df7043b7dd87c31c50fa875bc96d1a7779.tar.gz
cgit-0ff143df7043b7dd87c31c50fa875bc96d1a7779.tar.bz2
struct cgit_cmd: Differentiate between various usages of ctx.qry.path
For many commands/pages (e.g. 'tree', 'diff', 'plain', etc.), the ctx.qry.path argument is interpreted as a path within the "virtual" project directory structure. However, for some other commands (notably 'refs', and the clone-related commands) ctx.qry.path is used in a different context (as a more or less "real" path within the '.git' directory). This patch differentiates between these two usages of ctx.qry.path, by introducing a new variable - ctx.qry.vpath - which is equal to ctx.qry.path in the former case, and NULL in the latter. This will become useful in future patches when we want various pages and the links between them to preserve existing in-project paths. Signed-off-by: Johan Herland <johan@herland.net> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c6
-rw-r--r--cgit.h1
-rw-r--r--cmd.c42
-rw-r--r--cmd.h3
4 files changed, 30 insertions, 22 deletions
diff --git a/cgit.c b/cgit.c
index 9305d0a..2c3ad73 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,732 +1,738 @@
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, "enable-remote-branches")) 63 else if (!strcmp(name, "enable-remote-branches"))
64 repo->enable_remote_branches = atoi(value); 64 repo->enable_remote_branches = atoi(value);
65 else if (!strcmp(name, "max-stats")) 65 else if (!strcmp(name, "max-stats"))
66 repo->max_stats = cgit_find_stats_period(value, NULL); 66 repo->max_stats = cgit_find_stats_period(value, NULL);
67 else if (!strcmp(name, "module-link")) 67 else if (!strcmp(name, "module-link"))
68 repo->module_link= xstrdup(value); 68 repo->module_link= xstrdup(value);
69 else if (!strcmp(name, "section")) 69 else if (!strcmp(name, "section"))
70 repo->section = xstrdup(value); 70 repo->section = xstrdup(value);
71 else if (!strcmp(name, "readme") && value != NULL) { 71 else if (!strcmp(name, "readme") && value != NULL) {
72 if (*value == '/') 72 if (*value == '/')
73 repo->readme = xstrdup(value); 73 repo->readme = xstrdup(value);
74 else 74 else
75 repo->readme = xstrdup(fmt("%s/%s", repo->path, value)); 75 repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
76 } else if (ctx.cfg.enable_filter_overrides) { 76 } else if (ctx.cfg.enable_filter_overrides) {
77 if (!strcmp(name, "about-filter")) 77 if (!strcmp(name, "about-filter"))
78 repo->about_filter = new_filter(value, 0); 78 repo->about_filter = new_filter(value, 0);
79 else if (!strcmp(name, "commit-filter")) 79 else if (!strcmp(name, "commit-filter"))
80 repo->commit_filter = new_filter(value, 0); 80 repo->commit_filter = new_filter(value, 0);
81 else if (!strcmp(name, "source-filter")) 81 else if (!strcmp(name, "source-filter"))
82 repo->source_filter = new_filter(value, 1); 82 repo->source_filter = new_filter(value, 1);
83 } 83 }
84} 84}
85 85
86void config_cb(const char *name, const char *value) 86void config_cb(const char *name, const char *value)
87{ 87{
88 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 88 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
89 ctx.cfg.section = xstrdup(value); 89 ctx.cfg.section = xstrdup(value);
90 else if (!strcmp(name, "repo.url")) 90 else if (!strcmp(name, "repo.url"))
91 ctx.repo = cgit_add_repo(value); 91 ctx.repo = cgit_add_repo(value);
92 else if (ctx.repo && !strcmp(name, "repo.path")) 92 else if (ctx.repo && !strcmp(name, "repo.path"))
93 ctx.repo->path = trim_end(value, '/'); 93 ctx.repo->path = trim_end(value, '/');
94 else if (ctx.repo && !prefixcmp(name, "repo.")) 94 else if (ctx.repo && !prefixcmp(name, "repo."))
95 repo_config(ctx.repo, name + 5, value); 95 repo_config(ctx.repo, name + 5, value);
96 else if (!strcmp(name, "root-title")) 96 else if (!strcmp(name, "root-title"))
97 ctx.cfg.root_title = xstrdup(value); 97 ctx.cfg.root_title = xstrdup(value);
98 else if (!strcmp(name, "root-desc")) 98 else if (!strcmp(name, "root-desc"))
99 ctx.cfg.root_desc = xstrdup(value); 99 ctx.cfg.root_desc = xstrdup(value);
100 else if (!strcmp(name, "root-readme")) 100 else if (!strcmp(name, "root-readme"))
101 ctx.cfg.root_readme = xstrdup(value); 101 ctx.cfg.root_readme = xstrdup(value);
102 else if (!strcmp(name, "css")) 102 else if (!strcmp(name, "css"))
103 ctx.cfg.css = xstrdup(value); 103 ctx.cfg.css = xstrdup(value);
104 else if (!strcmp(name, "favicon")) 104 else if (!strcmp(name, "favicon"))
105 ctx.cfg.favicon = xstrdup(value); 105 ctx.cfg.favicon = xstrdup(value);
106 else if (!strcmp(name, "footer")) 106 else if (!strcmp(name, "footer"))
107 ctx.cfg.footer = xstrdup(value); 107 ctx.cfg.footer = xstrdup(value);
108 else if (!strcmp(name, "head-include")) 108 else if (!strcmp(name, "head-include"))
109 ctx.cfg.head_include = xstrdup(value); 109 ctx.cfg.head_include = xstrdup(value);
110 else if (!strcmp(name, "header")) 110 else if (!strcmp(name, "header"))
111 ctx.cfg.header = xstrdup(value); 111 ctx.cfg.header = xstrdup(value);
112 else if (!strcmp(name, "logo")) 112 else if (!strcmp(name, "logo"))
113 ctx.cfg.logo = xstrdup(value); 113 ctx.cfg.logo = xstrdup(value);
114 else if (!strcmp(name, "index-header")) 114 else if (!strcmp(name, "index-header"))
115 ctx.cfg.index_header = xstrdup(value); 115 ctx.cfg.index_header = xstrdup(value);
116 else if (!strcmp(name, "index-info")) 116 else if (!strcmp(name, "index-info"))
117 ctx.cfg.index_info = xstrdup(value); 117 ctx.cfg.index_info = xstrdup(value);
118 else if (!strcmp(name, "logo-link")) 118 else if (!strcmp(name, "logo-link"))
119 ctx.cfg.logo_link = xstrdup(value); 119 ctx.cfg.logo_link = xstrdup(value);
120 else if (!strcmp(name, "module-link")) 120 else if (!strcmp(name, "module-link"))
121 ctx.cfg.module_link = xstrdup(value); 121 ctx.cfg.module_link = xstrdup(value);
122 else if (!strcmp(name, "virtual-root")) { 122 else if (!strcmp(name, "virtual-root")) {
123 ctx.cfg.virtual_root = trim_end(value, '/'); 123 ctx.cfg.virtual_root = trim_end(value, '/');
124 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 124 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
125 ctx.cfg.virtual_root = ""; 125 ctx.cfg.virtual_root = "";
126 } else if (!strcmp(name, "nocache")) 126 } else if (!strcmp(name, "nocache"))
127 ctx.cfg.nocache = atoi(value); 127 ctx.cfg.nocache = atoi(value);
128 else if (!strcmp(name, "noplainemail")) 128 else if (!strcmp(name, "noplainemail"))
129 ctx.cfg.noplainemail = atoi(value); 129 ctx.cfg.noplainemail = atoi(value);
130 else if (!strcmp(name, "noheader")) 130 else if (!strcmp(name, "noheader"))
131 ctx.cfg.noheader = atoi(value); 131 ctx.cfg.noheader = atoi(value);
132 else if (!strcmp(name, "snapshots")) 132 else if (!strcmp(name, "snapshots"))
133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
134 else if (!strcmp(name, "enable-filter-overrides")) 134 else if (!strcmp(name, "enable-filter-overrides"))
135 ctx.cfg.enable_filter_overrides = atoi(value); 135 ctx.cfg.enable_filter_overrides = atoi(value);
136 else if (!strcmp(name, "enable-index-links")) 136 else if (!strcmp(name, "enable-index-links"))
137 ctx.cfg.enable_index_links = atoi(value); 137 ctx.cfg.enable_index_links = atoi(value);
138 else if (!strcmp(name, "enable-log-filecount")) 138 else if (!strcmp(name, "enable-log-filecount"))
139 ctx.cfg.enable_log_filecount = atoi(value); 139 ctx.cfg.enable_log_filecount = atoi(value);
140 else if (!strcmp(name, "enable-log-linecount")) 140 else if (!strcmp(name, "enable-log-linecount"))
141 ctx.cfg.enable_log_linecount = atoi(value); 141 ctx.cfg.enable_log_linecount = atoi(value);
142 else if (!strcmp(name, "enable-remote-branches")) 142 else if (!strcmp(name, "enable-remote-branches"))
143 ctx.cfg.enable_remote_branches = atoi(value); 143 ctx.cfg.enable_remote_branches = atoi(value);
144 else if (!strcmp(name, "enable-tree-linenumbers")) 144 else if (!strcmp(name, "enable-tree-linenumbers"))
145 ctx.cfg.enable_tree_linenumbers = atoi(value); 145 ctx.cfg.enable_tree_linenumbers = atoi(value);
146 else if (!strcmp(name, "max-stats")) 146 else if (!strcmp(name, "max-stats"))
147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
148 else if (!strcmp(name, "cache-size")) 148 else if (!strcmp(name, "cache-size"))
149 ctx.cfg.cache_size = atoi(value); 149 ctx.cfg.cache_size = atoi(value);
150 else if (!strcmp(name, "cache-root")) 150 else if (!strcmp(name, "cache-root"))
151 ctx.cfg.cache_root = xstrdup(value); 151 ctx.cfg.cache_root = xstrdup(value);
152 else if (!strcmp(name, "cache-root-ttl")) 152 else if (!strcmp(name, "cache-root-ttl"))
153 ctx.cfg.cache_root_ttl = atoi(value); 153 ctx.cfg.cache_root_ttl = atoi(value);
154 else if (!strcmp(name, "cache-repo-ttl")) 154 else if (!strcmp(name, "cache-repo-ttl"))
155 ctx.cfg.cache_repo_ttl = atoi(value); 155 ctx.cfg.cache_repo_ttl = atoi(value);
156 else if (!strcmp(name, "cache-scanrc-ttl")) 156 else if (!strcmp(name, "cache-scanrc-ttl"))
157 ctx.cfg.cache_scanrc_ttl = atoi(value); 157 ctx.cfg.cache_scanrc_ttl = atoi(value);
158 else if (!strcmp(name, "cache-static-ttl")) 158 else if (!strcmp(name, "cache-static-ttl"))
159 ctx.cfg.cache_static_ttl = atoi(value); 159 ctx.cfg.cache_static_ttl = atoi(value);
160 else if (!strcmp(name, "cache-dynamic-ttl")) 160 else if (!strcmp(name, "cache-dynamic-ttl"))
161 ctx.cfg.cache_dynamic_ttl = atoi(value); 161 ctx.cfg.cache_dynamic_ttl = atoi(value);
162 else if (!strcmp(name, "about-filter")) 162 else if (!strcmp(name, "about-filter"))
163 ctx.cfg.about_filter = new_filter(value, 0); 163 ctx.cfg.about_filter = new_filter(value, 0);
164 else if (!strcmp(name, "commit-filter")) 164 else if (!strcmp(name, "commit-filter"))
165 ctx.cfg.commit_filter = new_filter(value, 0); 165 ctx.cfg.commit_filter = new_filter(value, 0);
166 else if (!strcmp(name, "embedded")) 166 else if (!strcmp(name, "embedded"))
167 ctx.cfg.embedded = atoi(value); 167 ctx.cfg.embedded = atoi(value);
168 else if (!strcmp(name, "max-message-length")) 168 else if (!strcmp(name, "max-message-length"))
169 ctx.cfg.max_msg_len = atoi(value); 169 ctx.cfg.max_msg_len = atoi(value);
170 else if (!strcmp(name, "max-repodesc-length")) 170 else if (!strcmp(name, "max-repodesc-length"))
171 ctx.cfg.max_repodesc_len = atoi(value); 171 ctx.cfg.max_repodesc_len = atoi(value);
172 else if (!strcmp(name, "max-blob-size")) 172 else if (!strcmp(name, "max-blob-size"))
173 ctx.cfg.max_blob_size = atoi(value); 173 ctx.cfg.max_blob_size = atoi(value);
174 else if (!strcmp(name, "max-repo-count")) 174 else if (!strcmp(name, "max-repo-count"))
175 ctx.cfg.max_repo_count = atoi(value); 175 ctx.cfg.max_repo_count = atoi(value);
176 else if (!strcmp(name, "max-commit-count")) 176 else if (!strcmp(name, "max-commit-count"))
177 ctx.cfg.max_commit_count = atoi(value); 177 ctx.cfg.max_commit_count = atoi(value);
178 else if (!strcmp(name, "scan-path")) 178 else if (!strcmp(name, "scan-path"))
179 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 179 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
180 process_cached_repolist(value); 180 process_cached_repolist(value);
181 else 181 else
182 scan_tree(value, repo_config); 182 scan_tree(value, repo_config);
183 else if (!strcmp(name, "source-filter")) 183 else if (!strcmp(name, "source-filter"))
184 ctx.cfg.source_filter = new_filter(value, 1); 184 ctx.cfg.source_filter = new_filter(value, 1);
185 else if (!strcmp(name, "summary-log")) 185 else if (!strcmp(name, "summary-log"))
186 ctx.cfg.summary_log = atoi(value); 186 ctx.cfg.summary_log = atoi(value);
187 else if (!strcmp(name, "summary-branches")) 187 else if (!strcmp(name, "summary-branches"))
188 ctx.cfg.summary_branches = atoi(value); 188 ctx.cfg.summary_branches = atoi(value);
189 else if (!strcmp(name, "summary-tags")) 189 else if (!strcmp(name, "summary-tags"))
190 ctx.cfg.summary_tags = atoi(value); 190 ctx.cfg.summary_tags = atoi(value);
191 else if (!strcmp(name, "side-by-side-diffs")) 191 else if (!strcmp(name, "side-by-side-diffs"))
192 ctx.cfg.ssdiff = atoi(value); 192 ctx.cfg.ssdiff = atoi(value);
193 else if (!strcmp(name, "agefile")) 193 else if (!strcmp(name, "agefile"))
194 ctx.cfg.agefile = xstrdup(value); 194 ctx.cfg.agefile = xstrdup(value);
195 else if (!strcmp(name, "renamelimit")) 195 else if (!strcmp(name, "renamelimit"))
196 ctx.cfg.renamelimit = atoi(value); 196 ctx.cfg.renamelimit = atoi(value);
197 else if (!strcmp(name, "robots")) 197 else if (!strcmp(name, "robots"))
198 ctx.cfg.robots = xstrdup(value); 198 ctx.cfg.robots = xstrdup(value);
199 else if (!strcmp(name, "clone-prefix")) 199 else if (!strcmp(name, "clone-prefix"))
200 ctx.cfg.clone_prefix = xstrdup(value); 200 ctx.cfg.clone_prefix = xstrdup(value);
201 else if (!strcmp(name, "local-time")) 201 else if (!strcmp(name, "local-time"))
202 ctx.cfg.local_time = atoi(value); 202 ctx.cfg.local_time = atoi(value);
203 else if (!prefixcmp(name, "mimetype.")) 203 else if (!prefixcmp(name, "mimetype."))
204 add_mimetype(name + 9, value); 204 add_mimetype(name + 9, value);
205 else if (!strcmp(name, "include")) 205 else if (!strcmp(name, "include"))
206 parse_configfile(value, config_cb); 206 parse_configfile(value, config_cb);
207} 207}
208 208
209static void querystring_cb(const char *name, const char *value) 209static void querystring_cb(const char *name, const char *value)
210{ 210{
211 if (!value) 211 if (!value)
212 value = ""; 212 value = "";
213 213
214 if (!strcmp(name,"r")) { 214 if (!strcmp(name,"r")) {
215 ctx.qry.repo = xstrdup(value); 215 ctx.qry.repo = xstrdup(value);
216 ctx.repo = cgit_get_repoinfo(value); 216 ctx.repo = cgit_get_repoinfo(value);
217 } else if (!strcmp(name, "p")) { 217 } else if (!strcmp(name, "p")) {
218 ctx.qry.page = xstrdup(value); 218 ctx.qry.page = xstrdup(value);
219 } else if (!strcmp(name, "url")) { 219 } else if (!strcmp(name, "url")) {
220 if (*value == '/') 220 if (*value == '/')
221 value++; 221 value++;
222 ctx.qry.url = xstrdup(value); 222 ctx.qry.url = xstrdup(value);
223 cgit_parse_url(value); 223 cgit_parse_url(value);
224 } else if (!strcmp(name, "qt")) { 224 } else if (!strcmp(name, "qt")) {
225 ctx.qry.grep = xstrdup(value); 225 ctx.qry.grep = xstrdup(value);
226 } else if (!strcmp(name, "q")) { 226 } else if (!strcmp(name, "q")) {
227 ctx.qry.search = xstrdup(value); 227 ctx.qry.search = xstrdup(value);
228 } else if (!strcmp(name, "h")) { 228 } else if (!strcmp(name, "h")) {
229 ctx.qry.head = xstrdup(value); 229 ctx.qry.head = xstrdup(value);
230 ctx.qry.has_symref = 1; 230 ctx.qry.has_symref = 1;
231 } else if (!strcmp(name, "id")) { 231 } else if (!strcmp(name, "id")) {
232 ctx.qry.sha1 = xstrdup(value); 232 ctx.qry.sha1 = xstrdup(value);
233 ctx.qry.has_sha1 = 1; 233 ctx.qry.has_sha1 = 1;
234 } else if (!strcmp(name, "id2")) { 234 } else if (!strcmp(name, "id2")) {
235 ctx.qry.sha2 = xstrdup(value); 235 ctx.qry.sha2 = xstrdup(value);
236 ctx.qry.has_sha1 = 1; 236 ctx.qry.has_sha1 = 1;
237 } else if (!strcmp(name, "ofs")) { 237 } else if (!strcmp(name, "ofs")) {
238 ctx.qry.ofs = atoi(value); 238 ctx.qry.ofs = atoi(value);
239 } else if (!strcmp(name, "path")) { 239 } else if (!strcmp(name, "path")) {
240 ctx.qry.path = trim_end(value, '/'); 240 ctx.qry.path = trim_end(value, '/');
241 } else if (!strcmp(name, "name")) { 241 } else if (!strcmp(name, "name")) {
242 ctx.qry.name = xstrdup(value); 242 ctx.qry.name = xstrdup(value);
243 } else if (!strcmp(name, "mimetype")) { 243 } else if (!strcmp(name, "mimetype")) {
244 ctx.qry.mimetype = xstrdup(value); 244 ctx.qry.mimetype = xstrdup(value);
245 } else if (!strcmp(name, "s")){ 245 } else if (!strcmp(name, "s")){
246 ctx.qry.sort = xstrdup(value); 246 ctx.qry.sort = xstrdup(value);
247 } else if (!strcmp(name, "showmsg")) { 247 } else if (!strcmp(name, "showmsg")) {
248 ctx.qry.showmsg = atoi(value); 248 ctx.qry.showmsg = atoi(value);
249 } else if (!strcmp(name, "period")) { 249 } else if (!strcmp(name, "period")) {
250 ctx.qry.period = xstrdup(value); 250 ctx.qry.period = xstrdup(value);
251 } else if (!strcmp(name, "ss")) { 251 } else if (!strcmp(name, "ss")) {
252 ctx.qry.ssdiff = atoi(value); 252 ctx.qry.ssdiff = atoi(value);
253 } 253 }
254} 254}
255 255
256char *xstrdupn(const char *str) 256char *xstrdupn(const char *str)
257{ 257{
258 return (str ? xstrdup(str) : NULL); 258 return (str ? xstrdup(str) : NULL);
259} 259}
260 260
261static void prepare_context(struct cgit_context *ctx) 261static void prepare_context(struct cgit_context *ctx)
262{ 262{
263 memset(ctx, 0, sizeof(*ctx)); 263 memset(ctx, 0, sizeof(*ctx));
264 ctx->cfg.agefile = "info/web/last-modified"; 264 ctx->cfg.agefile = "info/web/last-modified";
265 ctx->cfg.nocache = 0; 265 ctx->cfg.nocache = 0;
266 ctx->cfg.cache_size = 0; 266 ctx->cfg.cache_size = 0;
267 ctx->cfg.cache_dynamic_ttl = 5; 267 ctx->cfg.cache_dynamic_ttl = 5;
268 ctx->cfg.cache_max_create_time = 5; 268 ctx->cfg.cache_max_create_time = 5;
269 ctx->cfg.cache_repo_ttl = 5; 269 ctx->cfg.cache_repo_ttl = 5;
270 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 270 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
271 ctx->cfg.cache_root_ttl = 5; 271 ctx->cfg.cache_root_ttl = 5;
272 ctx->cfg.cache_scanrc_ttl = 15; 272 ctx->cfg.cache_scanrc_ttl = 15;
273 ctx->cfg.cache_static_ttl = -1; 273 ctx->cfg.cache_static_ttl = -1;
274 ctx->cfg.css = "/cgit.css"; 274 ctx->cfg.css = "/cgit.css";
275 ctx->cfg.logo = "/cgit.png"; 275 ctx->cfg.logo = "/cgit.png";
276 ctx->cfg.local_time = 0; 276 ctx->cfg.local_time = 0;
277 ctx->cfg.enable_tree_linenumbers = 1; 277 ctx->cfg.enable_tree_linenumbers = 1;
278 ctx->cfg.max_repo_count = 50; 278 ctx->cfg.max_repo_count = 50;
279 ctx->cfg.max_commit_count = 50; 279 ctx->cfg.max_commit_count = 50;
280 ctx->cfg.max_lock_attempts = 5; 280 ctx->cfg.max_lock_attempts = 5;
281 ctx->cfg.max_msg_len = 80; 281 ctx->cfg.max_msg_len = 80;
282 ctx->cfg.max_repodesc_len = 80; 282 ctx->cfg.max_repodesc_len = 80;
283 ctx->cfg.max_blob_size = 0; 283 ctx->cfg.max_blob_size = 0;
284 ctx->cfg.max_stats = 0; 284 ctx->cfg.max_stats = 0;
285 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 285 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
286 ctx->cfg.renamelimit = -1; 286 ctx->cfg.renamelimit = -1;
287 ctx->cfg.robots = "index, nofollow"; 287 ctx->cfg.robots = "index, nofollow";
288 ctx->cfg.root_title = "Git repository browser"; 288 ctx->cfg.root_title = "Git repository browser";
289 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 289 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
290 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 290 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
291 ctx->cfg.section = ""; 291 ctx->cfg.section = "";
292 ctx->cfg.summary_branches = 10; 292 ctx->cfg.summary_branches = 10;
293 ctx->cfg.summary_log = 10; 293 ctx->cfg.summary_log = 10;
294 ctx->cfg.summary_tags = 10; 294 ctx->cfg.summary_tags = 10;
295 ctx->cfg.ssdiff = 0; 295 ctx->cfg.ssdiff = 0;
296 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 296 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
297 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 297 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
298 ctx->env.https = xstrdupn(getenv("HTTPS")); 298 ctx->env.https = xstrdupn(getenv("HTTPS"));
299 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 299 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
300 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 300 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
301 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 301 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
302 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 302 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
303 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 303 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
304 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 304 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
305 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 305 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
306 ctx->page.mimetype = "text/html"; 306 ctx->page.mimetype = "text/html";
307 ctx->page.charset = PAGE_ENCODING; 307 ctx->page.charset = PAGE_ENCODING;
308 ctx->page.filename = NULL; 308 ctx->page.filename = NULL;
309 ctx->page.size = 0; 309 ctx->page.size = 0;
310 ctx->page.modified = time(NULL); 310 ctx->page.modified = time(NULL);
311 ctx->page.expires = ctx->page.modified; 311 ctx->page.expires = ctx->page.modified;
312 ctx->page.etag = NULL; 312 ctx->page.etag = NULL;
313 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 313 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
314 if (ctx->env.script_name) 314 if (ctx->env.script_name)
315 ctx->cfg.script_name = ctx->env.script_name; 315 ctx->cfg.script_name = ctx->env.script_name;
316 if (ctx->env.query_string) 316 if (ctx->env.query_string)
317 ctx->qry.raw = ctx->env.query_string; 317 ctx->qry.raw = ctx->env.query_string;
318 if (!ctx->env.cgit_config) 318 if (!ctx->env.cgit_config)
319 ctx->env.cgit_config = CGIT_CONFIG; 319 ctx->env.cgit_config = CGIT_CONFIG;
320} 320}
321 321
322struct refmatch { 322struct refmatch {
323 char *req_ref; 323 char *req_ref;
324 char *first_ref; 324 char *first_ref;
325 int match; 325 int match;
326}; 326};
327 327
328int find_current_ref(const char *refname, const unsigned char *sha1, 328int find_current_ref(const char *refname, const unsigned char *sha1,
329 int flags, void *cb_data) 329 int flags, void *cb_data)
330{ 330{
331 struct refmatch *info; 331 struct refmatch *info;
332 332
333 info = (struct refmatch *)cb_data; 333 info = (struct refmatch *)cb_data;
334 if (!strcmp(refname, info->req_ref)) 334 if (!strcmp(refname, info->req_ref))
335 info->match = 1; 335 info->match = 1;
336 if (!info->first_ref) 336 if (!info->first_ref)
337 info->first_ref = xstrdup(refname); 337 info->first_ref = xstrdup(refname);
338 return info->match; 338 return info->match;
339} 339}
340 340
341char *find_default_branch(struct cgit_repo *repo) 341char *find_default_branch(struct cgit_repo *repo)
342{ 342{
343 struct refmatch info; 343 struct refmatch info;
344 char *ref; 344 char *ref;
345 345
346 info.req_ref = repo->defbranch; 346 info.req_ref = repo->defbranch;
347 info.first_ref = NULL; 347 info.first_ref = NULL;
348 info.match = 0; 348 info.match = 0;
349 for_each_branch_ref(find_current_ref, &info); 349 for_each_branch_ref(find_current_ref, &info);
350 if (info.match) 350 if (info.match)
351 ref = info.req_ref; 351 ref = info.req_ref;
352 else 352 else
353 ref = info.first_ref; 353 ref = info.first_ref;
354 if (ref) 354 if (ref)
355 ref = xstrdup(ref); 355 ref = xstrdup(ref);
356 return ref; 356 return ref;
357} 357}
358 358
359static int prepare_repo_cmd(struct cgit_context *ctx) 359static int prepare_repo_cmd(struct cgit_context *ctx)
360{ 360{
361 char *tmp; 361 char *tmp;
362 unsigned char sha1[20]; 362 unsigned char sha1[20];
363 int nongit = 0; 363 int nongit = 0;
364 364
365 setenv("GIT_DIR", ctx->repo->path, 1); 365 setenv("GIT_DIR", ctx->repo->path, 1);
366 setup_git_directory_gently(&nongit); 366 setup_git_directory_gently(&nongit);
367 if (nongit) { 367 if (nongit) {
368 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 368 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
369 "config error"); 369 "config error");
370 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 370 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
371 ctx->repo = NULL; 371 ctx->repo = NULL;
372 cgit_print_http_headers(ctx); 372 cgit_print_http_headers(ctx);
373 cgit_print_docstart(ctx); 373 cgit_print_docstart(ctx);
374 cgit_print_pageheader(ctx); 374 cgit_print_pageheader(ctx);
375 cgit_print_error(tmp); 375 cgit_print_error(tmp);
376 cgit_print_docend(); 376 cgit_print_docend();
377 return 1; 377 return 1;
378 } 378 }
379 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 379 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
380 380
381 if (!ctx->qry.head) { 381 if (!ctx->qry.head) {
382 ctx->qry.nohead = 1; 382 ctx->qry.nohead = 1;
383 ctx->qry.head = find_default_branch(ctx->repo); 383 ctx->qry.head = find_default_branch(ctx->repo);
384 ctx->repo->defbranch = ctx->qry.head; 384 ctx->repo->defbranch = ctx->qry.head;
385 } 385 }
386 386
387 if (!ctx->qry.head) { 387 if (!ctx->qry.head) {
388 cgit_print_http_headers(ctx); 388 cgit_print_http_headers(ctx);
389 cgit_print_docstart(ctx); 389 cgit_print_docstart(ctx);
390 cgit_print_pageheader(ctx); 390 cgit_print_pageheader(ctx);
391 cgit_print_error("Repository seems to be empty"); 391 cgit_print_error("Repository seems to be empty");
392 cgit_print_docend(); 392 cgit_print_docend();
393 return 1; 393 return 1;
394 } 394 }
395 395
396 if (get_sha1(ctx->qry.head, sha1)) { 396 if (get_sha1(ctx->qry.head, sha1)) {
397 tmp = xstrdup(ctx->qry.head); 397 tmp = xstrdup(ctx->qry.head);
398 ctx->qry.head = ctx->repo->defbranch; 398 ctx->qry.head = ctx->repo->defbranch;
399 ctx->page.status = 404; 399 ctx->page.status = 404;
400 ctx->page.statusmsg = "not found"; 400 ctx->page.statusmsg = "not found";
401 cgit_print_http_headers(ctx); 401 cgit_print_http_headers(ctx);
402 cgit_print_docstart(ctx); 402 cgit_print_docstart(ctx);
403 cgit_print_pageheader(ctx); 403 cgit_print_pageheader(ctx);
404 cgit_print_error(fmt("Invalid branch: %s", tmp)); 404 cgit_print_error(fmt("Invalid branch: %s", tmp));
405 cgit_print_docend(); 405 cgit_print_docend();
406 return 1; 406 return 1;
407 } 407 }
408 return 0; 408 return 0;
409} 409}
410 410
411static void process_request(void *cbdata) 411static void process_request(void *cbdata)
412{ 412{
413 struct cgit_context *ctx = cbdata; 413 struct cgit_context *ctx = cbdata;
414 struct cgit_cmd *cmd; 414 struct cgit_cmd *cmd;
415 415
416 cmd = cgit_get_cmd(ctx); 416 cmd = cgit_get_cmd(ctx);
417 if (!cmd) { 417 if (!cmd) {
418 ctx->page.title = "cgit error"; 418 ctx->page.title = "cgit error";
419 cgit_print_http_headers(ctx); 419 cgit_print_http_headers(ctx);
420 cgit_print_docstart(ctx); 420 cgit_print_docstart(ctx);
421 cgit_print_pageheader(ctx); 421 cgit_print_pageheader(ctx);
422 cgit_print_error("Invalid request"); 422 cgit_print_error("Invalid request");
423 cgit_print_docend(); 423 cgit_print_docend();
424 return; 424 return;
425 } 425 }
426 426
427 /* If cmd->want_vpath is set, assume ctx->qry.path contains a "virtual"
428 * in-project path limit to be made available at ctx->qry.vpath.
429 * Otherwise, no path limit is in effect (ctx->qry.vpath = NULL).
430 */
431 ctx->qry.vpath = cmd->want_vpath ? ctx->qry.path : NULL;
432
427 if (cmd->want_repo && !ctx->repo) { 433 if (cmd->want_repo && !ctx->repo) {
428 cgit_print_http_headers(ctx); 434 cgit_print_http_headers(ctx);
429 cgit_print_docstart(ctx); 435 cgit_print_docstart(ctx);
430 cgit_print_pageheader(ctx); 436 cgit_print_pageheader(ctx);
431 cgit_print_error(fmt("No repository selected")); 437 cgit_print_error(fmt("No repository selected"));
432 cgit_print_docend(); 438 cgit_print_docend();
433 return; 439 return;
434 } 440 }
435 441
436 if (ctx->repo && prepare_repo_cmd(ctx)) 442 if (ctx->repo && prepare_repo_cmd(ctx))
437 return; 443 return;
438 444
439 if (cmd->want_layout) { 445 if (cmd->want_layout) {
440 cgit_print_http_headers(ctx); 446 cgit_print_http_headers(ctx);
441 cgit_print_docstart(ctx); 447 cgit_print_docstart(ctx);
442 cgit_print_pageheader(ctx); 448 cgit_print_pageheader(ctx);
443 } 449 }
444 450
445 cmd->fn(ctx); 451 cmd->fn(ctx);
446 452
447 if (cmd->want_layout) 453 if (cmd->want_layout)
448 cgit_print_docend(); 454 cgit_print_docend();
449} 455}
450 456
451int cmp_repos(const void *a, const void *b) 457int cmp_repos(const void *a, const void *b)
452{ 458{
453 const struct cgit_repo *ra = a, *rb = b; 459 const struct cgit_repo *ra = a, *rb = b;
454 return strcmp(ra->url, rb->url); 460 return strcmp(ra->url, rb->url);
455} 461}
456 462
457char *build_snapshot_setting(int bitmap) 463char *build_snapshot_setting(int bitmap)
458{ 464{
459 const struct cgit_snapshot_format *f; 465 const struct cgit_snapshot_format *f;
460 char *result = xstrdup(""); 466 char *result = xstrdup("");
461 char *tmp; 467 char *tmp;
462 int len; 468 int len;
463 469
464 for (f = cgit_snapshot_formats; f->suffix; f++) { 470 for (f = cgit_snapshot_formats; f->suffix; f++) {
465 if (f->bit & bitmap) { 471 if (f->bit & bitmap) {
466 tmp = result; 472 tmp = result;
467 result = xstrdup(fmt("%s%s ", tmp, f->suffix)); 473 result = xstrdup(fmt("%s%s ", tmp, f->suffix));
468 free(tmp); 474 free(tmp);
469 } 475 }
470 } 476 }
471 len = strlen(result); 477 len = strlen(result);
472 if (len) 478 if (len)
473 result[len - 1] = '\0'; 479 result[len - 1] = '\0';
474 return result; 480 return result;
475} 481}
476 482
477char *get_first_line(char *txt) 483char *get_first_line(char *txt)
478{ 484{
479 char *t = xstrdup(txt); 485 char *t = xstrdup(txt);
480 char *p = strchr(t, '\n'); 486 char *p = strchr(t, '\n');
481 if (p) 487 if (p)
482 *p = '\0'; 488 *p = '\0';
483 return t; 489 return t;
484} 490}
485 491
486void print_repo(FILE *f, struct cgit_repo *repo) 492void print_repo(FILE *f, struct cgit_repo *repo)
487{ 493{
488 fprintf(f, "repo.url=%s\n", repo->url); 494 fprintf(f, "repo.url=%s\n", repo->url);
489 fprintf(f, "repo.name=%s\n", repo->name); 495 fprintf(f, "repo.name=%s\n", repo->name);
490 fprintf(f, "repo.path=%s\n", repo->path); 496 fprintf(f, "repo.path=%s\n", repo->path);
491 if (repo->owner) 497 if (repo->owner)
492 fprintf(f, "repo.owner=%s\n", repo->owner); 498 fprintf(f, "repo.owner=%s\n", repo->owner);
493 if (repo->desc) { 499 if (repo->desc) {
494 char *tmp = get_first_line(repo->desc); 500 char *tmp = get_first_line(repo->desc);
495 fprintf(f, "repo.desc=%s\n", tmp); 501 fprintf(f, "repo.desc=%s\n", tmp);
496 free(tmp); 502 free(tmp);
497 } 503 }
498 if (repo->readme) 504 if (repo->readme)
499 fprintf(f, "repo.readme=%s\n", repo->readme); 505 fprintf(f, "repo.readme=%s\n", repo->readme);
500 if (repo->defbranch) 506 if (repo->defbranch)
501 fprintf(f, "repo.defbranch=%s\n", repo->defbranch); 507 fprintf(f, "repo.defbranch=%s\n", repo->defbranch);
502 if (repo->module_link) 508 if (repo->module_link)
503 fprintf(f, "repo.module-link=%s\n", repo->module_link); 509 fprintf(f, "repo.module-link=%s\n", repo->module_link);
504 if (repo->section) 510 if (repo->section)
505 fprintf(f, "repo.section=%s\n", repo->section); 511 fprintf(f, "repo.section=%s\n", repo->section);
506 if (repo->clone_url) 512 if (repo->clone_url)
507 fprintf(f, "repo.clone-url=%s\n", repo->clone_url); 513 fprintf(f, "repo.clone-url=%s\n", repo->clone_url);
508 fprintf(f, "repo.enable-log-filecount=%d\n", 514 fprintf(f, "repo.enable-log-filecount=%d\n",
509 repo->enable_log_filecount); 515 repo->enable_log_filecount);
510 fprintf(f, "repo.enable-log-linecount=%d\n", 516 fprintf(f, "repo.enable-log-linecount=%d\n",
511 repo->enable_log_linecount); 517 repo->enable_log_linecount);
512 if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter) 518 if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter)
513 fprintf(f, "repo.about-filter=%s\n", repo->about_filter->cmd); 519 fprintf(f, "repo.about-filter=%s\n", repo->about_filter->cmd);
514 if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter) 520 if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter)
515 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd); 521 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd);
516 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter) 522 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
517 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); 523 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd);
518 if (repo->snapshots != ctx.cfg.snapshots) { 524 if (repo->snapshots != ctx.cfg.snapshots) {
519 char *tmp = build_snapshot_setting(repo->snapshots); 525 char *tmp = build_snapshot_setting(repo->snapshots);
520 fprintf(f, "repo.snapshots=%s\n", tmp); 526 fprintf(f, "repo.snapshots=%s\n", tmp);
521 free(tmp); 527 free(tmp);
522 } 528 }
523 if (repo->max_stats != ctx.cfg.max_stats) 529 if (repo->max_stats != ctx.cfg.max_stats)
524 fprintf(f, "repo.max-stats=%s\n", 530 fprintf(f, "repo.max-stats=%s\n",
525 cgit_find_stats_periodname(repo->max_stats)); 531 cgit_find_stats_periodname(repo->max_stats));
526 fprintf(f, "\n"); 532 fprintf(f, "\n");
527} 533}
528 534
529void print_repolist(FILE *f, struct cgit_repolist *list, int start) 535void print_repolist(FILE *f, struct cgit_repolist *list, int start)
530{ 536{
531 int i; 537 int i;
532 538
533 for(i = start; i < list->count; i++) 539 for(i = start; i < list->count; i++)
534 print_repo(f, &list->repos[i]); 540 print_repo(f, &list->repos[i]);
535} 541}
536 542
537/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 543/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
538 * and return 0 on success. 544 * and return 0 on success.
539 */ 545 */
540static int generate_cached_repolist(const char *path, const char *cached_rc) 546static int generate_cached_repolist(const char *path, const char *cached_rc)
541{ 547{
542 char *locked_rc; 548 char *locked_rc;
543 int idx; 549 int idx;
544 FILE *f; 550 FILE *f;
545 551
546 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 552 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
547 f = fopen(locked_rc, "wx"); 553 f = fopen(locked_rc, "wx");
548 if (!f) { 554 if (!f) {
549 /* Inform about the error unless the lockfile already existed, 555 /* Inform about the error unless the lockfile already existed,
550 * since that only means we've got concurrent requests. 556 * since that only means we've got concurrent requests.
551 */ 557 */
552 if (errno != EEXIST) 558 if (errno != EEXIST)
553 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 559 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
554 locked_rc, strerror(errno), errno); 560 locked_rc, strerror(errno), errno);
555 return errno; 561 return errno;
556 } 562 }
557 idx = cgit_repolist.count; 563 idx = cgit_repolist.count;
558 scan_tree(path, repo_config); 564 scan_tree(path, repo_config);
559 print_repolist(f, &cgit_repolist, idx); 565 print_repolist(f, &cgit_repolist, idx);
560 if (rename(locked_rc, cached_rc)) 566 if (rename(locked_rc, cached_rc))
561 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 567 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
562 locked_rc, cached_rc, strerror(errno), errno); 568 locked_rc, cached_rc, strerror(errno), errno);
563 fclose(f); 569 fclose(f);
564 return 0; 570 return 0;
565} 571}
566 572
567static void process_cached_repolist(const char *path) 573static void process_cached_repolist(const char *path)
568{ 574{
569 struct stat st; 575 struct stat st;
570 char *cached_rc; 576 char *cached_rc;
571 time_t age; 577 time_t age;
572 578
573 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 579 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root,
574 hash_str(path))); 580 hash_str(path)));
575 581
576 if (stat(cached_rc, &st)) { 582 if (stat(cached_rc, &st)) {
577 /* Nothing is cached, we need to scan without forking. And 583 /* Nothing is cached, we need to scan without forking. And
578 * if we fail to generate a cached repolist, we need to 584 * if we fail to generate a cached repolist, we need to
579 * invoke scan_tree manually. 585 * invoke scan_tree manually.
580 */ 586 */
581 if (generate_cached_repolist(path, cached_rc)) 587 if (generate_cached_repolist(path, cached_rc))
582 scan_tree(path, repo_config); 588 scan_tree(path, repo_config);
583 return; 589 return;
584 } 590 }
585 591
586 parse_configfile(cached_rc, config_cb); 592 parse_configfile(cached_rc, config_cb);
587 593
588 /* If the cached configfile hasn't expired, lets exit now */ 594 /* If the cached configfile hasn't expired, lets exit now */
589 age = time(NULL) - st.st_mtime; 595 age = time(NULL) - st.st_mtime;
590 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 596 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
591 return; 597 return;
592 598
593 /* The cached repolist has been parsed, but it was old. So lets 599 /* The cached repolist has been parsed, but it was old. So lets
594 * rescan the specified path and generate a new cached repolist 600 * rescan the specified path and generate a new cached repolist
595 * in a child-process to avoid latency for the current request. 601 * in a child-process to avoid latency for the current request.
596 */ 602 */
597 if (fork()) 603 if (fork())
598 return; 604 return;
599 605
600 exit(generate_cached_repolist(path, cached_rc)); 606 exit(generate_cached_repolist(path, cached_rc));
601} 607}
602 608
603static void cgit_parse_args(int argc, const char **argv) 609static void cgit_parse_args(int argc, const char **argv)
604{ 610{
605 int i; 611 int i;
606 int scan = 0; 612 int scan = 0;
607 613
608 for (i = 1; i < argc; i++) { 614 for (i = 1; i < argc; i++) {
609 if (!strncmp(argv[i], "--cache=", 8)) { 615 if (!strncmp(argv[i], "--cache=", 8)) {
610 ctx.cfg.cache_root = xstrdup(argv[i]+8); 616 ctx.cfg.cache_root = xstrdup(argv[i]+8);
611 } 617 }
612 if (!strcmp(argv[i], "--nocache")) { 618 if (!strcmp(argv[i], "--nocache")) {
613 ctx.cfg.nocache = 1; 619 ctx.cfg.nocache = 1;
614 } 620 }
615 if (!strcmp(argv[i], "--nohttp")) { 621 if (!strcmp(argv[i], "--nohttp")) {
616 ctx.env.no_http = "1"; 622 ctx.env.no_http = "1";
617 } 623 }
618 if (!strncmp(argv[i], "--query=", 8)) { 624 if (!strncmp(argv[i], "--query=", 8)) {
619 ctx.qry.raw = xstrdup(argv[i]+8); 625 ctx.qry.raw = xstrdup(argv[i]+8);
620 } 626 }
621 if (!strncmp(argv[i], "--repo=", 7)) { 627 if (!strncmp(argv[i], "--repo=", 7)) {
622 ctx.qry.repo = xstrdup(argv[i]+7); 628 ctx.qry.repo = xstrdup(argv[i]+7);
623 } 629 }
624 if (!strncmp(argv[i], "--page=", 7)) { 630 if (!strncmp(argv[i], "--page=", 7)) {
625 ctx.qry.page = xstrdup(argv[i]+7); 631 ctx.qry.page = xstrdup(argv[i]+7);
626 } 632 }
627 if (!strncmp(argv[i], "--head=", 7)) { 633 if (!strncmp(argv[i], "--head=", 7)) {
628 ctx.qry.head = xstrdup(argv[i]+7); 634 ctx.qry.head = xstrdup(argv[i]+7);
629 ctx.qry.has_symref = 1; 635 ctx.qry.has_symref = 1;
630 } 636 }
631 if (!strncmp(argv[i], "--sha1=", 7)) { 637 if (!strncmp(argv[i], "--sha1=", 7)) {
632 ctx.qry.sha1 = xstrdup(argv[i]+7); 638 ctx.qry.sha1 = xstrdup(argv[i]+7);
633 ctx.qry.has_sha1 = 1; 639 ctx.qry.has_sha1 = 1;
634 } 640 }
635 if (!strncmp(argv[i], "--ofs=", 6)) { 641 if (!strncmp(argv[i], "--ofs=", 6)) {
636 ctx.qry.ofs = atoi(argv[i]+6); 642 ctx.qry.ofs = atoi(argv[i]+6);
637 } 643 }
638 if (!strncmp(argv[i], "--scan-tree=", 12) || 644 if (!strncmp(argv[i], "--scan-tree=", 12) ||
639 !strncmp(argv[i], "--scan-path=", 12)) { 645 !strncmp(argv[i], "--scan-path=", 12)) {
640 /* HACK: the global snapshot bitmask defines the 646 /* HACK: the global snapshot bitmask defines the
641 * set of allowed snapshot formats, but the config 647 * set of allowed snapshot formats, but the config
642 * file hasn't been parsed yet so the mask is 648 * file hasn't been parsed yet so the mask is
643 * currently 0. By setting all bits high before 649 * currently 0. By setting all bits high before
644 * scanning we make sure that any in-repo cgitrc 650 * scanning we make sure that any in-repo cgitrc
645 * snapshot setting is respected by scan_tree(). 651 * snapshot setting is respected by scan_tree().
646 * BTW: we assume that there'll never be more than 652 * BTW: we assume that there'll never be more than
647 * 255 different snapshot formats supported by cgit... 653 * 255 different snapshot formats supported by cgit...
648 */ 654 */
649 ctx.cfg.snapshots = 0xFF; 655 ctx.cfg.snapshots = 0xFF;
650 scan++; 656 scan++;
651 scan_tree(argv[i] + 12, repo_config); 657 scan_tree(argv[i] + 12, repo_config);
652 } 658 }
653 } 659 }
654 if (scan) { 660 if (scan) {
655 qsort(cgit_repolist.repos, cgit_repolist.count, 661 qsort(cgit_repolist.repos, cgit_repolist.count,
656 sizeof(struct cgit_repo), cmp_repos); 662 sizeof(struct cgit_repo), cmp_repos);
657 print_repolist(stdout, &cgit_repolist, 0); 663 print_repolist(stdout, &cgit_repolist, 0);
658 exit(0); 664 exit(0);
659 } 665 }
660} 666}
661 667
662static int calc_ttl() 668static int calc_ttl()
663{ 669{
664 if (!ctx.repo) 670 if (!ctx.repo)
665 return ctx.cfg.cache_root_ttl; 671 return ctx.cfg.cache_root_ttl;
666 672
667 if (!ctx.qry.page) 673 if (!ctx.qry.page)
668 return ctx.cfg.cache_repo_ttl; 674 return ctx.cfg.cache_repo_ttl;
669 675
670 if (ctx.qry.has_symref) 676 if (ctx.qry.has_symref)
671 return ctx.cfg.cache_dynamic_ttl; 677 return ctx.cfg.cache_dynamic_ttl;
672 678
673 if (ctx.qry.has_sha1) 679 if (ctx.qry.has_sha1)
674 return ctx.cfg.cache_static_ttl; 680 return ctx.cfg.cache_static_ttl;
675 681
676 return ctx.cfg.cache_repo_ttl; 682 return ctx.cfg.cache_repo_ttl;
677} 683}
678 684
679int main(int argc, const char **argv) 685int main(int argc, const char **argv)
680{ 686{
681 const char *path; 687 const char *path;
682 char *qry; 688 char *qry;
683 int err, ttl; 689 int err, ttl;
684 690
685 prepare_context(&ctx); 691 prepare_context(&ctx);
686 cgit_repolist.length = 0; 692 cgit_repolist.length = 0;
687 cgit_repolist.count = 0; 693 cgit_repolist.count = 0;
688 cgit_repolist.repos = NULL; 694 cgit_repolist.repos = NULL;
689 695
690 cgit_parse_args(argc, argv); 696 cgit_parse_args(argc, argv);
691 parse_configfile(ctx.env.cgit_config, config_cb); 697 parse_configfile(ctx.env.cgit_config, config_cb);
692 ctx.repo = NULL; 698 ctx.repo = NULL;
693 http_parse_querystring(ctx.qry.raw, querystring_cb); 699 http_parse_querystring(ctx.qry.raw, querystring_cb);
694 700
695 /* If virtual-root isn't specified in cgitrc, lets pretend 701 /* If virtual-root isn't specified in cgitrc, lets pretend
696 * that virtual-root equals SCRIPT_NAME. 702 * that virtual-root equals SCRIPT_NAME.
697 */ 703 */
698 if (!ctx.cfg.virtual_root) 704 if (!ctx.cfg.virtual_root)
699 ctx.cfg.virtual_root = ctx.cfg.script_name; 705 ctx.cfg.virtual_root = ctx.cfg.script_name;
700 706
701 /* If no url parameter is specified on the querystring, lets 707 /* If no url parameter is specified on the querystring, lets
702 * use PATH_INFO as url. This allows cgit to work with virtual 708 * use PATH_INFO as url. This allows cgit to work with virtual
703 * urls without the need for rewriterules in the webserver (as 709 * urls without the need for rewriterules in the webserver (as
704 * long as PATH_INFO is included in the cache lookup key). 710 * long as PATH_INFO is included in the cache lookup key).
705 */ 711 */
706 path = ctx.env.path_info; 712 path = ctx.env.path_info;
707 if (!ctx.qry.url && path) { 713 if (!ctx.qry.url && path) {
708 if (path[0] == '/') 714 if (path[0] == '/')
709 path++; 715 path++;
710 ctx.qry.url = xstrdup(path); 716 ctx.qry.url = xstrdup(path);
711 if (ctx.qry.raw) { 717 if (ctx.qry.raw) {
712 qry = ctx.qry.raw; 718 qry = ctx.qry.raw;
713 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 719 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
714 free(qry); 720 free(qry);
715 } else 721 } else
716 ctx.qry.raw = xstrdup(ctx.qry.url); 722 ctx.qry.raw = xstrdup(ctx.qry.url);
717 cgit_parse_url(ctx.qry.url); 723 cgit_parse_url(ctx.qry.url);
718 } 724 }
719 725
720 ttl = calc_ttl(); 726 ttl = calc_ttl();
721 ctx.page.expires += ttl*60; 727 ctx.page.expires += ttl*60;
722 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")) 728 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
723 ctx.cfg.nocache = 1; 729 ctx.cfg.nocache = 1;
724 if (ctx.cfg.nocache) 730 if (ctx.cfg.nocache)
725 ctx.cfg.cache_size = 0; 731 ctx.cfg.cache_size = 0;
726 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 732 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
727 ctx.qry.raw, ttl, process_request, &ctx); 733 ctx.qry.raw, ttl, process_request, &ctx);
728 if (err) 734 if (err)
729 cgit_print_error(fmt("Error processing page: %s (%d)", 735 cgit_print_error(fmt("Error processing page: %s (%d)",
730 strerror(err), err)); 736 strerror(err), err));
731 return err; 737 return err;
732} 738}
diff --git a/cgit.h b/cgit.h
index cd4af72..f990b15 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,299 +1,300 @@
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 enable_remote_branches; 75 int enable_remote_branches;
76 int max_stats; 76 int max_stats;
77 time_t mtime; 77 time_t mtime;
78 struct cgit_filter *about_filter; 78 struct cgit_filter *about_filter;
79 struct cgit_filter *commit_filter; 79 struct cgit_filter *commit_filter;
80 struct cgit_filter *source_filter; 80 struct cgit_filter *source_filter;
81}; 81};
82 82
83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
84 const char *value); 84 const char *value);
85 85
86struct cgit_repolist { 86struct cgit_repolist {
87 int length; 87 int length;
88 int count; 88 int count;
89 struct cgit_repo *repos; 89 struct cgit_repo *repos;
90}; 90};
91 91
92struct commitinfo { 92struct commitinfo {
93 struct commit *commit; 93 struct commit *commit;
94 char *author; 94 char *author;
95 char *author_email; 95 char *author_email;
96 unsigned long author_date; 96 unsigned long author_date;
97 char *committer; 97 char *committer;
98 char *committer_email; 98 char *committer_email;
99 unsigned long committer_date; 99 unsigned long committer_date;
100 char *subject; 100 char *subject;
101 char *msg; 101 char *msg;
102 char *msg_encoding; 102 char *msg_encoding;
103}; 103};
104 104
105struct taginfo { 105struct taginfo {
106 char *tagger; 106 char *tagger;
107 char *tagger_email; 107 char *tagger_email;
108 unsigned long tagger_date; 108 unsigned long tagger_date;
109 char *msg; 109 char *msg;
110}; 110};
111 111
112struct refinfo { 112struct refinfo {
113 const char *refname; 113 const char *refname;
114 struct object *object; 114 struct object *object;
115 union { 115 union {
116 struct taginfo *tag; 116 struct taginfo *tag;
117 struct commitinfo *commit; 117 struct commitinfo *commit;
118 }; 118 };
119}; 119};
120 120
121struct reflist { 121struct reflist {
122 struct refinfo **refs; 122 struct refinfo **refs;
123 int alloc; 123 int alloc;
124 int count; 124 int count;
125}; 125};
126 126
127struct cgit_query { 127struct cgit_query {
128 int has_symref; 128 int has_symref;
129 int has_sha1; 129 int has_sha1;
130 char *raw; 130 char *raw;
131 char *repo; 131 char *repo;
132 char *page; 132 char *page;
133 char *search; 133 char *search;
134 char *grep; 134 char *grep;
135 char *head; 135 char *head;
136 char *sha1; 136 char *sha1;
137 char *sha2; 137 char *sha2;
138 char *path; 138 char *path;
139 char *name; 139 char *name;
140 char *mimetype; 140 char *mimetype;
141 char *url; 141 char *url;
142 char *period; 142 char *period;
143 int ofs; 143 int ofs;
144 int nohead; 144 int nohead;
145 char *sort; 145 char *sort;
146 int showmsg; 146 int showmsg;
147 int ssdiff; 147 int ssdiff;
148 char *vpath;
148}; 149};
149 150
150struct cgit_config { 151struct cgit_config {
151 char *agefile; 152 char *agefile;
152 char *cache_root; 153 char *cache_root;
153 char *clone_prefix; 154 char *clone_prefix;
154 char *css; 155 char *css;
155 char *favicon; 156 char *favicon;
156 char *footer; 157 char *footer;
157 char *head_include; 158 char *head_include;
158 char *header; 159 char *header;
159 char *index_header; 160 char *index_header;
160 char *index_info; 161 char *index_info;
161 char *logo; 162 char *logo;
162 char *logo_link; 163 char *logo_link;
163 char *module_link; 164 char *module_link;
164 char *robots; 165 char *robots;
165 char *root_title; 166 char *root_title;
166 char *root_desc; 167 char *root_desc;
167 char *root_readme; 168 char *root_readme;
168 char *script_name; 169 char *script_name;
169 char *section; 170 char *section;
170 char *virtual_root; 171 char *virtual_root;
171 int cache_size; 172 int cache_size;
172 int cache_dynamic_ttl; 173 int cache_dynamic_ttl;
173 int cache_max_create_time; 174 int cache_max_create_time;
174 int cache_repo_ttl; 175 int cache_repo_ttl;
175 int cache_root_ttl; 176 int cache_root_ttl;
176 int cache_scanrc_ttl; 177 int cache_scanrc_ttl;
177 int cache_static_ttl; 178 int cache_static_ttl;
178 int embedded; 179 int embedded;
179 int enable_filter_overrides; 180 int enable_filter_overrides;
180 int enable_index_links; 181 int enable_index_links;
181 int enable_log_filecount; 182 int enable_log_filecount;
182 int enable_log_linecount; 183 int enable_log_linecount;
183 int enable_remote_branches; 184 int enable_remote_branches;
184 int enable_tree_linenumbers; 185 int enable_tree_linenumbers;
185 int local_time; 186 int local_time;
186 int max_repo_count; 187 int max_repo_count;
187 int max_commit_count; 188 int max_commit_count;
188 int max_lock_attempts; 189 int max_lock_attempts;
189 int max_msg_len; 190 int max_msg_len;
190 int max_repodesc_len; 191 int max_repodesc_len;
191 int max_blob_size; 192 int max_blob_size;
192 int max_stats; 193 int max_stats;
193 int nocache; 194 int nocache;
194 int noplainemail; 195 int noplainemail;
195 int noheader; 196 int noheader;
196 int renamelimit; 197 int renamelimit;
197 int snapshots; 198 int snapshots;
198 int summary_branches; 199 int summary_branches;
199 int summary_log; 200 int summary_log;
200 int summary_tags; 201 int summary_tags;
201 int ssdiff; 202 int ssdiff;
202 struct string_list mimetypes; 203 struct string_list mimetypes;
203 struct cgit_filter *about_filter; 204 struct cgit_filter *about_filter;
204 struct cgit_filter *commit_filter; 205 struct cgit_filter *commit_filter;
205 struct cgit_filter *source_filter; 206 struct cgit_filter *source_filter;
206}; 207};
207 208
208struct cgit_page { 209struct cgit_page {
209 time_t modified; 210 time_t modified;
210 time_t expires; 211 time_t expires;
211 size_t size; 212 size_t size;
212 char *mimetype; 213 char *mimetype;
213 char *charset; 214 char *charset;
214 char *filename; 215 char *filename;
215 char *etag; 216 char *etag;
216 char *title; 217 char *title;
217 int status; 218 int status;
218 char *statusmsg; 219 char *statusmsg;
219}; 220};
220 221
221struct cgit_environment { 222struct cgit_environment {
222 char *cgit_config; 223 char *cgit_config;
223 char *http_host; 224 char *http_host;
224 char *https; 225 char *https;
225 char *no_http; 226 char *no_http;
226 char *path_info; 227 char *path_info;
227 char *query_string; 228 char *query_string;
228 char *request_method; 229 char *request_method;
229 char *script_name; 230 char *script_name;
230 char *server_name; 231 char *server_name;
231 char *server_port; 232 char *server_port;
232}; 233};
233 234
234struct cgit_context { 235struct cgit_context {
235 struct cgit_environment env; 236 struct cgit_environment env;
236 struct cgit_query qry; 237 struct cgit_query qry;
237 struct cgit_config cfg; 238 struct cgit_config cfg;
238 struct cgit_repo *repo; 239 struct cgit_repo *repo;
239 struct cgit_page page; 240 struct cgit_page page;
240}; 241};
241 242
242struct cgit_snapshot_format { 243struct cgit_snapshot_format {
243 const char *suffix; 244 const char *suffix;
244 const char *mimetype; 245 const char *mimetype;
245 write_archive_fn_t write_func; 246 write_archive_fn_t write_func;
246 int bit; 247 int bit;
247}; 248};
248 249
249extern const char *cgit_version; 250extern const char *cgit_version;
250 251
251extern struct cgit_repolist cgit_repolist; 252extern struct cgit_repolist cgit_repolist;
252extern struct cgit_context ctx; 253extern struct cgit_context ctx;
253extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 254extern const struct cgit_snapshot_format cgit_snapshot_formats[];
254 255
255extern struct cgit_repo *cgit_add_repo(const char *url); 256extern struct cgit_repo *cgit_add_repo(const char *url);
256extern struct cgit_repo *cgit_get_repoinfo(const char *url); 257extern struct cgit_repo *cgit_get_repoinfo(const char *url);
257extern void cgit_repo_config_cb(const char *name, const char *value); 258extern void cgit_repo_config_cb(const char *name, const char *value);
258 259
259extern int chk_zero(int result, char *msg); 260extern int chk_zero(int result, char *msg);
260extern int chk_positive(int result, char *msg); 261extern int chk_positive(int result, char *msg);
261extern int chk_non_negative(int result, char *msg); 262extern int chk_non_negative(int result, char *msg);
262 263
263extern char *trim_end(const char *str, char c); 264extern char *trim_end(const char *str, char c);
264extern char *strlpart(char *txt, int maxlen); 265extern char *strlpart(char *txt, int maxlen);
265extern char *strrpart(char *txt, int maxlen); 266extern char *strrpart(char *txt, int maxlen);
266 267
267extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 268extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
268extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 269extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
269 int flags, void *cb_data); 270 int flags, void *cb_data);
270 271
271extern void *cgit_free_commitinfo(struct commitinfo *info); 272extern void *cgit_free_commitinfo(struct commitinfo *info);
272 273
273extern int cgit_diff_files(const unsigned char *old_sha1, 274extern int cgit_diff_files(const unsigned char *old_sha1,
274 const unsigned char *new_sha1, 275 const unsigned char *new_sha1,
275 unsigned long *old_size, unsigned long *new_size, 276 unsigned long *old_size, unsigned long *new_size,
276 int *binary, linediff_fn fn); 277 int *binary, linediff_fn fn);
277 278
278extern void cgit_diff_tree(const unsigned char *old_sha1, 279extern void cgit_diff_tree(const unsigned char *old_sha1,
279 const unsigned char *new_sha1, 280 const unsigned char *new_sha1,
280 filepair_fn fn, const char *prefix); 281 filepair_fn fn, const char *prefix);
281 282
282extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 283extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
283 284
284extern char *fmt(const char *format,...); 285extern char *fmt(const char *format,...);
285 286
286extern struct commitinfo *cgit_parse_commit(struct commit *commit); 287extern struct commitinfo *cgit_parse_commit(struct commit *commit);
287extern struct taginfo *cgit_parse_tag(struct tag *tag); 288extern struct taginfo *cgit_parse_tag(struct tag *tag);
288extern void cgit_parse_url(const char *url); 289extern void cgit_parse_url(const char *url);
289 290
290extern const char *cgit_repobasename(const char *reponame); 291extern const char *cgit_repobasename(const char *reponame);
291 292
292extern int cgit_parse_snapshots_mask(const char *str); 293extern int cgit_parse_snapshots_mask(const char *str);
293 294
294extern int cgit_open_filter(struct cgit_filter *filter); 295extern int cgit_open_filter(struct cgit_filter *filter);
295extern int cgit_close_filter(struct cgit_filter *filter); 296extern int cgit_close_filter(struct cgit_filter *filter);
296 297
297extern int readfile(const char *path, char **buf, size_t *size); 298extern int readfile(const char *path, char **buf, size_t *size);
298 299
299#endif /* CGIT_H */ 300#endif /* CGIT_H */
diff --git a/cmd.c b/cmd.c
index 766f903..a9e426a 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,171 +1,171 @@
1/* cmd.c: the cgit command dispatcher 1/* cmd.c: the cgit command dispatcher
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 "cmd.h" 10#include "cmd.h"
11#include "cache.h" 11#include "cache.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13#include "ui-atom.h" 13#include "ui-atom.h"
14#include "ui-blob.h" 14#include "ui-blob.h"
15#include "ui-clone.h" 15#include "ui-clone.h"
16#include "ui-commit.h" 16#include "ui-commit.h"
17#include "ui-diff.h" 17#include "ui-diff.h"
18#include "ui-log.h" 18#include "ui-log.h"
19#include "ui-patch.h" 19#include "ui-patch.h"
20#include "ui-plain.h" 20#include "ui-plain.h"
21#include "ui-refs.h" 21#include "ui-refs.h"
22#include "ui-repolist.h" 22#include "ui-repolist.h"
23#include "ui-snapshot.h" 23#include "ui-snapshot.h"
24#include "ui-stats.h" 24#include "ui-stats.h"
25#include "ui-summary.h" 25#include "ui-summary.h"
26#include "ui-tag.h" 26#include "ui-tag.h"
27#include "ui-tree.h" 27#include "ui-tree.h"
28 28
29static void HEAD_fn(struct cgit_context *ctx) 29static void HEAD_fn(struct cgit_context *ctx)
30{ 30{
31 cgit_clone_head(ctx); 31 cgit_clone_head(ctx);
32} 32}
33 33
34static void atom_fn(struct cgit_context *ctx) 34static void atom_fn(struct cgit_context *ctx)
35{ 35{
36 cgit_print_atom(ctx->qry.head, ctx->qry.path, 10); 36 cgit_print_atom(ctx->qry.head, ctx->qry.path, 10);
37} 37}
38 38
39static void about_fn(struct cgit_context *ctx) 39static void about_fn(struct cgit_context *ctx)
40{ 40{
41 if (ctx->repo) 41 if (ctx->repo)
42 cgit_print_repo_readme(ctx->qry.path); 42 cgit_print_repo_readme(ctx->qry.path);
43 else 43 else
44 cgit_print_site_readme(); 44 cgit_print_site_readme();
45} 45}
46 46
47static void blob_fn(struct cgit_context *ctx) 47static void blob_fn(struct cgit_context *ctx)
48{ 48{
49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head); 49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head);
50} 50}
51 51
52static void commit_fn(struct cgit_context *ctx) 52static void commit_fn(struct cgit_context *ctx)
53{ 53{
54 cgit_print_commit(ctx->qry.sha1); 54 cgit_print_commit(ctx->qry.sha1);
55} 55}
56 56
57static void diff_fn(struct cgit_context *ctx) 57static void diff_fn(struct cgit_context *ctx)
58{ 58{
59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); 59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
60} 60}
61 61
62static void info_fn(struct cgit_context *ctx) 62static void info_fn(struct cgit_context *ctx)
63{ 63{
64 cgit_clone_info(ctx); 64 cgit_clone_info(ctx);
65} 65}
66 66
67static void log_fn(struct cgit_context *ctx) 67static void log_fn(struct cgit_context *ctx)
68{ 68{
69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, 69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); 70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
71} 71}
72 72
73static void ls_cache_fn(struct cgit_context *ctx) 73static void ls_cache_fn(struct cgit_context *ctx)
74{ 74{
75 ctx->page.mimetype = "text/plain"; 75 ctx->page.mimetype = "text/plain";
76 ctx->page.filename = "ls-cache.txt"; 76 ctx->page.filename = "ls-cache.txt";
77 cgit_print_http_headers(ctx); 77 cgit_print_http_headers(ctx);
78 cache_ls(ctx->cfg.cache_root); 78 cache_ls(ctx->cfg.cache_root);
79} 79}
80 80
81static void objects_fn(struct cgit_context *ctx) 81static void objects_fn(struct cgit_context *ctx)
82{ 82{
83 cgit_clone_objects(ctx); 83 cgit_clone_objects(ctx);
84} 84}
85 85
86static void repolist_fn(struct cgit_context *ctx) 86static void repolist_fn(struct cgit_context *ctx)
87{ 87{
88 cgit_print_repolist(); 88 cgit_print_repolist();
89} 89}
90 90
91static void patch_fn(struct cgit_context *ctx) 91static void patch_fn(struct cgit_context *ctx)
92{ 92{
93 cgit_print_patch(ctx->qry.sha1); 93 cgit_print_patch(ctx->qry.sha1);
94} 94}
95 95
96static void plain_fn(struct cgit_context *ctx) 96static void plain_fn(struct cgit_context *ctx)
97{ 97{
98 cgit_print_plain(ctx); 98 cgit_print_plain(ctx);
99} 99}
100 100
101static void refs_fn(struct cgit_context *ctx) 101static void refs_fn(struct cgit_context *ctx)
102{ 102{
103 cgit_print_refs(); 103 cgit_print_refs();
104} 104}
105 105
106static void snapshot_fn(struct cgit_context *ctx) 106static void snapshot_fn(struct cgit_context *ctx)
107{ 107{
108 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, ctx->qry.path, 108 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, ctx->qry.path,
109 ctx->repo->snapshots, ctx->qry.nohead); 109 ctx->repo->snapshots, ctx->qry.nohead);
110} 110}
111 111
112static void stats_fn(struct cgit_context *ctx) 112static void stats_fn(struct cgit_context *ctx)
113{ 113{
114 cgit_show_stats(ctx); 114 cgit_show_stats(ctx);
115} 115}
116 116
117static void summary_fn(struct cgit_context *ctx) 117static void summary_fn(struct cgit_context *ctx)
118{ 118{
119 cgit_print_summary(); 119 cgit_print_summary();
120} 120}
121 121
122static void tag_fn(struct cgit_context *ctx) 122static void tag_fn(struct cgit_context *ctx)
123{ 123{
124 cgit_print_tag(ctx->qry.sha1); 124 cgit_print_tag(ctx->qry.sha1);
125} 125}
126 126
127static void tree_fn(struct cgit_context *ctx) 127static void tree_fn(struct cgit_context *ctx)
128{ 128{
129 cgit_print_tree(ctx->qry.sha1, ctx->qry.path); 129 cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
130} 130}
131 131
132#define def_cmd(name, want_repo, want_layout) \ 132#define def_cmd(name, want_repo, want_layout, want_vpath) \
133 {#name, name##_fn, want_repo, want_layout} 133 {#name, name##_fn, want_repo, want_layout, want_vpath}
134 134
135struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx) 135struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
136{ 136{
137 static struct cgit_cmd cmds[] = { 137 static struct cgit_cmd cmds[] = {
138 def_cmd(HEAD, 1, 0), 138 def_cmd(HEAD, 1, 0, 0),
139 def_cmd(atom, 1, 0), 139 def_cmd(atom, 1, 0, 0),
140 def_cmd(about, 0, 1), 140 def_cmd(about, 0, 1, 0),
141 def_cmd(blob, 1, 0), 141 def_cmd(blob, 1, 0, 0),
142 def_cmd(commit, 1, 1), 142 def_cmd(commit, 1, 1, 1),
143 def_cmd(diff, 1, 1), 143 def_cmd(diff, 1, 1, 1),
144 def_cmd(info, 1, 0), 144 def_cmd(info, 1, 0, 0),
145 def_cmd(log, 1, 1), 145 def_cmd(log, 1, 1, 1),
146 def_cmd(ls_cache, 0, 0), 146 def_cmd(ls_cache, 0, 0, 0),
147 def_cmd(objects, 1, 0), 147 def_cmd(objects, 1, 0, 0),
148 def_cmd(patch, 1, 0), 148 def_cmd(patch, 1, 0, 1),
149 def_cmd(plain, 1, 0), 149 def_cmd(plain, 1, 0, 0),
150 def_cmd(refs, 1, 1), 150 def_cmd(refs, 1, 1, 0),
151 def_cmd(repolist, 0, 0), 151 def_cmd(repolist, 0, 0, 0),
152 def_cmd(snapshot, 1, 0), 152 def_cmd(snapshot, 1, 0, 0),
153 def_cmd(stats, 1, 1), 153 def_cmd(stats, 1, 1, 1),
154 def_cmd(summary, 1, 1), 154 def_cmd(summary, 1, 1, 0),
155 def_cmd(tag, 1, 1), 155 def_cmd(tag, 1, 1, 0),
156 def_cmd(tree, 1, 1), 156 def_cmd(tree, 1, 1, 1),
157 }; 157 };
158 int i; 158 int i;
159 159
160 if (ctx->qry.page == NULL) { 160 if (ctx->qry.page == NULL) {
161 if (ctx->repo) 161 if (ctx->repo)
162 ctx->qry.page = "summary"; 162 ctx->qry.page = "summary";
163 else 163 else
164 ctx->qry.page = "repolist"; 164 ctx->qry.page = "repolist";
165 } 165 }
166 166
167 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) 167 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
168 if (!strcmp(ctx->qry.page, cmds[i].name)) 168 if (!strcmp(ctx->qry.page, cmds[i].name))
169 return &cmds[i]; 169 return &cmds[i];
170 return NULL; 170 return NULL;
171} 171}
diff --git a/cmd.h b/cmd.h
index ec9e691..8dc01bd 100644
--- a/cmd.h
+++ b/cmd.h
@@ -1,15 +1,16 @@
1#ifndef CMD_H 1#ifndef CMD_H
2#define CMD_H 2#define CMD_H
3 3
4typedef void (*cgit_cmd_fn)(struct cgit_context *ctx); 4typedef void (*cgit_cmd_fn)(struct cgit_context *ctx);
5 5
6struct cgit_cmd { 6struct cgit_cmd {
7 const char *name; 7 const char *name;
8 cgit_cmd_fn fn; 8 cgit_cmd_fn fn;
9 unsigned int want_repo:1, 9 unsigned int want_repo:1,
10 want_layout:1; 10 want_layout:1,
11 want_vpath:1;
11}; 12};
12 13
13extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx); 14extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx);
14 15
15#endif /* CMD_H */ 16#endif /* CMD_H */