add option to delete files that are not "compressed"

PiperOrigin-RevId: 552472135
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 61378cd..bf5204d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,7 +61,6 @@
 read_macro("c/common/version.h" "BROTLI_VERSION_MAJOR" BROTLI_VERSION_MAJOR)
 read_macro("c/common/version.h" "BROTLI_VERSION_MINOR" BROTLI_VERSION_MINOR)
 read_macro("c/common/version.h" "BROTLI_VERSION_PATCH" BROTLI_VERSION_PATCH)
-set(BROTLI_VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_PATCH}")
 mark_as_advanced(BROTLI_VERSION BROTLI_VERSION_MAJOR BROTLI_VERSION_MINOR BROTLI_VERSION_PATCH)
 
 # ABI Version information
diff --git a/c/tools/brotli.c b/c/tools/brotli.c
index f7e531e..432fa52 100644
--- a/c/tools/brotli.c
+++ b/c/tools/brotli.c
@@ -22,6 +22,7 @@
 
 #include <brotli/decode.h>
 #include <brotli/encode.h>
+#include <brotli/types.h>
 
 #include "../common/constants.h"
 #include "../common/version.h"
@@ -107,6 +108,7 @@
   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;
@@ -205,6 +207,7 @@
   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;
@@ -300,6 +303,14 @@
           }
           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");
@@ -436,6 +447,14 @@
         }
         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");
@@ -553,6 +572,9 @@
     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;
   }
@@ -582,6 +604,7 @@
 "  -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");
@@ -867,22 +890,23 @@
   return is_ok;
 }
 
-static BROTLI_BOOL CloseFiles(Context* context, BROTLI_BOOL success) {
+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 (!success && context->current_output_path) {
-      unlink(context->current_output_path);
-    }
     if (fclose(context->fout) != 0) {
-      if (success) {
+      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 (success && is_ok && context->copy_stat) {
+    if (!rm_output && is_ok && context->copy_stat) {
       CopyStat(context->current_input_path, context->current_output_path);
     }
   }
@@ -896,7 +920,7 @@
       is_ok = BROTLI_FALSE;
     }
   }
-  if (success && context->junk_source && context->current_input_path) {
+  if (rm_input && context->current_input_path) {
     unlink(context->current_input_path);
   }
 
@@ -1058,6 +1082,8 @@
 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;
     BrotliDecoderState* s = BrotliDecoderCreateInstance(NULL, NULL, NULL);
     if (!s) {
       fprintf(stderr, "out of memory\n");
@@ -1079,7 +1105,9 @@
     }
     if (is_ok) is_ok = DecompressFile(context, s);
     BrotliDecoderDestroyInstance(s);
-    if (!CloseFiles(context, is_ok)) is_ok = BROTLI_FALSE;
+    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;
@@ -1124,6 +1152,8 @@
 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");
@@ -1169,7 +1199,17 @@
     }
     if (is_ok) is_ok = CompressFile(context, s);
     BrotliEncoderDestroyInstance(s);
-    if (!CloseFiles(context, is_ok)) is_ok = BROTLI_FALSE;
+    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;
@@ -1186,6 +1226,7 @@
   context.verbosity = 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;