/*
								+---------------------------------+
								|                                 |
								|   ***   Directory lookup  ***   |
								|                                 |
								|  Copyright  © -tHE SWINe- 2007  |
								|                                 |
								|             Dir.cpp             |
								|                                 |
								+---------------------------------+
*/

/**
 *	@file Dir.cpp
 *	@author -tHE SWINe-
 *	@date 2006
 *	@brief basic directory lookup and file info functions
 *
 *	@date 2007-07-03
 *
 *	passed code revision
 *
 *	added filename allocation, deallocation and copy functions to TFileInfo
 *
 *	added constructors to TFileInfo::TTime
 *
 *	added TFileInfo::b_valid so it's clear wheter call to CDirectory::Get_FirstFile
 *	or CDirectory::Get_NextFile functions failed or wheter there are no more files in
 *	the directory (as was identified by the same return value before)
 *
 *	merged source code of linux CDirectory and win32 CDirectory together as it shares
 *	substantial sections
 *
 *	created Dir.cpp
 *
 *	added CDirTraversal class
 *
 *	@date 2007-07-09
 *
 *	added TFileInfo copy-constructor TFileInfo(const TFileInfo &r_t_other)
 *	changed CDirTraversal's stack to queue so directory traversal is not reversed
 *
 *	@date 2008-02-21
 *
 *	fixed Win32 implementation of CDir::Get_FirstFile where it didn't detect
 *	failure properly and sometimes it could return invalid file data
 *
 *	@date 2008-02-27
 *
 *	fixend inconsistency between linux and windows implementation (in linux, TFileInfo used
 *	to contain bare filenames only, in windows it contained full paths. full paths are now
 *	used in both implementations). note this error made CDirTraversal fail under linux.
 *
 *	@date 2008-03-04
 *
 *	now using Integer.h header
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64
 *
 *	@date 2008-10-25
 *
 *	added CDirectory::path_Separator
 *
 *	@date 2008-11-21
 *
 *	added CDirTraversal::Traverse2, enabling file listener to interrupt directory traversal
 *
 *	added parameter b_recurse_subdirectories to both original CDirTraversal::Traverse() and
 *	new CDirTraversal::Traverse2() functions.
 *
 *	cleared-up CDirTraversal class documentation comment, regarding recursive traversal
 *	without recursion (ie. using explicit stack, placed on heap)
 *
 *	@date 2008-12-11
 *
 *	added TFileInfo::n_Size64() for greater conveniency
 *
 *	@date 2008-12-22
 *
 *	removed some g++ warnings
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 */

#include "NewFix.h"

#include "CallStack.h"
#include <vector>
#include <algorithm>
#include <utility>
#include "Dir.h"
#include "StlUtils.h"

/*
 *								=== TFileInfo::TTime ===
 */

#if defined(_WIN32) || defined(_WIN64)
/*
 *	TFileInfo::TTime::TTime(const SYSTEMTIME &r_t_time)
 *		- (win32) conversion constructor, taking struct SYSTEMTIME as input
 *		- always succeeds
 */
TFileInfo::TTime::TTime(const SYSTEMTIME &r_t_time)
	:n_month(r_t_time.wMonth), n_day(r_t_time.wDay), n_year(r_t_time.wYear),
	n_hour(r_t_time.wHour), n_minute(r_t_time.wMinute), n_second(r_t_time.wSecond)
{}

/*
 *	TFileInfo::TTime::TTime(const FILETIME &r_t_time)
 *		- (win32) conversion constructor, taking struct FILETIME as input
 *		- note FILETIME needs to be converted to SYSTEMTIME first,
 *		  in case conversion fails, all members are set to -1
 */
TFileInfo::TTime::TTime(const FILETIME &r_t_time)
{
	SYSTEMTIME t_time;
	if(!FileTimeToSystemTime(&r_t_time, &t_time)) {
		n_month = n_day = n_year =
			n_hour = n_minute = n_second = -1;
		return;
	}
	n_month = t_time.wMonth;
	n_day = t_time.wDay;
	n_year = t_time.wYear;
	n_hour = t_time.wHour;
	n_minute = t_time.wMinute;
	n_second = t_time.wSecond;
}
#else // _WIN32 || _WIN64
/*
 *	TFileInfo::TTime::TTime(const tm *p_time)
 *		- (unix) conversion constructor, taking struct tm as input
 *		- always succeeds
 */
TFileInfo::TTime::TTime(const tm *p_time)
	:n_month(p_time->tm_mon), n_day(p_time->tm_mday), n_year(p_time->tm_year + 1900),
	n_hour(p_time->tm_hour), n_minute(p_time->tm_min), n_second(p_time->tm_sec)
{}
#endif // _WIN32 || _WIN64

/*
 *	bool TFileInfo::TTime::operator ==(const TTime &r_t_time) const
 *		- equality operator
 *		- returns true in case this is equal to r_t_time, otherwise false
 */
bool TFileInfo::TTime::operator ==(const TTime &r_t_time) const
{
	return n_month == r_t_time.n_month &&
		n_day == r_t_time.n_day &&
		n_year == r_t_time.n_year &&
		n_hour == r_t_time.n_hour &&
		n_minute == r_t_time.n_minute &&
		n_second == r_t_time.n_second;
}

bool TFileInfo::TTime::operator <(const TTime &r_t_time) const
{
	return 
		n_year < r_t_time.n_year || (n_year == r_t_time.n_year && (
			n_month < r_t_time.n_month || (n_month == r_t_time.n_month && (
				n_day < r_t_time.n_day || (n_day == r_t_time.n_day && (
					n_hour < r_t_time.n_hour || (n_hour == r_t_time.n_hour && (
						n_minute < r_t_time.n_minute || (n_minute == r_t_time.n_minute && (
							n_second < r_t_time.n_second
						))
					))
				))
			))
		));
}
/*
 *								=== ~TFileInfo::TTime ===
 */
	
/*
 *								=== TFileInfo ===
 */

/*TFileInfo::TFileInfo(const char *p_s_filename) // moved to .h
{
	b_valid = SetFilename(p_s_filename) && GetInfo();
	// set filename and get info about the file
}*/

const char *TFileInfo::p_s_FileName() const
{
	size_t n_pos;
#if defined(_WIN32) || defined(_WIN64)
	if((n_pos = s_filename.find_last_of("/\\")) != std::string::npos)
#else // defined(_WIN32) || defined(_WIN64)
	if((n_pos = s_filename.rfind(CDirectory::path_Separator)) != std::string::npos)
#endif // defined(_WIN32) || defined(_WIN64)
		return s_filename.c_str() + n_pos + 1;
	else
		return s_filename.c_str();
}

const char *TFileInfo::p_s_FileName(const char *p_s_path)
{
	_ASSERTE(p_s_path); // may not be null
	const char *p_s_filename;
#if defined(_WIN32) || defined(_WIN64)
	if((p_s_filename = strrchr(p_s_path, '\\'))) { // todo - transform to a loop, declare a macro for filesystem accepting multiple path separators, declare a constant for path separator variants (CDirectory::path_Separator still remains a preferred separator)
		const char *p_s_filename2;
		if((p_s_filename2 = strrchr(p_s_filename, '/')))
			p_s_filename = p_s_filename2;
		return p_s_filename + 1;
	} else if((p_s_filename = strrchr(p_s_path, '/')))
		return p_s_filename + 1;
	else
		return p_s_path;
#else // defined(_WIN32) || defined(_WIN64)
	if((p_s_filename = strrchr(p_s_path, CDirectory::path_Separator)))
		return p_s_filename + 1;
	else
		return p_s_path;
#endif // defined(_WIN32) || defined(_WIN64)
}

const char *TFileInfo::p_s_Extension(const char *p_s_path)
{
	_ASSERTE(p_s_path); // may not be null
	const char *p_s_ext = strrchr(p_s_path, '.');
#if defined(_WIN32) || defined(_WIN64)
	if(p_s_ext && !strchr(p_s_ext, '/') && !strchr(p_s_ext, '\\'))
#else // defined(_WIN32) || defined(_WIN64)
	if(p_s_ext && !strchr(p_s_ext, CDirectory::path_Separator))
#endif // defined(_WIN32) || defined(_WIN64)
		return p_s_ext + 1;
	return "";
}

const char *TFileInfo::p_s_Extension() const
{
	size_t n_pos;
	if((n_pos = s_filename.rfind('.')) != std::string::npos) {
		size_t n_pos2;
#if defined(_WIN32) || defined(_WIN64)
		if((n_pos2 = s_filename.find_first_of("/\\", n_pos)) != std::string::npos)
#else // defined(_WIN32) || defined(_WIN64)
		if((n_pos2 = s_filename.find(CDirectory::path_Separator, n_pos)) != std::string::npos)
#endif // defined(_WIN32) || defined(_WIN64)
			return ""; // there is no '.' after last path separator
		return s_filename.c_str() + n_pos + 1;
	} else
		return ""; // no extension
}

/*
 *	bool TFileInfo::SetFilename(const char *p_s_new_filename)
 *		- sets filename to p_s_new_filename
 *		- returns true on success, false on failure
 *		- note p_s_new_filename can be 0 (sets this filename to 0 as well)
 */
bool TFileInfo::SetFilename(const char *p_s_new_filename)
{
	if(p_s_new_filename) {
		if(!stl_ut::AssignCStr(s_filename, p_s_new_filename))
			return false;

		__STL_UT_TRY {
			s_filename.c_str();
		} __STL_UT_CATCH(std::bad_alloc) {
			return false;
		}
		// make sure c_str() won't throw
	} else
		s_filename.erase();
	// copy filename

	return true;
}

/*
 *	bool TFileInfo::operator =(const TFileInfo &r_t_file_info)
 *		- copy-operator
 *		- returns true on success, false in case there
 *		  was not enough memory to hold filename string
 */
bool TFileInfo::operator =(const TFileInfo &r_t_file_info)
{
	if(!stl_ut::Assign(s_filename, r_t_file_info.s_filename)) {
		b_valid = false;
		return false;
	}
	// copy filename

	b_valid = r_t_file_info.b_valid;
	b_exists = r_t_file_info.b_exists;
	b_directory = r_t_file_info.b_directory;
	n_mode = r_t_file_info.n_mode;
	n_flags = r_t_file_info.n_flags;
	n_size_lo = r_t_file_info.n_size_lo;
	n_size_hi = r_t_file_info.n_size_hi;
	for(int n = 0; n < 3; ++ n)
		p_time[n] = r_t_file_info.p_time[n];
	// copy static data

	return true;
}

bool TFileInfo::Get_TempFileName(std::string &r_s_temp_file_name, const char *p_s_app_id)
{
	_ASSERTE(p_s_app_id && strlen(p_s_app_id));
	// may not be emtpy

#if defined(_WIN32) || defined(_WIN64)
	std::string s_temp_path;
	if(!stl_ut::Resize_To_N(s_temp_path, GetTempPath(0, NULL) + 1) ||
	   !GetTempPathA((DWORD)s_temp_path.size(), &s_temp_path[0])) // size won't exceed DWORD_MAX, since it's read from DWORD
		return false; // something went wrong
	s_temp_path.resize(strlen(s_temp_path.c_str()));
	// get temp path (eg. "c:\windows\temp")

	if(!stl_ut::Resize_To_N(r_s_temp_file_name, s_temp_path.length() + 16 + strlen(p_s_app_id)) ||
	   !GetTempFileNameA(s_temp_path.c_str(), p_s_app_id, 0, &r_s_temp_file_name[0]))
		return false; // something went wrong
	r_s_temp_file_name.resize(strlen(r_s_temp_file_name.c_str()));
	// get temp filename
#else // _WIN32 || _WIN64
	std::string s_tmp;
	if(!CPath::Get_TempDirectory(s_tmp) || // use "proper" temp (e.g. on cluster computers, temp is often directory specific to the job id, like "/tmp/pbs.132048.dm2")
	   !stl_ut::Format(r_s_temp_file_name, "%s/%sXXXXXX", s_tmp.c_str(), p_s_app_id)) // had 8 X's // 2012-07-17 change // t_odo - carry this change to documentation
		return false;														// 2013-11-13 changed template to not include the /tmp folder as that might not be the location of tmp
	// make template

	int n_file;
	if((n_file = mkstemp((char*)r_s_temp_file_name.c_str())) < 0)
		return false;
	close(n_file);
	// create temp file
#endif // _WIN32 || _WIN64

	return true;
}

#if defined(_WIN32) || defined(_WIN64)
bool TFileInfo::GetInfo()
{
	const char *p_s_filename = p_s_Path();

	WIN32_FIND_DATAA t_find_data;
	if(!p_s_filename) {
		b_valid = false; // isn't valid
		b_exists = false;
		return true;
	}
	HANDLE h_find;
	if((h_find = FindFirstFileA(p_s_filename, &t_find_data)) == INVALID_HANDLE_VALUE) {
		char p_s_buffer[4] = {0};
		if(GetFullPathNameA(p_s_filename, 4, p_s_buffer, NULL) > 0 && p_s_buffer[3] == 0) {
			if(strlen(p_s_buffer) == 3 && isalpha(p_s_buffer[0]) && p_s_buffer[1] == ':' && p_s_buffer[2] == '\\')
				p_s_filename = p_s_buffer;
		}
		// try getting full path, in case it looks like root directory, use it

		if(strlen(p_s_filename) == 3 && isalpha(p_s_filename[0]) && p_s_filename[1] == ':' && p_s_filename[2] == '\\') {
			memset(&t_find_data, 0, sizeof(t_find_data));
			t_find_data.dwFileAttributes = GetFileAttributesA(p_s_filename);
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			strncpy_s(t_find_data.cFileName, sizeof(t_find_data.cFileName), p_s_Path(), sizeof(t_find_data.cFileName) / sizeof(t_find_data.cFileName[0]));
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			strncpy(t_find_data.cFileName, p_s_Path(), sizeof(t_find_data.cFileName) / sizeof(t_find_data.cFileName[0]));
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			t_find_data.cFileName[sizeof(t_find_data.cFileName) /
				sizeof(t_find_data.cFileName[0])] = 0; // make sure it's null-terminated
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			strncpy_s(t_find_data.cAlternateFileName, sizeof(t_find_data.cAlternateFileName), p_s_filename, sizeof(t_find_data.cAlternateFileName) / sizeof(t_find_data.cAlternateFileName[0]));
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			strncpy(t_find_data.cAlternateFileName, p_s_filename, sizeof(t_find_data.cAlternateFileName) / sizeof(t_find_data.cAlternateFileName[0]));
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			t_find_data.cAlternateFileName[sizeof(t_find_data.cAlternateFileName) /
				sizeof(t_find_data.cAlternateFileName[0])] = 0; // make sure it's null-terminated
			// fill WIN32_FIND_DATA

			/*HANDLE h_file;
			if((h_file = CreateFile(p_s_filename, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
			   NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) {
			    GetFileTime(h_file, &t_find_data.ftCreationTime,
					&t_find_data.ftLastAccessTime, &t_find_data.ftLastWriteTime);
				CloseHandle(h_file);
			}
			// fill ftCreationTime, ftLastAccessTime and ftLastWriteTime*/
			// roots doesn't have file time under windows

			return GetInfo(t_find_data);
		}
		// this is root directory, FindFirstFile() cannot access those

		b_valid = true;
		b_exists = false; // doesn't exist
		return true;
	}
	FindClose(h_find); // !!
	// find the file

	return GetInfo(t_find_data);
	// use overload
}

bool TFileInfo::GetInfo(const WIN32_FIND_DATAA &r_t_find_data)
{
	//_ASSERTE(!strcmpi(p_s_FileName(), 
	/*if(!stl_ut::AssignCStr(s_filename, r_t_find_data.cFileName)) {
		b_valid = false;
		return false;
	}*/
	// make sure it's the right WIN32_FIND_DATA

	const char *p_s_filename = p_s_Path();
	if(!p_s_filename) {
		b_valid = false;
		return false;
	}
	// get filename

	b_exists = true;
	//_ASSERTE(!_access(p_s_filename, 00)); // it exist, right? // it routinely fails on files with unicode names
	// we have r_t_find_data, file does exist

	if(!_access(p_s_filename, 06))
		n_mode = flag_Read | flag_Write;
	else if(!_access(p_s_filename, 02))
		n_mode = flag_Read;
	else if(!_access(p_s_filename, 04))
		n_mode = flag_Write;
	// what about file mode?

	n_flags = 0;
	if(_access(p_s_filename, 2) == 0)
		n_flags |= (int)TFileInfo::flag_Write;
	if(_access(p_s_filename, 4) == 0)
		n_flags |= (int)TFileInfo::flag_Read | (int)TFileInfo::flag_Execute;
	// get access flags

	b_directory = (r_t_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
	n_size_lo = r_t_find_data.nFileSizeLow;
	n_size_hi = r_t_find_data.nFileSizeHigh;

	p_time[TFileInfo::time_Creation] = TFileInfo::TTime(r_t_find_data.ftCreationTime);
	p_time[TFileInfo::time_LastAccess] = TFileInfo::TTime(r_t_find_data.ftLastAccessTime);
	p_time[TFileInfo::time_LastWrite] = TFileInfo::TTime(r_t_find_data.ftLastWriteTime);
	if(p_time[TFileInfo::time_Creation].n_year < 0 ||
	   p_time[TFileInfo::time_LastAccess].n_year < 0 ||
	   p_time[TFileInfo::time_LastWrite].n_year < 0) {
		b_valid = false;
		return false;
	}
	// get time

	b_valid = true;
	// file data are valid

	return true;
}
#else // _WIN32 || _WIN64
bool TFileInfo::GetInfo()
{
	const char *p_s_filename = p_s_Path();
	if(!p_s_filename) {
		b_valid = false;
		return false;
	}
	// get file name

	struct stat t_stat;
	if(stat(p_s_filename, &t_stat)) {
		b_exists = false;
		b_valid = true; // doesn't exist
		return true;
	}
	// get file stat

	b_directory = S_ISDIR(t_stat.st_mode) != 0;// == S_IFDIR;
	// is it a directory?

	if(!access(p_s_filename, 00))
		b_exists = true;
	else
		b_exists = false;
	// does it exist?

	if(!access(p_s_filename, 06))
		n_mode = flag_Read | flag_Write;
	else if(!access(p_s_filename, 02))
		n_mode = flag_Read;
	else if(!access(p_s_filename, 04))
		n_mode = flag_Write;
	// what about file mode?

	n_flags = 0;
	if(t_stat.st_mode & S_IROTH)
		n_flags |= (int)TFileInfo::flag_other_Read;
	if(t_stat.st_mode & S_IWOTH)
		n_flags |= (int)TFileInfo::flag_other_Write;
	if(t_stat.st_mode & S_IXOTH)
		n_flags |= (int)TFileInfo::flag_other_Execute;
	//
	if(t_stat.st_mode & S_IRGRP)
		n_flags |= (int)TFileInfo::flag_group_Read;
	if(t_stat.st_mode & S_IWGRP)
		n_flags |= (int)TFileInfo::flag_group_Write;
	if(t_stat.st_mode & S_IXGRP)
		n_flags |= (int)TFileInfo::flag_group_Execute;
	//
	if(t_stat.st_mode & S_IRUSR)
		n_flags |= (int)TFileInfo::flag_owner_Read;
	if(t_stat.st_mode & S_IWUSR)
		n_flags |= (int)TFileInfo::flag_owner_Write;
	if(t_stat.st_mode & S_IXUSR)
		n_flags |= (int)TFileInfo::flag_owner_Execute;
	// get file / directory flags

#if 1 // (sizeof(t_off)) > 32
	n_size_lo = uint64_t(t_stat.st_size) & 0xffffffff;
	n_size_hi = uint64_t(t_stat.st_size) >> 32;
#else // this might prevent some warnings, if (sizeof(t_off)) <= 32
	n_size_lo = t_stat.st_size;
	n_size_hi = 0;
#endif
	p_time[TFileInfo::time_Creation] = TFileInfo::TTime(localtime(&t_stat.st_ctime));
	p_time[TFileInfo::time_LastAccess] = TFileInfo::TTime(localtime(&t_stat.st_atime));
	p_time[TFileInfo::time_LastWrite] = TFileInfo::TTime(localtime(&t_stat.st_mtime));
	// fill output structure

	b_valid = true;
	// file info is valid

	return true;
}
#endif // _WIN32 || _WIN64

/*
 *								=== ~TFileInfo ===
 */

/*
 *								=== CDirectory ===
 */

void CDirectory::Initialize(const char *p_s_dir_name)
{
#if defined(_WIN32) || defined(_WIN64)
	m_h_prev_file = 0;
#else // _WIN32 || _WIN64
	m_p_dir = 0;
#endif // _WIN32 || _WIN64

#if defined(_WIN32) || defined(_WIN64)
	int n_extra_chars = (p_s_dir_name[strlen(p_s_dir_name) - 1] == '\\' ||
		p_s_dir_name[strlen(p_s_dir_name) - 1] == '/')? 2 : 3;
	if(m_p_s_dir_name = new(std::nothrow) char[strlen(p_s_dir_name) + n_extra_chars]) {
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		size_t n_size = (strlen(p_s_dir_name) + n_extra_chars) * sizeof(char);
		strcpy_s(m_p_s_dir_name, n_size, p_s_dir_name);
		strcat_s(m_p_s_dir_name, n_size, (n_extra_chars == 2)? "*" : "\\*");
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(m_p_s_dir_name, p_s_dir_name);
		strcat(m_p_s_dir_name, (n_extra_chars == 2)? "*" : "\\*");
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		// make sure m_p_s_dir_name ends with \*, i.e. "c:\blah\*" or "e:\*"

		_ASSERTE(path_Separator == '\\');
		for(char *p_s_str = m_p_s_dir_name; *p_s_str; ++ p_s_str) {
			if(*p_s_str == '/')
				*p_s_str = path_Separator;
		}
		// make sure path separator is the right one
	}
#else // _WIN32 || _WIN64
	if((m_p_s_dir_name = new(std::nothrow) char[strlen(p_s_dir_name) + 1])) {
		strcpy(m_p_s_dir_name, p_s_dir_name);
		while(m_p_s_dir_name[strlen(m_p_s_dir_name) - 1] == '\\' ||
		   m_p_s_dir_name[strlen(m_p_s_dir_name) - 1] == '/')
			m_p_s_dir_name[strlen(m_p_s_dir_name) - 1] = 0;
		// make sure m_p_s_dir_name ends with dir name, i.e. "c:\blah" ("blah") or "e:" ("e:")

		_ASSERTE(path_Separator == '/');
		for(char *p_s_str = m_p_s_dir_name; *p_s_str; ++ p_s_str) {
			if(*p_s_str == '\\')
				*p_s_str = path_Separator;
		}
		// make sure path separator is the right one
	}
#endif // _WIN32 || _WIN64
}

/*
 *	CDirectory::~CDirectory()
 *		- default destructor
 */
CDirectory::~CDirectory()
{
	if(m_p_s_dir_name) {
		delete[] m_p_s_dir_name;
		m_p_s_dir_name = 0;
	}
#if !defined(_WIN32) && !defined(_WIN64)
	if(m_p_dir) {
		closedir(m_p_dir);
		m_p_dir = 0;
	}
#else // _WIN32 || _WIN64
	if(m_h_prev_file)
		FindClose(m_h_prev_file);
	// be polite and close handles
#endif // _WIN32 || _WIN64
}

#if defined(_WIN32) || defined(_WIN64)
/*
 *	bool CDirectory::Get_FirstFile(TFileInfo &r_t_file)
 *		- gets first file in the directory, copies it's data to r_t_file
 *		- in case the directory doesn't have any files, r_t_file.b_valid is set to false
 *		- returns true on success, false on failure (possibly inaccesible directory)
 */
bool CDirectory::Get_FirstFile(TFileInfo &r_t_file)
{
	WIN32_FIND_DATAA t_find_data;

	if(m_h_prev_file)
		FindClose(m_h_prev_file);
	// be polite and close handles

	if((m_h_prev_file = FindFirstFileA(m_p_s_dir_name,
	   &t_find_data)) == INVALID_HANDLE_VALUE) {
		r_t_file.SetNoFile();
		return true;
	}
	// no files

	if(!strcmp(".", t_find_data.cFileName) || !strcmp("..", t_find_data.cFileName))
		return Get_NextFile(r_t_file);
	// don't want self '.' and up '..'

	return GetFileInfo(t_find_data, r_t_file);
}

/*
 *	bool CDirectory::Get_NextFile(TFileInfo &r_t_file)
 *		- gets next file in the directory, copies it's data to r_t_file
 *		- in case the directory doesn't have any more files, r_t_file.b_valid is set to false
 *		- returns true on success, false on failure (possibly inaccesible directory)
 *		- in case Get_FirstFile was not called prior calling Get_NextFile, it's called
 *		  automatically. in order to get file list for this folder again, Get_FirstFile
 *		  must be called (r_t_file contents are only written to, not read)
 */
bool CDirectory::Get_NextFile(TFileInfo &r_t_file)
{
	WIN32_FIND_DATAA t_find_data;

	if(!m_h_prev_file)
		return Get_FirstFile(r_t_file);
	while(1) {
		if(!FindNextFileA(m_h_prev_file, &t_find_data)) {
			r_t_file.SetNoFile();
			return true;
		}
		// no more files

		if(!strcmp(".", t_find_data.cFileName) || !strcmp("..", t_find_data.cFileName))
			continue;
		// don't want self '.' and up '..'

		break;
	}

	return GetFileInfo(t_find_data, r_t_file);
}

bool CDirectory::GetFileInfo(const WIN32_FIND_DATAA &r_t_find_data, TFileInfo &r_t_file)
{
	char *p_s_full_name;
	size_t n_length = strlen(r_t_find_data.cFileName) + strlen(m_p_s_dir_name);
	if(!(p_s_full_name = new(std::nothrow) char[n_length])) {
		r_t_file.SetInvalid();
		return false;
	}
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	strcpy_s(p_s_full_name, n_length * sizeof(char), m_p_s_dir_name);
	strcpy_s(p_s_full_name + strlen(p_s_full_name) - 1,
		(n_length - strlen(p_s_full_name) + 1) * sizeof(char), r_t_find_data.cFileName);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	strcpy(p_s_full_name, m_p_s_dir_name);
	strcpy(p_s_full_name + strlen(p_s_full_name) - 1, r_t_find_data.cFileName);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	// full filename (overwrite '*' from m_p_s_dir_name -> no extra space for terminating 0)

	if(!r_t_file.SetFilename(p_s_full_name)) {
		delete[] p_s_full_name;
		r_t_file.SetInvalid();
		return false;
	}
	// copy full filename

	delete[] p_s_full_name;
	// cleanup

	return r_t_file.GetInfo(r_t_find_data);
	// update file info

	/*r_t_file.n_flags = 0;
	if(_access(p_s_full_name, 2) == 0)
		r_t_file.n_flags |= (int)TFileInfo::flag_Write;
	if(_access(p_s_full_name, 4) == 0)
		r_t_file.n_flags |= (int)TFileInfo::flag_Read | (int)TFileInfo::flag_Execute;
	// get access flags

	delete[] p_s_full_name;

	r_t_file.b_directory = (r_t_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
	r_t_file.n_size_lo = r_t_find_data.nFileSizeLow;
	r_t_file.n_size_hi = r_t_find_data.nFileSizeHigh;

	r_t_file.p_time[TFileInfo::time_Creation] = TFileInfo::TTime(r_t_find_data.ftCreationTime);
	r_t_file.p_time[TFileInfo::time_LastAccess] = TFileInfo::TTime(r_t_find_data.ftLastAccessTime);
	r_t_file.p_time[TFileInfo::time_LastWrite] = TFileInfo::TTime(r_t_find_data.ftLastWriteTime);
	if(r_t_file.p_time[TFileInfo::time_Creation].n_year < 0 ||
	   r_t_file.p_time[TFileInfo::time_LastAccess].n_year < 0 ||
	   r_t_file.p_time[TFileInfo::time_LastWrite].n_year < 0) {
		r_t_file.b_valid = false;
		return false;
	}
	// get time

	r_t_file.b_valid = true;
	// file data are valid

	return true;*/
}
#else // _WIN32 || _WIN64
/*
 *	bool Get_FirstFile(TFileInfo &r_t_file)
 *		- gets first file in the directory, copies it's data to r_t_file
 *		- in case the directory doesn't have any files, r_t_file.b_valid is set to false
 *		- returns true on success, false on failure (possibly inaccesible directory)
 */
bool CDirectory::Get_FirstFile(TFileInfo &r_t_file)
{
	if(m_p_dir) {
		closedir(m_p_dir);
		m_p_dir = 0;
	}

	if(!(m_p_dir = opendir(m_p_s_dir_name))) {
		//fprintf(stderr, "error: opendir(%s)\n", m_p_s_dir_name);
		r_t_file.SetInvalid();
		return false;
	}

	return Get_NextFile(r_t_file);
}

/*
 *	bool Get_NextFile(TFileInfo &r_t_file)
 *		- gets next file in the directory, copies it's data to r_t_file
 *		- in case the directory doesn't have any more files, r_t_file.b_valid is set to false
 *		- returns true on success, false on failure (possibly inaccesible directory)
 *		- in case Get_FirstFile was not called prior calling Get_NextFile, it's called
 *		  automatically. in order to get file list for this folder again, Get_FirstFile
 *		  must be called (r_t_file contents are only written to, not read)
 */
bool CDirectory::Get_NextFile(TFileInfo &r_t_file)
{
	if(!m_p_dir)
		return Get_FirstFile(r_t_file);

	for(;;) {
		struct dirent *p_file_record;
		if(!(p_file_record = readdir(m_p_dir))) {
			closedir(m_p_dir);
			m_p_dir = 0;
			r_t_file.SetNoFile();
			return true;
		}
		if(!strcmp(p_file_record->d_name, ".") || !strcmp(p_file_record->d_name, ".."))
			continue;
		// get filename

		char *p_s_full_name;
		if(!(p_s_full_name = new(std::nothrow) char[strlen(p_file_record->d_name) + strlen(m_p_s_dir_name) + 2])) {
			r_t_file.SetInvalid();
			return false;
		}
		strcpy(p_s_full_name, m_p_s_dir_name);
		strcat(p_s_full_name, "/");
		strcat(p_s_full_name, p_file_record->d_name);
		// full filename (overwrite '*' from m_p_s_dir_name -> no extra space for terminating 0)

		if(!r_t_file.SetFilename(p_s_full_name)) {
			delete[] p_s_full_name;
			r_t_file.SetInvalid();
			return false;
		}
		// copy full filename

		delete[] p_s_full_name;
		// don't need this anymore

		return r_t_file.GetInfo();
		// get info about file

		/*if(!r_t_file.SetFilename(p_s_full_name)) {
			delete[] p_s_full_name;
			r_t_file.b_valid = false;
			return false;
		}
		// copy full filename

		delete[] p_s_full_name;

		r_t_file.b_directory = (p_file_record->d_type & DT_DIR) != 0; // todo

		struct stat t_stat;
		if(stat(r_t_file.p_s_Filename(), &t_stat))
			return false;
		// get file stat

		r_t_file.n_flags = 0;
		if(t_stat.st_mode & S_IROTH)
			r_t_file.n_flags |= (int)TFileInfo::flag_other_Read;
		if(t_stat.st_mode & S_IWOTH)
			r_t_file.n_flags |= (int)TFileInfo::flag_other_Write;
		if(t_stat.st_mode & S_IXOTH)
			r_t_file.n_flags |= (int)TFileInfo::flag_other_Execute;
		//
		if(t_stat.st_mode & S_IRGRP)
			r_t_file.n_flags |= (int)TFileInfo::flag_group_Read;
		if(t_stat.st_mode & S_IWGRP)
			r_t_file.n_flags |= (int)TFileInfo::flag_group_Write;
		if(t_stat.st_mode & S_IXGRP)
			r_t_file.n_flags |= (int)TFileInfo::flag_group_Execute;
		//
		if(t_stat.st_mode & S_IRUSR)
			r_t_file.n_flags |= (int)TFileInfo::flag_owner_Read;
		if(t_stat.st_mode & S_IWUSR)
			r_t_file.n_flags |= (int)TFileInfo::flag_owner_Write;
		if(t_stat.st_mode & S_IXUSR)
			r_t_file.n_flags |= (int)TFileInfo::flag_owner_Execute;
		// get flags

#if 1 // (sizeof(t_off)) > 32
		r_t_file.n_size_lo = uint64_t(t_stat.st_size) & 0xffffffff;
		r_t_file.n_size_hi = uint64_t(t_stat.st_size) >> 32;
#else // this might prevent some warnings, if (sizeof(t_off)) <= 32
		r_t_file.n_size_lo = t_stat.st_size;
		r_t_file.n_size_hi = 0;
#endif
		r_t_file.p_time[TFileInfo::time_Creation] = TFileInfo::TTime(localtime(&t_stat.st_ctime));
		r_t_file.p_time[TFileInfo::time_LastAccess] = TFileInfo::TTime(localtime(&t_stat.st_atime));
		r_t_file.p_time[TFileInfo::time_LastWrite] = TFileInfo::TTime(localtime(&t_stat.st_mtime));
		// fill output structure

		return true;*/
	}
}

#endif // _WIN32 || _WIN64

/*
 *								=== ~CDirectory ===
 */
