X-Git-Url: https://gitweb.ps.run/ps-cgit/blobdiff_plain/4bb87cbf17588ec91b46bf0ef0be01672e9be787..e83b51b4f6bd53efea0c772e6ecdf1c5605ca611:/filter.c?ds=sidebyside diff --git a/filter.c b/filter.c index ba66e46..f5a5992 100644 --- a/filter.c +++ b/filter.c @@ -12,13 +12,68 @@ #include #include #include +#include + +static ssize_t (*libc_write)(int fd, const void *buf, size_t count); +static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL; +static struct cgit_filter *current_write_filter = NULL; + +static inline void reap_filter(struct cgit_filter *filter) +{ + if (filter && filter->cleanup) + filter->cleanup(filter); +} + +void cgit_cleanup_filters(void) +{ + int i; + reap_filter(ctx.cfg.about_filter); + reap_filter(ctx.cfg.commit_filter); + reap_filter(ctx.cfg.source_filter); + for (i = 0; i < cgit_repolist.count; ++i) { + reap_filter(cgit_repolist.repos[i].about_filter); + reap_filter(cgit_repolist.repos[i].commit_filter); + reap_filter(cgit_repolist.repos[i].source_filter); + } +} + +void cgit_init_filters(void) +{ + libc_write = dlsym(RTLD_NEXT, "write"); + if (!libc_write) + die("Could not locate libc's write function"); +} + +ssize_t write(int fd, const void *buf, size_t count) +{ + if (fd != STDOUT_FILENO || !filter_write) + return libc_write(fd, buf, count); + return filter_write(current_write_filter, buf, count); +} + +static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count)) +{ + /* We want to avoid buggy nested patterns. */ + assert(filter_write == NULL); + assert(current_write_filter == NULL); + current_write_filter = filter; + filter_write = new_write; +} + +static inline void unhook_write() +{ + assert(filter_write != NULL); + assert(current_write_filter != NULL); + filter_write = NULL; + current_write_filter = NULL; +} static int open_exec_filter(struct cgit_filter *base, va_list ap) { struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base; int i; - for (i = 0; i < filter->extra_args; i++) + for (i = 0; i < filter->base.argument_count; i++) filter->argv[i+1] = va_arg(ap, char *); filter->old_stdout = chk_positive(dup(STDOUT_FILENO), @@ -55,7 +110,7 @@ static int close_exec_filter(struct cgit_filter *base) die("Subprocess %s exited abnormally", filter->cmd); done: - for (i = 0; i < filter->extra_args; i++) + for (i = 0; i < filter->base.argument_count; i++) filter->argv[i+1] = NULL; return 0; @@ -67,24 +122,33 @@ static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *p fprintf(f, "%sexec:%s\n", prefix, filter->cmd); } -int cgit_open_filter(struct cgit_filter *filter, ...) +static void cleanup_exec_filter(struct cgit_filter *base) { - int result; - va_list ap; - va_start(ap, filter); - result = filter->open(filter, ap); - va_end(ap); - return result; + struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base; + if (filter->argv) { + free(filter->argv); + filter->argv = NULL; + } + if (filter->cmd) { + free(filter->cmd); + filter->cmd = NULL; + } } -int cgit_close_filter(struct cgit_filter *filter) +static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count) { - return filter->close(filter); -} + struct cgit_exec_filter *f; + int args_size = 0; -void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix) -{ - filter->fprintf(filter, f, prefix); + f = xmalloc(sizeof(*f)); + /* We leave argv for now and assign it below. */ + cgit_exec_filter_init(f, xstrdup(cmd), NULL); + f->base.argument_count = argument_count; + args_size = (2 + argument_count) * sizeof(char *); + f->argv = xmalloc(args_size); + memset(f->argv, 0, args_size); + f->argv[0] = f->cmd; + return &f->base; } void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv) @@ -93,41 +157,38 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar filter->base.open = open_exec_filter; filter->base.close = close_exec_filter; filter->base.fprintf = fprintf_exec_filter; + filter->base.cleanup = cleanup_exec_filter; filter->cmd = cmd; filter->argv = argv; + /* The argument count for open_filter is zero by default, unless called from new_filter, above. */ + filter->base.argument_count = 0; } -static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype) +int cgit_open_filter(struct cgit_filter *filter, ...) { - struct cgit_exec_filter *f; - int args_size = 0; + int result; + va_list ap; + va_start(ap, filter); + result = filter->open(filter, ap); + va_end(ap); + return result; +} - f = xmalloc(sizeof(*f)); - /* We leave argv for now and assign it below. */ - cgit_exec_filter_init(f, xstrdup(cmd), NULL); +int cgit_close_filter(struct cgit_filter *filter) +{ + return filter->close(filter); +} - switch (filtertype) { - case SOURCE: - case ABOUT: - f->extra_args = 1; - break; +void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix) +{ + filter->fprintf(filter, f, prefix); +} - case COMMIT: - default: - f->extra_args = 0; - break; - } - args_size = (2 + f->extra_args) * sizeof(char *); - f->argv = xmalloc(args_size); - memset(f->argv, 0, args_size); - f->argv[0] = f->cmd; - return &f->base; -} static const struct { const char *prefix; - struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype); + struct cgit_filter *(*ctor)(const char *cmd, int argument_count); } filter_specs[] = { { "exec", new_exec_filter }, }; @@ -137,6 +198,8 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) char *colon; int i; size_t len; + int argument_count; + if (!cmd || !cmd[0]) return NULL; @@ -149,14 +212,26 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) if (len == 1) colon = NULL; + switch (filtertype) { + case SOURCE: + case ABOUT: + argument_count = 1; + break; + + case COMMIT: + default: + argument_count = 0; + break; + } + /* If no prefix is given, exec filter is the default. */ if (!colon) - return new_exec_filter(cmd, filtertype); + return new_exec_filter(cmd, argument_count); for (i = 0; i < ARRAY_SIZE(filter_specs); i++) { if (len == strlen(filter_specs[i].prefix) && !strncmp(filter_specs[i].prefix, cmd, len)) - return filter_specs[i].ctor(colon + 1, filtertype); + return filter_specs[i].ctor(colon + 1, argument_count); } die("Invalid filter type: %.*s", (int) len, cmd);