#import <assert.h>
#import "RExplorer.h"
#include <IOKit/IOKitLibPrivate.h>
mach_port_t gMasterPort;
void callback(CFMachPortRef port, void *msg, CFIndex size, void *info);
static void IOREInterestCallback(
void * refcon,
io_service_t service,
natural_t messageType,
void * messageArgument );
static void IOREMatchingCallback(
void * refcon,
io_iterator_t iterator );
@implementation NSDictionary (Compare)
- (NSComparisonResult)compareNames:(NSDictionary *)dict2
{
return [(NSString *)[self objectForKey:@"name"] caseInsensitiveCompare:(NSString *)[dict2 objectForKey:@"name"]];
}
@end
@implementation RExplorer
static void addChildrenOfPlistToMapsRecursively(id plist, NSMapTable *_parentMap, NSMapTable *_keyMap) {
// Adds all the children of the given plist to the map-tables. Does not add the plist itself.
if ([plist isKindOfClass:[NSDictionary class]]) {
NSEnumerator *keyEnum = [plist keyEnumerator];
NSString *curKey;
id curChild;
while ((curKey = [keyEnum nextObject]) != nil) {
curChild = [plist objectForKey:curKey];
NSMapInsert(_parentMap, curChild, plist);
NSMapInsert(_keyMap, curChild, curKey);
addChildrenOfPlistToMapsRecursively(curChild, _parentMap, _keyMap);
}
} else if ([plist isKindOfClass:[NSArray class]]) {
unsigned i, c = [plist count];
id curChild;
for (i=0; i<c; i++) {
curChild = [plist objectAtIndex:i];
NSMapInsert(_parentMap, curChild, plist);
NSMapInsert(_keyMap, curChild, [NSString stringWithFormat:@" addChildrenOfPlistToMapsRecursively(curChild, _parentMap, _keyMap);
}
}
}
- (id)init
{
io_registry_entry_t entry;
kern_return_t kr;
io_object_t notification;
self = [super init];
gMasterPort = kIOMasterPortDefault;
notifyPort = IONotificationPortCreate( gMasterPort );
port = IONotificationPortGetMachPort( notifyPort );
assert( KERN_SUCCESS == (
kr = IOServiceAddInterestNotification( notifyPort,
IORegistryEntryFromPath( gMasterPort, kIOServicePlane ":/"),
kIOBusyInterest, &IOREInterestCallback, self, ¬ification )
));
assert( KERN_SUCCESS == (
kr = IOServiceAddMatchingNotification( notifyPort, kIOFirstMatchNotification,
IOServiceMatching("IOService"),
&IOREMatchingCallback, self, ¬ification )
));
while ( (entry = IOIteratorNext( notification )) ) {
IOObjectRelease( entry );
}
assert( KERN_SUCCESS == (
kr = IOServiceAddMatchingNotification( notifyPort, kIOTerminatedNotification,
IOServiceMatching("IOService"),
&IOREMatchingCallback, self, ¬ification )
));
while ( (entry = IOIteratorNext( notification )) ) {
IOObjectRelease( entry );
}
// create a timer to check for hardware additions/removals, etc.
updateTimer = [[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(checkForUpdate:) userInfo:nil repeats:YES] retain];
// register services
[NSApp registerServicesMenuSendTypes: [NSArray arrayWithObjects: NSStringPboardType, nil] returnTypes: [NSArray arrayWithObjects: NSStringPboardType, nil]];
return self;
}
- (void)awakeFromNib
{
int prefsSetting = 0;
[self initializeRegistryDictionaryWithPlane:kIOServicePlane];
[splitView setVertical:NO];
[propertiesOutlineView setDelegate:self];
[propertiesOutlineView setDataSource:self];
[browser setDelegate:self];
[browser setMinColumnWidth:170];
[browser setMaxVisibleColumns:7];
[planeBrowser setDelegate:self];
[window setDelegate:self];
keyColumn = [[propertiesOutlineView tableColumns] objectAtIndex:0];
valueColumn = [[propertiesOutlineView tableColumns] objectAtIndex:1];
[window setFrameAutosaveName:@"MainWindow"];
[window setFrameUsingName:@"MainWindow"];
[planeWindow setFrameAutosaveName:@"PlaneWindow"];
[planeWindow setFrameUsingName:@"PlaneWindow"];
[inspectorWindow setFrameAutosaveName:@"InspectorWindow"];
[inspectorWindow setFrameUsingName:@"InspectorWindow"];
[browser setPathSeparator:@":"];
prefsSetting = [[NSUserDefaults standardUserDefaults] integerForKey:@"UpdatePrefs"];
[updatePrefsMatrix selectCellAtRow:prefsSetting column:0];
[objectDescription setStringValue:@""];
[objectState setStringValue:@""];
return;
}
- (void)dealloc
{
[currentSelectedItemDict release];
[updateTimer release];
[super dealloc];
return;
}
- (NSDictionary *)dictForIterated:(io_registry_entry_t)passedEntry
{
kern_return_t status;
io_name_t name;
io_name_t className;
io_name_t location;
int retain, busy;
uint64_t state;
char * s;
char stateStr[256];
io_registry_entry_t iterated;
io_iterator_t regIterator;
NSMutableDictionary *localDict = [NSMutableDictionary dictionary];
NSMutableArray *localArray = [NSMutableArray array];
status = IORegistryEntryGetChildIterator(passedEntry, currentPlane, ®Iterator);
assert(status == KERN_SUCCESS);
status = IORegistryEntryGetNameInPlane(passedEntry, currentPlane, name);
assert(status == KERN_SUCCESS);
status = IOObjectGetClass(passedEntry, className);
assert(status == KERN_SUCCESS);
status = IORegistryEntryGetLocationInPlane(passedEntry, currentPlane, location);
if (status == KERN_SUCCESS) {
strcat(name, "@");
strcat(name, location);
}
s = stateStr + sprintf(stateStr, "(");
retain = IOObjectGetRetainCount(passedEntry);
status = IOServiceGetState(passedEntry, &state);
if (status == KERN_SUCCESS) {
status = IOServiceGetBusyState(passedEntry, &busy);
if (status == KERN_SUCCESS)
busy = 0;
s += sprintf(s, " state & kIOServiceMatchedState ? "" : "!",
state & kIOServiceRegisteredState ? "" : "!",
state & kIOServiceInactiveState ? "in" : "",
busy );
}
s += sprintf(s, "retain count
while ( (iterated = IOIteratorNext(regIterator)) != NULL ) {
id insideDict = [self dictForIterated:iterated];
[localArray addObject:insideDict];
// IOObjectRelease(iterated);
}
IOObjectRelease(regIterator);
[localDict setObject:localArray forKey:@"children"];
[localDict setObject:[NSString stringWithFormat:@" [localDict setObject:[NSString stringWithFormat:@" [localDict setObject:[NSString stringWithFormat:@"
[localDict setObject:[NSNumber numberWithInt:passedEntry] forKey:@"regEntry"];
return [NSDictionary dictionaryWithDictionary:localDict];
}
- (void)initializeRegistryDictionaryWithPlane:(const char *)plane
{
io_registry_entry_t rootEntry;
NSMutableDictionary *localDict = nil;
if (registryDict) {
[registryDict release];
}
registryDict = nil;
currentPlane = plane;
rootEntry = IORegistryGetRootEntry(kIOMasterPortDefault);
localDict = (NSMutableDictionary *)[self dictForIterated:rootEntry];
registryDict = [localDict retain];
}
- (NSDictionary *) propertiesForRegEntry:(NSDictionary *)object
{
NSDictionary * props;
if ([object objectForKey:@"properties"]) {
props = [[object objectForKey:@"properties"] retain];
//[informationView setString:[[object objectForKey:@"properties"] description]];
} else if ([object objectForKey:@"regEntry"]) {
CFDictionaryRef dict;
kern_return_t status;
status = IORegistryEntryCreateCFProperties([[object objectForKey:@"regEntry"] intValue],
&dict,
kCFAllocatorDefault, kNilOptions);
assert( KERN_SUCCESS == status );
assert( CFDictionaryGetTypeID() == CFGetTypeID(dict));
props = (NSDictionary *) dict;
} else
props = [object retain];
return [props autorelease];
}
- (void)changeLevel:(id)sender
{
id object = nil;
int column = [sender selectedColumn];
int row = [sender selectedRowInColumn:column];
NSDictionary * newItemDict;
int count;
int i;
autoUpdate = NO;
if (column < 0 || row < 0)
return;
if (column)
object = [[self childArrayAtColumn:column] objectAtIndex:row];
else
object = registryDict;
newItemDict = [self propertiesForRegEntry:object];
if (currentSelectedItemDict != newItemDict) {
[currentSelectedItemDict release];
currentSelectedItemDict = newItemDict;
[currentSelectedItemDict retain];
[inspectorText setString:[newItemDict description]];
[inspectorText display];
}
[objectDescription setStringValue:[NSString stringWithFormat:@" [object objectForKey:@"name"],
[object objectForKey:@"className"]]];
[objectState setStringValue:[NSString stringWithFormat:@" [object objectForKey:@"state"]]];
// go through and create a uniqued dictionary where all the values are uniqued and all the keys are uniqued
if ([currentSelectedItemDict count]) {
NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
NSArray *keyArray = [currentSelectedItemDict allKeys];
NSArray *valueArray = [currentSelectedItemDict allValues];
count = [currentSelectedItemDict count];
for (i = 0; i < count ; i++) {
if (CFGetTypeID([currentSelectedItemDict objectForKey:[keyArray objectAtIndex:i]]) == CFBooleanGetTypeID()) {
if ([[[currentSelectedItemDict objectForKey:[keyArray objectAtIndex:i]] description] isEqualToString:@"0"]) {
[newDict setObject:[[[RBool alloc] initWithBool:NO] autorelease] forKey:[keyArray objectAtIndex:i]];
} else {
[newDict setObject:[[[RBool alloc] initWithBool:YES] autorelease] forKey:[keyArray objectAtIndex:i]];
}
} else {
id newObj;
if ([[valueArray objectAtIndex:i] isKindOfClass:[NSString class]]) {
newObj = [NSString stringWithString:[valueArray objectAtIndex:i]];
} else if ([[valueArray objectAtIndex:i] isKindOfClass:[NSArray class]]) {
newObj = [NSArray arrayWithArray:[valueArray objectAtIndex:i]];
} else if ([[valueArray objectAtIndex:i] isKindOfClass:[NSDictionary class]]) {
newObj = [NSDictionary dictionaryWithDictionary:[valueArray objectAtIndex:i]];
} else {
newObj = [[[valueArray objectAtIndex:i] copy] autorelease];
}
[newDict setObject:newObj forKey:[keyArray objectAtIndex:i]];
}
}
[currentSelectedItemDict release];
currentSelectedItemDict = [newDict retain];
}
[self initializeMapsForDictionary:currentSelectedItemDict];
[propertiesOutlineView reloadData];
#if 0
count = [propertiesOutlineView numberOfRows];
for (i = 0; i < count ; i++) {
[propertiesOutlineView expandItem:[propertiesOutlineView itemAtRow:i] expandChildren:YES];
}
#endif
return;
}
- (void)dumpDictionaryToOutput:(id)sender
{
NSLog(@" return;
}
- (NSArray *)childArrayAtColumn:(int)column
{
int i = 0;
id lastDict = registryDict;
NSArray * localArray = nil;
for (i = 0; (i < column); i++ ) {
if (localArray) {
lastDict = [localArray objectAtIndex:[browser selectedRowInColumn:i]];
}
localArray = [[lastDict objectForKey:@"children"] sortedArrayUsingSelector:@selector(compareNames:)];
//NSLog(@"array = }
return localArray;
}
- (void)displayAboutWindow:(id)sender
{
if (aboutBoxOptions == nil) {
aboutBoxOptions = [[NSDictionary alloc] initWithObjectsAndKeys:
@"2000", @"CopyrightStartYear",
nil
];
}
NSShowSystemInfoPanel(aboutBoxOptions);
return;
}
- (void)displayPlaneWindow:(id)sender
{
[planeBrowser loadColumnZero];
[planeWindow makeKeyAndOrderFront:self];
return;
}
- (void)switchRootPlane:(id)sender
{
[self initializeRegistryDictionaryWithPlane:[[[sender selectedCell] stringValue] cString]];
[browser loadColumnZero];
[self changeLevel:browser];
[currentSelectedItemDict release];
currentSelectedItemDict = nil;
[propertiesOutlineView reloadData];
return;
}
- (void)doUpdate
{
int wasAuto = autoUpdate;
[self changeLevel:browser];
autoUpdate = wasAuto;
return;
}
- (void)reload
{
// reload
NSString *currentPath = [browser path];
[self initializeRegistryDictionaryWithPlane:currentPlane];
[browser loadColumnZero];
[browser setPath:currentPath];
[self doUpdate];
}
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
if (NSAlertDefaultReturn == returnCode) {
[self reload];
}
[NSApp endSheet:window];
dialogDisplayed = NO;
}
- (void)registryHasChanged
{
int prefsSetting = [[NSUserDefaults standardUserDefaults] integerForKey:@"UpdatePrefs"];
if (prefsSetting == 1) {
[self reload];
} else if (prefsSetting == 0 && !dialogDisplayed) {
NSBeginInformationalAlertSheet(NSLocalizedString(@"The IOKit Registry has been changed.\nDo you wish to update your display or skip this update?", @""), NSLocalizedString(@"Update", @""), NSLocalizedString(@"Skip", @""), NULL, window, self, @selector(sheetDidEnd:returnCode:contextInfo:), NULL, NULL, @"");
dialogDisplayed = YES;
}
}
- (void)forceUpdate:(id)sender
{
[self reload];
return;
}
// Window delegation
- (void)windowWillClose:(id)sender
{
[NSApp stop:nil];
return;
}
- (void)initializeMapsForDictionary:(NSDictionary *)dict
{
if (_parentMap) {
NSFreeMapTable(_parentMap);
}
if (_keyMap) {
NSFreeMapTable(_keyMap);
}
_parentMap = NSCreateMapTableWithZone(NSIntMapKeyCallBacks, NSNonRetainedObjectMapValueCallBacks, 100, [self zone]);
_keyMap = NSCreateMapTableWithZone(NSIntMapKeyCallBacks, NSObjectMapValueCallBacks, 100, [self zone]);
addChildrenOfPlistToMapsRecursively(dict, _parentMap, _keyMap);
//NSLog(@"
return;
}
static void IOREInterestCallback(
void * refcon,
io_service_t service,
natural_t messageType,
void * messageArgument )
{
((RExplorer *)refcon)->registryHasQuieted = messageArgument ? FALSE : TRUE;
}
static void IOREMatchingCallback(
void * refcon,
io_iterator_t iterator )
{
io_registry_entry_t entry;
while ( (entry = IOIteratorNext( iterator )) ) {
IOObjectRelease( entry );
}
((RExplorer *)refcon)->registryHasChanged = TRUE;
}
- (void)checkForUpdate:(NSTimer *)timer
{
kern_return_t kr;
struct {
mach_msg_header_t msgHdr;
void * content[1024];
} msg;
registryHasQuieted = FALSE;
do {
kr = mach_msg(&msg.msgHdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), port, 0, MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
break;
}
IODispatchCalloutFromMessage(NULL, &msg.msgHdr, notifyPort);
} while ( TRUE );
if (registryHasChanged && registryHasQuieted) {
registryHasChanged = FALSE;
[self registryHasChanged];
} else if (autoUpdate)
[self doUpdate];
return;
}
// search the dictionaries
- (NSArray *)searchResultsForText:(NSString *)text searchKeys:(BOOL)keys searchValues:(BOOL)values
{
if (!keys && !values) {
return [NSArray array];
}
if (keys) {
NSMutableArray *array = [NSMutableArray arrayWithArray:[self searchKeysResultsInDictionary:registryDict forText:text passedPath:@""]];
// do the root directory stuff here ...
NSEnumerator *rootEnum = [[[registryDict objectForKey:@"properties"] allKeys] objectEnumerator];
id aRootEntry = nil;
while (aRootEntry = [rootEnum nextObject]) {
if ([aRootEntry isEqualToString:@"IOCatalogue"]) {
// special case
}
}
return [NSArray arrayWithArray:array];
}
return [NSArray array];
}
- (NSArray *)searchKeysResultsInDictionary:(NSDictionary *)dict forText:(NSString *)text passedPath:(NSString *)path
{
NSArray *children = [dict objectForKey:@"children"];
NSEnumerator *kidEnum = [children objectEnumerator];
id aKid = nil;
NSMutableArray *array = [NSMutableArray array];
if ([(NSString *)[dict objectForKey:@"name"] length]) {
if ([[[dict objectForKey:@"name"] uppercaseString] rangeOfString:[text uppercaseString]].length > 0) {
[array addObject:[NSString stringWithFormat:@" }
}
while (aKid = [kidEnum nextObject]) {
[array addObjectsFromArray:[self searchKeysResultsInDictionary:aKid forText:text passedPath:[NSString stringWithFormat:@" }
return [[array copy] autorelease];
}
- (void)goToPath:(NSString *)path
{
NSString *newPath = [@":Root" stringByAppendingString:path];
int count = [[path componentsSeparatedByString:@":"] count];
if (count > 2) {
count -= 2;
}
[browser setPath:newPath];
[self changeLevel:browser];
[browser scrollColumnToVisible:count];
return;
}
- (void)copy:(id)sender
{
NSString *currentPath = [[browser path] substringFromIndex:5];
[[NSPasteboard generalPasteboard] declareTypes: [NSArray arrayWithObject:NSStringPboardType] owner: [self class]];
[[NSPasteboard generalPasteboard] setString:currentPath forType:NSStringPboardType];
}
- (void)updatePrefs:(id)sender
{
[[NSUserDefaults standardUserDefaults] setInteger:[updatePrefsMatrix selectedRow] forKey:@"UpdatePrefs"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
static void LogNSString( NSFileHandle *fileHandle, NSString *string)
{
char timeStr[16];
time_t t = time(NULL);
strftime(timeStr, sizeof(timeStr), "
[fileHandle writeData:[[NSString stringWithFormat:@"}
-(void)application:(NSApplication *)sender runTest:(unsigned int)testToRun duration:(NSTimeInterval)duration
{
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
NSString *logFileName = [NSString stringWithFormat:@" NSString *logFileDir = [[NSHomeDirectory() stringByAppendingPathComponent:@"Library"] stringByAppendingPathComponent:@"Logs"];
NSString *logFilePath = [logFileDir stringByAppendingPathComponent:logFileName];
[[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Library"] attributes:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:logFileDir attributes:nil];
[[NSFileManager defaultManager] createFileAtPath:logFilePath contents:nil attributes:nil];
NSFileHandle *logFile = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
NSString *initMessage = [NSString stringWithFormat:@"Test: LogNSString( logFile, initMessage);
NSDate *startTime = [NSDate date];
NSTimeInterval runningTime = 0;
int j = 0;
// register special defaults for testing ...
do {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
LogNSString( logFile, [NSString stringWithFormat:@"Iteration: LogNSString( logFile, @"Message: Start iterating all functions ...");
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]];
[self displayAboutWindow:self];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]];
// for updates 20 times
int i = 0;
for (i=0;i<20;i++) {
[self forceUpdate:self];
LogNSString( logFile, [NSString stringWithFormat:@"Output: ...forced update
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
LogNSString( logFile, [NSString stringWithFormat:@"Message: ...done with iterating all functions."]);
runningTime = -[startTime timeIntervalSinceNow];
j++;
[pool release];
} while (runningTime <= duration);
LogNSString( logFile, [NSString stringWithFormat:@"Message: Test completed in
[logFile closeFile];
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
// test self test
// [self application:NSApp runTest:0 duration:0];
return;
}
@end