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