diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/brotli/c/tools/brotli.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,1559 @@
+/* Copyright 2014 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/* Command line interface for Brotli library. */
+
+/* Mute strerror/strcpy warnings. */
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <brotli/decode.h>
+#include <brotli/encode.h>
+#include <brotli/types.h>
+
+#include "../common/constants.h"
+#include "../common/version.h"
+
+#if defined(_WIN32)
+#include <io.h>
+#include <share.h>
+#include <sys/utime.h>
+
+#define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO))
+
+#if !defined(__MINGW32__)
+#define STDIN_FILENO _fileno(stdin)
+#define STDOUT_FILENO _fileno(stdout)
+#define S_IRUSR S_IREAD
+#define S_IWUSR S_IWRITE
+#endif
+
+#define fdopen _fdopen
+#define isatty _isatty
+#define unlink _unlink
+#define utimbuf _utimbuf
+#define utime _utime
+
+#define fopen ms_fopen
+#define open ms_open
+
+#define chmod(F, P) (0)
+#define chown(F, O, G) (0)
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define fseek _fseeki64
+#define ftell _ftelli64
+#endif
+
+static FILE* ms_fopen(const char* filename, const char* mode) {
+  FILE* result = 0;
+  fopen_s(&result, filename, mode);
+  return result;
+}
+
+static int ms_open(const char* filename, int oflag, int pmode) {
+  int result = -1;
+  _sopen_s(&result, filename, oflag | O_BINARY, _SH_DENYNO, pmode);
+  return result;
+}
+#else  /* !defined(_WIN32) */
+#include <unistd.h>
+#include <utime.h>
+#define MAKE_BINARY(FILENO) (FILENO)
+#endif  /* defined(_WIN32) */
+
+#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
+#define HAVE_UTIMENSAT 1
+#elif defined(_ATFILE_SOURCE)
+#define HAVE_UTIMENSAT 1
+#else
+#define HAVE_UTIMENSAT 0
+#endif
+
+#if HAVE_UTIMENSAT
+#if defined(__APPLE__)
+#define ATIME_NSEC(S) ((S)->st_atimespec.tv_nsec)
+#define MTIME_NSEC(S) ((S)->st_mtimespec.tv_nsec)
+#else  /* defined(__APPLE__) */
+#define ATIME_NSEC(S) ((S)->st_atim.tv_nsec)
+#define MTIME_NSEC(S) ((S)->st_mtim.tv_nsec)
+#endif
+#endif  /* HAVE_UTIMENSAT */
+
+typedef enum {
+  COMMAND_COMPRESS,
+  COMMAND_DECOMPRESS,
+  COMMAND_HELP,
+  COMMAND_INVALID,
+  COMMAND_TEST_INTEGRITY,
+  COMMAND_NOOP,
+  COMMAND_VERSION
+} Command;
+
+typedef enum {
+  COMMENT_INIT,
+  COMMENT_READ,
+  COMMENT_OK,
+  COMMENT_BAD,
+} CommentState;
+
+#define DEFAULT_LGWIN 24
+#define DEFAULT_SUFFIX ".br"
+#define MAX_OPTIONS 24
+#define MAX_COMMENT_LEN 80
+
+typedef struct {
+  /* Parameters */
+  int quality;
+  int lgwin;
+  int verbosity;
+  BROTLI_BOOL force_overwrite;
+  BROTLI_BOOL junk_source;
+  BROTLI_BOOL reject_uncompressible;
+  BROTLI_BOOL copy_stat;
+  BROTLI_BOOL write_to_stdout;
+  BROTLI_BOOL test_integrity;
+  BROTLI_BOOL decompress;
+  BROTLI_BOOL large_window;
+  BROTLI_BOOL allow_concatenated;
+  const char* output_path;
+  const char* dictionary_path;
+  const char* suffix;
+  uint8_t comment[MAX_COMMENT_LEN];
+  size_t comment_len;
+  size_t comment_pos;
+  CommentState comment_state;
+  int not_input_indices[MAX_OPTIONS];
+  size_t longest_path_len;
+  size_t input_count;
+
+  /* Inner state */
+  int argc;
+  char** argv;
+  uint8_t* dictionary;
+  size_t dictionary_size;
+  BrotliEncoderPreparedDictionary* prepared_dictionary;
+  BrotliDecoderState* decoder;
+  char* modified_path;  /* Storage for path with appended / cut suffix */
+  int iterator;
+  int ignore;
+  BROTLI_BOOL iterator_error;
+  uint8_t* buffer;
+  uint8_t* input;
+  uint8_t* output;
+  const char* current_input_path;
+  const char* current_output_path;
+  int64_t input_file_length;  /* -1, if impossible to calculate */
+  FILE* fin;
+  FILE* fout;
+
+  /* I/O buffers */
+  size_t available_in;
+  const uint8_t* next_in;
+  size_t available_out;
+  uint8_t* next_out;
+
+  /* Reporting */
+  /* size_t would be large enough,
+     until 4GiB+ files are compressed / decompressed on 32-bit CPUs. */
+  size_t total_in;
+  size_t total_out;
+  clock_t start_time;
+  clock_t end_time;
+} Context;
+
+/* Parse base 64 encoded string to buffer. Not performance-centric.
+   |out_len| as input is buffer size; |out_len| as output is decoded length.
+   Returns BROTLI_FALSE if either input is not (relaxed) base 64 conformant,
+   or output does not fit buffer. */
+static BROTLI_BOOL ParseBase64(const char* str, uint8_t* out, size_t* out_len) {
+  size_t in_len = strlen(str);
+  size_t max_out_len = *out_len;
+  size_t i;
+  size_t bit_count = 0;
+  uint32_t bits = 0;
+  size_t padding_count = 0;
+  size_t octet_count = 0;
+  for (i = 0; i < in_len; ++i) {
+    char c = str[i];
+    int sextet = 0;
+    if (c == 9 || c == 10 || c == 13 || c == ' ') {
+      continue;
+    }
+    if (c == '=') {
+      padding_count++;
+      continue;
+    }
+    if (padding_count) return BROTLI_FALSE;
+    if (c == '+' || c == '-') {
+      sextet = 62;
+    } else if (c == '/' || c == '_') {
+      sextet = 63;
+    } else if (c >= 'A' && c <= 'Z') {
+      sextet = c - 'A';
+    } else if (c >= 'a' && c <= 'z') {
+      sextet = c - 'a' + 26;
+    } else if (c >= '0' && c <= '9') {
+      sextet = c - '0' + 52;
+    } else {
+      return BROTLI_FALSE;
+    }
+    bits = (bits << 6) | (uint32_t)sextet;
+    bit_count += 6;
+    if (bit_count >= 8) {
+      if (octet_count == max_out_len) return BROTLI_FALSE;
+      bit_count -= 8;
+      out[octet_count++] = (bits >> bit_count) & 0xFF;
+    }
+  }
+  if (padding_count > 2) return BROTLI_FALSE;
+  *out_len = octet_count;
+  return BROTLI_TRUE;
+}
+
+/* Parse up to 5 decimal digits. */
+static BROTLI_BOOL ParseInt(const char* s, int low, int high, int* result) {
+  int value = 0;
+  int i;
+  for (i = 0; i < 5; ++i) {
+    char c = s[i];
+    if (c == 0) break;
+    if (s[i] < '0' || s[i] > '9') return BROTLI_FALSE;
+    value = (10 * value) + (c - '0');
+  }
+  if (i == 0) return BROTLI_FALSE;
+  if (i > 1 && s[0] == '0') return BROTLI_FALSE;
+  if (s[i] != 0) return BROTLI_FALSE;
+  if (value < low || value > high) return BROTLI_FALSE;
+  *result = value;
+  return BROTLI_TRUE;
+}
+
+/* Returns "base file name" or its tail, if it contains '/' or '\'. */
+static const char* FileName(const char* path) {
+  const char* separator_position = strrchr(path, '/');
+  if (separator_position) path = separator_position + 1;
+  separator_position = strrchr(path, '\\');
+  if (separator_position) path = separator_position + 1;
+  return path;
+}
+
+/* Detect if the program name is a special alias that infers a command type. */
+static BROTLI_BOOL CheckAlias(const char* name, const char* alias) {
+  /* TODO: cast name to lower case? */
+  size_t alias_len = strlen(alias);
+  name = FileName(name);
+  /* Partial comparison. On Windows there could be ".exe" suffix. */
+  if (strncmp(name, alias, alias_len) == 0) {
+    char terminator = name[alias_len];
+    if (terminator == 0 || terminator == '.') return BROTLI_TRUE;
+  }
+  return BROTLI_FALSE;
+}
+
+static Command ParseParams(Context* params) {
+  int argc = params->argc;
+  char** argv = params->argv;
+  int i;
+  int next_option_index = 0;
+  size_t input_count = 0;
+  size_t longest_path_len = 1;
+  BROTLI_BOOL command_set = BROTLI_FALSE;
+  BROTLI_BOOL quality_set = BROTLI_FALSE;
+  BROTLI_BOOL output_set = BROTLI_FALSE;
+  BROTLI_BOOL keep_set = BROTLI_FALSE;
+  BROTLI_BOOL squash_set = BROTLI_FALSE;
+  BROTLI_BOOL lgwin_set = BROTLI_FALSE;
+  BROTLI_BOOL suffix_set = BROTLI_FALSE;
+  BROTLI_BOOL after_dash_dash = BROTLI_FALSE;
+  BROTLI_BOOL comment_set = BROTLI_FALSE;
+  BROTLI_BOOL concatenated_set = BROTLI_FALSE;
+  Command command = COMMAND_COMPRESS;
+
+  if (CheckAlias(argv[0], "brcat")) {
+    command_set = BROTLI_TRUE;
+    command = COMMAND_DECOMPRESS;
+    concatenated_set = BROTLI_TRUE;
+    params->allow_concatenated = BROTLI_TRUE;
+    output_set = BROTLI_TRUE;
+    params->write_to_stdout = BROTLI_TRUE;
+  } else if (CheckAlias(argv[0], "unbrotli")) {
+    command_set = BROTLI_TRUE;
+    command = COMMAND_DECOMPRESS;
+  }
+
+  for (i = 1; i < argc; ++i) {
+    const char* arg = argv[i];
+    /* C99 5.1.2.2.1: "members argv[0] through argv[argc-1] inclusive shall
+       contain pointers to strings"; NULL and 0-length are not forbidden. */
+    size_t arg_len = arg ? strlen(arg) : 0;
+
+    if (arg_len == 0) {
+      params->not_input_indices[next_option_index++] = i;
+      continue;
+    }
+
+    /* Too many options. The expected longest option list is:
+       "-q 0 -w 10 -o f -D d -S b -d -f -k -n -v -K --", i.e. 17 items in total.
+       This check is an additional guard that is never triggered, but provides
+       a guard for future changes. */
+    if (next_option_index > (MAX_OPTIONS - 2)) {
+      fprintf(stderr, "too many options passed\n");
+      return COMMAND_INVALID;
+    }
+
+    /* Input file entry. */
+    if (after_dash_dash || arg[0] != '-' || arg_len == 1) {
+      input_count++;
+      if (longest_path_len < arg_len) longest_path_len = arg_len;
+      continue;
+    }
+
+    /* Not a file entry. */
+    params->not_input_indices[next_option_index++] = i;
+
+    /* '--' entry stop parsing arguments. */
+    if (arg_len == 2 && arg[1] == '-') {
+      after_dash_dash = BROTLI_TRUE;
+      continue;
+    }
+
+    /* Simple / coalesced options. */
+    if (arg[1] != '-') {
+      size_t j;
+      for (j = 1; j < arg_len; ++j) {
+        char c = arg[j];
+        if (c >= '0' && c <= '9') {
+          if (quality_set) {
+            fprintf(stderr, "quality already set\n");
+            return COMMAND_INVALID;
+          }
+          quality_set = BROTLI_TRUE;
+          params->quality = c - '0';
+          continue;
+        } else if (c == 'c') {
+          if (output_set) {
+            fprintf(stderr, "write to standard output already set\n");
+            return COMMAND_INVALID;
+          }
+          output_set = BROTLI_TRUE;
+          params->write_to_stdout = BROTLI_TRUE;
+          continue;
+        } else if (c == 'd') {
+          if (command_set) {
+            fprintf(stderr, "command already set when parsing -d\n");
+            return COMMAND_INVALID;
+          }
+          command_set = BROTLI_TRUE;
+          command = COMMAND_DECOMPRESS;
+          continue;
+        } else if (c == 'f') {
+          if (params->force_overwrite) {
+            fprintf(stderr, "force output overwrite already set\n");
+            return COMMAND_INVALID;
+          }
+          params->force_overwrite = BROTLI_TRUE;
+          continue;
+        } else if (c == 'h') {
+          /* Don't parse further. */
+          return COMMAND_HELP;
+        } else if (c == 'j' || c == 'k') {
+          if (keep_set) {
+            fprintf(stderr, "argument --rm / -j or --keep / -k already set\n");
+            return COMMAND_INVALID;
+          }
+          keep_set = BROTLI_TRUE;
+          params->junk_source = TO_BROTLI_BOOL(c == 'j');
+          continue;
+        } else if (c == 'n') {
+          if (!params->copy_stat) {
+            fprintf(stderr, "argument --no-copy-stat / -n already set\n");
+            return COMMAND_INVALID;
+          }
+          params->copy_stat = BROTLI_FALSE;
+          continue;
+        } else if (c == 's') {
+          if (squash_set) {
+            fprintf(stderr, "argument --squash / -s already set\n");
+            return COMMAND_INVALID;
+          }
+          squash_set = BROTLI_TRUE;
+          params->reject_uncompressible = BROTLI_TRUE;
+          continue;
+        } else if (c == 't') {
+          if (command_set) {
+            fprintf(stderr, "command already set when parsing -t\n");
+            return COMMAND_INVALID;
+          }
+          command_set = BROTLI_TRUE;
+          command = COMMAND_TEST_INTEGRITY;
+          continue;
+        } else if (c == 'v') {
+          if (params->verbosity > 0) {
+            fprintf(stderr, "argument --verbose / -v already set\n");
+            return COMMAND_INVALID;
+          }
+          params->verbosity = 1;
+          continue;
+        } else if (c == 'K') {
+          if (concatenated_set) {
+            fprintf(stderr, "argument -K / --concatenated already set\n");
+            return COMMAND_INVALID;
+          }
+          concatenated_set = BROTLI_TRUE;
+          params->allow_concatenated = BROTLI_TRUE;
+          continue;
+        } else if (c == 'V') {
+          /* Don't parse further. */
+          return COMMAND_VERSION;
+        } else if (c == 'Z') {
+          if (quality_set) {
+            fprintf(stderr, "quality already set\n");
+            return COMMAND_INVALID;
+          }
+          quality_set = BROTLI_TRUE;
+          params->quality = 11;
+          continue;
+        }
+        /* o/q/w/C/D/S with parameter is expected */
+        if (c != 'o' && c != 'q' && c != 'w' && c != 'C' && c != 'D' &&
+            c != 'S') {
+          fprintf(stderr, "invalid argument -%c\n", c);
+          return COMMAND_INVALID;
+        }
+        if (j + 1 != arg_len) {
+          fprintf(stderr, "expected parameter for argument -%c\n", c);
+          return COMMAND_INVALID;
+        }
+        i++;
+        if (i == argc || !argv[i] || argv[i][0] == 0) {
+          fprintf(stderr, "expected parameter for argument -%c\n", c);
+          return COMMAND_INVALID;
+        }
+        params->not_input_indices[next_option_index++] = i;
+        if (c == 'o') {
+          if (output_set) {
+            fprintf(stderr, "write to standard output already set (-o)\n");
+            return COMMAND_INVALID;
+          }
+          params->output_path = argv[i];
+        } else if (c == 'q') {
+          if (quality_set) {
+            fprintf(stderr, "quality already set\n");
+            return COMMAND_INVALID;
+          }
+          quality_set = ParseInt(argv[i], BROTLI_MIN_QUALITY,
+                                 BROTLI_MAX_QUALITY, &params->quality);
+          if (!quality_set) {
+            fprintf(stderr, "error parsing quality value [%s]\n", argv[i]);
+            return COMMAND_INVALID;
+          }
+        } else if (c == 'w') {
+          if (lgwin_set) {
+            fprintf(stderr, "lgwin parameter already set\n");
+            return COMMAND_INVALID;
+          }
+          lgwin_set = ParseInt(argv[i], 0,
+                               BROTLI_MAX_WINDOW_BITS, &params->lgwin);
+          if (!lgwin_set) {
+            fprintf(stderr, "error parsing lgwin value [%s]\n", argv[i]);
+            return COMMAND_INVALID;
+          }
+          if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) {
+            fprintf(stderr,
+                    "lgwin parameter (%d) smaller than the minimum (%d)\n",
+                    params->lgwin, BROTLI_MIN_WINDOW_BITS);
+            return COMMAND_INVALID;
+          }
+        } else if (c == 'C') {
+          if (comment_set) {
+            fprintf(stderr, "comment already set\n");
+            return COMMAND_INVALID;
+          }
+          params->comment_len = MAX_COMMENT_LEN;
+          if (!ParseBase64(argv[i], params->comment, &params->comment_len)) {
+            fprintf(stderr, "invalid base64-encoded comment\n");
+            return COMMAND_INVALID;
+          }
+          comment_set = BROTLI_TRUE;
+        } else if (c == 'D') {
+          if (params->dictionary_path) {
+            fprintf(stderr, "dictionary path already set\n");
+            return COMMAND_INVALID;
+          }
+          params->dictionary_path = argv[i];
+        } else if (c == 'S') {
+          if (suffix_set) {
+            fprintf(stderr, "suffix already set\n");
+            return COMMAND_INVALID;
+          }
+          suffix_set = BROTLI_TRUE;
+          params->suffix = argv[i];
+        }
+      }
+    } else {  /* Double-dash. */
+      arg = &arg[2];
+      if (strcmp("best", arg) == 0) {
+        if (quality_set) {
+          fprintf(stderr, "quality already set\n");
+          return COMMAND_INVALID;
+        }
+        quality_set = BROTLI_TRUE;
+        params->quality = 11;
+      } else if (strcmp("concatenated", arg) == 0) {
+        if (concatenated_set) {
+          fprintf(stderr, "argument -K / --concatenated already set\n");
+          return COMMAND_INVALID;
+        }
+        concatenated_set = BROTLI_TRUE;
+        params->allow_concatenated = BROTLI_TRUE;
+        continue;
+      } else if (strcmp("decompress", arg) == 0) {
+        if (command_set) {
+          fprintf(stderr, "command already set when parsing --decompress\n");
+          return COMMAND_INVALID;
+        }
+        command_set = BROTLI_TRUE;
+        command = COMMAND_DECOMPRESS;
+      } else if (strcmp("force", arg) == 0) {
+        if (params->force_overwrite) {
+          fprintf(stderr, "force output overwrite already set\n");
+          return COMMAND_INVALID;
+        }
+        params->force_overwrite = BROTLI_TRUE;
+      } else if (strcmp("help", arg) == 0) {
+        /* Don't parse further. */
+        return COMMAND_HELP;
+      } else if (strcmp("keep", arg) == 0) {
+        if (keep_set) {
+          fprintf(stderr, "argument --rm / -j or --keep / -k already set\n");
+          return COMMAND_INVALID;
+        }
+        keep_set = BROTLI_TRUE;
+        params->junk_source = BROTLI_FALSE;
+      } else if (strcmp("no-copy-stat", arg) == 0) {
+        if (!params->copy_stat) {
+          fprintf(stderr, "argument --no-copy-stat / -n already set\n");
+          return COMMAND_INVALID;
+        }
+        params->copy_stat = BROTLI_FALSE;
+      } else if (strcmp("rm", arg) == 0) {
+        if (keep_set) {
+          fprintf(stderr, "argument --rm / -j or --keep / -k already set\n");
+          return COMMAND_INVALID;
+        }
+        keep_set = BROTLI_TRUE;
+        params->junk_source = BROTLI_TRUE;
+      } else if (strcmp("squash", arg) == 0) {
+        if (squash_set) {
+          fprintf(stderr, "argument --squash / -s already set\n");
+          return COMMAND_INVALID;
+        }
+        squash_set = BROTLI_TRUE;
+        params->reject_uncompressible = BROTLI_TRUE;
+        continue;
+      } else if (strcmp("stdout", arg) == 0) {
+        if (output_set) {
+          fprintf(stderr, "write to standard output already set\n");
+          return COMMAND_INVALID;
+        }
+        output_set = BROTLI_TRUE;
+        params->write_to_stdout = BROTLI_TRUE;
+      } else if (strcmp("test", arg) == 0) {
+        if (command_set) {
+          fprintf(stderr, "command already set when parsing --test\n");
+          return COMMAND_INVALID;
+        }
+        command_set = BROTLI_TRUE;
+        command = COMMAND_TEST_INTEGRITY;
+      } else if (strcmp("verbose", arg) == 0) {
+        if (params->verbosity > 0) {
+          fprintf(stderr, "argument --verbose / -v already set\n");
+          return COMMAND_INVALID;
+        }
+        params->verbosity = 1;
+      } else if (strcmp("version", arg) == 0) {
+        /* Don't parse further. */
+        return COMMAND_VERSION;
+      } else {
+        /* key=value */
+        const char* value = strchr(arg, '=');
+        size_t key_len;
+        if (!value || value[1] == 0) {
+          fprintf(stderr, "must pass the parameter as --%s=value\n", arg);
+          return COMMAND_INVALID;
+        }
+        key_len = (size_t)(value - arg);
+        value++;
+        if (strncmp("comment", arg, key_len) == 0) {
+          if (comment_set) {
+            fprintf(stderr, "comment already set\n");
+            return COMMAND_INVALID;
+          }
+          params->comment_len = MAX_COMMENT_LEN;
+          if (!ParseBase64(value, params->comment, &params->comment_len)) {
+            fprintf(stderr, "invalid base64-encoded comment\n");
+            return COMMAND_INVALID;
+          }
+          comment_set = BROTLI_TRUE;
+        } else if (strncmp("dictionary", arg, key_len) == 0) {
+          if (params->dictionary_path) {
+            fprintf(stderr, "dictionary path already set\n");
+            return COMMAND_INVALID;
+          }
+          params->dictionary_path = value;
+        } else if (strncmp("lgwin", arg, key_len) == 0) {
+          if (lgwin_set) {
+            fprintf(stderr, "lgwin parameter already set\n");
+            return COMMAND_INVALID;
+          }
+          lgwin_set = ParseInt(value, 0,
+                               BROTLI_MAX_WINDOW_BITS, &params->lgwin);
+          if (!lgwin_set) {
+            fprintf(stderr, "error parsing lgwin value [%s]\n", value);
+            return COMMAND_INVALID;
+          }
+          if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) {
+            fprintf(stderr,
+                    "lgwin parameter (%d) smaller than the minimum (%d)\n",
+                    params->lgwin, BROTLI_MIN_WINDOW_BITS);
+            return COMMAND_INVALID;
+          }
+        } else if (strncmp("large_window", arg, key_len) == 0) {
+          /* This option is intentionally not mentioned in help. */
+          if (lgwin_set) {
+            fprintf(stderr, "lgwin parameter already set\n");
+            return COMMAND_INVALID;
+          }
+          lgwin_set = ParseInt(value, 0,
+                               BROTLI_LARGE_MAX_WINDOW_BITS, &params->lgwin);
+          if (!lgwin_set) {
+            fprintf(stderr, "error parsing lgwin value [%s]\n", value);
+            return COMMAND_INVALID;
+          }
+          if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) {
+            fprintf(stderr,
+                    "lgwin parameter (%d) smaller than the minimum (%d)\n",
+                    params->lgwin, BROTLI_MIN_WINDOW_BITS);
+            return COMMAND_INVALID;
+          }
+        } else if (strncmp("output", arg, key_len) == 0) {
+          if (output_set) {
+            fprintf(stderr,
+                    "write to standard output already set (--output)\n");
+            return COMMAND_INVALID;
+          }
+          params->output_path = value;
+        } else if (strncmp("quality", arg, key_len) == 0) {
+          if (quality_set) {
+            fprintf(stderr, "quality already set\n");
+            return COMMAND_INVALID;
+          }
+          quality_set = ParseInt(value, BROTLI_MIN_QUALITY,
+                                 BROTLI_MAX_QUALITY, &params->quality);
+          if (!quality_set) {
+            fprintf(stderr, "error parsing quality value [%s]\n", value);
+            return COMMAND_INVALID;
+          }
+        } else if (strncmp("suffix", arg, key_len) == 0) {
+          if (suffix_set) {
+            fprintf(stderr, "suffix already set\n");
+            return COMMAND_INVALID;
+          }
+          suffix_set = BROTLI_TRUE;
+          params->suffix = value;
+        } else {
+          fprintf(stderr, "invalid parameter: [%s]\n", arg);
+          return COMMAND_INVALID;
+        }
+      }
+    }
+  }
+
+  params->input_count = input_count;
+  params->longest_path_len = longest_path_len;
+  params->decompress = (command == COMMAND_DECOMPRESS);
+  params->test_integrity = (command == COMMAND_TEST_INTEGRITY);
+
+  if (input_count > 1 && output_set) return COMMAND_INVALID;
+  if (params->test_integrity) {
+    if (params->output_path) return COMMAND_INVALID;
+    if (params->write_to_stdout) return COMMAND_INVALID;
+  }
+  if (params->reject_uncompressible && params->write_to_stdout) {
+    return COMMAND_INVALID;
+  }
+  if (strchr(params->suffix, '/') || strchr(params->suffix, '\\')) {
+    return COMMAND_INVALID;
+  }
+  if (!params->decompress && params->allow_concatenated) {
+    return COMMAND_INVALID;
+  }
+  if (params->allow_concatenated && params->comment_len) {
+    return COMMAND_INVALID;
+  }
+
+  return command;
+}
+
+static void PrintVersion(void) {
+  int major = BROTLI_VERSION_MAJOR;
+  int minor = BROTLI_VERSION_MINOR;
+  int patch = BROTLI_VERSION_PATCH;
+  fprintf(stdout, "brotli %d.%d.%d\n", major, minor, patch);
+}
+
+static void PrintHelp(const char* name, BROTLI_BOOL error) {
+  FILE* media = error ? stderr : stdout;
+  /* String is cut to pieces with length less than 509, to conform C90 spec. */
+  fprintf(media,
+"Usage: %s [OPTION]... [FILE]...\n",
+          name);
+  fprintf(media,
+"Options:\n"
+"  -#                          compression level (0-9)\n"
+"  -c, --stdout                write on standard output\n"
+"  -d, --decompress            decompress\n"
+"  -f, --force                 force output file overwrite\n"
+"  -h, --help                  display this help and exit\n");
+  fprintf(media,
+"  -j, --rm                    remove source file(s)\n"
+"  -s, --squash                remove destination file if larger than source\n"
+"  -k, --keep                  keep source file(s) (default)\n"
+"  -n, --no-copy-stat          do not copy source file(s) attributes\n"
+"  -o FILE, --output=FILE      output file (only if 1 input file)\n");
+  fprintf(media,
+"  -q NUM, --quality=NUM       compression level (%d-%d)\n",
+          BROTLI_MIN_QUALITY, BROTLI_MAX_QUALITY);
+  fprintf(media,
+"  -t, --test                  test compressed file integrity\n"
+"  -v, --verbose               verbose mode\n");
+  fprintf(media,
+"  -w NUM, --lgwin=NUM         set LZ77 window size (0, %d-%d)\n"
+"                              window size = 2**NUM - 16\n"
+"                              0 lets compressor choose the optimal value\n",
+          BROTLI_MIN_WINDOW_BITS, BROTLI_MAX_WINDOW_BITS);
+  fprintf(media,
+"  --large_window=NUM          use incompatible large-window brotli\n"
+"                              bitstream with window size (0, %d-%d)\n"
+"                              WARNING: this format is not compatible\n"
+"                              with brotli RFC 7932 and may not be\n"
+"                              decodable with regular brotli decoders\n",
+          BROTLI_MIN_WINDOW_BITS, BROTLI_LARGE_MAX_WINDOW_BITS);
+  fprintf(media,
+"  -C B64, --comment=B64       set comment; argument is base64-decoded first;\n"
+"                              (maximal decoded length: %d)\n"
+"                              when decoding: check stream comment;\n"
+"                              when encoding: embed comment (fingerprint)\n",
+          MAX_COMMENT_LEN);
+  fprintf(media,
+"  -D FILE, --dictionary=FILE  use FILE as raw (LZ77) dictionary\n"
+"  -K, --concatenated          allows concatenated brotli streams as input\n");
+  fprintf(media,
+"  -S SUF, --suffix=SUF        output file suffix (default:'%s')\n",
+          DEFAULT_SUFFIX);
+  fprintf(media,
+"  -V, --version               display version and exit\n"
+"  -Z, --best                  use best compression level (11) (default)\n"
+"Simple options could be coalesced, i.e. '-9kf' is equivalent to '-9 -k -f'.\n"
+"With no FILE, or when FILE is -, read standard input.\n"
+"All arguments after '--' are treated as files.\n");
+}
+
+static const char* PrintablePath(const char* path) {
+  return path ? path : "con";
+}
+
+static BROTLI_BOOL OpenInputFile(const char* input_path, FILE** f) {
+  *f = NULL;
+  if (!input_path) {
+    *f = fdopen(MAKE_BINARY(STDIN_FILENO), "rb");
+    return BROTLI_TRUE;
+  }
+  *f = fopen(input_path, "rb");
+  if (!*f) {
+    fprintf(stderr, "failed to open input file [%s]: %s\n",
+            PrintablePath(input_path), strerror(errno));
+    return BROTLI_FALSE;
+  }
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL OpenOutputFile(const char* output_path, FILE** f,
+                                  BROTLI_BOOL force) {
+  int fd;
+  *f = NULL;
+  if (!output_path) {
+    *f = fdopen(MAKE_BINARY(STDOUT_FILENO), "wb");
+    return BROTLI_TRUE;
+  }
+  fd = open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC,
+            S_IRUSR | S_IWUSR);
+  if (fd < 0) {
+    fprintf(stderr, "failed to open output file [%s]: %s\n",
+            PrintablePath(output_path), strerror(errno));
+    return BROTLI_FALSE;
+  }
+  *f = fdopen(fd, "wb");
+  if (!*f) {
+    fprintf(stderr, "failed to open output file [%s]: %s\n",
+            PrintablePath(output_path), strerror(errno));
+    return BROTLI_FALSE;
+  }
+  return BROTLI_TRUE;
+}
+
+static int64_t FileSize(const char* path) {
+  FILE* f = fopen(path, "rb");
+  int64_t retval;
+  if (f == NULL) {
+    return -1;
+  }
+  if (fseek(f, 0L, SEEK_END) != 0) {
+    fclose(f);
+    return -1;
+  }
+  retval = ftell(f);
+  if (fclose(f) != 0) {
+    return -1;
+  }
+  return retval;
+}
+
+static int CopyTimeStat(const struct stat* statbuf, const char* output_path) {
+#if HAVE_UTIMENSAT
+  struct timespec times[2];
+  times[0].tv_sec = statbuf->st_atime;
+  times[0].tv_nsec = ATIME_NSEC(statbuf);
+  times[1].tv_sec = statbuf->st_mtime;
+  times[1].tv_nsec = MTIME_NSEC(statbuf);
+  return utimensat(AT_FDCWD, output_path, times, AT_SYMLINK_NOFOLLOW);
+#else
+  struct utimbuf times;
+  times.actime = statbuf->st_atime;
+  times.modtime = statbuf->st_mtime;
+  return utime(output_path, &times);
+#endif
+}
+
+/* Copy file times and permissions.
+   TODO(eustas): this is a "best effort" implementation; honest cross-platform
+   fully featured implementation is way too hacky; add more hacks by request. */
+static void CopyStat(const char* input_path, const char* output_path) {
+  struct stat statbuf;
+  int res;
+  if (input_path == 0 || output_path == 0) {
+    return;
+  }
+  if (stat(input_path, &statbuf) != 0) {
+    return;
+  }
+  res = CopyTimeStat(&statbuf, output_path);
+  res = chmod(output_path, statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+  if (res != 0) {
+    fprintf(stderr, "setting access bits failed for [%s]: %s\n",
+            PrintablePath(output_path), strerror(errno));
+  }
+  res = chown(output_path, (uid_t)-1, statbuf.st_gid);
+  if (res != 0) {
+    fprintf(stderr, "setting group failed for [%s]: %s\n",
+            PrintablePath(output_path), strerror(errno));
+  }
+  res = chown(output_path, statbuf.st_uid, (gid_t)-1);
+  if (res != 0) {
+    fprintf(stderr, "setting user failed for [%s]: %s\n",
+            PrintablePath(output_path), strerror(errno));
+  }
+}
+
+/* Result ownership is passed to caller.
+   |*dictionary_size| is set to resulting buffer size. */
+static BROTLI_BOOL ReadDictionary(Context* context, Command command) {
+  static const int kMaxDictionarySize =
+      BROTLI_MAX_DISTANCE - BROTLI_MAX_BACKWARD_LIMIT(24);
+  FILE* f;
+  int64_t file_size_64;
+  uint8_t* buffer;
+  size_t bytes_read;
+
+  if (context->dictionary_path == NULL) return BROTLI_TRUE;
+  f = fopen(context->dictionary_path, "rb");
+  if (f == NULL) {
+    fprintf(stderr, "failed to open dictionary file [%s]: %s\n",
+            PrintablePath(context->dictionary_path), strerror(errno));
+    return BROTLI_FALSE;
+  }
+
+  file_size_64 = FileSize(context->dictionary_path);
+  if (file_size_64 == -1) {
+    fprintf(stderr, "could not get size of dictionary file [%s]",
+            PrintablePath(context->dictionary_path));
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+
+  if (file_size_64 > kMaxDictionarySize) {
+    fprintf(stderr, "dictionary [%s] is larger than maximum allowed: %d\n",
+            PrintablePath(context->dictionary_path), kMaxDictionarySize);
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+  context->dictionary_size = (size_t)file_size_64;
+
+  buffer = (uint8_t*)malloc(context->dictionary_size);
+  if (!buffer) {
+    fprintf(stderr, "could not read dictionary: out of memory\n");
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+  bytes_read = fread(buffer, sizeof(uint8_t), context->dictionary_size, f);
+  if (bytes_read != context->dictionary_size) {
+    free(buffer);
+    fprintf(stderr, "failed to read dictionary [%s]: %s\n",
+            PrintablePath(context->dictionary_path), strerror(errno));
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+  fclose(f);
+  context->dictionary = buffer;
+  if (command == COMMAND_COMPRESS) {
+    context->prepared_dictionary = BrotliEncoderPrepareDictionary(
+        BROTLI_SHARED_DICTIONARY_RAW, context->dictionary_size,
+        context->dictionary, BROTLI_MAX_QUALITY, NULL, NULL, NULL);
+    if (context->prepared_dictionary == NULL) {
+      fprintf(stderr, "failed to prepare dictionary [%s]\n",
+              PrintablePath(context->dictionary_path));
+      return BROTLI_FALSE;
+    }
+  }
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL NextFile(Context* context) {
+  const char* arg;
+  size_t arg_len;
+
+  /* Iterator points to last used arg; increment to search for the next one. */
+  context->iterator++;
+
+  context->input_file_length = -1;
+
+  /* No input path; read from console. */
+  if (context->input_count == 0) {
+    if (context->iterator > 1) return BROTLI_FALSE;
+    context->current_input_path = NULL;
+    /* Either write to the specified path, or to console. */
+    context->current_output_path = context->output_path;
+    return BROTLI_TRUE;
+  }
+
+  /* Skip option arguments. */
+  while (context->iterator == context->not_input_indices[context->ignore]) {
+    context->iterator++;
+    context->ignore++;
+  }
+
+  /* All args are scanned already. */
+  if (context->iterator >= context->argc) return BROTLI_FALSE;
+
+  /* Iterator now points to the input file name. */
+  arg = context->argv[context->iterator];
+  arg_len = strlen(arg);
+  /* Read from console. */
+  if (arg_len == 1 && arg[0] == '-') {
+    context->current_input_path = NULL;
+    context->current_output_path = context->output_path;
+    return BROTLI_TRUE;
+  }
+
+  context->current_input_path = arg;
+  context->input_file_length = FileSize(arg);
+  context->current_output_path = context->output_path;
+
+  if (context->output_path) return BROTLI_TRUE;
+  if (context->write_to_stdout) return BROTLI_TRUE;
+
+  strcpy(context->modified_path, arg);
+  context->current_output_path = context->modified_path;
+  /* If output is not specified, input path suffix should match. */
+  if (context->decompress) {
+    size_t suffix_len = strlen(context->suffix);
+    char* name = (char*)FileName(context->modified_path);
+    char* name_suffix;
+    size_t name_len = strlen(name);
+    if (name_len < suffix_len + 1) {
+      fprintf(stderr, "empty output file name for [%s] input file\n",
+              PrintablePath(arg));
+      context->iterator_error = BROTLI_TRUE;
+      return BROTLI_FALSE;
+    }
+    name_suffix = name + name_len - suffix_len;
+    if (strcmp(context->suffix, name_suffix) != 0) {
+      fprintf(stderr, "input file [%s] suffix mismatch\n",
+              PrintablePath(arg));
+      context->iterator_error = BROTLI_TRUE;
+      return BROTLI_FALSE;
+    }
+    name_suffix[0] = 0;
+    return BROTLI_TRUE;
+  } else {
+    strcpy(context->modified_path + arg_len, context->suffix);
+    return BROTLI_TRUE;
+  }
+}
+
+static BROTLI_BOOL OpenFiles(Context* context) {
+  BROTLI_BOOL is_ok = OpenInputFile(context->current_input_path, &context->fin);
+  if (!context->test_integrity && is_ok) {
+    is_ok = OpenOutputFile(
+        context->current_output_path, &context->fout, context->force_overwrite);
+  }
+  return is_ok;
+}
+
+static BROTLI_BOOL CloseFiles(Context* context, BROTLI_BOOL rm_input,
+                              BROTLI_BOOL rm_output) {
+  BROTLI_BOOL is_ok = BROTLI_TRUE;
+  if (!context->test_integrity && context->fout) {
+    if (fclose(context->fout) != 0) {
+      if (is_ok) {
+        fprintf(stderr, "fclose failed [%s]: %s\n",
+                PrintablePath(context->current_output_path), strerror(errno));
+      }
+      is_ok = BROTLI_FALSE;
+    }
+    if (rm_output && context->current_output_path) {
+      unlink(context->current_output_path);
+    }
+
+    /* TOCTOU violation, but otherwise it is impossible to set file times. */
+    if (!rm_output && is_ok && context->copy_stat) {
+      CopyStat(context->current_input_path, context->current_output_path);
+    }
+  }
+
+  if (context->fin) {
+    if (fclose(context->fin) != 0) {
+      if (is_ok) {
+        fprintf(stderr, "fclose failed [%s]: %s\n",
+                PrintablePath(context->current_input_path), strerror(errno));
+      }
+      is_ok = BROTLI_FALSE;
+    }
+  }
+  if (rm_input && context->current_input_path) {
+    unlink(context->current_input_path);
+  }
+
+  context->fin = NULL;
+  context->fout = NULL;
+
+  return is_ok;
+}
+
+static const size_t kFileBufferSize = 1 << 19;
+
+static void InitializeBuffers(Context* context) {
+  context->available_in = 0;
+  context->next_in = NULL;
+  context->available_out = kFileBufferSize;
+  context->next_out = context->output;
+  context->total_in = 0;
+  context->total_out = 0;
+  if (context->verbosity > 0) {
+    context->start_time = clock();
+  }
+}
+
+/* This method might give the false-negative result.
+   However, after an empty / incomplete read it should tell the truth. */
+static BROTLI_BOOL HasMoreInput(Context* context) {
+  return feof(context->fin) ? BROTLI_FALSE : BROTLI_TRUE;
+}
+
+static BROTLI_BOOL ProvideInput(Context* context) {
+  context->available_in =
+      fread(context->input, 1, kFileBufferSize, context->fin);
+  context->total_in += context->available_in;
+  context->next_in = context->input;
+  if (ferror(context->fin)) {
+    fprintf(stderr, "failed to read input [%s]: %s\n",
+            PrintablePath(context->current_input_path), strerror(errno));
+    return BROTLI_FALSE;
+  }
+  return BROTLI_TRUE;
+}
+
+/* Internal: should be used only in Provide-/Flush-Output. */
+static BROTLI_BOOL WriteOutput(Context* context) {
+  size_t out_size = (size_t)(context->next_out - context->output);
+  context->total_out += out_size;
+  if (out_size == 0) return BROTLI_TRUE;
+  if (context->test_integrity) return BROTLI_TRUE;
+
+  fwrite(context->output, 1, out_size, context->fout);
+  if (ferror(context->fout)) {
+    fprintf(stderr, "failed to write output [%s]: %s\n",
+            PrintablePath(context->current_output_path), strerror(errno));
+    return BROTLI_FALSE;
+  }
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL ProvideOutput(Context* context) {
+  if (!WriteOutput(context)) return BROTLI_FALSE;
+  context->available_out = kFileBufferSize;
+  context->next_out = context->output;
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL FlushOutput(Context* context) {
+  if (!WriteOutput(context)) return BROTLI_FALSE;
+  context->available_out = 0;
+  context->next_out = context->output;
+  return BROTLI_TRUE;
+}
+
+static void PrintBytes(size_t value) {
+  if (value < 1024) {
+    fprintf(stderr, "%d B", (int)value);
+  } else if (value < 1048576) {
+    fprintf(stderr, "%0.3f KiB", (double)value / 1024.0);
+  } else if (value < 1073741824) {
+    fprintf(stderr, "%0.3f MiB", (double)value / 1048576.0);
+  } else {
+    fprintf(stderr, "%0.3f GiB", (double)value / 1073741824.0);
+  }
+}
+
+static void PrintFileProcessingProgress(Context* context) {
+  fprintf(stderr, "[%s]: ", PrintablePath(context->current_input_path));
+  PrintBytes(context->total_in);
+  fprintf(stderr, " -> ");
+  PrintBytes(context->total_out);
+  fprintf(stderr, " in %1.2f sec", (double)(context->end_time - context->start_time) / CLOCKS_PER_SEC);
+}
+
+static const char* PrettyDecoderErrorString(BrotliDecoderErrorCode code) {
+  /* "_ERROR_domain_" is in only added in newer versions. If CLI is linked
+     against older shared library, return error string as is; result might be
+     a bit confusing, e.g. "RESERVED" instead of "FORMAT_RESERVED" */
+  const char* prefix = "_ERROR_";
+  size_t prefix_len = strlen(prefix);
+  const char* error_str = BrotliDecoderErrorString(code);
+  size_t error_len = strlen(error_str);
+  if (error_len > prefix_len) {
+    if (strncmp(error_str, prefix, prefix_len) == 0) {
+      error_str += prefix_len;
+    }
+  }
+  return error_str;
+}
+
+static void OnMetadataStart(void* opaque, size_t size) {
+  Context* context = (Context*) opaque;
+  if (context->comment_state == COMMENT_INIT) {
+    if (context->comment_len != size) {
+      context->comment_state = COMMENT_BAD;
+      return;
+    }
+    context->comment_pos = 0;
+    context->comment_state = COMMENT_READ;
+  }
+}
+
+static void OnMetadataChunk(void* opaque, const uint8_t* data, size_t size) {
+  Context* context = (Context*) opaque;
+  if (context->comment_state == COMMENT_READ) {
+    size_t i;
+    for (i = 0; i < size; ++i) {
+      if (context->comment_pos >= context->comment_len) {
+        context->comment_state = COMMENT_BAD;
+        return;
+      }
+      if (context->comment[context->comment_pos++] != data[i]) {
+        context->comment_state = COMMENT_BAD;
+        return;
+      }
+    }
+    if (context->comment_pos == context->comment_len) {
+      context->comment_state = COMMENT_OK;
+    }
+  }
+}
+
+static BROTLI_BOOL InitDecoder(Context* context) {
+  context->decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL);
+  if (!context->decoder) {
+    fprintf(stderr, "out of memory\n");
+    return BROTLI_FALSE;
+  }
+  /* This allows decoding "large-window" streams. Though it creates
+      fragmentation (new builds decode streams that old builds don't),
+      it is better from used experience perspective. */
+  BrotliDecoderSetParameter(
+      context->decoder, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1u);
+  if (context->dictionary) {
+    BrotliDecoderAttachDictionary(context->decoder,
+        BROTLI_SHARED_DICTIONARY_RAW, context->dictionary_size,
+        context->dictionary);
+  }
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL DecompressFile(Context* context) {
+  BrotliDecoderState* s = context->decoder;
+  BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
+  if (context->comment_len) {
+    context->comment_state = COMMENT_INIT;
+    BrotliDecoderSetMetadataCallbacks(s, &OnMetadataStart, &OnMetadataChunk,
+        (void*)context);
+  } else {
+    context->comment_state = COMMENT_OK;
+  }
+
+  InitializeBuffers(context);
+  for (;;) {
+    /* Early check */
+    if (context->comment_state == COMMENT_BAD) {
+      fprintf(stderr, "corrupt input [%s]\n",
+              PrintablePath(context->current_input_path));
+      if (context->verbosity > 0) {
+        fprintf(stderr, "reason: comment mismatch\n");
+      }
+      return BROTLI_FALSE;
+    }
+    if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
+      if (!HasMoreInput(context)) {
+        fprintf(stderr, "corrupt input [%s]\n",
+                PrintablePath(context->current_input_path));
+        if (context->verbosity > 0) {
+          fprintf(stderr, "reason: truncated input\n");
+        }
+        return BROTLI_FALSE;
+      }
+      if (!ProvideInput(context)) return BROTLI_FALSE;
+    } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
+      if (!ProvideOutput(context)) return BROTLI_FALSE;
+    } else if (result == BROTLI_DECODER_RESULT_SUCCESS) {
+      if (!FlushOutput(context)) return BROTLI_FALSE;
+      BROTLI_BOOL has_more_input = (context->available_in != 0);
+      int extra_char = EOF;
+      if (!has_more_input) {
+        extra_char = fgetc(context->fin);
+        if (extra_char != EOF) {
+          has_more_input = BROTLI_TRUE;
+          context->input[0] = (uint8_t)extra_char;
+          context->next_in = context->input;
+          context->available_in = 1;
+        }
+      }
+      if (has_more_input) {
+        if (context->allow_concatenated) {
+          if (context->verbosity > 0) {
+            fprintf(stderr, "extra input\n");
+          }
+          if (!ProvideOutput(context)) return BROTLI_FALSE;
+          BrotliDecoderDestroyInstance(context->decoder);
+          context->decoder = NULL;
+          if (!InitDecoder(context)) return BROTLI_FALSE;
+          s = context->decoder;
+        } else {
+          fprintf(stderr, "corrupt input [%s]\n",
+                  PrintablePath(context->current_input_path));
+          if (context->verbosity > 0) {
+            fprintf(stderr, "reason: extra input\n");
+          }
+          return BROTLI_FALSE;
+        }
+      } else {
+        if (context->verbosity > 0) {
+          context->end_time = clock();
+          fprintf(stderr, "Decompressed ");
+          PrintFileProcessingProgress(context);
+          fprintf(stderr, "\n");
+        }
+        /* Final check */
+        if (context->comment_state != COMMENT_OK) {
+          fprintf(stderr, "corrupt input [%s]\n",
+                  PrintablePath(context->current_input_path));
+          if (context->verbosity > 0) {
+            fprintf(stderr, "reason: comment mismatch\n");
+          }
+        }
+        return BROTLI_TRUE;
+      }
+    } else {  /* result == BROTLI_DECODER_RESULT_ERROR */
+      fprintf(stderr, "corrupt input [%s]\n",
+              PrintablePath(context->current_input_path));
+      if (context->verbosity > 0) {
+        BrotliDecoderErrorCode error = BrotliDecoderGetErrorCode(s);
+        const char* error_str = PrettyDecoderErrorString(error);
+        fprintf(stderr, "reason: %s (%d)\n", error_str, error);
+      }
+      return BROTLI_FALSE;
+    }
+
+    result = BrotliDecoderDecompressStream(s, &context->available_in,
+        &context->next_in, &context->available_out, &context->next_out, 0);
+  }
+}
+
+static BROTLI_BOOL DecompressFiles(Context* context) {
+  while (NextFile(context)) {
+    BROTLI_BOOL is_ok = BROTLI_TRUE;
+    BROTLI_BOOL rm_input = BROTLI_FALSE;
+    BROTLI_BOOL rm_output = BROTLI_TRUE;
+    if (!InitDecoder(context)) return BROTLI_FALSE;
+    is_ok = OpenFiles(context);
+    if (is_ok && !context->current_input_path &&
+        !context->force_overwrite && isatty(STDIN_FILENO)) {
+      fprintf(stderr, "Use -h help. Use -f to force input from a terminal.\n");
+      is_ok = BROTLI_FALSE;
+    }
+    if (is_ok) is_ok = DecompressFile(context);
+    if (context->decoder) BrotliDecoderDestroyInstance(context->decoder);
+    context->decoder = NULL;
+    rm_output = !is_ok;
+    rm_input = !rm_output && context->junk_source;
+    if (!CloseFiles(context, rm_input, rm_output)) is_ok = BROTLI_FALSE;
+    if (!is_ok) return BROTLI_FALSE;
+  }
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL CompressFile(Context* context, BrotliEncoderState* s) {
+  BROTLI_BOOL is_eof = BROTLI_FALSE;
+  BROTLI_BOOL prologue = !!context->comment_len;
+  InitializeBuffers(context);
+  for (;;) {
+    if (context->available_in == 0 && !is_eof) {
+      if (!ProvideInput(context)) return BROTLI_FALSE;
+      is_eof = !HasMoreInput(context);
+    }
+
+    if (prologue) {
+      prologue = BROTLI_FALSE;
+      const uint8_t* next_meta = context->comment;
+      size_t available_meta = context->comment_len;
+      if (!BrotliEncoderCompressStream(s,
+          BROTLI_OPERATION_EMIT_METADATA,
+          &available_meta, &next_meta,
+          &context->available_out, &context->next_out, NULL)) {
+        /* Should detect OOM? */
+        fprintf(stderr, "failed to emit metadata [%s]\n",
+                PrintablePath(context->current_input_path));
+        return BROTLI_FALSE;
+      }
+      if (available_meta != 0) {
+        fprintf(stderr, "failed to emit metadata [%s]\n",
+                PrintablePath(context->current_input_path));
+        return BROTLI_FALSE;
+      }
+    } else {
+      if (!BrotliEncoderCompressStream(s,
+          is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,
+          &context->available_in, &context->next_in,
+          &context->available_out, &context->next_out, NULL)) {
+        /* Should detect OOM? */
+        fprintf(stderr, "failed to compress data [%s]\n",
+                PrintablePath(context->current_input_path));
+        return BROTLI_FALSE;
+      }
+    }
+
+    if (context->available_out == 0) {
+      if (!ProvideOutput(context)) return BROTLI_FALSE;
+    }
+
+    if (BrotliEncoderIsFinished(s)) {
+      if (!FlushOutput(context)) return BROTLI_FALSE;
+      if (context->verbosity > 0) {
+        context->end_time = clock();
+        fprintf(stderr, "Compressed ");
+        PrintFileProcessingProgress(context);
+        fprintf(stderr, "\n");
+      }
+      return BROTLI_TRUE;
+    }
+  }
+}
+
+static BROTLI_BOOL CompressFiles(Context* context) {
+  while (NextFile(context)) {
+    BROTLI_BOOL is_ok = BROTLI_TRUE;
+    BROTLI_BOOL rm_input = BROTLI_FALSE;
+    BROTLI_BOOL rm_output = BROTLI_TRUE;
+    BrotliEncoderState* s = BrotliEncoderCreateInstance(NULL, NULL, NULL);
+    if (!s) {
+      fprintf(stderr, "out of memory\n");
+      return BROTLI_FALSE;
+    }
+    BrotliEncoderSetParameter(s,
+        BROTLI_PARAM_QUALITY, (uint32_t)context->quality);
+    if (context->lgwin > 0) {
+      /* Specified by user. */
+      /* Do not enable "large-window" extension, if not required. */
+      if (context->lgwin > BROTLI_MAX_WINDOW_BITS) {
+        BrotliEncoderSetParameter(s, BROTLI_PARAM_LARGE_WINDOW, 1u);
+      }
+      BrotliEncoderSetParameter(s,
+          BROTLI_PARAM_LGWIN, (uint32_t)context->lgwin);
+    } else {
+      /* 0, or not specified by user; could be chosen by compressor. */
+      uint32_t lgwin = DEFAULT_LGWIN;
+      /* Use file size to limit lgwin. */
+      if (context->input_file_length >= 0) {
+        lgwin = BROTLI_MIN_WINDOW_BITS;
+        while (BROTLI_MAX_BACKWARD_LIMIT(lgwin) <
+               (uint64_t)context->input_file_length) {
+          lgwin++;
+          if (lgwin == BROTLI_MAX_WINDOW_BITS) break;
+        }
+      }
+      BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, lgwin);
+    }
+    if (context->input_file_length > 0) {
+      uint32_t size_hint = context->input_file_length < (1 << 30) ?
+          (uint32_t)context->input_file_length : (1u << 30);
+      BrotliEncoderSetParameter(s, BROTLI_PARAM_SIZE_HINT, size_hint);
+    }
+    if (context->dictionary) {
+      BrotliEncoderAttachPreparedDictionary(s, context->prepared_dictionary);
+    }
+    is_ok = OpenFiles(context);
+    if (is_ok && !context->current_output_path &&
+        !context->force_overwrite && isatty(STDOUT_FILENO)) {
+      fprintf(stderr, "Use -h help. Use -f to force output to a terminal.\n");
+      is_ok = BROTLI_FALSE;
+    }
+    if (is_ok) is_ok = CompressFile(context, s);
+    BrotliEncoderDestroyInstance(s);
+    rm_output = !is_ok;
+    if (is_ok && context->reject_uncompressible) {
+      if (context->total_out >= context->total_in) {
+        rm_output = BROTLI_TRUE;
+        if (context->verbosity > 0) {
+          fprintf(stderr, "Output is larger than input\n");
+        }
+      }
+    }
+    rm_input = !rm_output && context->junk_source;
+    if (!CloseFiles(context, rm_input, rm_output)) is_ok = BROTLI_FALSE;
+    if (!is_ok) return BROTLI_FALSE;
+  }
+  return BROTLI_TRUE;
+}
+
+int main(int argc, char** argv) {
+  Command command;
+  Context context;
+  BROTLI_BOOL is_ok = BROTLI_TRUE;
+  int i;
+
+  context.quality = 11;
+  context.lgwin = -1;
+  context.verbosity = 0;
+  context.comment_len = 0;
+  context.force_overwrite = BROTLI_FALSE;
+  context.junk_source = BROTLI_FALSE;
+  context.reject_uncompressible = BROTLI_FALSE;
+  context.copy_stat = BROTLI_TRUE;
+  context.test_integrity = BROTLI_FALSE;
+  context.write_to_stdout = BROTLI_FALSE;
+  context.decompress = BROTLI_FALSE;
+  context.large_window = BROTLI_FALSE;
+  context.allow_concatenated = BROTLI_FALSE;
+  context.output_path = NULL;
+  context.dictionary_path = NULL;
+  context.suffix = DEFAULT_SUFFIX;
+  for (i = 0; i < MAX_OPTIONS; ++i) context.not_input_indices[i] = 0;
+  context.longest_path_len = 1;
+  context.input_count = 0;
+
+  context.argc = argc;
+  context.argv = argv;
+  context.dictionary = NULL;
+  context.dictionary_size = 0;
+  context.decoder = NULL;
+  context.prepared_dictionary = NULL;
+  context.modified_path = NULL;
+  context.iterator = 0;
+  context.ignore = 0;
+  context.iterator_error = BROTLI_FALSE;
+  context.buffer = NULL;
+  context.current_input_path = NULL;
+  context.current_output_path = NULL;
+  context.fin = NULL;
+  context.fout = NULL;
+
+  command = ParseParams(&context);
+
+  if (command == COMMAND_COMPRESS || command == COMMAND_DECOMPRESS ||
+      command == COMMAND_TEST_INTEGRITY) {
+    if (!ReadDictionary(&context, command)) is_ok = BROTLI_FALSE;
+    if (is_ok) {
+      size_t modified_path_len =
+          context.longest_path_len + strlen(context.suffix) + 1;
+      context.modified_path = (char*)malloc(modified_path_len);
+      context.buffer = (uint8_t*)malloc(kFileBufferSize * 2);
+      if (!context.modified_path || !context.buffer) {
+        fprintf(stderr, "out of memory\n");
+        is_ok = BROTLI_FALSE;
+      } else {
+        context.input = context.buffer;
+        context.output = context.buffer + kFileBufferSize;
+      }
+    }
+  }
+
+  if (!is_ok) command = COMMAND_NOOP;
+
+  switch (command) {
+    case COMMAND_NOOP:
+      break;
+
+    case COMMAND_VERSION:
+      PrintVersion();
+      break;
+
+    case COMMAND_COMPRESS:
+      is_ok = CompressFiles(&context);
+      break;
+
+    case COMMAND_DECOMPRESS:
+    case COMMAND_TEST_INTEGRITY:
+      is_ok = DecompressFiles(&context);
+      break;
+
+    case COMMAND_HELP:
+    case COMMAND_INVALID:
+    default:
+      is_ok = (command == COMMAND_HELP);
+      PrintHelp(FileName(argv[0]), is_ok);
+      break;
+  }
+
+  if (context.iterator_error) is_ok = BROTLI_FALSE;
+
+  BrotliEncoderDestroyPreparedDictionary(context.prepared_dictionary);
+  free(context.dictionary);
+  free(context.modified_path);
+  free(context.buffer);
+
+  if (!is_ok) exit(1);
+  return 0;
+}