chiark / gitweb /
Extract Subversion ignore data.
[xtoys] / xcatch.c
1 /* -*-c-*-
2  *
3  * $Id: xcatch.c,v 1.10 2004/04/08 01:36:29 mdw Exp $
4  *
5  * Catch input and trap it in an X window
6  *
7  * (c) 1998 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the Edgeware X tools collection.
13  *
14  * X tools is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * X tools is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with X tools; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include <errno.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <sys/types.h>
38 #include <sys/wait.h>
39
40 #include <fcntl.h>
41 #include <unistd.h>
42
43 #include <gtk/gtk.h>
44
45 #include <mLib/dstr.h>
46 #include <mLib/mdwopt.h>
47 #include <mLib/report.h>
48 #include <mLib/quis.h>
49
50 #include <mgLib/cancel.h>
51 #include <mgLib/msg.h>
52
53 /*----- Inportant state ---------------------------------------------------*/
54
55 static unsigned int flags;
56
57 #define f_closed 1u
58 #define f_bogus 2u
59
60 static GtkWidget *textbox = 0;
61 static GdkFont *font;
62
63 static pid_t kid = -1;
64 static int status;
65
66 /*----- Main code ---------------------------------------------------------*/
67
68 /* --- The window's closed --- */
69
70 static void killwin(GtkWidget *w, gpointer p)
71 {
72   if (flags & f_closed)
73     gtk_main_quit();
74   else
75     textbox = 0;
76 }
77
78 /* --- Some input has arrived --- */
79
80 static void ready(gpointer data, gint fd, GdkInputCondition c)
81 {
82   char buf[1024];
83   int doscroll = 1;
84   GtkText *t = 0;
85   GtkAdjustment *va = 0;
86
87   /* --- If not ready to read then go away --- */
88
89   if (!(c & GDK_INPUT_READ))
90     return;
91
92   /* --- Decide whether to scroll the window --- */
93
94   if (textbox) {
95     t = GTK_TEXT(textbox);
96     va = t->vadj;
97     if (va->value + va->page_size < va->upper)
98       doscroll = 0;
99     gtk_text_freeze(t);
100   }
101
102   /* --- Read data into the buffer --- *
103    *
104    * This is a bit of a mess.
105    */
106
107   for (;;) {
108     int r = read(fd, buf, sizeof(buf));
109
110     /* --- The read failed --- *
111      *
112      * Maybe there's no more data to read.  In this case, we get
113      * @EWOULDBLOCK@, indicating it's time to stop and do something else.
114      * Otherwise something serious has happened.
115      */
116
117     if (r < 0) {
118       if (errno == EWOULDBLOCK)
119         break;
120       msg(QUIS, ":~OK", "error reading data: %s", strerror(errno));
121       exit(EXIT_FAILURE);
122     }
123
124     /* --- End of file --- *
125      *
126      * If the box is closed, then exit quiety; otherwise wait for it to
127      * go away.
128      */
129
130     if (r == 0) {
131       close(fd);
132       if (textbox)
133         flags |= f_closed;
134       else
135         gtk_main_quit();
136       break;
137     }
138
139     /* --- If there's no output window, create one --- */
140
141     if (!textbox) {
142       GtkWidget *win;
143       GtkWidget *tbl;
144       GtkWidget *w;
145
146       win = gtk_dialog_new();
147       gtk_window_set_policy(GTK_WINDOW(win), 1, 1, 0);
148       gtk_signal_connect(GTK_OBJECT(win), "destroy",
149                          GTK_SIGNAL_FUNC(killwin), 0);
150
151       tbl = gtk_table_new(2, 2, 0);
152       gtk_container_border_width(GTK_CONTAINER(tbl), 8);
153       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(win)->vbox), tbl, 1, 1, 1);
154       gtk_widget_show(tbl);
155
156       textbox = gtk_text_new(0, 0);
157       t = GTK_TEXT(textbox);
158       va = t->vadj;
159       gtk_table_attach(GTK_TABLE(tbl), textbox, 0, 1, 0, 1,
160                        GTK_EXPAND | GTK_SHRINK | GTK_FILL,
161                        GTK_EXPAND | GTK_SHRINK | GTK_FILL,
162                        0, 0);
163       gtk_text_set_editable(t, 0);
164       gtk_widget_set_usize(textbox, 500, 300);
165       gtk_text_freeze(t);
166       gtk_widget_show(textbox);
167
168       w = gtk_vscrollbar_new(va);
169       gtk_table_attach(GTK_TABLE(tbl), w, 1, 2, 0, 1,
170                        0, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
171       gtk_widget_show(w);
172
173       w = gtk_hscrollbar_new(t->hadj);
174       gtk_table_attach(GTK_TABLE(tbl), w, 0, 1, 1, 2,
175                        GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0, 0);
176       gtk_widget_show(w);
177
178       gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(win)->action_area), 0);
179       w = gtk_button_new_with_label("Dismiss");
180       gtk_signal_connect_object(GTK_OBJECT(w), "clicked",
181                                 GTK_SIGNAL_FUNC(gtk_object_destroy),
182                                 GTK_OBJECT(win));
183       gtk_box_pack_end(GTK_BOX(GTK_DIALOG(win)->action_area), w, 0, 0, 0);
184       GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
185       gtk_widget_grab_default(w);
186       cancel(GTK_WINDOW(win), w);
187       gtk_widget_show(w);
188
189       gtk_widget_show(win);
190     }
191
192     gtk_text_insert(t, font, 0, 0, buf, r);
193   }
194
195   if (textbox) {
196     gtk_text_thaw(t);
197     if (doscroll)
198       gtk_adjustment_set_value(va, va->upper - va->page_size);
199   }
200 }
201
202 /* --- Signal handler --- */
203
204 static void reap(int sig)
205 {
206   pid_t k;
207   int s;
208   int e = errno;
209
210   for (;;) {
211     k = waitpid(-1, &s, WNOHANG);
212     if (k <= 0)
213       break;
214     if (k == kid) {
215       if (WIFEXITED(s))
216         status = WEXITSTATUS(s);
217       else
218         status = 127;
219     }
220   }
221   errno = e;
222 }
223
224 /* --- Main program --- */
225
226 static void version(FILE *fp)
227 {
228   fprintf(fp, "%s (xtoys version " VERSION ")\n", QUIS);
229 }
230
231 static void usage(FILE *fp)
232 {
233   fprintf(fp, "Usage: %s [-f file] [-F font] [command [args...]]\n", QUIS);
234 }
235
236 int main(int argc, char *argv[])
237 {
238   int fd = -1;
239
240   ego(argv[0]);
241
242   gtk_init(&argc, &argv);
243
244   for (;;) {
245     static struct option opt[] = {
246       { "help",         0,              0,      'h' },
247       { "usage",        0,              0,      'u' },
248       { "version",      0,              0,      'v' },
249       { "file",         OPTF_ARGREQ,    0,      'f' },
250       { "font",         OPTF_ARGREQ,    0,      'F' },
251       { 0,              0,              0,      0 }
252     };
253     int i = mdwopt(argc, argv, "+huvf:F:", opt, 0, 0, 0);
254
255     if (i < 0)
256       break;
257
258     switch (i) {
259       case 'h':
260         version(stdout);
261         fputc('\n', stdout);
262         usage(stdout);
263         fputs(
264 "\n"
265 "Catches input from a pipe or other source, and captures it in a window.\n"
266 "Nothing is displayed if there's no input.\n"
267 "\n"
268 "Options provided:\n"
269 "\n"
270 "-h, --help             Display this help text\n"
271 "-u, --usage            Display a quick usage summary\n"
272 "-v, --version          Display the version number\n"
273 "-f, --file=FILE\t      Read input from the named file\n"
274 "-F, --font=FONT\t      Display output in the named font\n",
275           stdout);
276         exit(0);
277         break;
278       case 'u':
279         usage(stdout);
280         exit(0);
281         break;
282       case 'v':
283         version(stdout);
284         exit(0);
285         break;
286       case 'f':
287         if ((fd = open(optarg, O_RDONLY)) < 0) {
288           die(1, "couldn't open file: %s", strerror(errno));
289           exit(1);
290         }
291         break;
292       case 'F':
293         font = gdk_font_load(optarg);
294         break;
295       default:
296         flags |= f_bogus;
297         break;
298     }
299   }
300
301   if (flags & f_bogus) {
302     usage(stderr);
303     exit(1);
304   }
305
306   if (fd == -1) {
307     if (optind == argc)
308       fd = STDIN_FILENO;
309     else {
310       int pfd[2];
311       struct sigaction sa;
312       sigset_t newmask, oldmask;
313
314       /* --- Set up a signal handler --- */
315
316       sa.sa_handler = reap;
317       sigemptyset(&sa.sa_mask);
318       sa.sa_flags = SA_NOCLDSTOP;
319 #ifdef SA_RESTART
320       sa.sa_flags |= SA_RESTART;
321 #endif
322       sigaction(SIGCHLD, &sa, 0);
323
324       /* --- Start a child program --- */
325
326       if (pipe(pfd))
327         die(1, "couldn't open pipe: %s", strerror(errno));
328
329       sigemptyset(&newmask);
330       sigaddset(&newmask, SIGCHLD);
331       sigprocmask(SIG_BLOCK, &newmask, &oldmask);
332
333       kid = fork();
334       if (kid < 0)
335         die(1, "couldn't fork: %s", strerror(errno));
336       if (kid == 0) {
337         dstr d = DSTR_INIT;
338
339         close(pfd[0]);
340         if (pfd[1] != STDOUT_FILENO)
341           dup2(pfd[1], STDOUT_FILENO);
342         if (pfd[1] != STDERR_FILENO)
343           dup2(pfd[1], STDERR_FILENO);
344         if (pfd[1] != STDOUT_FILENO && pfd[1] != STDERR_FILENO)
345           close(pfd[1]);
346         execvp(argv[optind], argv + optind);
347
348         dstr_putf(&d, "%s: couldn't run `%s': %s\n",
349                   QUIS, argv[optind], strerror(errno));
350         write(STDERR_FILENO, d.buf, d.len);
351         dstr_destroy(&d);
352         _exit(127);
353       }
354
355       sigprocmask(SIG_SETMASK, &oldmask, 0);
356       fd = pfd[0];
357       close(pfd[1]);
358     }
359   }
360
361   {
362     int f = fcntl(fd, F_GETFL);
363     fcntl(fd, F_SETFL, f | O_NONBLOCK);
364   }
365
366   gdk_input_add(fd, GDK_INPUT_READ, ready, 0);
367   gtk_main();
368   return (status);
369 }
370
371 /*----- That's all, folks -------------------------------------------------*/