/*=========================================================================== * * File: GOutput.CPP * Author: Dave Humphrey (uesp@m0use.net) * Created On: Wednesday, March 28, 2001 * * Implements the CGenOutput class for reading/handling files that contain * the format for outputing general data of some sort in the format of * printf() like % codes. * *=========================================================================*/ /* Include Files */ #include "goutput.h" #include "dl_file.h" #include <ctype.h> /*=========================================================================== * * Begin Local Variable Definitions * *=========================================================================*/ DEFINE_FILE(); /*=========================================================================== * End of Local Variable Definitions *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Default Constructor * *=========================================================================*/ CGenOutput::CGenOutput () { //DEFINE_FUNCTION("CGenOutput::CGenOutput()"); pFileBuffer = NULL; pDefaultBuffer = NULL; UserFunction = NULL; } /*=========================================================================== * End of Class CGenOutput Constructor *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Constructor - CGenOutput(pBuffer); * *=========================================================================*/ CGenOutput::CGenOutput (const char* pBuffer) { //DEFINE_FUNCTION("CGenOutput::CGenOutput()"); pFileBuffer = NULL; pDefaultBuffer = CreateString(pBuffer); UserFunction = NULL; } /*=========================================================================== * End of Class CGenOutput Constructor *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Constructor - CGenOutput(pFunction); * *=========================================================================*/ CGenOutput::CGenOutput (PUSER_OUTPUT_FUNC pFunction) { //DEFINE_FUNCTION("CGenOutput::CGenOutput()"); pFileBuffer = NULL; pDefaultBuffer = NULL; UserFunction = pFunction; } /*=========================================================================== * End of Class CGenOutput Constructor *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Constructor - CGenOutput(pBuffer, pFunction); * *=========================================================================*/ CGenOutput::CGenOutput (const char* pBuffer, PUSER_OUTPUT_FUNC pFunction) { //DEFINE_FUNCTION("CGenOutput::CGenOutput()"); pFileBuffer = NULL; pDefaultBuffer = CreateString(pBuffer); UserFunction = pFunction; } /*=========================================================================== * End of Class CGenOutput Constructor *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Method - void Destroy (void) * * Class pseudo-destructor * *=========================================================================*/ void CGenOutput::Destroy (void) { //DEFINE_FUNCTION("CGenOutput::Destroy()"); DestroyPointer(pDefaultBuffer); DestroyPointer(pFileBuffer); } /*=========================================================================== * End of Class Method CGenOutput::Destroy() *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Method - boolean OutputFile (pOutputFile, pUserData) * * Outputs the current file buffer, parsing any format fields, to the * given file stream. Returns FALSE on any error. The user data can * be a pointer to anything that the user wishes to pass onto the user * defined output procedure. ASSERTS if given an invalid file handle. * *=========================================================================*/ boolean CGenOutput::OutputFile (FILE* pOutputFile, void* pUserData) { DEFINE_FUNCTION("CGenOutput::OutputFile()"); char* pParse; int Result; /* Ensure valid input */ ASSERT(pOutputFile != NULL); pParse = GetBuffer(); /* Parse and output the entire buffer */ while (*pParse != NULL_CHAR) { /* Check for the format character */ if (*pParse == '%') { Result = ParseFormat((const char**)&pParse, pOutputFile, pUserData); if (!Result) return (FALSE); } /* Output a single, regular character */ else { Result = fputc(*pParse, pOutputFile); if (Result == EOF) { ErrorHandler.AddError(ERR_SYSTEM, errno, "Error, failed to write character to file!"); return (FALSE); } pParse++; } } /* Flush output and return success */ fflush(pOutputFile); return (TRUE); } /*=========================================================================== * End of Class Method CGenOutput::OutputFile() *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Method - boolean ParseFormat (ppParse, pOutputFile, pUserData); * * Parses a format field starting with the format character '%', * outputing any results to the given stream. Returns FALSE on any error. * Contains a printf() like format specifier of the form: * * %[Width][.Precision][Type] * * [Width] is an optional field which can be composed of the numbers * 0 thru 9. * [.Precision] is optional, starting with the decimal point, and is again * composed of numbers 0 to 9. * [Type] is not optional and is a single character, case sensitive. * * The format fields are parsed and on success, passed onto the custom * user output function where the exact use of the fields is entirely * up to the user. The pUserData can be a pointer to any data that the * user wants passed onto the user supplied output function. The presence, * or absence, of the various format fields is given by the mask member * of the format structure passed onto the user output function. * * ASSERTs if given any invalid input (except for the user data). * *=========================================================================*/ boolean CGenOutput::ParseFormat (const char** ppParse, FILE* pOutputFile, void* pUserData) { DEFINE_FUNCTION("CGenOutput::ParseFormat()"); COutputFormat Format = { 0, 0, 0, 0 }; /* Ensure valid input */ ASSERT(*ppParse != NULL && **ppParse != NULL && pOutputFile != NULL); ASSERT(**ppParse == '%'); /* Skip the '%' character */ (*ppParse)++; /* Parse out the width/precision fields, if any */ ParseFormatWidth(ppParse, Format); ParseFormatPrecision(ppParse, Format); /* Only the format specifying character should be left */ if (**ppParse == NULL_CHAR) { ErrorHandler.AddError(ERR_BADINPUT, "Invalid output format string received, no type character found!"); return (FALSE); } Format.Mask |= OUTPUT_MASK_TYPE; Format.Type = **ppParse; *ppParse += 1; /* Call the user supplied format output function, if any */ if (UserFunction != NULL) return UserFunction(Format, pOutputFile, pUserData); return (TRUE); } /*=========================================================================== * End of Class Method CGenOutput::ParseFormat() *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Method - void ParseFormatWidth (ppParse, Format); * * Parses the format width, if present, from the given buffer position. * Protected class method. * *=========================================================================*/ void CGenOutput::ParseFormatWidth (const char** ppParse, COutputFormat& Format) { //DEFINE_FUNCTION("CGenOutput::ParseFormatWidth()"); if (isdigit(**ppParse)) { Format.Mask |= OUTPUT_MASK_WIDTH; while (**ppParse != NULL_CHAR && isdigit(**ppParse)) { Format.Width *= 10; Format.Width += (int) (**ppParse - '0'); *ppParse += 1; } } } /*=========================================================================== * End of Class Method CGenOutput::ParseFormatWidth() *=========================================================================*/ /*=========================================================================== * * Class CGenOutput Method - void ParseFormatPrecision (ppParse, Format); * * Parses the format precision, if present, from the given buffer position. * Protected class method. * *=========================================================================*/ void CGenOutput::ParseFormatPrecision (const char** ppParse, COutputFormat& Format) { //DEFINE_FUNCTION("CGenOutput::ParseFormatPrecision()"); if (**ppParse == '.') { *ppParse += 1; Format.Mask |= OUTPUT_MASK_PRECISION; while (**ppParse != NULL_CHAR && isdigit(**ppParse)) { Format.Precision *= 10; Format.Precision += (int) (**ppParse - '0'); *ppParse += 1; } } } /*=========================================================================== * End of Class Method CGenOutput::ParseFormatPrecision() *=========================================================================*/ /*============================================================================ * * Class CGenOutput Method - boolean ReadFile (pFilename); * * Attempts to read in the specified file into the memory for later use. * Returns FALSE on any error (file-not-found, etc..). * *==========================================================================*/ boolean CGenOutput::ReadFile (const char* pFilename) { DEFINE_FUNCTION("CGenOutput::ReadFile()"); size_t BytesRead; boolean Result; /* Ensure valid input */ if (pFilename == NULL) return (FALSE); DestroyArrayPointer(pFileBuffer); /* Allocate a new file buffer and read file */ Result = ::ReadFile(&pFileBuffer, BytesRead, pFilename, FILE_BINARY); return (Result); } /*============================================================================ * End of Class Method CGenOutput::ReadFile() *==========================================================================*/ /*=========================================================================== * * Begin Module Test Routines * * Debug build only functions for testing this module. * *=========================================================================*/ #if defined(_DEBUG) /*=========================================================================== * * Function - boolean Test_FormatFunc (Format, pOutputFile, pUserData); * * Test user output function. * *=========================================================================*/ boolean Test_FormatFunc (const COutputFormat& Format, FILE* pOutputFile, void* pUserData) { DEFINE_FUNCTION("Test_FormatFunc()"); SystemLog.Printf ("Format (Mask=%d) (Width=%d) (Precision=%d) (Type=%c)", Format.Mask, Format.Width, Format.Precision, Format.Type); SystemLog.Printf ("UserData (0x%p) %s", pUserData, (char*)pUserData); return (TRUE); } /*=========================================================================== * End of Function Test_FormatFunc() *=========================================================================*/ /*=========================================================================== * * Function - void Test_goutput (void); * * Test the CGenOutput class. * 1. Test initialization of class * 2. Test the SetDefaultBuffer() method * 3. Tests the SetUserFunction() method * 4. Tests the ReadFile() method * 5. Test the OutputFile() method * *=========================================================================*/ void Test_goutput (void) { DEFINE_FUNCTION("Test_goutput()"); CGenOutput TestOutput1("This is a test buffer"); CGenOutput TestOutput2; CLogFile TestLog("goutput.log"); SystemLog.Printf (stdout, "============== Testing the CGenOutput class ==============="); /* Ensure the output buffers were properly initialized */ ASSERT(strcmp(TestOutput1.GetBuffer(), "This is a test buffer") == 0); ASSERT(strcmp(TestOutput2.GetBuffer(), "") == 0); /* Test the SetDefaultBuffer() method */ TestOutput1.SetDefaultBuffer(NULL); TestOutput2.SetDefaultBuffer(""); ASSERT(strcmp(TestOutput1.GetBuffer(), "") == 0); ASSERT(strcmp(TestOutput2.GetBuffer(), "") == 0); TestOutput2.SetDefaultBuffer("123456"); ASSERT(strcmp(TestOutput2.GetBuffer(), "123456") == 0); TestOutput2.Destroy(); ASSERT(strcmp(TestOutput2.GetBuffer(), "") == 0); /* Test the SetUserFunction() method */ TestOutput1.SetUserFunction(Test_FormatFunc); TestOutput2.SetUserFunction(NULL); /* Test the ReadFile() method */ ASSERT(TestOutput1.ReadFile("test1.out") == TRUE); ASSERT(TestOutput2.ReadFile("nofile.123") == FALSE); /* Test the OutputFile() method */ TestLog.Printf("TestOutput1...file buffer"); ASSERT(TestOutput1.OutputFile(TestLog.GetFileHandle(), "This is some user data") == TRUE); TestLog.Printf("TestOutput2...no buffer"); ASSERT(TestOutput2.OutputFile(TestLog.GetFileHandle(), "More user data") == TRUE); TestLog.Printf("TestOutput2...default buffer"); TestOutput2.SetDefaultBuffer("1.This is a test\n\t2.Format \"%20.3111d\""); ASSERT(TestOutput2.OutputFile(TestLog.GetFileHandle(), "More user data") == TRUE); } /*=========================================================================== * End of Function Test_goutput() *=========================================================================*/ #endif /*=========================================================================== * End of Module Test Routines *=========================================================================*/