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