Home | History | Annotate | Download | only in zopfli
      1 /*
      2 Copyright 2011 Google Inc. All Rights Reserved.
      3 
      4 Licensed under the Apache License, Version 2.0 (the "License");
      5 you may not use this file except in compliance with the License.
      6 You may obtain a copy of the License at
      7 
      8     http://www.apache.org/licenses/LICENSE-2.0
      9 
     10 Unless required by applicable law or agreed to in writing, software
     11 distributed under the License is distributed on an "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 See the License for the specific language governing permissions and
     14 limitations under the License.
     15 
     16 Author: lode.vandevenne (at) gmail.com (Lode Vandevenne)
     17 Author: jyrki.alakuijala (at) gmail.com (Jyrki Alakuijala)
     18 */
     19 
     20 /*
     21 Zopfli compressor program. It can output gzip-, zlib- or deflate-compatible
     22 data. By default it creates a .gz file. This tool can only compress, not
     23 decompress. Decompression can be done by any standard gzip, zlib or deflate
     24 decompressor.
     25 */
     26 
     27 #include <assert.h>
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 
     32 #include "deflate.h"
     33 #include "gzip_container.h"
     34 #include "zlib_container.h"
     35 
     36 /*
     37 Loads a file into a memory array.
     38 */
     39 static void LoadFile(const char* filename,
     40                      unsigned char** out, size_t* outsize) {
     41   FILE* file;
     42 
     43   *out = 0;
     44   *outsize = 0;
     45   file = fopen(filename, "rb");
     46   if (!file) return;
     47 
     48   fseek(file , 0 , SEEK_END);
     49   *outsize = ftell(file);
     50   rewind(file);
     51 
     52   *out = (unsigned char*)malloc(*outsize);
     53 
     54   if (*outsize && (*out)) {
     55     size_t testsize = fread(*out, 1, *outsize, file);
     56     if (testsize != *outsize) {
     57       /* It could be a directory */
     58       free(*out);
     59       *out = 0;
     60       *outsize = 0;
     61     }
     62   }
     63 
     64   assert(!(*outsize) || out);  /* If size is not zero, out must be allocated. */
     65   fclose(file);
     66 }
     67 
     68 /*
     69 Saves a file from a memory array, overwriting the file if it existed.
     70 */
     71 static void SaveFile(const char* filename,
     72                      const unsigned char* in, size_t insize) {
     73   FILE* file = fopen(filename, "wb" );
     74   assert(file);
     75   fwrite((char*)in, 1, insize, file);
     76   fclose(file);
     77 }
     78 
     79 /*
     80 outfilename: filename to write output to, or 0 to write to stdout instead
     81 */
     82 static void CompressFile(const ZopfliOptions* options,
     83                          ZopfliFormat output_type,
     84                          const char* infilename,
     85                          const char* outfilename) {
     86   unsigned char* in;
     87   size_t insize;
     88   unsigned char* out = 0;
     89   size_t outsize = 0;
     90   LoadFile(infilename, &in, &insize);
     91   if (insize == 0) {
     92     fprintf(stderr, "Invalid filename: %s\n", infilename);
     93     return;
     94   }
     95 
     96   ZopfliCompress(options, output_type, in, insize, &out, &outsize);
     97 
     98   if (outfilename) {
     99     SaveFile(outfilename, out, outsize);
    100   } else {
    101     size_t i;
    102     for (i = 0; i < outsize; i++) {
    103       /* Works only if terminal does not convert newlines. */
    104       printf("%c", out[i]);
    105     }
    106   }
    107 
    108   free(out);
    109   free(in);
    110 }
    111 
    112 /*
    113 Add two strings together. Size does not matter. Result must be freed.
    114 */
    115 static char* AddStrings(const char* str1, const char* str2) {
    116   size_t len = strlen(str1) + strlen(str2);
    117   char* result = (char*)malloc(len + 1);
    118   if (!result) exit(-1); /* Allocation failed. */
    119   strcpy(result, str1);
    120   strcat(result, str2);
    121   return result;
    122 }
    123 
    124 static char StringsEqual(const char* str1, const char* str2) {
    125   return strcmp(str1, str2) == 0;
    126 }
    127 
    128 int main(int argc, char* argv[]) {
    129   ZopfliOptions options;
    130   ZopfliFormat output_type = ZOPFLI_FORMAT_GZIP;
    131   const char* filename = 0;
    132   int output_to_stdout = 0;
    133   int i;
    134 
    135   ZopfliInitOptions(&options);
    136 
    137   for (i = 1; i < argc; i++) {
    138     const char* arg = argv[i];
    139     if (StringsEqual(arg, "-v")) options.verbose = 1;
    140     else if (StringsEqual(arg, "-c")) output_to_stdout = 1;
    141     else if (StringsEqual(arg, "--deflate")) {
    142       output_type = ZOPFLI_FORMAT_DEFLATE;
    143     }
    144     else if (StringsEqual(arg, "--zlib")) output_type = ZOPFLI_FORMAT_ZLIB;
    145     else if (StringsEqual(arg, "--gzip")) output_type = ZOPFLI_FORMAT_GZIP;
    146     else if (StringsEqual(arg, "--splitlast")) options.blocksplittinglast = 1;
    147     else if (arg[0] == '-' && arg[1] == '-' && arg[2] == 'i'
    148         && arg[3] >= '0' && arg[3] <= '9') {
    149       options.numiterations = atoi(arg + 3);
    150     }
    151     else if (StringsEqual(arg, "-h")) {
    152       fprintf(stderr,
    153           "Usage: zopfli [OPTION]... FILE\n"
    154           "  -h    gives this help\n"
    155           "  -c    write the result on standard output, instead of disk"
    156           " filename + '.gz'\n"
    157           "  -v    verbose mode\n"
    158           "  --i#  perform # iterations (default 15). More gives"
    159           " more compression but is slower."
    160           " Examples: --i10, --i50, --i1000\n");
    161       fprintf(stderr,
    162           "  --gzip        output to gzip format (default)\n"
    163           "  --zlib        output to zlib format instead of gzip\n"
    164           "  --deflate     output to deflate format instead of gzip\n"
    165           "  --splitlast   do block splitting last instead of first\n");
    166       return 0;
    167     }
    168   }
    169 
    170   if (options.numiterations < 1) {
    171     fprintf(stderr, "Error: must have 1 or more iterations");
    172     return 0;
    173   }
    174 
    175   for (i = 1; i < argc; i++) {
    176     if (argv[i][0] != '-') {
    177       char* outfilename;
    178       filename = argv[i];
    179       if (output_to_stdout) {
    180         outfilename = 0;
    181       } else if (output_type == ZOPFLI_FORMAT_GZIP) {
    182         outfilename = AddStrings(filename, ".gz");
    183       } else if (output_type == ZOPFLI_FORMAT_ZLIB) {
    184         outfilename = AddStrings(filename, ".zlib");
    185       } else {
    186         assert(output_type == ZOPFLI_FORMAT_DEFLATE);
    187         outfilename = AddStrings(filename, ".deflate");
    188       }
    189       if (options.verbose && outfilename) {
    190         fprintf(stderr, "Saving to: %s\n", outfilename);
    191       }
    192       CompressFile(&options, output_type, filename, outfilename);
    193       free(outfilename);
    194     }
    195   }
    196 
    197   if (!filename) {
    198     fprintf(stderr,
    199             "Please provide filename\nFor help, type: %s -h\n", argv[0]);
    200   }
    201 
    202   return 0;
    203 }
    204