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