⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 406-456.html

📁 The primary purpose of this book is to explain various data-compression techniques using the C progr
💻 HTML
📖 第 1 页 / 共 5 页
字号:
* 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 + -