ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/liblzf/lzf.c
Revision: 1.11
Committed: Sat Jun 23 13:48:22 2007 UTC (16 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.10: +14 -0 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 root 1.11 #if defined(__MINGW32__)
327     _setmode(fd, _O_BINARY);
328     #endif
329 root 1.10 return fd;
330     }
331    
332     static int
333     compose_name (const char *fname, char *oname)
334     {
335     char *p;
336    
337     if (mode == compress)
338     {
339     if (strlen (fname) > PATH_MAX - 4)
340     {
341     fprintf (stderr, "%s: %s.lzf: name too long", imagename, fname);
342     return -1;
343     }
344    
345     strcpy (oname, fname);
346     strcat (oname, ".lzf");
347     }
348     else
349     {
350     if (strlen (fname) > PATH_MAX)
351     {
352     fprintf (stderr, "%s: %s: name too long\n", imagename, fname);
353     return -1;
354     }
355    
356     strcpy (oname, fname);
357     p = &oname[strlen (oname)] - 4;
358     if (p < oname || strcmp (p, ".lzf"))
359     {
360     fprintf (stderr, "%s: %s: unknown suffix\n", imagename, fname);
361     return -1;
362     }
363    
364     *p = 0;
365     }
366    
367     return 0;
368     }
369    
370     static int
371     run_file (const char *fname)
372     {
373     int fd, fd2;
374     int rc;
375     struct stat mystat;
376     char oname[PATH_MAX + 1];
377    
378     if (mode != lzcat)
379     if (compose_name (fname, oname))
380     return -1;
381    
382 root 1.11 #if !defined(__MINGW32__)
383 root 1.10 rc = lstat (fname, &mystat);
384 root 1.11 #else
385     rc = stat (fname, &mystat);
386     #endif
387 root 1.10 fd = open (fname, O_RDONLY);
388 root 1.11 #if defined(__MINGW32__)
389     _setmode(fd, _O_BINARY);
390     #endif
391 root 1.10 if (rc || fd == -1)
392     {
393     fprintf (stderr, "%s: %s: ", imagename, fname);
394     perror ("");
395     return -1;
396     }
397    
398     if (!S_ISREG (mystat.st_mode))
399     {
400     fprintf (stderr, "%s: %s: not a regular file.\n", imagename, fname);
401     close (fd);
402     return -1;
403     }
404    
405     if (mode == lzcat)
406     {
407     rc = uncompress_fd (fd, 1);
408     close (fd);
409     return rc;
410     }
411    
412     fd2 = open_out (oname);
413     if (fd2 == -1)
414     {
415     fprintf (stderr, "%s: %s: ", imagename, oname);
416     perror ("");
417     close (fd);
418     return -1;
419     }
420    
421     if (mode == compress)
422     {
423     rc = compress_fd (fd, fd2);
424     if (!rc && verbose)
425     fprintf (stderr, "%s: %5.1f%% -- replaced with %s\n",
426     fname, nr_read == 0 ? 0 : 100.0 - nr_written / ((double) nr_read / 100.0), oname);
427     }
428     else
429     {
430     rc = uncompress_fd (fd, fd2);
431     if (!rc && verbose)
432     fprintf (stderr, "%s: %5.1f%% -- replaced with %s\n",
433     fname, nr_written == 0 ? 0 : 100.0 - nr_read / ((double) nr_written / 100.0), oname);
434     }
435    
436 root 1.11 #if !defined(__MINGW32__)
437 root 1.10 fchmod (fd2, mystat.st_mode);
438 root 1.11 #else
439     chmod (oname, mystat.st_mode);
440     #endif
441 root 1.10 close (fd);
442     close (fd2);
443    
444     if (!rc)
445     unlink (fname);
446    
447     return rc;
448 root 1.1 }
449    
450     int
451     main (int argc, char *argv[])
452     {
453 root 1.10 char *p = argv[0];
454     int optc;
455     int rc = 0;
456    
457     errno = 0;
458     p = getenv ("LZF_BLOCKSIZE");
459     if (p)
460     {
461     blocksize = strtoul (p, 0, 0);
462     if (errno || !blocksize || blocksize > MAX_BLOCKSIZE)
463     blocksize = BLOCKSIZE;
464     }
465    
466     p = strrchr (argv[0], '/');
467     imagename = p ? ++p : argv[0];
468    
469     if (!strncmp (imagename, "un", 2) || !strncmp (imagename, "de", 2))
470     mode = uncompress;
471    
472     if (strstr (imagename, "cat"))
473     mode = lzcat;
474    
475     #ifdef HAVE_GETOPT_LONG
476     while ((optc = getopt_long (argc, argv, "cdfhvb:", longopts, 0)) != -1)
477     #else
478     while ((optc = getopt (argc, argv, "cdfhvb:")) != -1)
479     #endif
480     {
481     switch (optc)
482     {
483     case 'c':
484     mode = compress;
485     break;
486     case 'd':
487     mode = uncompress;
488     break;
489     case 'f':
490     force = 1;
491     break;
492     case 'h':
493     usage (0);
494     break;
495     case 'v':
496     verbose = 1;
497     break;
498     case 'b':
499     errno = 0;
500     blocksize = strtoul (optarg, 0, 0);
501     if (errno || !blocksize || blocksize > MAX_BLOCKSIZE)
502     blocksize = BLOCKSIZE;
503     break;
504     default:
505     usage (1);
506     break;
507     }
508     }
509    
510     if (optind == argc)
511     { // stdin stdout
512     if (!force)
513     {
514     if ((mode == uncompress || mode == lzcat) && isatty (0))
515     {
516     fprintf (stderr, "%s: compressed data not read from a terminal. Use -f to force decompression.\n", imagename);
517     exit (1);
518     }
519     if (mode == compress && isatty (1))
520     {
521     fprintf (stderr, "%s: compressed data not written to a terminal. Use -f to force compression.\n", imagename);
522     exit (1);
523     }
524     }
525    
526     if (mode == compress)
527     rc = compress_fd (0, 1);
528     else
529     rc = uncompress_fd (0, 1);
530    
531     exit (rc ? 1 : 0);
532     }
533    
534     while (optind < argc)
535     rc |= run_file (argv[optind++]);
536 root 1.1
537 root 1.10 exit (rc ? 1 : 0);
538 root 1.1 }
539 root 1.10