all repos — cgit @ db6303b58883c4417f5bcc0c1ee34fed6553dca3

a hyperfast web frontend for git written in c

Merge branch 'lh/plugins'

Conflicts:
	cgit.c
	cgit.h
Lars Hjemli hjemli@gmail.com
Sun, 09 Aug 2009 13:46:01 +0200
commit

db6303b58883c4417f5bcc0c1ee34fed6553dca3

parent

17e3ff42646f182911fd0e5d872082977538db9e

7 files changed, 125 insertions(+), 32 deletions(-)

jump to
M cgit.ccgit.c

@@ -25,6 +25,21 @@ item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);

item->util = xstrdup(value); } +struct cgit_filter *new_filter(const char *cmd, int extra_args) +{ + struct cgit_filter *f; + + if (!cmd || !cmd[0]) + return NULL; + + f = xmalloc(sizeof(struct cgit_filter)); + f->cmd = xstrdup(cmd); + f->argv = xmalloc((2 + extra_args) * sizeof(char *)); + f->argv[0] = f->cmd; + f->argv[1] = NULL; + return f; +} + void config_cb(const char *name, const char *value) { if (!strcmp(name, "root-title"))

@@ -85,6 +100,8 @@ else if (!strcmp(name, "cache-static-ttl"))

ctx.cfg.cache_static_ttl = atoi(value); else if (!strcmp(name, "cache-dynamic-ttl")) ctx.cfg.cache_dynamic_ttl = atoi(value); + else if (!strcmp(name, "commit-filter")) + ctx.cfg.commit_filter = new_filter(value, 0); else if (!strcmp(name, "embedded")) ctx.cfg.embedded = atoi(value); else if (!strcmp(name, "max-message-length"))

@@ -95,6 +112,8 @@ else if (!strcmp(name, "max-repo-count"))

ctx.cfg.max_repo_count = atoi(value); else if (!strcmp(name, "max-commit-count")) ctx.cfg.max_commit_count = atoi(value); + else if (!strcmp(name, "source-filter")) + ctx.cfg.source_filter = new_filter(value, 1); else if (!strcmp(name, "summary-log")) ctx.cfg.summary_log = atoi(value); else if (!strcmp(name, "summary-branches"))

@@ -139,6 +158,10 @@ else if (ctx.repo && !strcmp(name, "repo.max-stats"))

ctx.repo->max_stats = cgit_find_stats_period(value, NULL); else if (ctx.repo && !strcmp(name, "repo.module-link")) ctx.repo->module_link= xstrdup(value); + else if (ctx.repo && !strcmp(name, "repo.commit-filter")) + ctx.repo->commit_filter = new_filter(value, 0); + else if (ctx.repo && !strcmp(name, "repo.source-filter")) + ctx.repo->source_filter = new_filter(value, 1); else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { if (*value == '/') ctx.repo->readme = xstrdup(value);
M cgit.hcgit.h

@@ -49,6 +49,15 @@ typedef void (*configfn)(const char *name, const char *value);

typedef void (*filepair_fn)(struct diff_filepair *pair); typedef void (*linediff_fn)(char *line, int len); +struct cgit_filter { + char *cmd; + char **argv; + int old_stdout; + int pipe_fh[2]; + int pid; + int exitstatus; +}; + struct cgit_repo { char *url; char *name;

@@ -65,6 +74,8 @@ int enable_log_filecount;

int enable_log_linecount; int max_stats; time_t mtime; + struct cgit_filter *commit_filter; + struct cgit_filter *source_filter; }; struct cgit_repolist {

@@ -177,6 +188,8 @@ int summary_branches;

int summary_log; int summary_tags; struct string_list mimetypes; + struct cgit_filter *commit_filter; + struct cgit_filter *source_filter; }; struct cgit_page {

@@ -250,6 +263,9 @@

extern const char *cgit_repobasename(const char *reponame); extern int cgit_parse_snapshots_mask(const char *str); + +extern int cgit_open_filter(struct cgit_filter *filter); +extern int cgit_close_filter(struct cgit_filter *filter); #endif /* CGIT_H */
M cgitrc.5.txtcgitrc.5.txt

@@ -55,6 +55,12 @@ repository url, generates valid clone urls for the repository. This

setting is only used if `repo.clone-url` is unspecified. Default value: none. +commit-filter:: + Specifies a command which will be invoked to format commit messages. + The command will get the message on its STDIN, and the STDOUT from the + command will be included verbatim as the commit message, i.e. this can + be used to implement bugtracker integration. Default value: none. + css:: Url which specifies the css document to include in all cgit pages. Default value: "/cgit.css".

@@ -206,6 +212,14 @@ "tar.bz2" bzip-compressed tar-file

"zip" zip-file Default value: none. +source-filter:: + Specifies a command which will be invoked to format plaintext blobs + in the tree view. The command will get the blob content on its STDIN + and the name of the blob as its only command line argument. The STDOUT + from the command will be included verbatim as the blob contents, i.e. + this can be used to implement e.g. syntax highlighting. Default value: + none. + summary-branches:: Specifies the number of branches to display in the repository "summary" view. Default value: "10".

@@ -231,6 +245,9 @@ -------------------

repo.clone-url:: A list of space-separated urls which can be used to clone this repo. Default value: none. + +repo.commit-filter:: + Override the default commit-filter. Default value: <commit-filter>. repo.defbranch:: The name of the default branch for this repository. If no such branch

@@ -271,6 +288,9 @@

repo.snapshots:: A mask of allowed snapshot-formats for this repo, restricted by the "snapshots" global setting. Default value: <snapshots>. + +repo.source-filter:: + Override the default source-filter. Default value: <source-filter>. repo.url:: The relative url used to access the repository. This must be the first
M shared.cshared.c

@@ -62,6 +62,8 @@ ret->max_stats = ctx.cfg.max_stats;

ret->module_link = ctx.cfg.module_link; ret->readme = NULL; ret->mtime = -1; + ret->commit_filter = ctx.cfg.commit_filter; + ret->source_filter = ctx.cfg.source_filter; return ret; }

@@ -355,3 +357,38 @@ str += tl;

} return rv; } + +int cgit_open_filter(struct cgit_filter *filter) +{ + + filter->old_stdout = chk_positive(dup(STDOUT_FILENO), + "Unable to duplicate STDOUT"); + chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess"); + filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); + if (filter->pid == 0) { + close(filter->pipe_fh[1]); + chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO), + "Unable to use pipe as STDIN"); + execvp(filter->cmd, filter->argv); + die("Unable to exec subprocess %s: %s (%d)", filter->cmd, + strerror(errno), errno); + } + close(filter->pipe_fh[0]); + chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO), + "Unable to use pipe as STDOUT"); + close(filter->pipe_fh[1]); + return 0; +} + +int cgit_close_filter(struct cgit_filter *filter) +{ + chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), + "Unable to restore STDOUT"); + close(filter->old_stdout); + if (filter->pid < 0) + return 0; + waitpid(filter->pid, &filter->exitstatus, 0); + if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus)) + return 0; + die("Subprocess %s exited abnormally", filter->cmd); +}
M ui-commit.cui-commit.c

@@ -93,11 +93,19 @@ html("</td></tr>");

} html("</table>\n"); html("<div class='commit-subject'>"); + if (ctx.repo->commit_filter) + cgit_open_filter(ctx.repo->commit_filter); html_txt(info->subject); + if (ctx.repo->commit_filter) + cgit_close_filter(ctx.repo->commit_filter); show_commit_decorations(commit); html("</div>"); html("<div class='commit-msg'>"); + if (ctx.repo->commit_filter) + cgit_open_filter(ctx.repo->commit_filter); html_txt(info->msg); + if (ctx.repo->commit_filter) + cgit_close_filter(ctx.repo->commit_filter); html("</div>"); if (parents < 3) { if (parents)
M ui-snapshot.cui-snapshot.c

@@ -12,37 +12,16 @@ #include "ui-shared.h"

static int write_compressed_tar_archive(struct archiver_args *args,const char *filter) { - int rw[2]; - pid_t gzpid; - int stdout2; - int status; int rv; + struct cgit_filter f; - stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing"); - chk_zero(pipe(rw), "Opening pipe from compressor subprocess"); - gzpid = chk_non_negative(fork(), "Forking compressor subprocess"); - if(gzpid==0) { - /* child */ - chk_zero(close(rw[1]), "Closing write end of pipe in child"); - chk_zero(close(STDIN_FILENO), "Closing STDIN"); - chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin"); - execlp(filter,filter,NULL); - _exit(-1); - } - /* parent */ - chk_zero(close(rw[0]), "Closing read end of pipe"); - chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor"); - + f.cmd = xstrdup(filter); + f.argv = malloc(2 * sizeof(char *)); + f.argv[0] = f.cmd; + f.argv[1] = NULL; + cgit_open_filter(&f); rv = write_tar_archive(args); - - chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor"); - chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT"); - chk_zero(close(stdout2), "Closing uncompressed STDOUT"); - chk_zero(close(rw[1]), "Closing write end of pipe in parent"); - chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process"); - if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) ) - cgit_print_error("Failed to compress archive"); - + cgit_close_filter(&f); return rv; }
M ui-tree.cui-tree.c

@@ -15,13 +15,23 @@ char *curr_rev;

char *match_path; int header = 0; -static void print_text_buffer(char *buf, unsigned long size) +static void print_text_buffer(const char *name, char *buf, unsigned long size) { unsigned long lineno, idx; const char *numberfmt = "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; html("<table summary='blob content' class='blob'>\n"); + if (ctx.repo->source_filter) { + html("<tr><td class='lines'><pre><code>"); + ctx.repo->source_filter->argv[1] = xstrdup(name); + cgit_open_filter(ctx.repo->source_filter); + write(STDOUT_FILENO, buf, size); + cgit_close_filter(ctx.repo->source_filter); + html("</code></pre></td></tr></table>\n"); + return; + } + html("<tr><td class='linenumbers'><pre>"); idx = 0; lineno = 0;

@@ -65,7 +75,7 @@ }

html("</table>\n"); } -static void print_object(const unsigned char *sha1, char *path) +static void print_object(const unsigned char *sha1, char *path, const char *basename) { enum object_type type; char *buf;

@@ -93,7 +103,7 @@

if (buffer_is_binary(buf, size)) print_binary_buffer(buf, size); else - print_text_buffer(buf, size); + print_text_buffer(basename, buf, size); }

@@ -219,7 +229,7 @@ state = 1;

ls_head(); return READ_TREE_RECURSIVE; } else { - print_object(sha1, buffer); + print_object(sha1, buffer, pathname); return 0; } }