author | Lars Hjemli <hjemli@gmail.com> | 2009-07-31 14:55:27 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2009-07-31 15:41:21 (UTC) |
commit | 46b7abed99e957008c01c02cf612aa526ba92f04 (patch) (unidiff) | |
tree | 2cdbfe5281c2ffa645def488d53db0a8b82842f4 | |
parent | 18dfbdc099c1398016427b6fa7f1a1facb363998 (diff) | |
download | cgit-46b7abed99e957008c01c02cf612aa526ba92f04.zip cgit-46b7abed99e957008c01c02cf612aa526ba92f04.tar.gz cgit-46b7abed99e957008c01c02cf612aa526ba92f04.tar.bz2 |
ui-tree: add support for source-filter option
This new option is used to specify an external command which will be
executed when displaying blob content in the tree view. Blob content
will be written to STDIN of the filter and STDOUT from the filter
will be included verbatim in the html output from cgit. The file name
of the blob will be passed as the only argument to the filter command.
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
-rw-r--r-- | cgit.c | 2 | ||||
-rw-r--r-- | cgit.h | 1 | ||||
-rw-r--r-- | cgitrc.5.txt | 8 | ||||
-rw-r--r-- | ui-tree.c | 18 |
4 files changed, 25 insertions, 4 deletions
@@ -39,128 +39,130 @@ void config_cb(const char *name, const char *value) | |||
39 | else if (!strcmp(name, "root-desc")) | 39 | else if (!strcmp(name, "root-desc")) |
40 | ctx.cfg.root_desc = xstrdup(value); | 40 | ctx.cfg.root_desc = xstrdup(value); |
41 | else if (!strcmp(name, "root-readme")) | 41 | else if (!strcmp(name, "root-readme")) |
42 | ctx.cfg.root_readme = xstrdup(value); | 42 | ctx.cfg.root_readme = xstrdup(value); |
43 | else if (!strcmp(name, "css")) | 43 | else if (!strcmp(name, "css")) |
44 | ctx.cfg.css = xstrdup(value); | 44 | ctx.cfg.css = xstrdup(value); |
45 | else if (!strcmp(name, "favicon")) | 45 | else if (!strcmp(name, "favicon")) |
46 | ctx.cfg.favicon = xstrdup(value); | 46 | ctx.cfg.favicon = xstrdup(value); |
47 | else if (!strcmp(name, "footer")) | 47 | else if (!strcmp(name, "footer")) |
48 | ctx.cfg.footer = xstrdup(value); | 48 | ctx.cfg.footer = xstrdup(value); |
49 | else if (!strcmp(name, "head-include")) | 49 | else if (!strcmp(name, "head-include")) |
50 | ctx.cfg.head_include = xstrdup(value); | 50 | ctx.cfg.head_include = xstrdup(value); |
51 | else if (!strcmp(name, "header")) | 51 | else if (!strcmp(name, "header")) |
52 | ctx.cfg.header = xstrdup(value); | 52 | ctx.cfg.header = xstrdup(value); |
53 | else if (!strcmp(name, "logo")) | 53 | else if (!strcmp(name, "logo")) |
54 | ctx.cfg.logo = xstrdup(value); | 54 | ctx.cfg.logo = xstrdup(value); |
55 | else if (!strcmp(name, "index-header")) | 55 | else if (!strcmp(name, "index-header")) |
56 | ctx.cfg.index_header = xstrdup(value); | 56 | ctx.cfg.index_header = xstrdup(value); |
57 | else if (!strcmp(name, "index-info")) | 57 | else if (!strcmp(name, "index-info")) |
58 | ctx.cfg.index_info = xstrdup(value); | 58 | ctx.cfg.index_info = xstrdup(value); |
59 | else if (!strcmp(name, "logo-link")) | 59 | else if (!strcmp(name, "logo-link")) |
60 | ctx.cfg.logo_link = xstrdup(value); | 60 | ctx.cfg.logo_link = xstrdup(value); |
61 | else if (!strcmp(name, "module-link")) | 61 | else if (!strcmp(name, "module-link")) |
62 | ctx.cfg.module_link = xstrdup(value); | 62 | ctx.cfg.module_link = xstrdup(value); |
63 | else if (!strcmp(name, "virtual-root")) { | 63 | else if (!strcmp(name, "virtual-root")) { |
64 | ctx.cfg.virtual_root = trim_end(value, '/'); | 64 | ctx.cfg.virtual_root = trim_end(value, '/'); |
65 | if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) | 65 | if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) |
66 | ctx.cfg.virtual_root = ""; | 66 | ctx.cfg.virtual_root = ""; |
67 | } else if (!strcmp(name, "nocache")) | 67 | } else if (!strcmp(name, "nocache")) |
68 | ctx.cfg.nocache = atoi(value); | 68 | ctx.cfg.nocache = atoi(value); |
69 | else if (!strcmp(name, "noheader")) | 69 | else if (!strcmp(name, "noheader")) |
70 | ctx.cfg.noheader = atoi(value); | 70 | ctx.cfg.noheader = atoi(value); |
71 | else if (!strcmp(name, "snapshots")) | 71 | else if (!strcmp(name, "snapshots")) |
72 | ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); | 72 | ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); |
73 | else if (!strcmp(name, "enable-index-links")) | 73 | else if (!strcmp(name, "enable-index-links")) |
74 | ctx.cfg.enable_index_links = atoi(value); | 74 | ctx.cfg.enable_index_links = atoi(value); |
75 | else if (!strcmp(name, "enable-log-filecount")) | 75 | else if (!strcmp(name, "enable-log-filecount")) |
76 | ctx.cfg.enable_log_filecount = atoi(value); | 76 | ctx.cfg.enable_log_filecount = atoi(value); |
77 | else if (!strcmp(name, "enable-log-linecount")) | 77 | else if (!strcmp(name, "enable-log-linecount")) |
78 | ctx.cfg.enable_log_linecount = atoi(value); | 78 | ctx.cfg.enable_log_linecount = atoi(value); |
79 | else if (!strcmp(name, "max-stats")) | 79 | else if (!strcmp(name, "max-stats")) |
80 | ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); | 80 | ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); |
81 | else if (!strcmp(name, "cache-size")) | 81 | else if (!strcmp(name, "cache-size")) |
82 | ctx.cfg.cache_size = atoi(value); | 82 | ctx.cfg.cache_size = atoi(value); |
83 | else if (!strcmp(name, "cache-root")) | 83 | else if (!strcmp(name, "cache-root")) |
84 | ctx.cfg.cache_root = xstrdup(value); | 84 | ctx.cfg.cache_root = xstrdup(value); |
85 | else if (!strcmp(name, "cache-root-ttl")) | 85 | else if (!strcmp(name, "cache-root-ttl")) |
86 | ctx.cfg.cache_root_ttl = atoi(value); | 86 | ctx.cfg.cache_root_ttl = atoi(value); |
87 | else if (!strcmp(name, "cache-repo-ttl")) | 87 | else if (!strcmp(name, "cache-repo-ttl")) |
88 | ctx.cfg.cache_repo_ttl = atoi(value); | 88 | ctx.cfg.cache_repo_ttl = atoi(value); |
89 | else if (!strcmp(name, "cache-static-ttl")) | 89 | else if (!strcmp(name, "cache-static-ttl")) |
90 | ctx.cfg.cache_static_ttl = atoi(value); | 90 | ctx.cfg.cache_static_ttl = atoi(value); |
91 | else if (!strcmp(name, "cache-dynamic-ttl")) | 91 | else if (!strcmp(name, "cache-dynamic-ttl")) |
92 | ctx.cfg.cache_dynamic_ttl = atoi(value); | 92 | ctx.cfg.cache_dynamic_ttl = atoi(value); |
93 | else if (!strcmp(name, "embedded")) | 93 | else if (!strcmp(name, "embedded")) |
94 | ctx.cfg.embedded = atoi(value); | 94 | ctx.cfg.embedded = atoi(value); |
95 | else if (!strcmp(name, "max-message-length")) | 95 | else if (!strcmp(name, "max-message-length")) |
96 | ctx.cfg.max_msg_len = atoi(value); | 96 | ctx.cfg.max_msg_len = atoi(value); |
97 | else if (!strcmp(name, "max-repodesc-length")) | 97 | else if (!strcmp(name, "max-repodesc-length")) |
98 | ctx.cfg.max_repodesc_len = atoi(value); | 98 | ctx.cfg.max_repodesc_len = atoi(value); |
99 | else if (!strcmp(name, "max-repo-count")) | 99 | else if (!strcmp(name, "max-repo-count")) |
100 | ctx.cfg.max_repo_count = atoi(value); | 100 | ctx.cfg.max_repo_count = atoi(value); |
101 | else if (!strcmp(name, "max-commit-count")) | 101 | else if (!strcmp(name, "max-commit-count")) |
102 | ctx.cfg.max_commit_count = atoi(value); | 102 | ctx.cfg.max_commit_count = atoi(value); |
103 | else if (!strcmp(name, "source-filter")) | ||
104 | ctx.cfg.source_filter = new_filter(value, 1); | ||
103 | else if (!strcmp(name, "summary-log")) | 105 | else if (!strcmp(name, "summary-log")) |
104 | ctx.cfg.summary_log = atoi(value); | 106 | ctx.cfg.summary_log = atoi(value); |
105 | else if (!strcmp(name, "summary-branches")) | 107 | else if (!strcmp(name, "summary-branches")) |
106 | ctx.cfg.summary_branches = atoi(value); | 108 | ctx.cfg.summary_branches = atoi(value); |
107 | else if (!strcmp(name, "summary-tags")) | 109 | else if (!strcmp(name, "summary-tags")) |
108 | ctx.cfg.summary_tags = atoi(value); | 110 | ctx.cfg.summary_tags = atoi(value); |
109 | else if (!strcmp(name, "agefile")) | 111 | else if (!strcmp(name, "agefile")) |
110 | ctx.cfg.agefile = xstrdup(value); | 112 | ctx.cfg.agefile = xstrdup(value); |
111 | else if (!strcmp(name, "renamelimit")) | 113 | else if (!strcmp(name, "renamelimit")) |
112 | ctx.cfg.renamelimit = atoi(value); | 114 | ctx.cfg.renamelimit = atoi(value); |
113 | else if (!strcmp(name, "robots")) | 115 | else if (!strcmp(name, "robots")) |
114 | ctx.cfg.robots = xstrdup(value); | 116 | ctx.cfg.robots = xstrdup(value); |
115 | else if (!strcmp(name, "clone-prefix")) | 117 | else if (!strcmp(name, "clone-prefix")) |
116 | ctx.cfg.clone_prefix = xstrdup(value); | 118 | ctx.cfg.clone_prefix = xstrdup(value); |
117 | else if (!strcmp(name, "local-time")) | 119 | else if (!strcmp(name, "local-time")) |
118 | ctx.cfg.local_time = atoi(value); | 120 | ctx.cfg.local_time = atoi(value); |
119 | else if (!strcmp(name, "repo.group")) | 121 | else if (!strcmp(name, "repo.group")) |
120 | ctx.cfg.repo_group = xstrdup(value); | 122 | ctx.cfg.repo_group = xstrdup(value); |
121 | else if (!strcmp(name, "repo.url")) | 123 | else if (!strcmp(name, "repo.url")) |
122 | ctx.repo = cgit_add_repo(value); | 124 | ctx.repo = cgit_add_repo(value); |
123 | else if (!strcmp(name, "repo.name")) | 125 | else if (!strcmp(name, "repo.name")) |
124 | ctx.repo->name = xstrdup(value); | 126 | ctx.repo->name = xstrdup(value); |
125 | else if (ctx.repo && !strcmp(name, "repo.path")) | 127 | else if (ctx.repo && !strcmp(name, "repo.path")) |
126 | ctx.repo->path = trim_end(value, '/'); | 128 | ctx.repo->path = trim_end(value, '/'); |
127 | else if (ctx.repo && !strcmp(name, "repo.clone-url")) | 129 | else if (ctx.repo && !strcmp(name, "repo.clone-url")) |
128 | ctx.repo->clone_url = xstrdup(value); | 130 | ctx.repo->clone_url = xstrdup(value); |
129 | else if (ctx.repo && !strcmp(name, "repo.desc")) | 131 | else if (ctx.repo && !strcmp(name, "repo.desc")) |
130 | ctx.repo->desc = xstrdup(value); | 132 | ctx.repo->desc = xstrdup(value); |
131 | else if (ctx.repo && !strcmp(name, "repo.owner")) | 133 | else if (ctx.repo && !strcmp(name, "repo.owner")) |
132 | ctx.repo->owner = xstrdup(value); | 134 | ctx.repo->owner = xstrdup(value); |
133 | else if (ctx.repo && !strcmp(name, "repo.defbranch")) | 135 | else if (ctx.repo && !strcmp(name, "repo.defbranch")) |
134 | ctx.repo->defbranch = xstrdup(value); | 136 | ctx.repo->defbranch = xstrdup(value); |
135 | else if (ctx.repo && !strcmp(name, "repo.snapshots")) | 137 | else if (ctx.repo && !strcmp(name, "repo.snapshots")) |
136 | ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ | 138 | ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ |
137 | else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) | 139 | else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) |
138 | ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); | 140 | ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); |
139 | else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) | 141 | else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) |
140 | ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); | 142 | ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); |
141 | else if (ctx.repo && !strcmp(name, "repo.max-stats")) | 143 | else if (ctx.repo && !strcmp(name, "repo.max-stats")) |
142 | ctx.repo->max_stats = cgit_find_stats_period(value, NULL); | 144 | ctx.repo->max_stats = cgit_find_stats_period(value, NULL); |
143 | else if (ctx.repo && !strcmp(name, "repo.module-link")) | 145 | else if (ctx.repo && !strcmp(name, "repo.module-link")) |
144 | ctx.repo->module_link= xstrdup(value); | 146 | ctx.repo->module_link= xstrdup(value); |
145 | else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { | 147 | else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { |
146 | if (*value == '/') | 148 | if (*value == '/') |
147 | ctx.repo->readme = xstrdup(value); | 149 | ctx.repo->readme = xstrdup(value); |
148 | else | 150 | else |
149 | ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); | 151 | ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); |
150 | } else if (!strcmp(name, "include")) | 152 | } else if (!strcmp(name, "include")) |
151 | parse_configfile(value, config_cb); | 153 | parse_configfile(value, config_cb); |
152 | } | 154 | } |
153 | 155 | ||
154 | static void querystring_cb(const char *name, const char *value) | 156 | static void querystring_cb(const char *name, const char *value) |
155 | { | 157 | { |
156 | if (!strcmp(name,"r")) { | 158 | if (!strcmp(name,"r")) { |
157 | ctx.qry.repo = xstrdup(value); | 159 | ctx.qry.repo = xstrdup(value); |
158 | ctx.repo = cgit_get_repoinfo(value); | 160 | ctx.repo = cgit_get_repoinfo(value); |
159 | } else if (!strcmp(name, "p")) { | 161 | } else if (!strcmp(name, "p")) { |
160 | ctx.qry.page = xstrdup(value); | 162 | ctx.qry.page = xstrdup(value); |
161 | } else if (!strcmp(name, "url")) { | 163 | } else if (!strcmp(name, "url")) { |
162 | ctx.qry.url = xstrdup(value); | 164 | ctx.qry.url = xstrdup(value); |
163 | cgit_parse_url(value); | 165 | cgit_parse_url(value); |
164 | } else if (!strcmp(name, "qt")) { | 166 | } else if (!strcmp(name, "qt")) { |
165 | ctx.qry.grep = xstrdup(value); | 167 | ctx.qry.grep = xstrdup(value); |
166 | } else if (!strcmp(name, "q")) { | 168 | } else if (!strcmp(name, "q")) { |
@@ -122,128 +122,129 @@ struct cgit_query { | |||
122 | char *name; | 122 | char *name; |
123 | char *mimetype; | 123 | char *mimetype; |
124 | char *url; | 124 | char *url; |
125 | char *period; | 125 | char *period; |
126 | int ofs; | 126 | int ofs; |
127 | int nohead; | 127 | int nohead; |
128 | char *sort; | 128 | char *sort; |
129 | int showmsg; | 129 | int showmsg; |
130 | }; | 130 | }; |
131 | 131 | ||
132 | struct cgit_filter { | 132 | struct cgit_filter { |
133 | char *cmd; | 133 | char *cmd; |
134 | char **argv; | 134 | char **argv; |
135 | int old_stdout; | 135 | int old_stdout; |
136 | int pipe_fh[2]; | 136 | int pipe_fh[2]; |
137 | int pid; | 137 | int pid; |
138 | int exitstatus; | 138 | int exitstatus; |
139 | }; | 139 | }; |
140 | 140 | ||
141 | struct cgit_config { | 141 | struct cgit_config { |
142 | char *agefile; | 142 | char *agefile; |
143 | char *cache_root; | 143 | char *cache_root; |
144 | char *clone_prefix; | 144 | char *clone_prefix; |
145 | char *css; | 145 | char *css; |
146 | char *favicon; | 146 | char *favicon; |
147 | char *footer; | 147 | char *footer; |
148 | char *head_include; | 148 | char *head_include; |
149 | char *header; | 149 | char *header; |
150 | char *index_header; | 150 | char *index_header; |
151 | char *index_info; | 151 | char *index_info; |
152 | char *logo; | 152 | char *logo; |
153 | char *logo_link; | 153 | char *logo_link; |
154 | char *module_link; | 154 | char *module_link; |
155 | char *repo_group; | 155 | char *repo_group; |
156 | char *robots; | 156 | char *robots; |
157 | char *root_title; | 157 | char *root_title; |
158 | char *root_desc; | 158 | char *root_desc; |
159 | char *root_readme; | 159 | char *root_readme; |
160 | char *script_name; | 160 | char *script_name; |
161 | char *virtual_root; | 161 | char *virtual_root; |
162 | int cache_size; | 162 | int cache_size; |
163 | int cache_dynamic_ttl; | 163 | int cache_dynamic_ttl; |
164 | int cache_max_create_time; | 164 | int cache_max_create_time; |
165 | int cache_repo_ttl; | 165 | int cache_repo_ttl; |
166 | int cache_root_ttl; | 166 | int cache_root_ttl; |
167 | int cache_static_ttl; | 167 | int cache_static_ttl; |
168 | int embedded; | 168 | int embedded; |
169 | int enable_index_links; | 169 | int enable_index_links; |
170 | int enable_log_filecount; | 170 | int enable_log_filecount; |
171 | int enable_log_linecount; | 171 | int enable_log_linecount; |
172 | int local_time; | 172 | int local_time; |
173 | int max_repo_count; | 173 | int max_repo_count; |
174 | int max_commit_count; | 174 | int max_commit_count; |
175 | int max_lock_attempts; | 175 | int max_lock_attempts; |
176 | int max_msg_len; | 176 | int max_msg_len; |
177 | int max_repodesc_len; | 177 | int max_repodesc_len; |
178 | int max_stats; | 178 | int max_stats; |
179 | int nocache; | 179 | int nocache; |
180 | int noheader; | 180 | int noheader; |
181 | int renamelimit; | 181 | int renamelimit; |
182 | int snapshots; | 182 | int snapshots; |
183 | int summary_branches; | 183 | int summary_branches; |
184 | int summary_log; | 184 | int summary_log; |
185 | int summary_tags; | 185 | int summary_tags; |
186 | struct cgit_filter *source_filter; | ||
186 | }; | 187 | }; |
187 | 188 | ||
188 | struct cgit_page { | 189 | struct cgit_page { |
189 | time_t modified; | 190 | time_t modified; |
190 | time_t expires; | 191 | time_t expires; |
191 | size_t size; | 192 | size_t size; |
192 | char *mimetype; | 193 | char *mimetype; |
193 | char *charset; | 194 | char *charset; |
194 | char *filename; | 195 | char *filename; |
195 | char *etag; | 196 | char *etag; |
196 | char *title; | 197 | char *title; |
197 | int status; | 198 | int status; |
198 | char *statusmsg; | 199 | char *statusmsg; |
199 | }; | 200 | }; |
200 | 201 | ||
201 | struct cgit_context { | 202 | struct cgit_context { |
202 | struct cgit_query qry; | 203 | struct cgit_query qry; |
203 | struct cgit_config cfg; | 204 | struct cgit_config cfg; |
204 | struct cgit_repo *repo; | 205 | struct cgit_repo *repo; |
205 | struct cgit_page page; | 206 | struct cgit_page page; |
206 | }; | 207 | }; |
207 | 208 | ||
208 | struct cgit_snapshot_format { | 209 | struct cgit_snapshot_format { |
209 | const char *suffix; | 210 | const char *suffix; |
210 | const char *mimetype; | 211 | const char *mimetype; |
211 | write_archive_fn_t write_func; | 212 | write_archive_fn_t write_func; |
212 | int bit; | 213 | int bit; |
213 | }; | 214 | }; |
214 | 215 | ||
215 | extern const char *cgit_version; | 216 | extern const char *cgit_version; |
216 | 217 | ||
217 | extern struct cgit_repolist cgit_repolist; | 218 | extern struct cgit_repolist cgit_repolist; |
218 | extern struct cgit_context ctx; | 219 | extern struct cgit_context ctx; |
219 | extern const struct cgit_snapshot_format cgit_snapshot_formats[]; | 220 | extern const struct cgit_snapshot_format cgit_snapshot_formats[]; |
220 | 221 | ||
221 | extern struct cgit_repo *cgit_add_repo(const char *url); | 222 | extern struct cgit_repo *cgit_add_repo(const char *url); |
222 | extern struct cgit_repo *cgit_get_repoinfo(const char *url); | 223 | extern struct cgit_repo *cgit_get_repoinfo(const char *url); |
223 | extern void cgit_repo_config_cb(const char *name, const char *value); | 224 | extern void cgit_repo_config_cb(const char *name, const char *value); |
224 | 225 | ||
225 | extern int chk_zero(int result, char *msg); | 226 | extern int chk_zero(int result, char *msg); |
226 | extern int chk_positive(int result, char *msg); | 227 | extern int chk_positive(int result, char *msg); |
227 | extern int chk_non_negative(int result, char *msg); | 228 | extern int chk_non_negative(int result, char *msg); |
228 | 229 | ||
229 | extern char *trim_end(const char *str, char c); | 230 | extern char *trim_end(const char *str, char c); |
230 | extern char *strlpart(char *txt, int maxlen); | 231 | extern char *strlpart(char *txt, int maxlen); |
231 | extern char *strrpart(char *txt, int maxlen); | 232 | extern char *strrpart(char *txt, int maxlen); |
232 | 233 | ||
233 | extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); | 234 | extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); |
234 | extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, | 235 | extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, |
235 | int flags, void *cb_data); | 236 | int flags, void *cb_data); |
236 | 237 | ||
237 | extern void *cgit_free_commitinfo(struct commitinfo *info); | 238 | extern void *cgit_free_commitinfo(struct commitinfo *info); |
238 | 239 | ||
239 | extern int cgit_diff_files(const unsigned char *old_sha1, | 240 | extern int cgit_diff_files(const unsigned char *old_sha1, |
240 | const unsigned char *new_sha1, | 241 | const unsigned char *new_sha1, |
241 | unsigned long *old_size, unsigned long *new_size, | 242 | unsigned long *old_size, unsigned long *new_size, |
242 | int *binary, linediff_fn fn); | 243 | int *binary, linediff_fn fn); |
243 | 244 | ||
244 | extern void cgit_diff_tree(const unsigned char *old_sha1, | 245 | extern void cgit_diff_tree(const unsigned char *old_sha1, |
245 | const unsigned char *new_sha1, | 246 | const unsigned char *new_sha1, |
246 | filepair_fn fn, const char *prefix); | 247 | filepair_fn fn, const char *prefix); |
247 | 248 | ||
248 | extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); | 249 | extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); |
249 | 250 | ||
diff --git a/cgitrc.5.txt b/cgitrc.5.txt index a207fe0..d420ad4 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt | |||
@@ -137,128 +137,136 @@ max-message-length:: | |||
137 | max-repo-count:: | 137 | max-repo-count:: |
138 | Specifies the number of entries to list per page on therepository | 138 | Specifies the number of entries to list per page on therepository |
139 | index page. Default value: "50". | 139 | index page. Default value: "50". |
140 | 140 | ||
141 | max-repodesc-length:: | 141 | max-repodesc-length:: |
142 | Specifies the maximum number of repo description characters to display | 142 | Specifies the maximum number of repo description characters to display |
143 | on the repository index page. Default value: "80". | 143 | on the repository index page. Default value: "80". |
144 | 144 | ||
145 | max-stats:: | 145 | max-stats:: |
146 | Set the default maximum statistics period. Valid values are "week", | 146 | Set the default maximum statistics period. Valid values are "week", |
147 | "month", "quarter" and "year". If unspecified, statistics are | 147 | "month", "quarter" and "year". If unspecified, statistics are |
148 | disabled. Default value: none. See also: "repo.max-stats". | 148 | disabled. Default value: none. See also: "repo.max-stats". |
149 | 149 | ||
150 | module-link:: | 150 | module-link:: |
151 | Text which will be used as the formatstring for a hyperlink when a | 151 | Text which will be used as the formatstring for a hyperlink when a |
152 | submodule is printed in a directory listing. The arguments for the | 152 | submodule is printed in a directory listing. The arguments for the |
153 | formatstring are the path and SHA1 of the submodule commit. Default | 153 | formatstring are the path and SHA1 of the submodule commit. Default |
154 | value: "./?repo=%s&page=commit&id=%s" | 154 | value: "./?repo=%s&page=commit&id=%s" |
155 | 155 | ||
156 | nocache:: | 156 | nocache:: |
157 | If set to the value "1" caching will be disabled. This settings is | 157 | If set to the value "1" caching will be disabled. This settings is |
158 | deprecated, and will not be honored starting with cgit-1.0. Default | 158 | deprecated, and will not be honored starting with cgit-1.0. Default |
159 | value: "0". | 159 | value: "0". |
160 | 160 | ||
161 | noheader:: | 161 | noheader:: |
162 | Flag which, when set to "1", will make cgit omit the standard header | 162 | Flag which, when set to "1", will make cgit omit the standard header |
163 | on all pages. Default value: none. See also: "embedded". | 163 | on all pages. Default value: none. See also: "embedded". |
164 | 164 | ||
165 | renamelimit:: | 165 | renamelimit:: |
166 | Maximum number of files to consider when detecting renames. The value | 166 | Maximum number of files to consider when detecting renames. The value |
167 | "-1" uses the compiletime value in git (for further info, look at | 167 | "-1" uses the compiletime value in git (for further info, look at |
168 | `man git-diff`). Default value: "-1". | 168 | `man git-diff`). Default value: "-1". |
169 | 169 | ||
170 | repo.group:: | 170 | repo.group:: |
171 | A value for the current repository group, which all repositories | 171 | A value for the current repository group, which all repositories |
172 | specified after this setting will inherit. Default value: none. | 172 | specified after this setting will inherit. Default value: none. |
173 | 173 | ||
174 | robots:: | 174 | robots:: |
175 | Text used as content for the "robots" meta-tag. Default value: | 175 | Text used as content for the "robots" meta-tag. Default value: |
176 | "index, nofollow". | 176 | "index, nofollow". |
177 | 177 | ||
178 | root-desc:: | 178 | root-desc:: |
179 | Text printed below the heading on the repository index page. Default | 179 | Text printed below the heading on the repository index page. Default |
180 | value: "a fast webinterface for the git dscm". | 180 | value: "a fast webinterface for the git dscm". |
181 | 181 | ||
182 | root-readme:: | 182 | root-readme:: |
183 | The content of the file specified with this option will be included | 183 | The content of the file specified with this option will be included |
184 | verbatim below the "about" link on the repository index page. Default | 184 | verbatim below the "about" link on the repository index page. Default |
185 | value: none. | 185 | value: none. |
186 | 186 | ||
187 | root-title:: | 187 | root-title:: |
188 | Text printed as heading on the repository index page. Default value: | 188 | Text printed as heading on the repository index page. Default value: |
189 | "Git Repository Browser". | 189 | "Git Repository Browser". |
190 | 190 | ||
191 | snapshots:: | 191 | snapshots:: |
192 | Text which specifies the default (and allowed) set of snapshot formats | 192 | Text which specifies the default (and allowed) set of snapshot formats |
193 | supported by cgit. The value is a space-separated list of zero or more | 193 | supported by cgit. The value is a space-separated list of zero or more |
194 | of the following values: | 194 | of the following values: |
195 | "tar" uncompressed tar-file | 195 | "tar" uncompressed tar-file |
196 | "tar.gz"gzip-compressed tar-file | 196 | "tar.gz"gzip-compressed tar-file |
197 | "tar.bz2"bzip-compressed tar-file | 197 | "tar.bz2"bzip-compressed tar-file |
198 | "zip" zip-file | 198 | "zip" zip-file |
199 | Default value: none. | 199 | Default value: none. |
200 | 200 | ||
201 | source-filter:: | ||
202 | Specifies a command which will be invoked to format plaintext blobs | ||
203 | in the tree view. The command will get the blob content on its STDIN | ||
204 | and the name of the blob as its only command line argument. The STDOUT | ||
205 | from the command will be included verbatim as the blob contents, i.e. | ||
206 | this can be used to implement e.g. syntax highlighting. Default value: | ||
207 | none. | ||
208 | |||
201 | summary-branches:: | 209 | summary-branches:: |
202 | Specifies the number of branches to display in the repository "summary" | 210 | Specifies the number of branches to display in the repository "summary" |
203 | view. Default value: "10". | 211 | view. Default value: "10". |
204 | 212 | ||
205 | summary-log:: | 213 | summary-log:: |
206 | Specifies the number of log entries to display in the repository | 214 | Specifies the number of log entries to display in the repository |
207 | "summary" view. Default value: "10". | 215 | "summary" view. Default value: "10". |
208 | 216 | ||
209 | summary-tags:: | 217 | summary-tags:: |
210 | Specifies the number of tags to display in the repository "summary" | 218 | Specifies the number of tags to display in the repository "summary" |
211 | view. Default value: "10". | 219 | view. Default value: "10". |
212 | 220 | ||
213 | virtual-root:: | 221 | virtual-root:: |
214 | Url which, if specified, will be used as root for all cgit links. It | 222 | Url which, if specified, will be used as root for all cgit links. It |
215 | will also cause cgit to generate 'virtual urls', i.e. urls like | 223 | will also cause cgit to generate 'virtual urls', i.e. urls like |
216 | '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default | 224 | '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default |
217 | value: none. | 225 | value: none. |
218 | NOTE: cgit has recently learned how to use PATH_INFO to achieve the | 226 | NOTE: cgit has recently learned how to use PATH_INFO to achieve the |
219 | same kind of virtual urls, so this option will probably be deprecated. | 227 | same kind of virtual urls, so this option will probably be deprecated. |
220 | 228 | ||
221 | REPOSITORY SETTINGS | 229 | REPOSITORY SETTINGS |
222 | ------------------- | 230 | ------------------- |
223 | repo.clone-url:: | 231 | repo.clone-url:: |
224 | A list of space-separated urls which can be used to clone this repo. | 232 | A list of space-separated urls which can be used to clone this repo. |
225 | Default value: none. | 233 | Default value: none. |
226 | 234 | ||
227 | repo.defbranch:: | 235 | repo.defbranch:: |
228 | The name of the default branch for this repository. If no such branch | 236 | The name of the default branch for this repository. If no such branch |
229 | exists in the repository, the first branch name (when sorted) is used | 237 | exists in the repository, the first branch name (when sorted) is used |
230 | as default instead. Default value: "master". | 238 | as default instead. Default value: "master". |
231 | 239 | ||
232 | repo.desc:: | 240 | repo.desc:: |
233 | The value to show as repository description. Default value: none. | 241 | The value to show as repository description. Default value: none. |
234 | 242 | ||
235 | repo.enable-log-filecount:: | 243 | repo.enable-log-filecount:: |
236 | A flag which can be used to disable the global setting | 244 | A flag which can be used to disable the global setting |
237 | `enable-log-filecount'. Default value: none. | 245 | `enable-log-filecount'. Default value: none. |
238 | 246 | ||
239 | repo.enable-log-linecount:: | 247 | repo.enable-log-linecount:: |
240 | A flag which can be used to disable the global setting | 248 | A flag which can be used to disable the global setting |
241 | `enable-log-linecount'. Default value: none. | 249 | `enable-log-linecount'. Default value: none. |
242 | 250 | ||
243 | repo.max-stats:: | 251 | repo.max-stats:: |
244 | Override the default maximum statistics period. Valid values are equal | 252 | Override the default maximum statistics period. Valid values are equal |
245 | to the values specified for the global "max-stats" setting. Default | 253 | to the values specified for the global "max-stats" setting. Default |
246 | value: none. | 254 | value: none. |
247 | 255 | ||
248 | repo.name:: | 256 | repo.name:: |
249 | The value to show as repository name. Default value: <repo.url>. | 257 | The value to show as repository name. Default value: <repo.url>. |
250 | 258 | ||
251 | repo.owner:: | 259 | repo.owner:: |
252 | A value used to identify the owner of the repository. Default value: | 260 | A value used to identify the owner of the repository. Default value: |
253 | none. | 261 | none. |
254 | 262 | ||
255 | repo.path:: | 263 | repo.path:: |
256 | An absolute path to the repository directory. For non-bare repositories | 264 | An absolute path to the repository directory. For non-bare repositories |
257 | this is the .git-directory. Default value: none. | 265 | this is the .git-directory. Default value: none. |
258 | 266 | ||
259 | repo.readme:: | 267 | repo.readme:: |
260 | A path (relative to <repo.path>) which specifies a file to include | 268 | A path (relative to <repo.path>) which specifies a file to include |
261 | verbatim as the "About" page for this repo. Default value: none. | 269 | verbatim as the "About" page for this repo. Default value: none. |
262 | 270 | ||
263 | repo.snapshots:: | 271 | repo.snapshots:: |
264 | A mask of allowed snapshot-formats for this repo, restricted by the | 272 | A mask of allowed snapshot-formats for this repo, restricted by the |
@@ -1,262 +1,272 @@ | |||
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(const char *name, 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 | if (ctx.cfg.source_filter) { | ||
26 | html("<tr><td class='lines'><pre><code>"); | ||
27 | ctx.cfg.source_filter->argv[1] = xstrdup(name); | ||
28 | cgit_open_filter(ctx.cfg.source_filter); | ||
29 | write(STDOUT_FILENO, buf, size); | ||
30 | cgit_close_filter(ctx.cfg.source_filter); | ||
31 | html("</code></pre></td></tr></table>\n"); | ||
32 | return; | ||
33 | } | ||
34 | |||
25 | html("<tr><td class='linenumbers'><pre>"); | 35 | html("<tr><td class='linenumbers'><pre>"); |
26 | idx = 0; | 36 | idx = 0; |
27 | lineno = 0; | 37 | lineno = 0; |
28 | 38 | ||
29 | if (size) { | 39 | if (size) { |
30 | htmlf(numberfmt, ++lineno); | 40 | htmlf(numberfmt, ++lineno); |
31 | while(idx < size - 1) { // skip absolute last newline | 41 | while(idx < size - 1) { // skip absolute last newline |
32 | if (buf[idx] == '\n') | 42 | if (buf[idx] == '\n') |
33 | htmlf(numberfmt, ++lineno); | 43 | htmlf(numberfmt, ++lineno); |
34 | idx++; | 44 | idx++; |
35 | } | 45 | } |
36 | } | 46 | } |
37 | html("</pre></td>\n"); | 47 | html("</pre></td>\n"); |
38 | html("<td class='lines'><pre><code>"); | 48 | html("<td class='lines'><pre><code>"); |
39 | html_txt(buf); | 49 | html_txt(buf); |
40 | html("</code></pre></td></tr></table>\n"); | 50 | html("</code></pre></td></tr></table>\n"); |
41 | } | 51 | } |
42 | 52 | ||
43 | #define ROWLEN 32 | 53 | #define ROWLEN 32 |
44 | 54 | ||
45 | static void print_binary_buffer(char *buf, unsigned long size) | 55 | static void print_binary_buffer(char *buf, unsigned long size) |
46 | { | 56 | { |
47 | unsigned long ofs, idx; | 57 | unsigned long ofs, idx; |
48 | static char ascii[ROWLEN + 1]; | 58 | static char ascii[ROWLEN + 1]; |
49 | 59 | ||
50 | html("<table summary='blob content' class='bin-blob'>\n"); | 60 | html("<table summary='blob content' class='bin-blob'>\n"); |
51 | html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); | 61 | html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); |
52 | for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { | 62 | for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { |
53 | htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); | 63 | htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); |
54 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) | 64 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) |
55 | htmlf("%*s%02x", | 65 | htmlf("%*s%02x", |
56 | idx == 16 ? 4 : 1, "", | 66 | idx == 16 ? 4 : 1, "", |
57 | buf[idx] & 0xff); | 67 | buf[idx] & 0xff); |
58 | html(" </td><td class='hex'>"); | 68 | html(" </td><td class='hex'>"); |
59 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) | 69 | for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) |
60 | ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; | 70 | ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; |
61 | ascii[idx] = '\0'; | 71 | ascii[idx] = '\0'; |
62 | html_txt(ascii); | 72 | html_txt(ascii); |
63 | html("</td></tr>\n"); | 73 | html("</td></tr>\n"); |
64 | } | 74 | } |
65 | html("</table>\n"); | 75 | html("</table>\n"); |
66 | } | 76 | } |
67 | 77 | ||
68 | static void print_object(const unsigned char *sha1, char *path) | 78 | static void print_object(const unsigned char *sha1, char *path, const char *basename) |
69 | { | 79 | { |
70 | enum object_type type; | 80 | enum object_type type; |
71 | char *buf; | 81 | char *buf; |
72 | unsigned long size; | 82 | unsigned long size; |
73 | 83 | ||
74 | type = sha1_object_info(sha1, &size); | 84 | type = sha1_object_info(sha1, &size); |
75 | if (type == OBJ_BAD) { | 85 | if (type == OBJ_BAD) { |
76 | cgit_print_error(fmt("Bad object name: %s", | 86 | cgit_print_error(fmt("Bad object name: %s", |
77 | sha1_to_hex(sha1))); | 87 | sha1_to_hex(sha1))); |
78 | return; | 88 | return; |
79 | } | 89 | } |
80 | 90 | ||
81 | buf = read_sha1_file(sha1, &type, &size); | 91 | buf = read_sha1_file(sha1, &type, &size); |
82 | if (!buf) { | 92 | if (!buf) { |
83 | cgit_print_error(fmt("Error reading object %s", | 93 | cgit_print_error(fmt("Error reading object %s", |
84 | sha1_to_hex(sha1))); | 94 | sha1_to_hex(sha1))); |
85 | return; | 95 | return; |
86 | } | 96 | } |
87 | 97 | ||
88 | html(" ("); | 98 | html(" ("); |
89 | cgit_plain_link("plain", NULL, NULL, ctx.qry.head, | 99 | cgit_plain_link("plain", NULL, NULL, ctx.qry.head, |
90 | curr_rev, path); | 100 | curr_rev, path); |
91 | htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); | 101 | htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); |
92 | 102 | ||
93 | if (buffer_is_binary(buf, size)) | 103 | if (buffer_is_binary(buf, size)) |
94 | print_binary_buffer(buf, size); | 104 | print_binary_buffer(buf, size); |
95 | else | 105 | else |
96 | print_text_buffer(buf, size); | 106 | print_text_buffer(basename, buf, size); |
97 | } | 107 | } |
98 | 108 | ||
99 | 109 | ||
100 | static int ls_item(const unsigned char *sha1, const char *base, int baselen, | 110 | static int ls_item(const unsigned char *sha1, const char *base, int baselen, |
101 | const char *pathname, unsigned int mode, int stage, | 111 | const char *pathname, unsigned int mode, int stage, |
102 | void *cbdata) | 112 | void *cbdata) |
103 | { | 113 | { |
104 | char *name; | 114 | char *name; |
105 | char *fullpath; | 115 | char *fullpath; |
106 | enum object_type type; | 116 | enum object_type type; |
107 | unsigned long size = 0; | 117 | unsigned long size = 0; |
108 | 118 | ||
109 | name = xstrdup(pathname); | 119 | name = xstrdup(pathname); |
110 | fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", | 120 | fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", |
111 | ctx.qry.path ? "/" : "", name); | 121 | ctx.qry.path ? "/" : "", name); |
112 | 122 | ||
113 | if (!S_ISGITLINK(mode)) { | 123 | if (!S_ISGITLINK(mode)) { |
114 | type = sha1_object_info(sha1, &size); | 124 | type = sha1_object_info(sha1, &size); |
115 | if (type == OBJ_BAD) { | 125 | if (type == OBJ_BAD) { |
116 | htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", | 126 | htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", |
117 | name, | 127 | name, |
118 | sha1_to_hex(sha1)); | 128 | sha1_to_hex(sha1)); |
119 | return 0; | 129 | return 0; |
120 | } | 130 | } |
121 | } | 131 | } |
122 | 132 | ||
123 | html("<tr><td class='ls-mode'>"); | 133 | html("<tr><td class='ls-mode'>"); |
124 | cgit_print_filemode(mode); | 134 | cgit_print_filemode(mode); |
125 | html("</td><td>"); | 135 | html("</td><td>"); |
126 | if (S_ISGITLINK(mode)) { | 136 | if (S_ISGITLINK(mode)) { |
127 | htmlf("<a class='ls-mod' href='"); | 137 | htmlf("<a class='ls-mod' href='"); |
128 | html_attr(fmt(ctx.repo->module_link, | 138 | html_attr(fmt(ctx.repo->module_link, |
129 | name, | 139 | name, |
130 | sha1_to_hex(sha1))); | 140 | sha1_to_hex(sha1))); |
131 | html("'>"); | 141 | html("'>"); |
132 | html_txt(name); | 142 | html_txt(name); |
133 | html("</a>"); | 143 | html("</a>"); |
134 | } else if (S_ISDIR(mode)) { | 144 | } else if (S_ISDIR(mode)) { |
135 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, | 145 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, |
136 | curr_rev, fullpath); | 146 | curr_rev, fullpath); |
137 | } else { | 147 | } else { |
138 | cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, | 148 | cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, |
139 | curr_rev, fullpath); | 149 | curr_rev, fullpath); |
140 | } | 150 | } |
141 | htmlf("</td><td class='ls-size'>%li</td>", size); | 151 | htmlf("</td><td class='ls-size'>%li</td>", size); |
142 | 152 | ||
143 | html("<td>"); | 153 | html("<td>"); |
144 | cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, | 154 | cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, |
145 | fullpath, 0, NULL, NULL, ctx.qry.showmsg); | 155 | fullpath, 0, NULL, NULL, ctx.qry.showmsg); |
146 | if (ctx.repo->max_stats) | 156 | if (ctx.repo->max_stats) |
147 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, | 157 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, |
148 | fullpath); | 158 | fullpath); |
149 | html("</td></tr>\n"); | 159 | html("</td></tr>\n"); |
150 | free(name); | 160 | free(name); |
151 | return 0; | 161 | return 0; |
152 | } | 162 | } |
153 | 163 | ||
154 | static void ls_head() | 164 | static void ls_head() |
155 | { | 165 | { |
156 | html("<table summary='tree listing' class='list'>\n"); | 166 | html("<table summary='tree listing' class='list'>\n"); |
157 | html("<tr class='nohover'>"); | 167 | html("<tr class='nohover'>"); |
158 | html("<th class='left'>Mode</th>"); | 168 | html("<th class='left'>Mode</th>"); |
159 | html("<th class='left'>Name</th>"); | 169 | html("<th class='left'>Name</th>"); |
160 | html("<th class='right'>Size</th>"); | 170 | html("<th class='right'>Size</th>"); |
161 | html("<th/>"); | 171 | html("<th/>"); |
162 | html("</tr>\n"); | 172 | html("</tr>\n"); |
163 | header = 1; | 173 | header = 1; |
164 | } | 174 | } |
165 | 175 | ||
166 | static void ls_tail() | 176 | static void ls_tail() |
167 | { | 177 | { |
168 | if (!header) | 178 | if (!header) |
169 | return; | 179 | return; |
170 | html("</table>\n"); | 180 | html("</table>\n"); |
171 | header = 0; | 181 | header = 0; |
172 | } | 182 | } |
173 | 183 | ||
174 | static void ls_tree(const unsigned char *sha1, char *path) | 184 | static void ls_tree(const unsigned char *sha1, char *path) |
175 | { | 185 | { |
176 | struct tree *tree; | 186 | struct tree *tree; |
177 | 187 | ||
178 | tree = parse_tree_indirect(sha1); | 188 | tree = parse_tree_indirect(sha1); |
179 | if (!tree) { | 189 | if (!tree) { |
180 | cgit_print_error(fmt("Not a tree object: %s", | 190 | cgit_print_error(fmt("Not a tree object: %s", |
181 | sha1_to_hex(sha1))); | 191 | sha1_to_hex(sha1))); |
182 | return; | 192 | return; |
183 | } | 193 | } |
184 | 194 | ||
185 | ls_head(); | 195 | ls_head(); |
186 | read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); | 196 | read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); |
187 | ls_tail(); | 197 | ls_tail(); |
188 | } | 198 | } |
189 | 199 | ||
190 | 200 | ||
191 | static int walk_tree(const unsigned char *sha1, const char *base, int baselen, | 201 | static int walk_tree(const unsigned char *sha1, const char *base, int baselen, |
192 | const char *pathname, unsigned mode, int stage, | 202 | const char *pathname, unsigned mode, int stage, |
193 | void *cbdata) | 203 | void *cbdata) |
194 | { | 204 | { |
195 | static int state; | 205 | static int state; |
196 | static char buffer[PATH_MAX]; | 206 | static char buffer[PATH_MAX]; |
197 | char *url; | 207 | char *url; |
198 | 208 | ||
199 | if (state == 0) { | 209 | if (state == 0) { |
200 | memcpy(buffer, base, baselen); | 210 | memcpy(buffer, base, baselen); |
201 | strcpy(buffer+baselen, pathname); | 211 | strcpy(buffer+baselen, pathname); |
202 | url = cgit_pageurl(ctx.qry.repo, "tree", | 212 | url = cgit_pageurl(ctx.qry.repo, "tree", |
203 | fmt("h=%s&path=%s", curr_rev, buffer)); | 213 | fmt("h=%s&path=%s", curr_rev, buffer)); |
204 | html("/"); | 214 | html("/"); |
205 | cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, | 215 | cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, |
206 | curr_rev, buffer); | 216 | curr_rev, buffer); |
207 | 217 | ||
208 | if (strcmp(match_path, buffer)) | 218 | if (strcmp(match_path, buffer)) |
209 | return READ_TREE_RECURSIVE; | 219 | return READ_TREE_RECURSIVE; |
210 | 220 | ||
211 | if (S_ISDIR(mode)) { | 221 | if (S_ISDIR(mode)) { |
212 | state = 1; | 222 | state = 1; |
213 | ls_head(); | 223 | ls_head(); |
214 | return READ_TREE_RECURSIVE; | 224 | return READ_TREE_RECURSIVE; |
215 | } else { | 225 | } else { |
216 | print_object(sha1, buffer); | 226 | print_object(sha1, buffer, pathname); |
217 | return 0; | 227 | return 0; |
218 | } | 228 | } |
219 | } | 229 | } |
220 | ls_item(sha1, base, baselen, pathname, mode, stage, NULL); | 230 | ls_item(sha1, base, baselen, pathname, mode, stage, NULL); |
221 | return 0; | 231 | return 0; |
222 | } | 232 | } |
223 | 233 | ||
224 | 234 | ||
225 | /* | 235 | /* |
226 | * Show a tree or a blob | 236 | * Show a tree or a blob |
227 | * rev: the commit pointing at the root tree object | 237 | * rev: the commit pointing at the root tree object |
228 | * path: path to tree or blob | 238 | * path: path to tree or blob |
229 | */ | 239 | */ |
230 | void cgit_print_tree(const char *rev, char *path) | 240 | void cgit_print_tree(const char *rev, char *path) |
231 | { | 241 | { |
232 | unsigned char sha1[20]; | 242 | unsigned char sha1[20]; |
233 | struct commit *commit; | 243 | struct commit *commit; |
234 | const char *paths[] = {path, NULL}; | 244 | const char *paths[] = {path, NULL}; |
235 | 245 | ||
236 | if (!rev) | 246 | if (!rev) |
237 | rev = ctx.qry.head; | 247 | rev = ctx.qry.head; |
238 | 248 | ||
239 | curr_rev = xstrdup(rev); | 249 | curr_rev = xstrdup(rev); |
240 | if (get_sha1(rev, sha1)) { | 250 | if (get_sha1(rev, sha1)) { |
241 | cgit_print_error(fmt("Invalid revision name: %s", rev)); | 251 | cgit_print_error(fmt("Invalid revision name: %s", rev)); |
242 | return; | 252 | return; |
243 | } | 253 | } |
244 | commit = lookup_commit_reference(sha1); | 254 | commit = lookup_commit_reference(sha1); |
245 | if (!commit || parse_commit(commit)) { | 255 | if (!commit || parse_commit(commit)) { |
246 | cgit_print_error(fmt("Invalid commit reference: %s", rev)); | 256 | cgit_print_error(fmt("Invalid commit reference: %s", rev)); |
247 | return; | 257 | return; |
248 | } | 258 | } |
249 | 259 | ||
250 | html("path: <a href='"); | 260 | html("path: <a href='"); |
251 | html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev))); | 261 | html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev))); |
252 | html("'>root</a>"); | 262 | html("'>root</a>"); |
253 | 263 | ||
254 | if (path == NULL) { | 264 | if (path == NULL) { |
255 | ls_tree(commit->tree->object.sha1, NULL); | 265 | ls_tree(commit->tree->object.sha1, NULL); |
256 | return; | 266 | return; |
257 | } | 267 | } |
258 | 268 | ||
259 | match_path = path; | 269 | match_path = path; |
260 | read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); | 270 | read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); |
261 | ls_tail(); | 271 | ls_tail(); |
262 | } | 272 | } |