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