/*****************************************************************************
*																			 *
*  HCMISC.H 																 *
*																			 *
*  Copyright (C) Microsoft Corporation 1990.								 *
*  All Rights reserved. 													 *
*																			 *
******************************************************************************
*																			 *
*  Module Intent															 *
*																			 *
*  Miscellaneous functions for HC including string manipulation and 		 *
*  memory allocation.														 *
*																			 *
******************************************************************************
*																			 *
*  Testing Notes															 *
*																			 *
******************************************************************************
*																			 *
*  Current Owner:  JohnSc													 *
*																			 *
******************************************************************************
*																			 *
*  Released by Development:  00/00/00										 *
*																			 *
*****************************************************************************/

/*****************************************************************************
*
*  Revision History:  Created 00/00/00 by NeelMah
*
*  11/21/90  JohnSc Added this comment header, fixed VSkipTrailing bug (tabs)
*
*****************************************************************************/

#include "stdafx.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/*****************************************************************************
*																			 *
*								Defines 									 *
*																			 *
*****************************************************************************/

#define cbSzBlockSize	32000	// string block size

// DOS int 21 AX error codes

#define wHunkyDory			  0x00
#define wInvalidFunctionCode  0x01
#define wFileNotFound		  0x02
#define wPathNotFound		  0x03
#define wTooManyOpenFiles	  0x04
#define wAccessDenied		  0x05
#define wInvalidHandle		  0x06
#define wInvalidAccessCode	  0x0c

/*****************************************************************************
*																			 *
*							 Static Variables								 *
*																			 *
*****************************************************************************/

static WORD cbSzBlockUsed = 0;	// no. of bytes already used.
static PSTR szBlock = NULL;    // current string block.

static void STDCALL WExtendedError(LPWORD perr, LPBYTE pclass, LPBYTE paction, LPBYTE plocus);

/*-----------------------------------------------------------------------------
*	SzParseList( szList )
*
*	Description:
*	   This function may be used to parse a list of strings separated
*	by semicolons and surrounded by whitespace.  Note that whitespace
*	does not delimit the strings in the list.
*
*	Arguments:
*	   szString -- List of strings to be parsed.  This should only be
*				   passed for the first string to be parsed from the
*				   list; successive strings are parsed by passing NULL.
*
*	Returns;
*	  The next string in the list.
*
*	+++
*
*	Notes:
*	  This process will destroy the original list of strings.  Also,
*	this function makes use of the library function strtok, which
*	contains the static variable of the list being parsed.	Thus,
*	you must not attempt to parse a different list using this
*	function or strtok while in the middle of parsing a list.
*-----------------------------------------------------------------------------*/

PSTR STDCALL SzParseList(PSTR pszList)
{
	pszList = SzTrimSz(StrToken(pszList, ';'));

	// If string contains only whitespace, go on to next string

	while (pszList != NULL && *pszList == '\0')
		pszList = SzTrimSz(StrToken(NULL, ';'));

	return pszList;
}


/*-----------------------------------------------------------------------------
*	SzGetKeySz(szStr, szKey, icbKeySize, piCount )
*
*	Description:
*		This function extracts a string from a list.  While it used
*	to be used to extract keywords, it is currently only used in
*	parsebld.c to extract build tags.
*
*	Arguments:
*
*	Returns;
*	  returns TRUE if successful else FALSE.
*-----------------------------------------------------------------------------*/

PSTR STDCALL SzGetKeySz(PSTR pszStr, PSTR szKey, int icbKeySize, int* piCount)
{
	PSTR szTemp;

	*piCount = 0;
	pszStr	= FirstNonSpace(pszStr, options.fDBCS);
	szTemp = szKey;

	while (*pszStr != '\0' && *pszStr != ';') {
		if (*piCount < icbKeySize)
			*szKey++ = *pszStr++;
		else
			pszStr++;
		(*piCount)++;
	}
	*szKey = '\0';
	RemoveTrailingSpaces(szTemp);
	if (*pszStr)
		pszStr++;
	return pszStr;
}

/*-----------------------------------------------------------------------------
*	SzGetExtSz(szStr)
*
*	Description:
*		This function returns the pointer to the extenstion if any for the
*	given file name string.
*
*	Arguments:
*	   1. szStr - string to which exten string to be appened.
*
*	Returns;
*	  returns the pointer to the extension of the given file name string if
*	any else returns null
*-----------------------------------------------------------------------------*/

PSTR STDCALL SzGetExtSz(PSTR szStr)
{
	PSTR  pszTmp;
	BOOL  fFound = FALSE;
	int   iT = 0;

	// skip trailing blanks

	RemoveTrailingSpaces(szStr);
	pszTmp = szStr + strlen(szStr) - 1;

	while ((pszTmp > szStr) && (iT < 4)) {
		if (*pszTmp == '.') {
			fFound = TRUE;
			break;
		}
		if (IsCharAlphaNumeric(*pszTmp)) {
			pszTmp--;
			iT++;
		}
		else
			break;
	}
	if (!fFound)
		return(NULL);
	return(pszTmp);
}

PSTR STDCALL SzSkipBlanksSz(PSTR sz)
{
	RemoveTrailingSpaces(sz);
	return FirstNonSpace(sz, options.fDBCS);
}

/*-----------------------------------------------------------------------------
*	QResizeTable()
*
*	Description:
*		This function acquires a table of initial size or resizes the
*	previously acquired table by increasing the size.
*
*	Arguments:
*	   1. qvTable  - initial table pointer.
*	   2. lcNew    - number of elements currently in table.
*	   3. qlcMac   - pointer to number of elements allocated for in table.
*	   4. cbEntry  - size of each entry in the table.
*	   5. cInit    - initial number of elements to put in table.
*	   6. cIncr    - if table is used fully, increment by these many entries.
*
*	Returns;
*	  returns the reallocated table pointer.
*-----------------------------------------------------------------------------*/

void* STDCALL QResizeTable(void* qvTable, int lcNew, int* qlcMac,
	int cbEntry, int cInit, int cIncr)
{
	if (*qlcMac <= lcNew) {
		if (*qlcMac == 0L) {
			qvTable = lcCalloc((int) cbEntry * cInit);
			*qlcMac = cInit;
		}
		else {
			*qlcMac += cIncr;
			ASSERT(qvTable != NULL);
			qvTable = lcReAlloc(qvTable, (int) cbEntry * (*qlcMac));
		}
	}
	ASSERT(qvTable != NULL);
	ASSERT(*qlcMac >= lcNew);
	return(qvTable);
}

/*-----------------------------------------------------------------------------
*	PSTR SzMacroFromSz( sz )
*
*	Description:
*	  This function returns a pointer to the macro string contained in
*	sz, if any.
*
*	Input
*	  sz:	Hotspot term possibly containing a macro.
*
*	Returns;
*	  Pointer to the macro string, or NULL if it isn't a macro.
*-----------------------------------------------------------------------------*/

PSTR STDCALL SzMacroFromSz(PSTR psz)
{
	if (*psz == '[' && CharAnsiUpper(*(psz + 1)) == 'S' && *(psz + 2) == ']')
		return(psz + 3);

	if (*psz == '$')
		return (psz + 1);

	if (*psz == '!')
		return (psz + 1);

	return( NULL );
}

/*-----------------------------------------------------------------------------
*	SzTranslateHash()
*
*	Description:
*	   This function is used to translate hash values via the alias table.
*	If the hash value is aliased, the function returns a pointer to the
*	context string corresponding to the translated hash value.
*
*	Arguments:
*	   1. qhash - pointer to id where the context id will be returned
*				  if the given id is found to be an alias id.
*
*	Returns;
*	  Alias string if one was found, otherwise NULL.
*
*-----------------------------------------------------------------------------*/

PSTR STDCALL SzTranslateHash(HASH* qhash )
{
	int  ialias;
	QALIAS qalias;

	if (pdrgAlias) {
		for (ialias = 0, qalias = (QALIAS) pdrgAlias->GetBasePtr();
				ialias < pdrgAlias->Count();
				ialias++, qalias++) {
			if (*qhash == qalias->hashAlias) {
				*qhash = qalias->hashCtx;
				return qalias->szCtx;
			}
		}
	}
	return NULL;
}

/***************************************************************************
 *
 -	Name:		  ErrorQch
 -
 *	Purpose:	  Displays standard WinHelp error message dialog based
 *				  the string passed.
 *
 *	Arguments:	  qch - string to display
 *
 *	Returns:	  Nothing.
 *
 *	Globals Used: hwndHelpCur	- main window handle
 *				  pchCaption - main help caption
 *
 *	Notes:		  Used by
 *
 ***************************************************************************/

void STDCALL ErrorQch(PCSTR qch)
{
	MessageBox(NULL, qch, GetStringResource(IDS_TITLE),
		MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL);
}

/***************************************************************************

	FUNCTION:	WExtendedError

	PURPOSE:	Get extended DOS error information

	PARAMETERS:
		perr
		pclass
		paction
		plocus

	RETURNS:

	COMMENTS:

	MODIFICATION DATES:
		01-Mar-1993 [ralphw]

***************************************************************************/

VOID STDCALL DOS3Call(void);

#pragma warning(disable:4035) // no return value
#pragma warning(disable:4704) // in-line assembler precludes global optimizations

static void STDCALL WExtendedError(LPWORD perr, LPBYTE pclass, LPBYTE paction,
	LPBYTE plocus)
{

// REVIEW: what is being returned?

}

int iCbTotalUncompressed;
int iCbTotalPhrase;
int iCbTotalJohn;

int iCbZeckBlockIn;
int iCbZeckBlockOut;

void FASTCALL AddZeckCounts( int iCbUncomp, int iCbComp)
{
	iCbZeckBlockIn	+= iCbUncomp;
	iCbZeckBlockOut += iCbComp;
}

void FASTCALL AddCharCounts(int iCbTotal, int iCbPhrase, int iCbJohn)
{
	iCbTotalUncompressed += iCbTotal;
	iCbTotalPhrase		 += iCbPhrase;
	iCbTotalJohn		 += iCbJohn;
}

const char txtIncreased[] = "INCREASED";
const char txtDecreased[] = "decreased";

void STDCALL ReportCharCounts(void)
{
	/*
	 * Note that we include zeck savings as part of the total savings count.
	 * The reason is that Zeck will get different savings results when
	 * run over plain text versus compressed text, so its savings must
	 * be calculated into the total savings count.
	 */

	if (options.fsCompress & COMPRESS_TEXT_HALL) {
		int savings = ((iCbTotalUncompressed - iCbTotalJohn) - cbHallOverhead)
			+ (iCbZeckBlockIn - iCbZeckBlockOut);
		wsprintf(szParentString, GetStringResource(IDS_COMPRESSION_SAVE),
			((options.fsCompress & COMPRESS_TEXT_ZECK) ? "Hall+Zeck" : "Hall"),
			((savings < 0) ? txtIncreased : txtDecreased),
			FormatNumber(abs(savings)));
		SendLogStringToParent();
	}
	else if (options.fsCompress & COMPRESS_TEXT_PHRASE) {
		int savings = (iCbTotalUncompressed - iCbTotalPhrase) - cbCompressedPhrase
			+ (iCbZeckBlockIn - iCbZeckBlockOut);
		wsprintf(szParentString, GetStringResource(IDS_COMPRESSION_SAVE),
			((options.fsCompress & COMPRESS_TEXT_ZECK) ? "Phrase+Zeck" : "Phrase"),
			((savings < 0) ? txtIncreased : txtDecreased),
			FormatNumber(abs(savings)));
		SendLogStringToParent();
	}
	else if (options.fsCompress & COMPRESS_TEXT_ZECK &&
			!(options.fsCompress & COMPRESS_TEXT_PHRASE) &&
			!(options.fsCompress & COMPRESS_TEXT_HALL)) {
		int savings = (iCbZeckBlockIn - iCbZeckBlockOut);
		wsprintf(szParentString, GetStringResource(IDS_COMPRESSION_SAVE),
			"Zeck",
			((savings < 0) ? txtIncreased : txtDecreased),
			FormatNumber(abs(savings)));
		SendLogStringToParent();
	}
}

#ifndef _INC_CTYPE
#include <ctype.h>
#endif

void STDCALL RemoveTrailingSpaces(PSTR pszString)
{
	if (!options.fDBCS) {
		PSTR psz = pszString + strlen(pszString) - 1;

		while (IsSpace(*psz)) {
			if (--psz <= pszString) {
				*pszString = '\0';
				return;
			}
		}
		psz[1] = '\0';
	}
	else {

		/*
		 * Removing trailing spaces in DBCS requires stepping through
		 * from the beginning of the string since we can't know if a
		 * trailing space is really a space or the second byte of a lead
		 * byte.
		 */
		PSTR psz = pszString + strlen(pszString) - 1;
		while (IsSpace(*psz) && psz > pszString + 2 &&
				!IsDBCSLeadByte(psz[-1])) {
			if (--psz <= pszString) {
				*pszString = '\0';
				return;
			}
		}
		psz[1] = '\0';
	}
}

/*-----------------------------------------------------------------------------
*	SzTrimSz( sz )
*
*	Description:
*		This function removes whitespaces (blank, tab, or newline) from
*	the beginning and ending of the string sz.
*
*	Arguments:
*		sz -- string to be trimmed of whitespace.
*
*	Returns:
*	  returns the pointer to the trimmed string.
*
*	+++
*
*	Notes:
*	  This function changes the original string.
*-----------------------------------------------------------------------------*/

PSTR STDCALL SzTrimSz(PSTR pszOrg)
{
	if (!pszOrg)
		return NULL;

	// Skip over leading whitespace

	if (options.fDBCS) {
		PSTR psz = pszOrg;
		while (!IsFirstByte(*psz) && IsSpace(*psz))
			psz++;
		if (psz != pszOrg)
			strcpy(pszOrg, psz);
	}

	else if (IsSpace(*pszOrg))
		strcpy(pszOrg, FirstNonSpace(pszOrg, FALSE));

	RemoveTrailingSpaces(pszOrg);

	return pszOrg;
}

BOOL STDCALL IsSpace(char ch)
{
	if (options.fDBCS)
		return (!IsFirstByte(ch) && (ch == ' ' || ch == '\t'));
	else
		return ((ch == ' ' || ch == '\t'));
}

/***************************************************************************

	FUNCTION:	StrToken

	PURPOSE:	DBCS-enabed variant of strtok

	PARAMETERS:
		pszList
		chDelimiter

	RETURNS:

	COMMENTS:
		You can NOT specify a DBCS character to look for

	MODIFICATION DATES:
		06-Jan-1995 [ralphw]

***************************************************************************/

PSTR STDCALL StrToken(PSTR pszList, char chDelimiter)
{
	static PSTR pszSavedList = NULL;
	PSTR psz;

	if (pszList) {
		pszSavedList = pszList;

		// On the first call, remove any leading token matches

		while (*pszSavedList == chDelimiter)
			pszSavedList++;
	}

	if (options.fDBCS) {
		psz = pszSavedList;
		while (*psz && *psz != chDelimiter) {
			psz = CharNext(psz);
		}
		if (!*psz)
			psz = NULL;
	}
	else {
		psz = strchr(pszSavedList, chDelimiter);
	}

	if (!psz) {
		if (!*pszSavedList)
			return NULL;
		else {
			PSTR pszReturn = pszSavedList;
			pszSavedList = pszSavedList + strlen(pszSavedList);
			return pszReturn;
		}
	}
	*psz++ = '\0';
	PSTR pszReturn = pszSavedList;
	pszSavedList = psz;
	return pszReturn;
}

/***************************************************************************

	FUNCTION:	StrToken

	PURPOSE:	DBCS-enabed variant of strtok

	PARAMETERS:
		pszList
		chDelimiter

	RETURNS:

	COMMENTS:
		You can NOT specify a DBCS character to look for

	MODIFICATION DATES:
		06-Jan-1995 [ralphw]

***************************************************************************/

PSTR STDCALL StrToken(PSTR pszList, PCSTR pszDelimeters)
{
	static PSTR pszSavedList = NULL;
	PSTR psz, pszTokens;

	if (pszList) {
		pszSavedList = pszList;

		// On the first call, remove any leading token matches

		for (psz = (PSTR) pszDelimeters; *psz; psz++) {
			if (*psz == *pszSavedList) {
				pszSavedList++;
				psz = (PSTR) pszDelimeters - 1;
			}
		}
	}

	if (options.fDBCS) {
		psz = pszSavedList;

		while (*psz) {
			for (pszTokens = (PSTR) pszDelimeters; pszTokens; pszTokens++) {
				if (*pszTokens == *psz)
					break;
			}
			if (*pszTokens == *psz)
				break;
			psz = CharNext(psz);
		}
		if (!*psz)
			psz = NULL;
	}
	else {
		psz = strpbrk(pszSavedList, pszDelimeters);
	}

	if (!psz) {
		if (!*pszSavedList)
			return NULL;
		else {
			PSTR pszReturn = pszSavedList;
			pszSavedList = pszSavedList + strlen(pszSavedList);
			return pszReturn;
		}
	}
	*psz++ = '\0';
	PSTR pszReturn = pszSavedList;
	pszSavedList = psz;
	return pszReturn;
}