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