Merge branch 'maint'
[paraslash.git] / filter.c
1 /* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file filter.c The stand-alone filter program. */
4
5 #include <regex.h>
6 #include <lopsub.h>
7
8 #include "filter.lsg.h"
9 #include "filter_cmd.lsg.h"
10 #include "para.h"
11 #include "list.h"
12 #include "sched.h"
13 #include "buffer_tree.h"
14 #include "filter.h"
15 #include "string.h"
16 #include "stdin.h"
17 #include "stdout.h"
18 #include "error.h"
19 #include "fd.h"
20 #include "version.h"
21
22 /** Array of error strings. */
23 DEFINE_PARA_ERRLIST;
24 static struct lls_parse_result *lpr; /* command line options */
25
26 #define CMD_PTR (lls_cmd(0, filter_suite))
27 #define OPT_RESULT(_name) \
28 (lls_opt_result(LSG_FILTER_PARA_FILTER_OPT_ ## _name, lpr))
29 #define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
30 #define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
31
32 /** The list of all status items used by para_{server,audiod,gui}. */
33 const char *status_item_list[] = {STATUS_ITEMS};
34
35 /**
36 * Dummy version which only contains NULL pointers.
37 *
38 * This is used by the amp filter which first tries to obtain the amplification
39 * value from an element in this array.
40 */
41 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
42
43 /** The task that reads from stdin. */
44 static struct stdin_task stdin_task_struct;
45 /** pointer to the stdin task. */
46 static struct stdin_task *sit = &stdin_task_struct;
47
48 /** The task that writes converted data to stdout. */
49 static struct stdout_task stdout_task_struct;
50 /** Pointer to the stdout task. */
51 static struct stdout_task *sot = &stdout_task_struct;
52
53 static int loglevel;
54 INIT_STDERR_LOGGING(loglevel);
55
56 static void handle_help_flag(void)
57 {
58 char *help;
59
60 if (OPT_GIVEN(DETAILED_HELP))
61 help = lls_long_help(CMD_PTR);
62 else if (OPT_GIVEN(HELP))
63 help = lls_short_help(CMD_PTR);
64 else
65 return;
66 printf("%s\n", help);
67 free(help);
68 print_filter_helps(OPT_GIVEN(DETAILED_HELP));
69 exit(EXIT_SUCCESS);
70 }
71
72 static int parse_config(void)
73 {
74 char *home, *cf; /* config file */
75 struct stat statbuf;
76 int ret;
77
78 version_handle_flag("filter", OPT_GIVEN(VERSION));
79 handle_help_flag();
80 home = para_homedir();
81 cf = make_message("%s/.paraslash/filter.conf", home);
82 free(home);
83 if (!stat(cf, &statbuf)) {
84 void *map;
85 size_t sz;
86 int cf_argc;
87 char **cf_argv, *errctx;
88 struct lls_parse_result *cf_lpr, *merged_lpr;
89
90 ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
91 if (ret != -E_EMPTY) {
92 if (ret < 0)
93 return ret;
94 ret = lls(lls_convert_config(map, sz, NULL, &cf_argv,
95 &errctx));
96 para_munmap(map, sz);
97 if (ret < 0) {
98 PARA_ERROR_LOG("syntax error in %s\n", cf);
99 return ret;
100 }
101 cf_argc = ret;
102 ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr,
103 &errctx));
104 lls_free_argv(cf_argv);
105 if (ret < 0) {
106 PARA_ERROR_LOG("parse error in %s\n", cf);
107 return ret;
108 }
109 ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr, &errctx));
110 lls_free_parse_result(cf_lpr, CMD_PTR);
111 if (ret < 0)
112 return ret;
113 lls_free_parse_result(lpr, CMD_PTR);
114 lpr = merged_lpr;
115 loglevel = OPT_UINT32_VAL(LOGLEVEL);
116 }
117 }
118 if (!OPT_GIVEN(FILTER)) {
119 print_filter_list();
120 exit(EXIT_SUCCESS);
121 }
122 return 1;
123 }
124
125 /**
126 * The main function of para_filter.
127 *
128 * Para_filter reads data from stdin, converts it by using a chain
129 * of filters (specified on the command line) and writes the resulting
130 * data to stdout.
131 *
132 * \param argc Number of command line options.
133 * \param argv Vector of arguments.
134 *
135 * \return \a EXIT_SUCCESS on success, EXIT_FAILURE on errors.
136 */
137 int main(int argc, char *argv[])
138 {
139 static struct sched s;
140 int i, ret;
141 const struct filter *f;
142 struct btr_node *parent;
143 struct filter_node **fns;
144 struct lls_parse_result *filter_lpr; /* per-filter options */
145 char *errctx;
146
147 ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
148 if (ret < 0)
149 goto out;
150 loglevel = OPT_UINT32_VAL(LOGLEVEL);
151 ret = parse_config();
152 if (ret < 0)
153 goto free_lpr;
154 sit->btrn = btr_new_node(&(struct btr_node_description)
155 EMBRACE(.name = "stdin"));
156 stdin_task_register(sit, &s);
157
158 fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns));
159 for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
160 const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
161 const char *name;
162 struct filter_node *fn;
163 struct task_info ti;
164
165 fn = fns[i] = para_calloc(sizeof(*fn));
166 fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
167 name = filter_name(fn->filter_num);
168 fn->lpr = filter_lpr;
169 PARA_DEBUG_LOG("filter #%d: %s\n", i, name);
170 f = filter_get(fn->filter_num);
171 fn->btrn = btr_new_node(&(struct btr_node_description)
172 EMBRACE(.name = name, .parent = parent,
173 .handler = f->execute, .context = fn));
174 ti.name = name;
175 ti.pre_select = f->pre_select;
176 ti.post_select = f->post_select;
177 ti.context = fn;
178 if (f->open)
179 f->open(fn);
180 fn->task = task_register(&ti, &s);
181 parent = fn->btrn;
182 }
183 sot->btrn = btr_new_node(&(struct btr_node_description)
184 EMBRACE(.name = "stdout", .parent = parent));
185 stdout_task_register(sot, &s);
186
187 s.default_timeout.tv_sec = 1;
188 s.default_timeout.tv_usec = 0;
189 btr_log_tree(sit->btrn, LL_INFO);
190 ret = schedule(&s);
191 sched_shutdown(&s);
192 for (i--; i >= 0; i--) {
193 struct filter_node *fn = fns[i];
194
195 f = filter_get(fn->filter_num);
196 if (f->close)
197 f->close(fn);
198 btr_remove_node(&fn->btrn);
199 if (f->teardown)
200 f->teardown(fn->lpr, fn->conf);
201 free(fn);
202 }
203 free(fns);
204 btr_remove_node(&sit->btrn);
205 btr_remove_node(&sot->btrn);
206 free_lpr:
207 lls_free_parse_result(lpr, CMD_PTR);
208 out:
209 if (ret < 0) {
210 if (errctx)
211 PARA_ERROR_LOG("%s\n", errctx);
212 free(errctx);
213 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
214 }
215 exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
216 }