/*++

    Copyright (c) 1994  Microsoft Corporation

    Module  Name :
        pudebug.c

    Abstract:

        This module defines functions required for
         Debugging and logging messages for a dynamic program.

    Author:
         Murali R. Krishnan ( MuraliK )    10-Sept-1994
         Modified to be moved to common dll in 22-Dec-1994.
   
    Revisions:
         MuraliK  16-May-1995  Code to load and save debug flags from registry
         MuraliK  16-Nov-1995  Remove DbgPrint (undoc api)
--*/
                                                     
                                              
/************************************************************
 * Include Headers
 ************************************************************/

# include <nt.h>
# include <ntrtl.h>
# include <nturtl.h>
# include <windows.h>
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <string.h>

# include "pudebug.h"


/*************************************************************
 * Global Variables and Default Values
 *************************************************************/

# define MAX_PRINTF_OUTPUT  ( 1024)

# define DEFAULT_DEBUG_FLAGS_VALUE  ( 0)
# define DEBUG_FLAGS_REGISTRY_LOCATION_W   L"DebugFlags"


/*************************************************************
 *   Functions 
 *************************************************************/

LPDEBUG_PRINTS
  PuCreateDebugPrintsObject( 
    IN char *               pszPrintLabel,
    IN DWORD                dwOutputFlags)
/*++
   This function creates a new DEBUG_PRINTS object for the required 
     program.

   Arguments:
      pszPrintLabel     pointer to null-terminated string containing
                         the label for program's debugging output
      dwOutputFlags     DWORD containing the output flags to be used.

   Returns:
       pointer to a new DEBUG_PRINTS object on success.
       Returns NULL on failure.
--*/     
{

   LPDEBUG_PRINTS   pDebugPrints;

   pDebugPrints = GlobalAlloc( GPTR, sizeof( DEBUG_PRINTS));

   if ( pDebugPrints != NULL) {
   
        if ( strlen( pszPrintLabel) < MAX_LABEL_LENGTH) {
     
            strcpy( pDebugPrints->m_rgchLabel, pszPrintLabel);
        } else {
            strncpy( pDebugPrints->m_rgchLabel, 
                     pszPrintLabel, MAX_LABEL_LENGTH - 1);
            pDebugPrints->m_rgchLabel[MAX_LABEL_LENGTH-1] = '\0';
                // terminate string
        }
    
        memset( pDebugPrints->m_rgchLogFilePath, 0, MAX_PATH);
        memset( pDebugPrints->m_rgchLogFileName, 0, MAX_PATH);
    
        pDebugPrints->m_LogFileHandle = INVALID_HANDLE_VALUE;
    
        pDebugPrints->m_dwOutputFlags = dwOutputFlags;
        pDebugPrints->m_StdErrHandle  = GetStdHandle( STD_ERROR_HANDLE);
        pDebugPrints->m_fInitialized = TRUE;
    }    


   return ( pDebugPrints);
} // PuCreateDebugPrintsObject()




LPDEBUG_PRINTS
  PuDeleteDebugPrintsObject( 
    IN OUT LPDEBUG_PRINTS pDebugPrints)
/*++
    This function cleans up the pDebugPrints object and 
      frees the allocated memory.

    Arguments:
       pDebugPrints     poitner to the DEBUG_PRINTS object.

    Returns:
        NULL  on  success.
        pDebugPrints() if the deallocation failed.

--*/
{
    if ( pDebugPrints != NULL) {

        DWORD dwError = PuCloseDbgPrintFile( pDebugPrints);
        
        if ( dwError != NO_ERROR) {
            
            SetLastError( dwError);
        } else {
            
            pDebugPrints = GlobalFree( pDebugPrints);
        }
    }

    return ( pDebugPrints);

} // PuDeleteDebugPrintsObject()




VOID
PuSetDbgOutputFlags(
    IN OUT LPDEBUG_PRINTS   pDebugPrints,
    IN DWORD                dwFlags)
{

    if ( pDebugPrints == NULL) {
        
        SetLastError( ERROR_INVALID_PARAMETER);
    } else {

        pDebugPrints->m_dwOutputFlags = dwFlags;
    }

    return;
} // PuSetDbgOutputFlags()



DWORD
PuGetDbgOutputFlags( 
    IN const LPDEBUG_PRINTS      pDebugPrints)
{
    return ( pDebugPrints != NULL) ? pDebugPrints->m_dwOutputFlags : 0;

} // PuGetDbgOutputFlags()


static DWORD
PuOpenDbgFileLocal( 
   IN OUT LPDEBUG_PRINTS pDebugPrints)
{

    if ( pDebugPrints->m_LogFileHandle != INVALID_HANDLE_VALUE) {
        
        //
        // Silently return as a file handle exists.
        //
        return ( NO_ERROR);
    }

    pDebugPrints->m_LogFileHandle = 
                      CreateFile( pDebugPrints->m_rgchLogFileName,
                                  GENERIC_WRITE,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                  NULL,
                                  OPEN_ALWAYS,
                                  FILE_ATTRIBUTE_NORMAL,
                                  NULL);
    
    if ( pDebugPrints->m_LogFileHandle == INVALID_HANDLE_VALUE) {

        CHAR  pchBuffer[1024];
        DWORD dwError = GetLastError();
       
        wsprintfA( pchBuffer,  
                  " Critical Error: Unable to Open File %s. Error = %d\n",
                  pDebugPrints->m_rgchLogFileName, dwError);
        OutputDebugString( pchBuffer); 

        return ( dwError);
    }

    return ( NO_ERROR);
} // PuOpenDbgFileLocal()





DWORD
PuOpenDbgPrintFile(
   IN OUT LPDEBUG_PRINTS      pDebugPrints,
   IN char *                  pszFileName,
   IN char *                  pszPathForFile)
/*++
  
  Opens a Debugging log file. This function can be called to set path
  and name of the debugging file.

  Arguments:
     pszFileName           pointer to null-terminated string containing 
                            the name of the file.

     pszPathForFile        pointer to null-terminated string containing the
                            path for the given file.
                           If NULL, then the old place where dbg files were
                           stored is used or if none, 
                           default windows directory will be used.

   Returns:
       Win32 error codes. NO_ERROR on success.

--*/
{

    if ( pszFileName == NULL || pDebugPrints == NULL) {
        
        return ( ERROR_INVALID_PARAMETER);
    }

    //
    //  Setup the Path information. if necessary.
    //
    
    if ( pszPathForFile != NULL) {
        
        // Path is being changed. 

        if ( strlen( pszPathForFile) < MAX_PATH) {
            
            strcpy( pDebugPrints->m_rgchLogFilePath, pszPathForFile);
        } else {
            
            return ( ERROR_INVALID_PARAMETER);
        }
    } else {
    
        if ( pDebugPrints->m_rgchLogFilePath[0] == '\0' &&  // no old path
            !GetWindowsDirectory( pDebugPrints->m_rgchLogFilePath, MAX_PATH)) {
         
            //
            //  Unable to get the windows default directory. Use current dir
            //
            
            strcpy( pDebugPrints->m_rgchLogFilePath, ".");
        }
    }
    
    //
    // Should need be, we need to create this directory for storing file
    //
    

    //
    // Form the complete Log File name and open the file.
    //
    if ( (strlen( pszFileName) + strlen( pDebugPrints->m_rgchLogFilePath))
         >= MAX_PATH) {
        
        return ( ERROR_NOT_ENOUGH_MEMORY);
    }

    //  form the complete path
    strcpy( pDebugPrints->m_rgchLogFileName, pDebugPrints->m_rgchLogFilePath);
    
    if ( pDebugPrints->m_rgchLogFileName[ strlen(pDebugPrints->m_rgchLogFileName) - 1]
        != '\\') {
        // Append a \ if necessary
        strcat( pDebugPrints->m_rgchLogFileName, "\\");
    };
    strcat( pDebugPrints->m_rgchLogFileName, pszFileName);

    return  PuOpenDbgFileLocal( pDebugPrints);    
    
} // PuOpenDbgPrintFile()




DWORD
PuReOpenDbgPrintFile(
    IN OUT LPDEBUG_PRINTS    pDebugPrints)
/*++
  
  This function closes any open log file and reopens a new copy. 
  If necessary. It makes a backup copy of the file.

--*/
{

    PuCloseDbgPrintFile( pDebugPrints);      // close any existing file.
    
    if ( pDebugPrints->m_dwOutputFlags & DbgOutputBackup) {
        
        // MakeBkupCopy();
        
        OutputDebugString( " Error: MakeBkupCopy() Not Yet Implemented\n");
    }

    return PuOpenDbgFileLocal( pDebugPrints);

} // PuReOpenDbgPrintFile()




DWORD
PuCloseDbgPrintFile( 
    IN OUT LPDEBUG_PRINTS    pDebugPrints)
{
    DWORD dwError = NO_ERROR;

    if ( pDebugPrints == NULL ) {
        dwError = ERROR_INVALID_PARAMETER;
    } else {
    
        if ( pDebugPrints->m_LogFileHandle != INVALID_HANDLE_VALUE) {
        
            FlushFileBuffers( pDebugPrints->m_LogFileHandle);
            
            if ( !CloseHandle( pDebugPrints->m_LogFileHandle)) {
        
                CHAR pchBuffer[1024];
 
                dwError = GetLastError();
            
                wsprintf( pchBuffer,
                          "CloseDbgPrintFile() : CloseHandle( %d) failed."
                          " Error = %d\n",
                          pDebugPrints->m_LogFileHandle,
                          dwError);
                OutputDebugString( pchBuffer);
            }
        
            pDebugPrints->m_LogFileHandle = INVALID_HANDLE_VALUE;
        }
    }

    return ( dwError);
} // DEBUG_PRINTS::CloseDbgPrintFile()


VOID
PuDbgPrint(
   IN OUT LPDEBUG_PRINTS      pDebugPrints,
   IN const char *            pszFilePath,
   IN int                     nLineNum,
   IN const char *            pszFormat,
   ...)
/*++

   Main function that examines the incoming message and prints out a header
    and the message.
     
--*/
{
   LPCSTR pszFileName = strrchr( pszFilePath, '\\');
   char pszOutput[ MAX_PRINTF_OUTPUT];
   LPCSTR pszMsg = "";
   va_list argsList;
   DWORD dwErr;

 
   //
   //  Skip the complete path name and retain file name in pszName
   //
 
   if ( pszFileName== NULL) {

      pszFileName = pszFilePath;  // if skipping \\ yields nothing use whole path.
   } 

# ifdef _PRINT_REASONS_INCLUDED_

  switch (pr) {

     case PrintError:
        pszMsg = "ERROR: ";
        break;

     case PrintWarning:
        pszMsg = "WARNING: ";
        break;

     case PrintCritical:
        pszMsg = "FATAL ERROR ";
        break;

     case PrintAssertion:
        pszMsg = "ASSERTION Failed ";
        break;

     case PrintLog:
        pfnPrintFunction = &DEBUG_PRINTS::DebugPrintNone;
     default:
        break;

  } /* switch */

# endif // _PRINT_REASONS_INClUDED_

    dwErr = GetLastError();

   // Format the message header

   wsprintf( pszOutput, "%s (%lu)[ %12s : %05d]", 
            pDebugPrints->m_rgchLabel, 
            GetCurrentThreadId(),
            pszFileName, nLineNum);

   // Format the incoming message

   va_start( argsList, pszFormat);
   vsprintf( pszOutput + strlen( pszOutput), pszFormat, argsList);
   va_end( argsList);

   //
   // Send the outputs to respective files.
   // 

   if ( pDebugPrints->m_dwOutputFlags & DbgOutputStderr) {
       
       DWORD nBytesWritten;

       ( VOID) WriteFile( pDebugPrints->m_StdErrHandle,
                          pszOutput,
                          strlen( pszOutput),
                          &nBytesWritten, 
                          NULL);
   }

   if ( pDebugPrints->m_dwOutputFlags & DbgOutputLogFile && 
        pDebugPrints->m_LogFileHandle != INVALID_HANDLE_VALUE) {

       DWORD nBytesWritten;

       //
       // Truncation of log files. Not yet implemented.

       ( VOID) WriteFile( pDebugPrints->m_LogFileHandle,
                          pszOutput,
                          strlen( pszOutput),
                          &nBytesWritten, 
                          NULL);
       
   }

   
   if ( pDebugPrints->m_dwOutputFlags & DbgOutputKdb) {
       
       OutputDebugString( pszOutput);
   }

   SetLastError( dwErr );


  return;

} // PuDbgPrint()



VOID
PuDbgAssertFailed( 
    IN OUT LPDEBUG_PRINTS         pDebugPrints,
    IN const char *               pszFilePath,
    IN int                        nLineNum,
    IN const char *               pszExpression,
    IN const char *               pszMessage)
/*++
    This function calls assertion failure and records assertion failure
     in log file.

--*/
{
    PuDbgPrint( pDebugPrints, pszFilePath, nLineNum,
                " Assertion (%s) Failed: %s\n",
                pszExpression, 
                pszMessage);

    DebugBreak();

    return;
} // PuDbgAssertFailed()




DWORD
PuLoadDebugFlagsFromReg(IN HKEY hkey, IN DWORD dwDefault)
/*++
  This function reads the debug flags assumed to be stored in 
   the location  "DebugFlags" under given key.
  If there is any error the default value is returned.
--*/
{
    DWORD err;
    DWORD dwDebug = dwDefault;
    DWORD  dwBuffer;
    DWORD  cbBuffer = sizeof(dwBuffer);
    DWORD  dwType;

    if( hkey != NULL )
    {
        err = RegQueryValueExW( hkey,
                               DEBUG_FLAGS_REGISTRY_LOCATION_W,
                               NULL,
                               &dwType,
                               (LPBYTE)&dwBuffer,
                               &cbBuffer );

        if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) )
        {
            dwDebug = dwBuffer;
        }
    }

    return dwDebug;
} // PuLoadDebugFlagsFromReg()





DWORD
PuSaveDebugFlagsInReg(IN HKEY hkey, IN DWORD dwDbg)
/*++
  Saves the debug flags in registry. On failure returns the error code for
   the operation that failed.

--*/
{
    DWORD err;

    if( hkey == NULL ) {
        
        err = ERROR_INVALID_PARAMETER;
    } else {

        err = RegSetValueExW(hkey,
                             DEBUG_FLAGS_REGISTRY_LOCATION_W,
                             0,
                             REG_DWORD,
                             (LPBYTE)&dwDbg,
                             sizeof(dwDbg) );
    }

    return (err);
} // PuSaveDebugFlagsInReg()


/****************************** End of File ******************************/




