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