/* Copyright 1989, 1990, 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_segment[] =
		"$Id: segment.c 88 2005-05-25 03:59:37Z ctm $";
#endif

/* Forward declarations in SegmentLdr.h (DO NOT DELETE THIS LINE) */

#include "rsys/common.h"

#include "FileMgr.h"
#include "SegmentLdr.h"
#include "MemoryMgr.h"
#include "ResourceMgr.h"
#include "StdFilePkg.h"
#include "SysErr.h"
#include "FontMgr.h"
#include "OSEvent.h"
#include "WindowMgr.h"

#include "rsys/hfs.h"
#include "rsys/file.h"
#include "rsys/notmac.h"
#include "rsys/glue.h"
#include "rsys/wind.h"
#include "rsys/segment.h"
#include "rsys/host.h"
#include "rsys/vdriver.h"
#include "rsys/executor.h"
#include "rsys/flags.h"
#include "rsys/prefs.h"
#include "rsys/osevent.h"
#include "rsys/cquick.h"
#include "rsys/desk.h"
#include "rsys/dcache.h"
#include "rsys/launch.h"

#include <ctype.h>

#if defined (CYGWIN32)

#include "winfs.h"

/*
 * NOTE:  I've looked at MINGW32 0.1.3 and the io.h #defines
 *        X_OK, which we use below.  I think Sam uses 0.1.3, even though
 *	  ARDI is currently at 0.1.2.
 */

#if !defined (OLD_MINGWIN32)
#include <io.h> /* needed for X_OK */
#else
#define X_OK 4 /* this is really just to get segment.c to compile */
#endif

#endif

#if defined(MSDOS)
#include "dosevents.h"
#include "vga.h"
#endif /* defined(MSDOS) */

typedef struct {
    INTEGER message;
    INTEGER count;
    AppFile files[1];
} finderinfo;

typedef finderinfo *finderinfoptr;
typedef struct { finderinfoptr p PACKED_P; } HIDDEN_finderinfoptr;
typedef HIDDEN_finderinfoptr *finderinfohand;

PUBLIC int ROMlib_cacheheuristic = FALSE;


#if defined(ONLY_DESTROY_BETWEEN_CODE_SEGMENTS)
static short flush_bypass = 0;

/* This is the total number of code resources accessible to the program.
 * If it's zero, we recompute it.  Remember to set it to zero whenever
 * launching a new program!
 */

typedef struct {
    syn68k_addr_t start;
    uint32 num_bytes;
} destroy_pair_t;

INTEGER ROMlib_num_code_resources = 0;

static int compar(const void *p1, const void *p2)
{
    int retval;
    uint32 s1, s2;

    s1 = (uint32) ((const destroy_pair_t *) p1)->start;
    s2 = (uint32) ((const destroy_pair_t *) p2)->start;

    if (s1 < s2)
	retval = -1;
    else if (s2 < s1)
	retval = 1;
    else
	retval = 0;
    return retval;
}
#endif

A0(PUBLIC, void, flushcache)
{
    ROMlib_destroy_blocks( (syn68k_addr_t) 0, (uint32) ~0, TRUE );
}

A2(PUBLIC trap, void, HWPriv, LONGINT, d0, LONGINT, a0)
{
    static char d_cache_enabled = TRUE, i_cache_enabled = TRUE;
    int new_state;

    switch (d0) {
    case 0:	/* Dis/Ena Instr cache */
        warning_unimplemented ("Dis/Ena instr cache");
	new_state = ((EM_A0 & 0xFFFF) != 0);
	EM_A0 = i_cache_enabled;
	i_cache_enabled = new_state;
	break;
    case 1:	/* Flush Instr cache */
	flushcache();
	break;
    case 2:	/* Dis/Ena Data cache */
        warning_unimplemented ("Dis/Ena data cache");
	new_state = ((EM_A0 & 0xFFFF) != 0);
	EM_A0 = d_cache_enabled;
	d_cache_enabled = new_state;
	break;
    case 3:	/* Flush Data cache */
#if 0
	flushcache();
#endif
	break;
    case 4:	/* Enable external cache */
        warning_unimplemented ("Enable external cache");
	break;
    case 5:	/* Disable external cache */
        warning_unimplemented ("Disable external cache");
	break;
    case 6:	/* Flush external cache */
	flushcache();
	break;
    case 9:	/* Flush cache range */
	ROMlib_destroy_blocks ((syn68k_addr_t) a0, EM_A1, TRUE);
	EM_D0 = noErr;	/* Maybe we should only touch d0.w? */
	break;
    default:
        warning_unexpected ("d0 = 0x%x", d0); 
	EM_D0 = hwParamErr;	/* Maybe we should only touch d0.w? */
	break;
    }
}

A1(PUBLIC, char *, ROMlib_undotdot, char *, origp)
{
    int dotcount, nleft;
    char *p, *oldloc;
    boolean_t slashseen_p;

    nleft = strlen(origp) + 1;
    slashseen_p = FALSE;
    dotcount = -1;
    for (p = origp; *p; p++, nleft--) {
	switch (*p) {
	case '/':
	    switch (dotcount) {
	    case 0:		/* slash slash */
	        BlockMove((Ptr) p+1, (Ptr) p, (Size) nleft-1);
		break;
	    case 1:		/* slash dot slash */
		BlockMove((Ptr) p+1, (Ptr) p-1, (Size) nleft-1);
		p -= 2;
		dotcount = 0;
		break;
	    case 2:		/* slash dot dot slash */
		for (oldloc = p-4; oldloc >= origp && *oldloc != '/'; --oldloc)
		    ;
		if (oldloc < origp)
		    oldloc = origp + 1;
		else
		    ++oldloc;
		BlockMove((Ptr) p+1, (Ptr) oldloc, (Size) nleft-1);
		p = oldloc - 1;
		dotcount = 0;
		break;
	    default:
		dotcount = 0;
		break;
	    }
	    slashseen_p = TRUE;
	    break;
	case '.':
	    if (slashseen_p)
	      dotcount++;
	    break;
	default:
	    slashseen_p = FALSE;
	    dotcount = -1;
	    break;
	}
    }
    return origp;
}

PRIVATE void lastcomponent( StringPtr dest, StringPtr src )
{
    unsigned char *c, *lastcolon;
    int n;

    lastcolon = 0;
    for (c = src + 1, n = src[0]; --n >= 0; ++c)
	if (*c == ':')
	    lastcolon = c;
    if (lastcolon) {
	++lastcolon;
	n = src[0] - (lastcolon - (src+1));
    } else
	n = src[0];
    dest[0] = n;
    memmove(dest+1, lastcolon, n);
}

#if defined (MSDOS) || defined (CYGWIN32)
PRIVATE char *
canonicalize_potential_windows_path (char *uname)
{
  char *p;

  for (p = uname; *p; ++p)
    if (*p == '\\')
      *p = '/';

  return uname;
}
#endif

PRIVATE boolean_t
full_pathname_p (char *uname)
{
  boolean_t retval;

  retval = uname[0] == '/';
#if defined (MSDOS) || defined(CYGWIN32)
  if (!retval && uname[0] && uname[1] == ':' && uname[2] == '/')
    retval = TRUE;
#endif
  return retval;
}

PRIVATE uint8
hexval (char c)
{
  uint8 retval;

  if (c >= '0' && c <= '9')
    retval = c - '0';
  else if (c >= 'a' && c <= 'f')
    retval = c - 'a' + 10;
  else if (c >= 'A' && c <= 'F')
    retval = c - 'A' + 10;
  else
    retval = 0;

  return retval;
}

/*
 * Copies a c string into a pascal string changing occurrances of ::XY
 * (where X and Y are hex digits) into the character 0xXY.
 */

PRIVATE void
colon_colon_copy (StringPtr dst, const char *src)
{
  StringPtr save_dst;

  save_dst = dst;
  while (*src)
    {
      if (src[0] == ':' && src[1] == ':'
	  && isxdigit (src[2]) && isxdigit (src[3]))
	{
	  *++dst = (hexval (src[2]) << 4) | hexval (src[3]);
	  src += 4;
	}
      else
	*++dst = *src++;
    }
  save_dst[0] = dst - save_dst;
}

/*
 * This routine converts the UNIX path path to a pascal string that
 * starts with a colon whose individual components have been converted
 * from AppleDouble representation to Mac representation.  In theory we
 * could use this whether or not we were using the netatalk naming convention,
 * but since this is a recent addition to ROMlib and since we're about to
 * start building 2.1 candidates it makes sense to just use this in the
 * unsupported case and continue using the old code with non-netatalk naming.
 */

#define MAC_LOCAL_FROM_UNIX_LOCAL(s)					\
({									\
  char *_s;								\
  unsigned char *retval;						\
  int len;								\
  int count;								\
  char *next_slash;							\
									\
  _s = (s);								\
  len = strlen (_s);							\
  retval = alloca (len + 2);						\
  retval[1] = ':';							\
  count = 1;								\
									\
  do									\
    {									\
      int component_len, shrinkage;					\
									\
      next_slash = strchr (_s, '/');					\
      if (next_slash)							\
	component_len = next_slash - _s;				\
      else								\
	component_len = strlen (_s);					\
      memcpy (retval+count+1, _s, component_len);			\
      shrinkage = ROMlib_UNIX7_to_Mac ((char *) retval+count+1,         \
                                       component_len);			\
      count += component_len - shrinkage;				\
      if (next_slash)							\
	{								\
	  _s += component_len + 1; /* skip the '/', too */		\
	  retval[count+1] = ':';					\
	  ++count;							\
	}								\
    }									\
    while (next_slash);							\
									\
  retval[0] = count;							\
  retval;								\
})

A2(PRIVATE, BOOLEAN, argv_to_appfile, char *, uname, AppFile *, ap)
{
  int namelen, pathlen;
  unsigned char *path, *p;
  VCBExtra *vcbp;
  BOOLEAN retval;
  INTEGER sysnamelen, totnamelen;
  CInfoPBRec cinfo;
  WDPBRec wpb;
  struct stat sbuf;

#if defined (MSDOS) || defined(CYGWIN32)
  uname = canonicalize_potential_windows_path (uname);
#endif

  if (uname && Ustat(uname, &sbuf) == 0)
    {    
      if (!full_pathname_p (uname))
	{
	  namelen = strlen (uname);
	  path = (unsigned char *) ALLOCA(MAXPATHLEN + 1 + namelen + 1);
	  if (getcwd ((char *) path, MAXPATHLEN))
	    {
	      pathlen = strlen ((char *) path);
	      path[pathlen] = '/';
	      if (uname[0] == '.' && uname[1] == '/')
		BlockMove ((Ptr) uname+2, (Ptr) (path+pathlen+1),
			   (Size) namelen+1-2);
	      else
		BlockMove ((Ptr) uname, (Ptr) (path+pathlen+1),
			   (Size) namelen+1);
	      uname = (char *) path;
#if defined (MSDOS) || defined(CYGWIN32)
	      uname = canonicalize_potential_windows_path (uname);
#endif
	    }
	  else
	    warning_unexpected ("getwd failed: expect trouble reading "
				"resources");
	}
      ROMlib_automount(uname);
      vcbp = (VCBExtra *) ROMlib_vcbbybiggestunixname(uname);
      if (vcbp)
	{
	  cinfo.hFileInfo.ioVRefNum = vcbp->vcb.vcbVRefNum;
	  wpb.ioNamePtr  = 0;
	  sysnamelen = strlen(vcbp->unixname);
	  if (sysnamelen == 1 + SLASH_CHAR_OFFSET) /* don't remove leading
						      '/' if that's
						      all there is */
	    {
	      sysnamelen = 0;
	      uname += SLASH_CHAR_OFFSET;
	    }

	  /* NOTE: we can probably skip the following "if" and the else
	     part and just use the "path = MAC_LOC..." line unconditionally,
	     but I don't want to possibly introduce a subtle bug at the last
	     minute before releasing 2.1 final. */

	  if (apple_double_quote_char == ':')
	    path = MAC_LOCAL_FROM_UNIX_LOCAL (uname + sysnamelen + 1);
	  else
	    {
	      totnamelen = strlen(uname);
	      pathlen = totnamelen - sysnamelen + 2;	/* count & NUL */
	      path = alloca(pathlen);
	      strcpy((char *) path+1, uname + sysnamelen); /* path has count and */
	      path[0] = pathlen - 2;		     /* is NUL terminated */
	      path[1] = ':';
	      for (p = path; *++p;)
		if (*p == '/')
		  *p = ':';

	      path[0] -= ROMlib_UNIX7_to_Mac((char *) path+1, path[0]);
	    }
	}
      else
	path = 0; /* TODO:  Some sort of problem here */
    }
  else
    {
      int len;
      len = strlen (uname);
      path = alloca (len + 1);
      colon_colon_copy (path, uname);
      cinfo.hFileInfo.ioVRefNum = 0;
      wpb.ioNamePtr  = RM(path);
    }
  cinfo.hFileInfo.ioNamePtr = RM(path);
  cinfo.hFileInfo.ioFDirIndex = CWC (0);
  cinfo.hFileInfo.ioDirID = 0;
  if ((retval = (PBGetCatInfo(&cinfo, FALSE) == noErr)))
    {
      ap->fType = cinfo.hFileInfo.ioFlFndrInfo.fdType;
      ap->versNum = 0;
      wpb.ioVRefNum  = cinfo.hFileInfo.ioVRefNum;
      wpb.ioWDProcID = TICKX("unix");
      wpb.ioWDDirID  = cinfo.hFileInfo.ioFlParID;
      if (PBOpenWD(&wpb, FALSE) == noErr)
	{
	  ap->vRefNum = wpb.ioVRefNum;
	  lastcomponent(ap->fName, path);
	}
      else
	{
	  ap->vRefNum = cinfo.hFileInfo.ioVRefNum;
	  str255assign(ap->fName, path);
	}
    }
  else
    {
      warning_unexpected ("%s: unable to get info on `%s'\n", program_name,
			  uname);
    }
  return retval;
}

#if defined(NEXTSTEP) && defined(BINCOMPAT)
PUBLIC INTEGER ROMlib_acceptsanotherfile = TRUE;
#endif

PUBLIC int ROMlib_print;

#if !defined (MSDOS) && !defined(CYGWIN32)
#define PATH_SEPARATER ':'
#else
#define PATH_SEPARATER ';'
#endif

A2(PUBLIC, void, ROMlib_seginit, LONGINT, argc, char **, argv)	/* INTERNAL */
{
    char *path, *firstcolon;
    char *fullpathname;
    finderinfohand fh;
    AppFile app;
    INTEGER newcount;
    THz saveZone;

    fullpathname = 0;
    if (Uaccess(argv[0], X_OK) == 0)
	fullpathname = argv[0];
    else {
	for (path = getenv("PATH"); path && path[0];
				      path = firstcolon ? firstcolon + 1 : 0) {
	    if ((firstcolon = strchr(path, PATH_SEPARATER)))
		*firstcolon = 0;
	    if (path[0]) {
		fullpathname =
		       (char *) NewPtr(strlen(path) + 1 + strlen(argv[0]) + 1);
		sprintf(fullpathname, "%s/%s", path, argv[0]);
	    } else {
		fullpathname = (char *) NewPtr(strlen(argv[0]) + 1);
		sprintf(fullpathname, "%s", argv[0]);
	    }
	    if (firstcolon)
	      *firstcolon = PATH_SEPARATER; /* if we don't replace this,
					       Linux gets confused */
	    if (Uaccess(fullpathname, X_OK) == 0)
		firstcolon = 0;	/* this will break us out of the loop */
	    else {
		DisposPtr((Ptr) fullpathname);
		fullpathname = 0;
	    }
	}
    }

    /* NOTE: It's not clear why we're calling argv_to_appfile on fullpathname
       below, but argv_to_appfile has enough potential side-effects that I'm
       not about to remove it.  OTOH, the call to OpenRFPerm will create
       a spurious %executor file and then fail, so I've #if 0'd it out. */

    if (argv_to_appfile(fullpathname, &app)) {
#if 0
	CurApRefNum = CW(OpenRFPerm(app.fName, CW(app.vRefNum), fsCurPerm));
#endif
	CurApName[0] = MIN(app.fName[0], sizeof(CurApName)-1);
	BlockMove((Ptr) app.fName+1, (Ptr) CurApName+1, (Size) CurApName[0]);
    } else {
	CurApRefNum = -1;
	CurApName[0] = 0;
    }
    saveZone = TheZone;
    TheZone = SysZone;
    fh = (finderinfohand)
		        NewHandle((Size) sizeof(finderinfo) - sizeof(AppFile));
    TheZone = saveZone;

    AppParmHandle = (Handle) RM(fh);
    HxX(fh, count)   = 0;
    HxX(fh, message) = ROMlib_print ? CWC(appPrint) : CWC(appOpen) ;
    if (fullpathname && fullpathname != argv[0])
	DisposPtr((Ptr) fullpathname);
#if defined(NEXTSTEP) && defined(BINCOMPAT)
    ROMlib_acceptsanotherfile = FALSE;
    if (ROMlib_toexec) {
	if (ROMlib_toopen)
	    ROMlib_toexec = *(char **) ROMlib_toexec;	/* ick! */
	if (argv_to_appfile(ROMlib_toexec, &app)) {
	    ROMlib_startupscreen = FALSE;
	    ROMlib_exit = TRUE;
	    newcount = Hx(fh, count) + 1;
	    HxX(fh, count) = CW(newcount);
	    SetHandleSize((Handle) fh,
		     (char *) &HxX(fh, files)[newcount] - (char *)STARH(fh));
	    HxX(fh, files)[Hx(fh, count)-1] = app;
	}
	if (ROMlib_toopen) {
	    if (argv_to_appfile(ROMlib_toopen, &app)) {
		newcount = Hx(fh, count) + 1;
		HxX(fh, count) = CW(newcount);
		SetHandleSize((Handle) fh,
		    (char *) &HxX(fh, files)[newcount] - (char *) STARH(fh));
		HxX(fh, files)[Hx(fh, count)-1] = app;
	    }
	}
    } else
#endif
    while (--argc > 0) {
	++argv;
	if (argv_to_appfile(argv[0], &app)) {
	    ROMlib_startupscreen = FALSE;
	    ROMlib_exit = TRUE;
	    newcount = Hx(fh, count) + 1;
	    HxX(fh, count) = CW(newcount);
	    SetHandleSize((Handle) fh,
		    (char *) &HxX(fh, files)[newcount] - (char *) STARH(fh));
	    HxX(fh, files)[Hx(fh, count)-1] = app;
	}
    }
}

A2(PUBLIC, void, CountAppFiles, INTEGER *, messagep,
					INTEGER *, countp)	/* IMII-57 */
{
    if (AppParmHandle)
      {
	if (messagep)
	  *messagep = STARH((finderinfohand) MR(AppParmHandle))->message;
	if (countp)
	  *countp   = STARH((finderinfohand) MR(AppParmHandle))->count;
      }
    else
      *countp = 0;
}

A2(PUBLIC, void, GetAppFiles, INTEGER, index, AppFile *, filep)	/* IMII-58 */
{
    *filep = STARH((finderinfohand)MR(AppParmHandle))->files[index-1];
}

A1(PUBLIC, void, ClrAppFiles, INTEGER, index)	/* IMII-58 */
{
    if (STARH((finderinfohand)MR(AppParmHandle))->files[index-1].fType) {
	STARH((finderinfohand)MR(AppParmHandle))->files[index-1].fType = 0;
	STARH((finderinfohand)MR(AppParmHandle))->count = 
		      CW(CW(STARH((finderinfohand)MR(AppParmHandle))->count) - 1);
    }
}

P3(PUBLIC pascal trap, void, GetAppParms, StringPtr, namep,	/* IMII-58 */
				      INTEGER *, rnp, HIDDEN_Handle *, aphandp)
{
    str255assign(namep, CurApName);
    *rnp = CurApRefNum;
    (*aphandp).p = AppParmHandle;
}

PUBLIC char *ROMlib_errorstring;
PUBLIC char ROMlib_exit = 0;

#if defined(MSDOS)
#include <limits.h>
#endif /* defined(MSDOS) */

#if defined(MSDOS)
PRIVATE void execme(const char *toexec)
{
#if !defined(MSDOS)
    execl(toexec, toexec, "-nosplash", (char *) 0);
#else
#if defined(notdef)	/* we don't do this, now that we run under Windows */
    int i;
    i386_registers_t regs;

    for (i = 3; i < OPEN_MAX; ++i)	/* leave 0, 1, 2 open */
	close(i);
    regs.l.ebx = (ULONGINT) toexec;
    regs.l.ecx = (ULONGINT) "-nosplash";
    regs.w.ax = 0xFF10;  /* Turbo assist case 16. */
    int21(&regs);
#endif
#endif
}
#endif /* defined(MSDOS) */

PUBLIC int ROMlib_nobrowser = 0;

PRIVATE BOOLEAN valid_browser( void )
{
  OSErr err;
  FInfo finfo;

  err = GetFInfo(FinderName, CW(BootDrive), &finfo);
  return !ROMlib_nobrowser && err == noErr && finfo.fdType == TICKX("APPL");
}

PRIVATE void launch_browser( void )
{
  /* Set the depth to what was specified on the command line;
   * if nothing was specified there, set the depth to the maximum
   * supported bits per pixel.
   */
  SetDepth (MR (MainDevice),
	    (flag_bpp
	     ? MIN (flag_bpp, vdriver_max_bpp)
	     : vdriver_max_bpp),
	    0, 0);
  Launch(FinderName, CW(BootDrive));
}

P0(PUBLIC pascal trap, void, ExitToShell)
{
  static char beenhere = FALSE;
#if defined(MSDOS)
    char *toexec;
#endif
    ALLOCABEGIN


#if 1

    Point pt;
    static SFTypeList applonly = { CLC(T('A','P','P','L')) };
    SFReply reply;
    char quickbytes[grafSize];
    LONGINT tmpA5;
    WindowPeek t_w;
    int i;

    if (ROMlib_mods & optionKey)
      ROMlib_exit = TRUE;

  /* NOTE: closing drivers is a bad thing in the long run, but for now
     we're doing it.  We actually do it here *and* in reinitialize_things().
     We have to do it here since we want the window taken out of the
     windowlist properly, before we hack it out ourself, but we have to
     do it in reinitializethings because launch calls that, but doesn't
     call exittoshell */

  for (i = DESK_ACC_MIN; i <= DESK_ACC_MAX; ++i)
    CloseDriver (-i - 1);

  empty_timer_queues ();

  if (WWExist == EXIST_YES)
      {
	/* remove global datastructures associated with each window
           remaining in the application's window list */
	for (t_w = MR (WindowList); t_w; t_w = WINDOW_NEXT_WINDOW (t_w))
	  pm_window_closed ((WindowPtr) t_w);
      }
  WindowList = 0;
  
    if (!ROMlib_exit
	&& (!beenhere
	    || strncmp((char *) CurApName+1,
		       BROWSER_NAME, CurApName[0]) != 0)) {
        beenhere = TRUE;

	/* if we call `InitWindows ()', we don't want it shouting
	   about 32bit uncleanliness, &c */
	size_info.application_p = FALSE;
	
	if (QDExist == EXIST_NO) {
	    a5 = (LONGINT) US_TO_SYN68K(&tmpA5);
	    CurrentA5 = (Ptr) CL(a5);
	    InitGraf((Ptr) quickbytes + sizeof(quickbytes) - 4);
	}
	InitFonts();
	FlushEvents( everyEvent, 0 );
	if (WWExist == EXIST_NO)
	    InitWindows();
        if (TEScrpHandle == (Handle)CLC(-1) || TEScrpHandle == (Handle)CLC(0))
	    TEInit();
	if (DlgFont == CWC(0) || DlgFont == CWC(-1))
	    InitDialogs((ProcPtr)0);
	InitCursor();

	if (valid_browser())
	  {
	    /* NOTE: much of the initialization done above isn't really
	             needed here, but I'd prefer to have the same environment
		     when we auto-launch browser as when we choose an app
		     using stdfile */
	    launch_browser();
	  }
	else
	  {
	    pt.h = 100;
	    pt.v = 100;
	    SFGetFile(pt, (StringPtr) "", (ProcPtr) 0, 1,
		      applonly, (ProcPtr) 0, &reply);

	    if (!reply.good)
	      ROMlib_exit = 1;
	    else
	      {
		CurApName[0] = MIN(reply.fName[0], 31);
		BlockMove((Ptr) reply.fName+1, (Ptr) CurApName+1,
			  (Size) CurApName[0]);
		Launch(CurApName, CW(reply.vRefNum));
	      }
	  }
    }

#endif

    CloseResFile(0);
    ROMlib_OurClose();

    dcache_invalidate_all (TRUE);

#if defined (X)
    autorepeatonX();
#endif /* X */

#if defined(SUN) && !defined(X) && !defined(NEXTSTEP)
    close(_windowfd);	/* may be bad choice */
    BlockMove(_savebits, _addr, (Size) _nb);
#endif /* SUN && !defined */

#if defined (SCO)
    ev_close();
    ioctl( 1, (MODESWITCH|_savemode), 0);
    restorekb();
#endif /* SCO */
    if (ROMlib_errorstring) {
	write(2, ROMlib_errorstring, strlen(ROMlib_errorstring));
	gui_abort();
    }
#if defined(NEXT)
    stopprotectingus();
#endif
#if 0
    if (FinderName[0]) {
	toexec = ALLOCA(FinderName[0]+1);
	BlockMove((Ptr) FinderName+1, (Ptr) toexec, (Size) FinderName[0]);
	toexec[FinderName[0]] = 0;
	execlp(toexec, toexec, (char *) 0); 
	/* if it fails we'll exit */
    }
#endif

#if defined(MSDOS)
#define EXECUTORSUFFIX ".exe"	/* This is necessary because of the way go32 */
#else /* works with Executor */
#define EXECUTORSUFFIX ""
#endif
#if defined(MSDOS)
    if (!ROMlib_exit) {
	toexec = ALLOCA(strlen(ROMlib_startdir) + 1 +
						   strlen(ROMlib_appname) +
						   strlen(EXECUTORSUFFIX) + 1);
	sprintf(toexec, "%s/%s%s", ROMlib_startdir, ROMlib_appname,
							       EXECUTORSUFFIX);
	execme(toexec);
    }
#endif
    exit(ROMlib_exit == 1 ? 0 : ROMlib_exit); /* 1 is historically good */
    ALLOCAEND	/* yeah, right, if exit fails... */
}

#if !defined (BINCOMPAT)

P1(PUBLIC pascal trap, void, UnloadSeg, Ptr, addr)	/* INTERNAL */
{
    /* NOP */
}

#else /* BINCOMPAT */

#define JMPLINSTR	0x4EF9
#define MOVESPINSTR	0x3F3C
#define LOADSEGTRAP	0xA9F0

P1(PUBLIC pascal trap, void, LoadSeg, INTEGER volatile, segno)
{
    Handle newcode;
    unsigned short offbytes;
    INTEGER taboff, nentries, savenentries;
    short *ptr, *saveptr;
#if defined(NEXTSTEP)
    if (ROMlib_appbit && !(ROMlib_appbit & ROMlib_whichapps))
	ExitToShell();
#endif

    ResLoad = -1;	/* CricketDraw III's behaviour suggested this */
    newcode = GetResource(TICK("CODE"), segno);
    HLock(newcode);
    taboff   = CW(((INTEGER *) STARH(newcode))[0]);
    if ((uint16) taboff == 0xA89F)  /* magic compressed resource signature */
      {
	/* We are totally dead here.  We almost certainly can't use
	 * `system_error' to inform the user because the window
	 * manager probably is not yet running.
	 */

	ROMlib_launch_failure = (system_version >= 0x700 ?
				 launch_compressed_ge7 :
				 launch_compressed_lt7); 
	C_ExitToShell ();
      }
    savenentries = nentries = CW(((INTEGER *) STARH(newcode))[1]);

    saveptr = ptr = (short *) ((char *) (long) SYN68K_TO_US(a5) + taboff + Cx(CurJTOffset));
    while (--nentries >= 0) {
	if (ptr[1] != CWC(JMPLINSTR)) {
	    offbytes = CW(*ptr);
	    *ptr++ = CW(segno);
	    *ptr++ = CWC(JMPLINSTR);
	    *(LONGINT *) ptr =
		 CL((LONGINT) (long) ((char *) US_TO_SYN68K(STARH(newcode)) + offbytes + 4));
	    ptr += 2;
	} else
	    ptr += 4;
    }
    ROMlib_destroy_blocks( (syn68k_addr_t) (long) US_TO_SYN68K(saveptr), 8L * savenentries,
			  TRUE);
}

#define SEGNOOFP(p) (CW(((INTEGER *)p)[-1]))

A2(PRIVATE, void, unpatch, Ptr, segstart, Ptr, p)
{
    INTEGER *ip;
    Ptr firstpc;

    ip = (INTEGER *) p;

    firstpc = MR(*(Ptr *)(p + 2));
    ip[1]  = ip[-1];	/* the segment number */
    ip[-1] = CW(firstpc - segstart - 4);
    ip[0]  = CWC(MOVESPINSTR);
    ip[2]  = CWC(LOADSEGTRAP);
}

P1(PUBLIC pascal trap, void, UnloadSeg, Ptr, addr)
{
    Ptr p, segstart;
    char *top, *bottom;

    Handle h;
    INTEGER segno;

    if (* (INTEGER *) addr == CWC(JMPLINSTR)) {
	segno = SEGNOOFP(addr);
	h = GetResource(TICK("CODE"), segno);
	if (!(*h).p)
	    LoadResource(h);
	segstart = STARH(h);
	for (p = addr;   SEGNOOFP(p) == segno; p += 8)
	    unpatch(segstart, p);

	bottom = (char *) p;

	for (p = addr-8; SEGNOOFP(p) == segno; p -= 8)
	    unpatch(segstart, p);

	top = (char *) p + 6;	/* +8 that we didn't zap, -2 that we went
					overboard on unpatch (see above) */
	ROMlib_destroy_blocks( (syn68k_addr_t) (long) US_TO_SYN68K(top),
			       bottom - top, FALSE);

	HUnlock(h);
	HPurge(h);
    }
}

#endif /* BINCOMPAT */
