2 * Copyright (c) 2009 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@
24 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
25 Responsibility: Becky Willrich
28 #include <CoreFoundation/CFURL.h>
29 #include <CoreFoundation/CFPriv.h>
30 #include <CoreFoundation/CFCharacterSetPriv.h>
31 #include <CoreFoundation/CFNumber.h>
32 #include "CFInternal.h"
33 #include <CoreFoundation/CFStringEncodingConverter.h>
34 #include <CoreFoundation/CFPriv.h>
39 #if DEPLOYMENT_TARGET_MACOSX
40 #include <CoreFoundation/CFNumberFormatter.h>
43 #include <sys/types.h>
44 #elif DEPLOYMENT_TARGET_EMBEDDED
47 #include <sys/types.h>
48 #elif DEPLOYMENT_TARGET_WINDOWS
50 #error Unknown or unspecified DEPLOYMENT_TARGET
53 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
54 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
56 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
57 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
58 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
);
59 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
60 CF_EXPORT CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
61 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
62 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
63 #elif DEPLOYMENT_TARGET_WINDOWS
65 #error Unknown or unspecified DEPLOYMENT_TARGET
70 #ifndef DEBUG_URL_MEMORY_USAGE
71 #define DEBUG_URL_MEMORY_USAGE 0
74 #if DEBUG_URL_MEMORY_USAGE
75 static CFAllocatorRef URLAllocator
= NULL
;
76 static UInt32 numFileURLsCreated
= 0;
77 static UInt32 numFileURLsConverted
= 0;
78 static UInt32 numFileURLsDealloced
= 0;
79 static UInt32 numURLs
= 0;
80 static UInt32 numDealloced
= 0;
81 static UInt32 numExtraDataAllocated
= 0;
82 static UInt32 numURLsWithBaseURL
= 0;
83 static UInt32 numNonUTF8EncodedURLs
= 0;
86 /* The bit flags in myURL->_flags */
87 #define HAS_SCHEME (0x0001)
88 #define HAS_USER (0x0002)
89 #define HAS_PASSWORD (0x0004)
90 #define HAS_HOST (0x0008)
91 #define HAS_PORT (0x0010)
92 #define HAS_PATH (0x0020)
93 #define HAS_PARAMETERS (0x0040)
94 #define HAS_QUERY (0x0080)
95 #define HAS_FRAGMENT (0x0100)
96 #define HAS_HTTP_SCHEME (0x0200)
97 // Last free bit (0x200) in lower word goes here!
98 #define IS_IPV6_ENCODED (0x0400)
99 #define IS_OLD_UTF8_STYLE (0x0800)
100 #define IS_DIRECTORY (0x1000)
101 #define IS_PARSED (0x2000)
102 #define IS_ABSOLUTE (0x4000)
103 #define IS_DECOMPOSABLE (0x8000)
105 #define PATH_TYPE_MASK (0x000F0000)
106 /* 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 */
107 #define POSIX_AND_URL_PATHS_MATCH (0x00100000)
108 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000)
110 /* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */
111 // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
112 // #define SCHEME_DIFFERS (0x00400000) unused
113 #define USER_DIFFERS (0x00800000)
114 #define PASSWORD_DIFFERS (0x01000000)
115 #define HOST_DIFFERS (0x02000000)
116 // 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
117 #define PORT_DIFFERS (0x04000000)
118 // #define PATH_DIFFERS (0x08000000) unused
119 // #define PARAMETERS_DIFFER (0x10000000) unused
120 // #define QUERY_DIFFERS (0x20000000) unused
121 #define PATH_HAS_FILE_ID (0x40000000)
122 #define HAS_FILE_SCHEME (0x80000000)
124 // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag
125 #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22)
127 // Other useful defines
128 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
129 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
130 #define FULL_URL_REPRESENTATION (0xF)
132 /* 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! */
133 #define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16)
134 #define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/'))
135 #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/")))
137 #define FILE_ID_PREFIX ".file"
138 #define FILE_ID_KEY "id"
140 #define ASSERT_CHECK_PATHSTYLE(x) 0
142 // In order to reduce the sizeof ( __CFURL ), move these items into a seperate structure which is
143 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
144 // either a sanitized string or a reserved pointer for URLHandle.
145 struct _CFURLAdditionalData
{
146 void *_reserved
; // Reserved for URLHandle's use.
147 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
148 CFHashCode hashValue
;
152 CFRuntimeBase _cfBase
;
154 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set.
155 CFStringRef _string
; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above)
158 struct _CFURLAdditionalData
* extra
;
159 void *_resourceInfo
; // For use by CarbonCore to cache property values. Retained and released by CFURL.
163 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
165 if ( url
&& url
->extra
)
166 return url
->extra
->_reserved
;
171 CF_INLINE CFMutableStringRef
_getSanitizedString ( const struct __CFURL
* url
)
173 if ( url
&& url
->extra
)
174 return url
->extra
->_sanitizedString
;
179 static void* _getResourceInfo ( const struct __CFURL
* url
)
182 return url
->_resourceInfo
;
188 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
190 if ( url
&& ! url
->extra
)
191 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), __kCFAllocatorGCScannedMemory
);
193 extra
->_reserved
= _getReserved( url
);
194 extra
->_sanitizedString
= _getSanitizedString( url
);
195 extra
->hashValue
= 0;
199 #if DEBUG_URL_MEMORY_USAGE
200 numExtraDataAllocated
++;
205 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
209 // Don't allocate extra space if we're just going to be storing NULL
210 if ( ! url
->extra
&& reserved
)
211 _CFURLAllocateExtraDataspace( url
);
214 __CFAssignWithWriteBarrier((void **)&url
->extra
->_reserved
, reserved
);
218 CF_INLINE
void _setSanitizedString ( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
222 // Don't allocate extra space if we're just going to be storing NULL
223 if ( ! url
->extra
&& sanitizedString
)
224 _CFURLAllocateExtraDataspace( url
);
227 url
->extra
->_sanitizedString
= sanitizedString
;
231 static void _setResourceInfo ( struct __CFURL
* url
, void* resourceInfo
)
235 if ( url
&& OSAtomicCompareAndSwapPtrBarrier( NULL
, resourceInfo
, &url
->_resourceInfo
)) {
236 CFRetain( resourceInfo
);
240 static void _convertToURLRepresentation(struct __CFURL
*url
);
241 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
);
242 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
243 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
244 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
245 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
246 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
247 static void computeSanitizedString(CFURLRef url
);
248 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
249 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
250 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
253 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
254 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->ranges
));
257 static Boolean _createOldUTF8StyleURLs
= false;
259 CF_INLINE Boolean
createOldUTF8StyleURLs(void) {
260 return (_createOldUTF8StyleURLs
);
263 // Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW
265 void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs
) {
266 _createOldUTF8StyleURLs
= createUTF8URLs
;
277 static const unsigned char sURLValidCharacters
[] = {
279 /* '!' 33 */ VALID
| UNRESERVED
| PATHVALID
,
282 /* '$' 36 */ VALID
| PATHVALID
,
284 /* '&' 38 */ VALID
| PATHVALID
,
285 /* ''' 39 */ VALID
| UNRESERVED
| PATHVALID
,
286 /* '(' 40 */ VALID
| UNRESERVED
| PATHVALID
,
287 /* ')' 41 */ VALID
| UNRESERVED
| PATHVALID
,
288 /* '*' 42 */ VALID
| UNRESERVED
| PATHVALID
,
289 /* '+' 43 */ VALID
| SCHEME
| PATHVALID
,
290 /* ',' 44 */ VALID
| PATHVALID
,
291 /* '-' 45 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
292 /* '.' 46 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
293 /* '/' 47 */ VALID
| PATHVALID
,
294 /* '0' 48 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
295 /* '1' 49 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
296 /* '2' 50 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
297 /* '3' 51 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
298 /* '4' 52 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
299 /* '5' 53 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
300 /* '6' 54 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
301 /* '7' 55 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
302 /* '8' 56 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
303 /* '9' 57 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
307 /* '=' 61 */ VALID
| PATHVALID
,
311 /* 'A' 65 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
312 /* 'B' 66 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
313 /* 'C' 67 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
314 /* 'D' 68 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
315 /* 'E' 69 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
316 /* 'F' 70 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
317 /* 'G' 71 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
318 /* 'H' 72 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
319 /* 'I' 73 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
320 /* 'J' 74 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
321 /* 'K' 75 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
322 /* 'L' 76 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
323 /* 'M' 77 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
324 /* 'N' 78 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
325 /* 'O' 79 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
326 /* 'P' 80 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
327 /* 'Q' 81 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
328 /* 'R' 82 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
329 /* 'S' 83 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
330 /* 'T' 84 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
331 /* 'U' 85 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
332 /* 'V' 86 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
333 /* 'W' 87 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
334 /* 'X' 88 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
335 /* 'Y' 89 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
336 /* 'Z' 90 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
341 /* '_' 95 */ VALID
| UNRESERVED
| PATHVALID
,
343 /* 'a' 97 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
344 /* 'b' 98 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
345 /* 'c' 99 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
346 /* 'd' 100 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
347 /* 'e' 101 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
348 /* 'f' 102 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
349 /* 'g' 103 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
350 /* 'h' 104 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
351 /* 'i' 105 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
352 /* 'j' 106 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
353 /* 'k' 107 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
354 /* 'l' 108 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
355 /* 'm' 109 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
356 /* 'n' 110 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
357 /* 'o' 111 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
358 /* 'p' 112 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
359 /* 'q' 113 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
360 /* 'r' 114 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
361 /* 's' 115 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
362 /* 't' 116 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
363 /* 'u' 117 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
364 /* 'v' 118 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
365 /* 'w' 119 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
366 /* 'x' 120 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
367 /* 'y' 121 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
368 /* 'z' 122 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
372 /* '~' 126 */ VALID
| UNRESERVED
| PATHVALID
,
376 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
377 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & VALID
) : false;
380 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
381 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & SCHEME
) : false;
384 // "Unreserved" as defined by RFC 2396
385 CF_INLINE Boolean
isUnreservedCharacter(UniChar ch
) {
386 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & UNRESERVED
) : false;
389 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
390 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & PATHVALID
) : false;
393 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
394 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & HEXDIGIT
) : false;
397 // Returns false if ch1 or ch2 isn't properly formatted
398 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
400 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
401 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
402 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
405 *result
= (*result
) << 4;
406 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
407 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
408 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
414 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
415 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
418 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
419 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
420 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
421 CFAllocatorRef alloc
= CFGetAllocator(array
);
422 CFMutableArrayRef mArray
= NULL
;
423 CFIndex i
, c
= CFArrayGetCount(array
);
424 for (i
= 0; i
< c
; i
++) {
425 CFStringRef origComp
= (CFStringRef
)CFArrayGetValueAtIndex(array
, i
);
426 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
427 if (!unescapedComp
) {
430 if (unescapedComp
!= origComp
) {
432 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
434 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
436 CFRelease(unescapedComp
);
439 if (mArray
) CFRelease(mArray
);
450 // 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.
451 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
452 if (preserveSlashes
) {
453 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?"), kCFStringEncodingUTF8
);
455 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
459 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
460 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
461 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
465 // 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
466 static Boolean
_hackToConvertSurrogates(UniChar highChar
, UniChar lowChar
, CFMutableStringRef str
) {
467 UniChar surrogate
[2];
468 uint8_t bytes
[6]; // Aki sez it should never take more than 6 bytes
471 surrogate
[0] = highChar
;
472 surrogate
[1] = lowChar
;
473 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &len
) != kCFStringEncodingConversionSuccess
) {
476 for (currByte
= bytes
; currByte
< bytes
+ len
; currByte
++) {
477 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
478 unsigned char high
, low
;
479 high
= ((*currByte
) & 0xf0) >> 4;
480 low
= (*currByte
) & 0x0f;
481 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
482 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
483 CFStringAppendCharacters(str
, escapeSequence
, 3);
488 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
489 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
490 uint8_t *bytePtr
= bytes
, *currByte
;
492 CFAllocatorRef alloc
= NULL
;
493 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
494 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
495 if (byteLength
<= 6) {
496 // The encoding cannot accomodate the character
499 alloc
= CFGetAllocator(str
);
500 bytePtr
= (uint8_t *)CFAllocatorAllocate(alloc
, byteLength
, 0);
501 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
502 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
506 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
507 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
508 unsigned char high
, low
;
509 high
= ((*currByte
) & 0xf0) >> 4;
510 low
= (*currByte
) & 0x0f;
511 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
512 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
513 CFStringAppendCharacters(str
, escapeSequence
, 3);
515 if (bytePtr
!= bytes
) {
516 CFAllocatorDeallocate(alloc
, bytePtr
);
521 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
522 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
523 CFMutableStringRef newStr
= NULL
;
526 CFRange percentRange
, searchRange
;
527 CFStringRef escapedStr
= NULL
;
528 CFMutableStringRef strForEscapedChar
= NULL
;
530 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
531 Boolean failed
= false;
533 if (!originalString
) return NULL
;
535 if (charactersToLeaveEscaped
== NULL
) {
536 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
539 length
= CFStringGetLength(originalString
);
540 searchRange
= CFRangeMake(0, length
);
542 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
543 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
544 uint8_t numBytesExpected
;
548 // Make sure we have at least 2 more characters
549 if (length
- percentRange
.location
< 3) { failed
= true; break; }
551 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
552 // so we assume the percent character is legit, and let it pass into the string
553 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
554 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
555 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
556 if (!(bytes
[0] & 0x80)) {
557 numBytesExpected
= 1;
558 } else if (!(bytes
[0] & 0x20)) {
559 numBytesExpected
= 2;
560 } else if (!(bytes
[0] & 0x10)) {
561 numBytesExpected
= 3;
563 numBytesExpected
= 4;
565 if (numBytesExpected
== 1) {
566 // one byte sequence (most common case); handle this specially
567 escapedChar
= bytes
[0];
568 if (!strForEscapedChar
) {
569 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
571 escapedStr
= strForEscapedChar
;
574 // Make sure up front that we have enough characters
575 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
576 for (j
= 1; j
< numBytesExpected
; j
++) {
577 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
578 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
579 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
580 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
583 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
584 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
587 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
588 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
589 escapedChar
= 0xfeff;
590 if (!strForEscapedChar
) {
591 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
593 CFRelease(escapedStr
);
594 escapedStr
= strForEscapedChar
;
599 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
600 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
601 searchRange
.length
= length
- searchRange
.location
;
604 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
605 if (escapedStr
!= strForEscapedChar
) {
606 CFRelease(escapedStr
);
614 newStr
= CFStringCreateMutable(alloc
, length
);
616 if (percentRange
.location
- mark
> 0) {
617 // The creation of this temporary string is unfortunate.
618 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
619 CFStringAppend(newStr
, substring
);
620 CFRelease(substring
);
622 CFStringAppend(newStr
, escapedStr
);
623 if (escapedStr
!= strForEscapedChar
) {
624 CFRelease(escapedStr
);
627 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
630 if (escapedStr
&& escapedStr
!= strForEscapedChar
) CFRelease(escapedStr
);
631 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
633 if (newStr
) CFRelease(newStr
);
637 // Need to cat on the remainder of the string
638 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
639 CFStringAppend(newStr
, substring
);
640 CFRelease(substring
);
644 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
649 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
650 if (enc
== kCFStringEncodingUTF8
) {
651 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
653 CFMutableStringRef newStr
= NULL
;
654 CFMutableStringRef escapedStr
= NULL
;
657 CFRange percentRange
, searchRange
;
658 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
659 Boolean failed
= false;
660 uint8_t byteBuffer
[8];
661 uint8_t *bytes
= byteBuffer
;
662 int capacityOfBytes
= 8;
664 if (!originalString
) return NULL
;
666 if (charactersToLeaveEscaped
== NULL
) {
667 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
670 length
= CFStringGetLength(originalString
);
671 searchRange
= CFRangeMake(0, length
);
673 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
675 CFIndex percentLoc
= percentRange
.location
;
676 CFStringRef convertedString
;
677 int numBytesUsed
= 0;
679 // Make sure we have at least 2 more characters
680 if (length
- percentLoc
< 3) { failed
= true; break; }
682 if (numBytesUsed
== capacityOfBytes
) {
683 if (bytes
== byteBuffer
) {
684 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
685 memmove(bytes
, byteBuffer
, capacityOfBytes
);
686 capacityOfBytes
= 16;
688 void *oldbytes
= bytes
;
689 int oldcap
= capacityOfBytes
;
690 capacityOfBytes
= 2*capacityOfBytes
;
691 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, capacityOfBytes
* sizeof(uint8_t), 0);
692 memmove(bytes
, oldbytes
, oldcap
);
693 CFAllocatorDeallocate(alloc
, oldbytes
);
697 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
699 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
701 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
703 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
704 searchRange
.location
= percentLoc
;
705 searchRange
.length
= length
- searchRange
.location
;
708 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
709 if (!convertedString
) {
715 newStr
= CFStringCreateMutable(alloc
, length
);
717 if (percentRange
.location
- mark
> 0) {
718 // The creation of this temporary string is unfortunate.
719 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
720 CFStringAppend(newStr
, substring
);
721 CFRelease(substring
);
725 CFStringAppend(newStr
, convertedString
);
726 CFRelease(convertedString
);
728 CFIndex i
, c
= CFStringGetLength(convertedString
);
730 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
732 for (i
= 0; i
< c
; i
++) {
733 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
734 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
735 CFStringAppendCharacters(newStr
, &ch1
, 1);
737 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
738 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
742 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
745 if (escapedStr
) CFRelease(escapedStr
);
746 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
748 if (newStr
) CFRelease(newStr
);
752 // Need to cat on the remainder of the string
753 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
754 CFStringAppend(newStr
, substring
);
755 CFRelease(substring
);
759 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
765 static CFStringRef
_addPercentEscapesToString(CFAllocatorRef allocator
, CFStringRef originalString
, Boolean (*shouldReplaceChar
)(UniChar
, void*), CFIndex (*handlePercentChar
)(CFIndex
, CFStringRef
, CFStringRef
*, void *), CFStringEncoding encoding
, void *context
) {
766 CFMutableStringRef newString
= NULL
;
768 CFStringInlineBuffer buf
;
770 if (!originalString
) return NULL
;
771 length
= CFStringGetLength(originalString
);
772 if (length
== 0) return (CFStringRef
)CFStringCreateCopy(allocator
, originalString
);
773 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
775 for (idx
= 0; idx
< length
; idx
++) {
776 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
777 Boolean shouldReplace
= shouldReplaceChar(ch
, context
);
779 // Perform the replacement
781 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
782 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
784 if (!_appendPercentEscapesForCharacter(ch
, encoding
, newString
)) {
785 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
786 if (encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1))) {
787 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
788 if (_hackToConvertSurrogates(ch
, CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1), newString
)) {
789 idx
++; // We consumed 2 characters, not 1
797 } else if (ch
== '%' && handlePercentChar
) {
798 CFStringRef replacementString
= NULL
;
799 CFIndex newIndex
= handlePercentChar(idx
, originalString
, &replacementString
, context
);
802 } else if (replacementString
) {
804 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
805 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
807 CFStringAppend(newString
, replacementString
);
808 CFRelease(replacementString
);
810 if (newIndex
== idx
) {
812 CFStringAppendCharacters(newString
, &ch
, 1);
815 if (!replacementString
&& newString
) {
817 for (tmpIndex
= idx
; tmpIndex
< newIndex
; tmpIndex
++) {
818 ch
= CFStringGetCharacterAtIndex(originalString
, idx
);
819 CFStringAppendCharacters(newString
, &ch
, 1);
824 } else if (newString
) {
825 CFStringAppendCharacters(newString
, &ch
, 1);
829 // Ran in to an encoding failure
830 if (newString
) CFRelease(newString
);
832 } else if (newString
) {
835 return (CFStringRef
)CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
840 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
841 CFIndex i
, c
= CFStringGetLength(string
);
842 CFStringInlineBuffer buf
;
843 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
844 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
848 static Boolean
_shouldPercentReplaceChar(UniChar ch
, void *context
) {
849 CFStringRef unescape
= ((CFStringRef
*)context
)[0];
850 CFStringRef escape
= ((CFStringRef
*)context
)[1];
851 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
853 if (unescape
&& _stringContainsCharacter(unescape
, ch
)) {
854 shouldReplace
= false;
856 } else if (escape
&& _stringContainsCharacter(escape
, ch
)) {
857 shouldReplace
= true;
859 return shouldReplace
;
862 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
863 CFStringRef strings
[2];
864 strings
[0] = charactersToLeaveUnescaped
;
865 strings
[1] = legalURLCharactersToBeEscaped
;
866 return _addPercentEscapesToString(allocator
, originalString
, _shouldPercentReplaceChar
, NULL
, encoding
, strings
);
871 static Boolean
__CFURLCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
872 CFURLRef url1
= (CFURLRef
)cf1
;
873 CFURLRef url2
= (CFURLRef
)cf2
;
874 UInt32 pathType1
, pathType2
;
876 __CFGenericValidateType(cf1
, CFURLGetTypeID());
877 __CFGenericValidateType(cf2
, CFURLGetTypeID());
879 if (url1
== url2
) return kCFCompareEqualTo
;
882 if (! url2
->_base
) return kCFCompareEqualTo
;
883 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
884 } else if ( url2
->_base
) {
888 pathType1
= URL_PATH_TYPE(url1
);
889 pathType2
= URL_PATH_TYPE(url2
);
890 if (pathType1
== pathType2
) {
891 if (pathType1
!= FULL_URL_REPRESENTATION
) {
892 return CFEqual(url1
->_string
, url2
->_string
);
894 // Do not compare the original strings; compare the sanatized strings.
895 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
898 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
899 CFStringRef scheme1
= CFURLCopyScheme(url1
);
900 CFStringRef scheme2
= CFURLCopyScheme(url2
);
902 if (scheme1
&& scheme2
) {
903 eq
= CFEqual(scheme1
, scheme2
);
906 } else if (!scheme1
&& !scheme2
) {
910 if (scheme1
) CFRelease(scheme1
);
911 else CFRelease(scheme2
);
913 if (!eq
) return false;
915 if (pathType1
== FULL_URL_REPRESENTATION
) {
916 if (!(url1
->_flags
& IS_PARSED
)) {
917 _parseComponentsOfURL(url1
);
919 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
924 if (pathType2
== FULL_URL_REPRESENTATION
) {
925 if (!(url2
->_flags
& IS_PARSED
)) {
926 _parseComponentsOfURL(url2
);
928 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
933 // No help for it; we now must convert to the canonical representation and compare.
934 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
939 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
940 CFURLRef url1
= (CFURLRef
)cf1
;
941 CFURLRef url2
= (CFURLRef
)cf2
;
942 UInt32 pathType1
, pathType2
;
944 __CFGenericValidateType(cf1
, CFURLGetTypeID());
945 __CFGenericValidateType(cf2
, CFURLGetTypeID());
947 if (url1
== url2
) return true;
948 if ((url1
->_flags
& IS_PARSED
) && (url2
->_flags
& IS_PARSED
) && (url1
->_flags
& IS_DIRECTORY
) != (url2
->_flags
& IS_DIRECTORY
)) return false;
950 if (! url2
->_base
) return false;
951 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
952 } else if ( url2
->_base
) {
956 pathType1
= URL_PATH_TYPE(url1
);
957 pathType2
= URL_PATH_TYPE(url2
);
958 if (pathType1
== pathType2
) {
959 if (pathType1
!= FULL_URL_REPRESENTATION
) {
960 return CFEqual(url1
->_string
, url2
->_string
);
962 // Do not compare the original strings; compare the sanatized strings.
963 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
966 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
967 CFStringRef scheme1
= CFURLCopyScheme(url1
);
968 CFStringRef scheme2
= CFURLCopyScheme(url2
);
970 if (scheme1
&& scheme2
) {
971 eq
= CFEqual(scheme1
, scheme2
);
974 } else if (!scheme1
&& !scheme2
) {
978 if (scheme1
) CFRelease(scheme1
);
979 else CFRelease(scheme2
);
981 if (!eq
) return false;
983 if (pathType1
== FULL_URL_REPRESENTATION
) {
984 if (!(url1
->_flags
& IS_PARSED
)) {
985 _parseComponentsOfURL(url1
);
987 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
992 if (pathType2
== FULL_URL_REPRESENTATION
) {
993 if (!(url2
->_flags
& IS_PARSED
)) {
994 _parseComponentsOfURL(url2
);
996 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
1001 // No help for it; we now must convert to the canonical representation and compare.
1002 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
1006 static CFHashCode
__CFURLHash(CFTypeRef cf
) {
1007 /* 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. */
1008 struct __CFURL
* url
= (struct __CFURL
*)cf
;
1009 CFHashCode result
= 0;
1013 // Allocate our extra space if it isn't already allocated
1014 if ( url
&& ! url
->extra
)
1015 _CFURLAllocateExtraDataspace( url
);
1018 result
= url
->extra
->hashValue
;
1021 if (CFURLCanBeDecomposed(url
)) {
1022 CFStringRef lastComp
= CFURLCopyLastPathComponent(url
);
1023 CFStringRef hostNameRef
= CFURLCopyHostName(url
);
1028 result
= CFHash(lastComp
);
1029 CFRelease(lastComp
);
1032 if ( hostNameRef
) {
1033 result
^= CFHash( hostNameRef
);
1034 CFRelease( hostNameRef
);
1037 result
= CFHash(CFURLGetString(url
));
1040 if ( ! result
) // never store a 0 value for the hashed value
1043 url
->extra
->hashValue
= result
;
1051 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
1052 CFURLRef url
= (CFURLRef
)cf
;
1053 __CFGenericValidateType(cf
, CFURLGetTypeID());
1055 CFRetain(url
->_string
);
1056 return url
->_string
;
1058 // Do not dereference url->_base; it may be an ObjC object
1059 return CFStringCreateWithFormat(CFGetAllocator(url
), NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
1064 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
1065 CFURLRef url
= (CFURLRef
)cf
;
1067 CFAllocatorRef alloc
= CFGetAllocator(url
);
1069 CFStringRef baseString
= CFCopyDescription(url
->_base
);
1070 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
);
1071 CFRelease(baseString
);
1073 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
);
1078 #if DEBUG_URL_MEMORY_USAGE
1080 extern __attribute((used
)) void __CFURLDumpMemRecord(void) {
1081 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
);
1084 // if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator);
1088 static void __CFURLDeallocate(CFTypeRef cf
) {
1089 CFURLRef url
= (CFURLRef
)cf
;
1090 CFAllocatorRef alloc
;
1091 __CFGenericValidateType(cf
, CFURLGetTypeID());
1092 alloc
= CFGetAllocator(url
);
1093 #if DEBUG_URL_MEMORY_USAGE
1095 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
1096 numFileURLsDealloced
++;
1099 if (url
->_string
) CFRelease(url
->_string
); // GC: 3879914
1100 if (url
->_base
) CFRelease(url
->_base
);
1101 if (url
->ranges
) CFAllocatorDeallocate(alloc
, url
->ranges
);
1102 if (_getSanitizedString(url
)) CFRelease(_getSanitizedString(url
));
1103 if ( url
->extra
!= NULL
) CFAllocatorDeallocate( alloc
, url
->extra
);
1104 if (_getResourceInfo(url
)) CFRelease(_getResourceInfo(url
));
1107 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
1109 static const CFRuntimeClass __CFURLClass
= {
1117 __CFURLCopyFormattingDescription
,
1118 __CFURLCopyDescription
1121 // When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but
1122 // when it is defined, we must prefix with static to prevent the string from being exported
1123 #ifdef __CONSTANT_CFSTRINGS__
1124 static CONST_STRING_DECL(kCFURLFileScheme
, "file")
1125 static CONST_STRING_DECL(kCFURLDataScheme
, "data")
1126 static CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1127 static CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1129 CONST_STRING_DECL(kCFURLFileScheme
, "file")
1130 CONST_STRING_DECL(kCFURLDataScheme
, "data")
1131 CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1132 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1134 __private_extern__
void __CFURLInitialize(void) {
1135 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1138 /* Toll-free bridging support; get the true CFURL from an NSURL */
1139 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1140 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, url
, "_cfurl");
1144 CFTypeID
CFURLGetTypeID(void) {
1145 return __kCFURLTypeID
;
1148 __private_extern__
void CFShowURL(CFURLRef url
) {
1150 fprintf(stdout
, "(null)\n");
1153 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1154 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1155 fprintf(stdout
, "ObjC bridged object}\n");
1158 fprintf(stdout
, "\n\tPath type: ");
1159 switch (URL_PATH_TYPE(url
)) {
1160 case kCFURLPOSIXPathStyle
:
1161 fprintf(stdout
, "POSIX");
1163 case kCFURLHFSPathStyle
:
1164 fprintf(stdout
, "HFS");
1166 case kCFURLWindowsPathStyle
:
1167 fprintf(stdout
, "NTFS");
1169 case FULL_URL_REPRESENTATION
:
1170 fprintf(stdout
, "Native URL");
1173 fprintf(stdout
, "UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url
));
1175 fprintf(stdout
, "\n\tRelative string: ");
1176 CFShow(url
->_string
);
1177 fprintf(stdout
, "\tBase URL: ");
1179 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1182 fprintf(stdout
, "(null)\n");
1184 fprintf(stdout
, "\tFlags: 0x%x\n}\n", (unsigned int)url
->_flags
);
1188 /***************************************************/
1189 /* URL creation and String/Data creation from URLS */
1190 /***************************************************/
1191 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, const char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1192 CFIndex neededLength
;
1196 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1200 *freeCharacters
= false;
1204 *ustring
= CFStringGetCharactersPtr(string
);
1206 *useCString
= false;
1207 *freeCharacters
= false;
1211 *freeCharacters
= true;
1212 length
= CFStringGetLength(string
);
1213 rg
= CFRangeMake(0, length
);
1214 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1215 if (neededLength
== length
) {
1216 char *buf
= (char *)CFAllocatorAllocate(alloc
, length
, 0);
1217 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, (uint8_t *)buf
, length
, NULL
);
1221 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1222 CFStringGetCharacters(string
, rg
, buf
);
1223 *useCString
= false;
1228 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1229 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1231 /* 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. */
1233 CFIndex idx
, base_idx
= 0;
1234 CFIndex string_length
;
1235 UInt32 flags
= (IS_PARSED
| *theFlags
);
1236 Boolean useCString
, freeCharacters
, isCompliant
;
1237 uint8_t numRanges
= 0;
1238 const char *cstring
= NULL
;
1239 const UniChar
*ustring
= NULL
;
1241 string_length
= CFStringGetLength(string
);
1242 constructBuffers(alloc
, string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1244 // Algorithm is as described in RFC 1808
1245 // 1: parse the fragment; remainder after left-most "#" is fragment
1246 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1247 if ('#' == STRING_CHAR(idx
)) {
1248 flags
|= HAS_FRAGMENT
;
1249 ranges
[8].location
= idx
+ 1;
1250 ranges
[8].length
= string_length
- (idx
+ 1);
1252 string_length
= idx
; // remove fragment from parse string
1256 // 2: parse the scheme
1257 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1258 UniChar ch
= STRING_CHAR(idx
);
1260 flags
|= HAS_SCHEME
;
1261 flags
|= IS_ABSOLUTE
;
1262 ranges
[0].location
= base_idx
;
1263 ranges
[0].length
= idx
;
1266 // optimization for http urls
1267 if (idx
== 4 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' && STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p') {
1268 flags
|= HAS_HTTP_SCHEME
;
1270 // optimization for file urls
1271 if (idx
== 4 && STRING_CHAR(0) == 'f' && STRING_CHAR(1) == 'i' && STRING_CHAR(2) == 'l' && STRING_CHAR(3) == 'e') {
1272 flags
|= HAS_FILE_SCHEME
;
1275 } else if (!scheme_valid(ch
)) {
1276 break; // invalid scheme character -- no scheme
1280 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1281 // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage
1282 // expects this to be treated identically to "scheme://" - REW, 12/08/03
1283 if (!(flags
& HAS_SCHEME
)) {
1285 } else if (base_idx
== string_length
) {
1286 isCompliant
= false;
1287 } else if (STRING_CHAR(base_idx
) != '/') {
1288 isCompliant
= false;
1294 // Clear the fragment flag if it's been set
1295 if (flags
& HAS_FRAGMENT
) {
1296 flags
&= (~HAS_FRAGMENT
);
1297 string_length
= CFStringGetLength(string
);
1299 (*theFlags
) = flags
;
1300 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
1301 (*range
)->location
= ranges
[0].location
;
1302 (*range
)->length
= ranges
[0].length
;
1304 if (freeCharacters
) {
1305 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1309 // URL is 1808-compliant
1310 flags
|= IS_DECOMPOSABLE
;
1312 // 3: parse the network location and login
1313 if (2 <= (string_length
- base_idx
) && '/' == STRING_CHAR(base_idx
) && '/' == STRING_CHAR(base_idx
+1)) {
1314 CFIndex base
= 2 + base_idx
, extent
;
1315 for (idx
= base
; idx
< string_length
; idx
++) {
1316 if ('/' == STRING_CHAR(idx
) || '?' == STRING_CHAR(idx
)) break;
1320 // net_loc parts extend from base to extent (but not including), which might be to end of string
1321 // net location is "<user>:<password>@<host>:<port>"
1322 if (extent
!= base
) {
1323 for (idx
= base
; idx
< extent
; idx
++) {
1324 if ('@' == STRING_CHAR(idx
)) { // there is a user
1328 ranges
[1].location
= base
; // base of the user
1329 for (idx2
= base
; idx2
< idx
; idx2
++) {
1330 if (':' == STRING_CHAR(idx2
)) { // found a password separator
1331 flags
|= HAS_PASSWORD
;
1333 ranges
[2].location
= idx2
+1; // base of the password
1334 ranges
[2].length
= idx
-(idx2
+1); // password extent
1335 ranges
[1].length
= idx2
- base
; // user extent
1339 if (!(flags
& HAS_PASSWORD
)) {
1340 // user extends to the '@'
1341 ranges
[1].length
= idx
- base
; // user extent
1349 ranges
[3].location
= base
; // base of host
1351 // base has been advanced past the user and password if they existed
1352 for (idx
= base
; idx
< extent
; idx
++) {
1353 // IPV6 support (RFC 2732) DCJ June/10/2002
1354 if ('[' == STRING_CHAR(idx
)) { // starting IPV6 explicit address
1355 // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end
1356 for ( ; idx
< extent
; ++ idx
) {
1357 if ( ']' == STRING_CHAR(idx
)) {
1358 flags
|= IS_IPV6_ENCODED
;
1363 // there is a port if we see a colon. Only the last one is the port, though.
1364 else if ( ':' == STRING_CHAR(idx
)) {
1367 ranges
[4].location
= idx
+1; // base of port
1368 ranges
[4].length
= extent
- (idx
+1); // port extent
1369 ranges
[3].length
= idx
- base
; // host extent
1373 if (!(flags
& HAS_PORT
)) {
1374 ranges
[3].length
= extent
- base
; // host extent
1380 // 4: parse the query; remainder after left-most "?" is query
1381 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1382 if ('?' == STRING_CHAR(idx
)) {
1385 ranges
[7].location
= idx
+ 1;
1386 ranges
[7].length
= string_length
- (idx
+1);
1387 string_length
= idx
; // remove query from parse string
1392 // 5: parse the parameters; remainder after left-most ";" is parameters
1393 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1394 if (';' == STRING_CHAR(idx
)) {
1395 flags
|= HAS_PARAMETERS
;
1397 ranges
[6].location
= idx
+ 1;
1398 ranges
[6].length
= string_length
- (idx
+1);
1399 string_length
= idx
; // remove parameters from parse string
1404 // 6: parse the path; it's whatever's left between string_length & base_idx
1405 if (string_length
- base_idx
!= 0 || (flags
& NET_LOCATION_MASK
))
1407 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1413 pathRg
.location
= base_idx
;
1414 pathRg
.length
= string_length
- base_idx
;
1417 if (pathRg
.length
> 0) {
1418 Boolean sawPercent
= FALSE
;
1419 for (idx
= pathRg
.location
; idx
< string_length
; idx
++) {
1420 if ('%' == STRING_CHAR(idx
)) {
1425 #if DEPLOYMENT_TARGET_MACOSX
1426 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) == '/') {
1427 flags
|= PATH_HAS_FILE_ID
;
1428 } else if (!sawPercent
) {
1429 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1431 #elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
1433 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1436 #error Unknown or unspecified DEPLOYMENT_TARGET
1439 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 1);
1442 } else if (ch
== '.') {
1443 if (pathRg
.length
== 1) {
1446 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 2);
1449 } else if (ch
!= '.') {
1451 } else if (pathRg
.length
== 2) {
1454 isDir
= (STRING_CHAR(pathRg
.location
+ pathRg
.length
- 3) == '/');
1461 isDir
= (baseURL
!= NULL
) ? CFURLHasDirectoryPath(baseURL
) : false;
1464 flags
|= IS_DIRECTORY
;
1468 if (freeCharacters
) {
1469 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1471 (*theFlags
) = flags
;
1472 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
)*numRanges
, 0);
1474 for (idx
= 0, flags
= 1; flags
!= (1<<9); flags
= (flags
<<1), idx
++) {
1475 if ((*theFlags
) & flags
) {
1476 (*range
)[numRanges
] = ranges
[idx
];
1482 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
) {
1484 Boolean sawIllegalChar
= false;
1485 for (idx
= base
; idx
< end
; idx
++) {
1486 Boolean shouldEscape
;
1487 UniChar ch
= STRING_CHAR(idx
);
1488 if (isURLLegalCharacter(ch
)) {
1489 if ((componentFlag
== HAS_USER
|| componentFlag
== HAS_PASSWORD
) && (ch
== '/' || ch
== '?' || ch
== '@')) {
1490 shouldEscape
= true;
1492 shouldEscape
= false;
1494 } else if (ch
== '%' && idx
+ 2 < end
&& isHexDigit(STRING_CHAR(idx
+ 1)) && isHexDigit(STRING_CHAR(idx
+2))) {
1495 shouldEscape
= false;
1496 } else if (componentFlag
== HAS_HOST
&& ((idx
== base
&& ch
== '[') || (idx
== end
-1 && ch
== ']'))) {
1497 shouldEscape
= false;
1499 shouldEscape
= true;
1501 if (!shouldEscape
) continue;
1503 sawIllegalChar
= true;
1504 if (componentFlag
&& flags
) {
1505 *flags
|= (componentFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
);
1507 if (!*escapedString
) {
1508 *escapedString
= CFStringCreateMutable(alloc
, 0);
1511 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[*mark
]), idx
- *mark
, kCFStringEncodingISOLatin1
, false);
1512 CFStringAppend(*escapedString
, tempString
);
1513 CFRelease(tempString
);
1515 CFStringAppendCharacters(*escapedString
, &(ustring
[*mark
]), idx
- *mark
);
1518 _appendPercentEscapesForCharacter(ch
, encoding
, *escapedString
); // This can never fail because anURL->_string was constructed from the encoding passed in
1520 return sawIllegalChar
;
1523 static void computeSanitizedString(CFURLRef url
) {
1524 CFAllocatorRef alloc
= CFGetAllocator(url
);
1525 CFIndex string_length
= CFStringGetLength(url
->_string
);
1526 Boolean useCString
, freeCharacters
;
1527 const char *cstring
= NULL
;
1528 const UniChar
*ustring
= NULL
;
1529 CFIndex base
; // where to scan from
1530 CFIndex mark
; // first character not-yet copied to sanitized string
1531 if (!(url
->_flags
& IS_PARSED
)) {
1532 _parseComponentsOfURL(url
);
1534 constructBuffers(alloc
, url
->_string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1535 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1536 // Impossible to have a problem character in the scheme
1537 CFMutableStringRef sanitizedString
= NULL
;
1538 base
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
).length
+ 1;
1540 if (!scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1541 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1543 if ( sanitizedString
) {
1544 _setSanitizedString( (struct __CFURL
*) url
, sanitizedString
);
1547 // Go component by component
1548 CFIndex currentComponent
= HAS_USER
;
1549 CFMutableStringRef sanitizedString
= NULL
;
1551 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1552 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->ranges
, currentComponent
);
1553 if (componentRange
.location
!= kCFNotFound
) {
1554 scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1556 currentComponent
= currentComponent
<< 1;
1558 if (sanitizedString
) {
1559 _setSanitizedString((struct __CFURL
*)url
, sanitizedString
);
1561 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1564 if (_getSanitizedString(url
) && mark
!= string_length
) {
1566 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1567 CFStringAppend(_getSanitizedString(url
), tempString
);
1568 CFRelease(tempString
);
1570 CFStringAppendCharacters(_getSanitizedString(url
), &(ustring
[mark
]), string_length
- mark
);
1573 if (freeCharacters
) {
1574 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1579 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1580 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1581 CFIndex string_length
= CFStringGetLength(comp
);
1582 Boolean useCString
, freeCharacters
;
1583 const char *cstring
= NULL
;
1584 const UniChar
*ustring
= NULL
;
1585 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1586 CFMutableStringRef result
= NULL
;
1588 constructBuffers(alloc
, comp
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1589 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1591 if (mark
< string_length
) {
1593 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1594 CFStringAppend(result
, tempString
);
1595 CFRelease(tempString
);
1597 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1601 // This should nevr happen
1603 result
= (CFMutableStringRef
)comp
;
1605 if (freeCharacters
) {
1606 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1612 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1613 struct __CFURL
*url
;
1614 #if DEBUG_URL_MEMORY_USAGE
1616 // if (!URLAllocator) {
1617 // URLAllocator = CFCountingAllocatorCreate(NULL);
1619 allocator
= URLAllocator
;
1621 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1624 if (createOldUTF8StyleURLs()) {
1625 url
->_flags
|= IS_OLD_UTF8_STYLE
;
1627 url
->_string
= NULL
;
1630 // url->_reserved = NULL;
1631 url
->_encoding
= kCFStringEncodingUTF8
;
1632 // url->_sanatizedString = NULL;
1638 // 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.
1639 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, UInt32 fsType
, CFURLRef base
) {
1640 CFAssert2((fsType
== FULL_URL_REPRESENTATION
) || (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) || (fsType
== kCFURLHFSPathStyle
) || ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__
, fsType
);
1642 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1643 url
->_string
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1644 url
->_flags
|= (fsType
<< 16);
1646 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1648 #if DEBUG_URL_MEMORY_USAGE
1649 if (fsType
!= FULL_URL_REPRESENTATION
) {
1650 numFileURLsCreated
++;
1653 numURLsWithBaseURL
++;
1657 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1658 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1659 CFIndex len
= CFStringGetLength(path
);
1660 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1661 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1662 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1664 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1665 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1669 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1670 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1672 #elif DEPLOYMENT_TARGET_WINDOWS
1673 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1674 CFIndex len
= CFStringGetLength(path
);
1675 UniChar firstChar
= 0 < len
? CFStringGetCharacterAtIndex(path
, 0) : 0;
1676 UniChar secondChar
= 1 < len
? CFStringGetCharacterAtIndex(path
, 1) : 0;
1677 Boolean isDrive
= ('A' <= firstChar
&& firstChar
<= 'Z') || ('a' <= firstChar
&& firstChar
<= 'z');
1678 isDrive
= isDrive
&& (secondChar
== ':' || secondChar
== '|');
1679 if (isDrive
|| (firstChar
== '\\' && secondChar
== '\\')) {
1680 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLWindowsPathStyle
, NULL
);
1681 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1682 } else if (firstChar
== '/') {
1683 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1684 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1686 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1687 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1691 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1692 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1695 #error Unknown or unspecified DEPLOYMENT_TARGET
1698 // Exported for Foundation's use
1699 CF_EXPORT Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1700 // 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.
1701 // 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
1702 CFStringInlineBuffer stringBuffer
;
1703 CFIndex idx
= 0, length
;
1704 Boolean sawHash
= false;
1706 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1709 length
= CFStringGetLength(string
);
1710 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1711 while (idx
< length
) {
1712 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1715 // Make sure that two valid hex digits follow a '%' character
1717 if ( idx
+ 2 > length
)
1719 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1720 idx
= -1; // To guarantee index < length, and our failure case is triggered
1724 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1726 if (! isHexDigit(ch
) ) {
1727 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1731 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1733 if (! isHexDigit(ch
) ) {
1734 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1741 if (ch
== '[' || ch
== ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1747 if ( isURLLegalCharacter( ch
) )
1757 CF_EXPORT
void _CFURLInitWithString(CFURLRef myURL
, CFStringRef string
, CFURLRef baseURL
) {
1758 struct __CFURL
*url
= (struct __CFURL
*)myURL
; // Supress annoying compile warnings
1759 Boolean isAbsolute
= false;
1760 CFRange colon
= CFStringFind(string
, CFSTR(":"), 0);
1761 if (colon
.location
!= kCFNotFound
) {
1764 for (i
= 0; i
< colon
.location
; i
++) {
1765 char ch
= (char)CFStringGetCharacterAtIndex(string
, i
);
1766 if (!scheme_valid(ch
)) {
1772 _CFURLInit(url
, string
, FULL_URL_REPRESENTATION
, isAbsolute
? NULL
: baseURL
);
1774 url
->_flags
|= IS_ABSOLUTE
;
1778 struct __CFURLEncodingTranslationParameters
{
1779 CFStringEncoding fromEnc
;
1780 CFStringEncoding toEnc
;
1781 const UniChar
*addlChars
;
1783 Boolean escapeHighBit
;
1784 Boolean escapePercents
;
1785 Boolean agreesOverASCII
;
1786 Boolean encodingsMatch
;
1789 static Boolean
_shouldEscapeForEncodingConversion(UniChar ch
, void *context
) {
1790 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1791 if (info
->escapeHighBit
&& ch
> 0x7F) {
1793 } else if (ch
== '%' && info
->escapePercents
) {
1795 } else if (info
->addlChars
) {
1796 const UniChar
*escChar
= info
->addlChars
;
1798 for (i
= 0; i
< info
->count
; escChar
++, i
++) {
1799 if (*escChar
== ch
) {
1807 static CFIndex
_convertEscapeSequence(CFIndex percentIndex
, CFStringRef urlString
, CFStringRef
*newString
, void *context
) {
1808 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1809 CFMutableDataRef newData
;
1810 Boolean sawNonASCIICharacter
= false;
1811 CFIndex i
= percentIndex
;
1814 if (info
->encodingsMatch
) return percentIndex
+ 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well
1815 newData
= CFDataCreateMutable(CFGetAllocator(urlString
), 0);
1816 length
= CFStringGetLength(urlString
);
1818 while (i
< length
&& CFStringGetCharacterAtIndex(urlString
, i
) == '%') {
1820 if (i
+2 >= length
|| !_translateBytes(CFStringGetCharacterAtIndex(urlString
, i
+1), CFStringGetCharacterAtIndex(urlString
, i
+2), &byte
)) {
1824 if (byte
> 0x7f) sawNonASCIICharacter
= true;
1825 CFDataAppendBytes(newData
, &byte
, 1);
1828 if (!sawNonASCIICharacter
&& info
->agreesOverASCII
) {
1831 CFStringRef tmp
= CFStringCreateWithBytes(CFGetAllocator(urlString
), CFDataGetBytePtr(newData
), CFDataGetLength(newData
), info
->fromEnc
, false);
1832 CFIndex tmpIndex
, tmpLen
;
1837 tmpLen
= CFStringGetLength(tmp
);
1838 *newString
= CFStringCreateMutable(CFGetAllocator(urlString
), 0);
1839 for (tmpIndex
= 0; tmpIndex
< tmpLen
; tmpIndex
++) {
1840 if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp
, tmpIndex
), info
->toEnc
, (CFMutableStringRef
)(*newString
))) {
1846 if (tmpIndex
< tmpLen
) {
1847 CFRelease(*newString
);
1856 /* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */
1857 static CFStringRef
_convertPercentEscapes(CFStringRef urlString
, CFStringEncoding fromEncoding
, CFStringEncoding toEncoding
, Boolean escapeAllHighBitCharacters
, Boolean escapePercents
, const UniChar
*addlCharsToEscape
, int numAddlChars
) {
1858 struct __CFURLEncodingTranslationParameters context
;
1859 context
.fromEnc
= fromEncoding
;
1860 context
.toEnc
= toEncoding
;
1861 context
.addlChars
= addlCharsToEscape
;
1862 context
.count
= numAddlChars
;
1863 context
.escapeHighBit
= escapeAllHighBitCharacters
;
1864 context
.escapePercents
= escapePercents
;
1865 context
.agreesOverASCII
= (__CFStringEncodingIsSupersetOfASCII(toEncoding
) && __CFStringEncodingIsSupersetOfASCII(fromEncoding
)) ? true : false;
1866 context
.encodingsMatch
= (fromEncoding
== toEncoding
) ? true : false;
1867 return _addPercentEscapesToString(CFGetAllocator(urlString
), urlString
, _shouldEscapeForEncodingConversion
, _convertEscapeSequence
, toEncoding
, &context
);
1870 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1871 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
1872 CFStringRef urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, encoding
, false);
1874 if (!urlString
|| CFStringGetLength(urlString
) == 0) {
1875 if (urlString
) CFRelease(urlString
);
1878 if (createOldUTF8StyleURLs()) {
1879 if (encoding
!= kCFStringEncodingUTF8
) {
1880 CFStringRef tmp
= _convertPercentEscapes(urlString
, encoding
, kCFStringEncodingUTF8
, false, false, NULL
, 0);
1881 CFRelease(urlString
);
1883 if (!urlString
) return NULL
;
1887 result
= _CFURLAlloc(allocator
);
1889 _CFURLInitWithString(result
, urlString
, baseURL
);
1890 if (encoding
!= kCFStringEncodingUTF8
&& !createOldUTF8StyleURLs()) {
1891 ((struct __CFURL
*)result
)->_encoding
= encoding
;
1892 #if DEBUG_URL_MEMORY_USAGE
1893 if ( encoding
!= kCFStringEncodingUTF8
) {
1894 numNonUTF8EncodedURLs
++;
1899 CFRelease(urlString
); // it's retained by result, now.
1903 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
1904 static const UniChar whitespaceChars
[4] = {' ', '\n', '\r', '\t'};
1905 CFStringRef myStr
= CFURLGetString(url
);
1908 if (url
->_flags
& IS_OLD_UTF8_STYLE
) {
1909 newStr
= (encoding
== kCFStringEncodingUTF8
) ? (CFStringRef
)CFRetain(myStr
) : _convertPercentEscapes(myStr
, kCFStringEncodingUTF8
, encoding
, true, false, escapeWhitespace
? whitespaceChars
: NULL
, escapeWhitespace
? 4 : 0);
1914 result
= CFStringCreateExternalRepresentation(allocator
, newStr
, encoding
, 0);
1919 // Any escape sequences in URLString will be interpreted via UTF-8.
1920 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1922 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1923 if (!_CFStringIsLegalURLString(URLString
)) return NULL
;
1924 url
= _CFURLAlloc(allocator
);
1926 _CFURLInitWithString(url
, URLString
, baseURL
);
1931 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1933 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1934 url
= _CFURLAlloc(allocator
);
1936 _CFURLInitWithString(url
, URLString
, baseURL
);
1941 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
1942 CFStringRef relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
1943 if (!relativeString
) {
1946 if (!useCompatibilityMode
) {
1947 CFURLRef url
= _CFURLCreateWithArbitraryString(alloc
, relativeString
, baseURL
);
1948 CFRelease(relativeString
);
1950 ((struct __CFURL
*)url
)->_encoding
= encoding
;
1951 CFURLRef absURL
= CFURLCopyAbsoluteURL(url
);
1952 #if DEBUG_URL_MEMORY_USAGE
1953 if ( encoding
!= kCFStringEncodingUTF8
) {
1954 numNonUTF8EncodedURLs
++;
1963 UInt32 absFlags
= 0;
1965 CFStringRef absString
= NULL
;
1966 Boolean absStringIsMutable
= false;
1969 absString
= relativeString
;
1971 UniChar ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
1972 if (ch
== '?' || ch
== ';' || ch
== '#') {
1973 // Nothing but parameter + query + fragment; append to the baseURL string
1974 CFStringRef baseString
;
1975 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1976 baseString
= CFURLGetString(baseURL
);
1978 baseString
= baseURL
->_string
;
1980 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
1981 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
1982 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
1983 absStringIsMutable
= true;
1985 UInt32 relFlags
= 0;
1987 CFStringRef relString
= NULL
;
1988 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
1989 if (relFlags
& HAS_SCHEME
) {
1990 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
1991 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
1992 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
1993 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
1994 CFAllocatorDeallocate(alloc
, relRanges
);
1996 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
1998 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
1999 CFRetain(relativeString
);
2000 absString
= relativeString
;
2002 if (baseScheme
) CFRelease(baseScheme
);
2004 CFRetain(relativeString
);
2005 relString
= relativeString
;
2008 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2009 if (!(baseURL
->_flags
& IS_PARSED
)) {
2010 _parseComponentsOfURL(baseURL
);
2012 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->ranges
);
2014 CFStringRef baseString
;
2015 UInt32 baseFlags
= 0;
2016 CFRange
*baseRanges
;
2017 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2018 baseString
= CFURLGetString(baseURL
);
2020 baseString
= baseURL
->_string
;
2022 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2023 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
2024 CFAllocatorDeallocate(alloc
, baseRanges
);
2026 absStringIsMutable
= true;
2028 if (relString
) CFRelease(relString
);
2029 CFAllocatorDeallocate(alloc
, relRanges
);
2031 CFRelease(relativeString
);
2033 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
2034 if (absFlags
& HAS_PATH
) {
2035 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
2036 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
2037 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), 0);
2038 CFStringRef newPath
;
2039 CFStringGetCharacters(absString
, pathRg
, buf
);
2040 buf
[pathRg
.length
] = '\0';
2041 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
2042 if (CFStringGetLength(newPath
) != pathRg
.length
) {
2043 if (!absStringIsMutable
) {
2044 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
2045 CFRelease(absString
);
2048 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
2051 // Do not deallocate buf; newPath took ownership of it.
2053 CFAllocatorDeallocate(alloc
, absRanges
);
2054 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
2055 CFRelease(absString
);
2057 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
2058 #if DEBUG_URL_MEMORY_USAGE
2059 if ( encoding
!= kCFStringEncodingUTF8
) {
2060 numNonUTF8EncodedURLs
++;
2068 /* 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 */
2069 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
2070 UniChar
*idx
= pathStr
;
2074 if (idx
!= pathStr
) {
2079 } else if (*(idx
+1) == pathDelimiter
) {
2080 if (idx
+ 2 != end
|| idx
!= pathStr
) {
2081 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
2085 // Do not delete the sole path component
2088 } else if (( end
-idx
>= 2 ) && *(idx
+1) == '.' && (idx
+2 == end
|| (( end
-idx
> 2 ) && *(idx
+2) == pathDelimiter
))) {
2089 if (idx
- pathStr
>= 2) {
2090 // 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.
2091 UniChar
*lastDelim
= idx
-2;
2092 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
2094 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
2095 // We have a genuine component to compact out
2097 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
2098 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2099 end
-= (idx
+ 3 - lastDelim
);
2102 } else if (lastDelim
!= pathStr
) {
2107 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
2115 } else if (stripLeadingDotDots
) {
2116 if (idx
+ 3 != end
) {
2117 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
2118 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2122 // Do not devolve the last path component
2128 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
2131 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
2134 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
- pathStr
, alloc
);
2137 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
2138 CFMutableStringRef newString
= CFStringCreateMutable(alloc
, 0);
2139 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
2140 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
2143 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
2144 if (rg
.location
!= kCFNotFound
) {
2145 CFStringGetCharacters(baseString
, rg
, buf
);
2146 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2147 CFStringAppendCString(newString
, ":", kCFStringEncodingASCII
);
2150 if (relFlags
& NET_LOCATION_MASK
) {
2151 CFStringAppend(newString
, relString
);
2153 CFStringAppendCString(newString
, "//", kCFStringEncodingASCII
);
2154 rg
= _netLocationRange(baseFlags
, baseRanges
);
2155 if (rg
.location
!= kCFNotFound
) {
2156 CFStringGetCharacters(baseString
, rg
, buf
);
2157 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2160 if (relFlags
& HAS_PATH
) {
2161 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
2162 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2163 CFStringRef newPath
;
2164 Boolean useRelPath
= false;
2165 Boolean useBasePath
= false;
2166 if (basePathRg
.location
== kCFNotFound
) {
2168 } else if (relPathRg
.length
== 0) {
2170 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
2172 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2176 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2177 } else if (useBasePath
) {
2178 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2180 // #warning FIXME - Get rid of this allocation
2181 UniChar
*newPathBuf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
2183 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
2184 idx
= newPathBuf
+ basePathRg
.length
- 1;
2185 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
2186 if (*idx
== '/') idx
++;
2187 CFStringGetCharacters(relString
, relPathRg
, idx
);
2188 end
= idx
+ relPathRg
.length
;
2190 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2192 /* Under Win32 absolute path can begin with letter
2193 * so we have to add one '/' to the newString
2196 // No - the input strings here are URL path strings, not Win32 paths.
2197 // Absolute paths should have had a '/' prepended before this point.
2198 // I have removed Sergey Zubarev's change and left his comment (and
2199 // this one) as a record. - REW, 1/5/2004
2201 // if the relative URL does not begin with a slash and
2202 // the base does not end with a slash, add a slash
2203 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2204 CFStringAppendCString(newString
, "/", kCFStringEncodingASCII
);
2207 CFStringAppend(newString
, newPath
);
2209 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2210 rg
.length
= CFStringGetLength(relString
);
2211 if (rg
.length
> rg
.location
) {
2212 rg
.length
-= rg
.location
;
2213 CFStringGetCharacters(relString
, rg
, buf
);
2214 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2217 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2218 if (rg
.location
!= kCFNotFound
) {
2219 CFStringGetCharacters(baseString
, rg
, buf
);
2220 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2223 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2224 // ??? Can this ever happen?
2225 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2227 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2228 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2229 rg
.location
--; // To pick up the separator
2231 CFStringGetCharacters(baseString
, rg
, buf
);
2232 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2234 } else if (relFlags
& HAS_PARAMETERS
) {
2235 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2236 rg
.location
--; // To get the semicolon that starts the parameters
2237 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2238 CFStringGetCharacters(relString
, rg
, buf
);
2239 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2241 // Sigh; we have to resolve these against one another
2242 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2243 if (rg
.location
!= kCFNotFound
) {
2244 CFStringAppendCString(newString
, ";", kCFStringEncodingASCII
);
2245 CFStringGetCharacters(baseString
, rg
, buf
);
2246 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2248 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2249 if (rg
.location
!= kCFNotFound
) {
2250 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2251 CFStringGetCharacters(relString
, rg
, buf
);
2252 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2254 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2255 if (rg
.location
!= kCFNotFound
) {
2256 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2257 CFStringGetCharacters(baseString
, rg
, buf
);
2258 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2261 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2262 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2263 if (rg
.location
!= kCFNotFound
) {
2264 CFStringAppendCString(newString
, "#", kCFStringEncodingASCII
);
2265 CFStringGetCharacters(relString
, rg
, buf
);
2266 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2271 CFAllocatorDeallocate(alloc
, buf
);
2275 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2276 CFURLRef anURL
, base
;
2277 CFURLPathStyle fsType
;
2278 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2279 CFStringRef baseString
, newString
;
2281 CFRange
*baseRanges
;
2284 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2285 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2286 CF_OBJC_CALL0(CFURLRef
, anURL
, relativeURL
, "absoluteURL");
2287 if (anURL
) CFRetain(anURL
);
2291 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2293 base
= relativeURL
->_base
;
2295 return (CFURLRef
)CFRetain(relativeURL
);
2297 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2298 fsType
= URL_PATH_TYPE(relativeURL
);
2300 if (!baseIsObjC
&& fsType
!= FULL_URL_REPRESENTATION
&& fsType
== URL_PATH_TYPE(base
)) {
2301 return _CFURLCopyAbsoluteFileURL(relativeURL
);
2303 if (fsType
!= FULL_URL_REPRESENTATION
) {
2304 _convertToURLRepresentation((struct __CFURL
*)relativeURL
);
2305 fsType
= FULL_URL_REPRESENTATION
;
2307 if (!(relativeURL
->_flags
& IS_PARSED
)) {
2308 _parseComponentsOfURL(relativeURL
);
2310 if ((relativeURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) && !(relativeURL
->_flags
& (RESOURCE_SPECIFIER_MASK
| NET_LOCATION_MASK
)) && !baseIsObjC
&& (URL_PATH_TYPE(base
) == kCFURLPOSIXPathStyle
)) {
2311 // There's nothing to relativeURL's string except the path
2312 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, CFURLHasDirectoryPath(base
), kCFURLPOSIXPathStyle
, alloc
);
2313 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, kCFURLPOSIXPathStyle
, CFURLHasDirectoryPath(relativeURL
));
2319 CFURLPathStyle baseType
= URL_PATH_TYPE(base
);
2320 if (baseType
!= FULL_URL_REPRESENTATION
) {
2321 _convertToURLRepresentation((struct __CFURL
*)base
);
2322 } else if (!(base
->_flags
& IS_PARSED
)) {
2323 _parseComponentsOfURL(base
);
2325 baseString
= base
->_string
;
2326 baseFlags
= base
->_flags
;
2327 baseRanges
= base
->ranges
;
2329 baseString
= CFURLGetString(base
);
2332 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2335 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->ranges
, baseString
, baseFlags
, baseRanges
);
2337 CFAllocatorDeallocate(alloc
, baseRanges
);
2339 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2340 CFRelease(newString
);
2341 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2342 #if DEBUG_URL_MEMORY_USAGE
2343 if ( relativeURL
->_encoding
!= kCFStringEncodingUTF8
) {
2344 numNonUTF8EncodedURLs
++;
2351 /*******************/
2352 /* Basic accessors */
2353 /*******************/
2354 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2355 return url
->_encoding
;
2358 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2359 anURL
= _CFURLFromNSURL(anURL
);
2360 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) return true;
2361 if (!(anURL
->_flags
& IS_PARSED
)) {
2362 _parseComponentsOfURL(anURL
);
2364 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2367 CFStringRef
CFURLGetString(CFURLRef url
) {
2368 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFStringRef
, url
, "relativeString");
2369 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2370 if (url
->_base
&& (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2371 return url
->_string
;
2373 _convertToURLRepresentation((struct __CFURL
*)url
);
2375 if (!_haveTestedOriginalString(url
)) {
2376 computeSanitizedString(url
);
2378 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2379 return url
->_string
;
2381 return _getSanitizedString( url
);
2385 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2386 CFIndex length
, charsConverted
, usedLength
;
2388 CFStringEncoding enc
;
2389 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2390 string
= CFURLGetString(url
);
2391 enc
= kCFStringEncodingUTF8
;
2393 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2394 _convertToURLRepresentation((struct __CFURL
*)url
);
2396 string
= url
->_string
;
2397 enc
= url
->_encoding
;
2399 length
= CFStringGetLength(string
);
2400 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2401 if (charsConverted
!= length
) {
2408 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2409 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, anURL
, "baseURL");
2410 return anURL
->_base
;
2413 // Assumes the URL is already parsed
2414 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2416 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2417 while (!(compFlag
& 1)) {
2418 compFlag
= compFlag
>> 1;
2427 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2430 CFAllocatorRef alloc
= CFGetAllocator(url
);
2431 CFAssert1(URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
, __kCFLogAssertion
, "%s(): passed a file system URL", __PRETTY_FUNCTION__
);
2432 if (removePercentEscapes
) fromOriginalString
= true;
2433 if (!(url
->_flags
& IS_PARSED
)) {
2434 _parseComponentsOfURL(url
);
2436 rg
= _rangeForComponent(url
->_flags
, url
->ranges
, compFlag
);
2437 if (rg
.location
== kCFNotFound
) return NULL
;
2438 if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_HTTP_SCHEME
) {
2439 comp
= kCFURLHTTPScheme
;
2441 } else if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_FILE_SCHEME
) {
2442 comp
= kCFURLFileScheme
;
2445 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2447 if (!fromOriginalString
) {
2448 if (!_haveTestedOriginalString(url
)) {
2449 computeSanitizedString(url
);
2451 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (url
->_flags
& (compFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
))) {
2452 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2457 if (removePercentEscapes
) {
2459 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
2460 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2462 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2470 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2472 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2473 CF_OBJC_CALL0(CFStringRef
, scheme
, anURL
, "scheme");
2474 if (scheme
) CFRetain(scheme
);
2477 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2479 return CFURLCopyScheme(anURL
->_base
);
2481 CFRetain(kCFURLFileScheme
); // because caller will release it
2482 return kCFURLFileScheme
;
2485 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_HTTP_SCHEME
) {
2486 CFRetain(kCFURLHTTPScheme
);
2487 return kCFURLHTTPScheme
;
2489 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_FILE_SCHEME
) {
2490 CFRetain(kCFURLFileScheme
);
2491 return kCFURLFileScheme
;
2493 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2496 } else if (anURL
->_base
) {
2497 return CFURLCopyScheme(anURL
->_base
);
2503 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2505 CFRange netRg
= {kCFNotFound
, 0};
2508 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2510 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2511 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2512 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2513 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2514 for (i
= 0; i
< c
; i
++) {
2515 if (netRgs
[i
].location
== kCFNotFound
) continue;
2516 if (netRg
.location
== kCFNotFound
) {
2519 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2525 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2526 anURL
= _CFURLFromNSURL(anURL
);
2527 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2528 // !!! This won't work if we go to putting the vol ref num in the net location for HFS
2530 return CFURLCopyNetLocation(anURL
->_base
);
2532 CFRetain(kCFURLLocalhost
);
2533 return kCFURLLocalhost
;
2536 if (!(anURL
->_flags
& IS_PARSED
)) {
2537 _parseComponentsOfURL(anURL
);
2539 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2540 // We provide the net location
2541 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->ranges
);
2543 if (!_haveTestedOriginalString(anURL
)) {
2544 computeSanitizedString(anURL
);
2546 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (anURL
->_flags
& (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2547 // 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.
2549 netRg
.length
= CFStringGetLength( _getSanitizedString(anURL
)) - netRg
.location
;
2550 if (CFStringFindWithOptions(_getSanitizedString(anURL
), CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2551 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2553 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), netRg
);
2555 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2558 } else if (anURL
->_base
) {
2559 return CFURLCopyNetLocation(anURL
->_base
);
2565 // 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.
2566 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
2567 anURL
= _CFURLFromNSURL(anURL
);
2568 if (URL_PATH_TYPE(anURL
) == kCFURLPOSIXPathStyle
&& (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2569 CFRetain(anURL
->_string
);
2570 return anURL
->_string
;
2572 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2573 _convertToURLRepresentation((struct __CFURL
*)anURL
);
2575 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
2578 /* 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.
2580 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.
2582 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
2583 CFStringRef path
= CFURLCopyPath(anURL
);
2584 if (!path
|| CFStringGetLength(path
) == 0) {
2585 if (path
) CFRelease(path
);
2586 if (isAbsolute
) *isAbsolute
= false;
2589 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
2591 if (isAbsolute
) *isAbsolute
= true;
2592 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
2596 if (isAbsolute
) *isAbsolute
= false;
2601 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
2602 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2603 if (URL_PATH_TYPE(anURL
) == FULL_URL_REPRESENTATION
) {
2604 if (!(anURL
->_flags
& IS_PARSED
)) {
2605 _parseComponentsOfURL(anURL
);
2607 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
2608 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2610 return CFURLHasDirectoryPath(anURL
->_base
);
2612 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2615 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
2616 UInt32 firstRsrcSpecFlag
= 0;
2617 UInt32 flag
= HAS_FRAGMENT
;
2618 while (flag
!= HAS_PATH
) {
2620 firstRsrcSpecFlag
= flag
;
2624 return firstRsrcSpecFlag
;
2627 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
2628 anURL
= _CFURLFromNSURL(anURL
);
2629 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2630 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2633 if (!(anURL
->_flags
& IS_PARSED
)) {
2634 _parseComponentsOfURL(anURL
);
2636 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
2637 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, HAS_SCHEME
);
2638 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
2639 if (!_haveTestedOriginalString(anURL
)) {
2640 computeSanitizedString(anURL
);
2642 if (_getSanitizedString(anURL
)) {
2643 // 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.
2644 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), CFRangeMake(base
, CFStringGetLength(_getSanitizedString(anURL
))-base
));
2646 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
2649 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
2651 if (firstRsrcSpecFlag
) {
2652 Boolean canUseOriginalString
= true;
2653 Boolean canUseSanitizedString
= true;
2654 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
2655 if (!_haveTestedOriginalString(anURL
)) {
2656 computeSanitizedString(anURL
);
2658 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
2659 // See if any pieces in the resource specifier differ between sanitized string and original string
2660 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
2661 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2662 canUseOriginalString
= false;
2667 if (!canUseOriginalString
) {
2668 // 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.
2669 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
2670 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2671 canUseSanitizedString
= false;
2676 if (canUseOriginalString
) {
2677 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2678 rg
.location
--; // Include the character that demarcates the component
2679 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
2680 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
2681 } else if (canUseSanitizedString
) {
2682 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2683 rg
.location
--; // Include the character that demarcates the component
2684 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2685 return CFStringCreateWithSubstring(alloc
, _getSanitizedString(anURL
), rg
);
2687 // Must compute the correct string to return; just reparse....
2688 UInt32 sanFlags
= 0;
2689 CFRange
*sanRanges
= NULL
;
2691 _parseComponents(alloc
, _getSanitizedString(anURL
), anURL
->_base
, &sanFlags
, &sanRanges
);
2692 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
2693 CFAllocatorDeallocate(alloc
, sanRanges
);
2694 rg
.location
--; // Include the character that demarcates the component
2695 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2696 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), rg
);
2699 // The resource specifier cannot possibly come from the base.
2705 /*************************************/
2706 /* Accessors that create new objects */
2707 /*************************************/
2709 // 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).
2710 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
2712 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2713 CF_OBJC_CALL0(CFStringRef
, tmp
, anURL
, "host");
2714 if (tmp
) CFRetain(tmp
);
2717 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2718 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2720 return CFURLCopyHostName(anURL
->_base
);
2722 CFRetain(kCFURLLocalhost
);
2723 return kCFURLLocalhost
;
2726 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
2728 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
2729 // Have to strip off the brackets to get the true hostname.
2730 // Assume that to be legal the first and last characters are brackets!
2731 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
2736 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2737 return CFURLCopyHostName(anURL
->_base
);
2743 // Return -1 to indicate no port is specified
2744 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
2746 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2748 CF_OBJC_CALL0(CFNumberRef
, cfPort
, anURL
, "port");
2750 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
2753 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2754 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2756 return CFURLGetPortNumber(anURL
->_base
);
2760 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
2762 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
2763 CFStringInlineBuffer buf
;
2764 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
2766 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
2771 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2772 return CFURLGetPortNumber(anURL
->_base
);
2778 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
2780 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2781 CF_OBJC_CALL0(CFStringRef
, user
, anURL
, "user");
2782 if (user
) CFRetain(user
);
2785 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2786 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2788 return CFURLCopyUserName(anURL
->_base
);
2792 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
2795 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2796 return CFURLCopyUserName(anURL
->_base
);
2802 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
2804 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2805 CF_OBJC_CALL0(CFStringRef
, passwd
, anURL
, "password");
2806 if (passwd
) CFRetain(passwd
);
2809 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2810 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2812 return CFURLCopyPassword(anURL
->_base
);
2816 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
2819 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2820 return CFURLCopyPassword(anURL
->_base
);
2826 // 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
2828 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
2830 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2831 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "parameterString");
2832 if (str
) CFRetain(str
);
2835 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2836 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2839 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
2840 if (str
) return str
;
2841 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2842 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
2844 // Parameter string definitely coming from the relative portion of the URL
2846 return _unescapedParameterString( anURL
->_base
);
2849 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2850 CFStringRef param
= _unescapedParameterString(anURL
);
2853 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2854 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
2856 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
2864 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
2866 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2867 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "query");
2868 if (str
) CFRetain(str
);
2871 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2872 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2875 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
2876 if (str
) return str
;
2877 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2878 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
2881 return _unescapedQueryString(anURL
->_base
);
2884 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2885 CFStringRef query
= _unescapedQueryString(anURL
);
2888 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2889 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
2891 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
2899 // Fragments are NEVER taken from a base URL
2900 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
2902 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2903 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "fragment");
2904 if (str
) CFRetain(str
);
2907 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2908 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2911 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
2915 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2916 CFStringRef fragment
= _unescapedFragment(anURL
);
2919 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2920 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
2922 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
2924 CFRelease(fragment
);
2930 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
2931 CFIndex firstMaskFlag
= 1;
2932 CFIndex lastComponentBeforeMask
= 0;
2933 while (firstMaskFlag
<= HAS_FRAGMENT
) {
2934 if (firstMaskFlag
& mask
) break;
2935 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
2936 firstMaskFlag
= firstMaskFlag
<< 1;
2938 if (lastComponentBeforeMask
== 0) {
2939 // mask includes HAS_SCHEME
2941 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
2942 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
2943 // case file:/path/immediately/without/host
2944 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2945 CFRange pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
2946 if (schemeRg
.length
+ 1 == pathRg
.location
) {
2947 return schemeRg
.length
+ 1;
2949 return schemeRg
.length
+ 3;
2952 // For all other components, the separator precedes the component, so there's no need
2953 // to add extra chars to get to the next insertion point
2954 CFRange rg
= _rangeForComponent(url
->_flags
, url
->ranges
, lastComponentBeforeMask
);
2955 return rg
.location
+ rg
.length
;
2959 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
2960 CFOptionFlags currentOption
;
2961 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
2962 Boolean haveReachedMask
= false;
2963 CFIndex beforeMask
= 0;
2964 CFIndex afterMask
= kCFNotFound
;
2965 CFRange
*currRange
= url
->ranges
;
2966 CFRange maskRange
= {kCFNotFound
, 0};
2967 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
2968 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
2969 firstMaskFlag
= currentOption
;
2970 haveReachedMask
= true;
2972 if (!(url
->_flags
& currentOption
)) continue;
2973 if (!haveReachedMask
) {
2974 beforeMask
= currRange
->location
+ currRange
->length
;
2975 } else if (currentOption
<= mask
) {
2976 if (maskRange
.location
== kCFNotFound
) {
2977 maskRange
= *currRange
;
2979 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
2982 afterMask
= currRange
->location
;
2987 if (afterMask
== kCFNotFound
) {
2988 afterMask
= maskRange
.location
+ maskRange
.length
;
2990 charRangeWithSeparators
->location
= beforeMask
;
2991 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
2995 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2997 switch (component
) {
2998 case kCFURLComponentScheme
:
3001 case kCFURLComponentNetLocation
:
3002 mask
= NET_LOCATION_MASK
;
3004 case kCFURLComponentPath
:
3007 case kCFURLComponentResourceSpecifier
:
3008 mask
= RESOURCE_SPECIFIER_MASK
;
3010 case kCFURLComponentUser
:
3013 case kCFURLComponentPassword
:
3014 mask
= HAS_PASSWORD
;
3016 case kCFURLComponentUserInfo
:
3017 mask
= HAS_USER
| HAS_PASSWORD
;
3019 case kCFURLComponentHost
:
3022 case kCFURLComponentPort
:
3025 case kCFURLComponentParameterString
:
3026 mask
= HAS_PARAMETERS
;
3028 case kCFURLComponentQuery
:
3031 case kCFURLComponentFragment
:
3032 mask
= HAS_FRAGMENT
;
3035 rangeIncludingSeparators
->location
= kCFNotFound
;
3036 rangeIncludingSeparators
->length
= 0;
3037 return CFRangeMake(kCFNotFound
, 0);
3040 if ((url
->_flags
& mask
) == 0) {
3041 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
3042 rangeIncludingSeparators
->length
= 0;
3043 return CFRangeMake(kCFNotFound
, 0);
3045 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
3049 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3050 if (component
== kCFURLComponentScheme
) {
3051 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
3052 rangeIncludingSeparators
->location
= 0;
3053 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
3055 } else if (component
== kCFURLComponentResourceSpecifier
) {
3056 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
3057 CFIndex stringLength
= CFStringGetLength(url
->_string
);
3058 if (schemeRg
.length
+ 1 == stringLength
) {
3059 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
3060 rangeIncludingSeparators
->length
= 0;
3061 return CFRangeMake(kCFNotFound
, 0);
3063 rangeIncludingSeparators
->location
= schemeRg
.length
;
3064 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
3065 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
3068 rangeIncludingSeparators
->location
= kCFNotFound
;
3069 rangeIncludingSeparators
->length
= 0;
3070 return CFRangeMake(kCFNotFound
, 0);
3075 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3076 CFRange charRange
, charRangeWithSeparators
;
3078 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
3079 url
= _CFURLFromNSURL(url
);
3080 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
3081 _convertToURLRepresentation((struct __CFURL
*)url
);
3083 if (!(url
->_flags
& IS_PARSED
)) {
3084 _parseComponentsOfURL(url
);
3087 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
3088 // Special-case this because non-decomposable URLs have a slightly strange flags setup
3089 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
3091 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
3094 if (charRangeWithSeparators
.location
== kCFNotFound
) {
3095 if (rangeIncludingSeparators
) {
3096 rangeIncludingSeparators
->location
= kCFNotFound
;
3097 rangeIncludingSeparators
->length
= 0;
3099 return CFRangeMake(kCFNotFound
, 0);
3100 } else if (rangeIncludingSeparators
) {
3101 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
3103 if (charRange
.location
== kCFNotFound
) {
3104 byteRange
= charRange
;
3105 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
3107 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
3108 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
3110 if (charRangeWithSeparators
.location
== charRange
.location
) {
3111 byteRange
.location
= rangeIncludingSeparators
->location
;
3114 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3115 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
3117 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3118 if (maxCharRangeWithSeparators
== maxCharRange
) {
3119 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
3123 rg
.location
= maxCharRange
;
3124 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
3125 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3126 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
3129 } else if (charRange
.location
== kCFNotFound
) {
3130 byteRange
= charRange
;
3132 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
3133 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3138 /* Component support */
3140 /* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */
3141 static CFStringRef
schemeSpecificString(CFURLRef url
) {
3143 isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3144 switch (URL_PATH_TYPE(url
)) {
3145 case kCFURLPOSIXPathStyle
:
3146 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3147 return (CFStringRef
)CFRetain(url
->_string
);
3149 return POSIXPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3151 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3152 case kCFURLHFSPathStyle
:
3153 return HFSPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3154 #elif DEPLOYMENT_TARGET_WINDOWS
3156 #error Unknown or unspecified DEPLOYMENT_TARGET
3158 case kCFURLWindowsPathStyle
:
3159 return WindowsPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3160 case FULL_URL_REPRESENTATION
:
3161 return CFURLCopyResourceSpecifier(url
);
3167 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
3168 if ( CFURLGetBaseURL(url
) != NULL
) {
3169 components
->scheme
= NULL
;
3171 components
->scheme
= CFURLCopyScheme(url
);
3173 components
->schemeSpecific
= schemeSpecificString(url
);
3177 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
3179 if (components
->scheme
) {
3181 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
3182 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
3183 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
3184 } else if (components
->schemeSpecific
) {
3185 str
= components
->schemeSpecific
;
3191 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3199 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3200 CFAllocatorRef alloc
= CFGetAllocator(url
);
3202 static CFStringRef emptyStr
= NULL
;
3204 emptyStr
= CFSTR("");
3207 if (!CFURLCanBeDecomposed(url
)) {
3210 if ((pathType
= URL_PATH_TYPE(url
)) == FULL_URL_REPRESENTATION
) {
3211 CFStringRef path
= CFURLCopyPath(url
);
3213 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3216 components
->pathComponents
= NULL
;
3218 components
->baseURL
= CFURLGetBaseURL(url
);
3219 if (components
->baseURL
) {
3220 CFRetain(components
->baseURL
);
3221 components
->scheme
= NULL
;
3223 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
3225 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
3226 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3227 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3228 if (url
->_flags
& HAS_PORT
) {
3229 components
->port
= CFURLGetPortNumber(url
);
3231 components
->port
= kCFNotFound
;
3233 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3234 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3235 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3238 case kCFURLPOSIXPathStyle
: {
3239 CFStringRef pathStr
;
3240 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3241 pathStr
= url
->_string
;
3244 pathStr
= POSIXPathToURLPath(url
->_string
, alloc
, url
->_flags
& IS_DIRECTORY
);
3246 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, pathStr
, CFSTR("/"));
3250 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3251 case kCFURLHFSPathStyle
:
3252 components
->pathComponents
= HFSPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3254 #elif DEPLOYMENT_TARGET_WINDOWS
3256 #error Unknown or unspecified DEPLOYMENT_TARGET
3258 case kCFURLWindowsPathStyle
:
3259 components
->pathComponents
= WindowsPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3262 components
->pathComponents
= NULL
;
3264 if (!components
->pathComponents
) {
3267 components
->scheme
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
3268 components
->user
= NULL
;
3269 components
->password
= NULL
;
3270 components
->host
= (CFStringRef
)CFRetain(kCFURLLocalhost
);
3271 components
->port
= kCFNotFound
;
3272 components
->parameterString
= NULL
;
3273 components
->query
= NULL
;
3274 components
->fragment
= NULL
;
3275 components
->baseURL
= CFURLGetBaseURL(url
);
3276 if (components
->baseURL
) CFRetain(components
->baseURL
);
3281 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3282 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3283 CFURLRef base
= comp
->baseURL
;
3285 Boolean hadPrePathComponent
= false;
3288 CFStringAppend(urlString
, comp
->scheme
);
3289 CFStringAppend(urlString
, CFSTR("://"));
3290 hadPrePathComponent
= true;
3292 if (comp
->user
|| comp
->password
) {
3294 CFStringAppend(urlString
, comp
->user
);
3296 if (comp
->password
) {
3297 CFStringAppend(urlString
, CFSTR(":"));
3298 CFStringAppend(urlString
, comp
->password
);
3300 CFStringAppend(urlString
, CFSTR("@"));
3301 hadPrePathComponent
= true;
3304 CFStringAppend(urlString
, comp
->host
);
3305 hadPrePathComponent
= true;
3307 if (comp
->port
!= kCFNotFound
) {
3308 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3309 hadPrePathComponent
= true;
3312 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFArrayGetCount( comp
->pathComponents
) == 0 || CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3313 CFStringAppend(urlString
, CFSTR("/"));
3315 if (comp
->pathComponents
) {
3316 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3317 CFStringAppend(urlString
, pathStr
);
3320 if (comp
->parameterString
) {
3321 CFStringAppend(urlString
, CFSTR(";"));
3322 CFStringAppend(urlString
, comp
->parameterString
);
3325 CFStringAppend(urlString
, CFSTR("?"));
3326 CFStringAppend(urlString
, comp
->query
);
3328 if (comp
->fragment
) {
3329 CFStringAppend(urlString
, CFSTR("#"));
3330 CFStringAppend(urlString
, comp
->fragment
);
3332 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3333 CFRelease(urlString
);
3337 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3338 CFAllocatorRef alloc
= CFGetAllocator(url
);
3339 CFURLComponentsRFC1808 oldComp
;
3341 if (!decomposeToRFC1808(url
, &oldComp
)) {
3344 comp
->scheme
= oldComp
.scheme
;
3346 if (oldComp
.password
) {
3347 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3348 CFRelease(oldComp
.password
);
3349 CFRelease(oldComp
.user
);
3351 comp
->userinfo
= oldComp
.user
;
3354 comp
->userinfo
= NULL
;
3356 comp
->host
= oldComp
.host
;
3357 comp
->port
= oldComp
.port
;
3358 if (!oldComp
.parameterString
) {
3359 comp
->pathComponents
= oldComp
.pathComponents
;
3361 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3362 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3363 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3364 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3366 CFRelease(oldComp
.pathComponents
);
3367 CFRelease(oldComp
.parameterString
);
3369 comp
->query
= oldComp
.query
;
3370 comp
->fragment
= oldComp
.fragment
;
3371 comp
->baseURL
= oldComp
.baseURL
;
3375 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3376 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3377 CFURLRef base
= comp
->baseURL
;
3379 Boolean hadPrePathComponent
= false;
3382 CFStringAppend(urlString
, comp
->scheme
);
3383 CFStringAppend(urlString
, CFSTR("://"));
3384 hadPrePathComponent
= true;
3386 if (comp
->userinfo
) {
3387 CFStringAppend(urlString
, comp
->userinfo
);
3388 CFStringAppend(urlString
, CFSTR("@"));
3389 hadPrePathComponent
= true;
3392 CFStringAppend(urlString
, comp
->host
);
3393 if (comp
->port
!= kCFNotFound
) {
3394 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3396 hadPrePathComponent
= true;
3398 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3399 CFStringAppend(urlString
, CFSTR("/"));
3401 if (comp
->pathComponents
) {
3402 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3403 CFStringAppend(urlString
, pathStr
);
3407 CFStringAppend(urlString
, CFSTR("?"));
3408 CFStringAppend(urlString
, comp
->query
);
3410 if (comp
->fragment
) {
3411 CFStringAppend(urlString
, CFSTR("#"));
3412 CFStringAppend(urlString
, comp
->fragment
);
3414 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3415 CFRelease(urlString
);
3419 #undef CFURLCopyComponents
3420 #undef CFURLCreateFromComponents
3423 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3424 url
= _CFURLFromNSURL(url
);
3425 switch (decompositionType
) {
3426 case kCFURLComponentDecompositionNonHierarchical
:
3427 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3428 case kCFURLComponentDecompositionRFC1808
:
3429 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3430 case kCFURLComponentDecompositionRFC2396
:
3431 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3438 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3439 switch (decompositionType
) {
3440 case kCFURLComponentDecompositionNonHierarchical
:
3441 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3442 case kCFURLComponentDecompositionRFC1808
:
3443 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3444 case kCFURLComponentDecompositionRFC2396
:
3445 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3451 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3452 return _getReserved(url
);
3455 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3456 _setReserved ( (struct __CFURL
*) url
, ptr
);
3459 CF_EXPORT
void *__CFURLResourceInfoPtr(CFURLRef url
) {
3460 return _getResourceInfo(url
);
3463 CF_EXPORT
void __CFURLSetResourceInfoPtr(CFURLRef url
, void *ptr
) {
3464 _setResourceInfo ( (struct __CFURL
*) url
, ptr
);
3467 /* File system stuff */
3469 /* HFSPath<->URLPath functions at the bottom of the file */
3470 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3472 CFMutableArrayRef urlComponents
= NULL
;
3475 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3476 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3479 CFStringRef str
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, 0);
3480 if (CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3481 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR("")); // So we get a leading '/' below
3482 i
= 2; // Skip over the drive letter and the empty string we just inserted
3485 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3486 CFStringRef fileComp
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
,i
);
3487 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3489 // Couldn't decode fileComp
3490 CFRelease(urlComponents
);
3493 if (urlComp
!= fileComp
) {
3494 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3500 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3501 CFArrayAppendValue(urlComponents
, CFSTR(""));
3503 return urlComponents
;
3506 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3507 CFArrayRef urlComponents
;
3510 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3511 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
);
3512 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3514 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3515 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3516 CFRelease(urlComponents
);
3520 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
) {
3521 CFStringRef pathString
= _replacePathIllegalCharacters(path
, alloc
, true);
3522 if (isDirectory
&& CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
)-1) != '/') {
3523 CFStringRef tmp
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@/"), pathString
);
3524 CFRelease(pathString
);
3530 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3531 // This is the easiest case; just remove the percent escape codes and we're done
3532 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3534 CFIndex length
= CFStringGetLength(result
);
3535 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3536 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3545 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3546 // Check for a drive letter, then flip all the slashes
3548 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
3549 SInt32 count
= CFArrayGetCount(tmp
);
3550 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
3551 CFStringRef newPath
;
3556 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
3557 CFArrayRemoveValueAtIndex(components
, count
-1);
3561 if (count
> 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
, 0)) == 0) {
3562 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
3563 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, (CFStringRef
)CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
3567 if (firstComponent
) {
3568 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
3570 CFArrayRemoveValueAtIndex(components
, 0);
3572 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
3573 CFArraySetValueAtIndex(components
, 0, driveStr
);
3574 CFRelease(driveStr
);
3577 CFRelease(firstComponent
);
3581 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
3582 CFRelease(components
);
3583 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
3590 // converts url from a file system path representation to a standard representation
3591 static void _convertToURLRepresentation(struct __CFURL
*url
) {
3592 CFStringRef path
= NULL
;
3593 Boolean isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3594 Boolean isFileReferencePath
= false;
3595 CFAllocatorRef alloc
= CFGetAllocator(url
);
3597 #if DEBUG_URL_MEMORY_USAGE
3598 numFileURLsConverted
++;
3601 switch (URL_PATH_TYPE(url
)) {
3602 case kCFURLPOSIXPathStyle
:
3603 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3604 path
= (CFStringRef
)CFRetain(url
->_string
);
3606 path
= POSIXPathToURLPath(url
->_string
, alloc
, isDir
);
3609 case kCFURLWindowsPathStyle
:
3610 path
= WindowsPathToURLPath(url
->_string
, alloc
, isDir
);
3613 CFAssert2(path
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, url
);
3615 CFMutableStringRef str
= CFStringCreateMutable(alloc
, 0);
3616 CFStringAppend(str
, isFileReferencePath
? CFSTR("file://") : CFSTR("file://localhost"));
3617 CFStringAppend(str
, path
);
3618 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 );
3619 CFRelease(url
->_string
);
3621 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
3622 url
->ranges
[0] = CFRangeMake(0, 4);
3623 url
->ranges
[1] = CFRangeMake(7, isFileReferencePath
? 0 : 9);
3624 url
->ranges
[2] = CFRangeMake(url
->ranges
[1].location
+ url
->ranges
[1].length
, CFStringGetLength(path
));
3627 CFRelease(url
->_string
);
3628 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_PARSED
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3629 url
->_string
= path
;
3630 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
3631 *(url
->ranges
) = CFRangeMake(0, CFStringGetLength(path
));
3635 // relativeURL is known to be a file system URL whose base is a matching file system URL
3636 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
) {
3637 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
3638 CFURLPathStyle fsType
= URL_PATH_TYPE(relativeURL
);
3639 CFURLRef base
= relativeURL
->_base
;
3640 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, (base
->_flags
& IS_DIRECTORY
) != 0, fsType
, alloc
);
3641 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, fsType
, (relativeURL
->_flags
& IS_DIRECTORY
) != 0);
3646 // Caller must release the returned string
3647 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
3648 CFIndex baseLen
= CFStringGetLength(basePath
);
3649 CFIndex relLen
= CFStringGetLength(relativePath
);
3650 UniChar pathDelimiter
= PATH_DELIM_FOR_TYPE(fsType
);
3651 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
3652 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
3654 if (buf
[baseLen
-1] != pathDelimiter
) {
3655 buf
[baseLen
] = pathDelimiter
;
3659 UniChar
*ptr
= buf
+ baseLen
- 1;
3660 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
3663 baseLen
= ptr
- buf
+ 1;
3665 if (fsType
== kCFURLHFSPathStyle
) {
3666 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3669 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
3670 *(buf
+ baseLen
+ relLen
) = '\0';
3671 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
3674 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
3675 CFURLRef url
= NULL
;
3676 uint8_t buf
[CFMaxPathSize
+ 1];
3677 if (_CFGetCurrentDirectory((char *)buf
, CFMaxPathLength
)) {
3678 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen((char *)buf
), true);
3683 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
3684 Boolean isAbsolute
= true;
3686 CFURLRef baseURL
, result
;
3688 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3690 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__
);
3692 len
= CFStringGetLength(filePath
);
3695 case kCFURLPOSIXPathStyle
:
3696 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3698 case kCFURLWindowsPathStyle
:
3699 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3700 /* Absolute path under Win32 can begin with "\\"
3703 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3705 case kCFURLHFSPathStyle
:
3706 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3712 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3714 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
3715 if (baseURL
) CFRelease(baseURL
);
3719 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
3721 Boolean isAbsolute
= true, releaseFilePath
= false;
3722 UniChar pathDelim
= '\0';
3725 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
3726 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(fsType
), __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3728 len
= CFStringGetLength(filePath
);
3731 case kCFURLPOSIXPathStyle
:
3732 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3736 case kCFURLWindowsPathStyle
:
3737 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3738 /* Absolute path under Win32 can begin with "\\"
3742 isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3745 case kCFURLHFSPathStyle
:
3746 { CFRange fullStrRange
= CFRangeMake( 0, CFStringGetLength( filePath
) );
3748 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3751 if ( _CFExecutableLinkedOnOrAfter( CFSystemVersionTiger
) &&
3752 filePath
&& CFStringFindWithOptions( filePath
, CFSTR("::"), fullStrRange
, 0, NULL
) ) {
3753 UniChar
* chars
= (UniChar
*) malloc( fullStrRange
.length
* sizeof( UniChar
) );
3754 CFIndex index
, writeIndex
, firstColonOffset
= -1;
3756 CFStringGetCharacters( filePath
, fullStrRange
, chars
);
3758 for ( index
= 0, writeIndex
= 0 ; index
< fullStrRange
.length
; index
++ ) {
3759 if ( chars
[ index
] == ':' ) {
3760 if ( index
+ 1 < fullStrRange
.length
&& chars
[ index
+ 1 ] == ':' ) {
3762 // Don't let :: go off the 'top' of the path -- which means that there always has to be at
3763 // least one ':' to the left of the current write position to go back to.
3764 if ( writeIndex
> 0 && firstColonOffset
>= 0 )
3767 while ( writeIndex
> 0 && writeIndex
>= firstColonOffset
&& chars
[ writeIndex
] != ':' )
3770 index
++; // skip over the first ':', so we replace the ':' which is there with a new one
3773 if ( firstColonOffset
== -1 )
3774 firstColonOffset
= writeIndex
;
3777 chars
[ writeIndex
++ ] = chars
[ index
];
3780 if ( releaseFilePath
&& filePath
)
3781 CFRelease( filePath
);
3783 filePath
= CFStringCreateWithCharacters( allocator
, chars
, writeIndex
);
3784 // reset len because a canonical HFS path can be a different length than the original CFString
3785 len
= CFStringGetLength(filePath
);
3786 releaseFilePath
= true;
3798 if (isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) != pathDelim
) {
3799 CFMutableStringRef tempRef
= CFStringCreateMutable(allocator
, 0);
3800 CFStringAppend(tempRef
, filePath
);
3801 CFStringAppendCharacters(tempRef
, &pathDelim
, 1);
3802 if ( releaseFilePath
&& filePath
) CFRelease( filePath
);
3804 releaseFilePath
= true;
3805 } else if (!isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) == pathDelim
) {
3806 if (len
== 1 || CFStringGetCharacterAtIndex(filePath
, len
-2) == pathDelim
) {
3807 // Override isDirectory
3810 CFStringRef tempRef
= CFStringCreateWithSubstring(allocator
, filePath
, CFRangeMake(0, len
-1));
3811 if ( releaseFilePath
&& filePath
)
3812 CFRelease( filePath
);
3814 releaseFilePath
= true;
3817 if (!filePath
|| CFStringGetLength(filePath
) == 0) {
3818 if (releaseFilePath
&& filePath
) CFRelease(filePath
);
3821 url
= _CFURLAlloc(allocator
);
3822 _CFURLInit((struct __CFURL
*)url
, filePath
, fsType
, baseURL
);
3823 if (releaseFilePath
) CFRelease(filePath
);
3824 if (isDirectory
) ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
3825 if (fsType
== kCFURLPOSIXPathStyle
) {
3826 // 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
3827 // 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
3828 CFStringInlineBuffer buf
;
3829 Boolean sawSlash
= FALSE
;
3830 Boolean mustPrependDotSlash
= FALSE
;
3831 CFIndex idx
, length
= CFStringGetLength(url
->_string
);
3832 CFStringInitInlineBuffer(url
->_string
, &buf
, CFRangeMake(0, length
));
3833 for (idx
= 0; idx
< length
; idx
++) {
3834 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
3835 if (!isPathLegalCharacter(ch
)) break;
3839 } else if (ch
== ':') {
3840 mustPrependDotSlash
= TRUE
;
3844 if (idx
== length
) {
3845 ((struct __CFURL
*)url
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
3847 if (mustPrependDotSlash
) {
3848 CFMutableStringRef newString
= CFStringCreateMutable(allocator
, 0);
3849 CFStringAppend(newString
, CFSTR("./"));
3850 CFStringAppend(newString
, url
->_string
);
3851 CFRelease(url
->_string
);
3852 ((struct __CFURL
*)url
)->_string
= newString
;
3858 static Boolean
_pathHasFileIDPrefix( CFStringRef path
)
3860 // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
3861 static const CFStringRef fileIDPrefix
= CFSTR( "/" FILE_ID_PREFIX
"/" );
3862 return path
&& CFStringHasPrefix( path
, fileIDPrefix
) && CFStringGetLength( path
) > CFStringGetLength( fileIDPrefix
);
3866 static Boolean
_pathHasFileIDOnly( CFStringRef path
)
3868 // Is file ID rooted and contains no additonal path segments
3870 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 );
3874 CF_EXPORT CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
3875 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
|| ASSERT_CHECK_PATHSTYLE(pathStyle
), __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
3877 return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL
), anURL
, pathStyle
, false);
3881 // 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
3882 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
3883 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
3884 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
3885 CFStringRef relPath
= NULL
;
3887 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3888 // We can grope the ivars
3889 CFURLPathStyle myType
= URL_PATH_TYPE(anURL
);
3890 if (myType
== fsType
) {
3891 relPath
= (CFStringRef
)CFRetain(anURL
->_string
);
3892 } else if (fsType
== kCFURLPOSIXPathStyle
&& myType
== FULL_URL_REPRESENTATION
) {
3893 if (!(anURL
->_flags
& IS_PARSED
)) {
3894 _parseComponentsOfURL(anURL
);
3896 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3897 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
3902 if (relPath
== NULL
) {
3903 CFStringRef urlPath
= CFURLCopyPath(anURL
);
3904 CFStringEncoding enc
= (anURL
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: anURL
->_encoding
;
3907 case kCFURLPOSIXPathStyle
:
3908 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
3910 case kCFURLHFSPathStyle
:
3913 case kCFURLWindowsPathStyle
:
3914 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
3917 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
3923 // For Tiger, leave this behavior in for all path types. For Leopard, it would be nice to remove this entirely
3924 // and do a linked-on-or-later check so we don't break third parties.
3925 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
3926 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
3927 if ( relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == PATH_DELIM_FOR_TYPE(fsType
)) {
3928 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
3933 // Note that !resolveAgainstBase implies !base
3934 if (!basePath
|| !relPath
) {
3937 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
3938 CFRelease(basePath
);
3944 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
3946 CFAllocatorRef alloc
= CFGetAllocator(url
);
3948 if (!url
) return false;
3949 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3950 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
3952 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
3956 #elif DEPLOYMENT_TARGET_WINDOWS
3957 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
3960 CFIndex pathLen
= CFStringGetLength(path
);
3961 CFIndex numConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLen
), CFStringFileSystemEncoding(), 0, true, buffer
, bufLen
-1, &usedLen
); // -1 because we need one byte to zero-terminate.
3963 if (numConverted
== pathLen
) {
3964 buffer
[usedLen
] = '\0';
3969 #error Unknown or unspecified DEPLOYMENT_TARGET
3974 #if DEPLOYMENT_TARGET_WINDOWS
3975 CF_EXPORT Boolean
_CFURLGetWideFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, wchar_t *buffer
, CFIndex bufferLength
) {
3976 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
3977 CFIndex pathLength
, charsConverted
, usedBufLen
;
3978 if (!path
) return false;
3979 pathLength
= CFStringGetLength(path
);
3980 if (pathLength
+1 > bufferLength
) {
3984 charsConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLength
), kCFStringEncodingUTF16
, 0, false, (UInt8
*)buffer
, bufferLength
*sizeof(wchar_t), &usedBufLen
);
3985 // CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer);
3987 if (charsConverted
!= pathLength
|| usedBufLen%sizeof
(wchar_t) != 0) {
3990 buffer
[usedBufLen
/sizeof(wchar_t)] = 0;
3991 // buffer[pathLength] = 0;
3997 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
3998 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4000 if (!path
) return NULL
;
4001 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4002 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
);
4003 #elif DEPLOYMENT_TARGET_WINDOWS
4004 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
);
4006 #error Unknown or unspecified DEPLOYMENT_TARGET
4012 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
4013 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4015 if (!path
) return NULL
;
4016 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4017 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
, baseURL
);
4018 #elif DEPLOYMENT_TARGET_WINDOWS
4019 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
4021 #error Unknown or unspecified DEPLOYMENT_TARGET
4028 /******************************/
4029 /* Support for path utilities */
4030 /******************************/
4032 // Assumes url is a CFURL (not an Obj-C NSURL)
4033 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
4034 UInt32 pathType
= URL_PATH_TYPE(url
);
4035 CFRange pathRg
, componentRg
;
4037 if (pathType
== FULL_URL_REPRESENTATION
) {
4038 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4039 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4041 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
4044 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
4048 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == PATH_DELIM_FOR_TYPE(pathType
)) {
4050 if (pathRg
.length
== 0) {
4055 if (CFStringFindWithOptions(url
->_string
, PATH_DELIM_AS_STRING_FOR_TYPE(pathType
), pathRg
, kCFCompareBackwards
, &componentRg
)) {
4056 componentRg
.location
++;
4057 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
4059 componentRg
= pathRg
;
4064 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
4067 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4068 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
4071 if (!path
) return NULL
;
4072 rg
= CFRangeMake(0, CFStringGetLength(path
));
4073 if ( rg
.length
== 0 ) return path
;
4074 length
= rg
.length
; // Remember this for comparison later
4075 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/' ) {
4078 if ( rg
.length
== 0 )
4080 // If we have reduced the string to empty, then it's "/", and that's what we return as
4081 // the last path component.
4084 else if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
4085 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
4086 rg
.location
= compRg
.location
+ 1;
4088 if (rg
.location
== 0 && rg
.length
== length
) {
4091 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), path
, rg
);
4095 CFRange rg
= _rangeOfLastPathComponent(url
);
4096 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
4098 return (CFStringRef
)CFRetain(CFSTR(""));
4100 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url
))) {
4101 return (CFStringRef
)CFRetain(CFSTR("/"));
4103 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
4104 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
&& !(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
4106 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
4107 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
4109 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
4118 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
4119 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
4120 CFStringRef ext
= NULL
;
4123 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
4124 if (rg
.location
!= kCFNotFound
) {
4126 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
4127 if (rg
.length
> 0) {
4128 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
4130 ext
= (CFStringRef
)CFRetain(CFSTR(""));
4133 CFRelease(lastPathComp
);
4138 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
4141 url
= _CFURLFromNSURL(url
);
4142 __CFGenericValidateType(url
, __kCFURLTypeID
);
4143 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
4145 fsType
= URL_PATH_TYPE(url
);
4146 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(pathComponent
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(pathComponent
)), 0, NULL
)) {
4147 // Must convert to full representation, and then work with it
4148 fsType
= FULL_URL_REPRESENTATION
;
4149 _convertToURLRepresentation((struct __CFURL
*)url
);
4152 if (fsType
== FULL_URL_REPRESENTATION
) {
4153 CFMutableStringRef newString
;
4154 CFStringRef newComp
;
4156 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4157 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
4159 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4160 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4161 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4162 if (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
4163 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4166 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
4168 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
4171 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4172 CFRelease(newString
);
4174 UniChar pathDelim
= PATH_DELIM_FOR_TYPE(fsType
);
4175 CFStringRef newString
;
4176 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != pathDelim
) {
4178 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@%c"), url
->_string
, pathDelim
, pathComponent
, pathDelim
);
4180 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@"), url
->_string
, pathDelim
, pathComponent
);
4184 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@%c"), url
->_string
, pathComponent
, pathDelim
);
4186 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@"), url
->_string
, pathComponent
);
4189 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, isDirectory
, url
->_base
);
4190 CFRelease(newString
);
4195 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
4197 CFMutableStringRef newString
;
4198 CFRange lastCompRg
, pathRg
;
4199 Boolean appendDotDot
= false;
4202 url
= _CFURLFromNSURL(url
);
4203 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4204 __CFGenericValidateType(url
, __kCFURLTypeID
);
4206 fsType
= URL_PATH_TYPE(url
);
4207 if (fsType
== FULL_URL_REPRESENTATION
) {
4208 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
4209 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
4210 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
4212 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
4214 lastCompRg
= _rangeOfLastPathComponent(url
);
4215 if (lastCompRg
.length
== 0) {
4216 appendDotDot
= true;
4217 } else if (lastCompRg
.length
== 1) {
4218 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
4219 if (ch
== '.' || ch
== PATH_DELIM_FOR_TYPE(fsType
)) {
4220 appendDotDot
= true;
4222 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
4223 appendDotDot
= true;
4226 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4229 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != PATH_DELIM_FOR_TYPE(fsType
)) {
4230 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4233 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
4235 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4237 // 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 "/.".
4238 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
4239 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == PATH_DELIM_FOR_TYPE(fsType
)) {
4240 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
4241 } else if (pathRg
.length
+delta
== 5) {
4242 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
4245 } else if (lastCompRg
.location
== pathRg
.location
) {
4246 CFStringReplace(newString
, pathRg
, CFSTR("."));
4247 CFStringInsert(newString
, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4249 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
4251 if (fsType
== FULL_URL_REPRESENTATION
) {
4252 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4254 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, true, url
->_base
);
4256 CFRelease(newString
);
4260 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
4261 CFMutableStringRef newString
;
4264 CFURLPathStyle fsType
;
4266 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4267 url
= _CFURLFromNSURL(url
);
4268 __CFGenericValidateType(url
, __kCFURLTypeID
);
4269 __CFGenericValidateType(extension
, CFStringGetTypeID());
4271 rg
= _rangeOfLastPathComponent(url
);
4272 if (rg
.location
< 0) return NULL
; // No path
4273 fsType
= URL_PATH_TYPE(url
);
4274 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(extension
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(extension
)), 0, NULL
)) {
4275 _convertToURLRepresentation((struct __CFURL
*)url
);
4276 fsType
= FULL_URL_REPRESENTATION
;
4277 rg
= _rangeOfLastPathComponent(url
);
4280 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4281 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
4282 if (fsType
== FULL_URL_REPRESENTATION
) {
4283 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4284 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
4286 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4288 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, extension
);
4289 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4291 CFRelease(newString
);
4295 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4299 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4300 url
= _CFURLFromNSURL(url
);
4301 __CFGenericValidateType(url
, __kCFURLTypeID
);
4302 rg
= _rangeOfLastPathComponent(url
);
4303 if (rg
.location
< 0) {
4305 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
4306 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4307 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
4308 CFStringDelete(newString
, dotRg
);
4309 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
) {
4310 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4312 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, URL_PATH_TYPE(url
), (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4314 CFRelease(newString
);
4316 result
= (CFURLRef
)CFRetain(url
);
4322 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4323 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4324 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4325 CFMutableArrayRef newComponents
= CFArrayCreateMutableCopy(alloc
, 0, components
);
4326 Boolean doSpecialLeadingColon
= false;
4327 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
4329 CFRelease(components
);
4332 if (!doSpecialLeadingColon
&& firstChar
!= ':') {
4333 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4334 } else if (firstChar
!= ':') {
4335 // see what we need to add at the beginning. Under MacOS, if the
4336 // first character isn't a ':', then the first component is the
4337 // volume name, and we need to find the mount point. Bleah. If we
4338 // don't find a mount point, we're going to have to lie, and make something up.
4339 CFStringRef firstComp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0);
4340 if (CFStringGetLength(firstComp
) == 1 && CFStringGetCharacterAtIndex(firstComp
, 0) == '/') {
4341 // "/" is the "magic" path for a UFS root directory
4342 CFArrayRemoveValueAtIndex(newComponents
, 0);
4343 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4345 // See if we can get a mount point.
4346 Boolean foundMountPoint
= false;
4347 if (!foundMountPoint
) {
4348 // Fall back to treating the volume name as the top level directory
4349 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4353 CFArrayRemoveValueAtIndex(newComponents
, 0);
4356 cnt
= CFArrayGetCount(newComponents
);
4357 for (i
= 0; i
< cnt
; i
++) {
4358 CFStringRef comp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, i
);
4359 CFStringRef newComp
= NULL
;
4360 CFRange searchRg
, slashRg
;
4361 searchRg
.location
= 0;
4362 searchRg
.length
= CFStringGetLength(comp
);
4363 while (CFStringFindWithOptions(comp
, CFSTR("/"), searchRg
, 0, &slashRg
)) {
4365 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4367 CFStringReplace((CFMutableStringRef
)newComp
, slashRg
, CFSTR(":"));
4368 searchRg
.length
= searchRg
.location
+ searchRg
.length
- slashRg
.location
- 1;
4369 searchRg
.location
= slashRg
.location
+ 1;
4372 CFArraySetValueAtIndex(newComponents
, i
, newComp
);
4376 if (isDir
&& CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, cnt
-1)) != 0) {
4377 CFArrayAppendValue(newComponents
, CFSTR(""));
4379 return newComponents
;
4381 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4382 CFArrayRef components
= HFSPathToURLComponents(path
, alloc
, isDir
);
4383 CFArrayRef newComponents
= components
? copyStringArrayWithTransformation(components
, escapePathComponent
) : NULL
;
4386 if (components
) CFRelease(components
);
4387 if (!newComponents
) return NULL
;
4389 cnt
= CFArrayGetCount(newComponents
);
4390 if (cnt
== 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0)) == 0) {
4391 result
= (CFStringRef
)CFRetain(CFSTR("/"));
4393 result
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
4395 CFRelease(newComponents
);
4398 #elif DEPLOYMENT_TARGET_WINDOWS
4400 #error Unknown or unspecified DEPLOYMENT_TARGET
4405 // keys and vals must have space for at least 4 key/value pairs. No argument can be NULL.
4406 // Caller must release values, but not keys
4407 static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url
, CFTypeRef
*keys
, CFTypeRef
*vals
, CFIndex
*count
) {
4408 CFAllocatorRef alloc
= CFGetAllocator(url
);
4409 CFURLRef base
= CFURLGetBaseURL(url
);
4410 keys
[0] = CFSTR("_CFURLStringType");
4411 keys
[1] = CFSTR("_CFURLString");
4412 keys
[2] = CFSTR("_CFURLBaseStringType");
4413 keys
[3] = CFSTR("_CFURLBaseURLString");
4414 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4415 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4416 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4417 vals
[1] = CFURLGetString(url
);
4419 SInt32 urlType
= URL_PATH_TYPE(url
);
4420 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4421 if (url
->_flags
& IS_DIRECTORY
) {
4422 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4423 vals
[1] = CFRetain(url
->_string
);
4425 vals
[1] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), url
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4428 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4429 vals
[1] = CFRetain(url
->_string
);
4431 vals
[1] = CFStringCreateWithSubstring(alloc
, url
->_string
, CFRangeMake(0, CFStringGetLength(url
->_string
) - 1));
4436 if (CF_IS_OBJC(__kCFURLTypeID
, base
)) {
4437 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4438 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4439 vals
[3] = CFURLGetString(base
);
4441 SInt32 urlType
= URL_PATH_TYPE(base
);
4442 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4443 if (base
->_flags
& IS_DIRECTORY
) {
4444 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4445 vals
[3] = CFRetain(base
->_string
);
4447 vals
[3] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), base
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4450 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4451 vals
[3] = CFRetain(base
->_string
);
4453 vals
[3] = CFStringCreateWithSubstring(alloc
, base
->_string
, CFRangeMake(0, CFStringGetLength(base
->_string
) - 1));
4463 // Private API for Finder to use
4464 CFPropertyListRef
_CFURLCopyPropertyListRepresentation(CFURLRef url
) {
4465 CFTypeRef keys
[4], vals
[4];
4466 CFDictionaryRef dict
;
4468 __CFURLCopyPropertyListKeysAndValues(url
, keys
, vals
, &count
);
4469 dict
= CFDictionaryCreate(CFGetAllocator(url
), keys
, vals
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4470 for (idx
= 0; idx
< count
; idx
++) {
4471 CFRelease(vals
[idx
]);
4476 CFURLRef
_CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc
, CFPropertyListRef pListRepresentation
) {
4477 CFStringRef baseString
, string
;
4478 CFNumberRef baseTypeNum
, urlTypeNum
;
4479 SInt32 baseType
, urlType
;
4480 CFURLRef baseURL
= NULL
, url
;
4481 CFDictionaryRef dict
= (CFDictionaryRef
)pListRepresentation
;
4483 // Start by getting all the pieces and verifying they're of the correct type.
4484 if (CFGetTypeID(pListRepresentation
) != CFDictionaryGetTypeID()) {
4487 string
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLString"));
4488 if (!string
|| CFGetTypeID(string
) != CFStringGetTypeID()) {
4491 urlTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLStringType"));
4492 if (!urlTypeNum
|| CFGetTypeID(urlTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum
, kCFNumberSInt32Type
, &urlType
) || (urlType
!= FULL_URL_REPRESENTATION
&& urlType
!= kCFURLPOSIXPathStyle
&& urlType
!= kCFURLHFSPathStyle
&& urlType
!= kCFURLWindowsPathStyle
)) {
4495 baseString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseURLString"));
4497 if (CFGetTypeID(baseString
) != CFStringGetTypeID()) {
4500 baseTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseStringType"));
4501 if (!baseTypeNum
|| CFGetTypeID(baseTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum
, kCFNumberSInt32Type
, &baseType
) ||
4502 (baseType
!= FULL_URL_REPRESENTATION
&& baseType
!= kCFURLPOSIXPathStyle
&& baseType
!= kCFURLHFSPathStyle
&& baseType
!= kCFURLWindowsPathStyle
)) {
4505 if (baseType
== FULL_URL_REPRESENTATION
) {
4506 baseURL
= _CFURLCreateWithArbitraryString(alloc
, baseString
, NULL
);
4508 baseURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, baseString
, (CFURLPathStyle
)baseType
, CFStringGetCharacterAtIndex(baseString
, CFStringGetLength(baseString
)-1) == PATH_DELIM_FOR_TYPE(baseType
), NULL
);
4511 if (urlType
== FULL_URL_REPRESENTATION
) {
4512 url
= _CFURLCreateWithArbitraryString(alloc
, string
, baseURL
);
4514 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, string
, (CFURLPathStyle
)urlType
, CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
)-1) == PATH_DELIM_FOR_TYPE(urlType
), baseURL
);
4516 if (baseURL
) CFRelease(baseURL
);