strings.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 55k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //============================================================================
  2. //
  3. // DBCS and UNICODE aware string routines
  4. //
  5. //
  6. //============================================================================
  7. #include "priv.h"
  8. #include "ids.h"
  9. #include <winnlsp.h>    // Get private NORM_ flag for StrEqIntl()
  10. #include <shlobj.h>     // STRRET LPCITEMIDLIST
  11. #include <w95wraps.h>
  12. #include <mluisupp.h>
  13. #define IS_DIGITA(ch)    InRange(ch, '0', '9')
  14. #define IS_DIGITW(ch)    InRange(ch, L'0', L'9')
  15. #define DM_INTERVAL 0
  16. #ifdef UNIX
  17. #ifdef BIG_ENDIAN
  18. #define READNATIVEWORD(x) MAKEWORD(*(char*)(x), *(char*)((char*)(x) + 1))
  19. #else 
  20. #define READNATIVEWORD(x) MAKEWORD(*(char*)((char*)(x) + 1), *(char*)(x))
  21. #endif
  22. #else
  23. #define READNATIVEWORD(x) (*(UNALIGNED WORD *)x)
  24. #endif
  25. __inline BOOL IsAsciiA(char ch)
  26. {
  27.     return !(ch & 0x80);
  28. }
  29. __inline BOOL IsAsciiW(WCHAR ch)
  30. {
  31.     return ch < 128;
  32. }
  33. __inline char Ascii_ToLowerA(char ch)
  34. {
  35.     return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
  36. }
  37. __inline WCHAR Ascii_ToLowerW(WCHAR ch)
  38. {
  39.     return (ch >= L'A' && ch <= L'Z') ? (ch - L'A' + L'a') : ch;
  40. }
  41. // WARNING: all of these APIs do not setup DS, so you can not access
  42. // any data in the default data seg of this DLL.
  43. //
  44. // do not create any global variables... talk to chrisg if you don't
  45. // understand thid
  46. /*
  47.  * StrEndN - Find the end of a string, but no more than n bytes
  48.  * Assumes   lpStart points to start of null terminated string
  49.  *           nBufSize is the maximum length
  50.  * returns ptr to just after the last byte to be included
  51.  */
  52. LPSTR
  53. lstrfns_StrEndNA(
  54.     LPCSTR lpStart,
  55.     int nBufSize)
  56. {
  57.     LPCSTR lpEnd;
  58.     for (lpEnd = lpStart + nBufSize; *lpStart && lpStart < lpEnd;
  59.          lpStart = AnsiNext(lpStart))
  60.         continue;   /* just getting to the end of the string */
  61.     if (lpStart > lpEnd)
  62.     {
  63.         /* We can only get here if the last byte before lpEnd was a lead byte
  64.         */
  65.         lpStart -= 2;
  66.     }
  67.     return((LPSTR)lpStart);
  68. }
  69. LPWSTR lstrfns_StrEndNW(LPCWSTR lpStart, int nBufSize)
  70. {
  71.     LPCWSTR lpEnd;
  72.     for (lpEnd = lpStart + nBufSize; *lpStart && (lpStart < lpEnd);
  73.          lpStart++)
  74.         continue;   /* just getting to the end of the string */
  75.     return((LPWSTR)lpStart);
  76. }
  77. /*
  78.  * ChrCmp -  Case sensitive character comparison for DBCS
  79.  * Assumes   w1, wMatch are characters to be compared
  80.  * Return    FALSE if they match, TRUE if no match
  81.  */
  82. __inline BOOL ChrCmpA_inline(WORD w1, WORD wMatch)
  83. {
  84.     /* Most of the time this won't match, so test it first for speed.
  85.     */
  86.     if (LOBYTE(w1) == LOBYTE(wMatch))
  87.     {
  88.         if (IsDBCSLeadByte(LOBYTE(w1)))
  89.         {
  90.             return(w1 != wMatch);
  91.         }
  92.         return FALSE;
  93.     }
  94.     return TRUE;
  95. }
  96. __inline BOOL ChrCmpW_inline(WCHAR w1, WCHAR wMatch)
  97. {
  98.     return(!(w1 == wMatch));
  99. }
  100. /*
  101.  * ChrCmpI - Case insensitive character comparison for DBCS
  102.  * Assumes   w1, wMatch are characters to be compared;
  103.  *           HIBYTE of wMatch is 0 if not a DBC
  104.  * Return    FALSE if match, TRUE if not
  105.  */
  106. BOOL ChrCmpIA(WORD w1, WORD wMatch)
  107. {
  108.     char sz1[3], sz2[3];
  109.     if (IsDBCSLeadByte(sz1[0] = LOBYTE(w1)))
  110.     {
  111.         sz1[1] = HIBYTE(w1);
  112.         sz1[2] = '';
  113.     }
  114.     else
  115.         sz1[1] = '';
  116. #if defined(MWBIG_ENDIAN)
  117.     sz2[0] = LOBYTE(wMatch);
  118.     sz2[1] = HIBYTE(wMatch);
  119. #else
  120.     *(WORD *)sz2 = wMatch;
  121. #endif
  122.     sz2[2] = '';
  123.     return lstrcmpiA(sz1, sz2);
  124. }
  125. BOOL ChrCmpIW(WCHAR w1, WCHAR wMatch)
  126. {
  127.     WCHAR sz1[2], sz2[2];
  128.     sz1[0] = w1;
  129.     sz1[1] = '';
  130.     sz2[0] = wMatch;
  131.     sz2[1] = '';
  132.     return StrCmpIW(sz1, sz2);
  133. }
  134. LPWSTR StrCpyW(LPWSTR pszDst, LPCWSTR pszSrc)
  135. {
  136.     LPWSTR psz = pszDst;
  137.     ASSERT(pszDst);
  138.     ASSERT(pszSrc);
  139.     while (*pszDst++ = *pszSrc++)
  140.         ;
  141.     return psz;
  142. }
  143. //***   StrCpyNX[AW] -- just like StrCpyN[AW], but returns ptr to EOS
  144. // NOTES
  145. //  do we really need 'A' version?  (for now we do for shell32 on 'old'
  146. //  platforms that we test on but don't ship)
  147. LPSTR StrCpyNXA(LPSTR pszDst, LPCSTR pszSrc, int cchMax)
  148. {
  149.     RIPMSG(cchMax >= 0 && pszDst && IS_VALID_WRITE_BUFFER(pszDst, char, cchMax), "StrCpyNXA: Caller passed bad pszDst");
  150.     RIPMSG(pszSrc && IS_VALID_STRING_PTRA(pszSrc, -1), "StrCpyNXA: Caller passed bad pszSrc");
  151.     // NOTE: Cannot use DEBUGWhackPathBuffer before copying because src and
  152.     // dest might overlap.  Must delay whacking until after we're done.
  153.     if (0 < cchMax)
  154.     {
  155.         if (!pszSrc)
  156.             goto NullItOut;
  157.         // Leave room for the null terminator
  158.         while (0 < --cchMax)
  159.         {
  160.             if (!(*pszDst++ = *pszSrc++))
  161.             {
  162.                 --pszDst;
  163.                 break;
  164.             }
  165.         }
  166.         cchMax++;
  167.         // in the cchMax>1 case, pszDst already points at the NULL, but reassigning it doesn't hurt
  168. NullItOut:
  169.         // Whack the unused part of the buffer
  170.         DEBUGWhackPathBufferA(pszDst, cchMax);
  171.         *pszDst = '';
  172.     }
  173.     return pszDst;
  174. }
  175. LPWSTR StrCpyNXW(LPWSTR pszDst, LPCWSTR pszSrc, int cchMax)
  176. {
  177.     RIPMSG(cchMax >= 0 && pszDst && IS_VALID_WRITE_BUFFER(pszDst, WCHAR, cchMax), "StrCpyNXW: Caller passed bad pszDst");
  178.     RIPMSG(pszSrc && IS_VALID_STRING_PTRW(pszSrc, -1), "StrCpyNXW: Caller passed bad pszSrc");
  179.     // NOTE: Cannot use DEBUGWhackPathBuffer before copying because src and
  180.     // dest might overlap.  Must delay whacking until after we're done.
  181.     if (0 < cchMax)
  182.     {
  183.         if (!pszSrc) // a test app passed in a NULL src ptr and we faulted, let's not fault here.
  184.             goto NullItOut;
  185.         // Leave room for the null terminator
  186.         while (0 < --cchMax)
  187.         {
  188.             if (!(*pszDst++ = *pszSrc++))
  189.             {
  190.                 --pszDst;
  191.                 break;
  192.             }
  193.         }
  194.         cchMax++;
  195.         // in the cchMax>1 case, pszDst already points at the NULL, but reassigning it doesn't hurt
  196. NullItOut:
  197.         // Whack the unused part of the buffer
  198.         DEBUGWhackPathBufferW(pszDst, cchMax);
  199.         *pszDst = L'';
  200.     }
  201.     return pszDst;
  202. }
  203. LPWSTR StrCpyNW(LPWSTR pszDst, LPCWSTR pszSrc, int cchMax)
  204. {
  205.     StrCpyNXW(pszDst, pszSrc, cchMax);
  206.     return pszDst;
  207. }
  208. LPWSTR StrCatW(LPWSTR pszDst, LPCWSTR pszSrc)
  209. {
  210.     LPWSTR psz = pszDst;
  211.     ASSERT(pszDst);
  212.     ASSERT(pszSrc);
  213.     while (0 != *pszDst)
  214.         pszDst++;
  215.     while (*pszDst++ = *pszSrc++)
  216.         ;
  217.     return psz;
  218. }
  219. LWSTDAPI_(LPWSTR) StrCatBuffW(LPWSTR pszDest, LPCWSTR pszSrc, int cchDestBuffSize)
  220. {
  221.     LPWSTR psz;
  222.     ASSERT(pszDest);
  223.     ASSERT(pszSrc);
  224.     ASSERT(cchDestBuffSize >= 0);
  225.     VDATEINPUTBUF(pszDest, TCHAR, cchDestBuffSize);
  226.     psz = pszDest;
  227.     // we walk forward till we find the end of pszDest, subtracting
  228.     // from cchDestBuffSize as we go.
  229.     while (*psz)
  230.     {
  231.         psz++;
  232.         cchDestBuffSize--;
  233.     }
  234.     if (cchDestBuffSize <= 0)
  235.     {
  236.         // check to make sure that the pszDest hadn't already overflowed its buffer!
  237.         ASSERT(cchDestBuffSize == 0);
  238.         return pszDest;
  239.     }
  240.     // call the shlwapi function here because win95 does not have lstrcpynW
  241.     StrCpyNW(psz, pszSrc, cchDestBuffSize);
  242.     return pszDest;
  243. }
  244.  
  245. LWSTDAPI_(LPSTR) StrCatBuffA(LPSTR pszDest, LPCSTR pszSrc, int cchDestBuffSize)
  246. {
  247.     LPSTR psz;
  248.    
  249.     ASSERT(pszDest);
  250.     ASSERT(pszSrc);
  251.     ASSERT(cchDestBuffSize >= 0);
  252.     VDATEINPUTBUF(pszDest, CHAR, cchDestBuffSize);
  253.     psz = pszDest;
  254.     
  255.     // we walk forward till we find the end of pszDest, subtracting
  256.     // from cchDestBuffSize as we go.
  257.     while (*psz)
  258.     {
  259.         psz++;
  260.         cchDestBuffSize--;
  261.     }
  262.     if (cchDestBuffSize <= 0)
  263.     {
  264.         // check to make sure that the pszDest hadn't already overflowed its buffer!
  265.         ASSERT(cchDestBuffSize == 0);
  266.         return pszDest;
  267.     }
  268.     // Let kernel do the work for us. 
  269.     //
  270.     // WARNING: We might generate a truncated DBCS sting becuase kernel's lstrcpynA
  271.     // dosent check for this. Ask me if I care.
  272.     lstrcpynA(psz, pszSrc, cchDestBuffSize);
  273.     return pszDest;
  274. }
  275.    
  276. /* StrNCat(front, back, count) - append count chars of back onto front
  277.  */
  278. LPSTR StrNCatA(LPSTR front, LPCSTR back, int cchMax)
  279. {
  280.     LPSTR start = front;
  281.     while (*front++)
  282.                 ;
  283.     front--;
  284.     lstrcpyn(front, back, cchMax);
  285.     return(start);    
  286. }
  287. LPWSTR StrNCatW(LPWSTR front, LPCWSTR back, int cchMax)
  288. {
  289.     LPWSTR start = front;
  290.     while (*front++)
  291.                 ;
  292.     front--;
  293.     StrCpyNW(front, back, cchMax);
  294.     
  295.     return(start);    
  296. }
  297. /*
  298.  * StrChr - Find first occurrence of character in string
  299.  * Assumes   lpStart points to start of null terminated string
  300.  *           wMatch  is the character to match
  301.  * returns ptr to the first occurrence of ch in str, NULL if not found.
  302.  */
  303. LPSTR _StrChrA(LPCSTR lpStart, WORD wMatch, BOOL fMBCS)
  304. {
  305.     if (fMBCS) {
  306.         for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  307.         {
  308.             if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  309.                 return((LPSTR)lpStart);
  310.         }
  311.     } else {
  312.         for ( ; *lpStart; lpStart++)
  313.         {
  314.             if ((BYTE)*lpStart == LOBYTE(wMatch)) {
  315.                 return((LPSTR)lpStart);
  316.             }
  317.         }
  318.     }
  319.     return (NULL);
  320. }
  321. LPSTR StrChrA(LPCSTR lpStart, WORD wMatch)
  322. {
  323.     CPINFO cpinfo;
  324.     return _StrChrA(lpStart, wMatch, GetCPInfo(CP_ACP, &cpinfo) && cpinfo.LeadByte[0]);
  325. }
  326. #ifdef ALIGNMENT_SCENARIO
  327. LPWSTR StrChrSlowW(const UNALIGNED WCHAR *lpStart, WCHAR wMatch)
  328. {
  329.     for ( ; *lpStart; lpStart++)
  330.     {
  331.       if (!ChrCmpW_inline(*lpStart, wMatch))
  332.         {
  333.             return((LPWSTR)lpStart);
  334.         }
  335.     }
  336. }
  337. #endif
  338. LPWSTR StrChrW(LPCWSTR lpStart, WCHAR wMatch)
  339. {
  340.     //
  341.     //  BUGBUG raymondc
  342.     //  Apparently, somebody is passing unaligned strings to StrChrW.
  343.     //  Find out who and make them stop.
  344.     //
  345.     ASSERT(!((ULONG_PTR)lpStart & 1)); // Assert alignedness
  346. #ifdef ALIGNMENT_SCENARIO
  347.     //
  348.     //  Since unaligned strings arrive so rarely, put the slow
  349.     //  version in a separate function so the common case stays
  350.     //  fast.  Believe it or not, we call StrChrW so often that
  351.     //  it is now a performance-sensitive function!
  352.     //
  353.     if ((ULONG_PTR)lpStart & 1)
  354.         return StrChrSlowW(lpStart, wMatch);
  355. #endif
  356.     for ( ; *lpStart; lpStart++)
  357.     {
  358.       if (!ChrCmpW_inline(*lpStart, wMatch))
  359.         {
  360.             return((LPWSTR)lpStart);
  361.         }
  362.     }
  363.     return (NULL);
  364. }
  365. /*
  366.  * StrRChr - Find last occurrence of character in string
  367.  * Assumes   lpStart points to start of string
  368.  *           lpEnd   points to end of string (NOT included in search)
  369.  *           wMatch  is the character to match
  370.  * returns ptr to the last occurrence of ch in str, NULL if not found.
  371.  */
  372. LPSTR StrRChrA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  373. {
  374.     LPCSTR lpFound = NULL;
  375.     ASSERT(lpStart);
  376.     ASSERT(!lpEnd || lpEnd <= lpStart + lstrlenA(lpStart));
  377.     if (!lpEnd)
  378.         lpEnd = lpStart + lstrlenA(lpStart);
  379.     for ( ; lpStart < lpEnd; lpStart = AnsiNext(lpStart))
  380.     {
  381.         // (ChrCmp returns FALSE when characters match)
  382.         if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  383.             lpFound = lpStart;
  384.     }
  385.     return ((LPSTR)lpFound);
  386. }
  387. LPWSTR StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  388. {
  389.     LPCWSTR lpFound = NULL;
  390.     if (!lpEnd)
  391.         lpEnd = lpStart + lstrlenW(lpStart);
  392.     for ( ; lpStart < lpEnd; lpStart++)
  393.     {
  394.         if (!ChrCmpW_inline(*lpStart, wMatch))
  395.             lpFound = lpStart;
  396.     }
  397.     return ((LPWSTR)lpFound);
  398. }
  399. /*
  400.  * StrChrI - Find first occurrence of character in string, case insensitive
  401.  * Assumes   lpStart points to start of null terminated string
  402.  *           wMatch  is the character to match
  403.  * returns ptr to the first occurrence of ch in str, NULL if not found.
  404.  */
  405. LPSTR StrChrIA(LPCSTR lpStart, WORD wMatch)
  406. {
  407.     wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  408.     for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  409.     {
  410.         if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  411.             return((LPSTR)lpStart);
  412.     }
  413.     return (NULL);
  414. }
  415. LPWSTR StrChrIW(LPCWSTR lpStart, WCHAR wMatch)
  416. {
  417.     for ( ; *lpStart; lpStart++)
  418.     {
  419.         if (!ChrCmpIW(*lpStart, wMatch))
  420.             return((LPWSTR)lpStart);
  421.     }
  422.     return (NULL);
  423. }
  424. /*
  425.  * StrRChrI - Find last occurrence of character in string, case insensitive
  426.  * Assumes   lpStart points to start of string
  427.  *           lpEnd   points to end of string (NOT included in search)
  428.  *           wMatch  is the character to match
  429.  * returns ptr to the last occurrence of ch in str, NULL if not found.
  430.  */
  431. LPSTR StrRChrIA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  432. {
  433.     LPCSTR lpFound = NULL;
  434.     ASSERT(lpStart);
  435.     ASSERT(!lpEnd || lpEnd <= lpStart + lstrlenA(lpStart));
  436.     if (!lpEnd)
  437.         lpEnd = lpStart + lstrlenA(lpStart);
  438.     wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  439.     for ( ; lpStart < lpEnd; lpStart = AnsiNext(lpStart))
  440.     {
  441.         if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  442.             lpFound = lpStart;
  443.     }
  444.     return ((LPSTR)lpFound);
  445. }
  446. LPWSTR StrRChrIW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  447. {
  448.     LPCWSTR lpFound = NULL;
  449.     if (!lpEnd)
  450.         lpEnd = lpStart + lstrlenW(lpStart);
  451.     for ( ; lpStart < lpEnd; lpStart++)
  452.     {
  453.         if (!ChrCmpIW(*lpStart, wMatch))
  454.             lpFound = lpStart;
  455.     }
  456.     return ((LPWSTR)lpFound);
  457. }
  458. /*----------------------------------------------------------
  459. Purpose: Returns a pointer to the first occurrence of a character
  460.          in psz that belongs to the set of characters in pszSet.
  461.          The search does not include the null terminator.
  462.          If psz contains no characters that are in the set of
  463.          characters in pszSet, this function returns NULL.
  464.          This function is DBCS-safe.
  465. Returns: see above
  466. Cond:    --
  467. */
  468. LPSTR
  469. WINAPI
  470. StrPBrkA(LPCSTR psz, LPCSTR pszSet)
  471. {
  472.     LPCSTR pszSetT;
  473.     ASSERT(psz);
  474.     ASSERT(pszSet);
  475.     while (*psz)
  476.     {
  477.         for (pszSetT = pszSet; *pszSetT; pszSetT = CharNextA(pszSetT))
  478.         {
  479.             if (*psz == *pszSetT)
  480.             {
  481.                 // Found first character that matches
  482.                 return (LPSTR)psz;      // Const -> non-const
  483.             }
  484.         }
  485.         psz = CharNextA(psz);
  486.     }
  487.     return NULL;
  488. }
  489. /*----------------------------------------------------------
  490. Purpose: Returns a pointer to the first occurrence of a character
  491.          in psz that belongs to the set of characters in pszSet.
  492.          The search does not include the null terminator.
  493. Returns: see above
  494. Cond:    --
  495. */
  496. LPWSTR WINAPI StrPBrkW(LPCWSTR psz, LPCWSTR pszSet)
  497. {
  498.     LPCWSTR pszSetT;
  499.     ASSERT(psz);
  500.     ASSERT(pszSet);
  501.     // Go thru the string to be inspected
  502.     while (*psz)
  503.     {
  504.         // Go thru the char set
  505.         for (pszSetT = pszSet; *pszSetT; pszSetT++)
  506.         {
  507.             if (*psz == *pszSetT)
  508.             {
  509.                 // Found first character that matches
  510.                 return (LPWSTR)psz;     // Const -> non-const
  511.             }
  512.         }
  513.         psz++;
  514.     }
  515.     return NULL;
  516. }
  517. int WINAPI StrToIntA(LPCSTR lpSrc)
  518. {
  519.     int n = 0;
  520.     BOOL bNeg = FALSE;
  521.     if (*lpSrc == '-') {
  522.         bNeg = TRUE;
  523.         lpSrc++;
  524.     }
  525.     while (IS_DIGITA(*lpSrc)) {
  526.         n *= 10;
  527.         n += *lpSrc - '0';
  528.         lpSrc++;
  529.     }
  530.     return bNeg ? -n : n;
  531. }
  532. int WINAPI StrToIntW(LPCWSTR lpSrc)
  533. {
  534.     int n = 0;
  535.     BOOL bNeg = FALSE;
  536.     if (*lpSrc == L'-') {
  537.         bNeg = TRUE;
  538.         lpSrc++;
  539.     }
  540.     while (IS_DIGITW(*lpSrc)) {
  541.         n *= 10;
  542.         n += *lpSrc - L'0';
  543.         lpSrc++;
  544.     }
  545.     return bNeg ? -n : n;
  546. }
  547. /*----------------------------------------------------------
  548. Purpose: Special verion of atoi.  Supports hexadecimal too.
  549.          If this function returns FALSE, *piRet is set to 0.
  550. Returns: TRUE if the string is a number, or contains a partial number
  551.          FALSE if the string is not a number
  552.         dwFlags are STIF_ bitfield
  553. Cond:    --
  554. */
  555. BOOL WINAPI StrToIntExA(LPCSTR pszString, DWORD dwFlags, int *piRet)
  556. {
  557.     BOOL bRet;
  558.     int n;
  559.     BOOL bNeg = FALSE;
  560.     LPCSTR psz;
  561.     LPCSTR pszAdj;
  562.     // Skip leading whitespace
  563.     //
  564.     for (psz = pszString; *psz == ' ' || *psz == 'n' || *psz == 't'; psz = CharNextA(psz))
  565.         ;
  566.     // Determine possible explicit signage
  567.     //
  568.     if (*psz == '+' || *psz == '-')
  569.     {
  570.         bNeg = (*psz == '+') ? FALSE : TRUE;
  571.         psz++;
  572.     }
  573.     // Or is this hexadecimal?
  574.     //
  575.     pszAdj = CharNextA(psz);
  576.     if ((STIF_SUPPORT_HEX & dwFlags) &&
  577.         *psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
  578.     {
  579.         // Yes
  580.         // (Never allow negative sign with hexadecimal numbers)
  581.         bNeg = FALSE;
  582.         psz = CharNextA(pszAdj);
  583.         pszAdj = psz;
  584.         // Do the conversion
  585.         //
  586.         for (n = 0; ; psz = CharNextA(psz))
  587.         {
  588.             if (IS_DIGITA(*psz))
  589.                 n = 0x10 * n + *psz - '0';
  590.             else
  591.             {
  592.                 CHAR ch = *psz;
  593.                 int n2;
  594.                 if (ch >= 'a')
  595.                     ch -= 'a' - 'A';
  596.                 n2 = ch - 'A' + 0xA;
  597.                 if (n2 >= 0xA && n2 <= 0xF)
  598.                     n = 0x10 * n + n2;
  599.                 else
  600.                     break;
  601.             }
  602.         }
  603.         // Return TRUE if there was at least one digit
  604.         bRet = (psz != pszAdj);
  605.     }
  606.     else
  607.     {
  608.         // No
  609.         pszAdj = psz;
  610.         // Do the conversion
  611.         for (n = 0; IS_DIGITA(*psz); psz = CharNextA(psz))
  612.             n = 10 * n + *psz - '0';
  613.         // Return TRUE if there was at least one digit
  614.         bRet = (psz != pszAdj);
  615.     }
  616.     *piRet = bNeg ? -n : n;
  617.     return bRet;
  618. }
  619. /*----------------------------------------------------------
  620.  Purpose: Wide-char wrapper for StrToIntExA.
  621.  Returns: see StrToIntExA
  622.  Cond:    --
  623.  */
  624. BOOL WINAPI StrToIntExW(
  625.     LPCWSTR   pwszString,
  626.     DWORD     dwFlags,          // STIF_ bitfield
  627.     int FAR * piRet)
  628. {
  629.     // Most strings will simply use this temporary buffer, but AnsiFromUnicode
  630.     // will allocate a buffer if the supplied string is bigger.
  631.     CHAR szBuf[MAX_PATH];
  632.     LPSTR pszString;
  633.     BOOL bRet = AnsiFromUnicode(&pszString, pwszString, szBuf, SIZECHARS(szBuf));
  634.     if (bRet)
  635.     {
  636.         bRet = StrToIntExA(pszString, dwFlags, piRet);
  637.         AnsiFromUnicode(&pszString, NULL, szBuf, 0);
  638.     }
  639.     return bRet;
  640. }
  641. /*----------------------------------------------------------
  642.  Purpose: Returns an integer value specifying the length of
  643.  the substring in psz that consists entirely of
  644.  characters in pszSet.  If psz begins with a character
  645.  not in pszSet, then this function returns 0.
  646.  This is a DBCS-safe version of the CRT strspn().
  647.  Returns: see above
  648.  Cond:    --
  649.  */
  650. int
  651. WINAPI
  652. StrSpnA(
  653.         LPCSTR psz,
  654.         LPCSTR pszSet)
  655. {
  656.     LPCSTR pszT;
  657.     LPCSTR pszSetT;
  658.     // Go thru the string to be inspected
  659.     for (pszT = psz; *pszT; pszT = CharNextA(pszT))
  660.     {
  661.         // Go thru the char set
  662.         for (pszSetT = pszSet; *pszSetT; pszSetT = CharNextA(pszSetT))
  663.         {
  664.             if (*pszSetT == *pszT)
  665.             {
  666.                 if ( !IsDBCSLeadByte(*pszSetT) )
  667.                 {
  668.                     break;      // Chars match
  669.                 }
  670.                 else if (pszSetT[1] == pszT[1])
  671.                 {
  672.                     break;      // Chars match
  673.                 }
  674.             }
  675.         }
  676.         // End of char set?
  677.         if (0 == *pszSetT)
  678.         {
  679.             break;      // Yes, no match on this inspected char
  680.         }
  681.     }
  682.     return (int)(pszT - psz);
  683. }
  684. /*----------------------------------------------------------
  685.  Purpose: Returns an integer value specifying the length of
  686.  the substring in psz that consists entirely of
  687.  characters in pszSet.  If psz begins with a character
  688.  not in pszSet, then this function returns 0.
  689.  This is a DBCS-safe version of the CRT strspn().
  690.  Returns: see above
  691.  Cond:    --
  692.  */
  693. STDAPI_(int) StrSpnW(LPCWSTR psz, LPCWSTR pszSet)
  694. {
  695.     LPCWSTR pszT;
  696.     LPCWSTR pszSetT;
  697.     ASSERT(psz);
  698.     ASSERT(pszSet);
  699.     // Go thru the string to be inspected
  700.     for (pszT = psz; *pszT; pszT++)
  701.     {
  702.         // Go thru the char set
  703.         for (pszSetT = pszSet; *pszSetT != *pszT; pszSetT++)
  704.         {
  705.             if (0 == *pszSetT)
  706.             {
  707.                 // Reached end of char set without finding a match
  708.                 return (int)(pszT - psz);
  709.             }
  710.         }
  711.     }
  712.     return (int)(pszT - psz);
  713. }
  714. // StrCSpn: return index to first char of lpStr that is present in lpSet.
  715. // Includes the NUL in the comparison; if no lpSet chars are found, returns
  716. // the index to the NUL in lpStr.
  717. // Just like CRT strcspn.
  718. //
  719. int StrCSpnA(LPCSTR lpStr, LPCSTR lpSet)
  720. {
  721.     // nature of the beast: O(lpStr*lpSet) work
  722.     LPCSTR lp = lpStr;
  723.     if (!lpStr || !lpSet)
  724.         return 0;
  725.     while (*lp)
  726.     {
  727.         if (StrChrA(lpSet, READNATIVEWORD(lp)))
  728.             return (int)(lp-lpStr);
  729.         lp = AnsiNext(lp);
  730.     }
  731.     return (int)(lp-lpStr); // ==lstrlen(lpStr)
  732. }
  733. int StrCSpnW(LPCWSTR lpStr, LPCWSTR lpSet)
  734. {
  735.     // nature of the beast: O(lpStr*lpSet) work
  736.     LPCWSTR lp = lpStr;
  737.     if (!lpStr || !lpSet)
  738.         return 0;
  739.     while (*lp)
  740.     {
  741.         if (StrChrW(lpSet, *lp))
  742.             return (int)(lp-lpStr);
  743.         lp++;
  744.     }
  745.     return (int)(lp-lpStr); // ==lstrlen(lpStr)
  746. }
  747. // StrCSpnI: case-insensitive version of StrCSpn.
  748. //
  749. int StrCSpnIA(LPCSTR lpStr, LPCSTR lpSet)
  750. {
  751.     // nature of the beast: O(lpStr*lpSet) work
  752.     LPCSTR lp = lpStr;
  753.     if (!lpStr || !lpSet)
  754.         return 0;
  755.     while (*lp)
  756.     {
  757.         if (StrChrIA(lpSet, READNATIVEWORD(lp)))
  758.             return (int)(lp-lpStr);
  759.         lp = AnsiNext(lp);
  760.     }
  761.     return (int)(lp-lpStr); // ==lstrlen(lpStr)
  762. }
  763. int StrCSpnIW(LPCWSTR lpStr, LPCWSTR lpSet)
  764. {
  765.     // nature of the beast: O(lpStr*lpSet) work
  766.     LPCWSTR lp = lpStr;
  767.     if (!lpStr || !lpSet)
  768.         return 0;
  769.     while (*lp)
  770.     {
  771.         if (StrChrIW(lpSet, *lp))
  772.             return (int)(lp-lpStr);
  773.         lp++;
  774.     }
  775.     return (int)(lp-lpStr); // ==lstrlen(lpStr)
  776. }
  777. /*
  778.  * StrCmpN      - Compare n bytes
  779.  *
  780.  * returns   See lstrcmp return values.
  781.  */
  782. int _StrCmpNA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar, BOOL fMBCS)
  783. {
  784.     LPCSTR lpszEnd = lpStr1 + nChar;
  785.     char sz1[4];
  786.     char sz2[4];
  787.     if (fMBCS) {
  788.         for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1 = AnsiNext(lpStr1), lpStr2 = AnsiNext(lpStr2)) {
  789.             WORD w1;
  790.             WORD w2;
  791.     
  792.             // If either pointer is at the null terminator already,
  793.             // we want to copy just one byte to make sure we don't read 
  794.             // past the buffer (might be at a page boundary).
  795.     
  796.             w1 = (*lpStr1) ? READNATIVEWORD(lpStr1) : 0;
  797.             w2 = (*lpStr2) ? READNATIVEWORD(lpStr2) : 0;
  798.     
  799.             // (ChrCmpA returns FALSE if the characters match)
  800.     
  801.             // Do the characters match?
  802.             if (ChrCmpA_inline(w1, w2)) 
  803.             {
  804.                 // No; determine the lexical value of the comparison
  805.                 // (since ChrCmp just returns true/false).
  806.     
  807.                 // Since the character may be a DBCS character; we
  808.                 // copy two bytes into each temporary buffer 
  809.                 // (in preparation for the lstrcmp call).
  810.     
  811.                 (*(WORD *)sz1) = w1;
  812.                 (*(WORD *)sz2) = w2;
  813.     
  814.                 // Add null terminators to temp buffers
  815.                 *AnsiNext(sz1) = 0;
  816.                 *AnsiNext(sz2) = 0;
  817.                 return lstrcmpA(sz1, sz2);
  818.             }
  819.         }
  820.     } else {
  821.         for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
  822.             if (*lpStr1 != *lpStr2) {
  823.                 // No; determine the lexical value of the comparison
  824.                 // (since ChrCmp just returns true/false).
  825.                 sz1[0] = *lpStr1;
  826.                 sz2[0] = *lpStr2;
  827.                 sz1[1] = sz2[1] = '';
  828.                 return lstrcmpA(sz1, sz2);
  829.             }
  830.         }
  831.     }
  832.     return 0;
  833. }
  834. STDAPI_(int) StrCmpNA(LPCSTR psz1, LPCSTR psz2, int nChar)
  835. {
  836.     CPINFO cpinfo;
  837.     return _StrCmpNA(psz1, psz2, nChar, GetCPInfo(CP_ACP, &cpinfo) && cpinfo.LeadByte[0]);
  838. }
  839. // cch1 and cch2 are the maximum # of chars to compare
  840. int _StrCmpLocaleW(DWORD dwFlags, LPCWSTR psz1, int cch1, LPCWSTR psz2, int cch2)
  841. {
  842.     int i = CompareStringW(GetThreadLocale(), dwFlags, psz1, cch1, psz2, cch2);
  843.     if (!i)
  844.     {
  845.         i = CompareStringW(LOCALE_SYSTEM_DEFAULT, dwFlags, psz1, cch1, psz2, cch2);
  846.     }
  847.     return i - CSTR_EQUAL;
  848. }
  849. int _StrCmpLocaleA(DWORD dwFlags, LPCSTR psz1, int cch1, LPCSTR psz2, int cch2)
  850. {
  851.     int i = CompareStringA(GetThreadLocale(), dwFlags, psz1, cch1, psz2, cch2);
  852.     if (!i)
  853.     {
  854.         i = CompareStringA(LOCALE_SYSTEM_DEFAULT, dwFlags, psz1, cch1, psz2, cch2);
  855.     }
  856.     return i - CSTR_EQUAL;
  857. }
  858. STDAPI_(int) StrCmpNW(LPCWSTR psz1, LPCWSTR psz2, int nChar)
  859. {
  860.     return _StrCmpLocaleW(NORM_STOP_ON_NULL, psz1, nChar, psz2, nChar);
  861. }
  862. /*
  863.  * Compare n bytes, case insensitive
  864.  *
  865.  * returns   See lstrcmpi return values.
  866.  */
  867. int StrCmpNIA(LPCSTR psz1, LPCSTR psz2, int nChar)
  868. {
  869.     if (g_bRunningOnNT)
  870.     {
  871.         return _StrCmpLocaleA(NORM_IGNORECASE | NORM_STOP_ON_NULL,  psz1, nChar, psz2, nChar);
  872.     }
  873.     else
  874.     {
  875.         //  Win95 doesn't support NORM_STOP_ON_NULL
  876.         int i;
  877.         LPCSTR lpszEnd = psz1 + nChar;
  878.         for ( ; (lpszEnd > psz1) && (*psz1 || *psz2); (psz1 = AnsiNext(psz1)), (psz2 = AnsiNext(psz2))) 
  879.         {
  880.             WORD w1, w2;
  881.             // If either pointer is at the null terminator already,
  882.             // we want to copy just one byte to make sure we don't read 
  883.             // past the buffer (might be at a page boundary).
  884.             if (IsAsciiA(*psz1) && IsAsciiA(*psz2))
  885.             {
  886.                 i = Ascii_ToLowerA(*psz1) - Ascii_ToLowerA(*psz2);
  887.             }
  888.             else
  889.             {
  890.                 w1 = (*psz1) ? READNATIVEWORD(psz1) : 0;
  891.                 w2 = (UINT)(IsDBCSLeadByte(*psz2)) ? (UINT)READNATIVEWORD(psz2) : (WORD)(BYTE)(*psz2);
  892.                 i = ChrCmpIA(w1, w2);
  893.             }
  894.             if (i)
  895.             {
  896.                 if (i < 0)
  897.                     return -1;
  898.                 else
  899.                     return 1;
  900.             }
  901.         }
  902.         return 0;
  903.     }
  904. }
  905. int StrCmpNIW(LPCWSTR psz1, LPCWSTR psz2, int nChar)
  906. {
  907.     return _StrCmpLocaleW(NORM_IGNORECASE | NORM_STOP_ON_NULL, psz1, nChar, psz2, nChar);
  908. }
  909. /*
  910.  * StrRStrI      - Search for last occurrence of a substring
  911.  *
  912.  * Assumes   lpSource points to the null terminated source string
  913.  *           lpLast points to where to search from in the source string
  914.  *           lpLast is not included in the search
  915.  *           lpSrch points to string to search for
  916.  * returns   last occurrence of string if successful; NULL otherwise
  917.  */
  918. LPSTR StrRStrIA(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch)
  919. {
  920.     LPCSTR lpFound = NULL;
  921.     WORD   wMatch;
  922.     UINT   uLen = 0;
  923.     LPCSTR  lpStart = NULL;
  924.     
  925.     if (!lpLast)
  926.         lpLast = lpSource + lstrlenA(lpSource);
  927.     if (lpSource >= lpLast || *lpSrch == 0)
  928.         return NULL;
  929.     wMatch = READNATIVEWORD(lpSrch);
  930.     wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  931.     
  932.     uLen = lstrlenA(lpSrch);
  933.     lpStart = lpSource;
  934.     while (*lpStart && (lpStart < lpLast))
  935.     {
  936.         if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  937.         {   
  938.             if (StrCmpNIA(lpStart, lpSrch, uLen) == 0)
  939.                 lpFound = lpStart;
  940.         }   
  941.         lpStart = AnsiNext(lpStart);
  942.     }
  943.     
  944.     return((LPSTR)lpFound);
  945. }
  946. LPWSTR StrRStrIW(LPCWSTR lpSource, LPCWSTR lpLast, LPCWSTR lpSrch)
  947. {
  948.     LPCWSTR lpFound = NULL;
  949.     WCHAR   wMatch;
  950.     UINT    uLen = 0;
  951.     LPCWSTR  lpStart = NULL;
  952.     if (!lpLast)
  953.         lpLast = lpSource + lstrlenW(lpSource);
  954.     if (lpSource >= lpLast || *lpSrch == 0)
  955.         return NULL;
  956.     wMatch = *lpSrch;
  957.     uLen = lstrlenW(lpSrch);
  958.     lpStart = lpSource;
  959.     while (*lpStart && (lpStart < lpLast))
  960.     {
  961.         if (!ChrCmpIW(*lpStart, wMatch))
  962.         {   
  963.             if (StrCmpNIW(lpStart, lpSrch, uLen) == 0)
  964.                 lpFound = lpStart;
  965.         }   
  966.         lpStart++;
  967.     }
  968.     
  969.     return((LPWSTR)lpFound);
  970. }
  971. /*
  972.  * StrStr      - Search for first occurrence of a substring
  973.  *
  974.  * Assumes   lpSource points to source string
  975.  *           lpSrch points to string to search for
  976.  * returns   first occurrence of string if successful; NULL otherwise
  977.  */
  978. LPSTR StrStrA(LPCSTR lpFirst, LPCSTR lpSrch)
  979. {
  980.     UINT uLen;
  981.     WORD wMatch;
  982.     CPINFO cpinfo;
  983.     BOOL fMBCS = GetCPInfo(CP_ACP, &cpinfo) && cpinfo.LeadByte[0];
  984.     uLen = (UINT)lstrlenA(lpSrch);
  985.     wMatch = READNATIVEWORD(lpSrch);
  986.     for ( ; (lpFirst=_StrChrA(lpFirst, wMatch, fMBCS))!=0 && _StrCmpNA(lpFirst, lpSrch, uLen, fMBCS);
  987.          lpFirst=AnsiNext(lpFirst))
  988.         continue; /* continue until we hit the end of the string or get a match */
  989.     return((LPSTR)lpFirst);
  990. }
  991. LPWSTR StrStrW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  992. {
  993.     UINT uLen;
  994.     WCHAR wMatch;
  995.     if (!lpSrch || !lpFirst)
  996.         return NULL;
  997.     uLen = (UINT)lstrlenW(lpSrch);
  998.     wMatch = *lpSrch;
  999.     for ( ; (lpFirst=StrChrW(lpFirst, wMatch))!=0 && StrCmpNW(lpFirst, lpSrch, uLen);
  1000.          lpFirst++)
  1001.         continue; /* continue until we hit the end of the string or get a match */
  1002.     return (LPWSTR)lpFirst;
  1003. }
  1004. /*
  1005.  * StrStrI   - Search for first occurrence of a substring, case insensitive
  1006.  *
  1007.  * Assumes   lpFirst points to source string
  1008.  *           lpSrch points to string to search for
  1009.  * returns   first occurrence of string if successful; NULL otherwise
  1010.  */
  1011. LPSTR StrStrIA(LPCSTR lpFirst, LPCSTR lpSrch)
  1012. {
  1013.     UINT uLen = (UINT)lstrlenA(lpSrch);
  1014.     WORD wMatch = READNATIVEWORD(lpSrch);
  1015.     for ( ; (lpFirst = StrChrIA(lpFirst, wMatch)) != 0 && StrCmpNIA(lpFirst, lpSrch, uLen);
  1016.          lpFirst=AnsiNext(lpFirst))
  1017.         continue; /* continue until we hit the end of the string or get a match */
  1018.     return (LPSTR)lpFirst;
  1019. }
  1020. LPWSTR StrStrIW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  1021. {
  1022.     UINT uLen = (UINT)lstrlenW(lpSrch);
  1023.     WCHAR wMatch = *lpSrch;
  1024.     for ( ; (lpFirst = StrChrIW(lpFirst, wMatch)) != 0 && StrCmpNIW(lpFirst, lpSrch, uLen);
  1025.          lpFirst++)
  1026.         continue; /* continue until we hit the end of the string or get a match */
  1027.     return (LPWSTR)lpFirst;
  1028. }
  1029. LPSTR StrDupA(LPCSTR psz)
  1030. {
  1031.     LPSTR pszRet = (LPSTR)LocalAlloc(LPTR, (lstrlenA(psz) + 1) * sizeof(*pszRet));
  1032.     if (pszRet) 
  1033.     {
  1034.         lstrcpyA(pszRet, psz);
  1035.     }
  1036.     return pszRet;
  1037. }
  1038. LPWSTR StrDupW(LPCWSTR psz)
  1039. {
  1040.     LPWSTR pszRet = (LPWSTR)LocalAlloc(LPTR, (lstrlenW(psz) + 1) * sizeof(*pszRet));
  1041.     if (pszRet) 
  1042.     {
  1043.         StrCpyW(pszRet, psz);
  1044.     }
  1045.     return pszRet;
  1046. }
  1047. void _StrOut(LPSTR* ppszBuf, HMODULE hmod, UINT idRes, DWORD* pdwTimeS, int* pdigits, UINT iDiv)
  1048. {
  1049.     if (*pdigits)
  1050.     {
  1051.         DWORD dwCur = *pdwTimeS/iDiv;
  1052.         if (dwCur || iDiv==1) 
  1053.         {
  1054.             DWORD dwBase;
  1055.             CHAR szBuf[64], szTemplate[64];
  1056.             LPSTR pszBuf = szBuf;
  1057.             *pdwTimeS -= dwCur*iDiv;
  1058.             for (dwBase=1; dwCur/(dwBase*10); dwBase*=10);
  1059.             DebugMsg(DM_INTERVAL, TEXT("dwCur, dwBase, *pdwTimeS = %d, %d, %d"), dwCur, dwBase, *pdwTimeS);
  1060.             //
  1061.             // LATER: We could use atoi if we mathematically trancate
  1062.             //  the numbers based on digits.
  1063.             //
  1064.             for (;dwBase; dwBase/=10, pszBuf++) 
  1065.             {
  1066.                 if (*pdigits) 
  1067.                 {
  1068.                     DWORD i = dwCur/dwBase;
  1069.                     dwCur -= i*dwBase;
  1070.                     *pszBuf = '0'+(unsigned short)i;
  1071.                     (*pdigits)--;
  1072.                 } 
  1073.                 else 
  1074.                 {
  1075.                     *pszBuf = '0';
  1076.                 }
  1077.             }
  1078.             *pszBuf = '';
  1079.             LoadStringA(hmod, idRes, szTemplate, ARRAYSIZE(szTemplate));
  1080.             wsprintfA(*ppszBuf, szTemplate, szBuf);
  1081.             (*ppszBuf) += lstrlenA(*ppszBuf);
  1082.         }
  1083.     }
  1084. }
  1085. BOOL _StrFromTimeInterval(LPSTR szBuf, DWORD dwTimeMS, int digits)
  1086. {
  1087.     DWORD dwTimeS = (dwTimeMS+500)/1000;
  1088.     LPSTR pszBuf = szBuf;
  1089.     DebugMsg(DM_INTERVAL, TEXT("dwTimeS = %d"), dwTimeS);
  1090.     szBuf = '';
  1091.     _StrOut(&pszBuf, g_hinst, IDS_HOUR, &dwTimeS, &digits, 3600);
  1092.     _StrOut(&pszBuf, g_hinst, IDS_MIN, &dwTimeS, &digits, 60);
  1093.     _StrOut(&pszBuf, g_hinst, IDS_SEC, &dwTimeS, &digits, 1);
  1094.     return TRUE;
  1095. }
  1096. //
  1097. //  This API converts a given time-interval (in msec) into a human readable
  1098. // string.
  1099. //
  1100. // Parameters:
  1101. //  pszOut   -- Specifies the string buffer. NULL is valid to query size.
  1102. //  cchMax   -- Specifies the size of buffer in char/WCHAR
  1103. //  dwTimeMS -- Specifies the time interval in msec
  1104. //  digits   -- Specifies the minimum number of digits to be displayed
  1105. //
  1106. // Returns:
  1107. //  Number of characters in the buffer (not including the terminator).
  1108. //
  1109. // Exmaples:
  1110. //  dwTimeMS digits     output
  1111. //   34000     3         34 sec
  1112. //   34000     2         34 sec
  1113. //   34000     1         30 sec
  1114. //   74000     3         1 min 14 sec
  1115. //   74000     2         1 min 10 sec
  1116. //   74000     1         1 min
  1117. //
  1118. int StrFromTimeIntervalA(LPSTR pszOut, UINT cchMax, DWORD dwTimeMS, int digits)
  1119. {
  1120.     CHAR szBuf[256];
  1121.     int cchRet = 0;
  1122.     if (_StrFromTimeInterval(szBuf, dwTimeMS, digits)) 
  1123.     {
  1124.         if (pszOut) 
  1125.         {
  1126.             lstrcpynA(pszOut, szBuf, cchMax);
  1127.             cchRet = lstrlenA(pszOut);
  1128.         }
  1129.         else 
  1130.         {
  1131.             cchRet = lstrlenA(szBuf);
  1132.         }
  1133.     }
  1134.     return cchRet;
  1135. }
  1136. int StrFromTimeIntervalW(LPWSTR pwszOut, UINT cchMax, DWORD dwTimeMS, int digits)
  1137. {
  1138.     CHAR szBuf[256];
  1139.     int cchRet = 0;
  1140.     if (_StrFromTimeInterval(szBuf, dwTimeMS, digits)) 
  1141.     {
  1142.         // - 1 because MultiByteToWideChar() also counts the NULL termination
  1143.         cchRet = MultiByteToWideChar(CP_ACP, 0, szBuf, -1, pwszOut, cchMax) - 1;
  1144.     }
  1145.     return cchRet;
  1146. }
  1147. /*
  1148.  * IntlStrEq
  1149.  *
  1150.  * returns TRUE if strings are equal, FALSE if not
  1151.  */
  1152. BOOL StrIsIntlEqualA(BOOL fCaseSens, LPCSTR lpString1, LPCSTR lpString2, int nChar) 
  1153. {
  1154.     DWORD dwFlags = fCaseSens ? LOCALE_USE_CP_ACP : (NORM_IGNORECASE | LOCALE_USE_CP_ACP);
  1155.     if (g_bRunningOnNT)
  1156.     {
  1157.         dwFlags |= NORM_STOP_ON_NULL;   // only supported on NT
  1158.     }
  1159.     else if (nChar != -1)
  1160.     {
  1161.         // On Win9x we have to do the check manually
  1162.         //
  1163.         int cch = 0;
  1164.         LPCSTR psz1 = lpString1;
  1165.         LPCSTR psz2 = lpString2;
  1166.         while(*psz1 != 0 && *psz2 != 0 && cch < nChar) 
  1167.         {
  1168.             psz1 = CharNextA(psz1);
  1169.             psz2 = CharNextA(psz2);
  1170.             cch = (int) min(psz1 - lpString1, psz2 - lpString2);
  1171.         }
  1172.         // add one in for terminating ''
  1173.         cch++;
  1174.         if (cch < nChar)
  1175.             nChar = cch;
  1176.     }
  1177.     return 0 == _StrCmpLocaleA(dwFlags, lpString1, nChar, lpString2, nChar);
  1178. }
  1179. BOOL StrIsIntlEqualW(BOOL fCaseSens, LPCWSTR psz1, LPCWSTR psz2, int nChar) 
  1180. {
  1181.     return 0 == _StrCmpLocaleW(fCaseSens ? NORM_STOP_ON_NULL : NORM_IGNORECASE | NORM_STOP_ON_NULL, 
  1182.         psz1, nChar, psz2, nChar);
  1183. }
  1184. // This is stolen from shell32 - util.c
  1185. #define LODWORD(_qw)    (DWORD)(_qw)
  1186. const short c_aOrders[] = {IDS_BYTES, IDS_ORDERKB, IDS_ORDERMB,
  1187.                           IDS_ORDERGB, IDS_ORDERTB, IDS_ORDERPB, IDS_ORDEREB};
  1188. void Int64ToStr(LONGLONG n, LPWSTR lpBuffer)
  1189. {
  1190.     WCHAR szTemp[40];
  1191.     LONGLONG  iChr;
  1192.     iChr = 0;
  1193.     do {
  1194.         szTemp[iChr++] = L'0' + (WCHAR)(n % 10);
  1195.         n = n / 10;
  1196.     } while (n != 0);
  1197.     do {
  1198.         iChr--;
  1199.         *lpBuffer++ = szTemp[iChr];
  1200.     } while (iChr != 0);
  1201.     *lpBuffer++ = L'';
  1202. }
  1203. //
  1204. //  Obtain NLS info about how numbers should be grouped.
  1205. //
  1206. //  The annoying thing is that LOCALE_SGROUPING and NUMBERFORMAT
  1207. //  have different ways of specifying number grouping.
  1208. //
  1209. //          LOCALE      NUMBERFMT      Sample   Country
  1210. //
  1211. //          3;0         3           1,234,567   United States
  1212. //          3;2;0       32          12,34,567   India
  1213. //          3           30           1234,567   ??
  1214. //
  1215. //  Not my idea.  That's the way it works.
  1216. //
  1217. //  Bonus treat - Win9x doesn't support complex number formats,
  1218. //  so we return only the first number.
  1219. //
  1220. UINT GetNLSGrouping(void)
  1221. {
  1222.     UINT grouping;
  1223.     LPWSTR psz;
  1224.     WCHAR szGrouping[32];
  1225.     // If no locale info, then assume Western style thousands
  1226.     if (!GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szGrouping, ARRAYSIZE(szGrouping)))
  1227.         return 3;
  1228.     grouping = 0;
  1229.     psz = szGrouping;
  1230.     if (g_bRunningOnNT)
  1231.     {
  1232.         for (;;)
  1233.         {
  1234.             if (*psz == L'0') break;             // zero - stop
  1235.             else if ((UINT)(*psz - L'0') < 10)   // digit - accumulate it
  1236.                 grouping = grouping * 10 + (UINT)(*psz - L'0');
  1237.             else if (*psz)                      // punctuation - ignore it
  1238.                 { }
  1239.             else                                // end of string, no "0" found
  1240.             {
  1241.                 grouping = grouping * 10;       // put zero on end (see examples)
  1242.                 break;                          // and finished
  1243.             }
  1244.             psz++;
  1245.         }
  1246.     }
  1247.     else
  1248.     {
  1249.         // Win9x - take only the first grouping
  1250.         grouping = StrToIntW(szGrouping);
  1251.     }
  1252.     return grouping;
  1253. }
  1254. // Sizes of various stringized numbers
  1255. #define MAX_INT64_SIZE  30              // 2^64 is less than 30 chars long
  1256. #define MAX_COMMA_NUMBER_SIZE   (MAX_INT64_SIZE + 10)
  1257. // takes a DWORD add commas etc to it and puts the result in the buffer
  1258. LPWSTR CommifyString(LONGLONG n, LPWSTR pszBuf, UINT cchBuf)
  1259. {
  1260.     WCHAR szNum[MAX_COMMA_NUMBER_SIZE], szSep[5];
  1261.     NUMBERFMTW nfmt;
  1262.     nfmt.NumDigits = 0;
  1263.     nfmt.LeadingZero = 0;
  1264.     nfmt.Grouping = GetNLSGrouping();
  1265.     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, ARRAYSIZE(szSep));
  1266.     nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
  1267.     nfmt.NegativeOrder = 0;
  1268.     Int64ToStr(n, szNum);
  1269.     if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szNum, &nfmt, pszBuf, cchBuf) == 0)
  1270.         StrCpyNW(pszBuf, szNum, cchBuf);
  1271.     return pszBuf;
  1272. }
  1273. /* converts numbers into sort formats
  1274.  *      532     -> 523 bytes
  1275.  *      1340    -> 1.3KB
  1276.  *      23506   -> 23.5KB
  1277.  *              -> 2.4MB
  1278.  *              -> 5.2GB
  1279.  */
  1280. LPWSTR StrFormatByteSizeW(LONGLONG n, LPWSTR pszBuf, UINT cchBuf)
  1281. {
  1282.     WCHAR szWholeNum[32], szOrder[32];
  1283.     int iOrder;
  1284.     // If the size is less than 1024, then the order should be bytes we have nothing
  1285.     // more to figure out
  1286.     if (n < 1024) 
  1287.     {
  1288.         wnsprintfW(szWholeNum, ARRAYSIZE(szWholeNum), L"%d", LODWORD(n));
  1289.         iOrder = 0;
  1290.     }
  1291.     else
  1292.     {
  1293.         UINT uInt, uLen, uDec;
  1294.         WCHAR szFormat[8];
  1295.         // Find the right order
  1296.         for (iOrder = 1; iOrder < ARRAYSIZE(c_aOrders) -1 && n >= 1000L * 1024L; n >>= 10, iOrder++);
  1297.             /* do nothing */
  1298.         uInt = LODWORD(n >> 10);
  1299.         CommifyString(uInt, szWholeNum, ARRAYSIZE(szWholeNum));
  1300.         uLen = lstrlenW(szWholeNum);
  1301.         if (uLen < 3)
  1302.         {
  1303.             uDec = LODWORD(n - (LONGLONG)uInt * 1024L) * 1000 / 1024;
  1304.             // At this point, uDec should be between 0 and 1000
  1305.             // we want get the top one (or two) digits.
  1306.             uDec /= 10;
  1307.             if (uLen == 2)
  1308.                 uDec /= 10;
  1309.             // Note that we need to set the format before getting the
  1310.             // intl char.
  1311.             StrCpyW(szFormat, L"%02d");
  1312.             szFormat[2] = TEXT('0') + 3 - uLen;
  1313.             GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
  1314.                            szWholeNum + uLen, ARRAYSIZE(szWholeNum) - uLen);
  1315.             uLen = lstrlenW(szWholeNum);
  1316.             wnsprintfW(szWholeNum + uLen, ARRAYSIZE(szWholeNum) - uLen, szFormat, uDec);
  1317.         }
  1318.     }
  1319.     MLLoadStringW(c_aOrders[iOrder], szOrder, ARRAYSIZE(szOrder));
  1320.     wnsprintfW(pszBuf, cchBuf, szOrder, szWholeNum);
  1321.     return pszBuf;
  1322. }
  1323. // dw - the nubmer to be converted
  1324. // pszBuf - buffer for the resulting string
  1325. // cchBuf - Max characters in Buffer
  1326. LPSTR StrFormatByteSize64A(LONGLONG dw, LPSTR pszBuf, UINT cchBuf)
  1327. {
  1328.     WCHAR szT[32];
  1329.     StrFormatByteSizeW(dw, szT, SIZECHARS(szT));
  1330.     SHUnicodeToAnsi(szT, pszBuf, cchBuf);
  1331.     return pszBuf;
  1332. }
  1333. LPSTR StrFormatByteSizeA(DWORD dw, LPSTR pszBuf, UINT cchBuf)
  1334. {
  1335.     return StrFormatByteSize64A((LONGLONG)dw, pszBuf, cchBuf);
  1336. }
  1337. LPWSTR StrFormatKBSizeW(LONGLONG n, LPWSTR pszBuf, UINT cchBuf)
  1338. {
  1339.     static WCHAR s_szOrder[16] = {0};
  1340.     WCHAR szNum[64];
  1341.     if (s_szOrder[0] == TEXT(''))
  1342.         LoadStringW(HINST_THISDLL, IDS_ORDERKB, s_szOrder, ARRAYSIZE(s_szOrder));
  1343.     CommifyString((n + 1023) / 1024, szNum, ARRAYSIZE(szNum));
  1344.     wnsprintfW(pszBuf, cchBuf, s_szOrder, szNum);
  1345.     return pszBuf;
  1346. }
  1347. LPSTR StrFormatKBSizeA(LONGLONG n, LPSTR pszBuf, UINT cchBuf)
  1348. {
  1349.     WCHAR szNum[64];
  1350.     StrFormatKBSizeW(n, szNum, ARRAYSIZE(szNum));
  1351.     SHUnicodeToAnsi(szNum, pszBuf, cchBuf);
  1352.     return pszBuf;
  1353. }
  1354. //  Win95 does not support the wide-char version of lstrcmp, lstrcmpi
  1355. //  Wrapper for lstrcmpW so it works on Win95
  1356. int StrCmpW(LPCWSTR pwsz1, LPCWSTR pwsz2)
  1357. {
  1358.     return _StrCmpLocaleW(0, pwsz1, -1, pwsz2, -1);
  1359. }
  1360. // Wrapper for lstrcmpiW so it works on Win95
  1361. int StrCmpIW(LPCWSTR pwsz1, LPCWSTR pwsz2)
  1362. {
  1363.     return _StrCmpLocaleW(NORM_IGNORECASE, pwsz1, -1, pwsz2, -1);
  1364. }
  1365. /*----------------------------------------------------------
  1366. Purpose: Trim the string pszTrimMe of any leading or trailing
  1367.          characters that are in pszTrimChars.
  1368. Returns: TRUE if anything was stripped
  1369. */
  1370. STDAPI_(BOOL) StrTrimA(IN OUT LPSTR pszTrimMe, LPCSTR pszTrimChars)
  1371. {
  1372.     BOOL bRet = FALSE;
  1373.     LPSTR psz;
  1374.     LPSTR pszStartMeat;
  1375.     LPSTR pszMark = NULL;
  1376.     
  1377.     ASSERT(IS_VALID_STRING_PTRA(pszTrimMe, -1));
  1378.     ASSERT(IS_VALID_STRING_PTRA(pszTrimChars, -1));
  1379.     
  1380.     if (pszTrimMe)
  1381.     {
  1382.         /* Trim leading characters. */
  1383.         
  1384.         psz = pszTrimMe;
  1385.         
  1386.         while (*psz && StrChrA(pszTrimChars, *psz))
  1387.             psz = CharNextA(psz);
  1388.         
  1389.         pszStartMeat = psz;
  1390.         
  1391.         /* Trim trailing characters. */
  1392.         
  1393.         // (The old algorithm used to start from the end and go
  1394.         // backwards, but that is piggy because DBCS version of
  1395.         // CharPrev iterates from the beginning of the string
  1396.         // on every call.)
  1397.         
  1398.         while (*psz)
  1399.         {
  1400.             if (StrChrA(pszTrimChars, *psz))
  1401.             {
  1402.                 if (!pszMark)
  1403.                 {
  1404.                     pszMark = psz;
  1405.                 }
  1406.             }
  1407.             else
  1408.             {
  1409.                 pszMark = NULL;
  1410.             }
  1411.             psz = CharNextA(psz);
  1412.         }
  1413.         
  1414.         // Any trailing characters to clip?
  1415.         if (pszMark)
  1416.         {
  1417.             // Yes
  1418.             *pszMark = '';
  1419.             bRet = TRUE;
  1420.         }
  1421.         
  1422.         /* Relocate stripped string. */
  1423.         
  1424.         if (pszStartMeat > pszTrimMe)
  1425.         {
  1426.             /* (+ 1) for null terminator. */
  1427.             MoveMemory(pszTrimMe, pszStartMeat, CbFromCchA(lstrlenA(pszStartMeat) + 1));
  1428.             bRet = TRUE;
  1429.         }
  1430.         else
  1431.             ASSERT(pszStartMeat == pszTrimMe);
  1432.         
  1433.         ASSERT(IS_VALID_STRING_PTRA(pszTrimMe, -1));
  1434.     }
  1435.     
  1436.     return bRet;
  1437. }
  1438. /*----------------------------------------------------------
  1439. Purpose: Trim the string pszTrimMe of any leading or trailing
  1440.          characters that are in pszTrimChars.
  1441. Returns: TRUE if anything was stripped
  1442. */
  1443. STDAPI_(BOOL) StrTrimW(IN OUT LPWSTR  pszTrimMe, LPCWSTR pszTrimChars)
  1444. {
  1445.     BOOL bRet = FALSE;
  1446.     LPWSTR psz;
  1447.     LPWSTR pszStartMeat;
  1448.     LPWSTR pszMark = NULL;
  1449.     
  1450.     ASSERT(IS_VALID_STRING_PTRW(pszTrimMe, -1));
  1451.     ASSERT(IS_VALID_STRING_PTRW(pszTrimChars, -1));
  1452.     
  1453.     if (pszTrimMe)
  1454.     {
  1455.         /* Trim leading characters. */
  1456.         
  1457.         psz = pszTrimMe;
  1458.         
  1459.         while (*psz && StrChrW(pszTrimChars, *psz))
  1460.             psz++;
  1461.         
  1462.         pszStartMeat = psz;
  1463.         
  1464.         /* Trim trailing characters. */
  1465.         
  1466.         // (The old algorithm used to start from the end and go
  1467.         // backwards, but that is piggy because DBCS version of
  1468.         // CharPrev iterates from the beginning of the string
  1469.         // on every call.)
  1470.         
  1471.         while (*psz)
  1472.         {
  1473.             if (StrChrW(pszTrimChars, *psz))
  1474.             {
  1475.                 if (!pszMark)
  1476.                 {
  1477.                     pszMark = psz;
  1478.                 }
  1479.             }
  1480.             else
  1481.             {
  1482.                 pszMark = NULL;
  1483.             }
  1484.             psz++;
  1485.         }
  1486.         
  1487.         // Any trailing characters to clip?
  1488.         if (pszMark)
  1489.         {
  1490.             // Yes
  1491.             *pszMark = '';
  1492.             bRet = TRUE;
  1493.         }
  1494.         
  1495.         /* Relocate stripped string. */
  1496.         
  1497.         if (pszStartMeat > pszTrimMe)
  1498.         {
  1499.             /* (+ 1) for null terminator. */
  1500.             MoveMemory(pszTrimMe, pszStartMeat, CbFromCchW(lstrlenW(pszStartMeat) + 1));
  1501.             bRet = TRUE;
  1502.         }
  1503.         else
  1504.             ASSERT(pszStartMeat == pszTrimMe);
  1505.         
  1506.         ASSERT(IS_VALID_STRING_PTRW(pszTrimMe, -1));
  1507.     }
  1508.     
  1509.     return bRet;
  1510. }
  1511. /*----------------------------------------------------------
  1512. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1513. Returns: < 0 if pch1 <  pch2
  1514.          = 0 if pch1 == pch2
  1515.          > 0 if pch1 >  pch2
  1516. */
  1517. LWSTDAPI_(int) StrCmpNCA(LPCSTR pch1, LPCSTR pch2, int n)
  1518. {
  1519.     if (n == 0)
  1520.         return 0;
  1521.     while (--n && *pch1 && *pch1 == *pch2)
  1522.     {
  1523.         pch1++;
  1524.         pch2++;
  1525.     }
  1526.     return *(unsigned char *)pch1 - *(unsigned char *)pch2;
  1527. }
  1528. /*----------------------------------------------------------
  1529. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1530. Returns: < 0 if pch1 <  pch2
  1531.          = 0 if pch1 == pch2
  1532.          > 0 if pch1 >  pch2
  1533. */
  1534. LWSTDAPI_(int) StrCmpNCW(LPCWSTR pch1, LPCWSTR pch2, int n)
  1535. {
  1536.     if (n == 0)
  1537.         return 0;
  1538.     while (--n && *pch1 && *pch1 == *pch2)
  1539.     {
  1540.         pch1++;
  1541.         pch2++;
  1542.     }
  1543.     return *pch1 - *pch2;
  1544. }
  1545. /*----------------------------------------------------------
  1546. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1547. Returns: < 0 if pch1 <  pch2
  1548.          = 0 if pch1 == pch2
  1549.          > 0 if pch1 >  pch2
  1550. */
  1551. LWSTDAPI_(int) StrCmpNICA(LPCSTR pch1, LPCSTR pch2, int n)
  1552. {
  1553.     int ch1, ch2;
  1554.     if (n != 0)
  1555.     {
  1556.         do {
  1557.             ch1 = *pch1++;
  1558.             if (ch1 >= 'A' && ch1 <= 'Z')
  1559.                 ch1 += 'a' - 'A';
  1560.             ch2 = *pch2++;
  1561.             if (ch2 >= 'A' && ch2 <= 'Z')
  1562.                 ch2 += 'a' - 'A';
  1563.         } while ( --n && ch1 && (ch1 == ch2) );
  1564.         return ch1 - ch2;
  1565.     }
  1566.     else
  1567.     {
  1568.         return 0;
  1569.     }
  1570. }
  1571. /*----------------------------------------------------------
  1572. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1573. Returns: < 0 if pch1 <  pch2
  1574.          = 0 if pch1 == pch2
  1575.          > 0 if pch1 >  pch2
  1576. */
  1577. LWSTDAPI_(int) StrCmpNICW(LPCWSTR pch1, LPCWSTR pch2, int n)
  1578. {
  1579.     int ch1, ch2;
  1580.     if (n != 0)
  1581.     {
  1582.         do {
  1583.             ch1 = *pch1++;
  1584.             if (ch1 >= L'A' && ch1 <= L'Z')
  1585.                 ch1 += L'a' - L'A';
  1586.             ch2 = *pch2++;
  1587.             if (ch2 >= L'A' && ch2 <= L'Z')
  1588.                 ch2 += L'a' - L'A';
  1589.         } while ( --n && ch1 && (ch1 == ch2) );
  1590.         return ch1 - ch2;
  1591.     }
  1592.     else
  1593.     {
  1594.         return 0;
  1595.     }
  1596. }
  1597. /*----------------------------------------------------------
  1598. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1599. Returns: < 0 if pch1 <  pch2
  1600.          = 0 if pch1 == pch2
  1601.          > 0 if pch1 >  pch2
  1602. */
  1603. LWSTDAPI_(int) StrCmpCA(LPCSTR pch1, LPCSTR pch2)
  1604. {
  1605.     while (*pch1 && (*pch1 == *pch2))
  1606.     {
  1607.         ++pch1;
  1608.         ++pch2;
  1609.     }   
  1610.     return *(unsigned char *)pch1 - *(unsigned char *)pch2;
  1611. }
  1612. /*----------------------------------------------------------
  1613. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1614. Returns: < 0 if pch1 <  pch2
  1615.          = 0 if pch1 == pch2
  1616.          > 0 if pch1 >  pch2
  1617. */
  1618. LWSTDAPI_(int) StrCmpCW(LPCWSTR pch1, LPCWSTR pch2)
  1619. {
  1620.     while (*pch1 && (*pch1 == *pch2))
  1621.     {
  1622.         ++pch1;
  1623.         ++pch2;
  1624.     }   
  1625.     return *pch1 - *pch2;
  1626. }
  1627. /*----------------------------------------------------------
  1628. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1629. Returns: < 0 if pch1 <  pch2
  1630.          = 0 if pch1 == pch2
  1631.          > 0 if pch1 >  pch2
  1632. */
  1633. LWSTDAPI_(int) StrCmpICA(LPCSTR pch1, LPCSTR pch2)
  1634. {
  1635.     int ch1, ch2;
  1636.     do {
  1637.         ch1 = *pch1++;
  1638.         if (ch1 >= 'A' && ch1 <= 'Z')
  1639.             ch1 += 'a' - 'A';
  1640.         ch2 = *pch2++;
  1641.         if (ch2 >= 'A' && ch2 <= 'Z')
  1642.             ch2 += 'a' - 'A';
  1643.     } while (ch1 && (ch1 == ch2));
  1644.     return ch1 - ch2;
  1645. }
  1646. /*----------------------------------------------------------
  1647. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1648. Returns: < 0 if pch1 <  pch2
  1649.          = 0 if pch1 == pch2
  1650.          > 0 if pch1 >  pch2
  1651. */
  1652. LWSTDAPI_(int) StrCmpICW(LPCWSTR pch1, LPCWSTR pch2)
  1653. {
  1654.     int ch1, ch2;
  1655.     do {
  1656.         ch1 = *pch1++;
  1657.         if (ch1 >= L'A' && ch1 <= L'Z')
  1658.             ch1 += L'a' - L'A';
  1659.         ch2 = *pch2++;
  1660.         if (ch2 >= L'A' && ch2 <= L'Z')
  1661.             ch2 += L'a' - L'A';
  1662.     } while (ch1 && (ch1 == ch2));
  1663.     return ch1 - ch2;
  1664. }
  1665. LWSTDAPI StrRetToStrW(STRRET *psr, LPCITEMIDLIST pidl, WCHAR **ppsz)
  1666. {
  1667.     HRESULT hres = S_OK;
  1668.     switch (psr->uType)
  1669.     {
  1670.     case STRRET_WSTR:
  1671.         *ppsz = psr->pOleStr;
  1672.         psr->pOleStr = NULL;   // avoid alias
  1673.         hres = *ppsz ? S_OK : E_FAIL;
  1674.         break;
  1675.     case STRRET_OFFSET:
  1676.         hres = SHStrDupA(STRRET_OFFPTR(pidl, psr), ppsz);
  1677.         break;
  1678.     case STRRET_CSTR:
  1679.         hres = SHStrDupA(psr->cStr, ppsz);
  1680.         break;
  1681.     default:
  1682.         *ppsz = NULL;
  1683.         hres = E_FAIL;
  1684.     }
  1685.     return hres;
  1686. }
  1687. HRESULT DupWideToAnsi(LPCWSTR pwsz, LPSTR *ppsz)
  1688. {
  1689.     UINT cch = WideCharToMultiByte(CP_ACP, 0, pwsz, -1, NULL, 0, NULL, NULL) + 1;
  1690.     *ppsz = CoTaskMemAlloc(cch * sizeof(**ppsz));
  1691.     if (*ppsz)
  1692.     {
  1693.         SHUnicodeToAnsi(pwsz, *ppsz, cch);
  1694.         return S_OK;
  1695.     }
  1696.     return E_OUTOFMEMORY;
  1697. }
  1698. HRESULT DupAnsiToAnsi(LPCSTR psz, LPSTR *ppsz)
  1699. {
  1700.     *ppsz = (LPSTR)CoTaskMemAlloc((lstrlenA(psz) + 1) * sizeof(**ppsz));
  1701.     if (*ppsz) 
  1702.     {
  1703.         lstrcpyA(*ppsz, psz);
  1704.         return S_OK;
  1705.     }
  1706.     return E_OUTOFMEMORY;
  1707. }
  1708. LWSTDAPI StrRetToStrA(STRRET *psr, LPCITEMIDLIST pidl, CHAR **ppsz)
  1709. {
  1710.     HRESULT hres;
  1711.     LPWSTR pwsz;
  1712.     switch (psr->uType)
  1713.     {
  1714.     case STRRET_WSTR:
  1715.         hres = DupWideToAnsi(psr->pOleStr, ppsz);
  1716.         pwsz = psr->pOleStr;
  1717.         psr->pOleStr = NULL;   // avoid alias
  1718.         CoTaskMemFree(pwsz);
  1719.         break;
  1720.     case STRRET_OFFSET:
  1721.         hres = DupAnsiToAnsi(STRRET_OFFPTR(pidl, psr), ppsz);
  1722.         break;
  1723.     case STRRET_CSTR:
  1724.         hres = DupAnsiToAnsi(psr->cStr, ppsz);
  1725.         break;
  1726.     default:
  1727.         *ppsz = NULL;
  1728.         hres = E_FAIL;
  1729.     }
  1730.     return hres;
  1731. }
  1732. STDAPI StrRetToBufA(STRRET *psr, LPCITEMIDLIST pidl, LPSTR pszBuf, UINT cchBuf)
  1733. {
  1734.     HRESULT hres = E_FAIL;
  1735.     switch (psr->uType)
  1736.     {
  1737.     case STRRET_WSTR:
  1738.         {
  1739.             LPWSTR pszStr = psr->pOleStr;   // temp copy because SHUnicodeToAnsi may overwrite buffer
  1740.             if (pszStr)
  1741.             {
  1742.                 SHUnicodeToAnsi(pszStr, pszBuf, cchBuf);
  1743.                 CoTaskMemFree(pszStr);
  1744.                 psr->uType = STRRET_CSTR;   // Make sure no one thinks things are allocated still
  1745.                 hres = S_OK;
  1746.             }
  1747.         }
  1748.         break;
  1749.     case STRRET_CSTR:
  1750.         SHAnsiToAnsi(psr->cStr, pszBuf, cchBuf);
  1751.         hres = S_OK;
  1752.         break;
  1753.     case STRRET_OFFSET:
  1754.         if (pidl)
  1755.         {
  1756.             // BUGBUG (DavePl) Alignment problems here
  1757.             SHAnsiToAnsi(STRRET_OFFPTR(pidl, psr), pszBuf, cchBuf);
  1758.             hres = S_OK;
  1759.         }
  1760.         break;
  1761.     }
  1762.     if (FAILED(hres) && cchBuf)
  1763.         *pszBuf = 0;
  1764.     return hres;
  1765. }
  1766. STDAPI StrRetToBufW(STRRET *psr, LPCITEMIDLIST pidl, LPWSTR pszBuf, UINT cchBuf)
  1767. {
  1768.     HRESULT hres = E_FAIL;
  1769.     
  1770.     switch (psr->uType)
  1771.     {
  1772.     case STRRET_WSTR:
  1773.         {
  1774.             LPWSTR pwszTmp = psr->pOleStr;
  1775.             if (pwszTmp)
  1776.             {
  1777.                 StrCpyNW(pszBuf, pwszTmp, cchBuf);
  1778.                 CoTaskMemFree(pwszTmp);
  1779.                 psr->uType = STRRET_CSTR;   // Make sure no one thinks things are allocated still
  1780.                 hres = S_OK;
  1781.             }
  1782.         }
  1783.         break;
  1784.     case STRRET_CSTR:
  1785.         SHAnsiToUnicode(psr->cStr, pszBuf, cchBuf);
  1786.         hres = S_OK;
  1787.         break;
  1788.     case STRRET_OFFSET:
  1789.         if (pidl)
  1790.         {
  1791.             // BUGBUG (DavePl) Alignment problems here
  1792.             SHAnsiToUnicode(STRRET_OFFPTR(pidl, psr), pszBuf, cchBuf);
  1793.             hres = S_OK;
  1794.         }
  1795.         break;
  1796.     }
  1797.     if (FAILED(hres) && cchBuf)
  1798.         *pszBuf = 0;
  1799.     return hres;
  1800. }
  1801. // dupe a string using the task allocator for returing from a COM interface
  1802. // These functions use SHAlloc, so they cannot go into shlwapi.
  1803. STDAPI SHStrDupA(LPCSTR psz, WCHAR **ppwsz)
  1804. {
  1805.     DWORD cch = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
  1806.     *ppwsz = (WCHAR *)CoTaskMemAlloc((cch + 1) * SIZEOF(WCHAR));
  1807.     if (*ppwsz)
  1808.     {
  1809.         MultiByteToWideChar(CP_ACP, 0, psz, -1, *ppwsz, cch);
  1810.         return S_OK;
  1811.     }
  1812.     return E_OUTOFMEMORY;
  1813. }
  1814. // dupe a string using the task allocator for returing from a COM interface
  1815. // Sometimes, due to structure packing, the pointer we get is not properly
  1816. // aligned for Win64, so we have to do UNALIGNED64.
  1817. STDAPI SHStrDupW(LPCWSTR psz, WCHAR **ppwsz)
  1818. {
  1819.     WCHAR *pwsz;
  1820.     pwsz = (WCHAR *)CoTaskMemAlloc((lstrlenW(psz) + 1) * SIZEOF(WCHAR));
  1821.     
  1822.     *((PVOID UNALIGNED64 *) ppwsz) = pwsz;
  1823.     if (pwsz)
  1824.     {
  1825.         StrCpyW(pwsz, psz);
  1826.         return S_OK;
  1827.     }
  1828.     return E_OUTOFMEMORY;
  1829. }
  1830.