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)
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.
15 * $RCSfile: machdep.c,v $
17 * $Date: 2000/04/10 09:40:03 $
18 * ------------------------------------------------------------------------*/
23 #ifdef HAVE_SYS_TYPES_H
24 # include <sys/types.h>
31 # include <sys/param.h>
33 #ifdef HAVE_SYS_STAT_H
34 # include <sys/stat.h>
44 /* Windows/DOS include files */
48 #if defined HAVE_CONIO_H
63 extern unsigned _stklen = 8000; /* Allocate an 8k stack segment */
71 /* Macintosh include files */
94 int allow_break_count = 0;
97 /* --------------------------------------------------------------------------
98 * Find information about a file:
99 * ------------------------------------------------------------------------*/
101 #include "machdep_time.h"
103 static Bool local readable ( String );
104 static Void local getFileInfo ( String, Time *, Long * );
106 static Void local getFileInfo(f,tm,sz) /* find time stamp and size of file*/
110 #if defined HAVE_SYS_STAT_H || defined HAVE_STAT_H || defined HAVE_UNIX_H
112 if (!stat(f,&scbuf)) {
113 if (tm) *tm = scbuf.st_mtime;
114 *sz = (Long)(scbuf.st_size);
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 */
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;
130 *sz = (Long)(r.r[0] == 1 ? r.r[4] : 0);
134 Void getFileSize ( String f, Long* sz )
136 getFileInfo ( f, NULL, sz );
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 * ------------------------------------------------------------------------*/
152 int access(char *fileName, int dummy) {
156 errno = getfinfo(fileName, 0, &fi);
157 if (errno != 0) return -1; /* Check file accessible. */
159 /* Cases dummy = existence, read, write. */
160 if (dummy == 0 || dummy & 0x6) return 0;
162 /* Case dummy = executable. */
164 if (fi.fdType == 'APPL') return 0;
173 static Bool local readable(f) /* is f a regular, readable file */
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
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 */
184 #elif defined HAVE_OS_SWI /* RISCOS specific */
185 os_regset r; /* RISCOS PRM p.850 -- JBS */
187 r.r[0] = 17; /* Read catalogue, no path */
190 return r.r[0] != 1; /* Does this check it's a regular file? ADR */
195 /* --------------------------------------------------------------------------
196 * Search for script files on the HUGS path:
197 * ------------------------------------------------------------------------*/
199 static String local hugsdir ( Void );
201 static String local hscriptDir ( Void );
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 );
209 #if (DOS_FILENAMES || __CYGWIN32__)
211 # define isSLASH(c) ((c)=='\\' || (c)=='/')
213 # define PATHSEP_STR ";"
214 # define DLL_ENDING ".dll"
217 # define isSLASH(c) ((c)==SLASH)
219 # define PATHSEP_STR ";"
220 /* Mac PEF (Preferred Executable Format) file */
221 # define DLL_ENDING ".pef"
224 # define isSLASH(c) ((c)==SLASH)
226 # define PATHSEP_STR ":"
227 # define DLL_ENDING ".u_o"
230 static String local hugsdir() { /* directory containing lib/Prelude.hs */
232 /* In HaskellScript (Win32 only), we lookup InstallDir in the registry. */
233 static char dir[FILENAME_MAX+1] = "";
234 if (dir[0] == '\0') { /* not initialised yet */
235 String s = readRegString(HKEY_LOCAL_MACHINE,HugsRoot,"InstallDir",
242 #elif HAVE_GETMODULEFILENAME && !DOS && !__CYGWIN32__
243 /* On Windows, we can find the binary we're running and it's
244 * conventional to put the libraries in the same place.
246 static char dir[FILENAME_MAX+1] = "";
247 if (dir[0] == '\0') { /* not initialised yet */
249 GetModuleFileName((HMODULE)0,dir,FILENAME_MAX+1);
250 if (dir[0] == '\0') { /* GetModuleFileName must have failed */
253 slash = strrchr(dir,SLASH);
254 if (slash) { /* truncate after directory name */
260 /* On Unix systems, we can't find the binary we're running and
261 * the libraries may not be installed near the binary anyway.
262 * This forces us to use a hardwired path which is set at
263 * configuration time (--datadir=...).
270 static String local hscriptDir() { /* Directory containing hscript.dll */
271 static char dir[FILENAME_MAX+1] = "";
272 if (dir[0] == '\0') { /* not initialised yet */
273 String s = readRegString(HKEY_LOCAL_MACHINE,HScriptRoot,"InstallDir","");
282 #if 0 /* apparently unused */
283 static String local RealPath(s) /* Find absolute pathname of file */
285 #if HAVE__FULLPATH /* eg DOS */
286 static char path[FILENAME_MAX+1];
287 _fullpath(path,s,FILENAME_MAX+1);
288 #elif HAVE_REALPATH /* eg Unix */
289 static char path[MAXPATHLEN+1];
292 static char path[FILENAME_MAX+1];
300 static int local pathCmp(p1,p2) /* Compare paths after normalisation */
303 #if HAVE__FULLPATH /* eg DOS */
304 static char path1[FILENAME_MAX+1];
305 static char path2[FILENAME_MAX+1];
306 _fullpath(path1,p1,FILENAME_MAX+1);
307 _fullpath(path2,p2,FILENAME_MAX+1);
308 #elif HAVE_REALPATH /* eg Unix */
309 static char path1[MAXPATHLEN+1];
310 static char path2[MAXPATHLEN+1];
314 static char path1[FILENAME_MAX+1];
315 static char path2[FILENAME_MAX+1];
319 #if CASE_INSENSITIVE_FILENAMES
323 return filenamecmp(path1,path2);
326 static String local normPath(s) /* Try, as much as possible, to normalize */
327 String s; { /* a pathname in some appropriate manner. */
328 #if PATH_CANONICALIZATION
329 String path = RealPath(s);
330 #if CASE_INSENSITIVE_FILENAMES
331 strlwr(path); /* and convert to lowercase */
334 #else /* ! PATH_CANONICALIZATION */
336 #endif /* ! PATH_CANONICALIZATION */
340 static String endings[] = { "", ".u_hi", ".hs", ".lhs", ".hsx", ".hash", 0 };
342 static String endings[] = { "", ".u_hi", ".hs", ".lhs", 0 };
344 static char searchBuf[FILENAME_MAX+1];
345 static Int searchPos;
347 #define searchReset(n) searchBuf[searchPos=(n)]='\0'
349 static Void local searchChr(c) /* Add single character to search buffer */
351 if (searchPos<FILENAME_MAX) {
352 searchBuf[searchPos++] = (char)c;
353 searchBuf[searchPos] = '\0';
357 static Void local searchStr(s) /* Add string to search buffer */
359 while (*s && searchPos<FILENAME_MAX)
360 searchBuf[searchPos++] = *s++;
361 searchBuf[searchPos] = '\0';
364 static Bool local tryEndings(s) /* Try each of the listed endings */
368 for (; endings[i]; ++i) {
369 Int save = searchPos;
370 searchStr(endings[i]);
371 if (readable(searchBuf))
382 /* scandir, June 98 Daan Leijen
383 searches the base directory and its direct subdirectories for a file
385 input: searchbuf contains SLASH terminated base directory
386 argument s contains the (base) filename
387 output: TRUE: searchBuf contains the full filename
388 FALSE: searchBuf is garbage, file not found
392 #ifdef HAVE_WINDOWS_H
394 static Bool scanSubDirs(s)
397 struct _finddata_t findInfo;
402 /* is it in the current directory ? */
403 if (tryEndings(s)) return TRUE;
408 /* initiate the search */
409 handle = _findfirst( searchBuf, &findInfo );
410 if (handle==-1) { errno = 0; return FALSE; }
412 /* search all subdirectories */
414 /* if we have a valid sub directory */
415 if (((findInfo.attrib & _A_SUBDIR) == _A_SUBDIR) &&
416 (findInfo.name[0] != '.')) {
418 searchStr(findInfo.name);
424 } while (_findnext( handle, &findInfo ) == 0);
426 _findclose( handle );
430 #elif defined(HAVE_FTW_H)
434 static char baseFile[FILENAME_MAX+1];
435 static char basePath[FILENAME_MAX+1];
436 static int basePathLen;
438 static int scanitem( const char* path,
439 const struct stat* statinfo,
442 if (info == FTW_D) { /* is it a directory */
446 if (tryEndings(baseFile)) {
453 static Bool scanSubDirs(s)
458 strcpy(basePath,searchBuf);
459 basePathLen = strlen(basePath);
461 /* is it in the current directory ? */
462 if (tryEndings(s)) return TRUE;
464 /* otherwise scan the subdirectories */
465 r = ftw( basePath, scanitem, 2 );
470 #endif /* HAVE_WINDOWS_H || HAVE_FTW_H */
471 #endif /* SEARCH_DIR */
473 String findPathname(along,nm) /* Look for a file along specified path */
474 String along; /* Return NULL if file does not exist */
476 /* AC, 1/21/99: modified to search hugsPath first, then projectPath */
477 String s = findMPathname(along,nm,hugsPath);
478 return s ? s : normPath(searchBuf);
481 /* AC, 1/21/99: modified to pass in path to search explicitly */
482 String findMPathname(along,nm,path)/* Look for a file along specified path */
483 String along; /* If nonzero, a path prefix from along is */
484 String nm; /* used as the first prefix in the search. */
486 String pathpt = path;
489 if (along) { /* Was a path for an existing file given? */
492 for (; along[i]; i++) {
494 if (isSLASH(along[i]))
500 return normPath(searchBuf);
502 if (pathpt && *pathpt) { /* Otherwise, we look along the HUGSPATH */
505 Bool recurse = FALSE; /* DL: shall we recurse ? */
508 if (*pathpt!=PATHSEP) {
509 /* Pre-define one MPW-style "shell-variable" */
510 if (strncmp(pathpt,"{Hugs}",6)==0) {
511 searchStr(hugsdir());
515 /* And another - we ought to generalise this stuff */
516 else if (strncmp(pathpt,"{HScript}",9)==0) {
517 searchStr(hscriptDir());
522 searchChr(*pathpt++);
523 } while (*pathpt && *pathpt!=PATHSEP);
524 recurse = (pathpt[-1] == SLASH);
529 if (*pathpt==PATHSEP)
537 if (recurse ? scanSubDirs(nm) : tryEndings(nm)) {
538 return normPath(searchBuf);
541 if (tryEndings(nm)) {
542 return normPath(searchBuf);
548 searchReset(0); /* As a last resort, look for file in the current dir */
549 return (tryEndings(nm) ? normPath(searchBuf) : 0);
552 /* --------------------------------------------------------------------------
553 * New path handling stuff for the Combined System (tm)
554 * ------------------------------------------------------------------------*/
556 char installDir[N_INSTALLDIR];
558 /* Sets installDir to $STGHUGSDIR, and ensures there is a trailing
561 void setInstallDir ( String argv_0 )
564 char* r = getenv("STGHUGSDIR");
567 "%s: installation error: environment variable STGHUGSDIR is not set.\n",
570 "%s: pls set it to be the directory where STGHugs98 is installed.\n\n",
576 if (strlen(r) > N_INSTALLDIR-30 ) {
578 "%s: environment variable STGHUGSDIR is suspiciously long; pls remedy\n\n",
583 strcpy ( installDir, r );
584 i = strlen(installDir);
585 if (installDir[i-1] != SLASH) installDir[i++] = SLASH;
590 Bool findFilesForModule (
594 Bool* sAvail, Time* sTime, Long* sSize,
595 Bool* oiAvail, Time* oiTime, Long* oSize, Long* iSize
598 /* Let the module name given be M.
599 For each path entry P,
600 a s(rc) file will be P/M.hs or P/M.lhs
601 an i(nterface) file will be P/M.hi
602 an o(bject) file will be P/M.o
603 If there is a s file or (both i and o files)
604 use P to fill in the path names.
605 Otherwise, move on to the next path entry.
606 If all path entries are exhausted, return False.
608 If in standalone, only look for (and succeed for) source modules.
609 Caller free()s path. sExt is statically allocated.
610 srcExt is only set if a valid source file is found.
614 String peStart, peEnd;
615 String augdPath; /* .:hugsPath:installDir/GhcPrel:installDir/lib */
619 *path = *sExt = NULL;
620 *sAvail = *oiAvail = oAvail = iAvail = FALSE;
621 *sSize = *oSize = *iSize = 0;
623 augdPath = malloc( 2*(10+3+strlen(installDir))
624 +strlen(hugsPath) +10/*paranoia*/);
626 internal("moduleNameToFileNames: malloc failed(2)");
629 strcat(augdPath, ".");
630 strcat(augdPath, PATHSEP_STR);
632 strcat(augdPath, hugsPath);
633 strcat(augdPath, PATHSEP_STR);
636 strcat(augdPath, installDir);
637 strcat(augdPath, "GhcPrel");
638 strcat(augdPath, PATHSEP_STR);
641 strcat(augdPath, installDir);
642 strcat(augdPath, "lib");
643 strcat(augdPath, PATHSEP_STR);
645 /* fprintf ( stderr, "augdpath = `%s'\n", augdPath ); */
649 /* Advance peStart and peEnd very paranoically, giving up at
650 the first sign of mutancy in the path string.
652 if (peEnd >= augdPath && !(*peEnd)) { free(augdPath); return FALSE; }
655 while (*peEnd && *peEnd != PATHSEP) peEnd++;
657 /* Now peStart .. peEnd-1 bracket the next path element. */
658 nPath = peEnd-peStart;
659 if (nPath + strlen(modName) + 10 /*slush*/ > FILENAME_MAX) {
660 ERRMSG(0) "Hugs path \"%s\" contains excessively long component",
667 strncpy(searchBuf, peStart, nPath);
668 searchBuf[nPath] = 0;
669 if (nPath > 0 && !isSLASH(searchBuf[nPath-1]))
670 searchBuf[nPath++] = SLASH;
672 strcpy(searchBuf+nPath, modName);
673 nPath += strlen(modName);
675 /* searchBuf now holds 'P/M'. Try out the various endings. */
676 *path = *sExt = NULL;
677 *sAvail = *oiAvail = oAvail = iAvail = FALSE;
678 *sSize = *oSize = *iSize = 0;
681 strcpy(searchBuf+nPath, DLL_ENDING);
682 if (readable(searchBuf)) {
684 getFileInfo(searchBuf, &oTime, oSize);
686 strcpy(searchBuf+nPath, HI_ENDING);
687 if (readable(searchBuf)) {
689 getFileInfo(searchBuf, &iTime, iSize);
691 if (oAvail && iAvail) {
693 *oiTime = whicheverIsLater ( oTime, iTime );
697 strcpy(searchBuf+nPath, ".hs");
698 if (readable(searchBuf)) {
701 getFileInfo(searchBuf, sTime, sSize);
704 strcpy(searchBuf+nPath, ".lhs");
705 if (readable(searchBuf)) {
708 getFileInfo(searchBuf, sTime, sSize);
714 if (*sAvail || *oiAvail) {
715 nPath -= strlen(modName);
716 *path = malloc(nPath+1);
718 internal("moduleNameToFileNames: malloc failed(1)");
719 strncpy(*path, searchBuf, nPath);
730 /* If the primaryObjectName is (eg)
732 and the extraFileName is (eg)
734 and DLL_ENDING is set to .o
736 /foo/bar/swampy_cbits.o
737 and set *extraFileSize to its size, or -1 if not avail
739 String getExtraObjectInfo ( String primaryObjectName,
740 String extraFileName,
747 Int i = strlen(primaryObjectName)-1;
748 while (i >= 0 && primaryObjectName[i] != SLASH) i--;
749 if (i == -1) return extraFileName;
751 xtra = malloc ( i+3+strlen(extraFileName)+strlen(DLL_ENDING) );
752 if (!xtra) internal("deriveExtraObjectName: malloc failed");
753 strncpy ( xtra, primaryObjectName, i );
755 strcat ( xtra, extraFileName );
756 strcat ( xtra, DLL_ENDING );
759 if (readable(xtra)) {
760 getFileInfo ( xtra, &xTime, &xSize );
761 *extraFileSize = xSize;
767 /* --------------------------------------------------------------------------
768 * Substitute old value of path into empty entries in new path
769 * eg substPath("a:b:c::d:e","x:y:z") = "a:b:c:x:y:z:d:e"
770 * ------------------------------------------------------------------------*/
772 static String local substPath ( String,String );
774 static String local substPath(new,sub) /* substitute sub path into new path*/
777 Bool substituted = FALSE; /* only allow one replacement */
778 Int maxlen = strlen(sub) + strlen(new); /* safe upper bound */
779 String r = (String) malloc(maxlen+1); /* result string */
780 String t = r; /* pointer into r */
781 String next = new; /* next uncopied char in new */
782 String start = next; /* start of last path component */
784 ERRMSG(0) "String storage space exhausted"
788 if (*next == PATHSEP || *next == '\0') {
789 if (!substituted && next == start) {
791 for(; *s != '\0'; ++s) {
798 } while ((*t++ = *next++) != '\0');
803 /* --------------------------------------------------------------------------
804 * Garbage collection notification:
805 * ------------------------------------------------------------------------*/
807 Bool gcMessages = FALSE; /* TRUE => print GC messages */
809 Void gcStarted() { /* Notify garbage collector start */
816 Void gcScanning() { /* Notify garbage collector scans */
823 Void gcRecovered(recovered) /* Notify garbage collection done */
826 Printf("%d}}",recovered);
831 Cell *CStackBase; /* Retain start of C control stack */
833 #if RISCOS /* Stack traversal for RISCOS */
835 /* Warning: The following code is specific to the Acorn ARM under RISCOS
836 (and C4). We must explicitly walk back through the stack frames, since
837 the stack is extended from the heap. (see PRM pp. 1757). gcCStack must
838 not be modified, since the offset '5' assumes that only v1 is used inside
839 this function. Hence we do all the real work in gcARM.
842 #define spreg 13 /* C3 has SP=R13 */
844 #define previousFrame(fp) ((int *)((fp)[-3]))
845 #define programCounter(fp) ((int *)((*(fp)-12) & ~0xFC000003))
846 #define isSubSPSP(w) (((w)&dontCare) == doCare)
847 #define doCare (0xE24DD000) /* SUB r13,r13,#0 */
848 #define dontCare (~0x00100FFF) /* S and # bits */
849 #define immediateArg(x) ( ((x)&0xFF) << (((x)&0xF00)>>7) )
851 static void gcARM(int *fp) {
852 int si = *programCounter(fp); /* Save instruction indicates how */
853 /* many registers in this frame */
855 if (si & (1<<0)) markWithoutMove(*regs--);
856 if (si & (1<<1)) markWithoutMove(*regs--);
857 if (si & (1<<2)) markWithoutMove(*regs--);
858 if (si & (1<<3)) markWithoutMove(*regs--);
859 if (si & (1<<4)) markWithoutMove(*regs--);
860 if (si & (1<<5)) markWithoutMove(*regs--);
861 if (si & (1<<6)) markWithoutMove(*regs--);
862 if (si & (1<<7)) markWithoutMove(*regs--);
863 if (si & (1<<8)) markWithoutMove(*regs--);
864 if (si & (1<<9)) markWithoutMove(*regs--);
865 if (previousFrame(fp)) {
866 /* The non-register stack space is for the previous frame is above
867 this fp, and not below the previous fp, because of the way stack
868 extension works. It seems the only way of discovering its size is
869 finding the SUB sp, sp, #? instruction by walking through the code
870 following the entry point.
872 int *oldpc = programCounter(previousFrame(fp));
874 for(i = 1; i < 6; ++i)
875 if(isSubSPSP(oldpc[i])) fsize += immediateArg(oldpc[i]) / 4;
876 for(i=1; i<=fsize; ++i)
877 markWithoutMove(fp[i]);
883 int *fp = 5 + &dummy;
886 fp = previousFrame(fp);
890 #else /* Garbage collection for standard stack machines */
892 Void gcCStack() { /* Garbage collect elements off */
893 Cell stackTop = NIL; /* C stack */
894 Cell *ptr = &stackTop;
895 #if SIZEOF_VOID_P == 2
896 if (((long)(ptr) - (long)(CStackBase))&1)
898 #elif STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
899 if (((long)(ptr) - (long)(CStackBase))&1)
902 if (((long)(ptr) - (long)(CStackBase))&3)
906 #define Blargh mark(*ptr);
908 markWithoutMove((*ptr)/sizeof(Cell)); \
909 markWithoutMove(( (void*)(*ptr)-(void*)heapTopFst)/sizeof(Cell)); \
910 markWithoutMove(( (void*)(*ptr)-(void*)heapTopSnd)/sizeof(Cell))
913 #define StackGrowsDown { while (ptr<=CStackBase) { Blargh; ptr++; }; }
914 #define StackGrowsUp { while (ptr>=CStackBase) { Blargh; ptr--; }; }
915 #define GuessDirection if (ptr>CStackBase) StackGrowsUp else StackGrowsDown
917 #if STACK_DIRECTION > 0
919 #elif STACK_DIRECTION < 0
925 #if SIZEOF_VOID_P==4 && STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
926 ptr = (Cell *)((long)(&stackTop) + 2);
930 #undef StackGrowsDown
932 #undef GuessDirection
936 /* --------------------------------------------------------------------------
937 * Terminal dependent stuff:
938 * ------------------------------------------------------------------------*/
940 #if (HAVE_TERMIO_H | HAVE_SGTTY_H | HAVE_TERMIOS_H)
942 /* grab the varargs prototype for ioctl */
944 # include <sys/ioctl.h>
947 /* The order of these three tests is very important because
948 * some systems have more than one of the requisite header file
949 * but only one of them seems to work.
950 * Anyone changing the order of the tests should try enabling each of the
951 * three branches in turn and write down which ones work as well as which
952 * OS/compiler they're using.
954 * OS Compiler sgtty termio termios notes
955 * Linux 2.0.18 gcc 2.7.2 absent works works 1
958 * 1) On Linux, termio.h just #includes termios.h and sgtty.h is
959 * implemented using termios.h.
960 * sgtty.h is in /usr/include/bsd which is not on my standard include
961 * path. Adding it does no harm but you might as well use termios.
963 * reid-alastair@cs.yale.edu
968 typedef struct termios TermParams;
969 #define getTerminal(tp) tcgetattr(fileno(stdin), &tp)
970 #define setTerminal(tp) tcsetattr(fileno(stdin), TCSAFLUSH, &tp)
971 #define noEcho(tp) tp.c_lflag &= ~(ICANON | ECHO); \
978 typedef struct sgttyb TermParams;
979 #define getTerminal(tp) ioctl(fileno(stdin),TIOCGETP,&tp)
980 #define setTerminal(tp) ioctl(fileno(stdin),TIOCSETP,&tp)
982 #define noEcho(tp) tp.sg_flags |= RAW; tp.sg_flags &= (~ECHO);
984 #define noEcho(tp) tp.sg_flags |= CBREAK; tp.sg_flags &= (~ECHO);
990 typedef struct termio TermParams;
991 #define getTerminal(tp) ioctl(fileno(stdin),TCGETA,&tp)
992 #define setTerminal(tp) ioctl(fileno(stdin),TCSETAF,&tp)
993 #define noEcho(tp) tp.c_lflag &= ~(ICANON | ECHO); \
999 static Bool messedWithTerminal = FALSE;
1000 static TermParams originalSettings;
1002 Void normalTerminal() { /* restore terminal initial state */
1003 if (messedWithTerminal)
1004 setTerminal(originalSettings);
1007 Void noechoTerminal() { /* set terminal into noecho mode */
1008 TermParams settings;
1010 if (!messedWithTerminal) {
1011 getTerminal(originalSettings);
1012 messedWithTerminal = TRUE;
1014 getTerminal(settings);
1016 setTerminal(settings);
1019 Int getTerminalWidth() { /* determine width of terminal */
1021 #ifdef _M_UNIX /* SCO Unix 3.2.4 defines TIOCGWINSZ*/
1022 #include <sys/stream.h> /* Required by sys/ptem.h */
1023 #include <sys/ptem.h> /* Required to declare winsize */
1025 static struct winsize terminalSize;
1026 ioctl(fileno(stdout),TIOCGWINSZ,&terminalSize);
1027 return (terminalSize.ws_col==0)? 80 : terminalSize.ws_col;
1033 Int readTerminalChar() { /* read character from terminal */
1034 return getchar(); /* without echo, assuming that */
1035 } /* noechoTerminal() is active... */
1039 Int readTerminalChar() { /* read character from terminal */
1040 return getchar(); /* without echo, assuming that */
1041 } /* noechoTerminal() is active... */
1043 Int getTerminalWidth() {
1044 return console_options.ncols;
1047 Void normalTerminal() {
1048 csetmode(C_ECHO, stdin);
1051 Void noechoTerminal() {
1052 csetmode(C_NOECHO, stdin);
1055 #else /* no terminal driver - eg DOS, RISCOS */
1057 static Bool terminalEchoReqd = TRUE;
1059 Int getTerminalWidth() {
1062 (void) os_swi3r(OS_ReadModeVariable, -1, 1, 0, &dummy, &dummy, &width);
1069 Void normalTerminal() { /* restore terminal initial state */
1070 terminalEchoReqd = TRUE;
1073 Void noechoTerminal() { /* turn terminal echo on/off */
1074 terminalEchoReqd = FALSE;
1077 Int readTerminalChar() { /* read character from terminal */
1078 if (terminalEchoReqd) {
1081 #if IS_WIN32 && !__BORLANDC__
1082 /* When reading a character from the console/terminal, we want
1083 * to operate in 'raw' mode (to use old UNIX tty parlance) and have
1084 * it return when a character is available and _not_ wait until
1085 * the next time the user hits carriage return. On Windows platforms,
1086 * this _can_ be done by reading directly from the console, using
1087 * getch(). However, this doesn't sit well with programming
1088 * environments such as Emacs which allow you to create sub-processes
1089 * running Hugs, and then communicate with the running interpreter
1090 * through its standard input and output handles. If you use getch()
1091 * in that setting, you end up trying to read the (unused) console
1092 * of the editor itself, through which not a lot of characters is
1093 * bound to come out, since the editor communicates input to Hugs
1094 * via the standard input handle.
1096 * To avoid this rather unfortunate situation, we use the Win32
1097 * console API and re-jig the input properties of the standard
1098 * input handle before trying to read a character using stdio's
1101 * The 'cost' of this solution is that it is Win32 specific and
1102 * won't work with Windows 3.1 + it is kind of ugly and verbose
1103 * to have to futz around with the console properties on a
1104 * per-char basis. Both of these disadvantages aren't in my
1113 /* I don't quite understand why, but if the FILE*'s underlying file
1114 descriptor is in text mode, we seem to lose the first carriage
1117 setmode(fileno(stdin), _O_BINARY);
1118 hIn = GetStdHandle(STD_INPUT_HANDLE);
1119 GetConsoleMode(hIn, &mo);
1120 SetConsoleMode(hIn, mo & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
1122 * On Win9x, the first time you change the mode (as above) a
1123 * raw '\n' is inserted. Since enter maps to a raw '\r', and we
1124 * map this (below) to '\n', we can just ignore all *raw* '\n's.
1128 } while (c == '\n');
1130 /* Same as it ever was - revert back state of stdin. */
1131 SetConsoleMode(hIn, mo);
1132 setmode(fileno(stdin), _O_TEXT);
1136 return c=='\r' ? '\n' : c; /* slight paranoia about CR-LF */
1140 #endif /* no terminal driver */
1142 /* --------------------------------------------------------------------------
1143 * Interrupt handling:
1144 * ------------------------------------------------------------------------*/
1146 static Void installHandlers ( void ) { /* Install handlers for all fatal */
1147 /* signals except SIGINT and SIGBREAK*/
1149 /* SetConsoleCtrlHandler(consoleHandler,TRUE); */
1151 #if !DONT_PANIC && !DOS
1153 signal(SIGABRT,panic);
1156 signal(SIGBUS,panic);
1159 signal(SIGFPE,panic);
1162 signal(SIGHUP,panic);
1165 signal(SIGILL,panic);
1168 signal(SIGQUIT,panic);
1171 signal(SIGSEGV,panic);
1174 signal(SIGTERM,panic);
1176 #endif /* !DONT_PANIC && !DOS */
1179 /* --------------------------------------------------------------------------
1181 * ------------------------------------------------------------------------*/
1183 static Bool local startEdit(line,nm) /* Start editor on file name at */
1184 Int line; /* given line. Both name and line */
1185 String nm; { /* or just line may be zero */
1186 static char editorCmd[FILENAME_MAX+1];
1189 if (hugsEdit && *hugsEdit) { /* Check that editor configured */
1191 /* On a Mac, files have creator information, telling which program
1192 to launch to, so an editor named to the empty string "" is often
1194 if (hugsEdit) { /* Check that editor configured */
1196 Int n = FILENAME_MAX;
1197 String he = hugsEdit;
1198 String ec = editorCmd;
1199 String rd = NULL; /* Set to nonnull to redo ... */
1201 for (; n>0 && *he && *he!=' ' && *he!='%'; n--)
1202 *ec++ = *he++; /* Copy editor name to buffer */
1203 /* assuming filename ends at space */
1205 if (nm && line && n>1 && *he){ /* Name, line, and enough space */
1206 rd = ec; /* save, in case we don't find name*/
1207 while (n>0 && *he) {
1209 if (*++he=='d' && n>10) {
1210 sprintf(ec,"%d",line);
1213 else if (*he=='s' && (size_t)n>strlen(nm)) {
1218 else if (*he=='%' && n>1) {
1222 else /* Ignore % char if not followed */
1223 *ec = '\0'; /* by one of d, s, or %, */
1224 for (; *ec && n>0; n--)
1226 } /* ignore % followed by anything other than d, s, or % */
1227 else { /* Copy other characters across */
1236 if (rd) { /* If file name was not included */
1241 if (nm && line==0 && n>1) { /* Name, but no line ... */
1243 for (; n>0 && *nm; n--) /* ... just copy file name */
1247 *ec = '\0'; /* Add terminating null byte */
1250 ERRMSG(0) "Hugs is not configured to use an editor"
1255 WinExec(editorCmd, SW_SHOW);
1258 if (shellEsc(editorCmd))
1259 Printf("Warning: Editor terminated abnormally\n");
1264 Int shellEsc(s) /* run a shell command (or shell) */
1267 return macsystem(s);
1271 s = fromEnv("SHELL","/bin/sh");
1278 #if RISCOS /* RISCOS also needs a chdir() */
1279 int chdir(char *s) { /* RISCOS PRM p. 885 -- JBS */
1280 return os_swi2(OS_FSControl + XOS_Bit, 0, (int)s) != NULL;
1282 #elif defined HAVE_PBHSETVOLSYNC /* Macintosh */
1283 int chdir(const char *s) {
1286 wd.ioCompletion = 0;
1287 str = (char*)malloc(strlen(s) + 1);
1288 if (str == 0) return -1;
1290 wd.ioNamePtr = C2PStr(str);
1293 errno = PBHSetVolSync(&wd);
1304 /*---------------------------------------------------------------------------
1305 * Printf-related operations:
1306 *-------------------------------------------------------------------------*/
1308 #if !defined(HAVE_VSNPRINTF)
1309 int vsnprintf(buffer, count, fmt, ap)
1314 #if defined(HAVE__VSNPRINTF)
1315 return _vsnprintf(buffer, count, fmt, ap);
1320 #endif /* HAVE_VSNPRINTF */
1322 #if !defined(HAVE_SNPRINTF)
1323 int snprintf(char* buffer, int count, const char* fmt, ...) {
1324 #if defined(HAVE__VSNPRINTF)
1326 va_list ap; /* pointer into argument list */
1327 va_start(ap, fmt); /* make ap point to first arg after fmt */
1328 r = vsnprintf(buffer, count, fmt, ap);
1329 va_end(ap); /* clean up */
1335 #endif /* HAVE_SNPRINTF */
1337 /* --------------------------------------------------------------------------
1338 * Things to do with the argv/argc and the env
1339 * ------------------------------------------------------------------------*/
1341 int nh_argc ( void )
1346 int nh_argvb ( int argno, int offset )
1348 return (int)(prog_argv[argno][offset]);
1351 /* --------------------------------------------------------------------------
1352 * Machine dependent control:
1353 * ------------------------------------------------------------------------*/
1355 Void machdep(what) /* Handle machine specific */
1356 Int what; { /* initialisation etc.. */
1359 case POSTPREL: break;
1360 case PREPREL : installHandlers();
1364 case EXIT : normalTerminal();
1369 /*-------------------------------------------------------------------------*/