2 * Copyright (c) 2012 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-2012, 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 <CoreFoundation/CFURLPriv.h>
50 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
51 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
52 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
54 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
55 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
56 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
);
57 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
58 CF_EXPORT CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
59 #if DEPLOYMENT_TARGET_MACOSX
60 static Boolean
_CFURLHasFileURLScheme(CFURLRef url
, Boolean
*hasScheme
);
66 #ifndef DEBUG_URL_MEMORY_USAGE
67 #define DEBUG_URL_MEMORY_USAGE 0
70 #if DEBUG_URL_MEMORY_USAGE
71 static UInt32 numURLs
= 0;
72 static UInt32 numDealloced
= 0;
73 static UInt32 numFileURLsCreated
= 0;
74 static UInt32 numExtraDataAllocated
= 0;
75 static UInt32 numURLsWithBaseURL
= 0;
76 static UInt32 numNonUTF8EncodedURLs
= 0;
79 /* The bit flags in myURL->_flags */
81 #define HAS_SCHEME (0x00000001)
82 #define HAS_USER (0x00000002)
83 #define HAS_PASSWORD (0x00000004)
84 #define HAS_HOST (0x00000008)
85 #define HAS_PORT (0x00000010)
86 #define HAS_PATH (0x00000020)
87 #define HAS_PARAMETERS (0x00000040)
88 #define HAS_QUERY (0x00000080)
89 #define HAS_FRAGMENT (0x00000100)
90 // various boolean flags
91 #define IS_IPV6_ENCODED (0x00000400)
92 #define IS_DIRECTORY (0x00000800)
93 #define IS_CANONICAL_FILE_URL (0x00001000)
94 #define PATH_HAS_FILE_ID (0x00002000)
95 #define IS_ABSOLUTE (0x00004000)
96 #define IS_DECOMPOSABLE (0x00008000)
97 #define POSIX_AND_URL_PATHS_MATCH (0x00010000) // 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
98 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00020000)
99 #define USES_EIGHTBITSTRINGENCODING (0x00040000)
100 // scheme bits and amount to shift it to translate to the kXXXXScheme enums
101 #define SCHEME_TYPE_MASK (0xE0000000)
102 #define SCHEME_SHIFT 29
104 kHasUncommonScheme
= 0,
112 // accessors for the scheme bits in _flags
113 CF_INLINE UInt32
_getSchemeTypeFromFlags(UInt32 flags
);
114 CF_INLINE
void _setSchemeTypeInFlags(UInt32
*flags
, UInt32 schemeType
);
116 // Other useful defines
117 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
118 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
119 // These flags can be compared for equality since these are all set once when the CFURL is created.
120 // IS_CANONICAL_FILE_URL cannot be compared since we don't create the URL string.
121 // POSIX_AND_URL_PATHS_MATCH cannot be compared because it may not be set
122 // ORIGINAL_AND_URL_STRINGS_MATCH cannot be compared because it gets set on demand later.
123 #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_ABSOLUTE | IS_DECOMPOSABLE | SCHEME_TYPE_MASK )
125 // 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.
126 #define FULL_URL_REPRESENTATION (0xF)
128 /* The bit flags in _CFURLAdditionalData->_additionalDataFlags */
129 /* If ORIGINAL_AND_URL_STRINGS_MATCH in myURL->_flags is false, these bits determine where they differ. XXXX_DIFFERS must match the HAS_XXXX */
130 #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
131 #define USER_DIFFERS HAS_USER
132 #define PASSWORD_DIFFERS HAS_PASSWORD
133 #define HOST_DIFFERS HAS_HOST
134 #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
135 #define PATH_DIFFERS HAS_PATH // unused
136 #define PARAMETERS_DIFFER HAS_PARAMETERS // unused
137 #define QUERY_DIFFER HAS_QUERY // unused
138 #define FRAGMENT_DIFFER HAS_FRAGMENT // unused
140 #define FILE_ID_PREFIX ".file"
141 #define FILE_ID_KEY "id"
142 #define FILE_ID_PREAMBLE "/.file/id="
143 #define FILE_ID_PREAMBLE_LENGTH 10
145 #define FILE_PREFIX "file://"
146 #define FILE_PREFIX_WITH_AUTHORITY "file://localhost"
147 static const UInt8 fileURLPrefixWithAuthority
[] = FILE_PREFIX_WITH_AUTHORITY
;
149 // In order to reduce the sizeof ( __CFURL ), move these items into a seperate structure which is
150 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
151 // either a sanitized string or a reserved pointer for URLHandle.
152 struct _CFURLAdditionalData
{
153 void *_reserved
; // Reserved for URLHandle's use.
154 CFStringRef _sanitizedString
; // The fully compliant RFC string. This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false.
155 UInt32 _additionalDataFlags
; // these flags only apply to things we need to keep state for in _CFURLAdditionalData (like the XXXX_DIFFERS flags)
159 CFRuntimeBase _cfBase
;
161 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes
162 CFStringRef _string
; // Never NULL
165 struct _CFURLAdditionalData
* _extra
;
166 void *_resourceInfo
; // For use by CarbonCore to cache property values. Retained and released by CFURL.
170 CF_INLINE
void* _getReserved ( const struct __CFURL
* url
)
172 if ( url
&& url
->_extra
) {
173 return ( url
->_extra
->_reserved
);
180 CF_INLINE CFStringRef
_getSanitizedString(const struct __CFURL
* url
)
182 if ( url
&& url
->_extra
) {
183 return ( url
->_extra
->_sanitizedString
);
190 CF_INLINE UInt32
_getAdditionalDataFlags(const struct __CFURL
* url
)
192 if ( url
&& url
->_extra
) {
193 return ( url
->_extra
->_additionalDataFlags
);
200 CF_INLINE
void* _getResourceInfo ( const struct __CFURL
* url
)
203 return url
->_resourceInfo
;
210 static void _CFURLAllocateExtraDataspace( struct __CFURL
* url
)
212 if ( url
&& ! url
->_extra
)
213 { struct _CFURLAdditionalData
* extra
= (struct _CFURLAdditionalData
*) CFAllocatorAllocate( CFGetAllocator( url
), sizeof( struct _CFURLAdditionalData
), __kCFAllocatorGCScannedMemory
);
215 extra
->_reserved
= _getReserved( url
);
216 extra
->_additionalDataFlags
= _getAdditionalDataFlags(url
);
217 extra
->_sanitizedString
= _getSanitizedString(url
);
221 #if DEBUG_URL_MEMORY_USAGE
222 numExtraDataAllocated
++;
227 CF_INLINE
void _setReserved ( struct __CFURL
* url
, void* reserved
)
231 // Don't allocate extra space if we're just going to be storing NULL
232 if ( !url
->_extra
&& reserved
)
233 _CFURLAllocateExtraDataspace( url
);
236 __CFAssignWithWriteBarrier((void **)&url
->_extra
->_reserved
, reserved
);
240 CF_INLINE
void _setSanitizedString( struct __CFURL
* url
, CFMutableStringRef sanitizedString
)
244 // Don't allocate extra space if we're just going to be storing NULL
245 if ( !url
->_extra
&& sanitizedString
) {
246 _CFURLAllocateExtraDataspace( url
);
250 if ( url
->_extra
->_sanitizedString
) {
251 CFRelease(url
->_extra
->_sanitizedString
);
253 url
->_extra
->_sanitizedString
= CFStringCreateCopy(CFGetAllocator(url
), sanitizedString
);
259 CF_INLINE
void _setAdditionalDataFlags(struct __CFURL
* url
, UInt32 additionalDataFlags
)
263 // Don't allocate extra space if we're just going to be storing 0
264 if ( !url
->_extra
&& (additionalDataFlags
!= 0) ) {
265 _CFURLAllocateExtraDataspace( url
);
269 url
->_extra
->_additionalDataFlags
= additionalDataFlags
;
274 CF_INLINE
void _setResourceInfo ( struct __CFURL
* url
, void* resourceInfo
)
278 if ( url
&& OSAtomicCompareAndSwapPtrBarrier( NULL
, resourceInfo
, &url
->_resourceInfo
)) {
279 CFRetain( resourceInfo
);
283 CF_INLINE UInt32
_getSchemeTypeFromFlags(UInt32 flags
)
285 return ( (flags
& SCHEME_TYPE_MASK
) >> SCHEME_SHIFT
);
288 CF_INLINE
void _setSchemeTypeInFlags(UInt32
*flags
, UInt32 schemeType
)
290 CFAssert2((schemeType
>= kHasUncommonScheme
) && (schemeType
< kMaxScheme
), __kCFLogAssertion
, "%s(): Received bad schemeType %d", __PRETTY_FUNCTION__
, schemeType
);
291 *flags
= (*flags
& ~SCHEME_TYPE_MASK
) + (schemeType
<< SCHEME_SHIFT
);
294 /* Returns whether the provided bytes can be stored in ASCII
296 static Boolean
__CFBytesInASCII(const uint8_t *bytes
, CFIndex len
) {
298 /* A bit of unrolling; go by 32s, 16s, and 8s first */
300 uint64_t val
= *(const uint64_t *)bytes
;
301 uint64_t hiBits
= (val
& 0x8080808080808080ULL
); // More efficient to collect this rather than do a conditional at every step
303 val
= *(const uint64_t *)bytes
;
304 hiBits
|= (val
& 0x8080808080808080ULL
);
306 val
= *(const uint64_t *)bytes
;
307 hiBits
|= (val
& 0x8080808080808080ULL
);
309 val
= *(const uint64_t *)bytes
;
310 if (hiBits
| (val
& 0x8080808080808080ULL
)) return false;
316 uint64_t val
= *(const uint64_t *)bytes
;
317 uint64_t hiBits
= (val
& 0x8080808080808080ULL
);
319 val
= *(const uint64_t *)bytes
;
320 if (hiBits
| (val
& 0x8080808080808080ULL
)) return false;
326 uint64_t val
= *(const uint64_t *)bytes
;
327 if (val
& 0x8080808080808080ULL
) return false;
334 uint32_t val
= *(const uint32_t *)bytes
;
335 if (val
& 0x80808080U
) return false;
339 /* Handle the rest one byte at a time */
341 if (*bytes
++ & 0x80) return false;
347 static Boolean
_pathHasFileIDPrefix(CFStringRef path
);
348 static void _convertToURLRepresentation(struct __CFURL
*url
, UInt32 fsType
);
349 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
350 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
351 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
352 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
353 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
354 static void computeSanitizedString(CFURLRef url
);
355 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
356 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
357 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
360 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
361 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->_ranges
));
372 static const unsigned char sURLValidCharacters
[128] = {
406 /* '!' 33 */ VALID
| UNRESERVED
| PATHVALID
,
409 /* '$' 36 */ VALID
| PATHVALID
,
411 /* '&' 38 */ VALID
| PATHVALID
,
412 /* ''' 39 */ VALID
| UNRESERVED
| PATHVALID
,
413 /* '(' 40 */ VALID
| UNRESERVED
| PATHVALID
,
414 /* ')' 41 */ VALID
| UNRESERVED
| PATHVALID
,
415 /* '*' 42 */ VALID
| UNRESERVED
| PATHVALID
,
416 /* '+' 43 */ VALID
| SCHEME
| PATHVALID
,
417 /* ',' 44 */ VALID
| PATHVALID
,
418 /* '-' 45 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
419 /* '.' 46 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
420 /* '/' 47 */ VALID
| PATHVALID
,
421 /* '0' 48 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
422 /* '1' 49 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
423 /* '2' 50 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
424 /* '3' 51 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
425 /* '4' 52 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
426 /* '5' 53 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
427 /* '6' 54 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
428 /* '7' 55 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
429 /* '8' 56 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
430 /* '9' 57 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
434 /* '=' 61 */ VALID
| PATHVALID
,
438 /* 'A' 65 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
439 /* 'B' 66 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
440 /* 'C' 67 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
441 /* 'D' 68 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
442 /* 'E' 69 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
443 /* 'F' 70 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
444 /* 'G' 71 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
445 /* 'H' 72 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
446 /* 'I' 73 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
447 /* 'J' 74 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
448 /* 'K' 75 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
449 /* 'L' 76 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
450 /* 'M' 77 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
451 /* 'N' 78 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
452 /* 'O' 79 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
453 /* 'P' 80 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
454 /* 'Q' 81 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
455 /* 'R' 82 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
456 /* 'S' 83 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
457 /* 'T' 84 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
458 /* 'U' 85 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
459 /* 'V' 86 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
460 /* 'W' 87 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
461 /* 'X' 88 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
462 /* 'Y' 89 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
463 /* 'Z' 90 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
468 /* '_' 95 */ VALID
| UNRESERVED
| PATHVALID
,
470 /* 'a' 97 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
471 /* 'b' 98 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
472 /* 'c' 99 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
473 /* 'd' 100 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
474 /* 'e' 101 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
475 /* 'f' 102 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
| HEXDIGIT
,
476 /* 'g' 103 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
477 /* 'h' 104 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
478 /* 'i' 105 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
479 /* 'j' 106 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
480 /* 'k' 107 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
481 /* 'l' 108 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
482 /* 'm' 109 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
483 /* 'n' 110 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
484 /* 'o' 111 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
485 /* 'p' 112 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
486 /* 'q' 113 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
487 /* 'r' 114 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
488 /* 's' 115 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
489 /* 't' 116 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
490 /* 'u' 117 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
491 /* 'v' 118 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
492 /* 'w' 119 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
493 /* 'x' 120 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
494 /* 'y' 121 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
495 /* 'z' 122 */ VALID
| UNRESERVED
| SCHEME
| PATHVALID
,
499 /* '~' 126 */ VALID
| UNRESERVED
| PATHVALID
,
503 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
504 return (ch
<= 127) ? (sURLValidCharacters
[ch
] & VALID
) : false;
507 CF_INLINE Boolean
scheme_valid(UniChar ch
) {
508 return (ch
<= 127) ? (sURLValidCharacters
[ch
] & SCHEME
) : false;
511 // "Unreserved" as defined by RFC 2396
512 CF_INLINE Boolean
isUnreservedCharacter(UniChar ch
) {
513 return (ch
<= 127) ? (sURLValidCharacters
[ch
] & UNRESERVED
) : false;
516 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
517 return (ch
<= 127) ? (sURLValidCharacters
[ch
] & PATHVALID
) : false;
520 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
521 return (ch
<= 127) ? (sURLValidCharacters
[ch
] & HEXDIGIT
) : false;
524 // Returns false if ch1 or ch2 isn't properly formatted
525 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
527 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
528 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
529 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
532 *result
= (*result
) << 4;
533 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
534 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
535 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
541 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
542 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (_getSanitizedString(url
) != NULL
);
546 CreateStringFromFileSystemRepresentationByAddingPercentEscapes creates a CFString
547 for the path-absolute form of a URI path component from the native file system representation.
549 The rules for path-absolute from rfc3986 are:
550 path-absolute = "/" [ segment-nz *( "/" segment ) ]
553 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
554 pct-encoded = "%" HEXDIG HEXDIG
555 unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
556 sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
558 static CFStringRef
CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc
, const UInt8
*bytes
, CFIndex numBytes
, Boolean windowsPath
)
560 static const UInt8 hexchars
[] = "0123456789ABCDEF";
561 STACK_BUFFER_DECL(UInt8
, stackBuf
, PATH_MAX
* 3); // worst case is every byte needs to be percent-escaped
564 const UInt8
*bytePtr
= bytes
;
568 // choose a buffer to percent-escape into.
569 if ( numBytes
<= PATH_MAX
) {
570 bufStartPtr
= &stackBuf
[0];
573 // worst case is every byte needs to be percent-escaped (numBytes * 3)
574 bufStartPtr
= (UInt8
*)malloc(numBytes
* 3);
577 if ( bufStartPtr
!= NULL
) {
578 bufBytePtr
= bufStartPtr
;
579 for ( idx
= 0; (idx
< numBytes
) && (*bytePtr
!= 0); ++idx
) {
580 switch ( *bytePtr
) {
581 // these are the visible 7-bit ascii characters that are not legal pchar octets
585 case ';': // 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 case '?': // we need to percent-escape '?' in file system paths so it won't be mistaken for the start of a query
597 // percent-escape non-pchar octets spread throughout the visible 7-bit ascii range
599 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
600 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
603 if ( (*bytePtr
<= ' ') || // percent-escape non-pchar octets that are space or less (control characters)
604 (*bytePtr
>= 0x7f) || // percent-escape non-pchar octets that del and 8-bit ascii with the high bit set
605 (windowsPath
&& (*bytePtr
== '/')) ) { // percent-escape the forward slash if this is a windowsPath
607 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
608 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
611 // copy everything else
612 *bufBytePtr
++ = *bytePtr
;
619 // did we convert numBytes?
620 if ( idx
== numBytes
) {
622 result
= CFStringCreateWithBytes(alloc
, bufStartPtr
, (CFIndex
)(bufBytePtr
-bufStartPtr
), kCFStringEncodingUTF8
, FALSE
);
625 // no, but it's OK if the remaining bytes are all nul (embedded nul bytes are not allowed)
626 for ( /* start where we left off */; (idx
< numBytes
) && (bufStartPtr
[idx
] == 0); ++idx
) {
629 if ( idx
== numBytes
) {
631 result
= CFStringCreateWithBytes(alloc
, bufStartPtr
, (CFIndex
)(bufBytePtr
-bufStartPtr
), kCFStringEncodingUTF8
, FALSE
);
634 // the remaining bytes were not all nul
639 // free the buffer if we malloc'd it
640 if ( bufStartPtr
!= &stackBuf
[0] ) {
650 // 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.
651 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
652 CFStringRef result
= NULL
;
653 STACK_BUFFER_DECL(char, buffer
, PATH_MAX
);
654 if ( CFStringGetCString(str
, buffer
, PATH_MAX
, kCFStringEncodingUTF8
) ) {
655 result
= CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault
, (const UInt8
*)buffer
, strlen(buffer
), !preserveSlashes
);
660 // We have 2 UniChars of a surrogate; we must convert to the correct percent-encoded UTF8 string and append to str. Added so that file system URLs can always be converted from POSIX to full URL representation. -- REW, 8/20/2001
661 static Boolean
_hackToConvertSurrogates(UniChar highChar
, UniChar lowChar
, CFMutableStringRef str
) {
662 UniChar surrogate
[2];
663 uint8_t bytes
[6]; // Aki sez it should never take more than 6 bytes
666 surrogate
[0] = highChar
;
667 surrogate
[1] = lowChar
;
668 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &len
) != kCFStringEncodingConversionSuccess
) {
671 for (currByte
= bytes
; currByte
< bytes
+ len
; currByte
++) {
672 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
673 unsigned char high
, low
;
674 high
= ((*currByte
) & 0xf0) >> 4;
675 low
= (*currByte
) & 0x0f;
676 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
677 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
678 CFStringAppendCharacters(str
, escapeSequence
, 3);
683 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
684 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
685 uint8_t *bytePtr
= bytes
, *currByte
;
687 CFAllocatorRef alloc
= NULL
;
688 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
689 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
690 if (byteLength
<= 6) {
691 // The encoding cannot accomodate the character
694 alloc
= CFGetAllocator(str
);
695 bytePtr
= (uint8_t *)CFAllocatorAllocate(alloc
, byteLength
, 0);
696 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
697 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
701 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
702 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
703 unsigned char high
, low
;
704 high
= ((*currByte
) & 0xf0) >> 4;
705 low
= (*currByte
) & 0x0f;
706 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
707 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
708 CFStringAppendCharacters(str
, escapeSequence
, 3);
710 if (bytePtr
!= bytes
) {
711 CFAllocatorDeallocate(alloc
, bytePtr
);
716 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
717 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
718 CFMutableStringRef newStr
= NULL
;
721 CFRange percentRange
, searchRange
;
722 CFStringRef escapedStr
= NULL
;
723 CFMutableStringRef strForEscapedChar
= NULL
;
725 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
726 Boolean failed
= false;
728 if (!originalString
) return NULL
;
730 if (charactersToLeaveEscaped
== NULL
) {
731 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
734 length
= CFStringGetLength(originalString
);
735 searchRange
= CFRangeMake(0, length
);
737 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
738 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
739 uint8_t numBytesExpected
;
743 // Make sure we have at least 2 more characters
744 if (length
- percentRange
.location
< 3) { failed
= true; break; }
746 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
747 // so we assume the percent character is legit, and let it pass into the string
748 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
749 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
750 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
751 if (!(bytes
[0] & 0x80)) {
752 numBytesExpected
= 1;
753 } else if (!(bytes
[0] & 0x20)) {
754 numBytesExpected
= 2;
755 } else if (!(bytes
[0] & 0x10)) {
756 numBytesExpected
= 3;
758 numBytesExpected
= 4;
760 if (numBytesExpected
== 1) {
761 // one byte sequence (most common case); handle this specially
762 escapedChar
= bytes
[0];
763 if (!strForEscapedChar
) {
764 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
766 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
769 // Make sure up front that we have enough characters
770 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
771 for (j
= 1; j
< numBytesExpected
; j
++) {
772 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
773 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
774 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
775 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
778 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
779 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
782 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
783 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
784 escapedChar
= 0xfeff;
785 if (!strForEscapedChar
) {
786 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
788 CFRelease(escapedStr
);
789 escapedStr
= (CFStringRef
)CFRetain(strForEscapedChar
);
794 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
795 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
796 searchRange
.length
= length
- searchRange
.location
;
799 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
801 CFRelease(escapedStr
);
809 newStr
= CFStringCreateMutable(alloc
, length
);
811 if (percentRange
.location
- mark
> 0) {
812 // The creation of this temporary string is unfortunate.
813 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
814 CFStringAppend(newStr
, substring
);
815 CFRelease(substring
);
817 CFStringAppend(newStr
, escapedStr
);
819 CFRelease(escapedStr
);
822 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
825 if (escapedStr
) CFRelease(escapedStr
);
826 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
828 if (newStr
) CFRelease(newStr
);
832 // Need to cat on the remainder of the string
833 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
834 CFStringAppend(newStr
, substring
);
835 CFRelease(substring
);
839 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
844 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
845 if (enc
== kCFStringEncodingUTF8
) {
846 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
848 CFMutableStringRef newStr
= NULL
;
849 CFMutableStringRef escapedStr
= NULL
;
852 CFRange percentRange
, searchRange
;
853 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
854 Boolean failed
= false;
855 uint8_t byteBuffer
[8];
856 uint8_t *bytes
= byteBuffer
;
857 int capacityOfBytes
= 8;
859 if (!originalString
) return NULL
;
861 if (charactersToLeaveEscaped
== NULL
) {
862 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
865 length
= CFStringGetLength(originalString
);
866 searchRange
= CFRangeMake(0, length
);
868 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
870 CFIndex percentLoc
= percentRange
.location
;
871 CFStringRef convertedString
;
872 int numBytesUsed
= 0;
874 // Make sure we have at least 2 more characters
875 if (length
- percentLoc
< 3) { failed
= true; break; }
877 if (numBytesUsed
== capacityOfBytes
) {
878 if (bytes
== byteBuffer
) {
879 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
880 memmove(bytes
, byteBuffer
, capacityOfBytes
);
881 capacityOfBytes
= 16;
883 void *oldbytes
= bytes
;
884 int oldcap
= capacityOfBytes
;
885 capacityOfBytes
= 2*capacityOfBytes
;
886 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, capacityOfBytes
* sizeof(uint8_t), 0);
887 memmove(bytes
, oldbytes
, oldcap
);
888 CFAllocatorDeallocate(alloc
, oldbytes
);
892 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
894 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
896 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
898 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
899 searchRange
.location
= percentLoc
;
900 searchRange
.length
= length
- searchRange
.location
;
903 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
904 if (!convertedString
) {
910 newStr
= CFStringCreateMutable(alloc
, length
);
912 if (percentRange
.location
- mark
> 0) {
913 // The creation of this temporary string is unfortunate.
914 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
915 CFStringAppend(newStr
, substring
);
916 CFRelease(substring
);
920 CFStringAppend(newStr
, convertedString
);
922 CFIndex i
, c
= CFStringGetLength(convertedString
);
924 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
926 for (i
= 0; i
< c
; i
++) {
927 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
928 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
929 CFStringAppendCharacters(newStr
, &ch1
, 1);
931 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
932 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
936 CFRelease(convertedString
);
937 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
940 if (escapedStr
) CFRelease(escapedStr
);
941 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
943 if (newStr
) CFRelease(newStr
);
947 // Need to cat on the remainder of the string
948 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
949 CFStringAppend(newStr
, substring
);
950 CFRelease(substring
);
954 return (CFStringRef
)CFStringCreateCopy(alloc
, originalString
);
960 static CFStringRef
_addPercentEscapesToString(CFAllocatorRef allocator
, CFStringRef originalString
, Boolean (*shouldReplaceChar
)(UniChar
, void*), CFIndex (*handlePercentChar
)(CFIndex
, CFStringRef
, CFStringRef
*, void *), CFStringEncoding encoding
, void *context
) {
961 CFMutableStringRef newString
= NULL
;
963 CFStringInlineBuffer buf
;
965 if (!originalString
) return NULL
;
966 length
= CFStringGetLength(originalString
);
967 if (length
== 0) return (CFStringRef
)CFStringCreateCopy(allocator
, originalString
);
968 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
970 for (idx
= 0; idx
< length
; idx
++) {
971 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
972 Boolean shouldReplace
= shouldReplaceChar(ch
, context
);
974 // Perform the replacement
976 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
977 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
979 if (!_appendPercentEscapesForCharacter(ch
, encoding
, newString
)) {
980 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
981 if (encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1))) {
982 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
983 if (_hackToConvertSurrogates(ch
, CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1), newString
)) {
984 idx
++; // We consumed 2 characters, not 1
992 } else if (ch
== '%' && handlePercentChar
) {
993 CFStringRef replacementString
= NULL
;
994 CFIndex newIndex
= handlePercentChar(idx
, originalString
, &replacementString
, context
);
997 } else if (replacementString
) {
999 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
1000 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
1002 CFStringAppend(newString
, replacementString
);
1003 CFRelease(replacementString
);
1005 if (newIndex
== idx
) {
1007 CFStringAppendCharacters(newString
, &ch
, 1);
1010 if (!replacementString
&& newString
) {
1012 for (tmpIndex
= idx
; tmpIndex
< newIndex
; tmpIndex
++) {
1013 ch
= CFStringGetCharacterAtIndex(originalString
, idx
);
1014 CFStringAppendCharacters(newString
, &ch
, 1);
1019 } else if (newString
) {
1020 CFStringAppendCharacters(newString
, &ch
, 1);
1024 // Ran in to an encoding failure
1025 if (newString
) CFRelease(newString
);
1027 } else if (newString
) {
1030 return (CFStringRef
)CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
1035 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
1036 CFIndex i
, c
= CFStringGetLength(string
);
1037 CFStringInlineBuffer buf
;
1038 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
1039 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
1043 static Boolean
_shouldPercentReplaceChar(UniChar ch
, void *context
) {
1044 CFStringRef unescape
= ((CFStringRef
*)context
)[0];
1045 CFStringRef escape
= ((CFStringRef
*)context
)[1];
1046 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
1047 if (shouldReplace
) {
1048 if (unescape
&& _stringContainsCharacter(unescape
, ch
)) {
1049 shouldReplace
= false;
1051 } else if (escape
&& _stringContainsCharacter(escape
, ch
)) {
1052 shouldReplace
= true;
1054 return shouldReplace
;
1057 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
1058 CFStringRef strings
[2];
1059 strings
[0] = charactersToLeaveUnescaped
;
1060 strings
[1] = legalURLCharactersToBeEscaped
;
1061 return _addPercentEscapesToString(allocator
, originalString
, _shouldPercentReplaceChar
, NULL
, encoding
, strings
);
1064 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
1066 CFURLRef url1
= (CFURLRef
)cf1
;
1067 CFURLRef url2
= (CFURLRef
)cf2
;
1069 __CFGenericValidateType(cf1
, CFURLGetTypeID());
1070 __CFGenericValidateType(cf2
, CFURLGetTypeID());
1072 if ( url1
== url2
) {
1076 if ( (url1
->_flags
& EQUAL_FLAGS_MASK
) != (url2
->_flags
& EQUAL_FLAGS_MASK
) ) {
1080 if ( (url1
->_base
&& !url2
->_base
) ||
1081 (!url1
->_base
&& url2
->_base
) ||
1082 (url1
->_base
&& url2
->_base
&& !CFEqual(url1
->_base
, url2
->_base
)) ) {
1086 // no base urls, so compare the URL strings
1087 // Do not compare the original strings; compare the sanatized strings.
1088 result
= CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
1095 static CFHashCode
__CFURLHash(CFTypeRef cf
)
1100 // use the CFHashCode of the URL
1101 result
= CFHash(CFURLGetString((CFURLRef
)cf
));
1104 // no object, no hashcode
1111 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
1112 CFURLRef url
= (CFURLRef
)cf
;
1113 __CFGenericValidateType(cf
, CFURLGetTypeID());
1115 CFRetain(url
->_string
);
1116 return url
->_string
;
1118 // Do not dereference url->_base; it may be an ObjC object
1119 return CFStringCreateWithFormat(CFGetAllocator(url
), NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
1124 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
1125 CFURLRef url
= (CFURLRef
)cf
;
1127 CFAllocatorRef alloc
= CFGetAllocator(url
);
1129 CFStringRef baseString
= CFCopyDescription(url
->_base
);
1130 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %d\n\tbase = %@}"), cf
, alloc
, url
->_string
, url
->_encoding
, baseString
);
1131 CFRelease(baseString
);
1133 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %d, base = (null)}"), cf
, alloc
, url
->_string
, url
->_encoding
);
1138 #if DEBUG_URL_MEMORY_USAGE
1140 extern __attribute((used
)) void __CFURLDumpMemRecord(void) {
1141 CFStringRef str
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%d URLs created; %d destroyed\n%d file URLs created; %d urls had 'extra' data allocated, %d had base urls, %d were not UTF8 encoded\n"), numURLs
, numDealloced
, numFileURLsCreated
, numExtraDataAllocated
, numURLsWithBaseURL
, numNonUTF8EncodedURLs
);
1147 static void __CFURLDeallocate(CFTypeRef cf
) {
1148 CFURLRef url
= (CFURLRef
)cf
;
1149 CFAllocatorRef alloc
;
1150 __CFGenericValidateType(cf
, CFURLGetTypeID());
1151 alloc
= CFGetAllocator(url
);
1152 #if DEBUG_URL_MEMORY_USAGE
1155 if (url
->_string
) CFRelease(url
->_string
); // GC: 3879914
1156 if (url
->_base
) CFRelease(url
->_base
);
1157 if (url
->_ranges
) CFAllocatorDeallocate(alloc
, url
->_ranges
);
1158 CFStringRef sanitizedString
= _getSanitizedString(url
);
1159 if (sanitizedString
) CFRelease(sanitizedString
);
1160 if ( url
->_extra
!= NULL
) CFAllocatorDeallocate( alloc
, url
->_extra
);
1161 if (_getResourceInfo(url
)) CFRelease(_getResourceInfo(url
));
1164 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
1166 static const CFRuntimeClass __CFURLClass
= {
1168 "CFURL", // className
1171 __CFURLDeallocate
, // finalize
1172 __CFURLEqual
, // equal
1173 __CFURLHash
, // hash
1174 __CFURLCopyFormattingDescription
, // copyFormattingDesc
1175 __CFURLCopyDescription
, // copyDebugDesc
1180 // When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but
1181 // when it is defined, we must prefix with static to prevent the string from being exported
1182 #ifdef __CONSTANT_CFSTRINGS__
1183 static CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1184 static CONST_STRING_DECL(kCFURLHTTPSScheme
, "https")
1185 static CONST_STRING_DECL(kCFURLFileScheme
, "file")
1186 static CONST_STRING_DECL(kCFURLDataScheme
, "data")
1187 static CONST_STRING_DECL(kCFURLFTPScheme
, "ftp")
1188 static CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1190 CONST_STRING_DECL(kCFURLHTTPScheme
, "http")
1191 CONST_STRING_DECL(kCFURLHTTPSScheme
, "https")
1192 CONST_STRING_DECL(kCFURLFileScheme
, "file")
1193 CONST_STRING_DECL(kCFURLDataScheme
, "data")
1194 CONST_STRING_DECL(kCFURLFTPScheme
, "ftp")
1195 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1197 __private_extern__
void __CFURLInitialize(void) {
1198 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1201 /* Toll-free bridging support; get the true CFURL from an NSURL */
1202 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1203 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID
, CFURLRef
, (NSURL
*)url
, _cfurl
);
1207 CFTypeID
CFURLGetTypeID(void) {
1208 return __kCFURLTypeID
;
1211 __private_extern__
void CFShowURL(CFURLRef url
) {
1213 fprintf(stdout
, "(null)\n");
1216 fprintf(stdout
, "<CFURL %p>{", (const void*)url
);
1217 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1218 fprintf(stdout
, "ObjC bridged object}\n");
1221 fprintf(stdout
, "\n\tRelative string: ");
1222 CFShow(url
->_string
);
1223 fprintf(stdout
, "\tBase URL: ");
1225 fprintf(stdout
, "<%p> ", (const void*)url
->_base
);
1228 fprintf(stdout
, "(null)\n");
1230 fprintf(stdout
, "\tFlags: 0x%x\n}\n", (unsigned int)url
->_flags
);
1234 /***************************************************/
1235 /* URL creation and String/Data creation from URLS */
1236 /***************************************************/
1237 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, Boolean useEightBitStringEncoding
, UInt8
*inBuffer
, CFIndex inBufferSize
, const char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1238 CFIndex neededLength
;
1242 *cstring
= CFStringGetCStringPtr(string
, (useEightBitStringEncoding
? __CFStringGetEightBitStringEncoding() : kCFStringEncodingISOLatin1
));
1246 *freeCharacters
= false;
1250 *ustring
= CFStringGetCharactersPtr(string
);
1252 *useCString
= false;
1253 *freeCharacters
= false;
1257 length
= CFStringGetLength(string
);
1258 rg
= CFRangeMake(0, length
);
1259 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1260 if (neededLength
== length
) {
1262 if ( (inBuffer
!= NULL
) && (length
<= inBufferSize
) ) {
1263 buf
= (char *)inBuffer
;
1264 *freeCharacters
= false;
1267 buf
= (char *)CFAllocatorAllocate(alloc
, length
, 0);
1268 *freeCharacters
= true;
1270 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, (uint8_t *)buf
, length
, NULL
);
1275 if ( (inBuffer
!= NULL
) && ((length
* sizeof(UniChar
)) <= inBufferSize
) ) {
1276 buf
= (UniChar
*)inBuffer
;
1277 *freeCharacters
= false;
1280 buf
= (UniChar
*)CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1281 *freeCharacters
= true;
1283 CFStringGetCharacters(string
, rg
, buf
);
1285 *useCString
= false;
1289 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1290 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1292 /* index gives the URL part involved; to calculate the correct range index, use the number of the bit of the equivalent flag (i.e. the host flag is HAS_HOST, which is 0x8. so the range index for the host is 3.) Note that this is true in this function ONLY, since the ranges stored in (*range) are actually packed, skipping those URL components that don't exist. This is why the indices are hard-coded in this function. */
1294 CFIndex idx
, base_idx
= 0;
1295 CFIndex string_length
;
1296 UInt32 flags
= *theFlags
;
1297 Boolean useEightBitStringEncoding
= (flags
& USES_EIGHTBITSTRINGENCODING
) != 0;
1298 Boolean useCString
, freeCharacters
, isCompliant
;
1299 uint8_t numRanges
= 0;
1300 const char *cstring
= NULL
;
1301 const UniChar
*ustring
= NULL
;
1302 CFIndex stackBufferSize
= 4096;
1303 STACK_BUFFER_DECL(UInt8
, stackBuffer
, stackBufferSize
);
1305 string_length
= CFStringGetLength(string
);
1306 constructBuffers(alloc
, string
, useEightBitStringEncoding
, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1308 // Algorithm is as described in RFC 1808
1309 // 1: parse the fragment; remainder after left-most "#" is fragment
1310 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1311 if ('#' == STRING_CHAR(idx
)) {
1312 flags
|= HAS_FRAGMENT
;
1313 ranges
[8].location
= idx
+ 1;
1314 ranges
[8].length
= string_length
- (idx
+ 1);
1316 string_length
= idx
; // remove fragment from parse string
1320 // 2: parse the scheme
1321 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1322 UniChar ch
= STRING_CHAR(idx
);
1324 flags
|= HAS_SCHEME
;
1325 flags
|= IS_ABSOLUTE
;
1326 ranges
[0].location
= base_idx
;
1327 ranges
[0].length
= idx
;
1330 // optimization for ftp urls
1331 if (idx
== 3 && STRING_CHAR(0) == 'f' && STRING_CHAR(1) == 't' && STRING_CHAR(2) == 'p') {
1332 _setSchemeTypeInFlags(&flags
, kHasFtpScheme
);
1334 else if (idx
== 4) {
1335 // optimization for http urls
1336 if (STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' && STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p') {
1337 _setSchemeTypeInFlags(&flags
, kHasHttpScheme
);
1339 // optimization for file urls
1340 if (STRING_CHAR(0) == 'f' && STRING_CHAR(1) == 'i' && STRING_CHAR(2) == 'l' && STRING_CHAR(3) == 'e') {
1341 _setSchemeTypeInFlags(&flags
, kHasFileScheme
);
1343 // optimization for data urls
1344 if (STRING_CHAR(0) == 'd' && STRING_CHAR(1) == 'a' && STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'a') {
1345 _setSchemeTypeInFlags(&flags
, kHasDataScheme
);
1348 // optimization for https urls
1349 else if (idx
== 5 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' && STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p' && STRING_CHAR(3) == 's') {
1350 _setSchemeTypeInFlags(&flags
, kHasHttpsScheme
);
1353 } else if (!scheme_valid(ch
)) {
1354 break; // invalid scheme character -- no scheme
1358 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1359 // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage
1360 // expects this to be treated identically to "scheme://" - REW, 12/08/03
1361 if (!(flags
& HAS_SCHEME
)) {
1363 } else if (base_idx
== string_length
) {
1364 isCompliant
= false;
1365 } else if (STRING_CHAR(base_idx
) != '/') {
1366 isCompliant
= false;
1372 // Clear the fragment flag if it's been set
1373 if (flags
& HAS_FRAGMENT
) {
1374 flags
&= (~HAS_FRAGMENT
);
1375 string_length
= CFStringGetLength(string
);
1377 (*theFlags
) = flags
;
1378 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
1379 (*range
)->location
= ranges
[0].location
;
1380 (*range
)->length
= ranges
[0].length
;
1382 if (freeCharacters
) {
1383 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1387 // URL is 1808-compliant
1388 flags
|= IS_DECOMPOSABLE
;
1390 // 3: parse the network location and login
1391 if (2 <= (string_length
- base_idx
) && '/' == STRING_CHAR(base_idx
) && '/' == STRING_CHAR(base_idx
+1)) {
1392 CFIndex base
= 2 + base_idx
, extent
;
1393 for (idx
= base
; idx
< string_length
; idx
++) {
1394 if ('/' == STRING_CHAR(idx
) || '?' == STRING_CHAR(idx
)) break;
1398 // net_loc parts extend from base to extent (but not including), which might be to end of string
1399 // net location is "<user>:<password>@<host>:<port>"
1400 if (extent
!= base
) {
1401 for (idx
= base
; idx
< extent
; idx
++) {
1402 if ('@' == STRING_CHAR(idx
)) { // there is a user
1406 ranges
[1].location
= base
; // base of the user
1407 for (idx2
= base
; idx2
< idx
; idx2
++) {
1408 if (':' == STRING_CHAR(idx2
)) { // found a password separator
1409 flags
|= HAS_PASSWORD
;
1411 ranges
[2].location
= idx2
+1; // base of the password
1412 ranges
[2].length
= idx
-(idx2
+1); // password extent
1413 ranges
[1].length
= idx2
- base
; // user extent
1417 if (!(flags
& HAS_PASSWORD
)) {
1418 // user extends to the '@'
1419 ranges
[1].length
= idx
- base
; // user extent
1427 ranges
[3].location
= base
; // base of host
1429 // base has been advanced past the user and password if they existed
1430 for (idx
= base
; idx
< extent
; idx
++) {
1431 // IPV6 support (RFC 2732) DCJ June/10/2002
1432 if ('[' == STRING_CHAR(idx
)) { // starting IPV6 explicit address
1433 // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end
1434 for ( ; idx
< extent
; ++ idx
) {
1435 if ( ']' == STRING_CHAR(idx
)) {
1436 flags
|= IS_IPV6_ENCODED
;
1441 // there is a port if we see a colon. Only the last one is the port, though.
1442 else if ( ':' == STRING_CHAR(idx
)) {
1445 ranges
[4].location
= idx
+1; // base of port
1446 ranges
[4].length
= extent
- (idx
+1); // port extent
1447 ranges
[3].length
= idx
- base
; // host extent
1451 if (!(flags
& HAS_PORT
)) {
1452 ranges
[3].length
= extent
- base
; // host extent
1458 // 4: parse the query; remainder after left-most "?" is query
1459 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1460 if ('?' == STRING_CHAR(idx
)) {
1463 ranges
[7].location
= idx
+ 1;
1464 ranges
[7].length
= string_length
- (idx
+1);
1465 string_length
= idx
; // remove query from parse string
1470 // 5: parse the parameters; remainder after left-most ";" is parameters
1471 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1472 if (';' == STRING_CHAR(idx
)) {
1473 flags
|= HAS_PARAMETERS
;
1475 ranges
[6].location
= idx
+ 1;
1476 ranges
[6].length
= string_length
- (idx
+1);
1477 string_length
= idx
; // remove parameters from parse string
1482 // 6: parse the path; it's whatever's left between string_length & base_idx
1483 if (string_length
- base_idx
!= 0 || (flags
& NET_LOCATION_MASK
))
1485 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1491 pathRg
.location
= base_idx
;
1492 pathRg
.length
= string_length
- base_idx
;
1495 if (pathRg
.length
> 0) {
1496 Boolean sawPercent
= FALSE
;
1497 for (idx
= pathRg
.location
; idx
< string_length
; idx
++) {
1498 if ('%' == STRING_CHAR(idx
)) {
1503 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
1504 if (pathRg
.length
> 6 && STRING_CHAR(pathRg
.location
) == '/' && STRING_CHAR(pathRg
.location
+ 1) == '.' && STRING_CHAR(pathRg
.location
+ 2) == 'f' && STRING_CHAR(pathRg
.location
+ 3) == 'i' && STRING_CHAR(pathRg
.location
+ 4) == 'l' && STRING_CHAR(pathRg
.location
+ 5) == 'e' && STRING_CHAR(pathRg
.location
+ 6) == '/') {
1505 flags
|= PATH_HAS_FILE_ID
;
1506 } else if (!sawPercent
) {
1507 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1509 #elif DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_WINDOWS
1511 flags
|= POSIX_AND_URL_PATHS_MATCH
;
1515 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 1);
1518 } else if (ch
== '.') {
1519 if (pathRg
.length
== 1) {
1522 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 2);
1525 } else if (ch
!= '.') {
1527 } else if (pathRg
.length
== 2) {
1530 isDir
= (STRING_CHAR(pathRg
.location
+ pathRg
.length
- 3) == '/');
1537 isDir
= (baseURL
!= NULL
) ? CFURLHasDirectoryPath(baseURL
) : false;
1540 flags
|= IS_DIRECTORY
;
1544 if (freeCharacters
) {
1545 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1547 (*theFlags
) = flags
;
1548 (*range
) = (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
)*numRanges
, 0);
1550 for (idx
= 0, flags
= 1; flags
!= (1<<9); flags
= (flags
<<1), idx
++) {
1551 if ((*theFlags
) & flags
) {
1552 (*range
)[numRanges
] = ranges
[idx
];
1558 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
) {
1560 Boolean sawIllegalChar
= false;
1561 for (idx
= base
; idx
< end
; idx
++) {
1562 Boolean shouldEscape
;
1563 UniChar ch
= STRING_CHAR(idx
);
1564 if (isURLLegalCharacter(ch
)) {
1565 if ((componentFlag
== HAS_USER
|| componentFlag
== HAS_PASSWORD
) && (ch
== '/' || ch
== '?' || ch
== '@')) {
1566 shouldEscape
= true;
1568 shouldEscape
= false;
1570 } else if (ch
== '%' && idx
+ 2 < end
&& isHexDigit(STRING_CHAR(idx
+ 1)) && isHexDigit(STRING_CHAR(idx
+2))) {
1571 shouldEscape
= false;
1572 } else if (componentFlag
== HAS_HOST
&& ((idx
== base
&& ch
== '[') || (idx
== end
-1 && ch
== ']'))) {
1573 shouldEscape
= false;
1575 shouldEscape
= true;
1578 sawIllegalChar
= true;
1579 if (componentFlag
&& flags
) {
1580 *flags
|= componentFlag
;
1582 if (!*escapedString
) {
1583 *escapedString
= CFStringCreateMutable(alloc
, 0);
1586 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[*mark
]), idx
- *mark
, kCFStringEncodingISOLatin1
, false);
1587 CFStringAppend(*escapedString
, tempString
);
1588 CFRelease(tempString
);
1590 CFStringAppendCharacters(*escapedString
, &(ustring
[*mark
]), idx
- *mark
);
1593 _appendPercentEscapesForCharacter(ch
, encoding
, *escapedString
); // This can never fail because anURL->_string was constructed from the encoding passed in
1596 return sawIllegalChar
;
1599 static void computeSanitizedString(CFURLRef url
) {
1600 CFAllocatorRef alloc
= CFGetAllocator(url
);
1601 CFIndex string_length
= CFStringGetLength(url
->_string
);
1602 Boolean useCString
, freeCharacters
;
1603 const char *cstring
= NULL
;
1604 const UniChar
*ustring
= NULL
;
1605 CFIndex base
; // where to scan from
1606 CFIndex mark
; // first character not-yet copied to sanitized string
1607 CFIndex stackBufferSize
= 4096;
1608 STACK_BUFFER_DECL(UInt8
, stackBuffer
, stackBufferSize
);
1609 CFMutableStringRef sanitizedString
= NULL
;
1610 UInt32 additionalDataFlags
= 0;
1611 Boolean useEightBitStringEncoding
= (url
->_flags
& USES_EIGHTBITSTRINGENCODING
) != 0;
1613 constructBuffers(alloc
, url
->_string
, useEightBitStringEncoding
, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1614 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1615 // Impossible to have a problem character in the scheme
1616 base
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
).length
+ 1;
1618 if (!scanCharacters(alloc
, &sanitizedString
, &additionalDataFlags
, cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1619 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1621 if ( sanitizedString
) {
1622 _setAdditionalDataFlags((struct __CFURL
*)url
, additionalDataFlags
);
1625 // Go component by component
1626 CFIndex currentComponent
= HAS_USER
;
1628 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1629 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->_ranges
, currentComponent
);
1630 if (componentRange
.location
!= kCFNotFound
) {
1631 scanCharacters(alloc
, & sanitizedString
, &additionalDataFlags
, cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1633 currentComponent
= currentComponent
<< 1;
1635 if (sanitizedString
) {
1636 _setAdditionalDataFlags((struct __CFURL
*)url
, additionalDataFlags
);
1638 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1641 if (sanitizedString
&& mark
!= string_length
) {
1643 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1644 CFStringAppend(sanitizedString
, tempString
);
1645 CFRelease(tempString
);
1647 CFStringAppendCharacters(sanitizedString
, &(ustring
[mark
]), string_length
- mark
);
1650 if ( sanitizedString
) {
1651 _setSanitizedString((struct __CFURL
*) url
, sanitizedString
);
1652 CFRelease(sanitizedString
);
1654 if (freeCharacters
) {
1655 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1660 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1661 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1662 CFIndex string_length
= CFStringGetLength(comp
);
1663 Boolean useCString
, freeCharacters
;
1664 const char *cstring
= NULL
;
1665 const UniChar
*ustring
= NULL
;
1666 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1667 CFMutableStringRef result
= NULL
;
1668 CFIndex stackBufferSize
= 1024;
1669 STACK_BUFFER_DECL(UInt8
, stackBuffer
, stackBufferSize
);
1671 constructBuffers(alloc
, comp
, false, stackBuffer
, stackBufferSize
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1672 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1674 if (mark
< string_length
) {
1676 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, (uint8_t *)&(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1677 CFStringAppend(result
, tempString
);
1678 CFRelease(tempString
);
1680 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1684 // This should nevr happen
1686 result
= (CFMutableStringRef
)comp
;
1688 if (freeCharacters
) {
1689 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1695 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1696 struct __CFURL
*url
;
1697 #if DEBUG_URL_MEMORY_USAGE
1700 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1703 url
->_encoding
= kCFStringEncodingUTF8
;
1704 url
->_string
= NULL
;
1706 url
->_ranges
= NULL
;
1708 url
->_resourceInfo
= NULL
;
1713 // It is the caller's responsibility to guarantee that if URLString is absolute, base is NULL. This is necessary to avoid duplicate processing for file system URLs, which had to decide whether to compute the cwd for the base; we don't want to duplicate that work. This ALSO means it's the caller's responsibility to set the IS_ABSOLUTE bit, since we may have a degenerate URL whose string is relative, but lacks a base.
1714 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, UInt32 fsType
, CFURLRef base
) {
1715 CFAssert2((fsType
== FULL_URL_REPRESENTATION
) || (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) || (fsType
== kCFURLHFSPathStyle
), __kCFLogAssertion
, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__
, fsType
);
1717 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1718 url
->_string
= CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1719 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1721 #if DEBUG_URL_MEMORY_USAGE
1722 if ( (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLHFSPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) ) {
1723 numFileURLsCreated
++;
1726 numURLsWithBaseURL
++;
1729 if (fsType
!= FULL_URL_REPRESENTATION
) {
1730 // _convertToURLRepresentation parses the URL
1731 _convertToURLRepresentation((struct __CFURL
*)url
, fsType
);
1734 _parseComponentsOfURL(url
);
1738 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
1739 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1740 CFIndex len
= CFStringGetLength(path
);
1741 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1742 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1743 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1745 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1746 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1750 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1751 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1753 #elif DEPLOYMENT_TARGET_WINDOWS
1754 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1755 CFIndex len
= CFStringGetLength(path
);
1756 // be sure to use the windows path separator when checking the path to see if it's a directory here
1757 if (!len
|| '\\' == CFStringGetCharacterAtIndex(path
, len
- 1))
1758 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1759 UniChar firstChar
= 0 < len
? CFStringGetCharacterAtIndex(path
, 0) : 0;
1760 UniChar secondChar
= 1 < len
? CFStringGetCharacterAtIndex(path
, 1) : 0;
1761 Boolean isDrive
= ('A' <= firstChar
&& firstChar
<= 'Z') || ('a' <= firstChar
&& firstChar
<= 'z');
1762 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1763 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1764 isDrive
= isDrive
&& (secondChar
== ':' || secondChar
== '|');
1765 if (isDrive
|| (firstChar
== '\\' && secondChar
== '\\')) {
1766 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLWindowsPathStyle
, NULL
);
1767 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1768 } else if (firstChar
== '/') {
1769 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1770 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1771 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1772 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1774 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1775 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1782 // Exported for Foundation's use
1783 CF_EXPORT Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1784 // Check each character to make sure it is a legal URL char. The valid characters are 'A'-'Z', 'a' - 'z', '0' - '9', plus the characters in "-_.!~*'()", and the set of reserved characters (these characters have special meanings in the URL syntax), which are ";/?:@&=+$,". In addition, percent escape sequences '%' hex-digit hex-digit are permitted.
1785 // Plus the hash character '#' which denotes the beginning of a fragment, and can appear exactly once in the entire URL string. -- REW, 12/13/2000
1786 CFStringInlineBuffer stringBuffer
;
1787 CFIndex idx
= 0, length
;
1788 Boolean sawHash
= false;
1790 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1793 length
= CFStringGetLength(string
);
1794 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1795 while (idx
< length
) {
1796 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1799 // Make sure that two valid hex digits follow a '%' character
1801 if ( idx
+ 2 > length
)
1803 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1804 idx
= -1; // To guarantee index < length, and our failure case is triggered
1808 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1810 if (! isHexDigit(ch
) ) {
1811 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1815 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1817 if (! isHexDigit(ch
) ) {
1818 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1825 if (ch
== '[' || ch
== ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1831 #if DEPLOYMENT_TARGET_WINDOWS
1832 // <rdar://problem/7134119> CF on Windows: CFURLCreateWithString should work with | in path on Windows
1833 if (isURLLegalCharacter(ch
) || ch
== '|')
1835 if ( isURLLegalCharacter( ch
) )
1846 CF_EXPORT
void _CFURLInitWithString(CFURLRef myURL
, CFStringRef string
, CFURLRef baseURL
) {
1847 struct __CFURL
*url
= (struct __CFURL
*)myURL
; // Supress annoying compile warnings
1848 Boolean isAbsolute
= false;
1849 CFRange colon
= CFStringFind(string
, CFSTR(":"), 0);
1850 if (colon
.location
!= kCFNotFound
) {
1853 for (i
= 0; i
< colon
.location
; i
++) {
1854 char ch
= (char)CFStringGetCharacterAtIndex(string
, i
);
1855 if (!scheme_valid(ch
)) {
1861 _CFURLInit(url
, string
, FULL_URL_REPRESENTATION
, isAbsolute
? NULL
: baseURL
);
1863 url
->_flags
|= IS_ABSOLUTE
;
1867 struct __CFURLEncodingTranslationParameters
{
1868 CFStringEncoding fromEnc
;
1869 CFStringEncoding toEnc
;
1870 const UniChar
*addlChars
;
1872 Boolean escapeHighBit
;
1873 Boolean escapePercents
;
1874 Boolean agreesOverASCII
;
1875 Boolean encodingsMatch
;
1878 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1879 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
1880 CFStringRef urlString
;
1881 Boolean useEightBitStringEncoding
= ( __CFStringEncodingIsSupersetOfASCII(encoding
) && __CFBytesInASCII(URLBytes
, length
) );
1882 urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, useEightBitStringEncoding
? __CFStringGetEightBitStringEncoding() : encoding
, false);
1884 if (!urlString
|| CFStringGetLength(urlString
) == 0) {
1885 if (urlString
) CFRelease(urlString
);
1888 result
= _CFURLAlloc(allocator
);
1890 if ( useEightBitStringEncoding
) {
1891 ((struct __CFURL
*)result
)->_flags
|= USES_EIGHTBITSTRINGENCODING
;
1893 _CFURLInitWithString(result
, urlString
, baseURL
);
1894 if (encoding
!= kCFStringEncodingUTF8
) {
1895 ((struct __CFURL
*)result
)->_encoding
= encoding
;
1896 #if DEBUG_URL_MEMORY_USAGE
1897 numNonUTF8EncodedURLs
++;
1901 CFRelease(urlString
); // it's retained by result, now.
1905 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
1906 CFDataRef result
= NULL
;
1908 CFStringRef myStr
= CFURLGetString(url
);
1910 result
= CFStringCreateExternalRepresentation(allocator
, myStr
, encoding
, 0);
1916 // Any escape sequences in URLString will be interpreted via UTF-8.
1917 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1919 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1920 if (!_CFStringIsLegalURLString(URLString
)) return NULL
;
1921 url
= _CFURLAlloc(allocator
);
1923 _CFURLInitWithString(url
, URLString
, baseURL
);
1928 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1930 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1931 url
= _CFURLAlloc(allocator
);
1933 _CFURLInitWithString(url
, URLString
, baseURL
);
1938 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
1939 CFURLRef result
= NULL
;
1941 // if not useCompatibilityMode, use CFURLCreateWithBytes and then CFURLCopyAbsoluteURL if there's a baseURL
1942 if ( !useCompatibilityMode
) {
1943 CFURLRef url
= CFURLCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, baseURL
);
1944 if ( url
!= NULL
) {
1945 if ( baseURL
!= NULL
) {
1946 result
= CFURLCopyAbsoluteURL(url
);
1953 UInt32 absFlags
= 0;
1955 CFStringRef absString
= NULL
;
1956 Boolean absStringIsMutable
= false;
1958 CFStringRef relativeString
;
1959 Boolean useEightBitStringEncoding
;
1961 useEightBitStringEncoding
= ( __CFStringEncodingIsSupersetOfASCII(encoding
) && __CFBytesInASCII(relativeURLBytes
, length
) );
1962 relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, useEightBitStringEncoding
? __CFStringGetEightBitStringEncoding() : encoding
, false);
1963 if ( relativeString
!= NULL
) {
1965 if ( useEightBitStringEncoding
) {
1966 absFlags
|= USES_EIGHTBITSTRINGENCODING
;
1968 absString
= relativeString
;
1970 UniChar ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
1971 if (ch
== '?' || ch
== ';' || ch
== '#') {
1972 // Nothing but parameter + query + fragment; append to the baseURL string
1973 CFStringRef baseString
;
1974 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1975 baseString
= CFURLGetString(baseURL
);
1977 baseString
= baseURL
->_string
;
1979 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
1980 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
1981 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
1982 absStringIsMutable
= true;
1984 UInt32 relFlags
= 0;
1986 CFStringRef relString
= NULL
;
1987 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
1988 if (relFlags
& HAS_SCHEME
) {
1989 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
1990 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
1991 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
1992 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
1993 CFAllocatorDeallocate(alloc
, relRanges
);
1995 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
1997 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
1998 if ( useEightBitStringEncoding
) {
1999 absFlags
|= USES_EIGHTBITSTRINGENCODING
;
2001 CFRetain(relativeString
);
2002 absString
= relativeString
;
2004 if (baseScheme
) CFRelease(baseScheme
);
2006 CFRetain(relativeString
);
2007 relString
= relativeString
;
2010 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2011 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->_ranges
);
2013 CFStringRef baseString
;
2014 UInt32 baseFlags
= 0;
2015 CFRange
*baseRanges
;
2016 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
2017 baseString
= CFURLGetString(baseURL
);
2019 baseString
= baseURL
->_string
;
2021 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2022 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
2023 CFAllocatorDeallocate(alloc
, baseRanges
);
2025 absStringIsMutable
= true;
2027 if (relString
) CFRelease(relString
);
2028 CFAllocatorDeallocate(alloc
, relRanges
);
2030 CFRelease(relativeString
);
2034 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
2035 if (absFlags
& HAS_PATH
) {
2036 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
2037 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
2038 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), 0);
2039 CFStringRef newPath
;
2040 CFStringGetCharacters(absString
, pathRg
, buf
);
2041 buf
[pathRg
.length
] = '\0';
2042 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
2043 if (CFStringGetLength(newPath
) != pathRg
.length
) {
2044 if (!absStringIsMutable
) {
2045 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
2046 CFRelease(absString
);
2049 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
2052 // Do not deallocate buf; newPath took ownership of it.
2054 CFAllocatorDeallocate(alloc
, absRanges
);
2055 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
2056 CFRelease(absString
);
2058 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
2059 #if DEBUG_URL_MEMORY_USAGE
2060 if ( encoding
!= kCFStringEncodingUTF8
) {
2061 numNonUTF8EncodedURLs
++;
2072 /* 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 */
2073 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
2074 UniChar
*idx
= pathStr
;
2078 if (idx
!= pathStr
) {
2083 } else if (*(idx
+1) == pathDelimiter
) {
2084 if (idx
+ 2 != end
|| idx
!= pathStr
) {
2085 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
2089 // Do not delete the sole path component
2092 } else if (( end
-idx
>= 2 ) && *(idx
+1) == '.' && (idx
+2 == end
|| (( end
-idx
> 2 ) && *(idx
+2) == pathDelimiter
))) {
2093 if (idx
- pathStr
>= 2) {
2094 // 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.
2095 UniChar
*lastDelim
= idx
-2;
2096 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
2098 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
2099 // We have a genuine component to compact out
2101 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
2102 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2103 end
-= (idx
+ 3 - lastDelim
);
2106 } else if (lastDelim
!= pathStr
) {
2111 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
2119 } else if (stripLeadingDotDots
) {
2120 if (idx
+ 3 != end
) {
2121 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
2122 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
2126 // Do not devolve the last path component
2132 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
2135 if (stripTrailingDelimiter
&& end
> pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
2138 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
- pathStr
, alloc
);
2141 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
2142 CFMutableStringRef newString
= CFStringCreateMutable(alloc
, 0);
2143 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
2144 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
2147 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
2148 if (rg
.location
!= kCFNotFound
) {
2149 CFStringGetCharacters(baseString
, rg
, buf
);
2150 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2151 CFStringAppendCString(newString
, ":", kCFStringEncodingASCII
);
2154 if (relFlags
& NET_LOCATION_MASK
) {
2155 CFStringAppend(newString
, relString
);
2157 CFStringAppendCString(newString
, "//", kCFStringEncodingASCII
);
2158 rg
= _netLocationRange(baseFlags
, baseRanges
);
2159 if (rg
.location
!= kCFNotFound
) {
2160 CFStringGetCharacters(baseString
, rg
, buf
);
2161 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2164 if (relFlags
& HAS_PATH
) {
2165 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
2166 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2167 CFStringRef newPath
;
2168 Boolean useRelPath
= false;
2169 Boolean useBasePath
= false;
2170 if (basePathRg
.location
== kCFNotFound
) {
2172 } else if (relPathRg
.length
== 0) {
2174 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
2176 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
2180 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
2181 } else if (useBasePath
) {
2182 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
2184 // #warning FIXME - Get rid of this allocation
2185 UniChar
*newPathBuf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
2187 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
2188 idx
= newPathBuf
+ basePathRg
.length
- 1;
2189 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
2190 if (*idx
== '/') idx
++;
2191 CFStringGetCharacters(relString
, relPathRg
, idx
);
2192 end
= idx
+ relPathRg
.length
;
2194 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
2196 /* Under Win32 absolute path can begin with letter
2197 * so we have to add one '/' to the newString
2200 // No - the input strings here are URL path strings, not Win32 paths.
2201 // Absolute paths should have had a '/' prepended before this point.
2202 // I have removed Sergey Zubarev's change and left his comment (and
2203 // this one) as a record. - REW, 1/5/2004
2205 // if the relative URL does not begin with a slash and
2206 // the base does not end with a slash, add a slash
2207 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2208 CFStringAppendCString(newString
, "/", kCFStringEncodingASCII
);
2211 CFStringAppend(newString
, newPath
);
2213 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2214 rg
.length
= CFStringGetLength(relString
);
2215 if (rg
.length
> rg
.location
) {
2216 rg
.length
-= rg
.location
;
2217 CFStringGetCharacters(relString
, rg
, buf
);
2218 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2221 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2222 if (rg
.location
!= kCFNotFound
) {
2223 CFStringGetCharacters(baseString
, rg
, buf
);
2224 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2227 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2228 // ??? Can this ever happen?
2229 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2231 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2232 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2233 rg
.location
--; // To pick up the separator
2235 CFStringGetCharacters(baseString
, rg
, buf
);
2236 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2238 } else if (relFlags
& HAS_PARAMETERS
) {
2239 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2240 rg
.location
--; // To get the semicolon that starts the parameters
2241 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2242 CFStringGetCharacters(relString
, rg
, buf
);
2243 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2245 // Sigh; we have to resolve these against one another
2246 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2247 if (rg
.location
!= kCFNotFound
) {
2248 CFStringAppendCString(newString
, ";", kCFStringEncodingASCII
);
2249 CFStringGetCharacters(baseString
, rg
, buf
);
2250 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2252 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2253 if (rg
.location
!= kCFNotFound
) {
2254 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2255 CFStringGetCharacters(relString
, rg
, buf
);
2256 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2258 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2259 if (rg
.location
!= kCFNotFound
) {
2260 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2261 CFStringGetCharacters(baseString
, rg
, buf
);
2262 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2265 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2266 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2267 if (rg
.location
!= kCFNotFound
) {
2268 CFStringAppendCString(newString
, "#", kCFStringEncodingASCII
);
2269 CFStringGetCharacters(relString
, rg
, buf
);
2270 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2275 CFAllocatorDeallocate(alloc
, buf
);
2279 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2280 CFURLRef anURL
, base
;
2281 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2282 CFStringRef baseString
, newString
;
2284 CFRange
*baseRanges
;
2287 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2288 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2289 anURL
= (CFURLRef
) CF_OBJC_CALLV((NSURL
*)relativeURL
, absoluteURL
);
2290 if (anURL
) CFRetain(anURL
);
2294 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2296 base
= relativeURL
->_base
;
2298 return (CFURLRef
)CFRetain(relativeURL
);
2300 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2303 baseString
= base
->_string
;
2304 baseFlags
= base
->_flags
;
2305 baseRanges
= base
->_ranges
;
2307 baseString
= CFURLGetString(base
);
2310 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2313 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->_ranges
, baseString
, baseFlags
, baseRanges
);
2315 CFAllocatorDeallocate(alloc
, baseRanges
);
2317 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2318 CFRelease(newString
);
2319 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2320 #if DEBUG_URL_MEMORY_USAGE
2321 if ( relativeURL
->_encoding
!= kCFStringEncodingUTF8
) {
2322 numNonUTF8EncodedURLs
++;
2329 /*******************/
2330 /* Basic accessors */
2331 /*******************/
2332 CFStringEncoding
_CFURLGetEncoding(CFURLRef url
) {
2333 return url
->_encoding
;
2336 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2337 anURL
= _CFURLFromNSURL(anURL
);
2338 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2341 CFStringRef
CFURLGetString(CFURLRef url
) {
2342 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID
, CFStringRef
, (NSURL
*)url
, relativeString
);
2343 if (!_haveTestedOriginalString(url
)) {
2344 computeSanitizedString(url
);
2346 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2347 return url
->_string
;
2349 return _getSanitizedString( url
);
2353 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2354 CFIndex length
, charsConverted
, usedLength
;
2356 CFStringEncoding enc
;
2357 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2358 string
= CFURLGetString(url
);
2359 enc
= kCFStringEncodingUTF8
;
2361 string
= url
->_string
;
2362 enc
= url
->_encoding
;
2364 length
= CFStringGetLength(string
);
2365 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2366 if (charsConverted
!= length
) {
2373 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2374 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID
, CFURLRef
, (NSURL
*)anURL
, baseURL
);
2375 return anURL
->_base
;
2378 // Assumes the URL is already parsed
2379 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2381 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2382 while (!(compFlag
& 1)) {
2383 compFlag
= compFlag
>> 1;
2392 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2395 CFAllocatorRef alloc
= CFGetAllocator(url
);
2396 if (removePercentEscapes
) {
2397 fromOriginalString
= true;
2399 rg
= _rangeForComponent(url
->_flags
, url
->_ranges
, compFlag
);
2400 if (rg
.location
== kCFNotFound
) {
2404 if ( compFlag
& HAS_SCHEME
) {
2405 switch ( _getSchemeTypeFromFlags(url
->_flags
) ) {
2406 case kHasHttpScheme
:
2407 comp
= (CFStringRef
)CFRetain(kCFURLHTTPScheme
);
2410 case kHasHttpsScheme
:
2411 comp
= (CFStringRef
)CFRetain(kCFURLHTTPSScheme
);
2414 case kHasFileScheme
:
2415 comp
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
2418 case kHasDataScheme
:
2419 comp
= (CFStringRef
)CFRetain(kCFURLDataScheme
);
2423 comp
= (CFStringRef
)CFRetain(kCFURLFTPScheme
);
2427 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2432 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2435 if (!fromOriginalString
) {
2436 if (!_haveTestedOriginalString(url
)) {
2437 computeSanitizedString(url
);
2439 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (_getAdditionalDataFlags(url
) & compFlag
)) {
2440 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2445 if (removePercentEscapes
) {
2447 if (url
->_encoding
== kCFStringEncodingUTF8
) {
2448 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2450 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2460 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2462 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2463 scheme
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, scheme
);
2469 switch ( _getSchemeTypeFromFlags(anURL
->_flags
) ) {
2470 case kHasHttpScheme
:
2471 scheme
= (CFStringRef
)CFRetain(kCFURLHTTPScheme
);
2474 case kHasHttpsScheme
:
2475 scheme
= (CFStringRef
)CFRetain(kCFURLHTTPSScheme
);
2478 case kHasFileScheme
:
2479 scheme
= (CFStringRef
)CFRetain(kCFURLFileScheme
);
2482 case kHasDataScheme
:
2483 scheme
= (CFStringRef
)CFRetain(kCFURLDataScheme
);
2487 scheme
= (CFStringRef
)CFRetain(kCFURLFTPScheme
);
2491 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2494 scheme
= CFURLCopyScheme(anURL
->_base
);
2505 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2507 CFRange netRg
= {kCFNotFound
, 0};
2510 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2512 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2513 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2514 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2515 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2516 for (i
= 0; i
< c
; i
++) {
2517 if (netRgs
[i
].location
== kCFNotFound
) continue;
2518 if (netRg
.location
== kCFNotFound
) {
2521 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2527 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2528 anURL
= _CFURLFromNSURL(anURL
);
2529 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2530 // We provide the net location
2531 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->_ranges
);
2533 if (!_haveTestedOriginalString(anURL
)) {
2534 computeSanitizedString(anURL
);
2536 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (_getAdditionalDataFlags(anURL
) & (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2537 // 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.
2539 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
2540 netRg
.length
= CFStringGetLength(sanitizedString
) - netRg
.location
;
2541 if (CFStringFindWithOptions(sanitizedString
, CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2542 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2544 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), sanitizedString
, netRg
);
2546 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2549 } else if (anURL
->_base
) {
2550 return CFURLCopyNetLocation(anURL
->_base
);
2556 // 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.
2557 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
2558 anURL
= _CFURLFromNSURL(anURL
);
2559 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
2562 /* 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.
2564 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.
2566 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
2567 CFStringRef path
= CFURLCopyPath(anURL
);
2568 if (!path
|| CFStringGetLength(path
) == 0) {
2569 if (path
) CFRelease(path
);
2570 if (isAbsolute
) *isAbsolute
= false;
2573 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
2575 if (isAbsolute
) *isAbsolute
= true;
2576 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
2580 if (isAbsolute
) *isAbsolute
= false;
2585 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
2586 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2587 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
2588 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2591 return CFURLHasDirectoryPath(anURL
->_base
);
2595 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
2596 UInt32 firstRsrcSpecFlag
= 0;
2597 UInt32 flag
= HAS_FRAGMENT
;
2598 while (flag
!= HAS_PATH
) {
2600 firstRsrcSpecFlag
= flag
;
2604 return firstRsrcSpecFlag
;
2607 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
2608 anURL
= _CFURLFromNSURL(anURL
);
2609 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2610 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
2611 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->_ranges
, HAS_SCHEME
);
2612 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
2613 if (!_haveTestedOriginalString(anURL
)) {
2614 computeSanitizedString(anURL
);
2617 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
2619 if (sanitizedString
) {
2620 // 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.
2621 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), sanitizedString
, CFRangeMake(base
, CFStringGetLength(sanitizedString
)-base
));
2623 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
2626 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
2628 if (firstRsrcSpecFlag
) {
2629 Boolean canUseOriginalString
= true;
2630 Boolean canUseSanitizedString
= true;
2631 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
2632 if (!_haveTestedOriginalString(anURL
)) {
2633 computeSanitizedString(anURL
);
2636 UInt32 additionalDataFlags
= _getAdditionalDataFlags(anURL
);
2637 CFStringRef sanitizedString
= _getSanitizedString(anURL
);
2639 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
2640 // See if any pieces in the resource specifier differ between sanitized string and original string
2641 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
2642 if (additionalDataFlags
& flag
) {
2643 canUseOriginalString
= false;
2648 if (!canUseOriginalString
) {
2649 // 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.
2650 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
2651 if (additionalDataFlags
& flag
) {
2652 canUseSanitizedString
= false;
2657 if (canUseOriginalString
) {
2658 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->_ranges
, firstRsrcSpecFlag
);
2659 rg
.location
--; // Include the character that demarcates the component
2660 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
2661 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
2662 } else if (canUseSanitizedString
) {
2663 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->_ranges
, firstRsrcSpecFlag
);
2664 rg
.location
--; // Include the character that demarcates the component
2665 rg
.length
= CFStringGetLength(sanitizedString
) - rg
.location
;
2666 return CFStringCreateWithSubstring(alloc
, sanitizedString
, rg
);
2668 // Must compute the correct string to return; just reparse....
2669 UInt32 sanFlags
= 0;
2670 CFRange
*sanRanges
= NULL
;
2672 _parseComponents(alloc
, sanitizedString
, anURL
->_base
, &sanFlags
, &sanRanges
);
2673 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
2674 CFAllocatorDeallocate(alloc
, sanRanges
);
2675 rg
.location
--; // Include the character that demarcates the component
2676 rg
.length
= CFStringGetLength(sanitizedString
) - rg
.location
;
2677 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), sanitizedString
, rg
);
2680 // The resource specifier cannot possibly come from the base.
2686 /*************************************/
2687 /* Accessors that create new objects */
2688 /*************************************/
2690 // 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).
2691 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
2693 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2694 tmp
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, host
);
2695 if (tmp
) CFRetain(tmp
);
2698 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2699 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
2701 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
2702 // Have to strip off the brackets to get the true hostname.
2703 // Assume that to be legal the first and last characters are brackets!
2704 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
2709 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2710 return CFURLCopyHostName(anURL
->_base
);
2716 // Return -1 to indicate no port is specified
2717 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
2719 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2720 CFNumberRef cfPort
= (CFNumberRef
) CF_OBJC_CALLV((NSURL
*)anURL
, port
);
2722 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
2725 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2726 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
2728 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
2729 CFStringInlineBuffer buf
;
2730 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
2732 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
2737 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2738 return CFURLGetPortNumber(anURL
->_base
);
2744 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
2746 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2747 user
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, user
);
2748 if (user
) CFRetain(user
);
2751 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2752 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
2755 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2756 return CFURLCopyUserName(anURL
->_base
);
2762 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
2764 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2765 passwd
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, password
);
2766 if (passwd
) CFRetain(passwd
);
2769 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2770 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
2773 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2774 return CFURLCopyPassword(anURL
->_base
);
2780 // 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
2782 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
2784 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2785 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, parameterString
);
2786 if (str
) CFRetain(str
);
2789 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2790 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
2791 if (str
) return str
;
2792 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2793 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
2795 // Parameter string definitely coming from the relative portion of the URL
2797 return _unescapedParameterString( anURL
->_base
);
2800 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2801 CFStringRef param
= _unescapedParameterString(anURL
);
2804 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
2805 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
2807 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
2815 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
2817 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2818 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, query
);
2819 if (str
) CFRetain(str
);
2822 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2823 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
2824 if (str
) return str
;
2825 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2826 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
2829 return _unescapedQueryString(anURL
->_base
);
2832 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2833 CFStringRef query
= _unescapedQueryString(anURL
);
2836 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
2837 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
2839 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
2847 // Fragments are NEVER taken from a base URL
2848 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
2850 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2851 str
= (CFStringRef
) CF_OBJC_CALLV((NSURL
*)anURL
, fragment
);
2852 if (str
) CFRetain(str
);
2855 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2856 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
2860 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2861 CFStringRef fragment
= _unescapedFragment(anURL
);
2864 if (anURL
->_encoding
== kCFStringEncodingUTF8
) {
2865 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
2867 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
2869 CFRelease(fragment
);
2875 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
2876 CFIndex firstMaskFlag
= 1;
2877 CFIndex lastComponentBeforeMask
= 0;
2878 while (firstMaskFlag
<= HAS_FRAGMENT
) {
2879 if (firstMaskFlag
& mask
) break;
2880 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
2881 firstMaskFlag
= firstMaskFlag
<< 1;
2883 if (lastComponentBeforeMask
== 0) {
2884 // mask includes HAS_SCHEME
2886 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
2887 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
2888 // case file:/path/immediately/without/host
2889 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
);
2890 CFRange pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
2891 if (schemeRg
.length
+ 1 == pathRg
.location
) {
2892 return schemeRg
.length
+ 1;
2894 return schemeRg
.length
+ 3;
2897 // For all other components, the separator precedes the component, so there's no need
2898 // to add extra chars to get to the next insertion point
2899 CFRange rg
= _rangeForComponent(url
->_flags
, url
->_ranges
, lastComponentBeforeMask
);
2900 return rg
.location
+ rg
.length
;
2904 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
2905 CFOptionFlags currentOption
;
2906 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
2907 Boolean haveReachedMask
= false;
2908 CFIndex beforeMask
= 0;
2909 CFIndex afterMask
= kCFNotFound
;
2910 CFRange
*currRange
= url
->_ranges
;
2911 CFRange maskRange
= {kCFNotFound
, 0};
2912 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
2913 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
2914 firstMaskFlag
= currentOption
;
2915 haveReachedMask
= true;
2917 if (!(url
->_flags
& currentOption
)) continue;
2918 if (!haveReachedMask
) {
2919 beforeMask
= currRange
->location
+ currRange
->length
;
2920 } else if (currentOption
<= mask
) {
2921 if (maskRange
.location
== kCFNotFound
) {
2922 maskRange
= *currRange
;
2924 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
2927 afterMask
= currRange
->location
;
2932 if (afterMask
== kCFNotFound
) {
2933 afterMask
= maskRange
.location
+ maskRange
.length
;
2935 charRangeWithSeparators
->location
= beforeMask
;
2936 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
2940 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2942 switch (component
) {
2943 case kCFURLComponentScheme
:
2946 case kCFURLComponentNetLocation
:
2947 mask
= NET_LOCATION_MASK
;
2949 case kCFURLComponentPath
:
2952 case kCFURLComponentResourceSpecifier
:
2953 mask
= RESOURCE_SPECIFIER_MASK
;
2955 case kCFURLComponentUser
:
2958 case kCFURLComponentPassword
:
2959 mask
= HAS_PASSWORD
;
2961 case kCFURLComponentUserInfo
:
2962 mask
= HAS_USER
| HAS_PASSWORD
;
2964 case kCFURLComponentHost
:
2967 case kCFURLComponentPort
:
2970 case kCFURLComponentParameterString
:
2971 mask
= HAS_PARAMETERS
;
2973 case kCFURLComponentQuery
:
2976 case kCFURLComponentFragment
:
2977 mask
= HAS_FRAGMENT
;
2980 rangeIncludingSeparators
->location
= kCFNotFound
;
2981 rangeIncludingSeparators
->length
= 0;
2982 return CFRangeMake(kCFNotFound
, 0);
2985 if ((url
->_flags
& mask
) == 0) {
2986 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
2987 rangeIncludingSeparators
->length
= 0;
2988 return CFRangeMake(kCFNotFound
, 0);
2990 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
2994 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2995 if (component
== kCFURLComponentScheme
) {
2996 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
);
2997 rangeIncludingSeparators
->location
= 0;
2998 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
3000 } else if (component
== kCFURLComponentResourceSpecifier
) {
3001 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_SCHEME
);
3002 CFIndex stringLength
= CFStringGetLength(url
->_string
);
3003 if (schemeRg
.length
+ 1 == stringLength
) {
3004 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
3005 rangeIncludingSeparators
->length
= 0;
3006 return CFRangeMake(kCFNotFound
, 0);
3008 rangeIncludingSeparators
->location
= schemeRg
.length
;
3009 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
3010 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
3013 rangeIncludingSeparators
->location
= kCFNotFound
;
3014 rangeIncludingSeparators
->length
= 0;
3015 return CFRangeMake(kCFNotFound
, 0);
3020 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
3021 CFRange charRange
, charRangeWithSeparators
;
3023 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
3024 url
= _CFURLFromNSURL(url
);
3026 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
3027 // Special-case this because non-decomposable URLs have a slightly strange flags setup
3028 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
3030 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
3033 if (charRangeWithSeparators
.location
== kCFNotFound
) {
3034 if (rangeIncludingSeparators
) {
3035 rangeIncludingSeparators
->location
= kCFNotFound
;
3036 rangeIncludingSeparators
->length
= 0;
3038 return CFRangeMake(kCFNotFound
, 0);
3039 } else if (rangeIncludingSeparators
) {
3040 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
3042 if (charRange
.location
== kCFNotFound
) {
3043 byteRange
= charRange
;
3044 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
3046 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
3047 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
3049 if (charRangeWithSeparators
.location
== charRange
.location
) {
3050 byteRange
.location
= rangeIncludingSeparators
->location
;
3053 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3054 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
3056 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3057 if (maxCharRangeWithSeparators
== maxCharRange
) {
3058 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
3062 rg
.location
= maxCharRange
;
3063 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
3064 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
3065 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
3068 } else if (charRange
.location
== kCFNotFound
) {
3069 byteRange
= charRange
;
3071 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
3072 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
3077 /* Component support */
3079 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
3080 if ( CFURLGetBaseURL(url
) != NULL
) {
3081 components
->scheme
= NULL
;
3083 components
->scheme
= CFURLCopyScheme(url
);
3085 components
->schemeSpecific
= CFURLCopyResourceSpecifier(url
);
3089 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
3091 if (components
->scheme
) {
3093 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
3094 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
3095 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
3096 } else if (components
->schemeSpecific
) {
3097 str
= components
->schemeSpecific
;
3103 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
3111 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
3112 CFAllocatorRef alloc
= CFGetAllocator(url
);
3113 static CFStringRef emptyStr
= NULL
;
3115 emptyStr
= CFSTR("");
3118 if (!CFURLCanBeDecomposed(url
)) {
3122 CFStringRef path
= CFURLCopyPath(url
);
3124 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
3127 components
->pathComponents
= NULL
;
3129 components
->baseURL
= CFURLGetBaseURL(url
);
3130 if (components
->baseURL
) {
3131 CFRetain(components
->baseURL
);
3132 components
->scheme
= NULL
;
3134 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
3136 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
3137 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3138 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3139 if (url
->_flags
& HAS_PORT
) {
3140 components
->port
= CFURLGetPortNumber(url
);
3142 components
->port
= kCFNotFound
;
3144 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3145 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3146 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3150 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3151 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3152 CFURLRef base
= comp
->baseURL
;
3154 Boolean hadPrePathComponent
= false;
3157 CFStringAppend(urlString
, comp
->scheme
);
3158 CFStringAppend(urlString
, CFSTR("://"));
3159 hadPrePathComponent
= true;
3161 if (comp
->user
|| comp
->password
) {
3163 CFStringAppend(urlString
, comp
->user
);
3165 if (comp
->password
) {
3166 CFStringAppend(urlString
, CFSTR(":"));
3167 CFStringAppend(urlString
, comp
->password
);
3169 CFStringAppend(urlString
, CFSTR("@"));
3170 hadPrePathComponent
= true;
3173 CFStringAppend(urlString
, comp
->host
);
3174 hadPrePathComponent
= true;
3176 if (comp
->port
!= kCFNotFound
) {
3177 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3178 hadPrePathComponent
= true;
3181 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFArrayGetCount( comp
->pathComponents
) == 0 || CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3182 CFStringAppend(urlString
, CFSTR("/"));
3184 if (comp
->pathComponents
) {
3185 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3186 CFStringAppend(urlString
, pathStr
);
3189 if (comp
->parameterString
) {
3190 CFStringAppend(urlString
, CFSTR(";"));
3191 CFStringAppend(urlString
, comp
->parameterString
);
3194 CFStringAppend(urlString
, CFSTR("?"));
3195 CFStringAppend(urlString
, comp
->query
);
3197 if (comp
->fragment
) {
3198 CFStringAppend(urlString
, CFSTR("#"));
3199 CFStringAppend(urlString
, comp
->fragment
);
3201 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3202 CFRelease(urlString
);
3206 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3207 CFAllocatorRef alloc
= CFGetAllocator(url
);
3208 CFURLComponentsRFC1808 oldComp
;
3210 if (!decomposeToRFC1808(url
, &oldComp
)) {
3213 comp
->scheme
= oldComp
.scheme
;
3215 if (oldComp
.password
) {
3216 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3217 CFRelease(oldComp
.password
);
3218 CFRelease(oldComp
.user
);
3220 comp
->userinfo
= oldComp
.user
;
3223 comp
->userinfo
= NULL
;
3225 comp
->host
= oldComp
.host
;
3226 comp
->port
= oldComp
.port
;
3227 if (!oldComp
.parameterString
) {
3228 comp
->pathComponents
= oldComp
.pathComponents
;
3230 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3231 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3232 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3233 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3235 CFRelease(oldComp
.pathComponents
);
3236 CFRelease(oldComp
.parameterString
);
3238 comp
->query
= oldComp
.query
;
3239 comp
->fragment
= oldComp
.fragment
;
3240 comp
->baseURL
= oldComp
.baseURL
;
3244 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3245 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3246 CFURLRef base
= comp
->baseURL
;
3248 Boolean hadPrePathComponent
= false;
3251 CFStringAppend(urlString
, comp
->scheme
);
3252 CFStringAppend(urlString
, CFSTR("://"));
3253 hadPrePathComponent
= true;
3255 if (comp
->userinfo
) {
3256 CFStringAppend(urlString
, comp
->userinfo
);
3257 CFStringAppend(urlString
, CFSTR("@"));
3258 hadPrePathComponent
= true;
3261 CFStringAppend(urlString
, comp
->host
);
3262 if (comp
->port
!= kCFNotFound
) {
3263 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3265 hadPrePathComponent
= true;
3267 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3268 CFStringAppend(urlString
, CFSTR("/"));
3270 if (comp
->pathComponents
) {
3271 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3272 CFStringAppend(urlString
, pathStr
);
3276 CFStringAppend(urlString
, CFSTR("?"));
3277 CFStringAppend(urlString
, comp
->query
);
3279 if (comp
->fragment
) {
3280 CFStringAppend(urlString
, CFSTR("#"));
3281 CFStringAppend(urlString
, comp
->fragment
);
3283 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3284 CFRelease(urlString
);
3288 #undef CFURLCopyComponents
3289 #undef CFURLCreateFromComponents
3292 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3293 url
= _CFURLFromNSURL(url
);
3294 switch (decompositionType
) {
3295 case kCFURLComponentDecompositionNonHierarchical
:
3296 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3297 case kCFURLComponentDecompositionRFC1808
:
3298 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3299 case kCFURLComponentDecompositionRFC2396
:
3300 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3307 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3308 switch (decompositionType
) {
3309 case kCFURLComponentDecompositionNonHierarchical
:
3310 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3311 case kCFURLComponentDecompositionRFC1808
:
3312 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3313 case kCFURLComponentDecompositionRFC2396
:
3314 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3320 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3321 return _getReserved(url
);
3324 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3325 _setReserved ( (struct __CFURL
*) url
, ptr
);
3328 CF_EXPORT
void *__CFURLResourceInfoPtr(CFURLRef url
) {
3329 return _getResourceInfo(url
);
3332 CF_EXPORT
void __CFURLSetResourceInfoPtr(CFURLRef url
, void *ptr
) {
3333 _setResourceInfo ( (struct __CFURL
*) url
, ptr
);
3336 /* File system stuff */
3338 /* HFSPath<->URLPath functions at the bottom of the file */
3339 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3341 CFMutableArrayRef urlComponents
= NULL
;
3344 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3345 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3348 CFStringRef str
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, 0);
3349 if (CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3350 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR("")); // So we get a leading '/' below
3351 i
= 2; // Skip over the drive letter and the empty string we just inserted
3354 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3355 CFStringRef fileComp
= (CFStringRef
)CFArrayGetValueAtIndex(urlComponents
,i
);
3356 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3358 // Couldn't decode fileComp
3359 CFRelease(urlComponents
);
3362 if (urlComp
!= fileComp
) {
3363 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3369 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3370 CFArrayAppendValue(urlComponents
, CFSTR(""));
3372 return urlComponents
;
3375 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3376 CFArrayRef urlComponents
;
3379 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3380 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
);
3381 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3383 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3384 str
= CFStringCreateByCombiningStrings(alloc
, urlComponents
, CFSTR("/"));
3385 CFRelease(urlComponents
);
3389 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
) {
3390 CFStringRef pathString
= _replacePathIllegalCharacters(path
, alloc
, true);
3391 if (isDirectory
&& CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
)-1) != '/') {
3392 CFStringRef tmp
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@/"), pathString
);
3393 CFRelease(pathString
);
3399 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3400 // This is the easiest case; just remove the percent escape codes and we're done
3401 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3403 CFIndex length
= CFStringGetLength(result
);
3404 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3405 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3413 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
3414 static Boolean
CanonicalFileURLStringToFileSystemRepresentation(CFStringRef str
, CFAllocatorRef alloc
, UInt8
*inBuffer
, CFIndex inBufferLen
)
3417 if ( inBuffer
&& inBufferLen
) {
3418 STACK_BUFFER_DECL(UInt8
, stackEscapedBuf
, PATH_MAX
* 3); // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3420 CFIndex strLength
= CFStringGetLength(str
) - (sizeof(fileURLPrefixWithAuthority
) - 1);
3421 if ( strLength
!= 0 ) {
3422 CFIndex maxBufLength
= strLength
* 3;
3424 CFIndex charsConverted
;
3425 if ( strLength
<= PATH_MAX
) {
3426 escapedBuf
= &stackEscapedBuf
[0];
3429 // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3430 escapedBuf
= (UInt8
*)malloc(maxBufLength
);
3432 if ( escapedBuf
!= NULL
) {
3433 charsConverted
= CFStringGetBytes(str
, CFRangeMake(sizeof(fileURLPrefixWithAuthority
) - 1, strLength
), kCFStringEncodingUTF8
, 0, false, escapedBuf
, maxBufLength
, &usedBufLen
);
3434 if ( charsConverted
) {
3435 static const UInt8 hexvalues
[] = {
3436 /* 00 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3437 /* 08 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3438 /* 10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3439 /* 18 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3440 /* 20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3441 /* 28 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3442 /* 30 */ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
3443 /* 38 */ 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3444 /* 40 */ 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3445 /* 48 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3446 /* 50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3447 /* 58 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3448 /* 60 */ 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3449 /* 68 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3450 /* 70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3451 /* 78 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3453 /* 80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3454 /* 88 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3455 /* 90 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3456 /* 98 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3457 /* A0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3458 /* A8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3459 /* B0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3460 /* B8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3461 /* C0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3462 /* C8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3463 /* D0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3464 /* D8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3465 /* E0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3466 /* E8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3467 /* F0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3468 /* F8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3473 const UInt8
*bytePtr
= escapedBuf
;
3475 Boolean trailingSlash
= false;
3477 bufPtr
= bufStartPtr
= inBuffer
;
3478 bufEndPtr
= inBuffer
+ inBufferLen
;
3481 for ( idx
= 0; (idx
< usedBufLen
) && result
; ++idx
) {
3482 if ( bufPtr
== bufEndPtr
) {
3483 // ooops, ran out of inBuffer
3484 *bufStartPtr
= '\0';
3488 switch ( *bytePtr
) {
3491 if ( idx
< usedBufLen
) {
3494 // convert hex digits
3495 *bufPtr
= hexvalues
[*bytePtr
++] << 4;
3496 *bufPtr
+= hexvalues
[*bytePtr
++];
3497 trailingSlash
= (*bufPtr
== '/');
3501 // copy everything else
3502 *bufPtr
= *bytePtr
++;
3503 trailingSlash
= (*bufPtr
== '/');
3510 // remove trailing slash (if any)
3511 if ( (bufPtr
> (bufStartPtr
+ 1)) && trailingSlash
) {
3514 if ( bufPtr
< bufEndPtr
) {
3520 // CFStringGetBytes failed
3524 // free the buffer if we malloc'd it
3525 if ( escapedBuf
!= &stackEscapedBuf
[0] ) {
3530 // could not allocate escapedBuf
3535 // str was zero characters
3541 // no inBuffer or inBufferLen is zero
3549 #if DEPLOYMENT_TARGET_WINDOWS
3550 // From CFPlatform.c
3551 extern CFStringRef
CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr
);
3554 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3555 // Check for a drive letter, then flip all the slashes
3557 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
3558 SInt32 count
= CFArrayGetCount(tmp
);
3559 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
3560 CFStringRef newPath
;
3565 if (CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
3566 CFArrayRemoveValueAtIndex(components
, count
-1);
3570 if (count
> 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(components
, 0)) == 0) {
3571 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
3572 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, (CFStringRef
)CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
3576 if (firstComponent
) {
3577 if (CFStringGetLength(firstComponent
) == 2 && ((ch
= CFStringGetCharacterAtIndex(firstComponent
, 1)) == '|' || ch
== ':')) {
3579 CFArrayRemoveValueAtIndex(components
, 0);
3581 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
3582 CFArraySetValueAtIndex(components
, 0, driveStr
);
3583 CFRelease(driveStr
);
3586 #if DEPLOYMENT_TARGET_WINDOWS
3588 // From <rdar://problem/5623405> [DEFECT] CFURL returns a Windows path that contains volume name instead of a drive letter
3589 // we need to replace the volume name (it is not valid on Windows) with the drive mounting point path
3590 // remove the first component and set the component with the drive letter to be the first component
3591 CFStringRef driveRootPath
= CFCreateWindowsDrivePathFromVolumeName(firstComponent
);
3593 if (driveRootPath
) {
3594 // remove trailing slash
3595 if (CFStringHasSuffix(driveRootPath
, CFSTR("\\"))) {
3596 CFStringRef newDriveRootPath
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, driveRootPath
, CFRangeMake(0, CFStringGetLength(driveRootPath
) - 1));
3597 CFRelease(driveRootPath
);
3598 driveRootPath
= newDriveRootPath
;
3601 // replace the first component of the path with the drive path
3602 CFArrayRemoveValueAtIndex(components
, 0);
3603 CFArraySetValueAtIndex(components
, 0, driveRootPath
);
3605 CFRelease(driveRootPath
);
3611 if ( firstComponent
) {
3612 CFRelease(firstComponent
);
3615 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
3616 CFRelease(components
);
3617 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
3624 // converts url from a file system path representation to a standard representation
3625 static void _convertToURLRepresentation(struct __CFURL
*url
, UInt32 fsType
) {
3626 CFStringRef path
= NULL
;
3627 Boolean isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3628 Boolean isFileReferencePath
= false;
3629 CFAllocatorRef alloc
= CFGetAllocator(url
);
3632 case kCFURLPOSIXPathStyle
:
3633 isFileReferencePath
= _pathHasFileIDPrefix(url
->_string
);
3634 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3635 path
= (CFStringRef
)CFRetain(url
->_string
);
3637 path
= POSIXPathToURLPath(url
->_string
, alloc
, isDir
);
3640 case kCFURLWindowsPathStyle
:
3641 path
= WindowsPathToURLPath(url
->_string
, alloc
, isDir
);
3644 CFAssert2(path
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, url
);
3648 CFMutableStringRef str
= CFStringCreateMutable(alloc
, 0);
3649 CFStringAppend(str
, isFileReferencePath
? CFSTR(FILE_PREFIX
) : CFSTR(FILE_PREFIX_WITH_AUTHORITY
));
3650 CFStringAppend(str
, path
);
3651 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | IS_DECOMPOSABLE
| IS_ABSOLUTE
| HAS_SCHEME
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
| ( isFileReferencePath
? PATH_HAS_FILE_ID
: HAS_HOST
);
3652 _setSchemeTypeInFlags(&url
->_flags
, kHasFileScheme
);
3653 CFRelease(url
->_string
);
3654 url
->_string
= CFStringCreateCopy(alloc
, str
);
3656 if (isFileReferencePath
) {
3657 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 2, 0);
3658 url
->_ranges
[0] = CFRangeMake(0, 4); // scheme "file"
3659 url
->_ranges
[1] = CFRangeMake(7, CFStringGetLength(path
)); // path
3662 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
3663 url
->_ranges
[0] = CFRangeMake(0, 4); // scheme "file"
3664 url
->_ranges
[1] = CFRangeMake(7, 9); // host "localhost"
3665 url
->_ranges
[2] = CFRangeMake(16, CFStringGetLength(path
)); // path
3669 CFRelease(url
->_string
);
3670 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | IS_DECOMPOSABLE
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3671 url
->_string
= CFStringCreateCopy(alloc
, path
);
3673 url
->_ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
3674 *(url
->_ranges
) = CFRangeMake(0, CFStringGetLength(url
->_string
));
3679 // Caller must release the returned string
3680 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
3681 CFIndex baseLen
= CFStringGetLength(basePath
);
3682 CFIndex relLen
= CFStringGetLength(relativePath
);
3683 UniChar pathDelimiter
= '/';
3684 UniChar
*buf
= (UniChar
*)CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
3685 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
3687 if (buf
[baseLen
-1] != pathDelimiter
) {
3688 buf
[baseLen
] = pathDelimiter
;
3692 UniChar
*ptr
= buf
+ baseLen
- 1;
3693 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
3696 baseLen
= ptr
- buf
+ 1;
3698 if (fsType
== kCFURLHFSPathStyle
) {
3699 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3702 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
3703 *(buf
+ baseLen
+ relLen
) = '\0';
3704 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
3707 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
3708 CFURLRef url
= NULL
;
3709 uint8_t buf
[CFMaxPathSize
+ 1];
3710 if (_CFGetCurrentDirectory((char *)buf
, CFMaxPathLength
)) {
3711 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen((char *)buf
), true);
3716 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
3717 Boolean isAbsolute
= true;
3719 CFURLRef baseURL
, result
;
3721 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3723 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__
);
3725 len
= CFStringGetLength(filePath
);
3728 case kCFURLPOSIXPathStyle
:
3729 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3731 case kCFURLWindowsPathStyle
:
3732 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3733 /* Absolute path under Win32 can begin with "\\"
3736 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3738 case kCFURLHFSPathStyle
:
3739 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3745 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3747 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
3748 if (baseURL
) CFRelease(baseURL
);
3752 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
3754 Boolean isAbsolute
= true, releaseFilePath
= false, releaseBaseURL
= false;
3755 UniChar pathDelim
= '\0';
3758 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
3759 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3761 len
= CFStringGetLength(filePath
);
3764 case kCFURLPOSIXPathStyle
:
3765 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3769 case kCFURLWindowsPathStyle
:
3770 isAbsolute
= (len
>= 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3771 /* Absolute path under Win32 can begin with "\\"
3775 isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3778 case kCFURLHFSPathStyle
:
3779 { CFRange fullStrRange
= CFRangeMake( 0, CFStringGetLength( filePath
) );
3781 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3784 if ( filePath
&& CFStringFindWithOptions( filePath
, CFSTR("::"), fullStrRange
, 0, NULL
) ) {
3785 UniChar
* chars
= (UniChar
*) malloc( fullStrRange
.length
* sizeof( UniChar
) );
3786 CFIndex index
, writeIndex
, firstColonOffset
= -1;
3788 CFStringGetCharacters( filePath
, fullStrRange
, chars
);
3790 for ( index
= 0, writeIndex
= 0 ; index
< fullStrRange
.length
; index
++ ) {
3791 if ( chars
[ index
] == ':' ) {
3792 if ( index
+ 1 < fullStrRange
.length
&& chars
[ index
+ 1 ] == ':' ) {
3794 // Don't let :: go off the 'top' of the path -- which means that there always has to be at
3795 // least one ':' to the left of the current write position to go back to.
3796 if ( writeIndex
> 0 && firstColonOffset
>= 0 )
3799 while ( writeIndex
> 0 && writeIndex
>= firstColonOffset
&& chars
[ writeIndex
] != ':' )
3802 index
++; // skip over the first ':', so we replace the ':' which is there with a new one
3805 if ( firstColonOffset
== -1 )
3806 firstColonOffset
= writeIndex
;
3809 chars
[ writeIndex
++ ] = chars
[ index
];
3812 if ( releaseFilePath
&& filePath
)
3813 CFRelease( filePath
);
3815 filePath
= CFStringCreateWithCharacters( allocator
, chars
, writeIndex
);
3816 // reset len because a canonical HFS path can be a different length than the original CFString
3817 len
= CFStringGetLength(filePath
);
3818 releaseFilePath
= true;
3829 else if ( baseURL
== NULL
) {
3830 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3831 releaseBaseURL
= true;
3835 if (isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) != pathDelim
) {
3836 CFMutableStringRef tempRef
= CFStringCreateMutable(allocator
, 0);
3837 CFStringAppend(tempRef
, filePath
);
3838 CFStringAppendCharacters(tempRef
, &pathDelim
, 1);
3839 if ( releaseFilePath
&& filePath
) CFRelease( filePath
);
3841 releaseFilePath
= true;
3842 } else if (!isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) == pathDelim
) {
3843 if (len
== 1 || CFStringGetCharacterAtIndex(filePath
, len
-2) == pathDelim
) {
3844 // Override isDirectory
3847 CFStringRef tempRef
= CFStringCreateWithSubstring(allocator
, filePath
, CFRangeMake(0, len
-1));
3848 if ( releaseFilePath
&& filePath
)
3849 CFRelease( filePath
);
3851 releaseFilePath
= true;
3854 if (!filePath
|| CFStringGetLength(filePath
) == 0) {
3855 if (releaseFilePath
&& filePath
) CFRelease(filePath
);
3856 if (releaseBaseURL
&& baseURL
) CFRelease(baseURL
);
3859 url
= _CFURLAlloc(allocator
);
3860 _CFURLInit((struct __CFURL
*)url
, filePath
, fsType
, baseURL
);
3861 if (releaseFilePath
) CFRelease(filePath
);
3862 if (releaseBaseURL
&& baseURL
) CFRelease(baseURL
);
3863 if (isDirectory
) ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
3864 if (fsType
== kCFURLPOSIXPathStyle
) {
3865 // Check if relative path is equivalent to URL representation; this will be true if url->_string contains only characters from the unreserved character set, plus '/' to delimit the path, plus ';', '@', '&', '=', '+', '$', ',' (according to RFC 2396) -- REW, 12/1/2000
3866 // Per Section 5 of RFC 2396, there's a special problem if a colon apears in the first path segment - in this position, it can be mistaken for the scheme name. Otherwise, it's o.k., and can be safely identified as part of the path. In this one case, we need to prepend "./" to make it clear what's going on.... -- REW, 8/24/2001
3867 CFStringInlineBuffer buf
;
3868 Boolean sawSlash
= FALSE
;
3869 Boolean mustPrependDotSlash
= FALSE
;
3870 CFIndex idx
, length
= CFStringGetLength(url
->_string
);
3871 CFStringInitInlineBuffer(url
->_string
, &buf
, CFRangeMake(0, length
));
3872 for (idx
= 0; idx
< length
; idx
++) {
3873 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
3874 if (!isPathLegalCharacter(ch
)) break;
3878 } else if (ch
== ':') {
3879 mustPrependDotSlash
= TRUE
;
3883 if (idx
== length
) {
3884 ((struct __CFURL
*)url
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
3886 if (mustPrependDotSlash
) {
3887 CFMutableStringRef newString
= CFStringCreateMutable(allocator
, 0);
3888 CFStringAppend(newString
, CFSTR("./"));
3889 CFStringAppend(newString
, url
->_string
);
3890 CFRelease(url
->_string
);
3891 ((struct __CFURL
*)url
)->_string
= CFStringCreateCopy(allocator
, newString
);
3892 CFRelease(newString
);
3898 static Boolean
_pathHasFileIDPrefix( CFStringRef path
)
3900 // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
3901 #ifdef __CONSTANT_STRINGS__
3904 CFStringRef fileIDPrefix
= CFSTR( "/" FILE_ID_PREFIX
"/" );
3905 return path
&& CFStringHasPrefix( path
, fileIDPrefix
) && CFStringGetLength( path
) > CFStringGetLength( fileIDPrefix
);
3908 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
3909 static Boolean
_pathHasFileIDOnly( CFStringRef path
)
3911 // Is file ID rooted and contains no additonal path segments
3913 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 );
3917 CF_EXPORT CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
3918 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
3922 result
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL
), anURL
, pathStyle
, false);
3927 // 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
3928 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
3929 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
3930 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
3931 CFStringRef relPath
= NULL
;
3933 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3934 // We can grope the ivars
3935 if (fsType
== kCFURLPOSIXPathStyle
) {
3936 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3937 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
3942 if (relPath
== NULL
) {
3943 CFStringRef urlPath
= CFURLCopyPath(anURL
);
3944 CFStringEncoding enc
= anURL
->_encoding
;
3947 case kCFURLPOSIXPathStyle
:
3948 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
3950 case kCFURLHFSPathStyle
:
3953 case kCFURLWindowsPathStyle
:
3954 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
3957 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
3963 // For Tiger, leave this behavior in for all path types. For Leopard, it would be nice to remove this entirely
3964 // and do a linked-on-or-later check so we don't break third parties.
3965 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
3966 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
3967 if ( relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == '/') {
3968 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
3975 // we have both basePath and relPath -- resolve them
3976 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
3977 CFRelease(basePath
);
3982 // we only have the relPath -- return it
3986 else if ( basePath
) {
3987 // we only have the basePath --- return it
3991 // we have nothing to return
3996 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
3997 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_WINDOWS
3998 CFAllocatorRef alloc
= CFGetAllocator(url
);
4001 if (!url
) return false;
4003 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
4004 if ( !resolveAgainstBase
|| (CFURLGetBaseURL(url
) == NULL
) ) {
4005 if (!CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4006 // We can grope the ivars
4007 if ( url
->_flags
& IS_CANONICAL_FILE_URL
) {
4008 return CanonicalFileURLStringToFileSystemRepresentation(url
->_string
, alloc
, buffer
, bufLen
);
4012 // else fall back to slower way.
4013 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
4015 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
4019 #elif DEPLOYMENT_TARGET_WINDOWS
4020 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
4023 CFIndex pathLen
= CFStringGetLength(path
);
4024 CFIndex numConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLen
), CFStringFileSystemEncoding(), 0, true, buffer
, bufLen
-1, &usedLen
); // -1 because we need one byte to zero-terminate.
4026 if (numConverted
== pathLen
) {
4027 buffer
[usedLen
] = '\0';
4035 #if DEPLOYMENT_TARGET_WINDOWS
4036 CF_EXPORT Boolean
_CFURLGetWideFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, wchar_t *buffer
, CFIndex bufferLength
) {
4037 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
4038 CFIndex pathLength
, charsConverted
, usedBufLen
;
4039 if (!path
) return false;
4040 pathLength
= CFStringGetLength(path
);
4041 if (pathLength
+1 > bufferLength
) {
4045 charsConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLength
), kCFStringEncodingUTF16
, 0, false, (UInt8
*)buffer
, bufferLength
*sizeof(wchar_t), &usedBufLen
);
4046 // CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer);
4048 if (charsConverted
!= pathLength
|| usedBufLen%sizeof
(wchar_t) != 0) {
4051 buffer
[usedBufLen
/sizeof(wchar_t)] = 0;
4052 // buffer[pathLength] = 0;
4058 static CFStringRef
_createPathFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
4059 char pathDelim
= PATH_SEP
;
4061 if ( isDirectory
) {
4062 // it is a directory: if it doesn't end with pathDelim, append a pathDelim. Limit stack buffer to PATH_MAX+1 in case a large bogus value is passed.
4063 if ( (bufLen
> 0) && (bufLen
<= PATH_MAX
) && (buffer
[bufLen
-1] != pathDelim
) ) {
4064 STACK_BUFFER_DECL(uint8_t, tempBuf
, bufLen
+ 1);
4065 memcpy(tempBuf
, buffer
, bufLen
);
4066 tempBuf
[bufLen
] = pathDelim
;
4067 path
= CFStringCreateWithBytes(allocator
, tempBuf
, bufLen
+ 1, CFStringFileSystemEncoding(), false);
4070 // already had pathDelim at end of buffer or bufLen is really large
4071 path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4075 // it is not a directory: remove any pathDelim characters at end (leaving at least one character)
4076 while ( (bufLen
> 1) && (buffer
[bufLen
-1] == pathDelim
) ) {
4079 path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
4085 CreatePOSIXFileURLStringFromPOSIXAbsolutePath creates file URL string from the native file system representation.
4087 The rules for path-absolute from rfc3986 are:
4088 path-absolute = "/" [ segment-nz *( "/" segment ) ]
4090 segment-nz = 1*pchar
4091 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
4092 pct-encoded = "%" HEXDIG HEXDIG
4093 unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
4094 sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
4096 static CFStringRef
CreatePOSIXFileURLStringFromPOSIXAbsolutePath(CFAllocatorRef alloc
, const UInt8
*bytes
, CFIndex numBytes
, Boolean isDirectory
, Boolean
*addedPercentEncoding
)
4098 static const UInt8 hexchars
[] = "0123456789ABCDEF";
4099 STACK_BUFFER_DECL(UInt8
, stackBuf
, (PATH_MAX
* 3) + sizeof(fileURLPrefixWithAuthority
)); // worst case is every byte needs to be percent-escaped
4102 const UInt8
*bytePtr
= bytes
;
4105 Boolean addedPercent
= FALSE
;
4107 // choose a buffer to percent-escape into.
4108 if ( numBytes
<= PATH_MAX
) {
4109 bufStartPtr
= &stackBuf
[0];
4112 // worst case is every byte needs to be percent-escaped (numBytes * 3)
4113 bufStartPtr
= (UInt8
*)malloc((numBytes
* 3) + sizeof(fileURLPrefixWithAuthority
));
4116 if ( bufStartPtr
!= NULL
) {
4117 // start with the fileURLPrefixWithAuthority
4118 strcpy((char *)bufStartPtr
, (char *)fileURLPrefixWithAuthority
);
4119 bufBytePtr
= bufStartPtr
+ sizeof(fileURLPrefixWithAuthority
) - 1;
4120 // append the percent-escaped path
4121 for ( idx
= 0; (idx
< numBytes
) && (*bytePtr
!= 0); ++idx
) {
4122 switch ( *bytePtr
) {
4123 // these are the visible 7-bit ascii characters that are not legal pchar octets
4127 case ';': // 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
4130 case '?': // we need to percent-escape '?' in file system paths so it won't be mistaken for the start of a query
4139 // percent-escape non-pchar octets spread throughout the visible 7-bit ascii range
4140 *bufBytePtr
++ = '%';
4141 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
4142 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
4143 addedPercent
= TRUE
;
4146 if ( (*bytePtr
<= ' ') || // percent-escape non-pchar octets that are space or less (control characters)
4147 (*bytePtr
>= 0x7f) ) { // percent-escape non-pchar octets that del and 8-bit ascii with the high bit set
4148 *bufBytePtr
++ = '%';
4149 *bufBytePtr
++ = hexchars
[*bytePtr
>> 4];
4150 *bufBytePtr
++ = hexchars
[*bytePtr
& 0x0f];
4151 addedPercent
= TRUE
;
4154 // copy everything else
4155 *bufBytePtr
++ = *bytePtr
;
4162 // did we convert numBytes?
4163 if ( idx
== numBytes
) {
4164 // if it is a directory and it doesn't end with PATH_SEP, append a PATH_SEP.
4165 if ( isDirectory
&& bytes
[numBytes
-1] != PATH_SEP
) {
4166 *bufBytePtr
++ = PATH_SEP
;
4169 // create the result
4170 result
= CFStringCreateWithBytes(alloc
, bufStartPtr
, (CFIndex
)(bufBytePtr
-bufStartPtr
), __CFStringGetEightBitStringEncoding(), FALSE
);
4173 // no, but it's OK if the remaining bytes are all nul (embedded nul bytes are not allowed)
4174 const UInt8
*nullBytePtr
= bytePtr
;
4175 for ( /* start where we left off */; (idx
< numBytes
) && (*nullBytePtr
== '\0'); ++idx
, ++nullBytePtr
) {
4178 if ( idx
== numBytes
) {
4179 // create the result
4180 result
= CFStringCreateWithBytes(alloc
, bufStartPtr
, (CFIndex
)(bufBytePtr
-bufStartPtr
), __CFStringGetEightBitStringEncoding(), FALSE
);
4183 // the remaining bytes were not all nul
4187 // free the buffer if we malloc'd it
4188 if ( bufStartPtr
!= &stackBuf
[0] ) {
4196 if ( addedPercentEncoding
) {
4197 *addedPercentEncoding
= addedPercent
;
4203 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
4204 CFURLRef newURL
= NULL
;
4205 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
4206 if ( bufLen
&& (*buffer
== '/') ) {
4207 Boolean addedPercentEncoding
;
4209 CFStringRef urlStr
= CreatePOSIXFileURLStringFromPOSIXAbsolutePath(allocator
, buffer
, bufLen
, isDirectory
, &addedPercentEncoding
);
4211 newURL
= _CFURLAlloc(allocator
);
4213 ((struct __CFURL
*)newURL
)->_flags
|= USES_EIGHTBITSTRINGENCODING
;
4214 // we know urlStr has a scheme and is absolute
4215 _CFURLInit((struct __CFURL
*)newURL
, urlStr
, FULL_URL_REPRESENTATION
, NULL
);
4216 ((struct __CFURL
*)newURL
)->_flags
|= (IS_ABSOLUTE
| IS_CANONICAL_FILE_URL
);
4217 if ( !addedPercentEncoding
) {
4218 ((struct __CFURL
*)newURL
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
4228 CFStringRef path
= _createPathFromFileSystemRepresentation(allocator
, buffer
, bufLen
, isDirectory
);
4230 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
);
4237 #elif DEPLOYMENT_TARGET_WINDOWS
4238 CFStringRef path
= _createPathFromFileSystemRepresentation(allocator
, buffer
, bufLen
, isDirectory
);
4240 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
);
4250 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
4251 CFStringRef path
= _createPathFromFileSystemRepresentation(allocator
, buffer
, bufLen
, isDirectory
);
4252 CFURLRef newURL
= NULL
;
4253 if (!path
) return NULL
;
4254 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
4255 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
, baseURL
);
4256 #elif DEPLOYMENT_TARGET_WINDOWS
4257 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
4264 /******************************/
4265 /* Support for path utilities */
4266 /******************************/
4268 // Assumes url is a CFURL (not an Obj-C NSURL)
4269 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
4270 CFRange pathRg
, componentRg
;
4272 pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
4274 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
4278 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == '/') {
4280 if (pathRg
.length
== 0) {
4285 if (CFStringFindWithOptions(url
->_string
, CFSTR("/"), pathRg
, kCFCompareBackwards
, &componentRg
)) {
4286 componentRg
.location
++;
4287 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
4289 componentRg
= pathRg
;
4294 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
4297 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4298 CFStringRef path
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLPOSIXPathStyle
, false);
4301 if (!path
) return NULL
;
4302 rg
= CFRangeMake(0, CFStringGetLength(path
));
4303 if ( rg
.length
== 0 ) return path
;
4304 length
= rg
.length
; // Remember this for comparison later
4305 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/' ) {
4308 if ( rg
.length
== 0 )
4310 // If we have reduced the string to empty, then it's "/", and that's what we return as
4311 // the last path component.
4314 else if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
4315 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
4316 rg
.location
= compRg
.location
+ 1;
4318 if (rg
.location
== 0 && rg
.length
== length
) {
4321 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), path
, rg
);
4325 Boolean filePathURLCreated
= false;
4326 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4327 if ( _CFURLIsFileReferenceURL(url
) ) {
4328 // use a file path URL or fail
4329 CFURLRef filePathURL
= CFURLCreateFilePathURL(CFGetAllocator(url
), url
, NULL
);
4330 if ( filePathURL
) {
4331 filePathURLCreated
= TRUE
;
4340 CFRange rg
= _rangeOfLastPathComponent(url
);
4341 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
4343 if ( filePathURLCreated
) {
4346 return (CFStringRef
)CFRetain(CFSTR(""));
4348 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == '/') {
4349 if ( filePathURLCreated
) {
4352 return (CFStringRef
)CFRetain(CFSTR("/"));
4354 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
4355 if (!(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
4357 if (url
->_encoding
== kCFStringEncodingUTF8
) {
4358 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
4360 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
4365 if ( filePathURLCreated
) {
4372 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
4373 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
4374 CFStringRef ext
= NULL
;
4377 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
4378 if (rg
.location
!= kCFNotFound
) {
4380 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
4381 if (rg
.length
> 0) {
4382 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
4384 ext
= (CFStringRef
)CFRetain(CFSTR(""));
4387 CFRelease(lastPathComp
);
4392 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
4394 url
= _CFURLFromNSURL(url
);
4395 __CFGenericValidateType(url
, __kCFURLTypeID
);
4396 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
4398 Boolean filePathURLCreated
= false;
4399 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4400 if ( _CFURLIsFileReferenceURL(url
) ) {
4401 // use a file path URL if possible (only because this is appending a path component)
4402 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4403 if ( filePathURL
) {
4404 filePathURLCreated
= TRUE
;
4410 CFMutableStringRef newString
;
4411 CFStringRef newComp
;
4413 if (!(url
->_flags
& HAS_PATH
)) {
4417 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4418 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), url
->_encoding
);
4419 pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
4420 if ( (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') && (CFStringGetCharacterAtIndex(newComp
, 0) != '/') ) {
4421 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4424 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
4426 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
4429 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4430 CFRelease(newString
);
4432 if ( filePathURLCreated
) {
4438 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
4440 CFMutableStringRef newString
;
4441 CFRange lastCompRg
, pathRg
;
4442 Boolean appendDotDot
= false;
4444 url
= _CFURLFromNSURL(url
);
4445 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4446 __CFGenericValidateType(url
, __kCFURLTypeID
);
4448 Boolean filePathURLCreated
= false;
4449 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4450 if ( _CFURLIsFileReferenceURL(url
) ) {
4451 // use a file path URL or fail
4452 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4453 if ( filePathURL
) {
4454 filePathURLCreated
= TRUE
;
4463 if (!(url
->_flags
& HAS_PATH
)) {
4464 if ( filePathURLCreated
) {
4469 pathRg
= _rangeForComponent(url
->_flags
, url
->_ranges
, HAS_PATH
);
4470 lastCompRg
= _rangeOfLastPathComponent(url
);
4471 if (lastCompRg
.length
== 0) {
4472 appendDotDot
= true;
4473 } else if (lastCompRg
.length
== 1) {
4474 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
4475 if (ch
== '.' || ch
== '/') {
4476 appendDotDot
= true;
4478 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
4479 appendDotDot
= true;
4482 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4485 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
4486 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
4489 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
4491 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR("/"));
4493 // 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 "/.".
4494 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
4495 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == '/') {
4496 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
4497 } else if (pathRg
.length
+delta
== 5) {
4498 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
4501 } else if (lastCompRg
.location
== pathRg
.location
) {
4502 CFStringReplace(newString
, pathRg
, CFSTR("."));
4503 CFStringInsert(newString
, 1, CFSTR("/"));
4505 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
4507 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4508 CFRelease(newString
);
4509 if ( filePathURLCreated
) {
4515 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
4516 CFMutableStringRef newString
;
4520 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4521 url
= _CFURLFromNSURL(url
);
4522 __CFGenericValidateType(url
, __kCFURLTypeID
);
4523 __CFGenericValidateType(extension
, CFStringGetTypeID());
4525 Boolean filePathURLCreated
= false;
4526 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4527 if ( _CFURLIsFileReferenceURL(url
) ) {
4528 // use a file path URL or fail
4529 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4530 if ( filePathURL
) {
4531 filePathURLCreated
= TRUE
;
4540 rg
= _rangeOfLastPathComponent(url
);
4541 if (rg
.location
< 0) {
4542 if ( filePathURLCreated
) {
4545 return NULL
; // No path
4548 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4549 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
4550 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), url
->_encoding
);
4551 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
4553 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4554 CFRelease(newString
);
4555 if ( filePathURLCreated
) {
4561 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
4565 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
4566 url
= _CFURLFromNSURL(url
);
4567 __CFGenericValidateType(url
, __kCFURLTypeID
);
4569 Boolean filePathURLCreated
= false;
4570 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4571 if ( _CFURLIsFileReferenceURL(url
) ) {
4572 // use a file path URL or fail
4573 CFURLRef filePathURL
= CFURLCreateFilePathURL(allocator
, url
, NULL
);
4574 if ( filePathURL
) {
4575 filePathURLCreated
= TRUE
;
4584 rg
= _rangeOfLastPathComponent(url
);
4585 if (rg
.location
< 0) {
4587 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
4588 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
4589 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
4590 CFStringDelete(newString
, dotRg
);
4591 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
4592 CFRelease(newString
);
4594 result
= (CFURLRef
)CFRetain(url
);
4596 if ( filePathURLCreated
) {
4603 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4604 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4605 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4606 CFMutableArrayRef newComponents
= CFArrayCreateMutableCopy(alloc
, 0, components
);
4607 Boolean doSpecialLeadingColon
= false;
4608 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
4610 CFRelease(components
);
4613 if (!doSpecialLeadingColon
&& firstChar
!= ':') {
4614 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4615 } else if (firstChar
!= ':') {
4616 // see what we need to add at the beginning. Under MacOS, if the
4617 // first character isn't a ':', then the first component is the
4618 // volume name, and we need to find the mount point. Bleah. If we
4619 // don't find a mount point, we're going to have to lie, and make something up.
4620 CFStringRef firstComp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0);
4621 if (CFStringGetLength(firstComp
) == 1 && CFStringGetCharacterAtIndex(firstComp
, 0) == '/') {
4622 // "/" is the "magic" path for a UFS root directory
4623 CFArrayRemoveValueAtIndex(newComponents
, 0);
4624 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4626 // See if we can get a mount point.
4627 Boolean foundMountPoint
= false;
4628 if (!foundMountPoint
) {
4629 // Fall back to treating the volume name as the top level directory
4630 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4634 CFArrayRemoveValueAtIndex(newComponents
, 0);
4637 cnt
= CFArrayGetCount(newComponents
);
4638 for (i
= 0; i
< cnt
; i
++) {
4639 CFStringRef comp
= (CFStringRef
)CFArrayGetValueAtIndex(newComponents
, i
);
4640 CFStringRef newComp
= NULL
;
4641 CFRange searchRg
, slashRg
;
4642 searchRg
.location
= 0;
4643 searchRg
.length
= CFStringGetLength(comp
);
4644 while (CFStringFindWithOptions(comp
, CFSTR("/"), searchRg
, 0, &slashRg
)) {
4646 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4648 CFStringReplace((CFMutableStringRef
)newComp
, slashRg
, CFSTR(":"));
4649 searchRg
.length
= searchRg
.location
+ searchRg
.length
- slashRg
.location
- 1;
4650 searchRg
.location
= slashRg
.location
+ 1;
4653 CFArraySetValueAtIndex(newComponents
, i
, newComp
);
4657 if (isDir
&& CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, cnt
-1)) != 0) {
4658 CFArrayAppendValue(newComponents
, CFSTR(""));
4660 return newComponents
;
4663 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
4664 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
4665 CFAllocatorRef alloc
= CFGetAllocator(array
);
4666 CFMutableArrayRef mArray
= NULL
;
4667 CFIndex i
, c
= CFArrayGetCount(array
);
4668 for (i
= 0; i
< c
; i
++) {
4669 CFStringRef origComp
= (CFStringRef
)CFArrayGetValueAtIndex(array
, i
);
4670 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
4671 if (!unescapedComp
) {
4674 if (unescapedComp
!= origComp
) {
4676 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
4678 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
4680 CFRelease(unescapedComp
);
4683 if (mArray
) CFRelease(mArray
);
4685 } else if (mArray
) {
4693 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
4694 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
4697 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4698 CFArrayRef components
= HFSPathToURLComponents(path
, alloc
, isDir
);
4699 CFArrayRef newComponents
= components
? copyStringArrayWithTransformation(components
, escapePathComponent
) : NULL
;
4702 if (components
) CFRelease(components
);
4703 if (!newComponents
) return NULL
;
4705 cnt
= CFArrayGetCount(newComponents
);
4706 if (cnt
== 1 && CFStringGetLength((CFStringRef
)CFArrayGetValueAtIndex(newComponents
, 0)) == 0) {
4707 result
= (CFStringRef
)CFRetain(CFSTR("/"));
4709 result
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
4711 CFRelease(newComponents
);
4718 // keys and vals must have space for at least 4 key/value pairs. No argument can be NULL.
4719 // Caller must release values, but not keys
4720 static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url
, CFTypeRef
*keys
, CFTypeRef
*vals
, CFIndex
*count
) {
4721 CFAllocatorRef alloc
= CFGetAllocator(url
);
4722 CFURLRef base
= CFURLGetBaseURL(url
);
4723 keys
[0] = CFSTR("_CFURLStringType");
4724 keys
[1] = CFSTR("_CFURLString");
4725 keys
[2] = CFSTR("_CFURLBaseStringType");
4726 keys
[3] = CFSTR("_CFURLBaseURLString");
4727 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
4728 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4729 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4730 vals
[1] = CFURLGetString(url
);
4732 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4733 vals
[0] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4734 if (url
->_flags
& IS_DIRECTORY
) {
4735 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) == '/') {
4736 vals
[1] = CFRetain(url
->_string
);
4738 vals
[1] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), url
->_string
, '/');
4741 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != '/') {
4742 vals
[1] = CFRetain(url
->_string
);
4744 vals
[1] = CFStringCreateWithSubstring(alloc
, url
->_string
, CFRangeMake(0, CFStringGetLength(url
->_string
) - 1));
4749 if (CF_IS_OBJC(__kCFURLTypeID
, base
)) {
4750 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4751 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4752 vals
[3] = CFURLGetString(base
);
4754 SInt32 urlType
= FULL_URL_REPRESENTATION
;
4755 vals
[2] = CFNumberCreate(alloc
, kCFNumberSInt32Type
, &urlType
);
4756 if (base
->_flags
& IS_DIRECTORY
) {
4757 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) == '/') {
4758 vals
[3] = CFRetain(base
->_string
);
4760 vals
[3] = CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@%c"), base
->_string
, '/');
4763 if (CFStringGetCharacterAtIndex(base
->_string
, CFStringGetLength(base
->_string
) - 1) != '/') {
4764 vals
[3] = CFRetain(base
->_string
);
4766 vals
[3] = CFStringCreateWithSubstring(alloc
, base
->_string
, CFRangeMake(0, CFStringGetLength(base
->_string
) - 1));
4776 // Private API for Finder to use
4777 CFPropertyListRef
_CFURLCopyPropertyListRepresentation(CFURLRef url
) {
4778 CFTypeRef keys
[4], vals
[4];
4779 CFDictionaryRef dict
;
4781 __CFURLCopyPropertyListKeysAndValues(url
, keys
, vals
, &count
);
4782 dict
= CFDictionaryCreate(CFGetAllocator(url
), keys
, vals
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4783 for (idx
= 0; idx
< count
; idx
++) {
4784 CFRelease(vals
[idx
]);
4789 CFURLRef
_CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc
, CFPropertyListRef pListRepresentation
) {
4790 CFStringRef baseString
, string
;
4791 CFNumberRef baseTypeNum
, urlTypeNum
;
4792 SInt32 baseType
, urlType
;
4793 CFURLRef baseURL
= NULL
, url
;
4794 CFDictionaryRef dict
= (CFDictionaryRef
)pListRepresentation
;
4796 // Start by getting all the pieces and verifying they're of the correct type.
4797 if (CFGetTypeID(pListRepresentation
) != CFDictionaryGetTypeID()) {
4800 string
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLString"));
4801 if (!string
|| CFGetTypeID(string
) != CFStringGetTypeID()) {
4804 urlTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLStringType"));
4805 if (!urlTypeNum
|| CFGetTypeID(urlTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum
, kCFNumberSInt32Type
, &urlType
) || (urlType
!= FULL_URL_REPRESENTATION
&& urlType
!= kCFURLPOSIXPathStyle
&& urlType
!= kCFURLHFSPathStyle
&& urlType
!= kCFURLWindowsPathStyle
)) {
4808 baseString
= (CFStringRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseURLString"));
4810 if (CFGetTypeID(baseString
) != CFStringGetTypeID()) {
4813 baseTypeNum
= (CFNumberRef
)CFDictionaryGetValue(dict
, CFSTR("_CFURLBaseStringType"));
4814 if (!baseTypeNum
|| CFGetTypeID(baseTypeNum
) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum
, kCFNumberSInt32Type
, &baseType
) ||
4815 (baseType
!= FULL_URL_REPRESENTATION
&& baseType
!= kCFURLPOSIXPathStyle
&& baseType
!= kCFURLHFSPathStyle
&& baseType
!= kCFURLWindowsPathStyle
)) {
4818 if (baseType
== FULL_URL_REPRESENTATION
) {
4819 baseURL
= _CFURLCreateWithArbitraryString(alloc
, baseString
, NULL
);
4821 baseURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, baseString
, (CFURLPathStyle
)baseType
, CFStringGetCharacterAtIndex(baseString
, CFStringGetLength(baseString
)-1) == '/', NULL
);
4824 if (urlType
== FULL_URL_REPRESENTATION
) {
4825 url
= _CFURLCreateWithArbitraryString(alloc
, string
, baseURL
);
4827 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, string
, (CFURLPathStyle
)urlType
, CFStringGetCharacterAtIndex(string
, CFStringGetLength(string
)-1) == '/', baseURL
);
4829 if (baseURL
) CFRelease(baseURL
);
4833 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4834 Boolean
_CFURLIsFileReferenceURL(CFURLRef url
)
4836 // returns TRUE if url is is a file URL whose path starts with a file ID reference
4837 Boolean result
= false;
4838 CFURLRef baseURL
= CFURLGetBaseURL(url
);
4840 result
= _CFURLIsFileReferenceURL(baseURL
);
4843 if ( CF_IS_OBJC(__kCFURLTypeID
, url
) ) {
4844 result
= (Boolean
) CF_OBJC_CALLV((NSURL
*)url
, isFileReferenceURL
);
4847 result
= ((_getSchemeTypeFromFlags(url
->_flags
) == kHasFileScheme
) && (url
->_flags
& PATH_HAS_FILE_ID
));
4853 static Boolean
_CFURLHasFileURLScheme(CFURLRef url
, Boolean
*hasScheme
)
4856 CFURLRef baseURL
= CFURLGetBaseURL(url
);
4859 result
= _CFURLHasFileURLScheme(baseURL
, hasScheme
);
4862 if ( CF_IS_OBJC(__kCFURLTypeID
, url
) ) {
4863 CFStringRef scheme
= CFURLCopyScheme(url
);
4865 if ( scheme
== kCFURLFileScheme
) {
4869 result
= CFStringCompare(scheme
, kCFURLFileScheme
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
;
4885 *hasScheme
= (url
->_flags
& HAS_SCHEME
) != 0;
4887 result
= (_getSchemeTypeFromFlags(url
->_flags
) == kHasFileScheme
);
4893 Boolean
_CFURLIsFileURL(CFURLRef url
)
4895 Boolean result
= _CFURLHasFileURLScheme(url
, NULL
);
4899 CFURLRef
CFURLCreateFilePathURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
)
4901 CFURLRef result
= NULL
;
4903 if (!_CFURLHasFileURLScheme(url
, &hasScheme
)) {
4905 CFLog(kCFLogLevelWarning
, CFSTR("CFURLCreateFilePathURL failed because it was passed this URL which has no scheme: %@"), url
);
4908 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLReadUnsupportedSchemeError
, NULL
);
4912 // File URL. Form of the path is unknown. Make a new URL.
4913 CFStringRef newURLString
;
4918 if ( CFURLGetBaseURL( url
)) {
4919 CFURLRef absURL
= CFURLCopyAbsoluteURL( url
);
4920 fsPath
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(absURL
), absURL
, kCFURLPOSIXPathStyle
, false);
4921 netLoc
= CFURLCopyNetLocation( absURL
);
4922 rSpec
= CFURLCopyResourceSpecifier( absURL
);
4923 CFRelease( absURL
);
4925 fsPath
= CFURLCreateStringWithFileSystemPath(CFGetAllocator(url
), url
, kCFURLPOSIXPathStyle
, false);
4926 netLoc
= CFURLCopyNetLocation( url
);
4927 rSpec
= CFURLCopyResourceSpecifier( url
);
4930 CFStringRef urlPath
= _replacePathIllegalCharacters( fsPath
, alloc
, true );
4931 newURLString
= CFStringCreateWithFormat( alloc
, NULL
, CFSTR("file://%@%@%@%@"), (netLoc
? netLoc
: CFSTR("")), urlPath
, ((CFStringCompare(urlPath
, CFSTR("/"), 0) != kCFCompareEqualTo
) ? (CFURLHasDirectoryPath( url
) ? CFSTR("/") : CFSTR("")) : CFSTR("")), (rSpec
? rSpec
: CFSTR("")));
4932 result
= CFURLCreateWithString( alloc
, newURLString
, NULL
);
4933 CFRelease( newURLString
);
4934 CFRelease( urlPath
);
4935 CFRelease( fsPath
);
4938 // Would be better here to get an underlying error back from CFURLCreateStringWithFileSystemPath
4939 *error
= CFErrorCreate( kCFAllocatorDefault
, kCFErrorDomainCocoa
, kCFURLNoSuchResourceError
, NULL
);
4944 CFRelease( netLoc
);
4956 CFURLRef
CFURLCreateFileReferenceURL(CFAllocatorRef alloc
, CFURLRef url
, CFErrorRef
*error
) { return NULL
; }