1 /* uclpack.c -- example program: a simple file packer
3 This file is part of the UCL data compression library.
5 Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
8 The UCL library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 The UCL library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with the UCL library; see the file COPYING.
20 If not, write to the Free Software Foundation, Inc.,
21 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 Markus F.X.J. Oberhumer
24 <markus@oberhumer.com>
28 /*************************************************************************
29 // NOTE: this is an example program, so do not use to backup your data
31 // This program lacks things like sophisticated file handling but is
32 // pretty complete regarding compression - it should provide a good
33 // starting point for adaption for you applications.
34 **************************************************************************/
39 static const char *progname
= NULL
;
41 static unsigned long total_in
= 0;
42 static unsigned long total_out
= 0;
43 static ucl_bool opt_debug
= 0;
45 /* don't compute or verify checksum, always use fast decompressor */
46 static ucl_bool opt_fast
= 0;
48 /* magic file header for compressed files */
49 static const unsigned char magic
[8] =
50 { 0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a };
53 /*************************************************************************
55 **************************************************************************/
57 ucl_uint
xread(FILE *f
, ucl_voidp buf
, ucl_uint len
, ucl_bool allow_eof
)
61 l
= ucl_fread(f
,buf
,len
);
64 fprintf(stderr
,"\nsomething's wrong with your C library !!!\n");
67 if (l
!= len
&& !allow_eof
)
69 fprintf(stderr
,"\nread error - premature end of file\n");
76 ucl_uint
xwrite(FILE *f
, const ucl_voidp buf
, ucl_uint len
)
82 l
= ucl_fwrite(f
,buf
,len
);
85 fprintf(stderr
,"\nwrite error [%ld %ld] (disk full ?)\n",
98 xread(f
,(ucl_voidp
) &c
,1,0);
102 void xputc(FILE *f
, int c
)
104 unsigned char cc
= (unsigned char) c
;
105 xwrite(f
,(const ucl_voidp
) &cc
,1);
108 /* read and write portable 32-bit integers */
110 ucl_uint32
xread32(FILE *f
)
116 v
= (ucl_uint32
) b
[3] << 0;
117 v
|= (ucl_uint32
) b
[2] << 8;
118 v
|= (ucl_uint32
) b
[1] << 16;
119 v
|= (ucl_uint32
) b
[0] << 24;
123 void xwrite32(FILE *f
, ucl_uint32 v
)
127 b
[3] = (unsigned char) (v
>> 0);
128 b
[2] = (unsigned char) (v
>> 8);
129 b
[1] = (unsigned char) (v
>> 16);
130 b
[0] = (unsigned char) (v
>> 24);
135 /*************************************************************************
137 **************************************************************************/
139 static ucl_uint
get_overhead(int method
, ucl_uint size
)
141 if (method
== 0x2b || method
== 0x2d || method
== 0x2e)
142 return size
/ 8 + 256;
147 static char method_name
[64];
149 static ucl_bool
set_method_name(int method
, int level
)
152 if (level
< 0 || level
> 10)
155 sprintf(method_name
,"uncompressed/%d", level
);
156 else if (method
== 0x2b)
157 sprintf(method_name
,"NRV2B-99/%d", level
);
158 else if (method
== 0x2d)
159 sprintf(method_name
,"NRV2D-99/%d", level
);
160 else if (method
== 0x2e)
161 sprintf(method_name
,"NRV2E-99/%d", level
);
168 /*************************************************************************
170 **************************************************************************/
172 int do_compress(FILE *fi
, FILE *fo
, int method
, int level
, ucl_uint block_size
)
176 ucl_byte
*out
= NULL
;
179 ucl_uint32 flags
= opt_fast
? 0 : 1;
181 ucl_uint overhead
= 0;
183 total_in
= total_out
= 0;
186 * Step 1: write magic header, flags & block size, init checksum
188 xwrite(fo
,magic
,sizeof(magic
));
190 xputc(fo
,method
); /* compression method */
191 xputc(fo
,level
); /* compression level */
192 xwrite32(fo
,block_size
);
193 checksum
= ucl_adler32(0,NULL
,0);
196 * Step 2: allocate compression buffers and work-memory
198 overhead
= get_overhead(method
,block_size
);
199 in
= (ucl_byte
*) ucl_malloc(block_size
);
200 out
= (ucl_byte
*) ucl_malloc(block_size
+ overhead
);
201 if (in
== NULL
|| out
== NULL
)
203 printf("%s: out of memory\n", progname
);
209 * Step 3: process blocks
214 in_len
= xread(fi
,in
,block_size
,1);
218 /* update checksum */
220 checksum
= ucl_adler32(checksum
,in
,in_len
);
226 out_len
= in_len
; /* uncompressed */
229 else if (method
== 0x2b)
230 r
= ucl_nrv2b_99_compress(in
,in_len
,out
,&out_len
,0,level
,NULL
,NULL
);
231 else if (method
== 0x2d)
232 r
= ucl_nrv2d_99_compress(in
,in_len
,out
,&out_len
,0,level
,NULL
,NULL
);
233 else if (method
== 0x2e)
234 r
= ucl_nrv2e_99_compress(in
,in_len
,out
,&out_len
,0,level
,NULL
,NULL
);
235 if (r
!= UCL_E_OK
|| out_len
> in_len
+ get_overhead(method
,in_len
))
237 /* this should NEVER happen */
238 printf("internal error - compression failed: %d\n", r
);
243 /* write uncompressed block size */
246 if (out_len
< in_len
)
248 /* write compressed block */
249 xwrite32(fo
,out_len
);
250 xwrite(fo
,out
,out_len
);
254 /* not compressible - write uncompressed block */
256 xwrite(fo
,in
,in_len
);
260 /* write EOF marker */
265 xwrite32(fo
,checksum
);
275 /*************************************************************************
278 // We are using overlapping (in-place) decompression to save some
279 // memory - see overlap.c.
280 **************************************************************************/
282 int do_decompress(FILE *fi
, FILE *fo
)
285 ucl_byte
*buf
= NULL
;
287 unsigned char m
[ sizeof(magic
) ];
293 ucl_uint overhead
= 0;
295 total_in
= total_out
= 0;
298 * Step 1: check magic header, read flags & block size, init checksum
300 if (xread(fi
,m
,sizeof(magic
),1) != sizeof(magic
) ||
301 memcmp(m
,magic
,sizeof(magic
)) != 0)
303 printf("%s: header error - this file is not compressed by uclpack\n", progname
);
310 block_size
= xread32(fi
);
311 overhead
= get_overhead(method
,block_size
);
312 if (overhead
== 0 || !set_method_name(method
, level
))
314 printf("%s: header error - invalid method %d (level %d)\n",
315 progname
, method
, level
);
319 if (block_size
< 1024 || block_size
> 8*1024*1024L)
321 printf("%s: header error - invalid block size %ld\n",
322 progname
, (long) block_size
);
326 checksum
= ucl_adler32(0,NULL
,0);
329 * Step 2: allocate buffer for in-place decompression
331 buf_len
= block_size
+ overhead
;
332 buf
= (ucl_byte
*) ucl_malloc(buf_len
);
335 printf("%s: out of memory\n", progname
);
341 * Step 3: process blocks
350 /* read uncompressed size */
351 out_len
= xread32(fi
);
353 /* exit if last block (EOF marker) */
357 /* read compressed size */
358 in_len
= xread32(fi
);
360 /* sanity check of the size values */
361 if (in_len
> block_size
|| out_len
> block_size
||
362 in_len
== 0 || in_len
> out_len
)
364 printf("%s: block size error - data corrupted\n", progname
);
369 /* place compressed block at the top of the buffer */
370 in
= buf
+ buf_len
- in_len
;
373 /* read compressed block data */
374 xread(fi
,in
,in_len
,0);
376 if (in_len
< out_len
)
378 /* decompress - use safe decompressor as data might be corrupted */
379 ucl_uint new_len
= out_len
;
384 r
= ucl_nrv2b_decompress_8(in
,in_len
,out
,&new_len
,NULL
);
386 r
= ucl_nrv2b_decompress_safe_8(in
,in_len
,out
,&new_len
,NULL
);
388 else if (method
== 0x2d)
391 r
= ucl_nrv2d_decompress_8(in
,in_len
,out
,&new_len
,NULL
);
393 r
= ucl_nrv2d_decompress_safe_8(in
,in_len
,out
,&new_len
,NULL
);
395 else if (method
== 0x2e)
398 r
= ucl_nrv2e_decompress_8(in
,in_len
,out
,&new_len
,NULL
);
400 r
= ucl_nrv2e_decompress_safe_8(in
,in_len
,out
,&new_len
,NULL
);
402 if (r
!= UCL_E_OK
|| new_len
!= out_len
)
404 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
);
408 /* write decompressed block */
409 xwrite(fo
,out
,out_len
);
410 /* update checksum */
411 if ((flags
& 1) && !opt_fast
)
412 checksum
= ucl_adler32(checksum
,out
,out_len
);
416 /* write original (incompressible) block */
417 xwrite(fo
,in
,in_len
);
418 /* update checksum */
419 if ((flags
& 1) && !opt_fast
)
420 checksum
= ucl_adler32(checksum
,in
,in_len
);
424 /* read and verify checksum */
427 ucl_uint32 c
= xread32(fi
);
428 if (!opt_fast
&& c
!= checksum
)
430 printf("%s: checksum error - data corrupted\n", progname
);
443 /*************************************************************************
445 **************************************************************************/
447 static void usage(void)
449 printf("usage: %s [-0123456789] input-file output-file (compress)\n", progname
);
450 printf("usage: %s -d input-file output-file (decompress)\n", progname
);
451 printf("usage: %s -t input-file... (test)\n", progname
);
456 /* open input file */
457 static FILE *xopen_fi(const char *name
)
461 f
= fopen(name
,"rb");
464 printf("%s: cannot open input file %s\n", progname
, name
);
467 #if defined(HAVE_STAT) && defined(S_ISREG)
470 #if defined(HAVE_LSTAT)
471 if (lstat(name
,&st
) != 0 || !S_ISREG(st
.st_mode
))
473 if (stat(name
,&st
) != 0 || !S_ISREG(st
.st_mode
))
476 printf("%s: %s is not a regular file\n", progname
, name
);
486 /* open output file */
487 static FILE *xopen_fo(const char *name
)
492 /* this is an example program, so make sure we don't overwrite a file */
493 f
= fopen(name
,"rb");
496 printf("%s: file %s already exists -- not overwritten\n", progname
, name
);
501 f
= fopen(name
,"wb");
504 printf("%s: cannot open output file %s\n", progname
, name
);
511 /*************************************************************************
513 **************************************************************************/
515 int main(int argc
, char *argv
[])
521 const char *in_name
= NULL
;
522 const char *out_name
= NULL
;
523 ucl_bool opt_decompress
= 0;
524 ucl_bool opt_test
= 0;
525 int opt_method
= 0x2b;
528 ucl_uint opt_block_size
= (2*1024*1024L);
530 ucl_uint opt_block_size
= (256*1024L);
535 _response(&argc
,&argv
);
536 _wildcard(&argc
,&argv
);
539 for (s
= progname
; *s
; s
++)
540 if (*s
== '/' || *s
== '\\')
543 printf("\nUCL real-time data compression library (v%s, %s).\n",
544 ucl_version_string(), ucl_version_date());
545 printf("Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer\n\n");
549 " This is an example program, do not use to backup your data !\n"
553 * Step 1: initialize the UCL library
555 if (ucl_init() != UCL_E_OK
)
557 printf("ucl_init() failed !!!\n");
562 * Step 2: get options
565 while (i
< argc
&& argv
[i
][0] == '-')
567 if (strcmp(argv
[i
],"-d") == 0)
569 else if (strcmp(argv
[i
],"-t") == 0)
571 else if (strcmp(argv
[i
],"-F") == 0)
573 else if (strcmp(argv
[i
],"--2b") == 0)
575 else if (strcmp(argv
[i
],"--nrv2b") == 0)
577 else if (strcmp(argv
[i
],"--2d") == 0)
579 else if (strcmp(argv
[i
],"--nrv2d") == 0)
581 else if (strcmp(argv
[i
],"--2e") == 0)
583 else if (strcmp(argv
[i
],"--nrv2e") == 0)
585 else if ((argv
[i
][1] >= '0' && argv
[i
][1] <= '9') && !argv
[i
][2])
586 opt_level
= argv
[i
][1] - '0';
587 else if (strcmp(argv
[i
],"--10") == 0)
589 else if (strcmp(argv
[i
],"--best") == 0)
591 else if (strcmp(argv
[i
],"--none") == 0)
593 else if (argv
[i
][1] == 'b' && argv
[i
][2])
595 #if (UCL_UINT_MAX > UINT_MAX) && defined(HAVE_ATOL)
596 ucl_int b
= (ucl_int
) atol(&argv
[i
][2]);
598 ucl_int b
= (ucl_int
) atoi(&argv
[i
][2]);
600 if (b
>= 1024L && b
<= 8*1024*1024L)
603 else if (strcmp(argv
[i
],"--debug") == 0)
609 if (opt_test
&& i
>= argc
)
611 if (!opt_test
&& i
+ 2 != argc
)
615 * Step 3: process file(s)
619 while (i
< argc
&& r
== 0)
622 fi
= xopen_fi(in_name
);
623 r
= do_decompress(fi
,NULL
);
625 printf("%s: tested ok: %-10s %-11s: %6ld -> %6ld bytes\n",
626 progname
, in_name
, method_name
, total_in
, total_out
);
631 else if (opt_decompress
)
634 out_name
= argv
[i
++];
635 fi
= xopen_fi(in_name
);
636 fo
= xopen_fo(out_name
);
637 r
= do_decompress(fi
,fo
);
639 printf("%s: decompressed %ld into %ld bytes\n",
640 progname
, total_in
, total_out
);
644 if (!set_method_name(opt_method
, opt_level
))
646 printf("%s: internal error - invalid method %d (level %d)\n",
647 progname
, opt_method
, opt_level
);
651 out_name
= argv
[i
++];
652 fi
= xopen_fi(in_name
);
653 fo
= xopen_fo(out_name
);
654 r
= do_compress(fi
,fo
,opt_method
,opt_level
,opt_block_size
);
656 printf("%s: algorithm %s, compressed %ld into %ld bytes\n",
657 progname
, method_name
, total_in
, total_out
);