home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Utilities / uncompresshelp / uncompresshelp.m < prev    next >
Encoding:
Text File  |  1994-02-06  |  10.9 KB  |  336 lines

  1. // uncompresshelp.m
  2. // cc uncompresshelp.m -g -O -s -o uncompresshelp -lIndexing_s
  3. //
  4. // Copyright 1993, 1994, Scott Hess.  This source code may be
  5. // redistributed and modified without restriction.  Well, one
  6. // restriction - do not claim that you wrote it.  If the user
  7. // receives non-trivial advantage from the use of this program, a
  8. // payment of $50-$100 would be appreciated.
  9. //
  10. // [I guess that would make this semi-shareware.  I'm not much
  11. //  interested in turning a profit (Ha!) on this program.  Rather,
  12. //  I am interested in getting some small return on time and energy
  13. //  invested so that I can work on other, similar programs.  You
  14. //  know, little programs that aren't worth doing for their own
  15. //  sake, but when you need them, you NEED them.  I love poking
  16. //  around in the obscure corners of NeXTSTEP, but too often I
  17. //  can't spend much time doing it because it's hard to justify
  18. //  the hours.  It's like a hobby that brings in a small income.
  19. //  You wouldn't want to live on it, because in reality the income
  20. //  barely covers equipment and supplies, but it's useful as
  21. //  justification when someone says "Why do you keep wasting your
  22. //  time with that?"  I also want to know if people find stuff like
  23. //  this _useful_, or just interesting ...]
  24. //
  25. // Scott Hess
  26. // 12901 Upton Avenue South, #326
  27. // Burnsville, MN  55337
  28. //
  29. //
  30. // uncompresshelp [helpfile.store [outputdir]]
  31. //
  32. // uncompresshelp extracts help files from a help storefile and
  33. // stores them back onto the filesystem.  helpfile.store is a help
  34. // storefile as generated by compresshelp.  outputdir is the
  35. // directory to store the files in.  helpfile.store defaults to
  36. // Help.store.  outputdir defaults to the input file with .store
  37. // stripped from the end.
  38. //
  39. //
  40. // To muck about with the Help.store files:
  41. //  o  Open an IXStoreFile instance on the file.
  42. //  o  Within the store, open an IXBTree on block one, which contains
  43. //     entries mapping the name of the helpfile element to the
  44. //     files which are contained within it.
  45. //  o  Within the store, open an IXBTree on block two, which contains
  46. //     entries mapping integer indices to file contents.
  47. //  o  The directory btree's values are of the form
  48. //     "filename key;[filename key;...]".  The filenames are files
  49. //     that are contained within the helpfile element, while the
  50. //     keys are integers which index into the file btree's to find
  51. //     the contents of the file.
  52. //  o  Individual file content keys may appear more than once in
  53. //     the directory.  This means that the specified files are the
  54. //     same [implemented as a call to link() in extractFile].
  55. //  o  File's which end in .rtf are compressed using /usr/ucb/compress.
  56. //     So is .index.store, apparently.
  57. //
  58. //
  59. // Version history:
  60. //
  61. // Wednesday, January 12, 1994:
  62. //     Fix inverted cur test in extractFile().
  63. //
  64. //     Rewrite uncompress call in terms of raw fork/execl/wait4.
  65. //     Previously it used system(), which did not work correctly
  66. //     when the filename contained spaces.  It also wouldn't have
  67. //     worked if the filename contained shell metacharacters (which
  68. //     was the reasoning behind not just putting quotes or
  69. //     double-quotes in the system call).
  70. //
  71. //     Added more descriptive and debugging output.
  72. //
  73. // Monday, January 10, 1994:
  74. //     First public release.
  75. #import <libc.h>
  76. #import <indexing/indexing.h>
  77. #import <objc/HashTable.h>
  78.  
  79. static HashTable *extractedFiles=nil;
  80. int uncompress( const char *filename)
  81. {
  82.     int pid;
  83.     if( !(pid=fork())) {
  84.     execl( "/usr/ucb/uncompress", "/usr/ucb/uncompress", filename, NULL);
  85.     exit( 1);
  86.     } else if( pid==-1) {
  87.     perror( "uncompresshelp: uncompress error");
  88.     return -1;
  89.     } else {
  90.     union wait status;
  91.     int ret=wait4( pid, &status, 0, NULL);
  92.     if( ret==-1) {
  93.         perror( "uncompresshelp: waiting for uncompress");
  94.         return -1;
  95.     } else if( WIFSIGNALED( status)) {
  96.         fprintf( stderr, "uncompresshelp: compress terminated on signal %d.\n", status.w_termsig);
  97.         return -1;
  98.     } else if( status.w_retcode) {
  99.         fprintf( stderr, "uncompresshelp: compress exited with %d.\n", status.w_retcode);
  100.         return -1;
  101.     }
  102.     }
  103.     return 0;
  104. }
  105. BOOL extractFile( IXBTreeCursor *fileCursor, unsigned fileId, const char *target)
  106. {
  107.     // If we've already extracted that index, just link the
  108.     // target to it.
  109.     const char *cur=[extractedFiles valueForKey:(void *)fileId];
  110.     if( cur) {
  111.     fprintf( stderr, "\tLinking to %s\n", cur);
  112.     if( link( cur, target)==-1) {
  113.         perror( "uncompresshelp: linking to extracted file");
  114.     }
  115.     
  116.     // Otherwise, we have to reach into the btree using fileCursor
  117.     // and find it.
  118.     } else {
  119.     void *file=NULL;
  120.     unsigned fileLen;
  121.     
  122.         // Make certain we have a table to map extracted files.
  123.     if( !extractedFiles) {
  124.         extractedFiles=[[HashTable alloc] initKeyDesc:"i" valueDesc:"%"];
  125.     }
  126.     
  127.         // Store the mapping for this index.
  128.     [extractedFiles insertKey:(void *)fileId value:(void *)NXUniqueString( target)];
  129.     
  130.         // Try to find the index in the table.
  131.     if( [fileCursor setKey:(void *)(&fileId) andLength:sizeof( fileId)]) {
  132.         int fd;
  133.         const char *slash, *dot;
  134.         
  135.         // Read the contents of the file.
  136.         fileLen=[fileCursor readValue:&file];
  137.         
  138.         // Open the file and stuff the contents into it.
  139.         fd=open( target, O_WRONLY|O_CREAT, 0644);
  140.         if( fd!=-1) {
  141.         if( write( fd, file, fileLen)<fileLen) {
  142.             perror( "uncompresshelp: Writing file");
  143.         }
  144.         if( close( fd)==-1) {
  145.             perror( "uncompresshelp: Closing file");
  146.         }
  147.         } else {
  148.         perror( "uncompresshelp: Opening file");
  149.         }
  150.         
  151.         // Free the contents.
  152.         free( file);
  153.         
  154.         // If the file is .rtf, uncompress it.
  155.         slash=rindex( target, '/');
  156.         slash=(slash ? slash+1 : target);
  157.         dot=rindex( slash, '.');
  158.         if( dot) {
  159.         if( !strcmp( dot, ".rtf") || !strcmp( slash, ".index.store")) {
  160.             char targetZ[ MAXPATHLEN+1];
  161.             sprintf( targetZ, "%s.Z", target);
  162.             fprintf( stderr, "\tRenaming %s\n\t      to %s\n", target, targetZ);
  163.             rename( target, targetZ);
  164.             fprintf( stderr, "\tUncompressing %s\n", targetZ);
  165.             uncompress( targetZ);
  166.         }
  167.         }
  168.     }
  169.     }
  170. }
  171.     // Create the directory, creating parent directories as needed.
  172. int mkdirs( char *dir, int mode)
  173. {
  174.     if( access( dir, F_OK)) {
  175.     int ret=0;
  176.     char *slash=rindex( dir, '/');
  177.     if( slash) {
  178.         *slash='\0';
  179.         ret=mkdirs( dir, mode);
  180.         *slash='/';
  181.     }
  182.     if( !ret) {
  183.         ret=mkdir( dir, mode);
  184.     }
  185.     return ret;
  186.     }
  187.     return 0;
  188. }
  189.     // *files is a file list in the format "filename key;[ filename
  190.     // key;...]".  Parse out the first filename/key pair into
  191.     // outputp/*indexp, and put the end position back into *files.
  192.     // Return YES if a filename was found.  This routine is more
  193.     // complex than needed because I was concerned that my only
  194.     // knowledge of what's possible for filenames is empirical -
  195.     // I know what's in some sample files, but I don't know whether
  196.     // there are other possibilities (consider spaces in filenames,
  197.     // for instance).  Better safe than sorry.
  198. BOOL getNextFile( const char **files, char *outputp, unsigned *indexp)
  199. {
  200.     const char *s, *p;
  201.     for( s=*files; *s; s++) {
  202.         // Get to a space followed by a digit.
  203.     if( *s==' ' && isdigit( s[ 1])) {
  204.         // Start accumulating the index.
  205.         unsigned i=s[ 1]-'0';
  206.         // Store away the end of the name.
  207.         p=s;
  208.         // Accumulate the rest of the index.
  209.         for( s+=2; isdigit( *s); s++) {
  210.         i*=10;
  211.         i+=s[ 0]-'0';
  212.         }
  213.         // It _must_ end with ; to be valid.
  214.         if( *s==';') {
  215.             // Store away the file name.
  216.         strncpy( outputp, *files, p-(*files));
  217.         outputp[ p-(*files)]='\0';
  218.             // Store away the index found.
  219.         *indexp=i;
  220.             // Return the new search position, skipping
  221.             // the extra space after interior elements of
  222.             // the list.
  223.         *files=(s[ 1] ? s+2 : s+1);
  224.         return YES;
  225.         }
  226.     }
  227.     }
  228.     return NO;
  229. }
  230. void main( int argc, char **argv)
  231. {
  232.     const char *inputFile="Help.store";
  233.     char outputDir[ MAXPATHLEN+1]="Help";
  234.     unsigned outputDirLen;
  235.     IXStoreFile *helpStore;
  236.     IXBTree *dirTree, *fileTree;
  237.     IXBTreeCursor *dirCursor, *fileCursor;
  238.  
  239.     // Decode command-line parameters.
  240.     if( argc>1) {
  241.     inputFile=argv[ 1];
  242.     if( argc>2) {
  243.         strcpy( outputDir, argv[ 2]);
  244.     } else {
  245.         const char *dot=rindex( inputFile, '.');
  246.         if( dot && !strcmp( dot, ".store")) {
  247.         strncpy( outputDir, inputFile, dot-inputFile);
  248.         outputDir[ dot-inputFile]='\0';
  249.         } else {
  250.         fprintf( stderr, "uncompresshelp:\tUnable to generate default outputDir:\n\t\t%s doesn't end in .store.\n", inputFile);
  251.         exit( 1);
  252.         }
  253.     }
  254.     }
  255.     outputDirLen=strlen( outputDir);
  256.     outputDir[ outputDirLen++]='/';
  257.     fprintf( stderr, "Extracting %s into %s\n", inputFile, outputDir);
  258.     
  259.     // Open up the storefile.
  260.     helpStore=[[IXStoreFile alloc] initFromFile:inputFile forWriting:NO];
  261.     if( !helpStore) {
  262.     fprintf( stderr, "Couldn't open %s\n", inputFile);
  263.     exit( 1);
  264.     }
  265.     
  266.     // Open up a tree to list our directories and files in
  267.     // them.
  268.     dirTree=[[IXBTree alloc] initFromBlock:1 inStore:helpStore];
  269.     dirCursor=[[IXBTreeCursor alloc] initWithBTree:dirTree];
  270.     
  271.     // Open up a tree to map indices to files.
  272.     fileTree=[[IXBTree alloc] initFromBlock:2 inStore:helpStore];
  273.     fileCursor=[[IXBTreeCursor alloc] initWithBTree:fileTree];
  274.  
  275.     // Start from the first entry in the directory list.
  276.     if( [dirCursor setFirst]) {
  277.     do {
  278.         char *dir=NULL, *files=NULL;
  279.         const char *s;
  280.         unsigned dirLen, filesLen;
  281.         
  282.         // Get the directory entry.
  283.         if( [dirCursor getKey:(void **)&dir andLength:&dirLen]) {
  284.         unsigned i;
  285.         const char *s;
  286.         
  287.             // Append the entry to the target directory.
  288.         strncpy( outputDir+outputDirLen, dir, dirLen);
  289.         outputDir[ outputDirLen+dirLen-1]='\0';
  290.         
  291.             // Read the list of files for that entry.
  292.         filesLen=[dirCursor readValue:(void **)&files];
  293.         
  294.             // Look for the first file in the list.
  295.         s=files;
  296.         if( getNextFile( &s, outputDir+outputDirLen+dirLen, &i)) {
  297.             // If that's the end, check to see if the
  298.             // directory entry's basename matches the
  299.             // only entry in the directory.
  300.             if( !*s) {
  301.             char *b=rindex( outputDir, '/');
  302.             if( b && !strcmp( b+1, outputDir+outputDirLen+dirLen)) {
  303.                 // If they match, then extract directly
  304.                 // into the name specified by the
  305.                 // directory entry.
  306.                 fprintf( stderr, "Extracting simple file %s\n", outputDir);
  307.                 *b='\0';
  308.                 mkdirs( outputDir, 0755);
  309.                 *b='/';
  310.                 extractFile( fileCursor, i, outputDir);
  311.                 // And go to the next entry.
  312.                 continue;
  313.             }
  314.             }
  315.             
  316.             // Make certain the directory(s) for the
  317.             // entry exist.
  318.             mkdirs( outputDir, 0755);
  319.             fprintf( stderr, "Extracting compound file %s\n", outputDir);
  320.             
  321.             // Put a slash between the parent dir and
  322.             // the filenames within it.
  323.             outputDir[ outputDirLen+dirLen-1]='/';
  324.             
  325.             // Extract each of the contained files.
  326.             do {
  327.             fprintf( stderr, "    Extracting %s\n", outputDir);
  328.             extractFile( fileCursor, i, outputDir);
  329.             } while( getNextFile( &s, outputDir+outputDirLen+dirLen, &i));
  330.         }
  331.         free( files);
  332.         }
  333.     } while( [dirCursor setNext]);
  334.     }
  335. }
  336.