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