2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright 1999-2002, Apple, Inc. All rights reserved.
27 Responsibility: Christopher Kane
30 #include "CFInternal.h"
32 #if defined(__WIN32__)
37 #define timeval xxx_timeval
38 #define BOOLEAN xxx_BOOLEAN
53 #include <sys/types.h>
60 #if defined(__WIN32__)
61 #define CF_OPENFLGS (_O_BINARY|_O_NOINHERIT)
63 #define CF_OPENFLGS (0)
67 __private_extern__ CFStringRef
_CFCopyExtensionForAbstractType(CFStringRef abstractType
) {
68 return (abstractType
? CFRetain(abstractType
) : NULL
);
72 __private_extern__ Boolean
_CFCreateDirectory(const char *path
) {
73 #if defined(__WIN32__)
74 return CreateDirectoryA(path
, (LPSECURITY_ATTRIBUTES
)NULL
);
76 return ((mkdir(path
, 0777) == 0) ? true : false);
80 __private_extern__ Boolean
_CFRemoveDirectory(const char *path
) {
81 #if defined(__WIN32__)
82 return RemoveDirectoryA(path
);
84 return ((rmdir(path
) == 0) ? true : false);
88 __private_extern__ Boolean
_CFDeleteFile(const char *path
) {
89 #if defined(__WIN32__)
90 return DeleteFileA(path
);
92 return unlink(path
) == 0;
96 __private_extern__ Boolean
_CFReadBytesFromFile(CFAllocatorRef alloc
, CFURLRef url
, void **bytes
, CFIndex
*length
, CFIndex maxLength
) {
97 // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
100 char path
[CFMaxPathSize
];
101 if (!CFURLGetFileSystemRepresentation(url
, true, path
, CFMaxPathSize
)) {
107 __CFSetNastyFile(url
);
109 #if defined(__WIN32__)
110 fd
= open(path
, O_RDONLY
|CF_OPENFLGS
, 0666|_S_IREAD
);
112 fd
= open(path
, O_RDONLY
|CF_OPENFLGS
, 0666);
117 if (fstat(fd
, &statBuf
) < 0) {
118 int saveerr
= thread_errno();
120 thread_set_errno(saveerr
);
123 if ((statBuf
.st_mode
& S_IFMT
) != S_IFREG
) {
125 thread_set_errno(EACCES
);
128 if (statBuf
.st_size
== 0) {
129 *bytes
= CFAllocatorAllocate(alloc
, 4, 0); // don't return constant string -- it's freed!
130 if (__CFOASafe
) __CFSetLastAllocationEventName(*bytes
, "CFUtilities (file-bytes)");
133 CFIndex desiredLength
;
134 if ((maxLength
>= statBuf
.st_size
) || (maxLength
== 0)) {
135 desiredLength
= statBuf
.st_size
;
137 desiredLength
= maxLength
;
139 *bytes
= CFAllocatorAllocate(alloc
, desiredLength
, 0);
140 if (__CFOASafe
) __CFSetLastAllocationEventName(*bytes
, "CFUtilities (file-bytes)");
141 if (read(fd
, *bytes
, desiredLength
) < 0) {
142 CFAllocatorDeallocate(alloc
, *bytes
);
146 *length
= desiredLength
;
152 __private_extern__ Boolean
_CFWriteBytesToFile(CFURLRef url
, const void *bytes
, CFIndex length
) {
156 char path
[CFMaxPathSize
];
157 if (!CFURLGetFileSystemRepresentation(url
, true, path
, CFMaxPathSize
)) {
161 #if defined(__WIN32__)
168 if (0 == stat(path
, &statBuf
)) {
169 mode
= statBuf
.st_mode
;
170 } else if (thread_errno() != ENOENT
) {
173 #if defined(__WIN32__)
174 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|CF_OPENFLGS
, 0666|_S_IWRITE
);
176 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|CF_OPENFLGS
, 0666);
181 if (length
&& write(fd
, bytes
, length
) != length
) {
182 int saveerr
= thread_errno();
184 thread_set_errno(saveerr
);
187 #if defined(__WIN32__)
188 FlushFileBuffers((HANDLE
)_get_osfhandle(fd
));
197 /* 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
198 If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */
199 __private_extern__ CFMutableArrayRef
_CFContentsOfDirectory(CFAllocatorRef alloc
, char *dirPath
, void *dirSpec
, CFURLRef dirURL
, CFStringRef matchingAbstractType
) {
200 CFMutableArrayRef files
= NULL
;
201 Boolean releaseBase
= false;
202 CFIndex pathLength
= dirPath
? strlen(dirPath
) : 0;
203 // MF:!!! Need to use four-letter type codes where appropriate.
204 CFStringRef extension
= (matchingAbstractType
? _CFCopyExtensionForAbstractType(matchingAbstractType
) : NULL
);
205 CFIndex extLen
= (extension
? CFStringGetLength(extension
) : 0);
206 uint8_t extBuff
[CFMaxPathSize
];
208 #if defined(__WIN32__)
209 /* Windows Variables */
210 /* The Win32 code has not been updated for:
211 path has been renamed dirPath
212 base has been renamed dirURL
213 dirPath may be NULL (in which case dirURL is not)
214 if dirPath is NULL, pathLength is 0
216 WIN32_FIND_DATA file
;
218 #elif defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__)
219 /* Solaris and HPUX Variables */
220 /* The Solaris and HPUX code has not been updated for:
221 base has been renamed dirURL
222 dirPath may be NULL (in which case dirURL is not)
223 if dirPath is NULL, pathLength is 0
228 #elif defined(__MACH__)
229 /* Mac OS X Variables */
233 uint8_t pathBuf
[CFMaxPathSize
];
238 CFStringGetBytes(extension
, CFRangeMake(0, extLen
), CFStringFileSystemEncoding(), 0, false, extBuff
, CFMaxPathSize
, &extLen
);
239 extBuff
[extLen
] = '\0';
242 #if defined(__WIN32__)
243 /* Windows Implementation */
245 if (pathLength
+ 2 >= CFMaxPathLength
) {
247 CFRelease(extension
);
251 if (NULL
!= dirPath
) {
252 dirPath
[pathLength
] = '\'';
253 dirPath
[pathLength
+ 1] = '*';
254 dirPath
[pathLength
+ 2] = '\0';
255 handle
= FindFirstFileA(dirPath
, &file
);
256 if (INVALID_HANDLE_VALUE
== handle
) {
257 dirPath
[pathLength
] = '\0';
259 CFRelease(extension
);
266 files
= CFArrayCreateMutable(alloc
, 0, &kCFTypeArrayCallBacks
);
270 CFIndex namelen
= strlen(file
.cFileName
);
271 if (file
.cFileName
[0] == '.' && (namelen
== 1 || (namelen
== 2 && file
.cFileName
[1] == '.'))) {
275 // Check to see if it matches the extension we're looking for.
276 if (_stricmp(&(file
.cFileName
[namelen
- extLen
]), extBuff
) != 0) {
280 if (dirURL
== NULL
) {
281 dirURL
= CFURLCreateFromFileSystemRepresentation(alloc
, dirPath
, pathLength
, true);
284 // MF:!!! What about the trailing slash?
285 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc
, file
.cFileName
, namelen
, false, dirURL
);
286 CFArrayAppendValue(files
, fileURL
);
288 } while (FindNextFileA(handle
, &file
));
290 dirPath
[pathLength
] = '\0';
292 #elif defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__)
293 /* Solaris and HPUX Implementation */
295 dirp
= opendir(dirPath
);
298 CFRelease(extension
);
301 // raiseErrno("opendir", path);
303 files
= CFArrayCreateMutable(alloc
, 0, & kCFTypeArrayCallBacks
);
305 while((dp
= readdir(dirp
)) != NULL
) {
307 unsigned namelen
= strlen(dp
->d_name
);
309 // skip . & ..; they cause descenders to go berserk
310 if (dp
->d_name
[0] == '.' && (namelen
== 1 || (namelen
== 2 && dp
->d_name
[1] == '.'))) {
315 // Check to see if it matches the extension we're looking for.
316 if (strncmp(&(dp
->d_name
[namelen
- extLen
]), extBuff
, extLen
) != 0) {
320 if (dirURL
== NULL
) {
321 dirURL
= CFURLCreateFromFileSystemRepresentation(alloc
, dirPath
, pathLength
, true);
324 // MF:!!! What about the trailing slash?
325 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc
, dp
->d_name
, namelen
, false, dirURL
);
326 CFArrayAppendValue(files
, fileURL
);
329 err
= closedir(dirp
);
336 CFRelease(extension
);
339 // raiseErrno("closedir", path);
342 #elif defined(__MACH__)
343 /* Mac OS X Variables - repeated for convenience */
347 // UInt8 pathBuf[CFMaxPathSize];
348 /* Mac OS X Implementation */
351 if (!CFURLGetFileSystemRepresentation(dirURL
, true, pathBuf
, CFMaxPathLength
)) {
352 if (extension
) CFRelease(extension
);
356 pathLength
= strlen(dirPath
);
359 fd
= open(dirPath
, O_RDONLY
, 0777);
362 CFRelease(extension
);
366 files
= CFArrayCreateMutable(alloc
, 0, &kCFTypeArrayCallBacks
);
368 while ((numread
= getdirentries(fd
, dirge
, sizeof(dirge
), &basep
)) > 0) {
370 for (dent
= (struct dirent
*)dirge
; dent
< (struct dirent
*)(dirge
+ numread
); dent
= (struct dirent
*)((char *)dent
+ dent
->d_reclen
)) {
374 nameLen
= dent
->d_namlen
;
375 // skip . & ..; they cause descenders to go berserk
376 if (0 == dent
->d_fileno
|| (dent
->d_name
[0] == '.' && (nameLen
== 1 || (nameLen
== 2 && dent
->d_name
[1] == '.')))) {
380 // Check to see if it matches the extension we're looking for.
381 if (strncmp(&(dent
->d_name
[nameLen
- extLen
]), extBuff
, extLen
) != 0) {
385 if (dirURL
== NULL
) {
386 dirURL
= CFURLCreateFromFileSystemRepresentation(alloc
, dirPath
, pathLength
, true);
390 if (dent
->d_type
== DT_DIR
|| dent
->d_type
== DT_UNKNOWN
) {
391 Boolean isDir
= (dent
->d_type
== DT_DIR
);
394 char subdirPath
[CFMaxPathLength
];
396 strncpy(subdirPath
, dirPath
, pathLength
);
397 subdirPath
[pathLength
] = '/';
398 strncpy(subdirPath
+ pathLength
+ 1, dent
->d_name
, nameLen
);
399 subdirPath
[pathLength
+ nameLen
+ 1] = '\0';
400 if (stat(subdirPath
, &statBuf
) == 0) {
401 isDir
= ((statBuf
.st_mode
& S_IFMT
) == S_IFDIR
);
404 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc
, dent
->d_name
, nameLen
, isDir
, dirURL
);
406 fileURL
= CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc
, dent
->d_name
, nameLen
, false, dirURL
);
408 CFArrayAppendValue(files
, fileURL
);
419 CFRelease(extension
);
425 #error _CFContentsOfDirectory() unknown architechture, not implemented
430 CFRelease(extension
);
438 __private_extern__ SInt32
_CFGetFileProperties(CFAllocatorRef alloc
, CFURLRef pathURL
, Boolean
*exists
, SInt32
*posixMode
, int64_t *size
, CFDateRef
*modTime
, SInt32
*ownerID
, CFArrayRef
*dirContents
) {
440 Boolean isDirectory
= false;
443 char path
[CFMaxPathLength
];
445 if ((exists
== NULL
) && (posixMode
== NULL
) && (size
== NULL
) && (modTime
== NULL
) && (ownerID
== NULL
) && (dirContents
== NULL
)) {
450 if (!CFURLGetFileSystemRepresentation(pathURL
, true, path
, CFMaxPathLength
)) {
454 if (stat(path
, &statBuf
) != 0) {
455 // stat failed, but why?
456 if (thread_errno() == ENOENT
) {
459 return thread_errno();
463 isDirectory
= ((statBuf
.st_mode
& S_IFMT
) == S_IFDIR
);
467 if (exists
!= NULL
) {
468 *exists
= fileExists
;
471 if (posixMode
!= NULL
) {
474 *posixMode
= statBuf
.st_mode
;
484 *size
= statBuf
.st_size
;
491 if (modTime
!= NULL
) {
493 CFTimeInterval theTime
;
495 theTime
= kCFAbsoluteTimeIntervalSince1970
+ statBuf
.st_mtime
;
497 *modTime
= CFDateCreate(alloc
, theTime
);
503 if (ownerID
!= NULL
) {
506 *ownerID
= statBuf
.st_uid
;
513 if (dirContents
!= NULL
) {
514 if (fileExists
&& isDirectory
) {
516 CFMutableArrayRef contents
= _CFContentsOfDirectory(alloc
, path
, NULL
, pathURL
, NULL
);
519 *dirContents
= contents
;
531 // MF:!!! Should pull in the rest of the UniChar based path utils from Foundation.
532 #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__)
533 #define UNIX_PATH_SEMANTICS
534 #elif defined(__WIN32__)
535 #define WINDOWS_PATH_SEMANTICS
537 #error Unknown platform
540 #if defined(WINDOWS_PATH_SEMANTICS)
541 #define CFPreferredSlash ((UniChar)'\\')
542 #elif defined(UNIX_PATH_SEMANTICS)
543 #define CFPreferredSlash ((UniChar)'/')
544 #elif defined(HFS_PATH_SEMANTICS)
545 #define CFPreferredSlash ((UniChar)':')
547 #error Cannot define NSPreferredSlash on this platform
550 #if defined(HFS_PATH_SEMANTICS)
551 #define HAS_DRIVE(S) (false)
552 #define HAS_NET(S) (false)
554 #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z')))
555 #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\')
558 #if defined(WINDOWS_PATH_SEMANTICS)
559 #define IS_SLASH(C) ((C) == '\\' || (C) == '/')
560 #elif defined(UNIX_PATH_SEMANTICS)
561 #define IS_SLASH(C) ((C) == '/')
562 #elif defined(HFS_PATH_SEMANTICS)
563 #define IS_SLASH(C) ((C) == ':')
566 __private_extern__ Boolean
_CFIsAbsolutePath(UniChar
*unichars
, CFIndex length
) {
570 #if defined(WINDOWS_PATH_SEMANTICS)
571 if (unichars
[0] == '~') {
577 if (HAS_NET(unichars
)) {
583 if (IS_SLASH(unichars
[2]) && HAS_DRIVE(unichars
)) {
586 #elif defined(HFS_PATH_SEMANTICS)
587 return !IS_SLASH(unichars
[0]);
589 if (unichars
[0] == '~') {
592 if (IS_SLASH(unichars
[0])) {
599 __private_extern__ Boolean
_CFStripTrailingPathSlashes(UniChar
*unichars
, CFIndex
*length
) {
600 Boolean destHasDrive
= (1 < *length
) && HAS_DRIVE(unichars
);
601 CFIndex oldLength
= *length
;
602 while (((destHasDrive
&& 3 < *length
) || (!destHasDrive
&& 1 < *length
)) && IS_SLASH(unichars
[*length
- 1])) {
605 return (oldLength
!= *length
);
608 __private_extern__ Boolean
_CFAppendPathComponent(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
, UniChar
*component
, CFIndex componentLength
) {
609 if (0 == componentLength
) {
612 if (maxLength
< *length
+ 1 + componentLength
) {
619 if (!IS_SLASH(unichars
[0])) {
620 unichars
[(*length
)++] = CFPreferredSlash
;
624 if (!HAS_DRIVE(unichars
) && !HAS_NET(unichars
)) {
625 unichars
[(*length
)++] = CFPreferredSlash
;
629 unichars
[(*length
)++] = CFPreferredSlash
;
632 memmove(unichars
+ *length
, component
, componentLength
* sizeof(UniChar
));
633 *length
+= componentLength
;
637 __private_extern__ Boolean
_CFAppendPathExtension(UniChar
*unichars
, CFIndex
*length
, CFIndex maxLength
, UniChar
*extension
, CFIndex extensionLength
) {
638 if (maxLength
< *length
+ 1 + extensionLength
) {
641 if ((0 < extensionLength
&& IS_SLASH(extension
[0])) || (1 < extensionLength
&& HAS_DRIVE(extension
))) {
644 _CFStripTrailingPathSlashes(unichars
, length
);
649 if (IS_SLASH(unichars
[0]) || unichars
[0] == '~') {
654 if (HAS_DRIVE(unichars
) || HAS_NET(unichars
)) {
659 if (IS_SLASH(unichars
[2]) && HAS_DRIVE(unichars
)) {
664 if (0 < *length
&& unichars
[0] == '~') {
666 Boolean hasSlash
= false;
667 for (idx
= 1; idx
< *length
; idx
++) {
668 if (IS_SLASH(unichars
[idx
])) {
677 unichars
[(*length
)++] = '.';
678 memmove(unichars
+ *length
, extension
, extensionLength
* sizeof(UniChar
));
679 *length
+= extensionLength
;
683 __private_extern__ Boolean
_CFTransmutePathSlashes(UniChar
*unichars
, CFIndex
*length
, UniChar replSlash
) {
684 CFIndex didx
, sidx
, scnt
= *length
;
685 sidx
= (1 < *length
&& HAS_NET(unichars
)) ? 2 : 0;
687 while (sidx
< scnt
) {
688 if (IS_SLASH(unichars
[sidx
])) {
689 unichars
[didx
++] = replSlash
;
690 for (sidx
++; sidx
< scnt
&& IS_SLASH(unichars
[sidx
]); sidx
++);
692 unichars
[didx
++] = unichars
[sidx
++];
696 return (scnt
!= didx
);
699 __private_extern__ CFIndex
_CFStartOfLastPathComponent(UniChar
*unichars
, CFIndex length
) {
704 for (idx
= length
- 1; idx
; idx
--) {
705 if (IS_SLASH(unichars
[idx
- 1])) {
709 if ((2 < length
) && HAS_DRIVE(unichars
)) {
715 __private_extern__ CFIndex
_CFLengthAfterDeletingLastPathComponent(UniChar
*unichars
, CFIndex length
) {
720 for (idx
= length
- 1; idx
; idx
--) {
721 if (IS_SLASH(unichars
[idx
- 1])) {
722 if ((idx
!= 1) && (!HAS_DRIVE(unichars
) || idx
!= 3)) {
728 if ((2 < length
) && HAS_DRIVE(unichars
)) {
734 __private_extern__ CFIndex
_CFStartOfPathExtension(UniChar
*unichars
, CFIndex length
) {
739 for (idx
= length
- 1; idx
; idx
--) {
740 if (IS_SLASH(unichars
[idx
- 1])) {
743 if (unichars
[idx
] != '.') {
746 if (idx
== 2 && HAS_DRIVE(unichars
)) {
754 __private_extern__ CFIndex
_CFLengthAfterDeletingPathExtension(UniChar
*unichars
, CFIndex length
) {
755 CFIndex start
= _CFStartOfPathExtension(unichars
, length
);
756 return ((0 < start
) ? start
: length
);
760 #undef UNIX_PATH_SEMANTICS
761 #undef WINDOWS_PATH_SEMANTICS
762 #undef HFS_PATH_SEMANTICS
763 #undef CFPreferredSlash