/* * util.c - Utility routines. */ /* Headers **********/ #include "project.h" #pragma hdrstop /****************************** Public Functions *****************************/ #ifdef WINNT // // These are some helper functions for handling Unicode strings // /*---------------------------------------------------------- Purpose: This function converts a wide-char string to a multi-byte string. If pszBuf is non-NULL and the converted string can fit in pszBuf, then *ppszAnsi will point to the given buffer. Otherwise, this function will allocate a buffer that can hold the converted string. If pszWide is NULL, then *ppszAnsi will be freed. Note that pszBuf must be the same pointer between the call that converted the string and the call that frees the string. Returns: TRUE FALSE (if out of memory) Cond: -- */ PRIVATE_CODE BOOL AnsiFromUnicode( LPSTR * ppszAnsi, LPCWSTR pwszWide, // NULL to clean up LPSTR pszBuf, int cchBuf) { BOOL bRet; // Convert the string? if (pwszWide) { // Yes; determine the converted string length int cch; LPSTR psz; cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, NULL, 0, NULL, NULL); // String too big, or is there no buffer? if (cch > cchBuf || NULL == pszBuf) { // Yes; allocate space cchBuf = cch + 1; psz = (LPSTR)LocalAlloc(LPTR, CbFromCchA(cchBuf)); } else { // No; use the provided buffer ASSERT(pszBuf); psz = pszBuf; } if (psz) { // Convert the string cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, psz, cchBuf, NULL, NULL); bRet = (0 < cch); } else { bRet = FALSE; } *ppszAnsi = psz; } else { // No; was this buffer allocated? if (*ppszAnsi && pszBuf != *ppszAnsi) { // Yes; clean up LocalFree((HLOCAL)*ppszAnsi); *ppszAnsi = NULL; } bRet = TRUE; } return bRet; } /*---------------------------------------------------------- Purpose: Wide-char wrapper for StrToIntExA. Returns: see StrToIntExA Cond: -- */ PUBLIC_CODE BOOL StrToIntExW( LPCWSTR pwszString, DWORD dwFlags, // STIF_ bitfield int FAR * piRet) { CHAR szBuf[MAX_BUF]; LPSTR pszString; BOOL bRet = AnsiFromUnicode(&pszString, pwszString, szBuf, SIZECHARS(szBuf)); if (bRet) { bRet = StrToIntExA(pszString, dwFlags, piRet); AnsiFromUnicode(&pszString, NULL, szBuf, 0); } return bRet; } /*---------------------------------------------------------- Purpose: Returns an integer value specifying the length of the substring in psz that consists entirely of characters in pszSet. If psz begins with a character not in pszSet, then this function returns 0. This is a DBCS-safe version of the CRT strspn(). Returns: see above Cond: -- */ PUBLIC_CODE int StrSpnW( LPCWSTR psz, LPCWSTR pszSet) { LPCWSTR pszT; LPCWSTR pszSetT; ASSERT(psz); ASSERT(pszSet); // Go thru the string to be inspected for (pszT = psz; *pszT; pszT++) { // Go thru the char set for (pszSetT = pszSet; *pszSetT != *pszT; pszSetT++) { if (0 == *pszSetT) { // Reached end of char set without finding a match return (int)(pszT - psz); } } } return (int)(pszT - psz); } /*---------------------------------------------------------- Purpose: Returns a pointer to the first occurrence of a character in psz that belongs to the set of characters in pszSet. The search does not include the null terminator. Returns: see above Cond: -- */ PUBLIC_CODE LPWSTR StrPBrkW( IN LPCWSTR psz, IN LPCWSTR pszSet) { LPCWSTR pszSetT; ASSERT(psz); ASSERT(pszSet); // Go thru the string to be inspected while (*psz) { // Go thru the char set for (pszSetT = pszSet; *pszSetT; pszSetT++) { if (*psz == *pszSetT) { // Found first character that matches return (LPWSTR)psz; // Const -> non-const } } psz++; } return NULL; } #endif // WINNT /*---------------------------------------------------------- Purpose: Special verion of atoi. Supports hexadecimal too. If this function returns FALSE, *piRet is set to 0. Returns: TRUE if the string is a number, or contains a partial number FALSE if the string is not a number Cond: -- */ PUBLIC_CODE BOOL StrToIntExA( LPCSTR pszString, DWORD dwFlags, // STIF_ bitfield int FAR * piRet) { #define IS_DIGIT(ch) InRange(ch, '0', '9') BOOL bRet; int n; BOOL bNeg = FALSE; LPCSTR psz; LPCSTR pszAdj; // Skip leading whitespace // for (psz = pszString; *psz == ' ' || *psz == '\n' || *psz == '\t'; psz = CharNextA(psz)) ; // Determine possible explicit signage // if (*psz == '+' || *psz == '-') { bNeg = (*psz == '+') ? FALSE : TRUE; psz++; } // Or is this hexadecimal? // pszAdj = CharNextA(psz); if ((STIF_SUPPORT_HEX & dwFlags) && *psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X')) { // Yes // (Never allow negative sign with hexadecimal numbers) bNeg = FALSE; psz = CharNextA(pszAdj); pszAdj = psz; // Do the conversion // for (n = 0; ; psz = CharNextA(psz)) { if (IS_DIGIT(*psz)) n = 0x10 * n + *psz - '0'; else { CHAR ch = *psz; int n2; if (ch >= 'a') ch -= 'a' - 'A'; n2 = ch - 'A' + 0xA; if (n2 >= 0xA && n2 <= 0xF) n = 0x10 * n + n2; else break; } } // Return TRUE if there was at least one digit bRet = (psz != pszAdj); } else { // No pszAdj = psz; // Do the conversion for (n = 0; IS_DIGIT(*psz); psz = CharNextA(psz)) n = 10 * n + *psz - '0'; // Return TRUE if there was at least one digit bRet = (psz != pszAdj); } *piRet = bNeg ? -n : n; return bRet; } /*---------------------------------------------------------- Purpose: Returns an integer value specifying the length of the substring in psz that consists entirely of characters in pszSet. If psz begins with a character not in pszSet, then this function returns 0. This is a DBCS-safe version of the CRT strspn(). Returns: see above Cond: -- */ PUBLIC_CODE int StrSpnA( LPCSTR psz, LPCSTR pszSet) { LPCSTR pszT; LPCSTR pszSetT; // Go thru the string to be inspected for (pszT = psz; *pszT; pszT = CharNextA(pszT)) { // Go thru the char set for (pszSetT = pszSet; *pszSetT; pszSetT = CharNextA(pszSetT)) { if (*pszSetT == *pszT) { if ( !IsDBCSLeadByte(*pszSetT) ) { break; // Chars match } else if (pszSetT[1] == pszT[1]) { break; // Chars match } } } // End of char set? if (0 == *pszSetT) { break; // Yes, no match on this inspected char } } return (int)(pszT - psz); } /*---------------------------------------------------------- Purpose: Returns a pointer to the first occurrence of a character in psz that belongs to the set of characters in pszSet. The search does not include the null terminator. If psz contains no characters that are in the set of characters in pszSet, this function returns NULL. This function is DBCS-safe. Returns: see above Cond: -- */ PUBLIC_CODE LPSTR StrPBrkA( LPCSTR psz, LPCSTR pszSet) { LPCSTR pszSetT; ASSERT(psz); ASSERT(pszSet); while (*psz) { for (pszSetT = pszSet; *pszSetT; pszSetT = CharNextA(pszSetT)) { if (*psz == *pszSetT) { // Found first character that matches return (LPSTR)psz; // Const -> non-const } } psz = CharNextA(psz); } return NULL; } PUBLIC_CODE BOOL IsPathDirectory(PCSTR pcszPath) { DWORD dwAttr; ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR)); dwAttr = GetFileAttributes(pcszPath); return(dwAttr != -1 && IS_FLAG_SET(dwAttr, FILE_ATTRIBUTE_DIRECTORY)); } PUBLIC_CODE BOOL KeyExists(HKEY hkeyRoot, PCSTR pcszSubKey) { BOOL bExists; HKEY hkey; ASSERT(IS_VALID_HANDLE(hkeyRoot, KEY)); ASSERT(IS_VALID_STRING_PTR(pcszSubKey, CSTR)); bExists = (RegOpenKey(hkeyRoot, pcszSubKey, &hkey) == ERROR_SUCCESS); if (bExists) EVAL(RegCloseKey(hkey) == ERROR_SUCCESS); return(bExists); } #ifdef DEBUG PUBLIC_CODE BOOL IsStringContained(PCSTR pcszBigger, PCSTR pcszSuffix) { ASSERT(IS_VALID_STRING_PTR(pcszBigger, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszSuffix, CSTR)); return(pcszSuffix >= pcszBigger && pcszSuffix <= pcszBigger + lstrlen(pcszBigger)); } #endif