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