1d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne/*
2d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneCopyright 2011 Google Inc. All Rights Reserved.
3d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
4d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneLicensed under the Apache License, Version 2.0 (the "License");
5d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenneyou may not use this file except in compliance with the License.
6d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneYou may obtain a copy of the License at
7d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
8d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    http://www.apache.org/licenses/LICENSE-2.0
9d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
10d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneUnless required by applicable law or agreed to in writing, software
11d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennedistributed under the License is distributed on an "AS IS" BASIS,
12d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneSee the License for the specific language governing permissions and
14d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennelimitations under the License.
15d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
16d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneAuthor: lode.vandevenne@gmail.com (Lode Vandevenne)
17d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneAuthor: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
18d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne*/
19d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
20d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne/*
21d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneZopfli compressor program. It can output gzip-, zlib- or deflate-compatible
22d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennedata. By default it creates a .gz file. This tool can only compress, not
23d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennedecompress. Decompression can be done by any standard gzip, zlib or deflate
24d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennedecompressor.
25d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne*/
26d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
27d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne#include <assert.h>
28d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne#include <stdio.h>
29d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne#include <stdlib.h>
30d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne#include <string.h>
31d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
32d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne#include "deflate.h"
33d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne#include "gzip_container.h"
34d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne#include "zlib_container.h"
35d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
36d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne/*
37d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneLoads a file into a memory array.
38d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne*/
39d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennestatic void LoadFile(const char* filename,
40d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne                     unsigned char** out, size_t* outsize) {
41d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  FILE* file;
42d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
43d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  *out = 0;
44d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  *outsize = 0;
45d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  file = fopen(filename, "rb");
46d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  if (!file) return;
47d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
48d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  fseek(file , 0 , SEEK_END);
49d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  *outsize = ftell(file);
50d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  rewind(file);
51d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
52d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  *out = (unsigned char*)malloc(*outsize);
53d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
54d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  if (*outsize && (*out)) {
55d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    size_t testsize = fread(*out, 1, *outsize, file);
56d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    if (testsize != *outsize) {
57d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      /* It could be a directory */
58d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      free(*out);
59d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      *out = 0;
60d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      *outsize = 0;
61d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    }
62d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  }
63d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
64d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  assert(!(*outsize) || out);  /* If size is not zero, out must be allocated. */
65d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  fclose(file);
66d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne}
67d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
68d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne/*
69d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneSaves a file from a memory array, overwriting the file if it existed.
70d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne*/
71d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennestatic void SaveFile(const char* filename,
72d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne                     const unsigned char* in, size_t insize) {
73d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  FILE* file = fopen(filename, "wb" );
74d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  assert(file);
75d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  fwrite((char*)in, 1, insize, file);
76d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  fclose(file);
77d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne}
78d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
79d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne/*
80d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenneoutfilename: filename to write output to, or 0 to write to stdout instead
81d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne*/
82981df0fe897c94382b9b963eb72bc36cbc2e729cLode Vandevennestatic void CompressFile(const ZopfliOptions* options,
838c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne                         ZopfliFormat output_type,
84981df0fe897c94382b9b963eb72bc36cbc2e729cLode Vandevenne                         const char* infilename,
85981df0fe897c94382b9b963eb72bc36cbc2e729cLode Vandevenne                         const char* outfilename) {
86d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  unsigned char* in;
87d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  size_t insize;
88d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  unsigned char* out = 0;
89d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  size_t outsize = 0;
90d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  LoadFile(infilename, &in, &insize);
91d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  if (insize == 0) {
92d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    fprintf(stderr, "Invalid filename: %s\n", infilename);
93d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    return;
94d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  }
958c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne
968c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne  ZopfliCompress(options, output_type, in, insize, &out, &outsize);
978c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne
98d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  if (outfilename) {
99d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    SaveFile(outfilename, out, outsize);
100d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  } else {
101d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    size_t i;
102d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    for (i = 0; i < outsize; i++) {
103d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      /* Works only if terminal does not convert newlines. */
104d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      printf("%c", out[i]);
105d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    }
106d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  }
107d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
108d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  free(out);
109d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  free(in);
110d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne}
111d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
112d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne/*
113d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode VandevenneAdd two strings together. Size does not matter. Result must be freed.
114d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne*/
115d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennestatic char* AddStrings(const char* str1, const char* str2) {
116d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  size_t len = strlen(str1) + strlen(str2);
117d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  char* result = (char*)malloc(len + 1);
118d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  if (!result) exit(-1); /* Allocation failed. */
119d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  strcpy(result, str1);
120d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  strcat(result, str2);
121d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  return result;
122d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne}
123d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
124d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevennestatic char StringsEqual(const char* str1, const char* str2) {
125d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  return strcmp(str1, str2) == 0;
126d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne}
127d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
128d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenneint main(int argc, char* argv[]) {
129981df0fe897c94382b9b963eb72bc36cbc2e729cLode Vandevenne  ZopfliOptions options;
1308c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne  ZopfliFormat output_type = ZOPFLI_FORMAT_GZIP;
131d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  const char* filename = 0;
132d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  int output_to_stdout = 0;
133d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  int i;
134d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
135981df0fe897c94382b9b963eb72bc36cbc2e729cLode Vandevenne  ZopfliInitOptions(&options);
136d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
137d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  for (i = 1; i < argc; i++) {
138806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    const char* arg = argv[i];
139806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    if (StringsEqual(arg, "-v")) options.verbose = 1;
140806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    else if (StringsEqual(arg, "-c")) output_to_stdout = 1;
141806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    else if (StringsEqual(arg, "--deflate")) {
1428c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne      output_type = ZOPFLI_FORMAT_DEFLATE;
1438c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne    }
144806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    else if (StringsEqual(arg, "--zlib")) output_type = ZOPFLI_FORMAT_ZLIB;
145806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    else if (StringsEqual(arg, "--gzip")) output_type = ZOPFLI_FORMAT_GZIP;
146806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    else if (StringsEqual(arg, "--splitlast")) options.blocksplittinglast = 1;
147806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    else if (arg[0] == '-' && arg[1] == '-' && arg[2] == 'i'
148806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne        && arg[3] >= '0' && arg[3] <= '9') {
149806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne      options.numiterations = atoi(arg + 3);
150806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    }
151806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    else if (StringsEqual(arg, "-h")) {
152806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne      fprintf(stderr,
153806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          "Usage: zopfli [OPTION]... FILE\n"
154d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne          "  -h    gives this help\n"
155d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne          "  -c    write the result on standard output, instead of disk"
156d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne          " filename + '.gz'\n"
157d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne          "  -v    verbose mode\n"
158806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          "  --i#  perform # iterations (default 15). More gives"
159806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          " more compression but is slower."
160806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          " Examples: --i10, --i50, --i1000\n");
161806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne      fprintf(stderr,
162806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          "  --gzip        output to gzip format (default)\n"
163806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          "  --zlib        output to zlib format instead of gzip\n"
164806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          "  --deflate     output to deflate format instead of gzip\n"
165806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne          "  --splitlast   do block splitting last instead of first\n");
166d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      return 0;
167d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    }
168d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  }
169d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
170806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne  if (options.numiterations < 1) {
171806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    fprintf(stderr, "Error: must have 1 or more iterations");
172806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne    return 0;
173806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne  }
174806be49c750347eb78f4d94bb21ad37aa9121f93Lode Vandevenne
175d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  for (i = 1; i < argc; i++) {
176d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    if (argv[i][0] != '-') {
177d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      char* outfilename;
178d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      filename = argv[i];
179d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      if (output_to_stdout) {
180d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne        outfilename = 0;
1818c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne      } else if (output_type == ZOPFLI_FORMAT_GZIP) {
182d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne        outfilename = AddStrings(filename, ".gz");
1838c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne      } else if (output_type == ZOPFLI_FORMAT_ZLIB) {
184d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne        outfilename = AddStrings(filename, ".zlib");
185d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      } else {
1868c218eff39749e738c92bf34155099ad280c16f7Lode Vandevenne        assert(output_type == ZOPFLI_FORMAT_DEFLATE);
187981df0fe897c94382b9b963eb72bc36cbc2e729cLode Vandevenne        outfilename = AddStrings(filename, ".deflate");
188d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      }
189d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      if (options.verbose && outfilename) {
190d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne        fprintf(stderr, "Saving to: %s\n", outfilename);
191d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      }
192d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      CompressFile(&options, output_type, filename, outfilename);
193d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne      free(outfilename);
194d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    }
195d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  }
196d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
197d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  if (!filename) {
198d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne    fprintf(stderr,
199d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne            "Please provide filename\nFor help, type: %s -h\n", argv[0]);
200d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  }
201d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne
202d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne  return 0;
203d5eb5f507386e9933f2d8248d311ceca41fe1df1Lode Vandevenne}
204