1167 lines
26 KiB
C
1167 lines
26 KiB
C
|
//============================================================================
|
||
|
//
|
||
|
// DBCS aware string routines...
|
||
|
//
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
//#if defined(UNIX) && !defined(UNICODE)
|
||
|
//#define UNICODE
|
||
|
//#endif
|
||
|
|
||
|
|
||
|
#include "ctlspriv.h"
|
||
|
|
||
|
#ifdef WINNT
|
||
|
#include <winnlsp.h> // Get private NORM_ flag for StrEqIntl()
|
||
|
#endif
|
||
|
|
||
|
// for those of us who don't ssync to nt's build headers
|
||
|
#ifndef NORM_STOP_ON_NULL
|
||
|
#define NORM_STOP_ON_NULL 0x10000000
|
||
|
#endif
|
||
|
|
||
|
// WARNING: all of these APIs do not setup DS, so you can not access
|
||
|
// any data in the default data seg of this DLL.
|
||
|
//
|
||
|
// do not create any global variables... talk to chrisg if you don't
|
||
|
// understand thid
|
||
|
|
||
|
#ifdef UNIX
|
||
|
|
||
|
#ifdef BIG_ENDIAN
|
||
|
#define READNATIVEWORD(x) MAKEWORD(*(char*)(x), *(char*)((char*)(x) + 1))
|
||
|
#else
|
||
|
#define READNATIVEWORD(x) MAKEWORD(*(char*)((char*)(x) + 1), *(char*)(x))
|
||
|
#endif
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define READNATIVEWORD(x) (*(UNALIGNED WORD *)x)
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
* StrEndN - Find the end of a string, but no more than n bytes
|
||
|
* Assumes lpStart points to start of null terminated string
|
||
|
* nBufSize is the maximum length
|
||
|
* returns ptr to just after the last byte to be included
|
||
|
*/
|
||
|
LPSTR lstrfns_StrEndNA(LPCSTR lpStart, int nBufSize)
|
||
|
{
|
||
|
LPCSTR lpEnd;
|
||
|
|
||
|
for (lpEnd = lpStart + nBufSize; *lpStart && OFFSETOF(lpStart) < OFFSETOF(lpEnd);
|
||
|
lpStart = AnsiNext(lpStart))
|
||
|
continue; /* just getting to the end of the string */
|
||
|
if (OFFSETOF(lpStart) > OFFSETOF(lpEnd))
|
||
|
{
|
||
|
/* We can only get here if the last byte before lpEnd was a lead byte
|
||
|
*/
|
||
|
lpStart -= 2;
|
||
|
}
|
||
|
return((LPSTR)lpStart);
|
||
|
}
|
||
|
|
||
|
LPWSTR lstrfns_StrEndNW(LPCWSTR lpStart, int nBufSize)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
LPCWSTR lpEnd;
|
||
|
|
||
|
for (lpEnd = lpStart + nBufSize; *lpStart && (lpStart < lpEnd);
|
||
|
lpStart++)
|
||
|
continue; /* just getting to the end of the string */
|
||
|
|
||
|
return((LPWSTR)lpStart);
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return NULL;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
// REVIEW WIN32 HACK - Convert to 32bit asm.
|
||
|
#ifndef WIN32
|
||
|
/*
|
||
|
* ReverseScan - Find last occurrence of a byte in a string
|
||
|
* Assumes lpSource points to first byte to check (end of the string)
|
||
|
* uLen is the number of bytes to check
|
||
|
* bMatch is the byte to match
|
||
|
* returns ptr to the last occurrence of ch in str, NULL if not found.
|
||
|
*/
|
||
|
LPSTR PASCAL ReverseScan(LPCSTR lpSource, UINT uLen, BYTE bMatch)
|
||
|
{
|
||
|
_asm
|
||
|
{
|
||
|
/* Load count */
|
||
|
mov cx,uLen
|
||
|
jcxz ReverseScanFail ; count is zero, return failure.
|
||
|
|
||
|
/* Load up es:di, ax */
|
||
|
les di,lpSource
|
||
|
mov al,bMatch
|
||
|
/* Set the direction flag based on bBackward
|
||
|
* Perform the search; return 0 if we reached the end of the string
|
||
|
* otherwise, return es:di+1
|
||
|
*/
|
||
|
std
|
||
|
repne scasb
|
||
|
jne ReverseScanFail ; check result of last compare.
|
||
|
|
||
|
inc di
|
||
|
mov dx,es
|
||
|
mov ax,di
|
||
|
jmp ReverseScanExit
|
||
|
|
||
|
ReverseScanFail:
|
||
|
xor ax,ax
|
||
|
xor dx,dx
|
||
|
|
||
|
/* clear the direction flag and return
|
||
|
*/
|
||
|
ReverseScanExit:
|
||
|
cld
|
||
|
}
|
||
|
if (0) return 0; // suppress warning, optimized out
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* ChrCmp - Case sensitive character comparison for DBCS
|
||
|
* Assumes w1, wMatch are characters to be compared
|
||
|
* Return FALSE if they match, TRUE if no match
|
||
|
*/
|
||
|
__inline BOOL ChrCmpA_inline(WORD w1, WORD wMatch)
|
||
|
{
|
||
|
/* Most of the time this won't match, so test it first for speed.
|
||
|
*/
|
||
|
if (LOBYTE(w1) == LOBYTE(wMatch))
|
||
|
{
|
||
|
if (IsDBCSLeadByte(LOBYTE(w1)))
|
||
|
{
|
||
|
return(w1 != wMatch);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL ChrCmpA(WORD w1, WORD wMatch)
|
||
|
{
|
||
|
return ChrCmpA_inline(w1, wMatch);
|
||
|
}
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
__inline BOOL ChrCmpW_inline(WCHAR w1, WCHAR wMatch)
|
||
|
{
|
||
|
return(!(w1 == wMatch));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
BOOL ChrCmpW(WCHAR w1, WCHAR wMatch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
return ChrCmpW_inline(w1, wMatch);
|
||
|
#else
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return FALSE;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* ChrCmpI - Case insensitive character comparison for DBCS
|
||
|
* Assumes w1, wMatch are characters to be compared;
|
||
|
* HIBYTE of wMatch is 0 if not a DBC
|
||
|
* Return FALSE if match, TRUE if not
|
||
|
*/
|
||
|
BOOL ChrCmpIA(WORD w1, WORD wMatch)
|
||
|
{
|
||
|
char sz1[3], sz2[3];
|
||
|
|
||
|
if (IsDBCSLeadByte(sz1[0] = LOBYTE(w1)))
|
||
|
{
|
||
|
sz1[1] = HIBYTE(w1);
|
||
|
sz1[2] = '\0';
|
||
|
}
|
||
|
else
|
||
|
sz1[1] = '\0';
|
||
|
|
||
|
#if defined(BIG_ENDIAN)
|
||
|
sz2[0] = LOBYTE(wMatch);
|
||
|
sz2[1] = HIBYTE(wMatch);
|
||
|
#else
|
||
|
*(WORD FAR *)sz2 = wMatch;
|
||
|
#endif
|
||
|
sz2[2] = '\0';
|
||
|
return lstrcmpiA(sz1, sz2);
|
||
|
}
|
||
|
|
||
|
BOOL ChrCmpIW(WCHAR w1, WCHAR wMatch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
WCHAR sz1[2], sz2[2];
|
||
|
|
||
|
sz1[0] = w1;
|
||
|
sz1[1] = TEXT('\0');
|
||
|
sz2[0] = wMatch;
|
||
|
sz2[1] = TEXT('\0');
|
||
|
|
||
|
return lstrcmpiW(sz1, sz2);
|
||
|
|
||
|
#else
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return FALSE;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
LPWSTR StrCpyW(LPWSTR psz1, LPCWSTR psz2)
|
||
|
{
|
||
|
LPWSTR psz = psz1;
|
||
|
do {
|
||
|
*psz1 = *psz2;
|
||
|
psz1++;
|
||
|
} while(*psz2++);
|
||
|
return psz;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPWSTR StrCpyNW(LPWSTR psz1, LPCWSTR psz2, int cchMax)
|
||
|
{
|
||
|
LPWSTR psz = psz1;
|
||
|
|
||
|
ASSERT(psz1);
|
||
|
ASSERT(psz2);
|
||
|
|
||
|
if (0 < cchMax)
|
||
|
{
|
||
|
// Leave room for the null terminator
|
||
|
while (0 < --cchMax)
|
||
|
{
|
||
|
if ( !(*psz1++ = *psz2++) )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (0 == cchMax)
|
||
|
*psz1 = '\0';
|
||
|
}
|
||
|
|
||
|
return psz;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* StrChr - Find first occurrence of character in string
|
||
|
* Assumes lpStart points to start of null terminated string
|
||
|
* wMatch is the character to match
|
||
|
* returns ptr to the first occurrence of ch in str, NULL if not found.
|
||
|
*/
|
||
|
LPSTR FAR PASCAL StrChrA(LPCSTR lpStart, WORD wMatch)
|
||
|
{
|
||
|
for ( ; *lpStart; lpStart = AnsiNext(lpStart))
|
||
|
{
|
||
|
if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
|
||
|
{
|
||
|
return((LPSTR)lpStart);
|
||
|
}
|
||
|
}
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
#ifdef ALIGNMENT_SCENARIO
|
||
|
|
||
|
LPWSTR StrChrSlowW(const UNALIGNED WCHAR *lpStart, WCHAR wMatch)
|
||
|
{
|
||
|
for ( ; *lpStart; lpStart++)
|
||
|
{
|
||
|
if (!ChrCmpW_inline(*lpStart, wMatch))
|
||
|
{
|
||
|
return((LPWSTR)lpStart);
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
LPWSTR FAR PASCAL StrChrW(LPCWSTR lpStart, WCHAR wMatch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
//
|
||
|
// BUGBUG raymondc
|
||
|
// Apparently, somebody is passing unaligned strings to StrChrW.
|
||
|
// Find out who and make them stop.
|
||
|
//
|
||
|
ASSERT(!((ULONG_PTR)lpStart & 1)); // Assert alignedness
|
||
|
|
||
|
#ifdef ALIGNMENT_SCENARIO
|
||
|
//
|
||
|
// Since unaligned strings arrive so rarely, put the slow
|
||
|
// version in a separate function so the common case stays
|
||
|
// fast. Believe it or not, we call StrChrW so often that
|
||
|
// it is now a performance-sensitive function!
|
||
|
//
|
||
|
if ((ULONG_PTR)lpStart & 1)
|
||
|
return StrChrSlowW(lpStart, wMatch);
|
||
|
#endif
|
||
|
|
||
|
for ( ; *lpStart; lpStart++)
|
||
|
{
|
||
|
if (!ChrCmpW_inline(*lpStart, wMatch))
|
||
|
{
|
||
|
return((LPWSTR)lpStart);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (NULL);
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return NULL;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* StrRChr - Find last occurrence of character in string
|
||
|
* Assumes lpStart points to start of string
|
||
|
* lpEnd points to end of string (NOT included in search)
|
||
|
* wMatch is the character to match
|
||
|
* returns ptr to the last occurrence of ch in str, NULL if not found.
|
||
|
*/
|
||
|
LPSTR FAR PASCAL StrRChrA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
|
||
|
{
|
||
|
LPCSTR lpFound = NULL;
|
||
|
|
||
|
if (!lpEnd)
|
||
|
lpEnd = lpStart + lstrlenA(lpStart);
|
||
|
|
||
|
for ( ; OFFSETOF(lpStart) < OFFSETOF(lpEnd); lpStart = AnsiNext(lpStart))
|
||
|
{
|
||
|
if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
|
||
|
lpFound = lpStart;
|
||
|
}
|
||
|
return ((LPSTR)lpFound);
|
||
|
}
|
||
|
|
||
|
LPWSTR FAR PASCAL StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
LPCWSTR lpFound = NULL;
|
||
|
|
||
|
if (!lpEnd)
|
||
|
lpEnd = lpStart + lstrlenW(lpStart);
|
||
|
|
||
|
for ( ; lpStart < lpEnd; lpStart++)
|
||
|
{
|
||
|
if (!ChrCmpW_inline(*lpStart, wMatch))
|
||
|
lpFound = lpStart;
|
||
|
}
|
||
|
return ((LPWSTR)lpFound);
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return NULL;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* StrRChrI - Find last occurrence of character in string, case insensitive
|
||
|
* Assumes lpStart points to start of string
|
||
|
* lpEnd points to end of string (NOT included in search)
|
||
|
* wMatch is the character to match
|
||
|
* returns ptr to the last occurrence of ch in str, NULL if not found.
|
||
|
*/
|
||
|
LPSTR FAR PASCAL StrRChrIA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
|
||
|
{
|
||
|
LPCSTR lpFound = NULL;
|
||
|
|
||
|
if (!lpEnd)
|
||
|
lpEnd = lpStart + lstrlenA(lpStart);
|
||
|
|
||
|
wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
|
||
|
|
||
|
for ( ; OFFSETOF(lpStart) < OFFSETOF(lpEnd); lpStart = AnsiNext(lpStart))
|
||
|
{
|
||
|
if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
|
||
|
lpFound = lpStart;
|
||
|
}
|
||
|
return ((LPSTR)lpFound);
|
||
|
}
|
||
|
|
||
|
LPWSTR FAR PASCAL StrRChrIW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
LPCWSTR lpFound = NULL;
|
||
|
|
||
|
if (!lpEnd)
|
||
|
lpEnd = lpStart + lstrlenW(lpStart);
|
||
|
|
||
|
for ( ; lpStart < lpEnd; lpStart++)
|
||
|
{
|
||
|
if (!ChrCmpIW(*lpStart, wMatch))
|
||
|
lpFound = lpStart;
|
||
|
}
|
||
|
return ((LPWSTR)lpFound);
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return NULL;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
// StrCSpn: return index to first char of lpStr that is present in lpSet.
|
||
|
// Includes the NUL in the comparison; if no lpSet chars are found, returns
|
||
|
// the index to the NUL in lpStr.
|
||
|
// Just like CRT strcspn.
|
||
|
//
|
||
|
int FAR PASCAL StrCSpnA(LPCSTR lpStr, LPCSTR lpSet)
|
||
|
{
|
||
|
// nature of the beast: O(lpStr*lpSet) work
|
||
|
LPCSTR lp = lpStr;
|
||
|
if (!lpStr || !lpSet)
|
||
|
return 0;
|
||
|
|
||
|
while (*lp)
|
||
|
{
|
||
|
if (StrChrA(lpSet, READNATIVEWORD(lp)))
|
||
|
return (int)(lp-lpStr);
|
||
|
lp = AnsiNext(lp);
|
||
|
}
|
||
|
|
||
|
return (int)(lp-lpStr); // ==lstrlen(lpStr)
|
||
|
}
|
||
|
|
||
|
int FAR PASCAL StrCSpnW(LPCWSTR lpStr, LPCWSTR lpSet)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
// nature of the beast: O(lpStr*lpSet) work
|
||
|
LPCWSTR lp = lpStr;
|
||
|
if (!lpStr || !lpSet)
|
||
|
return 0;
|
||
|
|
||
|
while (*lp)
|
||
|
{
|
||
|
if (StrChrW(lpSet, *lp))
|
||
|
return (int)(lp-lpStr);
|
||
|
lp++;
|
||
|
}
|
||
|
|
||
|
return (int)(lp-lpStr); // ==lstrlen(lpStr)
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return -1;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
// StrCSpnI: case-insensitive version of StrCSpn.
|
||
|
//
|
||
|
int FAR PASCAL StrCSpnIA(LPCSTR lpStr, LPCSTR lpSet)
|
||
|
{
|
||
|
// nature of the beast: O(lpStr*lpSet) work
|
||
|
LPCSTR lp = lpStr;
|
||
|
if (!lpStr || !lpSet)
|
||
|
return 0;
|
||
|
|
||
|
while (*lp)
|
||
|
{
|
||
|
if (StrChrIA(lpSet, READNATIVEWORD(lp)))
|
||
|
return (int)(lp-lpStr);
|
||
|
lp = AnsiNext(lp);
|
||
|
}
|
||
|
|
||
|
return (int)(lp-lpStr); // ==lstrlen(lpStr)
|
||
|
}
|
||
|
|
||
|
int FAR PASCAL StrCSpnIW(LPCWSTR lpStr, LPCWSTR lpSet)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
// nature of the beast: O(lpStr*lpSet) work
|
||
|
LPCWSTR lp = lpStr;
|
||
|
if (!lpStr || !lpSet)
|
||
|
return 0;
|
||
|
|
||
|
while (*lp)
|
||
|
{
|
||
|
if (StrChrIW(lpSet, *lp))
|
||
|
return (int)(lp-lpStr);
|
||
|
lp++;
|
||
|
}
|
||
|
|
||
|
return (int)(lp-lpStr); // ==lstrlen(lpStr)
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return -1;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* StrCmpN - Compare n bytes
|
||
|
*
|
||
|
* returns See lstrcmp return values.
|
||
|
* BUGBUG, won't work if source strings are in ROM
|
||
|
*/
|
||
|
int FAR PASCAL StrCmpNA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
|
||
|
{
|
||
|
char sz1[4];
|
||
|
char sz2[4];
|
||
|
LPCSTR lpszEnd = lpStr1 + nChar;
|
||
|
|
||
|
//DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);
|
||
|
|
||
|
for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1 = AnsiNext(lpStr1), lpStr2 = AnsiNext(lpStr2)) {
|
||
|
WORD wMatch;
|
||
|
|
||
|
|
||
|
wMatch = (WORD) (*lpStr2 | (*(lpStr2+1)<<8));
|
||
|
|
||
|
if (ChrCmpA_inline(READNATIVEWORD(lpStr1), wMatch)) {
|
||
|
int iRet;
|
||
|
|
||
|
(*(WORD FAR *)sz1) = READNATIVEWORD(lpStr1);
|
||
|
(*(WORD FAR *)sz2) = wMatch;
|
||
|
#ifndef UNIX
|
||
|
*AnsiNext(sz1) = 0;
|
||
|
*AnsiNext(sz2) = 0;
|
||
|
#else
|
||
|
*AnsiNext((LPWORD)sz1) = 0;
|
||
|
*AnsiNext((LPWORD)sz2) = 0;
|
||
|
#endif
|
||
|
|
||
|
iRet = lstrcmpA(sz1, sz2);
|
||
|
//DebugMsg(DM_TRACE, ".................... %d", iRet);
|
||
|
return iRet;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//DebugMsg(DM_TRACE, ".................... 0");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int FAR PASCAL StrCmpNW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
WCHAR sz1[2];
|
||
|
WCHAR sz2[2];
|
||
|
int i;
|
||
|
LPCWSTR lpszEnd = lpStr1 + nChar;
|
||
|
|
||
|
//DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);
|
||
|
|
||
|
for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
|
||
|
i = ChrCmpW_inline(*lpStr1, *lpStr2);
|
||
|
if (i) {
|
||
|
int iRet;
|
||
|
|
||
|
sz1[0] = *lpStr1;
|
||
|
sz2[0] = *lpStr2;
|
||
|
sz1[1] = TEXT('\0');
|
||
|
sz2[1] = TEXT('\0');
|
||
|
iRet = lstrcmpW(sz1, sz2);
|
||
|
//DebugMsg(DM_TRACE, ".................... %d", iRet);
|
||
|
return iRet;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//DebugMsg(DM_TRACE, ".................... 0");
|
||
|
return 0;
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return -1;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* StrCmpNI - Compare n bytes, case insensitive
|
||
|
*
|
||
|
* returns See lstrcmpi return values.
|
||
|
*/
|
||
|
|
||
|
#ifndef WINNT
|
||
|
__inline BOOL IsAsciiA(char ch)
|
||
|
{
|
||
|
return !(ch & 0x80);
|
||
|
}
|
||
|
|
||
|
__inline char Ascii_ToLowerA(char ch)
|
||
|
{
|
||
|
return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
|
||
|
}
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
__inline BOOL IsAsciiW(WCHAR ch)
|
||
|
{
|
||
|
return ch < 128;
|
||
|
}
|
||
|
|
||
|
__inline WCHAR Ascii_ToLowerW(WCHAR ch)
|
||
|
{
|
||
|
return (ch >= L'A' && ch <= L'Z') ? (ch - L'A' + L'a') : ch;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int FAR PASCAL StrCmpNIA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
|
||
|
{
|
||
|
int i;
|
||
|
LPCSTR lpszEnd = lpStr1 + nChar;
|
||
|
|
||
|
//DebugMsg(DM_TRACE, "StrCmpNI: %s %s %d returns:", lpStr1, lpStr2, nChar);
|
||
|
|
||
|
for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); (lpStr1 = AnsiNext(lpStr1)), (lpStr2 = AnsiNext(lpStr2))) {
|
||
|
WORD wMatch;
|
||
|
|
||
|
if (IsAsciiA(*lpStr1) && IsAsciiA(*lpStr2))
|
||
|
{
|
||
|
i = Ascii_ToLowerA(*lpStr1) - Ascii_ToLowerA(*lpStr2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#ifndef UNIX
|
||
|
wMatch = (UINT)(IsDBCSLeadByte(*lpStr2)) ? *(WORD FAR *)lpStr2 : (WORD)(BYTE)(*lpStr2);
|
||
|
#else
|
||
|
wMatch = (UINT)(IsDBCSLeadByte(*lpStr2)) ? (*lpStr2 | (*(lpStr2+1)<<8)) : (WORD)(BYTE)(*lpStr2);
|
||
|
#endif
|
||
|
|
||
|
i = ChrCmpIA(READNATIVEWORD(lpStr1), wMatch);
|
||
|
}
|
||
|
if (i) {
|
||
|
//DebugMsg(DM_TRACE, ".................... %d", i);
|
||
|
if (i < 0)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//DebugMsg(DM_TRACE, ".................... 0");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int FAR PASCAL StrCmpNIW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
int i;
|
||
|
LPCWSTR lpszEnd = lpStr1 + nChar;
|
||
|
|
||
|
//DebugMsg(DM_TRACE, "StrCmpNI: %s %s %d returns:", lpStr1, lpStr2, nChar);
|
||
|
|
||
|
for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
|
||
|
if (IsAsciiW(*lpStr1) && IsAsciiW(*lpStr2))
|
||
|
{
|
||
|
i = Ascii_ToLowerW(*lpStr1) - Ascii_ToLowerW(*lpStr2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
i = ChrCmpIW(*lpStr1, *lpStr2);
|
||
|
}
|
||
|
if (i) {
|
||
|
//DebugMsg(DM_TRACE, ".................... %d", i);
|
||
|
if (i < 0)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
//DebugMsg(DM_TRACE, ".................... 0");
|
||
|
return 0;
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return -1;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#else // WINNT
|
||
|
|
||
|
int StrCmpNIA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
// Win95 doesn't support NORM_STOP_ON_NULL
|
||
|
i = CompareStringA(GetThreadLocale(), NORM_IGNORECASE | NORM_STOP_ON_NULL,
|
||
|
lpStr1, nChar, lpStr2, nChar);
|
||
|
|
||
|
if (!i)
|
||
|
{
|
||
|
i = CompareStringA(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE | NORM_STOP_ON_NULL,
|
||
|
lpStr1, nChar, lpStr2, nChar);
|
||
|
}
|
||
|
|
||
|
return i - CSTR_EQUAL;
|
||
|
}
|
||
|
|
||
|
int StrCmpNIW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
// Win95 doesn't support NORM_STOP_ON_NULL
|
||
|
i = CompareStringW(GetThreadLocale(), NORM_IGNORECASE | NORM_STOP_ON_NULL,
|
||
|
lpStr1, nChar, lpStr2, nChar);
|
||
|
|
||
|
if (!i)
|
||
|
{
|
||
|
i = CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE | NORM_STOP_ON_NULL,
|
||
|
lpStr1, nChar, lpStr2, nChar);
|
||
|
}
|
||
|
|
||
|
return i - CSTR_EQUAL;
|
||
|
}
|
||
|
|
||
|
#endif // WINNT
|
||
|
|
||
|
|
||
|
/*
|
||
|
* IntlStrEq
|
||
|
*
|
||
|
* returns TRUE if strings are equal, FALSE if not
|
||
|
*/
|
||
|
BOOL IntlStrEqWorkerA(BOOL fCaseSens, LPCSTR lpString1, LPCSTR lpString2, int nChar) {
|
||
|
int retval;
|
||
|
DWORD dwFlags = fCaseSens ? LOCALE_USE_CP_ACP : (NORM_IGNORECASE | LOCALE_USE_CP_ACP);
|
||
|
|
||
|
#ifdef WINNT
|
||
|
//
|
||
|
// On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
|
||
|
//
|
||
|
dwFlags |= NORM_STOP_ON_NULL;
|
||
|
#else
|
||
|
//
|
||
|
// On Win9x we have to do the check manually
|
||
|
//
|
||
|
if (nChar != -1) {
|
||
|
LPCSTR psz1, psz2;
|
||
|
int cch = 0;
|
||
|
|
||
|
psz1 = lpString1;
|
||
|
psz2 = lpString2;
|
||
|
|
||
|
while( *psz1 != '\0' && *psz2 != '\0' && cch < nChar) {
|
||
|
#ifdef DBCS
|
||
|
psz1 = CharNextA(psz1);
|
||
|
psz2 = CharNextA(psz2);
|
||
|
|
||
|
cch = min(psz1 - lpString1, psz2 - lpString2);
|
||
|
#else
|
||
|
psz1++;
|
||
|
psz2++;
|
||
|
cch++;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// add one in for terminating '\0'
|
||
|
cch++;
|
||
|
|
||
|
if (cch < nChar) {
|
||
|
nChar = cch;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
retval = CompareStringA( GetThreadLocale(),
|
||
|
dwFlags,
|
||
|
lpString1,
|
||
|
nChar,
|
||
|
lpString2,
|
||
|
nChar );
|
||
|
if (retval == 0)
|
||
|
{
|
||
|
//
|
||
|
// The caller is not expecting failure. Try the system
|
||
|
// default locale id.
|
||
|
//
|
||
|
retval = CompareStringA( GetSystemDefaultLCID(),
|
||
|
dwFlags,
|
||
|
lpString1,
|
||
|
nChar,
|
||
|
lpString2,
|
||
|
nChar );
|
||
|
}
|
||
|
|
||
|
if (retval == 0)
|
||
|
{
|
||
|
if (lpString1 && lpString2)
|
||
|
{
|
||
|
//
|
||
|
// The caller is not expecting failure. We've never had a
|
||
|
// failure indicator before. We'll do a best guess by calling
|
||
|
// the C runtimes to do a non-locale sensitive compare.
|
||
|
//
|
||
|
if (fCaseSens)
|
||
|
retval = StrCmpNA(lpString1, lpString2, nChar) + 2;
|
||
|
else {
|
||
|
retval = StrCmpNIA(lpString1, lpString2, nChar) + 2;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
retval = 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (retval == 2);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL IntlStrEqWorkerW(BOOL fCaseSens, LPCWSTR lpString1, LPCWSTR lpString2, int nChar) {
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
int retval;
|
||
|
DWORD dwFlags = fCaseSens ? 0 : NORM_IGNORECASE;
|
||
|
|
||
|
#ifdef WINNT
|
||
|
//
|
||
|
// On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
|
||
|
//
|
||
|
dwFlags |= NORM_STOP_ON_NULL;
|
||
|
#else
|
||
|
//
|
||
|
// On Win9x we have to do the check manually
|
||
|
//
|
||
|
if (nChar != -1) {
|
||
|
LPCWSTR psz1, psz2;
|
||
|
int cch = 0;
|
||
|
|
||
|
psz1 = lpString1;
|
||
|
psz2 = lpString2;
|
||
|
|
||
|
while( *psz1 != TEXT('\0') && *psz2 != TEXT('\0') && cch < nChar) {
|
||
|
psz1++;
|
||
|
psz2++;
|
||
|
cch++;
|
||
|
}
|
||
|
|
||
|
// add one in for terminating '\0'
|
||
|
cch++;
|
||
|
|
||
|
if (cch < nChar) {
|
||
|
nChar = cch;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
retval = CompareStringW( GetThreadLocale(),
|
||
|
dwFlags,
|
||
|
lpString1,
|
||
|
nChar,
|
||
|
lpString2,
|
||
|
nChar );
|
||
|
if (retval == 0)
|
||
|
{
|
||
|
//
|
||
|
// The caller is not expecting failure. Try the system
|
||
|
// default locale id.
|
||
|
//
|
||
|
retval = CompareStringW( GetSystemDefaultLCID(),
|
||
|
dwFlags,
|
||
|
lpString1,
|
||
|
nChar,
|
||
|
lpString2,
|
||
|
nChar );
|
||
|
}
|
||
|
|
||
|
if (retval == 0)
|
||
|
{
|
||
|
if (lpString1 && lpString2)
|
||
|
{
|
||
|
//
|
||
|
// The caller is not expecting failure. We've never had a
|
||
|
// failure indicator before. We'll do a best guess by calling
|
||
|
// the C runtimes to do a non-locale sensitive compare.
|
||
|
//
|
||
|
if (fCaseSens)
|
||
|
retval = StrCmpNW(lpString1, lpString2, nChar) + 2;
|
||
|
else {
|
||
|
retval = StrCmpNIW(lpString1, lpString2, nChar) + 2;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
retval = 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (retval == 2);
|
||
|
|
||
|
#else
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return FALSE;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* StrRStrI - Search for last occurrence of a substring
|
||
|
*
|
||
|
* Assumes lpSource points to the null terminated source string
|
||
|
* lpLast points to where to search from in the source string
|
||
|
* lpLast is not included in the search
|
||
|
* lpSrch points to string to search for
|
||
|
* returns last occurrence of string if successful; NULL otherwise
|
||
|
*/
|
||
|
LPSTR FAR PASCAL StrRStrIA(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch)
|
||
|
{
|
||
|
LPCSTR lpFound = NULL;
|
||
|
LPSTR lpEnd;
|
||
|
char cHold;
|
||
|
|
||
|
if (!lpLast)
|
||
|
lpLast = lpSource + lstrlenA(lpSource);
|
||
|
|
||
|
if (lpSource >= lpLast || *lpSrch == 0)
|
||
|
return NULL;
|
||
|
|
||
|
lpEnd = lstrfns_StrEndNA(lpLast, (UINT)(lstrlenA(lpSrch)-1));
|
||
|
cHold = *lpEnd;
|
||
|
*lpEnd = 0;
|
||
|
|
||
|
while ((lpSource = StrStrIA(lpSource, lpSrch))!=0 &&
|
||
|
OFFSETOF(lpSource) < OFFSETOF(lpLast))
|
||
|
{
|
||
|
lpFound = lpSource;
|
||
|
lpSource = AnsiNext(lpSource);
|
||
|
}
|
||
|
*lpEnd = cHold;
|
||
|
return((LPSTR)lpFound);
|
||
|
}
|
||
|
|
||
|
LPWSTR FAR PASCAL StrRStrIW(LPCWSTR lpSource, LPCWSTR lpLast, LPCWSTR lpSrch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
LPCWSTR lpFound = NULL;
|
||
|
LPWSTR lpEnd;
|
||
|
WCHAR cHold;
|
||
|
|
||
|
if (!lpLast)
|
||
|
lpLast = lpSource + lstrlenW(lpSource);
|
||
|
|
||
|
if (lpSource >= lpLast || *lpSrch == 0)
|
||
|
return NULL;
|
||
|
|
||
|
lpEnd = lstrfns_StrEndNW(lpLast, (UINT)(lstrlenW(lpSrch)-1));
|
||
|
cHold = *lpEnd;
|
||
|
*lpEnd = 0;
|
||
|
|
||
|
while ((lpSource = StrStrIW(lpSource, lpSrch))!=0 &&
|
||
|
lpSource < lpLast)
|
||
|
{
|
||
|
lpFound = lpSource;
|
||
|
lpSource++;
|
||
|
}
|
||
|
*lpEnd = cHold;
|
||
|
return((LPWSTR)lpFound);
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return NULL;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* StrStr - Search for first occurrence of a substring
|
||
|
*
|
||
|
* Assumes lpSource points to source string
|
||
|
* lpSrch points to string to search for
|
||
|
* returns first occurrence of string if successful; NULL otherwise
|
||
|
*/
|
||
|
LPSTR FAR PASCAL StrStrA(LPCSTR lpFirst, LPCSTR lpSrch)
|
||
|
{
|
||
|
UINT uLen;
|
||
|
WORD wMatch;
|
||
|
|
||
|
uLen = (UINT)lstrlenA(lpSrch);
|
||
|
wMatch = READNATIVEWORD(lpSrch);
|
||
|
|
||
|
for ( ; (lpFirst=StrChrA(lpFirst, wMatch))!=0 && !IntlStrEqNA(lpFirst, lpSrch, uLen);
|
||
|
lpFirst=AnsiNext(lpFirst))
|
||
|
continue; /* continue until we hit the end of the string or get a match */
|
||
|
|
||
|
return((LPSTR)lpFirst);
|
||
|
}
|
||
|
|
||
|
LPWSTR FAR PASCAL StrStrW(LPCWSTR lpFirst, LPCWSTR lpSrch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
UINT uLen;
|
||
|
WCHAR wMatch;
|
||
|
|
||
|
uLen = (UINT)lstrlenW(lpSrch);
|
||
|
wMatch = *lpSrch;
|
||
|
|
||
|
for ( ; (lpFirst=StrChrW(lpFirst, wMatch))!=0 && !IntlStrEqNW(lpFirst, lpSrch, uLen);
|
||
|
lpFirst++)
|
||
|
continue; /* continue until we hit the end of the string or get a match */
|
||
|
|
||
|
return((LPWSTR)lpFirst);
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return NULL;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* StrChrI - Find first occurrence of character in string, case insensitive
|
||
|
* Assumes lpStart points to start of null terminated string
|
||
|
* wMatch is the character to match
|
||
|
* returns ptr to the first occurrence of ch in str, NULL if not found.
|
||
|
*/
|
||
|
LPSTR FAR PASCAL StrChrIA(LPCSTR lpStart, WORD wMatch)
|
||
|
{
|
||
|
wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
|
||
|
|
||
|
for ( ; *lpStart; lpStart = AnsiNext(lpStart))
|
||
|
{
|
||
|
if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
|
||
|
return((LPSTR)lpStart);
|
||
|
}
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
LPWSTR FAR PASCAL StrChrIW(LPCWSTR lpStart, WCHAR wMatch)
|
||
|
{
|
||
|
for ( ; *lpStart; lpStart++)
|
||
|
{
|
||
|
if (!ChrCmpIW(*lpStart, wMatch))
|
||
|
return((LPWSTR)lpStart);
|
||
|
}
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* StrStrI - Search for first occurrence of a substring, case insensitive
|
||
|
*
|
||
|
* Assumes lpFirst points to source string
|
||
|
* lpSrch points to string to search for
|
||
|
* returns first occurrence of string if successful; NULL otherwise
|
||
|
*/
|
||
|
LPSTR FAR PASCAL StrStrIA(LPCSTR lpFirst, LPCSTR lpSrch)
|
||
|
{
|
||
|
UINT uLen;
|
||
|
WORD wMatch;
|
||
|
|
||
|
uLen = (UINT)lstrlenA(lpSrch);
|
||
|
wMatch = READNATIVEWORD(lpSrch);
|
||
|
|
||
|
for ( ; (lpFirst = StrChrIA(lpFirst, wMatch)) != 0 && !IntlStrEqNIA(lpFirst, lpSrch, uLen);
|
||
|
lpFirst=AnsiNext(lpFirst))
|
||
|
continue; /* continue until we hit the end of the string or get a match */
|
||
|
|
||
|
return((LPSTR)lpFirst);
|
||
|
}
|
||
|
|
||
|
LPWSTR FAR PASCAL StrStrIW(LPCWSTR lpFirst, LPCWSTR lpSrch)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
|
||
|
UINT uLen;
|
||
|
WCHAR wMatch;
|
||
|
|
||
|
uLen = (UINT)lstrlenW(lpSrch);
|
||
|
wMatch = *lpSrch;
|
||
|
|
||
|
for ( ; (lpFirst = StrChrIW(lpFirst, wMatch)) != 0 && !IntlStrEqNIW(lpFirst, lpSrch, uLen);
|
||
|
lpFirst++)
|
||
|
continue; /* continue until we hit the end of the string or get a match */
|
||
|
|
||
|
return((LPWSTR)lpFirst);
|
||
|
|
||
|
#else
|
||
|
|
||
|
SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
|
||
|
return NULL;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
#ifndef UNICODE
|
||
|
|
||
|
// TruncateString - bugbug: the same logic in shdocvw
|
||
|
//
|
||
|
// purpose: cut a string at the given length in dbcs safe manner
|
||
|
// the string may be truncated at cch-1 if the sz[cch] points
|
||
|
// to a lead byte that would result in cutting in the middle
|
||
|
// of double byte character.
|
||
|
// It is caller's responsibility to reserve enough buffer for
|
||
|
// sz so we can put sz[cch]=0 safely.
|
||
|
//
|
||
|
// *Note this logic is not much perf hit when called with sbcs
|
||
|
// string, as it just bails out at the tail character always.
|
||
|
//
|
||
|
void TruncateString(char *sz, int cchBufferSize)
|
||
|
{
|
||
|
int cch = cchBufferSize - 1;
|
||
|
LPSTR psz = &sz[cch];
|
||
|
|
||
|
if (!sz || cchBufferSize <= 0) return;
|
||
|
|
||
|
while (psz > sz)
|
||
|
{
|
||
|
psz--;
|
||
|
if (!IsDBCSLeadByte(*psz))
|
||
|
{
|
||
|
// Found non-leadbyte for the first time.
|
||
|
// This is either a trail byte of double byte char
|
||
|
// or a single byte character we've first seen.
|
||
|
// Thus, the next pointer must be at either of a leadbyte
|
||
|
// or &sz[cch]
|
||
|
psz++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (((&sz[cch] - psz) & 1) && cch > 0)
|
||
|
{
|
||
|
// we're truncating the string in the middle of dbcs
|
||
|
cch--;
|
||
|
}
|
||
|
sz[cch] = '\0';
|
||
|
return;
|
||
|
}
|
||
|
#endif
|