1/*
2Copyright 2011 Google Inc. All Rights Reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15
16Author: lode.vandevenne@gmail.com (Lode Vandevenne)
17Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
18*/
19
20/*
21Zopfli compressor program. It can output gzip-, zlib- or deflate-compatible
22data. By default it creates a .gz file. This tool can only compress, not
23decompress. Decompression can be done by any standard gzip, zlib or deflate
24decompressor.
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/*
37Loads a file into a memory array.
38*/
39static 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/*
69Saves a file from a memory array, overwriting the file if it existed.
70*/
71static 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/*
80outfilename: filename to write output to, or 0 to write to stdout instead
81*/
82static 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/*
113Add two strings together. Size does not matter. Result must be freed.
114*/
115static 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
124static char StringsEqual(const char* str1, const char* str2) {
125  return strcmp(str1, str2) == 0;
126}
127
128int 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