/*=========================================================================== * * File: DL_Err.CPP * Author: Dave Humphrey (uesp@m0use.net) * Created On: Tuesday, April 24, 2001 * * Class CErrorDatabase and CErrorHandler for handling custom errors * in an application. The database class supports any system/platform * specific errors meaning that you only need to worry about handling * errors with the global objects ErrorHandler and ErrorDatabase. For * instance, if you were making a Windows, DirectDraw, COM application, * you would normally have to worry about the system, Windows, DirectDraw * and the COM specific errors, in addition to your own (thats 5 different * error systems). All these methods can be 'hidden' by the error handling * classes, making your code better looking. * * TODO: The ErrorHandler records error incidents in a singly linked list. * Initially, the list simply grew until the program ended, but in * some cases this caused problems (1000's of errors being saved). * Now, up to MAX_ERROR_INCIDENTS are recorded. Once this limit * is reached, the incidents are deleted except for the current * one. Better methods could be used if so required. * *=========================================================================*/ /* Include Files */ #include "dl_err.h" #include "dl_str.h" #include <errno.h> /* Include Windows error messages/functions */ #if defined(_WIN32) || defined(__BCPLUSPLUS__) #include "windows.h" #endif /* Include TC graphic error messages */ #if defined(_TCGRAPHERRORS) #include <graphics.h> #endif /*=========================================================================== * * Begin Local Variables * *=========================================================================*/ DEFINE_FILE(); /*=========================================================================== * End of Local Variables *=========================================================================*/ /*=========================================================================== * * Begin Global Variables * *=========================================================================*/ CErrorDatabase ErrorDatabase; CErrorHandler ErrorHandler(ErrorDatabase); /*=========================================================================== * End of Global Variables *=========================================================================*/ /*=========================================================================== * * Class CErrorRecord Constructor * *=========================================================================*/ CErrorRecord::CErrorRecord (void) { pMessage = NULL; CustomErrFunction = NULL; Code = ERR_NONE; Level = ERRLEVEL_UNKNOWN; pNext = NULL; } /*=========================================================================== * End of Class CErrorRecord Constructor *=========================================================================*/ /*=========================================================================== * * Class CErrorRecord Method - void Destroy (void); * * Deletes the contents of the object. * *=========================================================================*/ void CErrorRecord::Destroy (void) { //DEFINE_FUNCTION("CErrorRecord::Destroy()"); DestroyPointer(pMessage); Code = ERR_NONE; Level = ERRLEVEL_UNKNOWN; CustomErrFunction = NULL; /* Assumed the linked list structure is maintained by the parent */ pNext = NULL; } /*=========================================================================== * End of Class Method CErrorRecord::Destroy() *=========================================================================*/ /*=========================================================================== * * Class CErrorRecord Method - char* GetMsg (SubCode) const; * * Attempts to retrieve the message for the error. If the custom error * function is non-NULL, the message is returned from that function using * the given error SubCode. Otherwise, the current message string of the * object is returned. * *=========================================================================*/ char* CErrorRecord::GetMsg (const errcode_t SubCode) const { //DEFINE_FUNCTION("CErrorRecord::GetMsg()"); /* Return the current message string for a regular error */ if (CustomErrFunction == NULL) return (pMessage); /* Call the custom error function with the error's subcode */ return CustomErrFunction(SubCode); } /*=========================================================================== * End of Class Method CErrorRecord::GetMsg() *=========================================================================*/ /*=========================================================================== * * Class CErrorDatabase Constructor * *=========================================================================*/ CErrorDatabase::CErrorDatabase (void) { pHead = NULL; AddedDefaultErrors = FALSE; NumErrors = 0; InitDefaultErrors(); } /*=========================================================================== * End of Class CErrorDatabase Constructor *=========================================================================*/ /*=========================================================================== * * Class CErrorDatabase Method - void Destroy (void); * * Deletes the current contents of the object. * *=========================================================================*/ void CErrorDatabase::Destroy (void) { //DEFINE_FUNCTION("CErrorDatabase::Destroy()"); /* Delete the singly linked list */ ClearErrors(); } /*=========================================================================== * End of Class Method CErrorDatabase::Destroy() *=========================================================================*/ /*=========================================================================== * * Class CErrorDatabase Method - void Add (Code, pMessage, Level); * * Attempts to add the given custom error definition to the singly * linked list. The error Level is optional and defaults to ERRLEVEL_ERROR. * On any error, an exception is thrown (no memory to allocate). * *=========================================================================*/ void CErrorDatabase::Add (const errcode_t Code, const char* pMessage, const errlevel_t Level) { DEFINE_FUNCTION("CErrorDatabase::Add()"); CErrorRecord* pNewRecord; /* Ensure valid input */ ASSERT(pMessage != NULL); /* Attempt to allocate the new record */ CreatePointer(pNewRecord, CErrorRecord); /* Attempt to initialize the new record */ pNewRecord->SetCode(Code); pNewRecord->SetLevel(Level); pNewRecord->SetMsg(pMessage); /* Add the new error to the head of the linked list */ pNewRecord->SetNext(pHead); pHead = pNewRecord; NumErrors++; } /*=========================================================================== * End of Class Method CErrorDatabase::Add() *=========================================================================*/ /*=========================================================================== * * Class CErrorDatabase Method - void AddCustomError (Code, ErrFunction); * * Adds a custom error function with the given error code to the database. * Throws an exception if memory could not be allocated. * *=========================================================================*/ void CErrorDatabase::AddCustomError (const errcode_t Code, PERR_CUSTOM_FUNCTION ErrFunction) { DEFINE_FUNCTION("CErrorDatabase::AddCustomError()"); CErrorRecord* pNewRecord; /* Ensure valid input */ ASSERT(ErrFunction != NULL); /* Attempt to allocate the new record */ CreatePointer(pNewRecord, CErrorRecord); /* Attempt to initialize the new record */ pNewRecord->SetCode(Code); pNewRecord->SetFunction(ErrFunction); /* Add the new error to the head of the linked list */ pNewRecord->SetNext(pHead); pHead = pNewRecord; NumErrors++; } /*=========================================================================== * End of Class Method CErrorDatabase::AddCustomError() *=========================================================================*/ /*=========================================================================== * * Class CErrorDatabase Method - void ClearErrors (void); * * Deletes the entire singly linked list. * *=========================================================================*/ void CErrorDatabase::ClearErrors (void) { //DEFINE_FUNCTION("CErrorDatabase::ClearErrors()"); CErrorRecord* pListPtr; /* Delete all items in the singly linked list */ while (pHead != NULL) { pListPtr = pHead->GetNext(); DestroyPointer(pHead); pHead = pListPtr; } AddedDefaultErrors = FALSE; NumErrors = 0; } /*=========================================================================== * End of Class Method CErrorDatabase::ClearErrors() *=========================================================================*/ /*=========================================================================== * * Class CErrorDatabase Method - CErrorRecord* Find (Code); * * Attempt to find the custom error record with the given error code. * Returns NULL if the given record could not be found. * *=========================================================================*/ CErrorRecord* CErrorDatabase::Find (const errcode_t Code) { DEFINE_FUNCTION("CErrorDatabase::Find()"); CErrorRecord* pSearchPtr; /* Search the entire linked list */ for (pSearchPtr = pHead; pSearchPtr != NULL; pSearchPtr = pSearchPtr->GetNext()) { if (pSearchPtr->GetCode() == Code) return (pSearchPtr); } /* No record was found */ SystemLog.DebugPrintf("%s - Failed to find error record with code %ld!", ThisFunction, Code); return (NULL); } /*=========================================================================== * End of Class Method CErrorDatabase::Find() *=========================================================================*/ /*=========================================================================== * * Class CErrorDatabase Method - void InitDefaultErrors (void); * * Adds the default custom errors the the database. Will not add the * errors if they were previously added. * *=========================================================================*/ void CErrorDatabase::InitDefaultErrors (void) { //DEFINE_FUNCTION("CErrorDatabase::InitDefaultErrors()"); /* Ignore if the default errors have already been added */ if (AddedDefaultErrors) return; /* Add the regular custom errors */ Add (ERR_NONE, "No error generated.", ERRLEVEL_INFO); Add (ERR_MEM, "Failed to allocate memory!", ERRLEVEL_CRITICAL); Add (ERR_OPENFILE, "Failed to open file!", ERRLEVEL_ERROR); Add (ERR_READFILE, "Failed to read from file!", ERRLEVEL_ERROR); Add (ERR_WRITEFILE, "Failed to write to file!", ERRLEVEL_ERROR); Add (ERR_BADARRAYINDEX,"Index exceeds array limits!", ERRLEVEL_WARNING); Add (ERR_MAXINDEX, "Array has reached it's maximum size!", ERRLEVEL_WARNING); Add (ERR_BADINPUT, "Invalid input was received!", ERRLEVEL_WARNING); Add (ERR_OVERFLOW, "Received input that would result in an overflow!", ERRLEVEL_ERROR); Add (ERR_CUSTOM, "Custom application error!", ERRLEVEL_ERROR); /* Add the system error messages */ AddCustomError (ERR_SYSTEM, SystemErrorFunction); /* Add the graphic error messages under DOS if required */ #if defined(_TCGRAPHERRORS) AddCustomError(ERR_TCGRAPH, TCGraphErrorFunction); #endif /* Add the windows error messages under Windows if required */ #if defined(_WIN32) || defined(__BCPLUSPLUS__) AddCustomError(ERR_WINDOWS, WindowsErrorFunction); #endif AddedDefaultErrors = TRUE; } /*=========================================================================== * End of Class Method CErrorDatabase::InitDefaultErrors() *=========================================================================*/ /*=========================================================================== * * Class CErrorIncident Constructor * *=========================================================================*/ CErrorIncident::CErrorIncident (void) { pNext = NULL; pMessage = NULL; Code = ERR_NONE; SubCode = ERR_NONE; } /*=========================================================================== * End of Class CErrorIncident Constructor *=========================================================================*/ /*=========================================================================== * * Class CErrorIncident Method - void Destroy (void); * * Clears the contents of the object. * *=========================================================================*/ void CErrorIncident::Destroy (void) { //DEFINE_FUNCTION("CErrorIncident::Destroy()"); DestroyPointer(pMessage); Code = ERR_NONE; SubCode = ERR_NONE; /* Assumes that the linked list structure is maintained by the parent */ pNext = NULL; } /*=========================================================================== * End of Class Method CErrorIncident::Destroy() *=========================================================================*/ /*=========================================================================== * * Class CErrorIncident Method - void SetMsg (pString, Args); * * Sets the incident message string using a variable list of arguments, * as in the vprintf() type functions. Messages are limited to a * size of MAX_ERROR_MESSAGESIZE bytes. * *=========================================================================*/ void CErrorIncident::SetMsg (const char* pString, va_list Args) { //DEFINE_FUNCTION("CErrorIncident::SetMsg(char*, va_list)"); char MessageBuffer[MAX_ERROR_MESSAGESIZE*2]; int Result; /* Attempt to create the message string */ Result = vsnprintf(MessageBuffer, MAX_ERROR_MESSAGESIZE, pString, Args); /* Only set message if the message was successfully created */ if (Result >= 0) ReplaceString(&pMessage, MessageBuffer); } /*=========================================================================== * End of Class Method CErrorIncident::SetMsg() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Constructor * *=========================================================================*/ CErrorHandler::CErrorHandler (CErrorDatabase& ErrDB) : refErrorDatabase(ErrDB) { pIncidentHead = NULL; NumErrors = 0; pNotifyFunc = NULL; } /*=========================================================================== * End of Class CErrorHandler Constructor *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void Destroy (void); * * Clears the contents of the object. * *=========================================================================*/ void CErrorHandler::Destroy (void) { //DEFINE_FUNCTION("CErrorHandler::Destroy()"); /* Delete the current errors */ ClearErrors(); pNotifyFunc = NULL; } /*=========================================================================== * End of Class Method CErrorHandler::Destroy() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void AddError (Code, pString, ...); * * Adds the given error code and optional message to the top of the error * list. Throws an exception on a memory allocation error. Accepts message * input as per the printf() type functions. Message string should be * limited to MAX_ERROR_MESSAGESIZE characters in size. Does not ensure * that the message is correctly written. * *=========================================================================*/ void CErrorHandler::AddError (const errcode_t Code, const char* pString, ...) { //DEFINE_FUNCTION("CErrorHandler::AddError(errcode_t, char*, ...)"); va_list Args; va_start(Args, pString); AddError(Code, ERR_NONE, pString, Args); va_end(Args); } /*=========================================================================== * End of Class Method CErrorHandler::AddError() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void AddError (Code, SubCode, pString, ...); * * Adds the given error code and optional message to the top of the error * list. Throws an exception on a memory allocation error. Accepts message * input as per the printf() type functions. Message string should be * limited to MAX_ERROR_MESSAGESIZE characters in size. Does not ensure * that the message is correctly written. * *=========================================================================*/ void CErrorHandler::AddError (const errcode_t Code, const errcode_t SubCode, const char* pString, ...) { //DEFINE_FUNCTION("CErrorHandler::AddError(errcode_t, errcode_t, char*, ...)"); va_list Args; va_start(Args, pString); AddErrorV(Code, SubCode, pString, Args); va_end(Args); } /*=========================================================================== * End of Class Method CErrorHandler::AddError() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void AddErrorV (Code, SubCode, pString, Args); * * Adds the given error code and optional message to the top of the error * list. Throws an exception on a memory allocation error. Accepts message * input as per the vprintf() type functions. Message string should be * limited to MAX_ERROR_MESSAGESIZE characters in size. Does not ensure * that the message is correctly written. * *=========================================================================*/ void CErrorHandler::AddErrorV (const errcode_t Code, const errcode_t SubCode, const char* pString, va_list Args) { DEFINE_FUNCTION("CErrorHandler::AddErrorV(errcode_t, errcode_t, char*, va_list)"); CErrorIncident* pNewIncident; /* Ensure we don't exceed the maximum error limit */ if (NumErrors >= MAX_ERROR_INCIDENTS) { SystemLog.Printf("Clearing %u error incidents...", NumErrors); ClearErrors(); } /* Attempt to allocate a new incident object */ CreatePointer(pNewIncident, CErrorIncident); /* Initialize the new error incident */ pNewIncident->SetCode(Code); pNewIncident->SetSubCode(SubCode); if (pString != NULL) pNewIncident->SetMsg(pString, Args); /* Add error to head of incident list */ pNewIncident->SetNext(pIncidentHead); pIncidentHead = pNewIncident; NumErrors++; /* Output the error information to the log file */ OutputLastErrorToLog(); } /*=========================================================================== * End of Class Method CErrorHandler::AddErrorV() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void ClearErrors (void); * * Deletes all the errors currently in the linked list. * *=========================================================================*/ void CErrorHandler::ClearErrors (void) { //DEFINE_FUNCTION("CErrorHandler::ClearErrors()"); CErrorIncident* pListPtr; /* Delete all elements in the singly linked list */ while (pIncidentHead != NULL) { pListPtr = pIncidentHead->GetNext(); DestroyPointer(pIncidentHead); pIncidentHead = pListPtr; } NumErrors = 0; } /*=========================================================================== * End of Class Method CErrorHandler::ClearErrors() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void Exit (const char* pTitle); * * Displays the last error message using the Notify() method and then * aborts the program using exit() (end with cleanup code). * *=========================================================================*/ void CErrorHandler::Exit (const char* pTitle) { //DEFINE_FUNCTION("CErrorHandler::Exit()"); /* Display the error message to user */ Notify(pTitle); /* End program calling cleanup code first */ exit(EXIT_FAILURE); } /*=========================================================================== * End of Class Method CErrorHandler::Exit() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void _Exit (const char* pTitle); * * Displays the last error message using the Notify() method and then * aborts the program using _exit() (end without cleanup code). * *=========================================================================*/ void CErrorHandler::_Exit (const char* pTitle) { //DEFINE_FUNCTION("CErrorHandler::_Exit()"); /* Display the error message to user */ Notify(pTitle); /* End program immediately without calling any cleanup code */ _exit(EXIT_FAILURE); } /*=========================================================================== * End of Class Method CErrorHandler::_Exit() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - CErrorIncident* GetError (Index); * * Attempts to return the specified error from the linked list. ASSERTs * if the given index is invalid. The specified error record is not * removed from the incident list (unlike the PopError() method). * *=========================================================================*/ CErrorIncident* CErrorHandler::GetError (const int Index) { DEFINE_FUNCTION("CErrorHandler::GetError()"); CErrorIncident* pListPtr; int ListCounter; /* Ensure the input index is valid */ ASSERT(Index >= 0); ASSERT(Index < NumErrors); /* Move to the specified element in the list */ for (pListPtr = pIncidentHead, ListCounter = 0; pListPtr != NULL && ListCounter < Index; pListPtr = pListPtr->GetNext(), ListCounter++) { } /* Ensure a valid list element was found */ ASSERT(pListPtr != NULL); return (pListPtr); } /*=========================================================================== * End of Class Method CErrorHandler::GetError() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - char* GetLastErrorMsg (void); * * Returns the error message of the most recent incident. If no incidents * have been recorded, returns a standard 'no errors' type string. If the * incident's message is NULL, it attempts to return the database * message for that error. Always returns a valid string, never NULL. * *=========================================================================*/ char* CErrorHandler::GetLastErrorMsg (void) { //DEFINE_FUNCTION("CErrorHandler::GetLastErrorMsg()"); /* Ensure there is at least one incident recorded in list */ if (NumErrors == 0) return ("No recorded errors."); /* Check for a valid incident message */ if (pIncidentHead->GetMsg() != NULL) return (pIncidentHead->GetMsg()); /* Return the message from the error database */ return (GetLastErrorDBMsg()); } /*=========================================================================== * End of Class Method CErrorHandler::GetLastErrorMsg() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - char* GetLastDBErrorMsg (void); * * Returns the database error message of the most recent incident. If * no incidents have been recorded, returns a standard 'no errors' type * string. Always returns a valid string, never NULL. * *=========================================================================*/ char* CErrorHandler::GetLastErrorDBMsg (void) { //DEFINE_FUNCTION("CErrorHandler::GetLastDBErrorMsg()"); CErrorRecord* pErrRecord; /* Ensure there is at least one incident recorded in list */ if (NumErrors == 0) return ("No recorded errors."); /* Attempt to retrieve database message for error */ pErrRecord = refErrorDatabase.Find(pIncidentHead->GetCode()); if (pErrRecord != NULL) return (pErrRecord->GetMsg(pIncidentHead->GetSubCode())); /* No error message available for the incident! */ SystemLog.Printf("No message available for error %ld/%ld", pIncidentHead->GetCode(), pIncidentHead->GetSubCode()); return ("No message available for the error!"); } /*=========================================================================== * End of Class Method CErrorHandler::GetLastDBErrorMsg() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void Notify (pTitle); * * Notifies the user of the most recent error. Uses the Printf() method * to output results depending on the current compiled system. * Nothing happens if there are no current errors in the list. * *=========================================================================*/ void CErrorHandler::Notify (const char* pTitle) { //DEFINE_FUNCTION("CErrorHandler::Notify()"); CErrorRecord* pErrorRecord; /* Ignore if there are no errors to output */ if (pIncidentHead == NULL) return; /* Attempt to find the error record in the database of custom errors */ pErrorRecord = refErrorDatabase.Find(pIncidentHead->GetCode()); if (pErrorRecord == NULL) { Printf(pTitle, "Unknown error code %ld!\n\r%s\n\r", pIncidentHead->GetCode(), pIncidentHead->GetMsg()); } else { Printf(pTitle, "%s\r\n%s\r\n", pErrorRecord->GetMsg(pIncidentHead->GetSubCode()), pIncidentHead->GetMsg()); } } /*=========================================================================== * End of Class Method CErrorHandler::Notify() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void OutputLastErrorToLog (void); * * Protectd class method which outputs the topmost error incident to the * SystemLog. * *=========================================================================*/ void CErrorHandler::OutputLastErrorToLog (void) { //DEFINE_FUNCTION("CErrorHandler::OutputLastErrorToLog()"); CErrorRecord* pErrorRecord; /* Ensure there is a valid error in the list */ if (pIncidentHead == NULL) return; /* Attempt to get the database entry for the error */ pErrorRecord = refErrorDatabase.Find(pIncidentHead->GetCode()); SystemLog.Printf ("Error (%ld / %ld): %s", pIncidentHead->GetCode(), pIncidentHead->GetSubCode(), (pErrorRecord == NULL) ? "No database entry for error found!" : pErrorRecord->GetMsg(pIncidentHead->GetSubCode()) ); SystemLog.Printf ("\t\t User Message: %s", pIncidentHead->GetMsg()); } /*=========================================================================== * End of Class Method CErrorHandler::OutputLastErrorToLog() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - CErrorIncident* PopError (void); * * Returns the most recently added error incident in the list, removing * it from the incident list. Returns NULL if no errors currently exist. * * See Also: GetError(int) * *=========================================================================*/ CErrorIncident* CErrorHandler::PopError (void) { //DEFINE_FUNCTION("CErrorHandler::PopError()"); CErrorIncident* pLastError; /* Check if there are any errors left to return */ if (pIncidentHead == NULL) return (NULL); /* Remove the head from the singly linked list */ pLastError = pIncidentHead; pIncidentHead = pLastError->GetNext(); NumErrors--; return (pLastError); } /*=========================================================================== * End of Class Method CErrorHandler::PopError() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void Printf (pTitle, pString, ...); * * Accepts input as the printf() function, outputting the given error message * to whatever the current system supports. Uses the variable argument version * of the Printf() method. Does not check to ensure the message was * successfully output. * *=========================================================================*/ void CErrorHandler::Printf (const char* pTitle, const char* pString, ...) { DEFINE_FUNCTION("CErrorHandler::Printf(char*, char*, ...)"); va_list Args; /* Ensure valid input */ ASSERT(pString != NULL); /* Use the variable argument version of Printf() */ va_start(Args, pString); Printf(pTitle, pString, Args); va_end(Args); } /*=========================================================================== * End of Class Method CErrorHandler::Printf() *=========================================================================*/ /*=========================================================================== * * Class CErrorHandler Method - void Printf (pTitle, pString, Args); * * Outputs an error message to the user depending on the current system. * Accepts a variable argument list (va_list). If a custom output function * is installed, the error message is redirected to it. Otherwise the * error is output depending on the system. Current supported systems * include: * WIN32 - Message dialog is displayed. * DEFAULT - Message is output to stderr * Does not check to ensure message is correctly output. * * See Also: Printf (char*, char*, ...); * *=========================================================================*/ void CErrorHandler::Printf (const char* pTitle, const char* pString, va_list Args) { DEFINE_FUNCTION("CErrorHandler::Printf(char*, char*, va_list)"); char MsgBuffer[MAX_ERROR_MESSAGESIZE+1]; /* Ensure valid input */ ASSERT(pString != NULL && pTitle != NULL && Args != NULL); /* Use the custom notify function if supplied with one */ if (pNotifyFunc != NULL) { vsnprintf(MsgBuffer, MAX_ERROR_MESSAGESIZE, pString, Args); pNotifyFunc(pTitle, MsgBuffer); return; } /*---------- Begin Win32 Specific Code ----------------------------*/ #if (defined(_WIN32) || defined(__BCPLUSPLUS__)) && !defined(_CONSOLE) /* Output the variable argument list to the temporary string */ vsnprintf(MsgBuffer, MAX_ERROR_MESSAGESIZE, pString, Args); /* Display a standard error message box */ MessageBox(NULL, MsgBuffer, pTitle, MB_OK | MB_ICONHAND); /*---------- Begin Default Code (Output to stderr) ----------------*/ #else /* Output the variable argument list to the stderr stream */ fprintf (stderr, "%s\n", pTitle); fprintf (stderr, "\t"); vfprintf (stderr, pString, Args); fprintf (stderr, "\n"); fflush(stderr); #endif } /*=========================================================================== * End of Class Method CErrorHandler::Printf() *=========================================================================*/ /*=========================================================================== * * Function - char* SystemErrorFunction (Code); * * Returns the error message associated with the given system error code. * Always returns a valid string. * *=========================================================================*/ char* SystemErrorFunction (const errcode_t Code) { DEFINE_FUNCTION("SystemErrorFunction()"); char* pErrMessage; /* Check to ensure that it is a valid system error code */ if (Code < 0 || ((int) Code) >= _sys_nerr) return ("Invalid system error code!"); /* Retrieve the error message from the system, ensuring its valid */ pErrMessage = sys_errlist[(int)Code]; ASSERT(pErrMessage != NULL); //SystemLog.Printf ("SystemErrorFunction(%s)", pErrMessage); return (pErrMessage); } /*=========================================================================== * End of Function SystemErrorFunction() *=========================================================================*/ #if defined(_TCGRAPHERRORS) /*=========================================================================== * * Function - char* TCGraphErrorFunction (Code); * * Returns the error message associated with the given graphics error code * under TurboC for DOS. Always returns a valid string. * *=========================================================================*/ char* TCGraphErrorFunction (const errcode_t Code) { //DEFINE_FUNCTION("TCGraphErrorFunction()"); char* pErrMessage; /* Retrieve the error message from the library */ pErrMessage = grapherrormsg((int)Code); /* Ensure the message is valid */ if (pErrMessage == NULL) return ("Invalid graphics error code!"); return (pErrMessage); } /*=========================================================================== * End of Function TCGraphErrorFunction() *=========================================================================*/ #endif #if defined(_WIN32) || defined(__BCPLUSPLUS__) /*=========================================================================== * * Function - char* WindowsErrorFunction (Code); * * Returns the error message associated with the given windows error code * as returned by GetLastError(). Always returns a valid string. * *=========================================================================*/ char* WindowsErrorFunction (const errcode_t Code) { //DEFINE_FUNCTION("WindowsErrorFunction()"); static char ErrMessage[512] = ""; DWORD Result; /* Format message string */ #if defined(__BCPLUSPLUS__) Result = sprintf (ErrMessage, "Windows 16-bit error code = %ld (0x%04X)", Code, Code); #else Result = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) Code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &ErrMessage[0], 511, NULL); #endif /* Ensure the message is valid */ if (Result == 0) return ("Error creating Windows error message!"); return (ErrMessage); } /*=========================================================================== * End of Function WindowsErrorFunction() *=========================================================================*/ #endif /*=========================================================================== * * Begin Module Test Routines * * Debug builds only test routines for this module. * *=========================================================================*/ #if defined(_DEBUG) /* Turn off several warnings associated with the test code */ #if defined(__BCPLUSPLUS__) #pragma warn -rch #pragma warn -ccc #endif /*=========================================================================== * * Function - char* Test_CustomErrorFunc (const errcode_t Code); * * Test for the custom error function. Returns test error messages * depending on the value of the input error code. Always returns * a valid string. * *=========================================================================*/ char* Test_CustomErrorFunc (const errcode_t Code) { //DEFINE_FUNCTION("Test_CustomErrorFunc()"); switch (Code) { case TEST_ERR1: return ("Test Error 1"); case TEST_ERR2: return ("Test Error 2"); case TEST_ERR3: return ("Test Error 3"); case TEST_ERR4: return ("Test Error 4"); default: return ("Unknown Test Error Code"); } } /*=========================================================================== * End of Function Test_CustomErrorFunc() *=========================================================================*/ /*=========================================================================== * * Function - void Test_AddError (void); * * Tests the Add() method of the CErrorDatabase class. * 1. Add errors with/without default error level. * 2. Addition of duplicate error codes * 3. Indirectly tests the Find() method. * Function ASSERTs on any error. Results are output to the SystemLog. * *=========================================================================*/ void Test_AddError (void) { DEFINE_FUNCTION("Test_AddError()"); CErrorRecord* pFindRecord; SystemLog.Printf (stdout, "================ Test_AddError() ==================="); /* Test the addition of errors to the database */ ErrorDatabase.Add(TEST_ERR1, "Test error code 1 message"); ErrorDatabase.Add(TEST_ERR2, "Test error code 2 message", ERRLEVEL_WARNING); /* Check to ensure TEST_ERR1 was correctly stored */ pFindRecord = ErrorDatabase.Find(TEST_ERR1); ASSERT(pFindRecord != NULL); ASSERT(pFindRecord->GetCode() == TEST_ERR1); SystemLog.Printf ("TEST_ERR1 = '%s'", pFindRecord->GetMsg()); /* Check to ensure TEST_ERR2 was correctly stored */ pFindRecord = ErrorDatabase.Find(TEST_ERR2); ASSERT(pFindRecord != NULL); ASSERT(pFindRecord->GetCode() == TEST_ERR2); ASSERT(pFindRecord->GetLevel() == ERRLEVEL_WARNING); SystemLog.Printf ("TEST_ERR2 = '%s'", pFindRecord->GetMsg()); /* Attempt to add a duplicate error code */ ErrorDatabase.Add(TEST_ERR2, "Test error code 2a message", ERRLEVEL_CRITICAL); pFindRecord = ErrorDatabase.Find(TEST_ERR2); ASSERT(pFindRecord != NULL); SystemLog.Printf ("TEST_ERR2a = '%s'", pFindRecord->GetMsg()); } /*=========================================================================== * End of Function Test_AddError() *=========================================================================*/ /*=========================================================================== * * Function - void Test_AddCustomError (void); * * Tests the addition of a custom error to the database. * 1. Add the error and ensure it was correctly added. * 2. Test the retrieval of various valid/invalid error sub-codes. * 3. Indirectly tests the Find() method. * *=========================================================================*/ void Test_AddCustomError (void) { DEFINE_FUNCTION("Test_AddCustomError()"); CErrorRecord* pFindRecord; SystemLog.Printf (stdout, "================ Test_AddCustomError() ==================="); /* Add the custom error */ ErrorDatabase.AddCustomError(TEST_ERR3, Test_CustomErrorFunc); pFindRecord = ErrorDatabase.Find(TEST_ERR3); ASSERT(pFindRecord != NULL); /* Test the retrieval of various custom error messages */ SystemLog.Printf ("CustomError(ERR_NONE) = '%s'", pFindRecord->GetMsg(ERR_NONE)); SystemLog.Printf ("CustomError(TEST_ERR1) = '%s'", pFindRecord->GetMsg(TEST_ERR1)); SystemLog.Printf ("CustomError(TEST_ERR4) = '%s'", pFindRecord->GetMsg(TEST_ERR4)); } /*=========================================================================== * End of Function Test_AddCustomError() *=========================================================================*/ /*=========================================================================== * * Function - void Test_DefaultErrors (void); * * Tests the default errors in the CErrorDatabase class. * 1. Checks and outputs each default error message. * 2. Clears database and reinitializes default errors. * 3. Attempts to re-add default errors without clearing database. * 4. Indirectly tests the Find() and GetNumErrors() methods. * Function ASSERTs on any error. Results are output to the SystemLog. * *=========================================================================*/ void Test_DefaultErrors (void) { DEFINE_FUNCTION("Test_DefaultErrors()"); CErrorRecord* pFindRecord; signed int ErrorCounter; /* Default errors to test, ERR_NONE must be last in array */ errcode_t DefaultErrors[] = { ERR_MEM, ERR_OPENFILE, ERR_READFILE, ERR_WRITEFILE, ERR_OVERFLOW, ERR_BADINPUT, ERR_BADARRAYINDEX, ERR_MAXINDEX, ERR_SYSTEM, ERR_NONE }; SystemLog.Printf (stdout, "================ Test_DefaultErrors() ==================="); /* Test all error codes in the DefaultErrors[] array */ ErrorCounter = -1; do { ErrorCounter++; pFindRecord = ErrorDatabase.Find(DefaultErrors[ErrorCounter]); ASSERT(pFindRecord != NULL); SystemLog.Printf ("%2d) DefaultError(%2ld) = '%s'", ErrorCounter, DefaultErrors[ErrorCounter], pFindRecord->GetMsg()); } while (DefaultErrors[ErrorCounter] != ERR_NONE); /* Clear errors and retinitialize */ ErrorCounter = ErrorDatabase.GetNumErrors(); ErrorDatabase.ClearErrors(); ASSERT(ErrorDatabase.GetNumErrors() == 0); ErrorDatabase.InitDefaultErrors(); ASSERT(ErrorDatabase.GetNumErrors() == ErrorCounter); /* Attempt to re-add the default errors to database */ ErrorDatabase.InitDefaultErrors(); ASSERT(ErrorDatabase.GetNumErrors() == ErrorCounter); } /*=========================================================================== * End of Function Test_DefaultErrors() *=========================================================================*/ /*=========================================================================== * * Function - void Test_GetLastErrorMsg (void); * * Tests the CErrorHandler::GetLastErrorMsg() and GetLastErrorDBMsg() methods. * 1. Test with valid incidents/errors * 2. Test with invalid error codes * 3. Test with invalid incidents * *=========================================================================*/ void Test_GetLastErrorMsg (void) { //DEFINE_FUNCTION("Test_GetLastErrorMsg()"); SystemLog.Printf (stdout, "================ Testing GetLastErrorMsg() ==================="); /* Test with valid incidents/errors */ ErrorHandler.AddError(ERR_OPENFILE, "Test incident error message, %s, %d", "test string", 10101); SystemLog.Printf("\t***Good Code with Msg = %s", ErrorHandler.GetLastErrorMsg()); ErrorHandler.AddError(ERR_SYSTEM, ENOENT, "Test incident system error message, %s, %d", "test string", 10101); SystemLog.Printf("\t***System Code With Msg = %s", ErrorHandler.GetLastErrorMsg()); ErrorHandler.AddError(ERR_SYSTEM, ENOENT, NULL); SystemLog.Printf("\t***System Code no Msg = %s", ErrorHandler.GetLastErrorMsg()); /* Test with invalid error codes */ ErrorHandler.AddError(1414, 111, "Test invalid incident error message, %s, %d", "test string", 10101); SystemLog.Printf("\t***Bad Code with Msg = %s", ErrorHandler.GetLastErrorMsg()); /* Test with invalid error incidents */ ErrorHandler.ClearErrors(); SystemLog.Printf("\t***No Errors = %s", ErrorHandler.GetLastErrorMsg()); ErrorHandler.AddError(ERR_MEM, (char*)NULL); SystemLog.Printf("\t***Good Code, No Msg = %s", ErrorHandler.GetLastErrorMsg()); ErrorHandler.AddError(123, (char*)NULL); SystemLog.Printf("\t***Bad Code, No Msg = %s", ErrorHandler.GetLastErrorMsg()); } /*=========================================================================== * End of Function Test_GetLastErrorMsg() *=========================================================================*/ /*=========================================================================== * * Function - void Test_HandlerAddError (void); * * Tests the addition of errors to the ErrorHandler list. * 1. Tests the AddError() methods with a variety of inputs. * * *=========================================================================*/ void Test_HandlerAddError (void) { //DEFINE_FUNCTION("Test_HandlerAddError()"); SystemLog.Printf (stdout, "================ Test_HandlerAddError() ==================="); /* Test the standard AddError() method */ ErrorHandler.AddError(ERR_MEM, "Testing...Failed to allocate memory"); ErrorHandler.AddError(ERR_BADINPUT, "Testing ERR_BADINPUT (%s), 101 = %d", "ERR_BADINPUT", 101); ErrorHandler.AddError(ERR_NONE, "Testing ERR_NONE"); ErrorHandler.AddError(TEST_ERR1, "Testing TEST_ERR1"); ErrorHandler.AddError(TEST_ERR3, "Testing TEST_ERR3 with no subcode"); ErrorHandler.AddError(TEST_ERR5, "Testing invalid error code TEST_ERR5"); ErrorHandler.AddError(ERR_MEM); ErrorHandler.AddError(ERR_MEM, (char*)NULL); /* Test the sub-code AddError() method */ ErrorHandler.AddError(ERR_MEM, ERR_MEM, "Testing ERR_MEM subcode version"); ErrorHandler.AddError(ERR_NONE, ERR_NONE, "Testing ERR_NONE subcode version, %s, %d", "ERR_NONE", 101); ErrorHandler.AddError(TEST_ERR5, TEST_ERR5, "Testing invalid error code TEST_ERR5 with subcode TEST_ERR5"); ErrorHandler.AddError(TEST_ERR3, TEST_ERR1, "Testing TEST_ERR3 with subcode TEST_ERR1"); ErrorHandler.AddError(TEST_ERR3, TEST_ERR1, "Testing TEST_ERR3 with subcode TEST_ERR1 (%s, %d)", "TEST_ERR1", 101); ErrorHandler.AddError(TEST_ERR3, TEST_ERR3, "Testing TEST_ERR3 with subcode TEST_ERR3"); ErrorHandler.AddError(TEST_ERR3, TEST_ERR5, "Testing TEST_ERR3 with subcode TEST_ERR5"); ErrorHandler.AddError(ERR_SYSTEM, EACCES, "Testing ERR_SYSTEM with subcode EACCES"); ErrorHandler.AddError(ERR_MEM, ERR_MEM); ErrorHandler.AddError(ERR_MEM, ERR_MEM, NULL); } /*=========================================================================== * End of Function Test_HandlerAddError() *=========================================================================*/ /*=========================================================================== * * Function - void Test_HandlerNotify (void); * * Tests the Notify() method of the CErrorHandler class. * 1. Call Notify() for the current errors in the incident list. * 2. Tests the PopError() method to remove errors from handler. * *=========================================================================*/ void Test_HandlerNotify (void) { DEFINE_FUNCTION("Test_HandlerNotify()"); CErrorIncident* pErrorIncident; /* Test the Notify() method with a given title */ ErrorHandler.Notify("Test Title"); /* Output all errors in the incident list */ while (ErrorHandler.GetNumErrors() > 0) { ErrorHandler.Notify(); /* Delete the error from the handler */ pErrorIncident = ErrorHandler.PopError(); ASSERT(pErrorIncident != NULL); DestroyPointer(pErrorIncident); } } /*=========================================================================== * End of Function Test_HandlerNotify() *=========================================================================*/ /*=========================================================================== * * Function - void Test_SystemErrors (void); * * Tests the custom error system codes. * 1. Ensures the system error exists in the database. * 2. Checks default message output * 3. Tests each system error codes, outputting message to SystemLog * 4. Check a number of invalid system error codes * *=========================================================================*/ void Test_SystemErrors (void) { DEFINE_FUNCTION("Test_SystemErrors()"); CErrorRecord* pSystemError; int LoopCounter; SystemLog.Printf (stdout, "================ Test_SystemErrors() ==================="); /* Attempt to retrieve the system error record */ pSystemError = ErrorDatabase.Find(ERR_SYSTEM); ASSERT(pSystemError != NULL); /* Output default error message output */ SystemLog.Printf ("Default System Error Message = '%s'", pSystemError->GetMsg()); /* Output message for each valid system error */ for (LoopCounter = 0; LoopCounter < _sys_nerr; LoopCounter++) { SystemLog.Printf ("%2d) System Error = '%s'", LoopCounter, pSystemError->GetMsg(LoopCounter)); ASSERT(pSystemError->GetMsg(LoopCounter) != NULL); } /* Output a few invalid system error codes */ SystemLog.Printf ("System Error( 105) = '%s'", pSystemError->GetMsg(105)); SystemLog.Printf ("System Error(1111) = '%s'", pSystemError->GetMsg(1111)); } /*=========================================================================== * End of Function Test_SystemErrors() *=========================================================================*/ /*=========================================================================== * * Function - void Test_Allocation (void); * * Continually allocates and deletes errors and incidents to ensure reliability * in the long term. * *=========================================================================*/ void Test_Allocation (void) { DEFINE_FUNCTION("Test_Allocation()"); char MsgBuffer[101]; int LoopCounter; int ErrorCounter; int NumAllocations; SystemLog.Printf (stdout, "================ Start of Test_Allocation() ============"); /* Reset the current incident and database lists */ ErrorHandler.ClearErrors(); ErrorDatabase.ClearErrors(); /* Number of test loops */ for (LoopCounter = 0; LoopCounter < 1000; LoopCounter++) { NumAllocations = (int) ((float)rand() * 10000 / RAND_MAX); SystemLog.Printf (stdout, "================ Starting Test %d With %d Allocations ============", LoopCounter, NumAllocations); fprintf (stderr, "Test %d (%d)...", LoopCounter, NumAllocations); for (ErrorCounter = 0; ErrorCounter < NumAllocations; ErrorCounter++) { sprintf (MsgBuffer, "Error message %d of %d ", ErrorCounter, NumAllocations); ErrorDatabase.Add(ErrorCounter, MsgBuffer); ErrorHandler.AddError(ErrorCounter, "Testing error code %d of %d", ErrorCounter, NumAllocations); } /* Reset the incident and database lists */ SystemLog.Printf (stdout, "================ Clearing %d Allocated Errors ============", NumAllocations); ErrorHandler.ClearErrors(); ErrorDatabase.ClearErrors(); OutputBlockInfo(); /* Ensure the debug heap is still error free */ ASSERT(DebugHeapCheckMemory()); fprintf (stderr, "%u blocks remained after deletion.\n", GetNumBlocks()); } SystemLog.Printf (stdout, "================ End of Test_Allocation() ============"); } /*=========================================================================== * End of Function Test_Allocation() *=========================================================================*/ /*=========================================================================== * * Function - void Test_DLErr (void); * * Main test routine for the module. * 1. Tests the default error codes in the error database * 2. Tests the custom system error codes * 3. Tests the addition of regular errors to the database. * 4. Tests the addition of custom errors to the database * 5. Tests additition of errors to the ErrorHandler object. * 6, Tests the Notify()/PopError() methods of the ErrorHandler object. * 7. Clears all errors/incidents and outputs allocated blocks. * 8. Tests reliability of repeated number of allocations/deletions * 9. Tests the GetLastErrorMsg() and GetLastErrorDBMsg() methods * *=========================================================================*/ void Test_DLErr (void) { DEFINE_FUNCTION("Test_DLErr()"); ASSERT(DebugHeapCheckMemory()); SystemLog.Printf (stdout, "================ Test_DLErr() ==================="); Test_DefaultErrors(); Test_SystemErrors(); Test_AddError(); Test_AddCustomError(); Test_HandlerAddError(); Test_GetLastErrorMsg(); //Test_HandlerNotify(); //ErrorHandler.Exit("Exiting Application"); ASSERT(DebugHeapCheckMemory()); SystemLog.Printf(stdout, "========== Output Current Allocated Blocks =========="); OutputBlockInfo(); SystemLog.Printf(stdout, "========== Deleting Error Incidents and Database =========="); ErrorHandler.ClearErrors(); ErrorDatabase.ClearErrors(); OutputBlockInfo(); ASSERT(DebugHeapCheckMemory()); SystemLog.Printf (stdout, "================ End of Test_DLErr() ==================="); //Test_Allocation(); } /*=========================================================================== * End of Function Test_DLErr() *=========================================================================*/ /* Restore compiler warning options */ #if defined(__BCPLUSPLUS__) #pragma warn .rch #pragma warn .ccc #endif #endif /*=========================================================================== * End of Module Test Routines *=========================================================================*/