fixed GTKHTML detection
[k8lowj.git] / src / get_cmd_out.c
blobf4ee4c9bb5ba6dbfe308d1d7cea50a70562f8585
1 /* logjam - a GTK client for LiveJournal.
3 * Functions to run some external program and return its output.
4 * Copyright (C) 2004, Kir Kolyshkin <kir@sacred.ru>
6 * vim: tabstop=4 shiftwidth=4 noexpandtab :
7 */
9 #include "gtk-all.h"
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <sys/signal.h>
19 struct mypopen_t {
20 int fd;
21 pid_t pid;
22 gboolean cancelled;
25 /* An analogue of popen(command, "r").
26 * A bit more useful since we know child PID.
28 gboolean mypopen_r(const char *command, struct mypopen_t *p)
30 int fds[2];
31 /* parent fd is fds[0], child fd is fds[1] */
33 if (pipe(fds) == -1)
34 return FALSE;
36 if ((p->pid = fork()) == -1)
38 close(fds[0]);
39 close(fds[1]);
40 return FALSE;
43 if (p->pid == 0) {
44 /* Child */
45 close(fds[0]);
46 dup2(fds[1], 1);
47 close(fds[1]);
48 execl("/bin/sh", "sh", "-c", command, NULL);
49 /* if execl failed, exit with code 127 */
50 exit(127);
52 /* Parent */
53 close(fds[1]);
54 p->fd = fds[0];
55 return TRUE;
58 int mypclose(const struct mypopen_t *p)
60 int ret, status;
62 /* fprintf(stderr, "mypclose: pid=%lu, fd=%d\n", p->pid, p->fd); */
64 close(p->fd);
65 do {
66 ret = waitpid(p->pid, &status, 0);
67 } while ((ret == -1) && (errno == EINTR));
69 /* fprintf(stderr, "waitpid returned %d; errno = %d; "
70 "status = %d; exit = %d; signaled = %d\n",
71 ret, errno, status, WEXITSTATUS(status),
72 WIFSIGNALED(status)); */
73 if (ret == -1)
74 return -1;
75 return WEXITSTATUS(status);
78 /* Callback function for "Cancel" button of 'command is running' window
80 static void
81 cancel_cb(struct mypopen_t *p) {
82 p->cancelled = TRUE;
83 if (p->pid)
84 kill(p->pid, SIGTERM);
85 /* FIXME: should we send SIGKILL some time later
86 * if process still exists?
90 /* Dialog showing that command is being executed.
91 * Click on "Cancel" should kill the process which pid is p->pid
93 GtkWidget *command_is_running(const char * command, struct mypopen_t *p,
94 GtkWindow *parent) {
95 GtkWidget *win, *vbox, *frame, *text, *cancel;
97 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
98 gtk_window_set_title(GTK_WINDOW(win), _("Command is running..."));
99 gtk_window_set_transient_for(GTK_WINDOW(win), parent);
100 gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
101 gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DIALOG);
102 gtk_window_set_modal(GTK_WINDOW(win), TRUE);
103 gtk_window_set_default_size(GTK_WINDOW(win), 250, -1);
104 gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);
106 frame = gtk_frame_new(NULL);
107 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
108 gtk_container_add(GTK_CONTAINER(win), frame);
110 vbox = gtk_vbox_new(FALSE, 5);
111 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
112 gtk_container_add(GTK_CONTAINER(frame), vbox);
114 text = gtk_label_new(_("Command is running..."));
115 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
117 cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
118 gtk_box_pack_start(GTK_BOX(vbox), cancel, FALSE, FALSE, 0);
119 g_signal_connect_swapped(G_OBJECT(cancel), "clicked",
120 G_CALLBACK(cancel_cb), p);
122 gtk_widget_show_all(win);
123 return win;
126 /* Callback function for gtk_input_add_full(). Reads data from the
127 * file descriptor. Calls gtk_quit if there's nothing to read.
129 void pipe_read_cb(gpointer data, gint source, GdkInputCondition condition) {
130 gchar buf[BUFSIZ];
131 ssize_t c;
133 c = read(source, buf, sizeof(buf));
134 if (c == 0) /* EOF */
135 gtk_main_quit();
136 else
137 g_string_append_len((GString *)data, buf, c);
140 GString * get_command_output(const char *command, GError **err,
141 GtkWindow *parent){
142 GtkWidget *win;
143 GString *output;
144 int ret;
145 guint evid;
146 struct mypopen_t pd;
148 pd.cancelled = FALSE;
149 if (mypopen_r(command, &pd) == FALSE) {
150 g_set_error(err, 0, 0, _("command failed: %s"),
151 g_strerror(errno));
152 return NULL;
155 /* fprintf(stderr, "before read: pid=%lu, fd=%d\n", pd.pid, pd.fd); */
156 output = g_string_sized_new(BUFSIZ);
157 evid = gtk_input_add_full(pd.fd, GDK_INPUT_READ,
158 pipe_read_cb, NULL, output, NULL);
160 win = command_is_running(command, &pd, parent);
161 gtk_main();
163 gtk_input_remove(evid);
164 gtk_widget_destroy(win);
165 ret = mypclose(&pd);
166 if (pd.cancelled)
168 g_string_free(output, TRUE);
169 return NULL;
171 /* Positive 'ret' values are command exit code, while negative one
172 * means execution error or termination.
173 * Let's ignore all non-zero exit codes but 127, since 127
174 * means "command not found" from sh. Ugly hack?
176 if (ret < 0) {
177 g_string_free(output, TRUE);
178 g_set_error(err, 0, 0, _("command failed: %s"),
179 g_strerror(errno));
180 return NULL;
181 } else if (ret == 127)
183 g_string_free(output, TRUE);
184 /* Set err->code to 127 so caller will know that command
185 * was not found. It is probably a spelling error
186 * in command string and in this case caller should not
187 * close a dialog asking for command name, but rather let
188 * the user correct it (or click "Cancel").
190 g_set_error(err, 0, 127, _("command not found"));
191 return NULL;
193 return output;