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 /*
37 Loads a file into a memory array.
38 */
LoadFile(const char * filename,unsigned char ** out,size_t * outsize)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 */
SaveFile(const char * filename,const unsigned char * in,size_t insize)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 */
CompressFile(const ZopfliOptions * options,ZopfliFormat output_type,const char * infilename,const char * outfilename)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 */
AddStrings(const char * str1,const char * str2)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
StringsEqual(const char * str1,const char * str2)124 static char StringsEqual(const char* str1, const char* str2) {
125 return strcmp(str1, str2) == 0;
126 }
127
main(int argc,char * argv[])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