Cocoa Programming for Mac OS X - Aaron Hillegass [136]
64 bytes from 69.39.89.150: icmp_seq=4 ttl=50 time=35.685 ms
64 bytes from 69.39.89.150: icmp_seq=5 ttl=50 time=35.667 ms
64 bytes from 69.39.89.150: icmp_seq=6 ttl=50 time=36.435 ms
64 bytes from 69.39.89.150: icmp_seq=7 ttl=50 time=52.296 ms
64 bytes from 69.39.89.150: icmp_seq=8 ttl=50 time=36.142 ms
64 bytes from 69.39.89.150: icmp_seq=9 ttl=50 time=36.188 ms
--- www.bignerdranch.com ping statistics ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max/stddev = 34.546/37.313/52.296/5.021 ms
If you want to end the program prematurely, press Control-C to send it a sigint signal. This will cause it to write out the stats and terminate.
iPing
Now you are going to write a Cocoa app that uses NSTask to run ping (Figure 36.6).
Figure 36.6. Completed Application
In Xcode, create a new project, iPing, of type Cocoa Application. Set the Class Prefix to iPing. Uncheck Create Document-Based Application. In iPingAppDelegate, add two outlets, pointers to the NSTask and the NSPipe, and an action:
@interface iPingAppDelegate : NSObject IBOutlet NSTextView *outputView; IBOutlet NSTextField *hostField; IBOutlet NSButton *startButton; NSTask *task; NSPipe *pipe; } @property (assign) IBOutlet NSWindow *window; - (IBAction)startStopPing:(id)sender; @end Open MainMenu.xib and drop a text view, a text field, and a button onto the window. The button should be put in Toggle mode. The title should be Start Ping (Figure 36.7), and the alternate title should be Stop Ping. Set the state to Off. Figure 36.7. Button Attributes Make the iPingAppDelegate the target of the button; its action should be startStopPing:. Set the outputView, hostField, and startButton outlets to point to the text view, the text field, and the button, respectively (Figure 36.8). Figure 36.8. Object Diagram In iPingAppDelegate.m, implement startStopPing: - (IBAction)startStopPing:(id)sender { // Is the task running? if (task) { [task interrupt]; } else { task = [[NSTask alloc] init]; [task setLaunchPath:@"/sbin/ping"]; NSArray *args = [NSArray arrayWithObjects:@"-c10", [hostField stringValue], nil]; [task setArguments:args]; // Create a new pipe pipe = [[NSPipe alloc] init]; [task setStandardOutput:pipe]; NSFileHandle *fh = [pipe fileHandleForReading]; NSNotificationCenter *nc; nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self]; [nc addObserver:self selector:@selector(dataReady:) name:NSFileHandleReadCompletionNotification object:fh]; [nc addObserver:self selector:@selector(taskTerminated:) name:NSTaskDidTerminateNotification object:task]; [task launch]; [outputView setString:@""]; [fh readInBackgroundAndNotify]; } } While the task is running, the file handle will be posting notifications when data is ready. Implement the method that will get called: - (void)appendData:(NSData *)d { NSString *s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding]; NSTextStorage *ts = [outputView textStorage]; [ts replaceCharactersInRange:NSMakeRange([ts length], 0) withString:s]; } - (void)dataReady:(NSNotification *)n { NSData *d; d = [[n userInfo] valueForKey:NSFileHandleNotificationDataItem]; NSLog(@"dataReady:%ld bytes", [d length]); if ([d length]) { [self appendData:d]; } // If the task is running, start reading again if (task) [[pipe fileHandleForReading] readInBackgroundAndNotify]; } When the process is done, we should do some cleanup: - (void)taskTerminated:(NSNotification *)note { NSLog(@"taskTerminated:"); task = nil; [startButton setState:0]; } Build and run the application. Challenge: .tar and .tgz files # /usr/bin/tar tf MyFiles.tar If the tar file is also compressed, just add a z to the flags: # /usr/bin/tar tzf MyFiles.tgz Extend ZIPspector to deal
A listing of files in a zip file is given by zipinfo. You can get a similar listing for tar files by using the command-line tool tar: