2 * Copyright (c) 2008 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 1998-2004, Apple, Inc. All rights reserved.
25 Responsibility: Becky Willrich
28 #include <CoreFoundation/CFURL.h>
30 #include "CFCharacterSetPriv.h"
31 #include <CoreFoundation/CFNumber.h>
32 #include "CFInternal.h"
33 #include "CFStringEncodingConverter.h"
39 #if DEPLOYMENT_TARGET_MACOSX
42 #include <sys/types.h>
45 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
46 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
47 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
48 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
);
49 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
50 extern CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
52 #if DEPLOYMENT_TARGET_MACOSX
54 DEFINE_WEAK_CARBONCORE_FUNC(void, DisposeHandle
, (Handle A
), (A
))
55 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSNewAlias
, (const FSRef
*A
, const FSRef
*B
, AliasHandle
*C
), (A
, B
, C
), -3296)
56 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSGetVolumeInfo
, (FSVolumeRefNum A
, ItemCount B
, FSVolumeRefNum
*C
, FSVolumeInfoBitmap D
, FSVolumeInfo
*E
, HFSUniStr255
*F
, FSRef
*G
), (A
, B
, C
, D
, E
, F
, G
), -3296)
57 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSGetCatalogInfo
, (const FSRef
*A
, FSCatalogInfoBitmap B
, FSCatalogInfo
*C
, HFSUniStr255
*D
, FSSpec
*E
, FSRef
*F
), (A
, B
, C
, D
, E
, F
), -3296)
58 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSMakeFSRefUnicode
, (const FSRef
*A
, UniCharCount B
, const UniChar
*C
, TextEncoding D
, FSRef
*E
), (A
, B
, C
, D
, E
), -3296)
59 DEFINE_WEAK_CARBONCORE_FUNC(OSStatus
, FSPathMakeRef
, (const uint8_t *A
, FSRef
*B
, Boolean
*C
), (A
, B
, C
), -3296)
60 DEFINE_WEAK_CARBONCORE_FUNC(OSStatus
, FSRefMakePath
, (const FSRef
*A
, uint8_t *B
, UInt32 C
), (A
, B
, C
), -3296)
61 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSpMakeFSRef
, (const FSSpec
*A
, FSRef
*B
), (A
, B
), -3296)
62 DEFINE_WEAK_CARBONCORE_FUNC(Size
, GetAliasSizeFromPtr
, (AliasPtr A
), (A
), 0)
63 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, _FSGetFSRefInformationFast
, (const FSRef
* A
, SInt16
*B
, UInt32
*C
, UInt32
*D
, Boolean
*E
, Boolean
*F
, HFSUniStr255
*G
), (A
, B
, C
, D
, E
, F
, G
), -3296)
64 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, _FSGetVolumeByName
, ( CFStringRef volumeNameRef
, FSVolumeRefNum
* vRefNumP
), ( volumeNameRef
, vRefNumP
), -3296 )
68 #error Unknown or unspecified DEPLOYMENT_TARGET
72 #ifndef DEBUG_URL_MEMORY_USAGE
73 #define DEBUG_URL_MEMORY_USAGE 0
76 #if DEBUG_URL_MEMORY_USAGE
77 static CFAllocatorRef URLAllocator
= NULL
;
78 static UInt32 numFileURLsCreated
= 0;
79 static UInt32 numFileURLsConverted
= 0;
80 static UInt32 numFileURLsDealloced
= 0;
81 static UInt32 numURLs
= 0;
82 static UInt32 numDealloced
= 0;
83 static UInt32 numExtraDataAllocated
= 0;
84 static UInt32 numURLsWithBaseURL
= 0;
87 /* The bit flags in myURL->_flags */
88 #define HAS_SCHEME (0x0001)
89 #define HAS_USER (0x0002)
90 #define HAS_PASSWORD (0x0004)
91 #define HAS_HOST (0x0008)
92 #define HAS_PORT (0x0010)
93 #define HAS_PATH (0x0020)
94 #define HAS_PARAMETERS (0x0040)
95 #define HAS_QUERY (0x0080)
96 #define HAS_FRAGMENT (0x0100)
97 #define HAS_HTTP_SCHEME (0x0200)
98 // Last free bit (0x200) in lower word goes here!
99 #define IS_IPV6_ENCODED (0x0400)
100 #define IS_OLD_UTF8_STYLE (0x0800)
101 #define IS_DIRECTORY (0x1000)
102 #define IS_PARSED (0x2000)
103 #define IS_ABSOLUTE (0x4000)
104 #define IS_DECOMPOSABLE (0x8000)
106 #define PATH_TYPE_MASK (0x000F0000)
107 /* POSIX_AND_URL_PATHS_MATCH will only be true if the URL and POSIX paths are identical, character for character, except for the presence/absence of a trailing slash on directories */
108 #define POSIX_AND_URL_PATHS_MATCH (0x00100000)
109 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000)
111 /* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */
112 // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
113 // #define SCHEME_DIFFERS (0x00400000) unused
114 #define USER_DIFFERS (0x00800000)
115 #define PASSWORD_DIFFERS (0x01000000)
116 #define HOST_DIFFERS (0x02000000)
117 // Port can actually never differ because if there were a non-digit following a colon in the net location, we'd interpret the whole net location as the host
118 #define PORT_DIFFERS (0x04000000)
119 // #define PATH_DIFFERS (0x08000000) unused
120 // #define PARAMETERS_DIFFER (0x10000000) unused
121 // #define QUERY_DIFFERS (0x20000000) unused
122 // #define FRAGMENT_DIFfERS (0x40000000) unused
123 #define HAS_FILE_SCHEME (0x80000000)
125 // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag
126 #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22)
128 // Other useful defines
129 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
130 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
131 #define FULL_URL_REPRESENTATION (0xF)
133 /* URL_PATH_TYPE(anURL) will be one of the CFURLPathStyle constants, in which case string is a file system path, or will be FULL_URL_REPRESENTATION, in which case the string is the full URL string. One caveat - string always has a trailing path delimiter if the url is a directory URL. This must be stripped before returning file system representations! */
134 #define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16)
135 #define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/'))
136 #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/")))
139 // In order to get the sizeof ( __CFURL ) < 32 bytes, move these items into a seperate structure which is
140 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
141 // either a sanitized string or a reserved pointer for URLHandle.
142 struct _CFURLAdditionalData
{
143 void *_reserved
; // Reserved for URLHandle's use.
144 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 CFRuntimeBase _cfBase
;
150 CFStringRef _string
; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above)
153 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set.
154 struct _CFURLAdditionalData
* extra
;
158 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
160 if ( url
&& url
->extra
)
161 return url
->extra
->_reserved
;
166 CF_INLINE CFMutableStringRef
_getSanitizedString ( const struct __CFURL
* url
)
168 if ( url
&& url
->extra
)
169 return url
->extra
->_sanitizedString
;
174 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
176 if ( url
&& ! url
->extra
)
177 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), __kCFAllocatorGCScannedMemory
);
179 extra
->_reserved
= _getReserved( url
);
180 extra
->_sanitizedString
= _getSanitizedString( url
);
184 #if DEBUG_URL_MEMORY_USAGE
185 numExtraDataAllocated
++;
190 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
194 // Don't allocate extra space if we're just going to be storing NULL
195 if ( ! url
->extra
&& reserved
)
196 _CFURLAllocateExtraDataspace( url
);
199 CF_WRITE_BARRIER_BASE_ASSIGN(CFGetAllocator(url
), url
->extra
, url
->extra
->_reserved
, reserved
);
203 CF_INLINE
void _setSanitizedString ( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
207 // Don't allocate extra space if we're just going to be storing NULL
208 if ( ! url
->extra
&& sanitizedString
)
209 _CFURLAllocateExtraDataspace( url
);
212 url
->extra
->_sanitizedString
= sanitizedString
;
216 static void _convertToURLRepresentation(struct __CFURL
*url
);
217 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
);
218 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
219 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
220 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
221 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
222 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
223 static void computeSanitizedString(CFURLRef url
);
224 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
225 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
226 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
229 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
230 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->ranges
));
233 static Boolean _createOldUTF8StyleURLs
= false;
235 CF_INLINE Boolean
createOldUTF8StyleURLs(void) {
236 return (_createOldUTF8StyleURLs
);
239 // Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW
241 void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs
) {
242 _createOldUTF8StyleURLs
= createUTF8URLs
;
253 static const unsigned char sURLValidCharacters
[] = {
255 /* '!' 33 */ VALID
| UNRESERVED
| PATHVALID
,
258 /* '$' 36 */ VALID
| PATHVALID
,
260 /* '&' 38 */ VALID
| PATHVALID
,
261 /* ''' 39 */ VALID
| UNRESERVED
| PATHVALID
,
262 /* '(' 40 */ VALID
| UNRESERVED
| PATHVALID
,
263 /* ')' 41 */ VALID
| UNRESERVED
| PATHVALID
,
264 /* '*' 42 */ VALID
| UNRESERVED
| PATHVALID
,
265 /* '+' 43 */ VALID
| SCHEME
| PATHVALID
,
266 /* ',' 44 */ VALID
| PATHVALID
,
267 /* '-' 45 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
268 /* '.' 46 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
269 /* '/' 47 */ VALID
| PATHVALID
,
270 /* '0' 48 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
271 /* '1' 49 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
272 /* '2' 50 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
273 /* '3' 51 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
274 /* '4' 52 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
275 /* '5' 53 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
276 /* '6' 54 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
277 /* '7' 55 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
278 /* '8' 56 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
279 /* '9' 57 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
283 /* '=' 61 */ VALID
| PATHVALID
,
286 /* '@' 64 */ VALID
| PATHVALID
,
287 /* 'A' 65 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
288 /* 'B' 66 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
289 /* 'C' 67 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
290 /* 'D' 68 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
291 /* 'E' 69 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
292 /* 'F' 70 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
293 /* 'G' 71 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
294 /* 'H' 72 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
295 /* 'I' 73 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
296 /* 'J' 74 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
297 /* 'K' 75 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
298 /* 'L' 76 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
299 /* 'M' 77 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
300 /* 'N' 78 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
301 /* 'O' 79 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
302 /* 'P' 80 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
303 /* 'Q' 81 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
304 /* 'R' 82 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
305 /* 'S' 83 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
306 /* 'T' 84 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
307 /* 'U' 85 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
308 /* 'V' 86 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
309 /* 'W' 87 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
310 /* 'X' 88 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
311 /* 'Y' 89 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
312 /* 'Z' 90 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
317 /* '_' 95 */ VALID
| UNRESERVED
| PATHVALID
,
319 /* 'a' 97 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
320 /* 'b' 98 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
321 /* 'c' 99 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
322 /* 'd' 100 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
323 /* 'e' 101 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
324 /* 'f' 102 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
325 /* 'g' 103 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
326 /* 'h' 104 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
327 /* 'i' 105 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
328 /* 'j' 106 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
329 /* 'k' 107 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
330 /* 'l' 108 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
331 /* 'm' 109 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
332 /* 'n' 110 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
333 /* 'o' 111 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
334 /* 'p' 112 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
335 /* 'q' 113 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
336 /* 'r' 114 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
337 /* 's' 115 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
338 /* 't' 116 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
339 /* 'u' 117 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
340 /* 'v' 118 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
341 /* 'w' 119 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
342 /* 'x' 120 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
343 /* 'y' 121 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
344 /* 'z' 122 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
348 /* '~' 126 */ VALID
| UNRESERVED
| PATHVALID
,
352 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
353 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & VALID
) : false;
356 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
357 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & SCHEME
) : false;
360 // "Unreserved" as defined by RFC 2396
361 CF_INLINE Boolean
isUnreservedCharacter(UniChar ch
) {
362 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & UNRESERVED
) : false;
365 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
366 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & PATHVALID
) : false;
369 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
370 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & HEXDIGIT
) : false;
373 // Returns false if ch1 or ch2 isn't properly formatted
374 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
376 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
377 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
378 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
381 *result
= (*result
) << 4;
382 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
383 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
384 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
390 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
391 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
394 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
395 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
396 CFAllocatorRef alloc
= CFGetAllocator(array
);
397 CFMutableArrayRef mArray
= NULL
;
398 CFIndex i
, c
= CFArrayGetCount(array
);
399 for (i
= 0; i
< c
; i
++) {
400 CFStringRef origComp
= (CFStringRef
)CFArrayGetValueAtIndex(array
, i
);
401 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
402 if (!unescapedComp
) {
405 if (unescapedComp
!= origComp
) {
407 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
409 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
411 CFRelease(unescapedComp
);
414 if (mArray
) CFRelease(mArray
);
424 // 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.
425 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
426 if (preserveSlashes
) {
427 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?"), kCFStringEncodingUTF8
);
429 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
433 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
434 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
437 // 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
438 static Boolean
_hackToConvertSurrogates(UniChar highChar
, UniChar lowChar
, CFMutableStringRef str
) {
439 UniChar surrogate
[2];
440 uint8_t bytes
[6]; // Aki sez it should never take more than 6 bytes
443 surrogate
[0] = highChar
;
444 surrogate
[1] = lowChar
;
445 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &len
) != kCFStringEncodingConversionSuccess
) {
448 for (currByte
= bytes
; currByte
< bytes
+ len
; currByte
++) {
449 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
450 unsigned char high
, low
;
451 high
= ((*currByte
) & 0xf0) >> 4;
452 low
= (*currByte
) & 0x0f;
453 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
454 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
455 CFStringAppendCharacters(str
, escapeSequence
, 3);
460 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
461 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
462 uint8_t *bytePtr
= bytes
, *currByte
;
464 CFAllocatorRef alloc
= NULL
;
465 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
466 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
467 if (byteLength
<= 6) {
468 // The encoding cannot accomodate the character
471 alloc
= CFGetAllocator(str
);
472 bytePtr
= (uint8_t *)CFAllocatorAllocate(alloc
, byteLength
, 0);
473 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
474 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
478 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
479 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
480 unsigned char high
, low
;
481 high
= ((*currByte
) & 0xf0) >> 4;
482 low
= (*currByte
) & 0x0f;
483 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
484 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
485 CFStringAppendCharacters(str
, escapeSequence
, 3);
487 if (bytePtr
!= bytes
) {
488 CFAllocatorDeallocate(alloc
, bytePtr
);
493 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
494 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
495 CFMutableStringRef newStr
= NULL
;
498 CFRange percentRange
, searchRange
;
499 CFStringRef escapedStr
= NULL
;
500 CFMutableStringRef strForEscapedChar
= NULL
;
502 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
503 Boolean failed
= false;
505 if (!originalString
) return NULL
;
507 if (charactersToLeaveEscaped
== NULL
) {
508 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
511 length
= CFStringGetLength(originalString
);
512 searchRange
= CFRangeMake(0, length
);
514 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
515 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
516 uint8_t numBytesExpected
;
520 // Make sure we have at least 2 more characters
521 if (length
- percentRange
.location
< 3) { failed
= true; break; }
523 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
524 // so we assume the percent character is legit, and let it pass into the string
525 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
526 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
527 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
528 if (!(bytes
[0] & 0x80)) {
529 numBytesExpected
= 1;
530 } else if (!(bytes
[0] & 0x20)) {
531 numBytesExpected
= 2;
532 } else if (!(bytes
[0] & 0x10)) {
533 numBytesExpected
= 3;
535 numBytesExpected
= 4;
537 if (numBytesExpected
== 1) {
538 // one byte sequence (most common case); handle this specially
539 escapedChar
= bytes
[0];
540 if (!strForEscapedChar
) {
541 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
543 escapedStr
= strForEscapedChar
;
546 // Make sure up front that we have enough characters
547 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
548 for (j
= 1; j
< numBytesExpected
; j
++) {
549 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
550 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
551 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
552 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
555 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
556 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
559 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
560 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
561 escapedChar
= 0xfeff;
562 if (!strForEscapedChar
) {
563 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
565 CFRelease(escapedStr
);
566 escapedStr
= strForEscapedChar
;
571 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
572 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
573 searchRange
.length
= length
- searchRange
.location
;
576 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
577 if (escapedStr
!= strForEscapedChar
) {
578 CFRelease(escapedStr
);
586 newStr
= CFStringCreateMutable(alloc
, length
);
588 if (percentRange
.location
- mark
> 0) {
589 // The creation of this temporary string is unfortunate.
590 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
591 CFStringAppend(newStr
, substring
);
592 CFRelease(substring
);
594 CFStringAppend(newStr
, escapedStr
);
595 if (escapedStr
!= strForEscapedChar
) {
596 CFRelease(escapedStr
);
599 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
602 if (escapedStr
&& escapedStr
!= strForEscapedChar
) CFRelease(escapedStr
);
603 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
605 if (newStr
) CFRelease(newStr
);
609 // Need to cat on the remainder of the string
610 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
611 CFStringAppend(newStr
, substring
);
612 CFRelease(substring
);
616 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
621 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
622 if (enc
== kCFStringEncodingUTF8
) {
623 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
625 CFMutableStringRef newStr
= NULL
;
626 CFMutableStringRef escapedStr
= NULL
;
629 CFRange percentRange
, searchRange
;
630 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
631 Boolean failed
= false;
632 uint8_t byteBuffer
[8];
633 uint8_t *bytes
= byteBuffer
;
634 int capacityOfBytes
= 8;
636 if (!originalString
) return NULL
;
638 if (charactersToLeaveEscaped
== NULL
) {
639 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
642 length
= CFStringGetLength(originalString
);
643 searchRange
= CFRangeMake(0, length
);
645 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
647 CFIndex percentLoc
= percentRange
.location
;
648 CFStringRef convertedString
;
649 int numBytesUsed
= 0;
651 // Make sure we have at least 2 more characters
652 if (length
- percentLoc
< 3) { failed
= true; break; }
654 if (numBytesUsed
== capacityOfBytes
) {
655 if (bytes
== byteBuffer
) {
656 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
657 memmove(bytes
, byteBuffer
, capacityOfBytes
);
658 capacityOfBytes
= 16;
660 void *oldbytes
= bytes
;
661 int oldcap
= capacityOfBytes
;
662 capacityOfBytes
= 2*capacityOfBytes
;
663 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, capacityOfBytes
* sizeof(uint8_t), 0);
664 memmove(bytes
, oldbytes
, oldcap
);
665 CFAllocatorDeallocate(alloc
, oldbytes
);
669 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
671 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
673 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
675 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
676 searchRange
.location
= percentLoc
;
677 searchRange
.length
= length
- searchRange
.location
;
680 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
681 if (!convertedString
) {
687 newStr
= CFStringCreateMutable(alloc
, length
);
689 if (percentRange
.location
- mark
> 0) {
690 // The creation of this temporary string is unfortunate.
691 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
692 CFStringAppend(newStr
, substring
);
693 CFRelease(substring
);
697 CFStringAppend(newStr
, convertedString
);
698 CFRelease(convertedString
);
700 CFIndex i
, c
= CFStringGetLength(convertedString
);
702 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
704 for (i
= 0; i
< c
; i
++) {
705 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
706 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
707 CFStringAppendCharacters(newStr
, &ch1
, 1);
709 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
710 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
714 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
717 if (escapedStr
) CFRelease(escapedStr
);
718 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
720 if (newStr
) CFRelease(newStr
);
724 // Need to cat on the remainder of the string
725 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
726 CFStringAppend(newStr
, substring
);
727 CFRelease(substring
);
731 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
737 static CFStringRef
_addPercentEscapesToString(CFAllocatorRef allocator
, CFStringRef originalString
, Boolean (*shouldReplaceChar
)(UniChar
, void*), CFIndex (*handlePercentChar
)(CFIndex
, CFStringRef
, CFStringRef
*, void *), CFStringEncoding encoding
, void *context
) {
738 CFMutableStringRef newString
= NULL
;
740 CFStringInlineBuffer buf
;
742 if (!originalString
) return NULL
;
743 length
= CFStringGetLength(originalString
);
744 if (length
== 0) return (CFStringRef
)CFStringCreateCopy(allocator
, originalString
);
745 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
747 for (idx
= 0; idx
< length
; idx
++) {
748 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
749 Boolean shouldReplace
= shouldReplaceChar(ch
, context
);
751 // Perform the replacement
753 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
754 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
756 if (!_appendPercentEscapesForCharacter(ch
, encoding
, newString
)) {
757 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
758 if (encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1))) {
759 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
760 if (_hackToConvertSurrogates(ch
, CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1), newString
)) {
761 idx
++; // We consumed 2 characters, not 1
769 } else if (ch
== '%' && handlePercentChar
) {
770 CFStringRef replacementString
= NULL
;
771 CFIndex newIndex
= handlePercentChar(idx
, originalString
, &replacementString
, context
);
774 } else if (replacementString
) {
776 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
777 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
779 CFStringAppend(newString
, replacementString
);
780 CFRelease(replacementString
);
782 if (newIndex
== idx
) {
784 CFStringAppendCharacters(newString
, &ch
, 1);
787 if (!replacementString
&& newString
) {
789 for (tmpIndex
= idx
; tmpIndex
< newIndex
; tmpIndex
++) {
790 ch
= CFStringGetCharacterAtIndex(originalString
, idx
);
791 CFStringAppendCharacters(newString
, &ch
, 1);
796 } else if (newString
) {
797 CFStringAppendCharacters(newString
, &ch
, 1);
801 // Ran in to an encoding failure
802 if (newString
) CFRelease(newString
);
804 } else if (newString
) {
807 return (CFStringRef
)CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
812 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
813 CFIndex i
, c
= CFStringGetLength(string
);
814 CFStringInlineBuffer buf
;
815 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
816 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
820 static Boolean
_shouldPercentReplaceChar(UniChar ch
, void *context
) {
821 CFStringRef unescape
= ((CFStringRef
*)context
)[0];
822 CFStringRef escape
= ((CFStringRef
*)context
)[1];
823 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
825 if (unescape
&& _stringContainsCharacter(unescape
, ch
)) {
826 shouldReplace
= false;
828 } else if (escape
&& _stringContainsCharacter(escape
, ch
)) {
829 shouldReplace
= true;
831 return shouldReplace
;
834 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
835 CFStringRef strings
[2];
836 strings
[0] = charactersToLeaveUnescaped
;
837 strings
[1] = legalURLCharactersToBeEscaped
;
838 return _addPercentEscapesToString(allocator
, originalString
, _shouldPercentReplaceChar
, NULL
, encoding
, strings
);
841 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
842 CFURLRef url1
= (CFURLRef
)cf1
;
843 CFURLRef url2
= (CFURLRef
)cf2
;
844 UInt32 pathType1
, pathType2
;
846 __CFGenericValidateType(cf1
, CFURLGetTypeID());
847 __CFGenericValidateType(cf2
, CFURLGetTypeID());
849 if (url1
== url2
) return true;
850 if ((url1
->_flags
& IS_PARSED
) && (url2
->_flags
& IS_PARSED
) && (url1
->_flags
& IS_DIRECTORY
) != (url2
->_flags
& IS_DIRECTORY
)) return false;
852 if (! url2
->_base
) return false;
853 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
854 } else if ( url2
->_base
) {
858 pathType1
= URL_PATH_TYPE(url1
);
859 pathType2
= URL_PATH_TYPE(url2
);
860 if (pathType1
== pathType2
) {
861 if (pathType1
!= FULL_URL_REPRESENTATION
) {
862 return CFEqual(url1
->_string
, url2
->_string
);
864 // Do not compare the original strings; compare the sanatized strings.
865 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
868 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
869 CFStringRef scheme1
= CFURLCopyScheme(url1
);
870 CFStringRef scheme2
= CFURLCopyScheme(url2
);
872 if (scheme1
&& scheme2
) {
873 eq
= CFEqual(scheme1
, scheme2
);
876 } else if (!scheme1
&& !scheme2
) {
880 if (scheme1
) CFRelease(scheme1
);
881 else CFRelease(scheme2
);
883 if (!eq
) return false;
885 if (pathType1
== FULL_URL_REPRESENTATION
) {
886 if (!(url1
->_flags
& IS_PARSED
)) {
887 _parseComponentsOfURL(url1
);
889 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
894 if (pathType2
== FULL_URL_REPRESENTATION
) {
895 if (!(url2
->_flags
& IS_PARSED
)) {
896 _parseComponentsOfURL(url2
);
898 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
903 // No help for it; we now must convert to the canonical representation and compare.
904 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
908 static CFHashCode
__CFURLHash(CFTypeRef cf
) {
909 /* 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. */
910 CFURLRef url
= (CFURLRef
)cf
;
912 if (CFURLCanBeDecomposed(url
)) {
913 CFStringRef lastComp
= CFURLCopyLastPathComponent(url
);
914 CFStringRef hostNameRef
= CFURLCopyHostName(url
);
919 result
= CFHash(lastComp
);
925 result
^= CFHash( hostNameRef
);
926 CFRelease( hostNameRef
);
929 result
= CFHash(CFURLGetString(url
));
934 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
935 CFURLRef url
= (CFURLRef
)cf
;
936 __CFGenericValidateType(cf
, CFURLGetTypeID());
938 CFRetain(url
->_string
);
941 // Do not dereference url->_base; it may be an ObjC object
942 return CFStringCreateWithFormat(CFGetAllocator(url
), NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
947 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
948 CFURLRef url
= (CFURLRef
)cf
;
950 CFAllocatorRef alloc
= CFGetAllocator(url
);
952 CFStringRef baseString
= CFCopyDescription(url
->_base
);
953 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@,\n\tbase = %@}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
, baseString
);
954 CFRelease(baseString
);
956 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@, base = (null)}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
);
961 #if DEBUG_URL_MEMORY_USAGE
963 extern __attribute((used
)) void __CFURLDumpMemRecord(void) {
964 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\n"), numURLs
, numDealloced
, numFileURLsCreated
, numFileURLsConverted
, numFileURLsDealloced
, numExtraDataAllocated
, numURLsWithBaseURL
);
967 // if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator);
971 static void __CFURLDeallocate(CFTypeRef cf
) {
972 CFURLRef url
= (CFURLRef
)cf
;
973 CFAllocatorRef alloc
;
974 __CFGenericValidateType(cf
, CFURLGetTypeID());
975 alloc
= CFGetAllocator(url
);
976 #if DEBUG_URL_MEMORY_USAGE
978 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
979 numFileURLsDealloced
++;
982 if (url
->_string
) CFRelease(url
->_string
); // GC: 3879914
983 if (url
->_base
) CFRelease(url
->_base
);
984 if (url
->ranges
) CFAllocatorDeallocate(alloc
, url
->ranges
);
985 if (_getSanitizedString(url
)) CFRelease(_getSanitizedString(url
));
987 if ( url
->extra
!= NULL
)
988 CFAllocatorDeallocate( alloc
, url
->extra
);
991 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
993 static const CFRuntimeClass __CFURLClass
= {
1001 __CFURLCopyFormattingDescription
,
1002 __CFURLCopyDescription
1005 // When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but
1006 // when it is defined, we must prefix with static to prevent the string from being exported
1007 #ifdef __CONSTANT_CFSTRINGS__
1008 static CONST_STRING_DECL(kCFURLFileScheme
, "file")
1009 static CONST_STRING_DECL(kCFURLDataScheme
, "data")
1010 static CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1011 static CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1013 CONST_STRING_DECL(kCFURLFileScheme
, "file")
1014 CONST_STRING_DECL(kCFURLDataScheme
, "data")
1015 CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1016 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1018 __private_extern__
void __CFURLInitialize(void) {
1019 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1022 /* Toll-free bridging support; get the true CFURL from an NSURL */
1023 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1024 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, url
, "_cfurl");
1028 CFTypeID
CFURLGetTypeID(void) {
1029 return __kCFURLTypeID
;
1032 __private_extern__
void CFShowURL(CFURLRef url
) {
1034 fprintf(stdout
, "(null)\n");
1037 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1038 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1039 fprintf(stdout
, "ObjC bridged object}\n");
1042 fprintf(stdout
, "\n\tPath type: ");
1043 switch (URL_PATH_TYPE(url
)) {
1044 case kCFURLPOSIXPathStyle
:
1045 fprintf(stdout
, "POSIX");
1047 case kCFURLHFSPathStyle
:
1048 fprintf(stdout
, "HFS");
1050 case kCFURLWindowsPathStyle
:
1051 fprintf(stdout
, "NTFS");
1053 case FULL_URL_REPRESENTATION
:
1054 fprintf(stdout
, "Native URL");
1057 fprintf(stdout
, "UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url
));
1059 fprintf(stdout
, "\n\tRelative string: ");
1060 CFShow(url
->_string
);
1061 fprintf(stdout
, "\tBase URL: ");
1063 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1066 fprintf(stdout
, "(null)\n");
1068 fprintf(stdout
, "\tFlags: 0x%x\n}\n", (unsigned int)url
->_flags
);
1072 /***************************************************/
1073 /* URL creation and String/Data creation from URLS */
1074 /***************************************************/
1075 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, const char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1076 CFIndex neededLength
;
1080 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1084 *freeCharacters
= false;
1088 *ustring
= CFStringGetCharactersPtr(string
);
1090 *useCString
= false;
1091 *freeCharacters
= false;
1095 *freeCharacters
= true;
1096 length
= CFStringGetLength(string
);
1097 rg
= CFRangeMake(0, length
);
1098 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1099 if (neededLength
== length
) {
1100 char *buf
= (char *)CFAllocatorAllocate(alloc
, length
, 0);
1101 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, (uint8_t *)buf
, length
, NULL
);
1105 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1106 CFStringGetCharacters(string
, rg
, buf
);
1107 *useCString
= false;
1112 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1113 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1115 /* 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. */
1117 CFIndex idx
, base_idx
= 0;
1118 CFIndex string_length
;
1119 UInt32 flags
= (IS_PARSED
| *theFlags
);
1120 Boolean useCString
, freeCharacters
, isCompliant
;
1121 uint8_t numRanges
= 0;
1122 const char *cstring
= NULL
;
1123 const UniChar
*ustring
= NULL
;
1125 string_length
= CFStringGetLength(string
);
1126 constructBuffers(alloc
, string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1128 // Algorithm is as described in RFC 1808
1129 // 1: parse the fragment; remainder after left-most "#" is fragment
1130 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1131 if ('#' == STRING_CHAR(idx
)) {
1132 flags
|= HAS_FRAGMENT
;
1133 ranges
[8].location
= idx
+ 1;
1134 ranges
[8].length
= string_length
- (idx
+ 1);
1136 string_length
= idx
; // remove fragment from parse string
1140 // 2: parse the scheme
1141 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1142 UniChar ch
= STRING_CHAR(idx
);
1144 flags
|= HAS_SCHEME
;
1145 flags
|= IS_ABSOLUTE
;
1146 ranges
[0].location
= base_idx
;
1147 ranges
[0].length
= idx
;
1150 // optimization for http urls
1151 if (idx
== 4 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' &&
1152 STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p')
1154 flags
|= HAS_HTTP_SCHEME
;
1156 // optimization for file urls
1157 if (idx
== 4 && STRING_CHAR(0) == 'f' && STRING_CHAR(1) == 'i' &&
1158 STRING_CHAR(2) == 'l' && STRING_CHAR(3) == 'e')
1160 flags
|= HAS_FILE_SCHEME
;
1163 } else if (!scheme_valid(ch
)) {
1164 break; // invalid scheme character -- no scheme
1168 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1169 // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage
1170 // expects this to be treated identically to "scheme://" - REW, 12/08/03
1171 if (!(flags
& HAS_SCHEME
)) {
1173 } else if (base_idx
== string_length
) {
1174 isCompliant
= false;
1175 } else if (STRING_CHAR(base_idx
) != '/') {
1176 isCompliant
= false;
1182 // Clear the fragment flag if it's been set
1183 if (flags
& HAS_FRAGMENT
) {
1184 flags
&= (~HAS_FRAGMENT
);
1185 string_length
= CFStringGetLength(string
);
1187 (*theFlags
) = flags
;
1188 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
1189 (*range
)->location
= ranges
[0].location
;
1190 (*range
)->length
= ranges
[0].length
;
1192 if (freeCharacters
) {
1193 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1197 // URL is 1808-compliant
1198 flags
|= IS_DECOMPOSABLE
;
1200 // 3: parse the network location and login
1201 if (2 <= (string_length
- base_idx
) && '/' == STRING_CHAR(base_idx
) && '/' == STRING_CHAR(base_idx
+1)) {
1202 CFIndex base
= 2 + base_idx
, extent
;
1203 for (idx
= base
; idx
< string_length
; idx
++) {
1204 if ('/' == STRING_CHAR(idx
) || '?' == STRING_CHAR(idx
)) break;
1208 // net_loc parts extend from base to extent (but not including), which might be to end of string
1209 // net location is "<user>:<password>@<host>:<port>"
1210 if (extent
!= base
) {
1211 for (idx
= base
; idx
< extent
; idx
++) {
1212 if ('@' == STRING_CHAR(idx
)) { // there is a user
1216 ranges
[1].location
= base
; // base of the user
1217 for (idx2
= base
; idx2
< idx
; idx2
++) {
1218 if (':' == STRING_CHAR(idx2
)) { // found a password separator
1219 flags
|= HAS_PASSWORD
;
1221 ranges
[2].location
= idx2
+1; // base of the password
1222 ranges
[2].length
= idx
-(idx2
+1); // password extent
1223 ranges
[1].length
= idx2
- base
; // user extent
1227 if (!(flags
& HAS_PASSWORD
)) {
1228 // user extends to the '@'
1229 ranges
[1].length
= idx
- base
; // user extent
1237 ranges
[3].location
= base
; // base of host
1239 // base has been advanced past the user and password if they existed
1240 for (idx
= base
; idx
< extent
; idx
++) {
1241 // IPV6 support (RFC 2732) DCJ June/10/2002
1242 if ('[' == STRING_CHAR(idx
)) { // starting IPV6 explicit address
1243 // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end
1244 for ( ; idx
< extent
; ++ idx
) {
1245 if ( ']' == STRING_CHAR(idx
)) {
1246 flags
|= IS_IPV6_ENCODED
;
1251 // there is a port if we see a colon. Only the last one is the port, though.
1252 else if ( ':' == STRING_CHAR(idx
)) {
1255 ranges
[4].location
= idx
+1; // base of port
1256 ranges
[4].length
= extent
- (idx
+1); // port extent
1257 ranges
[3].length
= idx
- base
; // host extent
1261 if (!(flags
& HAS_PORT
)) {
1262 ranges
[3].length
= extent
- base
; // host extent
1268 // 4: parse the query; remainder after left-most "?" is query
1269 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1270 if ('?' == STRING_CHAR(idx
)) {
1273 ranges
[7].location
= idx
+ 1;
1274 ranges
[7].length
= string_length
- (idx
+1);
1275 string_length
= idx
; // remove query from parse string
1280 // 5: parse the parameters; remainder after left-most ";" is parameters
1281 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1282 if (';' == STRING_CHAR(idx
)) {
1283 flags
|= HAS_PARAMETERS
;
1285 ranges
[6].location
= idx
+ 1;
1286 ranges
[6].length
= string_length
- (idx
+1);
1287 string_length
= idx
; // remove parameters from parse string
1292 // 6: parse the path; it's whatever's left between string_length & base_idx
1293 if (string_length
- base_idx
!= 0 || (flags
& NET_LOCATION_MASK
))
1295 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1301 pathRg
.location
= base_idx
;
1302 pathRg
.length
= string_length
- base_idx
;
1305 if (pathRg
.length
> 0) {
1306 Boolean sawPercent
= FALSE
;
1307 for (idx
= pathRg
.location
; idx
< string_length
; idx
++) {
1308 if ('%' == STRING_CHAR(idx
)) {
1314 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1317 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 1);
1320 } else if (ch
== '.') {
1321 if (pathRg
.length
== 1) {
1324 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 2);
1327 } else if (ch
!= '.') {
1329 } else if (pathRg
.length
== 2) {
1332 isDir
= (STRING_CHAR(pathRg
.location
+ pathRg
.length
- 3) == '/');
1339 isDir
= (baseURL
!= NULL
) ? CFURLHasDirectoryPath(baseURL
) : false;
1342 flags
|= IS_DIRECTORY
;
1346 if (freeCharacters
) {
1347 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1349 (*theFlags
) = flags
;
1350 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
)*numRanges
, 0);
1352 for (idx
= 0, flags
= 1; flags
!= (1<<9); flags
= (flags
<<1), idx
++) {
1353 if ((*theFlags
) & flags
) {
1354 (*range
)[numRanges
] = ranges
[idx
];
1360 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
) {
1362 Boolean sawIllegalChar
= false;
1363 for (idx
= base
; idx
< end
; idx
++) {
1364 Boolean shouldEscape
;
1365 UniChar ch
= STRING_CHAR(idx
);
1366 if (isURLLegalCharacter(ch
)) {
1367 if ((componentFlag
== HAS_USER
|| componentFlag
== HAS_PASSWORD
) && (ch
== '/' || ch
== '?' || ch
== '@')) {
1368 shouldEscape
= true;
1370 shouldEscape
= false;
1372 } else if (ch
== '%' && idx
+ 2 < end
&& isHexDigit(STRING_CHAR(idx
+ 1)) && isHexDigit(STRING_CHAR(idx
+2))) {
1373 shouldEscape
= false;
1374 } else if (componentFlag
== HAS_HOST
&& ((idx
== base
&& ch
== '[') || (idx
== end
-1 && ch
== ']'))) {
1375 shouldEscape
= false;
1377 shouldEscape
= true;
1379 if (!shouldEscape
) continue;
1381 sawIllegalChar
= true;
1382 if (componentFlag
&& flags
) {
1383 *flags
|= (componentFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
);
1385 if (!*escapedString
) {
1386 *escapedString
= CFStringCreateMutable(alloc
, 0);
1389 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[*mark
]), idx
- *mark
, kCFStringEncodingISOLatin1
, false);
1390 CFStringAppend(*escapedString
, tempString
);
1391 CFRelease(tempString
);
1393 CFStringAppendCharacters(*escapedString
, &(ustring
[*mark
]), idx
- *mark
);
1396 _appendPercentEscapesForCharacter(ch
, encoding
, *escapedString
); // This can never fail because anURL->_string was constructed from the encoding passed in
1398 return sawIllegalChar
;
1401 static void computeSanitizedString(CFURLRef url
) {
1402 CFAllocatorRef alloc
= CFGetAllocator(url
);
1403 CFIndex string_length
= CFStringGetLength(url
->_string
);
1404 Boolean useCString
, freeCharacters
;
1405 const char *cstring
= NULL
;
1406 const UniChar
*ustring
= NULL
;
1407 CFIndex base
; // where to scan from
1408 CFIndex mark
; // first character not-yet copied to sanitized string
1409 if (!(url
->_flags
& IS_PARSED
)) {
1410 _parseComponentsOfURL(url
);
1412 constructBuffers(alloc
, url
->_string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1413 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1414 // Impossible to have a problem character in the scheme
1415 CFMutableStringRef sanitizedString
= NULL
;
1416 base
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
).length
+ 1;
1418 if (!scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1419 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1421 if ( sanitizedString
) {
1422 _setSanitizedString( (struct __CFURL
*) url
, sanitizedString
);
1425 // Go component by component
1426 CFIndex currentComponent
= HAS_USER
;
1427 CFMutableStringRef sanitizedString
= NULL
;
1429 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1430 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->ranges
, currentComponent
);
1431 if (componentRange
.location
!= kCFNotFound
) {
1432 scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1434 currentComponent
= currentComponent
<< 1;
1436 if (sanitizedString
) {
1437 _setSanitizedString((struct __CFURL
*)url
, sanitizedString
);
1439 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1442 if (_getSanitizedString(url
) && mark
!= string_length
) {
1444 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1445 CFStringAppend(_getSanitizedString(url
), tempString
);
1446 CFRelease(tempString
);
1448 CFStringAppendCharacters(_getSanitizedString(url
), &(ustring
[mark
]), string_length
- mark
);
1451 if (freeCharacters
) {
1452 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1457 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1458 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1459 CFIndex string_length
= CFStringGetLength(comp
);
1460 Boolean useCString
, freeCharacters
;
1461 const char *cstring
= NULL
;
1462 const UniChar
*ustring
= NULL
;
1463 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1464 CFMutableStringRef result
= NULL
;
1466 constructBuffers(alloc
, comp
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1467 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1469 if (mark
< string_length
) {
1471 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1472 CFStringAppend(result
, tempString
);
1473 CFRelease(tempString
);
1475 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1479 // This should nevr happen
1481 result
= (CFMutableStringRef
)comp
;
1483 if (freeCharacters
) {
1484 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1490 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1491 struct __CFURL
*url
;
1492 #if DEBUG_URL_MEMORY_USAGE
1494 // if (!URLAllocator) {
1495 // URLAllocator = CFCountingAllocatorCreate(NULL);
1497 allocator
= URLAllocator
;
1499 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1502 if (createOldUTF8StyleURLs()) {
1503 url
->_flags
|= IS_OLD_UTF8_STYLE
;
1505 url
->_string
= NULL
;
1508 // url->_reserved = NULL;
1509 url
->_encoding
= kCFStringEncodingUTF8
;
1510 // url->_sanatizedString = NULL;
1516 // 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.
1517 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, UInt32 fsType
, CFURLRef base
) {
1518 CFAssert1(URLString
!= NULL
&& CFGetTypeID(URLString
) == CFStringGetTypeID() && CFStringGetLength(URLString
) != 0, __kCFLogAssertion
, "%s(): internal CF error; empty string encountered", __PRETTY_FUNCTION__
);
1519 CFAssert2((fsType
== FULL_URL_REPRESENTATION
) || (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) || (fsType
== kCFURLHFSPathStyle
), __kCFLogAssertion
, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__
, fsType
);
1521 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1522 url
->_string
= (CFStringRef
)CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1523 url
->_flags
|= (fsType
<< 16);
1525 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1527 #if DEBUG_URL_MEMORY_USAGE
1528 if (fsType
!= FULL_URL_REPRESENTATION
) {
1529 numFileURLsCreated
++;
1532 numURLsWithBaseURL
++;
1536 #if DEPLOYMENT_TARGET_MACOSX
1537 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1538 CFIndex len
= CFStringGetLength(path
);
1539 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1540 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1541 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1543 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1544 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1548 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1549 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1552 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1553 CFIndex len
= CFStringGetLength(path
);
1555 char firstChar
= CFStringGetCharacterAtIndex(path
, 0);
1556 char secondChar
= CFStringGetCharacterAtIndex(path
, 1);
1557 if (((((firstChar
>= 'A') && (firstChar
<= 'Z')) || ((firstChar
>= 'a') && (firstChar
<= 'z'))) &&
1558 ((secondChar
== ':') || (secondChar
== '|'))) ||
1559 ((firstChar
== '\\') && (secondChar
== '\\')))
1561 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLWindowsPathStyle
, NULL
);
1562 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1564 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1565 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1570 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1571 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1574 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1575 CFIndex len
= CFStringGetLength(path
);
1576 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1577 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1578 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1580 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1581 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1585 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1586 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1589 #error Unknown or unspecified DEPLOYMENT_TARGET
1592 // Exported for Foundation's use
1593 CF_EXPORT Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1594 // 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.
1595 // 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
1596 CFStringInlineBuffer stringBuffer
;
1597 CFIndex idx
= 0, length
;
1598 Boolean sawHash
= false;
1600 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1603 length
= CFStringGetLength(string
);
1604 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1605 while (idx
< length
) {
1606 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1609 // Make sure that two valid hex digits follow a '%' character
1611 if ( idx
+ 2 > length
)
1613 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1614 idx
= -1; // To guarantee index < length, and our failure case is triggered
1618 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1620 if (! isHexDigit(ch
) ) {
1621 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1625 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1627 if (! isHexDigit(ch
) ) {
1628 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1635 if (ch
== '[' || ch
== ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1641 if ( isURLLegalCharacter( ch
) )
1651 CF_EXPORT
void _CFURLInitWithString(CFURLRef myURL
, CFStringRef string
, CFURLRef baseURL
) {
1652 struct __CFURL
*url
= (struct __CFURL
*)myURL
; // Supress annoying compile warnings
1653 Boolean isAbsolute
= false;
1654 CFRange colon
= CFStringFind(string
, CFSTR(":"), 0);
1655 if (colon
.location
!= kCFNotFound
) {
1658 for (i
= 0; i
< colon
.location
; i
++) {
1659 char ch
= (char)CFStringGetCharacterAtIndex(string
, i
);
1660 if (!scheme_valid(ch
)) {
1666 _CFURLInit(url
, string
, FULL_URL_REPRESENTATION
, isAbsolute
? NULL
: baseURL
);
1668 url
->_flags
|= IS_ABSOLUTE
;
1672 struct __CFURLEncodingTranslationParameters
{
1673 CFStringEncoding fromEnc
;
1674 CFStringEncoding toEnc
;
1675 const UniChar
*addlChars
;
1677 Boolean escapeHighBit
;
1678 Boolean escapePercents
;
1679 Boolean agreesOverASCII
;
1680 Boolean encodingsMatch
;
1683 static Boolean
_shouldEscapeForEncodingConversion(UniChar ch
, void *context
) {
1684 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1685 if (info
->escapeHighBit
&& ch
> 0x7F) {
1687 } else if (ch
== '%' && info
->escapePercents
) {
1689 } else if (info
->addlChars
) {
1690 const UniChar
*escChar
= info
->addlChars
;
1692 for (i
= 0; i
< info
->count
; escChar
++, i
++) {
1693 if (*escChar
== ch
) {
1701 static CFIndex
_convertEscapeSequence(CFIndex percentIndex
, CFStringRef urlString
, CFStringRef
*newString
, void *context
) {
1702 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1703 CFMutableDataRef newData
;
1704 Boolean sawNonASCIICharacter
= false;
1705 CFIndex i
= percentIndex
;
1708 if (info
->encodingsMatch
) return percentIndex
+ 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well
1709 newData
= CFDataCreateMutable(CFGetAllocator(urlString
), 0);
1710 length
= CFStringGetLength(urlString
);
1712 while (i
< length
&& CFStringGetCharacterAtIndex(urlString
, i
) == '%') {
1714 if (i
+2 >= length
|| !_translateBytes(CFStringGetCharacterAtIndex(urlString
, i
+1), CFStringGetCharacterAtIndex(urlString
, i
+2), &byte
)) {
1718 if (byte
> 0x7f) sawNonASCIICharacter
= true;
1719 CFDataAppendBytes(newData
, &byte
, 1);
1722 if (!sawNonASCIICharacter
&& info
->agreesOverASCII
) {
1725 CFStringRef tmp
= CFStringCreateWithBytes(CFGetAllocator(urlString
), CFDataGetBytePtr(newData
), CFDataGetLength(newData
), info
->fromEnc
, false);
1726 CFIndex tmpIndex
, tmpLen
;
1731 tmpLen
= CFStringGetLength(tmp
);
1732 *newString
= CFStringCreateMutable(CFGetAllocator(urlString
), 0);
1733 for (tmpIndex
= 0; tmpIndex
< tmpLen
; tmpIndex
++) {
1734 if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp
, tmpIndex
), info
->toEnc
, (CFMutableStringRef
)(*newString
))) {
1740 if (tmpIndex
< tmpLen
) {
1741 CFRelease(*newString
);
1750 /* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */
1751 static CFStringRef
_convertPercentEscapes(CFStringRef urlString
, CFStringEncoding fromEncoding
, CFStringEncoding toEncoding
, Boolean escapeAllHighBitCharacters
, Boolean escapePercents
, const UniChar
*addlCharsToEscape
, int numAddlChars
) {
1752 struct __CFURLEncodingTranslationParameters context
;
1753 context
.fromEnc
= fromEncoding
;
1754 context
.toEnc
= toEncoding
;
1755 context
.addlChars
= addlCharsToEscape
;
1756 context
.count
= numAddlChars
;
1757 context
.escapeHighBit
= escapeAllHighBitCharacters
;
1758 context
.escapePercents
= escapePercents
;
1759 context
.agreesOverASCII
= (__CFStringEncodingIsSupersetOfASCII(toEncoding
) && __CFStringEncodingIsSupersetOfASCII(fromEncoding
)) ? true : false;
1760 context
.encodingsMatch
= (fromEncoding
== toEncoding
) ? true : false;
1761 return _addPercentEscapesToString(CFGetAllocator(urlString
), urlString
, _shouldEscapeForEncodingConversion
, _convertEscapeSequence
, toEncoding
, &context
);
1764 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1765 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
1766 CFStringRef urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, encoding
, false);
1768 if (!urlString
|| CFStringGetLength(urlString
) == 0) {
1769 if (urlString
) CFRelease(urlString
);
1772 if (createOldUTF8StyleURLs()) {
1773 if (encoding
!= kCFStringEncodingUTF8
) {
1774 CFStringRef tmp
= _convertPercentEscapes(urlString
, encoding
, kCFStringEncodingUTF8
, false, false, NULL
, 0);
1775 CFRelease(urlString
);
1777 if (!urlString
) return NULL
;
1781 result
= _CFURLAlloc(allocator
);
1783 _CFURLInitWithString(result
, urlString
, baseURL
);
1784 if (encoding
!= kCFStringEncodingUTF8
&& !createOldUTF8StyleURLs()) {
1785 ((struct __CFURL
*)result
)->_encoding
= encoding
;
1788 CFRelease(urlString
); // it's retained by result, now.
1792 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
1793 static const UniChar whitespaceChars
[4] = {' ', '\n', '\r', '\t'};
1794 CFStringRef myStr
= CFURLGetString(url
);
1797 if (url
->_flags
& IS_OLD_UTF8_STYLE
) {
1798 newStr
= (encoding
== kCFStringEncodingUTF8
) ? (CFStringRef
)CFRetain(myStr
) : _convertPercentEscapes(myStr
, kCFStringEncodingUTF8
, encoding
, true, false, escapeWhitespace
? whitespaceChars
: NULL
, escapeWhitespace
? 4 : 0);
1803 result
= CFStringCreateExternalRepresentation(allocator
, newStr
, encoding
, 0);
1808 // Any escape sequences in URLString will be interpreted via UTF-8.
1809 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1811 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1812 if (!_CFStringIsLegalURLString(URLString
)) return NULL
;
1813 url
= _CFURLAlloc(allocator
);
1815 _CFURLInitWithString(url
, URLString
, baseURL
);
1820 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1822 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1823 url
= _CFURLAlloc(allocator
);
1825 _CFURLInitWithString(url
, URLString
, baseURL
);
1830 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
1831 CFStringRef relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
1832 if (!relativeString
) {
1835 if (!useCompatibilityMode
) {
1836 CFURLRef url
= _CFURLCreateWithArbitraryString(alloc
, relativeString
, baseURL
);
1837 CFRelease(relativeString
);
1839 ((struct __CFURL
*)url
)->_encoding
= encoding
;
1840 CFURLRef absURL
= CFURLCopyAbsoluteURL(url
);
1847 UInt32 absFlags
= 0;
1849 CFStringRef absString
= NULL
;
1850 Boolean absStringIsMutable
= false;
1853 absString
= relativeString
;
1855 UniChar ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
1856 if (ch
== '?' || ch
== ';' || ch
== '#') {
1857 // Nothing but parameter + query + fragment; append to the baseURL string
1858 CFStringRef baseString
;
1859 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1860 baseString
= CFURLGetString(baseURL
);
1862 baseString
= baseURL
->_string
;
1864 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
1865 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
1866 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
1867 absStringIsMutable
= true;
1869 UInt32 relFlags
= 0;
1871 CFStringRef relString
= NULL
;
1872 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
1873 if (relFlags
& HAS_SCHEME
) {
1874 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
1875 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
1876 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
1877 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
1878 CFAllocatorDeallocate(alloc
, relRanges
);
1880 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
1882 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
1883 CFRetain(relativeString
);
1884 absString
= relativeString
;
1886 if (baseScheme
) CFRelease(baseScheme
);
1888 CFRetain(relativeString
);
1889 relString
= relativeString
;
1892 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1893 if (!(baseURL
->_flags
& IS_PARSED
)) {
1894 _parseComponentsOfURL(baseURL
);
1896 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->ranges
);
1898 CFStringRef baseString
;
1899 UInt32 baseFlags
= 0;
1900 CFRange
*baseRanges
;
1901 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1902 baseString
= CFURLGetString(baseURL
);
1904 baseString
= baseURL
->_string
;
1906 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
1907 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
1908 CFAllocatorDeallocate(alloc
, baseRanges
);
1910 absStringIsMutable
= true;
1912 if (relString
) CFRelease(relString
);
1913 CFAllocatorDeallocate(alloc
, relRanges
);
1915 CFRelease(relativeString
);
1917 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
1918 if (absFlags
& HAS_PATH
) {
1919 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
1920 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
1921 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), 0);
1922 CFStringRef newPath
;
1923 CFStringGetCharacters(absString
, pathRg
, buf
);
1924 buf
[pathRg
.length
] = '\0';
1925 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
1926 if (CFStringGetLength(newPath
) != pathRg
.length
) {
1927 if (!absStringIsMutable
) {
1928 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
1929 CFRelease(absString
);
1932 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
1935 // Do not deallocate buf; newPath took ownership of it.
1937 CFAllocatorDeallocate(alloc
, absRanges
);
1938 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
1939 CFRelease(absString
);
1941 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
1947 /* 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 */
1948 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
1949 UniChar
*idx
= pathStr
;
1953 if (idx
!= pathStr
) {
1958 } else if (*(idx
+1) == pathDelimiter
) {
1959 if (idx
+ 2 != end
|| idx
!= pathStr
) {
1960 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
1964 // Do not delete the sole path component
1967 } else if (( end
-idx
>= 2 ) && *(idx
+1) == '.' && (idx
+2 == end
|| (( end
-idx
> 2 ) && *(idx
+2) == pathDelimiter
))) {
1968 if (idx
- pathStr
>= 2) {
1969 // 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.
1970 UniChar
*lastDelim
= idx
-2;
1971 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
1973 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
1974 // We have a genuine component to compact out
1976 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
1977 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
1978 end
-= (idx
+ 3 - lastDelim
);
1981 } else if (lastDelim
!= pathStr
) {
1986 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
1994 } else if (stripLeadingDotDots
) {
1995 if (idx
+ 3 != end
) {
1996 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
1997 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2001 // Do not devolve the last path component
2007 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
2010 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
2013 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
- pathStr
, alloc
);
2016 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
2017 CFMutableStringRef newString
= CFStringCreateMutable(alloc
, 0);
2018 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
2019 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
2022 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
2023 if (rg
.location
!= kCFNotFound
) {
2024 CFStringGetCharacters(baseString
, rg
, buf
);
2025 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2026 CFStringAppendCString(newString
, ":", kCFStringEncodingASCII
);
2029 if (relFlags
& NET_LOCATION_MASK
) {
2030 CFStringAppend(newString
, relString
);
2032 CFStringAppendCString(newString
, "//", kCFStringEncodingASCII
);
2033 rg
= _netLocationRange(baseFlags
, baseRanges
);
2034 if (rg
.location
!= kCFNotFound
) {
2035 CFStringGetCharacters(baseString
, rg
, buf
);
2036 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2039 if (relFlags
& HAS_PATH
) {
2040 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
2041 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2042 CFStringRef newPath
;
2043 Boolean useRelPath
= false;
2044 Boolean useBasePath
= false;
2045 if (basePathRg
.location
== kCFNotFound
) {
2047 } else if (relPathRg
.length
== 0) {
2049 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
2051 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2055 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2056 } else if (useBasePath
) {
2057 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2059 // #warning FIXME - Get rid of this allocation
2060 UniChar
*newPathBuf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
2062 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
2063 idx
= newPathBuf
+ basePathRg
.length
- 1;
2064 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
2065 if (*idx
== '/') idx
++;
2066 CFStringGetCharacters(relString
, relPathRg
, idx
);
2067 end
= idx
+ relPathRg
.length
;
2069 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2071 /* Under Win32 absolute path can begin with letter
2072 * so we have to add one '/' to the newString
2075 // No - the input strings here are URL path strings, not Win32 paths.
2076 // Absolute paths should have had a '/' prepended before this point.
2077 // I have removed Sergey Zubarev's change and left his comment (and
2078 // this one) as a record. - REW, 1/5/2004
2080 // if the relative URL does not begin with a slash and
2081 // the base does not end with a slash, add a slash
2082 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2083 CFStringAppendCString(newString
, "/", kCFStringEncodingASCII
);
2086 CFStringAppend(newString
, newPath
);
2088 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2089 rg
.length
= CFStringGetLength(relString
);
2090 if (rg
.length
> rg
.location
) {
2091 rg
.length
-= rg
.location
;
2092 CFStringGetCharacters(relString
, rg
, buf
);
2093 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2096 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2097 if (rg
.location
!= kCFNotFound
) {
2098 CFStringGetCharacters(baseString
, rg
, buf
);
2099 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2102 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2103 // ??? Can this ever happen?
2104 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2106 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2107 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2108 rg
.location
--; // To pick up the separator
2110 CFStringGetCharacters(baseString
, rg
, buf
);
2111 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2113 } else if (relFlags
& HAS_PARAMETERS
) {
2114 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2115 rg
.location
--; // To get the semicolon that starts the parameters
2116 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2117 CFStringGetCharacters(relString
, rg
, buf
);
2118 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2120 // Sigh; we have to resolve these against one another
2121 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2122 if (rg
.location
!= kCFNotFound
) {
2123 CFStringAppendCString(newString
, ";", kCFStringEncodingASCII
);
2124 CFStringGetCharacters(baseString
, rg
, buf
);
2125 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2127 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2128 if (rg
.location
!= kCFNotFound
) {
2129 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2130 CFStringGetCharacters(relString
, rg
, buf
);
2131 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2133 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2134 if (rg
.location
!= kCFNotFound
) {
2135 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2136 CFStringGetCharacters(baseString
, rg
, buf
);
2137 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2140 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2141 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2142 if (rg
.location
!= kCFNotFound
) {
2143 CFStringAppendCString(newString
, "#", kCFStringEncodingASCII
);
2144 CFStringGetCharacters(relString
, rg
, buf
);
2145 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2150 CFAllocatorDeallocate(alloc
, buf
);
2154 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2155 CFURLRef anURL
, base
;
2156 CFURLPathStyle fsType
;
2157 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2158 CFStringRef baseString
, newString
;
2160 CFRange
*baseRanges
;
2163 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2164 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2165 CF_OBJC_CALL0(CFURLRef
, anURL
, relativeURL
, "absoluteURL");
2166 if (anURL
) CFRetain(anURL
);
2170 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2172 base
= relativeURL
->_base
;
2174 return (CFURLRef
)CFRetain(relativeURL
);
2176 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2177 fsType
= URL_PATH_TYPE(relativeURL
);
2179 if (!baseIsObjC
&& fsType
!= FULL_URL_REPRESENTATION
&& fsType
== URL_PATH_TYPE(base
)) {
2180 return _CFURLCopyAbsoluteFileURL(relativeURL
);
2182 if (fsType
!= FULL_URL_REPRESENTATION
) {
2183 _convertToURLRepresentation((struct __CFURL
*)relativeURL
);
2184 fsType
= FULL_URL_REPRESENTATION
;
2186 if (!(relativeURL
->_flags
& IS_PARSED
)) {
2187 _parseComponentsOfURL(relativeURL
);
2189 if ((relativeURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) && !(relativeURL
->_flags
& (RESOURCE_SPECIFIER_MASK
| NET_LOCATION_MASK
)) && !baseIsObjC
&& (URL_PATH_TYPE(base
) == kCFURLPOSIXPathStyle
)) {
2190 // There's nothing to relativeURL's string except the path
2191 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, CFURLHasDirectoryPath(base
), kCFURLPOSIXPathStyle
, alloc
);
2192 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, kCFURLPOSIXPathStyle
, CFURLHasDirectoryPath(relativeURL
));
2198 CFURLPathStyle baseType
= URL_PATH_TYPE(base
);
2199 if (baseType
!= FULL_URL_REPRESENTATION
) {
2200 _convertToURLRepresentation((struct __CFURL
*)base
);
2201 } else if (!(base
->_flags
& IS_PARSED
)) {
2202 _parseComponentsOfURL(base
);
2204 baseString
= base
->_string
;
2205 baseFlags
= base
->_flags
;
2206 baseRanges
= base
->ranges
;
2208 baseString
= CFURLGetString(base
);
2211 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2214 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->ranges
, baseString
, baseFlags
, baseRanges
);
2216 CFAllocatorDeallocate(alloc
, baseRanges
);
2218 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2219 CFRelease(newString
);
2220 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2225 /*******************/
2226 /* Basic accessors */
2227 /*******************/
2228 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2229 return url
->_encoding
;
2232 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2233 anURL
= _CFURLFromNSURL(anURL
);
2234 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) return true;
2235 if (!(anURL
->_flags
& IS_PARSED
)) {
2236 _parseComponentsOfURL(anURL
);
2238 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2241 CFStringRef
CFURLGetString(CFURLRef url
) {
2242 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFStringRef
, url
, "relativeString");
2243 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2244 if (url
->_base
&& (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2245 return url
->_string
;
2247 _convertToURLRepresentation((struct __CFURL
*)url
);
2249 if (!_haveTestedOriginalString(url
)) {
2250 computeSanitizedString(url
);
2252 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2253 return url
->_string
;
2255 return _getSanitizedString( url
);
2259 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2260 CFIndex length
, charsConverted
, usedLength
;
2262 CFStringEncoding enc
;
2263 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2264 string
= CFURLGetString(url
);
2265 enc
= kCFStringEncodingUTF8
;
2267 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2268 _convertToURLRepresentation((struct __CFURL
*)url
);
2270 string
= url
->_string
;
2271 enc
= url
->_encoding
;
2273 length
= CFStringGetLength(string
);
2274 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2275 if (charsConverted
!= length
) {
2282 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2283 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, anURL
, "baseURL");
2284 return anURL
->_base
;
2287 // Assumes the URL is already parsed
2288 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2290 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2291 while (!(compFlag
& 1)) {
2292 compFlag
= compFlag
>> 1;
2301 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2304 CFAllocatorRef alloc
= CFGetAllocator(url
);
2305 CFAssert1(URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
, __kCFLogAssertion
, "%s(): passed a file system URL", __PRETTY_FUNCTION__
);
2306 if (removePercentEscapes
) fromOriginalString
= true;
2307 if (!(url
->_flags
& IS_PARSED
)) {
2308 _parseComponentsOfURL(url
);
2310 rg
= _rangeForComponent(url
->_flags
, url
->ranges
, compFlag
);
2311 if (rg
.location
== kCFNotFound
) return NULL
;
2312 if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_HTTP_SCHEME
) {
2313 comp
= kCFURLHTTPScheme
;
2315 } else if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_FILE_SCHEME
) {
2316 comp
= kCFURLFileScheme
;
2319 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2321 if (!fromOriginalString
) {
2322 if (!_haveTestedOriginalString(url
)) {
2323 computeSanitizedString(url
);
2325 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (url
->_flags
& (compFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
))) {
2326 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2331 if (removePercentEscapes
) {
2333 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
2334 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2336 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2344 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2346 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2347 CF_OBJC_CALL0(CFStringRef
, scheme
, anURL
, "scheme");
2348 if (scheme
) CFRetain(scheme
);
2351 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2353 return CFURLCopyScheme(anURL
->_base
);
2355 CFRetain(kCFURLFileScheme
); // because caller will release it
2356 return kCFURLFileScheme
;
2359 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_HTTP_SCHEME
) {
2360 CFRetain(kCFURLHTTPScheme
);
2361 return kCFURLHTTPScheme
;
2363 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_FILE_SCHEME
) {
2364 CFRetain(kCFURLFileScheme
);
2365 return kCFURLFileScheme
;
2367 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2370 } else if (anURL
->_base
) {
2371 return CFURLCopyScheme(anURL
->_base
);
2377 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2379 CFRange netRg
= {kCFNotFound
, 0};
2382 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2384 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2385 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2386 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2387 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2388 for (i
= 0; i
< c
; i
++) {
2389 if (netRgs
[i
].location
== kCFNotFound
) continue;
2390 if (netRg
.location
== kCFNotFound
) {
2393 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2399 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2400 anURL
= _CFURLFromNSURL(anURL
);
2401 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2402 // !!! This won't work if we go to putting the vol ref num in the net location for HFS
2404 return CFURLCopyNetLocation(anURL
->_base
);
2406 CFRetain(kCFURLLocalhost
);
2407 return kCFURLLocalhost
;
2410 if (!(anURL
->_flags
& IS_PARSED
)) {
2411 _parseComponentsOfURL(anURL
);
2413 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2414 // We provide the net location
2415 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->ranges
);
2417 if (!_haveTestedOriginalString(anURL
)) {
2418 computeSanitizedString(anURL
);
2420 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (anURL
->_flags
& (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2421 // 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.
2423 netRg
.length
= CFStringGetLength( _getSanitizedString(anURL
)) - netRg
.location
;
2424 if (CFStringFindWithOptions(_getSanitizedString(anURL
), CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2425 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2427 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), netRg
);
2429 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2432 } else if (anURL
->_base
) {
2433 return CFURLCopyNetLocation(anURL
->_base
);
2439 // 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.
2440 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
2441 anURL
= _CFURLFromNSURL(anURL
);
2442 if (URL_PATH_TYPE(anURL
) == kCFURLPOSIXPathStyle
&& (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2443 CFRetain(anURL
->_string
);
2444 return anURL
->_string
;
2446 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2447 _convertToURLRepresentation((struct __CFURL
*)anURL
);
2449 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
2452 /* 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.
2454 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.
2456 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
2457 CFStringRef path
= CFURLCopyPath(anURL
);
2458 if (!path
|| CFStringGetLength(path
) == 0) {
2459 if (path
) CFRelease(path
);
2460 if (isAbsolute
) *isAbsolute
= false;
2463 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
2465 if (isAbsolute
) *isAbsolute
= true;
2466 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
2470 if (isAbsolute
) *isAbsolute
= false;
2475 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
2476 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2477 if (URL_PATH_TYPE(anURL
) == FULL_URL_REPRESENTATION
) {
2478 if (!(anURL
->_flags
& IS_PARSED
)) {
2479 _parseComponentsOfURL(anURL
);
2481 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
2482 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2484 return CFURLHasDirectoryPath(anURL
->_base
);
2486 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2489 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
2490 UInt32 firstRsrcSpecFlag
= 0;
2491 UInt32 flag
= HAS_FRAGMENT
;
2492 while (flag
!= HAS_PATH
) {
2494 firstRsrcSpecFlag
= flag
;
2498 return firstRsrcSpecFlag
;
2501 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
2502 anURL
= _CFURLFromNSURL(anURL
);
2503 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2504 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2507 if (!(anURL
->_flags
& IS_PARSED
)) {
2508 _parseComponentsOfURL(anURL
);
2510 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
2511 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, HAS_SCHEME
);
2512 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
2513 if (!_haveTestedOriginalString(anURL
)) {
2514 computeSanitizedString(anURL
);
2516 if (_getSanitizedString(anURL
)) {
2517 // 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.
2518 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), CFRangeMake(base
, CFStringGetLength(_getSanitizedString(anURL
))-base
));
2520 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
2523 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
2525 if (firstRsrcSpecFlag
) {
2526 Boolean canUseOriginalString
= true;
2527 Boolean canUseSanitizedString
= true;
2528 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
2529 if (!_haveTestedOriginalString(anURL
)) {
2530 computeSanitizedString(anURL
);
2532 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
2533 // See if any pieces in the resource specifier differ between sanitized string and original string
2534 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
2535 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2536 canUseOriginalString
= false;
2541 if (!canUseOriginalString
) {
2542 // 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.
2543 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
2544 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2545 canUseSanitizedString
= false;
2550 if (canUseOriginalString
) {
2551 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2552 rg
.location
--; // Include the character that demarcates the component
2553 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
2554 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
2555 } else if (canUseSanitizedString
) {
2556 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2557 rg
.location
--; // Include the character that demarcates the component
2558 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2559 return CFStringCreateWithSubstring(alloc
, _getSanitizedString(anURL
), rg
);
2561 // Must compute the correct string to return; just reparse....
2562 UInt32 sanFlags
= 0;
2563 CFRange
*sanRanges
= NULL
;
2565 _parseComponents(alloc
, _getSanitizedString(anURL
), anURL
->_base
, &sanFlags
, &sanRanges
);
2566 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
2567 CFAllocatorDeallocate(alloc
, sanRanges
);
2568 rg
.location
--; // Include the character that demarcates the component
2569 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2570 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), rg
);
2573 // The resource specifier cannot possibly come from the base.
2579 /*************************************/
2580 /* Accessors that create new objects */
2581 /*************************************/
2583 // 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).
2584 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
2586 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2587 CF_OBJC_CALL0(CFStringRef
, tmp
, anURL
, "host");
2588 if (tmp
) CFRetain(tmp
);
2591 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2592 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2594 return CFURLCopyHostName(anURL
->_base
);
2596 CFRetain(kCFURLLocalhost
);
2597 return kCFURLLocalhost
;
2600 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
2602 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
2603 // Have to strip off the brackets to get the true hostname.
2604 // Assume that to be legal the first and last characters are brackets!
2605 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
2610 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2611 return CFURLCopyHostName(anURL
->_base
);
2617 // Return -1 to indicate no port is specified
2618 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
2620 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2622 CF_OBJC_CALL0(CFNumberRef
, cfPort
, anURL
, "port");
2624 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
2627 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2628 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2630 return CFURLGetPortNumber(anURL
->_base
);
2634 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
2636 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
2637 CFStringInlineBuffer buf
;
2638 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
2640 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
2645 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2646 return CFURLGetPortNumber(anURL
->_base
);
2652 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
2654 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2655 CF_OBJC_CALL0(CFStringRef
, user
, anURL
, "user");
2656 if (user
) CFRetain(user
);
2659 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2660 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2662 return CFURLCopyUserName(anURL
->_base
);
2666 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
2669 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2670 return CFURLCopyUserName(anURL
->_base
);
2676 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
2678 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2679 CF_OBJC_CALL0(CFStringRef
, passwd
, anURL
, "password");
2680 if (passwd
) CFRetain(passwd
);
2683 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2684 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2686 return CFURLCopyPassword(anURL
->_base
);
2690 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
2693 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2694 return CFURLCopyPassword(anURL
->_base
);
2700 // 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
2702 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
2704 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2705 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "parameterString");
2706 if (str
) CFRetain(str
);
2709 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2710 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2713 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
2714 if (str
) return str
;
2715 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2716 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
2718 // Parameter string definitely coming from the relative portion of the URL
2720 return _unescapedParameterString( anURL
->_base
);
2723 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2724 CFStringRef param
= _unescapedParameterString(anURL
);
2727 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2728 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
2730 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
2738 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
2740 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2741 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "query");
2742 if (str
) CFRetain(str
);
2745 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2746 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2749 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
2750 if (str
) return str
;
2751 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2752 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
2755 return _unescapedQueryString(anURL
->_base
);
2758 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2759 CFStringRef query
= _unescapedQueryString(anURL
);
2762 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2763 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
2765 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
2773 // Fragments are NEVER taken from a base URL
2774 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
2776 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2777 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "fragment");
2778 if (str
) CFRetain(str
);
2781 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2782 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2785 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
2789 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2790 CFStringRef fragment
= _unescapedFragment(anURL
);
2793 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2794 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
2796 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
2798 CFRelease(fragment
);
2804 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
2805 CFIndex firstMaskFlag
= 1;
2806 CFIndex lastComponentBeforeMask
= 0;
2807 while (firstMaskFlag
<= HAS_FRAGMENT
) {
2808 if (firstMaskFlag
& mask
) break;
2809 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
2810 firstMaskFlag
= firstMaskFlag
<< 1;
2812 if (lastComponentBeforeMask
== 0) {
2813 // mask includes HAS_SCHEME
2815 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
2816 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
2817 // case file:/path/immediately/without/host
2818 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2819 CFRange pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
2820 if (schemeRg
.length
+ 1 == pathRg
.location
) {
2821 return schemeRg
.length
+ 1;
2823 return schemeRg
.length
+ 3;
2826 // For all other components, the separator precedes the component, so there's no need
2827 // to add extra chars to get to the next insertion point
2828 CFRange rg
= _rangeForComponent(url
->_flags
, url
->ranges
, lastComponentBeforeMask
);
2829 return rg
.location
+ rg
.length
;
2833 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
2834 CFOptionFlags currentOption
;
2835 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
2836 Boolean haveReachedMask
= false;
2837 CFIndex beforeMask
= 0;
2838 CFIndex afterMask
= kCFNotFound
;
2839 CFRange
*currRange
= url
->ranges
;
2840 CFRange maskRange
= {kCFNotFound
, 0};
2841 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
2842 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
2843 firstMaskFlag
= currentOption
;
2844 haveReachedMask
= true;
2846 if (!(url
->_flags
& currentOption
)) continue;
2847 if (!haveReachedMask
) {
2848 beforeMask
= currRange
->location
+ currRange
->length
;
2849 } else if (currentOption
<= mask
) {
2850 if (maskRange
.location
== kCFNotFound
) {
2851 maskRange
= *currRange
;
2853 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
2856 afterMask
= currRange
->location
;
2861 if (afterMask
== kCFNotFound
) {
2862 afterMask
= maskRange
.location
+ maskRange
.length
;
2864 charRangeWithSeparators
->location
= beforeMask
;
2865 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
2869 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2871 switch (component
) {
2872 case kCFURLComponentScheme
:
2875 case kCFURLComponentNetLocation
:
2876 mask
= NET_LOCATION_MASK
;
2878 case kCFURLComponentPath
:
2881 case kCFURLComponentResourceSpecifier
:
2882 mask
= RESOURCE_SPECIFIER_MASK
;
2884 case kCFURLComponentUser
:
2887 case kCFURLComponentPassword
:
2888 mask
= HAS_PASSWORD
;
2890 case kCFURLComponentUserInfo
:
2891 mask
= HAS_USER
| HAS_PASSWORD
;
2893 case kCFURLComponentHost
:
2896 case kCFURLComponentPort
:
2899 case kCFURLComponentParameterString
:
2900 mask
= HAS_PARAMETERS
;
2902 case kCFURLComponentQuery
:
2905 case kCFURLComponentFragment
:
2906 mask
= HAS_FRAGMENT
;
2909 rangeIncludingSeparators
->location
= kCFNotFound
;
2910 rangeIncludingSeparators
->length
= 0;
2911 return CFRangeMake(kCFNotFound
, 0);
2914 if ((url
->_flags
& mask
) == 0) {
2915 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
2916 rangeIncludingSeparators
->length
= 0;
2917 return CFRangeMake(kCFNotFound
, 0);
2919 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
2923 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2924 if (component
== kCFURLComponentScheme
) {
2925 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2926 rangeIncludingSeparators
->location
= 0;
2927 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
2929 } else if (component
== kCFURLComponentResourceSpecifier
) {
2930 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2931 CFIndex stringLength
= CFStringGetLength(url
->_string
);
2932 if (schemeRg
.length
+ 1 == stringLength
) {
2933 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
2934 rangeIncludingSeparators
->length
= 0;
2935 return CFRangeMake(kCFNotFound
, 0);
2937 rangeIncludingSeparators
->location
= schemeRg
.length
;
2938 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
2939 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
2942 rangeIncludingSeparators
->location
= kCFNotFound
;
2943 rangeIncludingSeparators
->length
= 0;
2944 return CFRangeMake(kCFNotFound
, 0);
2949 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2950 CFRange charRange
, charRangeWithSeparators
;
2952 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
2953 url
= _CFURLFromNSURL(url
);
2954 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2955 _convertToURLRepresentation((struct __CFURL
*)url
);
2957 if (!(url
->_flags
& IS_PARSED
)) {
2958 _parseComponentsOfURL(url
);
2961 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
2962 // Special-case this because non-decomposable URLs have a slightly strange flags setup
2963 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
2965 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
2968 if (charRangeWithSeparators
.location
== kCFNotFound
) {
2969 if (rangeIncludingSeparators
) {
2970 rangeIncludingSeparators
->location
= kCFNotFound
;
2971 rangeIncludingSeparators
->length
= 0;
2973 return CFRangeMake(kCFNotFound
, 0);
2974 } else if (rangeIncludingSeparators
) {
2975 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
2977 if (charRange
.location
== kCFNotFound
) {
2978 byteRange
= charRange
;
2979 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
2981 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
2982 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
2984 if (charRangeWithSeparators
.location
== charRange
.location
) {
2985 byteRange
.location
= rangeIncludingSeparators
->location
;
2988 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
2989 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
2991 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
2992 if (maxCharRangeWithSeparators
== maxCharRange
) {
2993 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
2997 rg
.location
= maxCharRange
;
2998 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
2999 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3000 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
3003 } else if (charRange
.location
== kCFNotFound
) {
3004 byteRange
= charRange
;
3006 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
3007 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3012 /* Component support */
3014 /* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */
3015 static CFStringRef
schemeSpecificString(CFURLRef url
) {
3017 isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3018 switch (URL_PATH_TYPE(url
)) {
3019 case kCFURLPOSIXPathStyle
:
3020 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3021 return (CFStringRef
)CFRetain(url
->_string
);
3023 return POSIXPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3025 case kCFURLWindowsPathStyle
:
3026 return WindowsPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
3027 case FULL_URL_REPRESENTATION
:
3028 return CFURLCopyResourceSpecifier(url
);
3034 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
3035 if ( CFURLGetBaseURL(url
) != NULL
) {
3036 components
->scheme
= NULL
;
3038 components
->scheme
= CFURLCopyScheme(url
);
3040 components
->schemeSpecific
= schemeSpecificString(url
);
3044 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
3046 if (components
->scheme
) {
3048 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
3049 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
3050 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
3051 } else if (components
->schemeSpecific
) {
3052 str
= components
->schemeSpecific
;
3058 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3066 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3067 CFAllocatorRef alloc
= CFGetAllocator(url
);
3069 static CFStringRef emptyStr
= NULL
;
3071 emptyStr
= CFSTR("");
3074 if (!CFURLCanBeDecomposed(url
)) {
3077 if ((pathType
= URL_PATH_TYPE(url
)) == FULL_URL_REPRESENTATION
) {
3078 CFStringRef path
= CFURLCopyPath(url
);
3080 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3083 components
->pathComponents
= NULL
;
3085 components
->baseURL
= CFURLGetBaseURL(url
);
3086 if (components
->baseURL
) {
3087 CFRetain(components
->baseURL
);
3088 components
->scheme
= NULL
;
3090 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
3092 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
3093 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3094 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3095 if (url
->_flags
& HAS_PORT
) {
3096 components
->port
= CFURLGetPortNumber(url
);
3098 components
->port
= kCFNotFound
;
3100 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3101 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3102 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3105 case kCFURLPOSIXPathStyle
: {
3106 CFStringRef pathStr
;
3107 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3108 pathStr
= url
->_string
;
3111 pathStr
= POSIXPathToURLPath(url
->_string
, alloc
, url
->_flags
& IS_DIRECTORY
);
3113 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, pathStr
, CFSTR("/"));
3117 case kCFURLWindowsPathStyle
:
3118 components
->pathComponents
= WindowsPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3121 components
->pathComponents
= NULL
;
3123 if (!components
->pathComponents
) {
3126 components
->scheme
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
3127 components
->user
= NULL
;
3128 components
->password
= NULL
;
3129 components
->host
= (CFStringRef
)CFRetain(kCFURLLocalhost
);
3130 components
->port
= kCFNotFound
;
3131 components
->parameterString
= NULL
;
3132 components
->query
= NULL
;
3133 components
->fragment
= NULL
;
3134 components
->baseURL
= CFURLGetBaseURL(url
);
3135 if (components
->baseURL
) CFRetain(components
->baseURL
);
3140 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3141 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3142 CFURLRef base
= comp
->baseURL
;
3144 Boolean hadPrePathComponent
= false;
3147 CFStringAppend(urlString
, comp
->scheme
);
3148 CFStringAppend(urlString
, CFSTR("://"));
3149 hadPrePathComponent
= true;
3151 if (comp
->user
|| comp
->password
) {
3153 CFStringAppend(urlString
, comp
->user
);
3155 if (comp
->password
) {
3156 CFStringAppend(urlString
, CFSTR(":"));
3157 CFStringAppend(urlString
, comp
->password
);
3159 CFStringAppend(urlString
, CFSTR("@"));
3160 hadPrePathComponent
= true;
3163 CFStringAppend(urlString
, comp
->host
);
3164 hadPrePathComponent
= true;
3166 if (comp
->port
!= kCFNotFound
) {
3167 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3168 hadPrePathComponent
= true;
3171 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3172 CFStringAppend(urlString
, CFSTR("/"));
3174 if (comp
->pathComponents
) {
3175 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3176 CFStringAppend(urlString
, pathStr
);
3179 if (comp
->parameterString
) {
3180 CFStringAppend(urlString
, CFSTR(";"));
3181 CFStringAppend(urlString
, comp
->parameterString
);
3184 CFStringAppend(urlString
, CFSTR("?"));
3185 CFStringAppend(urlString
, comp
->query
);
3187 if (comp
->fragment
) {
3188 CFStringAppend(urlString
, CFSTR("#"));
3189 CFStringAppend(urlString
, comp
->fragment
);
3191 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3192 CFRelease(urlString
);
3196 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3197 CFAllocatorRef alloc
= CFGetAllocator(url
);
3198 CFURLComponentsRFC1808 oldComp
;
3200 if (!decomposeToRFC1808(url
, &oldComp
)) {
3203 comp
->scheme
= oldComp
.scheme
;
3205 if (oldComp
.password
) {
3206 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3207 CFRelease(oldComp
.password
);
3208 CFRelease(oldComp
.user
);
3210 comp
->userinfo
= oldComp
.user
;
3213 comp
->userinfo
= NULL
;
3215 comp
->host
= oldComp
.host
;
3216 comp
->port
= oldComp
.port
;
3217 if (!oldComp
.parameterString
) {
3218 comp
->pathComponents
= oldComp
.pathComponents
;
3220 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3221 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3222 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3223 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3225 CFRelease(oldComp
.pathComponents
);
3226 CFRelease(oldComp
.parameterString
);
3228 comp
->query
= oldComp
.query
;
3229 comp
->fragment
= oldComp
.fragment
;
3230 comp
->baseURL
= oldComp
.baseURL
;
3234 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3235 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3236 CFURLRef base
= comp
->baseURL
;
3238 Boolean hadPrePathComponent
= false;
3241 CFStringAppend(urlString
, comp
->scheme
);
3242 CFStringAppend(urlString
, CFSTR("://"));
3243 hadPrePathComponent
= true;
3245 if (comp
->userinfo
) {
3246 CFStringAppend(urlString
, comp
->userinfo
);
3247 CFStringAppend(urlString
, CFSTR("@"));
3248 hadPrePathComponent
= true;
3251 CFStringAppend(urlString
, comp
->host
);
3252 if (comp
->port
!= kCFNotFound
) {
3253 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3255 hadPrePathComponent
= true;
3257 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3258 CFStringAppend(urlString
, CFSTR("/"));
3260 if (comp
->pathComponents
) {
3261 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3262 CFStringAppend(urlString
, pathStr
);
3266 CFStringAppend(urlString
, CFSTR("?"));
3267 CFStringAppend(urlString
, comp
->query
);
3269 if (comp
->fragment
) {
3270 CFStringAppend(urlString
, CFSTR("#"));
3271 CFStringAppend(urlString
, comp
->fragment
);
3273 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3274 CFRelease(urlString
);
3278 #undef CFURLCopyComponents
3279 #undef CFURLCreateFromComponents
3282 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3283 url
= _CFURLFromNSURL(url
);
3284 switch (decompositionType
) {
3285 case kCFURLComponentDecompositionNonHierarchical
:
3286 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3287 case kCFURLComponentDecompositionRFC1808
:
3288 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3289 case kCFURLComponentDecompositionRFC2396
:
3290 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3297 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3298 switch (decompositionType
) {
3299 case kCFURLComponentDecompositionNonHierarchical
:
3300 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3301 case kCFURLComponentDecompositionRFC1808
:
3302 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3303 case kCFURLComponentDecompositionRFC2396
:
3304 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3310 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3311 return _getReserved(url
);
3314 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3315 _setReserved ( (struct __CFURL
*) url
, ptr
);
3319 /* File system stuff */
3321 /* HFSPath<->URLPath functions at the bottom of the file */
3322 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3324 CFMutableArrayRef urlComponents
= NULL
;
3327 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3328 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3331 CFStringRef str
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, 0);
3332 if (CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3333 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR("")); // So we get a leading '/' below
3334 i
= 2; // Skip over the drive letter and the empty string we just inserted
3337 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3338 CFStringRef fileComp
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
,i
);
3339 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3341 // Couldn't decode fileComp
3342 CFRelease(urlComponents
);
3345 if (urlComp
!= fileComp
) {
3346 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3352 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3353 CFArrayAppendValue(urlComponents
, CFSTR(""));
3355 return urlComponents
;
3358 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3359 CFArrayRef urlComponents
;
3362 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3363 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
);
3364 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3366 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3367 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3368 CFRelease(urlComponents
);
3372 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
) {
3373 CFStringRef pathString
= _replacePathIllegalCharacters(path
, alloc
, true);
3374 if (isDirectory
&& CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
)-1) != '/') {
3375 CFStringRef tmp
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@/"), pathString
);
3376 CFRelease(pathString
);
3382 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3383 // This is the easiest case; just remove the percent escape codes and we're done
3384 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3386 CFIndex length
= CFStringGetLength(result
);
3387 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3388 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3397 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3398 // Check for a drive letter, then flip all the slashes
3400 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
3401 SInt32 count
= CFArrayGetCount(tmp
);
3402 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
3403 CFStringRef newPath
;
3408 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
3409 CFArrayRemoveValueAtIndex(components
, count
-1);
3413 if (count
> 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
, 0)) == 0) {
3414 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
3415 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, (CFStringRef
)CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
3419 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
3421 CFArrayRemoveValueAtIndex(components
, 0);
3423 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
3424 CFArraySetValueAtIndex(components
, 0, driveStr
);
3425 CFRelease(driveStr
);
3428 CFRelease(firstComponent
);
3431 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
3432 CFRelease(components
);
3433 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
3438 // converts url from a file system path representation to a standard representation
3439 static void _convertToURLRepresentation(struct __CFURL
*url
) {
3440 CFStringRef path
= NULL
;
3441 Boolean isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3442 CFAllocatorRef alloc
= CFGetAllocator(url
);
3444 #if DEBUG_URL_MEMORY_USAGE
3445 numFileURLsConverted
++;
3448 switch (URL_PATH_TYPE(url
)) {
3449 case kCFURLPOSIXPathStyle
:
3450 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3451 path
= (CFStringRef
)CFRetain(url
->_string
);
3453 path
= POSIXPathToURLPath(url
->_string
, alloc
, isDir
);
3456 case kCFURLWindowsPathStyle
:
3457 path
= WindowsPathToURLPath(url
->_string
, alloc
, isDir
);
3460 CFAssert2(path
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, url
);
3462 CFMutableStringRef str
= CFStringCreateMutable(alloc
, 0);
3463 CFStringAppend(str
, CFSTR("file://localhost"));
3464 CFStringAppend(str
, path
);
3465 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
;
3466 CFRelease(url
->_string
);
3468 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
3469 url
->ranges
[0] = CFRangeMake(0, 4);
3470 url
->ranges
[1] = CFRangeMake(7, 9);
3471 url
->ranges
[2] = CFRangeMake(16, CFStringGetLength(path
));
3474 CFRelease(url
->_string
);
3475 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_PARSED
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3476 url
->_string
= path
;
3477 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
3478 *(url
->ranges
) = CFRangeMake(0, CFStringGetLength(path
));
3482 // relativeURL is known to be a file system URL whose base is a matching file system URL
3483 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
) {
3484 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
3485 CFURLPathStyle fsType
= URL_PATH_TYPE(relativeURL
);
3486 CFURLRef base
= relativeURL
->_base
;
3487 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, (base
->_flags
& IS_DIRECTORY
) != 0, fsType
, alloc
);
3488 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, fsType
, (relativeURL
->_flags
& IS_DIRECTORY
) != 0);
3493 // Caller must release the returned string
3494 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
3495 CFIndex baseLen
= CFStringGetLength(basePath
);
3496 CFIndex relLen
= CFStringGetLength(relativePath
);
3497 UniChar pathDelimiter
= PATH_DELIM_FOR_TYPE(fsType
);
3498 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
3499 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
3501 if (buf
[baseLen
-1] != pathDelimiter
) {
3502 buf
[baseLen
] = pathDelimiter
;
3506 UniChar
*ptr
= buf
+ baseLen
- 1;
3507 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
3510 baseLen
= ptr
- buf
+ 1;
3512 if (fsType
== kCFURLHFSPathStyle
) {
3513 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3516 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
3517 *(buf
+ baseLen
+ relLen
) = '\0';
3518 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
3521 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
3522 CFURLRef url
= NULL
;
3523 uint8_t buf
[CFMaxPathSize
+ 1];
3524 if (_CFGetCurrentDirectory((char *)buf
, CFMaxPathLength
)) {
3525 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen((char *)buf
), true);
3530 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
3531 Boolean isAbsolute
= true;
3533 CFURLRef baseURL
, result
;
3535 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3536 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__
);
3538 len
= CFStringGetLength(filePath
);
3541 case kCFURLPOSIXPathStyle
:
3542 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3544 case kCFURLWindowsPathStyle
:
3545 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3546 /* Absolute path under Win32 can begin with "\\"
3549 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3551 case kCFURLHFSPathStyle
:
3552 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3558 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3560 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
3561 if (baseURL
) CFRelease(baseURL
);
3565 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
3567 Boolean isAbsolute
= true, releaseFilePath
= false;
3568 UniChar pathDelim
= '\0';
3571 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
3572 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3574 len
= CFStringGetLength(filePath
);
3577 case kCFURLPOSIXPathStyle
:
3578 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3582 case kCFURLWindowsPathStyle
:
3583 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3584 /* Absolute path under Win32 can begin with "\\"
3588 isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3591 case kCFURLHFSPathStyle
:
3592 { CFRange fullStrRange
= CFRangeMake( 0, CFStringGetLength( filePath
) );
3594 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3597 if ( _CFExecutableLinkedOnOrAfter( CFSystemVersionTiger
) &&
3598 filePath
&& CFStringFindWithOptions( filePath
, CFSTR("::"), fullStrRange
, 0, NULL
) ) {
3599 UniChar
* chars
= (UniChar
*) malloc( fullStrRange
.length
* sizeof( UniChar
) );
3600 CFIndex index
, writeIndex
, firstColonOffset
= -1;
3602 CFStringGetCharacters( filePath
, fullStrRange
, chars
);
3604 for ( index
= 0, writeIndex
= 0 ; index
< fullStrRange
.length
; index
++ ) {
3605 if ( chars
[ index
] == ':' ) {
3606 if ( index
+ 1 < fullStrRange
.length
&& chars
[ index
+ 1 ] == ':' ) {
3608 // Don't let :: go off the 'top' of the path -- which means that there always has to be at
3609 // least one ':' to the left of the current write position to go back to.
3610 if ( writeIndex
> 0 && firstColonOffset
>= 0 )
3613 while ( writeIndex
> 0 && writeIndex
>= firstColonOffset
&& chars
[ writeIndex
] != ':' )
3616 index
++; // skip over the first ':', so we replace the ':' which is there with a new one
3619 if ( firstColonOffset
== -1 )
3620 firstColonOffset
= writeIndex
;
3623 chars
[ writeIndex
++ ] = chars
[ index
];
3626 if ( releaseFilePath
&& filePath
)
3627 CFRelease( filePath
);
3629 filePath
= CFStringCreateWithCharacters( allocator
, chars
, writeIndex
);
3630 // reset len because a canonical HFS path can be a different length than the original CFString
3631 len
= CFStringGetLength(filePath
);
3632 releaseFilePath
= true;
3644 if (isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) != pathDelim
) {
3645 CFMutableStringRef tempRef
= CFStringCreateMutable(allocator
, 0);
3646 CFStringAppend(tempRef
, filePath
);
3647 CFStringAppendCharacters(tempRef
, &pathDelim
, 1);
3648 if ( releaseFilePath
&& filePath
) CFRelease( filePath
);
3650 releaseFilePath
= true;
3651 } else if (!isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) == pathDelim
) {
3652 if (len
== 1 || CFStringGetCharacterAtIndex(filePath
, len
-2) == pathDelim
) {
3653 // Override isDirectory
3656 CFStringRef tempRef
= CFStringCreateWithSubstring(allocator
, filePath
, CFRangeMake(0, len
-1));
3657 if ( releaseFilePath
&& filePath
)
3658 CFRelease( filePath
);
3660 releaseFilePath
= true;
3663 if (!filePath
|| CFStringGetLength(filePath
) == 0) {
3664 if (releaseFilePath
&& filePath
) CFRelease(filePath
);
3667 url
= _CFURLAlloc(allocator
);
3668 _CFURLInit((struct __CFURL
*)url
, filePath
, fsType
, baseURL
);
3669 if (releaseFilePath
) CFRelease(filePath
);
3670 if (isDirectory
) ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
3671 if (fsType
== kCFURLPOSIXPathStyle
) {
3672 // 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
3673 // 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
3674 CFStringInlineBuffer buf
;
3675 Boolean sawSlash
= FALSE
;
3676 Boolean mustPrependDotSlash
= FALSE
;
3677 CFIndex idx
, length
= CFStringGetLength(url
->_string
);
3678 CFStringInitInlineBuffer(url
->_string
, &buf
, CFRangeMake(0, length
));
3679 for (idx
= 0; idx
< length
; idx
++) {
3680 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
3681 if (!isPathLegalCharacter(ch
)) break;
3685 } else if (ch
== ':') {
3686 mustPrependDotSlash
= TRUE
;
3690 if (idx
== length
) {
3691 ((struct __CFURL
*)url
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
3693 if (mustPrependDotSlash
) {
3694 CFMutableStringRef newString
= CFStringCreateMutable(allocator
, 0);
3695 CFStringAppend(newString
, CFSTR("./"));
3696 CFStringAppend(newString
, url
->_string
);
3697 CFRelease(url
->_string
);
3698 ((struct __CFURL
*)url
)->_string
= newString
;
3704 CF_EXPORT CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
3705 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
3706 return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL
), anURL
, pathStyle
, false);
3709 // 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
3710 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
3711 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
3712 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
3713 CFStringRef relPath
= NULL
;
3715 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3716 // We can grope the ivars
3717 CFURLPathStyle myType
= URL_PATH_TYPE(anURL
);
3718 if (myType
== fsType
) {
3719 relPath
= (CFStringRef
)CFRetain(anURL
->_string
);
3720 } else if (fsType
== kCFURLPOSIXPathStyle
&& myType
== FULL_URL_REPRESENTATION
) {
3721 if (!(anURL
->_flags
& IS_PARSED
)) {
3722 _parseComponentsOfURL(anURL
);
3724 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3725 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
3730 if (relPath
== NULL
) {
3731 CFStringRef urlPath
= CFURLCopyPath(anURL
);
3732 CFStringEncoding enc
= (anURL
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: anURL
->_encoding
;
3735 case kCFURLPOSIXPathStyle
:
3736 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
3738 case kCFURLHFSPathStyle
:
3741 case kCFURLWindowsPathStyle
:
3742 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
3745 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
3751 // For Tiger, leave this behavior in for all path types. For Leopard, it would be nice to remove this entirely
3752 // and do a linked-on-or-later check so we don't break third parties.
3753 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
3754 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
3755 if ( relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == PATH_DELIM_FOR_TYPE(fsType
)) {
3756 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
3761 // Note that !resolveAgainstBase implies !base
3762 if (!basePath
|| !relPath
) {
3765 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
3766 CFRelease(basePath
);
3772 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
3774 CFAllocatorRef alloc
= CFGetAllocator(url
);
3776 if (!url
) return false;
3777 #if DEPLOYMENT_TARGET_MACOSX
3778 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
3780 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
3788 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
3789 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
3791 if (!path
) return NULL
;
3792 #if DEPLOYMENT_TARGET_MACOSX
3793 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
);
3799 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
3800 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
3802 if (!path
) return NULL
;
3803 #if DEPLOYMENT_TARGET_MACOSX
3804 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
, baseURL
);
3811 /******************************/
3812 /* Support for path utilities */
3813 /******************************/
3815 // Assumes url is a CFURL (not an Obj-C NSURL)
3816 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
3817 UInt32 pathType
= URL_PATH_TYPE(url
);
3818 CFRange pathRg
, componentRg
;
3820 if (pathType
== FULL_URL_REPRESENTATION
) {
3821 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3822 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3824 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
3827 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
3831 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == PATH_DELIM_FOR_TYPE(pathType
)) {
3833 if (pathRg
.length
== 0) {
3838 if (CFStringFindWithOptions(url
->_string
, PATH_DELIM_AS_STRING_FOR_TYPE(pathType
), pathRg
, kCFCompareBackwards
, &componentRg
)) {
3839 componentRg
.location
++;
3840 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
3842 componentRg
= pathRg
;
3847 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
3850 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
3851 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
3854 if (!path
) return NULL
;
3855 rg
= CFRangeMake(0, CFStringGetLength(path
));
3856 length
= rg
.length
; // Remember this for comparison later
3857 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/') {
3860 if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
3861 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
3862 rg
.location
= compRg
.location
+ 1;
3864 if (rg
.location
== 0 && rg
.length
== length
) {
3867 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), path
, rg
);
3871 CFRange rg
= _rangeOfLastPathComponent(url
);
3872 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
3874 return (CFStringRef
)CFRetain(CFSTR(""));
3876 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url
))) {
3877 return (CFStringRef
)CFRetain(CFSTR("/"));
3879 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
3880 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
&& !(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
3882 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
3883 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
3885 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
3894 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
3895 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
3896 CFStringRef ext
= NULL
;
3899 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
3900 if (rg
.location
!= kCFNotFound
) {
3902 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
3903 if (rg
.length
> 0) {
3904 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
3906 ext
= (CFStringRef
)CFRetain(CFSTR(""));
3909 CFRelease(lastPathComp
);
3914 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
3917 url
= _CFURLFromNSURL(url
);
3918 __CFGenericValidateType(url
, __kCFURLTypeID
);
3919 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
3921 fsType
= URL_PATH_TYPE(url
);
3922 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(pathComponent
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(pathComponent
)), 0, NULL
)) {
3923 // Must convert to full representation, and then work with it
3924 fsType
= FULL_URL_REPRESENTATION
;
3925 _convertToURLRepresentation((struct __CFURL
*)url
);
3928 if (fsType
== FULL_URL_REPRESENTATION
) {
3929 CFMutableStringRef newString
;
3930 CFStringRef newComp
;
3932 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3933 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
3935 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
3936 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
3937 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3938 if (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
3939 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
3942 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
3944 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
3947 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
3948 CFRelease(newString
);
3950 UniChar pathDelim
= PATH_DELIM_FOR_TYPE(fsType
);
3951 CFStringRef newString
;
3952 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != pathDelim
) {
3954 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@%c"), url
->_string
, pathDelim
, pathComponent
, pathDelim
);
3956 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@"), url
->_string
, pathDelim
, pathComponent
);
3960 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@%c"), url
->_string
, pathComponent
, pathDelim
);
3962 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@"), url
->_string
, pathComponent
);
3965 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, isDirectory
, url
->_base
);
3966 CFRelease(newString
);
3971 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
3973 CFMutableStringRef newString
;
3974 CFRange lastCompRg
, pathRg
;
3975 Boolean appendDotDot
= false;
3978 url
= _CFURLFromNSURL(url
);
3979 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
3980 __CFGenericValidateType(url
, __kCFURLTypeID
);
3982 fsType
= URL_PATH_TYPE(url
);
3983 if (fsType
== FULL_URL_REPRESENTATION
) {
3984 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3985 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
3986 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3988 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
3990 lastCompRg
= _rangeOfLastPathComponent(url
);
3991 if (lastCompRg
.length
== 0) {
3992 appendDotDot
= true;
3993 } else if (lastCompRg
.length
== 1) {
3994 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
3995 if (ch
== '.' || ch
== PATH_DELIM_FOR_TYPE(fsType
)) {
3996 appendDotDot
= true;
3998 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
3999 appendDotDot
= true;
4002 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4005 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != PATH_DELIM_FOR_TYPE(fsType
)) {
4006 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4009 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
4011 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4013 // 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 "/.".
4014 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
4015 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == PATH_DELIM_FOR_TYPE(fsType
)) {
4016 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
4017 } else if (pathRg
.length
+delta
== 5) {
4018 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
4021 } else if (lastCompRg
.location
== pathRg
.location
) {
4022 CFStringReplace(newString
, pathRg
, CFSTR("."));
4023 CFStringInsert(newString
, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
4025 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
4027 if (fsType
== FULL_URL_REPRESENTATION
) {
4028 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4030 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, true, url
->_base
);
4032 CFRelease(newString
);
4036 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
4037 CFMutableStringRef newString
;
4040 CFURLPathStyle fsType
;
4042 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4043 url
= _CFURLFromNSURL(url
);
4044 __CFGenericValidateType(url
, __kCFURLTypeID
);
4045 __CFGenericValidateType(extension
, CFStringGetTypeID());
4047 rg
= _rangeOfLastPathComponent(url
);
4048 if (rg
.location
< 0) return NULL
; // No path
4049 fsType
= URL_PATH_TYPE(url
);
4050 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(extension
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(extension
)), 0, NULL
)) {
4051 _convertToURLRepresentation((struct __CFURL
*)url
);
4052 fsType
= FULL_URL_REPRESENTATION
;
4053 rg
= _rangeOfLastPathComponent(url
);
4056 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4057 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
4058 if (fsType
== FULL_URL_REPRESENTATION
) {
4059 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4060 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
4062 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4064 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, extension
);
4065 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4067 CFRelease(newString
);
4071 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4075 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4076 url
= _CFURLFromNSURL(url
);
4077 __CFGenericValidateType(url
, __kCFURLTypeID
);
4078 rg
= _rangeOfLastPathComponent(url
);
4079 if (rg
.location
< 0) {
4081 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
4082 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4083 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
4084 CFStringDelete(newString
, dotRg
);
4085 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
) {
4086 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4088 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, URL_PATH_TYPE(url
), (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4090 CFRelease(newString
);
4092 result
= (CFURLRef
)CFRetain(url
);
4101 // keys and vals must have space for at least 4 key/value pairs. No argument can be NULL.
4102 // Caller must release values, but not keys
4103 static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url
, CFTypeRef
*keys
, CFTypeRef
*vals
, CFIndex
*count
) {
4104 CFAllocatorRef alloc
= CFGetAllocator(url
);
4105 CFURLRef base
= CFURLGetBaseURL(url
);
4106 keys
[0] = CFSTR("_CFURLStringType");
4107 keys
[1] = CFSTR("_CFURLString");
4108 keys
[2] = CFSTR("_CFURLBaseStringType");
4109 keys
[3] = CFSTR("_CFURLBaseURLString");
4110 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4111 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4112 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4113 vals
[1] = CFURLGetString(url
);
4115 SInt32 urlType
= URL_PATH_TYPE(url
);
4116 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4117 if (url
->_flags
& IS_DIRECTORY
) {
4118 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4119 vals
[1] = CFRetain(url
->_string
);
4121 vals
[1] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), url
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4124 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4125 vals
[1] = CFRetain(url
->_string
);
4127 vals
[1] = CFStringCreateWithSubstring(alloc
, url
->_string
, CFRangeMake(0, CFStringGetLength(url
->_string
) - 1));
4132 if (CF_IS_OBJC(__kCFURLTypeID
, base
)) {
4133 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4134 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4135 vals
[3] = CFURLGetString(base
);
4137 SInt32 urlType
= URL_PATH_TYPE(base
);
4138 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4139 if (base
->_flags
& IS_DIRECTORY
) {
4140 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) == PATH_DELIM_FOR_TYPE(urlType
)) {
4141 vals
[3] = CFRetain(base
->_string
);
4143 vals
[3] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), base
->_string
, PATH_DELIM_FOR_TYPE(urlType
));
4146 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) != PATH_DELIM_FOR_TYPE(urlType
)) {
4147 vals
[3] = CFRetain(base
->_string
);
4149 vals
[3] = CFStringCreateWithSubstring(alloc
, base
->_string
, CFRangeMake(0, CFStringGetLength(base
->_string
) - 1));
4159 // Private API for Finder to use
4160 CFPropertyListRef
_CFURLCopyPropertyListRepresentation(CFURLRef url
) {
4161 CFTypeRef keys
[4], vals
[4];
4162 CFDictionaryRef dict
;
4164 __CFURLCopyPropertyListKeysAndValues(url
, keys
, vals
, &count
);
4165 dict
= CFDictionaryCreate(CFGetAllocator(url
), keys
, vals
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4166 for (idx
= 0; idx
< count
; idx
++) {
4167 CFRelease(vals
[idx
]);
4172 CFURLRef
_CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc
, CFPropertyListRef pListRepresentation
) {
4173 CFStringRef baseString
, string
;
4174 CFNumberRef baseTypeNum
, urlTypeNum
;
4175 SInt32 baseType
, urlType
;
4176 CFURLRef baseURL
= NULL
, url
;
4177 CFDictionaryRef dict
= (CFDictionaryRef
)pListRepresentation
;
4179 // Start by getting all the pieces and verifying they're of the correct type.
4180 if (CFGetTypeID(pListRepresentation
) != CFDictionaryGetTypeID()) {
4183 string
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLString"));
4184 if (!string
|| CFGetTypeID(string
) != CFStringGetTypeID()) {
4187 urlTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLStringType"));
4188 if (!urlTypeNum
|| CFGetTypeID(urlTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum
, kCFNumberSInt32Type
, &urlType
) || (urlType
!= FULL_URL_REPRESENTATION
&& urlType
!= kCFURLPOSIXPathStyle
&& urlType
!= kCFURLHFSPathStyle
&& urlType
!= kCFURLWindowsPathStyle
)) {
4191 baseString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseURLString"));
4193 if (CFGetTypeID(baseString
) != CFStringGetTypeID()) {
4196 baseTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseStringType"));
4197 if (!baseTypeNum
|| CFGetTypeID(baseTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum
, kCFNumberSInt32Type
, &baseType
) ||
4198 (baseType
!= FULL_URL_REPRESENTATION
&& baseType
!= kCFURLPOSIXPathStyle
&& baseType
!= kCFURLHFSPathStyle
&& baseType
!= kCFURLWindowsPathStyle
)) {
4201 if (baseType
== FULL_URL_REPRESENTATION
) {
4202 baseURL
= _CFURLCreateWithArbitraryString(alloc
, baseString
, NULL
);
4204 baseURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, baseString
, (CFURLPathStyle
)baseType
, CFStringGetCharacterAtIndex(baseString
, CFStringGetLength(baseString
)-1) == PATH_DELIM_FOR_TYPE(baseType
), NULL
);
4207 if (urlType
== FULL_URL_REPRESENTATION
) {
4208 url
= _CFURLCreateWithArbitraryString(alloc
, string
, baseURL
);
4210 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, string
, (CFURLPathStyle
)urlType
, CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
)-1) == PATH_DELIM_FOR_TYPE(urlType
), baseURL
);
4212 if (baseURL
) CFRelease(baseURL
);