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 * Hugs 98 is Copyright (c) Mark P Jones, Alastair Reid and the Yale
10 * Haskell Group 1994-99, and is distributed as Open Source software
11 * under the Artistic License; see the file "Artistic" that is included
12 * in the distribution for details.
14 * $RCSfile: machdep.c,v $
16 * $Date: 1999/10/11 12:15:12 $
17 * ------------------------------------------------------------------------*/
22 #ifdef HAVE_SYS_TYPES_H
23 # include <sys/types.h>
30 # include <sys/param.h>
32 #ifdef HAVE_SYS_STAT_H
33 # include <sys/stat.h>
43 /* Windows/DOS include files */
47 #if defined HAVE_CONIO_H && ! HUGS_FOR_WINDOWS
64 extern HCURSOR HandCursor; /* Forward references to cursors */
65 extern HCURSOR GarbageCursor;
66 extern HCURSOR SaveCursor;
67 static void local DrawStatusLine Args((HWND));
72 extern unsigned _stklen = 8000; /* Allocate an 8k stack segment */
80 /* Macintosh include files */
103 /* --------------------------------------------------------------------------
104 * Prototypes for registry reading
105 * ------------------------------------------------------------------------*/
109 /* where have we hidden things in the registry? */
111 #define HScriptRoot ("SOFTWARE\\Haskell\\HaskellScript\\")
114 #define HugsRoot ("SOFTWARE\\Haskell\\Hugs\\" HUGS_VERSION "\\")
115 #define ProjectRoot ("SOFTWARE\\Haskell\\Hugs\\Projects\\")
117 static Bool local createKey Args((HKEY, String, PHKEY, REGSAM));
118 static Bool local queryValue Args((HKEY, String, String, LPDWORD, LPBYTE, DWORD));
119 static Bool local setValue Args((HKEY, String, String, DWORD, LPBYTE, DWORD));
120 static String local readRegString Args((HKEY, String, String, String));
121 static Int local readRegInt Args((String,Int));
122 static Bool local writeRegString Args((String,String));
123 static Bool local writeRegInt Args((String,Int));
125 static String local readRegChildStrings Args((HKEY, String, String, Char, String));
126 #endif /* USE_REGISTRY */
128 /* --------------------------------------------------------------------------
129 * Find information about a file:
130 * ------------------------------------------------------------------------*/
133 typedef struct { unsigned hi, lo; } Time;
134 #define timeChanged(now,thn) (now.hi!=thn.hi || now.lo!=thn.lo)
135 #define timeSet(var,tm) var.hi = tm.hi; var.lo = tm.lo
136 error timeEarlier not defined
139 #define timeChanged(now,thn) (now!=thn)
140 #define timeSet(var,tm) var = tm
141 #define timeEarlier(earlier,now) (earlier < now)
144 static Bool local readable Args((String));
145 static Void local getFileInfo Args((String, Time *, Long *));
147 static Void local getFileInfo(f,tm,sz) /* find time stamp and size of file*/
151 #if defined HAVE_SYS_STAT_H || defined HAVE_STAT_H || defined HAVE_UNIX_H
153 if (!stat(f,&scbuf)) {
154 if (tm) *tm = scbuf.st_mtime;
155 *sz = (Long)(scbuf.st_size);
160 #else /* normally just use stat() */
161 os_regset r; /* RISCOS PRM p.850 and p.837 */
162 r.r[0] = 17; /* Read catalogue, no path */
165 if(r.r[0] == 1 && (r.r[2] & 0xFFF00000) == 0xFFF00000) {
166 if (tm) tm->hi = r.r[2] & 0xFF; /* Load address (high byte) */
167 if (tm) tm->lo = r.r[3]; /* Execution address (low 4 bytes) */
168 } else { /* Not found, or not time-stamped */
169 if (tm) tm->hi = tm->lo = 0;
171 *sz = (Long)(r.r[0] == 1 ? r.r[4] : 0);
175 Void getFileSize ( String f, Long* sz )
177 getFileInfo ( f, NULL, sz );
180 #if defined HAVE_GETFINFO /* Mac971031 */
181 /* --------------------------------------------------------------------------
182 * Define a MacOS version of access():
183 * If the file is not accessible, -1 is returned and errno is set to
184 * the reason for the failure.
185 * If the file is accessible and the dummy is 0 (existence), 2 (write),
186 * or 4 (read), the return is 0.
187 * If the file is accessible, and the dummy is 1 (executable), then if
188 * the file is a program (of type 'APPL'), the return is 0, otherwise -1.
189 * Warnings: Use with caution. UNIX access do no translate to Macs.
190 * Check of write access is not implemented (same as read).
191 * ------------------------------------------------------------------------*/
193 int access(char *fileName, int dummy) {
197 errno = getfinfo(fileName, 0, &fi);
198 if (errno != 0) return -1; /* Check file accessible. */
200 /* Cases dummy = existence, read, write. */
201 if (dummy == 0 || dummy & 0x6) return 0;
203 /* Case dummy = executable. */
205 if (fi.fdType == 'APPL') return 0;
214 static Bool local readable(f) /* is f a regular, readable file */
216 #if DJGPP2 || defined HAVE_GETFINFO /* stat returns bogus mode bits on djgpp2 */
217 return (0 == access(f,4));
218 #elif defined HAVE_SYS_STAT_H || defined HAVE_STAT_H
220 //fprintf(stderr, "readable: %s\n", f );
221 return ( !stat(f,&scbuf)
222 && (scbuf.st_mode & S_IREAD) /* readable */
223 && (scbuf.st_mode & S_IFREG) /* regular file */
225 #elif defined HAVE_OS_SWI /* RISCOS specific */
226 os_regset r; /* RISCOS PRM p.850 -- JBS */
228 r.r[0] = 17; /* Read catalogue, no path */
231 return r.r[0] != 1; /* Does this check it's a regular file? ADR */
236 /* --------------------------------------------------------------------------
237 * Search for script files on the HUGS path:
238 * ------------------------------------------------------------------------*/
240 static String local hugsdir Args((Void));
242 static String local hscriptDir Args((Void));
244 //static String local RealPath Args((String));
245 static int local pathCmp Args((String, String));
246 static String local normPath Args((String));
247 static Void local searchChr Args((Int));
248 static Void local searchStr Args((String));
249 static Bool local tryEndings Args((String));
253 # define isSLASH(c) ((c)=='\\' || (c)=='/')
255 # define DLL_ENDING ".dll"
258 # define isSLASH(c) ((c)==SLASH)
260 /* Mac PEF (Preferred Executable Format) file */
261 # define DLL_ENDING ".pef"
264 # define isSLASH(c) ((c)==SLASH)
266 # define DLL_ENDING ".o"
269 static String local hugsdir() { /* directory containing lib/Prelude.hs */
271 /* In HaskellScript (Win32 only), we lookup InstallDir in the registry. */
272 static char dir[FILENAME_MAX+1] = "";
273 if (dir[0] == '\0') { /* not initialised yet */
274 String s = readRegString(HKEY_LOCAL_MACHINE,HugsRoot,"InstallDir",
281 #elif HAVE_GETMODULEFILENAME && !DOS
282 /* On Windows, we can find the binary we're running and it's
283 * conventional to put the libraries in the same place.
285 static char dir[FILENAME_MAX+1] = "";
286 if (dir[0] == '\0') { /* not initialised yet */
288 GetModuleFileName((HMODULE)0,dir,FILENAME_MAX+1);
289 if (dir[0] == '\0') { /* GetModuleFileName must have failed */
292 if (slash = strrchr(dir,SLASH)) { /* truncate after directory name */
298 /* On Unix systems, we can't find the binary we're running and
299 * the libraries may not be installed near the binary anyway.
300 * This forces us to use a hardwired path which is set at
301 * configuration time (--datadir=...).
308 static String local hscriptDir() { /* directory containing ?? what Daan? */
309 static char dir[FILENAME_MAX+1] = "";
310 if (dir[0] == '\0') { /* not initialised yet */
311 String s = readRegString(HKEY_LOCAL_MACHINE,HScriptRoot,"InstallDir","");
320 #if 0 /* apparently unused */
321 static String local RealPath(s) /* Find absolute pathname of file */
323 #if HAVE__FULLPATH /* eg DOS */
324 static char path[FILENAME_MAX+1];
325 _fullpath(path,s,FILENAME_MAX+1);
326 #elif HAVE_REALPATH /* eg Unix */
327 static char path[MAXPATHLEN+1];
330 static char path[FILENAME_MAX+1];
338 static int local pathCmp(p1,p2) /* Compare paths after normalisation */
341 #if HAVE__FULLPATH /* eg DOS */
342 static char path1[FILENAME_MAX+1];
343 static char path2[FILENAME_MAX+1];
344 _fullpath(path1,p1,FILENAME_MAX+1);
345 _fullpath(path2,p2,FILENAME_MAX+1);
346 #elif HAVE_REALPATH /* eg Unix */
347 static char path1[MAXPATHLEN+1];
348 static char path2[MAXPATHLEN+1];
352 static char path1[FILENAME_MAX+1];
353 static char path2[FILENAME_MAX+1];
357 #if CASE_INSENSITIVE_FILENAMES
361 return filenamecmp(path1,path2);
364 static String local normPath(s) /* Try, as much as possible, to normalize */
365 String s; { /* a pathname in some appropriate manner. */
366 #if PATH_CANONICALIZATION
367 String path = RealPath(s);
368 #if CASE_INSENSITIVE_FILENAMES
369 strlwr(path); /* and convert to lowercase */
372 #else /* ! PATH_CANONICALIZATION */
374 #endif /* ! PATH_CANONICALIZATION */
378 static String endings[] = { "", ".hi", ".hs", ".lhs", ".hsx", ".hash", 0 };
380 static String endings[] = { "", ".hi", ".hs", ".lhs", 0 };
382 static char searchBuf[FILENAME_MAX+1];
383 static Int searchPos;
385 #define searchReset(n) searchBuf[searchPos=(n)]='\0'
387 static Void local searchChr(c) /* Add single character to search buffer */
389 if (searchPos<FILENAME_MAX) {
390 searchBuf[searchPos++] = (char)c;
391 searchBuf[searchPos] = '\0';
395 static Void local searchStr(s) /* Add string to search buffer */
397 while (*s && searchPos<FILENAME_MAX)
398 searchBuf[searchPos++] = *s++;
399 searchBuf[searchPos] = '\0';
402 static Bool local tryEndings(s) /* Try each of the listed endings */
406 for (; endings[i]; ++i) {
407 Int save = searchPos;
408 searchStr(endings[i]);
409 if (readable(searchBuf))
420 /* scandir, June 98 Daan Leijen
421 searches the base directory and its direct subdirectories for a file
423 input: searchbuf contains SLASH terminated base directory
424 argument s contains the (base) filename
425 output: TRUE: searchBuf contains the full filename
426 FALSE: searchBuf is garbage, file not found
430 #ifdef HAVE_WINDOWS_H
432 static Bool scanSubDirs(s)
435 struct _finddata_t findInfo;
440 /* is it in the current directory ? */
441 if (tryEndings(s)) return TRUE;
446 /* initiate the search */
447 handle = _findfirst( searchBuf, &findInfo );
448 if (handle==-1) { errno = 0; return FALSE; }
450 /* search all subdirectories */
452 /* if we have a valid sub directory */
453 if (((findInfo.attrib & _A_SUBDIR) == _A_SUBDIR) &&
454 (findInfo.name[0] != '.')) {
456 searchStr(findInfo.name);
462 } while (_findnext( handle, &findInfo ) == 0);
464 _findclose( handle );
468 #elif defined(HAVE_FTW_H)
472 static char baseFile[FILENAME_MAX+1];
473 static char basePath[FILENAME_MAX+1];
474 static int basePathLen;
476 static int scanitem( const char* path,
477 const struct stat* statinfo,
480 if (info == FTW_D) { /* is it a directory */
484 if (tryEndings(baseFile)) {
491 static Bool scanSubDirs(s)
496 strcpy(basePath,searchBuf);
497 basePathLen = strlen(basePath);
499 /* is it in the current directory ? */
500 if (tryEndings(s)) return TRUE;
502 /* otherwise scan the subdirectories */
503 r = ftw( basePath, scanitem, 2 );
508 #endif /* HAVE_WINDOWS_H || HAVE_FTW_H */
509 #endif /* SEARCH_DIR */
511 String findPathname(along,nm) /* Look for a file along specified path */
512 String along; /* Return NULL if file does not exist */
514 /* AC, 1/21/99: modified to search hugsPath first, then projectPath */
515 String s = findMPathname(along,nm,hugsPath);
520 s = findMPathname(along,nm,projectPath);
523 #endif /* USE_REGISTRY */
524 return s ? s : normPath(searchBuf);
527 /* AC, 1/21/99: modified to pass in path to search explicitly */
528 String findMPathname(along,nm,path)/* Look for a file along specified path */
529 String along; /* If nonzero, a path prefix from along is */
530 String nm; /* used as the first prefix in the search. */
532 String pathpt = path;
535 if (along) { /* Was a path for an existing file given? */
538 for (; along[i]; i++) {
540 if (isSLASH(along[i]))
546 return normPath(searchBuf);
548 if (pathpt && *pathpt) { /* Otherwise, we look along the HUGSPATH */
551 Bool recurse = FALSE; /* DL: shall we recurse ? */
554 if (*pathpt!=PATHSEP) {
555 /* Pre-define one MPW-style "shell-variable" */
556 if (strncmp(pathpt,"{Hugs}",6)==0) {
557 searchStr(hugsdir());
561 /* And another - we ought to generalise this stuff */
562 else if (strncmp(pathpt,"{HScript}",9)==0) {
563 searchStr(hscriptDir());
568 searchChr(*pathpt++);
569 } while (*pathpt && *pathpt!=PATHSEP);
570 recurse = (pathpt[-1] == SLASH);
575 if (*pathpt==PATHSEP)
583 if (recurse ? scanSubDirs(nm) : tryEndings(nm)) {
584 return normPath(searchBuf);
587 if (tryEndings(nm)) {
588 return normPath(searchBuf);
594 searchReset(0); /* As a last resort, look for file in the current dir */
595 return (tryEndings(nm) ? normPath(searchBuf) : 0);
598 /* --------------------------------------------------------------------------
599 * New path handling stuff for the Combined System (tm)
600 * ------------------------------------------------------------------------*/
602 #define N_DEFAULT_LIBDIR 1000
603 char defaultLibDir[N_DEFAULT_LIBDIR];
605 /* Assumes that getcwd()++argv[0] is the absolute path to the
606 executable. Basically wrong.
608 void setDefaultLibDir ( String argv_0 )
611 if (argv_0[0] != SLASH) {
612 if (!getcwd(defaultLibDir,N_DEFAULT_LIBDIR-strlen(argv_0)-10)) {
613 ERRMSG(0) "Can't get current working directory"
616 i = strlen(defaultLibDir);
617 defaultLibDir[i++] = SLASH;
621 strcpy(&defaultLibDir[i],argv_0);
623 while (defaultLibDir[i] != SLASH) i--;
625 strcpy(&defaultLibDir[i], "lib");
626 /* fprintf ( stderr, "default lib dir = %s\n", defaultLibDir ); */
629 Bool findFilesForModule (
633 Bool* sAvail, Time* sTime, Long* sSize,
634 Bool* iAvail, Time* iTime, Long* iSize,
635 Bool* oAvail, Time* oTime, Long* oSize
638 /* Let the module name given be M.
639 For each path entry P,
640 a s(rc) file will be P/M.hs or P/M.lhs
641 an i(nterface) file will be P/M.hi
642 an o(bject) file will be P/M.o
643 If there is a s file or (both i and o files)
644 use P to fill in the path names.
645 Otherwise, move on to the next path entry.
646 If all path entries are exhausted, return False.
650 String peStart, peEnd;
651 String augdPath; /* .:defaultLibDir:hugsPath */
653 *path = *sExt = NULL;
654 *sAvail = *iAvail = *oAvail = FALSE;
655 *sSize = *iSize = *oSize = 0;
657 augdPath = malloc(4+strlen(defaultLibDir)+strlen(hugsPath));
659 internal("moduleNameToFileNames: malloc failed(2)");
661 augdPath[1] = PATHSEP;
663 strcat ( augdPath, defaultLibDir );
664 augdPath[2+strlen(defaultLibDir)] = PATHSEP;
665 augdPath[3+strlen(defaultLibDir)] = 0;
666 strcat(augdPath,hugsPath);
670 /* Advance peStart and peEnd very paranoically, giving up at
671 the first sign of mutancy in the path string.
673 if (peEnd >= augdPath && !(*peEnd)) { free(augdPath); return FALSE; }
676 while (*peEnd && *peEnd != PATHSEP) peEnd++;
678 /* Now peStart .. peEnd-1 bracket the next path element. */
679 nPath = peEnd-peStart;
680 if (nPath + strlen(modName) + 10 /*slush*/ > FILENAME_MAX) {
681 ERRMSG(0) "Hugs path \"%s\" contains excessively long component",
688 strncpy(searchBuf, peStart, nPath);
689 searchBuf[nPath] = 0;
690 if (nPath > 0 && !isSLASH(searchBuf[nPath-1]))
691 searchBuf[nPath++] = SLASH;
693 strcpy(searchBuf+nPath, modName);
694 nPath += strlen(modName);
696 /* searchBuf now holds 'P/M'. Try out the various endings. */
697 *path = *sExt = NULL;
698 *sAvail = *iAvail = *oAvail = FALSE;
699 *sSize = *iSize = *oSize = 0;
701 strcpy(searchBuf+nPath, DLL_ENDING);
702 if (readable(searchBuf)) {
704 getFileInfo(searchBuf, oTime, oSize);
707 strcpy(searchBuf+nPath, ".hi");
708 if (readable(searchBuf)) {
710 getFileInfo(searchBuf, iTime, iSize);
713 strcpy(searchBuf+nPath, ".hs");
714 if (readable(searchBuf)) {
717 getFileInfo(searchBuf, sTime, sSize);
720 strcpy(searchBuf+nPath, ".lhs");
721 if (readable(searchBuf)) {
724 getFileInfo(searchBuf, sTime, sSize);
730 if (*sAvail || (*oAvail && *iAvail)) {
731 nPath -= strlen(modName);
732 *path = malloc(nPath+1);
734 internal("moduleNameToFileNames: malloc failed(1)");
735 strncpy(*path, searchBuf, nPath);
746 /* --------------------------------------------------------------------------
747 * Substitute old value of path into empty entries in new path
748 * eg substPath("a:b:c::d:e","x:y:z") = "a:b:c:x:y:z:d:e"
749 * ------------------------------------------------------------------------*/
751 static String local substPath Args((String,String));
753 static String local substPath(new,sub) /* substitute sub path into new path*/
756 Bool substituted = FALSE; /* only allow one replacement */
757 Int maxlen = strlen(sub) + strlen(new); /* safe upper bound */
758 String r = (String) malloc(maxlen+1); /* result string */
759 String t = r; /* pointer into r */
760 String next = new; /* next uncopied char in new */
761 String start = next; /* start of last path component */
763 ERRMSG(0) "String storage space exhausted"
767 if (*next == PATHSEP || *next == '\0') {
768 if (!substituted && next == start) {
770 for(; *s != '\0'; ++s) {
777 } while ((*t++ = *next++) != '\0');
782 /* --------------------------------------------------------------------------
783 * Garbage collection notification:
784 * ------------------------------------------------------------------------*/
786 Bool gcMessages = FALSE; /* TRUE => print GC messages */
788 Void gcStarted() { /* Notify garbage collector start */
790 SaveCursor = SetCursor(GarbageCursor);
798 Void gcScanning() { /* Notify garbage collector scans */
805 Void gcRecovered(recovered) /* Notify garbage collection done */
808 Printf("%d}}",recovered);
812 SetCursor(SaveCursor);
816 Cell *CStackBase; /* Retain start of C control stack */
818 #if RISCOS /* Stack traversal for RISCOS */
820 /* Warning: The following code is specific to the Acorn ARM under RISCOS
821 (and C4). We must explicitly walk back through the stack frames, since
822 the stack is extended from the heap. (see PRM pp. 1757). gcCStack must
823 not be modified, since the offset '5' assumes that only v1 is used inside
824 this function. Hence we do all the real work in gcARM.
827 #define spreg 13 /* C3 has SP=R13 */
829 #define previousFrame(fp) ((int *)((fp)[-3]))
830 #define programCounter(fp) ((int *)((*(fp)-12) & ~0xFC000003))
831 #define isSubSPSP(w) (((w)&dontCare) == doCare)
832 #define doCare (0xE24DD000) /* SUB r13,r13,#0 */
833 #define dontCare (~0x00100FFF) /* S and # bits */
834 #define immediateArg(x) ( ((x)&0xFF) << (((x)&0xF00)>>7) )
836 static void gcARM(int *fp) {
837 int si = *programCounter(fp); /* Save instruction indicates how */
838 /* many registers in this frame */
840 if (si & (1<<0)) markWithoutMove(*regs--);
841 if (si & (1<<1)) markWithoutMove(*regs--);
842 if (si & (1<<2)) markWithoutMove(*regs--);
843 if (si & (1<<3)) markWithoutMove(*regs--);
844 if (si & (1<<4)) markWithoutMove(*regs--);
845 if (si & (1<<5)) markWithoutMove(*regs--);
846 if (si & (1<<6)) markWithoutMove(*regs--);
847 if (si & (1<<7)) markWithoutMove(*regs--);
848 if (si & (1<<8)) markWithoutMove(*regs--);
849 if (si & (1<<9)) markWithoutMove(*regs--);
850 if (previousFrame(fp)) {
851 /* The non-register stack space is for the previous frame is above
852 this fp, and not below the previous fp, because of the way stack
853 extension works. It seems the only way of discovering its size is
854 finding the SUB sp, sp, #? instruction by walking through the code
855 following the entry point.
857 int *oldpc = programCounter(previousFrame(fp));
859 for(i = 1; i < 6; ++i)
860 if(isSubSPSP(oldpc[i])) fsize += immediateArg(oldpc[i]) / 4;
861 for(i=1; i<=fsize; ++i)
862 markWithoutMove(fp[i]);
868 int *fp = 5 + &dummy;
871 fp = previousFrame(fp);
875 #else /* Garbage collection for standard stack machines */
877 Void gcCStack() { /* Garbage collect elements off */
878 Cell stackTop = NIL; /* C stack */
879 Cell *ptr = &stackTop;
881 if (((long)(ptr) - (long)(CStackBase))&1)
883 #elif STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
884 if (((long)(ptr) - (long)(CStackBase))&1)
887 if (((long)(ptr) - (long)(CStackBase))&3)
891 #define Blargh markWithoutMove(*ptr);
893 markWithoutMove((*ptr)/sizeof(Cell)); \
894 markWithoutMove(( (void*)(*ptr)-(void*)heapTopFst)/sizeof(Cell)); \
895 markWithoutMove(( (void*)(*ptr)-(void*)heapTopSnd)/sizeof(Cell))
898 #define StackGrowsDown { while (ptr<=CStackBase) { Blargh; ptr++; }; }
899 #define StackGrowsUp { while (ptr>=CStackBase) { Blargh; ptr--; }; }
900 #define GuessDirection if (ptr>CStackBase) StackGrowsUp else StackGrowsDown
902 #if STACK_DIRECTION > 0
904 #elif STACK_DIRECTION < 0
910 #if SIZEOF_INTP==4 && STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
911 ptr = (Cell *)((long)(&stackTop) + 2);
915 #undef StackGrowsDown
917 #undef GuessDirection
921 /* --------------------------------------------------------------------------
922 * Terminal dependent stuff:
923 * ------------------------------------------------------------------------*/
925 #if (HAVE_TERMIO_H | HAVE_SGTTY_H | HAVE_TERMIOS_H)
927 /* grab the varargs prototype for ioctl */
929 # include <sys/ioctl.h>
932 /* The order of these three tests is very important because
933 * some systems have more than one of the requisite header file
934 * but only one of them seems to work.
935 * Anyone changing the order of the tests should try enabling each of the
936 * three branches in turn and write down which ones work as well as which
937 * OS/compiler they're using.
939 * OS Compiler sgtty termio termios notes
940 * Linux 2.0.18 gcc 2.7.2 absent works works 1
943 * 1) On Linux, termio.h just #includes termios.h and sgtty.h is
944 * implemented using termios.h.
945 * sgtty.h is in /usr/include/bsd which is not on my standard include
946 * path. Adding it does no harm but you might as well use termios.
948 * reid-alastair@cs.yale.edu
953 typedef struct termios TermParams;
954 #define getTerminal(tp) tcgetattr(fileno(stdin), &tp)
955 #define setTerminal(tp) tcsetattr(fileno(stdin), TCSAFLUSH, &tp)
956 #define noEcho(tp) tp.c_lflag &= ~(ICANON | ECHO); \
963 typedef struct sgttyb TermParams;
964 #define getTerminal(tp) ioctl(fileno(stdin),TIOCGETP,&tp)
965 #define setTerminal(tp) ioctl(fileno(stdin),TIOCSETP,&tp)
967 #define noEcho(tp) tp.sg_flags |= RAW; tp.sg_flags &= (~ECHO);
969 #define noEcho(tp) tp.sg_flags |= CBREAK; tp.sg_flags &= (~ECHO);
975 typedef struct termio TermParams;
976 #define getTerminal(tp) ioctl(fileno(stdin),TCGETA,&tp)
977 #define setTerminal(tp) ioctl(fileno(stdin),TCSETAF,&tp)
978 #define noEcho(tp) tp.c_lflag &= ~(ICANON | ECHO); \
984 static Bool messedWithTerminal = FALSE;
985 static TermParams originalSettings;
987 Void normalTerminal() { /* restore terminal initial state */
988 if (messedWithTerminal)
989 setTerminal(originalSettings);
992 Void noechoTerminal() { /* set terminal into noecho mode */
995 if (!messedWithTerminal) {
996 getTerminal(originalSettings);
997 messedWithTerminal = TRUE;
999 getTerminal(settings);
1001 setTerminal(settings);
1004 Int getTerminalWidth() { /* determine width of terminal */
1006 #ifdef _M_UNIX /* SCO Unix 3.2.4 defines TIOCGWINSZ*/
1007 #include <sys/stream.h> /* Required by sys/ptem.h */
1008 #include <sys/ptem.h> /* Required to declare winsize */
1010 static struct winsize terminalSize;
1011 ioctl(fileno(stdout),TIOCGWINSZ,&terminalSize);
1012 return (terminalSize.ws_col==0)? 80 : terminalSize.ws_col;
1018 Int readTerminalChar() { /* read character from terminal */
1019 return getchar(); /* without echo, assuming that */
1020 } /* noechoTerminal() is active... */
1024 Int readTerminalChar() { /* read character from terminal */
1025 return getchar(); /* without echo, assuming that */
1026 } /* noechoTerminal() is active... */
1028 Int getTerminalWidth() {
1029 return console_options.ncols;
1032 Void normalTerminal() {
1033 csetmode(C_ECHO, stdin);
1036 Void noechoTerminal() {
1037 csetmode(C_NOECHO, stdin);
1040 #else /* no terminal driver - eg DOS, RISCOS */
1042 static Bool terminalEchoReqd = TRUE;
1044 Int getTerminalWidth() {
1047 (void) os_swi3r(OS_ReadModeVariable, -1, 1, 0, &dummy, &dummy, &width);
1054 Void normalTerminal() { /* restore terminal initial state */
1055 terminalEchoReqd = TRUE;
1058 Void noechoTerminal() { /* turn terminal echo on/off */
1059 terminalEchoReqd = FALSE;
1062 Int readTerminalChar() { /* read character from terminal */
1063 if (terminalEchoReqd) {
1067 return c=='\r' ? '\n' : c; /* slight paranoia about CR-LF */
1071 #endif /* no terminal driver */
1073 /* --------------------------------------------------------------------------
1074 * Interrupt handling:
1075 * ------------------------------------------------------------------------*/
1077 Bool broken = FALSE;
1078 static Bool breakReqd = FALSE;
1079 static sigProto(ignoreBreak);
1080 static Void local installHandlers Args((Void));
1082 Bool breakOn(reqd) /* set break trapping on if reqd, */
1083 Bool reqd; { /* or off otherwise, returning old */
1084 Bool old = breakReqd;
1088 if (broken) { /* repond to break signal received */
1089 broken = FALSE; /* whilst break trap disabled */
1090 sigRaise(breakHandler);
1093 #if HANDLERS_CANT_LONGJMP
1094 ctrlbrk(ignoreBreak);
1096 ctrlbrk(breakHandler);
1099 ctrlbrk(ignoreBreak);
1104 static sigHandler(ignoreBreak) { /* record but don't respond to break*/
1105 ctrlbrk(ignoreBreak); /* reinstall signal handler */
1106 /* redundant on BSD systems but essential */
1107 /* on POSIX and other systems */
1114 static sigProto(panic);
1115 static sigHandler(panic) { /* exit in a panic, on receipt of */
1116 everybody(EXIT); /* an unexpected signal */
1117 fprintf(stderr,"\nUnexpected signal\n");
1119 sigResume;/*NOTREACHED*/
1121 #endif /* !DONT_PANIC */
1123 static Void local installHandlers() { /* Install handlers for all fatal */
1124 /* signals except SIGINT and SIGBREAK*/
1125 #if !DONT_PANIC && !DOS
1127 signal(SIGABRT,panic);
1130 signal(SIGBUS,panic);
1133 signal(SIGFPE,panic);
1136 signal(SIGHUP,panic);
1139 signal(SIGILL,panic);
1142 signal(SIGQUIT,panic);
1145 signal(SIGSEGV,panic);
1148 signal(SIGTERM,panic);
1150 #endif /* !DONT_PANIC && !DOS */
1153 /* --------------------------------------------------------------------------
1155 * ------------------------------------------------------------------------*/
1157 static Bool local startEdit(line,nm) /* Start editor on file name at */
1158 Int line; /* given line. Both name and line */
1159 String nm; { /* or just line may be zero */
1160 static char editorCmd[FILENAME_MAX+1];
1163 if (hugsEdit && *hugsEdit) { /* Check that editor configured */
1165 /* On a Mac, files have creator information, telling which program
1166 to launch to, so an editor named to the empty string "" is often
1168 if (hugsEdit) { /* Check that editor configured */
1170 Int n = FILENAME_MAX;
1171 String he = hugsEdit;
1172 String ec = editorCmd;
1173 String rd = NULL; /* Set to nonnull to redo ... */
1175 for (; n>0 && *he && *he!=' '; n--)
1176 *ec++ = *he++; /* Copy editor name to buffer */
1177 /* assuming filename ends at space */
1179 if (nm && line && n>1 && *he){ /* Name, line, and enough space */
1180 rd = ec; /* save, in case we don't find name*/
1181 while (n>0 && *he) {
1183 if (*++he=='d' && n>10) {
1184 sprintf(ec,"%d",line);
1187 else if (*he=='s' && (size_t)n>strlen(nm)) {
1192 else if (*he=='%' && n>1) {
1196 else /* Ignore % char if not followed */
1197 *ec = '\0'; /* by one of d, s, or %, */
1198 for (; *ec && n>0; n--)
1200 } /* ignore % followed by anything other than d, s, or % */
1201 else { /* Copy other characters across */
1210 if (rd) { /* If file name was not included */
1215 if (nm && line==0 && n>1) { /* Name, but no line ... */
1217 for (; n>0 && *nm; n--) /* ... just copy file name */
1221 *ec = '\0'; /* Add terminating null byte */
1224 ERRMSG(0) "Hugs is not configured to use an editor"
1229 WinExec(editorCmd, SW_SHOW);
1232 if (shellEsc(editorCmd))
1233 Printf("Warning: Editor terminated abnormally\n");
1238 Int shellEsc(s) /* run a shell command (or shell) */
1241 return macsystem(s);
1245 s = fromEnv("SHELL","/bin/sh");
1252 #if RISCOS /* RISCOS also needs a chdir() */
1253 int chdir(char *s) { /* RISCOS PRM p. 885 -- JBS */
1254 return os_swi2(OS_FSControl + XOS_Bit, 0, (int)s) != NULL;
1256 #elif defined HAVE_PBHSETVOLSYNC /* Macintosh */
1257 int chdir(const char *s) {
1260 wd.ioCompletion = 0;
1261 str = (char*)malloc(strlen(s) + 1);
1262 if (str == 0) return -1;
1264 wd.ioNamePtr = C2PStr(str);
1267 errno = PBHSetVolSync(&wd);
1278 /*---------------------------------------------------------------------------
1279 * Printf-related operations:
1280 *-------------------------------------------------------------------------*/
1282 #if !defined(HAVE_VSNPRINTF)
1283 int vsnprintf(buffer, count, fmt, ap)
1288 #if defined(HAVE__VSNPRINTF)
1289 return _vsnprintf(buffer, count, fmt, ap);
1294 #endif /* HAVE_VSNPRINTF */
1296 #if !defined(HAVE_SNPRINTF)
1297 int snprintf(char* buffer, int count, const char* fmt, ...) {
1298 #if defined(HAVE__VSNPRINTF)
1300 va_list ap; /* pointer into argument list */
1301 va_start(ap, fmt); /* make ap point to first arg after fmt */
1302 r = vsnprintf(buffer, count, fmt, ap);
1303 va_end(ap); /* clean up */
1309 #endif /* HAVE_SNPRINTF */
1311 /* --------------------------------------------------------------------------
1312 * Read/write values from/to the registry
1314 * All reads are from either HUGS_CURRENT_USER\\hugs_ROOT\\key or
1315 * HUGS_LOCAL_MACHINE\\hugs_ROOT\\key. (Machine entry is only used if
1316 * user entry doesn't exist).
1318 * All writes are to HUGS_CURRENT_USER\\HugsRoot\\key
1319 * ------------------------------------------------------------------------*/
1323 #define HugsRoot ("SOFTWARE\\Haskell\\Hugs\\" HUGS_VERSION "\\")
1325 static Bool local createKey Args((HKEY, PHKEY, REGSAM));
1326 static Bool local queryValue Args((HKEY, String, LPDWORD, LPBYTE, DWORD));
1327 static Bool local setValue Args((HKEY, String, DWORD, LPBYTE, DWORD));
1329 static Bool local createKey(hKey, phRootKey, samDesired)
1332 REGSAM samDesired; {
1334 return RegCreateKeyEx(hKey, HugsRoot,
1335 0, "", REG_OPTION_NON_VOLATILE,
1336 samDesired, NULL, phRootKey, &dwDisp)
1340 static Bool local queryValue(hKey, regPath, var, type, buf, bufSize)
1349 if (!createKey(hKey, regPath, &hRootKey, KEY_READ)) {
1352 LONG res = RegQueryValueEx(hRootKey, var, NULL, type, buf, &bufSize);
1353 RegCloseKey(hRootKey);
1354 return (res == ERROR_SUCCESS);
1358 static Bool local setValue(hKey, regPath, var, type, buf, bufSize)
1367 if (!createKey(hKey, regPath, &hRootKey, KEY_WRITE)) {
1370 LONG res = RegSetValueEx(hRootKey, var, 0, type, buf, bufSize);
1371 RegCloseKey(hRootKey);
1372 return (res == ERROR_SUCCESS);
1376 static String local readRegString(key,regPath,var,def) /* read String from registry */
1381 static char buf[300];
1383 if (queryValue(key, regPath,var, &type, buf, sizeof(buf))
1384 && type == REG_SZ) {
1391 static Int local readRegInt(var, def) /* read Int from registry */
1397 if (queryValue(HKEY_CURRENT_USER, HugsRoot, var, &type,
1398 (LPBYTE)&buf, sizeof(buf))
1399 && type == REG_DWORD) {
1401 } else if (queryValue(HKEY_LOCAL_MACHINE, HugsRoot, var, &type,
1402 (LPBYTE)&buf, sizeof(buf))
1403 && type == REG_DWORD) {
1410 static Bool local writeRegString(var,val) /* write String to registry */
1416 return setValue(HKEY_CURRENT_USER, HugsRoot, var,
1417 REG_SZ, (LPBYTE)val, lstrlen(val)+1);
1420 static Bool local writeRegInt(var,val) /* write String to registry */
1423 return setValue(HKEY_CURRENT_USER, HugsRoot, var,
1424 REG_DWORD, (LPBYTE)&val, sizeof(val));
1427 #endif /* USE_REGISTRY */
1429 /* --------------------------------------------------------------------------
1430 * Machine dependent control:
1431 * ------------------------------------------------------------------------*/
1433 Void machdep(what) /* Handle machine specific */
1434 Int what; { /* initialisation etc.. */
1437 case INSTALL : installHandlers();
1441 case EXIT : normalTerminal();
1442 #if HUGS_FOR_WINDOWS
1444 DestroyWindow(hWndMain);
1446 SetCursor(LoadCursor(NULL,IDC_ARROW));
1452 /*-------------------------------------------------------------------------*/