#include <nt.h>
#include <ntrtl.h>

#include "psxmsg.h"
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/times.h>
#include <fcntl.h>
#include <stdio.h>

#include "tsttmp.h"	// defines DbgPrint as printf 

extern int errno;

VOID p0(VOID);
VOID p1(VOID);
VOID ex0(VOID);
VOID a0(VOID);
VOID s0(VOID);
VOID call0(VOID);
VOID f1(VOID);
VOID f2(VOID);
VOID p0foo(VOID);

CONTEXT a0JumpBuff;

int _CRTAPI1 main(int argc, char *argv[])
{

    pid_t self;
    PCH p,t;
    PTEB ThreadInfo;

    ThreadInfo = NtCurrentTeb();

    self = getpid();

    DbgPrint("tstfork: My pid is %lx Argc = %lx\n",self,argc);
    DbgPrint("tstfork: StackBase %lx\n",ThreadInfo->NtTib.StackBase);
    DbgPrint("tstfork: StackLimit %lx\n",ThreadInfo->NtTib.StackLimit);
    DbgPrint("tstfork: ClientId %lx.%lx\n",ThreadInfo->ClientId.UniqueProcess,ThreadInfo->ClientId.UniqueThread);

    while(argc--){
        p = *argv++;
        t = p;
        while(*t++);
        DbgPrint("Argv --> %s\n",p);
    }


    p0();
    p1();
    ex0();
    a0();
    s0();
    call0();
    f1();
    f2();

    _exit(2);
    return 1;

}

VOID
p0foo(){}

ULONG
p0touch(
    IN PUCHAR pch,
    IN ULONG nb
    )
{
    ULONG ct = 0;

    DbgPrint("p0touch: pch %lx nb %lx\n",pch,nb);

    while (nb--) {
        ct += *pch++;
    }
    return ct;
}

VOID
p0()
{
    int fildes[2];
    int psxst;
    UCHAR buffer[525];
    UCHAR buf[525];
    int i;

    for(i=0;i<525;i++){
        buf[i] = (char)(i&255);
    }

    DbgPrint("p0:++\n");

    //
    // Create a pipe
    //

    psxst = pipe(fildes);

    if ( psxst ) {
        DbgPrint("p0: pipe() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }

    DbgPrint("p0: fildes[0] = %lx filedes[1] = %lx\n",fildes[0],fildes[1]);

    //
    // Test write followed by read
    //

    psxst = write(fildes[1],"Hello World\n",13);
    if ( psxst < 0 ) {
        DbgPrint("p0: write() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }

    DbgPrint("p0: write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);

    psxst = read(fildes[0],buffer,32);
    if ( psxst < 0 ) {
        DbgPrint("p0: read() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }

    p0touch(buffer,psxst);
    DbgPrint("p0: read() success transfered %lx bytes from fd %lx %s\n",psxst,fildes[1],buffer);
    //
    // End write followed by read
    //


    //
    // assume parents read will happen before childs write.
    // parents first read blocks testing write unblocks read
    //

    if ( !fork() ) {


        DbgPrint("p0:child pid %lx\n",getpid());
        DbgPrint("p0:child writing\n");

        sleep(8);

        psxst = write(fildes[1],"From Child\n\0",12);
        if ( psxst < 0 ) {
            DbgPrint("p0:child write() failed st = %lx errno %lx\n",psxst,errno);
            return;
        }
        DbgPrint("p0:child write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);

        psxst = write(fildes[1],"Small Write\n\0",13);
        if ( psxst < 0 ) {
            DbgPrint("p0:child write() failed st = %lx errno %lx\n",psxst,errno);
            return;
        }
        DbgPrint("p0:child write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);

        //
        // this write should block and get unblocked when read of
        // previous write completes
        //
        p0foo();
        psxst = write(fildes[1],buf,514);
        if ( psxst < 0 ) {
            DbgPrint("p0:child write() failed st = %lx errno %lx\n",psxst,errno);
            return;
        }
        DbgPrint("p0:child write() success transfered %lx bytes to fd %lx\n",psxst,fildes[1]);

        _exit(1);
    }

    DbgPrint("p0:parent reading\n");
    psxst = read(fildes[0],buffer,12);

    if ( psxst < 0 ) {
        DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }
    p0touch(buffer,psxst);
    DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx %s\n",psxst,fildes[1],buffer);

    DbgPrint("p0:parent sleeping\n");
    sleep(12);
    DbgPrint("p0:parent back from sleep\n");

    DbgPrint("p0:parent reading\n");
    psxst = read(fildes[0],buffer,13);
    if ( psxst < 0 ) {
        DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }
    p0touch(buffer,psxst);
    DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx %s\n",psxst,fildes[1],buffer);

    psxst = read(fildes[0],buffer,512);
    if ( psxst < 0 ) {
        DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }
    DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx\n",psxst,fildes[1]);

    for(i=0;i<psxst;i++){
        if ( buf[i] != buffer[i] ) {
            DbgPrint("p0: Compare failed. i %lx master %lx vs. %lx\n",i,buf[i],buffer[i]);
        }
    }

    psxst = read(fildes[0],buffer,512);
    if ( psxst < 0 ) {
        DbgPrint("p0:parent read() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }
    DbgPrint("p0:parent read() success transfered %lx bytes from fd %lx\n",psxst,fildes[1]);

    for(i=0;i<psxst;i++){
        if ( buf[i] != buffer[i] ) {
            DbgPrint("p0: Compare failed. i %lx master %lx vs. %lx\n",i,buf[i],buffer[i]);
        }
    }
    wait(NULL);

    close(fildes[0]);
    close(fildes[1]);

    DbgPrint("p0:--\n");

}

VOID
p1()
{
    int fildes[2];
    int psxst;
    UCHAR buffer[525];
    UCHAR buf[525];
    int i;

    for(i=0;i<525;i++){
        buf[i] = (char)(i&255);
    }

    DbgPrint("p1:++\n");

    //
    // Create a pipe
    //

    psxst = pipe(fildes);

    if ( psxst ) {
        DbgPrint("p1: pipe() failed st = %lx errno %lx\n",psxst,errno);
        return;
    }

    DbgPrint("p1: fildes[0] = %lx filedes[1] = %lx\n",fildes[0],fildes[1]);

    //
    // Test write to readonly fd
    //

    psxst = write(fildes[0],"Hello World\n",13);
    if ( psxst < 0 ) {
        DbgPrint("p1: write test NT_SUCCESS %lx errno %lx\n",psxst,errno);
    }

    //
    // Test read to writeonly fd
    //

    psxst = read(fildes[1],buf,13);
    if ( psxst < 0 ) {
        DbgPrint("p1: read test NT_SUCCESS %lx errno %lx\n",psxst,errno);
    }


    //
    // Close Write Handle
    //

    close(fildes[1]);

    //
    // Test read w/ no writers
    //

    psxst = read(fildes[0],buffer,32);
    if ( psxst == 0 ) {
        DbgPrint("p1: read test NT_SUCCESS %lx\n",psxst);
    }

    close(fildes[0]);

    DbgPrint("p1:--\n");

}

VOID
ex0()
{
    PCH Arg[3], Env[4];
    struct tms TimeBuffer;
    clock_t TimesRtn;
    int fildes[2];
    int rc;

    DbgPrint("ex0:++\n");

    rc = pipe(fildes);
    ASSERT(rc==0 && fildes[0] == 0 && fildes[1] == 1);

    if ( !fork() ) {

  	if ( !fork() ) {
  
              Arg[0]="tsthello.n10";
              Arg[1]=(PCH)NULL;
              Env[0]="NTUSER=MARKL";
              Env[1]=(PCH)NULL;
  
              //
              // We should not have any accumulated child times
              //
  
              TimesRtn = times(&TimeBuffer);
              ASSERT(TimesRtn != 0);
              ASSERT(TimeBuffer.tms_cstime == 0 && TimeBuffer.tms_cutime == 0);
  
              write(1,&TimeBuffer,sizeof(TimeBuffer));
              execve("\\DosDevices\\D:\\PSX\\TSTHELLO.exe",Arg,Env);
  
              DbgPrint("ex0:child returned from exec ? errno %lx\n",errno);
  
              _exit(1);
          }

          wait(NULL);
  
          //
          // We should not have any accumulated child times
          //
  
          TimesRtn = times(&TimeBuffer);
          ASSERT(TimesRtn != 0);
          DbgPrint("tms_cstime %lx tms_cutime %lx\n",TimeBuffer.tms_cstime,TimeBuffer.tms_cutime);
          ASSERT(TimeBuffer.tms_cstime != 0 || TimeBuffer.tms_cutime != 0);
  
          _exit(2);
      }

    wait(NULL);

    DbgPrint("ex0:parent wait satisfied \n");

    close(fildes[0]);
    close(fildes[1]);

    DbgPrint("ex0:--\n");

}

VOID
call0()
{
    VOID PdxNullPosixApi();

#ifdef SIMULATOR
    for(i=0;i<10;i++) {
        begin = rnuminstr();
        PdxNullPosixApi();
        end = rnuminstr();

        DbgPrint("Call Time 0x%lx 0x%lx 0x%lx dec %ld\n",begin, end, end - begin,end-begin);
    }
#endif // SIMULATOR

}

VOID
f1()
{
    pid_t child,wchild;

    DbgPrint("f1:+++\n");

    child = fork();

    if ( child == 0 ) {
        DbgPrint("tstfork_child: I am the child %lx\n",getpid());
        _exit(1);
    }

    DbgPrint("tstfork_parent: My child is %lx\n",child);

    wchild = wait(NULL);

    DbgPrint("tstfork_parent: Wait on child %lx satisfied %lx errno %lx\n",
        child,
        wchild,
        errno
        );
    DbgPrint("f1:---\n");
}

void
_CRTAPI1
f2catcher(
 IN int sig
 )
{
    DbgPrint("f2catcher, signal == %lx\n",sig);
}

VOID
f2()
{
    struct sigaction act, oact;
    pid_t child,wchild,parent;
    int psxst;


    //
    // Send signal to parent which sould be in wait. Parent should
    // get EINTR from wait. If it retries, then wait will succeed.
    //

    DbgPrint("f2:+++\n");

    act.sa_handler = f2catcher;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;

    if (sigaction(SIGUSR1, &act ,&oact) ) {
        DbgPrint("f2: fail sigaction errno %lx\n",errno);
        _exit(-1);
    }

    child = fork();
    if( !child ) {

        DbgPrint("tstfork_child: I am the child %lx parent %lx\n",
            getpid(),
            (parent = getppid())
            );

        DbgPrint("tstfork_child: Killing SIGUSR1 parent %lx\n",parent);

        psxst = kill(parent,SIGUSR1);

        if ( psxst < 0 ) {
            DbgPrint("tstfork_child: kill failed %lx errno %lx\n",psxst,errno);
            _exit(errno);
        }

        _exit(1);
    }

    DbgPrint("tstfork_parent: My child is %lx\n",child);

    wchild = wait(NULL);

    DbgPrint("tstfork_parent: Wait on child %lx satisfied %lx errno %lx\n",
        child,
        wchild,
        errno
        );

    if ( wchild < 0 && errno == EINTR ) {
        DbgPrint("tstfork_parent: wait interrupted by SIGUSR1. Rewait\n");

        wchild = wait(NULL);

        DbgPrint("tstfork_parent: Wait on child %lx satisfied %lx errno %lx\n",
            child,
            wchild,
            errno
            );
    }

    DbgPrint("f2:---\n");
}

void
_CRTAPI1
a0catcher(
 IN int sig
 )
{
    DbgPrint("a0catcher, signal == %lx long jumping...\n",sig);
    NtContinue(&a0JumpBuff,FALSE);
}

VOID
a0()
{
    struct sigaction act, oact;
    unsigned int previous;
    NTSTATUS st;
    sigset_t eset;

    //
    // Call alarm and then sit in a loop
    //

    DbgPrint("a0:+++\n");

    act.sa_handler = a0catcher;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;

    if (sigaction(SIGALRM, &act ,&oact) ) {
        DbgPrint("a0: fail sigaction errno %lx\n",errno);
        _exit(-1);
    }

    a0JumpBuff.ContextFlags = CONTEXT_FULL;

    st = NtGetContextThread(NtCurrentThread(),&a0JumpBuff);

#ifdef MIPS
    a0JumpBuff.IntV0 = 0x12345678;
#endif

#ifdef i386
    a0JumpBuff.Eax = 0x12345678;
#endif

    if (st == STATUS_SUCCESS) {
        previous = alarm(2);
        for(;;);
    }

    sigemptyset(&eset);
    sigprocmask(SIG_SETMASK,&eset,NULL);

    DbgPrint("a0: Longjumpp st = %lx\n",st);

    //
    // start a 10 second alarm and then cancel it
    //

    a0JumpBuff.ContextFlags = CONTEXT_FULL;

    st = NtGetContextThread(NtCurrentThread(),&a0JumpBuff);

#ifdef MIPS
    a0JumpBuff.IntV0 = 0x12345678;
#endif

#ifdef i386
    a0JumpBuff.Eax = 0x12345678;
#endif

    if (st == STATUS_SUCCESS) {
        previous = alarm(10);
        sleep(1);
        previous = alarm(0);
        DbgPrint("Previous = %lx\n",previous);
    }

    sigemptyset(&eset);
    sigprocmask(SIG_SETMASK,&eset,NULL);

    DbgPrint("a0:---\n");
}

VOID
s0()
{

    LARGE_INTEGER BeginTime,EndTime;
    unsigned int psxst;

    DbgPrint("s0:+++\n");

    NtQuerySystemTime(&BeginTime);

    DbgPrint("s0: sleeping\n");

    psxst = sleep(1);

    NtQuerySystemTime(&EndTime);

    DbgPrint("s0: sleep(1) returned %lx\n",psxst);
    DbgPrint("s0: BeginTime %lx %lx\n",BeginTime.HighPart,BeginTime.LowPart);
    DbgPrint("s0: EndTime %lx %lx\n",EndTime.HighPart,EndTime.LowPart);

    DbgPrint("s0:---\n");

}