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