2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2014, Apple Inc. All rights reserved.
26 Responsibility: Jim Luther/Chris Linn
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>
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>
45 #include <sys/types.h>
46 #include <sys/syslog.h>
47 #include <CoreFoundation/CFURLPriv.h>
50 #ifndef DEBUG_URL_MEMORY_USAGE
51 // enables various statistical counters which can be displayed with __CFURLDumpMemRecord().
52 #define DEBUG_URL_MEMORY_USAGE 0
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
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
);
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")
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")
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
100 /* The bit flags in myURL->_flags */
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
124 kHasUncommonScheme
= 0, // scheme is uncommon or scheme isn't in the canonical form (all lower case)
132 // accessors for the scheme bits in _flags
133 CF_INLINE UInt32
_getSchemeTypeFromFlags(UInt32 flags
);
134 CF_INLINE
void _setSchemeTypeInFlags(UInt32
*flags
, UInt32 schemeType
);
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 )
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)
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
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
165 #define FILE_PREFIX "file://"
166 static const UInt8 fileURLPrefix
[] = FILE_PREFIX
;
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
;
172 static Boolean
AddAuthorityToFileURL(void)
174 static Boolean result
= false;
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)
188 CFRuntimeBase _cfBase
;
190 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes
191 CFStringRef _string
; // Never NULL
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
199 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
201 if ( url
&& url
->_extra
) {
202 return ( url
->_extra
->_reserved
);
209 CF_INLINE CFStringRef
_getSanitizedString(const struct __CFURL
* url
)
211 if ( url
&& url
->_extra
) {
212 return ( url
->_extra
->_sanitizedString
);
219 CF_INLINE UInt32
_getAdditionalDataFlags(const struct __CFURL
* url
)
221 if ( url
&& url
->_extra
) {
222 return ( url
->_extra
->_additionalDataFlags
);
229 CF_INLINE
void* _getResourceInfo ( const struct __CFURL
* url
)
232 return url
->_resourceInfo
;
239 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
241 if ( url
&& ! url
->_extra
)
242 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), __kCFAllocatorGCScannedMemory
);
244 extra
->_reserved
= _getReserved( url
);
245 extra
->_additionalDataFlags
= _getAdditionalDataFlags(url
);
246 extra
->_sanitizedString
= _getSanitizedString(url
);
250 #if DEBUG_URL_MEMORY_USAGE
251 numExtraDataAllocated
++;
256 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
260 // Don't allocate extra space if we're just going to be storing NULL
261 if ( !url
->_extra
&& reserved
)
262 _CFURLAllocateExtraDataspace( url
);
265 __CFAssignWithWriteBarrier((void **)&url
->_extra
->_reserved
, reserved
);
269 CF_INLINE
void _setSanitizedString( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
273 // Don't allocate extra space if we're just going to be storing NULL
274 if ( !url
->_extra
&& sanitizedString
) {
275 _CFURLAllocateExtraDataspace( url
);
279 if ( url
->_extra
->_sanitizedString
) {
280 CFRelease(url
->_extra
->_sanitizedString
);
282 url
->_extra
->_sanitizedString
= CFStringCreateCopy(CFGetAllocator(url
), sanitizedString
);
288 CF_INLINE
void _setAdditionalDataFlags(struct __CFURL
* url
, UInt32 additionalDataFlags
)
292 // Don't allocate extra space if we're just going to be storing 0
293 if ( !url
->_extra
&& (additionalDataFlags
!= 0) ) {
294 _CFURLAllocateExtraDataspace( url
);
298 url
->_extra
->_additionalDataFlags
= additionalDataFlags
;
303 CF_INLINE
void _setResourceInfo ( struct __CFURL
* url
, void* resourceInfo
)
307 if ( url
&& OSAtomicCompareAndSwapPtrBarrier( NULL
, resourceInfo
, &url
->_resourceInfo
)) {
308 CFRetain( resourceInfo
);
312 CF_INLINE UInt32
_getSchemeTypeFromFlags(UInt32 flags
)
314 return ( (flags
& SCHEME_TYPE_MASK
) >> SCHEME_SHIFT
);
317 CF_INLINE
void _setSchemeTypeInFlags(UInt32
*flags
, UInt32 schemeType
)
319 CFAssert2((schemeType
>= kHasUncommonScheme
) && (schemeType
< kMaxScheme
), __kCFLogAssertion
, "%s(): Received bad schemeType %d", __PRETTY_FUNCTION__
, schemeType
);
320 *flags
= (*flags
& ~SCHEME_TYPE_MASK
) + (schemeType
<< SCHEME_SHIFT
);
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
);
343 static const unsigned char sURLValidCharacters
[128] = {
377 /* '!' 33 */ VALID
| PATHVALID
,
380 /* '$' 36 */ VALID
| PATHVALID
,
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
,
405 /* '=' 61 */ VALID
| PATHVALID
,
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
,
439 /* '_' 95 */ VALID
| PATHVALID
,
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
,
470 /* '~' 126 */ VALID
| PATHVALID
,
474 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
475 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & VALID
) != 0) : false;
478 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
479 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & SCHEME
) != 0) : false;
482 CF_INLINE Boolean
isALPHA(UniChar ch
) {
483 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & ALPHA
) != 0) : false;
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;
491 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
492 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & HEXDIGIT
) != 0) : false;
495 // Returns false if ch1 or ch2 isn't properly formatted
496 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
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';
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';
512 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
513 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
520 static const unsigned char sURLValidBytes
[256] = {
554 /* '!' 33 */ IS_PCHAR
,
557 /* '$' 36 */ IS_PCHAR
,
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
582 /* '=' 61 */ IS_PCHAR
,
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
,
616 /* '_' 95 */ IS_PCHAR
,
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
,
647 /* '~' 126 */ IS_PCHAR
,
779 CF_INLINE Boolean
is_pchar(unsigned char ch
) {
780 return ( (sURLValidBytes
[ch
] & IS_PCHAR
) != 0 );
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
789 The rules for path-absolute from rfc3986 are:
790 path-absolute = "/" [ segment-nz *( "/" segment ) ]
793 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
794 pct-encoded = "%" HEXDIG HEXDIG
795 unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
796 sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
798 static CFStringRef
CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc
, const UInt8
*bytes
, CFIndex numBytes
, Boolean isDirectory
, Boolean isAbsolute
, Boolean windowsPath
, Boolean
*addedPercentEncoding
)
800 static const UInt8 hexchars
[] = "0123456789ABCDEF";
801 const UInt8
*fileURLPrefixPtr
;
802 size_t fileURLPrefixLength
;
803 if ( AddAuthorityToFileURL() ) {
804 fileURLPrefixPtr
= fileURLPrefixWithAuthority
;
805 fileURLPrefixLength
= sizeof(fileURLPrefixWithAuthority
);
808 fileURLPrefixPtr
= fileURLPrefix
;
809 fileURLPrefixLength
= sizeof(fileURLPrefix
);
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
814 const UInt8
*bytePtr
= bytes
;
817 Boolean addedPercent
= FALSE
;
819 // choose a buffer to percent-escape into.
820 if ( numBytes
<= PATH_MAX
) {
821 bufStartPtr
= &stackBuf
[0];
824 // worst case is every byte needs to be percent-escaped
825 bufStartPtr
= (UInt8
*)malloc((numBytes
* 3) + (isAbsolute
? fileURLPrefixLength
: 0) + (isDirectory
? 1 : 0));
828 if ( bufStartPtr
!= NULL
) {
830 // start with the fileURLPrefix
831 strcpy((char *)bufStartPtr
, (char *)fileURLPrefixPtr
);
832 bufBytePtr
= bufStartPtr
+ fileURLPrefixLength
- 1;
835 bufBytePtr
= bufStartPtr
;
838 if ( !windowsPath
) {
839 for ( idx
= 0; (idx
< numBytes
) && (*bytePtr
!= 0); ++idx
) {
840 if ( is_pchar(*bytePtr
) ) {
841 *bufBytePtr
++ = *bytePtr
;
845 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
846 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
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
;
859 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
860 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
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
) {
876 if ( idx
== numBytes
) {
878 // if it is a directory and it doesn't end with PATH_SEP, append a PATH_SEP.
879 if ( bytes
[numBytes
-1] != '/' ) {
884 // it is not a directory: remove any pathDelim characters at end (leaving at least one character)
885 while ( (numBytes
> 1) && (bytes
[numBytes
-1] == '/') ) {
892 result
= CFStringCreateWithBytes(alloc
, bufStartPtr
, (CFIndex
)(bufBytePtr
-bufStartPtr
), kCFStringEncodingUTF8
, FALSE
);
895 // the remaining bytes were not all nul
899 // free the buffer if we malloc'd it
900 if ( bufStartPtr
!= &stackBuf
[0] ) {
908 if ( addedPercentEncoding
) {
909 *addedPercentEncoding
= addedPercent
;
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
);
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
;
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
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
);
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);
952 if (bytePtr
!= bytes
) {
953 CFAllocatorDeallocate(alloc
, bytePtr
);
958 static CFStringRef
UnescapeAllWithUTF8(CFAllocatorRef alloc
, CFStringRef originalString
)
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);
967 // choose a buffer to percent-escape into.
968 if ( maxBufferSize
<= stackBufferSize
) {
969 escapedBuf
= &escapedStackBuf
[0];
972 escapedBuf
= (UInt8
*)malloc(maxBufferSize
* 2);
975 CFIndex charsConverted
;
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,
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,
1018 const UInt8
*bytePtr
= escapedBuf
;
1021 bufPtr
= bufStartPtr
= unescapedBuf
;
1022 Boolean conversionOK
= TRUE
;
1024 for ( idx
= 0; (idx
< usedBufLen
) && conversionOK
; ++idx
) {
1025 switch ( *bytePtr
) {
1028 if ( idx
< usedBufLen
) {
1032 // get the hex digits
1033 hex1
= hexvalues
[*bytePtr
++];
1034 hex2
= hexvalues
[*bytePtr
++];
1036 if ( ((hex1
| hex2
) & 0x80) == 0 ) {
1037 // convert hex digits
1038 *bufPtr
= (hex1
<< 4) + hex2
;
1041 conversionOK
= FALSE
;
1045 conversionOK
= FALSE
;
1049 // copy everything else
1050 *bufPtr
= *bytePtr
++;
1055 if ( conversionOK
) {
1056 result
= CFStringCreateWithBytes(alloc
, unescapedBuf
, bufPtr
- bufStartPtr
, kCFStringEncodingUTF8
, false);
1060 // free the buffer if we malloc'd it
1061 if ( escapedBuf
!= &escapedStackBuf
[0] ) {
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
;
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;
1080 if (!originalString
) return NULL
;
1082 length
= CFStringGetLength(originalString
);
1084 if ((length
== 0) || (charactersToLeaveEscaped
== NULL
)) {
1085 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
1089 return ( UnescapeAllWithUTF8(alloc
, originalString
) );
1092 searchRange
= CFRangeMake(0, length
);
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
;
1100 // Make sure we have at least 2 more characters
1101 if (length
- percentRange
.location
< 3) { failed
= true; break; }
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;
1115 numBytesExpected
= 4;
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
);
1123 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
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; }
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);
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
);
1146 CFRelease(escapedStr
);
1147 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
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
;
1157 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
1159 CFRelease(escapedStr
);
1167 newStr
= CFStringCreateMutable(alloc
, length
);
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
);
1175 CFStringAppend(newStr
, escapedStr
);
1177 CFRelease(escapedStr
);
1180 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
1183 if (escapedStr
) CFRelease(escapedStr
);
1184 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
1186 if (newStr
) CFRelease(newStr
);
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
);
1197 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
1202 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
1203 if (enc
== kCFStringEncodingUTF8
) {
1204 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
1206 CFMutableStringRef newStr
= NULL
;
1207 CFMutableStringRef escapedStr
= NULL
;
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;
1217 if (!originalString
) return NULL
;
1219 if (charactersToLeaveEscaped
== NULL
) {
1220 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
1223 length
= CFStringGetLength(originalString
);
1224 searchRange
= CFRangeMake(0, length
);
1226 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
1228 CFIndex percentLoc
= percentRange
.location
;
1229 CFStringRef convertedString
;
1230 int numBytesUsed
= 0;
1232 // Make sure we have at least 2 more characters
1233 if (length
- percentLoc
< 3) { failed
= true; break; }
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;
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
);
1250 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
1252 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
1254 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
1256 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
1257 searchRange
.location
= percentLoc
;
1258 searchRange
.length
= length
- searchRange
.location
;
1261 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
1262 if (!convertedString
) {
1268 newStr
= CFStringCreateMutable(alloc
, length
);
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
);
1278 CFStringAppend(newStr
, convertedString
);
1280 CFIndex i
, c
= CFStringGetLength(convertedString
);
1282 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
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);
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
);
1294 CFRelease(convertedString
);
1295 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
1298 if (escapedStr
) CFRelease(escapedStr
);
1299 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
1301 if (newStr
) CFRelease(newStr
);
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
);
1312 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
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;
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
;
1331 kCharBufferMax
= 4096,
1333 STACK_BUFFER_DECL(UniChar
, charBuffer
, kCharBufferMax
);
1334 CFIndex charBufferIndex
= 0;
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
));
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;
1348 } else if (legalURLCharactersToBeEscaped
&& _stringContainsCharacter(legalURLCharactersToBeEscaped
, ch
)) {
1349 shouldReplace
= true;
1352 if (shouldReplace
) {
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
1358 static const UInt8 hexchars
[] = "0123456789ABCDEF";
1359 uint8_t bytes
[kMaxBytesPerUniChar
];
1360 uint8_t *bytePtr
= bytes
;
1365 // Perform the replacement
1367 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
1368 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
1370 // make sure charBuffer has enough room
1371 if ( charBufferIndex
>= (kCharBufferMax
- kMaxPercentEncodedUniChars
) ) {
1373 CFStringAppendCharacters(newString
, charBuffer
, charBufferIndex
);
1374 charBufferIndex
= 0;
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];
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];
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];
1403 idx
++; // We consumed 2 characters, not 1
1406 // surrogate pair conversion failed
1410 // not a surrogate pair
1414 } else if (newString
) {
1415 charBuffer
[charBufferIndex
++] = ch
;
1416 if ( charBufferIndex
== kCharBufferMax
) {
1417 CFStringAppendCharacters(newString
, charBuffer
, charBufferIndex
);
1418 charBufferIndex
= 0;
1423 // Ran into an encoding failure
1424 if (newString
) CFRelease(newString
);
1426 } else if (newString
) {
1427 if ( charBufferIndex
!= 0 ) {
1428 CFStringAppendCharacters(newString
, charBuffer
, charBufferIndex
);
1432 return (CFStringRef
)CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
1436 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
1438 CFURLRef url1
= (CFURLRef
)cf1
;
1439 CFURLRef url2
= (CFURLRef
)cf2
;
1441 __CFGenericValidateType(cf1
, CFURLGetTypeID());
1442 __CFGenericValidateType(cf2
, CFURLGetTypeID());
1444 if ( url1
== url2
) {
1448 if ( (url1
->_flags
& EQUAL_FLAGS_MASK
) != (url2
->_flags
& EQUAL_FLAGS_MASK
) ) {
1452 if ( (url1
->_base
&& !url2
->_base
) ||
1453 (!url1
->_base
&& url2
->_base
) ||
1454 (url1
->_base
&& url2
->_base
&& !CFEqual(url1
->_base
, url2
->_base
)) ) {
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
));
1467 static CFHashCode
__CFURLHash(CFTypeRef cf
)
1472 // use the CFHashCode of the URL
1473 result
= CFHash(CFURLGetString((CFURLRef
)cf
));
1476 // no object, no hashcode
1483 static CFStringRef
CreateTruncatedURLString(CFAllocatorRef alloc
, CFStringRef urlString
, CFIndex maxLength
, CFIndex suffixLength
)
1487 CFIndex len
= CFStringGetLength(urlString
);
1488 if ( len
<= maxLength
) {
1489 // return the retained urlString
1490 result
= CFStringCreateCopy(alloc
, urlString
);
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
);
1506 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
1508 CFURLRef url
= (CFURLRef
)cf
;
1509 __CFGenericValidateType(cf
, CFURLGetTypeID());
1510 CFAllocatorRef alloc
= CFGetAllocator(url
);
1512 Boolean isDataURL
= false;
1513 CFStringRef scheme
= CFURLCopyScheme(url
);
1515 isDataURL
= CFStringCompare(scheme
, kCFURLDataScheme
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
;
1522 result
= CFStringCreateCopy(alloc
, url
->_string
);
1525 // Do not dereference url->_base; it may be an ObjC object
1526 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
1530 if ( !url
->_base
) {
1531 result
= CreateTruncatedURLString(alloc
, url
->_string
, 128, 8);
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
);
1539 CFRelease(urlString
);
1542 CFRelease(baseString
);
1550 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
1551 CFURLRef url
= (CFURLRef
)cf
;
1553 CFAllocatorRef alloc
= CFGetAllocator(url
);
1554 Boolean isDataURL
= false;
1555 CFStringRef scheme
= CFURLCopyScheme(url
);
1558 isDataURL
= CFStringCompare(scheme
, kCFURLDataScheme
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
;
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
);
1568 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf
, alloc
, url
->_string
, (unsigned int)(url
->_encoding
));
1572 CFStringRef urlString
= CreateTruncatedURLString(alloc
, url
->_string
, 128, 8);
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
);
1578 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf
, alloc
, urlString
, (unsigned int)(url
->_encoding
));
1580 CFRelease(urlString
);
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
);
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
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
));
1607 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
1609 static const CFRuntimeClass __CFURLClass
= {
1611 "CFURL", // className
1614 __CFURLDeallocate
, // finalize
1615 __CFURLEqual
, // equal
1616 __CFURLHash
, // hash
1617 __CFURLCopyFormattingDescription
, // copyFormattingDesc
1618 __CFURLCopyDescription
, // copyDebugDesc
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
);
1629 CFTypeID
CFURLGetTypeID(void) {
1630 static dispatch_once_t initOnce
;
1631 dispatch_once(&initOnce
, ^{ __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
); });
1632 return __kCFURLTypeID
;
1635 CF_PRIVATE
void CFShowURL(CFURLRef url
) {
1637 fprintf(stdout
, "(null)\n");
1640 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1641 if (CF_IS_OBJC(CFURLGetTypeID(), url
)) {
1642 fprintf(stdout
, "ObjC bridged object}\n");
1645 fprintf(stdout
, "\n\tRelative string: ");
1646 CFShow(url
->_string
);
1647 fprintf(stdout
, "\tBase URL: ");
1649 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1652 fprintf(stdout
, "(null)\n");
1654 fprintf(stdout
, "\tFlags: 0x%x\n}\n", (unsigned int)url
->_flags
);
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
;
1666 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1670 *freeCharacters
= false;
1674 *ustring
= CFStringGetCharactersPtr(string
);
1676 *useCString
= false;
1677 *freeCharacters
= false;
1681 length
= CFStringGetLength(string
);
1682 rg
= CFRangeMake(0, length
);
1683 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1684 if (neededLength
== length
) {
1686 if ( (inBuffer
!= NULL
) && (length
<= inBufferSize
) ) {
1687 buf
= (char *)inBuffer
;
1688 *freeCharacters
= false;
1691 buf
= (char *)CFAllocatorAllocate(alloc
, length
, 0);
1692 *freeCharacters
= true;
1694 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, (uint8_t *)buf
, length
, NULL
);
1699 if ( (inBuffer
!= NULL
) && ((length
* sizeof(UniChar
)) <= inBufferSize
) ) {
1700 buf
= (UniChar
*)inBuffer
;
1701 *freeCharacters
= false;
1704 buf
= (UniChar
*)CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1705 *freeCharacters
= true;
1707 CFStringGetCharacters(string
, rg
, buf
);
1709 *useCString
= false;
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
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
1723 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
*packedRanges
, uint8_t *numberOfRanges
)
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
);
1732 constructBuffers(alloc
, string
, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1735 _parseComponentsCString(alloc
, baseURL
, cfStringLength
, cstring
, theFlags
, packedRanges
, numberOfRanges
);
1738 _parseComponentsUString(alloc
, baseURL
, cfStringLength
, ustring
, theFlags
, packedRanges
, numberOfRanges
);
1741 if (freeCharacters
) {
1742 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
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
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
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
) {
1758 return ( scanCharactersCString(alloc
, escapedString
, flags
, cstring
, useCString
, base
, end
, mark
, componentFlag
, encoding
));
1761 return ( scanCharactersUString(alloc
, escapedString
, flags
, ustring
, useCString
, base
, end
, mark
, componentFlag
, encoding
));
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;
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;
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
;
1786 if ( sanitizedString
) {
1787 _setAdditionalDataFlags((struct __CFURL
*)url
, additionalDataFlags
);
1790 // Go component by component
1791 CFIndex currentComponent
= HAS_USER
;
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
);
1798 currentComponent
= currentComponent
<< 1;
1800 if (sanitizedString
) {
1801 _setAdditionalDataFlags((struct __CFURL
*)url
, additionalDataFlags
);
1803 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1806 if (sanitizedString
&& mark
!= string_length
) {
1808 __CFStringAppendBytes(sanitizedString
, (char *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
);
1810 CFStringAppendCharacters(sanitizedString
, &(ustring
[mark
]), string_length
- mark
);
1813 if ( sanitizedString
) {
1814 _setSanitizedString((struct __CFURL
*) url
, sanitizedString
);
1815 CFRelease(sanitizedString
);
1817 if (freeCharacters
) {
1818 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
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
);
1834 constructBuffers(alloc
, comp
, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1835 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1837 if (mark
< string_length
) {
1839 __CFStringAppendBytes(result
, (char *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
);
1841 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1845 // This should nevr happen
1847 result
= (CFMutableStringRef
)comp
;
1849 if (freeCharacters
) {
1850 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
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
);
1861 static struct __CFURL
* _CFURLAlloc(CFAllocatorRef allocator
, uint8_t numberOfRanges
) {
1862 struct __CFURL
*url
;
1863 #if DEBUG_URL_MEMORY_USAGE
1865 // if ( numURLs % 1000 == 0 ) {
1866 // __CFURLDumpMemRecord();
1869 CFIndex extraBytes
= offsetof(struct __CFURL
, _ranges
[numberOfRanges
]) - sizeof(CFRuntimeBase
);
1870 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, CFURLGetTypeID(), extraBytes
, NULL
);
1873 url
->_encoding
= kCFStringEncodingUTF8
;
1874 url
->_string
= NULL
;
1877 url
->_resourceInfo
= NULL
;
1882 static Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1883 Boolean result
= true;
1885 CFStringInlineBuffer stringBuffer
;
1886 Boolean sawHash
= false;
1888 CFIndex checkHexDigit
= 0;
1891 length
= CFStringGetLength(string
);
1893 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1895 while ( idx
< length
) {
1896 CFIndex rangeLength
;
1897 const UniChar
*chPtr
;
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
1915 if ( *chPtr
== '[' || *chPtr
== ']' ) {
1916 continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1918 if ( *chPtr
== '#' ) {
1919 // only allow one # character
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
== '|' ) {
1935 if ( isURLLegalCharacter(*chPtr
) ) {
1945 if ( isHexDigit(*chPtr
) ) {
1957 break; // out of main while loop
1965 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1972 /* initializes a URL object with a URL string */
1973 static CFURLRef
_CFURLCreateWithURLString(CFAllocatorRef allocator
, CFStringRef string
, Boolean checkForLegalCharacters
, CFURLRef baseURL
)
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
;
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
);
1986 Boolean isAbsolute
= false;
1987 Boolean schemeCharsValid
= true;
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
== ':' ) {
2001 if ( !scheme_valid(*chPtr
) ) {
2002 schemeCharsValid
= false;
2014 #if DEBUG_URL_MEMORY_USAGE
2017 // get the range flags, ranges and range count from the parser
2018 uint8_t numberOfRanges
;
2020 CFRange packedRanges
[MAX_COMPONENTS
];
2022 _parseComponents(allocator
, string
, baseURL
, &flags
, packedRanges
, &numberOfRanges
);
2023 // allocate the URL object with the appropriate number of ranges
2024 result
= _CFURLAlloc(allocator
, numberOfRanges
);
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
2032 numURLsWithBaseURL
++;
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
);
2042 if ( input_baseURL
) {
2043 CFRelease(input_baseURL
);
2049 /* initializes a URL object with a file system path */
2050 static CFURLRef
_CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef fileSystemPath
, CFURLPathStyle pathStyle
, Boolean isDirectory
, CFURLRef baseURL
)
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
;
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
2064 struct __CFURL
*result
= NULL
;
2065 CFStringRef urlString
= NULL
;
2067 Boolean isFileReferencePath
= false;
2068 Boolean posixAndUrlPathsMatch
= false;
2069 Boolean releaseBaseURL
= false;
2070 CFIndex len
= CFStringGetLength(fileSystemPath
);
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
);
2085 case kCFURLWindowsPathStyle
:
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
== '|');
2093 // A disk designator
2095 urlString
= WindowsPathToURLPath(fileSystemPath
, allocator
, isDirectory
, isAbsolute
);
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.
2100 urlString
= WindowsPathToURLPath(fileSystemPath
, allocator
, isDirectory
, isAbsolute
);
2102 else if (firstChar
== '/') {
2103 // there's probably code that passes POSIX paths in but wrongly specifies kCFURLWindowsPathStyle.
2104 pathStyle
= kCFURLPOSIXPathStyle
;
2106 urlString
= POSIXPathToURLPath(fileSystemPath
, allocator
, isDirectory
, isAbsolute
, &posixAndUrlPathsMatch
);
2110 urlString
= WindowsPathToURLPath(fileSystemPath
, allocator
, isDirectory
, isAbsolute
);
2116 CFAssert2(urlString
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, urlString
);
2120 // if fileSystemPath is an absolute path, ignore baseURL (if provided)
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;
2129 // override isDirectory if the path is to root
2130 if ( !isDirectory
&& (len
== 1) && (CFStringGetCharacterAtIndex(urlString
, 0) == '/') ) {
2131 // Override isDirectory
2135 // allocate the URL object with the appropriate number of ranges
2136 result
= _CFURLAlloc(allocator
, isAbsolute
? (AddAuthorityToFileURL() ? 3 : 2) : 1);
2138 result
->_string
= CFStringCreateCopy(allocator
, urlString
);
2139 result
->_base
= baseURL
? CFURLCopyAbsoluteURL(baseURL
) : NULL
;
2140 #if DEBUG_URL_MEMORY_USAGE
2142 numURLsWithBaseURL
++;
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
;
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);
2158 _setSchemeTypeInFlags(&flags
, kHasFileScheme
);
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);
2167 result
->_flags
= flags
;
2168 result
->_ranges
[0] = CFRangeMake(0, 4); // scheme "file"
2169 result
->_ranges
[1] = CFRangeMake(7, CFStringGetLength(urlString
)- 7);
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
));
2177 if ( releaseBaseURL
&& baseURL
) {
2181 CFRelease(urlString
);
2184 else if ( baseURL
) {
2185 result
= (struct __CFURL
*)CFRetain(baseURL
);
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
);
2195 if ( input_baseURL
) {
2196 CFRelease(input_baseURL
);
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
)
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
);
2212 struct __CFURL
*result
= NULL
;
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;
2220 // if buffer contains an absolute path, ignore baseURL (if provided)
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;
2228 CFStringRef urlString
= CreateStringFromFileSystemRepresentationByAddingPercentEscapes(allocator
, buffer
, bufLen
, isDirectory
, isAbsolute
, false /*windowsPath*/, &addedPercentEncoding
);
2230 // allocate the URL object with the appropriate number of ranges
2231 result
= _CFURLAlloc(allocator
, isAbsolute
? (AddAuthorityToFileURL() ? 3 : 2) : 1);
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
++;
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);
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);
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
));
2260 if ( releaseBaseURL
&& baseURL
) {
2263 CFRelease(urlString
);
2265 #elif DEPLOYMENT_TARGET_WINDOWS
2266 CFStringRef filePath
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
2268 result
= _CFURLCreateWithFileSystemPath(allocator
, filePath
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
2269 CFRelease(filePath
);
2273 else if ( baseURL
) {
2274 result
= (struct __CFURL
*)CFRetain(baseURL
);
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
);
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
;
2290 result
= _CFURLCreateWithURLString(allocator
, urlString
, false /* checkForLegalCharacters */, baseURL
);
2292 if (encoding
!= kCFStringEncodingUTF8
) {
2293 ((struct __CFURL
*)result
)->_encoding
= encoding
;
2294 #if DEBUG_URL_MEMORY_USAGE
2295 numNonUTF8EncodedURLs
++;
2299 CFRelease(urlString
); // it's retained by result, now.
2304 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
2305 CFDataRef result
= NULL
;
2307 CFStringRef myStr
= CFURLGetString(url
);
2309 result
= CFStringCreateExternalRepresentation(allocator
, myStr
, encoding
, 0);
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
;
2319 url
= _CFURLCreateWithURLString(allocator
, URLString
, true /* checkForLegalCharacters */, baseURL
);
2324 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
2325 CFURLRef url
= NULL
;
2327 url
= _CFURLCreateWithURLString(allocator
, URLString
, false /* checkForLegalCharacters */, baseURL
);
2332 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
2333 CFURLRef result
= NULL
;
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
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.
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
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
);
2361 UInt32 absFlags
= 0;
2362 CFRange absRanges
[MAX_COMPONENTS
];
2363 uint8_t numberOfRanges
;
2364 CFStringRef absString
= NULL
;
2365 Boolean absStringIsMutable
= false;
2367 CFStringRef relativeString
;
2369 relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
2370 if ( relativeString
!= NULL
) {
2372 absString
= relativeString
;
2375 if ( CFStringGetLength(relativeString
) > 0 ) {
2376 ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
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
);
2384 baseString
= baseURL
->_string
;
2386 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
2387 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
2388 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
2389 absStringIsMutable
= true;
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));
2401 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, relRanges
, &numberOfRanges
);
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
;
2407 if (baseScheme
) CFRelease(baseScheme
);
2409 CFRetain(relativeString
);
2410 relString
= relativeString
;
2413 if (!CF_IS_OBJC(CFURLGetTypeID(), baseURL
)) {
2414 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->_ranges
);
2416 CFStringRef baseString
;
2417 UInt32 baseFlags
= 0;
2418 CFRange baseRanges
[MAX_COMPONENTS
];
2419 if (CF_IS_OBJC(CFURLGetTypeID(), baseURL
)) {
2420 baseString
= CFURLGetString(baseURL
);
2422 baseString
= baseURL
->_string
;
2424 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, baseRanges
, &numberOfRanges
);
2425 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
2427 absStringIsMutable
= true;
2429 if (relString
) CFRelease(relString
);
2431 CFRelease(relativeString
);
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
);
2450 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
2453 // Do not deallocate buf; newPath took ownership of it.
2455 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
2456 CFRelease(absString
);
2458 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
2459 #if DEBUG_URL_MEMORY_USAGE
2460 if ( encoding
!= kCFStringEncodingUTF8
) {
2461 numNonUTF8EncodedURLs
++;
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
;
2478 if (idx
!= pathStr
) {
2483 } else if (*(idx
+1) == pathDelimiter
) {
2484 if (idx
+ 2 != end
|| idx
!= pathStr
) {
2485 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
2489 // Do not delete the sole path component
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
--;
2498 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
2499 // We have a genuine component to compact out
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
);
2506 } else if (lastDelim
!= pathStr
) {
2511 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
2519 } else if (stripLeadingDotDots
) {
2520 if (idx
+ 3 != end
) {
2521 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
2522 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2526 // Do not devolve the last path component
2532 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
2535 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
2538 // return an zero-length string if end < pathStr
2539 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
>= pathStr
? end
- pathStr
: 0, alloc
);
2542 static CFMutableStringRef
resolveAbsoluteURLStringBuffer(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, const CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, const CFRange
*baseRanges
, UniChar
*buf
)
2544 CFStringAppendBuffer appendBuffer
;
2546 CFStringInitAppendBuffer(alloc
, &appendBuffer
);
2549 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
2550 if (rg
.location
!= kCFNotFound
) {
2551 CFStringGetCharacters(baseString
, rg
, buf
);
2552 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2554 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2557 if (relFlags
& NET_LOCATION_MASK
) {
2558 CFStringAppendStringToAppendBuffer(&appendBuffer
, relString
);
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
);
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
) {
2577 } else if (relPathRg
.length
== 0) {
2579 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
2581 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2585 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2586 } else if (useBasePath
) {
2587 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2589 // FIXME: Get rid of this allocation
2590 UniChar
*newPathBuf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
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
;
2599 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2601 /* Under Win32 absolute path can begin with letter
2602 * so we have to add one '/' to the newString
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
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) != '/') {
2614 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2617 CFStringAppendStringToAppendBuffer(&appendBuffer
, 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
);
2627 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2628 if (rg
.location
!= kCFNotFound
) {
2629 CFStringGetCharacters(baseString
, rg
, buf
);
2630 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2633 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2634 // ??? Can this ever happen?
2635 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2637 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2638 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2639 rg
.location
--; // To pick up the separator
2641 CFStringGetCharacters(baseString
, rg
, buf
);
2642 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
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
);
2651 // Sigh; we have to resolve these against one another
2652 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2653 if (rg
.location
!= kCFNotFound
) {
2655 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2656 CFStringGetCharacters(baseString
, rg
, buf
);
2657 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2659 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2660 if (rg
.location
!= kCFNotFound
) {
2662 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2663 CFStringGetCharacters(relString
, rg
, buf
);
2664 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2666 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2667 if (rg
.location
!= kCFNotFound
) {
2669 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2670 CFStringGetCharacters(baseString
, rg
, buf
);
2671 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
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
) {
2678 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2679 CFStringGetCharacters(relString
, rg
, buf
);
2680 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2685 return CFStringCreateMutableWithAppendBuffer(&appendBuffer
);
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
);
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
);
2704 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2705 CFURLRef anURL
, base
;
2706 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2707 CFStringRef baseString
, newString
;
2709 CFRange ranges
[MAX_COMPONENTS
];
2710 uint8_t numberOfRanges
;
2711 const CFRange
*baseRanges
;
2713 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2714 Boolean filePathURLCreated
= false;
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
);
2724 __CFGenericValidateType(relativeURL
, CFURLGetTypeID());
2726 base
= relativeURL
->_base
;
2728 return (CFURLRef
)CFRetain(relativeURL
);
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
);
2735 // could not convert file reference URL to file path URL -- fail will NULL
2738 filePathURLCreated
= true;
2742 baseIsObjC
= CF_IS_OBJC(CFURLGetTypeID(), base
);
2745 baseString
= base
->_string
;
2746 baseFlags
= base
->_flags
;
2747 baseRanges
= base
->_ranges
;
2749 baseString
= CFURLGetString(base
);
2751 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, ranges
, &numberOfRanges
);
2752 baseRanges
= ranges
;
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
++;
2765 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2766 if ( filePathURLCreated
) {
2775 /*******************/
2776 /* Basic accessors */
2777 /*******************/
2778 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2779 return url
->_encoding
;
2782 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2783 anURL
= _CFURLFromNSURL(anURL
);
2784 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2787 CFStringRef
CFURLGetString(CFURLRef url
) {
2788 CF_OBJC_FUNCDISPATCHV(CFURLGetTypeID(), CFStringRef
, (NSURL
*)url
, relativeString
);
2789 if (!_haveTestedOriginalString(url
)) {
2790 computeSanitizedString(url
);
2792 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2793 return url
->_string
;
2795 return _getSanitizedString( url
);
2799 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2800 CFIndex length
, charsConverted
, usedLength
;
2802 CFStringEncoding enc
;
2803 if (CF_IS_OBJC(CFURLGetTypeID(), url
)) {
2804 string
= CFURLGetString(url
);
2805 enc
= kCFStringEncodingUTF8
;
2807 string
= url
->_string
;
2808 enc
= url
->_encoding
;
2810 length
= CFStringGetLength(string
);
2811 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2812 if (charsConverted
!= length
) {
2819 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2820 CF_OBJC_FUNCDISPATCHV(CFURLGetTypeID(), CFURLRef
, (NSURL
*)anURL
, baseURL
);
2821 return anURL
->_base
;
2824 // Assumes the URL is already parsed
2825 static CFRange
_rangeForComponent(UInt32 flags
, const CFRange
*ranges
, UInt32 compFlag
) {
2827 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2828 while (!(compFlag
& 1)) {
2829 compFlag
= compFlag
>> 1;
2838 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2841 CFAllocatorRef alloc
= CFGetAllocator(url
);
2842 if (removePercentEscapes
) {
2843 fromOriginalString
= true;
2845 rg
= _rangeForComponent(url
->_flags
, url
->_ranges
, compFlag
);
2846 if (rg
.location
== kCFNotFound
) {
2850 if ( compFlag
& HAS_SCHEME
) {
2851 switch ( _getSchemeTypeFromFlags(url
->_flags
) ) {
2852 case kHasHttpScheme
:
2853 comp
= (CFStringRef
)CFRetain(kCFURLHTTPScheme
);
2856 case kHasHttpsScheme
:
2857 comp
= (CFStringRef
)CFRetain(kCFURLHTTPSScheme
);
2860 case kHasFileScheme
:
2861 comp
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
2864 case kHasDataScheme
:
2865 comp
= (CFStringRef
)CFRetain(kCFURLDataScheme
);
2869 comp
= (CFStringRef
)CFRetain(kCFURLFTPScheme
);
2873 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2878 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2881 if (comp
&& !fromOriginalString
) {
2882 if (!_haveTestedOriginalString(url
)) {
2883 computeSanitizedString(url
);
2885 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (_getAdditionalDataFlags(url
) & compFlag
)) {
2886 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2891 if (comp
&& removePercentEscapes
) {
2893 if (url
->_encoding
== kCFStringEncodingUTF8
) {
2894 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2896 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2906 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2908 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
2909 scheme
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, scheme
);
2915 switch ( _getSchemeTypeFromFlags(anURL
->_flags
) ) {
2916 case kHasHttpScheme
:
2917 scheme
= (CFStringRef
)CFRetain(kCFURLHTTPScheme
);
2920 case kHasHttpsScheme
:
2921 scheme
= (CFStringRef
)CFRetain(kCFURLHTTPSScheme
);
2924 case kHasFileScheme
:
2925 scheme
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
2928 case kHasDataScheme
:
2929 scheme
= (CFStringRef
)CFRetain(kCFURLDataScheme
);
2933 scheme
= (CFStringRef
)CFRetain(kCFURLFTPScheme
);
2937 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2940 scheme
= CFURLCopyScheme(anURL
->_base
);
2951 static CFRange
_netLocationRange(UInt32 flags
, const CFRange
*ranges
) {
2953 CFRange netRg
= {kCFNotFound
, 0};
2956 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
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
) {
2967 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
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
);
2979 if (!_haveTestedOriginalString(anURL
)) {
2980 computeSanitizedString(anURL
);
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.
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
;
2990 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), sanitizedString
, netRg
);
2992 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2995 } else if (anURL
->_base
) {
2996 return CFURLCopyNetLocation(anURL
->_base
);
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);
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.
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.
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;
3019 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
3021 if (isAbsolute
) *isAbsolute
= true;
3022 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
3026 if (isAbsolute
) *isAbsolute
= false;
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);
3038 return CFURLHasDirectoryPath(anURL
->_base
);
3042 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
3043 UInt32 firstRsrcSpecFlag
= 0;
3044 UInt32 flag
= HAS_FRAGMENT
;
3045 while (flag
!= HAS_PATH
) {
3047 firstRsrcSpecFlag
= flag
;
3051 return firstRsrcSpecFlag
;
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
);
3064 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
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
));
3070 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
3073 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
3075 if (firstRsrcSpecFlag
) {
3076 Boolean canUseOriginalString
= true;
3077 Boolean canUseSanitizedString
= true;
3078 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
3079 if (!_haveTestedOriginalString(anURL
)) {
3080 computeSanitizedString(anURL
);
3083 UInt32 additionalDataFlags
= _getAdditionalDataFlags(anURL
);
3084 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
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;
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;
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
);
3115 // Must compute the correct string to return; just reparse....
3116 UInt32 sanFlags
= 0;
3117 CFRange sanRanges
[MAX_COMPONENTS
];
3118 uint8_t numberOfRanges
;
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
);
3127 // The resource specifier cannot possibly come from the base.
3133 /*************************************/
3134 /* Accessors that create new objects */
3135 /*************************************/
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
) {
3140 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
3141 tmp
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, host
);
3142 if (tmp
) CFRetain(tmp
);
3145 __CFGenericValidateType(anURL
, CFURLGetTypeID());
3146 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
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));
3156 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3157 return CFURLCopyHostName(anURL
->_base
);
3163 // Return -1 to indicate no port is specified
3164 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
3166 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
3167 CFNumberRef cfPort
= (CFNumberRef
) CF_OBJC_CALLV((NSURL
*)anURL
, port
);
3169 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
3172 __CFGenericValidateType(anURL
, CFURLGetTypeID());
3173 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
3175 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
3176 CFStringInlineBuffer buf
;
3177 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
3179 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
3184 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3185 return CFURLGetPortNumber(anURL
->_base
);
3191 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
3193 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
3194 user
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, user
);
3195 if (user
) CFRetain(user
);
3198 __CFGenericValidateType(anURL
, CFURLGetTypeID());
3199 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
3202 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3203 return CFURLCopyUserName(anURL
->_base
);
3209 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
3211 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
3212 passwd
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, password
);
3213 if (passwd
) CFRetain(passwd
);
3216 __CFGenericValidateType(anURL
, CFURLGetTypeID());
3217 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
3220 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3221 return CFURLCopyPassword(anURL
->_base
);
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
3229 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
3231 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
3232 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, parameterString
);
3233 if (str
) CFRetain(str
);
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
))) {
3242 // Parameter string definitely coming from the relative portion of the URL
3244 return _unescapedParameterString( anURL
->_base
);
3247 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
3248 CFStringRef param
= _unescapedParameterString(anURL
);
3251 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
3252 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
3254 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
3262 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
3264 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
3265 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, query
);
3266 if (str
) CFRetain(str
);
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
))) {
3276 return _unescapedQueryString(anURL
->_base
);
3279 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
3280 CFStringRef query
= _unescapedQueryString(anURL
);
3283 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
3284 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
3286 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
3294 // Fragments are NEVER taken from a base URL
3295 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
3297 if (CF_IS_OBJC(CFURLGetTypeID(), anURL
)) {
3298 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, fragment
);
3299 if (str
) CFRetain(str
);
3302 __CFGenericValidateType(anURL
, CFURLGetTypeID());
3303 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
3307 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
3308 CFStringRef fragment
= _unescapedFragment(anURL
);
3311 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
3312 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
3314 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
3316 CFRelease(fragment
);
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;
3330 if (lastComponentBeforeMask
== 0) {
3331 // mask includes HAS_SCHEME
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;
3341 return schemeRg
.length
+ 3;
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
;
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;
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
;
3371 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
3374 afterMask
= currRange
->location
;
3379 if (afterMask
== kCFNotFound
) {
3380 afterMask
= maskRange
.location
+ maskRange
.length
;
3382 charRangeWithSeparators
->location
= beforeMask
;
3383 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
3387 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3389 switch (component
) {
3390 case kCFURLComponentScheme
:
3393 case kCFURLComponentNetLocation
:
3394 mask
= NET_LOCATION_MASK
;
3396 case kCFURLComponentPath
:
3399 case kCFURLComponentResourceSpecifier
:
3400 mask
= RESOURCE_SPECIFIER_MASK
;
3402 case kCFURLComponentUser
:
3405 case kCFURLComponentPassword
:
3406 mask
= HAS_PASSWORD
;
3408 case kCFURLComponentUserInfo
:
3409 mask
= HAS_USER
| HAS_PASSWORD
;
3411 case kCFURLComponentHost
:
3414 case kCFURLComponentPort
:
3417 case kCFURLComponentParameterString
:
3418 mask
= HAS_PARAMETERS
;
3420 case kCFURLComponentQuery
:
3423 case kCFURLComponentFragment
:
3424 mask
= HAS_FRAGMENT
;
3427 rangeIncludingSeparators
->location
= kCFNotFound
;
3428 rangeIncludingSeparators
->length
= 0;
3429 return CFRangeMake(kCFNotFound
, 0);
3432 if ((url
->_flags
& mask
) == 0) {
3433 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
3434 rangeIncludingSeparators
->length
= 0;
3435 return CFRangeMake(kCFNotFound
, 0);
3437 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
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;
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);
3455 rangeIncludingSeparators
->location
= schemeRg
.length
;
3456 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
3457 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
3460 rangeIncludingSeparators
->location
= kCFNotFound
;
3461 rangeIncludingSeparators
->length
= 0;
3462 return CFRangeMake(kCFNotFound
, 0);
3467 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3468 CFRange charRange
, charRangeWithSeparators
;
3470 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
3471 url
= _CFURLFromNSURL(url
);
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
);
3477 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
3480 if (charRangeWithSeparators
.location
== kCFNotFound
) {
3481 if (rangeIncludingSeparators
) {
3482 rangeIncludingSeparators
->location
= kCFNotFound
;
3483 rangeIncludingSeparators
->length
= 0;
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
));
3489 if (charRange
.location
== kCFNotFound
) {
3490 byteRange
= charRange
;
3491 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
3493 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
3494 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
3496 if (charRangeWithSeparators
.location
== charRange
.location
) {
3497 byteRange
.location
= rangeIncludingSeparators
->location
;
3500 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3501 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
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
;
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
;
3515 } else if (charRange
.location
== kCFNotFound
) {
3516 byteRange
= charRange
;
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
));
3524 /* Component support */
3526 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
3527 if ( CFURLGetBaseURL(url
) != NULL
) {
3528 components
->scheme
= NULL
;
3530 components
->scheme
= CFURLCopyScheme(url
);
3532 components
->schemeSpecific
= CFURLCopyResourceSpecifier(url
);
3536 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
3538 if (components
->scheme
) {
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
;
3550 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3558 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3559 CFAllocatorRef alloc
= CFGetAllocator(url
);
3560 static CFStringRef emptyStr
= NULL
;
3562 emptyStr
= CFSTR("");
3565 if (!CFURLCanBeDecomposed(url
)) {
3569 CFStringRef path
= CFURLCopyPath(url
);
3571 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3574 components
->pathComponents
= NULL
;
3576 components
->baseURL
= CFURLGetBaseURL(url
);
3577 if (components
->baseURL
) {
3578 CFRetain(components
->baseURL
);
3579 components
->scheme
= NULL
;
3581 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
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
);
3589 components
->port
= kCFNotFound
;
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);
3597 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3598 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3599 CFURLRef base
= comp
->baseURL
;
3601 Boolean hadPrePathComponent
= false;
3604 CFStringAppend(urlString
, comp
->scheme
);
3605 CFStringAppend(urlString
, CFSTR("://"));
3606 hadPrePathComponent
= true;
3608 if (comp
->user
|| comp
->password
) {
3610 CFStringAppend(urlString
, comp
->user
);
3612 if (comp
->password
) {
3613 CFStringAppend(urlString
, CFSTR(":"));
3614 CFStringAppend(urlString
, comp
->password
);
3616 CFStringAppend(urlString
, CFSTR("@"));
3617 hadPrePathComponent
= true;
3620 CFStringAppend(urlString
, comp
->host
);
3621 hadPrePathComponent
= true;
3623 if (comp
->port
!= kCFNotFound
) {
3624 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%ld"), (long)comp
->port
);
3625 hadPrePathComponent
= true;
3628 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFArrayGetCount( comp
->pathComponents
) == 0 || CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3629 CFStringAppend(urlString
, CFSTR("/"));
3631 if (comp
->pathComponents
) {
3632 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3633 CFStringAppend(urlString
, pathStr
);
3636 if (comp
->parameterString
) {
3637 CFStringAppend(urlString
, CFSTR(";"));
3638 CFStringAppend(urlString
, comp
->parameterString
);
3641 CFStringAppend(urlString
, CFSTR("?"));
3642 CFStringAppend(urlString
, comp
->query
);
3644 if (comp
->fragment
) {
3645 CFStringAppend(urlString
, CFSTR("#"));
3646 CFStringAppend(urlString
, comp
->fragment
);
3648 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3649 CFRelease(urlString
);
3653 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3654 CFAllocatorRef alloc
= CFGetAllocator(url
);
3655 CFURLComponentsRFC1808 oldComp
;
3657 if (!decomposeToRFC1808(url
, &oldComp
)) {
3660 comp
->scheme
= oldComp
.scheme
;
3662 if (oldComp
.password
) {
3663 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3664 CFRelease(oldComp
.password
);
3665 CFRelease(oldComp
.user
);
3667 comp
->userinfo
= oldComp
.user
;
3670 comp
->userinfo
= NULL
;
3672 comp
->host
= oldComp
.host
;
3673 comp
->port
= oldComp
.port
;
3674 if (!oldComp
.parameterString
) {
3675 comp
->pathComponents
= oldComp
.pathComponents
;
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
);
3682 CFRelease(oldComp
.pathComponents
);
3683 CFRelease(oldComp
.parameterString
);
3685 comp
->query
= oldComp
.query
;
3686 comp
->fragment
= oldComp
.fragment
;
3687 comp
->baseURL
= oldComp
.baseURL
;
3691 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3692 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3693 CFURLRef base
= comp
->baseURL
;
3695 Boolean hadPrePathComponent
= false;
3698 CFStringAppend(urlString
, comp
->scheme
);
3699 CFStringAppend(urlString
, CFSTR("://"));
3700 hadPrePathComponent
= true;
3702 if (comp
->userinfo
) {
3703 CFStringAppend(urlString
, comp
->userinfo
);
3704 CFStringAppend(urlString
, CFSTR("@"));
3705 hadPrePathComponent
= true;
3708 CFStringAppend(urlString
, comp
->host
);
3709 if (comp
->port
!= kCFNotFound
) {
3710 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%ld"), (long)comp
->port
);
3712 hadPrePathComponent
= true;
3714 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3715 CFStringAppend(urlString
, CFSTR("/"));
3717 if (comp
->pathComponents
) {
3718 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3719 CFStringAppend(urlString
, pathStr
);
3723 CFStringAppend(urlString
, CFSTR("?"));
3724 CFStringAppend(urlString
, comp
->query
);
3726 if (comp
->fragment
) {
3727 CFStringAppend(urlString
, CFSTR("#"));
3728 CFStringAppend(urlString
, comp
->fragment
);
3730 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3731 CFRelease(urlString
);
3735 #undef CFURLCopyComponents
3736 #undef CFURLCreateFromComponents
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
);
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
);
3767 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3768 // called with CFURL (not NSURL) from Foundation
3769 return _getReserved(url
);
3772 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3773 // called with CFURL (not NSURL) from Foundation
3774 _setReserved ( (struct __CFURL
*) url
, ptr
);
3777 CF_EXPORT
void *__CFURLResourceInfoPtr(CFURLRef url
) {
3779 url
= _CFURLFromNSURL(url
);
3780 return _getResourceInfo(url
);
3787 CF_EXPORT
void __CFURLSetResourceInfoPtr(CFURLRef url
, void *ptr
) {
3789 url
= _CFURLFromNSURL(url
);
3790 _setResourceInfo ( (struct __CFURL
*) url
, ptr
);
3794 /* File system stuff */
3796 /* HFSPath<->URLPath functions at the bottom of the file */
3797 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
) {
3799 CFMutableArrayRef urlComponents
= NULL
;
3802 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3803 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3806 CFStringRef str
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, 0);
3807 if (isAbsolute
&& CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3808 i
= 1; // Skip over the drive letter
3811 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3812 CFStringRef fileComp
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
,i
);
3813 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3815 // Couldn't decode fileComp
3816 CFRelease(urlComponents
);
3819 if (urlComp
!= fileComp
) {
3820 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3826 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3827 CFArrayAppendValue(urlComponents
, CFSTR(""));
3830 if ( AddAuthorityToFileURL() ) {
3831 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY
));
3834 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR(FILE_PREFIX
));
3837 return urlComponents
;
3840 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
) {
3841 CFArrayRef urlComponents
;
3844 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3845 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
, isAbsolute
);
3846 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3848 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3849 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3850 CFRelease(urlComponents
);
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
);
3862 if ( posixAndUrlPathsMatch
) {
3863 *posixAndUrlPathsMatch
= !addedPercentEncoding
;
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
);
3872 CFIndex length
= CFStringGetLength(result
);
3873 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3874 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
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
)
3885 size_t fileURLPrefixLength
;
3886 if ( AddAuthorityToFileURL() ) {
3887 fileURLPrefixLength
= sizeof(fileURLPrefixWithAuthority
);
3890 fileURLPrefixLength
= sizeof(fileURLPrefix
);
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
3896 CFIndex strLength
= CFStringGetLength(str
) - (fileURLPrefixLength
- 1);
3897 if ( strLength
!= 0 ) {
3898 CFIndex maxBufLength
= strLength
* 3;
3900 CFIndex charsConverted
;
3901 if ( strLength
<= PATH_MAX
) {
3902 escapedBuf
= &stackEscapedBuf
[0];
3905 // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3906 escapedBuf
= (UInt8
*)malloc(maxBufLength
);
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,
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,
3949 const UInt8
*bytePtr
= escapedBuf
;
3951 Boolean trailingSlash
= false;
3953 bufPtr
= bufStartPtr
= inBuffer
;
3954 bufEndPtr
= inBuffer
+ inBufferLen
;
3957 for ( idx
= 0; (idx
< usedBufLen
) && result
; ++idx
) {
3958 if ( bufPtr
== bufEndPtr
) {
3959 // ooops, ran out of inBuffer
3960 *bufStartPtr
= '\0';
3964 switch ( *bytePtr
) {
3967 if ( idx
< usedBufLen
) {
3970 // convert hex digits
3971 *bufPtr
= hexvalues
[*bytePtr
++] << 4;
3972 *bufPtr
+= hexvalues
[*bytePtr
++];
3973 trailingSlash
= (*bufPtr
== '/');
3977 // copy everything else
3978 *bufPtr
= *bytePtr
++;
3979 trailingSlash
= (*bufPtr
== '/');
3986 // remove trailing slash (if any)
3987 if ( (bufPtr
> (bufStartPtr
+ 1)) && trailingSlash
) {
3990 if ( bufPtr
< bufEndPtr
) {
3996 // CFStringGetBytes failed
4000 // free the buffer if we malloc'd it
4001 if ( escapedBuf
!= &stackEscapedBuf
[0] ) {
4006 // could not allocate escapedBuf
4011 // str was zero characters
4017 // no inBuffer or inBufferLen is zero
4025 #if DEPLOYMENT_TARGET_WINDOWS
4026 // From CFPlatform.c
4027 extern CFStringRef
CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr
);
4030 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
4031 // Check for a drive letter, then flip all the slashes
4033 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
4034 SInt32 count
= CFArrayGetCount(tmp
);
4035 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
4036 CFStringRef newPath
;
4041 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
4042 CFArrayRemoveValueAtIndex(components
, count
-1);
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
);
4052 if (firstComponent
) {
4053 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
4055 CFArrayRemoveValueAtIndex(components
, 0);
4057 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
4058 CFArraySetValueAtIndex(components
, 0, driveStr
);
4059 CFRelease(driveStr
);
4062 #if DEPLOYMENT_TARGET_WINDOWS
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
);
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
;
4077 // replace the first component of the path with the drive path
4078 CFArrayRemoveValueAtIndex(components
, 0);
4079 CFArraySetValueAtIndex(components
, 0, driveRootPath
);
4081 CFRelease(driveRootPath
);
4087 if ( firstComponent
) {
4088 CFRelease(firstComponent
);
4091 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
4092 CFRelease(components
);
4093 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
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
);
4108 if (buf
[baseLen
-1] != pathDelimiter
) {
4109 buf
[baseLen
] = pathDelimiter
;
4113 UniChar
*ptr
= buf
+ baseLen
- 1;
4114 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
4117 baseLen
= ptr
- buf
+ 1;
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.
4126 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
4127 *(buf
+ baseLen
+ relLen
) = '\0';
4128 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
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);
4140 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
4143 result
= _CFURLCreateWithFileSystemPath(allocator
, filePath
, fsType
, isDirectory
, NULL
);
4148 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
4151 result
= _CFURLCreateWithFileSystemPath(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
4156 static Boolean
_pathHasFileIDPrefix( CFStringRef path
)
4158 // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
4159 #ifdef __CONSTANT_STRINGS__
4162 CFStringRef fileIDPrefix
= CFSTR( "/" FILE_ID_PREFIX
"/" );
4163 return path
&& CFStringHasPrefix( path
, fileIDPrefix
) && CFStringGetLength( path
) > CFStringGetLength( fileIDPrefix
);
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
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;
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);
4191 // fall back to slower way.
4192 result
= CFURLCreateStringWithFileSystemPath(alloc
, anURL
, pathStyle
, false);
4194 #else // !DEPLOYMENT_TARGET_MACOSX
4195 result
= CFURLCreateStringWithFileSystemPath(alloc
, anURL
, pathStyle
, false);
4196 #endif // !DEPLOYMENT_TARGET_MACOSX
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
;
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);
4217 if (relPath
== NULL
) {
4218 CFStringRef urlPath
= CFURLCopyPath(anURL
);
4219 CFStringEncoding enc
= anURL
->_encoding
;
4222 case kCFURLPOSIXPathStyle
:
4223 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
4225 #pragma GCC diagnostic push
4226 #pragma GCC diagnostic ignored "-Wdeprecated"
4227 case kCFURLHFSPathStyle
:
4228 #pragma GCC diagnostic pop
4231 case kCFURLWindowsPathStyle
:
4232 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
4235 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
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));
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
);
4262 // we only have the relPath or relpath is absolute -- return it
4264 CFRelease(basePath
);
4269 else if ( basePath
) {
4270 // we only have the basePath --- return it
4274 // we have nothing to return
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
);
4284 if (!url
) return false;
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
);
4295 // else fall back to slower way.
4296 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
4298 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
4302 #elif DEPLOYMENT_TARGET_WINDOWS
4303 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
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.
4309 if (numConverted
== pathLen
) {
4310 buffer
[usedLen
] = '\0';
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
) {
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);
4331 if (charsConverted
!= pathLength
|| usedBufLen%sizeof
(wchar_t) != 0) {
4334 buffer
[usedBufLen
/sizeof(wchar_t)] = 0;
4335 // buffer[pathLength] = 0;
4341 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
4344 result
= _CFURLCreateWithFileSystemRepresentation(allocator
, buffer
, bufLen
, isDirectory
, NULL
);
4349 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
4352 result
= _CFURLCreateWithFileSystemRepresentation(allocator
, buffer
, bufLen
, isDirectory
, baseURL
);
4358 /******************************/
4359 /* Support for path utilities */
4360 /******************************/
4362 // Assumes url is a CFURL (not an Obj-C NSURL)
4363 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
4364 CFRange pathRg
, componentRg
;
4366 pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
4368 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
4372 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == '/') {
4374 if (pathRg
.length
== 0) {
4379 if (CFStringFindWithOptions(url
->_string
, CFSTR("/"), pathRg
, kCFCompareBackwards
, &componentRg
)) {
4380 componentRg
.location
++;
4381 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
4383 componentRg
= pathRg
;
4388 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
4391 if (CF_IS_OBJC(CFURLGetTypeID(), url
)) {
4392 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLPOSIXPathStyle
, false);
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) == '/' ) {
4402 if ( rg
.length
== 0 )
4404 // If we have reduced the string to empty, then it's "/", and that's what we return as
4405 // the last path component.
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;
4412 if (rg
.location
== 0 && rg
.length
== length
) {
4415 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), path
, rg
);
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
;
4434 CFRange rg
= _rangeOfLastPathComponent(url
);
4435 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
4437 if ( filePathURLCreated
) {
4440 return (CFStringRef
)CFRetain(CFSTR(""));
4442 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == '/') {
4443 if ( filePathURLCreated
) {
4446 return (CFStringRef
)CFRetain(CFSTR("/"));
4448 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
4449 if (!(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
4451 if (url
->_encoding
== kCFStringEncodingUTF8
) {
4452 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
4454 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
4459 if ( filePathURLCreated
) {
4466 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
4467 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
4468 CFStringRef ext
= NULL
;
4471 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
4472 if (rg
.location
!= kCFNotFound
) {
4474 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
4475 if (rg
.length
> 0) {
4476 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
4478 ext
= (CFStringRef
)CFRetain(CFSTR(""));
4481 CFRelease(lastPathComp
);
4486 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
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__
);
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
;
4504 CFMutableStringRef newString
;
4505 CFStringRef newComp
;
4507 if (!(url
->_flags
& HAS_PATH
)) {
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("/"));
4518 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
4520 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
4523 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4524 CFRelease(newString
);
4526 if ( filePathURLCreated
) {
4532 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
4534 CFMutableStringRef newString
;
4535 CFRange lastCompRg
, pathRg
;
4536 Boolean appendDotDot
= false;
4538 url
= _CFURLFromNSURL(url
);
4539 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4540 __CFGenericValidateType(url
, CFURLGetTypeID());
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
;
4557 if (!(url
->_flags
& HAS_PATH
)) {
4558 if ( filePathURLCreated
) {
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;
4572 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
4573 appendDotDot
= true;
4576 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4579 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
4580 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4583 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
4585 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR("/"));
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));
4595 } else if (lastCompRg
.location
== pathRg
.location
) {
4596 CFStringReplace(newString
, pathRg
, CFSTR("."));
4597 CFStringInsert(newString
, 1, CFSTR("/"));
4599 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
4601 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4602 CFRelease(newString
);
4603 if ( filePathURLCreated
) {
4609 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
4610 CFMutableStringRef newString
;
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());
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
;
4634 rg
= _rangeOfLastPathComponent(url
);
4635 if (rg
.location
< 0) {
4636 if ( filePathURLCreated
) {
4639 return NULL
; // No path
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
);
4647 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4648 CFRelease(newString
);
4649 if ( filePathURLCreated
) {
4655 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4659 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4660 url
= _CFURLFromNSURL(url
);
4661 __CFGenericValidateType(url
, CFURLGetTypeID());
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
;
4678 rg
= _rangeOfLastPathComponent(url
);
4679 if (rg
.location
< 0) {
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
);
4688 result
= (CFURLRef
)CFRetain(url
);
4690 if ( filePathURLCreated
) {
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
);
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
);
4718 vals
[1] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), url
->_string
, '/');
4721 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != '/') {
4722 vals
[1] = CFRetain(url
->_string
);
4724 vals
[1] = CFStringCreateWithSubstring(alloc
, url
->_string
, CFRangeMake(0, CFStringGetLength(url
->_string
) - 1));
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
);
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
);
4740 vals
[3] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), base
->_string
, '/');
4743 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) != '/') {
4744 vals
[3] = CFRetain(base
->_string
);
4746 vals
[3] = CFStringCreateWithSubstring(alloc
, base
->_string
, CFRangeMake(0, CFStringGetLength(base
->_string
) - 1));
4756 // Private API for Finder to use
4757 CFPropertyListRef
_CFURLCopyPropertyListRepresentation(CFURLRef url
) {
4758 CFTypeRef keys
[4], vals
[4];
4759 CFDictionaryRef dict
;
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
]);
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
;
4776 // Start by getting all the pieces and verifying they're of the correct type.
4777 if (CFGetTypeID(pListRepresentation
) != CFDictionaryGetTypeID()) {
4780 string
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLString"));
4781 if (!string
|| CFGetTypeID(string
) != CFStringGetTypeID()) {
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
4791 baseString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseURLString"));
4793 if (CFGetTypeID(baseString
) != CFStringGetTypeID()) {
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
4804 if (baseType
== FULL_URL_REPRESENTATION
) {
4805 baseURL
= _CFURLCreateWithArbitraryString(alloc
, baseString
, NULL
);
4807 baseURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, baseString
, (CFURLPathStyle
)baseType
, CFStringGetCharacterAtIndex(baseString
, CFStringGetLength(baseString
)-1) == '/', NULL
);
4810 if (urlType
== FULL_URL_REPRESENTATION
) {
4811 url
= _CFURLCreateWithArbitraryString(alloc
, string
, baseURL
);
4813 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, string
, (CFURLPathStyle
)urlType
, CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
)-1) == '/', baseURL
);
4815 if (baseURL
) CFRelease(baseURL
);
4819 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4820 Boolean
_CFURLIsFileReferenceURL(CFURLRef url
)
4822 return ( CFURLIsFileReferenceURL(url
) );
4825 Boolean
CFURLIsFileReferenceURL(CFURLRef url
)
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
);
4831 result
= CFURLIsFileReferenceURL(baseURL
);
4834 if ( CF_IS_OBJC(CFURLGetTypeID(), url
) ) {
4835 result
= (Boolean
) CF_OBJC_CALLV((NSURL
*)url
, isFileReferenceURL
);
4838 result
= ((_getSchemeTypeFromFlags(url
->_flags
) == kHasFileScheme
) && ((url
->_flags
& PATH_HAS_FILE_ID
) != 0));
4844 static Boolean
_CFURLHasFileURLScheme(CFURLRef url
, Boolean
*hasScheme
)
4847 CFURLRef baseURL
= CFURLGetBaseURL(url
);
4850 result
= _CFURLHasFileURLScheme(baseURL
, hasScheme
);
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
);
4857 if ( scheme
== kCFURLFileScheme
) {
4861 result
= CFStringCompare(scheme
, kCFURLFileScheme
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
;
4877 *hasScheme
= (url
->_flags
& HAS_SCHEME
) != 0;
4879 result
= (_getSchemeTypeFromFlags(url
->_flags
) == kHasFileScheme
);
4885 Boolean
_CFURLIsFileURL(CFURLRef url
)
4887 Boolean result
= _CFURLHasFileURLScheme(url
, NULL
);
4891 CFURLRef
CFURLCreateFilePathURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
)
4893 CFURLRef result
= NULL
;
4895 if (!_CFURLHasFileURLScheme(url
, &hasScheme
)) {
4897 CFLog(kCFLogLevelWarning
, CFSTR("CFURLCreateFilePathURL failed because it was passed this URL which has no scheme: %@"), url
);
4900 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLReadUnsupportedSchemeError
, NULL
);
4904 // File URL. Form of the path is unknown. Make a new URL.
4905 CFStringRef newURLString
;
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
);
4917 fsPath
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLPOSIXPathStyle
, false);
4918 netLoc
= CFURLCopyNetLocation( url
);
4919 rSpec
= CFURLCopyResourceSpecifier( url
);
4922 CFStringRef urlPath
= _replacePathIllegalCharacters( fsPath
, alloc
, true );
4924 CFStringAppendBuffer appendBuffer
;
4925 CFStringInitAppendBuffer(alloc
, &appendBuffer
);
4926 CFStringAppendStringToAppendBuffer(&appendBuffer
, CFSTR(FILE_PREFIX
));
4928 CFStringAppendStringToAppendBuffer(&appendBuffer
, netLoc
);
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);
4937 CFStringAppendStringToAppendBuffer(&appendBuffer
, rSpec
);
4939 newURLString
= CFStringCreateMutableWithAppendBuffer(&appendBuffer
);
4941 result
= CFURLCreateWithString( alloc
, newURLString
, NULL
);
4942 CFRelease( newURLString
);
4943 CFRelease( urlPath
);
4944 CFRelease( fsPath
);
4947 // Would be better here to get an underlying error back from CFURLCreateStringWithFileSystemPath
4948 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLNoSuchResourceError
, NULL
);
4953 CFRelease( netLoc
);
4965 CFURLRef
CFURLCreateFileReferenceURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
) { return NULL
; }