calculating directory size in cocoa - xcode

I want to calculate the directory (folder)size and i have to list all files and folders (subfolders) in a volume(drive) with its corresponding size.I am using the below code to calculate size.The problem with this code is the performance issue . I am using NSBrowser to display .
NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;
while (fileName = [filesEnumerator nextObject])
{
NSDictionary *fileDictionary = [[NSFileManager defaultManager] attributesOfItemAtPath:folderPath error:nil];
fileSize += [fileDictionary fileSize];
}
return fileSize;
Questions:
Is there any built in function available?
If not what is the best way to calculate the size?
Is it good to use cache to store already calculated file size?
Thanks...

You can use stat.
-(unsigned long long)getFolderSize : (NSString *)folderPath;
{
char *dir = (char *)[folderPath fileSystemRepresentation];
DIR *cd;
struct dirent *dirinfo;
int lastchar;
struct stat linfo;
static unsigned long long totalSize = 0;
cd = opendir(dir);
if (!cd) {
return 0;
}
while ((dirinfo = readdir(cd)) != NULL) {
if (strcmp(dirinfo->d_name, ".") && strcmp(dirinfo->d_name, "..")) {
char *d_name;
d_name = (char*)malloc(strlen(dir)+strlen(dirinfo->d_name)+2);
if (!d_name) {
//out of memory
closedir(cd);
exit(1);
}
strcpy(d_name, dir);
lastchar = strlen(dir) - 1;
if (lastchar >= 0 && dir[lastchar] != '/')
strcat(d_name, "/");
strcat(d_name, dirinfo->d_name);
if (lstat(d_name, &linfo) == -1) {
free(d_name);
continue;
}
if (S_ISDIR(linfo.st_mode)) {
if (!S_ISLNK(linfo.st_mode))
[self getFolderSize:[NSString stringWithCString:d_name encoding:NSUTF8StringEncoding]];
free(d_name);
} else {
if (S_ISREG(linfo.st_mode)) {
totalSize+=linfo.st_size;
} else {
free(d_name);
}
}
}
}
closedir(cd);
return totalSize;
}
Take a look at Mac OS X not reporting directory sizes correctly?

1. Is there any built in function available?
fileSize is a built-in function that gives you size.
2. If not what is the best way to calculate the size?
This method is good enough to calculate size of a folder/directory.
3. Is it good to use cache to store already calculated file size?
Yes you can store it in cache.

Related

Programmatically know whether a given path is network path or local path on Mac OS X

How can I programmatically determine whether a given path is network path or local path on Mac OS X at run time?
Ex:
1. /Volumes/abc/xyz (mounted using smb)
2. ../test/pqr (the application is on shared network path, hence the current working directory is also a network path and hence the relative path is also a network path)
Like on Windows below code will determine whether, pPath is a network shared path (like * 1. \TallyDT100\c\test\file.txt
2. z:\test\file.txt (A network path when z: is mapped to some network path).
UNIVERSAL_NAME_INFO * universalname = NULL; ///< for getting the universal path name of file on network share.
DWORD retval; ///< for getting the return value from WNetGetUniversalName
DWORD length = MAX_PATH_LEN; ///< length of universal name which would be made.
// The memory for getting the universal name information is allocated.
universalname = (UNIVERSAL_NAME_INFO *) Calloc (MAX_PATH_LEN * sizeof (Char));
retval = WNetGetUniversalName (pPath, UNIVERSAL_NAME_INFO_LEVEL, universalname, &length);
Free (universalname);
// NO_ERROR is returned only when it's drive mapped for shared network folder.
return (NO_ERROR == retval) ? true : false;
In case anyone stumbles upon this from Google search, here is some useful information from Apple:
There are numerous ways to test whether a volume is a network volume. The best method to use depends on the layer that you're working at:
If you're working on general application code, you should use NSURL for this. Construct a URL for any item on the volume and then call -getResourceValue:forKey:error: to get the NSURLVolumeIsLocalKey key; the returned value will be false (kCFBooleanFalse) for a network volume and true (kCFBooleanTrue) for a local volume.
If you're programming at the BSD layer, you can call statfs and test the MNT_LOCAL flag in the f_flags field that it returns. Alternatively, you can call getattrlist to request the ATTR_VOL_MOUNTFLAGS attribute, which can be more efficient than statfs under some circumstances.
https://developer.apple.com/library/mac/qa/nw09/_index.html
You can use DiskArbitration
#import <DiskArbitration/DiskArbitration.h>
+(BOOL)isNetworkVolume: (NSURL *)volumeURL
{
BOOL result = NO;
int err = 0;
DADiskRef disk;
DASessionRef session;
CFDictionaryRef descDict;
session = DASessionCreate(NULL);
if (session == NULL) {
err = EINVAL;
}
if (err == 0) {
disk = DADiskCreateFromVolumePath(NULL,session,(__bridge CFURLRef)volumeURL);
if (session == NULL) {
err = EINVAL;
}
}
if (err == 0) {
descDict = DADiskCopyDescription(disk);
if (descDict == NULL) {
err = EINVAL;
}
}
if (err == 0) {
CFBooleanRef VolumeNetworkKey = CFDictionaryGetValue(descDict,kDADiskDescriptionVolumeNetworkKey);
if (VolumeNetworkKey != NULL)
{
if (CFEqual(VolumeNetworkKey, kCFBooleanTrue)) {
result = YES;
}
}
}
if (descDict != NULL) {
CFRelease(descDict);
}
if (disk != NULL) {
CFRelease(disk);
}
if (session != NULL) {
CFRelease(session);
}
return result;
}
Check below how to detect whether your path exist on your machine or not:-
NSArray *arr=NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
NSString *str=[arr lastObject];
NSString *yourPath=[str stringByAppendingPathComponent:#"Volumes/abc/xyz"];
if([[NSFileManager defaultManager]fileExistsAtPath:yourPath])
{
NSLog(#"This is your Local Path");
}
You may use the NSLocalDomainMask and NSNetworkDomainMask in NSSearchPathForDirectoriesInDomains.
For your reference the link - http://www.filibeto.org/unix/macos/lib/dev/documentation/Cocoa/Conceptual/LowLevelFileMgmt/LowLevelFileMgmt.pdf. The topic " Locating Directories on the System " here discusses the NSSearchPathForDirectoriesInDomains class.

Is there a recommended way of making NSDatePicker use MY step sizes?

I'm wondering if some of you could offer advise on this.
I'm trying to change the NSDatePicker's step size to another value than 1.
On top of that I found that stepping minutes doesn't change the hours, nor the day.
I am using the delegate method datePickerCell:validateProposedDateValue:timeInterval:.
Now, though it does work as expected, the whole thing looks so much blown up that I started wondering if there is an easier way to accomplish this.
Any advise or direction for documentation is appreciated. Thank you.
Here's my code:
- (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue
timeInterval:(NSTimeInterval *)proposedTimeInterval {
DLog(#"date picker for: %#", [aDatePickerCell identifier]);
NSDate *newProposedDateValue = nil;
// just in case that we don't need a correction
NSDate *correctedProposedDateValue = *proposedDateValue;
// the interval that the step generates
// > 0 means: the old date is later than the new proposed date
// < 0 means: the old date is earlier than the new proposed date
int interval = [[self dateValue] timeIntervalSinceDate:*proposedDateValue];
// define expected interval values for our scenarios
// we don't care about a minute step that does not cross the hour here
// nor do we care about an hour step that does not cross the day
// minutes are stepped: minute is stepped but hour remains (01:59 <-> 01:00), so the difference is 59 minutes
int const minuteSteppedUpAcrossHour = -59 *60;
int const minuteSteppedDownAcrossHour = - minuteSteppedUpAcrossHour;
// nor do we care about an hour step that does not cross the day
// hours are stepped: hour is stepped but day remains (10.03.13 00:30 <-> 10.03.13 23:30) we have a difference of 23 hours
int const hourSteppedUpAcrossDay = -23 *60 *60;
int const hourSteppedDownAcrossDay = - hourSteppedUpAcrossDay;
// define correction values for our scenarios
int const anHour = 60 *60;
int const aDay = anHour *24;
switch (interval) {
case hourSteppedUpAcrossDay:
correctedProposedDateValue = [*proposedDateValue dateByAddingTimeInterval:(-aDay)];
break;
case minuteSteppedDownAcrossHour:
correctedProposedDateValue = [*proposedDateValue dateByAddingTimeInterval:(+anHour)];
break;
case hourSteppedDownAcrossDay:
correctedProposedDateValue = [*proposedDateValue dateByAddingTimeInterval:(+aDay)];
break;
case minuteSteppedUpAcrossHour:
correctedProposedDateValue = [*proposedDateValue dateByAddingTimeInterval:(-anHour)];
break;
default:
break;
}
if ([self dateValue] < correctedProposedDateValue) {
newProposedDateValue = [self roundDateUpForMinuteIntervalConstraint:correctedProposedDateValue];
} else {
newProposedDateValue = [self roundDateDownForMinuteIntervalConstraint:correctedProposedDateValue];
}
*proposedDateValue = newProposedDateValue;
}
- (NSDate *)roundDateUpForMinuteIntervalConstraint:(NSDate *)date {
return [self date:date roundedUpToMinutes:MINUTE_INTERVAL_CONSTRAINT_FOR_SESSIONS_START];
}
- (NSDate *)roundDateDownForMinuteIntervalConstraint:(NSDate *)date {
return [self date:date roundedDownToMinutes:MINUTE_INTERVAL_CONSTRAINT_FOR_SESSIONS_START];
}
- (NSDate *)date:(NSDate *)date roundedUpToMinutes:(int)minutes {
// Strip miliseconds by converting to int
int referenceTimeInterval = (int)[date timeIntervalSinceReferenceDate];
int remainingSeconds = referenceTimeInterval %(60 *minutes);
int timeRoundedUpToMinutes = 0;
if (remainingSeconds== 0) {
timeRoundedUpToMinutes = referenceTimeInterval;
} else {
timeRoundedUpToMinutes = referenceTimeInterval -remainingSeconds +(60 *minutes);
}
return [NSDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)timeRoundedUpToMinutes];
}
- (NSDate *)date:(NSDate *)date roundedDownToMinutes:(int)minutes {
// Strip miliseconds by converting to int
int referenceTimeInterval = (int)[date timeIntervalSinceReferenceDate];
int remainingSeconds = referenceTimeInterval %(60 *minutes);
int timeRoundedUpToMinutes = referenceTimeInterval -remainingSeconds;
return [NSDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)timeRoundedUpToMinutes];
}

MacOSX Lion: how to move a named window using CoreGraphics, without AppleScript?

Using CoreGraphics in a cocoa objective-c program running under Lion, I'd like to move a named window that is owned by a different process. I know I can do this via an auxiliary AppleScript method via ASOC, but I want to perform this task entirely within cocoa using CoreGraphics (or at least entirely within C or objective-c), and without any AppleScript, at all.
I know how to locate a named window of a named process using the code below, but once I get the info for that window, I haven't been able to figure out how to move it (see the comment "What do I do here ... ?" within this code). Could someone point me to some docs or make a suggestion as to how I can proceed?
Thanks in advance.
+(boolean_t)moveWindow:(NSString*)windowName ofProcess:(NSString*)processName to:(CGPoint*)location {
boolean_t result = false;
if (windowName == nil || processName == nil || location == nil) {
return (result);
}
CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFIndex nWindows = CFArrayGetCount(windows);
for (CFIndex i = 0; i < nWindows; i++) {
CFDictionaryRef windict = CFArrayGetValueAtIndex(windows, i);
CFNumberRef winOwnerPidRef = CFDictionaryGetValue(windict, kCGWindowOwnerPID);
if (winOwnerPidRef == nil) {
continue;
}
pid_t winOwnerPid = 0;
CFNumberGetValue(winOwnerPidRef, kCFNumberSInt32Type, (int*)&winOwnerPid);
if (winOwnerPid < 1) {
continue;
}
ProcessSerialNumber winOwnerPSN;
GetProcessForPID(winOwnerPid, &winOwnerPSN);
NSString* winOwner = nil;
ProcessSerialNumber psn;
psn.lowLongOfPSN = kNoProcess;
psn.highLongOfPSN = 0;
while (winOwner == nil && GetNextProcess(&psn) == noErr) {
if (psn.lowLongOfPSN != winOwnerPSN.lowLongOfPSN ||
psn.highLongOfPSN != winOwnerPSN.highLongOfPSN) {
continue;
}
CFStringRef procName = NULL;
if (CopyProcessName(&psn, &procName) == noErr) {
winOwner = (NSString*) procName;
}
CFRelease(procName);
}
if (winOwner == nil || [winOwner compare:processName] != NSOrderedSame) {
continue;
}
CFStringRef winNameRef = CFDictionaryGetValue(windict, kCGWindowName);
NSString* winName = (NSString*) winNameRef;
if (winName != nil && [winName compare:windowName] == NSOrderedSame) {
// ********************************************** //
// What do I do here in order to move the window? //
// ********************************************** //
result = true;
break;
}
}
return (result);
}
You can move the windows of other applications using Accessibility. Take a look at AXUIElementCreateApplication() and AXUIElementSetAttributeValue() with the attribute kAXPositionAttribute.
Note that Accessibility will need to be enabled (check "Enable access for assistive devices" in Universal Access Preferences) or your process will need to be trusted (see AXMakeProcessTrusted())

Is it possible to use FSEvents to get notifications that a folder has been moved?

I'm using the FSEvents API to get notifications of changes in a local directory that I'm tracking.
Is it possible to get a notification that the watched directory has been moved to another location on disk, using FSEvents or anything else?
Update:
Here is the code I have so far, I'm now trying to use the kFSEventStreamCreateFlagWatchRoot flag with FSEventStreamCreate to get the root changed notification, so far without success.
- (void)registerForFileSystemNotifications {
NSString *watchedDirectoryPath = [[NSUserDefaults standardUserDefaults] valueForKey:kMyWatchedDirectoryPathKey];
self.watchedDirectoryFileDescriptor = open([watchedDirectoryPath cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);
NSArray *paths = [NSArray arrayWithObject:watchedDirectoryPath];
void *appController = (void *)self;
FSEventStreamContext context = {0, appController, NULL, NULL, NULL};
FSEventStreamRef streamRef = FSEventStreamCreate(NULL,
&fsevents_callback,
&context,
(CFArrayRef) paths,
kFSEventStreamEventIdSinceNow,
(CFTimeInterval)2.0,
kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagWatchRoot);
FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(streamRef);
}
void fsevents_callback(ConstFSEventStreamRef streamRef,
void *userData,
size_t numumberOfEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
MyAppController *appController = (MyAppController *)userData;
char *newPath = calloc(4096, sizeof(char));
int pathIntPointer = (int)newPath;
int length = fcntl(appController.watchedDirectoryFileDescriptor, F_GETPATH, pathIntPointer);
NSString *newPathString = [[NSString alloc] initWithBytes:newPath length:(NSUInteger)length encoding:NSUTF8StringEncoding];
NSLog(#"newPathString: %#", newPathString); // empty
}
Yes. Pass kFSEventStreamCreateFlagWatchRoot as the last argument to FSEventStreamCreate, and you'll be notified if the directory's moved or renamed. From the docs:
Request notifications of changes along the path to the path(s) you're watching. For example, with this flag, if you watch "/foo/bar" and it is renamed to "/foo/bar.old", you would receive a RootChanged event. The same is true if the directory "/foo" were renamed. The event you receive is a special event: the path for the event is the original path you specified, the flag kFSEventStreamEventFlagRootChanged is set and event ID is zero. RootChanged events are useful to indicate that you should rescan a particular hierarchy because it changed completely (as opposed to the things inside of it changing). If you want to track the current location of a directory, it is best to open the directory before creating the stream so that you have a file descriptor for it and can issue an F_GETPATH fcntl() to find the current path.
Edit: adding fcntl example
That cocoadev example suggests the author's a bit inexperienced with pointers. The pathIntPointer is not only unnecessary, it's also the cause of your problem. Error checking of the return code from fnctl would have caught it. Here's a revised version of your callback:
void fsevents_callback(ConstFSEventStreamRef streamRef,
void *userData,
size_t numumberOfEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
MyAppController *appController = (MyAppController *)userData;
char newPath[ MAXPATHLEN ];
int rc;
rc = fcntl( appController.watchedDirectoryFileDescriptor, F_GETPATH, newPath );
if ( rc == -1 ) {
perror( "fnctl F_GETPATH" );
return;
}
NSString *newPathString = [[NSString alloc] initWithUTF8String: newPath ];
NSLog(#"newPathString: %#", newPathString);
[ newPathString release ];
}

Free VRam on OS X

does anyone know how to get the free(!) vram on os x?
I know that you can query for a registry entry:
typeCode = IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR(kIOFBMemorySizeKey),
kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents);
but this will return ALL vram, not the free vram. Under windows you can query for free VRAM using directshow
mDDrawResult = DirectDrawCreate(NULL, &mDDraw, NULL);
mDDrawResult = mDDraw->QueryInterface(IID_IDirectDraw2, (LPVOID *)&mDDraw2);
DDSCAPS ddscaps;
DWORD totalmem, freemem;
ddscaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
mDDrawResult = mDDraw2->GetAvailableVidMem(&ddscaps, &totalmem, &freemem);
Ugly, but it works. Anyone knows the osx way?
Best
Wendy
answering myself so others may use this:
#include <IOKit/graphics/IOGraphicsLib.h>
size_t currentFreeVRAM()
{
kern_return_t krc;
mach_port_t masterPort;
krc = IOMasterPort(bootstrap_port, &masterPort);
if (krc == KERN_SUCCESS)
{
CFMutableDictionaryRef pattern = IOServiceMatching(kIOAcceleratorClassName);
//CFShow(pattern);
io_iterator_t deviceIterator;
krc = IOServiceGetMatchingServices(masterPort, pattern, &deviceIterator);
if (krc == KERN_SUCCESS)
{
io_object_t object;
while ((object = IOIteratorNext(deviceIterator)))
{
CFMutableDictionaryRef properties = NULL;
krc = IORegistryEntryCreateCFProperties(object, &properties, kCFAllocatorDefault, (IOOptionBits)0);
if (krc == KERN_SUCCESS)
{
CFMutableDictionaryRef perf_properties = (CFMutableDictionaryRef) CFDictionaryGetValue( properties, CFSTR("PerformanceStatistics") );
//CFShow(perf_properties);
// look for a number of keys (this is mostly reverse engineering and best-guess effort)
const void* free_vram_number = CFDictionaryGetValue(perf_properties, CFSTR("vramFreeBytes"));
if (free_vram_number)
{
ssize_t vramFreeBytes;
CFNumberGetValue( (CFNumberRef) free_vram_number, kCFNumberSInt64Type, &vramFreeBytes);
return vramFreeBytes;
}
}
if (properties) CFRelease(properties);
IOObjectRelease(object);
}
IOObjectRelease(deviceIterator);
}
}
return 0; // when we come here, this is a fail
}
i am somewhat surprised that this query takes almost 3 msec ..
be aware that there may be more than one accelerator on your system ( eg. macbook )
so be sure you select the proper one for the query

Resources