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