/*****************************************************************************
 *                                                                            *
 *  BTKEY.C                                                                   *
 *                                                                            *
 *  Copyright (C) Microsoft Corporation 1989 - 1994.                          *
 *  All Rights reserved.                                                      *
 *                                                                            *
 ******************************************************************************
 *                                                                            *
 *  Module Intent                                                             *
 *                                                                            *
 *  Functions to deal with (i.e. size, compare) keys of all types.            *
 *                                                                            *
 ******************************************************************************
 *                                                                            *
 *  Current Owner:  BinhN                                                     *
 *                                                                            *
 *****************************************************************************/

/*****************************************************************************
 *
 *  Revision History:  Created 05/11/89 by JohnSc
 *
 *  08/21/90  JohnSc autodocified
 *   3/05/97     erinfox Change errors to HRESULTS
 *
 *****************************************************************************/

static char s_aszModule[] = __FILE__;	/* For error report */

#include <mvopsys.h>
#include <orkin.h>
#include <iterror.h>
#include <string.h>
#include <misc.h>
#include <wrapstor.h>
#include <mvdbcs.h>
#include <mvsearch.h>
#include "common.h"
#include <_mvutil.h>




// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

// When ligature tables are implemented again, take this out !!!!!!!!!!
//#define NOCHARTABLES_FIXME

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!




/*****************************************************************************
 *                                                                            *
 *                               Macros                                       *
 *                                                                            *
 *****************************************************************************/


#define StCopy(st1, st2)        (ST)QVCOPY((st1), (st2), (LONG)*(st2))

#define CbLenSt(st)             ((WORD)*(st))


/***************************************************************************
 *
 *	@doc	INTERNAL
 *
 *	@func	SHORT PASCAL FAR | WCmpKey |
 *		Compare two keys.
 *
 *	@parm	KEY | key1 |
 *		the UNCOMPRESSED keys to compare
 *
 *	@parm	KEY | key2 |
 *		the UNCOMPRESSED keys to compare
 *
 *	@parm	QBTHR | qbthr |
 *		qbthr->bth.rgchFormat[0]  - key type
 *		[qbthr->???               - other info ???]
 *
 *	@rdesc	-1 if key1 < key2; 0 if key1 == key2; 1 if key1 > key2
 *		args OUT:   [if comparing compressed keys, change state in qbthr->???]
 *
 *	@comm	Might be best to have this routine assume keys are expanded
 *		and do something else to compare keys in the scan routines.
 *		We're assuming fixed length keys are SZs.  Alternative
 *		would be to use a memcmp() function.
 *
 ***************************************************************************/

PUBLIC SHORT PASCAL FAR WCmpKey(KEY key1, KEY key2, QBTHR qbthr)
{
	SHORT   w;
	LONG	l;
	KT    kt = (KT)qbthr->bth.rgchFormat[ 0 ];
	LONG  l1, l2;


	switch (kt) {

#ifdef FULL_BTREE // {
		case KT_VSTI:
		{
			SZ sz1, sz2;

			WORD wl1,wl2;
			BYTE far *qbLigatures = NULL;
			wl1=(WORD)FoFromSz((SZ)key1).dwOffset;

			sz1 = (SZ)key1;
			ADVANCE_FO(sz1);
			key1 = (KEY)sz1;

			wl2=(WORD)FoFromSz((SZ)key2).dwOffset;

			sz2 = (SZ)key2;
			ADVANCE_FO(sz2);
			key2 = (KEY)sz2;

			if (qbthr->lrglpCharTab)
				qbLigatures=(BYTE FAR *)((LPCHARTAB)*qbthr->lrglpCharTab)->lpLigature;
			w=WCmpiSnn((SZ)key1, (SZ)key2, qbLigatures , wl1, wl2);
			break;
		}
		case KT_SZDEL:      // assume keys have been expanded for delta codeds
		case KT_SZDELMIN:
		case KT_SZ:

		case KT_SZMIN:
		case '1': case '2': case '3': case '4': case '5': // assume null term
		case '6': case '7': case '8': case '9': case 'a':
		case 'b': case 'c': case 'd': case 'e': case 'f':
			w = STRCMP((char *)key1, (char *)key2);
			break;

		case KT_SZI:
			w = WCmpiSz((SZ)key1, (SZ)key2,
			    (BYTE FAR *)((LPCHARTAB)*qbthr->lrglpCharTab)->lpLigature);
			break;

		case KT_SZISCAND:
			w = WCmpiScandSz((SZ)key1, (SZ)key2);
			break;

		case KT_SZMAP:

#ifdef NOCHARTABLES_FIXME
		w=STRCMP((SZ)key1, (SZ)key2);
#else
		if (PRIMARYLANGID(LANGIDFROMLCID(qbthr->bth.lcid)) == LANG_JAPANESE)
			w = StringJCompare (0L, (SZ)key1, STRLEN((char *)key1),
				(SZ)key2, STRLEN((char *)key2));
		else
			w = StrFntMappedLigatureComp((SZ)key1, (SZ)key2,
				qbthr->lrglpCharTab);

#endif
			break;
#endif // }

		case KT_EXTSORT:
			if (qbthr->pITSortKey == NULL ||
				FAILED(qbthr->pITSortKey->Compare((LPCVOID) key1,
												  (LPCVOID) key2,
													&l, NULL)))
			{
				l = 0;
				ITASSERT(FALSE);
			}

			// Reduce the long -/0/+ result to a word -/0/+.
			w = (SHORT) ((LOWORD(l) & 0x7fff) | ((LOWORD(l) >> 1) & 0x7fff) | HIWORD(l));
			break;

#ifdef FULL_BTREE // {
		case KT_ST:
		case KT_STMIN:
		case KT_STDEL:
		case KT_STDELMIN:
			w = WCmpSt((ST)key1, (ST)key2);
			break;
#endif // }

		case KT_LONG:
			l1 = *(LONG FAR *)key1;
			l2 = *(LONG FAR *)key2;
			if (l1 < l2)
			  w = -1;
			else if (l2 < l1)
			  w = 1;
			else
			  w = 0;
			break;
		}

	return w;
}

/***************************************************************************
 *
 *	@doc	INTERNAL
 *
 *	@func	SHORT PASCAL FAR | CbSizeKey |
 *		Return the key size (compressed or un-) in bytes 
 *
 *	@parm	KEY | key 
 *	@parm	QBTHR | qbthr
 *	@parm	BOOL | fCompressed |
 *		 TRUE to get the compressed size,
 *		FALSE to get the uncompressed size.
 *
 *	@rdesc	size of the key in bytes
 *
 *	@comm	It's impossible to tell how much suffix was discarded for
 *		the KT_*MIN key types.
 *
 ***************************************************************************/

PUBLIC SHORT PASCAL CbSizeKey(KEY key, QBTHR qbthr, BOOL  fCompressed)
{
	SHORT cb;
	DWORD dwKeySize;

	KT  kt = (KT)qbthr->bth.rgchFormat[ 0 ];


	switch(kt) {
#ifdef FULL_BTREE // {
		case KT_SZ:
		case KT_SZMIN:
		case KT_SZI:
		case KT_SZISCAND:
		case KT_SZMAP:
			cb = STRLEN((char *)key) + 1;
			break;

		case KT_VSTI:
		 	cb = (SHORT)LenSzFo((LPBYTE)key) + (SHORT)FoFromSz((LPBYTE)key).dwOffset;
			break;
		
		case KT_SZDEL:
		case KT_SZDELMIN:
			if (fCompressed)
				cb = 1 + STRLEN((char *)key + 1) + 1;
			else 
				cb = *(QB)key + STRLEN((char *)key + 1) + 1;
			break;

		case KT_ST:
		case KT_STMIN:
		case KT_STI:
			cb = CbLenSt((ST)key) + 1/* ? */;
			break;

		case KT_STDEL:
		case KT_STDELMIN:
			if (fCompressed) 
				cb = 1 + CbLenSt((ST)key + 1);
			else
				cb = *(QB)key + CbLenSt((ST)key + 1) + 1;
			break;
#endif // }
		case KT_EXTSORT:
			if (qbthr->pITSortKey == NULL ||
				FAILED(qbthr->pITSortKey->GetSize((LPCVOID) key, &dwKeySize)))
			{
				dwKeySize = 0;
				ITASSERT(FALSE);
			}

			// Key size can't exceed 32K - 1.  Such a key never should have been
			// allowed in to begin with.  We'll assert for the debug version.
			// We'll use 32K - 1 for the size for now, but there's a pretty
			// good chance a GP fault will occur if the sort object
			// ever has to visit parts of the key at 32K and beyond!
			if (dwKeySize <= 0x7fff)
				cb = (SHORT) dwKeySize;
			else
			{
				cb = 0x7fff;
				ITASSERT(FALSE);
			}
			break;

		case KT_LONG:
			cb = sizeof(LONG);
			break;

		case '1': case '2': case '3': case '4': case '5':
		case '6': case '7': case '8': case '9':
			cb = kt - '0';
			break;
			
		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
			cb = kt - 'a' + 10;
			break;
	}

	return cb;
}

/***************************************************************************
 *
 *	@doc	INTERNAL
 *
 *	@func	BOOL FAR PASCAL | FIsPrefix |
 *		Determines whether string key1 is a prefix of key2.
 *
 *	@parm	HBT | hbt |
 *		handle to a btree with string keys
 *
 *	@parm	KEY | key1 |
 *		Uncompressed key
 *
 *	@parm	KEY | key2 |
 *		Uncompressed key
 *
 *	@rdesc	TRUE if the string key1 is a prefix of the string key2
 *		FALSE if it isn't or if hbt doesn't contain string keys
 *
 *	@comm	Bugs: Doesn't work on STs yet
 *
 *		Method: temporarily shortens the second string so it can
 *		compare prefixes
 *
 ***************************************************************************/

PUBLIC BOOL FAR PASCAL FIsPrefix(HBT hbt, KEY key1, KEY key2)
{
	QBTHR qbthr;
	KT    kt;
	BOOL  f;
    ERRB  errb;
	HRESULT	hr;

#ifdef FULL_BTREE 
	SHORT   cb1, cb2;
	unsigned char c;
#endif


	if ((qbthr = (QBTHR) _GLOBALLOCK(hbt)) == NULL) 
	{
	    SetErrCode (&errb, E_INVALIDARG);
        return(FALSE);
	}
	assert(qbthr != NULL);

	kt = (KT)qbthr->bth.rgchFormat[ 0 ];

	switch(kt) {
#ifdef FULL_BTREE // {
		case KT_SZ:
		case KT_SZMIN:
		case KT_SZI:
		case KT_SZISCAND:
		case KT_SZDEL:
		case KT_SZDELMIN:
		case KT_SZMAP:
			/* both keys assumed to have been decompressed */
			cb1 = STRLEN((char *)key1);
			cb2 = STRLEN((char *)key2);
			break;

		case KT_ST:
		case KT_STMIN:
		case KT_STI:
		case KT_STDEL:
		case KT_STDELMIN:
			/* STs unimplemented */
			SetErrCode (&errb, E_NOTSUPPORTED);
			_GLOBALUNLOCK(hbt);
			return FALSE;
			break;
#endif // }
		case KT_EXTSORT:
			if (qbthr->pITSortKey == NULL ||
				FAILED(hr = qbthr->pITSortKey->IsRelated((LPCVOID) key1,
														 (LPCVOID) key2,
									(DWORD) IITSK_KEYRELATION_PREFIX, NULL)))
			{
				ITASSERT(FALSE);
				f = FALSE;
			}
			else
				f = (GetScode(hr) == S_OK);

			_GLOBALUNLOCK(hbt);
			return (f);
			break;

		case KT_LONG:
		case '1': case '2': case '3': case '4': case '5':
		case '6': case '7': case '8': case '9':
		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
		default:
			/* prefix doesn't make sense */
			SetErrCode (&errb, E_NOTSUPPORTED);
			_GLOBALUNLOCK(hbt);
			return FALSE;
			break;
	}
#ifdef FULL_BTREE // {

	// The length check and truncation method works as long as
	// ligatures aren't involved.  We won't worry about this for
	// IT 4.0 because the CharTab KTs won't be used.
	if (cb1 > cb2) {
		_GLOBALUNLOCK(hbt);
		return FALSE;
	}

	// Truncate longer string (key2).
	c = ((SZ)key2)[ cb1 ];
	((SZ)key2)[ cb1 ] = '\0';

	switch (kt)
	{
		case KT_SZ:
		case KT_SZMIN:
		case KT_SZDEL:
		case KT_SZDELMIN:
			f = STRCMP((char *)key1, (char *)key2) <=0;
			break;

		case KT_SZI:
			f = WCmpiSz((SZ)key1, (SZ)key2,
			    (BYTE FAR *) (*((LPCHARTAB FAR *)qbthr->lrglpCharTab))->lpLigature) <= 0;
			break;

		case KT_SZISCAND:
			f = WCmpiScandSz((SZ)key1, (SZ)key2) <= 0;
			break;

		case KT_SZMAP:

#ifdef NOCHARTABLES_FIXME
			
			f=(STRCMP((SZ)key1, (SZ)key2) <= 0);
#else
	        if (PRIMARYLANGID(LANGIDFROMLCID(qbthr->bth.lcid)) == LANG_JAPANESE)
    			f = (StringJCompare (0L, (SZ)key1, STRLEN((char *)key1),
    			    (SZ)key2, STRLEN((char *)key2)) <= 0);
	        else
    			f = (StrFntMappedLigatureComp((SZ)key1,
    				(SZ)key2, qbthr->lrglpCharTab) <= 0);

#endif
			break;
		default:
			assert(FALSE);
			break;
	}

	// Restore the longer string.
	((SZ)key2)[ cb1 ] = c;

	_GLOBALUNLOCK(hbt);
	return f;
#endif // }
}

/* EOF */