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/05/26 10:14:33 $
18 * ------------------------------------------------------------------------*/
23 #ifdef HAVE_SYS_TYPES_H
24 # include <sys/types.h>
33 # include <sys/param.h>
37 #ifdef HAVE_SYS_STAT_H
38 # include <sys/stat.h>
48 /* Windows/DOS include files */
52 #if defined HAVE_CONIO_H
67 extern unsigned _stklen = 8000; /* Allocate an 8k stack segment */
75 /* Macintosh include files */
98 int allow_break_count = 0;
101 /* --------------------------------------------------------------------------
102 * Find information about a file:
103 * ------------------------------------------------------------------------*/
105 #include "machdep_time.h"
107 static Bool local readable ( String );
108 static Void local getFileInfo ( String, Time *, Long * );
110 static Void local getFileInfo(f,tm,sz) /* find time stamp and size of file*/
114 #if defined HAVE_SYS_STAT_H || defined HAVE_STAT_H || defined HAVE_UNIX_H
116 if (!stat(f,&scbuf)) {
117 if (tm) *tm = scbuf.st_mtime;
118 *sz = (Long)(scbuf.st_size);
123 #else /* normally just use stat() */
124 os_regset r; /* RISCOS PRM p.850 and p.837 */
125 r.r[0] = 17; /* Read catalogue, no path */
128 if(r.r[0] == 1 && (r.r[2] & 0xFFF00000) == 0xFFF00000) {
129 if (tm) tm->hi = r.r[2] & 0xFF; /* Load address (high byte) */
130 if (tm) tm->lo = r.r[3]; /* Execution address (low 4 bytes) */
131 } else { /* Not found, or not time-stamped */
132 if (tm) tm->hi = tm->lo = 0;
134 *sz = (Long)(r.r[0] == 1 ? r.r[4] : 0);
138 Void getFileSize ( String f, Long* sz )
140 getFileInfo ( f, NULL, sz );
143 #if defined HAVE_GETFINFO /* Mac971031 */
144 /* --------------------------------------------------------------------------
145 * Define a MacOS version of access():
146 * If the file is not accessible, -1 is returned and errno is set to
147 * the reason for the failure.
148 * If the file is accessible and the dummy is 0 (existence), 2 (write),
149 * or 4 (read), the return is 0.
150 * If the file is accessible, and the dummy is 1 (executable), then if
151 * the file is a program (of type 'APPL'), the return is 0, otherwise -1.
152 * Warnings: Use with caution. UNIX access do no translate to Macs.
153 * Check of write access is not implemented (same as read).
154 * ------------------------------------------------------------------------*/
156 int access(char *fileName, int dummy) {
160 errno = getfinfo(fileName, 0, &fi);
161 if (errno != 0) return -1; /* Check file accessible. */
163 /* Cases dummy = existence, read, write. */
164 if (dummy == 0 || dummy & 0x6) return 0;
166 /* Case dummy = executable. */
168 if (fi.fdType == 'APPL') return 0;
177 static Bool local readable(f) /* is f a regular, readable file */
179 #if DJGPP2 || defined HAVE_GETFINFO /* stat returns bogus mode bits on djgpp2 */
180 return (0 == access(f,4));
181 #elif defined HAVE_SYS_STAT_H || defined HAVE_STAT_H
183 /* fprintf(stderr, "readable: %s\n", f ); */
184 return ( !stat(f,&scbuf)
185 && (scbuf.st_mode & S_IREAD) /* readable */
186 && (scbuf.st_mode & S_IFREG) /* regular file */
188 #elif defined HAVE_OS_SWI /* RISCOS specific */
189 os_regset r; /* RISCOS PRM p.850 -- JBS */
191 r.r[0] = 17; /* Read catalogue, no path */
194 return r.r[0] != 1; /* Does this check it's a regular file? ADR */
199 /* --------------------------------------------------------------------------
200 * Search for script files on the HUGS path:
201 * ------------------------------------------------------------------------*/
203 static String local hugsdir ( Void );
205 static String local hscriptDir ( Void );
207 static int local pathCmp ( String, String );
208 static String local normPath ( String );
209 static Void local searchChr ( Int );
210 static Void local searchStr ( String );
211 static Bool local tryEndings ( String );
213 #if (DOS_FILENAMES || __CYGWIN32__)
215 # define SLASH_STR "/"
216 # define isSLASH(c) ((c)=='\\' || (c)=='/')
218 # define PATHSEP_STR ";"
219 # define DLL_ENDING ".u_o"
222 # define isSLASH(c) ((c)==SLASH)
224 # define PATHSEP_STR ";"
225 /* Mac PEF (Preferred Executable Format) file */
226 # define DLL_ENDING ".pef"
229 # define SLASH_STR "/"
230 # define isSLASH(c) ((c)==SLASH)
232 # define PATHSEP_STR ":"
233 # define DLL_ENDING ".u_o"
236 static String local hugsdir() { /* directory containing lib/Prelude.hs */
238 /* In HaskellScript (Win32 only), we lookup InstallDir in the registry. */
239 static char dir[FILENAME_MAX+1] = "";
240 if (dir[0] == '\0') { /* not initialised yet */
241 String s = readRegString(HKEY_LOCAL_MACHINE,HugsRoot,"InstallDir",
248 #elif HAVE_GETMODULEFILENAME && !DOS && !__CYGWIN32__
249 /* On Windows, we can find the binary we're running and it's
250 * conventional to put the libraries in the same place.
252 static char dir[FILENAME_MAX+1] = "";
253 if (dir[0] == '\0') { /* not initialised yet */
255 GetModuleFileName((HMODULE)0,dir,FILENAME_MAX+1);
256 if (dir[0] == '\0') { /* GetModuleFileName must have failed */
259 slash = strrchr(dir,SLASH);
260 if (slash) { /* truncate after directory name */
266 /* On Unix systems, we can't find the binary we're running and
267 * the libraries may not be installed near the binary anyway.
268 * This forces us to use a hardwired path which is set at
269 * configuration time (--datadir=...).
276 static String local hscriptDir() { /* Directory containing hscript.dll */
277 static char dir[FILENAME_MAX+1] = "";
278 if (dir[0] == '\0') { /* not initialised yet */
279 String s = readRegString(HKEY_LOCAL_MACHINE,HScriptRoot,"InstallDir","");
289 static String local normPath(s) /* Try, as much as possible, to normalize */
290 String s; { /* a pathname in some appropriate manner. */
291 #if PATH_CANONICALIZATION
292 String path = RealPath(s);
293 #if CASE_INSENSITIVE_FILENAMES
294 strlwr(path); /* and convert to lowercase */
297 #else /* ! PATH_CANONICALIZATION */
299 #endif /* ! PATH_CANONICALIZATION */
303 static String endings[] = { "", ".u_hi", ".hs", ".lhs", ".hsx", ".hash", 0 };
305 static String endings[] = { "", ".u_hi", ".hs", ".lhs", 0 };
307 static char searchBuf[FILENAME_MAX+1];
308 static Int searchPos;
310 #define searchReset(n) searchBuf[searchPos=(n)]='\0'
312 static Void local searchChr(c) /* Add single character to search buffer */
314 if (searchPos<FILENAME_MAX) {
315 searchBuf[searchPos++] = (char)c;
316 searchBuf[searchPos] = '\0';
320 static Void local searchStr(s) /* Add string to search buffer */
322 while (*s && searchPos<FILENAME_MAX)
323 searchBuf[searchPos++] = *s++;
324 searchBuf[searchPos] = '\0';
327 static Bool local tryEndings(s) /* Try each of the listed endings */
331 for (; endings[i]; ++i) {
332 Int save = searchPos;
333 searchStr(endings[i]);
334 if (readable(searchBuf))
345 /* scandir, June 98 Daan Leijen
346 searches the base directory and its direct subdirectories for a file
348 input: searchbuf contains SLASH terminated base directory
349 argument s contains the (base) filename
350 output: TRUE: searchBuf contains the full filename
351 FALSE: searchBuf is garbage, file not found
355 #ifdef HAVE_WINDOWS_H
357 static Bool scanSubDirs(s)
360 struct _finddata_t findInfo;
365 /* is it in the current directory ? */
366 if (tryEndings(s)) return TRUE;
371 /* initiate the search */
372 handle = _findfirst( searchBuf, &findInfo );
373 if (handle==-1) { errno = 0; return FALSE; }
375 /* search all subdirectories */
377 /* if we have a valid sub directory */
378 if (((findInfo.attrib & _A_SUBDIR) == _A_SUBDIR) &&
379 (findInfo.name[0] != '.')) {
381 searchStr(findInfo.name);
387 } while (_findnext( handle, &findInfo ) == 0);
389 _findclose( handle );
393 #elif defined(HAVE_FTW_H)
397 static char baseFile[FILENAME_MAX+1];
398 static char basePath[FILENAME_MAX+1];
399 static int basePathLen;
401 static int scanitem( const char* path,
402 const struct stat* statinfo,
405 if (info == FTW_D) { /* is it a directory */
409 if (tryEndings(baseFile)) {
416 static Bool scanSubDirs(s)
421 strcpy(basePath,searchBuf);
422 basePathLen = strlen(basePath);
424 /* is it in the current directory ? */
425 if (tryEndings(s)) return TRUE;
427 /* otherwise scan the subdirectories */
428 r = ftw( basePath, scanitem, 2 );
433 #endif /* HAVE_WINDOWS_H || HAVE_FTW_H */
434 #endif /* SEARCH_DIR */
436 String findPathname(along,nm) /* Look for a file along specified path */
437 String along; /* Return NULL if file does not exist */
439 /* AC, 1/21/99: modified to search hugsPath first, then projectPath */
440 String s = findMPathname(along,nm,hugsPath);
441 return s ? s : normPath(searchBuf);
444 /* AC, 1/21/99: modified to pass in path to search explicitly */
445 String findMPathname(along,nm,path)/* Look for a file along specified path */
446 String along; /* If nonzero, a path prefix from along is */
447 String nm; /* used as the first prefix in the search. */
449 String pathpt = path;
452 if (along) { /* Was a path for an existing file given? */
455 for (; along[i]; i++) {
457 if (isSLASH(along[i]))
463 return normPath(searchBuf);
465 if (pathpt && *pathpt) { /* Otherwise, we look along the HUGSPATH */
468 Bool recurse = FALSE; /* DL: shall we recurse ? */
471 if (*pathpt!=PATHSEP) {
472 /* Pre-define one MPW-style "shell-variable" */
473 if (strncmp(pathpt,"{Hugs}",6)==0) {
474 searchStr(hugsdir());
478 /* And another - we ought to generalise this stuff */
479 else if (strncmp(pathpt,"{HScript}",9)==0) {
480 searchStr(hscriptDir());
485 searchChr(*pathpt++);
486 } while (*pathpt && *pathpt!=PATHSEP);
487 recurse = (pathpt[-1] == SLASH);
492 if (*pathpt==PATHSEP)
500 if (recurse ? scanSubDirs(nm) : tryEndings(nm)) {
501 return normPath(searchBuf);
504 if (tryEndings(nm)) {
505 return normPath(searchBuf);
511 searchReset(0); /* As a last resort, look for file in the current dir */
512 return (tryEndings(nm) ? normPath(searchBuf) : 0);
515 /* --------------------------------------------------------------------------
516 * New path handling stuff for the Combined System (tm)
517 * ------------------------------------------------------------------------*/
519 char installDir[N_INSTALLDIR];
521 /* Sets installDir to $STGHUGSDIR, and ensures there is a trailing
524 void setInstallDir ( String argv_0 )
527 char* r = getenv("STGHUGSDIR");
530 "%s: installation error: environment variable STGHUGSDIR is not set.\n",
533 "%s: pls set it to be the directory where STGHugs98 is installed.\n\n",
539 if (strlen(r) > N_INSTALLDIR-30 ) {
541 "%s: environment variable STGHUGSDIR is suspiciously long; pls remedy\n\n",
546 strcpy ( installDir, r );
547 i = strlen(installDir);
548 if (installDir[i-1] != SLASH) installDir[i++] = SLASH;
553 Bool findFilesForModule (
557 Bool* sAvail, Time* sTime, Long* sSize,
558 Bool* oiAvail, Time* oiTime, Long* oSize, Long* iSize
561 /* Let the module name given be M.
562 For each path entry P,
563 a s(rc) file will be P/M.hs or P/M.lhs
564 an i(nterface) file will be P/M.hi
565 an o(bject) file will be P/M.o
566 If there is a s file or (both i and o files)
567 use P to fill in the path names.
568 Otherwise, move on to the next path entry.
569 If all path entries are exhausted, return False.
571 If in standalone, only look for (and succeed for) source modules.
572 Caller free()s path. sExt is statically allocated.
573 srcExt is only set if a valid source file is found.
577 String peStart, peEnd;
578 String augdPath; /* .:hugsPath:installDir/../lib/std:installDir/lib */
582 *path = *sExt = NULL;
583 *sAvail = *oiAvail = oAvail = iAvail = FALSE;
584 *sSize = *oSize = *iSize = 0;
586 augdPath = malloc( 2*(10+3+strlen(installDir))
587 +strlen(hugsPath) +50/*paranoia*/);
589 internal("moduleNameToFileNames: malloc failed(2)");
594 strcat(augdPath, installDir);
595 strcat(augdPath, "..");
596 strcat(augdPath, SLASH_STR);
597 strcat(augdPath, "lib");
598 strcat(augdPath, SLASH_STR);
599 strcat(augdPath, "std");
600 strcat(augdPath, PATHSEP_STR);
603 strcat(augdPath, installDir);
604 strcat(augdPath, "lib");
605 strcat(augdPath, PATHSEP_STR);
607 /* these two were previously before the above `if' */
608 strcat(augdPath, ".");
609 strcat(augdPath, PATHSEP_STR);
611 strcat(augdPath, hugsPath);
612 strcat(augdPath, PATHSEP_STR);
614 /* fprintf ( stderr, "augdpath = `%s'\n", augdPath ); */
618 /* Advance peStart and peEnd very paranoically, giving up at
619 the first sign of mutancy in the path string.
621 if (peEnd >= augdPath && !(*peEnd)) { free(augdPath); return FALSE; }
624 while (*peEnd && *peEnd != PATHSEP) peEnd++;
626 /* Now peStart .. peEnd-1 bracket the next path element. */
627 nPath = peEnd-peStart;
628 if (nPath + strlen(modName) + 10 /*slush*/ > FILENAME_MAX) {
629 ERRMSG(0) "Hugs path \"%s\" contains excessively long component",
636 strncpy(searchBuf, peStart, nPath);
637 searchBuf[nPath] = 0;
638 if (nPath > 0 && !isSLASH(searchBuf[nPath-1]))
639 searchBuf[nPath++] = SLASH;
641 strcpy(searchBuf+nPath, modName);
642 nPath += strlen(modName);
644 /* searchBuf now holds 'P/M'. Try out the various endings. */
645 *path = *sExt = NULL;
646 *sAvail = *oiAvail = oAvail = iAvail = FALSE;
647 *sSize = *oSize = *iSize = 0;
650 strcpy(searchBuf+nPath, DLL_ENDING);
651 if (readable(searchBuf)) {
653 getFileInfo(searchBuf, &oTime, oSize);
655 strcpy(searchBuf+nPath, HI_ENDING);
656 if (readable(searchBuf)) {
658 getFileInfo(searchBuf, &iTime, iSize);
660 if (oAvail && iAvail) {
662 *oiTime = whicheverIsLater ( oTime, iTime );
666 strcpy(searchBuf+nPath, ".hs");
667 if (readable(searchBuf)) {
670 getFileInfo(searchBuf, sTime, sSize);
673 strcpy(searchBuf+nPath, ".lhs");
674 if (readable(searchBuf)) {
677 getFileInfo(searchBuf, sTime, sSize);
683 if (*sAvail || *oiAvail) {
684 nPath -= strlen(modName);
685 *path = malloc(nPath+1);
687 internal("moduleNameToFileNames: malloc failed(1)");
688 strncpy(*path, searchBuf, nPath);
699 /* If the primaryObjectName is (eg)
701 and the extraFileName is (eg)
703 and DLL_ENDING is set to .o
705 /foo/bar/swampy_cbits.o
706 and set *extraFileSize to its size, or -1 if not avail
708 String getExtraObjectInfo ( String primaryObjectName,
709 String extraFileName,
716 Int i = strlen(primaryObjectName)-1;
717 while (i >= 0 && primaryObjectName[i] != SLASH) i--;
718 if (i == -1) return extraFileName;
720 xtra = malloc ( i+3+strlen(extraFileName)+strlen(DLL_ENDING) );
721 if (!xtra) internal("deriveExtraObjectName: malloc failed");
722 strncpy ( xtra, primaryObjectName, i );
724 strcat ( xtra, extraFileName );
725 strcat ( xtra, DLL_ENDING );
728 if (readable(xtra)) {
729 getFileInfo ( xtra, &xTime, &xSize );
730 *extraFileSize = xSize;
736 /* --------------------------------------------------------------------------
737 * Substitute old value of path into empty entries in new path
738 * eg substPath("a:b:c::d:e","x:y:z") = "a:b:c:x:y:z:d:e"
739 * ------------------------------------------------------------------------*/
741 static String local substPath ( String,String );
743 static String local substPath(new,sub) /* substitute sub path into new path*/
746 Bool substituted = FALSE; /* only allow one replacement */
747 Int maxlen = strlen(sub) + strlen(new); /* safe upper bound */
748 String r = (String) malloc(maxlen+1); /* result string */
749 String t = r; /* pointer into r */
750 String next = new; /* next uncopied char in new */
751 String start = next; /* start of last path component */
753 ERRMSG(0) "String storage space exhausted"
757 if (*next == PATHSEP || *next == '\0') {
758 if (!substituted && next == start) {
760 for(; *s != '\0'; ++s) {
767 } while ((*t++ = *next++) != '\0');
772 /* --------------------------------------------------------------------------
773 * Garbage collection notification:
774 * ------------------------------------------------------------------------*/
776 Bool gcMessages = FALSE; /* TRUE => print GC messages */
778 Void gcStarted() { /* Notify garbage collector start */
785 Void gcScanning() { /* Notify garbage collector scans */
792 Void gcRecovered(recovered) /* Notify garbage collection done */
795 Printf("%d}}",recovered);
800 Cell *CStackBase; /* Retain start of C control stack */
802 #if RISCOS /* Stack traversal for RISCOS */
804 /* Warning: The following code is specific to the Acorn ARM under RISCOS
805 (and C4). We must explicitly walk back through the stack frames, since
806 the stack is extended from the heap. (see PRM pp. 1757). gcCStack must
807 not be modified, since the offset '5' assumes that only v1 is used inside
808 this function. Hence we do all the real work in gcARM.
811 #define spreg 13 /* C3 has SP=R13 */
813 #define previousFrame(fp) ((int *)((fp)[-3]))
814 #define programCounter(fp) ((int *)((*(fp)-12) & ~0xFC000003))
815 #define isSubSPSP(w) (((w)&dontCare) == doCare)
816 #define doCare (0xE24DD000) /* SUB r13,r13,#0 */
817 #define dontCare (~0x00100FFF) /* S and # bits */
818 #define immediateArg(x) ( ((x)&0xFF) << (((x)&0xF00)>>7) )
820 static void gcARM(int *fp) {
821 int si = *programCounter(fp); /* Save instruction indicates how */
822 /* many registers in this frame */
824 if (si & (1<<0)) markWithoutMove(*regs--);
825 if (si & (1<<1)) markWithoutMove(*regs--);
826 if (si & (1<<2)) markWithoutMove(*regs--);
827 if (si & (1<<3)) markWithoutMove(*regs--);
828 if (si & (1<<4)) markWithoutMove(*regs--);
829 if (si & (1<<5)) markWithoutMove(*regs--);
830 if (si & (1<<6)) markWithoutMove(*regs--);
831 if (si & (1<<7)) markWithoutMove(*regs--);
832 if (si & (1<<8)) markWithoutMove(*regs--);
833 if (si & (1<<9)) markWithoutMove(*regs--);
834 if (previousFrame(fp)) {
835 /* The non-register stack space is for the previous frame is above
836 this fp, and not below the previous fp, because of the way stack
837 extension works. It seems the only way of discovering its size is
838 finding the SUB sp, sp, #? instruction by walking through the code
839 following the entry point.
841 int *oldpc = programCounter(previousFrame(fp));
843 for(i = 1; i < 6; ++i)
844 if(isSubSPSP(oldpc[i])) fsize += immediateArg(oldpc[i]) / 4;
845 for(i=1; i<=fsize; ++i)
846 markWithoutMove(fp[i]);
852 int *fp = 5 + &dummy;
855 fp = previousFrame(fp);
859 #else /* Garbage collection for standard stack machines */
861 Void gcCStack() { /* Garbage collect elements off */
862 Cell stackTop = NIL; /* C stack */
863 Cell *ptr = &stackTop;
864 #if SIZEOF_VOID_P == 2
865 if (((long)(ptr) - (long)(CStackBase))&1)
867 #elif STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
868 if (((long)(ptr) - (long)(CStackBase))&1)
871 if (((long)(ptr) - (long)(CStackBase))&3)
875 #define Blargh mark(*ptr);
877 markWithoutMove((*ptr)/sizeof(Cell)); \
878 markWithoutMove(( (void*)(*ptr)-(void*)heapTopFst)/sizeof(Cell)); \
879 markWithoutMove(( (void*)(*ptr)-(void*)heapTopSnd)/sizeof(Cell))
882 #define StackGrowsDown { while (ptr<=CStackBase) { Blargh; ptr++; }; }
883 #define StackGrowsUp { while (ptr>=CStackBase) { Blargh; ptr--; }; }
884 #define GuessDirection if (ptr>CStackBase) StackGrowsUp else StackGrowsDown
886 #if STACK_DIRECTION > 0
888 #elif STACK_DIRECTION < 0
894 #if SIZEOF_VOID_P==4 && STACK_ALIGNMENT == 2 /* eg Macintosh 68000 */
895 ptr = (Cell *)((long)(&stackTop) + 2);
899 #undef StackGrowsDown
901 #undef GuessDirection
905 /* --------------------------------------------------------------------------
906 * Interrupt handling:
907 * ------------------------------------------------------------------------*/
909 static Void installHandlers ( void ) { /* Install handlers for all fatal */
910 /* signals except SIGINT and SIGBREAK*/
912 /* SetConsoleCtrlHandler(consoleHandler,TRUE); */
914 #if !DONT_PANIC && !DOS
916 signal(SIGABRT,panic);
919 signal(SIGBUS,panic);
922 signal(SIGFPE,panic);
925 signal(SIGHUP,panic);
928 signal(SIGILL,panic);
931 signal(SIGQUIT,panic);
934 signal(SIGSEGV,panic);
937 signal(SIGTERM,panic);
939 #endif /* !DONT_PANIC && !DOS */
942 /* --------------------------------------------------------------------------
944 * ------------------------------------------------------------------------*/
946 static Bool local startEdit(line,nm) /* Start editor on file name at */
947 Int line; /* given line. Both name and line */
948 String nm; { /* or just line may be zero */
949 static char editorCmd[FILENAME_MAX+1];
952 if (hugsEdit && *hugsEdit) { /* Check that editor configured */
954 /* On a Mac, files have creator information, telling which program
955 to launch to, so an editor named to the empty string "" is often
957 if (hugsEdit) { /* Check that editor configured */
959 Int n = FILENAME_MAX;
960 String he = hugsEdit;
961 String ec = editorCmd;
962 String rd = NULL; /* Set to nonnull to redo ... */
964 for (; n>0 && *he && *he!=' ' && *he!='%'; n--)
965 *ec++ = *he++; /* Copy editor name to buffer */
966 /* assuming filename ends at space */
968 if (nm && line && n>1 && *he){ /* Name, line, and enough space */
969 rd = ec; /* save, in case we don't find name*/
972 if (*++he=='d' && n>10) {
973 sprintf(ec,"%d",line);
976 else if (*he=='s' && (size_t)n>strlen(nm)) {
981 else if (*he=='%' && n>1) {
985 else /* Ignore % char if not followed */
986 *ec = '\0'; /* by one of d, s, or %, */
987 for (; *ec && n>0; n--)
989 } /* ignore % followed by anything other than d, s, or % */
990 else { /* Copy other characters across */
999 if (rd) { /* If file name was not included */
1004 if (nm && line==0 && n>1) { /* Name, but no line ... */
1006 for (; n>0 && *nm; n--) /* ... just copy file name */
1010 *ec = '\0'; /* Add terminating null byte */
1013 ERRMSG(0) "Hugs is not configured to use an editor"
1018 WinExec(editorCmd, SW_SHOW);
1021 if (shellEsc(editorCmd))
1022 Printf("Warning: Editor terminated abnormally\n");
1027 Int shellEsc(s) /* run a shell command (or shell) */
1030 return macsystem(s);
1034 s = fromEnv("SHELL","/bin/sh");
1041 #if RISCOS /* RISCOS also needs a chdir() */
1042 int chdir(char *s) { /* RISCOS PRM p. 885 -- JBS */
1043 return os_swi2(OS_FSControl + XOS_Bit, 0, (int)s) != NULL;
1045 #elif defined HAVE_PBHSETVOLSYNC /* Macintosh */
1046 int chdir(const char *s) {
1049 wd.ioCompletion = 0;
1050 str = (char*)malloc(strlen(s) + 1);
1051 if (str == 0) return -1;
1053 wd.ioNamePtr = C2PStr(str);
1056 errno = PBHSetVolSync(&wd);
1067 /* --------------------------------------------------------------------------
1068 * Things to do with the argv/argc and the env
1069 * ------------------------------------------------------------------------*/
1071 int nh_argc ( void )
1076 int nh_argvb ( int argno, int offset )
1078 return (int)(prog_argv[argno][offset]);
1081 /* --------------------------------------------------------------------------
1082 * Machine dependent control:
1083 * ------------------------------------------------------------------------*/
1085 Void machdep(what) /* Handle machine specific */
1086 Int what; { /* initialisation etc.. */
1089 case POSTPREL: break;
1090 case PREPREL : installHandlers();
1099 /*-------------------------------------------------------------------------*/