////////////////////////////////////////////////////////////////////////////////
// This source file is part of the ZipArchive library source distribution and
// is Copyrighted 2000 - 2014 by Artpol Software - Tadeusz Dracz
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// For the licensing details refer to the License.txt file.
//
// Web Site: http://www.artpol-software.com
////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "ZipException.h"
#include "zlib/zlib.h"
#include <errno.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#if defined _MFC_VER && defined _ZIP_IMPL_MFC
	IMPLEMENT_DYNAMIC( CZipException, CException)
#endif

CZipException::CZipException(int iCause, LPCTSTR lpszZipName)
#ifdef _MFC_VER
	:CException(TRUE)
#endif
{
	m_iCause = iCause;

	if (lpszZipName)
		m_szFileName = lpszZipName;

#ifdef _ZIP_SYSTEM_WIN
	m_iSystemError = ::GetLastError();
#else
	m_iSystemError = errno;
#endif
}

CZipException::~CZipException() throw()
{

}

// inline void CZipException::Throw(int iZipError, LPCTSTR lpszZipName)
// {
// #ifdef _MFC_VER
// 	throw new CZipException(iZipError, lpszZipName);
// #else
// 	CZipException e(iZipError, lpszZipName);
// 	throw e;
// #endif
// MSVC++: ignore "Unreachable code" warning here, it's due to 
// optimizations
// }


#ifdef _ZIP_ENABLE_ERROR_DESCRIPTION

ZBOOL CZipException::GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
	UINT* )

{
	if (!lpszError || !nMaxError)
		return FALSE;
	CZipString sz = GetErrorDescription();
	if (sz.IsEmpty())
		return FALSE;
	UINT iLen = sz.GetLength();
	if (nMaxError - 1 < iLen)
		iLen = nMaxError - 1;
	LPTSTR lpsz = sz.GetBuffer(iLen);
#if _MSC_VER >= 1400
	#ifdef _UNICODE	
		wcsncpy_s(lpszError, nMaxError, lpsz, iLen);
	#else
		strncpy_s(lpszError, nMaxError, lpsz, iLen);
	#endif
#else
	#ifdef _UNICODE	
		wcsncpy(lpszError, lpsz, iLen);
	#else
		strncpy(lpszError, lpsz, iLen);
	#endif
#endif

	lpszError[iLen] = _T('\0');
	return TRUE;
}


CZipString CZipException::GetErrorDescription()
{
	return GetInternalErrorDescription(m_iCause);
}


CZipString CZipException::GetSystemErrorDescription()
{
#ifdef WIN32	
	if (m_iSystemError)
	{
		LPVOID lpMsgBuf;
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,    
			          NULL, m_iSystemError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
				      (LPTSTR) &lpMsgBuf, 0, NULL);
		CZipString sz = (LPCTSTR)lpMsgBuf;
		LocalFree(lpMsgBuf);
		return sz;
	}
#endif
	return GetInternalErrorDescription(errno == 0 ? genericError : errno, true);
}

CZipString CZipException::GetInternalErrorDescription(int iCause, bool bNoLoop)
{
	CZipString sz;
	switch (iCause)
	{
		case EROFS:
			sz = _T("Read-only file system.");
			break;
		case ESPIPE:
			sz = _T("Illegal seek.");
			break;
		case ENOSPC:
			sz = _T("No space left on device.");
			break;
		case EFBIG:
			sz = _T("File too large.");
			break;
		case EMFILE:
			sz = _T("Too many open files.");
			break;
		case ENFILE:
			sz = _T("File table overflow.");
			break;
		case EINVAL:
			sz = _T("Invalid argument.");
			break;
		case EISDIR:
			sz = _T("Is a directory.");
			break;
		case ENOTDIR:
			sz = _T("Not a directory.");
			break;
		case ENODEV:
			sz = _T("No such device.");
			break;
		case EXDEV:
			sz = _T("Cross-device link.");
			break;
		case EEXIST:
			sz = _T("File exists.");
			break;
		case EFAULT:
			sz = _T("Bad address.");
			break;
		case EACCES:
			sz = _T("Permission denied.");
			break;
		case ENOMEM:
			sz = _T("Not enough space.");
			break;
		case EBADF:
			sz = _T("Bad file number.");
			break;
		case ENXIO:
			sz = _T("No such device or address.");
			break;
		case EIO:
			sz = _T("I/O error.");
			break;
		case EINTR:
			sz = _T("Interrupted system call.");
			break;
		case ENOENT:
			sz = _T("No such file or directory.");
			break;
		case EPERM:
			sz = _T("Not super-user.");
			break;
		case badZipFile:
			sz = _T("Damaged or not a zip file.");
			break;
		case badCrc:
			sz = _T("Crc is mismatched.");
			break;
		case noCallback:
			sz = _T("There is no spanned archive callback object set.");
			break;
		case noVolumeSize:
			sz = _T("The volume size was not defined for a split archive.");
			break;
		case aborted:
			sz = _T("Volume change aborted in a segmented archive.");
			break;
		case abortedAction:
			sz = _T("Action aborted.");
			break;
		case abortedSafely:
			sz = _T("Action aborted safely.");
			break;
		case nonRemovable:
			sz = _T("The device selected for the spanned archive is not removable.");
			break;
		case tooManyVolumes:
			sz = _T("The limit of the maximum number of volumes has been reached.");
			break;
		case tooManyFiles:
			sz = _T("The limit of the maximum number of files in an archive has been reached.");
			break;
		case tooLongData:
			sz = _T("The filename, the comment or local or central extra field of the file added to the archive is too long.");
			break;
		case tooBigSize:
			sz = _T("The file size is too large to be supported.");
			break;
		case badPassword:
			sz = _T("An incorrect password set for the file being decrypted.");
			break;
		case dirWithSize:
			sz = _T("The directory with a non-zero size found while testing.");
			break;
		case internalError:
			sz = _T("An internal error.");
			break;
		case fileError:
			sz.Format(_T("%s (%s)."), _T("A file error occurred"), (LPCTSTR)GetSystemErrorDescription());
			break;
		case notRemoved:
			sz.Format(_T("%s (%s)."), _T("Error while removing a file"), (LPCTSTR)GetSystemErrorDescription());
			break;
		case notRenamed:
			sz.Format(_T("%s (%s)."), _T("Error while renaming a file"), (LPCTSTR)GetSystemErrorDescription());
			break;
		case platfNotSupp:
			sz = _T("Cannot create a file for the specified platform.");
			break;
		case cdirNotFound:
			sz = _T("The central directory was not found in the archive (or you were trying to open not the last volume of a segmented archive).");
			break;
		case noZip64:
			sz = _T("The Zip64 format has not been enabled for the library, but is required to use the archive.");
			break;
		case noAES:
			sz = _T("WinZip AES encryption has not been enabled for the library, but is required to decompress the archive.");
			break;
#ifdef _ZIP_IMPL_STL
			case outOfBounds:
			sz = _T("The collection is empty and the bounds do not exist.");
			break;
#endif
#ifdef _ZIP_USE_LOCKING
		case mutexError:
			sz = _T("Locking or unlocking resources access was unsuccessful.");
			break;
#endif
		case streamEnd:
			sz = _T("Zlib library error (end of stream).");
			break;
		case errNo:
			sz = GetInternalErrorDescription(errno != errNo ? errno : genericError);
			break;
		case streamError:
			sz = _T("Zlib library error (stream error).");
			break;
		case dataError:
			sz = _T("Zlib library error (data error).");
			break;
		case memError:
			sz = _T("Not enough memory.");
			break;
		case bufError:
			sz = _T("Zlib library error (buffer error).");
			break;
		case versionError:
			sz = _T("Zlib library error (version error).");
			break;
		default:
			sz = bNoLoop ? _T("Unspecified error") :(LPCTSTR) GetSystemErrorDescription();
	}
	return sz;
}

#endif //_ZIP_ENABLE_ERROR_DESCRIPTION
