UKDirectoryEnumerator
Language: Objective-C, Author: Uli Kusterer
License: MIT/X11
A faster, more configurable variant of NSDirectoryEnumerator. Uses the Carbon FSGetCatalogInfoBulk() call under the hood to do its work, and gives you greater control over what additional file info you want. One advantage of this is that you get HFS-specific file info (file type, creator, Finder flags, resource fork sizes) for free.
UKDirectoryEnumerator source preview
/* ============================================================================= FILE: UKDirectoryEnumerator.m PROJECT: Filie COPYRIGHT: (c) 2004 M. Uli Kusterer, all rights reserved. AUTHORS: M. Uli Kusterer - UK LICENSES: MIT License REVISIONS: 2006-03-13 UK Clarified license, miscellaneous additions. 2004-04-15 UK Created. ========================================================================== */ // ----------------------------------------------------------------------------- // Headers: // ----------------------------------------------------------------------------- #import "UKDirectoryEnumerator.h" #import "NSString+NDCarbonUtilities.h" // ----------------------------------------------------------------------------- // Prototypes: // ----------------------------------------------------------------------------- NSDictionary* UKDictionaryFromFSCatInfo( FSCatalogInfo* currInfo, FSCatalogInfoBitmap whichInfo ); void UKFSCatInfoFromDictionary( NSDictionary* attrs, FSCatalogInfo* currInfo, FSCatalogInfoBitmap* whichInfo ); @implementation UKDirectoryEnumerator +(id) enumeratorWithPath: (NSString*)fpath { return [[[[self class] alloc] initWithPath: fpath] autorelease]; } +(id) enumeratorWithPath: (NSString*)fpath cacheSize: (ItemCount)n { return [[[[self class] alloc] initWithPath: fpath cacheSize: n] autorelease]; } // ----------------------------------------------------------------------------- // initWithPath: // Convenience initializer. Uses a default cache size. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(id) initWithPath: (NSString*)fpath { return [self initWithPath: fpath cacheSize: UKDirectoryEnumeratorCacheSize]; } // ----------------------------------------------------------------------------- // initWithPath:cacheSize: // Designated initializer. Opens our FSIterator and initializes our // cache. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(id) initWithPath: (NSString*)fpath cacheSize: (ItemCount)n { self = [super init]; if( self ) { FSRef container; OSErr err = noErr; whichInfo = kFSCatInfoNone; if( ![fpath getFSRef: &container] || (err = FSOpenIterator( &container, kFSIterateFlat, &iterator )) != noErr ) { if( err == noErr ) // getFSRef failed. err = fnfErr; // Invalid path. NSLog(@"UKDirectoryEnumerator::initWithPath: - MacOS Error ID= %d",err); [self autorelease]; return nil; } fpath = [NSString stringWithFSRef: &container]; // In case it's a link or an alias that got resolved. prefixlen = [fpath length] +(([fpath characterAtIndex: [fpath length] -1] == '/') ? 0 : 1); [self setCacheSize: n]; } return self; } // ----------------------------------------------------------------------------- // dealloc: // Close FSIterator that we wrap with this object. // // REVISIONS: // 2005-10-15 UK Made this release infoCache. Thanks Nicholas Jitkoff! // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(void) dealloc { if( cache ) free( cache ); if( infoCache ) free( infoCache ); if( iterator != NULL ) FSCloseIterator( iterator ); [super dealloc]; } // ----------------------------------------------------------------------------- // iterator: // Let those Carbon fans manually mess with the FSIterator if they have // a need to. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(FSIterator) iterator { return iterator; } // ----------------------------------------------------------------------------- // nextObjectFullPath: // Fetch the next file path from the cache. The Cache contains FSRefs, so // this will convert the FSRef into an NSString. If the cache is empty, // this will get the next batch of FSRefs into the cache using the Carbon // FSGetCatalogInfoBulk call, and also cache their file info. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(id) nextObjectFullPath { if( currIndex >= foundItems ) { OSErr err = FSGetCatalogInfoBulk( iterator, cacheSize, &foundItems, NULL, whichInfo, infoCache, cache, (FSSpec*) NULL, (HFSUniStr255*) NULL); if( err != noErr && err != errFSNoMoreItems ) { NSLog(@"UKDirectoryEnumerator::nextObjectFullPath - MacOS Error ID= %d",err); return nil; } currIndex = 0; if( foundItems == 0 ) { if( err != errFSNoMoreItems ) NSLog(@"UKDirectoryEnumerator::nextObjectFullPath - FSCatalogInfoBulk returned 0 items, but not errFSNoMoreItems."); return nil; } } return [NSString stringWithFSRef: &(cache[currIndex++]) ]; } // ----------------------------------------------------------------------------- // nextObject: // Fetch the next file name from the cache. This is made to work the same // way as NSDirectoryEnumerator, and thus only returns the filenames. If // you want the absolute pathname, use nextObjectFullPath, which this // calls internally. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(id) nextObject { NSString* fname = [self nextObjectFullPath]; if( !fname ) return nil; return [fname substringWithRange: NSMakeRange(prefixlen,[fname length] -prefixlen)]; // Remove the prefix (parent folder) from this path to make it relative. } // ----------------------------------------------------------------------------- // cacheExhausted: // Tells you whether the next call to nextObject will cause a reload of the // cache. You usually don't need this, but sometimes it's useful for // deciding when to update progress information. If there will be a short // pause while the File Manager caches some more FSRefs, you might as well // accept the overhead of drawing new status info to the screen. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(BOOL) cacheExhausted { return( currIndex >= foundItems ); } // ----------------------------------------------------------------------------- // fileAttributes: // This is basically the same as NSDirectoryEnumerator's fileAttributes // method. However, what you get here depends on what flags you specified // to setDesiredInfo:, which defaults to kFSCatInfoNone, which means you // get an empty dictionary here if you don't explicitly ask for info. // // Depending on what you pass to setDesiredInfo:, you can even get // additional info that you wouldn't get from an NSDirectoryEnumerator. // In particular, since we're using Carbon under the hood, we get all the // nice info the Finder knows, but other Cocoa apps don't. // // This is an expensive call. If you can, use others like isInvisible or // isDirectory. // // REVISIONS: // 2005-07-03 UK Extracted CatInfo -> Dictionary code into separate // function UKDictionaryFromFSCatInfo(). // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- #define UK_BTST(f,m) (((f) & (m)) == (m)) // Shorthand bit-test macro. -(NSDictionary*) fileAttributes { if( infoCache == NULL ) return [NSMutableDictionary dictionary]; FSCatalogInfo* currInfo = &(infoCache[currIndex -1]); return UKDictionaryFromFSCatInfo( currInfo, whichInfo ); } // ----------------------------------------------------------------------------- // isInvisible: // If you passed the kFSCatInfoFinderInfo flag to setDesiredInfo:, this // will return the value of the Finder's kIsInvisible file flag. Otherwise // this will ruthlessly claim the file was visible. // // This will *not* do any other checks, like whether the file name starts // with a period. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(BOOL) isInvisible { FSCatalogInfo* currInfo = &(infoCache[currIndex -1]); if( UK_BTST(whichInfo, kFSCatInfoFinderInfo) ) { FileInfo* fInfo = (FileInfo*) currInfo->finderInfo; return UK_BTST(fInfo->finderFlags, kIsInvisible); } else return NO; } // ----------------------------------------------------------------------------- // isDirectory: // If you passed the kFSCatInfoNodeFlags flag to setDesiredInfo:, this // will tell you whether an item is a directory (aka folder) or not. // Otherwise this will ruthlessly claim it was a file. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(BOOL) isDirectory { FSCatalogInfo* currInfo = &(infoCache[currIndex -1]); if( UK_BTST(whichInfo, kFSCatInfoNodeFlags) ) return UK_BTST(currInfo->nodeFlags, kFSNodeIsDirectoryMask); else return NO; } // ----------------------------------------------------------------------------- // setDesiredInfo: // Takes a bit field of or-ed together FSCatalogInfoBitmap flags that // control what information will be collected about files. You can then // query this information using the fileAttributes, isInvisible and // isDirectory methods. // // FSCatalogInfoBitmap and the associated flags are defined in // <Carbon/Files.h>. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(void) setDesiredInfo: (FSCatalogInfoBitmap)n { if( whichInfo != n ) { whichInfo = n; if( whichInfo == kFSCatInfoNone && infoCache != NULL ) { free( infoCache ); infoCache = NULL; } else if( whichInfo != kFSCatInfoNone && infoCache == NULL ) { infoCache = malloc( sizeof(FSCatalogInfo) * cacheSize ); if( cache == NULL ) whichInfo = kFSCatInfoNone; } } } // ----------------------------------------------------------------------------- // desiredInfo: // Returns the flags set using setDesiredInfo:. If you didn't call that, // you'll probably get the default kFSCatalogInfoNone. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(FSCatalogInfoBitmap) desiredInfo { return whichInfo; } #if UKDE_ALLOW_SETWHICHINFO // ----------------------------------------------------------------------------- // setWhichInfo: and whichInfo: *DEPRECATED* // ----------------------------------------------------------------------------- -(void) setWhichInfo: (FSCatalogInfoBitmap)n { [self setDesiredInfo: n]; } -(FSCatalogInfoBitmap) whichInfo { return [self desiredInfo]; } #endif // ----------------------------------------------------------------------------- // setCacheSize: // Controls the size (in number of files) of the cache used when getting // the files. The file list is retrieved in batches of that many files, // and -nextObject will automatically fetch the next item from the cache // and load the next batch into the cache as needed. // // Note that this destroys any currently cached items. So only call this // before your first call to -nextObject or when -cacheExhausted is YES. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(void) setCacheSize: (ItemCount)c { // Get rid of the old FSRef and FSCatalogInfo caches: if( cache ) { free(cache); cache = NULL; } if( infoCache ) { free(infoCache); infoCache = NULL; } // Allocate new caches of the requested size: cache = malloc( sizeof(FSRef) * c ); if( cache == NULL ) cacheSize = 0; else cacheSize = c; if( whichInfo != kFSCatInfoNone ) { infoCache = malloc( sizeof(FSCatalogInfo) * c ); if( cache == NULL ) whichInfo = kFSCatInfoNone; } } // ----------------------------------------------------------------------------- // cacheSize: // Returns the size (in number of files) of the cache used when getting // the files. The file list is retrieved in batches of that many files, // and -nextObject will automatically fetch the next item from the cache // and load the next batch into the cache as needed. // // REVISIONS: // 2004-11-11 UK Documented. // ----------------------------------------------------------------------------- -(ItemCount) cacheSize { return cacheSize; } @end @implementation NSFileManager (UKDirectoryEnumeratorVisibleDirectoryContents) // ----------------------------------------------------------------------------- // visibleDirectoryContentsAtPath: // Lists the contents of a particular directory (aka folder), removing // any items that are invisible according to MacOS X conventions. Note // that this will not consider "latent" invisibility. I.e. if you list the // contents of an invisible folder, only the files that are themselves // invisible inside it will be removed. // // This tries to apply the same criteria as the Finder when it comes to // invisibility. // // REVISIONS: // 2004-11-11 UK Created. // ----------------------------------------------------------------------------- -(NSArray*) visibleDirectoryContentsAtPath: (NSString*)path { NSMutableArray* arr = [NSMutableArray array]; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // Everything created now will be autoreleased if it isn't in arr: UKDirectoryEnumerator* enny = [[[UKDirectoryEnumerator alloc] initWithPath: path] autorelease]; NSString* fname; [enny setDesiredInfo: kFSCatInfoFinderInfo]; // Loop through the directory: while( (fname = [enny nextObject]) ) { if( [fname characterAtIndex: 0] == '.' ) // Unix-style invisibility? continue; if( [enny isInvisible] ) // MacOS-style invisibility? continue; [arr addObject: fname]; // File is visible and should be listed. } // Now, if we're at the file system root, consult .hidden on what other files we should hide: if( [path isEqualToString: @"/"] ) // At the root level, we have some specially hidden Unix folders: { NSArray* hiddenList = [[NSString stringWithContentsOfFile: @"/.hidden"] componentsSeparatedByString: @"\n"]; [arr removeObjectsInArray: hiddenList]; } // End of autoreleased area. [pool release]; return arr; } -(BOOL) changeCarbonFileAttributes: (NSDictionary*)attrs atPath: (NSString*)path { FSCatalogInfo info; FSRef fileRef; OSErr err = noErr; FSCatalogInfoBitmap whichInfo = kFSCatInfoNone; if( ![path getFSRef: &fileRef] ) return nil; UKFSCatInfoFromDictionary( attrs, &info, &whichInfo ); err = FSSetCatalogInfo( &fileRef, whichInfo, &info ); if( err != noErr ) NSLog( @"changeCarbonFileAttributes:atPath: FSSetCatalogInfo: MacOS Error ID=%d", err ); return( err == noErr ); } -(NSDictionary*) carbonFileAttributesAtPath: (NSString*)path whichInfo: (FSCatalogInfoBitmap)whichInfo { FSCatalogInfo info; FSRef fileRef; OSErr err = noErr; if( ![path getFSRef: &fileRef] ) return nil; err = FSGetCatalogInfo( &fileRef, whichInfo, &info, NULL,NULL, NULL ); if( err != noErr ) return nil; return UKDictionaryFromFSCatInfo( &info, whichInfo ); } @end NSDictionary* UKDictionaryFromFSCatInfo( FSCatalogInfo* currInfo, FSCatalogInfoBitmap whichInfo ) { NSMutableDictionary* dict = [NSMutableDictionary dictionary]; if( UK_BTST(whichInfo, kFSCatInfoNodeFlags) ) { [dict setObject: [NSNumber numberWithBool: UK_BTST(currInfo->nodeFlags, kFSNodeLockedMask)] forKey: UKItemIsLocked]; if( UK_BTST(currInfo->nodeFlags, kFSNodeIsDirectoryMask) ) [dict setObject: NSFileTypeDirectory forKey: NSFileType]; else [dict setObject: NSFileTypeRegular forKey: NSFileType]; } if( UK_BTST(whichInfo, kFSCatInfoFinderInfo) ) { FileInfo* fInfo = (FileInfo*) currInfo->finderInfo; [dict setObject: [NSNumber numberWithBool: UK_BTST(fInfo->finderFlags, kIsInvisible)] forKey: UKItemIsInvisible]; [dict setObject: [NSNumber numberWithBool: UK_BTST(fInfo->finderFlags, kIsAlias)] forKey: UKItemIsAlias]; [dict setObject: [NSNumber numberWithBool: UK_BTST(fInfo->finderFlags, kHasBundle)] forKey: UKItemHasBNDL]; [dict setObject: [NSNumber numberWithBool: UK_BTST(fInfo->finderFlags, kNameLocked)] forKey: UKItemNameIsLocked]; [dict setObject: [NSNumber numberWithBool: UK_BTST(fInfo->finderFlags, kIsStationery)] forKey: UKItemIsStationery]; [dict setObject: [NSNumber numberWithBool: UK_BTST(fInfo->finderFlags, kHasCustomIcon)] forKey: UKItemHasCustomIcon]; [dict setObject: [NSNumber numberWithInt: (fInfo->finderFlags & kColor) >> 1] forKey: UKLabelNumber]; [dict setObject: [NSNumber numberWithUnsignedLong: fInfo->fileType] forKey: NSFileHFSTypeCode]; [dict setObject: [NSNumber numberWithUnsignedLong: fInfo->fileCreator] forKey: NSFileHFSCreatorCode]; } if( UK_BTST(whichInfo, kFSCatInfoDataSizes) ) { [dict setObject: [NSNumber numberWithUnsignedLongLong: currInfo->dataLogicalSize] forKey: NSFileSize]; [dict setObject: [NSNumber numberWithUnsignedLongLong: currInfo->dataPhysicalSize] forKey: UKPhysicalFileSize]; } if( UK_BTST(whichInfo, kFSCatInfoRsrcSizes) ) { [dict setObject: [NSNumber numberWithUnsignedLongLong: currInfo->rsrcLogicalSize] forKey: UKLogicalResFileSize]; [dict setObject: [NSNumber numberWithUnsignedLongLong: currInfo->rsrcPhysicalSize] forKey: UKPhysicalResFileSize]; } if( UK_BTST(whichInfo, kFSCatInfoFinderXInfo) ) { ExtendedFileInfo* xInfo = (ExtendedFileInfo*) currInfo->extFinderInfo; if( !UK_BTST(xInfo->extendedFinderFlags, kExtendedFlagsAreInvalid) ) { [dict setObject: [NSNumber numberWithBool: UK_BTST(xInfo->extendedFinderFlags, kExtendedFlagHasCustomBadge)] forKey: UKItemHasCustomBadge]; [dict setObject: [NSNumber numberWithBool: UK_BTST(xInfo->extendedFinderFlags, kExtendedFlagHasRoutingInfo)] forKey: UKItemHasRoutingInfo]; } } if( UK_BTST(whichInfo, kFSCatInfoPermissions) ) { FSPermissionInfo* pInfo = (FSPermissionInfo*) currInfo->permissions; [dict setObject: [NSNumber numberWithUnsignedLong: pInfo->userID] forKey: NSFileOwnerAccountID]; [dict setObject: [NSNumber numberWithUnsignedLong: pInfo->groupID] forKey: NSFileGroupOwnerAccountID]; [dict setObject: [NSNumber numberWithUnsignedShort: pInfo->mode] forKey: NSFilePosixPermissions]; } CFAbsoluteTime absTime = 0; if( UK_BTST(whichInfo, kFSCatInfoCreateDate) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->createDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: NSFileCreationDate]; } if( UK_BTST(whichInfo, kFSCatInfoAttrMod) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->attributeModDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: UKFileAttrModificationDate]; } if( UK_BTST(whichInfo, kFSCatInfoContentMod) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->contentModDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: NSFileModificationDate]; } if( UK_BTST(whichInfo, kFSCatInfoAccessDate) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->accessDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: UKFileAccessDate]; } if( UK_BTST(whichInfo, kFSCatInfoBackupDate) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->backupDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: UKFileBackupDate]; } return dict; } void UKFSCatInfoFromDictionary( NSDictionary* attrs, FSCatalogInfo* currInfo, FSCatalogInfoBitmap* whichInfo ) { NSNumber* val = nil; (*whichInfo) = kFSCatInfoNone; memset( currInfo, 0, sizeof(FSCatalogInfo) ); // Clear all fields. // Node Flags: val = [attrs objectForKey: UKItemIsLocked]; if( val ) { (*whichInfo) |= kFSCatInfoNodeFlags; if( [val boolValue] ) currInfo->nodeFlags |= kFSNodeLockedMask; } // Finder Flags: FileInfo* fInfo = (FileInfo*) currInfo->finderInfo; val = [attrs objectForKey: UKItemIsInvisible]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; if( [val boolValue] ) fInfo->finderFlags |= kIsInvisible; } val = [attrs objectForKey: UKItemIsAlias]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; if( [val boolValue] ) fInfo->finderFlags |= kIsAlias; } val = [attrs objectForKey: UKItemHasBNDL]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; if( [val boolValue] ) fInfo->finderFlags |= kHasBundle; } val = [attrs objectForKey: UKItemNameIsLocked]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; if( [val boolValue] ) fInfo->finderFlags |= kNameLocked; } val = [attrs objectForKey: UKItemIsStationery]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; if( [val boolValue] ) fInfo->finderFlags |= kIsStationery; } val = [attrs objectForKey: UKItemHasCustomIcon]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; if( [val boolValue] ) fInfo->finderFlags |= kHasCustomIcon; } val = [attrs objectForKey: UKLabelNumber]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; fInfo->finderFlags |= ([val intValue] << 1) & kColor; } val = [attrs objectForKey: NSFileHFSTypeCode]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; fInfo->fileType = [val unsignedLongValue]; } val = [attrs objectForKey: NSFileHFSCreatorCode]; if( val ) { (*whichInfo) |= kFSCatInfoFinderInfo; fInfo->fileCreator = [val unsignedLongValue]; } // Extended Finder Flags: ExtendedFileInfo* xInfo = (ExtendedFileInfo*) currInfo->extFinderInfo; val = [attrs objectForKey: UKItemHasCustomBadge]; if( val ) { (*whichInfo) |= kFSCatInfoFinderXInfo; if( [val boolValue] ) xInfo->extendedFinderFlags |= kExtendedFlagHasCustomBadge; } val = [attrs objectForKey: UKItemHasRoutingInfo]; if( val ) { (*whichInfo) |= kFSCatInfoFinderXInfo; if( [val boolValue] ) xInfo->extendedFinderFlags |= kExtendedFlagHasRoutingInfo; } // Permissions: FSPermissionInfo* pInfo = (FSPermissionInfo*) currInfo->permissions; val = [attrs objectForKey: NSFileOwnerAccountID]; if( val ) { (*whichInfo) |= kFSCatInfoPermissions; pInfo->userID = [val unsignedLongValue]; } val = [attrs objectForKey: NSFileGroupOwnerAccountID]; if( val ) { (*whichInfo) |= kFSCatInfoPermissions; pInfo->groupID = [val unsignedLongValue]; } val = [attrs objectForKey: NSFilePosixPermissions]; if( val ) { (*whichInfo) |= kFSCatInfoPermissions; pInfo->mode = [val unsignedShortValue]; } // Dates: // TO DO: Write code to set dates. /*CFAbsoluteTime absTime = 0; if( UK_BTST(whichInfo, kFSCatInfoCreateDate) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->createDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: NSFileCreationDate]; } if( UK_BTST(whichInfo, kFSCatInfoAttrMod) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->attributeModDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: UKFileAttrModificationDate]; } if( UK_BTST(whichInfo, kFSCatInfoContentMod) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->contentModDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: NSFileModificationDate]; } if( UK_BTST(whichInfo, kFSCatInfoAccessDate) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->accessDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: UKFileAccessDate]; } if( UK_BTST(whichInfo, kFSCatInfoBackupDate) ) { UCConvertUTCDateTimeToCFAbsoluteTime( &currInfo->backupDate, &absTime ); [dict setObject: [NSDate dateWithTimeIntervalSinceReferenceDate: absTime] forKey: UKFileBackupDate]; }*/ }
UKDirectoryEnumerator header preview
/* ============================================================================= FILE: UKDirectoryEnumerator.h PROJECT: Filie COPYRIGHT: (c) 2004 M. Uli Kusterer, all rights reserved. AUTHORS: M. Uli Kusterer - UK LICENSES: MIT License REVISIONS: 2006-03-13 UK Clarified license, factory methods, miscellaneous additions. 2004-04-15 UK Created. ========================================================================== */ /* As of MacOS X 10.3, NSDirectoryEnumerator is dog-slow. So, this is my take on it, which uses Carbon's FSGetCatalogInfoBulk() to quickly list files. And to allow for more control over where this spends its cycles, you can even specify what kinds of information you want. By default, this will collect *no* info about the object at all. I.e. no file attributes, no info whether it's a file or folder etc. By explicitly requesting certain info, you can avoid a lot of work that NSDirectoryEnumerator would do needlessly. Also, this fetches files in batches of 16, which improves access locality and stuff. This doesn't yet support listing subfolders implicitly. You have to do that manually. Apart from that it should be a drop-in replacement. */ // ----------------------------------------------------------------------------- // Headers: // ----------------------------------------------------------------------------- #import <Foundation/Foundation.h> #import <Carbon/Carbon.h> // ----------------------------------------------------------------------------- // Constants: // ----------------------------------------------------------------------------- #define UKDirectoryEnumeratorCacheSize 16 // Default size for cache. #define UKDE_CACHE_SIZE UKDirectoryEnumeratorCacheSize // old name for UKDirectoryEnumeratorCacheSize. #ifndef UKDE_ALLOW_SETWHICHINFO #define UKDE_ALLOW_SETWHICHINFO 0 // Allow using old name setWhichInfo: instead of setDesiredInfo: and whichInfo instead of desiredInfo. Deprecated. #endif // ----------------------------------------------------------------------------- // UKDirectoryEnumerator: // ----------------------------------------------------------------------------- @interface UKDirectoryEnumerator : NSEnumerator { FSIterator iterator; // Carbon iterator corresponding to this enumerator. FSRef* cache; // Carbon file refs to the found files. We get them in batches for performance reasons. FSCatalogInfo* infoCache; // Cache for info about file. May be NULL if whichInfo is kFSCatInfoNone. ItemCount cacheSize; // Number of files the cache can hold. ItemCount foundItems; // Number of entries in cache that are used. ItemCount currIndex; // Index into cache for next item to get. If this is >= foundItems, we need to re-cache. FSCatalogInfoBitmap whichInfo; // Additional info to get for files (setDesiredInfo:/desiredInfo). int prefixlen; // The number of characters to remove from files' pathnames to make them relative to the folder's path. } +(id) enumeratorWithPath: (NSString*)fpath; +(id) enumeratorWithPath: (NSString*)fpath cacheSize: (ItemCount)n; -(id) initWithPath: (NSString*)fpath; -(id) initWithPath: (NSString*)fpath cacheSize: (ItemCount)n; // Designated. -(id) nextObject; // NSDirectoryEnumerator-compatible variant. -(id) nextObjectFullPath; // Variant that returns an absolute path. // Advanced users: -(void) setDesiredInfo: (FSCatalogInfoBitmap)n; // Flags what additional info you want. -(FSCatalogInfoBitmap) desiredInfo; -(NSDictionary*) fileAttributes; // You must set up whichInfo to get something in here. Note that this is an expensive call. If you can use isDirectory() or isInvisible(), then do that instead. -(BOOL) isInvisible; // You must set whichInfo to include kFSCatInfoFinderInfo to get something except NO here. -(BOOL) isDirectory; // You must set whichInfo to include kFSCatInfoNodeFlags to get something except NO here. -(void) setCacheSize: (ItemCount)c; // How many files to cache. Defaults to UKDirectoryEnumeratorCacheSize -(ItemCount) cacheSize; -(BOOL) cacheExhausted; // Not really needed. I like to use this to pick a convenient point in time at which to send a reloadData message to my view. -(FSIterator) iterator; // Carbon FSIterator behind this. #if UKDE_ALLOW_SETWHICHINFO // Old name for setDesiredInfo: *DEPRECATED* -(void) setWhichInfo: (FSCatalogInfoBitmap)n; // Flags what additional info you want. -(FSCatalogInfoBitmap) whichInfo; #endif @end // ----------------------------------------------------------------------------- // Constants: // ----------------------------------------------------------------------------- /* desiredInfo values and what values they add to the fileAttributes dictionary: (Note: By default, desiredInfo is kFSCatInfoNone, which means you get an empty fileAttributes dictionary) UKNSWorkspaceAttributeFlags: Gives you all the NSxxx keys that you'd get from NSWorkspace's NSDirectoryEnumerator. kFSCatInfoNodeFlags: NSFileType - either NSFileTypeDirectory or NSFileTypeRegular UKItemIsLocked - NSNumber containing boolean. kFSCatInfoFinderInfo: NSNumbers containing booleans if not specified differently. UKItemIsInvisible UKItemIsAlias UKItemHasBNDL UKItemNameIsLocked UKItemIsStationery UKItemHasCustomIcon UKLabelNumber - NSNumber containing number (0...7) indicating label of this file. NSFileHFSTypeCode - NSNumber containing HFS type code. NSFileHFSCreatorCode - NSNumber containing HFS creator code. kFSCatInfoFinderXInfo: UKItemHasCustomBadge - NSNumber containing boolean. UKItemHasRoutingInfo - NSNumber containing boolean. kFSCatInfoDataSizes: NSFileSize - NSNumber containing logical size of data fork. UKPhysicalFileSize - NSNumber containing physical size of data fork. kFSCatInfoRsrcSizes: UKLogicalResFileSize - NSNumber containing logical size of resource fork. UKPhysicalResFileSize - NSNumber containing physical size of resource fork. kFSCatInfoPermissions: NSFileOwnerAccountID - NSNumber containing owner ID. NSFileGroupOwnerAccountID - NSNumber containing owning group's ID. NSFilePosixPermissions - NSNumber containing unix permissions for file. kFSCatInfoCreateDate: NSFileCreationDate - NSDate containing the date and time the file was created at. kFSCatInfoContentMod: NSFileModificationDate - NSDate containing the date and time the file's contents were last modified. kFSCatInfoAttrMod: UKFileAttrModificationDate - NSDate containing the date and time the file's attributes were last modified. kFSCatInfoAccessDate: UKFileAccessDate - NSDate containing the date and time the filewas last accessed. kFSCatInfoBackupDate: UKFileBackupDate - NSDate containing the date and time the file was last backed up. */ #define UKNSWorkspaceAttributeFlags (kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoDataSizes \ | kFSCatInfoPermissions | kFSCatInfoCreateDate | kFSCatInfoContentMod) // UKDirectoryEnumerator-specific keys in fileAttributes dictionary: #define UKItemIsInvisible @"UKItemIsInvisible" // This is the HFS Finder flag. You still have to hide files starting with a period. #define UKItemIsAlias @"UKItemIsAlias" // HFS Finder flag. Different from Symlinks. #define UKItemHasBNDL @"UKItemHasBNDL" // HFS Finder flag. File has BNDL resource with type/creator -> icon mappings. #define UKItemNameIsLocked @"UKItemNameIsLocked" // HFS Finder flag. Name and icon can't be edited in Finder. #define UKItemIsLocked @"UKItemIsLocked" // HFS Finder flag. #define UKItemIsStationery @"UKItemIsStationery" // HFS Finder flag. File is stationery that will be copied when opened. #define UKItemHasCustomIcon @"UKItemHasCustomIcon" // HFS Finder flag. File/folder has a user-specified icon. #define UKPhysicalFileSize @"UKPhysicalFileSize" // Physical size of data fork (may be larger than actual used logical file size). #define UKLogicalResFileSize @"UKLogicalResFileSize" // Logical size of resource fork. #define UKPhysicalResFileSize @"UKPhysicalResFileSize" // Physical size of resource fork (may be larger than actual used logical size). #define UKItemHasCustomBadge @"UKItemHasCustomBadge" // HFS Finder flag. File has an icon badge (specified using resources). #define UKItemHasRoutingInfo @"UKItemHasRoutingInfo" // HFS Finder flag. File has routing info resource telling where in system folder it goes. #define UKLabelNumber @"UKLabelNumber" // HFS Finder info. This is the number of the label that's been applied to the icon. #define UKFileAttrModificationDate @"UKFileAttrModificationDate" // When file attributes (as opposed to contents) were last changed. #define UKFileAccessDate @"UKFileAccessDate" // When the file was last accessed. #define UKFileBackupDate @"UKFileBackupDate" // When the file was last backed up. // ----------------------------------------------------------------------------- // NSFileManager category: // Allows to only get user-visible files, and to set UKDirectoryEnumerator- // style file attributes and get them. // ----------------------------------------------------------------------------- @interface NSFileManager (UKDirectoryEnumeratorVisibleDirectoryContents) -(NSArray*) visibleDirectoryContentsAtPath: (NSString*)path; -(BOOL) changeCarbonFileAttributes: (NSDictionary*)attrs atPath: (NSString*)path; // Doesn't yet support all attributes! Check source code! -(NSDictionary*) carbonFileAttributesAtPath: (NSString*)path whichInfo: (FSCatalogInfoBitmap)whichInfo; @endDownload Archive
Dependencies:
Compatible with:
- Mac OS X 10.3
- Mac OS X 10.4 PPC
- Mac OS X 10.4 Intel
- Mac OS X 10.5 PPC
- Mac OS X 10.5 Intel
Comments
Comment feed
