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 */ 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 */ 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 */ 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 */ 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 124 static char StringsEqual(const char* str1, const char* str2) { 125 return strcmp(str1, str2) == 0; 126 } 127 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 if (StringsEqual(argv[i], "-v")) options.verbose = 1; 139 else if (StringsEqual(argv[i], "-c")) output_to_stdout = 1; 140 else if (StringsEqual(argv[i], "--deflate")) { 141 output_type = ZOPFLI_FORMAT_DEFLATE; 142 } 143 else if (StringsEqual(argv[i], "--zlib")) output_type = ZOPFLI_FORMAT_ZLIB; 144 else if (StringsEqual(argv[i], "--gzip")) output_type = ZOPFLI_FORMAT_GZIP; 145 else if (StringsEqual(argv[i], "--i5")) options.numiterations = 5; 146 else if (StringsEqual(argv[i], "--i10")) options.numiterations = 10; 147 else if (StringsEqual(argv[i], "--i15")) options.numiterations = 15; 148 else if (StringsEqual(argv[i], "--i25")) options.numiterations = 25; 149 else if (StringsEqual(argv[i], "--i50")) options.numiterations = 50; 150 else if (StringsEqual(argv[i], "--i100")) options.numiterations = 100; 151 else if (StringsEqual(argv[i], "--i250")) options.numiterations = 250; 152 else if (StringsEqual(argv[i], "--i500")) options.numiterations = 500; 153 else if (StringsEqual(argv[i], "--i1000")) options.numiterations = 1000; 154 else if (StringsEqual(argv[i], "-h")) { 155 fprintf(stderr, "Usage: zopfli [OPTION]... FILE\n" 156 " -h gives this help\n" 157 " -c write the result on standard output, instead of disk" 158 " filename + '.gz'\n" 159 " -v verbose mode\n" 160 " --gzip output to gzip format (default)\n" 161 " --deflate output to deflate format instead of gzip\n" 162 " --zlib output to zlib format instead of gzip\n"); 163 fprintf(stderr, " --i5 less compression, but faster\n" 164 " --i10 less compression, but faster\n" 165 " --i15 default compression, 15 iterations\n" 166 " --i25 more compression, but slower\n" 167 " --i50 more compression, but slower\n" 168 " --i100 more compression, but slower\n" 169 " --i250 more compression, but slower\n" 170 " --i500 more compression, but slower\n" 171 " --i1000 more compression, but slower\n"); 172 return 0; 173 } 174 } 175 176 for (i = 1; i < argc; i++) { 177 if (argv[i][0] != '-') { 178 char* outfilename; 179 filename = argv[i]; 180 if (output_to_stdout) { 181 outfilename = 0; 182 } else if (output_type == ZOPFLI_FORMAT_GZIP) { 183 outfilename = AddStrings(filename, ".gz"); 184 } else if (output_type == ZOPFLI_FORMAT_ZLIB) { 185 outfilename = AddStrings(filename, ".zlib"); 186 } else { 187 assert(output_type == ZOPFLI_FORMAT_DEFLATE); 188 outfilename = AddStrings(filename, ".deflate"); 189 } 190 if (options.verbose && outfilename) { 191 fprintf(stderr, "Saving to: %s\n", outfilename); 192 } 193 CompressFile(&options, output_type, filename, outfilename); 194 free(outfilename); 195 } 196 } 197 198 if (!filename) { 199 fprintf(stderr, 200 "Please provide filename\nFor help, type: %s -h\n", argv[0]); 201 } 202 203 return 0; 204 } 205