Merge branch 't/oggdec_latency_improvements'
[paraslash.git] / filter.c
1 /*
2  * Copyright (C) 2005-2014 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file filter.c The stand-alone filter program. */
8
9 #include <regex.h>
10
11 #include "para.h"
12 #include "filter.cmdline.h"
13 #include "list.h"
14 #include "sched.h"
15 #include "ggo.h"
16 #include "buffer_tree.h"
17 #include "filter.h"
18 #include "string.h"
19 #include "stdin.h"
20 #include "stdout.h"
21 #include "error.h"
22 #include "version.h"
23
24 /** The list of all status items used by para_{server,audiod,gui}. */
25 const char *status_item_list[] = {STATUS_ITEM_ARRAY};
26
27 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
28
29 /** Initialize the array of errors for para_filter. */
30 INIT_FILTER_ERRLISTS;
31
32 /** The task that reads from stdin. */
33 static struct stdin_task stdin_task_struct;
34 /** pointer to the stdin task. */
35 static struct stdin_task *sit = &stdin_task_struct;
36
37 /** The task that writes converted data to stdout. */
38 static struct stdout_task stdout_task_struct;
39 /** Pointer to the stdout task. */
40 static struct stdout_task *sot = &stdout_task_struct;
41
42 /** Gengetopt struct that holds the command line args. */
43 static struct filter_args_info conf;
44
45 static int loglevel;
46 INIT_STDERR_LOGGING(loglevel);
47
48 __noreturn static void print_help_and_die(void)
49 {
50         struct ggo_help h = DEFINE_GGO_HELP(filter);
51         bool d = conf.detailed_help_given;
52
53         ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
54         print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
55         exit(0);
56 }
57
58 static int parse_config(void)
59 {
60         static char *cf; /* config file */
61         struct stat statbuf;
62
63         version_handle_flag("filter", conf.version_given);
64         if (conf.help_given || conf.detailed_help_given)
65                 print_help_and_die();
66         if (!cf) {
67                 char *home = para_homedir();
68                 cf = make_message("%s/.paraslash/filter.conf", home);
69                 free(home);
70         }
71         if (!stat(cf, &statbuf)) {
72                 struct filter_cmdline_parser_params params = {
73                         .override = 0,
74                         .initialize = 0,
75                         .check_required = 0,
76                         .check_ambiguity = 0,
77                         .print_errors = 1
78                 };
79                 filter_cmdline_parser_config_file(cf, &conf, &params);
80                 loglevel = get_loglevel_by_name(conf.loglevel_arg);
81         }
82         if (!conf.filter_given)
83                 return -E_NO_FILTERS;
84         return 1;
85 }
86
87 /**
88  * The main function of para_filter.
89  *
90  * Para_filter reads data from stdin, converts it by using a chain
91  * of filters (specified on the command line) and writes the resulting
92  * data to stdout.
93  *
94  * \param argc Number of command line options.
95  * \param argv Vector of arguments.
96  *
97  * \return \a EXIT_SUCCESS on success, EXIT_FAILURE on errors.
98  */
99 int main(int argc, char *argv[])
100 {
101         static struct sched s;
102         int i, ret;
103         struct filter *f;
104         struct btr_node *parent;
105         struct filter_node **fns;
106
107         filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */
108         loglevel = get_loglevel_by_name(conf.loglevel_arg);
109         filter_init();
110         ret = parse_config();
111         if (ret < 0)
112                 goto out;
113         sit->btrn = btr_new_node(&(struct btr_node_description)
114                 EMBRACE(.name = "stdin"));
115         stdin_set_defaults(sit);
116         register_task(&s, &sit->task);
117
118         fns = para_malloc(conf.filter_given * sizeof(*fns));
119         for (i = 0, parent = sit->btrn; i < conf.filter_given; i++) {
120                 char *fa = conf.filter_arg[i];
121                 struct filter_node *fn;
122
123                 fn = fns[i] = para_calloc(sizeof(*fn));
124                 ret = check_filter_arg(fa, &fn->conf);
125                 if (ret < 0) {
126                         free(fn);
127                         goto out_cleanup;
128                 }
129                 fn->filter_num = ret;
130                 f = filters + fn->filter_num;
131                 sprintf(fn->task.status, "%s", f->name);
132                 PARA_DEBUG_LOG("filter #%d: %s\n", i, f->name);
133                 fn->btrn = btr_new_node(&(struct btr_node_description)
134                         EMBRACE(.name = f->name, .parent = parent,
135                         .handler = f->execute, .context = fn));
136                 fn->task.pre_select = f->pre_select;
137                 fn->task.post_select = f->post_select;
138                 f->open(fn);
139                 register_task(&s, &fn->task);
140                 parent = fn->btrn;
141         }
142         sot->btrn = btr_new_node(&(struct btr_node_description)
143                 EMBRACE(.name = "stdout", .parent = parent));
144         stdout_set_defaults(sot);
145         register_task(&s, &sot->task);
146
147         s.default_timeout.tv_sec = 1;
148         s.default_timeout.tv_usec = 0;
149         btr_log_tree(sit->btrn, LL_INFO);
150         ret = schedule(&s);
151 out_cleanup:
152         for (i--; i >= 0; i--) {
153                 struct filter_node *fn = fns[i];
154
155                 f = filters + fn->filter_num;
156                 if (f->close)
157                         f->close(fn);
158                 btr_remove_node(&fn->btrn);
159                 if (f->free_config)
160                         f->free_config(fn->conf);
161                 free(fn);
162         }
163         free(fns);
164         btr_remove_node(&sit->btrn);
165         btr_remove_node(&sot->btrn);
166 out:
167         if (ret < 0)
168                 PARA_EMERG_LOG("%s\n", para_strerror(-ret));
169         exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
170 }