3 * $Id: xcatch.c,v 1.9 2002/01/13 14:43:27 mdw Exp $
5 * Catch input and trap it in an X window
7 * (c) 1998 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the Edgeware X tools collection.
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.9 2002/01/13 14:43:27 mdw
33 * Kill some spurious warnings. Track @msg@ interface change.
35 * Revision 1.8 1999/06/19 23:42:55 mdw
36 * Improve signal handling. Fix options parsing to POSIX order only.
38 * Revision 1.7 1999/05/21 22:09:19 mdw
39 * Take advantage of new dynamic string macros.
41 * Revision 1.6 1999/05/19 20:41:15 mdw
42 * Track gratuitous change in mdwopt interface.
44 * Revision 1.5 1999/05/05 18:55:18 mdw
45 * Block SIGCHLD around the `fork' call to prevent a race.
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.
51 * Revision 1.3 1998/12/20 17:19:16 mdw
52 * Return exit status of child process, rather than always returning
55 * Revision 1.2 1998/12/16 00:10:58 mdw
56 * Fix tabbing in help text.
58 * Revision 1.1 1998/12/15 23:46:50 mdw
59 * New program: captures input and puts it in a window.
63 /*----- Header files ------------------------------------------------------*/
71 #include <sys/types.h>
79 #include <mLib/dstr.h>
80 #include <mLib/mdwopt.h>
81 #include <mLib/report.h>
82 #include <mLib/quis.h>
84 #include <mgLib/cancel.h>
85 #include <mgLib/msg.h>
87 /*----- Inportant state ---------------------------------------------------*/
89 static unsigned int flags
;
94 static GtkWidget
*textbox
= 0;
97 static pid_t kid
= -1;
100 /*----- Main code ---------------------------------------------------------*/
102 /* --- The window's closed --- */
104 static void killwin(GtkWidget
*w
, gpointer p
)
106 if (flags
& f_closed
)
112 /* --- Some input has arrived --- */
114 static void ready(gpointer data
, gint fd
, GdkInputCondition c
)
119 GtkAdjustment
*va
= 0;
121 /* --- If not ready to read then go away --- */
123 if (!(c
& GDK_INPUT_READ
))
126 /* --- Decide whether to scroll the window --- */
129 t
= GTK_TEXT(textbox
);
131 if (va
->value
+ va
->page_size
< va
->upper
)
136 /* --- Read data into the buffer --- *
138 * This is a bit of a mess.
142 int r
= read(fd
, buf
, sizeof(buf
));
144 /* --- The read failed --- *
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.
152 if (errno
== EWOULDBLOCK
)
154 msg(QUIS
, ":~OK", "error reading data: %s", strerror(errno
));
158 /* --- End of file --- *
160 * If the box is closed, then exit quiety; otherwise wait for it to
173 /* --- If there's no output window, create one --- */
180 win
= gtk_dialog_new();
181 gtk_window_set_policy(GTK_WINDOW(win
), 1, 1, 0);
182 gtk_signal_connect(GTK_OBJECT(win
), "destroy",
183 GTK_SIGNAL_FUNC(killwin
), 0);
185 tbl
= gtk_table_new(2, 2, 0);
186 gtk_container_border_width(GTK_CONTAINER(tbl
), 8);
187 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(win
)->vbox
), tbl
, 1, 1, 1);
188 gtk_widget_show(tbl
);
190 textbox
= gtk_text_new(0, 0);
191 t
= GTK_TEXT(textbox
);
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
,
197 gtk_text_set_editable(t
, 0);
198 gtk_widget_set_usize(textbox
, 500, 300);
200 gtk_widget_show(textbox
);
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);
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);
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
),
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
);
223 gtk_widget_show(win
);
226 gtk_text_insert(t
, font
, 0, 0, buf
, r
);
232 gtk_adjustment_set_value(va
, va
->upper
- va
->page_size
);
236 /* --- Signal handler --- */
238 static void reap(int sig
)
245 k
= waitpid(-1, &s
, WNOHANG
);
250 status
= WEXITSTATUS(s
);
258 /* --- Main program --- */
260 static void version(FILE *fp
)
262 fprintf(fp
, "%s (xtoys version " VERSION
")\n", QUIS
);
265 static void usage(FILE *fp
)
267 fprintf(fp
, "Usage: %s [-f file] [-F font] [command [args...]]\n", QUIS
);
270 int main(int argc
, char *argv
[])
276 gtk_init(&argc
, &argv
);
279 static struct option opt
[] = {
280 { "help", 0, 0, 'h' },
281 { "usage", 0, 0, 'u' },
282 { "version", 0, 0, 'v' },
283 { "file", OPTF_ARGREQ
, 0, 'f' },
284 { "font", OPTF_ARGREQ
, 0, 'F' },
287 int i
= mdwopt(argc
, argv
, "+huvf:F:", opt
, 0, 0, 0);
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"
302 "Options provided:\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"
307 "-f, --file=FILE\t Read input from the named file\n"
308 "-F, --font=FONT\t Display output in the named font\n",
321 if ((fd
= open(optarg
, O_RDONLY
)) < 0) {
322 die(1, "couldn't open file: %s", strerror(errno
));
327 font
= gdk_font_load(optarg
);
335 if (flags
& f_bogus
) {
346 sigset_t newmask
, oldmask
;
348 /* --- Set up a signal handler --- */
350 sa
.sa_handler
= reap
;
351 sigemptyset(&sa
.sa_mask
);
352 sa
.sa_flags
= SA_NOCLDSTOP
;
354 sa
.sa_flags
|= SA_RESTART
;
356 sigaction(SIGCHLD
, &sa
, 0);
358 /* --- Start a child program --- */
361 die(1, "couldn't open pipe: %s", strerror(errno
));
363 sigemptyset(&newmask
);
364 sigaddset(&newmask
, SIGCHLD
);
365 sigprocmask(SIG_BLOCK
, &newmask
, &oldmask
);
369 die(1, "couldn't fork: %s", strerror(errno
));
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
)
380 execvp(argv
[optind
], argv
+ optind
);
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
);
389 sigprocmask(SIG_SETMASK
, &oldmask
, 0);
396 int f
= fcntl(fd
, F_GETFL
);
397 fcntl(fd
, F_SETFL
, f
| O_NONBLOCK
);
400 gdk_input_add(fd
, GDK_INPUT_READ
, ready
, 0);
405 /*----- That's all, folks -------------------------------------------------*/