urlpars.cpp.1475
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 61k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. /*++
  2. Copyright (c) 1994  Microsoft Corporation
  3. Module Name:
  4.     urlpars.cpp
  5. Abstract:
  6.     Contains all the worker routines for Combine and Canonicalize
  7.     Contents:
  8.         (ConvertChar)
  9. Author:
  10.     Zeke Lucas (zekel) 16-Dez-96
  11. Environment:
  12.     Win32(s) user-mode DLL
  13. Revision History:
  14.     there is about one percent of this derived 
  15.     from the Spyglass or MSHTML/WININET codebase
  16. --*/
  17. #include "priv.h"
  18. #include <shstr.h>
  19. // DO NOT REMOVE : Url parsing must be ansi   - zekel - 21-dec-96
  20. #ifdef UNICODE 
  21. #undef UNICODE
  22. #endif
  23. // END DO NOT REMOVE
  24. #define HEX_ESCAPE '%'
  25. #define TERMSTR(pch)      *(pch) = TEXT('')
  26. // (TCHAR) 8 is backspace
  27. #define DEADSEGCHAR       ((TCHAR) 8)
  28. #define KILLSEG(pch)      *(pch) = DEADSEGCHAR
  29. #define CR          TEXT('r')
  30. #define LF          TEXT('n')
  31. #define TAB         TEXT('t')
  32. #define SPC         TEXT(' ')
  33. #define SLASH       TEXT('/')
  34. #define WHACK       TEXT('\')
  35. #define QUERY       TEXT('?')
  36. #define POUND       TEXT('#')
  37. #define SEMICOLON   TEXT(';')
  38. #define COLON       TEXT(':')
  39. #define BAR         TEXT('|')
  40. #define DOT         TEXT('.')
  41. #define UPF_SCHEME_OPAQUE           0x00000001  //  should not be treated as heriarchical
  42. #define UPF_SEG_ABSOLUTE            0x00000100  //  the initial segment is the root
  43. #define UPF_SEG_LOCKFIRST           0x00000200  //  this is for file parsing
  44. #define UPF_EXSEG_DIRECTORY           0x00001000  //  the final segment is a "directory" (trailing slash)
  45. //
  46. //  the masks are for inheritance purposes during BlendParts
  47. //  if you inherit that part you inherit that mask
  48. //
  49. #define UPF_SCHEME_MASK             0x000000FF
  50. #define UPF_SEG_MASK                0x00000F00
  51. #define UPF_EXSEG_MASK              0x0000F000
  52. //  right now these masks are unused, and can be recycled
  53. #define UPF_SERVER_MASK             0x000F0000
  54. #define UPF_QUERY_MASK              0x0F000000
  55. #define UPF_FRAG_MASK               0xF0000000
  56. #ifdef UNICODE
  57. typedef struct _UrlPartsW
  58. #else
  59. typedef struct _UrlPartsA
  60. #endif
  61. {
  62.     DWORD   dwFlags;
  63.     LPTSTR  pszScheme;
  64.     DWORD   dwScheme;
  65.     LPTSTR  pszServer;
  66.     LPTSTR  pszSegments;
  67.     DWORD   cSegments;
  68.     LPTSTR  pszExtraSegs;
  69.     DWORD   cExtraSegs;
  70.     LPTSTR  pszQuery;
  71.     LPTSTR  pszFragment;
  72. }
  73. #ifdef UNICODE
  74. URLPARTSW
  75. #else
  76. URLPARTSA
  77. #endif
  78.  ;
  79. #ifdef UNICODE
  80. #define URLPARTS    URLPARTSW
  81. #else
  82. #define URLPARTS    URLPARTSA
  83. #endif
  84. typedef URLPARTS *PURLPARTS;
  85. #ifdef UNICODE
  86. #define g_mpUrlSchemeTypes      g_mpUrlSchemeTypesW
  87. #else
  88. #define g_mpUrlSchemeTypes      g_mpUrlSchemeTypesA
  89. #endif
  90. #pragma data_seg(DATASEG_READONLY)
  91. TCHAR const c_szHttpScheme[]           = TEXT("http");
  92. TCHAR const c_szFileScheme[]           = TEXT("file");
  93. TCHAR const c_szFTPScheme[]            = TEXT("ftp");
  94. TCHAR const c_szGopherScheme[]         = TEXT("gopher");
  95. TCHAR const c_szMailToScheme[]         = TEXT("mailto");
  96. TCHAR const c_szNewsScheme[]           = TEXT("news");
  97. TCHAR const c_szNNTPScheme[]           = TEXT("nntp");
  98. TCHAR const c_szTelnetScheme[]         = TEXT("telnet");
  99. TCHAR const c_szWAISScheme[]           = TEXT("wais");
  100. TCHAR const c_szMkScheme[]             = TEXT("mk");
  101. TCHAR const c_szHttpsScheme[]          = TEXT("https");
  102. const struct  
  103. {
  104.     LPCTSTR pszScheme;
  105.     DWORD dwScheme;
  106.     DWORD cchScheme;
  107.     DWORD dwFlags;
  108. } g_mpUrlSchemeTypes[] = 
  109.     {
  110.     // Because we use a linear search, sort this in the order of 
  111.     // most common usage.
  112.     { c_szHttpScheme,   URL_SCHEME_HTTP,      SIZECHARS(c_szHttpScheme) - 1,     0},
  113.     { c_szFileScheme,   URL_SCHEME_FILE,      SIZECHARS(c_szFileScheme) - 1,     0},
  114.     { c_szFTPScheme,    URL_SCHEME_FTP,       SIZECHARS(c_szFTPScheme) - 1,      0},
  115.     { c_szHttpsScheme,  URL_SCHEME_HTTPS,     SIZECHARS(c_szHttpsScheme) -1,     0},
  116.     { c_szNewsScheme,   URL_SCHEME_NEWS,      SIZECHARS(c_szNewsScheme) - 1,     UPF_SCHEME_OPAQUE},
  117.     { c_szMailToScheme, URL_SCHEME_MAILTO,    SIZECHARS(c_szMailToScheme) - 1,   UPF_SCHEME_OPAQUE},
  118.     { c_szGopherScheme, URL_SCHEME_GOPHER,    SIZECHARS(c_szGopherScheme) - 1,   0},
  119.     { c_szNNTPScheme,   URL_SCHEME_NNTP,      SIZECHARS(c_szNNTPScheme) - 1,     0},
  120.     { c_szTelnetScheme, URL_SCHEME_TELNET,    SIZECHARS(c_szTelnetScheme) - 1,   0},
  121.     { c_szWAISScheme,   URL_SCHEME_WAIS,      SIZECHARS(c_szWAISScheme) - 1,     0},
  122.     { c_szMkScheme,     URL_SCHEME_MK,        SIZECHARS(c_szMkScheme) - 1,        0}
  123.     };
  124. #pragma data_seg()
  125. //
  126. //  there are very similar structures and functions in SHLWAPI
  127. //  but they are legacy APIs for URL.DLL and they are not very useful to me.
  128. //  i decided to not change them and make my own
  129. //  though we share the same URL_SCHEME* numbers
  130. //
  131. PRIVATE DWORD
  132. GetSchemeTypeAndFlags(LPCTSTR pszScheme, LPDWORD pdwFlags)
  133. {
  134.     DWORD i;
  135.     ASSERT(pszScheme);
  136.     for (i = 0; i < ARRAYSIZE(g_mpUrlSchemeTypes); i++)
  137.     {
  138.         if(0 == lstrcmp(pszScheme, g_mpUrlSchemeTypes[i].pszScheme))
  139.         {
  140.             if (pdwFlags)
  141.                 *pdwFlags |= g_mpUrlSchemeTypes[i].dwFlags;
  142.             return g_mpUrlSchemeTypes[i].dwScheme;
  143.         }
  144.     }
  145.     return URL_SCHEME_UNKNOWN;
  146. }
  147. #ifndef UNICODE // right now we only need this for ANSI
  148. /*----------------------------------------------------------
  149. Purpose: Return the scheme ordinal type (URL_SCHEME_*) based on the
  150.          URL string.
  151.   NOTE: this is used by ParseUrl() in url.c
  152. Returns: URL_SCHEME_ ordinal
  153. Cond:    --
  154. */
  155. extern "C"{
  156.     
  157. DWORD
  158. SchemeTypeFromURL(
  159.    LPCTSTR pszURL);
  160. }
  161. DWORD
  162. SchemeTypeFromURL(
  163.    LPCTSTR pszURL)
  164.    {
  165.    DWORD i;
  166.    ASSERT(IS_VALID_STRING_PTR(pszURL, CTSTR));
  167.    // We use a linear search.  A binary search wouldn't pay off 
  168.    // because the list isn't big enough, and we can sort the list
  169.    // according to the most popular protocol schemes and pay off
  170.    // bigger.
  171.    for (i = 0; i < ARRAYSIZE(g_mpUrlSchemeTypes); i++)
  172.       {
  173.       if (0 == lstrnicmp(pszURL, g_mpUrlSchemeTypes[i].pszScheme, 
  174.                          g_mpUrlSchemeTypes[i].cchScheme))
  175.          {
  176.           if(pszURL[g_mpUrlSchemeTypes[i].cchScheme] == TEXT(':'))
  177.             return g_mpUrlSchemeTypes[i].dwScheme;
  178.          }
  179.       }
  180.    return URL_SCHEME_UNKNOWN;
  181.    }
  182. #endif //!UNICODE
  183. //
  184. //  these are used during path fumbling that i do
  185. //  each string between a path delimiter ( '/' or '')
  186. //  is a segment.  we dont ever really care about 
  187. //  empty ("") segments, so it is best to use
  188. //  NextLiveSegment().
  189. //
  190. inline PRIVATE LPTSTR
  191. NextSegment(LPTSTR psz)
  192. {
  193.     ASSERT (psz);
  194.     return psz + lstrlen(psz) + 1;
  195. }
  196. #define IsLiveSegment(p)    ((p) && (*p) != DEADSEGCHAR)
  197. PRIVATE LPTSTR 
  198. NextLiveSegment(LPTSTR pszSeg, DWORD *piSeg, DWORD cSegs)
  199. {
  200.     if(pszSeg) do
  201.     {
  202.         if((*piSeg) +1 < cSegs)
  203.         {
  204.             pszSeg = NextSegment(pszSeg);
  205.             (*piSeg)++;
  206.         }
  207.         else
  208.             pszSeg = NULL;
  209.     } while (pszSeg && (*pszSeg == DEADSEGCHAR || !*pszSeg));
  210.     return pszSeg;
  211. }
  212. PRIVATE LPTSTR
  213. LastLiveSegment(LPTSTR pszSeg, DWORD cSegs, BOOL fFailIfFirst)
  214. {
  215.     DWORD iSeg = 0;
  216.     LPTSTR pszLast = NULL;
  217.     BOOL fLastIsFirst = FALSE;
  218.     if(cSegs)
  219.     {
  220.         if(IsLiveSegment(pszSeg))
  221.         {
  222.             pszLast = pszSeg;
  223.             fLastIsFirst = TRUE;
  224.         }
  225.         while(pszSeg = NextLiveSegment(pszSeg, &iSeg, cSegs))
  226.         {
  227.             if(!pszLast)
  228.                 fLastIsFirst = TRUE;
  229.             else 
  230.                 fLastIsFirst = FALSE;
  231.             pszLast = pszSeg;
  232.         }
  233.         if(fFailIfFirst && fLastIsFirst)
  234.             pszLast = NULL;
  235.     }
  236.     return pszLast;
  237. }
  238. PRIVATE LPTSTR
  239. FirstLiveSegment(LPTSTR pszSeg, DWORD *piSeg, DWORD cSegs)
  240. {
  241.     ASSERT(pszSeg && piSeg && cSegs);
  242.     *piSeg = 0;
  243.     if(!IsLiveSegment(pszSeg))
  244.         pszSeg = NextLiveSegment(pszSeg, piSeg, cSegs);
  245.     return pszSeg;
  246. }
  247. inline BOOL IsDrive(const TCHAR *p)
  248. {
  249.     return (*p && (p[1] == COLON || p[1] == BAR));
  250. }
  251. inline BOOL IsSeparator(const TCHAR *p)
  252. {
  253.     return (*p == SLASH || *p == WHACK );
  254. }
  255. inline BOOL IsAbsolute(const TCHAR *p)
  256. {
  257.     return (IsSeparator(p) || IsDrive(p));
  258. }
  259. inline BOOL IsUNC(const TCHAR *p)
  260. {
  261.     return (!StrNCmp(p, TEXT("\\"), 2)) || (!StrNCmp(p, TEXT("//"), 2));
  262. }
  263. inline BOOL IsDot(LPCTSTR p)     // if p == "." return TRUE
  264. {
  265.     return (*p == DOT && !p[1]);
  266. }
  267. inline BOOL IsDotDot(LPCTSTR p)  // if p == ".." return TRUE
  268. {
  269.     return (*p == DOT && p[1] == DOT && !p[2]);
  270. }
  271.        
  272. //+---------------------------------------------------------------------------
  273. //
  274. //  Method:     ConvertChar
  275. //
  276. //  Synopsis:
  277. //
  278. //  Arguments:  [szStr] --
  279. //              [cIn] --
  280. //              [cOut] --
  281. //
  282. //  Returns:
  283. //
  284. //  History:    03-20-96    JoeS (Joe Souza)    Created
  285. //
  286. //  Notes:
  287. //
  288. //----------------------------------------------------------------------------
  289. static void ConvertChar(LPTSTR ptr, TCHAR cIn, TCHAR cOut)
  290. {
  291.     while (*ptr)
  292.     {
  293.         if (*ptr == QUERY || *ptr == POUND )
  294.         {
  295.             break;
  296.         }
  297.         if (*ptr == cIn)
  298.         {
  299.             *ptr = cOut;
  300.         }
  301.         ptr = CharNext(ptr);
  302.     }
  303. }
  304. PUBLIC void WininetFixFileSlashes(TCHAR *p)
  305. {
  306.     // NB: This function assumes that p points to a file URL.
  307.     // The file URL *MUST* be of the form "file://...".
  308.     // HTParse() guarantees that this will be so.
  309.     int schemelen = 0;
  310.     schemelen = sizeof(TEXT("file://")) - 1;
  311.     if (p && lstrlen(p) > schemelen)
  312.     {
  313.         ConvertChar(p + schemelen, SLASH, WHACK);
  314.     }
  315. }
  316. //
  317. //  BUGBUGZEKEL shouldnt we be nuking all the bad whites here ? - zekel - 10-Dez-96
  318. //  you know what the real meal is here?
  319. //  ** URLs are allowed to whitespace in them **
  320. //  it just so happens that it is all supposed to be discarded
  321. //  so in honesty, we should remove all whitespace:
  322. //  TAB CR LF SPC and whatever
  323. //
  324. static void HTRemoveTabs(TCHAR *str)
  325. {
  326.     TCHAR *p, *p1;
  327.     if (!str)
  328.     {
  329.         return;
  330.     }
  331.     p = str;
  332.     while (*p)
  333.     {
  334.         if (*p == TAB)
  335.         {
  336.             p1 = p;
  337.             while (*p1 == TAB)
  338.             {
  339.                 ++p1;
  340.             }
  341.             lstrcpy(p, p1);
  342.         }
  343.         else
  344.         {
  345.             ++p;
  346.         }
  347.     }
  348. }
  349. PRIVATE CONST WORD isSafe[96] =
  350. /*   Bit 0       alphadigit     -- 'a' to 'z', '0' to '9', 'A' to 'Z'
  351. **   Bit 1       Hex            -- '0' to '9', 'a' to 'f', 'A' to 'F'
  352. **   Bit 2       valid scheme   -- alphadigit | "-" | "." | "+"
  353. **   Bit 3       mark           -- "$"| "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" | ","
  354. */
  355. /*   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F */
  356.     {0, 8, 0, 0, 8, 0, 0, 8, 8, 8, 8, 4, 8,12,12, 0,    /* 2x   !"#$%&'()*+,-./  */
  357.      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 8, 0, 0, 0, 0,    /* 3x  0123456789:;<=>?  */
  358.      8, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1,    /* 4x  @ABCDEFGHIJKLMNO  */
  359.      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 8,    /* 5X  PQRSTUVWXYZ[]^_  */
  360.      0, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1,    /* 6x  `abcdefghijklmno  */
  361.      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 8, 0};   /* 7X  pqrstuvwxyz{|}~  DEL */
  362. PRIVATE const TCHAR hex[] = TEXT("0123456789ABCDEF");
  363. PRIVATE inline BOOL IsSafe(TCHAR ch, WORD mask)
  364. {
  365.     if(ch > 31 && ch < 128 && (isSafe[ch - 32] & mask))
  366.         return TRUE;
  367.     return FALSE;
  368. }
  369. #define IsAlphaDigit(c)         IsSafe(c, 1)
  370. #define IsHex(c)                IsSafe(c, 2)
  371. #define IsValidSchemeChar(c)    IsSafe(c, 5)
  372. #define IsSafePathChar(c)       IsSafe(c, 9)
  373. /*+++
  374.   BreakUrl() 
  375.     Break a URL for its consituent parts
  376.   Parameters
  377.   IN -
  378.             the URL to crack open, need not be fully qualified
  379.      
  380.   OUT -
  381.     parts       absolute or relative may be nonzero (but not both).
  382.                 host, anchor and access may be nonzero if they were specified.
  383.                 Any which are nonzero point to zero terminated strings.
  384.      
  385.   Returns   
  386.     VOID
  387.   Details -
  388.   WARNING !! function munges the incoming buffer
  389. ---*/
  390. PRIVATE VOID BreakFragment(LPTSTR *ppsz, PURLPARTS parts)
  391. {
  392.     if(!**ppsz)
  393.         return;
  394.     TCHAR *pch = StrChr(*ppsz, POUND);
  395.     if (pch)
  396.     {
  397.         TERMSTR(pch);
  398.         parts->pszFragment = pch +1;
  399.     }
  400. }
  401. PRIVATE VOID BreakScheme(LPTSTR *ppsz, PURLPARTS parts)
  402. {
  403.     if(!**ppsz)
  404.         return;
  405.     TCHAR *pch;
  406.     for (pch = *ppsz; *pch; pch = CharNext(pch))
  407.     {
  408.         
  409.         if (*pch == COLON)
  410.         {
  411.             TERMSTR(pch);
  412.             CharLower(*ppsz);
  413.             if (!lstrcmp(*ppsz, TEXT("url")))
  414.             {
  415.                 *ppsz = pch +1;
  416.                 continue;
  417.             }
  418.             //  Scheme found!
  419.             parts->pszScheme = *ppsz;   
  420.             *ppsz = pch + 1;
  421.             break;
  422.         }
  423.         if(!IsValidSchemeChar(*pch))
  424.             break;
  425.     }
  426.     if(parts->pszScheme)
  427.         parts->dwScheme = GetSchemeTypeAndFlags(parts->pszScheme, &parts->dwFlags);
  428. }
  429. PRIVATE VOID BreakQuery(LPTSTR *ppsz, PURLPARTS parts)
  430. {
  431.     TCHAR *pch;
  432.     if(!**ppsz)
  433.         return;
  434.     if(parts->dwFlags & UPF_SCHEME_OPAQUE)
  435.         return;
  436.     pch = StrChr(*ppsz, QUERY);
  437.     if (pch)
  438.     {
  439.         TERMSTR(pch);
  440.         parts->pszQuery = pch + 1;
  441.     }
  442. }
  443. PRIVATE VOID MkBreakServer(LPTSTR *ppsz, PURLPARTS parts)
  444. {
  445.     if (**ppsz == TEXT('@'))
  446.     {
  447.         TCHAR *pch;
  448.         // treat everything to seperator as host
  449.         //
  450.         parts->pszServer = *ppsz;
  451.         pch = StrChr(*ppsz ,SLASH);
  452.         if (pch)
  453.         {
  454.             parts->dwFlags |= UPF_SEG_ABSOLUTE;
  455.             TERMSTR(pch);
  456.             *ppsz = pch + 1;
  457.         }
  458.     }
  459. }
  460. PRIVATE VOID DefaultBreakServer(LPTSTR *ppsz, PURLPARTS parts)
  461. {
  462.     if(parts->dwFlags & UPF_SCHEME_OPAQUE)
  463.         return ;
  464.     if (**ppsz == SLASH)
  465.     {
  466.         parts->dwFlags |= UPF_SEG_ABSOLUTE;
  467.         *ppsz = CharNext(*ppsz);
  468.         if (**ppsz == SLASH)
  469.         {
  470.             // we have a winner!
  471.             TCHAR * pch;
  472.             parts->pszServer = CharNext(*ppsz);
  473.             pch = StrChr(parts->pszServer, SLASH);
  474.             
  475.             if(pch)
  476.             {
  477.                 TERMSTR(pch);
  478.                 *ppsz = pch + 1;
  479.             }
  480.             else
  481.                 *ppsz = *ppsz + lstrlen(*ppsz);
  482.             // we want to CharLower() the hostname only...
  483.             pch = StrRChr(parts->pszServer, NULL, TEXT('@'));
  484.             if(!pch)
  485.                 pch = parts->pszServer;
  486.             CharLower(pch);
  487.         }
  488.     }
  489. }
  490. PRIVATE DWORD
  491. CountSlashes(LPTSTR *ppsz)
  492. {
  493.     DWORD cSlashes = 0;
  494.     LPTSTR pch = *ppsz;
  495.     while (IsSeparator(pch))
  496.     {
  497.         *ppsz = pch;
  498.         pch = CharNext(pch);
  499.         cSlashes++;
  500.     }
  501.     return cSlashes;
  502. }
  503. PRIVATE VOID FileBreakServer(LPTSTR *ppsz, PURLPARTS parts)
  504. {
  505.     LPTSTR pch;
  506.     
  507.     //  CountSlashes() will set *ppsz to the last slash
  508.     DWORD cSlashes = CountSlashes(ppsz);
  509.     if(cSlashes || IsDrive(*ppsz))
  510.         parts->dwFlags |= UPF_SEG_ABSOLUTE;
  511.     switch (cSlashes)
  512.     {
  513.     case 0:
  514.         break;
  515.     case 2:
  516.         if(IsDrive(CharNext(*ppsz)))
  517.         {
  518.             //  this is a root drive
  519.             TERMSTR(*ppsz);
  520.             parts->pszServer = *ppsz;
  521.             (*ppsz)++;
  522.             break;
  523.         } //else fallthru to UNC handling
  524.     case 4:
  525.     case 5:
  526.         //
  527.         // cases like "file:////..." or "file://///..."
  528.         // we see this as a UNC path
  529.         // lets set the server
  530.         //
  531.         parts->pszServer = ++(*ppsz);
  532.         for(pch = *ppsz; *pch && !IsSeparator(pch); pch = CharNext(pch));
  533.         
  534.         if(pch && *pch)
  535.         {
  536.             TERMSTR(pch);
  537.             *ppsz = pch + 1;
  538.         }
  539.         else
  540.             *ppsz = pch + lstrlen(pch);
  541.         break;
  542.     case 1:
  543.         //
  544.         //we think of "file:/..." as on the local machine
  545.         // so we have zero length pszServer
  546.         //
  547.     case 3:
  548.         //
  549.         //we think of file:///... as properly normalized on the local machine
  550.         // so we have zero length pszServer
  551.         //
  552.     default:
  553.         //  there is just too many, we pretend that there is just one and ignore
  554.         //  the rest
  555.         TERMSTR(*ppsz);
  556.         parts->pszServer = *ppsz;
  557.         (*ppsz)++;
  558.         break;
  559.     }
  560. }
  561. PRIVATE VOID BreakServer(LPTSTR *ppsz, PURLPARTS parts)
  562. {
  563.     if(!**ppsz)
  564.         return;
  565.     //  we pretend that whacks are always the equiv of slashes
  566.     ConvertChar(*ppsz, WHACK, SLASH);
  567.     switch(parts->dwScheme)
  568.     {
  569.     case URL_SCHEME_FILE:
  570.         FileBreakServer(ppsz, parts);
  571.         break;
  572.     case URL_SCHEME_MK:
  573.         MkBreakServer(ppsz, parts);
  574.         break;
  575.     default:
  576.         DefaultBreakServer(ppsz, parts);
  577.         break;
  578.     }
  579. }
  580. PRIVATE VOID DefaultBreakSegments(LPTSTR psz, PURLPARTS parts)
  581. {
  582.     TCHAR *pch;
  583.     while (pch = StrChr(psz, SLASH))
  584.     {
  585.         parts->cSegments++;
  586.         TERMSTR(pch);
  587.         psz = pch + 1;
  588.     }
  589.     if(!*psz)
  590.         parts->dwFlags |= UPF_EXSEG_DIRECTORY;
  591. }
  592.         
  593. PRIVATE VOID DefaultBreakPath(LPTSTR *ppsz, PURLPARTS parts)
  594. {
  595.     if(!**ppsz)
  596.         return;
  597.     if((**ppsz == SLASH) && !(parts->dwFlags & UPF_SCHEME_OPAQUE))
  598.     {
  599.         parts->dwFlags |= UPF_SEG_ABSOLUTE;
  600.         *ppsz = CharNext(*ppsz);
  601.     }
  602.     parts->pszSegments = *ppsz;
  603.     parts->cSegments = 1;
  604.     if(!(parts->dwFlags & UPF_SCHEME_OPAQUE))
  605.         DefaultBreakSegments(parts->pszSegments, parts);
  606. }
  607. PRIVATE VOID FileBreakPath(LPTSTR *ppsz, PURLPARTS parts)
  608. {
  609.     if(!**ppsz)
  610.         return;
  611.     if(IsSeparator(*ppsz) && !(parts->dwFlags & UPF_SCHEME_OPAQUE))
  612.     {
  613.         parts->dwFlags |= UPF_SEG_ABSOLUTE;
  614.         *ppsz = CharNext(*ppsz);
  615.     }
  616.     //
  617.     //  this will keep the drive letter from being backed up over
  618.     //  during canonicalization.  if we want keep the UNC share 
  619.     //  from being backed up we should do it here 
  620.     //  or in FileBreakServer() similarly 
  621.     //
  622.     if(IsDrive(*ppsz))
  623.         parts->dwFlags |= UPF_SEG_LOCKFIRST;
  624.     parts->pszSegments = *ppsz;
  625.     parts->cSegments = 1;
  626.     if(!(parts->dwFlags & UPF_SCHEME_OPAQUE))
  627.         DefaultBreakSegments(parts->pszSegments, parts);
  628. }
  629. PRIVATE VOID BreakPath(LPTSTR *ppsz, PURLPARTS parts)
  630. {
  631.     if(!**ppsz)
  632.         return;
  633.     switch(parts->dwScheme)
  634.     {
  635.     case URL_SCHEME_FILE:
  636.         FileBreakPath(ppsz, parts);
  637.         break;
  638.     default:
  639.         DefaultBreakPath(ppsz, parts);
  640.         break;
  641.     }
  642. }
  643. PRIVATE void 
  644. BreakUrl(LPTSTR pszUrl, PURLPARTS parts)
  645. {
  646.     LPTSTR pszRoot = pszUrl;
  647.     ASSERT(pszUrl && parts);
  648.     ZeroMemory(parts, SIZEOF(URLPARTS));
  649.     //
  650.     //  WARNING: this order is specific, according to the proposed standard
  651.     //  
  652.     BreakFragment(&pszRoot, parts);
  653.     BreakScheme(&pszRoot, parts);
  654.     BreakQuery(&pszRoot, parts);
  655.     BreakServer(&pszRoot, parts);
  656.     BreakPath(&pszRoot, parts);
  657.     return;
  658. }
  659. /*+++
  660.   WininetCopyUrlForParse()
  661.     this copies the url and prepends a "file://" if necessary
  662.     This should never be called except from wininet
  663.     everyone else should be calling UrlCreateFromPath()
  664.   Parameters
  665.   IN -
  666.     pszDst      the destination buffer
  667.     pszSrc      source buffer
  668.      
  669.   OUT -
  670.     pszDst      is filled with a Live URL
  671.   Returns   
  672.   VOID
  673.   NOTE - Assume "file:" if no scheme and it looks like fully-qualified file path.
  674. ---*/
  675. PRIVATE HRESULT
  676. WininetCopyUrlForParse(PSHSTR pstrDst, LPCTSTR pszSrc)
  677. {
  678.     static const TCHAR szFileSchemeString[] = TEXT("file://");
  679.     //#define FILE_SCHEME_LENGTH sizeof(szFileSchemeString) - 1
  680.     if (IsDrive(pszSrc) || IsUNC(pszSrc))
  681.     {
  682.         //
  683.         // NOTE: the first SetStr will always succeed
  684.         // because the default buffer is more than "file://"
  685.         pstrDst->SetStr(szFileSchemeString);
  686.         return pstrDst->Append(pszSrc);
  687.     }
  688.     else
  689.         return pstrDst->SetStr(pszSrc);
  690. }
  691. /*+++
  692.   BlendParts()  & all dependant Blend* functions
  693.         Blends the parts structures into one, taking the relavent 
  694.         bits from each one and dumping the unused data.
  695.   Parameters
  696.   IN -
  697.     partsUrl        the primary or relative parts   - Takes precedence
  698.     partsBase       the base or referrers parts
  699.      
  700.   OUT -
  701.     partsOut        the combined result
  702.   Returns   
  703.   VOID -
  704.   NOTE:  this will frequently NULL out the entire partsBase.
  705. ---*/
  706. PRIVATE VOID
  707. BlendScheme(PURLPARTS partsUrl, PURLPARTS partsBase, PURLPARTS partsOut)
  708. {
  709.     if(partsUrl->pszScheme)
  710.     {
  711.         LPCTSTR pszScheme = partsOut->pszScheme = partsUrl->pszScheme;
  712.         DWORD dwScheme = partsOut->dwScheme = partsUrl->dwScheme;
  713.         partsOut->dwFlags |= (partsUrl->dwFlags & UPF_SCHEME_MASK);
  714.         if ((dwScheme && (dwScheme != partsBase->dwScheme)) ||
  715.             (partsBase->pszScheme && lstrcmp(pszScheme, partsBase->pszScheme)))
  716.         {
  717.             //  they are different schemes.  DUMP partsBase.
  718.             ZeroMemory(partsBase, SIZEOF(URLPARTS));
  719.         }
  720.     }
  721.     else
  722.     {
  723.         partsOut->pszScheme = partsBase->pszScheme;
  724.         partsOut->dwScheme = partsBase->dwScheme;
  725.         partsOut->dwFlags |= (partsBase->dwFlags & UPF_SCHEME_MASK);
  726.     }
  727. }
  728. PRIVATE VOID
  729. BlendServer(PURLPARTS partsUrl, PURLPARTS partsBase, PURLPARTS partsOut)
  730. {
  731.     ASSERT(partsUrl && partsBase && partsOut);
  732.     //
  733.     //  if we have different hosts then everything but the pszAccess is DUMPED
  734.     //
  735.     if(partsUrl->pszServer)
  736.     {
  737.         partsOut->pszServer = partsUrl->pszServer;
  738.         // NOTUSED partsOut->dwFlags |= (partsUrl->dwFlags & UPF_SERVER_MASK);
  739.         if ((partsBase->pszServer && lstrcmp(partsUrl->pszServer, partsBase->pszServer)))
  740.         {
  741.             //  they are different Servers.  DUMP partsBase.
  742.             ZeroMemory(partsBase, SIZEOF(URLPARTS));
  743.         }
  744.     }
  745.     else
  746.     {
  747.         partsOut->pszServer = partsBase->pszServer;
  748.         // NOTUSED partsOut->dwFlags |= (partsBase->dwFlags & UPF_SERVER_MASK);
  749.     }
  750. }
  751. PRIVATE VOID
  752. BlendPath(PURLPARTS partsUrl, PURLPARTS partsBase, PURLPARTS partsOut)
  753. {
  754.     ASSERT(partsUrl && partsBase && partsOut);
  755.     if (partsUrl->dwFlags & UPF_SEG_ABSOLUTE)
  756.     {
  757.         //  just use the absolute path
  758.         partsOut->pszSegments = partsUrl->pszSegments;
  759.         partsOut->cSegments = partsUrl->cSegments;
  760.         partsOut->dwFlags |= (partsUrl->dwFlags & (UPF_SEG_MASK |UPF_EXSEG_MASK) );
  761.         ZeroMemory(partsBase, SIZEOF(URLPARTS));
  762.     }
  763.     else if ((partsBase->dwFlags & UPF_SEG_ABSOLUTE))
  764.     {                       
  765.         //  Adopt path not name 
  766.         partsOut->pszSegments = partsBase->pszSegments;
  767.         partsOut->cSegments = partsBase->cSegments;
  768.         partsOut->dwFlags |= (partsBase->dwFlags & UPF_SEG_MASK );
  769.         if(partsUrl->cSegments)
  770.         {
  771.             //
  772.             // this a relative path that needs to be combined
  773.             //
  774.             partsOut->pszExtraSegs = partsUrl->pszSegments;
  775.             partsOut->cExtraSegs = partsUrl->cSegments;
  776.             partsOut->dwFlags |= (partsUrl->dwFlags & UPF_EXSEG_MASK );
  777.             if(!(partsBase->dwFlags & UPF_EXSEG_DIRECTORY))
  778.             {
  779.                 //
  780.                 //  knock off the file name segment 
  781.                 //  as long as the it isnt the first or the first is not locked
  782.                 //
  783.                 LPTSTR pszLast = LastLiveSegment(partsOut->pszSegments, partsOut->cSegments, partsOut->dwFlags & UPF_SEG_LOCKFIRST);
  784.                 if(pszLast)
  785.                     KILLSEG(pszLast);
  786.             }
  787.         }
  788.     }
  789.     else if (partsUrl->cSegments)
  790.     {
  791.         partsOut->pszSegments = partsUrl->pszSegments;
  792.         partsOut->cSegments = partsUrl->cSegments;
  793.         partsOut->dwFlags |= (partsUrl->dwFlags & (UPF_SEG_MASK |UPF_EXSEG_MASK) );
  794.         ZeroMemory(partsBase, SIZEOF(URLPARTS));
  795.     }
  796.     else if (partsBase->cSegments)
  797.     {
  798.         partsOut->pszSegments = partsBase->pszSegments;
  799.         partsOut->cSegments = partsBase->cSegments;
  800.         partsOut->dwFlags |= (partsBase->dwFlags & (UPF_SEG_MASK |UPF_EXSEG_MASK) );
  801.     }
  802. }
  803. PRIVATE VOID
  804. BlendQuery(PURLPARTS partsUrl, PURLPARTS partsBase, PURLPARTS partsOut)
  805. {
  806.     if(partsUrl->pszQuery || partsUrl->cSegments)
  807.     {
  808.         LPCTSTR pszQuery = partsOut->pszQuery = partsUrl->pszQuery;
  809.         // NOTUSED partsOut->dwFlags |= (partsUrl->dwFlags & UPF_Query_MASK);
  810.         if ((partsBase->pszQuery && lstrcmp(pszQuery, partsBase->pszQuery)))
  811.         {
  812.             //  they are different Querys.  DUMP partsBase.
  813.             ZeroMemory(partsBase, SIZEOF(URLPARTS));
  814.         }
  815.     }
  816.     else
  817.     {
  818.         partsOut->pszQuery = partsBase->pszQuery;
  819.         // NOTUSED partsOut->dwFlags |= (partsBase->dwFlags & UPF_Query_MASK);
  820.     }
  821. }
  822. PRIVATE VOID
  823. BlendFragment(PURLPARTS partsUrl, PURLPARTS partsBase, PURLPARTS partsOut)
  824. {
  825.     if(partsUrl->pszFragment || partsUrl->cSegments)
  826.     {
  827.         LPCTSTR pszFragment = partsOut->pszFragment = partsUrl->pszFragment;
  828.         // NOTUSED partsOut->dwFlags |= (partsUrl->dwFlags & UPF_Fragment_MASK);
  829.         if ((partsBase->pszFragment && lstrcmp(pszFragment, partsBase->pszFragment)))
  830.         {
  831.             //  they are different Fragments.  DUMP partsBase.
  832.             ZeroMemory(partsBase, SIZEOF(URLPARTS));
  833.         }
  834.     }
  835.     else
  836.     {
  837.         partsOut->pszFragment = partsBase->pszFragment;
  838.         // NOTUSED partsOut->dwFlags |= (partsBase->dwFlags & UPF_Fragment_MASK);
  839.     }
  840. }
  841. PRIVATE VOID
  842. BlendParts(PURLPARTS partsUrl, PURLPARTS partsBase, PURLPARTS partsOut)
  843. {
  844.     //
  845.     //  partsUrl always takes priority over partsBase
  846.     //
  847.     ASSERT(partsUrl && partsBase && partsOut);
  848.     ZeroMemory(partsOut, SIZEOF(URLPARTS));
  849.     BlendScheme( partsUrl,  partsBase,  partsOut);
  850.     BlendServer( partsUrl,  partsBase,  partsOut);
  851.     BlendPath( partsUrl,  partsBase,  partsOut);
  852.     BlendQuery( partsUrl,  partsBase,  partsOut);
  853.     BlendFragment( partsUrl,  partsBase,  partsOut);
  854.     
  855. }
  856. PRIVATE VOID
  857. CanonServer(PURLPARTS parts)
  858. {
  859.     if (parts->pszServer)
  860.     {
  861.         LPTSTR pszName = StrRChr(parts->pszServer, NULL, TEXT('@'));
  862.         if(!pszName)
  863.             pszName = parts->pszServer;
  864.         //  FQDNs should be lower case.
  865.         CharLower(pszName);
  866.         //
  867.         //  Ignore default port numbers, and trailing dots on FQDNs
  868.         //  which will only cause identical adresses to look different
  869.         //
  870.         {
  871.             TCHAR *pch = StrChr(pszName, COLON);
  872.             if (pch && parts->dwScheme)
  873.             {              
  874.                 BOOL fIgnorePort = FALSE;
  875.                 //
  876.                 //  BUGBUG we should actually be getting this from
  877.                 //  the services file to find out the default protocol port
  878.                 //  but we dont think that most people will change them - zekel 17-Dec-96
  879.                 //
  880.                 switch(parts->dwScheme)
  881.                 {
  882.                 case URL_SCHEME_HTTP:
  883.                         if(lstrcmp(pch, TEXT(":80")) == 0)
  884.                             fIgnorePort = TRUE;
  885.                         break;
  886.                 case URL_SCHEME_FTP:
  887.                         if(lstrcmp(pch, TEXT(":21")) == 0)
  888.                             fIgnorePort = TRUE;
  889.                         break;
  890.                 case URL_SCHEME_GOPHER:
  891.                         if(lstrcmp(pch, TEXT(":70")) == 0)
  892.                             fIgnorePort = TRUE;
  893.                         break;
  894.                 case URL_SCHEME_HTTPS:
  895.                         if(lstrcmp(pch, TEXT(":443")) == 0)
  896.                             fIgnorePort = TRUE;
  897.                         break;
  898.                 default:
  899.                     break;
  900.                 }
  901.                 if(fIgnorePort)
  902.                     TERMSTR(pch);  // It is the default: ignore it 
  903.             }
  904.             pch = pszName + lstrlen(pszName) - 1;   // last character in the host name
  905.             if (*pch == DOT)
  906.                 TERMSTR(pch);  // ignore trailing DOTs
  907.         }
  908.     }
  909. }
  910. PRIVATE VOID
  911. CanonCombineSegs(PURLPARTS parts)
  912. {
  913.     ASSERT(parts);
  914.     ASSERT(parts->pszSegments && parts->cSegments);
  915.     ASSERT(parts->pszExtraSegs && parts->cExtraSegs);
  916.     LPTSTR pszLast = LastLiveSegment(parts->pszSegments, parts->cSegments, parts->dwFlags & UPF_SEG_LOCKFIRST);
  917.     LPTSTR pszExtra = parts->pszExtraSegs;
  918.     DWORD iExtra = 0;
  919.     DWORD cExtras = parts->cExtraSegs;
  920.     if(!IsLiveSegment(pszExtra))
  921.         pszExtra = NextLiveSegment(pszExtra, &iExtra, cExtras);
  922.     while(pszLast && pszExtra && IsDotDot(pszExtra))
  923.     {
  924.         KILLSEG(pszLast);
  925.         KILLSEG(pszExtra);
  926.         pszLast = LastLiveSegment(parts->pszSegments, parts->cSegments, parts->dwFlags & UPF_SEG_LOCKFIRST);
  927.         pszExtra = NextLiveSegment(pszExtra, &iExtra, cExtras);
  928.     }
  929. }
  930. PRIVATE VOID
  931. CanonSegments(LPTSTR pszSeg, 
  932.               DWORD cSegs, 
  933.               BOOL fLockFirst)
  934. {
  935.     DWORD  iSeg = 0;
  936.     LPTSTR pszLastSeg = NULL;
  937.     BOOL fLastIsFirst = TRUE;
  938.     BOOL fFirstSeg = TRUE;
  939.     ASSERT (pszSeg && cSegs);
  940.     pszSeg = FirstLiveSegment(pszSeg, &iSeg, cSegs);
  941.     while (pszSeg)
  942.     {
  943.         if(IsDot(pszSeg))
  944.         {
  945.             //  if it is just a "." we can discard the segment
  946.             KILLSEG(pszSeg);
  947.         }
  948.     
  949.         else if(IsDotDot(pszSeg))
  950.         {
  951.             //  if it is ".." then we discard it and the last seg
  952.             //
  953.             //  if we are at the first (root) or 
  954.             //  the last is the root and it is locked
  955.             //  then we dont want to do anything
  956.             //
  957.             if(pszLastSeg && !IsDotDot(pszLastSeg) && !(fLastIsFirst && fLockFirst))
  958.             {
  959.                 KILLSEG(pszLastSeg);
  960.                 pszLastSeg = NULL;
  961.                 KILLSEG(pszSeg);
  962.             }
  963.         }
  964.         if(IsLiveSegment(pszSeg))
  965.         {
  966.             if(!pszLastSeg && fFirstSeg)
  967.                 fLastIsFirst = TRUE;
  968.             else 
  969.                 fLastIsFirst = FALSE;
  970.                 
  971.             pszLastSeg = pszSeg;
  972.             fFirstSeg = FALSE;
  973.         }
  974.         pszSeg = NextLiveSegment(pszSeg, &iSeg, cSegs);
  975.     }
  976. }
  977. PRIVATE VOID
  978. CanonPath(PURLPARTS parts)
  979. {
  980.     ASSERT(parts);
  981.     if(parts->cSegments)
  982.         CanonSegments(parts->pszSegments, parts->cSegments, (parts->dwFlags & UPF_SEG_LOCKFIRST));
  983.     if(parts->cExtraSegs)
  984.         CanonSegments(parts->pszExtraSegs, parts->cExtraSegs, FALSE);
  985.     if(parts->cSegments && parts->cExtraSegs)
  986.         CanonCombineSegs(parts);
  987. }
  988. PRIVATE VOID
  989. CanonParts(PURLPARTS parts)
  990. {
  991.     ASSERT(parts);
  992.     //CanonScheme(parts);
  993.     CanonServer(parts);
  994.     CanonPath(parts);
  995.     //CanonQuery(parts);
  996.     //CanonFragment(parts);
  997. }
  998. PRIVATE HRESULT
  999. BuildScheme(PURLPARTS parts, DWORD dwFlags, PSHSTR pstr)
  1000. {
  1001.     HRESULT hr = S_OK;
  1002.     ASSERT(parts && pstr);
  1003.     if(parts->pszScheme)
  1004.     {
  1005.         hr = pstr->Append(parts->pszScheme);
  1006.         if(SUCCEEDED(hr))
  1007.             hr = pstr->Append(COLON);
  1008.     }
  1009.     return hr;
  1010. }
  1011. PRIVATE HRESULT
  1012. BuildServer(PURLPARTS parts, DWORD dwFlags, PSHSTR pstr)
  1013. {
  1014.     HRESULT hr = S_OK;
  1015.     ASSERT(parts && pstr);
  1016.     switch(parts->dwScheme)
  1017.     {
  1018.     case URL_SCHEME_MK:
  1019.     // CraigC's "mk:" has no // but acts like it does
  1020.         break;
  1021.     case URL_SCHEME_FILE:
  1022.         if ((dwFlags & URL_WININET_COMPATIBILITY) )
  1023.         {
  1024.             if(parts->pszServer && *parts->pszServer)
  1025.                 hr = pstr->Append(TEXT("////"));
  1026.             else if (IsDrive(parts->pszSegments))
  1027.                 hr = pstr->Append(SLASH);
  1028.             else if (parts->dwFlags & UPF_SEG_ABSOLUTE)
  1029.                 hr = pstr->Append(TEXT("//"));
  1030.                 
  1031.             break;
  1032.         }
  1033.         else if (!(parts->dwFlags & UPF_SEG_ABSOLUTE) )
  1034.             break;
  1035.         //else dropthrough if there is a server or we want to pretend
  1036.         //there is <gryn> for "file://" and "file:///"
  1037.         
  1038.     default:
  1039.         if(parts->pszServer && SUCCEEDED(hr))
  1040.             hr = pstr->Append(TEXT("//"));
  1041.     }
  1042.     if(parts->pszServer && SUCCEEDED(hr))
  1043.             hr = pstr->Append(parts->pszServer);
  1044.     return hr;
  1045. }
  1046. PRIVATE HRESULT
  1047. BuildSegments(LPTSTR pszSeg, DWORD cSegs, PSHSTR pstr, BOOL fRoot)
  1048. {
  1049.     DWORD iSeg = 0;
  1050.     HRESULT hr = S_OK;
  1051.     ASSERT(pszSeg && pstr);
  1052.     pszSeg = FirstLiveSegment(pszSeg, &iSeg, cSegs);
  1053.     if(!fRoot)
  1054.     {
  1055.         hr = pstr->Append(pszSeg);
  1056.         if(SUCCEEDED(hr))
  1057.             pszSeg = NextLiveSegment(pszSeg, &iSeg, cSegs);
  1058.         else
  1059.             pszSeg = NULL;
  1060.     }
  1061.     while (pszSeg)
  1062.     {
  1063.         hr = pstr->Append(SLASH);
  1064.         if(SUCCEEDED(hr))
  1065.             hr = pstr->Append(pszSeg);
  1066.         if(SUCCEEDED(hr))
  1067.             pszSeg = NextLiveSegment(pszSeg, &iSeg, cSegs);
  1068.         else 
  1069.             break;
  1070.     }
  1071.     return hr;
  1072. }
  1073. PRIVATE HRESULT
  1074. BuildPath(PURLPARTS parts, DWORD dwFlags, PSHSTR pstr)
  1075. {
  1076.     HRESULT hr = S_OK;
  1077.     ASSERT(parts && pstr);
  1078.     if(parts->cSegments)
  1079.         hr = BuildSegments(parts->pszSegments, parts->cSegments, pstr, parts->dwFlags & UPF_SEG_ABSOLUTE);
  1080.     if(SUCCEEDED(hr) && parts->cExtraSegs)
  1081.         hr = BuildSegments(parts->pszExtraSegs, parts->cExtraSegs, pstr, TRUE);
  1082.     //  trailing slash on a server name for IIS
  1083.     if( (parts->dwFlags & UPF_EXSEG_DIRECTORY) ||
  1084.         (!parts->cSegments && !parts->cExtraSegs && parts->dwFlags & UPF_SEG_ABSOLUTE))
  1085.         hr = pstr->Append(SLASH);
  1086.     return hr;
  1087. }
  1088. PRIVATE HRESULT
  1089. BuildQuery(PURLPARTS parts, DWORD dwFlags, PSHSTR pstr)
  1090. {
  1091.     HRESULT hr = S_OK;
  1092.     ASSERT(parts && pstr);
  1093.     if(parts->pszQuery)
  1094.     {
  1095.         hr = pstr->Append(QUERY);
  1096.         if(SUCCEEDED(hr))
  1097.             hr = pstr->Append(parts->pszQuery);
  1098.     }
  1099.     return hr;
  1100. }
  1101. PRIVATE HRESULT
  1102. BuildFragment(PURLPARTS parts, DWORD dwFlags, PSHSTR pstr)
  1103. {
  1104.     HRESULT hr = S_OK;
  1105.     ASSERT(parts && pstr);
  1106.     if(parts->pszFragment)
  1107.     {
  1108.         hr = pstr->Append(POUND);
  1109.         if(SUCCEEDED(hr))
  1110.             hr = pstr->Append(parts->pszFragment);
  1111.     }
  1112.     return hr;
  1113. }
  1114. PRIVATE HRESULT
  1115. BuildUrl(PURLPARTS parts, DWORD dwFlags, PSHSTR pstr)
  1116. {
  1117.     HRESULT hr;
  1118.     ASSERT(parts && pstr);
  1119.     if( 
  1120.         (SUCCEEDED(hr = BuildScheme(parts, dwFlags, pstr)))      &&
  1121.         (SUCCEEDED(hr = BuildServer(parts, dwFlags, pstr)))      &&
  1122.         (SUCCEEDED(hr = BuildPath(parts, dwFlags, pstr)))        &&
  1123.         (SUCCEEDED(hr = BuildQuery(parts, dwFlags, pstr)))
  1124.         )
  1125.         hr = BuildFragment(parts, dwFlags, pstr);
  1126.     return hr;
  1127. }
  1128. /*+++
  1129.   SHUrlEscape()
  1130.     Escapes an URL
  1131.     right now, i am only escaping stuff in the Path part of the URL
  1132.   Parameters
  1133.   IN -
  1134.     pszUrl      URL to examine
  1135.     pstrOut     SHSTR destination
  1136.     dwFlags     the relevant URL_* flags, 
  1137.      
  1138.   Returns   
  1139.   HRESULT -
  1140.     SUCCESS     S_OK
  1141.     ERROR       only E_OUTOFMEMORY
  1142.   Helper Routines
  1143.     Escape*(part)           each part gets its own escape routine (ie EscapeScheme)
  1144.     EscapeSpaces            will only escape spaces (WININET compatibility mostly)
  1145.     EscapeSegmentsGetNeededSize     gets the required size of destination buffer for all path segments
  1146.     EscapeLiveSegment               does the work of escaping each path segment
  1147. ---*/
  1148. PRIVATE HRESULT
  1149. EscapeSpaces(LPCTSTR psz, PSHSTR pstr, DWORD dwFlags)
  1150. {
  1151.     HRESULT hr = S_OK;
  1152.     LPCTSTR pch;
  1153.     DWORD cSpaces = 0;
  1154.     ASSERT(psz && pstr);
  1155.     
  1156.     pstr->Reset();
  1157.     for (pch = psz; *pch; pch = CharNext(pch))
  1158.     {
  1159.         if (*pch == SPC)
  1160.             cSpaces++;
  1161.     }
  1162.     if(cSpaces)
  1163.     {
  1164.         hr = pstr->SetSize(lstrlen(psz) + cSpaces * 2 + 1);
  1165.         if(SUCCEEDED(hr))
  1166.         {
  1167.             LPTSTR pchOut = (LPTSTR) *pstr;
  1168.             for (pch = psz; *pch; pch = CharNext(pch))
  1169.             {
  1170.                 if ((*pch == POUND || *pch == QUERY) && (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO))
  1171.                 {
  1172.                     lstrcpy(pchOut, pch);
  1173.                     pchOut += lstrlen(pchOut);
  1174.                 }
  1175.                 if (*pch == SPC)
  1176.                 {
  1177.                     *pchOut++ = HEX_ESCAPE;  
  1178.                     *pchOut++ = TEXT('2');
  1179.                     *pchOut++ = TEXT('0');
  1180.                 }
  1181.                 else
  1182.                     *pchOut++ = *pch;
  1183.             }
  1184.             TERMSTR(pchOut);
  1185.         }
  1186.             
  1187.     }
  1188.     else 
  1189.         hr = pstr->SetStr(psz);
  1190.     return hr;
  1191. }
  1192. inline PRIVATE HRESULT
  1193. EscapeScheme(PURLPARTS partsUrl, DWORD dwFlags, PURLPARTS partsOut, PSHSTR pstr)
  1194. {
  1195.     ASSERT(partsUrl && partsOut);
  1196.     partsOut->pszScheme = partsUrl->pszScheme;
  1197.     partsOut->dwScheme = partsUrl->dwScheme;
  1198.     return S_OK;
  1199. }
  1200. inline PRIVATE HRESULT
  1201. EscapeServer(PURLPARTS partsUrl, DWORD dwFlags, PURLPARTS partsOut, PSHSTR pstr)
  1202. {
  1203.     ASSERT(partsUrl && partsOut);
  1204.     partsOut->pszServer = partsUrl->pszServer;
  1205.     return S_OK;
  1206. }
  1207. inline PRIVATE HRESULT
  1208. EscapeQuery(PURLPARTS partsUrl, DWORD dwFlags, PURLPARTS partsOut, PSHSTR pstr)
  1209. {
  1210.     ASSERT(partsUrl && partsOut);
  1211.     partsOut->pszQuery = partsUrl->pszQuery;
  1212.     return S_OK;
  1213. }
  1214. inline PRIVATE HRESULT
  1215. EscapeFragment(PURLPARTS partsUrl, DWORD dwFlags, PURLPARTS partsOut, PSHSTR pstr)
  1216. {
  1217.     ASSERT(partsUrl && partsOut);
  1218.     partsOut->pszFragment = partsUrl->pszFragment;
  1219.     return S_OK;
  1220. }
  1221. PRIVATE DWORD
  1222. EscapeSegmentsGetNeededSize(LPTSTR pszSegments, DWORD cSegs)
  1223. {
  1224.     DWORD cchNeeded = 0;
  1225.     BOOL fResize = FALSE;
  1226.     LPTSTR pszSeg;
  1227.     DWORD iSeg;
  1228.     ASSERT(pszSegments && cSegs);
  1229.     pszSeg = FirstLiveSegment(pszSegments, &iSeg, cSegs);
  1230.     while (IsLiveSegment(pszSeg))
  1231.     {
  1232.         TCHAR *pch;
  1233.         for (pch = pszSeg; *pch; pch = CharNext(pch))
  1234.         {
  1235.             cchNeeded++;
  1236. #ifndef UNICODE
  1237.             if(IsDBCSLeadByte(*pch))
  1238.             {
  1239.                 cchNeeded += 4;
  1240.                 fResize = TRUE;
  1241.                 continue;
  1242.             }
  1243. #endif //UNICODE
  1244.             if(!IsSafePathChar(*pch))
  1245.             {
  1246.                 fResize = TRUE;
  1247.                 cchNeeded += 2;
  1248.             }
  1249.         }
  1250.         // for the NULL term
  1251.         cchNeeded++;
  1252.         pszSeg = NextLiveSegment(pszSeg, &iSeg, cSegs);
  1253.     }
  1254.     return fResize ? cchNeeded : 0;
  1255. }
  1256. PRIVATE VOID
  1257. EscapeLiveSegment(LPTSTR pszSeg, LPTSTR *ppchOut)
  1258. {
  1259.     LPTSTR pchIn;
  1260.     LPTSTR pchOut = *ppchOut;
  1261.     TCHAR ch;
  1262.     for (pchIn = pszSeg; *pchIn; pchIn = CharNext(pchIn))
  1263.     {
  1264.         ch = *pchIn;
  1265. #ifndef UNICODE
  1266.         if(IsDBCSLeadByte(ch))
  1267.         {
  1268.             //  must encode the next 2 chars...
  1269.             *pchOut++ = HEX_ESCAPE;
  1270.             *pchOut++ = hex[(ch >> 4) & 15];
  1271.             *pchOut++ = hex[ch & 15];
  1272.             //  must not use charnext here, cuz we need the real thing
  1273.             ch++;
  1274.             *pchOut++ = HEX_ESCAPE;
  1275.             *pchOut++ = hex[(ch >> 4) & 15];
  1276.             *pchOut++ = hex[ch & 15];
  1277.             continue;
  1278.         }
  1279. #endif // UNICODE
  1280.         if(!IsSafePathChar(ch))
  1281.         {
  1282.             *pchOut++ = HEX_ESCAPE;
  1283.             *pchOut++ = hex[(ch >> 4) & 15];
  1284.             *pchOut++ = hex[ch & 15];
  1285.         }
  1286.         else
  1287.             *pchOut++ = *pchIn;
  1288.     }
  1289.     TERMSTR(pchOut);
  1290.     // move past the terminator
  1291.     pchOut++;
  1292.     *ppchOut = pchOut;
  1293. }
  1294. PRIVATE HRESULT 
  1295. EscapeSegments(LPTSTR pszSegments, DWORD cSegs, PURLPARTS partsOut, PSHSTR pstr)
  1296. {
  1297.     DWORD cchNeeded;
  1298.     HRESULT hr = S_OK;
  1299.     ASSERT(pszSegments && cSegs && partsOut && pstr);
  1300.     cchNeeded = EscapeSegmentsGetNeededSize(pszSegments, cSegs);
  1301.     if(cchNeeded)
  1302.     {
  1303.         ASSERT(pstr);
  1304.         hr = pstr->SetSize(cchNeeded);
  1305.         if(SUCCEEDED(hr))
  1306.         {
  1307.             LPTSTR pchOut = (LPTSTR) *pstr;
  1308.             LPTSTR pszSeg;
  1309.             DWORD iSeg;
  1310.             partsOut->pszSegments = pchOut;
  1311.             partsOut->cSegments = 0;
  1312.             pszSeg = FirstLiveSegment(pszSegments, &iSeg, cSegs);
  1313.             while (IsLiveSegment(pszSeg))
  1314.             {
  1315.                 EscapeLiveSegment(pszSeg, &pchOut);
  1316.                 partsOut->cSegments++;
  1317.                 pszSeg = NextLiveSegment(pszSeg, &iSeg, cSegs);
  1318.             }
  1319.         }
  1320.     }
  1321.     else
  1322.     {
  1323.         partsOut->cSegments = cSegs;
  1324.         partsOut->pszSegments = pszSegments;
  1325.     }
  1326.     return hr;
  1327. }
  1328. PRIVATE HRESULT
  1329. EscapePath(PURLPARTS partsUrl, DWORD dwFlags, PURLPARTS partsOut, PSHSTR pstr)
  1330. {
  1331.     HRESULT hr = S_OK;
  1332.     ASSERT(partsUrl && partsOut && pstr);
  1333.     if(partsUrl->cSegments)
  1334.     {
  1335.         hr = EscapeSegments(partsUrl->pszSegments, partsUrl->cSegments, partsOut, pstr);
  1336.     }
  1337.     else 
  1338.     {
  1339.         partsOut->cSegments = 0;
  1340.         partsOut->pszSegments = NULL;
  1341.     }
  1342.     return hr;
  1343. }
  1344. HRESULT
  1345. SHUrlEscape (LPCTSTR pszUrl, 
  1346.              PSHSTR pstrOut,
  1347.              DWORD dwFlags)
  1348. {
  1349.     SHSTR strUrl;
  1350.     HRESULT hr;
  1351.     
  1352.     ASSERT(pszUrl && pstrOut);
  1353.     if(!pszUrl || !pstrOut)
  1354.         return E_INVALIDARG;
  1355.     //
  1356.     //  EscapeSpaces is remarkably stupid, 
  1357.     //  but so is this kind of functionality...
  1358.     //  it doesnt do any kind of real parsing, it
  1359.     //  only looks for spaces and escapes them...
  1360.     //
  1361.     if(dwFlags & URL_ESCAPE_SPACES_ONLY)
  1362.         return EscapeSpaces(pszUrl, pstrOut, dwFlags);
  1363.     pstrOut->Reset();
  1364.     hr = strUrl.SetStr(pszUrl);
  1365.         
  1366.     if(SUCCEEDED(hr))
  1367.     {
  1368.         URLPARTS partsUrl, partsOut;
  1369.         SHSTR strPath;
  1370.         BreakUrl(strUrl, &partsUrl);
  1371.         ZeroMemory(&partsOut, SIZEOF(URLPARTS));
  1372.         //
  1373.         //  NOTE the only function here that is really active right now is the EscapePath
  1374.         //  if some other part needs to be escaped, then add a new SHSTR in the 4th param
  1375.         //  and change the appropriate subroutine
  1376.         //
  1377.         if(
  1378.             (SUCCEEDED(hr = EscapeScheme(&partsUrl, dwFlags, &partsOut, NULL)))
  1379.             && (SUCCEEDED(hr = EscapeServer(&partsUrl, dwFlags, &partsOut, NULL)))
  1380.             && (SUCCEEDED(hr = EscapePath(&partsUrl, dwFlags, &partsOut, &strPath)))
  1381.             && (SUCCEEDED(hr = EscapeQuery(&partsUrl, dwFlags, &partsOut, NULL)))
  1382.             && (SUCCEEDED(hr = EscapeFragment(&partsUrl, dwFlags, &partsOut, NULL)))
  1383.            )
  1384.         {
  1385.             partsOut.dwFlags = partsUrl.dwFlags;
  1386.             hr = BuildUrl(&partsOut, dwFlags, pstrOut);
  1387.         }
  1388.     }
  1389.     else
  1390.         hr = E_OUTOFMEMORY;
  1391.     return hr;
  1392.     
  1393. }
  1394. /*+++
  1395.   SHUrlUnescape()
  1396.     Unescapes a string in place.  this is ok because 
  1397.     it should never grow
  1398.   Parameters
  1399.   IN -
  1400.     psz         string to unescape inplace
  1401.     dwFlags     the relevant URL_* flags, 
  1402.      
  1403.   Returns   
  1404.   HRESULT -
  1405.     SUCCESS     S_OK
  1406.     ERROR       DOESNT error right now
  1407.   Helper Routines
  1408.     HexToWord               takes a hexdigit and returns WORD with the right number or -1
  1409.     IsEscapedChar           looks at a ptr for "%XX" where X is a hexdigit
  1410.     TranslateEscapedChar    translates "%XX" to an 8 bit char
  1411. ---*/
  1412. PRIVATE WORD
  1413. HexToWord(TCHAR ch)
  1414. {
  1415.     if(ch >= TEXT('0') && ch <= TEXT('9'))
  1416.         return (WORD) ch - TEXT('0');
  1417.     if(ch >= TEXT('A') && ch <= TEXT('F'))
  1418.         return (WORD) ch - TEXT('A') + 10;
  1419.     if(ch >= TEXT('a') && ch <= TEXT('f'))
  1420.         return (WORD) ch - TEXT('a') + 10;
  1421.     ASSERT(FALSE);  //we have tried to use a non-hex number
  1422.     return (WORD) -1;
  1423. }
  1424. PRIVATE BOOL inline
  1425. IsEscapedChar(LPCTSTR pch)
  1426. {
  1427.     return (pch[0] == HEX_ESCAPE && IsHex(pch[1]) && IsHex(pch[2])) ? TRUE : FALSE;
  1428. }
  1429. PRIVATE TCHAR 
  1430. TranslateEscapedChar(LPCTSTR pch)
  1431. {
  1432.     TCHAR ch;
  1433.     ASSERT(IsEscapedChar(pch));
  1434.     pch++;
  1435.     ch = (TCHAR) HexToWord(*pch++) * 16; // hi nibble
  1436.     ch += HexToWord(*pch); // lo nibble
  1437.     return ch;
  1438. }
  1439. HRESULT SHUrlUnescape(LPTSTR psz, DWORD dwFlags)
  1440. {
  1441.     TCHAR *pchSrc = psz;
  1442.     TCHAR *pchDst = psz;
  1443.     BOOL fAfterSpecial = FALSE;
  1444.     while (*pchSrc)
  1445.     {
  1446.         if ((*pchSrc == POUND || *pchSrc == QUERY) && (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO))
  1447.         {
  1448.             lstrcpy(pchDst, pchSrc);
  1449.             pchDst += lstrlen(pchDst);
  1450.             break;
  1451.         }
  1452.         if (!fAfterSpecial && IsEscapedChar(pchSrc))
  1453.         {
  1454.             *pchDst++ = TranslateEscapedChar(pchSrc);
  1455.             pchSrc += 3; // enuff for "%XX"
  1456.         }
  1457.         else
  1458.         {
  1459.             *pchDst++ = *pchSrc++;
  1460.         }
  1461.     }
  1462.     TERMSTR(pchDst);
  1463.     return S_OK;
  1464. }
  1465. /*+++
  1466.   SHUrlParse()
  1467.     Canonicalize an URL 
  1468.     or Combine and Canonicalize two URLs
  1469.   Parameters
  1470.   IN -
  1471.     pszBase     the base or referring URL
  1472.     pszUrl      the relative URL, may be NULL
  1473.     dwFlags     the relevant URL_* flags, 
  1474.      
  1475.   Returns   
  1476.   HRESULT -
  1477.     SUCCESS     S_OK
  1478.     ERROR       appropriate error, usually just E_OUTOFMEMORY;
  1479.   NOTE:  pszUrl will always take precedence over pszBase.
  1480. ---*/
  1481. HRESULT SHUrlParse(LPCTSTR pszBase, LPCTSTR pszUrl, PSHSTR pstrOut, DWORD dwFlags)
  1482. {
  1483.     HRESULT hr = S_OK;
  1484.     URLPARTS partsBase, partsOut;
  1485.     
  1486.     SHSTR strBase;
  1487.     SHSTR strUrl;
  1488.     
  1489.     ASSERT(pszBase);
  1490.     ASSERT(pstrOut);
  1491.     pstrOut->Reset();
  1492.     //
  1493.     // Don't bother parsing if all we have in an inter-page link as the 
  1494.     // pszBase and no pszUrl to parse
  1495.     //
  1496.     if (pszBase[0] == POUND && (!pszUrl || !*pszUrl))
  1497.     {
  1498.         hr = pstrOut->SetStr(pszBase);
  1499.         goto quit;
  1500.     }
  1501.     //  
  1502.     //  now we will make copies of the URLs so that we can rip them apart
  1503.     //  CopyUrlForHTParse() will prepend a file: if it wants...
  1504.     //
  1505.     if(dwFlags & URL_WININET_COMPATIBILITY)
  1506.         hr = WininetCopyUrlForParse(&strBase, pszBase);
  1507.     else
  1508.     {
  1509.         hr = strBase.SetStr(pszBase);
  1510.         ASSERT(!IsUNC(pszBase) && !IsDrive(pszBase));
  1511.     }
  1512.     if(FAILED(hr))
  1513.         goto quit;
  1514.     // Trim leading and trailing whitespace
  1515.     strBase.Trim();
  1516.     // Remove tab characters.  Netscape does this.
  1517.     HTRemoveTabs((LPTSTR) strBase);
  1518.     //
  1519.     //  crack open the URLs in a violent manner.
  1520.     //  this can change the str buffers
  1521.     //  but thats ok because we dont touch them again
  1522.     //
  1523.     BreakUrl((LPTSTR) strBase, &partsBase);
  1524.     //
  1525.     //  if we are trying to combine... 
  1526.     //  then we handle the other URL
  1527.     //
  1528.     if(pszUrl)
  1529.     {
  1530.         URLPARTS partsUrl;
  1531.         if(dwFlags & URL_WININET_COMPATIBILITY)
  1532.             hr = WininetCopyUrlForParse(&strUrl, pszUrl);
  1533.         else
  1534.         {
  1535.             hr = strUrl.SetStr(pszUrl);
  1536.             ASSERT(!IsUNC(pszUrl) && !IsDrive(pszUrl));
  1537.         }
  1538.         if(FAILED(hr))
  1539.             goto quit;
  1540.         strUrl.Trim();
  1541.         HTRemoveTabs((LPTSTR) strUrl);
  1542.         BreakUrl((LPTSTR) strUrl, &partsUrl);
  1543.     
  1544.         //
  1545.         //  this is where the real combination logic happens
  1546.         //  this first parts is the one that takes precedence
  1547.         //
  1548.         BlendParts(&partsUrl, &partsBase, &partsOut);
  1549.     }
  1550.     else
  1551.         partsOut = partsBase;
  1552.     //
  1553.     //  we will now do the work of putting it together
  1554.     //  if these fail, it is because we are out of memory.
  1555.     //
  1556.     if (!(dwFlags & URL_DONT_SIMPLIFY))
  1557.         CanonParts(&partsOut);
  1558.     hr = BuildUrl(&partsOut, dwFlags, pstrOut);
  1559.     if(SUCCEEDED(hr))
  1560.     {
  1561.         if (dwFlags & URL_UNESCAPE)
  1562.             SHUrlUnescape((LPTSTR) *pstrOut, dwFlags);
  1563.         if (dwFlags & URL_ESCAPE_SPACES_ONLY || dwFlags & URL_ESCAPE_UNSAFE)
  1564.         {
  1565.             //
  1566.             //  we are going to reuse strUrl here
  1567.             //
  1568.             hr = strUrl.SetStr(*pstrOut);
  1569.             if(SUCCEEDED(hr))
  1570.                 hr = SHUrlEscape((LPTSTR)strUrl, pstrOut, dwFlags);
  1571.         }
  1572.     }
  1573.     if (SUCCEEDED(hr) && 
  1574.         (dwFlags & URL_WININET_COMPATIBILITY) && 
  1575.         (partsOut.dwScheme == URL_SCHEME_FILE))
  1576.         WininetFixFileSlashes((LPTSTR) *pstrOut);
  1577. quit:
  1578.     
  1579.     
  1580.     if(FAILED(hr))
  1581.         pstrOut->Reset();
  1582.     return hr;  
  1583. }
  1584. HRESULT
  1585. SHPathCreateFromUrl(LPCTSTR pszUrl, PSHSTR pstrOut, DWORD dwFlags)
  1586. {
  1587.     HRESULT hr;
  1588.     SHSTR strUrl;
  1589.     ASSERT(pszUrl && pstrOut);
  1590.     pstrOut->Reset();
  1591.     hr = strUrl.SetStr(pszUrl);
  1592.     if(SUCCEEDED(hr))
  1593.     {
  1594.         URLPARTS partsUrl;
  1595.         //  first we need to break it open
  1596.         BreakUrl((LPTSTR) strUrl, &partsUrl);
  1597.         //  then we make sure it is a file:
  1598.         if(partsUrl.dwScheme == URL_SCHEME_FILE)
  1599.         {
  1600.             //  this will disable a preceding slash when there is a drive
  1601.             if(IsDrive(partsUrl.pszSegments))
  1602.                 partsUrl.dwFlags = (partsUrl.dwFlags & ~UPF_SEG_ABSOLUTE);
  1603.             //  if there is a zero length server then 
  1604.             //  we skip building it
  1605.             if(partsUrl.pszServer && !*partsUrl.pszServer)
  1606.                 partsUrl.pszServer = NULL;
  1607.             //
  1608.             //  then go ahead and put the path together
  1609.             if( (SUCCEEDED(hr = BuildServer(&partsUrl, dwFlags, pstrOut))) &&
  1610.                 (SUCCEEDED(hr = BuildPath(&partsUrl, dwFlags, pstrOut)))
  1611.               )
  1612.             {
  1613.                 //  then decode it cuz paths arent escaped
  1614.                 ConvertChar((LPTSTR)*pstrOut, SLASH, WHACK);
  1615.                 SHUrlUnescape((LPTSTR)*pstrOut, dwFlags);
  1616.             }
  1617.         }
  1618.         else
  1619.             hr = E_INVALIDARG;
  1620.     }
  1621.     return hr;
  1622. }
  1623. HRESULT
  1624. SHUrlCreateFromPath(LPCTSTR pszPath, PSHSTR pstrOut, DWORD dwFlags)
  1625. {
  1626.     HRESULT hr;
  1627.     SHSTR strPath;
  1628.     ASSERT(pszPath && pstrOut);
  1629.     pstrOut->Reset();
  1630.     hr = strPath.SetStr(pszPath);
  1631.     if(SUCCEEDED(hr))
  1632.     {
  1633.         URLPARTS partsIn, partsOut;
  1634.         SHSTR strEscapedPath, strEscapedServer;
  1635.         LPTSTR pch = (LPTSTR)strPath;
  1636.         ZeroMemory(&partsIn, SIZEOF(URLPARTS));
  1637.         partsIn.pszScheme = (LPTSTR) c_szFileScheme;
  1638.         partsIn.dwScheme = URL_SCHEME_FILE;
  1639.         //  first break the path
  1640.         BreakServer(&pch, &partsIn);
  1641.         BreakPath(&pch, &partsIn);
  1642.         partsOut = partsIn;
  1643.         //  then escape the path
  1644.         hr = EscapePath(&partsIn, dwFlags, &partsOut, &strEscapedPath);
  1645.         if(SUCCEEDED(hr) && partsOut.pszServer)
  1646.         {
  1647.             //
  1648.             //  i am treating the pszServer exactly like a path segment
  1649.             //
  1650.             DWORD cbNeeded = EscapeSegmentsGetNeededSize(partsOut.pszServer, 1);
  1651.             if(cbNeeded && SUCCEEDED(hr = strEscapedServer.SetSize(cbNeeded)))
  1652.             {
  1653.                 pch = (LPTSTR) strEscapedServer;
  1654.                 EscapeLiveSegment(partsOut.pszServer, &pch);
  1655.                 partsOut.pszServer = (LPTSTR) strEscapedServer;
  1656.             }
  1657.         }
  1658.         else if(partsOut.dwFlags & UPF_SEG_ABSOLUTE)
  1659.             partsOut.pszServer = TEXT("");
  1660.         //  then build the URL
  1661.         if(SUCCEEDED(hr))
  1662.             hr = BuildUrl(&partsOut, dwFlags, pstrOut);
  1663.     }
  1664.     return hr;
  1665. }
  1666. PRIVATE HRESULT
  1667. CopyOutA(PSHSTRA pstr, LPSTR psz, LPDWORD pcch)
  1668. {
  1669.     HRESULT hr = S_OK;
  1670.     DWORD cch;
  1671.     ASSERT(pstr);
  1672.     ASSERT(psz);
  1673.     ASSERT(pcch);
  1674.     cch = pstr->GetLen();
  1675.     if(*pcch > cch)
  1676.         lstrcpyA(psz, pstr->GetStr());
  1677.     else
  1678.         hr = E_POINTER;
  1679.     *pcch = cch + (FAILED(hr) ? 1 : 0);
  1680.     return hr;
  1681. }
  1682. PRIVATE HRESULT
  1683. CopyOutW(PSHSTRW pstr, LPWSTR psz, LPDWORD pcch)
  1684. {
  1685.     HRESULT hr = S_OK;
  1686.     DWORD cch;
  1687.     ASSERT(pstr);
  1688.     ASSERT(psz);
  1689.     ASSERT(pcch);
  1690.     cch = pstr->GetLen();
  1691.     if(*pcch > cch)
  1692.         lstrcpyW(psz, pstr->GetStr());
  1693.     else
  1694.         hr = E_POINTER;
  1695.     *pcch = cch + (FAILED(hr) ? 1 : 0);
  1696.     return hr;
  1697. }
  1698. LWSTDAPI_(HRESULT)
  1699. UrlCombine(LPCTSTR pszBase, 
  1700.            LPCTSTR pszRelative, 
  1701.            LPTSTR pszCombined, 
  1702.            LPDWORD pcchCombined, 
  1703.            DWORD dwFlags)
  1704. {
  1705.     HRESULT hr;
  1706.     SHSTR strOut;
  1707.     if (!pszBase || !pszRelative || !pszCombined ||
  1708.         !pcchCombined || !*pcchCombined)
  1709.         hr = E_INVALIDARG;
  1710.     else
  1711.         hr = SHUrlParse(pszBase, pszRelative, &strOut, dwFlags);
  1712.     if(SUCCEEDED(hr) )
  1713.         hr = CopyOutA(&strOut, pszCombined, pcchCombined);
  1714.     return hr;
  1715. }
  1716. LWSTDAPI_(HRESULT)
  1717. UrlCanonicalize(LPCTSTR pszUrl,
  1718.                 LPTSTR pszCanonicalized, 
  1719.                 LPDWORD pcchCanonicalized, 
  1720.                 DWORD dwFlags)
  1721. {
  1722.     HRESULT hr;
  1723.     SHSTR strOut;
  1724.     if (!pszUrl || !pszCanonicalized || 
  1725.         !pcchCanonicalized || !*pcchCanonicalized )
  1726.         hr = E_INVALIDARG;
  1727.     else
  1728.         hr = SHUrlParse(pszUrl, NULL,&strOut, dwFlags);
  1729.     if(SUCCEEDED(hr) )
  1730.         hr = CopyOutA(&strOut, pszCanonicalized, pcchCanonicalized);
  1731.     return hr;
  1732. }
  1733. LWSTDAPI
  1734. UrlEscape(LPCTSTR pszUrl,
  1735.                 LPTSTR pszEscaped, 
  1736.                 LPDWORD pcchEscaped, 
  1737.                 DWORD dwFlags)
  1738. {
  1739.     HRESULT hr;
  1740.     SHSTR strOut;
  1741.     if (!pszUrl || !pszEscaped || 
  1742.         !pcchEscaped || !*pcchEscaped )
  1743.         hr = E_INVALIDARG;
  1744.     else
  1745.         hr = SHUrlEscape(pszUrl, &strOut, dwFlags);
  1746.     if(SUCCEEDED(hr) )
  1747.         hr = CopyOutA(&strOut, pszEscaped, pcchEscaped);
  1748.     return hr;
  1749. }
  1750. LWSTDAPI_(int)
  1751. UrlCompare(LPCTSTR psz1, LPCTSTR psz2, BOOL fIgnoreSlash)
  1752. {
  1753.     SHSTR str1, str2;
  1754.     if (psz1 && psz2)
  1755.     {
  1756.         if(SUCCEEDED(SHUrlParse(psz1, NULL, &str1, URL_UNESCAPE)))
  1757.         {
  1758.             if(SUCCEEDED(SHUrlParse(psz2, NULL, &str2, URL_UNESCAPE)))
  1759.             {
  1760.                 if(fIgnoreSlash)
  1761.                 {
  1762.                     LPTSTR pch;
  1763.                     pch = (LPTSTR)str1 + str1.GetLen() - 1;
  1764.                     if(*pch == SLASH)
  1765.                         TERMSTR(pch);
  1766.                     pch = (LPTSTR)str2 + str2.GetLen() - 1;
  1767.                     if(*pch == SLASH)
  1768.                         TERMSTR(pch);
  1769.                 }
  1770.                 return lstrcmp((LPTSTR) str1, (LPTSTR) str2);
  1771.             }
  1772.         }
  1773.     }
  1774.     return lstrcmp(psz1, psz2);
  1775. }
  1776.                     
  1777.     
  1778. LWSTDAPI
  1779. UrlUnescape(LPTSTR pszUrl, LPTSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
  1780. {
  1781.     HRESULT hr = S_OK;
  1782.     if(pszUrl)
  1783.     {
  1784.         if(dwFlags & URL_UNESCAPE_INPLACE)
  1785.         {
  1786.             SHUrlUnescape(pszUrl, dwFlags);
  1787.         }
  1788.         else if(pszOut && pcchOut && *pcchOut)
  1789.         {
  1790.             SHSTR strUrl;
  1791.             hr = strUrl.SetStr(pszUrl);
  1792.             if(SUCCEEDED(hr))
  1793.             {
  1794.                 SHUrlUnescape((LPTSTR)strUrl, dwFlags);
  1795.                 hr = CopyOutA(&strUrl, pszOut, pcchOut);
  1796.             }
  1797.         }
  1798.         else
  1799.             hr = E_INVALIDARG;
  1800.     }
  1801.     else 
  1802.         hr = E_INVALIDARG;
  1803.     return hr;
  1804. }
  1805. LWSTDAPI
  1806. PathCreateFromUrl(LPCTSTR pszUrl, LPTSTR pszPath, LPDWORD pcchPath, DWORD dwFlags)
  1807. {
  1808.     HRESULT hr;
  1809.     SHSTR strOut;
  1810.     if (!pszUrl || !pszPath || 
  1811.         !pcchPath || !*pcchPath )
  1812.         hr = E_INVALIDARG;
  1813.     else
  1814.         hr = SHPathCreateFromUrl(pszUrl, &strOut, dwFlags);
  1815.     if(SUCCEEDED(hr) )
  1816.         hr = CopyOutA(&strOut, pszPath, pcchPath);
  1817.     return hr;
  1818. }
  1819. LWSTDAPI
  1820. UrlCreateFromPath(LPCTSTR pszPath, LPTSTR pszUrl, LPDWORD pcchUrl, DWORD dwFlags)
  1821. {
  1822.     HRESULT hr;
  1823.     SHSTR strOut;
  1824.     if (!pszPath || !pszUrl || 
  1825.         !pcchUrl || !*pcchUrl )
  1826.         hr = E_INVALIDARG;
  1827.     else
  1828.         hr = SHUrlCreateFromPath(pszPath, &strOut, dwFlags);
  1829.     if(SUCCEEDED(hr) )
  1830.         hr = CopyOutA(&strOut, pszUrl, pcchUrl);
  1831.     return hr;
  1832. }
  1833. //
  1834. //  UNICODE version must thunk down because of URL restrictions to 
  1835. //  ASCII charset.  otherwise weird probs crop up
  1836. //
  1837. LWSTDAPI_(HRESULT)
  1838. UrlCombineW(LPCWSTR pszBase, 
  1839.            LPCWSTR pszRelative, 
  1840.            LPWSTR pszCombined, 
  1841.            LPDWORD pcchCombined, 
  1842.            DWORD dwFlags)
  1843. {
  1844.     HRESULT hr;
  1845.     SHSTRW strwOut;
  1846.     if (!pszBase || !pszRelative || !pszCombined ||
  1847.         !pcchCombined || !*pcchCombined)
  1848.         hr = E_INVALIDARG;
  1849.     else
  1850.     {
  1851.         SHSTRA straOut;
  1852.         SHSTRA straBase;
  1853.         SHSTRA straRelative;
  1854.         if(SUCCEEDED(straBase.SetStr(pszBase)) && SUCCEEDED(straRelative.SetStr(pszBase)))
  1855.             hr = SHUrlParse((LPSTR) straBase, (LPSTR)straRelative, &straOut, dwFlags);
  1856.         else
  1857.             hr = E_OUTOFMEMORY;
  1858.         if(SUCCEEDED(hr))
  1859.             hr = strwOut.SetStr(straOut);
  1860.     }
  1861.     if(SUCCEEDED(hr) )
  1862.         hr = CopyOutW(&strwOut, pszCombined, pcchCombined);
  1863.     return hr;
  1864. }
  1865. LWSTDAPI_(HRESULT)
  1866. UrlCanonicalizeW(LPCWSTR pszUrl, 
  1867.            LPWSTR pszCanonicalized, 
  1868.            LPDWORD pcchCanonicalized, 
  1869.            DWORD dwFlags)
  1870. {
  1871.     HRESULT hr;
  1872.     SHSTRW strwOut;
  1873.     if (!pszUrl || !pszCanonicalized ||
  1874.         !pcchCanonicalized || !*pcchCanonicalized)
  1875.         hr = E_INVALIDARG;
  1876.     else
  1877.     {
  1878.         SHSTRA straOut;
  1879.         SHSTRA straUrl;
  1880.         if(SUCCEEDED(straUrl.SetStr(pszUrl))) 
  1881.             hr = SHUrlParse((LPSTR) straUrl, NULL, &straOut, dwFlags);
  1882.         else
  1883.             hr = E_OUTOFMEMORY;
  1884.         if(SUCCEEDED(hr))
  1885.             hr = strwOut.SetStr(straOut);
  1886.     }
  1887.     if(SUCCEEDED(hr) )
  1888.         hr = CopyOutW(&strwOut, pszCanonicalized, pcchCanonicalized);
  1889.     return hr;
  1890. }
  1891. LWSTDAPI
  1892. UrlEscapeW(LPCWSTR pszUrl, 
  1893.            LPWSTR pszEscaped, 
  1894.            LPDWORD pcchEscaped, 
  1895.            DWORD dwFlags)
  1896. {
  1897.     HRESULT hr;
  1898.     SHSTRW strwOut;
  1899.     if (!pszUrl || !pszEscaped ||
  1900.         !pcchEscaped || !*pcchEscaped)
  1901.         hr = E_INVALIDARG;
  1902.     else
  1903.     {
  1904.         SHSTRA straOut;
  1905.         SHSTRA straUrl;
  1906.         if(SUCCEEDED(straUrl.SetStr(pszUrl))) 
  1907.             hr = SHUrlEscape((LPSTR) straUrl, &straOut, dwFlags);
  1908.         else
  1909.             hr = E_OUTOFMEMORY;
  1910.         if(SUCCEEDED(hr))
  1911.             hr = strwOut.SetStr(straOut);
  1912.     }
  1913.     if(SUCCEEDED(hr) )
  1914.         hr = CopyOutW(&strwOut, pszEscaped, pcchEscaped);
  1915.     return hr;
  1916. }
  1917. LWSTDAPI_(int)
  1918. UrlCompareW(LPCWSTR psz1, LPCWSTR psz2, BOOL fIgnoreSlash)
  1919. {
  1920.     if (psz1 && psz2)
  1921.     {
  1922.         SHSTRA stra1, stra2, straRaw1, straRaw2;
  1923.         if( SUCCEEDED(straRaw1.SetStr(psz1)) && SUCCEEDED(straRaw2.SetStr(psz2)) && 
  1924.             SUCCEEDED(SHUrlParse((LPSTR)straRaw1, NULL, &stra1, URL_UNESCAPE)))
  1925.         {
  1926.             if(SUCCEEDED(SHUrlParse((LPSTR)straRaw2, NULL, &stra2, URL_UNESCAPE)))
  1927.             {
  1928.                 if(fIgnoreSlash)
  1929.                 {
  1930.                     LPTSTR pch;
  1931.                     pch = (LPSTR)stra1 + stra1.GetLen() - 1;
  1932.                     if(*pch == SLASH)
  1933.                         TERMSTR(pch);
  1934.                     pch = (LPSTR)stra2 + stra2.GetLen() - 1;
  1935.                     if(*pch == SLASH)
  1936.                         TERMSTR(pch);
  1937.                 }
  1938.                 return lstrcmpA((LPSTR) stra1, (LPSTR) stra2);
  1939.             }
  1940.         }
  1941.     }
  1942.     return lstrcmpW(psz1, psz2);
  1943. }
  1944.                     
  1945.     
  1946. LWSTDAPI
  1947. UrlUnescapeW(LPWSTR pszUrl, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
  1948. {
  1949.     HRESULT hr = S_OK;
  1950.     if(pszUrl)
  1951.     {
  1952.         SHSTRA straUrl;
  1953.         hr = straUrl.SetStr(pszUrl);
  1954.         if(SUCCEEDED(hr))
  1955.         {
  1956.             SHSTRW strwUrl;
  1957.             SHUrlUnescape((LPTSTR)straUrl, dwFlags);
  1958.             hr = strwUrl.SetStr((LPSTR)straUrl);
  1959.             if(SUCCEEDED(hr))
  1960.             {
  1961.                 if(dwFlags & URL_UNESCAPE_INPLACE)
  1962.                 {
  1963.                     lstrcpyW(pszUrl, strwUrl.GetStr());
  1964.                 }
  1965.                 else if(pszOut && pcchOut && *pcchOut)
  1966.                 {
  1967.                     hr = CopyOutW(&strwUrl, pszOut, pcchOut);
  1968.                 }
  1969.                 else
  1970.                     hr = E_INVALIDARG;
  1971.             }
  1972.         }
  1973.     }
  1974.     else 
  1975.         hr = E_INVALIDARG;
  1976.     return hr;
  1977. }
  1978. LWSTDAPI
  1979. PathCreateFromUrlW
  1980.            (LPCWSTR pszUrl, 
  1981.            LPWSTR pszPath, 
  1982.            LPDWORD pcchPath, 
  1983.            DWORD dwFlags)
  1984. {
  1985.     HRESULT hr;
  1986.     SHSTRW strwOut;
  1987.     if (!pszUrl || !pszPath ||
  1988.         !pcchPath || !*pcchPath)
  1989.         hr = E_INVALIDARG;
  1990.     else
  1991.     {
  1992.         SHSTRA straOut;
  1993.         SHSTRA straUrl;
  1994.         if(SUCCEEDED(straUrl.SetStr(pszUrl))) 
  1995.             hr = SHPathCreateFromUrl((LPSTR) straUrl, &straOut, dwFlags);
  1996.         else
  1997.             hr = E_OUTOFMEMORY;
  1998.         if(SUCCEEDED(hr))
  1999.             hr = strwOut.SetStr(straOut);
  2000.     }
  2001.     if(SUCCEEDED(hr) )
  2002.         hr = CopyOutW(&strwOut, pszPath, pcchPath);
  2003.     return hr;
  2004. }
  2005. LWSTDAPI
  2006. UrlCreateFromPathW
  2007.            (LPCWSTR pszPath, 
  2008.            LPWSTR pszUrl, 
  2009.            LPDWORD pcchUrl, 
  2010.            DWORD dwFlags)
  2011. {
  2012.     HRESULT hr;
  2013.     SHSTRW strwOut;
  2014.     if (!pszPath || !pszUrl ||
  2015.         !pcchUrl || !*pcchUrl)
  2016.         hr = E_INVALIDARG;
  2017.     else
  2018.     {
  2019.         SHSTRA straOut;
  2020.         SHSTRA straPath;
  2021.         if(SUCCEEDED(straPath.SetStr(pszPath))) 
  2022.             hr = SHUrlCreateFromPath((LPSTR) straPath, &straOut, dwFlags);
  2023.         else
  2024.             hr = E_OUTOFMEMORY;
  2025.         if(SUCCEEDED(hr))
  2026.             hr = strwOut.SetStr(straOut);
  2027.     }
  2028.     if(SUCCEEDED(hr) )
  2029.         hr = CopyOutW(&strwOut, pszUrl, pcchUrl);
  2030.     return hr;
  2031. }