// // UKVerpackAppDelegate.m // VerpackIt // // Created by Uli Kusterer on 15.09.04. // Copyright 2004 M. Uli Kusterer. All rights reserved. // #import "UKVerpackAppDelegate.h" #import "PBXArchive.h" #import "PBXProject.h" #import "PBXGroup.h" #import "PBXFileReference.h" #import "NSString+PartialPaths.h" @implementation UKVerpackAppDelegate -(BOOL) application:(NSApplication *)sender openFile:(NSString *)filename { [projectPackage autorelease]; projectPackage = filename; if( [[filename pathExtension] isEqualToString: @"xcode"] || [[filename pathExtension] isEqualToString: @"pbproj"] || [[filename pathExtension] isEqualToString: @"xcodeproj"] ) filename = [filename stringByAppendingPathComponent: @"project.pbxproj"]; else projectPackage = [filename stringByDeletingLastPathComponent]; [dict autorelease]; dict = [[NSMutableDictionary dictionaryWithContentsOfFile: filename] retain]; [pbxArchive autorelease]; pbxArchive = nil; pbxArchive = [[PBXArchive alloc] initWithDictionary: dict projectPath: projectPackage]; [listView reloadData]; [self printFilePathsInProject: [pbxArchive projectFolderPath]]; // Fills neededFiles array. [self copyFilesInProject: self]; return YES; } -(void) dealloc { [neededFiles release]; [dict release]; [pbxArchive release]; [projectPackage release]; [super dealloc]; } // Before using this, you must have called [self printFilePathsInProject: ...]; or it won't know what to copy. -(void) copyFilesInProject: (id)sender { [[[textView textStorage] mutableString] appendString: @"\n\n--------------------\n"]; // Now create folder(s) to copy files to, and make sure we're enough subfolders deep so relative paths can go back up: NSMutableString* destFolder = [[[[pbxArchive projectFolderPath] stringByAppendingPathComponent: [[pbxArchive projectFolderPath] lastPathComponent]] mutableCopy] autorelease]; int maxPathDepth = [neededFiles maxUpwardsDepth], x; NSArray* components = [[pbxArchive projectFolderPath] pathComponents]; [[NSFileManager defaultManager] createDirectoryAtPath: destFolder attributes: nil]; for( x = ([components count] -maxPathDepth); x < [components count]; x++ ) { if( [destFolder characterAtIndex: [destFolder length] -1] != '/' ) [destFolder appendString: @"/"]; [destFolder appendString: [components objectAtIndex: x]]; [[NSFileManager defaultManager] createDirectoryAtPath: destFolder attributes: nil]; } [[[textView textStorage] mutableString] appendString: @"\nDest Folder: "]; [[[textView textStorage] mutableString] appendString: destFolder]; // Now copy files: NSEnumerator *fenny = [neededFiles objectEnumerator]; NSString *currPath; while( (currPath = [fenny nextObject]) ) { NSString* currDest = [destFolder stringByCombiningWithPartialPath: currPath]; NSString* currSource = [[pbxArchive projectFolderPath] stringByCombiningWithPartialPath: currPath]; [self ensureFolderExists: [currDest stringByDeletingLastPathComponent]]; NSString* errPath = [self copyPath: currSource toPath: currDest]; if( errPath == nil ) { [[[textView textStorage] mutableString] appendString: @"\nCopied File: "]; [[[textView textStorage] mutableString] appendString: currDest]; } else { [[[textView textStorage] mutableString] appendString: @"\nCouldn't Copy: "]; [[[textView textStorage] mutableString] appendString: errPath]; } } // Now copy project package: NSString* newPackage = [destFolder stringByAppendingPathComponent: [projectPackage lastPathComponent]]; NSString* errPath = [self copyPath: projectPackage toPath: newPackage]; if( errPath == nil ) { [[[textView textStorage] mutableString] appendString: @"\nCopied File: "]; [[[textView textStorage] mutableString] appendString: newPackage]; } else { [[[textView textStorage] mutableString] appendString: @"\nCouldn't Copy: "]; [[[textView textStorage] mutableString] appendString: errPath]; } } NSString* UKNoNilString( NSString* s ) { if( !s ) return @""; else return s; } -(NSString*) copyPath: (NSString*)sourcePath toPath: (NSString*)destPath { BOOL isDir = NO; if( ![[NSFileManager defaultManager] fileExistsAtPath: sourcePath isDirectory: &isDir] ) return sourcePath; NSString* currName = [sourcePath lastPathComponent]; NSString* currSuffix = [currName pathExtension]; if( isDir && [currName isEqualToString: @".svn"] ) // Skip subversion meta-info directory. return nil; if( !isDir && [currName isEqualToString: @".DS_Store"] ) // Skip Finder's info store. return nil; if( !isDir && [currSuffix isEqualToString: @"mode2"] ) // Skip user-specific info in project files. return nil; if( !isDir && [currSuffix isEqualToString: @"pbxuser"] ) // Skip user-specific info in project files. return nil; if( isDir ) { if( ![[NSFileManager defaultManager] createDirectoryAtPath: destPath attributes: nil] ) return destPath; NSDirectoryEnumerator* enny = [[NSFileManager defaultManager] enumeratorAtPath: sourcePath]; NSString* currSubPath = nil; while(( currSubPath = [enny nextObject] )) { NSString* currSourcePath = [sourcePath stringByAppendingPathComponent: currSubPath]; NSString* currDestPath = [destPath stringByAppendingPathComponent: currSubPath]; NSString* errorPath = [self copyPath: currSourcePath toPath: currDestPath]; if( errorPath ) return errorPath; [enny skipDescendents]; } } else [[NSFileManager defaultManager] copyPath: sourcePath toPath: destPath handler: nil]; return nil; } -(void) ensureFolderExists: (NSString*)path { if( [[NSFileManager defaultManager] fileExistsAtPath: path] ) return; // All hunky-dory. // Otherwise, work our way up the path and ensure everything exists: NSMutableString* workpath = [NSMutableString string]; NSEnumerator* enny = [[path pathComponents] objectEnumerator]; NSString* currComponent; while( (currComponent = [enny nextObject]) ) { [workpath appendString: @"/"]; [workpath appendString: currComponent]; if( ![[NSFileManager defaultManager] fileExistsAtPath: workpath] ) [[NSFileManager defaultManager] createDirectoryAtPath: workpath attributes: nil]; } } -(void) recursivelyPrintFilesInGroup: (PBXGroup*)grp atPath:(NSString*)path { static NSMutableString* str = nil; // Not thread-safe! static int ignoreCurrentGroup = 0; // if 0, don't add to neededFiles, >0 do. int x = 0; if( !str ) str = [@"\n" mutableCopy]; for( x = 0; x < [grp count]; x++ ) { id obj = [grp objectAtIndex: x]; NSString* subPath = UKNoNilString([obj path]); if( [[obj refType] isEqualToString: @"Relative to Enclosing Group"] ) subPath = [path stringByCombiningWithPartialPath: subPath]; else if( [[obj refType] isEqualToString: @"Relative to Project"] ) subPath = [[pbxArchive projectFolderPath] stringByCombiningWithPartialPath: subPath]; else if( [[obj refType] isEqualToString: @"Relative to Build Product"] ) subPath = [[pbxArchive buildProductPath] stringByCombiningWithPartialPath: subPath]; if( [obj isKindOfClass: [PBXGroup class]] ) // PBXGroup or PBXAlternateGroup { [[[textView textStorage] mutableString] appendString: str]; [[[textView textStorage] mutableString] appendString: [obj name]]; [str appendString: @"\t"]; // Indent one level. if( [[obj name] isEqualToString: @"Frameworks"] // FIX ME! This and below shouldn't ignore *all* frameworks! || [[obj name] isEqualToString: @"Products"] ) ignoreCurrentGroup++; [self recursivelyPrintFilesInGroup: obj atPath: subPath]; if( [[obj name] isEqualToString: @"Frameworks"] || [[obj name] isEqualToString: @"Products"] ) ignoreCurrentGroup--; [str deleteCharactersInRange: NSMakeRange([str length] -1,1)]; // Un-indent. } else if( [obj isKindOfClass: [PBXFileReference class]] ) { [[[textView textStorage] mutableString] appendString: str]; [[[textView textStorage] mutableString] appendString: subPath]; if( ignoreCurrentGroup <= 0 ) [neededFiles addObject: [subPath stringBySubtractingBasePath: [pbxArchive projectFolderPath]]]; } } } // Just a little test to see whether we can extract the file list: -(void) printFilePathsInProject: (NSString*)path { PBXProject* project = [pbxArchive rootObject]; PBXGroup* currGroup = [project mainGroup]; [neededFiles release]; neededFiles = [[NSMutableArray alloc] init]; [self recursivelyPrintFilesInGroup: currGroup atPath: path]; } - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item { if( !item ) return pbxArchive; return [((NSArray*)item) objectAtIndex: index]; } - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { if( item == self || item == nil ) return YES; return( [item respondsToSelector: @selector(count)] && [((NSArray*)item) count] > 0 ); } - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { if( item == self || item == nil ) return 1; if( [item respondsToSelector: @selector(count)] ) return( [((NSArray*)item) count] ); else return 0; } - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { NSString* str = nil; if( [item respondsToSelector: @selector(description)] ) str = [item description]; if( !str ) str = @"-"; return str; } @end |