1 #ifdef WIN32
2 #include <windows.h>
3
4 #elif defined(__APPLE__)
5 #include <unistd.h>
6 #include <util.h>
7
8 #else //X11
9 #include <unistd.h>
10 #ifdef sun
11 #include <stropts.h>
12 #endif
13
14 #endif
15 #include <fcntl.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <signal.h>
20 #include <ctype.h>
21 #include <FL/x.H>
22 #include <FL/Fl.H>
23 #include <FL/Fl_Double_Window.H>
24 #include <FL/Fl_Box.H>
25 #include <FL/Fl_Return_Button.H>
26 #include <FL/Fl_Multiline_Output.H>
27 #include <FL/Fl_Scrollbar.H>
28 #include <FL/fl_ask.H>
29
30
31 enum {running, completed, aborted, closed};
32
33 struct listener_data {
34 int status;
35 Fl_Multiline_Output *o;
36 #ifdef WIN32
37 HANDLE pid;
38 #else
39 pid_t pid;
40 #endif
41 int fd;
42 Fl_Button *ok, *interrupt;
43 Fl_Scrollbar *bar;
44 int nl; // current number of complete lines in buffer
45 int view_nl; // max no of visible lines in Fl_Multiline_Output
46 char *from; // start in buffer of displayed text during running
47 char *next; // just after last displayed buffer byte
48 char *eol; // points to where new bytes will be put in buffer
49 bool modified;
50 char buffer[10000000]; // buffer to hold text. Start from beginning again when more text than buffer size
51 };
52
53 typedef void (*thread_function_t) (void *);
54 extern int fl_create_thread(thread_function_t f, void* p, unsigned stack_size_KB);
55
56
inter_callback(Fl_Widget * o,struct listener_data * data)57 static void inter_callback(Fl_Widget *o, struct listener_data *data)
58 {
59 data->status = aborted;
60 #ifdef WIN32
61 BOOL ok = TerminateProcess(data->pid, 0);
62 if (ok) {
63 while ( WaitForSingleObject(data->pid, 0) != WAIT_OBJECT_0) Fl::wait(0.1);
64 }
65 #else
66 kill(data->pid, SIGKILL);
67 #endif
68 }
69
ok_callback(Fl_Widget * o,struct listener_data * data)70 static void ok_callback(Fl_Widget *o, struct listener_data *data)
71 {
72 data->status = closed;
73 }
74
bar_callback(Fl_Scrollbar * wgt,Fl_Multiline_Output * o)75 static void bar_callback(Fl_Scrollbar *wgt, Fl_Multiline_Output *o)
76 {
77 int first_line = wgt->value();
78 const char *p = o->value();
79 do {
80 if(*p == '\n') first_line--;
81 p++;
82 }
83 while(*p != 0 && first_line > 0);
84 o->position(p - o->value());
85 o->redraw();
86 }
87
88
copy_callback(Fl_Widget * wgt,Fl_Multiline_Output * o)89 static void copy_callback(Fl_Widget *wgt, Fl_Multiline_Output *o)
90 {
91 o->position(o->size(), 0);
92 o->copy(1);
93 }
94
95
ext_prog_listener(struct listener_data * data)96 void ext_prog_listener(struct listener_data *data)
97 {//transmits data from fd to FLTK viewer with CR emulation
98 char *p;
99 static char fd_buff[2000];
100 int l;
101 while (data->status == running) {
102 l = read(data->fd, fd_buff, sizeof(fd_buff));
103 if (l <= 0) break;
104 Fl::lock();
105 if (data->next + l >= data->buffer + sizeof(data->buffer)) {
106 data->next = data->from = data->eol = data->buffer;
107 data->nl = 0;
108 }
109 for (p = fd_buff; p < fd_buff + l; p++) {
110 if (*p == '\r') {
111 if (p+1 >= fd_buff + l || *(p+1) != '\n') {
112 while(data->eol > data->buffer && *(data->eol - 1) != '\n')
113 (data->eol)--;
114 }
115 }
116 else {
117 if (*p == '\n') {
118 data->eol = data->next;
119 data->nl++;
120 if(data->nl >= data->view_nl) data->from = strchr(data->from, '\n') + 1;
121 }
122 *(data->eol++) = *p;
123 if(data->eol > data->next) data->next = data->eol;
124 }
125 }
126 data->modified = true;
127 Fl::unlock();
128 }
129 Fl::lock();
130 if (data->status == running) {
131 data->o->static_value(data->buffer, data->next - data->buffer);
132 data->o->position(data->next - data->buffer, data->next - data->buffer);
133 data->o->redraw();
134 data->bar->value(data->nl, data->view_nl, 0, data->nl);
135 data->bar->show();
136 if (!data->ok->active()) {
137 data->ok->activate();
138 data->interrupt->label("Cancel");
139 data->status = completed;
140 }
141 }
142 Fl::unlock();
143 }
144
145
create_and_run_pseudoterminal(const char * label,struct listener_data * data)146 Fl_Window *create_and_run_pseudoterminal(const char *label, struct listener_data *data)
147 #define FL_min(a,b) ( (a) < (b) ? (a):(b) )
148 {
149 static char message[100];
150 Fl_Double_Window *w = new Fl_Double_Window( FL_min(700, Fl::w()), FL_min(600, Fl::h()-22 ) );
151 w->xclass("Terminal");
152 w->label(label);
153 w->callback((Fl_Callback*)inter_callback, data);
154 Fl_Multiline_Output *o = new Fl_Multiline_Output(0, 3, w->w() - 15, w->h() - 40);
155 o->textfont(FL_COURIER);
156 o->textsize(12);
157 o->maximum_size(5000000);
158 data->bar = new Fl_Scrollbar(o->x() + o->w(), o->y(), 15, o->h());
159 data->bar->callback((Fl_Callback*)bar_callback, o);
160 data->view_nl = o->h() / o->textsize();
161 #ifdef WIN32
162 data->view_nl = (int)(data->view_nl * 0.80);
163 #endif
164 data->bar->value(0, data->view_nl, 0, data->view_nl);
165 data->bar->hide();
166 sprintf(message, "Wait for %s completion", label);
167 Fl_Box *w_message = new Fl_Box(0, o->y() + o->h() + 3, w->w(), 20, message);
168 Fl_Button *w_copy = new Fl_Button(w->w()/2 - 30, w_message->y(), 60, 20, "Copy all");
169 w_copy->hide();
170 w_copy->callback((Fl_Callback*)copy_callback, o);
171 data->status = running;
172 data->o = o;
173 data->nl = 0;
174 data->from = data->next = data->eol = data->buffer;
175 Fl_Button *interrupt = new Fl_Button(5, w->h() - 35, 70, 30, "Interrupt");
176 interrupt->callback((Fl_Callback*)inter_callback, data);
177 interrupt->labelfont(FL_HELVETICA_BOLD);
178 Fl_Return_Button *ok = new Fl_Return_Button(w->w() - 60, interrupt->y(), 50, 30, "OK");
179 ok->callback((Fl_Callback*)ok_callback, data);
180 ok->labelfont(FL_HELVETICA_BOLD);
181 ok->deactivate();
182 data->ok = ok;
183 data->interrupt = interrupt;
184 data->modified = false;
185 w->end();
186 w->position( (Fl::w() - w->w())/2, (Fl::h() - w->h())/2 );
187 Fl_Box *r = new Fl_Box(interrupt->x() + interrupt->w(), o->y(), ok->x() - interrupt->x() - interrupt->w(), o->h());
188 w->resizable(r);
189 w->show();
190 #ifndef MICRO
191 w->hotspot(w);
192 #endif
193 fl_create_thread( (thread_function_t)ext_prog_listener, data, 0);
194 while (data->status == running) {
195 if (data->modified) {
196 data->modified = false;
197 data->o->static_value(data->from, data->next - data->from);
198 data->o->redraw();
199 }
200 Fl::wait(0.1);
201 }
202 w_message->hide();
203 w_copy->show();
204 #ifdef WIN32
205 _close(data->fd);
206 #else
207 close(data->fd);
208 #endif
209 return w;
210 }
211
212
run_external_prog_in_pseudoterm(char * cmd,const char * dialogfname,const char * label)213 int run_external_prog_in_pseudoterm(char *cmd, const char *dialogfname, const char *label)
214 /*
215 cmd can be:
216 prog arg1 arg2 ...
217 or:
218 "prog" arg1 "arg2" ... > outfile
219 and:
220 prog can also use file named in dialogfname as stdin (NULL if no such file)
221 label: short name of operation that is run
222 returns 0 iff cmd completed correctly
223 */
224 {
225 static struct listener_data data;
226 static bool inuse = 0;
227 if(inuse) {
228 fl_alert("Operation %s is not possible while another alignment/tree-building is in progress",
229 label);
230 return 1;
231 }
232 inuse = 1;
233 Fl_Window *w;
234 #ifdef WIN32
235 HANDLE ChildInput, ChildStdoutRd, ChildStdoutWr, stdouthandle = NULL;
236 SECURITY_ATTRIBUTES saAttr;
237 PROCESS_INFORMATION pi;
238 STARTUPINFOW si;
239 char *p, *q;
240
241 if(dialogfname != NULL) {
242 FILE *in = fopen(dialogfname, "r");
243 if(in == NULL) {
244 inuse = 0;
245 return 1;
246 }
247 ChildInput = (HANDLE)_get_osfhandle(fileno(in));
248 }
249 p = cmd;//is there stdout redirection in cmd ?
250 if(*p == '"') p = strchr(p+1, '"') + 1;
251 p = strchr(p, '>');
252 if(p != NULL) {
253 *p = 0;
254 p++;
255 while(*p == ' ') p++;
256 if (*p == '"') {
257 q = strchr(++p, '"');
258 if (q) *q = 0;
259 }
260 char *stdoutfname = strdup(p);
261 FILE *out = fopen(stdoutfname, "w");
262 free(stdoutfname);
263 if(out != NULL) stdouthandle = (HANDLE)_get_osfhandle(fileno(out));
264 }
265 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
266 saAttr.bInheritHandle = TRUE;
267 saAttr.lpSecurityDescriptor = NULL;
268 CreatePipe(&ChildStdoutRd, &ChildStdoutWr, &saAttr, 0/*suggested buffer size*/);
269 DWORD written;
270 WriteFile(ChildStdoutWr, "Running ", 8, &written, NULL);
271 WriteFile(ChildStdoutWr, cmd, strlen(cmd), &written, NULL);
272 WriteFile(ChildStdoutWr, "\n ", 1, &written, NULL);
273 SetHandleInformation(ChildStdoutRd, HANDLE_FLAG_INHERIT, 0);
274 ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
275 ZeroMemory( &si, sizeof(STARTUPINFOW) );
276 si.cb = sizeof(STARTUPINFOW);
277 si.hStdError = ChildStdoutWr;
278 si.hStdOutput = stdouthandle ? stdouthandle : ChildStdoutWr;
279 if(dialogfname != NULL) si.hStdInput = ChildInput;
280 else si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
281 si.dwFlags |= STARTF_USESTDHANDLES;
282 //conversion of utf-8 cmd into a WCHAR string
283 int wlen = MultiByteToWideChar(CP_UTF8, 0, cmd, -1, NULL, 0);
284 WCHAR *wcmd = new WCHAR[wlen];
285 MultiByteToWideChar(CP_UTF8, 0, cmd, -1, wcmd, wlen);
286 int retval = CreateProcessW(NULL, wcmd, 0,0,TRUE,CREATE_NO_WINDOW,0,0, &si, &pi);
287 delete wcmd;
288 CloseHandle(ChildStdoutWr);
289 if(stdouthandle != NULL) CloseHandle(stdouthandle);
290 if(dialogfname != NULL) CloseHandle(ChildInput);
291 if(retval == 0) {
292 CloseHandle(ChildStdoutRd);
293 inuse = 0;
294 return 1;
295 }
296 CloseHandle(pi.hThread);
297 data.pid = pi.hProcess;
298 data.fd = _open_osfhandle(
299 # ifdef _WIN64
300 (intptr_t)
301 # else
302 (long)
303 # endif
304 ChildStdoutRd, _O_RDONLY);
305 w = create_and_run_pseudoterminal(label, &data);
306 CloseHandle(pi.hProcess);
307 #else
308 // creates a pseudoterminal
309 pid_t pid;
310 int masterfd;
311 char *slavename;
312 #if defined(__APPLE__)
313 char slavename2[50];
314 if (fl_mac_os_version < 100500) { //special for Mac OS X 10.4 + i386
315 slavename = slavename2;
316 pid = forkpty(&masterfd, slavename2, NULL, NULL);
317 }
318 else
319 #endif
320 {
321 masterfd = posix_openpt(O_RDWR|O_NOCTTY);
322 grantpt(masterfd);
323 unlockpt(masterfd);
324 slavename = ptsname(masterfd);
325 if(masterfd == -1 || slavename == NULL) {
326 inuse = 0;
327 return 1;
328 }
329 pid = fork();
330 }
331 if(pid == -1) {
332 inuse = 0;
333 return 1;
334 }
335 else if(pid > 0) {// the parent
336 data.pid = pid;
337 data.fd = masterfd;
338 w = create_and_run_pseudoterminal(label, &data);
339 }
340 else {//the child
341 // when dialogfname = NULL, make stdin a tty, useful for kalign
342 freopen( (dialogfname ? dialogfname : slavename), "r", stdin);
343 freopen(slavename, "a", stderr);
344 #ifdef sun
345 ioctl(fileno(stderr), I_PUSH, "ptem");
346 // ioctl(fileno(stderr), I_PUSH, "ldterm");
347 // ioctl(fileno(stderr), I_PUSH, "ttcompat");
348 #endif
349 fputs(cmd, stderr); fputc('\n', stderr); fflush(stderr);//show command
350 //decode progname
351 char *p, *q, *progpath, **argv;
352 int argc, newout = -1;
353 close(masterfd);
354 p = cmd;
355 while(*p == ' ') p++;
356 if(*p == '"') q = strchr(++p, '"');
357 else {
358 q = strchr(p, ' ');
359 if(q == NULL) q = p + strlen(p);
360 }
361 progpath = new char[q-p+1];
362 memcpy(progpath, p, q-p); progpath[q-p] = 0;
363 //goto end of progname
364 p = q;
365 if(*p == '"') p++;
366 // take care of stdout
367 // bug in Mac OS X 10.4 i386 requires us to close stdout and reopen it on fd = 1
368 fclose(stdout);
369 if((q = strchr(p, '>')) != NULL) {//open desired stdout file
370 char stdoutfile[200];
371 FILE *f;
372 *q = 0;
373 do q++; while(*q == ' ');
374 if (*q == '"') q++;
375 strcpy(stdoutfile, q);
376 q = stdoutfile + strlen(stdoutfile) - 1;
377 while (q > stdoutfile && (*q == ' ' || *q == '"') ) q--;
378 *(q + 1) = 0;
379 f = fopen(stdoutfile, "w");
380 if(f) newout = fileno(f);
381 }
382 else {
383 newout = open(slavename, O_WRONLY|O_APPEND);
384 }
385 if(newout != 1 && newout != -1) {
386 dup2(newout, 1);
387 }
388 //count args
389 argc = 1;
390 q = p - 1;
391 while (*++q) {
392 if (isspace(*q)) continue;
393 argc++;
394 if (*q == '"') {
395 q = strchr(q + 1, '"');
396 if (!q) break;
397 }
398 else {
399 do q++; while ( *q && !isspace(*q) );
400 q--;
401 }
402 }
403 //put args in argv array
404 argv = (char **)malloc((argc + 1)*sizeof(char *));
405 argv[0] = progpath;
406 argv[argc] = NULL;
407 q = p - 1;
408 int i = 1;
409 while (*++q) {
410 if (isspace(*q)) continue;
411 if (*q == '"') {
412 argv[i++] = ++q;
413 q = strchr(q, '"');
414 if (!q) break;
415 *q = 0;
416 }
417 else {
418 argv[i++] = q;
419 do q++; while ( *q && !isspace(*q) );
420 if (*q == 0) {
421 break;
422 }
423 *q = 0;
424 }
425 }
426 int err = 1; const char *errmess;
427 if(access(progpath, X_OK) != 0) errmess = "executable";
428 else {
429 err = execv(progpath, argv);
430 errmess = "a valid program";
431 }
432 fprintf(stderr, "\n\n\n%s is not %s\n", progpath, errmess);
433 exit(err);
434 }
435 #endif
436 while(data.status == completed) Fl::wait();
437 delete w;
438 if(dialogfname != NULL) remove(dialogfname);
439 inuse = 0;
440 return data.status == aborted;
441 }
442