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