/* vim: set ft=objc et sw=4 ts=4 nowrap: */ /* * CDrecordController.m * * Copyright (c) 2002-2004 * * Author: Andreas Heppel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "CDrecordController.h" #include "CDrecordSettingsView.h" #include "CDrecordParametersView.h" #include "Constants.h" #include "Functions.h" #ifdef _ #undef _ #endif #define _(X) \ [[NSBundle bundleForClass: [self class]] localizedStringForKey:(X) value:@"" table:nil] static CDrecordController *singleInstance = nil; // // private methods // @interface CDrecordController (Private) - (void) checkForDrives; - (void) waitForEndOfBurning; - (NSMutableArray *) makeParamsForTask: (CDrecordTask)task withParameters: (NSDictionary *)sessionParams; - (void) appendTrackArgs: (NSMutableArray *)args forCDROM: (BOOL)isCDROM; - (void) sendOutputString: (NSString *)outString raw: (BOOL) raw; - (void) getCDrecordDrivers; @end @implementation CDrecordController - (id) init { self = [super init]; if (self) { statusLock = [NSLock new]; drivers = [NSMutableArray new]; [self checkForDrives]; [self getCDrecordDrivers]; } return self; } - (void) dealloc { singleInstance = nil; RELEASE(drives); RELEASE(drivers); RELEASE(statusLock); [super dealloc]; } // // BurnTool methods // - (NSString *) name { return @"cdrecord"; } - (id) preferences; { return [[CDrecordSettingsView singleInstance] autorelease]; } - (id) parameters; { return [[CDrecordParametersView singleInstance] autorelease]; } - (void) cleanUp { /* * Nothing to do here */ } + (id) singleInstance { if (! singleInstance) { singleInstance = [[CDrecordController alloc] init]; } return singleInstance; } // // Burner methods // - (NSArray *) availableDrives { return [drives allKeys]; } - (NSDictionary *) mediaInformation: (NSDictionary *) parameters { int i, count; NSString *cdrecord; NSMutableArray *cdrArgs; NSPipe *stdOut; NSArray *cdrOutput; NSString *outLine; NSMutableDictionary *info = nil; // cdrecord does not deliver useful information about // the media on my drive, thus I don't know the output // preset the dictionary with some defaults info = [NSMutableDictionary dictionaryWithObjectsAndKeys: _(@"Unknown"), @"type", @"n/a", @"vendor", @"n/a", @"speed", @"n/a", @"capacity", @"n/a", @"empty", @"n/a", @"remCapacity", @"n/a", @"sessions", @"n/a", @"appendable", nil]; cdrecord = [[parameters objectForKey: @"CDrecordParameters"] objectForKey: @"Program"]; if (!checkProgram(cdrecord)) return nil; NS_DURING cdrArgs = [self makeParamsForTask: TaskMediaInfo withParameters: parameters]; NS_HANDLER [self sendOutputString: [NSString stringWithFormat: @"Error: %@ -> %@", [localException name], [localException reason]] raw: NO]; NS_VALUERETURN(info, NSDictionary*); NS_ENDHANDLER // set up cdrecord task cdrTask = [[NSTask alloc] init]; stdOut = [[NSPipe alloc] init]; [cdrTask setLaunchPath: cdrecord]; [cdrTask setArguments: cdrArgs]; [cdrTask setStandardOutput: stdOut]; [cdrTask setStandardError: stdOut]; [self sendOutputString: [NSString stringWithFormat: _(@"Launching %@ %@"), cdrecord, [cdrArgs componentsJoinedByString: @" "]] raw: NO]; [cdrTask launch]; [cdrTask waitUntilExit]; /* * If cdrecord did not terminate gracefully we stop the whole affair. * We delete in any case the actual (not finished) file. */ { int termStatus = [cdrTask terminationStatus]; // FreeBSD needs an lvalue for the WIF* macros if ((WIFEXITED(termStatus) == 0) || WIFSIGNALED(termStatus)) { [info setObject: _(@"Unknown") forKey: @"type"]; return info; } } cdrOutput = [[[NSString alloc] initWithData: [[stdOut fileHandleForReading] availableData] encoding: NSISOLatin1StringEncoding] componentsSeparatedByString: @"\n"]; count = [cdrOutput count]; /* * Skip the first line in output. It contains only header data. */ for (i = 1; i < count; i++) { NSRange range; outLine = [cdrOutput objectAtIndex: i]; // check for not erasable first, then for erasable // if none applies don#t change the dictionary range = [outLine rangeOfString: @"Is not erasable"]; if (range.location != NSNotFound) { [info setObject: @"CD-R" forKey: @"type"]; continue; } else { range = [outLine rangeOfString: @"Is erasable"]; if (range.location != NSNotFound) { [info setObject: @"CD-RW" forKey: @"type"]; continue; } } // search manufacturer/vendor range = [outLine rangeOfString: @"Manufacturer"]; if (range.location != NSNotFound) { if ([outLine length] > range.location+13) [info setObject: [outLine substringFromIndex: 14] forKey: @"vendor"]; continue; } range = [outLine rangeOfString: @" speed low"]; if (range.location != NSNotFound) { NSRange range1 = [outLine rangeOfString: @"speed high"]; if ([outLine length] > range1.location+11) [info setObject: [NSString stringWithFormat: @"%dX - %dX", [[outLine substringFromIndex: 13] intValue], [[outLine substringFromIndex: range1.location+12] intValue]] forKey: @"speed"]; continue; } range = [outLine rangeOfString: @"ATIP start of lead out"]; if (range.location != NSNotFound) { if ([outLine length] > 41) { int frames = [[outLine substringFromIndex: 26] intValue]; NSString *str = [NSString stringWithFormat: @"%@ - %d blocks, %d/%d MB", [outLine substringWithRange: NSMakeRange(34,8)], frames, frames * 2048 / (1024*1024), frames * 2352 / (1024*1024)]; [info setObject: str forKey: @"capacity"]; } else [info setObject: [outLine substringFromIndex: 26] forKey: @"capacity"]; continue; } } RELEASE(stdOut); RELEASE(cdrTask); return info; } - (BOOL) blankCDRW: (EBlankingMode) mode withParameters: (NSDictionary *) parameters { NSString *cdrecord; NSMutableArray *cdrArgs; NSPipe *stdOut; cdrecord = [[parameters objectForKey: @"CDrecordParameters"] objectForKey: @"Program"]; if (!checkProgram(cdrecord)) return NO; /* The array is autoreleased! Don't release it here!!! */ NS_DURING cdrArgs = [self makeParamsForTask: TaskBlank withParameters: parameters]; NS_HANDLER [self sendOutputString: [NSString stringWithFormat: @"Error: %@ -> %@", [localException name], [localException reason]] raw: NO]; NS_VALUERETURN(NO, BOOL); NS_ENDHANDLER [cdrArgs addObject: [NSString stringWithFormat: @"blank=%@",mode==fullBlank?@"all":@"fast"]]; // set up cdrecord task cdrTask = [[NSTask alloc] init]; stdOut = [[NSPipe alloc] init]; [cdrTask setLaunchPath: cdrecord]; [cdrTask setArguments: cdrArgs]; [cdrTask setStandardOutput: stdOut]; [cdrTask setStandardError: stdOut]; [self sendOutputString: [NSString stringWithFormat: _(@"Launching %@ %@"), cdrecord, [cdrArgs componentsJoinedByString: @" "]] raw: NO]; [cdrTask launch]; while ([cdrTask isRunning]) { NSData *inData; while ((inData = [[[cdrTask standardError] fileHandleForReading] availableData]) && [inData length]) { int i, count; NSString *aLine; NSArray *theOutput; theOutput = [[[NSString alloc] initWithData: inData encoding: NSISOLatin1StringEncoding] componentsSeparatedByString: @"\n"]; count = [theOutput count]; for (i = 0; i < count; i++) { aLine = [theOutput objectAtIndex: i]; if (aLine && [aLine length]) [self sendOutputString: aLine raw: YES]; } // for (i = 0; i < count; i++) } // while ((inData = } // while ([cdrTask isRunning]) /* * If cdrecord did not terminate gracefully we stop the whole affair. * We delete in any case the actual (not finished) file. */ { int termStatus = [cdrTask terminationStatus]; // FreeBSD needs an lvalue for the WIF* macros if ((WIFEXITED(termStatus) == 0) || WIFSIGNALED(termStatus)) { return NO; } } RELEASE(stdOut); RELEASE(cdrTask); return YES; } - (BOOL) burnCDFromImage: (id) image andAudioTracks: (NSArray *) trackArray withParameters: (NSDictionary *) parameters { BOOL ret = YES; NSString *cdrecord; NSMutableArray *cdrArgs; NSPipe *stdOut; if (!image && (!trackArray || ![trackArray count])) { [self sendOutputString: _(@"No tracks to burn on CD.") raw: NO]; return NO; } burnStatus.processStatus = isStopped; cdrecord = [[parameters objectForKey: @"CDrecordParameters"] objectForKey: @"Program"]; if (!checkProgram(cdrecord)) return NO; // set image file as first entry, if there is one burnTracks = [[NSMutableArray alloc] init]; if (image) [burnTracks addObject: image]; [burnTracks addObjectsFromArray: trackArray]; /* The array is autoreleased! Don't release it here!!! */ NS_DURING cdrArgs = [self makeParamsForTask: TaskBurn withParameters: parameters]; NS_HANDLER [self sendOutputString: [NSString stringWithFormat: @"Error: %@ -> %@", [localException name], [localException reason]] raw: NO]; NS_VALRETURN(NO); NS_ENDHANDLER [self appendTrackArgs: cdrArgs forCDROM: image ? YES : NO]; burnStatus.trackNumber = 0; burnStatus.trackProgress = 0; burnStatus.entireProgress = 0; burnStatus.bufferLevel = 0; // set up cdrecord task cdrTask = [[NSTask alloc] init]; stdOut = [[NSPipe alloc] init]; [cdrTask setLaunchPath: cdrecord]; [cdrTask setArguments: cdrArgs]; [cdrTask setStandardOutput: stdOut]; [cdrTask setStandardError: stdOut]; [self sendOutputString: [NSString stringWithFormat: _(@"Launching %@ %@"), cdrecord, [cdrArgs componentsJoinedByString: @" "]] raw: NO]; [cdrTask launch]; burnStatus.processStatus = isWaiting; [self waitForEndOfBurning]; /* * If cdrecord did not terminate gracefully we stop the whole affair. * We delete in any case the actual (not finished) file. */ { int termStatus = [cdrTask terminationStatus]; // FreeBSD needs an lvalue for the WIF* macros if ((WIFEXITED(termStatus) == 0) || WIFSIGNALED(termStatus) || (burnStatus.processStatus == isCancelled)) { burnStatus.processStatus = isCancelled; ret = NO; } } RELEASE(stdOut); RELEASE(cdrTask); RELEASE(burnTracks); if (burnStatus.processStatus != isCancelled) { [statusLock lock]; burnStatus.processStatus = isStopped; [statusLock unlock]; } return ret; } - (void) ejectCD: (NSDictionary *) parameters { NSString *cdrecord; NSTask *task; NSArray *args; /* The array is autoreleased! Don't release it here!!! */ NS_DURING args = [self makeParamsForTask: TaskEject withParameters: parameters]; NS_HANDLER [self sendOutputString: [NSString stringWithFormat: @"Error: %@ -> %@", [localException name], [localException reason]] raw: NO]; NS_VOIDRETURN; NS_ENDHANDLER cdrecord = [[parameters objectForKey: @"CDrecordParameters"] objectForKey: @"Program"]; if (!checkProgram(cdrecord)) return; task = [[NSTask alloc] init]; [task setLaunchPath: cdrecord]; [task setArguments: args]; [task launch]; [task waitUntilExit]; RELEASE(task); } - (NSArray *) fileTypes { return [NSArray arrayWithObjects: @"wav", @"au", @"cdr", nil]; } - (NSArray *) drivers { return drivers; } - (SBurnerStatus) getStatus { SBurnerStatus status; [statusLock lock]; status = burnStatus; [statusLock unlock]; return status; } - (BOOL) stop: (BOOL)immediately { if (cdrTask && (burnStatus.processStatus == isBurning)) { [cdrTask terminate]; [self sendOutputString: _(@"Terminating process.") raw: NO]; burnStatus.processStatus = isCancelled; } else if (cdrTask && (burnStatus.processStatus == isWaiting)) { [cdrTask terminate]; [self sendOutputString: _(@"Terminating process.") raw: NO]; burnStatus.processStatus = isCancelled; } return YES; } @end