ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/liblzf/lzf.c
Revision: 1.10
Committed: Thu Oct 12 14:38:24 2006 UTC (18 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_0
Changes since 1.9: +453 -191 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /*
2 root 1.10 * Copyright (c) 2006 Stefan Traby <stefan@hello-penguin.com>
3 root 1.1 *
4     * Redistribution and use in source and binary forms, with or without modifica-
5     * tion, are permitted provided that the following conditions are met:
6     *
7     * 1. Redistributions of source code must retain the above copyright notice,
8     * this list of conditions and the following disclaimer.
9     *
10     * 2. Redistributions in binary form must reproduce the above copyright
11     * notice, this list of conditions and the following disclaimer in the
12     * documentation and/or other materials provided with the distribution.
13     *
14     * 3. The name of the author may not be used to endorse or promote products
15     * derived from this software without specific prior written permission.
16     *
17     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
19     * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20     * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
21     * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
25     * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
26     * OF THE POSSIBILITY OF SUCH DAMAGE.
27 pcg 1.4 *
28     * Alternatively, the contents of this file may be used under the terms of
29     * the GNU General Public License version 2 (the "GPL"), in which case the
30     * provisions of the GPL are applicable instead of the above. If you wish to
31     * allow the use of your version of this file only under the terms of the
32     * GPL and not to allow others to use your version of this file under the
33     * BSD license, indicate your decision by deleting the provisions above and
34     * replace them with the notice and other provisions required by the GPL. If
35     * you do not delete the provisions above, a recipient may use your version
36 root 1.10 * of this file under either the BSD or the GPL License.
37 root 1.1 */
38    
39     #include "config.h"
40     #include <stdio.h>
41 root 1.10 #include <string.h>
42 root 1.1 #include <stdlib.h>
43 root 1.10 #include <unistd.h>
44     #include <sys/types.h>
45     #include <sys/stat.h>
46     #include <fcntl.h>
47     #include <errno.h>
48     #include <limits.h>
49     #include "lzf.h"
50 root 1.1
51 root 1.10 #ifdef HAVE_GETOPT_H
52     # include <getopt.h>
53     #endif
54 root 1.1
55 root 1.10 #define BLOCKSIZE (1024 * 64 - 1)
56     #define MAX_BLOCKSIZE BLOCKSIZE
57 root 1.1
58 pcg 1.3 typedef unsigned char u8;
59    
60 root 1.10 static off_t nr_read, nr_written;
61    
62     static const char *imagename;
63     static enum { compress, uncompress, lzcat } mode = compress;
64     static int verbose = 0;
65     static int force = 0;
66     static long blocksize = BLOCKSIZE;
67    
68     #ifdef HAVE_GETOPT_LONG
69    
70     struct option longopts[] = {
71     {"compress", 0, 0, 'c'},
72     {"decompress", 0, 0, 'd'},
73     {"uncompress", 0, 0, 'd'},
74     {"force", 0, 0, 'f'},
75     {"help", 0, 0, 'h'},
76     {"verbose", 0, 0, 'v'},
77     {"blocksize", 1, 0, 'b'},
78     {0, 0, 0, 0}
79     };
80    
81     static const char *opt =
82     "-c --compress compress\n"
83     "-d --decompress decompress\n"
84     "-f --force force overwrite of output file\n"
85     "-h --help give this help\n" "-v --verbose verbose mode\n" "-b # --blocksize # set blocksize\n" "\n";
86    
87     #else
88    
89     static const char *opt =
90     "-c compress\n"
91     "-d decompress\n"
92     "-f force overwrite of output file\n"
93     "-h give this help\n"
94     "-v verbose mode\n"
95     "-b # set blocksize\n"
96     "\n";
97    
98     #endif
99    
100 root 1.1 static void
101 root 1.10 usage (int rc)
102 root 1.1 {
103     fprintf (stderr, "\n"
104 root 1.10 "lzf, a very lightweight compression/decompression utility written by Stefan Traby.\n"
105     "uses liblzf written by Marc Lehmann <schmorp@schmorp.de> You can find more info at\n"
106 pcg 1.3 "http://liblzf.plan9.de/\n"
107 root 1.1 "\n"
108 root 1.10 "usage: lzf [-dufhvb] [file ...]\n"
109     " unlzf [file ...]\n"
110     " lzcat [file ...]\n"
111     "\n%s",
112     opt);
113    
114     exit (rc);
115     }
116    
117     static inline ssize_t
118     rread (int fd, void *buf, size_t len)
119     {
120     ssize_t rc = 0, offset = 0;
121     char *p = buf;
122    
123     while (len && (rc = read (fd, &p[offset], len)) > 0)
124     {
125     offset += rc;
126     len -= rc;
127     }
128    
129     nr_read += offset;
130    
131     if (rc < 0)
132     return rc;
133    
134     return offset;
135     }
136    
137     /* returns 0 if all written else -1 */
138     static inline ssize_t
139     wwrite (int fd, void *buf, size_t len)
140     {
141     ssize_t rc;
142     char *b = buf;
143     size_t l = len;
144    
145     while (l)
146     {
147     rc = write (fd, b, l);
148     if (rc < 0)
149     {
150     fprintf (stderr, "%s: write error: ", imagename);
151     perror ("");
152     return -1;
153     }
154    
155     l -= rc;
156     b += rc;
157     }
158 root 1.1
159 root 1.10 nr_written += len;
160     return 0;
161 root 1.1 }
162    
163     /*
164     * Anatomy: an lzf file consists of any number of blocks in the following format:
165     *
166 root 1.8 * \x00 EOF (optional)
167 root 1.1 * "ZV\0" 2-byte-usize <uncompressed data>
168     * "ZV\1" 2-byte-csize 2-byte-usize <compressed data>
169     * "ZV\2" 4-byte-crc32-0xdebb20e3 (NYI)
170     */
171    
172 root 1.10
173     #define TYPE0_HDR_SIZE 5
174     #define TYPE1_HDR_SIZE 7
175     #define MAX_HDR_SIZE 7
176     #define MIN_HDR_SIZE 5
177    
178     static int
179     compress_fd (int from, int to)
180 root 1.1 {
181 root 1.10 ssize_t us, cs, len;
182     u8 buf1[MAX_BLOCKSIZE + MAX_HDR_SIZE + 16];
183     u8 buf2[MAX_BLOCKSIZE + MAX_HDR_SIZE + 16];
184     u8 *header;
185    
186     nr_read = nr_written = 0;
187     while ((us = rread (from, &buf1[MAX_HDR_SIZE], blocksize)) > 0)
188     {
189     cs = lzf_compress (&buf1[MAX_HDR_SIZE], us, &buf2[MAX_HDR_SIZE], us > 4 ? us - 4 : us);
190     if (cs)
191     {
192     header = &buf2[MAX_HDR_SIZE - TYPE1_HDR_SIZE];
193     header[0] = 'Z';
194     header[1] = 'V';
195     header[2] = 1;
196     header[3] = cs >> 8;
197     header[4] = cs & 0xff;
198     header[5] = us >> 8;
199     header[6] = us & 0xff;
200     len = cs + TYPE1_HDR_SIZE;
201     }
202     else
203     { // write uncompressed
204     header = &buf1[MAX_HDR_SIZE - TYPE0_HDR_SIZE];
205     header[0] = 'Z';
206     header[1] = 'V';
207     header[2] = 0;
208     header[3] = us >> 8;
209     header[4] = us & 0xff;
210     len = us + TYPE0_HDR_SIZE;
211     }
212    
213     if (wwrite (to, header, len) == -1)
214     return -1;
215     }
216    
217     return 0;
218 root 1.1 }
219    
220 root 1.10 static int
221     uncompress_fd (int from, int to)
222 root 1.1 {
223 root 1.10 u8 header[MAX_HDR_SIZE];
224     u8 buf1[MAX_BLOCKSIZE + MAX_HDR_SIZE + 16];
225     u8 buf2[MAX_BLOCKSIZE + MAX_HDR_SIZE + 16];
226     u8 *p;
227     int l, rd;
228     ssize_t rc, cs, us, bytes, over = 0;
229    
230     nr_read = nr_written = 0;
231     while (1)
232     {
233     rc = rread (from, header + over, MAX_HDR_SIZE - over);
234     if (rc < 0)
235     {
236     fprintf (stderr, "%s: read error: ", imagename);
237     perror ("");
238     return -1;
239     }
240    
241     rc += over;
242     over = 0;
243     if (!rc || header[0] == 0)
244     return 0;
245    
246     if (rc < MIN_HDR_SIZE || header[0] != 'Z' || header[1] != 'V')
247     {
248     fprintf (stderr, "%s: invalid data stream - magic not found or short header\n", imagename);
249     return -1;
250     }
251    
252     switch (header[2])
253     {
254     case 0:
255     cs = -1;
256     us = (header[3] << 8) | header[4];
257     p = &header[TYPE0_HDR_SIZE];
258     break;
259     case 1:
260     if (rc < TYPE1_HDR_SIZE)
261     {
262     goto short_read;
263     }
264     cs = (header[3] << 8) | header[4];
265     us = (header[5] << 8) | header[6];
266     p = &header[TYPE1_HDR_SIZE];
267     break;
268     default:
269     fprintf (stderr, "%s: unknown blocktype\n", imagename);
270     return -1;
271     }
272    
273     bytes = cs == -1 ? us : cs;
274     l = &header[rc] - p;
275    
276     if (l > 0)
277     memcpy (buf1, p, l);
278    
279     if (l > bytes)
280     {
281     over = l - bytes;
282     memmove (header, &p[bytes], over);
283     }
284    
285     p = &buf1[l];
286     rd = bytes - l;
287     if (rd > 0)
288     if ((rc = rread (from, p, rd)) != rd)
289     goto short_read;
290    
291     if (cs == -1)
292     {
293     if (wwrite (to, buf1, us))
294     return -1;
295     }
296     else
297     {
298     if (lzf_decompress (buf1, cs, buf2, us) != us)
299     {
300     fprintf (stderr, "%s: decompress: invalid stream - data corrupted\n", imagename);
301     return -1;
302     }
303    
304     if (wwrite (to, buf2, us))
305     return -1;
306     }
307     }
308    
309     return 0;
310    
311     short_read:
312     fprintf (stderr, "%s: short data\n", imagename);
313     return -1;
314     }
315    
316     static int
317     open_out (const char *name)
318     {
319     int fd;
320     int m = O_EXCL;
321    
322     if (force)
323     m = 0;
324    
325     fd = open (name, O_CREAT | O_WRONLY | O_TRUNC | m, 600);
326     return fd;
327     }
328    
329     static int
330     compose_name (const char *fname, char *oname)
331     {
332     char *p;
333    
334     if (mode == compress)
335     {
336     if (strlen (fname) > PATH_MAX - 4)
337     {
338     fprintf (stderr, "%s: %s.lzf: name too long", imagename, fname);
339     return -1;
340     }
341    
342     strcpy (oname, fname);
343     strcat (oname, ".lzf");
344     }
345     else
346     {
347     if (strlen (fname) > PATH_MAX)
348     {
349     fprintf (stderr, "%s: %s: name too long\n", imagename, fname);
350     return -1;
351     }
352    
353     strcpy (oname, fname);
354     p = &oname[strlen (oname)] - 4;
355     if (p < oname || strcmp (p, ".lzf"))
356     {
357     fprintf (stderr, "%s: %s: unknown suffix\n", imagename, fname);
358     return -1;
359     }
360    
361     *p = 0;
362     }
363    
364     return 0;
365     }
366    
367     static int
368     run_file (const char *fname)
369     {
370     int fd, fd2;
371     int rc;
372     struct stat mystat;
373     char oname[PATH_MAX + 1];
374    
375     if (mode != lzcat)
376     if (compose_name (fname, oname))
377     return -1;
378    
379     rc = lstat (fname, &mystat);
380     fd = open (fname, O_RDONLY);
381     if (rc || fd == -1)
382     {
383     fprintf (stderr, "%s: %s: ", imagename, fname);
384     perror ("");
385     return -1;
386     }
387    
388     if (!S_ISREG (mystat.st_mode))
389     {
390     fprintf (stderr, "%s: %s: not a regular file.\n", imagename, fname);
391     close (fd);
392     return -1;
393     }
394    
395     if (mode == lzcat)
396     {
397     rc = uncompress_fd (fd, 1);
398     close (fd);
399     return rc;
400     }
401    
402     fd2 = open_out (oname);
403     if (fd2 == -1)
404     {
405     fprintf (stderr, "%s: %s: ", imagename, oname);
406     perror ("");
407     close (fd);
408     return -1;
409     }
410    
411     if (mode == compress)
412     {
413     rc = compress_fd (fd, fd2);
414     if (!rc && verbose)
415     fprintf (stderr, "%s: %5.1f%% -- replaced with %s\n",
416     fname, nr_read == 0 ? 0 : 100.0 - nr_written / ((double) nr_read / 100.0), oname);
417     }
418     else
419     {
420     rc = uncompress_fd (fd, fd2);
421     if (!rc && verbose)
422     fprintf (stderr, "%s: %5.1f%% -- replaced with %s\n",
423     fname, nr_written == 0 ? 0 : 100.0 - nr_read / ((double) nr_written / 100.0), oname);
424     }
425    
426     fchmod (fd2, mystat.st_mode);
427     close (fd);
428     close (fd2);
429    
430     if (!rc)
431     unlink (fname);
432    
433     return rc;
434 root 1.1 }
435    
436     int
437     main (int argc, char *argv[])
438     {
439 root 1.10 char *p = argv[0];
440     int optc;
441     int rc = 0;
442    
443     errno = 0;
444     p = getenv ("LZF_BLOCKSIZE");
445     if (p)
446     {
447     blocksize = strtoul (p, 0, 0);
448     if (errno || !blocksize || blocksize > MAX_BLOCKSIZE)
449     blocksize = BLOCKSIZE;
450     }
451    
452     p = strrchr (argv[0], '/');
453     imagename = p ? ++p : argv[0];
454    
455     if (!strncmp (imagename, "un", 2) || !strncmp (imagename, "de", 2))
456     mode = uncompress;
457    
458     if (strstr (imagename, "cat"))
459     mode = lzcat;
460    
461     #ifdef HAVE_GETOPT_LONG
462     while ((optc = getopt_long (argc, argv, "cdfhvb:", longopts, 0)) != -1)
463     #else
464     while ((optc = getopt (argc, argv, "cdfhvb:")) != -1)
465     #endif
466     {
467     switch (optc)
468     {
469     case 'c':
470     mode = compress;
471     break;
472     case 'd':
473     mode = uncompress;
474     break;
475     case 'f':
476     force = 1;
477     break;
478     case 'h':
479     usage (0);
480     break;
481     case 'v':
482     verbose = 1;
483     break;
484     case 'b':
485     errno = 0;
486     blocksize = strtoul (optarg, 0, 0);
487     if (errno || !blocksize || blocksize > MAX_BLOCKSIZE)
488     blocksize = BLOCKSIZE;
489     break;
490     default:
491     usage (1);
492     break;
493     }
494     }
495    
496     if (optind == argc)
497     { // stdin stdout
498     if (!force)
499     {
500     if ((mode == uncompress || mode == lzcat) && isatty (0))
501     {
502     fprintf (stderr, "%s: compressed data not read from a terminal. Use -f to force decompression.\n", imagename);
503     exit (1);
504     }
505     if (mode == compress && isatty (1))
506     {
507     fprintf (stderr, "%s: compressed data not written to a terminal. Use -f to force compression.\n", imagename);
508     exit (1);
509     }
510     }
511    
512     if (mode == compress)
513     rc = compress_fd (0, 1);
514     else
515     rc = uncompress_fd (0, 1);
516    
517     exit (rc ? 1 : 0);
518     }
519    
520     while (optind < argc)
521     rc |= run_file (argv[optind++]);
522 root 1.1
523 root 1.10 exit (rc ? 1 : 0);
524 root 1.1 }
525 root 1.10