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