2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2013, Apple Inc. All rights reserved.
26 Responsibility: John Iarocci
29 #include <CoreFoundation/CFURL.h>
30 #include <CoreFoundation/CFPriv.h>
31 #include <CoreFoundation/CFCharacterSetPriv.h>
32 #include <CoreFoundation/CFNumber.h>
33 #include "CFInternal.h"
34 #include <CoreFoundation/CFStringEncodingConverter.h>
39 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
40 #if DEPLOYMENT_TARGET_MACOSX
41 #include <CoreFoundation/CFNumberFormatter.h>
45 #include <sys/types.h>
46 #include <sys/syslog.h>
47 #include <CoreFoundation/CFURLPriv.h>
50 #ifndef DEBUG_URL_MEMORY_USAGE
51 // enables various statistical counters which can be displayed with __CFURLDumpMemRecord().
52 #define DEBUG_URL_MEMORY_USAGE 0
55 #ifndef DEBUG_URL_INITIALIZER_LOGGING
56 // enables logging in URL initializer. You get to see the inputs and output for each URL created.
57 #define DEBUG_URL_INITIALIZER_LOGGING 0
61 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
62 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
63 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
);
65 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
);
66 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
);
67 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
, Boolean isAbsolute
, Boolean
*posixAndUrlPathsMatch
);
68 static CFStringRef
CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc
, const UInt8
*bytes
, CFIndex numBytes
, Boolean isDirectory
, Boolean isAbsolute
, Boolean windowsPath
, Boolean
*addedPercentEncoding
);
69 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
70 CF_EXPORT CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
71 #if DEPLOYMENT_TARGET_MACOSX
72 static Boolean
_CFURLHasFileURLScheme(CFURLRef url
, Boolean
*hasScheme
);
77 // When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but
78 // when it is defined, we must prefix with static to prevent the string from being exported
79 #ifdef __CONSTANT_CFSTRINGS__
80 static CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
81 static CONST_STRING_DECL(kCFURLHTTPSScheme
, "https")
82 static CONST_STRING_DECL(kCFURLFileScheme
, "file")
83 static CONST_STRING_DECL(kCFURLDataScheme
, "data")
84 static CONST_STRING_DECL(kCFURLFTPScheme
, "ftp")
85 static CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
87 CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
88 CONST_STRING_DECL(kCFURLHTTPSScheme
, "https")
89 CONST_STRING_DECL(kCFURLFileScheme
, "file")
90 CONST_STRING_DECL(kCFURLDataScheme
, "data")
91 CONST_STRING_DECL(kCFURLFTPScheme
, "ftp")
92 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
95 #if DEBUG_URL_MEMORY_USAGE
96 static uint numURLs
= 0; // number of URLs allocated
97 static uint numDealloced
= 0; // number of URLs deallocated
98 static uint numFileURLsParsed
= 0; // number of URLs created from a string which had to be parsed
99 static uint numExtraDataAllocated
= 0; // number of URLs with additional data -- either because URLHandle was used, or because a sanitizedString was needed
100 static uint numURLsWithBaseURL
= 0; // number of URLs with a baseURL
101 static uint numNonUTF8EncodedURLs
= 0; // number of URLs that don't have UTF8 encoding
104 /* The bit flags in myURL->_flags */
106 #define HAS_SCHEME (0x00000001)
107 #define HAS_USER (0x00000002)
108 #define HAS_PASSWORD (0x00000004)
109 #define HAS_HOST (0x00000008)
110 #define HAS_PORT (0x00000010)
111 #define HAS_PATH (0x00000020)
112 #define HAS_PARAMETERS (0x00000040)
113 #define HAS_QUERY (0x00000080)
114 #define HAS_FRAGMENT (0x00000100)
115 // various boolean flags
116 #define IS_IPV6_ENCODED (0x00000400)
117 #define IS_DIRECTORY (0x00000800)
118 #define IS_CANONICAL_FILE_URL (0x00001000) // if set, the URL is a file URL in the form "file://<absolute_percent_encoded_path>" (it was created from a file system path or representation)
119 #define PATH_HAS_FILE_ID (0x00002000)
120 #define IS_DECOMPOSABLE (0x00004000)
121 #define POSIX_AND_URL_PATHS_MATCH (0x00008000) // POSIX_AND_URL_PATHS_MATCH will only be true if the URL path and the POSIX path are identical, character for character, except for the presence/absence of a trailing slash on directories
122 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00010000)
123 // scheme bits and amount to shift it to translate to the kXXXXScheme enums
124 #define SCHEME_TYPE_MASK (0xE0000000)
125 #define SCHEME_SHIFT 29
127 kHasUncommonScheme
= 0,
135 // accessors for the scheme bits in _flags
136 CF_INLINE UInt32
_getSchemeTypeFromFlags(UInt32 flags
);
137 CF_INLINE
void _setSchemeTypeInFlags(UInt32
*flags
, UInt32 schemeType
);
139 // Other useful defines
140 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
141 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
142 // These flags can be compared for equality since these are all set once when the CFURL is created.
143 // IS_CANONICAL_FILE_URL cannot be compared since we don't always create the URL string.
144 // POSIX_AND_URL_PATHS_MATCH cannot be compared because it may not be set
145 // ORIGINAL_AND_URL_STRINGS_MATCH cannot be compared because it gets set on demand later.
146 #define EQUAL_FLAGS_MASK (HAS_SCHEME | HAS_USER | HAS_PASSWORD | HAS_HOST | HAS_PORT | HAS_PATH | HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT | IS_IPV6_ENCODED | IS_DIRECTORY | PATH_HAS_FILE_ID | IS_DECOMPOSABLE | SCHEME_TYPE_MASK )
148 // The value of FULL_URL_REPRESENTATION must not be in the CFURLPathStyle enums. Also, its value is exposed via _CFURLCopyPropertyListRepresentation to the Finder so don't change it.
149 #define FULL_URL_REPRESENTATION (0xF)
151 /* The bit flags in _CFURLAdditionalData->_additionalDataFlags */
152 /* If ORIGINAL_AND_URL_STRINGS_MATCH in myURL->_flags is false, these bits determine where they differ. XXXX_DIFFERS must match the HAS_XXXX */
153 #define SCHEME_DIFFERS HAS_SCHEME // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
154 #define USER_DIFFERS HAS_USER
155 #define PASSWORD_DIFFERS HAS_PASSWORD
156 #define HOST_DIFFERS HAS_HOST
157 #define PORT_DIFFERS HAS_PORT // 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
158 #define PATH_DIFFERS HAS_PATH // unused
159 #define PARAMETERS_DIFFER HAS_PARAMETERS // unused
160 #define QUERY_DIFFER HAS_QUERY // unused
161 #define FRAGMENT_DIFFER HAS_FRAGMENT // unused
163 #define FILE_ID_PREFIX ".file"
164 #define FILE_ID_KEY "id"
165 #define FILE_ID_PREAMBLE "/.file/id="
166 #define FILE_ID_PREAMBLE_LENGTH 10
168 #define FILE_PREFIX "file://"
169 static const UInt8 fileURLPrefix
[] = FILE_PREFIX
;
171 // FILE_PREFIX_WITH_AUTHORITY and fileURLPrefixWithAuthority are only needed because some code incorrectly expects file URLs to have a host of "localhost", so if the application is linked on or before OS X 10.9 or iOS 7.0, we add "localhost" to file path URLs we create.
172 #define FILE_PREFIX_WITH_AUTHORITY "file://localhost"
173 static const UInt8 fileURLPrefixWithAuthority
[] = FILE_PREFIX_WITH_AUTHORITY
;
175 static Boolean
AddAuthorityToFileURL(void)
177 static Boolean result
= false;
181 // In order to reduce the sizeof ( __CFURL ), move these items into a seperate structure which is
182 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
183 // either a sanitized string or a reserved pointer for URLHandle.
184 struct _CFURLAdditionalData
{
185 void *_reserved
; // Reserved for URLHandle's use.
186 CFStringRef _sanitizedString
; // The fully compliant RFC string. This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false.
187 UInt32 _additionalDataFlags
; // these flags only apply to things we need to keep state for in _CFURLAdditionalData (like the XXXX_DIFFERS flags)
191 CFRuntimeBase _cfBase
;
193 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes
194 CFStringRef _string
; // Never NULL
197 struct _CFURLAdditionalData
* _extra
;
198 void *_resourceInfo
; // For use by CoreServicesInternal to cache property values. Retained and released by CFURL.
202 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
204 if ( url
&& url
->_extra
) {
205 return ( url
->_extra
->_reserved
);
212 CF_INLINE CFStringRef
_getSanitizedString(const struct __CFURL
* url
)
214 if ( url
&& url
->_extra
) {
215 return ( url
->_extra
->_sanitizedString
);
222 CF_INLINE UInt32
_getAdditionalDataFlags(const struct __CFURL
* url
)
224 if ( url
&& url
->_extra
) {
225 return ( url
->_extra
->_additionalDataFlags
);
232 CF_INLINE
void* _getResourceInfo ( const struct __CFURL
* url
)
235 return url
->_resourceInfo
;
242 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
244 if ( url
&& ! url
->_extra
)
245 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), __kCFAllocatorGCScannedMemory
);
247 extra
->_reserved
= _getReserved( url
);
248 extra
->_additionalDataFlags
= _getAdditionalDataFlags(url
);
249 extra
->_sanitizedString
= _getSanitizedString(url
);
253 #if DEBUG_URL_MEMORY_USAGE
254 numExtraDataAllocated
++;
259 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
263 // Don't allocate extra space if we're just going to be storing NULL
264 if ( !url
->_extra
&& reserved
)
265 _CFURLAllocateExtraDataspace( url
);
268 __CFAssignWithWriteBarrier((void **)&url
->_extra
->_reserved
, reserved
);
272 CF_INLINE
void _setSanitizedString( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
276 // Don't allocate extra space if we're just going to be storing NULL
277 if ( !url
->_extra
&& sanitizedString
) {
278 _CFURLAllocateExtraDataspace( url
);
282 if ( url
->_extra
->_sanitizedString
) {
283 CFRelease(url
->_extra
->_sanitizedString
);
285 url
->_extra
->_sanitizedString
= CFStringCreateCopy(CFGetAllocator(url
), sanitizedString
);
291 CF_INLINE
void _setAdditionalDataFlags(struct __CFURL
* url
, UInt32 additionalDataFlags
)
295 // Don't allocate extra space if we're just going to be storing 0
296 if ( !url
->_extra
&& (additionalDataFlags
!= 0) ) {
297 _CFURLAllocateExtraDataspace( url
);
301 url
->_extra
->_additionalDataFlags
= additionalDataFlags
;
306 CF_INLINE
void _setResourceInfo ( struct __CFURL
* url
, void* resourceInfo
)
310 if ( url
&& OSAtomicCompareAndSwapPtrBarrier( NULL
, resourceInfo
, &url
->_resourceInfo
)) {
311 CFRetain( resourceInfo
);
315 CF_INLINE UInt32
_getSchemeTypeFromFlags(UInt32 flags
)
317 return ( (flags
& SCHEME_TYPE_MASK
) >> SCHEME_SHIFT
);
320 CF_INLINE
void _setSchemeTypeInFlags(UInt32
*flags
, UInt32 schemeType
)
322 CFAssert2((schemeType
>= kHasUncommonScheme
) && (schemeType
< kMaxScheme
), __kCFLogAssertion
, "%s(): Received bad schemeType %d", __PRETTY_FUNCTION__
, schemeType
);
323 *flags
= (*flags
& ~SCHEME_TYPE_MASK
) + (schemeType
<< SCHEME_SHIFT
);
326 static Boolean
_pathHasFileIDPrefix(CFStringRef path
);
327 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
328 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
329 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
330 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
331 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
332 static void computeSanitizedString(CFURLRef url
);
333 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
334 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
335 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
338 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
339 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->_ranges
));
350 static const unsigned char sURLValidCharacters
[128] = {
384 /* '!' 33 */ VALID
| PATHVALID
,
387 /* '$' 36 */ VALID
| PATHVALID
,
389 /* '&' 38 */ VALID
| PATHVALID
,
390 /* ''' 39 */ VALID
| PATHVALID
,
391 /* '(' 40 */ VALID
| PATHVALID
,
392 /* ')' 41 */ VALID
| PATHVALID
,
393 /* '*' 42 */ VALID
| PATHVALID
,
394 /* '+' 43 */ VALID
| SCHEME
| PATHVALID
,
395 /* ',' 44 */ VALID
| PATHVALID
,
396 /* '-' 45 */ VALID
| SCHEME
| PATHVALID
,
397 /* '.' 46 */ VALID
| SCHEME
| PATHVALID
,
398 /* '/' 47 */ VALID
| PATHVALID
,
399 /* '0' 48 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
400 /* '1' 49 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
401 /* '2' 50 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
402 /* '3' 51 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
403 /* '4' 52 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
404 /* '5' 53 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
405 /* '6' 54 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
406 /* '7' 55 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
407 /* '8' 56 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
408 /* '9' 57 */ VALID
| SCHEME
| PATHVALID
| HEXDIGIT
,
412 /* '=' 61 */ VALID
| PATHVALID
,
416 /* 'A' 65 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
417 /* 'B' 66 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
418 /* 'C' 67 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
419 /* 'D' 68 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
420 /* 'E' 69 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
421 /* 'F' 70 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
422 /* 'G' 71 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
423 /* 'H' 72 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
424 /* 'I' 73 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
425 /* 'J' 74 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
426 /* 'K' 75 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
427 /* 'L' 76 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
428 /* 'M' 77 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
429 /* 'N' 78 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
430 /* 'O' 79 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
431 /* 'P' 80 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
432 /* 'Q' 81 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
433 /* 'R' 82 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
434 /* 'S' 83 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
435 /* 'T' 84 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
436 /* 'U' 85 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
437 /* 'V' 86 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
438 /* 'W' 87 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
439 /* 'X' 88 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
440 /* 'Y' 89 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
441 /* 'Z' 90 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
446 /* '_' 95 */ VALID
| PATHVALID
,
448 /* 'a' 97 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
449 /* 'b' 98 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
450 /* 'c' 99 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
451 /* 'd' 100 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
452 /* 'e' 101 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
453 /* 'f' 102 */ VALID
| ALPHA
| SCHEME
| PATHVALID
| HEXDIGIT
,
454 /* 'g' 103 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
455 /* 'h' 104 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
456 /* 'i' 105 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
457 /* 'j' 106 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
458 /* 'k' 107 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
459 /* 'l' 108 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
460 /* 'm' 109 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
461 /* 'n' 110 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
462 /* 'o' 111 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
463 /* 'p' 112 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
464 /* 'q' 113 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
465 /* 'r' 114 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
466 /* 's' 115 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
467 /* 't' 116 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
468 /* 'u' 117 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
469 /* 'v' 118 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
470 /* 'w' 119 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
471 /* 'x' 120 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
472 /* 'y' 121 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
473 /* 'z' 122 */ VALID
| ALPHA
| SCHEME
| PATHVALID
,
477 /* '~' 126 */ VALID
| PATHVALID
,
481 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
482 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & VALID
) != 0) : false;
485 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
486 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & SCHEME
) != 0) : false;
489 CF_INLINE Boolean
isALPHA(UniChar ch
) {
490 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & ALPHA
) != 0) : false;
493 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
494 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & PATHVALID
) != 0) : false;
497 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
498 return (ch
<= 127) ? ((sURLValidCharacters
[ch
] & HEXDIGIT
) != 0) : false;
501 // Returns false if ch1 or ch2 isn't properly formatted
502 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
504 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
505 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
506 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
509 *result
= (*result
) << 4;
510 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
511 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
512 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
518 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
519 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
526 static const unsigned char sURLValidBytes
[256] = {
560 /* '!' 33 */ IS_PCHAR
,
563 /* '$' 36 */ IS_PCHAR
,
565 /* '&' 38 */ IS_PCHAR
,
566 /* ''' 39 */ IS_PCHAR
,
567 /* '(' 40 */ IS_PCHAR
,
568 /* ')' 41 */ IS_PCHAR
,
569 /* '*' 42 */ IS_PCHAR
,
570 /* '+' 43 */ IS_PCHAR
,
571 /* ',' 44 */ IS_PCHAR
,
572 /* '-' 45 */ IS_PCHAR
,
573 /* '.' 46 */ IS_PCHAR
,
574 /* '/' 47 */ IS_PCHAR
, // not really a pchar -- it's the segment delimiter
575 /* '0' 48 */ IS_PCHAR
,
576 /* '1' 49 */ IS_PCHAR
,
577 /* '2' 50 */ IS_PCHAR
,
578 /* '3' 51 */ IS_PCHAR
,
579 /* '4' 52 */ IS_PCHAR
,
580 /* '5' 53 */ IS_PCHAR
,
581 /* '6' 54 */ IS_PCHAR
,
582 /* '7' 55 */ IS_PCHAR
,
583 /* '8' 56 */ IS_PCHAR
,
584 /* '9' 57 */ IS_PCHAR
,
585 /* ':' 58 */ IS_PCHAR
,
586 /* ';' 59 */ 0, // we need to percent-escape ';' in file system paths so it won't be mistaken for the start of the obsolete param rule (rfc2396) that CFURL still supports
588 /* '=' 61 */ IS_PCHAR
,
591 /* '@' 64 */ IS_PCHAR
,
592 /* 'A' 65 */ IS_PCHAR
,
593 /* 'B' 66 */ IS_PCHAR
,
594 /* 'C' 67 */ IS_PCHAR
,
595 /* 'D' 68 */ IS_PCHAR
,
596 /* 'E' 69 */ IS_PCHAR
,
597 /* 'F' 70 */ IS_PCHAR
,
598 /* 'G' 71 */ IS_PCHAR
,
599 /* 'H' 72 */ IS_PCHAR
,
600 /* 'I' 73 */ IS_PCHAR
,
601 /* 'J' 74 */ IS_PCHAR
,
602 /* 'K' 75 */ IS_PCHAR
,
603 /* 'L' 76 */ IS_PCHAR
,
604 /* 'M' 77 */ IS_PCHAR
,
605 /* 'N' 78 */ IS_PCHAR
,
606 /* 'O' 79 */ IS_PCHAR
,
607 /* 'P' 80 */ IS_PCHAR
,
608 /* 'Q' 81 */ IS_PCHAR
,
609 /* 'R' 82 */ IS_PCHAR
,
610 /* 'S' 83 */ IS_PCHAR
,
611 /* 'T' 84 */ IS_PCHAR
,
612 /* 'U' 85 */ IS_PCHAR
,
613 /* 'V' 86 */ IS_PCHAR
,
614 /* 'W' 87 */ IS_PCHAR
,
615 /* 'X' 88 */ IS_PCHAR
,
616 /* 'Y' 89 */ IS_PCHAR
,
617 /* 'Z' 90 */ IS_PCHAR
,
622 /* '_' 95 */ IS_PCHAR
,
624 /* 'a' 97 */ IS_PCHAR
,
625 /* 'b' 98 */ IS_PCHAR
,
626 /* 'c' 99 */ IS_PCHAR
,
627 /* 'd' 100 */ IS_PCHAR
,
628 /* 'e' 101 */ IS_PCHAR
,
629 /* 'f' 102 */ IS_PCHAR
,
630 /* 'g' 103 */ IS_PCHAR
,
631 /* 'h' 104 */ IS_PCHAR
,
632 /* 'i' 105 */ IS_PCHAR
,
633 /* 'j' 106 */ IS_PCHAR
,
634 /* 'k' 107 */ IS_PCHAR
,
635 /* 'l' 108 */ IS_PCHAR
,
636 /* 'm' 109 */ IS_PCHAR
,
637 /* 'n' 110 */ IS_PCHAR
,
638 /* 'o' 111 */ IS_PCHAR
,
639 /* 'p' 112 */ IS_PCHAR
,
640 /* 'q' 113 */ IS_PCHAR
,
641 /* 'r' 114 */ IS_PCHAR
,
642 /* 's' 115 */ IS_PCHAR
,
643 /* 't' 116 */ IS_PCHAR
,
644 /* 'u' 117 */ IS_PCHAR
,
645 /* 'v' 118 */ IS_PCHAR
,
646 /* 'w' 119 */ IS_PCHAR
,
647 /* 'x' 120 */ IS_PCHAR
,
648 /* 'y' 121 */ IS_PCHAR
,
649 /* 'z' 122 */ IS_PCHAR
,
653 /* '~' 126 */ IS_PCHAR
,
785 CF_INLINE Boolean
is_pchar(unsigned char ch
) {
786 return ( (sURLValidBytes
[ch
] & IS_PCHAR
) != 0 );
791 CreateStringFromFileSystemRepresentationByAddingPercentEscapes creates a CFString
792 for the path-absolute form of a URI path component from the native file system representation.
793 Note: this code uses '/' path separators
795 The rules for path-absolute from rfc3986 are:
796 path-absolute = "/" [ segment-nz *( "/" segment ) ]
799 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
800 pct-encoded = "%" HEXDIG HEXDIG
801 unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
802 sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
804 static CFStringRef
CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc
, const UInt8
*bytes
, CFIndex numBytes
, Boolean isDirectory
, Boolean isAbsolute
, Boolean windowsPath
, Boolean
*addedPercentEncoding
)
806 static const UInt8 hexchars
[] = "0123456789ABCDEF";
807 const UInt8
*fileURLPrefixPtr
;
808 size_t fileURLPrefixLength
;
809 if ( AddAuthorityToFileURL() ) {
810 fileURLPrefixPtr
= fileURLPrefixWithAuthority
;
811 fileURLPrefixLength
= sizeof(fileURLPrefixWithAuthority
);
814 fileURLPrefixPtr
= fileURLPrefix
;
815 fileURLPrefixLength
= sizeof(fileURLPrefix
);
817 STACK_BUFFER_DECL(UInt8
, stackBuf
, (PATH_MAX
* 3) + (isAbsolute
? fileURLPrefixLength
: 0) + (isDirectory
? 1 : 0)); // worst case is every byte needs to be percent-escaped
820 const UInt8
*bytePtr
= bytes
;
823 Boolean addedPercent
= FALSE
;
825 // choose a buffer to percent-escape into.
826 if ( numBytes
<= PATH_MAX
) {
827 bufStartPtr
= &stackBuf
[0];
830 // worst case is every byte needs to be percent-escaped
831 bufStartPtr
= (UInt8
*)malloc((numBytes
* 3) + (isAbsolute
? fileURLPrefixLength
: 0) + (isDirectory
? 1 : 0));
834 if ( bufStartPtr
!= NULL
) {
836 // start with the fileURLPrefix
837 strcpy((char *)bufStartPtr
, (char *)fileURLPrefixPtr
);
838 bufBytePtr
= bufStartPtr
+ fileURLPrefixLength
- 1;
841 bufBytePtr
= bufStartPtr
;
844 if ( !windowsPath
) {
845 for ( idx
= 0; (idx
< numBytes
) && (*bytePtr
!= 0); ++idx
) {
846 if ( is_pchar(*bytePtr
) ) {
847 *bufBytePtr
++ = *bytePtr
;
851 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
852 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
859 for ( idx
= 0; (idx
< numBytes
) && (*bytePtr
!= 0); ++idx
) {
860 if ( is_pchar(*bytePtr
) && (*bytePtr
!= '/') ) { // percent-escape the forward slash if this is a windowsPath
861 *bufBytePtr
++ = *bytePtr
;
865 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
866 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
873 // did we convert numBytes?
874 if ( idx
!= numBytes
) {
875 // no, but it's OK if the remaining bytes are all nul (embedded nul bytes are not allowed)
876 const UInt8
*nullBytePtr
= bytePtr
;
877 for ( /* start where we left off */; (idx
< numBytes
) && (*nullBytePtr
== '\0'); ++idx
, ++nullBytePtr
) {
882 if ( idx
== numBytes
) {
884 // if it is a directory and it doesn't end with PATH_SEP, append a PATH_SEP.
885 if ( bytes
[numBytes
-1] != '/' ) {
890 // it is not a directory: remove any pathDelim characters at end (leaving at least one character)
891 while ( (numBytes
> 1) && (bytes
[numBytes
-1] == '/') ) {
898 result
= CFStringCreateWithBytes(alloc
, bufStartPtr
, (CFIndex
)(bufBytePtr
-bufStartPtr
), kCFStringEncodingUTF8
, FALSE
);
901 // the remaining bytes were not all nul
905 // free the buffer if we malloc'd it
906 if ( bufStartPtr
!= &stackBuf
[0] ) {
914 if ( addedPercentEncoding
) {
915 *addedPercentEncoding
= addedPercent
;
921 // 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.
922 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
923 CFStringRef result
= NULL
;
924 STACK_BUFFER_DECL(char, buffer
, PATH_MAX
);
925 if ( CFStringGetCString(str
, buffer
, PATH_MAX
, kCFStringEncodingUTF8
) ) {
926 result
= CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault
, (const UInt8
*)buffer
, strlen(buffer
), FALSE
, FALSE
, !preserveSlashes
, NULL
);
931 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
932 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
933 uint8_t *bytePtr
= bytes
, *currByte
;
935 CFAllocatorRef alloc
= NULL
;
936 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
937 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
938 if (byteLength
<= 6) {
939 // The encoding cannot accomodate the character
942 alloc
= CFGetAllocator(str
);
943 bytePtr
= (uint8_t *)CFAllocatorAllocate(alloc
, byteLength
, 0);
944 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
945 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
949 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
950 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
951 unsigned char high
, low
;
952 high
= ((*currByte
) & 0xf0) >> 4;
953 low
= (*currByte
) & 0x0f;
954 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
955 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
956 CFStringAppendCharacters(str
, escapeSequence
, 3);
958 if (bytePtr
!= bytes
) {
959 CFAllocatorDeallocate(alloc
, bytePtr
);
964 static CFStringRef
UnescapeAllWithUTF8(CFAllocatorRef alloc
, CFStringRef originalString
)
966 CFStringRef result
= NULL
;
967 CFIndex strLength
= CFStringGetLength(originalString
);
968 CFIndex maxBufferSize
= CFStringGetMaximumSizeForEncoding(strLength
, kCFStringEncodingUTF8
);
969 CFIndex stackBufferSize
= 2096;
970 STACK_BUFFER_DECL(UInt8
, escapedStackBuf
, stackBufferSize
*2);
973 // choose a buffer to percent-escape into.
974 if ( maxBufferSize
<= stackBufferSize
) {
975 escapedBuf
= &escapedStackBuf
[0];
978 escapedBuf
= (UInt8
*)malloc(maxBufferSize
* 2);
981 CFIndex charsConverted
;
983 unescapedBuf
= &escapedBuf
[maxBufferSize
];
984 charsConverted
= CFStringGetBytes(originalString
, CFRangeMake(0, strLength
), kCFStringEncodingUTF8
, 0, false, escapedBuf
, maxBufferSize
, &usedBufLen
);
985 if ( charsConverted
) {
986 // 0x80 marks invalid hex digits so this table can validate the digits while getting the values
987 static const UInt8 hexvalues
[] = {
988 /* 00 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
989 /* 08 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
990 /* 10 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
991 /* 18 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
992 /* 20 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
993 /* 28 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
994 /* 30 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
995 /* 38 */ 0x08, 0x09, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
996 /* 40 */ 0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80,
997 /* 48 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
998 /* 50 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
999 /* 58 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1000 /* 60 */ 0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80,
1001 /* 68 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1002 /* 70 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1003 /* 78 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1005 /* 80 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1006 /* 88 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1007 /* 90 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1008 /* 98 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1009 /* A0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1010 /* A8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1011 /* B0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1012 /* B8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1013 /* C0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1014 /* C8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1015 /* D0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1016 /* D8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1017 /* E0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1018 /* E8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1019 /* F0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1020 /* F8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1024 const UInt8
*bytePtr
= escapedBuf
;
1027 bufPtr
= bufStartPtr
= unescapedBuf
;
1028 Boolean conversionOK
= TRUE
;
1030 for ( idx
= 0; (idx
< usedBufLen
) && conversionOK
; ++idx
) {
1031 switch ( *bytePtr
) {
1034 if ( idx
< usedBufLen
) {
1038 // get the hex digits
1039 hex1
= hexvalues
[*bytePtr
++];
1040 hex2
= hexvalues
[*bytePtr
++];
1042 if ( ((hex1
| hex2
) & 0x80) == 0 ) {
1043 // convert hex digits
1044 *bufPtr
= (hex1
<< 4) + hex2
;
1047 conversionOK
= FALSE
;
1051 conversionOK
= FALSE
;
1055 // copy everything else
1056 *bufPtr
= *bytePtr
++;
1061 if ( conversionOK
) {
1062 result
= CFStringCreateWithBytes(alloc
, unescapedBuf
, bufPtr
- bufStartPtr
, kCFStringEncodingUTF8
, false);
1066 // free the buffer if we malloc'd it
1067 if ( escapedBuf
!= &escapedStackBuf
[0] ) {
1074 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
1075 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
1076 CFMutableStringRef newStr
= NULL
;
1079 CFRange percentRange
, searchRange
;
1080 CFStringRef escapedStr
= NULL
;
1081 CFMutableStringRef strForEscapedChar
= NULL
;
1082 UniChar escapedChar
;
1083 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
1084 Boolean failed
= false;
1086 if (!originalString
) return NULL
;
1088 length
= CFStringGetLength(originalString
);
1090 if ((length
== 0) || (charactersToLeaveEscaped
== NULL
)) {
1091 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
1095 return ( UnescapeAllWithUTF8(alloc
, originalString
) );
1098 searchRange
= CFRangeMake(0, length
);
1100 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
1101 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
1102 uint8_t numBytesExpected
;
1106 // Make sure we have at least 2 more characters
1107 if (length
- percentRange
.location
< 3) { failed
= true; break; }
1109 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
1110 // so we assume the percent character is legit, and let it pass into the string
1111 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
1112 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
1113 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
1114 if (!(bytes
[0] & 0x80)) {
1115 numBytesExpected
= 1;
1116 } else if (!(bytes
[0] & 0x20)) {
1117 numBytesExpected
= 2;
1118 } else if (!(bytes
[0] & 0x10)) {
1119 numBytesExpected
= 3;
1121 numBytesExpected
= 4;
1123 if (numBytesExpected
== 1) {
1124 // one byte sequence (most common case); handle this specially
1125 escapedChar
= bytes
[0];
1126 if (!strForEscapedChar
) {
1127 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
1129 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
1132 // Make sure up front that we have enough characters
1133 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
1134 for (j
= 1; j
< numBytesExpected
; j
++) {
1135 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
1136 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
1137 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
1138 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
1141 // FIXME: This uses about 1/3 of the time spent in CFURLCreateStringByReplacingPercentEscapes
1142 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
1143 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
1146 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
1147 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
1148 escapedChar
= 0xfeff;
1149 if (!strForEscapedChar
) {
1150 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
1152 CFRelease(escapedStr
);
1153 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
1158 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
1159 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
1160 searchRange
.length
= length
- searchRange
.location
;
1163 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
1165 CFRelease(escapedStr
);
1173 newStr
= CFStringCreateMutable(alloc
, length
);
1175 if (percentRange
.location
- mark
> 0) {
1176 // FIXME: The creation of this temporary string is unfortunate.
1177 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
1178 CFStringAppend(newStr
, substring
);
1179 CFRelease(substring
);
1181 CFStringAppend(newStr
, escapedStr
);
1183 CFRelease(escapedStr
);
1186 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
1189 if (escapedStr
) CFRelease(escapedStr
);
1190 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
1192 if (newStr
) CFRelease(newStr
);
1194 } else if (newStr
) {
1195 if (mark
< length
) {
1196 // Need to cat on the remainder of the string
1197 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
1198 CFStringAppend(newStr
, substring
);
1199 CFRelease(substring
);
1203 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
1208 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
1209 if (enc
== kCFStringEncodingUTF8
) {
1210 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
1212 CFMutableStringRef newStr
= NULL
;
1213 CFMutableStringRef escapedStr
= NULL
;
1216 CFRange percentRange
, searchRange
;
1217 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
1218 Boolean failed
= false;
1219 uint8_t byteBuffer
[8];
1220 uint8_t *bytes
= byteBuffer
;
1221 int capacityOfBytes
= 8;
1223 if (!originalString
) return NULL
;
1225 if (charactersToLeaveEscaped
== NULL
) {
1226 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
1229 length
= CFStringGetLength(originalString
);
1230 searchRange
= CFRangeMake(0, length
);
1232 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
1234 CFIndex percentLoc
= percentRange
.location
;
1235 CFStringRef convertedString
;
1236 int numBytesUsed
= 0;
1238 // Make sure we have at least 2 more characters
1239 if (length
- percentLoc
< 3) { failed
= true; break; }
1241 if (numBytesUsed
== capacityOfBytes
) {
1242 if (bytes
== byteBuffer
) {
1243 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
1244 memmove(bytes
, byteBuffer
, capacityOfBytes
);
1245 capacityOfBytes
= 16;
1247 void *oldbytes
= bytes
;
1248 int oldcap
= capacityOfBytes
;
1249 capacityOfBytes
= 2*capacityOfBytes
;
1250 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, capacityOfBytes
* sizeof(uint8_t), 0);
1251 memmove(bytes
, oldbytes
, oldcap
);
1252 CFAllocatorDeallocate(alloc
, oldbytes
);
1256 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
1258 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
1260 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
1262 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
1263 searchRange
.location
= percentLoc
;
1264 searchRange
.length
= length
- searchRange
.location
;
1267 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
1268 if (!convertedString
) {
1274 newStr
= CFStringCreateMutable(alloc
, length
);
1276 if (percentRange
.location
- mark
> 0) {
1277 // FIXME: The creation of this temporary string is unfortunate.
1278 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
1279 CFStringAppend(newStr
, substring
);
1280 CFRelease(substring
);
1284 CFStringAppend(newStr
, convertedString
);
1286 CFIndex i
, c
= CFStringGetLength(convertedString
);
1288 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
1290 for (i
= 0; i
< c
; i
++) {
1291 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
1292 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
1293 CFStringAppendCharacters(newStr
, &ch1
, 1);
1295 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
1296 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
1300 CFRelease(convertedString
);
1301 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
1304 if (escapedStr
) CFRelease(escapedStr
);
1305 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
1307 if (newStr
) CFRelease(newStr
);
1309 } else if (newStr
) {
1310 if (mark
< length
) {
1311 // Need to cat on the remainder of the string
1312 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
1313 CFStringAppend(newStr
, substring
);
1314 CFRelease(substring
);
1318 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
1323 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
1324 CFIndex i
, c
= CFStringGetLength(string
);
1325 CFStringInlineBuffer buf
;
1326 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
1327 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
1331 // Note: charactersToLeaveUnescaped and legalURLCharactersToBeEscaped only work for characters which can be represented as a single UTF16 codepoint.
1332 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
1333 CFMutableStringRef newString
= NULL
;
1334 CFIndex idx
, length
;
1335 CFStringInlineBuffer buf
;
1337 kCharBufferMax
= 4096,
1339 STACK_BUFFER_DECL(UniChar
, charBuffer
, kCharBufferMax
);
1340 CFIndex charBufferIndex
= 0;
1342 if (!originalString
) return NULL
;
1343 length
= CFStringGetLength(originalString
);
1344 if (length
== 0) return (CFStringRef
)CFStringCreateCopy(allocator
, originalString
);
1345 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
1347 for (idx
= 0; idx
< length
; idx
++) {
1348 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&buf
, idx
);
1349 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
1350 if (shouldReplace
) {
1351 if (charactersToLeaveUnescaped
&& _stringContainsCharacter(charactersToLeaveUnescaped
, ch
)) {
1352 shouldReplace
= false;
1354 } else if (legalURLCharactersToBeEscaped
&& _stringContainsCharacter(legalURLCharactersToBeEscaped
, ch
)) {
1355 shouldReplace
= true;
1358 if (shouldReplace
) {
1360 kMaxBytesPerUniChar
= 8, // 8 bytes is the maximum a single UniChar can require in any current encodings; future encodings could require more
1361 kMaxPercentEncodedUniChars
= kMaxBytesPerUniChar
* 3
1364 static const UInt8 hexchars
[] = "0123456789ABCDEF";
1365 uint8_t bytes
[kMaxBytesPerUniChar
];
1366 uint8_t *bytePtr
= bytes
;
1371 // Perform the replacement
1373 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
1374 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
1376 // make sure charBuffer has enough room
1377 if ( charBufferIndex
>= (kCharBufferMax
- kMaxPercentEncodedUniChars
) ) {
1379 CFStringAppendCharacters(newString
, charBuffer
, charBufferIndex
);
1380 charBufferIndex
= 0;
1383 // convert the UniChar to bytes
1384 if ( CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 8, &byteLength
) == kCFStringEncodingConversionSuccess
) {
1385 // percent-encode the bytes
1386 endPtr
= bytePtr
+ byteLength
;
1387 for ( currByte
= bytePtr
; currByte
< endPtr
; currByte
++ ) {
1388 charBuffer
[charBufferIndex
++] = '%';
1389 charBuffer
[charBufferIndex
++] = hexchars
[*currByte
>> 4];
1390 charBuffer
[charBufferIndex
++] = hexchars
[*currByte
& 0x0f];
1394 // FIXME: once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
1395 if ( encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(__CFStringGetCharacterFromInlineBufferQuick(&buf
, idx
+1)) ) {
1396 UniChar surrogate
[2];
1400 surrogate
[1] = __CFStringGetCharacterFromInlineBufferQuick(&buf
, idx
+1);
1401 // Aki sez it should never take more than 6 bytes
1402 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &byteLength
) == kCFStringEncodingConversionSuccess
) {
1403 endPtr
= bytePtr
+ byteLength
;
1404 for ( currByte
= bytes
; currByte
< endPtr
; currByte
++ ) {
1405 charBuffer
[charBufferIndex
++] = '%';
1406 charBuffer
[charBufferIndex
++] = hexchars
[*currByte
>> 4];
1407 charBuffer
[charBufferIndex
++] = hexchars
[*currByte
& 0x0f];
1409 idx
++; // We consumed 2 characters, not 1
1412 // surrogate pair conversion failed
1416 // not a surrogate pair
1420 } else if (newString
) {
1421 charBuffer
[charBufferIndex
++] = ch
;
1422 if ( charBufferIndex
== kCharBufferMax
) {
1423 CFStringAppendCharacters(newString
, charBuffer
, charBufferIndex
);
1424 charBufferIndex
= 0;
1429 // Ran into an encoding failure
1430 if (newString
) CFRelease(newString
);
1432 } else if (newString
) {
1433 if ( charBufferIndex
!= 0 ) {
1434 CFStringAppendCharacters(newString
, charBuffer
, charBufferIndex
);
1438 return (CFStringRef
)CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
1442 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
1444 CFURLRef url1
= (CFURLRef
)cf1
;
1445 CFURLRef url2
= (CFURLRef
)cf2
;
1447 __CFGenericValidateType(cf1
, CFURLGetTypeID());
1448 __CFGenericValidateType(cf2
, CFURLGetTypeID());
1450 if ( url1
== url2
) {
1454 if ( (url1
->_flags
& EQUAL_FLAGS_MASK
) != (url2
->_flags
& EQUAL_FLAGS_MASK
) ) {
1458 if ( (url1
->_base
&& !url2
->_base
) ||
1459 (!url1
->_base
&& url2
->_base
) ||
1460 (url1
->_base
&& url2
->_base
&& !CFEqual(url1
->_base
, url2
->_base
)) ) {
1464 // no base urls, so compare the URL strings
1465 // Do not compare the original strings; compare the sanatized strings.
1466 result
= CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
1473 static CFHashCode
__CFURLHash(CFTypeRef cf
)
1478 // use the CFHashCode of the URL
1479 result
= CFHash(CFURLGetString((CFURLRef
)cf
));
1482 // no object, no hashcode
1489 static CFStringRef
CreateTruncatedURLString(CFAllocatorRef alloc
, CFStringRef urlString
, CFIndex maxLength
, CFIndex suffixLength
)
1493 CFIndex len
= CFStringGetLength(urlString
);
1494 if ( len
<= maxLength
) {
1495 // return the retained urlString
1496 result
= CFStringCreateCopy(alloc
, urlString
);
1499 CFStringRef start
= CFStringCreateWithSubstring(alloc
, urlString
, CFRangeMake(0, maxLength
- suffixLength
));
1500 CFStringRef end
= CFStringCreateWithSubstring(alloc
, urlString
, CFRangeMake(len
- suffixLength
, suffixLength
));
1501 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@ ... %@"), start
, end
);
1512 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
1514 CFURLRef url
= (CFURLRef
)cf
;
1515 __CFGenericValidateType(cf
, CFURLGetTypeID());
1516 CFAllocatorRef alloc
= CFGetAllocator(url
);
1518 Boolean isDataURL
= false;
1519 CFStringRef scheme
= CFURLCopyScheme(url
);
1521 isDataURL
= CFStringCompare(scheme
, kCFURLDataScheme
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
;
1528 result
= CFStringCreateCopy(alloc
, url
->_string
);
1531 // Do not dereference url->_base; it may be an ObjC object
1532 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
1536 if ( !url
->_base
) {
1537 result
= CreateTruncatedURLString(alloc
, url
->_string
, 128, 8);
1540 CFStringRef urlString
= CreateTruncatedURLString(alloc
, url
->_string
, 128, 8);
1541 CFStringRef baseString
= CreateTruncatedURLString(alloc
, CFURLGetString(url
->_base
), 128, 8);
1542 // Do not dereference url->_base; it may be an ObjC object
1543 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@ -- %@"), urlString
, baseString
);
1545 CFRelease(urlString
);
1548 CFRelease(baseString
);
1556 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
1557 CFURLRef url
= (CFURLRef
)cf
;
1559 CFAllocatorRef alloc
= CFGetAllocator(url
);
1560 Boolean isDataURL
= false;
1561 CFStringRef scheme
= CFURLCopyScheme(url
);
1564 isDataURL
= CFStringCompare(scheme
, kCFURLDataScheme
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
;
1570 CFStringRef baseString
= CFCopyDescription(url
->_base
);
1571 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u\n\tbase = %@}"), cf
, alloc
, url
->_string
, (unsigned int)(url
->_encoding
), baseString
);
1572 CFRelease(baseString
);
1574 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf
, alloc
, url
->_string
, (unsigned int)(url
->_encoding
));
1578 CFStringRef urlString
= CreateTruncatedURLString(alloc
, url
->_string
, 128, 8);
1580 CFStringRef baseString
= CFCopyDescription(url
->_base
);
1581 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u\n\tbase = %@}"), cf
, alloc
, urlString
, (unsigned int)(url
->_encoding
), baseString
);
1582 CFRelease(baseString
);
1584 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf
, alloc
, urlString
, (unsigned int)(url
->_encoding
));
1586 CFRelease(urlString
);
1591 #if DEBUG_URL_MEMORY_USAGE
1592 extern __attribute((used
)) void __CFURLDumpMemRecord(void) {
1593 syslog(LOG_ERR
, "%d URLs created; %d destroyed; %d URLs parsed; %d urls had 'extra' data allocated; %d had base urls; %d were not UTF8 encoded", numURLs
, numDealloced
, numFileURLsParsed
, numExtraDataAllocated
, numURLsWithBaseURL
, numNonUTF8EncodedURLs
);
1597 static void __CFURLDeallocate(CFTypeRef cf
) {
1598 CFURLRef url
= (CFURLRef
)cf
;
1599 CFAllocatorRef alloc
;
1600 __CFGenericValidateType(cf
, CFURLGetTypeID());
1601 alloc
= CFGetAllocator(url
);
1602 #if DEBUG_URL_MEMORY_USAGE
1605 if (url
->_string
) CFRelease(url
->_string
); // GC: 3879914
1606 if (url
->_base
) CFRelease(url
->_base
);
1607 if (url
->_ranges
) CFAllocatorDeallocate(alloc
, url
->_ranges
);
1608 CFStringRef sanitizedString
= _getSanitizedString(url
);
1609 if (sanitizedString
) CFRelease(sanitizedString
);
1610 if ( url
->_extra
!= NULL
) CFAllocatorDeallocate( alloc
, url
->_extra
);
1611 if (_getResourceInfo(url
)) CFRelease(_getResourceInfo(url
));
1614 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
1616 static const CFRuntimeClass __CFURLClass
= {
1618 "CFURL", // className
1621 __CFURLDeallocate
, // finalize
1622 __CFURLEqual
, // equal
1623 __CFURLHash
, // hash
1624 __CFURLCopyFormattingDescription
, // copyFormattingDesc
1625 __CFURLCopyDescription
, // copyDebugDesc
1630 CF_PRIVATE
void __CFURLInitialize(void) {
1631 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1634 /* Toll-free bridging support; get the true CFURL from an NSURL */
1635 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1636 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID
, CFURLRef
, (NSURL
*)url
, _cfurl
);
1640 CFTypeID
CFURLGetTypeID(void) {
1641 return __kCFURLTypeID
;
1644 CF_PRIVATE
void CFShowURL(CFURLRef url
) {
1646 fprintf(stdout
, "(null)\n");
1649 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1650 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1651 fprintf(stdout
, "ObjC bridged object}\n");
1654 fprintf(stdout
, "\n\tRelative string: ");
1655 CFShow(url
->_string
);
1656 fprintf(stdout
, "\tBase URL: ");
1658 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1661 fprintf(stdout
, "(null)\n");
1663 fprintf(stdout
, "\tFlags: 0x%x\n}\n", (unsigned int)url
->_flags
);
1667 /***************************************************/
1668 /* URL creation and String/Data creation from URLS */
1669 /***************************************************/
1670 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, UInt8
*inBuffer
, CFIndex inBufferSize
, const char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1671 CFIndex neededLength
;
1675 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1679 *freeCharacters
= false;
1683 *ustring
= CFStringGetCharactersPtr(string
);
1685 *useCString
= false;
1686 *freeCharacters
= false;
1690 length
= CFStringGetLength(string
);
1691 rg
= CFRangeMake(0, length
);
1692 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1693 if (neededLength
== length
) {
1695 if ( (inBuffer
!= NULL
) && (length
<= inBufferSize
) ) {
1696 buf
= (char *)inBuffer
;
1697 *freeCharacters
= false;
1700 buf
= (char *)CFAllocatorAllocate(alloc
, length
, 0);
1701 *freeCharacters
= true;
1703 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, (uint8_t *)buf
, length
, NULL
);
1708 if ( (inBuffer
!= NULL
) && ((length
* sizeof(UniChar
)) <= inBufferSize
) ) {
1709 buf
= (UniChar
*)inBuffer
;
1710 *freeCharacters
= false;
1713 buf
= (UniChar
*)CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1714 *freeCharacters
= true;
1716 CFStringGetCharacters(string
, rg
, buf
);
1718 *useCString
= false;
1722 static void _parseComponentsCString(CFAllocatorRef alloc
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
, CFIndex cfStringLength
, const char *characterArray
)
1723 #define CFURL_INCLUDE_PARSE_COMPONENTS
1724 #include "CFURL.inc.h"
1725 #undef CFURL_INCLUDE_PARSE_COMPONENTS
1727 static void _parseComponentsUString(CFAllocatorRef alloc
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
, CFIndex cfStringLength
, const UniChar
*characterArray
)
1728 #define CFURL_INCLUDE_PARSE_COMPONENTS
1729 #include "CFURL.inc.h"
1730 #undef CFURL_INCLUDE_PARSE_COMPONENTS
1732 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1733 CFIndex cfStringLength
= CFStringGetLength(string
);
1734 Boolean useCString
, freeCharacters
;
1735 const char *cstring
= NULL
;
1736 const UniChar
*ustring
= NULL
;
1737 CFIndex stackBufferSize
= 4096;
1738 STACK_BUFFER_DECL(UInt8
, stackBuffer
, stackBufferSize
);
1740 constructBuffers(alloc
, string
, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1743 _parseComponentsCString(alloc
, baseURL
, theFlags
, range
, cfStringLength
, cstring
);
1746 _parseComponentsUString(alloc
, baseURL
, theFlags
, range
, cfStringLength
, ustring
);
1749 if (freeCharacters
) {
1750 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1754 static Boolean
scanCharactersCString(CFAllocatorRef alloc
, CFMutableStringRef
*escapedString
, UInt32
*flags
, const char *characterArray
, Boolean useCString
, CFIndex base
, CFIndex end
, CFIndex
*mark
, UInt32 componentFlag
, CFStringEncoding encoding
)
1755 #define CFURL_INCLUDE_SCAN_CHARACTERS
1756 #include "CFURL.inc.h"
1757 #undef CFURL_INCLUDE_SCAN_CHARACTERS
1759 static Boolean
scanCharactersUString(CFAllocatorRef alloc
, CFMutableStringRef
*escapedString
, UInt32
*flags
, const UniChar
*characterArray
, Boolean useCString
, CFIndex base
, CFIndex end
, CFIndex
*mark
, UInt32 componentFlag
, CFStringEncoding encoding
)
1760 #define CFURL_INCLUDE_SCAN_CHARACTERS
1761 #include "CFURL.inc.h"
1762 #undef CFURL_INCLUDE_SCAN_CHARACTERS
1764 static Boolean
scanCharacters(CFAllocatorRef alloc
, CFMutableStringRef
*escapedString
, UInt32
*flags
, const char *cstring
, const UniChar
*ustring
, Boolean useCString
, CFIndex base
, CFIndex end
, CFIndex
*mark
, UInt32 componentFlag
, CFStringEncoding encoding
) {
1766 return ( scanCharactersCString(alloc
, escapedString
, flags
, cstring
, useCString
, base
, end
, mark
, componentFlag
, encoding
));
1769 return ( scanCharactersUString(alloc
, escapedString
, flags
, ustring
, useCString
, base
, end
, mark
, componentFlag
, encoding
));
1773 static void computeSanitizedString(CFURLRef url
) {
1774 CFAllocatorRef alloc
= CFGetAllocator(url
);
1775 CFIndex string_length
= CFStringGetLength(url
->_string
);
1776 Boolean useCString
, freeCharacters
;
1777 const char *cstring
= NULL
;
1778 const UniChar
*ustring
= NULL
;
1779 CFIndex base
; // where to scan from
1780 CFIndex mark
; // first character not-yet copied to sanitized string
1781 CFIndex stackBufferSize
= 4096;
1782 STACK_BUFFER_DECL(UInt8
, stackBuffer
, stackBufferSize
);
1783 CFMutableStringRef sanitizedString
= NULL
;
1784 UInt32 additionalDataFlags
= 0;
1786 constructBuffers(alloc
, url
->_string
, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1787 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1788 // Impossible to have a problem character in the scheme
1789 base
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
).length
+ 1;
1791 if (!scanCharacters(alloc
, &sanitizedString
, &additionalDataFlags
, cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1792 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1794 if ( sanitizedString
) {
1795 _setAdditionalDataFlags((struct __CFURL
*)url
, additionalDataFlags
);
1798 // Go component by component
1799 CFIndex currentComponent
= HAS_USER
;
1801 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1802 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->_ranges
, currentComponent
);
1803 if (componentRange
.location
!= kCFNotFound
) {
1804 scanCharacters(alloc
, &sanitizedString
, &additionalDataFlags
, cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1806 currentComponent
= currentComponent
<< 1;
1808 if (sanitizedString
) {
1809 _setAdditionalDataFlags((struct __CFURL
*)url
, additionalDataFlags
);
1811 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1814 if (sanitizedString
&& mark
!= string_length
) {
1816 __CFStringAppendBytes(sanitizedString
, (char *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
);
1818 CFStringAppendCharacters(sanitizedString
, &(ustring
[mark
]), string_length
- mark
);
1821 if ( sanitizedString
) {
1822 _setSanitizedString((struct __CFURL
*) url
, sanitizedString
);
1823 CFRelease(sanitizedString
);
1825 if (freeCharacters
) {
1826 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1831 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1832 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1833 CFIndex string_length
= CFStringGetLength(comp
);
1834 Boolean useCString
, freeCharacters
;
1835 const char *cstring
= NULL
;
1836 const UniChar
*ustring
= NULL
;
1837 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1838 CFMutableStringRef result
= NULL
;
1839 CFIndex stackBufferSize
= 1024;
1840 STACK_BUFFER_DECL(UInt8
, stackBuffer
, stackBufferSize
);
1842 constructBuffers(alloc
, comp
, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1843 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1845 if (mark
< string_length
) {
1847 __CFStringAppendBytes(result
, (char *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
);
1849 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1853 // This should nevr happen
1855 result
= (CFMutableStringRef
)comp
;
1857 if (freeCharacters
) {
1858 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1864 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1865 struct __CFURL
*url
;
1866 #if DEBUG_URL_MEMORY_USAGE
1868 // if ( numURLs % 1000 == 0 ) {
1869 // __CFURLDumpMemRecord();
1872 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1875 url
->_encoding
= kCFStringEncodingUTF8
;
1876 url
->_string
= NULL
;
1878 url
->_ranges
= NULL
;
1880 url
->_resourceInfo
= NULL
;
1885 // 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. If parseURL is false, the caller is responsible for parsing the URL.
1886 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, CFURLRef base
, Boolean parseURL
) {
1888 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1889 url
->_string
= CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1890 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1892 #if DEBUG_URL_MEMORY_USAGE
1894 numURLsWithBaseURL
++;
1898 #if DEBUG_URL_MEMORY_USAGE
1899 numFileURLsParsed
++;
1901 _parseComponentsOfURL(url
);
1905 static Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1906 Boolean result
= true;
1908 CFStringInlineBuffer stringBuffer
;
1909 Boolean sawHash
= false;
1911 CFIndex checkHexDigit
= 0;
1914 length
= CFStringGetLength(string
);
1916 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1918 while ( idx
< length
) {
1919 CFIndex rangeLength
;
1920 const UniChar
*chPtr
;
1922 rangeLength
= (idx
+ __kCFStringInlineBufferLength
<= length
) ? __kCFStringInlineBufferLength
: length
- idx
;
1923 chPtr
= CFStringGetCharactersPtrFromInlineBuffer(&stringBuffer
, CFRangeMake(idx
, rangeLength
));
1924 for ( CFIndex rangeIdx
= 0; rangeIdx
< rangeLength
; ++rangeIdx
, ++chPtr
) {
1925 if ( !checkHexDigit
) {
1926 if ( *chPtr
== '%' ) {
1927 // percent encoded? make sure there at least 2 characters left to check
1928 if ( (idx
+ rangeIdx
+ 2) < length
) {
1929 // the next 2 characters must be HEXDIG
1938 if ( *chPtr
== '[' || *chPtr
== ']' ) {
1939 continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1941 if ( *chPtr
== '#' ) {
1942 // only allow one # character
1952 #if DEPLOYMENT_TARGET_WINDOWS
1953 // <rdar://problem/7134119> CF on Windows: CFURLCreateWithString should work with | in path on Windows
1954 if ( isURLLegalCharacter(*chPtr
) || *chPtr
== '|' ) {
1958 if ( isURLLegalCharacter(*chPtr
) ) {
1968 if ( isHexDigit(*chPtr
) ) {
1980 break; // out of main while loop
1988 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1995 /* initializes a URL object with a URL string */
1996 CF_EXPORT Boolean
_CFURLInitWithURLString(CFURLRef uninitializedURL
, CFStringRef string
, Boolean checkForLegalCharacters
, CFURLRef baseURL
)
1998 #if DEBUG_URL_INITIALIZER_LOGGING
1999 CFStringRef input_string
= string
? CFRetain(string
) : NULL
;
2000 Boolean input_checkForLegalCharacters
= checkForLegalCharacters
;
2001 CFURLRef input_baseURL
= baseURL
? CFRetain(baseURL
) : NULL
;
2003 Boolean result
= checkForLegalCharacters
? _CFStringIsLegalURLString(string
) : true;
2006 // determine if URL is absolute (i.e. it has a scheme and the scheme characters are valid
2007 CFStringInlineBuffer stringBuffer
;
2008 CFIndex length
= CFStringGetLength(string
);
2010 Boolean isAbsolute
= false;
2011 Boolean schemeCharsValid
= true;
2013 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
2014 // the first character of the scheme must be ALPHA
2015 if ( (length
> 0) && isALPHA(__CFStringGetCharacterFromInlineBufferQuick(&stringBuffer
, 0)) ) {
2016 // stop looking if we hit the end of the string, find a colon (isAbsolute), or find a non-scheme character (schemeCharsValid)
2017 while (idx
< length
&& schemeCharsValid
&& !isAbsolute
) {
2018 CFIndex rangeLength
= (idx
+ __kCFStringInlineBufferLength
<= length
) ? __kCFStringInlineBufferLength
: length
- idx
;
2019 const UniChar
*chPtr
= CFStringGetCharactersPtrFromInlineBuffer(&stringBuffer
, CFRangeMake(idx
, rangeLength
));
2020 for (CFIndex i
= 0; i
< rangeLength
; ++i
, ++chPtr
) {
2021 if ( *chPtr
== ':' ) {
2025 if ( !scheme_valid(*chPtr
) ) {
2026 schemeCharsValid
= false;
2037 _CFURLInit((struct __CFURL
*)uninitializedURL
, string
, isAbsolute
? NULL
: baseURL
, TRUE
);
2039 #if DEBUG_URL_INITIALIZER_LOGGING
2040 CFLog(kCFLogLevelError
, CFSTR("_CFURLInitWithURLString (in) string '%@', checkForLegalCharacters %@, baseURL %@\n\t_CFURLInitWithURLString (out) result %@, url %@"), input_string
, input_checkForLegalCharacters
? CFSTR("YES") : CFSTR("NO"), input_baseURL
, result
? CFSTR("YES") : CFSTR("NO"), uninitializedURL
);
2041 if ( input_string
) {
2042 CFRelease(input_string
);
2044 if ( input_baseURL
) {
2045 CFRelease(input_baseURL
);
2051 /* initializes a URL object with a file system path */
2052 CF_EXPORT Boolean
_CFURLInitWithFileSystemPath(CFURLRef uninitializedURL
, CFStringRef fileSystemPath
, CFURLPathStyle pathStyle
, Boolean isDirectory
, CFURLRef baseURL
)
2054 #if DEBUG_URL_INITIALIZER_LOGGING
2055 CFStringRef input_fileSystemPath
= fileSystemPath
? CFRetain(fileSystemPath
) : NULL
;
2056 CFURLPathStyle input_pathStyle
= pathStyle
;
2057 Boolean input_isDirectory
= isDirectory
;
2058 CFURLRef input_baseURL
= baseURL
? CFRetain(baseURL
) : NULL
;
2060 Boolean result
= false;
2061 CFAssert1(fileSystemPath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
2062 #pragma GCC diagnostic push
2063 #pragma GCC diagnostic ignored "-Wdeprecated"
2064 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
2065 #pragma GCC diagnostic pop
2067 struct __CFURL
*url
= (struct __CFURL
*)uninitializedURL
;
2068 CFAllocatorRef alloc
= CFGetAllocator(uninitializedURL
);
2069 CFStringRef urlString
= NULL
;
2071 Boolean isFileReferencePath
= false;
2072 Boolean posixAndUrlPathsMatch
= false;
2073 UniChar pathDelim
= '\0';
2074 Boolean releaseBaseURL
= false;
2075 CFIndex len
= CFStringGetLength(fileSystemPath
);
2078 // determine if fileSystemPath is an absolute path and what pathDelim we're using
2079 switch (pathStyle
) {
2080 case kCFURLPOSIXPathStyle
:
2082 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(fileSystemPath
, 0) == pathDelim
);
2086 case kCFURLWindowsPathStyle
:
2088 // this is what _CFURLInitFSPath() did (with a small change to handle absolute paths that begin with a single backslash)
2089 UniChar firstChar
= 0 < len
? CFStringGetCharacterAtIndex(fileSystemPath
, 0) : 0;
2090 UniChar secondChar
= 1 < len
? CFStringGetCharacterAtIndex(fileSystemPath
, 1) : 0;
2091 Boolean isDrive
= isALPHA(firstChar
) && (secondChar
== ':' || secondChar
== '|');
2094 // A disk designator
2098 else if ( firstChar
== '\\' ) {
2099 // Either a UNC name of any format (which always start with two backslash characters), or an absolute path which starts with a single backslash.
2103 else if (firstChar
== '/') {
2104 // We switch to kCFURLPOSIXPathStyle here because there's probably code that passes POSIX paths in but wrongly specifies kCFURLWindowsPathStyle.
2105 pathStyle
= kCFURLPOSIXPathStyle
;
2110 // We switch to kCFURLPOSIXPathStyle here because this is not an absolute path and we're going to set baseURL to the current working directory below, and so we assume the relative path will have to be combined with the base path at some point.
2111 pathStyle
= kCFURLPOSIXPathStyle
;
2119 // Convert the fileSystemPath to a urlString and determine if it is a file reference path.
2120 // The urlString returned will have a pathDelim at the end if isDirectory was true and no pathDelim if isDirectory was false (unless the urlPath is "/")
2121 // If isAbsolute, "file://" will be prepended to the urlString.
2122 switch (pathStyle
) {
2123 case kCFURLPOSIXPathStyle
:
2124 isFileReferencePath
= _pathHasFileIDPrefix(fileSystemPath
);
2125 urlString
= POSIXPathToURLPath(fileSystemPath
, alloc
, isDirectory
, isAbsolute
, &posixAndUrlPathsMatch
);
2129 case kCFURLWindowsPathStyle
:
2130 urlString
= WindowsPathToURLPath(fileSystemPath
, alloc
, isDirectory
, isAbsolute
);
2134 CFAssert2(urlString
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, urlString
);
2138 // if fileSystemPath is an absolute path, ignore baseURL (if provided)
2141 else if ( baseURL
== NULL
) {
2142 // if fileSystemPath is a relative path and no baseURL is provided, use the current working directory
2143 baseURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(uninitializedURL
));
2144 releaseBaseURL
= true;
2147 // override isDirectory if the path is to root
2148 if ( !isDirectory
&& (len
== 1) && (CFStringGetCharacterAtIndex(urlString
, 0) == pathDelim
) ) {
2149 // Override isDirectory
2153 _CFURLInit(url
, urlString
, baseURL
, FALSE
);
2157 UInt32 flags
= IS_DECOMPOSABLE
| HAS_SCHEME
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
2158 flags
|= (isDirectory
? IS_DIRECTORY
: 0);
2159 if ( isFileReferencePath
) {
2160 // if the URL is a file reference URL, don't set IS_CANONICAL_FILE_URL or POSIX_AND_URL_PATHS_MATCH
2161 flags
|= PATH_HAS_FILE_ID
;
2164 // only posix style paths can be canonical because POSIXPathToURLPath() converts the string to file system representation
2165 flags
|= ((pathStyle
== kCFURLPOSIXPathStyle
) ? IS_CANONICAL_FILE_URL
: 0);
2166 flags
|= (posixAndUrlPathsMatch
? POSIX_AND_URL_PATHS_MATCH
: 0);
2168 _setSchemeTypeInFlags(&flags
, kHasFileScheme
);
2170 if ( AddAuthorityToFileURL() ) {
2171 url
->_flags
= flags
| HAS_HOST
;
2172 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
2173 url
->_ranges
[0] = CFRangeMake(0, 4); // scheme "file"
2174 url
->_ranges
[1] = CFRangeMake(7, 9); // host "localhost"
2175 url
->_ranges
[2] = CFRangeMake(16, CFStringGetLength(urlString
)- 16);
2178 url
->_flags
= flags
;
2179 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 2, 0);
2180 url
->_ranges
[0] = CFRangeMake(0, 4); // scheme "file"
2181 url
->_ranges
[1] = CFRangeMake(7, CFStringGetLength(urlString
)- 7);
2184 url
->_flags
= (isDirectory
? IS_DIRECTORY
: 0) | IS_DECOMPOSABLE
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
2185 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
2186 *(url
->_ranges
) = CFRangeMake(0, CFStringGetLength(url
->_string
));
2189 if ( releaseBaseURL
&& baseURL
) {
2193 CFRelease(urlString
);
2197 #if DEBUG_URL_INITIALIZER_LOGGING
2198 #pragma GCC diagnostic push
2199 #pragma GCC diagnostic ignored "-Wdeprecated"
2200 CFLog(kCFLogLevelError
, CFSTR("_CFURLInitWithFileSystemPath (in) fileSystemPath '%@', pathStyle %@, isDirectory %@, baseURL %@\n\t_CFURLInitWithFileSystemPath (out) result %@, url %@"), input_fileSystemPath
, input_pathStyle
== kCFURLPOSIXPathStyle
? CFSTR("POSIX") : input_pathStyle
== kCFURLHFSPathStyle
? CFSTR("HFS"): CFSTR("Windows"), input_isDirectory
? CFSTR("YES") : CFSTR("NO"), input_baseURL
, result
? CFSTR("YES") : CFSTR("NO"), uninitializedURL
);
2201 #pragma GCC diagnostic pop
2202 if ( input_fileSystemPath
) {
2203 CFRelease(input_fileSystemPath
);
2205 if ( input_baseURL
) {
2206 CFRelease(input_baseURL
);
2212 /* initializes a URL object with the file system representation */
2213 CF_EXPORT Boolean
_CFURLInitWithFileSystemRepresentation(CFURLRef uninitializedURL
, const UInt8
*buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
)
2215 #if DEBUG_URL_INITIALIZER_LOGGING
2216 STACK_BUFFER_DECL(UInt8
, input_buffer
, bufLen
);
2217 CFIndex input_bufLen
= bufLen
;
2218 Boolean input_isDirectory
= isDirectory
;
2219 CFURLRef input_baseURL
= baseURL
? CFRetain(baseURL
) : NULL
;
2220 memcpy(input_buffer
, buffer
, bufLen
);
2222 Boolean result
= false;
2223 CFAllocatorRef alloc
= CFGetAllocator(uninitializedURL
);
2224 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
2225 struct __CFURL
*url
= (struct __CFURL
*)uninitializedURL
;
2226 Boolean isAbsolute
= bufLen
&& (*buffer
== '/');
2227 Boolean addedPercentEncoding
;
2228 Boolean releaseBaseURL
= false;
2231 // if buffer contains an absolute path, ignore baseURL (if provided)
2234 else if ( baseURL
== NULL
) {
2235 // if buffer contains a relative path and no baseURL is provided, use the current working directory
2236 baseURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(uninitializedURL
));
2237 releaseBaseURL
= true;
2239 CFStringRef urlString
= CreateStringFromFileSystemRepresentationByAddingPercentEscapes(alloc
, buffer
, bufLen
, isDirectory
, isAbsolute
, false /*windowsPath*/, &addedPercentEncoding
);
2241 _CFURLInit(url
, urlString
, baseURL
, FALSE
);
2245 if ( AddAuthorityToFileURL() ) {
2246 url
->_flags
= (addedPercentEncoding
? 0 : POSIX_AND_URL_PATHS_MATCH
) | (isDirectory
? IS_DIRECTORY
: 0) | IS_DECOMPOSABLE
| HAS_SCHEME
| HAS_HOST
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
| IS_CANONICAL_FILE_URL
;
2247 _setSchemeTypeInFlags(&url
->_flags
, kHasFileScheme
);
2248 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
2249 url
->_ranges
[0] = CFRangeMake(0, 4); // scheme "file"
2250 url
->_ranges
[1] = CFRangeMake(7, 9); // host "localhost"
2251 url
->_ranges
[2] = CFRangeMake(16, CFStringGetLength(urlString
)- 16);
2254 url
->_flags
= (addedPercentEncoding
? 0 : POSIX_AND_URL_PATHS_MATCH
) | (isDirectory
? IS_DIRECTORY
: 0) | IS_DECOMPOSABLE
| HAS_SCHEME
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
| IS_CANONICAL_FILE_URL
;
2255 _setSchemeTypeInFlags(&url
->_flags
, kHasFileScheme
);
2256 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 2, 0);
2257 url
->_ranges
[0] = CFRangeMake(0, 4); // scheme "file"
2258 url
->_ranges
[1] = CFRangeMake(7, CFStringGetLength(urlString
)- 7);
2261 url
->_flags
= (addedPercentEncoding
? 0 : POSIX_AND_URL_PATHS_MATCH
) | (isDirectory
? IS_DIRECTORY
: 0) | IS_DECOMPOSABLE
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
2262 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
2263 *(url
->_ranges
) = CFRangeMake(0, CFStringGetLength(url
->_string
));
2265 if ( releaseBaseURL
&& baseURL
) {
2268 CFRelease(urlString
);
2271 #elif DEPLOYMENT_TARGET_WINDOWS
2272 CFStringRef filePath
= CFStringCreateWithBytes(alloc
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
2274 result
= _CFURLInitWithFileSystemPath(uninitializedURL
, filePath
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
2275 CFRelease(filePath
);
2278 #if DEBUG_URL_INITIALIZER_LOGGING
2279 CFLog(kCFLogLevelError
, CFSTR("_CFURLInitWithFileSystemRepresentation (in) buffer '%*s', isDirectory %@, baseURL %@\n\t_CFURLInitWithFileSystemRepresentation (out) result %@, url %@"), input_bufLen
, input_buffer
, input_isDirectory
? CFSTR("YES") : CFSTR("NO"), input_baseURL
, result
? CFSTR("YES") : CFSTR("NO"), uninitializedURL
);
2280 if ( input_baseURL
) {
2281 CFRelease(input_baseURL
);
2287 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
2288 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
2289 CFStringRef urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, encoding
, false);
2290 CFURLRef result
= NULL
;
2292 if ( baseURL
|| CFStringGetLength(urlString
) != 0 ) {
2293 result
= _CFURLAlloc(allocator
);
2295 if ( !_CFURLInitWithURLString(result
, urlString
, false /* checkForLegalCharacters */, baseURL
) ) {
2300 if (encoding
!= kCFStringEncodingUTF8
) {
2301 ((struct __CFURL
*)result
)->_encoding
= encoding
;
2302 #if DEBUG_URL_MEMORY_USAGE
2303 numNonUTF8EncodedURLs
++;
2309 CFRelease(urlString
); // it's retained by result, now.
2314 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
2315 CFDataRef result
= NULL
;
2317 CFStringRef myStr
= CFURLGetString(url
);
2319 result
= CFStringCreateExternalRepresentation(allocator
, myStr
, encoding
, 0);
2325 // Any escape sequences in URLString will be interpreted via UTF-8.
2326 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
2327 CFURLRef url
= NULL
;
2328 if ( URLString
&& (baseURL
|| CFStringGetLength(URLString
) != 0) ) {
2329 url
= _CFURLAlloc(allocator
);
2331 if ( !_CFURLInitWithURLString(url
, URLString
, true /* checkForLegalCharacters */, baseURL
) ) {
2340 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
2341 CFURLRef url
= NULL
;
2342 if ( URLString
&& (baseURL
|| CFStringGetLength(URLString
) != 0) ) {
2343 url
= _CFURLAlloc(allocator
);
2345 if ( !_CFURLInitWithURLString(url
, URLString
, false /* checkForLegalCharacters */, baseURL
) ) {
2354 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
2355 CFURLRef result
= NULL
;
2358 CFURLCreateAbsoluteURLWithBytes() and useCompatibilityMode is for:
2359 <rdar://problem/2711611> Problem with '|' in url and calling CFURLCreateWithString()
2360 <rdar://problem/3085893> CFURL resolves "../" URLs at top level in a way that is not the same as web browsers
2361 <rdar://problem/3085920> CFURL API should be more helpful for accepting URLs with technically-bad characters
2362 <rdar://problem/3205656> Safari needs CFURL to deal with google.com URLs that end in "%"
2363 <rdar://problem/3219233> Safari needs CFURL to not remove path when relative URL is just a query string
2364 <rdar://problem/3219240> Safari needs CFURL to support "compatibility" resolution of partial URLs with schemes
2366 If useCompatibilityMode is true, the rules historically used on the web are used to resolve relativeString against baseURL - these rules are generally listed in the RFC as optional or alternate interpretations. Otherwise, the strict rules from the RFC are used.
2368 The major differences are that in compatibility mode, we are lenient of the scheme appearing in relative portion, leading "../" components are removed from the final URL's path, and if the relative portion contains only resource specifier pieces (query, parameters, and fragment), then the last path component of the base URL will not be deleted
2371 // if not useCompatibilityMode, use CFURLCreateWithBytes and then CFURLCopyAbsoluteURL if there's a baseURL
2372 if ( !useCompatibilityMode
) {
2373 CFURLRef url
= CFURLCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, baseURL
);
2374 if ( url
!= NULL
) {
2375 if ( baseURL
!= NULL
) {
2376 result
= CFURLCopyAbsoluteURL(url
);
2383 UInt32 absFlags
= 0;
2385 CFStringRef absString
= NULL
;
2386 Boolean absStringIsMutable
= false;
2388 CFStringRef relativeString
;
2390 relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
2391 if ( relativeString
!= NULL
) {
2393 absString
= relativeString
;
2396 if ( CFStringGetLength(relativeString
) > 0 ) {
2397 ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
2399 if (ch
== '?' || ch
== ';' || ch
== '#') {
2400 // Nothing but parameter + query + fragment; append to the baseURL string
2401 CFStringRef baseString
;
2402 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2403 baseString
= CFURLGetString(baseURL
);
2405 baseString
= baseURL
->_string
;
2407 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
2408 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
2409 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
2410 absStringIsMutable
= true;
2412 UInt32 relFlags
= 0;
2414 CFStringRef relString
= NULL
;
2415 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
2416 if (relFlags
& HAS_SCHEME
) {
2417 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
2418 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
2419 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
2420 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
2421 CFAllocatorDeallocate(alloc
, relRanges
);
2423 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
2425 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
2426 CFRetain(relativeString
);
2427 absString
= relativeString
;
2429 if (baseScheme
) CFRelease(baseScheme
);
2431 CFRetain(relativeString
);
2432 relString
= relativeString
;
2435 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2436 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->_ranges
);
2438 CFStringRef baseString
;
2439 UInt32 baseFlags
= 0;
2440 CFRange
*baseRanges
;
2441 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2442 baseString
= CFURLGetString(baseURL
);
2444 baseString
= baseURL
->_string
;
2446 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2447 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
2448 CFAllocatorDeallocate(alloc
, baseRanges
);
2450 absStringIsMutable
= true;
2452 if (relString
) CFRelease(relString
);
2453 CFAllocatorDeallocate(alloc
, relRanges
);
2455 CFRelease(relativeString
);
2459 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
2460 if (absFlags
& HAS_PATH
) {
2461 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
2462 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
2463 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), 0);
2464 CFStringRef newPath
;
2465 CFStringGetCharacters(absString
, pathRg
, buf
);
2466 buf
[pathRg
.length
] = '\0';
2467 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
2468 if (CFStringGetLength(newPath
) != pathRg
.length
) {
2469 if (!absStringIsMutable
) {
2470 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
2471 CFRelease(absString
);
2474 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
2477 // Do not deallocate buf; newPath took ownership of it.
2479 CFAllocatorDeallocate(alloc
, absRanges
);
2480 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
2481 CFRelease(absString
);
2483 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
2484 #if DEBUG_URL_MEMORY_USAGE
2485 if ( encoding
!= kCFStringEncodingUTF8
) {
2486 numNonUTF8EncodedURLs
++;
2497 /* 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 */
2498 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
2499 UniChar
*idx
= pathStr
;
2503 if (idx
!= pathStr
) {
2508 } else if (*(idx
+1) == pathDelimiter
) {
2509 if (idx
+ 2 != end
|| idx
!= pathStr
) {
2510 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
2514 // Do not delete the sole path component
2517 } else if (( end
-idx
>= 2 ) && *(idx
+1) == '.' && (idx
+2 == end
|| (( end
-idx
> 2 ) && *(idx
+2) == pathDelimiter
))) {
2518 if (idx
- pathStr
>= 2) {
2519 // 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.
2520 UniChar
*lastDelim
= idx
-2;
2521 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
2523 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
2524 // We have a genuine component to compact out
2526 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
2527 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2528 end
-= (idx
+ 3 - lastDelim
);
2531 } else if (lastDelim
!= pathStr
) {
2536 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
2544 } else if (stripLeadingDotDots
) {
2545 if (idx
+ 3 != end
) {
2546 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
2547 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2551 // Do not devolve the last path component
2557 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
2560 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
2563 // return an zero-length string if end < pathStr
2564 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
>= pathStr
? end
- pathStr
: 0, alloc
);
2567 static CFMutableStringRef
resolveAbsoluteURLStringBuffer(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
, UniChar
*buf
)
2569 CFStringAppendBuffer appendBuffer
;
2571 CFStringInitAppendBuffer(alloc
, &appendBuffer
);
2574 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
2575 if (rg
.location
!= kCFNotFound
) {
2576 CFStringGetCharacters(baseString
, rg
, buf
);
2577 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2579 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2582 if (relFlags
& NET_LOCATION_MASK
) {
2583 CFStringAppendStringToAppendBuffer(&appendBuffer
, relString
);
2587 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 2);
2588 rg
= _netLocationRange(baseFlags
, baseRanges
);
2589 if (rg
.location
!= kCFNotFound
) {
2590 CFStringGetCharacters(baseString
, rg
, buf
);
2591 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2594 if (relFlags
& HAS_PATH
) {
2595 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
2596 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2597 CFStringRef newPath
;
2598 Boolean useRelPath
= false;
2599 Boolean useBasePath
= false;
2600 if (basePathRg
.location
== kCFNotFound
) {
2602 } else if (relPathRg
.length
== 0) {
2604 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
2606 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2610 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2611 } else if (useBasePath
) {
2612 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2614 // FIXME: Get rid of this allocation
2615 UniChar
*newPathBuf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
2617 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
2618 idx
= newPathBuf
+ basePathRg
.length
- 1;
2619 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
2620 if (*idx
== '/') idx
++;
2621 CFStringGetCharacters(relString
, relPathRg
, idx
);
2622 end
= idx
+ relPathRg
.length
;
2624 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2626 /* Under Win32 absolute path can begin with letter
2627 * so we have to add one '/' to the newString
2630 // No - the input strings here are URL path strings, not Win32 paths.
2631 // Absolute paths should have had a '/' prepended before this point.
2632 // I have removed Sergey Zubarev's change and left his comment (and
2633 // this one) as a record. - REW, 1/5/2004
2635 // if the relative URL does not begin with a slash and
2636 // the base does not end with a slash, add a slash
2637 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2639 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2642 CFStringAppendStringToAppendBuffer(&appendBuffer
, newPath
);
2644 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2645 rg
.length
= CFStringGetLength(relString
);
2646 if (rg
.length
> rg
.location
) {
2647 rg
.length
-= rg
.location
;
2648 CFStringGetCharacters(relString
, rg
, buf
);
2649 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2652 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2653 if (rg
.location
!= kCFNotFound
) {
2654 CFStringGetCharacters(baseString
, rg
, buf
);
2655 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2658 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2659 // ??? Can this ever happen?
2660 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2662 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2663 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2664 rg
.location
--; // To pick up the separator
2666 CFStringGetCharacters(baseString
, rg
, buf
);
2667 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2669 } else if (relFlags
& HAS_PARAMETERS
) {
2670 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2671 rg
.location
--; // To get the semicolon that starts the parameters
2672 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2673 CFStringGetCharacters(relString
, rg
, buf
);
2674 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2676 // Sigh; we have to resolve these against one another
2677 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2678 if (rg
.location
!= kCFNotFound
) {
2680 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2681 CFStringGetCharacters(baseString
, rg
, buf
);
2682 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2684 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2685 if (rg
.location
!= kCFNotFound
) {
2687 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2688 CFStringGetCharacters(relString
, rg
, buf
);
2689 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2691 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2692 if (rg
.location
!= kCFNotFound
) {
2694 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2695 CFStringGetCharacters(baseString
, rg
, buf
);
2696 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2699 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2700 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2701 if (rg
.location
!= kCFNotFound
) {
2703 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, chars
, 1);
2704 CFStringGetCharacters(relString
, rg
, buf
);
2705 CFStringAppendCharactersToAppendBuffer(&appendBuffer
, buf
, rg
.length
);
2710 return CFStringCreateMutableWithAppendBuffer(&appendBuffer
);
2713 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
2714 CFMutableStringRef result
;
2715 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
2716 if ( bufLen
<= 1024 ) {
2717 STACK_BUFFER_DECL(UniChar
, buf
, bufLen
);
2718 result
= resolveAbsoluteURLStringBuffer(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
, buf
);
2722 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
2723 result
= resolveAbsoluteURLStringBuffer(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
, buf
);
2724 CFAllocatorDeallocate(alloc
, buf
);
2729 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2730 CFURLRef anURL
, base
;
2731 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2732 CFStringRef baseString
, newString
;
2734 CFRange
*baseRanges
;
2737 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2738 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2739 anURL
= (CFURLRef
) CF_OBJC_CALLV((NSURL
*)relativeURL
, absoluteURL
);
2740 if (anURL
) CFRetain(anURL
);
2744 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2746 base
= relativeURL
->_base
;
2748 return (CFURLRef
)CFRetain(relativeURL
);
2750 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2753 baseString
= base
->_string
;
2754 baseFlags
= base
->_flags
;
2755 baseRanges
= base
->_ranges
;
2757 baseString
= CFURLGetString(base
);
2760 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2763 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->_ranges
, baseString
, baseFlags
, baseRanges
);
2765 CFAllocatorDeallocate(alloc
, baseRanges
);
2767 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2768 CFRelease(newString
);
2769 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2770 #if DEBUG_URL_MEMORY_USAGE
2771 if ( relativeURL
->_encoding
!= kCFStringEncodingUTF8
) {
2772 numNonUTF8EncodedURLs
++;
2779 /*******************/
2780 /* Basic accessors */
2781 /*******************/
2782 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2783 return url
->_encoding
;
2786 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2787 anURL
= _CFURLFromNSURL(anURL
);
2788 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2791 CFStringRef
CFURLGetString(CFURLRef url
) {
2792 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID
, CFStringRef
, (NSURL
*)url
, relativeString
);
2793 if (!_haveTestedOriginalString(url
)) {
2794 computeSanitizedString(url
);
2796 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2797 return url
->_string
;
2799 return _getSanitizedString( url
);
2803 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2804 CFIndex length
, charsConverted
, usedLength
;
2806 CFStringEncoding enc
;
2807 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2808 string
= CFURLGetString(url
);
2809 enc
= kCFStringEncodingUTF8
;
2811 string
= url
->_string
;
2812 enc
= url
->_encoding
;
2814 length
= CFStringGetLength(string
);
2815 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2816 if (charsConverted
!= length
) {
2823 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2824 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID
, CFURLRef
, (NSURL
*)anURL
, baseURL
);
2825 return anURL
->_base
;
2828 // Assumes the URL is already parsed
2829 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2831 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2832 while (!(compFlag
& 1)) {
2833 compFlag
= compFlag
>> 1;
2842 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2845 CFAllocatorRef alloc
= CFGetAllocator(url
);
2846 if (removePercentEscapes
) {
2847 fromOriginalString
= true;
2849 rg
= _rangeForComponent(url
->_flags
, url
->_ranges
, compFlag
);
2850 if (rg
.location
== kCFNotFound
) {
2854 if ( compFlag
& HAS_SCHEME
) {
2855 switch ( _getSchemeTypeFromFlags(url
->_flags
) ) {
2856 case kHasHttpScheme
:
2857 comp
= (CFStringRef
)CFRetain(kCFURLHTTPScheme
);
2860 case kHasHttpsScheme
:
2861 comp
= (CFStringRef
)CFRetain(kCFURLHTTPSScheme
);
2864 case kHasFileScheme
:
2865 comp
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
2868 case kHasDataScheme
:
2869 comp
= (CFStringRef
)CFRetain(kCFURLDataScheme
);
2873 comp
= (CFStringRef
)CFRetain(kCFURLFTPScheme
);
2877 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2882 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2885 if (comp
&& !fromOriginalString
) {
2886 if (!_haveTestedOriginalString(url
)) {
2887 computeSanitizedString(url
);
2889 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (_getAdditionalDataFlags(url
) & compFlag
)) {
2890 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2895 if (comp
&& removePercentEscapes
) {
2897 if (url
->_encoding
== kCFStringEncodingUTF8
) {
2898 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2900 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2910 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2912 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2913 scheme
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, scheme
);
2919 switch ( _getSchemeTypeFromFlags(anURL
->_flags
) ) {
2920 case kHasHttpScheme
:
2921 scheme
= (CFStringRef
)CFRetain(kCFURLHTTPScheme
);
2924 case kHasHttpsScheme
:
2925 scheme
= (CFStringRef
)CFRetain(kCFURLHTTPSScheme
);
2928 case kHasFileScheme
:
2929 scheme
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
2932 case kHasDataScheme
:
2933 scheme
= (CFStringRef
)CFRetain(kCFURLDataScheme
);
2937 scheme
= (CFStringRef
)CFRetain(kCFURLFTPScheme
);
2941 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2944 scheme
= CFURLCopyScheme(anURL
->_base
);
2955 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2957 CFRange netRg
= {kCFNotFound
, 0};
2960 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2962 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2963 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2964 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2965 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2966 for (i
= 0; i
< c
; i
++) {
2967 if (netRgs
[i
].location
== kCFNotFound
) continue;
2968 if (netRg
.location
== kCFNotFound
) {
2971 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2977 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2978 anURL
= _CFURLFromNSURL(anURL
);
2979 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2980 // We provide the net location
2981 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->_ranges
);
2983 if (!_haveTestedOriginalString(anURL
)) {
2984 computeSanitizedString(anURL
);
2986 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (_getAdditionalDataFlags(anURL
) & (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2987 // 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.
2989 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
2990 netRg
.length
= CFStringGetLength(sanitizedString
) - netRg
.location
;
2991 if (CFStringFindWithOptions(sanitizedString
, CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2992 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2994 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), sanitizedString
, netRg
);
2996 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2999 } else if (anURL
->_base
) {
3000 return CFURLCopyNetLocation(anURL
->_base
);
3006 // 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.
3007 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
3008 anURL
= _CFURLFromNSURL(anURL
);
3009 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
3012 /* 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.
3014 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.
3016 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
3017 CFStringRef path
= CFURLCopyPath(anURL
);
3018 if (!path
|| CFStringGetLength(path
) == 0) {
3019 if (path
) CFRelease(path
);
3020 if (isAbsolute
) *isAbsolute
= false;
3023 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
3025 if (isAbsolute
) *isAbsolute
= true;
3026 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
3030 if (isAbsolute
) *isAbsolute
= false;
3035 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
3036 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3037 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
3038 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
3041 return CFURLHasDirectoryPath(anURL
->_base
);
3045 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
3046 UInt32 firstRsrcSpecFlag
= 0;
3047 UInt32 flag
= HAS_FRAGMENT
;
3048 while (flag
!= HAS_PATH
) {
3050 firstRsrcSpecFlag
= flag
;
3054 return firstRsrcSpecFlag
;
3057 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
3058 anURL
= _CFURLFromNSURL(anURL
);
3059 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3060 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
3061 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->_ranges
, HAS_SCHEME
);
3062 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
3063 if (!_haveTestedOriginalString(anURL
)) {
3064 computeSanitizedString(anURL
);
3067 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
3069 if (sanitizedString
) {
3070 // 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.
3071 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), sanitizedString
, CFRangeMake(base
, CFStringGetLength(sanitizedString
)-base
));
3073 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
3076 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
3078 if (firstRsrcSpecFlag
) {
3079 Boolean canUseOriginalString
= true;
3080 Boolean canUseSanitizedString
= true;
3081 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
3082 if (!_haveTestedOriginalString(anURL
)) {
3083 computeSanitizedString(anURL
);
3086 UInt32 additionalDataFlags
= _getAdditionalDataFlags(anURL
);
3087 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
3089 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
3090 // See if any pieces in the resource specifier differ between sanitized string and original string
3091 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
3092 if (additionalDataFlags
& flag
) {
3093 canUseOriginalString
= false;
3098 if (!canUseOriginalString
) {
3099 // 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.
3100 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
3101 if (additionalDataFlags
& flag
) {
3102 canUseSanitizedString
= false;
3107 if (canUseOriginalString
) {
3108 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->_ranges
, firstRsrcSpecFlag
);
3109 rg
.location
--; // Include the character that demarcates the component
3110 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
3111 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
3112 } else if (canUseSanitizedString
) {
3113 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->_ranges
, firstRsrcSpecFlag
);
3114 rg
.location
--; // Include the character that demarcates the component
3115 rg
.length
= CFStringGetLength(sanitizedString
) - rg
.location
;
3116 return CFStringCreateWithSubstring(alloc
, sanitizedString
, rg
);
3118 // Must compute the correct string to return; just reparse....
3119 UInt32 sanFlags
= 0;
3120 CFRange
*sanRanges
= NULL
;
3122 _parseComponents(alloc
, sanitizedString
, anURL
->_base
, &sanFlags
, &sanRanges
);
3123 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
3124 CFAllocatorDeallocate(alloc
, sanRanges
);
3125 rg
.location
--; // Include the character that demarcates the component
3126 rg
.length
= CFStringGetLength(sanitizedString
) - rg
.location
;
3127 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), sanitizedString
, rg
);
3130 // The resource specifier cannot possibly come from the base.
3136 /*************************************/
3137 /* Accessors that create new objects */
3138 /*************************************/
3140 // 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).
3141 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
3143 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3144 tmp
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, host
);
3145 if (tmp
) CFRetain(tmp
);
3148 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3149 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
3151 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
3152 // Have to strip off the brackets to get the true hostname.
3153 // Assume that to be legal the first and last characters are brackets!
3154 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
3159 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3160 return CFURLCopyHostName(anURL
->_base
);
3166 // Return -1 to indicate no port is specified
3167 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
3169 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3170 CFNumberRef cfPort
= (CFNumberRef
) CF_OBJC_CALLV((NSURL
*)anURL
, port
);
3172 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
3175 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3176 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
3178 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
3179 CFStringInlineBuffer buf
;
3180 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
3182 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
3187 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3188 return CFURLGetPortNumber(anURL
->_base
);
3194 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
3196 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3197 user
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, user
);
3198 if (user
) CFRetain(user
);
3201 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3202 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
3205 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3206 return CFURLCopyUserName(anURL
->_base
);
3212 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
3214 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3215 passwd
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, password
);
3216 if (passwd
) CFRetain(passwd
);
3219 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3220 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
3223 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
3224 return CFURLCopyPassword(anURL
->_base
);
3230 // 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
3232 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
3234 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3235 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, parameterString
);
3236 if (str
) CFRetain(str
);
3239 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3240 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
3241 if (str
) return str
;
3242 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
3243 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
3245 // Parameter string definitely coming from the relative portion of the URL
3247 return _unescapedParameterString( anURL
->_base
);
3250 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
3251 CFStringRef param
= _unescapedParameterString(anURL
);
3254 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
3255 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
3257 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
3265 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
3267 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3268 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, query
);
3269 if (str
) CFRetain(str
);
3272 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3273 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
3274 if (str
) return str
;
3275 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
3276 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
3279 return _unescapedQueryString(anURL
->_base
);
3282 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
3283 CFStringRef query
= _unescapedQueryString(anURL
);
3286 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
3287 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
3289 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
3297 // Fragments are NEVER taken from a base URL
3298 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
3300 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3301 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, fragment
);
3302 if (str
) CFRetain(str
);
3305 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
3306 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
3310 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
3311 CFStringRef fragment
= _unescapedFragment(anURL
);
3314 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
3315 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
3317 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
3319 CFRelease(fragment
);
3325 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
3326 CFIndex firstMaskFlag
= 1;
3327 CFIndex lastComponentBeforeMask
= 0;
3328 while (firstMaskFlag
<= HAS_FRAGMENT
) {
3329 if (firstMaskFlag
& mask
) break;
3330 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
3331 firstMaskFlag
= firstMaskFlag
<< 1;
3333 if (lastComponentBeforeMask
== 0) {
3334 // mask includes HAS_SCHEME
3336 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
3337 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
3338 // case file:/path/immediately/without/host
3339 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
);
3340 CFRange pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
3341 if (schemeRg
.length
+ 1 == pathRg
.location
) {
3342 return schemeRg
.length
+ 1;
3344 return schemeRg
.length
+ 3;
3347 // For all other components, the separator precedes the component, so there's no need
3348 // to add extra chars to get to the next insertion point
3349 CFRange rg
= _rangeForComponent(url
->_flags
, url
->_ranges
, lastComponentBeforeMask
);
3350 return rg
.location
+ rg
.length
;
3354 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
3355 CFOptionFlags currentOption
;
3356 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
3357 Boolean haveReachedMask
= false;
3358 CFIndex beforeMask
= 0;
3359 CFIndex afterMask
= kCFNotFound
;
3360 CFRange
*currRange
= url
->_ranges
;
3361 CFRange maskRange
= {kCFNotFound
, 0};
3362 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
3363 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
3364 firstMaskFlag
= currentOption
;
3365 haveReachedMask
= true;
3367 if (!(url
->_flags
& currentOption
)) continue;
3368 if (!haveReachedMask
) {
3369 beforeMask
= currRange
->location
+ currRange
->length
;
3370 } else if (currentOption
<= mask
) {
3371 if (maskRange
.location
== kCFNotFound
) {
3372 maskRange
= *currRange
;
3374 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
3377 afterMask
= currRange
->location
;
3382 if (afterMask
== kCFNotFound
) {
3383 afterMask
= maskRange
.location
+ maskRange
.length
;
3385 charRangeWithSeparators
->location
= beforeMask
;
3386 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
3390 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3392 switch (component
) {
3393 case kCFURLComponentScheme
:
3396 case kCFURLComponentNetLocation
:
3397 mask
= NET_LOCATION_MASK
;
3399 case kCFURLComponentPath
:
3402 case kCFURLComponentResourceSpecifier
:
3403 mask
= RESOURCE_SPECIFIER_MASK
;
3405 case kCFURLComponentUser
:
3408 case kCFURLComponentPassword
:
3409 mask
= HAS_PASSWORD
;
3411 case kCFURLComponentUserInfo
:
3412 mask
= HAS_USER
| HAS_PASSWORD
;
3414 case kCFURLComponentHost
:
3417 case kCFURLComponentPort
:
3420 case kCFURLComponentParameterString
:
3421 mask
= HAS_PARAMETERS
;
3423 case kCFURLComponentQuery
:
3426 case kCFURLComponentFragment
:
3427 mask
= HAS_FRAGMENT
;
3430 rangeIncludingSeparators
->location
= kCFNotFound
;
3431 rangeIncludingSeparators
->length
= 0;
3432 return CFRangeMake(kCFNotFound
, 0);
3435 if ((url
->_flags
& mask
) == 0) {
3436 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
3437 rangeIncludingSeparators
->length
= 0;
3438 return CFRangeMake(kCFNotFound
, 0);
3440 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
3444 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3445 if (component
== kCFURLComponentScheme
) {
3446 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
);
3447 rangeIncludingSeparators
->location
= 0;
3448 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
3450 } else if (component
== kCFURLComponentResourceSpecifier
) {
3451 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
);
3452 CFIndex stringLength
= CFStringGetLength(url
->_string
);
3453 if (schemeRg
.length
+ 1 == stringLength
) {
3454 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
3455 rangeIncludingSeparators
->length
= 0;
3456 return CFRangeMake(kCFNotFound
, 0);
3458 rangeIncludingSeparators
->location
= schemeRg
.length
;
3459 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
3460 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
3463 rangeIncludingSeparators
->location
= kCFNotFound
;
3464 rangeIncludingSeparators
->length
= 0;
3465 return CFRangeMake(kCFNotFound
, 0);
3470 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3471 CFRange charRange
, charRangeWithSeparators
;
3473 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
3474 url
= _CFURLFromNSURL(url
);
3476 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
3477 // Special-case this because non-decomposable URLs have a slightly strange flags setup
3478 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
3480 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
3483 if (charRangeWithSeparators
.location
== kCFNotFound
) {
3484 if (rangeIncludingSeparators
) {
3485 rangeIncludingSeparators
->location
= kCFNotFound
;
3486 rangeIncludingSeparators
->length
= 0;
3488 return CFRangeMake(kCFNotFound
, 0);
3489 } else if (rangeIncludingSeparators
) {
3490 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
3492 if (charRange
.location
== kCFNotFound
) {
3493 byteRange
= charRange
;
3494 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
3496 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
3497 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
3499 if (charRangeWithSeparators
.location
== charRange
.location
) {
3500 byteRange
.location
= rangeIncludingSeparators
->location
;
3503 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3504 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
3506 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3507 if (maxCharRangeWithSeparators
== maxCharRange
) {
3508 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
3512 rg
.location
= maxCharRange
;
3513 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
3514 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3515 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
3518 } else if (charRange
.location
== kCFNotFound
) {
3519 byteRange
= charRange
;
3521 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
3522 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3527 /* Component support */
3529 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
3530 if ( CFURLGetBaseURL(url
) != NULL
) {
3531 components
->scheme
= NULL
;
3533 components
->scheme
= CFURLCopyScheme(url
);
3535 components
->schemeSpecific
= CFURLCopyResourceSpecifier(url
);
3539 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
3541 if (components
->scheme
) {
3543 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
3544 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
3545 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
3546 } else if (components
->schemeSpecific
) {
3547 str
= components
->schemeSpecific
;
3553 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3561 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3562 CFAllocatorRef alloc
= CFGetAllocator(url
);
3563 static CFStringRef emptyStr
= NULL
;
3565 emptyStr
= CFSTR("");
3568 if (!CFURLCanBeDecomposed(url
)) {
3572 CFStringRef path
= CFURLCopyPath(url
);
3574 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3577 components
->pathComponents
= NULL
;
3579 components
->baseURL
= CFURLGetBaseURL(url
);
3580 if (components
->baseURL
) {
3581 CFRetain(components
->baseURL
);
3582 components
->scheme
= NULL
;
3584 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
3586 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
3587 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3588 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3589 if (url
->_flags
& HAS_PORT
) {
3590 components
->port
= CFURLGetPortNumber(url
);
3592 components
->port
= kCFNotFound
;
3594 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3595 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3596 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3600 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3601 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3602 CFURLRef base
= comp
->baseURL
;
3604 Boolean hadPrePathComponent
= false;
3607 CFStringAppend(urlString
, comp
->scheme
);
3608 CFStringAppend(urlString
, CFSTR("://"));
3609 hadPrePathComponent
= true;
3611 if (comp
->user
|| comp
->password
) {
3613 CFStringAppend(urlString
, comp
->user
);
3615 if (comp
->password
) {
3616 CFStringAppend(urlString
, CFSTR(":"));
3617 CFStringAppend(urlString
, comp
->password
);
3619 CFStringAppend(urlString
, CFSTR("@"));
3620 hadPrePathComponent
= true;
3623 CFStringAppend(urlString
, comp
->host
);
3624 hadPrePathComponent
= true;
3626 if (comp
->port
!= kCFNotFound
) {
3627 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%ld"), (long)comp
->port
);
3628 hadPrePathComponent
= true;
3631 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFArrayGetCount( comp
->pathComponents
) == 0 || CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3632 CFStringAppend(urlString
, CFSTR("/"));
3634 if (comp
->pathComponents
) {
3635 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3636 CFStringAppend(urlString
, pathStr
);
3639 if (comp
->parameterString
) {
3640 CFStringAppend(urlString
, CFSTR(";"));
3641 CFStringAppend(urlString
, comp
->parameterString
);
3644 CFStringAppend(urlString
, CFSTR("?"));
3645 CFStringAppend(urlString
, comp
->query
);
3647 if (comp
->fragment
) {
3648 CFStringAppend(urlString
, CFSTR("#"));
3649 CFStringAppend(urlString
, comp
->fragment
);
3651 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3652 CFRelease(urlString
);
3656 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3657 CFAllocatorRef alloc
= CFGetAllocator(url
);
3658 CFURLComponentsRFC1808 oldComp
;
3660 if (!decomposeToRFC1808(url
, &oldComp
)) {
3663 comp
->scheme
= oldComp
.scheme
;
3665 if (oldComp
.password
) {
3666 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3667 CFRelease(oldComp
.password
);
3668 CFRelease(oldComp
.user
);
3670 comp
->userinfo
= oldComp
.user
;
3673 comp
->userinfo
= NULL
;
3675 comp
->host
= oldComp
.host
;
3676 comp
->port
= oldComp
.port
;
3677 if (!oldComp
.parameterString
) {
3678 comp
->pathComponents
= oldComp
.pathComponents
;
3680 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3681 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3682 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3683 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3685 CFRelease(oldComp
.pathComponents
);
3686 CFRelease(oldComp
.parameterString
);
3688 comp
->query
= oldComp
.query
;
3689 comp
->fragment
= oldComp
.fragment
;
3690 comp
->baseURL
= oldComp
.baseURL
;
3694 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3695 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3696 CFURLRef base
= comp
->baseURL
;
3698 Boolean hadPrePathComponent
= false;
3701 CFStringAppend(urlString
, comp
->scheme
);
3702 CFStringAppend(urlString
, CFSTR("://"));
3703 hadPrePathComponent
= true;
3705 if (comp
->userinfo
) {
3706 CFStringAppend(urlString
, comp
->userinfo
);
3707 CFStringAppend(urlString
, CFSTR("@"));
3708 hadPrePathComponent
= true;
3711 CFStringAppend(urlString
, comp
->host
);
3712 if (comp
->port
!= kCFNotFound
) {
3713 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%ld"), (long)comp
->port
);
3715 hadPrePathComponent
= true;
3717 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3718 CFStringAppend(urlString
, CFSTR("/"));
3720 if (comp
->pathComponents
) {
3721 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3722 CFStringAppend(urlString
, pathStr
);
3726 CFStringAppend(urlString
, CFSTR("?"));
3727 CFStringAppend(urlString
, comp
->query
);
3729 if (comp
->fragment
) {
3730 CFStringAppend(urlString
, CFSTR("#"));
3731 CFStringAppend(urlString
, comp
->fragment
);
3733 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3734 CFRelease(urlString
);
3738 #undef CFURLCopyComponents
3739 #undef CFURLCreateFromComponents
3742 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3743 url
= _CFURLFromNSURL(url
);
3744 switch (decompositionType
) {
3745 case kCFURLComponentDecompositionNonHierarchical
:
3746 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3747 case kCFURLComponentDecompositionRFC1808
:
3748 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3749 case kCFURLComponentDecompositionRFC2396
:
3750 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3757 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3758 switch (decompositionType
) {
3759 case kCFURLComponentDecompositionNonHierarchical
:
3760 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3761 case kCFURLComponentDecompositionRFC1808
:
3762 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3763 case kCFURLComponentDecompositionRFC2396
:
3764 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3770 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3771 return _getReserved(url
);
3774 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3775 _setReserved ( (struct __CFURL
*) url
, ptr
);
3778 CF_EXPORT
void *__CFURLResourceInfoPtr(CFURLRef url
) {
3779 return _getResourceInfo(url
);
3782 CF_EXPORT
void __CFURLSetResourceInfoPtr(CFURLRef url
, void *ptr
) {
3783 _setResourceInfo ( (struct __CFURL
*) url
, ptr
);
3786 /* File system stuff */
3788 /* HFSPath<->URLPath functions at the bottom of the file */
3789 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
) {
3791 CFMutableArrayRef urlComponents
= NULL
;
3794 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3795 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3798 CFStringRef str
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, 0);
3799 if (isAbsolute
&& CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3800 i
= 1; // Skip over the drive letter
3803 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3804 CFStringRef fileComp
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
,i
);
3805 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3807 // Couldn't decode fileComp
3808 CFRelease(urlComponents
);
3811 if (urlComp
!= fileComp
) {
3812 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3818 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3819 CFArrayAppendValue(urlComponents
, CFSTR(""));
3822 if ( AddAuthorityToFileURL() ) {
3823 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY
));
3826 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR(FILE_PREFIX
));
3829 return urlComponents
;
3832 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
) {
3833 CFArrayRef urlComponents
;
3836 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3837 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
, isAbsolute
);
3838 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3840 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3841 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3842 CFRelease(urlComponents
);
3846 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
, Boolean isAbsolute
, Boolean
*posixAndUrlPathsMatch
) {
3847 Boolean addedPercentEncoding
;
3848 CFStringRef pathString
= NULL
;
3849 STACK_BUFFER_DECL(char, buffer
, PATH_MAX
);
3850 if ( CFStringGetFileSystemRepresentation(path
, buffer
, PATH_MAX
) ) {
3851 pathString
= CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault
, (const UInt8
*)buffer
, strlen(buffer
), isDirectory
, isAbsolute
, false /* windowsPath */, &addedPercentEncoding
);
3854 if ( posixAndUrlPathsMatch
) {
3855 *posixAndUrlPathsMatch
= !addedPercentEncoding
;
3860 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3861 // This is the easiest case; just remove the percent escape codes and we're done
3862 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3864 CFIndex length
= CFStringGetLength(result
);
3865 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3866 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3874 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
3875 static Boolean
CanonicalFileURLStringToFileSystemRepresentation(CFStringRef str
, UInt8
*inBuffer
, CFIndex inBufferLen
)
3877 size_t fileURLPrefixLength
;
3878 if ( AddAuthorityToFileURL() ) {
3879 fileURLPrefixLength
= sizeof(fileURLPrefixWithAuthority
);
3882 fileURLPrefixLength
= sizeof(fileURLPrefix
);
3885 if ( inBuffer
&& inBufferLen
) {
3886 STACK_BUFFER_DECL(UInt8
, stackEscapedBuf
, PATH_MAX
* 3); // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3888 CFIndex strLength
= CFStringGetLength(str
) - (fileURLPrefixLength
- 1);
3889 if ( strLength
!= 0 ) {
3890 CFIndex maxBufLength
= strLength
* 3;
3892 CFIndex charsConverted
;
3893 if ( strLength
<= PATH_MAX
) {
3894 escapedBuf
= &stackEscapedBuf
[0];
3897 // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3898 escapedBuf
= (UInt8
*)malloc(maxBufLength
);
3900 if ( escapedBuf
!= NULL
) {
3901 charsConverted
= CFStringGetBytes(str
, CFRangeMake(fileURLPrefixLength
- 1, strLength
), kCFStringEncodingUTF8
, 0, false, escapedBuf
, maxBufLength
, &usedBufLen
);
3902 if ( charsConverted
) {
3903 static const UInt8 hexvalues
[] = {
3904 /* 00 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3905 /* 08 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3906 /* 10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3907 /* 18 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3908 /* 20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3909 /* 28 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3910 /* 30 */ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
3911 /* 38 */ 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3912 /* 40 */ 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3913 /* 48 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3914 /* 50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3915 /* 58 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3916 /* 60 */ 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3917 /* 68 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3918 /* 70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3919 /* 78 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3921 /* 80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3922 /* 88 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3923 /* 90 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3924 /* 98 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3925 /* A0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3926 /* A8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3927 /* B0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3928 /* B8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3929 /* C0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3930 /* C8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3931 /* D0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3932 /* D8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3933 /* E0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3934 /* E8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3935 /* F0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3936 /* F8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3941 const UInt8
*bytePtr
= escapedBuf
;
3943 Boolean trailingSlash
= false;
3945 bufPtr
= bufStartPtr
= inBuffer
;
3946 bufEndPtr
= inBuffer
+ inBufferLen
;
3949 for ( idx
= 0; (idx
< usedBufLen
) && result
; ++idx
) {
3950 if ( bufPtr
== bufEndPtr
) {
3951 // ooops, ran out of inBuffer
3952 *bufStartPtr
= '\0';
3956 switch ( *bytePtr
) {
3959 if ( idx
< usedBufLen
) {
3962 // convert hex digits
3963 *bufPtr
= hexvalues
[*bytePtr
++] << 4;
3964 *bufPtr
+= hexvalues
[*bytePtr
++];
3965 trailingSlash
= (*bufPtr
== '/');
3969 // copy everything else
3970 *bufPtr
= *bytePtr
++;
3971 trailingSlash
= (*bufPtr
== '/');
3978 // remove trailing slash (if any)
3979 if ( (bufPtr
> (bufStartPtr
+ 1)) && trailingSlash
) {
3982 if ( bufPtr
< bufEndPtr
) {
3988 // CFStringGetBytes failed
3992 // free the buffer if we malloc'd it
3993 if ( escapedBuf
!= &stackEscapedBuf
[0] ) {
3998 // could not allocate escapedBuf
4003 // str was zero characters
4009 // no inBuffer or inBufferLen is zero
4017 #if DEPLOYMENT_TARGET_WINDOWS
4018 // From CFPlatform.c
4019 extern CFStringRef
CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr
);
4022 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
4023 // Check for a drive letter, then flip all the slashes
4025 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
4026 SInt32 count
= CFArrayGetCount(tmp
);
4027 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
4028 CFStringRef newPath
;
4033 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
4034 CFArrayRemoveValueAtIndex(components
, count
-1);
4038 if (count
> 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
, 0)) == 0) {
4039 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
4040 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, (CFStringRef
)CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
4044 if (firstComponent
) {
4045 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
4047 CFArrayRemoveValueAtIndex(components
, 0);
4049 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
4050 CFArraySetValueAtIndex(components
, 0, driveStr
);
4051 CFRelease(driveStr
);
4054 #if DEPLOYMENT_TARGET_WINDOWS
4056 // From <rdar://problem/5623405> [DEFECT] CFURL returns a Windows path that contains volume name instead of a drive letter
4057 // we need to replace the volume name (it is not valid on Windows) with the drive mounting point path
4058 // remove the first component and set the component with the drive letter to be the first component
4059 CFStringRef driveRootPath
= CFCreateWindowsDrivePathFromVolumeName(firstComponent
);
4061 if (driveRootPath
) {
4062 // remove trailing slash
4063 if (CFStringHasSuffix(driveRootPath
, CFSTR("\\"))) {
4064 CFStringRef newDriveRootPath
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, driveRootPath
, CFRangeMake(0, CFStringGetLength(driveRootPath
) - 1));
4065 CFRelease(driveRootPath
);
4066 driveRootPath
= newDriveRootPath
;
4069 // replace the first component of the path with the drive path
4070 CFArrayRemoveValueAtIndex(components
, 0);
4071 CFArraySetValueAtIndex(components
, 0, driveRootPath
);
4073 CFRelease(driveRootPath
);
4079 if ( firstComponent
) {
4080 CFRelease(firstComponent
);
4083 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
4084 CFRelease(components
);
4085 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
4092 // Caller must release the returned string
4093 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
4094 CFIndex baseLen
= CFStringGetLength(basePath
);
4095 CFIndex relLen
= CFStringGetLength(relativePath
);
4096 UniChar pathDelimiter
= '/';
4097 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
4098 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
4100 if (buf
[baseLen
-1] != pathDelimiter
) {
4101 buf
[baseLen
] = pathDelimiter
;
4105 UniChar
*ptr
= buf
+ baseLen
- 1;
4106 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
4109 baseLen
= ptr
- buf
+ 1;
4111 #pragma GCC diagnostic push
4112 #pragma GCC diagnostic ignored "-Wdeprecated"
4113 if (fsType
== kCFURLHFSPathStyle
) {
4114 #pragma GCC diagnostic pop
4115 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
4118 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
4119 *(buf
+ baseLen
+ relLen
) = '\0';
4120 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
4123 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
4124 CFURLRef url
= NULL
;
4125 uint8_t buf
[CFMaxPathSize
+ 1];
4126 if (_CFGetCurrentDirectory((char *)buf
, CFMaxPathLength
)) {
4127 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen((char *)buf
), true);
4132 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
4135 result
= _CFURLAlloc(allocator
);
4137 if ( !_CFURLInitWithFileSystemPath(result
, filePath
, fsType
, isDirectory
, NULL
) ) {
4146 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
4149 result
= _CFURLAlloc(allocator
);
4151 if ( !_CFURLInitWithFileSystemPath(result
, filePath
, fsType
, isDirectory
, baseURL
) ) {
4160 static Boolean
_pathHasFileIDPrefix( CFStringRef path
)
4162 // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
4163 #ifdef __CONSTANT_STRINGS__
4166 CFStringRef fileIDPrefix
= CFSTR( "/" FILE_ID_PREFIX
"/" );
4167 return path
&& CFStringHasPrefix( path
, fileIDPrefix
) && CFStringGetLength( path
) > CFStringGetLength( fileIDPrefix
);
4170 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4171 static Boolean
_pathHasFileIDOnly( CFStringRef path
)
4173 // Is file ID rooted and contains no additonal path segments
4175 return _pathHasFileIDPrefix( path
) && ( !CFStringFindWithOptions( path
, CFSTR("/"), CFRangeMake( sizeof(FILE_ID_PREFIX
) + 1, CFStringGetLength( path
) - sizeof(FILE_ID_PREFIX
) - 1), 0, &slashRange
) || slashRange
.location
== CFStringGetLength( path
) - 1 );
4179 CF_EXPORT CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
4180 #pragma GCC diagnostic push
4181 #pragma GCC diagnostic ignored "-Wdeprecated"
4182 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
4183 #pragma GCC diagnostic pop
4185 CFStringRef result
= NULL
;
4186 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
4187 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
4188 Boolean isCanonicalFileURL
= false;
4190 if ( (pathStyle
== kCFURLPOSIXPathStyle
) && (CFURLGetBaseURL(anURL
) == NULL
) ) {
4191 if ( !CF_IS_OBJC(__kCFURLTypeID
, anURL
) ) {
4192 // We can grope the ivars
4193 isCanonicalFileURL
= ((anURL
->_flags
& IS_CANONICAL_FILE_URL
) != 0);
4194 if ( isCanonicalFileURL
) {
4195 STACK_BUFFER_DECL(UInt8
, buffer
, PATH_MAX
+ 1);
4196 if ( CanonicalFileURLStringToFileSystemRepresentation(anURL
->_string
, buffer
, PATH_MAX
+ 1) ) {
4197 result
= CFStringCreateWithBytes(alloc
, buffer
, strlen((char *)buffer
), kCFStringEncodingUTF8
, false);
4203 // fall back to slower way.
4204 result
= CFURLCreateStringWithFileSystemPath(alloc
, anURL
, pathStyle
, false);
4206 #else // !DEPLOYMENT_TARGET_MACOSX
4207 result
= CFURLCreateStringWithFileSystemPath(alloc
, anURL
, pathStyle
, false);
4208 #endif // !DEPLOYMENT_TARGET_MACOSX
4214 // 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
4215 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
4216 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
4217 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
4218 CFStringRef relPath
= NULL
;
4220 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
4221 // We can grope the ivars
4222 if (fsType
== kCFURLPOSIXPathStyle
) {
4223 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
4224 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
4229 if (relPath
== NULL
) {
4230 CFStringRef urlPath
= CFURLCopyPath(anURL
);
4231 CFStringEncoding enc
= anURL
->_encoding
;
4234 case kCFURLPOSIXPathStyle
:
4235 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
4237 #pragma GCC diagnostic push
4238 #pragma GCC diagnostic ignored "-Wdeprecated"
4239 case kCFURLHFSPathStyle
:
4240 #pragma GCC diagnostic pop
4243 case kCFURLWindowsPathStyle
:
4244 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
4247 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
4253 // For Tiger, leave this behavior in for all path types. For Leopard, it would be nice to remove this entirely
4254 // and do a linked-on-or-later check so we don't break third parties.
4255 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
4256 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
4257 if ( relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == '/') {
4258 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
4264 if ( basePath
&& (CFStringGetCharacterAtIndex(relPath
, 0) != '/') ) {
4265 // we have both basePath and relPath, and relPath is not absolute -- resolve them
4266 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
4267 CFRelease(basePath
);
4272 // we only have the relPath or relpath is absolute -- return it
4274 CFRelease(basePath
);
4279 else if ( basePath
) {
4280 // we only have the basePath --- return it
4284 // we have nothing to return
4289 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
4290 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_WINDOWS
4291 CFAllocatorRef alloc
= CFGetAllocator(url
);
4294 if (!url
) return false;
4296 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
4297 if ( !resolveAgainstBase
|| (CFURLGetBaseURL(url
) == NULL
) ) {
4298 if (!CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4299 // We can grope the ivars
4300 if ( url
->_flags
& IS_CANONICAL_FILE_URL
) {
4301 return CanonicalFileURLStringToFileSystemRepresentation(url
->_string
, buffer
, bufLen
);
4305 // else fall back to slower way.
4306 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
4308 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
4312 #elif DEPLOYMENT_TARGET_WINDOWS
4313 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
4316 CFIndex pathLen
= CFStringGetLength(path
);
4317 CFIndex numConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLen
), CFStringFileSystemEncoding(), 0, true, buffer
, bufLen
-1, &usedLen
); // -1 because we need one byte to zero-terminate.
4319 if (numConverted
== pathLen
) {
4320 buffer
[usedLen
] = '\0';
4328 #if DEPLOYMENT_TARGET_WINDOWS
4329 CF_EXPORT Boolean
_CFURLGetWideFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, wchar_t *buffer
, CFIndex bufferLength
) {
4330 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
4331 CFIndex pathLength
, charsConverted
, usedBufLen
;
4332 if (!path
) return false;
4333 pathLength
= CFStringGetLength(path
);
4334 if (pathLength
+1 > bufferLength
) {
4338 charsConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLength
), kCFStringEncodingUTF16
, 0, false, (UInt8
*)buffer
, bufferLength
*sizeof(wchar_t), &usedBufLen
);
4339 // CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer);
4341 if (charsConverted
!= pathLength
|| usedBufLen%sizeof
(wchar_t) != 0) {
4344 buffer
[usedBufLen
/sizeof(wchar_t)] = 0;
4345 // buffer[pathLength] = 0;
4351 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
4354 result
= _CFURLAlloc(allocator
);
4356 if ( !_CFURLInitWithFileSystemRepresentation(result
, buffer
, bufLen
, isDirectory
, NULL
) ) {
4365 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
4368 result
= _CFURLAlloc(allocator
);
4370 if ( !_CFURLInitWithFileSystemRepresentation(result
, buffer
, bufLen
, isDirectory
, baseURL
) ) {
4380 /******************************/
4381 /* Support for path utilities */
4382 /******************************/
4384 // Assumes url is a CFURL (not an Obj-C NSURL)
4385 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
4386 CFRange pathRg
, componentRg
;
4388 pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
4390 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
4394 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == '/') {
4396 if (pathRg
.length
== 0) {
4401 if (CFStringFindWithOptions(url
->_string
, CFSTR("/"), pathRg
, kCFCompareBackwards
, &componentRg
)) {
4402 componentRg
.location
++;
4403 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
4405 componentRg
= pathRg
;
4410 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
4413 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4414 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLPOSIXPathStyle
, false);
4417 if (!path
) return NULL
;
4418 rg
= CFRangeMake(0, CFStringGetLength(path
));
4419 if ( rg
.length
== 0 ) return path
;
4420 length
= rg
.length
; // Remember this for comparison later
4421 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/' ) {
4424 if ( rg
.length
== 0 )
4426 // If we have reduced the string to empty, then it's "/", and that's what we return as
4427 // the last path component.
4430 else if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
4431 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
4432 rg
.location
= compRg
.location
+ 1;
4434 if (rg
.location
== 0 && rg
.length
== length
) {
4437 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), path
, rg
);
4441 Boolean filePathURLCreated
= false;
4442 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4443 if ( CFURLIsFileReferenceURL(url
) ) {
4444 // use a file path URL or fail
4445 CFURLRef filePathURL
= CFURLCreateFilePathURL(CFGetAllocator(url
), url
, NULL
);
4446 if ( filePathURL
) {
4447 filePathURLCreated
= TRUE
;
4456 CFRange rg
= _rangeOfLastPathComponent(url
);
4457 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
4459 if ( filePathURLCreated
) {
4462 return (CFStringRef
)CFRetain(CFSTR(""));
4464 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == '/') {
4465 if ( filePathURLCreated
) {
4468 return (CFStringRef
)CFRetain(CFSTR("/"));
4470 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
4471 if (!(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
4473 if (url
->_encoding
== kCFStringEncodingUTF8
) {
4474 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
4476 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
4481 if ( filePathURLCreated
) {
4488 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
4489 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
4490 CFStringRef ext
= NULL
;
4493 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
4494 if (rg
.location
!= kCFNotFound
) {
4496 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
4497 if (rg
.length
> 0) {
4498 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
4500 ext
= (CFStringRef
)CFRetain(CFSTR(""));
4503 CFRelease(lastPathComp
);
4508 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
4510 url
= _CFURLFromNSURL(url
);
4511 __CFGenericValidateType(url
, __kCFURLTypeID
);
4512 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
4514 Boolean filePathURLCreated
= false;
4515 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4516 if ( CFURLIsFileReferenceURL(url
) ) {
4517 // use a file path URL if possible (only because this is appending a path component)
4518 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4519 if ( filePathURL
) {
4520 filePathURLCreated
= TRUE
;
4526 CFMutableStringRef newString
;
4527 CFStringRef newComp
;
4529 if (!(url
->_flags
& HAS_PATH
)) {
4533 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4534 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), url
->_encoding
);
4535 pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
4536 if ( (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') && (CFStringGetCharacterAtIndex(newComp
, 0) != '/') ) {
4537 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4540 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
4542 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
4545 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4546 CFRelease(newString
);
4548 if ( filePathURLCreated
) {
4554 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
4556 CFMutableStringRef newString
;
4557 CFRange lastCompRg
, pathRg
;
4558 Boolean appendDotDot
= false;
4560 url
= _CFURLFromNSURL(url
);
4561 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4562 __CFGenericValidateType(url
, __kCFURLTypeID
);
4564 Boolean filePathURLCreated
= false;
4565 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4566 if ( CFURLIsFileReferenceURL(url
) ) {
4567 // use a file path URL or fail
4568 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4569 if ( filePathURL
) {
4570 filePathURLCreated
= TRUE
;
4579 if (!(url
->_flags
& HAS_PATH
)) {
4580 if ( filePathURLCreated
) {
4585 pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
4586 lastCompRg
= _rangeOfLastPathComponent(url
);
4587 if (lastCompRg
.length
== 0) {
4588 appendDotDot
= true;
4589 } else if (lastCompRg
.length
== 1) {
4590 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
4591 if (ch
== '.' || ch
== '/') {
4592 appendDotDot
= true;
4594 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
4595 appendDotDot
= true;
4598 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4601 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
4602 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4605 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
4607 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR("/"));
4609 // 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 "/.".
4610 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
4611 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == '/') {
4612 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
4613 } else if (pathRg
.length
+delta
== 5) {
4614 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
4617 } else if (lastCompRg
.location
== pathRg
.location
) {
4618 CFStringReplace(newString
, pathRg
, CFSTR("."));
4619 CFStringInsert(newString
, 1, CFSTR("/"));
4621 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
4623 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4624 CFRelease(newString
);
4625 if ( filePathURLCreated
) {
4631 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
4632 CFMutableStringRef newString
;
4636 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4637 url
= _CFURLFromNSURL(url
);
4638 __CFGenericValidateType(url
, __kCFURLTypeID
);
4639 __CFGenericValidateType(extension
, CFStringGetTypeID());
4641 Boolean filePathURLCreated
= false;
4642 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4643 if ( CFURLIsFileReferenceURL(url
) ) {
4644 // use a file path URL or fail
4645 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4646 if ( filePathURL
) {
4647 filePathURLCreated
= TRUE
;
4656 rg
= _rangeOfLastPathComponent(url
);
4657 if (rg
.location
< 0) {
4658 if ( filePathURLCreated
) {
4661 return NULL
; // No path
4664 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4665 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
4666 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), url
->_encoding
);
4667 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
4669 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4670 CFRelease(newString
);
4671 if ( filePathURLCreated
) {
4677 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4681 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4682 url
= _CFURLFromNSURL(url
);
4683 __CFGenericValidateType(url
, __kCFURLTypeID
);
4685 Boolean filePathURLCreated
= false;
4686 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4687 if ( CFURLIsFileReferenceURL(url
) ) {
4688 // use a file path URL or fail
4689 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4690 if ( filePathURL
) {
4691 filePathURLCreated
= TRUE
;
4700 rg
= _rangeOfLastPathComponent(url
);
4701 if (rg
.location
< 0) {
4703 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
4704 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4705 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
4706 CFStringDelete(newString
, dotRg
);
4707 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4708 CFRelease(newString
);
4710 result
= (CFURLRef
)CFRetain(url
);
4712 if ( filePathURLCreated
) {
4719 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4720 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4721 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4722 CFMutableArrayRef newComponents
= CFArrayCreateMutableCopy(alloc
, 0, components
);
4723 Boolean doSpecialLeadingColon
= false;
4724 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
4726 CFRelease(components
);
4729 if (!doSpecialLeadingColon
&& firstChar
!= ':') {
4730 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4731 } else if (firstChar
!= ':') {
4732 // see what we need to add at the beginning. Under MacOS, if the
4733 // first character isn't a ':', then the first component is the
4734 // volume name, and we need to find the mount point. Bleah. If we
4735 // don't find a mount point, we're going to have to lie, and make something up.
4736 CFStringRef firstComp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0);
4737 if (CFStringGetLength(firstComp
) == 1 && CFStringGetCharacterAtIndex(firstComp
, 0) == '/') {
4738 // "/" is the "magic" path for a UFS root directory
4739 CFArrayRemoveValueAtIndex(newComponents
, 0);
4740 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4742 // See if we can get a mount point.
4743 Boolean foundMountPoint
= false;
4744 if (!foundMountPoint
) {
4745 // Fall back to treating the volume name as the top level directory
4746 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4750 CFArrayRemoveValueAtIndex(newComponents
, 0);
4753 cnt
= CFArrayGetCount(newComponents
);
4754 for (i
= 0; i
< cnt
; i
++) {
4755 CFStringRef comp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, i
);
4756 CFStringRef newComp
= NULL
;
4757 CFRange searchRg
, slashRg
;
4758 searchRg
.location
= 0;
4759 searchRg
.length
= CFStringGetLength(comp
);
4760 while (CFStringFindWithOptions(comp
, CFSTR("/"), searchRg
, 0, &slashRg
)) {
4762 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4764 CFStringReplace((CFMutableStringRef
)newComp
, slashRg
, CFSTR(":"));
4765 searchRg
.length
= searchRg
.location
+ searchRg
.length
- slashRg
.location
- 1;
4766 searchRg
.location
= slashRg
.location
+ 1;
4769 CFArraySetValueAtIndex(newComponents
, i
, newComp
);
4773 if (isDir
&& CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, cnt
-1)) != 0) {
4774 CFArrayAppendValue(newComponents
, CFSTR(""));
4776 return newComponents
;
4779 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
4780 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
4781 CFAllocatorRef alloc
= CFGetAllocator(array
);
4782 CFMutableArrayRef mArray
= NULL
;
4783 CFIndex i
, c
= CFArrayGetCount(array
);
4784 for (i
= 0; i
< c
; i
++) {
4785 CFStringRef origComp
= (CFStringRef
)CFArrayGetValueAtIndex(array
, i
);
4786 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
4787 if (!unescapedComp
) {
4790 if (unescapedComp
!= origComp
) {
4792 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
4794 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
4796 CFRelease(unescapedComp
);
4799 if (mArray
) CFRelease(mArray
);
4801 } else if (mArray
) {
4809 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
4810 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
4813 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
, Boolean isAbsolute
) {
4814 CFArrayRef components
= HFSPathToURLComponents(path
, alloc
, isDir
);
4815 CFArrayRef newComponents
= components
? copyStringArrayWithTransformation(components
, escapePathComponent
) : NULL
;
4818 if (components
) CFRelease(components
);
4819 if (!newComponents
) return NULL
;
4821 cnt
= CFArrayGetCount(newComponents
);
4822 if (cnt
== 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0)) == 0) {
4823 result
= (CFStringRef
)CFRetain(CFSTR("/"));
4825 result
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
4827 if ( isAbsolute
&& result
) {
4828 CFStringRef temp
= result
;
4829 if ( AddAuthorityToFileURL() ) {
4830 result
= CFStringCreateWithFormat(alloc
, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY
"%@"), temp
);
4833 result
= CFStringCreateWithFormat(alloc
, 0, CFSTR(FILE_PREFIX
"%@"), temp
);
4837 CFRelease(newComponents
);
4844 // keys and vals must have space for at least 4 key/value pairs. No argument can be NULL.
4845 // Caller must release values, but not keys
4846 static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url
, CFTypeRef
*keys
, CFTypeRef
*vals
, CFIndex
*count
) {
4847 CFAllocatorRef alloc
= CFGetAllocator(url
);
4848 CFURLRef base
= CFURLGetBaseURL(url
);
4849 keys
[0] = CFSTR("_CFURLStringType");
4850 keys
[1] = CFSTR("_CFURLString");
4851 keys
[2] = CFSTR("_CFURLBaseStringType");
4852 keys
[3] = CFSTR("_CFURLBaseURLString");
4853 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4854 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4855 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4856 vals
[1] = CFURLGetString(url
);
4858 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4859 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4860 if (url
->_flags
& IS_DIRECTORY
) {
4861 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) == '/') {
4862 vals
[1] = CFRetain(url
->_string
);
4864 vals
[1] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), url
->_string
, '/');
4867 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != '/') {
4868 vals
[1] = CFRetain(url
->_string
);
4870 vals
[1] = CFStringCreateWithSubstring(alloc
, url
->_string
, CFRangeMake(0, CFStringGetLength(url
->_string
) - 1));
4875 if (CF_IS_OBJC(__kCFURLTypeID
, base
)) {
4876 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4877 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4878 vals
[3] = CFURLGetString(base
);
4880 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4881 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4882 if (base
->_flags
& IS_DIRECTORY
) {
4883 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) == '/') {
4884 vals
[3] = CFRetain(base
->_string
);
4886 vals
[3] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), base
->_string
, '/');
4889 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) != '/') {
4890 vals
[3] = CFRetain(base
->_string
);
4892 vals
[3] = CFStringCreateWithSubstring(alloc
, base
->_string
, CFRangeMake(0, CFStringGetLength(base
->_string
) - 1));
4902 // Private API for Finder to use
4903 CFPropertyListRef
_CFURLCopyPropertyListRepresentation(CFURLRef url
) {
4904 CFTypeRef keys
[4], vals
[4];
4905 CFDictionaryRef dict
;
4907 __CFURLCopyPropertyListKeysAndValues(url
, keys
, vals
, &count
);
4908 dict
= CFDictionaryCreate(CFGetAllocator(url
), keys
, vals
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4909 for (idx
= 0; idx
< count
; idx
++) {
4910 CFRelease(vals
[idx
]);
4915 CFURLRef
_CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc
, CFPropertyListRef pListRepresentation
) {
4916 CFStringRef baseString
, string
;
4917 CFNumberRef baseTypeNum
, urlTypeNum
;
4918 SInt32 baseType
, urlType
;
4919 CFURLRef baseURL
= NULL
, url
;
4920 CFDictionaryRef dict
= (CFDictionaryRef
)pListRepresentation
;
4922 // Start by getting all the pieces and verifying they're of the correct type.
4923 if (CFGetTypeID(pListRepresentation
) != CFDictionaryGetTypeID()) {
4926 string
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLString"));
4927 if (!string
|| CFGetTypeID(string
) != CFStringGetTypeID()) {
4930 urlTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLStringType"));
4931 #pragma GCC diagnostic push
4932 #pragma GCC diagnostic ignored "-Wdeprecated"
4933 if (!urlTypeNum
|| CFGetTypeID(urlTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum
, kCFNumberSInt32Type
, &urlType
) || (urlType
!= FULL_URL_REPRESENTATION
&& urlType
!= kCFURLPOSIXPathStyle
&& urlType
!= kCFURLHFSPathStyle
&& urlType
!= kCFURLWindowsPathStyle
)) {
4934 #pragma GCC diagnostic pop
4937 baseString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseURLString"));
4939 if (CFGetTypeID(baseString
) != CFStringGetTypeID()) {
4942 baseTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseStringType"));
4943 #pragma GCC diagnostic push
4944 #pragma GCC diagnostic ignored "-Wdeprecated"
4945 if (!baseTypeNum
|| CFGetTypeID(baseTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum
, kCFNumberSInt32Type
, &baseType
) ||
4946 (baseType
!= FULL_URL_REPRESENTATION
&& baseType
!= kCFURLPOSIXPathStyle
&& baseType
!= kCFURLHFSPathStyle
&& baseType
!= kCFURLWindowsPathStyle
)) {
4947 #pragma GCC diagnostic pop
4950 if (baseType
== FULL_URL_REPRESENTATION
) {
4951 baseURL
= _CFURLCreateWithArbitraryString(alloc
, baseString
, NULL
);
4953 baseURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, baseString
, (CFURLPathStyle
)baseType
, CFStringGetCharacterAtIndex(baseString
, CFStringGetLength(baseString
)-1) == '/', NULL
);
4956 if (urlType
== FULL_URL_REPRESENTATION
) {
4957 url
= _CFURLCreateWithArbitraryString(alloc
, string
, baseURL
);
4959 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, string
, (CFURLPathStyle
)urlType
, CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
)-1) == '/', baseURL
);
4961 if (baseURL
) CFRelease(baseURL
);
4965 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4966 Boolean
_CFURLIsFileReferenceURL(CFURLRef url
)
4968 return ( CFURLIsFileReferenceURL(url
) );
4971 Boolean
CFURLIsFileReferenceURL(CFURLRef url
)
4973 // returns TRUE if url is is a file URL whose path starts with a file ID reference
4974 Boolean result
= false;
4975 CFURLRef baseURL
= CFURLGetBaseURL(url
);
4977 result
= CFURLIsFileReferenceURL(baseURL
);
4980 if ( CF_IS_OBJC(__kCFURLTypeID
, url
) ) {
4981 result
= (Boolean
) CF_OBJC_CALLV((NSURL
*)url
, isFileReferenceURL
);
4984 result
= ((_getSchemeTypeFromFlags(url
->_flags
) == kHasFileScheme
) && ((url
->_flags
& PATH_HAS_FILE_ID
) != 0));
4990 static Boolean
_CFURLHasFileURLScheme(CFURLRef url
, Boolean
*hasScheme
)
4993 CFURLRef baseURL
= CFURLGetBaseURL(url
);
4996 result
= _CFURLHasFileURLScheme(baseURL
, hasScheme
);
4999 if ( CF_IS_OBJC(__kCFURLTypeID
, url
) ) {
5000 CFStringRef scheme
= CFURLCopyScheme(url
);
5002 if ( scheme
== kCFURLFileScheme
) {
5006 result
= CFStringCompare(scheme
, kCFURLFileScheme
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
;
5022 *hasScheme
= (url
->_flags
& HAS_SCHEME
) != 0;
5024 result
= (_getSchemeTypeFromFlags(url
->_flags
) == kHasFileScheme
);
5030 Boolean
_CFURLIsFileURL(CFURLRef url
)
5032 Boolean result
= _CFURLHasFileURLScheme(url
, NULL
);
5036 CFURLRef
CFURLCreateFilePathURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
)
5038 CFURLRef result
= NULL
;
5040 if (!_CFURLHasFileURLScheme(url
, &hasScheme
)) {
5042 CFLog(kCFLogLevelWarning
, CFSTR("CFURLCreateFilePathURL failed because it was passed this URL which has no scheme: %@"), url
);
5045 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLReadUnsupportedSchemeError
, NULL
);
5049 // File URL. Form of the path is unknown. Make a new URL.
5050 CFStringRef newURLString
;
5055 if ( CFURLGetBaseURL( url
)) {
5056 CFURLRef absURL
= CFURLCopyAbsoluteURL( url
);
5057 fsPath
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(absURL
), absURL
, kCFURLPOSIXPathStyle
, false);
5058 netLoc
= CFURLCopyNetLocation( absURL
);
5059 rSpec
= CFURLCopyResourceSpecifier( absURL
);
5060 CFRelease( absURL
);
5062 fsPath
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLPOSIXPathStyle
, false);
5063 netLoc
= CFURLCopyNetLocation( url
);
5064 rSpec
= CFURLCopyResourceSpecifier( url
);
5067 CFStringRef urlPath
= _replacePathIllegalCharacters( fsPath
, alloc
, true );
5068 newURLString
= CFStringCreateWithFormat( alloc
, NULL
, CFSTR(FILE_PREFIX
"%@%@%@%@"), (netLoc
? netLoc
: CFSTR("")), urlPath
, ((CFStringCompare(urlPath
, CFSTR("/"), 0) != kCFCompareEqualTo
) ? (CFURLHasDirectoryPath( url
) ? CFSTR("/") : CFSTR("")) : CFSTR("")), (rSpec
? rSpec
: CFSTR("")));
5069 result
= CFURLCreateWithString( alloc
, newURLString
, NULL
);
5070 CFRelease( newURLString
);
5071 CFRelease( urlPath
);
5072 CFRelease( fsPath
);
5075 // Would be better here to get an underlying error back from CFURLCreateStringWithFileSystemPath
5076 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLNoSuchResourceError
, NULL
);
5081 CFRelease( netLoc
);
5093 CFURLRef
CFURLCreateFileReferenceURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
) { return NULL
; }