2 * Copyright (c) 2013 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-2013, 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 CF_PRIVATE 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);
143 if (__CFOASafe
) __CFSetLastAllocationEventName(*bytes
, "CFUtilities (file-bytes)");
144 // fcntl(fd, F_NOCACHE, 1);
145 if (read(*fd
, *bytes
, desiredLength
) < 0) {
146 CFAllocatorDeallocate(alloc
, *bytes
);
149 closeAutoFSNoWait(no_hang_fd
);
152 *length
= desiredLength
;
154 closeAutoFSNoWait(no_hang_fd
);
158 CF_PRIVATE Boolean
_CFReadBytesFromPath(CFAllocatorRef alloc
, const char *path
, void **bytes
, CFIndex
*length
, CFIndex maxLength
, int extraOpenFlags
) {
160 Boolean result
= _CFReadBytesFromPathAndGetFD(alloc
, path
, bytes
, length
, maxLength
, extraOpenFlags
, &fd
);
166 CF_PRIVATE Boolean
_CFReadBytesFromFile(CFAllocatorRef alloc
, CFURLRef url
, void **bytes
, CFIndex
*length
, CFIndex maxLength
, int extraOpenFlags
) {
167 // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
169 char path
[CFMaxPathSize
];
170 if (!CFURLGetFileSystemRepresentation(url
, true, (uint8_t *)path
, CFMaxPathSize
)) {
173 return _CFReadBytesFromPath(alloc
, (const char *)path
, bytes
, length
, maxLength
, extraOpenFlags
);
176 CF_PRIVATE Boolean
_CFWriteBytesToFile(CFURLRef url
, const void *bytes
, CFIndex length
) {
179 struct statinfo statBuf
;
180 char path
[CFMaxPathSize
];
181 if (!CFURLGetFileSystemRepresentation(url
, true, (uint8_t *)path
, CFMaxPathSize
)) {
185 int no_hang_fd
= openAutoFSNoWait();
187 if (0 == stat(path
, &statBuf
)) {
188 mode
= statBuf
.st_mode
;
189 } else if (thread_errno() != ENOENT
) {
190 closeAutoFSNoWait(no_hang_fd
);
193 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|CF_OPENFLGS
, 0666);
195 closeAutoFSNoWait(no_hang_fd
);
198 if (length
&& write(fd
, bytes
, length
) != length
) {
199 int saveerr
= thread_errno();
201 closeAutoFSNoWait(no_hang_fd
);
202 thread_set_errno(saveerr
);
205 #if DEPLOYMENT_TARGET_WINDOWS
206 FlushFileBuffers((HANDLE
)_get_osfhandle(fd
));
211 closeAutoFSNoWait(no_hang_fd
);
216 /* 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
217 If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */
218 /* Lately, dirSpec appears to be (rightfully) unused. */
219 CF_PRIVATE CFMutableArrayRef
_CFCreateContentsOfDirectory(CFAllocatorRef alloc
, char *dirPath
, void *dirSpec
, CFURLRef dirURL
, CFStringRef matchingAbstractType
) {
220 CFMutableArrayRef files
= NULL
;
221 Boolean releaseBase
= false;
222 CFIndex pathLength
= dirPath
? strlen(dirPath
) : 0;
223 // MF:!!! Need to use four-letter type codes where appropriate.
224 CFStringRef extension
= (matchingAbstractType
? _CFCopyExtensionForAbstractType(matchingAbstractType
) : NULL
);
225 CFIndex targetExtLen
= (extension
? CFStringGetLength(extension
) : 0);
227 #if DEPLOYMENT_TARGET_WINDOWS
228 // This is a replacement for 'dirent' below, and also uses wchar_t to support unicode paths
229 wchar_t extBuff
[CFMaxPathSize
];
230 int extBuffInteriorDotCount
= 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :(
232 if (targetExtLen
> 0) {
233 CFIndex usedBytes
= 0;
234 CFStringGetBytes(extension
, CFRangeMake(0, targetExtLen
), kCFStringEncodingUTF16
, 0, false, (uint8_t *)extBuff
, CFMaxPathLength
, &usedBytes
);
235 targetExtLen
= usedBytes
/ sizeof(wchar_t);
236 extBuff
[targetExtLen
] = '\0';
237 wchar_t *extBuffStr
= (wchar_t *)extBuff
;
238 if (extBuffStr
[0] == '.')
239 extBuffStr
++; //skip the first dot, it's legitimate to have ".plist" for example
241 wchar_t *extBuffDotPtr
= extBuffStr
;
242 while ((extBuffDotPtr
= wcschr(extBuffStr
, '.'))) { //find the next . in the extension...
243 extBuffInteriorDotCount
++;
244 extBuffStr
= extBuffDotPtr
+ 1;
248 wchar_t pathBuf
[CFMaxPathSize
];
251 if (!_CFURLGetWideFileSystemRepresentation(dirURL
, true, pathBuf
, CFMaxPathLength
)) {
252 if (extension
) CFRelease(extension
);
256 pathLength
= wcslen(pathBuf
);
259 // Convert dirPath to a wide representation and put it into our pathBuf
260 // Get the real length of the string in UTF16 characters
261 CFStringRef dirPathStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, dirPath
, kCFStringEncodingUTF8
);
262 CFIndex strLen
= CFStringGetLength(dirPathStr
);
264 // Copy the string into the buffer and terminate
265 CFStringGetCharacters(dirPathStr
, CFRangeMake(0, strLen
), (UniChar
*)pathBuf
);
268 CFRelease(dirPathStr
);
271 WIN32_FIND_DATAW file
;
274 if (pathLength
+ 2 >= CFMaxPathLength
) {
276 CFRelease(extension
);
281 pathBuf
[pathLength
] = '\\';
282 pathBuf
[pathLength
+ 1] = '*';
283 pathBuf
[pathLength
+ 2] = '\0';
284 handle
= FindFirstFileW(pathBuf
, (LPWIN32_FIND_DATAW
)&file
);
285 if (INVALID_HANDLE_VALUE
== handle
) {
286 pathBuf
[pathLength
] = '\0';
288 CFRelease(extension
);
293 files
= CFArrayCreateMutable(alloc
, 0, &kCFTypeArrayCallBacks
);
297 CFIndex namelen
= wcslen(file
.cFileName
);
298 if (file
.cFileName
[0] == '.' && (namelen
== 1 || (namelen
== 2 && file
.cFileName
[1] == '.'))) {
302 if (targetExtLen
> namelen
) continue; // if the extension is the same length or longer than the name, it can't possibly match.
304 if (targetExtLen
> 0) {
305 if (file
.cFileName
[namelen
- 1] == '.') continue; //filename ends with a dot, no extension
307 wchar_t *fileExt
= NULL
;
309 if (extBuffInteriorDotCount
== 0) {
310 fileExt
= wcsrchr(file
.cFileName
, '.');
311 } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar"
312 wchar_t *save
= file
.cFileName
;
313 while ((save
= wcschr(save
, '.')) && !fileExt
) {
314 wchar_t *temp
= save
;
316 while ((temp
= wcschr(temp
, '.'))) {
317 if (++moreDots
== extBuffInteriorDotCount
) break;
319 if (moreDots
== extBuffInteriorDotCount
) {
325 if (!fileExt
) continue; //no extension
327 if (((const wchar_t *)extBuff
)[0] != '.')
328 fileExt
++; //omit the dot if the target file extension omits the dot
330 CFIndex fileExtLen
= wcslen(fileExt
);
332 //if the extensions are different lengths, they can't possibly match
333 if (fileExtLen
!= targetExtLen
) continue;
335 // Check to see if it matches the extension we're looking for.
336 if (_wcsicmp(fileExt
, (const wchar_t *)extBuff
) != 0) {
340 if (dirURL
== NULL
) {
341 CFStringRef dirURLStr
= CFStringCreateWithBytes(alloc
, (const uint8_t *)pathBuf
, pathLength
* sizeof(wchar_t), kCFStringEncodingUTF16
, NO
);
342 dirURL
= CFURLCreateWithFileSystemPath(alloc
, dirURLStr
, kCFURLWindowsPathStyle
, true);
343 CFRelease(dirURLStr
);
346 // MF:!!! What about the trailing slash?
347 CFStringRef fileURLStr
= CFStringCreateWithBytes(alloc
, (const uint8_t *)file
.cFileName
, namelen
* sizeof(wchar_t), kCFStringEncodingUTF16
, NO
);
348 fileURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, fileURLStr
, kCFURLWindowsPathStyle
, (file
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ? true : false, dirURL
);
349 CFArrayAppendValue(files
, fileURL
);
351 CFRelease(fileURLStr
);
352 } while (FindNextFileW(handle
, &file
));
354 pathBuf
[pathLength
] = '\0';
356 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
357 uint8_t extBuff
[CFMaxPathSize
];
358 int extBuffInteriorDotCount
= 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :(
360 if (targetExtLen
> 0) {
361 CFStringGetBytes(extension
, CFRangeMake(0, targetExtLen
), CFStringFileSystemEncoding(), 0, false, extBuff
, CFMaxPathLength
, &targetExtLen
);
362 extBuff
[targetExtLen
] = '\0';
363 char *extBuffStr
= (char *)extBuff
;
364 if (extBuffStr
[0] == '.')
365 extBuffStr
++; //skip the first dot, it's legitimate to have ".plist" for example
367 char *extBuffDotPtr
= extBuffStr
;
368 while ((extBuffDotPtr
= strchr(extBuffStr
, '.'))) { //find the next . in the extension...
369 extBuffInteriorDotCount
++;
370 extBuffStr
= extBuffDotPtr
+ 1;
374 uint8_t pathBuf
[CFMaxPathSize
];
377 if (!CFURLGetFileSystemRepresentation(dirURL
, true, pathBuf
, CFMaxPathLength
)) {
378 if (extension
) CFRelease(extension
);
381 dirPath
= (char *)pathBuf
;
382 pathLength
= strlen(dirPath
);
386 struct dirent buffer
;
390 int no_hang_fd
= __CFProphylacticAutofsAccess
? open("/dev/autofs_nowait", 0) : -1;
392 DIR *dirp
= opendir(dirPath
);
395 CFRelease(extension
);
397 if (-1 != no_hang_fd
) close(no_hang_fd
);
399 // raiseErrno("opendir", path);
401 files
= CFArrayCreateMutable(alloc
, 0, & kCFTypeArrayCallBacks
);
403 while((0 == readdir_r(dirp
, &buffer
, &dp
)) && dp
) {
405 unsigned namelen
= strlen(dp
->d_name
);
407 // skip . & ..; they cause descenders to go berserk
408 if (dp
->d_name
[0] == '.' && (namelen
== 1 || (namelen
== 2 && dp
->d_name
[1] == '.'))) {
412 if (targetExtLen
> namelen
) continue; // if the extension is the same length or longer than the name, it can't possibly match.
414 if (targetExtLen
> 0) {
415 if (dp
->d_name
[namelen
- 1] == '.') continue; //filename ends with a dot, no extension
417 char *fileExt
= NULL
;
418 if (extBuffInteriorDotCount
== 0) {
419 fileExt
= strrchr(dp
->d_name
, '.');
420 } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar"
421 char *save
= dp
->d_name
;
422 while ((save
= strchr(save
, '.')) && !fileExt
) {
425 while ((temp
= strchr(temp
, '.'))) {
426 if (++moreDots
== extBuffInteriorDotCount
) break;
428 if (moreDots
== extBuffInteriorDotCount
) {
434 if (!fileExt
) continue; //no extension
436 if (((char *)extBuff
)[0] != '.')
437 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
439 size_t fileExtLen
= strlen(fileExt
);
441 //if the extensions are different lengths, they can't possibly match
442 if (fileExtLen
!= targetExtLen
) continue;
444 // Check to see if it matches the extension we're looking for.
445 if (strncmp(fileExt
, (char *)extBuff
, fileExtLen
) != 0) {
449 if (dirURL
== NULL
) {
450 dirURL
= CFURLCreateFromFileSystemRepresentation(alloc
, (uint8_t *)dirPath
, pathLength
, true);
453 if (dp
->d_type
== DT_DIR
|| dp
->d_type
== DT_UNKNOWN
|| dp
->d_type
== DT_LNK
|| dp
->d_type
== DT_WHT
) {
454 Boolean isDir
= (dp
->d_type
== DT_DIR
);
457 char subdirPath
[CFMaxPathLength
];
458 struct statinfo statBuf
;
459 strlcpy(subdirPath
, dirPath
, sizeof(subdirPath
));
460 strlcat(subdirPath
, "/", sizeof(subdirPath
));
461 strlcat(subdirPath
, dp
->d_name
, sizeof(subdirPath
));
462 if (stat(subdirPath
, &statBuf
) == 0) {
463 isDir
= ((statBuf
.st_mode
& S_IFMT
) == S_IFDIR
);
466 #if DEPLOYMENT_TARGET_LINUX
467 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc
, (uint8_t *)dp
->d_name
, namelen
, isDir
, dirURL
);
469 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc
, (uint8_t *)dp
->d_name
, dp
->d_namlen
, isDir
, dirURL
);
472 #if DEPLOYMENT_TARGET_LINUX
473 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc
, (uint8_t *)dp
->d_name
, namelen
, false, dirURL
);
475 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc
, (uint8_t *)dp
->d_name
, dp
->d_namlen
, false, dirURL
);
478 CFArrayAppendValue(files
, fileURL
);
481 err
= closedir(dirp
);
482 if (-1 != no_hang_fd
) close(no_hang_fd
);
489 CFRelease(extension
);
496 #error _CFCreateContentsOfDirectory() unknown architecture, not implemented
501 CFRelease(extension
);
509 CF_PRIVATE SInt32
_CFGetPathProperties(CFAllocatorRef alloc
, char *path
, Boolean
*exists
, SInt32
*posixMode
, int64_t *size
, CFDateRef
*modTime
, SInt32
*ownerID
, CFArrayRef
*dirContents
) {
511 Boolean isDirectory
= false;
513 if ((exists
== NULL
) && (posixMode
== NULL
) && (size
== NULL
) && (modTime
== NULL
) && (ownerID
== NULL
) && (dirContents
== NULL
)) {
518 struct statinfo statBuf
;
520 if (stat(path
, &statBuf
) != 0) {
521 // stat failed, but why?
522 if (thread_errno() == ENOENT
) {
525 return thread_errno();
529 isDirectory
= ((statBuf
.st_mode
& S_IFMT
) == S_IFDIR
);
533 if (exists
!= NULL
) {
534 *exists
= fileExists
;
537 if (posixMode
!= NULL
) {
540 *posixMode
= statBuf
.st_mode
;
550 *size
= statBuf
.st_size
;
557 if (modTime
!= NULL
) {
559 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
560 struct timespec ts
= {statBuf
.st_mtime
, 0};
562 struct timespec ts
= statBuf
.st_mtimespec
;
564 *modTime
= CFDateCreate(alloc
, _CFAbsoluteTimeFromFileTimeSpec(ts
));
570 if (ownerID
!= NULL
) {
573 *ownerID
= statBuf
.st_uid
;
580 if (dirContents
!= NULL
) {
581 if (fileExists
&& isDirectory
) {
583 CFMutableArrayRef contents
= _CFCreateContentsOfDirectory(alloc
, (char *)path
, NULL
, NULL
, NULL
);
586 *dirContents
= contents
;
597 CF_PRIVATE SInt32
_CFGetFileProperties(CFAllocatorRef alloc
, CFURLRef pathURL
, Boolean
*exists
, SInt32
*posixMode
, int64_t *size
, CFDateRef
*modTime
, SInt32
*ownerID
, CFArrayRef
*dirContents
) {
599 char path
[CFMaxPathSize
];
601 if (!CFURLGetFileSystemRepresentation(pathURL
, true, (uint8_t *)path
, CFMaxPathLength
)) {
605 return _CFGetPathProperties(alloc
, path
, exists
, posixMode
, size
, modTime
, ownerID
, dirContents
);
609 #if DEPLOYMENT_TARGET_WINDOWS
610 #define WINDOWS_PATH_SEMANTICS
612 #define UNIX_PATH_SEMANTICS
615 #if defined(WINDOWS_PATH_SEMANTICS)
616 #define CFPreferredSlash ((UniChar)'\\')
617 #define CFPreferredSlashStr CFSTR("\\")
618 #elif defined(UNIX_PATH_SEMANTICS)
619 #define CFPreferredSlash ((UniChar)'/')
620 #define CFPreferredSlashStr CFSTR("/")
622 #error Cannot define NSPreferredSlash on this platform
625 static Boolean
_hasDrive(CFStringRef path
) {
626 if (CFStringGetLength(path
) >= 2) {
627 UniChar firstCharacters
[2];
628 firstCharacters
[0] = CFStringGetCharacterAtIndex(path
, 0);
629 firstCharacters
[1] = CFStringGetCharacterAtIndex(path
, 1);
630 if (firstCharacters
[1] == ':' &&
631 (('A' <= (firstCharacters
)[0] && (firstCharacters
)[0] <= 'Z') ||
632 ('a' <= (firstCharacters
)[0] && (firstCharacters
)[0] <= 'z'))
640 static Boolean
_hasNet(CFStringRef path
) {
641 if (CFStringGetLength(path
) >= 2) {
642 UniChar firstCharacters
[2];
643 firstCharacters
[0] = CFStringGetCharacterAtIndex(path
, 0);
644 firstCharacters
[1] = CFStringGetCharacterAtIndex(path
, 1);
645 if (firstCharacters
[0] == '\\' && firstCharacters
[1] == '\\') return true;
650 #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z')))
651 #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\')
653 #if defined(WINDOWS_PATH_SEMANTICS)
654 #define IS_SLASH(C) ((C) == '\\' || (C) == '/')
655 #elif defined(UNIX_PATH_SEMANTICS)
656 #define IS_SLASH(C) ((C) == '/')
659 CF_PRIVATE UniChar
_CFGetSlash() {
660 return CFPreferredSlash
;
663 CF_PRIVATE CFStringRef
_CFGetSlashStr() {
664 return CFPreferredSlashStr
;
667 CF_PRIVATE Boolean
_CFIsAbsolutePath(UniChar
*unichars
, CFIndex length
) {
671 #if defined(WINDOWS_PATH_SEMANTICS)
672 if (unichars
[0] == '~') {
678 if (HAS_NET(unichars
)) {
684 if (IS_SLASH(unichars
[2]) && HAS_DRIVE(unichars
)) {
688 if (unichars
[0] == '~') {
691 if (IS_SLASH(unichars
[0])) {
698 CF_PRIVATE Boolean
_CFStripTrailingPathSlashes(UniChar
*unichars
, CFIndex
*length
) {
699 Boolean destHasDrive
= (1 < *length
) && HAS_DRIVE(unichars
);
700 CFIndex oldLength
= *length
;
701 while (((destHasDrive
&& 3 < *length
) || (!destHasDrive
&& 1 < *length
)) && IS_SLASH(unichars
[*length
- 1])) {
704 return (oldLength
!= *length
);
707 static Boolean
_CFAppendTrailingPathSlash(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
) {
708 if (maxLength
< *length
+ 1) {
715 if (!IS_SLASH(unichars
[0])) {
716 unichars
[(*length
)++] = CFPreferredSlash
;
720 if (!HAS_DRIVE(unichars
) && !HAS_NET(unichars
)) {
721 unichars
[(*length
)++] = CFPreferredSlash
;
725 unichars
[(*length
)++] = CFPreferredSlash
;
731 CF_PRIVATE
void _CFAppendTrailingPathSlash2(CFMutableStringRef path
) {
732 static const UniChar slash
[1] = { CFPreferredSlash
};
733 CFIndex len
= CFStringGetLength(path
);
735 // Do nothing for this case
736 } else if (len
== 1) {
737 UniChar character
= CFStringGetCharacterAtIndex((CFStringRef
)path
, 0);
738 if (!IS_SLASH(character
)) {
739 CFStringAppendCharacters(path
, slash
, 1);
741 } else if (len
== 2) {
742 if (!_hasDrive(path
) && !_hasNet(path
)) {
743 CFStringAppendCharacters(path
, slash
, 1);
746 CFStringAppendCharacters(path
, slash
, 1);
750 CF_PRIVATE
void _CFAppendConditionalTrailingPathSlash2(CFMutableStringRef path
) {
751 static const UniChar slash
[1] = { CFPreferredSlash
};
752 UniChar character
= CFStringGetCharacterAtIndex((CFStringRef
)path
, CFStringGetLength(path
) - 1);
753 if (!IS_SLASH(character
)) {
754 CFStringAppendCharacters(path
, slash
, 1);
758 CF_PRIVATE
void _CFAppendPathComponent2(CFMutableStringRef path
, CFStringRef component
) {
759 _CFAppendTrailingPathSlash2(path
);
760 CFStringAppend(path
, component
);
763 CF_PRIVATE Boolean
_CFAppendPathComponent(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
, UniChar
*component
, CFIndex componentLength
) {
764 if (0 == componentLength
) {
767 if (maxLength
< *length
+ 1 + componentLength
) {
770 _CFAppendTrailingPathSlash(unichars
, length
, maxLength
);
771 memmove(unichars
+ *length
, component
, componentLength
* sizeof(UniChar
));
772 *length
+= componentLength
;
776 CF_PRIVATE Boolean
_CFAppendPathExtension2(CFMutableStringRef path
, CFStringRef extension
) {
781 if (0 < CFStringGetLength(extension
) && IS_SLASH(CFStringGetCharacterAtIndex(extension
, 0))) {
784 if (1 < CFStringGetLength(extension
)) {
785 if (_hasDrive(extension
)) return false;
788 Boolean destHasDrive
= (1 < CFStringGetLength(path
)) && _hasDrive(path
);
789 while (((destHasDrive
&& 3 < CFStringGetLength(path
)) || (!destHasDrive
&& 1 < CFStringGetLength(path
))) && IS_SLASH(CFStringGetCharacterAtIndex(path
, CFStringGetLength(path
) - 1))) {
790 CFStringDelete(path
, CFRangeMake(CFStringGetLength(path
) - 1, 1));
793 if (CFStringGetLength(path
) == 0) {
797 UniChar firstChar
= CFStringGetCharacterAtIndex(path
, 0);
798 CFIndex newLength
= CFStringGetLength(path
);
803 if (IS_SLASH(firstChar
) || firstChar
== '~') {
808 if (_hasDrive(path
) || _hasNet(path
)) {
813 if (IS_SLASH(CFStringGetCharacterAtIndex(path
, 2)) && _hasDrive(path
)) {
818 if (0 < newLength
&& firstChar
== '~') {
819 // Make sure we have a slash in the string
820 if (!CFStringFindWithOptions(path
, CFPreferredSlashStr
, CFRangeMake(1, newLength
- 1), 0, NULL
)) {
824 static const UniChar dotChar
= '.';
825 CFStringAppendCharacters(path
, &dotChar
, 1);
826 CFStringAppend(path
, extension
);
830 CF_PRIVATE Boolean
_CFAppendPathExtension(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
, UniChar
*extension
, CFIndex extensionLength
) {
831 if (maxLength
< *length
+ 1 + extensionLength
) {
834 if ((0 < extensionLength
&& IS_SLASH(extension
[0])) || (1 < extensionLength
&& HAS_DRIVE(extension
))) {
837 _CFStripTrailingPathSlashes(unichars
, length
);
842 if (IS_SLASH(unichars
[0]) || unichars
[0] == '~') {
847 if (HAS_DRIVE(unichars
) || HAS_NET(unichars
)) {
852 if (IS_SLASH(unichars
[2]) && HAS_DRIVE(unichars
)) {
857 if (0 < *length
&& unichars
[0] == '~') {
859 Boolean hasSlash
= false;
860 for (idx
= 1; idx
< *length
; idx
++) {
861 if (IS_SLASH(unichars
[idx
])) {
870 unichars
[(*length
)++] = '.';
871 memmove(unichars
+ *length
, extension
, extensionLength
* sizeof(UniChar
));
872 *length
+= extensionLength
;
876 CF_PRIVATE Boolean
_CFTransmutePathSlashes(UniChar
*unichars
, CFIndex
*length
, UniChar replSlash
) {
877 CFIndex didx
, sidx
, scnt
= *length
;
878 sidx
= (1 < *length
&& HAS_NET(unichars
)) ? 2 : 0;
880 while (sidx
< scnt
) {
881 if (IS_SLASH(unichars
[sidx
])) {
882 unichars
[didx
++] = replSlash
;
883 for (sidx
++; sidx
< scnt
&& IS_SLASH(unichars
[sidx
]); sidx
++);
885 unichars
[didx
++] = unichars
[sidx
++];
889 return (scnt
!= didx
);
892 CF_PRIVATE CFStringRef
_CFCreateLastPathComponent(CFAllocatorRef alloc
, CFStringRef path
, CFIndex
*slashIndex
) {
893 CFIndex len
= CFStringGetLength(path
);
895 // Can't be any path components in a string this short
896 if (slashIndex
) *slashIndex
= -1;
897 return (CFStringRef
)CFRetain(path
);
900 // Find the last slash
901 for (CFIndex i
= len
- 1; i
>= 0; i
--) {
902 if (IS_SLASH(CFStringGetCharacterAtIndex(path
, i
))) {
903 if (slashIndex
) *slashIndex
= i
;
904 return CFStringCreateWithSubstring(alloc
, path
, CFRangeMake(i
+ 1, len
- i
- 1));
908 // Strip any drive if we have one
909 if (len
> 2 && _hasDrive(path
)) {
910 if (slashIndex
) *slashIndex
= -1;
911 return CFStringCreateWithSubstring(alloc
, path
, CFRangeMake(2, len
- 2));
914 // No slash, so just return the same string
915 if (slashIndex
) *slashIndex
= -1;
916 return (CFStringRef
)CFRetain(path
);
919 CF_PRIVATE CFIndex
_CFStartOfLastPathComponent(UniChar
*unichars
, CFIndex length
) {
924 for (idx
= length
- 1; idx
; idx
--) {
925 if (IS_SLASH(unichars
[idx
- 1])) {
929 if ((2 < length
) && HAS_DRIVE(unichars
)) {
935 CF_PRIVATE CFIndex
_CFStartOfLastPathComponent2(CFStringRef path
) {
936 CFIndex length
= CFStringGetLength(path
);
940 for (CFIndex idx
= length
- 1; idx
; idx
--) {
941 if (IS_SLASH(CFStringGetCharacterAtIndex(path
, idx
- 1))) {
945 if ((2 < length
&& _hasDrive(path
))) {
951 CF_PRIVATE CFIndex
_CFLengthAfterDeletingLastPathComponent(UniChar
*unichars
, CFIndex length
) {
956 for (idx
= length
- 1; idx
; idx
--) {
957 if (IS_SLASH(unichars
[idx
- 1])) {
958 if ((idx
!= 1) && (!HAS_DRIVE(unichars
) || idx
!= 3)) {
964 if ((2 < length
) && HAS_DRIVE(unichars
)) {
970 CF_PRIVATE CFIndex
_CFStartOfPathExtension2(CFStringRef path
) {
971 if (CFStringGetLength(path
) < 2) {
974 Boolean hasDrive
= _hasDrive(path
);
975 for (CFIndex idx
= CFStringGetLength(path
) - 1; idx
; idx
--) {
976 UniChar thisCharacter
= CFStringGetCharacterAtIndex(path
, idx
);
977 if (IS_SLASH(thisCharacter
)) {
980 if (thisCharacter
!= '.') {
983 if (idx
== 2 && hasDrive
) {
991 CF_PRIVATE CFIndex
_CFStartOfPathExtension(UniChar
*unichars
, CFIndex length
) {
996 for (idx
= length
- 1; idx
; idx
--) {
997 if (IS_SLASH(unichars
[idx
- 1])) {
1000 if (unichars
[idx
] != '.') {
1003 if (idx
== 2 && HAS_DRIVE(unichars
)) {
1011 CF_PRIVATE CFIndex
_CFLengthAfterDeletingPathExtension2(CFStringRef path
) {
1012 CFIndex start
= _CFStartOfPathExtension2(path
);
1013 return ((0 < start
) ? start
: CFStringGetLength(path
));
1016 CF_PRIVATE CFIndex
_CFLengthAfterDeletingPathExtension(UniChar
*unichars
, CFIndex length
) {
1017 CFIndex start
= _CFStartOfPathExtension(unichars
, length
);
1018 return ((0 < start
) ? start
: length
);
1021 #if DEPLOYMENT_TARGET_WINDOWS
1027 // NOTE: on Windows the filename is UTF16-encoded, the fileNameLen is result of wcslen. This function automatically skips '.' and '..', and '._' files
1028 CF_PRIVATE
void _CFIterateDirectory(CFStringRef directoryPath
, Boolean (^fileHandler
)(CFStringRef fileName
, uint8_t fileType
)) {
1029 char directoryPathBuf
[CFMaxPathSize
];
1030 if (!CFStringGetFileSystemRepresentation(directoryPath
, directoryPathBuf
, CFMaxPathSize
)) return;
1032 #if DEPLOYMENT_TARGET_WINDOWS
1033 CFIndex cpathLen
= strlen(directoryPathBuf
);
1034 // Make sure there is room for the additional space we need in the win32 api
1035 if (cpathLen
+ 2 < CFMaxPathSize
) {
1036 WIN32_FIND_DATAW file
;
1039 directoryPathBuf
[cpathLen
++] = '\\';
1040 directoryPathBuf
[cpathLen
++] = '*';
1041 directoryPathBuf
[cpathLen
] = '\0';
1043 // Convert UTF8 buffer to windows appropriate UTF-16LE
1044 // Get the real length of the string in UTF16 characters
1045 CFStringRef cfStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, directoryPathBuf
, kCFStringEncodingUTF8
);
1046 cpathLen
= CFStringGetLength(cfStr
);
1047 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
1048 wchar_t *wideBuf
= (wchar_t *)malloc((cpathLen
+ 1) * sizeof(wchar_t));
1049 // Copy the string into the buffer and terminate
1050 CFStringGetCharacters(cfStr
, CFRangeMake(0, cpathLen
), (UniChar
*)wideBuf
);
1051 wideBuf
[cpathLen
] = 0;
1054 handle
= FindFirstFileW(wideBuf
, (LPWIN32_FIND_DATAW
)&file
);
1055 if (handle
!= INVALID_HANDLE_VALUE
) {
1057 CFIndex nameLen
= wcslen(file
.cFileName
);
1058 if (file
.cFileName
[0] == '.' && (nameLen
== 1 || (nameLen
== 2 && file
.cFileName
[1] == '.'))) {
1062 CFStringRef fileName
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const uint8_t *)file
.cFileName
, nameLen
* sizeof(wchar_t), kCFStringEncodingUTF16
, NO
);
1067 Boolean isDirectory
= file
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
1068 Boolean result
= fileHandler(fileName
, isDirectory
? DT_DIR
: DT_REG
);
1069 CFRelease(fileName
);
1071 } while (FindNextFileW(handle
, &file
));
1079 struct dirent
*dent
;
1080 if ((dirp
= opendir(directoryPathBuf
))) {
1081 while ((dent
= readdir(dirp
))) {
1082 #if DEPLOYMENT_TARGET_LINUX
1083 CFIndex nameLen
= strlen(dent
->d_name
);
1085 CFIndex nameLen
= dent
->d_namlen
;
1087 if (0 == nameLen
|| 0 == dent
->d_fileno
|| ('.' == dent
->d_name
[0] && (1 == nameLen
|| (2 == nameLen
&& '.' == dent
->d_name
[1]) || '_' == dent
->d_name
[1]))) {
1091 CFStringRef fileName
= CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault
, dent
->d_name
);
1096 Boolean result
= fileHandler(fileName
, dent
->d_type
);
1097 CFRelease(fileName
);
1100 (void)closedir(dirp
);