#include "File.h"

namespace AScript {

//-----------------------------------------------------------------------------
// DateTime
//-----------------------------------------------------------------------------
String DateTime::ToString(const TCHAR *format) const
{
	String rtn;
	for (const TCHAR *p = format; *p != _T('\0'); p++) {
		if (*p != _T('%')) {
			rtn.push_back(*p);
			continue;
		}
		TCHAR buff[32];
		p++;
		TCHAR ch = *p;
		if (ch == _T('a')) {			// locale
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('A')) {		// locale
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('b')) {		// locale
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('B')) {		// locale
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('c')) {		// locale
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('d')) {
			::_stprintf(buff, _T("%02d"), _day);
			rtn += buff;
		} else if (ch == _T('H')) {
			::_stprintf(buff, _T("%02d"), _hour);
			rtn += buff;
		} else if (ch == _T('I')) {
			::_stprintf(buff, _T("%02d"),
						(_hour == 0)? 12 : (_hour > 12)? _hour - 12 : _hour);
			rtn += buff;
		} else if (ch == _T('j')) {
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('m')) {
			::_stprintf(buff, _T("%02d"), _month);
			rtn += buff;
		} else if (ch == _T('M')) {
			::_stprintf(buff, _T("%02d"), _minute);
			rtn += buff;
		} else if (ch == _T('p')) {		// locale
			
		} else if (ch == _T('S')) {
			::_stprintf(buff, _T("%02d"), _second);
			rtn += buff;
		} else if (ch == _T('U')) {
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('w')) {
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('W')) {
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('x')) {		// locale
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('X')) {		// locale
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('y')) {
			::_stprintf(buff, _T("%02d"), _year % 100);
			rtn += buff;
		} else if (ch == _T('Y')) {
			::_stprintf(buff, _T("%04d"), _year);
			rtn += buff;
		} else if (ch == _T('Z')) {
			::_stprintf(buff, _T("****"));
			rtn += buff;
		} else if (ch == _T('%')) {
			rtn.push_back(ch);
		} else {
			rtn.push_back(ch);
		}
	}
	return rtn;
}

int DateTime::Compare(const DateTime &dt1, const DateTime &dt2)
{
	long result = 0;
	if ((result = dt1._year - dt2._year) != 0) {
	} else if ((result = dt1._month - dt2._month) != 0) {
	} else if ((result = dt1._day - dt2._day) != 0) {
	} else if ((result = dt1._hour - dt2._hour) != 0) {
	} else if ((result = dt1._minute - dt2._minute) != 0) {
	} else if ((result = dt1._second - dt2._second) != 0) {
	} else if ((result = dt1._microsec - dt2._microsec) != 0) {
	}
	return (result < 0)? -1 : (result > 0)? +1 : 0;
}

//-----------------------------------------------------------------------------
// File
//-----------------------------------------------------------------------------
const TCHAR File::SeparatorWin = _T('\\');
const TCHAR File::SeparatorUnix = _T('/');
String *File::_pWorkingDir = NULL;
String *File::_pBaseDir = NULL;

#if defined(WINVER)
const TCHAR File::Separator = SeparatorWin;

File::File() : _hFile(INVALID_HANDLE_VALUE), _needCloseFlag(false),
							_accessMode(ACCESS_Read), _textFlag(false)
{
}

File::File(const File &file) : _hFile(file._hFile), _needCloseFlag(file._needCloseFlag),
	_fileName(file._fileName), _accessMode(file._accessMode), _textFlag(file._textFlag)
{
}

File &File::Open(Signal sig, const TCHAR *fileName, const TCHAR *mode)
{
	Close();
	do {
		const TCHAR *p = mode;
		if (*p == _T('r')) {
			_accessMode = ACCESS_Read;
		} else if (*p == _T('w')) {
			_accessMode = ACCESS_Write;
		} else if (*p == _T('a')) {
			_accessMode = ACCESS_Write;
		} else {
			sig.SetError(ERR_IOError, _T("invalid open mode"));
			return *this;
		}
		p++;
		_textFlag = true;
		for ( ; *p != _T('\0'); p++) {
			TCHAR ch = *p;
			if (ch == _T('+')) {
				_accessMode = ACCESS_ReadWrite;
			} else if (ch == _T('t')) {
				_textFlag = true;
			} else if (ch == _T('b')) {
				_textFlag = false;
			} else {
				sig.SetError(ERR_IOError, _T("invalid open mode"));
				return *this;
			}
		}
	} while (0);
	_fileName = MakeAbsPath(fileName);
	DWORD dwDesiredAccess =
			(_accessMode == ACCESS_Read)? GENERIC_READ :
			(_accessMode == ACCESS_Write)? GENERIC_WRITE :
			(_accessMode == ACCESS_ReadWrite)? GENERIC_READ | GENERIC_WRITE :
			GENERIC_READ;
	DWORD dwShareMode = FILE_SHARE_READ;
	DWORD dwCreationDisposition =
			(_accessMode == ACCESS_Read)? OPEN_EXISTING :
			(_accessMode == ACCESS_Write)? CREATE_ALWAYS :
			(_accessMode == ACCESS_ReadWrite)? OPEN_ALWAYS :
			OPEN_EXISTING;
	_hFile = ::CreateFile(_fileName.c_str(), dwDesiredAccess, dwShareMode,
					NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
	if (_hFile == INVALID_HANDLE_VALUE) {
		sig.SetError(ERR_IOError, _T("can't open file '%s'"), ExtractBaseName(fileName).c_str());
		return *this;
	}
	_needCloseFlag = true;
	return *this;
}

File &File::OpenStdin()
{
	Close();
	_hFile = ::GetStdHandle(STD_INPUT_HANDLE);
	if (_hFile != INVALID_HANDLE_VALUE) {
		_fileName = _T("stdin"), _accessMode = ACCESS_Read, _textFlag = true;
	}
	return *this;
}

File &File::OpenStdout()
{
	Close();
	_hFile = ::GetStdHandle(STD_OUTPUT_HANDLE);
	if (_hFile != INVALID_HANDLE_VALUE) {
		_fileName = _T("stdout"), _accessMode = ACCESS_Write, _textFlag = true;
	}
	return *this;
}

File &File::OpenStderr()
{
	Close();
	_hFile = ::GetStdHandle(STD_ERROR_HANDLE);
	if (_hFile != INVALID_HANDLE_VALUE) {
		_fileName = _T("stderr"), _accessMode = ACCESS_Write, _textFlag = true;
	}
	return *this;
}

void File::Close()
{
	if (_needCloseFlag && _hFile != INVALID_HANDLE_VALUE) {
		::CloseHandle(_hFile);
		_hFile = INVALID_HANDLE_VALUE;
		_needCloseFlag = false;
		_fileName = _T("<invalid>");
	}
}

int File::Seek(long offset, int origin)
{
	if (_hFile == INVALID_HANDLE_VALUE) return -1;
	DWORD dwMoveMethod =
		(origin == SEEK_SET)? FILE_BEGIN :
		(origin == SEEK_CUR)? FILE_CURRENT :
		(origin == SEEK_END)? FILE_END : FILE_BEGIN;
	return ::SetFilePointer(_hFile, offset, NULL, dwMoveMethod);
}

int File::GetChar()
{
	if (_hFile == INVALID_HANDLE_VALUE) return -1;
	DWORD dwBytesRead;
	unsigned char buff[1];
	::ReadFile(_hFile, buff, sizeof(buff), &dwBytesRead, NULL);
	if (_textFlag) {
		while (dwBytesRead > 0 && buff[0] == _T('\r')) {
			::ReadFile(_hFile, buff, sizeof(buff), &dwBytesRead, NULL);
		}
	}
	return (dwBytesRead == 0)? -1 : buff[0];
}

void File::PutChar(int ch)
{
	if (_hFile == INVALID_HANDLE_VALUE) return;
	unsigned char buff[2];
	DWORD dwBytesToWrite, dwBytesWritten;
	if (_textFlag && ch == _T('\n')) {
		dwBytesToWrite = 2;
		buff[0] = _T('\r');
		buff[1] = static_cast<unsigned char>(ch);
		::WriteFile(_hFile, buff, dwBytesToWrite, &dwBytesWritten, NULL);
	} else {
		dwBytesToWrite = 1;
		buff[0] = static_cast<unsigned char>(ch);
		::WriteFile(_hFile, buff, dwBytesToWrite, &dwBytesWritten, NULL);
	}
}

size_t File::Read(void *buff, size_t bytes)
{
	DWORD dwBytesRead;
	::ReadFile(_hFile, buff, bytes, &dwBytesRead, NULL);
	return static_cast<size_t>(dwBytesRead);
}

FileStat File::GetFileStatByHandle(Signal sig) const
{
	unsigned long attr = 0;
	BY_HANDLE_FILE_INFORMATION attrData;
	String pathName = MakeAbsPath(_fileName.c_str(), NULL);
	if (::GetFileInformationByHandle(_hFile, &attrData) == 0) {
		sig.SetError(ERR_IOError, _T("failed to get file status of %s"), pathName.c_str());
		return FileStat();
	}
	if (attrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
		attr |= FileStat::ATTR_Dir;
	} else {
		attr |= FileStat::ATTR_Reg;
	}
	if (attrData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
		attr |= 0666;
	} else {
		attr |= 0777;
	}
	SYSTEMTIME stCTime, stATime, stMTime;
	::FileTimeToSystemTime(&attrData.ftLastAccessTime, &stATime);
	::FileTimeToSystemTime(&attrData.ftLastWriteTime, &stMTime);
	::FileTimeToSystemTime(&attrData.ftCreationTime, &stCTime);
	return FileStat(pathName.c_str(), attr, attrData.nFileSizeLow,
			DateTime(stATime), DateTime(stMTime), DateTime(stCTime), 0, 0);
}

FileStat File::GetFileStat(Signal sig, const TCHAR *fileName)
{
	unsigned long attr = 0;
	WIN32_FILE_ATTRIBUTE_DATA attrData;
	String pathName = MakeAbsPath(fileName, NULL);
	if (::GetFileAttributesEx(pathName.c_str(), GetFileExInfoStandard, &attrData) == 0) {
		sig.SetError(ERR_IOError, _T("failed to get file status of %s"), pathName.c_str());
		return FileStat();
	}
	if (attrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
		attr |= FileStat::ATTR_Dir;
	} else {
		attr |= FileStat::ATTR_Reg;
	}
	if (attrData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
		attr |= 0666;
	} else {
		attr |= 0777;
	}
	SYSTEMTIME stCTime, stATime, stMTime;
	::FileTimeToSystemTime(&attrData.ftLastAccessTime, &stATime);
	::FileTimeToSystemTime(&attrData.ftLastWriteTime, &stMTime);
	::FileTimeToSystemTime(&attrData.ftCreationTime, &stCTime);
	return FileStat(pathName.c_str(), attr, attrData.nFileSizeLow,
			DateTime(stATime), DateTime(stMTime), DateTime(stCTime), 0, 0);
}

bool File::IsExist(const TCHAR *pathName)
{
	WIN32_FILE_ATTRIBUTE_DATA attrData;
	return ::GetFileAttributesEx(pathName, GetFileExInfoStandard, &attrData) != 0;
}

#else // !defined(WINVER)
const TCHAR File::Separator = SeparatorUnix;

File::File() : _fp(NULL), _needCloseFlag(false), _accessMode(ACCESS_Read), _textFlag(false)
{
}

File::File(const File &file) : _fp(file._fp), _needCloseFlag(file._needCloseFlag),
	_fileName(file._fileName), _accessMode(file._accessMode), _textFlag(file._textFlag)
{
}

File &File::Open(Signal sig, const TCHAR *fileName, const TCHAR *mode)
{
	Close();
	do {
		const TCHAR *p = mode;
		if (*p == _T('r')) {
			_accessMode = ACCESS_Read;
		} else if (*p == _T('w')) {
			_accessMode = ACCESS_Write;
		} else if (*p == _T('a')) {
			_accessMode = ACCESS_Write;
		} else {
			sig.SetError(ERR_IOError, _T("invalid open mode"));
			return *this;
		}
		p++;
		_textFlag = true;
		for ( ; *p != _T('\0'); p++) {
			TCHAR ch = *p;
			if (ch == _T('+')) {
				_accessMode = ACCESS_ReadWrite;
			} else if (ch == _T('t')) {
				_textFlag = true;
			} else if (ch == _T('b')) {
				_textFlag = false;
			} else {
				sig.SetError(ERR_IOError, _T("invalid open mode"));
				return *this;
			}
		}
	} while (0);
	_fileName = MakeAbsPath(fileName);
	_fp = ::_tfopen(_fileName.c_str(), mode);
	if (_fp == NULL) {
		sig.SetError(ERR_IOError,
				_T("can't open file '%s'"), ExtractBaseName(fileName).c_str());
		return *this;
	}
	_needCloseFlag = true;
	return *this;
}

File &File::OpenStdin()
{
	_fp = stdin;
	_fileName = _T("stdin"), _accessMode = ACCESS_Read, _textFlag = true;
	return *this;
}

File &File::OpenStdout()
{
	_fp = stdout;
	_fileName = _T("stdout"), _accessMode = ACCESS_Write, _textFlag = true;
	return *this;
}

File &File::OpenStderr()
{
	_fp = stderr;
	_fileName = _T("stderr"), _accessMode = ACCESS_Write, _textFlag = true;
	return *this;
}

void File::Close()
{
	if (_needCloseFlag && _fp != NULL) {
		::fclose(_fp);
		_fp = NULL;
		_needCloseFlag = false;
		_fileName = _T("<invalid>");
	}
}

int File::Seek(long offset, int origin)
{
	if (_fp == NULL) return -1;
	return ::fseek(_fp, offset, origin);
}

int File::GetChar()
{
	if (_fp == NULL) return -1;
	return ::fgetc(_fp);
}

void File::PutChar(int ch)
{
	if (_fp == NULL) return;
	::fputc(ch, _fp);
}

size_t File::Read(void *buff, size_t bytes)
{
	return ::fread(buff, 1, bytes, _fp);
}

FileStat File::GetFileStatByHandle(Signal sig) const
{
}

FileStat File::GetFileStat(Signal sig, const TCHAR *fileName)
{
}

bool File::IsExist(const TCHAR *pathName)
{
	struct stat stat;
	return ::stat(pathName, &stat) == 0;
}

#endif
void File::PutString(const TCHAR *str)
{
	for ( ; *str != _T('\0'); str++) PutChar(*str);
}

String File::ExtractDirName(const TCHAR *pathName)
{
	const TCHAR *p = pathName + ::_tcslen(pathName);
	for ( ; p >= pathName; p--) {
		if (IsSeparator(*p)) {
			String str;
			return RegulateName(str, pathName, p - pathName);
		}
	}
	return String(_T(""));
}

String File::ExtractBaseName(const TCHAR *pathName)
{
	const TCHAR *p = pathName + ::_tcslen(pathName);
	for ( ; p >= pathName; p--) {
		if (IsSeparator(*p)) {
			String str;
			return RegulateName(str, p + 1);
		}
	}
	return String(pathName);
}

void File::Split(const TCHAR *pathName, StringList &pathNameSepList)
{
	String field;
	if (IsSeparator(*pathName)) field.push_back(Separator);
	for (const TCHAR *p = pathName; *p != _T('\0'); p++) {
		if (IsSeparator(*p)) {
			if (!field.empty()) {
				pathNameSepList.push_back(field);
				field.clear();
			}
		} else {
			field.push_back(*p);
		}
	}
	if (!field.empty()) pathNameSepList.push_back(field);
}

bool File::HasWildCard(const TCHAR *pathName)
{
	for (const TCHAR *p = pathName; *p != _T('\0'); p++) {
		if (*p == _T('*') || *p == _T('?') || *p == _T('[') || *p == _T(']')) {
			return true;
		}
	}
	return false;
}

// dirName must be specified as an absolute path.
String File::MakeAbsPath(const TCHAR *fileName, const TCHAR *dirName)
{
	String str;
	if (dirName == NULL || dirName[0] == _T('\0') || dirName[0] == _T('.') &&
					(dirName[1] == _T('\0') ||
					 dirName[1] == Separator && dirName[2] == _T('\0'))) {
		dirName = GetWorkingDir().c_str();
	}
	if (IsSeparator(fileName[0])) {
		for (const TCHAR *p = dirName;
						*p != _T('\0') && !IsSeparator(*p); p++) {
			str += *p;
		}
	} else if (fileName[0] != _T('\0') && fileName[1] == _T(':')) {
		// absolute path in Windows style.
	} else {
		int lenDirName = ::_tcslen(dirName);
		if (lenDirName > 0 && IsSeparator(*(dirName + lenDirName - 1))) {
			lenDirName--;
		}
		for (;;) {
			if (*fileName == _T('.') && IsSeparator(*(fileName + 1))) {
				fileName += 2;
			} else if (*fileName == _T('.') && *(fileName + 1) == _T('.') &&
												IsSeparator(*(fileName + 2))) {
				int len = lenDirName;
				if (len > 0) len--;
				for ( ; len > 0 && !IsSeparator(*(dirName + len)); len--) ;
				if (len > 0) lenDirName = len;
				fileName += 3;
			} else {
				break;
			}
		}
		for (const TCHAR *p = dirName; lenDirName > 0; p++, lenDirName--) {
			str += IsSeparator(*p)? Separator : *p;
		}
		str += Separator;
	}
	return RegulateName(str, fileName);
}

String &File::RegulateName(String &result, const TCHAR *name)
{
	for (const TCHAR *p = name; *p != _T('\0'); p++) {
		result += IsSeparator(*p)? Separator : *p;
	}
	return result;
}

String &File::RegulateName(String &result, const TCHAR *name, size_t cnt)
{
	for (const TCHAR *p = name; *p != _T('\0') && cnt > 0; p++, cnt--) {
		result += IsSeparator(*p)? Separator : *p;
	}
	return result;
}

const String &File::GetWorkingDir()
{
	if (_pWorkingDir == NULL) {
		TCHAR *pathName;
		int maxLen = 256;
		for (int i = 0; i < 16; i++, maxLen += 128) {
			pathName = ::_getcwd(NULL, maxLen);
			if (pathName != NULL) break;
		}
		if (pathName == NULL) ::exit(1);
		size_t len = ::_tcslen(pathName);
		if (len > 0 && pathName[len - 1] == Separator) pathName[len - 1] = _T('\0');
		_pWorkingDir = new String(pathName);
		::free(pathName);
	}
	return *_pWorkingDir;
}

#if defined(WINVER)
const String &File::GetBaseDir()
{
	if (_pBaseDir == NULL) {
		TCHAR pathName[512];
		// Win32 API
		::GetModuleFileName(NULL, pathName, NUMBEROF(pathName));
		TCHAR *p = pathName + ::_tcslen(pathName);
		for ( ; p >= pathName; p--) {
			if (IsSeparator(*p)) {
				*p = _T('\0');
				break;
			}
		}
		_pBaseDir = new String(pathName);
	}
	return *_pBaseDir;
}

bool File::ListDir(Signal sig, const TCHAR *dirName,
				const TCHAR *pattern, bool ignoreCaseFlag, EntryList &entries)
{
	HANDLE hFind;
	WIN32_FIND_DATA findData;
	String pathName(dirName);
	if (!pathName.empty()) pathName += Separator;
	pathName += _T("*.*");
	hFind = ::FindFirstFile(pathName.c_str(), &findData);
	if (hFind == INVALID_HANDLE_VALUE) return false;
	do{
		const TCHAR *fileName = findData.cFileName;
		if (::_tcscmp(fileName, _T(".")) == 0 ||
							::_tcscmp(fileName, _T("..")) == 0) {
			// nothing to do
		} else if (pattern == NULL || IsMatchName(pattern, fileName, ignoreCaseFlag)) {
			DWORD attr = findData.dwFileAttributes;
			entries.push_back(Entry(fileName,
							(attr & FILE_ATTRIBUTE_DIRECTORY)? true : false));
		}
	} while (::FindNextFile(hFind, &findData));
	::FindClose(hFind);
	return true;
}
#else
const String &File::GetBaseDir()
{
	if (_pBaseDir == NULL) {
		_pBaseDir = new String(_T(""));
	}
	return *_pBaseDir;
}

#include <sys/types.h>
#include <dirent.h>

bool File::ListDir(Signal sig, const TCHAR *dirName,
				const TCHAR *pattern, bool ignoreCaseFlag, EntryList &entries)
{
	struct dirent entry;
	DIR *dirp = ::opendir(dirName);
	if (dirp == NULL) return false;
	bool rtn = true;
	for (;;) {
		struct dirent *pEntry = NULL;
		if (::readdir_r(dirp, &entry, &pEntry) != 0) {
			rtn = false;
			break;
		}
		if (pEntry == NULL) break;
		const TCHAR *fileName = pEntry->d_name;
		if (::_tcscmp(fileName, _T(".")) == 0 ||
							::_tcscmp(fileName, _T("..")) == 0) {
			// nothing to do
		} else if (pattern == NULL || IsMatchName(pattern, fileName, ignoreCaseFlag)) {
			fileNames.push_back(fileName);
		}
		//pEntry->d_ino;
		//pEntry->d_off;
		//pEntry->d_reclen;
		//pEntry->d_type;
		//pEntry->d_name;
		if (pEntry->d_type == DT_UNKNOWN) {
			// d_type is not supported by all file systems
			struct stat stat;
			String pathNameEntry = pathName;
			pathNameEntry += Separator;
			pathNameEntry += fileName;
			::lstat(entry, pathNameEntry.c_str(), &stat);
			S_ISREG(stat.st_mode)
			S_ISDIR(stat.st_mode)
			S_ISCHR(stat.st_mode)
			S_ISBLK(stat.st_mode)
			S_ISFIFO(stat.st_mode)
			S_ISLNK(stat.st_mode)
			S_ISSOCK(stat.st_mode)
		} else {
			// DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK
		}
	}
	::closedir(dirp);
	return rtn;
}
#endif

bool File::Glob(Signal sig, const TCHAR *pattern, bool ignoreCaseFlag, EntryList &entries)
{
	StringList pathNameSepList;
	Split(pattern, pathNameSepList);
	if (pathNameSepList.empty()) {
		// nothing to do
	} else if (pathNameSepList.size() == 1) {
		GlobSub(sig, String(_T("")), ignoreCaseFlag,
					pathNameSepList.begin(), pathNameSepList.end(), entries);
	} else {
		GlobSub(sig, pathNameSepList.front(), ignoreCaseFlag,
					pathNameSepList.begin() + 1, pathNameSepList.end(), entries);
	}
	return true;
}

void File::GlobSub(Signal sig, const String &dirName, bool ignoreCaseFlag,
		StringList::iterator pPathNameSep, StringList::iterator pPathNameSepEnd,
		EntryList &entries)
{
	if (pPathNameSep == pPathNameSepEnd) return;
	EntryList entriesRaw;
	bool bottomFlag = (pPathNameSep + 1 == pPathNameSepEnd);
	ListDir(sig, dirName.c_str(), pPathNameSep->c_str(), ignoreCaseFlag, entriesRaw);
	foreach (EntryList, pEntry, entriesRaw) {
		//::_tprintf(_T("  %s\n"), pFileName->c_str());
		String pathName = dirName;
		if (!pathName.empty()) pathName += Separator;
		pathName += pEntry->GetFileName();
		if (bottomFlag) {
			entries.push_back(Entry(pathName, pEntry->IsDirectory()));
		} else if (pEntry->IsDirectory()) {
			GlobSub(sig, pathName, ignoreCaseFlag,
									pPathNameSep + 1, pPathNameSepEnd, entries);
		}
	}
}

bool File::IsMatchName(const TCHAR *pattern, const TCHAR *fileName, bool ignoreCaseFlag)
{
	if (*pattern == _T('\0')) {
		return *fileName == _T('\0');
	} else if (*pattern == _T('*')) {
		return IsMatchName(pattern + 1, fileName, ignoreCaseFlag) ||
			*fileName != _T('\0') && IsMatchName(pattern, fileName + 1, ignoreCaseFlag);
	} else if (*pattern == _T('?')) {
		return *fileName != _T('\0') && IsMatchName(pattern + 1, fileName + 1, ignoreCaseFlag);
	} else if (*pattern == _T('[')) {
		bool negFlag = false;
		pattern++;
		if (*pattern == _T('!')) {
			negFlag = true;
			pattern++;
			for ( ; *pattern != _T(']') && *pattern != _T('\0'); pattern++) {
				if (CompareChar(*fileName, *pattern, ignoreCaseFlag) == 0) return false;
			}
		} else {
			for ( ; *pattern != _T(']') && *pattern != _T('\0'); pattern++) {
				if (CompareChar(*fileName, *pattern, ignoreCaseFlag) != 0) return false;
			}
		}
		if (*pattern == _T(']')) pattern++;
		return IsMatchName(pattern, fileName + 1, ignoreCaseFlag);
	} else {
		return CompareChar(*pattern, *fileName, ignoreCaseFlag) == 0 &&
						IsMatchName(pattern + 1, fileName + 1, ignoreCaseFlag);
	}
}

//-----------------------------------------------------------------------------
// File::Formatter
//-----------------------------------------------------------------------------
void File::Formatter::PutChar(TCHAR ch)
{
	_file.PutChar(ch);
}

}
