summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
m---------git0
-rw-r--r--html.c20
-rwxr-xr-xtests/setup.sh18
-rwxr-xr-xtests/t0010-validate-html.sh5
-rwxr-xr-xtests/t0101-index.sh6
-rwxr-xr-xtests/t0102-summary.sh6
-rwxr-xr-xtests/t0108-patch.sh37
-rw-r--r--ui-patch.c6
9 files changed, 72 insertions, 28 deletions
diff --git a/Makefile b/Makefile
index 355186e..931c60e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,55 +1,55 @@
1CGIT_VERSION = v0.7.2 1CGIT_VERSION = v0.7.2
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.4.1 7GIT_VER = 1.5.5.rc1
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#
16# Define a way to invoke make in subdirs quietly, shamelessly ripped 16# Define a way to invoke make in subdirs quietly, shamelessly ripped
17# from git.git 17# from git.git
18# 18#
19QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir 19QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
20QUIET_SUBDIR1 = 20QUIET_SUBDIR1 =
21 21
22ifneq ($(findstring $(MAKEFLAGS),w),w) 22ifneq ($(findstring $(MAKEFLAGS),w),w)
23PRINT_DIR = --no-print-directory 23PRINT_DIR = --no-print-directory
24else # "make -w" 24else # "make -w"
25NO_SUBDIR = : 25NO_SUBDIR = :
26endif 26endif
27 27
28ifndef V 28ifndef V
29 QUIET_CC = @echo ' ' CC $@; 29 QUIET_CC = @echo ' ' CC $@;
30 QUIET_MM = @echo ' ' MM $@; 30 QUIET_MM = @echo ' ' MM $@;
31 QUIET_SUBDIR0 = +@subdir= 31 QUIET_SUBDIR0 = +@subdir=
32 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ 32 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
33 $(MAKE) $(PRINT_DIR) -C $$subdir 33 $(MAKE) $(PRINT_DIR) -C $$subdir
34endif 34endif
35 35
36# 36#
37# Define a pattern rule for automatic dependency building 37# Define a pattern rule for automatic dependency building
38# 38#
39%.d: %.c 39%.d: %.c
40 $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@ 40 $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@
41 41
42# 42#
43# Define a pattern rule for silent object building 43# Define a pattern rule for silent object building
44# 44#
45%.o: %.c 45%.o: %.c
46 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $< 46 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
47 47
48 48
49EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 49EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
50OBJECTS = 50OBJECTS =
51OBJECTS += cache.o 51OBJECTS += cache.o
52OBJECTS += cgit.o 52OBJECTS += cgit.o
53OBJECTS += cmd.o 53OBJECTS += cmd.o
54OBJECTS += configfile.o 54OBJECTS += configfile.o
55OBJECTS += html.o 55OBJECTS += html.o
diff --git a/git b/git
Subproject 527270689c364bea9b0630df9bae5e09c2071c1 Subproject 803d5158123346229d71de53818920efbc88ca0
diff --git a/html.c b/html.c
index 98ffaf9..937b5e7 100644
--- a/html.c
+++ b/html.c
@@ -11,151 +11,141 @@
11#include <stdlib.h> 11#include <stdlib.h>
12#include <stdarg.h> 12#include <stdarg.h>
13#include <string.h> 13#include <string.h>
14 14
15int htmlfd = STDOUT_FILENO; 15int htmlfd = STDOUT_FILENO;
16 16
17char *fmt(const char *format, ...) 17char *fmt(const char *format, ...)
18{ 18{
19 static char buf[8][1024]; 19 static char buf[8][1024];
20 static int bufidx; 20 static int bufidx;
21 int len; 21 int len;
22 va_list args; 22 va_list args;
23 23
24 bufidx++; 24 bufidx++;
25 bufidx &= 7; 25 bufidx &= 7;
26 26
27 va_start(args, format); 27 va_start(args, format);
28 len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args); 28 len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
29 va_end(args); 29 va_end(args);
30 if (len>sizeof(buf[bufidx])) { 30 if (len>sizeof(buf[bufidx])) {
31 fprintf(stderr, "[html.c] string truncated: %s\n", format); 31 fprintf(stderr, "[html.c] string truncated: %s\n", format);
32 exit(1); 32 exit(1);
33 } 33 }
34 return buf[bufidx]; 34 return buf[bufidx];
35} 35}
36 36
37void html(const char *txt) 37void html(const char *txt)
38{ 38{
39 write(htmlfd, txt, strlen(txt)); 39 write(htmlfd, txt, strlen(txt));
40} 40}
41 41
42void htmlf(const char *format, ...) 42void htmlf(const char *format, ...)
43{ 43{
44 static char buf[65536]; 44 static char buf[65536];
45 va_list args; 45 va_list args;
46 46
47 va_start(args, format); 47 va_start(args, format);
48 vsnprintf(buf, sizeof(buf), format, args); 48 vsnprintf(buf, sizeof(buf), format, args);
49 va_end(args); 49 va_end(args);
50 html(buf); 50 html(buf);
51} 51}
52 52
53void html_txt(char *txt) 53void html_txt(char *txt)
54{ 54{
55 char *t = txt; 55 char *t = txt;
56 while(t && *t){ 56 while(t && *t){
57 int c = *t; 57 int c = *t;
58 if (c=='<' || c=='>' || c=='&') { 58 if (c=='<' || c=='>' || c=='&') {
59 *t = '\0'; 59 write(htmlfd, txt, t - txt);
60 html(txt);
61 *t = c;
62 if (c=='>') 60 if (c=='>')
63 html("&gt;"); 61 html("&gt;");
64 else if (c=='<') 62 else if (c=='<')
65 html("&lt;"); 63 html("&lt;");
66 else if (c=='&') 64 else if (c=='&')
67 html("&amp;"); 65 html("&amp;");
68 txt = t+1; 66 txt = t+1;
69 } 67 }
70 t++; 68 t++;
71 } 69 }
72 if (t!=txt) 70 if (t!=txt)
73 html(txt); 71 html(txt);
74} 72}
75 73
76void html_ntxt(int len, char *txt) 74void html_ntxt(int len, char *txt)
77{ 75{
78 char *t = txt; 76 char *t = txt;
79 while(t && *t && len--){ 77 while(t && *t && len--){
80 int c = *t; 78 int c = *t;
81 if (c=='<' || c=='>' || c=='&') { 79 if (c=='<' || c=='>' || c=='&') {
82 *t = '\0'; 80 write(htmlfd, txt, t - txt);
83 html(txt);
84 *t = c;
85 if (c=='>') 81 if (c=='>')
86 html("&gt;"); 82 html("&gt;");
87 else if (c=='<') 83 else if (c=='<')
88 html("&lt;"); 84 html("&lt;");
89 else if (c=='&') 85 else if (c=='&')
90 html("&amp;"); 86 html("&amp;");
91 txt = t+1; 87 txt = t+1;
92 } 88 }
93 t++; 89 t++;
94 } 90 }
95 if (t!=txt) { 91 if (t!=txt)
96 char c = *t; 92 write(htmlfd, txt, t - txt);
97 *t = '\0';
98 html(txt);
99 *t = c;
100 }
101 if (len<0) 93 if (len<0)
102 html("..."); 94 html("...");
103} 95}
104 96
105void html_attr(char *txt) 97void html_attr(char *txt)
106{ 98{
107 char *t = txt; 99 char *t = txt;
108 while(t && *t){ 100 while(t && *t){
109 int c = *t; 101 int c = *t;
110 if (c=='<' || c=='>' || c=='\'') { 102 if (c=='<' || c=='>' || c=='\'') {
111 *t = '\0'; 103 write(htmlfd, txt, t - txt);
112 html(txt);
113 *t = c;
114 if (c=='>') 104 if (c=='>')
115 html("&gt;"); 105 html("&gt;");
116 else if (c=='<') 106 else if (c=='<')
117 html("&lt;"); 107 html("&lt;");
118 else if (c=='\'') 108 else if (c=='\'')
119 html("&quote;"); 109 html("&quote;");
120 txt = t+1; 110 txt = t+1;
121 } 111 }
122 t++; 112 t++;
123 } 113 }
124 if (t!=txt) 114 if (t!=txt)
125 html(txt); 115 html(txt);
126} 116}
127 117
128void html_hidden(char *name, char *value) 118void html_hidden(char *name, char *value)
129{ 119{
130 html("<input type='hidden' name='"); 120 html("<input type='hidden' name='");
131 html_attr(name); 121 html_attr(name);
132 html("' value='"); 122 html("' value='");
133 html_attr(value); 123 html_attr(value);
134 html("'/>"); 124 html("'/>");
135} 125}
136 126
137void html_option(char *value, char *text, char *selected_value) 127void html_option(char *value, char *text, char *selected_value)
138{ 128{
139 html("<option value='"); 129 html("<option value='");
140 html_attr(value); 130 html_attr(value);
141 html("'"); 131 html("'");
142 if (selected_value && !strcmp(selected_value, value)) 132 if (selected_value && !strcmp(selected_value, value))
143 html(" selected='selected'"); 133 html(" selected='selected'");
144 html(">"); 134 html(">");
145 html_txt(text); 135 html_txt(text);
146 html("</option>\n"); 136 html("</option>\n");
147} 137}
148 138
149void html_link_open(char *url, char *title, char *class) 139void html_link_open(char *url, char *title, char *class)
150{ 140{
151 html("<a href='"); 141 html("<a href='");
152 html_attr(url); 142 html_attr(url);
153 if (title) { 143 if (title) {
154 html("' title='"); 144 html("' title='");
155 html_attr(title); 145 html_attr(title);
156 } 146 }
157 if (class) { 147 if (class) {
158 html("' class='"); 148 html("' class='");
159 html_attr(class); 149 html_attr(class);
160 } 150 }
161 html("'>"); 151 html("'>");
diff --git a/tests/setup.sh b/tests/setup.sh
index 51d5a75..66bf406 100755
--- a/tests/setup.sh
+++ b/tests/setup.sh
@@ -9,100 +9,108 @@
9# cgit_url(url) - call cgit with the specified virtual url 9# cgit_url(url) - call cgit with the specified virtual url
10# 10#
11# Example script: 11# Example script:
12# 12#
13# . setup.sh 13# . setup.sh
14# prepare_tests "html validation" 14# prepare_tests "html validation"
15# run_test 'repo index' 'cgit_url "/" | tidy -e' 15# run_test 'repo index' 'cgit_url "/" | tidy -e'
16# run_test 'repo summary' 'cgit_url "/foo" | tidy -e' 16# run_test 'repo summary' 'cgit_url "/foo" | tidy -e'
17 17
18 18
19mkrepo() { 19mkrepo() {
20 name=$1 20 name=$1
21 count=$2 21 count=$2
22 dir=$PWD 22 dir=$PWD
23 test -d $name && return 23 test -d $name && return
24 printf "Creating testrepo %s\n" $name 24 printf "Creating testrepo %s\n" $name
25 mkdir -p $name 25 mkdir -p $name
26 cd $name 26 cd $name
27 git init 27 git init
28 for ((n=1; n<=count; n++)) 28 for ((n=1; n<=count; n++))
29 do 29 do
30 echo $n >file-$n 30 echo $n >file-$n
31 git add file-$n 31 git add file-$n
32 git commit -m "commit $n" 32 git commit -m "commit $n"
33 done 33 done
34 cd $dir 34 cd $dir
35} 35}
36 36
37setup_repos() 37setup_repos()
38{ 38{
39 rm -rf trash/cache 39 rm -rf trash/cache
40 mkdir -p trash/cache 40 mkdir -p trash/cache
41 mkrepo trash/repos/foo 5 >/dev/null 41 mkrepo trash/repos/foo 5 >/dev/null
42 mkrepo trash/repos/bar 50 >/dev/null 42 mkrepo trash/repos/bar 50 >/dev/null
43 cat >trash/cgitrc <<EOF 43 cat >trash/cgitrc <<EOF
44virtual-root=/ 44virtual-root=/
45cache-root=$PWD/trash/cache 45cache-root=$PWD/trash/cache
46 46
47nocache=0 47nocache=0
48snapshots=tar.gz tar.bz zip 48snapshots=tar.gz tar.bz zip
49enable-log-filecount=1 49enable-log-filecount=1
50enable-log-linecount=1 50enable-log-linecount=1
51summary-log=5 51summary-log=5
52summary-branches=5 52summary-branches=5
53summary-tags=5 53summary-tags=5
54 54
55repo.url=foo 55repo.url=foo
56repo.path=$PWD/trash/repos/foo/.git 56repo.path=$PWD/trash/repos/foo/.git
57repo.desc=the foo repo 57# Do not specify a description for this repo, as it then will be assigned
58# the constant value "[no description]" (which actually used to cause a
59# segfault).
58 60
59repo.url=bar 61repo.url=bar
60repo.path=$PWD/trash/repos/bar/.git 62repo.path=$PWD/trash/repos/bar/.git
61repo.desc=the bar repo 63repo.desc=the bar repo
62EOF 64EOF
63} 65}
64 66
65prepare_tests() 67prepare_tests()
66{ 68{
67 setup_repos 69 setup_repos
70 rm -f test-output.log 2>/dev/null
68 test_count=0 71 test_count=0
69 test_failed=0 72 test_failed=0
73 echo "[$0]" "$@" >test-output.log
70 echo "$@" "($0)" 74 echo "$@" "($0)"
71} 75}
72 76
73tests_done() 77tests_done()
74{ 78{
75 printf "\n" 79 printf "\n"
76 if test $test_failed -gt 0 80 if test $test_failed -gt 0
77 then 81 then
78 printf "[%s of %s tests failed]\n" $test_failed $test_count 82 printf "test: *** %s failure(s), logfile=%s\n" \
83 $test_failed "$(pwd)/test-output.log"
79 false 84 false
80 fi 85 fi
81} 86}
82 87
83run_test() 88run_test()
84{ 89{
85 desc=$1 90 desc=$1
86 script=$2 91 script=$2
87 ((test_count++)) 92 ((test_count++))
88 eval "$2" >test-output.log 93 printf "\ntest %d: name='%s'\n" $test_count "$desc" >>test-output.log
94 printf "test %d: eval='%s'\n" $test_count "$2" >>test-output.log
95 eval "$2" >>test-output.log 2>>test-output.log
89 res=$? 96 res=$?
97 printf "test %d: exitcode=%d\n" $test_count $res >>test-output.log
90 if test $res = 0 98 if test $res = 0
91 then 99 then
92 printf " %s: ok - %s\n" $test_count "$desc" 100 printf " %2d) %-60s [ok]\n" $test_count "$desc"
93 else 101 else
94 ((test_failed++)) 102 ((test_failed++))
95 printf " %s: fail - %s\n" $test_count "$desc" 103 printf " %2d) %-60s [failed]\n" $test_count "$desc"
96 fi 104 fi
97} 105}
98 106
99cgit_query() 107cgit_query()
100{ 108{
101 CGIT_CONFIG="$PWD/trash/cgitrc" QUERY_STRING="$1" "$PWD/../cgit" 109 CGIT_CONFIG="$PWD/trash/cgitrc" QUERY_STRING="$1" "$PWD/../cgit"
102} 110}
103 111
104cgit_url() 112cgit_url()
105{ 113{
106 CGIT_CONFIG="$PWD/trash/cgitrc" QUERY_STRING="url=$1" "$PWD/../cgit" 114 CGIT_CONFIG="$PWD/trash/cgitrc" QUERY_STRING="url=$1" "$PWD/../cgit"
107} 115}
108 116
diff --git a/tests/t0010-validate-html.sh b/tests/t0010-validate-html.sh
index 907a415..94aa52b 100755
--- a/tests/t0010-validate-html.sh
+++ b/tests/t0010-validate-html.sh
@@ -1,31 +1,34 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./setup.sh 3. ./setup.sh
4 4
5 5
6test_url() 6test_url()
7{ 7{
8 tidy_opt="-eq" 8 tidy_opt="-eq"
9 test -z "$NO_TIDY_WARNINGS" || tidy_opt+=" --show-warnings no" 9 test -z "$NO_TIDY_WARNINGS" || tidy_opt+=" --show-warnings no"
10 cgit_url "$1" | sed -e "1,4d" >trash/tidy-$test_count 10 cgit_url "$1" >trash/tidy-$test_count || return
11 sed -ie "1,4d" trash/tidy-$test_count || return
11 tidy $tidy_opt trash/tidy-$test_count 12 tidy $tidy_opt trash/tidy-$test_count
12 rc=$? 13 rc=$?
14
15 # tidy returns with exitcode 1 on warnings, 2 on error
13 if test $rc = 2 16 if test $rc = 2
14 then 17 then
15 false 18 false
16 else 19 else
17 : 20 :
18 fi 21 fi
19} 22}
20 23
21prepare_tests 'Validate html with tidy' 24prepare_tests 'Validate html with tidy'
22 25
23run_test 'index page' 'test_url ""' 26run_test 'index page' 'test_url ""'
24run_test 'foo' 'test_url "foo"' 27run_test 'foo' 'test_url "foo"'
25run_test 'foo/log' 'test_url "foo/log"' 28run_test 'foo/log' 'test_url "foo/log"'
26run_test 'foo/tree' 'test_url "foo/tree"' 29run_test 'foo/tree' 'test_url "foo/tree"'
27run_test 'foo/tree/file-1' 'test_url "foo/tree/file-1"' 30run_test 'foo/tree/file-1' 'test_url "foo/tree/file-1"'
28run_test 'foo/commit' 'test_url "foo/commit"' 31run_test 'foo/commit' 'test_url "foo/commit"'
29run_test 'foo/diff' 'test_url "foo/diff"' 32run_test 'foo/diff' 'test_url "foo/diff"'
30 33
31tests_done 34tests_done
diff --git a/tests/t0101-index.sh b/tests/t0101-index.sh
index 12ed00c..445af6a 100755
--- a/tests/t0101-index.sh
+++ b/tests/t0101-index.sh
@@ -1,13 +1,15 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./setup.sh 3. ./setup.sh
4 4
5prepare_tests "Check content on index page" 5prepare_tests "Check content on index page"
6 6
7run_test 'generate index page' 'cgit_url "" >trash/tmp' 7run_test 'generate index page' 'cgit_url "" >trash/tmp'
8run_test 'find foo repo' 'grep -e "foo" trash/tmp' 8run_test 'find foo repo' 'grep -e "foo" trash/tmp'
9run_test 'find foo description' 'grep -e "\[no description\]" trash/tmp'
9run_test 'find bar repo' 'grep -e "bar" trash/tmp' 10run_test 'find bar repo' 'grep -e "bar" trash/tmp'
10run_test 'no tree-link' 'grep -ve "foo/tree" trash/tmp' 11run_test 'find bar description' 'grep -e "the bar repo" trash/tmp'
11run_test 'no log-link' 'grep -ve "foo/log" trash/tmp' 12run_test 'no tree-link' '! grep -e "foo/tree" trash/tmp'
13run_test 'no log-link' '! grep -e "foo/log" trash/tmp'
12 14
13tests_done 15tests_done
diff --git a/tests/t0102-summary.sh b/tests/t0102-summary.sh
index 7edd675..f0b0d9a 100755
--- a/tests/t0102-summary.sh
+++ b/tests/t0102-summary.sh
@@ -1,20 +1,20 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./setup.sh 3. ./setup.sh
4 4
5prepare_tests "Check content on summary page" 5prepare_tests "Check content on summary page"
6 6
7run_test 'generate foo summary' 'cgit_url "foo" >trash/tmp' 7run_test 'generate foo summary' 'cgit_url "foo" >trash/tmp'
8run_test 'find commit 1' 'grep -e "commit 1" trash/tmp' 8run_test 'find commit 1' 'grep -e "commit 1" trash/tmp'
9run_test 'find commit 5' 'grep -e "commit 5" trash/tmp' 9run_test 'find commit 5' 'grep -e "commit 5" trash/tmp'
10run_test 'find branch master' 'grep -e "master" trash/tmp' 10run_test 'find branch master' 'grep -e "master" trash/tmp'
11run_test 'no tags' 'grep -ve "tags" trash/tmp' 11run_test 'no tags' '! grep -e "tags" trash/tmp'
12 12
13run_test 'generate bar summary' 'cgit_url "bar" >trash/tmp' 13run_test 'generate bar summary' 'cgit_url "bar" >trash/tmp'
14run_test 'no commit 45' 'grep -ve "commit 45" trash/tmp' 14run_test 'no commit 45' '! grep -e "commit 45" trash/tmp'
15run_test 'find commit 46' 'grep -e "commit 46" trash/tmp' 15run_test 'find commit 46' 'grep -e "commit 46" trash/tmp'
16run_test 'find commit 50' 'grep -e "commit 50" trash/tmp' 16run_test 'find commit 50' 'grep -e "commit 50" trash/tmp'
17run_test 'find branch master' 'grep -e "master" trash/tmp' 17run_test 'find branch master' 'grep -e "master" trash/tmp'
18run_test 'no tags' 'grep -ve "tags" trash/tmp' 18run_test 'no tags' '! grep -e "tags" trash/tmp'
19 19
20tests_done 20tests_done
diff --git a/tests/t0108-patch.sh b/tests/t0108-patch.sh
new file mode 100755
index 0000000..33351d6
--- a/dev/null
+++ b/tests/t0108-patch.sh
@@ -0,0 +1,37 @@
1#!/bin/sh
2
3. ./setup.sh
4
5prepare_tests "Check content on patch page"
6
7run_test 'generate foo/patch' '
8 cgit_query "url=foo/patch" >trash/tmp
9'
10
11run_test 'find `From:` line' '
12 grep -e "^From: " trash/tmp
13'
14
15run_test 'find `Date:` line' '
16 grep -e "^Date: " trash/tmp
17'
18
19run_test 'find `Subject:` line' '
20 grep -e "^Subject: commit 5" trash/tmp
21'
22
23run_test 'find `cgit` signature' '
24 tail -1 trash/tmp | grep -e "^cgit"
25'
26
27run_test 'find initial commit' '
28 root=$(git --git-dir=$PWD/trash/repos/foo/.git rev-list HEAD | tail -1)
29'
30
31run_test 'generate patch for initial commit' '
32 cgit_query "url=foo/patch&id=$root" >trash/tmp
33'
34
35run_test 'find `cgit` signature' '
36 tail -1 trash/tmp | grep -e "^cgit"
37'
diff --git a/ui-patch.c b/ui-patch.c
index 36bfae4..c1c4ce3 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -45,70 +45,74 @@ static void header(unsigned char *sha1, char *path1, int mode1,
45 htmlf("index %s..%s", abbrev1, abbrev2); 45 htmlf("index %s..%s", abbrev1, abbrev2);
46 free(abbrev1); 46 free(abbrev1);
47 free(abbrev2); 47 free(abbrev2);
48 if (mode1 != 0 && mode2 != 0) { 48 if (mode1 != 0 && mode2 != 0) {
49 htmlf(" %.6o", mode1); 49 htmlf(" %.6o", mode1);
50 if (mode2 != mode1) 50 if (mode2 != mode1)
51 htmlf("..%.6o", mode2); 51 htmlf("..%.6o", mode2);
52 } 52 }
53 htmlf("\n--- a/%s\n", path1); 53 htmlf("\n--- a/%s\n", path1);
54 htmlf("+++ b/%s\n", path2); 54 htmlf("+++ b/%s\n", path2);
55 } 55 }
56} 56}
57 57
58static void filepair_cb(struct diff_filepair *pair) 58static void filepair_cb(struct diff_filepair *pair)
59{ 59{
60 header(pair->one->sha1, pair->one->path, pair->one->mode, 60 header(pair->one->sha1, pair->one->path, pair->one->mode,
61 pair->two->sha1, pair->two->path, pair->two->mode); 61 pair->two->sha1, pair->two->path, pair->two->mode);
62 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 62 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
63 if (S_ISGITLINK(pair->one->mode)) 63 if (S_ISGITLINK(pair->one->mode))
64 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 64 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
65 if (S_ISGITLINK(pair->two->mode)) 65 if (S_ISGITLINK(pair->two->mode))
66 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 66 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
67 return; 67 return;
68 } 68 }
69 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line)) 69 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line))
70 html("Error running diff"); 70 html("Error running diff");
71} 71}
72 72
73void cgit_print_patch(char *hex) 73void cgit_print_patch(char *hex)
74{ 74{
75 struct commit *commit; 75 struct commit *commit;
76 struct commitinfo *info; 76 struct commitinfo *info;
77 unsigned char sha1[20], old_sha1[20]; 77 unsigned char sha1[20], old_sha1[20];
78 char *patchname; 78 char *patchname;
79 79
80 if (!hex) 80 if (!hex)
81 hex = ctx.qry.head; 81 hex = ctx.qry.head;
82 82
83 if (get_sha1(hex, sha1)) { 83 if (get_sha1(hex, sha1)) {
84 cgit_print_error(fmt("Bad object id: %s", hex)); 84 cgit_print_error(fmt("Bad object id: %s", hex));
85 return; 85 return;
86 } 86 }
87 commit = lookup_commit_reference(sha1); 87 commit = lookup_commit_reference(sha1);
88 if (!commit) { 88 if (!commit) {
89 cgit_print_error(fmt("Bad commit reference: %s", hex)); 89 cgit_print_error(fmt("Bad commit reference: %s", hex));
90 return; 90 return;
91 } 91 }
92 info = cgit_parse_commit(commit); 92 info = cgit_parse_commit(commit);
93 hashcpy(old_sha1, commit->parents->item->object.sha1); 93
94 if (commit->parents && commit->parents->item)
95 hashcpy(old_sha1, commit->parents->item->object.sha1);
96 else
97 hashclr(old_sha1);
94 98
95 patchname = fmt("%s.patch", sha1_to_hex(sha1)); 99 patchname = fmt("%s.patch", sha1_to_hex(sha1));
96 ctx.page.mimetype = "text/plain"; 100 ctx.page.mimetype = "text/plain";
97 ctx.page.filename = patchname; 101 ctx.page.filename = patchname;
98 cgit_print_http_headers(&ctx); 102 cgit_print_http_headers(&ctx);
99 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1)); 103 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
100 htmlf("From: %s%s\n", info->author, info->author_email); 104 htmlf("From: %s%s\n", info->author, info->author_email);
101 html("Date: "); 105 html("Date: ");
102 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n"); 106 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n");
103 htmlf("Subject: %s\n\n", info->subject); 107 htmlf("Subject: %s\n\n", info->subject);
104 if (info->msg && *info->msg) { 108 if (info->msg && *info->msg) {
105 htmlf("%s", info->msg); 109 htmlf("%s", info->msg);
106 if (info->msg[strlen(info->msg) - 1] != '\n') 110 if (info->msg[strlen(info->msg) - 1] != '\n')
107 html("\n"); 111 html("\n");
108 } 112 }
109 html("---\n"); 113 html("---\n");
110 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL); 114 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL);
111 html("--\n"); 115 html("--\n");
112 htmlf("cgit %s\n", CGIT_VERSION); 116 htmlf("cgit %s\n", CGIT_VERSION);
113 cgit_free_commitinfo(info); 117 cgit_free_commitinfo(info);
114} 118}