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