2 * Copyright (c) 2010 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-2009, Apple Inc. All rights reserved.
26 Responsibility: Becky Willrich
29 #include <CoreFoundation/CFURL.h>
30 #include <CoreFoundation/CFPriv.h>
31 #include <CoreFoundation/CFCharacterSetPriv.h>
32 #include <CoreFoundation/CFNumber.h>
33 #include "CFInternal.h"
34 #include <CoreFoundation/CFStringEncodingConverter.h>
35 #include <CoreFoundation/CFPriv.h>
40 #if DEPLOYMENT_TARGET_MACOSX
41 #include <CoreFoundation/CFNumberFormatter.h>
44 #include <sys/types.h>
45 #elif DEPLOYMENT_TARGET_EMBEDDED
48 #include <sys/types.h>
49 #elif DEPLOYMENT_TARGET_WINDOWS
51 #error Unknown or unspecified DEPLOYMENT_TARGET
54 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
55 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
57 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
58 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
59 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
);
60 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
61 CF_EXPORT CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
62 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
63 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
64 #elif DEPLOYMENT_TARGET_WINDOWS
66 #error Unknown or unspecified DEPLOYMENT_TARGET
71 #ifndef DEBUG_URL_MEMORY_USAGE
72 #define DEBUG_URL_MEMORY_USAGE 0
75 #if DEBUG_URL_MEMORY_USAGE
76 static CFAllocatorRef URLAllocator
= NULL
;
77 static UInt32 numFileURLsCreated
= 0;
78 static UInt32 numFileURLsConverted
= 0;
79 static UInt32 numFileURLsDealloced
= 0;
80 static UInt32 numURLs
= 0;
81 static UInt32 numDealloced
= 0;
82 static UInt32 numExtraDataAllocated
= 0;
83 static UInt32 numURLsWithBaseURL
= 0;
84 static UInt32 numNonUTF8EncodedURLs
= 0;
87 /* The bit flags in myURL->_flags */
88 #define HAS_SCHEME (0x0001)
89 #define HAS_USER (0x0002)
90 #define HAS_PASSWORD (0x0004)
91 #define HAS_HOST (0x0008)
92 #define HAS_PORT (0x0010)
93 #define HAS_PATH (0x0020)
94 #define HAS_PARAMETERS (0x0040)
95 #define HAS_QUERY (0x0080)
96 #define HAS_FRAGMENT (0x0100)
97 #define HAS_HTTP_SCHEME (0x0200)
98 // Last free bit (0x200) in lower word goes here!
99 #define IS_IPV6_ENCODED (0x0400)
100 #define IS_OLD_UTF8_STYLE (0x0800)
101 #define IS_DIRECTORY (0x1000)
102 #define IS_PARSED (0x2000)
103 #define IS_ABSOLUTE (0x4000)
104 #define IS_DECOMPOSABLE (0x8000)
106 #define PATH_TYPE_MASK (0x000F0000)
107 /* 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 */
108 #define POSIX_AND_URL_PATHS_MATCH (0x00100000)
109 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000)
111 /* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */
112 // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
113 // #define SCHEME_DIFFERS (0x00400000) unused
114 #define USER_DIFFERS (0x00800000)
115 #define PASSWORD_DIFFERS (0x01000000)
116 #define HOST_DIFFERS (0x02000000)
117 // 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
118 #define PORT_DIFFERS (0x04000000)
119 // #define PATH_DIFFERS (0x08000000) unused
120 // #define PARAMETERS_DIFFER (0x10000000) unused
121 // #define QUERY_DIFFERS (0x20000000) unused
122 #define PATH_HAS_FILE_ID (0x40000000)
123 #define HAS_FILE_SCHEME (0x80000000)
125 // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag
126 #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22)
128 // Other useful defines
129 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
130 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
131 #define FULL_URL_REPRESENTATION (0xF)
133 /* 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! */
134 #define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16)
135 #define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/'))
136 #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/")))
138 #define FILE_ID_PREFIX ".file"
139 #define FILE_ID_KEY "id"
141 #define ASSERT_CHECK_PATHSTYLE(x) 0
143 #if DEPLOYMENT_TARGET_WINDOWS
144 #define PATH_SEP '\\'
145 #define PATH_MAX MAX_PATH
150 // In order to reduce the sizeof ( __CFURL ), move these items into a seperate structure which is
151 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
152 // either a sanitized string or a reserved pointer for URLHandle.
153 struct _CFURLAdditionalData
{
154 void *_reserved
; // Reserved for URLHandle's use.
155 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
156 CFHashCode hashValue
;
160 CFRuntimeBase _cfBase
;
162 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set.
163 CFStringRef _string
; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above)
166 struct _CFURLAdditionalData
* extra
;
167 void *_resourceInfo
; // For use by CarbonCore to cache property values. Retained and released by CFURL.
171 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
173 if ( url
&& url
->extra
)
174 return url
->extra
->_reserved
;
179 CF_INLINE CFMutableStringRef
_getSanitizedString ( const struct __CFURL
* url
)
181 if ( url
&& url
->extra
)
182 return url
->extra
->_sanitizedString
;
187 static void* _getResourceInfo ( const struct __CFURL
* url
)
190 return url
->_resourceInfo
;
196 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
198 if ( url
&& ! url
->extra
)
199 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), __kCFAllocatorGCScannedMemory
);
201 extra
->_reserved
= _getReserved( url
);
202 extra
->_sanitizedString
= _getSanitizedString( url
);
203 extra
->hashValue
= 0;
207 #if DEBUG_URL_MEMORY_USAGE
208 numExtraDataAllocated
++;
213 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
217 // Don't allocate extra space if we're just going to be storing NULL
218 if ( ! url
->extra
&& reserved
)
219 _CFURLAllocateExtraDataspace( url
);
222 __CFAssignWithWriteBarrier((void **)&url
->extra
->_reserved
, reserved
);
226 CF_INLINE
void _setSanitizedString ( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
230 // Don't allocate extra space if we're just going to be storing NULL
231 if ( ! url
->extra
&& sanitizedString
)
232 _CFURLAllocateExtraDataspace( url
);
235 url
->extra
->_sanitizedString
= sanitizedString
;
239 static void _setResourceInfo ( struct __CFURL
* url
, void* resourceInfo
)
243 if ( url
&& OSAtomicCompareAndSwapPtrBarrier( NULL
, resourceInfo
, &url
->_resourceInfo
)) {
244 CFRetain( resourceInfo
);
248 static void _convertToURLRepresentation(struct __CFURL
*url
);
249 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
);
250 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
251 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
252 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
253 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
254 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
255 static void computeSanitizedString(CFURLRef url
);
256 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
257 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
258 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
261 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
262 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->ranges
));
265 static Boolean _createOldUTF8StyleURLs
= false;
267 CF_INLINE Boolean
createOldUTF8StyleURLs(void) {
268 return (_createOldUTF8StyleURLs
);
271 // Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW
273 void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs
) {
274 _createOldUTF8StyleURLs
= createUTF8URLs
;
285 static const unsigned char sURLValidCharacters
[] = {
287 /* '!' 33 */ VALID
| UNRESERVED
| PATHVALID
,
290 /* '$' 36 */ VALID
| PATHVALID
,
292 /* '&' 38 */ VALID
| PATHVALID
,
293 /* ''' 39 */ VALID
| UNRESERVED
| PATHVALID
,
294 /* '(' 40 */ VALID
| UNRESERVED
| PATHVALID
,
295 /* ')' 41 */ VALID
| UNRESERVED
| PATHVALID
,
296 /* '*' 42 */ VALID
| UNRESERVED
| PATHVALID
,
297 /* '+' 43 */ VALID
| SCHEME
| PATHVALID
,
298 /* ',' 44 */ VALID
| PATHVALID
,
299 /* '-' 45 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
300 /* '.' 46 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
301 /* '/' 47 */ VALID
| PATHVALID
,
302 /* '0' 48 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
303 /* '1' 49 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
304 /* '2' 50 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
305 /* '3' 51 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
306 /* '4' 52 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
307 /* '5' 53 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
308 /* '6' 54 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
309 /* '7' 55 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
310 /* '8' 56 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
311 /* '9' 57 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
315 /* '=' 61 */ VALID
| PATHVALID
,
319 /* 'A' 65 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
320 /* 'B' 66 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
321 /* 'C' 67 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
322 /* 'D' 68 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
323 /* 'E' 69 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
324 /* 'F' 70 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
325 /* 'G' 71 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
326 /* 'H' 72 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
327 /* 'I' 73 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
328 /* 'J' 74 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
329 /* 'K' 75 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
330 /* 'L' 76 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
331 /* 'M' 77 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
332 /* 'N' 78 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
333 /* 'O' 79 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
334 /* 'P' 80 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
335 /* 'Q' 81 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
336 /* 'R' 82 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
337 /* 'S' 83 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
338 /* 'T' 84 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
339 /* 'U' 85 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
340 /* 'V' 86 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
341 /* 'W' 87 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
342 /* 'X' 88 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
343 /* 'Y' 89 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
344 /* 'Z' 90 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
349 /* '_' 95 */ VALID
| UNRESERVED
| PATHVALID
,
351 /* 'a' 97 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
352 /* 'b' 98 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
353 /* 'c' 99 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
354 /* 'd' 100 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
355 /* 'e' 101 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
356 /* 'f' 102 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
357 /* 'g' 103 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
358 /* 'h' 104 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
359 /* 'i' 105 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
360 /* 'j' 106 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
361 /* 'k' 107 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
362 /* 'l' 108 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
363 /* 'm' 109 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
364 /* 'n' 110 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
365 /* 'o' 111 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
366 /* 'p' 112 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
367 /* 'q' 113 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
368 /* 'r' 114 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
369 /* 's' 115 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
370 /* 't' 116 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
371 /* 'u' 117 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
372 /* 'v' 118 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
373 /* 'w' 119 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
374 /* 'x' 120 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
375 /* 'y' 121 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
376 /* 'z' 122 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
380 /* '~' 126 */ VALID
| UNRESERVED
| PATHVALID
,
384 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
385 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & VALID
) : false;
388 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
389 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & SCHEME
) : false;
392 // "Unreserved" as defined by RFC 2396
393 CF_INLINE Boolean
isUnreservedCharacter(UniChar ch
) {
394 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & UNRESERVED
) : false;
397 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
398 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & PATHVALID
) : false;
401 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
402 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & HEXDIGIT
) : false;
405 // Returns false if ch1 or ch2 isn't properly formatted
406 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
408 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
409 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
410 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
413 *result
= (*result
) << 4;
414 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
415 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
416 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
422 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
423 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
426 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
427 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
428 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
429 CFAllocatorRef alloc
= CFGetAllocator(array
);
430 CFMutableArrayRef mArray
= NULL
;
431 CFIndex i
, c
= CFArrayGetCount(array
);
432 for (i
= 0; i
< c
; i
++) {
433 CFStringRef origComp
= (CFStringRef
)CFArrayGetValueAtIndex(array
, i
);
434 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
435 if (!unescapedComp
) {
438 if (unescapedComp
!= origComp
) {
440 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
442 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
444 CFRelease(unescapedComp
);
447 if (mArray
) CFRelease(mArray
);
458 // 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.
459 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
460 if (preserveSlashes
) {
461 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?"), kCFStringEncodingUTF8
);
463 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
467 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
468 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
469 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
473 // 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
474 static Boolean
_hackToConvertSurrogates(UniChar highChar
, UniChar lowChar
, CFMutableStringRef str
) {
475 UniChar surrogate
[2];
476 uint8_t bytes
[6]; // Aki sez it should never take more than 6 bytes
479 surrogate
[0] = highChar
;
480 surrogate
[1] = lowChar
;
481 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &len
) != kCFStringEncodingConversionSuccess
) {
484 for (currByte
= bytes
; currByte
< bytes
+ len
; currByte
++) {
485 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
486 unsigned char high
, low
;
487 high
= ((*currByte
) & 0xf0) >> 4;
488 low
= (*currByte
) & 0x0f;
489 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
490 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
491 CFStringAppendCharacters(str
, escapeSequence
, 3);
496 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
497 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
498 uint8_t *bytePtr
= bytes
, *currByte
;
500 CFAllocatorRef alloc
= NULL
;
501 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
502 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
503 if (byteLength
<= 6) {
504 // The encoding cannot accomodate the character
507 alloc
= CFGetAllocator(str
);
508 bytePtr
= (uint8_t *)CFAllocatorAllocate(alloc
, byteLength
, 0);
509 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
510 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
514 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
515 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
516 unsigned char high
, low
;
517 high
= ((*currByte
) & 0xf0) >> 4;
518 low
= (*currByte
) & 0x0f;
519 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
520 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
521 CFStringAppendCharacters(str
, escapeSequence
, 3);
523 if (bytePtr
!= bytes
) {
524 CFAllocatorDeallocate(alloc
, bytePtr
);
529 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
530 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
531 CFMutableStringRef newStr
= NULL
;
534 CFRange percentRange
, searchRange
;
535 CFStringRef escapedStr
= NULL
;
536 CFMutableStringRef strForEscapedChar
= NULL
;
538 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
539 Boolean failed
= false;
541 if (!originalString
) return NULL
;
543 if (charactersToLeaveEscaped
== NULL
) {
544 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
547 length
= CFStringGetLength(originalString
);
548 searchRange
= CFRangeMake(0, length
);
550 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
551 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
552 uint8_t numBytesExpected
;
556 // Make sure we have at least 2 more characters
557 if (length
- percentRange
.location
< 3) { failed
= true; break; }
559 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
560 // so we assume the percent character is legit, and let it pass into the string
561 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
562 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
563 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
564 if (!(bytes
[0] & 0x80)) {
565 numBytesExpected
= 1;
566 } else if (!(bytes
[0] & 0x20)) {
567 numBytesExpected
= 2;
568 } else if (!(bytes
[0] & 0x10)) {
569 numBytesExpected
= 3;
571 numBytesExpected
= 4;
573 if (numBytesExpected
== 1) {
574 // one byte sequence (most common case); handle this specially
575 escapedChar
= bytes
[0];
576 if (!strForEscapedChar
) {
577 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
579 escapedStr
= strForEscapedChar
;
582 // Make sure up front that we have enough characters
583 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
584 for (j
= 1; j
< numBytesExpected
; j
++) {
585 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
586 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
587 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
588 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
591 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
592 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
595 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
596 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
597 escapedChar
= 0xfeff;
598 if (!strForEscapedChar
) {
599 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
601 CFRelease(escapedStr
);
602 escapedStr
= strForEscapedChar
;
607 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
608 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
609 searchRange
.length
= length
- searchRange
.location
;
612 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
613 if (escapedStr
!= strForEscapedChar
) {
614 CFRelease(escapedStr
);
622 newStr
= CFStringCreateMutable(alloc
, length
);
624 if (percentRange
.location
- mark
> 0) {
625 // The creation of this temporary string is unfortunate.
626 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
627 CFStringAppend(newStr
, substring
);
628 CFRelease(substring
);
630 CFStringAppend(newStr
, escapedStr
);
631 if (escapedStr
!= strForEscapedChar
) {
632 CFRelease(escapedStr
);
635 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
638 if (escapedStr
&& escapedStr
!= strForEscapedChar
) CFRelease(escapedStr
);
639 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
641 if (newStr
) CFRelease(newStr
);
645 // Need to cat on the remainder of the string
646 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
647 CFStringAppend(newStr
, substring
);
648 CFRelease(substring
);
652 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
657 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
658 if (enc
== kCFStringEncodingUTF8
) {
659 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
661 CFMutableStringRef newStr
= NULL
;
662 CFMutableStringRef escapedStr
= NULL
;
665 CFRange percentRange
, searchRange
;
666 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
667 Boolean failed
= false;
668 uint8_t byteBuffer
[8];
669 uint8_t *bytes
= byteBuffer
;
670 int capacityOfBytes
= 8;
672 if (!originalString
) return NULL
;
674 if (charactersToLeaveEscaped
== NULL
) {
675 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
678 length
= CFStringGetLength(originalString
);
679 searchRange
= CFRangeMake(0, length
);
681 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
683 CFIndex percentLoc
= percentRange
.location
;
684 CFStringRef convertedString
;
685 int numBytesUsed
= 0;
687 // Make sure we have at least 2 more characters
688 if (length
- percentLoc
< 3) { failed
= true; break; }
690 if (numBytesUsed
== capacityOfBytes
) {
691 if (bytes
== byteBuffer
) {
692 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
693 memmove(bytes
, byteBuffer
, capacityOfBytes
);
694 capacityOfBytes
= 16;
696 void *oldbytes
= bytes
;
697 int oldcap
= capacityOfBytes
;
698 capacityOfBytes
= 2*capacityOfBytes
;
699 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, capacityOfBytes
* sizeof(uint8_t), 0);
700 memmove(bytes
, oldbytes
, oldcap
);
701 CFAllocatorDeallocate(alloc
, oldbytes
);
705 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
707 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
709 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
711 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
712 searchRange
.location
= percentLoc
;
713 searchRange
.length
= length
- searchRange
.location
;
716 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
717 if (!convertedString
) {
723 newStr
= CFStringCreateMutable(alloc
, length
);
725 if (percentRange
.location
- mark
> 0) {
726 // The creation of this temporary string is unfortunate.
727 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
728 CFStringAppend(newStr
, substring
);
729 CFRelease(substring
);
733 CFStringAppend(newStr
, convertedString
);
734 CFRelease(convertedString
);
736 CFIndex i
, c
= CFStringGetLength(convertedString
);
738 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
740 for (i
= 0; i
< c
; i
++) {
741 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
742 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
743 CFStringAppendCharacters(newStr
, &ch1
, 1);
745 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
746 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
750 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
753 if (escapedStr
) CFRelease(escapedStr
);
754 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
756 if (newStr
) CFRelease(newStr
);
760 // Need to cat on the remainder of the string
761 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
762 CFStringAppend(newStr
, substring
);
763 CFRelease(substring
);
767 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
773 static CFStringRef
_addPercentEscapesToString(CFAllocatorRef allocator
, CFStringRef originalString
, Boolean (*shouldReplaceChar
)(UniChar
, void*), CFIndex (*handlePercentChar
)(CFIndex
, CFStringRef
, CFStringRef
*, void *), CFStringEncoding encoding
, void *context
) {
774 CFMutableStringRef newString
= NULL
;
776 CFStringInlineBuffer buf
;
778 if (!originalString
) return NULL
;
779 length
= CFStringGetLength(originalString
);
780 if (length
== 0) return (CFStringRef
)CFStringCreateCopy(allocator
, originalString
);
781 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
783 for (idx
= 0; idx
< length
; idx
++) {
784 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
785 Boolean shouldReplace
= shouldReplaceChar(ch
, context
);
787 // Perform the replacement
789 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
790 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
792 if (!_appendPercentEscapesForCharacter(ch
, encoding
, newString
)) {
793 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
794 if (encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1))) {
795 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
796 if (_hackToConvertSurrogates(ch
, CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1), newString
)) {
797 idx
++; // We consumed 2 characters, not 1
805 } else if (ch
== '%' && handlePercentChar
) {
806 CFStringRef replacementString
= NULL
;
807 CFIndex newIndex
= handlePercentChar(idx
, originalString
, &replacementString
, context
);
810 } else if (replacementString
) {
812 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
813 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
815 CFStringAppend(newString
, replacementString
);
816 CFRelease(replacementString
);
818 if (newIndex
== idx
) {
820 CFStringAppendCharacters(newString
, &ch
, 1);
823 if (!replacementString
&& newString
) {
825 for (tmpIndex
= idx
; tmpIndex
< newIndex
; tmpIndex
++) {
826 ch
= CFStringGetCharacterAtIndex(originalString
, idx
);
827 CFStringAppendCharacters(newString
, &ch
, 1);
832 } else if (newString
) {
833 CFStringAppendCharacters(newString
, &ch
, 1);
837 // Ran in to an encoding failure
838 if (newString
) CFRelease(newString
);
840 } else if (newString
) {
843 return (CFStringRef
)CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
848 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
849 CFIndex i
, c
= CFStringGetLength(string
);
850 CFStringInlineBuffer buf
;
851 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
852 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
856 static Boolean
_shouldPercentReplaceChar(UniChar ch
, void *context
) {
857 CFStringRef unescape
= ((CFStringRef
*)context
)[0];
858 CFStringRef escape
= ((CFStringRef
*)context
)[1];
859 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
861 if (unescape
&& _stringContainsCharacter(unescape
, ch
)) {
862 shouldReplace
= false;
864 } else if (escape
&& _stringContainsCharacter(escape
, ch
)) {
865 shouldReplace
= true;
867 return shouldReplace
;
870 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
871 CFStringRef strings
[2];
872 strings
[0] = charactersToLeaveUnescaped
;
873 strings
[1] = legalURLCharactersToBeEscaped
;
874 return _addPercentEscapesToString(allocator
, originalString
, _shouldPercentReplaceChar
, NULL
, encoding
, strings
);
879 static Boolean
__CFURLCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
880 CFURLRef url1
= (CFURLRef
)cf1
;
881 CFURLRef url2
= (CFURLRef
)cf2
;
882 UInt32 pathType1
, pathType2
;
884 __CFGenericValidateType(cf1
, CFURLGetTypeID());
885 __CFGenericValidateType(cf2
, CFURLGetTypeID());
887 if (url1
== url2
) return kCFCompareEqualTo
;
890 if (! url2
->_base
) return kCFCompareEqualTo
;
891 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
892 } else if ( url2
->_base
) {
896 pathType1
= URL_PATH_TYPE(url1
);
897 pathType2
= URL_PATH_TYPE(url2
);
898 if (pathType1
== pathType2
) {
899 if (pathType1
!= FULL_URL_REPRESENTATION
) {
900 return CFEqual(url1
->_string
, url2
->_string
);
902 // Do not compare the original strings; compare the sanatized strings.
903 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
906 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
907 CFStringRef scheme1
= CFURLCopyScheme(url1
);
908 CFStringRef scheme2
= CFURLCopyScheme(url2
);
910 if (scheme1
&& scheme2
) {
911 eq
= CFEqual(scheme1
, scheme2
);
914 } else if (!scheme1
&& !scheme2
) {
918 if (scheme1
) CFRelease(scheme1
);
919 else CFRelease(scheme2
);
921 if (!eq
) return false;
923 if (pathType1
== FULL_URL_REPRESENTATION
) {
924 if (!(url1
->_flags
& IS_PARSED
)) {
925 _parseComponentsOfURL(url1
);
927 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
932 if (pathType2
== FULL_URL_REPRESENTATION
) {
933 if (!(url2
->_flags
& IS_PARSED
)) {
934 _parseComponentsOfURL(url2
);
936 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
941 // No help for it; we now must convert to the canonical representation and compare.
942 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
947 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
948 CFURLRef url1
= (CFURLRef
)cf1
;
949 CFURLRef url2
= (CFURLRef
)cf2
;
950 UInt32 pathType1
, pathType2
;
952 __CFGenericValidateType(cf1
, CFURLGetTypeID());
953 __CFGenericValidateType(cf2
, CFURLGetTypeID());
955 if (url1
== url2
) return true;
956 if ((url1
->_flags
& IS_PARSED
) && (url2
->_flags
& IS_PARSED
) && (url1
->_flags
& IS_DIRECTORY
) != (url2
->_flags
& IS_DIRECTORY
)) return false;
958 if (! url2
->_base
) return false;
959 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
960 } else if ( url2
->_base
) {
964 pathType1
= URL_PATH_TYPE(url1
);
965 pathType2
= URL_PATH_TYPE(url2
);
966 if (pathType1
== pathType2
) {
967 if (pathType1
!= FULL_URL_REPRESENTATION
) {
968 return CFEqual(url1
->_string
, url2
->_string
);
970 // Do not compare the original strings; compare the sanatized strings.
971 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
974 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
975 CFStringRef scheme1
= CFURLCopyScheme(url1
);
976 CFStringRef scheme2
= CFURLCopyScheme(url2
);
978 if (scheme1
&& scheme2
) {
979 eq
= CFEqual(scheme1
, scheme2
);
982 } else if (!scheme1
&& !scheme2
) {
986 if (scheme1
) CFRelease(scheme1
);
987 else CFRelease(scheme2
);
989 if (!eq
) return false;
991 if (pathType1
== FULL_URL_REPRESENTATION
) {
992 if (!(url1
->_flags
& IS_PARSED
)) {
993 _parseComponentsOfURL(url1
);
995 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
1000 if (pathType2
== FULL_URL_REPRESENTATION
) {
1001 if (!(url2
->_flags
& IS_PARSED
)) {
1002 _parseComponentsOfURL(url2
);
1004 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
1009 // No help for it; we now must convert to the canonical representation and compare.
1010 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
1014 static CFHashCode
__CFURLHash(CFTypeRef cf
) {
1015 /* 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. */
1016 struct __CFURL
* url
= (struct __CFURL
*)cf
;
1017 CFHashCode result
= 0;
1021 // Allocate our extra space if it isn't already allocated
1022 if ( url
&& ! url
->extra
)
1023 _CFURLAllocateExtraDataspace( url
);
1026 result
= url
->extra
->hashValue
;
1029 if (CFURLCanBeDecomposed(url
)) {
1030 CFStringRef lastComp
= CFURLCopyLastPathComponent(url
);
1031 CFStringRef hostNameRef
= CFURLCopyHostName(url
);
1036 result
= CFHash(lastComp
);
1037 CFRelease(lastComp
);
1040 if ( hostNameRef
) {
1041 result
^= CFHash( hostNameRef
);
1042 CFRelease( hostNameRef
);
1045 result
= CFHash(CFURLGetString(url
));
1048 if ( ! result
) // never store a 0 value for the hashed value
1051 url
->extra
->hashValue
= result
;
1059 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
1060 CFURLRef url
= (CFURLRef
)cf
;
1061 __CFGenericValidateType(cf
, CFURLGetTypeID());
1063 CFRetain(url
->_string
);
1064 return url
->_string
;
1066 // Do not dereference url->_base; it may be an ObjC object
1067 return CFStringCreateWithFormat(CFGetAllocator(url
), NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
1072 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
1073 CFURLRef url
= (CFURLRef
)cf
;
1075 CFAllocatorRef alloc
= CFGetAllocator(url
);
1077 CFStringRef baseString
= CFCopyDescription(url
->_base
);
1078 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
);
1079 CFRelease(baseString
);
1081 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
);
1086 #if DEBUG_URL_MEMORY_USAGE
1088 extern __attribute((used
)) void __CFURLDumpMemRecord(void) {
1089 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
);
1092 // if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator);
1096 static void __CFURLDeallocate(CFTypeRef cf
) {
1097 CFURLRef url
= (CFURLRef
)cf
;
1098 CFAllocatorRef alloc
;
1099 __CFGenericValidateType(cf
, CFURLGetTypeID());
1100 alloc
= CFGetAllocator(url
);
1101 #if DEBUG_URL_MEMORY_USAGE
1103 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
1104 numFileURLsDealloced
++;
1107 if (url
->_string
) CFRelease(url
->_string
); // GC: 3879914
1108 if (url
->_base
) CFRelease(url
->_base
);
1109 if (url
->ranges
) CFAllocatorDeallocate(alloc
, url
->ranges
);
1110 if (_getSanitizedString(url
)) CFRelease(_getSanitizedString(url
));
1111 if ( url
->extra
!= NULL
) CFAllocatorDeallocate( alloc
, url
->extra
);
1112 if (_getResourceInfo(url
)) CFRelease(_getResourceInfo(url
));
1115 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
1117 static const CFRuntimeClass __CFURLClass
= {
1125 __CFURLCopyFormattingDescription
,
1126 __CFURLCopyDescription
1129 // When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but
1130 // when it is defined, we must prefix with static to prevent the string from being exported
1131 #ifdef __CONSTANT_CFSTRINGS__
1132 static CONST_STRING_DECL(kCFURLFileScheme
, "file")
1133 static CONST_STRING_DECL(kCFURLDataScheme
, "data")
1134 static CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1135 static CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1137 CONST_STRING_DECL(kCFURLFileScheme
, "file")
1138 CONST_STRING_DECL(kCFURLDataScheme
, "data")
1139 CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1140 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1142 __private_extern__
void __CFURLInitialize(void) {
1143 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1146 /* Toll-free bridging support; get the true CFURL from an NSURL */
1147 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1148 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, url
, "_cfurl");
1152 CFTypeID
CFURLGetTypeID(void) {
1153 return __kCFURLTypeID
;
1156 __private_extern__
void CFShowURL(CFURLRef url
) {
1158 fprintf(stdout
, "(null)\n");
1161 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1162 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1163 fprintf(stdout
, "ObjC bridged object}\n");
1166 fprintf(stdout
, "\n\tPath type: ");
1167 switch (URL_PATH_TYPE(url
)) {
1168 case kCFURLPOSIXPathStyle
:
1169 fprintf(stdout
, "POSIX");
1171 case kCFURLHFSPathStyle
:
1172 fprintf(stdout
, "HFS");
1174 case kCFURLWindowsPathStyle
:
1175 fprintf(stdout
, "NTFS");
1177 case FULL_URL_REPRESENTATION
:
1178 fprintf(stdout
, "Native URL");
1181 fprintf(stdout
, "UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url
));
1183 fprintf(stdout
, "\n\tRelative string: ");
1184 CFShow(url
->_string
);
1185 fprintf(stdout
, "\tBase URL: ");
1187 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1190 fprintf(stdout
, "(null)\n");
1192 fprintf(stdout
, "\tFlags: 0x%x\n}\n", (unsigned int)url
->_flags
);
1196 /***************************************************/
1197 /* URL creation and String/Data creation from URLS */
1198 /***************************************************/
1199 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, const char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1200 CFIndex neededLength
;
1204 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1208 *freeCharacters
= false;
1212 *ustring
= CFStringGetCharactersPtr(string
);
1214 *useCString
= false;
1215 *freeCharacters
= false;
1219 *freeCharacters
= true;
1220 length
= CFStringGetLength(string
);
1221 rg
= CFRangeMake(0, length
);
1222 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1223 if (neededLength
== length
) {
1224 char *buf
= (char *)CFAllocatorAllocate(alloc
, length
, 0);
1225 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, (uint8_t *)buf
, length
, NULL
);
1229 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1230 CFStringGetCharacters(string
, rg
, buf
);
1231 *useCString
= false;
1236 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1237 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1239 /* 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. */
1241 CFIndex idx
, base_idx
= 0;
1242 CFIndex string_length
;
1243 UInt32 flags
= (IS_PARSED
| *theFlags
);
1244 Boolean useCString
, freeCharacters
, isCompliant
;
1245 uint8_t numRanges
= 0;
1246 const char *cstring
= NULL
;
1247 const UniChar
*ustring
= NULL
;
1249 string_length
= CFStringGetLength(string
);
1250 constructBuffers(alloc
, string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1252 // Algorithm is as described in RFC 1808
1253 // 1: parse the fragment; remainder after left-most "#" is fragment
1254 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1255 if ('#' == STRING_CHAR(idx
)) {
1256 flags
|= HAS_FRAGMENT
;
1257 ranges
[8].location
= idx
+ 1;
1258 ranges
[8].length
= string_length
- (idx
+ 1);
1260 string_length
= idx
; // remove fragment from parse string
1264 // 2: parse the scheme
1265 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1266 UniChar ch
= STRING_CHAR(idx
);
1268 flags
|= HAS_SCHEME
;
1269 flags
|= IS_ABSOLUTE
;
1270 ranges
[0].location
= base_idx
;
1271 ranges
[0].length
= idx
;
1274 // optimization for http urls
1275 if (idx
== 4 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' && STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p') {
1276 flags
|= HAS_HTTP_SCHEME
;
1278 // optimization for file urls
1279 if (idx
== 4 && STRING_CHAR(0) == 'f' && STRING_CHAR(1) == 'i' && STRING_CHAR(2) == 'l' && STRING_CHAR(3) == 'e') {
1280 flags
|= HAS_FILE_SCHEME
;
1283 } else if (!scheme_valid(ch
)) {
1284 break; // invalid scheme character -- no scheme
1288 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1289 // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage
1290 // expects this to be treated identically to "scheme://" - REW, 12/08/03
1291 if (!(flags
& HAS_SCHEME
)) {
1293 } else if (base_idx
== string_length
) {
1294 isCompliant
= false;
1295 } else if (STRING_CHAR(base_idx
) != '/') {
1296 isCompliant
= false;
1302 // Clear the fragment flag if it's been set
1303 if (flags
& HAS_FRAGMENT
) {
1304 flags
&= (~HAS_FRAGMENT
);
1305 string_length
= CFStringGetLength(string
);
1307 (*theFlags
) = flags
;
1308 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
1309 (*range
)->location
= ranges
[0].location
;
1310 (*range
)->length
= ranges
[0].length
;
1312 if (freeCharacters
) {
1313 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1317 // URL is 1808-compliant
1318 flags
|= IS_DECOMPOSABLE
;
1320 // 3: parse the network location and login
1321 if (2 <= (string_length
- base_idx
) && '/' == STRING_CHAR(base_idx
) && '/' == STRING_CHAR(base_idx
+1)) {
1322 CFIndex base
= 2 + base_idx
, extent
;
1323 for (idx
= base
; idx
< string_length
; idx
++) {
1324 if ('/' == STRING_CHAR(idx
) || '?' == STRING_CHAR(idx
)) break;
1328 // net_loc parts extend from base to extent (but not including), which might be to end of string
1329 // net location is "<user>:<password>@<host>:<port>"
1330 if (extent
!= base
) {
1331 for (idx
= base
; idx
< extent
; idx
++) {
1332 if ('@' == STRING_CHAR(idx
)) { // there is a user
1336 ranges
[1].location
= base
; // base of the user
1337 for (idx2
= base
; idx2
< idx
; idx2
++) {
1338 if (':' == STRING_CHAR(idx2
)) { // found a password separator
1339 flags
|= HAS_PASSWORD
;
1341 ranges
[2].location
= idx2
+1; // base of the password
1342 ranges
[2].length
= idx
-(idx2
+1); // password extent
1343 ranges
[1].length
= idx2
- base
; // user extent
1347 if (!(flags
& HAS_PASSWORD
)) {
1348 // user extends to the '@'
1349 ranges
[1].length
= idx
- base
; // user extent
1357 ranges
[3].location
= base
; // base of host
1359 // base has been advanced past the user and password if they existed
1360 for (idx
= base
; idx
< extent
; idx
++) {
1361 // IPV6 support (RFC 2732) DCJ June/10/2002
1362 if ('[' == STRING_CHAR(idx
)) { // starting IPV6 explicit address
1363 // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end
1364 for ( ; idx
< extent
; ++ idx
) {
1365 if ( ']' == STRING_CHAR(idx
)) {
1366 flags
|= IS_IPV6_ENCODED
;
1371 // there is a port if we see a colon. Only the last one is the port, though.
1372 else if ( ':' == STRING_CHAR(idx
)) {
1375 ranges
[4].location
= idx
+1; // base of port
1376 ranges
[4].length
= extent
- (idx
+1); // port extent
1377 ranges
[3].length
= idx
- base
; // host extent
1381 if (!(flags
& HAS_PORT
)) {
1382 ranges
[3].length
= extent
- base
; // host extent
1388 // 4: parse the query; remainder after left-most "?" is query
1389 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1390 if ('?' == STRING_CHAR(idx
)) {
1393 ranges
[7].location
= idx
+ 1;
1394 ranges
[7].length
= string_length
- (idx
+1);
1395 string_length
= idx
; // remove query from parse string
1400 // 5: parse the parameters; remainder after left-most ";" is parameters
1401 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1402 if (';' == STRING_CHAR(idx
)) {
1403 flags
|= HAS_PARAMETERS
;
1405 ranges
[6].location
= idx
+ 1;
1406 ranges
[6].length
= string_length
- (idx
+1);
1407 string_length
= idx
; // remove parameters from parse string
1412 // 6: parse the path; it's whatever's left between string_length & base_idx
1413 if (string_length
- base_idx
!= 0 || (flags
& NET_LOCATION_MASK
))
1415 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1421 pathRg
.location
= base_idx
;
1422 pathRg
.length
= string_length
- base_idx
;
1425 if (pathRg
.length
> 0) {
1426 Boolean sawPercent
= FALSE
;
1427 for (idx
= pathRg
.location
; idx
< string_length
; idx
++) {
1428 if ('%' == STRING_CHAR(idx
)) {
1433 #if DEPLOYMENT_TARGET_MACOSX
1434 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) == '/') {
1435 flags
|= PATH_HAS_FILE_ID
;
1436 } else if (!sawPercent
) {
1437 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1439 #elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
1441 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1444 #error Unknown or unspecified DEPLOYMENT_TARGET
1447 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 1);
1450 } else if (ch
== '.') {
1451 if (pathRg
.length
== 1) {
1454 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 2);
1457 } else if (ch
!= '.') {
1459 } else if (pathRg
.length
== 2) {
1462 isDir
= (STRING_CHAR(pathRg
.location
+ pathRg
.length
- 3) == '/');
1469 isDir
= (baseURL
!= NULL
) ? CFURLHasDirectoryPath(baseURL
) : false;
1472 flags
|= IS_DIRECTORY
;
1476 if (freeCharacters
) {
1477 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1479 (*theFlags
) = flags
;
1480 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
)*numRanges
, 0);
1482 for (idx
= 0, flags
= 1; flags
!= (1<<9); flags
= (flags
<<1), idx
++) {
1483 if ((*theFlags
) & flags
) {
1484 (*range
)[numRanges
] = ranges
[idx
];
1490 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
) {
1492 Boolean sawIllegalChar
= false;
1493 for (idx
= base
; idx
< end
; idx
++) {
1494 Boolean shouldEscape
;
1495 UniChar ch
= STRING_CHAR(idx
);
1496 if (isURLLegalCharacter(ch
)) {
1497 if ((componentFlag
== HAS_USER
|| componentFlag
== HAS_PASSWORD
) && (ch
== '/' || ch
== '?' || ch
== '@')) {
1498 shouldEscape
= true;
1500 shouldEscape
= false;
1502 } else if (ch
== '%' && idx
+ 2 < end
&& isHexDigit(STRING_CHAR(idx
+ 1)) && isHexDigit(STRING_CHAR(idx
+2))) {
1503 shouldEscape
= false;
1504 } else if (componentFlag
== HAS_HOST
&& ((idx
== base
&& ch
== '[') || (idx
== end
-1 && ch
== ']'))) {
1505 shouldEscape
= false;
1507 shouldEscape
= true;
1509 if (!shouldEscape
) continue;
1511 sawIllegalChar
= true;
1512 if (componentFlag
&& flags
) {
1513 *flags
|= (componentFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
);
1515 if (!*escapedString
) {
1516 *escapedString
= CFStringCreateMutable(alloc
, 0);
1519 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[*mark
]), idx
- *mark
, kCFStringEncodingISOLatin1
, false);
1520 CFStringAppend(*escapedString
, tempString
);
1521 CFRelease(tempString
);
1523 CFStringAppendCharacters(*escapedString
, &(ustring
[*mark
]), idx
- *mark
);
1526 _appendPercentEscapesForCharacter(ch
, encoding
, *escapedString
); // This can never fail because anURL->_string was constructed from the encoding passed in
1528 return sawIllegalChar
;
1531 static void computeSanitizedString(CFURLRef url
) {
1532 CFAllocatorRef alloc
= CFGetAllocator(url
);
1533 CFIndex string_length
= CFStringGetLength(url
->_string
);
1534 Boolean useCString
, freeCharacters
;
1535 const char *cstring
= NULL
;
1536 const UniChar
*ustring
= NULL
;
1537 CFIndex base
; // where to scan from
1538 CFIndex mark
; // first character not-yet copied to sanitized string
1539 if (!(url
->_flags
& IS_PARSED
)) {
1540 _parseComponentsOfURL(url
);
1542 constructBuffers(alloc
, url
->_string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1543 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1544 // Impossible to have a problem character in the scheme
1545 CFMutableStringRef sanitizedString
= NULL
;
1546 base
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
).length
+ 1;
1548 if (!scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1549 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1551 if ( sanitizedString
) {
1552 _setSanitizedString( (struct __CFURL
*) url
, sanitizedString
);
1555 // Go component by component
1556 CFIndex currentComponent
= HAS_USER
;
1557 CFMutableStringRef sanitizedString
= NULL
;
1559 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1560 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->ranges
, currentComponent
);
1561 if (componentRange
.location
!= kCFNotFound
) {
1562 scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1564 currentComponent
= currentComponent
<< 1;
1566 if (sanitizedString
) {
1567 _setSanitizedString((struct __CFURL
*)url
, sanitizedString
);
1569 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1572 if (_getSanitizedString(url
) && mark
!= string_length
) {
1574 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1575 CFStringAppend(_getSanitizedString(url
), tempString
);
1576 CFRelease(tempString
);
1578 CFStringAppendCharacters(_getSanitizedString(url
), &(ustring
[mark
]), string_length
- mark
);
1581 if (freeCharacters
) {
1582 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1587 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1588 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1589 CFIndex string_length
= CFStringGetLength(comp
);
1590 Boolean useCString
, freeCharacters
;
1591 const char *cstring
= NULL
;
1592 const UniChar
*ustring
= NULL
;
1593 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1594 CFMutableStringRef result
= NULL
;
1596 constructBuffers(alloc
, comp
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1597 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1599 if (mark
< string_length
) {
1601 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1602 CFStringAppend(result
, tempString
);
1603 CFRelease(tempString
);
1605 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1609 // This should nevr happen
1611 result
= (CFMutableStringRef
)comp
;
1613 if (freeCharacters
) {
1614 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1620 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1621 struct __CFURL
*url
;
1622 #if DEBUG_URL_MEMORY_USAGE
1624 // if (!URLAllocator) {
1625 // URLAllocator = CFCountingAllocatorCreate(NULL);
1627 allocator
= URLAllocator
;
1629 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1632 if (createOldUTF8StyleURLs()) {
1633 url
->_flags
|= IS_OLD_UTF8_STYLE
;
1635 url
->_string
= NULL
;
1638 // url->_reserved = NULL;
1639 url
->_encoding
= kCFStringEncodingUTF8
;
1640 // url->_sanatizedString = NULL;
1646 // 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.
1647 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, UInt32 fsType
, CFURLRef base
) {
1648 CFAssert2((fsType
== FULL_URL_REPRESENTATION
) || (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) || (fsType
== kCFURLHFSPathStyle
) || ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__
, fsType
);
1650 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1651 url
->_string
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1652 url
->_flags
|= (fsType
<< 16);
1654 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1656 #if DEBUG_URL_MEMORY_USAGE
1657 if (fsType
!= FULL_URL_REPRESENTATION
) {
1658 numFileURLsCreated
++;
1661 numURLsWithBaseURL
++;
1665 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1666 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1667 CFIndex len
= CFStringGetLength(path
);
1668 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1669 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1670 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1672 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1673 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1677 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1678 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1680 #elif DEPLOYMENT_TARGET_WINDOWS
1681 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1682 CFIndex len
= CFStringGetLength(path
);
1683 UniChar firstChar
= 0 < len
? CFStringGetCharacterAtIndex(path
, 0) : 0;
1684 UniChar secondChar
= 1 < len
? CFStringGetCharacterAtIndex(path
, 1) : 0;
1685 Boolean isDrive
= ('A' <= firstChar
&& firstChar
<= 'Z') || ('a' <= firstChar
&& firstChar
<= 'z');
1686 isDrive
= isDrive
&& (secondChar
== ':' || secondChar
== '|');
1687 if (isDrive
|| (firstChar
== '\\' && secondChar
== '\\')) {
1688 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLWindowsPathStyle
, NULL
);
1689 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1690 } else if (firstChar
== '/') {
1691 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1692 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1694 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1695 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1699 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1700 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1703 #error Unknown or unspecified DEPLOYMENT_TARGET
1706 // Exported for Foundation's use
1707 CF_EXPORT Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1708 // 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.
1709 // 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
1710 CFStringInlineBuffer stringBuffer
;
1711 CFIndex idx
= 0, length
;
1712 Boolean sawHash
= false;
1714 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1717 length
= CFStringGetLength(string
);
1718 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1719 while (idx
< length
) {
1720 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1723 // Make sure that two valid hex digits follow a '%' character
1725 if ( idx
+ 2 > length
)
1727 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1728 idx
= -1; // To guarantee index < length, and our failure case is triggered
1732 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1734 if (! isHexDigit(ch
) ) {
1735 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1739 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1741 if (! isHexDigit(ch
) ) {
1742 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1749 if (ch
== '[' || ch
== ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1755 if ( isURLLegalCharacter( ch
) )
1765 CF_EXPORT
void _CFURLInitWithString(CFURLRef myURL
, CFStringRef string
, CFURLRef baseURL
) {
1766 struct __CFURL
*url
= (struct __CFURL
*)myURL
; // Supress annoying compile warnings
1767 Boolean isAbsolute
= false;
1768 CFRange colon
= CFStringFind(string
, CFSTR(":"), 0);
1769 if (colon
.location
!= kCFNotFound
) {
1772 for (i
= 0; i
< colon
.location
; i
++) {
1773 char ch
= (char)CFStringGetCharacterAtIndex(string
, i
);
1774 if (!scheme_valid(ch
)) {
1780 _CFURLInit(url
, string
, FULL_URL_REPRESENTATION
, isAbsolute
? NULL
: baseURL
);
1782 url
->_flags
|= IS_ABSOLUTE
;
1786 struct __CFURLEncodingTranslationParameters
{
1787 CFStringEncoding fromEnc
;
1788 CFStringEncoding toEnc
;
1789 const UniChar
*addlChars
;
1791 Boolean escapeHighBit
;
1792 Boolean escapePercents
;
1793 Boolean agreesOverASCII
;
1794 Boolean encodingsMatch
;
1797 static Boolean
_shouldEscapeForEncodingConversion(UniChar ch
, void *context
) {
1798 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1799 if (info
->escapeHighBit
&& ch
> 0x7F) {
1801 } else if (ch
== '%' && info
->escapePercents
) {
1803 } else if (info
->addlChars
) {
1804 const UniChar
*escChar
= info
->addlChars
;
1806 for (i
= 0; i
< info
->count
; escChar
++, i
++) {
1807 if (*escChar
== ch
) {
1815 static CFIndex
_convertEscapeSequence(CFIndex percentIndex
, CFStringRef urlString
, CFStringRef
*newString
, void *context
) {
1816 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1817 CFMutableDataRef newData
;
1818 Boolean sawNonASCIICharacter
= false;
1819 CFIndex i
= percentIndex
;
1822 if (info
->encodingsMatch
) return percentIndex
+ 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well
1823 newData
= CFDataCreateMutable(CFGetAllocator(urlString
), 0);
1824 length
= CFStringGetLength(urlString
);
1826 while (i
< length
&& CFStringGetCharacterAtIndex(urlString
, i
) == '%') {
1828 if (i
+2 >= length
|| !_translateBytes(CFStringGetCharacterAtIndex(urlString
, i
+1), CFStringGetCharacterAtIndex(urlString
, i
+2), &byte
)) {
1832 if (byte
> 0x7f) sawNonASCIICharacter
= true;
1833 CFDataAppendBytes(newData
, &byte
, 1);
1836 if (!sawNonASCIICharacter
&& info
->agreesOverASCII
) {
1839 CFStringRef tmp
= CFStringCreateWithBytes(CFGetAllocator(urlString
), CFDataGetBytePtr(newData
), CFDataGetLength(newData
), info
->fromEnc
, false);
1840 CFIndex tmpIndex
, tmpLen
;
1845 tmpLen
= CFStringGetLength(tmp
);
1846 *newString
= CFStringCreateMutable(CFGetAllocator(urlString
), 0);
1847 for (tmpIndex
= 0; tmpIndex
< tmpLen
; tmpIndex
++) {
1848 if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp
, tmpIndex
), info
->toEnc
, (CFMutableStringRef
)(*newString
))) {
1854 if (tmpIndex
< tmpLen
) {
1855 CFRelease(*newString
);
1864 /* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */
1865 static CFStringRef
_convertPercentEscapes(CFStringRef urlString
, CFStringEncoding fromEncoding
, CFStringEncoding toEncoding
, Boolean escapeAllHighBitCharacters
, Boolean escapePercents
, const UniChar
*addlCharsToEscape
, int numAddlChars
) {
1866 struct __CFURLEncodingTranslationParameters context
;
1867 context
.fromEnc
= fromEncoding
;
1868 context
.toEnc
= toEncoding
;
1869 context
.addlChars
= addlCharsToEscape
;
1870 context
.count
= numAddlChars
;
1871 context
.escapeHighBit
= escapeAllHighBitCharacters
;
1872 context
.escapePercents
= escapePercents
;
1873 context
.agreesOverASCII
= (__CFStringEncodingIsSupersetOfASCII(toEncoding
) && __CFStringEncodingIsSupersetOfASCII(fromEncoding
)) ? true : false;
1874 context
.encodingsMatch
= (fromEncoding
== toEncoding
) ? true : false;
1875 return _addPercentEscapesToString(CFGetAllocator(urlString
), urlString
, _shouldEscapeForEncodingConversion
, _convertEscapeSequence
, toEncoding
, &context
);
1878 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1879 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
1880 CFStringRef urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, encoding
, false);
1882 if (!urlString
|| CFStringGetLength(urlString
) == 0) {
1883 if (urlString
) CFRelease(urlString
);
1886 if (createOldUTF8StyleURLs()) {
1887 if (encoding
!= kCFStringEncodingUTF8
) {
1888 CFStringRef tmp
= _convertPercentEscapes(urlString
, encoding
, kCFStringEncodingUTF8
, false, false, NULL
, 0);
1889 CFRelease(urlString
);
1891 if (!urlString
) return NULL
;
1895 result
= _CFURLAlloc(allocator
);
1897 _CFURLInitWithString(result
, urlString
, baseURL
);
1898 if (encoding
!= kCFStringEncodingUTF8
&& !createOldUTF8StyleURLs()) {
1899 ((struct __CFURL
*)result
)->_encoding
= encoding
;
1900 #if DEBUG_URL_MEMORY_USAGE
1901 if ( encoding
!= kCFStringEncodingUTF8
) {
1902 numNonUTF8EncodedURLs
++;
1907 CFRelease(urlString
); // it's retained by result, now.
1911 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
1912 static const UniChar whitespaceChars
[4] = {' ', '\n', '\r', '\t'};
1913 CFStringRef myStr
= CFURLGetString(url
);
1916 if (url
->_flags
& IS_OLD_UTF8_STYLE
) {
1917 newStr
= (encoding
== kCFStringEncodingUTF8
) ? (CFStringRef
)CFRetain(myStr
) : _convertPercentEscapes(myStr
, kCFStringEncodingUTF8
, encoding
, true, false, escapeWhitespace
? whitespaceChars
: NULL
, escapeWhitespace
? 4 : 0);
1922 result
= CFStringCreateExternalRepresentation(allocator
, newStr
, encoding
, 0);
1927 // Any escape sequences in URLString will be interpreted via UTF-8.
1928 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1930 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1931 if (!_CFStringIsLegalURLString(URLString
)) return NULL
;
1932 url
= _CFURLAlloc(allocator
);
1934 _CFURLInitWithString(url
, URLString
, baseURL
);
1939 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1941 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1942 url
= _CFURLAlloc(allocator
);
1944 _CFURLInitWithString(url
, URLString
, baseURL
);
1949 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
1950 CFStringRef relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
1951 if (!relativeString
) {
1954 if (!useCompatibilityMode
) {
1955 CFURLRef url
= _CFURLCreateWithArbitraryString(alloc
, relativeString
, baseURL
);
1956 CFRelease(relativeString
);
1958 ((struct __CFURL
*)url
)->_encoding
= encoding
;
1959 CFURLRef absURL
= CFURLCopyAbsoluteURL(url
);
1960 #if DEBUG_URL_MEMORY_USAGE
1961 if ( encoding
!= kCFStringEncodingUTF8
) {
1962 numNonUTF8EncodedURLs
++;
1971 UInt32 absFlags
= 0;
1973 CFStringRef absString
= NULL
;
1974 Boolean absStringIsMutable
= false;
1977 absString
= relativeString
;
1979 UniChar ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
1980 if (ch
== '?' || ch
== ';' || ch
== '#') {
1981 // Nothing but parameter + query + fragment; append to the baseURL string
1982 CFStringRef baseString
;
1983 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1984 baseString
= CFURLGetString(baseURL
);
1986 baseString
= baseURL
->_string
;
1988 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
1989 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
1990 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
1991 absStringIsMutable
= true;
1993 UInt32 relFlags
= 0;
1995 CFStringRef relString
= NULL
;
1996 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
1997 if (relFlags
& HAS_SCHEME
) {
1998 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
1999 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
2000 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
2001 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
2002 CFAllocatorDeallocate(alloc
, relRanges
);
2004 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
2006 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
2007 CFRetain(relativeString
);
2008 absString
= relativeString
;
2010 if (baseScheme
) CFRelease(baseScheme
);
2012 CFRetain(relativeString
);
2013 relString
= relativeString
;
2016 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2017 if (!(baseURL
->_flags
& IS_PARSED
)) {
2018 _parseComponentsOfURL(baseURL
);
2020 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->ranges
);
2022 CFStringRef baseString
;
2023 UInt32 baseFlags
= 0;
2024 CFRange
*baseRanges
;
2025 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2026 baseString
= CFURLGetString(baseURL
);
2028 baseString
= baseURL
->_string
;
2030 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2031 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
2032 CFAllocatorDeallocate(alloc
, baseRanges
);
2034 absStringIsMutable
= true;
2036 if (relString
) CFRelease(relString
);
2037 CFAllocatorDeallocate(alloc
, relRanges
);
2039 CFRelease(relativeString
);
2041 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
2042 if (absFlags
& HAS_PATH
) {
2043 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
2044 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
2045 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), 0);
2046 CFStringRef newPath
;
2047 CFStringGetCharacters(absString
, pathRg
, buf
);
2048 buf
[pathRg
.length
] = '\0';
2049 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
2050 if (CFStringGetLength(newPath
) != pathRg
.length
) {
2051 if (!absStringIsMutable
) {
2052 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
2053 CFRelease(absString
);
2056 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
2059 // Do not deallocate buf; newPath took ownership of it.
2061 CFAllocatorDeallocate(alloc
, absRanges
);
2062 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
2063 CFRelease(absString
);
2065 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
2066 #if DEBUG_URL_MEMORY_USAGE
2067 if ( encoding
!= kCFStringEncodingUTF8
) {
2068 numNonUTF8EncodedURLs
++;
2076 /* 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 */
2077 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
2078 UniChar
*idx
= pathStr
;
2082 if (idx
!= pathStr
) {
2087 } else if (*(idx
+1) == pathDelimiter
) {
2088 if (idx
+ 2 != end
|| idx
!= pathStr
) {
2089 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
2093 // Do not delete the sole path component
2096 } else if (( end
-idx
>= 2 ) && *(idx
+1) == '.' && (idx
+2 == end
|| (( end
-idx
> 2 ) && *(idx
+2) == pathDelimiter
))) {
2097 if (idx
- pathStr
>= 2) {
2098 // 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.
2099 UniChar
*lastDelim
= idx
-2;
2100 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
2102 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
2103 // We have a genuine component to compact out
2105 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
2106 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2107 end
-= (idx
+ 3 - lastDelim
);
2110 } else if (lastDelim
!= pathStr
) {
2115 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
2123 } else if (stripLeadingDotDots
) {
2124 if (idx
+ 3 != end
) {
2125 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
2126 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2130 // Do not devolve the last path component
2136 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
2139 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
2142 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
- pathStr
, alloc
);
2145 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
2146 CFMutableStringRef newString
= CFStringCreateMutable(alloc
, 0);
2147 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
2148 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
2151 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
2152 if (rg
.location
!= kCFNotFound
) {
2153 CFStringGetCharacters(baseString
, rg
, buf
);
2154 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2155 CFStringAppendCString(newString
, ":", kCFStringEncodingASCII
);
2158 if (relFlags
& NET_LOCATION_MASK
) {
2159 CFStringAppend(newString
, relString
);
2161 CFStringAppendCString(newString
, "//", kCFStringEncodingASCII
);
2162 rg
= _netLocationRange(baseFlags
, baseRanges
);
2163 if (rg
.location
!= kCFNotFound
) {
2164 CFStringGetCharacters(baseString
, rg
, buf
);
2165 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2168 if (relFlags
& HAS_PATH
) {
2169 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
2170 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2171 CFStringRef newPath
;
2172 Boolean useRelPath
= false;
2173 Boolean useBasePath
= false;
2174 if (basePathRg
.location
== kCFNotFound
) {
2176 } else if (relPathRg
.length
== 0) {
2178 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
2180 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2184 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2185 } else if (useBasePath
) {
2186 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2188 // #warning FIXME - Get rid of this allocation
2189 UniChar
*newPathBuf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
2191 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
2192 idx
= newPathBuf
+ basePathRg
.length
- 1;
2193 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
2194 if (*idx
== '/') idx
++;
2195 CFStringGetCharacters(relString
, relPathRg
, idx
);
2196 end
= idx
+ relPathRg
.length
;
2198 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2200 /* Under Win32 absolute path can begin with letter
2201 * so we have to add one '/' to the newString
2204 // No - the input strings here are URL path strings, not Win32 paths.
2205 // Absolute paths should have had a '/' prepended before this point.
2206 // I have removed Sergey Zubarev's change and left his comment (and
2207 // this one) as a record. - REW, 1/5/2004
2209 // if the relative URL does not begin with a slash and
2210 // the base does not end with a slash, add a slash
2211 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2212 CFStringAppendCString(newString
, "/", kCFStringEncodingASCII
);
2215 CFStringAppend(newString
, newPath
);
2217 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2218 rg
.length
= CFStringGetLength(relString
);
2219 if (rg
.length
> rg
.location
) {
2220 rg
.length
-= rg
.location
;
2221 CFStringGetCharacters(relString
, rg
, buf
);
2222 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2225 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2226 if (rg
.location
!= kCFNotFound
) {
2227 CFStringGetCharacters(baseString
, rg
, buf
);
2228 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2231 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2232 // ??? Can this ever happen?
2233 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2235 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2236 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2237 rg
.location
--; // To pick up the separator
2239 CFStringGetCharacters(baseString
, rg
, buf
);
2240 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2242 } else if (relFlags
& HAS_PARAMETERS
) {
2243 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2244 rg
.location
--; // To get the semicolon that starts the parameters
2245 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2246 CFStringGetCharacters(relString
, rg
, buf
);
2247 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2249 // Sigh; we have to resolve these against one another
2250 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2251 if (rg
.location
!= kCFNotFound
) {
2252 CFStringAppendCString(newString
, ";", kCFStringEncodingASCII
);
2253 CFStringGetCharacters(baseString
, rg
, buf
);
2254 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2256 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2257 if (rg
.location
!= kCFNotFound
) {
2258 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2259 CFStringGetCharacters(relString
, rg
, buf
);
2260 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2262 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2263 if (rg
.location
!= kCFNotFound
) {
2264 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2265 CFStringGetCharacters(baseString
, rg
, buf
);
2266 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2269 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2270 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2271 if (rg
.location
!= kCFNotFound
) {
2272 CFStringAppendCString(newString
, "#", kCFStringEncodingASCII
);
2273 CFStringGetCharacters(relString
, rg
, buf
);
2274 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2279 CFAllocatorDeallocate(alloc
, buf
);
2283 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2284 CFURLRef anURL
, base
;
2285 CFURLPathStyle fsType
;
2286 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2287 CFStringRef baseString
, newString
;
2289 CFRange
*baseRanges
;
2292 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2293 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2294 CF_OBJC_CALL0(CFURLRef
, anURL
, relativeURL
, "absoluteURL");
2295 if (anURL
) CFRetain(anURL
);
2299 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2301 base
= relativeURL
->_base
;
2303 return (CFURLRef
)CFRetain(relativeURL
);
2305 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2306 fsType
= URL_PATH_TYPE(relativeURL
);
2308 if (!baseIsObjC
&& fsType
!= FULL_URL_REPRESENTATION
&& fsType
== URL_PATH_TYPE(base
)) {
2309 return _CFURLCopyAbsoluteFileURL(relativeURL
);
2311 if (fsType
!= FULL_URL_REPRESENTATION
) {
2312 _convertToURLRepresentation((struct __CFURL
*)relativeURL
);
2313 fsType
= FULL_URL_REPRESENTATION
;
2315 if (!(relativeURL
->_flags
& IS_PARSED
)) {
2316 _parseComponentsOfURL(relativeURL
);
2318 if ((relativeURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) && !(relativeURL
->_flags
& (RESOURCE_SPECIFIER_MASK
| NET_LOCATION_MASK
)) && !baseIsObjC
&& (URL_PATH_TYPE(base
) == kCFURLPOSIXPathStyle
)) {
2319 // There's nothing to relativeURL's string except the path
2320 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, CFURLHasDirectoryPath(base
), kCFURLPOSIXPathStyle
, alloc
);
2321 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, kCFURLPOSIXPathStyle
, CFURLHasDirectoryPath(relativeURL
));
2327 CFURLPathStyle baseType
= URL_PATH_TYPE(base
);
2328 if (baseType
!= FULL_URL_REPRESENTATION
) {
2329 _convertToURLRepresentation((struct __CFURL
*)base
);
2330 } else if (!(base
->_flags
& IS_PARSED
)) {
2331 _parseComponentsOfURL(base
);
2333 baseString
= base
->_string
;
2334 baseFlags
= base
->_flags
;
2335 baseRanges
= base
->ranges
;
2337 baseString
= CFURLGetString(base
);
2340 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2343 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->ranges
, baseString
, baseFlags
, baseRanges
);
2345 CFAllocatorDeallocate(alloc
, baseRanges
);
2347 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2348 CFRelease(newString
);
2349 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2350 #if DEBUG_URL_MEMORY_USAGE
2351 if ( relativeURL
->_encoding
!= kCFStringEncodingUTF8
) {
2352 numNonUTF8EncodedURLs
++;
2359 /*******************/
2360 /* Basic accessors */
2361 /*******************/
2362 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2363 return url
->_encoding
;
2366 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2367 anURL
= _CFURLFromNSURL(anURL
);
2368 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) return true;
2369 if (!(anURL
->_flags
& IS_PARSED
)) {
2370 _parseComponentsOfURL(anURL
);
2372 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2375 CFStringRef
CFURLGetString(CFURLRef url
) {
2376 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFStringRef
, url
, "relativeString");
2377 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2378 if (url
->_base
&& (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2379 return url
->_string
;
2381 _convertToURLRepresentation((struct __CFURL
*)url
);
2383 if (!_haveTestedOriginalString(url
)) {
2384 computeSanitizedString(url
);
2386 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2387 return url
->_string
;
2389 return _getSanitizedString( url
);
2393 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2394 CFIndex length
, charsConverted
, usedLength
;
2396 CFStringEncoding enc
;
2397 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2398 string
= CFURLGetString(url
);
2399 enc
= kCFStringEncodingUTF8
;
2401 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2402 _convertToURLRepresentation((struct __CFURL
*)url
);
2404 string
= url
->_string
;
2405 enc
= url
->_encoding
;
2407 length
= CFStringGetLength(string
);
2408 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2409 if (charsConverted
!= length
) {
2416 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2417 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, anURL
, "baseURL");
2418 return anURL
->_base
;
2421 // Assumes the URL is already parsed
2422 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2424 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2425 while (!(compFlag
& 1)) {
2426 compFlag
= compFlag
>> 1;
2435 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2438 CFAllocatorRef alloc
= CFGetAllocator(url
);
2439 CFAssert1(URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
, __kCFLogAssertion
, "%s(): passed a file system URL", __PRETTY_FUNCTION__
);
2440 if (removePercentEscapes
) fromOriginalString
= true;
2441 if (!(url
->_flags
& IS_PARSED
)) {
2442 _parseComponentsOfURL(url
);
2444 rg
= _rangeForComponent(url
->_flags
, url
->ranges
, compFlag
);
2445 if (rg
.location
== kCFNotFound
) return NULL
;
2446 if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_HTTP_SCHEME
) {
2447 comp
= kCFURLHTTPScheme
;
2449 } else if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_FILE_SCHEME
) {
2450 comp
= kCFURLFileScheme
;
2453 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2455 if (!fromOriginalString
) {
2456 if (!_haveTestedOriginalString(url
)) {
2457 computeSanitizedString(url
);
2459 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (url
->_flags
& (compFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
))) {
2460 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2465 if (removePercentEscapes
) {
2467 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
2468 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2470 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2478 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2480 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2481 CF_OBJC_CALL0(CFStringRef
, scheme
, anURL
, "scheme");
2482 if (scheme
) CFRetain(scheme
);
2485 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2487 return CFURLCopyScheme(anURL
->_base
);
2489 CFRetain(kCFURLFileScheme
); // because caller will release it
2490 return kCFURLFileScheme
;
2493 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_HTTP_SCHEME
) {
2494 CFRetain(kCFURLHTTPScheme
);
2495 return kCFURLHTTPScheme
;
2497 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_FILE_SCHEME
) {
2498 CFRetain(kCFURLFileScheme
);
2499 return kCFURLFileScheme
;
2501 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2504 } else if (anURL
->_base
) {
2505 return CFURLCopyScheme(anURL
->_base
);
2511 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2513 CFRange netRg
= {kCFNotFound
, 0};
2516 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2518 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2519 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2520 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2521 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2522 for (i
= 0; i
< c
; i
++) {
2523 if (netRgs
[i
].location
== kCFNotFound
) continue;
2524 if (netRg
.location
== kCFNotFound
) {
2527 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2533 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2534 anURL
= _CFURLFromNSURL(anURL
);
2535 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2536 // !!! This won't work if we go to putting the vol ref num in the net location for HFS
2538 return CFURLCopyNetLocation(anURL
->_base
);
2540 CFRetain(kCFURLLocalhost
);
2541 return kCFURLLocalhost
;
2544 if (!(anURL
->_flags
& IS_PARSED
)) {
2545 _parseComponentsOfURL(anURL
);
2547 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2548 // We provide the net location
2549 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->ranges
);
2551 if (!_haveTestedOriginalString(anURL
)) {
2552 computeSanitizedString(anURL
);
2554 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (anURL
->_flags
& (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2555 // 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.
2557 netRg
.length
= CFStringGetLength( _getSanitizedString(anURL
)) - netRg
.location
;
2558 if (CFStringFindWithOptions(_getSanitizedString(anURL
), CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2559 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2561 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), netRg
);
2563 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2566 } else if (anURL
->_base
) {
2567 return CFURLCopyNetLocation(anURL
->_base
);
2573 // 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.
2574 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
2575 anURL
= _CFURLFromNSURL(anURL
);
2576 if (URL_PATH_TYPE(anURL
) == kCFURLPOSIXPathStyle
&& (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2577 CFRetain(anURL
->_string
);
2578 return anURL
->_string
;
2580 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2581 _convertToURLRepresentation((struct __CFURL
*)anURL
);
2583 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
2586 /* 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.
2588 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.
2590 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
2591 CFStringRef path
= CFURLCopyPath(anURL
);
2592 if (!path
|| CFStringGetLength(path
) == 0) {
2593 if (path
) CFRelease(path
);
2594 if (isAbsolute
) *isAbsolute
= false;
2597 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
2599 if (isAbsolute
) *isAbsolute
= true;
2600 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
2604 if (isAbsolute
) *isAbsolute
= false;
2609 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
2610 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2611 if (URL_PATH_TYPE(anURL
) == FULL_URL_REPRESENTATION
) {
2612 if (!(anURL
->_flags
& IS_PARSED
)) {
2613 _parseComponentsOfURL(anURL
);
2615 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
2616 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2618 return CFURLHasDirectoryPath(anURL
->_base
);
2620 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2623 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
2624 UInt32 firstRsrcSpecFlag
= 0;
2625 UInt32 flag
= HAS_FRAGMENT
;
2626 while (flag
!= HAS_PATH
) {
2628 firstRsrcSpecFlag
= flag
;
2632 return firstRsrcSpecFlag
;
2635 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
2636 anURL
= _CFURLFromNSURL(anURL
);
2637 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2638 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2641 if (!(anURL
->_flags
& IS_PARSED
)) {
2642 _parseComponentsOfURL(anURL
);
2644 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
2645 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, HAS_SCHEME
);
2646 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
2647 if (!_haveTestedOriginalString(anURL
)) {
2648 computeSanitizedString(anURL
);
2650 if (_getSanitizedString(anURL
)) {
2651 // 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.
2652 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), CFRangeMake(base
, CFStringGetLength(_getSanitizedString(anURL
))-base
));
2654 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
2657 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
2659 if (firstRsrcSpecFlag
) {
2660 Boolean canUseOriginalString
= true;
2661 Boolean canUseSanitizedString
= true;
2662 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
2663 if (!_haveTestedOriginalString(anURL
)) {
2664 computeSanitizedString(anURL
);
2666 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
2667 // See if any pieces in the resource specifier differ between sanitized string and original string
2668 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
2669 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2670 canUseOriginalString
= false;
2675 if (!canUseOriginalString
) {
2676 // 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.
2677 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
2678 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2679 canUseSanitizedString
= false;
2684 if (canUseOriginalString
) {
2685 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2686 rg
.location
--; // Include the character that demarcates the component
2687 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
2688 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
2689 } else if (canUseSanitizedString
) {
2690 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2691 rg
.location
--; // Include the character that demarcates the component
2692 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2693 return CFStringCreateWithSubstring(alloc
, _getSanitizedString(anURL
), rg
);
2695 // Must compute the correct string to return; just reparse....
2696 UInt32 sanFlags
= 0;
2697 CFRange
*sanRanges
= NULL
;
2699 _parseComponents(alloc
, _getSanitizedString(anURL
), anURL
->_base
, &sanFlags
, &sanRanges
);
2700 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
2701 CFAllocatorDeallocate(alloc
, sanRanges
);
2702 rg
.location
--; // Include the character that demarcates the component
2703 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2704 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), rg
);
2707 // The resource specifier cannot possibly come from the base.
2713 /*************************************/
2714 /* Accessors that create new objects */
2715 /*************************************/
2717 // 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).
2718 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
2720 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2721 CF_OBJC_CALL0(CFStringRef
, tmp
, anURL
, "host");
2722 if (tmp
) CFRetain(tmp
);
2725 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2726 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2728 return CFURLCopyHostName(anURL
->_base
);
2730 CFRetain(kCFURLLocalhost
);
2731 return kCFURLLocalhost
;
2734 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
2736 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
2737 // Have to strip off the brackets to get the true hostname.
2738 // Assume that to be legal the first and last characters are brackets!
2739 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
2744 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2745 return CFURLCopyHostName(anURL
->_base
);
2751 // Return -1 to indicate no port is specified
2752 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
2754 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2756 CF_OBJC_CALL0(CFNumberRef
, cfPort
, anURL
, "port");
2758 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
2761 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2762 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2764 return CFURLGetPortNumber(anURL
->_base
);
2768 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
2770 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
2771 CFStringInlineBuffer buf
;
2772 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
2774 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
2779 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2780 return CFURLGetPortNumber(anURL
->_base
);
2786 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
2788 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2789 CF_OBJC_CALL0(CFStringRef
, user
, anURL
, "user");
2790 if (user
) CFRetain(user
);
2793 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2794 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2796 return CFURLCopyUserName(anURL
->_base
);
2800 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
2803 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2804 return CFURLCopyUserName(anURL
->_base
);
2810 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
2812 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2813 CF_OBJC_CALL0(CFStringRef
, passwd
, anURL
, "password");
2814 if (passwd
) CFRetain(passwd
);
2817 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2818 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2820 return CFURLCopyPassword(anURL
->_base
);
2824 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
2827 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2828 return CFURLCopyPassword(anURL
->_base
);
2834 // 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
2836 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
2838 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2839 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "parameterString");
2840 if (str
) CFRetain(str
);
2843 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2844 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2847 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
2848 if (str
) return str
;
2849 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2850 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
2852 // Parameter string definitely coming from the relative portion of the URL
2854 return _unescapedParameterString( anURL
->_base
);
2857 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2858 CFStringRef param
= _unescapedParameterString(anURL
);
2861 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2862 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
2864 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
2872 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
2874 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2875 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "query");
2876 if (str
) CFRetain(str
);
2879 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2880 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2883 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
2884 if (str
) return str
;
2885 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2886 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
2889 return _unescapedQueryString(anURL
->_base
);
2892 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2893 CFStringRef query
= _unescapedQueryString(anURL
);
2896 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2897 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
2899 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
2907 // Fragments are NEVER taken from a base URL
2908 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
2910 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2911 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "fragment");
2912 if (str
) CFRetain(str
);
2915 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2916 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2919 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
2923 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2924 CFStringRef fragment
= _unescapedFragment(anURL
);
2927 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2928 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
2930 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
2932 CFRelease(fragment
);
2938 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
2939 CFIndex firstMaskFlag
= 1;
2940 CFIndex lastComponentBeforeMask
= 0;
2941 while (firstMaskFlag
<= HAS_FRAGMENT
) {
2942 if (firstMaskFlag
& mask
) break;
2943 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
2944 firstMaskFlag
= firstMaskFlag
<< 1;
2946 if (lastComponentBeforeMask
== 0) {
2947 // mask includes HAS_SCHEME
2949 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
2950 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
2951 // case file:/path/immediately/without/host
2952 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2953 CFRange pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
2954 if (schemeRg
.length
+ 1 == pathRg
.location
) {
2955 return schemeRg
.length
+ 1;
2957 return schemeRg
.length
+ 3;
2960 // For all other components, the separator precedes the component, so there's no need
2961 // to add extra chars to get to the next insertion point
2962 CFRange rg
= _rangeForComponent(url
->_flags
, url
->ranges
, lastComponentBeforeMask
);
2963 return rg
.location
+ rg
.length
;
2967 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
2968 CFOptionFlags currentOption
;
2969 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
2970 Boolean haveReachedMask
= false;
2971 CFIndex beforeMask
= 0;
2972 CFIndex afterMask
= kCFNotFound
;
2973 CFRange
*currRange
= url
->ranges
;
2974 CFRange maskRange
= {kCFNotFound
, 0};
2975 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
2976 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
2977 firstMaskFlag
= currentOption
;
2978 haveReachedMask
= true;
2980 if (!(url
->_flags
& currentOption
)) continue;
2981 if (!haveReachedMask
) {
2982 beforeMask
= currRange
->location
+ currRange
->length
;
2983 } else if (currentOption
<= mask
) {
2984 if (maskRange
.location
== kCFNotFound
) {
2985 maskRange
= *currRange
;
2987 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
2990 afterMask
= currRange
->location
;
2995 if (afterMask
== kCFNotFound
) {
2996 afterMask
= maskRange
.location
+ maskRange
.length
;
2998 charRangeWithSeparators
->location
= beforeMask
;
2999 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
3003 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3005 switch (component
) {
3006 case kCFURLComponentScheme
:
3009 case kCFURLComponentNetLocation
:
3010 mask
= NET_LOCATION_MASK
;
3012 case kCFURLComponentPath
:
3015 case kCFURLComponentResourceSpecifier
:
3016 mask
= RESOURCE_SPECIFIER_MASK
;
3018 case kCFURLComponentUser
:
3021 case kCFURLComponentPassword
:
3022 mask
= HAS_PASSWORD
;
3024 case kCFURLComponentUserInfo
:
3025 mask
= HAS_USER
| HAS_PASSWORD
;
3027 case kCFURLComponentHost
:
3030 case kCFURLComponentPort
:
3033 case kCFURLComponentParameterString
:
3034 mask
= HAS_PARAMETERS
;
3036 case kCFURLComponentQuery
:
3039 case kCFURLComponentFragment
:
3040 mask
= HAS_FRAGMENT
;
3043 rangeIncludingSeparators
->location
= kCFNotFound
;
3044 rangeIncludingSeparators
->length
= 0;
3045 return CFRangeMake(kCFNotFound
, 0);
3048 if ((url
->_flags
& mask
) == 0) {
3049 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
3050 rangeIncludingSeparators
->length
= 0;
3051 return CFRangeMake(kCFNotFound
, 0);
3053 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
3057 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3058 if (component
== kCFURLComponentScheme
) {
3059 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
3060 rangeIncludingSeparators
->location
= 0;
3061 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
3063 } else if (component
== kCFURLComponentResourceSpecifier
) {
3064 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
3065 CFIndex stringLength
= CFStringGetLength(url
->_string
);
3066 if (schemeRg
.length
+ 1 == stringLength
) {
3067 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
3068 rangeIncludingSeparators
->length
= 0;
3069 return CFRangeMake(kCFNotFound
, 0);
3071 rangeIncludingSeparators
->location
= schemeRg
.length
;
3072 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
3073 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
3076 rangeIncludingSeparators
->location
= kCFNotFound
;
3077 rangeIncludingSeparators
->length
= 0;
3078 return CFRangeMake(kCFNotFound
, 0);
3083 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3084 CFRange charRange
, charRangeWithSeparators
;
3086 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
3087 url
= _CFURLFromNSURL(url
);
3088 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
3089 _convertToURLRepresentation((struct __CFURL
*)url
);
3091 if (!(url
->_flags
& IS_PARSED
)) {
3092 _parseComponentsOfURL(url
);
3095 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
3096 // Special-case this because non-decomposable URLs have a slightly strange flags setup
3097 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
3099 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
3102 if (charRangeWithSeparators
.location
== kCFNotFound
) {
3103 if (rangeIncludingSeparators
) {
3104 rangeIncludingSeparators
->location
= kCFNotFound
;
3105 rangeIncludingSeparators
->length
= 0;
3107 return CFRangeMake(kCFNotFound
, 0);
3108 } else if (rangeIncludingSeparators
) {
3109 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
3111 if (charRange
.location
== kCFNotFound
) {
3112 byteRange
= charRange
;
3113 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
3115 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
3116 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
3118 if (charRangeWithSeparators
.location
== charRange
.location
) {
3119 byteRange
.location
= rangeIncludingSeparators
->location
;
3122 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3123 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
3125 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3126 if (maxCharRangeWithSeparators
== maxCharRange
) {
3127 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
3131 rg
.location
= maxCharRange
;
3132 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
3133 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3134 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
3137 } else if (charRange
.location
== kCFNotFound
) {
3138 byteRange
= charRange
;
3140 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
3141 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3146 /* Component support */
3148 /* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */
3149 static CFStringRef
schemeSpecificString(CFURLRef url
) {
3151 isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3152 switch (URL_PATH_TYPE(url
)) {
3153 case kCFURLPOSIXPathStyle
:
3154 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3155 return (CFStringRef
)CFRetain(url
->_string
);
3157 return POSIXPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3159 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3160 case kCFURLHFSPathStyle
:
3161 return HFSPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3162 #elif DEPLOYMENT_TARGET_WINDOWS
3164 #error Unknown or unspecified DEPLOYMENT_TARGET
3166 case kCFURLWindowsPathStyle
:
3167 return WindowsPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3168 case FULL_URL_REPRESENTATION
:
3169 return CFURLCopyResourceSpecifier(url
);
3175 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
3176 if ( CFURLGetBaseURL(url
) != NULL
) {
3177 components
->scheme
= NULL
;
3179 components
->scheme
= CFURLCopyScheme(url
);
3181 components
->schemeSpecific
= schemeSpecificString(url
);
3185 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
3187 if (components
->scheme
) {
3189 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
3190 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
3191 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
3192 } else if (components
->schemeSpecific
) {
3193 str
= components
->schemeSpecific
;
3199 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3207 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3208 CFAllocatorRef alloc
= CFGetAllocator(url
);
3210 static CFStringRef emptyStr
= NULL
;
3212 emptyStr
= CFSTR("");
3215 if (!CFURLCanBeDecomposed(url
)) {
3218 if ((pathType
= URL_PATH_TYPE(url
)) == FULL_URL_REPRESENTATION
) {
3219 CFStringRef path
= CFURLCopyPath(url
);
3221 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3224 components
->pathComponents
= NULL
;
3226 components
->baseURL
= CFURLGetBaseURL(url
);
3227 if (components
->baseURL
) {
3228 CFRetain(components
->baseURL
);
3229 components
->scheme
= NULL
;
3231 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
3233 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
3234 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3235 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3236 if (url
->_flags
& HAS_PORT
) {
3237 components
->port
= CFURLGetPortNumber(url
);
3239 components
->port
= kCFNotFound
;
3241 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3242 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3243 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3246 case kCFURLPOSIXPathStyle
: {
3247 CFStringRef pathStr
;
3248 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3249 pathStr
= url
->_string
;
3252 pathStr
= POSIXPathToURLPath(url
->_string
, alloc
, url
->_flags
& IS_DIRECTORY
);
3254 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, pathStr
, CFSTR("/"));
3258 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3259 case kCFURLHFSPathStyle
:
3260 components
->pathComponents
= HFSPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3262 #elif DEPLOYMENT_TARGET_WINDOWS
3264 #error Unknown or unspecified DEPLOYMENT_TARGET
3266 case kCFURLWindowsPathStyle
:
3267 components
->pathComponents
= WindowsPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3270 components
->pathComponents
= NULL
;
3272 if (!components
->pathComponents
) {
3275 components
->scheme
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
3276 components
->user
= NULL
;
3277 components
->password
= NULL
;
3278 components
->host
= (CFStringRef
)CFRetain(kCFURLLocalhost
);
3279 components
->port
= kCFNotFound
;
3280 components
->parameterString
= NULL
;
3281 components
->query
= NULL
;
3282 components
->fragment
= NULL
;
3283 components
->baseURL
= CFURLGetBaseURL(url
);
3284 if (components
->baseURL
) CFRetain(components
->baseURL
);
3289 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3290 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3291 CFURLRef base
= comp
->baseURL
;
3293 Boolean hadPrePathComponent
= false;
3296 CFStringAppend(urlString
, comp
->scheme
);
3297 CFStringAppend(urlString
, CFSTR("://"));
3298 hadPrePathComponent
= true;
3300 if (comp
->user
|| comp
->password
) {
3302 CFStringAppend(urlString
, comp
->user
);
3304 if (comp
->password
) {
3305 CFStringAppend(urlString
, CFSTR(":"));
3306 CFStringAppend(urlString
, comp
->password
);
3308 CFStringAppend(urlString
, CFSTR("@"));
3309 hadPrePathComponent
= true;
3312 CFStringAppend(urlString
, comp
->host
);
3313 hadPrePathComponent
= true;
3315 if (comp
->port
!= kCFNotFound
) {
3316 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3317 hadPrePathComponent
= true;
3320 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFArrayGetCount( comp
->pathComponents
) == 0 || CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3321 CFStringAppend(urlString
, CFSTR("/"));
3323 if (comp
->pathComponents
) {
3324 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3325 CFStringAppend(urlString
, pathStr
);
3328 if (comp
->parameterString
) {
3329 CFStringAppend(urlString
, CFSTR(";"));
3330 CFStringAppend(urlString
, comp
->parameterString
);
3333 CFStringAppend(urlString
, CFSTR("?"));
3334 CFStringAppend(urlString
, comp
->query
);
3336 if (comp
->fragment
) {
3337 CFStringAppend(urlString
, CFSTR("#"));
3338 CFStringAppend(urlString
, comp
->fragment
);
3340 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3341 CFRelease(urlString
);
3345 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3346 CFAllocatorRef alloc
= CFGetAllocator(url
);
3347 CFURLComponentsRFC1808 oldComp
;
3349 if (!decomposeToRFC1808(url
, &oldComp
)) {
3352 comp
->scheme
= oldComp
.scheme
;
3354 if (oldComp
.password
) {
3355 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3356 CFRelease(oldComp
.password
);
3357 CFRelease(oldComp
.user
);
3359 comp
->userinfo
= oldComp
.user
;
3362 comp
->userinfo
= NULL
;
3364 comp
->host
= oldComp
.host
;
3365 comp
->port
= oldComp
.port
;
3366 if (!oldComp
.parameterString
) {
3367 comp
->pathComponents
= oldComp
.pathComponents
;
3369 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3370 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3371 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3372 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3374 CFRelease(oldComp
.pathComponents
);
3375 CFRelease(oldComp
.parameterString
);
3377 comp
->query
= oldComp
.query
;
3378 comp
->fragment
= oldComp
.fragment
;
3379 comp
->baseURL
= oldComp
.baseURL
;
3383 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3384 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3385 CFURLRef base
= comp
->baseURL
;
3387 Boolean hadPrePathComponent
= false;
3390 CFStringAppend(urlString
, comp
->scheme
);
3391 CFStringAppend(urlString
, CFSTR("://"));
3392 hadPrePathComponent
= true;
3394 if (comp
->userinfo
) {
3395 CFStringAppend(urlString
, comp
->userinfo
);
3396 CFStringAppend(urlString
, CFSTR("@"));
3397 hadPrePathComponent
= true;
3400 CFStringAppend(urlString
, comp
->host
);
3401 if (comp
->port
!= kCFNotFound
) {
3402 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3404 hadPrePathComponent
= true;
3406 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3407 CFStringAppend(urlString
, CFSTR("/"));
3409 if (comp
->pathComponents
) {
3410 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3411 CFStringAppend(urlString
, pathStr
);
3415 CFStringAppend(urlString
, CFSTR("?"));
3416 CFStringAppend(urlString
, comp
->query
);
3418 if (comp
->fragment
) {
3419 CFStringAppend(urlString
, CFSTR("#"));
3420 CFStringAppend(urlString
, comp
->fragment
);
3422 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3423 CFRelease(urlString
);
3427 #undef CFURLCopyComponents
3428 #undef CFURLCreateFromComponents
3431 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3432 url
= _CFURLFromNSURL(url
);
3433 switch (decompositionType
) {
3434 case kCFURLComponentDecompositionNonHierarchical
:
3435 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3436 case kCFURLComponentDecompositionRFC1808
:
3437 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3438 case kCFURLComponentDecompositionRFC2396
:
3439 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3446 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3447 switch (decompositionType
) {
3448 case kCFURLComponentDecompositionNonHierarchical
:
3449 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3450 case kCFURLComponentDecompositionRFC1808
:
3451 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3452 case kCFURLComponentDecompositionRFC2396
:
3453 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3459 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3460 return _getReserved(url
);
3463 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3464 _setReserved ( (struct __CFURL
*) url
, ptr
);
3467 CF_EXPORT
void *__CFURLResourceInfoPtr(CFURLRef url
) {
3468 return _getResourceInfo(url
);
3471 CF_EXPORT
void __CFURLSetResourceInfoPtr(CFURLRef url
, void *ptr
) {
3472 _setResourceInfo ( (struct __CFURL
*) url
, ptr
);
3475 /* File system stuff */
3477 /* HFSPath<->URLPath functions at the bottom of the file */
3478 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3480 CFMutableArrayRef urlComponents
= NULL
;
3483 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3484 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3487 CFStringRef str
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, 0);
3488 if (CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3489 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR("")); // So we get a leading '/' below
3490 i
= 2; // Skip over the drive letter and the empty string we just inserted
3493 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3494 CFStringRef fileComp
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
,i
);
3495 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3497 // Couldn't decode fileComp
3498 CFRelease(urlComponents
);
3501 if (urlComp
!= fileComp
) {
3502 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3508 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3509 CFArrayAppendValue(urlComponents
, CFSTR(""));
3511 return urlComponents
;
3514 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3515 CFArrayRef urlComponents
;
3518 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3519 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
);
3520 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3522 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3523 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3524 CFRelease(urlComponents
);
3528 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
) {
3529 CFStringRef pathString
= _replacePathIllegalCharacters(path
, alloc
, true);
3530 if (isDirectory
&& CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
)-1) != '/') {
3531 CFStringRef tmp
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@/"), pathString
);
3532 CFRelease(pathString
);
3538 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3539 // This is the easiest case; just remove the percent escape codes and we're done
3540 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3542 CFIndex length
= CFStringGetLength(result
);
3543 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3544 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3553 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3554 // Check for a drive letter, then flip all the slashes
3556 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
3557 SInt32 count
= CFArrayGetCount(tmp
);
3558 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
3559 CFStringRef newPath
;
3564 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
3565 CFArrayRemoveValueAtIndex(components
, count
-1);
3569 if (count
> 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
, 0)) == 0) {
3570 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
3571 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, (CFStringRef
)CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
3575 if (firstComponent
) {
3576 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
3578 CFArrayRemoveValueAtIndex(components
, 0);
3580 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
3581 CFArraySetValueAtIndex(components
, 0, driveStr
);
3582 CFRelease(driveStr
);
3585 CFRelease(firstComponent
);
3589 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
3590 CFRelease(components
);
3591 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
3598 // converts url from a file system path representation to a standard representation
3599 static void _convertToURLRepresentation(struct __CFURL
*url
) {
3600 CFStringRef path
= NULL
;
3601 Boolean isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3602 Boolean isFileReferencePath
= false;
3603 CFAllocatorRef alloc
= CFGetAllocator(url
);
3605 #if DEBUG_URL_MEMORY_USAGE
3606 numFileURLsConverted
++;
3609 switch (URL_PATH_TYPE(url
)) {
3610 case kCFURLPOSIXPathStyle
:
3611 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3612 path
= (CFStringRef
)CFRetain(url
->_string
);
3614 path
= POSIXPathToURLPath(url
->_string
, alloc
, isDir
);
3617 case kCFURLWindowsPathStyle
:
3618 path
= WindowsPathToURLPath(url
->_string
, alloc
, isDir
);
3621 CFAssert2(path
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, url
);
3623 CFMutableStringRef str
= CFStringCreateMutable(alloc
, 0);
3624 CFStringAppend(str
, isFileReferencePath
? CFSTR("file://") : CFSTR("file://localhost"));
3625 CFStringAppend(str
, path
);
3626 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 );
3627 CFRelease(url
->_string
);
3629 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
3630 url
->ranges
[0] = CFRangeMake(0, 4);
3631 url
->ranges
[1] = CFRangeMake(7, isFileReferencePath
? 0 : 9);
3632 url
->ranges
[2] = CFRangeMake(url
->ranges
[1].location
+ url
->ranges
[1].length
, CFStringGetLength(path
));
3635 CFRelease(url
->_string
);
3636 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_PARSED
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3637 url
->_string
= path
;
3638 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
3639 *(url
->ranges
) = CFRangeMake(0, CFStringGetLength(path
));
3643 // relativeURL is known to be a file system URL whose base is a matching file system URL
3644 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
) {
3645 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
3646 CFURLPathStyle fsType
= URL_PATH_TYPE(relativeURL
);
3647 CFURLRef base
= relativeURL
->_base
;
3648 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, (base
->_flags
& IS_DIRECTORY
) != 0, fsType
, alloc
);
3649 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, fsType
, (relativeURL
->_flags
& IS_DIRECTORY
) != 0);
3654 // Caller must release the returned string
3655 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
3656 CFIndex baseLen
= CFStringGetLength(basePath
);
3657 CFIndex relLen
= CFStringGetLength(relativePath
);
3658 UniChar pathDelimiter
= PATH_DELIM_FOR_TYPE(fsType
);
3659 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
3660 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
3662 if (buf
[baseLen
-1] != pathDelimiter
) {
3663 buf
[baseLen
] = pathDelimiter
;
3667 UniChar
*ptr
= buf
+ baseLen
- 1;
3668 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
3671 baseLen
= ptr
- buf
+ 1;
3673 if (fsType
== kCFURLHFSPathStyle
) {
3674 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3677 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
3678 *(buf
+ baseLen
+ relLen
) = '\0';
3679 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
3682 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
3683 CFURLRef url
= NULL
;
3684 uint8_t buf
[CFMaxPathSize
+ 1];
3685 if (_CFGetCurrentDirectory((char *)buf
, CFMaxPathLength
)) {
3686 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen((char *)buf
), true);
3691 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
3692 Boolean isAbsolute
= true;
3694 CFURLRef baseURL
, result
;
3696 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3698 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__
);
3700 len
= CFStringGetLength(filePath
);
3703 case kCFURLPOSIXPathStyle
:
3704 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3706 case kCFURLWindowsPathStyle
:
3707 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3708 /* Absolute path under Win32 can begin with "\\"
3711 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3713 case kCFURLHFSPathStyle
:
3714 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3720 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3722 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
3723 if (baseURL
) CFRelease(baseURL
);
3727 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
3729 Boolean isAbsolute
= true, releaseFilePath
= false;
3730 UniChar pathDelim
= '\0';
3733 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
3734 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3736 len
= CFStringGetLength(filePath
);
3739 case kCFURLPOSIXPathStyle
:
3740 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3744 case kCFURLWindowsPathStyle
:
3745 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3746 /* Absolute path under Win32 can begin with "\\"
3750 isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3753 case kCFURLHFSPathStyle
:
3754 { CFRange fullStrRange
= CFRangeMake( 0, CFStringGetLength( filePath
) );
3756 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3759 if ( _CFExecutableLinkedOnOrAfter( CFSystemVersionTiger
) &&
3760 filePath
&& CFStringFindWithOptions( filePath
, CFSTR("::"), fullStrRange
, 0, NULL
) ) {
3761 UniChar
* chars
= (UniChar
*) malloc( fullStrRange
.length
* sizeof( UniChar
) );
3762 CFIndex index
, writeIndex
, firstColonOffset
= -1;
3764 CFStringGetCharacters( filePath
, fullStrRange
, chars
);
3766 for ( index
= 0, writeIndex
= 0 ; index
< fullStrRange
.length
; index
++ ) {
3767 if ( chars
[ index
] == ':' ) {
3768 if ( index
+ 1 < fullStrRange
.length
&& chars
[ index
+ 1 ] == ':' ) {
3770 // Don't let :: go off the 'top' of the path -- which means that there always has to be at
3771 // least one ':' to the left of the current write position to go back to.
3772 if ( writeIndex
> 0 && firstColonOffset
>= 0 )
3775 while ( writeIndex
> 0 && writeIndex
>= firstColonOffset
&& chars
[ writeIndex
] != ':' )
3778 index
++; // skip over the first ':', so we replace the ':' which is there with a new one
3781 if ( firstColonOffset
== -1 )
3782 firstColonOffset
= writeIndex
;
3785 chars
[ writeIndex
++ ] = chars
[ index
];
3788 if ( releaseFilePath
&& filePath
)
3789 CFRelease( filePath
);
3791 filePath
= CFStringCreateWithCharacters( allocator
, chars
, writeIndex
);
3792 // reset len because a canonical HFS path can be a different length than the original CFString
3793 len
= CFStringGetLength(filePath
);
3794 releaseFilePath
= true;
3806 if (isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) != pathDelim
) {
3807 CFMutableStringRef tempRef
= CFStringCreateMutable(allocator
, 0);
3808 CFStringAppend(tempRef
, filePath
);
3809 CFStringAppendCharacters(tempRef
, &pathDelim
, 1);
3810 if ( releaseFilePath
&& filePath
) CFRelease( filePath
);
3812 releaseFilePath
= true;
3813 } else if (!isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) == pathDelim
) {
3814 if (len
== 1 || CFStringGetCharacterAtIndex(filePath
, len
-2) == pathDelim
) {
3815 // Override isDirectory
3818 CFStringRef tempRef
= CFStringCreateWithSubstring(allocator
, filePath
, CFRangeMake(0, len
-1));
3819 if ( releaseFilePath
&& filePath
)
3820 CFRelease( filePath
);
3822 releaseFilePath
= true;
3825 if (!filePath
|| CFStringGetLength(filePath
) == 0) {
3826 if (releaseFilePath
&& filePath
) CFRelease(filePath
);
3829 url
= _CFURLAlloc(allocator
);
3830 _CFURLInit((struct __CFURL
*)url
, filePath
, fsType
, baseURL
);
3831 if (releaseFilePath
) CFRelease(filePath
);
3832 if (isDirectory
) ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
3833 if (fsType
== kCFURLPOSIXPathStyle
) {
3834 // 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
3835 // 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
3836 CFStringInlineBuffer buf
;
3837 Boolean sawSlash
= FALSE
;
3838 Boolean mustPrependDotSlash
= FALSE
;
3839 CFIndex idx
, length
= CFStringGetLength(url
->_string
);
3840 CFStringInitInlineBuffer(url
->_string
, &buf
, CFRangeMake(0, length
));
3841 for (idx
= 0; idx
< length
; idx
++) {
3842 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
3843 if (!isPathLegalCharacter(ch
)) break;
3847 } else if (ch
== ':') {
3848 mustPrependDotSlash
= TRUE
;
3852 if (idx
== length
) {
3853 ((struct __CFURL
*)url
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
3855 if (mustPrependDotSlash
) {
3856 CFMutableStringRef newString
= CFStringCreateMutable(allocator
, 0);
3857 CFStringAppend(newString
, CFSTR("./"));
3858 CFStringAppend(newString
, url
->_string
);
3859 CFRelease(url
->_string
);
3860 ((struct __CFURL
*)url
)->_string
= newString
;
3866 static Boolean
_pathHasFileIDPrefix( CFStringRef path
)
3868 // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
3869 static const CFStringRef fileIDPrefix
= CFSTR( "/" FILE_ID_PREFIX
"/" );
3870 return path
&& CFStringHasPrefix( path
, fileIDPrefix
) && CFStringGetLength( path
) > CFStringGetLength( fileIDPrefix
);
3874 static Boolean
_pathHasFileIDOnly( CFStringRef path
)
3876 // Is file ID rooted and contains no additonal path segments
3878 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 );
3882 CF_EXPORT CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
3883 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(pathStyle
), __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
3885 return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL
), anURL
, pathStyle
, false);
3889 // 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
3890 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
3891 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
3892 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
3893 CFStringRef relPath
= NULL
;
3895 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3896 // We can grope the ivars
3897 CFURLPathStyle myType
= URL_PATH_TYPE(anURL
);
3898 if (myType
== fsType
) {
3899 relPath
= (CFStringRef
)CFRetain(anURL
->_string
);
3900 } else if (fsType
== kCFURLPOSIXPathStyle
&& myType
== FULL_URL_REPRESENTATION
) {
3901 if (!(anURL
->_flags
& IS_PARSED
)) {
3902 _parseComponentsOfURL(anURL
);
3904 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3905 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
3910 if (relPath
== NULL
) {
3911 CFStringRef urlPath
= CFURLCopyPath(anURL
);
3912 CFStringEncoding enc
= (anURL
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: anURL
->_encoding
;
3915 case kCFURLPOSIXPathStyle
:
3916 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
3918 case kCFURLHFSPathStyle
:
3921 case kCFURLWindowsPathStyle
:
3922 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
3925 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
3931 // For Tiger, leave this behavior in for all path types. For Leopard, it would be nice to remove this entirely
3932 // and do a linked-on-or-later check so we don't break third parties.
3933 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
3934 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
3935 if ( relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == PATH_DELIM_FOR_TYPE(fsType
)) {
3936 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
3941 // Note that !resolveAgainstBase implies !base
3942 if (!basePath
|| !relPath
) {
3945 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
3946 CFRelease(basePath
);
3952 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
3954 CFAllocatorRef alloc
= CFGetAllocator(url
);
3956 if (!url
) return false;
3957 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3958 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
3960 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
3964 #elif DEPLOYMENT_TARGET_WINDOWS
3965 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
3968 CFIndex pathLen
= CFStringGetLength(path
);
3969 CFIndex numConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLen
), CFStringFileSystemEncoding(), 0, true, buffer
, bufLen
-1, &usedLen
); // -1 because we need one byte to zero-terminate.
3971 if (numConverted
== pathLen
) {
3972 buffer
[usedLen
] = '\0';
3977 #error Unknown or unspecified DEPLOYMENT_TARGET
3982 #if DEPLOYMENT_TARGET_WINDOWS
3983 CF_EXPORT Boolean
_CFURLGetWideFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, wchar_t *buffer
, CFIndex bufferLength
) {
3984 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
3985 CFIndex pathLength
, charsConverted
, usedBufLen
;
3986 if (!path
) return false;
3987 pathLength
= CFStringGetLength(path
);
3988 if (pathLength
+1 > bufferLength
) {
3992 charsConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLength
), kCFStringEncodingUTF16
, 0, false, (UInt8
*)buffer
, bufferLength
*sizeof(wchar_t), &usedBufLen
);
3993 // CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer);
3995 if (charsConverted
!= pathLength
|| usedBufLen%sizeof
(wchar_t) != 0) {
3998 buffer
[usedBufLen
/sizeof(wchar_t)] = 0;
3999 // buffer[pathLength] = 0;
4005 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
4006 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4008 if (!path
) return NULL
;
4009 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4010 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
);
4011 #elif DEPLOYMENT_TARGET_WINDOWS
4012 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
);
4014 #error Unknown or unspecified DEPLOYMENT_TARGET
4020 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
4021 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4023 if (!path
) return NULL
;
4024 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4025 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
, baseURL
);
4026 #elif DEPLOYMENT_TARGET_WINDOWS
4027 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
4029 #error Unknown or unspecified DEPLOYMENT_TARGET
4036 /******************************/
4037 /* Support for path utilities */
4038 /******************************/
4040 // Assumes url is a CFURL (not an Obj-C NSURL)
4041 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
4042 UInt32 pathType
= URL_PATH_TYPE(url
);
4043 CFRange pathRg
, componentRg
;
4045 if (pathType
== FULL_URL_REPRESENTATION
) {
4046 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4047 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4049 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
4052 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
4056 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == PATH_DELIM_FOR_TYPE(pathType
)) {
4058 if (pathRg
.length
== 0) {
4063 if (CFStringFindWithOptions(url
->_string
, PATH_DELIM_AS_STRING_FOR_TYPE(pathType
), pathRg
, kCFCompareBackwards
, &componentRg
)) {
4064 componentRg
.location
++;
4065 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
4067 componentRg
= pathRg
;
4072 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
4075 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4076 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
4079 if (!path
) return NULL
;
4080 rg
= CFRangeMake(0, CFStringGetLength(path
));
4081 if ( rg
.length
== 0 ) return path
;
4082 length
= rg
.length
; // Remember this for comparison later
4083 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/' ) {
4086 if ( rg
.length
== 0 )
4088 // If we have reduced the string to empty, then it's "/", and that's what we return as
4089 // the last path component.
4092 else if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
4093 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
4094 rg
.location
= compRg
.location
+ 1;
4096 if (rg
.location
== 0 && rg
.length
== length
) {
4099 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), path
, rg
);
4103 CFRange rg
= _rangeOfLastPathComponent(url
);
4104 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
4106 return (CFStringRef
)CFRetain(CFSTR(""));
4108 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url
))) {
4109 return (CFStringRef
)CFRetain(CFSTR("/"));
4111 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
4112 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
&& !(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
4114 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
4115 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
4117 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
4126 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
4127 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
4128 CFStringRef ext
= NULL
;
4131 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
4132 if (rg
.location
!= kCFNotFound
) {
4134 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
4135 if (rg
.length
> 0) {
4136 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
4138 ext
= (CFStringRef
)CFRetain(CFSTR(""));
4141 CFRelease(lastPathComp
);
4146 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
4149 url
= _CFURLFromNSURL(url
);
4150 __CFGenericValidateType(url
, __kCFURLTypeID
);
4151 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
4153 fsType
= URL_PATH_TYPE(url
);
4154 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(pathComponent
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(pathComponent
)), 0, NULL
)) {
4155 // Must convert to full representation, and then work with it
4156 fsType
= FULL_URL_REPRESENTATION
;
4157 _convertToURLRepresentation((struct __CFURL
*)url
);
4160 if (fsType
== FULL_URL_REPRESENTATION
) {
4161 CFMutableStringRef newString
;
4162 CFStringRef newComp
;
4164 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4165 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
4167 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4168 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4169 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4170 if (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
4171 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4174 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
4176 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
4179 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4180 CFRelease(newString
);
4182 UniChar pathDelim
= PATH_DELIM_FOR_TYPE(fsType
);
4183 CFStringRef newString
;
4184 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != pathDelim
) {
4186 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@%c"), url
->_string
, pathDelim
, pathComponent
, pathDelim
);
4188 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@"), url
->_string
, pathDelim
, pathComponent
);
4192 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@%c"), url
->_string
, pathComponent
, pathDelim
);
4194 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@"), url
->_string
, pathComponent
);
4197 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, isDirectory
, url
->_base
);
4198 CFRelease(newString
);
4203 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
4205 CFMutableStringRef newString
;
4206 CFRange lastCompRg
, pathRg
;
4207 Boolean appendDotDot
= false;
4210 url
= _CFURLFromNSURL(url
);
4211 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4212 __CFGenericValidateType(url
, __kCFURLTypeID
);
4214 fsType
= URL_PATH_TYPE(url
);
4215 if (fsType
== FULL_URL_REPRESENTATION
) {
4216 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4217 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
4218 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4220 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
4222 lastCompRg
= _rangeOfLastPathComponent(url
);
4223 if (lastCompRg
.length
== 0) {
4224 appendDotDot
= true;
4225 } else if (lastCompRg
.length
== 1) {
4226 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
4227 if (ch
== '.' || ch
== PATH_DELIM_FOR_TYPE(fsType
)) {
4228 appendDotDot
= true;
4230 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
4231 appendDotDot
= true;
4234 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4237 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != PATH_DELIM_FOR_TYPE(fsType
)) {
4238 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4241 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
4243 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4245 // 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 "/.".
4246 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
4247 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == PATH_DELIM_FOR_TYPE(fsType
)) {
4248 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
4249 } else if (pathRg
.length
+delta
== 5) {
4250 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
4253 } else if (lastCompRg
.location
== pathRg
.location
) {
4254 CFStringReplace(newString
, pathRg
, CFSTR("."));
4255 CFStringInsert(newString
, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4257 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
4259 if (fsType
== FULL_URL_REPRESENTATION
) {
4260 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4262 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, true, url
->_base
);
4264 CFRelease(newString
);
4268 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
4269 CFMutableStringRef newString
;
4272 CFURLPathStyle fsType
;
4274 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4275 url
= _CFURLFromNSURL(url
);
4276 __CFGenericValidateType(url
, __kCFURLTypeID
);
4277 __CFGenericValidateType(extension
, CFStringGetTypeID());
4279 rg
= _rangeOfLastPathComponent(url
);
4280 if (rg
.location
< 0) return NULL
; // No path
4281 fsType
= URL_PATH_TYPE(url
);
4282 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(extension
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(extension
)), 0, NULL
)) {
4283 _convertToURLRepresentation((struct __CFURL
*)url
);
4284 fsType
= FULL_URL_REPRESENTATION
;
4285 rg
= _rangeOfLastPathComponent(url
);
4288 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4289 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
4290 if (fsType
== FULL_URL_REPRESENTATION
) {
4291 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4292 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
4294 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4296 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, extension
);
4297 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4299 CFRelease(newString
);
4303 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4307 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4308 url
= _CFURLFromNSURL(url
);
4309 __CFGenericValidateType(url
, __kCFURLTypeID
);
4310 rg
= _rangeOfLastPathComponent(url
);
4311 if (rg
.location
< 0) {
4313 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
4314 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4315 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
4316 CFStringDelete(newString
, dotRg
);
4317 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
) {
4318 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4320 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, URL_PATH_TYPE(url
), (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4322 CFRelease(newString
);
4324 result
= (CFURLRef
)CFRetain(url
);
4330 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4331 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4332 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4333 CFMutableArrayRef newComponents
= CFArrayCreateMutableCopy(alloc
, 0, components
);
4334 Boolean doSpecialLeadingColon
= false;
4335 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
4337 CFRelease(components
);
4340 if (!doSpecialLeadingColon
&& firstChar
!= ':') {
4341 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4342 } else if (firstChar
!= ':') {
4343 // see what we need to add at the beginning. Under MacOS, if the
4344 // first character isn't a ':', then the first component is the
4345 // volume name, and we need to find the mount point. Bleah. If we
4346 // don't find a mount point, we're going to have to lie, and make something up.
4347 CFStringRef firstComp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0);
4348 if (CFStringGetLength(firstComp
) == 1 && CFStringGetCharacterAtIndex(firstComp
, 0) == '/') {
4349 // "/" is the "magic" path for a UFS root directory
4350 CFArrayRemoveValueAtIndex(newComponents
, 0);
4351 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4353 // See if we can get a mount point.
4354 Boolean foundMountPoint
= false;
4355 if (!foundMountPoint
) {
4356 // Fall back to treating the volume name as the top level directory
4357 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4361 CFArrayRemoveValueAtIndex(newComponents
, 0);
4364 cnt
= CFArrayGetCount(newComponents
);
4365 for (i
= 0; i
< cnt
; i
++) {
4366 CFStringRef comp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, i
);
4367 CFStringRef newComp
= NULL
;
4368 CFRange searchRg
, slashRg
;
4369 searchRg
.location
= 0;
4370 searchRg
.length
= CFStringGetLength(comp
);
4371 while (CFStringFindWithOptions(comp
, CFSTR("/"), searchRg
, 0, &slashRg
)) {
4373 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4375 CFStringReplace((CFMutableStringRef
)newComp
, slashRg
, CFSTR(":"));
4376 searchRg
.length
= searchRg
.location
+ searchRg
.length
- slashRg
.location
- 1;
4377 searchRg
.location
= slashRg
.location
+ 1;
4380 CFArraySetValueAtIndex(newComponents
, i
, newComp
);
4384 if (isDir
&& CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, cnt
-1)) != 0) {
4385 CFArrayAppendValue(newComponents
, CFSTR(""));
4387 return newComponents
;
4389 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4390 CFArrayRef components
= HFSPathToURLComponents(path
, alloc
, isDir
);
4391 CFArrayRef newComponents
= components
? copyStringArrayWithTransformation(components
, escapePathComponent
) : NULL
;
4394 if (components
) CFRelease(components
);
4395 if (!newComponents
) return NULL
;
4397 cnt
= CFArrayGetCount(newComponents
);
4398 if (cnt
== 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0)) == 0) {
4399 result
= (CFStringRef
)CFRetain(CFSTR("/"));
4401 result
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
4403 CFRelease(newComponents
);
4406 #elif DEPLOYMENT_TARGET_WINDOWS
4408 #error Unknown or unspecified DEPLOYMENT_TARGET
4413 // keys and vals must have space for at least 4 key/value pairs. No argument can be NULL.
4414 // Caller must release values, but not keys
4415 static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url
, CFTypeRef
*keys
, CFTypeRef
*vals
, CFIndex
*count
) {
4416 CFAllocatorRef alloc
= CFGetAllocator(url
);
4417 CFURLRef base
= CFURLGetBaseURL(url
);
4418 keys
[0] = CFSTR("_CFURLStringType");
4419 keys
[1] = CFSTR("_CFURLString");
4420 keys
[2] = CFSTR("_CFURLBaseStringType");
4421 keys
[3] = CFSTR("_CFURLBaseURLString");
4422 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4423 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4424 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4425 vals
[1] = CFURLGetString(url
);
4427 SInt32 urlType
= URL_PATH_TYPE(url
);
4428 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4429 if (url
->_flags
& IS_DIRECTORY
) {
4430 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4431 vals
[1] = CFRetain(url
->_string
);
4433 vals
[1] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), url
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4436 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4437 vals
[1] = CFRetain(url
->_string
);
4439 vals
[1] = CFStringCreateWithSubstring(alloc
, url
->_string
, CFRangeMake(0, CFStringGetLength(url
->_string
) - 1));
4444 if (CF_IS_OBJC(__kCFURLTypeID
, base
)) {
4445 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4446 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4447 vals
[3] = CFURLGetString(base
);
4449 SInt32 urlType
= URL_PATH_TYPE(base
);
4450 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4451 if (base
->_flags
& IS_DIRECTORY
) {
4452 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4453 vals
[3] = CFRetain(base
->_string
);
4455 vals
[3] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), base
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4458 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4459 vals
[3] = CFRetain(base
->_string
);
4461 vals
[3] = CFStringCreateWithSubstring(alloc
, base
->_string
, CFRangeMake(0, CFStringGetLength(base
->_string
) - 1));
4471 // Private API for Finder to use
4472 CFPropertyListRef
_CFURLCopyPropertyListRepresentation(CFURLRef url
) {
4473 CFTypeRef keys
[4], vals
[4];
4474 CFDictionaryRef dict
;
4476 __CFURLCopyPropertyListKeysAndValues(url
, keys
, vals
, &count
);
4477 dict
= CFDictionaryCreate(CFGetAllocator(url
), keys
, vals
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4478 for (idx
= 0; idx
< count
; idx
++) {
4479 CFRelease(vals
[idx
]);
4484 CFURLRef
_CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc
, CFPropertyListRef pListRepresentation
) {
4485 CFStringRef baseString
, string
;
4486 CFNumberRef baseTypeNum
, urlTypeNum
;
4487 SInt32 baseType
, urlType
;
4488 CFURLRef baseURL
= NULL
, url
;
4489 CFDictionaryRef dict
= (CFDictionaryRef
)pListRepresentation
;
4491 // Start by getting all the pieces and verifying they're of the correct type.
4492 if (CFGetTypeID(pListRepresentation
) != CFDictionaryGetTypeID()) {
4495 string
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLString"));
4496 if (!string
|| CFGetTypeID(string
) != CFStringGetTypeID()) {
4499 urlTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLStringType"));
4500 if (!urlTypeNum
|| CFGetTypeID(urlTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum
, kCFNumberSInt32Type
, &urlType
) || (urlType
!= FULL_URL_REPRESENTATION
&& urlType
!= kCFURLPOSIXPathStyle
&& urlType
!= kCFURLHFSPathStyle
&& urlType
!= kCFURLWindowsPathStyle
)) {
4503 baseString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseURLString"));
4505 if (CFGetTypeID(baseString
) != CFStringGetTypeID()) {
4508 baseTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseStringType"));
4509 if (!baseTypeNum
|| CFGetTypeID(baseTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum
, kCFNumberSInt32Type
, &baseType
) ||
4510 (baseType
!= FULL_URL_REPRESENTATION
&& baseType
!= kCFURLPOSIXPathStyle
&& baseType
!= kCFURLHFSPathStyle
&& baseType
!= kCFURLWindowsPathStyle
)) {
4513 if (baseType
== FULL_URL_REPRESENTATION
) {
4514 baseURL
= _CFURLCreateWithArbitraryString(alloc
, baseString
, NULL
);
4516 baseURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, baseString
, (CFURLPathStyle
)baseType
, CFStringGetCharacterAtIndex(baseString
, CFStringGetLength(baseString
)-1) == PATH_DELIM_FOR_TYPE(baseType
), NULL
);
4519 if (urlType
== FULL_URL_REPRESENTATION
) {
4520 url
= _CFURLCreateWithArbitraryString(alloc
, string
, baseURL
);
4522 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, string
, (CFURLPathStyle
)urlType
, CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
)-1) == PATH_DELIM_FOR_TYPE(urlType
), baseURL
);
4524 if (baseURL
) CFRelease(baseURL
);