This is TARInspector.m in view mode; [Download] [Up]
/***************************************************************************
TARInspector 1.0 (a .tar file content inspector module for the NeXTSTEP Workspace Manager)
Copyright (C) 1993 Glenn Brown
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 1, or (at your option) any later version.
This program is distributed WITHOUT ANY WARRANTY; without even the implied warrenty 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.
***************************************************************************/
/*Note: Don't be intimidated by the size of this file. It is so big because is has copious amounts of comments
You should read GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf before trying to understand this code. It's really simple, though: The workspace manager simply calls the ok: method whenever the user presses the ok ('Unarchive') button and calls revert: whenever the revert ('List Contents') button is pressed -OR- the workspace manager wants the inspector to update its display. The new method is called the first time TARInspector is used during a login session.*/
#import <mach/cthreads.h>
#import <stdio.h>
#import <mach/cthreads.h>
#import "TARInspector.h"
@implementation TARInspector
static id tarInspector = nil;
// I hate globals, but its in the
// GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf example and
// I say: if it ain't broke...
/***********
* new: This routine is based on the new routine found in
* the online developer documentation modified as noted. See
* GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
************/
+ new
{ if (tarInspector == nil) {
char path[MAXPATHLEN+1];
NXBundle *bundle = [NXBundle bundleForClass:self];
self = tarInspector = [super new];
if ([bundle getPath:path
forResource:"TARInspector"
ofType:"nib"]) {
[NXApp loadNibFile:path owner:tarInspector];
//vvvvvvvvvvvvvvvvvvvvvvvvGlenn's modifications
instructions = NXOpenMemory(NULL,0,NX_READWRITE);
[textOutput writeRichText:instructions];
// the above two lines open a stream in memory and write the
// rtf format TARInspector instructions just loaded from the
// .nib file into it so the can be reloaded into the
// scroller later.
//^^^^^^^^^^^^^^^^^^^^^^^^
} else {
fprintf (stderr, "Couldn't load TARInspector.nib\n");
tarInspector = nil;
}
}
return tarInspector;
}
/************
* asynchronouslyPerformFileOperation: This is a function rather than
* a method so that it can be executed asynchronously. See where it is
* called for details. Also see
* GeneralReference/02_ApplicationKit/Protocols/NXWorkspaceRequestProtocol.rtf
*************/
int asynchronouslyPerformFileOperation(char *sels){
int err;
err = [[Application workspace]
performFileOperation: WSM_DECOMPRESS_OPERATION
source: ""
destination:""
files: sels
options:""];
// Decompresses the selections using the workspace's decompress
// feature. The workspace does nice things during decompression like
// putting a message in the file viewer window and showing data about
// the decompression operation in the processes window.
// Unfortunately, the 'Unarchive' feature of the workspace manager is
// either not documented or does not exist as a 'File Operation'.
// Probably, the inspector that comes with 3.0 does not use the
// performFileOperation:...: method to do unarchiving but, rather, does
// everything (messages, e.g.) directly. I could not do this, however,
// since the internals of the Workspace manager are not documented.
free(sels);
return err;
}
/*************
* updateButtons: updates the buttons as recorded in the objects
* instance variables okEnabled and revertEnabled.
**************/
- updateButtons{
[[[self okButton] setEnabled:okEnabled] setTitle:"Unarchive"];
[[[self revertButton] setEnabled:revertEnabled] setTitle:"ListContents"];
return self;
}
/************
* ok: This method is called by the workspace when the OK ("Unarchive") button
* is pressed.
* See GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
*************/
- ok:sender{
unsigned numSels;
char *sels;
okEnabled = NO;
[self updateButtons];
// deactivate the "Unarchive" button so the user will not unarchive
// the file twice.
numSels = [self selectionCount];
// find out how many files are selected and ...
sels = calloc(numSels*(MAXPATHLEN+1),sizeof(char));
// allocate enough memory to hold the numSels selections plus the
// seperators between them plus the null termination.
if(sels){
// if memory allocation was successful
[self selectionPathsInto:sels separator:'\t'];
// read the names of the selected files into the allocated space
// using the seperator that the WorkspaceRequestProtocol asks for.
cthread_detach(cthread_fork(
(cthread_fn_t)asynchronouslyPerformFileOperation,
(any_t)sels));
// Tells the workspace manager to decompress the file.
// This call requires a little explaining!
// Because this inspector module executes as part of the
// workspace manager's main thread of execution, and because the
// performFileOperation:..: method blocks until workspace manager
// responds to the workspace request, the workspace manager will
// hang unless a seperate thread makes the workspaceManagerRequest.
// At least that's the only explaination I could come up with
// for why the workspace manager would hand during the request
// before I put the request in another thread!
}else{
// Insert error messager here if you want to. It will be called
// if memory for the selection could not be allocated.
return nil;
}
[super ok:sender];
return self;
// Above 2 lines as spec'd in
// GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
}
/************
* listContentsOfTarFile: This method just calls
* 'tar -tf /selection/' and pipes the result into the Text object in
* the ScrollView. Assumes there is only one selection.
*************/
- listContentsOfTarFile:sender{
char command[13+MAXPATHLEN+1];
// space for the command line to send to 'sh'
// 13 for "/bin/tar -tf ", MAXPATHLEN for file path, 1 for null at end
FILE *result; //file (stream) holding text result of 'tar -tf command'
NXStream *nxf; //will point to same stream as result.
strcpy(command,"/bin/tar -tf ");
// copy the command to the command line
[self selectionPathsInto:&command[13] separator:'\0'];
// puts the pathname of the selected file at the end of the command line.
[[textOutput setMonoFont:YES] setText:""];
// erase text in inspector and set mode to unformatted text
[[textOutput selectAll:self] alignSelLeft:self];
[textOutput setSelProp:NX_INDENT to:30];
[[textOutput setSelFontSize:10.0] setSelFontStyle:0];
// 3 lines above set text display attributes.
[[textOutput selectNull] sizeToFit]; // update display
result = popen(command,"r");
//execute the command and store results in a stream called result.
if(result != NULL){ //if tar -tf success then
nxf=NXOpenFile(fileno(result),NX_READONLY);
// converts (FILE *)result to a (NXStream *)
if(nxf!=NULL){ //if nothing weird happened
[textOutput readText:nxf]; //put result in text field
// [textOutput sizeToFit]; //update display
NXClose(nxf); //close the stream
}else{
//If there is an error here, I want to know about it!
//I've never seen this message, however.
[textOutput setText:"Error (1) listing contents."];
}
pclose(result);
// closes the file (stream) that the result were read into.
// I'm not sure that I need to close it, since NXClose was called
// above, but in the case that nxf == null, NXClose will not be
// called and the file should be closed. Also, the pclose man
// page sez that pclose can tell is its arg is not something that
// needs to be closed with pclose.
}else [textOutput setText:"Error (2) listing contents."];
//I would be amazed if this error were reported.
[textOutput sizeToFit]; // make all text visible.
[super revert:sender];
// as spec'd in GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
return self;
}
/************
** isNewSelection
** Tests if a new selection has been made. If so, it records the new
** selection as LastSelection along with lastSelection count.
** This method is only necessary because NeXT doesn't tell us this info
** directly... [Lame!]
**************/
- (int) isNewSelection{
char *currentSelection;
int currentSelectionCount;
currentSelectionCount = [self selectionCount]; //read current # selected
currentSelection = calloc( //alloc space for the
currentSelectionCount*(MAXPATHLEN+1), //tab delimited selection
sizeof(char)); //list
if(currentSelection){ //if alloc succeeded
[self selectionPathsInto:currentSelection separator: '\t'];
//read path into alloc'ed space
if(lastSelection==NULL){
//if this is first time to read a selection, then the current
//selection, whatever it is, is new.
lastSelection=currentSelection; //record the current selection
lastSelectionCount=currentSelectionCount;
return YES; //and report that it was new
}else if(strcmp(currentSelection,lastSelection)){
//if current and prev sels are different...
//(note: strcmp will _not_ be called if cS or lS is null
lastSelectionCount = currentSelectionCount; //then update count
free(lastSelection); //free the old sel info
lastSelection = currentSelection; //update selection
return YES; //report that sel is new
}else{ //if current selection is not new
free(currentSelection); //free it.
}
}/*else{Here would be a good place for an error message}*/
return NO; //returned if current selection could not be alloc'd
//or current selection is not new
}
/*************
** updateButtonsForNewSelection: If there is a new selection, enable
** the appropriate buttons
**************/
- updateButtonsForNewSelection{
okEnabled = YES; //enable 'Unarchive' button
revertEnabled = ([self selectionCount]==1);
// enable 'List Conents' button iff one file is selected
[self updateButtons]; //update the buttons.
return self;
}
/*************
** displayInstructions: puts the instructions (which were read earlier
** from the Text object just after they were loaded from the .nib file)
** back in the text display
**************/
- displayInstructions{
[textOutput setText:""]; //force Text object to scroll to top
NXSeek(instructions, 0L, NX_FROMSTART);
// rewind the stream holding the instructions to the start.
[[textOutput setMonoFont:NO] readRichText:instructions];
// enable RTF mode and load the RTF instructions into the display
return self;
}
/*************
** revert: This is the heart of the inspector. This method is called if
** the workspace manager wants the inspector to be updated or the revert
** button is pressed.
** See GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf.
**************/
- revert:sender{
if([self revertButton] != sender){
// This code is executed iff the revert button was _not_ the cause.
// of the revert message being sent... Meaning the workspace
// manager wants a display update.
if([self isNewSelection]){
// if a new selection has been made
[self updateButtonsForNewSelection];
// update buttons as appropriate.
[self displayInstructions];
// erase old content listing by replacing with instructions
} else [self updateButtons];
}else if([self selectionCount]==1){
// This branch is taken only if the user actually pressed
// the button and only one file is selected.
revertEnabled = NO;
[self updateButtons];
// above two lines disable the 'List Contents' button so the
// user cannot waste cycles listing the contents more than once!
[self listContentsOfTarFile:sender]; //listContentsOf the selected TarFile
}
[super revert:sender];
// as specified in GeneralRef/19_WorkspaceManager/Classes/WMInspector.rtf
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.