This is uncompresshelp.m in view mode; [Download] [Up]
// uncompresshelp.m // cc uncompresshelp.m -Wall -g -O -s -o uncompresshelp -lIndexing_s // cc -arch m68k -arch i386 uncompresshelp.m -Wall -g -O -s -o uncompresshelp -lIndexing_s // cc -arch m68k -arch i386 -arch hppa uncompresshelp.m -Wall -g -O -s -o uncompresshelp -lIndexing_s // // Copyright 1993, 1994, Scott Hess. This source code may be // redistributed and modified without restriction. Well, one // restriction - do not claim that you wrote it. If the user // receives non-trivial advantage from the use of this program, a // payment of $50-$100 would be appreciated. // // [I guess that would make this semi-shareware. I'm not much // interested in turning a profit (Ha!) on this program. Rather, // I am interested in getting some small return on time and energy // invested so that I can work on other, similar programs. You // know, little programs that aren't worth doing for their own // sake, but when you need them, you NEED them. I love poking // around in the obscure corners of NeXTSTEP, but too often I // can't spend much time doing it because it's hard to justify // the hours. It's like a hobby that brings in a small income. // You wouldn't want to live on it, because in reality the income // barely covers equipment and supplies, but it's useful as // justification when someone says "Why do you keep wasting your // time with that?" I also want to know if people find stuff like // this _useful_, or just interesting ...] // // Scott Hess // 12901 Upton Avenue South, #326 // Burnsville, MN 55337 // // // To muck about with the Help.store files: // o Open an IXStoreFile instance on the file. // o Within the store, open an IXBTree on block one, which contains // entries mapping the name of the helpfile element to the // files which are contained within it. // o Within the store, open an IXBTree on block two, which contains // entries mapping integer indices to file contents. // o The directory btree's values are of the form // "filename key;[filename key;...]". The filenames are files // that are contained within the helpfile element, while the // keys are integers which index into the file btree's to find // the contents of the file. // o Individual file content keys may appear more than once in // the directory. This means that the specified files are the // same [implemented as a call to link() in extractFile]. // o File's which end in .rtf are compressed using /usr/ucb/compress. // So is .index.store, apparently. // // // Version history: // // Wednesday, January 12, 1994: // Fix inverted cur test in extractFile(). // // Rewrite uncompress call in terms of raw fork/execl/wait4. // Previously it used system(), which did not work correctly // when the filename contained spaces. It also wouldn't have // worked if the filename contained shell metacharacters (which // was the reasoning behind not just putting quotes or // double-quotes in the system call). // // Added more descriptive and debugging output. // // Added -h option to display help info which is taken from this file // header. -AFI // // Reordered statements so that the absence of a help.store file will // terminate utility before "Extracting helpfile.store into outputdir" // message is printed. -AFI // // Monday, January 10, 1994: // First public release. // static const char *usage = "Usage: uncompresshelp [-h | [helpfile.store [outputdir]]]"; static const char *help = "uncompresshelp extracts help files from a help storefile and\n" "stores them back onto the filesystem. helpfile.store is a help\n" "storefile as generated by compresshelp. outputdir is the\n" "directory to store the files in. helpfile.store defaults to\n" "Help.store. outputdir defaults to the input file with .store\n" "stripped from the end."; #import <libc.h> #import <ansi.h> #import <indexing/indexing.h> #import <objc/HashTable.h> static HashTable *extractedFiles=nil; int uncompress( const char *filename) { int pid; if( !(pid=fork())) { execl( "/usr/ucb/uncompress", "/usr/ucb/uncompress", filename, NULL); exit( 1); } else if( pid==-1) { perror( "uncompresshelp: uncompress error"); return -1; } else { union wait status; int ret=wait4( pid, &status, 0, NULL); if( ret==-1) { perror( "uncompresshelp: waiting for uncompress"); return -1; } else if( WIFSIGNALED( status)) { fprintf( stderr, "uncompresshelp: compress terminated on signal %d.\n", status.w_termsig); return -1; } else if( status.w_retcode) { fprintf( stderr, "uncompresshelp: compress exited with %d.\n", status.w_retcode); return -1; } } return 0; } void extractFile( IXBTreeCursor *fileCursor, unsigned fileId, const char *target) { // If we've already extracted that index, just link the // target to it. const char *cur=[extractedFiles valueForKey:(void *)fileId]; if( cur) { fprintf( stderr, "\tLinking to %s\n", cur); if( link( cur, target)==-1) { perror( "uncompresshelp: linking to extracted file"); } // Otherwise, we have to reach into the btree using fileCursor // and find it. } else { void *file=NULL; unsigned fileLen; // The index is stored in big-endian form. unsigned indexFileId=NXSwapHostLongToBig( fileId); // Make certain we have a table to map extracted files. if( !extractedFiles) { extractedFiles=[[HashTable alloc] initKeyDesc:"i" valueDesc:"%"]; } // Store the mapping for this index. [extractedFiles insertKey:(void *)fileId value:(void *)NXUniqueString( target)]; // Try to find the index in the table. if( [fileCursor setKey:(void *)(&indexFileId) andLength:sizeof( indexFileId)]) { int fd; const char *slash, *dot; // Read the contents of the file. fileLen=[fileCursor readValue:&file]; // Open the file and stuff the contents into it. fd=open( target, O_WRONLY|O_CREAT, 0644); if( fd!=-1) { if( write( fd, file, fileLen)<fileLen) { perror( "uncompresshelp: Writing file"); } if( close( fd)==-1) { perror( "uncompresshelp: Closing file"); } } else { perror( "uncompresshelp: Opening file"); } // Free the contents. free( file); // If the file is .rtf, uncompress it. slash=rindex( target, '/'); slash=(slash ? slash+1 : target); dot=rindex( slash, '.'); if( dot) { if( !strcmp( dot, ".rtf") || !strcmp( slash, ".index.store")) { char targetZ[ MAXPATHLEN+1]; sprintf( targetZ, "%s.Z", target); fprintf( stderr, "\tRenaming %s\n\t to %s\n", target, targetZ); rename( target, targetZ); fprintf( stderr, "\tUncompressing %s\n", targetZ); uncompress( targetZ); } } } } } // Create the directory, creating parent directories as needed. int mkdirs( char *dir, int mode) { if( access( dir, F_OK)) { int ret=0; char *slash=rindex( dir, '/'); if( slash) { *slash='\0'; ret=mkdirs( dir, mode); *slash='/'; } if( !ret) { ret=mkdir( dir, mode); } return ret; } return 0; } // *files is a file list in the format "filename key;[ filename // key;...]". Parse out the first filename/key pair into // outputp/*indexp, and put the end position back into *files. // Return YES if a filename was found. This routine is more // complex than needed because I was concerned that my only // knowledge of what's possible for filenames is empirical - // I know what's in some sample files, but I don't know whether // there are other possibilities (consider spaces in filenames, // for instance). Better safe than sorry. BOOL getNextFile( const char **files, char *outputp, unsigned *indexp) { const char *s, *p; for( s=*files; *s; s++) { // Get to a space followed by a digit. if( *s==' ' && isdigit( s[ 1])) { // Start accumulating the index. unsigned i=s[ 1]-'0'; // Store away the end of the name. p=s; // Accumulate the rest of the index. for( s+=2; isdigit( *s); s++) { i*=10; i+=s[ 0]-'0'; } // It _must_ end with ; to be valid. if( *s==';') { // Store away the file name. strncpy( outputp, *files, p-(*files)); outputp[ p-(*files)]='\0'; // Store away the index found. *indexp=i; // Return the new search position, skipping // the extra space after interior elements of // the list. *files=(s[ 1] ? s+2 : s+1); return YES; } } } return NO; } void main( int argc, char **argv) { const char *inputFile="Help.store"; char outputDir[ MAXPATHLEN+1]="Help"; unsigned outputDirLen; IXStoreFile *helpStore; IXBTree *dirTree, *fileTree; IXBTreeCursor *dirCursor, *fileCursor; // Decode command-line parameters. if( argc>1) { inputFile=argv[ 1]; if( argc>2) { strcpy( outputDir, argv[ 2]); } else { const char *dot; int c; while( (c=getopt( argc, argv, "h"))!=EOF) { switch (c) { case 'h' : fprintf( stderr, "%s\n\n%s\n", help, usage); exit( 0); case '?' : default : fprintf( stderr, "%s\n", usage); exit( 1); } } if( (dot=rindex( inputFile, '.')) && !strcmp( dot, ".store")) { strncpy( outputDir, inputFile, dot-inputFile); outputDir[ dot-inputFile]='\0'; } else { fprintf( stderr, "uncompresshelp:\tUnable to generate default outputDir:\n\t\t%s doesn't end in .store.\n", inputFile); exit( 1); } } } // Open up the storefile. helpStore=[[IXStoreFile alloc] initFromFile:inputFile forWriting:NO]; if( !helpStore) { fprintf( stderr, "Couldn't open %s\n", inputFile); fprintf( stderr, "%s\n\n%s\n", help, usage); exit( 1); } outputDirLen=strlen( outputDir); outputDir[ outputDirLen++]='/'; fprintf( stderr, "Extracting %s into %s\n", inputFile, outputDir); // Open up a tree to list our directories and files in // them. dirTree=[[IXBTree alloc] initFromBlock:1 inStore:helpStore]; dirCursor=[[IXBTreeCursor alloc] initWithBTree:dirTree]; // Open up a tree to map indices to files. fileTree=[[IXBTree alloc] initFromBlock:2 inStore:helpStore]; fileCursor=[[IXBTreeCursor alloc] initWithBTree:fileTree]; // Start from the first entry in the directory list. if( [dirCursor setFirst]) { do { char *dir=NULL, *files=NULL; unsigned dirLen, filesLen; // Get the directory entry. if( [dirCursor getKey:(void **)&dir andLength:&dirLen]) { unsigned i; const char *s; // Append the entry to the target directory. strncpy( outputDir+outputDirLen, dir, dirLen); outputDir[ outputDirLen+dirLen-1]='\0'; // Read the list of files for that entry. filesLen=[dirCursor readValue:(void **)&files]; // Look for the first file in the list. s=files; if( getNextFile( &s, outputDir+outputDirLen+dirLen, &i)) { // If that's the end, check to see if the // directory entry's basename matches the // only entry in the directory. if( !*s) { char *b=rindex( outputDir, '/'); if( b && !strcmp( b+1, outputDir+outputDirLen+dirLen)) { // If they match, then extract directly // into the name specified by the // directory entry. fprintf( stderr, "Extracting simple file %s\n", outputDir); *b='\0'; mkdirs( outputDir, 0755); *b='/'; extractFile( fileCursor, i, outputDir); // And go to the next entry. continue; } } // Make certain the directory(s) for the // entry exist. mkdirs( outputDir, 0755); fprintf( stderr, "Extracting compound file %s\n", outputDir); // Put a slash between the parent dir and // the filenames within it. outputDir[ outputDirLen+dirLen-1]='/'; // Extract each of the contained files. do { fprintf( stderr, " Extracting %s\n", outputDir); extractFile( fileCursor, i, outputDir); } while( getNextFile( &s, outputDir+outputDirLen+dirLen, &i)); } free( files); } } while( [dirCursor setNext]); } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.