/ CFURL.c
CFURL.c
   1  /*
   2   * Copyright (c) 2015 Apple Inc. All rights reserved.
   3   *
   4   * @APPLE_LICENSE_HEADER_START@
   5   *
   6   * This file contains Original Code and/or Modifications of Original Code
   7   * as defined in and that are subject to the Apple Public Source License
   8   * Version 2.0 (the 'License'). You may not use this file except in
   9   * compliance with the License. Please obtain a copy of the License at
  10   * http://www.opensource.apple.com/apsl/ and read it before using this
  11   * file.
  12   *
  13   * The Original Code and all software distributed under the License are
  14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18   * Please see the License for the specific language governing rights and
  19   * limitations under the License.
  20   *
  21   * @APPLE_LICENSE_HEADER_END@
  22   */
  23  
  24  /*	CFURL.c
  25  	Copyright (c) 1998-2014, Apple Inc. All rights reserved.
  26  	Responsibility: Jim Luther/Chris Linn
  27  */
  28  
  29  #include <CoreFoundation/CFURL.h>
  30  #include <CoreFoundation/CFPriv.h>
  31  #include <CoreFoundation/CFCharacterSetPriv.h>
  32  #include <CoreFoundation/CFNumber.h>
  33  #include "CFInternal.h"
  34  #include <CoreFoundation/CFStringEncodingConverter.h>
  35  #include <limits.h>
  36  #include <stdlib.h>
  37  #include <stdio.h>
  38  #include <string.h>
  39  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
  40  #if DEPLOYMENT_TARGET_MACOSX
  41  #include <CoreFoundation/CFNumberFormatter.h>
  42  #endif
  43  #include <unistd.h>
  44  #include <sys/stat.h>
  45  #include <sys/types.h>
  46  #include <sys/syslog.h>
  47  #include <CoreFoundation/CFURLPriv.h>
  48  #endif
  49  
  50  #ifndef DEBUG_URL_MEMORY_USAGE
  51  // enables various statistical counters which can be displayed with __CFURLDumpMemRecord().
  52  #define DEBUG_URL_MEMORY_USAGE 0
  53  #endif
  54  
  55  #ifndef DEBUG_URL_INITIALIZER_LOGGING
  56  // enables logging in URL initializer. You get to see the inputs and output for each URL created.
  57  #define DEBUG_URL_INITIALIZER_LOGGING 0
  58  #endif
  59  
  60  
  61  static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute);
  62  static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute);
  63  static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory, Boolean isAbsolute, Boolean *posixAndUrlPathsMatch);
  64  static CFStringRef CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, Boolean isDirectory, Boolean isAbsolute, Boolean windowsPath, Boolean *addedPercentEncoding);
  65  CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase);
  66  CF_EXPORT CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator);
  67  #if DEPLOYMENT_TARGET_MACOSX
  68  static Boolean _CFURLHasFileURLScheme(CFURLRef url, Boolean *hasScheme);
  69  #endif
  70  
  71  
  72  
  73  // When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but
  74  // when it is defined, we must prefix with static to prevent the string from being exported
  75  #ifdef __CONSTANT_CFSTRINGS__
  76  static CONST_STRING_DECL(kCFURLHTTPScheme, "http")
  77  static CONST_STRING_DECL(kCFURLHTTPSScheme, "https")
  78  static CONST_STRING_DECL(kCFURLFileScheme, "file")
  79  static CONST_STRING_DECL(kCFURLDataScheme, "data")
  80  static CONST_STRING_DECL(kCFURLFTPScheme, "ftp")
  81  static CONST_STRING_DECL(kCFURLLocalhost, "localhost")
  82  #else
  83  CONST_STRING_DECL(kCFURLHTTPScheme, "http")
  84  CONST_STRING_DECL(kCFURLHTTPSScheme, "https")
  85  CONST_STRING_DECL(kCFURLFileScheme, "file")
  86  CONST_STRING_DECL(kCFURLDataScheme, "data")
  87  CONST_STRING_DECL(kCFURLFTPScheme, "ftp")
  88  CONST_STRING_DECL(kCFURLLocalhost, "localhost")
  89  #endif
  90  
  91  #if DEBUG_URL_MEMORY_USAGE
  92  static uint numURLs = 0;                    // number of URLs allocated
  93  static uint numDealloced = 0;               // number of URLs deallocated
  94  static uint numURLsParsed = 0;              // number of URLs created from a string which had to be parsed
  95  static uint numExtraDataAllocated = 0;      // number of URLs with additional data -- either because URLHandle was used, or because a sanitizedString was needed
  96  static uint numURLsWithBaseURL = 0;         // number of URLs with a baseURL
  97  static uint numNonUTF8EncodedURLs = 0;      // number of URLs that don't have UTF8 encoding
  98  #endif
  99  
 100  /* The bit flags in myURL->_flags */
 101  // component bits
 102  #define HAS_SCHEME                      (0x00000001)
 103  #define HAS_USER                        (0x00000002)
 104  #define HAS_PASSWORD                    (0x00000004)
 105  #define HAS_HOST                        (0x00000008)
 106  #define HAS_PORT                        (0x00000010)
 107  #define HAS_PATH                        (0x00000020)
 108  #define HAS_PARAMETERS                  (0x00000040)
 109  #define HAS_QUERY                       (0x00000080)
 110  #define HAS_FRAGMENT                    (0x00000100)
 111  #define MAX_COMPONENTS                  9
 112  // various boolean flags
 113  #define IS_IPV6_ENCODED                 (0x00000400)
 114  #define IS_DIRECTORY                    (0x00000800)
 115  #define IS_CANONICAL_FILE_URL           (0x00001000) // if set, the URL is a file URL in the form "file://<absolute_percent_encoded_path>" (it was created from a file system path or representation)
 116  #define PATH_HAS_FILE_ID                (0x00002000)
 117  #define IS_DECOMPOSABLE                 (0x00004000)
 118  #define POSIX_AND_URL_PATHS_MATCH       (0x00008000) // POSIX_AND_URL_PATHS_MATCH will only be true if the URL path and the POSIX path are identical, character for character, except for the presence/absence of a trailing slash on directories
 119  #define ORIGINAL_AND_URL_STRINGS_MATCH  (0x00010000)
 120  // scheme bits and amount to shift it to translate to the kXXXXScheme enums
 121  #define SCHEME_TYPE_MASK                (0xE0000000)
 122  #define SCHEME_SHIFT                    29
 123  enum {
 124      kHasUncommonScheme  = 0,    // scheme is uncommon or scheme isn't in the canonical form (all lower case)
 125      kHasHttpScheme      = 1,
 126      kHasHttpsScheme     = 2,
 127      kHasFileScheme      = 3,
 128      kHasDataScheme      = 4,
 129      kHasFtpScheme       = 5,
 130      kMaxScheme
 131  };
 132  // accessors for the scheme bits in _flags
 133  CF_INLINE UInt32 _getSchemeTypeFromFlags(UInt32 flags);
 134  CF_INLINE void _setSchemeTypeInFlags(UInt32 *flags, UInt32 schemeType);
 135  
 136  // Other useful defines
 137  #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
 138  #define RESOURCE_SPECIFIER_MASK  (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
 139  // These flags can be compared for equality since these are all set once when the CFURL is created.
 140  // IS_CANONICAL_FILE_URL cannot be compared since we don't always create the URL string.
 141  // POSIX_AND_URL_PATHS_MATCH cannot be compared because it may not be set
 142  // ORIGINAL_AND_URL_STRINGS_MATCH cannot be compared because it gets set on demand later.
 143  #define EQUAL_FLAGS_MASK (HAS_SCHEME | HAS_USER | HAS_PASSWORD | HAS_HOST | HAS_PORT | HAS_PATH | HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT | IS_IPV6_ENCODED | IS_DIRECTORY | PATH_HAS_FILE_ID | IS_DECOMPOSABLE | SCHEME_TYPE_MASK )
 144  
 145  // The value of FULL_URL_REPRESENTATION must not be in the CFURLPathStyle enums. Also, its value is exposed via _CFURLCopyPropertyListRepresentation to the Finder so don't change it.  
 146  #define FULL_URL_REPRESENTATION (0xF)
 147  
 148  /* The bit flags in _CFURLAdditionalData->_additionalDataFlags */
 149  /* If ORIGINAL_AND_URL_STRINGS_MATCH in myURL->_flags is false, these bits determine where they differ. XXXX_DIFFERS must match the HAS_XXXX */
 150  #define SCHEME_DIFFERS                  HAS_SCHEME      // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
 151  #define USER_DIFFERS                    HAS_USER
 152  #define PASSWORD_DIFFERS                HAS_PASSWORD
 153  #define HOST_DIFFERS                    HAS_HOST
 154  #define PORT_DIFFERS                    HAS_PORT        // Port can actually never differ because if there were a non-digit following a colon in the net location, we'd interpret the whole net location as the host 
 155  #define PATH_DIFFERS                    HAS_PATH        // unused
 156  #define PARAMETERS_DIFFER               HAS_PARAMETERS  // unused
 157  #define QUERY_DIFFER                    HAS_QUERY       // unused
 158  #define FRAGMENT_DIFFER                 HAS_FRAGMENT    // unused
 159  
 160  #define FILE_ID_PREFIX ".file"
 161  #define FILE_ID_KEY "id"
 162  #define FILE_ID_PREAMBLE "/.file/id="
 163  #define FILE_ID_PREAMBLE_LENGTH 10
 164  
 165  #define FILE_PREFIX "file://"
 166  static const UInt8 fileURLPrefix[] = FILE_PREFIX;
 167  
 168  // FILE_PREFIX_WITH_AUTHORITY and fileURLPrefixWithAuthority are only needed because some code incorrectly expects file URLs to have a host of "localhost", so if the application is linked on or before OS X 10.9 or iOS 7.0, we add "localhost" to file path URLs we create.
 169  #define FILE_PREFIX_WITH_AUTHORITY "file://localhost"
 170  static const UInt8 fileURLPrefixWithAuthority[] = FILE_PREFIX_WITH_AUTHORITY;
 171  
 172  static Boolean AddAuthorityToFileURL(void)
 173  {
 174      static Boolean result = false;
 175      return ( result );
 176  }
 177  
 178  //	In order to reduce the sizeof ( __CFURL ), move these items into a seperate structure which is
 179  //	only allocated when necessary.  In my tests, it's almost never needed -- very rarely does a CFURL have
 180  //	either a sanitized string or a reserved pointer for URLHandle.
 181  struct _CFURLAdditionalData {
 182      void *_reserved; // Reserved for URLHandle's use.
 183      CFStringRef _sanitizedString; // The fully compliant RFC string.  This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false.
 184      UInt32 _additionalDataFlags; // these flags only apply to things we need to keep state for in _CFURLAdditionalData (like the XXXX_DIFFERS flags)
 185  };
 186  
 187  struct __CFURL {
 188      CFRuntimeBase _cfBase;
 189      UInt32 _flags;
 190      CFStringEncoding _encoding; // The encoding to use when asked to remove percent escapes
 191      CFStringRef _string; // Never NULL
 192      CFURLRef _base;
 193      struct _CFURLAdditionalData* _extra;
 194      void *_resourceInfo;    // For use by CoreServicesInternal to cache property values. Retained and released by CFURL.
 195      CFRange _ranges[1]; // variable length (1 to 9) array of ranges
 196  };
 197  
 198  
 199  CF_INLINE void* _getReserved ( const struct __CFURL* url )
 200  {
 201      if ( url && url->_extra ) {
 202          return ( url->_extra->_reserved );
 203      }
 204      else {
 205          return ( NULL );
 206      }
 207  }
 208  
 209  CF_INLINE CFStringRef _getSanitizedString(const struct __CFURL* url)
 210  {
 211      if ( url && url->_extra ) {
 212          return ( url->_extra->_sanitizedString );
 213      }
 214      else {
 215  	return ( NULL );
 216      }
 217  }
 218  
 219  CF_INLINE UInt32 _getAdditionalDataFlags(const struct __CFURL* url)
 220  {
 221      if ( url && url->_extra ) {
 222          return ( url->_extra->_additionalDataFlags );
 223      }
 224      else {
 225  	return ( 0 );
 226      }
 227  }
 228  
 229  CF_INLINE void* _getResourceInfo ( const struct __CFURL* url )
 230  {
 231      if ( url ) {
 232          return url->_resourceInfo;
 233      }
 234      else {
 235          return NULL;
 236      }
 237  }
 238  
 239  static void _CFURLAllocateExtraDataspace( struct __CFURL* url )
 240  {	
 241      if ( url && ! url->_extra )
 242      {	struct _CFURLAdditionalData* extra = (struct _CFURLAdditionalData*) CFAllocatorAllocate( CFGetAllocator( url), sizeof( struct _CFURLAdditionalData ), __kCFAllocatorGCScannedMemory);
 243  	
 244  	extra->_reserved = _getReserved( url );
 245          extra->_additionalDataFlags = _getAdditionalDataFlags(url);
 246  	extra->_sanitizedString = _getSanitizedString(url);
 247  	
 248  	url->_extra = extra;
 249  	
 250  	#if DEBUG_URL_MEMORY_USAGE
 251  	numExtraDataAllocated ++;
 252  	#endif
 253      }
 254  }
 255  
 256  CF_INLINE void _setReserved ( struct __CFURL* url, void* reserved )
 257  {
 258      if ( url )
 259      {
 260          // Don't allocate extra space if we're just going to be storing NULL
 261          if ( !url->_extra && reserved )
 262              _CFURLAllocateExtraDataspace( url );
 263          
 264          if ( url->_extra )
 265              __CFAssignWithWriteBarrier((void **)&url->_extra->_reserved, reserved);
 266      }
 267  }
 268  
 269  CF_INLINE void _setSanitizedString( struct __CFURL* url, CFMutableStringRef sanitizedString )
 270  {
 271      if ( url )
 272      {
 273          // Don't allocate extra space if we're just going to be storing NULL
 274          if ( !url->_extra && sanitizedString ) {
 275              _CFURLAllocateExtraDataspace( url );
 276          }
 277          
 278          if ( url->_extra ) {
 279              if ( url->_extra->_sanitizedString ) {
 280                  CFRelease(url->_extra->_sanitizedString);
 281              }
 282              url->_extra->_sanitizedString = CFStringCreateCopy(CFGetAllocator(url), sanitizedString);
 283  
 284          }
 285      }
 286  }
 287  
 288  CF_INLINE void _setAdditionalDataFlags(struct __CFURL* url, UInt32 additionalDataFlags)
 289  {
 290      if ( url )
 291      {
 292          // Don't allocate extra space if we're just going to be storing 0
 293          if ( !url->_extra && (additionalDataFlags != 0) ) {
 294              _CFURLAllocateExtraDataspace( url );
 295          }
 296          
 297          if ( url->_extra ) {
 298              url->_extra->_additionalDataFlags = additionalDataFlags;
 299          }
 300      }
 301  }
 302  
 303  CF_INLINE void _setResourceInfo ( struct __CFURL* url, void* resourceInfo )
 304  {
 305      // Must be atomic
 306      // Never a GC object
 307      if ( url && OSAtomicCompareAndSwapPtrBarrier( NULL, resourceInfo, &url->_resourceInfo )) {
 308  	CFRetain( resourceInfo );
 309      }
 310  }
 311  
 312  CF_INLINE UInt32 _getSchemeTypeFromFlags(UInt32 flags)
 313  {
 314      return ( (flags & SCHEME_TYPE_MASK) >> SCHEME_SHIFT );
 315  }
 316  
 317  CF_INLINE void _setSchemeTypeInFlags(UInt32 *flags, UInt32 schemeType)
 318  {
 319      CFAssert2((schemeType >= kHasUncommonScheme) &&  (schemeType < kMaxScheme), __kCFLogAssertion, "%s(): Received bad schemeType %d", __PRETTY_FUNCTION__, schemeType);
 320      *flags = (*flags & ~SCHEME_TYPE_MASK) + (schemeType << SCHEME_SHIFT);
 321  }
 322  
 323  static Boolean _pathHasFileIDPrefix(CFStringRef path);
 324  static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc);
 325  static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef baseURL, UInt32 *theFlags, CFRange *packedRanges, uint8_t *numberOfRanges);
 326  static CFRange _rangeForComponent(UInt32 flags, const CFRange *ranges, UInt32 compFlag);
 327  static CFRange _netLocationRange(UInt32 flags, const CFRange *ranges);
 328  static UInt32 _firstResourceSpecifierFlag(UInt32 flags);
 329  static void computeSanitizedString(CFURLRef url);
 330  static CFStringRef correctedComponent(CFStringRef component, UInt32 compFlag, CFStringEncoding enc);
 331  static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, const CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, const CFRange *baseRanges);
 332  static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc);
 333  
 334  
 335  enum {
 336  	VALID = 1,
 337  	ALPHA = 2,
 338  	PATHVALID = 4,
 339  	SCHEME = 8,
 340  	HEXDIGIT = 16
 341  };
 342  
 343  static const unsigned char sURLValidCharacters[128] = {
 344      /* nul   0 */   0,
 345      /* soh   1 */   0,
 346      /* stx   2 */   0,
 347      /* etx   3 */   0,
 348      /* eot   4 */   0,
 349      /* enq   5 */   0,
 350      /* ack   6 */   0,
 351      /* bel   7 */   0,
 352      /* bs    8 */   0,
 353      /* ht    9 */   0,
 354      /* nl   10 */   0,
 355      /* vt   11 */   0,
 356      /* np   12 */   0,
 357      /* cr   13 */   0,
 358      /* so   14 */   0,
 359      /* si   15 */   0,
 360      /* dle  16 */   0,
 361      /* dc1  17 */   0,
 362      /* dc2  18 */   0,
 363      /* dc3  19 */   0,
 364      /* dc4  20 */   0,
 365      /* nak  21 */   0,
 366      /* syn  22 */   0,
 367      /* etb  23 */   0,
 368      /* can  24 */   0,
 369      /* em   25 */   0,
 370      /* sub  26 */   0,
 371      /* esc  27 */   0,
 372      /* fs   28 */   0,
 373      /* gs   29 */   0,
 374      /* rs   30 */   0,
 375      /* us   31 */   0,
 376      /* sp   32 */   0,
 377      /* '!'  33 */   VALID | PATHVALID ,
 378      /* '"'  34 */   0,
 379      /* '#'  35 */   0,
 380      /* '$'  36 */   VALID | PATHVALID ,
 381      /* '%'  37 */   0,
 382      /* '&'  38 */   VALID | PATHVALID ,
 383      /* '''  39 */   VALID | PATHVALID ,
 384      /* '('  40 */   VALID | PATHVALID ,
 385      /* ')'  41 */   VALID | PATHVALID ,
 386      /* '*'  42 */   VALID | PATHVALID ,
 387      /* '+'  43 */   VALID | SCHEME | PATHVALID ,
 388      /* ','  44 */   VALID | PATHVALID ,
 389      /* '-'  45 */   VALID | SCHEME | PATHVALID ,
 390      /* '.'  46 */   VALID | SCHEME | PATHVALID ,
 391      /* '/'  47 */   VALID | PATHVALID ,
 392      /* '0'  48 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 393      /* '1'  49 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 394      /* '2'  50 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 395      /* '3'  51 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 396      /* '4'  52 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 397      /* '5'  53 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 398      /* '6'  54 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 399      /* '7'  55 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 400      /* '8'  56 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 401      /* '9'  57 */   VALID | SCHEME | PATHVALID | HEXDIGIT ,
 402      /* ':'  58 */   VALID ,
 403      /* ';'  59 */   VALID ,
 404      /* '<'  60 */   0,
 405      /* '='  61 */   VALID | PATHVALID ,
 406      /* '>'  62 */   0,
 407      /* '?'  63 */   VALID ,
 408      /* '@'  64 */   VALID ,
 409      /* 'A'  65 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 410      /* 'B'  66 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 411      /* 'C'  67 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 412      /* 'D'  68 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 413      /* 'E'  69 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 414      /* 'F'  70 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 415      /* 'G'  71 */   VALID | ALPHA | SCHEME | PATHVALID ,
 416      /* 'H'  72 */   VALID | ALPHA | SCHEME | PATHVALID ,
 417      /* 'I'  73 */   VALID | ALPHA | SCHEME | PATHVALID ,
 418      /* 'J'  74 */   VALID | ALPHA | SCHEME | PATHVALID ,
 419      /* 'K'  75 */   VALID | ALPHA | SCHEME | PATHVALID ,
 420      /* 'L'  76 */   VALID | ALPHA | SCHEME | PATHVALID ,
 421      /* 'M'  77 */   VALID | ALPHA | SCHEME | PATHVALID ,
 422      /* 'N'  78 */   VALID | ALPHA | SCHEME | PATHVALID ,
 423      /* 'O'  79 */   VALID | ALPHA | SCHEME | PATHVALID ,
 424      /* 'P'  80 */   VALID | ALPHA | SCHEME | PATHVALID ,
 425      /* 'Q'  81 */   VALID | ALPHA | SCHEME | PATHVALID ,
 426      /* 'R'  82 */   VALID | ALPHA | SCHEME | PATHVALID ,
 427      /* 'S'  83 */   VALID | ALPHA | SCHEME | PATHVALID ,
 428      /* 'T'  84 */   VALID | ALPHA | SCHEME | PATHVALID ,
 429      /* 'U'  85 */   VALID | ALPHA | SCHEME | PATHVALID ,
 430      /* 'V'  86 */   VALID | ALPHA | SCHEME | PATHVALID ,
 431      /* 'W'  87 */   VALID | ALPHA | SCHEME | PATHVALID ,
 432      /* 'X'  88 */   VALID | ALPHA | SCHEME | PATHVALID ,
 433      /* 'Y'  89 */   VALID | ALPHA | SCHEME | PATHVALID ,
 434      /* 'Z'  90 */   VALID | ALPHA | SCHEME | PATHVALID ,
 435      /* '['  91 */   0,
 436      /* '\'  92 */   0,
 437      /* ']'  93 */   0,
 438      /* '^'  94 */   0,
 439      /* '_'  95 */   VALID | PATHVALID ,
 440      /* '`'  96 */   0,
 441      /* 'a'  97 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 442      /* 'b'  98 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 443      /* 'c'  99 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 444      /* 'd' 100 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 445      /* 'e' 101 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 446      /* 'f' 102 */   VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT ,
 447      /* 'g' 103 */   VALID | ALPHA | SCHEME | PATHVALID ,
 448      /* 'h' 104 */   VALID | ALPHA | SCHEME | PATHVALID ,
 449      /* 'i' 105 */   VALID | ALPHA | SCHEME | PATHVALID ,
 450      /* 'j' 106 */   VALID | ALPHA | SCHEME | PATHVALID ,
 451      /* 'k' 107 */   VALID | ALPHA | SCHEME | PATHVALID ,
 452      /* 'l' 108 */   VALID | ALPHA | SCHEME | PATHVALID ,
 453      /* 'm' 109 */   VALID | ALPHA | SCHEME | PATHVALID ,
 454      /* 'n' 110 */   VALID | ALPHA | SCHEME | PATHVALID ,
 455      /* 'o' 111 */   VALID | ALPHA | SCHEME | PATHVALID ,
 456      /* 'p' 112 */   VALID | ALPHA | SCHEME | PATHVALID ,
 457      /* 'q' 113 */   VALID | ALPHA | SCHEME | PATHVALID ,
 458      /* 'r' 114 */   VALID | ALPHA | SCHEME | PATHVALID ,
 459      /* 's' 115 */   VALID | ALPHA | SCHEME | PATHVALID ,
 460      /* 't' 116 */   VALID | ALPHA | SCHEME | PATHVALID ,
 461      /* 'u' 117 */   VALID | ALPHA | SCHEME | PATHVALID ,
 462      /* 'v' 118 */   VALID | ALPHA | SCHEME | PATHVALID ,
 463      /* 'w' 119 */   VALID | ALPHA | SCHEME | PATHVALID ,
 464      /* 'x' 120 */   VALID | ALPHA | SCHEME | PATHVALID ,
 465      /* 'y' 121 */   VALID | ALPHA | SCHEME | PATHVALID ,
 466      /* 'z' 122 */   VALID | ALPHA | SCHEME | PATHVALID ,
 467      /* '{' 123 */   0,
 468      /* '|' 124 */   0,
 469      /* '}' 125 */   0,
 470      /* '~' 126 */   VALID | PATHVALID ,
 471      /* del 127 */   0,
 472  };
 473  
 474  CF_INLINE Boolean isURLLegalCharacter(UniChar ch) {
 475      return (ch <= 127) ? ((sURLValidCharacters[ch] & VALID) != 0) : false;
 476  }
 477  
 478  CF_INLINE Boolean scheme_valid(UniChar ch) {
 479      return (ch <= 127) ? ((sURLValidCharacters[ch] & SCHEME) != 0) : false;
 480  }
 481  
 482  CF_INLINE Boolean isALPHA(UniChar ch) {
 483      return (ch <= 127) ? ((sURLValidCharacters[ch] & ALPHA) != 0) : false;
 484  }
 485  /*
 486   Currently unused, but left in for symmetry/informative purposes
 487  CF_INLINE Boolean isPathLegalCharacter(UniChar ch) {
 488      return (ch <= 127) ? ((sURLValidCharacters[ch] & PATHVALID) != 0) : false;
 489  }
 490  */
 491  CF_INLINE Boolean isHexDigit(UniChar ch) {
 492      return (ch <= 127) ? ((sURLValidCharacters[ch] & HEXDIGIT) != 0) : false;
 493  }
 494  
 495  // Returns false if ch1 or ch2 isn't properly formatted
 496  CF_INLINE Boolean _translateBytes(UniChar ch1, UniChar ch2, uint8_t *result) {
 497      *result = 0;
 498      if (ch1 >= '0' && ch1 <= '9') *result += (ch1 - '0');
 499      else if (ch1 >= 'a' && ch1 <= 'f') *result += 10 + ch1 - 'a';
 500      else if (ch1 >= 'A' && ch1 <= 'F') *result += 10 + ch1 - 'A';
 501      else return false;
 502  
 503      *result  = (*result) << 4;
 504      if (ch2 >= '0' && ch2 <= '9') *result += (ch2 - '0');
 505      else if (ch2 >= 'a' && ch2 <= 'f') *result += 10 + ch2 - 'a';
 506      else if (ch2 >= 'A' && ch2 <= 'F') *result += 10 + ch2 - 'A';
 507      else return false;
 508  
 509      return true;
 510  }
 511  
 512  CF_INLINE Boolean _haveTestedOriginalString(CFURLRef url) {
 513      return ((url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) != 0) || (_getSanitizedString(url) != NULL);
 514  }
 515  
 516  enum {
 517      IS_PCHAR = 0x01,
 518  };
 519  
 520  static const unsigned char sURLValidBytes[256] = {
 521      /* nul   0 */   0,
 522      /* soh   1 */   0,
 523      /* stx   2 */   0,
 524      /* etx   3 */   0,
 525      /* eot   4 */   0,
 526      /* enq   5 */   0,
 527      /* ack   6 */   0,
 528      /* bel   7 */   0,
 529      /* bs    8 */   0,
 530      /* ht    9 */   0,
 531      /* nl   10 */   0,
 532      /* vt   11 */   0,
 533      /* np   12 */   0,
 534      /* cr   13 */   0,
 535      /* so   14 */   0,
 536      /* si   15 */   0,
 537      /* dle  16 */   0,
 538      /* dc1  17 */   0,
 539      /* dc2  18 */   0,
 540      /* dc3  19 */   0,
 541      /* dc4  20 */   0,
 542      /* nak  21 */   0,
 543      /* syn  22 */   0,
 544      /* etb  23 */   0,
 545      /* can  24 */   0,
 546      /* em   25 */   0,
 547      /* sub  26 */   0,
 548      /* esc  27 */   0,
 549      /* fs   28 */   0,
 550      /* gs   29 */   0,
 551      /* rs   30 */   0,
 552      /* us   31 */   0,
 553      /* sp   32 */   0,
 554      /* '!'  33 */   IS_PCHAR,
 555      /* '"'  34 */   0,
 556      /* '#'  35 */   0,
 557      /* '$'  36 */   IS_PCHAR,
 558      /* '%'  37 */   0,
 559      /* '&'  38 */   IS_PCHAR,
 560      /* '''  39 */   IS_PCHAR,
 561      /* '('  40 */   IS_PCHAR,
 562      /* ')'  41 */   IS_PCHAR,
 563      /* '*'  42 */   IS_PCHAR,
 564      /* '+'  43 */   IS_PCHAR,
 565      /* ','  44 */   IS_PCHAR,
 566      /* '-'  45 */   IS_PCHAR,
 567      /* '.'  46 */   IS_PCHAR,
 568      /* '/'  47 */   IS_PCHAR,   // not really a pchar -- it's the segment delimiter
 569      /* '0'  48 */   IS_PCHAR,
 570      /* '1'  49 */   IS_PCHAR,
 571      /* '2'  50 */   IS_PCHAR,
 572      /* '3'  51 */   IS_PCHAR,
 573      /* '4'  52 */   IS_PCHAR,
 574      /* '5'  53 */   IS_PCHAR,
 575      /* '6'  54 */   IS_PCHAR,
 576      /* '7'  55 */   IS_PCHAR,
 577      /* '8'  56 */   IS_PCHAR,
 578      /* '9'  57 */   IS_PCHAR,
 579      /* ':'  58 */   IS_PCHAR,
 580      /* ';'  59 */   0,          // we need to percent-escape ';' in file system paths so it won't be mistaken for the start of the obsolete param rule (rfc2396) that CFURL still supports
 581      /* '<'  60 */   0,
 582      /* '='  61 */   IS_PCHAR,
 583      /* '>'  62 */   0,
 584      /* '?'  63 */   0,
 585      /* '@'  64 */   IS_PCHAR,
 586      /* 'A'  65 */   IS_PCHAR,
 587      /* 'B'  66 */   IS_PCHAR,
 588      /* 'C'  67 */   IS_PCHAR,
 589      /* 'D'  68 */   IS_PCHAR,
 590      /* 'E'  69 */   IS_PCHAR,
 591      /* 'F'  70 */   IS_PCHAR,
 592      /* 'G'  71 */   IS_PCHAR,
 593      /* 'H'  72 */   IS_PCHAR,
 594      /* 'I'  73 */   IS_PCHAR,
 595      /* 'J'  74 */   IS_PCHAR,
 596      /* 'K'  75 */   IS_PCHAR,
 597      /* 'L'  76 */   IS_PCHAR,
 598      /* 'M'  77 */   IS_PCHAR,
 599      /* 'N'  78 */   IS_PCHAR,
 600      /* 'O'  79 */   IS_PCHAR,
 601      /* 'P'  80 */   IS_PCHAR,
 602      /* 'Q'  81 */   IS_PCHAR,
 603      /* 'R'  82 */   IS_PCHAR,
 604      /* 'S'  83 */   IS_PCHAR,
 605      /* 'T'  84 */   IS_PCHAR,
 606      /* 'U'  85 */   IS_PCHAR,
 607      /* 'V'  86 */   IS_PCHAR,
 608      /* 'W'  87 */   IS_PCHAR,
 609      /* 'X'  88 */   IS_PCHAR,
 610      /* 'Y'  89 */   IS_PCHAR,
 611      /* 'Z'  90 */   IS_PCHAR,
 612      /* '['  91 */   0,
 613      /* '\'  92 */   0,
 614      /* ']'  93 */   0,
 615      /* '^'  94 */   0,
 616      /* '_'  95 */   IS_PCHAR,
 617      /* '`'  96 */   0,
 618      /* 'a'  97 */   IS_PCHAR,
 619      /* 'b'  98 */   IS_PCHAR,
 620      /* 'c'  99 */   IS_PCHAR,
 621      /* 'd' 100 */   IS_PCHAR,
 622      /* 'e' 101 */   IS_PCHAR,
 623      /* 'f' 102 */   IS_PCHAR,
 624      /* 'g' 103 */   IS_PCHAR,
 625      /* 'h' 104 */   IS_PCHAR,
 626      /* 'i' 105 */   IS_PCHAR,
 627      /* 'j' 106 */   IS_PCHAR,
 628      /* 'k' 107 */   IS_PCHAR,
 629      /* 'l' 108 */   IS_PCHAR,
 630      /* 'm' 109 */   IS_PCHAR,
 631      /* 'n' 110 */   IS_PCHAR,
 632      /* 'o' 111 */   IS_PCHAR,
 633      /* 'p' 112 */   IS_PCHAR,
 634      /* 'q' 113 */   IS_PCHAR,
 635      /* 'r' 114 */   IS_PCHAR,
 636      /* 's' 115 */   IS_PCHAR,
 637      /* 't' 116 */   IS_PCHAR,
 638      /* 'u' 117 */   IS_PCHAR,
 639      /* 'v' 118 */   IS_PCHAR,
 640      /* 'w' 119 */   IS_PCHAR,
 641      /* 'x' 120 */   IS_PCHAR,
 642      /* 'y' 121 */   IS_PCHAR,
 643      /* 'z' 122 */   IS_PCHAR,
 644      /* '{' 123 */   0,
 645      /* '|' 124 */   0,
 646      /* '}' 125 */   0,
 647      /* '~' 126 */   IS_PCHAR,
 648      /* del 127 */   0,
 649                      0,
 650                      0,
 651                      0,
 652                      0,
 653                      0,
 654                      0,
 655                      0,
 656                      0,
 657                      0,
 658                      0,
 659                      0,
 660                      0,
 661                      0,
 662                      0,
 663                      0,
 664                      0,
 665                      0,
 666                      0,
 667                      0,
 668                      0,
 669                      0,
 670                      0,
 671                      0,
 672                      0,
 673                      0,
 674                      0,
 675                      0,
 676                      0,
 677                      0,
 678                      0,
 679                      0,
 680                      0,
 681                      0,
 682                      0,
 683                      0,
 684                      0,
 685                      0,
 686                      0,
 687                      0,
 688                      0,
 689                      0,
 690                      0,
 691                      0,
 692                      0,
 693                      0,
 694                      0,
 695                      0,
 696                      0,
 697                      0,
 698                      0,
 699                      0,
 700                      0,
 701                      0,
 702                      0,
 703                      0,
 704                      0,
 705                      0,
 706                      0,
 707                      0,
 708                      0,
 709                      0,
 710                      0,
 711                      0,
 712                      0,
 713                      0,
 714                      0,
 715                      0,
 716                      0,
 717                      0,
 718                      0,
 719                      0,
 720                      0,
 721                      0,
 722                      0,
 723                      0,
 724                      0,
 725                      0,
 726                      0,
 727                      0,
 728                      0,
 729                      0,
 730                      0,
 731                      0,
 732                      0,
 733                      0,
 734                      0,
 735                      0,
 736                      0,
 737                      0,
 738                      0,
 739                      0,
 740                      0,
 741                      0,
 742                      0,
 743                      0,
 744                      0,
 745                      0,
 746                      0,
 747                      0,
 748                      0,
 749                      0,
 750                      0,
 751                      0,
 752                      0,
 753                      0,
 754                      0,
 755                      0,
 756                      0,
 757                      0,
 758                      0,
 759                      0,
 760                      0,
 761                      0,
 762                      0,
 763                      0,
 764                      0,
 765                      0,
 766                      0,
 767                      0,
 768                      0,
 769                      0,
 770                      0,
 771                      0,
 772                      0,
 773                      0,
 774                      0,
 775                      0,
 776                      0,
 777  };
 778  
 779  CF_INLINE Boolean is_pchar(unsigned char ch) {
 780      return ( (sURLValidBytes[ch] & IS_PCHAR) != 0 );
 781  }
 782  
 783  
 784  /*
 785   CreateStringFromFileSystemRepresentationByAddingPercentEscapes creates a CFString
 786   for the path-absolute form of a URI path component from the native file system representation.
 787   Note: this code uses '/' path separators
 788   
 789   The rules for path-absolute from rfc3986 are:
 790   path-absolute = "/" [ segment-nz *( "/" segment ) ]
 791   segment       = *pchar
 792   segment-nz    = 1*pchar
 793   pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
 794   pct-encoded   = "%" HEXDIG HEXDIG
 795   unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
 796   sub-delims    = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
 797   */
 798  static CFStringRef CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, Boolean isDirectory, Boolean isAbsolute, Boolean windowsPath, Boolean *addedPercentEncoding)
 799  {
 800      static const UInt8 hexchars[] = "0123456789ABCDEF";
 801      const UInt8 *fileURLPrefixPtr;
 802      size_t fileURLPrefixLength;
 803      if ( AddAuthorityToFileURL() ) {
 804          fileURLPrefixPtr = fileURLPrefixWithAuthority;
 805          fileURLPrefixLength = sizeof(fileURLPrefixWithAuthority);
 806      }
 807      else {
 808          fileURLPrefixPtr = fileURLPrefix;
 809          fileURLPrefixLength = sizeof(fileURLPrefix);
 810      }
 811      STACK_BUFFER_DECL(UInt8, stackBuf, (PATH_MAX * 3) + (isAbsolute ? fileURLPrefixLength : 0) + (isDirectory ? 1 : 0)); // worst case is every byte needs to be percent-escaped
 812      UInt8 *bufStartPtr;
 813      UInt8 *bufBytePtr;
 814      const UInt8 *bytePtr = bytes;
 815      CFIndex idx;
 816      CFStringRef result;
 817      Boolean addedPercent = FALSE;
 818     
 819      // choose a buffer to percent-escape into.
 820      if ( numBytes <= PATH_MAX ) {
 821          bufStartPtr = &stackBuf[0];
 822      }
 823      else {
 824          // worst case is every byte needs to be percent-escaped
 825          bufStartPtr = (UInt8 *)malloc((numBytes * 3) + (isAbsolute ? fileURLPrefixLength : 0) + (isDirectory ? 1 : 0));
 826      }
 827      
 828      if ( bufStartPtr != NULL ) {
 829          if ( isAbsolute ) {
 830              // start with the fileURLPrefix
 831              strcpy((char *)bufStartPtr, (char *)fileURLPrefixPtr);
 832              bufBytePtr = bufStartPtr + fileURLPrefixLength - 1;
 833          }
 834          else {
 835              bufBytePtr = bufStartPtr;
 836          }
 837          
 838          if ( !windowsPath ) {
 839              for ( idx = 0; (idx < numBytes) && (*bytePtr != 0); ++idx ) {
 840                  if ( is_pchar(*bytePtr) ) {
 841                      *bufBytePtr++ = *bytePtr;
 842                  }
 843                  else {
 844                      *bufBytePtr++ = '%';
 845                      *bufBytePtr++ = hexchars[*bytePtr >> 4];
 846                      *bufBytePtr++ = hexchars[*bytePtr & 0x0f];
 847                      addedPercent = TRUE;
 848                  }
 849                  ++bytePtr;
 850              }
 851          }
 852          else {
 853              for ( idx = 0; (idx < numBytes) && (*bytePtr != 0); ++idx ) {
 854                  if ( is_pchar(*bytePtr) && (*bytePtr != '/') ) {    // percent-escape the forward slash if this is a windowsPath
 855                      *bufBytePtr++ = *bytePtr;
 856                  }
 857                  else {
 858                      *bufBytePtr++ = '%';
 859                      *bufBytePtr++ = hexchars[*bytePtr >> 4];
 860                      *bufBytePtr++ = hexchars[*bytePtr & 0x0f];
 861                      addedPercent = TRUE;
 862                  }
 863                  ++bytePtr;
 864              }
 865          }
 866          
 867          // did we convert numBytes?
 868          if ( idx != numBytes ) {
 869              // no, but it's OK if the remaining bytes are all nul (embedded nul bytes are not allowed)
 870              const UInt8 *nullBytePtr = bytePtr;
 871              for ( /* start where we left off */; (idx < numBytes) && (*nullBytePtr == '\0'); ++idx, ++nullBytePtr ) {
 872                  // do nothing
 873              }
 874          }
 875          
 876          if ( idx == numBytes ) {
 877              if ( isDirectory ) {
 878                  // if it is a directory and it doesn't end with PATH_SEP, append a PATH_SEP.
 879                  if ( bytes[numBytes-1] != '/' ) {
 880                      *bufBytePtr++ = '/';
 881                  }
 882              }
 883              else {
 884                  // it is not a directory: remove any pathDelim characters at end (leaving at least one character)
 885                  while ( (numBytes > 1) && (bytes[numBytes-1] == '/') ) {
 886                      --bufBytePtr;
 887                      --numBytes;
 888                  }
 889              }
 890              
 891              // create the result
 892              result = CFStringCreateWithBytes(alloc, bufStartPtr, (CFIndex)(bufBytePtr-bufStartPtr), kCFStringEncodingUTF8, FALSE);
 893          }
 894          else {
 895              // the remaining bytes were not all nul
 896              result = NULL;
 897          }
 898          
 899          // free the buffer if we malloc'd it
 900          if ( bufStartPtr != &stackBuf[0] ) {
 901              free(bufStartPtr);
 902          }
 903      }
 904      else {
 905          result = NULL;
 906      }
 907  
 908      if ( addedPercentEncoding ) {
 909          *addedPercentEncoding = addedPercent;
 910      }
 911      
 912      return ( result );
 913  }
 914  
 915  // Returns NULL if str cannot be converted for whatever reason, str if str contains no characters in need of escaping, or a newly-created string with the appropriate % escape codes in place.  Caller must always release the returned string.
 916  CF_INLINE CFStringRef _replacePathIllegalCharacters(CFStringRef str, CFAllocatorRef alloc, Boolean preserveSlashes) {
 917      CFStringRef result = NULL;
 918      STACK_BUFFER_DECL(char, buffer, PATH_MAX);
 919      if ( CFStringGetCString(str, buffer, PATH_MAX, kCFStringEncodingUTF8) ) {
 920          result = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault, (const UInt8 *)buffer, strlen(buffer), FALSE, FALSE, !preserveSlashes, NULL);
 921      }
 922      return result;
 923  }
 924  
 925  static Boolean _appendPercentEscapesForCharacter(UniChar ch, CFStringEncoding encoding, CFMutableStringRef str) {
 926      uint8_t bytes[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
 927      uint8_t *bytePtr = bytes, *currByte;
 928      CFIndex byteLength;
 929      CFAllocatorRef alloc = NULL;
 930      if (CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, 6, &byteLength) != kCFStringEncodingConversionSuccess) {
 931          byteLength = CFStringEncodingByteLengthForCharacters(encoding, 0, &ch, 1);
 932          if (byteLength <= 6) {
 933              // The encoding cannot accomodate the character
 934              return false;
 935          }
 936          alloc = CFGetAllocator(str);
 937          bytePtr = (uint8_t *)CFAllocatorAllocate(alloc, byteLength, 0);
 938          if (!bytePtr || CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, byteLength, &byteLength) != kCFStringEncodingConversionSuccess) {
 939              if (bytePtr) CFAllocatorDeallocate(alloc, bytePtr);
 940              return false;
 941          }
 942      }
 943      for (currByte = bytePtr; currByte < bytePtr + byteLength; currByte ++) {
 944          UniChar escapeSequence[3] = {'%', '\0', '\0'};
 945          unsigned char high, low;
 946          high = ((*currByte) & 0xf0) >> 4;
 947          low = (*currByte) & 0x0f;
 948          escapeSequence[1] = (high < 10) ? '0' + high : 'A' + high - 10;
 949          escapeSequence[2] = (low < 10) ? '0' + low : 'A' + low - 10;
 950          CFStringAppendCharacters(str, escapeSequence, 3);
 951      }
 952      if (bytePtr != bytes) {
 953          CFAllocatorDeallocate(alloc, bytePtr);
 954      }
 955      return true;
 956  }
 957  
 958  static CFStringRef UnescapeAllWithUTF8(CFAllocatorRef alloc, CFStringRef originalString)
 959  {
 960      CFStringRef result = NULL;
 961      CFIndex strLength = CFStringGetLength(originalString);
 962      CFIndex maxBufferSize = CFStringGetMaximumSizeForEncoding(strLength, kCFStringEncodingUTF8);
 963      CFIndex stackBufferSize = 2096;
 964      STACK_BUFFER_DECL(UInt8, escapedStackBuf, stackBufferSize *2);
 965      UInt8 *escapedBuf;
 966      UInt8 *unescapedBuf;
 967      // choose a buffer to percent-escape into.
 968      if ( maxBufferSize <= stackBufferSize ) {
 969          escapedBuf = &escapedStackBuf[0];
 970      }
 971      else {
 972          escapedBuf = (UInt8 *)malloc(maxBufferSize * 2);
 973      }
 974      if ( escapedBuf ) {
 975          CFIndex charsConverted;
 976          CFIndex usedBufLen;
 977          unescapedBuf = &escapedBuf[maxBufferSize];
 978          charsConverted = CFStringGetBytes(originalString, CFRangeMake(0, strLength), kCFStringEncodingUTF8, 0, false, escapedBuf, maxBufferSize, &usedBufLen);
 979          if ( charsConverted ) {
 980              // 0x80 marks invalid hex digits so this table can validate the digits while getting the values
 981              static const UInt8 hexvalues[] = {
 982  		/* 00 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 983  		/* 08 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 984  		/* 10 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 985  		/* 18 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 986  		/* 20 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 987  		/* 28 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 988  		/* 30 */  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
 989  		/* 38 */  0x08, 0x09, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 990  		/* 40 */  0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80,
 991  		/* 48 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 992  		/* 50 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 993  		/* 58 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 994  		/* 60 */  0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80,
 995  		/* 68 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 996  		/* 70 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 997  		/* 78 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
 998  		
 999  		/* 80 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1000  		/* 88 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1001  		/* 90 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1002  		/* 98 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1003  		/* A0 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1004  		/* A8 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1005  		/* B0 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1006  		/* B8 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1007  		/* C0 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1008  		/* C8 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1009  		/* D0 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1010  		/* D8 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1011  		/* E0 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1012  		/* E8 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1013  		/* F0 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1014  		/* F8 */  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1015              };
1016              UInt8 *bufStartPtr;
1017              UInt8 *bufPtr;
1018              const UInt8 *bytePtr = escapedBuf;
1019              CFIndex idx;
1020              
1021              bufPtr = bufStartPtr = unescapedBuf;
1022              Boolean conversionOK = TRUE;
1023              
1024              for ( idx = 0; (idx < usedBufLen) && conversionOK; ++idx ) {
1025                  switch ( *bytePtr ) {
1026                      case '%':
1027                          idx += 2;
1028                          if ( idx < usedBufLen ) {
1029                              UInt8 hex1, hex2;
1030                              // skip over %
1031                              bytePtr++;
1032                              // get the hex digits
1033                              hex1 = hexvalues[*bytePtr++];
1034                              hex2 = hexvalues[*bytePtr++];
1035                              // validate them
1036                              if ( ((hex1 | hex2) & 0x80) == 0 ) {
1037                                  // convert hex digits
1038                                  *bufPtr = (hex1 << 4) + hex2;
1039                              }
1040                              else {
1041                                  conversionOK = FALSE;
1042                              }
1043                          }
1044                          else {
1045                              conversionOK = FALSE;
1046                          }
1047                          break;
1048                      default:
1049                          // copy everything else
1050                          *bufPtr = *bytePtr++;
1051                          break;
1052                  }
1053                  ++bufPtr;
1054              }
1055              if ( conversionOK ) {
1056                  result = CFStringCreateWithBytes(alloc, unescapedBuf, bufPtr - bufStartPtr, kCFStringEncodingUTF8, false);
1057              }
1058          }
1059          
1060          // free the buffer if we malloc'd it
1061          if ( escapedBuf != &escapedStackBuf[0] ) {
1062              free(escapedBuf);
1063          }
1064      }
1065      return ( result );
1066  }
1067  
1068  // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure.  May return the original string.
1069  CFStringRef  CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc, CFStringRef  originalString, CFStringRef  charactersToLeaveEscaped) {
1070      CFMutableStringRef newStr = NULL;
1071      CFIndex length;
1072      CFIndex mark = 0;
1073      CFRange percentRange, searchRange;
1074      CFStringRef escapedStr = NULL;
1075      CFMutableStringRef strForEscapedChar = NULL;
1076      UniChar escapedChar;
1077      Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0);
1078      Boolean failed = false;
1079      
1080      if (!originalString) return NULL;
1081      
1082      length = CFStringGetLength(originalString);
1083      
1084      if ((length == 0) || (charactersToLeaveEscaped == NULL)) {
1085          return (CFStringRef)CFStringCreateCopy(alloc, originalString);
1086      }
1087      
1088      if ( escapeAll ) {
1089          return ( UnescapeAllWithUTF8(alloc, originalString) );
1090      }
1091  	
1092      searchRange = CFRangeMake(0, length);
1093  
1094      while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) {
1095          uint8_t bytes[4]; // Single UTF-8 character could require up to 4 bytes.
1096          uint8_t numBytesExpected;
1097          UniChar ch1, ch2;
1098  
1099          escapedStr = NULL;
1100          // Make sure we have at least 2 more characters
1101          if (length - percentRange.location < 3) { failed = true; break; }
1102  
1103          // if we don't have at least 2 more characters, we can't interpret the percent escape code,
1104          // so we assume the percent character is legit, and let it pass into the string
1105          ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location+1);
1106          ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location+2);
1107          if (!_translateBytes(ch1, ch2, bytes)) { failed = true;  break; }
1108          if (!(bytes[0] & 0x80)) {
1109              numBytesExpected = 1;
1110          } else if (!(bytes[0] & 0x20)) {
1111              numBytesExpected = 2;
1112          } else if (!(bytes[0] & 0x10)) {
1113              numBytesExpected = 3;
1114          } else {
1115              numBytesExpected = 4;
1116          }
1117          if (numBytesExpected == 1) {
1118              // one byte sequence (most common case); handle this specially
1119              escapedChar = bytes[0];
1120              if (!strForEscapedChar) {
1121                  strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull);
1122              }
1123              escapedStr = (CFStringRef)CFRetain(strForEscapedChar);
1124          } else {
1125              CFIndex j;
1126              // Make sure up front that we have enough characters
1127              if (length < percentRange.location + numBytesExpected * 3) { failed = true; break; }
1128              for (j = 1; j < numBytesExpected; j ++) {
1129                  if (CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j) != '%') { failed = true; break; }
1130                  ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 1);
1131                  ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 2);
1132                  if (!_translateBytes(ch1, ch2, bytes+j)) { failed = true; break; }
1133              }
1134  
1135              // FIXME: This uses about 1/3 of the time spent in CFURLCreateStringByReplacingPercentEscapes
1136              // !!! We should do the low-level bit-twiddling ourselves; this is expensive!  REW, 6/10/99
1137              escapedStr = CFStringCreateWithBytes(alloc, bytes, numBytesExpected, kCFStringEncodingUTF8, false);
1138              if (!escapedStr) {
1139                  failed = true;
1140              } else if (CFStringGetLength(escapedStr) == 0 && numBytesExpected == 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf) {
1141                  // Somehow, the UCS-2 BOM got translated in to a UTF8 string
1142                  escapedChar = 0xfeff;
1143                  if (!strForEscapedChar) {
1144                      strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull);
1145                  }
1146                  CFRelease(escapedStr);
1147                  escapedStr = (CFStringRef)CFRetain(strForEscapedChar);
1148              }
1149              if (failed) break;
1150          }
1151  
1152          // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
1153          searchRange.location = percentRange.location + 3 * numBytesExpected;
1154          searchRange.length = length - searchRange.location;
1155          
1156          if (!escapeAll) {
1157              if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location != kCFNotFound) {
1158                  if (escapedStr) {
1159                      CFRelease(escapedStr);
1160                      escapedStr = NULL;
1161                  }
1162                  continue;
1163              } 
1164          }
1165          
1166          if (!newStr) {
1167              newStr = CFStringCreateMutable(alloc, length);
1168          }
1169          if (percentRange.location - mark > 0) {
1170              // FIXME: The creation of this temporary string is unfortunate.
1171              CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark));
1172              CFStringAppend(newStr, substring);
1173              CFRelease(substring);
1174          }
1175          CFStringAppend(newStr, escapedStr);
1176          if (escapedStr) {
1177              CFRelease(escapedStr);
1178              escapedStr = NULL;
1179          }
1180          mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence
1181      }
1182  
1183      if (escapedStr) CFRelease(escapedStr);
1184      if (strForEscapedChar) CFRelease(strForEscapedChar);
1185      if (failed) {
1186          if (newStr) CFRelease(newStr);
1187          return NULL;
1188      } else if (newStr) {
1189          if (mark < length) {
1190              // Need to cat on the remainder of the string
1191              CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark));
1192              CFStringAppend(newStr, substring);
1193              CFRelease(substring);
1194          }
1195          return newStr;
1196      } else {
1197  	return (CFStringRef)CFStringCreateCopy(alloc, originalString);
1198      }
1199  }
1200  
1201  CF_EXPORT
1202  CFStringRef CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc, CFStringRef  originalString, CFStringRef  charactersToLeaveEscaped, CFStringEncoding enc) {
1203      if (enc == kCFStringEncodingUTF8) {
1204          return CFURLCreateStringByReplacingPercentEscapes(alloc, originalString, charactersToLeaveEscaped);
1205      } else {
1206          CFMutableStringRef newStr = NULL;
1207          CFMutableStringRef escapedStr = NULL;
1208          CFIndex length;
1209          CFIndex mark = 0;
1210          CFRange percentRange, searchRange;
1211          Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0);
1212          Boolean failed = false;
1213          uint8_t byteBuffer[8];
1214          uint8_t *bytes = byteBuffer;
1215          int capacityOfBytes = 8;
1216          
1217          if (!originalString) return NULL;
1218      
1219          if (charactersToLeaveEscaped == NULL) {
1220              return (CFStringRef)CFStringCreateCopy(alloc, originalString);
1221          }
1222      
1223          length = CFStringGetLength(originalString);
1224          searchRange = CFRangeMake(0, length);
1225      
1226          while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) {
1227              UniChar ch1, ch2;
1228              CFIndex percentLoc = percentRange.location;
1229              CFStringRef convertedString;
1230              int numBytesUsed = 0;
1231              do {
1232                  // Make sure we have at least 2 more characters
1233                  if (length - percentLoc < 3) { failed = true; break; }
1234      
1235                  if (numBytesUsed == capacityOfBytes) {
1236                      if (bytes == byteBuffer) {
1237                          bytes = (uint8_t *)CFAllocatorAllocate(alloc, 16 * sizeof(uint8_t), 0);
1238                          memmove(bytes, byteBuffer, capacityOfBytes);
1239                          capacityOfBytes = 16;
1240                      } else {
1241  			void *oldbytes = bytes;
1242  			int oldcap = capacityOfBytes;
1243                          capacityOfBytes = 2*capacityOfBytes;
1244                          bytes = (uint8_t *)CFAllocatorAllocate(alloc, capacityOfBytes * sizeof(uint8_t), 0);
1245  			memmove(bytes, oldbytes, oldcap);
1246                          CFAllocatorDeallocate(alloc, oldbytes);
1247                      }
1248                  }
1249                  percentLoc ++;
1250                  ch1 = CFStringGetCharacterAtIndex(originalString, percentLoc);
1251                  percentLoc ++;
1252                  ch2 = CFStringGetCharacterAtIndex(originalString, percentLoc);
1253                  percentLoc ++;
1254                  if (!_translateBytes(ch1, ch2, bytes + numBytesUsed)) { failed = true;  break; }
1255                  numBytesUsed ++;
1256              } while (CFStringGetCharacterAtIndex(originalString, percentLoc) == '%');
1257              searchRange.location = percentLoc;
1258              searchRange.length = length - searchRange.location;
1259  
1260              if (failed) break;
1261              convertedString = CFStringCreateWithBytes(alloc, bytes, numBytesUsed, enc, false);
1262              if (!convertedString) {
1263                  failed = true;
1264                  break;
1265              }
1266      
1267              if (!newStr) {
1268                  newStr = CFStringCreateMutable(alloc, length);
1269              }
1270              if (percentRange.location - mark > 0) {
1271                  // FIXME: The creation of this temporary string is unfortunate.
1272                  CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark));
1273                  CFStringAppend(newStr, substring);
1274                  CFRelease(substring);
1275              }
1276  
1277              if (escapeAll) {
1278                  CFStringAppend(newStr, convertedString);
1279              } else {
1280                  CFIndex i, c = CFStringGetLength(convertedString);
1281                  if (!escapedStr) {
1282                      escapedStr = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &ch1, 1, 1, kCFAllocatorNull);
1283                  }
1284                  for (i = 0; i < c; i ++) {
1285                      ch1 = CFStringGetCharacterAtIndex(convertedString, i);
1286                      if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location == kCFNotFound) {
1287                          CFStringAppendCharacters(newStr, &ch1, 1);
1288                      } else {
1289                          // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
1290                          _appendPercentEscapesForCharacter(ch1, enc, newStr);
1291                      }
1292                  }
1293              }
1294  	    CFRelease(convertedString);
1295              mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence
1296          }
1297      
1298          if (escapedStr) CFRelease(escapedStr);
1299          if (bytes != byteBuffer) CFAllocatorDeallocate(alloc, bytes);
1300          if (failed) {
1301              if (newStr) CFRelease(newStr);
1302              return NULL;
1303          } else if (newStr) {
1304              if (mark < length) {
1305                  // Need to cat on the remainder of the string
1306                  CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark));
1307                  CFStringAppend(newStr, substring);
1308                  CFRelease(substring);
1309              }
1310              return newStr;
1311          } else {
1312              return (CFStringRef)CFStringCreateCopy(alloc, originalString);
1313          }
1314      }
1315  }
1316      
1317  static Boolean _stringContainsCharacter(CFStringRef string, UniChar ch) {
1318      CFIndex i, c = CFStringGetLength(string);
1319      CFStringInlineBuffer buf;
1320      CFStringInitInlineBuffer(string, &buf, CFRangeMake(0, c));
1321      for (i = 0; i < c; i ++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf, i) == ch) return true;
1322      return false;
1323  }
1324  
1325  // Note: charactersToLeaveUnescaped and legalURLCharactersToBeEscaped only work for characters which can be represented as a single UTF16 codepoint.
1326  CF_EXPORT CFStringRef CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator, CFStringRef originalString, CFStringRef charactersToLeaveUnescaped, CFStringRef legalURLCharactersToBeEscaped, CFStringEncoding encoding) {
1327      CFMutableStringRef newString = NULL;
1328      CFIndex idx, length;
1329      CFStringInlineBuffer buf;
1330      enum {
1331          kCharBufferMax = 4096,
1332      };
1333      STACK_BUFFER_DECL(UniChar, charBuffer, kCharBufferMax);
1334      CFIndex charBufferIndex = 0;
1335  
1336      if (!originalString) return NULL;
1337      length = CFStringGetLength(originalString);
1338      if (length == 0) return (CFStringRef)CFStringCreateCopy(allocator, originalString);
1339      CFStringInitInlineBuffer(originalString, &buf, CFRangeMake(0, length));
1340  
1341      for (idx = 0; idx < length; idx ++) {
1342          UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buf, idx);
1343          Boolean shouldReplace = (isURLLegalCharacter(ch) == false);
1344          if (shouldReplace) {
1345              if (charactersToLeaveUnescaped && _stringContainsCharacter(charactersToLeaveUnescaped, ch)) {
1346                  shouldReplace = false;
1347              }
1348          } else if (legalURLCharactersToBeEscaped && _stringContainsCharacter(legalURLCharactersToBeEscaped, ch)) {
1349              shouldReplace = true;
1350          }
1351          
1352          if (shouldReplace) {
1353              enum {
1354                  kMaxBytesPerUniChar = 8,    // 8 bytes is the maximum a single UniChar can require in any current encodings; future encodings could require more
1355                  kMaxPercentEncodedUniChars = kMaxBytesPerUniChar * 3
1356              };
1357              
1358              static const UInt8 hexchars[] = "0123456789ABCDEF";
1359              uint8_t bytes[kMaxBytesPerUniChar];
1360              uint8_t *bytePtr = bytes;
1361              uint8_t *currByte;
1362              uint8_t *endPtr;
1363              CFIndex byteLength;
1364  
1365              // Perform the replacement
1366              if ( !newString ) {
1367                  newString = CFStringCreateMutableCopy(CFGetAllocator(originalString), 0, originalString);
1368                  CFStringDelete(newString, CFRangeMake(idx, length-idx));
1369              }
1370              // make sure charBuffer has enough room
1371              if ( charBufferIndex >= (kCharBufferMax - kMaxPercentEncodedUniChars) ) {
1372                  // make room
1373                  CFStringAppendCharacters(newString, charBuffer, charBufferIndex);
1374                  charBufferIndex = 0;
1375              }
1376              
1377              // convert the UniChar to bytes
1378              if ( CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, 8, &byteLength) == kCFStringEncodingConversionSuccess ) {
1379                  // percent-encode the bytes
1380                  endPtr = bytePtr + byteLength;
1381                  for ( currByte = bytePtr; currByte < endPtr; currByte++ ) {
1382                      charBuffer[charBufferIndex++] = '%';
1383                      charBuffer[charBufferIndex++] = hexchars[*currByte >> 4];
1384                      charBuffer[charBufferIndex++] = hexchars[*currByte & 0x0f];
1385                  }
1386              }
1387              else {
1388                  // FIXME: once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
1389                  if ( encoding == kCFStringEncodingUTF8 && CFCharacterSetIsSurrogateHighCharacter(ch) && idx + 1 < length && CFCharacterSetIsSurrogateLowCharacter(__CFStringGetCharacterFromInlineBufferQuick(&buf, idx+1)) ) {
1390                      UniChar surrogate[2];
1391                      uint8_t *currByte;
1392                      
1393                      surrogate[0] = ch;
1394                      surrogate[1] = __CFStringGetCharacterFromInlineBufferQuick(&buf, idx+1);
1395                      // Aki sez it should never take more than 6 bytes
1396                      if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8, 0, surrogate, 2, NULL, bytes, 6, &byteLength) == kCFStringEncodingConversionSuccess) {
1397                          endPtr = bytePtr + byteLength;
1398                          for ( currByte = bytes; currByte < endPtr; currByte++ ) {
1399                              charBuffer[charBufferIndex++] = '%';
1400                              charBuffer[charBufferIndex++] = hexchars[*currByte >> 4];
1401                              charBuffer[charBufferIndex++] = hexchars[*currByte & 0x0f];
1402                          }
1403                          idx++; // We consumed 2 characters, not 1
1404                      }
1405                      else {
1406                          // surrogate pair conversion failed
1407                          break;
1408                      }
1409                  } else {
1410                      // not a surrogate pair
1411                      break;
1412                  }
1413              }
1414          } else if (newString) {
1415              charBuffer[charBufferIndex++] = ch;
1416              if ( charBufferIndex == kCharBufferMax ) {
1417                  CFStringAppendCharacters(newString, charBuffer, charBufferIndex);
1418                  charBufferIndex = 0;
1419              }
1420          }
1421      }
1422      if (idx < length) {
1423          // Ran into an encoding failure
1424          if (newString) CFRelease(newString);
1425          return NULL;
1426      } else if (newString) {
1427          if ( charBufferIndex != 0 ) {
1428              CFStringAppendCharacters(newString, charBuffer, charBufferIndex);
1429          }
1430          return newString;
1431      } else {
1432          return (CFStringRef)CFStringCreateCopy(CFGetAllocator(originalString), originalString);
1433      }
1434  }
1435  
1436  static Boolean __CFURLEqual(CFTypeRef  cf1, CFTypeRef  cf2) {
1437      Boolean result;
1438      CFURLRef  url1 = (CFURLRef)cf1;
1439      CFURLRef  url2 = (CFURLRef)cf2;
1440      
1441      __CFGenericValidateType(cf1, CFURLGetTypeID());
1442      __CFGenericValidateType(cf2, CFURLGetTypeID());
1443  
1444      if ( url1 == url2 ) {
1445          result = true;
1446      }
1447      else {
1448          if ( (url1->_flags & EQUAL_FLAGS_MASK) != (url2->_flags & EQUAL_FLAGS_MASK) ) {
1449              result = false;
1450          }
1451          else {
1452              if ( (url1->_base && !url2->_base) ||
1453                  (!url1->_base && url2->_base) ||
1454                  (url1->_base && url2->_base && !CFEqual(url1->_base, url2->_base)) ) {
1455                  result = false;
1456              }
1457              else {
1458                  // no base urls, so compare the URL strings
1459                  // Do not compare the original strings; compare the sanatized strings.
1460                  result = CFEqual(CFURLGetString(url1), CFURLGetString(url2));
1461              }
1462          }
1463      }
1464      return ( result ) ;
1465  }
1466  
1467  static CFHashCode __CFURLHash(CFTypeRef cf)
1468  {
1469      CFHashCode result;
1470      
1471      if ( cf ) {
1472          // use the CFHashCode of the URL
1473          result = CFHash(CFURLGetString((CFURLRef)cf));
1474      }
1475      else {
1476          // no object, no hashcode
1477          result = 0;
1478      }
1479      
1480      return ( result );
1481  }
1482  
1483  static CFStringRef CreateTruncatedURLString(CFAllocatorRef alloc, CFStringRef urlString, CFIndex maxLength, CFIndex suffixLength)
1484  {
1485      CFStringRef result;
1486      
1487      CFIndex len = CFStringGetLength(urlString);
1488      if ( len <= maxLength ) {
1489          // return the retained urlString
1490          result = CFStringCreateCopy(alloc, urlString);
1491      }
1492      else {
1493          CFStringRef start = CFStringCreateWithSubstring(alloc, urlString, CFRangeMake(0, maxLength - suffixLength));
1494          CFStringRef end = CFStringCreateWithSubstring(alloc, urlString, CFRangeMake(len - suffixLength, suffixLength));
1495          result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@ ... %@"), start, end);
1496          if ( start ) {
1497              CFRelease(start);
1498          }
1499          if ( (end) ) {
1500              CFRelease(end);
1501          }
1502      }
1503      return ( result );
1504  }
1505  
1506  static CFStringRef  __CFURLCopyFormattingDescription(CFTypeRef  cf, CFDictionaryRef formatOptions) {
1507      CFStringRef result;
1508      CFURLRef  url = (CFURLRef)cf;
1509      __CFGenericValidateType(cf, CFURLGetTypeID());
1510      CFAllocatorRef alloc = CFGetAllocator(url);
1511  
1512      Boolean isDataURL = false;
1513      CFStringRef scheme = CFURLCopyScheme(url);
1514      if ( scheme ) {
1515          isDataURL = CFStringCompare(scheme, kCFURLDataScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo;
1516          CFRelease(scheme);
1517      }
1518      
1519      if ( !isDataURL ) {
1520          if (!url->_base) {
1521              {
1522                  result = CFStringCreateCopy(alloc, url->_string);
1523              }
1524          } else {
1525              // Do not dereference url->_base; it may be an ObjC object
1526              result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@ -- %@"), url->_string, url->_base);
1527          }
1528      }
1529      else {
1530          if ( !url->_base ) {
1531              result = CreateTruncatedURLString(alloc, url->_string, 128, 8);
1532          }
1533          else {
1534              CFStringRef urlString = CreateTruncatedURLString(alloc, url->_string, 128, 8);
1535              CFStringRef baseString = CreateTruncatedURLString(alloc, CFURLGetString(url->_base), 128, 8);
1536              // Do not dereference url->_base; it may be an ObjC object
1537              result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@ -- %@"), urlString, baseString);
1538              if ( urlString ) {
1539                  CFRelease(urlString);
1540              }
1541              if ( baseString ) {
1542                  CFRelease(baseString);
1543              }
1544          }
1545      }
1546      return ( result );
1547  }
1548  
1549  
1550  static CFStringRef __CFURLCopyDescription(CFTypeRef cf) {
1551      CFURLRef url = (CFURLRef)cf;
1552      CFStringRef result;
1553      CFAllocatorRef alloc = CFGetAllocator(url);
1554      Boolean isDataURL = false;
1555      CFStringRef scheme = CFURLCopyScheme(url);
1556      
1557      if ( scheme ) {
1558          isDataURL = CFStringCompare(scheme, kCFURLDataScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo;
1559          CFRelease(scheme);
1560      }
1561      
1562      if ( !isDataURL ) {
1563          if ( url->_base) {
1564              CFStringRef baseString = CFCopyDescription(url->_base);
1565              result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u\n\tbase = %@}"), cf, alloc, url->_string, (unsigned int)(url->_encoding), baseString);
1566              CFRelease(baseString);
1567          } else {
1568              result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf, alloc, url->_string, (unsigned int)(url->_encoding));
1569          }
1570      }
1571      else {
1572          CFStringRef urlString = CreateTruncatedURLString(alloc, url->_string, 128, 8);
1573          if ( url->_base) {
1574              CFStringRef baseString = CFCopyDescription(url->_base);
1575              result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u\n\tbase = %@}"), cf, alloc, urlString, (unsigned int)(url->_encoding), baseString);
1576              CFRelease(baseString);
1577          } else {
1578              result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf, alloc, urlString, (unsigned int)(url->_encoding));
1579          }
1580          CFRelease(urlString);
1581      }
1582      return result;
1583  }
1584  
1585  #if DEBUG_URL_MEMORY_USAGE
1586  extern __attribute((used)) void __CFURLDumpMemRecord(void) {
1587      syslog(LOG_ERR, "%d URLs created; %d destroyed; %d URLs parsed; %d urls had 'extra' data allocated; %d had base urls; %d were not UTF8 encoded", numURLs, numDealloced, numURLsParsed, numExtraDataAllocated, numURLsWithBaseURL, numNonUTF8EncodedURLs);
1588  }
1589  #endif
1590  
1591  static void __CFURLDeallocate(CFTypeRef  cf) {
1592      CFURLRef  url = (CFURLRef)cf;
1593      CFAllocatorRef alloc;
1594      __CFGenericValidateType(cf, CFURLGetTypeID());
1595      alloc = CFGetAllocator(url);
1596  #if DEBUG_URL_MEMORY_USAGE
1597      numDealloced ++;
1598  #endif
1599      if (url->_string) CFRelease(url->_string); // GC: 3879914
1600      if (url->_base) CFRelease(url->_base);
1601      CFStringRef sanitizedString = _getSanitizedString(url);
1602      if (sanitizedString) CFRelease(sanitizedString);
1603      if ( url->_extra != NULL ) CFAllocatorDeallocate( alloc, url->_extra );
1604      if (_getResourceInfo(url)) CFRelease(_getResourceInfo(url));
1605  }
1606  
1607  static CFTypeID __kCFURLTypeID = _kCFRuntimeNotATypeID;
1608  
1609  static const CFRuntimeClass __CFURLClass = {
1610      0,                                  // version
1611      "CFURL",                            // className
1612      NULL,                               // init
1613      NULL,                               // copy
1614      __CFURLDeallocate,                  // finalize
1615      __CFURLEqual,                       // equal
1616      __CFURLHash,                        // hash
1617      __CFURLCopyFormattingDescription,   // copyFormattingDesc
1618      __CFURLCopyDescription,             // copyDebugDesc
1619      NULL,                               // reclaim
1620      NULL,                               // refcount
1621  };
1622  
1623  /* Toll-free bridging support; get the true CFURL from an NSURL */
1624  CF_INLINE CFURLRef _CFURLFromNSURL(CFURLRef url) {
1625      CF_OBJC_FUNCDISPATCHV(CFURLGetTypeID(), CFURLRef, (NSURL *)url, _cfurl);
1626      return url;
1627  }
1628  
1629  CFTypeID CFURLGetTypeID(void) {
1630      static dispatch_once_t initOnce;
1631      dispatch_once(&initOnce, ^{ __kCFURLTypeID = _CFRuntimeRegisterClass(&__CFURLClass); });
1632      return __kCFURLTypeID;
1633  }
1634  
1635  CF_PRIVATE void CFShowURL(CFURLRef url) {
1636      if (!url) {
1637          fprintf(stdout, "(null)\n");
1638          return;
1639      }
1640      fprintf(stdout, "<CFURL %p>{", (const void*)url);
1641      if (CF_IS_OBJC(CFURLGetTypeID(), url)) {
1642          fprintf(stdout, "ObjC bridged object}\n");
1643          return;
1644      }
1645      fprintf(stdout, "\n\tRelative string: ");
1646      CFShow(url->_string);
1647      fprintf(stdout, "\tBase URL: ");
1648      if (url->_base) {
1649          fprintf(stdout, "<%p> ", (const void*)url->_base);
1650          CFShow(url->_base);
1651      } else {
1652          fprintf(stdout, "(null)\n");
1653      }
1654      fprintf(stdout, "\tFlags: 0x%x\n}\n", (unsigned int)url->_flags);
1655  }
1656  
1657  
1658  /***************************************************/
1659  /* URL creation and String/Data creation from URLS */
1660  /***************************************************/
1661  static void constructBuffers(CFAllocatorRef alloc, CFStringRef string, UInt8 *inBuffer, CFIndex inBufferSize, const char **cstring, const UniChar **ustring, Boolean *useCString, Boolean *freeCharacters) {
1662      CFIndex neededLength;
1663      CFIndex length;
1664      CFRange rg;
1665  
1666      *cstring = CFStringGetCStringPtr(string, kCFStringEncodingISOLatin1);
1667      if (*cstring) {
1668          *ustring = NULL;
1669          *useCString = true;
1670          *freeCharacters = false;
1671          return;
1672      } 
1673  
1674      *ustring = CFStringGetCharactersPtr(string);
1675      if (*ustring) {
1676          *useCString = false;
1677          *freeCharacters = false;
1678          return;
1679      } 
1680  
1681      length = CFStringGetLength(string);
1682      rg = CFRangeMake(0, length);
1683      CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, NULL, INT_MAX, &neededLength);
1684      if (neededLength == length) {
1685          char *buf;
1686          if ( (inBuffer != NULL) && (length <= inBufferSize) ) {
1687              buf = (char *)inBuffer;
1688              *freeCharacters = false;
1689          }
1690          else {
1691              buf = (char *)CFAllocatorAllocate(alloc, length, 0);
1692              *freeCharacters = true;
1693          }
1694          CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, (uint8_t *)buf, length, NULL);
1695          *cstring = buf;
1696          *useCString = true;
1697      } else {
1698          UniChar *buf;
1699          if ( (inBuffer != NULL) && ((length * sizeof(UniChar)) <= inBufferSize) ) {
1700              buf = (UniChar *)inBuffer;
1701              *freeCharacters = false;
1702          }
1703          else {
1704              buf = (UniChar *)CFAllocatorAllocate(alloc, length * sizeof(UniChar), 0);
1705              *freeCharacters = true;
1706          }
1707          CFStringGetCharacters(string, rg, buf);
1708          *ustring = buf;
1709          *useCString = false;
1710      }
1711  }
1712  
1713  static void _parseComponentsCString(CFAllocatorRef alloc, CFURLRef baseURL, CFIndex cfStringLength, const char *characterArray, UInt32 *theFlags, CFRange *packedRanges, uint8_t *numberOfRanges)
1714  #define CFURL_INCLUDE_PARSE_COMPONENTS
1715  #include "CFURL.inc.h"
1716  #undef CFURL_INCLUDE_PARSE_COMPONENTS
1717  
1718  static void _parseComponentsUString(CFAllocatorRef alloc, CFURLRef baseURL, CFIndex cfStringLength, const UniChar *characterArray, UInt32 *theFlags, CFRange *packedRanges, uint8_t *numberOfRanges)
1719  #define CFURL_INCLUDE_PARSE_COMPONENTS
1720  #include "CFURL.inc.h"
1721  #undef CFURL_INCLUDE_PARSE_COMPONENTS
1722  
1723  static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef baseURL, UInt32 *theFlags, CFRange *packedRanges, uint8_t *numberOfRanges)
1724  {
1725      CFIndex cfStringLength = CFStringGetLength(string);
1726      Boolean useCString, freeCharacters;
1727      const char *cstring = NULL;
1728      const UniChar *ustring = NULL;
1729      CFIndex stackBufferSize = 4096;
1730      STACK_BUFFER_DECL(UInt8, stackBuffer, stackBufferSize);
1731      
1732      constructBuffers(alloc, string, stackBuffer, stackBufferSize, &cstring, &ustring, &useCString, &freeCharacters);
1733      
1734      if (useCString) {
1735          _parseComponentsCString(alloc, baseURL, cfStringLength, cstring, theFlags, packedRanges, numberOfRanges);
1736      }
1737      else {
1738          _parseComponentsUString(alloc, baseURL, cfStringLength, ustring, theFlags, packedRanges, numberOfRanges);
1739      }
1740      
1741      if (freeCharacters) {
1742          CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring);
1743      }
1744  }
1745  
1746  static Boolean scanCharactersCString(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const char *characterArray, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding)
1747  #define CFURL_INCLUDE_SCAN_CHARACTERS
1748  #include "CFURL.inc.h"
1749  #undef CFURL_INCLUDE_SCAN_CHARACTERS
1750  
1751  static Boolean scanCharactersUString(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const UniChar *characterArray, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding)
1752  #define CFURL_INCLUDE_SCAN_CHARACTERS
1753  #include "CFURL.inc.h"
1754  #undef CFURL_INCLUDE_SCAN_CHARACTERS
1755  
1756  static Boolean scanCharacters(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const char *cstring, const UniChar *ustring, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding) {
1757      if ( useCString ) {
1758          return ( scanCharactersCString(alloc, escapedString, flags, cstring, useCString, base, end, mark, componentFlag, encoding));
1759      }
1760      else {
1761          return ( scanCharactersUString(alloc, escapedString, flags, ustring, useCString, base, end, mark, componentFlag, encoding));
1762      }
1763  }
1764  
1765  static void computeSanitizedString(CFURLRef url) {
1766      CFAllocatorRef alloc = CFGetAllocator(url);
1767      CFIndex string_length = CFStringGetLength(url->_string);
1768      Boolean useCString, freeCharacters;
1769      const char *cstring = NULL;
1770      const UniChar *ustring = NULL;
1771      CFIndex base; // where to scan from
1772      CFIndex mark; // first character not-yet copied to sanitized string
1773      CFIndex stackBufferSize = 4096;
1774      STACK_BUFFER_DECL(UInt8, stackBuffer, stackBufferSize);
1775      CFMutableStringRef sanitizedString = NULL;
1776      UInt32 additionalDataFlags = 0;
1777  
1778      constructBuffers(alloc, url->_string, stackBuffer, stackBufferSize, &cstring, &ustring, &useCString, &freeCharacters);
1779      if (!(url->_flags & IS_DECOMPOSABLE)) {
1780          // Impossible to have a problem character in the scheme
1781          base = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME).length + 1;
1782          mark = 0;
1783          if (!scanCharacters(alloc, &sanitizedString, &additionalDataFlags, cstring, ustring, useCString, base, string_length, &mark, 0, url->_encoding)) {
1784              ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH;
1785          }
1786          if ( sanitizedString ) {
1787              _setAdditionalDataFlags((struct __CFURL*)url, additionalDataFlags);
1788          }
1789      } else {
1790          // Go component by component
1791          CFIndex currentComponent = HAS_USER;
1792          mark = 0;
1793          while (currentComponent < (HAS_FRAGMENT << 1)) {
1794              CFRange componentRange = _rangeForComponent(url->_flags, url->_ranges, currentComponent);
1795              if (componentRange.location != kCFNotFound) {
1796                  scanCharacters(alloc, &sanitizedString, &additionalDataFlags, cstring, ustring, useCString, componentRange.location, componentRange.location + componentRange.length, &mark, currentComponent, url->_encoding);
1797              }
1798              currentComponent = currentComponent << 1;
1799          }
1800          if (sanitizedString) {
1801              _setAdditionalDataFlags((struct __CFURL*)url, additionalDataFlags);
1802          } else {
1803              ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH;
1804          }
1805      }
1806      if (sanitizedString && mark != string_length) {
1807          if (useCString) {
1808              __CFStringAppendBytes(sanitizedString, (char *)&(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1);
1809          } else {
1810              CFStringAppendCharacters(sanitizedString, &(ustring[mark]), string_length - mark);
1811          }
1812      }
1813      if ( sanitizedString ) {
1814          _setSanitizedString((struct __CFURL*) url, sanitizedString);
1815          CFRelease(sanitizedString);
1816      }
1817      if (freeCharacters) {
1818          CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring);
1819      }
1820  }
1821  
1822  
1823  static CFStringRef correctedComponent(CFStringRef comp, UInt32 compFlag, CFStringEncoding enc) {
1824      CFAllocatorRef alloc = CFGetAllocator(comp);
1825      CFIndex string_length = CFStringGetLength(comp);
1826      Boolean useCString, freeCharacters;
1827      const char *cstring = NULL;
1828      const UniChar *ustring = NULL;
1829      CFIndex mark = 0; // first character not-yet copied to sanitized string
1830      CFMutableStringRef result = NULL;
1831      CFIndex stackBufferSize = 1024;
1832      STACK_BUFFER_DECL(UInt8, stackBuffer, stackBufferSize);
1833  
1834      constructBuffers(alloc, comp, stackBuffer, stackBufferSize, &cstring, &ustring, &useCString, &freeCharacters);
1835      scanCharacters(alloc, &result, NULL, cstring, ustring, useCString, 0, string_length, &mark, compFlag, enc);
1836      if (result) {
1837          if (mark < string_length) {
1838              if (useCString) {
1839                  __CFStringAppendBytes(result, (char *)&(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1);
1840              } else {
1841                  CFStringAppendCharacters(result, &(ustring[mark]), string_length - mark);
1842              }
1843          }
1844      } else {
1845          // This should nevr happen
1846          CFRetain(comp);
1847          result = (CFMutableStringRef)comp;
1848      }
1849      if (freeCharacters) {
1850          CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring);
1851      }
1852      return result;
1853  }
1854  
1855  
1856  static struct __CFURL * _CFURLAlloc(CFAllocatorRef allocator, uint8_t numberOfRanges);
1857  static CFURLRef _CFURLCreateWithURLString(CFAllocatorRef allocator, CFStringRef string, Boolean checkForLegalCharacters, CFURLRef baseURL);
1858  static CFURLRef _CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef fileSystemPath, CFURLPathStyle pathStyle, Boolean isDirectory, CFURLRef baseURL);
1859  static CFURLRef _CFURLCreateWithFileSystemRepresentation(CFAllocatorRef allocator, const UInt8 *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL);
1860  
1861  static struct __CFURL * _CFURLAlloc(CFAllocatorRef allocator, uint8_t numberOfRanges) {
1862      struct __CFURL *url;
1863  #if DEBUG_URL_MEMORY_USAGE
1864      numURLs ++;
1865  //    if ( numURLs % 1000 == 0 ) {
1866  //        __CFURLDumpMemRecord();
1867  //    }
1868  #endif
1869      CFIndex extraBytes = offsetof(struct __CFURL, _ranges[numberOfRanges]) - sizeof(CFRuntimeBase);
1870      url = (struct __CFURL *)_CFRuntimeCreateInstance(allocator, CFURLGetTypeID(), extraBytes, NULL);
1871      if (url) {
1872  	url->_flags = 0;
1873  	url->_encoding = kCFStringEncodingUTF8;
1874  	url->_string = NULL;
1875  	url->_base = NULL;
1876  	url->_extra = NULL;
1877  	url->_resourceInfo = NULL;
1878      }
1879      return url;
1880  }
1881  
1882  static Boolean _CFStringIsLegalURLString(CFStringRef string) {
1883      Boolean result = true;
1884      if ( string ) {
1885          CFStringInlineBuffer stringBuffer;
1886          Boolean sawHash = false;
1887          CFIndex idx = 0;
1888          CFIndex checkHexDigit = 0;
1889          CFIndex length;
1890          
1891          length = CFStringGetLength(string);
1892          {
1893              CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length));
1894              
1895              while ( idx < length ) {
1896                  CFIndex rangeLength;
1897                  const UniChar *chPtr;
1898                  
1899                  rangeLength = (idx + __kCFStringInlineBufferLength <= length) ? __kCFStringInlineBufferLength : length - idx;
1900                  chPtr = CFStringGetCharactersPtrFromInlineBuffer(&stringBuffer, CFRangeMake(idx, rangeLength));
1901                  for ( CFIndex rangeIdx = 0; rangeIdx < rangeLength; ++rangeIdx, ++chPtr ) {
1902                      if ( !checkHexDigit ) {
1903                          if ( *chPtr == '%' ) {
1904                              // percent encoded? make sure there at least 2 characters left to check
1905                              if ( (idx + rangeIdx + 2) < length ) {
1906                                  // the next 2 characters must be HEXDIG
1907                                  checkHexDigit = 2;
1908                                  continue;
1909                              }
1910                              else {
1911                                  result = false;
1912                                  break;
1913                              }
1914                          }
1915                          if ( *chPtr == '[' || *chPtr == ']' ) {
1916                              continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1917                          }
1918                          if ( *chPtr == '#' ) {
1919                              // only allow one # character
1920                              if ( !sawHash ) {
1921                                  sawHash = true;
1922                                  continue;
1923                              }
1924                              else {
1925                                  result = false;
1926                                  break;
1927                              }
1928                          }
1929  #if DEPLOYMENT_TARGET_WINDOWS
1930                          // <rdar://problem/7134119> CF on Windows: CFURLCreateWithString should work with | in path on Windows
1931                          if ( isURLLegalCharacter(*chPtr) || *chPtr == '|' ) {
1932                              continue;
1933                          }
1934  #else
1935                          if ( isURLLegalCharacter(*chPtr) ) {
1936                              continue;
1937                          }
1938  #endif
1939                          else {
1940                              result = false;
1941                              break;
1942                          }
1943                      }
1944                      else {
1945                          if ( isHexDigit(*chPtr) ) {
1946                              --checkHexDigit;
1947                              continue;
1948                          }
1949                          else {
1950                              result = false;
1951                              break;
1952                          }
1953                      }
1954                  }
1955                  
1956                  if ( !result ) {
1957                      break; // out of main while loop
1958                  }
1959                  
1960                  idx += rangeLength;
1961              }
1962          }
1963      }
1964      else {
1965          CFAssert(false, __kCFLogAssertion, "Cannot create an CFURL from a NULL string");
1966          result = false;
1967      }
1968      
1969      return ( result );
1970  }
1971  
1972  /* initializes a URL object with a URL string */
1973  static CFURLRef _CFURLCreateWithURLString(CFAllocatorRef allocator, CFStringRef string, Boolean checkForLegalCharacters, CFURLRef baseURL)
1974  {
1975      struct __CFURL *result = NULL;
1976  #if DEBUG_URL_INITIALIZER_LOGGING
1977      CFStringRef input_string = string ? CFRetain(string) : NULL;
1978      Boolean input_checkForLegalCharacters = checkForLegalCharacters;
1979      CFURLRef input_baseURL = baseURL ? CFRetain(baseURL) : NULL;
1980  #endif
1981      if ( checkForLegalCharacters ? _CFStringIsLegalURLString(string) : true ) {
1982          // determine if URL is absolute (i.e. it has a scheme and the scheme characters are valid
1983          CFStringInlineBuffer stringBuffer;
1984          CFIndex length = CFStringGetLength(string);
1985          CFIndex idx = 0;
1986          Boolean isAbsolute = false;
1987          Boolean schemeCharsValid = true;
1988  
1989          CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length));
1990          // the first character of the scheme must be ALPHA
1991          if ( (length > 0) && isALPHA(__CFStringGetCharacterFromInlineBufferQuick(&stringBuffer, 0)) ) {
1992              // stop looking if we hit the end of the string, find a colon (isAbsolute), or find a non-scheme character (schemeCharsValid)
1993              while (idx < length && schemeCharsValid && !isAbsolute) {
1994                  CFIndex rangeLength = (idx + __kCFStringInlineBufferLength <= length) ? __kCFStringInlineBufferLength : length - idx;
1995                  const UniChar *chPtr = CFStringGetCharactersPtrFromInlineBuffer(&stringBuffer, CFRangeMake(idx, rangeLength));
1996                  for (CFIndex i = 0; i < rangeLength; ++i, ++chPtr) {
1997                      if ( *chPtr == ':' ) {
1998                          isAbsolute = true;
1999                          break;
2000                      }
2001                      if ( !scheme_valid(*chPtr) ) {
2002                          schemeCharsValid = false;
2003                          break;
2004                      }
2005                  }
2006                  if ( isAbsolute ) {
2007                      baseURL = NULL;
2008                      break;
2009                  }
2010                  idx += rangeLength;
2011              }
2012          }
2013          
2014  #if DEBUG_URL_MEMORY_USAGE
2015          numURLsParsed++;
2016  #endif
2017          // get the range flags, ranges and range count from the parser
2018          uint8_t numberOfRanges;
2019          UInt32 flags = 0;
2020          CFRange packedRanges[MAX_COMPONENTS];
2021          
2022          _parseComponents(allocator, string, baseURL, &flags, packedRanges, &numberOfRanges);
2023          // allocate the URL object with the appropriate number of ranges
2024          result = _CFURLAlloc(allocator, numberOfRanges);
2025          if ( result ) {
2026              result->_flags = flags;
2027              memcpy(result->_ranges, packedRanges, sizeof(CFRange) * numberOfRanges);
2028              result->_string = CFStringCreateCopy(allocator, string);
2029              result->_base = baseURL ? CFURLCopyAbsoluteURL(baseURL) : NULL;
2030  #if DEBUG_URL_MEMORY_USAGE
2031              if ( url->_base ) {
2032                  numURLsWithBaseURL ++;
2033              }
2034  #endif
2035          }
2036      }
2037  #if DEBUG_URL_INITIALIZER_LOGGING
2038      CFLog(kCFLogLevelError, CFSTR("_CFURLCreateWithURLString (in) string '%@', checkForLegalCharacters %@, baseURL %@\n\t_CFURLCreateWithURLString (out) result %@"), input_string, input_checkForLegalCharacters ? CFSTR("YES") : CFSTR("NO"), input_baseURL, result);
2039      if ( input_string ) {
2040          CFRelease(input_string);
2041      }
2042      if ( input_baseURL ) {
2043          CFRelease(input_baseURL);
2044      }
2045  #endif   
2046      return ( result );
2047  }
2048  
2049  /* initializes a URL object with a file system path */
2050  static CFURLRef _CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef fileSystemPath, CFURLPathStyle pathStyle, Boolean isDirectory, CFURLRef baseURL)
2051  {
2052  #if DEBUG_URL_INITIALIZER_LOGGING
2053      CFStringRef input_fileSystemPath = fileSystemPath ? CFRetain(fileSystemPath) : NULL;
2054      CFURLPathStyle input_pathStyle = pathStyle;
2055      Boolean input_isDirectory = isDirectory;
2056      CFURLRef input_baseURL = baseURL ? CFRetain(baseURL) : NULL;
2057  #endif
2058      CFAssert1(fileSystemPath != NULL, __kCFLogAssertion, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__);
2059  #pragma GCC diagnostic push
2060  #pragma GCC diagnostic ignored "-Wdeprecated"
2061      CFAssert2(pathStyle == kCFURLPOSIXPathStyle || pathStyle == kCFURLHFSPathStyle || pathStyle == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__, pathStyle);
2062  #pragma GCC diagnostic pop
2063      
2064      struct __CFURL *result = NULL;
2065      CFStringRef urlString = NULL;
2066      Boolean isAbsolute;
2067      Boolean isFileReferencePath = false;
2068      Boolean posixAndUrlPathsMatch = false;
2069      Boolean releaseBaseURL = false;
2070      CFIndex len = CFStringGetLength(fileSystemPath);
2071      
2072      if (len > 0) {
2073          // Determine if fileSystemPath is an absolute path. If kCFURLPOSIXPathStyle, determine if it is a file reference path.
2074          // Then, convert the fileSystemPath to a urlString. The urlString returned will have a pathDelim at the end if isDirectory
2075          // was true and no pathDelim if isDirectory was false (unless the urlPath is "/").
2076          // If isAbsolute, "file://" will be prepended to the urlString.
2077          switch (pathStyle) {
2078              case kCFURLPOSIXPathStyle:
2079                  isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(fileSystemPath, 0) == '/');
2080                  isFileReferencePath = _pathHasFileIDPrefix(fileSystemPath);
2081                  urlString = POSIXPathToURLPath(fileSystemPath, allocator, isDirectory, isAbsolute, &posixAndUrlPathsMatch);
2082                  break;
2083                  
2084                  
2085              case kCFURLWindowsPathStyle:
2086              {
2087                  // this is what _CFURLInitFSPath() did (with a small change to handle absolute paths that begin with a single backslash)
2088                  UniChar firstChar = 0 < len ? CFStringGetCharacterAtIndex(fileSystemPath, 0) : 0;
2089                  UniChar secondChar = 1 < len ? CFStringGetCharacterAtIndex(fileSystemPath, 1) : 0;
2090                  Boolean isDrive = isALPHA(firstChar) && (secondChar == ':' || secondChar == '|');
2091                  
2092                  if ( isDrive) {
2093                      // A disk designator
2094                      isAbsolute = true;
2095                      urlString = WindowsPathToURLPath(fileSystemPath, allocator, isDirectory, isAbsolute);
2096                  }
2097                  else if ( firstChar == '\\' ) {
2098                      // Either a UNC name of any format (which always start with two backslash characters), or an absolute path which starts with a single backslash.
2099                      isAbsolute = true;
2100                      urlString = WindowsPathToURLPath(fileSystemPath, allocator, isDirectory, isAbsolute);
2101                  }
2102                  else if (firstChar == '/') {
2103                      // there's probably code that passes POSIX paths in but wrongly specifies kCFURLWindowsPathStyle.
2104                      pathStyle = kCFURLPOSIXPathStyle;
2105                      isAbsolute = true;
2106                      urlString = POSIXPathToURLPath(fileSystemPath, allocator, isDirectory, isAbsolute, &posixAndUrlPathsMatch);
2107                  }
2108                  else {
2109                      isAbsolute = false;
2110                      urlString = WindowsPathToURLPath(fileSystemPath, allocator, isDirectory, isAbsolute);
2111                  }
2112              }
2113                  break;
2114          }
2115          
2116          CFAssert2(urlString != NULL, __kCFLogAssertion, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__, urlString);
2117          
2118          if ( urlString ) {
2119              if ( isAbsolute ) {
2120                  // if fileSystemPath is an absolute path, ignore baseURL (if provided)
2121                  baseURL = NULL;
2122              }
2123              else if ( baseURL == NULL ) {
2124                  // if fileSystemPath is a relative path and no baseURL is provided, use the current working directory
2125                  baseURL = _CFURLCreateCurrentDirectoryURL(allocator);
2126                  releaseBaseURL = true;
2127              }
2128              
2129              // override isDirectory if the path is to root
2130              if ( !isDirectory && (len == 1) && (CFStringGetCharacterAtIndex(urlString, 0) == '/') ) {
2131                  // Override isDirectory
2132                  isDirectory = true;
2133              }
2134              
2135              // allocate the URL object with the appropriate number of ranges
2136              result = _CFURLAlloc(allocator, isAbsolute ? (AddAuthorityToFileURL() ? 3 : 2) : 1);
2137              if ( result ) {
2138                  result->_string = CFStringCreateCopy(allocator, urlString);
2139                  result->_base = baseURL ? CFURLCopyAbsoluteURL(baseURL) : NULL;
2140  #if DEBUG_URL_MEMORY_USAGE
2141                  if ( url->_base ) {
2142                      numURLsWithBaseURL ++;
2143                  }
2144  #endif
2145                  // hard coded parse
2146                  if ( isAbsolute ) {
2147                      UInt32 flags = IS_DECOMPOSABLE | HAS_SCHEME | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH;
2148                      flags |= (isDirectory ? IS_DIRECTORY : 0);
2149                      if ( isFileReferencePath ) {
2150                          // if the URL is a file reference URL, don't set IS_CANONICAL_FILE_URL or POSIX_AND_URL_PATHS_MATCH
2151                          flags |= PATH_HAS_FILE_ID;
2152                      }
2153                      else {
2154                          // only posix style paths can be canonical because POSIXPathToURLPath() converts the string to file system representation
2155                          flags |= ((pathStyle == kCFURLPOSIXPathStyle) ? IS_CANONICAL_FILE_URL : 0);
2156                          flags |= (posixAndUrlPathsMatch ? POSIX_AND_URL_PATHS_MATCH : 0);
2157                      }
2158                      _setSchemeTypeInFlags(&flags, kHasFileScheme);
2159                      
2160                      if ( AddAuthorityToFileURL() ) {
2161                          result->_flags = flags | HAS_HOST;
2162                          result->_ranges[0] = CFRangeMake(0, 4); // scheme "file"
2163                          result->_ranges[1] = CFRangeMake(7, 9); // host "localhost"
2164                          result->_ranges[2] = CFRangeMake(16, CFStringGetLength(urlString)- 16);
2165                      }
2166                      else {
2167                          result->_flags = flags;
2168                          result->_ranges[0] = CFRangeMake(0, 4); // scheme "file"
2169                          result->_ranges[1] = CFRangeMake(7, CFStringGetLength(urlString)- 7);
2170                      }
2171                  } else {
2172                      result->_flags = (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH;
2173                      result->_ranges[0] = CFRangeMake(0, CFStringGetLength(result->_string));
2174                  }
2175              }
2176              
2177              if ( releaseBaseURL && baseURL ) {
2178                  CFRelease(baseURL);
2179              }
2180              
2181              CFRelease(urlString);
2182          }
2183      }
2184      else if ( baseURL ) {
2185          result = (struct __CFURL *)CFRetain(baseURL);
2186      }
2187  #if DEBUG_URL_INITIALIZER_LOGGING
2188  #pragma GCC diagnostic push
2189  #pragma GCC diagnostic ignored "-Wdeprecated"
2190      CFLog(kCFLogLevelError, CFSTR("_CFURLCreateWithFileSystemPath (in) fileSystemPath '%@', pathStyle %@, isDirectory %@, baseURL %@\n\t_CFURLCreateWithFileSystemPath (out) result %@"), input_fileSystemPath, input_pathStyle == kCFURLPOSIXPathStyle ? CFSTR("POSIX") : input_pathStyle == kCFURLHFSPathStyle ? CFSTR("HFS"): CFSTR("Windows"), input_isDirectory ? CFSTR("YES") : CFSTR("NO"), input_baseURL, result);
2191  #pragma GCC diagnostic pop
2192      if ( input_fileSystemPath ) {
2193          CFRelease(input_fileSystemPath);
2194      }
2195      if ( input_baseURL ) {
2196          CFRelease(input_baseURL);
2197      }
2198  #endif
2199      return ( result );
2200  }
2201  
2202  /* initializes a URL object with the file system representation */
2203  static CFURLRef _CFURLCreateWithFileSystemRepresentation(CFAllocatorRef allocator, const UInt8 *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL)
2204  {
2205  #if DEBUG_URL_INITIALIZER_LOGGING
2206      STACK_BUFFER_DECL(UInt8, input_buffer, bufLen);
2207      CFIndex input_bufLen = bufLen;
2208      Boolean input_isDirectory = isDirectory;
2209      CFURLRef input_baseURL = baseURL ? CFRetain(baseURL) : NULL;
2210      memcpy(input_buffer, buffer, bufLen);
2211  #endif
2212      struct __CFURL *result = NULL;
2213      if (bufLen > 0) {
2214  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
2215          Boolean isAbsolute = bufLen && (*buffer == '/');
2216          Boolean addedPercentEncoding;
2217          Boolean releaseBaseURL = false;
2218          
2219          if ( isAbsolute ) {
2220              // if buffer contains an absolute path, ignore baseURL (if provided)
2221              baseURL = NULL;
2222          }
2223          else if ( baseURL == NULL ) {
2224              // if buffer contains a relative path and no baseURL is provided, use the current working directory
2225              baseURL = _CFURLCreateCurrentDirectoryURL(allocator);
2226              releaseBaseURL = true;
2227          }
2228          CFStringRef urlString = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(allocator, buffer, bufLen, isDirectory, isAbsolute, false /*windowsPath*/, &addedPercentEncoding);
2229          if ( urlString ) {
2230              // allocate the URL object with the appropriate number of ranges
2231              result = _CFURLAlloc(allocator, isAbsolute ? (AddAuthorityToFileURL() ? 3 : 2) : 1);
2232              if ( result ) {
2233                  result->_string = CFStringCreateCopy(allocator, urlString);
2234                  result->_base = baseURL ? CFURLCopyAbsoluteURL(baseURL) : NULL;
2235  #if DEBUG_URL_MEMORY_USAGE
2236                  if ( result->_base ) {
2237                      numURLsWithBaseURL ++;
2238                  }
2239  #endif
2240                  // hard coded parse
2241                  if ( isAbsolute ) {
2242                      if ( AddAuthorityToFileURL() ) {
2243                          result->_flags = (addedPercentEncoding ? 0 : POSIX_AND_URL_PATHS_MATCH ) | (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_SCHEME | HAS_HOST | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH | IS_CANONICAL_FILE_URL;
2244                          _setSchemeTypeInFlags(&result->_flags, kHasFileScheme);
2245                          result->_ranges[0] = CFRangeMake(0, 4); // scheme "file"
2246                          result->_ranges[1] = CFRangeMake(7, 9); // host "localhost"
2247                          result->_ranges[2] = CFRangeMake(16, CFStringGetLength(urlString)- 16);
2248                      }
2249                      else {
2250                          result->_flags = (addedPercentEncoding ? 0 : POSIX_AND_URL_PATHS_MATCH ) | (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_SCHEME | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH | IS_CANONICAL_FILE_URL;
2251                          _setSchemeTypeInFlags(&result->_flags, kHasFileScheme);
2252                          result->_ranges[0] = CFRangeMake(0, 4); // scheme "file"
2253                          result->_ranges[1] = CFRangeMake(7, CFStringGetLength(urlString)- 7);
2254                      }
2255                  } else {
2256                      result->_flags = (addedPercentEncoding ? 0 : POSIX_AND_URL_PATHS_MATCH ) | (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH;
2257                      *(result->_ranges) = CFRangeMake(0, CFStringGetLength(result->_string));
2258                  }
2259              }
2260              if ( releaseBaseURL && baseURL ) {
2261                  CFRelease(baseURL);
2262              }
2263              CFRelease(urlString);
2264          }
2265  #elif DEPLOYMENT_TARGET_WINDOWS
2266          CFStringRef filePath = CFStringCreateWithBytes(allocator, buffer, bufLen, CFStringFileSystemEncoding(), false);
2267          if ( filePath ) {
2268              result = _CFURLCreateWithFileSystemPath(allocator, filePath, kCFURLWindowsPathStyle, isDirectory, baseURL);
2269              CFRelease(filePath);
2270          }
2271  #endif
2272      }
2273      else if ( baseURL ) {
2274          result = (struct __CFURL *)CFRetain(baseURL);
2275      }
2276  #if DEBUG_URL_INITIALIZER_LOGGING
2277      CFLog(kCFLogLevelError, CFSTR("_CFURLCreateWithFileSystemRepresentation (in) buffer '%*s', isDirectory %@, baseURL %@\n\t_CFURLCreateWithFileSystemRepresentation (out) result %@"), input_bufLen, input_buffer, input_isDirectory ? CFSTR("YES") : CFSTR("NO"), input_baseURL, result);
2278      if ( input_baseURL ) {
2279          CFRelease(input_baseURL);
2280      }
2281  #endif
2282      return ( result );
2283  }
2284  
2285  // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
2286  CFURLRef CFURLCreateWithBytes(CFAllocatorRef allocator, const uint8_t *URLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL) {
2287      CFStringRef  urlString = CFStringCreateWithBytes(allocator, URLBytes, length, encoding, false);
2288      CFURLRef result = NULL;
2289      if ( urlString ) {
2290              result = _CFURLCreateWithURLString(allocator, urlString, false /* checkForLegalCharacters */, baseURL);
2291              if ( result ) {
2292                  if (encoding != kCFStringEncodingUTF8) {
2293                      ((struct __CFURL *)result)->_encoding = encoding;
2294  #if DEBUG_URL_MEMORY_USAGE
2295                      numNonUTF8EncodedURLs++;
2296  #endif
2297                  }
2298              }
2299          CFRelease(urlString); // it's retained by result, now.
2300      }
2301      return ( result );
2302  }
2303  
2304  CFDataRef CFURLCreateData(CFAllocatorRef allocator, CFURLRef  url, CFStringEncoding encoding, Boolean escapeWhitespace) {
2305      CFDataRef result = NULL;
2306      if ( url ) {
2307          CFStringRef myStr = CFURLGetString(url);
2308          if ( myStr ) {
2309              result = CFStringCreateExternalRepresentation(allocator, myStr, encoding, 0);
2310          }
2311      }
2312      return result;
2313  }
2314  
2315  // Any escape sequences in URLString will be interpreted via UTF-8.
2316  CFURLRef CFURLCreateWithString(CFAllocatorRef allocator, CFStringRef  URLString, CFURLRef  baseURL) {
2317      CFURLRef url = NULL;
2318      if ( URLString ) {
2319              url = _CFURLCreateWithURLString(allocator, URLString, true /* checkForLegalCharacters */, baseURL);
2320      }
2321      return ( url );
2322  }
2323  
2324  static CFURLRef _CFURLCreateWithArbitraryString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) {
2325      CFURLRef url = NULL;
2326      if ( URLString ) {
2327              url = _CFURLCreateWithURLString(allocator, URLString, false /* checkForLegalCharacters */, baseURL);
2328      }
2329      return ( url );
2330  }
2331  
2332  CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *relativeURLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL, Boolean useCompatibilityMode) {
2333      CFURLRef result = NULL;
2334      
2335      /*
2336       CFURLCreateAbsoluteURLWithBytes() and useCompatibilityMode is for:
2337           <rdar://problem/2711611> Problem with '|' in url and calling CFURLCreateWithString()
2338           <rdar://problem/3085893> CFURL resolves "../" URLs at top level in a way that is not the same as web browsers
2339           <rdar://problem/3085920> CFURL API should be more helpful for accepting URLs with technically-bad characters
2340           <rdar://problem/3205656> Safari needs CFURL to deal with google.com URLs that end in "%"
2341           <rdar://problem/3219233> Safari needs CFURL to not remove path when relative URL is just a query string
2342           <rdar://problem/3219240> Safari needs CFURL to support "compatibility" resolution of partial URLs with schemes
2343       
2344       If useCompatibilityMode is true, the rules historically used on the web are used to resolve relativeString against baseURL - these rules are generally listed in the RFC as optional or alternate interpretations.  Otherwise, the strict rules from the RFC are used.
2345       
2346       The major differences are that in compatibility mode, we are lenient of the scheme appearing in relative portion, leading "../" components are removed from the final URL's path, and if the relative portion contains only resource specifier pieces (query, parameters, and fragment), then the last path component of the base URL will not be deleted
2347       */
2348  
2349      // if not useCompatibilityMode, use CFURLCreateWithBytes and then CFURLCopyAbsoluteURL if there's a baseURL
2350      if ( !useCompatibilityMode ) {
2351          CFURLRef url = CFURLCreateWithBytes(alloc, relativeURLBytes, length, encoding, baseURL);
2352          if ( url != NULL ) {
2353              if ( baseURL != NULL ) {
2354                  result = CFURLCopyAbsoluteURL(url);
2355                  CFRelease(url);
2356              } else {
2357                  result = url;
2358              }
2359          }
2360      } else {
2361          UInt32 absFlags = 0;
2362          CFRange absRanges[MAX_COMPONENTS];
2363          uint8_t numberOfRanges;
2364          CFStringRef absString = NULL;
2365          Boolean absStringIsMutable = false;
2366          CFURLRef absURL;
2367          CFStringRef relativeString;
2368          
2369          relativeString = CFStringCreateWithBytes(alloc, relativeURLBytes, length, encoding, false);
2370          if ( relativeString != NULL ) {
2371              if (!baseURL) {
2372                  absString = relativeString;
2373              } else {
2374                  UniChar ch = 0;
2375                  if ( CFStringGetLength(relativeString) > 0 ) {
2376                      ch = CFStringGetCharacterAtIndex(relativeString, 0);
2377                  }
2378                  if (ch == '?' || ch == ';' || ch == '#') {
2379                      // Nothing but parameter + query + fragment; append to the baseURL string
2380                      CFStringRef baseString;
2381                      if (CF_IS_OBJC(CFURLGetTypeID(), baseURL)) {
2382                          baseString = CFURLGetString(baseURL);
2383                      } else {
2384                          baseString = baseURL->_string;
2385                      }
2386                      absString = CFStringCreateMutable(alloc, CFStringGetLength(baseString) + CFStringGetLength(relativeString));
2387                      CFStringAppend((CFMutableStringRef)absString, baseString);
2388                      CFStringAppend((CFMutableStringRef)absString, relativeString);
2389                      absStringIsMutable = true;
2390                  } else {
2391                      UInt32 relFlags = 0;
2392                      CFRange relRanges[MAX_COMPONENTS];
2393                      CFStringRef relString = NULL;
2394                      _parseComponents(alloc, relativeString, baseURL, &relFlags, relRanges, &numberOfRanges);
2395                      if (relFlags & HAS_SCHEME) {
2396                          CFStringRef baseScheme = CFURLCopyScheme(baseURL);
2397                          CFRange relSchemeRange = _rangeForComponent(relFlags, relRanges, HAS_SCHEME);
2398                          if (baseScheme && CFStringGetLength(baseScheme) == relSchemeRange.length && CFStringHasPrefix(relativeString, baseScheme)) {
2399                              relString = CFStringCreateWithSubstring(alloc, relativeString, CFRangeMake(relSchemeRange.length+1, CFStringGetLength(relativeString) - relSchemeRange.length - 1));
2400                              relFlags = 0;
2401                              _parseComponents(alloc, relString, baseURL, &relFlags, relRanges, &numberOfRanges);
2402                          } else {
2403                              // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
2404                              CFRetain(relativeString);
2405                              absString = relativeString;
2406                          }
2407                          if (baseScheme) CFRelease(baseScheme);
2408                      } else {
2409                          CFRetain(relativeString);
2410                          relString = relativeString;
2411                      }
2412                      if (!absString) {
2413                          if (!CF_IS_OBJC(CFURLGetTypeID(), baseURL)) {
2414                              absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseURL->_string, baseURL->_flags, baseURL->_ranges);
2415                          } else {
2416                              CFStringRef baseString;
2417                              UInt32 baseFlags = 0;
2418                              CFRange baseRanges[MAX_COMPONENTS];
2419                              if (CF_IS_OBJC(CFURLGetTypeID(), baseURL)) {
2420                                  baseString = CFURLGetString(baseURL);
2421                              } else {
2422                                  baseString = baseURL->_string;
2423                              }
2424                              _parseComponents(alloc, baseString, NULL, &baseFlags, baseRanges, &numberOfRanges);
2425                              absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges);
2426                          }
2427                          absStringIsMutable = true;
2428                      }
2429                      if (relString) CFRelease(relString);
2430                  }
2431                  CFRelease(relativeString);
2432              }            
2433          }
2434          if ( absString ) {
2435              _parseComponents(alloc, absString, NULL, &absFlags, absRanges, &numberOfRanges);
2436              if (absFlags & HAS_PATH) {
2437                  CFRange pathRg = _rangeForComponent(absFlags, absRanges, HAS_PATH);
2438                  // This is expensive, but it allows us to reuse _resolvedPath.  It should be cleaned up to get this allocation removed at some point. - REW
2439                  UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (pathRg.length + 1), 0);
2440                  CFStringRef newPath;
2441                  CFStringGetCharacters(absString, pathRg, buf);
2442                  buf[pathRg.length] = '\0';
2443                  newPath = _resolvedPath(buf, buf + pathRg.length, '/', true, false, alloc);
2444                  if (CFStringGetLength(newPath) != pathRg.length) {
2445                      if (!absStringIsMutable) {
2446                          CFStringRef tmp = CFStringCreateMutableCopy(alloc, CFStringGetLength(absString), absString);
2447                          CFRelease(absString);
2448                          absString = tmp;
2449                      }
2450                      CFStringReplace((CFMutableStringRef)absString, pathRg, newPath);
2451                  }
2452                  CFRelease(newPath);
2453                  // Do not deallocate buf; newPath took ownership of it.
2454              }
2455              absURL = _CFURLCreateWithArbitraryString(alloc, absString, NULL);
2456              CFRelease(absString);
2457              if (absURL) {
2458                  ((struct __CFURL *)absURL)->_encoding = encoding;
2459  #if DEBUG_URL_MEMORY_USAGE
2460                  if ( encoding != kCFStringEncodingUTF8 ) {
2461                      numNonUTF8EncodedURLs++;
2462                  }
2463  #endif
2464              }
2465              result = absURL;
2466          }
2467      }
2468      
2469      return ( result );
2470  }
2471  
2472  /* This function is this way because I pulled it out of _resolvedURLPath (so that _resolvedFileSystemPath could use it), and I didn't want to spend a bunch of energy reworking the code.  So instead of being a bit more intelligent about inputs, it just demands a slightly perverse set of parameters, to match the old _resolvedURLPath code.  -- REW, 6/14/99 */
2473  static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc) {
2474      UniChar *idx = pathStr;
2475      while (idx < end) {
2476          if (*idx == '.') {
2477              if (idx+1 == end) {
2478                  if (idx != pathStr) {
2479                      *idx = '\0';
2480                      end = idx;
2481                  }
2482                  break;
2483              } else if (*(idx+1) == pathDelimiter) {
2484                  if (idx + 2 != end || idx != pathStr) {
2485                      memmove(idx, idx+2, (end-(idx+2)+1) * sizeof(UniChar));
2486                      end -= 2;
2487                      continue;
2488                  } else {
2489                      // Do not delete the sole path component
2490                      break;
2491                  }
2492              } else if (( end-idx >= 2 ) &&  *(idx+1) == '.' && (idx+2 == end || (( end-idx > 2 ) && *(idx+2) == pathDelimiter))) {
2493                  if (idx - pathStr >= 2) {
2494                      // Need at least 2 characters between index and pathStr, because we know if index != newPath, then *(index-1) == pathDelimiter, and we need something before that to compact out.
2495                      UniChar *lastDelim = idx-2;
2496                      while (lastDelim >= pathStr && *lastDelim != pathDelimiter) lastDelim --;
2497                      lastDelim ++;
2498                      if (lastDelim != idx && (idx-lastDelim != 3 || *lastDelim != '.' || *(lastDelim +1) != '.')) {
2499                          // We have a genuine component to compact out
2500                          if (idx+2 != end) {
2501                              unsigned numCharsToMove = end - (idx+3) + 1; // +1 to move the '\0' as well
2502                              memmove(lastDelim, idx+3, numCharsToMove * sizeof(UniChar));
2503                              end -= (idx + 3 - lastDelim);
2504                              idx = lastDelim;
2505                              continue;
2506                          } else if (lastDelim != pathStr) {
2507                              *lastDelim = '\0';
2508                              end = lastDelim;
2509                              break;
2510                          } else {
2511                              // Don't allow the path string to devolve to the empty string.  Fall back to "." instead. - REW
2512                              pathStr[0] = '.';
2513                              pathStr[1] = '/';
2514                              pathStr[2] = '\0';
2515                              end = &pathStr[3];
2516                              break;
2517                          }
2518                      }
2519                  } else if (stripLeadingDotDots) {
2520                      if (idx + 3 != end) {
2521                          unsigned numCharsToMove = end - (idx + 3) + 1;
2522                          memmove(idx, idx+3, numCharsToMove * sizeof(UniChar));
2523                          end -= 3;
2524                          continue;
2525                      } else {
2526                          // Do not devolve the last path component
2527                          break;
2528                      }
2529                  }
2530              }
2531          }
2532  		while (idx < end && *idx != pathDelimiter) idx ++;
2533          idx ++;
2534      }
2535      if (stripTrailingDelimiter && end > pathStr && end-1 != pathStr && *(end-1) == pathDelimiter) {
2536          end --;
2537      }
2538      // return an zero-length string if end < pathStr
2539      return CFStringCreateWithCharactersNoCopy(alloc, pathStr, end >= pathStr ? end - pathStr : 0, alloc);
2540  }
2541  
2542  static CFMutableStringRef resolveAbsoluteURLStringBuffer(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, const CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, const CFRange *baseRanges, UniChar *buf)
2543  {
2544      CFStringAppendBuffer appendBuffer;
2545      UniChar chars[2];
2546      CFStringInitAppendBuffer(alloc, &appendBuffer);
2547      CFRange rg;
2548      
2549      rg = _rangeForComponent(baseFlags, baseRanges, HAS_SCHEME);
2550      if (rg.location != kCFNotFound) {
2551          CFStringGetCharacters(baseString, rg, buf);
2552          CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2553          chars[0] = ':';
2554          CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2555      }
2556      
2557      if (relFlags & NET_LOCATION_MASK) {
2558          CFStringAppendStringToAppendBuffer(&appendBuffer, relString);
2559      } else {
2560          chars[0] = '/';
2561          chars[1] = '/';
2562          CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 2);
2563          rg = _netLocationRange(baseFlags, baseRanges);
2564          if (rg.location != kCFNotFound) {
2565              CFStringGetCharacters(baseString, rg, buf);
2566              CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2567          }
2568          
2569          if (relFlags & HAS_PATH) {
2570              CFRange relPathRg = _rangeForComponent(relFlags, relRanges, HAS_PATH);
2571              CFRange basePathRg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH);
2572              CFStringRef newPath;
2573              Boolean useRelPath = false;
2574              Boolean useBasePath = false;
2575              if (basePathRg.location == kCFNotFound) {
2576                  useRelPath = true;
2577              } else if (relPathRg.length == 0) {
2578                  useBasePath = true;
2579              } else if (CFStringGetCharacterAtIndex(relString, relPathRg.location) == '/') {
2580                  useRelPath = true;
2581              } else if (basePathRg.location == kCFNotFound || basePathRg.length == 0) {
2582                  useRelPath = true;
2583              }
2584              if (useRelPath) {
2585                  newPath = CFStringCreateWithSubstring(alloc, relString, relPathRg);
2586              } else if (useBasePath) {
2587                  newPath = CFStringCreateWithSubstring(alloc, baseString, basePathRg);
2588              } else {
2589                  // FIXME: Get rid of this allocation
2590                  UniChar *newPathBuf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (relPathRg.length + basePathRg.length + 1), 0);
2591                  UniChar *idx, *end;
2592                  CFStringGetCharacters(baseString, basePathRg, newPathBuf);
2593                  idx = newPathBuf + basePathRg.length - 1;
2594                  while (idx != newPathBuf && *idx != '/') idx --;
2595                  if (*idx == '/') idx ++;
2596                  CFStringGetCharacters(relString, relPathRg, idx);
2597                  end = idx + relPathRg.length;
2598                  *end = 0;
2599                  newPath = _resolvedPath(newPathBuf, end, '/', false, false, alloc);
2600              }
2601              /* Under Win32 absolute path can begin with letter
2602               * so we have to add one '/' to the newString
2603               * (Sergey Zubarev)
2604               */
2605              // No - the input strings here are URL path strings, not Win32 paths.
2606              // Absolute paths should have had a '/' prepended before this point.
2607              // I have removed Sergey Zubarev's change and left his comment (and
2608              // this one) as a record. - REW, 1/5/2004
2609              
2610              // if the relative URL does not begin with a slash and
2611              // the base does not end with a slash, add a slash
2612              if ((basePathRg.location == kCFNotFound || basePathRg.length == 0) && CFStringGetCharacterAtIndex(newPath, 0) != '/') {
2613                  chars[0] = '/';
2614                  CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2615              }
2616              
2617              CFStringAppendStringToAppendBuffer(&appendBuffer, newPath);
2618              CFRelease(newPath);
2619              rg.location = relPathRg.location + relPathRg.length;
2620              rg.length = CFStringGetLength(relString);
2621              if (rg.length > rg.location) {
2622                  rg.length -= rg.location;
2623                  CFStringGetCharacters(relString, rg, buf);
2624                  CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2625              }
2626          } else {
2627              rg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH);
2628              if (rg.location != kCFNotFound) {
2629                  CFStringGetCharacters(baseString, rg, buf);
2630                  CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2631              }
2632              
2633              if (!(relFlags & RESOURCE_SPECIFIER_MASK)) {
2634                  // ???  Can this ever happen?
2635                  UInt32 rsrcFlag = _firstResourceSpecifierFlag(baseFlags);
2636                  if (rsrcFlag) {
2637                      rg.location = _rangeForComponent(baseFlags, baseRanges, rsrcFlag).location;
2638                      rg.length = CFStringGetLength(baseString) - rg.location;
2639                      rg.location --; // To pick up the separator
2640                      rg.length ++;
2641                      CFStringGetCharacters(baseString, rg, buf);
2642                      CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2643                  }
2644              } else if (relFlags & HAS_PARAMETERS) {
2645                  rg = _rangeForComponent(relFlags, relRanges, HAS_PARAMETERS);
2646                  rg.location --; // To get the semicolon that starts the parameters
2647                  rg.length = CFStringGetLength(relString) - rg.location;
2648                  CFStringGetCharacters(relString, rg, buf);
2649                  CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2650              } else {
2651                  // Sigh; we have to resolve these against one another
2652                  rg = _rangeForComponent(baseFlags, baseRanges, HAS_PARAMETERS);
2653                  if (rg.location != kCFNotFound) {
2654                      chars[0] = ';';
2655                      CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2656                      CFStringGetCharacters(baseString, rg, buf);
2657                      CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2658                  }
2659                  rg = _rangeForComponent(relFlags, relRanges, HAS_QUERY);
2660                  if (rg.location != kCFNotFound) {
2661                      chars[0] = '?';
2662                      CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2663                      CFStringGetCharacters(relString, rg, buf);
2664                      CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2665                  } else {
2666                      rg = _rangeForComponent(baseFlags, baseRanges, HAS_QUERY);
2667                      if (rg.location != kCFNotFound) {
2668                          chars[0] = '?';
2669                          CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2670                          CFStringGetCharacters(baseString, rg, buf);
2671                          CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2672                      }
2673                  }
2674                  // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2675                  rg = _rangeForComponent(relFlags, relRanges, HAS_FRAGMENT);
2676                  if (rg.location != kCFNotFound) {
2677                      chars[0] = '#';
2678                      CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2679                      CFStringGetCharacters(relString, rg, buf);
2680                      CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2681                  }
2682              }
2683          }
2684      }
2685      return CFStringCreateMutableWithAppendBuffer(&appendBuffer);
2686  }
2687  
2688  static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, const CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, const CFRange *baseRanges) {
2689      CFMutableStringRef result;
2690      CFIndex bufLen = CFStringGetLength(baseString) + CFStringGetLength(relString); // Overkill, but guarantees we never allocate again
2691      if ( bufLen <= 1024 ) {
2692          STACK_BUFFER_DECL(UniChar, buf, bufLen);
2693          result = resolveAbsoluteURLStringBuffer(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges, buf);
2694          return ( result );
2695      }
2696      else {
2697          UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, bufLen * sizeof(UniChar), 0);
2698          result = resolveAbsoluteURLStringBuffer(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges, buf);
2699          CFAllocatorDeallocate(alloc, buf);
2700          return ( result );
2701      }
2702  }
2703  
2704  CFURLRef CFURLCopyAbsoluteURL(CFURLRef  relativeURL) {
2705      CFURLRef  anURL, base;
2706      CFAllocatorRef alloc = CFGetAllocator(relativeURL);
2707      CFStringRef baseString, newString;
2708      UInt32 baseFlags;
2709      CFRange ranges[MAX_COMPONENTS];
2710      uint8_t numberOfRanges;
2711      const CFRange *baseRanges;
2712      Boolean baseIsObjC;
2713  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2714      Boolean filePathURLCreated = false;
2715  #endif
2716      
2717      CFAssert1(relativeURL != NULL, __kCFLogAssertion, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__);
2718      if (CF_IS_OBJC(CFURLGetTypeID(), relativeURL)) {
2719          anURL = (CFURLRef) CF_OBJC_CALLV((NSURL *)relativeURL, absoluteURL);
2720          if (anURL) CFRetain(anURL);
2721          return anURL;
2722      } 
2723  
2724      __CFGenericValidateType(relativeURL, CFURLGetTypeID());
2725  
2726      base = relativeURL->_base;
2727      if (!base) {
2728          return (CFURLRef)CFRetain(relativeURL);
2729      }
2730  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2731      else if ( CFURLIsFileReferenceURL(base) && !CFURLHasDirectoryPath(base) ) {
2732          // 16695827 - If the base URL is a file reference URL which doesn't end with a slash, we have to convert it to a file path URL before we can make it absolute.
2733          base = CFURLCreateFilePathURL(alloc, base, NULL);
2734          if ( !base ) {
2735              // could not convert file reference URL to file path URL -- fail will NULL
2736              return NULL;
2737          }
2738          filePathURLCreated = true;
2739      }
2740  #endif
2741      
2742      baseIsObjC = CF_IS_OBJC(CFURLGetTypeID(), base);
2743  
2744      if (!baseIsObjC) {
2745          baseString = base->_string;
2746          baseFlags = base->_flags;
2747          baseRanges = base->_ranges;
2748      } else {
2749          baseString = CFURLGetString(base);
2750          baseFlags = 0;
2751          _parseComponents(alloc, baseString, NULL, &baseFlags, ranges, &numberOfRanges);
2752          baseRanges = ranges;
2753      }
2754      
2755      newString = resolveAbsoluteURLString(alloc, relativeURL->_string, relativeURL->_flags, relativeURL->_ranges, baseString, baseFlags, baseRanges);
2756      anURL = _CFURLCreateWithArbitraryString(alloc, newString, NULL);
2757      CFRelease(newString);
2758      ((struct __CFURL *)anURL)->_encoding = relativeURL->_encoding;
2759  #if DEBUG_URL_MEMORY_USAGE
2760      if ( relativeURL->_encoding != kCFStringEncodingUTF8 ) {
2761  	numNonUTF8EncodedURLs++;
2762      }
2763  #endif
2764      
2765  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2766      if ( filePathURLCreated ) {
2767          CFRelease(base);
2768      }
2769  #endif
2770      
2771      return anURL;
2772  }
2773  
2774  
2775  /*******************/
2776  /* Basic accessors */
2777  /*******************/
2778  CFStringEncoding _CFURLGetEncoding(CFURLRef url) {
2779      return url->_encoding;
2780  }
2781  
2782  Boolean CFURLCanBeDecomposed(CFURLRef  anURL) {
2783      anURL = _CFURLFromNSURL(anURL);
2784      return ((anURL->_flags & IS_DECOMPOSABLE) != 0);
2785  }
2786  
2787  CFStringRef  CFURLGetString(CFURLRef  url) {
2788      CF_OBJC_FUNCDISPATCHV(CFURLGetTypeID(), CFStringRef, (NSURL *)url, relativeString);
2789      if (!_haveTestedOriginalString(url)) {
2790          computeSanitizedString(url);
2791      }
2792      if (url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) {
2793          return url->_string;
2794      } else {
2795          return _getSanitizedString( url );
2796      }
2797  }
2798  
2799  CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) {
2800      CFIndex length, charsConverted, usedLength;
2801      CFStringRef string;
2802      CFStringEncoding enc;
2803      if (CF_IS_OBJC(CFURLGetTypeID(), url)) {
2804          string = CFURLGetString(url);
2805          enc = kCFStringEncodingUTF8;
2806      } else {
2807          string = url->_string;
2808          enc = url->_encoding;
2809      }
2810      length = CFStringGetLength(string);
2811      charsConverted = CFStringGetBytes(string, CFRangeMake(0, length), enc, 0, false, buffer, bufferLength, &usedLength);
2812      if (charsConverted != length) {
2813          return -1;
2814      } else {
2815          return usedLength;
2816      }
2817  }
2818  
2819  CFURLRef  CFURLGetBaseURL(CFURLRef  anURL) {
2820      CF_OBJC_FUNCDISPATCHV(CFURLGetTypeID(), CFURLRef, (NSURL *)anURL, baseURL);
2821      return anURL->_base;
2822  }
2823  
2824  // Assumes the URL is already parsed
2825  static CFRange _rangeForComponent(UInt32 flags, const CFRange *ranges, UInt32 compFlag) {
2826      UInt32 idx = 0;
2827      if (!(flags & compFlag)) return CFRangeMake(kCFNotFound, 0);
2828      while (!(compFlag & 1)) {
2829          compFlag = compFlag >> 1;
2830          if (flags & 1) {
2831              idx ++;
2832          }
2833          flags = flags >> 1;
2834      }
2835      return ranges[idx];
2836  }
2837   
2838  static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boolean fromOriginalString, Boolean removePercentEscapes) {
2839      CFRange rg;
2840      CFStringRef comp;
2841      CFAllocatorRef alloc = CFGetAllocator(url);
2842      if (removePercentEscapes) {
2843          fromOriginalString = true;
2844      }
2845      rg = _rangeForComponent(url->_flags, url->_ranges, compFlag);
2846      if (rg.location == kCFNotFound) {
2847          comp = NULL;
2848      }
2849      else {
2850          if ( compFlag & HAS_SCHEME ) {
2851              switch ( _getSchemeTypeFromFlags(url->_flags) ) {
2852                  case kHasHttpScheme:
2853                      comp = (CFStringRef)CFRetain(kCFURLHTTPScheme);
2854                      break;
2855                      
2856                  case kHasHttpsScheme:
2857                      comp = (CFStringRef)CFRetain(kCFURLHTTPSScheme);
2858                      break;
2859                      
2860                  case kHasFileScheme:
2861                      comp = (CFStringRef)CFRetain(kCFURLFileScheme);
2862                      break;
2863                      
2864                  case kHasDataScheme:
2865                      comp = (CFStringRef)CFRetain(kCFURLDataScheme);
2866                      break;
2867                      
2868                  case kHasFtpScheme:
2869                      comp = (CFStringRef)CFRetain(kCFURLFTPScheme);
2870                      break;
2871                      
2872                  default:
2873                      comp = CFStringCreateWithSubstring(alloc, url->_string, rg);
2874                      break;
2875              }
2876          }
2877          else {
2878              comp = CFStringCreateWithSubstring(alloc, url->_string, rg);
2879          }
2880          
2881          if (comp && !fromOriginalString) {
2882              if (!_haveTestedOriginalString(url)) {
2883                  computeSanitizedString(url);
2884              }
2885              if (!(url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (_getAdditionalDataFlags(url) & compFlag)) {
2886                  CFStringRef newComp = correctedComponent(comp, compFlag, url->_encoding);
2887                  CFRelease(comp);
2888                  comp = newComp;
2889              }
2890          }
2891          if (comp && removePercentEscapes) {
2892              CFStringRef tmp;
2893              if (url->_encoding == kCFStringEncodingUTF8) {
2894                  tmp = CFURLCreateStringByReplacingPercentEscapes(alloc, comp, CFSTR(""));
2895              } else {
2896                  tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc, comp, CFSTR(""), url->_encoding);
2897              }
2898              CFRelease(comp);
2899              comp = tmp;
2900          }
2901          
2902      }
2903      return comp;
2904  }
2905  
2906  CFStringRef  CFURLCopyScheme(CFURLRef  anURL) {
2907      CFStringRef scheme;
2908      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
2909          scheme = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, scheme);
2910          if ( scheme ) {
2911              CFRetain(scheme);
2912          }
2913      }
2914      else {
2915          switch ( _getSchemeTypeFromFlags(anURL->_flags) ) {
2916              case kHasHttpScheme:
2917                  scheme = (CFStringRef)CFRetain(kCFURLHTTPScheme);
2918                  break;
2919                  
2920              case kHasHttpsScheme:
2921                  scheme = (CFStringRef)CFRetain(kCFURLHTTPSScheme);
2922                  break;
2923                  
2924              case kHasFileScheme:
2925                  scheme = (CFStringRef)CFRetain(kCFURLFileScheme);
2926                  break;
2927                  
2928              case kHasDataScheme:
2929                  scheme = (CFStringRef)CFRetain(kCFURLDataScheme);
2930                  break;
2931                  
2932              case kHasFtpScheme:
2933                  scheme = (CFStringRef)CFRetain(kCFURLFTPScheme);
2934                  break;
2935                  
2936              default:
2937                  scheme = _retainedComponentString(anURL, HAS_SCHEME, true, false);
2938                  if ( !scheme ) {
2939                      if (anURL->_base) {
2940                          scheme = CFURLCopyScheme(anURL->_base);
2941                      } else {
2942                          scheme = NULL;
2943                      }
2944                  }
2945                  break;
2946          }
2947      }
2948      return ( scheme );
2949  }
2950  
2951  static CFRange _netLocationRange(UInt32 flags, const CFRange *ranges) {
2952      CFRange netRgs[4];
2953      CFRange netRg = {kCFNotFound, 0};
2954      CFIndex i, c = 4;
2955  
2956      if ((flags & NET_LOCATION_MASK) == 0) return CFRangeMake(kCFNotFound, 0);
2957  
2958      netRgs[0] = _rangeForComponent(flags, ranges, HAS_USER);
2959      netRgs[1] = _rangeForComponent(flags, ranges, HAS_PASSWORD);
2960      netRgs[2] = _rangeForComponent(flags, ranges, HAS_HOST);
2961      netRgs[3] = _rangeForComponent(flags, ranges, HAS_PORT);
2962      for (i = 0; i < c; i ++) {
2963          if (netRgs[i].location == kCFNotFound) continue;
2964          if (netRg.location == kCFNotFound) {
2965              netRg = netRgs[i];
2966          } else {
2967              netRg.length = netRgs[i].location + netRgs[i].length - netRg.location;
2968          }
2969      }
2970      return netRg;
2971  }
2972  
2973  CFStringRef CFURLCopyNetLocation(CFURLRef  anURL) {
2974      anURL = _CFURLFromNSURL(anURL);
2975      if (anURL->_flags & NET_LOCATION_MASK) {
2976          // We provide the net location
2977          CFRange netRg = _netLocationRange(anURL->_flags, anURL->_ranges);
2978          CFStringRef netLoc;
2979          if (!_haveTestedOriginalString(anURL)) {
2980              computeSanitizedString(anURL);
2981          }
2982          if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (_getAdditionalDataFlags(anURL) & (USER_DIFFERS | PASSWORD_DIFFERS | HOST_DIFFERS | PORT_DIFFERS))) {
2983              // Only thing that can come before the net location is the scheme.  It's impossible for the scheme to contain percent escapes.  Therefore, we can use the location of netRg in _sanatizedString, just not the length. 
2984              CFRange netLocEnd;
2985              CFStringRef sanitizedString = _getSanitizedString(anURL);
2986              netRg.length = CFStringGetLength(sanitizedString) - netRg.location;
2987              if (CFStringFindWithOptions(sanitizedString, CFSTR("/"), netRg, 0, &netLocEnd)) {
2988                  netRg.length = netLocEnd.location - netRg.location;
2989              }
2990              netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, netRg);
2991          } else {
2992              netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, netRg);
2993          }
2994          return netLoc;
2995      } else if (anURL->_base) {
2996          return CFURLCopyNetLocation(anURL->_base);
2997      } else {
2998          return NULL;
2999      }
3000  }
3001  
3002  // NOTE - if you want an absolute path, you must first get the absolute URL.  If you want a file system path, use the file system methods above.
3003  CFStringRef  CFURLCopyPath(CFURLRef  anURL) {
3004      anURL = _CFURLFromNSURL(anURL);
3005      return _retainedComponentString(anURL, HAS_PATH, false, false);
3006  }
3007  
3008  /* NULL if CFURLCanBeDecomposed(anURL) is false; also does not resolve the URL against its base.  See also CFCreateAbsoluteURL().  Note that, strictly speaking, any leading '/' is not considered part of the URL's path, although its presence or absence determines whether the path is absolute.  CFURLCopyPath()'s return value includes any leading slash (giving the path the normal POSIX appearance); CFURLCopyStrictPath()'s return value omits any leading slash, and uses isAbsolute to report whether the URL's path is absolute.
3009  
3010    CFURLCopyFileSystemPath() returns the URL's path as a file system path for the given path style.  All percent escape sequences are replaced.  The URL is not resolved against its base before computing the path.
3011  */
3012  CFStringRef CFURLCopyStrictPath(CFURLRef anURL, Boolean *isAbsolute) {
3013      CFStringRef path = CFURLCopyPath(anURL);
3014      if (!path || CFStringGetLength(path) == 0) {
3015          if (path) CFRelease(path);
3016          if (isAbsolute) *isAbsolute = false;
3017          return NULL;
3018      }
3019      if (CFStringGetCharacterAtIndex(path, 0) == '/') {
3020          CFStringRef tmp;
3021          if (isAbsolute) *isAbsolute = true;
3022          tmp = CFStringCreateWithSubstring(CFGetAllocator(path), path, CFRangeMake(1, CFStringGetLength(path)-1));
3023          CFRelease(path);
3024          path = tmp;
3025      } else {
3026          if (isAbsolute) *isAbsolute = false;
3027      }
3028      return path;
3029  }
3030  
3031  Boolean CFURLHasDirectoryPath(CFURLRef  anURL) {
3032      anURL = _CFURLFromNSURL(anURL);
3033      __CFGenericValidateType(anURL, CFURLGetTypeID());
3034      if (!anURL->_base || (anURL->_flags & (HAS_PATH | NET_LOCATION_MASK))) {
3035          return ((anURL->_flags & IS_DIRECTORY) != 0);
3036      }
3037      else {
3038          return CFURLHasDirectoryPath(anURL->_base);
3039      }
3040  }
3041  
3042  static UInt32 _firstResourceSpecifierFlag(UInt32 flags) {
3043      UInt32 firstRsrcSpecFlag = 0;
3044      UInt32 flag = HAS_FRAGMENT;
3045      while (flag != HAS_PATH) {
3046          if (flags & flag) {
3047              firstRsrcSpecFlag = flag;
3048          }
3049          flag = flag >> 1;
3050      }
3051      return firstRsrcSpecFlag;
3052  }
3053  
3054  CFStringRef  CFURLCopyResourceSpecifier(CFURLRef  anURL) {
3055      anURL = _CFURLFromNSURL(anURL);
3056      __CFGenericValidateType(anURL, CFURLGetTypeID());
3057      if (!(anURL->_flags & IS_DECOMPOSABLE)) {
3058          CFRange schemeRg = _rangeForComponent(anURL->_flags, anURL->_ranges, HAS_SCHEME);
3059          CFIndex base = schemeRg.location + schemeRg.length + 1;
3060          if (!_haveTestedOriginalString(anURL)) {
3061              computeSanitizedString(anURL);
3062          }
3063          
3064          CFStringRef sanitizedString = _getSanitizedString(anURL);
3065          
3066          if (sanitizedString) {
3067              // It is impossible to have a percent escape in the scheme (if there were one, we would have considered the URL a relativeURL with a  colon in the path instead), so this range computation is always safe.
3068              return CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, CFRangeMake(base, CFStringGetLength(sanitizedString)-base));
3069          } else {
3070              return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, CFRangeMake(base, CFStringGetLength(anURL->_string)-base));
3071          }
3072      } else {
3073          UInt32 firstRsrcSpecFlag = _firstResourceSpecifierFlag(anURL->_flags);
3074          UInt32 flag;
3075          if (firstRsrcSpecFlag) {
3076              Boolean canUseOriginalString = true;
3077              Boolean canUseSanitizedString = true;
3078              CFAllocatorRef alloc = CFGetAllocator(anURL);
3079              if (!_haveTestedOriginalString(anURL)) {
3080                  computeSanitizedString(anURL);
3081              }
3082              
3083              UInt32 additionalDataFlags = _getAdditionalDataFlags(anURL);
3084              CFStringRef sanitizedString = _getSanitizedString(anURL);
3085              
3086              if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH)) {
3087                  // See if any pieces in the resource specifier differ between sanitized string and original string
3088                  for (flag = firstRsrcSpecFlag; flag != (HAS_FRAGMENT << 1); flag = flag << 1) {
3089                      if (additionalDataFlags & flag) {
3090                          canUseOriginalString = false;
3091                          break;
3092                      }
3093                  }
3094              }
3095              if (!canUseOriginalString) {
3096                  // If none of the pieces prior to the first resource specifier flag differ, then we can use the offset from the original string as the offset in the sanitized string.
3097                  for (flag = firstRsrcSpecFlag >> 1; flag != 0; flag = flag >> 1) {
3098                      if (additionalDataFlags & flag) {
3099                          canUseSanitizedString = false;
3100                          break;
3101                      }
3102                  }
3103              }
3104              if (canUseOriginalString) {
3105                  CFRange rg = _rangeForComponent(anURL->_flags, anURL->_ranges, firstRsrcSpecFlag);
3106                  rg.location --; // Include the character that demarcates the component
3107                  rg.length = CFStringGetLength(anURL->_string) - rg.location;
3108                  return CFStringCreateWithSubstring(alloc, anURL->_string, rg);
3109              } else if (canUseSanitizedString) {
3110                  CFRange rg = _rangeForComponent(anURL->_flags, anURL->_ranges, firstRsrcSpecFlag);
3111                  rg.location --; // Include the character that demarcates the component
3112                  rg.length = CFStringGetLength(sanitizedString) - rg.location;
3113                  return CFStringCreateWithSubstring(alloc, sanitizedString, rg);
3114              } else {
3115                  // Must compute the correct string to return; just reparse....
3116                  UInt32 sanFlags = 0;
3117                  CFRange sanRanges[MAX_COMPONENTS];
3118                  uint8_t numberOfRanges;
3119                  CFRange rg;
3120                  _parseComponents(alloc, sanitizedString, anURL->_base, &sanFlags, sanRanges, &numberOfRanges);
3121                  rg = _rangeForComponent(sanFlags, sanRanges, firstRsrcSpecFlag);
3122                  rg.location --; // Include the character that demarcates the component
3123                  rg.length = CFStringGetLength(sanitizedString) - rg.location;
3124                  return CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, rg);
3125              }
3126          } else {
3127              // The resource specifier cannot possibly come from the base.
3128              return NULL;
3129          }
3130      }
3131  }
3132  
3133  /*************************************/
3134  /* Accessors that create new objects */
3135  /*************************************/
3136  
3137  // For the next four methods, it is important to realize that, if a URL supplies any part of the net location (host, user, port, or password), it must supply all of the net location (i.e. none of it comes from its base URL).  Also, it is impossible for a URL to be relative, supply none of the net location, and still have its (empty) net location take precedence over its base URL (because there's nothing that precedes the net location except the scheme, and if the URL supplied the scheme, it would be absolute, and there would be no base).
3138  CFStringRef  CFURLCopyHostName(CFURLRef  anURL) {
3139      CFStringRef tmp;
3140      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
3141          tmp = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, host);
3142          if (tmp) CFRetain(tmp);
3143          return tmp;
3144      } 
3145      __CFGenericValidateType(anURL, CFURLGetTypeID());
3146      tmp = _retainedComponentString(anURL, HAS_HOST, true, true);
3147      if (tmp) {
3148          if (anURL->_flags & IS_IPV6_ENCODED) {
3149              // Have to strip off the brackets to get the true hostname.
3150              // Assume that to be legal the first and last characters are brackets!
3151              CFStringRef	strippedHost = CFStringCreateWithSubstring(CFGetAllocator(anURL), tmp, CFRangeMake(1, CFStringGetLength(tmp) - 2));
3152              CFRelease(tmp);
3153              tmp = strippedHost;
3154          }
3155          return tmp;
3156      } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3157          return CFURLCopyHostName(anURL->_base);
3158      } else {
3159          return NULL;
3160      }
3161  }
3162  
3163  // Return -1 to indicate no port is specified
3164  SInt32 CFURLGetPortNumber(CFURLRef  anURL) {
3165      CFStringRef port;
3166      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
3167          CFNumberRef cfPort = (CFNumberRef) CF_OBJC_CALLV((NSURL *)anURL, port);
3168          SInt32 num;
3169          if (cfPort && CFNumberGetValue(cfPort, kCFNumberSInt32Type, &num)) return num;
3170          return -1;
3171      } 
3172      __CFGenericValidateType(anURL, CFURLGetTypeID());
3173      port = _retainedComponentString(anURL, HAS_PORT, true, false);
3174      if (port) {
3175          SInt32 portNum, idx, length = CFStringGetLength(port);
3176          CFStringInlineBuffer buf;
3177          CFStringInitInlineBuffer(port, &buf, CFRangeMake(0, length));
3178          idx = 0;
3179          if (!__CFStringScanInteger(&buf, NULL, &idx, false, &portNum) || (idx != length)) {
3180              portNum = -1;
3181          }
3182          CFRelease(port);
3183          return portNum;
3184      } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3185          return CFURLGetPortNumber(anURL->_base);
3186      } else {
3187          return -1;
3188      }
3189  }
3190  
3191  CFStringRef  CFURLCopyUserName(CFURLRef  anURL) {
3192      CFStringRef user;
3193      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
3194          user = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, user);
3195          if (user) CFRetain(user);
3196          return user;
3197      } 
3198      __CFGenericValidateType(anURL, CFURLGetTypeID());
3199      user = _retainedComponentString(anURL, HAS_USER, true, true);
3200      if (user) {
3201          return user;
3202      } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3203          return CFURLCopyUserName(anURL->_base);
3204      } else {
3205          return NULL;
3206      }
3207  }
3208  
3209  CFStringRef  CFURLCopyPassword(CFURLRef  anURL) {
3210      CFStringRef passwd;
3211      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
3212          passwd = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, password);
3213          if (passwd) CFRetain(passwd);
3214          return passwd;
3215      } 
3216      __CFGenericValidateType(anURL, CFURLGetTypeID());
3217      passwd = _retainedComponentString(anURL, HAS_PASSWORD, true, true);
3218      if (passwd) {
3219          return passwd;
3220      } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3221          return CFURLCopyPassword(anURL->_base);
3222      } else {
3223          return NULL;
3224      }
3225  }
3226  
3227  // The NSURL methods do not deal with escaping escape characters at all; therefore, in order to properly bridge NSURL methods, and still provide the escaping behavior that we want, we need to create functions that match the ObjC behavior exactly, and have the public CFURL... functions call these. -- REW, 10/29/98
3228  
3229  static CFStringRef  _unescapedParameterString(CFURLRef  anURL) {
3230      CFStringRef str;
3231      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
3232          str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, parameterString);
3233          if (str) CFRetain(str);
3234          return str;
3235      } 
3236      __CFGenericValidateType(anURL, CFURLGetTypeID());
3237      str = _retainedComponentString(anURL, HAS_PARAMETERS, false, false);
3238      if (str) return str;
3239      if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL;
3240      if (!anURL->_base || (anURL->_flags & (NET_LOCATION_MASK | HAS_PATH | HAS_SCHEME))) {
3241          return NULL;
3242          // Parameter string definitely coming from the relative portion of the URL
3243      }
3244      return _unescapedParameterString( anURL->_base);
3245  }
3246  
3247  CFStringRef  CFURLCopyParameterString(CFURLRef  anURL, CFStringRef charactersToLeaveEscaped) {
3248      CFStringRef  param = _unescapedParameterString(anURL);
3249      if (param) {
3250          CFStringRef result;
3251          if (anURL->_encoding == kCFStringEncodingUTF8) {
3252              result = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), param, charactersToLeaveEscaped);
3253          } else {
3254              result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), param, charactersToLeaveEscaped, anURL->_encoding);
3255          }
3256          CFRelease(param);
3257          return result;
3258      }
3259      return NULL;
3260  }
3261  
3262  static CFStringRef  _unescapedQueryString(CFURLRef  anURL) {
3263      CFStringRef str;
3264      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
3265          str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, query);
3266          if (str) CFRetain(str);
3267          return str;
3268      } 
3269      __CFGenericValidateType(anURL, CFURLGetTypeID());
3270      str = _retainedComponentString(anURL, HAS_QUERY, false, false);
3271      if (str) return str;
3272      if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL;
3273      if (!anURL->_base || (anURL->_flags & (HAS_SCHEME | NET_LOCATION_MASK | HAS_PATH | HAS_PARAMETERS))) {
3274          return NULL;
3275      }
3276      return _unescapedQueryString(anURL->_base);
3277  }
3278  
3279  CFStringRef  CFURLCopyQueryString(CFURLRef  anURL, CFStringRef  charactersToLeaveEscaped) {
3280      CFStringRef  query = _unescapedQueryString(anURL);
3281      if (query) {
3282          CFStringRef tmp;
3283          if (anURL->_encoding == kCFStringEncodingUTF8) {
3284              tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), query, charactersToLeaveEscaped);
3285          } else {
3286              tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), query, charactersToLeaveEscaped, anURL->_encoding);
3287          }
3288          CFRelease(query);
3289          return tmp;
3290      }
3291      return NULL;
3292  }
3293  
3294  // Fragments are NEVER taken from a base URL
3295  static CFStringRef  _unescapedFragment(CFURLRef  anURL) {
3296      CFStringRef str;
3297      if (CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
3298          str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, fragment);
3299          if (str) CFRetain(str);
3300          return str;
3301      } 
3302      __CFGenericValidateType(anURL, CFURLGetTypeID());
3303      str = _retainedComponentString(anURL, HAS_FRAGMENT, false, false);
3304      return str;
3305  }
3306  
3307  CFStringRef  CFURLCopyFragment(CFURLRef  anURL, CFStringRef  charactersToLeaveEscaped) {
3308      CFStringRef  fragment = _unescapedFragment(anURL);
3309      if (fragment) {
3310          CFStringRef tmp;
3311          if (anURL->_encoding == kCFStringEncodingUTF8) {
3312              tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped);
3313          } else {
3314              tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped, anURL->_encoding);
3315          }
3316          CFRelease(fragment);
3317          return tmp;
3318      }
3319      return NULL;
3320  }
3321  
3322  static CFIndex insertionLocationForMask(CFURLRef url, CFOptionFlags mask) {
3323      CFIndex firstMaskFlag = 1;
3324      CFIndex lastComponentBeforeMask = 0;
3325      while (firstMaskFlag <= HAS_FRAGMENT) {
3326          if (firstMaskFlag & mask) break;
3327          if (url->_flags & firstMaskFlag) lastComponentBeforeMask = firstMaskFlag;
3328          firstMaskFlag = firstMaskFlag << 1;
3329      }
3330      if (lastComponentBeforeMask == 0) {
3331          // mask includes HAS_SCHEME
3332          return 0;
3333      } else if (lastComponentBeforeMask == HAS_SCHEME) {
3334          // Do not have to worry about the non-decomposable case here.  However, we must be prepared for the degenerate
3335          // case file:/path/immediately/without/host
3336          CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME);
3337          CFRange pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
3338          if (schemeRg.length + 1 == pathRg.location) {
3339              return schemeRg.length + 1;
3340          } else {
3341              return schemeRg.length + 3;
3342          }
3343      } else {
3344          // For all other components, the separator precedes the component, so there's no need
3345          // to add extra chars to get to the next insertion point
3346          CFRange rg = _rangeForComponent(url->_flags, url->_ranges, lastComponentBeforeMask);
3347          return rg.location + rg.length;
3348      }
3349  }
3350  
3351  static CFRange _CFURLGetCharRangeForMask(CFURLRef url, CFOptionFlags mask, CFRange *charRangeWithSeparators) {
3352      CFOptionFlags currentOption;
3353      CFOptionFlags firstMaskFlag = HAS_SCHEME;
3354      Boolean haveReachedMask = false;
3355      CFIndex beforeMask = 0;
3356      CFIndex afterMask = kCFNotFound;
3357      const CFRange *currRange = url->_ranges;
3358      CFRange maskRange = {kCFNotFound, 0};
3359      for (currentOption = 1; currentOption <= HAS_FRAGMENT; currentOption = currentOption << 1) {
3360          if (!haveReachedMask && (currentOption & mask) != 0) {
3361              firstMaskFlag = currentOption;
3362              haveReachedMask = true;
3363          }
3364          if (!(url->_flags & currentOption)) continue;
3365          if (!haveReachedMask) {
3366              beforeMask = currRange->location + currRange->length;
3367          } else if (currentOption <= mask) {
3368              if (maskRange.location == kCFNotFound) {
3369                  maskRange = *currRange;
3370              } else {
3371                  maskRange.length = currRange->location + currRange->length - maskRange.location;
3372              }
3373          } else {
3374              afterMask = currRange->location;
3375              break;
3376          }
3377          currRange ++;
3378      }
3379      if (afterMask == kCFNotFound) {
3380          afterMask = maskRange.location + maskRange.length;
3381      }
3382      charRangeWithSeparators->location = beforeMask;
3383      charRangeWithSeparators->length = afterMask - beforeMask;
3384      return maskRange;
3385  }
3386  
3387  static CFRange _getCharRangeInDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
3388      CFOptionFlags mask;
3389      switch (component) {
3390          case kCFURLComponentScheme: 
3391              mask = HAS_SCHEME; 
3392              break;
3393          case kCFURLComponentNetLocation: 
3394              mask = NET_LOCATION_MASK; 
3395              break;
3396          case kCFURLComponentPath: 
3397              mask = HAS_PATH; 
3398              break;
3399          case kCFURLComponentResourceSpecifier: 
3400              mask = RESOURCE_SPECIFIER_MASK; 
3401              break;
3402          case kCFURLComponentUser: 
3403              mask = HAS_USER; 
3404              break;
3405          case kCFURLComponentPassword:
3406              mask = HAS_PASSWORD;
3407              break;
3408          case kCFURLComponentUserInfo:
3409              mask = HAS_USER | HAS_PASSWORD;
3410              break;
3411          case kCFURLComponentHost:
3412              mask = HAS_HOST;
3413              break;
3414          case kCFURLComponentPort:
3415              mask = HAS_PORT;
3416              break;
3417          case kCFURLComponentParameterString:
3418              mask = HAS_PARAMETERS;
3419              break;
3420          case kCFURLComponentQuery:
3421              mask = HAS_QUERY;
3422              break;
3423          case kCFURLComponentFragment:
3424              mask = HAS_FRAGMENT;
3425              break;
3426          default:
3427              rangeIncludingSeparators->location = kCFNotFound;
3428              rangeIncludingSeparators->length = 0;
3429              return CFRangeMake(kCFNotFound, 0);
3430      }
3431  
3432      if ((url->_flags & mask) == 0) {
3433          rangeIncludingSeparators->location = insertionLocationForMask(url, mask);
3434          rangeIncludingSeparators->length = 0;
3435          return CFRangeMake(kCFNotFound, 0);
3436      } else {
3437          return _CFURLGetCharRangeForMask(url, mask, rangeIncludingSeparators);
3438      }
3439  }
3440  
3441  static CFRange _getCharRangeInNonDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
3442      if (component == kCFURLComponentScheme) {
3443          CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME);
3444          rangeIncludingSeparators->location = 0;
3445          rangeIncludingSeparators->length = schemeRg.length + 1;
3446          return schemeRg;
3447      } else if (component == kCFURLComponentResourceSpecifier) {
3448          CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME);
3449          CFIndex stringLength = CFStringGetLength(url->_string);
3450          if (schemeRg.length + 1 == stringLength) {
3451              rangeIncludingSeparators->location = schemeRg.length + 1;
3452              rangeIncludingSeparators->length = 0;
3453              return CFRangeMake(kCFNotFound, 0);
3454          } else {
3455              rangeIncludingSeparators->location = schemeRg.length;
3456              rangeIncludingSeparators->length = stringLength - schemeRg.length;
3457              return CFRangeMake(schemeRg.length + 1, rangeIncludingSeparators->length - 1);
3458          }
3459      } else {
3460          rangeIncludingSeparators->location = kCFNotFound;
3461          rangeIncludingSeparators->length = 0;
3462          return CFRangeMake(kCFNotFound, 0);
3463      }
3464      
3465  }
3466  
3467  CFRange CFURLGetByteRangeForComponent(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
3468      CFRange charRange, charRangeWithSeparators;
3469      CFRange byteRange;
3470      CFAssert2(component > 0 && component < 13, __kCFLogAssertion, "%s(): passed invalid component %d", __PRETTY_FUNCTION__, component);
3471      url = _CFURLFromNSURL(url);
3472  
3473      if (!(url->_flags & IS_DECOMPOSABLE)) {
3474          // Special-case this because non-decomposable URLs have a slightly strange flags setup
3475          charRange = _getCharRangeInNonDecomposableURL(url, component, &charRangeWithSeparators);
3476      } else {
3477          charRange = _getCharRangeInDecomposableURL(url, component, &charRangeWithSeparators);
3478      }
3479      
3480      if (charRangeWithSeparators.location == kCFNotFound) {
3481          if (rangeIncludingSeparators) {
3482              rangeIncludingSeparators->location = kCFNotFound;
3483              rangeIncludingSeparators->length = 0;
3484          }
3485          return CFRangeMake(kCFNotFound, 0);
3486      } else if (rangeIncludingSeparators) {
3487          CFStringGetBytes(url->_string, CFRangeMake(0, charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->location));
3488  
3489          if (charRange.location == kCFNotFound) {
3490              byteRange = charRange;
3491              CFStringGetBytes(url->_string, charRangeWithSeparators, url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->length));
3492          } else {
3493              CFIndex maxCharRange = charRange.location + charRange.length;
3494              CFIndex maxCharRangeWithSeparators = charRangeWithSeparators.location + charRangeWithSeparators.length;
3495  
3496              if (charRangeWithSeparators.location == charRange.location) {
3497                  byteRange.location = rangeIncludingSeparators->location;
3498              } else {
3499                  CFIndex numBytes;
3500                  CFStringGetBytes(url->_string, CFRangeMake(charRangeWithSeparators.location, charRange.location - charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &numBytes);
3501                  byteRange.location = charRangeWithSeparators.location + numBytes;
3502              }
3503              CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length));
3504              if (maxCharRangeWithSeparators == maxCharRange) {
3505                  rangeIncludingSeparators->length = byteRange.location + byteRange.length - rangeIncludingSeparators->location;
3506              } else {
3507                  CFIndex numBytes;
3508                  CFRange rg;
3509                  rg.location = maxCharRange;
3510                  rg.length = maxCharRangeWithSeparators - rg.location;
3511                  CFStringGetBytes(url->_string, rg, url->_encoding, 0, false, NULL, 0, &numBytes);
3512                  rangeIncludingSeparators->length = byteRange.location + byteRange.length + numBytes - rangeIncludingSeparators->location;
3513              }
3514          }
3515      } else if (charRange.location == kCFNotFound) {
3516          byteRange = charRange;
3517      } else {
3518          CFStringGetBytes(url->_string, CFRangeMake(0, charRange.location), url->_encoding, 0, false, NULL, 0, &(byteRange.location));
3519          CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length));
3520      }
3521      return byteRange;
3522  }
3523  
3524  /* Component support */
3525  
3526  static Boolean decomposeToNonHierarchical(CFURLRef url, CFURLComponentsNonHierarchical *components) {
3527      if ( CFURLGetBaseURL(url) != NULL)  {
3528          components->scheme = NULL;
3529      } else {
3530          components->scheme = CFURLCopyScheme(url);
3531      }
3532      components->schemeSpecific = CFURLCopyResourceSpecifier(url);
3533      return true;
3534  }
3535  
3536  static CFURLRef composeFromNonHierarchical(CFAllocatorRef alloc, const CFURLComponentsNonHierarchical *components) {
3537      CFStringRef str;
3538      if (components->scheme) {
3539          UniChar ch = ':';
3540          str = CFStringCreateMutableCopy(alloc, CFStringGetLength(components->scheme) + 1 + (components->schemeSpecific ? CFStringGetLength(components->schemeSpecific): 0), components->scheme);
3541          CFStringAppendCharacters((CFMutableStringRef)str, &ch, 1);
3542          if (components->schemeSpecific) CFStringAppend((CFMutableStringRef)str, components->schemeSpecific);
3543      } else if (components->schemeSpecific) {
3544          str = components->schemeSpecific;
3545          CFRetain(str);
3546      } else {
3547          str = NULL;
3548      }
3549      if (str) {
3550          CFURLRef url = CFURLCreateWithString(alloc, str, NULL);
3551          CFRelease(str);
3552          return url;
3553      } else {
3554          return NULL;
3555      }
3556  }
3557  
3558  static Boolean decomposeToRFC1808(CFURLRef url, CFURLComponentsRFC1808 *components) {
3559      CFAllocatorRef alloc = CFGetAllocator(url);
3560      static CFStringRef emptyStr = NULL;
3561      if (!emptyStr) {
3562          emptyStr = CFSTR("");
3563      }
3564  
3565      if (!CFURLCanBeDecomposed(url)) {
3566          return false;
3567      }
3568      
3569      CFStringRef path = CFURLCopyPath(url);
3570      if (path) {
3571          components->pathComponents = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("/"));
3572          CFRelease(path);
3573      } else {
3574          components->pathComponents = NULL;
3575      }
3576      components->baseURL = CFURLGetBaseURL(url);
3577      if (components->baseURL)  {
3578          CFRetain(components->baseURL);
3579          components->scheme = NULL;
3580      } else {
3581          components->scheme = _retainedComponentString(url, HAS_SCHEME, true, false);
3582      }
3583      components->user = _retainedComponentString(url, HAS_USER, false, false);
3584      components->password = _retainedComponentString(url, HAS_PASSWORD, false, false);
3585      components->host = _retainedComponentString(url, HAS_HOST, false, false);
3586      if (url->_flags & HAS_PORT) {
3587          components->port = CFURLGetPortNumber(url);
3588      } else {
3589          components->port = kCFNotFound;
3590      }
3591      components->parameterString = _retainedComponentString(url, HAS_PARAMETERS, false, false);
3592      components->query = _retainedComponentString(url, HAS_QUERY, false, false);
3593      components->fragment = _retainedComponentString(url, HAS_FRAGMENT, false, false);
3594      return true;
3595  }
3596  
3597  static CFURLRef composeFromRFC1808(CFAllocatorRef alloc, const CFURLComponentsRFC1808 *comp) {
3598      CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0);
3599      CFURLRef base = comp->baseURL;
3600      CFURLRef url;
3601      Boolean hadPrePathComponent = false;
3602      if (comp->scheme) {
3603          base = NULL;
3604          CFStringAppend(urlString, comp->scheme);
3605          CFStringAppend(urlString, CFSTR("://"));
3606          hadPrePathComponent = true;
3607      }
3608      if (comp->user || comp->password) {
3609          if (comp->user) {
3610              CFStringAppend(urlString, comp->user);
3611          }
3612          if (comp->password) {
3613              CFStringAppend(urlString, CFSTR(":"));
3614              CFStringAppend(urlString, comp->password);
3615          }
3616          CFStringAppend(urlString, CFSTR("@"));
3617          hadPrePathComponent = true;
3618      }
3619      if (comp->host) {
3620          CFStringAppend(urlString, comp->host);
3621          hadPrePathComponent = true;
3622      }
3623      if (comp->port != kCFNotFound) {
3624          CFStringAppendFormat(urlString, NULL, CFSTR(":%ld"), (long)comp->port);
3625          hadPrePathComponent = true;
3626      }
3627  
3628      if (hadPrePathComponent && (comp->pathComponents == NULL || CFArrayGetCount( comp->pathComponents ) == 0 || CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) {
3629          CFStringAppend(urlString, CFSTR("/"));
3630      }
3631      if (comp->pathComponents) {
3632          CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/"));
3633          CFStringAppend(urlString, pathStr);
3634          CFRelease(pathStr);
3635      }
3636      if (comp->parameterString) {
3637          CFStringAppend(urlString, CFSTR(";"));
3638          CFStringAppend(urlString, comp->parameterString);
3639      }
3640      if (comp->query) {
3641          CFStringAppend(urlString, CFSTR("?"));
3642          CFStringAppend(urlString, comp->query);
3643      }
3644      if (comp->fragment) {
3645          CFStringAppend(urlString, CFSTR("#"));
3646          CFStringAppend(urlString, comp->fragment);
3647      }
3648      url = CFURLCreateWithString(alloc, urlString, base);
3649      CFRelease(urlString);
3650      return url;
3651  }
3652  
3653  static Boolean decomposeToRFC2396(CFURLRef url, CFURLComponentsRFC2396 *comp) {
3654      CFAllocatorRef alloc = CFGetAllocator(url);
3655      CFURLComponentsRFC1808 oldComp;
3656      CFStringRef tmpStr;
3657      if (!decomposeToRFC1808(url, &oldComp)) {
3658          return false;
3659      }
3660      comp->scheme = oldComp.scheme;
3661      if (oldComp.user) {
3662          if (oldComp.password) {
3663              comp->userinfo = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@:%@"), oldComp.user, oldComp.password);
3664              CFRelease(oldComp.password);
3665              CFRelease(oldComp.user);
3666          } else {
3667              comp->userinfo = oldComp.user;
3668          }
3669      } else {
3670          comp->userinfo = NULL;
3671      }
3672      comp->host = oldComp.host;
3673      comp->port = oldComp.port;
3674      if (!oldComp.parameterString) {
3675          comp->pathComponents = oldComp.pathComponents;
3676      } else {
3677          int length = CFArrayGetCount(oldComp.pathComponents);
3678          comp->pathComponents = CFArrayCreateMutableCopy(alloc, length, oldComp.pathComponents);
3679          tmpStr = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp->pathComponents, length - 1), oldComp.parameterString);
3680          CFArraySetValueAtIndex((CFMutableArrayRef)comp->pathComponents, length - 1, tmpStr);
3681          CFRelease(tmpStr);
3682          CFRelease(oldComp.pathComponents);
3683          CFRelease(oldComp.parameterString);
3684      }
3685      comp->query = oldComp.query;
3686      comp->fragment = oldComp.fragment;
3687      comp->baseURL = oldComp.baseURL;
3688      return true;
3689  }
3690  
3691  static CFURLRef composeFromRFC2396(CFAllocatorRef alloc, const CFURLComponentsRFC2396 *comp) {
3692      CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0);
3693      CFURLRef base = comp->baseURL;
3694      CFURLRef url;
3695      Boolean hadPrePathComponent = false;
3696      if (comp->scheme) {
3697          base = NULL;
3698          CFStringAppend(urlString, comp->scheme);
3699          CFStringAppend(urlString, CFSTR("://"));
3700          hadPrePathComponent = true;
3701      }
3702      if (comp->userinfo) {
3703          CFStringAppend(urlString, comp->userinfo);
3704          CFStringAppend(urlString, CFSTR("@"));
3705          hadPrePathComponent = true;
3706      }
3707      if (comp->host) {
3708          CFStringAppend(urlString, comp->host);
3709          if (comp->port != kCFNotFound) {
3710              CFStringAppendFormat(urlString, NULL, CFSTR(":%ld"), (long)comp->port);
3711          }
3712          hadPrePathComponent = true;
3713      }
3714      if (hadPrePathComponent && (comp->pathComponents == NULL || CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) {
3715          CFStringAppend(urlString, CFSTR("/"));
3716      }
3717      if (comp->pathComponents) {
3718          CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/"));
3719          CFStringAppend(urlString, pathStr);
3720          CFRelease(pathStr);
3721      }
3722      if (comp->query) {
3723          CFStringAppend(urlString, CFSTR("?"));
3724          CFStringAppend(urlString, comp->query);
3725      }
3726      if (comp->fragment) {
3727          CFStringAppend(urlString, CFSTR("#"));
3728          CFStringAppend(urlString, comp->fragment);
3729      }
3730      url = CFURLCreateWithString(alloc, urlString, base);
3731      CFRelease(urlString);
3732      return url;
3733  }
3734  
3735  #undef CFURLCopyComponents
3736  #undef CFURLCreateFromComponents
3737  
3738  CF_EXPORT
3739  Boolean _CFURLCopyComponents(CFURLRef url, CFURLComponentDecomposition decompositionType, void *components) {
3740      url = _CFURLFromNSURL(url);
3741      switch (decompositionType) {
3742      case kCFURLComponentDecompositionNonHierarchical:
3743          return decomposeToNonHierarchical(url, (CFURLComponentsNonHierarchical *)components);
3744      case kCFURLComponentDecompositionRFC1808:
3745          return decomposeToRFC1808(url, (CFURLComponentsRFC1808 *)components);
3746      case kCFURLComponentDecompositionRFC2396:
3747          return decomposeToRFC2396(url, (CFURLComponentsRFC2396 *)components);
3748      default:
3749          return false;
3750      }
3751  }
3752  
3753  CF_EXPORT
3754  CFURLRef _CFURLCreateFromComponents(CFAllocatorRef alloc, CFURLComponentDecomposition decompositionType, const void *components) {
3755      switch (decompositionType) {
3756      case kCFURLComponentDecompositionNonHierarchical:
3757          return composeFromNonHierarchical(alloc, (const CFURLComponentsNonHierarchical *)components);
3758      case kCFURLComponentDecompositionRFC1808:
3759          return composeFromRFC1808(alloc, (const CFURLComponentsRFC1808 *)components);
3760      case kCFURLComponentDecompositionRFC2396:
3761          return composeFromRFC2396(alloc, (const CFURLComponentsRFC2396 *)components);
3762      default:
3763          return NULL;
3764      }
3765  }
3766  
3767  CF_EXPORT void *__CFURLReservedPtr(CFURLRef  url) {
3768      // called with CFURL (not NSURL) from Foundation
3769      return _getReserved(url);
3770  }
3771  
3772  CF_EXPORT void __CFURLSetReservedPtr(CFURLRef  url, void *ptr) {
3773      // called with CFURL (not NSURL) from Foundation
3774      _setReserved ( (struct __CFURL*) url, ptr );
3775  }
3776  
3777  CF_EXPORT void *__CFURLResourceInfoPtr(CFURLRef url) {
3778      if ( url ) {
3779          url = _CFURLFromNSURL(url);
3780          return _getResourceInfo(url);
3781      }
3782      else {
3783          return NULL;
3784      }
3785  }
3786  
3787  CF_EXPORT void __CFURLSetResourceInfoPtr(CFURLRef url, void *ptr) {
3788      if ( url ) {
3789          url = _CFURLFromNSURL(url);
3790          _setResourceInfo ( (struct __CFURL*) url, ptr );
3791      }
3792  }
3793  
3794  /* File system stuff */
3795  
3796  /* HFSPath<->URLPath functions at the bottom of the file */
3797  static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) {
3798      CFArrayRef tmp;
3799      CFMutableArrayRef urlComponents = NULL;
3800      CFIndex i=0;
3801  
3802      tmp = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("\\"));
3803      urlComponents = CFArrayCreateMutableCopy(alloc, 0, tmp);
3804      CFRelease(tmp);
3805  
3806      CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(urlComponents, 0);
3807      if (isAbsolute && CFStringGetLength(str) == 2 && CFStringGetCharacterAtIndex(str, 1) == ':') {
3808          i = 1; // Skip over the drive letter 
3809      }
3810      CFIndex c;
3811      for (c = CFArrayGetCount(urlComponents); i < c; i ++) {
3812          CFStringRef fileComp = (CFStringRef)CFArrayGetValueAtIndex(urlComponents,i);
3813          CFStringRef urlComp = _replacePathIllegalCharacters(fileComp, alloc, false);
3814          if (!urlComp) {
3815              // Couldn't decode fileComp
3816              CFRelease(urlComponents);
3817              return NULL;
3818          }
3819          if (urlComp != fileComp) {
3820              CFArraySetValueAtIndex(urlComponents, i, urlComp);
3821          }
3822          CFRelease(urlComp);
3823      }
3824  
3825      if (isDir) {
3826          if (CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(urlComponents, CFArrayGetCount(urlComponents) - 1)) != 0)
3827              CFArrayAppendValue(urlComponents, CFSTR(""));
3828      }
3829      if (isAbsolute) {
3830          if ( AddAuthorityToFileURL() ) {
3831              CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY));
3832          }
3833          else {
3834              CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR(FILE_PREFIX));
3835          }
3836      }
3837      return urlComponents;
3838  }
3839  
3840  static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) {
3841      CFArrayRef urlComponents;
3842      CFStringRef str;
3843  
3844      if (CFStringGetLength(path) == 0) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII);
3845      urlComponents = WindowsPathToURLComponents(path, alloc, isDir, isAbsolute);
3846      if (!urlComponents) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII);
3847  
3848      // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3849      str = CFStringCreateByCombiningStrings(alloc, urlComponents, CFSTR("/"));
3850      CFRelease(urlComponents);
3851      return str;
3852  }
3853  
3854  static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory, Boolean isAbsolute, Boolean *posixAndUrlPathsMatch) {
3855      Boolean addedPercentEncoding;
3856      CFStringRef pathString = NULL;
3857      STACK_BUFFER_DECL(char, buffer, PATH_MAX);
3858      if ( CFStringGetFileSystemRepresentation(path, buffer, PATH_MAX) ) {
3859          pathString = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault, (const UInt8 *)buffer, strlen(buffer), isDirectory, isAbsolute, false /* windowsPath */, &addedPercentEncoding);
3860      }
3861      
3862      if ( posixAndUrlPathsMatch ) {
3863          *posixAndUrlPathsMatch = !addedPercentEncoding;
3864      }
3865      return pathString;
3866  }
3867  
3868  static CFStringRef URLPathToPOSIXPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) {
3869      // This is the easiest case; just remove the percent escape codes and we're done
3870      CFStringRef result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, path, CFSTR(""), encoding);
3871      if (result) {
3872          CFIndex length = CFStringGetLength(result);
3873          if (length > 1 && CFStringGetCharacterAtIndex(result, length-1) == '/') {
3874              CFStringRef tmp = CFStringCreateWithSubstring(allocator, result, CFRangeMake(0, length-1));
3875              CFRelease(result);
3876              result = tmp;
3877          }
3878      }
3879      return result;
3880  }
3881  
3882  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
3883  static Boolean CanonicalFileURLStringToFileSystemRepresentation(CFStringRef str, UInt8 *inBuffer, CFIndex inBufferLen)
3884  {
3885      size_t fileURLPrefixLength;
3886      if ( AddAuthorityToFileURL() ) {
3887          fileURLPrefixLength = sizeof(fileURLPrefixWithAuthority);
3888      }
3889      else {
3890          fileURLPrefixLength = sizeof(fileURLPrefix);
3891      }
3892      Boolean result;
3893      if ( inBuffer && inBufferLen ) {
3894          STACK_BUFFER_DECL(UInt8, stackEscapedBuf, PATH_MAX * 3);    // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3895          UInt8 *escapedBuf;
3896          CFIndex strLength = CFStringGetLength(str) - (fileURLPrefixLength - 1);
3897          if ( strLength != 0 ) {
3898              CFIndex maxBufLength = strLength * 3;
3899              CFIndex usedBufLen;
3900              CFIndex charsConverted;
3901              if ( strLength <= PATH_MAX ) {
3902                  escapedBuf = &stackEscapedBuf[0];
3903              }
3904              else {
3905                  // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3906                  escapedBuf = (UInt8 *)malloc(maxBufLength);
3907              }
3908              if ( escapedBuf != NULL ) {
3909                  charsConverted = CFStringGetBytes(str, CFRangeMake(fileURLPrefixLength - 1, strLength), kCFStringEncodingUTF8, 0, false, escapedBuf, maxBufLength, &usedBufLen);
3910                  if ( charsConverted ) {
3911                      static const UInt8 hexvalues[] = {
3912                          /* 00 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3913                          /* 08 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3914                          /* 10 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3915                          /* 18 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3916                          /* 20 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3917                          /* 28 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3918                          /* 30 */  0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
3919                          /* 38 */  0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3920                          /* 40 */  0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3921                          /* 48 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3922                          /* 50 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3923                          /* 58 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3924                          /* 60 */  0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3925                          /* 68 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3926                          /* 70 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3927                          /* 78 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3928                          
3929                          /* 80 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3930                          /* 88 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3931                          /* 90 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3932                          /* 98 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3933                          /* A0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3934                          /* A8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3935                          /* B0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3936                          /* B8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3937                          /* C0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3938                          /* C8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3939                          /* D0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3940                          /* D8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3941                          /* E0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3942                          /* E8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3943                          /* F0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3944                          /* F8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3945                      };
3946                      UInt8 *bufStartPtr;
3947                      UInt8 *bufEndPtr;
3948                      UInt8 *bufPtr;
3949                      const UInt8 *bytePtr = escapedBuf;
3950                      CFIndex idx;
3951                      Boolean trailingSlash = false;
3952                      
3953                      bufPtr = bufStartPtr = inBuffer;
3954                      bufEndPtr = inBuffer + inBufferLen;
3955                      result = TRUE;
3956                      
3957                      for ( idx = 0; (idx < usedBufLen) && result; ++idx ) {
3958                          if ( bufPtr == bufEndPtr ) {
3959                              // ooops, ran out of inBuffer
3960                              *bufStartPtr = '\0';
3961                              result = FALSE;
3962                          }
3963                          else {
3964                              switch ( *bytePtr ) {
3965                                  case '%':
3966                                      idx += 2;
3967                                      if ( idx < usedBufLen ) {
3968                                          // skip over %
3969                                          bytePtr++;
3970                                          // convert hex digits
3971                                          *bufPtr = hexvalues[*bytePtr++] << 4;
3972                                          *bufPtr += hexvalues[*bytePtr++];
3973                                          trailingSlash = (*bufPtr == '/');
3974                                      }
3975                                      break;
3976                                  default:
3977                                      // copy everything else
3978                                      *bufPtr = *bytePtr++;
3979                                      trailingSlash = (*bufPtr == '/');
3980                                      break;
3981                              }
3982                              ++bufPtr;
3983                          }
3984                      }
3985                      if ( result ) {
3986                          // remove trailing slash (if any)
3987                          if ( (bufPtr > (bufStartPtr + 1)) && trailingSlash ) {
3988                              --bufPtr;
3989                          }
3990                          if ( bufPtr < bufEndPtr ) {
3991                              *bufPtr = '\0';
3992                          }
3993                      }
3994                  }
3995                  else {
3996                      // CFStringGetBytes failed
3997                      result = FALSE;
3998                  }
3999                  
4000                  // free the buffer if we malloc'd it
4001                  if ( escapedBuf != &stackEscapedBuf[0] ) {
4002                      free(escapedBuf);
4003                  }
4004              }
4005              else {
4006                  // could not allocate escapedBuf
4007                  result = FALSE;
4008              }
4009          }
4010          else {
4011              // str was zero characters
4012              *inBuffer = '\0';
4013              result = TRUE;
4014          }
4015      }
4016      else {
4017          // no inBuffer or inBufferLen is zero
4018          result = FALSE;
4019      }
4020      
4021      return ( result );
4022  }
4023  #endif
4024  
4025  #if DEPLOYMENT_TARGET_WINDOWS
4026  // From CFPlatform.c
4027  extern CFStringRef CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr);
4028  #endif
4029  
4030  static CFStringRef URLPathToWindowsPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) {
4031      // Check for a drive letter, then flip all the slashes
4032      CFStringRef result;
4033      CFArrayRef tmp = CFStringCreateArrayBySeparatingStrings(allocator, path, CFSTR("/"));
4034      SInt32 count = CFArrayGetCount(tmp);
4035      CFMutableArrayRef components = CFArrayCreateMutableCopy(allocator, count, tmp);
4036      CFStringRef newPath;
4037      
4038      
4039      
4040      CFRelease(tmp);
4041      if (CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(components,count-1)) == 0) {
4042          CFArrayRemoveValueAtIndex(components, count-1);
4043          count --;
4044      }
4045      
4046      if (count > 1 && CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(components, 0)) == 0) {
4047          // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
4048          CFStringRef firstComponent = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, (CFStringRef)CFArrayGetValueAtIndex(components, 1), CFSTR(""), encoding);
4049          UniChar ch;
4050  
4051  	{
4052              if (firstComponent) {
4053  		if (CFStringGetLength(firstComponent) == 2 && ((ch = CFStringGetCharacterAtIndex(firstComponent, 1)) == '|' || ch == ':')) {
4054  		    // Drive letter
4055  		    CFArrayRemoveValueAtIndex(components, 0);
4056  		    if (ch == '|') {
4057  			CFStringRef driveStr = CFStringCreateWithFormat(allocator, NULL, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent, 0));
4058  			CFArraySetValueAtIndex(components, 0, driveStr);
4059  			CFRelease(driveStr);
4060  		    }
4061  		}
4062  #if DEPLOYMENT_TARGET_WINDOWS
4063  		else {
4064  		    // From <rdar://problem/5623405> [DEFECT] CFURL returns a Windows path that contains volume name instead of a drive letter
4065  		    // we need to replace the volume name (it is not valid on Windows) with the drive mounting point path
4066  		    // remove the first component and set the component with the drive letter to be the first component
4067  		    CFStringRef driveRootPath = CFCreateWindowsDrivePathFromVolumeName(firstComponent);
4068  		    
4069  		    if (driveRootPath) {
4070  			// remove trailing slash
4071  			if (CFStringHasSuffix(driveRootPath, CFSTR("\\"))) {
4072  			    CFStringRef newDriveRootPath = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, driveRootPath, CFRangeMake(0, CFStringGetLength(driveRootPath) - 1));
4073  			    CFRelease(driveRootPath);
4074  			    driveRootPath = newDriveRootPath;
4075  			}
4076  			
4077  			// replace the first component of the path with the drive path
4078  			CFArrayRemoveValueAtIndex(components, 0);
4079  			CFArraySetValueAtIndex(components, 0, driveRootPath);
4080  			
4081  			CFRelease(driveRootPath);
4082  		    }
4083  		}
4084  #endif
4085  	    }
4086          }
4087          if ( firstComponent ) {
4088              CFRelease(firstComponent);
4089          }
4090      }
4091      newPath = CFStringCreateByCombiningStrings(allocator, components, CFSTR("\\"));
4092      CFRelease(components);
4093      result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, newPath, CFSTR(""), encoding);
4094      CFRelease(newPath);
4095      return result;
4096  }
4097  
4098  
4099  
4100  // Caller must release the returned string
4101  static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc) {
4102      CFIndex baseLen = CFStringGetLength(basePath);
4103      CFIndex relLen = CFStringGetLength(relativePath);
4104      UniChar pathDelimiter = '/';
4105      UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar)*(relLen + baseLen + 2), 0);
4106      CFStringGetCharacters(basePath, CFRangeMake(0, baseLen), buf);
4107      if (baseIsDir) {
4108          if (buf[baseLen-1] != pathDelimiter) {
4109              buf[baseLen] = pathDelimiter;
4110              baseLen ++;
4111          }
4112      } else {
4113          UniChar *ptr = buf + baseLen - 1;
4114          while (ptr > buf && *ptr != pathDelimiter) {
4115              ptr --;
4116          }
4117          baseLen = ptr - buf + 1;
4118      }
4119  #pragma GCC diagnostic push
4120  #pragma GCC diagnostic ignored "-Wdeprecated"
4121      if (fsType == kCFURLHFSPathStyle) {
4122  #pragma GCC diagnostic pop
4123          // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
4124          baseLen --;
4125      }
4126      CFStringGetCharacters(relativePath, CFRangeMake(0, relLen), buf + baseLen);
4127      *(buf + baseLen + relLen) = '\0';
4128      return _resolvedPath(buf, buf + baseLen + relLen, pathDelimiter, false, true, alloc);
4129  }
4130  
4131  CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator) {
4132      CFURLRef url = NULL;
4133      uint8_t buf[CFMaxPathSize + 1];
4134      if (_CFGetCurrentDirectory((char *)buf, CFMaxPathLength)) {
4135          url = CFURLCreateFromFileSystemRepresentation(allocator, buf, strlen((char *)buf), true);
4136      }
4137      return url;
4138  }
4139  
4140  CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory) {
4141      CFURLRef result;
4142      
4143      result = _CFURLCreateWithFileSystemPath(allocator, filePath, fsType, isDirectory, NULL);
4144      
4145      return ( result );
4146  }
4147  
4148  CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory, CFURLRef baseURL) {
4149      CFURLRef result;
4150      
4151      result = _CFURLCreateWithFileSystemPath(allocator, filePath, fsType, isDirectory, baseURL);
4152  
4153      return ( result );
4154  }
4155  
4156  static Boolean _pathHasFileIDPrefix( CFStringRef path )
4157  {
4158      // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
4159  #ifdef __CONSTANT_STRINGS__
4160      static const 
4161  #endif
4162      CFStringRef fileIDPrefix = CFSTR( "/" FILE_ID_PREFIX "/" );
4163      return path && CFStringHasPrefix( path, fileIDPrefix ) && CFStringGetLength( path ) > CFStringGetLength( fileIDPrefix );
4164  }
4165  
4166  
4167  CF_EXPORT CFStringRef CFURLCopyFileSystemPath(CFURLRef anURL, CFURLPathStyle pathStyle) {
4168  #pragma GCC diagnostic push
4169  #pragma GCC diagnostic ignored "-Wdeprecated"
4170      CFAssert2(pathStyle == kCFURLPOSIXPathStyle || pathStyle == kCFURLHFSPathStyle || pathStyle == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__, pathStyle);
4171  #pragma GCC diagnostic pop
4172      
4173      CFStringRef result = NULL;
4174      CFAllocatorRef alloc = CFGetAllocator(anURL);
4175  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
4176      Boolean isCanonicalFileURL = false;
4177      
4178      if ( (pathStyle == kCFURLPOSIXPathStyle) && (CFURLGetBaseURL(anURL) == NULL) ) {
4179          if ( !CF_IS_OBJC(CFURLGetTypeID(), anURL) ) {
4180              // We can grope the ivars
4181              isCanonicalFileURL = ((anURL->_flags & IS_CANONICAL_FILE_URL) != 0);
4182              if ( isCanonicalFileURL ) {
4183                  STACK_BUFFER_DECL(UInt8, buffer, PATH_MAX + 1);
4184                  if ( CanonicalFileURLStringToFileSystemRepresentation(anURL->_string, buffer, PATH_MAX + 1) ) {
4185                      result = CFStringCreateWithBytes(alloc, buffer, strlen((char *)buffer), kCFStringEncodingUTF8, false);
4186                  }
4187              }
4188          }
4189      }
4190      if ( ! result ) {
4191          // fall back to slower way.
4192          result = CFURLCreateStringWithFileSystemPath(alloc, anURL, pathStyle, false);
4193      }
4194  #else // !DEPLOYMENT_TARGET_MACOSX
4195      result = CFURLCreateStringWithFileSystemPath(alloc, anURL, pathStyle, false);
4196  #endif // !DEPLOYMENT_TARGET_MACOSX
4197      
4198      return ( result );
4199  }
4200  
4201  
4202  // There is no matching ObjC method for this functionality; because this function sits on top of the CFURL primitives, it's o.k. not to check for the need to dispatch an ObjC method instead, but this means care must be taken that this function never call anything that will result in dereferencing anURL without first checking for an ObjC dispatch.  -- REW, 10/29/98
4203  CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase) {
4204      CFURLRef base = resolveAgainstBase ? CFURLGetBaseURL(anURL) : NULL;
4205      CFStringRef basePath = base ? CFURLCreateStringWithFileSystemPath(allocator, base, fsType, false) : NULL;
4206      CFStringRef relPath = NULL;
4207      
4208      if (!CF_IS_OBJC(CFURLGetTypeID(), anURL)) {
4209          // We can grope the ivars
4210          if (fsType == kCFURLPOSIXPathStyle) {
4211              if (anURL->_flags & POSIX_AND_URL_PATHS_MATCH) {
4212                  relPath = _retainedComponentString(anURL, HAS_PATH, true, true);
4213              }
4214          }
4215      }
4216  
4217      if (relPath == NULL) {
4218          CFStringRef urlPath = CFURLCopyPath(anURL);
4219          CFStringEncoding enc = anURL->_encoding;
4220          if (urlPath) {
4221              switch (fsType) {
4222                  case kCFURLPOSIXPathStyle:
4223                      relPath = URLPathToPOSIXPath(urlPath, allocator, enc);
4224                      break;
4225  #pragma GCC diagnostic push
4226  #pragma GCC diagnostic ignored "-Wdeprecated"
4227                  case kCFURLHFSPathStyle:
4228  #pragma GCC diagnostic pop
4229  		    relPath = NULL;
4230                      break;
4231                  case kCFURLWindowsPathStyle:
4232                      relPath = URLPathToWindowsPath(urlPath, allocator, enc);
4233                      break;
4234                  default:
4235                      CFAssert2(true, __kCFLogAssertion, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__, fsType);
4236              }
4237              CFRelease(urlPath);
4238          }            
4239      }
4240  	
4241      //	For Tiger, leave this behavior in for all path types.  For Leopard, it would be nice to remove this entirely
4242      //	and do a linked-on-or-later check so we don't break third parties.
4243      //	See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
4244      //	<rdar://problem/4018895> CF needs to back out 4003028 for icky details.
4245      if ( relPath && CFURLHasDirectoryPath(anURL) && CFStringGetLength(relPath) > 1 && CFStringGetCharacterAtIndex(relPath, CFStringGetLength(relPath)-1) == '/') {
4246          CFStringRef tmp = CFStringCreateWithSubstring(allocator, relPath, CFRangeMake(0, CFStringGetLength(relPath)-1));
4247          CFRelease(relPath);
4248          relPath = tmp;
4249      }
4250      
4251      if ( relPath ) {
4252          // relPath is not absolute if it is zero length or doesn't start with a slash
4253          Boolean relPathIsRelative = ((CFStringGetLength(relPath) != 0) ? (CFStringGetCharacterAtIndex(relPath, 0) != '/') : TRUE);
4254          if ( basePath && relPathIsRelative ) {
4255              // we have both basePath and relPath, and relPath is not absolute -- resolve them
4256              CFStringRef result = _resolveFileSystemPaths(relPath, basePath, CFURLHasDirectoryPath(base), fsType, allocator);
4257              CFRelease(basePath);
4258              CFRelease(relPath);
4259              return result;
4260          }
4261          else {
4262              // we only have the relPath or relpath is absolute -- return it
4263              if ( basePath ) {
4264                  CFRelease(basePath);
4265              }
4266              return relPath;
4267          }
4268      }
4269      else if ( basePath ) {
4270          // we only have the basePath --- return it
4271          return basePath;
4272      }
4273      else {
4274          // we have nothing to return
4275          return NULL;
4276      }
4277  }
4278  
4279  Boolean CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, uint8_t *buffer, CFIndex bufLen) {
4280  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_WINDOWS
4281      CFAllocatorRef alloc = CFGetAllocator(url);
4282      CFStringRef path;
4283  
4284      if (!url) return false;
4285  #endif
4286  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
4287      if ( !resolveAgainstBase || (CFURLGetBaseURL(url) == NULL) ) {
4288          if (!CF_IS_OBJC(CFURLGetTypeID(), url)) {
4289              // We can grope the ivars
4290              if ( url->_flags & IS_CANONICAL_FILE_URL ) {
4291                  return CanonicalFileURLStringToFileSystemRepresentation(url->_string, buffer, bufLen);
4292              }
4293          }
4294      }
4295      // else fall back to slower way.
4296      path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLPOSIXPathStyle, resolveAgainstBase);
4297      if (path) {
4298          Boolean convResult = _CFStringGetFileSystemRepresentation(path, buffer, bufLen);
4299          CFRelease(path);
4300          return convResult;
4301      }
4302  #elif DEPLOYMENT_TARGET_WINDOWS
4303      path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLWindowsPathStyle, resolveAgainstBase);
4304      if (path) {
4305          CFIndex usedLen;
4306          CFIndex pathLen = CFStringGetLength(path);
4307          CFIndex numConverted = CFStringGetBytes(path, CFRangeMake(0, pathLen), CFStringFileSystemEncoding(), 0, true, buffer, bufLen-1, &usedLen); // -1 because we need one byte to zero-terminate.
4308          CFRelease(path);
4309          if (numConverted == pathLen) {
4310              buffer[usedLen] = '\0';
4311              return true;
4312          }
4313      }
4314  #endif
4315      return false;
4316  }
4317  
4318  #if DEPLOYMENT_TARGET_WINDOWS
4319  CF_EXPORT Boolean _CFURLGetWideFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, wchar_t *buffer, CFIndex bufferLength) {
4320  	CFStringRef path = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLWindowsPathStyle, resolveAgainstBase);
4321  	CFIndex pathLength, charsConverted, usedBufLen;
4322  	if (!path) return false;
4323  	pathLength = CFStringGetLength(path);
4324  	if (pathLength+1 > bufferLength) {
4325  		CFRelease(path);
4326  		return false;
4327  	}
4328  	charsConverted = CFStringGetBytes(path, CFRangeMake(0, pathLength), kCFStringEncodingUTF16, 0, false, (UInt8 *)buffer, bufferLength*sizeof(wchar_t), &usedBufLen);
4329  //	CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer);
4330  	CFRelease(path);
4331  	if (charsConverted != pathLength || usedBufLen%sizeof(wchar_t) != 0) {
4332  		return false;
4333  	} else {
4334  		buffer[usedBufLen/sizeof(wchar_t)] = 0;
4335  //		buffer[pathLength] = 0;
4336  		return true;
4337  	}
4338  }
4339  #endif
4340  
4341  CFURLRef CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory) {
4342      CFURLRef result;
4343      
4344      result = _CFURLCreateWithFileSystemRepresentation(allocator, buffer, bufLen, isDirectory, NULL);
4345      
4346      return ( result );
4347  }
4348  
4349  CF_EXPORT CFURLRef CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL) {
4350      CFURLRef result;
4351      
4352      result = _CFURLCreateWithFileSystemRepresentation(allocator, buffer, bufLen, isDirectory, baseURL);
4353      
4354      return ( result );
4355  }
4356  
4357  
4358  /******************************/
4359  /* Support for path utilities */
4360  /******************************/
4361  
4362  // Assumes url is a CFURL (not an Obj-C NSURL)
4363  static CFRange _rangeOfLastPathComponent(CFURLRef url) {
4364      CFRange pathRg, componentRg;
4365      
4366      pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
4367  
4368      if (pathRg.location == kCFNotFound || pathRg.length == 0) {
4369          // No path
4370          return pathRg;
4371      }
4372      if (CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) == '/') {
4373          pathRg.length --;
4374          if (pathRg.length == 0) {
4375              pathRg.length ++;
4376              return pathRg;
4377          }
4378      }
4379      if (CFStringFindWithOptions(url->_string, CFSTR("/"), pathRg, kCFCompareBackwards, &componentRg)) {
4380          componentRg.location ++;
4381          componentRg.length = pathRg.location + pathRg.length - componentRg.location;
4382      } else {
4383          componentRg = pathRg;
4384      }
4385      return componentRg;
4386  }
4387  
4388  CFStringRef CFURLCopyLastPathComponent(CFURLRef url) {
4389      CFStringRef result;
4390  
4391      if (CF_IS_OBJC(CFURLGetTypeID(), url)) {
4392          CFStringRef path = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLPOSIXPathStyle, false);
4393          CFIndex length;
4394          CFRange rg, compRg;
4395          if (!path) return NULL;
4396          rg = CFRangeMake(0, CFStringGetLength(path));
4397  	if ( rg.length == 0 ) return path;
4398          length = rg.length; // Remember this for comparison later
4399          if (CFStringGetCharacterAtIndex(path, rg.length - 1) == '/' ) {
4400              rg.length --;
4401          }
4402  	if ( rg.length == 0 )
4403  	{
4404  	    //	If we have reduced the string to empty, then it's "/", and that's what we return as
4405  	    //	the last path component.
4406  	   return path;
4407  	}
4408          else if (CFStringFindWithOptions(path, CFSTR("/"), rg, kCFCompareBackwards, &compRg)) {
4409              rg.length = rg.location + rg.length - (compRg.location+1);
4410              rg.location = compRg.location + 1;
4411          }
4412          if (rg.location == 0 && rg.length == length) {
4413              result = path;
4414          } else {
4415              result = CFStringCreateWithSubstring(CFGetAllocator(url), path, rg);
4416              CFRelease(path);
4417          }
4418      } else {
4419          Boolean filePathURLCreated = false;
4420  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4421          if ( CFURLIsFileReferenceURL(url) ) {
4422              // use a file path URL or fail
4423              CFURLRef filePathURL = CFURLCreateFilePathURL(CFGetAllocator(url), url, NULL);
4424              if ( filePathURL ) {
4425                  filePathURLCreated = TRUE;
4426                  url = filePathURL;
4427              }
4428              else {
4429                  return NULL;
4430              }
4431          }
4432  #endif
4433          
4434          CFRange rg = _rangeOfLastPathComponent(url);
4435          if (rg.location == kCFNotFound || rg.length == 0) {
4436              // No path
4437              if ( filePathURLCreated ) {
4438                  CFRelease(url);
4439              }
4440              return (CFStringRef)CFRetain(CFSTR(""));
4441          }
4442          if (rg.length == 1 && CFStringGetCharacterAtIndex(url->_string, rg.location) == '/') {
4443              if ( filePathURLCreated ) {
4444                  CFRelease(url);
4445              }
4446              return (CFStringRef)CFRetain(CFSTR("/"));
4447          }
4448          result = CFStringCreateWithSubstring(CFGetAllocator(url), url->_string, rg);
4449          if (!(url->_flags & POSIX_AND_URL_PATHS_MATCH)) {
4450              CFStringRef tmp;
4451              if (url->_encoding == kCFStringEncodingUTF8) {
4452                  tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url), result, CFSTR(""));
4453              } else {
4454                  tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url), result, CFSTR(""), url->_encoding);
4455              }
4456              CFRelease(result);
4457              result = tmp;
4458          }
4459          if ( filePathURLCreated ) {
4460              CFRelease(url);
4461          }
4462      }
4463      return result;
4464  }
4465  
4466  CFStringRef CFURLCopyPathExtension(CFURLRef url) {
4467      CFStringRef lastPathComp = CFURLCopyLastPathComponent(url);
4468      CFStringRef ext = NULL;
4469  
4470      if (lastPathComp) {
4471          CFRange rg = CFStringFind(lastPathComp, CFSTR("."), kCFCompareBackwards);
4472          if (rg.location != kCFNotFound) {
4473              rg.location ++;
4474              rg.length = CFStringGetLength(lastPathComp) - rg.location;
4475              if (rg.length > 0) {
4476                  ext = CFStringCreateWithSubstring(CFGetAllocator(url), lastPathComp, rg);
4477              } else {
4478                  ext = (CFStringRef)CFRetain(CFSTR(""));
4479              }
4480          }
4481          CFRelease(lastPathComp);
4482      }
4483      return ext;
4484  }
4485  
4486  CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRef url, CFStringRef pathComponent, Boolean isDirectory) {
4487      CFURLRef result;
4488      url = _CFURLFromNSURL(url);
4489      __CFGenericValidateType(url, CFURLGetTypeID());
4490      CFAssert1(pathComponent != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__);
4491  
4492      Boolean filePathURLCreated = false;
4493  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4494      if ( CFURLIsFileReferenceURL(url) ) {
4495          // use a file path URL if possible (only because this is appending a path component)
4496          CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4497          if ( filePathURL ) {
4498              filePathURLCreated = TRUE;
4499              url = filePathURL;
4500          }
4501      }
4502  #endif
4503  
4504      CFMutableStringRef newString;
4505      CFStringRef newComp;
4506      CFRange pathRg;
4507      if (!(url->_flags & HAS_PATH)) {
4508          result = NULL;
4509      }
4510      else {
4511          newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4512          newComp = CFURLCreateStringByAddingPercentEscapes(allocator, pathComponent, NULL, CFSTR(";?"), url->_encoding);
4513          pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
4514          if ( (!pathRg.length || CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') && (CFStringGetCharacterAtIndex(newComp, 0) != '/') ) {
4515              CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/"));
4516              pathRg.length ++;
4517          }
4518          CFStringInsert(newString, pathRg.location + pathRg.length, newComp);
4519          if (isDirectory) {
4520              CFStringInsert(newString, pathRg.location + pathRg.length + CFStringGetLength(newComp), CFSTR("/"));
4521          }
4522          CFRelease(newComp);
4523          result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4524          CFRelease(newString);
4525      }
4526      if ( filePathURLCreated ) {
4527          CFRelease(url);
4528      }
4529      return result;
4530  }
4531  
4532  CFURLRef CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator, CFURLRef url) {
4533      CFURLRef result;
4534      CFMutableStringRef newString;
4535      CFRange lastCompRg, pathRg;
4536      Boolean appendDotDot = false;
4537  
4538      url = _CFURLFromNSURL(url);
4539      CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
4540      __CFGenericValidateType(url, CFURLGetTypeID());
4541  
4542      Boolean filePathURLCreated = false;
4543  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4544      if ( CFURLIsFileReferenceURL(url) ) {
4545          // use a file path URL or fail
4546          CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4547          if ( filePathURL ) {
4548              filePathURLCreated = TRUE;
4549              url = filePathURL;
4550          }
4551          else {
4552              return NULL;
4553          }
4554      }
4555  #endif
4556      
4557      if (!(url->_flags & HAS_PATH)) {
4558          if ( filePathURLCreated ) {
4559              CFRelease(url);
4560          }
4561          return NULL;
4562      }
4563      pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
4564      lastCompRg = _rangeOfLastPathComponent(url);
4565      if (lastCompRg.length == 0) {
4566          appendDotDot = true;
4567      } else if (lastCompRg.length == 1) {
4568          UniChar ch = CFStringGetCharacterAtIndex(url->_string, lastCompRg.location);
4569          if (ch == '.' || ch == '/') {
4570              appendDotDot = true;
4571          }
4572      } else if (lastCompRg.length == 2 && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location) == '.' && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location+1) == '.') {
4573          appendDotDot = true;
4574      }
4575  
4576      newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4577      if (appendDotDot) {
4578          CFIndex delta = 0;
4579          if (pathRg.length > 0 && CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') {
4580              CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/"));
4581              delta ++;
4582          }
4583          CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR(".."));
4584          delta += 2;
4585          CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR("/"));
4586          delta ++;
4587          // We know we have "/../" at the end of the path; we wish to know if that's immediately preceded by "/." (but that "/." doesn't start the string), in which case we want to delete the "/.".
4588          if (pathRg.length + delta > 4 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 5) == '.') {
4589              if (pathRg.length+delta > 7 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 6) == '/') {
4590                  CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 6, 2));
4591              } else if (pathRg.length+delta == 5) {
4592                  CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 5, 2));
4593              }
4594          }
4595      } else if (lastCompRg.location == pathRg.location) {
4596          CFStringReplace(newString, pathRg, CFSTR("."));
4597          CFStringInsert(newString, 1, CFSTR("/"));
4598      } else {
4599          CFStringDelete(newString, CFRangeMake(lastCompRg.location, pathRg.location + pathRg.length - lastCompRg.location));
4600      }
4601      result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4602      CFRelease(newString);
4603      if ( filePathURLCreated ) {
4604          CFRelease(url);
4605      }
4606      return result;
4607  }
4608  
4609  CFURLRef CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator, CFURLRef url, CFStringRef extension) {
4610      CFMutableStringRef newString;
4611      CFURLRef result;
4612      CFRange rg;
4613      
4614      CFAssert1(url != NULL && extension != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
4615      url = _CFURLFromNSURL(url);
4616      __CFGenericValidateType(url, CFURLGetTypeID());
4617      __CFGenericValidateType(extension, CFStringGetTypeID());
4618  
4619      Boolean filePathURLCreated = false;
4620  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4621      if ( CFURLIsFileReferenceURL(url) ) {
4622          // use a file path URL or fail
4623          CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4624          if ( filePathURL ) {
4625              filePathURLCreated = TRUE;
4626              url = filePathURL;
4627          }
4628          else {
4629              return NULL;
4630          }
4631      }
4632  #endif
4633      
4634      rg = _rangeOfLastPathComponent(url);
4635      if (rg.location < 0) {
4636          if ( filePathURLCreated ) {
4637              CFRelease(url);
4638          }
4639          return NULL; // No path
4640      }
4641  
4642      newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4643      CFStringInsert(newString, rg.location + rg.length, CFSTR("."));
4644      CFStringRef newExt = CFURLCreateStringByAddingPercentEscapes(allocator, extension, NULL, CFSTR(";?/"), url->_encoding);
4645      CFStringInsert(newString, rg.location + rg.length + 1, newExt);
4646      CFRelease(newExt);
4647      result =  _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4648      CFRelease(newString);
4649      if ( filePathURLCreated ) {
4650          CFRelease(url);
4651      }
4652      return result;
4653  }
4654  
4655  CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef url) {
4656      CFRange rg, dotRg;
4657      CFURLRef result;
4658  
4659      CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
4660      url = _CFURLFromNSURL(url);
4661      __CFGenericValidateType(url, CFURLGetTypeID());
4662      
4663      Boolean filePathURLCreated = false;
4664  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4665      if ( CFURLIsFileReferenceURL(url) ) {
4666          // use a file path URL or fail
4667          CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4668          if ( filePathURL ) {
4669              filePathURLCreated = TRUE;
4670              url = filePathURL;
4671          }
4672          else {
4673              return NULL;
4674          }
4675      }
4676  #endif
4677      
4678      rg = _rangeOfLastPathComponent(url);
4679      if (rg.location < 0) {
4680          result = NULL;
4681      } else if (rg.length && CFStringFindWithOptions(url->_string, CFSTR("."), rg, kCFCompareBackwards, &dotRg)) {
4682          CFMutableStringRef newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4683          dotRg.length = rg.location + rg.length - dotRg.location;
4684          CFStringDelete(newString, dotRg);
4685          result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4686          CFRelease(newString);
4687      } else {
4688          result = (CFURLRef)CFRetain(url);
4689      }
4690      if ( filePathURLCreated ) {
4691          CFRelease(url);
4692      }
4693      return result;
4694  }
4695  
4696  
4697  
4698  // keys and vals must have space for at least 4 key/value pairs.  No argument can be NULL.
4699  // Caller must release values, but not keys
4700  static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url, CFTypeRef *keys, CFTypeRef *vals, CFIndex *count) {
4701      CFAllocatorRef alloc = CFGetAllocator(url);
4702      CFURLRef base = CFURLGetBaseURL(url);
4703      keys[0] = CFSTR("_CFURLStringType");
4704      keys[1] = CFSTR("_CFURLString");
4705      keys[2] = CFSTR("_CFURLBaseStringType");
4706      keys[3] = CFSTR("_CFURLBaseURLString");
4707      if (CF_IS_OBJC(CFURLGetTypeID(), url)) {
4708          SInt32 urlType = FULL_URL_REPRESENTATION;
4709          vals[0] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4710          vals[1] = CFURLGetString(url);
4711      } else {
4712          SInt32 urlType = FULL_URL_REPRESENTATION;
4713          vals[0] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4714          if (url->_flags & IS_DIRECTORY) {
4715              if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) == '/') {
4716                  vals[1] = CFRetain(url->_string);
4717              } else {
4718                  vals[1] = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%c"), url->_string, '/');
4719              }
4720          } else {
4721              if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) != '/') {
4722                  vals[1] = CFRetain(url->_string);
4723              } else {
4724                  vals[1] = CFStringCreateWithSubstring(alloc, url->_string, CFRangeMake(0, CFStringGetLength(url->_string) - 1));
4725              }
4726          }
4727      }
4728      if (base != NULL) {
4729          if (CF_IS_OBJC(CFURLGetTypeID(), base)) {
4730              SInt32 urlType = FULL_URL_REPRESENTATION;
4731              vals[2] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4732              vals[3] = CFURLGetString(base);
4733          } else {
4734              SInt32 urlType = FULL_URL_REPRESENTATION;
4735              vals[2] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4736              if (base->_flags & IS_DIRECTORY) {
4737                  if (CFStringGetCharacterAtIndex(base->_string, CFStringGetLength(base->_string) - 1) == '/') {
4738                      vals[3] = CFRetain(base->_string);
4739                  } else {
4740                      vals[3] = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%c"), base->_string, '/');
4741                  }
4742              } else {
4743                  if (CFStringGetCharacterAtIndex(base->_string, CFStringGetLength(base->_string) - 1) != '/') {
4744                      vals[3] = CFRetain(base->_string);
4745                  } else {
4746                      vals[3] = CFStringCreateWithSubstring(alloc, base->_string, CFRangeMake(0, CFStringGetLength(base->_string) - 1));
4747                  }
4748              }
4749          }
4750          *count = 4;
4751      } else {
4752          *count = 2;
4753      }
4754  }
4755  
4756  // Private API for Finder to use
4757  CFPropertyListRef _CFURLCopyPropertyListRepresentation(CFURLRef url) {
4758      CFTypeRef keys[4], vals[4];
4759      CFDictionaryRef dict;
4760      CFIndex count, idx;
4761      __CFURLCopyPropertyListKeysAndValues(url, keys, vals, &count);
4762      dict = CFDictionaryCreate(CFGetAllocator(url), keys, vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4763      for (idx = 0; idx < count; idx ++) {
4764          CFRelease(vals[idx]);
4765      }
4766      return dict;
4767  }
4768  
4769  CFURLRef _CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc, CFPropertyListRef pListRepresentation) {
4770      CFStringRef baseString, string;
4771      CFNumberRef baseTypeNum, urlTypeNum;
4772      SInt32 baseType, urlType;
4773      CFURLRef baseURL = NULL, url;
4774      CFDictionaryRef dict = (CFDictionaryRef)pListRepresentation;
4775  
4776      // Start by getting all the pieces and verifying they're of the correct type.
4777      if (CFGetTypeID(pListRepresentation) != CFDictionaryGetTypeID()) {
4778          return NULL;
4779      }
4780      string = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("_CFURLString"));
4781      if (!string || CFGetTypeID(string) != CFStringGetTypeID()) {
4782          return NULL;
4783      }
4784      urlTypeNum = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("_CFURLStringType"));
4785  #pragma GCC diagnostic push
4786  #pragma GCC diagnostic ignored "-Wdeprecated"
4787      if (!urlTypeNum || CFGetTypeID(urlTypeNum) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum, kCFNumberSInt32Type, &urlType) || (urlType != FULL_URL_REPRESENTATION && urlType != kCFURLPOSIXPathStyle && urlType != kCFURLHFSPathStyle && urlType != kCFURLWindowsPathStyle)) {
4788  #pragma GCC diagnostic pop
4789          return NULL;
4790      }
4791      baseString = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("_CFURLBaseURLString"));
4792      if (baseString) {
4793          if (CFGetTypeID(baseString) != CFStringGetTypeID()) {
4794              return NULL;
4795          }
4796          baseTypeNum = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("_CFURLBaseStringType"));
4797  #pragma GCC diagnostic push
4798  #pragma GCC diagnostic ignored "-Wdeprecated"
4799          if (!baseTypeNum || CFGetTypeID(baseTypeNum) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum, kCFNumberSInt32Type, &baseType) ||
4800              (baseType != FULL_URL_REPRESENTATION && baseType != kCFURLPOSIXPathStyle && baseType != kCFURLHFSPathStyle && baseType != kCFURLWindowsPathStyle)) {
4801  #pragma GCC diagnostic pop
4802              return NULL;
4803          }
4804          if (baseType == FULL_URL_REPRESENTATION) {
4805              baseURL = _CFURLCreateWithArbitraryString(alloc, baseString, NULL);
4806          } else {
4807              baseURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, baseString, (CFURLPathStyle)baseType, CFStringGetCharacterAtIndex(baseString, CFStringGetLength(baseString)-1) == '/', NULL);
4808          }
4809      }
4810      if (urlType == FULL_URL_REPRESENTATION) {
4811          url = _CFURLCreateWithArbitraryString(alloc, string, baseURL);
4812      } else {
4813          url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, string, (CFURLPathStyle)urlType, CFStringGetCharacterAtIndex(string, CFStringGetLength(string)-1) == '/', baseURL);
4814      }
4815      if (baseURL) CFRelease(baseURL);
4816      return url;
4817  }
4818  
4819  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4820  Boolean _CFURLIsFileReferenceURL(CFURLRef url)
4821  {
4822      return ( CFURLIsFileReferenceURL(url) );
4823  }
4824  
4825  Boolean CFURLIsFileReferenceURL(CFURLRef url)
4826  {
4827      // returns TRUE if url is is a file URL whose path starts with a file ID reference
4828      Boolean result = false;
4829      CFURLRef baseURL = CFURLGetBaseURL(url);
4830      if ( baseURL ) {
4831  	result = CFURLIsFileReferenceURL(baseURL);
4832      }
4833      else {
4834          if ( CF_IS_OBJC(CFURLGetTypeID(), url) ) {
4835              result = (Boolean) CF_OBJC_CALLV((NSURL *)url, isFileReferenceURL);
4836          }
4837          else {
4838              result = ((_getSchemeTypeFromFlags(url->_flags) == kHasFileScheme) && ((url->_flags & PATH_HAS_FILE_ID) != 0));
4839          }
4840      }
4841      return ( result );
4842  }
4843  
4844  static Boolean _CFURLHasFileURLScheme(CFURLRef url, Boolean *hasScheme)
4845  {
4846      Boolean result;
4847      CFURLRef baseURL = CFURLGetBaseURL(url);
4848      
4849      if ( baseURL ) {
4850  	result = _CFURLHasFileURLScheme(baseURL, hasScheme);
4851      }
4852      else {
4853          if ( CF_IS_OBJC(CFURLGetTypeID(), url) || (_getSchemeTypeFromFlags(url->_flags) == kHasUncommonScheme) ) {
4854              // if it's not a CFURL or the scheme is not a common canonical-form scheme, determine the scheme the slower way.
4855              CFStringRef scheme = CFURLCopyScheme(url);
4856              if ( scheme ) {
4857                  if ( scheme == kCFURLFileScheme ) {
4858                      result = true;
4859                  }
4860                  else {
4861                      result = CFStringCompare(scheme, kCFURLFileScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo;
4862                  }
4863                  if ( hasScheme ) {
4864                      *hasScheme = true;
4865                  }
4866                  CFRelease(scheme);
4867              }
4868              else {
4869                  if ( hasScheme ) {
4870                      *hasScheme = false;
4871                  }
4872                  result = false;
4873              }
4874          }
4875          else {
4876              if ( hasScheme ) {
4877                  *hasScheme = (url->_flags & HAS_SCHEME) != 0;
4878              }
4879              result = (_getSchemeTypeFromFlags(url->_flags) == kHasFileScheme);
4880          }
4881      }
4882      return ( result );
4883  }
4884  
4885  Boolean _CFURLIsFileURL(CFURLRef url)
4886  {
4887      Boolean result = _CFURLHasFileURLScheme(url, NULL);
4888      return ( result );
4889  }
4890  
4891  CFURLRef CFURLCreateFilePathURL(CFAllocatorRef alloc, CFURLRef url, CFErrorRef *error)
4892  {
4893      CFURLRef result = NULL;
4894      Boolean hasScheme;
4895      if (!_CFURLHasFileURLScheme(url, &hasScheme)) {
4896          if ( !hasScheme ) {
4897              CFLog(kCFLogLevelWarning, CFSTR("CFURLCreateFilePathURL failed because it was passed this URL which has no scheme: %@"), url);
4898          }
4899          if ( error ) {
4900              *error = CFErrorCreate( kCFAllocatorDefault, kCFErrorDomainCocoa, kCFURLReadUnsupportedSchemeError, NULL );
4901          }
4902          result = NULL;
4903      } else {
4904  	// File URL. Form of the path is unknown. Make a new URL.
4905  	CFStringRef newURLString;
4906  	CFStringRef netLoc;
4907  	CFStringRef fsPath;
4908  	CFStringRef rSpec;
4909          
4910  	if ( CFURLGetBaseURL( url )) {
4911  	    CFURLRef absURL = CFURLCopyAbsoluteURL( url );
4912  	    fsPath = CFURLCreateStringWithFileSystemPath(CFGetAllocator(absURL), absURL, kCFURLPOSIXPathStyle, false);
4913  	    netLoc = CFURLCopyNetLocation( absURL );
4914  	    rSpec = CFURLCopyResourceSpecifier( absURL );
4915  	    CFRelease( absURL );
4916  	} else {
4917  	    fsPath = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLPOSIXPathStyle, false);
4918  	    netLoc = CFURLCopyNetLocation( url );
4919  	    rSpec = CFURLCopyResourceSpecifier( url );
4920  	}
4921  	if ( fsPath ) {
4922  	    CFStringRef urlPath = _replacePathIllegalCharacters( fsPath, alloc, true );
4923              
4924              CFStringAppendBuffer appendBuffer;
4925              CFStringInitAppendBuffer(alloc, &appendBuffer);
4926              CFStringAppendStringToAppendBuffer(&appendBuffer, CFSTR(FILE_PREFIX));
4927              if ( netLoc ) {
4928                  CFStringAppendStringToAppendBuffer(&appendBuffer, netLoc);
4929              }
4930              CFStringAppendStringToAppendBuffer(&appendBuffer, urlPath);
4931              // if original url had a directory path and the path isn't "/", append a slash
4932              if ( CFURLHasDirectoryPath(url) && (CFStringCompare(urlPath, CFSTR("/"), 0) != kCFCompareEqualTo) ) {
4933                  UniChar slashUniChar = '/';
4934                  CFStringAppendCharactersToAppendBuffer(&appendBuffer, &slashUniChar, 1);
4935              }
4936              if ( rSpec ) {
4937                  CFStringAppendStringToAppendBuffer(&appendBuffer, rSpec);
4938              }
4939              newURLString = CFStringCreateMutableWithAppendBuffer(&appendBuffer);
4940              
4941  	    result = CFURLCreateWithString( alloc, newURLString, NULL );
4942  	    CFRelease( newURLString );
4943  	    CFRelease( urlPath );
4944  	    CFRelease( fsPath );	    
4945  	} else {
4946  	    if ( error ) { 
4947  		// Would be better here to get an underlying error back from CFURLCreateStringWithFileSystemPath
4948  		*error = CFErrorCreate( kCFAllocatorDefault, kCFErrorDomainCocoa, kCFURLNoSuchResourceError, NULL );
4949  	    }
4950  	    result = NULL;
4951  	}
4952  	if ( netLoc ) {
4953  	    CFRelease( netLoc );
4954  	}
4955  	if ( rSpec ) {
4956  	    CFRelease( rSpec );
4957  	}
4958      }
4959      return result;
4960  }
4961  
4962  #endif
4963  
4964  
4965  CFURLRef CFURLCreateFileReferenceURL(CFAllocatorRef alloc, CFURLRef url, CFErrorRef *error) { return NULL; }
4966  
4967  #include <CoreServices/FileManager.h>
4968  #include <sys/stat.h>
4969  #include <dlfcn.h>
4970  
4971  CFURLRef
4972  CFURLCreateFromFSRef (CFAllocatorRef alloc, const FSRefPtr fsRef)
4973  {
4974      char path[4096];
4975      struct stat st;
4976      Boolean isDir = false;
4977      static OSStatus (*FSRefMakePath_ptr)(const struct FSRef *ref, UInt8 *path, UInt32 pathBufferSize) = NULL;
4978  
4979      if (!FSRefMakePath_ptr)
4980      {
4981          void* lib = dlopen("/System/Library/Frameworks/CoreServices.framework/CoreServices", RTLD_GLOBAL);
4982          if (!lib)
4983              return NULL;
4984  
4985          *((void**) &FSRefMakePath_ptr) = dlsym(lib, "FSRefMakePath");
4986  
4987          if (!FSRefMakePath_ptr)
4988              return NULL;
4989      }
4990  
4991      if (FSRefMakePath_ptr(fsRef, (uint8_t*) path, sizeof(path)))
4992          return NULL;
4993  
4994      if (stat(path, &st))
4995          isDir = S_ISDIR(st.st_mode);
4996  
4997      return CFURLCreateFromFileSystemRepresentation(alloc,path, strlen(path), isDir);
4998  }
4999  
5000  Boolean CFURLGetFSRef(CFURLRef urlref, FSRefPtr fsref)
5001  {
5002      char* buf;
5003      CFIndex len;
5004      CFStringRef sref = CFURLCopyFileSystemPath(urlref, kCFURLPOSIXPathStyle);
5005      Boolean rv;
5006      static OSStatus (*FSPathMakeRef_ptr)(const UInt8 *path, struct FSRef *ref, Boolean *isDirectory);
5007  
5008      if (!sref)
5009          return false;
5010  
5011      if (!FSPathMakeRef_ptr)
5012      {
5013          void* lib = dlopen("/System/Library/Frameworks/CoreServices.framework/CoreServices", RTLD_GLOBAL);
5014          if (!lib)
5015              return false;
5016  
5017          *((void**) &FSPathMakeRef_ptr) = dlsym(lib, "FSPathMakeRef");
5018          if (!FSPathMakeRef_ptr)
5019              return false;
5020      }
5021  
5022      len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(sref), kCFStringEncodingUTF8);
5023      buf = malloc(len + 1);
5024  
5025      if (!CFStringGetCString(sref, buf, len, kCFStringEncodingUTF8))
5026      {
5027          free(buf);
5028          return false;
5029      }
5030  
5031      CFRelease(sref);
5032  
5033      rv = FSPathMakeRef_ptr((uint8_t*) buf, fsref, NULL) == 0;
5034  
5035      free(buf);
5036      return rv;
5037  }
5038