]> git.saurik.com Git - apple/cf.git/blob - URL.subproj/CFURL.c
CF-368.25.tar.gz
[apple/cf.git] / URL.subproj / CFURL.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFURL.c
24 Copyright 1998-2004, Apple, Inc. All rights reserved.
25 Responsibility: Becky Willrich
26 */
27
28 #include <CoreFoundation/CFURL.h>
29 #include "CFPriv.h"
30 #include "CFCharacterSetPriv.h"
31 #include <CoreFoundation/CFNumber.h>
32 #include "CFInternal.h"
33 #include "CFStringEncodingConverter.h"
34 #include "CFUtilitiesPriv.h"
35 #include <limits.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__)
40 #include <unistd.h>
41 #endif
42
43 #include <CarbonCore/Files.h>
44 #include <CarbonCore/Aliases.h>
45
46 static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir);
47 static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir);
48 static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir);
49 static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory);
50 static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir);
51 static CFStringRef URLPathToHFSPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding);
52 CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase);
53 extern CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator);
54
55 DEFINE_WEAK_CARBONCORE_FUNC(void, DisposeHandle, (Handle A), (A))
56 DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSNewAlias, (const FSRef *A, const FSRef *B, AliasHandle *C), (A, B, C), -3296)
57 DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSGetVolumeInfo, (FSVolumeRefNum A, ItemCount B, FSVolumeRefNum *C, FSVolumeInfoBitmap D, FSVolumeInfo *E, HFSUniStr255*F, FSRef *G), (A, B, C, D, E, F, G), -3296)
58 DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSGetCatalogInfo, (const FSRef *A, FSCatalogInfoBitmap B, FSCatalogInfo *C, HFSUniStr255 *D, FSSpec *E, FSRef *F), (A, B, C, D, E, F), -3296)
59 DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSMakeFSRefUnicode, (const FSRef *A, UniCharCount B, const UniChar *C, TextEncoding D, FSRef *E), (A, B, C, D, E), -3296)
60 DEFINE_WEAK_CARBONCORE_FUNC(OSStatus, FSPathMakeRef, (const uint8_t *A, FSRef *B, Boolean *C), (A, B, C), -3296)
61 DEFINE_WEAK_CARBONCORE_FUNC(OSStatus, FSRefMakePath, (const FSRef *A, uint8_t *B, UInt32 C), (A, B, C), -3296)
62 DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSpMakeFSRef, (const FSSpec *A, FSRef *B), (A, B), -3296)
63 DEFINE_WEAK_CARBONCORE_FUNC(Size, GetAliasSizeFromPtr, (AliasPtr A), (A), 0)
64 DEFINE_WEAK_CARBONCORE_FUNC(OSErr, _FSGetFSRefInformationFast, (const FSRef* A, SInt16 *B, UInt32 *C, UInt32 *D, Boolean *E, Boolean *F, HFSUniStr255 *G), (A, B, C, D, E, F, G), -3296)
65 DEFINE_WEAK_CARBONCORE_FUNC(CFStringRef, _FSCopyStrippedPathString, (CFAllocatorRef A, CFStringRef B), (A, B), (B ? (CFStringRef)CFRetain(B) : NULL))
66
67 DEFINE_WEAK_CARBONCORE_FUNC(OSErr, _FSGetVolumeByName, ( CFStringRef volumeNameRef, FSVolumeRefNum* vRefNumP), ( volumeNameRef, vRefNumP), -3296 )
68
69
70
71 #if defined(__MACH__)
72 #include <sys/stat.h>
73 #include <sys/types.h>
74 #endif
75
76
77 #ifndef DEBUG_URL_MEMORY_USAGE
78 #define DEBUG_URL_MEMORY_USAGE 0
79 #endif
80
81 #if DEBUG_URL_MEMORY_USAGE
82 static CFAllocatorRef URLAllocator = NULL;
83 static UInt32 numFileURLsCreated = 0;
84 static UInt32 numFileURLsConverted = 0;
85 static UInt32 numFileURLsDealloced = 0;
86 static UInt32 numURLs = 0;
87 static UInt32 numDealloced = 0;
88 static UInt32 numExtraDataAllocated = 0;
89 static UInt32 numURLsWithBaseURL = 0;
90 #endif
91
92 /* The bit flags in myURL->_flags */
93 #define HAS_SCHEME (0x0001)
94 #define HAS_USER (0x0002)
95 #define HAS_PASSWORD (0x0004)
96 #define HAS_HOST (0x0008)
97 #define HAS_PORT (0x0010)
98 #define HAS_PATH (0x0020)
99 #define HAS_PARAMETERS (0x0040)
100 #define HAS_QUERY (0x0080)
101 #define HAS_FRAGMENT (0x0100)
102 #define HAS_HTTP_SCHEME (0x0200)
103 // Last free bit (0x200) in lower word goes here!
104 #define IS_IPV6_ENCODED (0x0400)
105 #define IS_OLD_UTF8_STYLE (0x0800)
106 #define IS_DIRECTORY (0x1000)
107 #define IS_PARSED (0x2000)
108 #define IS_ABSOLUTE (0x4000)
109 #define IS_DECOMPOSABLE (0x8000)
110
111 #define PATH_TYPE_MASK (0x000F0000)
112 /* POSIX_AND_URL_PATHS_MATCH will only be true if the URL and POSIX paths are identical, character for character, except for the presence/absence of a trailing slash on directories */
113 #define POSIX_AND_URL_PATHS_MATCH (0x00100000)
114 #define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000)
115
116 /* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */
117 // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path
118 // #define SCHEME_DIFFERS (0x00400000) unused
119 #define USER_DIFFERS (0x00800000)
120 #define PASSWORD_DIFFERS (0x01000000)
121 #define HOST_DIFFERS (0x02000000)
122 // Port can actually never differ because if there were a non-digit following a colon in the net location, we'd interpret the whole net location as the host
123 #define PORT_DIFFERS (0x04000000)
124 // #define PATH_DIFFERS (0x08000000) unused
125 // #define PARAMETERS_DIFFER (0x10000000) unused
126 // #define QUERY_DIFFERS (0x20000000) unused
127 // #define FRAGMENT_DIFfERS (0x40000000) unused
128
129 // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag
130 #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22)
131
132 // Other useful defines
133 #define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT)
134 #define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT)
135 #define FULL_URL_REPRESENTATION (0xF)
136
137 /* URL_PATH_TYPE(anURL) will be one of the CFURLPathStyle constants, in which case string is a file system path, or will be FULL_URL_REPRESENTATION, in which case the string is the full URL string. One caveat - string always has a trailing path delimiter if the url is a directory URL. This must be stripped before returning file system representations! */
138 #define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16)
139 #define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/'))
140 #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/")))
141
142
143 // In order to get the sizeof ( __CFURL ) < 32 bytes, move these items into a seperate structure which is
144 // only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have
145 // either a sanitized string or a reserved pointer for URLHandle.
146 struct _CFURLAdditionalData {
147 void *_reserved; // Reserved for URLHandle's use.
148 CFMutableStringRef _sanitizedString; // The fully compliant RFC string. This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false. This should never be mutated except when the sanatized string is first computed
149 };
150
151 struct __CFURL {
152 CFRuntimeBase _cfBase;
153 UInt32 _flags;
154 CFStringRef _string; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above)
155 CFURLRef _base;
156 CFRange *ranges;
157 CFStringEncoding _encoding; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set.
158 struct _CFURLAdditionalData* extra;
159 };
160
161
162 CF_INLINE void* _getReserved ( const struct __CFURL* url )
163 {
164 if ( url && url->extra )
165 return url->extra->_reserved;
166
167 return NULL;
168 }
169
170 CF_INLINE CFMutableStringRef _getSanitizedString ( const struct __CFURL* url )
171 {
172 if ( url && url->extra )
173 return url->extra->_sanitizedString;
174
175 return NULL;
176 }
177
178 static void _CFURLAllocateExtraDataspace( struct __CFURL* url )
179 {
180 if ( url && ! url->extra )
181 { struct _CFURLAdditionalData* extra = (struct _CFURLAdditionalData*) CFAllocatorAllocate( CFGetAllocator( url), sizeof( struct _CFURLAdditionalData ), 0 );
182
183 extra->_reserved = _getReserved( url );
184 extra->_sanitizedString = _getSanitizedString( url );
185
186 url->extra = extra;
187
188 #if DEBUG_URL_MEMORY_USAGE
189 numExtraDataAllocated ++;
190 #endif
191 }
192 }
193
194 CF_INLINE void _setReserved ( struct __CFURL* url, void* reserved )
195 {
196 if ( url )
197 {
198 // Don't allocate extra space if we're just going to be storing NULL
199 if ( ! url->extra && reserved )
200 _CFURLAllocateExtraDataspace( url );
201
202 if ( url->extra )
203 url->extra->_reserved = reserved;
204 }
205 }
206
207 CF_INLINE void _setSanitizedString ( struct __CFURL* url, CFMutableStringRef sanitizedString )
208 {
209 if ( url )
210 {
211 // Don't allocate extra space if we're just going to be storing NULL
212 if ( ! url->extra && sanitizedString )
213 _CFURLAllocateExtraDataspace( url );
214
215 if ( url->extra )
216 url->extra->_sanitizedString = sanitizedString;
217 }
218 }
219
220 static void _convertToURLRepresentation(struct __CFURL *url);
221 static CFURLRef _CFURLCopyAbsoluteFileURL(CFURLRef relativeURL);
222 static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc);
223 static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef base, UInt32 *flags, CFRange **range);
224 static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag);
225 static CFRange _netLocationRange(UInt32 flags, CFRange *ranges);
226 static UInt32 _firstResourceSpecifierFlag(UInt32 flags);
227 static void computeSanitizedString(CFURLRef url);
228 static CFStringRef correctedComponent(CFStringRef component, UInt32 compFlag, CFStringEncoding enc);
229 static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges);
230 static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc);
231
232
233 CF_INLINE void _parseComponentsOfURL(CFURLRef url) {
234 _parseComponents(CFGetAllocator(url), url->_string, url->_base, &(((struct __CFURL *)url)->_flags), &(((struct __CFURL *)url)->ranges));
235 }
236
237 static Boolean _createOldUTF8StyleURLs = false;
238
239 CF_INLINE Boolean createOldUTF8StyleURLs(void) {
240 if (_createOldUTF8StyleURLs) {
241 return true;
242 }
243 return !_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar);
244 }
245
246 // Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW
247 CF_EXPORT
248 void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs) {
249 _createOldUTF8StyleURLs = createUTF8URLs;
250 }
251
252 enum {
253 VALID = 1,
254 UNRESERVED = 2,
255 PATHVALID = 4,
256 SCHEME = 8,
257 HEXDIGIT = 16
258 };
259
260 static const unsigned char sURLValidCharacters[] = {
261 /* ' ' 32 */ 0,
262 /* '!' 33 */ VALID | UNRESERVED | PATHVALID ,
263 /* '"' 34 */ 0,
264 /* '#' 35 */ 0,
265 /* '$' 36 */ VALID | PATHVALID ,
266 /* '%' 37 */ 0,
267 /* '&' 38 */ VALID | PATHVALID ,
268 /* ''' 39 */ VALID | UNRESERVED | PATHVALID ,
269 /* '(' 40 */ VALID | UNRESERVED | PATHVALID ,
270 /* ')' 41 */ VALID | UNRESERVED | PATHVALID ,
271 /* '*' 42 */ VALID | UNRESERVED | PATHVALID ,
272 /* '+' 43 */ VALID | SCHEME | PATHVALID ,
273 /* ',' 44 */ VALID | PATHVALID ,
274 /* '-' 45 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
275 /* '.' 46 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
276 /* '/' 47 */ VALID | PATHVALID ,
277 /* '0' 48 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
278 /* '1' 49 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
279 /* '2' 50 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
280 /* '3' 51 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
281 /* '4' 52 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
282 /* '5' 53 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
283 /* '6' 54 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
284 /* '7' 55 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
285 /* '8' 56 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
286 /* '9' 57 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
287 /* ':' 58 */ VALID ,
288 /* ';' 59 */ VALID ,
289 /* '<' 60 */ 0,
290 /* '=' 61 */ VALID | PATHVALID ,
291 /* '>' 62 */ 0,
292 /* '?' 63 */ VALID ,
293 /* '@' 64 */ VALID | PATHVALID ,
294 /* 'A' 65 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
295 /* 'B' 66 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
296 /* 'C' 67 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
297 /* 'D' 68 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
298 /* 'E' 69 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
299 /* 'F' 70 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
300 /* 'G' 71 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
301 /* 'H' 72 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
302 /* 'I' 73 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
303 /* 'J' 74 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
304 /* 'K' 75 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
305 /* 'L' 76 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
306 /* 'M' 77 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
307 /* 'N' 78 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
308 /* 'O' 79 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
309 /* 'P' 80 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
310 /* 'Q' 81 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
311 /* 'R' 82 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
312 /* 'S' 83 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
313 /* 'T' 84 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
314 /* 'U' 85 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
315 /* 'V' 86 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
316 /* 'W' 87 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
317 /* 'X' 88 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
318 /* 'Y' 89 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
319 /* 'Z' 90 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
320 /* '[' 91 */ 0,
321 /* '\' 92 */ 0,
322 /* ']' 93 */ 0,
323 /* '^' 94 */ 0,
324 /* '_' 95 */ VALID | UNRESERVED | PATHVALID ,
325 /* '`' 96 */ 0,
326 /* 'a' 97 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
327 /* 'b' 98 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
328 /* 'c' 99 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
329 /* 'd' 100 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
330 /* 'e' 101 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
331 /* 'f' 102 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT ,
332 /* 'g' 103 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
333 /* 'h' 104 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
334 /* 'i' 105 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
335 /* 'j' 106 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
336 /* 'k' 107 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
337 /* 'l' 108 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
338 /* 'm' 109 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
339 /* 'n' 110 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
340 /* 'o' 111 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
341 /* 'p' 112 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
342 /* 'q' 113 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
343 /* 'r' 114 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
344 /* 's' 115 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
345 /* 't' 116 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
346 /* 'u' 117 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
347 /* 'v' 118 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
348 /* 'w' 119 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
349 /* 'x' 120 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
350 /* 'y' 121 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
351 /* 'z' 122 */ VALID | UNRESERVED | SCHEME | PATHVALID ,
352 /* '{' 123 */ 0,
353 /* '|' 124 */ 0,
354 /* '}' 125 */ 0,
355 /* '~' 126 */ VALID | UNRESERVED | PATHVALID ,
356 /* '' 127 */ 0
357 };
358
359 CF_INLINE Boolean isURLLegalCharacter(UniChar ch) {
360 return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & VALID ) : false;
361 }
362
363 CF_INLINE Boolean scheme_valid(UniChar ch) {
364 return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & SCHEME ) : false;
365 }
366
367 // "Unreserved" as defined by RFC 2396
368 CF_INLINE Boolean isUnreservedCharacter(UniChar ch) {
369 return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & UNRESERVED ) : false;
370 }
371
372 CF_INLINE Boolean isPathLegalCharacter(UniChar ch) {
373 return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & PATHVALID ) : false;
374 }
375
376 CF_INLINE Boolean isHexDigit(UniChar ch) {
377 return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & HEXDIGIT ) : false;
378 }
379
380 // Returns false if ch1 or ch2 isn't properly formatted
381 CF_INLINE Boolean _translateBytes(UniChar ch1, UniChar ch2, uint8_t *result) {
382 *result = 0;
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';
386 else return false;
387
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';
392 else return false;
393
394 return true;
395 }
396
397 CF_INLINE Boolean _haveTestedOriginalString(CFURLRef url) {
398 return ((url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) != 0) || (_getSanitizedString(url) != NULL);
399 }
400
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) {
410 break;
411 }
412 if (unescapedComp != origComp) {
413 if (!mArray) {
414 mArray = CFArrayCreateMutableCopy(alloc, c, array);
415 }
416 CFArraySetValueAtIndex(mArray, i, unescapedComp);
417 }
418 CFRelease(unescapedComp);
419 }
420 if (i != c) {
421 if (mArray) CFRelease(mArray);
422 return NULL;
423 } else if (mArray) {
424 return mArray;
425 } else {
426 CFRetain(array);
427 return array;
428 }
429 }
430
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);
435 } else {
436 return CFURLCreateStringByAddingPercentEscapes(alloc, str, NULL, CFSTR(";?/"), kCFStringEncodingUTF8);
437 }
438 }
439
440 static CFStringRef escapePathComponent(CFAllocatorRef alloc, CFStringRef origComponent, CFIndex componentIndex) {
441 return CFURLCreateStringByAddingPercentEscapes(alloc, origComponent, NULL, CFSTR(";?/"), kCFStringEncodingUTF8);
442 }
443
444 // We have 2 UniChars of a surrogate; we must convert to the correct percent-encoded UTF8 string and append to str. Added so that file system URLs can always be converted from POSIX to full URL representation. -- REW, 8/20/2001
445 static Boolean _hackToConvertSurrogates(UniChar highChar, UniChar lowChar, CFMutableStringRef str) {
446 UniChar surrogate[2];
447 uint8_t bytes[6]; // Aki sez it should never take more than 6 bytes
448 UInt32 len;
449 uint8_t *currByte;
450 surrogate[0] = highChar;
451 surrogate[1] = lowChar;
452 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8, 0, surrogate, 2, NULL, bytes, 6, &len) != kCFStringEncodingConversionSuccess) {
453 return false;
454 }
455 for (currByte = bytes; currByte < bytes + len; currByte ++) {
456 UniChar escapeSequence[3] = {'%', '\0', '\0'};
457 unsigned char high, low;
458 high = ((*currByte) & 0xf0) >> 4;
459 low = (*currByte) & 0x0f;
460 escapeSequence[1] = (high < 10) ? '0' + high : 'A' + high - 10;
461 escapeSequence[2] = (low < 10) ? '0' + low : 'A' + low - 10;
462 CFStringAppendCharacters(str, escapeSequence, 3);
463 }
464 return true;
465 }
466
467 static Boolean _appendPercentEscapesForCharacter(UniChar ch, CFStringEncoding encoding, CFMutableStringRef str) {
468 uint8_t bytes[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more
469 uint8_t *bytePtr = bytes, *currByte;
470 UInt32 byteLength;
471 CFAllocatorRef alloc = NULL;
472 if (CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, 6, &byteLength) != kCFStringEncodingConversionSuccess) {
473 byteLength = CFStringEncodingByteLengthForCharacters(encoding, 0, &ch, 1);
474 if (byteLength <= 6) {
475 // The encoding cannot accomodate the character
476 return false;
477 }
478 alloc = CFGetAllocator(str);
479 bytePtr = CFAllocatorAllocate(alloc, byteLength, 0);
480 if (!bytePtr || CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, byteLength, &byteLength) != kCFStringEncodingConversionSuccess) {
481 if (bytePtr) CFAllocatorDeallocate(alloc, bytePtr);
482 return false;
483 }
484 }
485 for (currByte = bytePtr; currByte < bytePtr + byteLength; currByte ++) {
486 UniChar escapeSequence[3] = {'%', '\0', '\0'};
487 unsigned char high, low;
488 high = ((*currByte) & 0xf0) >> 4;
489 low = (*currByte) & 0x0f;
490 escapeSequence[1] = (high < 10) ? '0' + high : 'A' + high - 10;
491 escapeSequence[2] = (low < 10) ? '0' + low : 'A' + low - 10;
492 CFStringAppendCharacters(str, escapeSequence, 3);
493 }
494 if (bytePtr != bytes) {
495 CFAllocatorDeallocate(alloc, bytePtr);
496 }
497 return true;
498 }
499
500 // Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string.
501 CFStringRef CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc, CFStringRef originalString, CFStringRef charactersToLeaveEscaped) {
502 CFMutableStringRef newStr = NULL;
503 CFIndex length;
504 CFIndex mark = 0;
505 CFRange percentRange, searchRange;
506 CFStringRef escapedStr = NULL;
507 CFMutableStringRef strForEscapedChar = NULL;
508 UniChar escapedChar;
509 Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0);
510 Boolean failed = false;
511
512 if (!originalString) return NULL;
513
514 if (charactersToLeaveEscaped == NULL) {
515 return CFStringCreateCopy(alloc, originalString);
516 }
517
518 length = CFStringGetLength(originalString);
519 searchRange = CFRangeMake(0, length);
520
521 while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) {
522 uint8_t bytes[4]; // Single UTF-8 character could require up to 4 bytes.
523 uint8_t numBytesExpected;
524 UniChar ch1, ch2;
525
526 escapedStr = NULL;
527 // Make sure we have at least 2 more characters
528 if (length - percentRange.location < 3) { failed = true; break; }
529
530 // if we don't have at least 2 more characters, we can't interpret the percent escape code,
531 // so we assume the percent character is legit, and let it pass into the string
532 ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location+1);
533 ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location+2);
534 if (!_translateBytes(ch1, ch2, bytes)) { failed = true; break; }
535 if (!(bytes[0] & 0x80)) {
536 numBytesExpected = 1;
537 } else if (!(bytes[0] & 0x20)) {
538 numBytesExpected = 2;
539 } else if (!(bytes[0] & 0x10)) {
540 numBytesExpected = 3;
541 } else {
542 numBytesExpected = 4;
543 }
544 if (numBytesExpected == 1) {
545 // one byte sequence (most common case); handle this specially
546 escapedChar = bytes[0];
547 if (!strForEscapedChar) {
548 strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull);
549 }
550 escapedStr = strForEscapedChar;
551 } else {
552 CFIndex j;
553 // Make sure up front that we have enough characters
554 if (length < percentRange.location + numBytesExpected * 3) { failed = true; break; }
555 for (j = 1; j < numBytesExpected; j ++) {
556 if (CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j) != '%') { failed = true; break; }
557 ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 1);
558 ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 2);
559 if (!_translateBytes(ch1, ch2, bytes+j)) { failed = true; break; }
560 }
561
562 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99
563 escapedStr = CFStringCreateWithBytes(alloc, bytes, numBytesExpected, kCFStringEncodingUTF8, false);
564 if (!escapedStr) {
565 failed = true;
566 } else if (CFStringGetLength(escapedStr) == 0 && numBytesExpected == 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf) {
567 // Somehow, the UCS-2 BOM got translated in to a UTF8 string
568 escapedChar = 0xfeff;
569 if (!strForEscapedChar) {
570 strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull);
571 }
572 CFRelease(escapedStr);
573 escapedStr = strForEscapedChar;
574 }
575 if (failed) break;
576 }
577
578 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected.
579 searchRange.location = percentRange.location + 3 * numBytesExpected;
580 searchRange.length = length - searchRange.location;
581
582 if (!escapeAll) {
583 if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location != kCFNotFound) {
584 if (escapedStr != strForEscapedChar) {
585 CFRelease(escapedStr);
586 escapedStr = NULL;
587 }
588 continue;
589 }
590 }
591
592 if (!newStr) {
593 newStr = CFStringCreateMutable(alloc, length);
594 }
595 if (percentRange.location - mark > 0) {
596 // The creation of this temporary string is unfortunate.
597 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark));
598 CFStringAppend(newStr, substring);
599 CFRelease(substring);
600 }
601 CFStringAppend(newStr, escapedStr);
602 if (escapedStr != strForEscapedChar) {
603 CFRelease(escapedStr);
604 escapedStr = NULL;
605 }
606 mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence
607 }
608
609 if (escapedStr && escapedStr != strForEscapedChar) CFRelease(escapedStr);
610 if (strForEscapedChar) CFRelease(strForEscapedChar);
611 if (failed) {
612 if (newStr) CFRelease(newStr);
613 return NULL;
614 } else if (newStr) {
615 if (mark < length) {
616 // Need to cat on the remainder of the string
617 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark));
618 CFStringAppend(newStr, substring);
619 CFRelease(substring);
620 }
621 return newStr;
622 } else {
623 return CFStringCreateCopy(alloc, originalString);
624 }
625 }
626
627 CF_EXPORT
628 CFStringRef CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc, CFStringRef originalString, CFStringRef charactersToLeaveEscaped, CFStringEncoding enc) {
629 if (enc == kCFStringEncodingUTF8) {
630 return CFURLCreateStringByReplacingPercentEscapes(alloc, originalString, charactersToLeaveEscaped);
631 } else {
632 CFMutableStringRef newStr = NULL;
633 CFMutableStringRef escapedStr = NULL;
634 CFIndex length;
635 CFIndex mark = 0;
636 CFRange percentRange, searchRange;
637 Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0);
638 Boolean failed = false;
639 uint8_t byteBuffer[8];
640 uint8_t *bytes = byteBuffer;
641 int capacityOfBytes = 8;
642
643 if (!originalString) return NULL;
644
645 if (charactersToLeaveEscaped == NULL) {
646 return CFStringCreateCopy(alloc, originalString);
647 }
648
649 length = CFStringGetLength(originalString);
650 searchRange = CFRangeMake(0, length);
651
652 while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) {
653 UniChar ch1, ch2;
654 CFIndex percentLoc = percentRange.location;
655 CFStringRef convertedString;
656 int numBytesUsed = 0;
657 do {
658 // Make sure we have at least 2 more characters
659 if (length - percentLoc < 3) { failed = true; break; }
660
661 if (numBytesUsed == capacityOfBytes) {
662 if (bytes == byteBuffer) {
663 bytes = CFAllocatorAllocate(alloc, 16 * sizeof(uint8_t), 0);
664 memmove(bytes, byteBuffer, capacityOfBytes);
665 capacityOfBytes = 16;
666 } else {
667 capacityOfBytes = 2*capacityOfBytes;
668 bytes = CFAllocatorReallocate(alloc, bytes, capacityOfBytes * sizeof(uint8_t), 0);
669 }
670 }
671 percentLoc ++;
672 ch1 = CFStringGetCharacterAtIndex(originalString, percentLoc);
673 percentLoc ++;
674 ch2 = CFStringGetCharacterAtIndex(originalString, percentLoc);
675 percentLoc ++;
676 if (!_translateBytes(ch1, ch2, bytes + numBytesUsed)) { failed = true; break; }
677 numBytesUsed ++;
678 } while (CFStringGetCharacterAtIndex(originalString, percentLoc) == '%');
679 searchRange.location = percentLoc;
680 searchRange.length = length - searchRange.location;
681
682 if (failed) break;
683 convertedString = CFStringCreateWithBytes(alloc, bytes, numBytesUsed, enc, false);
684 if (!convertedString) {
685 failed = true;
686 break;
687 }
688
689 if (!newStr) {
690 newStr = CFStringCreateMutable(alloc, length);
691 }
692 if (percentRange.location - mark > 0) {
693 // The creation of this temporary string is unfortunate.
694 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark));
695 CFStringAppend(newStr, substring);
696 CFRelease(substring);
697 }
698
699 if (escapeAll) {
700 CFStringAppend(newStr, convertedString);
701 CFRelease(convertedString);
702 } else {
703 CFIndex i, c = CFStringGetLength(convertedString);
704 if (!escapedStr) {
705 escapedStr = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &ch1, 1, 1, kCFAllocatorNull);
706 }
707 for (i = 0; i < c; i ++) {
708 ch1 = CFStringGetCharacterAtIndex(convertedString, i);
709 if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location == kCFNotFound) {
710 CFStringAppendCharacters(newStr, &ch1, 1);
711 } else {
712 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail
713 _appendPercentEscapesForCharacter(ch1, enc, newStr);
714 }
715 }
716 }
717 mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence
718 }
719
720 if (escapedStr) CFRelease(escapedStr);
721 if (bytes != byteBuffer) CFAllocatorDeallocate(alloc, bytes);
722 if (failed) {
723 if (newStr) CFRelease(newStr);
724 return NULL;
725 } else if (newStr) {
726 if (mark < length) {
727 // Need to cat on the remainder of the string
728 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark));
729 CFStringAppend(newStr, substring);
730 CFRelease(substring);
731 }
732 return newStr;
733 } else {
734 return CFStringCreateCopy(alloc, originalString);
735 }
736 }
737 }
738
739
740 static CFStringRef _addPercentEscapesToString(CFAllocatorRef allocator, CFStringRef originalString, Boolean (*shouldReplaceChar)(UniChar, void*), CFIndex (*handlePercentChar)(CFIndex, CFStringRef, CFStringRef *, void *), CFStringEncoding encoding, void *context) {
741 CFMutableStringRef newString = NULL;
742 CFIndex idx, length;
743 CFStringInlineBuffer buf;
744
745 if (!originalString) return NULL;
746 length = CFStringGetLength(originalString);
747 if (length == 0) return CFStringCreateCopy(allocator, originalString);
748 CFStringInitInlineBuffer(originalString, &buf, CFRangeMake(0, length));
749
750 for (idx = 0; idx < length; idx ++) {
751 UniChar ch = CFStringGetCharacterFromInlineBuffer(&buf, idx);
752 Boolean shouldReplace = shouldReplaceChar(ch, context);
753 if (shouldReplace) {
754 // Perform the replacement
755 if (!newString) {
756 newString = CFStringCreateMutableCopy(CFGetAllocator(originalString), 0, originalString);
757 CFStringDelete(newString, CFRangeMake(idx, length-idx));
758 }
759 if (!_appendPercentEscapesForCharacter(ch, encoding, newString)) {
760 //#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars
761 if (encoding == kCFStringEncodingUTF8 && CFCharacterSetIsSurrogateHighCharacter(ch) && idx + 1 < length && CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf, idx+1))) {
762 // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation
763 if (_hackToConvertSurrogates(ch, CFStringGetCharacterFromInlineBuffer(&buf, idx+1), newString)) {
764 idx ++; // We consumed 2 characters, not 1
765 } else {
766 break;
767 }
768 } else {
769 break;
770 }
771 }
772 } else if (ch == '%' && handlePercentChar) {
773 CFStringRef replacementString = NULL;
774 CFIndex newIndex = handlePercentChar(idx, originalString, &replacementString, context);
775 if (newIndex < 0) {
776 break;
777 } else if (replacementString) {
778 if (!newString) {
779 newString = CFStringCreateMutableCopy(CFGetAllocator(originalString), 0, originalString);
780 CFStringDelete(newString, CFRangeMake(idx, length-idx));
781 }
782 CFStringAppend(newString, replacementString);
783 CFRelease(replacementString);
784 }
785 if (newIndex == idx) {
786 if (newString) {
787 CFStringAppendCharacters(newString, &ch, 1);
788 }
789 } else {
790 if (!replacementString && newString) {
791 CFIndex tmpIndex;
792 for (tmpIndex = idx; tmpIndex < newIndex; tmpIndex ++) {
793 ch = CFStringGetCharacterAtIndex(originalString, idx);
794 CFStringAppendCharacters(newString, &ch, 1);
795 }
796 }
797 idx = newIndex - 1;
798 }
799 } else if (newString) {
800 CFStringAppendCharacters(newString, &ch, 1);
801 }
802 }
803 if (idx < length) {
804 // Ran in to an encoding failure
805 if (newString) CFRelease(newString);
806 return NULL;
807 } else if (newString) {
808 return newString;
809 } else {
810 return CFStringCreateCopy(CFGetAllocator(originalString), originalString);
811 }
812 }
813
814
815 static Boolean _stringContainsCharacter(CFStringRef string, UniChar ch) {
816 CFIndex i, c = CFStringGetLength(string);
817 CFStringInlineBuffer buf;
818 CFStringInitInlineBuffer(string, &buf, CFRangeMake(0, c));
819 for (i = 0; i < c; i ++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf, i) == ch) return true;
820 return false;
821 }
822
823 static Boolean _shouldPercentReplaceChar(UniChar ch, void *context) {
824 CFStringRef unescape = ((CFStringRef *)context)[0];
825 CFStringRef escape = ((CFStringRef *)context)[1];
826 Boolean shouldReplace = (isURLLegalCharacter(ch) == false);
827 if (shouldReplace) {
828 if (unescape && _stringContainsCharacter(unescape, ch)) {
829 shouldReplace = false;
830 }
831 } else if (escape && _stringContainsCharacter(escape, ch)) {
832 shouldReplace = true;
833 }
834 return shouldReplace;
835 }
836
837 CF_EXPORT CFStringRef CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator, CFStringRef originalString, CFStringRef charactersToLeaveUnescaped, CFStringRef legalURLCharactersToBeEscaped, CFStringEncoding encoding) {
838 CFStringRef strings[2];
839 strings[0] = charactersToLeaveUnescaped;
840 strings[1] = legalURLCharactersToBeEscaped;
841 return _addPercentEscapesToString(allocator, originalString, _shouldPercentReplaceChar, NULL, encoding, strings);
842 }
843
844 static Boolean __CFURLEqual(CFTypeRef cf1, CFTypeRef cf2) {
845 CFURLRef url1 = cf1;
846 CFURLRef url2 = cf2;
847 UInt32 pathType1, pathType2;
848
849 __CFGenericValidateType(cf1, CFURLGetTypeID());
850 __CFGenericValidateType(cf2, CFURLGetTypeID());
851
852 if (url1 == url2) return true;
853 if ((url1->_flags & IS_PARSED) && (url2->_flags & IS_PARSED) && (url1->_flags & IS_DIRECTORY) != (url2->_flags & IS_DIRECTORY)) return false;
854 if ( url1->_base ) {
855 if (! url2->_base) return false;
856 if (!CFEqual( url1->_base, url2->_base )) return false;
857 } else if ( url2->_base) {
858 return false;
859 }
860
861 pathType1 = URL_PATH_TYPE(url1);
862 pathType2 = URL_PATH_TYPE(url2);
863 if (pathType1 == pathType2) {
864 if (pathType1 != FULL_URL_REPRESENTATION) {
865 return CFEqual(url1->_string, url2->_string);
866 } else {
867 // Do not compare the original strings; compare the sanatized strings.
868 return CFEqual(CFURLGetString(url1), CFURLGetString(url2));
869 }
870 } else {
871 // Try hard to avoid the expensive conversion from a file system representation to the canonical form
872 CFStringRef scheme1 = CFURLCopyScheme(url1);
873 CFStringRef scheme2 = CFURLCopyScheme(url2);
874 Boolean eq;
875 if (scheme1 && scheme2) {
876 eq = CFEqual(scheme1, scheme2);
877 CFRelease(scheme1);
878 CFRelease(scheme2);
879 } else if (!scheme1 && !scheme2) {
880 eq = TRUE;
881 } else {
882 eq = FALSE;
883 if (scheme1) CFRelease(scheme1);
884 else CFRelease(scheme2);
885 }
886 if (!eq) return false;
887
888 if (pathType1 == FULL_URL_REPRESENTATION) {
889 if (!(url1->_flags & IS_PARSED)) {
890 _parseComponentsOfURL(url1);
891 }
892 if (url1->_flags & (HAS_USER | HAS_PORT | HAS_PASSWORD | HAS_QUERY | HAS_PARAMETERS | HAS_FRAGMENT )) {
893 return false;
894 }
895 }
896
897 if (pathType2 == FULL_URL_REPRESENTATION) {
898 if (!(url2->_flags & IS_PARSED)) {
899 _parseComponentsOfURL(url2);
900 }
901 if (url2->_flags & (HAS_USER | HAS_PORT | HAS_PASSWORD | HAS_QUERY | HAS_PARAMETERS | HAS_FRAGMENT )) {
902 return false;
903 }
904 }
905
906 // No help for it; we now must convert to the canonical representation and compare.
907 return CFEqual(CFURLGetString(url1), CFURLGetString(url2));
908 }
909 }
910
911 static UInt32 __CFURLHash(CFTypeRef cf) {
912 /* This is tricky, because we do not want the hash value to change as a file system URL is changed to its canonical representation, nor do we wish to force the conversion to the canonical representation. We choose instead to take the last path component (or "/" in the unlikely case that the path is empty), then hash on that. */
913 CFURLRef url = cf;
914 UInt32 result;
915 if (CFURLCanBeDecomposed(url)) {
916 CFStringRef lastComp = CFURLCopyLastPathComponent(url);
917 CFStringRef hostNameRef = CFURLCopyHostName(url );
918
919 result = 0;
920
921 if (lastComp) {
922 result = CFHash(lastComp);
923 CFRelease(lastComp);
924 }
925
926 if ( hostNameRef )
927 {
928 result ^= CFHash( hostNameRef );
929 CFRelease( hostNameRef );
930 }
931 } else {
932 result = CFHash(CFURLGetString(url));
933 }
934 return result;
935 }
936
937 static CFStringRef __CFURLCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
938 CFURLRef url = cf;
939 __CFGenericValidateType(cf, CFURLGetTypeID());
940 if (! url->_base) {
941 CFRetain(url->_string);
942 return url->_string;
943 } else {
944 // Do not dereference url->_base; it may be an ObjC object
945 return CFStringCreateWithFormat(CFGetAllocator(url), NULL, CFSTR("%@ -- %@"), url->_string, url->_base);
946 }
947 }
948
949
950 static CFStringRef __CFURLCopyDescription(CFTypeRef cf) {
951 CFURLRef url = (CFURLRef)cf;
952 CFStringRef result;
953 CFAllocatorRef alloc = CFGetAllocator(url);
954 if ( url->_base) {
955 CFStringRef baseString = CFCopyDescription(url->_base);
956 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{type = %d, string = %@,\n\tbase = %@}"), cf, alloc, URL_PATH_TYPE(url), url->_string, baseString);
957 CFRelease(baseString);
958 } else {
959 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{type = %d, string = %@, base = (null)}"), cf, alloc, URL_PATH_TYPE(url), url->_string);
960 }
961 return result;
962 }
963
964 #if DEBUG_URL_MEMORY_USAGE
965
966 extern __attribute((used)) void __CFURLDumpMemRecord(void) {
967 CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d URLs created; %d destroyed\n%d file URLs created; %d converted; %d destroyed. %d urls had 'extra' data allocated, %d had base urls\n"), numURLs, numDealloced, numFileURLsCreated, numFileURLsConverted, numFileURLsDealloced, numExtraDataAllocated, numURLsWithBaseURL );
968 CFShow(str);
969 CFRelease(str);
970 // if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator);
971 }
972 #endif
973
974 static void __CFURLDeallocate(CFTypeRef cf) {
975 CFURLRef url = cf;
976 CFAllocatorRef alloc;
977 __CFGenericValidateType(cf, CFURLGetTypeID());
978 alloc = CFGetAllocator(url);
979 #if DEBUG_URL_MEMORY_USAGE
980 numDealloced ++;
981 if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) {
982 numFileURLsDealloced ++;
983 }
984 #endif
985 if (url->_string) CFRelease(url->_string); // GC: 3879914
986 if (url->_base) CFRelease(url->_base);
987 if (url->ranges) CFAllocatorDeallocate(alloc, url->ranges);
988 if (_getSanitizedString(url)) CFRelease(_getSanitizedString(url));
989
990 if ( url->extra != NULL )
991 CFAllocatorDeallocate( alloc, url->extra );
992 }
993
994 static CFTypeID __kCFURLTypeID = _kCFRuntimeNotATypeID;
995
996 static const CFRuntimeClass __CFURLClass = {
997 0,
998 "CFURL",
999 NULL, // init
1000 NULL, // copy
1001 __CFURLDeallocate,
1002 __CFURLEqual,
1003 __CFURLHash,
1004 __CFURLCopyFormattingDescription,
1005 __CFURLCopyDescription
1006 };
1007
1008 CONST_STRING_DECL(kCFURLFileScheme, "file")
1009 CONST_STRING_DECL(kCFURLLocalhost, "localhost")
1010
1011 __private_extern__ void __CFURLInitialize(void) {
1012 __kCFURLTypeID = _CFRuntimeRegisterClass(&__CFURLClass);
1013 }
1014
1015 /* Toll-free bridging support; get the true CFURL from an NSURL */
1016 CF_INLINE CFURLRef _CFURLFromNSURL(CFURLRef url) {
1017 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID, CFURLRef, url, "_cfurl");
1018 return url;
1019 }
1020
1021 CFTypeID CFURLGetTypeID(void) {
1022 return __kCFURLTypeID;
1023 }
1024
1025 __private_extern__ void CFShowURL(CFURLRef url) {
1026 if (!url) {
1027 fprintf(stdout, "(null)\n");
1028 return;
1029 }
1030 fprintf(stdout, "<CFURL %p>{", (const void*)url);
1031 if (CF_IS_OBJC(__kCFURLTypeID, url)) {
1032 fprintf(stdout, "ObjC bridged object}\n");
1033 return;
1034 }
1035 fprintf(stdout, "\n\tPath type: ");
1036 switch (URL_PATH_TYPE(url)) {
1037 case kCFURLPOSIXPathStyle:
1038 fprintf(stdout, "POSIX");
1039 break;
1040 case kCFURLHFSPathStyle:
1041 fprintf(stdout, "HFS");
1042 break;
1043 case kCFURLWindowsPathStyle:
1044 fprintf(stdout, "NTFS");
1045 break;
1046 case FULL_URL_REPRESENTATION:
1047 fprintf(stdout, "Native URL");
1048 break;
1049 default:
1050 fprintf(stdout, "UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url));
1051 }
1052 fprintf(stdout, "\n\tRelative string: ");
1053 CFShow(url->_string);
1054 fprintf(stdout, "\tBase URL: ");
1055 if (url->_base) {
1056 fprintf(stdout, "<%p> ", (const void*)url->_base);
1057 CFShow(url->_base);
1058 } else {
1059 fprintf(stdout, "(null)\n");
1060 }
1061 fprintf(stdout, "\tFlags: %p\n}\n", (const void*)url->_flags);
1062 }
1063
1064
1065 /***************************************************/
1066 /* URL creation and String/Data creation from URLS */
1067 /***************************************************/
1068 static void constructBuffers(CFAllocatorRef alloc, CFStringRef string, const unsigned char **cstring, const UniChar **ustring, Boolean *useCString, Boolean *freeCharacters) {
1069 CFIndex neededLength;
1070 CFIndex length;
1071 CFRange rg;
1072
1073 *cstring = CFStringGetCStringPtr(string, kCFStringEncodingISOLatin1);
1074 if (*cstring) {
1075 *ustring = NULL;
1076 *useCString = true;
1077 *freeCharacters = false;
1078 return;
1079 }
1080
1081 *ustring = CFStringGetCharactersPtr(string);
1082 if (*ustring) {
1083 *useCString = false;
1084 *freeCharacters = false;
1085 return;
1086 }
1087
1088 *freeCharacters = true;
1089 length = CFStringGetLength(string);
1090 rg = CFRangeMake(0, length);
1091 CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, NULL, INT_MAX, &neededLength);
1092 if (neededLength == length) {
1093 char *buf = CFAllocatorAllocate(alloc, length, 0);
1094 CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, buf, length, NULL);
1095 *cstring = buf;
1096 *useCString = true;
1097 } else {
1098 UniChar *buf = CFAllocatorAllocate(alloc, length * sizeof(UniChar), 0);
1099 CFStringGetCharacters(string, rg, buf);
1100 *useCString = false;
1101 *ustring = buf;
1102 }
1103 }
1104
1105 #define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)])
1106 static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef baseURL, UInt32 *theFlags, CFRange **range) {
1107 CFRange ranges[9];
1108 /* index gives the URL part involved; to calculate the correct range index, use the number of the bit of the equivalent flag (i.e. the host flag is HAS_HOST, which is 0x8. so the range index for the host is 3.) Note that this is true in this function ONLY, since the ranges stored in (*range) are actually packed, skipping those URL components that don't exist. This is why the indices are hard-coded in this function. */
1109
1110 CFIndex idx, base_idx = 0;
1111 CFIndex string_length;
1112 UInt32 flags = (IS_PARSED | *theFlags);
1113 Boolean useCString, freeCharacters, isCompliant;
1114 uint8_t numRanges = 0;
1115 const unsigned char *cstring = NULL;
1116 const UniChar *ustring = NULL;
1117
1118 string_length = CFStringGetLength(string);
1119 constructBuffers(alloc, string, &cstring, &ustring, &useCString, &freeCharacters);
1120
1121 // Algorithm is as described in RFC 1808
1122 // 1: parse the fragment; remainder after left-most "#" is fragment
1123 for (idx = base_idx; idx < string_length; idx++) {
1124 if ('#' == STRING_CHAR(idx)) {
1125 flags |= HAS_FRAGMENT;
1126 ranges[8].location = idx + 1;
1127 ranges[8].length = string_length - (idx + 1);
1128 numRanges ++;
1129 string_length = idx; // remove fragment from parse string
1130 break;
1131 }
1132 }
1133 // 2: parse the scheme
1134 for (idx = base_idx; idx < string_length; idx++) {
1135 UniChar ch = STRING_CHAR(idx);
1136 if (':' == ch) {
1137 flags |= HAS_SCHEME;
1138 flags |= IS_ABSOLUTE;
1139 ranges[0].location = base_idx;
1140 ranges[0].length = idx;
1141 numRanges ++;
1142 base_idx = idx + 1;
1143 // optimization for http urls
1144 if (idx == 4 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' &&
1145 STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p')
1146 {
1147 flags |= HAS_HTTP_SCHEME;
1148 }
1149 break;
1150 } else if (!scheme_valid(ch)) {
1151 break; // invalid scheme character -- no scheme
1152 }
1153 }
1154
1155 // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff)
1156 // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage
1157 // expects this to be treated identically to "scheme://" - REW, 12/08/03
1158 if (!(flags & HAS_SCHEME)) {
1159 isCompliant = true;
1160 } else if (base_idx == string_length) {
1161 isCompliant = false;
1162 } else if (STRING_CHAR(base_idx) != '/') {
1163 isCompliant = false;
1164 } else {
1165 isCompliant = true;
1166 }
1167
1168 if (!isCompliant) {
1169 // Clear the fragment flag if it's been set
1170 if (flags & HAS_FRAGMENT) {
1171 flags &= (~HAS_FRAGMENT);
1172 string_length = CFStringGetLength(string);
1173 }
1174 (*theFlags) = flags;
1175 (*range) = CFAllocatorAllocate(alloc, sizeof(CFRange), 0);
1176 (*range)->location = ranges[0].location;
1177 (*range)->length = ranges[0].length;
1178
1179 if (freeCharacters) {
1180 CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring);
1181 }
1182 return;
1183 }
1184 // URL is 1808-compliant
1185 flags |= IS_DECOMPOSABLE;
1186
1187 // 3: parse the network location and login
1188 if (2 <= (string_length - base_idx) && '/' == STRING_CHAR(base_idx) && '/' == STRING_CHAR(base_idx+1)) {
1189 CFIndex base = 2 + base_idx, extent;
1190 for (idx = base; idx < string_length; idx++) {
1191 if ('/' == STRING_CHAR(idx) || '?' == STRING_CHAR(idx)) break;
1192 }
1193 extent = idx;
1194
1195 // net_loc parts extend from base to extent (but not including), which might be to end of string
1196 // net location is "<user>:<password>@<host>:<port>"
1197 if (extent != base) {
1198 for (idx = base; idx < extent; idx++) {
1199 if ('@' == STRING_CHAR(idx)) { // there is a user
1200 CFIndex idx2;
1201 flags |= HAS_USER;
1202 numRanges ++;
1203 ranges[1].location = base; // base of the user
1204 for (idx2 = base; idx2 < idx; idx2++) {
1205 if (':' == STRING_CHAR(idx2)) { // found a password separator
1206 flags |= HAS_PASSWORD;
1207 numRanges ++;
1208 ranges[2].location = idx2+1; // base of the password
1209 ranges[2].length = idx-(idx2+1); // password extent
1210 ranges[1].length = idx2 - base; // user extent
1211 break;
1212 }
1213 }
1214 if (!(flags & HAS_PASSWORD)) {
1215 // user extends to the '@'
1216 ranges[1].length = idx - base; // user extent
1217 }
1218 base = idx + 1;
1219 break;
1220 }
1221 }
1222 flags |= HAS_HOST;
1223 numRanges ++;
1224 ranges[3].location = base; // base of host
1225
1226 // base has been advanced past the user and password if they existed
1227 for (idx = base; idx < extent; idx++) {
1228 // IPV6 support (RFC 2732) DCJ June/10/2002
1229 if ('[' == STRING_CHAR(idx)) { // starting IPV6 explicit address
1230 // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end
1231 for ( ; idx < extent; ++ idx ) {
1232 if ( ']' == STRING_CHAR(idx)) {
1233 flags |= IS_IPV6_ENCODED;
1234 break;
1235 }
1236 }
1237 }
1238 // there is a port if we see a colon. Only the last one is the port, though.
1239 else if ( ':' == STRING_CHAR(idx)) {
1240 flags |= HAS_PORT;
1241 numRanges ++;
1242 ranges[4].location = idx+1; // base of port
1243 ranges[4].length = extent - (idx+1); // port extent
1244 ranges[3].length = idx - base; // host extent
1245 break;
1246 }
1247 }
1248 if (!(flags & HAS_PORT)) {
1249 ranges[3].length = extent - base; // host extent
1250 }
1251 }
1252 base_idx = extent;
1253 }
1254
1255 // 4: parse the query; remainder after left-most "?" is query
1256 for (idx = base_idx; idx < string_length; idx++) {
1257 if ('?' == STRING_CHAR(idx)) {
1258 flags |= HAS_QUERY;
1259 numRanges ++;
1260 ranges[7].location = idx + 1;
1261 ranges[7].length = string_length - (idx+1);
1262 string_length = idx; // remove query from parse string
1263 break;
1264 }
1265 }
1266
1267 // 5: parse the parameters; remainder after left-most ";" is parameters
1268 for (idx = base_idx; idx < string_length; idx++) {
1269 if (';' == STRING_CHAR(idx)) {
1270 flags |= HAS_PARAMETERS;
1271 numRanges ++;
1272 ranges[6].location = idx + 1;
1273 ranges[6].length = string_length - (idx+1);
1274 string_length = idx; // remove parameters from parse string
1275 break;
1276 }
1277 }
1278
1279 // 6: parse the path; it's whatever's left between string_length & base_idx
1280 if (string_length - base_idx != 0 || (flags & NET_LOCATION_MASK))
1281 {
1282 // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/"
1283 UniChar ch;
1284 Boolean isDir;
1285 CFRange pathRg;
1286 flags |= HAS_PATH;
1287 numRanges ++;
1288 pathRg.location = base_idx;
1289 pathRg.length = string_length - base_idx;
1290 ranges[5] = pathRg;
1291
1292 if (pathRg.length > 0) {
1293 Boolean sawPercent = FALSE;
1294 for (idx = pathRg.location; idx < string_length; idx++) {
1295 if ('%' == STRING_CHAR(idx)) {
1296 sawPercent = TRUE;
1297 break;
1298 }
1299 }
1300 if (!sawPercent) {
1301 flags |= POSIX_AND_URL_PATHS_MATCH;
1302 }
1303
1304 ch = STRING_CHAR(pathRg.location + pathRg.length - 1);
1305 if (ch == '/') {
1306 isDir = true;
1307 } else if (ch == '.') {
1308 if (pathRg.length == 1) {
1309 isDir = true;
1310 } else {
1311 ch = STRING_CHAR(pathRg.location + pathRg.length - 2);
1312 if (ch == '/') {
1313 isDir = true;
1314 } else if (ch != '.') {
1315 isDir = false;
1316 } else if (pathRg.length == 2) {
1317 isDir = true;
1318 } else {
1319 isDir = (STRING_CHAR(pathRg.location + pathRg.length - 3) == '/');
1320 }
1321 }
1322 } else {
1323 isDir = false;
1324 }
1325 } else {
1326 isDir = (baseURL != NULL) ? CFURLHasDirectoryPath(baseURL) : false;
1327 }
1328 if (isDir) {
1329 flags |= IS_DIRECTORY;
1330 }
1331 }
1332
1333 if (freeCharacters) {
1334 CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring);
1335 }
1336 (*theFlags) = flags;
1337 (*range) = CFAllocatorAllocate(alloc, sizeof(CFRange)*numRanges, 0);
1338 numRanges = 0;
1339 for (idx = 0, flags = 1; flags != (1<<9); flags = (flags<<1), idx ++) {
1340 if ((*theFlags) & flags) {
1341 (*range)[numRanges] = ranges[idx];
1342 numRanges ++;
1343 }
1344 }
1345 }
1346
1347 static Boolean scanCharacters(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const unsigned char *cstring, const UniChar *ustring, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding) {
1348 CFIndex idx;
1349 Boolean sawIllegalChar = false;
1350 for (idx = base; idx < end; idx ++) {
1351 Boolean shouldEscape;
1352 UniChar ch = STRING_CHAR(idx);
1353 if (isURLLegalCharacter(ch)) {
1354 if ((componentFlag == HAS_USER || componentFlag == HAS_PASSWORD) && (ch == '/' || ch == '?' || ch == '@')) {
1355 shouldEscape = true;
1356 } else {
1357 shouldEscape = false;
1358 }
1359 } else if (ch == '%' && idx + 2 < end && isHexDigit(STRING_CHAR(idx + 1)) && isHexDigit(STRING_CHAR(idx+2))) {
1360 shouldEscape = false;
1361 } else if (componentFlag == HAS_HOST && ((idx == base && ch == '[') || (idx == end-1 && ch == ']'))) {
1362 shouldEscape = false;
1363 } else {
1364 shouldEscape = true;
1365 }
1366 if (!shouldEscape) continue;
1367
1368 sawIllegalChar = true;
1369 if (componentFlag && flags) {
1370 *flags |= (componentFlag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG);
1371 }
1372 if (!*escapedString) {
1373 *escapedString = CFStringCreateMutable(alloc, 0);
1374 }
1375 if (useCString) {
1376 CFStringRef tempString = CFStringCreateWithBytes(alloc, &(cstring[*mark]), idx - *mark, kCFStringEncodingISOLatin1, false);
1377 CFStringAppend(*escapedString, tempString);
1378 CFRelease(tempString);
1379 } else {
1380 CFStringAppendCharacters(*escapedString, &(ustring[*mark]), idx - *mark);
1381 }
1382 *mark = idx + 1;
1383 _appendPercentEscapesForCharacter(ch, encoding, *escapedString); // This can never fail because anURL->_string was constructed from the encoding passed in
1384 }
1385 return sawIllegalChar;
1386 }
1387
1388 static void computeSanitizedString(CFURLRef url) {
1389 CFAllocatorRef alloc = CFGetAllocator(url);
1390 CFIndex string_length = CFStringGetLength(url->_string);
1391 Boolean useCString, freeCharacters;
1392 const unsigned char *cstring = NULL;
1393 const UniChar *ustring = NULL;
1394 CFIndex base; // where to scan from
1395 CFIndex mark; // first character not-yet copied to sanitized string
1396 if (!(url->_flags & IS_PARSED)) {
1397 _parseComponentsOfURL(url);
1398 }
1399 constructBuffers(alloc, url->_string, &cstring, &ustring, &useCString, &freeCharacters);
1400 if (!(url->_flags & IS_DECOMPOSABLE)) {
1401 // Impossible to have a problem character in the scheme
1402 CFMutableStringRef sanitizedString = NULL;
1403 base = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME).length + 1;
1404 mark = 0;
1405 if (!scanCharacters(alloc, & sanitizedString, &(((struct __CFURL *)url)->_flags), cstring, ustring, useCString, base, string_length, &mark, 0, url->_encoding)) {
1406 ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH;
1407 }
1408 if ( sanitizedString ) {
1409 _setSanitizedString( (struct __CFURL*) url, sanitizedString );
1410 }
1411 } else {
1412 // Go component by component
1413 CFIndex currentComponent = HAS_USER;
1414 mark = 0;
1415 CFMutableStringRef sanitizedString = NULL;
1416 while (currentComponent < (HAS_FRAGMENT << 1)) {
1417 CFRange componentRange = _rangeForComponent(url->_flags, url->ranges, currentComponent);
1418 if (componentRange.location != kCFNotFound) {
1419 scanCharacters(alloc, & sanitizedString, &(((struct __CFURL *)url)->_flags), cstring, ustring, useCString, componentRange.location, componentRange.location + componentRange.length, &mark, currentComponent, url->_encoding);
1420 }
1421 currentComponent = currentComponent << 1;
1422 }
1423 if (sanitizedString) {
1424 _setSanitizedString((struct __CFURL *)url, sanitizedString);
1425 } else {
1426 ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH;
1427 }
1428 }
1429 if (_getSanitizedString(url) && mark != string_length) {
1430 if (useCString) {
1431 CFStringRef tempString = CFStringCreateWithBytes(alloc, &(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1, false);
1432 CFStringAppend(_getSanitizedString(url), tempString);
1433 CFRelease(tempString);
1434 } else {
1435 CFStringAppendCharacters(_getSanitizedString(url), &(ustring[mark]), string_length - mark);
1436 }
1437 }
1438 if (freeCharacters) {
1439 CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring);
1440 }
1441 }
1442
1443
1444 static CFStringRef correctedComponent(CFStringRef comp, UInt32 compFlag, CFStringEncoding enc) {
1445 CFAllocatorRef alloc = CFGetAllocator(comp);
1446 CFIndex string_length = CFStringGetLength(comp);
1447 Boolean useCString, freeCharacters;
1448 const unsigned char *cstring = NULL;
1449 const UniChar *ustring = NULL;
1450 CFIndex mark = 0; // first character not-yet copied to sanitized string
1451 CFMutableStringRef result = NULL;
1452
1453 constructBuffers(alloc, comp, &cstring, &ustring, &useCString, &freeCharacters);
1454 scanCharacters(alloc, &result, NULL, cstring, ustring, useCString, 0, string_length, &mark, compFlag, enc);
1455 if (result) {
1456 if (mark < string_length) {
1457 if (useCString) {
1458 CFStringRef tempString = CFStringCreateWithBytes(alloc, &(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1, false);
1459 CFStringAppend(result, tempString);
1460 CFRelease(tempString);
1461 } else {
1462 CFStringAppendCharacters(result, &(ustring[mark]), string_length - mark);
1463 }
1464 }
1465 } else {
1466 // This should nevr happen
1467 CFRetain(comp);
1468 result = (CFMutableStringRef)comp;
1469 }
1470 if (freeCharacters) {
1471 CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring);
1472 }
1473 return result;
1474 }
1475
1476 #undef STRING_CHAR
1477 CF_EXPORT CFURLRef _CFURLAlloc(CFAllocatorRef allocator) {
1478 struct __CFURL *url;
1479 #if DEBUG_URL_MEMORY_USAGE
1480 numURLs ++;
1481 // if (!URLAllocator) {
1482 // URLAllocator = CFCountingAllocatorCreate(NULL);
1483 // }
1484 allocator = URLAllocator;
1485 #endif
1486 url = (struct __CFURL *)_CFRuntimeCreateInstance(allocator, __kCFURLTypeID, sizeof(struct __CFURL) - sizeof(CFRuntimeBase), NULL);
1487 if (url) {
1488 url->_flags = 0;
1489 if (createOldUTF8StyleURLs()) {
1490 url->_flags |= IS_OLD_UTF8_STYLE;
1491 }
1492 url->_string = NULL;
1493 url->_base = NULL;
1494 url->ranges = NULL;
1495 // url->_reserved = NULL;
1496 url->_encoding = kCFStringEncodingUTF8;
1497 // url->_sanatizedString = NULL;
1498 url->extra = NULL;
1499 }
1500 return url;
1501 }
1502
1503 // It is the caller's responsibility to guarantee that if URLString is absolute, base is NULL. This is necessary to avoid duplicate processing for file system URLs, which had to decide whether to compute the cwd for the base; we don't want to duplicate that work. This ALSO means it's the caller's responsibility to set the IS_ABSOLUTE bit, since we may have a degenerate URL whose string is relative, but lacks a base.
1504 static void _CFURLInit(struct __CFURL *url, CFStringRef URLString, UInt32 fsType, CFURLRef base) {
1505 CFAssert1(URLString != NULL && CFGetTypeID(URLString) == CFStringGetTypeID() && CFStringGetLength(URLString) != 0, __kCFLogAssertion, "%s(): internal CF error; empty string encountered", __PRETTY_FUNCTION__);
1506 CFAssert2((fsType == FULL_URL_REPRESENTATION) || (fsType == kCFURLPOSIXPathStyle) || (fsType == kCFURLWindowsPathStyle) || (fsType == kCFURLHFSPathStyle), __kCFLogAssertion, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__, fsType);
1507
1508 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else.
1509 url->_string = CFStringCreateCopy(CFGetAllocator(url), URLString);
1510 url->_flags |= (fsType << 16);
1511
1512 url->_base = base ? CFURLCopyAbsoluteURL(base) : NULL;
1513
1514 #if DEBUG_URL_MEMORY_USAGE
1515 if (fsType != FULL_URL_REPRESENTATION) {
1516 numFileURLsCreated ++;
1517 }
1518 if ( url->_base )
1519 numURLsWithBaseURL ++;
1520 #endif
1521 }
1522
1523 CF_EXPORT void _CFURLInitFSPath(CFURLRef url, CFStringRef path) {
1524 CFIndex len = CFStringGetLength(path);
1525 if (len && CFStringGetCharacterAtIndex(path, 0) == '/') {
1526 _CFURLInit((struct __CFURL *)url, path, kCFURLPOSIXPathStyle, NULL);
1527 ((struct __CFURL *)url)->_flags |= IS_ABSOLUTE;
1528 } else {
1529 CFURLRef cwdURL = _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url));
1530 _CFURLInit((struct __CFURL *)url, path, kCFURLPOSIXPathStyle, cwdURL);
1531 CFRelease(cwdURL);
1532 }
1533 if (!len || '/' == CFStringGetCharacterAtIndex(path, len - 1))
1534 ((struct __CFURL *)url)->_flags |= IS_DIRECTORY;
1535 }
1536
1537 // Exported for Foundation's use
1538 CF_EXPORT Boolean _CFStringIsLegalURLString(CFStringRef string) {
1539 // Check each character to make sure it is a legal URL char. The valid characters are 'A'-'Z', 'a' - 'z', '0' - '9', plus the characters in "-_.!~*'()", and the set of reserved characters (these characters have special meanings in the URL syntax), which are ";/?:@&=+$,". In addition, percent escape sequences '%' hex-digit hex-digit are permitted.
1540 // Plus the hash character '#' which denotes the beginning of a fragment, and can appear exactly once in the entire URL string. -- REW, 12/13/2000
1541 CFStringInlineBuffer stringBuffer;
1542 CFIndex idx = 0, length;
1543 Boolean sawHash = false;
1544 if (!string) {
1545 CFAssert(false, __kCFLogAssertion, "Cannot create an CFURL from a NULL string");
1546 return false;
1547 }
1548 length = CFStringGetLength(string);
1549 CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length));
1550 while (idx < length) {
1551 UniChar ch = CFStringGetCharacterFromInlineBuffer(&stringBuffer, idx);
1552 idx ++;
1553
1554 // Make sure that two valid hex digits follow a '%' character
1555 if ( ch == '%' ) {
1556 if ( idx + 2 > length )
1557 {
1558 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1);
1559 idx = -1; // To guarantee index < length, and our failure case is triggered
1560 break;
1561 }
1562
1563 ch = CFStringGetCharacterFromInlineBuffer(&stringBuffer, idx);
1564 idx ++;
1565 if (! isHexDigit(ch) ) {
1566 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2);
1567 idx = -1;
1568 break;
1569 }
1570 ch = CFStringGetCharacterFromInlineBuffer(&stringBuffer, idx);
1571 idx ++;
1572 if (! isHexDigit(ch) ) {
1573 //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3);
1574 idx = -1;
1575 break;
1576 }
1577
1578 continue;
1579 }
1580 if (ch == '[' || ch == ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002
1581 if (ch == '#') {
1582 if (sawHash) break;
1583 sawHash = true;
1584 continue;
1585 }
1586 if ( isURLLegalCharacter( ch ) )
1587 continue;
1588 break;
1589 }
1590 if (idx < length) {
1591 return false;
1592 }
1593 return true;
1594 }
1595
1596 CF_EXPORT void _CFURLInitWithString(CFURLRef myURL, CFStringRef string, CFURLRef baseURL) {
1597 struct __CFURL *url = (struct __CFURL *)myURL; // Supress annoying compile warnings
1598 Boolean isAbsolute = false;
1599 CFRange colon = CFStringFind(string, CFSTR(":"), 0);
1600 if (colon.location != kCFNotFound) {
1601 isAbsolute = true;
1602 CFIndex i;
1603 for (i = 0; i < colon.location; i++) {
1604 char ch = CFStringGetCharacterAtIndex(string, i);
1605 if (!scheme_valid(ch)) {
1606 isAbsolute = false;
1607 break;
1608 }
1609 }
1610 }
1611 _CFURLInit(url, string, FULL_URL_REPRESENTATION, isAbsolute ? NULL : baseURL);
1612 if (isAbsolute) {
1613 url->_flags |= IS_ABSOLUTE;
1614 }
1615 }
1616
1617 struct __CFURLEncodingTranslationParameters {
1618 CFStringEncoding fromEnc;
1619 CFStringEncoding toEnc;
1620 const UniChar *addlChars;
1621 int count;
1622 Boolean escapeHighBit;
1623 Boolean escapePercents;
1624 Boolean agreesOverASCII;
1625 Boolean encodingsMatch;
1626 } ;
1627
1628 static Boolean _shouldEscapeForEncodingConversion(UniChar ch, void *context) {
1629 struct __CFURLEncodingTranslationParameters *info = (struct __CFURLEncodingTranslationParameters *)context;
1630 if (info->escapeHighBit && ch > 0x7F) {
1631 return true;
1632 } else if (ch == '%' && info->escapePercents) {
1633 return true;
1634 } else if (info->addlChars) {
1635 const UniChar *escChar = info->addlChars;
1636 int i;
1637 for (i = 0; i < info->count; escChar ++, i ++) {
1638 if (*escChar == ch) {
1639 return true;
1640 }
1641 }
1642 }
1643 return false;
1644 }
1645
1646 static CFIndex _convertEscapeSequence(CFIndex percentIndex, CFStringRef urlString, CFStringRef *newString, void *context) {
1647 struct __CFURLEncodingTranslationParameters *info = (struct __CFURLEncodingTranslationParameters *)context;
1648 CFMutableDataRef newData;
1649 Boolean sawNonASCIICharacter = false;
1650 CFIndex i = percentIndex;
1651 CFIndex length;
1652 *newString = NULL;
1653 if (info->encodingsMatch) return percentIndex + 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well
1654 newData = CFDataCreateMutable(CFGetAllocator(urlString), 0);
1655 length = CFStringGetLength(urlString);
1656
1657 while (i < length && CFStringGetCharacterAtIndex(urlString, i) == '%') {
1658 uint8_t byte;
1659 if (i+2 >= length || !_translateBytes(CFStringGetCharacterAtIndex(urlString, i+1), CFStringGetCharacterAtIndex(urlString, i+2), &byte)) {
1660 CFRelease(newData);
1661 return -1;
1662 }
1663 if (byte > 0x7f) sawNonASCIICharacter = true;
1664 CFDataAppendBytes(newData, &byte, 1);
1665 i += 3;
1666 }
1667 if (!sawNonASCIICharacter && info->agreesOverASCII) {
1668 return i;
1669 } else {
1670 CFStringRef tmp = CFStringCreateWithBytes(CFGetAllocator(urlString), CFDataGetBytePtr(newData), CFDataGetLength(newData), info->fromEnc, false);
1671 CFIndex tmpIndex, tmpLen;
1672 if (!tmp) {
1673 CFRelease(newData);
1674 return -1;
1675 }
1676 tmpLen = CFStringGetLength(tmp);
1677 *newString = CFStringCreateMutable(CFGetAllocator(urlString), 0);
1678 for (tmpIndex = 0; tmpIndex < tmpLen; tmpIndex ++) {
1679 if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp, tmpIndex), info->toEnc, (CFMutableStringRef)(*newString))) {
1680 break;
1681 }
1682 }
1683 CFRelease(tmp);
1684 CFRelease(newData);
1685 if (tmpIndex < tmpLen) {
1686 CFRelease(*newString);
1687 *newString = NULL;
1688 return -1;
1689 } else {
1690 return i;
1691 }
1692 }
1693 }
1694
1695 /* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */
1696 static CFStringRef _convertPercentEscapes(CFStringRef urlString, CFStringEncoding fromEncoding, CFStringEncoding toEncoding, Boolean escapeAllHighBitCharacters, Boolean escapePercents, const UniChar *addlCharsToEscape, int numAddlChars) {
1697 struct __CFURLEncodingTranslationParameters context;
1698 context.fromEnc = fromEncoding;
1699 context.toEnc = toEncoding;
1700 context.addlChars = addlCharsToEscape;
1701 context.count = numAddlChars;
1702 context.escapeHighBit = escapeAllHighBitCharacters;
1703 context.escapePercents = escapePercents;
1704 context.agreesOverASCII = (__CFStringEncodingIsSupersetOfASCII(toEncoding) && __CFStringEncodingIsSupersetOfASCII(fromEncoding)) ? true : false;
1705 context.encodingsMatch = (fromEncoding == toEncoding) ? true : false;
1706 return _addPercentEscapesToString(CFGetAllocator(urlString), urlString, _shouldEscapeForEncodingConversion, _convertEscapeSequence, toEncoding, &context);
1707 }
1708
1709 // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
1710 CFURLRef CFURLCreateWithBytes(CFAllocatorRef allocator, const uint8_t *URLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL) {
1711 CFStringRef urlString = CFStringCreateWithBytes(allocator, URLBytes, length, encoding, false);
1712 CFURLRef result;
1713 if (!urlString || CFStringGetLength(urlString) == 0) {
1714 if (urlString) CFRelease(urlString);
1715 return NULL;
1716 }
1717 if (createOldUTF8StyleURLs()) {
1718 if (encoding != kCFStringEncodingUTF8) {
1719 CFStringRef tmp = _convertPercentEscapes(urlString, encoding, kCFStringEncodingUTF8, false, false, NULL, 0);
1720 CFRelease(urlString);
1721 urlString = tmp;
1722 if (!urlString) return NULL;
1723 }
1724 }
1725
1726 result = _CFURLAlloc(allocator);
1727 if (result) {
1728 _CFURLInitWithString(result, urlString, baseURL);
1729 if (encoding != kCFStringEncodingUTF8 && !createOldUTF8StyleURLs()) {
1730 ((struct __CFURL *)result)->_encoding = encoding;
1731 }
1732 }
1733 CFRelease(urlString); // it's retained by result, now.
1734 return result;
1735 }
1736
1737 CFDataRef CFURLCreateData(CFAllocatorRef allocator, CFURLRef url, CFStringEncoding encoding, Boolean escapeWhitespace) {
1738 static const UniChar whitespaceChars[4] = {' ', '\n', '\r', '\t'};
1739 CFStringRef myStr = CFURLGetString(url);
1740 CFStringRef newStr;
1741 CFDataRef result;
1742 if (url->_flags & IS_OLD_UTF8_STYLE) {
1743 newStr = (encoding == kCFStringEncodingUTF8) ? CFRetain(myStr) : _convertPercentEscapes(myStr, kCFStringEncodingUTF8, encoding, true, false, escapeWhitespace ? whitespaceChars : NULL, escapeWhitespace ? 4 : 0);
1744 } else {
1745 newStr=myStr;
1746 CFRetain(newStr);
1747 }
1748 result = CFStringCreateExternalRepresentation(allocator, newStr, encoding, 0);
1749 CFRelease(newStr);
1750 return result;
1751 }
1752
1753 // Any escape sequences in URLString will be interpreted via UTF-8.
1754 CFURLRef CFURLCreateWithString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) {
1755 CFURLRef url;
1756 if (!URLString || CFStringGetLength(URLString) == 0) return NULL;
1757 if (!_CFStringIsLegalURLString(URLString)) return NULL;
1758 url = _CFURLAlloc(allocator);
1759 if (url) {
1760 _CFURLInitWithString(url, URLString, baseURL);
1761 }
1762 return url;
1763 }
1764
1765 static CFURLRef _CFURLCreateWithArbitraryString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) {
1766 CFURLRef url;
1767 if (!URLString || CFStringGetLength(URLString) == 0) return NULL;
1768 url = _CFURLAlloc(allocator);
1769 if (url) {
1770 _CFURLInitWithString(url, URLString, baseURL);
1771 }
1772 return url;
1773 }
1774
1775 CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *relativeURLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL, Boolean useCompatibilityMode) {
1776 CFStringRef relativeString = CFStringCreateWithBytes(alloc, relativeURLBytes, length, encoding, false);
1777 if (!relativeString) {
1778 return NULL;
1779 }
1780 if (!useCompatibilityMode) {
1781 CFURLRef url = _CFURLCreateWithArbitraryString(alloc, relativeString, baseURL);
1782 CFRelease(relativeString);
1783 if (url) {
1784 ((struct __CFURL *)url)->_encoding = encoding;
1785 CFURLRef absURL = CFURLCopyAbsoluteURL(url);
1786 CFRelease(url);
1787 return absURL;
1788 } else {
1789 return NULL;
1790 }
1791 } else {
1792 UInt32 absFlags = 0;
1793 CFRange *absRanges;
1794 CFStringRef absString = NULL;
1795 Boolean absStringIsMutable = false;
1796 CFURLRef absURL;
1797 if (!baseURL) {
1798 absString = relativeString;
1799 } else {
1800 UniChar ch = CFStringGetCharacterAtIndex(relativeString, 0);
1801 if (ch == '?' || ch == ';' || ch == '#') {
1802 // Nothing but parameter + query + fragment; append to the baseURL string
1803 CFStringRef baseString;
1804 if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) {
1805 baseString = CFURLGetString(baseURL);
1806 } else {
1807 baseString = baseURL->_string;
1808 }
1809 absString = CFStringCreateMutable(alloc, CFStringGetLength(baseString) + CFStringGetLength(relativeString));
1810 CFStringAppend((CFMutableStringRef)absString, baseString);
1811 CFStringAppend((CFMutableStringRef)absString, relativeString);
1812 absStringIsMutable = true;
1813 } else {
1814 UInt32 relFlags = 0;
1815 CFRange *relRanges;
1816 CFStringRef relString = NULL;
1817 _parseComponents(alloc, relativeString, baseURL, &relFlags, &relRanges);
1818 if (relFlags & HAS_SCHEME) {
1819 CFStringRef baseScheme = CFURLCopyScheme(baseURL);
1820 CFRange relSchemeRange = _rangeForComponent(relFlags, relRanges, HAS_SCHEME);
1821 if (baseScheme && CFStringGetLength(baseScheme) == relSchemeRange.length && CFStringHasPrefix(relativeString, baseScheme)) {
1822 relString = CFStringCreateWithSubstring(alloc, relativeString, CFRangeMake(relSchemeRange.length+1, CFStringGetLength(relativeString) - relSchemeRange.length - 1));
1823 CFAllocatorDeallocate(alloc, relRanges);
1824 relFlags = 0;
1825 _parseComponents(alloc, relString, baseURL, &relFlags, &relRanges);
1826 } else {
1827 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
1828 CFRetain(relativeString);
1829 absString = relativeString;
1830 }
1831 if (baseScheme) CFRelease(baseScheme);
1832 } else {
1833 CFRetain(relativeString);
1834 relString = relativeString;
1835 }
1836 if (!absString) {
1837 if (!CF_IS_OBJC(__kCFURLTypeID, baseURL)) {
1838 if (!(baseURL->_flags & IS_PARSED)) {
1839 _parseComponentsOfURL(baseURL);
1840 }
1841 absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseURL->_string, baseURL->_flags, baseURL->ranges);
1842 } else {
1843 CFStringRef baseString;
1844 UInt32 baseFlags = 0;
1845 CFRange *baseRanges;
1846 if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) {
1847 baseString = CFURLGetString(baseURL);
1848 } else {
1849 baseString = baseURL->_string;
1850 }
1851 _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges);
1852 absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges);
1853 CFAllocatorDeallocate(alloc, baseRanges);
1854 }
1855 absStringIsMutable = true;
1856 }
1857 if (relString) CFRelease(relString);
1858 CFAllocatorDeallocate(alloc, relRanges);
1859 }
1860 CFRelease(relativeString);
1861 }
1862 _parseComponents(alloc, absString, NULL, &absFlags, &absRanges);
1863 if (absFlags & HAS_PATH) {
1864 CFRange pathRg = _rangeForComponent(absFlags, absRanges, HAS_PATH);
1865 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW
1866 UniChar *buf = CFAllocatorAllocate(alloc, sizeof(UniChar) * (pathRg.length + 1), 0);
1867 CFStringRef newPath;
1868 CFStringGetCharacters(absString, pathRg, buf);
1869 buf[pathRg.length] = '\0';
1870 newPath = _resolvedPath(buf, buf + pathRg.length, '/', true, false, alloc);
1871 if (CFStringGetLength(newPath) != pathRg.length) {
1872 if (!absStringIsMutable) {
1873 CFStringRef tmp = CFStringCreateMutableCopy(alloc, CFStringGetLength(absString), absString);
1874 CFRelease(absString);
1875 absString = tmp;
1876 }
1877 CFStringReplace((CFMutableStringRef)absString, pathRg, newPath);
1878 }
1879 CFRelease(newPath);
1880 // Do not deallocate buf; newPath took ownership of it.
1881 }
1882 CFAllocatorDeallocate(alloc, absRanges);
1883 absURL = _CFURLCreateWithArbitraryString(alloc, absString, NULL);
1884 CFRelease(absString);
1885 if (absURL) {
1886 ((struct __CFURL *)absURL)->_encoding = encoding;
1887 }
1888 return absURL;
1889 }
1890 }
1891
1892 /* This function is this way because I pulled it out of _resolvedURLPath (so that _resolvedFileSystemPath could use it), and I didn't want to spend a bunch of energy reworking the code. So instead of being a bit more intelligent about inputs, it just demands a slightly perverse set of parameters, to match the old _resolvedURLPath code. -- REW, 6/14/99 */
1893 static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc) {
1894 UniChar *idx = pathStr;
1895 while (idx < end) {
1896 if (*idx == '.') {
1897 if (idx+1 == end) {
1898 if (idx != pathStr) {
1899 *idx = '\0';
1900 end = idx;
1901 }
1902 break;
1903 } else if (*(idx+1) == pathDelimiter) {
1904 if (idx + 2 != end || idx != pathStr) {
1905 memmove(idx, idx+2, (end-(idx+2)+1) * sizeof(UniChar));
1906 end -= 2;
1907 continue;
1908 } else {
1909 // Do not delete the sole path component
1910 break;
1911 }
1912 } else if (( end-idx >= 2 ) && *(idx+1) == '.' && (idx+2 == end || (( end-idx > 2 ) && *(idx+2) == pathDelimiter))) {
1913 if (idx - pathStr >= 2) {
1914 // Need at least 2 characters between index and pathStr, because we know if index != newPath, then *(index-1) == pathDelimiter, and we need something before that to compact out.
1915 UniChar *lastDelim = idx-2;
1916 while (lastDelim >= pathStr && *lastDelim != pathDelimiter) lastDelim --;
1917 lastDelim ++;
1918 if (lastDelim != idx && (idx-lastDelim != 3 || *lastDelim != '.' || *(lastDelim +1) != '.')) {
1919 // We have a genuine component to compact out
1920 if (idx+2 != end) {
1921 unsigned numCharsToMove = end - (idx+3) + 1; // +1 to move the '\0' as well
1922 memmove(lastDelim, idx+3, numCharsToMove * sizeof(UniChar));
1923 end -= (idx + 3 - lastDelim);
1924 idx = lastDelim;
1925 continue;
1926 } else if (lastDelim != pathStr) {
1927 *lastDelim = '\0';
1928 end = lastDelim;
1929 break;
1930 } else {
1931 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW
1932 pathStr[0] = '.';
1933 pathStr[1] = '/';
1934 pathStr[2] = '\0';
1935 end = & pathStr[3];
1936 break;
1937 }
1938 }
1939 } else if (stripLeadingDotDots) {
1940 if (idx + 3 != end) {
1941 unsigned numCharsToMove = end - (idx + 3) + 1;
1942 memmove(idx, idx+3, numCharsToMove * sizeof(UniChar));
1943 end -= 3;
1944 continue;
1945 } else {
1946 // Do not devolve the last path component
1947 break;
1948 }
1949 }
1950 }
1951 }
1952 while (idx < end && *idx != pathDelimiter) idx ++;
1953 idx ++;
1954 }
1955 if (stripTrailingDelimiter && end > pathStr && end-1 != pathStr && *(end-1) == pathDelimiter) {
1956 end --;
1957 }
1958 return CFStringCreateWithCharactersNoCopy(alloc, pathStr, end - pathStr, alloc);
1959 }
1960
1961 static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges) {
1962 CFMutableStringRef newString = CFStringCreateMutable(alloc, 0);
1963 CFIndex bufLen = CFStringGetLength(baseString) + CFStringGetLength(relString); // Overkill, but guarantees we never allocate again
1964 UniChar *buf = CFAllocatorAllocate(alloc, bufLen * sizeof(UniChar), 0);
1965 CFRange rg;
1966
1967 rg = _rangeForComponent(baseFlags, baseRanges, HAS_SCHEME);
1968 if (rg.location != kCFNotFound) {
1969 CFStringGetCharacters(baseString, rg, buf);
1970 CFStringAppendCharacters(newString, buf, rg.length);
1971 CFStringAppendCString(newString, ":", kCFStringEncodingASCII);
1972 }
1973
1974 if (relFlags & NET_LOCATION_MASK) {
1975 CFStringAppend(newString, relString);
1976 } else {
1977 CFStringAppendCString(newString, "//", kCFStringEncodingASCII);
1978 rg = _netLocationRange(baseFlags, baseRanges);
1979 if (rg.location != kCFNotFound) {
1980 CFStringGetCharacters(baseString, rg, buf);
1981 CFStringAppendCharacters(newString, buf, rg.length);
1982 }
1983
1984 if (relFlags & HAS_PATH) {
1985 CFRange relPathRg = _rangeForComponent(relFlags, relRanges, HAS_PATH);
1986 CFRange basePathRg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH);
1987 CFStringRef newPath;
1988 Boolean useRelPath = false;
1989 Boolean useBasePath = false;
1990 if (basePathRg.location == kCFNotFound) {
1991 useRelPath = true;
1992 } else if (relPathRg.length == 0) {
1993 useBasePath = true;
1994 } else if (CFStringGetCharacterAtIndex(relString, relPathRg.location) == '/') {
1995 useRelPath = true;
1996 } else if (basePathRg.location == kCFNotFound || basePathRg.length == 0) {
1997 useRelPath = true;
1998 }
1999 if (useRelPath) {
2000 newPath = CFStringCreateWithSubstring(alloc, relString, relPathRg);
2001 } else if (useBasePath) {
2002 newPath = CFStringCreateWithSubstring(alloc, baseString, basePathRg);
2003 } else {
2004 // #warning FIXME - Get rid of this allocation
2005 UniChar *newPathBuf = CFAllocatorAllocate(alloc, sizeof(UniChar) * (relPathRg.length + basePathRg.length + 1), 0);
2006 UniChar *idx, *end;
2007 CFStringGetCharacters(baseString, basePathRg, newPathBuf);
2008 idx = newPathBuf + basePathRg.length - 1;
2009 while (idx != newPathBuf && *idx != '/') idx --;
2010 if (*idx == '/') idx ++;
2011 CFStringGetCharacters(relString, relPathRg, idx);
2012 end = idx + relPathRg.length;
2013 *end = 0;
2014 newPath = _resolvedPath(newPathBuf, end, '/', false, false, alloc);
2015 }
2016 /* Under Win32 absolute path can begin with letter
2017 * so we have to add one '/' to the newString
2018 * (Sergey Zubarev)
2019 */
2020 // No - the input strings here are URL path strings, not Win32 paths.
2021 // Absolute paths should have had a '/' prepended before this point.
2022 // I have removed Sergey Zubarev's change and left his comment (and
2023 // this one) as a record. - REW, 1/5/2004
2024
2025 // if the relative URL does not begin with a slash and
2026 // the base does not end with a slash, add a slash
2027 if ((basePathRg.location == kCFNotFound || basePathRg.length == 0) && CFStringGetCharacterAtIndex(newPath, 0) != '/') {
2028 CFStringAppendCString(newString, "/", kCFStringEncodingASCII);
2029 }
2030
2031 CFStringAppend(newString, newPath);
2032 CFRelease(newPath);
2033 rg.location = relPathRg.location + relPathRg.length;
2034 rg.length = CFStringGetLength(relString);
2035 if (rg.length > rg.location) {
2036 rg.length -= rg.location;
2037 CFStringGetCharacters(relString, rg, buf);
2038 CFStringAppendCharacters(newString, buf, rg.length);
2039 }
2040 } else {
2041 rg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH);
2042 if (rg.location != kCFNotFound) {
2043 CFStringGetCharacters(baseString, rg, buf);
2044 CFStringAppendCharacters(newString, buf, rg.length);
2045 }
2046
2047 if (!(relFlags & RESOURCE_SPECIFIER_MASK)) {
2048 // ??? Can this ever happen?
2049 UInt32 rsrcFlag = _firstResourceSpecifierFlag(baseFlags);
2050 if (rsrcFlag) {
2051 rg.location = _rangeForComponent(baseFlags, baseRanges, rsrcFlag).location;
2052 rg.length = CFStringGetLength(baseString) - rg.location;
2053 rg.location --; // To pick up the separator
2054 rg.length ++;
2055 CFStringGetCharacters(baseString, rg, buf);
2056 CFStringAppendCharacters(newString, buf, rg.length);
2057 }
2058 } else if (relFlags & HAS_PARAMETERS) {
2059 rg = _rangeForComponent(relFlags, relRanges, HAS_PARAMETERS);
2060 rg.location --; // To get the semicolon that starts the parameters
2061 rg.length = CFStringGetLength(relString) - rg.location;
2062 CFStringGetCharacters(relString, rg, buf);
2063 CFStringAppendCharacters(newString, buf, rg.length);
2064 } else {
2065 // Sigh; we have to resolve these against one another
2066 rg = _rangeForComponent(baseFlags, baseRanges, HAS_PARAMETERS);
2067 if (rg.location != kCFNotFound) {
2068 CFStringAppendCString(newString, ";", kCFStringEncodingASCII);
2069 CFStringGetCharacters(baseString, rg, buf);
2070 CFStringAppendCharacters(newString, buf, rg.length);
2071 }
2072 rg = _rangeForComponent(relFlags, relRanges, HAS_QUERY);
2073 if (rg.location != kCFNotFound) {
2074 CFStringAppendCString(newString, "?", kCFStringEncodingASCII);
2075 CFStringGetCharacters(relString, rg, buf);
2076 CFStringAppendCharacters(newString, buf, rg.length);
2077 } else {
2078 rg = _rangeForComponent(baseFlags, baseRanges, HAS_QUERY);
2079 if (rg.location != kCFNotFound) {
2080 CFStringAppendCString(newString, "?", kCFStringEncodingASCII);
2081 CFStringGetCharacters(baseString, rg, buf);
2082 CFStringAppendCharacters(newString, buf, rg.length);
2083 }
2084 }
2085 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2086 rg = _rangeForComponent(relFlags, relRanges, HAS_FRAGMENT);
2087 if (rg.location != kCFNotFound) {
2088 CFStringAppendCString(newString, "#", kCFStringEncodingASCII);
2089 CFStringGetCharacters(relString, rg, buf);
2090 CFStringAppendCharacters(newString, buf, rg.length);
2091 }
2092 }
2093 }
2094 }
2095 CFAllocatorDeallocate(alloc, buf);
2096 return newString;
2097 }
2098
2099 CFURLRef CFURLCopyAbsoluteURL(CFURLRef relativeURL) {
2100 CFURLRef anURL, base;
2101 CFURLPathStyle fsType;
2102 CFAllocatorRef alloc = CFGetAllocator(relativeURL);
2103 CFStringRef baseString, newString;
2104 UInt32 baseFlags;
2105 CFRange *baseRanges;
2106 Boolean baseIsObjC;
2107
2108 CFAssert1(relativeURL != NULL, __kCFLogAssertion, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__);
2109 if (CF_IS_OBJC(__kCFURLTypeID, relativeURL)) {
2110 CF_OBJC_CALL0(CFURLRef, anURL, relativeURL, "absoluteURL");
2111 if (anURL) CFRetain(anURL);
2112 return anURL;
2113 }
2114
2115 __CFGenericValidateType(relativeURL, __kCFURLTypeID);
2116
2117 base = relativeURL->_base;
2118 if (!base) {
2119 return CFRetain(relativeURL);
2120 }
2121 baseIsObjC = CF_IS_OBJC(__kCFURLTypeID, base);
2122 fsType = URL_PATH_TYPE(relativeURL);
2123
2124 if (!baseIsObjC && fsType != FULL_URL_REPRESENTATION && fsType == URL_PATH_TYPE(base)) {
2125 return _CFURLCopyAbsoluteFileURL(relativeURL);
2126 }
2127 if (fsType != FULL_URL_REPRESENTATION) {
2128 _convertToURLRepresentation((struct __CFURL *)relativeURL);
2129 fsType = FULL_URL_REPRESENTATION;
2130 }
2131 if (!(relativeURL->_flags & IS_PARSED)) {
2132 _parseComponentsOfURL(relativeURL);
2133 }
2134 if ((relativeURL->_flags & POSIX_AND_URL_PATHS_MATCH) && !(relativeURL->_flags & (RESOURCE_SPECIFIER_MASK | NET_LOCATION_MASK)) && !baseIsObjC && (URL_PATH_TYPE(base) == kCFURLPOSIXPathStyle)) {
2135 // There's nothing to relativeURL's string except the path
2136 CFStringRef newPath = _resolveFileSystemPaths(relativeURL->_string, base->_string, CFURLHasDirectoryPath(base), kCFURLPOSIXPathStyle, alloc);
2137 CFURLRef result = CFURLCreateWithFileSystemPath(alloc, newPath, kCFURLPOSIXPathStyle, CFURLHasDirectoryPath(relativeURL));
2138 CFRelease(newPath);
2139 return result;
2140 }
2141
2142 if (!baseIsObjC) {
2143 CFURLPathStyle baseType = URL_PATH_TYPE(base);
2144 if (baseType != FULL_URL_REPRESENTATION) {
2145 _convertToURLRepresentation((struct __CFURL *)base);
2146 } else if (!(base->_flags & IS_PARSED)) {
2147 _parseComponentsOfURL(base);
2148 }
2149 baseString = base->_string;
2150 baseFlags = base->_flags;
2151 baseRanges = base->ranges;
2152 } else {
2153 baseString = CFURLGetString(base);
2154 baseFlags = 0;
2155 baseRanges = NULL;
2156 _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges);
2157 }
2158
2159 newString = resolveAbsoluteURLString(alloc, relativeURL->_string, relativeURL->_flags, relativeURL->ranges, baseString, baseFlags, baseRanges);
2160 if (baseIsObjC) {
2161 CFAllocatorDeallocate(alloc, baseRanges);
2162 }
2163 anURL = _CFURLCreateWithArbitraryString(alloc, newString, NULL);
2164 CFRelease(newString);
2165 ((struct __CFURL *)anURL)->_encoding = relativeURL->_encoding;
2166 return anURL;
2167 }
2168
2169
2170 /*******************/
2171 /* Basic accessors */
2172 /*******************/
2173 CFStringEncoding _CFURLGetEncoding(CFURLRef url) {
2174 return url->_encoding;
2175 }
2176
2177 Boolean CFURLCanBeDecomposed(CFURLRef anURL) {
2178 anURL = _CFURLFromNSURL(anURL);
2179 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) return true;
2180 if (!(anURL->_flags & IS_PARSED)) {
2181 _parseComponentsOfURL(anURL);
2182 }
2183 return ((anURL->_flags & IS_DECOMPOSABLE) != 0);
2184 }
2185
2186 CFStringRef CFURLGetString(CFURLRef url) {
2187 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID, CFStringRef , url, "relativeString");
2188 if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) {
2189 if (url->_base && (url->_flags & POSIX_AND_URL_PATHS_MATCH)) {
2190 return url->_string;
2191 }
2192 _convertToURLRepresentation((struct __CFURL *)url);
2193 }
2194 if (!_haveTestedOriginalString(url)) {
2195 computeSanitizedString(url);
2196 }
2197 if (url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) {
2198 return url->_string;
2199 } else {
2200 return _getSanitizedString( url );
2201 }
2202 }
2203
2204 CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) {
2205 CFIndex length, charsConverted, usedLength;
2206 CFStringRef string;
2207 CFStringEncoding enc;
2208 if (CF_IS_OBJC(__kCFURLTypeID, url)) {
2209 string = CFURLGetString(url);
2210 enc = kCFStringEncodingUTF8;
2211 } else {
2212 if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) {
2213 _convertToURLRepresentation((struct __CFURL *)url);
2214 }
2215 string = url->_string;
2216 enc = url->_encoding;
2217 }
2218 length = CFStringGetLength(string);
2219 charsConverted = CFStringGetBytes(string, CFRangeMake(0, length), enc, 0, false, buffer, bufferLength, &usedLength);
2220 if (charsConverted != length) {
2221 return -1;
2222 } else {
2223 return usedLength;
2224 }
2225 }
2226
2227 CFURLRef CFURLGetBaseURL(CFURLRef anURL) {
2228 CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID, CFURLRef, anURL, "baseURL");
2229 return anURL->_base;
2230 }
2231
2232 // Assumes the URL is already parsed
2233 static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag) {
2234 UInt32 idx = 0;
2235 if (!(flags & compFlag)) return CFRangeMake(kCFNotFound, 0);
2236 while (!(compFlag & 1)) {
2237 compFlag = compFlag >> 1;
2238 if (flags & 1) {
2239 idx ++;
2240 }
2241 flags = flags >> 1;
2242 }
2243 return ranges[idx];
2244 }
2245
2246 static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boolean fromOriginalString, Boolean removePercentEscapes) {
2247 CFRange rg;
2248 CFStringRef comp;
2249 CFAllocatorRef alloc = CFGetAllocator(url);
2250 CFAssert1(URL_PATH_TYPE(url) == FULL_URL_REPRESENTATION, __kCFLogAssertion, "%s(): passed a file system URL", __PRETTY_FUNCTION__);
2251 if (removePercentEscapes) fromOriginalString = true;
2252 if (!(url->_flags & IS_PARSED)) {
2253 _parseComponentsOfURL(url);
2254 }
2255 rg = _rangeForComponent(url->_flags, url->ranges, compFlag);
2256 if (rg.location == kCFNotFound) return NULL;
2257 if (compFlag & HAS_SCHEME && url->_flags & HAS_HTTP_SCHEME) {
2258 comp = CFSTR("http");
2259 } else {
2260 comp = CFStringCreateWithSubstring(alloc, url->_string, rg);
2261 }
2262 if (!fromOriginalString) {
2263 if (!_haveTestedOriginalString(url)) {
2264 computeSanitizedString(url);
2265 }
2266 if (!(url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (url->_flags & (compFlag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG))) {
2267 CFStringRef newComp = correctedComponent(comp, compFlag, url->_encoding);
2268 CFRelease(comp);
2269 comp = newComp;
2270 }
2271 }
2272 if (removePercentEscapes) {
2273 CFStringRef tmp;
2274 if (url->_flags & IS_OLD_UTF8_STYLE || url->_encoding == kCFStringEncodingUTF8) {
2275 tmp = CFURLCreateStringByReplacingPercentEscapes(alloc, comp, CFSTR(""));
2276 } else {
2277 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc, comp, CFSTR(""), url->_encoding);
2278 }
2279 CFRelease(comp);
2280 comp = tmp;
2281 }
2282 return comp;
2283 }
2284
2285 CFStringRef CFURLCopyScheme(CFURLRef anURL) {
2286 CFStringRef scheme;
2287 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2288 CF_OBJC_CALL0(CFStringRef, scheme, anURL, "scheme");
2289 if (scheme) CFRetain(scheme);
2290 return scheme;
2291 }
2292 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2293 if (anURL->_base) {
2294 return CFURLCopyScheme(anURL->_base);
2295 } else {
2296 CFRetain(kCFURLFileScheme); // because caller will release it
2297 return kCFURLFileScheme;
2298 }
2299 }
2300 if (anURL->_flags & IS_PARSED && anURL->_flags & HAS_HTTP_SCHEME) {
2301 return(CFSTR("http"));
2302 }
2303 scheme = _retainedComponentString(anURL, HAS_SCHEME, true, false);
2304 if (scheme) {
2305 return scheme;
2306 } else if (anURL->_base) {
2307 return CFURLCopyScheme(anURL->_base);
2308 } else {
2309 return NULL;
2310 }
2311 }
2312
2313 static CFRange _netLocationRange(UInt32 flags, CFRange *ranges) {
2314 CFRange netRgs[4];
2315 CFRange netRg = {kCFNotFound, 0};
2316 CFIndex i, c = 4;
2317
2318 if ((flags & NET_LOCATION_MASK) == 0) return CFRangeMake(kCFNotFound, 0);
2319
2320 netRgs[0] = _rangeForComponent(flags, ranges, HAS_USER);
2321 netRgs[1] = _rangeForComponent(flags, ranges, HAS_PASSWORD);
2322 netRgs[2] = _rangeForComponent(flags, ranges, HAS_HOST);
2323 netRgs[3] = _rangeForComponent(flags, ranges, HAS_PORT);
2324 for (i = 0; i < c; i ++) {
2325 if (netRgs[i].location == kCFNotFound) continue;
2326 if (netRg.location == kCFNotFound) {
2327 netRg = netRgs[i];
2328 } else {
2329 netRg.length = netRgs[i].location + netRgs[i].length - netRg.location;
2330 }
2331 }
2332 return netRg;
2333 }
2334
2335 CFStringRef CFURLCopyNetLocation(CFURLRef anURL) {
2336 anURL = _CFURLFromNSURL(anURL);
2337 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2338 // !!! This won't work if we go to putting the vol ref num in the net location for HFS
2339 if (anURL->_base) {
2340 return CFURLCopyNetLocation(anURL->_base);
2341 } else {
2342 CFRetain(kCFURLLocalhost);
2343 return kCFURLLocalhost;
2344 }
2345 }
2346 if (!(anURL->_flags & IS_PARSED)) {
2347 _parseComponentsOfURL(anURL);
2348 }
2349 if (anURL->_flags & NET_LOCATION_MASK) {
2350 // We provide the net location
2351 CFRange netRg = _netLocationRange(anURL->_flags, anURL->ranges);
2352 CFStringRef netLoc;
2353 if (!_haveTestedOriginalString(anURL)) {
2354 computeSanitizedString(anURL);
2355 }
2356 if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (anURL->_flags & (USER_DIFFERS | PASSWORD_DIFFERS | HOST_DIFFERS | PORT_DIFFERS))) {
2357 // Only thing that can come before the net location is the scheme. It's impossible for the scheme to contain percent escapes. Therefore, we can use the location of netRg in _sanatizedString, just not the length.
2358 CFRange netLocEnd;
2359 netRg.length = CFStringGetLength( _getSanitizedString(anURL)) - netRg.location;
2360 if (CFStringFindWithOptions(_getSanitizedString(anURL), CFSTR("/"), netRg, 0, &netLocEnd)) {
2361 netRg.length = netLocEnd.location - netRg.location;
2362 }
2363 netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), _getSanitizedString(anURL), netRg);
2364 } else {
2365 netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, netRg);
2366 }
2367 return netLoc;
2368 } else if (anURL->_base) {
2369 return CFURLCopyNetLocation(anURL->_base);
2370 } else {
2371 return NULL;
2372 }
2373 }
2374
2375 // NOTE - if you want an absolute path, you must first get the absolute URL. If you want a file system path, use the file system methods above.
2376 CFStringRef CFURLCopyPath(CFURLRef anURL) {
2377 anURL = _CFURLFromNSURL(anURL);
2378 if (URL_PATH_TYPE(anURL) == kCFURLPOSIXPathStyle && (anURL->_flags & POSIX_AND_URL_PATHS_MATCH)) {
2379 CFRetain(anURL->_string);
2380 return anURL->_string;
2381 }
2382 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2383 _convertToURLRepresentation((struct __CFURL *)anURL);
2384 }
2385 return _retainedComponentString(anURL, HAS_PATH, false, false);
2386 }
2387
2388 /* NULL if CFURLCanBeDecomposed(anURL) is false; also does not resolve the URL against its base. See also CFCreateAbsoluteURL(). Note that, strictly speaking, any leading '/' is not considered part of the URL's path, although its presence or absence determines whether the path is absolute. CFURLCopyPath()'s return value includes any leading slash (giving the path the normal POSIX appearance); CFURLCopyStrictPath()'s return value omits any leading slash, and uses isAbsolute to report whether the URL's path is absolute.
2389
2390 CFURLCopyFileSystemPath() returns the URL's path as a file system path for the given path style. All percent escape sequences are replaced. The URL is not resolved against its base before computing the path.
2391 */
2392 CFStringRef CFURLCopyStrictPath(CFURLRef anURL, Boolean *isAbsolute) {
2393 CFStringRef path = CFURLCopyPath(anURL);
2394 if (!path || CFStringGetLength(path) == 0) {
2395 if (path) CFRelease(path);
2396 if (isAbsolute) *isAbsolute = false;
2397 return NULL;
2398 }
2399 if (CFStringGetCharacterAtIndex(path, 0) == '/') {
2400 CFStringRef tmp;
2401 if (isAbsolute) *isAbsolute = true;
2402 tmp = CFStringCreateWithSubstring(CFGetAllocator(path), path, CFRangeMake(1, CFStringGetLength(path)-1));
2403 CFRelease(path);
2404 path = tmp;
2405 } else {
2406 if (isAbsolute) *isAbsolute = false;
2407 }
2408 return path;
2409 }
2410
2411 Boolean CFURLHasDirectoryPath(CFURLRef anURL) {
2412 __CFGenericValidateType(anURL, __kCFURLTypeID);
2413 if (URL_PATH_TYPE(anURL) == FULL_URL_REPRESENTATION) {
2414 if (!(anURL->_flags & IS_PARSED)) {
2415 _parseComponentsOfURL(anURL);
2416 }
2417 if (!anURL->_base || (anURL->_flags & (HAS_PATH | NET_LOCATION_MASK))) {
2418 return ((anURL->_flags & IS_DIRECTORY) != 0);
2419 }
2420 return CFURLHasDirectoryPath(anURL->_base);
2421 }
2422 return ((anURL->_flags & IS_DIRECTORY) != 0);
2423 }
2424
2425 static UInt32 _firstResourceSpecifierFlag(UInt32 flags) {
2426 UInt32 firstRsrcSpecFlag = 0;
2427 UInt32 flag = HAS_FRAGMENT;
2428 while (flag != HAS_PATH) {
2429 if (flags & flag) {
2430 firstRsrcSpecFlag = flag;
2431 }
2432 flag = flag >> 1;
2433 }
2434 return firstRsrcSpecFlag;
2435 }
2436
2437 CFStringRef CFURLCopyResourceSpecifier(CFURLRef anURL) {
2438 anURL = _CFURLFromNSURL(anURL);
2439 __CFGenericValidateType(anURL, __kCFURLTypeID);
2440 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2441 return NULL;
2442 }
2443 if (!(anURL->_flags & IS_PARSED)) {
2444 _parseComponentsOfURL(anURL);
2445 }
2446 if (!(anURL->_flags & IS_DECOMPOSABLE)) {
2447 CFRange schemeRg = _rangeForComponent(anURL->_flags, anURL->ranges, HAS_SCHEME);
2448 CFIndex base = schemeRg.location + schemeRg.length + 1;
2449 if (!_haveTestedOriginalString(anURL)) {
2450 computeSanitizedString(anURL);
2451 }
2452 if (_getSanitizedString(anURL)) {
2453 // It is impossible to have a percent escape in the scheme (if there were one, we would have considered the URL a relativeURL with a colon in the path instead), so this range computation is always safe.
2454 return CFStringCreateWithSubstring(CFGetAllocator(anURL), _getSanitizedString(anURL), CFRangeMake(base, CFStringGetLength(_getSanitizedString(anURL))-base));
2455 } else {
2456 return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, CFRangeMake(base, CFStringGetLength(anURL->_string)-base));
2457 }
2458 } else {
2459 UInt32 firstRsrcSpecFlag = _firstResourceSpecifierFlag(anURL->_flags);
2460 UInt32 flag;
2461 if (firstRsrcSpecFlag) {
2462 Boolean canUseOriginalString = true;
2463 Boolean canUseSanitizedString = true;
2464 CFAllocatorRef alloc = CFGetAllocator(anURL);
2465 if (!_haveTestedOriginalString(anURL)) {
2466 computeSanitizedString(anURL);
2467 }
2468 if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH)) {
2469 // See if any pieces in the resource specifier differ between sanitized string and original string
2470 for (flag = firstRsrcSpecFlag; flag != (HAS_FRAGMENT << 1); flag = flag << 1) {
2471 if (anURL->_flags & (flag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG)) {
2472 canUseOriginalString = false;
2473 break;
2474 }
2475 }
2476 }
2477 if (!canUseOriginalString) {
2478 // If none of the pieces prior to the first resource specifier flag differ, then we can use the offset from the original string as the offset in the sanitized string.
2479 for (flag = firstRsrcSpecFlag >> 1; flag != 0; flag = flag >> 1) {
2480 if (anURL->_flags & (flag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG)) {
2481 canUseSanitizedString = false;
2482 break;
2483 }
2484 }
2485 }
2486 if (canUseOriginalString) {
2487 CFRange rg = _rangeForComponent(anURL->_flags, anURL->ranges, firstRsrcSpecFlag);
2488 rg.location --; // Include the character that demarcates the component
2489 rg.length = CFStringGetLength(anURL->_string) - rg.location;
2490 return CFStringCreateWithSubstring(alloc, anURL->_string, rg);
2491 } else if (canUseSanitizedString) {
2492 CFRange rg = _rangeForComponent(anURL->_flags, anURL->ranges, firstRsrcSpecFlag);
2493 rg.location --; // Include the character that demarcates the component
2494 rg.length = CFStringGetLength(_getSanitizedString(anURL)) - rg.location;
2495 return CFStringCreateWithSubstring(alloc, _getSanitizedString(anURL), rg);
2496 } else {
2497 // Must compute the correct string to return; just reparse....
2498 UInt32 sanFlags = 0;
2499 CFRange *sanRanges = NULL;
2500 CFRange rg;
2501 _parseComponents(alloc, _getSanitizedString(anURL), anURL->_base, &sanFlags, &sanRanges);
2502 rg = _rangeForComponent(sanFlags, sanRanges, firstRsrcSpecFlag);
2503 CFAllocatorDeallocate(alloc, sanRanges);
2504 rg.location --; // Include the character that demarcates the component
2505 rg.length = CFStringGetLength(_getSanitizedString(anURL)) - rg.location;
2506 return CFStringCreateWithSubstring(CFGetAllocator(anURL), _getSanitizedString(anURL), rg);
2507 }
2508 } else {
2509 // The resource specifier cannot possibly come from the base.
2510 return NULL;
2511 }
2512 }
2513 }
2514
2515 /*************************************/
2516 /* Accessors that create new objects */
2517 /*************************************/
2518
2519 // For the next four methods, it is important to realize that, if a URL supplies any part of the net location (host, user, port, or password), it must supply all of the net location (i.e. none of it comes from its base URL). Also, it is impossible for a URL to be relative, supply none of the net location, and still have its (empty) net location take precedence over its base URL (because there's nothing that precedes the net location except the scheme, and if the URL supplied the scheme, it would be absolute, and there would be no base).
2520 CFStringRef CFURLCopyHostName(CFURLRef anURL) {
2521 CFStringRef tmp;
2522 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2523 CF_OBJC_CALL0(CFStringRef, tmp, anURL, "host");
2524 if (tmp) CFRetain(tmp);
2525 return tmp;
2526 }
2527 __CFGenericValidateType(anURL, __kCFURLTypeID);
2528 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2529 if (anURL->_base) {
2530 return CFURLCopyHostName(anURL->_base);
2531 } else {
2532 CFRetain(kCFURLLocalhost);
2533 return kCFURLLocalhost;
2534 }
2535 }
2536 tmp = _retainedComponentString(anURL, HAS_HOST, true, true);
2537 if (tmp) {
2538 if (anURL->_flags & IS_IPV6_ENCODED) {
2539 // Have to strip off the brackets to get the true hostname.
2540 // Assume that to be legal the first and last characters are brackets!
2541 CFStringRef strippedHost = CFStringCreateWithSubstring(CFGetAllocator(anURL), tmp, CFRangeMake(1, CFStringGetLength(tmp) - 2));
2542 CFRelease(tmp);
2543 tmp = strippedHost;
2544 }
2545 return tmp;
2546 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
2547 return CFURLCopyHostName(anURL->_base);
2548 } else {
2549 return NULL;
2550 }
2551 }
2552
2553 // Return -1 to indicate no port is specified
2554 SInt32 CFURLGetPortNumber(CFURLRef anURL) {
2555 CFStringRef port;
2556 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2557 CFNumberRef cfPort;
2558 CF_OBJC_CALL0(CFNumberRef, cfPort, anURL, "port");
2559 SInt32 num;
2560 if (cfPort && CFNumberGetValue(cfPort, kCFNumberSInt32Type, &num)) return num;
2561 return -1;
2562 }
2563 __CFGenericValidateType(anURL, __kCFURLTypeID);
2564 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2565 if (anURL->_base) {
2566 return CFURLGetPortNumber(anURL->_base);
2567 }
2568 return -1;
2569 }
2570 port = _retainedComponentString(anURL, HAS_PORT, true, false);
2571 if (port) {
2572 SInt32 portNum, idx, length = CFStringGetLength(port);
2573 CFStringInlineBuffer buf;
2574 CFStringInitInlineBuffer(port, &buf, CFRangeMake(0, length));
2575 idx = 0;
2576 if (!__CFStringScanInteger(&buf, NULL, &idx, false, &portNum) || (idx != length)) {
2577 portNum = -1;
2578 }
2579 CFRelease(port);
2580 return portNum;
2581 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
2582 return CFURLGetPortNumber(anURL->_base);
2583 } else {
2584 return -1;
2585 }
2586 }
2587
2588 CFStringRef CFURLCopyUserName(CFURLRef anURL) {
2589 CFStringRef user;
2590 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2591 CF_OBJC_CALL0(CFStringRef, user, anURL, "user");
2592 if (user) CFRetain(user);
2593 return user;
2594 }
2595 __CFGenericValidateType(anURL, __kCFURLTypeID);
2596 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2597 if (anURL->_base) {
2598 return CFURLCopyUserName(anURL->_base);
2599 }
2600 return NULL;
2601 }
2602 user = _retainedComponentString(anURL, HAS_USER, true, true);
2603 if (user) {
2604 return user;
2605 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
2606 return CFURLCopyUserName(anURL->_base);
2607 } else {
2608 return NULL;
2609 }
2610 }
2611
2612 CFStringRef CFURLCopyPassword(CFURLRef anURL) {
2613 CFStringRef passwd;
2614 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2615 CF_OBJC_CALL0(CFStringRef, passwd, anURL, "password");
2616 if (passwd) CFRetain(passwd);
2617 return passwd;
2618 }
2619 __CFGenericValidateType(anURL, __kCFURLTypeID);
2620 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2621 if (anURL->_base) {
2622 return CFURLCopyPassword(anURL->_base);
2623 }
2624 return NULL;
2625 }
2626 passwd = _retainedComponentString(anURL, HAS_PASSWORD, true, true);
2627 if (passwd) {
2628 return passwd;
2629 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
2630 return CFURLCopyPassword(anURL->_base);
2631 } else {
2632 return NULL;
2633 }
2634 }
2635
2636 // The NSURL methods do not deal with escaping escape characters at all; therefore, in order to properly bridge NSURL methods, and still provide the escaping behavior that we want, we need to create functions that match the ObjC behavior exactly, and have the public CFURL... functions call these. -- REW, 10/29/98
2637
2638 static CFStringRef _unescapedParameterString(CFURLRef anURL) {
2639 CFStringRef str;
2640 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2641 CF_OBJC_CALL0(CFStringRef, str, anURL, "parameterString");
2642 if (str) CFRetain(str);
2643 return str;
2644 }
2645 __CFGenericValidateType(anURL, __kCFURLTypeID);
2646 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2647 return NULL;
2648 }
2649 str = _retainedComponentString(anURL, HAS_PARAMETERS, false, false);
2650 if (str) return str;
2651 if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL;
2652 if (!anURL->_base || (anURL->_flags & (NET_LOCATION_MASK | HAS_PATH | HAS_SCHEME))) {
2653 return NULL;
2654 // Parameter string definitely coming from the relative portion of the URL
2655 }
2656 return _unescapedParameterString( anURL->_base);
2657 }
2658
2659 CFStringRef CFURLCopyParameterString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) {
2660 CFStringRef param = _unescapedParameterString(anURL);
2661 if (param) {
2662 CFStringRef result;
2663 if (anURL->_flags & IS_OLD_UTF8_STYLE || anURL->_encoding == kCFStringEncodingUTF8) {
2664 result = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), param, charactersToLeaveEscaped);
2665 } else {
2666 result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), param, charactersToLeaveEscaped, anURL->_encoding);
2667 }
2668 CFRelease(param);
2669 return result;
2670 }
2671 return NULL;
2672 }
2673
2674 static CFStringRef _unescapedQueryString(CFURLRef anURL) {
2675 CFStringRef str;
2676 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2677 CF_OBJC_CALL0(CFStringRef, str, anURL, "query");
2678 if (str) CFRetain(str);
2679 return str;
2680 }
2681 __CFGenericValidateType(anURL, __kCFURLTypeID);
2682 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2683 return NULL;
2684 }
2685 str = _retainedComponentString(anURL, HAS_QUERY, false, false);
2686 if (str) return str;
2687 if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL;
2688 if (!anURL->_base || (anURL->_flags & (HAS_SCHEME | NET_LOCATION_MASK | HAS_PATH | HAS_PARAMETERS))) {
2689 return NULL;
2690 }
2691 return _unescapedQueryString(anURL->_base);
2692 }
2693
2694 CFStringRef CFURLCopyQueryString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) {
2695 CFStringRef query = _unescapedQueryString(anURL);
2696 if (query) {
2697 CFStringRef tmp;
2698 if (anURL->_flags & IS_OLD_UTF8_STYLE || anURL->_encoding == kCFStringEncodingUTF8) {
2699 tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), query, charactersToLeaveEscaped);
2700 } else {
2701 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), query, charactersToLeaveEscaped, anURL->_encoding);
2702 }
2703 CFRelease(query);
2704 return tmp;
2705 }
2706 return NULL;
2707 }
2708
2709 // Fragments are NEVER taken from a base URL
2710 static CFStringRef _unescapedFragment(CFURLRef anURL) {
2711 CFStringRef str;
2712 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2713 CF_OBJC_CALL0(CFStringRef, str, anURL, "fragment");
2714 if (str) CFRetain(str);
2715 return str;
2716 }
2717 __CFGenericValidateType(anURL, __kCFURLTypeID);
2718 if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) {
2719 return NULL;
2720 }
2721 str = _retainedComponentString(anURL, HAS_FRAGMENT, false, false);
2722 return str;
2723 }
2724
2725 CFStringRef CFURLCopyFragment(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) {
2726 CFStringRef fragment = _unescapedFragment(anURL);
2727 if (fragment) {
2728 CFStringRef tmp;
2729 if (anURL->_flags & IS_OLD_UTF8_STYLE || anURL->_encoding == kCFStringEncodingUTF8) {
2730 tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped);
2731 } else {
2732 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped, anURL->_encoding);
2733 }
2734 CFRelease(fragment);
2735 return tmp;
2736 }
2737 return NULL;
2738 }
2739
2740 static CFIndex insertionLocationForMask(CFURLRef url, CFOptionFlags mask) {
2741 CFIndex firstMaskFlag = 1;
2742 CFIndex lastComponentBeforeMask = 0;
2743 while (firstMaskFlag <= HAS_FRAGMENT) {
2744 if (firstMaskFlag & mask) break;
2745 if (url->_flags & firstMaskFlag) lastComponentBeforeMask = firstMaskFlag;
2746 firstMaskFlag = firstMaskFlag << 1;
2747 }
2748 if (lastComponentBeforeMask == 0) {
2749 // mask includes HAS_SCHEME
2750 return 0;
2751 } else if (lastComponentBeforeMask == HAS_SCHEME) {
2752 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate
2753 // case file:/path/immediately/without/host
2754 CFRange schemeRg = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME);
2755 CFRange pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH);
2756 if (schemeRg.length + 1 == pathRg.location) {
2757 return schemeRg.length + 1;
2758 } else {
2759 return schemeRg.length + 3;
2760 }
2761 } else {
2762 // For all other components, the separator precedes the component, so there's no need
2763 // to add extra chars to get to the next insertion point
2764 CFRange rg = _rangeForComponent(url->_flags, url->ranges, lastComponentBeforeMask);
2765 return rg.location + rg.length;
2766 }
2767 }
2768
2769 static CFRange _CFURLGetCharRangeForMask(CFURLRef url, CFOptionFlags mask, CFRange *charRangeWithSeparators) {
2770 CFOptionFlags currentOption;
2771 CFOptionFlags firstMaskFlag = HAS_SCHEME;
2772 Boolean haveReachedMask = false;
2773 CFIndex beforeMask = 0;
2774 CFIndex afterMask = kCFNotFound;
2775 CFRange *currRange = url->ranges;
2776 CFRange maskRange = {kCFNotFound, 0};
2777 for (currentOption = 1; currentOption <= HAS_FRAGMENT; currentOption = currentOption << 1) {
2778 if (!haveReachedMask && (currentOption & mask) != 0) {
2779 firstMaskFlag = currentOption;
2780 haveReachedMask = true;
2781 }
2782 if (!(url->_flags & currentOption)) continue;
2783 if (!haveReachedMask) {
2784 beforeMask = currRange->location + currRange->length;
2785 } else if (currentOption <= mask) {
2786 if (maskRange.location == kCFNotFound) {
2787 maskRange = *currRange;
2788 } else {
2789 maskRange.length = currRange->location + currRange->length - maskRange.location;
2790 }
2791 } else {
2792 afterMask = currRange->location;
2793 break;
2794 }
2795 currRange ++;
2796 }
2797 if (afterMask == kCFNotFound) {
2798 afterMask = maskRange.location + maskRange.length;
2799 }
2800 charRangeWithSeparators->location = beforeMask;
2801 charRangeWithSeparators->length = afterMask - beforeMask;
2802 return maskRange;
2803 }
2804
2805 static CFRange _getCharRangeInDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
2806 CFOptionFlags mask;
2807 switch (component) {
2808 case kCFURLComponentScheme:
2809 mask = HAS_SCHEME;
2810 break;
2811 case kCFURLComponentNetLocation:
2812 mask = NET_LOCATION_MASK;
2813 break;
2814 case kCFURLComponentPath:
2815 mask = HAS_PATH;
2816 break;
2817 case kCFURLComponentResourceSpecifier:
2818 mask = RESOURCE_SPECIFIER_MASK;
2819 break;
2820 case kCFURLComponentUser:
2821 mask = HAS_USER;
2822 break;
2823 case kCFURLComponentPassword:
2824 mask = HAS_PASSWORD;
2825 break;
2826 case kCFURLComponentUserInfo:
2827 mask = HAS_USER | HAS_PASSWORD;
2828 break;
2829 case kCFURLComponentHost:
2830 mask = HAS_HOST;
2831 break;
2832 case kCFURLComponentPort:
2833 mask = HAS_PORT;
2834 break;
2835 case kCFURLComponentParameterString:
2836 mask = HAS_PARAMETERS;
2837 break;
2838 case kCFURLComponentQuery:
2839 mask = HAS_QUERY;
2840 break;
2841 case kCFURLComponentFragment:
2842 mask = HAS_FRAGMENT;
2843 break;
2844 default:
2845 rangeIncludingSeparators->location = kCFNotFound;
2846 rangeIncludingSeparators->length = 0;
2847 return CFRangeMake(kCFNotFound, 0);
2848 }
2849
2850 if ((url->_flags & mask) == 0) {
2851 rangeIncludingSeparators->location = insertionLocationForMask(url, mask);
2852 rangeIncludingSeparators->length = 0;
2853 return CFRangeMake(kCFNotFound, 0);
2854 } else {
2855 return _CFURLGetCharRangeForMask(url, mask, rangeIncludingSeparators);
2856 }
2857 }
2858
2859 static CFRange _getCharRangeInNonDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
2860 if (component == kCFURLComponentScheme) {
2861 CFRange schemeRg = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME);
2862 rangeIncludingSeparators->location = 0;
2863 rangeIncludingSeparators->length = schemeRg.length + 1;
2864 return schemeRg;
2865 } else if (component == kCFURLComponentResourceSpecifier) {
2866 CFRange schemeRg = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME);
2867 CFIndex stringLength = CFStringGetLength(url->_string);
2868 if (schemeRg.length + 1 == stringLength) {
2869 rangeIncludingSeparators->location = schemeRg.length + 1;
2870 rangeIncludingSeparators->length = 0;
2871 return CFRangeMake(kCFNotFound, 0);
2872 } else {
2873 rangeIncludingSeparators->location = schemeRg.length;
2874 rangeIncludingSeparators->length = stringLength - schemeRg.length;
2875 return CFRangeMake(schemeRg.length + 1, rangeIncludingSeparators->length - 1);
2876 }
2877 } else {
2878 rangeIncludingSeparators->location = kCFNotFound;
2879 rangeIncludingSeparators->length = 0;
2880 return CFRangeMake(kCFNotFound, 0);
2881 }
2882
2883 }
2884
2885 CFRange CFURLGetByteRangeForComponent(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
2886 CFRange charRange, charRangeWithSeparators;
2887 CFRange byteRange;
2888 CFAssert2(component > 0 && component < 13, __kCFLogAssertion, "%s(): passed invalid component %d", __PRETTY_FUNCTION__, component);
2889 url = _CFURLFromNSURL(url);
2890 if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) {
2891 _convertToURLRepresentation((struct __CFURL *)url);
2892 }
2893 if (!(url->_flags & IS_PARSED)) {
2894 _parseComponentsOfURL(url);
2895 }
2896
2897 if (!(url->_flags & IS_DECOMPOSABLE)) {
2898 // Special-case this because non-decomposable URLs have a slightly strange flags setup
2899 charRange = _getCharRangeInNonDecomposableURL(url, component, &charRangeWithSeparators);
2900 } else {
2901 charRange = _getCharRangeInDecomposableURL(url, component, &charRangeWithSeparators);
2902 }
2903
2904 if (charRangeWithSeparators.location == kCFNotFound) {
2905 if (rangeIncludingSeparators) {
2906 rangeIncludingSeparators->location = kCFNotFound;
2907 rangeIncludingSeparators->length = 0;
2908 }
2909 return CFRangeMake(kCFNotFound, 0);
2910 } else if (rangeIncludingSeparators) {
2911 CFStringGetBytes(url->_string, CFRangeMake(0, charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->location));
2912
2913 if (charRange.location == kCFNotFound) {
2914 byteRange = charRange;
2915 CFStringGetBytes(url->_string, charRangeWithSeparators, url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->length));
2916 } else {
2917 CFIndex maxCharRange = charRange.location + charRange.length;
2918 CFIndex maxCharRangeWithSeparators = charRangeWithSeparators.location + charRangeWithSeparators.length;
2919
2920 if (charRangeWithSeparators.location == charRange.location) {
2921 byteRange.location = rangeIncludingSeparators->location;
2922 } else {
2923 CFIndex numBytes;
2924 CFStringGetBytes(url->_string, CFRangeMake(charRangeWithSeparators.location, charRange.location - charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &numBytes);
2925 byteRange.location = charRangeWithSeparators.location + numBytes;
2926 }
2927 CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length));
2928 if (maxCharRangeWithSeparators == maxCharRange) {
2929 rangeIncludingSeparators->length = byteRange.location + byteRange.length - rangeIncludingSeparators->location;
2930 } else {
2931 CFIndex numBytes;
2932 CFRange rg;
2933 rg.location = maxCharRange;
2934 rg.length = maxCharRangeWithSeparators - rg.location;
2935 CFStringGetBytes(url->_string, rg, url->_encoding, 0, false, NULL, 0, &numBytes);
2936 rangeIncludingSeparators->length = byteRange.location + byteRange.length + numBytes - rangeIncludingSeparators->location;
2937 }
2938 }
2939 } else if (charRange.location == kCFNotFound) {
2940 byteRange = charRange;
2941 } else {
2942 CFStringGetBytes(url->_string, CFRangeMake(0, charRange.location), url->_encoding, 0, false, NULL, 0, &(byteRange.location));
2943 CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length));
2944 }
2945 return byteRange;
2946 }
2947
2948 /* Component support */
2949
2950 /* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */
2951 static CFStringRef schemeSpecificString(CFURLRef url) {
2952 Boolean isDir;
2953 isDir = ((url->_flags & IS_DIRECTORY) != 0);
2954 switch (URL_PATH_TYPE(url)) {
2955 case kCFURLPOSIXPathStyle:
2956 if (url->_flags & POSIX_AND_URL_PATHS_MATCH) {
2957 return CFRetain(url->_string);
2958 } else {
2959 return POSIXPathToURLPath(url->_string, CFGetAllocator(url), isDir);
2960 }
2961 case kCFURLHFSPathStyle:
2962 return HFSPathToURLPath(url->_string, CFGetAllocator(url), isDir);
2963 case kCFURLWindowsPathStyle:
2964 return WindowsPathToURLPath(url->_string, CFGetAllocator(url), isDir);
2965 case FULL_URL_REPRESENTATION:
2966 return CFURLCopyResourceSpecifier(url);
2967 default:
2968 return NULL;
2969 }
2970 }
2971
2972 static Boolean decomposeToNonHierarchical(CFURLRef url, CFURLComponentsNonHierarchical *components) {
2973 if ( CFURLGetBaseURL(url) != NULL) {
2974 components->scheme = NULL;
2975 } else {
2976 components->scheme = CFURLCopyScheme(url);
2977 }
2978 components->schemeSpecific = schemeSpecificString(url);
2979 return true;
2980 }
2981
2982 static CFURLRef composeFromNonHierarchical(CFAllocatorRef alloc, const CFURLComponentsNonHierarchical *components) {
2983 CFStringRef str;
2984 if (components->scheme) {
2985 UniChar ch = ':';
2986 str = CFStringCreateMutableCopy(alloc, CFStringGetLength(components->scheme) + 1 + (components->schemeSpecific ? CFStringGetLength(components->schemeSpecific): 0), components->scheme);
2987 CFStringAppendCharacters((CFMutableStringRef)str, &ch, 1);
2988 if (components->schemeSpecific) CFStringAppend((CFMutableStringRef)str, components->schemeSpecific);
2989 } else if (components->schemeSpecific) {
2990 str = components->schemeSpecific;
2991 CFRetain(str);
2992 } else {
2993 str = NULL;
2994 }
2995 if (str) {
2996 CFURLRef url = CFURLCreateWithString(alloc, str, NULL);
2997 CFRelease(str);
2998 return url;
2999 } else {
3000 return NULL;
3001 }
3002 }
3003
3004 static Boolean decomposeToRFC1808(CFURLRef url, CFURLComponentsRFC1808 *components) {
3005 CFAllocatorRef alloc = CFGetAllocator(url);
3006 int pathType;
3007 static CFStringRef emptyStr = NULL;
3008 if (!emptyStr) {
3009 emptyStr = CFSTR("");
3010 }
3011
3012 if (!CFURLCanBeDecomposed(url)) {
3013 return false;
3014 }
3015 if ((pathType = URL_PATH_TYPE(url)) == FULL_URL_REPRESENTATION) {
3016 CFStringRef path = CFURLCopyPath(url);
3017 if (path) {
3018 components->pathComponents = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("/"));
3019 CFRelease(path);
3020 } else {
3021 components->pathComponents = NULL;
3022 }
3023 components->baseURL = CFURLGetBaseURL(url);
3024 if (components->baseURL) {
3025 CFRetain(components->baseURL);
3026 components->scheme = NULL;
3027 } else {
3028 components->scheme = _retainedComponentString(url, HAS_SCHEME, true, false);
3029 }
3030 components->user = _retainedComponentString(url, HAS_USER, false, false);
3031 components->password = _retainedComponentString(url, HAS_PASSWORD, false, false);
3032 components->host = _retainedComponentString(url, HAS_HOST, false, false);
3033 if (url->_flags & HAS_PORT) {
3034 components->port = CFURLGetPortNumber(url);
3035 } else {
3036 components->port = kCFNotFound;
3037 }
3038 components->parameterString = _retainedComponentString(url, HAS_PARAMETERS, false, false);
3039 components->query = _retainedComponentString(url, HAS_QUERY, false, false);
3040 components->fragment = _retainedComponentString(url, HAS_FRAGMENT, false, false);
3041 } else {
3042 switch (pathType) {
3043 case kCFURLPOSIXPathStyle: {
3044 CFStringRef pathStr;
3045 if (url->_flags & POSIX_AND_URL_PATHS_MATCH) {
3046 pathStr = url->_string;
3047 CFRetain(pathStr);
3048 } else {
3049 pathStr = POSIXPathToURLPath(url->_string, alloc, url->_flags & IS_DIRECTORY);
3050 }
3051 components->pathComponents = CFStringCreateArrayBySeparatingStrings(alloc, pathStr, CFSTR("/"));
3052 CFRelease(pathStr);
3053 break;
3054 }
3055 case kCFURLHFSPathStyle:
3056 components->pathComponents = HFSPathToURLComponents(url->_string, alloc, ((url->_flags & IS_DIRECTORY) != 0));
3057 break;
3058 case kCFURLWindowsPathStyle:
3059 components->pathComponents = WindowsPathToURLComponents(url->_string, alloc, ((url->_flags & IS_DIRECTORY) != 0));
3060 break;
3061 default:
3062 components->pathComponents = NULL;
3063 }
3064 if (!components->pathComponents) {
3065 return false;
3066 }
3067 components->scheme = CFRetain(kCFURLFileScheme);
3068 components->user = NULL;
3069 components->password = NULL;
3070 components->host = CFRetain(kCFURLLocalhost);
3071 components->port = kCFNotFound;
3072 components->parameterString = NULL;
3073 components->query = NULL;
3074 components->fragment = NULL;
3075 components->baseURL = CFURLGetBaseURL(url);
3076 if (components->baseURL) CFRetain(components->baseURL);
3077 }
3078 return true;
3079 }
3080
3081 static CFURLRef composeFromRFC1808(CFAllocatorRef alloc, const CFURLComponentsRFC1808 *comp) {
3082 CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0);
3083 CFURLRef base = comp->baseURL;
3084 CFURLRef url;
3085 Boolean hadPrePathComponent = false;
3086 if (comp->scheme) {
3087 base = NULL;
3088 CFStringAppend(urlString, comp->scheme);
3089 CFStringAppend(urlString, CFSTR("://"));
3090 hadPrePathComponent = true;
3091 }
3092 if (comp->user || comp->password) {
3093 if (comp->user) {
3094 CFStringAppend(urlString, comp->user);
3095 }
3096 if (comp->password) {
3097 CFStringAppend(urlString, CFSTR(":"));
3098 CFStringAppend(urlString, comp->password);
3099 }
3100 CFStringAppend(urlString, CFSTR("@"));
3101 hadPrePathComponent = true;
3102 }
3103 if (comp->host) {
3104 CFStringAppend(urlString, comp->host);
3105 hadPrePathComponent = true;
3106 }
3107 if (comp->port != kCFNotFound) {
3108 CFStringAppendFormat(urlString, NULL, CFSTR(":%d"), comp->port);
3109 hadPrePathComponent = true;
3110 }
3111
3112 if (hadPrePathComponent && (comp->pathComponents == NULL || CFStringGetLength(CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) {
3113 CFStringAppend(urlString, CFSTR("/"));
3114 }
3115 if (comp->pathComponents) {
3116 CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/"));
3117 CFStringAppend(urlString, pathStr);
3118 CFRelease(pathStr);
3119 }
3120 if (comp->parameterString) {
3121 CFStringAppend(urlString, CFSTR(";"));
3122 CFStringAppend(urlString, comp->parameterString);
3123 }
3124 if (comp->query) {
3125 CFStringAppend(urlString, CFSTR("?"));
3126 CFStringAppend(urlString, comp->query);
3127 }
3128 if (comp->fragment) {
3129 CFStringAppend(urlString, CFSTR("#"));
3130 CFStringAppend(urlString, comp->fragment);
3131 }
3132 url = CFURLCreateWithString(alloc, urlString, base);
3133 CFRelease(urlString);
3134 return url;
3135 }
3136
3137 static Boolean decomposeToRFC2396(CFURLRef url, CFURLComponentsRFC2396 *comp) {
3138 CFAllocatorRef alloc = CFGetAllocator(url);
3139 CFURLComponentsRFC1808 oldComp;
3140 CFStringRef tmpStr;
3141 if (!decomposeToRFC1808(url, &oldComp)) {
3142 return false;
3143 }
3144 comp->scheme = oldComp.scheme;
3145 if (oldComp.user) {
3146 if (oldComp.password) {
3147 comp->userinfo = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@:%@"), oldComp.user, oldComp.password);
3148 CFRelease(oldComp.password);
3149 CFRelease(oldComp.user);
3150 } else {
3151 comp->userinfo = oldComp.user;
3152 }
3153 } else {
3154 comp->userinfo = NULL;
3155 }
3156 comp->host = oldComp.host;
3157 comp->port = oldComp.port;
3158 if (!oldComp.parameterString) {
3159 comp->pathComponents = oldComp.pathComponents;
3160 } else {
3161 int length = CFArrayGetCount(oldComp.pathComponents);
3162 comp->pathComponents = CFArrayCreateMutableCopy(alloc, length, oldComp.pathComponents);
3163 tmpStr = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp->pathComponents, length - 1), oldComp.parameterString);
3164 CFArraySetValueAtIndex((CFMutableArrayRef)comp->pathComponents, length - 1, tmpStr);
3165 CFRelease(tmpStr);
3166 CFRelease(oldComp.pathComponents);
3167 CFRelease(oldComp.parameterString);
3168 }
3169 comp->query = oldComp.query;
3170 comp->fragment = oldComp.fragment;
3171 comp->baseURL = oldComp.baseURL;
3172 return true;
3173 }
3174
3175 static CFURLRef composeFromRFC2396(CFAllocatorRef alloc, const CFURLComponentsRFC2396 *comp) {
3176 CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0);
3177 CFURLRef base = comp->baseURL;
3178 CFURLRef url;
3179 Boolean hadPrePathComponent = false;
3180 if (comp->scheme) {
3181 base = NULL;
3182 CFStringAppend(urlString, comp->scheme);
3183 CFStringAppend(urlString, CFSTR("://"));
3184 hadPrePathComponent = true;
3185 }
3186 if (comp->userinfo) {
3187 CFStringAppend(urlString, comp->userinfo);
3188 CFStringAppend(urlString, CFSTR("@"));
3189 hadPrePathComponent = true;
3190 }
3191 if (comp->host) {
3192 CFStringAppend(urlString, comp->host);
3193 if (comp->port != kCFNotFound) {
3194 CFStringAppendFormat(urlString, NULL, CFSTR(":%d"), comp->port);
3195 }
3196 hadPrePathComponent = true;
3197 }
3198 if (hadPrePathComponent && (comp->pathComponents == NULL || CFStringGetLength(CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) {
3199 CFStringAppend(urlString, CFSTR("/"));
3200 }
3201 if (comp->pathComponents) {
3202 CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/"));
3203 CFStringAppend(urlString, pathStr);
3204 CFRelease(pathStr);
3205 }
3206 if (comp->query) {
3207 CFStringAppend(urlString, CFSTR("?"));
3208 CFStringAppend(urlString, comp->query);
3209 }
3210 if (comp->fragment) {
3211 CFStringAppend(urlString, CFSTR("#"));
3212 CFStringAppend(urlString, comp->fragment);
3213 }
3214 url = CFURLCreateWithString(alloc, urlString, base);
3215 CFRelease(urlString);
3216 return url;
3217 }
3218
3219 #undef CFURLCopyComponents
3220 #undef CFURLCreateFromComponents
3221 CF_EXPORT
3222 Boolean _CFURLCopyComponents(CFURLRef url, CFURLComponentDecomposition decompositionType, void *components) {
3223 url = _CFURLFromNSURL(url);
3224 switch (decompositionType) {
3225 case kCFURLComponentDecompositionNonHierarchical:
3226 return decomposeToNonHierarchical(url, (CFURLComponentsNonHierarchical *)components);
3227 case kCFURLComponentDecompositionRFC1808:
3228 return decomposeToRFC1808(url, (CFURLComponentsRFC1808 *)components);
3229 case kCFURLComponentDecompositionRFC2396:
3230 return decomposeToRFC2396(url, (CFURLComponentsRFC2396 *)components);
3231 default:
3232 return false;
3233 }
3234 }
3235
3236 CF_EXPORT
3237 CFURLRef _CFURLCreateFromComponents(CFAllocatorRef alloc, CFURLComponentDecomposition decompositionType, const void *components) {
3238 switch (decompositionType) {
3239 case kCFURLComponentDecompositionNonHierarchical:
3240 return composeFromNonHierarchical(alloc, (const CFURLComponentsNonHierarchical *)components);
3241 case kCFURLComponentDecompositionRFC1808:
3242 return composeFromRFC1808(alloc, (const CFURLComponentsRFC1808 *)components);
3243 case kCFURLComponentDecompositionRFC2396:
3244 return composeFromRFC2396(alloc, (const CFURLComponentsRFC2396 *)components);
3245 default:
3246 return NULL;
3247 }
3248 }
3249
3250 CF_EXPORT void *__CFURLReservedPtr(CFURLRef url) {
3251 return _getReserved(url);
3252 }
3253
3254 CF_EXPORT void __CFURLSetReservedPtr(CFURLRef url, void *ptr) {
3255 _setReserved ( (struct __CFURL*) url, ptr );
3256 }
3257
3258
3259 /* File system stuff */
3260
3261 /* HFSPath<->URLPath functions at the bottom of the file */
3262 static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) {
3263 CFArrayRef tmp;
3264 CFMutableArrayRef urlComponents = NULL;
3265 CFIndex i=0;
3266
3267 tmp = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("\\"));
3268 urlComponents = CFArrayCreateMutableCopy(alloc, 0, tmp);
3269 CFRelease(tmp);
3270
3271 CFStringRef str = CFArrayGetValueAtIndex(urlComponents, 0);
3272 if (CFStringGetLength(str) == 2 && CFStringGetCharacterAtIndex(str, 1) == ':') {
3273 CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR("")); // So we get a leading '/' below
3274 i = 2; // Skip over the drive letter and the empty string we just inserted
3275 }
3276 CFIndex c;
3277 for (c = CFArrayGetCount(urlComponents); i < c; i ++) {
3278 CFStringRef fileComp = CFArrayGetValueAtIndex(urlComponents,i);
3279 CFStringRef urlComp = _replacePathIllegalCharacters(fileComp, alloc, false);
3280 if (!urlComp) {
3281 // Couldn't decode fileComp
3282 CFRelease(urlComponents);
3283 return NULL;
3284 }
3285 if (urlComp != fileComp) {
3286 CFArraySetValueAtIndex(urlComponents, i, urlComp);
3287 }
3288 CFRelease(urlComp);
3289 }
3290
3291 if (isDir) {
3292 if (CFStringGetLength(CFArrayGetValueAtIndex(urlComponents, CFArrayGetCount(urlComponents) - 1)) != 0)
3293 CFArrayAppendValue(urlComponents, CFSTR(""));
3294 }
3295 return urlComponents;
3296 }
3297
3298 static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) {
3299 CFArrayRef urlComponents;
3300 CFStringRef str;
3301
3302 if (CFStringGetLength(path) == 0) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII);
3303 urlComponents = WindowsPathToURLComponents(path, alloc, isDir);
3304 if (!urlComponents) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII);
3305
3306 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3307 str = CFStringCreateByCombiningStrings(alloc, urlComponents, CFSTR("/"));
3308 CFRelease(urlComponents);
3309 return str;
3310 }
3311
3312 static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory) {
3313 CFStringRef pathString = _replacePathIllegalCharacters(path, alloc, true);
3314 if (isDirectory && CFStringGetCharacterAtIndex(path, CFStringGetLength(path)-1) != '/') {
3315 CFStringRef tmp = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@/"), pathString);
3316 CFRelease(pathString);
3317 pathString = tmp;
3318 }
3319 return pathString;
3320 }
3321
3322 static CFStringRef URLPathToPOSIXPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) {
3323 // This is the easiest case; just remove the percent escape codes and we're done
3324 CFStringRef result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, path, CFSTR(""), encoding);
3325 if (result) {
3326 CFIndex length = CFStringGetLength(result);
3327 if (length > 1 && CFStringGetCharacterAtIndex(result, length-1) == '/') {
3328 CFStringRef tmp = CFStringCreateWithSubstring(allocator, result, CFRangeMake(0, length-1));
3329 CFRelease(result);
3330 result = tmp;
3331 }
3332 }
3333 return result;
3334 }
3335
3336
3337 static CFStringRef URLPathToWindowsPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) {
3338 // Check for a drive letter, then flip all the slashes
3339 CFStringRef result;
3340 CFArrayRef tmp = CFStringCreateArrayBySeparatingStrings(allocator, path, CFSTR("/"));
3341 SInt32 count = CFArrayGetCount(tmp);
3342 CFMutableArrayRef components = CFArrayCreateMutableCopy(allocator, count, tmp);
3343 CFStringRef newPath;
3344
3345 CFRelease(tmp);
3346 if (CFStringGetLength(CFArrayGetValueAtIndex(components,count-1)) == 0) {
3347 CFArrayRemoveValueAtIndex(components, count-1);
3348 count --;
3349 }
3350 if (count > 1 && CFStringGetLength(CFArrayGetValueAtIndex(components, 0)) == 0) {
3351 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
3352 CFStringRef firstComponent = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, CFArrayGetValueAtIndex(components, 1), CFSTR(""), encoding);
3353 UniChar ch;
3354 if (CFStringGetLength(firstComponent) == 2 && ((ch = CFStringGetCharacterAtIndex(firstComponent, 1)) == '|' || ch == ':')) {
3355 // Drive letter
3356 CFArrayRemoveValueAtIndex(components, 0);
3357 if (ch == '|') {
3358 CFStringRef driveStr = CFStringCreateWithFormat(allocator, NULL, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent, 0));
3359 CFArraySetValueAtIndex(components, 0, driveStr);
3360 CFRelease(driveStr);
3361 }
3362 }
3363 CFRelease(firstComponent);
3364 }
3365
3366 newPath = CFStringCreateByCombiningStrings(allocator, components, CFSTR("\\"));
3367 CFRelease(components);
3368 result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, newPath, CFSTR(""), encoding);
3369 CFRelease(newPath);
3370 return result;
3371 }
3372
3373 // converts url from a file system path representation to a standard representation
3374 static void _convertToURLRepresentation(struct __CFURL *url) {
3375 CFStringRef path = NULL;
3376 Boolean isDir = ((url->_flags & IS_DIRECTORY) != 0);
3377 CFAllocatorRef alloc = CFGetAllocator(url);
3378
3379 #if DEBUG_URL_MEMORY_USAGE
3380 numFileURLsConverted ++;
3381 #endif
3382
3383 switch (URL_PATH_TYPE(url)) {
3384 case kCFURLPOSIXPathStyle:
3385 if (url->_flags & POSIX_AND_URL_PATHS_MATCH) {
3386 path = CFRetain(url->_string);
3387 } else {
3388 path = POSIXPathToURLPath(url->_string, alloc, isDir);
3389 }
3390 break;
3391 case kCFURLHFSPathStyle:
3392 path = HFSPathToURLPath(url->_string, alloc, isDir);
3393 break;
3394 case kCFURLWindowsPathStyle:
3395 path = WindowsPathToURLPath(url->_string, alloc, isDir);
3396 break;
3397 }
3398 CFAssert2(path != NULL, __kCFLogAssertion, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__, url);
3399 if (!url->_base) {
3400 CFStringRef str;
3401 str = CFStringCreateWithFormat(alloc, NULL, CFSTR("file://localhost%@"), path);
3402 url->_flags = (url->_flags & (IS_DIRECTORY)) | (FULL_URL_REPRESENTATION << 16) | IS_DECOMPOSABLE | IS_ABSOLUTE | IS_PARSED | HAS_SCHEME | HAS_HOST | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH;
3403 CFRelease(url->_string);
3404 url->_string = str;
3405 url->ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 3, 0);
3406 url->ranges[0] = CFRangeMake(0, 4);
3407 url->ranges[1] = CFRangeMake(7, 9);
3408 url->ranges[2] = CFRangeMake(16, CFStringGetLength(path));
3409 CFRelease(path);
3410 } else {
3411 CFRelease(url->_string);
3412 url->_flags = (url->_flags & (IS_DIRECTORY)) | (FULL_URL_REPRESENTATION << 16) | IS_DECOMPOSABLE | IS_PARSED | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH;
3413 url->_string = path;
3414 url->ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange), 0);
3415 *(url->ranges) = CFRangeMake(0, CFStringGetLength(path));
3416 }
3417 }
3418
3419 // relativeURL is known to be a file system URL whose base is a matching file system URL
3420 static CFURLRef _CFURLCopyAbsoluteFileURL(CFURLRef relativeURL) {
3421 CFAllocatorRef alloc = CFGetAllocator(relativeURL);
3422 CFURLPathStyle fsType = URL_PATH_TYPE(relativeURL);
3423 CFURLRef base = relativeURL->_base;
3424 CFStringRef newPath = _resolveFileSystemPaths(relativeURL->_string, base->_string, (base->_flags & IS_DIRECTORY) != 0, fsType, alloc);
3425 CFURLRef result = CFURLCreateWithFileSystemPath(alloc, newPath, fsType, (relativeURL->_flags & IS_DIRECTORY) != 0);
3426 CFRelease(newPath);
3427 return result;
3428 }
3429
3430 // Caller must release the returned string
3431 static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc) {
3432 CFIndex baseLen = CFStringGetLength(basePath);
3433 CFIndex relLen = CFStringGetLength(relativePath);
3434 UniChar pathDelimiter = PATH_DELIM_FOR_TYPE(fsType);
3435 UniChar *buf = CFAllocatorAllocate(alloc, sizeof(UniChar)*(relLen + baseLen + 2), 0);
3436 CFStringGetCharacters(basePath, CFRangeMake(0, baseLen), buf);
3437 if (baseIsDir) {
3438 if (buf[baseLen-1] != pathDelimiter) {
3439 buf[baseLen] = pathDelimiter;
3440 baseLen ++;
3441 }
3442 } else {
3443 UniChar *ptr = buf + baseLen - 1;
3444 while (ptr > buf && *ptr != pathDelimiter) {
3445 ptr --;
3446 }
3447 baseLen = ptr - buf + 1;
3448 }
3449 if (fsType == kCFURLHFSPathStyle) {
3450 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
3451 baseLen --;
3452 }
3453 CFStringGetCharacters(relativePath, CFRangeMake(0, relLen), buf + baseLen);
3454 *(buf + baseLen + relLen) = '\0';
3455 return _resolvedPath(buf, buf + baseLen + relLen, pathDelimiter, false, true, alloc);
3456 }
3457
3458 CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator) {
3459 CFURLRef url = NULL;
3460 uint8_t buf[CFMaxPathSize + 1];
3461 if (_CFGetCurrentDirectory(buf, CFMaxPathLength)) {
3462 url = CFURLCreateFromFileSystemRepresentation(allocator, buf, strlen(buf), true);
3463 }
3464 return url;
3465 }
3466
3467 CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory) {
3468 Boolean isAbsolute = true;
3469 CFIndex len;
3470 CFURLRef baseURL, result;
3471
3472 CFAssert2(fsType == kCFURLPOSIXPathStyle || fsType == kCFURLHFSPathStyle || fsType == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__, fsType);
3473 CFAssert1(filePath != NULL, __kCFLogAssertion, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__);
3474
3475 len = CFStringGetLength(filePath);
3476
3477 switch(fsType) {
3478 case kCFURLPOSIXPathStyle:
3479 isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) == '/');
3480 break;
3481 case kCFURLWindowsPathStyle:
3482 isAbsolute = (len >= 3 && CFStringGetCharacterAtIndex(filePath, 1) == ':' && CFStringGetCharacterAtIndex(filePath, 2) == '\\');
3483 /* Absolute path under Win32 can begin with "\\"
3484 * (Sergey Zubarev)
3485 */
3486 if (!isAbsolute) isAbsolute = (len > 2 && CFStringGetCharacterAtIndex(filePath, 0) == '\\' && CFStringGetCharacterAtIndex(filePath, 1) == '\\');
3487 break;
3488 case kCFURLHFSPathStyle:
3489 isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) != ':');
3490 break;
3491 }
3492 if (isAbsolute) {
3493 baseURL = NULL;
3494 } else {
3495 baseURL = _CFURLCreateCurrentDirectoryURL(allocator);
3496 }
3497 result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, filePath, fsType, isDirectory, baseURL);
3498 if (baseURL) CFRelease(baseURL);
3499 return result;
3500 }
3501
3502 CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory, CFURLRef baseURL) {
3503 CFURLRef url;
3504 Boolean isAbsolute = true, releaseFilePath = false;
3505 UniChar pathDelim = '\0';
3506 CFIndex len;
3507
3508 CFAssert1(filePath != NULL, __kCFLogAssertion, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__);
3509 CFAssert2(fsType == kCFURLPOSIXPathStyle || fsType == kCFURLHFSPathStyle || fsType == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__, fsType);
3510
3511 len = CFStringGetLength(filePath);
3512
3513 switch(fsType) {
3514 case kCFURLPOSIXPathStyle:
3515 isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) == '/');
3516
3517 pathDelim = '/';
3518 break;
3519 case kCFURLWindowsPathStyle:
3520 isAbsolute = (len >= 3 && CFStringGetCharacterAtIndex(filePath, 1) == ':' && CFStringGetCharacterAtIndex(filePath, 2) == '\\');
3521 /* Absolute path under Win32 can begin with "\\"
3522 * (Sergey Zubarev)
3523 */
3524 if (!isAbsolute)
3525 isAbsolute = (len > 2 && CFStringGetCharacterAtIndex(filePath, 0) == '\\' && CFStringGetCharacterAtIndex(filePath, 1) == '\\');
3526 pathDelim = '\\';
3527 break;
3528 case kCFURLHFSPathStyle:
3529 { CFRange fullStrRange = CFRangeMake( 0, CFStringGetLength( filePath ) );
3530
3531 isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) != ':');
3532 pathDelim = ':';
3533
3534 if ( _CFExecutableLinkedOnOrAfter( CFSystemVersionTiger ) &&
3535 filePath && CFStringFindWithOptions( filePath, CFSTR("::"), fullStrRange, 0, NULL ) ) {
3536 UniChar* chars = (UniChar*) malloc( fullStrRange.length * sizeof( UniChar ) );
3537 CFIndex index, writeIndex, firstColonOffset = -1;
3538
3539 CFStringGetCharacters( filePath, fullStrRange, chars );
3540
3541 for ( index = 0, writeIndex = 0 ; index < fullStrRange.length; index ++ ) {
3542 if ( chars[ index ] == ':' ) {
3543 if ( index + 1 < fullStrRange.length && chars[ index + 1 ] == ':' ) {
3544
3545 // Don't let :: go off the 'top' of the path -- which means that there always has to be at
3546 // least one ':' to the left of the current write position to go back to.
3547 if ( writeIndex > 0 && firstColonOffset >= 0 )
3548 {
3549 writeIndex --;
3550 while ( writeIndex > 0 && writeIndex >= firstColonOffset && chars[ writeIndex ] != ':' )
3551 writeIndex --;
3552 }
3553 index ++; // skip over the first ':', so we replace the ':' which is there with a new one
3554 }
3555
3556 if ( firstColonOffset == -1 )
3557 firstColonOffset = writeIndex;
3558 }
3559
3560 chars[ writeIndex ++ ] = chars[ index ];
3561 }
3562
3563 if ( releaseFilePath && filePath )
3564 CFRelease( filePath );
3565
3566 filePath = CFStringCreateWithCharacters( allocator, chars, writeIndex );
3567 releaseFilePath = true;
3568
3569 free( chars );
3570 }
3571
3572 break;
3573 }
3574 }
3575 if (isAbsolute) {
3576 baseURL = NULL;
3577 }
3578
3579 if (isDirectory && len > 0 && CFStringGetCharacterAtIndex(filePath, len-1) != pathDelim) {
3580 CFStringRef tempRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c"), filePath, pathDelim);
3581 if ( releaseFilePath && filePath ) CFRelease( filePath );
3582 filePath = tempRef;
3583 releaseFilePath = true;
3584 } else if (!isDirectory && len > 0 && CFStringGetCharacterAtIndex(filePath, len-1) == pathDelim) {
3585 if (len == 1 || CFStringGetCharacterAtIndex(filePath, len-2) == pathDelim) {
3586 // Override isDirectory
3587 isDirectory = true;
3588 } else {
3589 CFStringRef tempRef = CFStringCreateWithSubstring(allocator, filePath, CFRangeMake(0, len-1));
3590 if ( releaseFilePath && filePath )
3591 CFRelease( filePath );
3592 filePath = tempRef;
3593 releaseFilePath = true;
3594 }
3595 }
3596 if (!filePath || CFStringGetLength(filePath) == 0) {
3597 if (releaseFilePath && filePath) CFRelease(filePath);
3598 return NULL;
3599 }
3600 url = _CFURLAlloc(allocator);
3601 _CFURLInit((struct __CFURL *)url, filePath, fsType, baseURL);
3602 if (releaseFilePath) CFRelease(filePath);
3603 if (isDirectory) ((struct __CFURL *)url)->_flags |= IS_DIRECTORY;
3604 if (fsType == kCFURLPOSIXPathStyle) {
3605 // Check if relative path is equivalent to URL representation; this will be true if url->_string contains only characters from the unreserved character set, plus '/' to delimit the path, plus ';', '@', '&', '=', '+', '$', ',' (according to RFC 2396) -- REW, 12/1/2000
3606 // Per Section 5 of RFC 2396, there's a special problem if a colon apears in the first path segment - in this position, it can be mistaken for the scheme name. Otherwise, it's o.k., and can be safely identified as part of the path. In this one case, we need to prepend "./" to make it clear what's going on.... -- REW, 8/24/2001
3607 CFStringInlineBuffer buf;
3608 Boolean sawSlash = FALSE;
3609 Boolean mustPrependDotSlash = FALSE;
3610 CFIndex idx, length = CFStringGetLength(url->_string);
3611 CFStringInitInlineBuffer(url->_string, &buf, CFRangeMake(0, length));
3612 for (idx = 0; idx < length; idx ++) {
3613 UniChar ch = CFStringGetCharacterFromInlineBuffer(&buf, idx);
3614 if (!isPathLegalCharacter(ch)) break;
3615 if (!sawSlash) {
3616 if (ch == '/') {
3617 sawSlash = TRUE;
3618 } else if (ch == ':') {
3619 mustPrependDotSlash = TRUE;
3620 }
3621 }
3622 }
3623 if (idx == length) {
3624 ((struct __CFURL *)url)->_flags |= POSIX_AND_URL_PATHS_MATCH;
3625 }
3626 if (mustPrependDotSlash) {
3627 CFStringRef newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("./%@"), url->_string);
3628 CFRelease(url->_string);
3629 ((struct __CFURL *)url)->_string = newString;
3630 }
3631 }
3632 return url;
3633 }
3634
3635 CF_EXPORT CFStringRef CFURLCopyFileSystemPath(CFURLRef anURL, CFURLPathStyle pathStyle) {
3636 CFAssert2(pathStyle == kCFURLPOSIXPathStyle || pathStyle == kCFURLHFSPathStyle || pathStyle == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__, pathStyle);
3637 return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL), anURL, pathStyle, false);
3638 }
3639
3640 // There is no matching ObjC method for this functionality; because this function sits on top of the CFURL primitives, it's o.k. not to check for the need to dispatch an ObjC method instead, but this means care must be taken that this function never call anything that will result in dereferencing anURL without first checking for an ObjC dispatch. -- REW, 10/29/98
3641 CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase) {
3642 CFURLRef base = resolveAgainstBase ? CFURLGetBaseURL(anURL) : NULL;
3643 CFStringRef basePath = base ? CFURLCreateStringWithFileSystemPath(allocator, base, fsType, false) : NULL;
3644 CFStringRef relPath = NULL;
3645
3646 if (!CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3647 // We can grope the ivars
3648 CFURLPathStyle myType = URL_PATH_TYPE(anURL);
3649 if (myType == fsType) {
3650 relPath = CFRetain(anURL->_string);
3651 } else if (fsType == kCFURLPOSIXPathStyle && myType == FULL_URL_REPRESENTATION) {
3652 if (!(anURL->_flags & IS_PARSED)) {
3653 _parseComponentsOfURL(anURL);
3654 }
3655 if (anURL->_flags & POSIX_AND_URL_PATHS_MATCH) {
3656 relPath = _retainedComponentString(anURL, HAS_PATH, true, true);
3657 }
3658 }
3659 }
3660
3661 if (relPath == NULL) {
3662 CFStringRef urlPath = CFURLCopyPath(anURL);
3663 CFStringEncoding enc = (anURL->_flags & IS_OLD_UTF8_STYLE) ? kCFStringEncodingUTF8 : anURL->_encoding;
3664 if (urlPath) {
3665 switch (fsType) {
3666 case kCFURLPOSIXPathStyle:
3667 relPath = URLPathToPOSIXPath(urlPath, allocator, enc);
3668 break;
3669 case kCFURLHFSPathStyle:
3670 relPath = URLPathToHFSPath(urlPath, allocator, enc);
3671 break;
3672 case kCFURLWindowsPathStyle:
3673 relPath = URLPathToWindowsPath(urlPath, allocator, enc);
3674 break;
3675 default:
3676 CFAssert2(true, __kCFLogAssertion, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__, fsType);
3677 }
3678 CFRelease(urlPath);
3679 }
3680 }
3681
3682 // For Tiger, leave this behavior in for all path types. For Chablis, it would be nice to remove this entirely
3683 // and do a linked-on-or-later check so we don't break third parties.
3684 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
3685 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details.
3686 if ( relPath && CFURLHasDirectoryPath(anURL) && CFStringGetLength(relPath) > 1 && CFStringGetCharacterAtIndex(relPath, CFStringGetLength(relPath)-1) == PATH_DELIM_FOR_TYPE(fsType)) {
3687 CFStringRef tmp = CFStringCreateWithSubstring(allocator, relPath, CFRangeMake(0, CFStringGetLength(relPath)-1));
3688 CFRelease(relPath);
3689 relPath = tmp;
3690 }
3691
3692 // Note that !resolveAgainstBase implies !base
3693 if (!basePath || !relPath) {
3694 return relPath;
3695 } else {
3696 CFStringRef result = _resolveFileSystemPaths(relPath, basePath, CFURLHasDirectoryPath(base), fsType, allocator);
3697 CFRelease(basePath);
3698 CFRelease(relPath);
3699 return result;
3700 }
3701 }
3702
3703 Boolean CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, uint8_t *buffer, CFIndex bufLen) {
3704 CFStringRef path;
3705 CFAllocatorRef alloc = CFGetAllocator(url);
3706
3707 if (!url) return false;
3708 #if defined(__WIN32__)
3709 path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLWindowsPathStyle, resolveAgainstBase);
3710 #else
3711 path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLPOSIXPathStyle, resolveAgainstBase);
3712 #endif
3713 if (path) {
3714 #if defined(__MACH__)
3715 Boolean convResult = _CFStringGetFileSystemRepresentation(path, buffer, bufLen);
3716 CFRelease(path);
3717 return convResult;
3718 #else
3719 CFIndex usedLen;
3720 CFIndex pathLen = CFStringGetLength(path);
3721 CFIndex numConverted = CFStringGetBytes(path, CFRangeMake(0, pathLen), CFStringFileSystemEncoding(), 0, true, buffer, bufLen-1, &usedLen); // -1 because we need one byte to zero-terminate.
3722 CFRelease(path);
3723 if (numConverted == pathLen) {
3724 buffer[usedLen] = '\0';
3725 return true;
3726 }
3727 #endif
3728 }
3729 return false;
3730 }
3731
3732 CFURLRef CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory) {
3733 CFStringRef path = CFStringCreateWithBytes(allocator, buffer, bufLen, CFStringFileSystemEncoding(), false);
3734 CFURLRef newURL;
3735 if (!path) return NULL;
3736 #if defined(__WIN32__)
3737 newURL = CFURLCreateWithFileSystemPath(allocator, path, kCFURLWindowsPathStyle, isDirectory);
3738 #else
3739 newURL = CFURLCreateWithFileSystemPath(allocator, path, kCFURLPOSIXPathStyle, isDirectory);
3740 #endif
3741 CFRelease(path);
3742 return newURL;
3743 }
3744
3745 CF_EXPORT CFURLRef CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL) {
3746 CFStringRef path = CFStringCreateWithBytes(allocator, buffer, bufLen, CFStringFileSystemEncoding(), false);
3747 CFURLRef newURL;
3748 if (!path) return NULL;
3749 #if defined(__WIN32__)
3750 newURL = CFURLCreateWithFileSystemPathRelativeToBase(allocator, path, kCFURLWindowsPathStyle, isDirectory, baseURL);
3751 #else
3752 newURL = CFURLCreateWithFileSystemPathRelativeToBase(allocator, path, kCFURLPOSIXPathStyle, isDirectory, baseURL);
3753 #endif
3754 CFRelease(path);
3755 return newURL;
3756 }
3757
3758
3759 /******************************/
3760 /* Support for path utilities */
3761 /******************************/
3762
3763 // Assumes url is a CFURL (not an Obj-C NSURL)
3764 static CFRange _rangeOfLastPathComponent(CFURLRef url) {
3765 UInt32 pathType = URL_PATH_TYPE(url);
3766 CFRange pathRg, componentRg;
3767
3768 if (pathType == FULL_URL_REPRESENTATION) {
3769 if (!(url->_flags & IS_PARSED)) _parseComponentsOfURL(url);
3770 pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH);
3771 } else {
3772 pathRg = CFRangeMake(0, CFStringGetLength(url->_string));
3773 }
3774
3775 if (pathRg.location == kCFNotFound || pathRg.length == 0) {
3776 // No path
3777 return pathRg;
3778 }
3779 if (CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) == PATH_DELIM_FOR_TYPE(pathType)) {
3780 pathRg.length --;
3781 if (pathRg.length == 0) {
3782 pathRg.length ++;
3783 return pathRg;
3784 }
3785 }
3786 if (CFStringFindWithOptions(url->_string, PATH_DELIM_AS_STRING_FOR_TYPE(pathType), pathRg, kCFCompareBackwards, &componentRg)) {
3787 componentRg.location ++;
3788 componentRg.length = pathRg.location + pathRg.length - componentRg.location;
3789 } else {
3790 componentRg = pathRg;
3791 }
3792 return componentRg;
3793 }
3794
3795 CFStringRef CFURLCopyLastPathComponent(CFURLRef url) {
3796 CFStringRef result;
3797
3798 if (CF_IS_OBJC(__kCFURLTypeID, url)) {
3799 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
3800 CFIndex length;
3801 CFRange rg, compRg;
3802 if (!path) return NULL;
3803 rg = CFRangeMake(0, CFStringGetLength(path));
3804 length = rg.length; // Remember this for comparison later
3805 if (CFStringGetCharacterAtIndex(path, rg.length - 1) == '/') {
3806 rg.length --;
3807 }
3808 if (CFStringFindWithOptions(path, CFSTR("/"), rg, kCFCompareBackwards, &compRg)) {
3809 rg.length = rg.location + rg.length - (compRg.location+1);
3810 rg.location = compRg.location + 1;
3811 }
3812 if (rg.location == 0 && rg.length == length) {
3813 result = path;
3814 } else {
3815 result = CFStringCreateWithSubstring(NULL, path, rg);
3816 CFRelease(path);
3817 }
3818 } else {
3819 CFRange rg = _rangeOfLastPathComponent(url);
3820 if (rg.location == kCFNotFound || rg.length == 0) {
3821 // No path
3822 return CFRetain(CFSTR(""));
3823 }
3824 if (rg.length == 1 && CFStringGetCharacterAtIndex(url->_string, rg.location) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url))) {
3825 return CFRetain(CFSTR("/"));
3826 }
3827 result = CFStringCreateWithSubstring(CFGetAllocator(url), url->_string, rg);
3828 if (URL_PATH_TYPE(url) == FULL_URL_REPRESENTATION && !(url->_flags & POSIX_AND_URL_PATHS_MATCH)) {
3829 CFStringRef tmp;
3830 if (url->_flags & IS_OLD_UTF8_STYLE || url->_encoding == kCFStringEncodingUTF8) {
3831 tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url), result, CFSTR(""));
3832 } else {
3833 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url), result, CFSTR(""), url->_encoding);
3834 }
3835 CFRelease(result);
3836 result = tmp;
3837 }
3838 }
3839 return result;
3840 }
3841
3842 CFStringRef CFURLCopyPathExtension(CFURLRef url) {
3843 CFStringRef lastPathComp = CFURLCopyLastPathComponent(url);
3844 CFStringRef ext = NULL;
3845
3846 if (lastPathComp) {
3847 CFRange rg = CFStringFind(lastPathComp, CFSTR("."), kCFCompareBackwards);
3848 if (rg.location != kCFNotFound) {
3849 rg.location ++;
3850 rg.length = CFStringGetLength(lastPathComp) - rg.location;
3851 if (rg.length > 0) {
3852 ext = CFStringCreateWithSubstring(CFGetAllocator(url), lastPathComp, rg);
3853 } else {
3854 ext = CFRetain(CFSTR(""));
3855 }
3856 }
3857 CFRelease(lastPathComp);
3858 }
3859 return ext;
3860 }
3861
3862 CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRef url, CFStringRef pathComponent, Boolean isDirectory) {
3863 UInt32 fsType;
3864 CFURLRef result;
3865 url = _CFURLFromNSURL(url);
3866 __CFGenericValidateType(url, __kCFURLTypeID);
3867 CFAssert1(pathComponent != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__);
3868
3869 fsType = URL_PATH_TYPE(url);
3870 if (fsType != FULL_URL_REPRESENTATION && CFStringFindWithOptions(pathComponent, PATH_DELIM_AS_STRING_FOR_TYPE(fsType), CFRangeMake(0, CFStringGetLength(pathComponent)), 0, NULL)) {
3871 // Must convert to full representation, and then work with it
3872 fsType = FULL_URL_REPRESENTATION;
3873 _convertToURLRepresentation((struct __CFURL *)url);
3874 }
3875
3876 if (fsType == FULL_URL_REPRESENTATION) {
3877 CFMutableStringRef newString;
3878 CFStringRef newComp;
3879 CFRange pathRg;
3880 if (!(url->_flags & IS_PARSED)) _parseComponentsOfURL(url);
3881 if (!(url->_flags & HAS_PATH)) return NULL;
3882
3883 newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
3884 newComp = CFURLCreateStringByAddingPercentEscapes(allocator, pathComponent, NULL, CFSTR(";?"), (url->_flags & IS_OLD_UTF8_STYLE) ? kCFStringEncodingUTF8 : url->_encoding);
3885 pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH);
3886 if (!pathRg.length || CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') {
3887 CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/"));
3888 pathRg.length ++;
3889 }
3890 CFStringInsert(newString, pathRg.location + pathRg.length, newComp);
3891 if (isDirectory) {
3892 CFStringInsert(newString, pathRg.location + pathRg.length + CFStringGetLength(newComp), CFSTR("/"));
3893 }
3894 CFRelease(newComp);
3895 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
3896 CFRelease(newString);
3897 } else {
3898 UniChar pathDelim = PATH_DELIM_FOR_TYPE(fsType);
3899 CFStringRef newString;
3900 if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) != pathDelim) {
3901 if (isDirectory) {
3902 newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c%@%c"), url->_string, pathDelim, pathComponent, pathDelim);
3903 } else {
3904 newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c%@"), url->_string, pathDelim, pathComponent);
3905 }
3906 } else {
3907 if (isDirectory) {
3908 newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%@%c"), url->_string, pathComponent, pathDelim);
3909 } else {
3910 newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%@"), url->_string, pathComponent);
3911 }
3912 }
3913 result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, fsType, isDirectory, url->_base);
3914 CFRelease(newString);
3915 }
3916 return result;
3917 }
3918
3919 CFURLRef CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator, CFURLRef url) {
3920 CFURLRef result;
3921 CFMutableStringRef newString;
3922 CFRange lastCompRg, pathRg;
3923 Boolean appendDotDot = false;
3924 UInt32 fsType;
3925
3926 url = _CFURLFromNSURL(url);
3927 CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
3928 __CFGenericValidateType(url, __kCFURLTypeID);
3929
3930 fsType = URL_PATH_TYPE(url);
3931 if (fsType == FULL_URL_REPRESENTATION) {
3932 if (!(url->_flags & IS_PARSED)) _parseComponentsOfURL(url);
3933 if (!(url->_flags & HAS_PATH)) return NULL;
3934 pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH);
3935 } else {
3936 pathRg = CFRangeMake(0, CFStringGetLength(url->_string));
3937 }
3938 lastCompRg = _rangeOfLastPathComponent(url);
3939 if (lastCompRg.length == 0) {
3940 appendDotDot = true;
3941 } else if (lastCompRg.length == 1) {
3942 UniChar ch = CFStringGetCharacterAtIndex(url->_string, lastCompRg.location);
3943 if (ch == '.' || ch == PATH_DELIM_FOR_TYPE(fsType)) {
3944 appendDotDot = true;
3945 }
3946 } else if (lastCompRg.length == 2 && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location) == '.' && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location+1) == '.') {
3947 appendDotDot = true;
3948 }
3949
3950 newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
3951 if (appendDotDot) {
3952 CFIndex delta = 0;
3953 if (pathRg.length > 0 && CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != PATH_DELIM_FOR_TYPE(fsType)) {
3954 CFStringInsert(newString, pathRg.location + pathRg.length, PATH_DELIM_AS_STRING_FOR_TYPE(fsType));
3955 delta ++;
3956 }
3957 CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR(".."));
3958 delta += 2;
3959 CFStringInsert(newString, pathRg.location + pathRg.length + delta, PATH_DELIM_AS_STRING_FOR_TYPE(fsType));
3960 delta ++;
3961 // We know we have "/../" at the end of the path; we wish to know if that's immediately preceded by "/." (but that "/." doesn't start the string), in which case we want to delete the "/.".
3962 if (pathRg.length + delta > 4 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 5) == '.') {
3963 if (pathRg.length+delta > 7 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 6) == PATH_DELIM_FOR_TYPE(fsType)) {
3964 CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 6, 2));
3965 } else if (pathRg.length+delta == 5) {
3966 CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 5, 2));
3967 }
3968 }
3969 } else if (lastCompRg.location == pathRg.location) {
3970 CFStringReplace(newString, pathRg, CFSTR("."));
3971 CFStringInsert(newString, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType));
3972 } else {
3973 CFStringDelete(newString, CFRangeMake(lastCompRg.location, pathRg.location + pathRg.length - lastCompRg.location));
3974 }
3975 if (fsType == FULL_URL_REPRESENTATION) {
3976 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
3977 } else {
3978 result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, fsType, true, url->_base);
3979 }
3980 CFRelease(newString);
3981 return result;
3982 }
3983
3984 CFURLRef CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator, CFURLRef url, CFStringRef extension) {
3985 CFMutableStringRef newString;
3986 CFURLRef result;
3987 CFRange rg;
3988 CFURLPathStyle fsType;
3989
3990 CFAssert1(url != NULL && extension != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
3991 url = _CFURLFromNSURL(url);
3992 __CFGenericValidateType(url, __kCFURLTypeID);
3993 __CFGenericValidateType(extension, CFStringGetTypeID());
3994
3995 rg = _rangeOfLastPathComponent(url);
3996 if (rg.location < 0) return NULL; // No path
3997 fsType = URL_PATH_TYPE(url);
3998 if (fsType != FULL_URL_REPRESENTATION && CFStringFindWithOptions(extension, PATH_DELIM_AS_STRING_FOR_TYPE(fsType), CFRangeMake(0, CFStringGetLength(extension)), 0, NULL)) {
3999 _convertToURLRepresentation((struct __CFURL *)url);
4000 fsType = FULL_URL_REPRESENTATION;
4001 rg = _rangeOfLastPathComponent(url);
4002 }
4003
4004 newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4005 CFStringInsert(newString, rg.location + rg.length, CFSTR("."));
4006 if (fsType == FULL_URL_REPRESENTATION) {
4007 CFStringRef newExt = CFURLCreateStringByAddingPercentEscapes(allocator, extension, NULL, CFSTR(";?/"), (url->_flags & IS_OLD_UTF8_STYLE) ? kCFStringEncodingUTF8 : url->_encoding);
4008 CFStringInsert(newString, rg.location + rg.length + 1, newExt);
4009 CFRelease(newExt);
4010 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4011 } else {
4012 CFStringInsert(newString, rg.location + rg.length + 1, extension);
4013 result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, fsType, (url->_flags & IS_DIRECTORY) != 0 ? true : false, url->_base);
4014 }
4015 CFRelease(newString);
4016 return result;
4017 }
4018
4019 CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef url) {
4020 CFRange rg, dotRg;
4021 CFURLRef result;
4022
4023 CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
4024 url = _CFURLFromNSURL(url);
4025 __CFGenericValidateType(url, __kCFURLTypeID);
4026 rg = _rangeOfLastPathComponent(url);
4027 if (rg.location < 0) {
4028 result = NULL;
4029 } else if (rg.length && CFStringFindWithOptions(url->_string, CFSTR("."), rg, kCFCompareBackwards, &dotRg)) {
4030 CFMutableStringRef newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4031 dotRg.length = rg.location + rg.length - dotRg.location;
4032 CFStringDelete(newString, dotRg);
4033 if (URL_PATH_TYPE(url) == FULL_URL_REPRESENTATION) {
4034 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4035 } else {
4036 result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, URL_PATH_TYPE(url), (url->_flags & IS_DIRECTORY) != 0 ? true : false, url->_base);
4037 }
4038 CFRelease(newString);
4039 } else {
4040 result = CFRetain(url);
4041 }
4042 return result;
4043 }
4044
4045 // We deal in FSRefs because they handle Unicode strings.
4046 // FSSpecs handle a much more limited set of characters.
4047 static Boolean __CFFSRefForVolumeName(CFStringRef volName, FSRef *spec, CFAllocatorRef alloc) {
4048 return false;
4049 }
4050
4051 static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) {
4052 CFArrayRef components = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR(":"));
4053 CFMutableArrayRef newComponents = CFArrayCreateMutableCopy(alloc, 0, components);
4054 Boolean doSpecialLeadingColon = false;
4055 UniChar firstChar = CFStringGetCharacterAtIndex(path, 0);
4056 UInt32 i, cnt;
4057 CFRelease(components);
4058
4059
4060 if (!doSpecialLeadingColon && firstChar != ':') {
4061 CFArrayInsertValueAtIndex(newComponents, 0, CFSTR(""));
4062 } else if (firstChar != ':') {
4063 // see what we need to add at the beginning. Under MacOS, if the
4064 // first character isn't a ':', then the first component is the
4065 // volume name, and we need to find the mount point. Bleah. If we
4066 // don't find a mount point, we're going to have to lie, and make something up.
4067 CFStringRef firstComp = CFArrayGetValueAtIndex(newComponents, 0);
4068 if (CFStringGetLength(firstComp) == 1 && CFStringGetCharacterAtIndex(firstComp, 0) == '/') {
4069 // "/" is the "magic" path for a UFS root directory
4070 CFArrayRemoveValueAtIndex(newComponents, 0);
4071 CFArrayInsertValueAtIndex(newComponents, 0, CFSTR(""));
4072 } else {
4073 // See if we can get a mount point.
4074 Boolean foundMountPoint = false;
4075 uint8_t buf[CFMaxPathLength];
4076 FSRef volSpec;
4077 // Now produce an FSSpec from the volume, then try and get the mount point
4078 if (__CFFSRefForVolumeName(firstComp, &volSpec, alloc) && (__CFCarbonCore_FSRefMakePath(&volSpec, buf, CFMaxPathLength) == noErr)) {
4079 // We win! Ladies and gentlemen, we have a mount point.
4080 if (buf[0] == '/' && buf[1] == '\0') {
4081 // Special case this common case
4082 foundMountPoint = true;
4083 CFArrayRemoveValueAtIndex(newComponents, 0);
4084 CFArrayInsertValueAtIndex(newComponents, 0, CFSTR(""));
4085 } else {
4086 // This is pretty inefficient; we can do better.
4087 CFStringRef mountPoint = CFStringCreateWithCString(alloc, buf, CFStringFileSystemEncoding());
4088 CFArrayRef mountComponents = mountPoint ? CFStringCreateArrayBySeparatingStrings(alloc, mountPoint, CFSTR("/")) : NULL;
4089 if (mountComponents) {
4090 CFIndex idx = CFArrayGetCount(mountComponents) - 1;
4091 CFArrayRemoveValueAtIndex(newComponents, 0);
4092 for ( ; idx >= 0; idx --) {
4093 CFArrayInsertValueAtIndex(newComponents, 0, CFArrayGetValueAtIndex(mountComponents, idx));
4094 }
4095 CFRelease(mountComponents);
4096 foundMountPoint = true;
4097 }
4098 if (mountPoint) CFRelease(mountPoint);
4099 }
4100 }
4101 if (!foundMountPoint) {
4102 // Fall back to treating the volume name as the top level directory
4103 CFArrayInsertValueAtIndex(newComponents, 0, CFSTR(""));
4104 }
4105 }
4106 } else {
4107 CFArrayRemoveValueAtIndex(newComponents, 0);
4108 }
4109
4110 cnt = CFArrayGetCount(newComponents);
4111 for (i = 0; i < cnt; i ++) {
4112 CFStringRef comp = CFArrayGetValueAtIndex(newComponents, i);
4113 CFStringRef newComp = NULL;
4114 CFRange searchRg, slashRg;
4115 searchRg.location = 0;
4116 searchRg.length = CFStringGetLength(comp);
4117 while (CFStringFindWithOptions(comp, CFSTR("/"), searchRg, 0, &slashRg)) {
4118 if (!newComp) {
4119 newComp = CFStringCreateMutableCopy(alloc, searchRg.location + searchRg.length, comp);
4120 }
4121 CFStringReplace((CFMutableStringRef)newComp, slashRg, CFSTR(":"));
4122 searchRg.length = searchRg.location + searchRg.length - slashRg.location - 1;
4123 searchRg.location = slashRg.location + 1;
4124 }
4125 if (newComp) {
4126 CFArraySetValueAtIndex(newComponents, i, newComp);
4127 CFRelease(newComp);
4128 }
4129 }
4130 if (isDir && CFStringGetLength(CFArrayGetValueAtIndex(newComponents, cnt-1)) != 0) {
4131 CFArrayAppendValue(newComponents, CFSTR(""));
4132 }
4133 return newComponents;
4134 }
4135
4136 static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) {
4137 CFArrayRef components = HFSPathToURLComponents(path, alloc, isDir);
4138 CFArrayRef newComponents = components ? copyStringArrayWithTransformation(components, escapePathComponent) : NULL;
4139 CFIndex cnt;
4140 CFStringRef result;
4141 if (components) CFRelease(components);
4142 if (!newComponents) return NULL;
4143
4144 cnt = CFArrayGetCount(newComponents);
4145 if (cnt == 1 && CFStringGetLength(CFArrayGetValueAtIndex(newComponents, 0)) == 0) {
4146 result = CFRetain(CFSTR("/"));
4147 } else {
4148 result = CFStringCreateByCombiningStrings(alloc, newComponents, CFSTR("/"));
4149 }
4150 CFRelease(newComponents);
4151 return result;
4152 }
4153
4154
4155 static CFStringRef URLPathToHFSPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) {
4156 return NULL;
4157 }
4158
4159