diff -r 0b375a3ceaa0 -r 588a8e6e2041 QTfrontend/util/platform/NSWorkspace_RBAdditions.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QTfrontend/util/platform/NSWorkspace_RBAdditions.m Mon Jan 14 12:35:01 2013 +0100 @@ -0,0 +1,263 @@ +// +// NSWorkspace_RBAdditions.m +// PathProps +// +// Created by Rainer Brockerhoff on 10/04/2007. +// Copyright 2007 Rainer Brockerhoff. All rights reserved. +// + +#import "NSWorkspace_RBAdditions.h" +#include +#include +#include + +NSString* NSWorkspace_RBfstypename = @"NSWorkspace_RBfstypename"; +NSString* NSWorkspace_RBmntonname = @"NSWorkspace_RBmntonname"; +NSString* NSWorkspace_RBmntfromname = @"NSWorkspace_RBmntfromname"; +NSString* NSWorkspace_RBdeviceinfo = @"NSWorkspace_RBdeviceinfo"; +NSString* NSWorkspace_RBimagefilepath = @"NSWorkspace_RBimagefilepath"; +NSString* NSWorkspace_RBconnectiontype = @"NSWorkspace_RBconnectiontype"; +NSString* NSWorkspace_RBpartitionscheme = @"NSWorkspace_RBpartitionscheme"; +NSString* NSWorkspace_RBserverURL = @"NSWorkspace_RBserverURL"; + +// This static funtion concatenates two strings, but first checks several possibilities... +// like one or the other nil, or one containing the other already. + +static NSString* AddPart(NSString* first,NSString* second) { + if (!second) { + return first; + } + second = [second stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (first) { + if ([first rangeOfString:second options:NSCaseInsensitiveSearch].location==NSNotFound) { + if ([second rangeOfString:first options:NSCaseInsensitiveSearch].location==NSNotFound) { + return [NSString stringWithFormat:@"%@; %@",first,second]; + } + return second; + } + return first; + } + return second; +} + +// This static functions recurses "upwards" over the IO registry. Returns strings that are concatenated +// and ultimately end up under the NSWorkspace_RBdeviceinfo key. +// This isn't too robust in that it assumes that objects returned by the objectForKey methods are +// either strings or dictionaries. A "standard" implementations would use either only CoreFoundation and +// IOKit calls for this, or do more robust type checking on the returned objects. +// +// Also notice that this works as determined experimentally in 10.4.9, there's no official docs I could find. +// YMMV, and it may stop working in any new version of Mac OS X. + +static NSString* CheckParents(io_object_t thing,NSString* part,NSMutableDictionary* dict) { + NSString* result = part; + io_iterator_t parentsIterator = 0; + kern_return_t kernResult = IORegistryEntryGetParentIterator(thing,kIOServicePlane,&parentsIterator); + if ((kernResult==KERN_SUCCESS)&&parentsIterator) { + io_object_t nextParent = 0; + while ((nextParent = IOIteratorNext(parentsIterator))) { + NSDictionary* props = nil; + NSString* image = nil; + NSString* partition = nil; + NSString* connection = nil; + kernResult = IORegistryEntryCreateCFProperties(nextParent,(CFMutableDictionaryRef*)&props,kCFAllocatorDefault,0); + if (IOObjectConformsTo(nextParent,"IOApplePartitionScheme")) { + partition = [props objectForKey:@"Content Mask"]; + } else if (IOObjectConformsTo(nextParent,"IOMedia")) { + partition = [props objectForKey:@"Content"]; + } else if (IOObjectConformsTo(nextParent,"IODiskImageBlockStorageDeviceOutKernel")) { + NSData* data = nil; + if ((data = [[props objectForKey:@"Protocol Characteristics"] objectForKey:@"Virtual Interface Location Path"])) { + image = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease]; + } + } else if (IOObjectConformsTo(nextParent,"IOHDIXHDDriveInKernel")) { + image = [props objectForKey:@"KDIURLPath"]; + } + NSDictionary* subdict; + if ((subdict = [props objectForKey:@"Protocol Characteristics"])) { + connection = [subdict objectForKey:@"Physical Interconnect"]; + } else { + connection = [props objectForKey:@"Physical Interconnect"]; + } + if (connection) { + [dict setObject:AddPart([dict objectForKey:NSWorkspace_RBconnectiontype],connection) forKey:NSWorkspace_RBconnectiontype]; + } + if (partition) { + [dict setObject:partition forKey:NSWorkspace_RBpartitionscheme]; + } + if (image) { + [dict setObject:image forKey:NSWorkspace_RBimagefilepath]; + } + NSString* value; + if ((subdict = [props objectForKey:@"Device Characteristics"])) { + if ((value = [subdict objectForKey:@"Product Name"])) { + result = AddPart(result,value); + } + if ((value = [subdict objectForKey:@"Product Revision Level"])) { + result = AddPart(result,value); + } + if ((value = [subdict objectForKey:@"Vendor Name"])) { + result = AddPart(result,value); + } + } + if ((value = [props objectForKey:@"USB Serial Number"])) { + result = AddPart(result,value); + } + if ((value = [props objectForKey:@"USB Vendor Name"])) { + result = AddPart(result,value); + } + NSString* cls = [(NSString*)IOObjectCopyClass(nextParent) autorelease]; + if (![cls isEqualToString:@"IOPCIDevice"]) { + +// Uncomment the following line to have the device tree dumped to the console. +// NSLog(@"=================================> %@:%@\n",cls,props); + + result = CheckParents(nextParent,result,dict); + } + IOObjectRelease(nextParent); + } + } + if (parentsIterator) { + IOObjectRelease(parentsIterator); + } + return result; +} + +// This formats the (partially undocumented) AFPXMountInfo info into a string. + +/* +static NSString* FormatAFPURL(AFPXVolMountInfoPtr mountInfo,NSString** devdesc) { + UInt8* work = ((UInt8*)mountInfo)+mountInfo->serverNameOffset; + if (devdesc) { + *devdesc = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease]; + } + work = ((UInt8*)mountInfo)+mountInfo->volNameOffset; + NSString* volname = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease]; + work = ((UInt8*)mountInfo)+mountInfo->alternateAddressOffset; + AFPAlternateAddress* afpa = (AFPAlternateAddress*)work; + AFPTagData* afpta = (AFPTagData*)(&afpa->fAddressList); + NSString* ip = nil; + NSString* dns = nil; + int i = afpa->fAddressCount; + while ((i-->0)) { + switch (afpta->fType) { + case kAFPTagTypeIP: + if (!ip) { + ip = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease]; + } + break; + case kAFPTagTypeIPPort: + ip = [NSString stringWithFormat:@"%u.%u.%u.%u:%u",afpta->fData[0],afpta->fData[1],afpta->fData[2],afpta->fData[3],OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[4])]; + break; + case kAFPTagTypeDNS: + dns = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease]; + break; + case 0x07: + ip = [NSString stringWithFormat:@"[%x:%x:%x:%x:%x:%x:%x:%x]",OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[0]), + OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[2]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[4]), + OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[6]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[8]), + OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[10]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[12]), + OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[14])]; + break; + } + afpta = (AFPTagData*)((char*)afpta+afpta->fLength); + } + return [NSString stringWithFormat:@"afp://%@/%@",dns?:(ip?:@""),volname]; +} +*/ + +@implementation NSWorkspace (NSWorkspace_RBAdditions) + +// Returns a NSDictionary with properties for the path. See details in the .h file. +// This assumes that the length of path is less than PATH_MAX (currently 1024 characters). + +- (NSDictionary*)propertiesForPath:(NSString*)path { + const char* ccpath = (const char*)[path fileSystemRepresentation]; + NSMutableDictionary* result = nil; + struct statfs fs; + if (!statfs(ccpath,&fs)) { + NSString* from = [NSString stringWithUTF8String:fs.f_mntfromname]; + result = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:fs.f_fstypename],NSWorkspace_RBfstypename, + [NSString stringWithUTF8String:fs.f_mntonname],NSWorkspace_RBmntonname, + nil]; + if (strncmp(fs.f_mntfromname,"/dev/",5)==0) { +// For a local volume,get the IO registry tree and search it for further info. + mach_port_t masterPort = 0; + io_iterator_t mediaIterator = 0; + kern_return_t kernResult = IOMasterPort(bootstrap_port,&masterPort); + if (kernResult==KERN_SUCCESS) { + CFMutableDictionaryRef classesToMatch = IOBSDNameMatching(masterPort,0,&fs.f_mntfromname[5]); + if (classesToMatch) { + kernResult = IOServiceGetMatchingServices(masterPort,classesToMatch,&mediaIterator); + if ((kernResult==KERN_SUCCESS)&&mediaIterator) { + io_object_t firstMedia = 0; + while ((firstMedia = IOIteratorNext(mediaIterator))) { + NSString* stuff = CheckParents(firstMedia,nil,result); + if (stuff) { + [result setObject:stuff forKey:NSWorkspace_RBdeviceinfo]; + } + IOObjectRelease(firstMedia); + } + } + } + } + if (mediaIterator) { + IOObjectRelease(mediaIterator); + } + if (masterPort) { + mach_port_deallocate(mach_task_self(),masterPort); + } + } + //Don't need this for disk images, gets around warnings for some deprecated functions + + /* else { +// For a network volume, get the volume reference number and use to get the server URL. + FSRef ref; + if (FSPathMakeRef((const UInt8*)ccpath,&ref,NULL)==noErr) { + FSCatalogInfo info; + if (FSGetCatalogInfo(&ref,kFSCatInfoVolume,&info,NULL,NULL,NULL)==noErr) { + ParamBlockRec pb; + UInt16 vmisize = 0; + VolumeMountInfoHeaderPtr mountInfo = NULL; + pb.ioParam.ioCompletion = NULL; + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = info.volume; + pb.ioParam.ioBuffer = (Ptr)&vmisize; + pb.ioParam.ioReqCount = sizeof(vmisize); + if ((PBGetVolMountInfoSize(&pb)==noErr)&&vmisize) { + mountInfo = (VolumeMountInfoHeaderPtr)malloc(vmisize); + if (mountInfo) { + pb.ioParam.ioBuffer = (Ptr)mountInfo; + pb.ioParam.ioReqCount = vmisize; + if (PBGetVolMountInfo(&pb)==noErr) { + NSString* url = nil; + switch (mountInfo->media) { + case AppleShareMediaType: + url = FormatAFPURL((AFPXVolMountInfoPtr)mountInfo,&from); + break; + case 'http': + url = from; + break; + case 'crbm': + case 'nfs_': + case 'cifs': + url = [NSString stringWithUTF8String:(char*)mountInfo+sizeof(VolumeMountInfoHeader)+sizeof(OSType)]; + break; + } + if (url) { + [result setObject:url forKey:NSWorkspace_RBserverURL]; + } + } + } + free(mountInfo); + } + } + } + }*/ + [result setObject:from forKey:NSWorkspace_RBmntfromname]; + } + return result; +} + +@end