#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <xr/sMisc.h>
#include <xr/Threads.h>
#include <xr/ThreadsMsg.h>
#include <xr/UIO.h>
#include <xr/GC.h>
#include "gc/gc←private.h"

int SPCR←debugMessages = 1;

int SPCR←ignoreErrors = 0;


void SPCR←error(s)
char *s;
{
    if (SPCR←debugMessages) fprintf(stderr, "%s\n", s);
    if (!SPCR←ignoreErrors) abort(s);
}

void XR←Panic(s)
char *s;
{
    SPCR←error(s);
}

void
SPCR←NotImplemented(s)
char *s;
{
	if (SPCR←debugMessages)
		fprintf(stderr, "%s not implemented in stubbed pcr\n", s);
	SPCR←error("unimplemented primitive\n");
}


/*
 * Unix error return code
 */

int *
XR←GetErrnoAddress()
{
    extern int errno;

    return( &errno );
}

/* Nobody actually looks at these.  We save them just in case ... */
XR←HandlerProc SPCR←handler←proc;
XR←Pointer SPCR←handler←data;

void
XR←RegisterHandler (proc, data)
    XR←HandlerProc proc;
    XR←Pointer data;
{
    if( (proc != NIL) || (data == NIL) ) {
        SPCR←handler←proc = proc;
    } else {
        SPCR←handler←proc = (XR←HandlerProc)
            (((XR←MesaProc)(data))->mp←proc);
    }
    SPCR←handler←data = data;
}

XR←Fildes SPCR←map←descr(d)
XR←Fildes d;
{
    switch(d) {
    	case -3:
    		/* XR←MSG←STDOUT in standard PCR */
    		return(XR←MSG←STDOUT);
    	case -2:
    		/* XR←MSG←STDIN in standard PCR */
    		return(XR←MSG←STDIN);
    	default:
    		return(d);
    }
}

/* A slight change to the default behavior, to allow for standard PCR	*/
/* XR←MSG←STDOUT def.							*/
int XR←Write(fd, buf, nbyte)
int fd;
char * buf;
int nbyte;
{
    return(write(SPCR←map←descr(fd), buf, nbyte));
}

int XR←WriteV(fd, iov, iovcnt)
int fd;
struct iovec *iov;
int iovcnt;
{
    return(writev(SPCR←map←descr(fd), iov, iovcnt));
}

/* Ditto for XR←MSG←STDIN.		*/
int XR←Read(fd, buf, nbyte)
int fd;
char * buf;
int nbyte;
{
    return(read(SPCR←map←descr(fd), buf, nbyte));
}

int XR←ReadV(fd, iov, iovcnt)
int fd;
struct iovec *iov;
int iovcnt;
{
    return(readv(SPCR←map←descr(fd), iov, iovcnt));
}

# define BUFSZ 1024
void
XR←Msg( descriptor, fmt, a0, a1, a2, a3, a4, a5, a6, a7 )
    int descriptor;
    char *fmt;
    int a0, a1, a2, a3, a4, a5, a6, a7;
{
    char buf[BUFSZ+1];
    
    buf[BUFSZ] = 1;
    sprintf(buf, fmt, a0, a1, a2, a3, a4, a5, a6, a7);
    if (buf[BUFSZ] != 1) {
    	SPCR←error("XR←Msg wrote past end of buffer");
    }
    XR←Write(descriptor, buf, strlen(buf));
}

void
XR←FPrintF( descriptor, fmt, a0, a1, a2, a3, a4, a5, a6, a7 )
    int descriptor;
    char *fmt;
    int a0, a1, a2, a3, a4, a5, a6, a7;
{
    XR←Msg(descriptor, fmt, a0, a1, a2, a3, a4, a5, a6, a7);
}


char *
XR←SPrintF( buf, fmt, a0, a1, a2, a3, a4, a5, a6, a7 )
    char * buf;
    char *fmt;
    int a0, a1, a2, a3, a4, a5, a6, a7;
{
    sprintf(buf, fmt, a0, a1, a2, a3, a4, a5, a6, a7);
    return(buf + strlen(buf));
}


int
XR←MsgWrite (buf, nbytes)
    char *buf;
    int nbytes;
{    
    return(XR←Write(XR←MSG←STDOUT, buf, nbytes) );   
}


#define DevNullOrFile(filename)     ((filename) != 0 \
                                    ? (filename)     \
                                    : "/dev/null")
#define maxStrlenOfStrAndDevNull(s) ((s) != 0        \
                                    ? strlen((s))    \
                                    : (sizeof("/dev/null") - 1))

int
XR←Spawn(cmd, in, out, err)
    char *cmd;
    char *in;	/* NIL, "" => /dev/null */
    char *out;
    char *err;
{
	int len = strlen(cmd) + maxStrlenOfStrAndDevNull(in) +
	          maxStrlenOfStrAndDevNull(out) + maxStrlenOfStrAndDevNull(err)
	          + 11;
		 /* 11 == strlen(" < ")+strlen(" > ")+strlen(" 2> ")+'\0' */
		       
	char *p = (char *)GC←malloc(len);
	
	sprintf(p, "%s < %s > %s 2> %s", cmd, DevNullOrFile(in),
	        DevNullOrFile(out), DevNullOrFile(err));
	        
	return system(p);
}

void XR←GCollect() { GC←gcollect(); }

unsigned XR←GCHeapSize()
{
    return(GC←heapsize);
}

unsigned XR←GCTotalByteCount()
{
    return(WORDS←TO←BYTES(GC←words←allocd←before←gc + GC←words←allocd));
}

# define I←DONT←KNOW 17

unsigned XR←GCTotalObjectCount()
{
    return(I←DONT←KNOW);
}

unsigned XR←GCCurrentByteCount()
{
    return(I←DONT←KNOW);
}

unsigned XR←GCCurrentObjectCount()
{
    return(I←DONT←KNOW);
}

static unsigned bytesAfterWhichToCollect;
	/* Not really, but we try to lie consistently ...	*/

unsigned XR←SetBytesAfterWhichToCollect(n)
unsigned n;
{
    unsigned old = bytesAfterWhichToCollect;
    
    bytesAfterWhichToCollect = n;
    return(old);
}

/* If p is a valid pointer to an object, return a pointer to the */
/* beginning of that object.  Otherwise return NIL.		 */
/* If szp is nonNIL, save the objects size in bytes in *szp.	 */
/* If ptrfreep is nonNIL, set *ptrfreep to 0 or 1 depending on   */
/* whether or not the object is pointer free.			 */
/* An object may be considered valid even though it is about to  */
/* be garbage collected because it was inaccessible during the   */
/* last mark phase.  A nonNIL result does guarantee that the     */
/* object can be read without generating a fault.                */
XR←Pointer XR←PtrTest(p, szp, ptrfreep)
void * p;  /* May be any 32 bit integer */
long * szp;
int * ptrfreep;
{
    char * base = (char *)GC←base(p);
    struct hblk * h = HBLKPTR(base);
    hdr * hhdr = HDR(h);
    
    if (base == 0) return(0);
    if (szp != 0) *szp = (long)GC←size(base);
    if (ptrfreep != 0) {
    	struct hblk * h = HBLKPTR(base);
        hdr * hhdr = HDR(h);
        if (hhdr -> hb←obj←kind == PTRFREE) {
            *ptrfreep = 1;
        } else {
            *ptrfreep = 0;
        }
    }
    return((XR←Pointer)base);
}

#undef XR←GetErrno
#undef XR←SetErrno

int XR←GetErrno() { return(errno); }

int XR←SetErrno(x) int x; { errno = x; }

XR←Pointer SPCR←freeMemPtr = (XR←Pointer)0x20000000;

XR←Pointer
XR←VMReserve (bytes)
    unsigned bytes;
{
    XR←Pointer result = SPCR←freeMemPtr;
    unsigned ps = 4096;		/* check if this is ok */
    
    SPCR←freeMemPtr += (bytes + ps - 1) & ~(ps - 1);
    return(result);
}

/* Force the right GC routines to be inncluded in .o */
void SPCR←require() {
	GC←malloc(0); GC←realloc(0, 0); GC←free(0); GC←malloc←atomic(0);
}

bool XR←StackPushTest(nbytes)
int nbytes;
{
    char dummy;
#   define SLOP 10000
    struct rlimit rl;
    
    if (getrlimit(RLIMIT←STACK, &rl) < 0) {
        SPCR←error("XR←StackPushTest");
    }
    if ((char *)GC←stackbottom - &dummy + SLOP + nbytes >= rl.rlim←cur) {
        return(FALSE);
    } else {
        return(TRUE);
    }
}


/* the following are not (readily) available in solaris, so we stub them */
/* most of the code is taken from (or patterned after) ppcr's XR←UIOImpl.c */

int
XR←GetRUsage()
{

    return -ENOSYS;
}


/* this is a guess  -- we'll see how it works */
int
XR←GetDTableSize1(XR←FDKind kind)
{
    switch( kind ) {
      case XR←FD←KIND←STD:
        return 32;
      default:
        return -EINVAL;
    }
}

int
XR←GetDTableSize(void)
{
    return XR←GetDTableSize1(XR←FD←KIND←STD);
}

int
XR←GetHostID(void)
{
    return -ENOSYS;
}

int 
XR←GetDomainName(char *name, int nameLen)
{
    return -ENOMEM;
}

int
XR←KillPG(int pgrp, int sig)
{
    return -1;
}

int
XR←GetPageSize(void)
{
    return 4096;
}

int
XR←GetHostName(char *name, int nameLen)
{
    struct utsname utsn;

    uname(&utsn);
    strncpy( name, utsn.nodename, nameLen );
    return 0;
}

struct XR←timeval {
	long	tv←sec;		/* seconds */
	long	tv←usec;	/* and microseconds */
};

struct XR←timezone {
	int	tz←minuteswest;	/* minutes west of Greenwich */
	int	tz←dsttime;	/* type of dst correction */
};


int
XR←GetTimeOfDay(struct XR←timeval *xtp, struct XR←timezone *xtzp)
{
    int ans = 0;
    int saverr = 0;
    struct timeval t;
    
    if ( xtp != NIL ) {
        ans = gettimeofday(&t);
        if ( ans != (-1) ) {
            xtp->tv←sec = t.tv←sec;
            xtp->tv←usec = t.tv←usec;
        } else {
            saverr =  - errno;
            }
    }
    
    if( xtzp != NIL ) {
        xtzp->tz←minuteswest = timezone / 60;		/* is in seconds */
        xtzp->tz←dsttime = daylight;
    }
    
    return saverr;
}


typedef   unsigned short XR←mode←t;
typedef   long XR←time←t;
typedef   int XR←size←t;
typedef	short	XR←dev←t;
typedef	long	XR←off←t;
typedef	unsigned long	XR←ino←t;
typedef	unsigned short	XR←uid←t;
typedef	unsigned short	XR←gid←t;

struct	XR←stat {
	XR←dev←t	xrst←dev;
	XR←ino←t	xrst←ino;
	XR←mode←t	xrst←mode;
	short	xrst←nlink;
	XR←uid←t	xrst←uid;
	XR←gid←t	xrst←gid;
	XR←dev←t	xrst←rdev;
	XR←off←t	xrst←size;
	XR←time←t	xrst←atime;
	int	xrst←spare1;
	XR←time←t	xrst←mtime;
	int	xrst←spare2;
	XR←time←t	xrst←ctime;
	int	xrst←spare3;
	long	xrst←blksize;
	long	xrst←blocks;
	long	xrst←spare4[2];
};


int
XR←FStat(XR←Fildes fildes, struct XR←stat *buf)
{
   struct stat sysstat;
   int ans;
   
   if( buf == NIL ) return -EFAULT;
   ans = fstat(fildes, &sysstat);
   if (ans == -1)  return -errno;
   
   (void)ConvertStat( buf, &sysstat);
   return 0;
}

int
XR←Stat(char *path, struct XR←stat *buf)
{
    int ans;
    struct stat sysstat;

    if( buf == NIL ) return -EFAULT;
    ans = stat( path, &sysstat );
    if (ans == -1) return -errno;
    (void)ConvertStat( buf, &sysstat);
    return 0;
}


int ConvertStat( struct XR←stat *xs, struct stat *ps)
{
    if( (xs == NIL) || (ps == NIL) ) return;
    xs->xrst←mode = ps->st←mode;
    xs->xrst←ino = ps->st←ino;
    xs->xrst←dev = ps->st←dev;
    xs->xrst←nlink = ps->st←nlink;
    xs->xrst←uid = ps->st←uid;
    xs->xrst←gid = ps->st←gid;
    xs->xrst←size = ps->st←size;
    xs->xrst←atime = ps->st←atime;
    xs->xrst←mtime = ps->st←mtime;
    xs->xrst←ctime = ps->st←ctime;
    xs->xrst←blksize = 4096; /* hack, hack */
    xs->xrst←blocks = xs->xrst←size / xs->xrst←blksize; /* hack, hack */
    return 0;
}


/* having trouble with lseek - seems to return fildes as result */
/*  when defined as XR←SYSCALL3(LSeek,lseek) */

int
XR←LSeek(int fildes, int offset, int whence)
{
    int res;
    res = lseek(fildes, offset, whence);
    return (res);
}