ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/liblzf/lzf.c
Revision: 1.13
Committed: Mon Nov 5 23:28:40 2007 UTC (16 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-3_6, rel-3_5, rel-3_4, rel-3_3, rel-3_1, rel-3_0
Changes since 1.12: +9 -8 lines
Log Message:
*** empty log message ***

File Contents

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