chiark / gitweb /
9a69aa1f81277a07e970ceffad7845e523007c48
[xtoys] / xcatch.c
1 /* -*-c-*-
2  *
3  * $Id: xcatch.c,v 1.3 1998/12/20 17:19:16 mdw Exp $
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 $
32  * Revision 1.3  1998/12/20 17:19:16  mdw
33  * Return exit status of child process, rather than always returning
34  * success.
35  *
36  * Revision 1.2  1998/12/16 00:10:58  mdw
37  * Fix tabbing in help text.
38  *
39  * Revision 1.1  1998/12/15 23:46:50  mdw
40  * New program: captures input and puts it in a window.
41  *
42  */
43
44 /*----- Header files ------------------------------------------------------*/
45
46 #include <errno.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include <sys/types.h>
53 #include <sys/wait.h>
54
55 #include <fcntl.h>
56 #include <unistd.h>
57
58 #include <gtk/gtk.h>
59
60 #include <mLib/dstr.h>
61 #include <mLib/mdwopt.h>
62 #include <mLib/report.h>
63 #include <mLib/quis.h>
64
65 #include <mgLib/cancel.h>
66 #include <mgLib/msg.h>
67
68 /*----- Inportant state ---------------------------------------------------*/
69
70 static unsigned int flags;
71
72 enum {
73   f_closed = 1,
74   f_bogus = 2
75 };
76
77 static GtkWidget *textbox = 0;
78 static GdkFont *font;
79
80 static pid_t kid = -1;
81 static int status;
82
83 /*----- Main code ---------------------------------------------------------*/
84
85 /* --- The window's closed --- */
86
87 static void killwin(GtkWidget *w, gpointer p)
88 {
89   if (flags & f_closed)
90     gtk_main_quit();
91   else
92     textbox = 0;
93 }
94
95 /* --- Some input has arrived --- */
96
97 static void ready(gpointer data, gint fd, GdkInputCondition c)
98 {
99   char buf[1024];
100   int count;
101
102   /* --- Read the next buffer of data --- */
103
104   if (!(c & GDK_INPUT_READ))
105     return;
106   count = read(fd, buf, sizeof(buf));
107
108   /* --- Decide what to do --- */
109
110   if (count < 0) {
111     msg(":~OK", "error reading data: %s", strerror(errno));
112     exit(EXIT_FAILURE);
113   }
114
115   if (count == 0) {
116     close(fd);
117     if (textbox)
118       flags |= f_closed;
119     else
120       gtk_main_quit();
121     return;
122   }
123
124   /* --- If there's no output window, create one --- */
125   
126   if (!textbox) {
127     GtkWidget *win;
128     GtkWidget *t;
129     GtkWidget *w;
130
131     win = gtk_dialog_new();
132     gtk_signal_connect(GTK_OBJECT(win), "destroy",
133                        GTK_SIGNAL_FUNC(killwin), 0);
134
135     t = gtk_table_new(2, 2, 0);
136     gtk_container_border_width(GTK_CONTAINER(t), 8);
137     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(win)->vbox), t, 1, 1, 0);
138     gtk_widget_show(t);
139
140     textbox = gtk_text_new(0, 0);
141     gtk_table_attach(GTK_TABLE(t), textbox, 0, 1, 0, 1,
142                      GTK_EXPAND | GTK_SHRINK | GTK_FILL,
143                      GTK_EXPAND | GTK_SHRINK | GTK_FILL,
144                      0, 0);
145     gtk_text_set_editable(GTK_TEXT(textbox), 0);
146     gtk_widget_set_usize(textbox, 500, 100);
147     gtk_widget_show(textbox);
148
149     w = gtk_vscrollbar_new(GTK_TEXT(textbox)->vadj);
150     gtk_table_attach(GTK_TABLE(t), w, 1, 2, 0, 1,
151                      0, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
152     gtk_widget_show(w);
153
154     w = gtk_hscrollbar_new(GTK_TEXT(textbox)->hadj);
155     gtk_table_attach(GTK_TABLE(t), w, 0, 1, 1, 2,
156                      GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0, 0);
157     gtk_widget_show(w);
158
159     gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(win)->action_area), 0);
160     w = gtk_button_new_with_label("Dismiss");
161     gtk_signal_connect_object(GTK_OBJECT(w), "clicked",
162                               GTK_SIGNAL_FUNC(gtk_object_destroy),
163                               GTK_OBJECT(win));
164     gtk_box_pack_end(GTK_BOX(GTK_DIALOG(win)->action_area), w, 0, 0, 0);
165     GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
166     gtk_widget_grab_default(w);
167     cancel(GTK_WINDOW(win), w);
168     gtk_widget_show(w);
169
170     gtk_widget_show(win);
171   }
172
173   /* --- Append the new text --- */
174
175   gtk_text_insert(GTK_TEXT(textbox), font, 0, 0, buf, count);
176 }
177
178 /* --- Signal handler --- */
179
180 static void reap(int sig)
181 {
182   pid_t k;
183   int s;
184
185   for (;;) {
186     k = waitpid(-1, &s, WNOHANG);
187     if (k <= 0)
188       break;
189     if (k == kid) {
190       if (WIFEXITED(s))
191         status = WEXITSTATUS(s);
192       else
193         status = 127;
194     }
195   }
196 }
197
198 /* --- Main program --- */
199
200 static void version(FILE *fp)
201 {
202   fprintf(fp, "%s (xtoys version " VERSION ")\n", QUIS);
203 }
204
205 static void usage(FILE *fp)
206 {
207   fprintf(fp, "Usage: %s [-f file] [-F font] [command [args...]]\n", QUIS);
208 }
209
210 int main(int argc, char *argv[])
211 {
212   int fd = -1;
213
214   ego(argv[0]);
215
216   gtk_init(&argc, &argv);
217
218   for (;;) {
219     static struct option opt[] = {
220       { "help",         0,              0,      'h' },
221       { "usage",        0,              0,      'u' },
222       { "version",      0,              0,      'v' },
223       { "file",         gFlag_argReq,   0,      'f' },
224       { "font",         gFlag_argReq,   0,      'F' },
225       { 0,              0,              0,      0 }
226     };
227     int i = mdwopt(argc, argv, "huvf:F:", opt, 0, 0, 0);
228
229     if (i < 0)
230       break;
231
232     switch (i) {
233       case 'h':
234         version(stdout);
235         fputc('\n', stdout);
236         usage(stdout);
237         fputs(
238 "\n"
239 "Catches input from a pipe or other source, and captures it in a window.\n"
240 "Nothing is displayed if there's no input.\n"
241 "\n"
242 "Options provided:\n"
243 "\n"
244 "-h, --help             Display this help text\n"
245 "-u, --usage            Display a quick usage summary\n"
246 "-v, --version          Display the version number\n"
247 "-f, --file=FILE\t      Read input from the named file\n"
248 "-F, --font=FONT\t      Display output in the named font\n",
249           stdout);
250         exit(0);
251         break;
252       case 'u':
253         usage(stdout);
254         exit(0);
255         break;
256       case 'v':
257         version(stdout);
258         exit(0);
259         break;
260       case 'f':
261         if ((fd = open(optarg, O_RDONLY)) < 0) {
262           die(1, "couldn't open file: %s", strerror(errno));
263           exit(1);
264         }
265         break;
266       case 'F':
267         font = gdk_font_load(optarg);
268         break;
269       default:
270         flags |= f_bogus;
271         break;
272     }
273   }
274
275   if (flags & f_bogus) {
276     usage(stderr);
277     exit(1);
278   }
279
280   if (fd == -1) {
281     if (optind == argc)
282       fd = STDIN_FILENO;
283     else {
284       int pfd[2];
285       struct sigaction sa;
286
287       /* --- Set up a signal handler --- */
288
289       sa.sa_handler = reap;
290       sigemptyset(&sa.sa_mask);
291       sa.sa_flags = 0;
292       sigaction(SIGCHLD, &sa, 0);
293
294       /* --- Start a child program --- */
295
296       if (pipe(pfd))
297         die(1, "couldn't open pipe: %s", strerror(errno));
298       kid = fork();
299       if (kid < 0)
300         die(1, "couldn't fork: %s", strerror(errno));
301       if (kid == 0) {
302         dstr d;
303
304         close(pfd[0]);
305         if (pfd[1] != STDOUT_FILENO)
306           dup2(pfd[1], STDOUT_FILENO);
307         if (pfd[1] != STDERR_FILENO)
308           dup2(pfd[1], STDERR_FILENO);
309         if (pfd[1] != STDOUT_FILENO && pfd[1] != STDERR_FILENO)
310           close(pfd[1]);
311         execvp(argv[optind], argv + optind);
312
313         dstr_create(&d);
314         dstr_putf(&d, "%s: couldn't run `%s': %s\n",
315                   QUIS, argv[optind], strerror(errno));
316         write(STDERR_FILENO, d.buf, d.len);
317         _exit(127);
318       }
319       fd = pfd[0];
320       close(pfd[1]);
321     }
322   }
323
324   gdk_input_add(fd, GDK_INPUT_READ, ready, 0);
325   gtk_main();
326   return (status);
327 }
328
329 /*----- That's all, folks -------------------------------------------------*/