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