// distribution boxbackup-0.10 (svn version: 494)
//  
// Copyright (c) 2003 - 2006
//      Ben Summers and contributors.  All rights reserved.
//  
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must 
//    display the following acknowledgment:
//        This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
//    products derived from this software without specific prior written
//    permission.
// 
// [Where legally impermissible the Authors do not disclaim liability for 
// direct physical injury or death caused solely by defects in the software 
// unless it is modified by a third party.]
// 
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//  
//  
//  
// Box Backup Win32 native port by Nick Knight

// Need at least 0x0500 to use GetFileSizeEx on Cygwin/MinGW
#define WINVER 0x0500

#include "Box.h"

#ifdef WIN32

// #include "emu.h"

#include <windows.h>
#include <fcntl.h>
// #include <atlenc.h>

#ifdef HAVE_UNISTD_H
	#include <unistd.h>
#endif
#ifdef HAVE_PROCESS_H
	#include <process.h>
#endif

#include <string>
#include <list>

// our implementation for a timer, based on a 
// simple thread which sleeps for a period of time

static bool gFinishTimer;
static CRITICAL_SECTION gLock;

typedef struct 
{
	int countDown;
	int interval;
}
Timer_t;

std::list<Timer_t> gTimerList;
static void (__cdecl *gTimerFunc) (int) = NULL;

int setitimer(int type, struct itimerval *timeout, void *arg)
{
	if (ITIMER_VIRTUAL == type)
	{
		EnterCriticalSection(&gLock);
		// we only need seconds for the mo!
		if (timeout->it_value.tv_sec  == 0 && 
		    timeout->it_value.tv_usec == 0)
		{
			gTimerList.clear();
		}
		else
		{
			Timer_t ourTimer;
			ourTimer.countDown = timeout->it_value.tv_sec;
			ourTimer.interval  = timeout->it_interval.tv_sec;
			gTimerList.push_back(ourTimer);
		}
		LeaveCriticalSection(&gLock);
	}
	
	// indicate success
	return 0;
}

static unsigned int WINAPI RunTimer(LPVOID lpParameter)
{
	gFinishTimer = false;

	while (!gFinishTimer)
	{
		std::list<Timer_t>::iterator it;
		EnterCriticalSection(&gLock);

		for (it = gTimerList.begin(); it != gTimerList.end(); it++)
		{
			Timer_t& rTimer(*it);

			rTimer.countDown --;
			if (rTimer.countDown == 0)
			{
				if (gTimerFunc != NULL)
				{
					gTimerFunc(0);
				}
				if (rTimer.interval)
				{
					rTimer.countDown = rTimer.interval;
				}
				else
				{
					// mark for deletion
					rTimer.countDown = -1;
				}
			}
		}

		for (it = gTimerList.begin(); it != gTimerList.end(); it++)
		{
			Timer_t& rTimer(*it);

			if (rTimer.countDown == -1)
			{
				gTimerList.erase(it);
				
				// the iterator is now invalid, so restart search
				it = gTimerList.begin();

				// if the list is now empty, don't try to increment 
				// the iterator again
				if (it == gTimerList.end()) break;
			}
		}

		LeaveCriticalSection(&gLock);
		// we only need to have a 1 second resolution
		Sleep(1000);
	}

	return 0;
}

int SetTimerHandler(void (__cdecl *func ) (int))
{
	gTimerFunc = func;
	return 0;
}

void InitTimer(void)
{
	InitializeCriticalSection(&gLock);

	// create our thread
	HANDLE ourThread = (HANDLE)_beginthreadex(NULL, 0, RunTimer, 0, 
		CREATE_SUSPENDED, NULL);
	SetThreadPriority(ourThread, THREAD_PRIORITY_LOWEST);
	ResumeThread(ourThread);
}

void FiniTimer(void)
{
	gFinishTimer = true;
	EnterCriticalSection(&gLock);
	DeleteCriticalSection(&gLock);
}

//Our constants we need to keep track of
//globals
struct passwd gTempPasswd;

bool EnableBackupRights( void )
{
	HANDLE hToken;
	TOKEN_PRIVILEGES token_priv;

	//open current process to adjust privileges
	if( !OpenProcessToken( GetCurrentProcess( ), 
		TOKEN_ADJUST_PRIVILEGES, 
		&hToken ))
	{
		printf( "Cannot open process token: error %d\n", 
			(int)GetLastError() );
		return false;
	}

	//let's build the token privilege struct - 
	//first, look up the LUID for the backup privilege

	if( !LookupPrivilegeValue( NULL, //this system
		SE_BACKUP_NAME, //the name of the privilege
		&( token_priv.Privileges[0].Luid )) ) //result
	{
		printf( "Cannot lookup backup privilege: error %d\n", 
			(int)GetLastError( ) );
		return false;
	}

	token_priv.PrivilegeCount = 1;
	token_priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	// now set the privilege
	// because we're going exit right after dumping the streams, there isn't 
	// any need to save current state

	if( !AdjustTokenPrivileges( hToken, //our process token
		false,  //we're not disabling everything
		&token_priv, //address of structure
		sizeof( token_priv ), //size of structure
		NULL, NULL )) //don't save current state
	{
		//this function is a little tricky - if we were adjusting
		//more than one privilege, it could return success but not
		//adjust them all - in the general case, you need to trap this
		printf( "Could not enable backup privileges: error %d\n", 
			(int)GetLastError( ) );
		return false;

	}
	else
	{
		return true;
	}
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    ConvertToWideString
//		Purpose: Converts a string from specified codepage to 
//			 a wide string (WCHAR*). Returns a buffer which 
//			 MUST be freed by the caller with delete[].
//			 In case of fire, logs the error and returns NULL.
//		Created: 4th February 2006
//
// --------------------------------------------------------------------------
WCHAR* ConvertToWideString(const char* pString, unsigned int codepage)
{
	int len = MultiByteToWideChar
	(
		codepage, // source code page
		0,        // character-type options
		pString,  // string to map
		-1,       // number of bytes in string - auto detect
		NULL,     // wide-character buffer
		0         // size of buffer - work out 
		          //   how much space we need
	);

	if (len == 0)
	{
		::syslog(LOG_WARNING, 
			"Failed to convert string to wide string: "
			"error %d", GetLastError());
		errno = EINVAL;
		return NULL;
	}

	WCHAR* buffer = new WCHAR[len];

	if (buffer == NULL)
	{
		::syslog(LOG_WARNING, 
			"Failed to convert string to wide string: "
			"out of memory");
		errno = ENOMEM;
		return NULL;
	}

	len = MultiByteToWideChar
	(
		codepage, // source code page
		0,        // character-type options
		pString,  // string to map
		-1,       // number of bytes in string - auto detect
		buffer,   // wide-character buffer
		len       // size of buffer
	);

	if (len == 0)
	{
		::syslog(LOG_WARNING, 
			"Failed to convert string to wide string: "
			"error %i", GetLastError());
		errno = EACCES;
		delete [] buffer;
		return NULL;
	}

	return buffer;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    ConvertUtf8ToWideString
//		Purpose: Converts a string from UTF-8 to a wide string.
//			 Returns a buffer which MUST be freed by the caller 
//			 with delete[].
//			 In case of fire, logs the error and returns NULL.
//		Created: 4th February 2006
//
// --------------------------------------------------------------------------
WCHAR* ConvertUtf8ToWideString(const char* pString)
{
	return ConvertToWideString(pString, CP_UTF8);
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    ConvertFromWideString
//		Purpose: Converts a wide string to a narrow string in the
//			 specified code page. Returns a buffer which MUST 
//			 be freed by the caller with delete[].
//			 In case of fire, logs the error and returns NULL.
//		Created: 4th February 2006
//
// --------------------------------------------------------------------------
char* ConvertFromWideString(const WCHAR* pString, unsigned int codepage)
{
	int len = WideCharToMultiByte
	(
		codepage, // destination code page
		0,        // character-type options
		pString,  // string to map
		-1,       // number of bytes in string - auto detect
		NULL,     // output buffer
		0,        // size of buffer - work out 
		          //   how much space we need
		NULL,     // replace unknown chars with system default
		NULL      // don't tell us when that happened
	);

	if (len == 0)
	{
		::syslog(LOG_WARNING, 
			"Failed to convert wide string to narrow: "
			"error %d", GetLastError());
		errno = EINVAL;
		return NULL;
	}

	char* buffer = new char[len];

	if (buffer == NULL)
	{
		::syslog(LOG_WARNING, 
			"Failed to convert wide string to narrow: "
			"out of memory");
		errno = ENOMEM;
		return NULL;
	}

	len = WideCharToMultiByte
	(
		codepage, // source code page
		0,        // character-type options
		pString,  // string to map
		-1,       // number of bytes in string - auto detect
		buffer,   // output buffer
		len,      // size of buffer
		NULL,     // replace unknown chars with system default
		NULL      // don't tell us when that happened
	);

	if (len == 0)
	{
		::syslog(LOG_WARNING, 
			"Failed to convert wide string to narrow: "
			"error %i", GetLastError());
		errno = EACCES;
		delete [] buffer;
		return NULL;
	}

	return buffer;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    ConvertUtf8ToConsole
//		Purpose: Converts a string from UTF-8 to the console 
//			 code page. On success, replaces contents of rDest 
//			 and returns true. In case of fire, logs the error 
//			 and returns false.
//		Created: 4th February 2006
//
// --------------------------------------------------------------------------
bool ConvertUtf8ToConsole(const char* pString, std::string& rDest)
{
	WCHAR* pWide = ConvertToWideString(pString, CP_UTF8);
	if (pWide == NULL)
	{
		return false;
	}

	char* pConsole = ConvertFromWideString(pWide, GetConsoleOutputCP());
	delete [] pWide;

	if (!pConsole)
	{
		return false;
	}

	rDest = pConsole;
	delete [] pConsole;

	return true;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    ConvertConsoleToUtf8
//		Purpose: Converts a string from the console code page
//			 to UTF-8. On success, replaces contents of rDest
//			 and returns true. In case of fire, logs the error 
//			 and returns false.
//		Created: 4th February 2006
//
// --------------------------------------------------------------------------
bool ConvertConsoleToUtf8(const char* pString, std::string& rDest)
{
	WCHAR* pWide = ConvertToWideString(pString, GetConsoleCP());
	if (pWide == NULL)
	{
		return false;
	}

	char* pConsole = ConvertFromWideString(pWide, CP_UTF8);
	delete [] pWide;

	if (!pConsole)
	{
		return false;
	}

	rDest = pConsole;
	delete [] pConsole;

	return true;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    ConvertPathToAbsoluteUnicode
//		Purpose: Converts relative paths to absolute (with unicode marker)
//		Created: 4th February 2006
//
// --------------------------------------------------------------------------
std::string ConvertPathToAbsoluteUnicode(const char *pFileName)
{
	std::string tmpStr("\\\\?\\");
	
	// Is the path relative or absolute?
	// Absolute paths on Windows are always a drive letter
	// followed by ':'
	
	if (pFileName[1] != ':')
	{
		// Must be relative. We need to get the 
		// current directory to make it absolute.
		
		char wd[PATH_MAX];
		if (::getcwd(wd, PATH_MAX) == 0)
		{
			::syslog(LOG_WARNING, 
				"Failed to open '%s': path too long", 
				pFileName);
			errno = ENAMETOOLONG;
			tmpStr = "";
			return tmpStr;
		}
		
		tmpStr += wd;
		if (tmpStr[tmpStr.length()] != '\\')
		{
			tmpStr += '\\';
		}
	}
	
	tmpStr += pFileName;
	return tmpStr;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    openfile
//		Purpose: replacement for any open calls - handles unicode filenames - supplied in utf8
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
HANDLE openfile(const char *pFileName, int flags, int mode)
{
	std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pFileName);
	
	if (AbsPathWithUnicode.size() == 0)
	{
		// error already logged by ConvertPathToAbsoluteUnicode()
		return NULL;
	}
	
	WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str());
	// We are responsible for freeing pBuffer
	
	if (pBuffer == NULL)
	{
		// error already logged by ConvertUtf8ToWideString()
		return NULL;
	}

	// flags could be O_WRONLY | O_CREAT | O_RDONLY
	DWORD createDisposition = OPEN_EXISTING;
	DWORD shareMode = FILE_SHARE_READ;
	DWORD accessRights = FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA;

	if (flags & O_WRONLY)
	{
		shareMode = FILE_SHARE_WRITE;
	}
	if (flags & O_RDWR)
	{
		shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
	}
	if (flags & O_CREAT)
	{
		createDisposition = OPEN_ALWAYS;
		shareMode |= FILE_SHARE_WRITE;
		accessRights |= FILE_WRITE_ATTRIBUTES 
			| FILE_WRITE_DATA | FILE_WRITE_EA 
			| FILE_ALL_ACCESS;
	}
	if (flags & O_TRUNC)
	{
		createDisposition = CREATE_ALWAYS;
	}
	if (flags & O_EXCL)
	{
		shareMode = 0;
	}

	HANDLE hdir = CreateFileW(pBuffer, 
		accessRights, 
		shareMode, 
		NULL, 
		createDisposition, 
		FILE_FLAG_BACKUP_SEMANTICS,
		NULL);
	
	delete [] pBuffer;

	if (hdir == INVALID_HANDLE_VALUE)
	{
		::syslog(LOG_WARNING, "Failed to open file %s: "
			"error %i", pFileName, GetLastError());
		return NULL;
	}

	return hdir;
}

// MinGW provides a getopt implementation
#ifndef __MINGW32__
//works with getopt
char *optarg;
//optind looks like an index into the string - how far we have moved along
int optind = 1;
char nextchar = -1;
#endif

// --------------------------------------------------------------------------
//
// Function
//		Name:    emu_fstat
//		Purpose: replacement for fstat supply a windows handle
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
int emu_fstat(HANDLE hdir, struct stat * st)
{
	ULARGE_INTEGER conv;

	if (hdir == INVALID_HANDLE_VALUE)
	{
		::syslog(LOG_ERR, "Error: invalid file handle in emu_fstat()");
		errno = EBADF;
		return -1;
	}

	BY_HANDLE_FILE_INFORMATION fi;
	if (!GetFileInformationByHandle(hdir, &fi))
	{
		::syslog(LOG_WARNING, "Failed to read file information: "
			"error %d", GetLastError());
		errno = EACCES;
		return -1;
	}

	memset(st, 0, sizeof(*st));

	// This next example is how we get our INODE (equivalent) information
	conv.HighPart = fi.nFileIndexHigh;
	conv.LowPart = fi.nFileIndexLow;
	st->st_ino = (_ino_t)conv.QuadPart;

	// get the time information
	st->st_ctime = ConvertFileTimeToTime_t(&fi.ftCreationTime);
	st->st_atime = ConvertFileTimeToTime_t(&fi.ftLastAccessTime);
	st->st_mtime = ConvertFileTimeToTime_t(&fi.ftLastWriteTime);

	// size of the file
	LARGE_INTEGER st_size;
	if (!GetFileSizeEx(hdir, &st_size))
	{
		::syslog(LOG_WARNING, "Failed to get file size: error %d",
			GetLastError());
		errno = EACCES;
		return -1;
	}

	conv.HighPart = st_size.HighPart;
	conv.LowPart = st_size.LowPart;
	st->st_size = (_off_t)conv.QuadPart;

	//the mode of the file
	st->st_mode = 0;
	//DWORD res = GetFileAttributes((LPCSTR)tmpStr.c_str());

	if (INVALID_FILE_ATTRIBUTES != fi.dwFileAttributes)
	{
		if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			st->st_mode |= S_IFDIR;
		}
		else
		{
			st->st_mode |= S_IFREG;
		}
	}
	else
	{
		::syslog(LOG_WARNING, "Failed to get file attributes: "
			"error %d", GetLastError());
		errno = EACCES;
		return -1;
	}

	return 0;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    OpenFileByNameUtf8
//		Purpose: Converts filename to Unicode and returns 
//			a handle to it. In case of error, sets errno,
//			logs the error and returns NULL.
//		Created: 10th December 2004
//
// --------------------------------------------------------------------------
HANDLE OpenFileByNameUtf8(const char* pFileName)
{
	std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pFileName);
	
	if (AbsPathWithUnicode.size() == 0)
	{
		// error already logged by ConvertPathToAbsoluteUnicode()
		return NULL;
	}
	
	WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str());
	// We are responsible for freeing pBuffer
	
	if (pBuffer == NULL)
	{
		// error already logged by ConvertUtf8ToWideString()
		return NULL;
	}

	HANDLE handle = CreateFileW(pBuffer, 
		FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA, 
		FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, 
		NULL, 
		OPEN_EXISTING, 
		FILE_FLAG_BACKUP_SEMANTICS,
		NULL);

	if (handle == INVALID_HANDLE_VALUE)
	{
		// if our open fails we should always be able to 
		// open in this mode - to get the inode information
		// at least one process must have the file open - 
		// in this case someone else does.
		handle = CreateFileW(pBuffer, 
			0, 
			FILE_SHARE_READ, 
			NULL, 
			OPEN_EXISTING, 
			FILE_FLAG_BACKUP_SEMANTICS,
			NULL);
	}

	delete [] pBuffer;

	if (handle == INVALID_HANDLE_VALUE)
	{
		DWORD err = GetLastError();

		if (err == ERROR_FILE_NOT_FOUND)
		{
			errno = ENOENT;
		}
		else
		{
			::syslog(LOG_WARNING, 
				"Failed to open '%s': error %d", pFileName, err);
			errno = EACCES;
		}

		return NULL;
	}

	return handle;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    emu_stat 
//		Purpose: replacement for the lstat and stat functions, 
//			works with unicode filenames supplied in utf8 format
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
int emu_stat(const char * pName, struct stat * st)
{
	// at the mo
	st->st_uid = 0;
	st->st_gid = 0;
	st->st_nlink = 1;

	HANDLE handle = OpenFileByNameUtf8(pName);

	if (handle == NULL)
	{
		// errno already set and error logged by OpenFileByNameUtf8()
		return -1;
	}

	int retVal = emu_fstat(handle, st);
	if (retVal != 0)
	{
		// error logged, but without filename
		::syslog(LOG_WARNING, "Failed to get file information "
			"for '%s'", pName);
	}

	// close the handle
	CloseHandle(handle);

	return retVal;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    statfs
//		Purpose: returns the mount point of where a file is located - 
//			in this case the volume serial number
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
int statfs(const char * pName, struct statfs * s)
{
	HANDLE handle = OpenFileByNameUtf8(pName);

	if (handle == NULL)
	{
		// errno already set and error logged by OpenFileByNameUtf8()
		return -1;
	}

	BY_HANDLE_FILE_INFORMATION fi;
	if (!GetFileInformationByHandle(handle, &fi))
	{
		::syslog(LOG_WARNING, "Failed to get file information "
			"for '%s': error %d", pName, GetLastError());
		CloseHandle(handle);
		errno = EACCES;
		return -1;
	}

	// convert volume serial number to a string
	_ui64toa(fi.dwVolumeSerialNumber, s->f_mntonname + 1, 16);

	// pseudo unix mount point
	s->f_mntonname[0] = DIRECTORY_SEPARATOR_ASCHAR;

	CloseHandle(handle);   // close the handle

	return 0;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    opendir
//		Purpose: replacement for unix function, uses win32 findfirst routines
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
DIR *opendir(const char *name)
{
	if (!name || !name[0])
	{
		errno = EINVAL;
		return NULL;
	}
	
	std::string dirName(name);

	//append a '\' win32 findfirst is sensitive to this
	if ( dirName[dirName.size()] != '\\' || dirName[dirName.size()] != '/' )
	{
		dirName += '\\';
	}

	// what is the search string? - everything
	dirName += '*';

	DIR *pDir = new DIR;
	if (pDir == NULL)
	{
		errno = ENOMEM;
		return NULL;
	}

	pDir->name = ConvertUtf8ToWideString(dirName.c_str());
	// We are responsible for freeing dir->name
	
	if (pDir->name == NULL)
	{
		delete pDir;
		return NULL;
	}

	pDir->fd = _wfindfirst((const wchar_t*)pDir->name, &(pDir->info));

	if (pDir->fd == -1)
	{
		delete [] pDir->name;
		delete pDir;
		return NULL;
	}
		
	pDir->result.d_name = 0;
	return pDir;
}

// this kinda makes it not thread friendly!
// but I don't think it needs to be.
char tempbuff[MAX_PATH];

// --------------------------------------------------------------------------
//
// Function
//		Name:    readdir
//		Purpose: as function above
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
struct dirent *readdir(DIR *dp)
{
	try
	{
		struct dirent *den = NULL;

		if (dp && dp->fd != -1)
		{
			if (!dp->result.d_name || 
				_wfindnext(dp->fd, &dp->info) != -1)
			{
				den         = &dp->result;
				std::wstring input(dp->info.name);
				memset(tempbuff, 0, sizeof(tempbuff));
				WideCharToMultiByte(CP_UTF8, 0, dp->info.name, 
					-1, &tempbuff[0], sizeof (tempbuff), 
					NULL, NULL);
				//den->d_name = (char *)dp->info.name;
				den->d_name = &tempbuff[0];
			}
		}
		else
		{
			errno = EBADF;
		}
		return den;
	}
	catch (...)
	{
		printf("Caught readdir");
	}
	return NULL;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    closedir
//		Purpose: as function above
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
int closedir(DIR *dp)
{
	try
	{
		int finres = -1;
		if (dp)
		{
			if(dp->fd != -1)
			{
				finres = _findclose(dp->fd);
			}

			delete [] dp->name;
			delete dp;
		}

		if (finres == -1) // errors go to EBADF 
		{
			errno = EBADF;
		}

		return finres;
	}
	catch (...)
	{
		printf("Caught closedir");
	}
	return -1;
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    poll
//		Purpose: a weak implimentation (just enough for box) 
//			of the unix poll for winsock2
//		Created: 25th October 2004
//
// --------------------------------------------------------------------------
int poll (struct pollfd *ufds, unsigned long nfds, int timeout)
{
	try
	{
		fd_set readfd;
		fd_set writefd;

		readfd.fd_count = 0;
		writefd.fd_count = 0;

		struct pollfd *ufdsTmp = ufds;

		timeval timOut;
		timeval *tmpptr; 

		if (timeout == INFTIM)
			tmpptr = NULL;
		else
			tmpptr = &timOut;

		timOut.tv_sec  = timeout / 1000;
		timOut.tv_usec = timeout * 1000;

		if (ufds->events & POLLIN)
		{
			for (unsigned long i = 0; i < nfds; i++)
			{
				readfd.fd_array[i] = ufdsTmp->fd;
				readfd.fd_count++;
			}
		}

		if (ufds->events & POLLOUT)
		{
			for (unsigned long i = 0; i < nfds; i++)
			{

				writefd.fd_array[i]=ufdsTmp->fd;
				writefd.fd_count++;
			}
		}	

		int noffds = select(0, &readfd, &writefd, 0, tmpptr);

		if (noffds == SOCKET_ERROR)
		{
			// int errval = WSAGetLastError();

			ufdsTmp = ufds;
			for (unsigned long i = 0; i < nfds; i++)
			{
				ufdsTmp->revents = POLLERR;
				ufdsTmp++;
			}
			return (-1);
		}

		return noffds;
	}
	catch (...)
	{
		printf("Caught poll");
	}

	return -1;
}

HANDLE gSyslogH = 0;
static bool sHaveWarnedEventLogFull = false;

void syslog(int loglevel, const char *frmt, ...)
{
	WORD errinfo;
	char buffer[1024];
	std::string sixfour(frmt);

	switch (loglevel)
	{
	case LOG_INFO:
		errinfo = EVENTLOG_INFORMATION_TYPE;
		break;
	case LOG_ERR:
		errinfo = EVENTLOG_ERROR_TYPE;
		break;
	case LOG_WARNING:
		errinfo = EVENTLOG_WARNING_TYPE;
		break;
	default:
		errinfo = EVENTLOG_WARNING_TYPE;
		break;
	}

	// taken from MSDN
	int sixfourpos;
	while ( (sixfourpos = (int)sixfour.find("%ll")) != -1 )
	{
		// maintain portability - change the 64 bit formater...
		std::string temp = sixfour.substr(0,sixfourpos);
		temp += "%I64";
		temp += sixfour.substr(sixfourpos+3, sixfour.length());
		sixfour = temp;
	}

	// printf("parsed string is:%s\r\n", sixfour.c_str());

	va_list args;
	va_start(args, frmt);

	int len = vsnprintf(buffer, sizeof(buffer)-1, sixfour.c_str(), args);
	ASSERT(len < sizeof(buffer))
	buffer[sizeof(buffer)-1] = 0;

	va_end(args);

	LPCSTR strings[] = { buffer, NULL };

	if (!ReportEvent(gSyslogH, // event log handle 
		errinfo,               // event type 
		0,                     // category zero 
		MSG_ERR_EXIST,	       // event identifier - 
		                       // we will call them all the same
		NULL,                  // no user security identifier 
		1,                     // one substitution string 
		0,                     // no data 
		strings,               // pointer to string array 
		NULL))                 // pointer to data 

	{
		DWORD err = GetLastError();
		if (err == ERROR_LOG_FILE_FULL)
		{
			if (!sHaveWarnedEventLogFull)
			{
				printf("Unable to send message to Event Log "
					"(Event Log is full):\r\n");
				sHaveWarnedEventLogFull = TRUE;
			}
		}
		else
		{
			printf("Unable to send message to Event Log: "
				"error %i:\r\n", (int)err);
		}
	}
	else
	{
		sHaveWarnedEventLogFull = false;
	}

	printf("%s\r\n", buffer);
}

int emu_chdir(const char* pDirName)
{
	WCHAR* pBuffer = ConvertUtf8ToWideString(pDirName);
	if (!pBuffer) return -1;
	int result = SetCurrentDirectoryW(pBuffer);
	delete [] pBuffer;
	if (result != 0) return 0;
	errno = EACCES;
	return -1;
}

char* emu_getcwd(char* pBuffer, int BufSize)
{
	DWORD len = GetCurrentDirectoryW(0, NULL);
	if (len == 0)
	{
		errno = EINVAL;
		return NULL;
	}

	if (len > BufSize)
	{
		errno = ENAMETOOLONG;
		return NULL;
	}

	WCHAR* pWide = new WCHAR [len];
	if (!pWide)
	{
		errno = ENOMEM;
		return NULL;
	}

	DWORD result = GetCurrentDirectoryW(len, pWide);
	if (result <= 0 || result >= len)
	{
		errno = EACCES;
		return NULL;
	}

	char* pUtf8 = ConvertFromWideString(pWide, CP_UTF8);
	delete [] pWide;

	if (!pUtf8)
	{
		return NULL;
	}

	strncpy(pBuffer, pUtf8, BufSize - 1);
	pBuffer[BufSize - 1] = 0;
	delete [] pUtf8;

	return pBuffer;
}

int emu_mkdir(const char* pPathName)
{
	WCHAR* pBuffer = ConvertToWideString(pPathName, CP_UTF8);
	if (!pBuffer)
	{
		return -1;
	}

	BOOL result = CreateDirectoryW(pBuffer, NULL);
	delete [] pBuffer;

	if (!result)
	{
		errno = EACCES;
		return -1;
	}

	return 0;
}

int emu_unlink(const char* pFileName)
{
	WCHAR* pBuffer = ConvertToWideString(pFileName, CP_UTF8);
	if (!pBuffer)
	{
		return -1;
	}

	BOOL result = DeleteFileW(pBuffer);
	delete [] pBuffer;

	if (!result)
	{
		errno = EACCES;
		return -1;
	}

	return 0;
}

int console_read(char* pBuffer, size_t BufferSize)
{
	HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);

	if (hConsole == INVALID_HANDLE_VALUE)
	{
		::fprintf(stderr, "Failed to get a handle on standard input: "
			"error %d\n", GetLastError());
		return -1;
	}

	int WideSize = BufferSize / 5;
	WCHAR* pWideBuffer = new WCHAR [WideSize];

	if (!pWideBuffer)
	{
		::perror("Failed to allocate wide character buffer");
		return -1;
	}

	DWORD numCharsRead = 0;

	if (!ReadConsoleW(
			hConsole,
			pWideBuffer,
			WideSize - 1,
			&numCharsRead,
			NULL // reserved
		)) 
	{
		::fprintf(stderr, "Failed to read from console: error %d\n",
			GetLastError());
		return -1;
	}

	pWideBuffer[numCharsRead] = 0;

	char* pUtf8 = ConvertFromWideString(pWideBuffer, GetConsoleCP());
	strncpy(pBuffer, pUtf8, BufferSize);
	delete [] pUtf8;

	return strlen(pBuffer);
}

#endif // WIN32


syntax highlighted by Code2HTML, v. 0.9.1