~ubuntu-branches/ubuntu/natty/bluefish/natty-proposed

« back to all changes in this revision

Viewing changes to src/filter.c

  • Committer: Bazaar Package Importer
  • Author(s): Davide Puricelli (evo)
  • Date: 2005-04-23 17:05:18 UTC
  • mto: (1.1.5 upstream) (5.1.2 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20050423170518-izh2k25xve7ui1jx
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Bluefish HTML Editor
2
 
 * filter.c - external filter backend implementation
3
 
 *
4
 
 * Copyright (c) 2000 Antti-Juhani Kaijanaho
5
 
 *
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.
10
 
 *
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.
15
 
 *
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
19
 
 */
20
 
 
21
 
#include "default_include.h"
22
 
 
23
 
#include <gdk/gdk.h>
24
 
#include <glib.h>
25
 
#include <errno.h>
26
 
#include <fcntl.h>
27
 
#include <stdlib.h>
28
 
#include <string.h>
29
 
#include <sys/types.h>
30
 
#include <sys/wait.h>
31
 
#include <time.h>
32
 
#include <unistd.h>
33
 
 
34
 
#include "filter.h"
35
 
#include "gtk_easy.h"
36
 
 
37
 
 
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
41
 
   is ignored.  */
42
 
#ifdef __GNUC__
43
 
#define UNUSED __attribute__ ((unused))
44
 
#else
45
 
#define UNUSED                                  /* nothing */
46
 
#endif
47
 
 
48
 
#define MAX3(a,b,c) (MAX(MAX((a), (b)), (c)))
49
 
 
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 */
55
 
 
56
 
/* Buffer capacity is divisible by CAPACITY_DIVISOR if capacity is
57
 
   ever modified by the functions here. */
58
 
#define CAPACITY_DIVISOR 64
59
 
 
60
 
/* All routines here return 0 if problems */
61
 
 
62
 
 
63
 
 
64
 
/********************* additions from Olivier ************************/
65
 
/* static void kill_subprocess_lcb(GtkWidget * widget, gpointer data)
66
 
{
67
 
        gtk_widget_destroy(GTK_WIDGET(gtk_widget_get_toplevel(widget)));
68
 
        g_free(data);
69
 
}
70
 
 
71
 
static void close_error_dialog_lcb(GtkWidget * widget, gpointer data)
72
 
{
73
 
        gtk_widget_destroy(GTK_WIDGET(gtk_widget_get_toplevel(widget)));
74
 
        g_free(data);
75
 
} */
76
 
 
77
 
/* static void subprocess_error_dialog(char const *message, int enable_killing, gpointer data)
78
 
{
79
 
        GtkWidget *win, *hbox, *vbox, *button;
80
 
 
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);
89
 
        if (enable_killing) {
90
 
                button = bf_stock_button(_("Kill filter process"), kill_subprocess_lcb, data);
91
 
                gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
92
 
        }
93
 
        gtk_widget_show_all(win);
94
 
} */
95
 
 
96
 
/********************* ********************** ************************/
97
 
 
98
 
 
99
 
 
100
 
 
101
 
 
102
 
static
103
 
void enomem_box(void)
104
 
{
105
 
        g_warning(_("Out of memory.\n"));
106
 
}
107
 
 
108
 
#ifdef __GNUC__
109
 
#  define errno_box() errno_box_hlp(__PRETTY_FUNCTION__)
110
 
#else
111
 
#  define errno_box() errno_box_hlp(_("unknown"))
112
 
#endif
113
 
 
114
 
static
115
 
void errno_box_hlp(char const *fun)
116
 
{
117
 
        g_warning(_("filter.c (%s): System call failed: %s.\n"), fun, strerror(errno));
118
 
}
119
 
 
120
 
static
121
 
void stderr_box(char *s)
122
 
{
123
 
        g_warning(_("Subprocess standard error: %s"), s);
124
 
}
125
 
 
126
 
static
127
 
void error_box(char *s)
128
 
{
129
 
        g_warning("%s", s);
130
 
}
131
 
 
132
 
/* Assumes that bufp contains random data. */
133
 
static
134
 
int init_buffer(buffer_t * bufp, size_t init_capacity)
135
 
{
136
 
        g_assert(bufp != NULL);
137
 
 
138
 
        bufp->capacity = (1 + init_capacity / CAPACITY_DIVISOR)
139
 
                * CAPACITY_DIVISOR;
140
 
        bufp->buf = malloc(bufp->capacity * sizeof *bufp->buf);
141
 
        if (bufp->buf == NULL) {
142
 
                enomem_box();
143
 
                return 0;
144
 
        }
145
 
        bufp->length = 0;
146
 
 
147
 
        g_assert(bufp->buf != NULL);
148
 
        g_assert(bufp->capacity != 0);
149
 
        return 1;
150
 
}
151
 
 
152
 
/* Return nonzero iff the buffer can hold at least
153
 
   new_minimum_capacity elements after the function returns.  */
154
 
static
155
 
int buffer_ensure_capacity(buffer_t * bufp, size_t new_minimum_capacity)
156
 
{
157
 
        size_t new_cap;
158
 
        buf_elem_t *new_buf;
159
 
 
160
 
        g_assert(bufp != NULL);
161
 
        g_assert(bufp->length <= bufp->capacity);
162
 
 
163
 
        if (bufp->capacity >= new_minimum_capacity)
164
 
                return 1;
165
 
 
166
 
        new_cap = (1 + new_minimum_capacity / CAPACITY_DIVISOR)
167
 
                * CAPACITY_DIVISOR;
168
 
 
169
 
        new_buf = realloc(bufp->buf, new_cap * sizeof *bufp->buf);
170
 
        if (new_buf == NULL) {
171
 
                enomem_box();
172
 
                return 0;
173
 
        }
174
 
 
175
 
        bufp->buf = new_buf;
176
 
        bufp->capacity = new_cap;
177
 
 
178
 
        g_assert(bufp->buf != NULL);
179
 
        g_assert(bufp->capacity != 0);
180
 
        g_assert(bufp->length <= bufp->capacity);
181
 
 
182
 
        return 1;
183
 
}
184
 
 
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. */
187
 
static
188
 
int buffer_trim(buffer_t * bufp)
189
 
{
190
 
        size_t new_cap;
191
 
        buf_elem_t *new_buf;
192
 
 
193
 
        g_assert(bufp != NULL);
194
 
        g_assert(bufp->buf != NULL);
195
 
        g_assert(bufp->capacity != 0);
196
 
        g_assert(bufp->length <= bufp->capacity);
197
 
 
198
 
        if (bufp->capacity == bufp->length + 1)
199
 
                return 1;
200
 
 
201
 
        new_cap = bufp->length + 1;
202
 
        new_buf = realloc(bufp->buf, new_cap * sizeof *bufp->buf);
203
 
        if (new_buf == NULL)
204
 
                return bufp->length + 1 <= bufp->capacity;
205
 
 
206
 
        bufp->buf = new_buf;
207
 
        bufp->capacity = new_cap;
208
 
 
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);
213
 
        return 1;
214
 
}
215
 
 
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. */
219
 
static
220
 
void subprocess(char const *commandline, int subprocess_stdio[])
221
 
{
222
 
        int result;
223
 
 
224
 
        /* Our first job is to set up the subprocess' standard streams: ... */
225
 
 
226
 
        /* ... stdin ... */
227
 
 
228
 
        result = dup2(subprocess_stdio[0], STDIN_FILENO);
229
 
        if (result == -1) {
230
 
                g_warning(_("subprocess unable to dup2 stdin: %s\n"), strerror(errno));
231
 
                _exit(EXIT_FAILURE);
232
 
        }
233
 
        result = close(subprocess_stdio[0]);
234
 
        g_assert(result == 0);
235
 
 
236
 
        /* ... stdout ... */
237
 
        result = dup2(subprocess_stdio[1], STDOUT_FILENO);
238
 
        if (result == -1) {
239
 
                g_warning(_("subprocess unable to dup2 stdout: %s\n"), strerror(errno));
240
 
                _exit(EXIT_FAILURE);
241
 
        }
242
 
        result = close(subprocess_stdio[1]);
243
 
        g_assert(result == 0);
244
 
 
245
 
        /* ... and stderr. */
246
 
        result = dup2(subprocess_stdio[2], STDERR_FILENO);
247
 
        if (result == -1) {
248
 
                g_warning(_("subprocess unable to dup2 stderr: %s\n"), strerror(errno));
249
 
                _exit(EXIT_FAILURE);
250
 
        }
251
 
        result = close(subprocess_stdio[2]);
252
 
        g_assert(result == 0);
253
 
 
254
 
        /* Now we exec /bin/sh -c "commandline".  That last NULL is
255
 
           important! */
256
 
        result = execl("/bin/sh", "sh", "-c", commandline, NULL);
257
 
 
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));
262
 
 
263
 
        _exit(EXIT_FAILURE);
264
 
}
265
 
 
266
 
static
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 */
270
 
                                         pid_t * sub_pid)
271
 
{
272
 
#ifndef WIN32 /*currently gcc doesn't support pipe() under WIN32 :-( */
273
 
        int pipefds[2];
274
 
        int subprocess_stdio[3];
275
 
        int result;
276
 
 
277
 
        /* Create subprocess standard input. */
278
 
        result = pipe(pipefds);
279
 
        if (result == -1) {
280
 
                errno_box();
281
 
                return 0;
282
 
        }
283
 
        subprocess_stdio[0] = pipefds[0];
284
 
        *stdin_fd = pipefds[1];
285
 
 
286
 
        /* Create subprocess standard output. */
287
 
        result = pipe(pipefds);
288
 
        if (result == -1) {
289
 
                errno_box();
290
 
                return 0;
291
 
        }
292
 
        subprocess_stdio[1] = pipefds[1];
293
 
        *stdout_fd = pipefds[0];
294
 
 
295
 
        /* Create subprocess standard error. */
296
 
        result = pipe(pipefds);
297
 
        if (result == -1) {
298
 
                errno_box();
299
 
                return 0;
300
 
        }
301
 
        subprocess_stdio[2] = pipefds[1];
302
 
        *stderr_fd = pipefds[0];
303
 
 
304
 
        /* Make the parent process side of the pipes nonblocking. */
305
 
        result = fcntl(*stdin_fd, F_SETFL, O_NONBLOCK);
306
 
        if (result == -1) {
307
 
                errno_box();
308
 
                return 0;
309
 
        }
310
 
 
311
 
        result = fcntl(*stdout_fd, F_SETFL, O_NONBLOCK);
312
 
        if (result == -1) {
313
 
                errno_box();
314
 
                return 0;
315
 
        }
316
 
 
317
 
        result = fcntl(*stderr_fd, F_SETFL, O_NONBLOCK);
318
 
        if (result == -1) {
319
 
                errno_box();
320
 
                return 0;
321
 
        }
322
 
 
323
 
        /* Create the subprocess. */
324
 
        DEBUG_MSG("filter.c (spawn_subprocess): forking...\n");
325
 
        *sub_pid = fork();
326
 
        if (*sub_pid == -1) {
327
 
                errno_box();
328
 
                return 0;
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();
339
 
        }
340
 
 
341
 
        /* Parent process */
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);
349
 
#endif
350
 
        return 1;
351
 
}
352
 
 
353
 
typedef struct {
354
 
        int eof_p;
355
 
        buffer_t *buf;
356
 
        size_t first_unwritten;
357
 
        int tag;
358
 
} input_data_t;
359
 
 
360
 
static
361
 
void write_lcb(gpointer data_gp, int fd, GdkInputCondition condition UNUSED)
362
 
{
363
 
        input_data_t *data;
364
 
        size_t count;
365
 
        ssize_t written;
366
 
 
367
 
        data = data_gp;
368
 
        count = data->buf->length - data->first_unwritten;
369
 
 
370
 
        if (data->eof_p)
371
 
                return;
372
 
 
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.  */
377
 
  retry:
378
 
        written = write(fd, data->buf->buf + data->first_unwritten, count);
379
 
 
380
 
        if (written == -1 && errno == EAGAIN) {
381
 
                DEBUG_MSG("filter.c (write_lcb): EAGAIN with %d\n", written);
382
 
                count = count / 2 + 1;
383
 
                goto retry;
384
 
        }
385
 
 
386
 
        if (written == -1 && errno == EINTR) {
387
 
                DEBUG_MSG("filter.c (write_lcb): EINTR\n");
388
 
                return;
389
 
        }
390
 
 
391
 
        if (written == -1) {
392
 
                errno_box();
393
 
                return;
394
 
        }
395
 
 
396
 
        data->first_unwritten += written;
397
 
        DEBUG_MSG("filter.c (write_lcb): wrote %d bytes\n", written);
398
 
 
399
 
        if (data->first_unwritten == data->buf->length) {
400
 
                /* End of data. */
401
 
                register int close_result;
402
 
                gdk_input_remove(data->tag);
403
 
                close_result = close(fd);
404
 
                g_assert(close_result == 0);
405
 
                data->eof_p = 1;
406
 
                DEBUG_MSG("filter.c (write_lcb): End of data.\n");
407
 
                return;
408
 
        }
409
 
}
410
 
 
411
 
static
412
 
void read_lcb(gpointer data_gp, int fd, GdkInputCondition condition UNUSED)
413
 
{
414
 
        input_data_t *data;
415
 
        const size_t blk = 64;
416
 
        ssize_t readc;
417
 
        int ok;
418
 
 
419
 
        data = data_gp;
420
 
        DEBUG_MSG("filter.c (read_lcb): data = %X\n, fd=%d", (int) data, fd);
421
 
 
422
 
        if (data->eof_p)
423
 
                return;
424
 
 
425
 
        ok = buffer_ensure_capacity(data->buf, data->buf->length + blk);
426
 
        if (!ok) {
427
 
                enomem_box();
428
 
                return;
429
 
        }
430
 
 
431
 
        readc = read(fd, data->buf->buf + data->buf->length, blk);
432
 
 
433
 
        if (readc == -1 && errno == EINTR) {
434
 
                DEBUG_MSG("filter.c (read_lcb): EINTR\n");
435
 
                return;
436
 
        }
437
 
 
438
 
        if (readc == -1 && errno == EAGAIN) {
439
 
                DEBUG_MSG("filter.c (read_lcb): EAGAIN\n");
440
 
                return;
441
 
        }
442
 
 
443
 
        if (readc == -1) {
444
 
                errno_box();
445
 
                return;
446
 
        }
447
 
 
448
 
        if (readc == 0) {
449
 
                register int close_rv;
450
 
                /* End of file. */
451
 
                data->eof_p = 1;
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);
458
 
                return;
459
 
        }
460
 
 
461
 
        data->buf->length += readc;
462
 
        DEBUG_MSG("filter.c (read_lcb): read %d bytes\n", readc);
463
 
}
464
 
 
465
 
int filter_buffer(char const *commandline, buffer_t in_buf, buffer_t * out_buf)
466
 
{
467
 
        int child_stdin, child_stdout, child_stderr;
468
 
        pid_t child_pid;
469
 
        int ok;
470
 
        int status;
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;
476
 
 
477
 
        init_buffer(out_buf, in_buf.length);
478
 
        init_buffer(&err_buf, 0);
479
 
 
480
 
        DEBUG_MSG("&out_data = %X, &err_data = %X\n", (int) &out_data, (int) &err_data);
481
 
 
482
 
        ok = spawn_subprocess(commandline, &child_stdin, &child_stdout, &child_stderr, &child_pid);
483
 
        if (!ok)
484
 
                return 0;
485
 
 
486
 
        DEBUG_MSG("child_stdin = %d, child_stdout = %d, child_stderr = %d\n", child_stdin, child_stdout, child_stderr);
487
 
 
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);
491
 
 
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) {
495
 
#ifndef WIN32
496
 
                static struct timespec const req = { SLEEPSEC, SLEEPUSEC };
497
 
                static struct timespec rem;
498
 
                register int nanosleep_rv;
499
 
#endif
500
 
 
501
 
                while (gtk_events_pending())
502
 
                        gtk_main_iteration();
503
 
 
504
 
                while (gtk_events_pending())
505
 
                        gtk_main_iteration();
506
 
 
507
 
                /* Avoid a busy loop by stopping for a short period at
508
 
                   every iteration, thus giving more time slices for
509
 
                   other processes.  */
510
 
#ifndef WIN32 /* no nanosleep here */
511
 
                nanosleep_rv = nanosleep(&req, &rem);
512
 
                g_assert(nanosleep_rv == 0);
513
 
#endif    
514
 
        }
515
 
 
516
 
        DEBUG_MSG("filter.c (filter_buffer): invoking waitpid.\n");
517
 
 
518
 
#ifndef WIN32
519
 
        waitpid(child_pid, &status, 0);
520
 
#endif
521
 
 
522
 
        DEBUG_MSG("filter.c (filter_buffer): waitpid returned.\n");
523
 
 
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));
528
 
                }
529
 
        } else if (WIFSIGNALED(status)) {
530
 
                exitreason = g_strdup_printf(_("bluefish: subprocess was killed by signal %d\n"), WTERMSIG(status));
531
 
        }
532
 
        if (exitreason != NULL) {
533
 
                error_box(exitreason);
534
 
                g_free(exitreason);
535
 
        }
536
 
 
537
 
        if (err_buf.length > 0) {
538
 
                ok = buffer_trim(&err_buf);
539
 
                if (!ok) {
540
 
                        enomem_box();
541
 
                        return 0;
542
 
                }
543
 
                err_buf.buf[err_buf.length] = '\0';
544
 
 
545
 
                stderr_box(err_buf.buf);
546
 
        }
547
 
 
548
 
        /* Add the terminating null character. */
549
 
        ok = buffer_trim(out_buf);
550
 
        if (!ok) {
551
 
                enomem_box();
552
 
                return 0;
553
 
        }
554
 
        out_buf->buf[out_buf->length] = '\0';
555
 
        return 1;
556
 
}