2 * Copyright (c) 2005 Apple Computer, 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"
34 #include "CFUtilitiesPriv.h"
39 #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__)
43 #include <CarbonCore/Files.h>
44 #include <CarbonCore/Aliases.h>
46 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
47 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
48 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
49 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
);
50 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
51 static CFStringRef
URLPathToHFSPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
);
52 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
53 extern CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
55 DEFINE_WEAK_CARBONCORE_FUNC(void, DisposeHandle
, (Handle A
), (A
))
56 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSNewAlias
, (const FSRef
*A
, const FSRef
*B
, AliasHandle
*C
), (A
, B
, C
), -3296)
57 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)
58 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)
59 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSMakeFSRefUnicode
, (const FSRef
*A
, UniCharCount B
, const UniChar
*C
, TextEncoding D
, FSRef
*E
), (A
, B
, C
, D
, E
), -3296)
60 DEFINE_WEAK_CARBONCORE_FUNC(OSStatus
, FSPathMakeRef
, (const uint8_t *A
, FSRef
*B
, Boolean
*C
), (A
, B
, C
), -3296)
61 DEFINE_WEAK_CARBONCORE_FUNC(OSStatus
, FSRefMakePath
, (const FSRef
*A
, uint8_t *B
, UInt32 C
), (A
, B
, C
), -3296)
62 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, FSpMakeFSRef
, (const FSSpec
*A
, FSRef
*B
), (A
, B
), -3296)
63 DEFINE_WEAK_CARBONCORE_FUNC(Size
, GetAliasSizeFromPtr
, (AliasPtr A
), (A
), 0)
64 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)
65 DEFINE_WEAK_CARBONCORE_FUNC(CFStringRef
, _FSCopyStrippedPathString
, (CFAllocatorRef A
, CFStringRef B
), (A
, B
), (B
? (CFStringRef
)CFRetain(B
) : NULL
))
67 DEFINE_WEAK_CARBONCORE_FUNC(OSErr
, _FSGetVolumeByName
, ( CFStringRef volumeNameRef
, FSVolumeRefNum
* vRefNumP
), ( volumeNameRef
, vRefNumP
), -3296 )
73 #include <sys/types.h>
77 #ifndef DEBUG_URL_MEMORY_USAGE
78 #define DEBUG_URL_MEMORY_USAGE 0
81 #if DEBUG_URL_MEMORY_USAGE
82 static CFAllocatorRef URLAllocator
= NULL
;
83 static UInt32 numFileURLsCreated
= 0;
84 static UInt32 numFileURLsConverted
= 0;
85 static UInt32 numFileURLsDealloced
= 0;
86 static UInt32 numURLs
= 0;
87 static UInt32 numDealloced
= 0;
88 static UInt32 numExtraDataAllocated
= 0;
89 static UInt32 numURLsWithBaseURL
= 0;
92 /* The bit flags in myURL->_flags */
93 #define HAS_SCHEME (0x0001)
94 #define HAS_USER (0x0002)
95 #define HAS_PASSWORD (0x0004)
96 #define HAS_HOST (0x0008)
97 #define HAS_PORT (0x0010)
98 #define HAS_PATH (0x0020)
99 #define HAS_PARAMETERS (0x0040)
100 #define HAS_QUERY (0x0080)
101 #define HAS_FRAGMENT (0x0100)
102 #define HAS_HTTP_SCHEME (0x0200)
103 // Last free bit (0x200) in lower word goes here!
104 #define IS_IPV6_ENCODED (0x0400)
105 #define IS_OLD_UTF8_STYLE (0x0800)
106 #define IS_DIRECTORY (0x1000)
107 #define IS_PARSED (0x2000)
108 #define IS_ABSOLUTE (0x4000)
109 #define IS_DECOMPOSABLE (0x8000)
111 #define PATH_TYPE_MASK (0x000F0000)
112 /* 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 */
113 #define POSIX_AND_URL_PATHS_MATCH (0x00100000)
114 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000)
116 /* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */
117 // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
118 // #define SCHEME_DIFFERS (0x00400000) unused
119 #define USER_DIFFERS (0x00800000)
120 #define PASSWORD_DIFFERS (0x01000000)
121 #define HOST_DIFFERS (0x02000000)
122 // 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
123 #define PORT_DIFFERS (0x04000000)
124 // #define PATH_DIFFERS (0x08000000) unused
125 // #define PARAMETERS_DIFFER (0x10000000) unused
126 // #define QUERY_DIFFERS (0x20000000) unused
127 // #define FRAGMENT_DIFfERS (0x40000000) unused
129 // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag
130 #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22)
132 // Other useful defines
133 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
134 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
135 #define FULL_URL_REPRESENTATION (0xF)
137 /* 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! */
138 #define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16)
139 #define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/'))
140 #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/")))
143 // In order to get the sizeof ( __CFURL ) < 32 bytes, move these items into a seperate structure which is
144 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
145 // either a sanitized string or a reserved pointer for URLHandle.
146 struct _CFURLAdditionalData
{
147 void *_reserved
; // Reserved for URLHandle's use.
148 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
152 CFRuntimeBase _cfBase
;
154 CFStringRef _string
; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above)
157 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set.
158 struct _CFURLAdditionalData
* extra
;
162 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
164 if ( url
&& url
->extra
)
165 return url
->extra
->_reserved
;
170 CF_INLINE CFMutableStringRef
_getSanitizedString ( const struct __CFURL
* url
)
172 if ( url
&& url
->extra
)
173 return url
->extra
->_sanitizedString
;
178 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
180 if ( url
&& ! url
->extra
)
181 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), 0 );
183 extra
->_reserved
= _getReserved( url
);
184 extra
->_sanitizedString
= _getSanitizedString( url
);
188 #if DEBUG_URL_MEMORY_USAGE
189 numExtraDataAllocated
++;
194 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
198 // Don't allocate extra space if we're just going to be storing NULL
199 if ( ! url
->extra
&& reserved
)
200 _CFURLAllocateExtraDataspace( url
);
203 url
->extra
->_reserved
= reserved
;
207 CF_INLINE
void _setSanitizedString ( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
211 // Don't allocate extra space if we're just going to be storing NULL
212 if ( ! url
->extra
&& sanitizedString
)
213 _CFURLAllocateExtraDataspace( url
);
216 url
->extra
->_sanitizedString
= sanitizedString
;
220 static void _convertToURLRepresentation(struct __CFURL
*url
);
221 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
);
222 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
223 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
224 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
225 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
226 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
227 static void computeSanitizedString(CFURLRef url
);
228 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
229 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
230 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
233 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
234 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->ranges
));
237 static Boolean _createOldUTF8StyleURLs
= false;
239 CF_INLINE Boolean
createOldUTF8StyleURLs(void) {
240 if (_createOldUTF8StyleURLs
) {
243 return !_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
);
246 // Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW
248 void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs
) {
249 _createOldUTF8StyleURLs
= createUTF8URLs
;
260 static const unsigned char sURLValidCharacters
[] = {
262 /* '!' 33 */ VALID
| UNRESERVED
| PATHVALID
,
265 /* '$' 36 */ VALID
| PATHVALID
,
267 /* '&' 38 */ VALID
| PATHVALID
,
268 /* ''' 39 */ VALID
| UNRESERVED
| PATHVALID
,
269 /* '(' 40 */ VALID
| UNRESERVED
| PATHVALID
,
270 /* ')' 41 */ VALID
| UNRESERVED
| PATHVALID
,
271 /* '*' 42 */ VALID
| UNRESERVED
| PATHVALID
,
272 /* '+' 43 */ VALID
| SCHEME
| PATHVALID
,
273 /* ',' 44 */ VALID
| PATHVALID
,
274 /* '-' 45 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
275 /* '.' 46 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
276 /* '/' 47 */ VALID
| PATHVALID
,
277 /* '0' 48 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
278 /* '1' 49 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
279 /* '2' 50 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
280 /* '3' 51 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
281 /* '4' 52 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
282 /* '5' 53 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
283 /* '6' 54 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
284 /* '7' 55 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
285 /* '8' 56 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
286 /* '9' 57 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
290 /* '=' 61 */ VALID
| PATHVALID
,
293 /* '@' 64 */ VALID
| PATHVALID
,
294 /* 'A' 65 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
295 /* 'B' 66 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
296 /* 'C' 67 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
297 /* 'D' 68 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
298 /* 'E' 69 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
299 /* 'F' 70 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
300 /* 'G' 71 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
301 /* 'H' 72 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
302 /* 'I' 73 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
303 /* 'J' 74 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
304 /* 'K' 75 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
305 /* 'L' 76 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
306 /* 'M' 77 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
307 /* 'N' 78 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
308 /* 'O' 79 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
309 /* 'P' 80 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
310 /* 'Q' 81 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
311 /* 'R' 82 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
312 /* 'S' 83 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
313 /* 'T' 84 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
314 /* 'U' 85 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
315 /* 'V' 86 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
316 /* 'W' 87 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
317 /* 'X' 88 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
318 /* 'Y' 89 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
319 /* 'Z' 90 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
324 /* '_' 95 */ VALID
| UNRESERVED
| PATHVALID
,
326 /* 'a' 97 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
327 /* 'b' 98 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
328 /* 'c' 99 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
329 /* 'd' 100 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
330 /* 'e' 101 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
331 /* 'f' 102 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
332 /* 'g' 103 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
333 /* 'h' 104 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
334 /* 'i' 105 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
335 /* 'j' 106 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
336 /* 'k' 107 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
337 /* 'l' 108 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
338 /* 'm' 109 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
339 /* 'n' 110 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
340 /* 'o' 111 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
341 /* 'p' 112 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
342 /* 'q' 113 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
343 /* 'r' 114 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
344 /* 's' 115 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
345 /* 't' 116 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
346 /* 'u' 117 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
347 /* 'v' 118 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
348 /* 'w' 119 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
349 /* 'x' 120 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
350 /* 'y' 121 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
351 /* 'z' 122 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
355 /* '~' 126 */ VALID
| UNRESERVED
| PATHVALID
,
359 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
360 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & VALID
) : false;
363 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
364 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & SCHEME
) : false;
367 // "Unreserved" as defined by RFC 2396
368 CF_INLINE Boolean
isUnreservedCharacter(UniChar ch
) {
369 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & UNRESERVED
) : false;
372 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
373 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & PATHVALID
) : false;
376 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
377 return ( ( 32 <= ch
) && ( ch
<= 127 ) ) ? ( sURLValidCharacters
[ ch
- 32 ] & HEXDIGIT
) : false;
380 // Returns false if ch1 or ch2 isn't properly formatted
381 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
383 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
384 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
385 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
388 *result
= (*result
) << 4;
389 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
390 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
391 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
397 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
398 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
401 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
402 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
403 CFAllocatorRef alloc
= CFGetAllocator(array
);
404 CFMutableArrayRef mArray
= NULL
;
405 CFIndex i
, c
= CFArrayGetCount(array
);
406 for (i
= 0; i
< c
; i
++) {
407 CFStringRef origComp
= CFArrayGetValueAtIndex(array
, i
);
408 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
409 if (!unescapedComp
) {
412 if (unescapedComp
!= origComp
) {
414 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
416 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
418 CFRelease(unescapedComp
);
421 if (mArray
) CFRelease(mArray
);
431 // 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.
432 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
433 if (preserveSlashes
) {
434 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?"), kCFStringEncodingUTF8
);
436 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
440 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
441 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
444 // 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
445 static Boolean
_hackToConvertSurrogates(UniChar highChar
, UniChar lowChar
, CFMutableStringRef str
) {
446 UniChar surrogate
[2];
447 uint8_t bytes
[6]; // Aki sez it should never take more than 6 bytes
450 surrogate
[0] = highChar
;
451 surrogate
[1] = lowChar
;
452 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &len
) != kCFStringEncodingConversionSuccess
) {
455 for (currByte
= bytes
; currByte
< bytes
+ len
; currByte
++) {
456 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
457 unsigned char high
, low
;
458 high
= ((*currByte
) & 0xf0) >> 4;
459 low
= (*currByte
) & 0x0f;
460 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
461 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
462 CFStringAppendCharacters(str
, escapeSequence
, 3);
467 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
468 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
469 uint8_t *bytePtr
= bytes
, *currByte
;
471 CFAllocatorRef alloc
= NULL
;
472 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
473 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
474 if (byteLength
<= 6) {
475 // The encoding cannot accomodate the character
478 alloc
= CFGetAllocator(str
);
479 bytePtr
= CFAllocatorAllocate(alloc
, byteLength
, 0);
480 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
481 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
485 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
486 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
487 unsigned char high
, low
;
488 high
= ((*currByte
) & 0xf0) >> 4;
489 low
= (*currByte
) & 0x0f;
490 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
491 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
492 CFStringAppendCharacters(str
, escapeSequence
, 3);
494 if (bytePtr
!= bytes
) {
495 CFAllocatorDeallocate(alloc
, bytePtr
);
500 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
501 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
502 CFMutableStringRef newStr
= NULL
;
505 CFRange percentRange
, searchRange
;
506 CFStringRef escapedStr
= NULL
;
507 CFMutableStringRef strForEscapedChar
= NULL
;
509 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
510 Boolean failed
= false;
512 if (!originalString
) return NULL
;
514 if (charactersToLeaveEscaped
== NULL
) {
515 return CFStringCreateCopy(alloc
, originalString
);
518 length
= CFStringGetLength(originalString
);
519 searchRange
= CFRangeMake(0, length
);
521 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
522 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
523 uint8_t numBytesExpected
;
527 // Make sure we have at least 2 more characters
528 if (length
- percentRange
.location
< 3) { failed
= true; break; }
530 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
531 // so we assume the percent character is legit, and let it pass into the string
532 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
533 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
534 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
535 if (!(bytes
[0] & 0x80)) {
536 numBytesExpected
= 1;
537 } else if (!(bytes
[0] & 0x20)) {
538 numBytesExpected
= 2;
539 } else if (!(bytes
[0] & 0x10)) {
540 numBytesExpected
= 3;
542 numBytesExpected
= 4;
544 if (numBytesExpected
== 1) {
545 // one byte sequence (most common case); handle this specially
546 escapedChar
= bytes
[0];
547 if (!strForEscapedChar
) {
548 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
550 escapedStr
= strForEscapedChar
;
553 // Make sure up front that we have enough characters
554 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
555 for (j
= 1; j
< numBytesExpected
; j
++) {
556 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
557 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
558 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
559 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
562 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
563 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
566 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
567 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
568 escapedChar
= 0xfeff;
569 if (!strForEscapedChar
) {
570 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
572 CFRelease(escapedStr
);
573 escapedStr
= strForEscapedChar
;
578 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
579 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
580 searchRange
.length
= length
- searchRange
.location
;
583 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
584 if (escapedStr
!= strForEscapedChar
) {
585 CFRelease(escapedStr
);
593 newStr
= CFStringCreateMutable(alloc
, length
);
595 if (percentRange
.location
- mark
> 0) {
596 // The creation of this temporary string is unfortunate.
597 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
598 CFStringAppend(newStr
, substring
);
599 CFRelease(substring
);
601 CFStringAppend(newStr
, escapedStr
);
602 if (escapedStr
!= strForEscapedChar
) {
603 CFRelease(escapedStr
);
606 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
609 if (escapedStr
&& escapedStr
!= strForEscapedChar
) CFRelease(escapedStr
);
610 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
612 if (newStr
) CFRelease(newStr
);
616 // Need to cat on the remainder of the string
617 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
618 CFStringAppend(newStr
, substring
);
619 CFRelease(substring
);
623 return CFStringCreateCopy(alloc
, originalString
);
628 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
629 if (enc
== kCFStringEncodingUTF8
) {
630 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
632 CFMutableStringRef newStr
= NULL
;
633 CFMutableStringRef escapedStr
= NULL
;
636 CFRange percentRange
, searchRange
;
637 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
638 Boolean failed
= false;
639 uint8_t byteBuffer
[8];
640 uint8_t *bytes
= byteBuffer
;
641 int capacityOfBytes
= 8;
643 if (!originalString
) return NULL
;
645 if (charactersToLeaveEscaped
== NULL
) {
646 return CFStringCreateCopy(alloc
, originalString
);
649 length
= CFStringGetLength(originalString
);
650 searchRange
= CFRangeMake(0, length
);
652 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
654 CFIndex percentLoc
= percentRange
.location
;
655 CFStringRef convertedString
;
656 int numBytesUsed
= 0;
658 // Make sure we have at least 2 more characters
659 if (length
- percentLoc
< 3) { failed
= true; break; }
661 if (numBytesUsed
== capacityOfBytes
) {
662 if (bytes
== byteBuffer
) {
663 bytes
= CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
664 memmove(bytes
, byteBuffer
, capacityOfBytes
);
665 capacityOfBytes
= 16;
667 capacityOfBytes
= 2*capacityOfBytes
;
668 bytes
= CFAllocatorReallocate(alloc
, bytes
, capacityOfBytes
* sizeof(uint8_t), 0);
672 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
674 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
676 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
678 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
679 searchRange
.location
= percentLoc
;
680 searchRange
.length
= length
- searchRange
.location
;
683 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
684 if (!convertedString
) {
690 newStr
= CFStringCreateMutable(alloc
, length
);
692 if (percentRange
.location
- mark
> 0) {
693 // The creation of this temporary string is unfortunate.
694 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
695 CFStringAppend(newStr
, substring
);
696 CFRelease(substring
);
700 CFStringAppend(newStr
, convertedString
);
701 CFRelease(convertedString
);
703 CFIndex i
, c
= CFStringGetLength(convertedString
);
705 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
707 for (i
= 0; i
< c
; i
++) {
708 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
709 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
710 CFStringAppendCharacters(newStr
, &ch1
, 1);
712 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
713 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
717 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
720 if (escapedStr
) CFRelease(escapedStr
);
721 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
723 if (newStr
) CFRelease(newStr
);
727 // Need to cat on the remainder of the string
728 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
729 CFStringAppend(newStr
, substring
);
730 CFRelease(substring
);
734 return CFStringCreateCopy(alloc
, originalString
);
740 static CFStringRef
_addPercentEscapesToString(CFAllocatorRef allocator
, CFStringRef originalString
, Boolean (*shouldReplaceChar
)(UniChar
, void*), CFIndex (*handlePercentChar
)(CFIndex
, CFStringRef
, CFStringRef
*, void *), CFStringEncoding encoding
, void *context
) {
741 CFMutableStringRef newString
= NULL
;
743 CFStringInlineBuffer buf
;
745 if (!originalString
) return NULL
;
746 length
= CFStringGetLength(originalString
);
747 if (length
== 0) return CFStringCreateCopy(allocator
, originalString
);
748 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
750 for (idx
= 0; idx
< length
; idx
++) {
751 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
752 Boolean shouldReplace
= shouldReplaceChar(ch
, context
);
754 // Perform the replacement
756 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
757 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
759 if (!_appendPercentEscapesForCharacter(ch
, encoding
, newString
)) {
760 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
761 if (encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1))) {
762 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
763 if (_hackToConvertSurrogates(ch
, CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1), newString
)) {
764 idx
++; // We consumed 2 characters, not 1
772 } else if (ch
== '%' && handlePercentChar
) {
773 CFStringRef replacementString
= NULL
;
774 CFIndex newIndex
= handlePercentChar(idx
, originalString
, &replacementString
, context
);
777 } else if (replacementString
) {
779 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
780 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
782 CFStringAppend(newString
, replacementString
);
783 CFRelease(replacementString
);
785 if (newIndex
== idx
) {
787 CFStringAppendCharacters(newString
, &ch
, 1);
790 if (!replacementString
&& newString
) {
792 for (tmpIndex
= idx
; tmpIndex
< newIndex
; tmpIndex
++) {
793 ch
= CFStringGetCharacterAtIndex(originalString
, idx
);
794 CFStringAppendCharacters(newString
, &ch
, 1);
799 } else if (newString
) {
800 CFStringAppendCharacters(newString
, &ch
, 1);
804 // Ran in to an encoding failure
805 if (newString
) CFRelease(newString
);
807 } else if (newString
) {
810 return CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
815 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
816 CFIndex i
, c
= CFStringGetLength(string
);
817 CFStringInlineBuffer buf
;
818 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
819 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
823 static Boolean
_shouldPercentReplaceChar(UniChar ch
, void *context
) {
824 CFStringRef unescape
= ((CFStringRef
*)context
)[0];
825 CFStringRef escape
= ((CFStringRef
*)context
)[1];
826 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
828 if (unescape
&& _stringContainsCharacter(unescape
, ch
)) {
829 shouldReplace
= false;
831 } else if (escape
&& _stringContainsCharacter(escape
, ch
)) {
832 shouldReplace
= true;
834 return shouldReplace
;
837 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
838 CFStringRef strings
[2];
839 strings
[0] = charactersToLeaveUnescaped
;
840 strings
[1] = legalURLCharactersToBeEscaped
;
841 return _addPercentEscapesToString(allocator
, originalString
, _shouldPercentReplaceChar
, NULL
, encoding
, strings
);
844 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
847 UInt32 pathType1
, pathType2
;
849 __CFGenericValidateType(cf1
, CFURLGetTypeID());
850 __CFGenericValidateType(cf2
, CFURLGetTypeID());
852 if (url1
== url2
) return true;
853 if ((url1
->_flags
& IS_PARSED
) && (url2
->_flags
& IS_PARSED
) && (url1
->_flags
& IS_DIRECTORY
) != (url2
->_flags
& IS_DIRECTORY
)) return false;
855 if (! url2
->_base
) return false;
856 if (!CFEqual( url1
->_base
, url2
->_base
)) return false;
857 } else if ( url2
->_base
) {
861 pathType1
= URL_PATH_TYPE(url1
);
862 pathType2
= URL_PATH_TYPE(url2
);
863 if (pathType1
== pathType2
) {
864 if (pathType1
!= FULL_URL_REPRESENTATION
) {
865 return CFEqual(url1
->_string
, url2
->_string
);
867 // Do not compare the original strings; compare the sanatized strings.
868 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
871 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
872 CFStringRef scheme1
= CFURLCopyScheme(url1
);
873 CFStringRef scheme2
= CFURLCopyScheme(url2
);
875 if (scheme1
&& scheme2
) {
876 eq
= CFEqual(scheme1
, scheme2
);
879 } else if (!scheme1
&& !scheme2
) {
883 if (scheme1
) CFRelease(scheme1
);
884 else CFRelease(scheme2
);
886 if (!eq
) return false;
888 if (pathType1
== FULL_URL_REPRESENTATION
) {
889 if (!(url1
->_flags
& IS_PARSED
)) {
890 _parseComponentsOfURL(url1
);
892 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
897 if (pathType2
== FULL_URL_REPRESENTATION
) {
898 if (!(url2
->_flags
& IS_PARSED
)) {
899 _parseComponentsOfURL(url2
);
901 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
906 // No help for it; we now must convert to the canonical representation and compare.
907 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
911 static UInt32
__CFURLHash(CFTypeRef cf
) {
912 /* 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. */
915 if (CFURLCanBeDecomposed(url
)) {
916 CFStringRef lastComp
= CFURLCopyLastPathComponent(url
);
917 CFStringRef hostNameRef
= CFURLCopyHostName(url
);
922 result
= CFHash(lastComp
);
928 result
^= CFHash( hostNameRef
);
929 CFRelease( hostNameRef
);
932 result
= CFHash(CFURLGetString(url
));
937 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
939 __CFGenericValidateType(cf
, CFURLGetTypeID());
941 CFRetain(url
->_string
);
944 // Do not dereference url->_base; it may be an ObjC object
945 return CFStringCreateWithFormat(CFGetAllocator(url
), NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
950 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
951 CFURLRef url
= (CFURLRef
)cf
;
953 CFAllocatorRef alloc
= CFGetAllocator(url
);
955 CFStringRef baseString
= CFCopyDescription(url
->_base
);
956 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@,\n\tbase = %@}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
, baseString
);
957 CFRelease(baseString
);
959 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@, base = (null)}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
);
964 #if DEBUG_URL_MEMORY_USAGE
966 extern __attribute((used
)) void __CFURLDumpMemRecord(void) {
967 CFStringRef str
= CFStringCreateWithFormat(NULL
, 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
);
970 // if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator);
974 static void __CFURLDeallocate(CFTypeRef cf
) {
976 CFAllocatorRef alloc
;
977 __CFGenericValidateType(cf
, CFURLGetTypeID());
978 alloc
= CFGetAllocator(url
);
979 #if DEBUG_URL_MEMORY_USAGE
981 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
982 numFileURLsDealloced
++;
985 if (url
->_string
) CFRelease(url
->_string
); // GC: 3879914
986 if (url
->_base
) CFRelease(url
->_base
);
987 if (url
->ranges
) CFAllocatorDeallocate(alloc
, url
->ranges
);
988 if (_getSanitizedString(url
)) CFRelease(_getSanitizedString(url
));
990 if ( url
->extra
!= NULL
)
991 CFAllocatorDeallocate( alloc
, url
->extra
);
994 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
996 static const CFRuntimeClass __CFURLClass
= {
1004 __CFURLCopyFormattingDescription
,
1005 __CFURLCopyDescription
1008 CONST_STRING_DECL(kCFURLFileScheme
, "file")
1009 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1011 __private_extern__
void __CFURLInitialize(void) {
1012 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1015 /* Toll-free bridging support; get the true CFURL from an NSURL */
1016 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1017 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, url
, "_cfurl");
1021 CFTypeID
CFURLGetTypeID(void) {
1022 return __kCFURLTypeID
;
1025 __private_extern__
void CFShowURL(CFURLRef url
) {
1027 fprintf(stdout
, "(null)\n");
1030 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1031 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1032 fprintf(stdout
, "ObjC bridged object}\n");
1035 fprintf(stdout
, "\n\tPath type: ");
1036 switch (URL_PATH_TYPE(url
)) {
1037 case kCFURLPOSIXPathStyle
:
1038 fprintf(stdout
, "POSIX");
1040 case kCFURLHFSPathStyle
:
1041 fprintf(stdout
, "HFS");
1043 case kCFURLWindowsPathStyle
:
1044 fprintf(stdout
, "NTFS");
1046 case FULL_URL_REPRESENTATION
:
1047 fprintf(stdout
, "Native URL");
1050 fprintf(stdout
, "UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url
));
1052 fprintf(stdout
, "\n\tRelative string: ");
1053 CFShow(url
->_string
);
1054 fprintf(stdout
, "\tBase URL: ");
1056 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1059 fprintf(stdout
, "(null)\n");
1061 fprintf(stdout
, "\tFlags: %p\n}\n", (const void*)url
->_flags
);
1065 /***************************************************/
1066 /* URL creation and String/Data creation from URLS */
1067 /***************************************************/
1068 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, const unsigned char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1069 CFIndex neededLength
;
1073 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1077 *freeCharacters
= false;
1081 *ustring
= CFStringGetCharactersPtr(string
);
1083 *useCString
= false;
1084 *freeCharacters
= false;
1088 *freeCharacters
= true;
1089 length
= CFStringGetLength(string
);
1090 rg
= CFRangeMake(0, length
);
1091 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1092 if (neededLength
== length
) {
1093 char *buf
= CFAllocatorAllocate(alloc
, length
, 0);
1094 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, buf
, length
, NULL
);
1098 UniChar
*buf
= CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1099 CFStringGetCharacters(string
, rg
, buf
);
1100 *useCString
= false;
1105 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1106 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1108 /* 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. */
1110 CFIndex idx
, base_idx
= 0;
1111 CFIndex string_length
;
1112 UInt32 flags
= (IS_PARSED
| *theFlags
);
1113 Boolean useCString
, freeCharacters
, isCompliant
;
1114 uint8_t numRanges
= 0;
1115 const unsigned char *cstring
= NULL
;
1116 const UniChar
*ustring
= NULL
;
1118 string_length
= CFStringGetLength(string
);
1119 constructBuffers(alloc
, string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1121 // Algorithm is as described in RFC 1808
1122 // 1: parse the fragment; remainder after left-most "#" is fragment
1123 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1124 if ('#' == STRING_CHAR(idx
)) {
1125 flags
|= HAS_FRAGMENT
;
1126 ranges
[8].location
= idx
+ 1;
1127 ranges
[8].length
= string_length
- (idx
+ 1);
1129 string_length
= idx
; // remove fragment from parse string
1133 // 2: parse the scheme
1134 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1135 UniChar ch
= STRING_CHAR(idx
);
1137 flags
|= HAS_SCHEME
;
1138 flags
|= IS_ABSOLUTE
;
1139 ranges
[0].location
= base_idx
;
1140 ranges
[0].length
= idx
;
1143 // optimization for http urls
1144 if (idx
== 4 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' &&
1145 STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p')
1147 flags
|= HAS_HTTP_SCHEME
;
1150 } else if (!scheme_valid(ch
)) {
1151 break; // invalid scheme character -- no scheme
1155 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1156 // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage
1157 // expects this to be treated identically to "scheme://" - REW, 12/08/03
1158 if (!(flags
& HAS_SCHEME
)) {
1160 } else if (base_idx
== string_length
) {
1161 isCompliant
= false;
1162 } else if (STRING_CHAR(base_idx
) != '/') {
1163 isCompliant
= false;
1169 // Clear the fragment flag if it's been set
1170 if (flags
& HAS_FRAGMENT
) {
1171 flags
&= (~HAS_FRAGMENT
);
1172 string_length
= CFStringGetLength(string
);
1174 (*theFlags
) = flags
;
1175 (*range
) = CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
1176 (*range
)->location
= ranges
[0].location
;
1177 (*range
)->length
= ranges
[0].length
;
1179 if (freeCharacters
) {
1180 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1184 // URL is 1808-compliant
1185 flags
|= IS_DECOMPOSABLE
;
1187 // 3: parse the network location and login
1188 if (2 <= (string_length
- base_idx
) && '/' == STRING_CHAR(base_idx
) && '/' == STRING_CHAR(base_idx
+1)) {
1189 CFIndex base
= 2 + base_idx
, extent
;
1190 for (idx
= base
; idx
< string_length
; idx
++) {
1191 if ('/' == STRING_CHAR(idx
) || '?' == STRING_CHAR(idx
)) break;
1195 // net_loc parts extend from base to extent (but not including), which might be to end of string
1196 // net location is "<user>:<password>@<host>:<port>"
1197 if (extent
!= base
) {
1198 for (idx
= base
; idx
< extent
; idx
++) {
1199 if ('@' == STRING_CHAR(idx
)) { // there is a user
1203 ranges
[1].location
= base
; // base of the user
1204 for (idx2
= base
; idx2
< idx
; idx2
++) {
1205 if (':' == STRING_CHAR(idx2
)) { // found a password separator
1206 flags
|= HAS_PASSWORD
;
1208 ranges
[2].location
= idx2
+1; // base of the password
1209 ranges
[2].length
= idx
-(idx2
+1); // password extent
1210 ranges
[1].length
= idx2
- base
; // user extent
1214 if (!(flags
& HAS_PASSWORD
)) {
1215 // user extends to the '@'
1216 ranges
[1].length
= idx
- base
; // user extent
1224 ranges
[3].location
= base
; // base of host
1226 // base has been advanced past the user and password if they existed
1227 for (idx
= base
; idx
< extent
; idx
++) {
1228 // IPV6 support (RFC 2732) DCJ June/10/2002
1229 if ('[' == STRING_CHAR(idx
)) { // starting IPV6 explicit address
1230 // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end
1231 for ( ; idx
< extent
; ++ idx
) {
1232 if ( ']' == STRING_CHAR(idx
)) {
1233 flags
|= IS_IPV6_ENCODED
;
1238 // there is a port if we see a colon. Only the last one is the port, though.
1239 else if ( ':' == STRING_CHAR(idx
)) {
1242 ranges
[4].location
= idx
+1; // base of port
1243 ranges
[4].length
= extent
- (idx
+1); // port extent
1244 ranges
[3].length
= idx
- base
; // host extent
1248 if (!(flags
& HAS_PORT
)) {
1249 ranges
[3].length
= extent
- base
; // host extent
1255 // 4: parse the query; remainder after left-most "?" is query
1256 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1257 if ('?' == STRING_CHAR(idx
)) {
1260 ranges
[7].location
= idx
+ 1;
1261 ranges
[7].length
= string_length
- (idx
+1);
1262 string_length
= idx
; // remove query from parse string
1267 // 5: parse the parameters; remainder after left-most ";" is parameters
1268 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1269 if (';' == STRING_CHAR(idx
)) {
1270 flags
|= HAS_PARAMETERS
;
1272 ranges
[6].location
= idx
+ 1;
1273 ranges
[6].length
= string_length
- (idx
+1);
1274 string_length
= idx
; // remove parameters from parse string
1279 // 6: parse the path; it's whatever's left between string_length & base_idx
1280 if (string_length
- base_idx
!= 0 || (flags
& NET_LOCATION_MASK
))
1282 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1288 pathRg
.location
= base_idx
;
1289 pathRg
.length
= string_length
- base_idx
;
1292 if (pathRg
.length
> 0) {
1293 Boolean sawPercent
= FALSE
;
1294 for (idx
= pathRg
.location
; idx
< string_length
; idx
++) {
1295 if ('%' == STRING_CHAR(idx
)) {
1301 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1304 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 1);
1307 } else if (ch
== '.') {
1308 if (pathRg
.length
== 1) {
1311 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 2);
1314 } else if (ch
!= '.') {
1316 } else if (pathRg
.length
== 2) {
1319 isDir
= (STRING_CHAR(pathRg
.location
+ pathRg
.length
- 3) == '/');
1326 isDir
= (baseURL
!= NULL
) ? CFURLHasDirectoryPath(baseURL
) : false;
1329 flags
|= IS_DIRECTORY
;
1333 if (freeCharacters
) {
1334 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1336 (*theFlags
) = flags
;
1337 (*range
) = CFAllocatorAllocate(alloc
, sizeof(CFRange
)*numRanges
, 0);
1339 for (idx
= 0, flags
= 1; flags
!= (1<<9); flags
= (flags
<<1), idx
++) {
1340 if ((*theFlags
) & flags
) {
1341 (*range
)[numRanges
] = ranges
[idx
];
1347 static Boolean
scanCharacters(CFAllocatorRef alloc
, CFMutableStringRef
*escapedString
, UInt32
*flags
, const unsigned char *cstring
, const UniChar
*ustring
, Boolean useCString
, CFIndex base
, CFIndex end
, CFIndex
*mark
, UInt32 componentFlag
, CFStringEncoding encoding
) {
1349 Boolean sawIllegalChar
= false;
1350 for (idx
= base
; idx
< end
; idx
++) {
1351 Boolean shouldEscape
;
1352 UniChar ch
= STRING_CHAR(idx
);
1353 if (isURLLegalCharacter(ch
)) {
1354 if ((componentFlag
== HAS_USER
|| componentFlag
== HAS_PASSWORD
) && (ch
== '/' || ch
== '?' || ch
== '@')) {
1355 shouldEscape
= true;
1357 shouldEscape
= false;
1359 } else if (ch
== '%' && idx
+ 2 < end
&& isHexDigit(STRING_CHAR(idx
+ 1)) && isHexDigit(STRING_CHAR(idx
+2))) {
1360 shouldEscape
= false;
1361 } else if (componentFlag
== HAS_HOST
&& ((idx
== base
&& ch
== '[') || (idx
== end
-1 && ch
== ']'))) {
1362 shouldEscape
= false;
1364 shouldEscape
= true;
1366 if (!shouldEscape
) continue;
1368 sawIllegalChar
= true;
1369 if (componentFlag
&& flags
) {
1370 *flags
|= (componentFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
);
1372 if (!*escapedString
) {
1373 *escapedString
= CFStringCreateMutable(alloc
, 0);
1376 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, &(cstring
[*mark
]), idx
- *mark
, kCFStringEncodingISOLatin1
, false);
1377 CFStringAppend(*escapedString
, tempString
);
1378 CFRelease(tempString
);
1380 CFStringAppendCharacters(*escapedString
, &(ustring
[*mark
]), idx
- *mark
);
1383 _appendPercentEscapesForCharacter(ch
, encoding
, *escapedString
); // This can never fail because anURL->_string was constructed from the encoding passed in
1385 return sawIllegalChar
;
1388 static void computeSanitizedString(CFURLRef url
) {
1389 CFAllocatorRef alloc
= CFGetAllocator(url
);
1390 CFIndex string_length
= CFStringGetLength(url
->_string
);
1391 Boolean useCString
, freeCharacters
;
1392 const unsigned char *cstring
= NULL
;
1393 const UniChar
*ustring
= NULL
;
1394 CFIndex base
; // where to scan from
1395 CFIndex mark
; // first character not-yet copied to sanitized string
1396 if (!(url
->_flags
& IS_PARSED
)) {
1397 _parseComponentsOfURL(url
);
1399 constructBuffers(alloc
, url
->_string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1400 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1401 // Impossible to have a problem character in the scheme
1402 CFMutableStringRef sanitizedString
= NULL
;
1403 base
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
).length
+ 1;
1405 if (!scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1406 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1408 if ( sanitizedString
) {
1409 _setSanitizedString( (struct __CFURL
*) url
, sanitizedString
);
1412 // Go component by component
1413 CFIndex currentComponent
= HAS_USER
;
1415 CFMutableStringRef sanitizedString
= NULL
;
1416 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1417 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->ranges
, currentComponent
);
1418 if (componentRange
.location
!= kCFNotFound
) {
1419 scanCharacters(alloc
, & sanitizedString
, &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1421 currentComponent
= currentComponent
<< 1;
1423 if (sanitizedString
) {
1424 _setSanitizedString((struct __CFURL
*)url
, sanitizedString
);
1426 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1429 if (_getSanitizedString(url
) && mark
!= string_length
) {
1431 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, &(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1432 CFStringAppend(_getSanitizedString(url
), tempString
);
1433 CFRelease(tempString
);
1435 CFStringAppendCharacters(_getSanitizedString(url
), &(ustring
[mark
]), string_length
- mark
);
1438 if (freeCharacters
) {
1439 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1444 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1445 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1446 CFIndex string_length
= CFStringGetLength(comp
);
1447 Boolean useCString
, freeCharacters
;
1448 const unsigned char *cstring
= NULL
;
1449 const UniChar
*ustring
= NULL
;
1450 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1451 CFMutableStringRef result
= NULL
;
1453 constructBuffers(alloc
, comp
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1454 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1456 if (mark
< string_length
) {
1458 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, &(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1459 CFStringAppend(result
, tempString
);
1460 CFRelease(tempString
);
1462 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1466 // This should nevr happen
1468 result
= (CFMutableStringRef
)comp
;
1470 if (freeCharacters
) {
1471 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1477 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1478 struct __CFURL
*url
;
1479 #if DEBUG_URL_MEMORY_USAGE
1481 // if (!URLAllocator) {
1482 // URLAllocator = CFCountingAllocatorCreate(NULL);
1484 allocator
= URLAllocator
;
1486 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1489 if (createOldUTF8StyleURLs()) {
1490 url
->_flags
|= IS_OLD_UTF8_STYLE
;
1492 url
->_string
= NULL
;
1495 // url->_reserved = NULL;
1496 url
->_encoding
= kCFStringEncodingUTF8
;
1497 // url->_sanatizedString = NULL;
1503 // 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.
1504 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, UInt32 fsType
, CFURLRef base
) {
1505 CFAssert1(URLString
!= NULL
&& CFGetTypeID(URLString
) == CFStringGetTypeID() && CFStringGetLength(URLString
) != 0, __kCFLogAssertion
, "%s(): internal CF error; empty string encountered", __PRETTY_FUNCTION__
);
1506 CFAssert2((fsType
== FULL_URL_REPRESENTATION
) || (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) || (fsType
== kCFURLHFSPathStyle
), __kCFLogAssertion
, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__
, fsType
);
1508 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1509 url
->_string
= CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1510 url
->_flags
|= (fsType
<< 16);
1512 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1514 #if DEBUG_URL_MEMORY_USAGE
1515 if (fsType
!= FULL_URL_REPRESENTATION
) {
1516 numFileURLsCreated
++;
1519 numURLsWithBaseURL
++;
1523 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1524 CFIndex len
= CFStringGetLength(path
);
1525 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1526 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1527 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1529 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1530 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1533 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1534 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1537 // Exported for Foundation's use
1538 CF_EXPORT Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1539 // 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.
1540 // 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
1541 CFStringInlineBuffer stringBuffer
;
1542 CFIndex idx
= 0, length
;
1543 Boolean sawHash
= false;
1545 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1548 length
= CFStringGetLength(string
);
1549 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1550 while (idx
< length
) {
1551 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1554 // Make sure that two valid hex digits follow a '%' character
1556 if ( idx
+ 2 > length
)
1558 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1559 idx
= -1; // To guarantee index < length, and our failure case is triggered
1563 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1565 if (! isHexDigit(ch
) ) {
1566 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1570 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1572 if (! isHexDigit(ch
) ) {
1573 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1580 if (ch
== '[' || ch
== ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1586 if ( isURLLegalCharacter( ch
) )
1596 CF_EXPORT
void _CFURLInitWithString(CFURLRef myURL
, CFStringRef string
, CFURLRef baseURL
) {
1597 struct __CFURL
*url
= (struct __CFURL
*)myURL
; // Supress annoying compile warnings
1598 Boolean isAbsolute
= false;
1599 CFRange colon
= CFStringFind(string
, CFSTR(":"), 0);
1600 if (colon
.location
!= kCFNotFound
) {
1603 for (i
= 0; i
< colon
.location
; i
++) {
1604 char ch
= CFStringGetCharacterAtIndex(string
, i
);
1605 if (!scheme_valid(ch
)) {
1611 _CFURLInit(url
, string
, FULL_URL_REPRESENTATION
, isAbsolute
? NULL
: baseURL
);
1613 url
->_flags
|= IS_ABSOLUTE
;
1617 struct __CFURLEncodingTranslationParameters
{
1618 CFStringEncoding fromEnc
;
1619 CFStringEncoding toEnc
;
1620 const UniChar
*addlChars
;
1622 Boolean escapeHighBit
;
1623 Boolean escapePercents
;
1624 Boolean agreesOverASCII
;
1625 Boolean encodingsMatch
;
1628 static Boolean
_shouldEscapeForEncodingConversion(UniChar ch
, void *context
) {
1629 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1630 if (info
->escapeHighBit
&& ch
> 0x7F) {
1632 } else if (ch
== '%' && info
->escapePercents
) {
1634 } else if (info
->addlChars
) {
1635 const UniChar
*escChar
= info
->addlChars
;
1637 for (i
= 0; i
< info
->count
; escChar
++, i
++) {
1638 if (*escChar
== ch
) {
1646 static CFIndex
_convertEscapeSequence(CFIndex percentIndex
, CFStringRef urlString
, CFStringRef
*newString
, void *context
) {
1647 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1648 CFMutableDataRef newData
;
1649 Boolean sawNonASCIICharacter
= false;
1650 CFIndex i
= percentIndex
;
1653 if (info
->encodingsMatch
) return percentIndex
+ 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well
1654 newData
= CFDataCreateMutable(CFGetAllocator(urlString
), 0);
1655 length
= CFStringGetLength(urlString
);
1657 while (i
< length
&& CFStringGetCharacterAtIndex(urlString
, i
) == '%') {
1659 if (i
+2 >= length
|| !_translateBytes(CFStringGetCharacterAtIndex(urlString
, i
+1), CFStringGetCharacterAtIndex(urlString
, i
+2), &byte
)) {
1663 if (byte
> 0x7f) sawNonASCIICharacter
= true;
1664 CFDataAppendBytes(newData
, &byte
, 1);
1667 if (!sawNonASCIICharacter
&& info
->agreesOverASCII
) {
1670 CFStringRef tmp
= CFStringCreateWithBytes(CFGetAllocator(urlString
), CFDataGetBytePtr(newData
), CFDataGetLength(newData
), info
->fromEnc
, false);
1671 CFIndex tmpIndex
, tmpLen
;
1676 tmpLen
= CFStringGetLength(tmp
);
1677 *newString
= CFStringCreateMutable(CFGetAllocator(urlString
), 0);
1678 for (tmpIndex
= 0; tmpIndex
< tmpLen
; tmpIndex
++) {
1679 if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp
, tmpIndex
), info
->toEnc
, (CFMutableStringRef
)(*newString
))) {
1685 if (tmpIndex
< tmpLen
) {
1686 CFRelease(*newString
);
1695 /* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */
1696 static CFStringRef
_convertPercentEscapes(CFStringRef urlString
, CFStringEncoding fromEncoding
, CFStringEncoding toEncoding
, Boolean escapeAllHighBitCharacters
, Boolean escapePercents
, const UniChar
*addlCharsToEscape
, int numAddlChars
) {
1697 struct __CFURLEncodingTranslationParameters context
;
1698 context
.fromEnc
= fromEncoding
;
1699 context
.toEnc
= toEncoding
;
1700 context
.addlChars
= addlCharsToEscape
;
1701 context
.count
= numAddlChars
;
1702 context
.escapeHighBit
= escapeAllHighBitCharacters
;
1703 context
.escapePercents
= escapePercents
;
1704 context
.agreesOverASCII
= (__CFStringEncodingIsSupersetOfASCII(toEncoding
) && __CFStringEncodingIsSupersetOfASCII(fromEncoding
)) ? true : false;
1705 context
.encodingsMatch
= (fromEncoding
== toEncoding
) ? true : false;
1706 return _addPercentEscapesToString(CFGetAllocator(urlString
), urlString
, _shouldEscapeForEncodingConversion
, _convertEscapeSequence
, toEncoding
, &context
);
1709 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1710 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
1711 CFStringRef urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, encoding
, false);
1713 if (!urlString
|| CFStringGetLength(urlString
) == 0) {
1714 if (urlString
) CFRelease(urlString
);
1717 if (createOldUTF8StyleURLs()) {
1718 if (encoding
!= kCFStringEncodingUTF8
) {
1719 CFStringRef tmp
= _convertPercentEscapes(urlString
, encoding
, kCFStringEncodingUTF8
, false, false, NULL
, 0);
1720 CFRelease(urlString
);
1722 if (!urlString
) return NULL
;
1726 result
= _CFURLAlloc(allocator
);
1728 _CFURLInitWithString(result
, urlString
, baseURL
);
1729 if (encoding
!= kCFStringEncodingUTF8
&& !createOldUTF8StyleURLs()) {
1730 ((struct __CFURL
*)result
)->_encoding
= encoding
;
1733 CFRelease(urlString
); // it's retained by result, now.
1737 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
1738 static const UniChar whitespaceChars
[4] = {' ', '\n', '\r', '\t'};
1739 CFStringRef myStr
= CFURLGetString(url
);
1742 if (url
->_flags
& IS_OLD_UTF8_STYLE
) {
1743 newStr
= (encoding
== kCFStringEncodingUTF8
) ? CFRetain(myStr
) : _convertPercentEscapes(myStr
, kCFStringEncodingUTF8
, encoding
, true, false, escapeWhitespace
? whitespaceChars
: NULL
, escapeWhitespace
? 4 : 0);
1748 result
= CFStringCreateExternalRepresentation(allocator
, newStr
, encoding
, 0);
1753 // Any escape sequences in URLString will be interpreted via UTF-8.
1754 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1756 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1757 if (!_CFStringIsLegalURLString(URLString
)) return NULL
;
1758 url
= _CFURLAlloc(allocator
);
1760 _CFURLInitWithString(url
, URLString
, baseURL
);
1765 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1767 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1768 url
= _CFURLAlloc(allocator
);
1770 _CFURLInitWithString(url
, URLString
, baseURL
);
1775 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
1776 CFStringRef relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
1777 if (!relativeString
) {
1780 if (!useCompatibilityMode
) {
1781 CFURLRef url
= _CFURLCreateWithArbitraryString(alloc
, relativeString
, baseURL
);
1782 CFRelease(relativeString
);
1784 ((struct __CFURL
*)url
)->_encoding
= encoding
;
1785 CFURLRef absURL
= CFURLCopyAbsoluteURL(url
);
1792 UInt32 absFlags
= 0;
1794 CFStringRef absString
= NULL
;
1795 Boolean absStringIsMutable
= false;
1798 absString
= relativeString
;
1800 UniChar ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
1801 if (ch
== '?' || ch
== ';' || ch
== '#') {
1802 // Nothing but parameter + query + fragment; append to the baseURL string
1803 CFStringRef baseString
;
1804 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1805 baseString
= CFURLGetString(baseURL
);
1807 baseString
= baseURL
->_string
;
1809 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
1810 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
1811 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
1812 absStringIsMutable
= true;
1814 UInt32 relFlags
= 0;
1816 CFStringRef relString
= NULL
;
1817 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
1818 if (relFlags
& HAS_SCHEME
) {
1819 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
1820 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
1821 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
1822 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
1823 CFAllocatorDeallocate(alloc
, relRanges
);
1825 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
1827 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
1828 CFRetain(relativeString
);
1829 absString
= relativeString
;
1831 if (baseScheme
) CFRelease(baseScheme
);
1833 CFRetain(relativeString
);
1834 relString
= relativeString
;
1837 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1838 if (!(baseURL
->_flags
& IS_PARSED
)) {
1839 _parseComponentsOfURL(baseURL
);
1841 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->ranges
);
1843 CFStringRef baseString
;
1844 UInt32 baseFlags
= 0;
1845 CFRange
*baseRanges
;
1846 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1847 baseString
= CFURLGetString(baseURL
);
1849 baseString
= baseURL
->_string
;
1851 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
1852 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
1853 CFAllocatorDeallocate(alloc
, baseRanges
);
1855 absStringIsMutable
= true;
1857 if (relString
) CFRelease(relString
);
1858 CFAllocatorDeallocate(alloc
, relRanges
);
1860 CFRelease(relativeString
);
1862 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
1863 if (absFlags
& HAS_PATH
) {
1864 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
1865 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
1866 UniChar
*buf
= CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), 0);
1867 CFStringRef newPath
;
1868 CFStringGetCharacters(absString
, pathRg
, buf
);
1869 buf
[pathRg
.length
] = '\0';
1870 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
1871 if (CFStringGetLength(newPath
) != pathRg
.length
) {
1872 if (!absStringIsMutable
) {
1873 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
1874 CFRelease(absString
);
1877 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
1880 // Do not deallocate buf; newPath took ownership of it.
1882 CFAllocatorDeallocate(alloc
, absRanges
);
1883 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
1884 CFRelease(absString
);
1886 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
1892 /* 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 */
1893 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
1894 UniChar
*idx
= pathStr
;
1898 if (idx
!= pathStr
) {
1903 } else if (*(idx
+1) == pathDelimiter
) {
1904 if (idx
+ 2 != end
|| idx
!= pathStr
) {
1905 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
1909 // Do not delete the sole path component
1912 } else if (( end
-idx
>= 2 ) && *(idx
+1) == '.' && (idx
+2 == end
|| (( end
-idx
> 2 ) && *(idx
+2) == pathDelimiter
))) {
1913 if (idx
- pathStr
>= 2) {
1914 // 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.
1915 UniChar
*lastDelim
= idx
-2;
1916 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
1918 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
1919 // We have a genuine component to compact out
1921 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
1922 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
1923 end
-= (idx
+ 3 - lastDelim
);
1926 } else if (lastDelim
!= pathStr
) {
1931 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
1939 } else if (stripLeadingDotDots
) {
1940 if (idx
+ 3 != end
) {
1941 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
1942 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
1946 // Do not devolve the last path component
1952 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
1955 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
1958 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
- pathStr
, alloc
);
1961 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
1962 CFMutableStringRef newString
= CFStringCreateMutable(alloc
, 0);
1963 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
1964 UniChar
*buf
= CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
1967 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
1968 if (rg
.location
!= kCFNotFound
) {
1969 CFStringGetCharacters(baseString
, rg
, buf
);
1970 CFStringAppendCharacters(newString
, buf
, rg
.length
);
1971 CFStringAppendCString(newString
, ":", kCFStringEncodingASCII
);
1974 if (relFlags
& NET_LOCATION_MASK
) {
1975 CFStringAppend(newString
, relString
);
1977 CFStringAppendCString(newString
, "//", kCFStringEncodingASCII
);
1978 rg
= _netLocationRange(baseFlags
, baseRanges
);
1979 if (rg
.location
!= kCFNotFound
) {
1980 CFStringGetCharacters(baseString
, rg
, buf
);
1981 CFStringAppendCharacters(newString
, buf
, rg
.length
);
1984 if (relFlags
& HAS_PATH
) {
1985 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
1986 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
1987 CFStringRef newPath
;
1988 Boolean useRelPath
= false;
1989 Boolean useBasePath
= false;
1990 if (basePathRg
.location
== kCFNotFound
) {
1992 } else if (relPathRg
.length
== 0) {
1994 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
1996 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2000 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2001 } else if (useBasePath
) {
2002 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2004 // #warning FIXME - Get rid of this allocation
2005 UniChar
*newPathBuf
= CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
2007 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
2008 idx
= newPathBuf
+ basePathRg
.length
- 1;
2009 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
2010 if (*idx
== '/') idx
++;
2011 CFStringGetCharacters(relString
, relPathRg
, idx
);
2012 end
= idx
+ relPathRg
.length
;
2014 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2016 /* Under Win32 absolute path can begin with letter
2017 * so we have to add one '/' to the newString
2020 // No - the input strings here are URL path strings, not Win32 paths.
2021 // Absolute paths should have had a '/' prepended before this point.
2022 // I have removed Sergey Zubarev's change and left his comment (and
2023 // this one) as a record. - REW, 1/5/2004
2025 // if the relative URL does not begin with a slash and
2026 // the base does not end with a slash, add a slash
2027 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2028 CFStringAppendCString(newString
, "/", kCFStringEncodingASCII
);
2031 CFStringAppend(newString
, newPath
);
2033 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2034 rg
.length
= CFStringGetLength(relString
);
2035 if (rg
.length
> rg
.location
) {
2036 rg
.length
-= rg
.location
;
2037 CFStringGetCharacters(relString
, rg
, buf
);
2038 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2041 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2042 if (rg
.location
!= kCFNotFound
) {
2043 CFStringGetCharacters(baseString
, rg
, buf
);
2044 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2047 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2048 // ??? Can this ever happen?
2049 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2051 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2052 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2053 rg
.location
--; // To pick up the separator
2055 CFStringGetCharacters(baseString
, rg
, buf
);
2056 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2058 } else if (relFlags
& HAS_PARAMETERS
) {
2059 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2060 rg
.location
--; // To get the semicolon that starts the parameters
2061 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2062 CFStringGetCharacters(relString
, rg
, buf
);
2063 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2065 // Sigh; we have to resolve these against one another
2066 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2067 if (rg
.location
!= kCFNotFound
) {
2068 CFStringAppendCString(newString
, ";", kCFStringEncodingASCII
);
2069 CFStringGetCharacters(baseString
, rg
, buf
);
2070 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2072 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2073 if (rg
.location
!= kCFNotFound
) {
2074 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2075 CFStringGetCharacters(relString
, rg
, buf
);
2076 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2078 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2079 if (rg
.location
!= kCFNotFound
) {
2080 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2081 CFStringGetCharacters(baseString
, rg
, buf
);
2082 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2085 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2086 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2087 if (rg
.location
!= kCFNotFound
) {
2088 CFStringAppendCString(newString
, "#", kCFStringEncodingASCII
);
2089 CFStringGetCharacters(relString
, rg
, buf
);
2090 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2095 CFAllocatorDeallocate(alloc
, buf
);
2099 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2100 CFURLRef anURL
, base
;
2101 CFURLPathStyle fsType
;
2102 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2103 CFStringRef baseString
, newString
;
2105 CFRange
*baseRanges
;
2108 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2109 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2110 CF_OBJC_CALL0(CFURLRef
, anURL
, relativeURL
, "absoluteURL");
2111 if (anURL
) CFRetain(anURL
);
2115 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2117 base
= relativeURL
->_base
;
2119 return CFRetain(relativeURL
);
2121 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2122 fsType
= URL_PATH_TYPE(relativeURL
);
2124 if (!baseIsObjC
&& fsType
!= FULL_URL_REPRESENTATION
&& fsType
== URL_PATH_TYPE(base
)) {
2125 return _CFURLCopyAbsoluteFileURL(relativeURL
);
2127 if (fsType
!= FULL_URL_REPRESENTATION
) {
2128 _convertToURLRepresentation((struct __CFURL
*)relativeURL
);
2129 fsType
= FULL_URL_REPRESENTATION
;
2131 if (!(relativeURL
->_flags
& IS_PARSED
)) {
2132 _parseComponentsOfURL(relativeURL
);
2134 if ((relativeURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) && !(relativeURL
->_flags
& (RESOURCE_SPECIFIER_MASK
| NET_LOCATION_MASK
)) && !baseIsObjC
&& (URL_PATH_TYPE(base
) == kCFURLPOSIXPathStyle
)) {
2135 // There's nothing to relativeURL's string except the path
2136 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, CFURLHasDirectoryPath(base
), kCFURLPOSIXPathStyle
, alloc
);
2137 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, kCFURLPOSIXPathStyle
, CFURLHasDirectoryPath(relativeURL
));
2143 CFURLPathStyle baseType
= URL_PATH_TYPE(base
);
2144 if (baseType
!= FULL_URL_REPRESENTATION
) {
2145 _convertToURLRepresentation((struct __CFURL
*)base
);
2146 } else if (!(base
->_flags
& IS_PARSED
)) {
2147 _parseComponentsOfURL(base
);
2149 baseString
= base
->_string
;
2150 baseFlags
= base
->_flags
;
2151 baseRanges
= base
->ranges
;
2153 baseString
= CFURLGetString(base
);
2156 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2159 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->ranges
, baseString
, baseFlags
, baseRanges
);
2161 CFAllocatorDeallocate(alloc
, baseRanges
);
2163 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2164 CFRelease(newString
);
2165 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2170 /*******************/
2171 /* Basic accessors */
2172 /*******************/
2173 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2174 return url
->_encoding
;
2177 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2178 anURL
= _CFURLFromNSURL(anURL
);
2179 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) return true;
2180 if (!(anURL
->_flags
& IS_PARSED
)) {
2181 _parseComponentsOfURL(anURL
);
2183 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2186 CFStringRef
CFURLGetString(CFURLRef url
) {
2187 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFStringRef
, url
, "relativeString");
2188 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2189 if (url
->_base
&& (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2190 return url
->_string
;
2192 _convertToURLRepresentation((struct __CFURL
*)url
);
2194 if (!_haveTestedOriginalString(url
)) {
2195 computeSanitizedString(url
);
2197 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2198 return url
->_string
;
2200 return _getSanitizedString( url
);
2204 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2205 CFIndex length
, charsConverted
, usedLength
;
2207 CFStringEncoding enc
;
2208 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2209 string
= CFURLGetString(url
);
2210 enc
= kCFStringEncodingUTF8
;
2212 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2213 _convertToURLRepresentation((struct __CFURL
*)url
);
2215 string
= url
->_string
;
2216 enc
= url
->_encoding
;
2218 length
= CFStringGetLength(string
);
2219 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2220 if (charsConverted
!= length
) {
2227 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2228 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, anURL
, "baseURL");
2229 return anURL
->_base
;
2232 // Assumes the URL is already parsed
2233 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2235 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2236 while (!(compFlag
& 1)) {
2237 compFlag
= compFlag
>> 1;
2246 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2249 CFAllocatorRef alloc
= CFGetAllocator(url
);
2250 CFAssert1(URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
, __kCFLogAssertion
, "%s(): passed a file system URL", __PRETTY_FUNCTION__
);
2251 if (removePercentEscapes
) fromOriginalString
= true;
2252 if (!(url
->_flags
& IS_PARSED
)) {
2253 _parseComponentsOfURL(url
);
2255 rg
= _rangeForComponent(url
->_flags
, url
->ranges
, compFlag
);
2256 if (rg
.location
== kCFNotFound
) return NULL
;
2257 if (compFlag
& HAS_SCHEME
&& url
->_flags
& HAS_HTTP_SCHEME
) {
2258 comp
= CFSTR("http");
2260 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2262 if (!fromOriginalString
) {
2263 if (!_haveTestedOriginalString(url
)) {
2264 computeSanitizedString(url
);
2266 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (url
->_flags
& (compFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
))) {
2267 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2272 if (removePercentEscapes
) {
2274 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
2275 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2277 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2285 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2287 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2288 CF_OBJC_CALL0(CFStringRef
, scheme
, anURL
, "scheme");
2289 if (scheme
) CFRetain(scheme
);
2292 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2294 return CFURLCopyScheme(anURL
->_base
);
2296 CFRetain(kCFURLFileScheme
); // because caller will release it
2297 return kCFURLFileScheme
;
2300 if (anURL
->_flags
& IS_PARSED
&& anURL
->_flags
& HAS_HTTP_SCHEME
) {
2301 return(CFSTR("http"));
2303 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2306 } else if (anURL
->_base
) {
2307 return CFURLCopyScheme(anURL
->_base
);
2313 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2315 CFRange netRg
= {kCFNotFound
, 0};
2318 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2320 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2321 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2322 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2323 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2324 for (i
= 0; i
< c
; i
++) {
2325 if (netRgs
[i
].location
== kCFNotFound
) continue;
2326 if (netRg
.location
== kCFNotFound
) {
2329 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2335 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2336 anURL
= _CFURLFromNSURL(anURL
);
2337 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2338 // !!! This won't work if we go to putting the vol ref num in the net location for HFS
2340 return CFURLCopyNetLocation(anURL
->_base
);
2342 CFRetain(kCFURLLocalhost
);
2343 return kCFURLLocalhost
;
2346 if (!(anURL
->_flags
& IS_PARSED
)) {
2347 _parseComponentsOfURL(anURL
);
2349 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2350 // We provide the net location
2351 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->ranges
);
2353 if (!_haveTestedOriginalString(anURL
)) {
2354 computeSanitizedString(anURL
);
2356 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (anURL
->_flags
& (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2357 // 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.
2359 netRg
.length
= CFStringGetLength( _getSanitizedString(anURL
)) - netRg
.location
;
2360 if (CFStringFindWithOptions(_getSanitizedString(anURL
), CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2361 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2363 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), netRg
);
2365 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2368 } else if (anURL
->_base
) {
2369 return CFURLCopyNetLocation(anURL
->_base
);
2375 // 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.
2376 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
2377 anURL
= _CFURLFromNSURL(anURL
);
2378 if (URL_PATH_TYPE(anURL
) == kCFURLPOSIXPathStyle
&& (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2379 CFRetain(anURL
->_string
);
2380 return anURL
->_string
;
2382 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2383 _convertToURLRepresentation((struct __CFURL
*)anURL
);
2385 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
2388 /* 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.
2390 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.
2392 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
2393 CFStringRef path
= CFURLCopyPath(anURL
);
2394 if (!path
|| CFStringGetLength(path
) == 0) {
2395 if (path
) CFRelease(path
);
2396 if (isAbsolute
) *isAbsolute
= false;
2399 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
2401 if (isAbsolute
) *isAbsolute
= true;
2402 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
2406 if (isAbsolute
) *isAbsolute
= false;
2411 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
2412 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2413 if (URL_PATH_TYPE(anURL
) == FULL_URL_REPRESENTATION
) {
2414 if (!(anURL
->_flags
& IS_PARSED
)) {
2415 _parseComponentsOfURL(anURL
);
2417 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
2418 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2420 return CFURLHasDirectoryPath(anURL
->_base
);
2422 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2425 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
2426 UInt32 firstRsrcSpecFlag
= 0;
2427 UInt32 flag
= HAS_FRAGMENT
;
2428 while (flag
!= HAS_PATH
) {
2430 firstRsrcSpecFlag
= flag
;
2434 return firstRsrcSpecFlag
;
2437 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
2438 anURL
= _CFURLFromNSURL(anURL
);
2439 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2440 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2443 if (!(anURL
->_flags
& IS_PARSED
)) {
2444 _parseComponentsOfURL(anURL
);
2446 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
2447 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, HAS_SCHEME
);
2448 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
2449 if (!_haveTestedOriginalString(anURL
)) {
2450 computeSanitizedString(anURL
);
2452 if (_getSanitizedString(anURL
)) {
2453 // 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.
2454 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), CFRangeMake(base
, CFStringGetLength(_getSanitizedString(anURL
))-base
));
2456 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
2459 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
2461 if (firstRsrcSpecFlag
) {
2462 Boolean canUseOriginalString
= true;
2463 Boolean canUseSanitizedString
= true;
2464 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
2465 if (!_haveTestedOriginalString(anURL
)) {
2466 computeSanitizedString(anURL
);
2468 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
2469 // See if any pieces in the resource specifier differ between sanitized string and original string
2470 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
2471 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2472 canUseOriginalString
= false;
2477 if (!canUseOriginalString
) {
2478 // 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.
2479 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
2480 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2481 canUseSanitizedString
= false;
2486 if (canUseOriginalString
) {
2487 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2488 rg
.location
--; // Include the character that demarcates the component
2489 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
2490 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
2491 } else if (canUseSanitizedString
) {
2492 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2493 rg
.location
--; // Include the character that demarcates the component
2494 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2495 return CFStringCreateWithSubstring(alloc
, _getSanitizedString(anURL
), rg
);
2497 // Must compute the correct string to return; just reparse....
2498 UInt32 sanFlags
= 0;
2499 CFRange
*sanRanges
= NULL
;
2501 _parseComponents(alloc
, _getSanitizedString(anURL
), anURL
->_base
, &sanFlags
, &sanRanges
);
2502 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
2503 CFAllocatorDeallocate(alloc
, sanRanges
);
2504 rg
.location
--; // Include the character that demarcates the component
2505 rg
.length
= CFStringGetLength(_getSanitizedString(anURL
)) - rg
.location
;
2506 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), _getSanitizedString(anURL
), rg
);
2509 // The resource specifier cannot possibly come from the base.
2515 /*************************************/
2516 /* Accessors that create new objects */
2517 /*************************************/
2519 // 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).
2520 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
2522 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2523 CF_OBJC_CALL0(CFStringRef
, tmp
, anURL
, "host");
2524 if (tmp
) CFRetain(tmp
);
2527 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2528 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2530 return CFURLCopyHostName(anURL
->_base
);
2532 CFRetain(kCFURLLocalhost
);
2533 return kCFURLLocalhost
;
2536 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
2538 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
2539 // Have to strip off the brackets to get the true hostname.
2540 // Assume that to be legal the first and last characters are brackets!
2541 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
2546 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2547 return CFURLCopyHostName(anURL
->_base
);
2553 // Return -1 to indicate no port is specified
2554 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
2556 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2558 CF_OBJC_CALL0(CFNumberRef
, cfPort
, anURL
, "port");
2560 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
2563 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2564 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2566 return CFURLGetPortNumber(anURL
->_base
);
2570 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
2572 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
2573 CFStringInlineBuffer buf
;
2574 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
2576 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
2581 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2582 return CFURLGetPortNumber(anURL
->_base
);
2588 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
2590 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2591 CF_OBJC_CALL0(CFStringRef
, user
, anURL
, "user");
2592 if (user
) CFRetain(user
);
2595 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2596 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2598 return CFURLCopyUserName(anURL
->_base
);
2602 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
2605 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2606 return CFURLCopyUserName(anURL
->_base
);
2612 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
2614 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2615 CF_OBJC_CALL0(CFStringRef
, passwd
, anURL
, "password");
2616 if (passwd
) CFRetain(passwd
);
2619 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2620 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2622 return CFURLCopyPassword(anURL
->_base
);
2626 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
2629 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2630 return CFURLCopyPassword(anURL
->_base
);
2636 // 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
2638 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
2640 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2641 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "parameterString");
2642 if (str
) CFRetain(str
);
2645 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2646 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2649 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
2650 if (str
) return str
;
2651 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2652 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
2654 // Parameter string definitely coming from the relative portion of the URL
2656 return _unescapedParameterString( anURL
->_base
);
2659 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2660 CFStringRef param
= _unescapedParameterString(anURL
);
2663 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2664 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
2666 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
2674 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
2676 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2677 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "query");
2678 if (str
) CFRetain(str
);
2681 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2682 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2685 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
2686 if (str
) return str
;
2687 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2688 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
2691 return _unescapedQueryString(anURL
->_base
);
2694 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2695 CFStringRef query
= _unescapedQueryString(anURL
);
2698 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2699 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
2701 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
2709 // Fragments are NEVER taken from a base URL
2710 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
2712 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2713 CF_OBJC_CALL0(CFStringRef
, str
, anURL
, "fragment");
2714 if (str
) CFRetain(str
);
2717 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2718 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2721 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
2725 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2726 CFStringRef fragment
= _unescapedFragment(anURL
);
2729 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2730 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
2732 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
2734 CFRelease(fragment
);
2740 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
2741 CFIndex firstMaskFlag
= 1;
2742 CFIndex lastComponentBeforeMask
= 0;
2743 while (firstMaskFlag
<= HAS_FRAGMENT
) {
2744 if (firstMaskFlag
& mask
) break;
2745 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
2746 firstMaskFlag
= firstMaskFlag
<< 1;
2748 if (lastComponentBeforeMask
== 0) {
2749 // mask includes HAS_SCHEME
2751 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
2752 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
2753 // case file:/path/immediately/without/host
2754 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2755 CFRange pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
2756 if (schemeRg
.length
+ 1 == pathRg
.location
) {
2757 return schemeRg
.length
+ 1;
2759 return schemeRg
.length
+ 3;
2762 // For all other components, the separator precedes the component, so there's no need
2763 // to add extra chars to get to the next insertion point
2764 CFRange rg
= _rangeForComponent(url
->_flags
, url
->ranges
, lastComponentBeforeMask
);
2765 return rg
.location
+ rg
.length
;
2769 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
2770 CFOptionFlags currentOption
;
2771 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
2772 Boolean haveReachedMask
= false;
2773 CFIndex beforeMask
= 0;
2774 CFIndex afterMask
= kCFNotFound
;
2775 CFRange
*currRange
= url
->ranges
;
2776 CFRange maskRange
= {kCFNotFound
, 0};
2777 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
2778 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
2779 firstMaskFlag
= currentOption
;
2780 haveReachedMask
= true;
2782 if (!(url
->_flags
& currentOption
)) continue;
2783 if (!haveReachedMask
) {
2784 beforeMask
= currRange
->location
+ currRange
->length
;
2785 } else if (currentOption
<= mask
) {
2786 if (maskRange
.location
== kCFNotFound
) {
2787 maskRange
= *currRange
;
2789 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
2792 afterMask
= currRange
->location
;
2797 if (afterMask
== kCFNotFound
) {
2798 afterMask
= maskRange
.location
+ maskRange
.length
;
2800 charRangeWithSeparators
->location
= beforeMask
;
2801 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
2805 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2807 switch (component
) {
2808 case kCFURLComponentScheme
:
2811 case kCFURLComponentNetLocation
:
2812 mask
= NET_LOCATION_MASK
;
2814 case kCFURLComponentPath
:
2817 case kCFURLComponentResourceSpecifier
:
2818 mask
= RESOURCE_SPECIFIER_MASK
;
2820 case kCFURLComponentUser
:
2823 case kCFURLComponentPassword
:
2824 mask
= HAS_PASSWORD
;
2826 case kCFURLComponentUserInfo
:
2827 mask
= HAS_USER
| HAS_PASSWORD
;
2829 case kCFURLComponentHost
:
2832 case kCFURLComponentPort
:
2835 case kCFURLComponentParameterString
:
2836 mask
= HAS_PARAMETERS
;
2838 case kCFURLComponentQuery
:
2841 case kCFURLComponentFragment
:
2842 mask
= HAS_FRAGMENT
;
2845 rangeIncludingSeparators
->location
= kCFNotFound
;
2846 rangeIncludingSeparators
->length
= 0;
2847 return CFRangeMake(kCFNotFound
, 0);
2850 if ((url
->_flags
& mask
) == 0) {
2851 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
2852 rangeIncludingSeparators
->length
= 0;
2853 return CFRangeMake(kCFNotFound
, 0);
2855 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
2859 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2860 if (component
== kCFURLComponentScheme
) {
2861 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2862 rangeIncludingSeparators
->location
= 0;
2863 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
2865 } else if (component
== kCFURLComponentResourceSpecifier
) {
2866 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2867 CFIndex stringLength
= CFStringGetLength(url
->_string
);
2868 if (schemeRg
.length
+ 1 == stringLength
) {
2869 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
2870 rangeIncludingSeparators
->length
= 0;
2871 return CFRangeMake(kCFNotFound
, 0);
2873 rangeIncludingSeparators
->location
= schemeRg
.length
;
2874 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
2875 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
2878 rangeIncludingSeparators
->location
= kCFNotFound
;
2879 rangeIncludingSeparators
->length
= 0;
2880 return CFRangeMake(kCFNotFound
, 0);
2885 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2886 CFRange charRange
, charRangeWithSeparators
;
2888 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
2889 url
= _CFURLFromNSURL(url
);
2890 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2891 _convertToURLRepresentation((struct __CFURL
*)url
);
2893 if (!(url
->_flags
& IS_PARSED
)) {
2894 _parseComponentsOfURL(url
);
2897 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
2898 // Special-case this because non-decomposable URLs have a slightly strange flags setup
2899 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
2901 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
2904 if (charRangeWithSeparators
.location
== kCFNotFound
) {
2905 if (rangeIncludingSeparators
) {
2906 rangeIncludingSeparators
->location
= kCFNotFound
;
2907 rangeIncludingSeparators
->length
= 0;
2909 return CFRangeMake(kCFNotFound
, 0);
2910 } else if (rangeIncludingSeparators
) {
2911 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
2913 if (charRange
.location
== kCFNotFound
) {
2914 byteRange
= charRange
;
2915 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
2917 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
2918 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
2920 if (charRangeWithSeparators
.location
== charRange
.location
) {
2921 byteRange
.location
= rangeIncludingSeparators
->location
;
2924 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
2925 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
2927 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
2928 if (maxCharRangeWithSeparators
== maxCharRange
) {
2929 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
2933 rg
.location
= maxCharRange
;
2934 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
2935 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
2936 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
2939 } else if (charRange
.location
== kCFNotFound
) {
2940 byteRange
= charRange
;
2942 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
2943 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
2948 /* Component support */
2950 /* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */
2951 static CFStringRef
schemeSpecificString(CFURLRef url
) {
2953 isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
2954 switch (URL_PATH_TYPE(url
)) {
2955 case kCFURLPOSIXPathStyle
:
2956 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
2957 return CFRetain(url
->_string
);
2959 return POSIXPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
2961 case kCFURLHFSPathStyle
:
2962 return HFSPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
2963 case kCFURLWindowsPathStyle
:
2964 return WindowsPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
2965 case FULL_URL_REPRESENTATION
:
2966 return CFURLCopyResourceSpecifier(url
);
2972 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
2973 if ( CFURLGetBaseURL(url
) != NULL
) {
2974 components
->scheme
= NULL
;
2976 components
->scheme
= CFURLCopyScheme(url
);
2978 components
->schemeSpecific
= schemeSpecificString(url
);
2982 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
2984 if (components
->scheme
) {
2986 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
2987 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
2988 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
2989 } else if (components
->schemeSpecific
) {
2990 str
= components
->schemeSpecific
;
2996 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3004 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3005 CFAllocatorRef alloc
= CFGetAllocator(url
);
3007 static CFStringRef emptyStr
= NULL
;
3009 emptyStr
= CFSTR("");
3012 if (!CFURLCanBeDecomposed(url
)) {
3015 if ((pathType
= URL_PATH_TYPE(url
)) == FULL_URL_REPRESENTATION
) {
3016 CFStringRef path
= CFURLCopyPath(url
);
3018 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3021 components
->pathComponents
= NULL
;
3023 components
->baseURL
= CFURLGetBaseURL(url
);
3024 if (components
->baseURL
) {
3025 CFRetain(components
->baseURL
);
3026 components
->scheme
= NULL
;
3028 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
3030 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
3031 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3032 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3033 if (url
->_flags
& HAS_PORT
) {
3034 components
->port
= CFURLGetPortNumber(url
);
3036 components
->port
= kCFNotFound
;
3038 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3039 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3040 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3043 case kCFURLPOSIXPathStyle
: {
3044 CFStringRef pathStr
;
3045 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3046 pathStr
= url
->_string
;
3049 pathStr
= POSIXPathToURLPath(url
->_string
, alloc
, url
->_flags
& IS_DIRECTORY
);
3051 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, pathStr
, CFSTR("/"));
3055 case kCFURLHFSPathStyle
:
3056 components
->pathComponents
= HFSPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3058 case kCFURLWindowsPathStyle
:
3059 components
->pathComponents
= WindowsPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3062 components
->pathComponents
= NULL
;
3064 if (!components
->pathComponents
) {
3067 components
->scheme
= CFRetain(kCFURLFileScheme
);
3068 components
->user
= NULL
;
3069 components
->password
= NULL
;
3070 components
->host
= CFRetain(kCFURLLocalhost
);
3071 components
->port
= kCFNotFound
;
3072 components
->parameterString
= NULL
;
3073 components
->query
= NULL
;
3074 components
->fragment
= NULL
;
3075 components
->baseURL
= CFURLGetBaseURL(url
);
3076 if (components
->baseURL
) CFRetain(components
->baseURL
);
3081 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3082 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3083 CFURLRef base
= comp
->baseURL
;
3085 Boolean hadPrePathComponent
= false;
3088 CFStringAppend(urlString
, comp
->scheme
);
3089 CFStringAppend(urlString
, CFSTR("://"));
3090 hadPrePathComponent
= true;
3092 if (comp
->user
|| comp
->password
) {
3094 CFStringAppend(urlString
, comp
->user
);
3096 if (comp
->password
) {
3097 CFStringAppend(urlString
, CFSTR(":"));
3098 CFStringAppend(urlString
, comp
->password
);
3100 CFStringAppend(urlString
, CFSTR("@"));
3101 hadPrePathComponent
= true;
3104 CFStringAppend(urlString
, comp
->host
);
3105 hadPrePathComponent
= true;
3107 if (comp
->port
!= kCFNotFound
) {
3108 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3109 hadPrePathComponent
= true;
3112 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength(CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3113 CFStringAppend(urlString
, CFSTR("/"));
3115 if (comp
->pathComponents
) {
3116 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3117 CFStringAppend(urlString
, pathStr
);
3120 if (comp
->parameterString
) {
3121 CFStringAppend(urlString
, CFSTR(";"));
3122 CFStringAppend(urlString
, comp
->parameterString
);
3125 CFStringAppend(urlString
, CFSTR("?"));
3126 CFStringAppend(urlString
, comp
->query
);
3128 if (comp
->fragment
) {
3129 CFStringAppend(urlString
, CFSTR("#"));
3130 CFStringAppend(urlString
, comp
->fragment
);
3132 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3133 CFRelease(urlString
);
3137 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3138 CFAllocatorRef alloc
= CFGetAllocator(url
);
3139 CFURLComponentsRFC1808 oldComp
;
3141 if (!decomposeToRFC1808(url
, &oldComp
)) {
3144 comp
->scheme
= oldComp
.scheme
;
3146 if (oldComp
.password
) {
3147 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3148 CFRelease(oldComp
.password
);
3149 CFRelease(oldComp
.user
);
3151 comp
->userinfo
= oldComp
.user
;
3154 comp
->userinfo
= NULL
;
3156 comp
->host
= oldComp
.host
;
3157 comp
->port
= oldComp
.port
;
3158 if (!oldComp
.parameterString
) {
3159 comp
->pathComponents
= oldComp
.pathComponents
;
3161 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3162 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3163 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3164 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3166 CFRelease(oldComp
.pathComponents
);
3167 CFRelease(oldComp
.parameterString
);
3169 comp
->query
= oldComp
.query
;
3170 comp
->fragment
= oldComp
.fragment
;
3171 comp
->baseURL
= oldComp
.baseURL
;
3175 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3176 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3177 CFURLRef base
= comp
->baseURL
;
3179 Boolean hadPrePathComponent
= false;
3182 CFStringAppend(urlString
, comp
->scheme
);
3183 CFStringAppend(urlString
, CFSTR("://"));
3184 hadPrePathComponent
= true;
3186 if (comp
->userinfo
) {
3187 CFStringAppend(urlString
, comp
->userinfo
);
3188 CFStringAppend(urlString
, CFSTR("@"));
3189 hadPrePathComponent
= true;
3192 CFStringAppend(urlString
, comp
->host
);
3193 if (comp
->port
!= kCFNotFound
) {
3194 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3196 hadPrePathComponent
= true;
3198 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength(CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3199 CFStringAppend(urlString
, CFSTR("/"));
3201 if (comp
->pathComponents
) {
3202 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3203 CFStringAppend(urlString
, pathStr
);
3207 CFStringAppend(urlString
, CFSTR("?"));
3208 CFStringAppend(urlString
, comp
->query
);
3210 if (comp
->fragment
) {
3211 CFStringAppend(urlString
, CFSTR("#"));
3212 CFStringAppend(urlString
, comp
->fragment
);
3214 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3215 CFRelease(urlString
);
3219 #undef CFURLCopyComponents
3220 #undef CFURLCreateFromComponents
3222 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3223 url
= _CFURLFromNSURL(url
);
3224 switch (decompositionType
) {
3225 case kCFURLComponentDecompositionNonHierarchical
:
3226 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3227 case kCFURLComponentDecompositionRFC1808
:
3228 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3229 case kCFURLComponentDecompositionRFC2396
:
3230 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3237 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3238 switch (decompositionType
) {
3239 case kCFURLComponentDecompositionNonHierarchical
:
3240 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3241 case kCFURLComponentDecompositionRFC1808
:
3242 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3243 case kCFURLComponentDecompositionRFC2396
:
3244 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3250 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3251 return _getReserved(url
);
3254 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3255 _setReserved ( (struct __CFURL
*) url
, ptr
);
3259 /* File system stuff */
3261 /* HFSPath<->URLPath functions at the bottom of the file */
3262 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3264 CFMutableArrayRef urlComponents
= NULL
;
3267 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3268 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3271 CFStringRef str
= CFArrayGetValueAtIndex(urlComponents
, 0);
3272 if (CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3273 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR("")); // So we get a leading '/' below
3274 i
= 2; // Skip over the drive letter and the empty string we just inserted
3277 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3278 CFStringRef fileComp
= CFArrayGetValueAtIndex(urlComponents
,i
);
3279 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3281 // Couldn't decode fileComp
3282 CFRelease(urlComponents
);
3285 if (urlComp
!= fileComp
) {
3286 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3292 if (CFStringGetLength(CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3293 CFArrayAppendValue(urlComponents
, CFSTR(""));
3295 return urlComponents
;
3298 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3299 CFArrayRef urlComponents
;
3302 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3303 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
);
3304 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3306 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3307 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3308 CFRelease(urlComponents
);
3312 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
) {
3313 CFStringRef pathString
= _replacePathIllegalCharacters(path
, alloc
, true);
3314 if (isDirectory
&& CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
)-1) != '/') {
3315 CFStringRef tmp
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@/"), pathString
);
3316 CFRelease(pathString
);
3322 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3323 // This is the easiest case; just remove the percent escape codes and we're done
3324 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3326 CFIndex length
= CFStringGetLength(result
);
3327 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3328 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3337 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3338 // Check for a drive letter, then flip all the slashes
3340 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
3341 SInt32 count
= CFArrayGetCount(tmp
);
3342 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
3343 CFStringRef newPath
;
3346 if (CFStringGetLength(CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
3347 CFArrayRemoveValueAtIndex(components
, count
-1);
3350 if (count
> 1 && CFStringGetLength(CFArrayGetValueAtIndex(components
, 0)) == 0) {
3351 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
3352 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
3354 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
3356 CFArrayRemoveValueAtIndex(components
, 0);
3358 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
3359 CFArraySetValueAtIndex(components
, 0, driveStr
);
3360 CFRelease(driveStr
);
3363 CFRelease(firstComponent
);
3366 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
3367 CFRelease(components
);
3368 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
3373 // converts url from a file system path representation to a standard representation
3374 static void _convertToURLRepresentation(struct __CFURL
*url
) {
3375 CFStringRef path
= NULL
;
3376 Boolean isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3377 CFAllocatorRef alloc
= CFGetAllocator(url
);
3379 #if DEBUG_URL_MEMORY_USAGE
3380 numFileURLsConverted
++;
3383 switch (URL_PATH_TYPE(url
)) {
3384 case kCFURLPOSIXPathStyle
:
3385 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3386 path
= CFRetain(url
->_string
);
3388 path
= POSIXPathToURLPath(url
->_string
, alloc
, isDir
);
3391 case kCFURLHFSPathStyle
:
3392 path
= HFSPathToURLPath(url
->_string
, alloc
, isDir
);
3394 case kCFURLWindowsPathStyle
:
3395 path
= WindowsPathToURLPath(url
->_string
, alloc
, isDir
);
3398 CFAssert2(path
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, url
);
3401 str
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("file://localhost%@"), path
);
3402 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_ABSOLUTE
| IS_PARSED
| HAS_SCHEME
| HAS_HOST
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3403 CFRelease(url
->_string
);
3405 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
3406 url
->ranges
[0] = CFRangeMake(0, 4);
3407 url
->ranges
[1] = CFRangeMake(7, 9);
3408 url
->ranges
[2] = CFRangeMake(16, CFStringGetLength(path
));
3411 CFRelease(url
->_string
);
3412 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_PARSED
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3413 url
->_string
= path
;
3414 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
3415 *(url
->ranges
) = CFRangeMake(0, CFStringGetLength(path
));
3419 // relativeURL is known to be a file system URL whose base is a matching file system URL
3420 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
) {
3421 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
3422 CFURLPathStyle fsType
= URL_PATH_TYPE(relativeURL
);
3423 CFURLRef base
= relativeURL
->_base
;
3424 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, (base
->_flags
& IS_DIRECTORY
) != 0, fsType
, alloc
);
3425 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, fsType
, (relativeURL
->_flags
& IS_DIRECTORY
) != 0);
3430 // Caller must release the returned string
3431 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
3432 CFIndex baseLen
= CFStringGetLength(basePath
);
3433 CFIndex relLen
= CFStringGetLength(relativePath
);
3434 UniChar pathDelimiter
= PATH_DELIM_FOR_TYPE(fsType
);
3435 UniChar
*buf
= CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
3436 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
3438 if (buf
[baseLen
-1] != pathDelimiter
) {
3439 buf
[baseLen
] = pathDelimiter
;
3443 UniChar
*ptr
= buf
+ baseLen
- 1;
3444 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
3447 baseLen
= ptr
- buf
+ 1;
3449 if (fsType
== kCFURLHFSPathStyle
) {
3450 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3453 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
3454 *(buf
+ baseLen
+ relLen
) = '\0';
3455 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
3458 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
3459 CFURLRef url
= NULL
;
3460 uint8_t buf
[CFMaxPathSize
+ 1];
3461 if (_CFGetCurrentDirectory(buf
, CFMaxPathLength
)) {
3462 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen(buf
), true);
3467 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
3468 Boolean isAbsolute
= true;
3470 CFURLRef baseURL
, result
;
3472 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3473 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__
);
3475 len
= CFStringGetLength(filePath
);
3478 case kCFURLPOSIXPathStyle
:
3479 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3481 case kCFURLWindowsPathStyle
:
3482 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3483 /* Absolute path under Win32 can begin with "\\"
3486 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3488 case kCFURLHFSPathStyle
:
3489 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3495 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3497 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
3498 if (baseURL
) CFRelease(baseURL
);
3502 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
3504 Boolean isAbsolute
= true, releaseFilePath
= false;
3505 UniChar pathDelim
= '\0';
3508 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
3509 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3511 len
= CFStringGetLength(filePath
);
3514 case kCFURLPOSIXPathStyle
:
3515 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3519 case kCFURLWindowsPathStyle
:
3520 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3521 /* Absolute path under Win32 can begin with "\\"
3525 isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3528 case kCFURLHFSPathStyle
:
3529 { CFRange fullStrRange
= CFRangeMake( 0, CFStringGetLength( filePath
) );
3531 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3534 if ( _CFExecutableLinkedOnOrAfter( CFSystemVersionTiger
) &&
3535 filePath
&& CFStringFindWithOptions( filePath
, CFSTR("::"), fullStrRange
, 0, NULL
) ) {
3536 UniChar
* chars
= (UniChar
*) malloc( fullStrRange
.length
* sizeof( UniChar
) );
3537 CFIndex index
, writeIndex
, firstColonOffset
= -1;
3539 CFStringGetCharacters( filePath
, fullStrRange
, chars
);
3541 for ( index
= 0, writeIndex
= 0 ; index
< fullStrRange
.length
; index
++ ) {
3542 if ( chars
[ index
] == ':' ) {
3543 if ( index
+ 1 < fullStrRange
.length
&& chars
[ index
+ 1 ] == ':' ) {
3545 // Don't let :: go off the 'top' of the path -- which means that there always has to be at
3546 // least one ':' to the left of the current write position to go back to.
3547 if ( writeIndex
> 0 && firstColonOffset
>= 0 )
3550 while ( writeIndex
> 0 && writeIndex
>= firstColonOffset
&& chars
[ writeIndex
] != ':' )
3553 index
++; // skip over the first ':', so we replace the ':' which is there with a new one
3556 if ( firstColonOffset
== -1 )
3557 firstColonOffset
= writeIndex
;
3560 chars
[ writeIndex
++ ] = chars
[ index
];
3563 if ( releaseFilePath
&& filePath
)
3564 CFRelease( filePath
);
3566 filePath
= CFStringCreateWithCharacters( allocator
, chars
, writeIndex
);
3567 releaseFilePath
= true;
3579 if (isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) != pathDelim
) {
3580 CFStringRef tempRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c"), filePath
, pathDelim
);
3581 if ( releaseFilePath
&& filePath
) CFRelease( filePath
);
3583 releaseFilePath
= true;
3584 } else if (!isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) == pathDelim
) {
3585 if (len
== 1 || CFStringGetCharacterAtIndex(filePath
, len
-2) == pathDelim
) {
3586 // Override isDirectory
3589 CFStringRef tempRef
= CFStringCreateWithSubstring(allocator
, filePath
, CFRangeMake(0, len
-1));
3590 if ( releaseFilePath
&& filePath
)
3591 CFRelease( filePath
);
3593 releaseFilePath
= true;
3596 if (!filePath
|| CFStringGetLength(filePath
) == 0) {
3597 if (releaseFilePath
&& filePath
) CFRelease(filePath
);
3600 url
= _CFURLAlloc(allocator
);
3601 _CFURLInit((struct __CFURL
*)url
, filePath
, fsType
, baseURL
);
3602 if (releaseFilePath
) CFRelease(filePath
);
3603 if (isDirectory
) ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
3604 if (fsType
== kCFURLPOSIXPathStyle
) {
3605 // 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
3606 // 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
3607 CFStringInlineBuffer buf
;
3608 Boolean sawSlash
= FALSE
;
3609 Boolean mustPrependDotSlash
= FALSE
;
3610 CFIndex idx
, length
= CFStringGetLength(url
->_string
);
3611 CFStringInitInlineBuffer(url
->_string
, &buf
, CFRangeMake(0, length
));
3612 for (idx
= 0; idx
< length
; idx
++) {
3613 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
3614 if (!isPathLegalCharacter(ch
)) break;
3618 } else if (ch
== ':') {
3619 mustPrependDotSlash
= TRUE
;
3623 if (idx
== length
) {
3624 ((struct __CFURL
*)url
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
3626 if (mustPrependDotSlash
) {
3627 CFStringRef newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("./%@"), url
->_string
);
3628 CFRelease(url
->_string
);
3629 ((struct __CFURL
*)url
)->_string
= newString
;
3635 CF_EXPORT CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
3636 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
3637 return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL
), anURL
, pathStyle
, false);
3640 // 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
3641 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
3642 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
3643 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
3644 CFStringRef relPath
= NULL
;
3646 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3647 // We can grope the ivars
3648 CFURLPathStyle myType
= URL_PATH_TYPE(anURL
);
3649 if (myType
== fsType
) {
3650 relPath
= CFRetain(anURL
->_string
);
3651 } else if (fsType
== kCFURLPOSIXPathStyle
&& myType
== FULL_URL_REPRESENTATION
) {
3652 if (!(anURL
->_flags
& IS_PARSED
)) {
3653 _parseComponentsOfURL(anURL
);
3655 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3656 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
3661 if (relPath
== NULL
) {
3662 CFStringRef urlPath
= CFURLCopyPath(anURL
);
3663 CFStringEncoding enc
= (anURL
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: anURL
->_encoding
;
3666 case kCFURLPOSIXPathStyle
:
3667 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
3669 case kCFURLHFSPathStyle
:
3670 relPath
= URLPathToHFSPath(urlPath
, allocator
, enc
);
3672 case kCFURLWindowsPathStyle
:
3673 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
3676 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
3682 // For Tiger, leave this behavior in for all path types. For Chablis, it would be nice to remove this entirely
3683 // and do a linked-on-or-later check so we don't break third parties.
3684 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
3685 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
3686 if ( relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == PATH_DELIM_FOR_TYPE(fsType
)) {
3687 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
3692 // Note that !resolveAgainstBase implies !base
3693 if (!basePath
|| !relPath
) {
3696 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
3697 CFRelease(basePath
);
3703 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
3705 CFAllocatorRef alloc
= CFGetAllocator(url
);
3707 if (!url
) return false;
3708 #if defined(__WIN32__)
3709 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
3711 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
3714 #if defined(__MACH__)
3715 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
3720 CFIndex pathLen
= CFStringGetLength(path
);
3721 CFIndex numConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLen
), CFStringFileSystemEncoding(), 0, true, buffer
, bufLen
-1, &usedLen
); // -1 because we need one byte to zero-terminate.
3723 if (numConverted
== pathLen
) {
3724 buffer
[usedLen
] = '\0';
3732 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
3733 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
3735 if (!path
) return NULL
;
3736 #if defined(__WIN32__)
3737 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
);
3739 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
);
3745 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
3746 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
3748 if (!path
) return NULL
;
3749 #if defined(__WIN32__)
3750 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
3752 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
, baseURL
);
3759 /******************************/
3760 /* Support for path utilities */
3761 /******************************/
3763 // Assumes url is a CFURL (not an Obj-C NSURL)
3764 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
3765 UInt32 pathType
= URL_PATH_TYPE(url
);
3766 CFRange pathRg
, componentRg
;
3768 if (pathType
== FULL_URL_REPRESENTATION
) {
3769 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3770 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3772 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
3775 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
3779 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == PATH_DELIM_FOR_TYPE(pathType
)) {
3781 if (pathRg
.length
== 0) {
3786 if (CFStringFindWithOptions(url
->_string
, PATH_DELIM_AS_STRING_FOR_TYPE(pathType
), pathRg
, kCFCompareBackwards
, &componentRg
)) {
3787 componentRg
.location
++;
3788 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
3790 componentRg
= pathRg
;
3795 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
3798 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
3799 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
3802 if (!path
) return NULL
;
3803 rg
= CFRangeMake(0, CFStringGetLength(path
));
3804 length
= rg
.length
; // Remember this for comparison later
3805 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/') {
3808 if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
3809 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
3810 rg
.location
= compRg
.location
+ 1;
3812 if (rg
.location
== 0 && rg
.length
== length
) {
3815 result
= CFStringCreateWithSubstring(NULL
, path
, rg
);
3819 CFRange rg
= _rangeOfLastPathComponent(url
);
3820 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
3822 return CFRetain(CFSTR(""));
3824 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url
))) {
3825 return CFRetain(CFSTR("/"));
3827 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
3828 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
&& !(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
3830 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
3831 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
3833 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
3842 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
3843 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
3844 CFStringRef ext
= NULL
;
3847 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
3848 if (rg
.location
!= kCFNotFound
) {
3850 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
3851 if (rg
.length
> 0) {
3852 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
3854 ext
= CFRetain(CFSTR(""));
3857 CFRelease(lastPathComp
);
3862 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
3865 url
= _CFURLFromNSURL(url
);
3866 __CFGenericValidateType(url
, __kCFURLTypeID
);
3867 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
3869 fsType
= URL_PATH_TYPE(url
);
3870 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(pathComponent
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(pathComponent
)), 0, NULL
)) {
3871 // Must convert to full representation, and then work with it
3872 fsType
= FULL_URL_REPRESENTATION
;
3873 _convertToURLRepresentation((struct __CFURL
*)url
);
3876 if (fsType
== FULL_URL_REPRESENTATION
) {
3877 CFMutableStringRef newString
;
3878 CFStringRef newComp
;
3880 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3881 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
3883 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
3884 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
3885 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3886 if (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
3887 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
3890 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
3892 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
3895 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
3896 CFRelease(newString
);
3898 UniChar pathDelim
= PATH_DELIM_FOR_TYPE(fsType
);
3899 CFStringRef newString
;
3900 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != pathDelim
) {
3902 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@%c"), url
->_string
, pathDelim
, pathComponent
, pathDelim
);
3904 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@"), url
->_string
, pathDelim
, pathComponent
);
3908 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@%c"), url
->_string
, pathComponent
, pathDelim
);
3910 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@"), url
->_string
, pathComponent
);
3913 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, isDirectory
, url
->_base
);
3914 CFRelease(newString
);
3919 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
3921 CFMutableStringRef newString
;
3922 CFRange lastCompRg
, pathRg
;
3923 Boolean appendDotDot
= false;
3926 url
= _CFURLFromNSURL(url
);
3927 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
3928 __CFGenericValidateType(url
, __kCFURLTypeID
);
3930 fsType
= URL_PATH_TYPE(url
);
3931 if (fsType
== FULL_URL_REPRESENTATION
) {
3932 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3933 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
3934 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3936 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
3938 lastCompRg
= _rangeOfLastPathComponent(url
);
3939 if (lastCompRg
.length
== 0) {
3940 appendDotDot
= true;
3941 } else if (lastCompRg
.length
== 1) {
3942 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
3943 if (ch
== '.' || ch
== PATH_DELIM_FOR_TYPE(fsType
)) {
3944 appendDotDot
= true;
3946 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
3947 appendDotDot
= true;
3950 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
3953 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != PATH_DELIM_FOR_TYPE(fsType
)) {
3954 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
3957 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
3959 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
3961 // 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 "/.".
3962 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
3963 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == PATH_DELIM_FOR_TYPE(fsType
)) {
3964 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
3965 } else if (pathRg
.length
+delta
== 5) {
3966 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
3969 } else if (lastCompRg
.location
== pathRg
.location
) {
3970 CFStringReplace(newString
, pathRg
, CFSTR("."));
3971 CFStringInsert(newString
, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
3973 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
3975 if (fsType
== FULL_URL_REPRESENTATION
) {
3976 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
3978 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, true, url
->_base
);
3980 CFRelease(newString
);
3984 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
3985 CFMutableStringRef newString
;
3988 CFURLPathStyle fsType
;
3990 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
3991 url
= _CFURLFromNSURL(url
);
3992 __CFGenericValidateType(url
, __kCFURLTypeID
);
3993 __CFGenericValidateType(extension
, CFStringGetTypeID());
3995 rg
= _rangeOfLastPathComponent(url
);
3996 if (rg
.location
< 0) return NULL
; // No path
3997 fsType
= URL_PATH_TYPE(url
);
3998 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(extension
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(extension
)), 0, NULL
)) {
3999 _convertToURLRepresentation((struct __CFURL
*)url
);
4000 fsType
= FULL_URL_REPRESENTATION
;
4001 rg
= _rangeOfLastPathComponent(url
);
4004 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4005 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
4006 if (fsType
== FULL_URL_REPRESENTATION
) {
4007 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
4008 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
4010 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4012 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, extension
);
4013 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4015 CFRelease(newString
);
4019 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4023 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4024 url
= _CFURLFromNSURL(url
);
4025 __CFGenericValidateType(url
, __kCFURLTypeID
);
4026 rg
= _rangeOfLastPathComponent(url
);
4027 if (rg
.location
< 0) {
4029 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
4030 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4031 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
4032 CFStringDelete(newString
, dotRg
);
4033 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
) {
4034 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4036 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, URL_PATH_TYPE(url
), (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
4038 CFRelease(newString
);
4040 result
= CFRetain(url
);
4045 // We deal in FSRefs because they handle Unicode strings.
4046 // FSSpecs handle a much more limited set of characters.
4047 static Boolean
__CFFSRefForVolumeName(CFStringRef volName
, FSRef
*spec
, CFAllocatorRef alloc
) {
4051 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4052 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4053 CFMutableArrayRef newComponents
= CFArrayCreateMutableCopy(alloc
, 0, components
);
4054 Boolean doSpecialLeadingColon
= false;
4055 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
4057 CFRelease(components
);
4060 if (!doSpecialLeadingColon
&& firstChar
!= ':') {
4061 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4062 } else if (firstChar
!= ':') {
4063 // see what we need to add at the beginning. Under MacOS, if the
4064 // first character isn't a ':', then the first component is the
4065 // volume name, and we need to find the mount point. Bleah. If we
4066 // don't find a mount point, we're going to have to lie, and make something up.
4067 CFStringRef firstComp
= CFArrayGetValueAtIndex(newComponents
, 0);
4068 if (CFStringGetLength(firstComp
) == 1 && CFStringGetCharacterAtIndex(firstComp
, 0) == '/') {
4069 // "/" is the "magic" path for a UFS root directory
4070 CFArrayRemoveValueAtIndex(newComponents
, 0);
4071 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4073 // See if we can get a mount point.
4074 Boolean foundMountPoint
= false;
4075 uint8_t buf
[CFMaxPathLength
];
4077 // Now produce an FSSpec from the volume, then try and get the mount point
4078 if (__CFFSRefForVolumeName(firstComp
, &volSpec
, alloc
) && (__CFCarbonCore_FSRefMakePath(&volSpec
, buf
, CFMaxPathLength
) == noErr
)) {
4079 // We win! Ladies and gentlemen, we have a mount point.
4080 if (buf
[0] == '/' && buf
[1] == '\0') {
4081 // Special case this common case
4082 foundMountPoint
= true;
4083 CFArrayRemoveValueAtIndex(newComponents
, 0);
4084 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4086 // This is pretty inefficient; we can do better.
4087 CFStringRef mountPoint
= CFStringCreateWithCString(alloc
, buf
, CFStringFileSystemEncoding());
4088 CFArrayRef mountComponents
= mountPoint
? CFStringCreateArrayBySeparatingStrings(alloc
, mountPoint
, CFSTR("/")) : NULL
;
4089 if (mountComponents
) {
4090 CFIndex idx
= CFArrayGetCount(mountComponents
) - 1;
4091 CFArrayRemoveValueAtIndex(newComponents
, 0);
4092 for ( ; idx
>= 0; idx
--) {
4093 CFArrayInsertValueAtIndex(newComponents
, 0, CFArrayGetValueAtIndex(mountComponents
, idx
));
4095 CFRelease(mountComponents
);
4096 foundMountPoint
= true;
4098 if (mountPoint
) CFRelease(mountPoint
);
4101 if (!foundMountPoint
) {
4102 // Fall back to treating the volume name as the top level directory
4103 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4107 CFArrayRemoveValueAtIndex(newComponents
, 0);
4110 cnt
= CFArrayGetCount(newComponents
);
4111 for (i
= 0; i
< cnt
; i
++) {
4112 CFStringRef comp
= CFArrayGetValueAtIndex(newComponents
, i
);
4113 CFStringRef newComp
= NULL
;
4114 CFRange searchRg
, slashRg
;
4115 searchRg
.location
= 0;
4116 searchRg
.length
= CFStringGetLength(comp
);
4117 while (CFStringFindWithOptions(comp
, CFSTR("/"), searchRg
, 0, &slashRg
)) {
4119 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4121 CFStringReplace((CFMutableStringRef
)newComp
, slashRg
, CFSTR(":"));
4122 searchRg
.length
= searchRg
.location
+ searchRg
.length
- slashRg
.location
- 1;
4123 searchRg
.location
= slashRg
.location
+ 1;
4126 CFArraySetValueAtIndex(newComponents
, i
, newComp
);
4130 if (isDir
&& CFStringGetLength(CFArrayGetValueAtIndex(newComponents
, cnt
-1)) != 0) {
4131 CFArrayAppendValue(newComponents
, CFSTR(""));
4133 return newComponents
;
4136 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4137 CFArrayRef components
= HFSPathToURLComponents(path
, alloc
, isDir
);
4138 CFArrayRef newComponents
= components
? copyStringArrayWithTransformation(components
, escapePathComponent
) : NULL
;
4141 if (components
) CFRelease(components
);
4142 if (!newComponents
) return NULL
;
4144 cnt
= CFArrayGetCount(newComponents
);
4145 if (cnt
== 1 && CFStringGetLength(CFArrayGetValueAtIndex(newComponents
, 0)) == 0) {
4146 result
= CFRetain(CFSTR("/"));
4148 result
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
4150 CFRelease(newComponents
);
4155 static CFStringRef
URLPathToHFSPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {