/* perlhost.h
 *
 * (c) 1999 Microsoft Corporation. All rights reserved.
 * Portions (c) 1999 ActiveState Tool Corp, http://www.ActiveState.com/
 *
 *    You may distribute under the terms of either the GNU General Public
 *    License or the Artistic License, as specified in the README file.
 */

#ifndef UNDER_CE
#define CHECK_HOST_INTERP
#endif

#ifndef ___PerlHost_H___
#define ___PerlHost_H___

#ifndef UNDER_CE
#include <signal.h>
#endif
#include "iperlsys.h"
#include "vmem.h"
#include "vdir.h"

#ifndef WC_NO_BEST_FIT_CHARS
#  define WC_NO_BEST_FIT_CHARS 0x00000400
#endif

START_EXTERN_C
extern char *	g_getlogin(void);
END_EXTERN_C

class CPerlHost
{
public:
    /* Constructors */
    CPerlHost(void);
    CPerlHost(struct IPerlMem** ppMem, struct IPerlMem** ppMemShared,
		 struct IPerlMem** ppMemParse, struct IPerlEnv** ppEnv,
		 struct IPerlStdIO** ppStdIO, struct IPerlLIO** ppLIO,
		 struct IPerlDir** ppDir, struct IPerlSock** ppSock,
		 struct IPerlProc** ppProc);
    CPerlHost(CPerlHost& host);
    ~CPerlHost(void);

    static CPerlHost* IPerlMem2Host(struct IPerlMem* piPerl);
    static CPerlHost* IPerlMemShared2Host(struct IPerlMem* piPerl);
    static CPerlHost* IPerlMemParse2Host(struct IPerlMem* piPerl);
    static CPerlHost* IPerlEnv2Host(struct IPerlEnv* piPerl);
    static CPerlHost* IPerlStdIO2Host(struct IPerlStdIO* piPerl);
    static CPerlHost* IPerlLIO2Host(struct IPerlLIO* piPerl);
    static CPerlHost* IPerlDir2Host(struct IPerlDir* piPerl);
    static CPerlHost* IPerlSock2Host(struct IPerlSock* piPerl);
    static CPerlHost* IPerlProc2Host(struct IPerlProc* piPerl);

    BOOL PerlCreate(void);
    int PerlParse(int argc, char** argv, char** env);
    int PerlRun(void);
    void PerlDestroy(void);

/* IPerlMem */
    /* Locks provided but should be unnecessary as this is private pool */
    inline void* Malloc(size_t size) { return m_pVMem->Malloc(size); };
    inline void* Realloc(void* ptr, size_t size) { return m_pVMem->Realloc(ptr, size); };
    inline void Free(void* ptr) { m_pVMem->Free(ptr); };
    inline void* Calloc(size_t num, size_t size)
    {
	size_t count = num*size;
	void* lpVoid = Malloc(count);
	if (lpVoid)
	    ZeroMemory(lpVoid, count);
	return lpVoid;
    };
    inline void GetLock(void) { m_pVMem->GetLock(); };
    inline void FreeLock(void) { m_pVMem->FreeLock(); };
    inline int IsLocked(void) { return m_pVMem->IsLocked(); };

/* IPerlMemShared */
    /* Locks used to serialize access to the pool */
    inline void GetLockShared(void) { m_pVMemShared->GetLock(); };
    inline void FreeLockShared(void) { m_pVMemShared->FreeLock(); };
    inline int IsLockedShared(void) { return m_pVMemShared->IsLocked(); };
    inline void* MallocShared(size_t size)
    {
	void *result;
	GetLockShared();
	result = m_pVMemShared->Malloc(size);
	FreeLockShared();
	return result;
    };
    inline void* ReallocShared(void* ptr, size_t size)
    {
	void *result;
	GetLockShared();
	result = m_pVMemShared->Realloc(ptr, size);
	FreeLockShared();
	return result;
    };
    inline void FreeShared(void* ptr)
    {
	GetLockShared();
	m_pVMemShared->Free(ptr);
	FreeLockShared();
    };
    inline void* CallocShared(size_t num, size_t size)
    {
	size_t count = num*size;
	void* lpVoid = MallocShared(count);
	if (lpVoid)
	    ZeroMemory(lpVoid, count);
	return lpVoid;
    };

/* IPerlMemParse */
    /* Assume something else is using locks to mangaging serialize
       on a batch basis
     */
    inline void GetLockParse(void) { m_pVMemParse->GetLock(); };
    inline void FreeLockParse(void) { m_pVMemParse->FreeLock(); };
    inline int IsLockedParse(void) { return m_pVMemParse->IsLocked(); };
    inline void* MallocParse(size_t size) { return m_pVMemParse->Malloc(size); };
    inline void* ReallocParse(void* ptr, size_t size) { return m_pVMemParse->Realloc(ptr, size); };
    inline void FreeParse(void* ptr) { m_pVMemParse->Free(ptr); };
    inline void* CallocParse(size_t num, size_t size)
    {
	size_t count = num*size;
	void* lpVoid = MallocParse(count);
	if (lpVoid)
	    ZeroMemory(lpVoid, count);
	return lpVoid;
    };

/* IPerlEnv */
    char *Getenv(const char *varname);
    int Putenv(const char *envstring);
    inline char *Getenv(const char *varname, unsigned long *len)
    {
	*len = 0;
	char *e = Getenv(varname);
	if (e)
	    *len = strlen(e);
	return e;
    }
    void* CreateChildEnv(void) { return CreateLocalEnvironmentStrings(*m_pvDir); };
    void FreeChildEnv(void* pStr) { FreeLocalEnvironmentStrings((char*)pStr); };
    char* GetChildDir(void);
    void FreeChildDir(char* pStr);
    void Reset(void);
    void Clearenv(void);

    inline LPSTR GetIndex(DWORD &dwIndex)
    {
	if(dwIndex < m_dwEnvCount)
	{
	    ++dwIndex;
	    return m_lppEnvList[dwIndex-1];
	}
	return NULL;
    };

protected:
    LPSTR Find(LPCSTR lpStr);
    void Add(LPCSTR lpStr);

    LPSTR CreateLocalEnvironmentStrings(VDir &vDir);
    void FreeLocalEnvironmentStrings(LPSTR lpStr);
    LPSTR* Lookup(LPCSTR lpStr);
    DWORD CalculateEnvironmentSpace(void);

public:

/* IPerlDIR */
    virtual int Chdir(const char *dirname);

/* IPerllProc */
    void Abort(void);
    void Exit(int status);
    void _Exit(int status);
    int Execl(const char *cmdname, const char *arg0, const char *arg1, const char *arg2, const char *arg3);
    int Execv(const char *cmdname, const char *const *argv);
    int Execvp(const char *cmdname, const char *const *argv);

    inline VMem* GetMemShared(void) { m_pVMemShared->AddRef(); return m_pVMemShared; };
    inline VMem* GetMemParse(void) { m_pVMemParse->AddRef(); return m_pVMemParse; };
    inline VDir* GetDir(void) { return m_pvDir; };

public:

    struct IPerlMem	    m_hostperlMem;
    struct IPerlMem	    m_hostperlMemShared;
    struct IPerlMem	    m_hostperlMemParse;
    struct IPerlEnv	    m_hostperlEnv;
    struct IPerlStdIO	    m_hostperlStdIO;
    struct IPerlLIO	    m_hostperlLIO;
    struct IPerlDir	    m_hostperlDir;
    struct IPerlSock	    m_hostperlSock;
    struct IPerlProc	    m_hostperlProc;

    struct IPerlMem*	    m_pHostperlMem;
    struct IPerlMem*	    m_pHostperlMemShared;
    struct IPerlMem*	    m_pHostperlMemParse;
    struct IPerlEnv*	    m_pHostperlEnv;
    struct IPerlStdIO*	    m_pHostperlStdIO;
    struct IPerlLIO*	    m_pHostperlLIO;
    struct IPerlDir*	    m_pHostperlDir;
    struct IPerlSock*	    m_pHostperlSock;
    struct IPerlProc*	    m_pHostperlProc;

    inline char* MapPathA(const char *pInName) { return m_pvDir->MapPathA(pInName); };
    inline WCHAR* MapPathW(const WCHAR *pInName) { return m_pvDir->MapPathW(pInName); };
protected:

    VDir*   m_pvDir;
    VMem*   m_pVMem;
    VMem*   m_pVMemShared;
    VMem*   m_pVMemParse;

    DWORD   m_dwEnvCount;
    LPSTR*  m_lppEnvList;
    BOOL    m_bTopLevel;	// is this a toplevel host?
    static long num_hosts;
public:
    inline  int LastHost(void) { return num_hosts == 1L; };
    struct interpreter *host_perl;
};

long CPerlHost::num_hosts = 0L;

extern "C" void win32_checkTLS(struct interpreter *host_perl);

#define STRUCT2RAWPTR(x, y) (CPerlHost*)(((LPBYTE)x)-offsetof(CPerlHost, y))
#ifdef CHECK_HOST_INTERP
inline CPerlHost* CheckInterp(CPerlHost *host)
{
 win32_checkTLS(host->host_perl);
 return host;
}
#define STRUCT2PTR(x, y) CheckInterp(STRUCT2RAWPTR(x, y))
#else
#define STRUCT2PTR(x, y) STRUCT2RAWPTR(x, y)
#endif

inline CPerlHost* IPerlMem2Host(struct IPerlMem* piPerl)
{
    return STRUCT2RAWPTR(piPerl, m_hostperlMem);
}

inline CPerlHost* IPerlMemShared2Host(struct IPerlMem* piPerl)
{
    return STRUCT2RAWPTR(piPerl, m_hostperlMemShared);
}

inline CPerlHost* IPerlMemParse2Host(struct IPerlMem* piPerl)
{
    return STRUCT2RAWPTR(piPerl, m_hostperlMemParse);
}

inline CPerlHost* IPerlEnv2Host(struct IPerlEnv* piPerl)
{
    return STRUCT2PTR(piPerl, m_hostperlEnv);
}

inline CPerlHost* IPerlStdIO2Host(struct IPerlStdIO* piPerl)
{
    return STRUCT2PTR(piPerl, m_hostperlStdIO);
}

inline CPerlHost* IPerlLIO2Host(struct IPerlLIO* piPerl)
{
    return STRUCT2PTR(piPerl, m_hostperlLIO);
}

inline CPerlHost* IPerlDir2Host(struct IPerlDir* piPerl)
{
    return STRUCT2PTR(piPerl, m_hostperlDir);
}

inline CPerlHost* IPerlSock2Host(struct IPerlSock* piPerl)
{
    return STRUCT2PTR(piPerl, m_hostperlSock);
}

inline CPerlHost* IPerlProc2Host(struct IPerlProc* piPerl)
{
    return STRUCT2PTR(piPerl, m_hostperlProc);
}



#undef IPERL2HOST
#define IPERL2HOST(x) IPerlMem2Host(x)

/* IPerlMem */
void*
PerlMemMalloc(struct IPerlMem* piPerl, size_t size)
{
    return IPERL2HOST(piPerl)->Malloc(size);
}
void*
PerlMemRealloc(struct IPerlMem* piPerl, void* ptr, size_t size)
{
    return IPERL2HOST(piPerl)->Realloc(ptr, size);
}
void
PerlMemFree(struct IPerlMem* piPerl, void* ptr)
{
    IPERL2HOST(piPerl)->Free(ptr);
}
void*
PerlMemCalloc(struct IPerlMem* piPerl, size_t num, size_t size)
{
    return IPERL2HOST(piPerl)->Calloc(num, size);
}

void
PerlMemGetLock(struct IPerlMem* piPerl)
{
    IPERL2HOST(piPerl)->GetLock();
}

void
PerlMemFreeLock(struct IPerlMem* piPerl)
{
    IPERL2HOST(piPerl)->FreeLock();
}

int
PerlMemIsLocked(struct IPerlMem* piPerl)
{
    return IPERL2HOST(piPerl)->IsLocked();
}

const struct IPerlMem perlMem =
{
    PerlMemMalloc,
    PerlMemRealloc,
    PerlMemFree,
    PerlMemCalloc,
    PerlMemGetLock,
    PerlMemFreeLock,
    PerlMemIsLocked,
};

#undef IPERL2HOST
#define IPERL2HOST(x) IPerlMemShared2Host(x)

/* IPerlMemShared */
void*
PerlMemSharedMalloc(struct IPerlMem* piPerl, size_t size)
{
    return IPERL2HOST(piPerl)->MallocShared(size);
}
void*
PerlMemSharedRealloc(struct IPerlMem* piPerl, void* ptr, size_t size)
{
    return IPERL2HOST(piPerl)->ReallocShared(ptr, size);
}
void
PerlMemSharedFree(struct IPerlMem* piPerl, void* ptr)
{
    IPERL2HOST(piPerl)->FreeShared(ptr);
}
void*
PerlMemSharedCalloc(struct IPerlMem* piPerl, size_t num, size_t size)
{
    return IPERL2HOST(piPerl)->CallocShared(num, size);
}

void
PerlMemSharedGetLock(struct IPerlMem* piPerl)
{
    IPERL2HOST(piPerl)->GetLockShared();
}

void
PerlMemSharedFreeLock(struct IPerlMem* piPerl)
{
    IPERL2HOST(piPerl)->FreeLockShared();
}

int
PerlMemSharedIsLocked(struct IPerlMem* piPerl)
{
    return IPERL2HOST(piPerl)->IsLockedShared();
}

const struct IPerlMem perlMemShared =
{
    PerlMemSharedMalloc,
    PerlMemSharedRealloc,
    PerlMemSharedFree,
    PerlMemSharedCalloc,
    PerlMemSharedGetLock,
    PerlMemSharedFreeLock,
    PerlMemSharedIsLocked,
};

#undef IPERL2HOST
#define IPERL2HOST(x) IPerlMemParse2Host(x)

/* IPerlMemParse */
void*
PerlMemParseMalloc(struct IPerlMem* piPerl, size_t size)
{
    return IPERL2HOST(piPerl)->MallocParse(size);
}
void*
PerlMemParseRealloc(struct IPerlMem* piPerl, void* ptr, size_t size)
{
    return IPERL2HOST(piPerl)->ReallocParse(ptr, size);
}
void
PerlMemParseFree(struct IPerlMem* piPerl, void* ptr)
{
    IPERL2HOST(piPerl)->FreeParse(ptr);
}
void*
PerlMemParseCalloc(struct IPerlMem* piPerl, size_t num, size_t size)
{
    return IPERL2HOST(piPerl)->CallocParse(num, size);
}

void
PerlMemParseGetLock(struct IPerlMem* piPerl)
{
    IPERL2HOST(piPerl)->GetLockParse();
}

void
PerlMemParseFreeLock(struct IPerlMem* piPerl)
{
    IPERL2HOST(piPerl)->FreeLockParse();
}

int
PerlMemParseIsLocked(struct IPerlMem* piPerl)
{
    return IPERL2HOST(piPerl)->IsLockedParse();
}

const struct IPerlMem perlMemParse =
{
    PerlMemParseMalloc,
    PerlMemParseRealloc,
    PerlMemParseFree,
    PerlMemParseCalloc,
    PerlMemParseGetLock,
    PerlMemParseFreeLock,
    PerlMemParseIsLocked,
};


#undef IPERL2HOST
#define IPERL2HOST(x) IPerlEnv2Host(x)

/* IPerlEnv */
char*
PerlEnvGetenv(struct IPerlEnv* piPerl, const char *varname)
{
    return IPERL2HOST(piPerl)->Getenv(varname);
};

int
PerlEnvPutenv(struct IPerlEnv* piPerl, const char *envstring)
{
    return IPERL2HOST(piPerl)->Putenv(envstring);
};

char*
PerlEnvGetenv_len(struct IPerlEnv* piPerl, const char* varname, unsigned long* len)
{
    return IPERL2HOST(piPerl)->Getenv(varname, len);
}

int
PerlEnvUname(struct IPerlEnv* piPerl, struct utsname *name)
{
    return win32_uname(name);
}

void
PerlEnvClearenv(struct IPerlEnv* piPerl)
{
    IPERL2HOST(piPerl)->Clearenv();
}

void*
PerlEnvGetChildenv(struct IPerlEnv* piPerl)
{
    return IPERL2HOST(piPerl)->CreateChildEnv();
}

void
PerlEnvFreeChildenv(struct IPerlEnv* piPerl, void* childEnv)
{
    IPERL2HOST(piPerl)->FreeChildEnv(childEnv);
}

char*
PerlEnvGetChilddir(struct IPerlEnv* piPerl)
{
    return IPERL2HOST(piPerl)->GetChildDir();
}

void
PerlEnvFreeChilddir(struct IPerlEnv* piPerl, char* childDir)
{
    IPERL2HOST(piPerl)->FreeChildDir(childDir);
}

unsigned long
PerlEnvOsId(struct IPerlEnv* piPerl)
{
    return win32_os_id();
}

char*
PerlEnvLibPath(struct IPerlEnv* piPerl, WIN32_NO_REGISTRY_M_(const char *pl) STRLEN *const len)
{
    return win32_get_privlib(WIN32_NO_REGISTRY_M_(pl) len);
}

char*
PerlEnvSiteLibPath(struct IPerlEnv* piPerl, const char *pl, STRLEN *const len)
{
    return win32_get_sitelib(pl, len);
}

char*
PerlEnvVendorLibPath(struct IPerlEnv* piPerl, const char *pl,
		     STRLEN *const len)
{
    return win32_get_vendorlib(pl, len);
}

void
PerlEnvGetChildIO(struct IPerlEnv* piPerl, child_IO_table* ptr)
{
    win32_get_child_IO(ptr);
}

const struct IPerlEnv perlEnv =
{
    PerlEnvGetenv,
    PerlEnvPutenv,
    PerlEnvGetenv_len,
    PerlEnvUname,
    PerlEnvClearenv,
    PerlEnvGetChildenv,
    PerlEnvFreeChildenv,
    PerlEnvGetChilddir,
    PerlEnvFreeChilddir,
    PerlEnvOsId,
    PerlEnvLibPath,
    PerlEnvSiteLibPath,
    PerlEnvVendorLibPath,
    PerlEnvGetChildIO,
};

#undef IPERL2HOST
#define IPERL2HOST(x) IPerlStdIO2Host(x)

/* PerlStdIO */
FILE*
PerlStdIOStdin(struct IPerlStdIO* piPerl)
{
    return win32_stdin();
}

FILE*
PerlStdIOStdout(struct IPerlStdIO* piPerl)
{
    return win32_stdout();
}

FILE*
PerlStdIOStderr(struct IPerlStdIO* piPerl)
{
    return win32_stderr();
}

FILE*
PerlStdIOOpen(struct IPerlStdIO* piPerl, const char *path, const char *mode)
{
    return win32_fopen(path, mode);
}

int
PerlStdIOClose(struct IPerlStdIO* piPerl, FILE* pf)
{
    return win32_fclose((pf));
}

int
PerlStdIOEof(struct IPerlStdIO* piPerl, FILE* pf)
{
    return win32_feof(pf);
}

int
PerlStdIOError(struct IPerlStdIO* piPerl, FILE* pf)
{
    return win32_ferror(pf);
}

void
PerlStdIOClearerr(struct IPerlStdIO* piPerl, FILE* pf)
{
    win32_clearerr(pf);
}

int
PerlStdIOGetc(struct IPerlStdIO* piPerl, FILE* pf)
{
    return win32_getc(pf);
}

STDCHAR*
PerlStdIOGetBase(struct IPerlStdIO* piPerl, FILE* pf)
{
#ifdef FILE_base
    FILE *f = pf;
    return FILE_base(f);
#else
    return NULL;
#endif
}

int
PerlStdIOGetBufsiz(struct IPerlStdIO* piPerl, FILE* pf)
{
#ifdef FILE_bufsiz
    FILE *f = pf;
    return FILE_bufsiz(f);
#else
    return (-1);
#endif
}

int
PerlStdIOGetCnt(struct IPerlStdIO* piPerl, FILE* pf)
{
#ifdef USE_STDIO_PTR
    FILE *f = pf;
    return FILE_cnt(f);
#else
    return (-1);
#endif
}

STDCHAR*
PerlStdIOGetPtr(struct IPerlStdIO* piPerl, FILE* pf)
{
#ifdef USE_STDIO_PTR
    FILE *f = pf;
    return FILE_ptr(f);
#else
    return NULL;
#endif
}

char*
PerlStdIOGets(struct IPerlStdIO* piPerl, char* s, int n, FILE* pf)
{
    return win32_fgets(s, n, pf);
}

int
PerlStdIOPutc(struct IPerlStdIO* piPerl, int c, FILE* pf)
{
    return win32_fputc(c, pf);
}

int
PerlStdIOPuts(struct IPerlStdIO* piPerl, const char *s, FILE* pf)
{
    return win32_fputs(s, pf);
}

int
PerlStdIOFlush(struct IPerlStdIO* piPerl, FILE* pf)
{
    return win32_fflush(pf);
}

int
PerlStdIOUngetc(struct IPerlStdIO* piPerl,int c, FILE* pf)
{
    return win32_ungetc(c, pf);
}

int
PerlStdIOFileno(struct IPerlStdIO* piPerl, FILE* pf)
{
    return win32_fileno(pf);
}

FILE*
PerlStdIOFdopen(struct IPerlStdIO* piPerl, int fd, const char *mode)
{
    return win32_fdopen(fd, mode);
}

FILE*
PerlStdIOReopen(struct IPerlStdIO* piPerl, const char*path, const char*mode, FILE* pf)
{
    return win32_freopen(path, mode, (FILE*)pf);
}

SSize_t
PerlStdIORead(struct IPerlStdIO* piPerl, void *buffer, Size_t size, Size_t count, FILE* pf)
{
    return win32_fread(buffer, size, count, pf);
}

SSize_t
PerlStdIOWrite(struct IPerlStdIO* piPerl, const void *buffer, Size_t size, Size_t count, FILE* pf)
{
    return win32_fwrite(buffer, size, count, pf);
}

void
PerlStdIOSetBuf(struct IPerlStdIO* piPerl, FILE* pf, char* buffer)
{
    win32_setbuf(pf, buffer);
}

int
PerlStdIOSetVBuf(struct IPerlStdIO* piPerl, FILE* pf, char* buffer, int type, Size_t size)
{
    return win32_setvbuf(pf, buffer, type, size);
}

void
PerlStdIOSetCnt(struct IPerlStdIO* piPerl, FILE* pf, int n)
{
#ifdef STDIO_CNT_LVALUE
    FILE *f = pf;
    FILE_cnt(f) = n;
#endif
}

void
PerlStdIOSetPtr(struct IPerlStdIO* piPerl, FILE* pf, STDCHAR * ptr)
{
#ifdef STDIO_PTR_LVALUE
    FILE *f = pf;
    FILE_ptr(f) = ptr;
#endif
}

void
PerlStdIOSetlinebuf(struct IPerlStdIO* piPerl, FILE* pf)
{
    win32_setvbuf(pf, NULL, _IOLBF, 0);
}

int
PerlStdIOPrintf(struct IPerlStdIO* piPerl, FILE* pf, const char *format,...)
{
    va_list(arglist);
    va_start(arglist, format);
    return win32_vfprintf(pf, format, arglist);
}

int
PerlStdIOVprintf(struct IPerlStdIO* piPerl, FILE* pf, const char *format, va_list arglist)
{
    return win32_vfprintf(pf, format, arglist);
}

Off_t
PerlStdIOTell(struct IPerlStdIO* piPerl, FILE* pf)
{
    return win32_ftell(pf);
}

int
PerlStdIOSeek(struct IPerlStdIO* piPerl, FILE* pf, Off_t offset, int origin)
{
    return win32_fseek(pf, offset, origin);
}

void
PerlStdIORewind(struct IPerlStdIO* piPerl, FILE* pf)
{
    win32_rewind(pf);
}

FILE*
PerlStdIOTmpfile(struct IPerlStdIO* piPerl)
{
    return win32_tmpfile();
}

int
PerlStdIOGetpos(struct IPerlStdIO* piPerl, FILE* pf, Fpos_t *p)
{
    return win32_fgetpos(pf, p);
}

int
PerlStdIOSetpos(struct IPerlStdIO* piPerl, FILE* pf, const Fpos_t *p)
{
    return win32_fsetpos(pf, p);
}
void
PerlStdIOInit(struct IPerlStdIO* piPerl)
{
}

void
PerlStdIOInitOSExtras(struct IPerlStdIO* piPerl)
{
    Perl_init_os_extras();
}

int
PerlStdIOOpenOSfhandle(struct IPerlStdIO* piPerl, intptr_t osfhandle, int flags)
{
    return win32_open_osfhandle(osfhandle, flags);
}

intptr_t
PerlStdIOGetOSfhandle(struct IPerlStdIO* piPerl, int filenum)
{
    return win32_get_osfhandle(filenum);
}

FILE*
PerlStdIOFdupopen(struct IPerlStdIO* piPerl, FILE* pf)
{
#ifndef UNDER_CE
    FILE* pfdup;
    fpos_t pos;
    char mode[3];
    int fileno = win32_dup(win32_fileno(pf));

    /* open the file in the same mode */
    if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_RD) {
	mode[0] = 'r';
	mode[1] = 0;
    }
    else if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_WR) {
	mode[0] = 'a';
	mode[1] = 0;
    }
    else if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_RW) {
	mode[0] = 'r';
	mode[1] = '+';
	mode[2] = 0;
    }

    /* it appears that the binmode is attached to the
     * file descriptor so binmode files will be handled
     * correctly
     */
    pfdup = win32_fdopen(fileno, mode);

    /* move the file pointer to the same position */
    if (!fgetpos(pf, &pos)) {
	fsetpos(pfdup, &pos);
    }
    return pfdup;
#else
    return 0;
#endif
}

const struct IPerlStdIO perlStdIO =
{
    PerlStdIOStdin,
    PerlStdIOStdout,
    PerlStdIOStderr,
    PerlStdIOOpen,
    PerlStdIOClose,
    PerlStdIOEof,
    PerlStdIOError,
    PerlStdIOClearerr,
    PerlStdIOGetc,
    PerlStdIOGetBase,
    PerlStdIOGetBufsiz,
    PerlStdIOGetCnt,
    PerlStdIOGetPtr,
    PerlStdIOGets,
    PerlStdIOPutc,
    PerlStdIOPuts,
    PerlStdIOFlush,
    PerlStdIOUngetc,
    PerlStdIOFileno,
    PerlStdIOFdopen,
    PerlStdIOReopen,
    PerlStdIORead,
    PerlStdIOWrite,
    PerlStdIOSetBuf,
    PerlStdIOSetVBuf,
    PerlStdIOSetCnt,
    PerlStdIOSetPtr,
    PerlStdIOSetlinebuf,
    PerlStdIOPrintf,
    PerlStdIOVprintf,
    PerlStdIOTell,
    PerlStdIOSeek,
    PerlStdIORewind,
    PerlStdIOTmpfile,
    PerlStdIOGetpos,
    PerlStdIOSetpos,
    PerlStdIOInit,
    PerlStdIOInitOSExtras,
    PerlStdIOFdupopen,
};


#undef IPERL2HOST
#define IPERL2HOST(x) IPerlLIO2Host(x)

/* IPerlLIO */
int
PerlLIOAccess(struct IPerlLIO* piPerl, const char *path, int mode)
{
    return win32_access(path, mode);
}

int
PerlLIOChmod(struct IPerlLIO* piPerl, const char *filename, int pmode)
{
    return win32_chmod(filename, pmode);
}

int
PerlLIOChown(struct IPerlLIO* piPerl, const char *filename, uid_t owner, gid_t group)
{
    return chown(filename, owner, group);
}

int
PerlLIOChsize(struct IPerlLIO* piPerl, int handle, Off_t size)
{
    return win32_chsize(handle, size);
}

int
PerlLIOClose(struct IPerlLIO* piPerl, int handle)
{
    return win32_close(handle);
}

int
PerlLIODup(struct IPerlLIO* piPerl, int handle)
{
    return win32_dup(handle);
}

int
PerlLIODup2(struct IPerlLIO* piPerl, int handle1, int handle2)
{
    return win32_dup2(handle1, handle2);
}

int
PerlLIOFlock(struct IPerlLIO* piPerl, int fd, int oper)
{
    return win32_flock(fd, oper);
}

int
PerlLIOFileStat(struct IPerlLIO* piPerl, int handle, Stat_t *buffer)
{
    return win32_fstat(handle, buffer);
}

int
PerlLIOIOCtl(struct IPerlLIO* piPerl, int i, unsigned int u, char *data)
{
    u_long u_long_arg;
    int retval;

    /* mauke says using memcpy avoids alignment issues */
    memcpy(&u_long_arg, data, sizeof u_long_arg); 
    retval = win32_ioctlsocket((SOCKET)i, (long)u, &u_long_arg);
    memcpy(data, &u_long_arg, sizeof u_long_arg);
    return retval;
}

int
PerlLIOIsatty(struct IPerlLIO* piPerl, int fd)
{
    return win32_isatty(fd);
}

int
PerlLIOLink(struct IPerlLIO* piPerl, const char*oldname, const char *newname)
{
    return win32_link(oldname, newname);
}

Off_t
PerlLIOLseek(struct IPerlLIO* piPerl, int handle, Off_t offset, int origin)
{
    return win32_lseek(handle, offset, origin);
}

int
PerlLIOLstat(struct IPerlLIO* piPerl, const char *path, Stat_t *buffer)
{
    return win32_stat(path, buffer);
}

char*
PerlLIOMktemp(struct IPerlLIO* piPerl, char *Template)
{
    return mktemp(Template);
}

int
PerlLIOOpen(struct IPerlLIO* piPerl, const char *filename, int oflag)
{
    return win32_open(filename, oflag);
}

int
PerlLIOOpen3(struct IPerlLIO* piPerl, const char *filename, int oflag, int pmode)
{
    return win32_open(filename, oflag, pmode);
}

int
PerlLIORead(struct IPerlLIO* piPerl, int handle, void *buffer, unsigned int count)
{
    return win32_read(handle, buffer, count);
}

int
PerlLIORename(struct IPerlLIO* piPerl, const char *OldFileName, const char *newname)
{
    return win32_rename(OldFileName, newname);
}

int
PerlLIOSetmode(struct IPerlLIO* piPerl, int handle, int mode)
{
    return win32_setmode(handle, mode);
}

int
PerlLIONameStat(struct IPerlLIO* piPerl, const char *path, Stat_t *buffer)
{
    return win32_stat(path, buffer);
}

char*
PerlLIOTmpnam(struct IPerlLIO* piPerl, char *string)
{
    return tmpnam(string);
}

int
PerlLIOUmask(struct IPerlLIO* piPerl, int pmode)
{
    return umask(pmode);
}

int
PerlLIOUnlink(struct IPerlLIO* piPerl, const char *filename)
{
    return win32_unlink(filename);
}

int
PerlLIOUtime(struct IPerlLIO* piPerl, const char *filename, struct utimbuf *times)
{
    return win32_utime(filename, times);
}

int
PerlLIOWrite(struct IPerlLIO* piPerl, int handle, const void *buffer, unsigned int count)
{
    return win32_write(handle, buffer, count);
}

const struct IPerlLIO perlLIO =
{
    PerlLIOAccess,
    PerlLIOChmod,
    PerlLIOChown,
    PerlLIOChsize,
    PerlLIOClose,
    PerlLIODup,
    PerlLIODup2,
    PerlLIOFlock,
    PerlLIOFileStat,
    PerlLIOIOCtl,
    PerlLIOIsatty,
    PerlLIOLink,
    PerlLIOLseek,
    PerlLIOLstat,
    PerlLIOMktemp,
    PerlLIOOpen,
    PerlLIOOpen3,
    PerlLIORead,
    PerlLIORename,
    PerlLIOSetmode,
    PerlLIONameStat,
    PerlLIOTmpnam,
    PerlLIOUmask,
    PerlLIOUnlink,
    PerlLIOUtime,
    PerlLIOWrite,
};


#undef IPERL2HOST
#define IPERL2HOST(x) IPerlDir2Host(x)

/* IPerlDIR */
int
PerlDirMakedir(struct IPerlDir* piPerl, const char *dirname, int mode)
{
    return win32_mkdir(dirname, mode);
}

int
PerlDirChdir(struct IPerlDir* piPerl, const char *dirname)
{
    return IPERL2HOST(piPerl)->Chdir(dirname);
}

int
PerlDirRmdir(struct IPerlDir* piPerl, const char *dirname)
{
    return win32_rmdir(dirname);
}

int
PerlDirClose(struct IPerlDir* piPerl, DIR *dirp)
{
    return win32_closedir(dirp);
}

DIR*
PerlDirOpen(struct IPerlDir* piPerl, const char *filename)
{
    return win32_opendir(filename);
}

struct direct *
PerlDirRead(struct IPerlDir* piPerl, DIR *dirp)
{
    return win32_readdir(dirp);
}

void
PerlDirRewind(struct IPerlDir* piPerl, DIR *dirp)
{
    win32_rewinddir(dirp);
}

void
PerlDirSeek(struct IPerlDir* piPerl, DIR *dirp, long loc)
{
    win32_seekdir(dirp, loc);
}

long
PerlDirTell(struct IPerlDir* piPerl, DIR *dirp)
{
    return win32_telldir(dirp);
}

char*
PerlDirMapPathA(struct IPerlDir* piPerl, const char* path)
{
    return IPERL2HOST(piPerl)->MapPathA(path);
}

WCHAR*
PerlDirMapPathW(struct IPerlDir* piPerl, const WCHAR* path)
{
    return IPERL2HOST(piPerl)->MapPathW(path);
}

const struct IPerlDir perlDir =
{
    PerlDirMakedir,
    PerlDirChdir,
    PerlDirRmdir,
    PerlDirClose,
    PerlDirOpen,
    PerlDirRead,
    PerlDirRewind,
    PerlDirSeek,
    PerlDirTell,
    PerlDirMapPathA,
    PerlDirMapPathW,
};


/* IPerlSock */
u_long
PerlSockHtonl(struct IPerlSock* piPerl, u_long hostlong)
{
    return win32_htonl(hostlong);
}

u_short
PerlSockHtons(struct IPerlSock* piPerl, u_short hostshort)
{
    return win32_htons(hostshort);
}

u_long
PerlSockNtohl(struct IPerlSock* piPerl, u_long netlong)
{
    return win32_ntohl(netlong);
}

u_short
PerlSockNtohs(struct IPerlSock* piPerl, u_short netshort)
{
    return win32_ntohs(netshort);
}

SOCKET PerlSockAccept(struct IPerlSock* piPerl, SOCKET s, struct sockaddr* addr, int* addrlen)
{
    return win32_accept(s, addr, addrlen);
}

int
PerlSockBind(struct IPerlSock* piPerl, SOCKET s, const struct sockaddr* name, int namelen)
{
    return win32_bind(s, name, namelen);
}

int
PerlSockConnect(struct IPerlSock* piPerl, SOCKET s, const struct sockaddr* name, int namelen)
{
    return win32_connect(s, name, namelen);
}

void
PerlSockEndhostent(struct IPerlSock* piPerl)
{
    win32_endhostent();
}

void
PerlSockEndnetent(struct IPerlSock* piPerl)
{
    win32_endnetent();
}

void
PerlSockEndprotoent(struct IPerlSock* piPerl)
{
    win32_endprotoent();
}

void
PerlSockEndservent(struct IPerlSock* piPerl)
{
    win32_endservent();
}

struct hostent*
PerlSockGethostbyaddr(struct IPerlSock* piPerl, const char* addr, int len, int type)
{
    return win32_gethostbyaddr(addr, len, type);
}

struct hostent*
PerlSockGethostbyname(struct IPerlSock* piPerl, const char* name)
{
    return win32_gethostbyname(name);
}

struct hostent*
PerlSockGethostent(struct IPerlSock* piPerl)
{
    win32_croak_not_implemented("gethostent");
    return NULL;
}

int
PerlSockGethostname(struct IPerlSock* piPerl, char* name, int namelen)
{
    return win32_gethostname(name, namelen);
}

struct netent *
PerlSockGetnetbyaddr(struct IPerlSock* piPerl, long net, int type)
{
    return win32_getnetbyaddr(net, type);
}

struct netent *
PerlSockGetnetbyname(struct IPerlSock* piPerl, const char *name)
{
    return win32_getnetbyname((char*)name);
}

struct netent *
PerlSockGetnetent(struct IPerlSock* piPerl)
{
    return win32_getnetent();
}

int PerlSockGetpeername(struct IPerlSock* piPerl, SOCKET s, struct sockaddr* name, int* namelen)
{
    return win32_getpeername(s, name, namelen);
}

struct protoent*
PerlSockGetprotobyname(struct IPerlSock* piPerl, const char* name)
{
    return win32_getprotobyname(name);
}

struct protoent*
PerlSockGetprotobynumber(struct IPerlSock* piPerl, int number)
{
    return win32_getprotobynumber(number);
}

struct protoent*
PerlSockGetprotoent(struct IPerlSock* piPerl)
{
    return win32_getprotoent();
}

struct servent*
PerlSockGetservbyname(struct IPerlSock* piPerl, const char* name, const char* proto)
{
    return win32_getservbyname(name, proto);
}

struct servent*
PerlSockGetservbyport(struct IPerlSock* piPerl, int port, const char* proto)
{
    return win32_getservbyport(port, proto);
}

struct servent*
PerlSockGetservent(struct IPerlSock* piPerl)
{
    return win32_getservent();
}

int
PerlSockGetsockname(struct IPerlSock* piPerl, SOCKET s, struct sockaddr* name, int* namelen)
{
    return win32_getsockname(s, name, namelen);
}

int
PerlSockGetsockopt(struct IPerlSock* piPerl, SOCKET s, int level, int optname, char* optval, int* optlen)
{
    return win32_getsockopt(s, level, optname, optval, optlen);
}

unsigned long
PerlSockInetAddr(struct IPerlSock* piPerl, const char* cp)
{
    return win32_inet_addr(cp);
}

char*
PerlSockInetNtoa(struct IPerlSock* piPerl, struct in_addr in)
{
    return win32_inet_ntoa(in);
}

int
PerlSockListen(struct IPerlSock* piPerl, SOCKET s, int backlog)
{
    return win32_listen(s, backlog);
}

int
PerlSockRecv(struct IPerlSock* piPerl, SOCKET s, char* buffer, int len, int flags)
{
    return win32_recv(s, buffer, len, flags);
}

int
PerlSockRecvfrom(struct IPerlSock* piPerl, SOCKET s, char* buffer, int len, int flags, struct sockaddr* from, int* fromlen)
{
    return win32_recvfrom(s, buffer, len, flags, from, fromlen);
}

int
PerlSockSelect(struct IPerlSock* piPerl, int nfds, char* readfds, char* writefds, char* exceptfds, const struct timeval* timeout)
{
    return win32_select(nfds, (Perl_fd_set*)readfds, (Perl_fd_set*)writefds, (Perl_fd_set*)exceptfds, timeout);
}

int
PerlSockSend(struct IPerlSock* piPerl, SOCKET s, const char* buffer, int len, int flags)
{
    return win32_send(s, buffer, len, flags);
}

int
PerlSockSendto(struct IPerlSock* piPerl, SOCKET s, const char* buffer, int len, int flags, const struct sockaddr* to, int tolen)
{
    return win32_sendto(s, buffer, len, flags, to, tolen);
}

void
PerlSockSethostent(struct IPerlSock* piPerl, int stayopen)
{
    win32_sethostent(stayopen);
}

void
PerlSockSetnetent(struct IPerlSock* piPerl, int stayopen)
{
    win32_setnetent(stayopen);
}

void
PerlSockSetprotoent(struct IPerlSock* piPerl, int stayopen)
{
    win32_setprotoent(stayopen);
}

void
PerlSockSetservent(struct IPerlSock* piPerl, int stayopen)
{
    win32_setservent(stayopen);
}

int
PerlSockSetsockopt(struct IPerlSock* piPerl, SOCKET s, int level, int optname, const char* optval, int optlen)
{
    return win32_setsockopt(s, level, optname, optval, optlen);
}

int
PerlSockShutdown(struct IPerlSock* piPerl, SOCKET s, int how)
{
    return win32_shutdown(s, how);
}

SOCKET
PerlSockSocket(struct IPerlSock* piPerl, int af, int type, int protocol)
{
    return win32_socket(af, type, protocol);
}

int
PerlSockSocketpair(struct IPerlSock* piPerl, int domain, int type, int protocol, int* fds)
{
    return Perl_my_socketpair(domain, type, protocol, fds);
}

int
PerlSockClosesocket(struct IPerlSock* piPerl, SOCKET s)
{
    return win32_closesocket(s);
}

int
PerlSockIoctlsocket(struct IPerlSock* piPerl, SOCKET s, long cmd, u_long *argp)
{
    return win32_ioctlsocket(s, cmd, argp);
}

const struct IPerlSock perlSock =
{
    PerlSockHtonl,
    PerlSockHtons,
    PerlSockNtohl,
    PerlSockNtohs,
    PerlSockAccept,
    PerlSockBind,
    PerlSockConnect,
    PerlSockEndhostent,
    PerlSockEndnetent,
    PerlSockEndprotoent,
    PerlSockEndservent,
    PerlSockGethostname,
    PerlSockGetpeername,
    PerlSockGethostbyaddr,
    PerlSockGethostbyname,
    PerlSockGethostent,
    PerlSockGetnetbyaddr,
    PerlSockGetnetbyname,
    PerlSockGetnetent,
    PerlSockGetprotobyname,
    PerlSockGetprotobynumber,
    PerlSockGetprotoent,
    PerlSockGetservbyname,
    PerlSockGetservbyport,
    PerlSockGetservent,
    PerlSockGetsockname,
    PerlSockGetsockopt,
    PerlSockInetAddr,
    PerlSockInetNtoa,
    PerlSockListen,
    PerlSockRecv,
    PerlSockRecvfrom,
    PerlSockSelect,
    PerlSockSend,
    PerlSockSendto,
    PerlSockSethostent,
    PerlSockSetnetent,
    PerlSockSetprotoent,
    PerlSockSetservent,
    PerlSockSetsockopt,
    PerlSockShutdown,
    PerlSockSocket,
    PerlSockSocketpair,
    PerlSockClosesocket,
};


/* IPerlProc */

#define EXECF_EXEC 1
#define EXECF_SPAWN 2

void
PerlProcAbort(struct IPerlProc* piPerl)
{
    win32_abort();
}

char *
PerlProcCrypt(struct IPerlProc* piPerl, const char* clear, const char* salt)
{
    return win32_crypt(clear, salt);
}

PERL_CALLCONV_NO_RET void
PerlProcExit(struct IPerlProc* piPerl, int status)
{
    exit(status);
}

PERL_CALLCONV_NO_RET void
PerlProc_Exit(struct IPerlProc* piPerl, int status)
{
    _exit(status);
}

int
PerlProcExecl(struct IPerlProc* piPerl, const char *cmdname, const char *arg0, const char *arg1, const char *arg2, const char *arg3)
{
    return execl(cmdname, arg0, arg1, arg2, arg3);
}

int
PerlProcExecv(struct IPerlProc* piPerl, const char *cmdname, const char *const *argv)
{
    return win32_execvp(cmdname, argv);
}

int
PerlProcExecvp(struct IPerlProc* piPerl, const char *cmdname, const char *const *argv)
{
    return win32_execvp(cmdname, argv);
}

uid_t
PerlProcGetuid(struct IPerlProc* piPerl)
{
    return getuid();
}

uid_t
PerlProcGeteuid(struct IPerlProc* piPerl)
{
    return geteuid();
}

gid_t
PerlProcGetgid(struct IPerlProc* piPerl)
{
    return getgid();
}

gid_t
PerlProcGetegid(struct IPerlProc* piPerl)
{
    return getegid();
}

char *
PerlProcGetlogin(struct IPerlProc* piPerl)
{
    return g_getlogin();
}

int
PerlProcKill(struct IPerlProc* piPerl, int pid, int sig)
{
    return win32_kill(pid, sig);
}

int
PerlProcKillpg(struct IPerlProc* piPerl, int pid, int sig)
{
    return win32_kill(pid, -sig);
}

int
PerlProcPauseProc(struct IPerlProc* piPerl)
{
    return win32_sleep((32767L << 16) + 32767);
}

PerlIO*
PerlProcPopen(struct IPerlProc* piPerl, const char *command, const char *mode)
{
    dTHX;
    PERL_FLUSHALL_FOR_CHILD;
    return win32_popen(command, mode);
}

PerlIO*
PerlProcPopenList(struct IPerlProc* piPerl, const char *mode, IV narg, SV **args)
{
    dTHX;
    PERL_FLUSHALL_FOR_CHILD;
    return win32_popenlist(mode, narg, args);
}

int
PerlProcPclose(struct IPerlProc* piPerl, PerlIO *stream)
{
    return win32_pclose(stream);
}

int
PerlProcPipe(struct IPerlProc* piPerl, int *phandles)
{
    return win32_pipe(phandles, 512, O_BINARY);
}

int
PerlProcSetuid(struct IPerlProc* piPerl, uid_t u)
{
    return setuid(u);
}

int
PerlProcSetgid(struct IPerlProc* piPerl, gid_t g)
{
    return setgid(g);
}

int
PerlProcSleep(struct IPerlProc* piPerl, unsigned int s)
{
    return win32_sleep(s);
}

int
PerlProcTimes(struct IPerlProc* piPerl, struct tms *timebuf)
{
    return win32_times(timebuf);
}

int
PerlProcWait(struct IPerlProc* piPerl, int *status)
{
    return win32_wait(status);
}

int
PerlProcWaitpid(struct IPerlProc* piPerl, int pid, int *status, int flags)
{
    return win32_waitpid(pid, status, flags);
}

Sighandler_t
PerlProcSignal(struct IPerlProc* piPerl, int sig, Sighandler_t subcode)
{
    return win32_signal(sig, subcode);
}

int
PerlProcGetTimeOfDay(struct IPerlProc* piPerl, struct timeval *t, void *z)
{
    return win32_gettimeofday(t, z);
}

#ifdef USE_ITHREADS
static THREAD_RET_TYPE
win32_start_child(LPVOID arg)
{
    PerlInterpreter *my_perl = (PerlInterpreter*)arg;
    int status;
    HWND parent_message_hwnd;
#ifdef PERL_SYNC_FORK
    static long sync_fork_id = 0;
    long id = ++sync_fork_id;
#endif


    PERL_SET_THX(my_perl);
    win32_checkTLS(my_perl);

#ifdef PERL_SYNC_FORK
    w32_pseudo_id = id;
#else
    w32_pseudo_id = GetCurrentThreadId();
#endif
#ifdef PERL_USES_PL_PIDSTATUS    
    hv_clear(PL_pidstatus);
#endif    

    /* create message window and tell parent about it */
    parent_message_hwnd = w32_message_hwnd;
    w32_message_hwnd = win32_create_message_window();
    if (parent_message_hwnd != NULL)
        PostMessage(parent_message_hwnd, WM_USER_MESSAGE, w32_pseudo_id, (LPARAM)w32_message_hwnd);

    /* push a zero on the stack (we are the child) */
    {
	dSP;
	dTARGET;
	PUSHi(0);
	PUTBACK;
    }

    /* continue from next op */
    PL_op = PL_op->op_next;

    {
	dJMPENV;
	volatile int oldscope = 1; /* We are responsible for all scopes */

restart:
	JMPENV_PUSH(status);
	switch (status) {
	case 0:
	    CALLRUNOPS(aTHX);
            /* We may have additional unclosed scopes if fork() was called
             * from within a BEGIN block.  See perlfork.pod for more details.
             * We cannot clean up these other scopes because they belong to a
             * different interpreter, but we also cannot leave PL_scopestack_ix
             * dangling because that can trigger an assertion in perl_destruct().
             */
            if (PL_scopestack_ix > oldscope) {
                PL_scopestack[oldscope-1] = PL_scopestack[PL_scopestack_ix-1];
                PL_scopestack_ix = oldscope;
            }
	    status = 0;
	    break;
	case 2:
	    while (PL_scopestack_ix > oldscope)
		LEAVE;
	    FREETMPS;
	    PL_curstash = PL_defstash;
	    if (PL_curstash != PL_defstash) {
		SvREFCNT_dec(PL_curstash);
		PL_curstash = (HV *)SvREFCNT_inc(PL_defstash);
	    }
	    if (PL_endav && !PL_minus_c) {
		PERL_SET_PHASE(PERL_PHASE_END);
		call_list(oldscope, PL_endav);
	    }
	    status = STATUS_EXIT;
	    break;
	case 3:
	    if (PL_restartop) {
		POPSTACK_TO(PL_mainstack);
		PL_op = PL_restartop;
		PL_restartop = (OP*)NULL;
		goto restart;
	    }
	    PerlIO_printf(Perl_error_log, "panic: restartop\n");
	    FREETMPS;
	    status = 1;
	    break;
	}
	JMPENV_POP;

	/* XXX hack to avoid perl_destruct() freeing optree */
        win32_checkTLS(my_perl);
	PL_main_root = (OP*)NULL;
    }

    win32_checkTLS(my_perl);
    /* close the std handles to avoid fd leaks */
    {
	do_close(PL_stdingv, FALSE);
	do_close(gv_fetchpv("STDOUT", TRUE, SVt_PVIO), FALSE); /* PL_stdoutgv - ISAGN */
	do_close(PL_stderrgv, FALSE);
    }

    /* destroy everything (waits for any pseudo-forked children) */
    win32_checkTLS(my_perl);
    perl_destruct(my_perl);
    win32_checkTLS(my_perl);
    perl_free(my_perl);

#ifdef PERL_SYNC_FORK
    return id;
#else
    return (DWORD)status;
#endif
}
#endif /* USE_ITHREADS */

int
PerlProcFork(struct IPerlProc* piPerl)
{
#ifdef USE_ITHREADS
    dTHX;
    DWORD id;
    HANDLE handle;
    CPerlHost *h;

    if (w32_num_pseudo_children >= MAXIMUM_WAIT_OBJECTS) {
	errno = EAGAIN;
	return -1;
    }
    h = new CPerlHost(*(CPerlHost*)w32_internal_host);
    PerlInterpreter *new_perl = perl_clone_using((PerlInterpreter*)aTHX,
						 CLONEf_COPY_STACKS,
						 h->m_pHostperlMem,
						 h->m_pHostperlMemShared,
						 h->m_pHostperlMemParse,
						 h->m_pHostperlEnv,
						 h->m_pHostperlStdIO,
						 h->m_pHostperlLIO,
						 h->m_pHostperlDir,
						 h->m_pHostperlSock,
						 h->m_pHostperlProc
						 );
    new_perl->Isys_intern.internal_host = h;
    h->host_perl = new_perl;
#  ifdef PERL_SYNC_FORK
    id = win32_start_child((LPVOID)new_perl);
    PERL_SET_THX(aTHX);
#  else
    if (w32_message_hwnd == INVALID_HANDLE_VALUE)
        w32_message_hwnd = win32_create_message_window();
    new_perl->Isys_intern.message_hwnd = w32_message_hwnd;
    w32_pseudo_child_message_hwnds[w32_num_pseudo_children] =
        (w32_message_hwnd == NULL) ? (HWND)NULL : (HWND)INVALID_HANDLE_VALUE;
#    ifdef USE_RTL_THREAD_API
    handle = (HANDLE)_beginthreadex((void*)NULL, 0, win32_start_child,
				    (void*)new_perl, 0, (unsigned*)&id);
#    else
    handle = CreateThread(NULL, 0, win32_start_child,
			  (LPVOID)new_perl, 0, &id);
#    endif
    PERL_SET_THX(aTHX);	/* XXX perl_clone*() set TLS */
    if (!handle) {
	errno = EAGAIN;
	return -1;
    }
    w32_pseudo_child_handles[w32_num_pseudo_children] = handle;
    w32_pseudo_child_pids[w32_num_pseudo_children] = id;
    w32_pseudo_child_sigterm[w32_num_pseudo_children] = 0;
    ++w32_num_pseudo_children;
#  endif
    return -(int)id;
#else
    win32_croak_not_implemented("fork()");
    return -1;
#endif /* USE_ITHREADS */
}

int
PerlProcGetpid(struct IPerlProc* piPerl)
{
    return win32_getpid();
}

void*
PerlProcDynaLoader(struct IPerlProc* piPerl, const char* filename)
{
    return win32_dynaload(filename);
}

void
PerlProcGetOSError(struct IPerlProc* piPerl, SV* sv, DWORD dwErr)
{
    win32_str_os_error(sv, dwErr);
}

int
PerlProcSpawnvp(struct IPerlProc* piPerl, int mode, const char *cmdname, const char *const *argv)
{
    return win32_spawnvp(mode, cmdname, argv);
}

int
PerlProcLastHost(struct IPerlProc* piPerl)
{
 /* this dTHX is unused in an optimized build since CPerlHost::num_hosts
    is a static */
 dTHX;
 CPerlHost *h = (CPerlHost*)w32_internal_host;
 return h->LastHost();
}

const struct IPerlProc perlProc =
{
    PerlProcAbort,
    PerlProcCrypt,
    PerlProcExit,
    PerlProc_Exit,
    PerlProcExecl,
    PerlProcExecv,
    PerlProcExecvp,
    PerlProcGetuid,
    PerlProcGeteuid,
    PerlProcGetgid,
    PerlProcGetegid,
    PerlProcGetlogin,
    PerlProcKill,
    PerlProcKillpg,
    PerlProcPauseProc,
    PerlProcPopen,
    PerlProcPclose,
    PerlProcPipe,
    PerlProcSetuid,
    PerlProcSetgid,
    PerlProcSleep,
    PerlProcTimes,
    PerlProcWait,
    PerlProcWaitpid,
    PerlProcSignal,
    PerlProcFork,
    PerlProcGetpid,
    PerlProcDynaLoader,
    PerlProcGetOSError,
    PerlProcSpawnvp,
    PerlProcLastHost,
    PerlProcPopenList,
    PerlProcGetTimeOfDay
};


/*
 * CPerlHost
 */

CPerlHost::CPerlHost(void)
{
    /* Construct a host from scratch */
    InterlockedIncrement(&num_hosts);
    m_pvDir = new VDir();
    m_pVMem = new VMem();
    m_pVMemShared = new VMem();
    m_pVMemParse =  new VMem();

    m_pvDir->Init(NULL, m_pVMem);

    m_dwEnvCount = 0;
    m_lppEnvList = NULL;
    m_bTopLevel = TRUE;

    CopyMemory(&m_hostperlMem, &perlMem, sizeof(perlMem));
    CopyMemory(&m_hostperlMemShared, &perlMemShared, sizeof(perlMemShared));
    CopyMemory(&m_hostperlMemParse, &perlMemParse, sizeof(perlMemParse));
    CopyMemory(&m_hostperlEnv, &perlEnv, sizeof(perlEnv));
    CopyMemory(&m_hostperlStdIO, &perlStdIO, sizeof(perlStdIO));
    CopyMemory(&m_hostperlLIO, &perlLIO, sizeof(perlLIO));
    CopyMemory(&m_hostperlDir, &perlDir, sizeof(perlDir));
    CopyMemory(&m_hostperlSock, &perlSock, sizeof(perlSock));
    CopyMemory(&m_hostperlProc, &perlProc, sizeof(perlProc));

    m_pHostperlMem	    = &m_hostperlMem;
    m_pHostperlMemShared    = &m_hostperlMemShared;
    m_pHostperlMemParse	    = &m_hostperlMemParse;
    m_pHostperlEnv	    = &m_hostperlEnv;
    m_pHostperlStdIO	    = &m_hostperlStdIO;
    m_pHostperlLIO	    = &m_hostperlLIO;
    m_pHostperlDir	    = &m_hostperlDir;
    m_pHostperlSock	    = &m_hostperlSock;
    m_pHostperlProc	    = &m_hostperlProc;
}

#define SETUPEXCHANGE(xptr, iptr, table) \
    STMT_START {				\
	if (xptr) {				\
	    iptr = *xptr;			\
	    *xptr = &table;			\
	}					\
	else {					\
	    iptr = &table;			\
	}					\
    } STMT_END

CPerlHost::CPerlHost(struct IPerlMem** ppMem, struct IPerlMem** ppMemShared,
		 struct IPerlMem** ppMemParse, struct IPerlEnv** ppEnv,
		 struct IPerlStdIO** ppStdIO, struct IPerlLIO** ppLIO,
		 struct IPerlDir** ppDir, struct IPerlSock** ppSock,
		 struct IPerlProc** ppProc)
{
    InterlockedIncrement(&num_hosts);
    m_pvDir = new VDir(0);
    m_pVMem = new VMem();
    m_pVMemShared = new VMem();
    m_pVMemParse =  new VMem();

    m_pvDir->Init(NULL, m_pVMem);

    m_dwEnvCount = 0;
    m_lppEnvList = NULL;
    m_bTopLevel = FALSE;

    CopyMemory(&m_hostperlMem, &perlMem, sizeof(perlMem));
    CopyMemory(&m_hostperlMemShared, &perlMemShared, sizeof(perlMemShared));
    CopyMemory(&m_hostperlMemParse, &perlMemParse, sizeof(perlMemParse));
    CopyMemory(&m_hostperlEnv, &perlEnv, sizeof(perlEnv));
    CopyMemory(&m_hostperlStdIO, &perlStdIO, sizeof(perlStdIO));
    CopyMemory(&m_hostperlLIO, &perlLIO, sizeof(perlLIO));
    CopyMemory(&m_hostperlDir, &perlDir, sizeof(perlDir));
    CopyMemory(&m_hostperlSock, &perlSock, sizeof(perlSock));
    CopyMemory(&m_hostperlProc, &perlProc, sizeof(perlProc));

    SETUPEXCHANGE(ppMem,	m_pHostperlMem,		m_hostperlMem);
    SETUPEXCHANGE(ppMemShared,	m_pHostperlMemShared,	m_hostperlMemShared);
    SETUPEXCHANGE(ppMemParse,	m_pHostperlMemParse,	m_hostperlMemParse);
    SETUPEXCHANGE(ppEnv,	m_pHostperlEnv,		m_hostperlEnv);
    SETUPEXCHANGE(ppStdIO,	m_pHostperlStdIO,	m_hostperlStdIO);
    SETUPEXCHANGE(ppLIO,	m_pHostperlLIO,		m_hostperlLIO);
    SETUPEXCHANGE(ppDir,	m_pHostperlDir,		m_hostperlDir);
    SETUPEXCHANGE(ppSock,	m_pHostperlSock,	m_hostperlSock);
    SETUPEXCHANGE(ppProc,	m_pHostperlProc,	m_hostperlProc);
}
#undef SETUPEXCHANGE

CPerlHost::CPerlHost(CPerlHost& host)
{
    /* Construct a host from another host */
    InterlockedIncrement(&num_hosts);
    m_pVMem = new VMem();
    m_pVMemShared = host.GetMemShared();
    m_pVMemParse =  host.GetMemParse();

    /* duplicate directory info */
    m_pvDir = new VDir(0);
    m_pvDir->Init(host.GetDir(), m_pVMem);

    CopyMemory(&m_hostperlMem, &perlMem, sizeof(perlMem));
    CopyMemory(&m_hostperlMemShared, &perlMemShared, sizeof(perlMemShared));
    CopyMemory(&m_hostperlMemParse, &perlMemParse, sizeof(perlMemParse));
    CopyMemory(&m_hostperlEnv, &perlEnv, sizeof(perlEnv));
    CopyMemory(&m_hostperlStdIO, &perlStdIO, sizeof(perlStdIO));
    CopyMemory(&m_hostperlLIO, &perlLIO, sizeof(perlLIO));
    CopyMemory(&m_hostperlDir, &perlDir, sizeof(perlDir));
    CopyMemory(&m_hostperlSock, &perlSock, sizeof(perlSock));
    CopyMemory(&m_hostperlProc, &perlProc, sizeof(perlProc));
    m_pHostperlMem	    = &m_hostperlMem;
    m_pHostperlMemShared    = &m_hostperlMemShared;
    m_pHostperlMemParse	    = &m_hostperlMemParse;
    m_pHostperlEnv	    = &m_hostperlEnv;
    m_pHostperlStdIO	    = &m_hostperlStdIO;
    m_pHostperlLIO	    = &m_hostperlLIO;
    m_pHostperlDir	    = &m_hostperlDir;
    m_pHostperlSock	    = &m_hostperlSock;
    m_pHostperlProc	    = &m_hostperlProc;

    m_dwEnvCount = 0;
    m_lppEnvList = NULL;
    m_bTopLevel = FALSE;

    /* duplicate environment info */
    LPSTR lpPtr;
    DWORD dwIndex = 0;
    while(lpPtr = host.GetIndex(dwIndex))
	Add(lpPtr);
}

CPerlHost::~CPerlHost(void)
{
    Reset();
    InterlockedDecrement(&num_hosts);
    delete m_pvDir;
    m_pVMemParse->Release();
    m_pVMemShared->Release();
    m_pVMem->Release();
}

LPSTR
CPerlHost::Find(LPCSTR lpStr)
{
    LPSTR lpPtr;
    LPSTR* lppPtr = Lookup(lpStr);
    if(lppPtr != NULL) {
	for(lpPtr = *lppPtr; *lpPtr != '\0' && *lpPtr != '='; ++lpPtr)
	    ;

	if(*lpPtr == '=')
	    ++lpPtr;

	return lpPtr;
    }
    return NULL;
}

int
lookup(const void *arg1, const void *arg2)
{   // Compare strings
    char*ptr1, *ptr2;
    char c1,c2;

    ptr1 = *(char**)arg1;
    ptr2 = *(char**)arg2;
    for(;;) {
	c1 = *ptr1++;
	c2 = *ptr2++;
	if(c1 == '\0' || c1 == '=') {
	    if(c2 == '\0' || c2 == '=')
		break;

	    return -1; // string 1 < string 2
	}
	else if(c2 == '\0' || c2 == '=')
	    return 1; // string 1 > string 2
	else if(c1 != c2) {
	    c1 = toupper(c1);
	    c2 = toupper(c2);
	    if(c1 != c2) {
		if(c1 < c2)
		    return -1; // string 1 < string 2

		return 1; // string 1 > string 2
	    }
	}
    }
    return 0;
}

LPSTR*
CPerlHost::Lookup(LPCSTR lpStr)
{
#ifdef UNDER_CE
    if (!m_lppEnvList || !m_dwEnvCount)
	return NULL;
#endif
    if (!lpStr)
	return NULL;
    return (LPSTR*)bsearch(&lpStr, m_lppEnvList, m_dwEnvCount, sizeof(LPSTR), lookup);
}

int
compare(const void *arg1, const void *arg2)
{   // Compare strings
    char*ptr1, *ptr2;
    char c1,c2;

    ptr1 = *(char**)arg1;
    ptr2 = *(char**)arg2;
    for(;;) {
	c1 = *ptr1++;
	c2 = *ptr2++;
	if(c1 == '\0' || c1 == '=') {
	    if(c1 == c2)
		break;

	    return -1; // string 1 < string 2
	}
	else if(c2 == '\0' || c2 == '=')
	    return 1; // string 1 > string 2
	else if(c1 != c2) {
	    c1 = toupper(c1);
	    c2 = toupper(c2);
	    if(c1 != c2) {
		if(c1 < c2)
		    return -1; // string 1 < string 2

		return 1; // string 1 > string 2
	    }
	}
    }
    return 0;
}

void
CPerlHost::Add(LPCSTR lpStr)
{
    char szBuffer[1024];
    LPSTR *lpPtr;
    int index, length = strlen(lpStr)+1;

    for(index = 0; lpStr[index] != '\0' && lpStr[index] != '='; ++index)
	szBuffer[index] = lpStr[index];

    szBuffer[index] = '\0';

    // replacing ?
    lpPtr = Lookup(szBuffer);
    if (lpPtr != NULL) {
	// must allocate things via host memory allocation functions 
	// rather than perl's Renew() et al, as the perl interpreter
	// may either not be initialized enough when we allocate these,
	// or may already be dead when we go to free these
	*lpPtr = (char*)Realloc(*lpPtr, length * sizeof(char));
	strcpy(*lpPtr, lpStr);
    }
    else {
	m_lppEnvList = (LPSTR*)Realloc(m_lppEnvList, (m_dwEnvCount+1) * sizeof(LPSTR));
	if (m_lppEnvList) {
	    m_lppEnvList[m_dwEnvCount] = (char*)Malloc(length * sizeof(char));
	    if (m_lppEnvList[m_dwEnvCount] != NULL) {
		strcpy(m_lppEnvList[m_dwEnvCount], lpStr);
		++m_dwEnvCount;
		qsort(m_lppEnvList, m_dwEnvCount, sizeof(LPSTR), compare);
	    }
	}
    }
}

DWORD
CPerlHost::CalculateEnvironmentSpace(void)
{
    DWORD index;
    DWORD dwSize = 0;
    for(index = 0; index < m_dwEnvCount; ++index)
	dwSize += strlen(m_lppEnvList[index]) + 1;

    return dwSize;
}

void
CPerlHost::FreeLocalEnvironmentStrings(LPSTR lpStr)
{
    Safefree(lpStr);
}

char*
CPerlHost::GetChildDir(void)
{
    char* ptr;
    size_t length;

    Newx(ptr, MAX_PATH+1, char);
    m_pvDir->GetCurrentDirectoryA(MAX_PATH+1, ptr);
    length = strlen(ptr);
    if (length > 3) {
        if ((ptr[length-1] == '\\') || (ptr[length-1] == '/'))
            ptr[length-1] = 0;
    }
    return ptr;
}

void
CPerlHost::FreeChildDir(char* pStr)
{
    Safefree(pStr);
}

LPSTR
CPerlHost::CreateLocalEnvironmentStrings(VDir &vDir)
{
    LPSTR lpStr, lpPtr, lpEnvPtr, lpTmp, lpLocalEnv, lpAllocPtr;
    DWORD dwSize, dwEnvIndex;
    int nLength, compVal;

    // get the process environment strings
    lpAllocPtr = lpTmp = (LPSTR)win32_getenvironmentstrings();

    // step over current directory stuff
    while(*lpTmp == '=')
	lpTmp += strlen(lpTmp) + 1;

    // save the start of the environment strings
    lpEnvPtr = lpTmp;
    for(dwSize = 1; *lpTmp != '\0'; lpTmp += strlen(lpTmp) + 1) {
	// calculate the size of the environment strings
	dwSize += strlen(lpTmp) + 1;
    }

    // add the size of current directories
    dwSize += vDir.CalculateEnvironmentSpace();

    // add the additional space used by changes made to the environment
    dwSize += CalculateEnvironmentSpace();

    Newx(lpStr, dwSize, char);
    lpPtr = lpStr;
    if(lpStr != NULL) {
	// build the local environment
	lpStr = vDir.BuildEnvironmentSpace(lpStr);

	dwEnvIndex = 0;
	lpLocalEnv = GetIndex(dwEnvIndex);
	while(*lpEnvPtr != '\0') {
	    if(!lpLocalEnv) {
		// all environment overrides have been added
		// so copy string into place
		strcpy(lpStr, lpEnvPtr);
		nLength = strlen(lpEnvPtr) + 1;
		lpStr += nLength;
		lpEnvPtr += nLength;
	    }
	    else {
		// determine which string to copy next
		compVal = compare(&lpEnvPtr, &lpLocalEnv);
		if(compVal < 0) {
		    strcpy(lpStr, lpEnvPtr);
		    nLength = strlen(lpEnvPtr) + 1;
		    lpStr += nLength;
		    lpEnvPtr += nLength;
		}
		else {
		    char *ptr = strchr(lpLocalEnv, '=');
		    if(ptr && ptr[1]) {
			strcpy(lpStr, lpLocalEnv);
			lpStr += strlen(lpLocalEnv) + 1;
		    }
		    lpLocalEnv = GetIndex(dwEnvIndex);
		    if(compVal == 0) {
			// this string was replaced
			lpEnvPtr += strlen(lpEnvPtr) + 1;
		    }
		}
	    }
	}

	while(lpLocalEnv) {
	    // still have environment overrides to add
	    // so copy the strings into place if not an override
	    char *ptr = strchr(lpLocalEnv, '=');
	    if(ptr && ptr[1]) {
		strcpy(lpStr, lpLocalEnv);
		lpStr += strlen(lpLocalEnv) + 1;
	    }
	    lpLocalEnv = GetIndex(dwEnvIndex);
	}

	// add final NULL
	*lpStr = '\0';
    }

    // release the process environment strings
    win32_freeenvironmentstrings(lpAllocPtr);

    return lpPtr;
}

void
CPerlHost::Reset(void)
{
    if(m_lppEnvList != NULL) {
	for(DWORD index = 0; index < m_dwEnvCount; ++index) {
	    Free(m_lppEnvList[index]);
	    m_lppEnvList[index] = NULL;
	}
    }
    m_dwEnvCount = 0;
    Free(m_lppEnvList);
    m_lppEnvList = NULL;
}

void
CPerlHost::Clearenv(void)
{
    char ch;
    LPSTR lpPtr, lpStr, lpEnvPtr;
    if (m_lppEnvList != NULL) {
	/* set every entry to an empty string */
	for(DWORD index = 0; index < m_dwEnvCount; ++index) {
	    char* ptr = strchr(m_lppEnvList[index], '=');
	    if(ptr) {
		*++ptr = 0;
	    }
	}
    }

    /* get the process environment strings */
    lpStr = lpEnvPtr = (LPSTR)win32_getenvironmentstrings();

    /* step over current directory stuff */
    while(*lpStr == '=')
	lpStr += strlen(lpStr) + 1;

    while(*lpStr) {
	lpPtr = strchr(lpStr, '=');
	if(lpPtr) {
	    ch = *++lpPtr;
	    *lpPtr = 0;
	    Add(lpStr);
	    if (m_bTopLevel)
		(void)win32_putenv(lpStr);
	    *lpPtr = ch;
	}
	lpStr += strlen(lpStr) + 1;
    }

    win32_freeenvironmentstrings(lpEnvPtr);
}


char*
CPerlHost::Getenv(const char *varname)
{
    if (!m_bTopLevel) {
	char *pEnv = Find(varname);
	if (pEnv && *pEnv)
	    return pEnv;
    }
    return win32_getenv(varname);
}

int
CPerlHost::Putenv(const char *envstring)
{
    Add(envstring);
    if (m_bTopLevel)
	return win32_putenv(envstring);

    return 0;
}

int
CPerlHost::Chdir(const char *dirname)
{
    int ret;
    if (!dirname) {
	errno = ENOENT;
	return -1;
    }
    ret = m_pvDir->SetCurrentDirectoryA((char*)dirname);
    if(ret < 0) {
	errno = ENOENT;
    }
    return ret;
}

#endif /* ___PerlHost_H___ */
