#include "stdafx.h"

#include "aplAggr.h"

#ifdef _MFC_VER
	#include <share.h>
	#include <io.h>
	#include <Aclapi.h>
	#include <direct.h>

#else
	#include <stdio.h>

    #ifndef __GNUC__
        #include <io.h>
    #else
        #include <unistd.h>
    #endif

    #include <sys/stat.h>

	typedef LONG HRESULT;

	#include "WinError.h"
	
	#ifdef __linux__
		#include <limits.h>
	#endif

	// Чтобы не плодить ifdef-ы в коде (не вырезать работу с неподдерживаемыми флагами)
	#define _SH_DENYRW      0x10    /* deny read/write mode */
	#define _SH_DENYWR      0x20    /* deny write mode */
	#define _SH_DENYRD      0x30    /* deny read mode */
	#define _SH_DENYNO      0x40    /* deny none mode */
	#define _SH_SECURE      0x80    /* secure mode */
#endif


const HANDLE CaplFile::hFileNull=0;


CaplFile::CaplFile() : m_hFile(NULL)
{
}

CaplFile::~CaplFile()
{
	if(m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE )
	{
		fclose(m_hFile);
		m_hFile=0;
	}
}

BOOL CaplFile::Open(LPCTSTR lpszFileName, unsigned int nOpenFlags,
					CFileException* pException)
{
	if (lpszFileName == NULL)
	{
		if(0!=pException) pException->m_cause=CFileException::fileNotFound;
		return FALSE;
	}

	if(0!=pException) pException->m_strFileName=lpszFileName;

	TCHAR szMode[20]; // C-runtime open string
	int nMode = 0;

	int iShare = _SH_DENYNO;

	// determine read/write mode depending on CFile mode
	if (nOpenFlags & modeCreate)
	{
		if (nOpenFlags & modeNoTruncate)
		{
			szMode[nMode++] = _T('a');
		}
		else
		{
			szMode[nMode++] = _T('w');
		}
	}
	else if (nOpenFlags & modeWrite)
	{
		szMode[nMode++] = _T('a');
	}
	else
	{
		szMode[nMode++] = _T('r');
	}

	// add '+' if necessary (when read/write modes mismatched)
	if ((szMode[0] == _T('r') && (nOpenFlags & modeReadWrite)) ||
		(szMode[0] != _T('r') && !(nOpenFlags & modeWrite)))
	{
		// current szMode mismatched, need to add '+' to fix
		szMode[nMode++] = _T('+');
	}

	// will be inverted if not necessary
	if (nOpenFlags & modeNoInherit)
		szMode[nMode++] = _T('N');

	if (nOpenFlags & typeText)
	{
		szMode[nMode++] = _T('t');
	}
	else // Чтобы не было проблем в Linux двоичный по умолчанию //if (nOpenFlags & typeBinary)
	{
		szMode[nMode++] = _T('b');
	}


	// проверка shareDenyRead должна идти первой и сделана так потому что shareDenyRead(0x30) = shareExclusive(0x10) | shareDenyWrite(0x20)
	// и если придет shareDenyRead то shareExclusive и shareDenyWrite тоже как бы будут установлены, но
	// _fsopen ожидает только один флаг из четырех - в этой CRT-функции это не флаг а перечисление, а
	// дебилы из Microsoft перенесли эти значения в бинарный флаг CFile хреновы черти
	if ((nOpenFlags & shareExclusive) && (nOpenFlags & shareDenyWrite))	// это проверка shareDenyRead )))
		iShare = _SH_DENYRD;
	else if (nOpenFlags & shareExclusive)
		iShare = _SH_DENYRW;
	else if (nOpenFlags & shareDenyWrite)
		iShare = _SH_DENYWR;
	else
		iShare = _SH_DENYNO; // нет блокировки

#ifdef UNICODE
	if(nOpenFlags & typeUnicode)
	{
		szMode[nMode++] = _T(',');
		szMode[nMode++] = _T(' ');
		szMode[nMode++] = _T('c');
		szMode[nMode++] = _T('c');
		szMode[nMode++] = _T('s');
		szMode[nMode++] = _T('=');
		szMode[nMode++] = _T('U');
		szMode[nMode++] = _T('T');
		szMode[nMode++] = _T('F');
		szMode[nMode++] = _T('-');
		szMode[nMode++] = _T('8');
	}
#endif
	szMode[nMode++] = _T('\0');


	#ifdef  __linux__
		m_hFile = fopen(CaplStringAdapter(lpszFileName), CaplStringAdapter(szMode));
		if(NULL != m_hFile && INVALID_HANDLE_VALUE != m_hFile)
		{
			// ЯАИ: flockfile / ftrylockfile ничего не блокируют ни внутри процесса ни между процессами

			if(0!=lockf(fileno(m_hFile),F_TEST,0)) // Файл заблокирован в другом процессе
			{
				fclose(m_hFile);
				m_hFile=0;
			}
			else if ((iShare&_SH_DENYRW) | (iShare&_SH_DENYWR) | (iShare&_SH_DENYRD))
			{
				lockf(fileno(m_hFile),F_LOCK,0); // блокируем файл (если не удастся,то вернет -1)
			}
		}
	#else
		m_hFile = __fsopen(lpszFileName, szMode,iShare);
	#endif


	if(NULL == m_hFile || INVALID_HANDLE_VALUE == m_hFile)
	{
		m_hFile=0;

		if (pException != NULL)
		{
			#ifdef __linux__
				pException->m_lOsError = errno;
				pException->m_cause = CFileException::ErrnoToException(errno);
			#else
				pException->m_lOsError = _doserrno;
				pException->m_cause = CFileException::OsErrorToException(_doserrno);
			#endif
			
		}

		return FALSE;
	}

	return TRUE;
}

LPTSTR CaplFile::ReadString(LPTSTR lpsz, unsigned int nMax)
{
	if (lpsz == NULL)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::ReadString -> Пустой буфер"));
		return 0; // На всякий случай
	}

	if(NULL == m_hFile )
	{
		APL_STOP_IF_DEBUG  
		APL_WRITE_TO_LOG(_T("CaplFile::ReadString -> Некорректный файл"));
		return 0; 
	}

	LPTSTR lpszResult = _fgets(lpsz, nMax, m_hFile);
	if (lpszResult == NULL && !feof(m_hFile))
	{
		//APL_STOP_IF_DEBUG
	}
	return lpszResult;
}


#ifdef USE_APL_STRING_AS_CSTRING

bool CaplFile::ReadString(CaplStringT& sStr)
{
	sStr = _T("");    // empty string without deallocating
	if(m_hFile == NULL ) { ASSERT(FALSE); return false;}

	const int nMaxSize = 128;
	LPTSTR lpsz = sStr.GetBuffer(nMaxSize);
	LPTSTR lpszResult;
	int nLen = 0;

	for (;;)
	{
        lpszResult = _fgets(lpsz, nMaxSize+1, m_hFile);
		sStr.ReleaseBuffer();

		// handle error/eof case
		if (lpszResult == NULL && !feof(m_hFile))
		{
			break;
		}

		// if string is read completely or EOF
		if (lpszResult == NULL ||
            (nLen = (int)_strlen(lpsz)) < nMaxSize ||
			lpsz[nLen-1] == '\n')
			break;

		nLen = sStr.GetLength();
		lpsz = sStr.GetBuffer(nMaxSize + nLen) + nLen;
	}

	// remove '\n' from end of string if present
	lpsz = sStr.GetBuffer(0);
	nLen = sStr.GetLength();
	if (nLen != 0 && lpsz[nLen-1] == '\n')
		sStr.GetBufferSetLength(nLen-1);

	return nLen != 0;
}
#else

bool CaplFile::ReadString(CString& sStr)
{
	sStr = _T("");    // empty string without deallocating

	if(m_hFile == NULL ) { ASSERT(FALSE); return false;}

	const int nMaxSize = 128;
	LPTSTR lpsz = sStr.GetBuffer(nMaxSize);
	LPTSTR lpszResult;
	int nLen = 0;

	for (;;)
	{
		lpszResult = _fgetts(lpsz, nMaxSize+1, m_hFile);
		sStr.ReleaseBuffer();

		// handle error/eof case
		if (lpszResult == NULL && !feof(m_hFile))
		{
			break;
		}

		// if string is read completely or EOF
		if (lpszResult == NULL ||
			(nLen = (int)lstrlen(lpsz)) < nMaxSize ||
			lpsz[nLen-1] == '\n')
			break;

		nLen = sStr.GetLength();
		lpsz = sStr.GetBuffer(nMaxSize + nLen) + nLen;
	}

	// remove '\n' from end of string if present
	lpsz = sStr.GetBuffer(0);
	nLen = sStr.GetLength();
	if (nLen != 0 && lpsz[nLen-1] == '\n')
		sStr.GetBufferSetLength(nLen-1);

	return nLen != 0;
}
#endif

bool CaplFile::Close()
{
	if(m_hFile == NULL )
	{
		ASSERT(FALSE);
		m_hFile=0;
		return false;
	}

	int nErr = fclose(m_hFile);

	m_hFile = NULL;

	if (0==nErr) return true;

	APL_STOP_IF_DEBUG
	return false;
}

bool CaplFile::Flush()
{
	if(m_hFile == NULL ) { ASSERT(FALSE); return false;}

	fflush(m_hFile);
	return true;
}

// получить текущую позицию
long long CaplFile::GetPosition( ) const
{
	if(m_hFile == NULL ) { ASSERT(FALSE); return -1;}

	long long pos=__ftell64(m_hFile);
	if(-1L==pos) 
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::GetPosition() -> Ошибка определения позиции в файле"));
	}
	return pos;
}


unsigned long long CaplFile::Seek(long long lOff, unsigned int nFrom)
{
	if(!(nFrom == begin || nFrom == end || nFrom == current)) {	ASSERT(FALSE); return 0;}
	
	if(m_hFile == NULL ) { ASSERT(FALSE); return 0;}

//	переделал на работу с int64. Извращение с int32 пока закомментарил
// 	LONG lOff32;
// 
// 	if ((lOff < LONG_MIN) || (lOff > LONG_MAX))
// 	{
// 		APL_STOP_IF_DEBUG
// 		APL_WRITE_TO_LOG(_T("CaplFile::Seek() ->  Некорректная позиция в файле"));
// 		return -1L;
// 	}
//	lOff32 = (LONG)lOff;

	if (__fseek64(m_hFile, lOff, nFrom) != 0)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::Seek() ->  Ошибка позиционирования файла"));
		return -1L;
	}

	return __ftell64(m_hFile);
}

unsigned long long CaplFile::SeekToEnd()
{
	return Seek(0, end);
}

void CaplFile::SeekToBegin()
{
	Seek(0, begin);
}

bool CaplFile::WriteString(LPCTSTR lpsz)
{
	int lpszResult;

#ifdef _UNICODE
	CString str_tmp;
	str_tmp = lpsz;
	// юникодный вариант при выводе сам добавляет \r ко всем \n
	str_tmp.Remove(_T('\r'));
	lpsz = (LPTSTR)(LPCTSTR) str_tmp;
#endif

	lpszResult = _fputs(lpsz,  m_hFile);

	// handle error/eof case
	if (lpszResult == (int)_EOF)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::WriteString(LPCTSTR lpsz) ->  Ошибка записи строки в файл"));
		return false;  // На всякий случай
	}

	//	lpsz = (LPCTSTR)
	return true;
}

__int64 CaplFile::GetLength() const
{
	if(NULL == m_hFile )
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::GetLength() ->  Некорректный файл"));
		return -1L;
	}

	__int64 nCurrent = __ftell64(m_hFile); // Запоминаем позицию
	if (nCurrent == -1)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::GetLength() ->  Некорректная исходная позиция в файле"));
		return -1L;
	}

	if (__fseek64(m_hFile, 0, SEEK_END) != 0)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::GetLength() ->  Ошибка установки в конец файла"));
		return -1L;
	}

	__int64 nLength = __ftell64(m_hFile);
	if (nLength == -1)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::GetLength() ->  Ошибка получения позиции конца файла"));
		return -1L;
	}

	// Восстанавливаем позицию
	if (__fseek64(m_hFile, nCurrent, SEEK_SET) != 0)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::GetLength() ->  Ошибка восстановления позиции в файле"));
		return -1L;
	}
	return nLength;
}

bool CaplFile::Write(const void* lpBuf, unsigned int nCount)
{
	if(NULL == m_hFile )
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::Write() ->  Некорректный файл"));
		return false;
	}

	if (lpBuf == NULL)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::Write() ->  Отсутствует буффер"));
		return false;
	}

	if (fwrite(lpBuf, sizeof(BYTE), nCount, m_hFile) != nCount)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::Write() ->  Ошибка записи в файл"));
		return false;
	}
	return true;
}

UINT CaplFile::Read(void* lpBuf, UINT nCount)
{
	if(NULL == m_hFile )
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::Read() ->  Некорректный файл"));
		return -1;
	}

	if (lpBuf == NULL)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::Read() ->  Отсутствует буффер"));
		return -1;
	}

	UINT count=fread(lpBuf, sizeof(BYTE), nCount, m_hFile);
	return count;
}


bool CaplFile::SetLength(unsigned long long dwNewLen)
{
	if(NULL == m_hFile )
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::SetLength() ->  Некорректный файл"));
		return false;
	}

	if (( (long long)dwNewLen < 0) /*|| (dwNewLen > LONG_MAX) остатки 32-битной обработки */)
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::SetLength() ->  Некорректный размер файла"));
		return false;
	}

	Seek(dwNewLen, (UINT)begin); // Для идентичности с реализацией в MFC

#ifndef __GNUC__
    //if (!::SetEndOfFile((HANDLE)_get_osfhandle(_fileno(m_hFile)))) // оригинальный вариант
    if(0!=_chsize_s(_fileno(m_hFile),dwNewLen))
#else
    if (0!=ftruncate(_fileno(m_hFile), dwNewLen))
#endif
	{
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("CaplFile::SetLength() ->  Ошибка установки размера файла"));
		return false;
	}
	return true;
}


bool CaplFile::Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName)
{
	if(0==lpszOldName || 0==lpszNewName) return false;
	if(_T('\0')==lpszOldName[0] || _T('\0')==lpszNewName[0]) return false;

#ifdef __linux__
	if (0== rename(CaplStringAdapter(lpszOldName), CaplStringAdapter(lpszNewName) ) )   return true;
#else
	if (0== _rename( lpszOldName, lpszNewName ) )   return true;
#endif

	return false;
}


bool CaplFile::Remove(LPCTSTR lpszFileName)
{
	if(0==lpszFileName) return false;
	if(_T('\0')==lpszFileName[0]) return false;

#ifdef __linux__
	if(0==remove(CaplStringAdapter(lpszFileName))) return true;
#else
	if(0==_remove(lpszFileName)) return true;
#endif

	return false;
}

//************************************************************************************************************
//************************************************************************************************************
//************************************************************************************************************

bool CaplFile::CreateFolder(LPCTSTR path)
{
	if(0==path) return false;
	if(_T('\0')==path[0]) return false;

#ifdef __linux__
	int res = mkdir((const char*)CaplStringAdapter(path), 0755);
	if(res == -1 && errno != EEXIST) return false;
	return true;
#else

	#ifndef _MFC_VER

		#ifdef _UNICODE
			int res = _wmkdir(path);
		#else
			int res = _mkdir(path);
		#endif

		if(res == -1 && errno != EEXIST) return false;
		return true;

	#else

		BOOL res=CreateDirectory(path, 0);
		if(0!=res) return true;

		DWORD err=GetLastError();
		if(ERROR_ALREADY_EXISTS==err) return true;

		if(ERROR_PATH_NOT_FOUND!=err) return false;

		// Возможно, это длинный путь и попробуем помудрить

		CString sPath(path);
		if(sPath.GetLength()<MAX_PATH) return false; // Путь кортокий, дело в чет-то другом.

		CString sTmp=sPath.Left(4);
		if(sTmp==_T("\\\\?\\")) return false; // Уже такое есть

		sPath.Insert(0,_T("\\\\?\\"));

		res=CreateDirectory(sPath, 0);
		if(0!=res) return true;

		err=GetLastError();
		if(ERROR_ALREADY_EXISTS==err) return true;

		return false;
	#endif

#endif
}

//************************************************************************************************************

bool CaplFile::IsFileExist(LPCTSTR lpszFileName) // проверяет существование файла
{
	if(0==lpszFileName) return false;
	if(_T('\0')==lpszFileName[0]) return false;

/*
#ifdef _MFC_VER
	_finddata_t data;
	long nFind = _findfirst(lpszFileName,&data);
	if (-1==nFind) return false;
	_findclose(nFind);
	return true;
#else*/
	
#ifndef __GNUC__
	//int k=__access(lpszFileName, 0);
	//return (0==k);

	DWORD dwFileAttributes=GetFileAttributes(lpszFileName);
	return (INVALID_FILE_ATTRIBUTES!=dwFileAttributes);

#else
	struct stat st;
	#ifdef __linux__
		int k=stat(LPCSTR(CaplStringAdapter(lpszFileName)), &st);
	#else
		int k=wstat(lpszFileName, &st);  // Под Win stat не работет почему-то
	#endif
	return (k != -1);

#endif
}

//************************************************************************************************************
bool CaplFile::IsFileAttrAllowWrite(LPCTSTR lpszFileName) // Проверяет атрибуты файла на доступность для записи
{
	if(0==lpszFileName) return false;
	if(_T('\0')==lpszFileName[0]) return false;

#ifndef linux

	int k=__access(lpszFileName, 02); // 02 - Write-only
	return (0==k);

	// 	DWORD dwFileAttributes=GetFileAttributes(lpszFileName);
	// 	if(INVALID_FILE_ATTRIBUTES==dwFileAttributes) return false;
	// 	static const dwROAttributes=FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM;
	// 	if(dwFileAttributes & dwROAttributes) return false;
	// 	return true;

#else

	struct stat st;
	int k=stat(LPCSTR(CaplStringAdapter(lpszFileName)), &st);
	if (k == -1) return false;

	if(st.st_mode& (S_IWUSR | S_IWGRP | S_IWOTH))
		return true;
	return false;

#endif

}
//************************************************************************************************************
//************************************************************************************************************
//************************************************************************************************************
bool CaplFile::CopyFile(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName, bool bFailIfExists, bool bCopyAccessInfo)
{
	if(0==lpExistingFileName || 0== lpNewFileName) return false;
	if(_T('\0')==lpExistingFileName[0] || _T('\0')==lpNewFileName[0] ) return false;

	#ifdef _MFC_VER

		if (TRUE!=::CopyFile(lpExistingFileName, lpNewFileName, bFailIfExists ? 1:0)) return false;
		if(bCopyAccessInfo)
		{
			bool ret_val = true;
			PACL pOldDACL = NULL;
			PSECURITY_DESCRIPTOR pSD = NULL;

			// Get a pointer to the existing DACL.
			if (ERROR_SUCCESS != GetNamedSecurityInfo((LPTSTR)lpExistingFileName, SE_FILE_OBJECT,
				DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD))
			{
				aplMessageBox(_T("GetNamedSecurityInfo Error {CaplFile::CopyFile}"),MB_OK|MB_ICONWARNING);
				ret_val = false;
			}
			else
			{
				// Attach the new ACL as the object's DACL.
				if (ERROR_SUCCESS != SetNamedSecurityInfo((LPTSTR)lpNewFileName, SE_FILE_OBJECT, 
					DACL_SECURITY_INFORMATION,
					NULL, NULL, pOldDACL, NULL))
				{
					aplMessageBox(_T("SetNamedSecurityInfo Error {CaplFile::CopyFile}"),MB_OK|MB_ICONWARNING);
					ret_val = false;
				}  
			}
			if(pSD != NULL) 	LocalFree((HLOCAL) pSD); 
			if(pOldDACL != NULL) LocalFree((HLOCAL) pOldDACL); 

			return ret_val;
		}
		return true;
	#else

#ifndef __linux__
	const int iShare=_SH_DENYRW;
#endif

	if(bFailIfExists)
	{
		#ifdef  __linux__
			FILE* dest = fopen(CaplStringAdapter(lpNewFileName), "r");
		#else
			// ЯАИ: Это не будет выполняться, на всякий случай пусть останется
			FILE* dest = __fsopen(lpNewFileName, _T("r"),iShare);
		#endif

		if(0!=dest)
		{
			fclose(dest);
			return false;
		}
	}

	#ifdef  __linux__
		FILE* dest = fopen(CaplStringAdapter(lpNewFileName), "wb");
	#else
		FILE* dest = __fsopen(lpNewFileName, _T("wb"),iShare);
	#endif

	if(0==dest) return false;


	#ifdef  __linux__
		FILE* source = fopen(CaplStringAdapter(lpExistingFileName), "rb");
	#else
		FILE* source = __fsopen(lpExistingFileName, _T("rb"),iShare);
	#endif
	if(0==source){ fclose(dest); return false;}


	LONG flen = 0;
	if(0==fseek(source, 0, SEEK_END))flen = ftell(source);

	bool bRezult=true;

	if(flen>0)
	{
		 if(0==fseek(source, 0, SEEK_SET))
		 {

			 int bufsize=1024*1024;
			 if(flen<bufsize) bufsize=flen;

			 char *buf=new char[bufsize];
			 if(0==buf) bRezult=false;
			 else
			 {
				while (!feof( source ) )
				{
					size_t size = fread(buf, 1, bufsize, source);
					if(0==size) break;
					fwrite(buf, 1, size, dest);
				}
				delete[] buf;
			}
		 }
	}
	fclose(source);
	fclose(dest);
	
	if(bCopyAccessInfo)
	{
		//APL_STOP_IF_DEBUG; //Непонятно, как копировать доступ под линуксом
	}

	return bRezult;

	#endif
}


bool CaplFile::CreateBackupFile(LPCTSTR lpExistingFileName, int iNumBackupCopies)
{
	if(!CaplFile::IsFileExist(lpExistingFileName)) return false;

	if(iNumBackupCopies<1) iNumBackupCopies=1;

	int i;
	CString buf, sTmpCopyName, sFname;

	// Создаем временнкю копию
	sTmpCopyName=lpExistingFileName;
	sTmpCopyName+=_T(".bak.tmp");
	if(!CaplFile::CopyFile(lpExistingFileName,sTmpCopyName,false,true))
	{
		//В ту  же папку не получилось, пишем во временную
		buf=lpExistingFileName;
		i=buf.ReverseFind(aplDirRazd);
		if(i>=0) sFname=buf.Right(buf.GetLength()-(i+1));
		else sFname=lpExistingFileName;

		aplGetTempPath(sTmpCopyName);
		sTmpCopyName+=sFname;
		sTmpCopyName+=_T(".bak.tmp");
		if(!CaplFile::CopyFile(lpExistingFileName,sTmpCopyName,false,true))
		{
			return false;
		}
	}

	sFname=sTmpCopyName.Left(sTmpCopyName.GetLength()-8); // без .tmp

	CString sOldCopy,sPrevCopy;

	//Удаляем самую старую копию
	if(1==iNumBackupCopies) buf=_T(".bak");
	else buf.Format(_T(".%02i.bak"),iNumBackupCopies-1);
		
	sOldCopy=sFname+buf;
	if(CaplFile::IsFileExist(sOldCopy))	CaplFile::Remove(sOldCopy);

	// Преименовываем старые копии
	for(i=iNumBackupCopies-2;  i>=0; i--)
	{
		if(0==i) sPrevCopy=sFname+_T(".bak");
		else
		{
			buf.Format(_T(".%02i.bak"),i);
			sPrevCopy=sFname+buf;
		}
		if(CaplFile::IsFileExist(sPrevCopy)) CaplFile::Rename(sPrevCopy,sOldCopy);	
		sOldCopy=sPrevCopy;
	}

	CaplFile::Rename(sTmpCopyName,sOldCopy);

	return true;
}
//**************************************************

void CaplFile::NormalizeFileName(CString &fname)
{
	if(fname==_T("")) return;

	fname.TrimRight();

	int i=-1;
	while (true)
	{
		i++;
		TCHAR t=fname[i];
		if(t==_T('\0')) return;

		if(t>=_T('0') && t<=_T('9')) continue;
		if(t>=_T('a') && t<=_T('z')) continue;
		if(t>=_T('A') && t<=_T('Z')) continue;
		if( _T(' ')==t || _T('-')==t || _T('+')==t || _T('_')==t || _T('(')==t || _T(')')==t)continue;
		if( _T('.')==t || _T(',')==t || _T('^')==t || _T('~')==t || _T('#')==t || _T('!')==t) continue;
		if( _T('`')==t || _T('\'')==t ) continue;
		if(t>=_T('а') && t<=_T('я')) continue;
		if(t>=_T('А') && t<=_T('Я')) continue;
		if( _T('№')==t || _T('ё')==t || _T('Ё')==t) continue;

		if( _T('"')==t)   {fname.SetAt(i,_T('\'')); continue;}
		if( _T('*')==t)   {fname.SetAt(i,_T('x')); continue;}
		if( _T('?')==t)   {fname.SetAt(i,_T('_')); continue;}
		if( _T('\\')==t)   {fname.SetAt(i,_T('_')); continue;}
		if( _T('/')==t)   {fname.SetAt(i,_T('_')); continue;}

		fname.SetAt(i,_T('~'));
	}
}
//***********************************************************************************
//***********************************************************************************
//***********************************************************************************

CaplFileException::CaplFileException(int cause, LONG lOsError, LPCTSTR lpszArchiveName)
{
	m_cause=cause;
	m_lOsError=lOsError;
	m_strFileName=lpszArchiveName;
}
//***********************************************************************************
int CaplFileException::ErrnoToException(int nErrno)
{
	switch(nErrno)
	{
	case EPERM:
	case EACCES:
		return CFileException::accessDenied;
	case EBADF:
		return CFileException::invalidFile;
	case EDEADLOCK:
		return CFileException::sharingViolation;
	case EMFILE:
		return CFileException::tooManyOpenFiles;
	case ENOENT:
	case ENFILE:
		return CFileException::fileNotFound;
	case ENOSPC:
		return CFileException::diskFull;
	case EINVAL:
	case EIO:
		return CFileException::hardIO;
	default:
		return CFileException::genericException;
	}
}
//***********************************************************************************
int CaplFileException::OsErrorToException(LONG lOsErr)
{
	// NT Error codes
	switch ((UINT)lOsErr)
	{
	case NO_ERROR:
		return CFileException::none;
	case ERROR_FILE_NOT_FOUND:
		return CFileException::fileNotFound;
	case ERROR_PATH_NOT_FOUND:
		return CFileException::badPath;
	case ERROR_TOO_MANY_OPEN_FILES:
		return CFileException::tooManyOpenFiles;
	case ERROR_ACCESS_DENIED:
		return CFileException::accessDenied;
	case ERROR_INVALID_HANDLE:
		return CFileException::fileNotFound;
	case ERROR_BAD_FORMAT:
		return CFileException::invalidFile;
	case ERROR_INVALID_ACCESS:
		return CFileException::accessDenied;
	case ERROR_INVALID_DRIVE:
		return CFileException::badPath;
	case ERROR_CURRENT_DIRECTORY:
		return CFileException::removeCurrentDir;
	case ERROR_NOT_SAME_DEVICE:
		return CFileException::badPath;
	case ERROR_NO_MORE_FILES:
		return CFileException::fileNotFound;
	case ERROR_WRITE_PROTECT:
		return CFileException::accessDenied;
	case ERROR_BAD_UNIT:
		return CFileException::hardIO;
	case ERROR_NOT_READY:
		return CFileException::hardIO;
	case ERROR_BAD_COMMAND:
		return CFileException::hardIO;
	case ERROR_CRC:
		return CFileException::hardIO;
	case ERROR_BAD_LENGTH:
		return CFileException::badSeek;
	case ERROR_SEEK:
		return CFileException::badSeek;
	case ERROR_NOT_DOS_DISK:
		return CFileException::invalidFile;
	case ERROR_SECTOR_NOT_FOUND:
		return CFileException::badSeek;
	case ERROR_WRITE_FAULT:
		return CFileException::accessDenied;
	case ERROR_READ_FAULT:
		return CFileException::badSeek;
	case ERROR_SHARING_VIOLATION:
		return CFileException::sharingViolation;
	case ERROR_LOCK_VIOLATION:
		return CFileException::lockViolation;
	case ERROR_WRONG_DISK:
		return CFileException::badPath;
	case ERROR_SHARING_BUFFER_EXCEEDED:
		return CFileException::tooManyOpenFiles;
	case ERROR_HANDLE_EOF:
		return CFileException::endOfFile;
	case ERROR_HANDLE_DISK_FULL:
		return CFileException::diskFull;
	case ERROR_DUP_NAME:
		return CFileException::badPath;
	case ERROR_BAD_NETPATH:
		return CFileException::badPath;
	case ERROR_NETWORK_BUSY:
		return CFileException::accessDenied;
	case ERROR_DEV_NOT_EXIST:
		return CFileException::badPath;
	case ERROR_ADAP_HDW_ERR:
		return CFileException::hardIO;
	case ERROR_BAD_NET_RESP:
		return CFileException::accessDenied;
	case ERROR_UNEXP_NET_ERR:
		return CFileException::hardIO;
	case ERROR_BAD_REM_ADAP:
		return CFileException::invalidFile;
	case ERROR_NO_SPOOL_SPACE:
		return CFileException::directoryFull;
	case ERROR_NETNAME_DELETED:
		return CFileException::accessDenied;
	case ERROR_NETWORK_ACCESS_DENIED:
		return CFileException::accessDenied;
	case ERROR_BAD_DEV_TYPE:
		return CFileException::invalidFile;
	case ERROR_BAD_NET_NAME:
		return CFileException::badPath;
	case ERROR_TOO_MANY_NAMES:
		return CFileException::tooManyOpenFiles;
	case ERROR_SHARING_PAUSED:
		return CFileException::badPath;
	case ERROR_REQ_NOT_ACCEP:
		return CFileException::accessDenied;
	case ERROR_FILE_EXISTS:
		return CFileException::accessDenied;
	case ERROR_CANNOT_MAKE:
		return CFileException::accessDenied;
	case ERROR_ALREADY_ASSIGNED:
		return CFileException::badPath;
	case ERROR_INVALID_PASSWORD:
		return CFileException::accessDenied;
	case ERROR_NET_WRITE_FAULT:
		return CFileException::hardIO;
	case ERROR_DISK_CHANGE:
		return CFileException::fileNotFound;
	case ERROR_DRIVE_LOCKED:
		return CFileException::lockViolation;
	case ERROR_BUFFER_OVERFLOW:
		return CFileException::badPath;
	case ERROR_DISK_FULL:
		return CFileException::diskFull;
	case ERROR_NO_MORE_SEARCH_HANDLES:
		return CFileException::tooManyOpenFiles;
	case ERROR_INVALID_TARGET_HANDLE:
		return CFileException::invalidFile;
	case ERROR_INVALID_CATEGORY:
		return CFileException::hardIO;
	case ERROR_INVALID_NAME:
		return CFileException::badPath;
	case ERROR_INVALID_LEVEL:
		return CFileException::badPath;
	case ERROR_NO_VOLUME_LABEL:
		return CFileException::badPath;
	case ERROR_NEGATIVE_SEEK:
		return CFileException::badSeek;
	case ERROR_SEEK_ON_DEVICE:
		return CFileException::badSeek;
	case ERROR_DIR_NOT_ROOT:
		return CFileException::badPath;
	case ERROR_DIR_NOT_EMPTY:
		return CFileException::removeCurrentDir;
	case ERROR_LABEL_TOO_LONG:
		return CFileException::badPath;
	case ERROR_BAD_PATHNAME:
		return CFileException::badPath;
	case ERROR_LOCK_FAILED:
		return CFileException::lockViolation;
	case ERROR_BUSY:
		return CFileException::accessDenied;
	case ERROR_INVALID_ORDINAL:
		return CFileException::invalidFile;
	case ERROR_ALREADY_EXISTS:
		return CFileException::accessDenied;
	case ERROR_INVALID_EXE_SIGNATURE:
		return CFileException::invalidFile;
	case ERROR_BAD_EXE_FORMAT:
		return CFileException::invalidFile;
	case ERROR_FILENAME_EXCED_RANGE:
		return CFileException::badPath;
	case ERROR_META_EXPANSION_TOO_LONG:
		return CFileException::badPath;
	case ERROR_DIRECTORY:
		return CFileException::badPath;
	case ERROR_OPERATION_ABORTED:
		return CFileException::hardIO;
	case ERROR_IO_INCOMPLETE:
		return CFileException::hardIO;
	case ERROR_IO_PENDING:
		return CFileException::hardIO;
	case ERROR_SWAPERROR:
		return CFileException::accessDenied;
	default:
		return CFileException::genericException;
	}
}

LPCTSTR CaplFileException::Error2String(int iErr)
{
	LPCTSTR sErr=_T("?");

	switch (iErr)
	{
	case none: sErr=_T("none"); break;
	case genericException: sErr=_T("genericException"); break;
	case fileNotFound: sErr=_T("fileNotFound"); break;
	case badPath: sErr=_T("badPath"); break;
	case tooManyOpenFiles: sErr=_T("tooManyOpenFiles"); break;
	case accessDenied: sErr=_T("accessDenied"); break;
	case invalidFile: sErr=_T("invalidFile"); break;
	case removeCurrentDir: sErr=_T("removeCurrentDir"); break;
	case directoryFull: sErr=_T("directoryFull"); break;
	case badSeek: sErr=_T("badSeek"); break;
	case hardIO: sErr=_T("hardIO"); break;
	case sharingViolation: sErr=_T("sharingViolation"); break;
	case lockViolation: sErr=_T("lockViolation"); break;
	case diskFull: sErr=_T("diskFull"); break;
	case endOfFile: sErr=_T("endOfFile"); break;
	default: break;
	}
	return sErr;
}

//***********************************************************************************
//***********************************************************************************
//***********************************************************************************

#ifdef _MFC_VER
CaplEnterCriticalSection::CaplEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) :
	m_iCountEnter(0), m_lpCriticalSection(lpCriticalSection), m_lpAplCriticalSection(0)
{
	::EnterCriticalSection(m_lpCriticalSection);

	m_iCountEnter=1;
}

void CaplEnterCriticalSection::Set(LPCRITICAL_SECTION lpCriticalSection)
{
	ASSERT(m_lpAplCriticalSection==0);
	if(m_iCountEnter>0) 
		Leave();
	m_lpCriticalSection = lpCriticalSection;

	if(m_lpCriticalSection!=0)
	{
		::EnterCriticalSection(m_lpCriticalSection);
		m_iCountEnter=1;
	}
}
#endif

CaplEnterCriticalSection::CaplEnterCriticalSection(CAplCriticalSection* lpAplCriticalSection) :
	m_iCountEnter(0), m_lpAplCriticalSection(0)
{
	if(lpAplCriticalSection != 0)
		Set(lpAplCriticalSection);
}

void CaplEnterCriticalSection::Set(CAplCriticalSection* lpAplCriticalSection)
{
#ifdef _MFC_VER
	m_lpCriticalSection =0;
#else
	ASSERT(m_lpAplCriticalSection==0);
#endif
	if(m_iCountEnter>0) 
		Leave();
	
	m_lpAplCriticalSection = lpAplCriticalSection;

	if(m_lpAplCriticalSection!=0)
	{
		m_lpAplCriticalSection->EnterAplCriticalSection();
		m_iCountEnter=1;
	}
}

CaplEnterCriticalSection::~CaplEnterCriticalSection()
{
	if(m_iCountEnter>0)
	{
#ifdef _MFC_VER
		if(m_lpCriticalSection!=0)
		{
			::LeaveCriticalSection(m_lpCriticalSection);
		}
		else
#endif
		if(m_lpAplCriticalSection!=0)
		{
			m_lpAplCriticalSection->LeaveAplCriticalSection();
		}
	}
}

void CaplEnterCriticalSection::Leave()
{
	if(m_iCountEnter<=0) return;
#ifdef _MFC_VER
	if(m_lpCriticalSection!=0)
	{
		::LeaveCriticalSection(m_lpCriticalSection);
	}
	else
#endif
	if(m_lpAplCriticalSection!=0)
	{
		m_lpAplCriticalSection->LeaveAplCriticalSection();
	}

	m_iCountEnter--;
	if(m_iCountEnter<=0)
		m_lpAplCriticalSection = 0;
}


