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