aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile22
-rw-r--r--cgit.c82
-rw-r--r--cgit.css148
-rw-r--r--cgit.h23
-rw-r--r--cgitrc.5.txt58
-rw-r--r--cmd.c48
-rw-r--r--cmd.h3
-rwxr-xr-xfilters/commit-links.sh16
-rwxr-xr-xfilters/syntax-highlighting.sh29
m---------git0
-rw-r--r--html.c70
-rw-r--r--html.h18
-rw-r--r--scan-tree.c73
-rw-r--r--scan-tree.h3
-rw-r--r--shared.c88
-rw-r--r--ui-atom.c4
-rw-r--r--ui-blob.c35
-rw-r--r--ui-blob.h1
-rw-r--r--ui-commit.c46
-rw-r--r--ui-commit.h2
-rw-r--r--ui-diff.c92
-rw-r--r--ui-log.c53
-rw-r--r--ui-patch.c8
-rw-r--r--ui-patch.h2
-rw-r--r--ui-plain.c70
-rw-r--r--ui-refs.c4
-rw-r--r--ui-shared.c270
-rw-r--r--ui-shared.h71
-rw-r--r--ui-snapshot.c14
-rw-r--r--ui-ssdiff.c369
-rw-r--r--ui-ssdiff.h13
-rw-r--r--ui-stats.c8
-rw-r--r--ui-summary.c26
-rw-r--r--ui-tag.c24
-rw-r--r--ui-tree.c23
35 files changed, 1511 insertions, 305 deletions
diff --git a/Makefile b/Makefile
index 5162020..2a15469 100644
--- a/Makefile
+++ b/Makefile
@@ -5,12 +5,15 @@ CGIT_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.4.3 8GIT_VER = 1.7.2.2
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 NO_OPENSSL to disable linking with OpenSSL and use bundled SHA1
15# implementation (slower).
16#
14# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin). 17# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin).
15# 18#
16 19
@@ -68,7 +71,7 @@ endif
68 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $< 71 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
69 72
70 73
71EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 74EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lpthread
72OBJECTS = 75OBJECTS =
73OBJECTS += cache.o 76OBJECTS += cache.o
74OBJECTS += cgit.o 77OBJECTS += cgit.o
@@ -90,6 +93,7 @@ OBJECTS += ui-refs.o
90OBJECTS += ui-repolist.o 93OBJECTS += ui-repolist.o
91OBJECTS += ui-shared.o 94OBJECTS += ui-shared.o
92OBJECTS += ui-snapshot.o 95OBJECTS += ui-snapshot.o
96OBJECTS += ui-ssdiff.o
93OBJECTS += ui-stats.o 97OBJECTS += ui-stats.o
94OBJECTS += ui-summary.o 98OBJECTS += ui-summary.o
95OBJECTS += ui-tag.o 99OBJECTS += ui-tag.o
@@ -123,17 +127,25 @@ endif
123ifdef NO_STRCASESTR 127ifdef NO_STRCASESTR
124 CFLAGS += -DNO_STRCASESTR 128 CFLAGS += -DNO_STRCASESTR
125endif 129endif
130ifdef NO_OPENSSL
131 CFLAGS += -DNO_OPENSSL
132 GIT_OPTIONS += NO_OPENSSL=1
133else
134 EXTLIBS += -lcrypto
135endif
126 136
127cgit: $(OBJECTS) libgit 137cgit: $(OBJECTS) libgit
128 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS) 138 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
129 139
130cgit.o: VERSION 140cgit.o: VERSION
131 141
132-include $(OBJECTS:.o=.d) 142ifneq "$(MAKECMDGOALS)" "clean"
143 -include $(OBJECTS:.o=.d)
144endif
133 145
134libgit: 146libgit:
135 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 libgit.a 147 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) libgit.a
136 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 xdiff/lib.a 148 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) xdiff/lib.a
137 149
138test: all 150test: all
139 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all 151 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
diff --git a/cgit.c b/cgit.c
index 6c7e811..d6146e2 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,6 +1,7 @@
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 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
4 * 5 *
5 * Licensed under GNU General Public License v2 6 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 7 * (see COPYING for full license text)
@@ -21,7 +22,7 @@ void add_mimetype(const char *name, const char *value)
21{ 22{
22 struct string_list_item *item; 23 struct string_list_item *item;
23 24
24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes); 25 item = string_list_insert(&ctx.cfg.mimetypes, xstrdup(name));
25 item->util = xstrdup(value); 26 item->util = xstrdup(value);
26} 27}
27 28
@@ -60,6 +61,10 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 61 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
61 else if (!strcmp(name, "enable-log-linecount")) 62 else if (!strcmp(name, "enable-log-linecount"))
62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 63 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
64 else if (!strcmp(name, "enable-remote-branches"))
65 repo->enable_remote_branches = atoi(value);
66 else if (!strcmp(name, "enable-subject-links"))
67 repo->enable_subject_links = atoi(value);
63 else if (!strcmp(name, "max-stats")) 68 else if (!strcmp(name, "max-stats"))
64 repo->max_stats = cgit_find_stats_period(value, NULL); 69 repo->max_stats = cgit_find_stats_period(value, NULL);
65 else if (!strcmp(name, "module-link")) 70 else if (!strcmp(name, "module-link"))
@@ -67,7 +72,8 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
67 else if (!strcmp(name, "section")) 72 else if (!strcmp(name, "section"))
68 repo->section = xstrdup(value); 73 repo->section = xstrdup(value);
69 else if (!strcmp(name, "readme") && value != NULL) { 74 else if (!strcmp(name, "readme") && value != NULL) {
70 if (*value == '/') 75 char *colon;
76 if (*value == '/' || ((colon = strchr(value, ':')) != NULL && colon != value && *(colon + 1) != '\0'))
71 repo->readme = xstrdup(value); 77 repo->readme = xstrdup(value);
72 else 78 else
73 repo->readme = xstrdup(fmt("%s/%s", repo->path, value)); 79 repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
@@ -131,12 +137,18 @@ void config_cb(const char *name, const char *value)
131 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 137 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
132 else if (!strcmp(name, "enable-filter-overrides")) 138 else if (!strcmp(name, "enable-filter-overrides"))
133 ctx.cfg.enable_filter_overrides = atoi(value); 139 ctx.cfg.enable_filter_overrides = atoi(value);
140 else if (!strcmp(name, "enable-gitweb-owner"))
141 ctx.cfg.enable_gitweb_owner = atoi(value);
134 else if (!strcmp(name, "enable-index-links")) 142 else if (!strcmp(name, "enable-index-links"))
135 ctx.cfg.enable_index_links = atoi(value); 143 ctx.cfg.enable_index_links = atoi(value);
136 else if (!strcmp(name, "enable-log-filecount")) 144 else if (!strcmp(name, "enable-log-filecount"))
137 ctx.cfg.enable_log_filecount = atoi(value); 145 ctx.cfg.enable_log_filecount = atoi(value);
138 else if (!strcmp(name, "enable-log-linecount")) 146 else if (!strcmp(name, "enable-log-linecount"))
139 ctx.cfg.enable_log_linecount = atoi(value); 147 ctx.cfg.enable_log_linecount = atoi(value);
148 else if (!strcmp(name, "enable-remote-branches"))
149 ctx.cfg.enable_remote_branches = atoi(value);
150 else if (!strcmp(name, "enable-subject-links"))
151 ctx.cfg.enable_subject_links = atoi(value);
140 else if (!strcmp(name, "enable-tree-linenumbers")) 152 else if (!strcmp(name, "enable-tree-linenumbers"))
141 ctx.cfg.enable_tree_linenumbers = atoi(value); 153 ctx.cfg.enable_tree_linenumbers = atoi(value);
142 else if (!strcmp(name, "max-stats")) 154 else if (!strcmp(name, "max-stats"))
@@ -144,7 +156,7 @@ void config_cb(const char *name, const char *value)
144 else if (!strcmp(name, "cache-size")) 156 else if (!strcmp(name, "cache-size"))
145 ctx.cfg.cache_size = atoi(value); 157 ctx.cfg.cache_size = atoi(value);
146 else if (!strcmp(name, "cache-root")) 158 else if (!strcmp(name, "cache-root"))
147 ctx.cfg.cache_root = xstrdup(value); 159 ctx.cfg.cache_root = xstrdup(expand_macros(value));
148 else if (!strcmp(name, "cache-root-ttl")) 160 else if (!strcmp(name, "cache-root-ttl"))
149 ctx.cfg.cache_root_ttl = atoi(value); 161 ctx.cfg.cache_root_ttl = atoi(value);
150 else if (!strcmp(name, "cache-repo-ttl")) 162 else if (!strcmp(name, "cache-repo-ttl"))
@@ -161,19 +173,28 @@ void config_cb(const char *name, const char *value)
161 ctx.cfg.commit_filter = new_filter(value, 0); 173 ctx.cfg.commit_filter = new_filter(value, 0);
162 else if (!strcmp(name, "embedded")) 174 else if (!strcmp(name, "embedded"))
163 ctx.cfg.embedded = atoi(value); 175 ctx.cfg.embedded = atoi(value);
176 else if (!strcmp(name, "max-atom-items"))
177 ctx.cfg.max_atom_items = atoi(value);
164 else if (!strcmp(name, "max-message-length")) 178 else if (!strcmp(name, "max-message-length"))
165 ctx.cfg.max_msg_len = atoi(value); 179 ctx.cfg.max_msg_len = atoi(value);
166 else if (!strcmp(name, "max-repodesc-length")) 180 else if (!strcmp(name, "max-repodesc-length"))
167 ctx.cfg.max_repodesc_len = atoi(value); 181 ctx.cfg.max_repodesc_len = atoi(value);
182 else if (!strcmp(name, "max-blob-size"))
183 ctx.cfg.max_blob_size = atoi(value);
168 else if (!strcmp(name, "max-repo-count")) 184 else if (!strcmp(name, "max-repo-count"))
169 ctx.cfg.max_repo_count = atoi(value); 185 ctx.cfg.max_repo_count = atoi(value);
170 else if (!strcmp(name, "max-commit-count")) 186 else if (!strcmp(name, "max-commit-count"))
171 ctx.cfg.max_commit_count = atoi(value); 187 ctx.cfg.max_commit_count = atoi(value);
188 else if (!strcmp(name, "project-list"))
189 ctx.cfg.project_list = xstrdup(expand_macros(value));
172 else if (!strcmp(name, "scan-path")) 190 else if (!strcmp(name, "scan-path"))
173 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 191 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
174 process_cached_repolist(value); 192 process_cached_repolist(expand_macros(value));
193 else if (ctx.cfg.project_list)
194 scan_projects(expand_macros(value),
195 ctx.cfg.project_list, repo_config);
175 else 196 else
176 scan_tree(value, repo_config); 197 scan_tree(expand_macros(value), repo_config);
177 else if (!strcmp(name, "source-filter")) 198 else if (!strcmp(name, "source-filter"))
178 ctx.cfg.source_filter = new_filter(value, 1); 199 ctx.cfg.source_filter = new_filter(value, 1);
179 else if (!strcmp(name, "summary-log")) 200 else if (!strcmp(name, "summary-log"))
@@ -182,10 +203,14 @@ void config_cb(const char *name, const char *value)
182 ctx.cfg.summary_branches = atoi(value); 203 ctx.cfg.summary_branches = atoi(value);
183 else if (!strcmp(name, "summary-tags")) 204 else if (!strcmp(name, "summary-tags"))
184 ctx.cfg.summary_tags = atoi(value); 205 ctx.cfg.summary_tags = atoi(value);
206 else if (!strcmp(name, "side-by-side-diffs"))
207 ctx.cfg.ssdiff = atoi(value);
185 else if (!strcmp(name, "agefile")) 208 else if (!strcmp(name, "agefile"))
186 ctx.cfg.agefile = xstrdup(value); 209 ctx.cfg.agefile = xstrdup(value);
187 else if (!strcmp(name, "renamelimit")) 210 else if (!strcmp(name, "renamelimit"))
188 ctx.cfg.renamelimit = atoi(value); 211 ctx.cfg.renamelimit = atoi(value);
212 else if (!strcmp(name, "remove-suffix"))
213 ctx.cfg.remove_suffix = atoi(value);
189 else if (!strcmp(name, "robots")) 214 else if (!strcmp(name, "robots"))
190 ctx.cfg.robots = xstrdup(value); 215 ctx.cfg.robots = xstrdup(value);
191 else if (!strcmp(name, "clone-prefix")) 216 else if (!strcmp(name, "clone-prefix"))
@@ -195,7 +220,7 @@ void config_cb(const char *name, const char *value)
195 else if (!prefixcmp(name, "mimetype.")) 220 else if (!prefixcmp(name, "mimetype."))
196 add_mimetype(name + 9, value); 221 add_mimetype(name + 9, value);
197 else if (!strcmp(name, "include")) 222 else if (!strcmp(name, "include"))
198 parse_configfile(value, config_cb); 223 parse_configfile(expand_macros(value), config_cb);
199} 224}
200 225
201static void querystring_cb(const char *name, const char *value) 226static void querystring_cb(const char *name, const char *value)
@@ -209,6 +234,8 @@ static void querystring_cb(const char *name, const char *value)
209 } else if (!strcmp(name, "p")) { 234 } else if (!strcmp(name, "p")) {
210 ctx.qry.page = xstrdup(value); 235 ctx.qry.page = xstrdup(value);
211 } else if (!strcmp(name, "url")) { 236 } else if (!strcmp(name, "url")) {
237 if (*value == '/')
238 value++;
212 ctx.qry.url = xstrdup(value); 239 ctx.qry.url = xstrdup(value);
213 cgit_parse_url(value); 240 cgit_parse_url(value);
214 } else if (!strcmp(name, "qt")) { 241 } else if (!strcmp(name, "qt")) {
@@ -238,6 +265,14 @@ static void querystring_cb(const char *name, const char *value)
238 ctx.qry.showmsg = atoi(value); 265 ctx.qry.showmsg = atoi(value);
239 } else if (!strcmp(name, "period")) { 266 } else if (!strcmp(name, "period")) {
240 ctx.qry.period = xstrdup(value); 267 ctx.qry.period = xstrdup(value);
268 } else if (!strcmp(name, "ss")) {
269 ctx.qry.ssdiff = atoi(value);
270 } else if (!strcmp(name, "all")) {
271 ctx.qry.show_all = atoi(value);
272 } else if (!strcmp(name, "context")) {
273 ctx.qry.context = atoi(value);
274 } else if (!strcmp(name, "ignorews")) {
275 ctx.qry.ignorews = atoi(value);
241 } 276 }
242} 277}
243 278
@@ -262,15 +297,19 @@ static void prepare_context(struct cgit_context *ctx)
262 ctx->cfg.css = "/cgit.css"; 297 ctx->cfg.css = "/cgit.css";
263 ctx->cfg.logo = "/cgit.png"; 298 ctx->cfg.logo = "/cgit.png";
264 ctx->cfg.local_time = 0; 299 ctx->cfg.local_time = 0;
300 ctx->cfg.enable_gitweb_owner = 1;
265 ctx->cfg.enable_tree_linenumbers = 1; 301 ctx->cfg.enable_tree_linenumbers = 1;
266 ctx->cfg.max_repo_count = 50; 302 ctx->cfg.max_repo_count = 50;
267 ctx->cfg.max_commit_count = 50; 303 ctx->cfg.max_commit_count = 50;
268 ctx->cfg.max_lock_attempts = 5; 304 ctx->cfg.max_lock_attempts = 5;
269 ctx->cfg.max_msg_len = 80; 305 ctx->cfg.max_msg_len = 80;
270 ctx->cfg.max_repodesc_len = 80; 306 ctx->cfg.max_repodesc_len = 80;
307 ctx->cfg.max_blob_size = 0;
271 ctx->cfg.max_stats = 0; 308 ctx->cfg.max_stats = 0;
272 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 309 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
310 ctx->cfg.project_list = NULL;
273 ctx->cfg.renamelimit = -1; 311 ctx->cfg.renamelimit = -1;
312 ctx->cfg.remove_suffix = 0;
274 ctx->cfg.robots = "index, nofollow"; 313 ctx->cfg.robots = "index, nofollow";
275 ctx->cfg.root_title = "Git repository browser"; 314 ctx->cfg.root_title = "Git repository browser";
276 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 315 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
@@ -279,6 +318,8 @@ static void prepare_context(struct cgit_context *ctx)
279 ctx->cfg.summary_branches = 10; 318 ctx->cfg.summary_branches = 10;
280 ctx->cfg.summary_log = 10; 319 ctx->cfg.summary_log = 10;
281 ctx->cfg.summary_tags = 10; 320 ctx->cfg.summary_tags = 10;
321 ctx->cfg.max_atom_items = 10;
322 ctx->cfg.ssdiff = 0;
282 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 323 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
283 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 324 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
284 ctx->env.https = xstrdupn(getenv("HTTPS")); 325 ctx->env.https = xstrdupn(getenv("HTTPS"));
@@ -410,6 +451,12 @@ static void process_request(void *cbdata)
410 return; 451 return;
411 } 452 }
412 453
454 /* If cmd->want_vpath is set, assume ctx->qry.path contains a "virtual"
455 * in-project path limit to be made available at ctx->qry.vpath.
456 * Otherwise, no path limit is in effect (ctx->qry.vpath = NULL).
457 */
458 ctx->qry.vpath = cmd->want_vpath ? ctx->qry.path : NULL;
459
413 if (cmd->want_repo && !ctx->repo) { 460 if (cmd->want_repo && !ctx->repo) {
414 cgit_print_http_headers(ctx); 461 cgit_print_http_headers(ctx);
415 cgit_print_docstart(ctx); 462 cgit_print_docstart(ctx);
@@ -541,7 +588,10 @@ static int generate_cached_repolist(const char *path, const char *cached_rc)
541 return errno; 588 return errno;
542 } 589 }
543 idx = cgit_repolist.count; 590 idx = cgit_repolist.count;
544 scan_tree(path, repo_config); 591 if (ctx.cfg.project_list)
592 scan_projects(path, ctx.cfg.project_list, repo_config);
593 else
594 scan_tree(path, repo_config);
545 print_repolist(f, &cgit_repolist, idx); 595 print_repolist(f, &cgit_repolist, idx);
546 if (rename(locked_rc, cached_rc)) 596 if (rename(locked_rc, cached_rc))
547 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 597 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
@@ -555,17 +605,25 @@ static void process_cached_repolist(const char *path)
555 struct stat st; 605 struct stat st;
556 char *cached_rc; 606 char *cached_rc;
557 time_t age; 607 time_t age;
608 unsigned long hash;
558 609
559 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 610 hash = hash_str(path);
560 hash_str(path))); 611 if (ctx.cfg.project_list)
612 hash += hash_str(ctx.cfg.project_list);
613 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, hash));
561 614
562 if (stat(cached_rc, &st)) { 615 if (stat(cached_rc, &st)) {
563 /* Nothing is cached, we need to scan without forking. And 616 /* Nothing is cached, we need to scan without forking. And
564 * if we fail to generate a cached repolist, we need to 617 * if we fail to generate a cached repolist, we need to
565 * invoke scan_tree manually. 618 * invoke scan_tree manually.
566 */ 619 */
567 if (generate_cached_repolist(path, cached_rc)) 620 if (generate_cached_repolist(path, cached_rc)) {
568 scan_tree(path, repo_config); 621 if (ctx.cfg.project_list)
622 scan_projects(path, ctx.cfg.project_list,
623 repo_config);
624 else
625 scan_tree(path, repo_config);
626 }
569 return; 627 return;
570 } 628 }
571 629
@@ -674,7 +732,7 @@ int main(int argc, const char **argv)
674 cgit_repolist.repos = NULL; 732 cgit_repolist.repos = NULL;
675 733
676 cgit_parse_args(argc, argv); 734 cgit_parse_args(argc, argv);
677 parse_configfile(ctx.env.cgit_config, config_cb); 735 parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
678 ctx.repo = NULL; 736 ctx.repo = NULL;
679 http_parse_querystring(ctx.qry.raw, querystring_cb); 737 http_parse_querystring(ctx.qry.raw, querystring_cb);
680 738
diff --git a/cgit.css b/cgit.css
index c47ebc9..0c88b65 100644
--- a/cgit.css
+++ b/cgit.css
@@ -64,7 +64,7 @@ table#header td.sub {
64} 64}
65 65
66table.tabs { 66table.tabs {
67 /* border-bottom: solid 2px #ccc; */ 67 border-bottom: solid 3px #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;
@@ -102,10 +102,16 @@ table.tabs td.form select {
102 font-size: 90%; 102 font-size: 90%;
103} 103}
104 104
105div.path {
106 margin: 0px;
107 padding: 5px 2em 2px 2em;
108 color: #000;
109 background-color: #eee;
110}
111
105div.content { 112div.content {
106 margin: 0px; 113 margin: 0px;
107 padding: 2em; 114 padding: 2em;
108 border-top: solid 3px #ccc;
109 border-bottom: solid 3px #ccc; 115 border-bottom: solid 3px #ccc;
110} 116}
111 117
@@ -158,10 +164,26 @@ table.list td.logmsg {
158 padding: 1em 0.5em 2em 0.5em; 164 padding: 1em 0.5em 2em 0.5em;
159} 165}
160 166
167table.list td.lognotes-label {
168 text-align:right;
169 vertical-align:top;
170}
171
172table.list td.lognotes {
173 font-family: monospace;
174 white-space: pre;
175 padding: 0em 0.5em 2em 0.5em;
176}
177
161table.list td a { 178table.list td a {
162 color: black; 179 color: black;
163} 180}
164 181
182table.list td a.ls-dir {
183 font-weight: bold;
184 color: #00f;
185}
186
165table.list td a:hover { 187table.list td a:hover {
166 color: #00f; 188 color: #00f;
167} 189}
@@ -315,6 +337,24 @@ div.commit-msg {
315 font-family: monospace; 337 font-family: monospace;
316} 338}
317 339
340div.notes-header {
341 font-weight: bold;
342 padding-top: 1.5em;
343}
344
345div.notes {
346 white-space: pre;
347 font-family: monospace;
348 border: solid 1px #ee9;
349 background-color: #ffd;
350 padding: 0.3em 2em 0.3em 1em;
351 float: left;
352}
353
354div.notes-footer {
355 clear: left;
356}
357
318div.diffstat-header { 358div.diffstat-header {
319 font-weight: bold; 359 font-weight: bold;
320 padding-top: 1.5em; 360 padding-top: 1.5em;
@@ -520,7 +560,10 @@ a.deco {
520 border: solid 1px #770000; 560 border: solid 1px #770000;
521} 561}
522 562
523div.commit-subject a { 563div.commit-subject a.branch-deco,
564div.commit-subject a.tag-deco,
565div.commit-subject a.remote-deco,
566div.commit-subject a.deco {
524 margin-left: 1em; 567 margin-left: 1em;
525 font-size: 75%; 568 font-size: 75%;
526} 569}
@@ -601,3 +644,102 @@ table.hgraph div.bar {
601 background-color: #eee; 644 background-color: #eee;
602 height: 1em; 645 height: 1em;
603} 646}
647
648table.ssdiff {
649 width: 100%;
650}
651
652table.ssdiff td {
653 font-size: 75%;
654 font-family: monospace;
655 white-space: pre;
656 padding: 1px 4px 1px 4px;
657 border-left: solid 1px #aaa;
658 border-right: solid 1px #aaa;
659}
660
661table.ssdiff td.add {
662 color: black;
663 background: #cfc;
664 min-width: 50%;
665}
666
667table.ssdiff td.add_dark {
668 color: black;
669 background: #aca;
670 min-width: 50%;
671}
672
673table.ssdiff span.add {
674 background: #cfc;
675 font-weight: bold;
676}
677
678table.ssdiff td.del {
679 color: black;
680 background: #fcc;
681 min-width: 50%;
682}
683
684table.ssdiff td.del_dark {
685 color: black;
686 background: #caa;
687 min-width: 50%;
688}
689
690table.ssdiff span.del {
691 background: #fcc;
692 font-weight: bold;
693}
694
695table.ssdiff td.changed {
696 color: black;
697 background: #ffc;
698 min-width: 50%;
699}
700
701table.ssdiff td.changed_dark {
702 color: black;
703 background: #cca;
704 min-width: 50%;
705}
706
707table.ssdiff td.lineno {
708 color: black;
709 background: #eee;
710 text-align: right;
711 width: 3em;
712 min-width: 3em;
713}
714
715table.ssdiff td.hunk {
716 color: #black;
717 background: #ccf;
718 border-top: solid 1px #aaa;
719 border-bottom: solid 1px #aaa;
720}
721
722table.ssdiff td.head {
723 border-top: solid 1px #aaa;
724 border-bottom: solid 1px #aaa;
725}
726
727table.ssdiff td.head div.head {
728 font-weight: bold;
729 color: black;
730}
731
732table.ssdiff td.foot {
733 border-top: solid 1px #aaa;
734 border-left: none;
735 border-right: none;
736 border-bottom: none;
737}
738
739table.ssdiff td.space {
740 border: none;
741}
742
743table.ssdiff td.space div {
744 min-height: 3em;
745} \ No newline at end of file
diff --git a/cgit.h b/cgit.h
index 6c6c460..4090cd4 100644
--- a/cgit.h
+++ b/cgit.h
@@ -19,6 +19,7 @@
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#include <notes.h>
22 23
23 24
24/* 25/*
@@ -72,6 +73,8 @@ struct cgit_repo {
72 int snapshots; 73 int snapshots;
73 int enable_log_filecount; 74 int enable_log_filecount;
74 int enable_log_linecount; 75 int enable_log_linecount;
76 int enable_remote_branches;
77 int enable_subject_links;
75 int max_stats; 78 int max_stats;
76 time_t mtime; 79 time_t mtime;
77 struct cgit_filter *about_filter; 80 struct cgit_filter *about_filter;
@@ -143,6 +146,11 @@ struct cgit_query {
143 int nohead; 146 int nohead;
144 char *sort; 147 char *sort;
145 int showmsg; 148 int showmsg;
149 int ssdiff;
150 int show_all;
151 int context;
152 int ignorews;
153 char *vpath;
146}; 154};
147 155
148struct cgit_config { 156struct cgit_config {
@@ -159,6 +167,7 @@ struct cgit_config {
159 char *logo; 167 char *logo;
160 char *logo_link; 168 char *logo_link;
161 char *module_link; 169 char *module_link;
170 char *project_list;
162 char *robots; 171 char *robots;
163 char *root_title; 172 char *root_title;
164 char *root_desc; 173 char *root_desc;
@@ -175,25 +184,32 @@ struct cgit_config {
175 int cache_static_ttl; 184 int cache_static_ttl;
176 int embedded; 185 int embedded;
177 int enable_filter_overrides; 186 int enable_filter_overrides;
187 int enable_gitweb_owner;
178 int enable_index_links; 188 int enable_index_links;
179 int enable_log_filecount; 189 int enable_log_filecount;
180 int enable_log_linecount; 190 int enable_log_linecount;
191 int enable_remote_branches;
192 int enable_subject_links;
181 int enable_tree_linenumbers; 193 int enable_tree_linenumbers;
182 int local_time; 194 int local_time;
195 int max_atom_items;
183 int max_repo_count; 196 int max_repo_count;
184 int max_commit_count; 197 int max_commit_count;
185 int max_lock_attempts; 198 int max_lock_attempts;
186 int max_msg_len; 199 int max_msg_len;
187 int max_repodesc_len; 200 int max_repodesc_len;
201 int max_blob_size;
188 int max_stats; 202 int max_stats;
189 int nocache; 203 int nocache;
190 int noplainemail; 204 int noplainemail;
191 int noheader; 205 int noheader;
192 int renamelimit; 206 int renamelimit;
207 int remove_suffix;
193 int snapshots; 208 int snapshots;
194 int summary_branches; 209 int summary_branches;
195 int summary_log; 210 int summary_log;
196 int summary_tags; 211 int summary_tags;
212 int ssdiff;
197 struct string_list mimetypes; 213 struct string_list mimetypes;
198 struct cgit_filter *about_filter; 214 struct cgit_filter *about_filter;
199 struct cgit_filter *commit_filter; 215 struct cgit_filter *commit_filter;
@@ -268,11 +284,12 @@ extern void *cgit_free_commitinfo(struct commitinfo *info);
268extern int cgit_diff_files(const unsigned char *old_sha1, 284extern int cgit_diff_files(const unsigned char *old_sha1,
269 const unsigned char *new_sha1, 285 const unsigned char *new_sha1,
270 unsigned long *old_size, unsigned long *new_size, 286 unsigned long *old_size, unsigned long *new_size,
271 int *binary, linediff_fn fn); 287 int *binary, int context, int ignorews,
288 linediff_fn fn);
272 289
273extern void cgit_diff_tree(const unsigned char *old_sha1, 290extern void cgit_diff_tree(const unsigned char *old_sha1,
274 const unsigned char *new_sha1, 291 const unsigned char *new_sha1,
275 filepair_fn fn, const char *prefix); 292 filepair_fn fn, const char *prefix, int ignorews);
276 293
277extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 294extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
278 295
@@ -291,4 +308,6 @@ extern int cgit_close_filter(struct cgit_filter *filter);
291 308
292extern int readfile(const char *path, char **buf, size_t *size); 309extern int readfile(const char *path, char **buf, size_t *size);
293 310
311extern char *expand_macros(const char *txt);
312
294#endif /* CGIT_H */ 313#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 0c13485..c643fae 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -95,6 +95,11 @@ enable-filter-overrides::
95 Flag which, when set to "1", allows all filter settings to be 95 Flag which, when set to "1", allows all filter settings to be
96 overridden in repository-specific cgitrc files. Default value: none. 96 overridden in repository-specific cgitrc files. Default value: none.
97 97
98enable-gitweb-owner::
99 If set to "1" and scan-path is enabled, we first check each repository
100 for the git config value "gitweb.owner" to determine the owner.
101 Default value: "1". See also: scan-path.
102
98enable-index-links:: 103enable-index-links::
99 Flag which, when set to "1", will make cgit generate extra links for 104 Flag which, when set to "1", will make cgit generate extra links for
100 each repo in the repository index (specifically, to the "summary", 105 each repo in the repository index (specifically, to the "summary",
@@ -110,6 +115,17 @@ enable-log-linecount::
110 and removed lines for each commit on the repository log page. Default 115 and removed lines for each commit on the repository log page. Default
111 value: "0". 116 value: "0".
112 117
118enable-remote-branches::
119 Flag which, when set to "1", will make cgit display remote branches
120 in the summary and refs views. Default value: "0". See also:
121 "repo.enable-remote-branches".
122
123enable-subject-links::
124 Flag which, when set to "1", will make cgit use the subject of the
125 parent commit as link text when generating links to parent commits
126 in commit view. Default value: "0". See also:
127 "repo.enable-subject-links".
128
113enable-tree-linenumbers:: 129enable-tree-linenumbers::
114 Flag which, when set to "1", will make cgit generate linenumber links 130 Flag which, when set to "1", will make cgit generate linenumber links
115 for plaintext blobs printed in the tree view. Default value: "1". 131 for plaintext blobs printed in the tree view. Default value: "1".
@@ -161,6 +177,10 @@ logo-link::
161 calculated url of the repository index page will be used. Default 177 calculated url of the repository index page will be used. Default
162 value: none. 178 value: none.
163 179
180max-atom-items::
181 Specifies the number of items to display in atom feeds view. Default
182 value: "10".
183
164max-commit-count:: 184max-commit-count::
165 Specifies the number of entries to list per page in "log" view. Default 185 Specifies the number of entries to list per page in "log" view. Default
166 value: "50". 186 value: "50".
@@ -177,6 +197,10 @@ max-repodesc-length::
177 Specifies the maximum number of repo description characters to display 197 Specifies the maximum number of repo description characters to display
178 on the repository index page. Default value: "80". 198 on the repository index page. Default value: "80".
179 199
200max-blob-size::
201 Specifies the maximum size of a blob to display HTML for in KBytes.
202 Default value: "0" (limit disabled).
203
180max-stats:: 204max-stats::
181 Set the default maximum statistics period. Valid values are "week", 205 Set the default maximum statistics period. Valid values are "week",
182 "month", "quarter" and "year". If unspecified, statistics are 206 "month", "quarter" and "year". If unspecified, statistics are
@@ -205,6 +229,16 @@ noheader::
205 Flag which, when set to "1", will make cgit omit the standard header 229 Flag which, when set to "1", will make cgit omit the standard header
206 on all pages. Default value: none. See also: "embedded". 230 on all pages. Default value: none. See also: "embedded".
207 231
232project-list::
233 A list of subdirectories inside of scan-path, relative to it, that
234 should loaded as git repositories. This must be defined prior to
235 scan-path. Default value: none. See also: scan-path.
236
237remove-suffix::
238 If set to "1" and scan-path is enabled, if any repositories are found
239 with a suffix of ".git", this suffix will be removed for the url and
240 name. Default value: "0". See also: scan-path.
241
208renamelimit:: 242renamelimit::
209 Maximum number of files to consider when detecting renames. The value 243 Maximum number of files to consider when detecting renames. The value
210 "-1" uses the compiletime value in git (for further info, look at 244 "-1" uses the compiletime value in git (for further info, look at
@@ -234,13 +268,20 @@ root-title::
234scan-path:: 268scan-path::
235 A path which will be scanned for repositories. If caching is enabled, 269 A path which will be scanned for repositories. If caching is enabled,
236 the result will be cached as a cgitrc include-file in the cache 270 the result will be cached as a cgitrc include-file in the cache
237 directory. Default value: none. See also: cache-scanrc-ttl. 271 directory. If project-list has been defined prior to scan-path,
272 scan-path loads only the directories listed in the file pointed to by
273 project-list. Default value: none. See also: cache-scanrc-ttl,
274 project-list.
238 275
239section:: 276section::
240 The name of the current repository section - all repositories defined 277 The name of the current repository section - all repositories defined
241 after this option will inherit the current section name. Default value: 278 after this option will inherit the current section name. Default value:
242 none. 279 none.
243 280
281side-by-side-diffs::
282 If set to "1" shows side-by-side diffs instead of unidiffs per
283 default. Default value: "0".
284
244snapshots:: 285snapshots::
245 Text which specifies the default set of snapshot formats generated by 286 Text which specifies the default set of snapshot formats generated by
246 cgit. The value is a space-separated list of zero or more of the 287 cgit. The value is a space-separated list of zero or more of the
@@ -304,6 +345,14 @@ repo.enable-log-linecount::
304 A flag which can be used to disable the global setting 345 A flag which can be used to disable the global setting
305 `enable-log-linecount'. Default value: none. 346 `enable-log-linecount'. Default value: none.
306 347
348repo.enable-remote-branches::
349 Flag which, when set to "1", will make cgit display remote branches
350 in the summary and refs views. Default value: <enable-remote-branches>.
351
352repo.enable-subject-links::
353 A flag which can be used to override the global setting
354 `enable-subject-links'. Default value: none.
355
307repo.max-stats:: 356repo.max-stats::
308 Override the default maximum statistics period. Valid values are equal 357 Override the default maximum statistics period. Valid values are equal
309 to the values specified for the global "max-stats" setting. Default 358 to the values specified for the global "max-stats" setting. Default
@@ -322,7 +371,9 @@ repo.path::
322 371
323repo.readme:: 372repo.readme::
324 A path (relative to <repo.path>) which specifies a file to include 373 A path (relative to <repo.path>) which specifies a file to include
325 verbatim as the "About" page for this repo. Default value: none. 374 verbatim as the "About" page for this repo. You may also specify a
375 git refspec by head or by hash by prepending the refspec followed by
376 a colon. For example, "master:docs/readme.mkd" Default value: none.
326 377
327repo.snapshots:: 378repo.snapshots::
328 A mask of allowed snapshot-formats for this repo, restricted by the 379 A mask of allowed snapshot-formats for this repo, restricted by the
@@ -413,7 +464,7 @@ snapshots=tar.gz tar.bz2 zip
413## List of common mimetypes 464## List of common mimetypes
414## 465##
415 466
416mimetype.git=image/git 467mimetype.gif=image/gif
417mimetype.html=text/html 468mimetype.html=text/html
418mimetype.jpg=image/jpeg 469mimetype.jpg=image/jpeg
419mimetype.jpeg=image/jpeg 470mimetype.jpeg=image/jpeg
@@ -499,3 +550,4 @@ will generate the following html element:
499AUTHOR 550AUTHOR
500------ 551------
501Lars Hjemli <hjemli@gmail.com> 552Lars Hjemli <hjemli@gmail.com>
553Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/cmd.c b/cmd.c
index 766f903..6dc9f5e 100644
--- a/cmd.c
+++ b/cmd.c
@@ -33,7 +33,7 @@ static void HEAD_fn(struct cgit_context *ctx)
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, ctx->cfg.max_atom_items);
37} 37}
38 38
39static void about_fn(struct cgit_context *ctx) 39static void about_fn(struct cgit_context *ctx)
@@ -51,7 +51,7 @@ static void blob_fn(struct cgit_context *ctx)
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, ctx->qry.path);
55} 55}
56 56
57static void diff_fn(struct cgit_context *ctx) 57static void diff_fn(struct cgit_context *ctx)
@@ -90,7 +90,7 @@ static void repolist_fn(struct cgit_context *ctx)
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, ctx->qry.path);
94} 94}
95 95
96static void plain_fn(struct cgit_context *ctx) 96static void plain_fn(struct cgit_context *ctx)
@@ -129,31 +129,31 @@ static void tree_fn(struct cgit_context *ctx)
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
diff --git a/cmd.h b/cmd.h
index ec9e691..8dc01bd 100644
--- a/cmd.h
+++ b/cmd.h
@@ -7,7 +7,8 @@ struct 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);
diff --git a/filters/commit-links.sh b/filters/commit-links.sh
index 165a533..110c609 100755
--- a/filters/commit-links.sh
+++ b/filters/commit-links.sh
@@ -1,12 +1,14 @@
1#!/bin/sh 1#!/bin/sh
2# This script can be used to generate links in commit messages - the first 2# This script can be used to generate links in commit messages.
3# sed expression generates links to commits referenced by their SHA1, while
4# the second expression generates links to a fictional bugtracker.
5# 3#
6# To use this script, refer to this file with either the commit-filter or the 4# To use this script, refer to this file with either the commit-filter or the
7# repo.commit-filter options in cgitrc. 5# repo.commit-filter options in cgitrc.
8 6
9sed -re ' 7# This expression generates links to commits referenced by their SHA1.
10s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g 8regex=$regex'
11s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g 9s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g'
12' 10# This expression generates links to a fictional bugtracker.
11regex=$regex'
12s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g'
13
14sed -re "$regex"
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
index 999ad0c..6b1c576 100755
--- a/filters/syntax-highlighting.sh
+++ b/filters/syntax-highlighting.sh
@@ -3,6 +3,10 @@
3# tree-view by refering to this file with the source-filter or repo.source- 3# tree-view by refering to this file with the source-filter or repo.source-
4# filter options in cgitrc. 4# filter options in cgitrc.
5# 5#
6# This script requires a shell supporting the ${var##pattern} syntax.
7# It is supported by at least dash and bash, however busybox environments
8# might have to use an external call to sed instead.
9#
6# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax 10# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
7# highlighting, so you'll probably want something like the following included 11# highlighting, so you'll probably want something like the following included
8# in your css file (generated by highlight 2.4.8 and adapted for cgit): 12# in your css file (generated by highlight 2.4.8 and adapted for cgit):
@@ -20,20 +24,11 @@
20# table.blob .kwc { color:#000000; font-weight:bold; } 24# table.blob .kwc { color:#000000; font-weight:bold; }
21# table.blob .kwd { color:#010181; } 25# table.blob .kwd { color:#010181; }
22 26
23case "$1" in 27# store filename and extension in local vars
24 *.c) 28BASENAME="$1"
25 highlight -f -I -X -S c 29EXTENSION="${BASENAME##*.}"
26 ;; 30
27 *.h) 31# map Makefile and Makefile.* to .mk
28 highlight -f -I -X -S c