#include "rar.hpp" ErrorHandler::ErrorHandler() { Clean(); } void ErrorHandler::Clean() { ExitCode=RARX_SUCCESS; ErrCount=0; EnableBreak=true; Silent=false; UserBreak=false; MainExit=false; DisableShutdown=false; } void ErrorHandler::MemoryError() { MemoryErrorMsg(); Exit(RARX_MEMORY); } void ErrorHandler::OpenError(const wchar *FileName) { #ifndef SILENT OpenErrorMsg(FileName); Exit(RARX_OPEN); #endif } void ErrorHandler::CloseError(const wchar *FileName) { if (!UserBreak) { uiMsg(UIERROR_FILECLOSE,FileName); SysErrMsg(); } // We must not call Exit and throw an exception here, because this function // is called from File object destructor and can be invoked when stack // unwinding while handling another exception. Throwing a new exception // when stack unwinding is prohibited and terminates a program. // If necessary, we can check std::uncaught_exception() before throw. SetErrorCode(RARX_FATAL); } void ErrorHandler::ReadError(const wchar *FileName) { #ifndef SILENT ReadErrorMsg(FileName); #endif #if !defined(SILENT) || defined(RARDLL) Exit(RARX_FATAL); #endif } bool ErrorHandler::AskRepeatRead(const wchar *FileName) { #if !defined(SILENT) && !defined(SFX_MODULE) if (!Silent) { SysErrMsg(); bool Repeat=uiAskRepeatRead(FileName); if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog. DisableShutdown=true; return Repeat; } #endif return false; } void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName) { #ifndef SILENT WriteErrorMsg(ArcName,FileName); #endif #if !defined(SILENT) || defined(RARDLL) Exit(RARX_WRITE); #endif } #ifdef _WIN_ALL void ErrorHandler::WriteErrorFAT(const wchar *FileName) { SysErrMsg(); uiMsg(UIERROR_NTFSREQUIRED,FileName); #if !defined(SILENT) && !defined(SFX_MODULE) || defined(RARDLL) Exit(RARX_WRITE); #endif } #endif bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull) { #ifndef SILENT if (!Silent) { // We do not display "repeat write" prompt in Android, so we do not // need the matching system error message. SysErrMsg(); bool Repeat=uiAskRepeatWrite(FileName,DiskFull); if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog. DisableShutdown=true; return Repeat; } #endif return false; } void ErrorHandler::SeekError(const wchar *FileName) { if (!UserBreak) { uiMsg(UIERROR_FILESEEK,FileName); SysErrMsg(); } #if !defined(SILENT) || defined(RARDLL) Exit(RARX_FATAL); #endif } void ErrorHandler::GeneralErrMsg(const wchar *fmt,...) { va_list arglist; va_start(arglist,fmt); wchar Msg[1024]; vswprintf(Msg,ASIZE(Msg),fmt,arglist); uiMsg(UIERROR_GENERALERRMSG,Msg); SysErrMsg(); va_end(arglist); } void ErrorHandler::MemoryErrorMsg() { uiMsg(UIERROR_MEMORY); SetErrorCode(RARX_MEMORY); } void ErrorHandler::OpenErrorMsg(const wchar *FileName) { OpenErrorMsg(NULL,FileName); } void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName) { uiMsg(UIERROR_FILEOPEN,ArcName,FileName); SysErrMsg(); SetErrorCode(RARX_OPEN); } void ErrorHandler::CreateErrorMsg(const wchar *FileName) { CreateErrorMsg(NULL,FileName); } void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName) { uiMsg(UIERROR_FILECREATE,ArcName,FileName); SysErrMsg(); SetErrorCode(RARX_CREATE); } void ErrorHandler::ReadErrorMsg(const wchar *FileName) { ReadErrorMsg(NULL,FileName); } void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName) { uiMsg(UIERROR_FILEREAD,ArcName,FileName); SysErrMsg(); SetErrorCode(RARX_FATAL); } void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName) { uiMsg(UIERROR_FILEWRITE,ArcName,FileName); SysErrMsg(); SetErrorCode(RARX_WRITE); } void ErrorHandler::ArcBrokenMsg(const wchar *ArcName) { uiMsg(UIERROR_ARCBROKEN,ArcName); SetErrorCode(RARX_CRC); } void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName) { uiMsg(UIERROR_CHECKSUM,ArcName,FileName); SetErrorCode(RARX_CRC); } void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName) { uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName); ErrHandler.SetErrorCode(RARX_FATAL); } void ErrorHandler::Exit(RAR_EXIT ExitCode) { uiAlarm(UIALARM_ERROR); Throw(ExitCode); } void ErrorHandler::SetErrorCode(RAR_EXIT Code) { switch(Code) { case RARX_WARNING: case RARX_USERBREAK: if (ExitCode==RARX_SUCCESS) ExitCode=Code; break; case RARX_CRC: if (ExitCode!=RARX_BADPWD) ExitCode=Code; break; case RARX_FATAL: if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING) ExitCode=RARX_FATAL; break; default: ExitCode=Code; break; } ErrCount++; } #ifdef _WIN_ALL BOOL __stdcall ProcessSignal(DWORD SigType) #else #if defined(__sun) extern "C" #endif void _stdfunction ProcessSignal(int SigType) #endif { #ifdef _WIN_ALL // When a console application is run as a service, this allows the service // to continue running after the user logs off. if (SigType==CTRL_LOGOFF_EVENT) return TRUE; #endif ErrHandler.UserBreak=true; ErrHandler.SetDisableShutdown(); mprintf(St(MBreak)); #ifdef _WIN_ALL // Let the main thread to handle 'throw' and destroy file objects. for (uint I=0;!ErrHandler.MainExit && I<50;I++) Sleep(100); #if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL) ExtRes.UnloadDLL(); #endif exit(RARX_USERBREAK); #endif #ifdef _UNIX static uint BreakCount=0; // User continues to press Ctrl+C, exit immediately without cleanup. if (++BreakCount>1) exit(RARX_USERBREAK); // Otherwise return from signal handler and let Wait() function to close // files and quit. We cannot use the same approach as in Windows, // because Unix signal handler can block execution of our main code. #endif #if defined(_WIN_ALL) && !defined(_MSC_VER) // Never reached, just to avoid a compiler warning return TRUE; #endif } void ErrorHandler::SetSignalHandlers(bool Enable) { EnableBreak=Enable; #ifdef _WIN_ALL SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE); #else signal(SIGINT,Enable ? ProcessSignal:SIG_IGN); signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN); #endif } void ErrorHandler::Throw(RAR_EXIT Code) { if (Code==RARX_USERBREAK && !EnableBreak) return; #if !defined(SILENT) // Do not write "aborted" when just displaying online help. if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR) mprintf(L"\n%s\n",St(MProgAborted)); #endif SetErrorCode(Code); throw Code; } bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) { #if !defined(SFX_MODULE) && !defined(SILENT) #ifdef _WIN_ALL int ErrType=GetLastError(); if (ErrType!=0) return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), Msg,(DWORD)Size,NULL)!=0; #endif #if defined(_UNIX) || defined(_EMX) if (errno!=0) { char *err=strerror(errno); if (err!=NULL) { CharToWide(err,Msg,Size); return true; } } #endif #endif return false; } void ErrorHandler::SysErrMsg() { #if !defined(SFX_MODULE) && !defined(SILENT) wchar Msg[1024]; if (!GetSysErrMsg(Msg,ASIZE(Msg))) return; #ifdef _WIN_ALL wchar *CurMsg=Msg; while (CurMsg!=NULL) { while (*CurMsg=='\r' || *CurMsg=='\n') CurMsg++; if (*CurMsg==0) break; wchar *EndMsg=wcschr(CurMsg,'\r'); if (EndMsg==NULL) EndMsg=wcschr(CurMsg,'\n'); if (EndMsg!=NULL) { *EndMsg=0; EndMsg++; } uiMsg(UIERROR_SYSERRMSG,CurMsg); CurMsg=EndMsg; } #endif #if defined(_UNIX) || defined(_EMX) uiMsg(UIERROR_SYSERRMSG,Msg); #endif #endif } int ErrorHandler::GetSystemErrorCode() { #ifdef _WIN_ALL return GetLastError(); #else return errno; #endif } void ErrorHandler::SetSystemErrorCode(int Code) { #ifdef _WIN_ALL SetLastError(Code); #else errno=Code; #endif }