📄 406-456.html
字号:
* it tries to open the file, which ought to work. Second, it strips the* leading drive and path information from the file, since we don't keep* that information in the archive. Finally, it checks to see if the* resulting name is one that has already been added to the archive.* If it has, the file is skipped so that we don't end up with an* invalid archive.*/int AddFileListToArchive(){ int i; int j; int skip; char *s; FILE *input_text_file; for ( i = 0 ; FileList[ i ] != NULL ; i++ ) { input_text_file = fopen( FileList[ i ], "rb" ); if ( input_text_file == NULL ) FatalError( "Could not open %s to add to CAR file", FileList[ i ] );#ifdef MSDOS s = strrchr( FileList[ i ], '\\' ); if ( s == NULL ) s = strrchr( FileList[ i ], ':' );#endif#ifndef MSDOS /* Must be UNIX */ s = strrchr( FileList[ i ], '/' );#endif if ( s != NULL ) s++; else s = FileList[ i ]; skip = 0; for ( j = 0 ; j < i ; j++ ) if ( strcmp( s, FileList[ j ] ) == 0 ) { fprintf( stderr, "Duplicate file name: %s", FileList[ i ] ); fprintf( stderr, " Skipping this file...\n" ); skip = 1; break; } if (s != FileList[ i ] ) { for ( j = 0 ; s[ j ] != '\0' ; j++ ) FileList[ i ][ j ] = s[ j ]; FileList[ i ][ j ] = '\0'; } if ( !skip ) { strcpy( Header.file_name, FileList[ i ] ); Insert( input_text_file, "Adding" ); } else fclose( input_text_file ); } return( i );}/** This is the main loop where all the serious work done by this* program takes place. Essentially, this routine starts at the* beginning of the input CAR file, and processes every file in* the CAR. Depending on what command is being executed, that might* mean expanding the file, copying it to standard output,* adding it to the output CAR, or skipping over it completely.*/int ProcessAllFilesInInputCar( command, count )int command;int count;{ int matched; FILE *input_text_file; FILE *output_destination; if ( command == 'P' ) output_destination = stdout; else if ( command == 'T' )#ifdef MSDOS output_destination = fopen( "NUL", "wb" );#else output_destination = fopen( "/dev/null", "wb" );#endif else output_destination = NULL;/** This is the loop where it all happens. I read in the header for* each file in the input CAR, then see if it matches any of the file* and wildcard specifications in the FileList created earlier. That* information, combined with the command, tells me what I need to* know in order to process the file. Note that if the 'Addfiles' command* is being executed, the InputCarFile will be NULL, so this loop* can be safely skipped.*/ while ( InputCarFile != NULL && ReadFileHeader() != 0 ) { matched = SearchFileList( Header.file_name ); switch ( command ) { case 'D' : if ( matched ) { SkipOverFileFromInputCar(); count++; } else CopyFileFromInputCar(); break; case 'A' : if ( matched ) SkipOverFileFromInputCar(); else CopyFileFromInputCar(); break; case 'L' : if ( matched ) { ListCarFileEntry(); count++; } else SkipOverFileFromInputCar(); break; case 'P' : case 'X' : case 'T' : if ( matched ) { Extract( output_destination ); count++; } else SkipOverFileFromInputCar(); break; case 'R' : if ( matched ) { input_text_file = fopen( Header.file_name, "rb" ); if ( input_text_file == NULL ) { fprintf( stderr, "Could not find %s", Header.file_name ); fprintf( stderr, " for replacement, skipping\n" ); CopyFileFromInputCar(); } else { SkipOverFileFromInputCar(); Insert( input_text_file, "Replacing" ); count++; fclose(input_text_file ); } } else CopyFileFromInputCar(); break; } } return( count );}/** This routine looks through the entire list of arguments to see if* there is a match with the file name currently in the header. As each* new file in InputCarFile is encountered in the main processing loop,* this routine is called to determine if it has an appearance anywhere* in the FileList[] array. The results is used to in the main loop* to determine what action to take. For example, if the command were* the 'Delete' command, the match result would determine whether to* copy the file form the InputCarFile to the OutputCarFile, or skip* over it.** The actual work in this routine is really performed by the* WildCardMatch() routine which checks the file name against one of the* names in the FileList[] array. Since most of the commands can use* wild cards to specify file names inside the CAR file, we need a* special comparison routine.*/int SearchFileList( file_name )char *file_name;{ int i; for ( i = 0 ; FileList[ i ] != NULL ; i++ ) { if ( WildCardMatch( file_name, FileList[ i ] ) ) return( 1 ); } return( 0 );}/** WildCardMatch() compares string to wild_string, looking for a match.* Wild card characters supported are only '*' and '?', where '*'* represents a string of any length, including 0, and '?' represents any* single character.*/int WildCardMatch( string, wild_string )char *string;char *wild_string;{ for ( ; ; ) { if ( *wild_string == '*' ) { wild_string++; for ( ; ; ) { while ( *string != '\0' && *string != *wild_string ) string++; if ( WildCardMatch( string, wild_string ) ) return( 1 ); else if ( *string == '\0' ) return( 0 ); else string++; } } else if ( *wild_string == '?' ) { wild_string++; if ( * string++ == '\0' ) return( 0 ); } else { if ( * string != *wild_string ) return( 0 ); if ( *string == '\0' ) return( 1 ); string++; wild_string++; } }}/** When the main processing loop reads in a header, it checks to see* if it is going to copy that file either to the OutputCarFile or expand* it. If neither is going to happen, we need to skip past this file and* go on to the next header. This can be done by seeking past the* compressed file. Since the compressed size is stored in the header* information, it is easy to do. Note that this routine assumes that the* file pointer has not been modified since the header was read in. This* means it should be located at the first byte of the compressed data.*/void SkipOverFileFromInputCar(){ fseek( InputCarFile, Header.compressed_size, SEEK_CUR );}/** When performing an operation that modifies the input CAR file,* compressed files will frequently need to be copied from the input CAR* file to the output CAR file. This routine does that using simple* repeated block copy operations. Since it is writing directly to the* output CAR file, the first thing it needs to do is write out the* current Header so that the CAR file will be properly structured.* Following that, the compressed file is copied one block at a time to* the output. When this routine completes, the input file pointer is* positioned at the next header in the input CAR file, and the output* file pointer is positioned at the EOF position in the output file.* This is the proper place for the next record to begin.*/void CopyFileFromInputCar(){ char buffer[ 256 ]; 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" ); }}/** When the operation requested by the user is 'List', this routine is* called to print out the column headers. List output goes to standard* output, unlike most of the other messages in this program, which go* to stderr.*/void PrintListTitles(){ printf( "\n" ); printf( " Original Compressed\n" ); printf( "Filename Size Size Ratio CRC-32 Method\n" ); printf( "________ ____ ____ _________________\n" );}/** When the List command is given, the main loop reads in each header* block, then tests to see if the file name in the header block matches* one of the file names (including wildcards) in the FileList. If it is,* this routine is called to print out the information on the file.*/void ListCarFileEntry(){ static char *methods[] = { "Stored", "LZSS" };printf( "%-20s %10lu %10lu %4d%% %081x %s\n", Header.file_name, Header.original_size, Header.compressed_size, RatioInPercent( Header.compressed_size, Header.original_size ), Header.original_crc, methods[ Header.compression_method - 1 ] );}/** The compression figure used in this book is calculated here. The value* is scaled so that a file that has just been stored has a compression* ratio of 0%, while one that has been shrunk down to nothing would have* a ratio of 100%.*/int RatioInPercent( compressed, original )unsigned long compressed;unsigned long original;{ int result; if ( original == 0 ) return( 0 ); result = (int) ( ( 100L * compressed ) / original ); return( 100 - result );}/** This routine is where all the information about the next file in* the archive is read in. The data is read into the global Header* structure. To preserve portability of CAR files across systems,* the data in each file header is packed into an unsigned char array* before it is written out to the file. To read this data back in* to the Header structure, we first read it into another unsigned* character array, then employ an unpacking routine to convert that* data into ints and longs. This helps us avoid problems with* big/little endian conflicts, as well as incompatibilities in structure* packing, which show up even between different compilers targetted for* the same architecture.** To avoid causing any additional confusion, the data members for the* header structure are at least stored in exactly the same order as* they appear in the structure definition. The primary difference is* that the entire file name character array is not stored, which would* waste a lot of space. Instead, we just store the number of characters* in the name, including the null termination character. The file name* serves the additional purpose of identifying the end of the CAR file* with a file name length of 0 bytes.*/int ReadFileHeader(){ unsigned char header_data[ 17 ]; unsigned long header_crc; int i; int c; for ( i = 0 ; ; ) { c = getc( InputCarFile ); Header.file_name[ i ] = (char) c; if ( c == '\0' ) break; if ( ++i == FILENAME_MAX ) FatalError ( "File name exceeded maximum in header" );}if ( i == 0 ) return( 0 );header_crc = CalculateBlockCRC32( i + 1, CRC_MASK, Header.file_name );fread( header_data, 1, 17, InputCarFile );Header.compression_method = (char) UnpackUnsignedData( 1, header_data + 0 );Header.original_size = UnpackUnsignedData( 4, header_data + 1 );Header.compressed_size = UnpackUnsignedData( 4, header_data + 5 );Header.original_crc = UnpackUnsignedData( 4, header_data + 9 );Header.header_crc = UnpackUnsignedData( 4, header_data + 13 );header_crc = CalculateBlockCRC32(13, header_crc, header_data );header_crc ^= CRC_MASK;if ( Header.header_crc != header_crc ) FatalError( "Header checksum error for file %s", Header.file_name );return( 1 );}/** This routine is used to transform packed characters into unsigned* integers. Its only purpose is to convert packed character data* into integers and longs.*/unsigned long UnpackUnsignedData( number_of_bytes, buffer )int number_of_bytes;unsigned char *buffer;{ unsigned long result; int shift_count; result = 0; shift_count = 0; while ( number_of_bytes-- > ) { result |= (unsigned long) * buffer++ << shift_count; shift_count += 8; } return( result );}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -