QTfrontend/NSWorkspace_RBAdditions.m
author koda
Thu, 17 Jun 2010 19:57:51 +0200
changeset 3513 f589230fa21b
parent 2400 2422ea85d100
child 3697 d5b30d6373fc
permissions -rw-r--r--
now it's possible to select the scheme file in the ifrontendfix a type about loading an image (iphone file system IS case senstive) add rotation for iphone build too make the ifrontend work again with 3.0 sdk reworked openalbridge following most of an old implementation by Smaxx and making it more modular -- now sources are limited but the memory extension and cleanup is todo nil'd many variables in engine that were causing intialization problems

//
//  NSWorkspace_RBAdditions.m
//  PathProps
//
//  Created by Rainer Brockerhoff on 10/04/2007.
//  Copyright 2007 Rainer Brockerhoff. All rights reserved.
//

#import "NSWorkspace_RBAdditions.h"
#include <IOKit/IOKitLib.h>
#include <sys/mount.h>

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