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