lzopack.c 16 KB

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