summaryrefslogtreecommitdiffabout
authorJohan Herland <johan@herland.net>2010-09-30 18:15:14 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2010-11-09 15:37:39 (UTC)
commit1415f3f3e017d0123e850707c55cb7e5e5887406 (patch) (unidiff)
tree9036ea6ce534e40ff35394359332a9a628276c0f
parente0c6f23789e4893781bcd5af2281d468991ccf3a (diff)
downloadcgit-1415f3f3e017d0123e850707c55cb7e5e5887406.zip
cgit-1415f3f3e017d0123e850707c55cb7e5e5887406.tar.gz
cgit-1415f3f3e017d0123e850707c55cb7e5e5887406.tar.bz2
ui-log: Fix filecount/linecount when path limit is in effect
When using ui-log with path limits, the listing of commits enables parent rewriting in Git's internal log machinery. This did not work well together with cgit_diff_commit() which is used to generate the filecount and linecount numbers for each commit in the log view. cgit_diff_commit() would operate without any path limits, and would therefore process the full diff between the commits shown (which, because of parent rewriting, is not the same as processing the diff for the commit itself). Additionally, the bottom commit in the log view would (again, because of parent rewriting) have zero parents, causing us to process the entire diff between the empty tree and that commit. Since path limits were not in effect, this would (in large projects) reports thousands of files and millions of lines changed in that bottom commit. This patch fixes the issue by applying the same path limit to cgit_diff_commit() as is applied to the rest of the log view. The result is that the filecount/linecount now only reflects the diff as it pertains to the given path limit. Signed-off-by: Johan Herland <johan@herland.net> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.h3
-rw-r--r--shared.c4
-rw-r--r--ui-log.c2
3 files changed, 5 insertions, 4 deletions
diff --git a/cgit.h b/cgit.h
index a9896cf..f5f68ac 100644
--- a/cgit.h
+++ b/cgit.h
@@ -169,149 +169,150 @@ struct cgit_config {
169 char *module_link; 169 char *module_link;
170 char *project_list; 170 char *project_list;
171 char *readme; 171 char *readme;
172 char *robots; 172 char *robots;
173 char *root_title; 173 char *root_title;
174 char *root_desc; 174 char *root_desc;
175 char *root_readme; 175 char *root_readme;
176 char *script_name; 176 char *script_name;
177 char *section; 177 char *section;
178 char *virtual_root; 178 char *virtual_root;
179 char *strict_export; 179 char *strict_export;
180 int cache_size; 180 int cache_size;
181 int cache_dynamic_ttl; 181 int cache_dynamic_ttl;
182 int cache_max_create_time; 182 int cache_max_create_time;
183 int cache_repo_ttl; 183 int cache_repo_ttl;
184 int cache_root_ttl; 184 int cache_root_ttl;
185 int cache_scanrc_ttl; 185 int cache_scanrc_ttl;
186 int cache_static_ttl; 186 int cache_static_ttl;
187 int embedded; 187 int embedded;
188 int enable_filter_overrides; 188 int enable_filter_overrides;
189 int enable_gitweb_owner; 189 int enable_gitweb_owner;
190 int enable_index_links; 190 int enable_index_links;
191 int enable_log_filecount; 191 int enable_log_filecount;
192 int enable_log_linecount; 192 int enable_log_linecount;
193 int enable_remote_branches; 193 int enable_remote_branches;
194 int enable_subject_links; 194 int enable_subject_links;
195 int enable_tree_linenumbers; 195 int enable_tree_linenumbers;
196 int local_time; 196 int local_time;
197 int max_atom_items; 197 int max_atom_items;
198 int max_repo_count; 198 int max_repo_count;
199 int max_commit_count; 199 int max_commit_count;
200 int max_lock_attempts; 200 int max_lock_attempts;
201 int max_msg_len; 201 int max_msg_len;
202 int max_repodesc_len; 202 int max_repodesc_len;
203 int max_blob_size; 203 int max_blob_size;
204 int max_stats; 204 int max_stats;
205 int nocache; 205 int nocache;
206 int noplainemail; 206 int noplainemail;
207 int noheader; 207 int noheader;
208 int renamelimit; 208 int renamelimit;
209 int remove_suffix; 209 int remove_suffix;
210 int section_from_path; 210 int section_from_path;
211 int snapshots; 211 int snapshots;
212 int summary_branches; 212 int summary_branches;
213 int summary_log; 213 int summary_log;
214 int summary_tags; 214 int summary_tags;
215 int ssdiff; 215 int ssdiff;
216 struct string_list mimetypes; 216 struct string_list mimetypes;
217 struct cgit_filter *about_filter; 217 struct cgit_filter *about_filter;
218 struct cgit_filter *commit_filter; 218 struct cgit_filter *commit_filter;
219 struct cgit_filter *source_filter; 219 struct cgit_filter *source_filter;
220}; 220};
221 221
222struct cgit_page { 222struct cgit_page {
223 time_t modified; 223 time_t modified;
224 time_t expires; 224 time_t expires;
225 size_t size; 225 size_t size;
226 char *mimetype; 226 char *mimetype;
227 char *charset; 227 char *charset;
228 char *filename; 228 char *filename;
229 char *etag; 229 char *etag;
230 char *title; 230 char *title;
231 int status; 231 int status;
232 char *statusmsg; 232 char *statusmsg;
233}; 233};
234 234
235struct cgit_environment { 235struct cgit_environment {
236 char *cgit_config; 236 char *cgit_config;
237 char *http_host; 237 char *http_host;
238 char *https; 238 char *https;
239 char *no_http; 239 char *no_http;
240 char *path_info; 240 char *path_info;
241 char *query_string; 241 char *query_string;
242 char *request_method; 242 char *request_method;
243 char *script_name; 243 char *script_name;
244 char *server_name; 244 char *server_name;
245 char *server_port; 245 char *server_port;
246}; 246};
247 247
248struct cgit_context { 248struct cgit_context {
249 struct cgit_environment env; 249 struct cgit_environment env;
250 struct cgit_query qry; 250 struct cgit_query qry;
251 struct cgit_config cfg; 251 struct cgit_config cfg;
252 struct cgit_repo *repo; 252 struct cgit_repo *repo;
253 struct cgit_page page; 253 struct cgit_page page;
254}; 254};
255 255
256struct cgit_snapshot_format { 256struct cgit_snapshot_format {
257 const char *suffix; 257 const char *suffix;
258 const char *mimetype; 258 const char *mimetype;
259 write_archive_fn_t write_func; 259 write_archive_fn_t write_func;
260 int bit; 260 int bit;
261}; 261};
262 262
263extern const char *cgit_version; 263extern const char *cgit_version;
264 264
265extern struct cgit_repolist cgit_repolist; 265extern struct cgit_repolist cgit_repolist;
266extern struct cgit_context ctx; 266extern struct cgit_context ctx;
267extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 267extern const struct cgit_snapshot_format cgit_snapshot_formats[];
268 268
269extern struct cgit_repo *cgit_add_repo(const char *url); 269extern struct cgit_repo *cgit_add_repo(const char *url);
270extern struct cgit_repo *cgit_get_repoinfo(const char *url); 270extern struct cgit_repo *cgit_get_repoinfo(const char *url);
271extern void cgit_repo_config_cb(const char *name, const char *value); 271extern void cgit_repo_config_cb(const char *name, const char *value);
272 272
273extern int chk_zero(int result, char *msg); 273extern int chk_zero(int result, char *msg);
274extern int chk_positive(int result, char *msg); 274extern int chk_positive(int result, char *msg);
275extern int chk_non_negative(int result, char *msg); 275extern int chk_non_negative(int result, char *msg);
276 276
277extern char *trim_end(const char *str, char c); 277extern char *trim_end(const char *str, char c);
278extern char *strlpart(char *txt, int maxlen); 278extern char *strlpart(char *txt, int maxlen);
279extern char *strrpart(char *txt, int maxlen); 279extern char *strrpart(char *txt, int maxlen);
280 280
281extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 281extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
282extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 282extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
283 int flags, void *cb_data); 283 int flags, void *cb_data);
284 284
285extern void *cgit_free_commitinfo(struct commitinfo *info); 285extern void *cgit_free_commitinfo(struct commitinfo *info);
286 286
287extern int cgit_diff_files(const unsigned char *old_sha1, 287extern int cgit_diff_files(const unsigned char *old_sha1,
288 const unsigned char *new_sha1, 288 const unsigned char *new_sha1,
289 unsigned long *old_size, unsigned long *new_size, 289 unsigned long *old_size, unsigned long *new_size,
290 int *binary, int context, int ignorews, 290 int *binary, int context, int ignorews,
291 linediff_fn fn); 291 linediff_fn fn);
292 292
293extern void cgit_diff_tree(const unsigned char *old_sha1, 293extern void cgit_diff_tree(const unsigned char *old_sha1,
294 const unsigned char *new_sha1, 294 const unsigned char *new_sha1,
295 filepair_fn fn, const char *prefix, int ignorews); 295 filepair_fn fn, const char *prefix, int ignorews);
296 296
297extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 297extern void cgit_diff_commit(struct commit *commit, filepair_fn fn,
298 const char *prefix);
298 299
299__attribute__((format (printf,1,2))) 300__attribute__((format (printf,1,2)))
300extern char *fmt(const char *format,...); 301extern char *fmt(const char *format,...);
301 302
302extern struct commitinfo *cgit_parse_commit(struct commit *commit); 303extern struct commitinfo *cgit_parse_commit(struct commit *commit);
303extern struct taginfo *cgit_parse_tag(struct tag *tag); 304extern struct taginfo *cgit_parse_tag(struct tag *tag);
304extern void cgit_parse_url(const char *url); 305extern void cgit_parse_url(const char *url);
305 306
306extern const char *cgit_repobasename(const char *reponame); 307extern const char *cgit_repobasename(const char *reponame);
307 308
308extern int cgit_parse_snapshots_mask(const char *str); 309extern int cgit_parse_snapshots_mask(const char *str);
309 310
310extern int cgit_open_filter(struct cgit_filter *filter); 311extern int cgit_open_filter(struct cgit_filter *filter);
311extern int cgit_close_filter(struct cgit_filter *filter); 312extern int cgit_close_filter(struct cgit_filter *filter);
312 313
313extern int readfile(const char *path, char **buf, size_t *size); 314extern int readfile(const char *path, char **buf, size_t *size);
314 315
315extern char *expand_macros(const char *txt); 316extern char *expand_macros(const char *txt);
316 317
317#endif /* CGIT_H */ 318#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index 72ac140..765cd27 100644
--- a/shared.c
+++ b/shared.c
@@ -213,263 +213,263 @@ static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
213 file->ptr = read_sha1_file(sha1, &type, 213 file->ptr = read_sha1_file(sha1, &type,
214 (unsigned long *)&file->size); 214 (unsigned long *)&file->size);
215 } 215 }
216 return 1; 216 return 1;
217} 217}
218 218
219/* 219/*
220 * Receive diff-buffers from xdiff and concatenate them as 220 * Receive diff-buffers from xdiff and concatenate them as
221 * needed across multiple callbacks. 221 * needed across multiple callbacks.
222 * 222 *
223 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 223 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
224 * ripped from git and modified to use globals instead of 224 * ripped from git and modified to use globals instead of
225 * a special callback-struct. 225 * a special callback-struct.
226 */ 226 */
227char *diffbuf = NULL; 227char *diffbuf = NULL;
228int buflen = 0; 228int buflen = 0;
229 229
230int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 230int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
231{ 231{
232 int i; 232 int i;
233 233
234 for (i = 0; i < nbuf; i++) { 234 for (i = 0; i < nbuf; i++) {
235 if (mb[i].ptr[mb[i].size-1] != '\n') { 235 if (mb[i].ptr[mb[i].size-1] != '\n') {
236 /* Incomplete line */ 236 /* Incomplete line */
237 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 237 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
238 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 238 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
239 buflen += mb[i].size; 239 buflen += mb[i].size;
240 continue; 240 continue;
241 } 241 }
242 242
243 /* we have a complete line */ 243 /* we have a complete line */
244 if (!diffbuf) { 244 if (!diffbuf) {
245 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 245 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
246 continue; 246 continue;
247 } 247 }
248 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 248 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
249 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 249 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
250 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 250 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
251 free(diffbuf); 251 free(diffbuf);
252 diffbuf = NULL; 252 diffbuf = NULL;
253 buflen = 0; 253 buflen = 0;
254 } 254 }
255 if (diffbuf) { 255 if (diffbuf) {
256 ((linediff_fn)priv)(diffbuf, buflen); 256 ((linediff_fn)priv)(diffbuf, buflen);
257 free(diffbuf); 257 free(diffbuf);
258 diffbuf = NULL; 258 diffbuf = NULL;
259 buflen = 0; 259 buflen = 0;
260 } 260 }
261 return 0; 261 return 0;
262} 262}
263 263
264int cgit_diff_files(const unsigned char *old_sha1, 264int cgit_diff_files(const unsigned char *old_sha1,
265 const unsigned char *new_sha1, unsigned long *old_size, 265 const unsigned char *new_sha1, unsigned long *old_size,
266 unsigned long *new_size, int *binary, int context, 266 unsigned long *new_size, int *binary, int context,
267 int ignorews, linediff_fn fn) 267 int ignorews, linediff_fn fn)
268{ 268{
269 mmfile_t file1, file2; 269 mmfile_t file1, file2;
270 xpparam_t diff_params; 270 xpparam_t diff_params;
271 xdemitconf_t emit_params; 271 xdemitconf_t emit_params;
272 xdemitcb_t emit_cb; 272 xdemitcb_t emit_cb;
273 273
274 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 274 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
275 return 1; 275 return 1;
276 276
277 *old_size = file1.size; 277 *old_size = file1.size;
278 *new_size = file2.size; 278 *new_size = file2.size;
279 279
280 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || 280 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
281 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { 281 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
282 *binary = 1; 282 *binary = 1;
283 if (file1.size) 283 if (file1.size)
284 free(file1.ptr); 284 free(file1.ptr);
285 if (file2.size) 285 if (file2.size)
286 free(file2.ptr); 286 free(file2.ptr);
287 return 0; 287 return 0;
288 } 288 }
289 289
290 memset(&diff_params, 0, sizeof(diff_params)); 290 memset(&diff_params, 0, sizeof(diff_params));
291 memset(&emit_params, 0, sizeof(emit_params)); 291 memset(&emit_params, 0, sizeof(emit_params));
292 memset(&emit_cb, 0, sizeof(emit_cb)); 292 memset(&emit_cb, 0, sizeof(emit_cb));
293 diff_params.flags = XDF_NEED_MINIMAL; 293 diff_params.flags = XDF_NEED_MINIMAL;
294 if (ignorews) 294 if (ignorews)
295 diff_params.flags |= XDF_IGNORE_WHITESPACE; 295 diff_params.flags |= XDF_IGNORE_WHITESPACE;
296 emit_params.ctxlen = context > 0 ? context : 3; 296 emit_params.ctxlen = context > 0 ? context : 3;
297 emit_params.flags = XDL_EMIT_FUNCNAMES; 297 emit_params.flags = XDL_EMIT_FUNCNAMES;
298 emit_cb.outf = filediff_cb; 298 emit_cb.outf = filediff_cb;
299 emit_cb.priv = fn; 299 emit_cb.priv = fn;
300 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 300 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
301 if (file1.size) 301 if (file1.size)
302 free(file1.ptr); 302 free(file1.ptr);
303 if (file2.size) 303 if (file2.size)
304 free(file2.ptr); 304 free(file2.ptr);
305 return 0; 305 return 0;
306} 306}
307 307
308void cgit_diff_tree(const unsigned char *old_sha1, 308void cgit_diff_tree(const unsigned char *old_sha1,
309 const unsigned char *new_sha1, 309 const unsigned char *new_sha1,
310 filepair_fn fn, const char *prefix, int ignorews) 310 filepair_fn fn, const char *prefix, int ignorews)
311{ 311{
312 struct diff_options opt; 312 struct diff_options opt;
313 int ret; 313 int ret;
314 int prefixlen; 314 int prefixlen;
315 315
316 diff_setup(&opt); 316 diff_setup(&opt);
317 opt.output_format = DIFF_FORMAT_CALLBACK; 317 opt.output_format = DIFF_FORMAT_CALLBACK;
318 opt.detect_rename = 1; 318 opt.detect_rename = 1;
319 opt.rename_limit = ctx.cfg.renamelimit; 319 opt.rename_limit = ctx.cfg.renamelimit;
320 DIFF_OPT_SET(&opt, RECURSIVE); 320 DIFF_OPT_SET(&opt, RECURSIVE);
321 if (ignorews) 321 if (ignorews)
322 DIFF_XDL_SET(&opt, IGNORE_WHITESPACE); 322 DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
323 opt.format_callback = cgit_diff_tree_cb; 323 opt.format_callback = cgit_diff_tree_cb;
324 opt.format_callback_data = fn; 324 opt.format_callback_data = fn;
325 if (prefix) { 325 if (prefix) {
326 opt.nr_paths = 1; 326 opt.nr_paths = 1;
327 opt.paths = &prefix; 327 opt.paths = &prefix;
328 prefixlen = strlen(prefix); 328 prefixlen = strlen(prefix);
329 opt.pathlens = &prefixlen; 329 opt.pathlens = &prefixlen;
330 } 330 }
331 diff_setup_done(&opt); 331 diff_setup_done(&opt);
332 332
333 if (old_sha1 && !is_null_sha1(old_sha1)) 333 if (old_sha1 && !is_null_sha1(old_sha1))
334 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 334 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
335 else 335 else
336 ret = diff_root_tree_sha1(new_sha1, "", &opt); 336 ret = diff_root_tree_sha1(new_sha1, "", &opt);
337 diffcore_std(&opt); 337 diffcore_std(&opt);
338 diff_flush(&opt); 338 diff_flush(&opt);
339} 339}
340 340
341void cgit_diff_commit(struct commit *commit, filepair_fn fn) 341void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix)
342{ 342{
343 unsigned char *old_sha1 = NULL; 343 unsigned char *old_sha1 = NULL;
344 344
345 if (commit->parents) 345 if (commit->parents)
346 old_sha1 = commit->parents->item->object.sha1; 346 old_sha1 = commit->parents->item->object.sha1;
347 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL, 347 cgit_diff_tree(old_sha1, commit->object.sha1, fn, prefix,
348 ctx.qry.ignorews); 348 ctx.qry.ignorews);
349} 349}
350 350
351int cgit_parse_snapshots_mask(const char *str) 351int cgit_parse_snapshots_mask(const char *str)
352{ 352{
353 const struct cgit_snapshot_format *f; 353 const struct cgit_snapshot_format *f;
354 static const char *delim = " \t,:/|;"; 354 static const char *delim = " \t,:/|;";
355 int tl, sl, rv = 0; 355 int tl, sl, rv = 0;
356 356
357 /* favor legacy setting */ 357 /* favor legacy setting */
358 if(atoi(str)) 358 if(atoi(str))
359 return 1; 359 return 1;
360 for(;;) { 360 for(;;) {
361 str += strspn(str,delim); 361 str += strspn(str,delim);
362 tl = strcspn(str,delim); 362 tl = strcspn(str,delim);
363 if (!tl) 363 if (!tl)
364 break; 364 break;
365 for (f = cgit_snapshot_formats; f->suffix; f++) { 365 for (f = cgit_snapshot_formats; f->suffix; f++) {
366 sl = strlen(f->suffix); 366 sl = strlen(f->suffix);
367 if((tl == sl && !strncmp(f->suffix, str, tl)) || 367 if((tl == sl && !strncmp(f->suffix, str, tl)) ||
368 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { 368 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) {
369 rv |= f->bit; 369 rv |= f->bit;
370 break; 370 break;
371 } 371 }
372 } 372 }
373 str += tl; 373 str += tl;
374 } 374 }
375 return rv; 375 return rv;
376} 376}
377 377
378int cgit_open_filter(struct cgit_filter *filter) 378int cgit_open_filter(struct cgit_filter *filter)
379{ 379{
380 380
381 filter->old_stdout = chk_positive(dup(STDOUT_FILENO), 381 filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
382 "Unable to duplicate STDOUT"); 382 "Unable to duplicate STDOUT");
383 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess"); 383 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
384 filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); 384 filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
385 if (filter->pid == 0) { 385 if (filter->pid == 0) {
386 close(filter->pipe_fh[1]); 386 close(filter->pipe_fh[1]);
387 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO), 387 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
388 "Unable to use pipe as STDIN"); 388 "Unable to use pipe as STDIN");
389 execvp(filter->cmd, filter->argv); 389 execvp(filter->cmd, filter->argv);
390 die("Unable to exec subprocess %s: %s (%d)", filter->cmd, 390 die("Unable to exec subprocess %s: %s (%d)", filter->cmd,
391 strerror(errno), errno); 391 strerror(errno), errno);
392 } 392 }
393 close(filter->pipe_fh[0]); 393 close(filter->pipe_fh[0]);
394 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO), 394 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
395 "Unable to use pipe as STDOUT"); 395 "Unable to use pipe as STDOUT");
396 close(filter->pipe_fh[1]); 396 close(filter->pipe_fh[1]);
397 return 0; 397 return 0;
398} 398}
399 399
400int cgit_close_filter(struct cgit_filter *filter) 400int cgit_close_filter(struct cgit_filter *filter)
401{ 401{
402 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), 402 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
403 "Unable to restore STDOUT"); 403 "Unable to restore STDOUT");
404 close(filter->old_stdout); 404 close(filter->old_stdout);
405 if (filter->pid < 0) 405 if (filter->pid < 0)
406 return 0; 406 return 0;
407 waitpid(filter->pid, &filter->exitstatus, 0); 407 waitpid(filter->pid, &filter->exitstatus, 0);
408 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus)) 408 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus))
409 return 0; 409 return 0;
410 die("Subprocess %s exited abnormally", filter->cmd); 410 die("Subprocess %s exited abnormally", filter->cmd);
411} 411}
412 412
413/* Read the content of the specified file into a newly allocated buffer, 413/* Read the content of the specified file into a newly allocated buffer,
414 * zeroterminate the buffer and return 0 on success, errno otherwise. 414 * zeroterminate the buffer and return 0 on success, errno otherwise.
415 */ 415 */
416int readfile(const char *path, char **buf, size_t *size) 416int readfile(const char *path, char **buf, size_t *size)
417{ 417{
418 int fd, e; 418 int fd, e;
419 struct stat st; 419 struct stat st;
420 420
421 fd = open(path, O_RDONLY); 421 fd = open(path, O_RDONLY);
422 if (fd == -1) 422 if (fd == -1)
423 return errno; 423 return errno;
424 if (fstat(fd, &st)) { 424 if (fstat(fd, &st)) {
425 e = errno; 425 e = errno;
426 close(fd); 426 close(fd);
427 return e; 427 return e;
428 } 428 }
429 if (!S_ISREG(st.st_mode)) { 429 if (!S_ISREG(st.st_mode)) {
430 close(fd); 430 close(fd);
431 return EISDIR; 431 return EISDIR;
432 } 432 }
433 *buf = xmalloc(st.st_size + 1); 433 *buf = xmalloc(st.st_size + 1);
434 *size = read_in_full(fd, *buf, st.st_size); 434 *size = read_in_full(fd, *buf, st.st_size);
435 e = errno; 435 e = errno;
436 (*buf)[*size] = '\0'; 436 (*buf)[*size] = '\0';
437 close(fd); 437 close(fd);
438 return (*size == st.st_size ? 0 : e); 438 return (*size == st.st_size ? 0 : e);
439} 439}
440 440
441int is_token_char(char c) 441int is_token_char(char c)
442{ 442{
443 return isalnum(c) || c == '_'; 443 return isalnum(c) || c == '_';
444} 444}
445 445
446/* Replace name with getenv(name), return pointer to zero-terminating char 446/* Replace name with getenv(name), return pointer to zero-terminating char
447 */ 447 */
448char *expand_macro(char *name, int maxlength) 448char *expand_macro(char *name, int maxlength)
449{ 449{
450 char *value; 450 char *value;
451 int len; 451 int len;
452 452
453 len = 0; 453 len = 0;
454 value = getenv(name); 454 value = getenv(name);
455 if (value) { 455 if (value) {
456 len = strlen(value); 456 len = strlen(value);
457 if (len > maxlength) 457 if (len > maxlength)
458 len = maxlength; 458 len = maxlength;
459 strncpy(name, value, len); 459 strncpy(name, value, len);
460 } 460 }
461 return name + len; 461 return name + len;
462} 462}
463 463
464#define EXPBUFSIZE (1024 * 8) 464#define EXPBUFSIZE (1024 * 8)
465 465
466/* Replace all tokens prefixed by '$' in the specified text with the 466/* Replace all tokens prefixed by '$' in the specified text with the
467 * value of the named environment variable. 467 * value of the named environment variable.
468 * NB: the return value is a static buffer, i.e. it must be strdup'd 468 * NB: the return value is a static buffer, i.e. it must be strdup'd
469 * by the caller. 469 * by the caller.
470 */ 470 */
471char *expand_macros(const char *txt) 471char *expand_macros(const char *txt)
472{ 472{
473 static char result[EXPBUFSIZE]; 473 static char result[EXPBUFSIZE];
474 char *p, *start; 474 char *p, *start;
475 int len; 475 int len;
diff --git a/ui-log.c b/ui-log.c
index 41b5225..bc0c02c 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,232 +1,232 @@
1/* ui-log.c: functions for log output 1/* ui-log.c: functions for log 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 "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
13int files, add_lines, rem_lines; 13int files, add_lines, rem_lines;
14 14
15void count_lines(char *line, int size) 15void count_lines(char *line, int size)
16{ 16{
17 if (size <= 0) 17 if (size <= 0)
18 return; 18 return;
19 19
20 if (line[0] == '+') 20 if (line[0] == '+')
21 add_lines++; 21 add_lines++;
22 22
23 else if (line[0] == '-') 23 else if (line[0] == '-')
24 rem_lines++; 24 rem_lines++;
25} 25}
26 26
27void inspect_files(struct diff_filepair *pair) 27void inspect_files(struct diff_filepair *pair)
28{ 28{
29 unsigned long old_size = 0; 29 unsigned long old_size = 0;
30 unsigned long new_size = 0; 30 unsigned long new_size = 0;
31 int binary = 0; 31 int binary = 0;
32 32
33 files++; 33 files++;
34 if (ctx.repo->enable_log_linecount) 34 if (ctx.repo->enable_log_linecount)
35 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 35 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
36 &new_size, &binary, 0, ctx.qry.ignorews, 36 &new_size, &binary, 0, ctx.qry.ignorews,
37 count_lines); 37 count_lines);
38} 38}
39 39
40void show_commit_decorations(struct commit *commit) 40void show_commit_decorations(struct commit *commit)
41{ 41{
42 struct name_decoration *deco; 42 struct name_decoration *deco;
43 static char buf[1024]; 43 static char buf[1024];
44 44
45 buf[sizeof(buf) - 1] = 0; 45 buf[sizeof(buf) - 1] = 0;
46 deco = lookup_decoration(&name_decoration, &commit->object); 46 deco = lookup_decoration(&name_decoration, &commit->object);
47 while (deco) { 47 while (deco) {
48 if (!prefixcmp(deco->name, "refs/heads/")) { 48 if (!prefixcmp(deco->name, "refs/heads/")) {
49 strncpy(buf, deco->name + 11, sizeof(buf) - 1); 49 strncpy(buf, deco->name + 11, sizeof(buf) - 1);
50 cgit_log_link(buf, NULL, "branch-deco", buf, NULL, 50 cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
51 ctx.qry.vpath, 0, NULL, NULL, 51 ctx.qry.vpath, 0, NULL, NULL,
52 ctx.qry.showmsg); 52 ctx.qry.showmsg);
53 } 53 }
54 else if (!prefixcmp(deco->name, "tag: refs/tags/")) { 54 else if (!prefixcmp(deco->name, "tag: refs/tags/")) {
55 strncpy(buf, deco->name + 15, sizeof(buf) - 1); 55 strncpy(buf, deco->name + 15, sizeof(buf) - 1);
56 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf); 56 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
57 } 57 }
58 else if (!prefixcmp(deco->name, "refs/tags/")) { 58 else if (!prefixcmp(deco->name, "refs/tags/")) {
59 strncpy(buf, deco->name + 10, sizeof(buf) - 1); 59 strncpy(buf, deco->name + 10, sizeof(buf) - 1);
60 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf); 60 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
61 } 61 }
62 else if (!prefixcmp(deco->name, "refs/remotes/")) { 62 else if (!prefixcmp(deco->name, "refs/remotes/")) {
63 strncpy(buf, deco->name + 13, sizeof(buf) - 1); 63 strncpy(buf, deco->name + 13, sizeof(buf) - 1);
64 cgit_log_link(buf, NULL, "remote-deco", NULL, 64 cgit_log_link(buf, NULL, "remote-deco", NULL,
65 sha1_to_hex(commit->object.sha1), 65 sha1_to_hex(commit->object.sha1),
66 ctx.qry.vpath, 0, NULL, NULL, 66 ctx.qry.vpath, 0, NULL, NULL,
67 ctx.qry.showmsg); 67 ctx.qry.showmsg);
68 } 68 }
69 else { 69 else {
70 strncpy(buf, deco->name, sizeof(buf) - 1); 70 strncpy(buf, deco->name, sizeof(buf) - 1);
71 cgit_commit_link(buf, NULL, "deco", ctx.qry.head, 71 cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
72 sha1_to_hex(commit->object.sha1), 72 sha1_to_hex(commit->object.sha1),
73 ctx.qry.vpath, 0); 73 ctx.qry.vpath, 0);
74 } 74 }
75 deco = deco->next; 75 deco = deco->next;
76 } 76 }
77} 77}
78 78
79void print_commit(struct commit *commit) 79void print_commit(struct commit *commit)
80{ 80{
81 struct commitinfo *info; 81 struct commitinfo *info;
82 char *tmp; 82 char *tmp;
83 int cols = 2; 83 int cols = 2;
84 84
85 info = cgit_parse_commit(commit); 85 info = cgit_parse_commit(commit);
86 htmlf("<tr%s><td>", 86 htmlf("<tr%s><td>",
87 ctx.qry.showmsg ? " class='logheader'" : ""); 87 ctx.qry.showmsg ? " class='logheader'" : "");
88 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); 88 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
89 tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp); 89 tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp);
90 html_link_open(tmp, NULL, NULL); 90 html_link_open(tmp, NULL, NULL);
91 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 91 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
92 html_link_close(); 92 html_link_close();
93 htmlf("</td><td%s>", 93 htmlf("</td><td%s>",
94 ctx.qry.showmsg ? " class='logsubject'" : ""); 94 ctx.qry.showmsg ? " class='logsubject'" : "");
95 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, 95 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
96 sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0); 96 sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0);
97 show_commit_decorations(commit); 97 show_commit_decorations(commit);
98 html("</td><td>"); 98 html("</td><td>");
99 html_txt(info->author); 99 html_txt(info->author);
100 if (ctx.repo->enable_log_filecount) { 100 if (ctx.repo->enable_log_filecount) {
101 files = 0; 101 files = 0;
102 add_lines = 0; 102 add_lines = 0;
103 rem_lines = 0; 103 rem_lines = 0;
104 cgit_diff_commit(commit, inspect_files); 104 cgit_diff_commit(commit, inspect_files, ctx.qry.vpath);
105 html("</td><td>"); 105 html("</td><td>");
106 htmlf("%d", files); 106 htmlf("%d", files);
107 if (ctx.repo->enable_log_linecount) { 107 if (ctx.repo->enable_log_linecount) {
108 html("</td><td>"); 108 html("</td><td>");
109 htmlf("-%d/+%d", rem_lines, add_lines); 109 htmlf("-%d/+%d", rem_lines, add_lines);
110 } 110 }
111 } 111 }
112 html("</td></tr>\n"); 112 html("</td></tr>\n");
113 if (ctx.qry.showmsg) { 113 if (ctx.qry.showmsg) {
114 struct strbuf notes = STRBUF_INIT; 114 struct strbuf notes = STRBUF_INIT;
115 format_note(NULL, commit->object.sha1, &notes, PAGE_ENCODING, 0); 115 format_note(NULL, commit->object.sha1, &notes, PAGE_ENCODING, 0);
116 116
117 if (ctx.repo->enable_log_filecount) { 117 if (ctx.repo->enable_log_filecount) {
118 cols++; 118 cols++;
119 if (ctx.repo->enable_log_linecount) 119 if (ctx.repo->enable_log_linecount)
120 cols++; 120 cols++;
121 } 121 }
122 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>", 122 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>",
123 cols); 123 cols);
124 html_txt(info->msg); 124 html_txt(info->msg);
125 html("</td></tr>\n"); 125 html("</td></tr>\n");
126 if (notes.len != 0) { 126 if (notes.len != 0) {
127 html("<tr class='nohover'>"); 127 html("<tr class='nohover'>");
128 html("<td class='lognotes-label'>Notes:</td>"); 128 html("<td class='lognotes-label'>Notes:</td>");
129 htmlf("<td colspan='%d' class='lognotes'>", 129 htmlf("<td colspan='%d' class='lognotes'>",
130 cols); 130 cols);
131 html_txt(notes.buf); 131 html_txt(notes.buf);
132 html("</td></tr>\n"); 132 html("</td></tr>\n");
133 } 133 }
134 strbuf_release(&notes); 134 strbuf_release(&notes);
135 } 135 }
136 cgit_free_commitinfo(info); 136 cgit_free_commitinfo(info);
137} 137}
138 138
139static const char *disambiguate_ref(const char *ref) 139static const char *disambiguate_ref(const char *ref)
140{ 140{
141 unsigned char sha1[20]; 141 unsigned char sha1[20];
142 const char *longref; 142 const char *longref;
143 143
144 longref = fmt("refs/heads/%s", ref); 144 longref = fmt("refs/heads/%s", ref);
145 if (get_sha1(longref, sha1) == 0) 145 if (get_sha1(longref, sha1) == 0)
146 return longref; 146 return longref;
147 147
148 return ref; 148 return ref;
149} 149}
150 150
151void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, 151void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
152 char *path, int pager) 152 char *path, int pager)
153{ 153{
154 struct rev_info rev; 154 struct rev_info rev;
155 struct commit *commit; 155 struct commit *commit;
156 const char *argv[] = {NULL, NULL, NULL, NULL, NULL}; 156 const char *argv[] = {NULL, NULL, NULL, NULL, NULL};
157 int argc = 2; 157 int argc = 2;
158 int i, columns = 3; 158 int i, columns = 3;
159 159
160 if (!tip) 160 if (!tip)
161 tip = ctx.qry.head; 161 tip = ctx.qry.head;
162 162
163 argv[1] = disambiguate_ref(tip); 163 argv[1] = disambiguate_ref(tip);
164 164
165 if (grep && pattern) { 165 if (grep && pattern) {
166 if (!strcmp(grep, "grep") || !strcmp(grep, "author") || 166 if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
167 !strcmp(grep, "committer")) 167 !strcmp(grep, "committer"))
168 argv[argc++] = fmt("--%s=%s", grep, pattern); 168 argv[argc++] = fmt("--%s=%s", grep, pattern);
169 if (!strcmp(grep, "range")) 169 if (!strcmp(grep, "range"))
170 argv[1] = pattern; 170 argv[1] = pattern;
171 } 171 }
172 172
173 if (path) { 173 if (path) {
174 argv[argc++] = "--"; 174 argv[argc++] = "--";
175 argv[argc++] = path; 175 argv[argc++] = path;
176 } 176 }
177 init_revisions(&rev, NULL); 177 init_revisions(&rev, NULL);
178 rev.abbrev = DEFAULT_ABBREV; 178 rev.abbrev = DEFAULT_ABBREV;
179 rev.commit_format = CMIT_FMT_DEFAULT; 179 rev.commit_format = CMIT_FMT_DEFAULT;
180 rev.verbose_header = 1; 180 rev.verbose_header = 1;
181 rev.show_root_diff = 0; 181 rev.show_root_diff = 0;
182 setup_revisions(argc, argv, &rev, NULL); 182 setup_revisions(argc, argv, &rev, NULL);
183 load_ref_decorations(DECORATE_FULL_REFS); 183 load_ref_decorations(DECORATE_FULL_REFS);
184 rev.show_decorations = 1; 184 rev.show_decorations = 1;
185 rev.grep_filter.regflags |= REG_ICASE; 185 rev.grep_filter.regflags |= REG_ICASE;
186 compile_grep_patterns(&rev.grep_filter); 186 compile_grep_patterns(&rev.grep_filter);
187 prepare_revision_walk(&rev); 187 prepare_revision_walk(&rev);
188 188
189 if (pager) 189 if (pager)
190 html("<table class='list nowrap'>"); 190 html("<table class='list nowrap'>");
191 191
192 html("<tr class='nohover'><th class='left'>Age</th>" 192 html("<tr class='nohover'><th class='left'>Age</th>"
193 "<th class='left'>Commit message"); 193 "<th class='left'>Commit message");
194 if (pager) { 194 if (pager) {
195 html(" ("); 195 html(" (");
196 cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL, 196 cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
197 NULL, ctx.qry.head, ctx.qry.sha1, 197 NULL, ctx.qry.head, ctx.qry.sha1,
198 ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep, 198 ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep,
199 ctx.qry.search, ctx.qry.showmsg ? 0 : 1); 199 ctx.qry.search, ctx.qry.showmsg ? 0 : 1);
200 html(")"); 200 html(")");
201 } 201 }
202 html("</th><th class='left'>Author</th>"); 202 html("</th><th class='left'>Author</th>");
203 if (ctx.repo->enable_log_filecount) { 203 if (ctx.repo->enable_log_filecount) {
204 html("<th class='left'>Files</th>"); 204 html("<th class='left'>Files</th>");
205 columns++; 205 columns++;
206 if (ctx.repo->enable_log_linecount) { 206 if (ctx.repo->enable_log_linecount) {
207 html("<th class='left'>Lines</th>"); 207 html("<th class='left'>Lines</th>");
208 columns++; 208 columns++;
209 } 209 }
210 } 210 }
211 html("</tr>\n"); 211 html("</tr>\n");
212 212
213 if (ofs<0) 213 if (ofs<0)
214 ofs = 0; 214 ofs = 0;
215 215
216 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { 216 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
217 free(commit->buffer); 217 free(commit->buffer);
218 commit->buffer = NULL; 218 commit->buffer = NULL;
219 free_commit_list(commit->parents); 219 free_commit_list(commit->parents);
220 commit->parents = NULL; 220 commit->parents = NULL;
221 } 221 }
222 222
223 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { 223 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
224 print_commit(commit); 224 print_commit(commit);
225 free(commit->buffer); 225 free(commit->buffer);
226 commit->buffer = NULL; 226 commit->buffer = NULL;
227 free_commit_list(commit->parents); 227 free_commit_list(commit->parents);
228 commit->parents = NULL; 228 commit->parents = NULL;
229 } 229 }
230 if (pager) { 230 if (pager) {
231 html("</table><div class='pager'>"); 231 html("</table><div class='pager'>");
232 if (ofs > 0) { 232 if (ofs > 0) {