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