/* -*-c-*-
*
- * $Id: xcatch.c,v 1.2 1998/12/16 00:10:58 mdw Exp $
+ * $Id: xcatch.c,v 1.9 2002/01/13 14:43:27 mdw Exp $
*
* Catch input and trap it in an X window
*
/*----- Revision history --------------------------------------------------*
*
* $Log: xcatch.c,v $
+ * Revision 1.9 2002/01/13 14:43:27 mdw
+ * Kill some spurious warnings. Track @msg@ interface change.
+ *
+ * Revision 1.8 1999/06/19 23:42:55 mdw
+ * Improve signal handling. Fix options parsing to POSIX order only.
+ *
+ * Revision 1.7 1999/05/21 22:09:19 mdw
+ * Take advantage of new dynamic string macros.
+ *
+ * Revision 1.6 1999/05/19 20:41:15 mdw
+ * Track gratuitous change in mdwopt interface.
+ *
+ * Revision 1.5 1999/05/05 18:55:18 mdw
+ * Block SIGCHLD around the `fork' call to prevent a race.
+ *
+ * Revision 1.4 1999/03/24 22:23:57 mdw
+ * Improve display for large files. Keep newly added material in view if
+ * scrolled to bottom of window.
+ *
+ * Revision 1.3 1998/12/20 17:19:16 mdw
+ * Return exit status of child process, rather than always returning
+ * success.
+ *
* Revision 1.2 1998/12/16 00:10:58 mdw
* Fix tabbing in help text.
*
static unsigned int flags;
-enum {
- f_closed = 1,
- f_bogus = 2
-};
+#define f_closed 1u
+#define f_bogus 2u
-GtkWidget *textbox = 0;
-GdkFont *font;
+static GtkWidget *textbox = 0;
+static GdkFont *font;
+
+static pid_t kid = -1;
+static int status;
/*----- Main code ---------------------------------------------------------*/
static void ready(gpointer data, gint fd, GdkInputCondition c)
{
char buf[1024];
- int count;
+ int doscroll = 1;
+ GtkText *t = 0;
+ GtkAdjustment *va = 0;
- /* --- Read the next buffer of data --- */
+ /* --- If not ready to read then go away --- */
if (!(c & GDK_INPUT_READ))
return;
- count = read(fd, buf, sizeof(buf));
- /* --- Decide what to do --- */
+ /* --- Decide whether to scroll the window --- */
- if (count < 0) {
- msg(":~OK", "error reading data: %s", strerror(errno));
- exit(EXIT_FAILURE);
+ if (textbox) {
+ t = GTK_TEXT(textbox);
+ va = t->vadj;
+ if (va->value + va->page_size < va->upper)
+ doscroll = 0;
+ gtk_text_freeze(t);
}
- if (count == 0) {
- close(fd);
- if (textbox)
- flags |= f_closed;
- else
- gtk_main_quit();
- return;
- }
+ /* --- Read data into the buffer --- *
+ *
+ * This is a bit of a mess.
+ */
- /* --- If there's no output window, create one --- */
-
- if (!textbox) {
- GtkWidget *win;
- GtkWidget *t;
- GtkWidget *w;
-
- win = gtk_dialog_new();
- gtk_signal_connect(GTK_OBJECT(win), "destroy",
- GTK_SIGNAL_FUNC(killwin), 0);
-
- t = gtk_table_new(2, 2, 0);
- gtk_container_border_width(GTK_CONTAINER(t), 8);
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(win)->vbox), t, 1, 1, 0);
- gtk_widget_show(t);
-
- textbox = gtk_text_new(0, 0);
- gtk_table_attach(GTK_TABLE(t), textbox, 0, 1, 0, 1,
- GTK_EXPAND | GTK_SHRINK | GTK_FILL,
- GTK_EXPAND | GTK_SHRINK | GTK_FILL,
- 0, 0);
- gtk_text_set_editable(GTK_TEXT(textbox), 0);
- gtk_widget_set_usize(textbox, 500, 100);
- gtk_widget_show(textbox);
-
- w = gtk_vscrollbar_new(GTK_TEXT(textbox)->vadj);
- gtk_table_attach(GTK_TABLE(t), w, 1, 2, 0, 1,
- 0, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
- gtk_widget_show(w);
-
- w = gtk_hscrollbar_new(GTK_TEXT(textbox)->hadj);
- gtk_table_attach(GTK_TABLE(t), w, 0, 1, 1, 2,
- GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0, 0);
- gtk_widget_show(w);
-
- gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(win)->action_area), 0);
- w = gtk_button_new_with_label("Dismiss");
- gtk_signal_connect_object(GTK_OBJECT(w), "clicked",
- GTK_SIGNAL_FUNC(gtk_object_destroy),
- GTK_OBJECT(win));
- gtk_box_pack_end(GTK_BOX(GTK_DIALOG(win)->action_area), w, 0, 0, 0);
- GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
- gtk_widget_grab_default(w);
- cancel(GTK_WINDOW(win), w);
- gtk_widget_show(w);
-
- gtk_widget_show(win);
- }
+ for (;;) {
+ int r = read(fd, buf, sizeof(buf));
+
+ /* --- The read failed --- *
+ *
+ * Maybe there's no more data to read. In this case, we get
+ * @EWOULDBLOCK@, indicating it's time to stop and do something else.
+ * Otherwise something serious has happened.
+ */
- /* --- Append the new text --- */
+ if (r < 0) {
+ if (errno == EWOULDBLOCK)
+ break;
+ msg(QUIS, ":~OK", "error reading data: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
- gtk_text_insert(GTK_TEXT(textbox), font, 0, 0, buf, count);
+ /* --- End of file --- *
+ *
+ * If the box is closed, then exit quiety; otherwise wait for it to
+ * go away.
+ */
+
+ if (r == 0) {
+ close(fd);
+ if (textbox)
+ flags |= f_closed;
+ else
+ gtk_main_quit();
+ break;
+ }
+
+ /* --- If there's no output window, create one --- */
+
+ if (!textbox) {
+ GtkWidget *win;
+ GtkWidget *tbl;
+ GtkWidget *w;
+
+ win = gtk_dialog_new();
+ gtk_window_set_policy(GTK_WINDOW(win), 1, 1, 0);
+ gtk_signal_connect(GTK_OBJECT(win), "destroy",
+ GTK_SIGNAL_FUNC(killwin), 0);
+
+ tbl = gtk_table_new(2, 2, 0);
+ gtk_container_border_width(GTK_CONTAINER(tbl), 8);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(win)->vbox), tbl, 1, 1, 1);
+ gtk_widget_show(tbl);
+
+ textbox = gtk_text_new(0, 0);
+ t = GTK_TEXT(textbox);
+ va = t->vadj;
+ gtk_table_attach(GTK_TABLE(tbl), textbox, 0, 1, 0, 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ 0, 0);
+ gtk_text_set_editable(t, 0);
+ gtk_widget_set_usize(textbox, 500, 300);
+ gtk_text_freeze(t);
+ gtk_widget_show(textbox);
+
+ w = gtk_vscrollbar_new(va);
+ gtk_table_attach(GTK_TABLE(tbl), w, 1, 2, 0, 1,
+ 0, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show(w);
+
+ w = gtk_hscrollbar_new(t->hadj);
+ gtk_table_attach(GTK_TABLE(tbl), w, 0, 1, 1, 2,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0, 0);
+ gtk_widget_show(w);
+
+ gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(win)->action_area), 0);
+ w = gtk_button_new_with_label("Dismiss");
+ gtk_signal_connect_object(GTK_OBJECT(w), "clicked",
+ GTK_SIGNAL_FUNC(gtk_object_destroy),
+ GTK_OBJECT(win));
+ gtk_box_pack_end(GTK_BOX(GTK_DIALOG(win)->action_area), w, 0, 0, 0);
+ GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(w);
+ cancel(GTK_WINDOW(win), w);
+ gtk_widget_show(w);
+
+ gtk_widget_show(win);
+ }
+
+ gtk_text_insert(t, font, 0, 0, buf, r);
+ }
+
+ if (textbox) {
+ gtk_text_thaw(t);
+ if (doscroll)
+ gtk_adjustment_set_value(va, va->upper - va->page_size);
+ }
}
/* --- Signal handler --- */
static void reap(int sig)
{
- while (waitpid(-1, 0, WNOHANG) > 0)
- ;
+ pid_t k;
+ int s;
+ int e = errno;
+
+ for (;;) {
+ k = waitpid(-1, &s, WNOHANG);
+ if (k <= 0)
+ break;
+ if (k == kid) {
+ if (WIFEXITED(s))
+ status = WEXITSTATUS(s);
+ else
+ status = 127;
+ }
+ }
+ errno = e;
}
/* --- Main program --- */
{ "help", 0, 0, 'h' },
{ "usage", 0, 0, 'u' },
{ "version", 0, 0, 'v' },
- { "file", gFlag_argReq, 0, 'f' },
- { "font", gFlag_argReq, 0, 'F' },
+ { "file", OPTF_ARGREQ, 0, 'f' },
+ { "font", OPTF_ARGREQ, 0, 'F' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "huvf:F:", opt, 0, 0, 0);
+ int i = mdwopt(argc, argv, "+huvf:F:", opt, 0, 0, 0);
if (i < 0)
break;
fd = STDIN_FILENO;
else {
int pfd[2];
- pid_t kid;
struct sigaction sa;
+ sigset_t newmask, oldmask;
/* --- Set up a signal handler --- */
sa.sa_handler = reap;
sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
+ sa.sa_flags = SA_NOCLDSTOP;
+#ifdef SA_RESTART
+ sa.sa_flags |= SA_RESTART;
+#endif
sigaction(SIGCHLD, &sa, 0);
/* --- Start a child program --- */
if (pipe(pfd))
die(1, "couldn't open pipe: %s", strerror(errno));
+
+ sigemptyset(&newmask);
+ sigaddset(&newmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &newmask, &oldmask);
+
kid = fork();
if (kid < 0)
die(1, "couldn't fork: %s", strerror(errno));
if (kid == 0) {
- dstr d;
+ dstr d = DSTR_INIT;
close(pfd[0]);
if (pfd[1] != STDOUT_FILENO)
close(pfd[1]);
execvp(argv[optind], argv + optind);
- dstr_create(&d);
dstr_putf(&d, "%s: couldn't run `%s': %s\n",
QUIS, argv[optind], strerror(errno));
write(STDERR_FILENO, d.buf, d.len);
+ dstr_destroy(&d);
_exit(127);
}
+
+ sigprocmask(SIG_SETMASK, &oldmask, 0);
fd = pfd[0];
close(pfd[1]);
}
}
+ {
+ int f = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, f | O_NONBLOCK);
+ }
+
gdk_input_add(fd, GDK_INPUT_READ, ready, 0);
gtk_main();
- return (0);
+ return (status);
}
/*----- That's all, folks -------------------------------------------------*/