2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright 1998-2002, Apple, Inc. All rights reserved.
27 Responsibility: Becky Willrich
30 #include <CoreFoundation/CFURL.h>
32 #include "CFCharacterSetPriv.h"
33 #include <CoreFoundation/CFNumber.h>
34 #include "CFInternal.h"
35 #include "CFStringEncodingConverter.h"
36 #include "CFUtilities.h"
41 #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__)
46 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
47 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
48 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
49 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
);
50 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
);
51 static CFStringRef
URLPathToHFSPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
);
52 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
);
53 static Boolean
_CFGetFSRefFromURL(CFAllocatorRef alloc
, CFURLRef url
, void *voidRef
);
54 static CFURLRef
_CFCreateURLFromFSRef(CFAllocatorRef alloc
, const void *voidRef
, Boolean isDirectory
);
55 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
);
57 #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) || defined(__WIN32__)
58 #if !defined(HAVE_CARBONCORE)
61 #define FSGetVolumeInfo(A, B, C, D, E, F, G) (-3296)
62 #define FSGetCatalogInfo(A, B, C, D, E, F) (-3296)
63 #define FSMakeFSRefUnicode(A, B, C, D, E) (-3296)
64 #define FSPathMakeRef(A, B, C) (-3296)
65 #define FSRefMakePath(A, B, C) (-3296)
66 #define FSpMakeFSRef(A, B) (-3296)
67 #define FSNewAlias(A, B, C) (-3296)
68 #define DisposeHandle(A) (-3296)
71 #include <mach-o/dyld.h>
73 static void __CF_DisposeHandle_internal(Handle h
) {
74 static void (*dyfunc
)(Handle
) = NULL
;
76 void *image
= __CFLoadCarbonCore();
77 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_DisposeHandle", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
82 static OSErr
__CF_FSNewAlias_internal(const FSRef
*A
, const FSRef
*B
, AliasHandle
*C
) {
83 static OSErr (*dyfunc
)(const FSRef
*, const FSRef
*, AliasHandle
*) = NULL
;
85 void *image
= __CFLoadCarbonCore();
86 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_FSNewAlias", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
88 return dyfunc(A
, B
, C
);
91 static OSErr
__CF_FSGetVolumeInfo_internal(FSVolumeRefNum A
, ItemCount B
, FSVolumeRefNum
*C
, FSVolumeInfoBitmap D
, FSVolumeInfo
*E
, HFSUniStr255
*F
, FSRef
*G
) {
92 static OSErr (*dyfunc
)(FSVolumeRefNum
, ItemCount
, FSVolumeRefNum
*, FSVolumeInfoBitmap
, FSVolumeInfo
*, HFSUniStr255
*, FSRef
*) = NULL
;
94 void *image
= __CFLoadCarbonCore();
95 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_FSGetVolumeInfo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
97 return dyfunc(A
, B
, C
, D
, E
, F
, G
);
100 static OSErr
__CF_FSGetCatalogInfo_internal(const FSRef
*A
, FSCatalogInfoBitmap B
, FSCatalogInfo
*C
, HFSUniStr255
*D
, FSSpec
*E
, FSRef
*F
) {
101 static OSErr (*dyfunc
)(const FSRef
*, FSCatalogInfoBitmap
, FSCatalogInfo
*, HFSUniStr255
*, FSSpec
*, FSRef
*) = NULL
;
102 if (NULL
== dyfunc
) {
103 void *image
= __CFLoadCarbonCore();
104 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_FSGetCatalogInfo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
106 return dyfunc(A
, B
, C
, D
, E
, F
);
109 static OSErr
__CF_FSMakeFSRefUnicode_internal(const FSRef
*A
, UniCharCount B
, const UniChar
*C
, TextEncoding D
, FSRef
*E
) {
110 static OSErr (*dyfunc
)(const FSRef
*, UniChar
, const UniChar
*, TextEncoding
, FSRef
*) = NULL
;
111 if (NULL
== dyfunc
) {
112 void *image
= __CFLoadCarbonCore();
113 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_FSMakeFSRefUnicode", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
115 return dyfunc(A
, B
, C
, D
, E
);
118 static OSStatus
__CF_FSPathMakeRef_internal(const uint8_t *A
, FSRef
*B
, Boolean
*C
) {
119 static OSStatus (*dyfunc
)(const uint8_t *, FSRef
*, Boolean
*) = NULL
;
120 if (NULL
== dyfunc
) {
121 void *image
= __CFLoadCarbonCore();
122 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_FSPathMakeRef", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
124 return dyfunc(A
, B
, C
);
127 static OSStatus
__CF_FSRefMakePath_internal(const FSRef
*A
, uint8_t *B
, UInt32 C
) {
128 static OSStatus (*dyfunc
)(const FSRef
*, uint8_t *, UInt32
) = NULL
;
129 if (NULL
== dyfunc
) {
130 void *image
= __CFLoadCarbonCore();
131 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_FSRefMakePath", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
133 return dyfunc(A
, B
, C
);
136 static OSErr
__CF_FSpMakeFSRef_internal(const FSSpec
*A
, FSRef
*B
) {
137 static OSErr (*dyfunc
)(const FSSpec
*, FSRef
*) = NULL
;
138 if (NULL
== dyfunc
) {
139 void *image
= __CFLoadCarbonCore();
140 dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_FSpMakeFSRef", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
145 #define FSGetVolumeInfo(A, B, C, D, E, F, G) __CF_FSGetVolumeInfo_internal(A, B, C, D, E, F, G)
146 #define FSGetCatalogInfo(A, B, C, D, E, F) __CF_FSGetCatalogInfo_internal(A, B, C, D, E, F)
147 #define FSMakeFSRefUnicode(A, B, C, D, E) __CF_FSMakeFSRefUnicode_internal(A, B, C, D, E)
148 #define FSPathMakeRef(A, B, C) __CF_FSPathMakeRef_internal(A, B, C)
149 #define FSRefMakePath(A, B, C) __CF_FSRefMakePath_internal(A, B, C)
150 #define FSpMakeFSRef(A, B) __CF_FSpMakeFSRef_internal(A, B)
151 #define FSNewAlias(A, B, C) __CF_FSNewAlias_internal(A, B, C)
152 #define DisposeHandle(A) __CF_DisposeHandle_internal(A)
156 #if defined(__MACOS8__)
158 #include <CodeFragments.h>
159 #include <MacErrors.h>
162 static Boolean
__CFMacOS8HasFSRefs() {
163 static Boolean sHasFSRefs
= (Boolean
) -1;
164 if ( sHasFSRefs
== (Boolean
) -1 ) {
166 sHasFSRefs
= Gestalt( gestaltFSAttr
, &result
) == noErr
&&
167 ( result
& (1 << gestaltHasHFSPlusAPIs
) ) != 0;
172 static Ptr
__CFGropeAroundForMacOS8Symbol(ConstStr255Param name
) {
173 static const char *libraries
[] = {"\pCarbonLib", "\pInterfaceLib", "\pPrivateInterfaceLib"};
175 for (idx
= 0; idx
< sizeof(libraries
) / sizeof(libraries
[0]); idx
++) {
176 CFragConnectionID connID
; /* We get the connections ONLY if already available. */
177 OSErr err
= GetSharedLibrary(libraries
[idx
], kPowerPCCFragArch
, kFindCFrag
, &connID
, NULL
, NULL
);
180 CFragSymbolClass symbolClass
;
181 err
= FindSymbol(connID
, name
, &cfmfunc
, &symbolClass
);
182 if (err
== noErr
&& symbolClass
== kTVectorCFragSymbol
) {
190 static OSErr
__CF_FSMakeFSRefUnicode_internal(const FSRef
*A
, UniCharCount B
, const UniChar
*C
, TextEncoding D
, FSRef
*E
) {
191 static OSErr (*cfmfunc
)(const FSRef
*, UniChar
, const UniChar
*, TextEncoding
, FSRef
*) = NULL
;
192 static Boolean looked
= false;
193 if (!looked
&& __CFMacOS8HasFSRefs()) {
194 cfmfunc
= __CFGropeAroundForMacOS8Symbol("\pFSMakeFSRefUnicode");
197 return (cfmfunc
!= NULL
) ? cfmfunc(A
, B
, C
, D
, E
) : cfragNoSymbolErr
;
200 static OSErr
__CF_FSGetVolumeInfo_internal(FSVolumeRefNum A
, ItemCount B
, FSVolumeRefNum
*C
, FSVolumeInfoBitmap D
, FSVolumeInfo
*E
, HFSUniStr255
*F
, FSRef
*G
) {
201 static OSErr (*cfmfunc
)(FSVolumeRefNum
, ItemCount
, FSVolumeRefNum
*, FSVolumeInfoBitmap
, FSVolumeInfo
*, HFSUniStr255
*, FSRef
*) = NULL
;
202 static Boolean looked
= false;
203 if (!looked
&& __CFMacOS8HasFSRefs()) {
204 cfmfunc
= __CFGropeAroundForMacOS8Symbol("\pFSGetVolumeInfo");
207 return (cfmfunc
!= NULL
) ? cfmfunc(A
, B
, C
, D
, E
, F
, G
) : cfragNoSymbolErr
;
210 static OSErr
__CF_FSGetCatalogInfo_internal(const FSRef
*A
, FSCatalogInfoBitmap B
, FSCatalogInfo
*C
, HFSUniStr255
*D
, FSSpec
*E
, FSRef
*F
) {
211 static OSErr (*cfmfunc
)(const FSRef
*, FSCatalogInfoBitmap
, FSCatalogInfo
*, HFSUniStr255
*, FSSpec
*, FSRef
*) = NULL
;
212 static Boolean looked
= false;
213 if (!looked
&& __CFMacOS8HasFSRefs()) {
214 cfmfunc
= __CFGropeAroundForMacOS8Symbol("\pFSGetCatalogInfo");
217 return (cfmfunc
!= NULL
) ? cfmfunc(A
, B
, C
, D
, E
, F
) : cfragNoSymbolErr
;
220 static OSStatus
__CF_FSRefMakePath_internal(const FSRef
*A
, uint8_t *B
, UInt32 C
) {
221 static OSStatus (*cfmfunc
)(const FSRef
*, uint8_t *buf
, UInt32
) = NULL
;
222 static Boolean looked
= false;
223 if (!looked
&& __CFMacOS8HasFSRefs()) {
224 cfmfunc
= __CFGropeAroundForMacOS8Symbol("\pFSRefMakePath");
227 return (cfmfunc
!= NULL
) ? cfmfunc(A
, B
, C
) : cfragNoSymbolErr
;
230 #define FSMakeFSRefUnicode(A, B, C, D, E) __CF_FSMakeFSRefUnicode_internal(A, B, C, D, E)
231 #define FSGetVolumeInfo(A, B, C, D, E, F, G) __CF_FSGetVolumeInfo_internal(A, B, C, D, E, F, G)
232 #define FSGetCatalogInfo(A, B, C, D, E, F) __CF_FSGetCatalogInfo_internal(A, B, C, D, E, F)
233 #define FSRefMakePath(A, B, C) __CF_FSRefMakePath_internal(A, B, C)
236 #if defined(__MACH__)
237 #include <sys/stat.h>
238 #include <sys/types.h>
241 #if defined(__MACOS8__)
246 #define DEBUG_URL_MEMORY_USAGE 0
248 /* The bit flags in myURL->_flags */
249 #define HAS_SCHEME (0x0001)
250 #define HAS_USER (0x0002)
251 #define HAS_PASSWORD (0x0004)
252 #define HAS_HOST (0x0008)
253 #define HAS_PORT (0x0010)
254 #define HAS_PATH (0x0020)
255 #define HAS_PARAMETERS (0x0040)
256 #define HAS_QUERY (0x0080)
257 #define HAS_FRAGMENT (0x0100)
258 // Last free bit (0x200) in lower word goes here!
259 #define IS_IPV6_ENCODED (0x0400)
260 #define IS_OLD_UTF8_STYLE (0x0800)
261 #define IS_DIRECTORY (0x1000)
262 #define IS_PARSED (0x2000)
263 #define IS_ABSOLUTE (0x4000)
264 #define IS_DECOMPOSABLE (0x8000)
266 #define PATH_TYPE_MASK (0x000F0000)
267 /* POSIX_AND_URL_PATHS_MATCH will only be true if the URL and POSIX paths are identical, character for character, except for the presence/absence of a trailing slash on directories */
268 #define POSIX_AND_URL_PATHS_MATCH (0x00100000)
269 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000)
271 /* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */
272 // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
273 #define SCHEME_DIFFERS (0x00400000)
274 #define USER_DIFFERS (0x00800000)
275 #define PASSWORD_DIFFERS (0x01000000)
276 #define HOST_DIFFERS (0x02000000)
277 // 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
278 #define PORT_DIFFERS (0x04000000)
279 #define PATH_DIFFERS (0x08000000)
280 #define PARAMETERS_DIFFER (0x10000000)
281 #define QUERY_DIFFERS (0x20000000)
282 #define FRAGMENT_DIFfERS (0x40000000)
284 // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag
285 #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22)
287 // Other useful defines
288 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
289 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
290 #define FULL_URL_REPRESENTATION (0xF)
292 /* URL_PATH_TYPE(anURL) will be one of the CFURLPathStyle constants, in which case string is a file system path, or will be FULL_URL_REPRESENTATION, in which case the string is the full URL string. One caveat - string always has a trailing path delimiter if the url is a directory URL. This must be stripped before returning file system representations! */
293 #define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16)
294 #define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/'))
295 #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/")))
299 CFRuntimeBase _cfBase
;
301 CFStringRef _string
; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above)
302 CFURLRef _base
; // NULL for absolute URLs; if present, _base is guaranteed to itself be absolute.
304 void *_reserved
; // Reserved for URLHandle's use.
305 CFStringEncoding _encoding
; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set.
306 CFMutableStringRef _sanatizedString
; // The fully compliant RFC string. This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false. This should never be mutated except when the sanatized string is first computed
309 static void _convertToURLRepresentation(struct __CFURL
*url
);
310 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
);
311 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
);
312 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef base
, UInt32
*flags
, CFRange
**range
);
313 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
);
314 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
);
315 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
);
316 static void computeSanitizedString(CFURLRef url
);
317 static CFStringRef
correctedComponent(CFStringRef component
, UInt32 compFlag
, CFStringEncoding enc
);
318 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
);
319 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
);
322 CF_INLINE
void _parseComponentsOfURL(CFURLRef url
) {
323 _parseComponents(CFGetAllocator(url
), url
->_string
, url
->_base
, &(((struct __CFURL
*)url
)->_flags
), &(((struct __CFURL
*)url
)->ranges
));
326 static Boolean _createOldUTF8StyleURLs
= false;
328 CF_INLINE Boolean
createOldUTF8StyleURLs(void) {
329 if (_createOldUTF8StyleURLs
) {
332 return !_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
);
335 // Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW
337 void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs
) {
338 _createOldUTF8StyleURLs
= createUTF8URLs
;
341 CF_INLINE Boolean
scheme_valid(UniChar c
) {
342 if (('a' <= c
&& c
<= 'z') || ('A' <= c
&& c
<= 'Z')) return true;
343 if ('0' <= c
&& c
<= '9') return true; // Added 12/1/2000; RFC 2396 extends schemes to include digits
344 if ((c
== '.') || (c
== '-') || (c
== '+')) return true;
348 // "Unreserved" as defined by RFC 2396
349 CF_INLINE Boolean
isUnreservedCharacter(UniChar ch
) {
350 // unreserved characters are ASCII-7 alphanumerics, plus certain punctuation marks, all in the 0-127 range
351 if (ch
> 0x7f) return false;
352 if (('a' <= ch
&& ch
<= 'z') || ('A' <= ch
&& ch
<= 'Z')) return true;
353 if ('0' <= ch
&& ch
<= '9') return true;
354 if (ch
== '-' || ch
== '_' || ch
== '.' || ch
== '!' || ch
== '~' || ch
== '*' || ch
== '\'' || ch
== '(' || ch
== ')') return true;
358 CF_INLINE Boolean
isPathLegalCharacter(UniChar ch
) {
359 // the unreserved chars plus a couple others
360 if (ch
> 0x7f) return false;
361 if (('a' <= ch
&& ch
<= 'z') || ('A' <= ch
&& ch
<= 'Z')) return true;
362 if ('0' <= ch
&& ch
<= '9') return true;
363 if (ch
== '-' || ch
== '_' || ch
== '.' || ch
== '!' || ch
== '~' || ch
== '*' || ch
== '\'' || ch
== '(' || ch
== ')') return true;
364 if (ch
== '/' || ch
== ':' || ch
== '@' || ch
== '&' || ch
== '=' || ch
== '+' || ch
== '$' || ch
== ',') return true;
368 CF_INLINE Boolean
isURLLegalCharacter(UniChar ch
) {
369 if (ch
> 0x7f) return false;
370 if (('a' <= ch
&& ch
<= 'z') || ('A' <= ch
&& ch
<= 'Z')) return true;
371 if ('0' <= ch
&& ch
<= '9') return true;
372 if (ch
== '-' || ch
== '_' || ch
== '.' || ch
== '!' || ch
== '~' || ch
== '*' || ch
== '\'' || ch
== '(' || ch
== ')') return true;
373 if (ch
== ';' || ch
== '/' || ch
== '?' || ch
== ':' || ch
== '@' || ch
== '&' || ch
== '=' || ch
== '+' || ch
== '$' || ch
== ',') return true;
377 CF_INLINE Boolean
isHexDigit(UniChar ch
) {
378 return ((ch
>= '0' && ch
<= '9') || (ch
>= 'a' && ch
<= 'f') || (ch
>= 'A' && ch
<= 'F'));
380 // Returns false if ch1 or ch2 isn't properly formatted
381 CF_INLINE Boolean
_translateBytes(UniChar ch1
, UniChar ch2
, uint8_t *result
) {
383 if (ch1
>= '0' && ch1
<= '9') *result
+= (ch1
- '0');
384 else if (ch1
>= 'a' && ch1
<= 'f') *result
+= 10 + ch1
- 'a';
385 else if (ch1
>= 'A' && ch1
<= 'F') *result
+= 10 + ch1
- 'A';
388 *result
= (*result
) << 4;
389 if (ch2
>= '0' && ch2
<= '9') *result
+= (ch2
- '0');
390 else if (ch2
>= 'a' && ch2
<= 'f') *result
+= 10 + ch2
- 'a';
391 else if (ch2
>= 'A' && ch2
<= 'F') *result
+= 10 + ch2
- 'A';
397 CF_INLINE Boolean
_haveTestedOriginalString(CFURLRef url
) {
398 return ((url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) != 0) || (url
->_sanatizedString
!= NULL
);
401 typedef CFStringRef (*StringTransformation
)(CFAllocatorRef
, CFStringRef
, CFIndex
);
402 static CFArrayRef
copyStringArrayWithTransformation(CFArrayRef array
, StringTransformation transformation
) {
403 CFAllocatorRef alloc
= CFGetAllocator(array
);
404 CFMutableArrayRef mArray
= NULL
;
405 CFIndex i
, c
= CFArrayGetCount(array
);
406 for (i
= 0; i
< c
; i
++) {
407 CFStringRef origComp
= CFArrayGetValueAtIndex(array
, i
);
408 CFStringRef unescapedComp
= transformation(alloc
, origComp
, i
);
409 if (!unescapedComp
) {
412 if (unescapedComp
!= origComp
) {
414 mArray
= CFArrayCreateMutableCopy(alloc
, c
, array
);
416 CFArraySetValueAtIndex(mArray
, i
, unescapedComp
);
418 CFRelease(unescapedComp
);
421 if (mArray
) CFRelease(mArray
);
431 // Returns NULL if str cannot be converted for whatever reason, str if str contains no characters in need of escaping, or a newly-created string with the appropriate % escape codes in place. Caller must always release the returned string.
432 CF_INLINE CFStringRef
_replacePathIllegalCharacters(CFStringRef str
, CFAllocatorRef alloc
, Boolean preserveSlashes
) {
433 if (preserveSlashes
) {
434 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?"), kCFStringEncodingUTF8
);
436 return CFURLCreateStringByAddingPercentEscapes(alloc
, str
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
440 static CFStringRef
escapePathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
441 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
444 static CFStringRef
escapeWindowsPathComponent(CFAllocatorRef alloc
, CFStringRef origComponent
, CFIndex componentIndex
) {
445 if (CFStringGetLength(origComponent
) == 2 && CFStringGetCharacterAtIndex(origComponent
, 1) == '|') {
446 // Don't corrupt a drive letter component
447 CFRetain(origComponent
);
448 return origComponent
;
450 return CFURLCreateStringByAddingPercentEscapes(alloc
, origComponent
, NULL
, CFSTR(";?/"), kCFStringEncodingUTF8
);
454 // 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
455 static Boolean
_hackToConvertSurrogates(UniChar highChar
, UniChar lowChar
, CFMutableStringRef str
) {
456 UniChar surrogate
[2];
457 uint8_t bytes
[6]; // Aki sez it should never take more than 6 bytes
460 surrogate
[0] = highChar
;
461 surrogate
[1] = lowChar
;
462 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8
, 0, surrogate
, 2, NULL
, bytes
, 6, &len
) != kCFStringEncodingConversionSuccess
) {
465 for (currByte
= bytes
; currByte
< bytes
+ len
; currByte
++) {
466 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
467 unsigned char high
, low
;
468 high
= ((*currByte
) & 0xf0) >> 4;
469 low
= (*currByte
) & 0x0f;
470 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
471 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
472 CFStringAppendCharacters(str
, escapeSequence
, 3);
477 static Boolean
_appendPercentEscapesForCharacter(UniChar ch
, CFStringEncoding encoding
, CFMutableStringRef str
) {
478 uint8_t bytes
[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
479 uint8_t *bytePtr
= bytes
, *currByte
;
481 CFAllocatorRef alloc
= NULL
;
482 if (CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, 6, &byteLength
) != kCFStringEncodingConversionSuccess
) {
483 byteLength
= CFStringEncodingByteLengthForCharacters(encoding
, 0, &ch
, 1);
484 if (byteLength
<= 6) {
485 // The encoding cannot accomodate the character
488 alloc
= CFGetAllocator(str
);
489 bytePtr
= CFAllocatorAllocate(alloc
, byteLength
, 0);
490 if (!bytePtr
|| CFStringEncodingUnicodeToBytes(encoding
, 0, &ch
, 1, NULL
, bytePtr
, byteLength
, &byteLength
) != kCFStringEncodingConversionSuccess
) {
491 if (bytePtr
) CFAllocatorDeallocate(alloc
, bytePtr
);
495 for (currByte
= bytePtr
; currByte
< bytePtr
+ byteLength
; currByte
++) {
496 UniChar escapeSequence
[3] = {'%', '\0', '\0'};
497 unsigned char high
, low
;
498 high
= ((*currByte
) & 0xf0) >> 4;
499 low
= (*currByte
) & 0x0f;
500 escapeSequence
[1] = (high
< 10) ? '0' + high
: 'A' + high
- 10;
501 escapeSequence
[2] = (low
< 10) ? '0' + low
: 'A' + low
- 10;
502 CFStringAppendCharacters(str
, escapeSequence
, 3);
504 if (bytePtr
!= bytes
) {
505 CFAllocatorDeallocate(alloc
, bytePtr
);
510 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
511 CFStringRef
CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
) {
512 CFMutableStringRef newStr
= NULL
;
515 CFRange percentRange
, searchRange
;
516 CFStringRef escapedStr
= NULL
;
517 CFMutableStringRef strForEscapedChar
= NULL
;
519 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
520 Boolean failed
= false;
522 if (!originalString
) return NULL
;
524 if (charactersToLeaveEscaped
== NULL
) {
525 return CFStringCreateCopy(alloc
, originalString
);
528 length
= CFStringGetLength(originalString
);
529 searchRange
= CFRangeMake(0, length
);
531 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
532 uint8_t bytes
[4]; // Single UTF-8 character could require up to 4 bytes.
533 uint8_t numBytesExpected
;
537 // Make sure we have at least 2 more characters
538 if (length
- percentRange
.location
< 3) { failed
= true; break; }
540 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
541 // so we assume the percent character is legit, and let it pass into the string
542 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+1);
543 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+2);
544 if (!_translateBytes(ch1
, ch2
, bytes
)) { failed
= true; break; }
545 if (!(bytes
[0] & 0x80)) {
546 numBytesExpected
= 1;
547 } else if (!(bytes
[0] & 0x20)) {
548 numBytesExpected
= 2;
549 } else if (!(bytes
[0] & 0x10)) {
550 numBytesExpected
= 3;
552 numBytesExpected
= 4;
554 if (numBytesExpected
== 1) {
555 // one byte sequence (most common case); handle this specially
556 escapedChar
= bytes
[0];
557 if (!strForEscapedChar
) {
558 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
560 escapedStr
= strForEscapedChar
;
563 // Make sure up front that we have enough characters
564 if (length
< percentRange
.location
+ numBytesExpected
* 3) { failed
= true; break; }
565 for (j
= 1; j
< numBytesExpected
; j
++) {
566 if (CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
) != '%') { failed
= true; break; }
567 ch1
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 1);
568 ch2
= CFStringGetCharacterAtIndex(originalString
, percentRange
.location
+ 3*j
+ 2);
569 if (!_translateBytes(ch1
, ch2
, bytes
+j
)) { failed
= true; break; }
572 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
573 escapedStr
= CFStringCreateWithBytes(alloc
, bytes
, numBytesExpected
, kCFStringEncodingUTF8
, false);
576 } else if (CFStringGetLength(escapedStr
) == 0 && numBytesExpected
== 3 && bytes
[0] == 0xef && bytes
[1] == 0xbb && bytes
[2] == 0xbf) {
577 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
578 escapedChar
= 0xfeff;
579 if (!strForEscapedChar
) {
580 strForEscapedChar
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &escapedChar
, 1, 1, kCFAllocatorNull
);
582 CFRelease(escapedStr
);
583 escapedStr
= strForEscapedChar
;
588 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
589 searchRange
.location
= percentRange
.location
+ 3 * numBytesExpected
;
590 searchRange
.length
= length
- searchRange
.location
;
593 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
!= kCFNotFound
) {
594 if (escapedStr
!= strForEscapedChar
) {
595 CFRelease(escapedStr
);
603 newStr
= CFStringCreateMutable(alloc
, length
);
605 if (percentRange
.location
- mark
> 0) {
606 // The creation of this temporary string is unfortunate.
607 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
608 CFStringAppend(newStr
, substring
);
609 CFRelease(substring
);
611 CFStringAppend(newStr
, escapedStr
);
612 if (escapedStr
!= strForEscapedChar
) {
613 CFRelease(escapedStr
);
616 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
619 if (escapedStr
&& escapedStr
!= strForEscapedChar
) CFRelease(escapedStr
);
620 if (strForEscapedChar
) CFRelease(strForEscapedChar
);
622 if (newStr
) CFRelease(newStr
);
626 // Need to cat on the remainder of the string
627 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
628 CFStringAppend(newStr
, substring
);
629 CFRelease(substring
);
633 return CFStringCreateCopy(alloc
, originalString
);
638 CFStringRef
CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc
, CFStringRef originalString
, CFStringRef charactersToLeaveEscaped
, CFStringEncoding enc
) {
639 if (enc
== kCFStringEncodingUTF8
) {
640 return CFURLCreateStringByReplacingPercentEscapes(alloc
, originalString
, charactersToLeaveEscaped
);
642 CFMutableStringRef newStr
= NULL
;
643 CFMutableStringRef escapedStr
= NULL
;
646 CFRange percentRange
, searchRange
;
647 Boolean escapeAll
= (charactersToLeaveEscaped
&& CFStringGetLength(charactersToLeaveEscaped
) == 0);
648 Boolean failed
= false;
649 uint8_t byteBuffer
[8];
650 uint8_t *bytes
= byteBuffer
;
651 int capacityOfBytes
= 8;
653 if (!originalString
) return NULL
;
655 if (charactersToLeaveEscaped
== NULL
) {
656 return CFStringCreateCopy(alloc
, originalString
);
659 length
= CFStringGetLength(originalString
);
660 searchRange
= CFRangeMake(0, length
);
662 while (!failed
&& CFStringFindWithOptions(originalString
, CFSTR("%"), searchRange
, 0, &percentRange
)) {
664 CFIndex percentLoc
= percentRange
.location
;
665 CFStringRef convertedString
;
666 int numBytesUsed
= 0;
668 // Make sure we have at least 2 more characters
669 if (length
- percentLoc
< 3) { failed
= true; break; }
671 if (numBytesUsed
== capacityOfBytes
) {
672 if (bytes
== byteBuffer
) {
673 bytes
= CFAllocatorAllocate(alloc
, 16 * sizeof(uint8_t), 0);
674 memmove(bytes
, byteBuffer
, capacityOfBytes
);
675 capacityOfBytes
= 16;
677 capacityOfBytes
= 2*capacityOfBytes
;
678 bytes
= CFAllocatorReallocate(alloc
, bytes
, capacityOfBytes
* sizeof(uint8_t), 0);
682 ch1
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
684 ch2
= CFStringGetCharacterAtIndex(originalString
, percentLoc
);
686 if (!_translateBytes(ch1
, ch2
, bytes
+ numBytesUsed
)) { failed
= true; break; }
688 } while (CFStringGetCharacterAtIndex(originalString
, percentLoc
) == '%');
689 searchRange
.location
= percentLoc
;
690 searchRange
.length
= length
- searchRange
.location
;
693 convertedString
= CFStringCreateWithBytes(alloc
, bytes
, numBytesUsed
, enc
, false);
694 if (!convertedString
) {
700 newStr
= CFStringCreateMutable(alloc
, length
);
702 if (percentRange
.location
- mark
> 0) {
703 // The creation of this temporary string is unfortunate.
704 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, percentRange
.location
- mark
));
705 CFStringAppend(newStr
, substring
);
706 CFRelease(substring
);
710 CFStringAppend(newStr
, convertedString
);
711 CFRelease(convertedString
);
713 CFIndex i
, c
= CFStringGetLength(convertedString
);
715 escapedStr
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, &ch1
, 1, 1, kCFAllocatorNull
);
717 for (i
= 0; i
< c
; i
++) {
718 ch1
= CFStringGetCharacterAtIndex(convertedString
, i
);
719 if (CFStringFind(charactersToLeaveEscaped
, escapedStr
, 0).location
== kCFNotFound
) {
720 CFStringAppendCharacters(newStr
, &ch1
, 1);
722 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
723 _appendPercentEscapesForCharacter(ch1
, enc
, newStr
);
727 mark
= searchRange
.location
;// We need mark to be the index of the first character beyond the escape sequence
730 if (escapedStr
) CFRelease(escapedStr
);
731 if (bytes
!= byteBuffer
) CFAllocatorDeallocate(alloc
, bytes
);
733 if (newStr
) CFRelease(newStr
);
737 // Need to cat on the remainder of the string
738 CFStringRef substring
= CFStringCreateWithSubstring(alloc
, originalString
, CFRangeMake(mark
, length
- mark
));
739 CFStringAppend(newStr
, substring
);
740 CFRelease(substring
);
744 return CFStringCreateCopy(alloc
, originalString
);
750 static CFStringRef
_addPercentEscapesToString(CFAllocatorRef allocator
, CFStringRef originalString
, Boolean (*shouldReplaceChar
)(UniChar
, void*), CFIndex (*handlePercentChar
)(CFIndex
, CFStringRef
, CFStringRef
*, void *), CFStringEncoding encoding
, void *context
) {
751 CFMutableStringRef newString
= NULL
;
753 CFStringInlineBuffer buf
;
755 if (!originalString
) return NULL
;
756 length
= CFStringGetLength(originalString
);
757 if (length
== 0) return CFStringCreateCopy(allocator
, originalString
);
758 CFStringInitInlineBuffer(originalString
, &buf
, CFRangeMake(0, length
));
760 for (idx
= 0; idx
< length
; idx
++) {
761 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
762 Boolean shouldReplace
= shouldReplaceChar(ch
, context
);
764 // Perform the replacement
766 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
767 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
769 if (!_appendPercentEscapesForCharacter(ch
, encoding
, newString
)) {
770 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
771 if (encoding
== kCFStringEncodingUTF8
&& CFCharacterSetIsSurrogateHighCharacter(ch
) && idx
+ 1 < length
&& CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1))) {
772 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
773 if (_hackToConvertSurrogates(ch
, CFStringGetCharacterFromInlineBuffer(&buf
, idx
+1), newString
)) {
774 idx
++; // We consumed 2 characters, not 1
782 } else if (ch
== '%' && handlePercentChar
) {
783 CFStringRef replacementString
= NULL
;
784 CFIndex newIndex
= handlePercentChar(idx
, originalString
, &replacementString
, context
);
787 } else if (replacementString
) {
789 newString
= CFStringCreateMutableCopy(CFGetAllocator(originalString
), 0, originalString
);
790 CFStringDelete(newString
, CFRangeMake(idx
, length
-idx
));
792 CFStringAppend(newString
, replacementString
);
793 CFRelease(replacementString
);
795 if (newIndex
== idx
) {
797 CFStringAppendCharacters(newString
, &ch
, 1);
800 if (!replacementString
&& newString
) {
802 for (tmpIndex
= idx
; tmpIndex
< newIndex
; tmpIndex
++) {
803 ch
= CFStringGetCharacterAtIndex(originalString
, idx
);
804 CFStringAppendCharacters(newString
, &ch
, 1);
809 } else if (newString
) {
810 CFStringAppendCharacters(newString
, &ch
, 1);
814 // Ran in to an encoding failure
815 if (newString
) CFRelease(newString
);
817 } else if (newString
) {
820 return CFStringCreateCopy(CFGetAllocator(originalString
), originalString
);
825 static Boolean
_stringContainsCharacter(CFStringRef string
, UniChar ch
) {
826 CFIndex i
, c
= CFStringGetLength(string
);
827 CFStringInlineBuffer buf
;
828 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, c
));
829 for (i
= 0; i
< c
; i
++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, i
) == ch
) return true;
833 static Boolean
_shouldPercentReplaceChar(UniChar ch
, void *context
) {
834 CFStringRef unescape
= ((CFStringRef
*)context
)[0];
835 CFStringRef escape
= ((CFStringRef
*)context
)[1];
836 Boolean shouldReplace
= (isURLLegalCharacter(ch
) == false);
838 if (unescape
&& _stringContainsCharacter(unescape
, ch
)) {
839 shouldReplace
= false;
841 } else if (escape
&& _stringContainsCharacter(escape
, ch
)) {
842 shouldReplace
= true;
844 return shouldReplace
;
847 CF_EXPORT CFStringRef
CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator
, CFStringRef originalString
, CFStringRef charactersToLeaveUnescaped
, CFStringRef legalURLCharactersToBeEscaped
, CFStringEncoding encoding
) {
848 CFStringRef strings
[2];
849 strings
[0] = charactersToLeaveUnescaped
;
850 strings
[1] = legalURLCharactersToBeEscaped
;
851 return _addPercentEscapesToString(allocator
, originalString
, _shouldPercentReplaceChar
, NULL
, encoding
, strings
);
854 static Boolean
__CFURLEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
857 UInt32 pathType1
, pathType2
;
859 __CFGenericValidateType(cf1
, CFURLGetTypeID());
860 __CFGenericValidateType(cf2
, CFURLGetTypeID());
862 if (url1
== url2
) return true;
863 if ((url1
->_flags
& IS_PARSED
) && (url2
->_flags
& IS_PARSED
) && (url1
->_flags
& IS_DIRECTORY
) != (url2
->_flags
& IS_DIRECTORY
)) return false;
865 if (!url2
->_base
) return false;
866 if (!CFEqual(url1
->_base
, url2
->_base
)) return false;
867 } else if (url2
->_base
) {
871 pathType1
= URL_PATH_TYPE(url1
);
872 pathType2
= URL_PATH_TYPE(url2
);
873 if (pathType1
== pathType2
) {
874 if (pathType1
!= FULL_URL_REPRESENTATION
) {
875 return CFEqual(url1
->_string
, url2
->_string
);
877 // Do not compare the original strings; compare the sanatized strings.
878 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
881 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
882 CFStringRef scheme1
= CFURLCopyScheme(url1
);
883 CFStringRef scheme2
= CFURLCopyScheme(url2
);
885 if (scheme1
&& scheme2
) {
886 eq
= CFEqual(scheme1
, scheme2
);
889 } else if (!scheme1
&& !scheme2
) {
893 if (scheme1
) CFRelease(scheme1
);
894 else CFRelease(scheme2
);
896 if (!eq
) return false;
898 if (pathType1
== FULL_URL_REPRESENTATION
) {
899 if (!(url1
->_flags
& IS_PARSED
)) {
900 _parseComponentsOfURL(url1
);
902 if (url1
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
907 if (pathType2
== FULL_URL_REPRESENTATION
) {
908 if (!(url2
->_flags
& IS_PARSED
)) {
909 _parseComponentsOfURL(url2
);
911 if (url2
->_flags
& (HAS_USER
| HAS_PORT
| HAS_PASSWORD
| HAS_QUERY
| HAS_PARAMETERS
| HAS_FRAGMENT
)) {
916 // No help for it; we now must convert to the canonical representation and compare.
917 return CFEqual(CFURLGetString(url1
), CFURLGetString(url2
));
921 static UInt32
__CFURLHash(CFTypeRef cf
) {
922 /* This is tricky, because we do not want the hash value to change as a file system URL is changed to its canonical representation, nor do we wish to force the conversion to the canonical representation. We choose instead to take the last path component (or "/" in the unlikely case that the path is empty), then hash on that. */
925 if (CFURLCanBeDecomposed(url
)) {
926 CFStringRef lastComp
= CFURLCopyLastPathComponent(url
);
928 result
= CFHash(lastComp
);
934 result
= CFHash(CFURLGetString(url
));
939 static CFStringRef
__CFURLCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
941 __CFGenericValidateType(cf
, CFURLGetTypeID());
943 CFRetain(url
->_string
);
946 // Do not dereference url->_base; it may be an ObjC object
947 return CFStringCreateWithFormat(CFGetAllocator(url
), NULL
, CFSTR("%@ -- %@"), url
->_string
, url
->_base
);
952 static CFStringRef
__CFURLCopyDescription(CFTypeRef cf
) {
953 CFURLRef url
= (CFURLRef
)cf
;
955 CFAllocatorRef alloc
= CFGetAllocator(url
);
957 CFStringRef baseString
= CFCopyDescription(url
->_base
);
958 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@,\n\tbase = %@}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
, baseString
);
959 CFRelease(baseString
);
961 result
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("<CFURL %p [%p]>{type = %d, string = %@, base = (null)}"), cf
, alloc
, URL_PATH_TYPE(url
), url
->_string
);
966 #if DEBUG_URL_MEMORY_USAGE
967 static CFAllocatorRef URLAllocator
= NULL
;
968 static UInt32 numFileURLsCreated
= 0;
969 static UInt32 numFileURLsConverted
= 0;
970 static UInt32 numFileURLsDealloced
= 0;
971 static UInt32 numURLs
= 0;
972 static UInt32 numDealloced
= 0;
973 void __CFURLDumpMemRecord(void) {
974 CFStringRef str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d URLs created; %d destroyed\n%d file URLs created; %d converted; %d destroyed\n"), numURLs
, numDealloced
, numFileURLsCreated
, numFileURLsConverted
, numFileURLsDealloced
);
977 if (URLAllocator
) CFCountingAllocatorPrintPointers(URLAllocator
);
981 static void __CFURLDeallocate(CFTypeRef cf
) {
983 CFAllocatorRef alloc
;
984 __CFGenericValidateType(cf
, CFURLGetTypeID());
985 alloc
= CFGetAllocator(url
);
986 #if DEBUG_URL_MEMORY_USAGE
988 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
989 numFileURLsDealloced
++;
992 CFRelease(url
->_string
);
993 if (url
->_base
) CFRelease(url
->_base
);
994 if (url
->ranges
) CFAllocatorDeallocate(alloc
, url
->ranges
);
995 if (url
->_sanatizedString
) CFRelease(url
->_sanatizedString
);
998 static CFTypeID __kCFURLTypeID
= _kCFRuntimeNotATypeID
;
1000 static const CFRuntimeClass __CFURLClass
= {
1008 __CFURLCopyFormattingDescription
,
1009 __CFURLCopyDescription
1012 CONST_STRING_DECL(kCFURLFileScheme
, "file")
1013 CONST_STRING_DECL(kCFURLLocalhost
, "localhost")
1015 __private_extern__
void __CFURLInitialize(void) {
1016 __kCFURLTypeID
= _CFRuntimeRegisterClass(&__CFURLClass
);
1019 /* Toll-free bridging support; get the true CFURL from an NSURL */
1020 CF_INLINE CFURLRef
_CFURLFromNSURL(CFURLRef url
) {
1021 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, url
, "_cfurl");
1025 CFTypeID
CFURLGetTypeID(void) {
1026 return __kCFURLTypeID
;
1029 __private_extern__
void CFShowURL(CFURLRef url
) {
1034 printf("<CFURL 0x%x>{", (unsigned)url
);
1035 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
1036 printf("ObjC bridged object}\n");
1039 printf("\n\tPath type: ");
1040 switch (URL_PATH_TYPE(url
)) {
1041 case kCFURLPOSIXPathStyle
:
1044 case kCFURLHFSPathStyle
:
1047 case kCFURLWindowsPathStyle
:
1050 case FULL_URL_REPRESENTATION
:
1051 printf("Native URL");
1054 printf("UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url
));
1056 printf("\n\tRelative string: ");
1057 CFShow(url
->_string
);
1058 printf("\tBase URL: ");
1060 printf("<0x%x> ", (unsigned)url
->_base
);
1065 printf("\tFlags: 0x%x\n}\n", (unsigned)url
->_flags
);
1069 /***************************************************/
1070 /* URL creation and String/Data creation from URLS */
1071 /***************************************************/
1072 static void constructBuffers(CFAllocatorRef alloc
, CFStringRef string
, const unsigned char **cstring
, const UniChar
**ustring
, Boolean
*useCString
, Boolean
*freeCharacters
) {
1073 CFIndex neededLength
;
1077 *cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingISOLatin1
);
1081 *freeCharacters
= false;
1085 *ustring
= CFStringGetCharactersPtr(string
);
1087 *useCString
= false;
1088 *freeCharacters
= false;
1092 *freeCharacters
= true;
1093 length
= CFStringGetLength(string
);
1094 rg
= CFRangeMake(0, length
);
1095 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, NULL
, INT_MAX
, &neededLength
);
1096 if (neededLength
== length
) {
1097 char *buf
= CFAllocatorAllocate(alloc
, length
, 0);
1098 CFStringGetBytes(string
, rg
, kCFStringEncodingISOLatin1
, 0, false, buf
, length
, NULL
);
1102 UniChar
*buf
= CFAllocatorAllocate(alloc
, length
* sizeof(UniChar
), 0);
1103 CFStringGetCharacters(string
, rg
, buf
);
1104 *useCString
= false;
1109 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1110 static void _parseComponents(CFAllocatorRef alloc
, CFStringRef string
, CFURLRef baseURL
, UInt32
*theFlags
, CFRange
**range
) {
1112 /* 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. */
1114 CFIndex idx
, base_idx
= 0;
1115 CFIndex string_length
;
1116 UInt32 flags
= (IS_PARSED
| *theFlags
);
1117 Boolean useCString
, freeCharacters
, isCompliant
;
1118 uint8_t numRanges
= 0;
1119 const unsigned char *cstring
= NULL
;
1120 const UniChar
*ustring
= NULL
;
1122 string_length
= CFStringGetLength(string
);
1123 constructBuffers(alloc
, string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1125 // Algorithm is as described in RFC 1808
1126 // 1: parse the fragment; remainder after left-most "#" is fragment
1127 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1128 if ('#' == STRING_CHAR(idx
)) {
1129 flags
|= HAS_FRAGMENT
;
1130 ranges
[8].location
= idx
+ 1;
1131 ranges
[8].length
= string_length
- (idx
+ 1);
1133 string_length
= idx
; // remove fragment from parse string
1137 // 2: parse the scheme
1138 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1139 UniChar ch
= STRING_CHAR(idx
);
1141 flags
|= HAS_SCHEME
;
1142 flags
|= IS_ABSOLUTE
;
1143 ranges
[0].location
= base_idx
;
1144 ranges
[0].length
= idx
;
1148 } else if (!scheme_valid(ch
)) {
1149 break; // invalid scheme character -- no scheme
1153 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1154 if (!(flags
& HAS_SCHEME
)) {
1156 } else if (!(base_idx
< string_length
)) {
1157 isCompliant
= false;
1158 } else if (STRING_CHAR(base_idx
) != '/') {
1159 isCompliant
= false;
1165 // Clear the fragment flag if it's been set
1166 if (flags
& HAS_FRAGMENT
) {
1167 flags
&= (~HAS_FRAGMENT
);
1168 string_length
= CFStringGetLength(string
);
1170 (*theFlags
) = flags
;
1171 (*range
) = CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
1172 (*range
)->location
= ranges
[0].location
;
1173 (*range
)->length
= ranges
[0].length
;
1174 if (freeCharacters
) {
1175 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1180 // URL is 1808-compliant
1181 flags
|= IS_DECOMPOSABLE
;
1183 // 3: parse the network location and login
1184 if (2 <= (string_length
- base_idx
) && '/' == STRING_CHAR(base_idx
) && '/' == STRING_CHAR(base_idx
+1)) {
1185 CFIndex base
= 2 + base_idx
, extent
;
1186 Boolean insideIPV6Host
= false;
1187 for (idx
= base
; idx
< string_length
; idx
++) {
1188 if ('/' == STRING_CHAR(idx
) || '?' == STRING_CHAR(idx
)) break;
1192 // net_loc parts extend from base to extent (but not including), which might be to end of string
1193 // net location is "<user>:<password>@<host>:<port>"
1194 if (extent
!= base
) {
1195 for (idx
= base
; idx
< extent
; idx
++) {
1196 if ('@' == STRING_CHAR(idx
)) { // there is a user
1200 ranges
[1].location
= base
; // base of the user
1201 for (idx2
= base
; idx2
< idx
; idx2
++) {
1202 if (':' == STRING_CHAR(idx2
)) { // found a password separator
1203 flags
|= HAS_PASSWORD
;
1205 ranges
[2].location
= idx2
+1; // base of the password
1206 ranges
[2].length
= idx
-(idx2
+1); // password extent
1207 ranges
[1].length
= idx2
- base
; // user extent
1211 if (!(flags
& HAS_PASSWORD
)) {
1212 // user extends to the '@'
1213 ranges
[1].length
= idx
- base
; // user extent
1221 ranges
[3].location
= base
; // base of host
1223 // base has been advanced past the user and password if they existed
1224 for (idx
= base
; idx
< extent
; idx
++) {
1225 // IPV6 support (RFC 2732) DCJ June/10/2002
1226 if ('[' == STRING_CHAR(idx
)) { // starting IPV6 explicit address
1227 insideIPV6Host
= true;
1228 flags
|= IS_IPV6_ENCODED
;
1230 if (']' == STRING_CHAR(idx
)) { // ending IPV6 explicit address
1231 insideIPV6Host
= false;
1233 // there is a port if we see a colon outside ipv6 address
1234 if (!insideIPV6Host
&& ':' == STRING_CHAR(idx
)) {
1237 ranges
[4].location
= idx
+1; // base of port
1238 ranges
[4].length
= extent
- (idx
+1); // port extent
1239 ranges
[3].length
= idx
- base
; // host extent
1243 if (!(flags
& HAS_PORT
)) {
1244 ranges
[3].length
= extent
- base
; // host extent
1250 // 4: parse the query; remainder after left-most "?" is query
1251 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1252 if ('?' == STRING_CHAR(idx
)) {
1255 ranges
[7].location
= idx
+ 1;
1256 ranges
[7].length
= string_length
- (idx
+1);
1257 string_length
= idx
; // remove query from parse string
1262 // 5: parse the parameters; remainder after left-most ";" is parameters
1263 for (idx
= base_idx
; idx
< string_length
; idx
++) {
1264 if (';' == STRING_CHAR(idx
)) {
1265 flags
|= HAS_PARAMETERS
;
1267 ranges
[6].location
= idx
+ 1;
1268 ranges
[6].length
= string_length
- (idx
+1);
1269 string_length
= idx
; // remove parameters from parse string
1274 // 6: parse the path; it's whatever's left between string_length & base_idx
1275 if (string_length
- base_idx
!= 0 || (flags
& NET_LOCATION_MASK
))
1277 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1283 pathRg
.location
= base_idx
;
1284 pathRg
.length
= string_length
- base_idx
;
1287 if (pathRg
.length
> 0) {
1288 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 1);
1291 } else if (ch
== '.') {
1292 if (pathRg
.length
== 1) {
1295 ch
= STRING_CHAR(pathRg
.location
+ pathRg
.length
- 2);
1298 } else if (ch
!= '.') {
1300 } else if (pathRg
.length
== 2) {
1303 isDir
= (STRING_CHAR(pathRg
.location
+ pathRg
.length
- 3) == '/');
1310 isDir
= (baseURL
!= NULL
) ? CFURLHasDirectoryPath(baseURL
) : false;
1313 flags
|= IS_DIRECTORY
;
1317 if (freeCharacters
) {
1318 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1320 (*theFlags
) = flags
;
1321 (*range
) = CFAllocatorAllocate(alloc
, sizeof(CFRange
)*numRanges
, 0);
1323 for (idx
= 0, flags
= 1; flags
!= (1<<9); flags
= (flags
<<1), idx
++) {
1324 if ((*theFlags
) & flags
) {
1325 (*range
)[numRanges
] = ranges
[idx
];
1329 if (((*theFlags
) & HAS_PATH
) && !CFStringFindWithOptions(string
, CFSTR("%"), ranges
[5], 0, NULL
)) {
1330 (*theFlags
) |= POSIX_AND_URL_PATHS_MATCH
;
1334 static Boolean
scanCharacters(CFAllocatorRef alloc
, CFMutableStringRef
*escapedString
, UInt32
*flags
, const unsigned char *cstring
, const UniChar
*ustring
, Boolean useCString
, CFIndex base
, CFIndex end
, CFIndex
*mark
, UInt32 componentFlag
, CFStringEncoding encoding
) {
1336 Boolean sawIllegalChar
= false;
1337 for (idx
= base
; idx
< end
; idx
++) {
1338 Boolean shouldEscape
;
1339 UniChar ch
= STRING_CHAR(idx
);
1340 if (isURLLegalCharacter(ch
)) {
1341 if ((componentFlag
== HAS_USER
|| componentFlag
== HAS_PASSWORD
) && (ch
== '/' || ch
== '?' || ch
== '@')) {
1342 shouldEscape
= true;
1344 shouldEscape
= false;
1346 } else if (ch
== '%' && idx
+ 2 < end
&& isHexDigit(STRING_CHAR(idx
+ 1)) && isHexDigit(STRING_CHAR(idx
+2))) {
1347 shouldEscape
= false;
1348 } else if (componentFlag
== HAS_HOST
&& ((idx
== base
&& ch
== '[') || (idx
== end
-1 && ch
== ']'))) {
1349 shouldEscape
= false;
1351 shouldEscape
= true;
1353 if (!shouldEscape
) continue;
1355 sawIllegalChar
= true;
1356 if (componentFlag
&& flags
) {
1357 *flags
|= (componentFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
);
1359 if (!*escapedString
) {
1360 *escapedString
= CFStringCreateMutable(alloc
, 0);
1363 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, &(cstring
[*mark
]), idx
- *mark
, kCFStringEncodingISOLatin1
, false);
1364 CFStringAppend(*escapedString
, tempString
);
1365 CFRelease(tempString
);
1367 CFStringAppendCharacters(*escapedString
, &(ustring
[*mark
]), idx
- *mark
);
1370 _appendPercentEscapesForCharacter(ch
, encoding
, *escapedString
); // This can never fail because anURL->_string was constructed from the encoding passed in
1372 return sawIllegalChar
;
1375 static void computeSanitizedString(CFURLRef url
) {
1376 CFAllocatorRef alloc
= CFGetAllocator(url
);
1377 CFIndex string_length
= CFStringGetLength(url
->_string
);
1378 Boolean useCString
, freeCharacters
;
1379 const unsigned char *cstring
= NULL
;
1380 const UniChar
*ustring
= NULL
;
1381 CFIndex base
; // where to scan from
1382 CFIndex mark
; // first character not-yet copied to sanitized string
1383 if (!(url
->_flags
& IS_PARSED
)) {
1384 _parseComponentsOfURL(url
);
1386 constructBuffers(alloc
, url
->_string
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1387 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
1388 // Impossible to have a problem character in the scheme
1389 base
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
).length
+ 1;
1391 if (!scanCharacters(alloc
, &(((struct __CFURL
*)url
)->_sanatizedString
), &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, base
, string_length
, &mark
, 0, url
->_encoding
)) {
1392 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1395 // Go component by component
1396 CFIndex currentComponent
= HAS_USER
;
1398 while (currentComponent
< (HAS_FRAGMENT
<< 1)) {
1399 CFRange componentRange
= _rangeForComponent(url
->_flags
, url
->ranges
, currentComponent
);
1400 if (componentRange
.location
!= kCFNotFound
) {
1401 scanCharacters(alloc
, &(((struct __CFURL
*)url
)->_sanatizedString
), &(((struct __CFURL
*)url
)->_flags
), cstring
, ustring
, useCString
, componentRange
.location
, componentRange
.location
+ componentRange
.length
, &mark
, currentComponent
, url
->_encoding
);
1403 currentComponent
= currentComponent
<< 1;
1405 if (!url
->_sanatizedString
) {
1406 ((struct __CFURL
*)url
)->_flags
|= ORIGINAL_AND_URL_STRINGS_MATCH
;
1409 if (url
->_sanatizedString
&& mark
!= string_length
) {
1411 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, &(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1412 CFStringAppend(url
->_sanatizedString
, tempString
);
1413 CFRelease(tempString
);
1415 CFStringAppendCharacters(url
->_sanatizedString
, &(ustring
[mark
]), string_length
- mark
);
1418 if (freeCharacters
) {
1419 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1424 static CFStringRef
correctedComponent(CFStringRef comp
, UInt32 compFlag
, CFStringEncoding enc
) {
1425 CFAllocatorRef alloc
= CFGetAllocator(comp
);
1426 CFIndex string_length
= CFStringGetLength(comp
);
1427 Boolean useCString
, freeCharacters
;
1428 const unsigned char *cstring
= NULL
;
1429 const UniChar
*ustring
= NULL
;
1430 CFIndex mark
= 0; // first character not-yet copied to sanitized string
1431 CFMutableStringRef result
= NULL
;
1433 constructBuffers(alloc
, comp
, &cstring
, &ustring
, &useCString
, &freeCharacters
);
1434 scanCharacters(alloc
, &result
, NULL
, cstring
, ustring
, useCString
, 0, string_length
, &mark
, compFlag
, enc
);
1436 if (mark
< string_length
) {
1438 CFStringRef tempString
= CFStringCreateWithBytes(alloc
, &(cstring
[mark
]), string_length
- mark
, kCFStringEncodingISOLatin1
, false);
1439 CFStringAppend(result
, tempString
);
1440 CFRelease(tempString
);
1442 CFStringAppendCharacters(result
, &(ustring
[mark
]), string_length
- mark
);
1446 // This should nevr happen
1448 result
= (CFMutableStringRef
)comp
;
1450 if (freeCharacters
) {
1451 CFAllocatorDeallocate(alloc
, useCString
? (void *)cstring
: (void *)ustring
);
1458 CF_EXPORT CFURLRef
_CFURLAlloc(CFAllocatorRef allocator
) {
1459 struct __CFURL
*url
;
1460 #if DEBUG_URL_MEMORY_USAGE
1462 if (!URLAllocator
) {
1463 URLAllocator
= CFCountingAllocatorCreate(NULL
);
1465 allocator
= URLAllocator
;
1467 url
= (struct __CFURL
*)_CFRuntimeCreateInstance(allocator
, __kCFURLTypeID
, sizeof(struct __CFURL
) - sizeof(CFRuntimeBase
), NULL
);
1470 if (createOldUTF8StyleURLs()) {
1471 url
->_flags
|= IS_OLD_UTF8_STYLE
;
1473 url
->_string
= NULL
;
1476 url
->_reserved
= NULL
;
1477 url
->_encoding
= kCFStringEncodingUTF8
;
1478 url
->_sanatizedString
= NULL
;
1483 // 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.
1484 static void _CFURLInit(struct __CFURL
*url
, CFStringRef URLString
, UInt32 fsType
, CFURLRef base
) {
1485 CFAssert1(URLString
!= NULL
&& CFGetTypeID(URLString
) == CFStringGetTypeID() && CFStringGetLength(URLString
) != 0, __kCFLogAssertion
, "%s(): internal CF error; empty string encountered", __PRETTY_FUNCTION__
);
1486 CFAssert2((fsType
== FULL_URL_REPRESENTATION
) || (fsType
== kCFURLPOSIXPathStyle
) || (fsType
== kCFURLWindowsPathStyle
) || (fsType
== kCFURLHFSPathStyle
), __kCFLogAssertion
, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__
, fsType
);
1488 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1489 url
->_string
= CFStringCreateCopy(CFGetAllocator(url
), URLString
);
1490 url
->_flags
|= (fsType
<< 16);
1491 url
->_base
= base
? CFURLCopyAbsoluteURL(base
) : NULL
;
1492 #if DEBUG_URL_MEMORY_USAGE
1493 if (fsType
!= FULL_URL_REPRESENTATION
) {
1494 numFileURLsCreated
++;
1499 CF_EXPORT
void _CFURLInitFSPath(CFURLRef url
, CFStringRef path
) {
1500 CFIndex len
= CFStringGetLength(path
);
1501 if (len
&& CFStringGetCharacterAtIndex(path
, 0) == '/') {
1502 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, NULL
);
1503 ((struct __CFURL
*)url
)->_flags
|= IS_ABSOLUTE
;
1505 CFURLRef cwdURL
= _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url
));
1506 _CFURLInit((struct __CFURL
*)url
, path
, kCFURLPOSIXPathStyle
, cwdURL
);
1509 if (!len
|| '/' == CFStringGetCharacterAtIndex(path
, len
- 1))
1510 ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
1513 // Exported for Foundation's use
1514 CF_EXPORT Boolean
_CFStringIsLegalURLString(CFStringRef string
) {
1515 // 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.
1516 // 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
1517 CFStringInlineBuffer stringBuffer
;
1518 CFIndex idx
= 0, length
;
1519 Boolean sawHash
= false;
1521 CFAssert(false, __kCFLogAssertion
, "Cannot create an CFURL from a NULL string");
1524 length
= CFStringGetLength(string
);
1525 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
1526 while (idx
< length
) {
1527 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1529 if (ch
>= 'a' && ch
<= 'z') continue;
1530 if (ch
>= '0' && ch
<= '9') continue;
1531 if (ch
>= 'A' && ch
<= 'Z') continue;
1532 if (ch
== '-' || ch
== '_' || ch
== '.' || ch
== '!' || ch
== '~' || ch
== '*' || ch
== '\'' || ch
== '(' || ch
== ')') continue;
1533 if (ch
== ';' || ch
== '/' || ch
== '?' || ch
== ':' || ch
== '@' || ch
== '&' || ch
== '=' || ch
== '+' || ch
== '$' || ch
== ',') continue;
1534 if (ch
== '[' || ch
== ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1540 // Commenting out all the CFAsserts below because they cause the program to abort if running against the debug library. If we have a non-fatal assert, we should use that instead. -- REW 5/20/2002
1542 //CFAssert1(false, __kCFLogAssertion, "Detected illegal URL character 0x%x when trying to create a CFURL", ch);
1545 if (idx
+ 2 > length
) {
1546 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1547 idx
= -1; // To guarantee index < length, and our failure case is triggered
1550 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1552 if (!((ch
>= '0' && ch
<= '9') || (ch
>= 'a' && ch
<= 'f') || (ch
>= 'A' && ch
<= 'F'))) {
1553 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1557 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, idx
);
1559 if (!((ch
>= '0' && ch
<= '9') || (ch
>= 'a' && ch
<= 'f') || (ch
>= 'A' && ch
<= 'F'))) {
1560 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1571 CF_EXPORT
void _CFURLInitWithString(CFURLRef myURL
, CFStringRef string
, CFURLRef baseURL
) {
1572 struct __CFURL
*url
= (struct __CFURL
*)myURL
; // Supress annoying compile warnings
1573 Boolean isAbsolute
= false;
1574 CFRange colon
= CFStringFind(string
, CFSTR(":"), 0);
1575 if (colon
.location
!= kCFNotFound
) {
1578 for (i
= 0; i
< colon
.location
; i
++) {
1579 char ch
= CFStringGetCharacterAtIndex(string
, i
);
1580 if (!scheme_valid(ch
)) {
1586 _CFURLInit(url
, string
, FULL_URL_REPRESENTATION
, isAbsolute
? NULL
: baseURL
);
1588 url
->_flags
|= IS_ABSOLUTE
;
1592 struct __CFURLEncodingTranslationParameters
{
1593 CFStringEncoding fromEnc
;
1594 CFStringEncoding toEnc
;
1595 const UniChar
*addlChars
;
1597 Boolean escapeHighBit
;
1598 Boolean escapePercents
;
1599 Boolean agreesOverASCII
;
1600 Boolean encodingsMatch
;
1603 static Boolean
_shouldEscapeForEncodingConversion(UniChar ch
, void *context
) {
1604 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1605 if (info
->escapeHighBit
&& ch
> 0x7F) {
1607 } else if (ch
== '%' && info
->escapePercents
) {
1609 } else if (info
->addlChars
) {
1610 const UniChar
*escChar
= info
->addlChars
;
1612 for (i
= 0; i
< info
->count
; escChar
++, i
++) {
1613 if (*escChar
== ch
) {
1621 static CFIndex
_convertEscapeSequence(CFIndex percentIndex
, CFStringRef urlString
, CFStringRef
*newString
, void *context
) {
1622 struct __CFURLEncodingTranslationParameters
*info
= (struct __CFURLEncodingTranslationParameters
*)context
;
1623 CFMutableDataRef newData
;
1624 Boolean sawNonASCIICharacter
= false;
1625 CFIndex i
= percentIndex
;
1628 if (info
->encodingsMatch
) return percentIndex
+ 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well
1629 newData
= CFDataCreateMutable(CFGetAllocator(urlString
), 0);
1630 length
= CFStringGetLength(urlString
);
1632 while (i
< length
&& CFStringGetCharacterAtIndex(urlString
, i
) == '%') {
1634 if (i
+2 >= length
|| !_translateBytes(CFStringGetCharacterAtIndex(urlString
, i
+1), CFStringGetCharacterAtIndex(urlString
, i
+2), &byte
)) {
1638 if (byte
> 0x7f) sawNonASCIICharacter
= true;
1639 CFDataAppendBytes(newData
, &byte
, 1);
1642 if (!sawNonASCIICharacter
&& info
->agreesOverASCII
) {
1645 CFStringRef tmp
= CFStringCreateWithBytes(CFGetAllocator(urlString
), CFDataGetBytePtr(newData
), CFDataGetLength(newData
), info
->fromEnc
, false);
1646 CFIndex tmpIndex
, tmpLen
;
1651 tmpLen
= CFStringGetLength(tmp
);
1652 *newString
= CFStringCreateMutable(CFGetAllocator(urlString
), 0);
1653 for (tmpIndex
= 0; tmpIndex
< tmpLen
; tmpIndex
++) {
1654 if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp
, tmpIndex
), info
->toEnc
, (CFMutableStringRef
)(*newString
))) {
1660 if (tmpIndex
< tmpLen
) {
1661 CFRelease(*newString
);
1670 /* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */
1671 static CFStringRef
_convertPercentEscapes(CFStringRef urlString
, CFStringEncoding fromEncoding
, CFStringEncoding toEncoding
, Boolean escapeAllHighBitCharacters
, Boolean escapePercents
, const UniChar
*addlCharsToEscape
, int numAddlChars
) {
1672 struct __CFURLEncodingTranslationParameters context
;
1673 context
.fromEnc
= fromEncoding
;
1674 context
.toEnc
= toEncoding
;
1675 context
.addlChars
= addlCharsToEscape
;
1676 context
.count
= numAddlChars
;
1677 context
.escapeHighBit
= escapeAllHighBitCharacters
;
1678 context
.escapePercents
= escapePercents
;
1679 context
.agreesOverASCII
= (__CFStringEncodingIsSupersetOfASCII(toEncoding
) && __CFStringEncodingIsSupersetOfASCII(fromEncoding
)) ? true : false;
1680 context
.encodingsMatch
= (fromEncoding
== toEncoding
) ? true : false;
1681 return _addPercentEscapesToString(CFGetAllocator(urlString
), urlString
, _shouldEscapeForEncodingConversion
, _convertEscapeSequence
, toEncoding
, &context
);
1684 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1685 CFURLRef
CFURLCreateWithBytes(CFAllocatorRef allocator
, const uint8_t *URLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
) {
1686 CFStringRef urlString
= CFStringCreateWithBytes(allocator
, URLBytes
, length
, encoding
, false);
1688 if (!urlString
|| CFStringGetLength(urlString
) == 0) {
1689 if (urlString
) CFRelease(urlString
);
1692 if (createOldUTF8StyleURLs()) {
1693 if (encoding
!= kCFStringEncodingUTF8
) {
1694 CFStringRef tmp
= _convertPercentEscapes(urlString
, encoding
, kCFStringEncodingUTF8
, false, false, NULL
, 0);
1695 CFRelease(urlString
);
1697 if (!urlString
) return NULL
;
1701 result
= _CFURLAlloc(allocator
);
1703 _CFURLInitWithString(result
, urlString
, baseURL
);
1704 if (encoding
!= kCFStringEncodingUTF8
&& !createOldUTF8StyleURLs()) {
1705 ((struct __CFURL
*)result
)->_encoding
= encoding
;
1708 CFRelease(urlString
); // it's retained by result, now.
1712 CFDataRef
CFURLCreateData(CFAllocatorRef allocator
, CFURLRef url
, CFStringEncoding encoding
, Boolean escapeWhitespace
) {
1713 static const UniChar whitespaceChars
[4] = {' ', '\n', '\r', '\t'};
1714 CFStringRef myStr
= CFURLGetString(url
);
1717 if (url
->_flags
& IS_OLD_UTF8_STYLE
) {
1718 newStr
= (encoding
== kCFStringEncodingUTF8
) ? CFRetain(myStr
) : _convertPercentEscapes(myStr
, kCFStringEncodingUTF8
, encoding
, true, false, escapeWhitespace
? whitespaceChars
: NULL
, escapeWhitespace
? 4 : 0);
1723 result
= CFStringCreateExternalRepresentation(allocator
, newStr
, encoding
, 0);
1728 // Any escape sequences in URLString will be interpreted via UTF-8.
1729 CFURLRef
CFURLCreateWithString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1731 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1732 if (!_CFStringIsLegalURLString(URLString
)) return NULL
;
1733 url
= _CFURLAlloc(allocator
);
1735 _CFURLInitWithString(url
, URLString
, baseURL
);
1740 static CFURLRef
_CFURLCreateWithArbitraryString(CFAllocatorRef allocator
, CFStringRef URLString
, CFURLRef baseURL
) {
1742 if (!URLString
|| CFStringGetLength(URLString
) == 0) return NULL
;
1743 url
= _CFURLAlloc(allocator
);
1745 _CFURLInitWithString(url
, URLString
, baseURL
);
1750 CFURLRef
CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc
, const UInt8
*relativeURLBytes
, CFIndex length
, CFStringEncoding encoding
, CFURLRef baseURL
, Boolean useCompatibilityMode
) {
1751 CFStringRef relativeString
= CFStringCreateWithBytes(alloc
, relativeURLBytes
, length
, encoding
, false);
1752 if (!relativeString
) {
1755 if (!useCompatibilityMode
) {
1756 CFURLRef url
= _CFURLCreateWithArbitraryString(alloc
, relativeString
, baseURL
);
1757 CFRelease(relativeString
);
1758 ((struct __CFURL
*)url
)->_encoding
= encoding
;
1760 CFURLRef absURL
= CFURLCopyAbsoluteURL(url
);
1767 UInt32 absFlags
= 0;
1769 CFStringRef absString
= NULL
;
1770 Boolean absStringIsMutable
= false;
1773 absString
= relativeString
;
1775 UniChar ch
= CFStringGetCharacterAtIndex(relativeString
, 0);
1776 if (ch
== '?' || ch
== ';' || ch
== '#') {
1777 // Nothing but parameter + query + fragment; append to the baseURL string
1778 CFStringRef baseString
;
1779 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1780 baseString
= CFURLGetString(baseURL
);
1782 baseString
= baseURL
->_string
;
1784 absString
= CFStringCreateMutable(alloc
, CFStringGetLength(baseString
) + CFStringGetLength(relativeString
));
1785 CFStringAppend((CFMutableStringRef
)absString
, baseString
);
1786 CFStringAppend((CFMutableStringRef
)absString
, relativeString
);
1787 absStringIsMutable
= true;
1789 UInt32 relFlags
= 0;
1791 CFStringRef relString
= NULL
;
1792 _parseComponents(alloc
, relativeString
, baseURL
, &relFlags
, &relRanges
);
1793 if (relFlags
& HAS_SCHEME
) {
1794 CFStringRef baseScheme
= CFURLCopyScheme(baseURL
);
1795 CFRange relSchemeRange
= _rangeForComponent(relFlags
, relRanges
, HAS_SCHEME
);
1796 if (baseScheme
&& CFStringGetLength(baseScheme
) == relSchemeRange
.length
&& CFStringHasPrefix(relativeString
, baseScheme
)) {
1797 relString
= CFStringCreateWithSubstring(alloc
, relativeString
, CFRangeMake(relSchemeRange
.length
+1, CFStringGetLength(relativeString
) - relSchemeRange
.length
- 1));
1798 CFAllocatorDeallocate(alloc
, relRanges
);
1800 _parseComponents(alloc
, relString
, baseURL
, &relFlags
, &relRanges
);
1802 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
1803 CFRetain(relativeString
);
1804 absString
= relativeString
;
1806 if (baseScheme
) CFRelease(baseScheme
);
1808 CFRetain(relativeString
);
1809 relString
= relativeString
;
1812 if (!CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1813 if (!(baseURL
->_flags
& IS_PARSED
)) {
1814 _parseComponentsOfURL(baseURL
);
1816 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseURL
->_string
, baseURL
->_flags
, baseURL
->ranges
);
1818 CFStringRef baseString
;
1819 UInt32 baseFlags
= 0;
1820 CFRange
*baseRanges
;
1821 if (CF_IS_OBJC(__kCFURLTypeID
, baseURL
)) {
1822 baseString
= CFURLGetString(baseURL
);
1824 baseString
= baseURL
->_string
;
1826 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
1827 absString
= resolveAbsoluteURLString(alloc
, relString
, relFlags
, relRanges
, baseString
, baseFlags
, baseRanges
);
1828 CFAllocatorDeallocate(alloc
, baseRanges
);
1830 absStringIsMutable
= true;
1832 if (relString
) CFRelease(relString
);
1833 CFAllocatorDeallocate(alloc
, relRanges
);
1835 CFRelease(relativeString
);
1837 _parseComponents(alloc
, absString
, NULL
, &absFlags
, &absRanges
);
1838 if (absFlags
& HAS_PATH
) {
1839 CFRange pathRg
= _rangeForComponent(absFlags
, absRanges
, HAS_PATH
);
1840 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
1841 UniChar
*buf
= CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (pathRg
.length
+ 1), NULL
);
1842 CFStringRef newPath
;
1843 CFStringGetCharacters(absString
, pathRg
, buf
);
1844 buf
[pathRg
.length
] = '\0';
1845 newPath
= _resolvedPath(buf
, buf
+ pathRg
.length
, '/', true, false, alloc
);
1846 if (CFStringGetLength(newPath
) != pathRg
.length
) {
1847 if (!absStringIsMutable
) {
1848 CFStringRef tmp
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(absString
), absString
);
1849 CFRelease(absString
);
1852 CFStringReplace((CFMutableStringRef
)absString
, pathRg
, newPath
);
1855 // Do not deallocate buf; newPath took ownership of it.
1857 CFAllocatorDeallocate(alloc
, absRanges
);
1858 absURL
= _CFURLCreateWithArbitraryString(alloc
, absString
, NULL
);
1859 CFRelease(absString
);
1861 ((struct __CFURL
*)absURL
)->_encoding
= encoding
;
1867 /* 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 */
1868 static CFStringRef
_resolvedPath(UniChar
*pathStr
, UniChar
*end
, UniChar pathDelimiter
, Boolean stripLeadingDotDots
, Boolean stripTrailingDelimiter
, CFAllocatorRef alloc
) {
1869 UniChar
*idx
= pathStr
;
1873 if (idx
!= pathStr
) {
1878 } else if (*(idx
+1) == pathDelimiter
) {
1879 if (idx
+ 2 != end
|| idx
!= pathStr
) {
1880 memmove(idx
, idx
+2, (end
-(idx
+2)+1) * sizeof(UniChar
));
1884 // Do not delete the sole path component
1887 } else if (*(idx
+1) == '.' && (idx
+2 == end
|| *(idx
+2) == pathDelimiter
)) {
1888 if (idx
- pathStr
>= 2) {
1889 // 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.
1890 UniChar
*lastDelim
= idx
-2;
1891 while (lastDelim
>= pathStr
&& *lastDelim
!= pathDelimiter
) lastDelim
--;
1893 if (lastDelim
!= idx
&& (idx
-lastDelim
!= 3 || *lastDelim
!= '.' || *(lastDelim
+1) != '.')) {
1894 // We have a genuine component to compact out
1896 unsigned numCharsToMove
= end
- (idx
+3) + 1; // +1 to move the '\0' as well
1897 memmove(lastDelim
, idx
+3, numCharsToMove
* sizeof(UniChar
));
1898 end
-= (idx
+ 3 - lastDelim
);
1901 } else if (lastDelim
!= pathStr
) {
1906 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
1913 } else if (stripLeadingDotDots
) {
1914 if (idx
+ 3 != end
) {
1915 unsigned numCharsToMove
= end
- (idx
+ 3) + 1;
1916 memmove(idx
, idx
+3, numCharsToMove
* sizeof(UniChar
));
1920 // Do not devolve the last path component
1926 while (idx
< end
&& *idx
!= pathDelimiter
) idx
++;
1929 if (stripTrailingDelimiter
&& end
!= pathStr
&& end
-1 != pathStr
&& *(end
-1) == pathDelimiter
) {
1932 return CFStringCreateWithCharactersNoCopy(alloc
, pathStr
, end
- pathStr
, alloc
);
1935 static CFMutableStringRef
resolveAbsoluteURLString(CFAllocatorRef alloc
, CFStringRef relString
, UInt32 relFlags
, CFRange
*relRanges
, CFStringRef baseString
, UInt32 baseFlags
, CFRange
*baseRanges
) {
1936 CFMutableStringRef newString
= CFStringCreateMutable(alloc
, 0);
1937 CFIndex bufLen
= CFStringGetLength(baseString
) + CFStringGetLength(relString
); // Overkill, but guarantees we never allocate again
1938 UniChar
*buf
= CFAllocatorAllocate(alloc
, bufLen
* sizeof(UniChar
), 0);
1941 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_SCHEME
);
1942 if (rg
.location
!= kCFNotFound
) {
1943 CFStringGetCharacters(baseString
, rg
, buf
);
1944 CFStringAppendCharacters(newString
, buf
, rg
.length
);
1945 CFStringAppendCString(newString
, ":", kCFStringEncodingASCII
);
1948 if (relFlags
& NET_LOCATION_MASK
) {
1949 CFStringAppend(newString
, relString
);
1951 CFStringAppendCString(newString
, "//", kCFStringEncodingASCII
);
1952 rg
= _netLocationRange(baseFlags
, baseRanges
);
1953 if (rg
.location
!= kCFNotFound
) {
1954 CFStringGetCharacters(baseString
, rg
, buf
);
1955 CFStringAppendCharacters(newString
, buf
, rg
.length
);
1958 if (relFlags
& HAS_PATH
) {
1959 CFRange relPathRg
= _rangeForComponent(relFlags
, relRanges
, HAS_PATH
);
1960 CFRange basePathRg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
1961 CFStringRef newPath
;
1962 Boolean useRelPath
= false;
1963 Boolean useBasePath
= false;
1964 if (basePathRg
.location
== kCFNotFound
) {
1966 } else if (relPathRg
.length
== 0) {
1968 } else if (CFStringGetCharacterAtIndex(relString
, relPathRg
.location
) == '/') {
1970 } else if (basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) {
1974 newPath
= CFStringCreateWithSubstring(alloc
, relString
, relPathRg
);
1975 } else if (useBasePath
) {
1976 newPath
= CFStringCreateWithSubstring(alloc
, baseString
, basePathRg
);
1978 // #warning FIXME - Get rid of this allocation
1979 UniChar
*newPathBuf
= CFAllocatorAllocate(alloc
, sizeof(UniChar
) * (relPathRg
.length
+ basePathRg
.length
+ 1), 0);
1981 CFStringGetCharacters(baseString
, basePathRg
, newPathBuf
);
1982 idx
= newPathBuf
+ basePathRg
.length
- 1;
1983 while (idx
!= newPathBuf
&& *idx
!= '/') idx
--;
1984 if (*idx
== '/') idx
++;
1985 CFStringGetCharacters(relString
, relPathRg
, idx
);
1986 end
= idx
+ relPathRg
.length
;
1988 newPath
= _resolvedPath(newPathBuf
, end
, '/', false, false, alloc
);
1990 /* Under Win32 absolute path can begin with letter
1991 * so we have to add one '/' to the newString
1994 #if defined(__WIN32__)
1995 if (CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
1996 CFStringAppend(newString
, CFSTR("/"));
1999 // if the relative URL does not begin with a slash and
2000 // the base does not end with a slash, add a slash
2001 if ((basePathRg
.location
== kCFNotFound
|| basePathRg
.length
== 0) && CFStringGetCharacterAtIndex(newPath
, 0) != '/') {
2002 CFStringAppendCString(newString
, "/", kCFStringEncodingASCII
);
2005 CFStringAppend(newString
, newPath
);
2007 rg
.location
= relPathRg
.location
+ relPathRg
.length
;
2008 rg
.length
= CFStringGetLength(relString
);
2009 if (rg
.length
> rg
.location
) {
2010 rg
.length
-= rg
.location
;
2011 CFStringGetCharacters(relString
, rg
, buf
);
2012 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2015 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PATH
);
2016 if (rg
.location
!= kCFNotFound
) {
2017 CFStringGetCharacters(baseString
, rg
, buf
);
2018 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2021 if (!(relFlags
& RESOURCE_SPECIFIER_MASK
)) {
2022 // ??? Can this ever happen?
2023 UInt32 rsrcFlag
= _firstResourceSpecifierFlag(baseFlags
);
2025 rg
.location
= _rangeForComponent(baseFlags
, baseRanges
, rsrcFlag
).location
;
2026 rg
.length
= CFStringGetLength(baseString
) - rg
.location
;
2027 rg
.location
--; // To pick up the separator
2029 CFStringGetCharacters(baseString
, rg
, buf
);
2030 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2032 } else if (relFlags
& HAS_PARAMETERS
) {
2033 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_PARAMETERS
);
2034 rg
.location
--; // To get the semicolon that starts the parameters
2035 rg
.length
= CFStringGetLength(relString
) - rg
.location
;
2036 CFStringGetCharacters(relString
, rg
, buf
);
2037 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2039 // Sigh; we have to resolve these against one another
2040 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_PARAMETERS
);
2041 if (rg
.location
!= kCFNotFound
) {
2042 CFStringAppendCString(newString
, ";", kCFStringEncodingASCII
);
2043 CFStringGetCharacters(baseString
, rg
, buf
);
2044 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2046 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_QUERY
);
2047 if (rg
.location
!= kCFNotFound
) {
2048 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2049 CFStringGetCharacters(relString
, rg
, buf
);
2050 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2052 rg
= _rangeForComponent(baseFlags
, baseRanges
, HAS_QUERY
);
2053 if (rg
.location
!= kCFNotFound
) {
2054 CFStringAppendCString(newString
, "?", kCFStringEncodingASCII
);
2055 CFStringGetCharacters(baseString
, rg
, buf
);
2056 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2059 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2060 rg
= _rangeForComponent(relFlags
, relRanges
, HAS_FRAGMENT
);
2061 if (rg
.location
!= kCFNotFound
) {
2062 CFStringAppendCString(newString
, "#", kCFStringEncodingASCII
);
2063 CFStringGetCharacters(relString
, rg
, buf
);
2064 CFStringAppendCharacters(newString
, buf
, rg
.length
);
2069 CFAllocatorDeallocate(alloc
, buf
);
2073 CFURLRef
CFURLCopyAbsoluteURL(CFURLRef relativeURL
) {
2074 CFURLRef anURL
, base
;
2075 CFURLPathStyle fsType
;
2076 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
2077 CFStringRef baseString
, newString
;
2079 CFRange
*baseRanges
;
2082 CFAssert1(relativeURL
!= NULL
, __kCFLogAssertion
, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__
);
2083 if (CF_IS_OBJC(__kCFURLTypeID
, relativeURL
)) {
2084 CFURLRef (*absoluteURLMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2085 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("absoluteURL");
2086 anURL
= absoluteURLMsg((const void *)relativeURL
, s
);
2087 if (anURL
) CFRetain(anURL
);
2091 __CFGenericValidateType(relativeURL
, __kCFURLTypeID
);
2093 base
= relativeURL
->_base
;
2095 return CFRetain(relativeURL
);
2097 baseIsObjC
= CF_IS_OBJC(__kCFURLTypeID
, base
);
2098 fsType
= URL_PATH_TYPE(relativeURL
);
2100 if (!baseIsObjC
&& fsType
!= FULL_URL_REPRESENTATION
&& fsType
== URL_PATH_TYPE(base
)) {
2101 return _CFURLCopyAbsoluteFileURL(relativeURL
);
2103 if (fsType
!= FULL_URL_REPRESENTATION
) {
2104 _convertToURLRepresentation((struct __CFURL
*)relativeURL
);
2105 fsType
= FULL_URL_REPRESENTATION
;
2107 if (!(relativeURL
->_flags
& IS_PARSED
)) {
2108 _parseComponentsOfURL(relativeURL
);
2110 if ((relativeURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) && !(relativeURL
->_flags
& (RESOURCE_SPECIFIER_MASK
| NET_LOCATION_MASK
)) && !baseIsObjC
&& (URL_PATH_TYPE(base
) == kCFURLPOSIXPathStyle
)) {
2111 // There's nothing to relativeURL's string except the path
2112 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, CFURLHasDirectoryPath(base
), kCFURLPOSIXPathStyle
, alloc
);
2113 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, kCFURLPOSIXPathStyle
, CFURLHasDirectoryPath(relativeURL
));
2119 CFURLPathStyle baseType
= URL_PATH_TYPE(base
);
2120 if (baseType
!= FULL_URL_REPRESENTATION
) {
2121 _convertToURLRepresentation((struct __CFURL
*)base
);
2122 } else if (!(base
->_flags
& IS_PARSED
)) {
2123 _parseComponentsOfURL(base
);
2125 baseString
= base
->_string
;
2126 baseFlags
= base
->_flags
;
2127 baseRanges
= base
->ranges
;
2129 baseString
= CFURLGetString(base
);
2132 _parseComponents(alloc
, baseString
, NULL
, &baseFlags
, &baseRanges
);
2135 newString
= resolveAbsoluteURLString(alloc
, relativeURL
->_string
, relativeURL
->_flags
, relativeURL
->ranges
, baseString
, baseFlags
, baseRanges
);
2137 CFAllocatorDeallocate(alloc
, baseRanges
);
2139 anURL
= _CFURLCreateWithArbitraryString(alloc
, newString
, NULL
);
2140 CFRelease(newString
);
2141 ((struct __CFURL
*)anURL
)->_encoding
= relativeURL
->_encoding
;
2146 /*******************/
2147 /* Basic accessors */
2148 /*******************/
2150 Boolean
CFURLCanBeDecomposed(CFURLRef anURL
) {
2151 anURL
= _CFURLFromNSURL(anURL
);
2152 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) return true;
2153 if (!(anURL
->_flags
& IS_PARSED
)) {
2154 _parseComponentsOfURL(anURL
);
2156 return ((anURL
->_flags
& IS_DECOMPOSABLE
) != 0);
2159 CFStringRef
CFURLGetString(CFURLRef url
) {
2160 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFStringRef
, url
, "relativeString");
2161 if (URL_PATH_TYPE(url
) != FULL_URL_REPRESENTATION
) {
2162 if (url
->_base
&& (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2163 return url
->_string
;
2165 _convertToURLRepresentation((struct __CFURL
*)url
);
2167 if (!_haveTestedOriginalString(url
)) {
2168 computeSanitizedString(url
);
2170 if (url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) {
2171 return url
->_string
;
2173 return url
->_sanatizedString
;
2177 CFIndex
CFURLGetBytes(CFURLRef url
, UInt8
*buffer
, CFIndex bufferLength
) {
2178 CFIndex length
, charsConverted
, usedLength
;
2180 CFStringEncoding enc
;
2181 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
2182 string
= CFURLGetString(url
);
2183 enc
= kCFStringEncodingUTF8
;
2185 string
= url
->_string
;
2186 enc
= url
->_encoding
;
2188 length
= CFStringGetLength(string
);
2189 charsConverted
= CFStringGetBytes(string
, CFRangeMake(0, length
), enc
, 0, false, buffer
, bufferLength
, &usedLength
);
2190 if (charsConverted
!= length
) {
2197 CFURLRef
CFURLGetBaseURL(CFURLRef anURL
) {
2198 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID
, CFURLRef
, anURL
, "baseURL");
2199 return anURL
->_base
;
2202 // Assumes the URL is already parsed
2203 static CFRange
_rangeForComponent(UInt32 flags
, CFRange
*ranges
, UInt32 compFlag
) {
2205 if (!(flags
& compFlag
)) return CFRangeMake(kCFNotFound
, 0);
2206 while (!(compFlag
& 1)) {
2207 compFlag
= compFlag
>> 1;
2216 static CFStringRef
_retainedComponentString(CFURLRef url
, UInt32 compFlag
, Boolean fromOriginalString
, Boolean removePercentEscapes
) {
2219 CFAllocatorRef alloc
= CFGetAllocator(url
);
2220 CFAssert1(URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
, __kCFLogAssertion
, "%s(): passed a file system URL", __PRETTY_FUNCTION__
);
2221 if (removePercentEscapes
) fromOriginalString
= true;
2222 if (!(url
->_flags
& IS_PARSED
)) {
2223 _parseComponentsOfURL(url
);
2225 rg
= _rangeForComponent(url
->_flags
, url
->ranges
, compFlag
);
2226 if (rg
.location
== kCFNotFound
) return NULL
;
2227 comp
= CFStringCreateWithSubstring(alloc
, url
->_string
, rg
);
2228 if (!fromOriginalString
) {
2229 if (!_haveTestedOriginalString(url
)) {
2230 computeSanitizedString(url
);
2232 if (!(url
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (url
->_flags
& (compFlag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
))) {
2233 CFStringRef newComp
= correctedComponent(comp
, compFlag
, url
->_encoding
);
2238 if (removePercentEscapes
) {
2240 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
2241 tmp
= CFURLCreateStringByReplacingPercentEscapes(alloc
, comp
, CFSTR(""));
2243 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc
, comp
, CFSTR(""), url
->_encoding
);
2251 CFStringRef
CFURLCopyScheme(CFURLRef anURL
) {
2253 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2254 CFStringRef (*schemeMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2255 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("scheme");
2256 scheme
= schemeMsg((const void *)anURL
, s
);
2257 if (scheme
) CFRetain(scheme
);
2260 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2262 return CFURLCopyScheme(anURL
->_base
);
2264 CFRetain(kCFURLFileScheme
); // because caller will release it
2265 return kCFURLFileScheme
;
2268 scheme
= _retainedComponentString(anURL
, HAS_SCHEME
, true, false);
2271 } else if (anURL
->_base
) {
2272 return CFURLCopyScheme(anURL
->_base
);
2278 static CFRange
_netLocationRange(UInt32 flags
, CFRange
*ranges
) {
2280 CFRange netRg
= {kCFNotFound
, 0};
2283 if ((flags
& NET_LOCATION_MASK
) == 0) return CFRangeMake(kCFNotFound
, 0);
2285 netRgs
[0] = _rangeForComponent(flags
, ranges
, HAS_USER
);
2286 netRgs
[1] = _rangeForComponent(flags
, ranges
, HAS_PASSWORD
);
2287 netRgs
[2] = _rangeForComponent(flags
, ranges
, HAS_HOST
);
2288 netRgs
[3] = _rangeForComponent(flags
, ranges
, HAS_PORT
);
2289 for (i
= 0; i
< c
; i
++) {
2290 if (netRgs
[i
].location
== kCFNotFound
) continue;
2291 if (netRg
.location
== kCFNotFound
) {
2294 netRg
.length
= netRgs
[i
].location
+ netRgs
[i
].length
- netRg
.location
;
2300 CFStringRef
CFURLCopyNetLocation(CFURLRef anURL
) {
2301 anURL
= _CFURLFromNSURL(anURL
);
2302 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2303 // !!! This won't work if we go to putting the vol ref num in the net location for HFS
2305 return CFURLCopyNetLocation(anURL
->_base
);
2307 CFRetain(kCFURLLocalhost
);
2308 return kCFURLLocalhost
;
2311 if (!(anURL
->_flags
& IS_PARSED
)) {
2312 _parseComponentsOfURL(anURL
);
2314 if (anURL
->_flags
& NET_LOCATION_MASK
) {
2315 // We provide the net location
2316 CFRange netRg
= _netLocationRange(anURL
->_flags
, anURL
->ranges
);
2318 if (!_haveTestedOriginalString(anURL
)) {
2319 computeSanitizedString(anURL
);
2321 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
) && (anURL
->_flags
& (USER_DIFFERS
| PASSWORD_DIFFERS
| HOST_DIFFERS
| PORT_DIFFERS
))) {
2322 // 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.
2324 netRg
.length
= CFStringGetLength(anURL
->_sanatizedString
) - netRg
.location
;
2325 if (CFStringFindWithOptions(anURL
->_sanatizedString
, CFSTR("/"), netRg
, 0, &netLocEnd
)) {
2326 netRg
.length
= netLocEnd
.location
- netRg
.location
;
2328 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_sanatizedString
, netRg
);
2330 netLoc
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, netRg
);
2333 } else if (anURL
->_base
) {
2334 return CFURLCopyNetLocation(anURL
->_base
);
2340 // 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.
2341 CFStringRef
CFURLCopyPath(CFURLRef anURL
) {
2342 anURL
= _CFURLFromNSURL(anURL
);
2343 if (URL_PATH_TYPE(anURL
) == kCFURLPOSIXPathStyle
&& (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
2344 CFRetain(anURL
->_string
);
2345 return anURL
->_string
;
2347 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2348 _convertToURLRepresentation((struct __CFURL
*)anURL
);
2350 return _retainedComponentString(anURL
, HAS_PATH
, false, false);
2353 /* 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.
2355 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.
2357 CFStringRef
CFURLCopyStrictPath(CFURLRef anURL
, Boolean
*isAbsolute
) {
2358 CFStringRef path
= CFURLCopyPath(anURL
);
2359 if (!path
|| CFStringGetLength(path
) == 0) {
2360 if (path
) CFRelease(path
);
2361 if (isAbsolute
) *isAbsolute
= false;
2364 if (CFStringGetCharacterAtIndex(path
, 0) == '/') {
2366 if (isAbsolute
) *isAbsolute
= true;
2367 tmp
= CFStringCreateWithSubstring(CFGetAllocator(path
), path
, CFRangeMake(1, CFStringGetLength(path
)-1));
2371 if (isAbsolute
) *isAbsolute
= false;
2376 Boolean
CFURLHasDirectoryPath(CFURLRef anURL
) {
2377 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2378 if (URL_PATH_TYPE(anURL
) == FULL_URL_REPRESENTATION
) {
2379 if (!(anURL
->_flags
& IS_PARSED
)) {
2380 _parseComponentsOfURL(anURL
);
2382 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_PATH
| NET_LOCATION_MASK
))) {
2383 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2385 return CFURLHasDirectoryPath(anURL
->_base
);
2387 return ((anURL
->_flags
& IS_DIRECTORY
) != 0);
2390 static UInt32
_firstResourceSpecifierFlag(UInt32 flags
) {
2391 UInt32 firstRsrcSpecFlag
= 0;
2392 UInt32 flag
= HAS_FRAGMENT
;
2393 while (flag
!= HAS_PATH
) {
2395 firstRsrcSpecFlag
= flag
;
2399 return firstRsrcSpecFlag
;
2402 CFStringRef
CFURLCopyResourceSpecifier(CFURLRef anURL
) {
2403 anURL
= _CFURLFromNSURL(anURL
);
2404 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2405 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2408 if (!(anURL
->_flags
& IS_PARSED
)) {
2409 _parseComponentsOfURL(anURL
);
2411 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) {
2412 CFRange schemeRg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, HAS_SCHEME
);
2413 CFIndex base
= schemeRg
.location
+ schemeRg
.length
+ 1;
2414 if (!_haveTestedOriginalString(anURL
)) {
2415 computeSanitizedString(anURL
);
2417 if (anURL
->_sanatizedString
) {
2418 // 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.
2419 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_sanatizedString
, CFRangeMake(base
, CFStringGetLength(anURL
->_sanatizedString
)-base
));
2421 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_string
, CFRangeMake(base
, CFStringGetLength(anURL
->_string
)-base
));
2424 UInt32 firstRsrcSpecFlag
= _firstResourceSpecifierFlag(anURL
->_flags
);
2426 if (firstRsrcSpecFlag
) {
2427 Boolean canUseOriginalString
= true;
2428 Boolean canUseSanitizedString
= true;
2429 CFAllocatorRef alloc
= CFGetAllocator(anURL
);
2430 if (!_haveTestedOriginalString(anURL
)) {
2431 computeSanitizedString(anURL
);
2433 if (!(anURL
->_flags
& ORIGINAL_AND_URL_STRINGS_MATCH
)) {
2434 // See if any pieces in the resource specifier differ between sanitized string and original string
2435 for (flag
= firstRsrcSpecFlag
; flag
!= (HAS_FRAGMENT
<< 1); flag
= flag
<< 1) {
2436 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2437 canUseOriginalString
= false;
2442 if (!canUseOriginalString
) {
2443 // 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.
2444 for (flag
= firstRsrcSpecFlag
>> 1; flag
!= 0; flag
= flag
>> 1) {
2445 if (anURL
->_flags
& (flag
<< BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG
)) {
2446 canUseSanitizedString
= false;
2451 if (canUseOriginalString
) {
2452 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2453 rg
.location
--; // Include the character that demarcates the component
2454 rg
.length
= CFStringGetLength(anURL
->_string
) - rg
.location
;
2455 return CFStringCreateWithSubstring(alloc
, anURL
->_string
, rg
);
2456 } else if (canUseSanitizedString
) {
2457 CFRange rg
= _rangeForComponent(anURL
->_flags
, anURL
->ranges
, firstRsrcSpecFlag
);
2458 rg
.location
--; // Include the character that demarcates the component
2459 rg
.length
= CFStringGetLength(anURL
->_sanatizedString
) - rg
.location
;
2460 return CFStringCreateWithSubstring(alloc
, anURL
->_sanatizedString
, rg
);
2462 // Must compute the correct string to return; just reparse....
2463 UInt32 sanFlags
= 0;
2464 CFRange
*sanRanges
= NULL
;
2466 _parseComponents(alloc
, anURL
->_sanatizedString
, anURL
->_base
, &sanFlags
, &sanRanges
);
2467 rg
= _rangeForComponent(sanFlags
, sanRanges
, firstRsrcSpecFlag
);
2468 CFAllocatorDeallocate(alloc
, sanRanges
);
2469 rg
.location
--; // Include the character that demarcates the component
2470 rg
.length
= CFStringGetLength(anURL
->_sanatizedString
) - rg
.location
;
2471 return CFStringCreateWithSubstring(CFGetAllocator(anURL
), anURL
->_sanatizedString
, rg
);
2474 // The resource specifier cannot possibly come from the base.
2480 /*************************************/
2481 /* Accessors that create new objects */
2482 /*************************************/
2484 // 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).
2485 CFStringRef
CFURLCopyHostName(CFURLRef anURL
) {
2487 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2488 CFStringRef (*hostMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2489 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("host");
2490 tmp
= hostMsg((const void *)anURL
, s
);
2491 if (tmp
) CFRetain(tmp
);
2494 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2495 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2497 return CFURLCopyHostName(anURL
->_base
);
2499 CFRetain(kCFURLLocalhost
);
2500 return kCFURLLocalhost
;
2503 tmp
= _retainedComponentString(anURL
, HAS_HOST
, true, true);
2505 if (anURL
->_flags
& IS_IPV6_ENCODED
) {
2506 // Have to strip off the brackets to get the true hostname.
2507 // Assume that to be legal the first and last characters are brackets!
2508 CFStringRef strippedHost
= CFStringCreateWithSubstring(CFGetAllocator(anURL
), tmp
, CFRangeMake(1, CFStringGetLength(tmp
) - 2));
2513 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2514 return CFURLCopyHostName(anURL
->_base
);
2520 // Return -1 to indicate no port is specified
2521 SInt32
CFURLGetPortNumber(CFURLRef anURL
) {
2523 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2524 CFNumberRef (*portMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2525 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("port");
2526 CFNumberRef cfPort
= portMsg((const void *)anURL
, s
);
2528 if (cfPort
&& CFNumberGetValue(cfPort
, kCFNumberSInt32Type
, &num
)) return num
;
2531 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2532 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2534 return CFURLGetPortNumber(anURL
->_base
);
2538 port
= _retainedComponentString(anURL
, HAS_PORT
, true, false);
2540 SInt32 portNum
, idx
, length
= CFStringGetLength(port
);
2541 CFStringInlineBuffer buf
;
2542 CFStringInitInlineBuffer(port
, &buf
, CFRangeMake(0, length
));
2544 if (!__CFStringScanInteger(&buf
, NULL
, &idx
, false, &portNum
) || (idx
!= length
)) {
2549 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2550 return CFURLGetPortNumber(anURL
->_base
);
2556 CFStringRef
CFURLCopyUserName(CFURLRef anURL
) {
2558 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2559 CFStringRef (*userMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2560 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("user");
2561 user
= userMsg((const void *)anURL
, s
);
2562 if (user
) CFRetain(user
);
2565 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2566 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2568 return CFURLCopyUserName(anURL
->_base
);
2572 user
= _retainedComponentString(anURL
, HAS_USER
, true, true);
2575 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2576 return CFURLCopyUserName(anURL
->_base
);
2582 CFStringRef
CFURLCopyPassword(CFURLRef anURL
) {
2584 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2585 CFStringRef (*passwordMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2586 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("password");
2587 passwd
= passwordMsg((const void *)anURL
, s
);
2588 if (passwd
) CFRetain(passwd
);
2591 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2592 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2594 return CFURLCopyPassword(anURL
->_base
);
2598 passwd
= _retainedComponentString(anURL
, HAS_PASSWORD
, true, true);
2601 } else if (anURL
->_base
&& !(anURL
->_flags
& NET_LOCATION_MASK
) && !(anURL
->_flags
& HAS_SCHEME
)) {
2602 return CFURLCopyPassword(anURL
->_base
);
2608 // 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
2610 static CFStringRef
_unescapedParameterString(CFURLRef anURL
) {
2612 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2613 CFStringRef (*paramMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2614 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("parameterString");
2615 str
= paramMsg((const void *)anURL
, s
);
2616 if (str
) CFRetain(str
);
2619 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2620 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2623 str
= _retainedComponentString(anURL
, HAS_PARAMETERS
, false, false);
2624 if (str
) return str
;
2625 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2626 if (!anURL
->_base
|| (anURL
->_flags
& (NET_LOCATION_MASK
| HAS_PATH
| HAS_SCHEME
))) {
2628 // Parameter string definitely coming from the relative portion of the URL
2630 return _unescapedParameterString(anURL
->_base
);
2633 CFStringRef
CFURLCopyParameterString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2634 CFStringRef param
= _unescapedParameterString(anURL
);
2637 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2638 result
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
);
2640 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), param
, charactersToLeaveEscaped
, anURL
->_encoding
);
2648 static CFStringRef
_unescapedQueryString(CFURLRef anURL
) {
2650 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2651 CFStringRef (*queryMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2652 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("query");
2653 str
= queryMsg((const void *)anURL
, s
);
2654 if (str
) CFRetain(str
);
2657 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2658 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2661 str
= _retainedComponentString(anURL
, HAS_QUERY
, false, false);
2662 if (str
) return str
;
2663 if (!(anURL
->_flags
& IS_DECOMPOSABLE
)) return NULL
;
2664 if (!anURL
->_base
|| (anURL
->_flags
& (HAS_SCHEME
| NET_LOCATION_MASK
| HAS_PATH
| HAS_PARAMETERS
))) {
2667 return _unescapedQueryString(anURL
->_base
);
2670 CFStringRef
CFURLCopyQueryString(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2671 CFStringRef query
= _unescapedQueryString(anURL
);
2674 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2675 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
);
2677 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), query
, charactersToLeaveEscaped
, anURL
->_encoding
);
2685 // Fragments are NEVER taken from a base URL
2686 static CFStringRef
_unescapedFragment(CFURLRef anURL
) {
2688 if (CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
2689 CFStringRef (*fragmentMsg
)(const void *, SEL
) = (void *)__CFSendObjCMsg
;
2690 static SEL s
= NULL
; if (!s
) s
= __CFGetObjCSelector("fragment");
2691 str
= fragmentMsg((const void *)anURL
, s
);
2692 if (str
) CFRetain(str
);
2695 __CFGenericValidateType(anURL
, __kCFURLTypeID
);
2696 if (URL_PATH_TYPE(anURL
) != FULL_URL_REPRESENTATION
) {
2699 str
= _retainedComponentString(anURL
, HAS_FRAGMENT
, false, false);
2703 CFStringRef
CFURLCopyFragment(CFURLRef anURL
, CFStringRef charactersToLeaveEscaped
) {
2704 CFStringRef fragment
= _unescapedFragment(anURL
);
2707 if (anURL
->_flags
& IS_OLD_UTF8_STYLE
|| anURL
->_encoding
== kCFStringEncodingUTF8
) {
2708 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
);
2710 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL
), fragment
, charactersToLeaveEscaped
, anURL
->_encoding
);
2712 CFRelease(fragment
);
2718 static CFIndex
insertionLocationForMask(CFURLRef url
, CFOptionFlags mask
) {
2719 CFIndex firstMaskFlag
= 1;
2720 CFIndex lastComponentBeforeMask
= 0;
2721 while (firstMaskFlag
<= HAS_FRAGMENT
) {
2722 if (firstMaskFlag
& mask
) break;
2723 if (url
->_flags
& firstMaskFlag
) lastComponentBeforeMask
= firstMaskFlag
;
2724 firstMaskFlag
= firstMaskFlag
<< 1;
2726 if (lastComponentBeforeMask
== 0) {
2727 // mask includes HAS_SCHEME
2729 } else if (lastComponentBeforeMask
== HAS_SCHEME
) {
2730 // Do not have to worry about the non-decomposable case here.
2731 return _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
).length
+ 3;
2733 // For all other components, the separator precedes the component, so there's no need
2734 // to add extra chars to get to the next insertion point
2735 CFRange rg
= _rangeForComponent(url
->_flags
, url
->ranges
, lastComponentBeforeMask
);
2736 return rg
.location
+ rg
.length
;
2740 static CFRange
_CFURLGetCharRangeForMask(CFURLRef url
, CFOptionFlags mask
, CFRange
*charRangeWithSeparators
) {
2741 CFOptionFlags currentOption
;
2742 CFOptionFlags firstMaskFlag
= HAS_SCHEME
;
2743 Boolean haveReachedMask
= false;
2744 CFIndex beforeMask
= 0;
2745 CFIndex afterMask
= kCFNotFound
;
2746 CFRange
*currRange
= url
->ranges
;
2747 CFRange maskRange
= {kCFNotFound
, 0};
2748 for (currentOption
= 1; currentOption
<= HAS_FRAGMENT
; currentOption
= currentOption
<< 1) {
2749 if (!haveReachedMask
&& (currentOption
& mask
) != 0) {
2750 firstMaskFlag
= currentOption
;
2751 haveReachedMask
= true;
2753 if (!(url
->_flags
& currentOption
)) continue;
2754 if (!haveReachedMask
) {
2755 beforeMask
= currRange
->location
+ currRange
->length
;
2756 } else if (currentOption
<= mask
) {
2757 if (maskRange
.location
== kCFNotFound
) {
2758 maskRange
= *currRange
;
2760 maskRange
.length
= currRange
->location
+ currRange
->length
- maskRange
.location
;
2763 afterMask
= currRange
->location
;
2768 if (afterMask
== kCFNotFound
) {
2769 afterMask
= maskRange
.location
+ maskRange
.length
;
2771 charRangeWithSeparators
->location
= beforeMask
;
2772 charRangeWithSeparators
->length
= afterMask
- beforeMask
;
2776 static CFRange
_getCharRangeInDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2778 switch (component
) {
2779 case kCFURLComponentScheme
:
2782 case kCFURLComponentNetLocation
:
2783 mask
= NET_LOCATION_MASK
;
2785 case kCFURLComponentPath
:
2788 case kCFURLComponentResourceSpecifier
:
2789 mask
= RESOURCE_SPECIFIER_MASK
;
2791 case kCFURLComponentUser
:
2794 case kCFURLComponentPassword
:
2795 mask
= HAS_PASSWORD
;
2797 case kCFURLComponentUserInfo
:
2798 mask
= HAS_USER
| HAS_PASSWORD
;
2800 case kCFURLComponentHost
:
2803 case kCFURLComponentPort
:
2806 case kCFURLComponentParameterString
:
2807 mask
= HAS_PARAMETERS
;
2809 case kCFURLComponentQuery
:
2812 case kCFURLComponentFragment
:
2813 mask
= HAS_FRAGMENT
;
2816 rangeIncludingSeparators
->location
= kCFNotFound
;
2817 rangeIncludingSeparators
->length
= 0;
2818 return CFRangeMake(kCFNotFound
, 0);
2821 if ((url
->_flags
& mask
) == 0) {
2822 rangeIncludingSeparators
->location
= insertionLocationForMask(url
, mask
);
2823 rangeIncludingSeparators
->length
= 0;
2824 return CFRangeMake(kCFNotFound
, 0);
2826 return _CFURLGetCharRangeForMask(url
, mask
, rangeIncludingSeparators
);
2830 static CFRange
_getCharRangeInNonDecomposableURL(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2831 if (component
== kCFURLComponentScheme
) {
2832 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2833 rangeIncludingSeparators
->location
= 0;
2834 rangeIncludingSeparators
->length
= schemeRg
.length
+ 1;
2836 } else if (component
== kCFURLComponentResourceSpecifier
) {
2837 CFRange schemeRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_SCHEME
);
2838 CFIndex stringLength
= CFStringGetLength(url
->_string
);
2839 if (schemeRg
.length
+ 1 == stringLength
) {
2840 rangeIncludingSeparators
->location
= schemeRg
.length
+ 1;
2841 rangeIncludingSeparators
->length
= 0;
2842 return CFRangeMake(kCFNotFound
, 0);
2844 rangeIncludingSeparators
->location
= schemeRg
.length
;
2845 rangeIncludingSeparators
->length
= stringLength
- schemeRg
.length
;
2846 return CFRangeMake(schemeRg
.length
+ 1, rangeIncludingSeparators
->length
- 1);
2849 rangeIncludingSeparators
->location
= kCFNotFound
;
2850 rangeIncludingSeparators
->length
= 0;
2851 return CFRangeMake(kCFNotFound
, 0);
2856 CFRange
CFURLGetByteRangeForComponent(CFURLRef url
, CFURLComponentType component
, CFRange
*rangeIncludingSeparators
) {
2857 CFRange charRange
, charRangeWithSeparators
;
2859 CFAssert2(component
> 0 && component
< 13, __kCFLogAssertion
, "%s(): passed invalid component %d", __PRETTY_FUNCTION__
, component
);
2860 url
= _CFURLFromNSURL(url
);
2861 if (!(url
->_flags
& IS_PARSED
)) {
2862 _parseComponentsOfURL(url
);
2865 if (!(url
->_flags
& IS_DECOMPOSABLE
)) {
2866 // Special-case this because non-decomposable URLs have a slightly strange flags setup
2867 charRange
= _getCharRangeInNonDecomposableURL(url
, component
, &charRangeWithSeparators
);
2869 charRange
= _getCharRangeInDecomposableURL(url
, component
, &charRangeWithSeparators
);
2872 if (charRangeWithSeparators
.location
== kCFNotFound
) {
2873 if (rangeIncludingSeparators
) {
2874 rangeIncludingSeparators
->location
= kCFNotFound
;
2875 rangeIncludingSeparators
->length
= 0;
2877 return CFRangeMake(kCFNotFound
, 0);
2878 } else if (rangeIncludingSeparators
) {
2879 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->location
));
2881 if (charRange
.location
== kCFNotFound
) {
2882 byteRange
= charRange
;
2883 CFStringGetBytes(url
->_string
, charRangeWithSeparators
, url
->_encoding
, 0, false, NULL
, 0, &(rangeIncludingSeparators
->length
));
2885 CFIndex maxCharRange
= charRange
.location
+ charRange
.length
;
2886 CFIndex maxCharRangeWithSeparators
= charRangeWithSeparators
.location
+ charRangeWithSeparators
.length
;
2888 if (charRangeWithSeparators
.location
== charRange
.location
) {
2889 byteRange
.location
= rangeIncludingSeparators
->location
;
2892 CFStringGetBytes(url
->_string
, CFRangeMake(charRangeWithSeparators
.location
, charRange
.location
- charRangeWithSeparators
.location
), url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
2893 byteRange
.location
= charRangeWithSeparators
.location
+ numBytes
;
2895 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
2896 if (maxCharRangeWithSeparators
== maxCharRange
) {
2897 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
- rangeIncludingSeparators
->location
;
2901 rg
.location
= maxCharRange
;
2902 rg
.length
= maxCharRangeWithSeparators
- rg
.location
;
2903 CFStringGetBytes(url
->_string
, rg
, url
->_encoding
, 0, false, NULL
, 0, &numBytes
);
2904 rangeIncludingSeparators
->length
= byteRange
.location
+ byteRange
.length
+ numBytes
- rangeIncludingSeparators
->location
;
2907 } else if (charRange
.location
== kCFNotFound
) {
2908 byteRange
= charRange
;
2910 CFStringGetBytes(url
->_string
, CFRangeMake(0, charRange
.location
), url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.location
));
2911 CFStringGetBytes(url
->_string
, charRange
, url
->_encoding
, 0, false, NULL
, 0, &(byteRange
.length
));
2916 /* Component support */
2918 /* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */
2919 static CFStringRef
schemeSpecificString(CFURLRef url
) {
2921 isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
2922 switch (URL_PATH_TYPE(url
)) {
2923 case kCFURLPOSIXPathStyle
:
2924 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
2925 return CFRetain(url
->_string
);
2927 return POSIXPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
2929 case kCFURLHFSPathStyle
:
2930 return HFSPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
2931 case kCFURLWindowsPathStyle
:
2932 return WindowsPathToURLPath(url
->_string
, CFGetAllocator(url
), isDir
);
2933 case FULL_URL_REPRESENTATION
:
2934 return CFURLCopyResourceSpecifier(url
);
2940 static Boolean
decomposeToNonHierarchical(CFURLRef url
, CFURLComponentsNonHierarchical
*components
) {
2941 if (CFURLGetBaseURL(url
) != NULL
) {
2942 components
->scheme
= NULL
;
2944 components
->scheme
= CFURLCopyScheme(url
);
2946 components
->schemeSpecific
= schemeSpecificString(url
);
2950 static CFURLRef
composeFromNonHierarchical(CFAllocatorRef alloc
, const CFURLComponentsNonHierarchical
*components
) {
2952 if (components
->scheme
) {
2954 str
= CFStringCreateMutableCopy(alloc
, CFStringGetLength(components
->scheme
) + 1 + (components
->schemeSpecific
? CFStringGetLength(components
->schemeSpecific
): 0), components
->scheme
);
2955 CFStringAppendCharacters((CFMutableStringRef
)str
, &ch
, 1);
2956 if (components
->schemeSpecific
) CFStringAppend((CFMutableStringRef
)str
, components
->schemeSpecific
);
2957 } else if (components
->schemeSpecific
) {
2958 str
= components
->schemeSpecific
;
2964 CFURLRef url
= CFURLCreateWithString(alloc
, str
, NULL
);
2972 static Boolean
decomposeToRFC1808(CFURLRef url
, CFURLComponentsRFC1808
*components
) {
2973 CFAllocatorRef alloc
= CFGetAllocator(url
);
2975 static CFStringRef emptyStr
= NULL
;
2977 emptyStr
= CFSTR("");
2980 if (!CFURLCanBeDecomposed(url
)) {
2983 if ((pathType
= URL_PATH_TYPE(url
)) == FULL_URL_REPRESENTATION
) {
2984 CFStringRef path
= CFURLCopyPath(url
);
2986 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("/"));
2989 components
->pathComponents
= NULL
;
2991 components
->baseURL
= CFURLGetBaseURL(url
);
2992 if (components
->baseURL
) {
2993 CFRetain(components
->baseURL
);
2994 components
->scheme
= NULL
;
2996 components
->scheme
= _retainedComponentString(url
, HAS_SCHEME
, true, false);
2998 components
->user
= _retainedComponentString(url
, HAS_USER
, false, false);
2999 components
->password
= _retainedComponentString(url
, HAS_PASSWORD
, false, false);
3000 components
->host
= _retainedComponentString(url
, HAS_HOST
, false, false);
3001 if (url
->_flags
& HAS_PORT
) {
3002 components
->port
= CFURLGetPortNumber(url
);
3004 components
->port
= kCFNotFound
;
3006 components
->parameterString
= _retainedComponentString(url
, HAS_PARAMETERS
, false, false);
3007 components
->query
= _retainedComponentString(url
, HAS_QUERY
, false, false);
3008 components
->fragment
= _retainedComponentString(url
, HAS_FRAGMENT
, false, false);
3011 case kCFURLPOSIXPathStyle
: {
3012 CFStringRef pathStr
;
3013 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3014 pathStr
= url
->_string
;
3017 pathStr
= POSIXPathToURLPath(url
->_string
, alloc
, url
->_flags
& IS_DIRECTORY
);
3019 components
->pathComponents
= CFStringCreateArrayBySeparatingStrings(alloc
, pathStr
, CFSTR("/"));
3023 case kCFURLHFSPathStyle
:
3024 components
->pathComponents
= HFSPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3026 case kCFURLWindowsPathStyle
:
3027 components
->pathComponents
= WindowsPathToURLComponents(url
->_string
, alloc
, ((url
->_flags
& IS_DIRECTORY
) != 0));
3030 components
->pathComponents
= NULL
;
3032 if (!components
->pathComponents
) {
3035 components
->scheme
= CFRetain(kCFURLFileScheme
);
3036 components
->user
= NULL
;
3037 components
->password
= NULL
;
3038 components
->host
= CFRetain(kCFURLLocalhost
);
3039 components
->port
= kCFNotFound
;
3040 components
->parameterString
= NULL
;
3041 components
->query
= NULL
;
3042 components
->fragment
= NULL
;
3043 components
->baseURL
= CFURLGetBaseURL(url
);
3044 if (components
->baseURL
) CFRetain(components
->baseURL
);
3049 static CFURLRef
composeFromRFC1808(CFAllocatorRef alloc
, const CFURLComponentsRFC1808
*comp
) {
3050 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3051 CFURLRef base
= comp
->baseURL
;
3053 Boolean hadPrePathComponent
= false;
3056 CFStringAppend(urlString
, comp
->scheme
);
3057 CFStringAppend(urlString
, CFSTR("://"));
3058 hadPrePathComponent
= true;
3060 if (comp
->user
|| comp
->password
) {
3062 CFStringAppend(urlString
, comp
->user
);
3064 if (comp
->password
) {
3065 CFStringAppend(urlString
, CFSTR(":"));
3066 CFStringAppend(urlString
, comp
->password
);
3068 CFStringAppend(urlString
, CFSTR("@"));
3069 hadPrePathComponent
= true;
3072 CFStringAppend(urlString
, comp
->host
);
3073 if (comp
->port
!= kCFNotFound
) {
3074 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3076 hadPrePathComponent
= true;
3078 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength(CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3079 CFStringAppend(urlString
, CFSTR("/"));
3081 if (comp
->pathComponents
) {
3082 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3083 CFStringAppend(urlString
, pathStr
);
3086 if (comp
->parameterString
) {
3087 CFStringAppend(urlString
, CFSTR(";"));
3088 CFStringAppend(urlString
, comp
->parameterString
);
3091 CFStringAppend(urlString
, CFSTR("?"));
3092 CFStringAppend(urlString
, comp
->query
);
3094 if (comp
->fragment
) {
3095 CFStringAppend(urlString
, CFSTR("#"));
3096 CFStringAppend(urlString
, comp
->fragment
);
3098 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3099 CFRelease(urlString
);
3103 static Boolean
decomposeToRFC2396(CFURLRef url
, CFURLComponentsRFC2396
*comp
) {
3104 CFAllocatorRef alloc
= CFGetAllocator(url
);
3105 CFURLComponentsRFC1808 oldComp
;
3107 if (!decomposeToRFC1808(url
, &oldComp
)) {
3110 comp
->scheme
= oldComp
.scheme
;
3112 if (oldComp
.password
) {
3113 comp
->userinfo
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@:%@"), oldComp
.user
, oldComp
.password
);
3114 CFRelease(oldComp
.password
);
3115 CFRelease(oldComp
.user
);
3117 comp
->userinfo
= oldComp
.user
;
3120 comp
->userinfo
= NULL
;
3122 comp
->host
= oldComp
.host
;
3123 comp
->port
= oldComp
.port
;
3124 if (!oldComp
.parameterString
) {
3125 comp
->pathComponents
= oldComp
.pathComponents
;
3127 int length
= CFArrayGetCount(oldComp
.pathComponents
);
3128 comp
->pathComponents
= CFArrayCreateMutableCopy(alloc
, length
, oldComp
.pathComponents
);
3129 tmpStr
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp
->pathComponents
, length
- 1), oldComp
.parameterString
);
3130 CFArraySetValueAtIndex((CFMutableArrayRef
)comp
->pathComponents
, length
- 1, tmpStr
);
3132 CFRelease(oldComp
.pathComponents
);
3133 CFRelease(oldComp
.parameterString
);
3135 comp
->query
= oldComp
.query
;
3136 comp
->fragment
= oldComp
.fragment
;
3137 comp
->baseURL
= oldComp
.baseURL
;
3141 static CFURLRef
composeFromRFC2396(CFAllocatorRef alloc
, const CFURLComponentsRFC2396
*comp
) {
3142 CFMutableStringRef urlString
= CFStringCreateMutable(alloc
, 0);
3143 CFURLRef base
= comp
->baseURL
;
3145 Boolean hadPrePathComponent
= false;
3148 CFStringAppend(urlString
, comp
->scheme
);
3149 CFStringAppend(urlString
, CFSTR("://"));
3150 hadPrePathComponent
= true;
3152 if (comp
->userinfo
) {
3153 CFStringAppend(urlString
, comp
->userinfo
);
3154 CFStringAppend(urlString
, CFSTR("@"));
3155 hadPrePathComponent
= true;
3158 CFStringAppend(urlString
, comp
->host
);
3159 if (comp
->port
!= kCFNotFound
) {
3160 CFStringAppendFormat(urlString
, NULL
, CFSTR(":%d"), comp
->port
);
3162 hadPrePathComponent
= true;
3164 if (hadPrePathComponent
&& (comp
->pathComponents
== NULL
|| CFStringGetLength(CFArrayGetValueAtIndex(comp
->pathComponents
, 0)) != 0)) {
3165 CFStringAppend(urlString
, CFSTR("/"));
3167 if (comp
->pathComponents
) {
3168 CFStringRef pathStr
= CFStringCreateByCombiningStrings(alloc
, comp
->pathComponents
, CFSTR("/"));
3169 CFStringAppend(urlString
, pathStr
);
3173 CFStringAppend(urlString
, CFSTR("?"));
3174 CFStringAppend(urlString
, comp
->query
);
3176 if (comp
->fragment
) {
3177 CFStringAppend(urlString
, CFSTR("#"));
3178 CFStringAppend(urlString
, comp
->fragment
);
3180 url
= CFURLCreateWithString(alloc
, urlString
, base
);
3181 CFRelease(urlString
);
3185 #undef CFURLCopyComponents
3186 #undef CFURLCreateFromComponents
3188 Boolean
_CFURLCopyComponents(CFURLRef url
, CFURLComponentDecomposition decompositionType
, void *components
) {
3189 url
= _CFURLFromNSURL(url
);
3190 switch (decompositionType
) {
3191 case kCFURLComponentDecompositionNonHierarchical
:
3192 return decomposeToNonHierarchical(url
, (CFURLComponentsNonHierarchical
*)components
);
3193 case kCFURLComponentDecompositionRFC1808
:
3194 return decomposeToRFC1808(url
, (CFURLComponentsRFC1808
*)components
);
3195 case kCFURLComponentDecompositionRFC2396
:
3196 return decomposeToRFC2396(url
, (CFURLComponentsRFC2396
*)components
);
3203 CFURLRef
_CFURLCreateFromComponents(CFAllocatorRef alloc
, CFURLComponentDecomposition decompositionType
, const void *components
) {
3204 switch (decompositionType
) {
3205 case kCFURLComponentDecompositionNonHierarchical
:
3206 return composeFromNonHierarchical(alloc
, (const CFURLComponentsNonHierarchical
*)components
);
3207 case kCFURLComponentDecompositionRFC1808
:
3208 return composeFromRFC1808(alloc
, (const CFURLComponentsRFC1808
*)components
);
3209 case kCFURLComponentDecompositionRFC2396
:
3210 return composeFromRFC2396(alloc
, (const CFURLComponentsRFC2396
*)components
);
3216 CF_EXPORT
void *__CFURLReservedPtr(CFURLRef url
) {
3217 return url
->_reserved
;
3220 CF_EXPORT
void __CFURLSetReservedPtr(CFURLRef url
, void *ptr
) {
3221 ((struct __CFURL
*)url
)->_reserved
= ptr
;
3225 /* File system stuff */
3227 /* HFSPath<->URLPath functions at the bottom of the file */
3228 static CFArrayRef
WindowsPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3230 CFMutableArrayRef urlComponents
= NULL
;
3234 tmp
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR("\\"));
3235 urlComponents
= CFArrayCreateMutableCopy(alloc
, 0, tmp
);
3237 /* We must not replace ".:" with ".|" on WIN32.
3240 #if !defined(__WIN32__)
3241 str
= CFArrayGetValueAtIndex(urlComponents
, 0);
3242 if (CFStringGetLength(str
) == 2 && CFStringGetCharacterAtIndex(str
, 1) == ':') {
3243 CFStringRef newComponent
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%c|"), CFStringGetCharacterAtIndex(str
, 0));
3244 CFArraySetValueAtIndex(urlComponents
, 0, newComponent
);
3245 CFRelease(newComponent
);
3246 CFArrayInsertValueAtIndex(urlComponents
, 0, CFSTR("")); // So we get a leading '/' below
3247 i
= 2; // Skip over the drive letter and the empty string we just inserted
3250 #if defined(__WIN32__)
3251 // cjk: should this be done on all platforms?
3253 for (c
= CFArrayGetCount(urlComponents
); i
< c
; i
++) {
3254 CFStringRef fileComp
= CFArrayGetValueAtIndex(urlComponents
,i
);
3255 CFStringRef urlComp
= _replacePathIllegalCharacters(fileComp
, alloc
, false);
3257 // Couldn't decode fileComp
3258 CFRelease(urlComponents
);
3261 if (urlComp
!= fileComp
) {
3262 CFArraySetValueAtIndex(urlComponents
, i
, urlComp
);
3268 if (CFStringGetLength(CFArrayGetValueAtIndex(urlComponents
, CFArrayGetCount(urlComponents
) - 1)) != 0)
3269 CFArrayAppendValue(urlComponents
, CFSTR(""));
3271 return urlComponents
;
3274 static CFStringRef
WindowsPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
3275 CFArrayRef urlComponents
;
3276 CFArrayRef newComponents
;
3279 if (CFStringGetLength(path
) == 0) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3280 urlComponents
= WindowsPathToURLComponents(path
, alloc
, isDir
);
3281 if (!urlComponents
) return CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3283 newComponents
= copyStringArrayWithTransformation(urlComponents
, escapeWindowsPathComponent
);
3284 if (newComponents
) {
3285 str
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
3286 CFRelease(newComponents
);
3288 str
= CFStringCreateWithCString(alloc
, "", kCFStringEncodingASCII
);
3290 CFRelease(urlComponents
);
3294 static CFStringRef
POSIXPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDirectory
) {
3295 CFStringRef pathString
= _replacePathIllegalCharacters(path
, alloc
, true);
3296 if (isDirectory
&& CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
)-1) != '/') {
3297 CFStringRef tmp
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@/"), pathString
);
3298 CFRelease(pathString
);
3304 static CFStringRef
URLPathToPOSIXPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3305 // This is the easiest case; just remove the percent escape codes and we're done
3306 CFStringRef result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, path
, CFSTR(""), encoding
);
3308 CFIndex length
= CFStringGetLength(result
);
3309 if (length
> 1 && CFStringGetCharacterAtIndex(result
, length
-1) == '/') {
3310 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, result
, CFRangeMake(0, length
-1));
3319 static CFStringRef
URLPathToWindowsPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
3320 // Check for a drive letter, then flip all the slashes
3322 CFArrayRef tmp
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
3323 SInt32 count
= CFArrayGetCount(tmp
);
3324 CFMutableArrayRef components
= CFArrayCreateMutableCopy(allocator
, count
, tmp
);
3325 CFStringRef newPath
;
3328 if (CFStringGetLength(CFArrayGetValueAtIndex(components
,count
-1)) == 0) {
3329 CFArrayRemoveValueAtIndex(components
, count
-1);
3332 if (count
> 1 && CFStringGetLength(CFArrayGetValueAtIndex(components
, 0)) == 0) {
3333 // Absolute path; we need to remove the first component, and check for a drive letter in the second component
3334 CFStringRef firstComponent
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, CFArrayGetValueAtIndex(components
, 1), CFSTR(""), encoding
);
3335 CFArrayRemoveValueAtIndex(components
, 0);
3336 if (CFStringGetLength(firstComponent
) == 2 && CFStringGetCharacterAtIndex(firstComponent
, 1) == '|') {
3338 CFStringRef driveStr
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent
, 0));
3339 CFArraySetValueAtIndex(components
, 0, driveStr
);
3340 CFRelease(driveStr
);
3342 CFRelease(firstComponent
);
3345 newPath
= CFStringCreateByCombiningStrings(allocator
, components
, CFSTR("\\"));
3346 CFRelease(components
);
3347 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
3352 // converts url from a file system path representation to a standard representation
3353 static void _convertToURLRepresentation(struct __CFURL
*url
) {
3354 CFStringRef path
= NULL
;
3355 Boolean isDir
= ((url
->_flags
& IS_DIRECTORY
) != 0);
3356 CFAllocatorRef alloc
= CFGetAllocator(url
);
3358 #if DEBUG_URL_MEMORY_USAGE
3359 numFileURLsConverted
++;
3362 switch (URL_PATH_TYPE(url
)) {
3363 case kCFURLPOSIXPathStyle
:
3364 if (url
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3365 path
= CFRetain(url
->_string
);
3367 path
= POSIXPathToURLPath(url
->_string
, alloc
, isDir
);
3370 case kCFURLHFSPathStyle
:
3371 path
= HFSPathToURLPath(url
->_string
, alloc
, isDir
);
3373 case kCFURLWindowsPathStyle
:
3374 path
= WindowsPathToURLPath(url
->_string
, alloc
, isDir
);
3377 CFAssert2(path
!= NULL
, __kCFLogAssertion
, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__
, url
);
3380 str
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("file://localhost%@"), path
);
3381 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_ABSOLUTE
| IS_PARSED
| HAS_SCHEME
| HAS_HOST
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3382 CFRelease(url
->_string
);
3384 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
) * 3, 0);
3385 url
->ranges
[0] = CFRangeMake(0, 4);
3386 url
->ranges
[1] = CFRangeMake(7, 9);
3387 url
->ranges
[2] = CFRangeMake(16, CFStringGetLength(path
));
3390 CFRelease(url
->_string
);
3391 url
->_flags
= (url
->_flags
& (IS_DIRECTORY
)) | (FULL_URL_REPRESENTATION
<< 16) | IS_DECOMPOSABLE
| IS_PARSED
| HAS_PATH
| ORIGINAL_AND_URL_STRINGS_MATCH
;
3392 url
->_string
= path
;
3393 url
->ranges
= (CFRange
*)CFAllocatorAllocate(alloc
, sizeof(CFRange
), 0);
3394 *(url
->ranges
) = CFRangeMake(0, CFStringGetLength(path
));
3398 // relativeURL is known to be a file system URL whose base is a matching file system URL
3399 static CFURLRef
_CFURLCopyAbsoluteFileURL(CFURLRef relativeURL
) {
3400 CFAllocatorRef alloc
= CFGetAllocator(relativeURL
);
3401 CFURLPathStyle fsType
= URL_PATH_TYPE(relativeURL
);
3402 CFURLRef base
= relativeURL
->_base
;
3403 CFStringRef newPath
= _resolveFileSystemPaths(relativeURL
->_string
, base
->_string
, (base
->_flags
& IS_DIRECTORY
) != 0, fsType
, alloc
);
3404 CFURLRef result
= CFURLCreateWithFileSystemPath(alloc
, newPath
, fsType
, (relativeURL
->_flags
& IS_DIRECTORY
) != 0);
3409 // Caller must release the returned string
3410 static CFStringRef
_resolveFileSystemPaths(CFStringRef relativePath
, CFStringRef basePath
, Boolean baseIsDir
, CFURLPathStyle fsType
, CFAllocatorRef alloc
) {
3411 CFIndex baseLen
= CFStringGetLength(basePath
);
3412 CFIndex relLen
= CFStringGetLength(relativePath
);
3413 UniChar pathDelimiter
= PATH_DELIM_FOR_TYPE(fsType
);
3414 UniChar
*buf
= CFAllocatorAllocate(alloc
, sizeof(UniChar
)*(relLen
+ baseLen
+ 2), 0);
3415 CFStringGetCharacters(basePath
, CFRangeMake(0, baseLen
), buf
);
3417 if (buf
[baseLen
-1] != pathDelimiter
) {
3418 buf
[baseLen
] = pathDelimiter
;
3422 UniChar
*ptr
= buf
+ baseLen
- 1;
3423 while (ptr
> buf
&& *ptr
!= pathDelimiter
) {
3426 baseLen
= ptr
- buf
+ 1;
3428 if (fsType
== kCFURLHFSPathStyle
) {
3429 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3432 CFStringGetCharacters(relativePath
, CFRangeMake(0, relLen
), buf
+ baseLen
);
3433 *(buf
+ baseLen
+ relLen
) = '\0';
3434 return _resolvedPath(buf
, buf
+ baseLen
+ relLen
, pathDelimiter
, false, true, alloc
);
3437 CFURLRef
_CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator
) {
3438 CFURLRef url
= NULL
;
3439 #if defined(__MACOS8__)
3443 if (HGetVol(NULL
, &vRefNum
, &dirID
) == noErr
&& FSMakeFSSpec(vRefNum
, dirID
, NULL
, &fsSpec
) == noErr
) {
3444 url
= _CFCreateURLFromFSSpec(allocator
, (void *)(&fsSpec
), true);
3447 uint8_t buf
[CFMaxPathSize
+ 1];
3448 if (_CFGetCurrentDirectory(buf
, CFMaxPathLength
)) {
3449 url
= CFURLCreateFromFileSystemRepresentation(allocator
, buf
, strlen(buf
), true);
3455 CFURLRef
CFURLCreateWithFileSystemPath(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
) {
3456 Boolean isAbsolute
= true;
3457 CFIndex len
= CFStringGetLength(filePath
);
3458 CFURLRef baseURL
, result
;
3460 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3461 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__
);
3464 case kCFURLPOSIXPathStyle
:
3465 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3467 case kCFURLWindowsPathStyle
:
3468 isAbsolute
= (len
> 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3469 /* Absolute path under Win32 can begin with "\\"
3472 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3474 case kCFURLHFSPathStyle
:
3475 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3481 baseURL
= _CFURLCreateCurrentDirectoryURL(allocator
);
3483 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, filePath
, fsType
, isDirectory
, baseURL
);
3484 if (baseURL
) CFRelease(baseURL
);
3488 CF_EXPORT CFURLRef
CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator
, CFStringRef filePath
, CFURLPathStyle fsType
, Boolean isDirectory
, CFURLRef baseURL
) {
3490 Boolean isAbsolute
= true, releaseFilePath
= false;
3491 UniChar pathDelim
= '\0';
3492 CFIndex len
= CFStringGetLength(filePath
);
3494 CFAssert1(filePath
!= NULL
, __kCFLogAssertion
, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__
);
3495 CFAssert2(fsType
== kCFURLPOSIXPathStyle
|| fsType
== kCFURLHFSPathStyle
|| fsType
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__
, fsType
);
3498 case kCFURLPOSIXPathStyle
:
3499 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) == '/');
3502 case kCFURLWindowsPathStyle
:
3503 isAbsolute
= (len
> 3 && CFStringGetCharacterAtIndex(filePath
, 1) == ':' && CFStringGetCharacterAtIndex(filePath
, 2) == '\\');
3504 /* Absolute path under Win32 can begin with "\\"
3507 if (!isAbsolute
) isAbsolute
= (len
> 2 && CFStringGetCharacterAtIndex(filePath
, 0) == '\\' && CFStringGetCharacterAtIndex(filePath
, 1) == '\\');
3510 case kCFURLHFSPathStyle
:
3511 isAbsolute
= (len
> 0 && CFStringGetCharacterAtIndex(filePath
, 0) != ':');
3518 if (isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) != pathDelim
) {
3519 filePath
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c"), filePath
, pathDelim
);
3520 releaseFilePath
= true;
3521 } else if (!isDirectory
&& len
> 0 && CFStringGetCharacterAtIndex(filePath
, len
-1) == pathDelim
) {
3522 if (len
== 1 || CFStringGetCharacterAtIndex(filePath
, len
-2) == pathDelim
) {
3523 // Override isDirectory
3526 filePath
= CFStringCreateWithSubstring(allocator
, filePath
, CFRangeMake(0, len
-1));
3527 releaseFilePath
= true;
3530 if (!filePath
|| CFStringGetLength(filePath
) == 0) {
3531 if (releaseFilePath
&& filePath
) CFRelease(filePath
);
3534 url
= _CFURLAlloc(allocator
);
3535 _CFURLInit((struct __CFURL
*)url
, filePath
, fsType
, baseURL
);
3536 if (releaseFilePath
) CFRelease(filePath
);
3537 if (isDirectory
) ((struct __CFURL
*)url
)->_flags
|= IS_DIRECTORY
;
3538 if (fsType
== kCFURLPOSIXPathStyle
) {
3539 // 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
3540 // 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
3541 CFStringInlineBuffer buf
;
3542 Boolean sawSlash
= FALSE
;
3543 Boolean mustPrependDotSlash
= FALSE
;
3544 CFIndex idx
, length
= CFStringGetLength(url
->_string
);
3545 CFStringInitInlineBuffer(url
->_string
, &buf
, CFRangeMake(0, length
));
3546 for (idx
= 0; idx
< length
; idx
++) {
3547 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
3548 if (!isPathLegalCharacter(ch
)) break;
3552 } else if (ch
== ':') {
3553 mustPrependDotSlash
= TRUE
;
3557 if (idx
== length
) {
3558 ((struct __CFURL
*)url
)->_flags
|= POSIX_AND_URL_PATHS_MATCH
;
3560 if (mustPrependDotSlash
) {
3561 CFStringRef newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("./%@"), url
->_string
);
3562 CFRelease(url
->_string
);
3563 ((struct __CFURL
*)url
)->_string
= newString
;
3569 CFStringRef
CFURLCopyFileSystemPath(CFURLRef anURL
, CFURLPathStyle pathStyle
) {
3570 CFAssert2(pathStyle
== kCFURLPOSIXPathStyle
|| pathStyle
== kCFURLHFSPathStyle
|| pathStyle
== kCFURLWindowsPathStyle
, __kCFLogAssertion
, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__
, pathStyle
);
3571 return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL
), anURL
, pathStyle
, false);
3574 // 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
3575 CFStringRef
CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator
, CFURLRef anURL
, CFURLPathStyle fsType
, Boolean resolveAgainstBase
) {
3576 CFURLRef base
= resolveAgainstBase
? CFURLGetBaseURL(anURL
) : NULL
;
3577 CFStringRef basePath
= base
? CFURLCreateStringWithFileSystemPath(allocator
, base
, fsType
, false) : NULL
;
3578 CFStringRef relPath
= NULL
;
3580 if (!CF_IS_OBJC(__kCFURLTypeID
, anURL
)) {
3581 // We can grope the ivars
3582 CFURLPathStyle myType
= URL_PATH_TYPE(anURL
);
3583 if (myType
== fsType
) {
3584 relPath
= CFRetain(anURL
->_string
);
3585 } else if (fsType
== kCFURLPOSIXPathStyle
&& myType
== FULL_URL_REPRESENTATION
) {
3586 if (!(anURL
->_flags
& IS_PARSED
)) {
3587 _parseComponentsOfURL(anURL
);
3589 if (anURL
->_flags
& POSIX_AND_URL_PATHS_MATCH
) {
3590 relPath
= _retainedComponentString(anURL
, HAS_PATH
, true, true);
3595 if (relPath
== NULL
) {
3596 CFStringRef urlPath
= CFURLCopyPath(anURL
);
3597 CFStringEncoding enc
= (anURL
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: anURL
->_encoding
;
3600 case kCFURLPOSIXPathStyle
:
3601 relPath
= URLPathToPOSIXPath(urlPath
, allocator
, enc
);
3603 case kCFURLHFSPathStyle
:
3604 relPath
= URLPathToHFSPath(urlPath
, allocator
, enc
);
3606 case kCFURLWindowsPathStyle
:
3607 relPath
= URLPathToWindowsPath(urlPath
, allocator
, enc
);
3610 CFAssert2(true, __kCFLogAssertion
, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__
, fsType
);
3615 if (relPath
&& CFURLHasDirectoryPath(anURL
) && CFStringGetLength(relPath
) > 1 && CFStringGetCharacterAtIndex(relPath
, CFStringGetLength(relPath
)-1) == PATH_DELIM_FOR_TYPE(fsType
)) {
3616 CFStringRef tmp
= CFStringCreateWithSubstring(allocator
, relPath
, CFRangeMake(0, CFStringGetLength(relPath
)-1));
3621 // Note that !resolveAgainstBase implies !base
3622 if (!basePath
|| !relPath
) {
3625 CFStringRef result
= _resolveFileSystemPaths(relPath
, basePath
, CFURLHasDirectoryPath(base
), fsType
, allocator
);
3626 CFRelease(basePath
);
3632 Boolean
CFURLGetFileSystemRepresentation(CFURLRef url
, Boolean resolveAgainstBase
, uint8_t *buffer
, CFIndex bufLen
) {
3634 CFAllocatorRef alloc
= CFGetAllocator(url
);
3636 if (!url
) return false;
3637 #if defined(__WIN32__)
3638 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLWindowsPathStyle
, resolveAgainstBase
);
3639 #elif defined(__MACOS8__)
3640 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLHFSPathStyle
, resolveAgainstBase
);
3642 path
= CFURLCreateStringWithFileSystemPath(alloc
, url
, kCFURLPOSIXPathStyle
, resolveAgainstBase
);
3645 #if defined(__MACH__)
3646 Boolean convResult
= _CFStringGetFileSystemRepresentation(path
, buffer
, bufLen
);
3651 CFIndex pathLen
= CFStringGetLength(path
);
3652 CFIndex numConverted
= CFStringGetBytes(path
, CFRangeMake(0, pathLen
), CFStringFileSystemEncoding(), 0, true, buffer
, bufLen
-1, &usedLen
); // -1 because we need one byte to zero-terminate.
3654 if (numConverted
== pathLen
) {
3655 buffer
[usedLen
] = '\0';
3663 CFURLRef
CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
) {
3664 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
3666 if (!path
) return NULL
;
3667 #if defined(__WIN32__)
3668 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
);
3669 #elif defined(__MACOS8__)
3670 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLHFSPathStyle
, isDirectory
);
3672 newURL
= CFURLCreateWithFileSystemPath(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
);
3678 CF_EXPORT CFURLRef
CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator
, const uint8_t *buffer
, CFIndex bufLen
, Boolean isDirectory
, CFURLRef baseURL
) {
3679 CFStringRef path
= CFStringCreateWithBytes(allocator
, buffer
, bufLen
, CFStringFileSystemEncoding(), false);
3681 if (!path
) return NULL
;
3682 #if defined(__WIN32__)
3683 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLWindowsPathStyle
, isDirectory
, baseURL
);
3684 #elif defined(__MACOS8__)
3685 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLHFSPathStyle
, isDirectory
, baseURL
);
3687 newURL
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, path
, kCFURLPOSIXPathStyle
, isDirectory
, baseURL
);
3694 /******************************/
3695 /* Support for path utilities */
3696 /******************************/
3698 // Assumes url is a CFURL (not an Obj-C NSURL)
3699 static CFRange
_rangeOfLastPathComponent(CFURLRef url
) {
3700 UInt32 pathType
= URL_PATH_TYPE(url
);
3701 CFRange pathRg
, componentRg
;
3703 if (pathType
== FULL_URL_REPRESENTATION
) {
3704 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3705 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3707 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
3710 if (pathRg
.location
== kCFNotFound
|| pathRg
.length
== 0) {
3714 if (CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) == PATH_DELIM_FOR_TYPE(pathType
)) {
3716 if (pathRg
.length
== 0) {
3721 if (CFStringFindWithOptions(url
->_string
, PATH_DELIM_AS_STRING_FOR_TYPE(pathType
), pathRg
, kCFCompareBackwards
, &componentRg
)) {
3722 componentRg
.location
++;
3723 componentRg
.length
= pathRg
.location
+ pathRg
.length
- componentRg
.location
;
3725 componentRg
= pathRg
;
3730 CFStringRef
CFURLCopyLastPathComponent(CFURLRef url
) {
3733 if (CF_IS_OBJC(__kCFURLTypeID
, url
)) {
3734 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
3737 if (!path
) return NULL
;
3738 rg
= CFRangeMake(0, CFStringGetLength(path
));
3739 length
= rg
.length
; // Remember this for comparison later
3740 if (CFStringGetCharacterAtIndex(path
, rg
.length
- 1) == '/') {
3743 if (CFStringFindWithOptions(path
, CFSTR("/"), rg
, kCFCompareBackwards
, &compRg
)) {
3744 rg
.length
= rg
.location
+ rg
.length
- (compRg
.location
+1);
3745 rg
.location
= compRg
.location
+ 1;
3747 if (rg
.location
== 0 && rg
.length
== length
) {
3750 result
= CFStringCreateWithSubstring(NULL
, path
, rg
);
3754 CFRange rg
= _rangeOfLastPathComponent(url
);
3755 if (rg
.location
== kCFNotFound
|| rg
.length
== 0) {
3757 return CFRetain(CFSTR(""));
3759 if (rg
.length
== 1 && CFStringGetCharacterAtIndex(url
->_string
, rg
.location
) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url
))) {
3760 return CFRetain(CFSTR("/"));
3762 result
= CFStringCreateWithSubstring(CFGetAllocator(url
), url
->_string
, rg
);
3763 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
&& !(url
->_flags
& POSIX_AND_URL_PATHS_MATCH
)) {
3765 if (url
->_flags
& IS_OLD_UTF8_STYLE
|| url
->_encoding
== kCFStringEncodingUTF8
) {
3766 tmp
= CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url
), result
, CFSTR(""));
3768 tmp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url
), result
, CFSTR(""), url
->_encoding
);
3777 CFStringRef
CFURLCopyPathExtension(CFURLRef url
) {
3778 CFStringRef lastPathComp
= CFURLCopyLastPathComponent(url
);
3779 CFStringRef ext
= NULL
;
3782 CFRange rg
= CFStringFind(lastPathComp
, CFSTR("."), kCFCompareBackwards
);
3783 if (rg
.location
!= kCFNotFound
) {
3785 rg
.length
= CFStringGetLength(lastPathComp
) - rg
.location
;
3786 if (rg
.length
> 0) {
3787 ext
= CFStringCreateWithSubstring(CFGetAllocator(url
), lastPathComp
, rg
);
3789 ext
= CFRetain(CFSTR(""));
3792 CFRelease(lastPathComp
);
3797 CFURLRef
CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef pathComponent
, Boolean isDirectory
) {
3800 url
= _CFURLFromNSURL(url
);
3801 __CFGenericValidateType(url
, __kCFURLTypeID
);
3802 CFAssert1(pathComponent
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__
);
3804 fsType
= URL_PATH_TYPE(url
);
3805 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(pathComponent
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(pathComponent
)), 0, NULL
)) {
3806 // Must convert to full representation, and then work with it
3807 fsType
= FULL_URL_REPRESENTATION
;
3808 _convertToURLRepresentation((struct __CFURL
*)url
);
3811 if (fsType
== FULL_URL_REPRESENTATION
) {
3812 CFMutableStringRef newString
;
3813 CFStringRef newComp
;
3815 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3816 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
3818 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
3819 newComp
= CFURLCreateStringByAddingPercentEscapes(allocator
, pathComponent
, NULL
, CFSTR(";?"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
3820 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3821 if (!pathRg
.length
|| CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != '/') {
3822 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, CFSTR("/"));
3825 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, newComp
);
3827 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ CFStringGetLength(newComp
), CFSTR("/"));
3830 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
3831 CFRelease(newString
);
3833 UniChar pathDelim
= PATH_DELIM_FOR_TYPE(fsType
);
3834 CFStringRef newString
;
3835 if (CFStringGetCharacterAtIndex(url
->_string
, CFStringGetLength(url
->_string
) - 1) != pathDelim
) {
3837 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@%c"), url
->_string
, pathDelim
, pathComponent
, pathDelim
);
3839 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%c%@"), url
->_string
, pathDelim
, pathComponent
);
3843 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@%c"), url
->_string
, pathComponent
, pathDelim
);
3845 newString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%@%@"), url
->_string
, pathComponent
);
3848 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, isDirectory
, url
->_base
);
3849 CFRelease(newString
);
3854 CFURLRef
CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator
, CFURLRef url
) {
3856 CFMutableStringRef newString
;
3857 CFRange lastCompRg
, pathRg
;
3858 Boolean appendDotDot
= false;
3861 url
= _CFURLFromNSURL(url
);
3862 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
3863 __CFGenericValidateType(url
, __kCFURLTypeID
);
3865 fsType
= URL_PATH_TYPE(url
);
3866 if (fsType
== FULL_URL_REPRESENTATION
) {
3867 if (!(url
->_flags
& IS_PARSED
)) _parseComponentsOfURL(url
);
3868 if (!(url
->_flags
& HAS_PATH
)) return NULL
;
3869 pathRg
= _rangeForComponent(url
->_flags
, url
->ranges
, HAS_PATH
);
3871 pathRg
= CFRangeMake(0, CFStringGetLength(url
->_string
));
3873 lastCompRg
= _rangeOfLastPathComponent(url
);
3874 if (lastCompRg
.length
== 0) {
3875 appendDotDot
= true;
3876 } else if (lastCompRg
.length
== 1) {
3877 UniChar ch
= CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
);
3878 if (ch
== '.' || ch
== PATH_DELIM_FOR_TYPE(fsType
)) {
3879 appendDotDot
= true;
3881 } else if (lastCompRg
.length
== 2 && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
) == '.' && CFStringGetCharacterAtIndex(url
->_string
, lastCompRg
.location
+1) == '.') {
3882 appendDotDot
= true;
3885 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
3888 if (pathRg
.length
> 0 && CFStringGetCharacterAtIndex(url
->_string
, pathRg
.location
+ pathRg
.length
- 1) != PATH_DELIM_FOR_TYPE(fsType
)) {
3889 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
3892 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, CFSTR(".."));
3894 CFStringInsert(newString
, pathRg
.location
+ pathRg
.length
+ delta
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
3896 // 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 "/.".
3897 if (pathRg
.length
+ delta
> 4 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 5) == '.') {
3898 if (pathRg
.length
+delta
> 7 && CFStringGetCharacterAtIndex(newString
, pathRg
.location
+ pathRg
.length
+ delta
- 6) == PATH_DELIM_FOR_TYPE(fsType
)) {
3899 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 6, 2));
3900 } else if (pathRg
.length
+delta
== 5) {
3901 CFStringDelete(newString
, CFRangeMake(pathRg
.location
+ pathRg
.length
+ delta
- 5, 2));
3904 } else if (lastCompRg
.location
== pathRg
.location
) {
3905 CFStringReplace(newString
, pathRg
, CFSTR("."));
3906 CFStringInsert(newString
, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
));
3908 CFStringDelete(newString
, CFRangeMake(lastCompRg
.location
, pathRg
.location
+ pathRg
.length
- lastCompRg
.location
));
3910 if (fsType
== FULL_URL_REPRESENTATION
) {
3911 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
3913 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, true, url
->_base
);
3915 CFRelease(newString
);
3919 CFURLRef
CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator
, CFURLRef url
, CFStringRef extension
) {
3920 CFMutableStringRef newString
;
3923 CFURLPathStyle fsType
;
3925 CFAssert1(url
!= NULL
&& extension
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
3926 url
= _CFURLFromNSURL(url
);
3927 __CFGenericValidateType(url
, __kCFURLTypeID
);
3928 __CFGenericValidateType(extension
, CFStringGetTypeID());
3930 rg
= _rangeOfLastPathComponent(url
);
3931 if (rg
.location
< 0) return NULL
; // No path
3932 fsType
= URL_PATH_TYPE(url
);
3933 if (fsType
!= FULL_URL_REPRESENTATION
&& CFStringFindWithOptions(extension
, PATH_DELIM_AS_STRING_FOR_TYPE(fsType
), CFRangeMake(0, CFStringGetLength(extension
)), 0, NULL
)) {
3934 _convertToURLRepresentation((struct __CFURL
*)url
);
3935 fsType
= FULL_URL_REPRESENTATION
;
3936 rg
= _rangeOfLastPathComponent(url
);
3939 newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
3940 CFStringInsert(newString
, rg
.location
+ rg
.length
, CFSTR("."));
3941 if (fsType
== FULL_URL_REPRESENTATION
) {
3942 CFStringRef newExt
= CFURLCreateStringByAddingPercentEscapes(allocator
, extension
, NULL
, CFSTR(";?/"), (url
->_flags
& IS_OLD_UTF8_STYLE
) ? kCFStringEncodingUTF8
: url
->_encoding
);
3943 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, newExt
);
3945 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
3947 CFStringInsert(newString
, rg
.location
+ rg
.length
+ 1, extension
);
3948 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, fsType
, (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
3950 CFRelease(newString
);
3954 CFURLRef
CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator
, CFURLRef url
) {
3958 CFAssert1(url
!= NULL
, __kCFLogAssertion
, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__
);
3959 url
= _CFURLFromNSURL(url
);
3960 __CFGenericValidateType(url
, __kCFURLTypeID
);
3961 rg
= _rangeOfLastPathComponent(url
);
3962 if (rg
.location
< 0) {
3964 } else if (rg
.length
&& CFStringFindWithOptions(url
->_string
, CFSTR("."), rg
, kCFCompareBackwards
, &dotRg
)) {
3965 CFMutableStringRef newString
= CFStringCreateMutableCopy(allocator
, 0, url
->_string
);
3966 dotRg
.length
= rg
.location
+ rg
.length
- dotRg
.location
;
3967 CFStringDelete(newString
, dotRg
);
3968 if (URL_PATH_TYPE(url
) == FULL_URL_REPRESENTATION
) {
3969 result
= _CFURLCreateWithArbitraryString(allocator
, newString
, url
->_base
);
3971 result
= CFURLCreateWithFileSystemPathRelativeToBase(allocator
, newString
, URL_PATH_TYPE(url
), (url
->_flags
& IS_DIRECTORY
) != 0 ? true : false, url
->_base
);
3973 CFRelease(newString
);
3975 result
= CFRetain(url
);
3980 #if defined(HAVE_CARBONCORE)
3981 // We deal in FSRefs because they handle Unicode strings.
3982 // FSSpecs handle a much more limited set of characters.
3983 static Boolean
__CFFSRefForVolumeName(CFStringRef volName
, FSRef
*spec
, CFAllocatorRef alloc
) {
3986 Boolean success
= false;
3987 CFMutableStringRef str
= CFStringCreateMutableWithExternalCharactersNoCopy(alloc
, NULL
, 0, 0, kCFAllocatorNull
);
3989 for (volIndex
= 1; FSGetVolumeInfo(0, volIndex
, NULL
, kFSVolInfoNone
, NULL
, &name
, spec
) == noErr
; volIndex
++) {
3990 CFStringSetExternalCharactersNoCopy(str
, name
.unicode
, name
.length
, name
.length
);
3991 if (CFStringCompare(str
, volName
, 0) == kCFCompareEqualTo
) {
4000 #define __CFFSRefForVolumeName(A, B, C) (-3296)
4003 static CFArrayRef
HFSPathToURLComponents(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4004 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4005 CFMutableArrayRef newComponents
= CFArrayCreateMutableCopy(alloc
, 0, components
);
4006 Boolean doSpecialLeadingColon
= false;
4007 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
4009 CFRelease(components
);
4011 #if defined(HAVE_CARBONCORE) && !defined(__MACOS8__)
4012 doSpecialLeadingColon
= true;
4015 if (!doSpecialLeadingColon
&& firstChar
!= ':') {
4016 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4017 } else if (firstChar
!= ':') {
4018 // see what we need to add at the beginning. Under MacOS, if the
4019 // first character isn't a ':', then the first component is the
4020 // volume name, and we need to find the mount point. Bleah. If we
4021 // don't find a mount point, we're going to have to lie, and make something up.
4022 CFStringRef firstComp
= CFArrayGetValueAtIndex(newComponents
, 0);
4023 if (CFStringGetLength(firstComp
) == 1 && CFStringGetCharacterAtIndex(firstComp
, 0) == '/') {
4024 // "/" is the "magic" path for a UFS root directory
4025 CFArrayRemoveValueAtIndex(newComponents
, 0);
4026 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4028 // See if we can get a mount point.
4029 Boolean foundMountPoint
= false;
4030 uint8_t buf
[CFMaxPathLength
];
4032 // Now produce an FSSpec from the volume, then try and get the mount point
4033 if (__CFFSRefForVolumeName(firstComp
, &volSpec
, alloc
) && (FSRefMakePath(&volSpec
, buf
, CFMaxPathLength
) == noErr
)) {
4034 // We win! Ladies and gentlemen, we have a mount point.
4035 if (buf
[0] == '/' && buf
[1] == '\0') {
4036 // Special case this common case
4037 foundMountPoint
= true;
4038 CFArrayRemoveValueAtIndex(newComponents
, 0);
4039 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4041 // This is pretty inefficient; we can do better.
4042 CFStringRef mountPoint
= CFStringCreateWithCString(alloc
, buf
, CFStringFileSystemEncoding());
4043 CFArrayRef mountComponents
= mountPoint
? CFStringCreateArrayBySeparatingStrings(alloc
, mountPoint
, CFSTR("/")) : NULL
;
4044 if (mountComponents
) {
4045 CFIndex idx
= CFArrayGetCount(mountComponents
) - 1;
4046 CFArrayRemoveValueAtIndex(newComponents
, 0);
4047 for ( ; idx
>= 0; idx
--) {
4048 CFArrayInsertValueAtIndex(newComponents
, 0, CFArrayGetValueAtIndex(mountComponents
, idx
));
4050 CFRelease(mountComponents
);
4051 foundMountPoint
= true;
4053 if (mountPoint
) CFRelease(mountPoint
);
4056 if (!foundMountPoint
) {
4057 // Fall back to treating the volume name as the top level directory
4058 CFArrayInsertValueAtIndex(newComponents
, 0, CFSTR(""));
4062 CFArrayRemoveValueAtIndex(newComponents
, 0);
4065 cnt
= CFArrayGetCount(newComponents
);
4066 for (i
= 0; i
< cnt
; i
++) {
4067 CFStringRef comp
= CFArrayGetValueAtIndex(newComponents
, i
);
4068 CFStringRef newComp
= NULL
;
4069 CFRange searchRg
, slashRg
;
4070 searchRg
.location
= 0;
4071 searchRg
.length
= CFStringGetLength(comp
);
4072 while (CFStringFindWithOptions(comp
, CFSTR("/"), searchRg
, 0, &slashRg
)) {
4074 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4076 CFStringReplace((CFMutableStringRef
)newComp
, slashRg
, CFSTR(":"));
4077 searchRg
.length
= searchRg
.location
+ searchRg
.length
- slashRg
.location
- 1;
4078 searchRg
.location
= slashRg
.location
+ 1;
4081 CFArraySetValueAtIndex(newComponents
, i
, newComp
);
4085 if (isDir
&& CFStringGetLength(CFArrayGetValueAtIndex(newComponents
, cnt
-1)) != 0) {
4086 CFArrayAppendValue(newComponents
, CFSTR(""));
4088 return newComponents
;
4091 static CFStringRef
HFSPathToURLPath(CFStringRef path
, CFAllocatorRef alloc
, Boolean isDir
) {
4092 CFArrayRef components
= HFSPathToURLComponents(path
, alloc
, isDir
);
4093 CFArrayRef newComponents
= components
? copyStringArrayWithTransformation(components
, escapePathComponent
) : NULL
;
4096 if (components
) CFRelease(components
);
4097 if (!newComponents
) return NULL
;
4099 cnt
= CFArrayGetCount(newComponents
);
4100 if (cnt
== 1 && CFStringGetLength(CFArrayGetValueAtIndex(newComponents
, 0)) == 0) {
4101 result
= CFRetain(CFSTR("/"));
4103 result
= CFStringCreateByCombiningStrings(alloc
, newComponents
, CFSTR("/"));
4105 CFRelease(newComponents
);
4109 static CFMutableStringRef
filePathToHFSPath(unsigned char *buf
, CFAllocatorRef allocator
);
4110 static CFStringRef
colonToSlash(CFStringRef comp
, CFAllocatorRef alloc
);
4112 static CFStringRef
URLPathToHFSPath(CFStringRef path
, CFAllocatorRef allocator
, CFStringEncoding encoding
) {
4113 CFStringRef result
= NULL
;
4114 #if defined(__MACOS8__)
4115 // Slashes become colons; escaped slashes stay slashes.
4116 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
4117 CFMutableArrayRef mutableComponents
= CFArrayCreateMutableCopy(allocator
, 0, components
);
4118 SInt32 count
= CFArrayGetCount(mutableComponents
);
4119 CFStringRef newPath
;
4120 CFRelease(components
);
4122 if (count
&& CFStringGetLength(CFArrayGetValueAtIndex(mutableComponents
, count
-1)) == 0) {
4123 CFArrayRemoveValueAtIndex(mutableComponents
, count
-1);
4126 // On MacOS absolute paths do NOT begin with colon while relative paths DO.
4127 if ((count
> 0) && CFEqual(CFArrayGetValueAtIndex(mutableComponents
, 0), CFSTR(""))) {
4128 CFArrayRemoveValueAtIndex(mutableComponents
, 0);
4130 CFArrayInsertValueAtIndex(mutableComponents
, 0, CFSTR(""));
4132 newPath
= CFStringCreateByCombiningStrings(allocator
, mutableComponents
, CFSTR(":"));
4133 CFRelease(mutableComponents
);
4134 result
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, newPath
, CFSTR(""), encoding
);
4136 #elif defined(HAVE_CARBONCORE)
4137 if (CFStringGetLength(path
) > 0 && CFStringGetCharacterAtIndex(path
, 0) == '/') {
4138 // Absolute path; to do this properly, we need to go to the file system, generate an FSRef, then generate a path from there. That's what filePathToHFSPath does.
4139 CFStringRef nativePath
= URLPathToPOSIXPath(path
, allocator
, encoding
);
4140 unsigned char buf
[CFMaxPathLength
];
4141 if (nativePath
&& _CFStringGetFileSystemRepresentation(nativePath
, buf
, CFMaxPathLength
)) {
4142 result
= filePathToHFSPath(buf
, allocator
);
4144 if (nativePath
) CFRelease(nativePath
);
4145 } else if (CFStringGetLength(path
) == 0) {
4150 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(allocator
, path
, CFSTR("/"));
4151 CFMutableArrayRef mutableComponents
= CFArrayCreateMutableCopy(allocator
, 0, components
);
4152 SInt32 count
= CFArrayGetCount(mutableComponents
);
4154 CFRelease(components
);
4156 if (CFStringGetLength(CFArrayGetValueAtIndex(mutableComponents
, count
-1)) == 0) {
4157 // Strip off the trailing slash
4158 CFArrayRemoveValueAtIndex(mutableComponents
, count
-1);
4160 // On MacOS absolute paths do NOT begin with colon while relative paths DO.
4161 CFArrayInsertValueAtIndex(mutableComponents
, 0, CFSTR(""));
4162 for (i
= 0, c
= CFArrayGetCount(mutableComponents
); i
< c
; i
++) {
4163 CFStringRef origComp
, comp
, newComp
;
4164 origComp
= CFArrayGetValueAtIndex(mutableComponents
, i
);
4165 comp
= CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator
, origComp
, CFSTR(""), encoding
);
4166 newComp
= colonToSlash(comp
, allocator
);
4167 if (newComp
!= origComp
) {
4168 CFArraySetValueAtIndex(mutableComponents
, i
, newComp
);
4173 result
= CFStringCreateByCombiningStrings(allocator
, mutableComponents
, CFSTR(":"));
4174 CFRelease(mutableComponents
);
4180 static CFStringRef
colonToSlash(CFStringRef comp
, CFAllocatorRef alloc
) {
4181 CFStringRef newComp
= NULL
;
4182 CFRange searchRg
, colonRg
;
4183 searchRg
.location
= 0;
4184 searchRg
.length
= CFStringGetLength(comp
);
4185 while (CFStringFindWithOptions(comp
, CFSTR(":"), searchRg
, 0, &colonRg
)) {
4187 newComp
= CFStringCreateMutableCopy(alloc
, searchRg
.location
+ searchRg
.length
, comp
);
4189 CFStringReplace((CFMutableStringRef
)newComp
, colonRg
, CFSTR("/"));
4190 searchRg
.length
= searchRg
.location
+ searchRg
.length
- colonRg
.location
- 1;
4191 searchRg
.location
= colonRg
.location
+ 1;
4201 static CFMutableStringRef
filePathToHFSPath(unsigned char *buf
, CFAllocatorRef allocator
) {
4202 #if defined(HAVE_CARBONCORE)
4203 // The only way to do this right is to get an FSSpec, and then work up the path from there. This is problematic, of course, if the URL doesn't actually represent a file on the disk, but there's no way around that. So - first get the POSIX path, then run it through NativePathNameToFSSpec to get a valid FSSpec. If this succeeds, we iterate upwards using FSGetCatalogInfo to find the names of the parent directories until we reach the volume. REW, 10/29/99
4205 if (FSPathMakeRef(buf
, &fsRef
, NULL
) == noErr
) {
4206 FSRef fsRef2
, *parRef
, *fileRef
;
4207 CFMutableStringRef mString
= CFStringCreateMutable(allocator
, 0);
4208 CFMutableStringRef extString
= CFStringCreateMutableWithExternalCharactersNoCopy(allocator
, NULL
, 0, 0, kCFAllocatorNull
);
4213 while (err
== noErr
) {
4215 FSCatalogInfo catInfo
;
4216 err
= FSGetCatalogInfo(fileRef
, kFSCatInfoParentDirID
, &catInfo
, &name
, NULL
, parRef
);
4218 CFStringSetExternalCharactersNoCopy(extString
, name
.unicode
, name
.length
, 255);
4219 CFStringInsert(mString
, 0, extString
);
4220 if (catInfo
.parentDirID
== fsRtParID
) {
4223 CFStringInsert(mString
, 0, CFSTR(":"));
4226 parRef
= (fileRef
== &fsRef
) ? &fsRef2
: &fsRef
;
4229 CFRelease(extString
);
4238 unsigned char *lastPathComponent
= buf
+ strlen(buf
);
4239 unsigned char *parentPath
;
4240 CFMutableStringRef parentHFSPath
;
4241 lastPathComponent
--;
4242 if (*lastPathComponent
== '/') {
4243 // We're not interested in trailing slashes
4244 *lastPathComponent
= '\0';
4245 lastPathComponent
--;
4247 while (lastPathComponent
> buf
&& *lastPathComponent
!= '/') {
4248 if (*lastPathComponent
== ':') {
4249 *lastPathComponent
= '/';
4251 lastPathComponent
--;
4253 if (lastPathComponent
== buf
) {
4254 parentPath
= (char *)"/";
4255 lastPathComponent
++;
4257 *lastPathComponent
= '\0';
4258 lastPathComponent
++;
4261 parentHFSPath
= filePathToHFSPath(parentPath
, allocator
);
4262 if (parentHFSPath
) {
4263 CFStringAppendCString(parentHFSPath
, ":", kCFStringEncodingASCII
);
4264 CFStringAppendCString(parentHFSPath
, lastPathComponent
, CFStringFileSystemEncoding());
4266 return parentHFSPath
;
4269 //#warning filePathToHFSPath unimplemented in the non-CarbonCore case
4270 return CFStringCreateMutable(allocator
, 0);
4275 Boolean
_CFGetFSSpecFromURL(CFAllocatorRef alloc
, CFURLRef url
, struct FSSpec
*voidspec
) {
4276 Boolean result
= false;
4277 #if defined (__MACOS8__)
4278 CFURLRef absURL
= CFURLCopyAbsoluteURL(url
);
4279 CFStringRef filePath
;
4280 filePath
= CFURLCopyFileSystemPath(absURL
, kCFURLHFSPathStyle
);
4283 result
= _CFGetFSSpecFromPathString(alloc
, filePath
, voidspec
);
4284 CFRelease(filePath
);
4286 #elif defined(HAVE_CARBONCORE)
4288 if (_CFGetFSRefFromURL(alloc
, url
, &fileRef
)) {
4289 result
= (FSGetCatalogInfo(&fileRef
, 0, NULL
, NULL
, (FSSpec
*)voidspec
, NULL
) == noErr
);
4295 static Boolean
_CFGetFSRefFromHFSPath(CFAllocatorRef alloc
, CFStringRef path
, void *voidRef
) {
4296 CFArrayRef components
= CFStringCreateArrayBySeparatingStrings(alloc
, path
, CFSTR(":"));
4297 CFIndex idx
, count
, bufferLen
= 0;
4298 UniChar
*buffer
= NULL
;
4299 Boolean result
= false;
4300 if (components
&& (count
= CFArrayGetCount(components
)) > 0 && __CFFSRefForVolumeName(CFArrayGetValueAtIndex(components
, 0), voidRef
, alloc
)) {
4301 FSRef ref2
, *parentRef
, *newRef
;
4302 parentRef
= voidRef
;
4304 for (idx
= 1; idx
< count
; idx
++ ) {
4305 CFStringRef comp
= CFArrayGetValueAtIndex(components
, idx
);
4306 CFIndex compLength
= CFStringGetLength(comp
);
4307 UniChar
*chars
= (UniChar
*)CFStringGetCharactersPtr(comp
);
4310 bufferLen
= (compLength
< 32) ? 32 : compLength
;
4311 buffer
= CFAllocatorAllocate(alloc
, bufferLen
* sizeof(UniChar
), 0);
4312 } else if (bufferLen
< compLength
) {
4313 buffer
= CFAllocatorReallocate(alloc
, buffer
, compLength
* sizeof(UniChar
), 0);
4314 bufferLen
= compLength
;
4317 CFStringGetCharacters(comp
, CFRangeMake(0, compLength
), chars
);
4319 if (FSMakeFSRefUnicode(parentRef
, compLength
, chars
, CFStringGetSystemEncoding(), newRef
) != noErr
) {
4323 newRef
= (newRef
== &ref2
) ? voidRef
: &ref2
;
4327 if (parentRef
!= voidRef
) {
4328 *((FSRef
*)voidRef
) = *parentRef
;
4331 if (components
) CFRelease(components
);
4332 if (buffer
) CFAllocatorDeallocate(alloc
, buffer
);
4337 static Boolean
_CFGetFSRefFromURL(CFAllocatorRef alloc
, CFURLRef url
, void *voidRef
) {
4338 Boolean result
= false;
4339 #if defined(__MACOS8__)
4341 CFStringRef hfsPath
;
4342 if (!__CFMacOS8HasFSRefs()) return false;
4343 absURL
= CFURLCopyAbsoluteURL(url
);
4344 hfsPath
= absURL
? CFURLCopyFileSystemPath(url
, kCFURLHFSPathStyle
) : NULL
;
4345 result
= hfsPath
? _CFGetFSRefFromHFSPath(alloc
, hfsPath
, voidRef
) : false;
4346 if (absURL
) CFRelease(absURL
);
4347 if (hfsPath
) CFRelease(hfsPath
);
4348 #elif defined(HAVE_CARBONCORE)
4350 CFStringRef filePath
, scheme
;
4351 scheme
= CFURLCopyScheme(url
);
4352 if (scheme
&& !CFEqual(scheme
, kCFURLFileScheme
)) {
4355 } else if (scheme
) {
4358 absURL
= CFURLCopyAbsoluteURL(url
);
4359 if (!CF_IS_OBJC(__kCFURLTypeID
, absURL
) && URL_PATH_TYPE(absURL
) == kCFURLHFSPathStyle
) {
4360 // We special case kCFURLHFSPathStyle because we can avoid the expensive conversion to a POSIX native path -- REW, 2/23/2000
4361 result
= _CFGetFSRefFromHFSPath(alloc
, absURL
->_string
, voidRef
);
4364 filePath
= CFURLCopyFileSystemPath(absURL
, kCFURLPOSIXPathStyle
);
4367 char buf
[CFMaxPathLength
];
4369 result
= (_CFStringGetFileSystemRepresentation(filePath
, buf
, CFMaxPathLength
) && (FSPathMakeRef(buf
, voidRef
, NULL
) == noErr
) ? true : false);
4370 CFRelease(filePath
);
4377 CFURLRef
_CFCreateURLFromFSSpec(CFAllocatorRef alloc
, const struct FSSpec
*voidspec
, Boolean isDirectory
) {
4378 CFURLRef url
= NULL
;
4379 #if defined(__MACOS8__)
4380 CFStringRef str
= _CFCreateStringWithHFSPathFromFSSpec(alloc
, voidspec
);
4382 url
= CFURLCreateWithFileSystemPath(alloc
, str
, kCFURLHFSPathStyle
, isDirectory
);
4385 #elif defined(HAVE_CARBONCORE)
4387 if (FSpMakeFSRef((const FSSpec
*)voidspec
, &ref
) == noErr
) {
4388 url
= _CFCreateURLFromFSRef(alloc
, (void *)(&ref
), isDirectory
);
4394 static CFURLRef
_CFCreateURLFromFSRef(CFAllocatorRef alloc
, const void *voidRef
, Boolean isDirectory
) {
4395 CFURLRef url
= NULL
;
4396 #if defined(__MACOS8__)
4397 CFStringRef path
= _CFCreateStringWithHFSPathFromFSRef(alloc
, voidRef
);
4399 url
= CFURLCreateWithFileSystemPath(alloc
, path
, kCFURLHFSPathStyle
, isDirectory
);
4402 #elif defined(HAVE_CARBONCORE)
4403 uint8_t buf
[CFMaxPathLength
];
4404 if (FSRefMakePath((const FSRef
*)voidRef
, buf
, CFMaxPathLength
) == noErr
) {
4405 url
= CFURLCreateFromFileSystemRepresentation(alloc
, buf
, strlen(buf
), isDirectory
);
4411 CFURLRef
CFURLCreateFromFSRef(CFAllocatorRef allocator
, const FSRef
*fsRef
) {
4412 #if defined(HAVE_CARBONCORE) || defined(__MACOS8__)
4413 Boolean isDirectory
;
4414 FSCatalogInfo catInfo
;
4415 #if defined(__MACOS8__)
4416 if (!__CFMacOS8HasFSRefs()) return NULL
;
4418 if (FSGetCatalogInfo(fsRef
, kFSCatInfoNodeFlags
, &catInfo
, NULL
, NULL
, NULL
) != noErr
) {
4421 isDirectory
= catInfo
.nodeFlags
& kFSNodeIsDirectoryMask
;
4422 return _CFCreateURLFromFSRef(allocator
, fsRef
, isDirectory
);
4428 Boolean
CFURLGetFSRef(CFURLRef url
, FSRef
*fsRef
) {
4429 #if defined(__MACOS8__)
4430 return __CFMacOS8HasFSRefs() ? _CFGetFSRefFromURL(CFGetAllocator(url
), url
, fsRef
) : false;
4432 Boolean result
= false;
4435 result
= _CFGetFSRefFromURL(CFGetAllocator(url
), url
, fsRef
);