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