2 * Copyright (c) 2012 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-2011, Apple Inc. All rights reserved.
26 Responsibility: John Iarocci
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_LINUX
40 #if DEPLOYMENT_TARGET_MACOSX
41 #include <CoreFoundation/CFNumberFormatter.h>
45 #include <sys/types.h>
46 #include <CoreFoundation/CFURLPriv.h>
49 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
50 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
52 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
53 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
54 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
);
55 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
56 CF_EXPORT CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
57 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
58 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
63 #ifndef DEBUG_URL_MEMORY_USAGE
64 #define DEBUG_URL_MEMORY_USAGE 0
67 #if DEBUG_URL_MEMORY_USAGE
68 static CFAllocatorRef URLAllocator
= NULL
;
69 static UInt32 numFileURLsCreated
= 0;
70 static UInt32 numFileURLsConverted
= 0;
71 static UInt32 numFileURLsDealloced
= 0;
72 static UInt32 numURLs
= 0;
73 static UInt32 numDealloced
= 0;
74 static UInt32 numExtraDataAllocated
= 0;
75 static UInt32 numURLsWithBaseURL
= 0;
76 static UInt32 numNonUTF8EncodedURLs
= 0;
79 /* The bit flags in myURL->_flags */
80 #define HAS_SCHEME (0x0001)
81 #define HAS_USER (0x0002)
82 #define HAS_PASSWORD (0x0004)
83 #define HAS_HOST (0x0008)
84 #define HAS_PORT (0x0010)
85 #define HAS_PATH (0x0020)
86 #define HAS_PARAMETERS (0x0040)
87 #define HAS_QUERY (0x0080)
88 #define HAS_FRAGMENT (0x0100)
89 #define HAS_HTTP_SCHEME (0x0200)
90 // Last free bit (0x200) in lower word goes here!
91 #define IS_IPV6_ENCODED (0x0400)
92 #define IS_OLD_UTF8_STYLE (0x0800)
93 #define IS_DIRECTORY (0x1000)
94 #define IS_PARSED (0x2000)
95 #define IS_ABSOLUTE (0x4000)
96 #define IS_DECOMPOSABLE (0x8000)
98 #define PATH_TYPE_MASK (0x000F0000)
99 /* POSIX_AND_URL_PATHS_MATCH will only be true if the URL and POSIX paths are identical, character for character, except for the presence/absence of a trailing slash on directories */
100 #define POSIX_AND_URL_PATHS_MATCH (0x00100000)
101 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000)
103 /* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */
104 // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
105 // #define SCHEME_DIFFERS (0x00400000) unused
106 #define USER_DIFFERS (0x00800000)
107 #define PASSWORD_DIFFERS (0x01000000)
108 #define HOST_DIFFERS (0x02000000)
109 // 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
110 #define PORT_DIFFERS (0x04000000)
111 // #define PATH_DIFFERS (0x08000000) unused
112 // #define PARAMETERS_DIFFER (0x10000000) unused
113 // #define QUERY_DIFFERS (0x20000000) unused
114 #define PATH_HAS_FILE_ID (0x40000000)
115 #define HAS_FILE_SCHEME (0x80000000)
117 // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag
118 #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22)
120 // Other useful defines
121 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
122 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
123 #define FULL_URL_REPRESENTATION (0xF)
125 /* URL_PATH_TYPE(anURL) will be one of the CFURLPathStyle constants, in which case string is a file system path, or will be FULL_URL_REPRESENTATION, in which case the string is the full URL string. One caveat - string always has a trailing path delimiter if the url is a directory URL. This must be stripped before returning file system representations! */
126 #define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16)
127 #define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/'))
128 #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/")))
130 #define FILE_ID_PREFIX ".file"
131 #define FILE_ID_KEY "id"
132 #define FILE_ID_PREAMBLE "/.file/id="
133 #define FILE_ID_PREAMBLE_LENGTH 10
135 #define ASSERT_CHECK_PATHSTYLE(x) 0
137 #if DEPLOYMENT_TARGET_WINDOWS
138 #define PATH_SEP '\\'
139 #define PATH_MAX MAX_PATH
144 // In order to reduce the sizeof ( __CFURL ), move these items into a seperate structure which is
145 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
146 // either a sanitized string or a reserved pointer for URLHandle.
147 struct _CFURLAdditionalData
{
148 void *_reserved
; // Reserved for URLHandle's use.
149 CFMutableStringRef _sanitizedString
; // The fully compliant RFC string. This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false. This should never be mutated except when the sanatized string is first computed
150 CFHashCode hashValue
;
154 CFRuntimeBase _cfBase
;
156 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set.
157 CFStringRef _string
; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above)
160 struct _CFURLAdditionalData
* extra
;
161 void *_resourceInfo
; // For use by CarbonCore to cache property values. Retained and released by CFURL.
165 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
167 if ( url
&& url
->extra
)
168 return url
->extra
->_reserved
;
173 CF_INLINE CFMutableStringRef
_getSanitizedString ( const struct __CFURL
* url
)
175 if ( url
&& url
->extra
)
176 return url
->extra
->_sanitizedString
;
181 static void* _getResourceInfo ( const struct __CFURL
* url
)
184 return url
->_resourceInfo
;
190 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
192 if ( url
&& ! url
->extra
)
193 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), __kCFAllocatorGCScannedMemory
);
195 extra
->_reserved
= _getReserved( url
);
196 extra
->_sanitizedString
= _getSanitizedString( url
);
197 extra
->hashValue
= 0;
201 #if DEBUG_URL_MEMORY_USAGE
202 numExtraDataAllocated
++;
207 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
211 // Don't allocate extra space if we're just going to be storing NULL
212 if ( ! url
->extra
&& reserved
)
213 _CFURLAllocateExtraDataspace( url
);
216 __CFAssignWithWriteBarrier((void **)&url
->extra
->_reserved
, reserved
);
220 CF_INLINE
void _setSanitizedString ( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
224 // Don't allocate extra space if we're just going to be storing NULL
225 if ( ! url
->extra
&& sanitizedString
)
226 _CFURLAllocateExtraDataspace( url
);
229 url
->extra
->_sanitizedString
= sanitizedString
;
233 static void _setResourceInfo ( struct __CFURL
* url
, void* resourceInfo
)
237 if ( url
&& OSAtomicCompareAndSwapPtrBarrier( NULL
, resourceInfo
, &url
->_resourceInfo
)) {
238 CFRetain( resourceInfo
);
242 static void _convertToURLRepresentation(struct __CFURL
*url
);
243 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
);
244 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
245 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
246 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
247 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
248 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
249 static void computeSanitizedString(CFURLRef url
);
250 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
251 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
252 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
255 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
256 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->ranges
));
259 static Boolean _createOldUTF8StyleURLs
= false;
261 CF_INLINE Boolean
createOldUTF8StyleURLs(void) {
262 return (_createOldUTF8StyleURLs
);
265 // Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW
267 void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs
) {
268 _createOldUTF8StyleURLs
= createUTF8URLs
;
279 static const unsigned char sURLValidCharacters
[] = {
281 /* '!' 33 */ VALID
| UNRESERVED
| PATHVALID
,
284 /* '$' 36 */ VALID
| PATHVALID
,
286 /* '&' 38 */ VALID
| PATHVALID
,
287 /* ''' 39 */ VALID
| UNRESERVED
| PATHVALID
,
288 /* '(' 40 */ VALID
| UNRESERVED
| PATHVALID
,
289 /* ')' 41 */ VALID
| UNRESERVED
| PATHVALID
,
290 /* '*' 42 */ VALID
| UNRESERVED
| PATHVALID
,
291 /* '+' 43 */ VALID
| SCHEME
| PATHVALID
,
292 /* ',' 44 */ VALID
| PATHVALID
,
293 /* '-' 45 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
294 /* '.' 46 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
295 /* '/' 47 */ VALID
| PATHVALID
,
296 /* '0' 48 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
297 /* '1' 49 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
298 /* '2' 50 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
299 /* '3' 51 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
300 /* '4' 52 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
301 /* '5' 53 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
302 /* '6' 54 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
303 /* '7' 55 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
304 /* '8' 56 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
305 /* '9' 57 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
309 /* '=' 61 */ VALID
| PATHVALID
,
313 /* 'A' 65 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
314 /* 'B' 66 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
315 /* 'C' 67 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
316 /* 'D' 68 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
317 /* 'E' 69 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
318 /* 'F' 70 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
319 /* 'G' 71 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
320 /* 'H' 72 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
321 /* 'I' 73 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
322 /* 'J' 74 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
323 /* 'K' 75 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
324 /* 'L' 76 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
325 /* 'M' 77 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
326 /* 'N' 78 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
327 /* 'O' 79 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
328 /* 'P' 80 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
329 /* 'Q' 81 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
330 /* 'R' 82 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
331 /* 'S' 83 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
332 /* 'T' 84 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
333 /* 'U' 85 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
334 /* 'V' 86 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
335 /* 'W' 87 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
336 /* 'X' 88 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
337 /* 'Y' 89 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
338 /* 'Z' 90 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
343 /* '_' 95 */ VALID
| UNRESERVED
| PATHVALID
,
345 /* 'a' 97 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
346 /* 'b' 98 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
347 /* 'c' 99 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
348 /* 'd' 100 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
349 /* 'e' 101 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
350 /* 'f' 102 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
351 /* 'g' 103 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
352 /* 'h' 104 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
353 /* 'i' 105 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
354 /* 'j' 106 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
355 /* 'k' 107 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
356 /* 'l' 108 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
357 /* 'm' 109 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
358 /* 'n' 110 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
359 /* 'o' 111 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
360 /* 'p' 112 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
361 /* 'q' 113 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
362 /* 'r' 114 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
363 /* 's' 115 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
364 /* 't' 116 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
365 /* 'u' 117 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
366 /* 'v' 118 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
367 /* 'w' 119 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
368 /* 'x' 120 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
369 /* 'y' 121 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
370 /* 'z' 122 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
374 /* '~' 126 */ VALID
| UNRESERVED
| PATHVALID
,
378 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
379 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & VALID
) : false;
382 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
383 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & SCHEME
) : false;
386 // "Unreserved" as defined by RFC 2396
387 CF_INLINE Boolean
isUnreservedCharacter(UniChar ch
) {
388 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & UNRESERVED
) : false;
391 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
392 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & PATHVALID
) : false;
395 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
396 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & HEXDIGIT
) : false;
399 // Returns false if ch1 or ch2 isn't properly formatted
400 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
402 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
403 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
404 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
407 *result
= (*result
) << 4;
408 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
409 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
410 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
416 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
417 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
420 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
421 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
422 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
423 CFAllocatorRef alloc
= CFGetAllocator(array
);
424 CFMutableArrayRef mArray
= NULL
;
425 CFIndex i
, c
= CFArrayGetCount(array
);
426 for (i
= 0; i
< c
; i
++) {
427 CFStringRef origComp
= (CFStringRef
)CFArrayGetValueAtIndex(array
, i
);
428 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
429 if (!unescapedComp
) {
432 if (unescapedComp
!= origComp
) {
434 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
436 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
438 CFRelease(unescapedComp
);
441 if (mArray
) CFRelease(mArray
);
452 // 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.
453 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
454 if (preserveSlashes
) {
455 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?"), kCFStringEncodingUTF8
);
457 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
461 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
462 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
463 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
467 // We have 2 UniChars of a surrogate; we must convert to the correct percent-encoded UTF8 string and append to str. Added so that file system URLs can always be converted from POSIX to full URL representation. -- REW, 8/20/2001
468 static Boolean
_hackToConvertSurrogates(UniChar highChar
, UniChar lowChar
, CFMutableStringRef str
) {
469 UniChar surrogate
[2];
470 uint8_t bytes
[6]; // Aki sez it should never take more than 6 bytes
473 surrogate
[0] = highChar
;
474 surrogate
[1] = lowChar
;
475 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &len
) != kCFStringEncodingConversionSuccess
) {
478 for (currByte
= bytes
; currByte
< bytes
+ len
; currByte
++) {
479 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
480 unsigned char high
, low
;
481 high
= ((*currByte
) & 0xf0) >> 4;
482 low
= (*currByte
) & 0x0f;
483 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
484 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
485 CFStringAppendCharacters(str
, escapeSequence
, 3);
490 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
491 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
492 uint8_t *bytePtr
= bytes
, *currByte
;
494 CFAllocatorRef alloc
= NULL
;
495 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
496 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
497 if (byteLength
<= 6) {
498 // The encoding cannot accomodate the character
501 alloc
= CFGetAllocator(str
);
502 bytePtr
= (uint8_t *)CFAllocatorAllocate(alloc
, byteLength
, 0);
503 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
504 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
508 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
509 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
510 unsigned char high
, low
;
511 high
= ((*currByte
) & 0xf0) >> 4;
512 low
= (*currByte
) & 0x0f;
513 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
514 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
515 CFStringAppendCharacters(str
, escapeSequence
, 3);
517 if (bytePtr
!= bytes
) {
518 CFAllocatorDeallocate(alloc
, bytePtr
);
523 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
524 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
525 CFMutableStringRef newStr
= NULL
;
528 CFRange percentRange
, searchRange
;
529 CFStringRef escapedStr
= NULL
;
530 CFMutableStringRef strForEscapedChar
= NULL
;
532 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
533 Boolean failed
= false;
535 if (!originalString
) return NULL
;
537 if (charactersToLeaveEscaped
== NULL
) {
538 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
541 length
= CFStringGetLength(originalString
);
542 searchRange
= CFRangeMake(0, length
);
544 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
545 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
546 uint8_t numBytesExpected
;
550 // Make sure we have at least 2 more characters
551 if (length
- percentRange
.location
< 3) { failed
= true; break; }
553 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
554 // so we assume the percent character is legit, and let it pass into the string
555 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
556 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
557 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
558 if (!(bytes
[0] & 0x80)) {
559 numBytesExpected
= 1;
560 } else if (!(bytes
[0] & 0x20)) {
561 numBytesExpected
= 2;
562 } else if (!(bytes
[0] & 0x10)) {
563 numBytesExpected
= 3;
565 numBytesExpected
= 4;
567 if (numBytesExpected
== 1) {
568 // one byte sequence (most common case); handle this specially
569 escapedChar
= bytes
[0];
570 if (!strForEscapedChar
) {
571 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
573 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
576 // Make sure up front that we have enough characters
577 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
578 for (j
= 1; j
< numBytesExpected
; j
++) {
579 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
580 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
581 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
582 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
585 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
586 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
589 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
590 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
591 escapedChar
= 0xfeff;
592 if (!strForEscapedChar
) {
593 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
595 CFRelease(escapedStr
);
596 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
601 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
602 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
603 searchRange
.length
= length
- searchRange
.location
;
606 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
608 CFRelease(escapedStr
);
616 newStr
= CFStringCreateMutable(alloc
, length
);
618 if (percentRange
.location
- mark
> 0) {
619 // The creation of this temporary string is unfortunate.
620 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
621 CFStringAppend(newStr
, substring
);
622 CFRelease(substring
);
624 CFStringAppend(newStr
, escapedStr
);
626 CFRelease(escapedStr
);
629 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
632 if (escapedStr
) CFRelease(escapedStr
);
633 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
635 if (newStr
) CFRelease(newStr
);
639 // Need to cat on the remainder of the string
640 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
641 CFStringAppend(newStr
, substring
);
642 CFRelease(substring
);
646 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
651 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
652 if (enc
== kCFStringEncodingUTF8
) {
653 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
655 CFMutableStringRef newStr
= NULL
;
656 CFMutableStringRef escapedStr
= NULL
;
659 CFRange percentRange
, searchRange
;
660 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
661 Boolean failed
= false;
662 uint8_t byteBuffer
[8];
663 uint8_t *bytes
= byteBuffer
;
664 int capacityOfBytes
= 8;
666 if (!originalString
) return NULL
;
668 if (charactersToLeaveEscaped
== NULL
) {
669 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
672 length
= CFStringGetLength(originalString
);
673 searchRange
= CFRangeMake(0, length
);
675 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
677 CFIndex percentLoc
= percentRange
.location
;
678 CFStringRef convertedString
;
679 int numBytesUsed
= 0;
681 // Make sure we have at least 2 more characters
682 if (length
- percentLoc
< 3) { failed
= true; break; }
684 if (numBytesUsed
== capacityOfBytes
) {
685 if (bytes
== byteBuffer
) {
686 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
687 memmove(bytes
, byteBuffer
, capacityOfBytes
);
688 capacityOfBytes
= 16;
690 void *oldbytes
= bytes
;
691 int oldcap
= capacityOfBytes
;
692 capacityOfBytes
= 2*capacityOfBytes
;
693 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, capacityOfBytes
* sizeof(uint8_t), 0);
694 memmove(bytes
, oldbytes
, oldcap
);
695 CFAllocatorDeallocate(alloc
, oldbytes
);
699 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
701 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
703 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
705 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
706 searchRange
.location
= percentLoc
;
707 searchRange
.length
= length
- searchRange
.location
;
710 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
711 if (!convertedString
) {
717 newStr
= CFStringCreateMutable(alloc
, length
);
719 if (percentRange
.location
- mark
> 0) {
720 // The creation of this temporary string is unfortunate.
721 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
722 CFStringAppend(newStr
, substring
);
723 CFRelease(substring
);
727 CFStringAppend(newStr
, convertedString
);
729 CFIndex i
, c
= CFStringGetLength(convertedString
);
731 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
733 for (i
= 0; i
< c
; i
++) {
734 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
735 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
736 CFStringAppendCharacters(newStr
, &ch1
, 1);
738 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
739 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
743 CFRelease(convertedString
);
744 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
747 if (escapedStr
) CFRelease(escapedStr
);
748 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
750 if (newStr
) CFRelease(newStr
);
754 // Need to cat on the remainder of the string
755 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
756 CFStringAppend(newStr
, substring
);
757 CFRelease(substring
);
761 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
767 static CFStringRef
_addPercentEscapesToString(CFAllocatorRef allocator
, CFStringRef originalString
, Boolean (*shouldReplaceChar
)(UniChar
, void*), CFIndex (*handlePercentChar
)(CFIndex
, CFStringRef
, CFStringRef
*, void *), CFStringEncoding encoding
, void *context
) {
768 CFMutableStringRef newString
= NULL
;
770 CFStringInlineBuffer buf
;
772 if (!originalString
) return NULL
;
773 length
= CFStringGetLength(originalString
);
774 if (length
== 0) return (CFStringRef
)CFStringCreateCopy(allocator
, originalString
);
775 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
777 for (idx
= 0; idx
< length
; idx
++) {
778 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
779 Boolean shouldReplace
= shouldReplaceChar(ch
, context
);
781 // Perform the replacement
783 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
784 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
786 if (!_appendPercentEscapesForCharacter(ch
, encoding
, newString
)) {
787 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
788 if (encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1))) {
789 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
790 if (_hackToConvertSurrogates(ch
, CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1), newString
)) {
791 idx
++; // We consumed 2 characters, not 1
799 } else if (ch
== '%' && handlePercentChar
) {
800 CFStringRef replacementString
= NULL
;
801 CFIndex newIndex
= handlePercentChar(idx
, originalString
, &replacementString
, context
);
804 } else if (replacementString
) {
806 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
807 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
809 CFStringAppend(newString
, replacementString
);
810 CFRelease(replacementString
);
812 if (newIndex
== idx
) {
814 CFStringAppendCharacters(newString
, &ch
, 1);
817 if (!replacementString
&& newString
) {
819 for (tmpIndex
= idx
; tmpIndex
< newIndex
; tmpIndex
++) {
820 ch
= CFStringGetCharacterAtIndex(originalString
, idx
);
821 CFStringAppendCharacters(newString
, &ch
, 1);
826 } else if (newString
) {
827 CFStringAppendCharacters(newString
, &ch
, 1);
831 // Ran in to an encoding failure
832 if (newString
) CFRelease(newString
);
834 } else if (newString
) {
837 return (CFStringRef
)CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
842 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
843 CFIndex i
, c
= CFStringGetLength(string
);
844 CFStringInlineBuffer buf
;
845 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
846 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
850 static Boolean
_shouldPercentReplaceChar(UniChar ch
, void *context
) {
851 CFStringRef unescape
= ((CFStringRef
*)context
)[0];
852 CFStringRef escape
= ((CFStringRef
*)context
)[1];
853 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
855 if (unescape
&& _stringContainsCharacter(unescape
, ch
)) {
856 shouldReplace
= false;
858 } else if (escape
&& _stringContainsCharacter(escape
, ch
)) {
859 shouldReplace
= true;
861 return shouldReplace
;
864 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
865 CFStringRef strings
[2];
866 strings
[0] = charactersToLeaveUnescaped
;
867 strings
[1] = legalURLCharactersToBeEscaped
;
868 return _addPercentEscapesToString(allocator
, originalString
, _shouldPercentReplaceChar
, NULL
, encoding
, strings
);
873 static Boolean
__CFURLCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
874 CFURLRef url1
= (CFURLRef
)cf1
;
875 CFURLRef url2
= (CFURLRef
)cf2
;
876 UInt32 pathType1
, pathType2
;
878 __CFGenericValidateType(cf1
, CFURLGetTypeID());
879 __CFGenericValidateType(cf2
, CFURLGetTypeID());
881 if (url1
== url2
) return kCFCompareEqualTo
;
884 if (! url2
->_base
) return kCFCompareEqualTo
;
885 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
886 } else if ( url2
->_base
) {
890 pathType1
= URL_PATH_TYPE(url1
);
891 pathType2
= URL_PATH_TYPE(url2
);
892 if (pathType1
== pathType2
) {
893 if (pathType1
!= FULL_URL_REPRESENTATION
) {
894 return CFEqual(url1
->_string
, url2
->_string
);
896 // Do not compare the original strings; compare the sanatized strings.
897 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
900 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
901 CFStringRef scheme1
= CFURLCopyScheme(url1
);
902 CFStringRef scheme2
= CFURLCopyScheme(url2
);
904 if (scheme1
&& scheme2
) {
905 eq
= CFEqual(scheme1
, scheme2
);
908 } else if (!scheme1
&& !scheme2
) {
912 if (scheme1
) CFRelease(scheme1
);
913 else CFRelease(scheme2
);
915 if (!eq
) return false;
917 if (pathType1
== FULL_URL_REPRESENTATION
) {
918 if (!(url1
->_flags
& IS_PARSED
)) {
919 _parseComponentsOfURL(url1
);
921 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
926 if (pathType2
== FULL_URL_REPRESENTATION
) {
927 if (!(url2
->_flags
& IS_PARSED
)) {
928 _parseComponentsOfURL(url2
);
930 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
935 // No help for it; we now must convert to the canonical representation and compare.
936 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
941 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
942 CFURLRef url1
= (CFURLRef
)cf1
;
943 CFURLRef url2
= (CFURLRef
)cf2
;
944 UInt32 pathType1
, pathType2
;
946 __CFGenericValidateType(cf1
, CFURLGetTypeID());
947 __CFGenericValidateType(cf2
, CFURLGetTypeID());
949 if (url1
== url2
) return true;
950 if ((url1
->_flags
& IS_PARSED
) && (url2
->_flags
& IS_PARSED
) && (url1
->_flags
& IS_DIRECTORY
) != (url2
->_flags
& IS_DIRECTORY
)) return false;
952 if (! url2
->_base
) return false;
953 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
954 } else if ( url2
->_base
) {
958 pathType1
= URL_PATH_TYPE(url1
);
959 pathType2
= URL_PATH_TYPE(url2
);
960 if (pathType1
== pathType2
) {
961 if (pathType1
!= FULL_URL_REPRESENTATION
) {
962 return CFEqual(url1
->_string
, url2
->_string
);
964 // Do not compare the original strings; compare the sanatized strings.
965 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
968 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
969 CFStringRef scheme1
= CFURLCopyScheme(url1
);
970 CFStringRef scheme2
= CFURLCopyScheme(url2
);
972 if (scheme1
&& scheme2
) {
973 eq
= CFEqual(scheme1
, scheme2
);
976 } else if (!scheme1
&& !scheme2
) {
980 if (scheme1
) CFRelease(scheme1
);
981 else CFRelease(scheme2
);
983 if (!eq
) return false;
985 if (pathType1
== FULL_URL_REPRESENTATION
) {
986 if (!(url1
->_flags
& IS_PARSED
)) {
987 _parseComponentsOfURL(url1
);
989 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
994 if (pathType2
== FULL_URL_REPRESENTATION
) {
995 if (!(url2
->_flags
& IS_PARSED
)) {
996 _parseComponentsOfURL(url2
);
998 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
1003 // No help for it; we now must convert to the canonical representation and compare.
1004 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
1008 static CFHashCode
__CFURLHash(CFTypeRef cf
) {
1009 /* This is tricky, because we do not want the hash value to change as a file system URL is changed to its canonical representation, nor do we wish to force the conversion to the canonical representation. We choose instead to take the last path component (or "/" in the unlikely case that the path is empty), then hash on that. */
1010 struct __CFURL
* url
= (struct __CFURL
*)cf
;
1011 CFHashCode result
= 0;
1015 // Allocate our extra space if it isn't already allocated
1016 if ( url
&& ! url
->extra
)
1017 _CFURLAllocateExtraDataspace( url
);
1020 result
= url
->extra
->hashValue
;
1023 if (CFURLCanBeDecomposed(url
)) {
1024 CFStringRef lastComp
= CFURLCopyLastPathComponent(url
);
1025 CFStringRef hostNameRef
= CFURLCopyHostName(url
);
1030 result
= CFHash(lastComp
);
1031 CFRelease(lastComp
);
1034 if ( hostNameRef
) {
1035 result
^= CFHash( hostNameRef
);
1036 CFRelease( hostNameRef
);
1039 result
= CFHash(CFURLGetString(url
));
1042 if ( ! result
) // never store a 0 value for the hashed value
1045 url
->extra
->hashValue
= result
;
1053 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
1054 CFURLRef url
= (CFURLRef
)cf
;
1055 __CFGenericValidateType(cf
, CFURLGetTypeID());
1057 #if DEPLOYMENT_TARGET_MACOSX
1059 CFRetain(url
->_string
);
1060 return url
->_string
;
1063 CFRetain(url
->_string
);
1064 return url
->_string
;
1067 // Do not dereference url->_base; it may be an ObjC object
1068 return CFStringCreateWithFormat(CFGetAllocator(url
), NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
1073 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
1074 CFURLRef url
= (CFURLRef
)cf
;
1076 CFAllocatorRef alloc
= CFGetAllocator(url
);
1078 CFStringRef baseString
= CFCopyDescription(url
->_base
);
1079 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@, encoding = %d\n\tbase = %@}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
, url
->_encoding
, baseString
);
1080 CFRelease(baseString
);
1082 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@, encoding = %d, base = (null)}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
, url
->_encoding
);
1087 #if DEBUG_URL_MEMORY_USAGE
1089 extern __attribute((used
)) void __CFURLDumpMemRecord(void) {
1090 CFStringRef str
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%d URLs created; %d destroyed\n%d file URLs created; %d converted; %d destroyed. %d urls had 'extra' data allocated, %d had base urls, %d were not UTF8 encoded\n"), numURLs
, numDealloced
, numFileURLsCreated
, numFileURLsConverted
, numFileURLsDealloced
, numExtraDataAllocated
, numURLsWithBaseURL
, numNonUTF8EncodedURLs
);
1093 // if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator);
1097 static void __CFURLDeallocate(CFTypeRef cf
) {
1098 CFURLRef url
= (CFURLRef
)cf
;
1099 CFAllocatorRef alloc
;
1100 __CFGenericValidateType(cf
, CFURLGetTypeID());
1101 alloc
= CFGetAllocator(url
);
1102 #if DEBUG_URL_MEMORY_USAGE
1104 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
1105 numFileURLsDealloced
++;
1108 if (url
->_string
) CFRelease(url
->_string
); // GC: 3879914
1109 if (url
->_base
) CFRelease(url
->_base
);
1110 if (url
->ranges
) CFAllocatorDeallocate(alloc
, url
->ranges
);
1111 if (_getSanitizedString(url
)) CFRelease(_getSanitizedString(url
));
1112 if ( url
->extra
!= NULL
) CFAllocatorDeallocate( alloc
, url
->extra
);
1113 if (_getResourceInfo(url
)) CFRelease(_getResourceInfo(url
));
1116 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
1118 static const CFRuntimeClass __CFURLClass
= {
1126 __CFURLCopyFormattingDescription
,
1127 __CFURLCopyDescription
1130 // When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but
1131 // when it is defined, we must prefix with static to prevent the string from being exported
1132 #ifdef __CONSTANT_CFSTRINGS__
1133 static CONST_STRING_DECL(kCFURLFileScheme
, "file")
1134 static CONST_STRING_DECL(kCFURLDataScheme
, "data")
1135 static CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1136 static CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1138 CONST_STRING_DECL(kCFURLFileScheme
, "file")
1139 CONST_STRING_DECL(kCFURLDataScheme
, "data")
1140 CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1141 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1143 __private_extern__
void __CFURLInitialize(void) {
1144 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1147 /* Toll-free bridging support; get the true CFURL from an NSURL */
1148 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1149 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, url
, "_cfurl");
1153 CFTypeID
CFURLGetTypeID(void) {
1154 return __kCFURLTypeID
;
1157 __private_extern__
void CFShowURL(CFURLRef url
) {
1159 fprintf(stdout
, "(null)\n");
1162 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1163 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1164 fprintf(stdout
, "ObjC bridged object}\n");
1167 fprintf(stdout
, "\n\tPath type: ");
1168 switch (URL_PATH_TYPE(url
)) {
1169 case kCFURLPOSIXPathStyle
:
1170 fprintf(stdout
, "POSIX");
1172 case kCFURLHFSPathStyle
:
1173 fprintf(stdout
, "HFS");
1175 case kCFURLWindowsPathStyle
:
1176 fprintf(stdout
, "NTFS");
1178 case FULL_URL_REPRESENTATION
:
1179 fprintf(stdout
, "Native URL");
1182 fprintf(stdout
, "UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url
));
1184 fprintf(stdout
, "\n\tRelative string: ");
1185 CFShow(url
->_string
);
1186 fprintf(stdout
, "\tBase URL: ");
1188 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1191 fprintf(stdout
, "(null)\n");
1193 fprintf(stdout
, "\tFlags: 0x%x\n}\n", (unsigned int)url
->_flags
);
1197 /***************************************************/
1198 /* URL creation and String/Data creation from URLS */
1199 /***************************************************/
1200 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, const char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1201 CFIndex neededLength
;
1205 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1209 *freeCharacters
= false;
1213 *ustring
= CFStringGetCharactersPtr(string
);
1215 *useCString
= false;
1216 *freeCharacters
= false;
1220 *freeCharacters
= true;
1221 length
= CFStringGetLength(string
);
1222 rg
= CFRangeMake(0, length
);
1223 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1224 if (neededLength
== length
) {
1225 char *buf
= (char *)CFAllocatorAllocate(alloc
, length
, 0);
1226 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, (uint8_t *)buf
, length
, NULL
);
1230 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1231 CFStringGetCharacters(string
, rg
, buf
);
1232 *useCString
= false;
1237 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1238 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1240 /* index gives the URL part involved; to calculate the correct range index, use the number of the bit of the equivalent flag (i.e. the host flag is HAS_HOST, which is 0x8. so the range index for the host is 3.) Note that this is true in this function ONLY, since the ranges stored in (*range) are actually packed, skipping those URL components that don't exist. This is why the indices are hard-coded in this function. */
1242 CFIndex idx
, base_idx
= 0;
1243 CFIndex string_length
;
1244 UInt32 flags
= (IS_PARSED
| *theFlags
);
1245 Boolean useCString
, freeCharacters
, isCompliant
;
1246 uint8_t numRanges
= 0;
1247 const char *cstring
= NULL
;
1248 const UniChar
*ustring
= NULL
;
1250 string_length
= CFStringGetLength(string
);
1251 constructBuffers(alloc
, string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1253 // Algorithm is as described in RFC 1808
1254 // 1: parse the fragment; remainder after left-most "#" is fragment
1255 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1256 if ('#' == STRING_CHAR(idx
)) {
1257 flags
|= HAS_FRAGMENT
;
1258 ranges
[8].location
= idx
+ 1;
1259 ranges
[8].length
= string_length
- (idx
+ 1);
1261 string_length
= idx
; // remove fragment from parse string
1265 // 2: parse the scheme
1266 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1267 UniChar ch
= STRING_CHAR(idx
);
1269 flags
|= HAS_SCHEME
;
1270 flags
|= IS_ABSOLUTE
;
1271 ranges
[0].location
= base_idx
;
1272 ranges
[0].length
= idx
;
1275 // optimization for http urls
1276 if (idx
== 4 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' && STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p') {
1277 flags
|= HAS_HTTP_SCHEME
;
1279 // optimization for file urls
1280 if (idx
== 4 && STRING_CHAR(0) == 'f' && STRING_CHAR(1) == 'i' && STRING_CHAR(2) == 'l' && STRING_CHAR(3) == 'e') {
1281 flags
|= HAS_FILE_SCHEME
;
1284 } else if (!scheme_valid(ch
)) {
1285 break; // invalid scheme character -- no scheme
1289 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1290 // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage
1291 // expects this to be treated identically to "scheme://" - REW, 12/08/03
1292 if (!(flags
& HAS_SCHEME
)) {
1294 } else if (base_idx
== string_length
) {
1295 isCompliant
= false;
1296 } else if (STRING_CHAR(base_idx
) != '/') {
1297 isCompliant
= false;
1303 // Clear the fragment flag if it's been set
1304 if (flags
& HAS_FRAGMENT
) {
1305 flags
&= (~HAS_FRAGMENT
);
1306 string_length
= CFStringGetLength(string
);
1308 (*theFlags
) = flags
;
1309 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
1310 (*range
)->location
= ranges
[0].location
;
1311 (*range
)->length
= ranges
[0].length
;
1313 if (freeCharacters
) {
1314 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1318 // URL is 1808-compliant
1319 flags
|= IS_DECOMPOSABLE
;
1321 // 3: parse the network location and login
1322 if (2 <= (string_length
- base_idx
) && '/' == STRING_CHAR(base_idx
) && '/' == STRING_CHAR(base_idx
+1)) {
1323 CFIndex base
= 2 + base_idx
, extent
;
1324 for (idx
= base
; idx
< string_length
; idx
++) {
1325 if ('/' == STRING_CHAR(idx
) || '?' == STRING_CHAR(idx
)) break;
1329 // net_loc parts extend from base to extent (but not including), which might be to end of string
1330 // net location is "<user>:<password>@<host>:<port>"
1331 if (extent
!= base
) {
1332 for (idx
= base
; idx
< extent
; idx
++) {
1333 if ('@' == STRING_CHAR(idx
)) { // there is a user
1337 ranges
[1].location
= base
; // base of the user
1338 for (idx2
= base
; idx2
< idx
; idx2
++) {
1339 if (':' == STRING_CHAR(idx2
)) { // found a password separator
1340 flags
|= HAS_PASSWORD
;
1342 ranges
[2].location
= idx2
+1; // base of the password
1343 ranges
[2].length
= idx
-(idx2
+1); // password extent
1344 ranges
[1].length
= idx2
- base
; // user extent
1348 if (!(flags
& HAS_PASSWORD
)) {
1349 // user extends to the '@'
1350 ranges
[1].length
= idx
- base
; // user extent
1358 ranges
[3].location
= base
; // base of host
1360 // base has been advanced past the user and password if they existed
1361 for (idx
= base
; idx
< extent
; idx
++) {
1362 // IPV6 support (RFC 2732) DCJ June/10/2002
1363 if ('[' == STRING_CHAR(idx
)) { // starting IPV6 explicit address
1364 // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end
1365 for ( ; idx
< extent
; ++ idx
) {
1366 if ( ']' == STRING_CHAR(idx
)) {
1367 flags
|= IS_IPV6_ENCODED
;
1372 // there is a port if we see a colon. Only the last one is the port, though.
1373 else if ( ':' == STRING_CHAR(idx
)) {
1376 ranges
[4].location
= idx
+1; // base of port
1377 ranges
[4].length
= extent
- (idx
+1); // port extent
1378 ranges
[3].length
= idx
- base
; // host extent
1382 if (!(flags
& HAS_PORT
)) {
1383 ranges
[3].length
= extent
- base
; // host extent
1389 // 4: parse the query; remainder after left-most "?" is query
1390 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1391 if ('?' == STRING_CHAR(idx
)) {
1394 ranges
[7].location
= idx
+ 1;
1395 ranges
[7].length
= string_length
- (idx
+1);
1396 string_length
= idx
; // remove query from parse string
1401 // 5: parse the parameters; remainder after left-most ";" is parameters
1402 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1403 if (';' == STRING_CHAR(idx
)) {
1404 flags
|= HAS_PARAMETERS
;
1406 ranges
[6].location
= idx
+ 1;
1407 ranges
[6].length
= string_length
- (idx
+1);
1408 string_length
= idx
; // remove parameters from parse string
1413 // 6: parse the path; it's whatever's left between string_length & base_idx
1414 if (string_length
- base_idx
!= 0 || (flags
& NET_LOCATION_MASK
))
1416 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1422 pathRg
.location
= base_idx
;
1423 pathRg
.length
= string_length
- base_idx
;
1426 if (pathRg
.length
> 0) {
1427 Boolean sawPercent
= FALSE
;
1428 for (idx
= pathRg
.location
; idx
< string_length
; idx
++) {
1429 if ('%' == STRING_CHAR(idx
)) {
1434 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1435 if (pathRg
.length
> 6 && STRING_CHAR(pathRg
.location
) == '/' && STRING_CHAR(pathRg
.location
+ 1) == '.' && STRING_CHAR(pathRg
.location
+ 2) == 'f' && STRING_CHAR(pathRg
.location
+ 3) == 'i' && STRING_CHAR(pathRg
.location
+ 4) == 'l' && STRING_CHAR(pathRg
.location
+ 5) == 'e' && STRING_CHAR(pathRg
.location
+ 6) == '/') {
1436 flags
|= PATH_HAS_FILE_ID
;
1437 } else if (!sawPercent
) {
1438 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1440 #elif DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_WINDOWS
1442 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1445 #error Unknown or unspecified DEPLOYMENT_TARGET
1448 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 1);
1451 } else if (ch
== '.') {
1452 if (pathRg
.length
== 1) {
1455 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 2);
1458 } else if (ch
!= '.') {
1460 } else if (pathRg
.length
== 2) {
1463 isDir
= (STRING_CHAR(pathRg
.location
+ pathRg
.length
- 3) == '/');
1470 isDir
= (baseURL
!= NULL
) ? CFURLHasDirectoryPath(baseURL
) : false;
1473 flags
|= IS_DIRECTORY
;
1477 if (freeCharacters
) {
1478 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1480 (*theFlags
) = flags
;
1481 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
)*numRanges
, 0);
1483 for (idx
= 0, flags
= 1; flags
!= (1<<9); flags
= (flags
<<1), idx
++) {
1484 if ((*theFlags
) & flags
) {
1485 (*range
)[numRanges
] = ranges
[idx
];
1491 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
) {
1493 Boolean sawIllegalChar
= false;
1494 for (idx
= base
; idx
< end
; idx
++) {
1495 Boolean shouldEscape
;
1496 UniChar ch
= STRING_CHAR(idx
);
1497 if (isURLLegalCharacter(ch
)) {
1498 if ((componentFlag
== HAS_USER
|| componentFlag
== HAS_PASSWORD
) && (ch
== '/' || ch
== '?' || ch
== '@')) {
1499 shouldEscape
= true;
1501 shouldEscape
= false;
1503 } else if (ch
== '%' && idx
+ 2 < end
&& isHexDigit(STRING_CHAR(idx
+ 1)) && isHexDigit(STRING_CHAR(idx
+2))) {
1504 shouldEscape
= false;
1505 } else if (componentFlag
== HAS_HOST
&& ((idx
== base
&& ch
== '[') || (idx
== end
-1 && ch
== ']'))) {
1506 shouldEscape
= false;
1508 shouldEscape
= true;
1510 if (!shouldEscape
) continue;
1512 sawIllegalChar
= true;
1513 if (componentFlag
&& flags
) {
1514 *flags
|= (componentFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
);
1516 if (!*escapedString
) {
1517 *escapedString
= CFStringCreateMutable(alloc
, 0);
1520 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[*mark
]), idx
- *mark
, kCFStringEncodingISOLatin1
, false);
1521 CFStringAppend(*escapedString
, tempString
);
1522 CFRelease(tempString
);
1524 CFStringAppendCharacters(*escapedString
, &(ustring
[*mark
]), idx
- *mark
);
1527 _appendPercentEscapesForCharacter(ch
, encoding
, *escapedString
); // This can never fail because anURL->_string was constructed from the encoding passed in
1529 return sawIllegalChar
;
1532 static void computeSanitizedString(CFURLRef url
) {
1533 CFAllocatorRef alloc
= CFGetAllocator(url
);
1534 CFIndex string_length
= CFStringGetLength(url
->_string
);
1535 Boolean useCString
, freeCharacters
;
1536 const char *cstring
= NULL
;
1537 const UniChar
*ustring
= NULL
;
1538 CFIndex base
; // where to scan from
1539 CFIndex mark
; // first character not-yet copied to sanitized string
1540 if (!(url
->_flags
& IS_PARSED
)) {
1541 _parseComponentsOfURL(url
);
1543 constructBuffers(alloc
, url
->_string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1544 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1545 // Impossible to have a problem character in the scheme
1546 CFMutableStringRef sanitizedString
= NULL
;
1547 base
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
).length
+ 1;
1549 if (!scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1550 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1552 if ( sanitizedString
) {
1553 _setSanitizedString( (struct __CFURL
*) url
, sanitizedString
);
1556 // Go component by component
1557 CFIndex currentComponent
= HAS_USER
;
1558 CFMutableStringRef sanitizedString
= NULL
;
1560 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1561 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->ranges
, currentComponent
);
1562 if (componentRange
.location
!= kCFNotFound
) {
1563 scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1565 currentComponent
= currentComponent
<< 1;
1567 if (sanitizedString
) {
1568 _setSanitizedString((struct __CFURL
*)url
, sanitizedString
);
1570 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1573 if (_getSanitizedString(url
) && mark
!= string_length
) {
1575 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1576 CFStringAppend(_getSanitizedString(url
), tempString
);
1577 CFRelease(tempString
);
1579 CFStringAppendCharacters(_getSanitizedString(url
), &(ustring
[mark
]), string_length
- mark
);
1582 if (freeCharacters
) {
1583 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1588 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1589 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1590 CFIndex string_length
= CFStringGetLength(comp
);
1591 Boolean useCString
, freeCharacters
;
1592 const char *cstring
= NULL
;
1593 const UniChar
*ustring
= NULL
;
1594 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1595 CFMutableStringRef result
= NULL
;
1597 constructBuffers(alloc
, comp
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1598 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1600 if (mark
< string_length
) {
1602 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1603 CFStringAppend(result
, tempString
);
1604 CFRelease(tempString
);
1606 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1610 // This should nevr happen
1612 result
= (CFMutableStringRef
)comp
;
1614 if (freeCharacters
) {
1615 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1621 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1622 struct __CFURL
*url
;
1623 #if DEBUG_URL_MEMORY_USAGE
1625 // if (!URLAllocator) {
1626 // URLAllocator = CFCountingAllocatorCreate(NULL);
1628 allocator
= URLAllocator
;
1630 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1633 if (createOldUTF8StyleURLs()) {
1634 url
->_flags
|= IS_OLD_UTF8_STYLE
;
1636 url
->_encoding
= kCFStringEncodingUTF8
;
1637 url
->_string
= NULL
;
1641 url
->_resourceInfo
= NULL
;
1642 // url->_reserved = NULL;
1643 // url->_sanatizedString = NULL;
1648 // It is the caller's responsibility to guarantee that if URLString is absolute, base is NULL. This is necessary to avoid duplicate processing for file system URLs, which had to decide whether to compute the cwd for the base; we don't want to duplicate that work. This ALSO means it's the caller's responsibility to set the IS_ABSOLUTE bit, since we may have a degenerate URL whose string is relative, but lacks a base.
1649 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, UInt32 fsType
, CFURLRef base
) {
1650 CFAssert2((fsType
== FULL_URL_REPRESENTATION
) || (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) || (fsType
== kCFURLHFSPathStyle
) || ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__
, fsType
);
1652 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1653 url
->_string
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1654 url
->_flags
|= (fsType
<< 16);
1656 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1658 #if DEBUG_URL_MEMORY_USAGE
1659 if (fsType
!= FULL_URL_REPRESENTATION
) {
1660 numFileURLsCreated
++;
1663 numURLsWithBaseURL
++;
1666 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
1667 _convertToURLRepresentation((struct __CFURL
*)url
);
1669 if (!(url
->_flags
& IS_PARSED
)) {
1670 _parseComponentsOfURL(url
);
1675 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
1676 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1677 CFIndex len
= CFStringGetLength(path
);
1678 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1679 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1680 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1682 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1683 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1687 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1688 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1690 #elif DEPLOYMENT_TARGET_WINDOWS
1691 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1692 CFIndex len
= CFStringGetLength(path
);
1693 // be sure to use the windows path separator when checking the path to see if it's a directory here
1694 if (!len
|| '\\' == CFStringGetCharacterAtIndex(path
, len
- 1))
1695 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1696 UniChar firstChar
= 0 < len
? CFStringGetCharacterAtIndex(path
, 0) : 0;
1697 UniChar secondChar
= 1 < len
? CFStringGetCharacterAtIndex(path
, 1) : 0;
1698 Boolean isDrive
= ('A' <= firstChar
&& firstChar
<= 'Z') || ('a' <= firstChar
&& firstChar
<= 'z');
1699 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1700 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1701 isDrive
= isDrive
&& (secondChar
== ':' || secondChar
== '|');
1702 if (isDrive
|| (firstChar
== '\\' && secondChar
== '\\')) {
1703 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLWindowsPathStyle
, NULL
);
1704 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1705 } else if (firstChar
== '/') {
1706 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1707 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1708 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1709 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1711 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1712 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1718 #error Unknown or unspecified DEPLOYMENT_TARGET
1721 // Exported for Foundation's use
1722 CF_EXPORT Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1723 // Check each character to make sure it is a legal URL char. The valid characters are 'A'-'Z', 'a' - 'z', '0' - '9', plus the characters in "-_.!~*'()", and the set of reserved characters (these characters have special meanings in the URL syntax), which are ";/?:@&=+$,". In addition, percent escape sequences '%' hex-digit hex-digit are permitted.
1724 // Plus the hash character '#' which denotes the beginning of a fragment, and can appear exactly once in the entire URL string. -- REW, 12/13/2000
1725 CFStringInlineBuffer stringBuffer
;
1726 CFIndex idx
= 0, length
;
1727 Boolean sawHash
= false;
1729 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1732 length
= CFStringGetLength(string
);
1733 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1734 while (idx
< length
) {
1735 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1738 // Make sure that two valid hex digits follow a '%' character
1740 if ( idx
+ 2 > length
)
1742 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1743 idx
= -1; // To guarantee index < length, and our failure case is triggered
1747 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1749 if (! isHexDigit(ch
) ) {
1750 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1754 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1756 if (! isHexDigit(ch
) ) {
1757 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1764 if (ch
== '[' || ch
== ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1770 #if DEPLOYMENT_TARGET_WINDOWS
1771 // <rdar://problem/7134119> CF on Windows: CFURLCreateWithString should work with | in path on Windows
1772 if (isURLLegalCharacter(ch
) || ch
== '|')
1774 if ( isURLLegalCharacter( ch
) )
1785 CF_EXPORT
void _CFURLInitWithString(CFURLRef myURL
, CFStringRef string
, CFURLRef baseURL
) {
1786 struct __CFURL
*url
= (struct __CFURL
*)myURL
; // Supress annoying compile warnings
1787 Boolean isAbsolute
= false;
1788 CFRange colon
= CFStringFind(string
, CFSTR(":"), 0);
1789 if (colon
.location
!= kCFNotFound
) {
1792 for (i
= 0; i
< colon
.location
; i
++) {
1793 char ch
= (char)CFStringGetCharacterAtIndex(string
, i
);
1794 if (!scheme_valid(ch
)) {
1800 _CFURLInit(url
, string
, FULL_URL_REPRESENTATION
, isAbsolute
? NULL
: baseURL
);
1802 url
->_flags
|= IS_ABSOLUTE
;
1806 struct __CFURLEncodingTranslationParameters
{
1807 CFStringEncoding fromEnc
;
1808 CFStringEncoding toEnc
;
1809 const UniChar
*addlChars
;
1811 Boolean escapeHighBit
;
1812 Boolean escapePercents
;
1813 Boolean agreesOverASCII
;
1814 Boolean encodingsMatch
;
1817 static Boolean
_shouldEscapeForEncodingConversion(UniChar ch
, void *context
) {
1818 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1819 if (info
->escapeHighBit
&& ch
> 0x7F) {
1821 } else if (ch
== '%' && info
->escapePercents
) {
1823 } else if (info
->addlChars
) {
1824 const UniChar
*escChar
= info
->addlChars
;
1826 for (i
= 0; i
< info
->count
; escChar
++, i
++) {
1827 if (*escChar
== ch
) {
1835 static CFIndex
_convertEscapeSequence(CFIndex percentIndex
, CFStringRef urlString
, CFStringRef
*newString
, void *context
) {
1836 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1837 CFMutableDataRef newData
;
1838 Boolean sawNonASCIICharacter
= false;
1839 CFIndex i
= percentIndex
;
1842 if (info
->encodingsMatch
) return percentIndex
+ 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well
1843 newData
= CFDataCreateMutable(CFGetAllocator(urlString
), 0);
1844 length
= CFStringGetLength(urlString
);
1846 while (i
< length
&& CFStringGetCharacterAtIndex(urlString
, i
) == '%') {
1848 if (i
+2 >= length
|| !_translateBytes(CFStringGetCharacterAtIndex(urlString
, i
+1), CFStringGetCharacterAtIndex(urlString
, i
+2), &byte
)) {
1852 if (byte
> 0x7f) sawNonASCIICharacter
= true;
1853 CFDataAppendBytes(newData
, &byte
, 1);
1856 if (!sawNonASCIICharacter
&& info
->agreesOverASCII
) {
1860 CFStringRef tmp
= CFStringCreateWithBytes(CFGetAllocator(urlString
), CFDataGetBytePtr(newData
), CFDataGetLength(newData
), info
->fromEnc
, false);
1861 CFIndex tmpIndex
, tmpLen
;
1866 tmpLen
= CFStringGetLength(tmp
);
1867 *newString
= CFStringCreateMutable(CFGetAllocator(urlString
), 0);
1868 for (tmpIndex
= 0; tmpIndex
< tmpLen
; tmpIndex
++) {
1869 if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp
, tmpIndex
), info
->toEnc
, (CFMutableStringRef
)(*newString
))) {
1875 if (tmpIndex
< tmpLen
) {
1876 CFRelease(*newString
);
1885 /* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */
1886 static CFStringRef
_convertPercentEscapes(CFStringRef urlString
, CFStringEncoding fromEncoding
, CFStringEncoding toEncoding
, Boolean escapeAllHighBitCharacters
, Boolean escapePercents
, const UniChar
*addlCharsToEscape
, int numAddlChars
) {
1887 struct __CFURLEncodingTranslationParameters context
;
1888 context
.fromEnc
= fromEncoding
;
1889 context
.toEnc
= toEncoding
;
1890 context
.addlChars
= addlCharsToEscape
;
1891 context
.count
= numAddlChars
;
1892 context
.escapeHighBit
= escapeAllHighBitCharacters
;
1893 context
.escapePercents
= escapePercents
;
1894 context
.agreesOverASCII
= (__CFStringEncodingIsSupersetOfASCII(toEncoding
) && __CFStringEncodingIsSupersetOfASCII(fromEncoding
)) ? true : false;
1895 context
.encodingsMatch
= (fromEncoding
== toEncoding
) ? true : false;
1896 return _addPercentEscapesToString(CFGetAllocator(urlString
), urlString
, _shouldEscapeForEncodingConversion
, _convertEscapeSequence
, toEncoding
, &context
);
1899 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1900 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
1901 CFStringRef urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, encoding
, false);
1903 if (!urlString
|| CFStringGetLength(urlString
) == 0) {
1904 if (urlString
) CFRelease(urlString
);
1907 #if DEPLOYMENT_TARGET_MACOSX
1908 if ( !baseURL
&& *URLBytes
== '/' ) {
1909 CFLog(kCFLogLevelWarning
, CFSTR("CFURLCreateWithBytes was passed these invalid URLBytes: '%@' (a file system path instead of an URL string). The URL created will not work with most file URL functions. CFURLCreateFromFileSystemRepresentation should be used instead."), urlString
);
1912 if (createOldUTF8StyleURLs()) {
1913 if (encoding
!= kCFStringEncodingUTF8
) {
1914 CFStringRef tmp
= _convertPercentEscapes(urlString
, encoding
, kCFStringEncodingUTF8
, false, false, NULL
, 0);
1915 CFRelease(urlString
);
1917 if (!urlString
) return NULL
;
1921 result
= _CFURLAlloc(allocator
);
1923 _CFURLInitWithString(result
, urlString
, baseURL
);
1924 if (encoding
!= kCFStringEncodingUTF8
&& !createOldUTF8StyleURLs()) {
1925 ((struct __CFURL
*)result
)->_encoding
= encoding
;
1926 #if DEBUG_URL_MEMORY_USAGE
1927 if ( encoding
!= kCFStringEncodingUTF8
) {
1928 numNonUTF8EncodedURLs
++;
1933 CFRelease(urlString
); // it's retained by result, now.
1937 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
1938 static const UniChar whitespaceChars
[4] = {' ', '\n', '\r', '\t'};
1939 CFStringRef myStr
= CFURLGetString(url
);
1942 if (url
->_flags
& IS_OLD_UTF8_STYLE
) {
1943 newStr
= (encoding
== kCFStringEncodingUTF8
) ? (CFStringRef
)CFRetain(myStr
) : _convertPercentEscapes(myStr
, kCFStringEncodingUTF8
, encoding
, true, false, escapeWhitespace
? whitespaceChars
: NULL
, escapeWhitespace
? 4 : 0);
1948 result
= CFStringCreateExternalRepresentation(allocator
, newStr
, encoding
, 0);
1953 // Any escape sequences in URLString will be interpreted via UTF-8.
1954 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1956 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1957 if (!_CFStringIsLegalURLString(URLString
)) return NULL
;
1958 #if DEPLOYMENT_TARGET_MACOSX
1959 if ( !baseURL
&& CFStringGetCharacterAtIndex(URLString
, 0) == '/' ) {
1960 CFLog(kCFLogLevelWarning
, CFSTR("CFURLCreateWithString was passed this invalid URL string: '%@' (a file system path instead of an URL string). The URL created will not work with most file URL functions. CFURLCreateWithFileSystemPath or CFURLCreateWithFileSystemPathRelativeToBase should be used instead."), URLString
);
1963 url
= _CFURLAlloc(allocator
);
1965 _CFURLInitWithString(url
, URLString
, baseURL
);
1970 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1972 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1973 url
= _CFURLAlloc(allocator
);
1975 _CFURLInitWithString(url
, URLString
, baseURL
);
1980 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
1981 CFStringRef relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
1982 if (!relativeString
) {
1985 #if DEPLOYMENT_TARGET_MACOSX
1986 if ( !baseURL
&& *relativeURLBytes
== '/' ) {
1987 CFLog(kCFLogLevelWarning
, CFSTR("CFURLCreateAbsoluteURLWithBytes was passed these invalid relativeURLBytes (and no baseURL): '%@' (a file system path instead of an URL string). The URL created will not work with most file URL functions. CFURLCreateFromFileSystemRepresentationRelativeToBase should be used instead."), relativeString
);
1990 if (!useCompatibilityMode
) {
1991 CFURLRef url
= _CFURLCreateWithArbitraryString(alloc
, relativeString
, baseURL
);
1992 CFRelease(relativeString
);
1994 ((struct __CFURL
*)url
)->_encoding
= encoding
;
1995 CFURLRef absURL
= CFURLCopyAbsoluteURL(url
);
1996 #if DEBUG_URL_MEMORY_USAGE
1997 if ( encoding
!= kCFStringEncodingUTF8
) {
1998 numNonUTF8EncodedURLs
++;
2007 UInt32 absFlags
= 0;
2009 CFStringRef absString
= NULL
;
2010 Boolean absStringIsMutable
= false;
2013 absString
= relativeString
;
2015 UniChar ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
2016 if (ch
== '?' || ch
== ';' || ch
== '#') {
2017 // Nothing but parameter + query + fragment; append to the baseURL string
2018 CFStringRef baseString
;
2019 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2020 baseString
= CFURLGetString(baseURL
);
2022 baseString
= baseURL
->_string
;
2024 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
2025 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
2026 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
2027 absStringIsMutable
= true;
2029 UInt32 relFlags
= 0;
2031 CFStringRef relString
= NULL
;
2032 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
2033 if (relFlags
& HAS_SCHEME
) {
2034 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
2035 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
2036 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
2037 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
2038 CFAllocatorDeallocate(alloc
, relRanges
);
2040 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
2042 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
2043 CFRetain(relativeString
);
2044 absString
= relativeString
;
2046 if (baseScheme
) CFRelease(baseScheme
);
2048 CFRetain(relativeString
);
2049 relString
= relativeString
;
2052 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2053 if (!(baseURL
->_flags
& IS_PARSED
)) {
2054 _parseComponentsOfURL(baseURL
);
2056 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->ranges
);
2058 CFStringRef baseString
;
2059 UInt32 baseFlags
= 0;
2060 CFRange
*baseRanges
;
2061 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2062 baseString
= CFURLGetString(baseURL
);
2064 baseString
= baseURL
->_string
;
2066 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2067 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
2068 CFAllocatorDeallocate(alloc
, baseRanges
);
2070 absStringIsMutable
= true;
2072 if (relString
) CFRelease(relString
);
2073 CFAllocatorDeallocate(alloc
, relRanges
);
2075 CFRelease(relativeString
);
2077 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
2078 if (absFlags
& HAS_PATH
) {
2079 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
2080 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
2081 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), 0);
2082 CFStringRef newPath
;
2083 CFStringGetCharacters(absString
, pathRg
, buf
);
2084 buf
[pathRg
.length
] = '\0';
2085 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
2086 if (CFStringGetLength(newPath
) != pathRg
.length
) {
2087 if (!absStringIsMutable
) {
2088 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
2089 CFRelease(absString
);
2092 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
2095 // Do not deallocate buf; newPath took ownership of it.
2097 CFAllocatorDeallocate(alloc
, absRanges
);
2098 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
2099 CFRelease(absString
);
2101 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
2102 #if DEBUG_URL_MEMORY_USAGE
2103 if ( encoding
!= kCFStringEncodingUTF8
) {
2104 numNonUTF8EncodedURLs
++;
2112 /* 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 */
2113 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
2114 UniChar
*idx
= pathStr
;
2118 if (idx
!= pathStr
) {
2123 } else if (*(idx
+1) == pathDelimiter
) {
2124 if (idx
+ 2 != end
|| idx
!= pathStr
) {
2125 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
2129 // Do not delete the sole path component
2132 } else if (( end
-idx
>= 2 ) && *(idx
+1) == '.' && (idx
+2 == end
|| (( end
-idx
> 2 ) && *(idx
+2) == pathDelimiter
))) {
2133 if (idx
- pathStr
>= 2) {
2134 // 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.
2135 UniChar
*lastDelim
= idx
-2;
2136 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
2138 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
2139 // We have a genuine component to compact out
2141 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
2142 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2143 end
-= (idx
+ 3 - lastDelim
);
2146 } else if (lastDelim
!= pathStr
) {
2151 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
2159 } else if (stripLeadingDotDots
) {
2160 if (idx
+ 3 != end
) {
2161 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
2162 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2166 // Do not devolve the last path component
2172 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
2175 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
2178 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
- pathStr
, alloc
);
2181 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
2182 CFMutableStringRef newString
= CFStringCreateMutable(alloc
, 0);
2183 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
2184 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
2187 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
2188 if (rg
.location
!= kCFNotFound
) {
2189 CFStringGetCharacters(baseString
, rg
, buf
);
2190 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2191 CFStringAppendCString(newString
, ":", kCFStringEncodingASCII
);
2194 if (relFlags
& NET_LOCATION_MASK
) {
2195 CFStringAppend(newString
, relString
);
2197 CFStringAppendCString(newString
, "//", kCFStringEncodingASCII
);
2198 rg
= _netLocationRange(baseFlags
, baseRanges
);
2199 if (rg
.location
!= kCFNotFound
) {
2200 CFStringGetCharacters(baseString
, rg
, buf
);
2201 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2204 if (relFlags
& HAS_PATH
) {
2205 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
2206 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2207 CFStringRef newPath
;
2208 Boolean useRelPath
= false;
2209 Boolean useBasePath
= false;
2210 if (basePathRg
.location
== kCFNotFound
) {
2212 } else if (relPathRg
.length
== 0) {
2214 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
2216 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2220 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2221 } else if (useBasePath
) {
2222 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2224 // #warning FIXME - Get rid of this allocation
2225 UniChar
*newPathBuf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
2227 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
2228 idx
= newPathBuf
+ basePathRg
.length
- 1;
2229 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
2230 if (*idx
== '/') idx
++;
2231 CFStringGetCharacters(relString
, relPathRg
, idx
);
2232 end
= idx
+ relPathRg
.length
;
2234 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2236 /* Under Win32 absolute path can begin with letter
2237 * so we have to add one '/' to the newString
2240 // No - the input strings here are URL path strings, not Win32 paths.
2241 // Absolute paths should have had a '/' prepended before this point.
2242 // I have removed Sergey Zubarev's change and left his comment (and
2243 // this one) as a record. - REW, 1/5/2004
2245 // if the relative URL does not begin with a slash and
2246 // the base does not end with a slash, add a slash
2247 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2248 CFStringAppendCString(newString
, "/", kCFStringEncodingASCII
);
2251 CFStringAppend(newString
, newPath
);
2253 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2254 rg
.length
= CFStringGetLength(relString
);
2255 if (rg
.length
> rg
.location
) {
2256 rg
.length
-= rg
.location
;
2257 CFStringGetCharacters(relString
, rg
, buf
);
2258 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2261 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2262 if (rg
.location
!= kCFNotFound
) {
2263 CFStringGetCharacters(baseString
, rg
, buf
);
2264 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2267 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2268 // ??? Can this ever happen?
2269 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2271 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2272 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2273 rg
.location
--; // To pick up the separator
2275 CFStringGetCharacters(baseString
, rg
, buf
);
2276 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2278 } else if (relFlags
& HAS_PARAMETERS
) {
2279 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2280 rg
.location
--; // To get the semicolon that starts the parameters
2281 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2282 CFStringGetCharacters(relString
, rg
, buf
);
2283 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2285 // Sigh; we have to resolve these against one another
2286 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2287 if (rg
.location
!= kCFNotFound
) {
2288 CFStringAppendCString(newString
, ";", kCFStringEncodingASCII
);
2289 CFStringGetCharacters(baseString
, rg
, buf
);
2290 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2292 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2293 if (rg
.location
!= kCFNotFound
) {
2294 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2295 CFStringGetCharacters(relString
, rg
, buf
);
2296 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2298 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2299 if (rg
.location
!= kCFNotFound
) {
2300 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2301 CFStringGetCharacters(baseString
, rg
, buf
);
2302 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2305 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2306 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2307 if (rg
.location
!= kCFNotFound
) {
2308 CFStringAppendCString(newString
, "#", kCFStringEncodingASCII
);
2309 CFStringGetCharacters(relString
, rg
, buf
);
2310 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2315 CFAllocatorDeallocate(alloc
, buf
);
2319 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2320 CFURLRef anURL
, base
;
2321 CFURLPathStyle fsType
;
2322 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2323 CFStringRef baseString
, newString
;
2325 CFRange
*baseRanges
;
2328 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2329 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2330 CF_OBJC_CALL0(CFURLRef
, anURL
, relativeURL
, "absoluteURL");
2331 if (anURL
) CFRetain(anURL
);
2335 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2337 base
= relativeURL
->_base
;
2339 return (CFURLRef
)CFRetain(relativeURL
);
2341 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2342 fsType
= URL_PATH_TYPE(relativeURL
);
2344 if (!baseIsObjC
&& fsType
!= FULL_URL_REPRESENTATION
&& fsType
== URL_PATH_TYPE(base
)) {
2345 return _CFURLCopyAbsoluteFileURL(relativeURL
);
2347 if (fsType
!= FULL_URL_REPRESENTATION
) {
2348 _convertToURLRepresentation((struct __CFURL
*)relativeURL
);
2349 fsType
= FULL_URL_REPRESENTATION
;
2351 if (!(relativeURL
->_flags
& IS_PARSED
)) {
2352 _parseComponentsOfURL(relativeURL
);
2354 if ((relativeURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) && !(relativeURL
->_flags
& (RESOURCE_SPECIFIER_MASK
| NET_LOCATION_MASK
)) && !baseIsObjC
&& (URL_PATH_TYPE(base
) == kCFURLPOSIXPathStyle
)) {
2355 // There's nothing to relativeURL's string except the path
2356 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, CFURLHasDirectoryPath(base
), kCFURLPOSIXPathStyle
, alloc
);
2357 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, kCFURLPOSIXPathStyle
, CFURLHasDirectoryPath(relativeURL
));
2363 CFURLPathStyle baseType
= URL_PATH_TYPE(base
);
2364 if (baseType
!= FULL_URL_REPRESENTATION
) {
2365 _convertToURLRepresentation((struct __CFURL
*)base
);
2366 } else if (!(base
->_flags
& IS_PARSED
)) {
2367 _parseComponentsOfURL(base
);
2369 baseString
= base
->_string
;
2370 baseFlags
= base
->_flags
;
2371 baseRanges
= base
->ranges
;
2373 baseString
= CFURLGetString(base
);
2376 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2379 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->ranges
, baseString
, baseFlags
, baseRanges
);
2381 CFAllocatorDeallocate(alloc
, baseRanges
);
2383 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2384 CFRelease(newString
);
2385 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2386 #if DEBUG_URL_MEMORY_USAGE
2387 if ( relativeURL
->_encoding
!= kCFStringEncodingUTF8
) {
2388 numNonUTF8EncodedURLs
++;
2395 /*******************/
2396 /* Basic accessors */
2397 /*******************/
2398 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2399 return url
->_encoding
;
2402 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2403 anURL
= _CFURLFromNSURL(anURL
);
2404 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) return true;
2405 if (!(anURL
->_flags
& IS_PARSED
)) {
2406 _parseComponentsOfURL(anURL
);
2408 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2411 CFStringRef
CFURLGetString(CFURLRef url
) {
2412 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFStringRef
, url
, "relativeString");
2413 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2414 if (url
->_base
&& (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2415 return url
->_string
;
2417 _convertToURLRepresentation((struct __CFURL
*)url
);
2419 if (!_haveTestedOriginalString(url
)) {
2420 computeSanitizedString(url
);
2422 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2423 return url
->_string
;
2425 return _getSanitizedString( url
);
2429 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2430 CFIndex length
, charsConverted
, usedLength
;
2432 CFStringEncoding enc
;
2433 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2434 string
= CFURLGetString(url
);
2435 enc
= kCFStringEncodingUTF8
;
2437 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2438 _convertToURLRepresentation((struct __CFURL
*)url
);
2440 string
= url
->_string
;
2441 enc
= url
->_encoding
;
2443 length
= CFStringGetLength(string
);
2444 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2445 if (charsConverted
!= length
) {
2452 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2453 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, anURL
, "baseURL");
2454 return anURL
->_base
;
2457 // Assumes the URL is already parsed
2458 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2460 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2461 while (!(compFlag
& 1)) {
2462 compFlag
= compFlag
>> 1;
2471 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2474 CFAllocatorRef alloc
= CFGetAllocator(url
);
2475 CFAssert1(URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
, __kCFLogAssertion
, "%s(): passed a file system URL", __PRETTY_FUNCTION__
);
2476 if (removePercentEscapes
) fromOriginalString
= true;
2477 if (!(url
->_flags
& IS_PARSED
)) {
2478 _parseComponentsOfURL(url
);
2480 rg
= _rangeForComponent(url
->_flags
, url
->ranges
, compFlag
);
2481 if (rg
.location
== kCFNotFound
) return NULL
;
2482 if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_HTTP_SCHEME
) {
2483 comp
= kCFURLHTTPScheme
;
2485 } else if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_FILE_SCHEME
) {
2486 comp
= kCFURLFileScheme
;
2489 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2491 if (!fromOriginalString
) {
2492 if (!_haveTestedOriginalString(url
)) {
2493 computeSanitizedString(url
);
2495 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (url
->_flags
& (compFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
))) {
2496 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2501 if (removePercentEscapes
) {
2503 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
2504 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2506 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2514 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2516 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2517 CF_OBJC_CALL0(CFStringRef
, scheme
, anURL
, "scheme");
2518 if (scheme
) CFRetain(scheme
);
2521 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2523 return CFURLCopyScheme(anURL
->_base
);
2525 CFRetain(kCFURLFileScheme
); // because caller will release it
2526 return kCFURLFileScheme
;
2529 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_HTTP_SCHEME
) {
2530 CFRetain(kCFURLHTTPScheme
);
2531 return kCFURLHTTPScheme
;
2533 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_FILE_SCHEME
) {
2534 CFRetain(kCFURLFileScheme
);
2535 return kCFURLFileScheme
;
2537 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2540 } else if (anURL
->_base
) {
2541 return CFURLCopyScheme(anURL
->_base
);
2547 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2549 CFRange netRg
= {kCFNotFound
, 0};
2552 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2554 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2555 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2556 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2557 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2558 for (i
= 0; i
< c
; i
++) {
2559 if (netRgs
[i
].location
== kCFNotFound
) continue;
2560 if (netRg
.location
== kCFNotFound
) {
2563 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2569 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2570 anURL
= _CFURLFromNSURL(anURL
);
2571 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2572 // !!! This won't work if we go to putting the vol ref num in the net location for HFS
2574 return CFURLCopyNetLocation(anURL
->_base
);
2576 CFRetain(kCFURLLocalhost
);
2577 return kCFURLLocalhost
;
2580 if (!(anURL
->_flags
& IS_PARSED
)) {
2581 _parseComponentsOfURL(anURL
);
2583 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2584 // We provide the net location
2585 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->ranges
);
2587 if (!_haveTestedOriginalString(anURL
)) {
2588 computeSanitizedString(anURL
);
2590 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (anURL
->_flags
& (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2591 // 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.
2593 netRg
.length
= CFStringGetLength( _getSanitizedString(anURL
)) - netRg
.location
;
2594 if (CFStringFindWithOptions(_getSanitizedString(anURL
), CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2595 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2597 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), netRg
);
2599 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2602 } else if (anURL
->_base
) {
2603 return CFURLCopyNetLocation(anURL
->_base
);
2609 // 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.
2610 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
2611 anURL
= _CFURLFromNSURL(anURL
);
2612 if (URL_PATH_TYPE(anURL
) == kCFURLPOSIXPathStyle
&& (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2613 CFRetain(anURL
->_string
);
2614 return anURL
->_string
;
2616 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2617 _convertToURLRepresentation((struct __CFURL
*)anURL
);
2619 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
2622 /* 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.
2624 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.
2626 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
2627 CFStringRef path
= CFURLCopyPath(anURL
);
2628 if (!path
|| CFStringGetLength(path
) == 0) {
2629 if (path
) CFRelease(path
);
2630 if (isAbsolute
) *isAbsolute
= false;
2633 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
2635 if (isAbsolute
) *isAbsolute
= true;
2636 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
2640 if (isAbsolute
) *isAbsolute
= false;
2645 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
2646 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2647 if (URL_PATH_TYPE(anURL
) == FULL_URL_REPRESENTATION
) {
2648 if (!(anURL
->_flags
& IS_PARSED
)) {
2649 _parseComponentsOfURL(anURL
);
2651 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
2652 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2654 return CFURLHasDirectoryPath(anURL
->_base
);
2656 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2659 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
2660 UInt32 firstRsrcSpecFlag
= 0;
2661 UInt32 flag
= HAS_FRAGMENT
;
2662 while (flag
!= HAS_PATH
) {
2664 firstRsrcSpecFlag
= flag
;
2668 return firstRsrcSpecFlag
;
2671 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
2672 anURL
= _CFURLFromNSURL(anURL
);
2673 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2674 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2677 if (!(anURL
->_flags
& IS_PARSED
)) {
2678 _parseComponentsOfURL(anURL
);
2680 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
2681 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, HAS_SCHEME
);
2682 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
2683 if (!_haveTestedOriginalString(anURL
)) {
2684 computeSanitizedString(anURL
);
2686 if (_getSanitizedString(anURL
)) {
2687 // 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.
2688 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), CFRangeMake(base
, CFStringGetLength(_getSanitizedString(anURL
))-base
));
2690 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
2693 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
2695 if (firstRsrcSpecFlag
) {
2696 Boolean canUseOriginalString
= true;
2697 Boolean canUseSanitizedString
= true;
2698 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
2699 if (!_haveTestedOriginalString(anURL
)) {
2700 computeSanitizedString(anURL
);
2702 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
2703 // See if any pieces in the resource specifier differ between sanitized string and original string
2704 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
2705 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2706 canUseOriginalString
= false;
2711 if (!canUseOriginalString
) {
2712 // 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.
2713 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
2714 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2715 canUseSanitizedString
= false;
2720 if (canUseOriginalString
) {
2721 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2722 rg
.location
--; // Include the character that demarcates the component
2723 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
2724 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
2725 } else if (canUseSanitizedString
) {
2726 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2727 rg
.location
--; // Include the character that demarcates the component
2728 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2729 return CFStringCreateWithSubstring(alloc
, _getSanitizedString(anURL
), rg
);
2731 // Must compute the correct string to return; just reparse....
2732 UInt32 sanFlags
= 0;
2733 CFRange
*sanRanges
= NULL
;
2735 _parseComponents(alloc
, _getSanitizedString(anURL
), anURL
->_base
, &sanFlags
, &sanRanges
);
2736 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
2737 CFAllocatorDeallocate(alloc
, sanRanges
);
2738 rg
.location
--; // Include the character that demarcates the component
2739 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2740 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), rg
);
2743 // The resource specifier cannot possibly come from the base.
2749 /*************************************/
2750 /* Accessors that create new objects */
2751 /*************************************/
2753 // 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).
2754 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
2756 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2757 CF_OBJC_CALL0(CFStringRef
, tmp
, anURL
, "host");
2758 if (tmp
) CFRetain(tmp
);
2761 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2762 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2764 return CFURLCopyHostName(anURL
->_base
);
2766 CFRetain(kCFURLLocalhost
);
2767 return kCFURLLocalhost
;
2770 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
2772 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
2773 // Have to strip off the brackets to get the true hostname.
2774 // Assume that to be legal the first and last characters are brackets!
2775 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
2780 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2781 return CFURLCopyHostName(anURL
->_base
);
2787 // Return -1 to indicate no port is specified
2788 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
2790 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2792 CF_OBJC_CALL0(CFNumberRef
, cfPort
, anURL
, "port");
2794 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
2797 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2798 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2800 return CFURLGetPortNumber(anURL
->_base
);
2804 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
2806 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
2807 CFStringInlineBuffer buf
;
2808 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
2810 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
2815 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2816 return CFURLGetPortNumber(anURL
->_base
);
2822 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
2824 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2825 CF_OBJC_CALL0(CFStringRef
, user
, anURL
, "user");
2826 if (user
) CFRetain(user
);
2829 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2830 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2832 return CFURLCopyUserName(anURL
->_base
);
2836 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
2839 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2840 return CFURLCopyUserName(anURL
->_base
);
2846 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
2848 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2849 CF_OBJC_CALL0(CFStringRef
, passwd
, anURL
, "password");
2850 if (passwd
) CFRetain(passwd
);
2853 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2854 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2856 return CFURLCopyPassword(anURL
->_base
);
2860 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
2863 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2864 return CFURLCopyPassword(anURL
->_base
);
2870 // 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
2872 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
2874 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2875 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "parameterString");
2876 if (str
) CFRetain(str
);
2879 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2880 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2883 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
2884 if (str
) return str
;
2885 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2886 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
2888 // Parameter string definitely coming from the relative portion of the URL
2890 return _unescapedParameterString( anURL
->_base
);
2893 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2894 CFStringRef param
= _unescapedParameterString(anURL
);
2897 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2898 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
2900 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
2908 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
2910 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2911 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "query");
2912 if (str
) CFRetain(str
);
2915 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2916 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2919 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
2920 if (str
) return str
;
2921 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2922 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
2925 return _unescapedQueryString(anURL
->_base
);
2928 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2929 CFStringRef query
= _unescapedQueryString(anURL
);
2932 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2933 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
2935 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
2943 // Fragments are NEVER taken from a base URL
2944 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
2946 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2947 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "fragment");
2948 if (str
) CFRetain(str
);
2951 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2952 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2955 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
2959 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2960 CFStringRef fragment
= _unescapedFragment(anURL
);
2963 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2964 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
2966 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
2968 CFRelease(fragment
);
2974 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
2975 CFIndex firstMaskFlag
= 1;
2976 CFIndex lastComponentBeforeMask
= 0;
2977 while (firstMaskFlag
<= HAS_FRAGMENT
) {
2978 if (firstMaskFlag
& mask
) break;
2979 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
2980 firstMaskFlag
= firstMaskFlag
<< 1;
2982 if (lastComponentBeforeMask
== 0) {
2983 // mask includes HAS_SCHEME
2985 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
2986 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
2987 // case file:/path/immediately/without/host
2988 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2989 CFRange pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
2990 if (schemeRg
.length
+ 1 == pathRg
.location
) {
2991 return schemeRg
.length
+ 1;
2993 return schemeRg
.length
+ 3;
2996 // For all other components, the separator precedes the component, so there's no need
2997 // to add extra chars to get to the next insertion point
2998 CFRange rg
= _rangeForComponent(url
->_flags
, url
->ranges
, lastComponentBeforeMask
);
2999 return rg
.location
+ rg
.length
;
3003 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
3004 CFOptionFlags currentOption
;
3005 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
3006 Boolean haveReachedMask
= false;
3007 CFIndex beforeMask
= 0;
3008 CFIndex afterMask
= kCFNotFound
;
3009 CFRange
*currRange
= url
->ranges
;
3010 CFRange maskRange
= {kCFNotFound
, 0};
3011 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
3012 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
3013 firstMaskFlag
= currentOption
;
3014 haveReachedMask
= true;
3016 if (!(url
->_flags
& currentOption
)) continue;
3017 if (!haveReachedMask
) {
3018 beforeMask
= currRange
->location
+ currRange
->length
;
3019 } else if (currentOption
<= mask
) {
3020 if (maskRange
.location
== kCFNotFound
) {
3021 maskRange
= *currRange
;
3023 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
3026 afterMask
= currRange
->location
;
3031 if (afterMask
== kCFNotFound
) {
3032 afterMask
= maskRange
.location
+ maskRange
.length
;
3034 charRangeWithSeparators
->location
= beforeMask
;
3035 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
3039 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3041 switch (component
) {
3042 case kCFURLComponentScheme
:
3045 case kCFURLComponentNetLocation
:
3046 mask
= NET_LOCATION_MASK
;
3048 case kCFURLComponentPath
:
3051 case kCFURLComponentResourceSpecifier
:
3052 mask
= RESOURCE_SPECIFIER_MASK
;
3054 case kCFURLComponentUser
:
3057 case kCFURLComponentPassword
:
3058 mask
= HAS_PASSWORD
;
3060 case kCFURLComponentUserInfo
:
3061 mask
= HAS_USER
| HAS_PASSWORD
;
3063 case kCFURLComponentHost
:
3066 case kCFURLComponentPort
:
3069 case kCFURLComponentParameterString
:
3070 mask
= HAS_PARAMETERS
;
3072 case kCFURLComponentQuery
:
3075 case kCFURLComponentFragment
:
3076 mask
= HAS_FRAGMENT
;
3079 rangeIncludingSeparators
->location
= kCFNotFound
;
3080 rangeIncludingSeparators
->length
= 0;
3081 return CFRangeMake(kCFNotFound
, 0);
3084 if ((url
->_flags
& mask
) == 0) {
3085 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
3086 rangeIncludingSeparators
->length
= 0;
3087 return CFRangeMake(kCFNotFound
, 0);
3089 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
3093 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3094 if (component
== kCFURLComponentScheme
) {
3095 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
3096 rangeIncludingSeparators
->location
= 0;
3097 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
3099 } else if (component
== kCFURLComponentResourceSpecifier
) {
3100 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
3101 CFIndex stringLength
= CFStringGetLength(url
->_string
);
3102 if (schemeRg
.length
+ 1 == stringLength
) {
3103 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
3104 rangeIncludingSeparators
->length
= 0;
3105 return CFRangeMake(kCFNotFound
, 0);
3107 rangeIncludingSeparators
->location
= schemeRg
.length
;
3108 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
3109 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
3112 rangeIncludingSeparators
->location
= kCFNotFound
;
3113 rangeIncludingSeparators
->length
= 0;
3114 return CFRangeMake(kCFNotFound
, 0);
3119 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3120 CFRange charRange
, charRangeWithSeparators
;
3122 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
3123 url
= _CFURLFromNSURL(url
);
3124 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
3125 _convertToURLRepresentation((struct __CFURL
*)url
);
3127 if (!(url
->_flags
& IS_PARSED
)) {
3128 _parseComponentsOfURL(url
);
3131 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
3132 // Special-case this because non-decomposable URLs have a slightly strange flags setup
3133 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
3135 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
3138 if (charRangeWithSeparators
.location
== kCFNotFound
) {
3139 if (rangeIncludingSeparators
) {
3140 rangeIncludingSeparators
->location
= kCFNotFound
;
3141 rangeIncludingSeparators
->length
= 0;
3143 return CFRangeMake(kCFNotFound
, 0);
3144 } else if (rangeIncludingSeparators
) {
3145 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
3147 if (charRange
.location
== kCFNotFound
) {
3148 byteRange
= charRange
;
3149 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
3151 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
3152 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
3154 if (charRangeWithSeparators
.location
== charRange
.location
) {
3155 byteRange
.location
= rangeIncludingSeparators
->location
;
3158 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3159 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
3161 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3162 if (maxCharRangeWithSeparators
== maxCharRange
) {
3163 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
3167 rg
.location
= maxCharRange
;
3168 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
3169 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3170 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
3173 } else if (charRange
.location
== kCFNotFound
) {
3174 byteRange
= charRange
;
3176 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
3177 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3182 /* Component support */
3184 /* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */
3185 static CFStringRef
schemeSpecificString(CFURLRef url
) {
3187 isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3188 switch (URL_PATH_TYPE(url
)) {
3189 case kCFURLPOSIXPathStyle
:
3190 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3191 return (CFStringRef
)CFRetain(url
->_string
);
3193 return POSIXPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3195 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3196 case kCFURLHFSPathStyle
:
3197 return HFSPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3199 case kCFURLWindowsPathStyle
:
3200 return WindowsPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3201 case FULL_URL_REPRESENTATION
:
3202 return CFURLCopyResourceSpecifier(url
);
3208 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
3209 if ( CFURLGetBaseURL(url
) != NULL
) {
3210 components
->scheme
= NULL
;
3212 components
->scheme
= CFURLCopyScheme(url
);
3214 components
->schemeSpecific
= schemeSpecificString(url
);
3218 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
3220 if (components
->scheme
) {
3222 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
3223 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
3224 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
3225 } else if (components
->schemeSpecific
) {
3226 str
= components
->schemeSpecific
;
3232 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3240 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3241 CFAllocatorRef alloc
= CFGetAllocator(url
);
3243 static CFStringRef emptyStr
= NULL
;
3245 emptyStr
= CFSTR("");
3248 if (!CFURLCanBeDecomposed(url
)) {
3251 if ((pathType
= URL_PATH_TYPE(url
)) == FULL_URL_REPRESENTATION
) {
3252 CFStringRef path
= CFURLCopyPath(url
);
3254 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3257 components
->pathComponents
= NULL
;
3259 components
->baseURL
= CFURLGetBaseURL(url
);
3260 if (components
->baseURL
) {
3261 CFRetain(components
->baseURL
);
3262 components
->scheme
= NULL
;
3264 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
3266 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
3267 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3268 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3269 if (url
->_flags
& HAS_PORT
) {
3270 components
->port
= CFURLGetPortNumber(url
);
3272 components
->port
= kCFNotFound
;
3274 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3275 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3276 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3279 case kCFURLPOSIXPathStyle
: {
3280 CFStringRef pathStr
;
3281 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3282 pathStr
= url
->_string
;
3285 pathStr
= POSIXPathToURLPath(url
->_string
, alloc
, url
->_flags
& IS_DIRECTORY
);
3287 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, pathStr
, CFSTR("/"));
3291 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3292 case kCFURLHFSPathStyle
:
3293 components
->pathComponents
= HFSPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3296 case kCFURLWindowsPathStyle
:
3297 components
->pathComponents
= WindowsPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3300 components
->pathComponents
= NULL
;
3302 if (!components
->pathComponents
) {
3305 components
->scheme
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
3306 components
->user
= NULL
;
3307 components
->password
= NULL
;
3308 components
->host
= (CFStringRef
)CFRetain(kCFURLLocalhost
);
3309 components
->port
= kCFNotFound
;
3310 components
->parameterString
= NULL
;
3311 components
->query
= NULL
;
3312 components
->fragment
= NULL
;
3313 components
->baseURL
= CFURLGetBaseURL(url
);
3314 if (components
->baseURL
) CFRetain(components
->baseURL
);
3319 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3320 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3321 CFURLRef base
= comp
->baseURL
;
3323 Boolean hadPrePathComponent
= false;
3326 CFStringAppend(urlString
, comp
->scheme
);
3327 CFStringAppend(urlString
, CFSTR("://"));
3328 hadPrePathComponent
= true;
3330 if (comp
->user
|| comp
->password
) {
3332 CFStringAppend(urlString
, comp
->user
);
3334 if (comp
->password
) {
3335 CFStringAppend(urlString
, CFSTR(":"));
3336 CFStringAppend(urlString
, comp
->password
);
3338 CFStringAppend(urlString
, CFSTR("@"));
3339 hadPrePathComponent
= true;
3342 CFStringAppend(urlString
, comp
->host
);
3343 hadPrePathComponent
= true;
3345 if (comp
->port
!= kCFNotFound
) {
3346 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3347 hadPrePathComponent
= true;
3350 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFArrayGetCount( comp
->pathComponents
) == 0 || CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3351 CFStringAppend(urlString
, CFSTR("/"));
3353 if (comp
->pathComponents
) {
3354 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3355 CFStringAppend(urlString
, pathStr
);
3358 if (comp
->parameterString
) {
3359 CFStringAppend(urlString
, CFSTR(";"));
3360 CFStringAppend(urlString
, comp
->parameterString
);
3363 CFStringAppend(urlString
, CFSTR("?"));
3364 CFStringAppend(urlString
, comp
->query
);
3366 if (comp
->fragment
) {
3367 CFStringAppend(urlString
, CFSTR("#"));
3368 CFStringAppend(urlString
, comp
->fragment
);
3370 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3371 CFRelease(urlString
);
3375 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3376 CFAllocatorRef alloc
= CFGetAllocator(url
);
3377 CFURLComponentsRFC1808 oldComp
;
3379 if (!decomposeToRFC1808(url
, &oldComp
)) {
3382 comp
->scheme
= oldComp
.scheme
;
3384 if (oldComp
.password
) {
3385 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3386 CFRelease(oldComp
.password
);
3387 CFRelease(oldComp
.user
);
3389 comp
->userinfo
= oldComp
.user
;
3392 comp
->userinfo
= NULL
;
3394 comp
->host
= oldComp
.host
;
3395 comp
->port
= oldComp
.port
;
3396 if (!oldComp
.parameterString
) {
3397 comp
->pathComponents
= oldComp
.pathComponents
;
3399 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3400 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3401 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3402 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3404 CFRelease(oldComp
.pathComponents
);
3405 CFRelease(oldComp
.parameterString
);
3407 comp
->query
= oldComp
.query
;
3408 comp
->fragment
= oldComp
.fragment
;
3409 comp
->baseURL
= oldComp
.baseURL
;
3413 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3414 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3415 CFURLRef base
= comp
->baseURL
;
3417 Boolean hadPrePathComponent
= false;
3420 CFStringAppend(urlString
, comp
->scheme
);
3421 CFStringAppend(urlString
, CFSTR("://"));
3422 hadPrePathComponent
= true;
3424 if (comp
->userinfo
) {
3425 CFStringAppend(urlString
, comp
->userinfo
);
3426 CFStringAppend(urlString
, CFSTR("@"));
3427 hadPrePathComponent
= true;
3430 CFStringAppend(urlString
, comp
->host
);
3431 if (comp
->port
!= kCFNotFound
) {
3432 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3434 hadPrePathComponent
= true;
3436 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3437 CFStringAppend(urlString
, CFSTR("/"));
3439 if (comp
->pathComponents
) {
3440 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3441 CFStringAppend(urlString
, pathStr
);
3445 CFStringAppend(urlString
, CFSTR("?"));
3446 CFStringAppend(urlString
, comp
->query
);
3448 if (comp
->fragment
) {
3449 CFStringAppend(urlString
, CFSTR("#"));
3450 CFStringAppend(urlString
, comp
->fragment
);
3452 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3453 CFRelease(urlString
);
3457 #undef CFURLCopyComponents
3458 #undef CFURLCreateFromComponents
3461 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3462 url
= _CFURLFromNSURL(url
);
3463 switch (decompositionType
) {
3464 case kCFURLComponentDecompositionNonHierarchical
:
3465 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3466 case kCFURLComponentDecompositionRFC1808
:
3467 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3468 case kCFURLComponentDecompositionRFC2396
:
3469 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3476 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3477 switch (decompositionType
) {
3478 case kCFURLComponentDecompositionNonHierarchical
:
3479 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3480 case kCFURLComponentDecompositionRFC1808
:
3481 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3482 case kCFURLComponentDecompositionRFC2396
:
3483 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3489 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3490 return _getReserved(url
);
3493 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3494 _setReserved ( (struct __CFURL
*) url
, ptr
);
3497 CF_EXPORT
void *__CFURLResourceInfoPtr(CFURLRef url
) {
3498 return _getResourceInfo(url
);
3501 CF_EXPORT
void __CFURLSetResourceInfoPtr(CFURLRef url
, void *ptr
) {
3502 _setResourceInfo ( (struct __CFURL
*) url
, ptr
);
3505 /* File system stuff */
3507 /* HFSPath<->URLPath functions at the bottom of the file */
3508 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3510 CFMutableArrayRef urlComponents
= NULL
;
3513 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3514 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3517 CFStringRef str
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, 0);
3518 if (CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3519 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR("")); // So we get a leading '/' below
3520 i
= 2; // Skip over the drive letter and the empty string we just inserted
3523 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3524 CFStringRef fileComp
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
,i
);
3525 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3527 // Couldn't decode fileComp
3528 CFRelease(urlComponents
);
3531 if (urlComp
!= fileComp
) {
3532 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3538 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3539 CFArrayAppendValue(urlComponents
, CFSTR(""));
3541 return urlComponents
;
3544 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3545 CFArrayRef urlComponents
;
3548 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3549 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
);
3550 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3552 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3553 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3554 CFRelease(urlComponents
);
3558 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
) {
3559 CFStringRef pathString
= _replacePathIllegalCharacters(path
, alloc
, true);
3560 if (isDirectory
&& CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
)-1) != '/') {
3561 CFStringRef tmp
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@/"), pathString
);
3562 CFRelease(pathString
);
3568 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3569 // This is the easiest case; just remove the percent escape codes and we're done
3570 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3572 CFIndex length
= CFStringGetLength(result
);
3573 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3574 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3582 #if DEPLOYMENT_TARGET_WINDOWS
3583 // From CFPlatform.c
3584 extern CFStringRef
CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr
);
3587 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3588 // Check for a drive letter, then flip all the slashes
3590 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
3591 SInt32 count
= CFArrayGetCount(tmp
);
3592 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
3593 CFStringRef newPath
;
3598 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
3599 CFArrayRemoveValueAtIndex(components
, count
-1);
3603 if (count
> 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
, 0)) == 0) {
3604 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
3605 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, (CFStringRef
)CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
3609 if (firstComponent
) {
3610 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
3612 CFArrayRemoveValueAtIndex(components
, 0);
3614 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
3615 CFArraySetValueAtIndex(components
, 0, driveStr
);
3616 CFRelease(driveStr
);
3619 #if DEPLOYMENT_TARGET_WINDOWS
3621 // From <rdar://problem/5623405> [DEFECT] CFURL returns a Windows path that contains volume name instead of a drive letter
3622 // we need to replace the volume name (it is not valid on Windows) with the drive mounting point path
3623 // remove the first component and set the component with the drive letter to be the first component
3624 CFStringRef driveRootPath
= CFCreateWindowsDrivePathFromVolumeName(firstComponent
);
3626 if (driveRootPath
) {
3627 // remove trailing slash
3628 if (CFStringHasSuffix(driveRootPath
, CFSTR("\\"))) {
3629 CFStringRef newDriveRootPath
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, driveRootPath
, CFRangeMake(0, CFStringGetLength(driveRootPath
) - 1));
3630 CFRelease(driveRootPath
);
3631 driveRootPath
= newDriveRootPath
;
3634 // replace the first component of the path with the drive path
3635 CFArrayRemoveValueAtIndex(components
, 0);
3636 CFArraySetValueAtIndex(components
, 0, driveRootPath
);
3638 CFRelease(driveRootPath
);
3644 if ( firstComponent
) {
3645 CFRelease(firstComponent
);
3648 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
3649 CFRelease(components
);
3650 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
3657 // converts url from a file system path representation to a standard representation
3658 static void _convertToURLRepresentation(struct __CFURL
*url
) {
3659 CFStringRef path
= NULL
;
3660 Boolean isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3661 Boolean isFileReferencePath
= false;
3662 CFAllocatorRef alloc
= CFGetAllocator(url
);
3664 #if DEBUG_URL_MEMORY_USAGE
3665 numFileURLsConverted
++;
3668 switch (URL_PATH_TYPE(url
)) {
3669 case kCFURLPOSIXPathStyle
:
3670 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3671 path
= (CFStringRef
)CFRetain(url
->_string
);
3673 path
= POSIXPathToURLPath(url
->_string
, alloc
, isDir
);
3676 case kCFURLWindowsPathStyle
:
3677 path
= WindowsPathToURLPath(url
->_string
, alloc
, isDir
);
3680 CFAssert2(path
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, url
);
3684 CFMutableStringRef str
= CFStringCreateMutable(alloc
, 0);
3685 CFStringAppend(str
, isFileReferencePath
? CFSTR("file://") : CFSTR("file://localhost"));
3686 CFStringAppend(str
, path
);
3687 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_ABSOLUTE
| IS_PARSED
| HAS_SCHEME
| HAS_FILE_SCHEME
| HAS_HOST
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
| ( isFileReferencePath
? PATH_HAS_FILE_ID
: 0 );
3688 CFRelease(url
->_string
);
3690 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
3691 url
->ranges
[0] = CFRangeMake(0, 4);
3692 url
->ranges
[1] = CFRangeMake(7, isFileReferencePath
? 0 : 9);
3693 url
->ranges
[2] = CFRangeMake(url
->ranges
[1].location
+ url
->ranges
[1].length
, CFStringGetLength(path
));
3696 CFRelease(url
->_string
);
3697 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_PARSED
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3698 url
->_string
= path
;
3699 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
3700 *(url
->ranges
) = CFRangeMake(0, CFStringGetLength(path
));
3705 // relativeURL is known to be a file system URL whose base is a matching file system URL
3706 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
) {
3707 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
3708 CFURLPathStyle fsType
= URL_PATH_TYPE(relativeURL
);
3709 CFURLRef base
= relativeURL
->_base
;
3710 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, (base
->_flags
& IS_DIRECTORY
) != 0, fsType
, alloc
);
3711 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, fsType
, (relativeURL
->_flags
& IS_DIRECTORY
) != 0);
3716 // Caller must release the returned string
3717 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
3718 CFIndex baseLen
= CFStringGetLength(basePath
);
3719 CFIndex relLen
= CFStringGetLength(relativePath
);
3720 UniChar pathDelimiter
= PATH_DELIM_FOR_TYPE(fsType
);
3721 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
3722 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
3724 if (buf
[baseLen
-1] != pathDelimiter
) {
3725 buf
[baseLen
] = pathDelimiter
;
3729 UniChar
*ptr
= buf
+ baseLen
- 1;
3730 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
3733 baseLen
= ptr
- buf
+ 1;
3735 if (fsType
== kCFURLHFSPathStyle
) {
3736 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3739 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
3740 *(buf
+ baseLen
+ relLen
) = '\0';
3741 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
3744 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
3745 CFURLRef url
= NULL
;
3746 uint8_t buf
[CFMaxPathSize
+ 1];
3747 if (_CFGetCurrentDirectory((char *)buf
, CFMaxPathLength
)) {
3748 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen((char *)buf
), true);
3753 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
3754 Boolean isAbsolute
= true;
3756 CFURLRef baseURL
, result
;
3758 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3760 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__
);
3762 len
= CFStringGetLength(filePath
);
3765 case kCFURLPOSIXPathStyle
:
3766 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3768 case kCFURLWindowsPathStyle
:
3769 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3770 /* Absolute path under Win32 can begin with "\\"
3773 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3775 case kCFURLHFSPathStyle
:
3776 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3782 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3784 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
3785 if (baseURL
) CFRelease(baseURL
);
3789 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
3791 Boolean isAbsolute
= true, releaseFilePath
= false, releaseBaseURL
= false;
3792 UniChar pathDelim
= '\0';
3795 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
3796 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3798 len
= CFStringGetLength(filePath
);
3801 case kCFURLPOSIXPathStyle
:
3802 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3806 case kCFURLWindowsPathStyle
:
3807 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3808 /* Absolute path under Win32 can begin with "\\"
3812 isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3815 case kCFURLHFSPathStyle
:
3816 { CFRange fullStrRange
= CFRangeMake( 0, CFStringGetLength( filePath
) );
3818 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3821 if ( filePath
&& CFStringFindWithOptions( filePath
, CFSTR("::"), fullStrRange
, 0, NULL
) ) {
3822 UniChar
* chars
= (UniChar
*) malloc( fullStrRange
.length
* sizeof( UniChar
) );
3823 CFIndex index
, writeIndex
, firstColonOffset
= -1;
3825 CFStringGetCharacters( filePath
, fullStrRange
, chars
);
3827 for ( index
= 0, writeIndex
= 0 ; index
< fullStrRange
.length
; index
++ ) {
3828 if ( chars
[ index
] == ':' ) {
3829 if ( index
+ 1 < fullStrRange
.length
&& chars
[ index
+ 1 ] == ':' ) {
3831 // Don't let :: go off the 'top' of the path -- which means that there always has to be at
3832 // least one ':' to the left of the current write position to go back to.
3833 if ( writeIndex
> 0 && firstColonOffset
>= 0 )
3836 while ( writeIndex
> 0 && writeIndex
>= firstColonOffset
&& chars
[ writeIndex
] != ':' )
3839 index
++; // skip over the first ':', so we replace the ':' which is there with a new one
3842 if ( firstColonOffset
== -1 )
3843 firstColonOffset
= writeIndex
;
3846 chars
[ writeIndex
++ ] = chars
[ index
];
3849 if ( releaseFilePath
&& filePath
)
3850 CFRelease( filePath
);
3852 filePath
= CFStringCreateWithCharacters( allocator
, chars
, writeIndex
);
3853 // reset len because a canonical HFS path can be a different length than the original CFString
3854 len
= CFStringGetLength(filePath
);
3855 releaseFilePath
= true;
3866 else if ( baseURL
== NULL
) {
3867 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3868 releaseBaseURL
= true;
3872 if (isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) != pathDelim
) {
3873 CFMutableStringRef tempRef
= CFStringCreateMutable(allocator
, 0);
3874 CFStringAppend(tempRef
, filePath
);
3875 CFStringAppendCharacters(tempRef
, &pathDelim
, 1);
3876 if ( releaseFilePath
&& filePath
) CFRelease( filePath
);
3878 releaseFilePath
= true;
3879 } else if (!isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) == pathDelim
) {
3880 if (len
== 1 || CFStringGetCharacterAtIndex(filePath
, len
-2) == pathDelim
) {
3881 // Override isDirectory
3884 CFStringRef tempRef
= CFStringCreateWithSubstring(allocator
, filePath
, CFRangeMake(0, len
-1));
3885 if ( releaseFilePath
&& filePath
)
3886 CFRelease( filePath
);
3888 releaseFilePath
= true;
3891 if (!filePath
|| CFStringGetLength(filePath
) == 0) {
3892 if (releaseFilePath
&& filePath
) CFRelease(filePath
);
3893 if (releaseBaseURL
&& baseURL
) CFRelease(baseURL
);
3896 url
= _CFURLAlloc(allocator
);
3897 _CFURLInit((struct __CFURL
*)url
, filePath
, fsType
, baseURL
);
3898 if (releaseFilePath
) CFRelease(filePath
);
3899 if (releaseBaseURL
&& baseURL
) CFRelease(baseURL
);
3900 if (isDirectory
) ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
3901 if (fsType
== kCFURLPOSIXPathStyle
) {
3902 // Check if relative path is equivalent to URL representation; this will be true if url->_string contains only characters from the unreserved character set, plus '/' to delimit the path, plus ';', '@', '&', '=', '+', '$', ',' (according to RFC 2396) -- REW, 12/1/2000
3903 // Per Section 5 of RFC 2396, there's a special problem if a colon apears in the first path segment - in this position, it can be mistaken for the scheme name. Otherwise, it's o.k., and can be safely identified as part of the path. In this one case, we need to prepend "./" to make it clear what's going on.... -- REW, 8/24/2001
3904 CFStringInlineBuffer buf
;
3905 Boolean sawSlash
= FALSE
;
3906 Boolean mustPrependDotSlash
= FALSE
;
3907 CFIndex idx
, length
= CFStringGetLength(url
->_string
);
3908 CFStringInitInlineBuffer(url
->_string
, &buf
, CFRangeMake(0, length
));
3909 for (idx
= 0; idx
< length
; idx
++) {
3910 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
3911 if (!isPathLegalCharacter(ch
)) break;
3915 } else if (ch
== ':') {
3916 mustPrependDotSlash
= TRUE
;
3920 if (idx
== length
) {
3921 ((struct __CFURL
*)url
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
3923 if (mustPrependDotSlash
) {
3924 CFMutableStringRef newString
= CFStringCreateMutable(allocator
, 0);
3925 CFStringAppend(newString
, CFSTR("./"));
3926 CFStringAppend(newString
, url
->_string
);
3927 CFRelease(url
->_string
);
3928 ((struct __CFURL
*)url
)->_string
= newString
;
3934 static Boolean
_pathHasFileIDPrefix( CFStringRef path
)
3936 // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
3937 CFStringRef fileIDPrefix
= CFSTR( "/" FILE_ID_PREFIX
"/" );
3938 return path
&& CFStringHasPrefix( path
, fileIDPrefix
) && CFStringGetLength( path
) > CFStringGetLength( fileIDPrefix
);
3942 static Boolean
_pathHasFileIDOnly( CFStringRef path
)
3944 // Is file ID rooted and contains no additonal path segments
3946 return _pathHasFileIDPrefix( path
) && ( !CFStringFindWithOptions( path
, CFSTR("/"), CFRangeMake( sizeof(FILE_ID_PREFIX
) + 1, CFStringGetLength( path
) - sizeof(FILE_ID_PREFIX
) - 1), 0, &slashRange
) || slashRange
.location
== CFStringGetLength( path
) - 1 );
3950 CF_EXPORT CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
3951 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(pathStyle
), __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
3953 return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL
), anURL
, pathStyle
, false);
3957 // 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
3958 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
3959 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
3960 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
3961 CFStringRef relPath
= NULL
;
3963 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3964 // We can grope the ivars
3965 CFURLPathStyle myType
= URL_PATH_TYPE(anURL
);
3966 if (myType
== fsType
) {
3967 relPath
= (CFStringRef
)CFRetain(anURL
->_string
);
3968 } else if (fsType
== kCFURLPOSIXPathStyle
&& myType
== FULL_URL_REPRESENTATION
) {
3969 if (!(anURL
->_flags
& IS_PARSED
)) {
3970 _parseComponentsOfURL(anURL
);
3972 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3973 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
3978 if (relPath
== NULL
) {
3979 CFStringRef urlPath
= CFURLCopyPath(anURL
);
3980 CFStringEncoding enc
= (anURL
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: anURL
->_encoding
;
3983 case kCFURLPOSIXPathStyle
:
3984 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
3986 case kCFURLHFSPathStyle
:
3989 case kCFURLWindowsPathStyle
:
3990 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
3993 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
3999 // For Tiger, leave this behavior in for all path types. For Leopard, it would be nice to remove this entirely
4000 // and do a linked-on-or-later check so we don't break third parties.
4001 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
4002 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
4003 if ( relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == PATH_DELIM_FOR_TYPE(fsType
)) {
4004 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
4009 // Note that !resolveAgainstBase implies !base
4010 if (!basePath
|| !relPath
) {
4012 CFRelease(basePath
);
4016 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
4017 CFRelease(basePath
);
4023 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
4025 CFAllocatorRef alloc
= CFGetAllocator(url
);
4027 if (!url
) return false;
4028 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
4029 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
4031 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
4035 #elif DEPLOYMENT_TARGET_WINDOWS
4036 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
4039 CFIndex pathLen
= CFStringGetLength(path
);
4040 CFIndex numConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLen
), CFStringFileSystemEncoding(), 0, true, buffer
, bufLen
-1, &usedLen
); // -1 because we need one byte to zero-terminate.
4042 if (numConverted
== pathLen
) {
4043 buffer
[usedLen
] = '\0';
4048 #error Unknown or unspecified DEPLOYMENT_TARGET
4053 #if DEPLOYMENT_TARGET_WINDOWS
4054 CF_EXPORT Boolean
_CFURLGetWideFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, wchar_t *buffer
, CFIndex bufferLength
) {
4055 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
4056 CFIndex pathLength
, charsConverted
, usedBufLen
;
4057 if (!path
) return false;
4058 pathLength
= CFStringGetLength(path
);
4059 if (pathLength
+1 > bufferLength
) {
4063 charsConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLength
), kCFStringEncodingUTF16
, 0, false, (UInt8
*)buffer
, bufferLength
*sizeof(wchar_t), &usedBufLen
);
4064 // CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer);
4066 if (charsConverted
!= pathLength
|| usedBufLen%sizeof
(wchar_t) != 0) {
4069 buffer
[usedBufLen
/sizeof(wchar_t)] = 0;
4070 // buffer[pathLength] = 0;
4076 static CFStringRef
_createPathFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
4077 char pathDelim
= PATH_SEP
;
4079 if ( isDirectory
) {
4080 // it is a directory: if it doesn't end with pathDelim, append a pathDelim. Limit stack buffer to PATH_MAX+1 in case a large bogus value is passed.
4081 if ( (bufLen
> 0) && (bufLen
<= PATH_MAX
) && (buffer
[bufLen
-1] != pathDelim
) ) {
4082 STACK_BUFFER_DECL(uint8_t, tempBuf
, bufLen
+ 1);
4083 memcpy(tempBuf
, buffer
, bufLen
);
4084 tempBuf
[bufLen
] = pathDelim
;
4085 path
= CFStringCreateWithBytes(allocator
, tempBuf
, bufLen
+ 1, CFStringFileSystemEncoding(), false);
4088 // already had pathDelim at end of buffer or bufLen is really large
4089 path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4093 // it is not a directory: remove any pathDelim characters at end (leaving at least one character)
4094 while ( (bufLen
> 1) && (buffer
[bufLen
-1] == pathDelim
) ) {
4097 path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4102 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
4103 CFStringRef path
= _createPathFromFileSystemRepresentation(allocator
, buffer
, bufLen
, isDirectory
);
4105 if (!path
) return NULL
;
4106 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
4107 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
);
4108 #elif DEPLOYMENT_TARGET_WINDOWS
4109 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
);
4111 #error Unknown or unspecified DEPLOYMENT_TARGET
4117 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
4118 CFStringRef path
= _createPathFromFileSystemRepresentation(allocator
, buffer
, bufLen
, isDirectory
);
4120 if (!path
) return NULL
;
4121 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
4122 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
, baseURL
);
4123 #elif DEPLOYMENT_TARGET_WINDOWS
4124 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
4126 #error Unknown or unspecified DEPLOYMENT_TARGET
4133 /******************************/
4134 /* Support for path utilities */
4135 /******************************/
4137 // Assumes url is a CFURL (not an Obj-C NSURL)
4138 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
4139 UInt32 pathType
= URL_PATH_TYPE(url
);
4140 CFRange pathRg
, componentRg
;
4142 if (pathType
== FULL_URL_REPRESENTATION
) {
4143 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4144 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4146 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
4149 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
4153 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == PATH_DELIM_FOR_TYPE(pathType
)) {
4155 if (pathRg
.length
== 0) {
4160 if (CFStringFindWithOptions(url
->_string
, PATH_DELIM_AS_STRING_FOR_TYPE(pathType
), pathRg
, kCFCompareBackwards
, &componentRg
)) {
4161 componentRg
.location
++;
4162 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
4164 componentRg
= pathRg
;
4169 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
4172 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4173 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
4176 if (!path
) return NULL
;
4177 rg
= CFRangeMake(0, CFStringGetLength(path
));
4178 if ( rg
.length
== 0 ) return path
;
4179 length
= rg
.length
; // Remember this for comparison later
4180 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/' ) {
4183 if ( rg
.length
== 0 )
4185 // If we have reduced the string to empty, then it's "/", and that's what we return as
4186 // the last path component.
4189 else if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
4190 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
4191 rg
.location
= compRg
.location
+ 1;
4193 if (rg
.location
== 0 && rg
.length
== length
) {
4196 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), path
, rg
);
4200 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4201 Boolean filePathURLCreated
= false;
4203 if ( _CFURLIsFileReferenceURL(url
) ) {
4204 // use a file path URL or fail
4205 CFURLRef filePathURL
= CFURLCreateFilePathURL(CFGetAllocator(url
), url
, NULL
);
4206 if ( filePathURL
) {
4207 filePathURLCreated
= TRUE
;
4216 CFRange rg
= _rangeOfLastPathComponent(url
);
4217 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
4219 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4220 if ( filePathURLCreated
) {
4224 return (CFStringRef
)CFRetain(CFSTR(""));
4226 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url
))) {
4227 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4228 if ( filePathURLCreated
) {
4232 return (CFStringRef
)CFRetain(CFSTR("/"));
4234 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
4235 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
&& !(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
4237 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
4238 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
4240 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
4245 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4246 if ( filePathURLCreated
) {
4254 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
4255 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
4256 CFStringRef ext
= NULL
;
4259 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
4260 if (rg
.location
!= kCFNotFound
) {
4262 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
4263 if (rg
.length
> 0) {
4264 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
4266 ext
= (CFStringRef
)CFRetain(CFSTR(""));
4269 CFRelease(lastPathComp
);
4274 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
4277 url
= _CFURLFromNSURL(url
);
4278 __CFGenericValidateType(url
, __kCFURLTypeID
);
4279 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
4281 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4282 Boolean filePathURLCreated
= false;
4284 if ( _CFURLIsFileReferenceURL(url
) ) {
4285 // use a file path URL if possible (only because this is appending a path component)
4286 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4287 if ( filePathURL
) {
4288 filePathURLCreated
= TRUE
;
4294 fsType
= URL_PATH_TYPE(url
);
4295 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(pathComponent
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(pathComponent
)), 0, NULL
)) {
4296 // Must convert to full representation, and then work with it
4297 fsType
= FULL_URL_REPRESENTATION
;
4298 _convertToURLRepresentation((struct __CFURL
*)url
);
4301 if (fsType
== FULL_URL_REPRESENTATION
) {
4302 CFMutableStringRef newString
;
4303 CFStringRef newComp
;
4305 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4306 if (!(url
->_flags
& HAS_PATH
)) {
4310 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4311 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4312 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4313 if (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
4314 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4317 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
4319 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
4322 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4323 CFRelease(newString
);
4326 UniChar pathDelim
= PATH_DELIM_FOR_TYPE(fsType
);
4327 CFStringRef newString
;
4328 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != pathDelim
) {
4330 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@%c"), url
->_string
, pathDelim
, pathComponent
, pathDelim
);
4332 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@"), url
->_string
, pathDelim
, pathComponent
);
4336 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@%c"), url
->_string
, pathComponent
, pathDelim
);
4338 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@"), url
->_string
, pathComponent
);
4341 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, isDirectory
, url
->_base
);
4342 CFRelease(newString
);
4344 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4345 if ( filePathURLCreated
) {
4352 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
4354 CFMutableStringRef newString
;
4355 CFRange lastCompRg
, pathRg
;
4356 Boolean appendDotDot
= false;
4359 url
= _CFURLFromNSURL(url
);
4360 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4361 __CFGenericValidateType(url
, __kCFURLTypeID
);
4363 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4364 Boolean filePathURLCreated
= false;
4366 if ( _CFURLIsFileReferenceURL(url
) ) {
4367 // use a file path URL or fail
4368 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4369 if ( filePathURL
) {
4370 filePathURLCreated
= TRUE
;
4379 fsType
= URL_PATH_TYPE(url
);
4380 if (fsType
== FULL_URL_REPRESENTATION
) {
4381 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4382 if (!(url
->_flags
& HAS_PATH
)) {
4383 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4384 if ( filePathURLCreated
) {
4390 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4392 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
4394 lastCompRg
= _rangeOfLastPathComponent(url
);
4395 if (lastCompRg
.length
== 0) {
4396 appendDotDot
= true;
4397 } else if (lastCompRg
.length
== 1) {
4398 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
4399 if (ch
== '.' || ch
== PATH_DELIM_FOR_TYPE(fsType
)) {
4400 appendDotDot
= true;
4402 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
4403 appendDotDot
= true;
4406 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4409 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != PATH_DELIM_FOR_TYPE(fsType
)) {
4410 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4413 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
4415 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4417 // 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 "/.".
4418 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
4419 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == PATH_DELIM_FOR_TYPE(fsType
)) {
4420 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
4421 } else if (pathRg
.length
+delta
== 5) {
4422 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
4425 } else if (lastCompRg
.location
== pathRg
.location
) {
4426 CFStringReplace(newString
, pathRg
, CFSTR("."));
4427 CFStringInsert(newString
, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4429 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
4431 if (fsType
== FULL_URL_REPRESENTATION
) {
4432 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4434 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, true, url
->_base
);
4436 CFRelease(newString
);
4437 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4438 if ( filePathURLCreated
) {
4445 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
4446 CFMutableStringRef newString
;
4449 CFURLPathStyle fsType
;
4451 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4452 url
= _CFURLFromNSURL(url
);
4453 __CFGenericValidateType(url
, __kCFURLTypeID
);
4454 __CFGenericValidateType(extension
, CFStringGetTypeID());
4456 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4457 Boolean filePathURLCreated
= false;
4459 if ( _CFURLIsFileReferenceURL(url
) ) {
4460 // use a file path URL or fail
4461 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4462 if ( filePathURL
) {
4463 filePathURLCreated
= TRUE
;
4472 rg
= _rangeOfLastPathComponent(url
);
4473 if (rg
.location
< 0) {
4474 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4475 if ( filePathURLCreated
) {
4479 return NULL
; // No path
4481 fsType
= URL_PATH_TYPE(url
);
4482 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(extension
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(extension
)), 0, NULL
)) {
4483 _convertToURLRepresentation((struct __CFURL
*)url
);
4484 fsType
= FULL_URL_REPRESENTATION
;
4485 rg
= _rangeOfLastPathComponent(url
);
4488 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4489 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
4490 if (fsType
== FULL_URL_REPRESENTATION
) {
4491 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4492 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
4494 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4496 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, extension
);
4497 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4499 CFRelease(newString
);
4500 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4501 if ( filePathURLCreated
) {
4508 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4512 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4513 url
= _CFURLFromNSURL(url
);
4514 __CFGenericValidateType(url
, __kCFURLTypeID
);
4516 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4517 Boolean filePathURLCreated
= false;
4519 if ( _CFURLIsFileReferenceURL(url
) ) {
4520 // use a file path URL or fail
4521 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4522 if ( filePathURL
) {
4523 filePathURLCreated
= TRUE
;
4532 rg
= _rangeOfLastPathComponent(url
);
4533 if (rg
.location
< 0) {
4535 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
4536 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4537 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
4538 CFStringDelete(newString
, dotRg
);
4539 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
) {
4540 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4542 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, URL_PATH_TYPE(url
), (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4544 CFRelease(newString
);
4546 result
= (CFURLRef
)CFRetain(url
);
4548 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4549 if ( filePathURLCreated
) {
4557 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4558 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4559 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4560 CFMutableArrayRef newComponents
= CFArrayCreateMutableCopy(alloc
, 0, components
);
4561 Boolean doSpecialLeadingColon
= false;
4562 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
4564 CFRelease(components
);
4567 if (!doSpecialLeadingColon
&& firstChar
!= ':') {
4568 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4569 } else if (firstChar
!= ':') {
4570 // see what we need to add at the beginning. Under MacOS, if the
4571 // first character isn't a ':', then the first component is the
4572 // volume name, and we need to find the mount point. Bleah. If we
4573 // don't find a mount point, we're going to have to lie, and make something up.
4574 CFStringRef firstComp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0);
4575 if (CFStringGetLength(firstComp
) == 1 && CFStringGetCharacterAtIndex(firstComp
, 0) == '/') {
4576 // "/" is the "magic" path for a UFS root directory
4577 CFArrayRemoveValueAtIndex(newComponents
, 0);
4578 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4580 // See if we can get a mount point.
4581 Boolean foundMountPoint
= false;
4582 if (!foundMountPoint
) {
4583 // Fall back to treating the volume name as the top level directory
4584 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4588 CFArrayRemoveValueAtIndex(newComponents
, 0);
4591 cnt
= CFArrayGetCount(newComponents
);
4592 for (i
= 0; i
< cnt
; i
++) {
4593 CFStringRef comp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, i
);
4594 CFStringRef newComp
= NULL
;
4595 CFRange searchRg
, slashRg
;
4596 searchRg
.location
= 0;
4597 searchRg
.length
= CFStringGetLength(comp
);
4598 while (CFStringFindWithOptions(comp
, CFSTR("/"), searchRg
, 0, &slashRg
)) {
4600 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4602 CFStringReplace((CFMutableStringRef
)newComp
, slashRg
, CFSTR(":"));
4603 searchRg
.length
= searchRg
.location
+ searchRg
.length
- slashRg
.location
- 1;
4604 searchRg
.location
= slashRg
.location
+ 1;
4607 CFArraySetValueAtIndex(newComponents
, i
, newComp
);
4611 if (isDir
&& CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, cnt
-1)) != 0) {
4612 CFArrayAppendValue(newComponents
, CFSTR(""));
4614 return newComponents
;
4616 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4617 CFArrayRef components
= HFSPathToURLComponents(path
, alloc
, isDir
);
4618 CFArrayRef newComponents
= components
? copyStringArrayWithTransformation(components
, escapePathComponent
) : NULL
;
4621 if (components
) CFRelease(components
);
4622 if (!newComponents
) return NULL
;
4624 cnt
= CFArrayGetCount(newComponents
);
4625 if (cnt
== 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0)) == 0) {
4626 result
= (CFStringRef
)CFRetain(CFSTR("/"));
4628 result
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
4630 CFRelease(newComponents
);
4637 // keys and vals must have space for at least 4 key/value pairs. No argument can be NULL.
4638 // Caller must release values, but not keys
4639 static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url
, CFTypeRef
*keys
, CFTypeRef
*vals
, CFIndex
*count
) {
4640 CFAllocatorRef alloc
= CFGetAllocator(url
);
4641 CFURLRef base
= CFURLGetBaseURL(url
);
4642 keys
[0] = CFSTR("_CFURLStringType");
4643 keys
[1] = CFSTR("_CFURLString");
4644 keys
[2] = CFSTR("_CFURLBaseStringType");
4645 keys
[3] = CFSTR("_CFURLBaseURLString");
4646 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4647 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4648 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4649 vals
[1] = CFURLGetString(url
);
4651 SInt32 urlType
= URL_PATH_TYPE(url
);
4652 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4653 if (url
->_flags
& IS_DIRECTORY
) {
4654 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4655 vals
[1] = CFRetain(url
->_string
);
4657 vals
[1] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), url
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4660 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4661 vals
[1] = CFRetain(url
->_string
);
4663 vals
[1] = CFStringCreateWithSubstring(alloc
, url
->_string
, CFRangeMake(0, CFStringGetLength(url
->_string
) - 1));
4668 if (CF_IS_OBJC(__kCFURLTypeID
, base
)) {
4669 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4670 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4671 vals
[3] = CFURLGetString(base
);
4673 SInt32 urlType
= URL_PATH_TYPE(base
);
4674 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4675 if (base
->_flags
& IS_DIRECTORY
) {
4676 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4677 vals
[3] = CFRetain(base
->_string
);
4679 vals
[3] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), base
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4682 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4683 vals
[3] = CFRetain(base
->_string
);
4685 vals
[3] = CFStringCreateWithSubstring(alloc
, base
->_string
, CFRangeMake(0, CFStringGetLength(base
->_string
) - 1));
4695 // Private API for Finder to use
4696 CFPropertyListRef
_CFURLCopyPropertyListRepresentation(CFURLRef url
) {
4697 CFTypeRef keys
[4], vals
[4];
4698 CFDictionaryRef dict
;
4700 __CFURLCopyPropertyListKeysAndValues(url
, keys
, vals
, &count
);
4701 dict
= CFDictionaryCreate(CFGetAllocator(url
), keys
, vals
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4702 for (idx
= 0; idx
< count
; idx
++) {
4703 CFRelease(vals
[idx
]);
4708 CFURLRef
_CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc
, CFPropertyListRef pListRepresentation
) {
4709 CFStringRef baseString
, string
;
4710 CFNumberRef baseTypeNum
, urlTypeNum
;
4711 SInt32 baseType
, urlType
;
4712 CFURLRef baseURL
= NULL
, url
;
4713 CFDictionaryRef dict
= (CFDictionaryRef
)pListRepresentation
;
4715 // Start by getting all the pieces and verifying they're of the correct type.
4716 if (CFGetTypeID(pListRepresentation
) != CFDictionaryGetTypeID()) {
4719 string
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLString"));
4720 if (!string
|| CFGetTypeID(string
) != CFStringGetTypeID()) {
4723 urlTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLStringType"));
4724 if (!urlTypeNum
|| CFGetTypeID(urlTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum
, kCFNumberSInt32Type
, &urlType
) || (urlType
!= FULL_URL_REPRESENTATION
&& urlType
!= kCFURLPOSIXPathStyle
&& urlType
!= kCFURLHFSPathStyle
&& urlType
!= kCFURLWindowsPathStyle
)) {
4727 baseString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseURLString"));
4729 if (CFGetTypeID(baseString
) != CFStringGetTypeID()) {
4732 baseTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseStringType"));
4733 if (!baseTypeNum
|| CFGetTypeID(baseTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum
, kCFNumberSInt32Type
, &baseType
) ||
4734 (baseType
!= FULL_URL_REPRESENTATION
&& baseType
!= kCFURLPOSIXPathStyle
&& baseType
!= kCFURLHFSPathStyle
&& baseType
!= kCFURLWindowsPathStyle
)) {
4737 if (baseType
== FULL_URL_REPRESENTATION
) {
4738 baseURL
= _CFURLCreateWithArbitraryString(alloc
, baseString
, NULL
);
4740 baseURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, baseString
, (CFURLPathStyle
)baseType
, CFStringGetCharacterAtIndex(baseString
, CFStringGetLength(baseString
)-1) == PATH_DELIM_FOR_TYPE(baseType
), NULL
);
4743 if (urlType
== FULL_URL_REPRESENTATION
) {
4744 url
= _CFURLCreateWithArbitraryString(alloc
, string
, baseURL
);
4746 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, string
, (CFURLPathStyle
)urlType
, CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
)-1) == PATH_DELIM_FOR_TYPE(urlType
), baseURL
);
4748 if (baseURL
) CFRelease(baseURL
);
4752 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4753 Boolean
_CFURLIsFileReferenceURL(CFURLRef url
)
4755 // returns TRUE if url is is a file URL whose path starts with a file ID reference
4757 return _CFURLIsFileReferenceURL( url
->_base
);
4759 if (URL_PATH_TYPE(url
) == kCFURLFileReferencePathStyle
) {
4761 } else if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
) {
4762 if (!(url
->_flags
& IS_PARSED
)) {
4763 _parseComponentsOfURL(url
);
4765 return ((url
->_flags
& HAS_FILE_SCHEME
) && (url
->_flags
& PATH_HAS_FILE_ID
));
4767 // Otherwise, the path type is some other non-ID path type
4771 Boolean
_CFURLIsFileURL(CFURLRef url
)
4774 return _CFURLIsFileURL( url
->_base
);
4776 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
4778 } else if (!(url
->_flags
& IS_PARSED
)) {
4779 _parseComponentsOfURL(url
);
4781 return (url
->_flags
& HAS_FILE_SCHEME
) != 0;
4785 CFURLRef
CFURLCreateFilePathURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
)
4787 CFURLRef result
= NULL
;
4788 if (!_CFURLIsFileURL(url
)) {
4790 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLReadUnsupportedSchemeError
, NULL
);
4794 // File URL. Form of the path is unknown. Make a new URL.
4795 CFStringRef newURLString
;
4800 if ( CFURLGetBaseURL( url
)) {
4801 CFURLRef absURL
= CFURLCopyAbsoluteURL( url
);
4802 fsPath
= CFURLCopyFileSystemPath( absURL
, kCFURLPOSIXPathStyle
);
4803 netLoc
= CFURLCopyNetLocation( absURL
);
4804 rSpec
= CFURLCopyResourceSpecifier( absURL
);
4805 CFRelease( absURL
);
4807 fsPath
= CFURLCopyFileSystemPath( url
, kCFURLPOSIXPathStyle
);
4808 netLoc
= CFURLCopyNetLocation( url
);
4809 rSpec
= CFURLCopyResourceSpecifier( url
);
4812 CFStringRef urlPath
= _replacePathIllegalCharacters( fsPath
, alloc
, true );
4813 newURLString
= CFStringCreateWithFormat( alloc
, NULL
, CFSTR("file://%@%@%@%@"), (netLoc
? netLoc
: CFSTR("")), urlPath
, ((CFStringCompare(urlPath
, CFSTR("/"), 0) != kCFCompareEqualTo
) ? (CFURLHasDirectoryPath( url
) ? CFSTR("/") : CFSTR("")) : CFSTR("")), (rSpec
? rSpec
: CFSTR("")));
4814 result
= CFURLCreateWithString( alloc
, newURLString
, NULL
);
4815 CFRelease( newURLString
);
4816 CFRelease( urlPath
);
4817 CFRelease( fsPath
);
4820 // Would be better here to get an underlying error back from CFURLCopyFileSystemPath
4821 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLNoSuchResourceError
, NULL
);
4826 CFRelease( netLoc
);
4837 CFURLRef
CFURLCreateFileReferenceURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
) {