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