/*
 * Module Name:  WSFSLIB.C
 *								
 * Library/DLL:  Common library functions for handling working set tuner files.
 *								
 *								
 * Description:							
 *
 * Library routines called by the working set tuner programs to open and
 * read working set tuner files.  These functions may be useful to ISVs, etc.,
 *
 *	This is an OS/2 2.x specific file
 *
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 *
 * Modification History:		
 *				
 *	03/26/90	- created			
 *						
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <time.h>
#include <wserror.h>
#include <wsdata.h>
#include <wsfslib.h>

#define MAXLINE 128


BOOL fWsIndicator = FALSE;

/*
 *	    Function declarations and prototypes.
 */


/*
 *			
 ***EP WsWSPOpen
 *					
 * Effects:							
 *								
 * Opens a WSP file, and reads and validates the file header.
 *
 * Returns:							
 *	
 *	Returns 0.  If an error is encountered, exits with ERROR via an
 *	indirect call through pfnExit.
 */

USHORT FAR PASCAL
WsWSPOpen( PSZ pszFileName, FILE **phFile, PFN pfnExit, wsphdr_t *pWspHdr,
		INT iExitCode, INT iOpenPrintCode )
{
	ULONG	rc = NO_ERROR;
	INT		iRet = 0;
	ULONG	cbRead = 0;
	size_t	stRead = 0;

	/* Open module's input WSP file. */

	if ((*phFile = fopen(pszFileName, "rb")) == NULL)
	{
		iRet = (*pfnExit)(iExitCode, iOpenPrintCode, MSG_FILE_OPEN, rc,
				pszFileName);
		return((USHORT)iRet);
	}



	/* Read WSP file header. */
	stRead = fread((PVOID) pWspHdr, (ULONG) sizeof(*pWspHdr),1, *phFile);
	if(!stRead)
	{
		iRet = (*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_OPEN, rc,
				pszFileName);
		return((USHORT)iRet);
	}


	/* Read module pathname (directly follows file header). */

#ifdef DEBUG
	printf("WspHdr (%s): ulTime 0x%lx, ulSnaps 0x%lx, OffBits 0x%lx\n",
//			szModPath, pWspHdr->wsphdr_ulTimeStamp,
			pszFileName, pWspHdr->wsphdr_ulTimeStamp, // mdg 4/98
			pWspHdr->wsphdr_ulSnaps, pWspHdr->wsphdr_ulOffBits);
#endif /* DEBUG */

	/* Validate the WSP file header. */
	if (_strcmpi(pWspHdr->wsphdr_chSignature, "WSP"))
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_BAD_HDR, (ULONG)-1, pszFileName);

	return(NO_ERROR);
}


/*
 *			
 ***EP WsTMIOpen
 *					
 * Effects:							
 *								
 * Opens a TMI file, and reads and validates the file header.
 *
 * Returns:							
 *	
 *	Returns the number of records in the TMI file.  If an error is
 *	encountered, exits with ERROR via an indirect call through pfnExit.
 */

ULONG FAR PASCAL
WsTMIOpen( PSZ pszFileName, FILE **phFile, PFN pfnExit, USHORT usId, PCHAR pch)
{
	//ULONG	ulTmp;
	ULONG	rc = NO_ERROR;
	ULONG	cbRead = 0;
	ULONG	cFxns = 0;
	CHAR	szLineTMI[MAXLINE];	// Line from TMI file
	CHAR	szTDFID[8];		// TDF Identifier string
	ULONG	ulTDFID = 0;		// TDF Identifier

	/* Open TMI file (contains function names, etc., in ASCII). */

	if ((*phFile = fopen(pszFileName, "rt")) == NULL)
	{
		(*pfnExit)(NOEXIT, PRINT_MSG, MSG_FILE_OPEN, rc,
			pszFileName);
		return(MSG_FILE_OPEN);
	}

	/* Validate TMI file. */
	if (fgets(szLineTMI, MAXLINE, *phFile) == NULL){
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
				pszFileName);
	}
							// # fxns
	if (fgets(szLineTMI, MAXLINE, *phFile) == NULL){
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
				pszFileName);
	}
	szLineTMI[strlen(szLineTMI) - 1] = '\0';
	if (sscanf(szLineTMI,"/* Total Symbols= %u */", &cFxns) != 1){
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
				pszFileName);
	}
							// MODNAME
	if (fgets(szLineTMI, MAXLINE, *phFile) == NULL)
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
				pszFileName);
							// MAJOR
	if (fgets(szLineTMI, MAXLINE, *phFile) == NULL)
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
				pszFileName);
							// TDFID
	if (fgets(szLineTMI, MAXLINE, *phFile) == NULL)
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
				pszFileName);
	if (sscanf(szLineTMI, "TDFID   = %s", szTDFID) != 1)
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
				pszFileName);
	ulTDFID = strtoul(szTDFID, (char **) 0, 0);

	/* Check identifier field */

	if (ulTDFID != (ULONG) usId)
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_BAD_HDR, (ULONG)-1,
				pszFileName);

	return(cFxns);
}


/*
 *			
 ***EP WsTMIReadRec
 *					
 * Effects:							
 *								
 * Reads a record from a TMI file, including the variable length function
 * name.
 *
 * Returns:							
 *	
 *	Function size, in bytes, from this record.  If an error is
 *	encountered, exits with ERROR via an indirect call through pfnExit.
 */

ULONG FAR PASCAL
WsTMIReadRec( PSZ *ppszFxnName, PULONG pulFxnIndex, PULONG pulFxnAddr,
			  FILE *hFile, PFN pfnExit, PCHAR pch)
{
	ULONG	rc;
	ULONG	cbFxn;
	UINT	uiFxnAddrObj;	// object portion of function address
	ULONG	cbFxnName;		// size in bytes of function name
	// Read in function name, etc.

	rc = fscanf(hFile, "%ld %x:%lx 0x%lx %ul",  // mdg 98/4
				pulFxnIndex, &uiFxnAddrObj, pulFxnAddr, &cbFxn,
				&cbFxnName);

	if (rc != 5)
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc, "TMI file");

   *ppszFxnName = malloc( 1 + cbFxnName );   // Allocate space for function name
   if (*ppszFxnName == NULL)  // Abort if no mem
		(*pfnExit)(ERROR, PRINT_MSG, MSG_NO_MEM, 1 + cbFxnName, "TMI file");
   rc = fgetc( hFile ); // Skip leading blank space
   fgets( *ppszFxnName, cbFxnName + 1, hFile );
   rc = fgetc( hFile );
	if (rc != '\n' || strlen( *ppszFxnName ) != cbFxnName)
		(*pfnExit)(ERROR, PRINT_MSG, MSG_FILE_READ, rc, "TMI file");

   return(cbFxn);
}

LPVOID APIENTRY AllocAndLockMem(DWORD cbMem, HGLOBAL *hMem)
{

	//
	// Changed to GHND from GMEM_MOVABLE
	//
	*hMem = GlobalAlloc(GHND, cbMem);

	if(!*hMem) {
		return(NULL);
	}

	return(GlobalLock(*hMem));
}

BOOL APIENTRY  UnlockAndFreeMem(HGLOBAL hMem)
{
	BOOL fRet;

	fRet = GlobalUnlock(hMem);
	if (fRet) {
		return(fRet);
	}

	if (!GlobalFree(hMem)) {
		return(FALSE);
	}

	return(TRUE);

}

void
ConvertAppToOem( unsigned argc, char* argv[] )
/*++

Routine Description:

    Converts the command line from ANSI to OEM, and force the app
    to use OEM APIs

Arguments:

    argc - Standard C argument count.

    argv - Standard C argument strings.

Return Value:

    None.

--*/

{
    unsigned i;

    for( i=0; i<argc; i++ ) {
        CharToOem( argv[i], argv[i] );
    }
    SetFileApisToOEM();
}


/*
 *			
 ***EP WsIndicator
 *					
 * Effects:							
 *								
 * Displays a progress indicator on the console. Doesn't use stdout which may be
 * redirected.
 *					
 * Parameters:							
 *								
 * eFunc             Description                         nVal
 * WSINDF_NEW,       Start new indicator                 Value of 100% limit
 * WSINDF_PROGRESS,  Set progress of current indicator   Value of progress toward limit
 * WSINDF_FINISH     Mark indicator as finished          -ignored-
 * -invalid-         Do nothing
 *
 * In all valid cases, pszLabel sets string to display before indicator. If NULL,
 * uses last set string.
 *
 * Returns:							
 *	
 *	Function size, in bytes, from this record.  If an error is
 *	encountered, exits with ERROR via an indirect call through pfnExit.
 */

VOID FAR PASCAL
WsProgress( WsIndicator_e eFunc, const char *pszLbl, unsigned long nVal )
{
   static unsigned long
                     nLimit = 0, nCurrent = 0;
   static const char *
                     pszLabel = "";
   static unsigned   nLabelLen = 0;
   static char       bStarted = FALSE;
   static unsigned   nLastLen = 0;
   static HANDLE     hConsole = NULL;
   DWORD             pnChars;

   switch (eFunc)
   {
   case WSINDF_NEW:
      if (bStarted)
         WsIndicator( WSINDF_FINISH, NULL, 0 );
      bStarted = TRUE;
      nLimit = nVal;
      nCurrent = 0;
      nLastLen = ~0; // Force redraw
      WsIndicator ( WSINDF_PROGRESS, pszLbl, 0 );
      break;

   case WSINDF_PROGRESS:
      if (!bStarted)
         break;
      if (pszLbl != NULL)
      {
         pszLabel = pszLbl;
         nLabelLen = strlen( pszLabel );
      }
      if (nVal > nCurrent) // Compare to current progress (ignore reverses)
         if (nVal <= nLimit)
            nCurrent = nVal;
         else
            nCurrent = nLimit;
      {                    // Calculate an indicator string and print it
         unsigned nLen = (unsigned) ((40.1 * (double)nCurrent) / nLimit);
         char *   pszBuf;

         if (nLastLen == nLen)   // Optimization - Don't redraw if result would be the same
         {
            if (pszLbl == NULL)
               break;
         }
         else
            nLastLen = nLen;
         if (hConsole == NULL)
         {
            hConsole = CreateFile( "CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
            if (hConsole == NULL)   // Couldn't get the console for some reason?
               break;
         }
         WriteConsole( hConsole, "\r", 1, &pnChars, NULL );
         WriteConsole( hConsole, pszLabel, nLabelLen, &pnChars, NULL );
         WriteConsole( hConsole, " ", 1, &pnChars, NULL );
         pszBuf = malloc( nLen + 1 );
         if (pszBuf == NULL)  // No memory? Oh, well...
            break;
         memset( pszBuf, '-', nLen );
         pszBuf[nLen] = '\0';
         WriteConsole( hConsole, pszBuf, nLen, &pnChars, NULL );
         free( pszBuf);
      }
      break;

   case WSINDF_FINISH:
      if (!bStarted)
         break;
      WsIndicator( WSINDF_PROGRESS, pszLbl, nLimit );
      if (hConsole != NULL)
      {
         WriteConsole( hConsole, "\n", 1, &pnChars, NULL );
         CloseHandle( hConsole );
         hConsole = NULL;
      }
      bStarted = FALSE;
   }
}