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