/* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* CFFileUtilities.c Copyright 1999-2002, Apple, Inc. All rights reserved. Responsibility: Christopher Kane */ #include "CFInternal.h" #include "CFPriv.h" #if defined(__WIN32__) #include #include #include #include #define timeval xxx_timeval #define BOOLEAN xxx_BOOLEAN #include #undef BOOLEAN #undef timeval #define fstat _fstat #define open _open #define close _close #define write _write #define read _read #define stat _stat #else #include #include #include #include #include #include #include #include #include #endif #if defined(__WIN32__) #define CF_OPENFLGS (_O_BINARY|_O_NOINHERIT) #else #define CF_OPENFLGS (0) #endif __private_extern__ CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType) { return (abstractType ? CFRetain(abstractType) : NULL); } __private_extern__ Boolean _CFCreateDirectory(const char *path) { #if defined(__WIN32__) return CreateDirectoryA(path, (LPSECURITY_ATTRIBUTES)NULL); #else return ((mkdir(path, 0777) == 0) ? true : false); #endif } __private_extern__ Boolean _CFRemoveDirectory(const char *path) { #if defined(__WIN32__) return RemoveDirectoryA(path); #else return ((rmdir(path) == 0) ? true : false); #endif } __private_extern__ Boolean _CFDeleteFile(const char *path) { #if defined(__WIN32__) return DeleteFileA(path); #else return unlink(path) == 0; #endif } __private_extern__ Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef url, void **bytes, CFIndex *length, CFIndex maxLength) { // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length. struct stat statBuf; int fd = -1; char path[CFMaxPathSize]; if (!CFURLGetFileSystemRepresentation(url, true, path, CFMaxPathSize)) { return false; } *bytes = NULL; #if defined(__WIN32__) fd = open(path, O_RDONLY|CF_OPENFLGS, 0666|_S_IREAD); #else fd = open(path, O_RDONLY|CF_OPENFLGS, 0666); #endif if (fd < 0) { return false; } if (fstat(fd, &statBuf) < 0) { int saveerr = thread_errno(); close(fd); thread_set_errno(saveerr); return false; } if ((statBuf.st_mode & S_IFMT) != S_IFREG) { close(fd); thread_set_errno(EACCES); return false; } if (statBuf.st_size == 0) { *bytes = CFAllocatorAllocate(alloc, 4, 0); // don't return constant string -- it's freed! if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); *length = 0; } else { CFIndex desiredLength; if ((maxLength >= statBuf.st_size) || (maxLength == 0)) { desiredLength = statBuf.st_size; } else { desiredLength = maxLength; } *bytes = CFAllocatorAllocate(alloc, desiredLength, 0); if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); // fcntl(fd, F_NOCACHE, 1); if (read(fd, *bytes, desiredLength) < 0) { CFAllocatorDeallocate(alloc, *bytes); close(fd); return false; } *length = desiredLength; } close(fd); return true; } __private_extern__ Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length) { struct stat statBuf; int fd = -1; int mode, mask; char path[CFMaxPathSize]; if (!CFURLGetFileSystemRepresentation(url, true, path, CFMaxPathSize)) { return false; } #if defined(__WIN32__) mask = 0; #else mask = umask(0); umask(mask); #endif mode = 0666 & ~mask; if (0 == stat(path, &statBuf)) { mode = statBuf.st_mode; } else if (thread_errno() != ENOENT) { return false; } #if defined(__WIN32__) fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666|_S_IWRITE); #else fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666); #endif if (fd < 0) { return false; } if (length && write(fd, bytes, length) != length) { int saveerr = thread_errno(); close(fd); thread_set_errno(saveerr); return false; } #if defined(__WIN32__) FlushFileBuffers((HANDLE)_get_osfhandle(fd)); #else fsync(fd); #endif close(fd); return true; } /* 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 If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */ __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType) { CFMutableArrayRef files = NULL; Boolean releaseBase = false; CFIndex pathLength = dirPath ? strlen(dirPath) : 0; // MF:!!! Need to use four-letter type codes where appropriate. CFStringRef extension = (matchingAbstractType ? _CFCopyExtensionForAbstractType(matchingAbstractType) : NULL); CFIndex extLen = (extension ? CFStringGetLength(extension) : 0); uint8_t extBuff[CFMaxPathSize]; if (extLen > 0) { CFStringGetBytes(extension, CFRangeMake(0, extLen), CFStringFileSystemEncoding(), 0, false, extBuff, CFMaxPathSize, &extLen); extBuff[extLen] = '\0'; } uint8_t pathBuf[CFMaxPathSize]; if (!dirPath) { if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) { if (extension) CFRelease(extension); return NULL; } else { dirPath = pathBuf; pathLength = strlen(dirPath); } } #if defined(__WIN32__) WIN32_FIND_DATA file; HANDLE handle; if (pathLength + 2 >= CFMaxPathLength) { if (extension) { CFRelease(extension); } return NULL; } dirPath[pathLength] = '\\'; dirPath[pathLength + 1] = '*'; dirPath[pathLength + 2] = '\0'; handle = FindFirstFileA(dirPath, &file); if (INVALID_HANDLE_VALUE == handle) { dirPath[pathLength] = '\0'; if (extension) { CFRelease(extension); } return NULL; } files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); do { CFURLRef fileURL; CFIndex namelen = strlen(file.cFileName); if (file.cFileName[0] == '.' && (namelen == 1 || (namelen == 2 && file.cFileName[1] == '.'))) { continue; } if (extLen > 0) { // Check to see if it matches the extension we're looking for. if (_stricmp(&(file.cFileName[namelen - extLen]), extBuff) != 0) { continue; } } if (dirURL == NULL) { dirURL = CFURLCreateFromFileSystemRepresentation(alloc, dirPath, pathLength, true); releaseBase = true; } // MF:!!! What about the trailing slash? fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, file.cFileName, namelen, false, dirURL); CFArrayAppendValue(files, fileURL); CFRelease(fileURL); } while (FindNextFileA(handle, &file)); FindClose(handle); dirPath[pathLength] = '\0'; #elif defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) /* Solaris and HPUX Implementation */ /* The Solaris and HPUX code has not been updated for: base has been renamed dirURL dirPath may be NULL (in which case dirURL is not) if dirPath is NULL, pathLength is 0 */ DIR *dirp; struct dirent *dp; int err; dirp = opendir(dirPath); if (!dirp) { if (extension) { CFRelease(extension); } return NULL; // raiseErrno("opendir", path); } files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks); while((dp = readdir(dirp)) != NULL) { CFURLRef fileURL; unsigned namelen = strlen(dp->d_name); // skip . & ..; they cause descenders to go berserk if (dp->d_name[0] == '.' && (namelen == 1 || (namelen == 2 && dp->d_name[1] == '.'))) { continue; } if (extLen > 0) { // Check to see if it matches the extension we're looking for. if (strncmp(&(dp->d_name[namelen - extLen]), extBuff, extLen) != 0) { continue; } } if (dirURL == NULL) { dirURL = CFURLCreateFromFileSystemRepresentation(alloc, dirPath, pathLength, true); releaseBase = true; } // MF:!!! What about the trailing slash? fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, dp->d_name, namelen, false, dirURL); CFArrayAppendValue(files, fileURL); CFRelease(fileURL); } err = closedir(dirp); if (err != 0) { CFRelease(files); if (releaseBase) { CFRelease(dirURL); } if (extension) { CFRelease(extension); } return NULL; // raiseErrno("closedir", path); } #elif defined(__MACH__) int fd, numread; long basep; char dirge[8192]; fd = open(dirPath, O_RDONLY, 0777); if (fd < 0) { if (extension) { CFRelease(extension); } return NULL; } files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); while ((numread = getdirentries(fd, dirge, sizeof(dirge), &basep)) > 0) { struct dirent *dent; for (dent = (struct dirent *)dirge; dent < (struct dirent *)(dirge + numread); dent = (struct dirent *)((char *)dent + dent->d_reclen)) { CFURLRef fileURL; CFIndex nameLen; nameLen = dent->d_namlen; // skip . & ..; they cause descenders to go berserk if (0 == dent->d_fileno || (dent->d_name[0] == '.' && (nameLen == 1 || (nameLen == 2 && dent->d_name[1] == '.')))) { continue; } if (extLen > 0) { // Check to see if it matches the extension we're looking for. if (strncmp(&(dent->d_name[nameLen - extLen]), extBuff, extLen) != 0) { continue; } } if (dirURL == NULL) { dirURL = CFURLCreateFromFileSystemRepresentation(alloc, dirPath, pathLength, true); releaseBase = true; } if (dent->d_type == DT_DIR || dent->d_type == DT_UNKNOWN) { Boolean isDir = (dent->d_type == DT_DIR); if (!isDir) { // Ugh; must stat. char subdirPath[CFMaxPathLength]; struct stat statBuf; strncpy(subdirPath, dirPath, pathLength); subdirPath[pathLength] = '/'; strncpy(subdirPath + pathLength + 1, dent->d_name, nameLen); subdirPath[pathLength + nameLen + 1] = '\0'; if (stat(subdirPath, &statBuf) == 0) { isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR); } } fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, dent->d_name, nameLen, isDir, dirURL); } else { fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, dent->d_name, nameLen, false, dirURL); } CFArrayAppendValue(files, fileURL); CFRelease(fileURL); } } close(fd); if (-1 == numread) { CFRelease(files); if (releaseBase) { CFRelease(dirURL); } if (extension) { CFRelease(extension); } return NULL; } #else #error _CFContentsOfDirectory() unknown architechture, not implemented #endif if (extension) { CFRelease(extension); } if (releaseBase) { CFRelease(dirURL); } return files; } __private_extern__ SInt32 _CFGetFileProperties(CFAllocatorRef alloc, CFURLRef pathURL, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) { Boolean fileExists; Boolean isDirectory = false; struct stat statBuf; char path[CFMaxPathLength]; if ((exists == NULL) && (posixMode == NULL) && (size == NULL) && (modTime == NULL) && (ownerID == NULL) && (dirContents == NULL)) { // Nothing to do. return 0; } if (!CFURLGetFileSystemRepresentation(pathURL, true, path, CFMaxPathLength)) { return -1; } if (stat(path, &statBuf) != 0) { // stat failed, but why? if (thread_errno() == ENOENT) { fileExists = false; } else { return thread_errno(); } } else { fileExists = true; isDirectory = ((statBuf.st_mode & S_IFMT) == S_IFDIR); } if (exists != NULL) { *exists = fileExists; } if (posixMode != NULL) { if (fileExists) { *posixMode = statBuf.st_mode; } else { *posixMode = 0; } } if (size != NULL) { if (fileExists) { *size = statBuf.st_size; } else { *size = 0; } } if (modTime != NULL) { if (fileExists) { CFTimeInterval theTime; theTime = kCFAbsoluteTimeIntervalSince1970 + statBuf.st_mtime; *modTime = CFDateCreate(alloc, theTime); } else { *modTime = NULL; } } if (ownerID != NULL) { if (fileExists) { *ownerID = statBuf.st_uid; } else { *ownerID = -1; } } if (dirContents != NULL) { if (fileExists && isDirectory) { CFMutableArrayRef contents = _CFContentsOfDirectory(alloc, path, NULL, pathURL, NULL); if (contents) { *dirContents = contents; } else { *dirContents = NULL; } } else { *dirContents = NULL; } } return 0; } // MF:!!! Should pull in the rest of the UniChar based path utils from Foundation. #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) #define UNIX_PATH_SEMANTICS #elif defined(__WIN32__) #define WINDOWS_PATH_SEMANTICS #else #error Unknown platform #endif #if defined(WINDOWS_PATH_SEMANTICS) #define CFPreferredSlash ((UniChar)'\\') #elif defined(UNIX_PATH_SEMANTICS) #define CFPreferredSlash ((UniChar)'/') #elif defined(HFS_PATH_SEMANTICS) #define CFPreferredSlash ((UniChar)':') #else #error Cannot define NSPreferredSlash on this platform #endif #if defined(HFS_PATH_SEMANTICS) #define HAS_DRIVE(S) (false) #define HAS_NET(S) (false) #else #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z'))) #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\') #endif #if defined(WINDOWS_PATH_SEMANTICS) #define IS_SLASH(C) ((C) == '\\' || (C) == '/') #elif defined(UNIX_PATH_SEMANTICS) #define IS_SLASH(C) ((C) == '/') #elif defined(HFS_PATH_SEMANTICS) #define IS_SLASH(C) ((C) == ':') #endif __private_extern__ Boolean _CFIsAbsolutePath(UniChar *unichars, CFIndex length) { if (length < 1) { return false; } #if defined(WINDOWS_PATH_SEMANTICS) if (unichars[0] == '~') { return true; } if (length < 2) { return false; } if (HAS_NET(unichars)) { return true; } if (length < 3) { return false; } if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { return true; } #elif defined(HFS_PATH_SEMANTICS) return !IS_SLASH(unichars[0]); #else if (unichars[0] == '~') { return true; } if (IS_SLASH(unichars[0])) { return true; } #endif return false; } __private_extern__ Boolean _CFStripTrailingPathSlashes(UniChar *unichars, CFIndex *length) { Boolean destHasDrive = (1 < *length) && HAS_DRIVE(unichars); CFIndex oldLength = *length; while (((destHasDrive && 3 < *length) || (!destHasDrive && 1 < *length)) && IS_SLASH(unichars[*length - 1])) { (*length)--; } return (oldLength != *length); } __private_extern__ Boolean _CFAppendPathComponent(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *component, CFIndex componentLength) { if (0 == componentLength) { return true; } if (maxLength < *length + 1 + componentLength) { return false; } switch (*length) { case 0: break; case 1: if (!IS_SLASH(unichars[0])) { unichars[(*length)++] = CFPreferredSlash; } break; case 2: if (!HAS_DRIVE(unichars) && !HAS_NET(unichars)) { unichars[(*length)++] = CFPreferredSlash; } break; default: unichars[(*length)++] = CFPreferredSlash; break; } memmove(unichars + *length, component, componentLength * sizeof(UniChar)); *length += componentLength; return true; } __private_extern__ Boolean _CFAppendPathExtension(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *extension, CFIndex extensionLength) { if (maxLength < *length + 1 + extensionLength) { return false; } if ((0 < extensionLength && IS_SLASH(extension[0])) || (1 < extensionLength && HAS_DRIVE(extension))) { return false; } _CFStripTrailingPathSlashes(unichars, length); switch (*length) { case 0: return false; case 1: if (IS_SLASH(unichars[0]) || unichars[0] == '~') { return false; } break; case 2: if (HAS_DRIVE(unichars) || HAS_NET(unichars)) { return false; } break; case 3: if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { return false; } break; } if (0 < *length && unichars[0] == '~') { CFIndex idx; Boolean hasSlash = false; for (idx = 1; idx < *length; idx++) { if (IS_SLASH(unichars[idx])) { hasSlash = true; break; } } if (!hasSlash) { return false; } } unichars[(*length)++] = '.'; memmove(unichars + *length, extension, extensionLength * sizeof(UniChar)); *length += extensionLength; return true; } __private_extern__ Boolean _CFTransmutePathSlashes(UniChar *unichars, CFIndex *length, UniChar replSlash) { CFIndex didx, sidx, scnt = *length; sidx = (1 < *length && HAS_NET(unichars)) ? 2 : 0; didx = sidx; while (sidx < scnt) { if (IS_SLASH(unichars[sidx])) { unichars[didx++] = replSlash; for (sidx++; sidx < scnt && IS_SLASH(unichars[sidx]); sidx++); } else { unichars[didx++] = unichars[sidx++]; } } *length = didx; return (scnt != didx); } __private_extern__ CFIndex _CFStartOfLastPathComponent(UniChar *unichars, CFIndex length) { CFIndex idx; if (length < 2) { return 0; } for (idx = length - 1; idx; idx--) { if (IS_SLASH(unichars[idx - 1])) { return idx; } } if ((2 < length) && HAS_DRIVE(unichars)) { return 2; } return 0; } __private_extern__ CFIndex _CFLengthAfterDeletingLastPathComponent(UniChar *unichars, CFIndex length) { CFIndex idx; if (length < 2) { return 0; } for (idx = length - 1; idx; idx--) { if (IS_SLASH(unichars[idx - 1])) { if ((idx != 1) && (!HAS_DRIVE(unichars) || idx != 3)) { return idx - 1; } return idx; } } if ((2 < length) && HAS_DRIVE(unichars)) { return 2; } return 0; } __private_extern__ CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length) { CFIndex idx; if (length < 2) { return 0; } for (idx = length - 1; idx; idx--) { if (IS_SLASH(unichars[idx - 1])) { return 0; } if (unichars[idx] != '.') { continue; } if (idx == 2 && HAS_DRIVE(unichars)) { return 0; } return idx; } return 0; } __private_extern__ CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length) { CFIndex start = _CFStartOfPathExtension(unichars, length); return ((0 < start) ? start : length); } #undef CF_OPENFLGS #undef UNIX_PATH_SEMANTICS #undef WINDOWS_PATH_SEMANTICS #undef HFS_PATH_SEMANTICS #undef CFPreferredSlash #undef HAS_DRIVE #undef HAS_NET #undef IS_SLASH