/* * Listing 15 -- churnx.c * * This is a utility program used to test compression/decompression * programs for accuracy, speed, and compression ratios. Calling * CHURN.EXE with a single argument will cause CHURN to compress then * decompress every file in the specified directory tree, checking the * compression ratio and the accuracy of the operation. This is a good * program to run overnight when you think your new algorithm works * properly. * * This program will presently compile on my SCO Xenix system. * I don't know if the directory access and time routines are portable * to other *NIX systems. * * To build this program: * * cc -o churnx churnx.c -lx * * */ #include #include #include #include #include #include #include /* * Some global variables. */ long total_input_bytes; long total_output_bytes; /* * Declarations for global routines. */ void churn_files( char *path ); void close_all_the_files( void ); int compress( char *file_name ); /* * main() doesn't have to do a whole lot in this program. It * reads in the command line to determine what the root directory * to start looking at is, then it initializes the total byte counts * and the start time. It can then call churn_files(), which does all * the work, then report on the statistics resulting from churn_files. */ void main( int argc, char *argv[] ) { long int start_time; long int stop_time; char argument[ 81 ]; if ( argc < 2 ) strcpy( argument, "/" ); else strcpy( argument, argv[ 1 ] ); total_input_bytes = 0L; total_output_bytes = 0L; start_time = time( (long *) 0 ); churn_files( argument ); stop_time = time( (long *) 0 ); printf( "%ld seconds elapsed time\n", stop_time - start_time ); printf( "Total input bytes: %8ld: \n", total_input_bytes ); printf( "Total output bytes: %8ld: \n", total_output_bytes ); printf( "Reduction:%ld%% ", 100L - ( ( 100L * total_output_bytes) / total_input_bytes ) ); printf( "Bits per byte: %f\n", 8.0 * total_output_bytes / total_input_bytes ); } /* * churn_files() is a routine that sits in a loop looking at * files in the directory specified by its single argument, "path". * As each file is looked at, one of three things happens. If it * is a normal file, and has a compressed extension name, like ".ZIP", * the file is ignored. If it is a normal file, and doesn't have a * compressed extension name, it is compressed and decompressed by * another routine. Finally, if the file is a subdirectory, * churn_files() is called recursively with the file name as its * path argument. This is one of those rare routines where recursion * provides a way to truly simplify the task at hand. */ void churn_files( char *path ) { DIR *dirp; struct direct *entry; struct stat buf; char full_name[ 81 ]; dirp = opendir( path ); if ( dirp == NULL ) { printf( "Error opening directory %s\n", path ); exit( -1 ); } for ( entry=readdir(dirp) ; entry != NULL ; entry=readdir(dirp) ) { if ( entry->d_name[0] != '.' ) { strcpy( full_name, path ); strcat( full_name, "/" ); strcat( full_name, entry->d_name ); if ( stat( full_name, &buf ) == -1 ) { printf( "Error reading stat from file %s!\n", full_name ); exit( -1 ); } if ( buf.st_mode & 040000 ) churn_files( full_name ); else { fprintf( stderr, "Testing %s\n", full_name ); if ( !compress( full_name ) ) fprintf( stderr, "Comparison failed!\n" ); } } } } /* * This is the routine that does the majority of the work for * this program. It takes a file whose name is passed here. It then * compresses, then decompresses that file. It then compares the file * to the decompressed output, and reports on the results. */ FILE *input; FILE *output; FILE *compressed; int compress( char *file_name ) { long new_size; long old_size; char command_line[132]; int c; fflush( stdout ); printf( "%s\n", file_name ); sprintf( command_line, "./comp-1 -f %s\n", file_name ); system( command_line ); sprintf( command_line, "./expand-1\n" ); system( command_line ); input = fopen( file_name, "rb" ); output = fopen( "test.out", "rb" ); compressed = fopen( "test.cmp", "rb" ); if ( input == NULL || output == NULL || compressed == NULL ) { close_all_the_files(); printf( "Failed, couldn't open file!\n" ); return( 0 ); } fseek( input, 0L, 2 ); old_size = ftell( input ); fseek( input, 0L, 0 ); fseek( compressed, 0L, 2 ); new_size = ftell( compressed ); printf( "Old size: %8ld: ", old_size ); printf( "New size: %8ld\n", new_size ); if ( old_size == 0L ) old_size = 1L; printf( "Reduction: %ld%% ", 100L - ( ( 100L * new_size ) / old_size ) ); printf( "Bits per byte: %f\n", 8.0 * new_size / old_size ); total_input_bytes += old_size; total_output_bytes += new_size; do { c = getc( input ); if ( getc( output ) != c ) { printf( "Failed comparison!\n" ); close_all_the_files(); return( 0 ); } } while ( c != EOF ); printf( "File compare passed.\n" ); close_all_the_files(); return( 1 ); } void close_all_the_files() { if ( input != NULL ) fclose( input ); if ( output != NULL ) fclose( output ); if ( compressed != NULL ) fclose( compressed ); }