[project @ 2000-04-10 15:39:09 by sewardj]
[ghc-hetmet.git] / ghc / interpreter / machdep.c
1
2 /* --------------------------------------------------------------------------
3  * Machine dependent code
4  * RISCOS specific code provided by Bryan Scatergood, JBS
5  * Macintosh specific code provided by Hans Aberg (haberg@matematik.su.se)
6  * HaskellScript code and recursive directory search provided by
7  *  Daan Leijen (leijen@fwi.uva.nl)
8  *
9  * The Hugs 98 system is Copyright (c) Mark P Jones, Alastair Reid, the
10  * Yale Haskell Group, and the Oregon Graduate Institute of Science and
11  * Technology, 1994-1999, All rights reserved.  It is distributed as
12  * free software under the license in the file "License", which is
13  * included in the distribution.
14  *
15  * $RCSfile: machdep.c,v $
16  * $Revision: 1.29 $
17  * $Date: 2000/04/10 15:39:09 $
18  * ------------------------------------------------------------------------*/
19
20 #ifdef HAVE_SIGNAL_H
21 # include <signal.h>
22 #endif
23 #ifdef HAVE_SYS_TYPES_H
24 # include <sys/types.h>
25 #else
26 # ifdef HAVE_TYPES_H
27 #  include <types.h>
28 # endif
29 #endif
30 #if HAVE_SYS_PARAM_H
31 # include <sys/param.h>
32 #endif
33 #ifdef HAVE_SYS_STAT_H
34 # include <sys/stat.h>
35 #else
36 # ifdef HAVE_STAT_H
37 #  include <stat.h>
38 # endif
39 #endif
40 #ifdef HAVE_TIME_H
41 # include <time.h>
42 #endif
43
44 /* Windows/DOS include files */
45 #ifdef HAVE_DOS_H
46 # include <dos.h>
47 #endif
48 #if defined HAVE_CONIO_H
49 # include <conio.h>
50 #endif
51 #ifdef HAVE_IO_H
52 # include <io.h>
53 #endif
54 #ifdef HAVE_STD_H
55 # include <std.h>
56 #endif
57 #ifdef HAVE_WINDOWS_H
58 # include <windows.h>
59 #endif
60
61 #if DOS
62 #include <mem.h>
63 extern unsigned _stklen = 8000;         /* Allocate an 8k stack segment    */
64 #endif
65
66 #if RISCOS
67 #include "swis.h"
68 #include "os.h"
69 #endif
70
71 /* Macintosh include files */
72 #ifdef HAVE_CONSOLE_H
73 # include <console.h>
74 #endif
75 #ifdef HAVE_PASCAL_H
76 # include <pascal.h>
77 #endif
78 #ifdef HAVE_FILES_H
79 # include <Files.h>
80 #endif
81 #ifdef HAVE_FCNTL_H
82 # include <fcntl.h>
83 #endif
84 #ifdef HAVE_ERRNO_H
85 # include <errno.h>
86 #endif
87 #ifdef HAVE_STDLIB_H
88 # include <stdlib.h>
89 #endif
90 #ifdef HAVE_UNIX_H
91 #include <unix.h>
92 #endif
93 #if SYMANTEC_C
94 int allow_break_count = 0;
95 #endif
96
97 /* --------------------------------------------------------------------------
98  * Find information about a file:
99  * ------------------------------------------------------------------------*/
100
101 #include "machdep_time.h"
102
103 static Bool local readable      ( String );
104 static Void local getFileInfo   ( String, Time *, Long * );
105
106 static Void local getFileInfo(f,tm,sz)  /* find time stamp and size of file*/
107 String f;
108 Time   *tm;
109 Long   *sz; {
110 #if defined HAVE_SYS_STAT_H || defined HAVE_STAT_H || defined HAVE_UNIX_H
111     struct stat scbuf;
112     if (!stat(f,&scbuf)) {
113         if (tm) *tm = scbuf.st_mtime;
114         *sz = (Long)(scbuf.st_size);
115     } else {
116         if (tm) *tm = 0;
117         *sz = 0;
118     }
119 #else                                   /* normally just use stat()        */
120     os_regset r;                        /* RISCOS PRM p.850 and p.837      */
121     r.r[0] = 17;                        /* Read catalogue, no path         */
122     r.r[1] = (int)s;
123     os_swi(OS_File, &r);
124     if(r.r[0] == 1 && (r.r[2] & 0xFFF00000) == 0xFFF00000) {
125         if (tm) tm->hi = r.r[2] & 0xFF; /* Load address (high byte)        */
126         if (tm) tm->lo = r.r[3];        /* Execution address (low 4 bytes) */
127     } else {                            /* Not found, or not time-stamped  */
128         if (tm) tm->hi = tm->lo = 0;
129     }
130     *sz = (Long)(r.r[0] == 1 ? r.r[4] : 0);
131 #endif
132 }
133
134 Void getFileSize ( String f, Long* sz )
135 {
136    getFileInfo ( f, NULL, sz );
137 }
138
139 #if defined HAVE_GETFINFO               /* Mac971031 */
140 /* --------------------------------------------------------------------------
141  * Define a MacOS version of access():
142  *   If the file is not accessible, -1 is returned and errno is set to
143  * the reason for the failure.
144  *   If the file is accessible and the dummy is 0 (existence), 2 (write), 
145  * or 4 (read), the return is 0.
146  *   If the file is accessible, and the dummy is 1 (executable), then if
147  * the file is a program (of type 'APPL'), the return is 0, otherwise -1.
148  *   Warnings: Use with caution. UNIX access do no translate to Macs.
149  * Check of write access is not implemented (same as read).
150  * ------------------------------------------------------------------------*/
151
152 int access(char *fileName, int dummy) { 
153         FInfo   fi;
154         short   rc;
155         
156         errno = getfinfo(fileName, 0, &fi);
157         if (errno != 0)  return -1;             /* Check file accessible. */
158         
159         /* Cases dummy = existence, read, write. */
160         if (dummy == 0 || dummy & 0x6)  return 0;
161         
162         /* Case dummy = executable. */
163         if (dummy == 1) { 
164                 if (fi.fdType == 'APPL')  return 0;
165                 errno = fi.fdType;
166                 return -1;
167         }
168         
169         return 0;
170 }
171 #endif
172
173 static Bool local readable(f)           /* is f a regular, readable file   */
174 String f; {
175 #if DJGPP2 || defined HAVE_GETFINFO /* stat returns bogus mode bits on djgpp2 */
176     return (0 == access(f,4));
177 #elif defined HAVE_SYS_STAT_H || defined HAVE_STAT_H
178     struct stat scbuf;
179     /* fprintf(stderr, "readable: %s\n", f ); */
180     return (  !stat(f,&scbuf) 
181            && (scbuf.st_mode & S_IREAD) /* readable     */
182            && (scbuf.st_mode & S_IFREG) /* regular file */
183            );
184 #elif defined HAVE_OS_SWI /* RISCOS specific */
185     os_regset r;                        /* RISCOS PRM p.850     -- JBS     */
186     assert(dummy == 0);
187     r.r[0] = 17; /* Read catalogue, no path */
188     r.r[1] = (int)f;
189     os_swi(OS_File, &r);
190     return r.r[0] != 1; /* Does this check it's a regular file? ADR */
191 #endif
192 }
193
194
195 /* --------------------------------------------------------------------------
196  * Search for script files on the HUGS path:
197  * ------------------------------------------------------------------------*/
198
199 static String local hugsdir       ( Void );
200 #if HSCRIPT
201 static String local hscriptDir    ( Void );
202 #endif
203 static int    local pathCmp       ( String, String );
204 static String local normPath      ( String );
205 static Void   local searchChr     ( Int );
206 static Void   local searchStr     ( String );
207 static Bool   local tryEndings    ( String );
208
209 #if (DOS_FILENAMES || __CYGWIN32__) 
210 # define SLASH                   '\\'
211 # define SLASH_STR               "\\"
212 # define isSLASH(c)              ((c)=='\\' || (c)=='/')
213 # define PATHSEP                 ';'
214 # define PATHSEP_STR             ";"
215 # define DLL_ENDING              ".dll"
216 #elif MAC_FILENAMES
217 # define SLASH                   ':'
218 # define isSLASH(c)              ((c)==SLASH)
219 # define PATHSEP                 ';'
220 # define PATHSEP_STR             ";"
221 /* Mac PEF (Preferred Executable Format) file */
222 # define DLL_ENDING              ".pef" 
223 #else
224 # define SLASH                   '/'
225 # define SLASH_STR               "/"
226 # define isSLASH(c)              ((c)==SLASH)
227 # define PATHSEP                 ':'
228 # define PATHSEP_STR             ":"
229 # define DLL_ENDING              ".u_o"
230 #endif
231
232 static String local hugsdir() {     /* directory containing lib/Prelude.hs */
233 #if HSCRIPT
234     /* In HaskellScript (Win32 only), we lookup InstallDir in the registry. */
235     static char dir[FILENAME_MAX+1] = "";
236     if (dir[0] == '\0') { /* not initialised yet */
237         String s = readRegString(HKEY_LOCAL_MACHINE,HugsRoot,"InstallDir", 
238                                  HUGSDIR);
239         if (s) { 
240             strcpy(dir,s); 
241         }
242     }
243     return dir;
244 #elif HAVE_GETMODULEFILENAME && !DOS && !__CYGWIN32__
245     /* On Windows, we can find the binary we're running and it's
246      * conventional to put the libraries in the same place.
247      */
248     static char dir[FILENAME_MAX+1] = "";
249     if (dir[0] == '\0') { /* not initialised yet */
250         String slash = 0;
251         GetModuleFileName((HMODULE)0,dir,FILENAME_MAX+1);
252         if (dir[0] == '\0') { /* GetModuleFileName must have failed */
253             return HUGSDIR;
254         }
255         slash = strrchr(dir,SLASH);
256         if (slash) { /* truncate after directory name */
257             *slash = '\0';
258         }
259     }
260     return dir;
261 #else
262     /* On Unix systems, we can't find the binary we're running and
263      * the libraries may not be installed near the binary anyway.
264      * This forces us to use a hardwired path which is set at 
265      * configuration time (--datadir=...).
266      */
267     return HUGSDIR;
268 #endif
269 }
270
271 #if HSCRIPT    
272 static String local hscriptDir() {  /* Directory containing hscript.dll    */
273     static char dir[FILENAME_MAX+1] = "";
274     if (dir[0] == '\0') { /* not initialised yet */
275         String s = readRegString(HKEY_LOCAL_MACHINE,HScriptRoot,"InstallDir","");
276         if (s) {
277             strcpy(dir,s);
278         }
279     }
280     return dir;
281 }
282 #endif
283
284 #if 0  /* apparently unused */
285 static String local RealPath(s)         /* Find absolute pathname of file  */
286 String s; {
287 #if HAVE__FULLPATH  /* eg DOS */
288     static char path[FILENAME_MAX+1];
289     _fullpath(path,s,FILENAME_MAX+1);
290 #elif HAVE_REALPATH /* eg Unix */
291     static char path[MAXPATHLEN+1];
292     realpath(s,path);                
293 #else
294     static char path[FILENAME_MAX+1];
295     strcpy(path,s);
296 #endif
297     return path;
298 }
299 #endif
300
301
302 static int local pathCmp(p1,p2)       /* Compare paths after normalisation */
303 String p1;
304 String p2; {
305 #if HAVE__FULLPATH  /* eg DOS */
306     static char path1[FILENAME_MAX+1];
307     static char path2[FILENAME_MAX+1];
308     _fullpath(path1,p1,FILENAME_MAX+1);
309     _fullpath(path2,p2,FILENAME_MAX+1);
310 #elif HAVE_REALPATH /* eg Unix */
311     static char path1[MAXPATHLEN+1];
312     static char path2[MAXPATHLEN+1];
313     realpath(p1,path1);                
314     realpath(p2,path2);                
315 #else
316     static char path1[FILENAME_MAX+1];
317     static char path2[FILENAME_MAX+1];
318     strcpy(path1,p1);
319     strcpy(path2,p2);
320 #endif
321 #if CASE_INSENSITIVE_FILENAMES
322     strlwr(path1);
323     strlwr(path2);
324 #endif
325     return filenamecmp(path1,path2);
326 }
327
328 static String local normPath(s) /* Try, as much as possible, to normalize  */
329 String s; {                     /* a pathname in some appropriate manner.  */
330 #if PATH_CANONICALIZATION
331     String path = RealPath(s);
332 #if CASE_INSENSITIVE_FILENAMES
333     strlwr(path);                       /* and convert to lowercase        */
334 #endif
335     return path;
336 #else /* ! PATH_CANONICALIZATION */
337     return s;
338 #endif /* ! PATH_CANONICALIZATION */
339 }
340
341 #if HSCRIPT
342 static String endings[] = { "", ".u_hi", ".hs", ".lhs", ".hsx", ".hash", 0 };
343 #else
344 static String endings[] = { "", ".u_hi", ".hs", ".lhs", 0 };
345 #endif
346 static char   searchBuf[FILENAME_MAX+1];
347 static Int    searchPos;
348
349 #define searchReset(n)          searchBuf[searchPos=(n)]='\0'
350
351 static Void local searchChr(c)  /* Add single character to search buffer   */
352 Int c; {
353     if (searchPos<FILENAME_MAX) {
354         searchBuf[searchPos++] = (char)c;
355         searchBuf[searchPos]   = '\0';
356     }
357 }
358
359 static Void local searchStr(s)  /* Add string to search buffer             */
360 String s; {
361     while (*s && searchPos<FILENAME_MAX)
362         searchBuf[searchPos++] = *s++;
363     searchBuf[searchPos] = '\0';
364 }
365
366 static Bool local tryEndings(s) /* Try each of the listed endings          */
367 String s; {
368     Int i = 0;
369     searchStr(s);
370     for (; endings[i]; ++i) {
371         Int save = searchPos;
372         searchStr(endings[i]);
373         if (readable(searchBuf))
374             return TRUE;
375         searchReset(save);
376     }
377     return FALSE;
378 }
379
380
381
382 #if SEARCH_DIR
383
384 /* scandir, June 98 Daan Leijen
385    searches the base directory and its direct subdirectories for a file
386
387    input: searchbuf contains SLASH terminated base directory
388           argument s contains the (base) filename
389    output: TRUE: searchBuf contains the full filename
390            FALSE: searchBuf is garbage, file not found
391 */
392           
393
394 #ifdef HAVE_WINDOWS_H
395
396 static Bool scanSubDirs(s)
397 String s;
398 {
399     struct _finddata_t findInfo;
400     long handle;
401     int  save;
402     
403     save = searchPos;
404     /* is it in the current directory ? */
405     if (tryEndings(s)) return TRUE;
406
407     searchReset(save);
408     searchStr("*");
409     
410     /* initiate the search */
411     handle = _findfirst( searchBuf, &findInfo );
412     if (handle==-1) { errno = 0; return FALSE; }
413     
414     /* search all subdirectories */
415     do {
416         /* if we have a valid sub directory */
417         if (((findInfo.attrib & _A_SUBDIR) == _A_SUBDIR) &&
418             (findInfo.name[0] != '.')) {
419             searchReset(save);
420             searchStr(findInfo.name);
421             searchChr(SLASH);
422             if (tryEndings(s)) {
423                 return TRUE;
424             }
425         }
426     } while (_findnext( handle, &findInfo ) == 0);
427     
428     _findclose( handle );
429     return FALSE;
430 }
431
432 #elif defined(HAVE_FTW_H)
433
434 #include <ftw.h>
435
436 static char baseFile[FILENAME_MAX+1];
437 static char basePath[FILENAME_MAX+1];
438 static int  basePathLen;
439
440 static int scanitem( const char* path, 
441                      const struct stat* statinfo, 
442                      int info )
443 {
444     if (info == FTW_D) { /* is it a directory */
445         searchReset(0);
446         searchStr(path);
447         searchChr(SLASH);
448         if (tryEndings(baseFile)) {
449             return 1;
450         }
451     }
452     return 0;
453 }
454
455 static Bool scanSubDirs(s)
456 String s;
457 {
458     int r;
459     strcpy(baseFile,s);
460     strcpy(basePath,searchBuf);
461     basePathLen = strlen(basePath);
462
463     /* is it in the current directory ? */
464     if (tryEndings(s)) return TRUE;
465     
466     /* otherwise scan the subdirectories */
467     r = ftw( basePath, scanitem, 2 );
468     errno = 0;
469     return (r > 0);
470 }
471
472 #endif /* HAVE_WINDOWS_H || HAVE_FTW_H */
473 #endif /* SEARCH_DIR */
474
475 String findPathname(along,nm)   /* Look for a file along specified path    */
476 String along;                   /* Return NULL if file does not exist      */ 
477 String nm; {
478     /* AC, 1/21/99: modified to search hugsPath first, then projectPath */
479     String s = findMPathname(along,nm,hugsPath);
480     return s ? s : normPath(searchBuf);
481 }
482
483 /* AC, 1/21/99: modified to pass in path to search explicitly */
484 String findMPathname(along,nm,path)/* Look for a file along specified path   */
485 String along;                   /* If nonzero, a path prefix from along is */
486 String nm;                      /* used as the first prefix in the search. */
487 String path; {
488     String pathpt = path;
489
490     searchReset(0);
491     if (along) {                /* Was a path for an existing file given?  */
492         Int last = (-1);
493         Int i    = 0;
494         for (; along[i]; i++) {
495             searchChr(along[i]);
496             if (isSLASH(along[i]))
497                 last = i;
498         }
499         searchReset(last+1);
500     }
501     if (tryEndings(nm))
502         return normPath(searchBuf);
503
504     if (pathpt && *pathpt) {    /* Otherwise, we look along the HUGSPATH   */
505         Bool more = TRUE;
506         do {
507             Bool recurse = FALSE;   /* DL: shall we recurse ? */
508             searchReset(0);
509             if (*pathpt) {
510                 if (*pathpt!=PATHSEP) {
511                     /* Pre-define one MPW-style "shell-variable" */
512                     if (strncmp(pathpt,"{Hugs}",6)==0) {
513                         searchStr(hugsdir());
514                         pathpt += 6;
515                     }
516 #if HSCRIPT
517                     /* And another - we ought to generalise this stuff */
518                     else if (strncmp(pathpt,"{HScript}",9)==0) {
519                         searchStr(hscriptDir());
520                         pathpt += 9;
521                     }
522 #endif
523                     do {
524                         searchChr(*pathpt++);
525                     } while (*pathpt && *pathpt!=PATHSEP);
526                     recurse = (pathpt[-1] == SLASH);
527                     if (!recurse) {
528                         searchChr(SLASH);
529                     }
530                 }
531                 if (*pathpt==PATHSEP)
532                     pathpt++;
533                 else
534                     more = FALSE;
535             } else {
536                 more = FALSE;
537             }
538 #if SEARCH_DIR
539             if (recurse ? scanSubDirs(nm) : tryEndings(nm)) {
540                 return normPath(searchBuf);
541             }
542 #else   
543             if (tryEndings(nm)) {
544                 return normPath(searchBuf);
545             }
546 #endif
547         } while (more);
548     }
549
550     searchReset(0);  /* As a last resort, look for file in the current dir */
551     return (tryEndings(nm) ? normPath(searchBuf) : 0);
552 }
553
554 /* --------------------------------------------------------------------------
555  * New path handling stuff for the Combined System (tm)
556  * ------------------------------------------------------------------------*/
557
558 char installDir[N_INSTALLDIR];
559
560 /* Sets installDir to $STGHUGSDIR, and ensures there is a trailing
561    slash at the end.
562 */
563 void setInstallDir ( String argv_0 )
564 {
565    int   i;
566    char* r = getenv("STGHUGSDIR");
567    if (!r) {
568       fprintf(stderr, 
569           "%s: installation error: environment variable STGHUGSDIR is not set.\n",
570           argv_0 );
571       fprintf(stderr, 
572           "%s: pls set it to be the directory where STGHugs98 is installed.\n\n",
573           argv_0 );
574       exit(2);
575
576    }
577
578    if (strlen(r) > N_INSTALLDIR-30 ) {
579       fprintf(stderr, 
580           "%s: environment variable STGHUGSDIR is suspiciously long; pls remedy\n\n",
581           argv_0 );
582       exit(2);
583    }
584
585    strcpy ( installDir, r );
586    i = strlen(installDir);
587    if (installDir[i-1] != SLASH) installDir[i++] = SLASH;
588    installDir[i] = 0;
589 }
590
591
592 Bool findFilesForModule ( 
593         String  modName,
594         String* path,
595         String* sExt,
596         Bool* sAvail,  Time* sTime,  Long* sSize,
597         Bool* oiAvail, Time* oiTime, Long* oSize, Long* iSize
598      )
599 {
600    /* Let the module name given be M.
601       For each path entry P,
602         a  s(rc)       file will be P/M.hs or P/M.lhs
603         an i(nterface) file will be P/M.hi
604         an o(bject)    file will be P/M.o
605       If there is a s file or (both i and o files)
606         use P to fill in the path names.
607       Otherwise, move on to the next path entry.
608       If all path entries are exhausted, return False.
609
610       If in standalone, only look for (and succeed for) source modules.
611       Caller free()s path.  sExt is statically allocated.
612       srcExt is only set if a valid source file is found.
613    */
614    Int    nPath;
615    Bool   literate;
616    String peStart, peEnd;
617    String augdPath;       /* .:hugsPath:installDir/../lib/std:installDir/lib */
618    Time   oTime,  iTime;
619    Bool   oAvail, iAvail;
620
621    *path = *sExt = NULL;
622    *sAvail = *oiAvail = oAvail = iAvail = FALSE;
623    *sSize  = *oSize  = *iSize  = 0;
624
625    augdPath = malloc( 2*(10+3+strlen(installDir)) 
626                       +strlen(hugsPath) +50/*paranoia*/);
627    if (!augdPath)
628       internal("moduleNameToFileNames: malloc failed(2)");
629
630    augdPath[0] = 0;
631    strcat(augdPath, ".");
632    strcat(augdPath, PATHSEP_STR);
633
634    strcat(augdPath, hugsPath);
635    strcat(augdPath, PATHSEP_STR);
636
637    if (combined) {
638       strcat(augdPath, installDir);
639       strcat(augdPath, "..");
640       strcat(augdPath, SLASH_STR);
641       strcat(augdPath, "lib");
642       strcat(augdPath, SLASH_STR);
643       strcat(augdPath, "std");
644       strcat(augdPath, PATHSEP_STR);
645    }
646
647    strcat(augdPath, installDir);
648    strcat(augdPath, "lib");
649    strcat(augdPath, PATHSEP_STR);
650
651    /* fprintf ( stderr, "augdpath = `%s'\n", augdPath ); */
652
653    peEnd = augdPath-1;
654    while (1) {
655       /* Advance peStart and peEnd very paranoically, giving up at
656          the first sign of mutancy in the path string.
657       */
658       if (peEnd >= augdPath && !(*peEnd)) { free(augdPath); return FALSE; }
659       peStart = peEnd+1;
660       peEnd = peStart;
661       while (*peEnd && *peEnd != PATHSEP) peEnd++;
662       
663       /* Now peStart .. peEnd-1 bracket the next path element. */
664       nPath = peEnd-peStart;
665       if (nPath + strlen(modName) + 10 /*slush*/ > FILENAME_MAX) {
666          ERRMSG(0) "Hugs path \"%s\" contains excessively long component", 
667                    hugsPath
668          EEND;
669          free(augdPath); 
670          return FALSE;
671       }
672
673       strncpy(searchBuf, peStart, nPath); 
674       searchBuf[nPath] = 0;
675       if (nPath > 0 && !isSLASH(searchBuf[nPath-1])) 
676          searchBuf[nPath++] = SLASH;
677
678       strcpy(searchBuf+nPath, modName);
679       nPath += strlen(modName);
680
681       /* searchBuf now holds 'P/M'.  Try out the various endings. */
682       *path = *sExt                         = NULL;
683       *sAvail = *oiAvail = oAvail = iAvail  = FALSE;
684       *sSize = *oSize = *iSize              = 0;
685
686       if (combined) {
687          strcpy(searchBuf+nPath, DLL_ENDING);
688          if (readable(searchBuf)) {
689             oAvail = TRUE;
690             getFileInfo(searchBuf, &oTime, oSize);
691          }
692          strcpy(searchBuf+nPath, HI_ENDING);
693          if (readable(searchBuf)) {
694             iAvail = TRUE;
695             getFileInfo(searchBuf, &iTime, iSize);
696          }
697          if (oAvail && iAvail) {
698             *oiAvail = TRUE;
699             *oiTime = whicheverIsLater ( oTime, iTime );
700          }
701       }
702
703       strcpy(searchBuf+nPath, ".hs");
704       if (readable(searchBuf)) {
705          *sAvail = TRUE;
706          literate = FALSE;
707          getFileInfo(searchBuf, sTime, sSize);
708          *sExt = ".hs";
709       } else {
710          strcpy(searchBuf+nPath, ".lhs");
711          if (readable(searchBuf)) {
712             *sAvail = TRUE;
713             literate = TRUE;
714             getFileInfo(searchBuf, sTime, sSize);
715             *sExt = ".lhs";
716          }
717       }
718
719       /* Success? */
720       if (*sAvail || *oiAvail) {
721          nPath -= strlen(modName);
722          *path = malloc(nPath+1);
723          if (!(*path))
724             internal("moduleNameToFileNames: malloc failed(1)");
725          strncpy(*path, searchBuf, nPath);
726          (*path)[nPath] = 0;
727          free(augdPath); 
728          return TRUE;
729       }
730
731    }
732    
733 }
734
735
736 /* If the primaryObjectName is (eg)
737      /foo/bar/PrelSwamp.o
738    and the extraFileName is (eg)
739      swampy_cbits
740    and DLL_ENDING is set to .o
741    return
742      /foo/bar/swampy_cbits.o
743      and set *extraFileSize to its size, or -1 if not avail
744 */
745 String getExtraObjectInfo ( String primaryObjectName,
746                             String extraFileName,
747                             Int*   extraFileSize )
748 {
749    Time   xTime;
750    Long   xSize;
751    String xtra;
752
753    Int i = strlen(primaryObjectName)-1;
754    while (i >= 0 && primaryObjectName[i] != SLASH) i--;
755    if (i == -1) return extraFileName;
756    i++;
757    xtra = malloc ( i+3+strlen(extraFileName)+strlen(DLL_ENDING) );
758    if (!xtra) internal("deriveExtraObjectName: malloc failed");
759    strncpy ( xtra, primaryObjectName, i );
760    xtra[i] = 0;
761    strcat ( xtra, extraFileName );
762    strcat ( xtra, DLL_ENDING );
763
764    *extraFileSize = -1;
765    if (readable(xtra)) {
766       getFileInfo ( xtra, &xTime, &xSize );
767       *extraFileSize = xSize;
768    }
769    return xtra;
770 }
771
772
773 /* --------------------------------------------------------------------------
774  * Substitute old value of path into empty entries in new path
775  * eg substPath("a:b:c::d:e","x:y:z") = "a:b:c:x:y:z:d:e"
776  * ------------------------------------------------------------------------*/
777
778 static String local substPath ( String,String );
779
780 static String local substPath(new,sub) /* substitute sub path into new path*/
781 String new;
782 String sub; {
783     Bool   substituted = FALSE;            /*   only allow one replacement */
784     Int    maxlen      = strlen(sub) + strlen(new);    /* safe upper bound */
785     String r = (String) malloc(maxlen+1);  /* result string                */
786     String t = r;                          /* pointer into r               */
787     String next = new;                     /* next uncopied char in new    */
788     String start = next;                   /* start of last path component */
789     if (r == 0) {
790         ERRMSG(0) "String storage space exhausted"
791         EEND;
792     }
793     do {
794         if (*next == PATHSEP || *next == '\0') {
795             if (!substituted && next == start) {
796                 String s = sub;
797                 for(; *s != '\0'; ++s) {
798                     *t++ = *s;
799                 }
800                 substituted = TRUE;
801             }
802             start = next+1;
803         }
804     } while ((*t++ = *next++) != '\0');
805     return r;
806 }
807
808
809 /* --------------------------------------------------------------------------
810  * Garbage collection notification:
811  * ------------------------------------------------------------------------*/
812
813 Bool gcMessages = FALSE;                /* TRUE => print GC messages       */
814
815 Void gcStarted() {                      /* Notify garbage collector start  */
816     if (gcMessages) {
817         Printf("{{Gc");
818         FlushStdout();
819     }
820 }
821
822 Void gcScanning() {                     /* Notify garbage collector scans  */
823     if (gcMessages) {
824         Putchar(':');
825         FlushStdout();
826     }
827 }
828
829 Void gcRecovered(recovered)             /* Notify garbage collection done  */
830 Int recovered; {
831     if (gcMessages) {
832         Printf("%d}}",recovered);
833         FlushStdout();
834     }
835 }
836
837 Cell *CStackBase;                       /* Retain start of C control stack */
838
839 #if RISCOS                              /* Stack traversal for RISCOS      */
840
841 /* Warning: The following code is specific to the Acorn ARM under RISCOS
842    (and C4).  We must explicitly walk back through the stack frames, since
843    the stack is extended from the heap. (see PRM pp. 1757).  gcCStack must
844    not be modified, since the offset '5' assumes that only v1 is used inside
845    this function. Hence we do all the real work in gcARM.
846 */
847                   
848 #define spreg 13 /* C3 has SP=R13 */
849
850 #define previousFrame(fp)       ((int *)((fp)[-3]))
851 #define programCounter(fp)      ((int *)((*(fp)-12) & ~0xFC000003))
852 #define isSubSPSP(w)            (((w)&dontCare) == doCare)
853 #define doCare                  (0xE24DD000)  /* SUB r13,r13,#0 */
854 #define dontCare                (~0x00100FFF) /* S and # bits   */
855 #define immediateArg(x)         ( ((x)&0xFF) << (((x)&0xF00)>>7) )
856
857 static void gcARM(int *fp) {
858     int si = *programCounter(fp);       /* Save instruction indicates how */
859                                         /* many registers in this frame   */
860     int *regs = fp - 4;
861     if (si & (1<<0)) markWithoutMove(*regs--);
862     if (si & (1<<1)) markWithoutMove(*regs--);
863     if (si & (1<<2)) markWithoutMove(*regs--);
864     if (si & (1<<3)) markWithoutMove(*regs--);
865     if (si & (1<<4)) markWithoutMove(*regs--);
866     if (si & (1<<5)) markWithoutMove(*regs--);
867     if (si & (1<<6)) markWithoutMove(*regs--);
868     if (si & (1<<7)) markWithoutMove(*regs--);
869     if (si & (1<<8)) markWithoutMove(*regs--);
870     if (si & (1<<9)) markWithoutMove(*regs--);
871     if (previousFrame(fp)) {
872         /* The non-register stack space is for the previous frame is above
873            this fp, and not below the previous fp, because of the way stack
874            extension works. It seems the only way of discovering its size is
875            finding the SUB sp, sp, #? instruction by walking through the code
876            following the entry point.
877         */
878         int *oldpc = programCounter(previousFrame(fp));
879         int fsize = 0, i;
880         for(i = 1; i < 6; ++i)
881             if(isSubSPSP(oldpc[i])) fsize += immediateArg(oldpc[i]) / 4;
882         for(i=1; i<=fsize; ++i)
883             markWithoutMove(fp[i]);
884     }
885 }
886
887 void gcCStack() {
888     int dummy;
889     int *fp = 5 + &dummy;
890     while (fp) {
891         gcARM(fp);
892         fp = previousFrame(fp);
893     }
894 }
895
896 #else                   /* Garbage collection for standard stack machines  */
897
898 Void gcCStack() {                       /* Garbage collect elements off    */
899     Cell stackTop = NIL;                /* C stack                         */
900     Cell *ptr = &stackTop;
901 #if SIZEOF_VOID_P == 2
902     if (((long)(ptr) - (long)(CStackBase))&1)
903         fatal("gcCStack");
904 #elif STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
905     if (((long)(ptr) - (long)(CStackBase))&1)
906         fatal("gcCStack");
907 #else 
908     if (((long)(ptr) - (long)(CStackBase))&3)
909         fatal("gcCStack");
910 #endif
911
912 #define Blargh mark(*ptr);
913 #if 0
914                markWithoutMove((*ptr)/sizeof(Cell)); \
915                markWithoutMove(( (void*)(*ptr)-(void*)heapTopFst)/sizeof(Cell));  \
916                markWithoutMove(( (void*)(*ptr)-(void*)heapTopSnd)/sizeof(Cell))
917 #endif
918
919 #define StackGrowsDown  { while (ptr<=CStackBase) { Blargh; ptr++; }; }
920 #define StackGrowsUp    { while (ptr>=CStackBase) { Blargh; ptr--; }; }
921 #define GuessDirection  if (ptr>CStackBase) StackGrowsUp else StackGrowsDown
922
923 #if STACK_DIRECTION > 0
924     StackGrowsUp;
925 #elif STACK_DIRECTION < 0
926     StackGrowsDown;
927 #else
928     GuessDirection;
929 #endif
930
931 #if SIZEOF_VOID_P==4 && STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
932     ptr = (Cell *)((long)(&stackTop) + 2);
933     StackGrowsDown;
934 #endif
935
936 #undef  StackGrowsDown
937 #undef  StackGrowsUp
938 #undef  GuessDirection
939 }
940 #endif
941
942 /* --------------------------------------------------------------------------
943  * Terminal dependent stuff:
944  * ------------------------------------------------------------------------*/
945
946 #if (HAVE_TERMIO_H | HAVE_SGTTY_H | HAVE_TERMIOS_H)
947
948 /* grab the varargs prototype for ioctl */
949 #if HAVE_SYS_IOCTL_H
950 # include <sys/ioctl.h>
951 #endif
952
953 /* The order of these three tests is very important because
954  * some systems have more than one of the requisite header file
955  * but only one of them seems to work.
956  * Anyone changing the order of the tests should try enabling each of the
957  * three branches in turn and write down which ones work as well as which
958  * OS/compiler they're using.
959  *
960  * OS            Compiler      sgtty     termio  termios   notes
961  * Linux 2.0.18  gcc 2.7.2     absent    works   works     1
962  *
963  * Notes:
964  * 1) On Linux, termio.h just #includes termios.h and sgtty.h is
965  *    implemented using termios.h.
966  *    sgtty.h is in /usr/include/bsd which is not on my standard include
967  *    path.  Adding it does no harm but you might as well use termios.
968  *    --
969  *    reid-alastair@cs.yale.edu
970  */
971 #if HAVE_TERMIOS_H
972
973 #include <termios.h>
974 typedef  struct termios  TermParams;
975 #define  getTerminal(tp) tcgetattr(fileno(stdin), &tp)
976 #define  setTerminal(tp) tcsetattr(fileno(stdin), TCSAFLUSH, &tp)
977 #define  noEcho(tp)      tp.c_lflag    &= ~(ICANON | ECHO); \
978                          tp.c_cc[VMIN]  = 1;                \
979                          tp.c_cc[VTIME] = 0;
980
981 #elif HAVE_SGTTY_H
982
983 #include <sgtty.h>
984 typedef  struct sgttyb   TermParams;
985 #define  getTerminal(tp) ioctl(fileno(stdin),TIOCGETP,&tp)
986 #define  setTerminal(tp) ioctl(fileno(stdin),TIOCSETP,&tp)
987 #if HPUX
988 #define  noEcho(tp)      tp.sg_flags |= RAW; tp.sg_flags &= (~ECHO);
989 #else
990 #define  noEcho(tp)      tp.sg_flags |= CBREAK; tp.sg_flags &= (~ECHO);
991 #endif
992
993 #elif HAVE_TERMIO_H
994
995 #include <termio.h>
996 typedef  struct termio   TermParams;
997 #define  getTerminal(tp) ioctl(fileno(stdin),TCGETA,&tp)
998 #define  setTerminal(tp) ioctl(fileno(stdin),TCSETAF,&tp)
999 #define  noEcho(tp)      tp.c_lflag    &= ~(ICANON | ECHO); \
1000                          tp.c_cc[VMIN]  = 1;                \
1001                          tp.c_cc[VTIME] = 0;
1002
1003 #endif
1004
1005 static Bool messedWithTerminal = FALSE;
1006 static TermParams originalSettings;
1007
1008 Void normalTerminal() {                 /* restore terminal initial state  */
1009     if (messedWithTerminal)
1010         setTerminal(originalSettings);
1011 }
1012
1013 Void noechoTerminal() {                 /* set terminal into noecho mode   */
1014     TermParams settings;
1015
1016     if (!messedWithTerminal) {
1017         getTerminal(originalSettings);
1018         messedWithTerminal = TRUE;
1019     }
1020     getTerminal(settings);
1021     noEcho(settings);
1022     setTerminal(settings);
1023 }
1024
1025 Int getTerminalWidth() {                /* determine width of terminal     */
1026 #ifdef TIOCGWINSZ
1027 #ifdef _M_UNIX                          /* SCO Unix 3.2.4 defines TIOCGWINSZ*/
1028 #include <sys/stream.h>                 /* Required by sys/ptem.h          */
1029 #include <sys/ptem.h>                   /* Required to declare winsize     */
1030 #endif
1031     static struct winsize terminalSize;
1032     ioctl(fileno(stdout),TIOCGWINSZ,&terminalSize);
1033     return (terminalSize.ws_col==0)? 80 : terminalSize.ws_col;
1034 #else
1035     return 80;
1036 #endif
1037 }
1038
1039 Int readTerminalChar() {                /* read character from terminal    */
1040     return getchar();                   /* without echo, assuming that     */
1041 }                                       /* noechoTerminal() is active...   */
1042
1043 #elif SYMANTEC_C
1044
1045 Int readTerminalChar() {                /* read character from terminal    */
1046     return getchar();                   /* without echo, assuming that     */
1047 }                                       /* noechoTerminal() is active...   */
1048  
1049 Int getTerminalWidth() {
1050     return console_options.ncols;
1051 }
1052
1053 Void normalTerminal() {
1054     csetmode(C_ECHO, stdin);
1055 }
1056
1057 Void noechoTerminal() {
1058     csetmode(C_NOECHO, stdin);
1059 }
1060
1061 #else /* no terminal driver - eg DOS, RISCOS */
1062
1063 static Bool terminalEchoReqd = TRUE;
1064
1065 Int getTerminalWidth() {
1066 #if RISCOS
1067     int dummy, width;
1068     (void) os_swi3r(OS_ReadModeVariable, -1, 1, 0, &dummy, &dummy, &width);
1069     return width+1;
1070 #else
1071     return 80;
1072 #endif
1073 }
1074
1075 Void normalTerminal() {                 /* restore terminal initial state  */
1076     terminalEchoReqd = TRUE;
1077 }
1078
1079 Void noechoTerminal() {                 /* turn terminal echo on/off       */
1080     terminalEchoReqd = FALSE;
1081 }
1082
1083 Int readTerminalChar() {                /* read character from terminal    */
1084     if (terminalEchoReqd) {
1085         return getchar();
1086     } else {
1087 #if IS_WIN32 && !__BORLANDC__
1088         /* When reading a character from the console/terminal, we want
1089          * to operate in 'raw' mode (to use old UNIX tty parlance) and have
1090          * it return when a character is available and _not_ wait until
1091          * the next time the user hits carriage return. On Windows platforms,
1092          * this _can_ be done by reading directly from the console, using
1093          * getch().  However, this doesn't sit well with programming
1094          * environments such as Emacs which allow you to create sub-processes
1095          * running Hugs, and then communicate with the running interpreter
1096          * through its standard input and output handles. If you use getch()
1097          * in that setting, you end up trying to read the (unused) console
1098          * of the editor itself, through which not a lot of characters is
1099          * bound to come out, since the editor communicates input to Hugs
1100          * via the standard input handle.
1101          *
1102          * To avoid this rather unfortunate situation, we use the Win32
1103          * console API and re-jig the input properties of the standard
1104          * input handle before trying to read a character using stdio's
1105          * getchar().
1106          * 
1107          * The 'cost' of this solution is that it is Win32 specific and
1108          * won't work with Windows 3.1 + it is kind of ugly and verbose
1109          * to have to futz around with the console properties on a
1110          * per-char basis. Both of these disadvantages aren't in my
1111          * opinion fatal.
1112          *
1113          * -- sof 5/99
1114          */
1115         Int c;
1116         DWORD mo;
1117         HANDLE hIn;
1118  
1119         /* I don't quite understand why, but if the FILE*'s underlying file
1120            descriptor is in text mode, we seem to lose the first carriage
1121            return.
1122          */
1123         setmode(fileno(stdin), _O_BINARY);
1124         hIn = GetStdHandle(STD_INPUT_HANDLE);
1125         GetConsoleMode(hIn, &mo);
1126         SetConsoleMode(hIn, mo & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
1127         /* 
1128          * On Win9x, the first time you change the mode (as above) a
1129          * raw '\n' is inserted.  Since enter maps to a raw '\r', and we
1130          * map this (below) to '\n', we can just ignore all *raw* '\n's.
1131          */
1132         do {
1133           c = getc(stdin);
1134         } while (c == '\n');
1135  
1136         /* Same as it ever was - revert back state of stdin. */
1137         SetConsoleMode(hIn, mo);
1138         setmode(fileno(stdin), _O_TEXT);
1139 #else
1140         Int c = getch();
1141 #endif
1142         return c=='\r' ? '\n' : c;      /* slight paranoia about CR-LF    */
1143     }
1144 }
1145
1146 #endif /* no terminal driver */
1147
1148 /* --------------------------------------------------------------------------
1149  * Interrupt handling:
1150  * ------------------------------------------------------------------------*/
1151
1152 static Void installHandlers ( void ) { /* Install handlers for all fatal   */ 
1153                                       /* signals except SIGINT and SIGBREAK*/
1154 #if IS_WIN32
1155     /* SetConsoleCtrlHandler(consoleHandler,TRUE); */
1156 #endif
1157 #if !DONT_PANIC && !DOS
1158 # ifdef SIGABRT
1159     signal(SIGABRT,panic);
1160 # endif
1161 # ifdef SIGBUS
1162     signal(SIGBUS,panic);
1163 # endif
1164 # ifdef SIGFPE
1165     signal(SIGFPE,panic);
1166 # endif
1167 # ifdef SIGHUP
1168     signal(SIGHUP,panic);
1169 # endif
1170 # ifdef SIGILL
1171     signal(SIGILL,panic);
1172 # endif
1173 # ifdef SIGQUIT
1174     signal(SIGQUIT,panic);
1175 # endif
1176 # ifdef SIGSEGV
1177     signal(SIGSEGV,panic);
1178 # endif
1179 # ifdef SIGTERM
1180     signal(SIGTERM,panic);
1181 # endif
1182 #endif /* !DONT_PANIC && !DOS */
1183 }
1184
1185 /* --------------------------------------------------------------------------
1186  * Shell escapes:
1187  * ------------------------------------------------------------------------*/
1188
1189 static Bool local startEdit(line,nm)    /* Start editor on file name at    */
1190 Int    line;                            /* given line.  Both name and line */
1191 String nm; {                            /* or just line may be zero        */
1192     static char editorCmd[FILENAME_MAX+1];
1193
1194 #if !SYMANTEC_C
1195     if (hugsEdit && *hugsEdit) {        /* Check that editor configured    */
1196 #else
1197     /* On a Mac, files have creator information, telling which program
1198        to launch to, so an editor named to the empty string "" is often
1199        desirable. */
1200     if (hugsEdit) {        /* Check that editor configured    */
1201 #endif
1202         Int n     = FILENAME_MAX;
1203         String he = hugsEdit;
1204         String ec = editorCmd;
1205         String rd = NULL;               /* Set to nonnull to redo ...      */
1206
1207         for (; n>0 && *he && *he!=' ' && *he!='%'; n--)
1208             *ec++ = *he++;              /* Copy editor name to buffer      */
1209                                         /* assuming filename ends at space */
1210
1211         if (nm && line && n>1 && *he){  /* Name, line, and enough space    */
1212             rd = ec;                    /* save, in case we don't find name*/
1213             while (n>0 && *he) {
1214                 if (*he=='%') {
1215                     if (*++he=='d' && n>10) {
1216                         sprintf(ec,"%d",line);
1217                         he++;
1218                     }
1219                     else if (*he=='s' && (size_t)n>strlen(nm)) {
1220                         strcpy(ec,nm);
1221                         rd = NULL;
1222                         he++;
1223                     }
1224                     else if (*he=='%' && n>1) {
1225                         strcpy(ec,"%");
1226                         he++;
1227                     }
1228                     else                /* Ignore % char if not followed   */
1229                         *ec = '\0';     /* by one of d, s, or %,           */
1230                     for (; *ec && n>0; n--)
1231                         ec++;
1232                 }   /* ignore % followed by anything other than d, s, or % */
1233                 else {                  /* Copy other characters across    */
1234                     *ec++ = *he++;
1235                     n--;
1236                 }
1237             }
1238         }
1239         else
1240             line = 0;
1241
1242         if (rd) {                       /* If file name was not included   */
1243             ec   = rd;
1244             line = 0;
1245         }
1246
1247         if (nm && line==0 && n>1) {     /* Name, but no line ...           */
1248             *ec++ = ' ';
1249             for (; n>0 && *nm; n--)     /* ... just copy file name         */
1250                 *ec++ = *nm++;
1251         }
1252
1253         *ec = '\0';                     /* Add terminating null byte       */
1254     }
1255     else {
1256         ERRMSG(0) "Hugs is not configured to use an editor"
1257         EEND;
1258     }
1259
1260 #if HAVE_WINEXEC
1261     WinExec(editorCmd, SW_SHOW);
1262     return FALSE;
1263 #else
1264     if (shellEsc(editorCmd))
1265         Printf("Warning: Editor terminated abnormally\n");
1266     return TRUE;
1267 #endif
1268 }
1269
1270 Int shellEsc(s)                         /* run a shell command (or shell)  */
1271 String s; {
1272 #if HAVE_MACSYSTEM
1273     return macsystem(s);
1274 #else
1275 #if HAVE_BIN_SH
1276     if (s[0]=='\0') {
1277         s = fromEnv("SHELL","/bin/sh");
1278     }
1279 #endif
1280     return system(s);
1281 #endif
1282 }
1283
1284 #if RISCOS                              /* RISCOS also needs a chdir()     */
1285 int chdir(char *s) {                    /* RISCOS PRM p. 885    -- JBS     */
1286     return os_swi2(OS_FSControl + XOS_Bit, 0, (int)s) != NULL;
1287 }
1288 #elif defined HAVE_PBHSETVOLSYNC        /* Macintosh */
1289 int chdir(const char *s) {      
1290     char* str;
1291     WDPBRec wd;
1292     wd.ioCompletion = 0;
1293     str = (char*)malloc(strlen(s) + 1);
1294     if (str == 0) return -1;
1295     strcpy(str, s);
1296     wd.ioNamePtr = C2PStr(str);
1297     wd.ioVRefNum = 0;
1298     wd.ioWDDirID = 0;
1299     errno = PBHSetVolSync(&wd);
1300     free(str);
1301     if (errno == 0) {
1302         return 0;
1303     } else {
1304         return -1;
1305     }
1306 }
1307 #endif
1308
1309
1310 /*---------------------------------------------------------------------------
1311  * Printf-related operations:
1312  *-------------------------------------------------------------------------*/
1313
1314 #if !defined(HAVE_VSNPRINTF)
1315 int vsnprintf(buffer, count, fmt, ap)
1316 char*       buffer;
1317 int         count;
1318 const char* fmt;
1319 va_list     ap; {
1320 #if defined(HAVE__VSNPRINTF)
1321     return _vsnprintf(buffer, count, fmt, ap);
1322 #else
1323     return 0;
1324 #endif
1325 }
1326 #endif /* HAVE_VSNPRINTF */
1327
1328 #if !defined(HAVE_SNPRINTF)
1329 int snprintf(char* buffer, int count, const char* fmt, ...) {
1330 #if defined(HAVE__VSNPRINTF)
1331     int r;
1332     va_list ap;                    /* pointer into argument list           */
1333     va_start(ap, fmt);             /* make ap point to first arg after fmt */
1334     r = vsnprintf(buffer, count, fmt, ap);
1335     va_end(ap);                    /* clean up                             */
1336     return r;
1337 #else
1338     return 0;
1339 #endif
1340 }
1341 #endif /* HAVE_SNPRINTF */
1342
1343 /* --------------------------------------------------------------------------
1344  * Things to do with the argv/argc and the env
1345  * ------------------------------------------------------------------------*/
1346
1347 int nh_argc ( void )
1348 {
1349   return prog_argc;
1350 }
1351
1352 int nh_argvb ( int argno, int offset )
1353 {
1354   return (int)(prog_argv[argno][offset]);
1355 }
1356
1357 /* --------------------------------------------------------------------------
1358  * Machine dependent control:
1359  * ------------------------------------------------------------------------*/
1360
1361 Void machdep(what)                      /* Handle machine specific         */
1362 Int what; {                             /* initialisation etc..            */
1363     switch (what) {
1364         case MARK    : break;
1365         case POSTPREL: break;
1366         case PREPREL : installHandlers();
1367                        break;
1368         case RESET   :
1369         case BREAK   :
1370         case EXIT    : normalTerminal();
1371                        break;
1372     }
1373 }
1374
1375 /*-------------------------------------------------------------------------*/