1 /****************************************************************************
2 **
3 **  This file is part of GAP, a system for computational discrete algebra.
4 **
5 **  Copyright of GAP belongs to its developers, whose names are too numerous
6 **  to list here. Please refer to the COPYRIGHT file for details.
7 **
8 **  SPDX-License-Identifier: GPL-2.0-or-later
9 **
10 **  The  files  "system.c" and  "sysfiles.c"   contain  all operating  system
11 **  dependent functions.  File and  stream operations are implemented in this
12 **  files, all the other system dependent functions in "system.c".  There are
13 **  various  labels determine which operating  system is  actually used, they
14 **  are described in "system.c".
15 */
16 
17 #include "sysfiles.h"
18 
19 #include "bool.h"
20 #include "calls.h"
21 #include "compstat.h"
22 #include "error.h"
23 #include "gapstate.h"
24 #include "gaputils.h"
25 #include "gvars.h"
26 #include "io.h"
27 #include "lists.h"
28 #include "modules.h"
29 #include "plist.h"
30 #include "read.h"
31 #include "records.h"
32 #include "stats.h"
33 #include "stringobj.h"
34 #include "sysenv.h"
35 #include "sysopt.h"
36 
37 #include "hpc/thread.h"
38 
39 #ifdef HAVE_LIBREADLINE
40 #include <readline/readline.h>
41 #endif
42 
43 #include <assert.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <termios.h>
47 #include <time.h>
48 #include <unistd.h>
49 
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
53 #ifdef HAVE_SYS_WAIT_H
54 #include <sys/wait.h>
55 #endif
56 
57 #ifdef HAVE_SELECT
58 /* Only for the Hook handler calls: */
59 #include <sys/time.h>
60 #endif
61 
62 #ifdef HAVE_SIGNAL_H                       /* signal handling functions       */
63 #include <signal.h>
64 typedef void sig_handler_t ( int );
65 #endif
66 
67 #ifdef HAVE_SYS_IOCTL_H
68 #include <sys/ioctl.h>                  /* for TIOCGWINSZ */
69 #endif
70 
71 #ifdef SYS_IS_CYGWIN32
72 #include <process.h>
73 #endif
74 
75 #ifdef SYS_IS_DARWIN
76 #include <mach-o/dyld.h>
77 #endif
78 
79 #include <zlib.h>
80 
81 
82 static ssize_t SyWriteandcheck(Int fid, const void * buf, size_t count);
83 
84 
85 /****************************************************************************
86 **
87 *V  syBuf . . . . . . . . . . . . . .  buffer and other info for files, local
88 **
89 **  'syBuf' is  a array used as  buffers for  file I/O to   prevent the C I/O
90 **  routines  from   allocating their  buffers  using  'malloc',  which would
91 **  otherwise confuse Gasman.
92 **
93 **
94 **  Actually these days SyBuf just stores various file info. SyBuffers
95 **  stores buffers for the relatively few files that need them.
96 */
97 
98 // The type of file stored in a 'SYS_SY_BUF'
99 typedef enum {
100     unused_socket,    // Socket is free
101     raw_socket,       // Plain UNIX socket stored in 'fp'
102     gzip_socket       // A gzFile handled by zlib stored in 'gzfp'
103 } GAPSocketType;
104 
105 GAP_STATIC_ASSERT(unused_socket == 0, "unused_socket must be zero");
106 
107 typedef struct {
108     // gzfp is used if type == gzip_socket
109     gzFile gzfp;
110 
111     // file descriptor for this file (only used if type == raw_socket)
112     int fp;
113 
114     // file descriptor for the echo (only used if type == raw_socket)
115     int echo;
116 
117     // file is either a plain descriptor, pipe or gzipped
118     GAPSocketType type;
119 
120     // set to 1 by any read operation that hits eof; reset to 0 by a
121     // subsequent successful read
122     UInt ateof;
123 
124     // records that last character read was \r for cygwin and other systems
125     // that need end-of-line hackery
126     UInt crlast;
127 
128     // if non-negative then this file has a buffer in syBuffers[bufno]; if
129     // negative, this file may not be buffered
130     Int bufno;
131 
132     // set when this fid is a *stdin* or *errin* and really is a tty
133     Int isTTY;
134 } SYS_SY_BUF;
135 
136 #define SYS_FILE_BUF_SIZE 20000
137 
138 typedef struct {
139     Char buf[SYS_FILE_BUF_SIZE];
140     UInt inuse;
141     UInt bufstart;
142     UInt buflen;
143 } SYS_SY_BUFFER;
144 
145 static SYS_SY_BUF syBuf[256];
146 
147 static SYS_SY_BUFFER syBuffers[32];
148 
149 
150 /* utility to check return value of 'write'  */
echoandcheck(int fid,const char * buf,size_t count)151 static ssize_t echoandcheck(int fid, const char *buf, size_t count)
152 {
153   int ret;
154   if (syBuf[fid].type == gzip_socket) {
155       ret = gzwrite(syBuf[fid].gzfp, buf, count);
156       if (ret < 0) {
157           ErrorQuit(
158               "Could not write to compressed file, see 'LastSystemError();'\n",
159               0L, 0L);
160       }
161   }
162   else {
163       ret = write(syBuf[fid].echo, buf, count);
164       if (ret < 0) {
165           if (syBuf[fid].fp == fileno(stdout) || syBuf[fid].fp == fileno(stderr)) {
166               Panic("Could not write to stdout/stderr.");
167           } else {
168               ErrorQuit("Could not write to file descriptor %d, see "
169                         "'LastSystemError();'\n",
170                         syBuf[fid].fp, 0L);
171           }
172       }
173   }
174   return ret;
175 }
176 
177 
178 /****************************************************************************
179 **
180 *F * * * * * * * * * * * * * * dynamic loading  * * * * * * * * * * * * * * *
181 */
182 
183 
184 /****************************************************************************
185 **
186 *F  SyFindOrLinkGapRootFile( <filename>, <result> ) . . . . . .  load or link
187 **
188 **  'SyFindOrLinkGapRootFile'  tries to find a GAP  file in the root area and
189 **  check if there is a corresponding statically linked module. If the CRC
190 **  matches the statically linked module is loaded, and <result->module_info>
191 **  is set to point to its StructInitInfo instance.
192 **
193 **  The function returns:
194 **
195 **  0: no file or module was found
196 **  2: a statically linked module was found
197 **  3: only a GAP file was found; its path is stored in  <result->path>
198 */
SyFindOrLinkGapRootFile(const Char * filename,TypGRF_Data * result)199 Int SyFindOrLinkGapRootFile(const Char * filename, TypGRF_Data * result)
200 {
201     // find the GAP file
202     Int found_gap =
203         SyFindGapRootFile(filename, result->path, sizeof(result->path)) != 0;
204 
205     if (SyUseModule) {
206         // search for a statically linked module matching the given filename
207         Char module[GAP_PATH_MAX];
208         strxcpy(module, "GAPROOT/", sizeof(module));
209         strxcat(module, filename, sizeof(module));
210         for (Int k = 0; CompInitFuncs[k]; k++) {
211             StructInitInfo * info = (*(CompInitFuncs[k]))();
212             if (info && !strcmp(module, info->name)) {
213                 // found a matching statically linked module; if there is also
214                 // a GAP file, compare their CRC
215                 if (found_gap && info->crc != SyGAPCRC(result->path)) {
216                     Pr("#W Static module %s has CRC mismatch, ignoring\n",
217                        (Int)filename, 0);
218                     break;
219                 }
220 
221                 result->module_info = info;
222                 return 2;
223             }
224         }
225     }
226 
227     return found_gap ? 3 : 0;
228 }
229 
230 
231 /****************************************************************************
232 **
233 *F  SyGAPCRC( <name> )  . . . . . . . . . . . . . . . . . . crc of a GAP file
234 **
235 **  This function should  be clever and handle  white spaces and comments but
236 **  one has to make certain that such characters are not ignored in strings.
237 **
238 **  This function *never* returns a 0 unless an error occurred.
239 */
240 static const UInt4 syCcitt32[ 256 ] =
241 {
242 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
243 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL,
244 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L,
245 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L,
246 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL,
247 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
248 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL,
249 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL,
250 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L,
251 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L,
252 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
253 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L,
254 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL,
255 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L,
256 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L,
257 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
258 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L,
259 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L,
260 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL,
261 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L,
262 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
263 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL,
264 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L,
265 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L,
266 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL,
267 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
268 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L,
269 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL,
270 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L,
271 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L,
272 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
273 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL,
274 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L,
275 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL,
276 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L,
277 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
278 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL,
279 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L,
280 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL,
281 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL,
282 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
283 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L,
284 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
285 };
286 
SyGAPCRC(const Char * name)287 Int4 SyGAPCRC( const Char * name )
288 {
289     UInt4       crc;
290     UInt4       old;
291     UInt4       new;
292     Int4        ch;
293     Int         fid;
294     Int         seen_nl;
295 
296     /* the CRC of a non existing file is 0                                 */
297     fid = SyFopen( name, "r" );
298     if ( fid == -1 ) {
299         return 0;
300     }
301 
302     /* read in the file byte by byte and compute the CRC                   */
303     crc = 0x12345678L;
304     seen_nl = 0;
305 
306     while ( (ch = SyGetch(fid) )!= EOF ) {
307         if ( ch == '\377' || ch == '\n' || ch == '\r' )
308             ch = '\n';
309         if ( ch == '\n' ) {
310             if ( seen_nl )
311                 continue;
312             else
313                 seen_nl = 1;
314         }
315         else
316             seen_nl = 0;
317         old = (crc >> 8) & 0x00FFFFFFL;
318         new = syCcitt32[ ( (UInt4)( crc ^ ch ) ) & 0xff ];
319         crc = old ^ new;
320     }
321     if ( crc == 0 ) {
322         crc = 1;
323     }
324 
325     /* and close it again                                                  */
326     SyFclose( fid );
327     /* Emulate a signed shift: */
328     if (crc & 0x80000000L)
329         return (Int4) ((crc >> 4) | 0xF0000000L);
330     else
331         return (Int4) (crc >> 4);
332 }
333 
334 
335 /*
336 <#GAPDoc Label="CrcString">
337 <ManSection>
338 <Func Name="CrcString" Arg='str'/>
339 <Returns>an integer</Returns>
340 
341 <Description>
342 This function computes a cyclic redundancy check number from a string
343 <A>str</A>. See also <Ref Func="CrcFile"/>.
344 <Example>
345 gap> CrcString("GAP example string");
346 -50451670
347 </Example>
348 </Description>
349 </ManSection>
350 
351 <#/GAPDoc>
352 */
353 
354 /* And here we include a variant working on a GAP string */
FuncCrcString(Obj self,Obj str)355 static Obj FuncCrcString(Obj self, Obj str)
356 {
357     UInt4       crc;
358     UInt4       old;
359     UInt4       new;
360     UInt4       i, len;
361     const Char  *ptr;
362     Int4        ch;
363     Int         seen_nl;
364 
365     // check the argument
366     RequireStringRep("CrcString", str);
367 
368     ptr = CONST_CSTR_STRING(str);
369     len = GET_LEN_STRING(str);
370     crc = 0x12345678L;
371     seen_nl = 0;
372     for (i = 0; i < len; i++) {
373         ch = (Int4)(ptr[i]);
374         if ( ch == '\377' || ch == '\n' || ch == '\r' )
375             ch = '\n';
376         if ( ch == '\n' ) {
377             if ( seen_nl )
378                 continue;
379             else
380                 seen_nl = 1;
381         }
382         else
383             seen_nl = 0;
384         old = (crc >> 8) & 0x00FFFFFFL;
385         new = syCcitt32[ ( (UInt4)( crc ^ ch ) ) & 0xff ];
386         crc = old ^ new;
387     }
388     if ( crc == 0 ) {
389         crc = 1;
390     }
391     return INTOBJ_INT(((Int4) crc) >> 4);
392 }
393 
394 
395 /****************************************************************************
396 **
397 *F * * * * * * * * * * finding location of executable * * * * * * * * * * * *
398 */
399 
400 /****************************************************************************
401 ** The function 'find_yourself' is based on code (C) 2015 Mark Whitis, under
402 ** the MIT License : https://stackoverflow.com/a/34271901/928031
403 */
404 
405 static void
find_yourself(const char * argv0,char * result,size_t resultsize)406 find_yourself(const char * argv0, char * result, size_t resultsize)
407 {
408     GAP_ASSERT(resultsize >= GAP_PATH_MAX);
409 
410     char tmpbuf[GAP_PATH_MAX];
411 
412     // absolute path, like '/usr/bin/gap'
413     if (argv0[0] == '/') {
414         if (realpath(argv0, result) && !access(result, F_OK)) {
415             return;    // success
416         }
417     }
418     // relative path, like 'bin/gap.sh'
419     else if (strchr(argv0, '/')) {
420         if (!getcwd(tmpbuf, sizeof(tmpbuf)))
421             return;
422         strlcat(tmpbuf, "/", sizeof(tmpbuf));
423         strlcat(tmpbuf, argv0, sizeof(tmpbuf));
424         if (realpath(tmpbuf, result) && !access(result, F_OK)) {
425             return;    // success
426         }
427     }
428     // executable name, like 'gap'
429     else {
430         char pathenv[GAP_PATH_MAX], *saveptr, *pathitem;
431         strlcpy(pathenv, getenv("PATH"), sizeof(pathenv));
432         pathitem = strtok_r(pathenv, ":", &saveptr);
433         for (; pathitem; pathitem = strtok_r(NULL, ":", &saveptr)) {
434             strlcpy(tmpbuf, pathitem, sizeof(tmpbuf));
435             strlcat(tmpbuf, "/", sizeof(tmpbuf));
436             strlcat(tmpbuf, argv0, sizeof(tmpbuf));
437             if (realpath(tmpbuf, result) && !access(result, F_OK)) {
438                 return;    // success
439             }
440         }
441     }
442 
443     *result = 0;    // reset buffer after error
444 }
445 
446 
447 char GAPExecLocation[GAP_PATH_MAX] = "";
448 
SetupGAPLocation(int argc,char ** argv)449 void SetupGAPLocation(int argc, char ** argv)
450 {
451     // In the code below, we keep reseting locBuf, as some of the methods we
452     // try do not promise to leave the buffer empty on a failed return.
453     char locBuf[GAP_PATH_MAX] = "";
454     Int4 length = 0;
455 
456 #ifdef SYS_IS_DARWIN
457     uint32_t len = sizeof(locBuf);
458     if (_NSGetExecutablePath(locBuf, &len) != 0) {
459         *locBuf = 0;    // reset buffer after error
460     }
461 #endif
462 
463     // try Linux procfs
464     if (!*locBuf) {
465         ssize_t ret = readlink("/proc/self/exe", locBuf, sizeof(locBuf));
466         if (ret < 0)
467             *locBuf = 0;    // reset buffer after error
468     }
469 
470     // try FreeBSD / DragonFly BSD procfs
471     if (!*locBuf) {
472         ssize_t ret = readlink("/proc/curproc/file", locBuf, sizeof(locBuf));
473         if (ret < 0)
474             *locBuf = 0;    // reset buffer after error
475     }
476 
477     // try NetBSD procfs
478     if (!*locBuf) {
479         ssize_t ret = readlink("/proc/curproc/exe", locBuf, sizeof(locBuf));
480         if (ret < 0)
481             *locBuf = 0;    // reset buffer after error
482     }
483 
484     // if we are still failing, go and search the path
485     if (!*locBuf) {
486         find_yourself(argv[0], locBuf, GAP_PATH_MAX);
487     }
488 
489     // resolve symlinks (if present)
490     if (!realpath(locBuf, GAPExecLocation))
491         *GAPExecLocation = 0;    // reset buffer after error
492 
493     // now strip the executable name off
494     length = strlen(GAPExecLocation);
495     while (length > 0 && GAPExecLocation[length] != '/') {
496         GAPExecLocation[length] = 0;
497         length--;
498     }
499 }
500 
501 
502 /****************************************************************************
503 **
504 *F * * * * * * * * * * * * * * * window handler * * * * * * * * * * * * * * *
505 */
506 
507 
508 /****************************************************************************
509 **
510 *F  IS_SEP( <C> ) . . . . . . . . . . . . . . . . . . . .  is <C> a separator
511 */
512 #define IS_SEP(C)       (!IsAlpha(C) && !IsDigit(C) && (C)!='_')
513 
514 
515 /****************************************************************************
516 **
517 *F  CTR( <V> )  . . . . . . . . . . . . . . . .  convert <V> into control-<V>
518 */
519 #define CTR(C)          ((C) & 0x1F)    /* <ctr> character                 */
520 
521 
522 /****************************************************************************
523 **
524 *F  Esc( <V> )  . . . . . . . . . . . . . . . . . convert <V> into escape-<V>
525 */
526 #define Esc(C)          ((C) | 0x100)   /* <esc> character                 */
527 
528 
529 /****************************************************************************
530 **
531 *F  CTV( <V> )  . . . . . . . . . . . . . . . . .  convert <V> into quote <V>
532 */
533 #define CTV(C)          ((C) | 0x200)   /* <ctr>V quotes characters        */
534 
535 
536 /****************************************************************************
537 **
538 *F  syWinPut( <fid>, <cmd>, <str> ) . . . . send a line to the window handler
539 **
540 **  'syWinPut'  send the command   <cmd> and the  string  <str> to the window
541 **  handler associated with the  file identifier <fid>.   In the string <str>
542 **  '@'  characters are duplicated, and   control characters are converted to
543 **  '@<chr>', e.g., <newline> is converted to '@J'.
544 */
syWinPut(Int fid,const Char * cmd,const Char * str)545 void syWinPut (
546     Int                 fid,
547     const Char *        cmd,
548     const Char *        str )
549 {
550     Char                tmp [130];      /* temporary buffer                */
551     const Char *        s;              /* pointer into the string         */
552     Char *              t;              /* pointer into the temporary      */
553 
554     /* if not running under a window handler, don't do anything            */
555     if (!SyWindow || 4 <= fid || syBuf[fid].type == gzip_socket)
556         return;
557 
558     /* print the cmd                                                       */
559     echoandcheck( fid, cmd, strlen(cmd) );
560 
561     /* print the output line, duplicate '@' and handle <ctr>-<chr>         */
562     s = str;  t = tmp;
563     while ( *s != '\0' ) {
564         if ( *s == '@' ) {
565             *t++ = '@';  *t++ = *s++;
566         }
567         else if ( CTR('A') <= *s && *s <= CTR('Z') ) {
568             *t++ = '@';  *t++ = *s++ - CTR('A') + 'A';
569         }
570         else {
571             *t++ = *s++;
572         }
573         if ( 128 <= t-tmp ) {
574             echoandcheck( fid, tmp, t-tmp );
575             t = tmp;
576         }
577     }
578     if ( 0 < t-tmp ) {
579         echoandcheck( fid, tmp, t-tmp );
580     }
581 }
582 
583 
584 /****************************************************************************
585 **
586 *F  SyWinCmd( <str>, <len> )  . . . . . . . . . . . . .  execute a window cmd
587 **
588 **  'SyWinCmd' send   the  command <str> to  the   window  handler (<len>  is
589 **  ignored).  In the string <str> '@' characters are duplicated, and control
590 **  characters  are converted to  '@<chr>', e.g.,  <newline> is converted  to
591 **  '@J'.  Then  'SyWinCmd' waits for  the window handlers answer and returns
592 **  that string.
593 */
594 static Char WinCmdBuffer[8000];
595 
SyWinCmd(const Char * str,UInt len)596 const Char * SyWinCmd (
597     const Char *        str,
598     UInt                len )
599 {
600     Char                buf [130];      /* temporary buffer                */
601     const Char *        s;              /* pointer into the string         */
602     const Char *        bb;             /* pointer into the temporary      */
603     Char *              b;              /* pointer into the temporary      */
604     UInt                i;              /* loop variable                   */
605 #ifdef SYS_IS_CYGWIN32
606     UInt                len1;           /* temporary storage for len       */
607 #endif
608 
609     /* if not running under a window handler, don't do nothing             */
610     if ( ! SyWindow )
611         return "I1+S52+No Window Handler Present";
612 
613     /* compute the length of the (expanded) string (and ignore argument)   */
614     len = 0;
615     for ( s = str; *s != '\0'; s++ )
616         len += 1 + (*s == '@' || (CTR('A') <= *s && *s <= CTR('Z')));
617 
618     /* send the length to the window handler                               */
619     b = buf;
620     for ( ; 0 < len;  len /= 10 ) {
621         *b++ = (len % 10) + '0';
622     }
623     *b++ = '+';
624     *b++ = '\0';
625     syWinPut( 1, "@w", buf );
626 
627     /* send the string to the window handler                               */
628     syWinPut( 1, "", str );
629 
630     /* read the length of the answer                                       */
631     b = WinCmdBuffer;
632     i = 3;
633     while ( 0 < i ) {
634         len = read( 0, b, i );
635         i  -= len;
636         b  += len;
637     }
638     if ( WinCmdBuffer[0] != '@' || WinCmdBuffer[1] != 'a' )
639         return "I1+S41+Illegal Answer";
640     b = WinCmdBuffer+2;
641     for ( i=1,len=0; '0' <= *b && *b <= '9';  i *= 10 ) {
642         len += (*b-'0')*i;
643         while ( read( 0, b, 1 ) != 1 )  ;
644     }
645 
646     /* read the arguments of the answer                                    */
647     b = WinCmdBuffer;
648     i = len;
649 #ifdef SYS_IS_CYGWIN32
650     len1 = len;
651     while ( 0 < i ) {
652         len = read( 0, b, i );
653         b += len;
654         i  -= len;
655         s  += len;
656     }
657     len = len1;
658 #else
659     while ( 0 < i ) {
660         len = read( 0, b, i );
661         i  -= len;
662         s  += len;
663     }
664 #endif
665 
666     /* shrink '@@' into '@'                                                */
667     for ( bb = b = WinCmdBuffer;  0 < len;  len-- ) {
668         if ( *bb == '@' ) {
669             bb++;
670             if ( *bb == '@' )
671                 *b++ = '@';
672             else if ( 'A' <= *bb && *bb <= 'Z' )
673                 *b++ = CTR(*bb);
674             bb++;
675         }
676         else {
677             *b++ = *bb++;
678         }
679     }
680     *b = 0;
681 
682     /* return the string                                                   */
683     return WinCmdBuffer;
684 }
685 
686 
687 /****************************************************************************
688 **
689 *F * * * * * * * * * * * * * * * * open/close * * * * * * * * * * * * * * * *
690 */
691 
692 
693 // Mark a member of syBuf as unused
SyBufMarkUnused(Int i)694 static void SyBufMarkUnused(Int i)
695 {
696     GAP_ASSERT(i >= 0 && i < ARRAY_SIZE(syBuf));
697     memset(&(syBuf[i]), 0, sizeof(syBuf[i]));
698     syBuf[i].type = unused_socket;
699 }
700 
701 // There is no explicit method to mark syBufs as used,
702 // they are marked as used when created.
703 
704 // Check if a member of syBuf is in use
SyBufInUse(Int i)705 static Int SyBufInUse(Int i)
706 {
707     if (i >= 0 && i < ARRAY_SIZE(syBuf))
708         return syBuf[i].type != unused_socket;
709     return 0;
710 }
711 
SyRedirectStderrToStdOut(void)712 void SyRedirectStderrToStdOut(void)
713 {
714     syBuf[2].fp = syBuf[0].fp;
715     syBuf[2].echo = syBuf[0].echo;
716     syBuf[2].isTTY = syBuf[0].isTTY;
717     syBuf[3].fp = syBuf[1].fp;
718     syBuf[3].echo = syBuf[1].echo;
719 }
720 
721 /****************************************************************************
722 **
723 *F  SyBufFileno( <fid> ) . . . . . . . . . . . .  get operating system fileno
724 **
725 **  Given a 'syBuf' buffer id, return the associated file descriptor, if any.
726 **  For gzipped files, -1 is returned.
727 */
SyBufFileno(Int fid)728 int SyBufFileno(Int fid)
729 {
730     if (fid == -1)
731         return -1;
732     GAP_ASSERT(0 <= fid && fid < ARRAY_SIZE(syBuf));
733 
734     // fp is only valid in the raw case
735     return (syBuf[fid].type == raw_socket) ? syBuf[fid].fp : -1;
736 }
737 
SyBufIsTTY(Int fid)738 Int SyBufIsTTY(Int fid)
739 {
740     GAP_ASSERT(0 <= fid && fid < ARRAY_SIZE(syBuf));
741     return syBuf[fid].isTTY;
742 }
743 
SyBufSetEOF(Int fid)744 void SyBufSetEOF(Int fid)
745 {
746     GAP_ASSERT(0 <= fid && fid < ARRAY_SIZE(syBuf));
747     syBuf[fid].ateof = 1;
748 }
749 
750 
751 /****************************************************************************
752 **
753 *F  SyFopen( <name>, <mode> ) . . . . . . . .  open the file with name <name>
754 **
755 **  The function 'SyFopen'  is called to open the file with the name  <name>.
756 **  If <mode> is "r" it is opened for reading, in this case  it  must  exist.
757 **  If <mode> is "w" it is opened for writing, it is created  if  neccessary.
758 **  If <mode> is "a" it is opened for appending, i.e., it is  not  truncated.
759 **
760 **  'SyFopen' returns an integer used by the scanner to  identify  the  file.
761 **  'SyFopen' returns -1 if it cannot open the file.
762 **
763 **  The following standard files names and file identifiers  are  guaranteed:
764 **  'SyFopen( "*stdin*", "r")' returns 0 identifying the standard input file.
765 **  'SyFopen( "*stdout*","w")' returns 1 identifying the standard outpt file.
766 **  'SyFopen( "*errin*", "r")' returns 2 identifying the brk loop input file.
767 **  'SyFopen( "*errout*","w")' returns 3 identifying the error messages file.
768 **
769 **  If it is necessary  to adjust the filename  this should be done here, the
770 **  filename convention used in GAP is that '/' is the directory separator.
771 **
772 **  Right now GAP does not read nonascii files, but if this changes sometimes
773 **  'SyFopen' must adjust the mode argument to open the file in binary mode.
774 */
775 
SyFopen(const Char * name,const Char * mode)776 Int SyFopen (
777     const Char *        name,
778     const Char *        mode )
779 {
780     Int                 fid;
781     Char                namegz [1024];
782     Char                cmd [1024];
783     int                 flags = 0;
784 
785     /* handle standard files                                               */
786     if ( strcmp( name, "*stdin*" ) == 0 ) {
787         if ( strcmp( mode, "r" ) != 0 )
788           return -1;
789         else
790           return 0;
791     }
792     else if ( strcmp( name, "*stdout*" ) == 0 ) {
793         if ( strcmp( mode, "w" ) != 0 && strcmp( mode, "a" ) != 0 )
794           return -1;
795         else
796           return 1;
797     }
798     else if ( strcmp( name, "*errin*" ) == 0 ) {
799         if ( strcmp( mode, "r" ) != 0 )
800           return -1;
801         else if ( !SyBufInUse( 2 ) )
802           return -1;
803         else
804           return 2;
805     }
806     else if ( strcmp( name, "*errout*" ) == 0 ) {
807         if ( strcmp( mode, "w" ) != 0 && strcmp( mode, "a" ) != 0 )
808           return -1;
809         else
810           return 3;
811     }
812 
813     HashLock(&syBuf);
814     /* try to find an unused file identifier                               */
815     for ( fid = 4; fid < ARRAY_SIZE(syBuf); ++fid )
816         if ( !SyBufInUse(fid) )
817           break;
818 
819     if ( fid == ARRAY_SIZE(syBuf) ) {
820         HashUnlock(&syBuf);
821         return (Int)-1;
822     }
823 
824     /* set up <namegz> and <cmd> for pipe command                          */
825     namegz[0] = '\0';
826     // Need space for "gunzip < '", ".gz'" and terminating \0.
827     if (strlen(name) <= sizeof(cmd) - 10 - 4 - 1) {
828       strxcpy( namegz, name, sizeof(namegz) );
829       strxcat( namegz, ".gz", sizeof(namegz) );
830 
831       strxcpy( cmd, "gunzip < '", sizeof(cmd) );
832       strxcat( cmd, namegz, sizeof(cmd) );
833       strxcat( cmd, "'", sizeof(cmd) );
834     }
835     if (strncmp( mode, "r", 1 ) == 0)
836       flags = O_RDONLY;
837     else if (strncmp( mode, "w",1 ) == 0)
838       flags = O_WRONLY | O_CREAT | O_TRUNC;
839     else if (strncmp( mode, "a",1) == 0)
840       flags = O_WRONLY | O_APPEND | O_CREAT;
841     else {
842         Panic("Unknown mode %s", mode);
843     }
844 
845 #ifdef SYS_IS_CYGWIN32
846     if(strlen(mode) >= 2 && mode[1] == 'b')
847        flags |= O_BINARY;
848 #endif
849 
850     /* try to open the file                                                */
851     syBuf[fid].fp = open(name,flags, 0644);
852     if ( 0 <= syBuf[fid].fp ) {
853         syBuf[fid].type = raw_socket;
854         syBuf[fid].echo = syBuf[fid].fp;
855         syBuf[fid].bufno = -1;
856     }
857     else if (strncmp(mode, "r", 1) == 0 && SyIsReadableFile(namegz) == 0 &&
858              (syBuf[fid].gzfp = gzopen(namegz, mode))) {
859         syBuf[fid].type = gzip_socket;
860         syBuf[fid].fp = -1;
861         syBuf[fid].bufno = -1;
862     }
863     else {
864         HashUnlock(&syBuf);
865         return (Int)-1;
866     }
867 
868     HashUnlock(&syBuf);
869 
870     if(strncmp(mode, "r", 1) == 0)
871         SySetBuffering(fid);
872 
873     /* return file identifier                                              */
874     return fid;
875 }
876 
877 // Lock on SyBuf for both SyBuf and SyBuffers
878 
SySetBuffering(UInt fid)879 UInt SySetBuffering( UInt fid )
880 {
881   UInt bufno;
882 
883   if (!SyBufInUse(fid))
884     ErrorQuit("Can't set buffering for a closed stream", 0, 0);
885   if (syBuf[fid].bufno >= 0)
886     return 1;
887 
888   bufno = 0;
889   HashLock(&syBuf);
890   while (bufno < ARRAY_SIZE(syBuffers) &&
891          syBuffers[bufno].inuse != 0)
892     bufno++;
893   if (bufno >= ARRAY_SIZE(syBuffers)) {
894       HashUnlock(&syBuf);
895       return 0;
896   }
897   syBuf[fid].bufno = bufno;
898   syBuffers[bufno].inuse = 1;
899   syBuffers[bufno].bufstart = 0;
900   syBuffers[bufno].buflen = 0;
901   HashUnlock(&syBuf);
902   return 1;
903 }
904 
905 /****************************************************************************
906 **
907 *F  SyFclose( <fid> ) . . . . . . . . . . . . . . . . .  close the file <fid>
908 **
909 **  'SyFclose' closes the file with the identifier <fid>  which  is  obtained
910 **  from 'SyFopen'.
911 */
SyFclose(Int fid)912 Int SyFclose (
913     Int                 fid )
914 {
915     /* check file identifier                                               */
916     if ( ARRAY_SIZE(syBuf) <= fid || fid < 0 ) {
917         fputs("gap: panic 'SyFclose' asked to close illegal fid!\n",stderr);
918         return -1;
919     }
920     if ( !SyBufInUse(fid) ) {
921         fputs("gap: panic 'SyFclose' asked to close closed file!\n",stderr);
922         return -1;
923     }
924 
925     /* refuse to close the standard files                                  */
926     if ( fid == 0 || fid == 1 || fid == 2 || fid == 3 ) {
927         return -1;
928     }
929     HashLock(&syBuf);
930     /* try to close the file                                               */
931     if (syBuf[fid].type == raw_socket && close(syBuf[fid].fp) == EOF) {
932         fputs("gap: 'SyFclose' cannot close file, ",stderr);
933         fputs("maybe your file system is full?\n",stderr);
934         SyBufMarkUnused(fid);
935         HashUnlock(&syBuf);
936         return -1;
937     }
938 
939     if (syBuf[fid].type == gzip_socket) {
940         if (gzclose(syBuf[fid].gzfp) < 0) {
941             fputs("gap: 'SyFclose' cannot close compressed file", stderr);
942         }
943     }
944 
945     /* mark the buffer as unused                                           */
946     if (syBuf[fid].bufno >= 0)
947       syBuffers[syBuf[fid].bufno].inuse = 0;
948     SyBufMarkUnused(fid);
949     HashUnlock(&syBuf);
950     return 0;
951 }
952 
953 
954 /****************************************************************************
955 **
956 *F  SyIsEndOfFile( <fid> )  . . . . . . . . . . . . . . . end of file reached
957 */
SyIsEndOfFile(Int fid)958 Int SyIsEndOfFile (
959     Int                 fid )
960 {
961     /* check file identifier                                               */
962     if ( !SyBufInUse(fid) ) {
963         return -1;
964     }
965 
966     /* *stdin* and *errin* are never at end of file                        */
967     if ( fid < 4 )
968         return 0;
969 
970     /* How to detect end of file ?? */
971 
972     return syBuf[fid].ateof;
973     /* return feof(syBuf[fid].fp);*/
974 }
975 
976 
977 /****************************************************************************
978 **
979 *F  syStartraw( <fid> ) . . . . . . start raw mode on input file <fid>, local
980 **
981 **  The  following four  functions are  the  actual system  dependent part of
982 **  'SyFgets'.
983 **
984 **  'syStartraw' tries to put the file with the file  identifier  <fid>  into
985 **  raw mode.  I.e.,  disabling  echo  and  any  buffering.  It also finds  a
986 **  place to put the echoing  for  'syEchoch'.  If  'syStartraw'  succedes it
987 **  returns 1, otherwise, e.g., if the <fid> is not a terminal, it returns 0.
988 **
989 **  'syStopraw' stops the raw mode for the file  <fid>  again,  switching  it
990 **  back into whatever mode the terminal had before 'syStartraw'.
991 **
992 **  'syGetch' reads one character from the file <fid>, which must  have  been
993 **  turned into raw mode before, and returns it.
994 **
995 **  'syEchoch' puts the character <ch> to the file opened by 'syStartraw' for
996 **  echoing.  Note that if the user redirected 'stdout' but not 'stdin',  the
997 **  echo for 'stdin' must go to 'ttyname(fileno(stdin))' instead of 'stdout'.
998 */
999 
1000 /****************************************************************************
1001 **  For UNIX System V, input/output redirection and typeahead are  supported.
1002 **  We  turn off input buffering  and canonical input editing and  also echo.
1003 **  Because we leave the signals enabled  we  have  to disable the characters
1004 **  for interrupt and quit, which are usually set to '<ctr>-C' and '<ctr>-B'.
1005 **  We   also turn off the  xon/xoff  start  and  stop  characters, which are
1006 **  usually set to  '<ctr>-S'  and '<ctr>-Q' so we  can get those characters.
1007 **  We do  not turn of  signals  'ISIG' because  we want   to catch  stop and
1008 **  continue signals if this particular version  of UNIX supports them, so we
1009 **  can turn the terminal line back to cooked mode before stopping GAP.
1010 */
1011 static struct termios   syOld, syNew;           /* old and new terminal state      */
1012 
1013 #ifdef SIGTSTP
1014 
1015 static Int syFid;
1016 
syAnswerCont(int signr)1017 static void syAnswerCont(int signr)
1018 {
1019     syStartraw( syFid );
1020     signal( SIGCONT, SIG_DFL );
1021     kill( getpid(), SIGCONT );
1022 }
1023 
syAnswerTstp(int signr)1024 static void syAnswerTstp(int signr)
1025 {
1026     syStopraw( syFid );
1027     signal( SIGCONT, syAnswerCont );
1028     kill( getpid(), SIGTSTP );
1029 }
1030 
1031 #endif
1032 
syStartraw(Int fid)1033 UInt syStartraw ( Int fid )
1034 {
1035     /* if running under a window handler, tell it that we want to read     */
1036     if ( SyWindow ) {
1037         if      ( fid == 0 ) { syWinPut( fid, "@i", "" );  return 1; }
1038         else if ( fid == 2 ) { syWinPut( fid, "@e", "" );  return 1; }
1039         else {                                             return 0; }
1040     }
1041 
1042     /* try to get the terminal attributes, will fail if not terminal       */
1043     const int fd = SyBufFileno(fid);
1044     GAP_ASSERT(fd >= 0);
1045     if ( tcgetattr( fd, &syOld) == -1 )
1046         return 0;
1047 
1048     /* disable interrupt, quit, start and stop output characters           */
1049     syNew = syOld;
1050     syNew.c_cc[VINTR] = 0377;
1051     syNew.c_cc[VQUIT] = 0377;
1052     /*C 27-Nov-90 martin changing '<ctr>S' and '<ctr>Q' does not work      */
1053     /*C syNew.c_iflag    &= ~(IXON|INLCR|ICRNL);                           */
1054     syNew.c_iflag    &= ~(INLCR|ICRNL);
1055 
1056     /* disable input buffering, line editing and echo                      */
1057     syNew.c_cc[VMIN]  = 1;
1058     syNew.c_cc[VTIME] = 0;
1059     syNew.c_lflag    &= ~(ECHO|ICANON);
1060 
1061     if ( tcsetattr( fd, TCSANOW, &syNew) == -1 )
1062         return 0;
1063 
1064 #ifdef SIGTSTP
1065     /* install signal handler for stop                                     */
1066     syFid = fid;
1067     signal( SIGTSTP, syAnswerTstp );
1068 #endif
1069 
1070     /* indicate success                                                    */
1071     return 1;
1072 }
1073 
1074 
1075 /****************************************************************************
1076 **
1077 *F  syStopraw( <fid> )  . . . . . .  stop raw mode on input file <fid>, local
1078 */
1079 
syStopraw(Int fid)1080 void syStopraw (
1081     Int                 fid )
1082 {
1083     /* if running under a window handler, don't do nothing                 */
1084     if ( SyWindow )
1085         return;
1086 
1087 #ifdef SIGTSTP
1088     /* remove signal handler for stop                                      */
1089     signal( SIGTSTP, SIG_DFL );
1090 #endif
1091 
1092     /* enable input buffering, line editing and echo again                 */
1093     const int fd = SyBufFileno(fid);
1094     GAP_ASSERT(fd >= 0);
1095     if (tcsetattr(fd, TCSANOW, &syOld) == -1)
1096         fputs("gap: 'tcsetattr' could not turn off raw mode!\n",stderr);
1097 }
1098 
1099 
1100 /****************************************************************************
1101 **
1102 *F  SyIsIntr()  . . . . . . . . . . . . . . . . check wether user hit <ctr>-C
1103 **
1104 **  'SyIsIntr' is called from the evaluator at  regular  intervals  to  check
1105 **  wether the user hit '<ctr>-C' to interrupt a computation.
1106 **
1107 **  'SyIsIntr' returns 1 if the user typed '<ctr>-C' and 0 otherwise.
1108 */
1109 
1110 
1111 /****************************************************************************
1112 **
1113 *f  SyIsIntr()
1114 **
1115 **  For  UNIX  we  install 'syAnswerIntr' to  answer interrupt 'SIGINT'. If
1116 **  two interrupts  occur within 1 second 'syAnswerIntr' exits GAP.
1117 */
1118 #ifdef HAVE_SIGNAL
1119 
1120 
1121 static UInt syLastIntr; /* time of the last interrupt      */
1122 
1123 
1124 #ifdef HAVE_LIBREADLINE
1125 static Int doingReadline;
1126 #endif
1127 
syAnswerIntr(int signr)1128 static void syAnswerIntr(int signr)
1129 {
1130     UInt                nowIntr;
1131 
1132 #ifdef HAVE_LIBREADLINE
1133     /* ignore during readline */
1134     if (doingReadline) return;
1135 #endif
1136 
1137     /* get the current wall clock time                                     */
1138     nowIntr = time(0);
1139 
1140     /* if the last '<ctr>-C' was less than a second ago, exit GAP          */
1141     if ( syLastIntr && nowIntr-syLastIntr < 1 ) {
1142         fputs("gap: you hit '<ctr>-C' twice in a second, goodbye.\n",stderr);
1143         SyExit( 1 );
1144     }
1145 
1146     /* reinstall 'syAnswerIntr' as signal handler                          */
1147     signal( SIGINT, syAnswerIntr );
1148     siginterrupt( SIGINT, 0 );
1149 
1150     /* remember time of this interrupt                                     */
1151     syLastIntr = nowIntr;
1152 
1153 #ifdef HAVE_SIGNAL
1154     /* interrupt the executor                                              */
1155     InterruptExecStat();
1156 #endif
1157 }
1158 
1159 
SyInstallAnswerIntr(void)1160 void SyInstallAnswerIntr ( void )
1161 {
1162     if ( signal( SIGINT, SIG_IGN ) != SIG_IGN )
1163     {
1164         signal( SIGINT, syAnswerIntr );
1165         siginterrupt( SIGINT, 0 );
1166     }
1167 }
1168 
1169 
SyIsIntr(void)1170 UInt SyIsIntr ( void )
1171 {
1172     UInt                isIntr;
1173 
1174     isIntr = (syLastIntr != 0);
1175 #ifdef HPCGAP
1176     /* The following write has to be conditional to avoid serious
1177      * performance degradation on shared memory (especially NUMA)
1178      * architectures when multiple threads all try to write to the same
1179      * location at the same time. Branch prediction can be expected to
1180      * be near perfect.
1181      */
1182     if (isIntr) syLastIntr = 0;
1183 #else
1184     syLastIntr = 0;
1185 #endif
1186     return isIntr;
1187 }
1188 
1189 #endif
1190 
1191 
1192 /****************************************************************************
1193  **
1194  *F  getwindowsize() . . . . . . . get screen size from termcap or TIOCGWINSZ
1195  **
1196  **  For UNIX  we  install 'syWindowChangeIntr' to answer 'SIGWINCH'.
1197  */
1198 
1199 #ifdef TIOCGWINSZ
1200 /* signal routine: window size changed */
syWindowChangeIntr(int signr)1201 void syWindowChangeIntr ( int signr )
1202 {
1203     struct winsize win;
1204     if(ioctl(0, TIOCGWINSZ, (char *) &win) >= 0) {
1205         if(!SyNrRowsLocked && win.ws_row > 0)
1206             SyNrRows = win.ws_row;
1207         if(!SyNrColsLocked && win.ws_col > 0)
1208           SyNrCols = win.ws_col - 1;        /* never trust last column */
1209         if (SyNrCols < 20) SyNrCols = 20;
1210         if (SyNrCols > MAXLENOUTPUTLINE) SyNrCols = MAXLENOUTPUTLINE;
1211     }
1212 }
1213 
1214 #endif /* TIOCGWINSZ */
1215 
getwindowsize(void)1216 void getwindowsize( void )
1217 {
1218 /* it might be that SyNrRows, SyNrCols have been set by the user with -x, -y */
1219 /* otherwise they are zero */
1220 
1221 /* first strategy: try to ask the operating system */
1222 #ifdef TIOCGWINSZ
1223     if (SyNrRows <= 0 || SyNrCols <= 0) {
1224         struct winsize win;
1225 
1226         if(ioctl(0, TIOCGWINSZ, (char *) &win) >= 0) {
1227             if (SyNrRows <= 0)
1228                 SyNrRows = win.ws_row;
1229             if (SyNrCols <= 0)
1230                 SyNrCols = win.ws_col;
1231         }
1232         (void) signal(SIGWINCH, syWindowChangeIntr);
1233     }
1234 #endif /* TIOCGWINSZ */
1235 
1236 #ifdef USE_TERMCAP
1237 /* note that if we define TERMCAP, this has to be linked with -ltermcap */
1238 /* maybe that is -ltermlib on some SYSV machines */
1239     if (SyNrRows <= 0 || SyNrCols <= 0) {
1240               /* this failed - next attempt: try to find info in TERMCAP */
1241         char *sp;
1242         char bp[1024];
1243 
1244         if ((sp = getenv("TERM")) != NULL && tgetent(bp,sp) == 1) {
1245             if(SyNrRows <= 0)
1246                 SyNrRows = tgetnum("li");
1247             if(SyNrCols <= 0)
1248                 SyNrCols = tgetnum("co");
1249         }
1250     }
1251 #endif
1252 
1253     /* if nothing worked, use 80x24 */
1254     if (SyNrCols <= 0)
1255         SyNrCols = 80;
1256     if (SyNrRows <= 0)
1257         SyNrRows = 24;
1258 
1259     /* reset SyNrCols if value is strange */
1260     if (SyNrCols < 20) SyNrCols = 20;
1261     if (SyNrCols > MAXLENOUTPUTLINE) SyNrCols = MAXLENOUTPUTLINE;
1262 }
1263 
1264 
1265 /****************************************************************************
1266 **
1267 *F * * * * * * * * * * * * * * * * * output * * * * * * * * * * * * * * * * *
1268 */
1269 
1270 
1271 /****************************************************************************
1272 **
1273 *F  syEchoch( <ch>, <fid> ) . . . . . . . . . . . echo a char to <fid>, local
1274 */
1275 
1276 
1277 /****************************************************************************
1278 **
1279 *f  syEchoch( <ch>, <fid> )
1280 */
syEchoch(Int ch,Int fid)1281 static void syEchoch(Int ch, Int fid)
1282 {
1283     Char                ch2;
1284 
1285     /* write the character to the associate echo output device             */
1286     ch2 = ch;
1287     echoandcheck( fid, (char*)&ch2, 1 );
1288 
1289     /* if running under a window handler, duplicate '@'                    */
1290     if ( SyWindow && ch == '@' ) {
1291         ch2 = ch;
1292         echoandcheck( fid, (char*)&ch2, 1 );
1293     }
1294 }
1295 
1296 /****************************************************************************
1297 **
1298 *F  SyEchoch( <ch>, <fid> ) . . . . . . . . . . . . .  echo a char from <fid>
1299 */
SyEchoch(Int ch,Int fid)1300 Int SyEchoch (
1301     Int                 ch,
1302     Int                 fid )
1303 {
1304     /* check file identifier                                               */
1305     if ( !SyBufInUse(fid) ) {
1306         return -1;
1307     }
1308     syEchoch(ch,fid);
1309     return 0;
1310 }
1311 
1312 
1313 
1314 /****************************************************************************
1315 **
1316 *F  syEchos( <ch>, <fid> )  . . . . . . . . . . . echo a char to <fid>, local
1317 */
1318 
1319 
1320 /****************************************************************************
1321 **
1322 *f  syEchos( <ch>, <fid> )
1323 */
syEchos(const Char * str,Int fid)1324 static void syEchos(const Char * str, Int fid)
1325 {
1326     /* if running under a window handler, send the line to it              */
1327     if ( SyWindow && fid < 4 )
1328         syWinPut( fid, (fid == 1 ? "@n" : "@f"), str );
1329 
1330     /* otherwise, write it to the associate echo output device             */
1331     else
1332         echoandcheck(fid, str, strlen(str) );
1333 }
1334 
1335 
1336 /****************************************************************************
1337 **
1338 *F  SyFputs( <line>, <fid> )  . . . . . . . .  write a line to the file <fid>
1339 **
1340 **  'SyFputs' is called to put the  <line>  to the file identified  by <fid>.
1341 */
1342 static UInt syNrchar;                   /* nr of chars already on the line */
1343 static Char syPrompt[MAXLENOUTPUTLINE]; /* characters already on the line  */
1344 
1345 
1346 /****************************************************************************
1347 **
1348 *f  SyFputs( <line>, <fid> )
1349 */
SyFputs(const Char * line,Int fid)1350 void SyFputs (
1351     const Char *        line,
1352     Int                 fid )
1353 {
1354     UInt                i;
1355 
1356     /* if outputing to the terminal compute the cursor position and length */
1357     if ( fid == 1 || fid == 3 ) {
1358         syNrchar = 0;
1359         for ( i = 0; line[i] != '\0'; i++ ) {
1360             if ( line[i] == '\n' )  syNrchar = 0;
1361             else                    syPrompt[syNrchar++] = line[i];
1362         }
1363         syPrompt[syNrchar] = '\0';
1364     }
1365 
1366     /* otherwise compute only the length                                   */
1367     else {
1368         i = strlen(line);
1369     }
1370 
1371     /* if running under a window handler, send the line to it              */
1372     if ( SyWindow && fid < 4 )
1373         syWinPut( fid, (fid == 1 ? "@n" : "@f"), line );
1374 
1375     /* otherwise, write it to the output file                              */
1376     else
1377         SyWriteandcheck(fid, line, i);
1378 }
1379 
1380 
1381 /****************************************************************************
1382 **
1383 *F * * * * * * * * * * * * * * * * * input  * * * * * * * * * * * * * * * * *
1384 */
1385 
1386 
1387 /****************************************************************************
1388 **
1389 *F  SyFtell( <fid> )  . . . . . . . . . . . . . . . . . .  position of stream
1390 */
SyFtell(Int fid)1391 Int SyFtell (
1392     Int                 fid )
1393 {
1394     /* check file identifier                                               */
1395     if ( !SyBufInUse(fid) ) {
1396         return -1;
1397     }
1398 
1399     Int ret;
1400 
1401     switch (syBuf[fid].type) {
1402     case raw_socket:
1403         ret = (Int)lseek(syBuf[fid].fp, 0, SEEK_CUR);
1404         break;
1405     case gzip_socket:
1406         ret = (Int)gzseek(syBuf[fid].gzfp, 0, SEEK_CUR);
1407         break;
1408     case unused_socket:
1409     default:
1410         return -1;
1411     }
1412 
1413     // Need to account for characters in buffer
1414     if (syBuf[fid].bufno >= 0) {
1415         UInt bufno = syBuf[fid].bufno;
1416         ret -= syBuffers[bufno].buflen - syBuffers[bufno].bufstart;
1417     }
1418     return ret;
1419 }
1420 
1421 
1422 /****************************************************************************
1423 **
1424 *F  SyFseek( <fid>, <pos> )   . . . . . . . . . . . seek a position of stream
1425 */
SyFseek(Int fid,Int pos)1426 Int SyFseek (
1427     Int                 fid,
1428     Int                 pos )
1429 {
1430     /* check file identifier                                               */
1431     if ( !SyBufInUse(fid) ) {
1432         return -1;
1433     }
1434 
1435     if (syBuf[fid].bufno >= 0) {
1436         UInt bufno = syBuf[fid].bufno;
1437         syBuffers[bufno].buflen = 0;
1438         syBuffers[bufno].bufstart = 0;
1439     }
1440 
1441     switch (syBuf[fid].type) {
1442     case raw_socket:
1443         return (Int)lseek(syBuf[fid].fp, pos, SEEK_SET);
1444     case gzip_socket:
1445         return (Int)gzseek(syBuf[fid].gzfp, pos, SEEK_SET);
1446     case unused_socket:
1447     default:
1448         return -1;
1449     }
1450 }
1451 
1452 
1453 /****************************************************************************
1454 **
1455 *F  syGetchTerm( <fid> )  . . . . . . . . . . . . . . . . . get a char from <fid>
1456 **
1457 **  'SyGetchTerm' reads a character from <fid>, which is already switched
1458 **  to raw mode if it is *stdin* or *errin*.
1459 
1460 */
1461 
1462 
1463 
1464 /****************************************************************************
1465 **
1466 *f  syGetchTerm( <fid> )  . . . . . . . . . . . . . . . . . . . . . UNIX
1467 **
1468 **  This version should be called if the input is stdin and command-line editing
1469 **  etc. is switched on. It handles possible messages from xgap and systems
1470 **  that return odd things rather than waiting for a key
1471 **
1472 */
1473 
1474 
1475 /* In the cygwin environment it is not predictable if text files get the
1476  * '\r' in their line ends filtered out *before* GAP sees them. This leads
1477  * to problem with continuation of strings or integers over several lines in
1478  * GAP input. Therefore we introduce a hack which removes such '\r's
1479  * before '\n's on such a system. Add here if there are other systems with
1480  * a similar problem.
1481  */
1482 
1483 #ifdef SYS_IS_CYGWIN32
1484 #  define LINE_END_HACK 1
1485 #endif
1486 
SyRead(Int fid,void * ptr,size_t len)1487 Int SyRead(Int fid, void * ptr, size_t len)
1488 {
1489     /* check file identifier                                               */
1490     if ( !SyBufInUse(fid) ) {
1491         return -1;
1492     }
1493 
1494     if (syBuf[fid].type == gzip_socket) {
1495         return gzread(syBuf[fid].gzfp, ptr, len);
1496     }
1497     else {
1498         return read(syBuf[fid].fp, ptr, len);
1499     }
1500 }
1501 
SyReadWithBuffer(Int fid,void * ptr,size_t len)1502 Int SyReadWithBuffer(Int fid, void * ptr, size_t len)
1503 {
1504     /* check file identifier                                               */
1505     if ( !SyBufInUse(fid) ) {
1506         return -1;
1507     }
1508 
1509     // first drain the buffer
1510     if (syBuf[fid].bufno >= 0) {
1511         UInt   bufno = syBuf[fid].bufno;
1512         size_t avail = syBuffers[bufno].buflen - syBuffers[bufno].bufstart;
1513         if (avail > 0) {
1514             if (avail > len)
1515                 avail = len;
1516             memcpy(ptr, syBuffers[bufno].buf + syBuffers[bufno].bufstart,
1517                    avail);
1518             syBuffers[bufno].bufstart += avail;
1519             return avail;
1520         }
1521     }
1522 
1523     return SyRead(fid, ptr, len);
1524 }
1525 
1526 
SyWrite(Int fid,const void * ptr,size_t len)1527 Int SyWrite(Int fid, const void * ptr, size_t len)
1528 {
1529     /* check file identifier                                               */
1530     if ( !SyBufInUse(fid) ) {
1531         return -1;
1532     }
1533 
1534     if (syBuf[fid].type == gzip_socket) {
1535         return gzwrite(syBuf[fid].gzfp, ptr, len);
1536     }
1537     else {
1538         return write(syBuf[fid].echo, ptr, len);
1539     }
1540 }
1541 
SyWriteandcheck(Int fid,const void * buf,size_t count)1542 static ssize_t SyWriteandcheck(Int fid, const void * buf, size_t count)
1543 {
1544     int ret;
1545     if (syBuf[fid].type == gzip_socket) {
1546         ret = gzwrite(syBuf[fid].gzfp, buf, count);
1547         if (ret < 0) {
1548             ErrorQuit(
1549                 "Cannot write to compressed file, see 'LastSystemError();'\n",
1550                 0L, 0L);
1551         }
1552     }
1553     else {
1554         ret = write(syBuf[fid].fp, buf, count);
1555         if (ret < 0) {
1556             if (syBuf[fid].fp == fileno(stdout) || syBuf[fid].fp == fileno(stderr)) {
1557                 Panic("Could not write to stdout/stderr.");
1558             } else {
1559                 ErrorQuit("Cannot write to file descriptor %d, see "
1560                           "'LastSystemError();'\n",
1561                           syBuf[fid].fp, 0L);
1562             }
1563         }
1564     }
1565 
1566     return ret;
1567 }
1568 
syGetchTerm(Int fid)1569 static Int syGetchTerm(Int fid)
1570 {
1571     UChar                ch;
1572     Char                str[2];
1573     Int ret;
1574 
1575     /* retry on errors or end-of-file. Ignore 0 bytes */
1576 
1577 #ifdef LINE_END_HACK
1578  tryagain:
1579 #endif
1580     while ( (ret = SyRead( fid, &ch, 1 )) == -1 && errno == EAGAIN )
1581         ;
1582     if (ret <= 0) return EOF;
1583 
1584     /* if running under a window handler, handle special characters        */
1585     if ( SyWindow && ch == '@' ) {
1586         do {
1587             while ( (ret = SyRead(fid, &ch, 1)) == -1 &&
1588                     errno == EAGAIN ) ;
1589             if (ret <= 0) return EOF;
1590         } while ( ch < '@' || 'z' < ch );
1591         if ( ch == 'y' ) {
1592             do {
1593                 while ( (ret = SyRead(fid, &ch, 1)) == -1 &&
1594                         errno == EAGAIN );
1595                 if (ret <= 0) return EOF;
1596             } while ( ch < '@' || 'z' < ch );
1597             str[0] = ch;
1598             str[1] = 0;
1599             syWinPut( syBuf[fid].echo, "@s", str );
1600             ch = syGetchTerm(fid);
1601         }
1602         else if ( 'A' <= ch && ch <= 'Z' )
1603             ch = CTR(ch);
1604     }
1605 
1606 #ifdef LINE_END_HACK
1607     /* A hack for non ANSI-C confirming systems which deliver \r or \r\n
1608      * line ends. These are translated to \n here.
1609      */
1610     if (ch == '\n') {
1611         if (syBuf[fid].crlast) {
1612             syBuf[fid].crlast = 0;
1613             goto tryagain;
1614         } else
1615             return (UChar)'\n';
1616     }
1617     if (ch == '\r') {
1618         syBuf[fid].crlast = 1;
1619         return (Int)'\n';
1620     }
1621     // We saw a '\r' without a '\n'
1622     syBuf[fid].crlast = 0;
1623 #endif  /* line end hack */
1624 
1625     /* return the character                                                */
1626     return (Int)ch;
1627 }
1628 
syGetchNonTerm(Int fid)1629 static Int syGetchNonTerm(Int fid)
1630 {
1631     UChar               ch = 0;
1632     UInt                bufno;
1633     int                 ret;
1634 
1635 
1636     /* we jump back here if the byte we just read was the \n of \r\n, in which
1637        case it doesn't count */
1638 
1639 #ifdef LINE_END_HACK
1640  tryagain:
1641 #endif
1642     if (syBuf[fid].bufno < 0)
1643         while ((ret = SyRead(fid, &ch, 1)) == -1 && errno == EAGAIN)
1644             ;
1645     else {
1646         bufno = syBuf[fid].bufno;
1647         if (syBuffers[bufno].bufstart < syBuffers[bufno].buflen) {
1648             ch = syBuffers[bufno].buf[syBuffers[bufno].bufstart++];
1649             ret = 1;
1650         } else {
1651             while ((ret = SyRead(fid, syBuffers[bufno].buf,
1652                                  SYS_FILE_BUF_SIZE)) == -1 &&
1653                    errno == EAGAIN)
1654                 ;
1655             if (ret > 0) {
1656                 ch = syBuffers[bufno].buf[0];
1657                 syBuffers[bufno].bufstart = 1;
1658                 syBuffers[bufno].buflen = ret;
1659             }
1660         }
1661     }
1662 
1663     if (ret < 1) {
1664         syBuf[fid].ateof = 1;
1665         return EOF;
1666     }
1667 
1668 #ifdef LINE_END_HACK
1669     /* A hack for non ANSI-C confirming systems which deliver \r or \r\n
1670      * line ends. These are translated to \n here.
1671      */
1672     if (ch == '\n') {
1673         if (syBuf[fid].crlast) {
1674             syBuf[fid].crlast = 0;
1675             goto tryagain;
1676         } else
1677             return (UChar)'\n';
1678     }
1679     if (ch == '\r') {
1680         syBuf[fid].crlast = 1;
1681         return (Int)'\n';
1682     }
1683     // We saw a '\r' without a '\n'
1684     syBuf[fid].crlast = 0;
1685 #endif  /* line end hack */
1686 
1687     /* return the character                                                */
1688     return (Int)ch;
1689 }
1690 
1691 
1692 
1693 /****************************************************************************
1694 **
1695 *f  syGetch( <fid> )
1696 */
1697 
syGetch(Int fid)1698 static Int syGetch(Int fid)
1699 {
1700     if (syBuf[fid].isTTY)
1701       return syGetchTerm(fid);
1702     else
1703       return syGetchNonTerm(fid);
1704 }
1705 
1706 
1707 /****************************************************************************
1708 **
1709 *F  SyGetch( <fid> )  . . . . . . . . . . . . . . . . . get a char from <fid>
1710 **
1711 **  'SyGetch' reads a character from <fid>, which is switch to raw mode if it
1712 **  is *stdin* or *errin*.
1713 */
SyGetch(Int fid)1714 Int SyGetch (
1715     Int                 fid )
1716 {
1717     Int                 ch;
1718 
1719     /* check file identifier                                               */
1720     if ( !SyBufInUse(fid) ) {
1721         return -1;
1722     }
1723 
1724     /* if we are reading stdin or errin use raw mode                       */
1725     if ( fid == 0 || fid == 2 ) {
1726         syStartraw(fid);
1727     }
1728     ch = syGetch(fid);
1729     if ( fid == 0 || fid == 2 ) {
1730         syStopraw(fid);
1731     }
1732     return ch;
1733 }
1734 
1735 
1736 /****************************************************************************
1737 **
1738 *F  SyFgets( <line>, <length>, <fid> )  . . . . .  get a line from file <fid>
1739 **
1740 **  'SyFgets' is called to read a line from the file  with  identifier <fid>.
1741 **  'SyFgets' (like 'fgets') reads characters until either  <length>-1  chars
1742 **  have been read or until a <newline> or an  <eof> character is encoutered.
1743 **  It retains the '\n' (unlike 'gets'), if any, and appends '\0' to  <line>.
1744 **  'SyFgets' returns <line> if any char has been read, otherwise '(char*)0'.
1745 **
1746 **  'SyFgets'  allows to edit  the input line if the  file  <fid> refers to a
1747 **  terminal with the following commands:
1748 **
1749 **      <ctr>-A move the cursor to the beginning of the line.
1750 **      <esc>-B move the cursor to the beginning of the previous word.
1751 **      <ctr>-B move the cursor backward one character.
1752 **      <ctr>-F move the cursor forward  one character.
1753 **      <esc>-F move the cursor to the end of the next word.
1754 **      <ctr>-E move the cursor to the end of the line.
1755 **
1756 **      <ctr>-H, <del> delete the character left of the cursor.
1757 **      <ctr>-D delete the character under the cursor.
1758 **      <ctr>-K delete up to the end of the line.
1759 **      <esc>-D delete forward to the end of the next word.
1760 **      <esc>-<del> delete backward to the beginning of the last word.
1761 **      <ctr>-X delete entire input line, and discard all pending input.
1762 **      <ctr>-Y insert (yank) a just killed text.
1763 **
1764 **      <ctr>-T exchange (twiddle) current and previous character.
1765 **      <esc>-U uppercase next word.
1766 **      <esc>-L lowercase next word.
1767 **      <esc>-C capitalize next word.
1768 **
1769 **      <tab>   complete the identifier before the cursor.
1770 **      <ctr>-L insert last input line before current character.
1771 **      <ctr>-P redisplay the last input line, another <ctr>-P will redisplay
1772 **              the line before that, etc.  If the cursor is not in the first
1773 **              column only the lines starting with the string to the left of
1774 **              the cursor are taken. The history is limitied to ~8000 chars.
1775 **      <ctr>-N Like <ctr>-P but goes the other way round through the history
1776 **      <esc>-< goes to the beginning of the history.
1777 **      <esc>-> goes to the end of the history.
1778 **      <ctr>-O accept this line and perform a <ctr>-N.
1779 **
1780 **      <ctr>-V enter next character literally.
1781 **      <ctr>-U execute the next command 4 times.
1782 **      <esc>-<num> execute the next command <num> times.
1783 **      <esc>-<ctr>-L repaint input line.
1784 **
1785 **  Not yet implemented commands:
1786 **
1787 **      <ctr>-S search interactive for a string forward.
1788 **      <ctr>-R search interactive for a string backward.
1789 **      <esc>-Y replace yanked string with previously killed text.
1790 **      <ctr>-_ undo a command.
1791 **      <esc>-T exchange two words.
1792 */
1793 
1794 static UInt syCTRO; /* number of '<ctr>-O' pending     */
1795 static UInt syESCN; /* number of '<Esc>-N' pending     */
1796 
1797 static UInt FreezeStdin;    // When true, ignore if any new input from stdin
1798                             // This is used to stop HPC-GAP from reading stdin
1799                             // while forked subprocesses are running.
1800 
1801 
1802 #ifdef HAVE_SELECT
1803 
1804 static Obj OnCharReadHookActive = 0;  /* if bound the hook is active */
1805 static Obj OnCharReadHookInFds = 0;   /* a list of UNIX file descriptors for reading */
1806 static Obj OnCharReadHookInFuncs = 0; /* a list of GAP functions with 0 args */
1807 static Obj OnCharReadHookOutFds = 0;  /* a list of UNIX file descriptors for writing */
1808 static Obj OnCharReadHookOutFuncs = 0;/* a list of GAP functions with 0 args */
1809 static Obj OnCharReadHookExcFds = 0;  /* a list of UNIX file descriptors */
1810 static Obj OnCharReadHookExcFuncs = 0;/* a list of GAP functions with 0 args */
1811 
OnCharReadHookActiveCheck(void)1812 static Int OnCharReadHookActiveCheck(void)
1813 {
1814     return OnCharReadHookActive != 0 || FreezeStdin != 0;
1815 }
1816 
1817 
HandleCharReadHook(int stdinfd)1818 static void HandleCharReadHook(int stdinfd)
1819 /* This is called directly before a character is read from stdin in the case
1820  * of an interactive session with command line editing. We have to return
1821  * as soon as stdin is ready to read! We just use `select' and care for
1822  * handlers for streams. */
1823 {
1824   fd_set infds,outfds,excfds;
1825   int n,maxfd;
1826   Int i,j;
1827   Obj o;
1828   static int WeAreAlreadyInHere = 0;
1829 
1830   /* Just to make sure: */
1831   if (WeAreAlreadyInHere) return;
1832   WeAreAlreadyInHere = 1;
1833 
1834   while (1) {  /* breaks when fd becomes ready */
1835     FD_ZERO(&infds);
1836     FD_ZERO(&outfds);
1837     FD_ZERO(&excfds);
1838     FD_SET(stdinfd,&infds);
1839     maxfd = stdinfd;
1840     /* Handle input file descriptors: */
1841     if (OnCharReadHookInFds != (Obj) 0 &&
1842         IS_PLIST(OnCharReadHookInFds) &&
1843         OnCharReadHookInFuncs != (Obj) 0 &&
1844         IS_PLIST(OnCharReadHookInFuncs)) {
1845       for (i = 1;i <= LEN_PLIST(OnCharReadHookInFds);i++) {
1846         o = ELM_PLIST(OnCharReadHookInFds,i);
1847         if (o != (Obj) 0 && IS_INTOBJ(o)) {
1848           j = INT_INTOBJ(o);  /* a UNIX file descriptor */
1849           FD_SET(j,&infds);
1850           if (j > maxfd) maxfd = j;
1851         }
1852       }
1853     }
1854     /* Handle output file descriptors: */
1855     if (OnCharReadHookOutFds != (Obj) 0 &&
1856         IS_PLIST(OnCharReadHookOutFds) &&
1857         OnCharReadHookOutFuncs != (Obj) 0 &&
1858         IS_PLIST(OnCharReadHookOutFuncs)) {
1859       for (i = 1;i <= LEN_PLIST(OnCharReadHookOutFds);i++) {
1860         o = ELM_PLIST(OnCharReadHookOutFds,i);
1861         if (o != (Obj) 0 && IS_INTOBJ(o)) {
1862           j = INT_INTOBJ(o);  /* a UNIX file descriptor */
1863           FD_SET(j,&outfds);
1864           if (j > maxfd) maxfd = j;
1865         }
1866       }
1867     }
1868     /* Handle exception file descriptors: */
1869     if (OnCharReadHookExcFds != (Obj) 0 &&
1870         IS_PLIST(OnCharReadHookExcFds) &&
1871         OnCharReadHookExcFuncs != (Obj) 0 &&
1872         IS_PLIST(OnCharReadHookExcFuncs)) {
1873       for (i = 1;i <= LEN_PLIST(OnCharReadHookExcFds);i++) {
1874         o = ELM_PLIST(OnCharReadHookExcFds,i);
1875         if (o != (Obj) 0 && IS_INTOBJ(o)) {
1876           j = INT_INTOBJ(o);  /* a UNIX file descriptor */
1877           FD_SET(j,&excfds);
1878           if (j > maxfd) maxfd = j;
1879         }
1880       }
1881     }
1882 
1883     n = select(maxfd+1,&infds,&outfds,&excfds,NULL);
1884     if (n >= 0) {
1885       /* Now run through the lists and call functions if ready: */
1886 
1887       if (OnCharReadHookInFds != (Obj) 0 &&
1888           IS_PLIST(OnCharReadHookInFds) &&
1889           OnCharReadHookInFuncs != (Obj) 0 &&
1890           IS_PLIST(OnCharReadHookInFuncs)) {
1891         for (i = 1;i <= LEN_PLIST(OnCharReadHookInFds);i++) {
1892           o = ELM_PLIST(OnCharReadHookInFds,i);
1893           if (o != (Obj) 0 && IS_INTOBJ(o)) {
1894             j = INT_INTOBJ(o);  /* a UNIX file descriptor */
1895             if (FD_ISSET(j,&infds)) {
1896               o = ELM_PLIST(OnCharReadHookInFuncs,i);
1897               if (o != (Obj) 0 && IS_FUNC(o))
1898                 Call1ArgsInNewReader(o,INTOBJ_INT(i));
1899             }
1900           }
1901         }
1902       }
1903       /* Handle output file descriptors: */
1904       if (OnCharReadHookOutFds != (Obj) 0 &&
1905           IS_PLIST(OnCharReadHookOutFds) &&
1906           OnCharReadHookOutFuncs != (Obj) 0 &&
1907           IS_PLIST(OnCharReadHookOutFuncs)) {
1908         for (i = 1;i <= LEN_PLIST(OnCharReadHookOutFds);i++) {
1909           o = ELM_PLIST(OnCharReadHookOutFds,i);
1910           if (o != (Obj) 0 && IS_INTOBJ(o)) {
1911             j = INT_INTOBJ(o);  /* a UNIX file descriptor */
1912             if (FD_ISSET(j,&outfds)) {
1913               o = ELM_PLIST(OnCharReadHookOutFuncs,i);
1914               if (o != (Obj) 0 && IS_FUNC(o))
1915                 Call1ArgsInNewReader(o,INTOBJ_INT(i));
1916             }
1917           }
1918         }
1919       }
1920       /* Handle exception file descriptors: */
1921       if (OnCharReadHookExcFds != (Obj) 0 &&
1922           IS_PLIST(OnCharReadHookExcFds) &&
1923           OnCharReadHookExcFuncs != (Obj) 0 &&
1924           IS_PLIST(OnCharReadHookExcFuncs)) {
1925         for (i = 1;i <= LEN_PLIST(OnCharReadHookExcFds);i++) {
1926           o = ELM_PLIST(OnCharReadHookExcFds,i);
1927           if (o != (Obj) 0 && IS_INTOBJ(o)) {
1928             j = INT_INTOBJ(o);  /* a UNIX file descriptor */
1929             if (FD_ISSET(j,&excfds)) {
1930               o = ELM_PLIST(OnCharReadHookExcFuncs,i);
1931               if (o != (Obj) 0 && IS_FUNC(o))
1932                 Call1ArgsInNewReader(o,INTOBJ_INT(i));
1933             }
1934           }
1935         }
1936       }
1937 
1938       // Return if there is input to read from stdin,
1939       // and FreezeStdin is false.
1940       if (FD_ISSET(stdinfd, &infds) && !FreezeStdin) {
1941           WeAreAlreadyInHere = 0;
1942           break;
1943       }
1944     } else
1945       break;
1946   } /* while (1) */
1947 }
1948 #endif   /* HAVE_SELECT */
1949 
1950 
1951 
1952 /***************************************************************************
1953 **
1954 *F HasAvailableBytes( <fid> ) returns positive if  a subsequent read to <fid>
1955 **                            will read at least one byte without blocking
1956 **
1957 */
1958 
HasAvailableBytes(UInt fid)1959 Int HasAvailableBytes( UInt fid )
1960 {
1961   UInt bufno;
1962   if (!SyBufInUse(fid))
1963     return -1;
1964 
1965   if (syBuf[fid].bufno >= 0)
1966     {
1967       bufno = syBuf[fid].bufno;
1968       if (syBuffers[bufno].bufstart < syBuffers[bufno].buflen)
1969         return 1;
1970     }
1971 
1972 #ifdef HAVE_SELECT
1973   // All sockets other than raw sockets are always ready
1974   if (syBuf[fid].type == raw_socket) {
1975     fd_set set;
1976     struct timeval tv;
1977     FD_ZERO( &set);
1978     FD_SET( syBuf[fid].fp, &set );
1979     tv.tv_sec = 0;
1980     tv.tv_usec = 0;
1981     return select( syBuf[fid].fp + 1, &set, NULL, NULL, &tv);
1982   }
1983 #endif
1984   /* best guess */
1985   Int ret = SyIsEndOfFile(fid);
1986   return (ret != -1 && ret != 1);
1987 }
1988 
1989 
syFgetsNoEdit(Char * line,UInt length,Int fid,UInt block)1990 static Char * syFgetsNoEdit(Char * line, UInt length, Int fid, UInt block)
1991 {
1992   UInt x = 0;
1993   int ret = 0;
1994 
1995   /* if stream is buffered, and the buffer has a full line,
1996    * grab it -- we could make more use of the buffer, but
1997    * this covers the majority of cases simply. */
1998 #ifndef LINE_END_HACK
1999   UInt bufno;
2000   Char* newlinepos;
2001   Char* bufstart;
2002   int buflen;
2003   if(!syBuf[fid].isTTY && syBuf[fid].bufno >= 0) {
2004     bufno = syBuf[fid].bufno;
2005     if (syBuffers[bufno].bufstart < syBuffers[bufno].buflen) {
2006       bufstart = syBuffers[bufno].buf + syBuffers[bufno].bufstart;
2007       buflen = syBuffers[bufno].buflen - syBuffers[bufno].bufstart;
2008       newlinepos = memchr(bufstart, '\n', buflen);
2009       if(newlinepos && (newlinepos - bufstart) < length - 2) {
2010           newlinepos++;
2011           memcpy(line, bufstart, newlinepos - bufstart);
2012           line[newlinepos - bufstart] = '\0';
2013           syBuffers[bufno].bufstart += (newlinepos - bufstart);
2014           return line;
2015       }
2016     }
2017   }
2018 #endif
2019 
2020   while (x < length -1) {
2021     if (!block && x && !HasAvailableBytes( fid ))
2022       {
2023         break;
2024       }
2025     ret = syGetch(fid);
2026     if (ret == EOF)
2027       break;
2028     if ((line[x++] = ret) == '\n')
2029       break;
2030   }
2031   line[x] = '\0';
2032   syBuf[fid].ateof = (ret == EOF);
2033   if (x)
2034     return line;
2035   else
2036     return NULL;
2037 }
2038 
2039 /* will be imported from library, first is generic function which does some
2040    checks before returning result to kernel, the second is the list of handler
2041    functions which do the actual work. */
2042 static Obj LineEditKeyHandler;
2043 static Obj LineEditKeyHandlers;
2044 static Obj GAPInfo;
2045 
2046 #ifdef HAVE_LIBREADLINE
2047 
2048 /* we import GAP level functions from GAPInfo components */
2049 static Obj CLEFuncs;
2050 static Obj KeyHandler;
2051 
2052 static int GAPMacroNumber = 0;
2053 
GAP_set_macro(int count,int key)2054 static int GAP_set_macro(int count, int key)
2055 {
2056  GAPMacroNumber = count;
2057  return 0;
2058 }
2059 /* a generic rl_command_func_t that delegates to GAP level */
GAP_rl_func(int count,int key)2060 static int GAP_rl_func(int count, int key)
2061 {
2062    Obj   rldata, linestr, okey, res, obj, data, beginchange, endchange, m;
2063    Int   len, n, hook, dlen, max, i;
2064 
2065    /* we shift indices 0-based on C-level and 1-based on GAP level */
2066    linestr = MakeString(rl_line_buffer);
2067    okey = INTOBJ_INT(key + 1000*GAPMacroNumber);
2068    GAPMacroNumber = 0;
2069    rldata = NEW_PLIST(T_PLIST, 6);
2070    if (GAP_rl_func == rl_last_func) {
2071      SET_LEN_PLIST(rldata, 6);
2072      SET_ELM_PLIST(rldata, 6, True);
2073    }
2074    else
2075      SET_LEN_PLIST(rldata, 5);
2076    SET_ELM_PLIST(rldata, 1, INTOBJ_INT(count));
2077    SET_ELM_PLIST(rldata, 2, okey);
2078    SET_ELM_PLIST(rldata, 3, linestr);
2079    SET_ELM_PLIST(rldata, 4, INTOBJ_INT(rl_point+1));
2080    SET_ELM_PLIST(rldata, 5, INTOBJ_INT(rl_mark+1));
2081    res = Call1ArgsInNewReader(KeyHandler, rldata);
2082    if (!res) return 0;
2083    if (!IS_LIST(res)) return 0;
2084    len = LEN_LIST(res);
2085    if (len == 0) return 0;
2086    obj = ELM_LIST(res, 1);
2087    if (IsStringConv(obj)) {
2088       /* insert txt */
2089       rl_insert_text(CONST_CSTR_STRING(obj));
2090       n = 1;
2091    } else if ((obj == True || obj == False) && len > 2) {
2092       /* kill or delete text */
2093       beginchange = ELM_LIST(res, 2);
2094       if (!IS_INTOBJ(beginchange)) return 0;
2095       endchange = ELM_LIST(res, 3);
2096       if (!IS_INTOBJ(endchange)) return 0;
2097       if (obj == True)
2098          rl_kill_text(INT_INTOBJ(beginchange)-1, INT_INTOBJ(endchange)-1);
2099       else
2100          rl_delete_text(INT_INTOBJ(beginchange)-1, INT_INTOBJ(endchange)-1);
2101       n = 3;
2102    }  else if (IS_INTOBJ(obj) && len > 2) {
2103       /* delete some text and insert */
2104       beginchange = obj;
2105       endchange = ELM_LIST(res, 2);
2106       if (!IS_INTOBJ(endchange)) return 0;
2107       obj = ELM_LIST(res, 3);
2108       if (!IsStringConv(obj)) return 0;
2109       rl_begin_undo_group();
2110       rl_delete_text(INT_INTOBJ(beginchange)-1, INT_INTOBJ(endchange)-1);
2111       rl_point = INT_INTOBJ(beginchange)-1;
2112       rl_insert_text(CONST_CSTR_STRING(obj));
2113       rl_end_undo_group();
2114       n = 3;
2115    } else if (IS_INTOBJ(obj) && len == 2) {
2116       /* several hooks to particular rl_ functions with data */
2117       hook = INT_INTOBJ(obj);
2118       data = ELM_LIST(res, 2);
2119       if (hook == 1) {
2120          /* display matches */
2121          if (!IS_LIST(data)) return 0;
2122          /* -1, because first is word to be completed */
2123          dlen = LEN_LIST(data)-1;
2124          /* +2, must be in 'argv' format, terminated by 0 */
2125          char **strs = (char**)calloc(dlen+2, sizeof(char*));
2126          max = 0;
2127          for (i=0; i <= dlen; i++) {
2128             if (!IsStringConv(ELM_LIST(data, i+1))) {
2129                free(strs);
2130                return 0;
2131             }
2132             strs[i] = CSTR_STRING(ELM_LIST(data, i+1));
2133             if (max < strlen(strs[i])) max = strlen(strs[i]);
2134          }
2135          rl_display_match_list(strs, dlen, max);
2136          free(strs);
2137          rl_on_new_line();
2138       }
2139       else if (hook == 2) {
2140          /* put these characters into sequence of input keys */
2141          if (!IsStringConv(data)) return 0;
2142          dlen = strlen(CSTR_STRING(data));
2143          for (i=0; i < dlen; i++)
2144              rl_stuff_char(CSTR_STRING(data)[i]);
2145       }
2146       n = 2;
2147    } else if (IS_INTOBJ(obj) && len == 1) {
2148       /* several hooks to particular rl_ functions with no data */
2149       hook = INT_INTOBJ(obj);
2150       /* ring bell */
2151       if (hook == 100) rl_ding();
2152       /* return line (execute Ctrl-m) */
2153       else if (hook == 101) rl_execute_next(13);
2154       n = 1;
2155    } else
2156       n = 0;
2157 
2158    /* optionally we can return the new point, or new point and mark */
2159    if (len > n) {
2160       n++;
2161       m = ELM_LIST(res, n);
2162       if (IS_INTOBJ(m))
2163           rl_point = INT_INTOBJ(m) - 1;
2164    }
2165    if (len > n) {
2166       n++;
2167       m = ELM_LIST(res, n);
2168       if (IS_INTOBJ(m))
2169           rl_mark = INT_INTOBJ(m) - 1;
2170    }
2171    return 0;
2172 }
2173 
FuncBINDKEYSTOGAPHANDLER(Obj self,Obj keys)2174 static Obj FuncBINDKEYSTOGAPHANDLER(Obj self, Obj keys)
2175 {
2176   Char*  seq;
2177 
2178   if (!IsStringConv(keys)) return False;
2179   seq = CSTR_STRING(keys);
2180   rl_bind_keyseq(seq, GAP_rl_func);
2181 
2182   return True;
2183 }
2184 
FuncBINDKEYSTOMACRO(Obj self,Obj keys,Obj macro)2185 static Obj FuncBINDKEYSTOMACRO(Obj self, Obj keys, Obj macro)
2186 {
2187   Char   *seq, *macr;
2188 
2189   if (!IsStringConv(keys)) return False;
2190   if (!IsStringConv(macro)) return False;
2191   seq = CSTR_STRING(keys);
2192   macr = CSTR_STRING(macro);
2193   rl_generic_bind(ISMACR, seq, macr, rl_get_keymap());
2194   return True;
2195 }
2196 
FuncREADLINEINITLINE(Obj self,Obj line)2197 static Obj FuncREADLINEINITLINE(Obj self, Obj line)
2198 {
2199   Char   *cline;
2200 
2201   if (!IsStringConv(line)) return False;
2202   cline = CSTR_STRING(line);
2203   rl_parse_and_bind(cline);
2204   return True;
2205 }
2206 
2207 /* init is needed once */
2208 static Int ISINITREADLINE = 0;
2209 /* a hook function called regularly while waiting on input */
2210 static Int current_rl_fid;
charreadhook_rl(void)2211 static int charreadhook_rl(void)
2212 {
2213 #ifdef HAVE_SELECT
2214     if (OnCharReadHookActiveCheck())
2215         HandleCharReadHook(syBuf[current_rl_fid].fp);
2216 #endif
2217   return 0;
2218 }
2219 
initreadline(void)2220 static void initreadline(void)
2221 {
2222 
2223   /* allows users to configure GAP specific settings in their ~/.inputrc like:
2224        $if GAP
2225           ....
2226        $endif                                                             */
2227   rl_readline_name = "GAP";
2228   /* this should pipe signals through to GAP  */
2229   rl_already_prompted = 1 ;
2230 
2231   rl_catch_signals = 0;
2232   rl_catch_sigwinch = 1;
2233   /* hook to read from other channels */
2234   rl_event_hook = 0;
2235   /* give GAP_rl_func a name that can be used in .inputrc */
2236   rl_add_defun( "handled-by-GAP", GAP_rl_func, -1 );
2237 
2238   rl_bind_keyseq("\\C-x\\C-g", GAP_set_macro);
2239 
2240   CLEFuncs = ELM_REC(GAPInfo, RNamName("CommandLineEditFunctions"));
2241   KeyHandler = ELM_REC(CLEFuncs, RNamName("KeyHandler"));
2242   ISINITREADLINE = 1;
2243 }
2244 
readlineFgets(Char * line,UInt length,Int fid,UInt block)2245 static Char * readlineFgets(Char * line, UInt length, Int fid, UInt block)
2246 {
2247   char *                 rlres = (char*)NULL;
2248 
2249   current_rl_fid = fid;
2250   if (!ISINITREADLINE) initreadline();
2251 
2252   /* read at most as much as we can buffer */
2253   rl_num_chars_to_read = length-2;
2254 #ifdef HAVE_SELECT
2255   /* hook to read from other channels */
2256   rl_event_hook = (OnCharReadHookActiveCheck()) ? charreadhook_rl : 0;
2257 #endif
2258   /* now do the real work */
2259   doingReadline = 1;
2260   rlres = readline(STATE(Prompt));
2261   doingReadline = 0;
2262   /* we get a NULL pointer on EOF, say by pressing Ctr-d  */
2263   if (!rlres) {
2264     if (!SyCTRD) {
2265       while (!rlres)
2266         rlres = readline(STATE(Prompt));
2267     }
2268     else {
2269       printf("\n");fflush(stdout);
2270       line[0] = '\0';
2271       return (Char*)0;
2272     }
2273   }
2274   /* maybe add to history, we use key 0 for this function */
2275   GAP_rl_func(0, 0);
2276   strlcpy(line, rlres, length);
2277   // FIXME: handle the case where rlres contains more than length
2278   // characters better?
2279   free(rlres);
2280   strlcat(line, "\n", length);
2281 
2282   /* send the whole line (unclipped) to the window handler               */
2283   syWinPut( fid, (*line != '\0' ? "@r" : "@x"), line );
2284 
2285   return line;
2286 }
2287 
2288 #endif
2289 
2290 #ifdef HPCGAP
2291 
2292 static GVarDescriptor GVarBeginEdit, GVarEndEdit;
2293 
syBeginEdit(Int fid)2294 static Int syBeginEdit(Int fid)
2295 {
2296   Obj func = GVarFunction(&GVarBeginEdit);
2297   Obj result;
2298   if (!func)
2299     return syStartraw(fid);
2300   result = CALL_1ARGS(func, INTOBJ_INT(fid));
2301   return result != False && result != Fail && result != INTOBJ_INT(0);
2302 }
2303 
syEndEdit(Int fid)2304 static Int syEndEdit(Int fid)
2305 {
2306   Obj func = GVarFunction(&GVarEndEdit);
2307   Obj result;
2308   if (!func) {
2309     syStopraw(fid);
2310     return 1;
2311   }
2312   result = CALL_1ARGS(func, INTOBJ_INT(fid));
2313   return result != False && result != Fail && result != INTOBJ_INT(0);
2314 }
2315 
2316 #else
2317 
2318 #define syBeginEdit(fid)    syStartraw(fid)
2319 #define syEndEdit(fid)      syStopraw(fid)
2320 
2321 #endif
2322 
syFgets(Char * line,UInt length,Int fid,UInt block)2323 static Char * syFgets(Char * line, UInt length, Int fid, UInt block)
2324 {
2325     Int                 ch,  ch2,  ch3, last;
2326     Char                * p,  * q,  * r,  * s,  * t;
2327     static Char         yank [32768];
2328     Char                old [512],  new [512];
2329     Int                 oldc,  newc;
2330     Int                 rep, len;
2331     Char                buffer [512];
2332     Int                 rn;
2333     Int                 rubdel;
2334     Obj                 linestr, yankstr, args, res;
2335 
2336     /* check file identifier                                               */
2337     if ( !SyBufInUse(fid) ) {
2338         return (Char*)0;
2339     }
2340 
2341     /* no line editing if the file is not '*stdin*' or '*errin*'           */
2342     if ( fid != 0 && fid != 2 ) {
2343       p = syFgetsNoEdit(line, length, fid, block);
2344 
2345         return p;
2346     }
2347 
2348     /* no line editing if the user disabled it
2349        or we can't make it into raw mode */
2350     if ( SyLineEdit == 0 || ! syBeginEdit(fid) ) {
2351         p = syFgetsNoEdit(line, length, fid, block );
2352         return p;
2353     }
2354 
2355 #ifdef HAVE_LIBREADLINE
2356     if (SyUseReadline) {
2357       /* switch back to cooked mode                                          */
2358       if ( SyLineEdit )
2359           syEndEdit(fid);
2360 
2361       p = readlineFgets(line, length, fid, block);
2362 
2363       if ( EndLineHook ) Call0ArgsInNewReader( EndLineHook );
2364       if (!p)
2365         return p;
2366       else
2367         return line;
2368     }
2369 #endif
2370 
2371     /* In line editing mode 'length' is not allowed bigger than the
2372       yank buffer (= length of line buffer for input files).*/
2373     if (length > 32768)
2374        ErrorQuit("Cannot handle lines with more than 32768 characters in line edit mode.",0,0);
2375 
2376     /* the line starts out blank                                           */
2377     line[0] = '\0';  p = line;
2378     for ( q = old; q < old+sizeof(old); ++q )  *q = ' ';
2379     oldc = 0;
2380     last = 0;
2381     ch = 0;
2382     rubdel=0; /* do we want to east a `del' character? */
2383 
2384     while ( 1 ) {
2385 
2386         /* get a character, handle <ctr>V<chr>, <esc><num> and <ctr>U<num> */
2387         rep = 1; ch2 = 0;
2388         do {
2389             if ( syESCN > 0 ) { if (ch == Esc('N')) {ch = '\n'; syESCN--; }
2390                                 else {ch = Esc('N'); } }
2391             else if ( syCTRO % 2 == 1 ) { ch = CTR('N'); syCTRO = syCTRO - 1; }
2392             else if ( syCTRO != 0 ) { ch = CTR('O'); rep = syCTRO / 2; }
2393             else {
2394 #ifdef HAVE_SELECT
2395                 if (OnCharReadHookActiveCheck())
2396                     HandleCharReadHook(syBuf[fid].fp);
2397 #endif
2398               ch = syGetch(fid);
2399             }
2400             if ( ch2==0        && ch==CTR('V') ) {             ch2=ch; ch=0;}
2401             if ( ch2==0        && ch==CTR('[') ) {             ch2=ch; ch=0;}
2402             if ( ch2==0        && ch==CTR('U') ) {             ch2=ch; ch=0;}
2403             if ( ch2==CTR('[') && ch==CTR('V') ) { ch2=Esc(CTR('V'));  ch=0;}
2404             if ( ch2==CTR('[') && IsDigit(ch)  ) { rep=ch-'0'; ch2=ch; ch=0;}
2405             if ( ch2==CTR('[') && ch=='['      ) {             ch2=ch; ch=0;}
2406             if ( ch2==CTR('U') && ch==CTR('V') ) { rep=4*rep;  ch2=ch; ch=0;}
2407             if ( ch2==CTR('U') && ch==CTR('[') ) { rep=4*rep;  ch2=ch; ch=0;}
2408             if ( ch2==CTR('U') && ch==CTR('U') ) { rep=4*rep;  ch2=ch; ch=0;}
2409             if ( ch2==CTR('U') && IsDigit(ch)  ) { rep=ch-'0'; ch2=ch; ch=0;}
2410             if ( IsDigit(ch2)  && ch==CTR('V') ) {             ch2=ch; ch=0;}
2411             if ( IsDigit(ch2)  && ch==CTR('[') ) {             ch2=ch; ch=0;}
2412             if ( IsDigit(ch2)  && ch==CTR('U') ) {             ch2=ch; ch=0;}
2413             if ( IsDigit(ch2)  && IsDigit(ch)  ) { rep=10*rep+ch-'0';  ch=0;}
2414             /* get rid of tilde in windows commands */
2415             if (rubdel==1) {
2416               if ( ch==126 ) {ch2=0;ch=0;};
2417               rubdel=0;
2418             }
2419         } while ( ch == 0 );
2420         if ( ch2==CTR('V') )       ch  = CTV(ch);
2421         if ( ch2==Esc(CTR('V')) )  ch  = CTV(ch | 0x80);
2422         if ( ch2==CTR('[') )       ch  = Esc(ch);
2423         if ( ch2==CTR('U') )       rep = 4*rep;
2424         /* windows keys */
2425         if ( ch2=='[' && ch=='A')  ch  = CTR('P');
2426         if ( ch2=='[' && ch=='B')  ch  = CTR('N');
2427         if ( ch2=='[' && ch=='C')  ch  = CTR('F');
2428         if ( ch2=='[' && ch=='D')  ch  = CTR('B');
2429         if ( ch2=='[' && ch=='1') { ch  = CTR('A');rubdel=1;} /* home */
2430         if ( ch2=='[' && ch=='3') { ch  = CTR('D');rubdel=1;} /* del */
2431         if ( ch2=='[' && ch=='4') { ch  = CTR('E');rubdel=1;} /* end */
2432         if ( ch2=='[' && ch=='5') { ch  = CTR('P');rubdel=1;} /* pgup */
2433         if ( ch2=='[' && ch=='6') { ch  = CTR('N');rubdel=1;} /* pgdwn */
2434 
2435         /* now perform the requested action <rep> times in the input line  */
2436         while ( rep-- > 0 ) {
2437           /* check for key handler on GAP level */
2438           Int runLineEditKeyHandler = 0;
2439           if (ch >= 0) {
2440 #ifdef HPCGAP
2441             RegionReadLock(REGION(LineEditKeyHandlers));
2442 #endif
2443             runLineEditKeyHandler =
2444                   ch < LEN_PLIST(LineEditKeyHandlers) &&
2445                   ELM_PLIST(LineEditKeyHandlers, ch + 1) != 0;
2446 #ifdef HPCGAP
2447             RegionUnlock(REGION(LineEditKeyHandlers));
2448 #endif
2449           }
2450           if (runLineEditKeyHandler) {
2451             /* prepare data for GAP handler:
2452                    [linestr, ch, ppos, length, yankstr]
2453                GAP handler must return new
2454                    [linestr, ppos, yankstr]
2455                or an integer, interpreted as number of Esc('N')
2456                calls for the next lines.                                  */
2457             linestr = MakeString(line);
2458             yankstr = MakeString(yank);
2459             args = NEW_PLIST(T_PLIST, 5);
2460             SET_LEN_PLIST(args, 5);
2461             SET_ELM_PLIST(args,1,linestr);
2462             SET_ELM_PLIST(args,2,INTOBJ_INT(ch));
2463             SET_ELM_PLIST(args,3,INTOBJ_INT((p-line)+1));
2464             SET_ELM_PLIST(args,4,INTOBJ_INT(length));
2465             SET_ELM_PLIST(args,5,yankstr);
2466             res = Call1ArgsInNewReader(LineEditKeyHandler, args);
2467             if (IS_INTOBJ(res)){
2468                syESCN = INT_INTOBJ(res);
2469                ch = Esc('N');
2470                SET_ELM_PLIST(args,2,INTOBJ_INT(ch));
2471                res = Call1ArgsInNewReader(LineEditKeyHandler, args);
2472             }
2473             if (IS_BAG_REF(res) && IS_LIST(res) && LEN_LIST(res) == 3) {
2474               linestr = ELM_LIST(res,1);
2475               len = GET_LEN_STRING(linestr);
2476               memcpy(line,CONST_CHARS_STRING(linestr),len);
2477               line[len] = '\0';
2478               p = line + (INT_INTOBJ(ELM_LIST(res,2)) - 1);
2479               yankstr = ELM_LIST(res,3);
2480               len = GET_LEN_STRING(yankstr);
2481               memcpy(yank,CONST_CHARS_STRING(yankstr),len);
2482               yank[len] = '\0';
2483             }
2484           }
2485           else {
2486             switch ( ch ) {
2487 
2488             case CTR('A'): /* move cursor to the start of the line         */
2489                 while ( p > line )  --p;
2490                 break;
2491 
2492             case Esc('B'): /* move cursor one word to the left             */
2493             case Esc('b'):
2494                 if ( p > line ) do {
2495                     --p;
2496                 } while ( p>line && (!IS_SEP(*(p-1)) || IS_SEP(*p)));
2497                 break;
2498 
2499             case CTR('B'): /* move cursor one character to the left        */
2500                 if ( p > line )  --p;
2501                 break;
2502 
2503             case CTR('F'): /* move cursor one character to the right       */
2504                 if ( *p != '\0' )  ++p;
2505                 break;
2506 
2507             case Esc('F'): /* move cursor one word to the right            */
2508             case Esc('f'):
2509                 if ( *p != '\0' ) do {
2510                     ++p;
2511                 } while ( *p!='\0' && (IS_SEP(*(p-1)) || !IS_SEP(*p)));
2512                 break;
2513 
2514             case CTR('E'): /* move cursor to the end of the line           */
2515                 while ( *p != '\0' )  ++p;
2516                 break;
2517 
2518             case CTR('H'): /* delete the character left of the cursor      */
2519             case 127:
2520                 if ( p == line ) break;
2521                 --p;
2522                 /* let '<ctr>-D' do the work                               */
2523 
2524             case CTR('D'): /* delete the character at the cursor           */
2525                            /* on an empty line '<ctr>-D' is <eof>          */
2526                 if ( p == line && *p == '\0' && SyCTRD && !rubdel ) {
2527                     ch = EOF; rep = 0; break;
2528                 }
2529                 if ( *p != '\0' ) {
2530                     for ( q = p; *(q+1) != '\0'; ++q )
2531                         *q = *(q+1);
2532                     *q = '\0';
2533                 }
2534                 break;
2535 
2536             case CTR('X'): /* delete the line                              */
2537                 p = line;
2538                 /* let '<ctr>-K' do the work                               */
2539 
2540             case CTR('K'): /* delete to end of line                        */
2541                 if ( last!=CTR('X') && last!=CTR('K') && last!=Esc(127)
2542                   && last!=Esc('D') && last!=Esc('d') )  yank[0] = '\0';
2543                 for ( r = yank; *r != '\0'; ++r ) ;
2544                 for ( s = p; *s != '\0'; ++s )  r[s-p] = *s;
2545                 r[s-p] = '\0';
2546                 *p = '\0';
2547                 break;
2548 
2549             case Esc(127): /* delete the word left of the cursor           */
2550                 q = p;
2551                 if ( p > line ) do {
2552                     --p;
2553                 } while ( p>line && (!IS_SEP(*(p-1)) || IS_SEP(*p)));
2554                 if ( last!=CTR('X') && last!=CTR('K') && last!=Esc(127)
2555                   && last!=Esc('D') && last!=Esc('d') )  yank[0] = '\0';
2556                 for ( r = yank; *r != '\0'; ++r ) ;
2557                 for ( ; yank <= r; --r )  r[q-p] = *r;
2558                 for ( s = p; s < q; ++s )  yank[s-p] = *s;
2559                 for ( r = p; *q != '\0'; ++q, ++r )
2560                     *r = *q;
2561                 *r = '\0';
2562                 break;
2563 
2564             case Esc('D'): /* delete the word right of the cursor          */
2565             case Esc('d'):
2566                 q = p;
2567                 if ( *q != '\0' ) do {
2568                     ++q;
2569                 } while ( *q!='\0' && (IS_SEP(*(q-1)) || !IS_SEP(*q)));
2570                 if ( last!=CTR('X') && last!=CTR('K') && last!=Esc(127)
2571                   && last!=Esc('D') && last!=Esc('d') )  yank[0] = '\0';
2572                 for ( r = yank; *r != '\0'; ++r ) ;
2573                 for ( s = p; s < q; ++s )  r[s-p] = *s;
2574                 r[s-p] = '\0';
2575                 for ( r = p; *q != '\0'; ++q, ++r )
2576                     *r = *q;
2577                 *r = '\0';
2578                 break;
2579 
2580             case CTR('T'): /* twiddle characters                           */
2581                 if ( p == line )  break;
2582                 if ( *p == '\0' )  --p;
2583                 if ( p == line )  break;
2584                 ch2 = *(p-1);  *(p-1) = *p;  *p = ch2;
2585                 ++p;
2586                 break;
2587 
2588             case CTR('Y'): /* insert (yank) deleted text                   */
2589                 if (strlen(yank) + strlen(line) - 2 > length) {
2590                     syEchoch(CTR('G'), fid);
2591                     break;
2592                 }
2593                 for ( r = yank; *r != '\0' && *r != '\n'; ++r ) {
2594                     ch2 = *r;
2595                     for ( q = p; ch2; ++q ) {
2596                         ch3 = *q; *q = ch2; ch2 = ch3;
2597                     }
2598                     *q = '\0'; ++p;
2599                 }
2600                 break;
2601 
2602             case Esc('U'): /* uppercase word                               */
2603             case Esc('u'):
2604                 if ( *p != '\0' ) do {
2605                     if ('a' <= *p && *p <= 'z')  *p = *p + 'A' - 'a';
2606                     ++p;
2607                 } while ( *p!='\0' && (IS_SEP(*(p-1)) || !IS_SEP(*p)));
2608                 break;
2609 
2610             case Esc('C'): /* capitalize word                              */
2611             case Esc('c'):
2612                 while ( *p!='\0' && IS_SEP(*p) )  ++p;
2613                 if ( 'a' <= *p && *p <= 'z' )  *p = *p + 'A'-'a';
2614                 if ( *p != '\0' ) ++p;
2615                 /* lowercase rest of the word                              */
2616 
2617             case Esc('L'): /* lowercase word                               */
2618             case Esc('l'):
2619                 if ( *p != '\0' ) do {
2620                     if ('A' <= *p && *p <= 'Z')  *p = *p + 'a' - 'A';
2621                     ++p;
2622                 } while ( *p!='\0' && (IS_SEP(*(p-1)) || !IS_SEP(*p)));
2623                 break;
2624 
2625             case Esc(CTR('L')): /* repaint input line                      */
2626                 syEchoch('\n',fid);
2627                 for ( q = syPrompt; q < syPrompt+syNrchar; ++q )
2628                     syEchoch( *q, fid );
2629                 for ( q = old; q < old+sizeof(old); ++q )  *q = ' ';
2630                 oldc = 0;
2631                 break;
2632 
2633             case EOF:     /* end of file on input                          */
2634                 break;
2635 
2636             case CTR('M'): /* (same as '\r', '\n') append \n and exit      */
2637             case CTR('J'):
2638                 while ( *p != '\0' )  ++p;
2639                 *p++ = '\n'; *p = '\0';
2640                 rep = 0;
2641                 break;
2642 
2643             case CTR('O'): /* accept line, perform '<ctr>-N' next time     */
2644                 while ( *p != '\0' )  ++p;
2645                 *p++ = '\n'; *p = '\0';
2646                 syCTRO = 2 * rep + 1;
2647                 rep = 0;
2648                 break;
2649 
2650             case CTR('I'): /* try to complete the identifier before dot    */
2651                 if ( p == line || IS_SEP(p[-1]) ) {
2652                   /* If we don't have an identifier to complete, insert a tab */
2653                     ch2 = ch & 0xff;
2654                     for ( q = p; ch2; ++q ) {
2655                         ch3 = *q; *q = ch2; ch2 = ch3;
2656                     }
2657                     *q = '\0'; ++p;
2658                 }
2659                 else {
2660   /* Here is actually a bug, because it is not checked if the results
2661      leaves 'line' shorter than 'length'. But we ignore this problem
2662      assuming that interactive input lines are much shorter than
2663      32768 characters.                                                       */
2664 
2665                   /* Locate in q the current identifier */
2666                     if ( (q = p) > line ) do {
2667                         --q;
2668                     } while ( q>line && (!IS_SEP(*(q-1)) || IS_SEP(*q)));
2669 
2670                     /* determine if the thing immediately before the
2671                        current identifier is a . */
2672                     rn = (line < q && *(q-1) == '.'
2673                                    && (line == q-1 || *(q-2) != '.'));
2674 
2675                     /* Copy the current identifier into buffer */
2676                     r = buffer;  s = q;
2677                     while ( s < p )  *r++ = *s++;
2678                     *r = '\0';
2679 
2680                     if ( (rn ? iscomplete_rnam( buffer, p-q )
2681                           : iscomplete_gvar( buffer, p-q )) ) {
2682                       /* Complete already, just beep for single tab */
2683                       if ( last != CTR('I') )
2684                         syEchoch( CTR('G'), fid );
2685                       else {
2686 
2687                         /* Double tab after a complete identifier
2688                            print list of completions */
2689                         syWinPut( fid, "@c", "" );
2690                         syEchos( "\n    ", fid );
2691                         syEchos( buffer, fid );
2692                         while ( (rn ? completion_rnam( buffer, p-q )
2693                                  : completion_gvar( buffer, p-q )) ) {
2694                           syEchos( "\n    ", fid );
2695                           syEchos( buffer, fid );
2696                         }
2697                         syEchos( "\n", fid );
2698 
2699                         /* Reprint the prompt and input line so far */
2700                         for ( q=syPrompt; q<syPrompt+syNrchar; ++q )
2701                           syEchoch( *q, fid );
2702                         for ( q = old; q < old+sizeof(old); ++q )
2703                           *q = ' ';
2704                         oldc = 0;
2705                         syWinPut( fid, (fid == 0 ? "@i" : "@e"), "" );
2706                       }
2707                     }
2708                     else if ( (rn ? ! completion_rnam( buffer, p-q )
2709                                   : ! completion_gvar( buffer, p-q )) ) {
2710 
2711                       /* Not complete, and there are no completions */
2712                         if ( last != CTR('I') )
2713 
2714                           /* beep after 1 tab */
2715                             syEchoch( CTR('G'), fid );
2716                         else {
2717 
2718                           /* print a message otherwise */
2719                           syWinPut( fid, "@c", "" );
2720                           syEchos("\n    identifier has no completions\n",
2721                                   fid);
2722                           for ( q=syPrompt; q<syPrompt+syNrchar; ++q )
2723                             syEchoch( *q, fid );
2724                           for ( q = old; q < old+sizeof(old); ++q )
2725                             *q = ' ';
2726                           oldc = 0;
2727                           syWinPut( fid, (fid == 0 ? "@i" : "@e"), "" );
2728                         }
2729                     }
2730                     else {
2731 
2732                       /*not complete and we have a completion. Now we have to
2733                         find the longest common prefix of all the completions*/
2734 
2735                         t = p;
2736 
2737                       /* Insert the necessary part of the current completion */
2738                         for ( s = buffer+(p-q); *s != '\0'; s++ ) {
2739 
2740                       /* Insert character from buffer into the line, I think */
2741                             ch2 = *s;
2742                             for ( r = p; ch2; r++ ) {
2743                                 ch3 = *r; *r = ch2; ch2 = ch3;
2744                             }
2745                             *r = '\0'; p++;
2746                         }
2747 
2748                         /* Now we work through the alternative
2749                            completions reducing p, each time to point
2750                            just after the longest common stem t
2751                            meanwhile still points to the place where
2752                            we started this batch of completion, so if
2753                            p gets down to t, we have nothing
2754                            unambiguous to add */
2755 
2756                         while ( t < p
2757                              && (rn ? completion_rnam( buffer, t-q )
2758                                     : completion_gvar( buffer, t-q )) ) {
2759 
2760                           /* check the length of common prefix */
2761                             r = t;  s = buffer+(t-q);
2762                             while ( r < p && *r == *s ) {
2763                                 r++; s++;
2764                             }
2765                             s = p;  p = r;
2766 
2767                             /* Now close up over the part of the
2768                                completion which turned out to be
2769                                ambiguous */
2770                             while ( *s != '\0' )  *r++ = *s++;
2771                             *r = '\0';
2772                         }
2773 
2774                         /* OK, now we have done the largest possible completion.
2775                            If it was nothing then we can't complete. Deal appropriately */
2776                         if ( t == p ) {
2777                             if ( last != CTR('I') )
2778                                 syEchoch( CTR('G'), fid );
2779                             else {
2780                                 syWinPut( fid, "@c", "" );
2781                                 buffer[t-q] = '\0';
2782                                 while (
2783                                   (rn ? completion_rnam( buffer, t-q )
2784                                       : completion_gvar( buffer, t-q )) ) {
2785                                     syEchos( "\n    ", fid );
2786                                     syEchos( buffer, fid );
2787                                 }
2788                                 syEchos( "\n", fid );
2789                                 for ( q=syPrompt; q<syPrompt+syNrchar; ++q )
2790                                     syEchoch( *q, fid );
2791                                 for ( q = old; q < old+sizeof(old); ++q )
2792                                     *q = ' ';
2793                                 oldc = 0;
2794                                 syWinPut( fid, (fid == 0 ? "@i" : "@e"), "");
2795                             }
2796                         }
2797 
2798                       /* If we managed to do some completion then we're happy */
2799                     }
2800                 }
2801                 break;
2802 
2803             default:      /* default, insert normal character              */
2804                 ch2 = ch & 0xff;
2805                 for ( q = p; ch2; ++q ) {
2806                     ch3 = *q; *q = ch2; ch2 = ch3;
2807                 }
2808                 *q = '\0'; ++p;
2809                 break;
2810 
2811             } /* switch ( ch ) */
2812           } /* key handler hook */
2813           last = ch;
2814         }
2815 
2816         if ( ch==EOF || ch=='\n' || ch=='\r' || ch==CTR('O') ) {
2817             /* if there is a hook for line ends, call it before echoing */
2818             if ( EndLineHook ) Call0ArgsInNewReader( EndLineHook );
2819             syEchoch('\r',fid);  syEchoch('\n',fid);  break;
2820         }
2821 
2822         /* now update the screen line according to the differences         */
2823         for ( q = line, r = new, newc = 0; *q != '\0'; ++q ) {
2824             if ( q == p )  newc = r-new;
2825             if ( *q==CTR('I') )  { do *r++=' '; while ((r-new+syNrchar)%8); }
2826             else if ( *q==0x7F ) { *r++ = '^'; *r++ = '?'; }
2827             else if ( /* '\0'<=*q  && */*q<' '  ) { *r++ = '^'; *r++ = *q+'@'; }
2828             else if ( ' ' <=*q && *q<0x7F ) { *r++ = *q; }
2829             else {
2830                 *r++ = '\\';                 *r++ = '0'+*(UChar*)q/64%4;
2831                 *r++ = '0'+*(UChar*)q/8 %8;  *r++ = '0'+*(UChar*)q   %8;
2832             }
2833             if ( r >= new+SyNrCols-syNrchar-2 ) {
2834                 if ( q >= p ) { q++; break; }
2835                 new[0] = '$';   new[1] = r[-5]; new[2] = r[-4];
2836                 new[3] = r[-3]; new[4] = r[-2]; new[5] = r[-1];
2837                 r = new+6;
2838             }
2839         }
2840         if ( q == p )  newc = r-new;
2841         for (      ; r < new+sizeof(new); ++r )  *r = ' ';
2842         if ( q[0] != '\0' && q[1] != '\0' )
2843             new[SyNrCols-syNrchar-2] = '$';
2844         else if ( q[1] == '\0' && ' ' <= *q && *q < 0x7F )
2845             new[SyNrCols-syNrchar-2] = *q;
2846         else if ( q[1] == '\0' && q[0] != '\0' )
2847             new[SyNrCols-syNrchar-2] = '$';
2848         for ( q = old, r = new; r < new+sizeof(new); ++r, ++q ) {
2849             if ( *q == *r )  continue;
2850             while (oldc<(q-old)) { syEchoch(old[oldc],fid);  ++oldc; }
2851             while (oldc>(q-old)) { syEchoch('\b',fid);       --oldc; }
2852             *q = *r;  syEchoch( *q, fid ); ++oldc;
2853         }
2854         while ( oldc < newc ) { syEchoch(old[oldc],fid);  ++oldc; }
2855         while ( oldc > newc ) { syEchoch('\b',fid);       --oldc; }
2856 
2857 
2858     }
2859 
2860     if (line[1] != '\0') {
2861       /* Now we put the new string into the history,
2862          we use key handler with key 0 to update the command line history */
2863       linestr = MakeString(line);
2864       args = NEW_PLIST(T_PLIST, 5);
2865       SET_LEN_PLIST(args, 5);
2866       SET_ELM_PLIST(args, 1, linestr);
2867       SET_ELM_PLIST(args, 2, INTOBJ_INT(0));
2868       SET_ELM_PLIST(args, 3, INTOBJ_INT(1));
2869       SET_ELM_PLIST(args, 4, INTOBJ_INT(length));
2870       SET_ELM_PLIST(args, 5, linestr);
2871       Call1ArgsInNewReader(LineEditKeyHandler, args);
2872     }
2873 
2874     /* send the whole line (unclipped) to the window handler               */
2875     syWinPut( fid, (*line != '\0' ? "@r" : "@x"), line );
2876 
2877     /* switch back to cooked mode                                          */
2878     if ( SyLineEdit == 1 )
2879         syEndEdit(fid);
2880 
2881     /* return the line (or '0' at end-of-file)                             */
2882     if ( *line == '\0' )
2883         return (Char*)0;
2884     return line;
2885 }
2886 
SyFgets(Char * line,UInt length,Int fid)2887 Char * SyFgets (
2888     Char *              line,
2889     UInt                length,
2890     Int                 fid)
2891 {
2892   return syFgets( line, length, fid, 1);
2893 }
2894 
2895 
SyFgetsSemiBlock(Char * line,UInt length,Int fid)2896 Char *SyFgetsSemiBlock (
2897     Char *              line,
2898     UInt                length,
2899     Int                 fid)
2900 {
2901   return syFgets( line, length, fid, 0);
2902 }
2903 
2904 
2905 /****************************************************************************
2906 **
2907 *F * * * * * * * * * * * * system error messages  * * * * * * * * * * * * * *
2908 */
2909 
2910 
2911 /****************************************************************************
2912 **
2913 *V  SyLastErrorNo . . . . . . . . . . . . . . . . . . . . . last error number
2914 */
2915 Int SyLastErrorNo;
2916 
2917 
2918 /****************************************************************************
2919 **
2920 *V  SyLastErrorMessage  . . . . . . . . . . . . . . . . .  last error message
2921 */
2922 Char SyLastErrorMessage [ 1024 ];
2923 
2924 
2925 /****************************************************************************
2926 **
2927 *F  SyClearErrorNo()  . . . . . . . . . . . . . . . . .  clear error messages
2928 */
2929 
SyClearErrorNo(void)2930 void SyClearErrorNo ( void )
2931 {
2932     errno = 0;
2933     SyLastErrorNo = 0;
2934     strxcpy( SyLastErrorMessage, "no error", sizeof(SyLastErrorMessage) );
2935 }
2936 
2937 
2938 /****************************************************************************
2939 **
2940 *F  SySetErrorNo()  . . . . . . . . . . . . . . . . . . . . set error message
2941 */
2942 
SySetErrorNo(void)2943 void SySetErrorNo ( void )
2944 {
2945     const Char *        err;
2946 
2947     if ( errno != 0 ) {
2948         SyLastErrorNo = errno;
2949         err = strerror(errno);
2950         strxcpy( SyLastErrorMessage, err, sizeof(SyLastErrorMessage) );
2951     }
2952     else {
2953         SyClearErrorNo();
2954     }
2955 }
2956 
2957 /****************************************************************************
2958 **
2959 *F * * * * * * * * * * * * * file and execution * * * * * * * * * * * * * * *
2960 */
2961 
2962 /****************************************************************************
2963 **
2964 *F  SyExecuteProcess( <dir>, <prg>, <in>, <out>, <args> ) . . . . new process
2965 **
2966 **  Start  <prg> in  directory <dir>  with  standard input connected to <in>,
2967 **  standard  output  connected to <out>   and arguments.  No  path search is
2968 **  performed, the return  value of the process  is returned if the operation
2969 **  system supports such a concept.
2970 */
2971 
2972 
2973 /****************************************************************************
2974 **
2975 *f  SyExecuteProcess( <dir>, <prg>, <in>, <out>, <args> )
2976 */
2977 #if defined(HAVE_FORK) || defined(HAVE_VFORK)
2978 
2979 #ifndef WEXITSTATUS
2980 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
2981 #endif
2982 #ifndef WIFEXITED
2983 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
2984 #endif
2985 
2986 #ifdef SYS_IS_CYGWIN32
2987 
SyExecuteProcess(Char * dir,Char * prg,Int in,Int out,Char * args[])2988 UInt SyExecuteProcess (
2989     Char *                  dir,
2990     Char *                  prg,
2991     Int                     in,
2992     Int                     out,
2993     Char *                  args[] )
2994 {
2995     int savestdin, savestdout;
2996     Int tin, tout;
2997     int res;
2998 
2999     /* change the working directory                                    */
3000     if ( chdir(dir) == -1 ) return -1;
3001 
3002     /* if <in> is -1 open "/dev/null"                                  */
3003     if ( in == -1 )
3004         tin = open( "/dev/null", O_RDONLY );
3005     else
3006         tin = SyBufFileno(in);
3007     if ( tin == -1 )
3008         return -1;
3009 
3010     /* if <out> is -1 open "/dev/null"                                 */
3011     if ( out == -1 )
3012         tout = open( "/dev/null", O_WRONLY );
3013     else
3014         tout = SyBufFileno(out);
3015     if ( tout == -1 ) {
3016         if (in == -1) close(tin);
3017         return -1;
3018     }
3019 
3020     /* set standard input to <in>, standard output to <out>            */
3021     savestdin = -1;   /* Just to please the compiler */
3022     if ( tin != 0 ) {
3023         savestdin = dup(0);
3024         if (savestdin == -1 || dup2(tin,0) == -1) {
3025             if (out == -1) close(tout);
3026             if (in == -1) close(tin);
3027             return -1;
3028         }
3029         fcntl( 0, F_SETFD, 0 );
3030     }
3031 
3032     if ( tout != 1 ) {
3033         savestdout = dup(1);
3034         if (savestdout == -1 || dup2( tout, 1 ) == -1) {
3035             if (tin != 0) {
3036                 close(0);
3037                 dup2(savestdin,0);
3038                 close(savestdin);
3039             }
3040             if (out == -1) close(tout);
3041             if (in == -1) close(tin);
3042             return -1;
3043         }
3044         fcntl( 1, F_SETFD, 0 );
3045     }
3046 
3047     FreezeStdin = 1;
3048     /* now try to execute the program                                  */
3049     res = spawnve( _P_WAIT, prg, (const char * const *) args,
3050                                  (const char * const *) environ );
3051 
3052     /* Now repair the open file descriptors: */
3053     if (tout != 1) {
3054         close(1);
3055         dup2(savestdout,1);
3056         close(savestdout);
3057     }
3058     if (tin != 0) {
3059         close(0);
3060         dup2(savestdin,0);
3061         close(savestdin);
3062     }
3063     if (out == -1) close(tout);
3064     if (in == -1) close(tin);
3065 
3066     FreezeStdin = 0;
3067 
3068     /* Report result: */
3069     if (res < 0) return -1;
3070     return WEXITSTATUS(res);
3071 }
3072 
3073 #else
3074 
NullSignalHandler(int scratch)3075 static void NullSignalHandler(int scratch)
3076 {
3077 }
3078 
SyExecuteProcess(Char * dir,Char * prg,Int in,Int out,Char * args[])3079 UInt SyExecuteProcess (
3080     Char *                  dir,
3081     Char *                  prg,
3082     Int                     in,
3083     Int                     out,
3084     Char *                  args[] )
3085 {
3086     pid_t                   pid;                    /* process id          */
3087     pid_t                   wait_pid;
3088     int                     status;                 /* do not use `Int'    */
3089     Int                     tin;                    /* temp in             */
3090     Int                     tout;                   /* temp out            */
3091     sig_handler_t           *func;
3092     sig_handler_t           * volatile func2;
3093 
3094 
3095     /* turn off the SIGCHLD handling, so that we can be sure to collect this child
3096        `After that, we call the old signal handler, in case any other children have died in the
3097        meantime. This resets the handler */
3098 
3099     func2 = signal( SIGCHLD, SIG_DFL );
3100 
3101     /* This may return SIG_DFL (0x0) or SIG_IGN (0x1) if the previous handler
3102      * was set to the default or 'ignore'. In these cases (or if SIG_ERR is
3103      * returned), just use a null signal hander - the default on most systems
3104      * is to do nothing */
3105     if(func2 == SIG_ERR || func2 == SIG_DFL || func2 == SIG_IGN)
3106       func2 = &NullSignalHandler;
3107 
3108     /* clone the process                                                   */
3109     pid = vfork();
3110     if ( pid == -1 ) {
3111         return -1;
3112     }
3113 
3114     /* we are the parent                                                   */
3115     if ( pid != 0 ) {
3116         // Stop trying to read input
3117         FreezeStdin = 1;
3118 
3119         /* ignore a CTRL-C                                                 */
3120         func = signal( SIGINT, SIG_IGN );
3121 
3122         /* wait for some action                                            */
3123         wait_pid = waitpid( pid, &status, 0 );
3124         FreezeStdin = 0;
3125         if ( wait_pid == -1 ) {
3126             signal( SIGINT, func );
3127             (*func2)(SIGCHLD);
3128             return -1;
3129         }
3130 
3131         if ( WIFSIGNALED(status) ) {
3132             signal( SIGINT, func );
3133             (*func2)(SIGCHLD);
3134             return -1;
3135         }
3136         signal( SIGINT, func );
3137         (*func2)(SIGCHLD);
3138         return WEXITSTATUS(status);
3139     }
3140 
3141     /* we are the child                                                    */
3142     else {
3143 
3144         /* change the working directory                                    */
3145         if ( chdir(dir) == -1 ) {
3146             _exit(-1);
3147         }
3148 
3149         /* if <in> is -1 open "/dev/null"                                  */
3150         if ( in == -1 ) {
3151             tin = open( "/dev/null", O_RDONLY );
3152         }
3153         else {
3154             tin = SyBufFileno(in);
3155         }
3156         if ( tin == -1 ) {
3157             _exit(-1);
3158         }
3159 
3160         /* if <out> is -1 open "/dev/null"                                 */
3161         if ( out == -1 ) {
3162             tout = open( "/dev/null", O_WRONLY );
3163         }
3164         else {
3165             tout = SyBufFileno(out);
3166         }
3167         if ( tout == -1 ) {
3168             _exit(-1);
3169         }
3170 
3171         /* set standard input to <in>, standard output to <out>            */
3172         if ( tin != 0 ) {
3173             if ( dup2( tin, 0 ) == -1 ) {
3174                 _exit(-1);
3175             }
3176         }
3177         fcntl( 0, F_SETFD, 0 );
3178 
3179         if ( tout != 1 ) {
3180             if ( dup2( tout, 1 ) == -1 ) {
3181                 _exit(-1);
3182             }
3183         }
3184         fcntl( 1, F_SETFD, 0 );
3185 
3186         /* now try to execute the program                                  */
3187         execve( prg, args, environ );
3188         _exit(-1);
3189     }
3190 
3191     /* this should not happen                                              */
3192     return -1;
3193 }
3194 #endif
3195 
3196 #endif
3197 
3198 
3199 /****************************************************************************
3200 **
3201 *F  SyIsExistingFile( <name> )  . . . . . . . . . . . does file <name> exists
3202 **
3203 **  'SyIsExistingFile' returns 1 if the  file <name> exists and 0  otherwise.
3204 **  It does not check if the file is readable, writable or excuteable. <name>
3205 **  is a system dependent description of the file.
3206 */
SyIsExistingFile(const Char * name)3207 Int SyIsExistingFile ( const Char * name )
3208 {
3209     Int         res;
3210 
3211     SyClearErrorNo();
3212     res = access( name, F_OK );
3213     if ( res == -1 ) {
3214         SySetErrorNo();
3215     }
3216     return res;
3217 }
3218 
3219 /****************************************************************************
3220 **
3221 *F  SyIsReadableFile( <name> )  . . . . . . . . . . . is file <name> readable
3222 **
3223 **  'SyIsReadableFile'   returns 0  if the   file  <name> is   readable and
3224 **  -1 otherwise. <name> is a system dependent description of the file.
3225 */
SyIsReadableFile(const Char * name)3226 Int SyIsReadableFile ( const Char * name )
3227 {
3228     Int         res;
3229     Char        xname[1024];
3230 
3231     SyClearErrorNo();
3232     res = access( name, R_OK );
3233     if ( res == -1 ) {
3234       /* we might be able to read the file via zlib */
3235 
3236       /* beware of buffer overflows */
3237       if ( strlcpy(xname, name, sizeof(xname)) < sizeof(xname) &&
3238             strlcat(xname, ".gz", sizeof(xname))  < sizeof(xname) ) {
3239         res = access(xname, R_OK);
3240       }
3241 
3242       if (res == -1)
3243         SySetErrorNo();
3244     }
3245     return res;
3246 }
3247 
3248 
3249 /****************************************************************************
3250 **
3251 *F  SyIsWritableFile( <name> )  . . . . . . . . . is the file <name> writable
3252 **
3253 **  'SyIsWritableFile'   returns  1  if  the  file  <name> is  writable and 0
3254 **  otherwise. <name> is a system dependent description of the file.
3255 */
SyIsWritableFile(const Char * name)3256 Int SyIsWritableFile ( const Char * name )
3257 {
3258     Int         res;
3259 
3260     SyClearErrorNo();
3261     res = access( name, W_OK );
3262     if ( res == -1 ) {
3263         SySetErrorNo();
3264     }
3265     return res;
3266 }
3267 
3268 
3269 /****************************************************************************
3270 **
3271 *F  SyIsExecutableFile( <name> )  . . . . . . . . . is file <name> executable
3272 **
3273 **  'SyIsExecutableFile' returns 1 if the  file <name>  is  executable and  0
3274 **  otherwise. <name> is a system dependent description of the file.
3275 */
SyIsExecutableFile(const Char * name)3276 Int SyIsExecutableFile ( const Char * name )
3277 {
3278     Int         res;
3279 
3280     SyClearErrorNo();
3281     res = access( name, X_OK );
3282     if ( res == -1 ) {
3283         SySetErrorNo();
3284     }
3285     return res;
3286 }
3287 
3288 
3289 /****************************************************************************
3290 **
3291 *F  SyIsDirectoryPath( <name> ) . . . . . . . . .  is file <name> a directory
3292 **
3293 **  'SyIsDirectoryPath' returns 1 if the  file <name>  is a directory  and  0
3294 **  otherwise. <name> is a system dependent description of the file.
3295 */
SyIsDirectoryPath(const Char * name)3296 Int SyIsDirectoryPath ( const Char * name )
3297 {
3298     struct stat     buf;                /* buffer for `stat'               */
3299 
3300     SyClearErrorNo();
3301     if ( stat( name, &buf ) == -1 ) {
3302         SySetErrorNo();
3303         return -1;
3304     }
3305     return S_ISDIR(buf.st_mode) ? 0 : -1;
3306 }
3307 
3308 
3309 /****************************************************************************
3310 **
3311 *F  SyRemoveFile( <name> )  . . . . . . . . . . . . . . .  remove file <name>
3312 */
SyRemoveFile(const Char * name)3313 Int SyRemoveFile ( const Char * name )
3314 {
3315     Int res;
3316     SyClearErrorNo();
3317     res = unlink(name);
3318     if (res == -1)
3319        SySetErrorNo();
3320     return res;
3321 }
3322 
3323 /****************************************************************************
3324 **
3325 *f  SyMkdir( <name> )  . . . . . . . . . . . . . . . . create directory
3326 **  with users umask permissions.
3327 */
SyMkdir(const Char * name)3328 Int SyMkdir ( const Char * name )
3329 {
3330     Int res;
3331     SyClearErrorNo();
3332     res = mkdir(name, 0777);
3333     if (res == -1)
3334        SySetErrorNo();
3335     return res;
3336 }
3337 
3338 /****************************************************************************
3339 **
3340 *f  SyRemoveDir( <name> )  . . . . . . . . . . . . . . . . .  using `rmdir'
3341 */
SyRmdir(const Char * name)3342 Int SyRmdir ( const Char * name )
3343 {
3344     Int res;
3345     SyClearErrorNo();
3346     res = rmdir(name);
3347     if (res == -1)
3348        SySetErrorNo();
3349     return res;
3350 }
3351 
3352 /****************************************************************************
3353 **
3354 *F  SyIsDir( <name> )  . . . . . . . . . . . . .  test if something is a dir
3355 **
3356 **  Returns 'F' for a regular file, 'L' for a symbolic link and 'D'
3357 **  for a real directory, 'C' for a character device, 'B' for a block
3358 **  device 'P' for a FIFO (named pipe) and 'S' for a socket.
3359 */
SyIsDir(const Char * name)3360 Obj SyIsDir ( const Char * name )
3361 {
3362   Int res;
3363   struct stat ourlstatbuf;
3364 
3365   res = lstat(name,&ourlstatbuf);
3366   if (res < 0) {
3367     SySetErrorNo();
3368     return Fail;
3369   }
3370   if      (S_ISREG(ourlstatbuf.st_mode)) return ObjsChar['F'];
3371   else if (S_ISDIR(ourlstatbuf.st_mode)) return ObjsChar['D'];
3372   else if (S_ISLNK(ourlstatbuf.st_mode)) return ObjsChar['L'];
3373 #ifdef S_ISCHR
3374   else if (S_ISCHR(ourlstatbuf.st_mode)) return ObjsChar['C'];
3375 #endif
3376 #ifdef S_ISBLK
3377   else if (S_ISBLK(ourlstatbuf.st_mode)) return ObjsChar['B'];
3378 #endif
3379 #ifdef S_ISFIFO
3380   else if (S_ISFIFO(ourlstatbuf.st_mode)) return ObjsChar['P'];
3381 #endif
3382 #ifdef S_ISSOCK
3383   else if (S_ISSOCK(ourlstatbuf.st_mode)) return ObjsChar['S'];
3384 #endif
3385   else return ObjsChar['?'];
3386 }
3387 
3388 /****************************************************************************
3389 **
3390 *F  SyFindGapRootFile( <filename>,<buffer> ) . .  find file in system area
3391 */
SyFindGapRootFile(const Char * filename,Char * buffer,size_t bufferSize)3392 Char * SyFindGapRootFile ( const Char * filename, Char * buffer, size_t bufferSize )
3393 {
3394     Int k;
3395 
3396     for ( k = 0; k < ARRAY_SIZE(SyGapRootPaths); k++ ) {
3397         if ( SyGapRootPaths[k][0] ) {
3398             if (strlcpy( buffer, SyGapRootPaths[k], bufferSize ) >= bufferSize)
3399                 continue;
3400             if (strlcat( buffer, filename, bufferSize ) >= bufferSize)
3401                 continue;
3402             if ( SyIsReadableFile(buffer) == 0 ) {
3403                 return buffer;
3404             }
3405         }
3406     }
3407     buffer[0] = '\0';
3408     return 0;
3409 }
3410 
3411 
3412 /****************************************************************************
3413 **
3414 *F * * * * * * * * * * * * * * * directories  * * * * * * * * * * * * * * * *
3415 */
3416 
3417 
3418 /****************************************************************************
3419 **
3420 *F  SyTmpname() . . . . . . . . . . . . . . . . . return a temporary filename
3421 **
3422 **  'SyTmpname' creates and returns  a new temporary name.  Subsequent  calls
3423 **  to 'SyTmpname'  should  produce different  file names  *even* if no files
3424 **  were created.
3425 */
SyTmpname(void)3426 Char *SyTmpname ( void )
3427 {
3428   static char name[1024];
3429   strxcpy(name, "/tmp/gaptempfile.XXXXXX", sizeof(name));
3430   close(mkstemp(name));
3431   return name;
3432 }
3433 
3434 
3435 /****************************************************************************
3436 **
3437 *F  SyTmpdir( <hint> )  . . . . . . . . . . . .  return a temporary directory
3438 **
3439 **  'SyTmpdir'  returns the directory   for  a temporary directory.  This  is
3440 **  guaranteed  to be newly  created and empty  immediately after the call to
3441 **  'SyTmpdir'. <hint> should be used by 'SyTmpdir' to  construct the name of
3442 **  the directory (but 'SyTmpdir' is free to use only  a part of <hint>), and
3443 **  must be a string of at most 8 alphanumerical characters.  Under UNIX this
3444 **  would   usually   represent   '/usr/tmp/<hint>_<proc_id>_<cnt>/',   e.g.,
3445 **  '/usr/tmp/guava_17188_1/'.
3446 */
SyTmpdir(const Char * hint)3447 Char * SyTmpdir( const Char * hint )
3448 {
3449 #ifdef SYS_IS_CYGWIN32
3450 #define TMPDIR_BASE "/cygdrive/c/WINDOWS/Temp/"
3451 #else
3452 #define TMPDIR_BASE "/tmp/"
3453 #endif
3454   static char name[1024];
3455   static const char *base = TMPDIR_BASE;
3456   char * env_tmpdir = getenv("TMPDIR");
3457   if (env_tmpdir != NULL) {
3458     strxcpy(name, env_tmpdir, sizeof(name));
3459     strxcat(name, "/", sizeof(name));
3460   }
3461   else
3462     strxcpy(name, base, sizeof(name));
3463   if (hint)
3464     strxcat(name, hint, sizeof(name));
3465   else
3466     strxcat(name, "gaptempdir", sizeof(name));
3467   strxcat(name, "XXXXXX", sizeof(name));
3468   return mkdtemp(name);
3469 }
3470 
3471 /****************************************************************************
3472 **
3473 *F  SyReadStringFile( <fid> ) . . . . . . . . read file content into a string
3474 **
3475 */
SyReadStringFile(Int fid)3476 Obj SyReadStringFile(Int fid)
3477 {
3478     Char            buf[32769];
3479     Int             ret, len;
3480     UInt            lstr;
3481     Obj             str;
3482 
3483     /* read <fid> until we see  eof   (in 32kB pieces)                     */
3484     str = NEW_STRING(0);
3485     len = 0;
3486     do {
3487         ret = SyRead(fid, buf, 32768);
3488         if (ret < 0) {
3489             SySetErrorNo();
3490             return Fail;
3491         }
3492         len += ret;
3493         GROW_STRING( str, len );
3494         lstr = GET_LEN_STRING(str);
3495         memcpy( CHARS_STRING(str)+lstr, buf, ret );
3496         *(CHARS_STRING(str)+lstr+ret) = '\0';
3497         SET_LEN_STRING(str, lstr+ret);
3498     } while(ret > 0);
3499 
3500     /* fix the length of <str>                                             */
3501     len = GET_LEN_STRING(str);
3502     ResizeBag( str, SIZEBAG_STRINGLEN(len) );
3503 
3504     syBuf[fid].ateof = 1;
3505     return str;
3506 }
3507 
3508 #if !defined(SYS_IS_CYGWIN32)
3509 /* fstat seems completely broken under CYGWIN */
3510 /* first try to get the whole file as one chunk, this avoids garbage
3511    collections because of the GROW_STRING calls below    */
SyReadStringFileStat(Int fid)3512 static Obj SyReadStringFileStat(Int fid)
3513 {
3514     Int             ret, len;
3515     Obj             str;
3516     Int             l;
3517     char            *ptr;
3518     struct stat     fstatbuf;
3519 
3520     GAP_ASSERT(syBuf[fid].type != gzip_socket);
3521 
3522     if( fstat( syBuf[fid].fp, &fstatbuf) == 0 ) {
3523         if((off_t)(Int)fstatbuf.st_size != fstatbuf.st_size) {
3524             ErrorMayQuit(
3525                 "The file is too big to fit the current workspace",
3526                 (Int)0, (Int)0);
3527         }
3528         len = (Int) fstatbuf.st_size;
3529         str = NEW_STRING( len );
3530         CHARS_STRING(str)[len] = '\0';
3531         SET_LEN_STRING(str, len);
3532         ptr = CSTR_STRING(str);
3533         while (len > 0) {
3534             l = (len > 1048576) ? 1048576 : len;
3535             ret = SyRead(fid, ptr, l);
3536             if (ret == -1) {
3537                 SySetErrorNo();
3538                 return Fail;
3539             }
3540             len -= ret;
3541             ptr += ret;
3542         }
3543         syBuf[fid].ateof = 1;
3544         return str;
3545     } else {
3546         SySetErrorNo();
3547         return Fail;
3548     }
3549 }
3550 
SyReadStringFid(Int fid)3551 Obj SyReadStringFid(Int fid)
3552 {
3553     if (syBuf[fid].type != raw_socket) {
3554         return SyReadStringFile(fid);
3555     } else {
3556         return SyReadStringFileStat(fid);
3557     }
3558 }
3559 
3560 #else
3561 
SyReadStringFid(Int fid)3562 Obj SyReadStringFid(Int fid) {
3563     return SyReadStringFile(fid);
3564 }
3565 
3566 #endif
3567 
3568 
3569 #ifdef USE_CUSTOM_MEMMOVE
3570 // The memmove in glibc on 32-bit SSE2 systems, contained in
3571 // __memmove_sse2_unaligned, is buggy in at least versions
3572 // 2.21 - 2.26 when crossing the 2GB boundary, so GAP must
3573 // include its own simple memmove implementation.
SyMemmove(void * dst,const void * src,UInt size)3574 void * SyMemmove(void * dst, const void * src, UInt size)
3575 {
3576     char *       d = dst;
3577     const char * s = src;
3578 
3579     if ((d == s) || (size == 0))
3580         return dst;
3581 
3582     if (d + size < s || d > s + size) {
3583         memcpy(dst, src, size);
3584     }
3585     else if (d > s) {
3586         d = d + (size - 1);
3587         s = s + (size - 1);
3588 
3589         // This is about 4x slower than glibc, but
3590         // is simple and also complicated enough that
3591         // gcc or clang seem unable to "optimise" it back
3592         // into a call to memmove.
3593 
3594         // Do size 4 jumps
3595         while (size > 4) {
3596             *d = *s;
3597             *(d - 1) = *(s - 1);
3598             *(d - 2) = *(s - 2);
3599             *(d - 3) = *(s - 3);
3600             d -= 4;
3601             s -= 4;
3602             size -= 4;
3603         }
3604         // Finish
3605         while (size > 0) {
3606             *d-- = *s--;
3607             size--;
3608         }
3609     }
3610     else {
3611         while (size > 4) {
3612             *d = *s;
3613             *(d + 1) = *(s + 1);
3614             *(d + 2) = *(s + 2);
3615             *(d + 3) = *(s + 3);
3616             d += 4;
3617             s += 4;
3618             size -= 4;
3619         }
3620         // Finish
3621         while (size > 0) {
3622             *d++ = *s++;
3623             size--;
3624         }
3625     }
3626     return dst;
3627 }
3628 #endif
3629 
3630 
3631 /****************************************************************************
3632 **
3633 *V  GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
3634 */
3635 static StructGVarFunc GVarFuncs [] = {
3636 
3637     GVAR_FUNC(CrcString, 1, "string"),
3638 #ifdef HAVE_LIBREADLINE
3639     GVAR_FUNC(BINDKEYSTOGAPHANDLER, 1, "keyseq"),
3640     GVAR_FUNC(BINDKEYSTOMACRO, 2, "keyseq, macro"),
3641     GVAR_FUNC(READLINEINITLINE, 1, "line"),
3642 #endif
3643 
3644     { 0, 0, 0, 0, 0 }
3645 
3646 };
3647 
3648 
3649 /****************************************************************************
3650 **
3651 *F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
3652 */
3653 
3654 // This function is called by 'InitSystem', before the usual module
3655 // initialization.
InitSysFiles(void)3656 void InitSysFiles(void)
3657 {
3658     memset(syBuffers, 0, sizeof(syBuf));
3659 
3660     memset(syBuf, 0, sizeof(syBuf));
3661 
3662     // open the standard files
3663     struct stat stat_in, stat_out, stat_err;
3664     fstat(fileno(stdin), &stat_in);
3665     fstat(fileno(stdout), &stat_out);
3666     fstat(fileno(stderr), &stat_err);
3667 
3668     // set up stdin
3669     syBuf[0].type = raw_socket;
3670     syBuf[0].fp = fileno(stdin);
3671     syBuf[0].echo = fileno(stdout);
3672     syBuf[0].bufno = -1;
3673     syBuf[0].isTTY = isatty(fileno(stdin));
3674     if (syBuf[0].isTTY) {
3675         // if stdin is on a terminal, make sure stdout in on the same terminal
3676         if (stat_in.st_dev != stat_out.st_dev ||
3677             stat_in.st_ino != stat_out.st_ino)
3678             syBuf[0].echo = open(ttyname(fileno(stdin)), O_WRONLY);
3679     }
3680 
3681     // set up stdout
3682     syBuf[1].type = raw_socket;
3683     syBuf[1].echo = syBuf[1].fp = fileno(stdout);
3684     syBuf[1].bufno = -1;
3685     syBuf[1].isTTY = isatty(fileno(stdout));
3686 
3687     // set up errin (defaults to stdin, unless stderr is on a terminal)
3688     syBuf[2].type = raw_socket;
3689     syBuf[2].fp = fileno(stdin);
3690     syBuf[2].echo = fileno(stderr);
3691     syBuf[2].bufno = -1;
3692     syBuf[2].isTTY = isatty(fileno(stderr));
3693     if (syBuf[2].isTTY) {
3694         // if stderr is on a terminal, make sure errin in on the same terminal
3695         if (stat_in.st_dev != stat_err.st_dev ||
3696             stat_in.st_ino != stat_err.st_ino)
3697             syBuf[2].fp = open(ttyname(fileno(stderr)), O_RDONLY);
3698     }
3699 
3700     // set up errout
3701     syBuf[3].type = raw_socket;
3702     syBuf[3].echo = syBuf[3].fp = fileno(stderr);
3703     syBuf[3].bufno = -1;
3704 
3705     // turn off buffering
3706     setbuf(stdin, (char *)0);
3707     setbuf(stdout, (char *)0);
3708     setbuf(stderr, (char *)0);
3709 }
3710 
3711 /* TODO: Should probably do some checks preSave for open files etc and refuse to save
3712    if any are found */
3713 
3714 /****************************************************************************
3715 **
3716 *F  InitKernel( <module> ) . . . . . . .  initialise kernel data structures
3717 */
3718 
InitKernel(StructInitInfo * module)3719 static Int InitKernel(
3720       StructInitInfo * module )
3721 {
3722   /* init filters and functions                                          */
3723   InitHdlrFuncsFromTable( GVarFuncs );
3724 
3725   /* line edit key handler from library                                  */
3726   ImportGVarFromLibrary("GAPInfo", &GAPInfo);
3727   ImportFuncFromLibrary("LineEditKeyHandler", &LineEditKeyHandler);
3728   ImportGVarFromLibrary("LineEditKeyHandlers", &LineEditKeyHandlers);
3729 
3730 #ifdef HPCGAP
3731   /* GAP hooks to allow library to override how we start/stop raw mode   */
3732   DeclareGVar(&GVarBeginEdit, "TERMINAL_BEGIN_EDIT");
3733   DeclareGVar(&GVarEndEdit, "TERMINAL_END_EDIT");
3734 #endif
3735 
3736 #ifdef HAVE_SELECT
3737     InitCopyGVar("OnCharReadHookActive",&OnCharReadHookActive);
3738     InitCopyGVar("OnCharReadHookInFds",&OnCharReadHookInFds);
3739     InitCopyGVar("OnCharReadHookInFuncs",&OnCharReadHookInFuncs);
3740     InitCopyGVar("OnCharReadHookOutFds",&OnCharReadHookOutFds);
3741     InitCopyGVar("OnCharReadHookOutFuncs",&OnCharReadHookOutFuncs);
3742     InitCopyGVar("OnCharReadHookExcFds",&OnCharReadHookExcFds);
3743     InitCopyGVar("OnCharReadHookExcFuncs",&OnCharReadHookExcFuncs);
3744 #endif
3745 
3746 
3747   /* return success                                                      */
3748   return 0;
3749 
3750 }
3751 
3752 /****************************************************************************
3753 **
3754 *F  InitLibrary( <module> ) . . . . . . .  initialise library data structures
3755 */
3756 
InitLibrary(StructInitInfo * module)3757 static Int InitLibrary(
3758       StructInitInfo * module )
3759 {
3760   /* init filters and functions                                          */
3761   InitGVarFuncsFromTable( GVarFuncs );
3762 
3763   return 0;
3764 }
3765 
3766 /****************************************************************************
3767 **
3768 *F  InitInfoSysFiles()  . . . . . . . . . . . . . . . table of init functions
3769 */
3770 static StructInitInfo module = {
3771     // init struct using C99 designated initializers; for a full list of
3772     // fields, please refer to the definition of StructInitInfo
3773     .type = MODULE_BUILTIN,
3774     .name = "sysfiles",
3775     .initKernel = InitKernel,
3776     .initLibrary = InitLibrary,
3777 };
3778 
InitInfoSysFiles(void)3779 StructInitInfo * InitInfoSysFiles ( void )
3780 {
3781     return &module;
3782 }
3783