Improve display for large files. Keep newly added material in view if
[xtoys] / xcatch.c
CommitLineData
63351ac6 1/* -*-c-*-
2 *
566df691 3 * $Id: xcatch.c,v 1.4 1999/03/24 22:23:57 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 $
566df691 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 *
d62a74ae 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 *
4e6aae28 40 * Revision 1.2 1998/12/16 00:10:58 mdw
41 * Fix tabbing in help text.
42 *
63351ac6 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
74static unsigned int flags;
75
76enum {
77 f_closed = 1,
78 f_bogus = 2
79};
80
d62a74ae 81static GtkWidget *textbox = 0;
82static GdkFont *font;
83
84static pid_t kid = -1;
85static int status;
63351ac6 86
87/*----- Main code ---------------------------------------------------------*/
88
89/* --- The window's closed --- */
90
91static 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
101static void ready(gpointer data, gint fd, GdkInputCondition c)
102{
103 char buf[1024];
566df691 104 int doscroll = 1;
105 GtkText *t;
106 GtkAdjustment *va;
63351ac6 107
566df691 108 /* --- If not ready to read then go away --- */
63351ac6 109
110 if (!(c & GDK_INPUT_READ))
111 return;
63351ac6 112
566df691 113 /* --- Decide whether to scroll the window --- */
63351ac6 114
566df691 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);
63351ac6 121 }
122
566df691 123 /* --- Read data into the buffer --- *
124 *
125 * This is a bit of a mess.
126 */
63351ac6 127
566df691 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 */
63351ac6 150
566df691 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 }
63351ac6 211
566df691 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 }
63351ac6 220}
221
222/* --- Signal handler --- */
223
224static void reap(int sig)
225{
d62a74ae 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 }
63351ac6 240}
241
242/* --- Main program --- */
243
244static void version(FILE *fp)
245{
246 fprintf(fp, "%s (xtoys version " VERSION ")\n", QUIS);
247}
248
249static void usage(FILE *fp)
250{
251 fprintf(fp, "Usage: %s [-f file] [-F font] [command [args...]]\n", QUIS);
252}
253
254int 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"
4e6aae28 291"-f, --file=FILE\t Read input from the named file\n"
292"-F, --font=FONT\t Display output in the named font\n",
63351ac6 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];
63351ac6 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
566df691 368 {
369 int f = fcntl(fd, F_GETFL);
370 fcntl(fd, F_SETFL, f | O_NONBLOCK);
371 }
372
63351ac6 373 gdk_input_add(fd, GDK_INPUT_READ, ready, 0);
374 gtk_main();
d62a74ae 375 return (status);
63351ac6 376}
377
378/*----- That's all, folks -------------------------------------------------*/