//+------------------------------------------------------------------- // // File: log.cxx // // Contents: The code for the logging servers client logging methods // // Functions: Log::Log() // Log::~Log() // Log::Info(inc, char *[]) // Log::WriteData(PCHAR) // // History: 24-Sep-90 DaveWi Initial Coding // 11-Mar-94 DaveY Miniature Log. // 10-Aug-95 ChrisAB Changed fopen()s to _fsopens() for // proper chicago operation // //-------------------------------------------------------------------- #include #pragma hdrstop extern "C" { #include #include #include #include #include #include #include #include #include } #include "log.hxx" #include "log.h" #define CCH_MAX_CHARS 1024 // Set up a table of the status text values const char * aStatus[] = { LOG_PASS_TXT, LOG_FAIL_TXT, LOG_WARN_TXT, LOG_ABORT_TXT, LOG_INFO_TXT, LOG_START_TXT, LOG_DONE_TXT }; #ifdef WIN16 #define OutputDebugStringA OutputDebugString #endif //+------------------------------------------------------------------- // // Member: Log::Log(int, char *[], DWORD) // // Synopsis: Log constructor with command line data given // // Arguments: [argc] - Argument count // [argv] - Argument array // [fUseUnicode] - whether we should use WCHARs // // Returns: Results from Info() // // History: ??-???-?? ???????? Created // Jun-11-92 DonCl added line to zero out LogStats struct // //-------------------------------------------------------------------- Log :: Log(int argc, char *argv[], DWORD dwCharType) : fInfoSet(FALSE), ulLastError(ERROR_SUCCESS) { #if defined (_WIN32) // // Check the result of the CMutex Contructor and continue if // it succeeded // ulLastError = hMtxSerialize.QueryError(); if (ulLastError == ERROR_SUCCESS) { ulLastError = Info(argc, argv); } #else ulLastError = Info(argc, argv); #endif } //+------------------------------------------------------------------- // // Member: Log::Log(PCHAR, PCHAR, PCHAR, PCHAR, PCHAR) // // Synopsis: Log constructor with internal data given // // Arguments: [pszSrvr] - Name of logging server // [pszTest] - Name of this test // [pszName] - Name of test runner // [pszSubPath] - Users log file qualifer // [pszObject] - Name of invoking object // // Returns: // // Modifies: // // History: ??-???-?? ???????? Created // Jun-11-92 DonCl added line to zero out LogStats struct // // BUGBUG wszName unused - left until all components can co-ordinate // with this. SarahJ //-------------------------------------------------------------------- Log :: Log(PCHAR pszSrvr, PCHAR pszTest, PCHAR pszName, PCHAR pszSubPath, PCHAR pszObject) : ulLastError(ERROR_SUCCESS) { #if defined (_WIN32) // // Check the result of the CMutex Contructor and continue if // it succeeded // ulLastError = hMtxSerialize.QueryError(); if (ulLastError != ERROR_SUCCESS) { return; } #endif InitLogging(); // Print warning if server name given /* BUGBUG Need to restore this once remote server is supported. if(NULL != pszSrvr) { fprintf(stderr, "Remote server not supported with this version of " "log.lib. Continuing.\n"); } */ ulLastError = SetInfo(NULL, pszTest, pszSubPath, pszObject); if(ulLastError == ERROR_SUCCESS) { ulLastError = LogOpen(); if(ulLastError == ERROR_SUCCESS) { WriteData("\n*LOG_START*-%s", pszShortLogFileName); } } } //+------------------------------------------------------------------- // // Member: Log::~Log() // // Synopsis: Destroy the Log Object // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- Log :: ~Log() { if(fInfoSet != FALSE) { if(WriteData("\n*LOG_DONE*") != ERROR_SUCCESS) { LogPrintf(stderr, "ERROR: Failed to log \"%s\" status\n", LOG_DONE_TXT); } } SetLoggingDir((PCHAR) NULL); SetMachineName((PCHAR) NULL); SetObjectName((PCHAR) NULL); SetTestName((PCHAR) NULL); SetPath((PCHAR) NULL); SetStatus((PCHAR) NULL); SetLogFileName((PCHAR) NULL); SetShortLogFileName((PCHAR) NULL); CloseLogFile(); } //+------------------------------------------------------------------- // // Member: Log::Info(int, char *[]) // // Synopsis: Parse the command line and set the appropriate member variables // // Arguments: [argc] - Argument count // [argv] - Argument vector // // Returns: // // Modifies: // // History: ??-???-?? ???????? Created // 30-Oct-92 SarahJ Updated parms to SetInfo // //-------------------------------------------------------------------- ULONG Log :: Info(int argc, char *argv[]) { USHORT usNdx; // Index into cmnd line args PCHAR pszTest = NULL; // Name of this test PCHAR pszSubPath = NULL; // Users log file qualifer PCHAR pszObject = NULL; // Name of invoking object PCHAR pch; // Temporary pointer into a string InitLogging(); // All the argv processing is still done in CHARs if(ulLastError == ERROR_SUCCESS) { // // For every command line switch, check its validity and parse its // value. This version allows / or - for switches. If there are // no command line switches, this loop is skipped. This is for the // case when Log() is called with no messages. // for(usNdx = 1; usNdx < (unsigned short) argc; ++usNdx) { char *szArg = argv[ usNdx]; pch = szArg; // check for / - if(*szArg == '/' || *szArg == '-') { register int i = 1; pch++; // // Check if next char is m, ie handed down from manager code. // If so skip m // if (*pch == 'm' || *pch == 'M') { pch++; i++; } ++pch; // Skip next char and check for : if(*pch++ == ':') { switch(toupper(szArg[i])) { case 'O': // Object name found pszObject = (PCHAR)pch; break; case 'P': // Directory Path switch found pszSubPath = (PCHAR)pch; break; case 'L': // Logging server name found // BUGBUG We don't want this message spit to debugger // all the time. // BUGBUG OutputDebugStringA("Logging server not supported with " // BUGBUG "this version of log.lib. Continuing.\n"); break; case 'T': // Test name found pszTest = (PCHAR)pch; break; default: // Skip this unknown argument break; } } } } // If no test name was given, set pszTest to the apps base name. char szName[_MAX_FNAME]; if(pszTest == NULL || *pszTest == NULLTERM) { GetBaseFilename(argv[0], szName); pszTest = szName; } } ulLastError = SetInfo(NULL, pszTest, pszSubPath, pszObject); // This was not here before, but it seems the log should be opened by now if(ERROR_SUCCESS == ulLastError) { ulLastError = LogOpen() || WriteData("\n*LOG_START*-%s", pszShortLogFileName); } return ulLastError; } //+------------------------------------------------------------------- // // Member: Log::WriteData(PCHAR, ...) // // Synopsis: Write a string to the Log file. // // Arguments: [pszFormat] - a printf-like format string // [...] - The data to print out. // // Returns: ERROR_SUCCESS if successful // Any error code from Log::SetStrData() or Log::LogData // or CTCLock::QueryError() // // Modifies: ulLastError // // History: ??-???-?? ???????? Created // 3-Jul-92 DeanE Modified to call WriteDataList // //-------------------------------------------------------------------- ULONG Log :: WriteData(PCHAR psz, ...) { CHAR szBuffer[CCH_MAX_CHARS]; va_list varArgs; #ifndef WIN16 CTCLock clMemLock (hMtxSerialize); // Serialize access to this object ULONG ulErr = clMemLock.QueryError(); if (ulErr != NO_ERROR) { return ulErr; } #endif szBuffer[0] = '\0'; szBuffer[CCH_MAX_CHARS-1] = '\0'; if ((psz != NULL) && (pfLog != NULL)) { // Print the caller's string to a buffer va_start(varArgs, psz); _vsnprintf(szBuffer, CCH_MAX_CHARS-1, psz, varArgs); va_end(varArgs); int iRet = fprintf(pfLog, "%s\n", szBuffer); if (iRet < 0) { OutputDebugStringA("CTLOG::Failed to log data string : "); OutputDebugStringA(szBuffer); OutputDebugStringA("\n"); } else { fflush(pfLog); } } return(ERROR_SUCCESS); } //+------------------------------------------------------------------- // // Member: Log::WriteData2(PCHAR) // // Synopsis: Write a string to the Log file. // // Arguments: [psz] - string to print out // // Returns: ERROR_SUCCESS if successful // Any error code from Log::SetStrData() or Log::LogData // or CTCLock::QueryError() // // Modifies: ulLastError // // History: 3-Jul-92 DeanE Modified to call WriteDataList // //-------------------------------------------------------------------- ULONG Log :: WriteData2(PCHAR psz) { #ifndef WIN16 CTCLock clMemLock (hMtxSerialize); // Serialize access to this object ULONG ulErr = clMemLock.QueryError(); if (ulErr != NO_ERROR) { return ulErr; } #endif if ((psz != NULL) && (pfLog != NULL)) { int iRet = fprintf(pfLog, "%s", psz); if (iRet < 0) { OutputDebugStringA("CTLOG::Failed to log data string : "); OutputDebugStringA(psz); OutputDebugStringA("\n"); } else { fflush(pfLog); } } return(ERROR_SUCCESS); } #ifndef WIN16 //+------------------------------------------------------------------- // // Member: Log::WriteData2(PWCHAR) // // Synopsis: Write a string to the Log file. // // Arguments: [pwsz] - string to print out // // Returns: ERROR_SUCCESS if successful // Any error code from Log::SetStrData() or Log::LogData // or CTCLock::QueryError() // // Modifies: ulLastError // // History: 3-Jul-92 DeanE Modified to call WriteDataList // //-------------------------------------------------------------------- ULONG Log :: WriteData2(PWCHAR pwsz) { #ifndef WIN16 CTCLock clMemLock (hMtxSerialize); // Serialize access to this object ULONG ulErr = clMemLock.QueryError(); if (ulErr != NO_ERROR) { return ulErr; } #endif if ((pwsz != NULL) && (pfLog != NULL)) { int iRet = fprintf(pfLog, "%ls", pwsz); if (iRet < 0) { OutputDebugStringA("CTLOG::Failed to log data string : "); // Note on Chicago this call will be stubbed out. OutputDebugStringW(pwsz); OutputDebugStringA("\n"); } else { fflush(pfLog); } } return(ERROR_SUCCESS); } #endif //+------------------------------------------------------------------- // // Member: Log::WriteVar // // Synopsis: Write a variation status to the Log file. // // Arguments: // // Returns: ERROR_SUCCESS if successful // Any error code from Log::SetStrData() or Log::LogData // or CTCLock::QueryError() // // Modifies: ulLastError // // History: ??-???-?? ???????? Created // 3-Jul-92 DeanE Modified to call WriteDataList // //-------------------------------------------------------------------- ULONG Log :: WriteVar(PCHAR pszVariation, USHORT usStatus, PCHAR pszStrFmt, ... ) { CHAR szBuffer[CCH_MAX_CHARS]; va_list varArgs; #ifndef WIN16 CTCLock clMemLock (hMtxSerialize); // Serialize access to this object ULONG ulErr = clMemLock.QueryError(); if (ulErr != NO_ERROR) { return ulErr; } #endif szBuffer[0] = '\0'; szBuffer[CCH_MAX_CHARS-1] = '\0'; if (pfLog != NULL) { int iRet; if (pszStrFmt != NULL) { // Print the caller's string to a buffer va_start(varArgs, pszStrFmt); _vsnprintf(szBuffer, CCH_MAX_CHARS-1, pszStrFmt, varArgs); va_end(varArgs); iRet = fprintf(pfLog, "%s: %s: %s\n", aStatus[usStatus], pszVariation, szBuffer); } else { iRet = fprintf(pfLog, "%s: %s\n", aStatus[usStatus], pszVariation); } if (iRet < 0) { OutputDebugStringA("CTLOG::Failed to log variation string : "); OutputDebugStringA(pszVariation); OutputDebugStringA("\n"); } else { fflush(pfLog); } fflush(pfLog); } return(ERROR_SUCCESS); } //+------------------------------------------------------------------- // // Member: Log ::InitLogging(VOID) // // Synopsis: Initialize the classes data members. // // Modifies: // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- void Log :: InitLogging(VOID) { pszLoggingDir = NULL; pszMachineName = NULL; pszObjectName = NULL; pszTestName = NULL; pszPath = NULL; pszStatus = NULL; pszVariation = NULL; pszLogFileName = NULL; pszShortLogFileName = NULL; fInfoSet = FALSE; pfLog = NULL; } //+------------------------------------------------------------------- // // Member: Log ::SetLogFile(VOID) // // Synopsis: Combine the logging file name components into a full path // file name. If this new file name is not the same as that // referenced by element pszLogFileName, switch to log data // into the file in this new name. // // Returns: ERROR_SUCCESS if successful, else // ERROR_INVALID_PARAMETER // ERROR_OUTOFMEMORY // ERROR_CANTOPEN // Any error from AddComponent, CheckDir, SetLogFileName, // or SetEventCount // // Modifies: // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- ULONG Log :: SetLogFile() { if (((pszLoggingDir != (PCHAR) NULL) && (strlen(pszLoggingDir) > _MAX_PATH)) || ((pszPath != NULL) && (strlen(pszPath) > _MAX_PATH))) { return ERROR_INVALID_PARAMETER; } PCHAR pszNewFileName = new char[_MAX_PATH + 1]; if(pszNewFileName == NULL) { return ERROR_OUTOFMEMORY; } // // No - // For each component of the new log file path, append it to the // root logging directory. Make sure only one back-slash exists // between each appended path component. // if(pszLoggingDir != NULL && *pszLoggingDir != NULLTERM) { strcpy(pszNewFileName, pszLoggingDir); } else { *pszNewFileName = NULLTERM; } ulLastError = AddComponent(pszNewFileName, pszPath) || AddComponent(pszNewFileName, pszTestName); // // The the new file name was successfully built, see if it the same as // the name of the currently open log file (if one is open). If is not // the same file name, close the open one (if open) and open the new // logging file. If the open is successful, save the name of the newly // opened file for the next time thru here. // // A later version of this method could be written to create a LRU queue // of some max number of open logging files. This version just keeps one // file open at a time, closing it only to switch to a different log file. // if(ulLastError == ERROR_SUCCESS) { strcat(pszNewFileName, (const char *)".Log"); ulLastError = SetLogFileName((const char *) pszNewFileName); if (ulLastError == ERROR_SUCCESS) { char szName[_MAX_FNAME]; GetBaseFilename(pszNewFileName, szName); ulLastError = SetShortLogFileName((const char *) szName); } if (ulLastError == ERROR_SUCCESS) { // Changed this from fopen() to work on chicago 16 bit pfLog = _fsopen(pszNewFileName, "a", _SH_DENYNO); if(pfLog == NULL) { ulLastError = ERROR_CANTOPEN; OutputDebugStringA("CTLOG::Failed to create log file"); OutputDebugStringA(pszNewFileName); OutputDebugStringA("\n"); } } } delete [] pszNewFileName; return ulLastError; } //+------------------------------------------------------------------- // // Member: Log ::AddComponent(PCHAR, PCHAR) // // Synopsis: Append a path component to the given string in szNewName. // Make sure there is one and only one '\' between each // component and no trailing '\' is present. // // Arguments: [szNewName] - Pointer to exist path (must not be NULL) // [szComponent] - New component to add // // Returns: ERROR_SUCCESS if successful // ERROR_INVALID_PARAMETER // // Modifies: // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- ULONG Log :: AddComponent(PCHAR szNewName, PCHAR szComponent) { PCHAR pch = NULL; // Path component provided? if (szComponent != NULL && *szComponent != NULLTERM) { int nLen = strlen((const char *)szComponent); // // Trim trailing and leading '\'s from the component to be appended, // then append the component to the file name. // pch = szComponent + nLen; while (pch > szComponent) { if (*pch == SLASH) { *pch = NULLTERM; pch--; } else { break; } } pch = szComponent; while (*pch == SLASH) { pch++; } // // Append one '\' to the file name then append the given component. // if (strlen((const char *)szNewName) + nLen + 1 < _MAX_PATH) { nLen = strlen((const char *)szNewName); if (nLen > 0) { // Add component separater szNewName[ nLen++] = SLASH; } strcpy(&szNewName[nLen], (const char *)pch); } else { ulLastError = ERROR_INVALID_PARAMETER; } } return(ulLastError); } //+------------------------------------------------------------------- // // Member: Log ::LogOpen(VOID) // // Synopsis: If we are not in the LogSvr and if logging remotly, try // to a START record to the server. If not, or if the attempt // fails, log the data locally. This may require opening the // local log file. // // Returns: ERROR_SUCCESS if successful // Results from Remote(), SetLogFile(), or OpenLogFile() // // Modifies: // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- ULONG Log :: LogOpen() { if(pfLog == NULL) { // // Something failed in the remote logging attempt, or this is our // first call to open the log file, so set up to log locally. // ulLastError = SetLogFile(); if(ulLastError != ERROR_SUCCESS) { return ulLastError; // Setup failed... Don't go any further. } } return ERROR_SUCCESS; } //+------------------------------------------------------------------- // // Member: Log ::SetInfo(const char *, const char *, // const char *, const char *) // // Synopsis: Set the logging information about the test being run. // User is set to pszTest OR logged on username OR MY_NAME in // that order of preference. // Machinename is set to computername OR MY_NAME in // that order of preference. // // // Arguments: [pszSrvr] - Name of logging server // [pszTest] - Name of the test being run // [pszSubPath] - Log file path qualifier // [pszObject] - Name of object logging the data // // Returns: USHORT - ERROR_SUCCESS (ERROR_SUCCESS) if successful. Otherwise, // the return value from SetTestName, // SetTester, SetPath, or SerObjectName. // // Modifies: // // History: ??-???-?? ???????? Created // 09-Feb-92 BryanT Added Win32 support // 01-Jul-92 Lizch Fixed bug where testername was getting // overwritten if machinename not set. // 30-Jul-92 SarahJ Fixed memory trashing bug - SetTester // & SetmachineName were trashing environment // variables // 30-Oct-92 SarahJ Removed all signs of pszTester - it // was only mis-used //-------------------------------------------------------------------- ULONG Log :: SetInfo(const char * pszSrvr, const char * pszTest, const char * pszSubPath, const char * pszObject) { ulLastError = SetTestName(pszTest) || SetPath(pszSubPath) || SetObjectName(pszObject); if(ulLastError == ERROR_SUCCESS && pszMachineName == NULL) { SetMachineName("MyMachineName"); if(pszMachineName == NULL) { // ulLastError has been set to error by SetMachineName. fprintf(stderr, "ERROR! machine name not set\n"); } } if(ulLastError == ERROR_SUCCESS) { fInfoSet = TRUE; // Note that info has been set } return ulLastError; } //+------------------------------------------------------------------- // // Member: Log ::OpenLogFile(VOID) // // Synopsis: Opens log file if not already open. // // Returns: ERROR_SUCCESS if successful, else ERROR_CANTOPEN // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- ULONG Log :: OpenLogFile(VOID) { ULONG ulErr = ERROR_SUCCESS; if(pfLog == NULL) { // changed this from fopen() to work on chicago 16 bit pfLog = _fsopen(pszLogFileName, "a", _SH_DENYNO); if(pfLog == NULL) { ulErr = ERROR_CANTOPEN; } } return(ulErr); } //+------------------------------------------------------------------- // // Member: Log ::CloseLogFile(VOID) // // Synopsis: If a logging file is open, write event count to the // beginning of the file and close the file // // Returns: - sets ulLastError if there is an error // // Modifies: // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- void Log :: CloseLogFile(VOID) { if(pfLog != NULL) { fclose(pfLog); pfLog = NULL; } // SetLogFileName((PCHAR) NULL); } //+------------------------------------------------------------------- // // // Member: Log ::LogPrintf(HANDLE, PCHAR, ...) // // Synopsis: This version has a max formared output of STRBUFSIZ // (see log.h). The only check I know how to make is to // strlen the format string. It is not fool-proof but it's // better than nothing. The method allows a printf-like format // and args to be written to a file opened with 'open()'. // // Arguments: [nHandle] - Output File handle // [pszFmt] - Format string for output // [...] - Data to pass printf() // // Returns: ERROR_SUCCESS if successful // // Modifies: // // History: ??-???-?? ???????? Created // 16-Sep-92 SarahJ Changed this function to at most write // STDSTRBUFSIZ bytes. // // Note: I have assumed that LogPrintf does not print > 1K // and can find no use with more data. // If I am wrong then the code from SetStrData should be // copied here. // //-------------------------------------------------------------------- int Log :: LogPrintf(FILE *pf, PCHAR pszFmt, ...) { return ERROR_SUCCESS; } //+------------------------------------------------------------------- // // Member: Log ::NewString(PCHAR *, const char *) // // Synopsis: This method will delete the existing string if it exists, // and (if a new string is given) will create and return a // duplicate string. // The assumption, here, is that the original pointer was // properly initialized to NULL prior to calling this method // the firsttime for that original string. // // Arguments: [pszOrig] - The original string // [pszNewStr] - The new and improved string // // Returns: ERROR_SUCCESS if successful, else ERROR_OUTOFMEMORY. // // Modifies: // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- ULONG Log :: NewString(PCHAR *pszOrig, const char * pszNewStr) { DelString(pszOrig); // If a new string was given, duplicate it. if(pszNewStr != NULL) { *pszOrig = new char[strlen(pszNewStr) + 1]; if(*pszOrig == NULL) { ulLastError = ERROR_OUTOFMEMORY; } else { strcpy(*pszOrig, pszNewStr); } } return ulLastError; } //+------------------------------------------------------------------- // // Function: DelString(PCHAR *) // // Synopsis: Given a pointer to a string, delete the memory if necessary // and set the pointer to NULL // // Arguments: [pszOrig] Original string to delete // // Returns: // // History: ??-???-?? ???????? Created // //-------------------------------------------------------------------- VOID Log :: DelString(PCHAR *pszOrig) { if (*pszOrig != NULL) { delete *pszOrig; *pszOrig = NULL; } } //+------------------------------------------------------------------- // // Function: GetBaseFilename // // Synopsis: Return a filename with it's extension and path // stripped off // // Arguments: [pszFileWithExtension] -- The full filename // [pszBaseName] -- Where to put the base name // // Returns: // // History: 15-Apr-96 MikeW Created // 17-Oct-96 EricHans Fixed to return with path stripped // //-------------------------------------------------------------------- void Log :: GetBaseFilename(LPSTR pszFileWithExtension, LPSTR pszBaseName) { LPSTR pszStartOfExtension; LPSTR pszLastBackslash; UINT nCharsInBase; pszStartOfExtension = strrchr(pszFileWithExtension, '.'); // assume this is a fully qualified path or // just the filename itself pszLastBackslash = strrchr(pszFileWithExtension, '\\'); if (NULL == pszStartOfExtension) { // find the end of the string pszStartOfExtension = strchr(pszFileWithExtension, '\0'); } if (NULL == pszLastBackslash) { nCharsInBase = pszStartOfExtension - pszFileWithExtension; strncpy(pszBaseName, pszFileWithExtension, nCharsInBase); } else { if (pszLastBackslash > pszStartOfExtension) { // boundary condition: there's actually a dot in the path // find the end of the string instead pszStartOfExtension = strchr(pszFileWithExtension, '\0'); } nCharsInBase = (pszStartOfExtension - pszLastBackslash) - 1; strncpy(pszBaseName, pszLastBackslash + 1, nCharsInBase); } pszBaseName[nCharsInBase] = '\0'; }