diff options
Diffstat (limited to 'filter.c')
-rw-r--r-- | filter.c | 81 |
1 files changed, 60 insertions, 21 deletions
@@ -12,6 +12,11 @@ | |||
12 | #include <unistd.h> | 12 | #include <unistd.h> |
13 | #include <string.h> | 13 | #include <string.h> |
14 | #include <stdlib.h> | 14 | #include <stdlib.h> |
15 | #include <dlfcn.h> | ||
16 | |||
17 | static ssize_t (*libc_write)(int fd, const void *buf, size_t count); | ||
18 | static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL; | ||
19 | static struct cgit_filter *current_write_filter = NULL; | ||
15 | 20 | ||
16 | static inline void reap_filter(struct cgit_filter *filter) | 21 | static inline void reap_filter(struct cgit_filter *filter) |
17 | { | 22 | { |
@@ -32,12 +37,43 @@ void cgit_cleanup_filters(void) | |||
32 | } | 37 | } |
33 | } | 38 | } |
34 | 39 | ||
40 | void cgit_init_filters(void) | ||
41 | { | ||
42 | libc_write = dlsym(RTLD_NEXT, "write"); | ||
43 | if (!libc_write) | ||
44 | die("Could not locate libc's write function"); | ||
45 | } | ||
46 | |||
47 | ssize_t write(int fd, const void *buf, size_t count) | ||
48 | { | ||
49 | if (fd != STDOUT_FILENO || !filter_write) | ||
50 | return libc_write(fd, buf, count); | ||
51 | return filter_write(current_write_filter, buf, count); | ||
52 | } | ||
53 | |||
54 | static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count)) | ||
55 | { | ||
56 | /* We want to avoid buggy nested patterns. */ | ||
57 | assert(filter_write == NULL); | ||
58 | assert(current_write_filter == NULL); | ||
59 | current_write_filter = filter; | ||
60 | filter_write = new_write; | ||
61 | } | ||
62 | |||
63 | static inline void unhook_write() | ||
64 | { | ||
65 | assert(filter_write != NULL); | ||
66 | assert(current_write_filter != NULL); | ||
67 | filter_write = NULL; | ||
68 | current_write_filter = NULL; | ||
69 | } | ||
70 | |||
35 | static int open_exec_filter(struct cgit_filter *base, va_list ap) | 71 | static int open_exec_filter(struct cgit_filter *base, va_list ap) |
36 | { | 72 | { |
37 | struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base; | 73 | struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base; |
38 | int i; | 74 | int i; |
39 | 75 | ||
40 | for (i = 0; i < filter->extra_args; i++) | 76 | for (i = 0; i < filter->base.argument_count; i++) |
41 | filter->argv[i+1] = va_arg(ap, char *); | 77 | filter->argv[i+1] = va_arg(ap, char *); |
42 | 78 | ||
43 | filter->old_stdout = chk_positive(dup(STDOUT_FILENO), | 79 | filter->old_stdout = chk_positive(dup(STDOUT_FILENO), |
@@ -74,7 +110,7 @@ static int close_exec_filter(struct cgit_filter *base) | |||
74 | die("Subprocess %s exited abnormally", filter->cmd); | 110 | die("Subprocess %s exited abnormally", filter->cmd); |
75 | 111 | ||
76 | done: | 112 | done: |
77 | for (i = 0; i < filter->extra_args; i++) | 113 | for (i = 0; i < filter->base.argument_count; i++) |
78 | filter->argv[i+1] = NULL; | 114 | filter->argv[i+1] = NULL; |
79 | return 0; | 115 | return 0; |
80 | 116 | ||
@@ -99,7 +135,7 @@ static void cleanup_exec_filter(struct cgit_filter *base) | |||
99 | } | 135 | } |
100 | } | 136 | } |
101 | 137 | ||
102 | static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype) | 138 | static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count) |
103 | { | 139 | { |
104 | struct cgit_exec_filter *f; | 140 | struct cgit_exec_filter *f; |
105 | int args_size = 0; | 141 | int args_size = 0; |
@@ -107,20 +143,8 @@ static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filterty | |||
107 | f = xmalloc(sizeof(*f)); | 143 | f = xmalloc(sizeof(*f)); |
108 | /* We leave argv for now and assign it below. */ | 144 | /* We leave argv for now and assign it below. */ |
109 | cgit_exec_filter_init(f, xstrdup(cmd), NULL); | 145 | cgit_exec_filter_init(f, xstrdup(cmd), NULL); |
110 | 146 | f->base.argument_count = argument_count; | |
111 | switch (filtertype) { | 147 | args_size = (2 + argument_count) * sizeof(char *); |
112 | case SOURCE: | ||
113 | case ABOUT: | ||
114 | f->extra_args = 1; | ||
115 | break; | ||
116 | |||
117 | case COMMIT: | ||
118 | default: | ||
119 | f->extra_args = 0; | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | args_size = (2 + f->extra_args) * sizeof(char *); | ||
124 | f->argv = xmalloc(args_size); | 148 | f->argv = xmalloc(args_size); |
125 | memset(f->argv, 0, args_size); | 149 | memset(f->argv, 0, args_size); |
126 | f->argv[0] = f->cmd; | 150 | f->argv[0] = f->cmd; |
@@ -136,6 +160,8 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar | |||
136 | filter->base.cleanup = cleanup_exec_filter; | 160 | filter->base.cleanup = cleanup_exec_filter; |
137 | filter->cmd = cmd; | 161 | filter->cmd = cmd; |
138 | filter->argv = argv; | 162 | filter->argv = argv; |
163 | /* The argument count for open_filter is zero by default, unless called from new_filter, above. */ | ||
164 | filter->base.argument_count = 0; | ||
139 | } | 165 | } |
140 | 166 | ||
141 | int cgit_open_filter(struct cgit_filter *filter, ...) | 167 | int cgit_open_filter(struct cgit_filter *filter, ...) |
@@ -162,7 +188,7 @@ void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix | |||
162 | 188 | ||
163 | static const struct { | 189 | static const struct { |
164 | const char *prefix; | 190 | const char *prefix; |
165 | struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype); | 191 | struct cgit_filter *(*ctor)(const char *cmd, int argument_count); |
166 | } filter_specs[] = { | 192 | } filter_specs[] = { |
167 | { "exec", new_exec_filter }, | 193 | { "exec", new_exec_filter }, |
168 | }; | 194 | }; |
@@ -172,6 +198,8 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) | |||
172 | char *colon; | 198 | char *colon; |
173 | int i; | 199 | int i; |
174 | size_t len; | 200 | size_t len; |
201 | int argument_count; | ||
202 | |||
175 | if (!cmd || !cmd[0]) | 203 | if (!cmd || !cmd[0]) |
176 | return NULL; | 204 | return NULL; |
177 | 205 | ||
@@ -184,16 +212,27 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) | |||
184 | if (len == 1) | 212 | if (len == 1) |
185 | colon = NULL; | 213 | colon = NULL; |
186 | 214 | ||
215 | switch (filtertype) { | ||
216 | case SOURCE: | ||
217 | case ABOUT: | ||
218 | argument_count = 1; | ||
219 | break; | ||
220 | |||
221 | case COMMIT: | ||
222 | default: | ||
223 | argument_count = 0; | ||
224 | break; | ||
225 | } | ||
226 | |||
187 | /* If no prefix is given, exec filter is the default. */ | 227 | /* If no prefix is given, exec filter is the default. */ |
188 | if (!colon) | 228 | if (!colon) |
189 | return new_exec_filter(cmd, filtertype); | 229 | return new_exec_filter(cmd, argument_count); |
190 | 230 | ||
191 | for (i = 0; i < ARRAY_SIZE(filter_specs); i++) { | 231 | for (i = 0; i < ARRAY_SIZE(filter_specs); i++) { |
192 | if (len == strlen(filter_specs[i].prefix) && | 232 | if (len == strlen(filter_specs[i].prefix) && |
193 | !strncmp(filter_specs[i].prefix, cmd, len)) | 233 | !strncmp(filter_specs[i].prefix, cmd, len)) |
194 | return filter_specs[i].ctor(colon + 1, filtertype); | 234 | return filter_specs[i].ctor(colon + 1, argument_count); |
195 | } | 235 | } |
196 | 236 | ||
197 | die("Invalid filter type: %.*s", (int) len, cmd); | 237 | die("Invalid filter type: %.*s", (int) len, cmd); |
198 | } | 238 | } |
199 | |||