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@gmail.com (Lode Vandevenne)
17 Author: jyrki.alakuijala@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 /* Windows workaround for stdout output. */
37 #if _WIN32
38 #include <fcntl.h>
39 #endif
40 
41 /*
42 Loads a file into a memory array. Returns 1 on success, 0 if file doesn't exist
43 or couldn't be opened.
44 */
LoadFile(const char * filename,unsigned char ** out,size_t * outsize)45 static int LoadFile(const char* filename,
46                     unsigned char** out, size_t* outsize) {
47   FILE* file;
48 
49   *out = 0;
50   *outsize = 0;
51   file = fopen(filename, "rb");
52   if (!file) return 0;
53 
54   fseek(file , 0 , SEEK_END);
55   *outsize = ftell(file);
56   if(*outsize > 2147483647) {
57     fprintf(stderr,"Files larger than 2GB are not supported.\n");
58     exit(EXIT_FAILURE);
59   }
60   rewind(file);
61 
62   *out = (unsigned char*)malloc(*outsize);
63 
64   if (*outsize && (*out)) {
65     size_t testsize = fread(*out, 1, *outsize, file);
66     if (testsize != *outsize) {
67       /* It could be a directory */
68       free(*out);
69       *out = 0;
70       *outsize = 0;
71       fclose(file);
72       return 0;
73     }
74   }
75 
76   assert(!(*outsize) || out);  /* If size is not zero, out must be allocated. */
77   fclose(file);
78   return 1;
79 }
80 
81 /*
82 Saves a file from a memory array, overwriting the file if it existed.
83 */
SaveFile(const char * filename,const unsigned char * in,size_t insize)84 static void SaveFile(const char* filename,
85                      const unsigned char* in, size_t insize) {
86   FILE* file = fopen(filename, "wb" );
87   if (file == NULL) {
88       fprintf(stderr,"Error: Cannot write to output file, terminating.\n");
89       exit (EXIT_FAILURE);
90   }
91   assert(file);
92   fwrite((char*)in, 1, insize, file);
93   fclose(file);
94 }
95 
96 /*
97 outfilename: filename to write output to, or 0 to write to stdout instead
98 */
CompressFile(const ZopfliOptions * options,ZopfliFormat output_type,const char * infilename,const char * outfilename)99 static void CompressFile(const ZopfliOptions* options,
100                          ZopfliFormat output_type,
101                          const char* infilename,
102                          const char* outfilename) {
103   unsigned char* in;
104   size_t insize;
105   unsigned char* out = 0;
106   size_t outsize = 0;
107   if (!LoadFile(infilename, &in, &insize)) {
108     fprintf(stderr, "Invalid filename: %s\n", infilename);
109     return;
110   }
111 
112   ZopfliCompress(options, output_type, in, insize, &out, &outsize);
113 
114   if (outfilename) {
115     SaveFile(outfilename, out, outsize);
116   } else {
117 #if _WIN32
118     /* Windows workaround for stdout output. */
119     _setmode(_fileno(stdout), _O_BINARY);
120 #endif
121     fwrite(out, 1, outsize, stdout);
122   }
123 
124   free(out);
125   free(in);
126 }
127 
128 /*
129 Add two strings together. Size does not matter. Result must be freed.
130 */
AddStrings(const char * str1,const char * str2)131 static char* AddStrings(const char* str1, const char* str2) {
132   size_t len = strlen(str1) + strlen(str2);
133   char* result = (char*)malloc(len + 1);
134   if (!result) exit(-1); /* Allocation failed. */
135   strcpy(result, str1);
136   strcat(result, str2);
137   return result;
138 }
139 
StringsEqual(const char * str1,const char * str2)140 static char StringsEqual(const char* str1, const char* str2) {
141   return strcmp(str1, str2) == 0;
142 }
143 
main(int argc,char * argv[])144 int main(int argc, char* argv[]) {
145   ZopfliOptions options;
146   ZopfliFormat output_type = ZOPFLI_FORMAT_GZIP;
147   const char* filename = 0;
148   int output_to_stdout = 0;
149   int i;
150 
151   ZopfliInitOptions(&options);
152 
153   for (i = 1; i < argc; i++) {
154     const char* arg = argv[i];
155     if (StringsEqual(arg, "-v")) options.verbose = 1;
156     else if (StringsEqual(arg, "-c")) output_to_stdout = 1;
157     else if (StringsEqual(arg, "--deflate")) {
158       output_type = ZOPFLI_FORMAT_DEFLATE;
159     }
160     else if (StringsEqual(arg, "--zlib")) output_type = ZOPFLI_FORMAT_ZLIB;
161     else if (StringsEqual(arg, "--gzip")) output_type = ZOPFLI_FORMAT_GZIP;
162     else if (StringsEqual(arg, "--splitlast"))  /* Ignore */;
163     else if (arg[0] == '-' && arg[1] == '-' && arg[2] == 'i'
164         && arg[3] >= '0' && arg[3] <= '9') {
165       options.numiterations = atoi(arg + 3);
166     }
167     else if (StringsEqual(arg, "-h")) {
168       fprintf(stderr,
169           "Usage: zopfli [OPTION]... FILE...\n"
170           "  -h    gives this help\n"
171           "  -c    write the result on standard output, instead of disk"
172           " filename + '.gz'\n"
173           "  -v    verbose mode\n"
174           "  --i#  perform # iterations (default 15). More gives"
175           " more compression but is slower."
176           " Examples: --i10, --i50, --i1000\n");
177       fprintf(stderr,
178           "  --gzip        output to gzip format (default)\n"
179           "  --zlib        output to zlib format instead of gzip\n"
180           "  --deflate     output to deflate format instead of gzip\n"
181           "  --splitlast   ignored, left for backwards compatibility\n");
182       return 0;
183     }
184   }
185 
186   if (options.numiterations < 1) {
187     fprintf(stderr, "Error: must have 1 or more iterations\n");
188     return 0;
189   }
190 
191   for (i = 1; i < argc; i++) {
192     if (argv[i][0] != '-') {
193       char* outfilename;
194       filename = argv[i];
195       if (output_to_stdout) {
196         outfilename = 0;
197       } else if (output_type == ZOPFLI_FORMAT_GZIP) {
198         outfilename = AddStrings(filename, ".gz");
199       } else if (output_type == ZOPFLI_FORMAT_ZLIB) {
200         outfilename = AddStrings(filename, ".zlib");
201       } else {
202         assert(output_type == ZOPFLI_FORMAT_DEFLATE);
203         outfilename = AddStrings(filename, ".deflate");
204       }
205       if (options.verbose && outfilename) {
206         fprintf(stderr, "Saving to: %s\n", outfilename);
207       }
208       CompressFile(&options, output_type, filename, outfilename);
209       free(outfilename);
210     }
211   }
212 
213   if (!filename) {
214     fprintf(stderr,
215             "Please provide filename\nFor help, type: %s -h\n", argv[0]);
216   }
217 
218   return 0;
219 }
220