1/*
2 * Copyright (c) 2014 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
62static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir);
63static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute);
64#endif
65static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute);
66static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute);
67static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory, Boolean isAbsolute, Boolean *posixAndUrlPathsMatch);
68static CFStringRef CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, Boolean isDirectory, Boolean isAbsolute, Boolean windowsPath, Boolean *addedPercentEncoding);
69CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase);
70CF_EXPORT CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator);
71#if DEPLOYMENT_TARGET_MACOSX
72static 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__
80static CONST_STRING_DECL(kCFURLHTTPScheme, "http")
81static CONST_STRING_DECL(kCFURLHTTPSScheme, "https")
82static CONST_STRING_DECL(kCFURLFileScheme, "file")
83static CONST_STRING_DECL(kCFURLDataScheme, "data")
84static CONST_STRING_DECL(kCFURLFTPScheme, "ftp")
85static CONST_STRING_DECL(kCFURLLocalhost, "localhost")
86#else
87CONST_STRING_DECL(kCFURLHTTPScheme, "http")
88CONST_STRING_DECL(kCFURLHTTPSScheme, "https")
89CONST_STRING_DECL(kCFURLFileScheme, "file")
90CONST_STRING_DECL(kCFURLDataScheme, "data")
91CONST_STRING_DECL(kCFURLFTPScheme, "ftp")
92CONST_STRING_DECL(kCFURLLocalhost, "localhost")
93#endif
94
95#if DEBUG_URL_MEMORY_USAGE
96static uint numURLs = 0;                    // number of URLs allocated
97static uint numDealloced = 0;               // number of URLs deallocated
98static uint numFileURLsParsed = 0;          // number of URLs created from a string which had to be parsed
99static uint numExtraDataAllocated = 0;      // number of URLs with additional data -- either because URLHandle was used, or because a sanitizedString was needed
100static uint numURLsWithBaseURL = 0;         // number of URLs with a baseURL
101static 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
126enum {
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
136CF_INLINE UInt32 _getSchemeTypeFromFlags(UInt32 flags);
137CF_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://"
169static 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"
173static const UInt8 fileURLPrefixWithAuthority[] = FILE_PREFIX_WITH_AUTHORITY;
174
175static 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.
184struct _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
190struct __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
202CF_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
212CF_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
222CF_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
232CF_INLINE void* _getResourceInfo ( const struct __CFURL* url )
233{
234    if ( url ) {
235        return url->_resourceInfo;
236    }
237    else {
238        return NULL;
239    }
240}
241
242static 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
259CF_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
272CF_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
291CF_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
306CF_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
315CF_INLINE UInt32 _getSchemeTypeFromFlags(UInt32 flags)
316{
317    return ( (flags & SCHEME_TYPE_MASK) >> SCHEME_SHIFT );
318}
319
320CF_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
326static Boolean _pathHasFileIDPrefix(CFStringRef path);
327static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc);
328static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef base, UInt32 *flags, CFRange **range);
329static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag);
330static CFRange _netLocationRange(UInt32 flags, CFRange *ranges);
331static UInt32 _firstResourceSpecifierFlag(UInt32 flags);
332static void computeSanitizedString(CFURLRef url);
333static CFStringRef correctedComponent(CFStringRef component, UInt32 compFlag, CFStringEncoding enc);
334static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges);
335static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc);
336
337
338CF_INLINE void _parseComponentsOfURL(CFURLRef url) {
339    _parseComponents(CFGetAllocator(url), url->_string, url->_base, &(((struct __CFURL *)url)->_flags), &(((struct __CFURL *)url)->_ranges));
340}
341
342enum {
343	VALID = 1,
344	ALPHA = 2,
345	PATHVALID = 4,
346	SCHEME = 8,
347	HEXDIGIT = 16
348};
349
350static 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
481CF_INLINE Boolean isURLLegalCharacter(UniChar ch) {
482    return (ch <= 127) ? ((sURLValidCharacters[ch] & VALID) != 0) : false;
483}
484
485CF_INLINE Boolean scheme_valid(UniChar ch) {
486    return (ch <= 127) ? ((sURLValidCharacters[ch] & SCHEME) != 0) : false;
487}
488
489CF_INLINE Boolean isALPHA(UniChar ch) {
490    return (ch <= 127) ? ((sURLValidCharacters[ch] & ALPHA) != 0) : false;
491}
492
493CF_INLINE Boolean isPathLegalCharacter(UniChar ch) {
494    return (ch <= 127) ? ((sURLValidCharacters[ch] & PATHVALID) != 0) : false;
495}
496
497CF_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
502CF_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
518CF_INLINE Boolean _haveTestedOriginalString(CFURLRef url) {
519    return ((url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) != 0) || (_getSanitizedString(url) != NULL);
520}
521
522enum {
523    IS_PCHAR = 0x01,
524};
525
526static 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
785CF_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 */
804static 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.
922CF_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
931static 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
964static 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.
1075CFStringRef  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
1207CF_EXPORT
1208CFStringRef 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
1323static 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.
1332CF_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
1442static 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
1473static 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
1489static 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
1512static 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
1556static 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
1592extern __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
1597static 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
1614static CFTypeID __kCFURLTypeID = _kCFRuntimeNotATypeID;
1615
1616static 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
1630CF_PRIVATE void __CFURLInitialize(void) {
1631    __kCFURLTypeID = _CFRuntimeRegisterClass(&__CFURLClass);
1632}
1633
1634/* Toll-free bridging support; get the true CFURL from an NSURL */
1635CF_INLINE CFURLRef _CFURLFromNSURL(CFURLRef url) {
1636    CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID, CFURLRef, (NSURL *)url, _cfurl);
1637    return url;
1638}
1639
1640CFTypeID CFURLGetTypeID(void) {
1641    return __kCFURLTypeID;
1642}
1643
1644CF_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/***************************************************/
1670static 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
1722static 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
1727static 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
1732static 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
1754static 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
1759static 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
1764static 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
1773static 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
1831static 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
1864CF_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.
1886static 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
1905static 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 */
1996CF_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 */
2052CF_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 */
2213CF_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    if ( bufLen > 0 ) {
2224        CFAllocatorRef alloc = CFGetAllocator(uninitializedURL);
2225#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
2226        struct __CFURL *url = (struct __CFURL *)uninitializedURL;
2227        Boolean isAbsolute = bufLen && (*buffer == '/');
2228        Boolean addedPercentEncoding;
2229        Boolean releaseBaseURL = false;
2230
2231        if ( isAbsolute ) {
2232            // if buffer contains an absolute path, ignore baseURL (if provided)
2233            baseURL = NULL;
2234        }
2235        else if ( baseURL == NULL ) {
2236            // if buffer contains a relative path and no baseURL is provided, use the current working directory
2237            baseURL = _CFURLCreateCurrentDirectoryURL(CFGetAllocator(uninitializedURL));
2238            releaseBaseURL = true;
2239        }
2240        CFStringRef urlString = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(alloc, buffer, bufLen, isDirectory, isAbsolute, false /*windowsPath*/, &addedPercentEncoding);
2241        if ( urlString ) {
2242            _CFURLInit(url, urlString, baseURL, FALSE);
2243
2244            // hard coded parse
2245            if ( isAbsolute ) {
2246                if ( AddAuthorityToFileURL() ) {
2247                    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;
2248                    _setSchemeTypeInFlags(&url->_flags, kHasFileScheme);
2249                    url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 3, 0);
2250                    url->_ranges[0] = CFRangeMake(0, 4); // scheme "file"
2251                    url->_ranges[1] = CFRangeMake(7, 9); // host "localhost"
2252                    url->_ranges[2] = CFRangeMake(16, CFStringGetLength(urlString)- 16);
2253                }
2254                else {
2255                    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;
2256                    _setSchemeTypeInFlags(&url->_flags, kHasFileScheme);
2257                    url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 2, 0);
2258                    url->_ranges[0] = CFRangeMake(0, 4); // scheme "file"
2259                    url->_ranges[1] = CFRangeMake(7, CFStringGetLength(urlString)- 7);
2260                }
2261            } else {
2262                url->_flags = (addedPercentEncoding ? 0 : POSIX_AND_URL_PATHS_MATCH ) | (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH;
2263                url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange), 0);
2264                *(url->_ranges) = CFRangeMake(0, CFStringGetLength(url->_string));
2265            }
2266            if ( releaseBaseURL && baseURL ) {
2267                CFRelease(baseURL);
2268            }
2269            CFRelease(urlString);
2270            result = true;
2271        }
2272#elif DEPLOYMENT_TARGET_WINDOWS
2273        CFStringRef filePath = CFStringCreateWithBytes(alloc, buffer, bufLen, CFStringFileSystemEncoding(), false);
2274        if ( filePath ) {
2275            result = _CFURLInitWithFileSystemPath(uninitializedURL, filePath, kCFURLWindowsPathStyle, isDirectory, baseURL);
2276            CFRelease(filePath);
2277        }
2278#endif
2279    }
2280#if DEBUG_URL_INITIALIZER_LOGGING
2281    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);
2282    if ( input_baseURL ) {
2283        CFRelease(input_baseURL);
2284    }
2285#endif
2286    return ( result );
2287}
2288
2289// encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes.
2290CFURLRef CFURLCreateWithBytes(CFAllocatorRef allocator, const uint8_t *URLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL) {
2291    CFStringRef  urlString = CFStringCreateWithBytes(allocator, URLBytes, length, encoding, false);
2292    CFURLRef result = NULL;
2293    if ( urlString ) {
2294        if ( baseURL || CFStringGetLength(urlString) != 0 ) {
2295            result = _CFURLAlloc(allocator);
2296            if (result) {
2297                if ( !_CFURLInitWithURLString(result, urlString, false /* checkForLegalCharacters */, baseURL) ) {
2298                    CFRelease(result);
2299                    result = NULL;
2300                }
2301                else {
2302                    if (encoding != kCFStringEncodingUTF8) {
2303                        ((struct __CFURL *)result)->_encoding = encoding;
2304#if DEBUG_URL_MEMORY_USAGE
2305                        numNonUTF8EncodedURLs++;
2306#endif
2307                    }
2308                }
2309            }
2310        }
2311        CFRelease(urlString); // it's retained by result, now.
2312    }
2313    return ( result );
2314}
2315
2316CFDataRef CFURLCreateData(CFAllocatorRef allocator, CFURLRef  url, CFStringEncoding encoding, Boolean escapeWhitespace) {
2317    CFDataRef result = NULL;
2318    if ( url ) {
2319        CFStringRef myStr = CFURLGetString(url);
2320        if ( myStr ) {
2321            result = CFStringCreateExternalRepresentation(allocator, myStr, encoding, 0);
2322        }
2323    }
2324    return result;
2325}
2326
2327// Any escape sequences in URLString will be interpreted via UTF-8.
2328CFURLRef CFURLCreateWithString(CFAllocatorRef allocator, CFStringRef  URLString, CFURLRef  baseURL) {
2329    CFURLRef url = NULL;
2330    if ( URLString && (baseURL || CFStringGetLength(URLString) != 0) ) {
2331        url = _CFURLAlloc(allocator);
2332        if (url) {
2333            if ( !_CFURLInitWithURLString(url, URLString, true /* checkForLegalCharacters */, baseURL) ) {
2334                CFRelease(url);
2335                url = NULL;
2336            }
2337        }
2338    }
2339    return ( url );
2340}
2341
2342static CFURLRef _CFURLCreateWithArbitraryString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) {
2343    CFURLRef url = NULL;
2344    if ( URLString && (baseURL || CFStringGetLength(URLString) != 0) ) {
2345        url = _CFURLAlloc(allocator);
2346        if (url) {
2347            if ( !_CFURLInitWithURLString(url, URLString, false /* checkForLegalCharacters */, baseURL) ) {
2348                CFRelease(url);
2349                url = NULL;
2350            }
2351        }
2352    }
2353    return ( url );
2354}
2355
2356CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *relativeURLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL, Boolean useCompatibilityMode) {
2357    CFURLRef result = NULL;
2358
2359    /*
2360     CFURLCreateAbsoluteURLWithBytes() and useCompatibilityMode is for:
2361         <rdar://problem/2711611> Problem with '|' in url and calling CFURLCreateWithString()
2362         <rdar://problem/3085893> CFURL resolves "../" URLs at top level in a way that is not the same as web browsers
2363         <rdar://problem/3085920> CFURL API should be more helpful for accepting URLs with technically-bad characters
2364         <rdar://problem/3205656> Safari needs CFURL to deal with google.com URLs that end in "%"
2365         <rdar://problem/3219233> Safari needs CFURL to not remove path when relative URL is just a query string
2366         <rdar://problem/3219240> Safari needs CFURL to support "compatibility" resolution of partial URLs with schemes
2367
2368     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.
2369
2370     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
2371     */
2372
2373    // if not useCompatibilityMode, use CFURLCreateWithBytes and then CFURLCopyAbsoluteURL if there's a baseURL
2374    if ( !useCompatibilityMode ) {
2375        CFURLRef url = CFURLCreateWithBytes(alloc, relativeURLBytes, length, encoding, baseURL);
2376        if ( url != NULL ) {
2377            if ( baseURL != NULL ) {
2378                result = CFURLCopyAbsoluteURL(url);
2379                CFRelease(url);
2380            } else {
2381                result = url;
2382            }
2383        }
2384    } else {
2385        UInt32 absFlags = 0;
2386        CFRange *absRanges;
2387        CFStringRef absString = NULL;
2388        Boolean absStringIsMutable = false;
2389        CFURLRef absURL;
2390        CFStringRef relativeString;
2391
2392        relativeString = CFStringCreateWithBytes(alloc, relativeURLBytes, length, encoding, false);
2393        if ( relativeString != NULL ) {
2394            if (!baseURL) {
2395                absString = relativeString;
2396            } else {
2397                UniChar ch = 0;
2398                if ( CFStringGetLength(relativeString) > 0 ) {
2399                    ch = CFStringGetCharacterAtIndex(relativeString, 0);
2400                }
2401                if (ch == '?' || ch == ';' || ch == '#') {
2402                    // Nothing but parameter + query + fragment; append to the baseURL string
2403                    CFStringRef baseString;
2404                    if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) {
2405                        baseString = CFURLGetString(baseURL);
2406                    } else {
2407                        baseString = baseURL->_string;
2408                    }
2409                    absString = CFStringCreateMutable(alloc, CFStringGetLength(baseString) + CFStringGetLength(relativeString));
2410                    CFStringAppend((CFMutableStringRef)absString, baseString);
2411                    CFStringAppend((CFMutableStringRef)absString, relativeString);
2412                    absStringIsMutable = true;
2413                } else {
2414                    UInt32 relFlags = 0;
2415                    CFRange *relRanges;
2416                    CFStringRef relString = NULL;
2417                    _parseComponents(alloc, relativeString, baseURL, &relFlags, &relRanges);
2418                    if (relFlags & HAS_SCHEME) {
2419                        CFStringRef baseScheme = CFURLCopyScheme(baseURL);
2420                        CFRange relSchemeRange = _rangeForComponent(relFlags, relRanges, HAS_SCHEME);
2421                        if (baseScheme && CFStringGetLength(baseScheme) == relSchemeRange.length && CFStringHasPrefix(relativeString, baseScheme)) {
2422                            relString = CFStringCreateWithSubstring(alloc, relativeString, CFRangeMake(relSchemeRange.length+1, CFStringGetLength(relativeString) - relSchemeRange.length - 1));
2423                            CFAllocatorDeallocate(alloc, relRanges);
2424                            relFlags = 0;
2425                            _parseComponents(alloc, relString, baseURL, &relFlags, &relRanges);
2426                        } else {
2427                            // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match
2428                            CFRetain(relativeString);
2429                            absString = relativeString;
2430                        }
2431                        if (baseScheme) CFRelease(baseScheme);
2432                    } else {
2433                        CFRetain(relativeString);
2434                        relString = relativeString;
2435                    }
2436                    if (!absString) {
2437                        if (!CF_IS_OBJC(__kCFURLTypeID, baseURL)) {
2438                            absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseURL->_string, baseURL->_flags, baseURL->_ranges);
2439                        } else {
2440                            CFStringRef baseString;
2441                            UInt32 baseFlags = 0;
2442                            CFRange *baseRanges;
2443                            if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) {
2444                                baseString = CFURLGetString(baseURL);
2445                            } else {
2446                                baseString = baseURL->_string;
2447                            }
2448                            _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges);
2449                            absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges);
2450                            CFAllocatorDeallocate(alloc, baseRanges);
2451                        }
2452                        absStringIsMutable = true;
2453                    }
2454                    if (relString) CFRelease(relString);
2455                    CFAllocatorDeallocate(alloc, relRanges);
2456                }
2457                CFRelease(relativeString);
2458            }
2459        }
2460        if ( absString ) {
2461            _parseComponents(alloc, absString, NULL, &absFlags, &absRanges);
2462            if (absFlags & HAS_PATH) {
2463                CFRange pathRg = _rangeForComponent(absFlags, absRanges, HAS_PATH);
2464                // This is expensive, but it allows us to reuse _resolvedPath.  It should be cleaned up to get this allocation removed at some point. - REW
2465                UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (pathRg.length + 1), 0);
2466                CFStringRef newPath;
2467                CFStringGetCharacters(absString, pathRg, buf);
2468                buf[pathRg.length] = '\0';
2469                newPath = _resolvedPath(buf, buf + pathRg.length, '/', true, false, alloc);
2470                if (CFStringGetLength(newPath) != pathRg.length) {
2471                    if (!absStringIsMutable) {
2472                        CFStringRef tmp = CFStringCreateMutableCopy(alloc, CFStringGetLength(absString), absString);
2473                        CFRelease(absString);
2474                        absString = tmp;
2475                    }
2476                    CFStringReplace((CFMutableStringRef)absString, pathRg, newPath);
2477                }
2478                CFRelease(newPath);
2479                // Do not deallocate buf; newPath took ownership of it.
2480            }
2481            CFAllocatorDeallocate(alloc, absRanges);
2482            absURL = _CFURLCreateWithArbitraryString(alloc, absString, NULL);
2483            CFRelease(absString);
2484            if (absURL) {
2485                ((struct __CFURL *)absURL)->_encoding = encoding;
2486#if DEBUG_URL_MEMORY_USAGE
2487                if ( encoding != kCFStringEncodingUTF8 ) {
2488                    numNonUTF8EncodedURLs++;
2489                }
2490#endif
2491            }
2492            result = absURL;
2493        }
2494    }
2495
2496    return ( result );
2497}
2498
2499/* 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 */
2500static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc) {
2501    UniChar *idx = pathStr;
2502    while (idx < end) {
2503        if (*idx == '.') {
2504            if (idx+1 == end) {
2505                if (idx != pathStr) {
2506                    *idx = '\0';
2507                    end = idx;
2508                }
2509                break;
2510            } else if (*(idx+1) == pathDelimiter) {
2511                if (idx + 2 != end || idx != pathStr) {
2512                    memmove(idx, idx+2, (end-(idx+2)+1) * sizeof(UniChar));
2513                    end -= 2;
2514                    continue;
2515                } else {
2516                    // Do not delete the sole path component
2517                    break;
2518                }
2519            } else if (( end-idx >= 2 ) &&  *(idx+1) == '.' && (idx+2 == end || (( end-idx > 2 ) && *(idx+2) == pathDelimiter))) {
2520                if (idx - pathStr >= 2) {
2521                    // 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.
2522                    UniChar *lastDelim = idx-2;
2523                    while (lastDelim >= pathStr && *lastDelim != pathDelimiter) lastDelim --;
2524                    lastDelim ++;
2525                    if (lastDelim != idx && (idx-lastDelim != 3 || *lastDelim != '.' || *(lastDelim +1) != '.')) {
2526                        // We have a genuine component to compact out
2527                        if (idx+2 != end) {
2528                            unsigned numCharsToMove = end - (idx+3) + 1; // +1 to move the '\0' as well
2529                            memmove(lastDelim, idx+3, numCharsToMove * sizeof(UniChar));
2530                            end -= (idx + 3 - lastDelim);
2531                            idx = lastDelim;
2532                            continue;
2533                        } else if (lastDelim != pathStr) {
2534                            *lastDelim = '\0';
2535                            end = lastDelim;
2536                            break;
2537                        } else {
2538                            // Don't allow the path string to devolve to the empty string.  Fall back to "." instead. - REW
2539                            pathStr[0] = '.';
2540                            pathStr[1] = '/';
2541                            pathStr[2] = '\0';
2542                            end = &pathStr[3];
2543                            break;
2544                        }
2545                    }
2546                } else if (stripLeadingDotDots) {
2547                    if (idx + 3 != end) {
2548                        unsigned numCharsToMove = end - (idx + 3) + 1;
2549                        memmove(idx, idx+3, numCharsToMove * sizeof(UniChar));
2550                        end -= 3;
2551                        continue;
2552                    } else {
2553                        // Do not devolve the last path component
2554                        break;
2555                    }
2556                }
2557            }
2558        }
2559		while (idx < end && *idx != pathDelimiter) idx ++;
2560        idx ++;
2561    }
2562    if (stripTrailingDelimiter && end > pathStr && end-1 != pathStr && *(end-1) == pathDelimiter) {
2563        end --;
2564    }
2565    // return an zero-length string if end < pathStr
2566    return CFStringCreateWithCharactersNoCopy(alloc, pathStr, end >= pathStr ? end - pathStr : 0, alloc);
2567}
2568
2569static CFMutableStringRef resolveAbsoluteURLStringBuffer(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges, UniChar *buf)
2570{
2571    CFStringAppendBuffer appendBuffer;
2572    UniChar chars[2];
2573    CFStringInitAppendBuffer(alloc, &appendBuffer);
2574    CFRange rg;
2575
2576    rg = _rangeForComponent(baseFlags, baseRanges, HAS_SCHEME);
2577    if (rg.location != kCFNotFound) {
2578        CFStringGetCharacters(baseString, rg, buf);
2579        CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2580        chars[0] = ':';
2581        CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2582    }
2583
2584    if (relFlags & NET_LOCATION_MASK) {
2585        CFStringAppendStringToAppendBuffer(&appendBuffer, relString);
2586    } else {
2587        chars[0] = '/';
2588        chars[1] = '/';
2589        CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 2);
2590        rg = _netLocationRange(baseFlags, baseRanges);
2591        if (rg.location != kCFNotFound) {
2592            CFStringGetCharacters(baseString, rg, buf);
2593            CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2594        }
2595
2596        if (relFlags & HAS_PATH) {
2597            CFRange relPathRg = _rangeForComponent(relFlags, relRanges, HAS_PATH);
2598            CFRange basePathRg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH);
2599            CFStringRef newPath;
2600            Boolean useRelPath = false;
2601            Boolean useBasePath = false;
2602            if (basePathRg.location == kCFNotFound) {
2603                useRelPath = true;
2604            } else if (relPathRg.length == 0) {
2605                useBasePath = true;
2606            } else if (CFStringGetCharacterAtIndex(relString, relPathRg.location) == '/') {
2607                useRelPath = true;
2608            } else if (basePathRg.location == kCFNotFound || basePathRg.length == 0) {
2609                useRelPath = true;
2610            }
2611            if (useRelPath) {
2612                newPath = CFStringCreateWithSubstring(alloc, relString, relPathRg);
2613            } else if (useBasePath) {
2614                newPath = CFStringCreateWithSubstring(alloc, baseString, basePathRg);
2615            } else {
2616                // FIXME: Get rid of this allocation
2617                UniChar *newPathBuf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (relPathRg.length + basePathRg.length + 1), 0);
2618                UniChar *idx, *end;
2619                CFStringGetCharacters(baseString, basePathRg, newPathBuf);
2620                idx = newPathBuf + basePathRg.length - 1;
2621                while (idx != newPathBuf && *idx != '/') idx --;
2622                if (*idx == '/') idx ++;
2623                CFStringGetCharacters(relString, relPathRg, idx);
2624                end = idx + relPathRg.length;
2625                *end = 0;
2626                newPath = _resolvedPath(newPathBuf, end, '/', false, false, alloc);
2627            }
2628            /* Under Win32 absolute path can begin with letter
2629             * so we have to add one '/' to the newString
2630             * (Sergey Zubarev)
2631             */
2632            // No - the input strings here are URL path strings, not Win32 paths.
2633            // Absolute paths should have had a '/' prepended before this point.
2634            // I have removed Sergey Zubarev's change and left his comment (and
2635            // this one) as a record. - REW, 1/5/2004
2636
2637            // if the relative URL does not begin with a slash and
2638            // the base does not end with a slash, add a slash
2639            if ((basePathRg.location == kCFNotFound || basePathRg.length == 0) && CFStringGetCharacterAtIndex(newPath, 0) != '/') {
2640                chars[0] = '/';
2641                CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2642            }
2643
2644            CFStringAppendStringToAppendBuffer(&appendBuffer, newPath);
2645            CFRelease(newPath);
2646            rg.location = relPathRg.location + relPathRg.length;
2647            rg.length = CFStringGetLength(relString);
2648            if (rg.length > rg.location) {
2649                rg.length -= rg.location;
2650                CFStringGetCharacters(relString, rg, buf);
2651                CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2652            }
2653        } else {
2654            rg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH);
2655            if (rg.location != kCFNotFound) {
2656                CFStringGetCharacters(baseString, rg, buf);
2657                CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2658            }
2659
2660            if (!(relFlags & RESOURCE_SPECIFIER_MASK)) {
2661                // ???  Can this ever happen?
2662                UInt32 rsrcFlag = _firstResourceSpecifierFlag(baseFlags);
2663                if (rsrcFlag) {
2664                    rg.location = _rangeForComponent(baseFlags, baseRanges, rsrcFlag).location;
2665                    rg.length = CFStringGetLength(baseString) - rg.location;
2666                    rg.location --; // To pick up the separator
2667                    rg.length ++;
2668                    CFStringGetCharacters(baseString, rg, buf);
2669                    CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2670                }
2671            } else if (relFlags & HAS_PARAMETERS) {
2672                rg = _rangeForComponent(relFlags, relRanges, HAS_PARAMETERS);
2673                rg.location --; // To get the semicolon that starts the parameters
2674                rg.length = CFStringGetLength(relString) - rg.location;
2675                CFStringGetCharacters(relString, rg, buf);
2676                CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2677            } else {
2678                // Sigh; we have to resolve these against one another
2679                rg = _rangeForComponent(baseFlags, baseRanges, HAS_PARAMETERS);
2680                if (rg.location != kCFNotFound) {
2681                    chars[0] = ';';
2682                    CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2683                    CFStringGetCharacters(baseString, rg, buf);
2684                    CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2685                }
2686                rg = _rangeForComponent(relFlags, relRanges, HAS_QUERY);
2687                if (rg.location != kCFNotFound) {
2688                    chars[0] = '?';
2689                    CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2690                    CFStringGetCharacters(relString, rg, buf);
2691                    CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2692                } else {
2693                    rg = _rangeForComponent(baseFlags, baseRanges, HAS_QUERY);
2694                    if (rg.location != kCFNotFound) {
2695                        chars[0] = '?';
2696                        CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2697                        CFStringGetCharacters(baseString, rg, buf);
2698                        CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2699                    }
2700                }
2701                // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL?
2702                rg = _rangeForComponent(relFlags, relRanges, HAS_FRAGMENT);
2703                if (rg.location != kCFNotFound) {
2704                    chars[0] = '#';
2705                    CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1);
2706                    CFStringGetCharacters(relString, rg, buf);
2707                    CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length);
2708                }
2709            }
2710        }
2711    }
2712    return CFStringCreateMutableWithAppendBuffer(&appendBuffer);
2713}
2714
2715static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges) {
2716    CFMutableStringRef result;
2717    CFIndex bufLen = CFStringGetLength(baseString) + CFStringGetLength(relString); // Overkill, but guarantees we never allocate again
2718    if ( bufLen <= 1024 ) {
2719        STACK_BUFFER_DECL(UniChar, buf, bufLen);
2720        result = resolveAbsoluteURLStringBuffer(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges, buf);
2721        return ( result );
2722    }
2723    else {
2724        UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, bufLen * sizeof(UniChar), 0);
2725        result = resolveAbsoluteURLStringBuffer(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges, buf);
2726        CFAllocatorDeallocate(alloc, buf);
2727        return ( result );
2728    }
2729}
2730
2731CFURLRef CFURLCopyAbsoluteURL(CFURLRef  relativeURL) {
2732    CFURLRef  anURL, base;
2733    CFAllocatorRef alloc = CFGetAllocator(relativeURL);
2734    CFStringRef baseString, newString;
2735    UInt32 baseFlags;
2736    CFRange *baseRanges;
2737    Boolean baseIsObjC;
2738
2739    CFAssert1(relativeURL != NULL, __kCFLogAssertion, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__);
2740    if (CF_IS_OBJC(__kCFURLTypeID, relativeURL)) {
2741        anURL = (CFURLRef) CF_OBJC_CALLV((NSURL *)relativeURL, absoluteURL);
2742        if (anURL) CFRetain(anURL);
2743        return anURL;
2744    }
2745
2746    __CFGenericValidateType(relativeURL, __kCFURLTypeID);
2747
2748    base = relativeURL->_base;
2749    if (!base) {
2750        return (CFURLRef)CFRetain(relativeURL);
2751    }
2752    baseIsObjC = CF_IS_OBJC(__kCFURLTypeID, base);
2753
2754    if (!baseIsObjC) {
2755        baseString = base->_string;
2756        baseFlags = base->_flags;
2757        baseRanges = base->_ranges;
2758    } else {
2759        baseString = CFURLGetString(base);
2760        baseFlags = 0;
2761        baseRanges = NULL;
2762        _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges);
2763    }
2764
2765    newString = resolveAbsoluteURLString(alloc, relativeURL->_string, relativeURL->_flags, relativeURL->_ranges, baseString, baseFlags, baseRanges);
2766    if (baseIsObjC) {
2767        CFAllocatorDeallocate(alloc, baseRanges);
2768    }
2769    anURL = _CFURLCreateWithArbitraryString(alloc, newString, NULL);
2770    CFRelease(newString);
2771    ((struct __CFURL *)anURL)->_encoding = relativeURL->_encoding;
2772#if DEBUG_URL_MEMORY_USAGE
2773    if ( relativeURL->_encoding != kCFStringEncodingUTF8 ) {
2774	numNonUTF8EncodedURLs++;
2775    }
2776#endif
2777    return anURL;
2778}
2779
2780
2781/*******************/
2782/* Basic accessors */
2783/*******************/
2784CFStringEncoding _CFURLGetEncoding(CFURLRef url) {
2785    return url->_encoding;
2786}
2787
2788Boolean CFURLCanBeDecomposed(CFURLRef  anURL) {
2789    anURL = _CFURLFromNSURL(anURL);
2790    return ((anURL->_flags & IS_DECOMPOSABLE) != 0);
2791}
2792
2793CFStringRef  CFURLGetString(CFURLRef  url) {
2794    CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID, CFStringRef, (NSURL *)url, relativeString);
2795    if (!_haveTestedOriginalString(url)) {
2796        computeSanitizedString(url);
2797    }
2798    if (url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) {
2799        return url->_string;
2800    } else {
2801        return _getSanitizedString( url );
2802    }
2803}
2804
2805CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) {
2806    CFIndex length, charsConverted, usedLength;
2807    CFStringRef string;
2808    CFStringEncoding enc;
2809    if (CF_IS_OBJC(__kCFURLTypeID, url)) {
2810        string = CFURLGetString(url);
2811        enc = kCFStringEncodingUTF8;
2812    } else {
2813        string = url->_string;
2814        enc = url->_encoding;
2815    }
2816    length = CFStringGetLength(string);
2817    charsConverted = CFStringGetBytes(string, CFRangeMake(0, length), enc, 0, false, buffer, bufferLength, &usedLength);
2818    if (charsConverted != length) {
2819        return -1;
2820    } else {
2821        return usedLength;
2822    }
2823}
2824
2825CFURLRef  CFURLGetBaseURL(CFURLRef  anURL) {
2826    CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID, CFURLRef, (NSURL *)anURL, baseURL);
2827    return anURL->_base;
2828}
2829
2830// Assumes the URL is already parsed
2831static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag) {
2832    UInt32 idx = 0;
2833    if (!(flags & compFlag)) return CFRangeMake(kCFNotFound, 0);
2834    while (!(compFlag & 1)) {
2835        compFlag = compFlag >> 1;
2836        if (flags & 1) {
2837            idx ++;
2838        }
2839        flags = flags >> 1;
2840    }
2841    return ranges[idx];
2842}
2843
2844static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boolean fromOriginalString, Boolean removePercentEscapes) {
2845    CFRange rg;
2846    CFStringRef comp;
2847    CFAllocatorRef alloc = CFGetAllocator(url);
2848    if (removePercentEscapes) {
2849        fromOriginalString = true;
2850    }
2851    rg = _rangeForComponent(url->_flags, url->_ranges, compFlag);
2852    if (rg.location == kCFNotFound) {
2853        comp = NULL;
2854    }
2855    else {
2856        if ( compFlag & HAS_SCHEME ) {
2857            switch ( _getSchemeTypeFromFlags(url->_flags) ) {
2858                case kHasHttpScheme:
2859                    comp = (CFStringRef)CFRetain(kCFURLHTTPScheme);
2860                    break;
2861
2862                case kHasHttpsScheme:
2863                    comp = (CFStringRef)CFRetain(kCFURLHTTPSScheme);
2864                    break;
2865
2866                case kHasFileScheme:
2867                    comp = (CFStringRef)CFRetain(kCFURLFileScheme);
2868                    break;
2869
2870                case kHasDataScheme:
2871                    comp = (CFStringRef)CFRetain(kCFURLDataScheme);
2872                    break;
2873
2874                case kHasFtpScheme:
2875                    comp = (CFStringRef)CFRetain(kCFURLFTPScheme);
2876                    break;
2877
2878                default:
2879                    comp = CFStringCreateWithSubstring(alloc, url->_string, rg);
2880                    break;
2881            }
2882        }
2883        else {
2884            comp = CFStringCreateWithSubstring(alloc, url->_string, rg);
2885        }
2886
2887        if (comp && !fromOriginalString) {
2888            if (!_haveTestedOriginalString(url)) {
2889                computeSanitizedString(url);
2890            }
2891            if (!(url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (_getAdditionalDataFlags(url) & compFlag)) {
2892                CFStringRef newComp = correctedComponent(comp, compFlag, url->_encoding);
2893                CFRelease(comp);
2894                comp = newComp;
2895            }
2896        }
2897        if (comp && removePercentEscapes) {
2898            CFStringRef tmp;
2899            if (url->_encoding == kCFStringEncodingUTF8) {
2900                tmp = CFURLCreateStringByReplacingPercentEscapes(alloc, comp, CFSTR(""));
2901            } else {
2902                tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc, comp, CFSTR(""), url->_encoding);
2903            }
2904            CFRelease(comp);
2905            comp = tmp;
2906        }
2907
2908    }
2909    return comp;
2910}
2911
2912CFStringRef  CFURLCopyScheme(CFURLRef  anURL) {
2913    CFStringRef scheme;
2914    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
2915        scheme = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, scheme);
2916        if ( scheme ) {
2917            CFRetain(scheme);
2918        }
2919    }
2920    else {
2921        switch ( _getSchemeTypeFromFlags(anURL->_flags) ) {
2922            case kHasHttpScheme:
2923                scheme = (CFStringRef)CFRetain(kCFURLHTTPScheme);
2924                break;
2925
2926            case kHasHttpsScheme:
2927                scheme = (CFStringRef)CFRetain(kCFURLHTTPSScheme);
2928                break;
2929
2930            case kHasFileScheme:
2931                scheme = (CFStringRef)CFRetain(kCFURLFileScheme);
2932                break;
2933
2934            case kHasDataScheme:
2935                scheme = (CFStringRef)CFRetain(kCFURLDataScheme);
2936                break;
2937
2938            case kHasFtpScheme:
2939                scheme = (CFStringRef)CFRetain(kCFURLFTPScheme);
2940                break;
2941
2942            default:
2943                scheme = _retainedComponentString(anURL, HAS_SCHEME, true, false);
2944                if ( !scheme ) {
2945                    if (anURL->_base) {
2946                        scheme = CFURLCopyScheme(anURL->_base);
2947                    } else {
2948                        scheme = NULL;
2949                    }
2950                }
2951                break;
2952        }
2953    }
2954    return ( scheme );
2955}
2956
2957static CFRange _netLocationRange(UInt32 flags, CFRange *ranges) {
2958    CFRange netRgs[4];
2959    CFRange netRg = {kCFNotFound, 0};
2960    CFIndex i, c = 4;
2961
2962    if ((flags & NET_LOCATION_MASK) == 0) return CFRangeMake(kCFNotFound, 0);
2963
2964    netRgs[0] = _rangeForComponent(flags, ranges, HAS_USER);
2965    netRgs[1] = _rangeForComponent(flags, ranges, HAS_PASSWORD);
2966    netRgs[2] = _rangeForComponent(flags, ranges, HAS_HOST);
2967    netRgs[3] = _rangeForComponent(flags, ranges, HAS_PORT);
2968    for (i = 0; i < c; i ++) {
2969        if (netRgs[i].location == kCFNotFound) continue;
2970        if (netRg.location == kCFNotFound) {
2971            netRg = netRgs[i];
2972        } else {
2973            netRg.length = netRgs[i].location + netRgs[i].length - netRg.location;
2974        }
2975    }
2976    return netRg;
2977}
2978
2979CFStringRef CFURLCopyNetLocation(CFURLRef  anURL) {
2980    anURL = _CFURLFromNSURL(anURL);
2981    if (anURL->_flags & NET_LOCATION_MASK) {
2982        // We provide the net location
2983        CFRange netRg = _netLocationRange(anURL->_flags, anURL->_ranges);
2984        CFStringRef netLoc;
2985        if (!_haveTestedOriginalString(anURL)) {
2986            computeSanitizedString(anURL);
2987        }
2988        if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (_getAdditionalDataFlags(anURL) & (USER_DIFFERS | PASSWORD_DIFFERS | HOST_DIFFERS | PORT_DIFFERS))) {
2989            // 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.
2990            CFRange netLocEnd;
2991            CFStringRef sanitizedString = _getSanitizedString(anURL);
2992            netRg.length = CFStringGetLength(sanitizedString) - netRg.location;
2993            if (CFStringFindWithOptions(sanitizedString, CFSTR("/"), netRg, 0, &netLocEnd)) {
2994                netRg.length = netLocEnd.location - netRg.location;
2995            }
2996            netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, netRg);
2997        } else {
2998            netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, netRg);
2999        }
3000        return netLoc;
3001    } else if (anURL->_base) {
3002        return CFURLCopyNetLocation(anURL->_base);
3003    } else {
3004        return NULL;
3005    }
3006}
3007
3008// 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.
3009CFStringRef  CFURLCopyPath(CFURLRef  anURL) {
3010    anURL = _CFURLFromNSURL(anURL);
3011    return _retainedComponentString(anURL, HAS_PATH, false, false);
3012}
3013
3014/* 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.
3015
3016  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.
3017*/
3018CFStringRef CFURLCopyStrictPath(CFURLRef anURL, Boolean *isAbsolute) {
3019    CFStringRef path = CFURLCopyPath(anURL);
3020    if (!path || CFStringGetLength(path) == 0) {
3021        if (path) CFRelease(path);
3022        if (isAbsolute) *isAbsolute = false;
3023        return NULL;
3024    }
3025    if (CFStringGetCharacterAtIndex(path, 0) == '/') {
3026        CFStringRef tmp;
3027        if (isAbsolute) *isAbsolute = true;
3028        tmp = CFStringCreateWithSubstring(CFGetAllocator(path), path, CFRangeMake(1, CFStringGetLength(path)-1));
3029        CFRelease(path);
3030        path = tmp;
3031    } else {
3032        if (isAbsolute) *isAbsolute = false;
3033    }
3034    return path;
3035}
3036
3037Boolean CFURLHasDirectoryPath(CFURLRef  anURL) {
3038    __CFGenericValidateType(anURL, __kCFURLTypeID);
3039    if (!anURL->_base || (anURL->_flags & (HAS_PATH | NET_LOCATION_MASK))) {
3040        return ((anURL->_flags & IS_DIRECTORY) != 0);
3041    }
3042    else {
3043        return CFURLHasDirectoryPath(anURL->_base);
3044    }
3045}
3046
3047static UInt32 _firstResourceSpecifierFlag(UInt32 flags) {
3048    UInt32 firstRsrcSpecFlag = 0;
3049    UInt32 flag = HAS_FRAGMENT;
3050    while (flag != HAS_PATH) {
3051        if (flags & flag) {
3052            firstRsrcSpecFlag = flag;
3053        }
3054        flag = flag >> 1;
3055    }
3056    return firstRsrcSpecFlag;
3057}
3058
3059CFStringRef  CFURLCopyResourceSpecifier(CFURLRef  anURL) {
3060    anURL = _CFURLFromNSURL(anURL);
3061    __CFGenericValidateType(anURL, __kCFURLTypeID);
3062    if (!(anURL->_flags & IS_DECOMPOSABLE)) {
3063        CFRange schemeRg = _rangeForComponent(anURL->_flags, anURL->_ranges, HAS_SCHEME);
3064        CFIndex base = schemeRg.location + schemeRg.length + 1;
3065        if (!_haveTestedOriginalString(anURL)) {
3066            computeSanitizedString(anURL);
3067        }
3068
3069        CFStringRef sanitizedString = _getSanitizedString(anURL);
3070
3071        if (sanitizedString) {
3072            // 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.
3073            return CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, CFRangeMake(base, CFStringGetLength(sanitizedString)-base));
3074        } else {
3075            return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, CFRangeMake(base, CFStringGetLength(anURL->_string)-base));
3076        }
3077    } else {
3078        UInt32 firstRsrcSpecFlag = _firstResourceSpecifierFlag(anURL->_flags);
3079        UInt32 flag;
3080        if (firstRsrcSpecFlag) {
3081            Boolean canUseOriginalString = true;
3082            Boolean canUseSanitizedString = true;
3083            CFAllocatorRef alloc = CFGetAllocator(anURL);
3084            if (!_haveTestedOriginalString(anURL)) {
3085                computeSanitizedString(anURL);
3086            }
3087
3088            UInt32 additionalDataFlags = _getAdditionalDataFlags(anURL);
3089            CFStringRef sanitizedString = _getSanitizedString(anURL);
3090
3091            if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH)) {
3092                // See if any pieces in the resource specifier differ between sanitized string and original string
3093                for (flag = firstRsrcSpecFlag; flag != (HAS_FRAGMENT << 1); flag = flag << 1) {
3094                    if (additionalDataFlags & flag) {
3095                        canUseOriginalString = false;
3096                        break;
3097                    }
3098                }
3099            }
3100            if (!canUseOriginalString) {
3101                // 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.
3102                for (flag = firstRsrcSpecFlag >> 1; flag != 0; flag = flag >> 1) {
3103                    if (additionalDataFlags & flag) {
3104                        canUseSanitizedString = false;
3105                        break;
3106                    }
3107                }
3108            }
3109            if (canUseOriginalString) {
3110                CFRange rg = _rangeForComponent(anURL->_flags, anURL->_ranges, firstRsrcSpecFlag);
3111                rg.location --; // Include the character that demarcates the component
3112                rg.length = CFStringGetLength(anURL->_string) - rg.location;
3113                return CFStringCreateWithSubstring(alloc, anURL->_string, rg);
3114            } else if (canUseSanitizedString) {
3115                CFRange rg = _rangeForComponent(anURL->_flags, anURL->_ranges, firstRsrcSpecFlag);
3116                rg.location --; // Include the character that demarcates the component
3117                rg.length = CFStringGetLength(sanitizedString) - rg.location;
3118                return CFStringCreateWithSubstring(alloc, sanitizedString, rg);
3119            } else {
3120                // Must compute the correct string to return; just reparse....
3121                UInt32 sanFlags = 0;
3122                CFRange *sanRanges = NULL;
3123                CFRange rg;
3124                _parseComponents(alloc, sanitizedString, anURL->_base, &sanFlags, &sanRanges);
3125                rg = _rangeForComponent(sanFlags, sanRanges, firstRsrcSpecFlag);
3126                CFAllocatorDeallocate(alloc, sanRanges);
3127                rg.location --; // Include the character that demarcates the component
3128                rg.length = CFStringGetLength(sanitizedString) - rg.location;
3129                return CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, rg);
3130            }
3131        } else {
3132            // The resource specifier cannot possibly come from the base.
3133            return NULL;
3134        }
3135    }
3136}
3137
3138/*************************************/
3139/* Accessors that create new objects */
3140/*************************************/
3141
3142// 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).
3143CFStringRef  CFURLCopyHostName(CFURLRef  anURL) {
3144    CFStringRef tmp;
3145    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3146        tmp = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, host);
3147        if (tmp) CFRetain(tmp);
3148        return tmp;
3149    }
3150    __CFGenericValidateType(anURL, __kCFURLTypeID);
3151    tmp = _retainedComponentString(anURL, HAS_HOST, true, true);
3152    if (tmp) {
3153        if (anURL->_flags & IS_IPV6_ENCODED) {
3154            // Have to strip off the brackets to get the true hostname.
3155            // Assume that to be legal the first and last characters are brackets!
3156            CFStringRef	strippedHost = CFStringCreateWithSubstring(CFGetAllocator(anURL), tmp, CFRangeMake(1, CFStringGetLength(tmp) - 2));
3157            CFRelease(tmp);
3158            tmp = strippedHost;
3159        }
3160        return tmp;
3161    } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3162        return CFURLCopyHostName(anURL->_base);
3163    } else {
3164        return NULL;
3165    }
3166}
3167
3168// Return -1 to indicate no port is specified
3169SInt32 CFURLGetPortNumber(CFURLRef  anURL) {
3170    CFStringRef port;
3171    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3172        CFNumberRef cfPort = (CFNumberRef) CF_OBJC_CALLV((NSURL *)anURL, port);
3173        SInt32 num;
3174        if (cfPort && CFNumberGetValue(cfPort, kCFNumberSInt32Type, &num)) return num;
3175        return -1;
3176    }
3177    __CFGenericValidateType(anURL, __kCFURLTypeID);
3178    port = _retainedComponentString(anURL, HAS_PORT, true, false);
3179    if (port) {
3180        SInt32 portNum, idx, length = CFStringGetLength(port);
3181        CFStringInlineBuffer buf;
3182        CFStringInitInlineBuffer(port, &buf, CFRangeMake(0, length));
3183        idx = 0;
3184        if (!__CFStringScanInteger(&buf, NULL, &idx, false, &portNum) || (idx != length)) {
3185            portNum = -1;
3186        }
3187        CFRelease(port);
3188        return portNum;
3189    } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3190        return CFURLGetPortNumber(anURL->_base);
3191    } else {
3192        return -1;
3193    }
3194}
3195
3196CFStringRef  CFURLCopyUserName(CFURLRef  anURL) {
3197    CFStringRef user;
3198    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3199        user = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, user);
3200        if (user) CFRetain(user);
3201        return user;
3202    }
3203    __CFGenericValidateType(anURL, __kCFURLTypeID);
3204    user = _retainedComponentString(anURL, HAS_USER, true, true);
3205    if (user) {
3206        return user;
3207    } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3208        return CFURLCopyUserName(anURL->_base);
3209    } else {
3210        return NULL;
3211    }
3212}
3213
3214CFStringRef  CFURLCopyPassword(CFURLRef  anURL) {
3215    CFStringRef passwd;
3216    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3217        passwd = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, password);
3218        if (passwd) CFRetain(passwd);
3219        return passwd;
3220    }
3221    __CFGenericValidateType(anURL, __kCFURLTypeID);
3222    passwd = _retainedComponentString(anURL, HAS_PASSWORD, true, true);
3223    if (passwd) {
3224        return passwd;
3225    } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) {
3226        return CFURLCopyPassword(anURL->_base);
3227    } else {
3228        return NULL;
3229    }
3230}
3231
3232// 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
3233
3234static CFStringRef  _unescapedParameterString(CFURLRef  anURL) {
3235    CFStringRef str;
3236    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3237        str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, parameterString);
3238        if (str) CFRetain(str);
3239        return str;
3240    }
3241    __CFGenericValidateType(anURL, __kCFURLTypeID);
3242    str = _retainedComponentString(anURL, HAS_PARAMETERS, false, false);
3243    if (str) return str;
3244    if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL;
3245    if (!anURL->_base || (anURL->_flags & (NET_LOCATION_MASK | HAS_PATH | HAS_SCHEME))) {
3246        return NULL;
3247        // Parameter string definitely coming from the relative portion of the URL
3248    }
3249    return _unescapedParameterString( anURL->_base);
3250}
3251
3252CFStringRef  CFURLCopyParameterString(CFURLRef  anURL, CFStringRef charactersToLeaveEscaped) {
3253    CFStringRef  param = _unescapedParameterString(anURL);
3254    if (param) {
3255        CFStringRef result;
3256        if (anURL->_encoding == kCFStringEncodingUTF8) {
3257            result = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), param, charactersToLeaveEscaped);
3258        } else {
3259            result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), param, charactersToLeaveEscaped, anURL->_encoding);
3260        }
3261        CFRelease(param);
3262        return result;
3263    }
3264    return NULL;
3265}
3266
3267static CFStringRef  _unescapedQueryString(CFURLRef  anURL) {
3268    CFStringRef str;
3269    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3270        str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, query);
3271        if (str) CFRetain(str);
3272        return str;
3273    }
3274    __CFGenericValidateType(anURL, __kCFURLTypeID);
3275    str = _retainedComponentString(anURL, HAS_QUERY, false, false);
3276    if (str) return str;
3277    if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL;
3278    if (!anURL->_base || (anURL->_flags & (HAS_SCHEME | NET_LOCATION_MASK | HAS_PATH | HAS_PARAMETERS))) {
3279        return NULL;
3280    }
3281    return _unescapedQueryString(anURL->_base);
3282}
3283
3284CFStringRef  CFURLCopyQueryString(CFURLRef  anURL, CFStringRef  charactersToLeaveEscaped) {
3285    CFStringRef  query = _unescapedQueryString(anURL);
3286    if (query) {
3287        CFStringRef tmp;
3288        if (anURL->_encoding == kCFStringEncodingUTF8) {
3289            tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), query, charactersToLeaveEscaped);
3290        } else {
3291            tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), query, charactersToLeaveEscaped, anURL->_encoding);
3292        }
3293        CFRelease(query);
3294        return tmp;
3295    }
3296    return NULL;
3297}
3298
3299// Fragments are NEVER taken from a base URL
3300static CFStringRef  _unescapedFragment(CFURLRef  anURL) {
3301    CFStringRef str;
3302    if (CF_IS_OBJC(__kCFURLTypeID, anURL)) {
3303        str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, fragment);
3304        if (str) CFRetain(str);
3305        return str;
3306    }
3307    __CFGenericValidateType(anURL, __kCFURLTypeID);
3308    str = _retainedComponentString(anURL, HAS_FRAGMENT, false, false);
3309    return str;
3310}
3311
3312CFStringRef  CFURLCopyFragment(CFURLRef  anURL, CFStringRef  charactersToLeaveEscaped) {
3313    CFStringRef  fragment = _unescapedFragment(anURL);
3314    if (fragment) {
3315        CFStringRef tmp;
3316        if (anURL->_encoding == kCFStringEncodingUTF8) {
3317            tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped);
3318        } else {
3319            tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped, anURL->_encoding);
3320        }
3321        CFRelease(fragment);
3322        return tmp;
3323    }
3324    return NULL;
3325}
3326
3327static CFIndex insertionLocationForMask(CFURLRef url, CFOptionFlags mask) {
3328    CFIndex firstMaskFlag = 1;
3329    CFIndex lastComponentBeforeMask = 0;
3330    while (firstMaskFlag <= HAS_FRAGMENT) {
3331        if (firstMaskFlag & mask) break;
3332        if (url->_flags & firstMaskFlag) lastComponentBeforeMask = firstMaskFlag;
3333        firstMaskFlag = firstMaskFlag << 1;
3334    }
3335    if (lastComponentBeforeMask == 0) {
3336        // mask includes HAS_SCHEME
3337        return 0;
3338    } else if (lastComponentBeforeMask == HAS_SCHEME) {
3339        // Do not have to worry about the non-decomposable case here.  However, we must be prepared for the degenerate
3340        // case file:/path/immediately/without/host
3341        CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME);
3342        CFRange pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
3343        if (schemeRg.length + 1 == pathRg.location) {
3344            return schemeRg.length + 1;
3345        } else {
3346            return schemeRg.length + 3;
3347        }
3348    } else {
3349        // For all other components, the separator precedes the component, so there's no need
3350        // to add extra chars to get to the next insertion point
3351        CFRange rg = _rangeForComponent(url->_flags, url->_ranges, lastComponentBeforeMask);
3352        return rg.location + rg.length;
3353    }
3354}
3355
3356static CFRange _CFURLGetCharRangeForMask(CFURLRef url, CFOptionFlags mask, CFRange *charRangeWithSeparators) {
3357    CFOptionFlags currentOption;
3358    CFOptionFlags firstMaskFlag = HAS_SCHEME;
3359    Boolean haveReachedMask = false;
3360    CFIndex beforeMask = 0;
3361    CFIndex afterMask = kCFNotFound;
3362    CFRange *currRange = url->_ranges;
3363    CFRange maskRange = {kCFNotFound, 0};
3364    for (currentOption = 1; currentOption <= HAS_FRAGMENT; currentOption = currentOption << 1) {
3365        if (!haveReachedMask && (currentOption & mask) != 0) {
3366            firstMaskFlag = currentOption;
3367            haveReachedMask = true;
3368        }
3369        if (!(url->_flags & currentOption)) continue;
3370        if (!haveReachedMask) {
3371            beforeMask = currRange->location + currRange->length;
3372        } else if (currentOption <= mask) {
3373            if (maskRange.location == kCFNotFound) {
3374                maskRange = *currRange;
3375            } else {
3376                maskRange.length = currRange->location + currRange->length - maskRange.location;
3377            }
3378        } else {
3379            afterMask = currRange->location;
3380            break;
3381        }
3382        currRange ++;
3383    }
3384    if (afterMask == kCFNotFound) {
3385        afterMask = maskRange.location + maskRange.length;
3386    }
3387    charRangeWithSeparators->location = beforeMask;
3388    charRangeWithSeparators->length = afterMask - beforeMask;
3389    return maskRange;
3390}
3391
3392static CFRange _getCharRangeInDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
3393    CFOptionFlags mask;
3394    switch (component) {
3395        case kCFURLComponentScheme:
3396            mask = HAS_SCHEME;
3397            break;
3398        case kCFURLComponentNetLocation:
3399            mask = NET_LOCATION_MASK;
3400            break;
3401        case kCFURLComponentPath:
3402            mask = HAS_PATH;
3403            break;
3404        case kCFURLComponentResourceSpecifier:
3405            mask = RESOURCE_SPECIFIER_MASK;
3406            break;
3407        case kCFURLComponentUser:
3408            mask = HAS_USER;
3409            break;
3410        case kCFURLComponentPassword:
3411            mask = HAS_PASSWORD;
3412            break;
3413        case kCFURLComponentUserInfo:
3414            mask = HAS_USER | HAS_PASSWORD;
3415            break;
3416        case kCFURLComponentHost:
3417            mask = HAS_HOST;
3418            break;
3419        case kCFURLComponentPort:
3420            mask = HAS_PORT;
3421            break;
3422        case kCFURLComponentParameterString:
3423            mask = HAS_PARAMETERS;
3424            break;
3425        case kCFURLComponentQuery:
3426            mask = HAS_QUERY;
3427            break;
3428        case kCFURLComponentFragment:
3429            mask = HAS_FRAGMENT;
3430            break;
3431        default:
3432            rangeIncludingSeparators->location = kCFNotFound;
3433            rangeIncludingSeparators->length = 0;
3434            return CFRangeMake(kCFNotFound, 0);
3435    }
3436
3437    if ((url->_flags & mask) == 0) {
3438        rangeIncludingSeparators->location = insertionLocationForMask(url, mask);
3439        rangeIncludingSeparators->length = 0;
3440        return CFRangeMake(kCFNotFound, 0);
3441    } else {
3442        return _CFURLGetCharRangeForMask(url, mask, rangeIncludingSeparators);
3443    }
3444}
3445
3446static CFRange _getCharRangeInNonDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
3447    if (component == kCFURLComponentScheme) {
3448        CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME);
3449        rangeIncludingSeparators->location = 0;
3450        rangeIncludingSeparators->length = schemeRg.length + 1;
3451        return schemeRg;
3452    } else if (component == kCFURLComponentResourceSpecifier) {
3453        CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME);
3454        CFIndex stringLength = CFStringGetLength(url->_string);
3455        if (schemeRg.length + 1 == stringLength) {
3456            rangeIncludingSeparators->location = schemeRg.length + 1;
3457            rangeIncludingSeparators->length = 0;
3458            return CFRangeMake(kCFNotFound, 0);
3459        } else {
3460            rangeIncludingSeparators->location = schemeRg.length;
3461            rangeIncludingSeparators->length = stringLength - schemeRg.length;
3462            return CFRangeMake(schemeRg.length + 1, rangeIncludingSeparators->length - 1);
3463        }
3464    } else {
3465        rangeIncludingSeparators->location = kCFNotFound;
3466        rangeIncludingSeparators->length = 0;
3467        return CFRangeMake(kCFNotFound, 0);
3468    }
3469
3470}
3471
3472CFRange CFURLGetByteRangeForComponent(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) {
3473    CFRange charRange, charRangeWithSeparators;
3474    CFRange byteRange;
3475    CFAssert2(component > 0 && component < 13, __kCFLogAssertion, "%s(): passed invalid component %d", __PRETTY_FUNCTION__, component);
3476    url = _CFURLFromNSURL(url);
3477
3478    if (!(url->_flags & IS_DECOMPOSABLE)) {
3479        // Special-case this because non-decomposable URLs have a slightly strange flags setup
3480        charRange = _getCharRangeInNonDecomposableURL(url, component, &charRangeWithSeparators);
3481    } else {
3482        charRange = _getCharRangeInDecomposableURL(url, component, &charRangeWithSeparators);
3483    }
3484
3485    if (charRangeWithSeparators.location == kCFNotFound) {
3486        if (rangeIncludingSeparators) {
3487            rangeIncludingSeparators->location = kCFNotFound;
3488            rangeIncludingSeparators->length = 0;
3489        }
3490        return CFRangeMake(kCFNotFound, 0);
3491    } else if (rangeIncludingSeparators) {
3492        CFStringGetBytes(url->_string, CFRangeMake(0, charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->location));
3493
3494        if (charRange.location == kCFNotFound) {
3495            byteRange = charRange;
3496            CFStringGetBytes(url->_string, charRangeWithSeparators, url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->length));
3497        } else {
3498            CFIndex maxCharRange = charRange.location + charRange.length;
3499            CFIndex maxCharRangeWithSeparators = charRangeWithSeparators.location + charRangeWithSeparators.length;
3500
3501            if (charRangeWithSeparators.location == charRange.location) {
3502                byteRange.location = rangeIncludingSeparators->location;
3503            } else {
3504                CFIndex numBytes;
3505                CFStringGetBytes(url->_string, CFRangeMake(charRangeWithSeparators.location, charRange.location - charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &numBytes);
3506                byteRange.location = charRangeWithSeparators.location + numBytes;
3507            }
3508            CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length));
3509            if (maxCharRangeWithSeparators == maxCharRange) {
3510                rangeIncludingSeparators->length = byteRange.location + byteRange.length - rangeIncludingSeparators->location;
3511            } else {
3512                CFIndex numBytes;
3513                CFRange rg;
3514                rg.location = maxCharRange;
3515                rg.length = maxCharRangeWithSeparators - rg.location;
3516                CFStringGetBytes(url->_string, rg, url->_encoding, 0, false, NULL, 0, &numBytes);
3517                rangeIncludingSeparators->length = byteRange.location + byteRange.length + numBytes - rangeIncludingSeparators->location;
3518            }
3519        }
3520    } else if (charRange.location == kCFNotFound) {
3521        byteRange = charRange;
3522    } else {
3523        CFStringGetBytes(url->_string, CFRangeMake(0, charRange.location), url->_encoding, 0, false, NULL, 0, &(byteRange.location));
3524        CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length));
3525    }
3526    return byteRange;
3527}
3528
3529/* Component support */
3530
3531static Boolean decomposeToNonHierarchical(CFURLRef url, CFURLComponentsNonHierarchical *components) {
3532    if ( CFURLGetBaseURL(url) != NULL)  {
3533        components->scheme = NULL;
3534    } else {
3535        components->scheme = CFURLCopyScheme(url);
3536    }
3537    components->schemeSpecific = CFURLCopyResourceSpecifier(url);
3538    return true;
3539}
3540
3541static CFURLRef composeFromNonHierarchical(CFAllocatorRef alloc, const CFURLComponentsNonHierarchical *components) {
3542    CFStringRef str;
3543    if (components->scheme) {
3544        UniChar ch = ':';
3545        str = CFStringCreateMutableCopy(alloc, CFStringGetLength(components->scheme) + 1 + (components->schemeSpecific ? CFStringGetLength(components->schemeSpecific): 0), components->scheme);
3546        CFStringAppendCharacters((CFMutableStringRef)str, &ch, 1);
3547        if (components->schemeSpecific) CFStringAppend((CFMutableStringRef)str, components->schemeSpecific);
3548    } else if (components->schemeSpecific) {
3549        str = components->schemeSpecific;
3550        CFRetain(str);
3551    } else {
3552        str = NULL;
3553    }
3554    if (str) {
3555        CFURLRef url = CFURLCreateWithString(alloc, str, NULL);
3556        CFRelease(str);
3557        return url;
3558    } else {
3559        return NULL;
3560    }
3561}
3562
3563static Boolean decomposeToRFC1808(CFURLRef url, CFURLComponentsRFC1808 *components) {
3564    CFAllocatorRef alloc = CFGetAllocator(url);
3565    static CFStringRef emptyStr = NULL;
3566    if (!emptyStr) {
3567        emptyStr = CFSTR("");
3568    }
3569
3570    if (!CFURLCanBeDecomposed(url)) {
3571        return false;
3572    }
3573
3574    CFStringRef path = CFURLCopyPath(url);
3575    if (path) {
3576        components->pathComponents = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("/"));
3577        CFRelease(path);
3578    } else {
3579        components->pathComponents = NULL;
3580    }
3581    components->baseURL = CFURLGetBaseURL(url);
3582    if (components->baseURL)  {
3583        CFRetain(components->baseURL);
3584        components->scheme = NULL;
3585    } else {
3586        components->scheme = _retainedComponentString(url, HAS_SCHEME, true, false);
3587    }
3588    components->user = _retainedComponentString(url, HAS_USER, false, false);
3589    components->password = _retainedComponentString(url, HAS_PASSWORD, false, false);
3590    components->host = _retainedComponentString(url, HAS_HOST, false, false);
3591    if (url->_flags & HAS_PORT) {
3592        components->port = CFURLGetPortNumber(url);
3593    } else {
3594        components->port = kCFNotFound;
3595    }
3596    components->parameterString = _retainedComponentString(url, HAS_PARAMETERS, false, false);
3597    components->query = _retainedComponentString(url, HAS_QUERY, false, false);
3598    components->fragment = _retainedComponentString(url, HAS_FRAGMENT, false, false);
3599    return true;
3600}
3601
3602static CFURLRef composeFromRFC1808(CFAllocatorRef alloc, const CFURLComponentsRFC1808 *comp) {
3603    CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0);
3604    CFURLRef base = comp->baseURL;
3605    CFURLRef url;
3606    Boolean hadPrePathComponent = false;
3607    if (comp->scheme) {
3608        base = NULL;
3609        CFStringAppend(urlString, comp->scheme);
3610        CFStringAppend(urlString, CFSTR("://"));
3611        hadPrePathComponent = true;
3612    }
3613    if (comp->user || comp->password) {
3614        if (comp->user) {
3615            CFStringAppend(urlString, comp->user);
3616        }
3617        if (comp->password) {
3618            CFStringAppend(urlString, CFSTR(":"));
3619            CFStringAppend(urlString, comp->password);
3620        }
3621        CFStringAppend(urlString, CFSTR("@"));
3622        hadPrePathComponent = true;
3623    }
3624    if (comp->host) {
3625        CFStringAppend(urlString, comp->host);
3626        hadPrePathComponent = true;
3627    }
3628    if (comp->port != kCFNotFound) {
3629        CFStringAppendFormat(urlString, NULL, CFSTR(":%ld"), (long)comp->port);
3630        hadPrePathComponent = true;
3631    }
3632
3633    if (hadPrePathComponent && (comp->pathComponents == NULL || CFArrayGetCount( comp->pathComponents ) == 0 || CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) {
3634        CFStringAppend(urlString, CFSTR("/"));
3635    }
3636    if (comp->pathComponents) {
3637        CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/"));
3638        CFStringAppend(urlString, pathStr);
3639        CFRelease(pathStr);
3640    }
3641    if (comp->parameterString) {
3642        CFStringAppend(urlString, CFSTR(";"));
3643        CFStringAppend(urlString, comp->parameterString);
3644    }
3645    if (comp->query) {
3646        CFStringAppend(urlString, CFSTR("?"));
3647        CFStringAppend(urlString, comp->query);
3648    }
3649    if (comp->fragment) {
3650        CFStringAppend(urlString, CFSTR("#"));
3651        CFStringAppend(urlString, comp->fragment);
3652    }
3653    url = CFURLCreateWithString(alloc, urlString, base);
3654    CFRelease(urlString);
3655    return url;
3656}
3657
3658static Boolean decomposeToRFC2396(CFURLRef url, CFURLComponentsRFC2396 *comp) {
3659    CFAllocatorRef alloc = CFGetAllocator(url);
3660    CFURLComponentsRFC1808 oldComp;
3661    CFStringRef tmpStr;
3662    if (!decomposeToRFC1808(url, &oldComp)) {
3663        return false;
3664    }
3665    comp->scheme = oldComp.scheme;
3666    if (oldComp.user) {
3667        if (oldComp.password) {
3668            comp->userinfo = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@:%@"), oldComp.user, oldComp.password);
3669            CFRelease(oldComp.password);
3670            CFRelease(oldComp.user);
3671        } else {
3672            comp->userinfo = oldComp.user;
3673        }
3674    } else {
3675        comp->userinfo = NULL;
3676    }
3677    comp->host = oldComp.host;
3678    comp->port = oldComp.port;
3679    if (!oldComp.parameterString) {
3680        comp->pathComponents = oldComp.pathComponents;
3681    } else {
3682        int length = CFArrayGetCount(oldComp.pathComponents);
3683        comp->pathComponents = CFArrayCreateMutableCopy(alloc, length, oldComp.pathComponents);
3684        tmpStr = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp->pathComponents, length - 1), oldComp.parameterString);
3685        CFArraySetValueAtIndex((CFMutableArrayRef)comp->pathComponents, length - 1, tmpStr);
3686        CFRelease(tmpStr);
3687        CFRelease(oldComp.pathComponents);
3688        CFRelease(oldComp.parameterString);
3689    }
3690    comp->query = oldComp.query;
3691    comp->fragment = oldComp.fragment;
3692    comp->baseURL = oldComp.baseURL;
3693    return true;
3694}
3695
3696static CFURLRef composeFromRFC2396(CFAllocatorRef alloc, const CFURLComponentsRFC2396 *comp) {
3697    CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0);
3698    CFURLRef base = comp->baseURL;
3699    CFURLRef url;
3700    Boolean hadPrePathComponent = false;
3701    if (comp->scheme) {
3702        base = NULL;
3703        CFStringAppend(urlString, comp->scheme);
3704        CFStringAppend(urlString, CFSTR("://"));
3705        hadPrePathComponent = true;
3706    }
3707    if (comp->userinfo) {
3708        CFStringAppend(urlString, comp->userinfo);
3709        CFStringAppend(urlString, CFSTR("@"));
3710        hadPrePathComponent = true;
3711    }
3712    if (comp->host) {
3713        CFStringAppend(urlString, comp->host);
3714        if (comp->port != kCFNotFound) {
3715            CFStringAppendFormat(urlString, NULL, CFSTR(":%ld"), (long)comp->port);
3716        }
3717        hadPrePathComponent = true;
3718    }
3719    if (hadPrePathComponent && (comp->pathComponents == NULL || CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) {
3720        CFStringAppend(urlString, CFSTR("/"));
3721    }
3722    if (comp->pathComponents) {
3723        CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/"));
3724        CFStringAppend(urlString, pathStr);
3725        CFRelease(pathStr);
3726    }
3727    if (comp->query) {
3728        CFStringAppend(urlString, CFSTR("?"));
3729        CFStringAppend(urlString, comp->query);
3730    }
3731    if (comp->fragment) {
3732        CFStringAppend(urlString, CFSTR("#"));
3733        CFStringAppend(urlString, comp->fragment);
3734    }
3735    url = CFURLCreateWithString(alloc, urlString, base);
3736    CFRelease(urlString);
3737    return url;
3738}
3739
3740#undef CFURLCopyComponents
3741#undef CFURLCreateFromComponents
3742
3743CF_EXPORT
3744Boolean _CFURLCopyComponents(CFURLRef url, CFURLComponentDecomposition decompositionType, void *components) {
3745    url = _CFURLFromNSURL(url);
3746    switch (decompositionType) {
3747    case kCFURLComponentDecompositionNonHierarchical:
3748        return decomposeToNonHierarchical(url, (CFURLComponentsNonHierarchical *)components);
3749    case kCFURLComponentDecompositionRFC1808:
3750        return decomposeToRFC1808(url, (CFURLComponentsRFC1808 *)components);
3751    case kCFURLComponentDecompositionRFC2396:
3752        return decomposeToRFC2396(url, (CFURLComponentsRFC2396 *)components);
3753    default:
3754        return false;
3755    }
3756}
3757
3758CF_EXPORT
3759CFURLRef _CFURLCreateFromComponents(CFAllocatorRef alloc, CFURLComponentDecomposition decompositionType, const void *components) {
3760    switch (decompositionType) {
3761    case kCFURLComponentDecompositionNonHierarchical:
3762        return composeFromNonHierarchical(alloc, (const CFURLComponentsNonHierarchical *)components);
3763    case kCFURLComponentDecompositionRFC1808:
3764        return composeFromRFC1808(alloc, (const CFURLComponentsRFC1808 *)components);
3765    case kCFURLComponentDecompositionRFC2396:
3766        return composeFromRFC2396(alloc, (const CFURLComponentsRFC2396 *)components);
3767    default:
3768        return NULL;
3769    }
3770}
3771
3772CF_EXPORT void *__CFURLReservedPtr(CFURLRef  url) {
3773    return _getReserved(url);
3774}
3775
3776CF_EXPORT void __CFURLSetReservedPtr(CFURLRef  url, void *ptr) {
3777    _setReserved ( (struct __CFURL*) url, ptr );
3778}
3779
3780CF_EXPORT void *__CFURLResourceInfoPtr(CFURLRef url) {
3781    return _getResourceInfo(url);
3782}
3783
3784CF_EXPORT void __CFURLSetResourceInfoPtr(CFURLRef url, void *ptr) {
3785    _setResourceInfo ( (struct __CFURL*) url, ptr );
3786}
3787
3788/* File system stuff */
3789
3790/* HFSPath<->URLPath functions at the bottom of the file */
3791static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) {
3792    CFArrayRef tmp;
3793    CFMutableArrayRef urlComponents = NULL;
3794    CFIndex i=0;
3795
3796    tmp = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("\\"));
3797    urlComponents = CFArrayCreateMutableCopy(alloc, 0, tmp);
3798    CFRelease(tmp);
3799
3800    CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(urlComponents, 0);
3801    if (isAbsolute && CFStringGetLength(str) == 2 && CFStringGetCharacterAtIndex(str, 1) == ':') {
3802        i = 1; // Skip over the drive letter
3803    }
3804    CFIndex c;
3805    for (c = CFArrayGetCount(urlComponents); i < c; i ++) {
3806        CFStringRef fileComp = (CFStringRef)CFArrayGetValueAtIndex(urlComponents,i);
3807        CFStringRef urlComp = _replacePathIllegalCharacters(fileComp, alloc, false);
3808        if (!urlComp) {
3809            // Couldn't decode fileComp
3810            CFRelease(urlComponents);
3811            return NULL;
3812        }
3813        if (urlComp != fileComp) {
3814            CFArraySetValueAtIndex(urlComponents, i, urlComp);
3815        }
3816        CFRelease(urlComp);
3817    }
3818
3819    if (isDir) {
3820        if (CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(urlComponents, CFArrayGetCount(urlComponents) - 1)) != 0)
3821            CFArrayAppendValue(urlComponents, CFSTR(""));
3822    }
3823    if (isAbsolute) {
3824        if ( AddAuthorityToFileURL() ) {
3825            CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY));
3826        }
3827        else {
3828            CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR(FILE_PREFIX));
3829        }
3830    }
3831    return urlComponents;
3832}
3833
3834static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) {
3835    CFArrayRef urlComponents;
3836    CFStringRef str;
3837
3838    if (CFStringGetLength(path) == 0) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII);
3839    urlComponents = WindowsPathToURLComponents(path, alloc, isDir, isAbsolute);
3840    if (!urlComponents) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII);
3841
3842    // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here.
3843    str = CFStringCreateByCombiningStrings(alloc, urlComponents, CFSTR("/"));
3844    CFRelease(urlComponents);
3845    return str;
3846}
3847
3848static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory, Boolean isAbsolute, Boolean *posixAndUrlPathsMatch) {
3849    Boolean addedPercentEncoding;
3850    CFStringRef pathString = NULL;
3851    STACK_BUFFER_DECL(char, buffer, PATH_MAX);
3852    if ( CFStringGetFileSystemRepresentation(path, buffer, PATH_MAX) ) {
3853        pathString = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault, (const UInt8 *)buffer, strlen(buffer), isDirectory, isAbsolute, false /* windowsPath */, &addedPercentEncoding);
3854    }
3855
3856    if ( posixAndUrlPathsMatch ) {
3857        *posixAndUrlPathsMatch = !addedPercentEncoding;
3858    }
3859    return pathString;
3860}
3861
3862static CFStringRef URLPathToPOSIXPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) {
3863    // This is the easiest case; just remove the percent escape codes and we're done
3864    CFStringRef result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, path, CFSTR(""), encoding);
3865    if (result) {
3866        CFIndex length = CFStringGetLength(result);
3867        if (length > 1 && CFStringGetCharacterAtIndex(result, length-1) == '/') {
3868            CFStringRef tmp = CFStringCreateWithSubstring(allocator, result, CFRangeMake(0, length-1));
3869            CFRelease(result);
3870            result = tmp;
3871        }
3872    }
3873    return result;
3874}
3875
3876#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
3877static Boolean CanonicalFileURLStringToFileSystemRepresentation(CFStringRef str, UInt8 *inBuffer, CFIndex inBufferLen)
3878{
3879    size_t fileURLPrefixLength;
3880    if ( AddAuthorityToFileURL() ) {
3881        fileURLPrefixLength = sizeof(fileURLPrefixWithAuthority);
3882    }
3883    else {
3884        fileURLPrefixLength = sizeof(fileURLPrefix);
3885    }
3886    Boolean result;
3887    if ( inBuffer && inBufferLen ) {
3888        STACK_BUFFER_DECL(UInt8, stackEscapedBuf, PATH_MAX * 3);    // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3889        UInt8 *escapedBuf;
3890        CFIndex strLength = CFStringGetLength(str) - (fileURLPrefixLength - 1);
3891        if ( strLength != 0 ) {
3892            CFIndex maxBufLength = strLength * 3;
3893            CFIndex usedBufLen;
3894            CFIndex charsConverted;
3895            if ( strLength <= PATH_MAX ) {
3896                escapedBuf = &stackEscapedBuf[0];
3897            }
3898            else {
3899                // worst case size is every unicode code point could be a 3-byte UTF8 sequence
3900                escapedBuf = (UInt8 *)malloc(maxBufLength);
3901            }
3902            if ( escapedBuf != NULL ) {
3903                charsConverted = CFStringGetBytes(str, CFRangeMake(fileURLPrefixLength - 1, strLength), kCFStringEncodingUTF8, 0, false, escapedBuf, maxBufLength, &usedBufLen);
3904                if ( charsConverted ) {
3905                    static const UInt8 hexvalues[] = {
3906                        /* 00 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3907                        /* 08 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3908                        /* 10 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3909                        /* 18 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3910                        /* 20 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3911                        /* 28 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3912                        /* 30 */  0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
3913                        /* 38 */  0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3914                        /* 40 */  0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3915                        /* 48 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3916                        /* 50 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3917                        /* 58 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3918                        /* 60 */  0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,
3919                        /* 68 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3920                        /* 70 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3921                        /* 78 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3922
3923                        /* 80 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3924                        /* 88 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3925                        /* 90 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3926                        /* 98 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3927                        /* A0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3928                        /* A8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3929                        /* B0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3930                        /* B8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3931                        /* C0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3932                        /* C8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3933                        /* D0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3934                        /* D8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3935                        /* E0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3936                        /* E8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3937                        /* F0 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3938                        /* F8 */  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3939                    };
3940                    UInt8 *bufStartPtr;
3941                    UInt8 *bufEndPtr;
3942                    UInt8 *bufPtr;
3943                    const UInt8 *bytePtr = escapedBuf;
3944                    CFIndex idx;
3945                    Boolean trailingSlash = false;
3946
3947                    bufPtr = bufStartPtr = inBuffer;
3948                    bufEndPtr = inBuffer + inBufferLen;
3949                    result = TRUE;
3950
3951                    for ( idx = 0; (idx < usedBufLen) && result; ++idx ) {
3952                        if ( bufPtr == bufEndPtr ) {
3953                            // ooops, ran out of inBuffer
3954                            *bufStartPtr = '\0';
3955                            result = FALSE;
3956                        }
3957                        else {
3958                            switch ( *bytePtr ) {
3959                                case '%':
3960                                    idx += 2;
3961                                    if ( idx < usedBufLen ) {
3962                                        // skip over %
3963                                        bytePtr++;
3964                                        // convert hex digits
3965                                        *bufPtr = hexvalues[*bytePtr++] << 4;
3966                                        *bufPtr += hexvalues[*bytePtr++];
3967                                        trailingSlash = (*bufPtr == '/');
3968                                    }
3969                                    break;
3970                                default:
3971                                    // copy everything else
3972                                    *bufPtr = *bytePtr++;
3973                                    trailingSlash = (*bufPtr == '/');
3974                                    break;
3975                            }
3976                            ++bufPtr;
3977                        }
3978                    }
3979                    if ( result ) {
3980                        // remove trailing slash (if any)
3981                        if ( (bufPtr > (bufStartPtr + 1)) && trailingSlash ) {
3982                            --bufPtr;
3983                        }
3984                        if ( bufPtr < bufEndPtr ) {
3985                            *bufPtr = '\0';
3986                        }
3987                    }
3988                }
3989                else {
3990                    // CFStringGetBytes failed
3991                    result = FALSE;
3992                }
3993
3994                // free the buffer if we malloc'd it
3995                if ( escapedBuf != &stackEscapedBuf[0] ) {
3996                    free(escapedBuf);
3997                }
3998            }
3999            else {
4000                // could not allocate escapedBuf
4001                result = FALSE;
4002            }
4003        }
4004        else {
4005            // str was zero characters
4006            *inBuffer = '\0';
4007            result = TRUE;
4008        }
4009    }
4010    else {
4011        // no inBuffer or inBufferLen is zero
4012        result = FALSE;
4013    }
4014
4015    return ( result );
4016}
4017#endif
4018
4019#if DEPLOYMENT_TARGET_WINDOWS
4020// From CFPlatform.c
4021extern CFStringRef CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr);
4022#endif
4023
4024static CFStringRef URLPathToWindowsPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) {
4025    // Check for a drive letter, then flip all the slashes
4026    CFStringRef result;
4027    CFArrayRef tmp = CFStringCreateArrayBySeparatingStrings(allocator, path, CFSTR("/"));
4028    SInt32 count = CFArrayGetCount(tmp);
4029    CFMutableArrayRef components = CFArrayCreateMutableCopy(allocator, count, tmp);
4030    CFStringRef newPath;
4031
4032
4033
4034    CFRelease(tmp);
4035    if (CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(components,count-1)) == 0) {
4036        CFArrayRemoveValueAtIndex(components, count-1);
4037        count --;
4038    }
4039
4040    if (count > 1 && CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(components, 0)) == 0) {
4041        // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component
4042        CFStringRef firstComponent = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, (CFStringRef)CFArrayGetValueAtIndex(components, 1), CFSTR(""), encoding);
4043        UniChar ch;
4044
4045	{
4046            if (firstComponent) {
4047		if (CFStringGetLength(firstComponent) == 2 && ((ch = CFStringGetCharacterAtIndex(firstComponent, 1)) == '|' || ch == ':')) {
4048		    // Drive letter
4049		    CFArrayRemoveValueAtIndex(components, 0);
4050		    if (ch == '|') {
4051			CFStringRef driveStr = CFStringCreateWithFormat(allocator, NULL, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent, 0));
4052			CFArraySetValueAtIndex(components, 0, driveStr);
4053			CFRelease(driveStr);
4054		    }
4055		}
4056#if DEPLOYMENT_TARGET_WINDOWS
4057		else {
4058		    // From <rdar://problem/5623405> [DEFECT] CFURL returns a Windows path that contains volume name instead of a drive letter
4059		    // we need to replace the volume name (it is not valid on Windows) with the drive mounting point path
4060		    // remove the first component and set the component with the drive letter to be the first component
4061		    CFStringRef driveRootPath = CFCreateWindowsDrivePathFromVolumeName(firstComponent);
4062
4063		    if (driveRootPath) {
4064			// remove trailing slash
4065			if (CFStringHasSuffix(driveRootPath, CFSTR("\\"))) {
4066			    CFStringRef newDriveRootPath = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, driveRootPath, CFRangeMake(0, CFStringGetLength(driveRootPath) - 1));
4067			    CFRelease(driveRootPath);
4068			    driveRootPath = newDriveRootPath;
4069			}
4070
4071			// replace the first component of the path with the drive path
4072			CFArrayRemoveValueAtIndex(components, 0);
4073			CFArraySetValueAtIndex(components, 0, driveRootPath);
4074
4075			CFRelease(driveRootPath);
4076		    }
4077		}
4078#endif
4079	    }
4080        }
4081        if ( firstComponent ) {
4082            CFRelease(firstComponent);
4083        }
4084    }
4085    newPath = CFStringCreateByCombiningStrings(allocator, components, CFSTR("\\"));
4086    CFRelease(components);
4087    result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, newPath, CFSTR(""), encoding);
4088    CFRelease(newPath);
4089    return result;
4090}
4091
4092
4093
4094// Caller must release the returned string
4095static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc) {
4096    CFIndex baseLen = CFStringGetLength(basePath);
4097    CFIndex relLen = CFStringGetLength(relativePath);
4098    UniChar pathDelimiter = '/';
4099    UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar)*(relLen + baseLen + 2), 0);
4100    CFStringGetCharacters(basePath, CFRangeMake(0, baseLen), buf);
4101    if (baseIsDir) {
4102        if (buf[baseLen-1] != pathDelimiter) {
4103            buf[baseLen] = pathDelimiter;
4104            baseLen ++;
4105        }
4106    } else {
4107        UniChar *ptr = buf + baseLen - 1;
4108        while (ptr > buf && *ptr != pathDelimiter) {
4109            ptr --;
4110        }
4111        baseLen = ptr - buf + 1;
4112    }
4113#pragma GCC diagnostic push
4114#pragma GCC diagnostic ignored "-Wdeprecated"
4115    if (fsType == kCFURLHFSPathStyle) {
4116#pragma GCC diagnostic pop
4117        // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first.
4118        baseLen --;
4119    }
4120    CFStringGetCharacters(relativePath, CFRangeMake(0, relLen), buf + baseLen);
4121    *(buf + baseLen + relLen) = '\0';
4122    return _resolvedPath(buf, buf + baseLen + relLen, pathDelimiter, false, true, alloc);
4123}
4124
4125CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator) {
4126    CFURLRef url = NULL;
4127    uint8_t buf[CFMaxPathSize + 1];
4128    if (_CFGetCurrentDirectory((char *)buf, CFMaxPathLength)) {
4129        url = CFURLCreateFromFileSystemRepresentation(allocator, buf, strlen((char *)buf), true);
4130    }
4131    return url;
4132}
4133
4134CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory) {
4135    CFURLRef result;
4136
4137    result = _CFURLAlloc(allocator);
4138    if ( result ) {
4139        if ( !_CFURLInitWithFileSystemPath(result, filePath, fsType, isDirectory, NULL) ) {
4140            CFRelease(result);
4141            result = NULL;
4142        }
4143    }
4144
4145    return ( result );
4146}
4147
4148CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory, CFURLRef baseURL) {
4149    CFURLRef result;
4150
4151    result = _CFURLAlloc(allocator);
4152    if ( result ) {
4153        if ( !_CFURLInitWithFileSystemPath(result, filePath, fsType, isDirectory, baseURL) ) {
4154            CFRelease(result);
4155            result = NULL;
4156        }
4157    }
4158
4159    return ( result );
4160}
4161
4162static Boolean _pathHasFileIDPrefix( CFStringRef path )
4163{
4164    // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix.
4165#ifdef __CONSTANT_STRINGS__
4166    static const
4167#endif
4168    CFStringRef fileIDPrefix = CFSTR( "/" FILE_ID_PREFIX "/" );
4169    return path && CFStringHasPrefix( path, fileIDPrefix ) && CFStringGetLength( path ) > CFStringGetLength( fileIDPrefix );
4170}
4171
4172#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4173static Boolean _pathHasFileIDOnly( CFStringRef path )
4174{
4175    // Is file ID rooted and contains no additonal path segments
4176    CFRange slashRange;
4177    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 );
4178}
4179#endif
4180
4181CF_EXPORT CFStringRef CFURLCopyFileSystemPath(CFURLRef anURL, CFURLPathStyle pathStyle) {
4182#pragma GCC diagnostic push
4183#pragma GCC diagnostic ignored "-Wdeprecated"
4184    CFAssert2(pathStyle == kCFURLPOSIXPathStyle || pathStyle == kCFURLHFSPathStyle || pathStyle == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__, pathStyle);
4185#pragma GCC diagnostic pop
4186
4187    CFStringRef result = NULL;
4188    CFAllocatorRef alloc = CFGetAllocator(anURL);
4189#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
4190    Boolean isCanonicalFileURL = false;
4191
4192    if ( (pathStyle == kCFURLPOSIXPathStyle) && (CFURLGetBaseURL(anURL) == NULL) ) {
4193        if ( !CF_IS_OBJC(__kCFURLTypeID, anURL) ) {
4194            // We can grope the ivars
4195            isCanonicalFileURL = ((anURL->_flags & IS_CANONICAL_FILE_URL) != 0);
4196            if ( isCanonicalFileURL ) {
4197                STACK_BUFFER_DECL(UInt8, buffer, PATH_MAX + 1);
4198                if ( CanonicalFileURLStringToFileSystemRepresentation(anURL->_string, buffer, PATH_MAX + 1) ) {
4199                    result = CFStringCreateWithBytes(alloc, buffer, strlen((char *)buffer), kCFStringEncodingUTF8, false);
4200                }
4201            }
4202        }
4203    }
4204    if ( ! result ) {
4205        // fall back to slower way.
4206        result = CFURLCreateStringWithFileSystemPath(alloc, anURL, pathStyle, false);
4207    }
4208#else // !DEPLOYMENT_TARGET_MACOSX
4209    result = CFURLCreateStringWithFileSystemPath(alloc, anURL, pathStyle, false);
4210#endif // !DEPLOYMENT_TARGET_MACOSX
4211
4212    return ( result );
4213}
4214
4215
4216// 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
4217CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase) {
4218    CFURLRef base = resolveAgainstBase ? CFURLGetBaseURL(anURL) : NULL;
4219    CFStringRef basePath = base ? CFURLCreateStringWithFileSystemPath(allocator, base, fsType, false) : NULL;
4220    CFStringRef relPath = NULL;
4221
4222    if (!CF_IS_OBJC(__kCFURLTypeID, anURL)) {
4223        // We can grope the ivars
4224        if (fsType == kCFURLPOSIXPathStyle) {
4225            if (anURL->_flags & POSIX_AND_URL_PATHS_MATCH) {
4226                relPath = _retainedComponentString(anURL, HAS_PATH, true, true);
4227            }
4228        }
4229    }
4230
4231    if (relPath == NULL) {
4232        CFStringRef urlPath = CFURLCopyPath(anURL);
4233        CFStringEncoding enc = anURL->_encoding;
4234        if (urlPath) {
4235            switch (fsType) {
4236                case kCFURLPOSIXPathStyle:
4237                    relPath = URLPathToPOSIXPath(urlPath, allocator, enc);
4238                    break;
4239#pragma GCC diagnostic push
4240#pragma GCC diagnostic ignored "-Wdeprecated"
4241                case kCFURLHFSPathStyle:
4242#pragma GCC diagnostic pop
4243		    relPath = NULL;
4244                    break;
4245                case kCFURLWindowsPathStyle:
4246                    relPath = URLPathToWindowsPath(urlPath, allocator, enc);
4247                    break;
4248                default:
4249                    CFAssert2(true, __kCFLogAssertion, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__, fsType);
4250            }
4251            CFRelease(urlPath);
4252        }
4253    }
4254
4255    //	For Tiger, leave this behavior in for all path types.  For Leopard, it would be nice to remove this entirely
4256    //	and do a linked-on-or-later check so we don't break third parties.
4257    //	See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and
4258    //	<rdar://problem/4018895> CF needs to back out 4003028 for icky details.
4259    if ( relPath && CFURLHasDirectoryPath(anURL) && CFStringGetLength(relPath) > 1 && CFStringGetCharacterAtIndex(relPath, CFStringGetLength(relPath)-1) == '/') {
4260        CFStringRef tmp = CFStringCreateWithSubstring(allocator, relPath, CFRangeMake(0, CFStringGetLength(relPath)-1));
4261        CFRelease(relPath);
4262        relPath = tmp;
4263    }
4264
4265    if ( relPath ) {
4266        if ( basePath && (CFStringGetCharacterAtIndex(relPath, 0) != '/') ) {
4267            // we have both basePath and relPath, and relPath is not absolute -- resolve them
4268            CFStringRef result = _resolveFileSystemPaths(relPath, basePath, CFURLHasDirectoryPath(base), fsType, allocator);
4269            CFRelease(basePath);
4270            CFRelease(relPath);
4271            return result;
4272        }
4273        else {
4274            // we only have the relPath or relpath is absolute -- return it
4275            if ( basePath ) {
4276                CFRelease(basePath);
4277            }
4278            return relPath;
4279        }
4280    }
4281    else if ( basePath ) {
4282        // we only have the basePath --- return it
4283        return basePath;
4284    }
4285    else {
4286        // we have nothing to return
4287        return NULL;
4288    }
4289}
4290
4291Boolean CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, uint8_t *buffer, CFIndex bufLen) {
4292#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_WINDOWS
4293    CFAllocatorRef alloc = CFGetAllocator(url);
4294    CFStringRef path;
4295
4296    if (!url) return false;
4297#endif
4298#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
4299    if ( !resolveAgainstBase || (CFURLGetBaseURL(url) == NULL) ) {
4300        if (!CF_IS_OBJC(__kCFURLTypeID, url)) {
4301            // We can grope the ivars
4302            if ( url->_flags & IS_CANONICAL_FILE_URL ) {
4303                return CanonicalFileURLStringToFileSystemRepresentation(url->_string, buffer, bufLen);
4304            }
4305        }
4306    }
4307    // else fall back to slower way.
4308    path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLPOSIXPathStyle, resolveAgainstBase);
4309    if (path) {
4310        Boolean convResult = _CFStringGetFileSystemRepresentation(path, buffer, bufLen);
4311        CFRelease(path);
4312        return convResult;
4313    }
4314#elif DEPLOYMENT_TARGET_WINDOWS
4315    path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLWindowsPathStyle, resolveAgainstBase);
4316    if (path) {
4317        CFIndex usedLen;
4318        CFIndex pathLen = CFStringGetLength(path);
4319        CFIndex numConverted = CFStringGetBytes(path, CFRangeMake(0, pathLen), CFStringFileSystemEncoding(), 0, true, buffer, bufLen-1, &usedLen); // -1 because we need one byte to zero-terminate.
4320        CFRelease(path);
4321        if (numConverted == pathLen) {
4322            buffer[usedLen] = '\0';
4323            return true;
4324        }
4325    }
4326#endif
4327    return false;
4328}
4329
4330#if DEPLOYMENT_TARGET_WINDOWS
4331CF_EXPORT Boolean _CFURLGetWideFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, wchar_t *buffer, CFIndex bufferLength) {
4332	CFStringRef path = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLWindowsPathStyle, resolveAgainstBase);
4333	CFIndex pathLength, charsConverted, usedBufLen;
4334	if (!path) return false;
4335	pathLength = CFStringGetLength(path);
4336	if (pathLength+1 > bufferLength) {
4337		CFRelease(path);
4338		return false;
4339	}
4340	charsConverted = CFStringGetBytes(path, CFRangeMake(0, pathLength), kCFStringEncodingUTF16, 0, false, (UInt8 *)buffer, bufferLength*sizeof(wchar_t), &usedBufLen);
4341//	CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer);
4342	CFRelease(path);
4343	if (charsConverted != pathLength || usedBufLen%sizeof(wchar_t) != 0) {
4344		return false;
4345	} else {
4346		buffer[usedBufLen/sizeof(wchar_t)] = 0;
4347//		buffer[pathLength] = 0;
4348		return true;
4349	}
4350}
4351#endif
4352
4353CFURLRef CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory) {
4354    CFURLRef result;
4355
4356    result = _CFURLAlloc(allocator);
4357    if ( result ) {
4358        if ( !_CFURLInitWithFileSystemRepresentation(result, buffer, bufLen, isDirectory, NULL) ) {
4359            CFRelease(result);
4360            result = NULL;
4361        }
4362    }
4363
4364    return ( result );
4365}
4366
4367CF_EXPORT CFURLRef CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL) {
4368    CFURLRef result;
4369
4370    result = _CFURLAlloc(allocator);
4371    if ( result ) {
4372        if ( !_CFURLInitWithFileSystemRepresentation(result, buffer, bufLen, isDirectory, baseURL) ) {
4373            CFRelease(result);
4374            result = NULL;
4375        }
4376    }
4377
4378    return ( result );
4379}
4380
4381
4382/******************************/
4383/* Support for path utilities */
4384/******************************/
4385
4386// Assumes url is a CFURL (not an Obj-C NSURL)
4387static CFRange _rangeOfLastPathComponent(CFURLRef url) {
4388    CFRange pathRg, componentRg;
4389
4390    pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
4391
4392    if (pathRg.location == kCFNotFound || pathRg.length == 0) {
4393        // No path
4394        return pathRg;
4395    }
4396    if (CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) == '/') {
4397        pathRg.length --;
4398        if (pathRg.length == 0) {
4399            pathRg.length ++;
4400            return pathRg;
4401        }
4402    }
4403    if (CFStringFindWithOptions(url->_string, CFSTR("/"), pathRg, kCFCompareBackwards, &componentRg)) {
4404        componentRg.location ++;
4405        componentRg.length = pathRg.location + pathRg.length - componentRg.location;
4406    } else {
4407        componentRg = pathRg;
4408    }
4409    return componentRg;
4410}
4411
4412CFStringRef CFURLCopyLastPathComponent(CFURLRef url) {
4413    CFStringRef result;
4414
4415    if (CF_IS_OBJC(__kCFURLTypeID, url)) {
4416        CFStringRef path = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLPOSIXPathStyle, false);
4417        CFIndex length;
4418        CFRange rg, compRg;
4419        if (!path) return NULL;
4420        rg = CFRangeMake(0, CFStringGetLength(path));
4421	if ( rg.length == 0 ) return path;
4422        length = rg.length; // Remember this for comparison later
4423        if (CFStringGetCharacterAtIndex(path, rg.length - 1) == '/' ) {
4424            rg.length --;
4425        }
4426	if ( rg.length == 0 )
4427	{
4428	    //	If we have reduced the string to empty, then it's "/", and that's what we return as
4429	    //	the last path component.
4430	   return path;
4431	}
4432        else if (CFStringFindWithOptions(path, CFSTR("/"), rg, kCFCompareBackwards, &compRg)) {
4433            rg.length = rg.location + rg.length - (compRg.location+1);
4434            rg.location = compRg.location + 1;
4435        }
4436        if (rg.location == 0 && rg.length == length) {
4437            result = path;
4438        } else {
4439            result = CFStringCreateWithSubstring(CFGetAllocator(url), path, rg);
4440            CFRelease(path);
4441        }
4442    } else {
4443        Boolean filePathURLCreated = false;
4444#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4445        if ( CFURLIsFileReferenceURL(url) ) {
4446            // use a file path URL or fail
4447            CFURLRef filePathURL = CFURLCreateFilePathURL(CFGetAllocator(url), url, NULL);
4448            if ( filePathURL ) {
4449                filePathURLCreated = TRUE;
4450                url = filePathURL;
4451            }
4452            else {
4453                return NULL;
4454            }
4455        }
4456#endif
4457
4458        CFRange rg = _rangeOfLastPathComponent(url);
4459        if (rg.location == kCFNotFound || rg.length == 0) {
4460            // No path
4461            if ( filePathURLCreated ) {
4462                CFRelease(url);
4463            }
4464            return (CFStringRef)CFRetain(CFSTR(""));
4465        }
4466        if (rg.length == 1 && CFStringGetCharacterAtIndex(url->_string, rg.location) == '/') {
4467            if ( filePathURLCreated ) {
4468                CFRelease(url);
4469            }
4470            return (CFStringRef)CFRetain(CFSTR("/"));
4471        }
4472        result = CFStringCreateWithSubstring(CFGetAllocator(url), url->_string, rg);
4473        if (!(url->_flags & POSIX_AND_URL_PATHS_MATCH)) {
4474            CFStringRef tmp;
4475            if (url->_encoding == kCFStringEncodingUTF8) {
4476                tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url), result, CFSTR(""));
4477            } else {
4478                tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url), result, CFSTR(""), url->_encoding);
4479            }
4480            CFRelease(result);
4481            result = tmp;
4482        }
4483        if ( filePathURLCreated ) {
4484            CFRelease(url);
4485        }
4486    }
4487    return result;
4488}
4489
4490CFStringRef CFURLCopyPathExtension(CFURLRef url) {
4491    CFStringRef lastPathComp = CFURLCopyLastPathComponent(url);
4492    CFStringRef ext = NULL;
4493
4494    if (lastPathComp) {
4495        CFRange rg = CFStringFind(lastPathComp, CFSTR("."), kCFCompareBackwards);
4496        if (rg.location != kCFNotFound) {
4497            rg.location ++;
4498            rg.length = CFStringGetLength(lastPathComp) - rg.location;
4499            if (rg.length > 0) {
4500                ext = CFStringCreateWithSubstring(CFGetAllocator(url), lastPathComp, rg);
4501            } else {
4502                ext = (CFStringRef)CFRetain(CFSTR(""));
4503            }
4504        }
4505        CFRelease(lastPathComp);
4506    }
4507    return ext;
4508}
4509
4510CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRef url, CFStringRef pathComponent, Boolean isDirectory) {
4511    CFURLRef result;
4512    url = _CFURLFromNSURL(url);
4513    __CFGenericValidateType(url, __kCFURLTypeID);
4514    CFAssert1(pathComponent != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__);
4515
4516    Boolean filePathURLCreated = false;
4517#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4518    if ( CFURLIsFileReferenceURL(url) ) {
4519        // use a file path URL if possible (only because this is appending a path component)
4520        CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4521        if ( filePathURL ) {
4522            filePathURLCreated = TRUE;
4523            url = filePathURL;
4524        }
4525    }
4526#endif
4527
4528    CFMutableStringRef newString;
4529    CFStringRef newComp;
4530    CFRange pathRg;
4531    if (!(url->_flags & HAS_PATH)) {
4532        result = NULL;
4533    }
4534    else {
4535        newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4536        newComp = CFURLCreateStringByAddingPercentEscapes(allocator, pathComponent, NULL, CFSTR(";?"), url->_encoding);
4537        pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
4538        if ( (!pathRg.length || CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') && (CFStringGetCharacterAtIndex(newComp, 0) != '/') ) {
4539            CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/"));
4540            pathRg.length ++;
4541        }
4542        CFStringInsert(newString, pathRg.location + pathRg.length, newComp);
4543        if (isDirectory) {
4544            CFStringInsert(newString, pathRg.location + pathRg.length + CFStringGetLength(newComp), CFSTR("/"));
4545        }
4546        CFRelease(newComp);
4547        result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4548        CFRelease(newString);
4549    }
4550    if ( filePathURLCreated ) {
4551        CFRelease(url);
4552    }
4553    return result;
4554}
4555
4556CFURLRef CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator, CFURLRef url) {
4557    CFURLRef result;
4558    CFMutableStringRef newString;
4559    CFRange lastCompRg, pathRg;
4560    Boolean appendDotDot = false;
4561
4562    url = _CFURLFromNSURL(url);
4563    CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
4564    __CFGenericValidateType(url, __kCFURLTypeID);
4565
4566    Boolean filePathURLCreated = false;
4567#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4568    if ( CFURLIsFileReferenceURL(url) ) {
4569        // use a file path URL or fail
4570        CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4571        if ( filePathURL ) {
4572            filePathURLCreated = TRUE;
4573            url = filePathURL;
4574        }
4575        else {
4576            return NULL;
4577        }
4578    }
4579#endif
4580
4581    if (!(url->_flags & HAS_PATH)) {
4582        if ( filePathURLCreated ) {
4583            CFRelease(url);
4584        }
4585        return NULL;
4586    }
4587    pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH);
4588    lastCompRg = _rangeOfLastPathComponent(url);
4589    if (lastCompRg.length == 0) {
4590        appendDotDot = true;
4591    } else if (lastCompRg.length == 1) {
4592        UniChar ch = CFStringGetCharacterAtIndex(url->_string, lastCompRg.location);
4593        if (ch == '.' || ch == '/') {
4594            appendDotDot = true;
4595        }
4596    } else if (lastCompRg.length == 2 && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location) == '.' && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location+1) == '.') {
4597        appendDotDot = true;
4598    }
4599
4600    newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4601    if (appendDotDot) {
4602        CFIndex delta = 0;
4603        if (pathRg.length > 0 && CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') {
4604            CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/"));
4605            delta ++;
4606        }
4607        CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR(".."));
4608        delta += 2;
4609        CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR("/"));
4610        delta ++;
4611        // 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 "/.".
4612        if (pathRg.length + delta > 4 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 5) == '.') {
4613            if (pathRg.length+delta > 7 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 6) == '/') {
4614                CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 6, 2));
4615            } else if (pathRg.length+delta == 5) {
4616                CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 5, 2));
4617            }
4618        }
4619    } else if (lastCompRg.location == pathRg.location) {
4620        CFStringReplace(newString, pathRg, CFSTR("."));
4621        CFStringInsert(newString, 1, CFSTR("/"));
4622    } else {
4623        CFStringDelete(newString, CFRangeMake(lastCompRg.location, pathRg.location + pathRg.length - lastCompRg.location));
4624    }
4625    result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4626    CFRelease(newString);
4627    if ( filePathURLCreated ) {
4628        CFRelease(url);
4629    }
4630    return result;
4631}
4632
4633CFURLRef CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator, CFURLRef url, CFStringRef extension) {
4634    CFMutableStringRef newString;
4635    CFURLRef result;
4636    CFRange rg;
4637
4638    CFAssert1(url != NULL && extension != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
4639    url = _CFURLFromNSURL(url);
4640    __CFGenericValidateType(url, __kCFURLTypeID);
4641    __CFGenericValidateType(extension, CFStringGetTypeID());
4642
4643    Boolean filePathURLCreated = false;
4644#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4645    if ( CFURLIsFileReferenceURL(url) ) {
4646        // use a file path URL or fail
4647        CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4648        if ( filePathURL ) {
4649            filePathURLCreated = TRUE;
4650            url = filePathURL;
4651        }
4652        else {
4653            return NULL;
4654        }
4655    }
4656#endif
4657
4658    rg = _rangeOfLastPathComponent(url);
4659    if (rg.location < 0) {
4660        if ( filePathURLCreated ) {
4661            CFRelease(url);
4662        }
4663        return NULL; // No path
4664    }
4665
4666    newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4667    CFStringInsert(newString, rg.location + rg.length, CFSTR("."));
4668    CFStringRef newExt = CFURLCreateStringByAddingPercentEscapes(allocator, extension, NULL, CFSTR(";?/"), url->_encoding);
4669    CFStringInsert(newString, rg.location + rg.length + 1, newExt);
4670    CFRelease(newExt);
4671    result =  _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4672    CFRelease(newString);
4673    if ( filePathURLCreated ) {
4674        CFRelease(url);
4675    }
4676    return result;
4677}
4678
4679CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef url) {
4680    CFRange rg, dotRg;
4681    CFURLRef result;
4682
4683    CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__);
4684    url = _CFURLFromNSURL(url);
4685    __CFGenericValidateType(url, __kCFURLTypeID);
4686
4687    Boolean filePathURLCreated = false;
4688#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4689    if ( CFURLIsFileReferenceURL(url) ) {
4690        // use a file path URL or fail
4691        CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL);
4692        if ( filePathURL ) {
4693            filePathURLCreated = TRUE;
4694            url = filePathURL;
4695        }
4696        else {
4697            return NULL;
4698        }
4699    }
4700#endif
4701
4702    rg = _rangeOfLastPathComponent(url);
4703    if (rg.location < 0) {
4704        result = NULL;
4705    } else if (rg.length && CFStringFindWithOptions(url->_string, CFSTR("."), rg, kCFCompareBackwards, &dotRg)) {
4706        CFMutableStringRef newString = CFStringCreateMutableCopy(allocator, 0, url->_string);
4707        dotRg.length = rg.location + rg.length - dotRg.location;
4708        CFStringDelete(newString, dotRg);
4709        result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base);
4710        CFRelease(newString);
4711    } else {
4712        result = (CFURLRef)CFRetain(url);
4713    }
4714    if ( filePathURLCreated ) {
4715        CFRelease(url);
4716    }
4717    return result;
4718}
4719
4720
4721#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4722static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) {
4723    CFArrayRef components = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR(":"));
4724    CFMutableArrayRef newComponents = CFArrayCreateMutableCopy(alloc, 0, components);
4725    Boolean doSpecialLeadingColon = false;
4726    UniChar firstChar = CFStringGetCharacterAtIndex(path, 0);
4727    UInt32 i, cnt;
4728    CFRelease(components);
4729
4730
4731    if (!doSpecialLeadingColon && firstChar != ':') {
4732        CFArrayInsertValueAtIndex(newComponents, 0, CFSTR(""));
4733    } else if (firstChar != ':') {
4734        // see what we need to add at the beginning. Under MacOS, if the
4735        // first character isn't a ':', then the first component is the
4736        // volume name, and we need to find the mount point.  Bleah. If we
4737        // don't find a mount point, we're going to have to lie, and make something up.
4738        CFStringRef firstComp = (CFStringRef)CFArrayGetValueAtIndex(newComponents, 0);
4739        if (CFStringGetLength(firstComp) == 1 && CFStringGetCharacterAtIndex(firstComp, 0) == '/') {
4740            // "/" is the "magic" path for a UFS root directory
4741            CFArrayRemoveValueAtIndex(newComponents, 0);
4742            CFArrayInsertValueAtIndex(newComponents, 0, CFSTR(""));
4743        } else {
4744            // See if we can get a mount point.
4745            Boolean foundMountPoint = false;
4746            if (!foundMountPoint) {
4747                // Fall back to treating the volume name as the top level directory
4748                CFArrayInsertValueAtIndex(newComponents, 0, CFSTR(""));
4749            }
4750        }
4751    } else {
4752        CFArrayRemoveValueAtIndex(newComponents, 0);
4753    }
4754
4755    cnt = CFArrayGetCount(newComponents);
4756    for (i = 0; i < cnt; i ++) {
4757        CFStringRef comp = (CFStringRef)CFArrayGetValueAtIndex(newComponents, i);
4758        CFStringRef newComp = NULL;
4759        CFRange searchRg, slashRg;
4760        searchRg.location = 0;
4761        searchRg.length = CFStringGetLength(comp);
4762        while (CFStringFindWithOptions(comp, CFSTR("/"), searchRg, 0, &slashRg)) {
4763            if (!newComp) {
4764                newComp = CFStringCreateMutableCopy(alloc, searchRg.location + searchRg.length, comp);
4765            }
4766            CFStringReplace((CFMutableStringRef)newComp, slashRg, CFSTR(":"));
4767            searchRg.length = searchRg.location + searchRg.length - slashRg.location - 1;
4768            searchRg.location = slashRg.location + 1;
4769        }
4770        if (newComp) {
4771            CFArraySetValueAtIndex(newComponents, i, newComp);
4772            CFRelease(newComp);
4773        }
4774    }
4775    if (isDir && CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(newComponents, cnt-1)) != 0) {
4776            CFArrayAppendValue(newComponents, CFSTR(""));
4777    }
4778    return newComponents;
4779}
4780
4781typedef CFStringRef (*StringTransformation)(CFAllocatorRef, CFStringRef, CFIndex);
4782static CFArrayRef copyStringArrayWithTransformation(CFArrayRef array, StringTransformation transformation) {
4783    CFAllocatorRef alloc = CFGetAllocator(array);
4784    CFMutableArrayRef mArray = NULL;
4785    CFIndex i, c = CFArrayGetCount(array);
4786    for (i = 0; i < c; i ++) {
4787        CFStringRef origComp = (CFStringRef)CFArrayGetValueAtIndex(array, i);
4788        CFStringRef unescapedComp = transformation(alloc, origComp, i);
4789        if (!unescapedComp) {
4790            break;
4791        }
4792        if (unescapedComp != origComp) {
4793            if (!mArray) {
4794                mArray = CFArrayCreateMutableCopy(alloc, c, array);
4795            }
4796            CFArraySetValueAtIndex(mArray, i, unescapedComp);
4797        }
4798        CFRelease(unescapedComp);
4799    }
4800    if (i != c) {
4801        if (mArray) CFRelease(mArray);
4802        return NULL;
4803    } else if (mArray) {
4804        return mArray;
4805    } else {
4806        CFRetain(array);
4807        return array;
4808    }
4809}
4810
4811static CFStringRef escapePathComponent(CFAllocatorRef alloc, CFStringRef origComponent, CFIndex componentIndex) {
4812    return CFURLCreateStringByAddingPercentEscapes(alloc, origComponent, NULL, CFSTR(";?/"), kCFStringEncodingUTF8);
4813}
4814
4815static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) {
4816    CFArrayRef components = HFSPathToURLComponents(path, alloc, isDir);
4817    CFArrayRef newComponents = components ? copyStringArrayWithTransformation(components, escapePathComponent) : NULL;
4818    CFIndex cnt;
4819    CFStringRef result;
4820    if (components) CFRelease(components);
4821    if (!newComponents) return NULL;
4822
4823    cnt = CFArrayGetCount(newComponents);
4824    if (cnt == 1 && CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(newComponents, 0)) == 0) {
4825        result = (CFStringRef)CFRetain(CFSTR("/"));
4826    } else {
4827        result = CFStringCreateByCombiningStrings(alloc, newComponents, CFSTR("/"));
4828    }
4829    if ( isAbsolute && result )  {
4830        CFStringRef temp = result;
4831        if ( AddAuthorityToFileURL() ) {
4832            result = CFStringCreateWithFormat(alloc, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY "%@"), temp);
4833        }
4834        else {
4835            result = CFStringCreateWithFormat(alloc, 0, CFSTR(FILE_PREFIX "%@"), temp);
4836        }
4837        CFRelease(temp);
4838    }
4839    CFRelease(newComponents);
4840    return result;
4841}
4842#endif
4843
4844
4845
4846// keys and vals must have space for at least 4 key/value pairs.  No argument can be NULL.
4847// Caller must release values, but not keys
4848static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url, CFTypeRef *keys, CFTypeRef *vals, CFIndex *count) {
4849    CFAllocatorRef alloc = CFGetAllocator(url);
4850    CFURLRef base = CFURLGetBaseURL(url);
4851    keys[0] = CFSTR("_CFURLStringType");
4852    keys[1] = CFSTR("_CFURLString");
4853    keys[2] = CFSTR("_CFURLBaseStringType");
4854    keys[3] = CFSTR("_CFURLBaseURLString");
4855    if (CF_IS_OBJC(__kCFURLTypeID, url)) {
4856        SInt32 urlType = FULL_URL_REPRESENTATION;
4857        vals[0] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4858        vals[1] = CFURLGetString(url);
4859    } else {
4860        SInt32 urlType = FULL_URL_REPRESENTATION;
4861        vals[0] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4862        if (url->_flags & IS_DIRECTORY) {
4863            if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) == '/') {
4864                vals[1] = CFRetain(url->_string);
4865            } else {
4866                vals[1] = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%c"), url->_string, '/');
4867            }
4868        } else {
4869            if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) != '/') {
4870                vals[1] = CFRetain(url->_string);
4871            } else {
4872                vals[1] = CFStringCreateWithSubstring(alloc, url->_string, CFRangeMake(0, CFStringGetLength(url->_string) - 1));
4873            }
4874        }
4875    }
4876    if (base != NULL) {
4877        if (CF_IS_OBJC(__kCFURLTypeID, base)) {
4878            SInt32 urlType = FULL_URL_REPRESENTATION;
4879            vals[2] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4880            vals[3] = CFURLGetString(base);
4881        } else {
4882            SInt32 urlType = FULL_URL_REPRESENTATION;
4883            vals[2] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType);
4884            if (base->_flags & IS_DIRECTORY) {
4885                if (CFStringGetCharacterAtIndex(base->_string, CFStringGetLength(base->_string) - 1) == '/') {
4886                    vals[3] = CFRetain(base->_string);
4887                } else {
4888                    vals[3] = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%c"), base->_string, '/');
4889                }
4890            } else {
4891                if (CFStringGetCharacterAtIndex(base->_string, CFStringGetLength(base->_string) - 1) != '/') {
4892                    vals[3] = CFRetain(base->_string);
4893                } else {
4894                    vals[3] = CFStringCreateWithSubstring(alloc, base->_string, CFRangeMake(0, CFStringGetLength(base->_string) - 1));
4895                }
4896            }
4897        }
4898        *count = 4;
4899    } else {
4900        *count = 2;
4901    }
4902}
4903
4904// Private API for Finder to use
4905CFPropertyListRef _CFURLCopyPropertyListRepresentation(CFURLRef url) {
4906    CFTypeRef keys[4], vals[4];
4907    CFDictionaryRef dict;
4908    CFIndex count, idx;
4909    __CFURLCopyPropertyListKeysAndValues(url, keys, vals, &count);
4910    dict = CFDictionaryCreate(CFGetAllocator(url), keys, vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4911    for (idx = 0; idx < count; idx ++) {
4912        CFRelease(vals[idx]);
4913    }
4914    return dict;
4915}
4916
4917CFURLRef _CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc, CFPropertyListRef pListRepresentation) {
4918    CFStringRef baseString, string;
4919    CFNumberRef baseTypeNum, urlTypeNum;
4920    SInt32 baseType, urlType;
4921    CFURLRef baseURL = NULL, url;
4922    CFDictionaryRef dict = (CFDictionaryRef)pListRepresentation;
4923
4924    // Start by getting all the pieces and verifying they're of the correct type.
4925    if (CFGetTypeID(pListRepresentation) != CFDictionaryGetTypeID()) {
4926        return NULL;
4927    }
4928    string = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("_CFURLString"));
4929    if (!string || CFGetTypeID(string) != CFStringGetTypeID()) {
4930        return NULL;
4931    }
4932    urlTypeNum = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("_CFURLStringType"));
4933#pragma GCC diagnostic push
4934#pragma GCC diagnostic ignored "-Wdeprecated"
4935    if (!urlTypeNum || CFGetTypeID(urlTypeNum) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum, kCFNumberSInt32Type, &urlType) || (urlType != FULL_URL_REPRESENTATION && urlType != kCFURLPOSIXPathStyle && urlType != kCFURLHFSPathStyle && urlType != kCFURLWindowsPathStyle)) {
4936#pragma GCC diagnostic pop
4937        return NULL;
4938    }
4939    baseString = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("_CFURLBaseURLString"));
4940    if (baseString) {
4941        if (CFGetTypeID(baseString) != CFStringGetTypeID()) {
4942            return NULL;
4943        }
4944        baseTypeNum = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("_CFURLBaseStringType"));
4945#pragma GCC diagnostic push
4946#pragma GCC diagnostic ignored "-Wdeprecated"
4947        if (!baseTypeNum || CFGetTypeID(baseTypeNum) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum, kCFNumberSInt32Type, &baseType) ||
4948            (baseType != FULL_URL_REPRESENTATION && baseType != kCFURLPOSIXPathStyle && baseType != kCFURLHFSPathStyle && baseType != kCFURLWindowsPathStyle)) {
4949#pragma GCC diagnostic pop
4950            return NULL;
4951        }
4952        if (baseType == FULL_URL_REPRESENTATION) {
4953            baseURL = _CFURLCreateWithArbitraryString(alloc, baseString, NULL);
4954        } else {
4955            baseURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, baseString, (CFURLPathStyle)baseType, CFStringGetCharacterAtIndex(baseString, CFStringGetLength(baseString)-1) == '/', NULL);
4956        }
4957    }
4958    if (urlType == FULL_URL_REPRESENTATION) {
4959        url = _CFURLCreateWithArbitraryString(alloc, string, baseURL);
4960    } else {
4961        url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, string, (CFURLPathStyle)urlType, CFStringGetCharacterAtIndex(string, CFStringGetLength(string)-1) == '/', baseURL);
4962    }
4963    if (baseURL) CFRelease(baseURL);
4964    return url;
4965}
4966
4967#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
4968Boolean _CFURLIsFileReferenceURL(CFURLRef url)
4969{
4970    return ( CFURLIsFileReferenceURL(url) );
4971}
4972
4973Boolean CFURLIsFileReferenceURL(CFURLRef url)
4974{
4975    // returns TRUE if url is is a file URL whose path starts with a file ID reference
4976    Boolean result = false;
4977    CFURLRef baseURL = CFURLGetBaseURL(url);
4978    if ( baseURL ) {
4979	result = CFURLIsFileReferenceURL(baseURL);
4980    }
4981    else {
4982        if ( CF_IS_OBJC(__kCFURLTypeID, url) ) {
4983            result = (Boolean) CF_OBJC_CALLV((NSURL *)url, isFileReferenceURL);
4984        }
4985        else {
4986            result = ((_getSchemeTypeFromFlags(url->_flags) == kHasFileScheme) && ((url->_flags & PATH_HAS_FILE_ID) != 0));
4987        }
4988    }
4989    return ( result );
4990}
4991
4992static Boolean _CFURLHasFileURLScheme(CFURLRef url, Boolean *hasScheme)
4993{
4994    Boolean result;
4995    CFURLRef baseURL = CFURLGetBaseURL(url);
4996
4997    if ( baseURL ) {
4998	result = _CFURLHasFileURLScheme(baseURL, hasScheme);
4999    }
5000    else {
5001        if ( CF_IS_OBJC(__kCFURLTypeID, url) ) {
5002            CFStringRef scheme = CFURLCopyScheme(url);
5003            if ( scheme ) {
5004                if ( scheme == kCFURLFileScheme ) {
5005                    result = true;
5006                }
5007                else {
5008                    result = CFStringCompare(scheme, kCFURLFileScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo;
5009                }
5010                if ( hasScheme ) {
5011                    *hasScheme = true;
5012                }
5013                CFRelease(scheme);
5014            }
5015            else {
5016                if ( hasScheme ) {
5017                    *hasScheme = false;
5018                }
5019                result = false;
5020            }
5021        }
5022        else {
5023            if ( hasScheme ) {
5024                *hasScheme = (url->_flags & HAS_SCHEME) != 0;
5025            }
5026            result = (_getSchemeTypeFromFlags(url->_flags) == kHasFileScheme);
5027        }
5028    }
5029    return ( result );
5030}
5031
5032Boolean _CFURLIsFileURL(CFURLRef url)
5033{
5034    Boolean result = _CFURLHasFileURLScheme(url, NULL);
5035    return ( result );
5036}
5037
5038CFURLRef CFURLCreateFilePathURL(CFAllocatorRef alloc, CFURLRef url, CFErrorRef *error)
5039{
5040    CFURLRef result = NULL;
5041    Boolean hasScheme;
5042    if (!_CFURLHasFileURLScheme(url, &hasScheme)) {
5043        if ( !hasScheme ) {
5044            CFLog(kCFLogLevelWarning, CFSTR("CFURLCreateFilePathURL failed because it was passed this URL which has no scheme: %@"), url);
5045        }
5046        if ( error ) {
5047            *error = CFErrorCreate( kCFAllocatorDefault, kCFErrorDomainCocoa, kCFURLReadUnsupportedSchemeError, NULL );
5048        }
5049        result = NULL;
5050    } else {
5051	// File URL. Form of the path is unknown. Make a new URL.
5052	CFStringRef newURLString;
5053	CFStringRef netLoc;
5054	CFStringRef fsPath;
5055	CFStringRef rSpec;
5056
5057	if ( CFURLGetBaseURL( url )) {
5058	    CFURLRef absURL = CFURLCopyAbsoluteURL( url );
5059	    fsPath = CFURLCreateStringWithFileSystemPath(CFGetAllocator(absURL), absURL, kCFURLPOSIXPathStyle, false);
5060	    netLoc = CFURLCopyNetLocation( absURL );
5061	    rSpec = CFURLCopyResourceSpecifier( absURL );
5062	    CFRelease( absURL );
5063	} else {
5064	    fsPath = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLPOSIXPathStyle, false);
5065	    netLoc = CFURLCopyNetLocation( url );
5066	    rSpec = CFURLCopyResourceSpecifier( url );
5067	}
5068	if ( fsPath ) {
5069	    CFStringRef urlPath = _replacePathIllegalCharacters( fsPath, alloc, true );
5070	    newURLString = CFStringCreateWithFormat( alloc, NULL, CFSTR(FILE_PREFIX "%@%@%@%@"), (netLoc ? netLoc : CFSTR("")), urlPath, ((CFStringCompare(urlPath, CFSTR("/"), 0) != kCFCompareEqualTo) ? (CFURLHasDirectoryPath( url ) ? CFSTR("/") : CFSTR("")) : CFSTR("")), (rSpec ? rSpec : CFSTR("")));
5071	    result = CFURLCreateWithString( alloc, newURLString, NULL );
5072	    CFRelease( newURLString );
5073	    CFRelease( urlPath );
5074	    CFRelease( fsPath );
5075	} else {
5076	    if ( error ) {
5077		// Would be better here to get an underlying error back from CFURLCreateStringWithFileSystemPath
5078		*error = CFErrorCreate( kCFAllocatorDefault, kCFErrorDomainCocoa, kCFURLNoSuchResourceError, NULL );
5079	    }
5080	    result = NULL;
5081	}
5082	if ( netLoc ) {
5083	    CFRelease( netLoc );
5084	}
5085	if ( rSpec ) {
5086	    CFRelease( rSpec );
5087	}
5088    }
5089    return result;
5090}
5091
5092#endif
5093
5094
5095CFURLRef CFURLCreateFileReferenceURL(CFAllocatorRef alloc, CFURLRef url, CFErrorRef *error) { return NULL; }
5096
5097