1 /****************************************************************************
2 **
3 *W  pty.c                       XGAP source                      Frank Celler
4 **
5 **
6 *Y  Copyright 1995-1997,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
7 *Y  Copyright 1997,       Frank Celler,                 Huerth,       Germany
8 **
9 **  This file contains all the code for handling pseudo ttys.  'GetMasterPty'
10 **  is based on code from 'xterm'.
11 **
12 **  GAP is started in a special mode that will mask special characters.  The
13 **  following '@' sequences produced by GAP are recoginzed:
14 **
15 **    'pX.'          		package mode version X
16 **    '@'			a single '@'
17 **    'A'..'Z'			a control character
18 **    '1','2','3','4','5','6'	full garbage collection information
19 **    '!','"','#','$','%','&'   partial garbage collection information
20 **    'e'              		gap is waiting for error input
21 **    'c'              		completion started
22 **    'f'              		error output
23 **    'h'              		help started
24 **    'i'              		gap is waiting for input
25 **    'm'             		end of 'Exec'
26 **    'n'              		normal output
27 **    'r'              		the current input line follows
28 **    'sN'             		ACK for '@yN'
29 **    'w'              		a window command follows
30 **    'x'              		the current input line is empty
31 **    'z' 			start of 'Exec'
32 */
33 #include    "utils.h"
34 
35 #include    "gaptext.h"
36 #include    "xcmds.h"
37 #include    "xgap.h"
38 
39 #include    "pty.h"
40 
41 #ifdef HAVE_OPENPTY
42   #if defined(HAVE_UTIL_H)
43     #include <util.h>     /* for openpty() on Mac OS X, OpenBSD and NetBSD */
44   #elif defined(HAVE_LIBUTIL_H)
45     #include <libutil.h>  /* for openpty() on FreeBSD */
46   #elif defined(HAVE_PTY_H)
47     #include <pty.h>      /* for openpty() on Cygwin, Interix, OSF/1 4 and 5 */
48   #endif
49 #endif
50 
51 
52 /****************************************************************************
53 **
54 
55 *F * * * * * * * * * * * * * * local variables * * * * * * * * * * * * * * *
56 */
57 
58 
59 /****************************************************************************
60 **
61 
62 *V  GapPID  . . . . . . . . . . . . . . . . . . . . . . . . gap subprocess id
63 */
64 static int GapPID = -1;
65 
66 
67 /****************************************************************************
68 **
69 *V  FromGap . . . . . . . . . . . . . . . . . . . . . . for messages from gap
70 */
71 static int FromGap;
72 
73 
74 /****************************************************************************
75 **
76 *V  ToGap . . . . . . . . . . . . . . . . . . . . . . . . for messages to gap
77 */
78 static int ToGap;
79 
80 
81 /* * * * * * * * * * * * * *  global variables * * * * * * * * * * * * * * */
82 
83 
84 /****************************************************************************
85 **
86 
87 *V  QuitGapCtrlD  . . . . . . . . . . . . . . . . . . . . . . . quit on CTR-D
88 */
89 Boolean QuitGapCtrlD = FALSE;
90 
91 
92 /****************************************************************************
93 **
94 *V  ScreenSizeBuffer  . . . . . . . . . . . . . .  screen size change command
95 */
96 char ScreenSizeBuffer[1024] = { 0 };
97 
98 
99 /****************************************************************************
100 **
101 *V  ExecRunning . . . . . . . . . . . . . . . . . .  external program running
102 */
103 Boolean ExecRunning = False;
104 
105 
106 /****************************************************************************
107 **
108 
109 *F * * * * * * * * * * * *  communication with GAP * * * * * * * * * * * * *
110 */
111 
112 
113 /****************************************************************************
114 **
115 
116 *F  ReadGap( <line>, <len> )  . . . . . . . . . . . . . . . . read gap output
117 */
118 #ifdef DEBUG_ON
119 
READ_GAP(file,where,line,len)120 Int READ_GAP ( file, where, line, len )
121     String	file;
122     Int         where;
123     String      line;
124     Int         len;
125 {
126     Int         n;
127     Int         old;
128 
129     if ( Debug & D_COMM )
130     {
131 	printf( "%04d:%s: ReadGap( buf, %d ) = ", where, file, len );
132 	fflush( stdout );
133     }
134     if ( len < 0 )
135     {
136         len = read( FromGap, line, -len );
137 	if ( Debug & D_COMM )
138 	{
139 	    if ( len == -1 )
140 		fprintf( stdout, "-1: no input\n" );
141 	    else
142 	    {
143 		fprintf( stdout, "%d: '", len );
144 		fwrite( line, 1, len, stdout );
145 		fprintf( stdout, "'\n" );
146 	    }
147 	    fflush( stdout );
148 	}
149 	return len;
150     }
151     else
152     {
153         old = len;
154         while ( 0 < len )
155         {
156             while ( ( n = read( FromGap, line, len ) ) < 0 )
157 		;
158             line = line + n;
159             len  = len - n;
160         }
161 	if ( Debug & D_COMM )
162 	{
163 	    fprintf( stdout, "%d: '", old );
164 	    fwrite( line-old, 1, old, stdout );
165 	    fprintf( stdout, "'\n" );
166 	    fflush( stdout );
167 	}
168         return old;
169     }
170 }
171 
172 #else
173 
ReadGap(line,len)174 Int ReadGap ( line, len )
175     String	line;
176     Int         len;
177 {
178     Int         n;
179     Int         old;
180 
181     if ( len < 0 )
182         return read( FromGap, line, -len );
183     else
184     {
185         old = len;
186         while ( 0 < len )
187         {
188             while ( ( n = read( FromGap, line, len ) ) < 0 )
189 		;
190             line = line + n;
191             len  = len - n;
192         }
193         return old;
194     }
195 }
196 
197 #endif
198 
199 
200 /****************************************************************************
201 **
202 *F  WriteGap( <line>, <len> ) . . . . . . . . . . . . . . . . write gap input
203 */
204 extern int errno;
205 
206 #ifdef DEBUG_ON
207 
WRITE_GAP(file,where,line,len)208 void WRITE_GAP ( file, where, line, len )
209     String	file;
210     Int		where;
211     String	line;
212     Int		len;
213 {
214     Int         res;
215 
216     if ( Debug & D_COMM )
217     {
218 	printf( "%04d:%s: WriteGap( %d ) = '", where, file, len );
219 	fwrite( line, 1, len, stdout );
220 	fprintf( stdout, "'\n" );
221 	fflush( stdout );
222     }
223     while ( 0 < len )
224     {
225         res = write( ToGap, line, len );
226         if ( res < 0 )
227         {
228 	    if ( errno == EAGAIN )
229 		continue;
230             perror( "WriteGap" );
231             KillGap();
232             exit(1);
233         }
234         len  -= res;
235         line += res;
236     }
237 }
238 
239 #else
240 
WriteGap(line,len)241 void WriteGap ( line, len )
242     String	line;
243     Int         len;
244 {
245     Int         res;
246 
247     while ( 0 < len )
248     {
249         res = write( ToGap, line, len );
250         if ( res < 0 )
251         {
252 	    if ( errno == EAGAIN )
253 		continue;
254             perror( "WriteGap" );
255             KillGap();
256             exit(1);
257         }
258         len  -= res;
259         line += res;
260     }
261 }
262 
263 #endif
264 
265 
266 /****************************************************************************
267 **
268 
269 *V  InBuffer  . . . . . . . . . . . . . . . . . . . . .  buffer of gap output
270 */
271 #define SIZE_BUFFER	16000
272 
273 static struct _in_buf
274 {
275     char    buffer[SIZE_BUFFER];
276     Int     pos;
277     Int     len;
278 }
279 InBuffer;
280 
281 
282 /****************************************************************************
283 **
284 *V  GapBuffer . . . . . . . . . . . . . . . . temporary buffer for 'ReadLine'
285 */
286 static char GapBuffer[SIZE_BUFFER];
287 
288 
289 /****************************************************************************
290 **
291 *V  LastLine  . . . . . . . . . . . . . . . . . . . .  beginning of last line
292 */
293 static Int LastLine;
294 
295 
296 /****************************************************************************
297 **
298 
299 *D  CURRENT( <buf> )  . . . . . . . . . . . . . . . . . . . .  current symbol
300 */
301 #define	CURRENT(buf)	    ((buf).buffer[(buf).pos])
302 
303 
304 /****************************************************************************
305 **
306 *D  READ_CURRENT( <buf> ) . . . . . . . . . . . . . .  consume current symbol
307 */
308 #define	READ_CURRENT(buf)   ((buf).buffer[(buf).pos++])
309 
310 
311 /****************************************************************************
312 **
313 *D  HAS_INPUT( <buf> )  . . . . . . . . . . . . . . . . . . . check for input
314 */
315 #define	HAS_INPUT(buf)	( ((buf).len <= (buf).pos) ? (buf).pos = 0, \
316  			  ((buf).len=ReadGap((buf).buffer,-SIZE_BUFFER))>0 :\
317 			  1 )
318 
319 
320 /****************************************************************************
321 **
322 *D  HAS_BUFFERED( <buf> ) . . . . . . . . . . . . .  check for buffered input
323 */
324 #define HAS_BUFFERED(buf)   ( (buf).pos < (buf).len )
325 
326 
327 /****************************************************************************
328 **
329 *D  LOOK_AHEAD( <buf> )	. . . look ahead if there is enough input, dont check
330 */
331 #define LOOK_AHEAD(buf)	( ((buf).pos+1 < (buf).len ) ? \
332 			  ((buf).buffer)[(buf).pos+1] : '\0' )
333 
334 
335 /****************************************************************************
336 **
337 
338 *F  WaitInput( <buf> )  . . . . . . . . . . . . . . .  wait for one character
339 */
WaitInput(buf)340 void WaitInput ( buf )
341     struct _in_buf    * buf;
342 {
343     Int                 len;
344 
345     if ( buf->len <= buf->pos )
346     {
347 	buf->pos = 0;
348 	ReadGap( buf->buffer, 1 );
349 	len = ReadGap( buf->buffer+1, -(SIZE_BUFFER-1) );
350 	buf->len = (len < 0) ? 1 : len+1;
351     }
352 }
353 
354 
355 /****************************************************************************
356 **
357 *F  WaitInput2( <buf> ) . . . . . . . . . . . . . . . wait for two characters
358 */
WaitInput2(buf)359 void WaitInput2 ( buf )
360     struct _in_buf    * buf;
361 {
362     Int                 len;
363 
364     if ( buf->len <= 1 + buf->pos )
365     {
366 	if ( buf->pos+1 == buf->len )
367 	{
368 	    *buf->buffer = buf->buffer[buf->pos];
369 	    buf->pos = 0;
370 	    ReadGap( buf->buffer+1, 1 );
371 	    len = ReadGap( buf->buffer+2, -(SIZE_BUFFER-2) );
372 	    buf->len = (len < 0) ? 2 : len+2;
373 	}
374 	else
375 	{
376 	    buf->pos = 0;
377 	    ReadGap( buf->buffer, 2 );
378 	    len = ReadGap( buf->buffer+2, -(SIZE_BUFFER-2) );
379 	    buf->len = (len < 0) ? 2 : len+2;
380 	}
381     }
382 }
383 
384 /****************************************************************************
385 **
386 *F  ReadLine( <buf> ) . . . . . . . . . . . . . . . . . . . . . . read a line
387 */
ReadLine(buf)388 void ReadLine ( buf )
389     struct _in_buf    * buf;
390 {
391     String              ptr = GapBuffer;
392 
393     do
394     {
395 	WaitInput(buf);
396 	if ( CURRENT(*buf) == '\n' )
397 	{
398 	    *ptr++ = READ_CURRENT(*buf);
399 	    *ptr   = 0;
400 	    return;
401 	}
402 	else if ( CURRENT(*buf) == '\r' )
403 	    (void) READ_CURRENT(*buf);
404 	else if ( CURRENT(*buf) == '@' )
405 	{
406 	    (void) READ_CURRENT(*buf);
407 	    WaitInput(buf);
408 	    if ( CURRENT(*buf) == 'J' )
409 	    {
410 		*ptr++ = '\n';
411 		*ptr   = 0;
412 		(void) READ_CURRENT(*buf);
413 		return;
414 	    }
415 	    else if ( CURRENT(*buf) != '@' )
416 		*ptr++ = '^';
417 	    *ptr++ = READ_CURRENT(*buf);
418 	}
419 	else
420 	    *ptr++ = READ_CURRENT(*buf);
421     } while ( 1 );
422 }
423 
424 
425 /****************************************************************************
426 **
427 
428 *F  StoreInput( <str>, <len> )	. . . . . . . . . store input for later usage
429 */
430 static struct _storage
431 {
432     String      buffer;
433     Int         size;
434     Int         len;
435 }
436 Storage = { 0, 0, 0 };
437 
StoreInput(str,len)438 void StoreInput ( str, len )
439     String      str;
440     Int         len;
441 {
442     if ( Storage.buffer == 0 )
443     {
444 	Storage.buffer = XtMalloc(4096);
445 	Storage.size   = 4096;
446     }
447     if ( Storage.size <= Storage.len + len )
448     {
449 	Storage.size  += ((len/4096+1) * 4096);
450 	Storage.buffer = XtRealloc( Storage.buffer, Storage.size );
451     }
452     memcpy( Storage.buffer+Storage.len, str, len );
453     Storage.len += len;
454 }
455 
456 
457 /****************************************************************************
458 **
459 *F  ProcessStoredInput( <state> ) . . . . . . . . .  feed stored input to gap
460 */
461 static Char InputCookie = 'A';
462 
ProcessStoredInput(state)463 void ProcessStoredInput ( state )
464     Int             state;
465 {
466     String          ptr;
467     String          free;
468     Int             len;
469     static Boolean  inProgress = False;
470 
471     /* if we are already processing input do not start again */
472     if ( inProgress || state != 0 )
473 	return;
474 
475     /* if gap is not accepting input return */
476     if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
477 	return;
478 
479     /* if no input is waiting return */
480     if ( Storage.len == 0 && *ScreenSizeBuffer == 0 )
481 	return;
482 
483     /* otherwise make sure that gap does not want to tell use something */
484 again:
485     if ( HAS_INPUT(InBuffer) )
486 	GapOutput( 0, 0, 0 );
487     if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
488 	return;
489 
490     /* send '@yN' and wait for ACK '@sN' */
491     if ( InputCookie++ == 'Z' )  InputCookie = 'A';
492     WriteGap( "@y", 2 );
493     WriteGap( &InputCookie, 1 );
494     WaitInput(&InBuffer);
495     if ( CURRENT(InBuffer) != '@' )
496 	goto again;
497     WaitInput2(&InBuffer);
498     if ( LOOK_AHEAD(InBuffer) != 's' )
499 	goto again;
500     (void)READ_CURRENT(InBuffer);
501     (void)READ_CURRENT(InBuffer);
502     WaitInput(&InBuffer);
503     if ( READ_CURRENT(InBuffer) != InputCookie )
504 	goto again;
505 
506     /* if the screen was resized,  process resize command first */
507     if ( *ScreenSizeBuffer != 0 )
508     {
509 	WriteGap( ScreenSizeBuffer, strlen(ScreenSizeBuffer) );
510 	*ScreenSizeBuffer = 0;
511 	return;
512     }
513 
514     /* start processing input,  check reaction of gap */
515     inProgress = True;
516     len = Storage.len;
517     free = ptr = Storage.buffer;
518     while ( 0 < len )
519     {
520 	WriteGap( ptr, 1 );  len--;  ptr++;
521 	if (    ptr[-1] == '\n'
522 	     || ptr[-1] == '\r'
523 	     || (free<ptr-1 && ptr[-2]=='@' && ptr[-1]=='M')
524 	     || (free<ptr-1 && ptr[-2]=='@' && ptr[-1]=='J')
525 	    )
526 	    break;
527 	if (    ! QuitGapCtrlD && GapState == GAP_INPUT
528 	     && ptr[-1] == '@' && ptr[0] == 'D' )
529 	{
530 	    WriteGap( "F@H", 3 );
531 	    len--;
532 	    ptr++;
533 	}
534     }
535 
536     /* create new buffer,  store remaining input, and free old */
537     inProgress = False;
538     if ( len <= Storage.size )
539     {
540 	Storage.len = len;
541 	for ( ;  0 < len;  len-- )
542 	    *free++ = *ptr++;
543     }
544     else
545     {
546 	Storage.size   = ( 4096 < len ) ? len : 4096;
547 	Storage.buffer = XtMalloc(Storage.size);
548 	memcpy( Storage.buffer, ptr, len );
549 	Storage.len = len;
550 	XtFree(free);
551     }
552     if ( GapState == GAP_HELP )
553 	ProcessStoredInput(0);
554 }
555 
556 
557 /****************************************************************************
558 **
559 *F  SimulateInput( <str> )  . . . . . . . . . .  enter a line as command line
560 */
SimulateInput(str)561 void SimulateInput ( str )
562     String  str;
563 {
564     Int     pos;
565 
566     /* if <GAP> is not accepting input,  discard line */
567     if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
568 	return;
569 
570     /* ok, do it.  get current cursor position */
571     pos = GTPosition(GapTalk) - LastLine;
572     StoreInput( "@A@K", 4 );
573     StoreInput( str, strlen(str) );
574     StoreInput( "@Y@A", 4 );
575     while ( 0 < pos-- )
576 	StoreInput( "@F", 2 );
577     ProcessStoredInput(0);
578 }
579 
580 
581 /****************************************************************************
582 **
583 *F  KeyboardInput( <str>, <len> ) . . . . . . . . . .  process keyboard input
584 */
585 Boolean PlayingBack = False;
586 FILE * Playback = 0;
587 
PlaybackFile(str)588 int PlaybackFile ( str )
589     String      str;
590 {
591     if ( Playback != 0 ) {
592 	fclose(Playback);
593     }
594     Playback = fopen( str, "r" );
595     if ( Playback != 0 ) {
596 	PlayingBack = True;
597     }
598     else {
599 	PlayingBack = False;
600     }
601     return PlayingBack;
602 }
603 
ResumePlayback(void)604 int ResumePlayback ( void )
605 {
606     if ( PlayingBack || Playback == 0 )
607 	return False;
608     PlayingBack = True;
609     return True;
610 }
611 
KeyboardInput(str,len)612 void KeyboardInput ( str, len )
613     String      str;
614     Int     	len;
615 {
616     char        buf[1025];
617 
618 #ifndef	EXIT_ON_DOUBLE_CTR_C
619     static Int	ltime = 0;
620     Int         ntime;
621 #endif
622 
623     /* read playback file */
624     if ( PlayingBack && GapState == GAP_INPUT ) {
625 	if ( *str == 'q' || *str == 'Q' ) {
626 	    fclose(Playback);
627 	    PlayingBack = False;
628 	    Playback = 0;
629 	    StoreInput( "\"Playback STOPPED\";;\n", 21 );
630 	}
631 	else if ( *str=='z' || *str=='Z' || *str=='y' || *str=='Y' ) {
632 	    PlayingBack = False;
633 	    StoreInput( "\"Playback SUPENDED\";;\n", 22 );
634 	}
635 	else {
636 	    if ( fgets( buf, 1024, Playback ) == 0 ) {
637 		fclose(Playback);
638 		PlayingBack = False;
639 		Playback = 0;
640 	    }
641 	    else {
642 		StoreInput( buf, strlen(buf) );
643 		if ( feof(Playback) ) {
644 		    fclose(Playback);
645 		    PlayingBack = False;
646 		    Playback = 0;
647 		}
648 	    }
649 	    if ( ! PlayingBack )
650 		StoreInput( "\"Playback ENDED\";;\n", 19 );
651 	}
652     }
653 
654     /* handle help mode directly */
655     else if ( GapState == GAP_HELP )
656     {
657 	if ( HAS_INPUT(InBuffer) || HAS_INPUT(InBuffer) )
658 	    GapOutput( 0, 0, 0 );
659 	if ( GapState != GAP_HELP )
660 	{
661 	    KeyboardInput( str, len );
662 	    return;
663 	}
664 
665 	/* send '@yN' and wait for ACK '@sN' */
666 	if ( InputCookie++ == 'Z' )  InputCookie = 'A';
667 	WriteGap( "@y", 2 );
668 	WriteGap( &InputCookie, 1 );
669 	WaitInput(&InBuffer);
670 	if ( CURRENT(InBuffer) != '@' )
671 	{
672 	    GapOutput( 0, 0, 0 );
673 	    KeyboardInput( str, len );
674 	    return;
675 	}
676 	WaitInput2(&InBuffer);
677 	if ( LOOK_AHEAD(InBuffer) != 's' )
678 	{
679 	    GapOutput( 0, 0, 0 );
680 	    KeyboardInput( str, len );
681 	    return;
682 	}
683 	(void)READ_CURRENT(InBuffer);
684 	(void)READ_CURRENT(InBuffer);
685 	if ( READ_CURRENT(InBuffer) != InputCookie ) {
686 	    GapOutput( 0, 0, 0 );
687 	    KeyboardInput( str, len );
688 	    return;
689 	}
690 
691 	/* write a character and start again */
692 	WriteGap( str, 1 );
693 	if ( *str == '@' && 1 < len )
694 	{
695 	    WriteGap( str+1, 1 );
696 	    str++;
697 	    len--;
698 	}
699 	str++;
700 	len--;
701 	if ( 0 < len )
702 	    KeyboardInput( str, len );
703 	return;
704     }
705 
706     /* consume input */
707     else if ( PlayingBack && GapState == GAP_RUNNING ) {
708 	;
709     }
710     else {
711     while ( 0 < len )
712     {
713 
714 	/* handle <CTR-C> */
715 	if ( 2 <= len && *str == '@' && str[1] == 'C' )
716 	{
717 #           ifndef EXIT_ON_DOUBLE_CTR_C
718 	        while ( 2 <= len && *str == '@' && str[1] == 'C' )
719 		{
720 		    str += 2;
721 		    len -= 2;
722 		}
723 		ntime = (int) time(0);
724 		if ( 2 < ntime - ltime )
725 		    InterruptGap();
726 		ltime = ntime;
727 #           else
728 		InterruptGap();
729 		str += 2;
730 		len -= 2;
731 #           endif
732 	}
733 
734 	/* otherwise store it */
735 	else
736 	{
737 	    StoreInput( str, 1 );
738 	    len--;
739 	    str++;
740 	}
741     }
742     }
743 
744     /* try to process input */
745     ProcessStoredInput(0);
746 }
747 
748 
749 /****************************************************************************
750 **
751 *F  CheckCaretPos( <new>, <old> ) . . . . . . . . . . .  check caret movement
752 */
CheckCaretPos(new,old)753 Int CheckCaretPos ( new, old )
754     Int     new;
755     Int     old;
756 {
757     /* if <LastLine> is -1,  then gap is running,  ignore move */
758     if ( LastLine < 0 )
759 	return 0;
760 
761     /* if the new position is before the last line,  ignore move */
762     else if ( new < LastLine )
763 	return 0;
764 
765     /* otherwise move in the correct direction */
766     else if ( new < old )
767     {
768 	while ( new++ < old )
769 	    WriteGap( "@B", 2 );
770 	return 0;
771     }
772     else if ( old < new )
773     {
774 	while ( old++ < new )
775 	    WriteGap( "@F", 2 );
776 	return 0;
777     }
778     else
779 	return 0;
780 }
781 
782 
783 /****************************************************************************
784 **
785 *F  ParseInt( <buf>, <val> )  . . . . . . . . . . . . . . .  get a long value
786 */
ParseInt(struct _in_buf * buf,Int * val)787 static Boolean ParseInt (
788     struct _in_buf    * buf,
789     Int               * val )
790 {
791     Int                 mult;
792 
793     *val = 0;
794     mult = 1;
795     do
796     {
797 	WaitInput(buf);
798 	if ( CURRENT(*buf) == '+' )
799 	{
800 	    (void) READ_CURRENT(*buf);
801 	    return True;
802 	}
803 	else if ( CURRENT(*buf) == '-' )
804 	{
805 	    (void) READ_CURRENT(*buf);
806 	    *val = -*val;
807 	    return True;
808 	}
809 	else if ( '0' <= CURRENT(*buf) && CURRENT(*buf) <= '9' )
810 	    *val += mult * (READ_CURRENT(*buf)-'0');
811 	else
812 	    return False;
813 	mult = mult * 10;
814     } while (1);
815 }
816 
817 
818 /****************************************************************************
819 **
820 *F  GapOutput( <cld>, <fid>, <id> ) . . . . . . . . . . . . handle gap output
821 */
822 #undef  CTR
823 #define CTR(a)	( a & 0x1f )
824 
825 static char TBuf[SIZE_BUFFER];
826 
GapOutput(cld,fid,id)827 void GapOutput ( cld, fid, id )
828     XtPointer       cld;
829     int           * fid;
830     XtInputId       id;
831 {
832     char            ch;
833     Int             special;
834     Int             len;
835 
836     /* wait a while for input */
837     HAS_INPUT(InBuffer);
838     HAS_INPUT(InBuffer);
839     HAS_INPUT(InBuffer);
840 
841     /* special code for 'Exec' */
842     if ( ExecRunning )
843     {
844 	DEBUG( D_COMM, ("GapOutput: exec still active\n") );
845 	len = 0;
846 	while ( HAS_BUFFERED(InBuffer) && len < SIZE_BUFFER-3 )
847 	{
848 	    /* '@' is special */
849 	    if ( CURRENT(InBuffer) == '@' )
850 	    {
851 		(void) READ_CURRENT(InBuffer);
852 		WaitInput(&InBuffer);
853 		if ( CURRENT(InBuffer) == 'm' )
854 		{
855 
856 		    /* get ride of any output left over */
857 		    if ( 0 < len )
858 		    {
859 			TBuf[len] = 0;
860 			GTReplaceText( GapTalk, TBuf, len );
861 			if ( SpyMode )
862 			    fwrite( TBuf, 1, len, stderr );
863 		    }
864 
865 		    /* collect ouptut 'TBuf' in case it is not "mAgIc" */
866 		    len = 0;
867 		    TBuf[len++] = '@';
868 		    TBuf[len++] = 'm';
869 		    (void)READ_CURRENT(InBuffer);
870 		    WaitInput(&InBuffer);
871 		    if ( CURRENT(InBuffer) != 'A' )  continue;
872 		    (void)READ_CURRENT(InBuffer);
873 		    TBuf[len++] = 'A';
874 		    WaitInput(&InBuffer);
875 		    if ( CURRENT(InBuffer) != 'g' )  continue;
876 		    (void)READ_CURRENT(InBuffer);
877 		    TBuf[len++] = 'g';
878 		    WaitInput(&InBuffer);
879 		    if ( CURRENT(InBuffer) != 'I' )  continue;
880 		    (void)READ_CURRENT(InBuffer);
881 		    TBuf[len++] = 'I';
882 		    WaitInput(&InBuffer);
883 		    if ( CURRENT(InBuffer) != 'c' )  continue;
884 		    (void)READ_CURRENT(InBuffer);
885 		    len = 0;
886 		    ExecRunning = False;
887 		    DEBUG( D_COMM, ("GapOutput: %s: '%s'\n",
888                            "leaving exec loop, input remaining",
889 		           InBuffer.buffer+InBuffer.pos) );
890 		    goto end_exec_loop;
891 
892 		}
893 		else
894 		{
895 		    TBuf[len++] = '@';
896 		    continue;
897 		}
898 	    }
899 
900 	    /* store input */
901 	    else
902 		TBuf[len++] = READ_CURRENT(InBuffer);
903 	}
904 	TBuf[len] = 0;
905 	GTReplaceText( GapTalk, TBuf, len );
906         if ( SpyMode )
907 	    fwrite( TBuf, 1, len, stderr );
908 	return;
909     }
910 end_exec_loop:
911 
912     /* process gap output */
913     while ( HAS_BUFFERED(InBuffer) )
914     {
915 	/* '@' is special */
916 	if ( CURRENT(InBuffer) == '@' )
917 	{
918 	    (void) READ_CURRENT(InBuffer);
919 	    WaitInput(&InBuffer);
920 	    if ( 'A' <= CURRENT(InBuffer) && CURRENT(InBuffer) <= 'Z' )
921 	    {
922 		special = 0;
923 		ch = READ_CURRENT(InBuffer);
924 		ch = CTR(ch);
925 	    }
926 	    else if ( CURRENT(InBuffer) == '@' )
927 	    {
928 		special = 0;
929 		ch = READ_CURRENT(InBuffer);
930 	    }
931 	    else if ( CURRENT(InBuffer) == 'z' )
932 	    {
933 		(void)READ_CURRENT(InBuffer);
934 		ExecRunning = True;
935 		DEBUG( D_COMM, ("GapOutput: entering exec loop\n") );
936 		GapOutput( cld, fid, id );
937 		return;
938 	    }
939 
940 	    else
941 	    {
942 		special = 1;
943 		ch = READ_CURRENT(InBuffer);
944 	    }
945 	}
946 	else
947 	{
948 	    special = 0;
949 	    ch = READ_CURRENT(InBuffer);
950 	}
951 
952 	/* process window commands */
953 	if ( special )
954 	{
955 
956 	    /* '1' to '6' are garbage */
957 	    if ( '1' <= ch && ch <= '6' )
958 	    {
959 		Int	size;
960 		ParseInt( &InBuffer, &size );
961 		UpdateMemoryInfo( (int) (ch-'0'), size );
962 	    }
963 
964 	    /* '!','"','#','$','%','&' are garbage */
965 	    else if ( '!' <= ch && ch <= '&' ) {
966 		Int	size;
967 		ParseInt( &InBuffer, &size );
968 	    }
969 
970 	    /* 'i' means gap is waiting for input */
971 	    else if ( ch == 'i' )
972 	    {
973 		LastLine = GTPosition(GapTalk);
974 		GapState = GAP_INPUT;
975 		UpdateMenus(GapState);
976 		UpdateXCMDS(True);
977 		ProcessStoredInput(0);
978 	    }
979 
980 	    /* 'e' means gap is waiting for error input */
981 	    else if ( ch == 'e' )
982 	    {
983 		LastLine = GTPosition(GapTalk);
984 		GapState = GAP_ERROR;
985 		UpdateMenus(GapState);
986 		UpdateXCMDS(True);
987 		ProcessStoredInput(0);
988 	    }
989 
990 	    /* 'r' is the current input line */
991 	    else if ( ch == 'r' )
992 	    {
993 		ReadLine(&InBuffer);
994 		GTSetPosition( GapTalk, LastLine );
995 		GTReplaceText( GapTalk, GapBuffer, strlen(GapBuffer) );
996 		if ( SpyMode )
997 		{
998 		    fwrite( GapBuffer, 1, strlen(GapBuffer), stderr );
999 		}
1000 		GapState = GAP_RUNNING;
1001 		UpdateMenus(GapState);
1002 		UpdateXCMDS(False);
1003 		ProcessStoredInput(1);
1004 		LastLine = -1;
1005 	    }
1006 
1007 	    /* 'x' no text at current line */
1008 	    else if ( ch == 'x' )
1009 	    {
1010 		GTSetPosition( GapTalk, LastLine );
1011 		GTReplaceText( GapTalk, "", 0 );
1012 		GapState = GAP_RUNNING;
1013 		UpdateMenus(GapState);
1014 		UpdateXCMDS(True);
1015 		LastLine = -1;
1016 	    }
1017 
1018 	    /* 'c' completion output started */
1019 	    else if ( ch == 'c' )
1020 	    {
1021 		GapState = GAP_RUNNING;
1022 		UpdateMenus(GapState);
1023 		UpdateXCMDS(True);
1024 		LastLine = -1;
1025 	    }
1026 
1027 	    /* 'h' help output started */
1028 	    else if ( ch == 'h' )
1029 	    {
1030 		GapState = GAP_HELP;
1031 		UpdateMenus(GapState);
1032 		UpdateXCMDS(False);
1033 		LastLine = -1;
1034 	    }
1035 
1036 	    /* 'w' is a window command */
1037 	    else if ( ch == 'w' )
1038 	    {
1039 		long	i;
1040 		long    m;
1041 		char  * ptr;
1042 		char  * cmd;
1043 
1044 		len = 0;
1045 		WaitInput(&InBuffer);
1046 		ch = READ_CURRENT(InBuffer);
1047 		for ( len = 0, m = 1;  '0' <= ch && ch <= '9';  m *= 10 ) {
1048 		    len += (ch-'0') * m;
1049 		    WaitInput(&InBuffer);
1050 		    ch = READ_CURRENT(InBuffer);
1051 		}
1052 		ptr = cmd = XtMalloc(len+1);
1053 		i   = len;
1054 	        while ( 0 < i )
1055 		{
1056 		    WaitInput(&InBuffer);
1057 		    while ( HAS_INPUT(InBuffer) && 0 < i )
1058 		    {
1059 			*ptr++ = READ_CURRENT(InBuffer);
1060 			i--;
1061 		    }
1062 		}
1063 		*ptr++ = 0;
1064 		GapWindowCmd( cmd, len );
1065 		XtFree(cmd);
1066 	    }
1067 
1068 	    /* ignore 'n' for the moment */
1069 	    else if ( ch == 'n' )
1070 		ch = 'n';
1071 
1072 	    /* ignore 'f' for the moment */
1073 	    else if ( ch == 'f' )
1074 		ch = 'f';
1075 
1076 	    /* ignore 's',  see 'SimulateInput' */
1077 	    else if ( ch == 's' ) {
1078 		WaitInput(&InBuffer);
1079 		(void)READ_CURRENT(InBuffer);
1080 		continue;
1081 	    }
1082 	}
1083 
1084 	/* collect normal characters and display them */
1085 	else if ( ' ' <= ch && ch < 127 && GapState == GAP_RUNNING )
1086 	{
1087 	    TBuf[0] = ch;
1088 	    for ( len = 1;  len<SIZE_BUFFER && HAS_BUFFERED(InBuffer); )
1089 		if ( CURRENT(InBuffer) == '@' )
1090 		{
1091 		    if ( LOOK_AHEAD(InBuffer) == 'n' )
1092 		    {
1093 			(void)READ_CURRENT(InBuffer);
1094 			/* WaitInput(&InBuffer); */
1095 			(void)READ_CURRENT(InBuffer);
1096 		    }
1097 		    else if ( LOOK_AHEAD(InBuffer) == 'f' )
1098 		    {
1099 			(void)READ_CURRENT(InBuffer);
1100 			/* WaitInput(&InBuffer); */
1101 			(void)READ_CURRENT(InBuffer);
1102 		    }
1103 		    else if ( LOOK_AHEAD(InBuffer) == 'J' )
1104 		    {
1105 			(void)READ_CURRENT(InBuffer);
1106 			/* WaitInput(&InBuffer); */
1107 			(void)READ_CURRENT(InBuffer);
1108 			TBuf[len++] = '\n';
1109 		    }
1110 		    else
1111 			break;
1112 		}
1113 		else if ( ' '<=CURRENT(InBuffer) && CURRENT(InBuffer)<127 )
1114 		    TBuf[len++] = READ_CURRENT(InBuffer);
1115 	        else
1116 		    break;
1117 	    GTReplaceText( GapTalk, TBuf, len );
1118 	}
1119 
1120 	/* collect normal characters and display them */
1121 	else if ( ' ' <= ch && ch < 127 && GapState != GAP_RUNNING )
1122 	{
1123 	    TBuf[0] = ch;
1124 	    for ( len = 1;  len<SIZE_BUFFER && HAS_INPUT(InBuffer);  len++ )
1125 		if ( CURRENT(InBuffer) == '@' )
1126 		    break;
1127 		else if ( ' '<=CURRENT(InBuffer) && CURRENT(InBuffer)<127 )
1128 		    TBuf[len] = READ_CURRENT(InBuffer);
1129 	        else
1130 		    break;
1131 	    GTReplaceText( GapTalk, TBuf, len );
1132 	}
1133 
1134 	/* carriage return */
1135 	else if ( ch == '\n' )
1136 	{
1137 	    if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
1138 		GTReplaceText( GapTalk, &ch, 1 );
1139 	}
1140 
1141 	/* <CTR-G> rings a bell */
1142 	else if ( ch == CTR('G') )
1143 	    GTBell(GapTalk);
1144 
1145 	/* <CTR-H> moves to the left */
1146 	else if ( ch == CTR('H') )
1147 	    GTMoveCaret( GapTalk, -1 );
1148 
1149 	/* ignore anything else */
1150 	else
1151 	    ch = ch;
1152     }
1153 }
1154 
1155 
1156 /****************************************************************************
1157 **
1158 
1159 *F * * * * * * * * * * * * starting/stopping gap + * * * * * * * * * * * * *
1160 */
1161 
1162 
1163 /****************************************************************************
1164 **
1165 
1166 *F  KillGap() . . . . . . . . . . . . . . . . . . . . .  kill the running gap
1167 */
KillGap()1168 void KillGap ()
1169 {
1170     if ( GapPID != -1 )
1171     {
1172 	close(ToGap);
1173         kill( GapPID, SIGKILL );
1174     }
1175 }
1176 
1177 
1178 /****************************************************************************
1179 **
1180 *F  InterruptGap()  . . . . . . . . . . . . . . . .  interupt the running gap
1181 */
InterruptGap()1182 void InterruptGap ()
1183 {
1184     if ( GapPID != -1 )
1185         kill( GapPID, SIGINT );
1186 }
1187 
1188 
1189 /****************************************************************************
1190 **
1191 *F  OpenPty( <master>, <slave> ) . . . . . .  open a pty master/slave pair
1192 */
1193 
1194 #ifdef HAVE_OPENPTY
1195 
OpenPty(int * master,int * slave)1196 static UInt OpenPty(int * master, int * slave)
1197 {
1198     /* openpty is available on OpenBSD, NetBSD and FreeBSD, Mac OS X,
1199        Cygwin, Interix, OSF/1 4 and 5, and glibc (since 1998), and hence
1200        on most modern Linux systems. See also:
1201        http://www.gnu.org/software/gnulib/manual/html_node/openpty.html */
1202     return (openpty(master, slave, NULL, NULL, NULL) < 0);
1203 }
1204 
1205 #elif defined(HAVE_POSIX_OPENPT)
1206 
OpenPty(int * master,int * slave)1207 static UInt OpenPty(int * master, int * slave)
1208 {
1209     /* Attempt to use POSIX 98 pseudo ttys. Opening a master tty is done
1210        via posix_openpt, which is available on virtually every current
1211        UNIX system; indeed, according to gnulib, it is available on at
1212        least the following systems:
1213          - glibc >= 2.2.1 (released January 2001; but is a stub on GNU/Hurd),
1214          - Mac OS X >= 10.4 (released April 2005),
1215          - FreeBSD >= 5.1 (released June 2003),
1216          - NetBSD >= 3.0 (released December 2005),
1217          - AIX >= 5.2 (released October 2002),
1218          - HP-UX >= 11.31 (released February 2007),
1219          - Solaris >= 10 (released January 2005),
1220          - Cygwin >= 1.7 (released December 2009).
1221        Systems lacking posix_openpt (in addition to older versions of
1222        the systems listed above) include:
1223          - OpenBSD
1224          - Minix 3.1.8
1225          - IRIX 6.5
1226          - OSF/1 5.1
1227          - mingw
1228          - MSVC 9
1229          - Interix 3.5
1230          - BeOS
1231        */
1232     *master = posix_openpt(O_RDWR | O_NOCTTY);
1233     if (*master < 0) {
1234         fputs("OpenPty: posix_openpt failed\n");
1235         return 1;
1236     }
1237 
1238     if (grantpt(*master)) {
1239         fputs("OpenPty: grantpt failed\n");
1240         goto error;
1241     }
1242     if (unlockpt(*master)) {
1243         close(*master);
1244         fputs("OpenPty: unlockpt failed\n");
1245         goto error;
1246     }
1247 
1248     *slave = open(ptsname(*master), O_RDWR, 0);
1249     if (*slave < 0) {
1250         fputs("OpenPty: opening slave tty failed\n");
1251         goto error;
1252     }
1253     return 0;
1254 
1255 error:
1256     close(*master);
1257     return 1;
1258 }
1259 
1260 #else
1261 
OpenPty(int * master,int * slave)1262 static UInt OpenPty(int * master, int * slave)
1263 {
1264     fputs("no pseudo tty support available\n");
1265     return 1;
1266 }
1267 
1268 #endif
1269 
1270 
1271 /****************************************************************************
1272 **
1273 *F  StartGapProcess( <name>, <argv> ) . . . start a gap subprocess using ptys
1274 */
GapStatusHasChanged()1275 static void GapStatusHasChanged ()
1276 {
1277 #   ifdef SYS_HAS_UNION_WAIT
1278         union wait	w;
1279 #   else
1280         int             w;
1281 #   endif
1282 
1283     /* if the child was stopped return */
1284     if ( wait3( &w, WNOHANG | WUNTRACED, 0 ) != GapPID || WIFSTOPPED(w) )
1285 	return;
1286 #   ifdef DEBUG_ON
1287         fputs( "gap status has changed, leaving xgap\n", stderr );
1288         fprintf( stderr,"Signal: %d\n",WTERMSIG(w));
1289 #   endif
1290     exit(1);
1291 }
1292 
StartGapProcess(name,argv)1293 int StartGapProcess ( name, argv )
1294     String          name;
1295     String          argv[];
1296 {
1297     Int             j;       /* loop variables                  */
1298     char            c[8];    /* buffer for communication        */
1299     int             master;  /* pipe to GAP                     */
1300     int             n;       /* return value of 'select'        */
1301     int             slave;   /* pipe from GAP                   */
1302     /* struct */ fd_set   fds;     /* for 'select'                    */
1303     struct timeval  timeout; /* time to wait for aknowledgement */
1304 
1305 #   if HAVE_TERMIOS_H
1306         struct termios  tst; /* old and new terminal state      */
1307 #   else
1308 #     if HAVE_TERMIO_H
1309         struct termio   tst; /* old and new terminal state      */
1310 #     else
1311         struct sgttyb   tst; /* old and new terminal state      */
1312 #     endif
1313 #   endif
1314 
1315     /* open pseudo terminal for communication with gap */
1316     if (OpenPty(&master, &slave))
1317     {
1318         fprintf( stderr, "open pty failed (errno %d)\n", errno );
1319         exit(1);
1320     }
1321 #   if HAVE_TERMIOS_H
1322         if ( tcgetattr( slave, &tst ) == -1 )
1323         {
1324             fputs( "tcgetattr on slave pty failed\n", stderr );
1325             exit(1);
1326         }
1327         tst.c_cc[VINTR] = 0377;
1328         tst.c_cc[VQUIT] = 0377;
1329         tst.c_iflag    &= ~(INLCR|ICRNL);
1330         tst.c_cc[VMIN]  = 1;
1331         tst.c_cc[VTIME] = 0;
1332         tst.c_lflag    &= ~(ECHO|ICANON);
1333         if ( tcsetattr( slave, TCSANOW, &tst ) == -1 )
1334         {
1335             fputs( "tcsetattr on slave pty failed\n", stderr );
1336             exit(1);
1337         }
1338 #   else
1339 #     if HAVE_TERMIO_H
1340         if ( ioctl( slave, TCGETA, &tst ) == -1 )
1341 	{
1342 	    fputs( "ioctl TCGETA on slave pty failed\n", stderr );
1343 	    exit(1);
1344 	}
1345         tst.c_cc[VINTR] = 0377;
1346         tst.c_cc[VQUIT] = 0377;
1347         tst.c_iflag    &= ~(INLCR|ICRNL);
1348         tst.c_cc[VMIN]  = 1;
1349         tst.c_cc[VTIME] = 0;
1350         /* Note that this is at least on Linux dangerous!
1351            Therefore, we now have the HAVE_TERMIOS_H section for POSIX
1352            Terminal control. */
1353         tst.c_lflag    &= ~(ECHO|ICANON);
1354         if ( ioctl( slave, TCSETAW, &tst ) == -1 )
1355         {
1356 	    fputs( "ioctl TCSETAW on slave pty failed\n", stderr );
1357 	    exit(1);
1358 	}
1359 #     else
1360         if ( ioctl( slave, TIOCGETP, (char*)&tst ) == -1 )
1361         {
1362 	    if ( ttydev )
1363 	      fprintf( stderr, "ioctl TIOCGETP on slave pty failed (%s)\n",
1364 		       ttydev );
1365 	    else
1366 	      fputs( "ioctl TIOCGETP on slave pty failed\n", stderr );
1367 	    exit(1);
1368         }
1369         tst.sg_flags |= RAW;
1370         tst.sg_flags &= ~ECHO;
1371         if ( ioctl( slave, TIOCSETN, (char*)&tst ) == -1 )
1372         {
1373             fputs( "ioctl on TIOCSETN slave pty failed\n", stderr );
1374 	    exit(1);
1375 	}
1376 #endif
1377 #endif
1378 
1379     /* set input to non blocking operation */
1380     if ( fcntl( master, F_SETFL, O_NDELAY ) < 0 )
1381     {
1382         fputs( "Panic: cannot set non blocking operation.\n", stderr );
1383         exit(1);
1384     }
1385 
1386     /* fork to gap, dup pipe to stdin and stdout */
1387     GapPID = fork();
1388     if ( GapPID == 0 )
1389     {
1390         dup2( slave, 0 );
1391         dup2( slave, 1 );
1392         /* The following is necessary because otherwise the GAP process
1393            will ignore the SIGINT signal: */
1394         signal( SIGINT, SIG_DFL );
1395 #       ifdef SYS_HAS_EXECV_CCHARPP
1396             execv( name, (const char**) argv );
1397 #       else
1398             execv( name, (void*) argv );
1399 #       endif
1400         if (write( 1, "@-", 2) < 2) fputs("Child: Cannot write @-", stderr);
1401         close(slave);
1402         _exit(1);
1403     }
1404     ToGap   = master;
1405     FromGap = master;
1406 
1407     /* check if the fork was successful */
1408     if ( GapPID == -1 )
1409     {
1410         fputs( "Panic: cannot fork to subprocess.\n", stderr );
1411         exit(1);
1412     }
1413 
1414     /* wait at least 60 sec before giving up */
1415     timeout.tv_sec  = 60;
1416     timeout.tv_usec = 0;
1417 
1418     /* wait for an aknowledgement (@p) from the gap subprocess */
1419     j = 0;
1420     while ( j < 10 && c[j-1] != '.' )
1421     {
1422 
1423         /* set <FromGap> port for listen */
1424 #       ifdef FD_SET
1425 	    FD_ZERO(&fds);
1426 	    FD_SET( FromGap, &fds );
1427 #       else
1428         {
1429 	    Int     i;
1430 
1431             for ( i = FromGap/sizeof(fds.fds_bits); 0 <= i; i-- )
1432                 fds.fds_bits[i] = 0;
1433             fds.fds_bits[FromGap/sizeof(fds.fds_bits)] =
1434                                    ( 1 << (FromGap % sizeof(fds.fds_bits)) );
1435         }
1436 #       endif
1437 
1438         /* use 'select' to check port */
1439         if ( (n = select(FromGap+1, &fds, 0, 0, &timeout)) == -1 )
1440         {
1441             kill( GapPID, SIGKILL );
1442             perror("select failed");
1443             exit(1);
1444         }
1445         else if ( n == 0 )
1446         {
1447             kill( GapPID, SIGKILL );
1448             fputs("Panic: cannot establish communication with gap.", stderr);
1449             exit(1);
1450         }
1451         else
1452             ReadGap( &(c[j++]), 1 );
1453     }
1454 
1455     /* check if we got "@p" */
1456     if ( c[j-1] != '.' || strncmp( c, "@p", 2 ) )
1457     {
1458         if ( ! strncmp( c, "@-", 2 ) )
1459         {
1460             fputs( "Panic: cannot start subprocess ", stderr );
1461             fputs( name, stderr );
1462             fputs( ".\n", stderr );
1463         }
1464         else
1465         {
1466             strcpy( c+3, "'\n" );
1467             fputs( "Panic: cannot talk with gap, got '", stderr );
1468             fputs( c, stderr );
1469             kill( GapPID, SIGKILL );
1470         }
1471         exit(1);
1472     }
1473 
1474     /* if the gap dies,  stop program */
1475     signal( SIGCHLD, GapStatusHasChanged );
1476     InBuffer.pos = InBuffer.len = 0;
1477     return FromGap;
1478 }
1479 
1480 
1481 /****************************************************************************
1482 **
1483 
1484 *E  pty.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here
1485 */
1486