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 56typedef unsigned char u8; 57 58static off_t nr_read, nr_written; 59 60static const char *imagename; 61static enum { compress, uncompress, lzcat } mode = compress; 62static int verbose = 0; 63static int force = 0; 64static 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 98static void 99usage (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 115static inline ssize_t 116rread (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 */ 136static inline ssize_t 137wwrite (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 176static int 177compress_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 218static int 219uncompress_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 309short_read: 310 fprintf (stderr, "%s: short data\n", imagename); 311 return -1; 312} 313 314static int 315open_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 330static int 331compose_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 368static int 369run_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 448int 449main (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 538