2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1999-2014, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFPriv.h>
37 #if DEPLOYMENT_TARGET_WINDOWS
47 #define mkdir(a,b) _NS_mkdir(a)
48 #define rmdir _NS_rmdir
49 #define unlink _NS_unlink
51 #define statinfo _stat
57 #include <sys/types.h>
65 CF_INLINE
int openAutoFSNoWait() {
66 #if DEPLOYMENT_TARGET_WINDOWS
69 return (__CFProphylacticAutofsAccess
? open("/dev/autofs_nowait", 0) : -1);
73 CF_INLINE
void closeAutoFSNoWait(int fd
) {
74 if (-1 != fd
) close(fd
);
77 CF_PRIVATE CFStringRef
_CFCopyExtensionForAbstractType(CFStringRef abstractType
) {
78 return (abstractType
? (CFStringRef
)CFRetain(abstractType
) : NULL
);
82 CF_PRIVATE Boolean
_CFCreateDirectory(const char *path
) {
83 int no_hang_fd
= openAutoFSNoWait();
84 int ret
= ((mkdir(path
, 0777) == 0) ? true : false);
85 closeAutoFSNoWait(no_hang_fd
);
89 CF_PRIVATE Boolean
_CFRemoveDirectory(const char *path
) {
90 int no_hang_fd
= openAutoFSNoWait();
91 int ret
= ((rmdir(path
) == 0) ? true : false);
92 closeAutoFSNoWait(no_hang_fd
);
96 CF_PRIVATE Boolean
_CFDeleteFile(const char *path
) {
97 int no_hang_fd
= openAutoFSNoWait();
98 int ret
= unlink(path
) == 0;
99 closeAutoFSNoWait(no_hang_fd
);
103 static Boolean
_CFReadBytesFromPathAndGetFD(CFAllocatorRef alloc
, const char *path
, void **bytes
, CFIndex
*length
, CFIndex maxLength
, int extraOpenFlags
, int *fd
) { // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
104 struct statinfo statBuf
;
109 int no_hang_fd
= openAutoFSNoWait();
110 *fd
= open(path
, O_RDONLY
|extraOpenFlags
|CF_OPENFLGS
, 0666);
113 closeAutoFSNoWait(no_hang_fd
);
116 if (fstat(*fd
, &statBuf
) < 0) {
117 int saveerr
= thread_errno();
120 closeAutoFSNoWait(no_hang_fd
);
121 thread_set_errno(saveerr
);
124 if ((statBuf
.st_mode
& S_IFMT
) != S_IFREG
) {
127 closeAutoFSNoWait(no_hang_fd
);
128 thread_set_errno(EACCES
);
131 if (statBuf
.st_size
== 0) {
132 *bytes
= CFAllocatorAllocate(alloc
, 4, 0); // don't return constant string -- it's freed!
133 if (__CFOASafe
) __CFSetLastAllocationEventName(*bytes
, "CFUtilities (file-bytes)");
136 CFIndex desiredLength
;
137 if ((maxLength
>= statBuf
.st_size
) || (maxLength
== 0)) {
138 desiredLength
= statBuf
.st_size
;
140 desiredLength
= maxLength
;
142 *bytes
= CFAllocatorAllocate(alloc
, desiredLength
, 0);
146 closeAutoFSNoWait(no_hang_fd
);
149 if (__CFOASafe
) __CFSetLastAllocationEventName(*bytes
, "CFUtilities (file-bytes)");
150 // fcntl(fd, F_NOCACHE, 1);
151 if (read(*fd
, *bytes
, desiredLength
) < 0) {
152 CFAllocatorDeallocate(alloc
, *bytes
);
155 closeAutoFSNoWait(no_hang_fd
);
158 *length
= desiredLength
;
160 closeAutoFSNoWait(no_hang_fd
);
164 static Boolean
_CFReadBytesFromPath(CFAllocatorRef alloc
, const char *path
, void **bytes
, CFIndex
*length
, CFIndex maxLength
, int extraOpenFlags
) {
166 Boolean result
= _CFReadBytesFromPathAndGetFD(alloc
, path
, bytes
, length
, maxLength
, extraOpenFlags
, &fd
);
172 CF_PRIVATE Boolean
_CFReadBytesFromFile(CFAllocatorRef alloc
, CFURLRef url
, void **bytes
, CFIndex
*length
, CFIndex maxLength
, int extraOpenFlags
) {
173 // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
175 char path
[CFMaxPathSize
];
176 if (!CFURLGetFileSystemRepresentation(url
, true, (uint8_t *)path
, CFMaxPathSize
)) {
179 return _CFReadBytesFromPath(alloc
, (const char *)path
, bytes
, length
, maxLength
, extraOpenFlags
);
182 CF_PRIVATE Boolean
_CFWriteBytesToFile(CFURLRef url
, const void *bytes
, CFIndex length
) {
185 struct statinfo statBuf
;
186 char path
[CFMaxPathSize
];
187 if (!CFURLGetFileSystemRepresentation(url
, true, (uint8_t *)path
, CFMaxPathSize
)) {
191 int no_hang_fd
= openAutoFSNoWait();
193 if (0 == stat(path
, &statBuf
)) {
194 mode
= statBuf
.st_mode
;
195 } else if (thread_errno() != ENOENT
) {
196 closeAutoFSNoWait(no_hang_fd
);
199 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|CF_OPENFLGS
, 0666);
201 closeAutoFSNoWait(no_hang_fd
);
204 if (length
&& write(fd
, bytes
, length
) != length
) {
205 int saveerr
= thread_errno();
207 closeAutoFSNoWait(no_hang_fd
);
208 thread_set_errno(saveerr
);
211 #if DEPLOYMENT_TARGET_WINDOWS
212 FlushFileBuffers((HANDLE
)_get_osfhandle(fd
));
217 closeAutoFSNoWait(no_hang_fd
);
222 /* On Mac OS 8/9, one of dirSpec and dirURL must be non-NULL. On all other platforms, one of path and dirURL must be non-NULL
223 If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */
224 /* Lately, dirSpec appears to be (rightfully) unused. */
225 CF_PRIVATE CFMutableArrayRef
_CFCreateContentsOfDirectory(CFAllocatorRef alloc
, char *dirPath
, void *dirSpec
, CFURLRef dirURL
, CFStringRef matchingAbstractType
) {
226 CFMutableArrayRef files
= NULL
;
227 Boolean releaseBase
= false;
228 CFIndex pathLength
= dirPath
? strlen(dirPath
) : 0;
229 // MF:!!! Need to use four-letter type codes where appropriate.
230 CFStringRef extension
= (matchingAbstractType
? _CFCopyExtensionForAbstractType(matchingAbstractType
) : NULL
);
231 CFIndex targetExtLen
= (extension
? CFStringGetLength(extension
) : 0);
233 #if DEPLOYMENT_TARGET_WINDOWS
234 // This is a replacement for 'dirent' below, and also uses wchar_t to support unicode paths
235 wchar_t extBuff
[CFMaxPathSize
];
236 int extBuffInteriorDotCount
= 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :(
238 if (targetExtLen
> 0) {
239 CFIndex usedBytes
= 0;
240 CFStringGetBytes(extension
, CFRangeMake(0, targetExtLen
), kCFStringEncodingUTF16
, 0, false, (uint8_t *)extBuff
, CFMaxPathLength
, &usedBytes
);
241 targetExtLen
= usedBytes
/ sizeof(wchar_t);
242 extBuff
[targetExtLen
] = '\0';
243 wchar_t *extBuffStr
= (wchar_t *)extBuff
;
244 if (extBuffStr
[0] == '.')
245 extBuffStr
++; //skip the first dot, it's legitimate to have ".plist" for example
247 wchar_t *extBuffDotPtr
= extBuffStr
;
248 while ((extBuffDotPtr
= wcschr(extBuffStr
, '.'))) { //find the next . in the extension...
249 extBuffInteriorDotCount
++;
250 extBuffStr
= extBuffDotPtr
+ 1;
254 wchar_t pathBuf
[CFMaxPathSize
];
257 if (!_CFURLGetWideFileSystemRepresentation(dirURL
, true, pathBuf
, CFMaxPathLength
)) {
258 if (extension
) CFRelease(extension
);
262 pathLength
= wcslen(pathBuf
);
265 // Convert dirPath to a wide representation and put it into our pathBuf
266 // Get the real length of the string in UTF16 characters
267 CFStringRef dirPathStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, dirPath
, kCFStringEncodingUTF8
);
268 CFIndex strLen
= CFStringGetLength(dirPathStr
);
270 // Copy the string into the buffer and terminate
271 CFStringGetCharacters(dirPathStr
, CFRangeMake(0, strLen
), (UniChar
*)pathBuf
);
274 CFRelease(dirPathStr
);
277 WIN32_FIND_DATAW file
;
280 if (pathLength
+ 2 >= CFMaxPathLength
) {
282 CFRelease(extension
);
287 pathBuf
[pathLength
] = '\\';
288 pathBuf
[pathLength
+ 1] = '*';
289 pathBuf
[pathLength
+ 2] = '\0';
290 handle
= FindFirstFileW(pathBuf
, (LPWIN32_FIND_DATAW
)&file
);
291 if (INVALID_HANDLE_VALUE
== handle
) {
292 pathBuf
[pathLength
] = '\0';
294 CFRelease(extension
);
299 files
= CFArrayCreateMutable(alloc
, 0, &kCFTypeArrayCallBacks
);
303 CFIndex namelen
= wcslen(file
.cFileName
);
304 if (file
.cFileName
[0] == '.' && (namelen
== 1 || (namelen
== 2 && file
.cFileName
[1] == '.'))) {
308 if (targetExtLen
> namelen
) continue; // if the extension is the same length or longer than the name, it can't possibly match.
310 if (targetExtLen
> 0) {
311 if (file
.cFileName
[namelen
- 1] == '.') continue; //filename ends with a dot, no extension
313 wchar_t *fileExt
= NULL
;
315 if (extBuffInteriorDotCount
== 0) {
316 fileExt
= wcsrchr(file
.cFileName
, '.');
317 } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar"
318 wchar_t *save
= file
.cFileName
;
319 while ((save
= wcschr(save
, '.')) && !fileExt
) {
320 wchar_t *temp
= save
;
322 while ((temp
= wcschr(temp
, '.'))) {
323 if (++moreDots
== extBuffInteriorDotCount
) break;
325 if (moreDots
== extBuffInteriorDotCount
) {
331 if (!fileExt
) continue; //no extension
333 if (((const wchar_t *)extBuff
)[0] != '.')
334 fileExt
++; //omit the dot if the target file extension omits the dot
336 CFIndex fileExtLen
= wcslen(fileExt
);
338 //if the extensions are different lengths, they can't possibly match
339 if (fileExtLen
!= targetExtLen
) continue;
341 // Check to see if it matches the extension we're looking for.
342 if (_wcsicmp(fileExt
, (const wchar_t *)extBuff
) != 0) {
346 if (dirURL
== NULL
) {
347 CFStringRef dirURLStr
= CFStringCreateWithBytes(alloc
, (const uint8_t *)pathBuf
, pathLength
* sizeof(wchar_t), kCFStringEncodingUTF16
, NO
);
348 dirURL
= CFURLCreateWithFileSystemPath(alloc
, dirURLStr
, kCFURLWindowsPathStyle
, true);
349 CFRelease(dirURLStr
);
352 // MF:!!! What about the trailing slash?
353 CFStringRef fileURLStr
= CFStringCreateWithBytes(alloc
, (const uint8_t *)file
.cFileName
, namelen
* sizeof(wchar_t), kCFStringEncodingUTF16
, NO
);
354 fileURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, fileURLStr
, kCFURLWindowsPathStyle
, (file
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ? true : false, dirURL
);
355 CFArrayAppendValue(files
, fileURL
);
357 CFRelease(fileURLStr
);
358 } while (FindNextFileW(handle
, &file
));
360 pathBuf
[pathLength
] = '\0';
362 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
363 uint8_t extBuff
[CFMaxPathSize
];
364 int extBuffInteriorDotCount
= 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :(
366 if (targetExtLen
> 0) {
367 CFStringGetBytes(extension
, CFRangeMake(0, targetExtLen
), CFStringFileSystemEncoding(), 0, false, extBuff
, CFMaxPathLength
, &targetExtLen
);
368 extBuff
[targetExtLen
] = '\0';
369 char *extBuffStr
= (char *)extBuff
;
370 if (extBuffStr
[0] == '.')
371 extBuffStr
++; //skip the first dot, it's legitimate to have ".plist" for example
373 char *extBuffDotPtr
= extBuffStr
;
374 while ((extBuffDotPtr
= strchr(extBuffStr
, '.'))) { //find the next . in the extension...
375 extBuffInteriorDotCount
++;
376 extBuffStr
= extBuffDotPtr
+ 1;
380 uint8_t pathBuf
[CFMaxPathSize
];
383 if (!CFURLGetFileSystemRepresentation(dirURL
, true, pathBuf
, CFMaxPathLength
)) {
384 if (extension
) CFRelease(extension
);
387 dirPath
= (char *)pathBuf
;
388 pathLength
= strlen(dirPath
);
392 struct dirent buffer
;
396 int no_hang_fd
= __CFProphylacticAutofsAccess
? open("/dev/autofs_nowait", 0) : -1;
398 DIR *dirp
= opendir(dirPath
);
401 CFRelease(extension
);
403 if (-1 != no_hang_fd
) close(no_hang_fd
);
405 // raiseErrno("opendir", path);
407 files
= CFArrayCreateMutable(alloc
, 0, & kCFTypeArrayCallBacks
);
409 while((0 == readdir_r(dirp
, &buffer
, &dp
)) && dp
) {
411 unsigned namelen
= strlen(dp
->d_name
);
413 // skip . & ..; they cause descenders to go berserk
414 if (dp
->d_name
[0] == '.' && (namelen
== 1 || (namelen
== 2 && dp
->d_name
[1] == '.'))) {
418 if (targetExtLen
> namelen
) continue; // if the extension is the same length or longer than the name, it can't possibly match.
420 if (targetExtLen
> 0) {
421 if (dp
->d_name
[namelen
- 1] == '.') continue; //filename ends with a dot, no extension
423 char *fileExt
= NULL
;
424 if (extBuffInteriorDotCount
== 0) {
425 fileExt
= strrchr(dp
->d_name
, '.');
426 } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar"
427 char *save
= dp
->d_name
;
428 while ((save
= strchr(save
, '.')) && !fileExt
) {
431 while ((temp
= strchr(temp
, '.'))) {
432 if (++moreDots
== extBuffInteriorDotCount
) break;
434 if (moreDots
== extBuffInteriorDotCount
) {
440 if (!fileExt
) continue; //no extension
442 if (((char *)extBuff
)[0] != '.')
443 fileExt
++; //omit the dot if the target extension omits the dot; safe, because we checked to make sure it isn't the last character just before
445 size_t fileExtLen
= strlen(fileExt
);
447 //if the extensions are different lengths, they can't possibly match
448 if (fileExtLen
!= targetExtLen
) continue;
450 // Check to see if it matches the extension we're looking for.
451 if (strncmp(fileExt
, (char *)extBuff
, fileExtLen
) != 0) {
455 if (dirURL
== NULL
) {
456 dirURL
= CFURLCreateFromFileSystemRepresentation(alloc
, (uint8_t *)dirPath
, pathLength
, true);
459 if (dp
->d_type
== DT_DIR
|| dp
->d_type
== DT_UNKNOWN
|| dp
->d_type
== DT_LNK
|| dp
->d_type
== DT_WHT
) {
460 Boolean isDir
= (dp
->d_type
== DT_DIR
);
463 char subdirPath
[CFMaxPathLength
];
464 struct statinfo statBuf
;
465 strlcpy(subdirPath
, dirPath
, sizeof(subdirPath
));
466 strlcat(subdirPath
, "/", sizeof(subdirPath
));
467 strlcat(subdirPath
, dp
->d_name
, sizeof(subdirPath
));
468 if (stat(subdirPath
, &statBuf
) == 0) {
469 isDir
= ((statBuf
.st_mode
& S_IFMT
) == S_IFDIR
);
472 #if DEPLOYMENT_TARGET_LINUX
473 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc
, (uint8_t *)dp
->d_name
, namelen
, isDir
, dirURL
);
475 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc
, (uint8_t *)dp
->d_name
, dp
->d_namlen
, isDir
, dirURL
);
478 #if DEPLOYMENT_TARGET_LINUX
479 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc
, (uint8_t *)dp
->d_name
, namelen
, false, dirURL
);
481 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc
, (uint8_t *)dp
->d_name
, dp
->d_namlen
, false, dirURL
);
484 CFArrayAppendValue(files
, fileURL
);
487 err
= closedir(dirp
);
488 if (-1 != no_hang_fd
) close(no_hang_fd
);
495 CFRelease(extension
);
502 #error _CFCreateContentsOfDirectory() unknown architecture, not implemented
507 CFRelease(extension
);
515 CF_PRIVATE SInt32
_CFGetPathProperties(CFAllocatorRef alloc
, char *path
, Boolean
*exists
, SInt32
*posixMode
, int64_t *size
, CFDateRef
*modTime
, SInt32
*ownerID
, CFArrayRef
*dirContents
) {
517 Boolean isDirectory
= false;
519 if ((exists
== NULL
) && (posixMode
== NULL
) && (size
== NULL
) && (modTime
== NULL
) && (ownerID
== NULL
) && (dirContents
== NULL
)) {
524 struct statinfo statBuf
;
526 if (stat(path
, &statBuf
) != 0) {
527 // stat failed, but why?
528 if (thread_errno() == ENOENT
) {
531 return thread_errno();
535 isDirectory
= ((statBuf
.st_mode
& S_IFMT
) == S_IFDIR
);
539 if (exists
!= NULL
) {
540 *exists
= fileExists
;
543 if (posixMode
!= NULL
) {
546 *posixMode
= statBuf
.st_mode
;
556 *size
= statBuf
.st_size
;
563 if (modTime
!= NULL
) {
565 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
566 struct timespec ts
= {statBuf
.st_mtime
, 0};
568 struct timespec ts
= statBuf
.st_mtimespec
;
570 *modTime
= CFDateCreate(alloc
, _CFAbsoluteTimeFromFileTimeSpec(ts
));
576 if (ownerID
!= NULL
) {
579 *ownerID
= statBuf
.st_uid
;
586 if (dirContents
!= NULL
) {
587 if (fileExists
&& isDirectory
) {
589 CFMutableArrayRef contents
= _CFCreateContentsOfDirectory(alloc
, (char *)path
, NULL
, NULL
, NULL
);
592 *dirContents
= contents
;
603 CF_PRIVATE SInt32
_CFGetFileProperties(CFAllocatorRef alloc
, CFURLRef pathURL
, Boolean
*exists
, SInt32
*posixMode
, int64_t *size
, CFDateRef
*modTime
, SInt32
*ownerID
, CFArrayRef
*dirContents
) {
605 char path
[CFMaxPathSize
];
607 if (!CFURLGetFileSystemRepresentation(pathURL
, true, (uint8_t *)path
, CFMaxPathLength
)) {
611 return _CFGetPathProperties(alloc
, path
, exists
, posixMode
, size
, modTime
, ownerID
, dirContents
);
615 #if DEPLOYMENT_TARGET_WINDOWS
616 #define WINDOWS_PATH_SEMANTICS
618 #define UNIX_PATH_SEMANTICS
621 #if defined(WINDOWS_PATH_SEMANTICS)
622 #define CFPreferredSlash ((UniChar)'\\')
623 #define CFPreferredSlashStr CFSTR("\\")
624 #elif defined(UNIX_PATH_SEMANTICS)
625 #define CFPreferredSlash ((UniChar)'/')
626 #define CFPreferredSlashStr CFSTR("/")
628 #error Cannot define NSPreferredSlash on this platform
631 static Boolean
_hasDrive(CFStringRef path
) {
632 if (CFStringGetLength(path
) >= 2) {
633 UniChar firstCharacters
[2];
634 firstCharacters
[0] = CFStringGetCharacterAtIndex(path
, 0);
635 firstCharacters
[1] = CFStringGetCharacterAtIndex(path
, 1);
636 if (firstCharacters
[1] == ':' &&
637 (('A' <= (firstCharacters
)[0] && (firstCharacters
)[0] <= 'Z') ||
638 ('a' <= (firstCharacters
)[0] && (firstCharacters
)[0] <= 'z'))
646 static Boolean
_hasNet(CFStringRef path
) {
647 if (CFStringGetLength(path
) >= 2) {
648 UniChar firstCharacters
[2];
649 firstCharacters
[0] = CFStringGetCharacterAtIndex(path
, 0);
650 firstCharacters
[1] = CFStringGetCharacterAtIndex(path
, 1);
651 if (firstCharacters
[0] == '\\' && firstCharacters
[1] == '\\') return true;
656 #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z')))
657 #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\')
659 #if defined(WINDOWS_PATH_SEMANTICS)
660 #define IS_SLASH(C) ((C) == '\\' || (C) == '/')
661 #elif defined(UNIX_PATH_SEMANTICS)
662 #define IS_SLASH(C) ((C) == '/')
665 CF_PRIVATE UniChar
_CFGetSlash() {
666 return CFPreferredSlash
;
669 CF_PRIVATE CFStringRef
_CFGetSlashStr() {
670 return CFPreferredSlashStr
;
673 CF_PRIVATE Boolean
_CFIsAbsolutePath(UniChar
*unichars
, CFIndex length
) {
677 #if defined(WINDOWS_PATH_SEMANTICS)
678 if (unichars
[0] == '~') {
684 if (HAS_NET(unichars
)) {
690 if (IS_SLASH(unichars
[2]) && HAS_DRIVE(unichars
)) {
694 if (unichars
[0] == '~') {
697 if (IS_SLASH(unichars
[0])) {
704 CF_PRIVATE Boolean
_CFStripTrailingPathSlashes(UniChar
*unichars
, CFIndex
*length
) {
705 Boolean destHasDrive
= (1 < *length
) && HAS_DRIVE(unichars
);
706 CFIndex oldLength
= *length
;
707 while (((destHasDrive
&& 3 < *length
) || (!destHasDrive
&& 1 < *length
)) && IS_SLASH(unichars
[*length
- 1])) {
710 return (oldLength
!= *length
);
713 static Boolean
_CFAppendTrailingPathSlash(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
) {
714 if (maxLength
< *length
+ 1) {
721 if (!IS_SLASH(unichars
[0])) {
722 unichars
[(*length
)++] = CFPreferredSlash
;
726 if (!HAS_DRIVE(unichars
) && !HAS_NET(unichars
)) {
727 unichars
[(*length
)++] = CFPreferredSlash
;
731 unichars
[(*length
)++] = CFPreferredSlash
;
737 CF_PRIVATE
void _CFAppendTrailingPathSlash2(CFMutableStringRef path
) {
738 static const UniChar slash
[1] = { CFPreferredSlash
};
739 CFIndex len
= CFStringGetLength(path
);
741 // Do nothing for this case
742 } else if (len
== 1) {
743 UniChar character
= CFStringGetCharacterAtIndex((CFStringRef
)path
, 0);
744 if (!IS_SLASH(character
)) {
745 CFStringAppendCharacters(path
, slash
, 1);
747 } else if (len
== 2) {
748 if (!_hasDrive(path
) && !_hasNet(path
)) {
749 CFStringAppendCharacters(path
, slash
, 1);
752 CFStringAppendCharacters(path
, slash
, 1);
756 CF_PRIVATE
void _CFAppendConditionalTrailingPathSlash2(CFMutableStringRef path
) {
757 static const UniChar slash
[1] = { CFPreferredSlash
};
758 UniChar character
= CFStringGetCharacterAtIndex((CFStringRef
)path
, CFStringGetLength(path
) - 1);
759 if (!IS_SLASH(character
)) {
760 CFStringAppendCharacters(path
, slash
, 1);
764 CF_PRIVATE
void _CFAppendPathComponent2(CFMutableStringRef path
, CFStringRef component
) {
765 _CFAppendTrailingPathSlash2(path
);
766 CFStringAppend(path
, component
);
769 CF_PRIVATE Boolean
_CFAppendPathComponent(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
, UniChar
*component
, CFIndex componentLength
) {
770 if (0 == componentLength
) {
773 if (maxLength
< *length
+ 1 + componentLength
) {
776 _CFAppendTrailingPathSlash(unichars
, length
, maxLength
);
777 memmove(unichars
+ *length
, component
, componentLength
* sizeof(UniChar
));
778 *length
+= componentLength
;
782 CF_PRIVATE Boolean
_CFAppendPathExtension2(CFMutableStringRef path
, CFStringRef extension
) {
787 if (0 < CFStringGetLength(extension
) && IS_SLASH(CFStringGetCharacterAtIndex(extension
, 0))) {
790 if (1 < CFStringGetLength(extension
)) {
791 if (_hasDrive(extension
)) return false;
794 Boolean destHasDrive
= (1 < CFStringGetLength(path
)) && _hasDrive(path
);
795 while (((destHasDrive
&& 3 < CFStringGetLength(path
)) || (!destHasDrive
&& 1 < CFStringGetLength(path
))) && IS_SLASH(CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
) - 1))) {
796 CFStringDelete(path
, CFRangeMake(CFStringGetLength(path
) - 1, 1));
799 if (CFStringGetLength(path
) == 0) {
803 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
804 CFIndex newLength
= CFStringGetLength(path
);
809 if (IS_SLASH(firstChar
) || firstChar
== '~') {
814 if (_hasDrive(path
) || _hasNet(path
)) {
819 if (IS_SLASH(CFStringGetCharacterAtIndex(path
, 2)) && _hasDrive(path
)) {
824 if (0 < newLength
&& firstChar
== '~') {
825 // Make sure we have a slash in the string
826 if (!CFStringFindWithOptions(path
, CFPreferredSlashStr
, CFRangeMake(1, newLength
- 1), 0, NULL
)) {
830 static const UniChar dotChar
= '.';
831 CFStringAppendCharacters(path
, &dotChar
, 1);
832 CFStringAppend(path
, extension
);
836 CF_PRIVATE Boolean
_CFAppendPathExtension(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
, UniChar
*extension
, CFIndex extensionLength
) {
837 if (maxLength
< *length
+ 1 + extensionLength
) {
840 if ((0 < extensionLength
&& IS_SLASH(extension
[0])) || (1 < extensionLength
&& HAS_DRIVE(extension
))) {
843 _CFStripTrailingPathSlashes(unichars
, length
);
848 if (IS_SLASH(unichars
[0]) || unichars
[0] == '~') {
853 if (HAS_DRIVE(unichars
) || HAS_NET(unichars
)) {
858 if (IS_SLASH(unichars
[2]) && HAS_DRIVE(unichars
)) {
863 if (0 < *length
&& unichars
[0] == '~') {
865 Boolean hasSlash
= false;
866 for (idx
= 1; idx
< *length
; idx
++) {
867 if (IS_SLASH(unichars
[idx
])) {
876 unichars
[(*length
)++] = '.';
877 memmove(unichars
+ *length
, extension
, extensionLength
* sizeof(UniChar
));
878 *length
+= extensionLength
;
882 CF_PRIVATE Boolean
_CFTransmutePathSlashes(UniChar
*unichars
, CFIndex
*length
, UniChar replSlash
) {
883 CFIndex didx
, sidx
, scnt
= *length
;
884 sidx
= (1 < *length
&& HAS_NET(unichars
)) ? 2 : 0;
886 while (sidx
< scnt
) {
887 if (IS_SLASH(unichars
[sidx
])) {
888 unichars
[didx
++] = replSlash
;
889 for (sidx
++; sidx
< scnt
&& IS_SLASH(unichars
[sidx
]); sidx
++);
891 unichars
[didx
++] = unichars
[sidx
++];
895 return (scnt
!= didx
);
898 CF_PRIVATE CFStringRef
_CFCreateLastPathComponent(CFAllocatorRef alloc
, CFStringRef path
, CFIndex
*slashIndex
) {
899 CFIndex len
= CFStringGetLength(path
);
901 // Can't be any path components in a string this short
902 if (slashIndex
) *slashIndex
= -1;
903 return (CFStringRef
)CFRetain(path
);
906 // Find the last slash
907 for (CFIndex i
= len
- 1; i
>= 0; i
--) {
908 if (IS_SLASH(CFStringGetCharacterAtIndex(path
, i
))) {
909 if (slashIndex
) *slashIndex
= i
;
910 return CFStringCreateWithSubstring(alloc
, path
, CFRangeMake(i
+ 1, len
- i
- 1));
914 // Strip any drive if we have one
915 if (len
> 2 && _hasDrive(path
)) {
916 if (slashIndex
) *slashIndex
= -1;
917 return CFStringCreateWithSubstring(alloc
, path
, CFRangeMake(2, len
- 2));
920 // No slash, so just return the same string
921 if (slashIndex
) *slashIndex
= -1;
922 return (CFStringRef
)CFRetain(path
);
925 CF_PRIVATE CFIndex
_CFStartOfLastPathComponent(UniChar
*unichars
, CFIndex length
) {
930 for (idx
= length
- 1; idx
; idx
--) {
931 if (IS_SLASH(unichars
[idx
- 1])) {
935 if ((2 < length
) && HAS_DRIVE(unichars
)) {
941 CF_PRIVATE CFIndex
_CFStartOfLastPathComponent2(CFStringRef path
) {
942 CFIndex length
= CFStringGetLength(path
);
946 for (CFIndex idx
= length
- 1; idx
; idx
--) {
947 if (IS_SLASH(CFStringGetCharacterAtIndex(path
, idx
- 1))) {
951 if ((2 < length
&& _hasDrive(path
))) {
957 CF_PRIVATE CFIndex
_CFLengthAfterDeletingLastPathComponent(UniChar
*unichars
, CFIndex length
) {
962 for (idx
= length
- 1; idx
; idx
--) {
963 if (IS_SLASH(unichars
[idx
- 1])) {
964 if ((idx
!= 1) && (!HAS_DRIVE(unichars
) || idx
!= 3)) {
970 if ((2 < length
) && HAS_DRIVE(unichars
)) {
976 CF_PRIVATE CFIndex
_CFStartOfPathExtension2(CFStringRef path
) {
977 if (CFStringGetLength(path
) < 2) {
980 Boolean hasDrive
= _hasDrive(path
);
981 for (CFIndex idx
= CFStringGetLength(path
) - 1; idx
; idx
--) {
982 UniChar thisCharacter
= CFStringGetCharacterAtIndex(path
, idx
);
983 if (IS_SLASH(thisCharacter
)) {
986 if (thisCharacter
!= '.') {
989 if (idx
== 2 && hasDrive
) {
997 CF_PRIVATE CFIndex
_CFStartOfPathExtension(UniChar
*unichars
, CFIndex length
) {
1002 for (idx
= length
- 1; idx
; idx
--) {
1003 if (IS_SLASH(unichars
[idx
- 1])) {
1006 if (unichars
[idx
] != '.') {
1009 if (idx
== 2 && HAS_DRIVE(unichars
)) {
1017 CF_PRIVATE CFIndex
_CFLengthAfterDeletingPathExtension2(CFStringRef path
) {
1018 CFIndex start
= _CFStartOfPathExtension2(path
);
1019 return ((0 < start
) ? start
: CFStringGetLength(path
));
1022 CF_PRIVATE CFIndex
_CFLengthAfterDeletingPathExtension(UniChar
*unichars
, CFIndex length
) {
1023 CFIndex start
= _CFStartOfPathExtension(unichars
, length
);
1024 return ((0 < start
) ? start
: length
);
1027 #if DEPLOYMENT_TARGET_WINDOWS
1033 // NOTE: on Windows the filename is UTF16-encoded, the fileNameLen is result of wcslen. This function automatically skips '.' and '..', and '._' files
1034 CF_PRIVATE
void _CFIterateDirectory(CFStringRef directoryPath
, Boolean (^fileHandler
)(CFStringRef fileName
, uint8_t fileType
)) {
1035 char directoryPathBuf
[CFMaxPathSize
];
1036 if (!CFStringGetFileSystemRepresentation(directoryPath
, directoryPathBuf
, CFMaxPathSize
)) return;
1038 #if DEPLOYMENT_TARGET_WINDOWS
1039 CFIndex cpathLen
= strlen(directoryPathBuf
);
1040 // Make sure there is room for the additional space we need in the win32 api
1041 if (cpathLen
+ 2 < CFMaxPathSize
) {
1042 WIN32_FIND_DATAW file
;
1045 directoryPathBuf
[cpathLen
++] = '\\';
1046 directoryPathBuf
[cpathLen
++] = '*';
1047 directoryPathBuf
[cpathLen
] = '\0';
1049 // Convert UTF8 buffer to windows appropriate UTF-16LE
1050 // Get the real length of the string in UTF16 characters
1051 CFStringRef cfStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, directoryPathBuf
, kCFStringEncodingUTF8
);
1052 cpathLen
= CFStringGetLength(cfStr
);
1053 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
1054 wchar_t *wideBuf
= (wchar_t *)malloc((cpathLen
+ 1) * sizeof(wchar_t));
1055 // Copy the string into the buffer and terminate
1056 CFStringGetCharacters(cfStr
, CFRangeMake(0, cpathLen
), (UniChar
*)wideBuf
);
1057 wideBuf
[cpathLen
] = 0;
1060 handle
= FindFirstFileW(wideBuf
, (LPWIN32_FIND_DATAW
)&file
);
1061 if (handle
!= INVALID_HANDLE_VALUE
) {
1063 CFIndex nameLen
= wcslen(file
.cFileName
);
1064 if (file
.cFileName
[0] == '.' && (nameLen
== 1 || (nameLen
== 2 && file
.cFileName
[1] == '.'))) {
1068 CFStringRef fileName
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const uint8_t *)file
.cFileName
, nameLen
* sizeof(wchar_t), kCFStringEncodingUTF16
, NO
);
1073 Boolean isDirectory
= file
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
1074 Boolean result
= fileHandler(fileName
, isDirectory
? DT_DIR
: DT_REG
);
1075 CFRelease(fileName
);
1077 } while (FindNextFileW(handle
, &file
));
1085 struct dirent
*dent
;
1086 if ((dirp
= opendir(directoryPathBuf
))) {
1087 while ((dent
= readdir(dirp
))) {
1088 #if DEPLOYMENT_TARGET_LINUX
1089 CFIndex nameLen
= strlen(dent
->d_name
);
1091 CFIndex nameLen
= dent
->d_namlen
;
1093 if (0 == nameLen
|| 0 == dent
->d_fileno
|| ('.' == dent
->d_name
[0] && (1 == nameLen
|| (2 == nameLen
&& '.' == dent
->d_name
[1]) || '_' == dent
->d_name
[1]))) {
1097 CFStringRef fileName
= CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault
, dent
->d_name
);
1102 Boolean result
= fileHandler(fileName
, dent
->d_type
);
1103 CFRelease(fileName
);
1106 (void)closedir(dirp
);