author | Lars Hjemli <hjemli@gmail.com> | 2009-03-15 08:06:20 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2009-03-15 08:06:20 (UTC) |
commit | 780d2d98598e621d12c7b23d4c68b5ddc7019838 (patch) (unidiff) | |
tree | ab520cb674fd07ab265693ba6f89126e7c0f823f | |
parent | 942a4c200168e110ec009c3260dc5f5f050de325 (diff) | |
parent | 6fddad7251021b307c8a3f70fdd2aa04c3f74eaa (diff) | |
download | cgit-780d2d98598e621d12c7b23d4c68b5ddc7019838.zip cgit-780d2d98598e621d12c7b23d4c68b5ddc7019838.tar.gz cgit-780d2d98598e621d12c7b23d4c68b5ddc7019838.tar.bz2 |
Merge branch 'stable'
-rw-r--r-- | ui-snapshot.c | 23 | ||||
-rw-r--r-- | ui-tree.c | 13 |
2 files changed, 25 insertions, 11 deletions
diff --git a/ui-snapshot.c b/ui-snapshot.c index f25613e..5372f5d 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c | |||
@@ -1,194 +1,205 @@ | |||
1 | /* ui-snapshot.c: generate snapshot of a commit | 1 | /* ui-snapshot.c: generate snapshot of a commit |
2 | * | 2 | * |
3 | * Copyright (C) 2006 Lars Hjemli | 3 | * Copyright (C) 2006 Lars Hjemli |
4 | * | 4 | * |
5 | * Licensed under GNU General Public License v2 | 5 | * Licensed under GNU General Public License v2 |
6 | * (see COPYING for full license text) | 6 | * (see COPYING for full license text) |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "cgit.h" | 9 | #include "cgit.h" |
10 | #include "html.h" | 10 | #include "html.h" |
11 | #include "ui-shared.h" | 11 | #include "ui-shared.h" |
12 | 12 | ||
13 | static int write_compressed_tar_archive(struct archiver_args *args,const char *filter) | 13 | static int write_compressed_tar_archive(struct archiver_args *args,const char *filter) |
14 | { | 14 | { |
15 | int rw[2]; | 15 | int rw[2]; |
16 | pid_t gzpid; | 16 | pid_t gzpid; |
17 | int stdout2; | 17 | int stdout2; |
18 | int status; | 18 | int status; |
19 | int rv; | 19 | int rv; |
20 | 20 | ||
21 | stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing"); | 21 | stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing"); |
22 | chk_zero(pipe(rw), "Opening pipe from compressor subprocess"); | 22 | chk_zero(pipe(rw), "Opening pipe from compressor subprocess"); |
23 | gzpid = chk_non_negative(fork(), "Forking compressor subprocess"); | 23 | gzpid = chk_non_negative(fork(), "Forking compressor subprocess"); |
24 | if(gzpid==0) { | 24 | if(gzpid==0) { |
25 | /* child */ | 25 | /* child */ |
26 | chk_zero(close(rw[1]), "Closing write end of pipe in child"); | 26 | chk_zero(close(rw[1]), "Closing write end of pipe in child"); |
27 | chk_zero(close(STDIN_FILENO), "Closing STDIN"); | 27 | chk_zero(close(STDIN_FILENO), "Closing STDIN"); |
28 | chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin"); | 28 | chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin"); |
29 | execlp(filter,filter,NULL); | 29 | execlp(filter,filter,NULL); |
30 | _exit(-1); | 30 | _exit(-1); |
31 | } | 31 | } |
32 | /* parent */ | 32 | /* parent */ |
33 | chk_zero(close(rw[0]), "Closing read end of pipe"); | 33 | chk_zero(close(rw[0]), "Closing read end of pipe"); |
34 | chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor"); | 34 | chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor"); |
35 | 35 | ||
36 | rv = write_tar_archive(args); | 36 | rv = write_tar_archive(args); |
37 | 37 | ||
38 | chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor"); | 38 | chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor"); |
39 | chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT"); | 39 | chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT"); |
40 | chk_zero(close(stdout2), "Closing uncompressed STDOUT"); | 40 | chk_zero(close(stdout2), "Closing uncompressed STDOUT"); |
41 | chk_zero(close(rw[1]), "Closing write end of pipe in parent"); | 41 | chk_zero(close(rw[1]), "Closing write end of pipe in parent"); |
42 | chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process"); | 42 | chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process"); |
43 | if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) ) | 43 | if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) ) |
44 | cgit_print_error("Failed to compress archive"); | 44 | cgit_print_error("Failed to compress archive"); |
45 | 45 | ||
46 | return rv; | 46 | return rv; |
47 | } | 47 | } |
48 | 48 | ||
49 | static int write_tar_gzip_archive(struct archiver_args *args) | 49 | static int write_tar_gzip_archive(struct archiver_args *args) |
50 | { | 50 | { |
51 | return write_compressed_tar_archive(args,"gzip"); | 51 | return write_compressed_tar_archive(args,"gzip"); |
52 | } | 52 | } |
53 | 53 | ||
54 | static int write_tar_bzip2_archive(struct archiver_args *args) | 54 | static int write_tar_bzip2_archive(struct archiver_args *args) |
55 | { | 55 | { |
56 | return write_compressed_tar_archive(args,"bzip2"); | 56 | return write_compressed_tar_archive(args,"bzip2"); |
57 | } | 57 | } |
58 | 58 | ||
59 | const struct cgit_snapshot_format cgit_snapshot_formats[] = { | 59 | const struct cgit_snapshot_format cgit_snapshot_formats[] = { |
60 | { ".zip", "application/x-zip", write_zip_archive, 0x1 }, | 60 | { ".zip", "application/x-zip", write_zip_archive, 0x1 }, |
61 | { ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x2 }, | 61 | { ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x2 }, |
62 | { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x4 }, | 62 | { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x4 }, |
63 | { ".tar", "application/x-tar", write_tar_archive, 0x8 }, | 63 | { ".tar", "application/x-tar", write_tar_archive, 0x8 }, |
64 | {} | 64 | {} |
65 | }; | 65 | }; |
66 | 66 | ||
67 | static const struct cgit_snapshot_format *get_format(const char *filename) | 67 | static const struct cgit_snapshot_format *get_format(const char *filename) |
68 | { | 68 | { |
69 | const struct cgit_snapshot_format *fmt; | 69 | const struct cgit_snapshot_format *fmt; |
70 | int fl, sl; | 70 | int fl, sl; |
71 | 71 | ||
72 | fl = strlen(filename); | 72 | fl = strlen(filename); |
73 | for(fmt = cgit_snapshot_formats; fmt->suffix; fmt++) { | 73 | for(fmt = cgit_snapshot_formats; fmt->suffix; fmt++) { |
74 | sl = strlen(fmt->suffix); | 74 | sl = strlen(fmt->suffix); |
75 | if (sl >= fl) | 75 | if (sl >= fl) |
76 | continue; | 76 | continue; |
77 | if (!strcmp(fmt->suffix, filename + fl - sl)) | 77 | if (!strcmp(fmt->suffix, filename + fl - sl)) |
78 | return fmt; | 78 | return fmt; |
79 | } | 79 | } |
80 | return NULL; | 80 | return NULL; |
81 | } | 81 | } |
82 | 82 | ||
83 | static int make_snapshot(const struct cgit_snapshot_format *format, | 83 | static int make_snapshot(const struct cgit_snapshot_format *format, |
84 | const char *hex, const char *prefix, | 84 | const char *hex, const char *prefix, |
85 | const char *filename) | 85 | const char *filename) |
86 | { | 86 | { |
87 | struct archiver_args args; | 87 | struct archiver_args args; |
88 | struct commit *commit; | 88 | struct commit *commit; |
89 | unsigned char sha1[20]; | 89 | unsigned char sha1[20]; |
90 | 90 | ||
91 | if(get_sha1(hex, sha1)) { | 91 | if(get_sha1(hex, sha1)) { |
92 | cgit_print_error(fmt("Bad object id: %s", hex)); | 92 | cgit_print_error(fmt("Bad object id: %s", hex)); |
93 | return 1; | 93 | return 1; |
94 | } | 94 | } |
95 | commit = lookup_commit_reference(sha1); | 95 | commit = lookup_commit_reference(sha1); |
96 | if(!commit) { | 96 | if(!commit) { |
97 | cgit_print_error(fmt("Not a commit reference: %s", hex)); | 97 | cgit_print_error(fmt("Not a commit reference: %s", hex)); |
98 | return 1; | 98 | return 1; |
99 | } | 99 | } |
100 | memset(&args, 0, sizeof(args)); | 100 | memset(&args, 0, sizeof(args)); |
101 | if (prefix) { | 101 | if (prefix) { |
102 | args.base = fmt("%s/", prefix); | 102 | args.base = fmt("%s/", prefix); |
103 | args.baselen = strlen(prefix) + 1; | 103 | args.baselen = strlen(prefix) + 1; |
104 | } else { | 104 | } else { |
105 | args.base = ""; | 105 | args.base = ""; |
106 | args.baselen = 0; | 106 | args.baselen = 0; |
107 | } | 107 | } |
108 | args.tree = commit->tree; | 108 | args.tree = commit->tree; |
109 | args.time = commit->date; | 109 | args.time = commit->date; |
110 | ctx.page.mimetype = xstrdup(format->mimetype); | 110 | ctx.page.mimetype = xstrdup(format->mimetype); |
111 | ctx.page.filename = xstrdup(filename); | 111 | ctx.page.filename = xstrdup(filename); |
112 | cgit_print_http_headers(&ctx); | 112 | cgit_print_http_headers(&ctx); |
113 | format->write_func(&args); | 113 | format->write_func(&args); |
114 | return 0; | 114 | return 0; |
115 | } | 115 | } |
116 | 116 | ||
117 | /* Try to guess the requested revision from the requested snapshot name. | 117 | /* Try to guess the requested revision from the requested snapshot name. |
118 | * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become | 118 | * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become |
119 | * "cgit-0.7.2". If this is a valid commit object name we've got a winner. | 119 | * "cgit-0.7.2". If this is a valid commit object name we've got a winner. |
120 | * Otherwise, if the snapshot name has a prefix matching the result from | 120 | * Otherwise, if the snapshot name has a prefix matching the result from |
121 | * repo_basename(), we strip the basename and any following '-' and '_' | 121 | * repo_basename(), we strip the basename and any following '-' and '_' |
122 | * characters ("cgit-0.7.2" -> "0.7.2") and check the resulting name once | 122 | * characters ("cgit-0.7.2" -> "0.7.2") and check the resulting name once |
123 | * more. If this still isn't a valid commit object name, we check if pre- | 123 | * more. If this still isn't a valid commit object name, we check if pre- |
124 | * pending a 'v' to the remaining snapshot name ("0.7.2" -> "v0.7.2") gives | 124 | * pending a 'v' to the remaining snapshot name ("0.7.2" -> "v0.7.2") gives |
125 | * us something valid. | 125 | * us something valid. |
126 | */ | 126 | */ |
127 | static const char *get_ref_from_filename(const char *url, const char *filename, | 127 | static const char *get_ref_from_filename(const char *url, const char *filename, |
128 | const struct cgit_snapshot_format *format) | 128 | const struct cgit_snapshot_format *format) |
129 | { | 129 | { |
130 | const char *reponame; | 130 | const char *reponame; |
131 | unsigned char sha1[20]; | 131 | unsigned char sha1[20]; |
132 | char *snapshot; | 132 | char *snapshot; |
133 | 133 | ||
134 | snapshot = xstrdup(filename); | 134 | snapshot = xstrdup(filename); |
135 | snapshot[strlen(snapshot) - strlen(format->suffix)] = '\0'; | 135 | snapshot[strlen(snapshot) - strlen(format->suffix)] = '\0'; |
136 | fprintf(stderr, "snapshot=%s\n", snapshot); | 136 | fprintf(stderr, "snapshot=%s\n", snapshot); |
137 | 137 | ||
138 | if (get_sha1(snapshot, sha1) == 0) | 138 | if (get_sha1(snapshot, sha1) == 0) |
139 | return snapshot; | 139 | return snapshot; |
140 | 140 | ||
141 | reponame = cgit_repobasename(url); | 141 | reponame = cgit_repobasename(url); |
142 | fprintf(stderr, "reponame=%s\n", reponame); | 142 | fprintf(stderr, "reponame=%s\n", reponame); |
143 | if (prefixcmp(snapshot, reponame) == 0) { | 143 | if (prefixcmp(snapshot, reponame) == 0) { |
144 | snapshot += strlen(reponame); | 144 | snapshot += strlen(reponame); |
145 | while (snapshot && (*snapshot == '-' || *snapshot == '_')) | 145 | while (snapshot && (*snapshot == '-' || *snapshot == '_')) |
146 | snapshot++; | 146 | snapshot++; |
147 | } | 147 | } |
148 | 148 | ||
149 | if (get_sha1(snapshot, sha1) == 0) | 149 | if (get_sha1(snapshot, sha1) == 0) |
150 | return snapshot; | 150 | return snapshot; |
151 | 151 | ||
152 | snapshot = fmt("v%s", snapshot); | 152 | snapshot = fmt("v%s", snapshot); |
153 | if (get_sha1(snapshot, sha1) == 0) | 153 | if (get_sha1(snapshot, sha1) == 0) |
154 | return snapshot; | 154 | return snapshot; |
155 | 155 | ||
156 | return NULL; | 156 | return NULL; |
157 | } | 157 | } |
158 | 158 | ||
159 | void show_error(char *msg) | ||
160 | { | ||
161 | ctx.page.mimetype = "text/html"; | ||
162 | cgit_print_http_headers(&ctx); | ||
163 | cgit_print_docstart(&ctx); | ||
164 | cgit_print_pageheader(&ctx); | ||
165 | cgit_print_error(msg); | ||
166 | cgit_print_docend(); | ||
167 | } | ||
168 | |||
159 | void cgit_print_snapshot(const char *head, const char *hex, | 169 | void cgit_print_snapshot(const char *head, const char *hex, |
160 | const char *filename, int snapshots, int dwim) | 170 | const char *filename, int snapshots, int dwim) |
161 | { | 171 | { |
162 | const struct cgit_snapshot_format* f; | 172 | const struct cgit_snapshot_format* f; |
163 | char *prefix = NULL; | 173 | char *prefix = NULL; |
164 | 174 | ||
175 | if (!filename) { | ||
176 | show_error("No snapshot name specified"); | ||
177 | return; | ||
178 | } | ||
179 | |||
165 | f = get_format(filename); | 180 | f = get_format(filename); |
166 | if (!f) { | 181 | if (!f) { |
167 | ctx.page.mimetype = "text/html"; | 182 | show_error(xstrdup(fmt("Unsupported snapshot format: %s", |
168 | cgit_print_http_headers(&ctx); | 183 | filename))); |
169 | cgit_print_docstart(&ctx); | ||
170 | cgit_print_pageheader(&ctx); | ||
171 | cgit_print_error(fmt("Unsupported snapshot format: %s", filename)); | ||
172 | cgit_print_docend(); | ||
173 | return; | 184 | return; |
174 | } | 185 | } |
175 | 186 | ||
176 | if (!hex && dwim) { | 187 | if (!hex && dwim) { |
177 | hex = get_ref_from_filename(ctx.repo->url, filename, f); | 188 | hex = get_ref_from_filename(ctx.repo->url, filename, f); |
178 | if (hex == NULL) { | 189 | if (hex == NULL) { |
179 | html_status(404, "Not found", 0); | 190 | html_status(404, "Not found", 0); |
180 | return; | 191 | return; |
181 | } | 192 | } |
182 | prefix = xstrdup(filename); | 193 | prefix = xstrdup(filename); |
183 | prefix[strlen(filename) - strlen(f->suffix)] = '\0'; | 194 | prefix[strlen(filename) - strlen(f->suffix)] = '\0'; |
184 | } | 195 | } |
185 | 196 | ||
186 | if (!hex) | 197 | if (!hex) |
187 | hex = head; | 198 | hex = head; |
188 | 199 | ||
189 | if (!prefix) | 200 | if (!prefix) |
190 | prefix = xstrdup(cgit_repobasename(ctx.repo->url)); | 201 | prefix = xstrdup(cgit_repobasename(ctx.repo->url)); |
191 | 202 | ||
192 | make_snapshot(f, hex, prefix, filename); | 203 | make_snapshot(f, hex, prefix, filename); |
193 | free(prefix); | 204 | free(prefix); |
194 | } | 205 | } |
@@ -1,259 +1,262 @@ | |||
1 | /* ui-tree.c: functions for tree output | 1 | /* ui-tree.c: functions for tree output |
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 <ctype.h> | 9 | #include <ctype.h> |
10 | #include "cgit.h" | 10 | #include "cgit.h" |
11 | #include "html.h" | 11 | #include "html.h" |
12 | #include "ui-shared.h" | 12 | #include "ui-shared.h" |
13 | 13 | ||
14 | char *curr_rev; | 14 | char *curr_rev; |
15 | char *match_path; | 15 | char *match_path; |
16 | int header = 0; | 16 | int header = 0; |
17 | 17 | ||
18 | static void print_text_buffer(char *buf, unsigned long size) | 18 | static void print_text_buffer(char *buf, unsigned long size) |
19 | { | 19 | { |
20 | unsigned long lineno, idx; | 20 | unsigned long lineno, idx; |
21 | const char *numberfmt = | 21 | const char *numberfmt = |
22 | "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; | 22 | "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; |
23 | 23 | ||
24 | html("<table summary='blob content' class='blob'>\n"); | 24 | html("<table summary='blob content' class='blob'>\n"); |
25 | html("<tr><td class='linenumbers'><pre>"); | 25 | html("<tr><td class='linenumbers'><pre>"); |
26 | idx = 0; | 26 | idx = 0; |
27 | lineno = 0; | 27 | lineno = 0; |
28 | htmlf(numberfmt, ++lineno); | 28 | |
29 | while(idx < size - 1) { // skip absolute last newline | 29 | if (size) { |
30 | if (buf[idx] == '\n') | 30 | htmlf(numberfmt, ++lineno); |
31 | htmlf(numberfmt, ++lineno); | 31 | while(idx < size - 1) { // skip absolute last newline |
32 | idx++; | 32 | if (buf[idx] == '\n') |
33 | htmlf(numberfmt, ++lineno); | ||
34 | idx++; | ||
35 | } | ||
33 | } | 36 | } |
34 | html("</pre></td>\n"); | 37 | html("</pre></td>\n"); |
35 | html("<td class='lines'><pre><code>"); | 38 | html("<td class='lines'><pre><code>"); |
36 | html_txt(buf); | 39 | html_txt(buf); |
37 | html("</code></pre></td></tr></table>\n"); | 40 | html("</code></pre></td></tr></table>\n"); |
38 | } | 41 | } |
39 | 42 | ||
40 | #define ROWLEN 32 | 43 | #define ROWLEN 32 |
41 | 44 | ||
42 | static void print_binary_buffer(char *buf, unsigned long size) | 45 | static void print_binary_buffer(char *buf, unsigned long size) |
43 | { | 46 | { |
44 | unsigned long ofs, idx; | 47 | unsigned long ofs, idx; |
45 | static char ascii[ROWLEN + 1]; | 48 | static char ascii[ROWLEN + 1]; |
46 | 49 | ||
47 | html("<table summary='blob content' class='bin-blob'>\n"); | 50 | html("<table summary='blob content' class='bin-blob'>\n"); |
48 | html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); | 51 | html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); |
49 | for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { | 52 | for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { |
50 | htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); | 53 | htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); |
51 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) | 54 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) |
52 | htmlf("%*s%02x", | 55 | htmlf("%*s%02x", |
53 | idx == 16 ? 4 : 1, "", | 56 | idx == 16 ? 4 : 1, "", |
54 | buf[idx] & 0xff); | 57 | buf[idx] & 0xff); |
55 | html(" </td><td class='hex'>"); | 58 | html(" </td><td class='hex'>"); |
56 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) | 59 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) |
57 | ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; | 60 | ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; |
58 | ascii[idx] = '\0'; | 61 | ascii[idx] = '\0'; |
59 | html_txt(ascii); | 62 | html_txt(ascii); |
60 | html("</td></tr>\n"); | 63 | html("</td></tr>\n"); |
61 | } | 64 | } |
62 | html("</table>\n"); | 65 | html("</table>\n"); |
63 | } | 66 | } |
64 | 67 | ||
65 | static void print_object(const unsigned char *sha1, char *path) | 68 | static void print_object(const unsigned char *sha1, char *path) |
66 | { | 69 | { |
67 | enum object_type type; | 70 | enum object_type type; |
68 | char *buf; | 71 | char *buf; |
69 | unsigned long size; | 72 | unsigned long size; |
70 | 73 | ||
71 | type = sha1_object_info(sha1, &size); | 74 | type = sha1_object_info(sha1, &size); |
72 | if (type == OBJ_BAD) { | 75 | if (type == OBJ_BAD) { |
73 | cgit_print_error(fmt("Bad object name: %s", | 76 | cgit_print_error(fmt("Bad object name: %s", |
74 | sha1_to_hex(sha1))); | 77 | sha1_to_hex(sha1))); |
75 | return; | 78 | return; |
76 | } | 79 | } |
77 | 80 | ||
78 | buf = read_sha1_file(sha1, &type, &size); | 81 | buf = read_sha1_file(sha1, &type, &size); |
79 | if (!buf) { | 82 | if (!buf) { |
80 | cgit_print_error(fmt("Error reading object %s", | 83 | cgit_print_error(fmt("Error reading object %s", |
81 | sha1_to_hex(sha1))); | 84 | sha1_to_hex(sha1))); |
82 | return; | 85 | return; |
83 | } | 86 | } |
84 | 87 | ||
85 | html(" ("); | 88 | html(" ("); |
86 | cgit_plain_link("plain", NULL, NULL, ctx.qry.head, | 89 | cgit_plain_link("plain", NULL, NULL, ctx.qry.head, |
87 | curr_rev, path); | 90 | curr_rev, path); |
88 | htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); | 91 | htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); |
89 | 92 | ||
90 | if (buffer_is_binary(buf, size)) | 93 | if (buffer_is_binary(buf, size)) |
91 | print_binary_buffer(buf, size); | 94 | print_binary_buffer(buf, size); |
92 | else | 95 | else |
93 | print_text_buffer(buf, size); | 96 | print_text_buffer(buf, size); |
94 | } | 97 | } |
95 | 98 | ||
96 | 99 | ||
97 | static int ls_item(const unsigned char *sha1, const char *base, int baselen, | 100 | static int ls_item(const unsigned char *sha1, const char *base, int baselen, |
98 | const char *pathname, unsigned int mode, int stage, | 101 | const char *pathname, unsigned int mode, int stage, |
99 | void *cbdata) | 102 | void *cbdata) |
100 | { | 103 | { |
101 | char *name; | 104 | char *name; |
102 | char *fullpath; | 105 | char *fullpath; |
103 | enum object_type type; | 106 | enum object_type type; |
104 | unsigned long size = 0; | 107 | unsigned long size = 0; |
105 | 108 | ||
106 | name = xstrdup(pathname); | 109 | name = xstrdup(pathname); |
107 | fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", | 110 | fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", |
108 | ctx.qry.path ? "/" : "", name); | 111 | ctx.qry.path ? "/" : "", name); |
109 | 112 | ||
110 | if (!S_ISGITLINK(mode)) { | 113 | if (!S_ISGITLINK(mode)) { |
111 | type = sha1_object_info(sha1, &size); | 114 | type = sha1_object_info(sha1, &size); |
112 | if (type == OBJ_BAD) { | 115 | if (type == OBJ_BAD) { |
113 | htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", | 116 | htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", |
114 | name, | 117 | name, |
115 | sha1_to_hex(sha1)); | 118 | sha1_to_hex(sha1)); |
116 | return 0; | 119 | return 0; |
117 | } | 120 | } |
118 | } | 121 | } |
119 | 122 | ||
120 | html("<tr><td class='ls-mode'>"); | 123 | html("<tr><td class='ls-mode'>"); |
121 | cgit_print_filemode(mode); | 124 | cgit_print_filemode(mode); |
122 | html("</td><td>"); | 125 | html("</td><td>"); |
123 | if (S_ISGITLINK(mode)) { | 126 | if (S_ISGITLINK(mode)) { |
124 | htmlf("<a class='ls-mod' href='"); | 127 | htmlf("<a class='ls-mod' href='"); |
125 | html_attr(fmt(ctx.repo->module_link, | 128 | html_attr(fmt(ctx.repo->module_link, |
126 | name, | 129 | name, |
127 | sha1_to_hex(sha1))); | 130 | sha1_to_hex(sha1))); |
128 | html("'>"); | 131 | html("'>"); |
129 | html_txt(name); | 132 | html_txt(name); |
130 | html("</a>"); | 133 | html("</a>"); |
131 | } else if (S_ISDIR(mode)) { | 134 | } else if (S_ISDIR(mode)) { |
132 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, | 135 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, |
133 | curr_rev, fullpath); | 136 | curr_rev, fullpath); |
134 | } else { | 137 | } else { |
135 | cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, | 138 | cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, |
136 | curr_rev, fullpath); | 139 | curr_rev, fullpath); |
137 | } | 140 | } |
138 | htmlf("</td><td class='ls-size'>%li</td>", size); | 141 | htmlf("</td><td class='ls-size'>%li</td>", size); |
139 | 142 | ||
140 | html("<td>"); | 143 | html("<td>"); |
141 | cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, | 144 | cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, |
142 | fullpath, 0, NULL, NULL, ctx.qry.showmsg); | 145 | fullpath, 0, NULL, NULL, ctx.qry.showmsg); |
143 | if (ctx.repo->max_stats) | 146 | if (ctx.repo->max_stats) |
144 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, | 147 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, |
145 | fullpath); | 148 | fullpath); |
146 | html("</td></tr>\n"); | 149 | html("</td></tr>\n"); |
147 | free(name); | 150 | free(name); |
148 | return 0; | 151 | return 0; |
149 | } | 152 | } |
150 | 153 | ||
151 | static void ls_head() | 154 | static void ls_head() |
152 | { | 155 | { |
153 | html("<table summary='tree listing' class='list'>\n"); | 156 | html("<table summary='tree listing' class='list'>\n"); |
154 | html("<tr class='nohover'>"); | 157 | html("<tr class='nohover'>"); |
155 | html("<th class='left'>Mode</th>"); | 158 | html("<th class='left'>Mode</th>"); |
156 | html("<th class='left'>Name</th>"); | 159 | html("<th class='left'>Name</th>"); |
157 | html("<th class='right'>Size</th>"); | 160 | html("<th class='right'>Size</th>"); |
158 | html("<th/>"); | 161 | html("<th/>"); |
159 | html("</tr>\n"); | 162 | html("</tr>\n"); |
160 | header = 1; | 163 | header = 1; |
161 | } | 164 | } |
162 | 165 | ||
163 | static void ls_tail() | 166 | static void ls_tail() |
164 | { | 167 | { |
165 | if (!header) | 168 | if (!header) |
166 | return; | 169 | return; |
167 | html("</table>\n"); | 170 | html("</table>\n"); |
168 | header = 0; | 171 | header = 0; |
169 | } | 172 | } |
170 | 173 | ||
171 | static void ls_tree(const unsigned char *sha1, char *path) | 174 | static void ls_tree(const unsigned char *sha1, char *path) |
172 | { | 175 | { |
173 | struct tree *tree; | 176 | struct tree *tree; |
174 | 177 | ||
175 | tree = parse_tree_indirect(sha1); | 178 | tree = parse_tree_indirect(sha1); |
176 | if (!tree) { | 179 | if (!tree) { |
177 | cgit_print_error(fmt("Not a tree object: %s", | 180 | cgit_print_error(fmt("Not a tree object: %s", |
178 | sha1_to_hex(sha1))); | 181 | sha1_to_hex(sha1))); |
179 | return; | 182 | return; |
180 | } | 183 | } |
181 | 184 | ||
182 | ls_head(); | 185 | ls_head(); |
183 | read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); | 186 | read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); |
184 | ls_tail(); | 187 | ls_tail(); |
185 | } | 188 | } |
186 | 189 | ||
187 | 190 | ||
188 | static int walk_tree(const unsigned char *sha1, const char *base, int baselen, | 191 | static int walk_tree(const unsigned char *sha1, const char *base, int baselen, |
189 | const char *pathname, unsigned mode, int stage, | 192 | const char *pathname, unsigned mode, int stage, |
190 | void *cbdata) | 193 | void *cbdata) |
191 | { | 194 | { |
192 | static int state; | 195 | static int state; |
193 | static char buffer[PATH_MAX]; | 196 | static char buffer[PATH_MAX]; |
194 | char *url; | 197 | char *url; |
195 | 198 | ||
196 | if (state == 0) { | 199 | if (state == 0) { |
197 | memcpy(buffer, base, baselen); | 200 | memcpy(buffer, base, baselen); |
198 | strcpy(buffer+baselen, pathname); | 201 | strcpy(buffer+baselen, pathname); |
199 | url = cgit_pageurl(ctx.qry.repo, "tree", | 202 | url = cgit_pageurl(ctx.qry.repo, "tree", |
200 | fmt("h=%s&path=%s", curr_rev, buffer)); | 203 | fmt("h=%s&path=%s", curr_rev, buffer)); |
201 | html("/"); | 204 | html("/"); |
202 | cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, | 205 | cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, |
203 | curr_rev, buffer); | 206 | curr_rev, buffer); |
204 | 207 | ||
205 | if (strcmp(match_path, buffer)) | 208 | if (strcmp(match_path, buffer)) |
206 | return READ_TREE_RECURSIVE; | 209 | return READ_TREE_RECURSIVE; |
207 | 210 | ||
208 | if (S_ISDIR(mode)) { | 211 | if (S_ISDIR(mode)) { |
209 | state = 1; | 212 | state = 1; |
210 | ls_head(); | 213 | ls_head(); |
211 | return READ_TREE_RECURSIVE; | 214 | return READ_TREE_RECURSIVE; |
212 | } else { | 215 | } else { |
213 | print_object(sha1, buffer); | 216 | print_object(sha1, buffer); |
214 | return 0; | 217 | return 0; |
215 | } | 218 | } |
216 | } | 219 | } |
217 | ls_item(sha1, base, baselen, pathname, mode, stage, NULL); | 220 | ls_item(sha1, base, baselen, pathname, mode, stage, NULL); |
218 | return 0; | 221 | return 0; |
219 | } | 222 | } |
220 | 223 | ||
221 | 224 | ||
222 | /* | 225 | /* |
223 | * Show a tree or a blob | 226 | * Show a tree or a blob |
224 | * rev: the commit pointing at the root tree object | 227 | * rev: the commit pointing at the root tree object |
225 | * path: path to tree or blob | 228 | * path: path to tree or blob |
226 | */ | 229 | */ |
227 | void cgit_print_tree(const char *rev, char *path) | 230 | void cgit_print_tree(const char *rev, char *path) |
228 | { | 231 | { |
229 | unsigned char sha1[20]; | 232 | unsigned char sha1[20]; |
230 | struct commit *commit; | 233 | struct commit *commit; |
231 | const char *paths[] = {path, NULL}; | 234 | const char *paths[] = {path, NULL}; |
232 | 235 | ||
233 | if (!rev) | 236 | if (!rev) |
234 | rev = ctx.qry.head; | 237 | rev = ctx.qry.head; |
235 | 238 | ||
236 | curr_rev = xstrdup(rev); | 239 | curr_rev = xstrdup(rev); |
237 | if (get_sha1(rev, sha1)) { | 240 | if (get_sha1(rev, sha1)) { |
238 | cgit_print_error(fmt("Invalid revision name: %s", rev)); | 241 | cgit_print_error(fmt("Invalid revision name: %s", rev)); |
239 | return; | 242 | return; |
240 | } | 243 | } |
241 | commit = lookup_commit_reference(sha1); | 244 | commit = lookup_commit_reference(sha1); |
242 | if (!commit || parse_commit(commit)) { | 245 | if (!commit || parse_commit(commit)) { |
243 | cgit_print_error(fmt("Invalid commit reference: %s", rev)); | 246 | cgit_print_error(fmt("Invalid commit reference: %s", rev)); |
244 | return; | 247 | return; |
245 | } | 248 | } |
246 | 249 | ||
247 | html("path: <a href='"); | 250 | html("path: <a href='"); |
248 | html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev))); | 251 | html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev))); |
249 | html("'>root</a>"); | 252 | html("'>root</a>"); |
250 | 253 | ||
251 | if (path == NULL) { | 254 | if (path == NULL) { |
252 | ls_tree(commit->tree->object.sha1, NULL); | 255 | ls_tree(commit->tree->object.sha1, NULL); |
253 | return; | 256 | return; |
254 | } | 257 | } |
255 | 258 | ||
256 | match_path = path; | 259 | match_path = path; |
257 | read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); | 260 | read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); |
258 | ls_tail(); | 261 | ls_tail(); |
259 | } | 262 | } |