1 | #include "filterproc.h" |
---|
2 | #include <sys/wait.h> |
---|
3 | #include <string.h> |
---|
4 | #include <glib.h> |
---|
5 | |
---|
6 | struct filter_data { |
---|
7 | const char **in; |
---|
8 | const char *in_end; |
---|
9 | GString *out_str; |
---|
10 | GMainLoop *loop; |
---|
11 | int err; |
---|
12 | }; |
---|
13 | |
---|
14 | static gboolean filter_stdin(GIOChannel *channel, GIOCondition condition, gpointer data_) |
---|
15 | { |
---|
16 | struct filter_data *data = data_; |
---|
17 | gboolean done = condition & (G_IO_ERR | G_IO_HUP); |
---|
18 | |
---|
19 | if (condition & G_IO_OUT) { |
---|
20 | gsize n; |
---|
21 | GIOStatus ret = g_io_channel_write_chars(channel, *data->in, data->in_end - *data->in, &n, NULL); |
---|
22 | *data->in += n; |
---|
23 | if (ret == G_IO_STATUS_ERROR) |
---|
24 | data->err = 1; |
---|
25 | if (ret == G_IO_STATUS_ERROR || *data->in == data->in_end) |
---|
26 | done = TRUE; |
---|
27 | } |
---|
28 | |
---|
29 | if (condition & G_IO_ERR) |
---|
30 | data->err = 1; |
---|
31 | |
---|
32 | if (done) |
---|
33 | g_io_channel_shutdown(channel, TRUE, NULL); |
---|
34 | return !done; |
---|
35 | } |
---|
36 | |
---|
37 | static gboolean filter_stdout(GIOChannel *channel, GIOCondition condition, gpointer data_) |
---|
38 | { |
---|
39 | struct filter_data *data = data_; |
---|
40 | gboolean done = condition & (G_IO_ERR | G_IO_HUP); |
---|
41 | |
---|
42 | if (condition & (G_IO_IN | G_IO_HUP)) { |
---|
43 | gchar *buf; |
---|
44 | gsize n; |
---|
45 | GIOStatus ret = g_io_channel_read_to_end(channel, &buf, &n, NULL); |
---|
46 | g_string_append_len(data->out_str, buf, n); |
---|
47 | g_free(buf); |
---|
48 | if (ret == G_IO_STATUS_ERROR) { |
---|
49 | data->err = 1; |
---|
50 | done = TRUE; |
---|
51 | } |
---|
52 | } |
---|
53 | |
---|
54 | if (condition & G_IO_ERR) |
---|
55 | data->err = 1; |
---|
56 | |
---|
57 | if (done) { |
---|
58 | g_io_channel_shutdown(channel, TRUE, NULL); |
---|
59 | g_main_loop_quit(data->loop); |
---|
60 | } |
---|
61 | return !done; |
---|
62 | } |
---|
63 | |
---|
64 | int call_filter(const char *const *argv, const char *in, char **out, int *status) |
---|
65 | { |
---|
66 | GPid child_pid; |
---|
67 | int child_stdin, child_stdout; |
---|
68 | |
---|
69 | if (!g_spawn_async_with_pipes(NULL, (char**)argv, NULL, |
---|
70 | G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, |
---|
71 | NULL, NULL, |
---|
72 | &child_pid, &child_stdin, &child_stdout, NULL, |
---|
73 | NULL)) { |
---|
74 | *out = NULL; |
---|
75 | return 1; |
---|
76 | } |
---|
77 | |
---|
78 | if (in == NULL) in = ""; |
---|
79 | GMainContext *context = g_main_context_new(); |
---|
80 | struct filter_data data = {&in, in + strlen(in), g_string_new(""), g_main_loop_new(context, FALSE), 0}; |
---|
81 | |
---|
82 | GIOChannel *channel = g_io_channel_unix_new(child_stdin); |
---|
83 | g_io_channel_set_encoding(channel, NULL, NULL); |
---|
84 | g_io_channel_set_flags(channel, g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK, NULL); |
---|
85 | GSource *source = g_io_create_watch(channel, G_IO_OUT | G_IO_ERR | G_IO_HUP); |
---|
86 | g_io_channel_unref(channel); |
---|
87 | g_source_set_callback(source, (GSourceFunc)filter_stdin, &data, NULL); |
---|
88 | g_source_attach(source, context); |
---|
89 | g_source_unref(source); |
---|
90 | |
---|
91 | channel = g_io_channel_unix_new(child_stdout); |
---|
92 | g_io_channel_set_encoding(channel, NULL, NULL); |
---|
93 | g_io_channel_set_flags(channel, g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK, NULL); |
---|
94 | source = g_io_create_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP); |
---|
95 | g_io_channel_unref(channel); |
---|
96 | g_source_set_callback(source, (GSourceFunc)filter_stdout, &data, NULL); |
---|
97 | g_source_attach(source, context); |
---|
98 | g_source_unref(source); |
---|
99 | |
---|
100 | g_main_loop_run(data.loop); |
---|
101 | |
---|
102 | g_main_loop_unref(data.loop); |
---|
103 | g_main_context_unref(context); |
---|
104 | |
---|
105 | waitpid(child_pid, status, 0); |
---|
106 | g_spawn_close_pid(child_pid); |
---|
107 | *out = g_string_free(data.out_str, data.err); |
---|
108 | return data.err; |
---|
109 | } |
---|