1/* lzopack.c -- LZO example program: a simple file packer 2 3 This file is part of the LZO real-time data compression library. 4 5 Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer 6 Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer 7 Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer 8 Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer 9 Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer 10 Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer 11 Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer 12 Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer 13 Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer 14 Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer 15 Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer 16 Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer 17 Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer 18 Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer 19 Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer 20 Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer 21 All Rights Reserved. 22 23 The LZO library is free software; you can redistribute it and/or 24 modify it under the terms of the GNU General Public License as 25 published by the Free Software Foundation; either version 2 of 26 the License, or (at your option) any later version. 27 28 The LZO library is distributed in the hope that it will be useful, 29 but WITHOUT ANY WARRANTY; without even the implied warranty of 30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 GNU General Public License for more details. 32 33 You should have received a copy of the GNU General Public License 34 along with the LZO library; see the file COPYING. 35 If not, write to the Free Software Foundation, Inc., 36 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 37 38 Markus F.X.J. Oberhumer 39 <markus@oberhumer.com> 40 http://www.oberhumer.com/opensource/lzo/ 41 */ 42 43 44/************************************************************************* 45// NOTE: this is an example program, so do not use to backup your data. 46// 47// This program lacks things like sophisticated file handling but is 48// pretty complete regarding compression - it should provide a good 49// starting point for adaption for your applications. 50// 51// Please study LZO.FAQ and simple.c first. 52**************************************************************************/ 53 54#include "lzo/lzoconf.h" 55#include "lzo/lzo1x.h" 56 57/* portability layer */ 58static const char *progname = NULL; 59#define WANT_LZO_MALLOC 1 60#define WANT_LZO_FREAD 1 61#define WANT_LZO_WILDARGV 1 62#define WANT_XMALLOC 1 63#include "examples/portab.h" 64 65 66static unsigned long total_in = 0; 67static unsigned long total_out = 0; 68static lzo_bool opt_debug = 0; 69 70/* magic file header for lzopack-compressed files */ 71static const unsigned char magic[7] = 72 { 0x00, 0xe9, 0x4c, 0x5a, 0x4f, 0xff, 0x1a }; 73 74 75/************************************************************************* 76// file IO 77**************************************************************************/ 78 79lzo_uint xread(FILE *fp, lzo_voidp buf, lzo_uint len, lzo_bool allow_eof) 80{ 81 lzo_uint l; 82 83 l = (lzo_uint) lzo_fread(fp, buf, len); 84 if (l > len) 85 { 86 fprintf(stderr, "\nsomething's wrong with your C library !!!\n"); 87 exit(1); 88 } 89 if (l != len && !allow_eof) 90 { 91 fprintf(stderr, "\nread error - premature end of file\n"); 92 exit(1); 93 } 94 total_in += (unsigned long) l; 95 return l; 96} 97 98lzo_uint xwrite(FILE *fp, const lzo_voidp buf, lzo_uint len) 99{ 100 if (fp != NULL && lzo_fwrite(fp, buf, len) != len) 101 { 102 fprintf(stderr, "\nwrite error (disk full ?)\n"); 103 exit(1); 104 } 105 total_out += (unsigned long) len; 106 return len; 107} 108 109 110int xgetc(FILE *fp) 111{ 112 unsigned char c; 113 xread(fp, (lzo_voidp) &c, 1, 0); 114 return c; 115} 116 117void xputc(FILE *fp, int c) 118{ 119 unsigned char cc = (unsigned char) (c & 0xff); 120 xwrite(fp, (const lzo_voidp) &cc, 1); 121} 122 123/* read and write portable 32-bit integers */ 124 125lzo_uint32 xread32(FILE *fp) 126{ 127 unsigned char b[4]; 128 lzo_uint32 v; 129 130 xread(fp, b, 4, 0); 131 v = (lzo_uint32) b[3] << 0; 132 v |= (lzo_uint32) b[2] << 8; 133 v |= (lzo_uint32) b[1] << 16; 134 v |= (lzo_uint32) b[0] << 24; 135 return v; 136} 137 138void xwrite32(FILE *fp, lzo_xint v) 139{ 140 unsigned char b[4]; 141 142 b[3] = (unsigned char) ((v >> 0) & 0xff); 143 b[2] = (unsigned char) ((v >> 8) & 0xff); 144 b[1] = (unsigned char) ((v >> 16) & 0xff); 145 b[0] = (unsigned char) ((v >> 24) & 0xff); 146 xwrite(fp, b, 4); 147} 148 149 150/************************************************************************* 151// compress 152// 153// possible improvement: we could use overlapping compression to 154// save some memory - see overlap.c. This would require some minor 155// changes in the decompression code as well, because if a block 156// turns out to be incompressible we would still have to store it in its 157// "compressed" (i.e. then slightly enlarged) form because the original 158// (uncompressed) data would have been lost during the overlapping 159// compression. 160**************************************************************************/ 161 162int do_compress(FILE *fi, FILE *fo, int compression_level, lzo_uint block_size) 163{ 164 int r = 0; 165 lzo_bytep in = NULL; 166 lzo_bytep out = NULL; 167 lzo_voidp wrkmem = NULL; 168 lzo_uint in_len; 169 lzo_uint out_len; 170 lzo_uint32 wrk_len = 0; 171 lzo_uint32 flags = 1; /* do compute a checksum */ 172 int method = 1; /* compression method: LZO1X */ 173 lzo_uint32 checksum; 174 175 total_in = total_out = 0; 176 177/* 178 * Step 1: write magic header, flags & block size, init checksum 179 */ 180 xwrite(fo, magic, sizeof(magic)); 181 xwrite32(fo, flags); 182 xputc(fo, method); /* compression method */ 183 xputc(fo, compression_level); /* compression level */ 184 xwrite32(fo, block_size); 185 checksum = lzo_adler32(0, NULL, 0); 186 187/* 188 * Step 2: allocate compression buffers and work-memory 189 */ 190 in = (lzo_bytep) xmalloc(block_size); 191 out = (lzo_bytep) xmalloc(block_size + block_size / 16 + 64 + 3); 192 if (compression_level == 9) 193 wrk_len = LZO1X_999_MEM_COMPRESS; 194 else 195 wrk_len = LZO1X_1_MEM_COMPRESS; 196 wrkmem = (lzo_voidp) xmalloc(wrk_len); 197 if (in == NULL || out == NULL || wrkmem == NULL) 198 { 199 printf("%s: out of memory\n", progname); 200 r = 1; 201 goto err; 202 } 203 204/* 205 * Step 3: process blocks 206 */ 207 for (;;) 208 { 209 /* read block */ 210 in_len = xread(fi, in, block_size, 1); 211 if (in_len == 0) 212 break; 213 214 /* update checksum */ 215 if (flags & 1) 216 checksum = lzo_adler32(checksum, in, in_len); 217 218 /* clear wrkmem (not needed, only for debug/benchmark purposes) */ 219 if (opt_debug) 220 lzo_memset(wrkmem, 0xff, wrk_len); 221 222 /* compress block */ 223 if (compression_level == 9) 224 r = lzo1x_999_compress(in, in_len, out, &out_len, wrkmem); 225 else 226 r = lzo1x_1_compress(in, in_len, out, &out_len, wrkmem); 227 if (r != LZO_E_OK || out_len > in_len + in_len / 16 + 64 + 3) 228 { 229 /* this should NEVER happen */ 230 printf("internal error - compression failed: %d\n", r); 231 r = 2; 232 goto err; 233 } 234 235 /* write uncompressed block size */ 236 xwrite32(fo, in_len); 237 238 if (out_len < in_len) 239 { 240 /* write compressed block */ 241 xwrite32(fo, out_len); 242 xwrite(fo, out, out_len); 243 } 244 else 245 { 246 /* not compressible - write uncompressed block */ 247 xwrite32(fo, in_len); 248 xwrite(fo, in, in_len); 249 } 250 } 251 252 /* write EOF marker */ 253 xwrite32(fo, 0); 254 255 /* write checksum */ 256 if (flags & 1) 257 xwrite32(fo, checksum); 258 259 r = 0; 260err: 261 lzo_free(wrkmem); 262 lzo_free(out); 263 lzo_free(in); 264 return r; 265} 266 267 268/************************************************************************* 269// decompress / test 270// 271// We are using overlapping (in-place) decompression to save some 272// memory - see overlap.c. 273**************************************************************************/ 274 275int do_decompress(FILE *fi, FILE *fo) 276{ 277 int r = 0; 278 lzo_bytep buf = NULL; 279 lzo_uint buf_len; 280 unsigned char m [ sizeof(magic) ]; 281 lzo_uint32 flags; 282 int method; 283 int compression_level; 284 lzo_uint block_size; 285 lzo_uint32 checksum; 286 287 total_in = total_out = 0; 288 289/* 290 * Step 1: check magic header, read flags & block size, init checksum 291 */ 292 if (xread(fi, m, sizeof(magic),1) != sizeof(magic) || 293 memcmp(m, magic, sizeof(magic)) != 0) 294 { 295 printf("%s: header error - this file is not compressed by lzopack\n", progname); 296 r = 1; 297 goto err; 298 } 299 flags = xread32(fi); 300 method = xgetc(fi); 301 compression_level = xgetc(fi); 302 if (method != 1) 303 { 304 printf("%s: header error - invalid method %d (level %d)\n", 305 progname, method, compression_level); 306 r = 2; 307 goto err; 308 } 309 block_size = xread32(fi); 310 if (block_size < 1024 || block_size > 8*1024*1024L) 311 { 312 printf("%s: header error - invalid block size %ld\n", 313 progname, (long) block_size); 314 r = 3; 315 goto err; 316 } 317 checksum = lzo_adler32(0,NULL,0); 318 319/* 320 * Step 2: allocate buffer for in-place decompression 321 */ 322 buf_len = block_size + block_size / 16 + 64 + 3; 323 buf = (lzo_bytep) xmalloc(buf_len); 324 if (buf == NULL) 325 { 326 printf("%s: out of memory\n", progname); 327 r = 4; 328 goto err; 329 } 330 331/* 332 * Step 3: process blocks 333 */ 334 for (;;) 335 { 336 lzo_bytep in; 337 lzo_bytep out; 338 lzo_uint in_len; 339 lzo_uint out_len; 340 341 /* read uncompressed size */ 342 out_len = xread32(fi); 343 344 /* exit if last block (EOF marker) */ 345 if (out_len == 0) 346 break; 347 348 /* read compressed size */ 349 in_len = xread32(fi); 350 351 /* sanity check of the size values */ 352 if (in_len > block_size || out_len > block_size || 353 in_len == 0 || in_len > out_len) 354 { 355 printf("%s: block size error - data corrupted\n", progname); 356 r = 5; 357 goto err; 358 } 359 360 /* place compressed block at the top of the buffer */ 361 in = buf + buf_len - in_len; 362 out = buf; 363 364 /* read compressed block data */ 365 xread(fi, in, in_len, 0); 366 367 if (in_len < out_len) 368 { 369 /* decompress - use safe decompressor as data might be corrupted 370 * during a file transfer */ 371 lzo_uint new_len = out_len; 372 373 r = lzo1x_decompress_safe(in, in_len, out, &new_len, NULL); 374 if (r != LZO_E_OK || new_len != out_len) 375 { 376 printf("%s: compressed data violation\n", progname); 377 r = 6; 378 goto err; 379 } 380 /* write decompressed block */ 381 xwrite(fo, out, out_len); 382 /* update checksum */ 383 if (flags & 1) 384 checksum = lzo_adler32(checksum, out, out_len); 385 } 386 else 387 { 388 /* write original (incompressible) block */ 389 xwrite(fo, in, in_len); 390 /* update checksum */ 391 if (flags & 1) 392 checksum = lzo_adler32(checksum, in, in_len); 393 } 394 } 395 396 /* read and verify checksum */ 397 if (flags & 1) 398 { 399 lzo_uint32 c = xread32(fi); 400 if (c != checksum) 401 { 402 printf("%s: checksum error - data corrupted\n", progname); 403 r = 7; 404 goto err; 405 } 406 } 407 408 r = 0; 409err: 410 lzo_free(buf); 411 return r; 412} 413 414 415/************************************************************************* 416// 417**************************************************************************/ 418 419static void usage(void) 420{ 421 printf("usage: %s [-9] input-file output-file (compress)\n", progname); 422 printf("usage: %s -d input-file output-file (decompress)\n", progname); 423 printf("usage: %s -t input-file... (test)\n", progname); 424 exit(1); 425} 426 427 428/* open input file */ 429static FILE *xopen_fi(const char *name) 430{ 431 FILE *fp; 432 433 fp = fopen(name, "rb"); 434 if (fp == NULL) 435 { 436 printf("%s: cannot open input file %s\n", progname, name); 437 exit(1); 438 } 439#if defined(HAVE_STAT) && defined(S_ISREG) 440 { 441 struct stat st; 442 int is_regular = 1; 443 if (stat(name, &st) != 0 || !S_ISREG(st.st_mode)) 444 is_regular = 0; 445 if (!is_regular) 446 { 447 printf("%s: %s is not a regular file\n", progname, name); 448 fclose(fp); fp = NULL; 449 exit(1); 450 } 451 } 452#endif 453 return fp; 454} 455 456 457/* open output file */ 458static FILE *xopen_fo(const char *name) 459{ 460 FILE *fp; 461 462#if 0 463 /* this is an example program, so make sure we don't overwrite a file */ 464 fp = fopen(name, "rb"); 465 if (fp != NULL) 466 { 467 printf("%s: file %s already exists -- not overwritten\n", progname, name); 468 fclose(fp); fp = NULL; 469 exit(1); 470 } 471#endif 472 fp = fopen(name, "wb"); 473 if (fp == NULL) 474 { 475 printf("%s: cannot open output file %s\n", progname, name); 476 exit(1); 477 } 478 return fp; 479} 480 481 482/* close file */ 483static void xclose(FILE *fp) 484{ 485 if (fp) 486 { 487 int err; 488 err = ferror(fp); 489 if (fclose(fp) != 0) 490 err = 1; 491 if (err) 492 { 493 printf("%s: error while closing file\n", progname); 494 exit(1); 495 } 496 } 497} 498 499 500/************************************************************************* 501// 502**************************************************************************/ 503 504int __lzo_cdecl_main main(int argc, char *argv[]) 505{ 506 int i = 1; 507 int r = 0; 508 FILE *fi = NULL; 509 FILE *fo = NULL; 510 const char *in_name = NULL; 511 const char *out_name = NULL; 512 unsigned opt_decompress = 0; 513 unsigned opt_test = 0; 514 int opt_compression_level = 1; 515 lzo_uint opt_block_size; 516 const char *s; 517 518 lzo_wildargv(&argc, &argv); 519 520 progname = argv[0]; 521 for (s = progname; *s; s++) 522 if ((*s == '/' || *s == '\\') && s[1]) 523 progname = s + 1; 524 525 printf("\nLZO real-time data compression library (v%s, %s).\n", 526 lzo_version_string(), lzo_version_date()); 527 printf("Copyright (C) 1996-2011 Markus Franz Xaver Johannes Oberhumer\nAll Rights Reserved.\n\n"); 528 529#if 0 530 printf( 531"*** DISCLAIMER ***\n" 532" This is an example program, do not use to backup your data !\n" 533" Get LZOP if you're interested into a full-featured packer.\n" 534" See http://www.oberhumer.com/opensource/lzop/\n" 535"\n"); 536#endif 537 538 539/* 540 * Step 1: initialize the LZO library 541 */ 542 if (lzo_init() != LZO_E_OK) 543 { 544 printf("internal error - lzo_init() failed !!!\n"); 545 printf("(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable '-DLZO_DEBUG' for diagnostics)\n"); 546 exit(1); 547 } 548 549 550/* 551 * Step 2: setup memory 552 */ 553 opt_block_size = 256 * 1024L; 554 555#if defined(ACC_MM_AHSHIFT) 556 /* reduce memory requirements for ancient 16-bit DOS 640kB real-mode */ 557 if (ACC_MM_AHSHIFT != 3) 558 opt_block_size = 16 * 1024L; 559#endif 560 561 562/* 563 * Step 3: get options 564 */ 565 566 while (i < argc && argv[i][0] == '-') 567 { 568 if (strcmp(argv[i],"-d") == 0) 569 opt_decompress = 1; 570 else if (strcmp(argv[i],"-t") == 0) 571 opt_test = 1; 572 else if (strcmp(argv[i],"-9") == 0) 573 opt_compression_level = 9; 574 else if (argv[i][1] == 'b' && argv[i][2]) 575 { 576 long b = atol(&argv[i][2]); 577 if (b >= 1024L && b <= 8*1024*1024L) 578 opt_block_size = (lzo_uint) b; 579 else 580 { 581 printf("%s: invalid block_size in option '%s'.\n", progname, argv[i]); 582 usage(); 583 } 584 } 585 else if (strcmp(argv[i],"--debug") == 0) 586 opt_debug += 1; 587 else 588 usage(); 589 i++; 590 } 591 if (opt_test && i >= argc) 592 usage(); 593 if (!opt_test && i + 2 != argc) 594 usage(); 595 596 597/* 598 * Step 4: process file(s) 599 */ 600 601 if (opt_test) 602 { 603 while (i < argc && r == 0) 604 { 605 in_name = argv[i++]; 606 fi = xopen_fi(in_name); 607 r = do_decompress(fi, NULL); 608 if (r == 0) 609 printf("%s: %s tested ok (%lu -> %lu bytes)\n", 610 progname, in_name, total_in, total_out); 611 xclose(fi); fi = NULL; 612 } 613 } 614 else if (opt_decompress) 615 { 616 in_name = argv[i++]; 617 out_name = argv[i++]; 618 fi = xopen_fi(in_name); 619 fo = xopen_fo(out_name); 620 r = do_decompress(fi, fo); 621 if (r == 0) 622 printf("%s: decompressed %lu into %lu bytes\n", 623 progname, total_in, total_out); 624 } 625 else /* compress */ 626 { 627 in_name = argv[i++]; 628 out_name = argv[i++]; 629 fi = xopen_fi(in_name); 630 fo = xopen_fo(out_name); 631 r = do_compress(fi, fo, opt_compression_level, opt_block_size); 632 if (r == 0) 633 printf("%s: compressed %lu into %lu bytes\n", 634 progname, total_in, total_out); 635 } 636 637 xclose(fi); fi = NULL; 638 xclose(fo); fo = NULL; 639 return r; 640} 641 642/* 643vi:ts=4:et 644*/ 645 646