Previous Table of Contents Next


The processing loop starts off by reading in the next available header file from the input CAR file. If we are at the end of file, this operation returns a 0 and we exit. Otherwise, we call SearchFileList(), which looks for a match of the file name in the FileList[] array, including wild card matches. The result of that search is stored in the match variable, at which point the switch statement is started. The actions taken in the switch depend on the command give on the command line:

Delete: If match is true, it means the user wants to delete this file from the CAR archive. In this case, the file is skipped over with a call to SkipOverFileFromInputCar(). Otherwise, the file is copied to the output CAR file with a call to CopyFileFromInputCar().
Add: If match is true, means that one of the files that was added to the output CAR file at the start of the program also appears in input CAR file. When this is the case, we have to skip over the file, since it has been superseded. If no match is found, the file is copied to the output CAR file.
List: If a match is found, the file statistics are listed on stdout. No output file is being created by this command, so after it is listed, it is automatically skipped.
Print:
Test:
Xtract:
If a match is found, it means this file has to be extracted to one of the possible destinations. For the Print command, it goes to stdout. For Test, it goes to the null device, and for Xtract, to a file that is created with the appropriate name. If no match was found, the file is skipped.
Replace: If a match is found, it means we need to replace the version of the file found in the archive with a file of the same name in the current directory. If that file is found, it is Inserted into the output file, and the current input file is skipped. If no match is found, or the file cannot be opened, the file in the input CAR file is copied to the output CAR file.

Once all of these operations are complete, a count of matched files is returned to the calling routine, for display when the program exits.

Skipping/Copying Input File

The main processing loop only has one of three choices to take to go past the current file in the input CAR file. The first two are the skip and copy routines. One of these copies the current file in the input CAR to the output CAR file. The second routine skips over the file and moves on to the next header.

The skip operation was discussed previously, and is quite simple, since we have the exact size in bytes of the compressed file stored in the header. All the program has to do is advance that number of bytes forward in the input file. Once this is done, the file is lost to the output file, so this is only done when the file is to be Deleted or Replaced (including replacement with an Add command).

void SkipOverFileFromInputCar()
{
   fseek( InputCarFile, Header.compressed_size, SEEK_CUR );
}

void CopyfileFromInputCar()
{
 char buffer[ 256 ];
 unsigned int count;

 WriteFileHeader();
 while ( Header.compressed_size != 0 ) {
  if ( Header.compressed_size < 256 )
   count = (int) Header.compressed_size;
  else
   count = 256;
  if ( fread( buffer, 1, count, InputCarFile ) != count )
   FatalError( "Error reading input file %s", Header.file_name );
  Header.compressed_size -= count;
  if ( fwrite( buffer, 1, count, OutputCarFile) != count )
   FatalError( "Error writing to output CAR file" );
 }
}

Copying the file from the input CAR file to the output CAR file is the “normal” mode of operation, where the contents of the input file are not lost. This is only marginally more complicated than the skip routine. All we need to do here is read in the predetermined number of bytes a block at a time, and write them out to the output file, checking for errors along the way.

Once the copy is complete, the input file pointer is left pointing at the next file header in the input CAR file, and the program is ready to start back at the top of the loop.

File Insertion

The Insertion routine is called to insert an external file into the output CAR file. The insertion routine makes a first attempt to compress the file using the LZSS compression routine. If that routine fails, a straight storage routine is called instead. Since we don’t know what the size of the compressed file will be until after the compression actually takes place, Insert() has to back up and rewrite the header after the compression is finally successful. In addition, the compression method is stored in the header file as well. A compression method of 1 is used for normal storage, 2 for LZSS compression. Clearly it would be relatively simple to add new forms of compression by adding new numbers to the table. All that would be needed then is additional code in the Extract() routine to support the new compression method.

void Insert( input_text_file, operation )
FILE *input_text_file;
char *operation;
{
 long saved_position_of_header;
 long saved_position_of_file;

 fprintf( stderr, "%s %-20s", operation, Header.file_name );
 saved_position_of_header = ftell( OutputCarFile );
 Header.compression_method = 2;
 WriteFileHeader();
 saved_position_of_file = ftell(OutputCarFile);
 fseek( input_text_file, OL, SEEK_END );
 Header.original_size = ftell( input_text_file );
 fseek( input_text_file, OL, SEEK_SET );

 if ( !LZSSCompress( input_text_file ) ) {
  Header.compression_method = 1;
  fseek( OutputCarFile, saved_position_of_file, SEEK_SET );
  rewind( input_text_file );
  Store( input_text_file );
 }
 fclose( input_text_file );
 fseek( OutputCarFile, saved_position_of_header, SEEK_SET );
 WriteFileHeader();
 fseek( OutputCarFile, OL, SEEK_END );
 printf( " %d%%\n", RatioInPercent( Header.compressed_size,
                     Header.original_size ) );
}


Previous Table of Contents Next