/* File to file example - Test program for the library lzlib Copyright (C) 2010-2024 Antonio Diaz Diaz. This program is free software: you have unlimited permission to copy, distribute, and modify it. Try 'ffexample -h' for usage information. This program is an example of how file-to-file compression/decompression can be implemented using lzlib. */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ #include #include #endif #include "lzlib.h" #ifndef min #define min(x,y) ((x) <= (y) ? (x) : (y)) #endif static void show_help( void ) { printf( "ffexample is an example program showing how file-to-file (de)compression can\n" "be implemented using lzlib. The content of infile is compressed,\n" "decompressed, or both, and then written to outfile.\n" "\nUsage: ffexample operation [infile [outfile]]\n" ); printf( "\nOperation:\n" " -h display this help and exit\n" " -c compress infile to outfile\n" " -d decompress infile to outfile\n" " -b both (compress then decompress) infile to outfile\n" " -m compress (multimember) infile to outfile\n" " -l compress (1 member per line) infile to outfile\n" " -r decompress with resync if data error or leading garbage\n" "\nIf infile or outfile are omitted, or are specified as '-', standard input or\n" "standard output are used in their place respectively.\n" "\nReport bugs to lzip-bug@nongnu.org\n" "Lzlib home page: http://www.nongnu.org/lzip/lzlib.html\n" ); } int ffcompress( struct LZ_Encoder * const encoder, FILE * const infile, FILE * const outfile ) { enum { buffer_size = 16384 }; uint8_t buffer[buffer_size]; while( true ) { int len, ret; int size = min( buffer_size, LZ_compress_write_size( encoder ) ); if( size > 0 ) { len = fread( buffer, 1, size, infile ); ret = LZ_compress_write( encoder, buffer, len ); if( ret < 0 || ferror( infile ) ) break; if( feof( infile ) ) LZ_compress_finish( encoder ); } ret = LZ_compress_read( encoder, buffer, buffer_size ); if( ret < 0 ) break; len = fwrite( buffer, 1, ret, outfile ); if( len < ret ) break; if( LZ_compress_finished( encoder ) == 1 ) return 0; } return 1; } int ffdecompress( struct LZ_Decoder * const decoder, FILE * const infile, FILE * const outfile ) { enum { buffer_size = 16384 }; uint8_t buffer[buffer_size]; while( true ) { int len, ret; int size = min( buffer_size, LZ_decompress_write_size( decoder ) ); if( size > 0 ) { len = fread( buffer, 1, size, infile ); ret = LZ_decompress_write( decoder, buffer, len ); if( ret < 0 || ferror( infile ) ) break; if( feof( infile ) ) LZ_decompress_finish( decoder ); } ret = LZ_decompress_read( decoder, buffer, buffer_size ); if( ret < 0 ) break; len = fwrite( buffer, 1, ret, outfile ); if( len < ret ) break; if( LZ_decompress_finished( decoder ) == 1 ) return 0; } return 1; } int ffboth( struct LZ_Encoder * const encoder, struct LZ_Decoder * const decoder, FILE * const infile, FILE * const outfile ) { enum { buffer_size = 16384 }; uint8_t buffer[buffer_size]; while( true ) { int len, ret; int size = min( buffer_size, LZ_compress_write_size( encoder ) ); if( size > 0 ) { len = fread( buffer, 1, size, infile ); ret = LZ_compress_write( encoder, buffer, len ); if( ret < 0 || ferror( infile ) ) break; if( feof( infile ) ) LZ_compress_finish( encoder ); } size = min( buffer_size, LZ_decompress_write_size( decoder ) ); if( size > 0 ) { ret = LZ_compress_read( encoder, buffer, size ); if( ret < 0 ) break; ret = LZ_decompress_write( decoder, buffer, ret ); if( ret < 0 ) break; if( LZ_compress_finished( encoder ) == 1 ) LZ_decompress_finish( decoder ); } ret = LZ_decompress_read( decoder, buffer, buffer_size ); if( ret < 0 ) break; len = fwrite( buffer, 1, ret, outfile ); if( len < ret ) break; if( LZ_decompress_finished( decoder ) == 1 ) return 0; } return 1; } int ffmmcompress( FILE * const infile, FILE * const outfile ) { enum { buffer_size = 16384, member_size = 4096 }; uint8_t buffer[buffer_size]; bool done = false; struct LZ_Encoder * const encoder = LZ_compress_open( 65535, 16, member_size ); if( !encoder || LZ_compress_errno( encoder ) != LZ_ok ) { fputs( "ffexample: Not enough memory.\n", stderr ); LZ_compress_close( encoder ); return 1; } while( true ) { int len, ret; int size = min( buffer_size, LZ_compress_write_size( encoder ) ); if( size > 0 ) { len = fread( buffer, 1, size, infile ); ret = LZ_compress_write( encoder, buffer, len ); if( ret < 0 || ferror( infile ) ) break; if( feof( infile ) ) LZ_compress_finish( encoder ); } ret = LZ_compress_read( encoder, buffer, buffer_size ); if( ret < 0 ) break; len = fwrite( buffer, 1, ret, outfile ); if( len < ret ) break; if( LZ_compress_member_finished( encoder ) == 1 ) { if( LZ_compress_finished( encoder ) == 1 ) { done = true; break; } if( LZ_compress_restart_member( encoder, member_size ) < 0 ) break; } } if( LZ_compress_close( encoder ) < 0 ) done = false; return done; } /* Compress 'infile' to 'outfile' as a multimember stream with one member for each line of text terminated by a newline character or by EOF. Return 0 if success, 1 if error. */ int fflfcompress( struct LZ_Encoder * const encoder, FILE * const infile, FILE * const outfile ) { enum { buffer_size = 16384 }; uint8_t buffer[buffer_size]; while( true ) { int len, ret; int size = min( buffer_size, LZ_compress_write_size( encoder ) ); if( size > 0 ) { for( len = 0; len < size; ) { int ch = getc( infile ); if( ch == EOF || ( buffer[len++] = ch ) == '\n' ) break; } /* avoid writing an empty member to outfile */ if( len == 0 && LZ_compress_data_position( encoder ) == 0 ) return 0; ret = LZ_compress_write( encoder, buffer, len ); if( ret < 0 || ferror( infile ) ) break; if( feof( infile ) || buffer[len-1] == '\n' ) LZ_compress_finish( encoder ); } ret = LZ_compress_read( encoder, buffer, buffer_size ); if( ret < 0 ) break; len = fwrite( buffer, 1, ret, outfile ); if( len < ret ) break; if( LZ_compress_member_finished( encoder ) == 1 ) { if( feof( infile ) && LZ_compress_finished( encoder ) == 1 ) return 0; if( LZ_compress_restart_member( encoder, INT64_MAX ) < 0 ) break; } } return 1; } /* Decompress 'infile' to 'outfile' with automatic resynchronization to next member in case of data error, including the automatic removal of leading garbage. */ int ffrsdecompress( struct LZ_Decoder * const decoder, FILE * const infile, FILE * const outfile ) { enum { buffer_size = 16384 }; uint8_t buffer[buffer_size]; while( true ) { int len, ret; int size = min( buffer_size, LZ_decompress_write_size( decoder ) ); if( size > 0 ) { len = fread( buffer, 1, size, infile ); ret = LZ_decompress_write( decoder, buffer, len ); if( ret < 0 || ferror( infile ) ) break; if( feof( infile ) ) LZ_decompress_finish( decoder ); } ret = LZ_decompress_read( decoder, buffer, buffer_size ); if( ret < 0 ) { if( LZ_decompress_errno( decoder ) == LZ_header_error || LZ_decompress_errno( decoder ) == LZ_data_error ) { LZ_decompress_sync_to_member( decoder ); continue; } break; } len = fwrite( buffer, 1, ret, outfile ); if( len < ret ) break; if( LZ_decompress_finished( decoder ) == 1 ) return 0; } return 1; } int main( const int argc, const char * const argv[] ) { #if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ setmode( STDIN_FILENO, O_BINARY ); setmode( STDOUT_FILENO, O_BINARY ); #endif struct LZ_Encoder * const encoder = LZ_compress_open( 65535, 16, INT64_MAX ); struct LZ_Decoder * const decoder = LZ_decompress_open(); FILE * const infile = ( argc >= 3 && strcmp( argv[2], "-" ) != 0 ) ? fopen( argv[2], "rb" ) : stdin; FILE * const outfile = ( argc >= 4 && strcmp( argv[3], "-" ) != 0 ) ? fopen( argv[3], "wb" ) : stdout; int retval; if( argc < 2 || argc > 4 || strlen( argv[1] ) != 2 || argv[1][0] != '-' ) { show_help(); return 1; } if( !encoder || LZ_compress_errno( encoder ) != LZ_ok || !decoder || LZ_decompress_errno( decoder ) != LZ_ok ) { fputs( "ffexample: Not enough memory.\n", stderr ); LZ_compress_close( encoder ); LZ_decompress_close( decoder ); return 1; } if( !infile ) { fprintf( stderr, "ffexample: %s: Can't open input file: %s\n", argv[2], strerror( errno ) ); return 1; } if( !outfile ) { fprintf( stderr, "ffexample: %s: Can't open output file: %s\n", argv[3], strerror( errno ) ); return 1; } switch( argv[1][1] ) { case 'c': retval = ffcompress( encoder, infile, outfile ); break; case 'd': retval = ffdecompress( decoder, infile, outfile ); break; case 'b': retval = ffboth( encoder, decoder, infile, outfile ); break; case 'm': retval = ffmmcompress( infile, outfile ); break; case 'l': retval = fflfcompress( encoder, infile, outfile ); break; case 'r': retval = ffrsdecompress( decoder, infile, outfile ); break; default: show_help(); return argv[1][1] != 'h'; } if( LZ_decompress_close( decoder ) < 0 || LZ_compress_close( encoder ) < 0 || fclose( outfile ) != 0 || fclose( infile ) != 0 ) retval = 1; return retval; }