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

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_osutil[] =
	    "$Id: osutil.c,v 2.41 2001/08/09 00:01:32 ctm Exp ctm $";
#endif

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

#include "rsys/common.h"
#include "QuickDraw.h"
#include "FileMgr.h"
#include "EventMgr.h"
#include "OSUtil.h"
#include "MemoryMgr.h"
#include "Serial.h"
#include "FontMgr.h"
#include "TimeMgr.h"
#include "MenuMgr.h"
#include "ToolboxEvent.h"

#include "rsys/glue.h"
#include "rsys/notmac.h"
#include "rsys/mman.h"
#include "rsys/blockinterrupts.h"
#include "rsys/trapglue.h"
#include "rsys/pstuff.h"
#include "rsys/osutil.h"
#include "rsys/host.h"
#include "rsys/time.h"
#include "rsys/toolevent.h"

#if defined(NEXT) && !defined (OPENSTEP)
#include <appkit/publicWraps.h>
#endif

#if defined(MSDOS)
#include <pc.h>
#endif

#include "SDL.h"
#include "SDL_thread.h"

/*
 * NOTE: HandToHand is not directly called by the outside world.
 *	 Hence, the handle that hp points to is not swapped.
 */

A1(PUBLIC trap, OSErrRET, HandToHand, HIDDEN_Handle *, hp)
{
    Handle nh;
    Size s;
    OSErr err;
	
    if (!hp->p)
      {
	warning_unexpected ("hp = %p", hp);
/*-->*/ return nilHandleErr;
      }

    s = GetHandleSize((*hp).p);
    if ((err = MemError()))
/*-->*/	return(err);

    nh = NewHandle(s);
    if ((err = MemError()))
/*-->*/	return(err);

    BlockMove(STARH((*hp).p), STARH(nh), s);
    (*hp).p = nh;
    return noErr;
}

/*
 * NOTE: PtrToHand is not directly callable, hence the handle that
 *	 h points to isn't swapped.
 */

A3(PUBLIC trap, OSErrRET, PtrToHand, Ptr, p, HIDDEN_Handle *, h, LONGINT, s)
{
    Handle nh;
    OSErr err;
	
    nh = NewHandle(s);
    if ((err = MemError()))
	return(err);
    BlockMove(p, STARH(nh), s);
    if ((err = MemError()))
	return(err);
    (*h).p = nh;
    return(noErr);
}

A3(PUBLIC trap, OSErrRET, PtrToXHand, Ptr, p, Handle, h, LONGINT, s)
{
    OSErr err;
	
/*
 * DO *NOT* use ReallocHandle here.  It will fail if the handle is locked.
 */
    SetHandleSize(h, s);
    if ((err = MemError()))
	return(err);
    BlockMove(p, STARH(h), s);
    if ((err = MemError()))
	return(err);
    return(noErr);
}

A2(PUBLIC trap, OSErrRET, HandAndHand, Handle, h1, Handle, h2)
{
    Size s1 = GetHandleSize(h1), s2 = GetHandleSize(h2);
    OSErr err;
	
    SetHandleSize(h2, s1+s2);
    if ((err = MemError()))
	return(err);
    BlockMove(STARH(h1), STARH(h2)+s2, s1);
    if ((err = MemError()))
	return(err);
    return(noErr);
}

A3(PUBLIC trap, OSErrRET, PtrAndHand, Ptr, p, Handle, h, LONGINT, s1)
{
    Size s2 = GetHandleSize(h);
    OSErr err;
    
    SetHandleSize(h, s1+s2);
    if ((err = MemError()))
	return(err);
    BlockMove(p, STARH(h)+s2, s1);
    if ((err = MemError()))
	return(err);
    return(noErr);
}

/* NOTE:  This set of tables was created by running a test program on
	  the Mac.  It replaces the tables that were hand created from
	  (incomplete) information in Inside Macintosh */

PRIVATE unsigned char casefold[256] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20, '!', '"', '#', '$', '%', '&','\'', '(', ')', '*', '+', ',', '-', '.', '/',
 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[','\\', ']', '^', '_',
 'a', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~',0x7F,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0xCB,0x89,0x80,0xCC,0x81,0x82,0x83,0x8F,
0x90,0x91,0x92,0x93,0x94,0x95,0x84,0x97,0x98,0x99,0x85,0xCD,0x9C,0x9D,0x9E,0x86,
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xAE,0xAF,
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCE,
0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
};

PRIVATE unsigned char diacfold[256] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20, '!', '"', '#', '$', '%', '&','\'', '(', ')', '*', '+', ',', '-', '.', '/',
 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[','\\', ']', '^', '_',
 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',0x7F,
 'A', 'A', 'C', 'E', 'N', 'O', 'U', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e',
 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u',
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE, 'O',
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA, 'a', 'o',0xBD,0xBE, 'o',
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0x20, 'A', 'A', 'O',0xCE,0xCF,
0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 'y',0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
};

PRIVATE unsigned char bothfold[256] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20, '!', '"', '#', '$', '%', '&','\'', '(', ')', '*', '+', ',', '-', '.', '/',
 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[','\\', ']', '^', '_',
 'a', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~',0x7F,
 'A', 'A', 'C', 'E', 'N', 'O', 'U', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E',
 'E', 'E', 'I', 'I', 'I', 'I', 'N', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U',
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE, 'O',
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA, 'A', 'O',0xBD,0xAE, 'O',
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA, 'A', 'A', 'O',0xCE,0xCE,
0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 'Y',0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
};

PRIVATE unsigned char order[256] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x21,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,
0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD,
0xAE,0x4E,0x58,0x5B,0x5E,0x61,0x67,0x69,0x6B,0x6D,0x73,0x75,0x77,0x79,0x7C,0x83,
0x8D,0x8F,0x91,0x93,0x96,0x99,0x9F,0xA1,0xA3,0xA5,0xA8,0xAF,0xB0,0xB1,0xB2,0xB3,
0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x50,0x51,0x52,0x53,0x54,0x5C,0x62,0x63,
0x64,0x65,0x6E,0x6F,0x70,0x71,0x7D,0x84,0x85,0x86,0x87,0x88,0x9A,0x9B,0x9C,0x9D,
0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81,
0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x56,0x89,
0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x8B,
0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
};


A5(PUBLIC, LONGINT, ROMlib_RelString, unsigned char *, s1, unsigned char *, s2,
			       BOOLEAN, casesig, BOOLEAN, diacsig, LONGINT, d0)
{
    register INTEGER n1, n2;
    register unsigned char *s, *t;
    unsigned char c1, c2;

    s = s1;
    t = s2;
    n1 = d0 >> 16;
    n2 = d0 & 0xFFFF;
    if (n1 > n2)
	n1 = n2;
    if (casesig && diacsig) {
	while (n1--) {
	    c1 = *s++;
	    c2 = *t++;
	    if ((c1 == 0x20 && c2 == 0xCA) ||
	        (c2 == 0x20 && c1 == 0xCA))
/*-->*/		continue;
	    if (c1 != c2)
/*-->*/		return order[c1] < order[c2] ? -1 : 1;
	}
    } else if ( casesig && !diacsig) {
	while (n1--) {
	    c1 = *s++;
	    c2 = *t++;
	    if ((c1 == 0x20 && c2 == 0xCA) ||
	        (c2 == 0x20 && c1 == 0xCA))
/*-->*/		continue;
	    if (diacfold[c1] != diacfold[c2])
/*-->*/		return order[diacfold[c1]] < order[diacfold[c2]] ? -1 : 1;
	}
    } else if (!casesig &&  diacsig) {
	while (n1--) {
	    c1 = *s++;
	    c2 = *t++;
	    if ((c1 == 0x20 && c2 == 0xCA) ||
	        (c2 == 0x20 && c1 == 0xCA))
/*-->*/		continue;
	    if (casefold[c1] != casefold[c2])
/*-->*/		return order[casefold[c1]] < order[casefold[c2]] ? -1 : 1;
	}
    } else { /*!casesig && !diacsig*/
	while (n1--) {
	    c1 = *s++;
	    c2 = *t++;
	    if ((c1 == 0x20 && c2 == 0xCA) ||
	        (c2 == 0x20 && c1 == 0xCA))
/*-->*/		continue;
	    if (bothfold[c1] != bothfold[c2])
/*-->*/		return order[bothfold[c1]] < order[bothfold[c2]] ? -1 : 1;
	}
    }
    n1 = d0 >> 16;
    if (n1 != n2)
/*-->*/	return n1 < n2 ? -1 : 1;
    return 0;
}

A4(PUBLIC trap, INTEGERRET, RelString, StringPtr, s1, StringPtr, s2,
					    BOOLEAN, casesig, BOOLEAN, diacsig)
{
    return ROMlib_RelString((unsigned char *) s1 + 1, (unsigned char *) s2 + 1,
	casesig, diacsig,
		   (LONGINT) (unsigned char) s1[0] << 16 | (unsigned char) s2[0]);
}

A4(PUBLIC trap, BOOLEANRET, EqualString, StringPtr, s1, StringPtr, s2,
					    BOOLEAN, casesig, BOOLEAN, diacsig)
{
    return RelString(s1, s2, casesig, diacsig) ? FALSE : TRUE;
}

A2(PUBLIC, int, ROMlib_strcmp, const Byte *, s1, const Byte *, s2)	/* INTERNAL */
{
    register int n1 = U(s1[0]), n2 = U(s2[0]);
    register unsigned char *p1 = (char *) s1+1, *p2 = (char *) s2+1, c1, c2;

#if !defined (LETGCCWAIL)
    c1 = 0;
    c2 = 0;
#endif /* LETGCCWAIL */

    while (n1 && n2 && ((c1 = bothfold[U(*p1)]) == (c2 = bothfold[U(*p2)]))) {
	n1--;
	n2--;
	p1++;
	p2++;
    }
    if (!n1)
	return n2 ? -1 : 0;
    if (!n2)
	return 1;
    else
	return c1 < c2 ? -1 : 1;
}

A3(PUBLIC, void, ROMlib_UprString, StringPtr, s, BOOLEAN, diac, INTEGER, len)
{
    unsigned char *p, *ep, *base;

    if (diac)
	base = casefold;
    else
	base = bothfold;
		
    for (p = s, ep = p + len; p != ep; p++)
	*p = base[U(*p)];
}

A2(PUBLIC trap, void, UprString, StringPtr, s, BOOLEAN, diac)
{
    ROMlib_UprString(s+1, diac, (INTEGER) (unsigned char) s[0]);
}

A1(PUBLIC, void, GetDateTime, LONGINT *, mactimepointer)
{
#undef Time  /* Why is this here? */
    if (mactimepointer) {
      unsigned long msecs;
      msecs = msecs_elapsed ();
      *mactimepointer = CL (UNIXTIMETOMACTIME (ROMlib_start_time.tv_sec)
			    + (((ROMlib_start_time.tv_usec / 1000) + msecs)
			       / 1000));
    }
}

A1(PUBLIC trap, OSErrRET, ReadDateTime, LONGINT *, secs)
{
    GetDateTime(secs);
    return(noErr);
}

A1(PUBLIC trap, OSErrRET, SetDateTime, LONGINT, mactime)
{
#if !defined(SYSV) && !defined (CYGWIN32)
    struct timeval thetime;

    thetime.tv_sec  = MACTIMETOGUNIXTIME(mactime);
    thetime.tv_usec = 0;

    if (settimeofday(&thetime, 0) < 0)
	return(clkWrErr);
    else
	return(noErr);
#else /* defined(SYSV) */
#warning "SetDateTime not implemented"
    return clkWrErr;
#endif /* defined(SYSV) */
}

PRIVATE unsigned long long
secsinminutes (ULONGINT nminutes)
{
  return nminutes * 60;
}

PRIVATE unsigned long long
secsinhours (ULONGINT nhours)
{
  return nhours * secsinminutes((LONGINT) 60);
}

PRIVATE unsigned long long
secsindays (ULONGINT ndays)
{
  return ndays * secsinhours((LONGINT) 24);
}

PRIVATE INTEGER daysinmonths[13] = {
	0,
	31,
	31 + 28,
	31 + 28 + 31,
	31 + 28 + 31 + 30,
	31 + 28 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
};

PRIVATE INTEGER daysinleapmonths[13] = {
	0,
	31,
	31 + 29,
	31 + 29 + 31,
	31 + 29 + 31 + 30,
	31 + 29 + 31 + 30 + 31,
	31 + 29 + 31 + 30 + 31 + 30,
	31 + 29 + 31 + 30 + 31 + 30 + 31,
	31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
	31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
	31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
	31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
	31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
};

PRIVATE unsigned long long
secsinmonths (ULONGINT nmonths)
{
  return secsindays((ULONGINT) daysinmonths[nmonths]);
}

PRIVATE unsigned long long
secsinleapmonths (ULONGINT nmonths)
{
  return secsindays((ULONGINT) daysinleapmonths[nmonths]);
}

PRIVATE unsigned long long
daysinyears (ULONGINT year)
{
  return (ULONGINT) 365 * year + year / 4 - year / 100 + year / 400;
}

A1(PRIVATE, BOOLEAN, isleap, ULONGINT, year)
{
    return !(year % 4) && (year % 100 || !(year % 400));
}


/* month January = 1, hour Midnight = 0 */

PUBLIC long long
ROMlib_long_long_secs (INTEGER year, INTEGER month, INTEGER day, INTEGER hour,
		       INTEGER minute, INTEGER second)
{
/* #warning Make this work for years < 1904 ... it's probably wrong */
  long long retval;

  if (year < 1904)
    warning_unexpected ("year = %d", year);
  retval  = secsindays(daysinyears(year-1) - daysinyears(1903));
  retval += secsinmonths(month-1);
  retval += secsindays(day-1);
  retval += secsinhours(hour);
  retval += secsinminutes(minute);
  retval += second;
  if (isleap(year) && month > 2)
    retval += secsindays((ULONGINT) 1);
  return retval;
}

/*
 * NOTE: not callable from the outside world directly
 */

A2(PUBLIC trap, void, Date2Secs, DateTimeRec *, d, LONGINT *, s)
{
    LONGINT l;

    l  = ROMlib_long_long_secs (CW (d->year), CW (d->month), CW (d->day),
				CW (d->hour), CW (d->minute), CW (d->second));
    *s = l;
}

/* #warning should fix to work with dates less than 1904 */

PUBLIC void
date_to_swapped_fields (long long mactime, INTEGER *yearp, INTEGER *monthp,
			INTEGER *dayp, INTEGER *hourp, INTEGER *minutep,
			INTEGER *secondp, INTEGER *dayofweekp,
			INTEGER *dayofyearp, INTEGER *weekofyearp)
{
#define NSECSINMINUTE	(60L)
#define NSECSINHOUR	(60L * NSECSINMINUTE)
#define NSECSINDAY	(24L * NSECSINHOUR)
#define NSECSINYEAR	(365L * NSECSINDAY)
#define NSECSINLEAPYEAR	(366L * NSECSINDAY)

/*
 * NOTE:  the function below relies on the start date being 1/1/1904.  It is
 *	  clear that Apple picked that date so there would be no funky non-
 *	  leap years (1900 was one).
 */
    long long secs_left;
    ULONGINT approx_year, days, secs_approx_year;
    INTEGER month, day, hour, minute;

    if (mactime < 0)
      warning_unexpected (NULL_STRING);

    if (dayofweekp)
      *dayofweekp = CW((mactime / NSECSINDAY + 5) % 7 + 1);

    approx_year = mactime / NSECSINLEAPYEAR;
    days = 365 * approx_year + (approx_year+3)/4;
    secs_left = mactime - (days * NSECSINDAY);
    secs_approx_year = isleap(approx_year) ? NSECSINLEAPYEAR : NSECSINYEAR;
    if (secs_left < secs_approx_year)
	*yearp = CW(approx_year + 1904);
    else {
	approx_year++;
	secs_left -= secs_approx_year;
	*yearp = CW(approx_year + 1904);
    }

    if (secs_left < 0)
      {
	warning_unimplemented ("This code is flat out wrong");
	secs_left %= NSECSINLEAPYEAR;
	if (secs_left < 0)
	  secs_left += NSECSINLEAPYEAR;
	warning_unexpected ("bad date");
      }

    if (dayofyearp)
      *dayofyearp = CW (secs_left / NSECSINDAY + 1);

    if (weekofyearp)
      *weekofyearp = CW (secs_left / NSECSINDAY / 7 + 1);

    if (isleap(approx_year)) {
	for (month = 0;
	     secsinleapmonths(month+1) <= (unsigned long long) secs_left;
	     month++)
	    ;
	secs_left -= secsinleapmonths(month);
    } else {
	for (month = 0;
	     secsinmonths(month+1) <= (unsigned long long) secs_left;
	     month++)
	    ;
	secs_left -= secsinmonths(month);
    }
    *monthp = CW(month+1);

    day = secs_left / NSECSINDAY;
    *dayp = CW(day + 1);
    secs_left -= day * NSECSINDAY;

    hour = secs_left / NSECSINHOUR;
    *hourp = CW(hour);
    secs_left -= hour * NSECSINHOUR;

    minute = secs_left / NSECSINMINUTE;
    *minutep = CW(minute);
    secs_left -= minute * NSECSINMINUTE;

    *secondp = CW(secs_left);
}

A2(PUBLIC trap, void, Secs2Date, LONGINT, mactime, DateTimeRec *, d)
{
  date_to_swapped_fields ((unsigned long) mactime, &d->year, &d->month,
			  &d->day, &d->hour, &d->minute, &d->second,
			  &d->dayOfWeek, 0, 0);
}

A1(PUBLIC, void, GetTime, DateTimeRec *, d)
{
    LONGINT secs;
	
    GetDateTime(&secs);
    Secs2Date(CL(secs), d);
}

A1(PUBLIC, void, SetTime, DateTimeRec *, d)
{
    LONGINT secs;
	
    Date2Secs(d, &secs);
    SetDateTime(secs);
}

typedef enum {Read, Write} ReadWriteType;

#define VALID	0xA8

A0(PRIVATE, void, setdefaults)
{
    SPValid     = VALID;
    SPAlarm     = SPATalkB = SPATalkA = SPConfig = 0;
    SPPrint     = SPPortB = SPPortA = CW(baud9600 | stop10 | data8 | noParity);
    SPFont      = CW(geneva - 1);
    SPKbd       = 0x63;
    SPVolCtl    = 3;
    SPClikCaret = 0x88;
    SPMisc2     = 0x4C;
}

A1(PRIVATE, OSErr, openparam, INTEGER *, rnp)
{
    static char paramname[] = PARAMRAMMACNAME;
    OSErr err;

    err = FSOpen((StringPtr) paramname, Cx(BootDrive), rnp);
    if (err == fnfErr) {
	if ((err = Create((StringPtr) paramname, Cx(BootDrive), TICK("unix"),
						       TICK("pram"))) == noErr)
	    err = FSOpen((StringPtr) paramname, Cx(BootDrive), rnp);
    }
    return err;
}

PUBLIC LONGINT ROMlib_GMTcorrect;

A0(PRIVATE, void, deriveglobals)
{
    struct tm *tm, tml, tmg, *tmlater, *tmearlier;
    time_t unixtimenow, gmtimenow, ltimenow;

    unixtimenow = (ROMlib_start_time.tv_sec
		   + ((ROMlib_start_time.tv_usec / 1000 + msecs_elapsed ())
		      / 1000));
    tm = localtime(&unixtimenow);
    BlockMove((Ptr) tm, (Ptr) &tml, (Size) sizeof(tml));
    tm = gmtime(&unixtimenow);
    BlockMove((Ptr) tm, (Ptr) &tmg, (Size) sizeof(tmg));
    if (tml.tm_year != tmg.tm_year) {
       tmlater = tml.tm_year > tmg.tm_year ? &tml : &tmg; 
       tmearlier = tml.tm_year > tmg.tm_year ? &tmg : &tml;
       /* Adjust the day of year */
       tmlater->tm_yday = tmearlier->tm_yday + 1;
    }
    ltimenow = (tml.tm_yday * 24 * 60 * 60) + /* seconds per day */
        (tml.tm_hour * 60 * 60) + /* seconds per hour */
        (tml.tm_min * 60) + tml.tm_sec;
    gmtimenow = (tmg.tm_yday * 24 * 60 * 60) +
        (tmg.tm_hour * 60 * 60) +
        (tmg.tm_min * 60) + tmg.tm_sec;
    ROMlib_GMTcorrect = gmtimenow - ltimenow;

    KeyThresh    = CW((short) ((SPKbd >> 4) & 0xF) * 4);
    KeyRepThresh = CW((short) (SPKbd & 0xF) * 4);
    MenuFlash    = CW((short) (SPMisc2 >> 2) & 3);
    CaretTime    = CL((short) (SPClikCaret & 0xF) * 4);
    DoubleTime   = CL((short) (SPClikCaret & 0xF0) / 4);
}

A0(PUBLIC trap, OSErrRET, InitUtil)		/* IMII-380 */
{
    INTEGER rn;
    SysParmType sp;
    LONGINT count;
    OSErr err;
    BOOLEAN badread;

#if !defined(LETGCCWAIL)
    badread = FALSE;
#endif
    if ((err = openparam(&rn)) == noErr) {
	count = sizeof(sp);
	if (FSRead(rn, &count, (Ptr) &sp) == noErr && sp.valid == VALID &&
							 count == sizeof(sp)) {
	    SPValid	= sp.valid;
	    SPATalkA    = sp.aTalkA;
	    SPATalkB    = sp.aTalkB;
	    SPConfig    = sp.config;
	    SPPortA	= sp.portA;
	    SPPortB	= sp.portB;
	    SPAlarm	= sp.alarm;
	    SPFont	= sp.font;
	    SPKbd	= CW(sp.kbdPrint) >> 8;
	    SPPrint	= CW(sp.kbdPrint);
	    SPVolCtl    = CW(sp.volClik)  >> 8;
	    SPClikCaret = CW(sp.volClik);
#if !defined (BIGENDIAN)
	    SPMisc2	= sp.misc;
#else
	    SPMisc2	= sp.misc >> 8;
#warning this is broken
#endif
	    badread = FALSE;
	} else
	    badread = TRUE;
    }
    if (err != noErr || badread)
	setdefaults();
    deriveglobals();
    if (err)
	err = prInitErr;
    else
      err = FSClose(rn);
    return err;
}

A0(PUBLIC, SysPPtr, GetSysPPtr)	/* IMII-381 */
{
    return (SysPPtr) &SPValid;
}

A0(PUBLIC trap, OSErrRET, WriteParam)		/* IMII-382 */
{
    INTEGER rn;
    SysParmType sp;
    LONGINT count;
    OSErr err, err2;

    err = prWrErr; 
    if (openparam(&rn) == noErr) {
	sp.valid    = SPValid;
	sp.aTalkA   = SPATalkA;
	sp.aTalkB   = SPATalkB;
	sp.config   = SPConfig;
	sp.portA    = SPPortA;
	sp.portB    = SPPortB;
	sp.alarm    = SPAlarm;
	sp.font     = SPFont;
	sp.kbdPrint = CW((short) (SPKbd    << 8) | (SPPrint     & 0xff));
	sp.volClik  = CW((short) (SPVolCtl << 8) | (SPClikCaret & 0xff));
#if !defined (BIGENDIAN)
	sp.misc     = SPMisc2;
#else
	sp.misc     = SPMisc2 << 8;
#endif
	count = sizeof(sp);
	if (FSWrite(rn, &count, (Ptr) &sp) == noErr && count == sizeof(sp))
	    err = noErr;
	deriveglobals();
	ROMlib_beepedonce = FALSE;
	err2 = FSClose(rn);
	if (err == noErr)
	  err = err2;
    }
    return err;
}

A2(PUBLIC trap, void, Enqueue, QElemPtr, e, QHdrPtr, h)
{
    HIDDEN_QElemPtr *qpp;
    virtual_int_state_t block;

    block = block_virtual_ints ();
    for (qpp = (HIDDEN_QElemPtr *) &h->qHead; (*qpp).p && MR((*qpp).p) != e;
					qpp = (HIDDEN_QElemPtr *) MR((*qpp).p))
	;
    if (!(*qpp).p) {
	e->evQElem.qLink = 0;
	if (h->qTail)
	    MR(h->qTail)->evQElem.qLink = RM(e);
	else
	    h->qHead = RM(e);
	h->qTail = RM(e);
    }
    restore_virtual_ints (block);
}

A2(PUBLIC trap, OSErrRET, Dequeue, QElemPtr, e, QHdrPtr, h)
{
    HIDDEN_QElemPtr *qpp;
    OSErr retval;
    virtual_int_state_t block;
	
    retval = qErr;
    block = block_virtual_ints ();
    for (qpp = (HIDDEN_QElemPtr *) &h->qHead; (*qpp).p && MR((*qpp).p) != e;
					qpp = (HIDDEN_QElemPtr *) MR((*qpp).p))
	;
    if ((*qpp).p) {
	(*qpp).p = e->evQElem.qLink;
	if (MR(h->qTail) == e)
	    h->qTail = qpp == (HIDDEN_QElemPtr *) &h->qHead ? (QElemPtr) 0 : RM((QElemPtr) qpp);
	retval = noErr;
    }
    restore_virtual_ints (block);
    return retval;
}

A2(PUBLIC, LONGINT, NGetTrapAddress, INTEGER, n, INTEGER, ttype) /* IMII-384 */
{
  LONGINT retval;

  retval = (LONGINT) ((ttype == OSTrap) ?
		      (LONGINT) CL((long) ostraptable[n&(NOSENTRIES-1)])
		      :
		      (LONGINT) CL((long) tooltraptable[n&(NTOOLENTRIES-1)]));
  warning_trace_info ("n = 0x%x, ttype = %d, retval = %p", (uint16) n, ttype,
		      (void *) retval);
  return retval;
}

#if !defined(BINCOMPAT)

A2(PUBLIC, void, SetTrapAddress, LONGINT, addr,	/* IMII-384 NOT SUPPORTED */
								 INTEGER, n)
{
}
#endif

PRIVATE BOOLEAN shouldbeawake;
PUBLIC SDL_cond *shouldbeawake_cond = NULL;
PUBLIC SDL_mutex *shouldbeawake_mutex = NULL;

A0(PUBLIC, void, C_ROMlib_wakeup)
{
  shouldbeawake = TRUE;
};

/* argument n is in 1/60ths of a second */

A2 (PUBLIC trap, void, Delay, LONGINT, n, LONGINT *, ftp)	/* IMII-384 */
{
  if (n > 0)
    {
#if defined (CYGWIN32)
      clock_t finish_clocks;
      void __attribute__ ((stdcall)) Sleep(int32 dwMilliseconds);

      finish_clocks = clock() + n * CLOCKS_PER_SEC / 60;
      while (clock () < finish_clocks)
	{
	  Sleep(0);  /* Give up current CPU timeslice */
	  check_virtual_interrupt ();
	}
#elif defined (MSDOS)
      unsigned long finish_msecs;
      /* Busy looping is fine here. */
      finish_msecs = msecs_elapsed () + n * 1000 / 60;
      while (msecs_elapsed () < finish_msecs)
	{
	  check_virtual_interrupt ();
	}
#else /* !MSDOS */
      TMTask tm;
      int old_mask;
      old_mask = sigblock (sigmask (SIGALRM));

      tm.tmAddr = RM ((ProcPtr) P_ROMlib_wakeup);
      InsTime ((QElemPtr) &tm);
      if (!shouldbeawake_cond)
	shouldbeawake_cond = SDL_CreateCond ();
      if (!shouldbeawake_mutex)
	shouldbeawake_mutex = SDL_CreateMutex ();

      SDL_mutexP (shouldbeawake_mutex);
      shouldbeawake = FALSE;
      PrimeTime ((QElemPtr) &tm, n * 1000 / 60);
      while (!shouldbeawake)
	{
#if 0
/* sigsuspend() is an elegant solution.  Since SDL's timer also uses 
   SIGALRM under Linux, I'm not going to mess with this code.
   The CYGWIN32 code above doesn't really need to be modified for SDL
   either, since the SDL version would do basically the same thing.
 */

#warning THIS WONT WORK WITH THREADED SDL

#if !defined(USE_BSD_SIGNALS)
	  sigset_t zero_mask;
	  sigemptyset(&zero_mask);
	  sigsuspend (&zero_mask);  /* NOTE: this will allow both UNIX signals
				       and our thread_set_state kludges */
#else
	  sigsetmask (0);
#endif
#else
	  SDL_CondWait (shouldbeawake_cond, shouldbeawake_mutex);
	  SDL_mutexP (shouldbeawake_mutex);
#endif
	  check_virtual_interrupt ();
	}
      RmvTime ((QElemPtr) &tm);
      SDL_mutexV (shouldbeawake_mutex);

      sigsetmask (old_mask);
#endif /* !MSDOS */
    }

  /* Note:  we're really called from a stub, so no CL() is needed here. */
  if (ftp)
    *ftp = TickCount ();
}

P1(PUBLIC pascal trap, void, SysBeep, INTEGER, i)	/* SYSTEM DEPENDENT */
{
#if defined(MAC)
    DebugStr("\004Beep");
#elif defined(X) || defined(NEXTSTEP) || defined (CYGWIN32)
    host_beep_at_user ();
#elif defined(MSDOS)
    sound(440);
    Delay(i, (LONGINT *) 0);
    sound(0);
#else
    write(1, "\7", 1);
#endif
}

PUBLIC char ROMlib_phoneyrom[10] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0x06, 0x7C,
};

A2(PUBLIC trap, void, Environs, INTEGER *, rom, INTEGER *, machine)
{
    unsigned char rom8, rom9;

    rom8 = ((unsigned char *)ROMlib_phoneyrom)[8];
    rom9 = ((unsigned char *)ROMlib_phoneyrom)[9];

    *rom = CW (rom9);
    *machine = CW (rom8 + 1);
}

#if !defined(NEXT) && !defined(SYN68K)
INTEGER ROMlib_processor     = env68020;
#else
INTEGER ROMlib_processor     = env68040;
#endif

#if !defined(SYN68K)
INTEGER ROMlib_hasFPU        = TRUE;
#endif

A2(PUBLIC trap, OSErrRET, SysEnvirons, INTEGER, vers, SysEnvRecPtr, p)
{
#if defined(NEXT)
    ROMlib_processor = ROMlib_040 ? env68040 : env68030;
#endif

    if (vers <= 0)
/*-->*/	return envBadVers;
    p->environsVersion = CW (vers);
    p->machineType     = CWC (53);
    p->systemVersion   = SysVersion;

#if !defined (SYN68K)
    p->processor       = CW(ROMlib_processor);
    p->hasFPU          = ROMlib_hasFPU;
#else /* SYN68K */
    p->processor       = CWC(env68040);
    p->hasFPU          = FALSE;
#endif /* SYN68K */
    p->hasColorQD      = TRUE;
    p->keyBoardType    = CWC(envAExtendKbd);
    p->atDrvrVersNum   = 0;
    p->sysVRefNum      = BootDrive;

    return vers <= SYSRECVNUM ? noErr : envVersTooBig;
}

A0(PUBLIC, void, SetUpA5)
{
}

A0(PUBLIC, void, RestoreA5)
{
}

#define TRUE32b	1

A1(PUBLIC, void, GetMMUMode, INTEGER *, ip)	/* IMV-592 */
{
    *ip = CWC(TRUE32b);
}

A1(PUBLIC, void, SwapMMUMode, Byte *, bp)	/* IMV-593 */
{
    *bp = CB(TRUE32b);
}

A1(PUBLIC, LONGINT, StripAddress, LONGINT, l)	/* IMV-593 */
{
    return l;
}
