summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2006-12-20 21:48:27 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2006-12-20 21:48:27 (UTC)
commit36aba00273e7af1b94bf8c5dd5068709d983d01e (patch) (unidiff)
treed9be4e6f27b115a799af40cad43445f63fbf2238
parenta53042865a4ac8b1fa1d6b37720787601e181495 (diff)
downloadcgit-36aba00273e7af1b94bf8c5dd5068709d983d01e.zip
cgit-36aba00273e7af1b94bf8c5dd5068709d983d01e.tar.gz
cgit-36aba00273e7af1b94bf8c5dd5068709d983d01e.tar.bz2
Add basic diff view
Finally, xdiff is used to show per-file diffs via commit view. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c2
-rw-r--r--cgit.css25
-rw-r--r--cgit.h2
-rw-r--r--shared.c4
-rw-r--r--ui-diff.c131
-rw-r--r--xdiff.h105
7 files changed, 270 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 58a583b..4aa7afe 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,30 @@
1CGIT_VERSION = 0.1-pre 1CGIT_VERSION = 0.1-pre
2 2
3INSTALL_BIN = /var/www/htdocs/cgit.cgi 3INSTALL_BIN = /var/www/htdocs/cgit.cgi
4INSTALL_CSS = /var/www/htdocs/cgit.css 4INSTALL_CSS = /var/www/htdocs/cgit.css
5CACHE_ROOT = /var/cache/cgit 5CACHE_ROOT = /var/cache/cgit
6 6
7EXTLIBS = ../git/libgit.a ../git/xdiff/lib.a -lz -lcrypto 7EXTLIBS = ../git/libgit.a ../git/xdiff/lib.a -lz -lcrypto
8OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 8OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
9 ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c 9 ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c ui-diff.o
10 10
11CFLAGS += -Wall 11CFLAGS += -Wall
12 12
13all: cgit 13all: cgit
14 14
15install: all clean-cache 15install: all clean-cache
16 install cgit $(INSTALL_BIN) 16 install cgit $(INSTALL_BIN)
17 install cgit.css $(INSTALL_CSS) 17 install cgit.css $(INSTALL_CSS)
18 18
19cgit: cgit.c cgit.h git.h $(OBJECTS) 19cgit: cgit.c cgit.h git.h $(OBJECTS)
20 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \ 20 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \
21 $(OBJECTS) $(EXTLIBS) 21 $(OBJECTS) $(EXTLIBS)
22 22
23$(OBJECTS): cgit.h git.h 23$(OBJECTS): cgit.h git.h
24 24
25.PHONY: clean 25.PHONY: clean
26clean: 26clean:
27 rm -f cgit *.o 27 rm -f cgit *.o
28 28
29clean-cache: 29clean-cache:
30 rm -rf $(CACHE_ROOT)/* 30 rm -rf $(CACHE_ROOT)/*
diff --git a/cgit.c b/cgit.c
index 372b436..ac43441 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,134 +1,136 @@
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 10
11const char cgit_version[] = CGIT_VERSION; 11const char cgit_version[] = CGIT_VERSION;
12 12
13static void cgit_print_repo_page(struct cacheitem *item) 13static void cgit_print_repo_page(struct cacheitem *item)
14{ 14{
15 if (chdir(fmt("%s/%s", cgit_root, cgit_query_repo)) || 15 if (chdir(fmt("%s/%s", cgit_root, cgit_query_repo)) ||
16 cgit_read_config("info/cgit", cgit_repo_config_cb)) { 16 cgit_read_config("info/cgit", cgit_repo_config_cb)) {
17 char *title = fmt("%s - %s", cgit_root_title, "Bad request"); 17 char *title = fmt("%s - %s", cgit_root_title, "Bad request");
18 cgit_print_docstart(title, item); 18 cgit_print_docstart(title, item);
19 cgit_print_pageheader(title); 19 cgit_print_pageheader(title);
20 cgit_print_error(fmt("Unable to scan repository: %s", 20 cgit_print_error(fmt("Unable to scan repository: %s",
21 strerror(errno))); 21 strerror(errno)));
22 cgit_print_docend(); 22 cgit_print_docend();
23 return; 23 return;
24 } 24 }
25 setenv("GIT_DIR", fmt("%s/%s", cgit_root, cgit_query_repo), 1); 25 setenv("GIT_DIR", fmt("%s/%s", cgit_root, cgit_query_repo), 1);
26 char *title = fmt("%s - %s", cgit_repo_name, cgit_repo_desc); 26 char *title = fmt("%s - %s", cgit_repo_name, cgit_repo_desc);
27 cgit_print_docstart(title, item); 27 cgit_print_docstart(title, item);
28 cgit_print_pageheader(title); 28 cgit_print_pageheader(title);
29 if (!cgit_query_page) { 29 if (!cgit_query_page) {
30 cgit_print_summary(); 30 cgit_print_summary();
31 } else if (!strcmp(cgit_query_page, "log")) { 31 } else if (!strcmp(cgit_query_page, "log")) {
32 cgit_print_log(cgit_query_head, cgit_query_ofs, 100); 32 cgit_print_log(cgit_query_head, cgit_query_ofs, 100);
33 } else if (!strcmp(cgit_query_page, "tree")) { 33 } else if (!strcmp(cgit_query_page, "tree")) {
34 cgit_print_tree(cgit_query_sha1); 34 cgit_print_tree(cgit_query_sha1);
35 } else if (!strcmp(cgit_query_page, "commit")) { 35 } else if (!strcmp(cgit_query_page, "commit")) {
36 cgit_print_commit(cgit_query_sha1); 36 cgit_print_commit(cgit_query_sha1);
37 } else if (!strcmp(cgit_query_page, "view")) { 37 } else if (!strcmp(cgit_query_page, "view")) {
38 cgit_print_view(cgit_query_sha1); 38 cgit_print_view(cgit_query_sha1);
39 } else if (!strcmp(cgit_query_page, "diff")) {
40 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
39 } 41 }
40 cgit_print_docend(); 42 cgit_print_docend();
41} 43}
42 44
43static void cgit_fill_cache(struct cacheitem *item) 45static void cgit_fill_cache(struct cacheitem *item)
44{ 46{
45 static char buf[PATH_MAX]; 47 static char buf[PATH_MAX];
46 48
47 getcwd(buf, sizeof(buf)); 49 getcwd(buf, sizeof(buf));
48 htmlfd = item->fd; 50 htmlfd = item->fd;
49 item->st.st_mtime = time(NULL); 51 item->st.st_mtime = time(NULL);
50 if (cgit_query_repo) 52 if (cgit_query_repo)
51 cgit_print_repo_page(item); 53 cgit_print_repo_page(item);
52 else 54 else
53 cgit_print_repolist(item); 55 cgit_print_repolist(item);
54 chdir(buf); 56 chdir(buf);
55} 57}
56 58
57static void cgit_check_cache(struct cacheitem *item) 59static void cgit_check_cache(struct cacheitem *item)
58{ 60{
59 int i = 0; 61 int i = 0;
60 62
61 cache_prepare(item); 63 cache_prepare(item);
62 top: 64 top:
63 if (++i > cgit_max_lock_attempts) { 65 if (++i > cgit_max_lock_attempts) {
64 die("cgit_refresh_cache: unable to lock %s: %s", 66 die("cgit_refresh_cache: unable to lock %s: %s",
65 item->name, strerror(errno)); 67 item->name, strerror(errno));
66 } 68 }
67 if (!cache_exist(item)) { 69 if (!cache_exist(item)) {
68 if (!cache_lock(item)) { 70 if (!cache_lock(item)) {
69 sleep(1); 71 sleep(1);
70 goto top; 72 goto top;
71 } 73 }
72 if (!cache_exist(item)) { 74 if (!cache_exist(item)) {
73 cgit_fill_cache(item); 75 cgit_fill_cache(item);
74 cache_unlock(item); 76 cache_unlock(item);
75 } else { 77 } else {
76 cache_cancel_lock(item); 78 cache_cancel_lock(item);
77 } 79 }
78 } else if (cache_expired(item) && cache_lock(item)) { 80 } else if (cache_expired(item) && cache_lock(item)) {
79 if (cache_expired(item)) { 81 if (cache_expired(item)) {
80 cgit_fill_cache(item); 82 cgit_fill_cache(item);
81 cache_unlock(item); 83 cache_unlock(item);
82 } else { 84 } else {
83 cache_cancel_lock(item); 85 cache_cancel_lock(item);
84 } 86 }
85 } 87 }
86} 88}
87 89
88static void cgit_print_cache(struct cacheitem *item) 90static void cgit_print_cache(struct cacheitem *item)
89{ 91{
90 static char buf[4096]; 92 static char buf[4096];
91 ssize_t i; 93 ssize_t i;
92 94
93 int fd = open(item->name, O_RDONLY); 95 int fd = open(item->name, O_RDONLY);
94 if (fd<0) 96 if (fd<0)
95 die("Unable to open cached file %s", item->name); 97 die("Unable to open cached file %s", item->name);
96 98
97 while((i=read(fd, buf, sizeof(buf))) > 0) 99 while((i=read(fd, buf, sizeof(buf))) > 0)
98 write(STDOUT_FILENO, buf, i); 100 write(STDOUT_FILENO, buf, i);
99 101
100 close(fd); 102 close(fd);
101} 103}
102 104
103static void cgit_parse_args(int argc, const char **argv) 105static void cgit_parse_args(int argc, const char **argv)
104{ 106{
105 int i; 107 int i;
106 108
107 for (i = 1; i < argc; i++) { 109 for (i = 1; i < argc; i++) {
108 if (!strncmp(argv[i], "--root=", 7)) { 110 if (!strncmp(argv[i], "--root=", 7)) {
109 cgit_root = xstrdup(argv[i]+7); 111 cgit_root = xstrdup(argv[i]+7);
110 } 112 }
111 if (!strncmp(argv[i], "--cache=", 8)) { 113 if (!strncmp(argv[i], "--cache=", 8)) {
112 cgit_cache_root = xstrdup(argv[i]+8); 114 cgit_cache_root = xstrdup(argv[i]+8);
113 } 115 }
114 if (!strcmp(argv[i], "--nocache")) { 116 if (!strcmp(argv[i], "--nocache")) {
115 cgit_nocache = 1; 117 cgit_nocache = 1;
116 } 118 }
117 if (!strncmp(argv[i], "--query=", 8)) { 119 if (!strncmp(argv[i], "--query=", 8)) {
118 cgit_querystring = xstrdup(argv[i]+8); 120 cgit_querystring = xstrdup(argv[i]+8);
119 } 121 }
120 if (!strncmp(argv[i], "--repo=", 7)) { 122 if (!strncmp(argv[i], "--repo=", 7)) {
121 cgit_query_repo = xstrdup(argv[i]+7); 123 cgit_query_repo = xstrdup(argv[i]+7);
122 } 124 }
123 if (!strncmp(argv[i], "--page=", 7)) { 125 if (!strncmp(argv[i], "--page=", 7)) {
124 cgit_query_page = xstrdup(argv[i]+7); 126 cgit_query_page = xstrdup(argv[i]+7);
125 } 127 }
126 if (!strncmp(argv[i], "--head=", 7)) { 128 if (!strncmp(argv[i], "--head=", 7)) {
127 cgit_query_head = xstrdup(argv[i]+7); 129 cgit_query_head = xstrdup(argv[i]+7);
128 cgit_query_has_symref = 1; 130 cgit_query_has_symref = 1;
129 } 131 }
130 if (!strncmp(argv[i], "--sha1=", 7)) { 132 if (!strncmp(argv[i], "--sha1=", 7)) {
131 cgit_query_sha1 = xstrdup(argv[i]+7); 133 cgit_query_sha1 = xstrdup(argv[i]+7);
132 cgit_query_has_sha1 = 1; 134 cgit_query_has_sha1 = 1;
133 } 135 }
134 if (!strncmp(argv[i], "--ofs=", 6)) { 136 if (!strncmp(argv[i], "--ofs=", 6)) {
diff --git a/cgit.css b/cgit.css
index 5f01926..7b8e468 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,166 +1,191 @@
1body { 1body {
2 font-family: arial; 2 font-family: arial;
3 font-size: normal; 3 font-size: normal;
4 background: white; 4 background: white;
5 padding: 0em; 5 padding: 0em;
6 margin: 0.5em 1em; 6 margin: 0.5em 1em;
7} 7}
8 8
9 9
10h2 { 10h2 {
11 font-size: normal; 11 font-size: normal;
12 font-weight: bold; 12 font-weight: bold;
13 margin-bottom: 0.1em; 13 margin-bottom: 0.1em;
14} 14}
15 15
16a { 16a {
17 color: blue; 17 color: blue;
18 text-decoration: none; 18 text-decoration: none;
19} 19}
20 20
21a:hover { 21a:hover {
22 text-decoration: underline; 22 text-decoration: underline;
23} 23}
24 24
25table.list { 25table.list {
26 border: solid 1px black; 26 border: solid 1px black;
27 border-collapse: collapse; 27 border-collapse: collapse;
28 border: solid 1px #aaa; 28 border: solid 1px #aaa;
29} 29}
30table.list tr { 30table.list tr {
31 background: white; 31 background: white;
32} 32}
33table.list tr:hover { 33table.list tr:hover {
34 background: #eee; 34 background: #eee;
35} 35}
36table.list th { 36table.list th {
37 font-weight: bold; 37 font-weight: bold;
38 background: #ddd; 38 background: #ddd;
39 border-bottom: solid 1px #aaa; 39 border-bottom: solid 1px #aaa;
40 padding: 0.1em 0.5em 0.1em 0.5em; 40 padding: 0.1em 0.5em 0.1em 0.5em;
41 vertical-align: baseline; 41 vertical-align: baseline;
42} 42}
43table.list td { 43table.list td {
44 border: none; 44 border: none;
45 padding: 0.1em 0.5em 0.1em 0.5em; 45 padding: 0.1em 0.5em 0.1em 0.5em;
46} 46}
47img { 47img {
48 border: none; 48 border: none;
49} 49}
50 50
51 51
52div#header { 52div#header {
53 background-color: #eee; 53 background-color: #eee;
54 padding: 0.25em 0.25em 0.25em 0.5em; 54 padding: 0.25em 0.25em 0.25em 0.5em;
55 font-size: 150%; 55 font-size: 150%;
56 font-weight: bold; 56 font-weight: bold;
57 border: solid 1px #ccc; 57 border: solid 1px #ccc;
58 vertical-align: middle; 58 vertical-align: middle;
59} 59}
60div#header img#logo { 60div#header img#logo {
61 float: right; 61 float: right;
62} 62}
63div#header a { 63div#header a {
64 color: black; 64 color: black;
65} 65}
66 66
67div#content { 67div#content {
68 margin: 0.5em 0.5em; 68 margin: 0.5em 0.5em;
69} 69}
70 70
71div#blob { 71div#blob {
72 border: solid 1px black; 72 border: solid 1px black;
73} 73}
74 74
75div.error { 75div.error {
76 color: red; 76 color: red;
77 font-weight: bold; 77 font-weight: bold;
78 margin: 1em 2em; 78 margin: 1em 2em;
79} 79}
80div.ls-blob, div.ls-dir {
81 font-family: monospace;
82}
80div.ls-dir a { 83div.ls-dir a {
81 font-weight: bold; 84 font-weight: bold;
82} 85}
83th.filesize, td.filesize { 86th.filesize, td.filesize {
84 text-align: right; 87 text-align: right;
85} 88}
89td.filesize {
90 font-family: monospace;
91}
86td.filemode { 92td.filemode {
87 font-family: monospace; 93 font-family: monospace;
88} 94}
89 95
90td.blob { 96td.blob {
91 white-space: pre; 97 white-space: pre;
92 font-family: courier; 98 font-family: courier;
93 font-size: 100%; 99 font-size: 100%;
94 background-color: white; 100 background-color: white;
95} 101}
96 102
97table.log td { 103table.log td {
98 white-space: nowrap; 104 white-space: nowrap;
99} 105}
100 106
101table.commit-info { 107table.commit-info {
102 border-collapse: collapse; 108 border-collapse: collapse;
103 margin-top: 1.5em; 109 margin-top: 1.5em;
104} 110}
105table.commit-info th { 111table.commit-info th {
106 text-align: left; 112 text-align: left;
107 font-weight: normal; 113 font-weight: normal;
108 padding: 0.1em 1em 0.1em 0.1em; 114 padding: 0.1em 1em 0.1em 0.1em;
109} 115}
110table.commit-info td { 116table.commit-info td {
111 font-weight: normal; 117 font-weight: normal;
112 padding: 0.1em 1em 0.1em 0.1em; 118 padding: 0.1em 1em 0.1em 0.1em;
113} 119}
114div.commit-subject { 120div.commit-subject {
115 font-weight: bold; 121 font-weight: bold;
116 font-size: 125%; 122 font-size: 125%;
117 margin: 1.5em 0em 0.5em 0em; 123 margin: 1.5em 0em 0.5em 0em;
118 padding: 0em; 124 padding: 0em;
119} 125}
120div.commit-msg { 126div.commit-msg {
121 white-space: pre; 127 white-space: pre;
122 font-family: monospace; 128 font-family: monospace;
123} 129}
124table.diffstat { 130table.diffstat {
125 border-collapse: collapse; 131 border-collapse: collapse;
126 margin-top: 1.5em; 132 margin-top: 1.5em;
127} 133}
128table.diffstat th { 134table.diffstat th {
129 font-weight: normal; 135 font-weight: normal;
130 text-align: left; 136 text-align: left;
131 text-decoration: underline; 137 text-decoration: underline;
132 padding: 0.1em 1em 0.1em 0.1em; 138 padding: 0.1em 1em 0.1em 0.1em;
133 font-size: 100%; 139 font-size: 100%;
134} 140}
135table.diffstat td { 141table.diffstat td {
136 padding: 0.1em 1em 0.1em 0.1em; 142 padding: 0.1em 1em 0.1em 0.1em;
137 font-size: 100%; 143 font-size: 100%;
138} 144}
139table.diffstat td span.modechange { 145table.diffstat td span.modechange {
140 padding-left: 1em; 146 padding-left: 1em;
141 color: red; 147 color: red;
142} 148}
143table.diffstat td.add a { 149table.diffstat td.add a {
144 color: green; 150 color: green;
145} 151}
146table.diffstat td.del a { 152table.diffstat td.del a {
147 color: red; 153 color: red;
148} 154}
149table.diffstat td.upd a { 155table.diffstat td.upd a {
150 color: blue; 156 color: blue;
151} 157}
152table.diffstat td.summary { 158table.diffstat td.summary {
153 /* border-top: solid 1px black; */ 159 /* border-top: solid 1px black; */
154 color: #888; 160 color: #888;
155 padding-top: 0.5em; 161 padding-top: 0.5em;
156} 162}
163
164table.diff td {
165 border: solid 1px black;
166 font-family: monospace;
167 white-space: pre;
168}
169
170table.diff td div.hunk {
171 background: #ccc;
172}
173
174table.diff td div.add {
175 color: green;
176}
177
178table.diff td div.del {
179 color: red;
180}
181
157.sha1 { 182.sha1 {
158 font-family: courier; 183 font-family: courier;
159 font-size: 90%; 184 font-size: 90%;
160} 185}
161.left { 186.left {
162 text-align: left; 187 text-align: left;
163} 188}
164.right { 189.right {
165 text-align: right; 190 text-align: right;
166} 191}
diff --git a/cgit.h b/cgit.h
index 37584d6..bba2d6c 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,108 +1,110 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4#include "git.h" 4#include "git.h"
5#include <openssl/sha.h> 5#include <openssl/sha.h>
6#include <ctype.h> 6#include <ctype.h>
7#include <sched.h> 7#include <sched.h>
8 8
9typedef void (*configfn)(const char *name, const char *value); 9typedef void (*configfn)(const char *name, const char *value);
10 10
11struct cacheitem { 11struct cacheitem {
12 char *name; 12 char *name;
13 struct stat st; 13 struct stat st;
14 int ttl; 14 int ttl;
15 int fd; 15 int fd;
16}; 16};
17 17
18struct commitinfo { 18struct commitinfo {
19 struct commit *commit; 19 struct commit *commit;
20 char *author; 20 char *author;
21 char *author_email; 21 char *author_email;
22 unsigned long author_date; 22 unsigned long author_date;
23 char *committer; 23 char *committer;
24 char *committer_email; 24 char *committer_email;
25 unsigned long committer_date; 25 unsigned long committer_date;
26 char *subject; 26 char *subject;
27 char *msg; 27 char *msg;
28}; 28};
29 29
30extern const char cgit_version[]; 30extern const char cgit_version[];
31 31
32extern char *cgit_root; 32extern char *cgit_root;
33extern char *cgit_root_title; 33extern char *cgit_root_title;
34extern char *cgit_css; 34extern char *cgit_css;
35extern char *cgit_logo; 35extern char *cgit_logo;
36extern char *cgit_logo_link; 36extern char *cgit_logo_link;
37extern char *cgit_virtual_root; 37extern char *cgit_virtual_root;
38extern char *cgit_cache_root; 38extern char *cgit_cache_root;
39 39
40extern int cgit_nocache; 40extern int cgit_nocache;
41extern int cgit_max_lock_attempts; 41extern int cgit_max_lock_attempts;
42extern int cgit_cache_root_ttl; 42extern int cgit_cache_root_ttl;
43extern int cgit_cache_repo_ttl; 43extern int cgit_cache_repo_ttl;
44extern int cgit_cache_dynamic_ttl; 44extern int cgit_cache_dynamic_ttl;
45extern int cgit_cache_static_ttl; 45extern int cgit_cache_static_ttl;
46extern int cgit_cache_max_create_time; 46extern int cgit_cache_max_create_time;
47 47
48extern char *cgit_repo_name; 48extern char *cgit_repo_name;
49extern char *cgit_repo_desc; 49extern char *cgit_repo_desc;
50extern char *cgit_repo_owner; 50extern char *cgit_repo_owner;
51 51
52extern int cgit_query_has_symref; 52extern int cgit_query_has_symref;
53extern int cgit_query_has_sha1; 53extern int cgit_query_has_sha1;
54 54
55extern char *cgit_querystring; 55extern char *cgit_querystring;
56extern char *cgit_query_repo; 56extern char *cgit_query_repo;
57extern char *cgit_query_page; 57extern char *cgit_query_page;
58extern char *cgit_query_head; 58extern char *cgit_query_head;
59extern char *cgit_query_sha1; 59extern char *cgit_query_sha1;
60extern char *cgit_query_sha2;
60extern int cgit_query_ofs; 61extern int cgit_query_ofs;
61 62
62extern int htmlfd; 63extern int htmlfd;
63 64
64extern void cgit_global_config_cb(const char *name, const char *value); 65extern void cgit_global_config_cb(const char *name, const char *value);
65extern void cgit_repo_config_cb(const char *name, const char *value); 66extern void cgit_repo_config_cb(const char *name, const char *value);
66extern void cgit_querystring_cb(const char *name, const char *value); 67extern void cgit_querystring_cb(const char *name, const char *value);
67 68
68extern void *cgit_free_commitinfo(struct commitinfo *info); 69extern void *cgit_free_commitinfo(struct commitinfo *info);
69 70
70extern char *fmt(const char *format,...); 71extern char *fmt(const char *format,...);
71 72
72extern void html(const char *txt); 73extern void html(const char *txt);
73extern void htmlf(const char *format,...); 74extern void htmlf(const char *format,...);
74extern void html_txt(char *txt); 75extern void html_txt(char *txt);
75extern void html_attr(char *txt); 76extern void html_attr(char *txt);
76extern void html_link_open(char *url, char *title, char *class); 77extern void html_link_open(char *url, char *title, char *class);
77extern void html_link_close(void); 78extern void html_link_close(void);
78extern void html_filemode(unsigned short mode); 79extern void html_filemode(unsigned short mode);
79 80
80extern int cgit_read_config(const char *filename, configfn fn); 81extern int cgit_read_config(const char *filename, configfn fn);
81extern int cgit_parse_query(char *txt, configfn fn); 82extern int cgit_parse_query(char *txt, configfn fn);
82extern struct commitinfo *cgit_parse_commit(struct commit *commit); 83extern struct commitinfo *cgit_parse_commit(struct commit *commit);
83 84
84extern void cache_prepare(struct cacheitem *item); 85extern void cache_prepare(struct cacheitem *item);
85extern int cache_lock(struct cacheitem *item); 86extern int cache_lock(struct cacheitem *item);
86extern int cache_unlock(struct cacheitem *item); 87extern int cache_unlock(struct cacheitem *item);
87extern int cache_cancel_lock(struct cacheitem *item); 88extern int cache_cancel_lock(struct cacheitem *item);
88extern int cache_exist(struct cacheitem *item); 89extern int cache_exist(struct cacheitem *item);
89extern int cache_expired(struct cacheitem *item); 90extern int cache_expired(struct cacheitem *item);
90 91
91extern char *cgit_repourl(const char *reponame); 92extern char *cgit_repourl(const char *reponame);
92extern char *cgit_pageurl(const char *reponame, const char *pagename, 93extern char *cgit_pageurl(const char *reponame, const char *pagename,
93 const char *query); 94 const char *query);
94 95
95extern void cgit_print_error(char *msg); 96extern void cgit_print_error(char *msg);
96extern void cgit_print_date(unsigned long secs); 97extern void cgit_print_date(unsigned long secs);
97extern void cgit_print_docstart(char *title, struct cacheitem *item); 98extern void cgit_print_docstart(char *title, struct cacheitem *item);
98extern void cgit_print_docend(); 99extern void cgit_print_docend();
99extern void cgit_print_pageheader(char *title); 100extern void cgit_print_pageheader(char *title);
100 101
101extern void cgit_print_repolist(struct cacheitem *item); 102extern void cgit_print_repolist(struct cacheitem *item);
102extern void cgit_print_summary(); 103extern void cgit_print_summary();
103extern void cgit_print_log(const char *tip, int ofs, int cnt); 104extern void cgit_print_log(const char *tip, int ofs, int cnt);
104extern void cgit_print_view(const char *hex); 105extern void cgit_print_view(const char *hex);
105extern void cgit_print_tree(const char *hex); 106extern void cgit_print_tree(const char *hex);
106extern void cgit_print_commit(const char *hex); 107extern void cgit_print_commit(const char *hex);
108extern void cgit_print_diff(const char *old_hex, const char *new_hex);
107 109
108#endif /* CGIT_H */ 110#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index b576df8..762eb38 100644
--- a/shared.c
+++ b/shared.c
@@ -1,100 +1,104 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback functions
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 10
11char *cgit_root = "/usr/src/git"; 11char *cgit_root = "/usr/src/git";
12char *cgit_root_title = "Git repository browser"; 12char *cgit_root_title = "Git repository browser";
13char *cgit_css = "/cgit.css"; 13char *cgit_css = "/cgit.css";
14char *cgit_logo = "/git-logo.png"; 14char *cgit_logo = "/git-logo.png";
15char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 15char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
16char *cgit_virtual_root = NULL; 16char *cgit_virtual_root = NULL;
17 17
18char *cgit_cache_root = "/var/cache/cgit"; 18char *cgit_cache_root = "/var/cache/cgit";
19 19
20int cgit_nocache = 0; 20int cgit_nocache = 0;
21int cgit_max_lock_attempts = 5; 21int cgit_max_lock_attempts = 5;
22int cgit_cache_root_ttl = 5; 22int cgit_cache_root_ttl = 5;
23int cgit_cache_repo_ttl = 5; 23int cgit_cache_repo_ttl = 5;
24int cgit_cache_dynamic_ttl = 5; 24int cgit_cache_dynamic_ttl = 5;
25int cgit_cache_static_ttl = -1; 25int cgit_cache_static_ttl = -1;
26int cgit_cache_max_create_time = 5; 26int cgit_cache_max_create_time = 5;
27 27
28char *cgit_repo_name = NULL; 28char *cgit_repo_name = NULL;
29char *cgit_repo_desc = NULL; 29char *cgit_repo_desc = NULL;
30char *cgit_repo_owner = NULL; 30char *cgit_repo_owner = NULL;
31 31
32int cgit_query_has_symref = 0; 32int cgit_query_has_symref = 0;
33int cgit_query_has_sha1 = 0; 33int cgit_query_has_sha1 = 0;
34 34
35char *cgit_querystring = NULL; 35char *cgit_querystring = NULL;
36char *cgit_query_repo = NULL; 36char *cgit_query_repo = NULL;
37char *cgit_query_page = NULL; 37char *cgit_query_page = NULL;
38char *cgit_query_head = NULL; 38char *cgit_query_head = NULL;
39char *cgit_query_sha1 = NULL; 39char *cgit_query_sha1 = NULL;
40char *cgit_query_sha2 = NULL;
40int cgit_query_ofs = 0; 41int cgit_query_ofs = 0;
41 42
42int htmlfd = 0; 43int htmlfd = 0;
43 44
44void cgit_global_config_cb(const char *name, const char *value) 45void cgit_global_config_cb(const char *name, const char *value)
45{ 46{
46 if (!strcmp(name, "root")) 47 if (!strcmp(name, "root"))
47 cgit_root = xstrdup(value); 48 cgit_root = xstrdup(value);
48 else if (!strcmp(name, "root-title")) 49 else if (!strcmp(name, "root-title"))
49 cgit_root_title = xstrdup(value); 50 cgit_root_title = xstrdup(value);
50 else if (!strcmp(name, "css")) 51 else if (!strcmp(name, "css"))
51 cgit_css = xstrdup(value); 52 cgit_css = xstrdup(value);
52 else if (!strcmp(name, "logo")) 53 else if (!strcmp(name, "logo"))
53 cgit_logo = xstrdup(value); 54 cgit_logo = xstrdup(value);
54 else if (!strcmp(name, "logo-link")) 55 else if (!strcmp(name, "logo-link"))
55 cgit_logo_link = xstrdup(value); 56 cgit_logo_link = xstrdup(value);
56 else if (!strcmp(name, "virtual-root")) 57 else if (!strcmp(name, "virtual-root"))
57 cgit_virtual_root = xstrdup(value); 58 cgit_virtual_root = xstrdup(value);
58 else if (!strcmp(name, "nocache")) 59 else if (!strcmp(name, "nocache"))
59 cgit_nocache = atoi(value); 60 cgit_nocache = atoi(value);
60 else if (!strcmp(name, "cache-root")) 61 else if (!strcmp(name, "cache-root"))
61 cgit_cache_root = xstrdup(value); 62 cgit_cache_root = xstrdup(value);
62} 63}
63 64
64void cgit_repo_config_cb(const char *name, const char *value) 65void cgit_repo_config_cb(const char *name, const char *value)
65{ 66{
66 if (!strcmp(name, "name")) 67 if (!strcmp(name, "name"))
67 cgit_repo_name = xstrdup(value); 68 cgit_repo_name = xstrdup(value);
68 else if (!strcmp(name, "desc")) 69 else if (!strcmp(name, "desc"))
69 cgit_repo_desc = xstrdup(value); 70 cgit_repo_desc = xstrdup(value);
70 else if (!strcmp(name, "owner")) 71 else if (!strcmp(name, "owner"))
71 cgit_repo_owner = xstrdup(value); 72 cgit_repo_owner = xstrdup(value);
72} 73}
73 74
74void cgit_querystring_cb(const char *name, const char *value) 75void cgit_querystring_cb(const char *name, const char *value)
75{ 76{
76 if (!strcmp(name,"r")) { 77 if (!strcmp(name,"r")) {
77 cgit_query_repo = xstrdup(value); 78 cgit_query_repo = xstrdup(value);
78 } else if (!strcmp(name, "p")) { 79 } else if (!strcmp(name, "p")) {
79 cgit_query_page = xstrdup(value); 80 cgit_query_page = xstrdup(value);
80 } else if (!strcmp(name, "h")) { 81 } else if (!strcmp(name, "h")) {
81 cgit_query_head = xstrdup(value); 82 cgit_query_head = xstrdup(value);
82 cgit_query_has_symref = 1; 83 cgit_query_has_symref = 1;
83 } else if (!strcmp(name, "id")) { 84 } else if (!strcmp(name, "id")) {
84 cgit_query_sha1 = xstrdup(value); 85 cgit_query_sha1 = xstrdup(value);
85 cgit_query_has_sha1 = 1; 86 cgit_query_has_sha1 = 1;
87 } else if (!strcmp(name, "id2")) {
88 cgit_query_sha2 = xstrdup(value);
89 cgit_query_has_sha1 = 1;
86 } else if (!strcmp(name, "ofs")) { 90 } else if (!strcmp(name, "ofs")) {
87 cgit_query_ofs = atoi(value); 91 cgit_query_ofs = atoi(value);
88 } 92 }
89} 93}
90 94
91void *cgit_free_commitinfo(struct commitinfo *info) 95void *cgit_free_commitinfo(struct commitinfo *info)
92{ 96{
93 free(info->author); 97 free(info->author);
94 free(info->author_email); 98 free(info->author_email);
95 free(info->committer); 99 free(info->committer);
96 free(info->committer_email); 100 free(info->committer_email);
97 free(info->subject); 101 free(info->subject);
98 free(info); 102 free(info);
99 return NULL; 103 return NULL;
100} 104}
diff --git a/ui-diff.c b/ui-diff.c
new file mode 100644
index 0000000..0bd9ade
--- a/dev/null
+++ b/ui-diff.c
@@ -0,0 +1,131 @@
1/* ui-diff.c: show diff between two blobs
2 *
3 * Copyright (C) 2006 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#include "xdiff.h"
11
12char *diff_buffer;
13int diff_buffer_size;
14
15
16/*
17 * print a single line returned from xdiff
18 */
19static void print_line(char *line, int len)
20{
21 char *class = "ctx";
22 char c = line[len-1];
23
24 if (line[0] == '+')
25 class = "add";
26 else if (line[0] == '-')
27 class = "del";
28 else if (line[0] == '@')
29 class = "hunk";
30
31 htmlf("<div class='%s'>", class);
32 line[len-1] = '\0';
33 html_txt(line);
34 html("</div>");
35 line[len-1] = c;
36}
37
38/*
39 * Receive diff-buffers from xdiff and concatenate them as
40 * needed across multiple callbacks.
41 *
42 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
43 * ripped from git and modified to use globals instead of
44 * a special callback-struct.
45 */
46int diff_cb(void *priv_, mmbuffer_t *mb, int nbuf)
47{
48 int i;
49
50 for (i = 0; i < nbuf; i++) {
51 if (mb[i].ptr[mb[i].size-1] != '\n') {
52 /* Incomplete line */
53 diff_buffer = xrealloc(diff_buffer,
54 diff_buffer_size + mb[i].size);
55 memcpy(diff_buffer + diff_buffer_size,
56 mb[i].ptr, mb[i].size);
57 diff_buffer_size += mb[i].size;
58 continue;
59 }
60
61 /* we have a complete line */
62 if (!diff_buffer) {
63 print_line(mb[i].ptr, mb[i].size);
64 continue;
65 }
66 diff_buffer = xrealloc(diff_buffer,
67 diff_buffer_size + mb[i].size);
68 memcpy(diff_buffer + diff_buffer_size, mb[i].ptr, mb[i].size);
69 print_line(diff_buffer, diff_buffer_size + mb[i].size);
70 free(diff_buffer);
71 diff_buffer = NULL;
72 diff_buffer_size = 0;
73 }
74 if (diff_buffer) {
75 print_line(diff_buffer, diff_buffer_size);
76 free(diff_buffer);
77 diff_buffer = NULL;
78 diff_buffer_size = 0;
79 }
80 return 0;
81}
82
83static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
84{
85 char type[20];
86
87 if (is_null_sha1(sha1)) {
88 file->ptr = (char *)"";
89 file->size = 0;
90 } else {
91 file->ptr = read_sha1_file(sha1, type, &file->size);
92 }
93 return 1;
94}
95
96static void run_diff(const unsigned char *sha1, const unsigned char *sha2)
97{
98 mmfile_t file1, file2;
99 xpparam_t diff_params;
100 xdemitconf_t emit_params;
101 xdemitcb_t emit_cb;
102
103 if (!load_mmfile(&file1, sha1) || !load_mmfile(&file2, sha2)) {
104 cgit_print_error("Unable to load files for diff");
105 return;
106 }
107
108 diff_params.flags = XDF_NEED_MINIMAL;
109
110 emit_params.ctxlen = 3;
111 emit_params.flags = XDL_EMIT_FUNCNAMES;
112
113 emit_cb.outf = diff_cb;
114
115 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
116}
117
118
119
120void cgit_print_diff(const char *old_hex, const char *new_hex)
121{
122 unsigned char sha1[20], sha2[20];
123
124 get_sha1(old_hex, sha1);
125 get_sha1(new_hex, sha2);
126
127 html("<h2>diff</h2>\n");
128 html("<table class='diff'><tr><td>");
129 run_diff(sha1, sha2);
130 html("</td></tr></table>");
131}
diff --git a/xdiff.h b/xdiff.h
new file mode 100644
index 0000000..fa409d5
--- a/dev/null
+++ b/xdiff.h
@@ -0,0 +1,105 @@
1/*
2 * LibXDiff by Davide Libenzi ( File Differential Library )
3 * Copyright (C) 2003 Davide Libenzi
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * Davide Libenzi <davidel@xmailserver.org>
20 *
21 */
22
23#if !defined(XDIFF_H)
24#define XDIFF_H
25
26#ifdef __cplusplus
27extern "C" {
28#endif /* #ifdef __cplusplus */
29
30
31#define XDF_NEED_MINIMAL (1 << 1)
32#define XDF_IGNORE_WHITESPACE (1 << 2)
33#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
34#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE)
35
36#define XDL_PATCH_NORMAL '-'
37#define XDL_PATCH_REVERSE '+'
38#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
39#define XDL_PATCH_IGNOREBSPACE (1 << 8)
40
41#define XDL_EMIT_FUNCNAMES (1 << 0)
42#define XDL_EMIT_COMMON (1 << 1)
43
44#define XDL_MMB_READONLY (1 << 0)
45
46#define XDL_MMF_ATOMIC (1 << 0)
47
48#define XDL_BDOP_INS 1
49#define XDL_BDOP_CPY 2
50#define XDL_BDOP_INSB 3
51
52#define XDL_MERGE_MINIMAL 0
53#define XDL_MERGE_EAGER 1
54#define XDL_MERGE_ZEALOUS 2
55
56typedef struct s_mmfile {
57 char *ptr;
58 long size;
59} mmfile_t;
60
61typedef struct s_mmbuffer {
62 char *ptr;
63 long size;
64} mmbuffer_t;
65
66typedef struct s_xpparam {
67 unsigned long flags;
68} xpparam_t;
69
70typedef struct s_xdemitcb {
71 void *priv;
72 int (*outf)(void *, mmbuffer_t *, int);
73} xdemitcb_t;
74
75typedef struct s_xdemitconf {
76 long ctxlen;
77 unsigned long flags;
78} xdemitconf_t;
79
80typedef struct s_bdiffparam {
81 long bsize;
82} bdiffparam_t;
83
84
85#define xdl_malloc(x) malloc(x)
86#define xdl_free(ptr) free(ptr)
87#define xdl_realloc(ptr,x) realloc(ptr,x)
88
89void *xdl_mmfile_first(mmfile_t *mmf, long *size);
90void *xdl_mmfile_next(mmfile_t *mmf, long *size);
91long xdl_mmfile_size(mmfile_t *mmf);
92
93int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
94 xdemitconf_t const *xecfg, xdemitcb_t *ecb);
95
96int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
97 mmfile_t *mf2, const char *name2,
98 xpparam_t const *xpp, int level, mmbuffer_t *result);
99
100#ifdef __cplusplus
101}
102#endif /* #ifdef __cplusplus */
103
104#endif /* #if !defined(XDIFF_H) */
105