/ 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