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