summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2007-12-10 20:47:29 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2007-12-11 01:43:24 (UTC)
commit620bb3e5e4ff87da740fe7232ba74330b5f862d4 (patch) (unidiff)
tree152cc090cfc56c387393f6aa6666b9d87ad4a23a
parentafcdd083dab81afef744e261d81a452698188c30 (diff)
downloadcgit-620bb3e5e4ff87da740fe7232ba74330b5f862d4.zip
cgit-620bb3e5e4ff87da740fe7232ba74330b5f862d4.tar.gz
cgit-620bb3e5e4ff87da740fe7232ba74330b5f862d4.tar.bz2
Add plain patch view
The new view mimics the output from `git format-patch`, making it possible to cherry-pick directly from cgit with something like `curl $url | git am`. Inspired by a patch to `git-apply` by Mike Hommey: http://thread.gmane.org/gmane.comp.version-control.git/67611/focus=67610 Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c5
-rw-r--r--cgit.h2
-rw-r--r--shared.c2
-rw-r--r--ui-patch.c105
-rw-r--r--ui-shared.c8
6 files changed, 122 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 72c189c..eed59df 100644
--- a/Makefile
+++ b/Makefile
@@ -1,83 +1,83 @@
1CGIT_VERSION = v0.7.1 1CGIT_VERSION = v0.7.1
2CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit 3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_CONFIG = /etc/cgitrc 4CGIT_CONFIG = /etc/cgitrc
5CACHE_ROOT = /var/cache/cgit 5CACHE_ROOT = /var/cache/cgit
6SHA1_HEADER = <openssl/sha.h> 6SHA1_HEADER = <openssl/sha.h>
7GIT_VER = 1.5.3.5 7GIT_VER = 1.5.3.5
8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
9 9
10# 10#
11# Let the user override the above settings. 11# Let the user override the above settings.
12# 12#
13-include cgit.conf 13-include cgit.conf
14 14
15 15
16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ 18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
19 ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o 19 ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o ui-patch.o
20 20
21 21
22ifdef NEEDS_LIBICONV 22ifdef NEEDS_LIBICONV
23 EXTLIBS += -liconv 23 EXTLIBS += -liconv
24endif 24endif
25 25
26 26
27.PHONY: all git test install clean distclean emptycache force-version get-git 27.PHONY: all git test install clean distclean emptycache force-version get-git
28 28
29all: cgit git 29all: cgit git
30 30
31VERSION: force-version 31VERSION: force-version
32 @./gen-version.sh "$(CGIT_VERSION)" 32 @./gen-version.sh "$(CGIT_VERSION)"
33-include VERSION 33-include VERSION
34 34
35 35
36CFLAGS += -g -Wall -Igit 36CFLAGS += -g -Wall -Igit
37CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 37CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
38CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 38CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
39CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 39CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
40CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 40CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
41CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 41CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
42 42
43 43
44cgit: cgit.c $(OBJECTS) 44cgit: cgit.c $(OBJECTS)
45 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 45 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
46 46
47$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION 47$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
48 48
49git/xdiff/lib.a: | git 49git/xdiff/lib.a: | git
50 50
51git/libgit.a: | git 51git/libgit.a: | git
52 52
53git: 53git:
54 cd git && $(MAKE) xdiff/lib.a 54 cd git && $(MAKE) xdiff/lib.a
55 cd git && $(MAKE) libgit.a 55 cd git && $(MAKE) libgit.a
56 56
57test: all 57test: all
58 $(MAKE) -C tests 58 $(MAKE) -C tests
59 59
60install: all 60install: all
61 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH) 61 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH)
62 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 62 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
63 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css 63 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css
64 install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png 64 install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png
65 65
66uninstall: 66uninstall:
67 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 67 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
68 rm -f $(CGIT_SCRIPT_PATH)/cgit.css 68 rm -f $(CGIT_SCRIPT_PATH)/cgit.css
69 rm -f $(CGIT_SCRIPT_PATH)/cgit.png 69 rm -f $(CGIT_SCRIPT_PATH)/cgit.png
70 70
71clean: 71clean:
72 rm -f cgit VERSION *.o 72 rm -f cgit VERSION *.o
73 cd git && $(MAKE) clean 73 cd git && $(MAKE) clean
74 74
75distclean: clean 75distclean: clean
76 git clean -d -x 76 git clean -d -x
77 cd git && git clean -d -x 77 cd git && git clean -d -x
78 78
79emptycache: 79emptycache:
80 rm -rf $(DESTDIR)$(CACHE_ROOT)/* 80 rm -rf $(DESTDIR)$(CACHE_ROOT)/*
81 81
82get-git: 82get-git:
83 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git 83 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 142e416..9ca93a7 100644
--- a/cgit.c
+++ b/cgit.c
@@ -14,128 +14,133 @@ static int cgit_prepare_cache(struct cacheitem *item)
14 char *title = fmt("%s - %s", cgit_root_title, "Bad request"); 14 char *title = fmt("%s - %s", cgit_root_title, "Bad request");
15 cgit_print_docstart(title, item); 15 cgit_print_docstart(title, item);
16 cgit_print_pageheader(title, 0); 16 cgit_print_pageheader(title, 0);
17 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo)); 17 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo));
18 cgit_print_docend(); 18 cgit_print_docend();
19 return 0; 19 return 0;
20 } 20 }
21 21
22 if (!cgit_repo) { 22 if (!cgit_repo) {
23 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root)); 23 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root));
24 item->ttl = cgit_cache_root_ttl; 24 item->ttl = cgit_cache_root_ttl;
25 return 1; 25 return 1;
26 } 26 }
27 27
28 if (!cgit_cmd) { 28 if (!cgit_cmd) {
29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root, 29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
30 cache_safe_filename(cgit_repo->url), 30 cache_safe_filename(cgit_repo->url),
31 cache_safe_filename(cgit_querystring))); 31 cache_safe_filename(cgit_querystring)));
32 item->ttl = cgit_cache_repo_ttl; 32 item->ttl = cgit_cache_repo_ttl;
33 } else { 33 } else {
34 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root, 34 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
35 cache_safe_filename(cgit_repo->url), 35 cache_safe_filename(cgit_repo->url),
36 cgit_query_page, 36 cgit_query_page,
37 cache_safe_filename(cgit_querystring))); 37 cache_safe_filename(cgit_querystring)));
38 if (cgit_query_has_symref) 38 if (cgit_query_has_symref)
39 item->ttl = cgit_cache_dynamic_ttl; 39 item->ttl = cgit_cache_dynamic_ttl;
40 else if (cgit_query_has_sha1) 40 else if (cgit_query_has_sha1)
41 item->ttl = cgit_cache_static_ttl; 41 item->ttl = cgit_cache_static_ttl;
42 else 42 else
43 item->ttl = cgit_cache_repo_ttl; 43 item->ttl = cgit_cache_repo_ttl;
44 } 44 }
45 return 1; 45 return 1;
46} 46}
47 47
48static void cgit_print_repo_page(struct cacheitem *item) 48static void cgit_print_repo_page(struct cacheitem *item)
49{ 49{
50 char *title; 50 char *title;
51 int show_search; 51 int show_search;
52 52
53 if (!cgit_query_head) 53 if (!cgit_query_head)
54 cgit_query_head = cgit_repo->defbranch; 54 cgit_query_head = cgit_repo->defbranch;
55 55
56 if (chdir(cgit_repo->path)) { 56 if (chdir(cgit_repo->path)) {
57 title = fmt("%s - %s", cgit_root_title, "Bad request"); 57 title = fmt("%s - %s", cgit_root_title, "Bad request");
58 cgit_print_docstart(title, item); 58 cgit_print_docstart(title, item);
59 cgit_print_pageheader(title, 0); 59 cgit_print_pageheader(title, 0);
60 cgit_print_error(fmt("Unable to scan repository: %s", 60 cgit_print_error(fmt("Unable to scan repository: %s",
61 strerror(errno))); 61 strerror(errno)));
62 cgit_print_docend(); 62 cgit_print_docend();
63 return; 63 return;
64 } 64 }
65 65
66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
67 show_search = 0; 67 show_search = 0;
68 setenv("GIT_DIR", cgit_repo->path, 1); 68 setenv("GIT_DIR", cgit_repo->path, 1);
69 69
70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
71 cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1, 71 cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1,
72 cgit_repobasename(cgit_repo->url), 72 cgit_repobasename(cgit_repo->url),
73 cgit_query_path, 73 cgit_query_path,
74 cgit_repo->snapshots ); 74 cgit_repo->snapshots );
75 return; 75 return;
76 } 76 }
77 77
78 if (cgit_cmd == CMD_PATCH) {
79 cgit_print_patch(cgit_query_sha1, item);
80 return;
81 }
82
78 if (cgit_cmd == CMD_BLOB) { 83 if (cgit_cmd == CMD_BLOB) {
79 cgit_print_blob(item, cgit_query_sha1, cgit_query_path); 84 cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
80 return; 85 return;
81 } 86 }
82 87
83 show_search = (cgit_cmd == CMD_LOG); 88 show_search = (cgit_cmd == CMD_LOG);
84 cgit_print_docstart(title, item); 89 cgit_print_docstart(title, item);
85 if (!cgit_cmd) { 90 if (!cgit_cmd) {
86 cgit_print_pageheader("summary", show_search); 91 cgit_print_pageheader("summary", show_search);
87 cgit_print_summary(); 92 cgit_print_summary();
88 cgit_print_docend(); 93 cgit_print_docend();
89 return; 94 return;
90 } 95 }
91 96
92 cgit_print_pageheader(cgit_query_page, show_search); 97 cgit_print_pageheader(cgit_query_page, show_search);
93 98
94 switch(cgit_cmd) { 99 switch(cgit_cmd) {
95 case CMD_LOG: 100 case CMD_LOG:
96 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 101 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
97 cgit_max_commit_count, cgit_query_grep, cgit_query_search, 102 cgit_max_commit_count, cgit_query_grep, cgit_query_search,
98 cgit_query_path, 1); 103 cgit_query_path, 1);
99 break; 104 break;
100 case CMD_TREE: 105 case CMD_TREE:
101 cgit_print_tree(cgit_query_sha1, cgit_query_path); 106 cgit_print_tree(cgit_query_sha1, cgit_query_path);
102 break; 107 break;
103 case CMD_COMMIT: 108 case CMD_COMMIT:
104 cgit_print_commit(cgit_query_sha1); 109 cgit_print_commit(cgit_query_sha1);
105 break; 110 break;
106 case CMD_REFS: 111 case CMD_REFS:
107 cgit_print_refs(); 112 cgit_print_refs();
108 break; 113 break;
109 case CMD_TAG: 114 case CMD_TAG:
110 cgit_print_tag(cgit_query_sha1); 115 cgit_print_tag(cgit_query_sha1);
111 break; 116 break;
112 case CMD_DIFF: 117 case CMD_DIFF:
113 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path); 118 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path);
114 break; 119 break;
115 default: 120 default:
116 cgit_print_error("Invalid request"); 121 cgit_print_error("Invalid request");
117 } 122 }
118 cgit_print_docend(); 123 cgit_print_docend();
119} 124}
120 125
121static void cgit_fill_cache(struct cacheitem *item, int use_cache) 126static void cgit_fill_cache(struct cacheitem *item, int use_cache)
122{ 127{
123 static char buf[PATH_MAX]; 128 static char buf[PATH_MAX];
124 int stdout2; 129 int stdout2;
125 130
126 getcwd(buf, sizeof(buf)); 131 getcwd(buf, sizeof(buf));
127 item->st.st_mtime = time(NULL); 132 item->st.st_mtime = time(NULL);
128 133
129 if (use_cache) { 134 if (use_cache) {
130 stdout2 = chk_positive(dup(STDOUT_FILENO), 135 stdout2 = chk_positive(dup(STDOUT_FILENO),
131 "Preserving STDOUT"); 136 "Preserving STDOUT");
132 chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); 137 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
133 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); 138 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
134 } 139 }
135 140
136 if (cgit_repo) 141 if (cgit_repo)
137 cgit_print_repo_page(item); 142 cgit_print_repo_page(item);
138 else 143 else
139 cgit_print_repolist(item); 144 cgit_print_repolist(item);
140 145
141 if (use_cache) { 146 if (use_cache) {
diff --git a/cgit.h b/cgit.h
index f223dbc..66c40b9 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,96 +1,97 @@
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 <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19#include <utf8.h> 19#include <utf8.h>
20 20
21 21
22/* 22/*
23 * The valid cgit repo-commands 23 * The valid cgit repo-commands
24 */ 24 */
25#define CMD_LOG 1 25#define CMD_LOG 1
26#define CMD_COMMIT 2 26#define CMD_COMMIT 2
27#define CMD_DIFF 3 27#define CMD_DIFF 3
28#define CMD_TREE 4 28#define CMD_TREE 4
29#define CMD_BLOB 5 29#define CMD_BLOB 5
30#define CMD_SNAPSHOT 6 30#define CMD_SNAPSHOT 6
31#define CMD_TAG 7 31#define CMD_TAG 7
32#define CMD_REFS 8 32#define CMD_REFS 8
33#define CMD_PATCH 9
33 34
34/* 35/*
35 * Dateformats used on misc. pages 36 * Dateformats used on misc. pages
36 */ 37 */
37#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 38#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
38#define FMT_SHORTDATE "%Y-%m-%d" 39#define FMT_SHORTDATE "%Y-%m-%d"
39 40
40 41
41/* 42/*
42 * Limits used for relative dates 43 * Limits used for relative dates
43 */ 44 */
44#define TM_MIN 60 45#define TM_MIN 60
45#define TM_HOUR (TM_MIN * 60) 46#define TM_HOUR (TM_MIN * 60)
46#define TM_DAY (TM_HOUR * 24) 47#define TM_DAY (TM_HOUR * 24)
47#define TM_WEEK (TM_DAY * 7) 48#define TM_WEEK (TM_DAY * 7)
48#define TM_YEAR (TM_DAY * 365) 49#define TM_YEAR (TM_DAY * 365)
49#define TM_MONTH (TM_YEAR / 12.0) 50#define TM_MONTH (TM_YEAR / 12.0)
50 51
51 52
52/* 53/*
53 * Default encoding 54 * Default encoding
54 */ 55 */
55#define PAGE_ENCODING "UTF-8" 56#define PAGE_ENCODING "UTF-8"
56 57
57typedef void (*configfn)(const char *name, const char *value); 58typedef void (*configfn)(const char *name, const char *value);
58typedef void (*filepair_fn)(struct diff_filepair *pair); 59typedef void (*filepair_fn)(struct diff_filepair *pair);
59typedef void (*linediff_fn)(char *line, int len); 60typedef void (*linediff_fn)(char *line, int len);
60 61
61struct cacheitem { 62struct cacheitem {
62 char *name; 63 char *name;
63 struct stat st; 64 struct stat st;
64 int ttl; 65 int ttl;
65 int fd; 66 int fd;
66}; 67};
67 68
68struct repoinfo { 69struct repoinfo {
69 char *url; 70 char *url;
70 char *name; 71 char *name;
71 char *path; 72 char *path;
72 char *desc; 73 char *desc;
73 char *owner; 74 char *owner;
74 char *defbranch; 75 char *defbranch;
75 char *group; 76 char *group;
76 char *module_link; 77 char *module_link;
77 char *readme; 78 char *readme;
78 char *clone_url; 79 char *clone_url;
79 int snapshots; 80 int snapshots;
80 int enable_log_filecount; 81 int enable_log_filecount;
81 int enable_log_linecount; 82 int enable_log_linecount;
82}; 83};
83 84
84struct repolist { 85struct repolist {
85 int length; 86 int length;
86 int count; 87 int count;
87 struct repoinfo *repos; 88 struct repoinfo *repos;
88}; 89};
89 90
90struct commitinfo { 91struct commitinfo {
91 struct commit *commit; 92 struct commit *commit;
92 char *author; 93 char *author;
93 char *author_email; 94 char *author_email;
94 unsigned long author_date; 95 unsigned long author_date;
95 char *committer; 96 char *committer;
96 char *committer_email; 97 char *committer_email;
@@ -222,72 +223,73 @@ extern void html_option(char *value, char *text, char *selected_value);
222extern void html_link_open(char *url, char *title, char *class); 223extern void html_link_open(char *url, char *title, char *class);
223extern void html_link_close(void); 224extern void html_link_close(void);
224extern void html_filemode(unsigned short mode); 225extern void html_filemode(unsigned short mode);
225extern int html_include(const char *filename); 226extern int html_include(const char *filename);
226 227
227extern int cgit_read_config(const char *filename, configfn fn); 228extern int cgit_read_config(const char *filename, configfn fn);
228extern int cgit_parse_query(char *txt, configfn fn); 229extern int cgit_parse_query(char *txt, configfn fn);
229extern struct commitinfo *cgit_parse_commit(struct commit *commit); 230extern struct commitinfo *cgit_parse_commit(struct commit *commit);
230extern struct taginfo *cgit_parse_tag(struct tag *tag); 231extern struct taginfo *cgit_parse_tag(struct tag *tag);
231extern void cgit_parse_url(const char *url); 232extern void cgit_parse_url(const char *url);
232 233
233extern char *cache_safe_filename(const char *unsafe); 234extern char *cache_safe_filename(const char *unsafe);
234extern int cache_lock(struct cacheitem *item); 235extern int cache_lock(struct cacheitem *item);
235extern int cache_unlock(struct cacheitem *item); 236extern int cache_unlock(struct cacheitem *item);
236extern int cache_cancel_lock(struct cacheitem *item); 237extern int cache_cancel_lock(struct cacheitem *item);
237extern int cache_exist(struct cacheitem *item); 238extern int cache_exist(struct cacheitem *item);
238extern int cache_expired(struct cacheitem *item); 239extern int cache_expired(struct cacheitem *item);
239 240
240extern char *cgit_repourl(const char *reponame); 241extern char *cgit_repourl(const char *reponame);
241extern char *cgit_fileurl(const char *reponame, const char *pagename, 242extern char *cgit_fileurl(const char *reponame, const char *pagename,
242 const char *filename, const char *query); 243 const char *filename, const char *query);
243extern char *cgit_pageurl(const char *reponame, const char *pagename, 244extern char *cgit_pageurl(const char *reponame, const char *pagename,
244 const char *query); 245 const char *query);
245 246
246extern const char *cgit_repobasename(const char *reponame); 247extern const char *cgit_repobasename(const char *reponame);
247 248
248extern void cgit_tree_link(char *name, char *title, char *class, char *head, 249extern void cgit_tree_link(char *name, char *title, char *class, char *head,
249 char *rev, char *path); 250 char *rev, char *path);
250extern void cgit_log_link(char *name, char *title, char *class, char *head, 251extern void cgit_log_link(char *name, char *title, char *class, char *head,
251 char *rev, char *path, int ofs, char *grep, 252 char *rev, char *path, int ofs, char *grep,
252 char *pattern); 253 char *pattern);
253extern void cgit_commit_link(char *name, char *title, char *class, char *head, 254extern void cgit_commit_link(char *name, char *title, char *class, char *head,
254 char *rev); 255 char *rev);
255extern void cgit_refs_link(char *name, char *title, char *class, char *head, 256extern void cgit_refs_link(char *name, char *title, char *class, char *head,
256 char *rev, char *path); 257 char *rev, char *path);
257extern void cgit_snapshot_link(char *name, char *title, char *class, 258extern void cgit_snapshot_link(char *name, char *title, char *class,
258 char *head, char *rev, char *archivename); 259 char *head, char *rev, char *archivename);
259extern void cgit_diff_link(char *name, char *title, char *class, char *head, 260extern void cgit_diff_link(char *name, char *title, char *class, char *head,
260 char *new_rev, char *old_rev, char *path); 261 char *new_rev, char *old_rev, char *path);
261 262
262extern void cgit_object_link(struct object *obj); 263extern void cgit_object_link(struct object *obj);
263 264
264extern void cgit_print_error(char *msg); 265extern void cgit_print_error(char *msg);
265extern void cgit_print_date(time_t secs, char *format); 266extern void cgit_print_date(time_t secs, char *format);
266extern void cgit_print_age(time_t t, time_t max_relative, char *format); 267extern void cgit_print_age(time_t t, time_t max_relative, char *format);
267extern void cgit_print_docstart(char *title, struct cacheitem *item); 268extern void cgit_print_docstart(char *title, struct cacheitem *item);
268extern void cgit_print_docend(); 269extern void cgit_print_docend();
269extern void cgit_print_pageheader(char *title, int show_search); 270extern void cgit_print_pageheader(char *title, int show_search);
270extern void cgit_print_snapshot_start(const char *mimetype, 271extern void cgit_print_snapshot_start(const char *mimetype,
271 const char *filename, 272 const char *filename,
272 struct cacheitem *item); 273 struct cacheitem *item);
273extern void cgit_print_branches(int maxcount); 274extern void cgit_print_branches(int maxcount);
274extern void cgit_print_tags(int maxcount); 275extern void cgit_print_tags(int maxcount);
275 276
276extern void cgit_print_repolist(struct cacheitem *item); 277extern void cgit_print_repolist(struct cacheitem *item);
277extern void cgit_print_summary(); 278extern void cgit_print_summary();
278extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, 279extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
279 char *pattern, char *path, int pager); 280 char *pattern, char *path, int pager);
280extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 281extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
281extern void cgit_print_tree(const char *rev, char *path); 282extern void cgit_print_tree(const char *rev, char *path);
282extern void cgit_print_commit(char *hex); 283extern void cgit_print_commit(char *hex);
283extern void cgit_print_refs(); 284extern void cgit_print_refs();
284extern void cgit_print_tag(char *revname); 285extern void cgit_print_tag(char *revname);
285extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix); 286extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix);
287extern void cgit_print_patch(char *hex, struct cacheitem *item);
286extern void cgit_print_snapshot(struct cacheitem *item, const char *head, 288extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
287 const char *hex, const char *prefix, 289 const char *hex, const char *prefix,
288 const char *filename, int snapshot); 290 const char *filename, int snapshot);
289extern void cgit_print_snapshot_links(const char *repo, const char *head, 291extern void cgit_print_snapshot_links(const char *repo, const char *head,
290 const char *hex, int snapshots); 292 const char *hex, int snapshots);
291extern int cgit_parse_snapshots_mask(const char *str); 293extern int cgit_parse_snapshots_mask(const char *str);
292 294
293#endif /* CGIT_H */ 295#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index 2c309f5..9c992c8 100644
--- a/shared.c
+++ b/shared.c
@@ -9,129 +9,129 @@
9#include "cgit.h" 9#include "cgit.h"
10 10
11struct repolist cgit_repolist; 11struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13int cgit_cmd; 13int cgit_cmd;
14 14
15const char *cgit_version = CGIT_VERSION; 15const char *cgit_version = CGIT_VERSION;
16 16
17char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
18char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
19char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
20char *cgit_index_header = NULL; 20char *cgit_index_header = NULL;
21char *cgit_index_info = NULL; 21char *cgit_index_info = NULL;
22char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 22char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
23char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; 23char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
24char *cgit_agefile = "info/web/last-modified"; 24char *cgit_agefile = "info/web/last-modified";
25char *cgit_virtual_root = NULL; 25char *cgit_virtual_root = NULL;
26char *cgit_script_name = CGIT_SCRIPT_NAME; 26char *cgit_script_name = CGIT_SCRIPT_NAME;
27char *cgit_cache_root = CGIT_CACHE_ROOT; 27char *cgit_cache_root = CGIT_CACHE_ROOT;
28char *cgit_repo_group = NULL; 28char *cgit_repo_group = NULL;
29char *cgit_robots = "index, nofollow"; 29char *cgit_robots = "index, nofollow";
30char *cgit_clone_prefix = NULL; 30char *cgit_clone_prefix = NULL;
31 31
32int cgit_nocache = 0; 32int cgit_nocache = 0;
33int cgit_snapshots = 0; 33int cgit_snapshots = 0;
34int cgit_enable_index_links = 0; 34int cgit_enable_index_links = 0;
35int cgit_enable_log_filecount = 0; 35int cgit_enable_log_filecount = 0;
36int cgit_enable_log_linecount = 0; 36int cgit_enable_log_linecount = 0;
37int cgit_max_lock_attempts = 5; 37int cgit_max_lock_attempts = 5;
38int cgit_cache_root_ttl = 5; 38int cgit_cache_root_ttl = 5;
39int cgit_cache_repo_ttl = 5; 39int cgit_cache_repo_ttl = 5;
40int cgit_cache_dynamic_ttl = 5; 40int cgit_cache_dynamic_ttl = 5;
41int cgit_cache_static_ttl = -1; 41int cgit_cache_static_ttl = -1;
42int cgit_cache_max_create_time = 5; 42int cgit_cache_max_create_time = 5;
43int cgit_summary_log = 0; 43int cgit_summary_log = 0;
44int cgit_summary_tags = 0; 44int cgit_summary_tags = 0;
45int cgit_summary_branches = 0; 45int cgit_summary_branches = 0;
46int cgit_renamelimit = -1; 46int cgit_renamelimit = -1;
47 47
48int cgit_max_msg_len = 60; 48int cgit_max_msg_len = 60;
49int cgit_max_repodesc_len = 60; 49int cgit_max_repodesc_len = 60;
50int cgit_max_commit_count = 50; 50int cgit_max_commit_count = 50;
51 51
52int cgit_query_has_symref = 0; 52int cgit_query_has_symref = 0;
53int cgit_query_has_sha1 = 0; 53int cgit_query_has_sha1 = 0;
54 54
55char *cgit_querystring = NULL; 55char *cgit_querystring = NULL;
56char *cgit_query_repo = NULL; 56char *cgit_query_repo = NULL;
57char *cgit_query_page = NULL; 57char *cgit_query_page = NULL;
58char *cgit_query_head = NULL; 58char *cgit_query_head = NULL;
59char *cgit_query_search = NULL; 59char *cgit_query_search = NULL;
60char *cgit_query_grep = NULL; 60char *cgit_query_grep = NULL;
61char *cgit_query_sha1 = NULL; 61char *cgit_query_sha1 = NULL;
62char *cgit_query_sha2 = NULL; 62char *cgit_query_sha2 = NULL;
63char *cgit_query_path = NULL; 63char *cgit_query_path = NULL;
64char *cgit_query_name = NULL; 64char *cgit_query_name = NULL;
65int cgit_query_ofs = 0; 65int cgit_query_ofs = 0;
66 66
67int htmlfd = 0; 67int htmlfd = 0;
68 68
69 69
70int cgit_get_cmd_index(const char *cmd) 70int cgit_get_cmd_index(const char *cmd)
71{ 71{
72 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 72 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
73 "snapshot", "tag", "refs", NULL}; 73 "snapshot", "tag", "refs", "patch", NULL};
74 int i; 74 int i;
75 75
76 for(i = 0; cmds[i]; i++) 76 for(i = 0; cmds[i]; i++)
77 if (!strcmp(cmd, cmds[i])) 77 if (!strcmp(cmd, cmds[i]))
78 return i + 1; 78 return i + 1;
79 return 0; 79 return 0;
80} 80}
81 81
82int chk_zero(int result, char *msg) 82int chk_zero(int result, char *msg)
83{ 83{
84 if (result != 0) 84 if (result != 0)
85 die("%s: %s", msg, strerror(errno)); 85 die("%s: %s", msg, strerror(errno));
86 return result; 86 return result;
87} 87}
88 88
89int chk_positive(int result, char *msg) 89int chk_positive(int result, char *msg)
90{ 90{
91 if (result <= 0) 91 if (result <= 0)
92 die("%s: %s", msg, strerror(errno)); 92 die("%s: %s", msg, strerror(errno));
93 return result; 93 return result;
94} 94}
95 95
96int chk_non_negative(int result, char *msg) 96int chk_non_negative(int result, char *msg)
97{ 97{
98 if (result < 0) 98 if (result < 0)
99 die("%s: %s",msg, strerror(errno)); 99 die("%s: %s",msg, strerror(errno));
100 return result; 100 return result;
101} 101}
102 102
103struct repoinfo *add_repo(const char *url) 103struct repoinfo *add_repo(const char *url)
104{ 104{
105 struct repoinfo *ret; 105 struct repoinfo *ret;
106 106
107 if (++cgit_repolist.count > cgit_repolist.length) { 107 if (++cgit_repolist.count > cgit_repolist.length) {
108 if (cgit_repolist.length == 0) 108 if (cgit_repolist.length == 0)
109 cgit_repolist.length = 8; 109 cgit_repolist.length = 8;
110 else 110 else
111 cgit_repolist.length *= 2; 111 cgit_repolist.length *= 2;
112 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 112 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
113 cgit_repolist.length * 113 cgit_repolist.length *
114 sizeof(struct repoinfo)); 114 sizeof(struct repoinfo));
115 } 115 }
116 116
117 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 117 ret = &cgit_repolist.repos[cgit_repolist.count-1];
118 ret->url = trim_end(url, '/'); 118 ret->url = trim_end(url, '/');
119 ret->name = ret->url; 119 ret->name = ret->url;
120 ret->path = NULL; 120 ret->path = NULL;
121 ret->desc = NULL; 121 ret->desc = NULL;
122 ret->owner = NULL; 122 ret->owner = NULL;
123 ret->group = cgit_repo_group; 123 ret->group = cgit_repo_group;
124 ret->defbranch = "master"; 124 ret->defbranch = "master";
125 ret->snapshots = cgit_snapshots; 125 ret->snapshots = cgit_snapshots;
126 ret->enable_log_filecount = cgit_enable_log_filecount; 126 ret->enable_log_filecount = cgit_enable_log_filecount;
127 ret->enable_log_linecount = cgit_enable_log_linecount; 127 ret->enable_log_linecount = cgit_enable_log_linecount;
128 ret->module_link = cgit_module_link; 128 ret->module_link = cgit_module_link;
129 ret->readme = NULL; 129 ret->readme = NULL;
130 return ret; 130 return ret;
131} 131}
132 132
133struct repoinfo *cgit_get_repoinfo(const char *url) 133struct repoinfo *cgit_get_repoinfo(const char *url)
134{ 134{
135 int i; 135 int i;
136 struct repoinfo *repo; 136 struct repoinfo *repo;
137 137
diff --git a/ui-patch.c b/ui-patch.c
new file mode 100644
index 0000000..ef79c7c
--- a/dev/null
+++ b/ui-patch.c
@@ -0,0 +1,105 @@
1/* ui-patch.c: generate patch view
2 *
3 * Copyright (C) 2007 Lars Hjemli
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9#include "cgit.h"
10
11static void print_line(char *line, int len)
12{
13 char c = line[len-1];
14
15 line[len-1] = '\0';
16 htmlf("%s\n", line);
17 line[len-1] = c;
18}
19
20static void header(unsigned char *sha1, char *path1, int mode1,
21 unsigned char *sha2, char *path2, int mode2)
22{
23 char *abbrev1, *abbrev2;
24 int subproject;
25
26 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
27 htmlf("diff --git a/%s b/%s\n", path1, path2);
28
29 if (is_null_sha1(sha1))
30 path1 = "dev/null";
31 if (is_null_sha1(sha2))
32 path2 = "dev/null";
33
34 if (mode1 == 0)
35 htmlf("new file mode %.6o\n", mode2);
36
37 if (mode2 == 0)
38 htmlf("deleted file mode %.6o\n", mode1);
39
40 if (!subproject) {
41 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
42 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
43 htmlf("index %s..%s", abbrev1, abbrev2);
44 free(abbrev1);
45 free(abbrev2);
46 if (mode1 != 0 && mode2 != 0) {
47 htmlf(" %.6o", mode1);
48 if (mode2 != mode1)
49 htmlf("..%.6o", mode2);
50 }
51 htmlf("\n--- a/%s\n", path1);
52 htmlf("+++ b/%s\n", path2);
53 }
54}
55
56static void filepair_cb(struct diff_filepair *pair)
57{
58 header(pair->one->sha1, pair->one->path, pair->one->mode,
59 pair->two->sha1, pair->two->path, pair->two->mode);
60 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
61 if (S_ISGITLINK(pair->one->mode))
62 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
63 if (S_ISGITLINK(pair->two->mode))
64 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
65 return;
66 }
67 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line))
68 html("Error running diff");
69}
70
71void cgit_print_patch(char *hex, struct cacheitem *item)
72{
73 struct commit *commit;
74 struct commitinfo *info;
75 unsigned char sha1[20], old_sha1[20];
76 char *patchname;
77
78 if (!hex)
79 hex = cgit_query_head;
80
81 if (get_sha1(hex, sha1)) {
82 cgit_print_error(fmt("Bad object id: %s", hex));
83 return;
84 }
85 commit = lookup_commit_reference(sha1);
86 if (!commit) {
87 cgit_print_error(fmt("Bad commit reference: %s", hex));
88 return;
89 }
90 info = cgit_parse_commit(commit);
91 hashcpy(old_sha1, commit->parents->item->object.sha1);
92
93 patchname = fmt("%s.patch", sha1_to_hex(sha1));
94 cgit_print_snapshot_start("text/plain", patchname, item);
95 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
96 htmlf("From: %s%s\n", info->author, info->author_email);
97 html("Date: ");
98 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n");
99 htmlf("Subject: %s\n\n%s", info->subject, info->msg);
100 html("---\n");
101 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL);
102 html("--\n");
103 htmlf("cgit %s\n", CGIT_VERSION);
104 cgit_free_commitinfo(info);
105}
diff --git a/ui-shared.c b/ui-shared.c
index ece041c..60aa2e3 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -211,128 +211,134 @@ void cgit_log_link(char *name, char *title, char *class, char *head,
211 html(delim); 211 html(delim);
212 html("qt="); 212 html("qt=");
213 html_attr(grep); 213 html_attr(grep);
214 delim = "&"; 214 delim = "&";
215 html(delim); 215 html(delim);
216 html("q="); 216 html("q=");
217 html_attr(pattern); 217 html_attr(pattern);
218 } 218 }
219 if (ofs > 0) { 219 if (ofs > 0) {
220 html(delim); 220 html(delim);
221 html("ofs="); 221 html("ofs=");
222 htmlf("%d", ofs); 222 htmlf("%d", ofs);
223 } 223 }
224 html("'>"); 224 html("'>");
225 html_txt(name); 225 html_txt(name);
226 html("</a>"); 226 html("</a>");
227} 227}
228 228
229void cgit_commit_link(char *name, char *title, char *class, char *head, 229void cgit_commit_link(char *name, char *title, char *class, char *head,
230 char *rev) 230 char *rev)
231{ 231{
232 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 232 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
233 name[cgit_max_msg_len] = '\0'; 233 name[cgit_max_msg_len] = '\0';
234 name[cgit_max_msg_len - 1] = '.'; 234 name[cgit_max_msg_len - 1] = '.';
235 name[cgit_max_msg_len - 2] = '.'; 235 name[cgit_max_msg_len - 2] = '.';
236 name[cgit_max_msg_len - 3] = '.'; 236 name[cgit_max_msg_len - 3] = '.';
237 } 237 }
238 reporevlink("commit", name, title, class, head, rev, NULL); 238 reporevlink("commit", name, title, class, head, rev, NULL);
239} 239}
240 240
241void cgit_refs_link(char *name, char *title, char *class, char *head, 241void cgit_refs_link(char *name, char *title, char *class, char *head,
242 char *rev, char *path) 242 char *rev, char *path)
243{ 243{
244 reporevlink("refs", name, title, class, head, rev, path); 244 reporevlink("refs", name, title, class, head, rev, path);
245} 245}
246 246
247void cgit_snapshot_link(char *name, char *title, char *class, char *head, 247void cgit_snapshot_link(char *name, char *title, char *class, char *head,
248 char *rev, char *archivename) 248 char *rev, char *archivename)
249{ 249{
250 reporevlink("snapshot", name, title, class, head, rev, archivename); 250 reporevlink("snapshot", name, title, class, head, rev, archivename);
251} 251}
252 252
253void cgit_diff_link(char *name, char *title, char *class, char *head, 253void cgit_diff_link(char *name, char *title, char *class, char *head,
254 char *new_rev, char *old_rev, char *path) 254 char *new_rev, char *old_rev, char *path)
255{ 255{
256 char *delim; 256 char *delim;
257 257
258 delim = repolink(title, class, "diff", head, path); 258 delim = repolink(title, class, "diff", head, path);
259 if (new_rev && strcmp(new_rev, cgit_query_head)) { 259 if (new_rev && strcmp(new_rev, cgit_query_head)) {
260 html(delim); 260 html(delim);
261 html("id="); 261 html("id=");
262 html_attr(new_rev); 262 html_attr(new_rev);
263 delim = "&amp;"; 263 delim = "&amp;";
264 } 264 }
265 if (old_rev) { 265 if (old_rev) {
266 html(delim); 266 html(delim);
267 html("id2="); 267 html("id2=");
268 html_attr(old_rev); 268 html_attr(old_rev);
269 } 269 }
270 html("'>"); 270 html("'>");
271 html_txt(name); 271 html_txt(name);
272 html("</a>"); 272 html("</a>");
273} 273}
274 274
275void cgit_patch_link(char *name, char *title, char *class, char *head,
276 char *rev)
277{
278 reporevlink("patch", name, title, class, head, rev, NULL);
279}
280
275void cgit_object_link(struct object *obj) 281void cgit_object_link(struct object *obj)
276{ 282{
277 char *page, *arg, *url; 283 char *page, *arg, *url;
278 284
279 if (obj->type == OBJ_COMMIT) { 285 if (obj->type == OBJ_COMMIT) {
280 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 286 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
281 cgit_query_head, sha1_to_hex(obj->sha1)); 287 cgit_query_head, sha1_to_hex(obj->sha1));
282 return; 288 return;
283 } else if (obj->type == OBJ_TREE) { 289 } else if (obj->type == OBJ_TREE) {
284 page = "tree"; 290 page = "tree";
285 arg = "id"; 291 arg = "id";
286 } else if (obj->type == OBJ_TAG) { 292 } else if (obj->type == OBJ_TAG) {
287 page = "tag"; 293 page = "tag";
288 arg = "id"; 294 arg = "id";
289 } else { 295 } else {
290 page = "blob"; 296 page = "blob";
291 arg = "id"; 297 arg = "id";
292 } 298 }
293 299
294 url = cgit_pageurl(cgit_query_repo, page, 300 url = cgit_pageurl(cgit_query_repo, page,
295 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 301 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
296 html_link_open(url, NULL, NULL); 302 html_link_open(url, NULL, NULL);
297 htmlf("%s %s", typename(obj->type), 303 htmlf("%s %s", typename(obj->type),
298 sha1_to_hex(obj->sha1)); 304 sha1_to_hex(obj->sha1));
299 html_link_close(); 305 html_link_close();
300} 306}
301 307
302void cgit_print_date(time_t secs, char *format) 308void cgit_print_date(time_t secs, char *format)
303{ 309{
304 char buf[64]; 310 char buf[64];
305 struct tm *time; 311 struct tm *time;
306 312
307 if (!secs) 313 if (!secs)
308 return; 314 return;
309 time = gmtime(&secs); 315 time = gmtime(&secs);
310 strftime(buf, sizeof(buf)-1, format, time); 316 strftime(buf, sizeof(buf)-1, format, time);
311 html_txt(buf); 317 html_txt(buf);
312} 318}
313 319
314void cgit_print_age(time_t t, time_t max_relative, char *format) 320void cgit_print_age(time_t t, time_t max_relative, char *format)
315{ 321{
316 time_t now, secs; 322 time_t now, secs;
317 323
318 if (!t) 324 if (!t)
319 return; 325 return;
320 time(&now); 326 time(&now);
321 secs = now - t; 327 secs = now - t;
322 328
323 if (secs > max_relative && max_relative >= 0) { 329 if (secs > max_relative && max_relative >= 0) {
324 cgit_print_date(t, format); 330 cgit_print_date(t, format);
325 return; 331 return;
326 } 332 }
327 333
328 if (secs < TM_HOUR * 2) { 334 if (secs < TM_HOUR * 2) {
329 htmlf("<span class='age-mins'>%.0f min.</span>", 335 htmlf("<span class='age-mins'>%.0f min.</span>",
330 secs * 1.0 / TM_MIN); 336 secs * 1.0 / TM_MIN);
331 return; 337 return;
332 } 338 }
333 if (secs < TM_DAY * 2) { 339 if (secs < TM_DAY * 2) {
334 htmlf("<span class='age-hours'>%.0f hours</span>", 340 htmlf("<span class='age-hours'>%.0f hours</span>",
335 secs * 1.0 / TM_HOUR); 341 secs * 1.0 / TM_HOUR);
336 return; 342 return;
337 } 343 }
338 if (secs < TM_WEEK * 2) { 344 if (secs < TM_WEEK * 2) {
@@ -429,128 +435,130 @@ int print_archive_ref(const char *refname, const unsigned char *sha1,
429 return 0; 435 return 0;
430} 436}
431 437
432void add_hidden_formfields(int incl_head, int incl_search, char *page) 438void add_hidden_formfields(int incl_head, int incl_search, char *page)
433{ 439{
434 char *url; 440 char *url;
435 441
436 if (!cgit_virtual_root) { 442 if (!cgit_virtual_root) {
437 url = fmt("%s/%s", cgit_query_repo, page); 443 url = fmt("%s/%s", cgit_query_repo, page);
438 if (cgit_query_path) 444 if (cgit_query_path)
439 url = fmt("%s/%s", url, cgit_query_path); 445 url = fmt("%s/%s", url, cgit_query_path);
440 html_hidden("url", url); 446 html_hidden("url", url);
441 } 447 }
442 448
443 if (incl_head && strcmp(cgit_query_head, cgit_repo->defbranch)) 449 if (incl_head && strcmp(cgit_query_head, cgit_repo->defbranch))
444 html_hidden("h", cgit_query_head); 450 html_hidden("h", cgit_query_head);
445 451
446 if (cgit_query_sha1) 452 if (cgit_query_sha1)
447 html_hidden("id", cgit_query_sha1); 453 html_hidden("id", cgit_query_sha1);
448 if (cgit_query_sha2) 454 if (cgit_query_sha2)
449 html_hidden("id2", cgit_query_sha2); 455 html_hidden("id2", cgit_query_sha2);
450 456
451 if (incl_search) { 457 if (incl_search) {
452 if (cgit_query_grep) 458 if (cgit_query_grep)
453 html_hidden("qt", cgit_query_grep); 459 html_hidden("qt", cgit_query_grep);
454 if (cgit_query_search) 460 if (cgit_query_search)
455 html_hidden("q", cgit_query_search); 461 html_hidden("q", cgit_query_search);
456 } 462 }
457} 463}
458 464
459void cgit_print_pageheader(char *title, int show_search) 465void cgit_print_pageheader(char *title, int show_search)
460{ 466{
461 static const char *default_info = "This is cgit, a fast webinterface for git repositories"; 467 static const char *default_info = "This is cgit, a fast webinterface for git repositories";
462 int header = 0; 468 int header = 0;
463 char *url; 469 char *url;
464 470
465 html("<table id='layout' summary=''>\n"); 471 html("<table id='layout' summary=''>\n");
466 html("<tr><td id='sidebar'>\n"); 472 html("<tr><td id='sidebar'>\n");
467 html("<table class='sidebar' cellspacing='0' summary=''>\n"); 473 html("<table class='sidebar' cellspacing='0' summary=''>\n");
468 html("<tr><td class='sidebar'>\n<a href='"); 474 html("<tr><td class='sidebar'>\n<a href='");
469 html_attr(cgit_rooturl()); 475 html_attr(cgit_rooturl());
470 htmlf("'><img src='%s' alt='cgit'/></a>\n", 476 htmlf("'><img src='%s' alt='cgit'/></a>\n",
471 cgit_logo); 477 cgit_logo);
472 html("</td></tr>\n<tr><td class='sidebar'>\n"); 478 html("</td></tr>\n<tr><td class='sidebar'>\n");
473 if (cgit_query_repo) { 479 if (cgit_query_repo) {
474 html("<h1 class='first'>"); 480 html("<h1 class='first'>");
475 html_txt(strrpart(cgit_repo->name, 20)); 481 html_txt(strrpart(cgit_repo->name, 20));
476 html("</h1>\n"); 482 html("</h1>\n");
477 html_txt(cgit_repo->desc); 483 html_txt(cgit_repo->desc);
478 if (cgit_repo->owner) { 484 if (cgit_repo->owner) {
479 html("<h1>owner</h1>\n"); 485 html("<h1>owner</h1>\n");
480 html_txt(cgit_repo->owner); 486 html_txt(cgit_repo->owner);
481 } 487 }
482 html("<h1>navigate</h1>\n"); 488 html("<h1>navigate</h1>\n");
483 reporevlink(NULL, "summary", NULL, "menu", cgit_query_head, 489 reporevlink(NULL, "summary", NULL, "menu", cgit_query_head,
484 NULL, NULL); 490 NULL, NULL);
485 cgit_log_link("log", NULL, "menu", cgit_query_head, NULL, NULL, 491 cgit_log_link("log", NULL, "menu", cgit_query_head, NULL, NULL,
486 0, NULL, NULL); 492 0, NULL, NULL);
487 cgit_tree_link("tree", NULL, "menu", cgit_query_head, 493 cgit_tree_link("tree", NULL, "menu", cgit_query_head,
488 cgit_query_sha1, NULL); 494 cgit_query_sha1, NULL);
489 cgit_commit_link("commit", NULL, "menu", cgit_query_head, 495 cgit_commit_link("commit", NULL, "menu", cgit_query_head,
490 cgit_query_sha1); 496 cgit_query_sha1);
491 cgit_diff_link("diff", NULL, "menu", cgit_query_head, 497 cgit_diff_link("diff", NULL, "menu", cgit_query_head,
492 cgit_query_sha1, cgit_query_sha2, NULL); 498 cgit_query_sha1, cgit_query_sha2, NULL);
499 cgit_patch_link("patch", NULL, "menu", cgit_query_head,
500 cgit_query_sha1);
493 501
494 for_each_ref(print_archive_ref, &header); 502 for_each_ref(print_archive_ref, &header);
495 503
496 if (cgit_repo->clone_url || cgit_clone_prefix) { 504 if (cgit_repo->clone_url || cgit_clone_prefix) {
497 html("<h1>clone</h1>\n"); 505 html("<h1>clone</h1>\n");
498 if (cgit_repo->clone_url) 506 if (cgit_repo->clone_url)
499 url = cgit_repo->clone_url; 507 url = cgit_repo->clone_url;
500 else 508 else
501 url = fmt("%s%s", cgit_clone_prefix, 509 url = fmt("%s%s", cgit_clone_prefix,
502 cgit_repo->url); 510 cgit_repo->url);
503 html("<a class='menu' href='"); 511 html("<a class='menu' href='");
504 html_attr(url); 512 html_attr(url);
505 html("' title='"); 513 html("' title='");
506 html_attr(url); 514 html_attr(url);
507 html("'>\n"); 515 html("'>\n");
508 html_txt(strrpart(url, 20)); 516 html_txt(strrpart(url, 20));
509 html("</a>\n"); 517 html("</a>\n");
510 } 518 }
511 519
512 html("<h1>branch</h1>\n"); 520 html("<h1>branch</h1>\n");
513 html("<form method='get' action=''>\n"); 521 html("<form method='get' action=''>\n");
514 add_hidden_formfields(0, 1, cgit_query_page); 522 add_hidden_formfields(0, 1, cgit_query_page);
515 // html("<table summary='branch selector' class='grid'><tr><td id='branch-dropdown-cell'>"); 523 // html("<table summary='branch selector' class='grid'><tr><td id='branch-dropdown-cell'>");
516 html("<select name='h' onchange='this.form.submit();'>\n"); 524 html("<select name='h' onchange='this.form.submit();'>\n");
517 for_each_branch_ref(print_branch_option, cgit_query_head); 525 for_each_branch_ref(print_branch_option, cgit_query_head);
518 html("</select>\n"); 526 html("</select>\n");
519 // html("</td><td>"); 527 // html("</td><td>");
520 html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n"); 528 html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n");
521 // html("</td></tr></table>"); 529 // html("</td></tr></table>");
522 html("</form>\n"); 530 html("</form>\n");
523 531
524 html("<h1>search</h1>\n"); 532 html("<h1>search</h1>\n");
525 html("<form method='get' action='"); 533 html("<form method='get' action='");
526 if (cgit_virtual_root) 534 if (cgit_virtual_root)
527 html_attr(cgit_fileurl(cgit_query_repo, "log", 535 html_attr(cgit_fileurl(cgit_query_repo, "log",
528 cgit_query_path, NULL)); 536 cgit_query_path, NULL));
529 html("'>\n"); 537 html("'>\n");
530 add_hidden_formfields(1, 0, "log"); 538 add_hidden_formfields(1, 0, "log");
531 html("<select name='qt'>\n"); 539 html("<select name='qt'>\n");
532 html_option("grep", "log msg", cgit_query_grep); 540 html_option("grep", "log msg", cgit_query_grep);
533 html_option("author", "author", cgit_query_grep); 541 html_option("author", "author", cgit_query_grep);
534 html_option("committer", "committer", cgit_query_grep); 542 html_option("committer", "committer", cgit_query_grep);
535 html("</select>\n"); 543 html("</select>\n");
536 html("<input class='txt' type='text' name='q' value='"); 544 html("<input class='txt' type='text' name='q' value='");
537 html_attr(cgit_query_search); 545 html_attr(cgit_query_search);
538 html("'/>\n"); 546 html("'/>\n");
539 html("</form>\n"); 547 html("</form>\n");
540 } else { 548 } else {
541 if (!cgit_index_info || html_include(cgit_index_info)) 549 if (!cgit_index_info || html_include(cgit_index_info))
542 html(default_info); 550 html(default_info);
543 } 551 }
544 552
545 html("</td></tr></table></td>\n"); 553 html("</td></tr></table></td>\n");
546 554
547 html("<td id='content'>\n"); 555 html("<td id='content'>\n");
548} 556}
549 557
550 558
551void cgit_print_snapshot_start(const char *mimetype, const char *filename, 559void cgit_print_snapshot_start(const char *mimetype, const char *filename,
552 struct cacheitem *item) 560 struct cacheitem *item)
553{ 561{
554 htmlf("Content-Type: %s\n", mimetype); 562 htmlf("Content-Type: %s\n", mimetype);
555 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 563 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
556 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 564 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));