1
/* Bluefish HTML Editor
2
* filter.c - external filter backend implementation
4
* Copyright (c) 2000 Antti-Juhani Kaijanaho
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
#include "default_include.h"
29
#include <sys/types.h>
38
/* Putting UNUSED after a variable's name in a declaration tells GCC
39
that the variable is meant to be unused, and this makes GCC not
40
warn about this variable being unused. For other compilers, UNUSED
43
#define UNUSED __attribute__ ((unused))
45
#define UNUSED /* nothing */
48
#define MAX3(a,b,c) (MAX(MAX((a), (b)), (c)))
50
/* How long should we sleep after reading and writing a chunk? Keep
51
this small but not zero; this should be used to avoid the busy loop
52
in filter_buffer() taking all the processor's time. */
53
#define SLEEPSEC 0 /* seconds, and */
54
#define SLEEPUSEC 50 /* microseconds */
56
/* Buffer capacity is divisible by CAPACITY_DIVISOR if capacity is
57
ever modified by the functions here. */
58
#define CAPACITY_DIVISOR 64
60
/* All routines here return 0 if problems */
64
/********************* additions from Olivier ************************/
65
/* static void kill_subprocess_lcb(GtkWidget * widget, gpointer data)
67
gtk_widget_destroy(GTK_WIDGET(gtk_widget_get_toplevel(widget)));
71
static void close_error_dialog_lcb(GtkWidget * widget, gpointer data)
73
gtk_widget_destroy(GTK_WIDGET(gtk_widget_get_toplevel(widget)));
77
/* static void subprocess_error_dialog(char const *message, int enable_killing, gpointer data)
79
GtkWidget *win, *hbox, *vbox, *button;
81
win = window_with_title(_("Filter error"), GTK_WIN_POS_MOUSE, GTK_WINDOW_DIALOG, 5);
82
vbox = gtk_vbox_new(FALSE, 0);
83
gtk_container_add(GTK_CONTAINER(win), vbox);
84
gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(message), FALSE, FALSE, 0);
85
hbox = gtk_hbox_new(FALSE, 0);
86
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
87
button = bf_stock_ok_button(close_error_dialog_lcb, data);
88
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
90
button = bf_stock_button(_("Kill filter process"), kill_subprocess_lcb, data);
91
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
93
gtk_widget_show_all(win);
96
/********************* ********************** ************************/
103
void enomem_box(void)
105
g_warning(_("Out of memory.\n"));
109
# define errno_box() errno_box_hlp(__PRETTY_FUNCTION__)
111
# define errno_box() errno_box_hlp(_("unknown"))
115
void errno_box_hlp(char const *fun)
117
g_warning(_("filter.c (%s): System call failed: %s.\n"), fun, strerror(errno));
121
void stderr_box(char *s)
123
g_warning(_("Subprocess standard error: %s"), s);
127
void error_box(char *s)
132
/* Assumes that bufp contains random data. */
134
int init_buffer(buffer_t * bufp, size_t init_capacity)
136
g_assert(bufp != NULL);
138
bufp->capacity = (1 + init_capacity / CAPACITY_DIVISOR)
140
bufp->buf = malloc(bufp->capacity * sizeof *bufp->buf);
141
if (bufp->buf == NULL) {
147
g_assert(bufp->buf != NULL);
148
g_assert(bufp->capacity != 0);
152
/* Return nonzero iff the buffer can hold at least
153
new_minimum_capacity elements after the function returns. */
155
int buffer_ensure_capacity(buffer_t * bufp, size_t new_minimum_capacity)
160
g_assert(bufp != NULL);
161
g_assert(bufp->length <= bufp->capacity);
163
if (bufp->capacity >= new_minimum_capacity)
166
new_cap = (1 + new_minimum_capacity / CAPACITY_DIVISOR)
169
new_buf = realloc(bufp->buf, new_cap * sizeof *bufp->buf);
170
if (new_buf == NULL) {
176
bufp->capacity = new_cap;
178
g_assert(bufp->buf != NULL);
179
g_assert(bufp->capacity != 0);
180
g_assert(bufp->length <= bufp->capacity);
185
/* Make bufp->capacity == bufp->length + 1 true, if possible. Return
186
zero iff capacity isn't at least bufp->length + 1 at return time. */
188
int buffer_trim(buffer_t * bufp)
193
g_assert(bufp != NULL);
194
g_assert(bufp->buf != NULL);
195
g_assert(bufp->capacity != 0);
196
g_assert(bufp->length <= bufp->capacity);
198
if (bufp->capacity == bufp->length + 1)
201
new_cap = bufp->length + 1;
202
new_buf = realloc(bufp->buf, new_cap * sizeof *bufp->buf);
204
return bufp->length + 1 <= bufp->capacity;
207
bufp->capacity = new_cap;
209
g_assert(bufp->buf != NULL);
210
g_assert(bufp->capacity != 0);
211
g_assert(bufp->length <= bufp->capacity);
212
g_assert(bufp->capacity == bufp->length + 1);
216
/* This is the subprocess. We dump all error messages to
217
subprocess_stdio[2], which the parent will show as error boxes. We
218
use no GUI stuff here. */
220
void subprocess(char const *commandline, int subprocess_stdio[])
224
/* Our first job is to set up the subprocess' standard streams: ... */
228
result = dup2(subprocess_stdio[0], STDIN_FILENO);
230
g_warning(_("subprocess unable to dup2 stdin: %s\n"), strerror(errno));
233
result = close(subprocess_stdio[0]);
234
g_assert(result == 0);
237
result = dup2(subprocess_stdio[1], STDOUT_FILENO);
239
g_warning(_("subprocess unable to dup2 stdout: %s\n"), strerror(errno));
242
result = close(subprocess_stdio[1]);
243
g_assert(result == 0);
245
/* ... and stderr. */
246
result = dup2(subprocess_stdio[2], STDERR_FILENO);
248
g_warning(_("subprocess unable to dup2 stderr: %s\n"), strerror(errno));
251
result = close(subprocess_stdio[2]);
252
g_assert(result == 0);
254
/* Now we exec /bin/sh -c "commandline". That last NULL is
256
result = execl("/bin/sh", "sh", "-c", commandline, NULL);
258
/* If all goes well, we never reach this line. But of course,
259
the world is not perfect, and we occasionally fail to exec
260
the process. We deal here with the problem. */
261
g_warning(_("subprocess unable to exec filter: %s\n"), strerror(errno));
267
int spawn_subprocess(char const *commandline, int *stdin_fd, /* subprocess stdin, nonblocking */
268
int *stdout_fd, /* subprocess stdout,nonblocking */
269
int *stderr_fd, /* subprocess stderr, nonblocking */
272
#ifndef WIN32 /*currently gcc doesn't support pipe() under WIN32 :-( */
274
int subprocess_stdio[3];
277
/* Create subprocess standard input. */
278
result = pipe(pipefds);
283
subprocess_stdio[0] = pipefds[0];
284
*stdin_fd = pipefds[1];
286
/* Create subprocess standard output. */
287
result = pipe(pipefds);
292
subprocess_stdio[1] = pipefds[1];
293
*stdout_fd = pipefds[0];
295
/* Create subprocess standard error. */
296
result = pipe(pipefds);
301
subprocess_stdio[2] = pipefds[1];
302
*stderr_fd = pipefds[0];
304
/* Make the parent process side of the pipes nonblocking. */
305
result = fcntl(*stdin_fd, F_SETFL, O_NONBLOCK);
311
result = fcntl(*stdout_fd, F_SETFL, O_NONBLOCK);
317
result = fcntl(*stderr_fd, F_SETFL, O_NONBLOCK);
323
/* Create the subprocess. */
324
DEBUG_MSG("filter.c (spawn_subprocess): forking...\n");
326
if (*sub_pid == -1) {
329
} else if (*sub_pid == 0) {
330
DEBUG_MSG("filter.c (spawn_subprocess) / child: fork successful.\n");
331
result = close(*stdin_fd);
332
g_assert(result == 0);
333
result = close(*stdout_fd);
334
g_assert(result == 0);
335
result = close(*stderr_fd);
336
g_assert(result == 0);
337
subprocess(commandline, subprocess_stdio);
338
g_assert_not_reached();
342
DEBUG_MSG("filter.c (spawn_subprocess) / parent: fork successful.\n");
343
result = close(subprocess_stdio[0]);
344
g_assert(result == 0);
345
result = close(subprocess_stdio[1]);
346
g_assert(result == 0);
347
result = close(subprocess_stdio[2]);
348
g_assert(result == 0);
356
size_t first_unwritten;
361
void write_lcb(gpointer data_gp, int fd, GdkInputCondition condition UNUSED)
368
count = data->buf->length - data->first_unwritten;
373
/* Retry on EAGAIN until successful. Since EAGAIN probably
374
means that we're trying to feed too much to fit in the
375
kernel buffer, we try again with a smaller chunk. On
376
EINTR, we try again in the next iteration. */
378
written = write(fd, data->buf->buf + data->first_unwritten, count);
380
if (written == -1 && errno == EAGAIN) {
381
DEBUG_MSG("filter.c (write_lcb): EAGAIN with %d\n", written);
382
count = count / 2 + 1;
386
if (written == -1 && errno == EINTR) {
387
DEBUG_MSG("filter.c (write_lcb): EINTR\n");
396
data->first_unwritten += written;
397
DEBUG_MSG("filter.c (write_lcb): wrote %d bytes\n", written);
399
if (data->first_unwritten == data->buf->length) {
401
register int close_result;
402
gdk_input_remove(data->tag);
403
close_result = close(fd);
404
g_assert(close_result == 0);
406
DEBUG_MSG("filter.c (write_lcb): End of data.\n");
412
void read_lcb(gpointer data_gp, int fd, GdkInputCondition condition UNUSED)
415
const size_t blk = 64;
420
DEBUG_MSG("filter.c (read_lcb): data = %X\n, fd=%d", (int) data, fd);
425
ok = buffer_ensure_capacity(data->buf, data->buf->length + blk);
431
readc = read(fd, data->buf->buf + data->buf->length, blk);
433
if (readc == -1 && errno == EINTR) {
434
DEBUG_MSG("filter.c (read_lcb): EINTR\n");
438
if (readc == -1 && errno == EAGAIN) {
439
DEBUG_MSG("filter.c (read_lcb): EAGAIN\n");
449
register int close_rv;
452
DEBUG_MSG("filter.c (read_lcb): data->eof_p == %d\n", data->eof_p);
453
gdk_input_remove(data->tag);
454
close_rv = close(fd);
455
DEBUG_MSG("filter.c (read_lcb): close(%d) -> %d\n", fd, close_rv);
456
g_assert(close_rv == 0);
457
DEBUG_MSG("filter.c (read_lcb): end of file on fd %d, data->eof_p == %d\n", fd, data->eof_p);
461
data->buf->length += readc;
462
DEBUG_MSG("filter.c (read_lcb): read %d bytes\n", readc);
465
int filter_buffer(char const *commandline, buffer_t in_buf, buffer_t * out_buf)
467
int child_stdin, child_stdout, child_stderr;
471
buffer_t err_buf = { 0, 0, 0 };
472
input_data_t in_data = { 0, &in_buf, 0, 0 };
473
input_data_t out_data = { 0, out_buf, 0, 0 };
474
input_data_t err_data = { 0, &err_buf, 0, 0 };
475
char *exitreason = NULL;
477
init_buffer(out_buf, in_buf.length);
478
init_buffer(&err_buf, 0);
480
DEBUG_MSG("&out_data = %X, &err_data = %X\n", (int) &out_data, (int) &err_data);
482
ok = spawn_subprocess(commandline, &child_stdin, &child_stdout, &child_stderr, &child_pid);
486
DEBUG_MSG("child_stdin = %d, child_stdout = %d, child_stderr = %d\n", child_stdin, child_stdout, child_stderr);
488
in_data.tag = gdk_input_add(child_stdin, GDK_INPUT_WRITE, write_lcb, &in_data);
489
out_data.tag = gdk_input_add(child_stdout, GDK_INPUT_READ, read_lcb, &out_data);
490
err_data.tag = gdk_input_add(child_stderr, GDK_INPUT_READ, read_lcb, &err_data);
492
/* During this loop the writing and reading happens. We wait
493
until all streams are closed. */
494
while (!in_data.eof_p || !out_data.eof_p || !err_data.eof_p) {
496
static struct timespec const req = { SLEEPSEC, SLEEPUSEC };
497
static struct timespec rem;
498
register int nanosleep_rv;
501
while (gtk_events_pending())
502
gtk_main_iteration();
504
while (gtk_events_pending())
505
gtk_main_iteration();
507
/* Avoid a busy loop by stopping for a short period at
508
every iteration, thus giving more time slices for
510
#ifndef WIN32 /* no nanosleep here */
511
nanosleep_rv = nanosleep(&req, &rem);
512
g_assert(nanosleep_rv == 0);
516
DEBUG_MSG("filter.c (filter_buffer): invoking waitpid.\n");
519
waitpid(child_pid, &status, 0);
522
DEBUG_MSG("filter.c (filter_buffer): waitpid returned.\n");
524
/* Incorporate the exit status in error_buf */
525
if (WIFEXITED(status)) {
526
if (WEXITSTATUS(status) != 0) {
527
exitreason = g_strdup_printf(_("subprocess terminated abnormally with exit code %d\n"), WEXITSTATUS(status));
529
} else if (WIFSIGNALED(status)) {
530
exitreason = g_strdup_printf(_("bluefish: subprocess was killed by signal %d\n"), WTERMSIG(status));
532
if (exitreason != NULL) {
533
error_box(exitreason);
537
if (err_buf.length > 0) {
538
ok = buffer_trim(&err_buf);
543
err_buf.buf[err_buf.length] = '\0';
545
stderr_box(err_buf.buf);
548
/* Add the terminating null character. */
549
ok = buffer_trim(out_buf);
554
out_buf->buf[out_buf->length] = '\0';