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