Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/brotli/c/tools/brotli.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* Copyright 2014 Google Inc. All Rights Reserved. | |
| 2 | |
| 3 Distributed under MIT license. | |
| 4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT | |
| 5 */ | |
| 6 | |
| 7 /* Command line interface for Brotli library. */ | |
| 8 | |
| 9 /* Mute strerror/strcpy warnings. */ | |
| 10 #if !defined(_CRT_SECURE_NO_WARNINGS) | |
| 11 #define _CRT_SECURE_NO_WARNINGS | |
| 12 #endif | |
| 13 | |
| 14 #include <errno.h> | |
| 15 #include <fcntl.h> | |
| 16 #include <stdio.h> | |
| 17 #include <stdlib.h> | |
| 18 #include <string.h> | |
| 19 #include <sys/stat.h> | |
| 20 #include <sys/types.h> | |
| 21 #include <time.h> | |
| 22 | |
| 23 #include <brotli/decode.h> | |
| 24 #include <brotli/encode.h> | |
| 25 #include <brotli/types.h> | |
| 26 | |
| 27 #include "../common/constants.h" | |
| 28 #include "../common/version.h" | |
| 29 | |
| 30 #if defined(_WIN32) | |
| 31 #include <io.h> | |
| 32 #include <share.h> | |
| 33 #include <sys/utime.h> | |
| 34 | |
| 35 #define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO)) | |
| 36 | |
| 37 #if !defined(__MINGW32__) | |
| 38 #define STDIN_FILENO _fileno(stdin) | |
| 39 #define STDOUT_FILENO _fileno(stdout) | |
| 40 #define S_IRUSR S_IREAD | |
| 41 #define S_IWUSR S_IWRITE | |
| 42 #endif | |
| 43 | |
| 44 #define fdopen _fdopen | |
| 45 #define isatty _isatty | |
| 46 #define unlink _unlink | |
| 47 #define utimbuf _utimbuf | |
| 48 #define utime _utime | |
| 49 | |
| 50 #define fopen ms_fopen | |
| 51 #define open ms_open | |
| 52 | |
| 53 #define chmod(F, P) (0) | |
| 54 #define chown(F, O, G) (0) | |
| 55 | |
| 56 #if defined(_MSC_VER) && (_MSC_VER >= 1400) | |
| 57 #define fseek _fseeki64 | |
| 58 #define ftell _ftelli64 | |
| 59 #endif | |
| 60 | |
| 61 static FILE* ms_fopen(const char* filename, const char* mode) { | |
| 62 FILE* result = 0; | |
| 63 fopen_s(&result, filename, mode); | |
| 64 return result; | |
| 65 } | |
| 66 | |
| 67 static int ms_open(const char* filename, int oflag, int pmode) { | |
| 68 int result = -1; | |
| 69 _sopen_s(&result, filename, oflag | O_BINARY, _SH_DENYNO, pmode); | |
| 70 return result; | |
| 71 } | |
| 72 #else /* !defined(_WIN32) */ | |
| 73 #include <unistd.h> | |
| 74 #include <utime.h> | |
| 75 #define MAKE_BINARY(FILENO) (FILENO) | |
| 76 #endif /* defined(_WIN32) */ | |
| 77 | |
| 78 #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) | |
| 79 #define HAVE_UTIMENSAT 1 | |
| 80 #elif defined(_ATFILE_SOURCE) | |
| 81 #define HAVE_UTIMENSAT 1 | |
| 82 #else | |
| 83 #define HAVE_UTIMENSAT 0 | |
| 84 #endif | |
| 85 | |
| 86 #if HAVE_UTIMENSAT | |
| 87 #if defined(__APPLE__) | |
| 88 #define ATIME_NSEC(S) ((S)->st_atimespec.tv_nsec) | |
| 89 #define MTIME_NSEC(S) ((S)->st_mtimespec.tv_nsec) | |
| 90 #else /* defined(__APPLE__) */ | |
| 91 #define ATIME_NSEC(S) ((S)->st_atim.tv_nsec) | |
| 92 #define MTIME_NSEC(S) ((S)->st_mtim.tv_nsec) | |
| 93 #endif | |
| 94 #endif /* HAVE_UTIMENSAT */ | |
| 95 | |
| 96 typedef enum { | |
| 97 COMMAND_COMPRESS, | |
| 98 COMMAND_DECOMPRESS, | |
| 99 COMMAND_HELP, | |
| 100 COMMAND_INVALID, | |
| 101 COMMAND_TEST_INTEGRITY, | |
| 102 COMMAND_NOOP, | |
| 103 COMMAND_VERSION | |
| 104 } Command; | |
| 105 | |
| 106 typedef enum { | |
| 107 COMMENT_INIT, | |
| 108 COMMENT_READ, | |
| 109 COMMENT_OK, | |
| 110 COMMENT_BAD, | |
| 111 } CommentState; | |
| 112 | |
| 113 #define DEFAULT_LGWIN 24 | |
| 114 #define DEFAULT_SUFFIX ".br" | |
| 115 #define MAX_OPTIONS 24 | |
| 116 #define MAX_COMMENT_LEN 80 | |
| 117 | |
| 118 typedef struct { | |
| 119 /* Parameters */ | |
| 120 int quality; | |
| 121 int lgwin; | |
| 122 int verbosity; | |
| 123 BROTLI_BOOL force_overwrite; | |
| 124 BROTLI_BOOL junk_source; | |
| 125 BROTLI_BOOL reject_uncompressible; | |
| 126 BROTLI_BOOL copy_stat; | |
| 127 BROTLI_BOOL write_to_stdout; | |
| 128 BROTLI_BOOL test_integrity; | |
| 129 BROTLI_BOOL decompress; | |
| 130 BROTLI_BOOL large_window; | |
| 131 BROTLI_BOOL allow_concatenated; | |
| 132 const char* output_path; | |
| 133 const char* dictionary_path; | |
| 134 const char* suffix; | |
| 135 uint8_t comment[MAX_COMMENT_LEN]; | |
| 136 size_t comment_len; | |
| 137 size_t comment_pos; | |
| 138 CommentState comment_state; | |
| 139 int not_input_indices[MAX_OPTIONS]; | |
| 140 size_t longest_path_len; | |
| 141 size_t input_count; | |
| 142 | |
| 143 /* Inner state */ | |
| 144 int argc; | |
| 145 char** argv; | |
| 146 uint8_t* dictionary; | |
| 147 size_t dictionary_size; | |
| 148 BrotliEncoderPreparedDictionary* prepared_dictionary; | |
| 149 BrotliDecoderState* decoder; | |
| 150 char* modified_path; /* Storage for path with appended / cut suffix */ | |
| 151 int iterator; | |
| 152 int ignore; | |
| 153 BROTLI_BOOL iterator_error; | |
| 154 uint8_t* buffer; | |
| 155 uint8_t* input; | |
| 156 uint8_t* output; | |
| 157 const char* current_input_path; | |
| 158 const char* current_output_path; | |
| 159 int64_t input_file_length; /* -1, if impossible to calculate */ | |
| 160 FILE* fin; | |
| 161 FILE* fout; | |
| 162 | |
| 163 /* I/O buffers */ | |
| 164 size_t available_in; | |
| 165 const uint8_t* next_in; | |
| 166 size_t available_out; | |
| 167 uint8_t* next_out; | |
| 168 | |
| 169 /* Reporting */ | |
| 170 /* size_t would be large enough, | |
| 171 until 4GiB+ files are compressed / decompressed on 32-bit CPUs. */ | |
| 172 size_t total_in; | |
| 173 size_t total_out; | |
| 174 clock_t start_time; | |
| 175 clock_t end_time; | |
| 176 } Context; | |
| 177 | |
| 178 /* Parse base 64 encoded string to buffer. Not performance-centric. | |
| 179 |out_len| as input is buffer size; |out_len| as output is decoded length. | |
| 180 Returns BROTLI_FALSE if either input is not (relaxed) base 64 conformant, | |
| 181 or output does not fit buffer. */ | |
| 182 static BROTLI_BOOL ParseBase64(const char* str, uint8_t* out, size_t* out_len) { | |
| 183 size_t in_len = strlen(str); | |
| 184 size_t max_out_len = *out_len; | |
| 185 size_t i; | |
| 186 size_t bit_count = 0; | |
| 187 uint32_t bits = 0; | |
| 188 size_t padding_count = 0; | |
| 189 size_t octet_count = 0; | |
| 190 for (i = 0; i < in_len; ++i) { | |
| 191 char c = str[i]; | |
| 192 int sextet = 0; | |
| 193 if (c == 9 || c == 10 || c == 13 || c == ' ') { | |
| 194 continue; | |
| 195 } | |
| 196 if (c == '=') { | |
| 197 padding_count++; | |
| 198 continue; | |
| 199 } | |
| 200 if (padding_count) return BROTLI_FALSE; | |
| 201 if (c == '+' || c == '-') { | |
| 202 sextet = 62; | |
| 203 } else if (c == '/' || c == '_') { | |
| 204 sextet = 63; | |
| 205 } else if (c >= 'A' && c <= 'Z') { | |
| 206 sextet = c - 'A'; | |
| 207 } else if (c >= 'a' && c <= 'z') { | |
| 208 sextet = c - 'a' + 26; | |
| 209 } else if (c >= '0' && c <= '9') { | |
| 210 sextet = c - '0' + 52; | |
| 211 } else { | |
| 212 return BROTLI_FALSE; | |
| 213 } | |
| 214 bits = (bits << 6) | (uint32_t)sextet; | |
| 215 bit_count += 6; | |
| 216 if (bit_count >= 8) { | |
| 217 if (octet_count == max_out_len) return BROTLI_FALSE; | |
| 218 bit_count -= 8; | |
| 219 out[octet_count++] = (bits >> bit_count) & 0xFF; | |
| 220 } | |
| 221 } | |
| 222 if (padding_count > 2) return BROTLI_FALSE; | |
| 223 *out_len = octet_count; | |
| 224 return BROTLI_TRUE; | |
| 225 } | |
| 226 | |
| 227 /* Parse up to 5 decimal digits. */ | |
| 228 static BROTLI_BOOL ParseInt(const char* s, int low, int high, int* result) { | |
| 229 int value = 0; | |
| 230 int i; | |
| 231 for (i = 0; i < 5; ++i) { | |
| 232 char c = s[i]; | |
| 233 if (c == 0) break; | |
| 234 if (s[i] < '0' || s[i] > '9') return BROTLI_FALSE; | |
| 235 value = (10 * value) + (c - '0'); | |
| 236 } | |
| 237 if (i == 0) return BROTLI_FALSE; | |
| 238 if (i > 1 && s[0] == '0') return BROTLI_FALSE; | |
| 239 if (s[i] != 0) return BROTLI_FALSE; | |
| 240 if (value < low || value > high) return BROTLI_FALSE; | |
| 241 *result = value; | |
| 242 return BROTLI_TRUE; | |
| 243 } | |
| 244 | |
| 245 /* Returns "base file name" or its tail, if it contains '/' or '\'. */ | |
| 246 static const char* FileName(const char* path) { | |
| 247 const char* separator_position = strrchr(path, '/'); | |
| 248 if (separator_position) path = separator_position + 1; | |
| 249 separator_position = strrchr(path, '\\'); | |
| 250 if (separator_position) path = separator_position + 1; | |
| 251 return path; | |
| 252 } | |
| 253 | |
| 254 /* Detect if the program name is a special alias that infers a command type. */ | |
| 255 static BROTLI_BOOL CheckAlias(const char* name, const char* alias) { | |
| 256 /* TODO: cast name to lower case? */ | |
| 257 size_t alias_len = strlen(alias); | |
| 258 name = FileName(name); | |
| 259 /* Partial comparison. On Windows there could be ".exe" suffix. */ | |
| 260 if (strncmp(name, alias, alias_len) == 0) { | |
| 261 char terminator = name[alias_len]; | |
| 262 if (terminator == 0 || terminator == '.') return BROTLI_TRUE; | |
| 263 } | |
| 264 return BROTLI_FALSE; | |
| 265 } | |
| 266 | |
| 267 static Command ParseParams(Context* params) { | |
| 268 int argc = params->argc; | |
| 269 char** argv = params->argv; | |
| 270 int i; | |
| 271 int next_option_index = 0; | |
| 272 size_t input_count = 0; | |
| 273 size_t longest_path_len = 1; | |
| 274 BROTLI_BOOL command_set = BROTLI_FALSE; | |
| 275 BROTLI_BOOL quality_set = BROTLI_FALSE; | |
| 276 BROTLI_BOOL output_set = BROTLI_FALSE; | |
| 277 BROTLI_BOOL keep_set = BROTLI_FALSE; | |
| 278 BROTLI_BOOL squash_set = BROTLI_FALSE; | |
| 279 BROTLI_BOOL lgwin_set = BROTLI_FALSE; | |
| 280 BROTLI_BOOL suffix_set = BROTLI_FALSE; | |
| 281 BROTLI_BOOL after_dash_dash = BROTLI_FALSE; | |
| 282 BROTLI_BOOL comment_set = BROTLI_FALSE; | |
| 283 BROTLI_BOOL concatenated_set = BROTLI_FALSE; | |
| 284 Command command = COMMAND_COMPRESS; | |
| 285 | |
| 286 if (CheckAlias(argv[0], "brcat")) { | |
| 287 command_set = BROTLI_TRUE; | |
| 288 command = COMMAND_DECOMPRESS; | |
| 289 concatenated_set = BROTLI_TRUE; | |
| 290 params->allow_concatenated = BROTLI_TRUE; | |
| 291 output_set = BROTLI_TRUE; | |
| 292 params->write_to_stdout = BROTLI_TRUE; | |
| 293 } else if (CheckAlias(argv[0], "unbrotli")) { | |
| 294 command_set = BROTLI_TRUE; | |
| 295 command = COMMAND_DECOMPRESS; | |
| 296 } | |
| 297 | |
| 298 for (i = 1; i < argc; ++i) { | |
| 299 const char* arg = argv[i]; | |
| 300 /* C99 5.1.2.2.1: "members argv[0] through argv[argc-1] inclusive shall | |
| 301 contain pointers to strings"; NULL and 0-length are not forbidden. */ | |
| 302 size_t arg_len = arg ? strlen(arg) : 0; | |
| 303 | |
| 304 if (arg_len == 0) { | |
| 305 params->not_input_indices[next_option_index++] = i; | |
| 306 continue; | |
| 307 } | |
| 308 | |
| 309 /* Too many options. The expected longest option list is: | |
| 310 "-q 0 -w 10 -o f -D d -S b -d -f -k -n -v -K --", i.e. 17 items in total. | |
| 311 This check is an additional guard that is never triggered, but provides | |
| 312 a guard for future changes. */ | |
| 313 if (next_option_index > (MAX_OPTIONS - 2)) { | |
| 314 fprintf(stderr, "too many options passed\n"); | |
| 315 return COMMAND_INVALID; | |
| 316 } | |
| 317 | |
| 318 /* Input file entry. */ | |
| 319 if (after_dash_dash || arg[0] != '-' || arg_len == 1) { | |
| 320 input_count++; | |
| 321 if (longest_path_len < arg_len) longest_path_len = arg_len; | |
| 322 continue; | |
| 323 } | |
| 324 | |
| 325 /* Not a file entry. */ | |
| 326 params->not_input_indices[next_option_index++] = i; | |
| 327 | |
| 328 /* '--' entry stop parsing arguments. */ | |
| 329 if (arg_len == 2 && arg[1] == '-') { | |
| 330 after_dash_dash = BROTLI_TRUE; | |
| 331 continue; | |
| 332 } | |
| 333 | |
| 334 /* Simple / coalesced options. */ | |
| 335 if (arg[1] != '-') { | |
| 336 size_t j; | |
| 337 for (j = 1; j < arg_len; ++j) { | |
| 338 char c = arg[j]; | |
| 339 if (c >= '0' && c <= '9') { | |
| 340 if (quality_set) { | |
| 341 fprintf(stderr, "quality already set\n"); | |
| 342 return COMMAND_INVALID; | |
| 343 } | |
| 344 quality_set = BROTLI_TRUE; | |
| 345 params->quality = c - '0'; | |
| 346 continue; | |
| 347 } else if (c == 'c') { | |
| 348 if (output_set) { | |
| 349 fprintf(stderr, "write to standard output already set\n"); | |
| 350 return COMMAND_INVALID; | |
| 351 } | |
| 352 output_set = BROTLI_TRUE; | |
| 353 params->write_to_stdout = BROTLI_TRUE; | |
| 354 continue; | |
| 355 } else if (c == 'd') { | |
| 356 if (command_set) { | |
| 357 fprintf(stderr, "command already set when parsing -d\n"); | |
| 358 return COMMAND_INVALID; | |
| 359 } | |
| 360 command_set = BROTLI_TRUE; | |
| 361 command = COMMAND_DECOMPRESS; | |
| 362 continue; | |
| 363 } else if (c == 'f') { | |
| 364 if (params->force_overwrite) { | |
| 365 fprintf(stderr, "force output overwrite already set\n"); | |
| 366 return COMMAND_INVALID; | |
| 367 } | |
| 368 params->force_overwrite = BROTLI_TRUE; | |
| 369 continue; | |
| 370 } else if (c == 'h') { | |
| 371 /* Don't parse further. */ | |
| 372 return COMMAND_HELP; | |
| 373 } else if (c == 'j' || c == 'k') { | |
| 374 if (keep_set) { | |
| 375 fprintf(stderr, "argument --rm / -j or --keep / -k already set\n"); | |
| 376 return COMMAND_INVALID; | |
| 377 } | |
| 378 keep_set = BROTLI_TRUE; | |
| 379 params->junk_source = TO_BROTLI_BOOL(c == 'j'); | |
| 380 continue; | |
| 381 } else if (c == 'n') { | |
| 382 if (!params->copy_stat) { | |
| 383 fprintf(stderr, "argument --no-copy-stat / -n already set\n"); | |
| 384 return COMMAND_INVALID; | |
| 385 } | |
| 386 params->copy_stat = BROTLI_FALSE; | |
| 387 continue; | |
| 388 } else if (c == 's') { | |
| 389 if (squash_set) { | |
| 390 fprintf(stderr, "argument --squash / -s already set\n"); | |
| 391 return COMMAND_INVALID; | |
| 392 } | |
| 393 squash_set = BROTLI_TRUE; | |
| 394 params->reject_uncompressible = BROTLI_TRUE; | |
| 395 continue; | |
| 396 } else if (c == 't') { | |
| 397 if (command_set) { | |
| 398 fprintf(stderr, "command already set when parsing -t\n"); | |
| 399 return COMMAND_INVALID; | |
| 400 } | |
| 401 command_set = BROTLI_TRUE; | |
| 402 command = COMMAND_TEST_INTEGRITY; | |
| 403 continue; | |
| 404 } else if (c == 'v') { | |
| 405 if (params->verbosity > 0) { | |
| 406 fprintf(stderr, "argument --verbose / -v already set\n"); | |
| 407 return COMMAND_INVALID; | |
| 408 } | |
| 409 params->verbosity = 1; | |
| 410 continue; | |
| 411 } else if (c == 'K') { | |
| 412 if (concatenated_set) { | |
| 413 fprintf(stderr, "argument -K / --concatenated already set\n"); | |
| 414 return COMMAND_INVALID; | |
| 415 } | |
| 416 concatenated_set = BROTLI_TRUE; | |
| 417 params->allow_concatenated = BROTLI_TRUE; | |
| 418 continue; | |
| 419 } else if (c == 'V') { | |
| 420 /* Don't parse further. */ | |
| 421 return COMMAND_VERSION; | |
| 422 } else if (c == 'Z') { | |
| 423 if (quality_set) { | |
| 424 fprintf(stderr, "quality already set\n"); | |
| 425 return COMMAND_INVALID; | |
| 426 } | |
| 427 quality_set = BROTLI_TRUE; | |
| 428 params->quality = 11; | |
| 429 continue; | |
| 430 } | |
| 431 /* o/q/w/C/D/S with parameter is expected */ | |
| 432 if (c != 'o' && c != 'q' && c != 'w' && c != 'C' && c != 'D' && | |
| 433 c != 'S') { | |
| 434 fprintf(stderr, "invalid argument -%c\n", c); | |
| 435 return COMMAND_INVALID; | |
| 436 } | |
| 437 if (j + 1 != arg_len) { | |
| 438 fprintf(stderr, "expected parameter for argument -%c\n", c); | |
| 439 return COMMAND_INVALID; | |
| 440 } | |
| 441 i++; | |
| 442 if (i == argc || !argv[i] || argv[i][0] == 0) { | |
| 443 fprintf(stderr, "expected parameter for argument -%c\n", c); | |
| 444 return COMMAND_INVALID; | |
| 445 } | |
| 446 params->not_input_indices[next_option_index++] = i; | |
| 447 if (c == 'o') { | |
| 448 if (output_set) { | |
| 449 fprintf(stderr, "write to standard output already set (-o)\n"); | |
| 450 return COMMAND_INVALID; | |
| 451 } | |
| 452 params->output_path = argv[i]; | |
| 453 } else if (c == 'q') { | |
| 454 if (quality_set) { | |
| 455 fprintf(stderr, "quality already set\n"); | |
| 456 return COMMAND_INVALID; | |
| 457 } | |
| 458 quality_set = ParseInt(argv[i], BROTLI_MIN_QUALITY, | |
| 459 BROTLI_MAX_QUALITY, ¶ms->quality); | |
| 460 if (!quality_set) { | |
| 461 fprintf(stderr, "error parsing quality value [%s]\n", argv[i]); | |
| 462 return COMMAND_INVALID; | |
| 463 } | |
| 464 } else if (c == 'w') { | |
| 465 if (lgwin_set) { | |
| 466 fprintf(stderr, "lgwin parameter already set\n"); | |
| 467 return COMMAND_INVALID; | |
| 468 } | |
| 469 lgwin_set = ParseInt(argv[i], 0, | |
| 470 BROTLI_MAX_WINDOW_BITS, ¶ms->lgwin); | |
| 471 if (!lgwin_set) { | |
| 472 fprintf(stderr, "error parsing lgwin value [%s]\n", argv[i]); | |
| 473 return COMMAND_INVALID; | |
| 474 } | |
| 475 if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) { | |
| 476 fprintf(stderr, | |
| 477 "lgwin parameter (%d) smaller than the minimum (%d)\n", | |
| 478 params->lgwin, BROTLI_MIN_WINDOW_BITS); | |
| 479 return COMMAND_INVALID; | |
| 480 } | |
| 481 } else if (c == 'C') { | |
| 482 if (comment_set) { | |
| 483 fprintf(stderr, "comment already set\n"); | |
| 484 return COMMAND_INVALID; | |
| 485 } | |
| 486 params->comment_len = MAX_COMMENT_LEN; | |
| 487 if (!ParseBase64(argv[i], params->comment, ¶ms->comment_len)) { | |
| 488 fprintf(stderr, "invalid base64-encoded comment\n"); | |
| 489 return COMMAND_INVALID; | |
| 490 } | |
| 491 comment_set = BROTLI_TRUE; | |
| 492 } else if (c == 'D') { | |
| 493 if (params->dictionary_path) { | |
| 494 fprintf(stderr, "dictionary path already set\n"); | |
| 495 return COMMAND_INVALID; | |
| 496 } | |
| 497 params->dictionary_path = argv[i]; | |
| 498 } else if (c == 'S') { | |
| 499 if (suffix_set) { | |
| 500 fprintf(stderr, "suffix already set\n"); | |
| 501 return COMMAND_INVALID; | |
| 502 } | |
| 503 suffix_set = BROTLI_TRUE; | |
| 504 params->suffix = argv[i]; | |
| 505 } | |
| 506 } | |
| 507 } else { /* Double-dash. */ | |
| 508 arg = &arg[2]; | |
| 509 if (strcmp("best", arg) == 0) { | |
| 510 if (quality_set) { | |
| 511 fprintf(stderr, "quality already set\n"); | |
| 512 return COMMAND_INVALID; | |
| 513 } | |
| 514 quality_set = BROTLI_TRUE; | |
| 515 params->quality = 11; | |
| 516 } else if (strcmp("concatenated", arg) == 0) { | |
| 517 if (concatenated_set) { | |
| 518 fprintf(stderr, "argument -K / --concatenated already set\n"); | |
| 519 return COMMAND_INVALID; | |
| 520 } | |
| 521 concatenated_set = BROTLI_TRUE; | |
| 522 params->allow_concatenated = BROTLI_TRUE; | |
| 523 continue; | |
| 524 } else if (strcmp("decompress", arg) == 0) { | |
| 525 if (command_set) { | |
| 526 fprintf(stderr, "command already set when parsing --decompress\n"); | |
| 527 return COMMAND_INVALID; | |
| 528 } | |
| 529 command_set = BROTLI_TRUE; | |
| 530 command = COMMAND_DECOMPRESS; | |
| 531 } else if (strcmp("force", arg) == 0) { | |
| 532 if (params->force_overwrite) { | |
| 533 fprintf(stderr, "force output overwrite already set\n"); | |
| 534 return COMMAND_INVALID; | |
| 535 } | |
| 536 params->force_overwrite = BROTLI_TRUE; | |
| 537 } else if (strcmp("help", arg) == 0) { | |
| 538 /* Don't parse further. */ | |
| 539 return COMMAND_HELP; | |
| 540 } else if (strcmp("keep", arg) == 0) { | |
| 541 if (keep_set) { | |
| 542 fprintf(stderr, "argument --rm / -j or --keep / -k already set\n"); | |
| 543 return COMMAND_INVALID; | |
| 544 } | |
| 545 keep_set = BROTLI_TRUE; | |
| 546 params->junk_source = BROTLI_FALSE; | |
| 547 } else if (strcmp("no-copy-stat", arg) == 0) { | |
| 548 if (!params->copy_stat) { | |
| 549 fprintf(stderr, "argument --no-copy-stat / -n already set\n"); | |
| 550 return COMMAND_INVALID; | |
| 551 } | |
| 552 params->copy_stat = BROTLI_FALSE; | |
| 553 } else if (strcmp("rm", arg) == 0) { | |
| 554 if (keep_set) { | |
| 555 fprintf(stderr, "argument --rm / -j or --keep / -k already set\n"); | |
| 556 return COMMAND_INVALID; | |
| 557 } | |
| 558 keep_set = BROTLI_TRUE; | |
| 559 params->junk_source = BROTLI_TRUE; | |
| 560 } else if (strcmp("squash", arg) == 0) { | |
| 561 if (squash_set) { | |
| 562 fprintf(stderr, "argument --squash / -s already set\n"); | |
| 563 return COMMAND_INVALID; | |
| 564 } | |
| 565 squash_set = BROTLI_TRUE; | |
| 566 params->reject_uncompressible = BROTLI_TRUE; | |
| 567 continue; | |
| 568 } else if (strcmp("stdout", arg) == 0) { | |
| 569 if (output_set) { | |
| 570 fprintf(stderr, "write to standard output already set\n"); | |
| 571 return COMMAND_INVALID; | |
| 572 } | |
| 573 output_set = BROTLI_TRUE; | |
| 574 params->write_to_stdout = BROTLI_TRUE; | |
| 575 } else if (strcmp("test", arg) == 0) { | |
| 576 if (command_set) { | |
| 577 fprintf(stderr, "command already set when parsing --test\n"); | |
| 578 return COMMAND_INVALID; | |
| 579 } | |
| 580 command_set = BROTLI_TRUE; | |
| 581 command = COMMAND_TEST_INTEGRITY; | |
| 582 } else if (strcmp("verbose", arg) == 0) { | |
| 583 if (params->verbosity > 0) { | |
| 584 fprintf(stderr, "argument --verbose / -v already set\n"); | |
| 585 return COMMAND_INVALID; | |
| 586 } | |
| 587 params->verbosity = 1; | |
| 588 } else if (strcmp("version", arg) == 0) { | |
| 589 /* Don't parse further. */ | |
| 590 return COMMAND_VERSION; | |
| 591 } else { | |
| 592 /* key=value */ | |
| 593 const char* value = strchr(arg, '='); | |
| 594 size_t key_len; | |
| 595 if (!value || value[1] == 0) { | |
| 596 fprintf(stderr, "must pass the parameter as --%s=value\n", arg); | |
| 597 return COMMAND_INVALID; | |
| 598 } | |
| 599 key_len = (size_t)(value - arg); | |
| 600 value++; | |
| 601 if (strncmp("comment", arg, key_len) == 0) { | |
| 602 if (comment_set) { | |
| 603 fprintf(stderr, "comment already set\n"); | |
| 604 return COMMAND_INVALID; | |
| 605 } | |
| 606 params->comment_len = MAX_COMMENT_LEN; | |
| 607 if (!ParseBase64(value, params->comment, ¶ms->comment_len)) { | |
| 608 fprintf(stderr, "invalid base64-encoded comment\n"); | |
| 609 return COMMAND_INVALID; | |
| 610 } | |
| 611 comment_set = BROTLI_TRUE; | |
| 612 } else if (strncmp("dictionary", arg, key_len) == 0) { | |
| 613 if (params->dictionary_path) { | |
| 614 fprintf(stderr, "dictionary path already set\n"); | |
| 615 return COMMAND_INVALID; | |
| 616 } | |
| 617 params->dictionary_path = value; | |
| 618 } else if (strncmp("lgwin", arg, key_len) == 0) { | |
| 619 if (lgwin_set) { | |
| 620 fprintf(stderr, "lgwin parameter already set\n"); | |
| 621 return COMMAND_INVALID; | |
| 622 } | |
| 623 lgwin_set = ParseInt(value, 0, | |
| 624 BROTLI_MAX_WINDOW_BITS, ¶ms->lgwin); | |
| 625 if (!lgwin_set) { | |
| 626 fprintf(stderr, "error parsing lgwin value [%s]\n", value); | |
| 627 return COMMAND_INVALID; | |
| 628 } | |
| 629 if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) { | |
| 630 fprintf(stderr, | |
| 631 "lgwin parameter (%d) smaller than the minimum (%d)\n", | |
| 632 params->lgwin, BROTLI_MIN_WINDOW_BITS); | |
| 633 return COMMAND_INVALID; | |
| 634 } | |
| 635 } else if (strncmp("large_window", arg, key_len) == 0) { | |
| 636 /* This option is intentionally not mentioned in help. */ | |
| 637 if (lgwin_set) { | |
| 638 fprintf(stderr, "lgwin parameter already set\n"); | |
| 639 return COMMAND_INVALID; | |
| 640 } | |
| 641 lgwin_set = ParseInt(value, 0, | |
| 642 BROTLI_LARGE_MAX_WINDOW_BITS, ¶ms->lgwin); | |
| 643 if (!lgwin_set) { | |
| 644 fprintf(stderr, "error parsing lgwin value [%s]\n", value); | |
| 645 return COMMAND_INVALID; | |
| 646 } | |
| 647 if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) { | |
| 648 fprintf(stderr, | |
| 649 "lgwin parameter (%d) smaller than the minimum (%d)\n", | |
| 650 params->lgwin, BROTLI_MIN_WINDOW_BITS); | |
| 651 return COMMAND_INVALID; | |
| 652 } | |
| 653 } else if (strncmp("output", arg, key_len) == 0) { | |
| 654 if (output_set) { | |
| 655 fprintf(stderr, | |
| 656 "write to standard output already set (--output)\n"); | |
| 657 return COMMAND_INVALID; | |
| 658 } | |
| 659 params->output_path = value; | |
| 660 } else if (strncmp("quality", arg, key_len) == 0) { | |
| 661 if (quality_set) { | |
| 662 fprintf(stderr, "quality already set\n"); | |
| 663 return COMMAND_INVALID; | |
| 664 } | |
| 665 quality_set = ParseInt(value, BROTLI_MIN_QUALITY, | |
| 666 BROTLI_MAX_QUALITY, ¶ms->quality); | |
| 667 if (!quality_set) { | |
| 668 fprintf(stderr, "error parsing quality value [%s]\n", value); | |
| 669 return COMMAND_INVALID; | |
| 670 } | |
| 671 } else if (strncmp("suffix", arg, key_len) == 0) { | |
| 672 if (suffix_set) { | |
| 673 fprintf(stderr, "suffix already set\n"); | |
| 674 return COMMAND_INVALID; | |
| 675 } | |
| 676 suffix_set = BROTLI_TRUE; | |
| 677 params->suffix = value; | |
| 678 } else { | |
| 679 fprintf(stderr, "invalid parameter: [%s]\n", arg); | |
| 680 return COMMAND_INVALID; | |
| 681 } | |
| 682 } | |
| 683 } | |
| 684 } | |
| 685 | |
| 686 params->input_count = input_count; | |
| 687 params->longest_path_len = longest_path_len; | |
| 688 params->decompress = (command == COMMAND_DECOMPRESS); | |
| 689 params->test_integrity = (command == COMMAND_TEST_INTEGRITY); | |
| 690 | |
| 691 if (input_count > 1 && output_set) return COMMAND_INVALID; | |
| 692 if (params->test_integrity) { | |
| 693 if (params->output_path) return COMMAND_INVALID; | |
| 694 if (params->write_to_stdout) return COMMAND_INVALID; | |
| 695 } | |
| 696 if (params->reject_uncompressible && params->write_to_stdout) { | |
| 697 return COMMAND_INVALID; | |
| 698 } | |
| 699 if (strchr(params->suffix, '/') || strchr(params->suffix, '\\')) { | |
| 700 return COMMAND_INVALID; | |
| 701 } | |
| 702 if (!params->decompress && params->allow_concatenated) { | |
| 703 return COMMAND_INVALID; | |
| 704 } | |
| 705 if (params->allow_concatenated && params->comment_len) { | |
| 706 return COMMAND_INVALID; | |
| 707 } | |
| 708 | |
| 709 return command; | |
| 710 } | |
| 711 | |
| 712 static void PrintVersion(void) { | |
| 713 int major = BROTLI_VERSION_MAJOR; | |
| 714 int minor = BROTLI_VERSION_MINOR; | |
| 715 int patch = BROTLI_VERSION_PATCH; | |
| 716 fprintf(stdout, "brotli %d.%d.%d\n", major, minor, patch); | |
| 717 } | |
| 718 | |
| 719 static void PrintHelp(const char* name, BROTLI_BOOL error) { | |
| 720 FILE* media = error ? stderr : stdout; | |
| 721 /* String is cut to pieces with length less than 509, to conform C90 spec. */ | |
| 722 fprintf(media, | |
| 723 "Usage: %s [OPTION]... [FILE]...\n", | |
| 724 name); | |
| 725 fprintf(media, | |
| 726 "Options:\n" | |
| 727 " -# compression level (0-9)\n" | |
| 728 " -c, --stdout write on standard output\n" | |
| 729 " -d, --decompress decompress\n" | |
| 730 " -f, --force force output file overwrite\n" | |
| 731 " -h, --help display this help and exit\n"); | |
| 732 fprintf(media, | |
| 733 " -j, --rm remove source file(s)\n" | |
| 734 " -s, --squash remove destination file if larger than source\n" | |
| 735 " -k, --keep keep source file(s) (default)\n" | |
| 736 " -n, --no-copy-stat do not copy source file(s) attributes\n" | |
| 737 " -o FILE, --output=FILE output file (only if 1 input file)\n"); | |
| 738 fprintf(media, | |
| 739 " -q NUM, --quality=NUM compression level (%d-%d)\n", | |
| 740 BROTLI_MIN_QUALITY, BROTLI_MAX_QUALITY); | |
| 741 fprintf(media, | |
| 742 " -t, --test test compressed file integrity\n" | |
| 743 " -v, --verbose verbose mode\n"); | |
| 744 fprintf(media, | |
| 745 " -w NUM, --lgwin=NUM set LZ77 window size (0, %d-%d)\n" | |
| 746 " window size = 2**NUM - 16\n" | |
| 747 " 0 lets compressor choose the optimal value\n", | |
| 748 BROTLI_MIN_WINDOW_BITS, BROTLI_MAX_WINDOW_BITS); | |
| 749 fprintf(media, | |
| 750 " --large_window=NUM use incompatible large-window brotli\n" | |
| 751 " bitstream with window size (0, %d-%d)\n" | |
| 752 " WARNING: this format is not compatible\n" | |
| 753 " with brotli RFC 7932 and may not be\n" | |
| 754 " decodable with regular brotli decoders\n", | |
| 755 BROTLI_MIN_WINDOW_BITS, BROTLI_LARGE_MAX_WINDOW_BITS); | |
| 756 fprintf(media, | |
| 757 " -C B64, --comment=B64 set comment; argument is base64-decoded first;\n" | |
| 758 " (maximal decoded length: %d)\n" | |
| 759 " when decoding: check stream comment;\n" | |
| 760 " when encoding: embed comment (fingerprint)\n", | |
| 761 MAX_COMMENT_LEN); | |
| 762 fprintf(media, | |
| 763 " -D FILE, --dictionary=FILE use FILE as raw (LZ77) dictionary\n" | |
| 764 " -K, --concatenated allows concatenated brotli streams as input\n"); | |
| 765 fprintf(media, | |
| 766 " -S SUF, --suffix=SUF output file suffix (default:'%s')\n", | |
| 767 DEFAULT_SUFFIX); | |
| 768 fprintf(media, | |
| 769 " -V, --version display version and exit\n" | |
| 770 " -Z, --best use best compression level (11) (default)\n" | |
| 771 "Simple options could be coalesced, i.e. '-9kf' is equivalent to '-9 -k -f'.\n" | |
| 772 "With no FILE, or when FILE is -, read standard input.\n" | |
| 773 "All arguments after '--' are treated as files.\n"); | |
| 774 } | |
| 775 | |
| 776 static const char* PrintablePath(const char* path) { | |
| 777 return path ? path : "con"; | |
| 778 } | |
| 779 | |
| 780 static BROTLI_BOOL OpenInputFile(const char* input_path, FILE** f) { | |
| 781 *f = NULL; | |
| 782 if (!input_path) { | |
| 783 *f = fdopen(MAKE_BINARY(STDIN_FILENO), "rb"); | |
| 784 return BROTLI_TRUE; | |
| 785 } | |
| 786 *f = fopen(input_path, "rb"); | |
| 787 if (!*f) { | |
| 788 fprintf(stderr, "failed to open input file [%s]: %s\n", | |
| 789 PrintablePath(input_path), strerror(errno)); | |
| 790 return BROTLI_FALSE; | |
| 791 } | |
| 792 return BROTLI_TRUE; | |
| 793 } | |
| 794 | |
| 795 static BROTLI_BOOL OpenOutputFile(const char* output_path, FILE** f, | |
| 796 BROTLI_BOOL force) { | |
| 797 int fd; | |
| 798 *f = NULL; | |
| 799 if (!output_path) { | |
| 800 *f = fdopen(MAKE_BINARY(STDOUT_FILENO), "wb"); | |
| 801 return BROTLI_TRUE; | |
| 802 } | |
| 803 fd = open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC, | |
| 804 S_IRUSR | S_IWUSR); | |
| 805 if (fd < 0) { | |
| 806 fprintf(stderr, "failed to open output file [%s]: %s\n", | |
| 807 PrintablePath(output_path), strerror(errno)); | |
| 808 return BROTLI_FALSE; | |
| 809 } | |
| 810 *f = fdopen(fd, "wb"); | |
| 811 if (!*f) { | |
| 812 fprintf(stderr, "failed to open output file [%s]: %s\n", | |
| 813 PrintablePath(output_path), strerror(errno)); | |
| 814 return BROTLI_FALSE; | |
| 815 } | |
| 816 return BROTLI_TRUE; | |
| 817 } | |
| 818 | |
| 819 static int64_t FileSize(const char* path) { | |
| 820 FILE* f = fopen(path, "rb"); | |
| 821 int64_t retval; | |
| 822 if (f == NULL) { | |
| 823 return -1; | |
| 824 } | |
| 825 if (fseek(f, 0L, SEEK_END) != 0) { | |
| 826 fclose(f); | |
| 827 return -1; | |
| 828 } | |
| 829 retval = ftell(f); | |
| 830 if (fclose(f) != 0) { | |
| 831 return -1; | |
| 832 } | |
| 833 return retval; | |
| 834 } | |
| 835 | |
| 836 static int CopyTimeStat(const struct stat* statbuf, const char* output_path) { | |
| 837 #if HAVE_UTIMENSAT | |
| 838 struct timespec times[2]; | |
| 839 times[0].tv_sec = statbuf->st_atime; | |
| 840 times[0].tv_nsec = ATIME_NSEC(statbuf); | |
| 841 times[1].tv_sec = statbuf->st_mtime; | |
| 842 times[1].tv_nsec = MTIME_NSEC(statbuf); | |
| 843 return utimensat(AT_FDCWD, output_path, times, AT_SYMLINK_NOFOLLOW); | |
| 844 #else | |
| 845 struct utimbuf times; | |
| 846 times.actime = statbuf->st_atime; | |
| 847 times.modtime = statbuf->st_mtime; | |
| 848 return utime(output_path, ×); | |
| 849 #endif | |
| 850 } | |
| 851 | |
| 852 /* Copy file times and permissions. | |
| 853 TODO(eustas): this is a "best effort" implementation; honest cross-platform | |
| 854 fully featured implementation is way too hacky; add more hacks by request. */ | |
| 855 static void CopyStat(const char* input_path, const char* output_path) { | |
| 856 struct stat statbuf; | |
| 857 int res; | |
| 858 if (input_path == 0 || output_path == 0) { | |
| 859 return; | |
| 860 } | |
| 861 if (stat(input_path, &statbuf) != 0) { | |
| 862 return; | |
| 863 } | |
| 864 res = CopyTimeStat(&statbuf, output_path); | |
| 865 res = chmod(output_path, statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); | |
| 866 if (res != 0) { | |
| 867 fprintf(stderr, "setting access bits failed for [%s]: %s\n", | |
| 868 PrintablePath(output_path), strerror(errno)); | |
| 869 } | |
| 870 res = chown(output_path, (uid_t)-1, statbuf.st_gid); | |
| 871 if (res != 0) { | |
| 872 fprintf(stderr, "setting group failed for [%s]: %s\n", | |
| 873 PrintablePath(output_path), strerror(errno)); | |
| 874 } | |
| 875 res = chown(output_path, statbuf.st_uid, (gid_t)-1); | |
| 876 if (res != 0) { | |
| 877 fprintf(stderr, "setting user failed for [%s]: %s\n", | |
| 878 PrintablePath(output_path), strerror(errno)); | |
| 879 } | |
| 880 } | |
| 881 | |
| 882 /* Result ownership is passed to caller. | |
| 883 |*dictionary_size| is set to resulting buffer size. */ | |
| 884 static BROTLI_BOOL ReadDictionary(Context* context, Command command) { | |
| 885 static const int kMaxDictionarySize = | |
| 886 BROTLI_MAX_DISTANCE - BROTLI_MAX_BACKWARD_LIMIT(24); | |
| 887 FILE* f; | |
| 888 int64_t file_size_64; | |
| 889 uint8_t* buffer; | |
| 890 size_t bytes_read; | |
| 891 | |
| 892 if (context->dictionary_path == NULL) return BROTLI_TRUE; | |
| 893 f = fopen(context->dictionary_path, "rb"); | |
| 894 if (f == NULL) { | |
| 895 fprintf(stderr, "failed to open dictionary file [%s]: %s\n", | |
| 896 PrintablePath(context->dictionary_path), strerror(errno)); | |
| 897 return BROTLI_FALSE; | |
| 898 } | |
| 899 | |
| 900 file_size_64 = FileSize(context->dictionary_path); | |
| 901 if (file_size_64 == -1) { | |
| 902 fprintf(stderr, "could not get size of dictionary file [%s]", | |
| 903 PrintablePath(context->dictionary_path)); | |
| 904 fclose(f); | |
| 905 return BROTLI_FALSE; | |
| 906 } | |
| 907 | |
| 908 if (file_size_64 > kMaxDictionarySize) { | |
| 909 fprintf(stderr, "dictionary [%s] is larger than maximum allowed: %d\n", | |
| 910 PrintablePath(context->dictionary_path), kMaxDictionarySize); | |
| 911 fclose(f); | |
| 912 return BROTLI_FALSE; | |
| 913 } | |
| 914 context->dictionary_size = (size_t)file_size_64; | |
| 915 | |
| 916 buffer = (uint8_t*)malloc(context->dictionary_size); | |
| 917 if (!buffer) { | |
| 918 fprintf(stderr, "could not read dictionary: out of memory\n"); | |
| 919 fclose(f); | |
| 920 return BROTLI_FALSE; | |
| 921 } | |
| 922 bytes_read = fread(buffer, sizeof(uint8_t), context->dictionary_size, f); | |
| 923 if (bytes_read != context->dictionary_size) { | |
| 924 free(buffer); | |
| 925 fprintf(stderr, "failed to read dictionary [%s]: %s\n", | |
| 926 PrintablePath(context->dictionary_path), strerror(errno)); | |
| 927 fclose(f); | |
| 928 return BROTLI_FALSE; | |
| 929 } | |
| 930 fclose(f); | |
| 931 context->dictionary = buffer; | |
| 932 if (command == COMMAND_COMPRESS) { | |
| 933 context->prepared_dictionary = BrotliEncoderPrepareDictionary( | |
| 934 BROTLI_SHARED_DICTIONARY_RAW, context->dictionary_size, | |
| 935 context->dictionary, BROTLI_MAX_QUALITY, NULL, NULL, NULL); | |
| 936 if (context->prepared_dictionary == NULL) { | |
| 937 fprintf(stderr, "failed to prepare dictionary [%s]\n", | |
| 938 PrintablePath(context->dictionary_path)); | |
| 939 return BROTLI_FALSE; | |
| 940 } | |
| 941 } | |
| 942 return BROTLI_TRUE; | |
| 943 } | |
| 944 | |
| 945 static BROTLI_BOOL NextFile(Context* context) { | |
| 946 const char* arg; | |
| 947 size_t arg_len; | |
| 948 | |
| 949 /* Iterator points to last used arg; increment to search for the next one. */ | |
| 950 context->iterator++; | |
| 951 | |
| 952 context->input_file_length = -1; | |
| 953 | |
| 954 /* No input path; read from console. */ | |
| 955 if (context->input_count == 0) { | |
| 956 if (context->iterator > 1) return BROTLI_FALSE; | |
| 957 context->current_input_path = NULL; | |
| 958 /* Either write to the specified path, or to console. */ | |
| 959 context->current_output_path = context->output_path; | |
| 960 return BROTLI_TRUE; | |
| 961 } | |
| 962 | |
| 963 /* Skip option arguments. */ | |
| 964 while (context->iterator == context->not_input_indices[context->ignore]) { | |
| 965 context->iterator++; | |
| 966 context->ignore++; | |
| 967 } | |
| 968 | |
| 969 /* All args are scanned already. */ | |
| 970 if (context->iterator >= context->argc) return BROTLI_FALSE; | |
| 971 | |
| 972 /* Iterator now points to the input file name. */ | |
| 973 arg = context->argv[context->iterator]; | |
| 974 arg_len = strlen(arg); | |
| 975 /* Read from console. */ | |
| 976 if (arg_len == 1 && arg[0] == '-') { | |
| 977 context->current_input_path = NULL; | |
| 978 context->current_output_path = context->output_path; | |
| 979 return BROTLI_TRUE; | |
| 980 } | |
| 981 | |
| 982 context->current_input_path = arg; | |
| 983 context->input_file_length = FileSize(arg); | |
| 984 context->current_output_path = context->output_path; | |
| 985 | |
| 986 if (context->output_path) return BROTLI_TRUE; | |
| 987 if (context->write_to_stdout) return BROTLI_TRUE; | |
| 988 | |
| 989 strcpy(context->modified_path, arg); | |
| 990 context->current_output_path = context->modified_path; | |
| 991 /* If output is not specified, input path suffix should match. */ | |
| 992 if (context->decompress) { | |
| 993 size_t suffix_len = strlen(context->suffix); | |
| 994 char* name = (char*)FileName(context->modified_path); | |
| 995 char* name_suffix; | |
| 996 size_t name_len = strlen(name); | |
| 997 if (name_len < suffix_len + 1) { | |
| 998 fprintf(stderr, "empty output file name for [%s] input file\n", | |
| 999 PrintablePath(arg)); | |
| 1000 context->iterator_error = BROTLI_TRUE; | |
| 1001 return BROTLI_FALSE; | |
| 1002 } | |
| 1003 name_suffix = name + name_len - suffix_len; | |
| 1004 if (strcmp(context->suffix, name_suffix) != 0) { | |
| 1005 fprintf(stderr, "input file [%s] suffix mismatch\n", | |
| 1006 PrintablePath(arg)); | |
| 1007 context->iterator_error = BROTLI_TRUE; | |
| 1008 return BROTLI_FALSE; | |
| 1009 } | |
| 1010 name_suffix[0] = 0; | |
| 1011 return BROTLI_TRUE; | |
| 1012 } else { | |
| 1013 strcpy(context->modified_path + arg_len, context->suffix); | |
| 1014 return BROTLI_TRUE; | |
| 1015 } | |
| 1016 } | |
| 1017 | |
| 1018 static BROTLI_BOOL OpenFiles(Context* context) { | |
| 1019 BROTLI_BOOL is_ok = OpenInputFile(context->current_input_path, &context->fin); | |
| 1020 if (!context->test_integrity && is_ok) { | |
| 1021 is_ok = OpenOutputFile( | |
| 1022 context->current_output_path, &context->fout, context->force_overwrite); | |
| 1023 } | |
| 1024 return is_ok; | |
| 1025 } | |
| 1026 | |
| 1027 static BROTLI_BOOL CloseFiles(Context* context, BROTLI_BOOL rm_input, | |
| 1028 BROTLI_BOOL rm_output) { | |
| 1029 BROTLI_BOOL is_ok = BROTLI_TRUE; | |
| 1030 if (!context->test_integrity && context->fout) { | |
| 1031 if (fclose(context->fout) != 0) { | |
| 1032 if (is_ok) { | |
| 1033 fprintf(stderr, "fclose failed [%s]: %s\n", | |
| 1034 PrintablePath(context->current_output_path), strerror(errno)); | |
| 1035 } | |
| 1036 is_ok = BROTLI_FALSE; | |
| 1037 } | |
| 1038 if (rm_output && context->current_output_path) { | |
| 1039 unlink(context->current_output_path); | |
| 1040 } | |
| 1041 | |
| 1042 /* TOCTOU violation, but otherwise it is impossible to set file times. */ | |
| 1043 if (!rm_output && is_ok && context->copy_stat) { | |
| 1044 CopyStat(context->current_input_path, context->current_output_path); | |
| 1045 } | |
| 1046 } | |
| 1047 | |
| 1048 if (context->fin) { | |
| 1049 if (fclose(context->fin) != 0) { | |
| 1050 if (is_ok) { | |
| 1051 fprintf(stderr, "fclose failed [%s]: %s\n", | |
| 1052 PrintablePath(context->current_input_path), strerror(errno)); | |
| 1053 } | |
| 1054 is_ok = BROTLI_FALSE; | |
| 1055 } | |
| 1056 } | |
| 1057 if (rm_input && context->current_input_path) { | |
| 1058 unlink(context->current_input_path); | |
| 1059 } | |
| 1060 | |
| 1061 context->fin = NULL; | |
| 1062 context->fout = NULL; | |
| 1063 | |
| 1064 return is_ok; | |
| 1065 } | |
| 1066 | |
| 1067 static const size_t kFileBufferSize = 1 << 19; | |
| 1068 | |
| 1069 static void InitializeBuffers(Context* context) { | |
| 1070 context->available_in = 0; | |
| 1071 context->next_in = NULL; | |
| 1072 context->available_out = kFileBufferSize; | |
| 1073 context->next_out = context->output; | |
| 1074 context->total_in = 0; | |
| 1075 context->total_out = 0; | |
| 1076 if (context->verbosity > 0) { | |
| 1077 context->start_time = clock(); | |
| 1078 } | |
| 1079 } | |
| 1080 | |
| 1081 /* This method might give the false-negative result. | |
| 1082 However, after an empty / incomplete read it should tell the truth. */ | |
| 1083 static BROTLI_BOOL HasMoreInput(Context* context) { | |
| 1084 return feof(context->fin) ? BROTLI_FALSE : BROTLI_TRUE; | |
| 1085 } | |
| 1086 | |
| 1087 static BROTLI_BOOL ProvideInput(Context* context) { | |
| 1088 context->available_in = | |
| 1089 fread(context->input, 1, kFileBufferSize, context->fin); | |
| 1090 context->total_in += context->available_in; | |
| 1091 context->next_in = context->input; | |
| 1092 if (ferror(context->fin)) { | |
| 1093 fprintf(stderr, "failed to read input [%s]: %s\n", | |
| 1094 PrintablePath(context->current_input_path), strerror(errno)); | |
| 1095 return BROTLI_FALSE; | |
| 1096 } | |
| 1097 return BROTLI_TRUE; | |
| 1098 } | |
| 1099 | |
| 1100 /* Internal: should be used only in Provide-/Flush-Output. */ | |
| 1101 static BROTLI_BOOL WriteOutput(Context* context) { | |
| 1102 size_t out_size = (size_t)(context->next_out - context->output); | |
| 1103 context->total_out += out_size; | |
| 1104 if (out_size == 0) return BROTLI_TRUE; | |
| 1105 if (context->test_integrity) return BROTLI_TRUE; | |
| 1106 | |
| 1107 fwrite(context->output, 1, out_size, context->fout); | |
| 1108 if (ferror(context->fout)) { | |
| 1109 fprintf(stderr, "failed to write output [%s]: %s\n", | |
| 1110 PrintablePath(context->current_output_path), strerror(errno)); | |
| 1111 return BROTLI_FALSE; | |
| 1112 } | |
| 1113 return BROTLI_TRUE; | |
| 1114 } | |
| 1115 | |
| 1116 static BROTLI_BOOL ProvideOutput(Context* context) { | |
| 1117 if (!WriteOutput(context)) return BROTLI_FALSE; | |
| 1118 context->available_out = kFileBufferSize; | |
| 1119 context->next_out = context->output; | |
| 1120 return BROTLI_TRUE; | |
| 1121 } | |
| 1122 | |
| 1123 static BROTLI_BOOL FlushOutput(Context* context) { | |
| 1124 if (!WriteOutput(context)) return BROTLI_FALSE; | |
| 1125 context->available_out = 0; | |
| 1126 context->next_out = context->output; | |
| 1127 return BROTLI_TRUE; | |
| 1128 } | |
| 1129 | |
| 1130 static void PrintBytes(size_t value) { | |
| 1131 if (value < 1024) { | |
| 1132 fprintf(stderr, "%d B", (int)value); | |
| 1133 } else if (value < 1048576) { | |
| 1134 fprintf(stderr, "%0.3f KiB", (double)value / 1024.0); | |
| 1135 } else if (value < 1073741824) { | |
| 1136 fprintf(stderr, "%0.3f MiB", (double)value / 1048576.0); | |
| 1137 } else { | |
| 1138 fprintf(stderr, "%0.3f GiB", (double)value / 1073741824.0); | |
| 1139 } | |
| 1140 } | |
| 1141 | |
| 1142 static void PrintFileProcessingProgress(Context* context) { | |
| 1143 fprintf(stderr, "[%s]: ", PrintablePath(context->current_input_path)); | |
| 1144 PrintBytes(context->total_in); | |
| 1145 fprintf(stderr, " -> "); | |
| 1146 PrintBytes(context->total_out); | |
| 1147 fprintf(stderr, " in %1.2f sec", (double)(context->end_time - context->start_time) / CLOCKS_PER_SEC); | |
| 1148 } | |
| 1149 | |
| 1150 static const char* PrettyDecoderErrorString(BrotliDecoderErrorCode code) { | |
| 1151 /* "_ERROR_domain_" is in only added in newer versions. If CLI is linked | |
| 1152 against older shared library, return error string as is; result might be | |
| 1153 a bit confusing, e.g. "RESERVED" instead of "FORMAT_RESERVED" */ | |
| 1154 const char* prefix = "_ERROR_"; | |
| 1155 size_t prefix_len = strlen(prefix); | |
| 1156 const char* error_str = BrotliDecoderErrorString(code); | |
| 1157 size_t error_len = strlen(error_str); | |
| 1158 if (error_len > prefix_len) { | |
| 1159 if (strncmp(error_str, prefix, prefix_len) == 0) { | |
| 1160 error_str += prefix_len; | |
| 1161 } | |
| 1162 } | |
| 1163 return error_str; | |
| 1164 } | |
| 1165 | |
| 1166 static void OnMetadataStart(void* opaque, size_t size) { | |
| 1167 Context* context = (Context*) opaque; | |
| 1168 if (context->comment_state == COMMENT_INIT) { | |
| 1169 if (context->comment_len != size) { | |
| 1170 context->comment_state = COMMENT_BAD; | |
| 1171 return; | |
| 1172 } | |
| 1173 context->comment_pos = 0; | |
| 1174 context->comment_state = COMMENT_READ; | |
| 1175 } | |
| 1176 } | |
| 1177 | |
| 1178 static void OnMetadataChunk(void* opaque, const uint8_t* data, size_t size) { | |
| 1179 Context* context = (Context*) opaque; | |
| 1180 if (context->comment_state == COMMENT_READ) { | |
| 1181 size_t i; | |
| 1182 for (i = 0; i < size; ++i) { | |
| 1183 if (context->comment_pos >= context->comment_len) { | |
| 1184 context->comment_state = COMMENT_BAD; | |
| 1185 return; | |
| 1186 } | |
| 1187 if (context->comment[context->comment_pos++] != data[i]) { | |
| 1188 context->comment_state = COMMENT_BAD; | |
| 1189 return; | |
| 1190 } | |
| 1191 } | |
| 1192 if (context->comment_pos == context->comment_len) { | |
| 1193 context->comment_state = COMMENT_OK; | |
| 1194 } | |
| 1195 } | |
| 1196 } | |
| 1197 | |
| 1198 static BROTLI_BOOL InitDecoder(Context* context) { | |
| 1199 context->decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL); | |
| 1200 if (!context->decoder) { | |
| 1201 fprintf(stderr, "out of memory\n"); | |
| 1202 return BROTLI_FALSE; | |
| 1203 } | |
| 1204 /* This allows decoding "large-window" streams. Though it creates | |
| 1205 fragmentation (new builds decode streams that old builds don't), | |
| 1206 it is better from used experience perspective. */ | |
| 1207 BrotliDecoderSetParameter( | |
| 1208 context->decoder, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1u); | |
| 1209 if (context->dictionary) { | |
| 1210 BrotliDecoderAttachDictionary(context->decoder, | |
| 1211 BROTLI_SHARED_DICTIONARY_RAW, context->dictionary_size, | |
| 1212 context->dictionary); | |
| 1213 } | |
| 1214 return BROTLI_TRUE; | |
| 1215 } | |
| 1216 | |
| 1217 static BROTLI_BOOL DecompressFile(Context* context) { | |
| 1218 BrotliDecoderState* s = context->decoder; | |
| 1219 BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; | |
| 1220 if (context->comment_len) { | |
| 1221 context->comment_state = COMMENT_INIT; | |
| 1222 BrotliDecoderSetMetadataCallbacks(s, &OnMetadataStart, &OnMetadataChunk, | |
| 1223 (void*)context); | |
| 1224 } else { | |
| 1225 context->comment_state = COMMENT_OK; | |
| 1226 } | |
| 1227 | |
| 1228 InitializeBuffers(context); | |
| 1229 for (;;) { | |
| 1230 /* Early check */ | |
| 1231 if (context->comment_state == COMMENT_BAD) { | |
| 1232 fprintf(stderr, "corrupt input [%s]\n", | |
| 1233 PrintablePath(context->current_input_path)); | |
| 1234 if (context->verbosity > 0) { | |
| 1235 fprintf(stderr, "reason: comment mismatch\n"); | |
| 1236 } | |
| 1237 return BROTLI_FALSE; | |
| 1238 } | |
| 1239 if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { | |
| 1240 if (!HasMoreInput(context)) { | |
| 1241 fprintf(stderr, "corrupt input [%s]\n", | |
| 1242 PrintablePath(context->current_input_path)); | |
| 1243 if (context->verbosity > 0) { | |
| 1244 fprintf(stderr, "reason: truncated input\n"); | |
| 1245 } | |
| 1246 return BROTLI_FALSE; | |
| 1247 } | |
| 1248 if (!ProvideInput(context)) return BROTLI_FALSE; | |
| 1249 } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { | |
| 1250 if (!ProvideOutput(context)) return BROTLI_FALSE; | |
| 1251 } else if (result == BROTLI_DECODER_RESULT_SUCCESS) { | |
| 1252 if (!FlushOutput(context)) return BROTLI_FALSE; | |
| 1253 BROTLI_BOOL has_more_input = (context->available_in != 0); | |
| 1254 int extra_char = EOF; | |
| 1255 if (!has_more_input) { | |
| 1256 extra_char = fgetc(context->fin); | |
| 1257 if (extra_char != EOF) { | |
| 1258 has_more_input = BROTLI_TRUE; | |
| 1259 context->input[0] = (uint8_t)extra_char; | |
| 1260 context->next_in = context->input; | |
| 1261 context->available_in = 1; | |
| 1262 } | |
| 1263 } | |
| 1264 if (has_more_input) { | |
| 1265 if (context->allow_concatenated) { | |
| 1266 if (context->verbosity > 0) { | |
| 1267 fprintf(stderr, "extra input\n"); | |
| 1268 } | |
| 1269 if (!ProvideOutput(context)) return BROTLI_FALSE; | |
| 1270 BrotliDecoderDestroyInstance(context->decoder); | |
| 1271 context->decoder = NULL; | |
| 1272 if (!InitDecoder(context)) return BROTLI_FALSE; | |
| 1273 s = context->decoder; | |
| 1274 } else { | |
| 1275 fprintf(stderr, "corrupt input [%s]\n", | |
| 1276 PrintablePath(context->current_input_path)); | |
| 1277 if (context->verbosity > 0) { | |
| 1278 fprintf(stderr, "reason: extra input\n"); | |
| 1279 } | |
| 1280 return BROTLI_FALSE; | |
| 1281 } | |
| 1282 } else { | |
| 1283 if (context->verbosity > 0) { | |
| 1284 context->end_time = clock(); | |
| 1285 fprintf(stderr, "Decompressed "); | |
| 1286 PrintFileProcessingProgress(context); | |
| 1287 fprintf(stderr, "\n"); | |
| 1288 } | |
| 1289 /* Final check */ | |
| 1290 if (context->comment_state != COMMENT_OK) { | |
| 1291 fprintf(stderr, "corrupt input [%s]\n", | |
| 1292 PrintablePath(context->current_input_path)); | |
| 1293 if (context->verbosity > 0) { | |
| 1294 fprintf(stderr, "reason: comment mismatch\n"); | |
| 1295 } | |
| 1296 } | |
| 1297 return BROTLI_TRUE; | |
| 1298 } | |
| 1299 } else { /* result == BROTLI_DECODER_RESULT_ERROR */ | |
| 1300 fprintf(stderr, "corrupt input [%s]\n", | |
| 1301 PrintablePath(context->current_input_path)); | |
| 1302 if (context->verbosity > 0) { | |
| 1303 BrotliDecoderErrorCode error = BrotliDecoderGetErrorCode(s); | |
| 1304 const char* error_str = PrettyDecoderErrorString(error); | |
| 1305 fprintf(stderr, "reason: %s (%d)\n", error_str, error); | |
| 1306 } | |
| 1307 return BROTLI_FALSE; | |
| 1308 } | |
| 1309 | |
| 1310 result = BrotliDecoderDecompressStream(s, &context->available_in, | |
| 1311 &context->next_in, &context->available_out, &context->next_out, 0); | |
| 1312 } | |
| 1313 } | |
| 1314 | |
| 1315 static BROTLI_BOOL DecompressFiles(Context* context) { | |
| 1316 while (NextFile(context)) { | |
| 1317 BROTLI_BOOL is_ok = BROTLI_TRUE; | |
| 1318 BROTLI_BOOL rm_input = BROTLI_FALSE; | |
| 1319 BROTLI_BOOL rm_output = BROTLI_TRUE; | |
| 1320 if (!InitDecoder(context)) return BROTLI_FALSE; | |
| 1321 is_ok = OpenFiles(context); | |
| 1322 if (is_ok && !context->current_input_path && | |
| 1323 !context->force_overwrite && isatty(STDIN_FILENO)) { | |
| 1324 fprintf(stderr, "Use -h help. Use -f to force input from a terminal.\n"); | |
| 1325 is_ok = BROTLI_FALSE; | |
| 1326 } | |
| 1327 if (is_ok) is_ok = DecompressFile(context); | |
| 1328 if (context->decoder) BrotliDecoderDestroyInstance(context->decoder); | |
| 1329 context->decoder = NULL; | |
| 1330 rm_output = !is_ok; | |
| 1331 rm_input = !rm_output && context->junk_source; | |
| 1332 if (!CloseFiles(context, rm_input, rm_output)) is_ok = BROTLI_FALSE; | |
| 1333 if (!is_ok) return BROTLI_FALSE; | |
| 1334 } | |
| 1335 return BROTLI_TRUE; | |
| 1336 } | |
| 1337 | |
| 1338 static BROTLI_BOOL CompressFile(Context* context, BrotliEncoderState* s) { | |
| 1339 BROTLI_BOOL is_eof = BROTLI_FALSE; | |
| 1340 BROTLI_BOOL prologue = !!context->comment_len; | |
| 1341 InitializeBuffers(context); | |
| 1342 for (;;) { | |
| 1343 if (context->available_in == 0 && !is_eof) { | |
| 1344 if (!ProvideInput(context)) return BROTLI_FALSE; | |
| 1345 is_eof = !HasMoreInput(context); | |
| 1346 } | |
| 1347 | |
| 1348 if (prologue) { | |
| 1349 prologue = BROTLI_FALSE; | |
| 1350 const uint8_t* next_meta = context->comment; | |
| 1351 size_t available_meta = context->comment_len; | |
| 1352 if (!BrotliEncoderCompressStream(s, | |
| 1353 BROTLI_OPERATION_EMIT_METADATA, | |
| 1354 &available_meta, &next_meta, | |
| 1355 &context->available_out, &context->next_out, NULL)) { | |
| 1356 /* Should detect OOM? */ | |
| 1357 fprintf(stderr, "failed to emit metadata [%s]\n", | |
| 1358 PrintablePath(context->current_input_path)); | |
| 1359 return BROTLI_FALSE; | |
| 1360 } | |
| 1361 if (available_meta != 0) { | |
| 1362 fprintf(stderr, "failed to emit metadata [%s]\n", | |
| 1363 PrintablePath(context->current_input_path)); | |
| 1364 return BROTLI_FALSE; | |
| 1365 } | |
| 1366 } else { | |
| 1367 if (!BrotliEncoderCompressStream(s, | |
| 1368 is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, | |
| 1369 &context->available_in, &context->next_in, | |
| 1370 &context->available_out, &context->next_out, NULL)) { | |
| 1371 /* Should detect OOM? */ | |
| 1372 fprintf(stderr, "failed to compress data [%s]\n", | |
| 1373 PrintablePath(context->current_input_path)); | |
| 1374 return BROTLI_FALSE; | |
| 1375 } | |
| 1376 } | |
| 1377 | |
| 1378 if (context->available_out == 0) { | |
| 1379 if (!ProvideOutput(context)) return BROTLI_FALSE; | |
| 1380 } | |
| 1381 | |
| 1382 if (BrotliEncoderIsFinished(s)) { | |
| 1383 if (!FlushOutput(context)) return BROTLI_FALSE; | |
| 1384 if (context->verbosity > 0) { | |
| 1385 context->end_time = clock(); | |
| 1386 fprintf(stderr, "Compressed "); | |
| 1387 PrintFileProcessingProgress(context); | |
| 1388 fprintf(stderr, "\n"); | |
| 1389 } | |
| 1390 return BROTLI_TRUE; | |
| 1391 } | |
| 1392 } | |
| 1393 } | |
| 1394 | |
| 1395 static BROTLI_BOOL CompressFiles(Context* context) { | |
| 1396 while (NextFile(context)) { | |
| 1397 BROTLI_BOOL is_ok = BROTLI_TRUE; | |
| 1398 BROTLI_BOOL rm_input = BROTLI_FALSE; | |
| 1399 BROTLI_BOOL rm_output = BROTLI_TRUE; | |
| 1400 BrotliEncoderState* s = BrotliEncoderCreateInstance(NULL, NULL, NULL); | |
| 1401 if (!s) { | |
| 1402 fprintf(stderr, "out of memory\n"); | |
| 1403 return BROTLI_FALSE; | |
| 1404 } | |
| 1405 BrotliEncoderSetParameter(s, | |
| 1406 BROTLI_PARAM_QUALITY, (uint32_t)context->quality); | |
| 1407 if (context->lgwin > 0) { | |
| 1408 /* Specified by user. */ | |
| 1409 /* Do not enable "large-window" extension, if not required. */ | |
| 1410 if (context->lgwin > BROTLI_MAX_WINDOW_BITS) { | |
| 1411 BrotliEncoderSetParameter(s, BROTLI_PARAM_LARGE_WINDOW, 1u); | |
| 1412 } | |
| 1413 BrotliEncoderSetParameter(s, | |
| 1414 BROTLI_PARAM_LGWIN, (uint32_t)context->lgwin); | |
| 1415 } else { | |
| 1416 /* 0, or not specified by user; could be chosen by compressor. */ | |
| 1417 uint32_t lgwin = DEFAULT_LGWIN; | |
| 1418 /* Use file size to limit lgwin. */ | |
| 1419 if (context->input_file_length >= 0) { | |
| 1420 lgwin = BROTLI_MIN_WINDOW_BITS; | |
| 1421 while (BROTLI_MAX_BACKWARD_LIMIT(lgwin) < | |
| 1422 (uint64_t)context->input_file_length) { | |
| 1423 lgwin++; | |
| 1424 if (lgwin == BROTLI_MAX_WINDOW_BITS) break; | |
| 1425 } | |
| 1426 } | |
| 1427 BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, lgwin); | |
| 1428 } | |
| 1429 if (context->input_file_length > 0) { | |
| 1430 uint32_t size_hint = context->input_file_length < (1 << 30) ? | |
| 1431 (uint32_t)context->input_file_length : (1u << 30); | |
| 1432 BrotliEncoderSetParameter(s, BROTLI_PARAM_SIZE_HINT, size_hint); | |
| 1433 } | |
| 1434 if (context->dictionary) { | |
| 1435 BrotliEncoderAttachPreparedDictionary(s, context->prepared_dictionary); | |
| 1436 } | |
| 1437 is_ok = OpenFiles(context); | |
| 1438 if (is_ok && !context->current_output_path && | |
| 1439 !context->force_overwrite && isatty(STDOUT_FILENO)) { | |
| 1440 fprintf(stderr, "Use -h help. Use -f to force output to a terminal.\n"); | |
| 1441 is_ok = BROTLI_FALSE; | |
| 1442 } | |
| 1443 if (is_ok) is_ok = CompressFile(context, s); | |
| 1444 BrotliEncoderDestroyInstance(s); | |
| 1445 rm_output = !is_ok; | |
| 1446 if (is_ok && context->reject_uncompressible) { | |
| 1447 if (context->total_out >= context->total_in) { | |
| 1448 rm_output = BROTLI_TRUE; | |
| 1449 if (context->verbosity > 0) { | |
| 1450 fprintf(stderr, "Output is larger than input\n"); | |
| 1451 } | |
| 1452 } | |
| 1453 } | |
| 1454 rm_input = !rm_output && context->junk_source; | |
| 1455 if (!CloseFiles(context, rm_input, rm_output)) is_ok = BROTLI_FALSE; | |
| 1456 if (!is_ok) return BROTLI_FALSE; | |
| 1457 } | |
| 1458 return BROTLI_TRUE; | |
| 1459 } | |
| 1460 | |
| 1461 int main(int argc, char** argv) { | |
| 1462 Command command; | |
| 1463 Context context; | |
| 1464 BROTLI_BOOL is_ok = BROTLI_TRUE; | |
| 1465 int i; | |
| 1466 | |
| 1467 context.quality = 11; | |
| 1468 context.lgwin = -1; | |
| 1469 context.verbosity = 0; | |
| 1470 context.comment_len = 0; | |
| 1471 context.force_overwrite = BROTLI_FALSE; | |
| 1472 context.junk_source = BROTLI_FALSE; | |
| 1473 context.reject_uncompressible = BROTLI_FALSE; | |
| 1474 context.copy_stat = BROTLI_TRUE; | |
| 1475 context.test_integrity = BROTLI_FALSE; | |
| 1476 context.write_to_stdout = BROTLI_FALSE; | |
| 1477 context.decompress = BROTLI_FALSE; | |
| 1478 context.large_window = BROTLI_FALSE; | |
| 1479 context.allow_concatenated = BROTLI_FALSE; | |
| 1480 context.output_path = NULL; | |
| 1481 context.dictionary_path = NULL; | |
| 1482 context.suffix = DEFAULT_SUFFIX; | |
| 1483 for (i = 0; i < MAX_OPTIONS; ++i) context.not_input_indices[i] = 0; | |
| 1484 context.longest_path_len = 1; | |
| 1485 context.input_count = 0; | |
| 1486 | |
| 1487 context.argc = argc; | |
| 1488 context.argv = argv; | |
| 1489 context.dictionary = NULL; | |
| 1490 context.dictionary_size = 0; | |
| 1491 context.decoder = NULL; | |
| 1492 context.prepared_dictionary = NULL; | |
| 1493 context.modified_path = NULL; | |
| 1494 context.iterator = 0; | |
| 1495 context.ignore = 0; | |
| 1496 context.iterator_error = BROTLI_FALSE; | |
| 1497 context.buffer = NULL; | |
| 1498 context.current_input_path = NULL; | |
| 1499 context.current_output_path = NULL; | |
| 1500 context.fin = NULL; | |
| 1501 context.fout = NULL; | |
| 1502 | |
| 1503 command = ParseParams(&context); | |
| 1504 | |
| 1505 if (command == COMMAND_COMPRESS || command == COMMAND_DECOMPRESS || | |
| 1506 command == COMMAND_TEST_INTEGRITY) { | |
| 1507 if (!ReadDictionary(&context, command)) is_ok = BROTLI_FALSE; | |
| 1508 if (is_ok) { | |
| 1509 size_t modified_path_len = | |
| 1510 context.longest_path_len + strlen(context.suffix) + 1; | |
| 1511 context.modified_path = (char*)malloc(modified_path_len); | |
| 1512 context.buffer = (uint8_t*)malloc(kFileBufferSize * 2); | |
| 1513 if (!context.modified_path || !context.buffer) { | |
| 1514 fprintf(stderr, "out of memory\n"); | |
| 1515 is_ok = BROTLI_FALSE; | |
| 1516 } else { | |
| 1517 context.input = context.buffer; | |
| 1518 context.output = context.buffer + kFileBufferSize; | |
| 1519 } | |
| 1520 } | |
| 1521 } | |
| 1522 | |
| 1523 if (!is_ok) command = COMMAND_NOOP; | |
| 1524 | |
| 1525 switch (command) { | |
| 1526 case COMMAND_NOOP: | |
| 1527 break; | |
| 1528 | |
| 1529 case COMMAND_VERSION: | |
| 1530 PrintVersion(); | |
| 1531 break; | |
| 1532 | |
| 1533 case COMMAND_COMPRESS: | |
| 1534 is_ok = CompressFiles(&context); | |
| 1535 break; | |
| 1536 | |
| 1537 case COMMAND_DECOMPRESS: | |
| 1538 case COMMAND_TEST_INTEGRITY: | |
| 1539 is_ok = DecompressFiles(&context); | |
| 1540 break; | |
| 1541 | |
| 1542 case COMMAND_HELP: | |
| 1543 case COMMAND_INVALID: | |
| 1544 default: | |
| 1545 is_ok = (command == COMMAND_HELP); | |
| 1546 PrintHelp(FileName(argv[0]), is_ok); | |
| 1547 break; | |
| 1548 } | |
| 1549 | |
| 1550 if (context.iterator_error) is_ok = BROTLI_FALSE; | |
| 1551 | |
| 1552 BrotliEncoderDestroyPreparedDictionary(context.prepared_dictionary); | |
| 1553 free(context.dictionary); | |
| 1554 free(context.modified_path); | |
| 1555 free(context.buffer); | |
| 1556 | |
| 1557 if (!is_ok) exit(1); | |
| 1558 return 0; | |
| 1559 } |
