Blame | Last modification | View Log | RSS feed
/* uclpack.c -- example program: a simple file packerThis file is part of the UCL data compression library.Copyright (C) 1996-2002 Markus Franz Xaver Johannes OberhumerAll Rights Reserved.The UCL library is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public License aspublished by the Free Software Foundation; either version 2 ofthe License, or (at your option) any later version.The UCL library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with the UCL library; see the file COPYING.If not, write to the Free Software Foundation, Inc.,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.Markus F.X.J. Oberhumer<markus@oberhumer.com>*//*************************************************************************// NOTE: this is an example program, so do not use to backup your data//// This program lacks things like sophisticated file handling but is// pretty complete regarding compression - it should provide a good// starting point for adaption for you applications.**************************************************************************/#include <ucl/ucl.h>#include "lutil.h"static const char *progname = NULL;static unsigned long total_in = 0;static unsigned long total_out = 0;static ucl_bool opt_debug = 0;/* don't compute or verify checksum, always use fast decompressor */static ucl_bool opt_fast = 0;/* magic file header for compressed files */static const unsigned char magic[8] ={ 0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a };/*************************************************************************// file IO**************************************************************************/ucl_uint xread(FILE *f, ucl_voidp buf, ucl_uint len, ucl_bool allow_eof){ucl_uint l;l = ucl_fread(f,buf,len);if (l > len){fprintf(stderr,"\nsomething's wrong with your C library !!!\n");exit(1);}if (l != len && !allow_eof){fprintf(stderr,"\nread error - premature end of file\n");exit(1);}total_in += l;return l;}ucl_uint xwrite(FILE *f, const ucl_voidp buf, ucl_uint len){ucl_uint l;if (f != NULL){l = ucl_fwrite(f,buf,len);if (l != len){fprintf(stderr,"\nwrite error [%ld %ld] (disk full ?)\n",(long)len, (long)l);exit(1);}}total_out += len;return len;}int xgetc(FILE *f){unsigned char c;xread(f,(ucl_voidp) &c,1,0);return c;}void xputc(FILE *f, int c){unsigned char cc = (unsigned char) c;xwrite(f,(const ucl_voidp) &cc,1);}/* read and write portable 32-bit integers */ucl_uint32 xread32(FILE *f){unsigned char b[4];ucl_uint32 v;xread(f,b,4,0);v = (ucl_uint32) b[3] << 0;v |= (ucl_uint32) b[2] << 8;v |= (ucl_uint32) b[1] << 16;v |= (ucl_uint32) b[0] << 24;return v;}void xwrite32(FILE *f, ucl_uint32 v){unsigned char b[4];b[3] = (unsigned char) (v >> 0);b[2] = (unsigned char) (v >> 8);b[1] = (unsigned char) (v >> 16);b[0] = (unsigned char) (v >> 24);xwrite(f,b,4);}/*************************************************************************// util**************************************************************************/static ucl_uint get_overhead(int method, ucl_uint size){if (method == 0x2b || method == 0x2d || method == 0x2e)return size / 8 + 256;return 0;}static char method_name[64];static ucl_bool set_method_name(int method, int level){method_name[0] = 0;if (level < 0 || level > 10)return 0;if (level == 0)sprintf(method_name,"uncompressed/%d", level);else if (method == 0x2b)sprintf(method_name,"NRV2B-99/%d", level);else if (method == 0x2d)sprintf(method_name,"NRV2D-99/%d", level);else if (method == 0x2e)sprintf(method_name,"NRV2E-99/%d", level);elsereturn 0;return 1;}/*************************************************************************// compress**************************************************************************/int do_compress(FILE *fi, FILE *fo, int method, int level, ucl_uint block_size){int r = 0;ucl_byte *in = NULL;ucl_byte *out = NULL;ucl_uint in_len;ucl_uint out_len;ucl_uint32 flags = opt_fast ? 0 : 1;ucl_uint32 checksum;ucl_uint overhead = 0;total_in = total_out = 0;/** Step 1: write magic header, flags & block size, init checksum*/xwrite(fo,magic,sizeof(magic));xwrite32(fo,flags);xputc(fo,method); /* compression method */xputc(fo,level); /* compression level */xwrite32(fo,block_size);checksum = ucl_adler32(0,NULL,0);/** Step 2: allocate compression buffers and work-memory*/overhead = get_overhead(method,block_size);in = (ucl_byte *) ucl_malloc(block_size);out = (ucl_byte *) ucl_malloc(block_size + overhead);if (in == NULL || out == NULL){printf("%s: out of memory\n", progname);r = 1;goto err;}/** Step 3: process blocks*/for (;;){/* read block */in_len = xread(fi,in,block_size,1);if (in_len <= 0)break;/* update checksum */if (flags & 1)checksum = ucl_adler32(checksum,in,in_len);/* compress block */r = UCL_E_ERROR;if (level == 0){out_len = in_len; /* uncompressed */r = UCL_E_OK;}else if (method == 0x2b)r = ucl_nrv2b_99_compress(in,in_len,out,&out_len,0,level,NULL,NULL);else if (method == 0x2d)r = ucl_nrv2d_99_compress(in,in_len,out,&out_len,0,level,NULL,NULL);else if (method == 0x2e)r = ucl_nrv2e_99_compress(in,in_len,out,&out_len,0,level,NULL,NULL);if (r != UCL_E_OK || out_len > in_len + get_overhead(method,in_len)){/* this should NEVER happen */printf("internal error - compression failed: %d\n", r);r = 2;goto err;}/* write uncompressed block size */xwrite32(fo,in_len);if (out_len < in_len){/* write compressed block */xwrite32(fo,out_len);xwrite(fo,out,out_len);}else{/* not compressible - write uncompressed block */xwrite32(fo,in_len);xwrite(fo,in,in_len);}}/* write EOF marker */xwrite32(fo,0);/* write checksum */if (flags & 1)xwrite32(fo,checksum);r = 0;err:ucl_free(out);ucl_free(in);return r;}/*************************************************************************// decompress / test//// We are using overlapping (in-place) decompression to save some// memory - see overlap.c.**************************************************************************/int do_decompress(FILE *fi, FILE *fo){int r = 0;ucl_byte *buf = NULL;ucl_uint buf_len;unsigned char m [ sizeof(magic) ];ucl_uint32 flags;int method;int level;ucl_uint block_size;ucl_uint32 checksum;ucl_uint overhead = 0;total_in = total_out = 0;/** Step 1: check magic header, read flags & block size, init checksum*/if (xread(fi,m,sizeof(magic),1) != sizeof(magic) ||memcmp(m,magic,sizeof(magic)) != 0){printf("%s: header error - this file is not compressed by uclpack\n", progname);r = 1;goto err;}flags = xread32(fi);method = xgetc(fi);level = xgetc(fi);block_size = xread32(fi);overhead = get_overhead(method,block_size);if (overhead == 0 || !set_method_name(method, level)){printf("%s: header error - invalid method %d (level %d)\n",progname, method, level);r = 2;goto err;}if (block_size < 1024 || block_size > 8*1024*1024L){printf("%s: header error - invalid block size %ld\n",progname, (long) block_size);r = 3;goto err;}checksum = ucl_adler32(0,NULL,0);/** Step 2: allocate buffer for in-place decompression*/buf_len = block_size + overhead;buf = (ucl_byte *) ucl_malloc(buf_len);if (buf == NULL){printf("%s: out of memory\n", progname);r = 4;goto err;}/** Step 3: process blocks*/for (;;){ucl_byte *in;ucl_byte *out;ucl_uint in_len;ucl_uint out_len;/* read uncompressed size */out_len = xread32(fi);/* exit if last block (EOF marker) */if (out_len == 0)break;/* read compressed size */in_len = xread32(fi);/* sanity check of the size values */if (in_len > block_size || out_len > block_size ||in_len == 0 || in_len > out_len){printf("%s: block size error - data corrupted\n", progname);r = 5;goto err;}/* place compressed block at the top of the buffer */in = buf + buf_len - in_len;out = buf;/* read compressed block data */xread(fi,in,in_len,0);if (in_len < out_len){/* decompress - use safe decompressor as data might be corrupted */ucl_uint new_len = out_len;if (method == 0x2b){if (opt_fast)r = ucl_nrv2b_decompress_8(in,in_len,out,&new_len,NULL);elser = ucl_nrv2b_decompress_safe_8(in,in_len,out,&new_len,NULL);}else if (method == 0x2d){if (opt_fast)r = ucl_nrv2d_decompress_8(in,in_len,out,&new_len,NULL);elser = ucl_nrv2d_decompress_safe_8(in,in_len,out,&new_len,NULL);}else if (method == 0x2e){if (opt_fast)r = ucl_nrv2e_decompress_8(in,in_len,out,&new_len,NULL);elser = ucl_nrv2e_decompress_safe_8(in,in_len,out,&new_len,NULL);}if (r != UCL_E_OK || new_len != out_len){printf("%s: compressed data violation: error %d (0x%x: %ld/%ld/%ld)\n", progname, r, method, (long) in_len, (long) out_len, (long) new_len);r = 6;goto err;}/* write decompressed block */xwrite(fo,out,out_len);/* update checksum */if ((flags & 1) && !opt_fast)checksum = ucl_adler32(checksum,out,out_len);}else{/* write original (incompressible) block */xwrite(fo,in,in_len);/* update checksum */if ((flags & 1) && !opt_fast)checksum = ucl_adler32(checksum,in,in_len);}}/* read and verify checksum */if (flags & 1){ucl_uint32 c = xread32(fi);if (!opt_fast && c != checksum){printf("%s: checksum error - data corrupted\n", progname);r = 7;goto err;}}r = 0;err:ucl_free(buf);return r;}/*************************************************************************//**************************************************************************/static void usage(void){printf("usage: %s [-0123456789] input-file output-file (compress)\n", progname);printf("usage: %s -d input-file output-file (decompress)\n", progname);printf("usage: %s -t input-file... (test)\n", progname);exit(1);}/* open input file */static FILE *xopen_fi(const char *name){FILE *f;f = fopen(name,"rb");if (f == NULL){printf("%s: cannot open input file %s\n", progname, name);exit(1);}#if defined(HAVE_STAT) && defined(S_ISREG){struct stat st;#if defined(HAVE_LSTAT)if (lstat(name,&st) != 0 || !S_ISREG(st.st_mode))#elseif (stat(name,&st) != 0 || !S_ISREG(st.st_mode))#endif{printf("%s: %s is not a regular file\n", progname, name);fclose(f);exit(1);}}#endifreturn f;}/* open output file */static FILE *xopen_fo(const char *name){FILE *f;#if 0/* this is an example program, so make sure we don't overwrite a file */f = fopen(name,"rb");if (f != NULL){printf("%s: file %s already exists -- not overwritten\n", progname, name);fclose(f);exit(1);}#endiff = fopen(name,"wb");if (f == NULL){printf("%s: cannot open output file %s\n", progname, name);exit(1);}return f;}/*************************************************************************//**************************************************************************/int main(int argc, char *argv[]){int i = 1;int r = 0;FILE *fi = NULL;FILE *fo = NULL;const char *in_name = NULL;const char *out_name = NULL;ucl_bool opt_decompress = 0;ucl_bool opt_test = 0;int opt_method = 0x2b;int opt_level = 7;#if defined(MAINT)ucl_uint opt_block_size = (2*1024*1024L);#elseucl_uint opt_block_size = (256*1024L);#endifconst char *s;#if defined(__EMX__)_response(&argc,&argv);_wildcard(&argc,&argv);#endifprogname = argv[0];for (s = progname; *s; s++)if (*s == '/' || *s == '\\')progname = s + 1;printf("\nUCL real-time data compression library (v%s, %s).\n",ucl_version_string(), ucl_version_date());printf("Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer\n\n");printf("*** WARNING ***\n"" This is an example program, do not use to backup your data !\n""\n");/** Step 1: initialize the UCL library*/if (ucl_init() != UCL_E_OK){printf("ucl_init() failed !!!\n");exit(1);}/** Step 2: get options*/while (i < argc && argv[i][0] == '-'){if (strcmp(argv[i],"-d") == 0)opt_decompress = 1;else if (strcmp(argv[i],"-t") == 0)opt_test = 1;else if (strcmp(argv[i],"-F") == 0)opt_fast = 1;else if (strcmp(argv[i],"--2b") == 0)opt_method = 0x2b;else if (strcmp(argv[i],"--nrv2b") == 0)opt_method = 0x2b;else if (strcmp(argv[i],"--2d") == 0)opt_method = 0x2d;else if (strcmp(argv[i],"--nrv2d") == 0)opt_method = 0x2d;else if (strcmp(argv[i],"--2e") == 0)opt_method = 0x2e;else if (strcmp(argv[i],"--nrv2e") == 0)opt_method = 0x2e;else if ((argv[i][1] >= '0' && argv[i][1] <= '9') && !argv[i][2])opt_level = argv[i][1] - '0';else if (strcmp(argv[i],"--10") == 0)opt_level = 10;else if (strcmp(argv[i],"--best") == 0)opt_level = 10;else if (strcmp(argv[i],"--none") == 0)opt_level = 0;else if (argv[i][1] == 'b' && argv[i][2]){#if (UCL_UINT_MAX > UINT_MAX) && defined(HAVE_ATOL)ucl_int b = (ucl_int) atol(&argv[i][2]);#elseucl_int b = (ucl_int) atoi(&argv[i][2]);#endifif (b >= 1024L && b <= 8*1024*1024L)opt_block_size = b;}else if (strcmp(argv[i],"--debug") == 0)opt_debug = 1;elseusage();i++;}if (opt_test && i >= argc)usage();if (!opt_test && i + 2 != argc)usage();/** Step 3: process file(s)*/if (opt_test){while (i < argc && r == 0){in_name = argv[i++];fi = xopen_fi(in_name);r = do_decompress(fi,NULL);if (r == 0)printf("%s: tested ok: %-10s %-11s: %6ld -> %6ld bytes\n",progname, in_name, method_name, total_in, total_out);fclose(fi);fi = NULL;}}else if (opt_decompress){in_name = argv[i++];out_name = argv[i++];fi = xopen_fi(in_name);fo = xopen_fo(out_name);r = do_decompress(fi,fo);if (r == 0)printf("%s: decompressed %ld into %ld bytes\n",progname, total_in, total_out);}else /* compress */{if (!set_method_name(opt_method, opt_level)){printf("%s: internal error - invalid method %d (level %d)\n",progname, opt_method, opt_level);goto quit;}in_name = argv[i++];out_name = argv[i++];fi = xopen_fi(in_name);fo = xopen_fo(out_name);r = do_compress(fi,fo,opt_method,opt_level,opt_block_size);if (r == 0)printf("%s: algorithm %s, compressed %ld into %ld bytes\n",progname, method_name, total_in, total_out);}quit:if (fi) fclose(fi);if (fo) fclose(fo);return r;}/*vi:ts=4:et*/