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.