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" contains all operating system
11 ** dependent functions. This file contains all system dependent functions
12 ** except file and stream operations, which are implemented in "sysfiles.c".
13 ** The following labels determine which operating system is actually used.
14 */
15
16 #include "system.h"
17
18 #include "gaputils.h"
19 #ifdef GAP_MEM_CHECK
20 #include "gasman_intern.h"
21 #endif
22 #include "profile.h"
23 #include "sysfiles.h"
24 #include "sysmem.h"
25 #include "sysopt.h"
26
27 #ifdef HPCGAP
28 #include "hpc/misc.h"
29 #endif
30
31 #ifdef USE_JULIA_GC
32 #include "julia.h"
33 #endif
34
35 #include <assert.h>
36 #include <fcntl.h>
37 #include <stdarg.h>
38 #include <time.h>
39 #include <unistd.h>
40
41 #include <sys/stat.h>
42
43
44 #ifdef HAVE_LIBREADLINE
45 #include <readline/readline.h>
46 #endif
47
48 #include <sys/time.h> /* definition of 'struct timeval' */
49 #include <sys/types.h>
50
51 #ifdef HAVE_SYS_RESOURCE_H
52 #include <sys/resource.h> /* definition of 'struct rusage' */
53 #endif
54
55 #ifdef SYS_IS_DARWIN
56 #include <mach/mach_time.h>
57 #endif
58
59
60 /****************************************************************************
61 **
62 *V SyKernelVersion . . . . . . . . . . . . . . . hard coded kernel version
63 ** do not edit the following line. Occurrences of `4.11.0' and `today'
64 ** will be replaced by string matching by distribution wrapping scripts.
65 */
66 const Char * SyKernelVersion = "4.11.0";
67
68 /****************************************************************************
69 **
70 *F * * * * * * * * * * * command line settable options * * * * * * * * * * *
71 */
72
73 /****************************************************************************
74 **
75 *V SyArchitecture . . . . . . . . . . . . . . . . name of the architecture
76 */
77 const Char * SyArchitecture = GAPARCH;
78
79
80 /****************************************************************************
81 **
82 *V SyCTRD . . . . . . . . . . . . . . . . . . . true if '<ctr>-D' is <eof>
83 */
84 UInt SyCTRD;
85
86
87 /****************************************************************************
88 **
89 *V SyCompileInput . . . . . . . . . . . . . . . . . . from this input file
90 */
91 Char SyCompileInput[GAP_PATH_MAX];
92
93
94 /****************************************************************************
95 **
96 *V SyCompileMagic1 . . . . . . . . . . . . . . . . . . and this magic string
97 */
98 Char * SyCompileMagic1;
99
100
101 /****************************************************************************
102 **
103 *V SyCompileName . . . . . . . . . . . . . . . . . . . . . . with this name
104 */
105 Char SyCompileName[256];
106
107
108 /****************************************************************************
109 **
110 *V SyCompileOutput . . . . . . . . . . . . . . . . . . into this output file
111 */
112 Char SyCompileOutput[GAP_PATH_MAX];
113
114
115 /****************************************************************************
116 **
117 *V SyCompilePlease . . . . . . . . . . . . . . . tell GAP to compile a file
118 */
119 Int SyCompilePlease;
120
121
122 /****************************************************************************
123 **
124 *V SyDebugLoading . . . . . . . . . output messages about loading of files
125 */
126 Int SyDebugLoading;
127
128
129 /****************************************************************************
130 **
131 *V SyGapRootPaths . . . . . . . . . . . . . . . . . . . array of root paths
132 **
133 */
134 Char SyGapRootPaths[MAX_GAP_DIRS][GAP_PATH_MAX];
135 Char DotGapPath[GAP_PATH_MAX];
136
137 /****************************************************************************
138 **
139 *V IgnoreGapRC . . . . . . . . . . . . . . . . . . . -r option for kernel
140 **
141 */
142 static Int IgnoreGapRC;
143
144 /****************************************************************************
145 **
146 *V SyLineEdit . . . . . . . . . . . . . . . . . . . . support line editing
147 **
148 ** 0: no line editing
149 ** 1: line editing if terminal
150 ** 2: always line editing (EMACS)
151 */
152 UInt SyLineEdit;
153
154 /****************************************************************************
155 **
156 *V SyUseReadline . . . . . . . . . . . . . . . . . . support line editing
157 **
158 ** Switch for not using readline although GAP is compiled with libreadline
159 */
160 UInt SyUseReadline;
161
162 /****************************************************************************
163 **
164 *V SyMsgsFlagBags . . . . . . . . . . . . . . . . . enable gasman messages
165 **
166 ** 'SyMsgsFlagBags' determines whether garbage collections are reported or
167 ** not.
168 **
169 ** Per default it is false, i.e. Gasman is silent about garbage collections.
170 ** It can be changed by using the '-g' option on the GAP command line.
171 **
172 ** This is used in the function 'SyMsgsBags' below.
173 **
174 ** Put in this package because the command line processing takes place here.
175 */
176 UInt SyMsgsFlagBags;
177
178
179 /****************************************************************************
180 **
181 *V SyNrCols . . . . . . . . . . . . . . . . . . length of the output lines
182 **
183 ** 'SyNrCols' is the length of the lines on the standard output device.
184 **
185 ** Per default this is 80 characters which is the usual width of terminals.
186 ** It can be changed by the '-x' options for larger terminals or printers.
187 **
188 ** 'Pr' uses this to decide where to insert a <newline> on the output lines.
189 ** 'SyRead' uses it to decide when to start scrolling the echoed input line.
190 **
191 ** See also getwindowsize() below.
192 **
193 ** Put in this package because the command line processing takes place here.
194 */
195 UInt SyNrCols;
196 UInt SyNrColsLocked;
197
198 /****************************************************************************
199 **
200 *V SyNrRows . . . . . . . . . . . . . . . . . number of lines on the screen
201 **
202 ** 'SyNrRows' is the number of lines on the standard output device.
203 **
204 ** Per default this is 24, which is the usual size of terminal screens.
205 ** It can be changed with the '-y' option for larger terminals or printers.
206 **
207 ** 'SyHelp' uses this to decide where to stop with '-- <space> for more --'.
208 **
209 ** See also getwindowsize() below.
210 */
211 UInt SyNrRows;
212 UInt SyNrRowsLocked;
213
214 /****************************************************************************
215 **
216 *V SyQuiet . . . . . . . . . . . . . . . . . . . . . . . . . suppress prompt
217 **
218 ** 'SyQuiet' determines whether GAP should print the prompt and the banner.
219 **
220 ** Per default its false, i.e. GAP prints the prompt and the nice banner.
221 ** It can be changed by the '-q' option to have GAP operate in silent mode.
222 **
223 ** It is used by the functions in 'gap.c' to suppress printing the prompts.
224 **
225 ** Put in this package because the command line processing takes place here.
226 */
227 UInt SyQuiet;
228
229 /****************************************************************************
230 **
231 *V SyQuitOnBreak . . . . . . . . . . exit GAP instead of entering break loop
232 */
233 UInt SyQuitOnBreak;
234
235 /****************************************************************************
236 **
237 *V SyRestoring . . . . . . . . . . . . . . . . . . . . restoring a workspace
238 **
239 ** `SyRestoring' determines whether GAP is restoring a workspace or not. If
240 ** it is zero no restoring should take place otherwise it holds the filename
241 ** of a workspace to restore.
242 **
243 */
244 Char * SyRestoring;
245
246
247 /****************************************************************************
248 **
249 *V SyInitializing set to 1 during library init
250 **
251 ** `SyInitializing' is set to 1 during the library initialization phase of
252 ** startup. It suppresses some behaviours that may not be possible so early
253 ** such as homogeneity tests in the plist code.
254 */
255
256 UInt SyInitializing;
257
258
259 /****************************************************************************
260 **
261 *V SyLoadSystemInitFile . . . . . . should GAP load 'lib/init.g' at startup
262 */
263 Int SyLoadSystemInitFile = 1;
264
265
266 /****************************************************************************
267 **
268 *V SyUseModule . . . . . . . . . check for static modules in 'READ_GAP_ROOT'
269 */
270 int SyUseModule;
271
272
273 /****************************************************************************
274 **
275 *V SyWindow . . . . . . . . . . . . . . . . running under a window handler
276 **
277 ** 'SyWindow' is 1 if GAP is running under a window handler front end such
278 ** as 'xgap', and 0 otherwise.
279 **
280 ** If running under a window handler front end, GAP adds various commands
281 ** starting with '@' to the output to let 'xgap' know what is going on.
282 */
283 UInt SyWindow;
284
285
286 /****************************************************************************
287 **
288 *F * * * * * * * * * * * * * time related functions * * * * * * * * * * * * *
289 */
290
291 /****************************************************************************
292 **
293 *F SyTime() . . . . . . . . . . . . . . . return time spent in milliseconds
294 **
295 ** 'SyTime' returns the number of milliseconds spent by GAP so far.
296 **
297 ** Should be as accurate as possible, because it is used for profiling.
298 */
SyTime(void)299 UInt SyTime ( void )
300 {
301 struct rusage buf;
302
303 if ( getrusage( RUSAGE_SELF, &buf ) ) {
304 Panic("'SyTime' could not get time");
305 }
306 return buf.ru_utime.tv_sec*1000 + buf.ru_utime.tv_usec/1000;
307 }
SyTimeSys(void)308 UInt SyTimeSys ( void )
309 {
310 struct rusage buf;
311
312 if ( getrusage( RUSAGE_SELF, &buf ) ) {
313 Panic("'SyTimeSys' could not get time");
314 }
315 return buf.ru_stime.tv_sec*1000 + buf.ru_stime.tv_usec/1000;
316 }
SyTimeChildren(void)317 UInt SyTimeChildren ( void )
318 {
319 struct rusage buf;
320
321 if ( getrusage( RUSAGE_CHILDREN, &buf ) ) {
322 Panic("'SyTimeChildren' could not get time");
323 }
324 return buf.ru_utime.tv_sec*1000 + buf.ru_utime.tv_usec/1000;
325 }
SyTimeChildrenSys(void)326 UInt SyTimeChildrenSys ( void )
327 {
328 struct rusage buf;
329
330 if ( getrusage( RUSAGE_CHILDREN, &buf ) ) {
331 Panic("'SyTimeChildrenSys' could not get time");
332 }
333 return buf.ru_stime.tv_sec*1000 + buf.ru_stime.tv_usec/1000;
334 }
335
336
337 /****************************************************************************
338 **
339 *F * * * * * * * * * * * * * * * string functions * * * * * * * * * * * * * *
340 */
341
342
343 #ifndef HAVE_STRLCPY
344
strlcpy(char * dst,const char * src,size_t len)345 size_t strlcpy (
346 char *dst,
347 const char *src,
348 size_t len)
349 {
350 /* Keep a copy of the original src. */
351 const char * const orig_src = src;
352
353 /* If a non-empty len was specified, we can actually copy some data. */
354 if (len > 0) {
355 /* Copy up to len-1 bytes (reserve one for the terminating zero). */
356 while (--len > 0) {
357 /* Copy from src to dst; if we reach the string end, we are
358 done and can simply return the total source string length */
359 if ((*dst++ = *src++) == 0) {
360 /* return length of source string without the zero byte */
361 return src - orig_src - 1;
362 }
363 }
364
365 /* If we got here, then we used up the whole buffer and len is zero.
366 We must make sure to terminate the destination string. */
367 *dst = 0;
368 }
369
370 /* in the end, we must return the length of the source string, no
371 matter whether we completely copied or not; so advance src
372 till its terminator is reached */
373 while (*src++)
374 ;
375
376 /* return length of source string without the zero byte */
377 return src - orig_src - 1;
378 }
379
380 #endif /* !HAVE_STRLCPY */
381
382
383 #ifndef HAVE_STRLCAT
384
strlcat(char * dst,const char * src,size_t len)385 size_t strlcat (
386 char *dst,
387 const char *src,
388 size_t len)
389 {
390 /* Keep a copy of the original dst. */
391 const char * const orig_dst = dst;
392
393 /* Find the end of the dst string, so that we can append after it. */
394 while (*dst != 0 && len > 0) {
395 dst++;
396 len--;
397 }
398
399 /* We can only append anything if there is free space left in the
400 destination buffer. */
401 if (len > 0) {
402 /* One byte goes away for the terminating zero. */
403 len--;
404
405 /* Do the actual work and append from src to dst, until we either
406 appended everything, or reached the dst buffer's end. */
407 while (*src != 0 && len > 0) {
408 *dst++ = *src++;
409 len--;
410 }
411
412 /* Terminate, terminate, terminate! */
413 *dst = 0;
414 }
415
416 /* Compute the final result. */
417 return (dst - orig_dst) + strlen(src);
418 }
419
420 #endif /* !HAVE_STRLCAT */
421
strxcpy(char * dst,const char * src,size_t len)422 size_t strxcpy (
423 char *dst,
424 const char *src,
425 size_t len)
426 {
427 size_t res = strlcpy(dst, src, len);
428 assert(res < len);
429 return res;
430 }
431
strxcat(char * dst,const char * src,size_t len)432 size_t strxcat (
433 char *dst,
434 const char *src,
435 size_t len)
436 {
437 size_t res = strlcat(dst, src, len);
438 assert(res < len);
439 return res;
440 }
441
442 /****************************************************************************
443 **
444 *F SySleep( <secs> ) . . . . . . . . . . . . . .sleep GAP for <secs> seconds
445 **
446 ** NB Various OS events (like signals) might wake us up
447 **
448 */
SySleep(UInt secs)449 void SySleep ( UInt secs )
450 {
451 sleep( (unsigned int) secs );
452 }
453
454 /****************************************************************************
455 **
456 *F SyUSleep( <msecs> ) . . . . . . . . . .sleep GAP for <msecs> microseconds
457 **
458 ** NB Various OS events (like signals) might wake us up
459 **
460 */
SyUSleep(UInt msecs)461 void SyUSleep ( UInt msecs )
462 {
463 usleep( (unsigned int) msecs );
464 }
465
466 /****************************************************************************
467 **
468 *F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
469 */
470
471
472 /****************************************************************************
473 **
474 *F SyExit( <ret> ) . . . . . . . . . . . . . exit GAP with return code <ret>
475 **
476 ** 'SyExit' is the official way to exit GAP, bus errors are the unofficial.
477 ** The function 'SyExit' must perform all the necessary cleanup operations.
478 ** If ret is 0 'SyExit' should signal to a calling process that all is ok.
479 ** If ret is 1 'SyExit' should signal a failure to the calling process.
480 **
481 ** If the user calls 'QUIT_GAP' with a value, then the global variable
482 ** 'UserHasQUIT' will be set, and their requested return value will be
483 ** in 'SystemErrorCode'. If the return value would be 0, we check
484 ** this value and use it instead.
485 */
SyExit(UInt ret)486 void SyExit (
487 UInt ret )
488 {
489 #ifdef USE_JULIA_GC
490 jl_atexit_hook(ret);
491 #endif
492 exit( (int)ret );
493 }
494
495
496 /****************************************************************************
497 **
498 *F Panic( <msg> )
499 */
Panic_(const char * file,int line,const char * fmt,...)500 void Panic_(const char * file, int line, const char * fmt, ...)
501 {
502 fprintf(stderr, "Panic in %s:%d: ", file, line);
503 va_list args;
504 va_start(args, fmt);
505 vfprintf(stderr, fmt, args);
506 va_end (args);
507 fputs("\n", stderr);
508 SyExit(1);
509 }
510
511
512 /****************************************************************************
513 **
514 *F SyNanosecondsSinceEpoch()
515 **
516 ** 'SyNanosecondsSinceEpoch' returns a 64-bit integer which represents the
517 ** number of nanoseconds since some unspecified starting point. This means
518 ** that the number returned by this function is not in itself meaningful,
519 ** but the difference between the values returned by two consecutive calls
520 ** can be used to measure wallclock time.
521 **
522 ** The accuracy of this is system dependent. For systems that implement
523 ** clock_getres, we could get the promised accuracy.
524 **
525 ** Note that gettimeofday has been marked obsolete in the POSIX standard.
526 ** We are using it because it is implemented in most systems still.
527 **
528 ** If we are using gettimeofday we cannot guarantee the values that
529 ** are returned by SyNanosecondsSinceEpoch to be monotonic.
530 **
531 ** Returns -1 to represent failure
532 **
533 */
SyNanosecondsSinceEpoch(void)534 Int8 SyNanosecondsSinceEpoch(void)
535 {
536 Int8 res;
537
538 #if defined(SYS_IS_DARWIN)
539 static mach_timebase_info_data_t timeinfo;
540 if ( timeinfo.denom == 0 ) {
541 (void) mach_timebase_info(&timeinfo);
542 }
543 res = mach_absolute_time();
544
545 res *= timeinfo.numer;
546 res /= timeinfo.denom;
547 #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
548 struct timespec ts;
549
550 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
551 res = ts.tv_sec;
552 res *= 1000000000L;
553 res += ts.tv_nsec;
554 } else {
555 res = -1;
556 }
557 #elif defined(HAVE_GETTIMEOFDAY)
558 struct timeval tv;
559
560 if (gettimeofday(&tv, NULL) == 0) {
561 res = tv.tv_sec;
562 res *= 1000000L;
563 res += tv.tv_usec;
564 res *= 1000;
565 } else {
566 res = -1;
567 };
568 #else
569 res = -1;
570 #endif
571
572 return res;
573 }
574
575
576 /****************************************************************************
577 **
578 *V SyNanosecondsSinceEpochMethod
579 *V SyNanosecondsSinceEpochMonotonic
580 **
581 ** These constants give information about the method used to obtain
582 ** NanosecondsSinceEpoch, and whether the values returned are guaranteed
583 ** to be monotonic.
584 */
585 #if defined(SYS_IS_DARWIN)
586 const char * const SyNanosecondsSinceEpochMethod = "mach_absolute_time";
587 const Int SyNanosecondsSinceEpochMonotonic = 1;
588 #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
589 const char * const SyNanosecondsSinceEpochMethod = "clock_gettime";
590 const Int SyNanosecondsSinceEpochMonotonic = 1;
591 #elif defined(HAVE_GETTIMEOFDAY)
592 const char * const SyNanosecondsSinceEpochMethod = "gettimeofday";
593 const Int SyNanosecondsSinceEpochMonotonic = 0;
594 #else
595 const char * const SyNanosecondsSinceEpochMethod = "unsupported";
596 const Int SyNanosecondsSinceEpochMonotonic = 0;
597 #endif
598
599
600 /****************************************************************************
601 **
602 *F SyNanosecondsSinceEpochResolution()
603 **
604 ** 'SyNanosecondsSinceEpochResolution' returns a 64-bit integer which
605 ** represents the resolution in nanoseconds of the timer used for
606 ** SyNanosecondsSinceEpoch.
607 **
608 ** If the return value is positive then the value has been returned
609 ** by the operating system can can probably be relied on. If the
610 ** return value is negative it is just an estimate (as in the case
611 ** of gettimeofday we have no way to get the exact resolution so we
612 ** just pretend that the resolution is 1000 nanoseconds).
613 **
614 ** A result of 0 signifies inability to obtain any sensible value.
615 */
SyNanosecondsSinceEpochResolution(void)616 Int8 SyNanosecondsSinceEpochResolution(void)
617 {
618 Int8 res;
619
620 #if defined(SYS_IS_DARWIN)
621 static mach_timebase_info_data_t timeinfo;
622 if ( timeinfo.denom == 0 ) {
623 (void) mach_timebase_info(&timeinfo);
624 }
625 res = timeinfo.numer;
626 res /= timeinfo.denom;
627 #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
628 struct timespec ts;
629
630 if (clock_getres(CLOCK_MONOTONIC, &ts) == 0) {
631 res = ts.tv_sec;
632 res *= 1000000000L;
633 res += ts.tv_nsec;
634 } else {
635 res = 0;
636 }
637 #elif defined(HAVE_GETTIMEOFDAY)
638 res = -1000;
639 #else
640 res = 0;
641 #endif
642
643 return res;
644 }
645
646 /****************************************************************************
647 **
648 *F SySetGapRootPath( <string> ) . . . . . . . . . set the root directories
649 **
650 ** 'SySetGapRootPath' takes a string and modifies a list of root directories
651 ** in 'SyGapRootPaths'.
652 **
653 ** A leading semicolon in <string> means that the list of directories in
654 ** <string> is appended to the existing list of root paths. A trailing
655 ** semicolon means they are prepended. If there is no leading or trailing
656 ** semicolon, then the root paths are overwritten.
657 */
658
659
660 /****************************************************************************
661 **
662 *f SySetGapRootPath( <string> )
663 **
664 ** This function assumes that the system uses '/' as path separator.
665 ** Currently, we support nothing else. For Windows (or rather: Cygwin), we
666 ** rely on a small hack which converts the path separator '\' used there
667 ** on '/' on the fly. Put differently: Systems that use completely different
668 ** path separators, or none at all, are currently not supported.
669 */
670
SySetGapRootPath(const Char * string)671 static void SySetGapRootPath( const Char * string )
672 {
673 const Char * p;
674 Char * q;
675 Int i;
676 Int n;
677
678 /* set string to a default value if unset */
679 if ( string == 0 || *string == 0 ) {
680 string = "./";
681 }
682
683 /*
684 ** check if we append, prepend or overwrite.
685 */
686 if( string[0] == ';' ) {
687 /* Count the number of root directories already present. */
688 n = 0; while( SyGapRootPaths[n][0] != '\0' ) n++;
689
690 /* Skip leading semicolon. */
691 string++;
692
693 }
694 else if( string[ strlen(string) - 1 ] == ';' ) {
695 /* Count the number of directories in 'string'. */
696 n = 0; p = string; while( *p ) if( *p++ == ';' ) n++;
697
698 /* Find last root path. */
699 for( i = 0; i < MAX_GAP_DIRS; i++ )
700 if( SyGapRootPaths[i][0] == '\0' ) break;
701 i--;
702
703 #ifdef HPCGAP
704 n *= 2; // for each root <ROOT> we also add <ROOT/hpcgap> as a root
705 #endif
706
707 /* Move existing root paths to the back */
708 if( i + n >= MAX_GAP_DIRS ) return;
709 while( i >= 0 ) {
710 memcpy( SyGapRootPaths[i+n], SyGapRootPaths[i], sizeof(SyGapRootPaths[i+n]) );
711 i--;
712 }
713
714 n = 0;
715
716 }
717 else {
718 /* Make sure to wipe out all possibly existing root paths */
719 for( i = 0; i < MAX_GAP_DIRS; i++ ) SyGapRootPaths[i][0] = '\0';
720 n = 0;
721 }
722
723 /* unpack the argument */
724 p = string;
725 while ( *p ) {
726 if( n >= MAX_GAP_DIRS ) return;
727
728 q = SyGapRootPaths[n];
729 while ( *p && *p != ';' ) {
730 *q = *p++;
731
732 #ifdef SYS_IS_CYGWIN32
733 /* fix up for DOS */
734 if (*q == '\\')
735 *q = '/';
736 #endif
737
738 q++;
739 }
740 if ( q == SyGapRootPaths[n] ) {
741 strxcpy( SyGapRootPaths[n], "./", sizeof(SyGapRootPaths[n]) );
742 }
743 else if ( q[-1] != '/' ) {
744 *q++ = '/';
745 *q = '\0';
746 }
747 else {
748 *q = '\0';
749 }
750 if ( *p ) {
751 p++;
752 }
753 n++;
754 #ifdef HPCGAP
755 // or each root <ROOT> we also add <ROOT/hpcgap> as a root (and first)
756 if( n < MAX_GAP_DIRS ) {
757 strlcpy( SyGapRootPaths[n], SyGapRootPaths[n-1], sizeof(SyGapRootPaths[n]) );
758 }
759 strxcat( SyGapRootPaths[n-1], "hpcgap/", sizeof(SyGapRootPaths[n-1]) );
760 n++;
761 #endif
762 }
763 }
764
765 /****************************************************************************
766 **
767 *F SySetInitialGapRootPaths( <string> ) . . . . . set the root directories
768 **
769 ** Set up GAP's initial root paths, based on the location of the
770 ** GAP executable.
771 */
SySetInitialGapRootPaths(void)772 static void SySetInitialGapRootPaths(void)
773 {
774 if (GAPExecLocation[0] != 0) {
775 // GAPExecLocation might be a subdirectory of GAP root,
776 // so we will go and search for the true GAP root.
777 // We try stepping back up to two levels.
778 char pathbuf[GAP_PATH_MAX];
779 char initgbuf[GAP_PATH_MAX];
780 strxcpy(pathbuf, GAPExecLocation, sizeof(pathbuf));
781 for (Int i = 0; i < 3; ++i) {
782 strxcpy(initgbuf, pathbuf, sizeof(initgbuf));
783 strxcat(initgbuf, "lib/init.g", sizeof(initgbuf));
784
785 if (SyIsReadableFile(initgbuf) == 0) {
786 SySetGapRootPath(pathbuf);
787 // escape from loop
788 return;
789 }
790 // try up a directory level
791 strxcat(pathbuf, "../", sizeof(pathbuf));
792 }
793 }
794
795 // Set GAP root path to current directory, if we have no other
796 // idea, and for backwards compatibility.
797 // Note that GAPExecLocation must always end with a slash.
798 SySetGapRootPath("./");
799 }
800
801 /****************************************************************************
802 **
803 *F syLongjmp( <jump buffer>, <value>)
804 ** Perform a long jump
805 **
806 *F RegisterSyLongjmpObserver( <func> )
807 ** Register a function to be called before longjmp is called.
808 ** returns 1 on success, 0 if the table of functions is already full.
809 ** This function is idempotent -- if a function is passed multiple times
810 ** it is still only registered once.
811 */
812
813 enum { signalSyLongjmpFuncsLen = 16 };
814
815 static voidfunc signalSyLongjmpFuncs[signalSyLongjmpFuncsLen];
816
RegisterSyLongjmpObserver(voidfunc func)817 Int RegisterSyLongjmpObserver(voidfunc func)
818 {
819 Int i;
820 for (i = 0; i < signalSyLongjmpFuncsLen; ++i) {
821 if (signalSyLongjmpFuncs[i] == func) {
822 return 1;
823 }
824 if (signalSyLongjmpFuncs[i] == 0) {
825 signalSyLongjmpFuncs[i] = func;
826 return 1;
827 }
828 }
829 return 0;
830 }
831
syLongjmp(syJmp_buf * buf,int val)832 void syLongjmp(syJmp_buf* buf, int val)
833 {
834 Int i;
835 for (i = 0; i < signalSyLongjmpFuncsLen && signalSyLongjmpFuncs[i]; ++i)
836 (signalSyLongjmpFuncs[i])();
837 syLongjmpInternal(*buf, val);
838 }
839
840 /****************************************************************************
841 **
842 *F InitSystem( <argc>, <argv> ) . . . . . . . . . initialize system package
843 **
844 ** 'InitSystem' is called very early during the initialization from 'main'.
845 ** It is passed the command line array <argc>, <argv> to look for options.
846 **
847 ** For UNIX it initializes the default files 'stdin', 'stdout' and 'stderr',
848 ** installs the handler 'syAnswerIntr' to answer the user interrupts
849 ** '<ctr>-C', scans the command line for options, sets up the GAP root paths,
850 ** locates the '.gaprc' file (if any), and more.
851 */
852
853 typedef struct { Char symbol; UInt value; } sizeMultiplier;
854
855 static sizeMultiplier memoryUnits[]= {
856 {'k', 1024},
857 {'K', 1024},
858 {'m', 1024*1024},
859 {'M', 1024*1024},
860 {'g', 1024*1024*1024},
861 {'G', 1024*1024*1024},
862 #ifdef SYS_IS_64_BIT
863 {'t', 1024UL*1024*1024*1024},
864 {'T', 1024UL*1024*1024*1024},
865 {'p', 1024UL*1024*1024*1024*1024}, /* you never know */
866 {'P', 1024UL*1024*1024*1024*1024},
867 #endif
868 };
869
ParseMemory(Char * s)870 static UInt ParseMemory( Char * s)
871 {
872 double size = atof(s);
873 Char symbol = s[strlen(s)-1];
874 UInt i;
875 UInt maxmem;
876 #ifdef SYS_IS_64_BIT
877 maxmem = 15000000000000000000UL;
878 #else
879 maxmem = 4000000000UL;
880 #endif
881
882 for (i = 0; i < ARRAY_SIZE(memoryUnits); i++) {
883 if (symbol == memoryUnits[i].symbol) {
884 UInt value = memoryUnits[i].value;
885 if (size > maxmem/value)
886 return maxmem;
887 else
888 return size * value;
889 }
890 }
891 if (!IsDigit(symbol))
892 fputs("Unrecognised memory unit ignored", stderr);
893 return size;
894 }
895
896
897 struct optInfo {
898 Char shortkey;
899 Char longkey[50];
900 Int (*handler)(Char **, void *);
901 void *otherArg;
902 UInt minargs;
903 };
904
905
toggle(Char ** argv,void * Variable)906 static Int toggle( Char ** argv, void *Variable )
907 {
908 UInt * variable = (UInt *) Variable;
909 *variable = !*variable;
910 return 0;
911 }
912
913 #ifdef HPCGAP
storePosInteger(Char ** argv,void * Where)914 static Int storePosInteger( Char **argv, void *Where )
915 {
916 UInt *where = (UInt *)Where;
917 UInt n;
918 Char *p = argv[0];
919 n = 0;
920 while (IsDigit(*p)) {
921 n = n * 10 + (*p-'0');
922 p++;
923 }
924 if (p == argv[0] || *p || n == 0)
925 fputs("Argument not a positive integer", stderr);
926 *where = n;
927 return 1;
928 }
929 #endif
930
storeString(Char ** argv,void * Where)931 static Int storeString( Char **argv, void *Where )
932 {
933 Char **where = (Char **)Where;
934 *where = argv[0];
935 return 1;
936 }
937
938 #ifdef USE_GASMAN
storeMemory(Char ** argv,void * Where)939 static Int storeMemory( Char **argv, void *Where )
940 {
941 UInt *where = (UInt *)Where;
942 *where = ParseMemory(argv[0]);
943 return 1;
944 }
945 #endif
946
storeMemory2(Char ** argv,void * Where)947 static Int storeMemory2( Char **argv, void *Where )
948 {
949 UInt *where = (UInt *)Where;
950 *where = ParseMemory(argv[0])/1024;
951 return 1;
952 }
953
processCompilerArgs(Char ** argv,void * dummy)954 static Int processCompilerArgs( Char **argv, void * dummy)
955 {
956 SyCompilePlease = 1;
957 strxcat( SyCompileOutput, argv[0], sizeof(SyCompileOutput) );
958 strxcat( SyCompileInput, argv[1], sizeof(SyCompileInput) );
959 strxcat( SyCompileName, argv[2], sizeof(SyCompileName) );
960 SyCompileMagic1 = argv[3];
961 return 4;
962 }
963
unsetString(Char ** argv,void * Where)964 static Int unsetString( Char **argv, void *Where)
965 {
966 *(Char **)Where = (Char *)0;
967 return 0;
968 }
969
forceLineEditing(Char ** argv,void * Level)970 static Int forceLineEditing( Char **argv,void *Level)
971 {
972 UInt level = (UInt)Level;
973 SyLineEdit = level;
974 return 0;
975 }
976
setGapRootPath(Char ** argv,void * Dummy)977 static Int setGapRootPath( Char **argv, void *Dummy)
978 {
979 SySetGapRootPath( argv[0] );
980 return 1;
981 }
982
983
984 #ifndef GAP_MEM_CHECK
985 // Provide stub with helpful error message
986
enableMemCheck(Char ** argv,void * dummy)987 static Int enableMemCheck(Char ** argv, void * dummy)
988 {
989 SyFputs( "# Error: --enableMemCheck not supported by this copy of GAP\n", 3);
990 SyFputs( " pass --enable-memory-checking to ./configure\n", 3 );
991 SyExit(2);
992 }
993 #endif
994
995
996 /* These are just the options that need kernel processing. Additional options will be
997 recognised and handled in the library */
998
999 /* These options must be kept in sync with those in system.g, so the help output
1000 is correct */
1001 static const struct optInfo options[] = {
1002 { 'B', "architecture", storeString, &SyArchitecture, 1}, /* default architecture needs to be passed from kernel
1003 to library. Might be needed for autoload of compiled files */
1004 { 'C', "", processCompilerArgs, 0, 4}, /* must handle in kernel */
1005 { 'D', "debug-loading", toggle, &SyDebugLoading, 0}, /* must handle in kernel */
1006 { 'K', "maximal-workspace", storeMemory2, &SyStorKill, 1}, /* could handle from library with new interface */
1007 { 'L', "", storeString, &SyRestoring, 1}, /* must be handled in kernel */
1008 { 'M', "", toggle, &SyUseModule, 0}, /* must be handled in kernel */
1009 { 'R', "", unsetString, &SyRestoring, 0}, /* kernel */
1010 { 'e', "", toggle, &SyCTRD, 0 }, /* kernel */
1011 { 'f', "", forceLineEditing, (void *)2, 0 }, /* probably library now */
1012 { 'E', "", toggle, &SyUseReadline, 0 }, /* kernel */
1013 { 'l', "roots", setGapRootPath, 0, 1}, /* kernel */
1014 { 'm', "", storeMemory2, &SyStorMin, 1 }, /* kernel */
1015 { 'r', "", toggle, &IgnoreGapRC, 0 }, /* kernel */
1016 #ifdef USE_GASMAN
1017 { 's', "", storeMemory, &SyAllocPool, 1 }, /* kernel */
1018 #endif
1019 { 'n', "", forceLineEditing, 0, 0}, /* prob library */
1020 #ifdef USE_GASMAN
1021 { 'o', "", storeMemory2, &SyStorMax, 1 }, /* library with new interface */
1022 #endif
1023 { 'p', "", toggle, &SyWindow, 0 }, /* ?? */
1024 { 'q', "", toggle, &SyQuiet, 0 }, /* ?? */
1025 #ifdef HPCGAP
1026 { 'S', "", toggle, &ThreadUI, 0 }, /* Thread UI */
1027 { 'Z', "", toggle, &DeadlockCheck, 0 }, /* Deadlock prevention */
1028 { 'P', "", storePosInteger, &SyNumProcessors, 1 }, /* number of CPUs */
1029 { 'G', "", storePosInteger, &SyNumGCThreads, 1 }, /* number of GC threads */
1030 { 0 , "single-thread", toggle, &SingleThreadStartup, 0 }, /* startup with one thread only */
1031 #endif
1032 /* The following options must be handled in the kernel so they are set up before loading the library */
1033 { 0 , "prof", enableProfilingAtStartup, 0, 1}, /* enable profiling at startup */
1034 { 0 , "memprof", enableMemoryProfilingAtStartup, 0, 1 }, /* enable memory profiling at startup */
1035 { 0 , "cover", enableCodeCoverageAtStartup, 0, 1}, /* enable code coverage at startup */
1036 { 0 , "quitonbreak", toggle, &SyQuitOnBreak, 0}, /* Quit GAP if we enter the break loop */
1037 { 0 , "enableMemCheck", enableMemCheck, 0, 0 },
1038 { 0, "", 0, 0, 0}};
1039
1040
1041 Char ** SyOriginalArgv;
1042 UInt SyOriginalArgc;
1043
1044
1045
InitSystem(Int argc,Char * argv[],UInt handleSignals)1046 void InitSystem (
1047 Int argc,
1048 Char * argv [],
1049 UInt handleSignals )
1050 {
1051 UInt i; /* loop variable */
1052 Int res; /* return from option processing function */
1053
1054 /* Initialize global and static variables */
1055 SyCTRD = 1;
1056 SyCompilePlease = 0;
1057 SyDebugLoading = 0;
1058 SyLineEdit = 1;
1059 #ifdef HPCGAP
1060 SyUseReadline = 0;
1061 #else
1062 SyUseReadline = 1;
1063 #endif
1064 SyMsgsFlagBags = 0;
1065 SyNrCols = 0;
1066 SyNrColsLocked = 0;
1067 SyNrRows = 0;
1068 SyNrRowsLocked = 0;
1069 SyQuiet = 0;
1070 SyInitializing = 0;
1071
1072 SyStorMin = 16 * sizeof(Obj) * 1024; // in kB
1073 SyStorMax = 256 * sizeof(Obj) * 1024; // in kB
1074 #ifdef SYS_IS_64_BIT
1075 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
1076 // Set to 3/4 of memory size (in kB), if this is larger
1077 Int SyStorMaxFromMem =
1078 (sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES) * 3L) / 4 / 1024;
1079 SyStorMax = SyStorMaxFromMem > SyStorMax ? SyStorMaxFromMem : SyStorMax;
1080 #endif
1081 #endif // defined(SYS_IS_64_BIT)
1082
1083 #ifdef USE_GASMAN
1084 #ifdef SYS_IS_64_BIT
1085 SyAllocPool = 4096L*1024*1024; /* Note this is in bytes! */
1086 #else
1087 SyAllocPool = 1536L*1024*1024; /* Note this is in bytes! */
1088 #endif // defined(SYS_IS_64_BIT)
1089 SyStorOverrun = 0;
1090 SyStorKill = 0;
1091 #endif // defined(USE_GASMAN)
1092 SyUseModule = 1;
1093 SyWindow = 0;
1094
1095 for (i = 0; i < 2; i++) {
1096 UInt j;
1097 for (j = 0; j < 7; j++) {
1098 SyGasmanNumbers[i][j] = 0;
1099 }
1100 }
1101
1102 InitSysFiles();
1103
1104 #ifdef HAVE_LIBREADLINE
1105 rl_readline_name = "GAP";
1106 rl_initialize ();
1107 #endif
1108
1109 if (handleSignals) {
1110 SyInstallAnswerIntr();
1111 }
1112
1113 #if defined(SYS_DEFAULT_PATHS)
1114 SySetGapRootPath( SYS_DEFAULT_PATHS );
1115 #else
1116 SySetInitialGapRootPaths();
1117 #endif
1118
1119 /* save the original command line for export to GAP */
1120 SyOriginalArgc = argc;
1121 SyOriginalArgv = argv;
1122
1123 /* scan the command line for options that we have to process in the kernel */
1124 /* we just scan the whole command line looking for the keys for the options we recognise */
1125 /* anything else will presumably be dealt with in the library */
1126 while ( argc > 1 )
1127 {
1128 if (argv[1][0] == '-' ) {
1129
1130 if ( strlen(argv[1]) != 2 && argv[1][1] != '-') {
1131 fputs("gap: sorry, options must not be grouped '", stderr);
1132 fputs(argv[1], stderr);
1133 fputs("'.\n", stderr);
1134 goto usage;
1135 }
1136
1137
1138 for (i = 0; options[i].shortkey != argv[1][1] &&
1139 (argv[1][1] != '-' || argv[1][2] == 0 || strcmp(options[i].longkey, argv[1] + 2)) &&
1140 (options[i].shortkey != 0 || options[i].longkey[0] != 0); i++)
1141 ;
1142
1143
1144
1145
1146 if (argc < 2 + options[i].minargs)
1147 {
1148 Char buf[2];
1149 fputs("gap: option ", stderr);
1150 fputs(argv[1], stderr);
1151 fputs(" requires at least ", stderr);
1152 buf[0] = options[i].minargs + '0';
1153 buf[1] = '\0';
1154 fputs(buf, stderr);
1155 fputs(" arguments\n", stderr);
1156 goto usage;
1157 }
1158 if (options[i].handler) {
1159 res = (*options[i].handler)(argv+2, options[i].otherArg);
1160
1161 switch (res)
1162 {
1163 case -1: goto usage;
1164 /* case -2: goto fullusage; */
1165 default: ; /* fall through and continue */
1166 }
1167 }
1168 else
1169 res = options[i].minargs;
1170 /* recordOption(argv[1][1], res, argv+2); */
1171 argv += 1 + res;
1172 argc -= 1 + res;
1173
1174 }
1175 else {
1176 argv++;
1177 argc--;
1178 }
1179
1180 }
1181 /* adjust SyUseReadline if no readline support available or for XGAP */
1182 #if !defined(HAVE_LIBREADLINE)
1183 SyUseReadline = 0;
1184 #endif
1185 if (SyWindow) SyUseReadline = 0;
1186
1187 /* now that the user has had a chance to give -x and -y,
1188 we determine the size of the screen ourselves */
1189 getwindowsize();
1190
1191 /* fix max if it is lower than min */
1192 if ( SyStorMax != 0 && SyStorMax < SyStorMin ) {
1193 SyStorMax = SyStorMin;
1194 }
1195
1196 #ifdef USE_GASMAN
1197 /* fix pool size if larger than SyStorKill */
1198 if ( SyStorKill != 0 && SyAllocPool != 0 &&
1199 SyAllocPool > 1024 * SyStorKill ) {
1200 SyAllocPool = SyStorKill * 1024;
1201 }
1202 #endif
1203
1204 /* when running in package mode set ctrl-d and line editing */
1205 if ( SyWindow ) {
1206 /* SyLineEdit = 1;
1207 SyCTRD = 1; */
1208 SyRedirectStderrToStdOut();
1209 syWinPut( 0, "@p", "1." );
1210 }
1211
1212 /* should GAP load 'init/lib.g' on initialization */
1213 if ( SyCompilePlease || SyRestoring ) {
1214 SyLoadSystemInitFile = 0;
1215 }
1216
1217 /* the compiler will *not* read in the .gaprc file
1218 if ( gaprc && ! ( SyCompilePlease || SyRestoring ) ) {
1219 sySetGapRCFile();
1220 }
1221 */
1222
1223 /* the users home directory */
1224 if ( getenv("HOME") != 0 ) {
1225 strxcpy(DotGapPath, getenv("HOME"), sizeof(DotGapPath));
1226 # if defined(SYS_IS_DARWIN) && SYS_IS_DARWIN
1227 /* On Darwin, add .gap to the sys roots, but leave */
1228 /* DotGapPath at $HOME/Library/Preferences/GAP */
1229 strxcat(DotGapPath, "/.gap;", sizeof(DotGapPath));
1230 if (!IgnoreGapRC) {
1231 SySetGapRootPath(DotGapPath);
1232 }
1233
1234 strxcpy(DotGapPath, getenv("HOME"), sizeof(DotGapPath));
1235 strxcat(DotGapPath, "/Library/Preferences/GAP;", sizeof(DotGapPath));
1236 # elif defined(__CYGWIN__)
1237 strxcat(DotGapPath, "/_gap;", sizeof(DotGapPath));
1238 # else
1239 strxcat(DotGapPath, "/.gap;", sizeof(DotGapPath));
1240 # endif
1241
1242 if (!IgnoreGapRC) {
1243 SySetGapRootPath(DotGapPath);
1244 }
1245 DotGapPath[strlen(DotGapPath)-1] = '\0';
1246
1247 /* and in this case we can also expand paths which start
1248 with a tilde ~ */
1249 Char userhome[GAP_PATH_MAX];
1250 strxcpy(userhome, getenv("HOME"), sizeof(userhome));
1251 const UInt userhomelen = strlen(userhome);
1252 for (i = 0; i < MAX_GAP_DIRS && SyGapRootPaths[i][0]; i++) {
1253 const UInt pathlen = strlen(SyGapRootPaths[i]);
1254 if (SyGapRootPaths[i][0] == '~' &&
1255 userhomelen + pathlen < sizeof(SyGapRootPaths[i])) {
1256 SyMemmove(SyGapRootPaths[i] + userhomelen,
1257 /* don't copy the ~ but the trailing '\0' */
1258 SyGapRootPaths[i] + 1, pathlen);
1259 memcpy(SyGapRootPaths[i], userhome, userhomelen);
1260 }
1261 }
1262 }
1263
1264
1265 /* now we start */
1266 return;
1267
1268 /* print a usage message */
1269 usage:
1270 fputs("usage: gap [OPTIONS] [FILES]\n", stderr);
1271 fputs(" run the Groups, Algorithms and Programming system, Version ", stderr);
1272 fputs(SyBuildVersion, stderr);
1273 fputs("\n", stderr);
1274 fputs(" use '-h' option to get help.\n", stderr);
1275 fputs("\n", stderr);
1276 SyExit( 1 );
1277 }
1278