/*
 * Copyright (c) 1991-1993 by Xerox Corporation.  All rights reserved.
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to copy this garbage collector for any purpose,
 * provided the above notices are retained on all copies.
 */
# include "gc←private.h"
# include <stdio.h>
# include <signal.h>

/* Blatantly OS dependent routines, except for those that are related 	*/
/* dynamic loading.							*/

# ifdef SUNOS5
# undef sigmask
# endif

/* Disable and enable signals during nontrivial allocations	*/

# ifdef OS2

# define INCL←DOSEXCEPTIONS
# define INCL←DOSPROCESS
# define INCL←DOSERRORS
# define INCL←DOSMODULEMGR
# include <os2.h>

/* A kludge to get around what appears to be a header file bug */
# ifndef WORD
#   define WORD unsigned short
# endif
# ifndef DWORD
#   define DWORD unsigned long
# endif

# define EXE386 1
# include <newexe.h>
# include <exe386.h>

void GC←disable←signals(void)
{
    ULONG nest;
    
    DosEnterMustComplete(&nest);
    if (nest != 1) ABORT("nested GC←disable←signals");
}

void GC←enable←signals(void)
{
    ULONG nest;
    
    DosExitMustComplete(&nest);
    if (nest != 0) ABORT("GC←enable←signals");
}


# else

#  ifndef PCR

#   ifdef sigmask
	/* Use the traditional BSD interface */
#	define SIGSET←T int
#	define SIG←DEL(set, signal) (set) &= ~(sigmask(signal))
#	define SIG←FILL(set)  (set) = 0x7fffffff
    	  /* Setting the leading bit appears to provoke a bug in some	*/
    	  /* longjmp implementations.  Most systems appear not to have	*/
    	  /* a signal 32.						*/
#	define SIGSETMASK(old, new) (old) = sigsetmask(new)
#   else
	/* Use POSIX/SYSV interface	*/
#	define SIGSET←T sigset←t
#	define SIG←DEL(set, signal) sigdelset(&(set), (signal))
#	define SIG←FILL(set) sigfillset(&set)
#	define SIGSETMASK(old, new) sigprocmask(SIG←SETMASK, &(new), &(old))
#   endif

static bool mask←initialized = FALSE;

static SIGSET←T new←mask;

static SIGSET←T old←mask;

static SIGSET←T dummy;

void GC←disable←signals()
{
    if (!mask←initialized) {
    	SIG←FILL(new←mask);

	SIG←DEL(new←mask, SIGSEGV);
	SIG←DEL(new←mask, SIGILL);
	SIG←DEL(new←mask, SIGQUIT);
#	ifdef SIGBUS
	    SIG←DEL(new←mask, SIGBUS);
#	endif
#	ifdef SIGIOT
	    SIG←DEL(new←mask, SIGIOT);
#	endif
#	ifdef SIGEMT
	    SIG←DEL(new←mask, SIGEMT);
#	endif
#	ifdef SIGTRAP
	    SIG←DEL(new←mask, SIGTRAP);
#	endif 
	mask←initialized = TRUE;
    }     
    SIGSETMASK(old←mask,new←mask);
}

void GC←enable←signals()
{
    SIGSETMASK(dummy,old←mask);
}

#  endif  /* !PCR */

# endif /*!OS/2 */

/*
 * Find the base of the stack.
 * Used only in single-threaded environment.
 * With threads, GC←mark←roots needs to know how to do this.
 * Called with allocator lock held.
 */

# ifdef OS2

ptr←t GC←get←stack←base()
{
    PTIB ptib;
    PPIB ppib;
    
    if (DosGetInfoBlocks(&ptib, &ppib) != NO←ERROR) {
    	GC←err←printf0("DosGetInfoBlocks failed\n");
    	ABORT("DosGetInfoBlocks failed\n");
    }
    return((ptr←t)(ptib -> tib←pstacklimit));
}

# else

# if !defined(THREADS) && !defined(STACKBOTTOM) && defined(HEURISTIC2)
  /* Some tools to implement HEURISTIC2	*/
#   define MIN←PAGE←SIZE 256	/* Smallest conceivable page size, bytes */
#   include <setjmp.h>
    /* static */ jmp←buf GC←jmp←buf;
    
    /*ARGSUSED*/
    void GC←fault←handler(sig)
    int sig;
    {
        longjmp(GC←jmp←buf, 1);
    }
# endif

ptr←t GC←get←stack←base()
{
    word dummy;
    static ptr←t result;
    		/* Needs to be static, since otherwise it may not be	*/
    		/* preserved across the longjmp.  Can safely be 	*/
    		/* static since it's only called once, with the		*/
    		/* allocation lock held.				*/
#   ifdef ←←STDC←←
	typedef void (*handler)(int);
#   else
	typedef void (*handler)();
#   endif
#   ifdef HEURISTIC2
      static handler old←segv←handler, old←bus←handler;
      		/* See above for static declaration.			*/
#   endif
#   define STACKBOTTOM←ALIGNMENT←M1 0xffffff

#   ifdef STACKBOTTOM
	return(STACKBOTTOM);
#   else
#	ifdef HEURISTIC1
#	   ifdef STACK←GROWS←DOWN
	     result = (ptr←t)((((word)(&dummy))
	     		       + STACKBOTTOM←ALIGNMENT←M1)
			      & ~STACKBOTTOM←ALIGNMENT←M1);
#	   else
	     result = (ptr←t)(((word)(&dummy))
			      & ~STACKBOTTOM←ALIGNMENT←M1);
#	   endif
#	endif /* HEURISTIC1 */
#	ifdef HEURISTIC2
	   old←segv←handler = signal(SIGSEGV, GC←fault←handler);
#	   ifdef SIGBUS
	     old←bus←handler = signal(SIGBUS, GC←fault←handler);
#	   endif
	   if (setjmp(GC←jmp←buf) == 0) {
	     result = (ptr←t)(((word)(&dummy))
			      & ~(MIN←PAGE←SIZE-1));
	     for (;;) {
#	         ifdef STACK←GROWS←DOWN
		   result += MIN←PAGE←SIZE;
#	         else
		   result -= MIN←PAGE←SIZE;
#	         endif
		 GC←noop(*result);
	     }
	   }
	   (void) signal(SIGSEGV, old←segv←handler);
#	   ifdef SIGBUS
	       (void) signal(SIGBUS, old←bus←handler);
#	   endif
#	   ifdef STACK←GROWS←UP
	      result += MIN←PAGE←SIZE;
#	   endif
#	endif /* HEURISTIC2 */
    	return(result);
#   endif /* STACKBOTTOM */
}

# endif /* ! OS2 */

/*
 * Register static data segment(s) as roots.
 * If more data segments are added later then they need to be registered
 * add that point (as we do with SunOS dynamic loading),
 * or GC←mark←roots needs to check for them (as we do with PCR).
 * Called with allocator lock held.
 */

# ifdef OS2

void GC←register←data←segments()
{
    PTIB ptib;
    PPIB ppib;
    HMODULE module←handle;
#   define PBUFSIZ 512
    UCHAR path[PBUFSIZ];
    FILE * myexefile;
    struct exe←hdr hdrdos;	/* MSDOS header.	*/
    struct e32←exe hdr386;	/* Real header for my executable */
    struct o32←obj seg;	/* Currrent segment */
    int nsegs;
    
    
    if (DosGetInfoBlocks(&ptib, &ppib) != NO←ERROR) {
    	GC←err←printf0("DosGetInfoBlocks failed\n");
    	ABORT("DosGetInfoBlocks failed\n");
    }
    module←handle = ppib -> pib←hmte;
    if (DosQueryModuleName(module←handle, PBUFSIZ, path) != NO←ERROR) {
    	GC←err←printf0("DosQueryModuleName failed\n");
    	ABORT("DosGetInfoBlocks failed\n");
    }
    myexefile = fopen(path, "rb");
    if (myexefile == 0) {
        GC←err←puts("Couldn't open executable ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Failed to open executable\n");
    }
    if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) {
        GC←err←puts("Couldn't read MSDOS header from ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Couldn't read MSDOS header");
    }
    if (E←MAGIC(hdrdos) != EMAGIC) {
        GC←err←puts("Executable has wrong DOS magic number: ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Bad DOS magic number");
    }
    if (fseek(myexefile, E←LFANEW(hdrdos), SEEK←SET) != 0) {
        GC←err←puts("Seek to new header failed in ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Bad DOS magic number");
    }
    if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) {
        GC←err←puts("Couldn't read MSDOS header from ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Couldn't read OS/2 header");
    }
    if (E32←MAGIC1(hdr386) != E32MAGIC1 || E32←MAGIC2(hdr386) != E32MAGIC2) {
        GC←err←puts("Executable has wrong OS/2 magic number:");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Bad OS/2 magic number");
    }
    if ( E32←BORDER(hdr386) != E32LEBO || E32←WORDER(hdr386) != E32LEWO) {
        GC←err←puts("Executable %s has wrong byte order: ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Bad byte order");
    }
    if ( E32←CPU(hdr386) == E32CPU286) {
        GC←err←puts("GC can't handle 80286 executables: ");
        GC←err←puts(path); GC←err←puts("\n");
        EXIT();
    }
    if (fseek(myexefile, E←LFANEW(hdrdos) + E32←OBJTAB(hdr386),
    	      SEEK←SET) != 0) {
        GC←err←puts("Seek to object table failed: ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Seek to object table failed");
    }
    for (nsegs = E32←OBJCNT(hdr386); nsegs > 0; nsegs--) {
      int flags;
      if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) {
        GC←err←puts("Couldn't read obj table entry from ");
        GC←err←puts(path); GC←err←puts("\n");
        ABORT("Couldn't read obj table entry");
      }
      flags = O32←FLAGS(seg);
      if (!(flags & OBJWRITE)) continue;
      if (!(flags & OBJREAD)) continue;
      if (flags & OBJINVALID) {
          GC←err←printf0("Object with invalid pages?\n");
          continue;
      } 
      GC←add←roots←inner(O32←BASE(seg), O32←BASE(seg)+O32←SIZE(seg));
    }
}

# else

void GC←register←data←segments()
{
    extern int end;
 
#   ifndef PCR      
      GC←add←roots←inner(DATASTART, (char *)(&end));
#   endif
    /* Dynamic libraries are added at every collection, since they may  */
    /* change.								*/
}

# endif  /* ! OS2 */