deflate-ruby 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +95 -92
  3. data/GEM_VERIFICATION_REPORT.md +140 -0
  4. data/LICENSE.txt +6 -6
  5. data/README.md +87 -65
  6. data/Rakefile +23 -0
  7. data/ext/deflate_ruby/{libdeflate/lib/x86/adler32_impl.h → adler32_impl.h} +8 -7
  8. data/ext/deflate_ruby/common_defs.h +748 -0
  9. data/ext/deflate_ruby/{libdeflate/lib/x86/cpu_features.c → cpu_features.c} +46 -16
  10. data/ext/deflate_ruby/{libdeflate/lib/x86/cpu_features.h → cpu_features.h} +2 -1
  11. data/ext/deflate_ruby/{libdeflate/lib/x86/crc32_impl.h → crc32_impl.h} +22 -23
  12. data/ext/deflate_ruby/{libdeflate/lib/crc32_multipliers.h → crc32_multipliers.h} +2 -4
  13. data/ext/deflate_ruby/{libdeflate/lib/x86/crc32_pclmul_template.h → crc32_pclmul_template.h} +23 -94
  14. data/ext/deflate_ruby/{libdeflate/lib/crc32_tables.h → crc32_tables.h} +1 -1
  15. data/ext/deflate_ruby/{libdeflate/lib/deflate_compress.c → deflate_compress.c} +59 -60
  16. data/ext/deflate_ruby/deflate_ruby.c +392 -218
  17. data/ext/deflate_ruby/deflate_ruby.h +6 -0
  18. data/ext/deflate_ruby/extconf.rb +35 -25
  19. data/ext/deflate_ruby/libdeflate/adler32.c +162 -0
  20. data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/adler32_impl.h +14 -7
  21. data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/crc32_impl.h +25 -31
  22. data/ext/deflate_ruby/libdeflate/arm/crc32_pmull_helpers.h +156 -0
  23. data/ext/deflate_ruby/libdeflate/arm/crc32_pmull_wide.h +226 -0
  24. data/ext/deflate_ruby/libdeflate/bt_matchfinder.h +342 -0
  25. data/ext/deflate_ruby/libdeflate/common_defs.h +2 -1
  26. data/ext/deflate_ruby/libdeflate/cpu_features_common.h +93 -0
  27. data/ext/deflate_ruby/libdeflate/crc32.c +262 -0
  28. data/ext/deflate_ruby/libdeflate/crc32_multipliers.h +375 -0
  29. data/ext/deflate_ruby/libdeflate/crc32_tables.h +587 -0
  30. data/ext/deflate_ruby/libdeflate/decompress_template.h +777 -0
  31. data/ext/deflate_ruby/libdeflate/deflate_compress.c +4128 -0
  32. data/ext/deflate_ruby/libdeflate/deflate_compress.h +15 -0
  33. data/ext/deflate_ruby/libdeflate/deflate_constants.h +56 -0
  34. data/ext/deflate_ruby/libdeflate/deflate_decompress.c +1208 -0
  35. data/ext/deflate_ruby/libdeflate/gzip_compress.c +90 -0
  36. data/ext/deflate_ruby/libdeflate/gzip_constants.h +45 -0
  37. data/ext/deflate_ruby/libdeflate/gzip_decompress.c +144 -0
  38. data/ext/deflate_ruby/libdeflate/hc_matchfinder.h +401 -0
  39. data/ext/deflate_ruby/libdeflate/ht_matchfinder.h +234 -0
  40. data/ext/deflate_ruby/libdeflate/lib_common.h +106 -0
  41. data/ext/deflate_ruby/libdeflate/libdeflate.h +2 -2
  42. data/ext/deflate_ruby/libdeflate/{lib/matchfinder_common.h → matchfinder_common.h} +3 -3
  43. data/ext/deflate_ruby/libdeflate/x86/adler32_impl.h +135 -0
  44. data/ext/deflate_ruby/libdeflate/x86/adler32_template.h +518 -0
  45. data/ext/deflate_ruby/libdeflate/x86/cpu_features.c +213 -0
  46. data/ext/deflate_ruby/libdeflate/x86/cpu_features.h +170 -0
  47. data/ext/deflate_ruby/libdeflate/x86/crc32_impl.h +159 -0
  48. data/ext/deflate_ruby/libdeflate/x86/crc32_pclmul_template.h +424 -0
  49. data/ext/deflate_ruby/libdeflate/x86/decompress_impl.h +57 -0
  50. data/ext/deflate_ruby/libdeflate.h +411 -0
  51. data/ext/deflate_ruby/matchfinder_common.h +224 -0
  52. data/ext/deflate_ruby/matchfinder_impl.h +122 -0
  53. data/ext/deflate_ruby/utils.c +141 -0
  54. data/ext/deflate_ruby/zlib_compress.c +82 -0
  55. data/ext/deflate_ruby/zlib_constants.h +21 -0
  56. data/ext/deflate_ruby/zlib_decompress.c +104 -0
  57. data/lib/deflate_ruby/version.rb +1 -1
  58. data/lib/deflate_ruby.rb +1 -63
  59. data/sig/deflate_ruby.rbs +4 -0
  60. data/test/test_deflate_ruby.rb +220 -0
  61. data/test/test_helper.rb +6 -0
  62. metadata +90 -144
  63. data/ext/deflate_ruby/libdeflate/CMakeLists.txt +0 -270
  64. data/ext/deflate_ruby/libdeflate/NEWS.md +0 -494
  65. data/ext/deflate_ruby/libdeflate/README.md +0 -228
  66. data/ext/deflate_ruby/libdeflate/libdeflate-config.cmake.in +0 -3
  67. data/ext/deflate_ruby/libdeflate/libdeflate.pc.in +0 -18
  68. data/ext/deflate_ruby/libdeflate/programs/CMakeLists.txt +0 -105
  69. data/ext/deflate_ruby/libdeflate/programs/benchmark.c +0 -696
  70. data/ext/deflate_ruby/libdeflate/programs/checksum.c +0 -218
  71. data/ext/deflate_ruby/libdeflate/programs/config.h.in +0 -19
  72. data/ext/deflate_ruby/libdeflate/programs/gzip.c +0 -688
  73. data/ext/deflate_ruby/libdeflate/programs/prog_util.c +0 -521
  74. data/ext/deflate_ruby/libdeflate/programs/prog_util.h +0 -225
  75. data/ext/deflate_ruby/libdeflate/programs/test_checksums.c +0 -200
  76. data/ext/deflate_ruby/libdeflate/programs/test_custom_malloc.c +0 -155
  77. data/ext/deflate_ruby/libdeflate/programs/test_incomplete_codes.c +0 -385
  78. data/ext/deflate_ruby/libdeflate/programs/test_invalid_streams.c +0 -130
  79. data/ext/deflate_ruby/libdeflate/programs/test_litrunlen_overflow.c +0 -72
  80. data/ext/deflate_ruby/libdeflate/programs/test_overread.c +0 -95
  81. data/ext/deflate_ruby/libdeflate/programs/test_slow_decompression.c +0 -472
  82. data/ext/deflate_ruby/libdeflate/programs/test_trailing_bytes.c +0 -151
  83. data/ext/deflate_ruby/libdeflate/programs/test_util.c +0 -237
  84. data/ext/deflate_ruby/libdeflate/programs/test_util.h +0 -61
  85. data/ext/deflate_ruby/libdeflate/programs/tgetopt.c +0 -118
  86. data/ext/deflate_ruby/libdeflate/scripts/android_build.sh +0 -118
  87. data/ext/deflate_ruby/libdeflate/scripts/android_tests.sh +0 -69
  88. data/ext/deflate_ruby/libdeflate/scripts/benchmark.sh +0 -10
  89. data/ext/deflate_ruby/libdeflate/scripts/checksum.sh +0 -10
  90. data/ext/deflate_ruby/libdeflate/scripts/checksum_benchmarks.sh +0 -253
  91. data/ext/deflate_ruby/libdeflate/scripts/cmake-helper.sh +0 -17
  92. data/ext/deflate_ruby/libdeflate/scripts/deflate_benchmarks.sh +0 -119
  93. data/ext/deflate_ruby/libdeflate/scripts/exec_tests.sh +0 -38
  94. data/ext/deflate_ruby/libdeflate/scripts/gen-release-archives.sh +0 -37
  95. data/ext/deflate_ruby/libdeflate/scripts/gen_bitreverse_tab.py +0 -19
  96. data/ext/deflate_ruby/libdeflate/scripts/gen_crc32_multipliers.c +0 -199
  97. data/ext/deflate_ruby/libdeflate/scripts/gen_crc32_tables.c +0 -105
  98. data/ext/deflate_ruby/libdeflate/scripts/gen_default_litlen_costs.py +0 -44
  99. data/ext/deflate_ruby/libdeflate/scripts/gen_offset_slot_map.py +0 -29
  100. data/ext/deflate_ruby/libdeflate/scripts/gzip_tests.sh +0 -523
  101. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_compress/corpus/0 +0 -0
  102. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_compress/fuzz.c +0 -95
  103. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_decompress/corpus/0 +0 -3
  104. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_decompress/fuzz.c +0 -62
  105. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/fuzz.sh +0 -108
  106. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/gzip_decompress/corpus/0 +0 -0
  107. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/gzip_decompress/fuzz.c +0 -19
  108. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/zlib_decompress/corpus/0 +0 -3
  109. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/zlib_decompress/fuzz.c +0 -19
  110. data/ext/deflate_ruby/libdeflate/scripts/run_tests.sh +0 -416
  111. data/ext/deflate_ruby/libdeflate/scripts/toolchain-i686-w64-mingw32.cmake +0 -8
  112. data/ext/deflate_ruby/libdeflate/scripts/toolchain-x86_64-w64-mingw32.cmake +0 -8
  113. /data/ext/deflate_ruby/{libdeflate/lib/adler32.c → adler32.c} +0 -0
  114. /data/ext/deflate_ruby/{libdeflate/lib/x86/adler32_template.h → adler32_template.h} +0 -0
  115. /data/ext/deflate_ruby/{libdeflate/lib/bt_matchfinder.h → bt_matchfinder.h} +0 -0
  116. /data/ext/deflate_ruby/{libdeflate/lib/cpu_features_common.h → cpu_features_common.h} +0 -0
  117. /data/ext/deflate_ruby/{libdeflate/lib/crc32.c → crc32.c} +0 -0
  118. /data/ext/deflate_ruby/{libdeflate/lib/arm/crc32_pmull_helpers.h → crc32_pmull_helpers.h} +0 -0
  119. /data/ext/deflate_ruby/{libdeflate/lib/arm/crc32_pmull_wide.h → crc32_pmull_wide.h} +0 -0
  120. /data/ext/deflate_ruby/{libdeflate/lib/x86/decompress_impl.h → decompress_impl.h} +0 -0
  121. /data/ext/deflate_ruby/{libdeflate/lib/decompress_template.h → decompress_template.h} +0 -0
  122. /data/ext/deflate_ruby/{libdeflate/lib/deflate_compress.h → deflate_compress.h} +0 -0
  123. /data/ext/deflate_ruby/{libdeflate/lib/deflate_constants.h → deflate_constants.h} +0 -0
  124. /data/ext/deflate_ruby/{libdeflate/lib/deflate_decompress.c → deflate_decompress.c} +0 -0
  125. /data/ext/deflate_ruby/{libdeflate/lib/gzip_compress.c → gzip_compress.c} +0 -0
  126. /data/ext/deflate_ruby/{libdeflate/lib/gzip_constants.h → gzip_constants.h} +0 -0
  127. /data/ext/deflate_ruby/{libdeflate/lib/gzip_decompress.c → gzip_decompress.c} +0 -0
  128. /data/ext/deflate_ruby/{libdeflate/lib/hc_matchfinder.h → hc_matchfinder.h} +0 -0
  129. /data/ext/deflate_ruby/{libdeflate/lib/ht_matchfinder.h → ht_matchfinder.h} +0 -0
  130. /data/ext/deflate_ruby/{libdeflate/lib/lib_common.h → lib_common.h} +0 -0
  131. /data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/cpu_features.c +0 -0
  132. /data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/cpu_features.h +0 -0
  133. /data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/matchfinder_impl.h +0 -0
  134. /data/ext/deflate_ruby/libdeflate/{lib/riscv → riscv}/matchfinder_impl.h +0 -0
  135. /data/ext/deflate_ruby/libdeflate/{lib/utils.c → utils.c} +0 -0
  136. /data/ext/deflate_ruby/libdeflate/{lib/x86 → x86}/matchfinder_impl.h +0 -0
  137. /data/ext/deflate_ruby/libdeflate/{lib/zlib_compress.c → zlib_compress.c} +0 -0
  138. /data/ext/deflate_ruby/libdeflate/{lib/zlib_constants.h → zlib_constants.h} +0 -0
  139. /data/ext/deflate_ruby/libdeflate/{lib/zlib_decompress.c → zlib_decompress.c} +0 -0
@@ -1,688 +0,0 @@
1
- /*
2
- * gzip.c - a file compression and decompression program
3
- *
4
- * Copyright 2016 Eric Biggers
5
- *
6
- * Permission is hereby granted, free of charge, to any person
7
- * obtaining a copy of this software and associated documentation
8
- * files (the "Software"), to deal in the Software without
9
- * restriction, including without limitation the rights to use,
10
- * copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- * copies of the Software, and to permit persons to whom the
12
- * Software is furnished to do so, subject to the following
13
- * conditions:
14
- *
15
- * The above copyright notice and this permission notice shall be
16
- * included in all copies or substantial portions of the Software.
17
- *
18
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
- * OTHER DEALINGS IN THE SOFTWARE.
26
- */
27
-
28
- #include "prog_util.h"
29
-
30
- #include <errno.h>
31
- #include <sys/stat.h>
32
- #ifdef _WIN32
33
- # include <sys/utime.h>
34
- #else
35
- # include <sys/time.h>
36
- # include <unistd.h>
37
- # include <utime.h>
38
- #endif
39
-
40
- #define GZIP_MIN_HEADER_SIZE 10
41
- #define GZIP_FOOTER_SIZE 8
42
- #define GZIP_MIN_OVERHEAD (GZIP_MIN_HEADER_SIZE + GZIP_FOOTER_SIZE)
43
- #define GZIP_ID1 0x1F
44
- #define GZIP_ID2 0x8B
45
-
46
- struct options {
47
- bool to_stdout;
48
- bool decompress;
49
- bool force;
50
- bool keep;
51
- bool test;
52
- int compression_level;
53
- const tchar *suffix;
54
- };
55
-
56
- static const tchar *const optstring = T("1::2::3::4::5::6::7::8::9::cdfhknqS:tV");
57
-
58
- static void
59
- show_usage(FILE *fp)
60
- {
61
- fprintf(fp,
62
- "Usage: %"TS" [-LEVEL] [-cdfhkqtV] [-S SUF] FILE...\n"
63
- "Compress or decompress the specified FILEs.\n"
64
- "\n"
65
- "Options:\n"
66
- " -1 fastest (worst) compression\n"
67
- " -6 medium compression (default)\n"
68
- " -12 slowest (best) compression\n"
69
- " -c write to standard output\n"
70
- " -d decompress\n"
71
- " -f overwrite existing output files; (de)compress hard-linked files;\n"
72
- " allow reading/writing compressed data from/to terminal;\n"
73
- " with gunzip -c, pass through non-gzipped data\n"
74
- " -h print this help\n"
75
- " -k don't delete input files\n"
76
- " -q suppress warnings\n"
77
- " -S SUF use suffix SUF instead of .gz\n"
78
- " -t test file integrity\n"
79
- " -V show version and legal information\n",
80
- prog_invocation_name);
81
- }
82
-
83
- static void
84
- show_version(void)
85
- {
86
- printf(
87
- "gzip compression program v" LIBDEFLATE_VERSION_STRING "\n"
88
- "Copyright 2016 Eric Biggers\n"
89
- "\n"
90
- "This program is free software which may be modified and/or redistributed\n"
91
- "under the terms of the MIT license. There is NO WARRANTY, to the extent\n"
92
- "permitted by law. See the COPYING file for details.\n"
93
- );
94
- }
95
-
96
- /* Was the program invoked in decompression mode? */
97
- static bool
98
- is_gunzip(void)
99
- {
100
- if (tstrxcmp(prog_invocation_name, T("gunzip")) == 0)
101
- return true;
102
- if (tstrxcmp(prog_invocation_name, T("libdeflate-gunzip")) == 0)
103
- return true;
104
- #ifdef _WIN32
105
- if (tstrxcmp(prog_invocation_name, T("gunzip.exe")) == 0)
106
- return true;
107
- if (tstrxcmp(prog_invocation_name, T("libdeflate-gunzip.exe")) == 0)
108
- return true;
109
- #endif
110
- return false;
111
- }
112
-
113
- static const tchar *
114
- get_suffix(const tchar *path, const tchar *suffix)
115
- {
116
- size_t path_len = tstrlen(path);
117
- size_t suffix_len = tstrlen(suffix);
118
- const tchar *p;
119
-
120
- if (path_len <= suffix_len)
121
- return NULL;
122
- p = &path[path_len - suffix_len];
123
- if (tstrxcmp(p, suffix) == 0)
124
- return p;
125
- return NULL;
126
- }
127
-
128
- static bool
129
- has_suffix(const tchar *path, const tchar *suffix)
130
- {
131
- return get_suffix(path, suffix) != NULL;
132
- }
133
-
134
- static tchar *
135
- append_suffix(const tchar *path, const tchar *suffix)
136
- {
137
- size_t path_len = tstrlen(path);
138
- size_t suffix_len = tstrlen(suffix);
139
- tchar *suffixed_path;
140
-
141
- suffixed_path = xmalloc((path_len + suffix_len + 1) * sizeof(tchar));
142
- if (suffixed_path == NULL)
143
- return NULL;
144
- tmemcpy(suffixed_path, path, path_len);
145
- tmemcpy(&suffixed_path[path_len], suffix, suffix_len + 1);
146
- return suffixed_path;
147
- }
148
-
149
- static int
150
- do_compress(struct libdeflate_compressor *compressor,
151
- struct file_stream *in, struct file_stream *out)
152
- {
153
- const void *uncompressed_data = in->mmap_mem;
154
- size_t uncompressed_size = in->mmap_size;
155
- void *compressed_data;
156
- size_t actual_compressed_size;
157
- size_t max_compressed_size;
158
- int ret;
159
-
160
- max_compressed_size = libdeflate_gzip_compress_bound(compressor,
161
- uncompressed_size);
162
- compressed_data = xmalloc(max_compressed_size);
163
- if (compressed_data == NULL) {
164
- msg("%"TS": file is probably too large to be processed by this "
165
- "program", in->name);
166
- ret = -1;
167
- goto out;
168
- }
169
-
170
- actual_compressed_size = libdeflate_gzip_compress(compressor,
171
- uncompressed_data,
172
- uncompressed_size,
173
- compressed_data,
174
- max_compressed_size);
175
- if (actual_compressed_size == 0) {
176
- msg("Bug in libdeflate_gzip_compress_bound()!");
177
- ret = -1;
178
- goto out;
179
- }
180
-
181
- ret = full_write(out, compressed_data, actual_compressed_size);
182
- out:
183
- free(compressed_data);
184
- return ret;
185
- }
186
-
187
- static int
188
- do_decompress(struct libdeflate_decompressor *decompressor,
189
- struct file_stream *in, struct file_stream *out,
190
- const struct options *options)
191
- {
192
- const u8 *compressed_data = in->mmap_mem;
193
- size_t compressed_size = in->mmap_size;
194
- void *uncompressed_data = NULL;
195
- size_t uncompressed_size;
196
- size_t max_uncompressed_size;
197
- size_t actual_in_nbytes;
198
- size_t actual_out_nbytes;
199
- enum libdeflate_result result;
200
- int ret = 0;
201
-
202
- if (compressed_size < GZIP_MIN_OVERHEAD ||
203
- compressed_data[0] != GZIP_ID1 ||
204
- compressed_data[1] != GZIP_ID2) {
205
- if (options->force && options->to_stdout)
206
- return full_write(out, compressed_data, compressed_size);
207
- msg("%"TS": not in gzip format", in->name);
208
- return -1;
209
- }
210
-
211
- /*
212
- * Use the ISIZE field as a hint for the decompressed data size. It may
213
- * need to be increased later, however, because the file may contain
214
- * multiple gzip members and the particular ISIZE we happen to use may
215
- * not be the largest; or the real size may be >= 4 GiB, causing ISIZE
216
- * to overflow. In any case, make sure to allocate at least one byte.
217
- */
218
- uncompressed_size =
219
- get_unaligned_le32(&compressed_data[compressed_size - 4]);
220
- if (uncompressed_size == 0)
221
- uncompressed_size = 1;
222
-
223
- /*
224
- * DEFLATE cannot expand data more than 1032x, so there's no need to
225
- * ever allocate a buffer more than 1032 times larger than the
226
- * compressed data. This is a fail-safe, albeit not a very good one, if
227
- * ISIZE becomes corrupted on a small file. (The 1032x number comes
228
- * from each 2 bits generating a 258-byte match. This is a hard upper
229
- * bound; the real upper bound is slightly smaller due to overhead.)
230
- */
231
- if (compressed_size <= SIZE_MAX / 1032)
232
- max_uncompressed_size = compressed_size * 1032;
233
- else
234
- max_uncompressed_size = SIZE_MAX;
235
-
236
- do {
237
- if (uncompressed_data == NULL) {
238
- uncompressed_size = MIN(uncompressed_size,
239
- max_uncompressed_size);
240
- uncompressed_data = xmalloc(uncompressed_size);
241
- if (uncompressed_data == NULL) {
242
- msg("%"TS": file is probably too large to be "
243
- "processed by this program", in->name);
244
- ret = -1;
245
- goto out;
246
- }
247
- }
248
-
249
- result = libdeflate_gzip_decompress_ex(decompressor,
250
- compressed_data,
251
- compressed_size,
252
- uncompressed_data,
253
- uncompressed_size,
254
- &actual_in_nbytes,
255
- &actual_out_nbytes);
256
-
257
- if (result == LIBDEFLATE_INSUFFICIENT_SPACE) {
258
- if (uncompressed_size >= max_uncompressed_size) {
259
- msg("Bug in libdeflate_gzip_decompress_ex(): data expanded too much!");
260
- ret = -1;
261
- goto out;
262
- }
263
- if (uncompressed_size * 2 <= uncompressed_size) {
264
- msg("%"TS": file corrupt or too large to be "
265
- "processed by this program", in->name);
266
- ret = -1;
267
- goto out;
268
- }
269
- uncompressed_size *= 2;
270
- free(uncompressed_data);
271
- uncompressed_data = NULL;
272
- continue;
273
- }
274
-
275
- if (result != LIBDEFLATE_SUCCESS) {
276
- msg("%"TS": file corrupt or not in gzip format",
277
- in->name);
278
- ret = -1;
279
- goto out;
280
- }
281
-
282
- if (actual_in_nbytes == 0 ||
283
- actual_in_nbytes > compressed_size ||
284
- actual_out_nbytes > uncompressed_size) {
285
- msg("Bug in libdeflate_gzip_decompress_ex(): impossible actual_nbytes value!");
286
- ret = -1;
287
- goto out;
288
- }
289
-
290
- if (!options->test) {
291
- ret = full_write(out, uncompressed_data, actual_out_nbytes);
292
- if (ret != 0)
293
- goto out;
294
- }
295
-
296
- compressed_data += actual_in_nbytes;
297
- compressed_size -= actual_in_nbytes;
298
-
299
- } while (compressed_size != 0);
300
- out:
301
- free(uncompressed_data);
302
- return ret;
303
- }
304
-
305
- static int
306
- stat_file(struct file_stream *in, stat_t *stbuf, bool allow_hard_links)
307
- {
308
- if (tfstat(in->fd, stbuf) != 0) {
309
- msg("%"TS": unable to stat file", in->name);
310
- return -1;
311
- }
312
-
313
- if (!S_ISREG(stbuf->st_mode) && !in->is_standard_stream) {
314
- warn("%"TS" is %s -- skipping",
315
- in->name, S_ISDIR(stbuf->st_mode) ? "a directory" :
316
- "not a regular file");
317
- return -2;
318
- }
319
-
320
- if (stbuf->st_nlink > 1 && !allow_hard_links) {
321
- warn("%"TS" has multiple hard links -- skipping (use -f to process anyway)",
322
- in->name);
323
- return -2;
324
- }
325
-
326
- return 0;
327
- }
328
-
329
- static void
330
- restore_mode(struct file_stream *out, const stat_t *stbuf)
331
- {
332
- #ifndef _WIN32
333
- if (fchmod(out->fd, stbuf->st_mode) != 0)
334
- msg_errno("%"TS": unable to preserve mode", out->name);
335
- #endif
336
- }
337
-
338
- static void
339
- restore_owner_and_group(struct file_stream *out, const stat_t *stbuf)
340
- {
341
- #ifndef _WIN32
342
- if (fchown(out->fd, stbuf->st_uid, stbuf->st_gid) != 0) {
343
- msg_errno("%"TS": unable to preserve owner and group",
344
- out->name);
345
- }
346
- #endif
347
- }
348
-
349
- static void
350
- restore_timestamps(struct file_stream *out, const tchar *newpath,
351
- const stat_t *stbuf)
352
- {
353
- int ret;
354
- #ifdef __APPLE__
355
- struct timespec times[2] = { stbuf->st_atimespec, stbuf->st_mtimespec };
356
-
357
- ret = futimens(out->fd, times);
358
- #elif (defined(HAVE_FUTIMENS) && defined(HAVE_STAT_NANOSECOND_PRECISION)) || \
359
- /* fallback detection method for direct compilation */ \
360
- (!defined(HAVE_CONFIG_H) && defined(UTIME_NOW))
361
- struct timespec times[2] = { stbuf->st_atim, stbuf->st_mtim };
362
-
363
- ret = futimens(out->fd, times);
364
- #else
365
- struct tutimbuf times = { stbuf->st_atime, stbuf->st_mtime };
366
-
367
- ret = tutime(newpath, &times);
368
- #endif
369
- if (ret != 0)
370
- msg_errno("%"TS": unable to preserve timestamps", out->name);
371
- }
372
-
373
- static void
374
- restore_metadata(struct file_stream *out, const tchar *newpath,
375
- const stat_t *stbuf)
376
- {
377
- restore_mode(out, stbuf);
378
- restore_owner_and_group(out, stbuf);
379
- restore_timestamps(out, newpath, stbuf);
380
- }
381
-
382
- static int
383
- decompress_file(struct libdeflate_decompressor *decompressor, const tchar *path,
384
- const struct options *options)
385
- {
386
- tchar *oldpath = (tchar *)path;
387
- tchar *newpath = NULL;
388
- struct file_stream in;
389
- struct file_stream out;
390
- stat_t stbuf;
391
- int ret;
392
- int ret2;
393
-
394
- if (path != NULL) {
395
- const tchar *suffix = get_suffix(path, options->suffix);
396
- if (suffix == NULL) {
397
- /*
398
- * Input file is unsuffixed. If the file doesn't exist,
399
- * then try it suffixed. Otherwise, if we're not
400
- * writing to stdout, skip the file with warning status.
401
- * Otherwise, go ahead and try to open the file anyway
402
- * (which will very likely fail).
403
- */
404
- if (tstat(path, &stbuf) != 0 && errno == ENOENT) {
405
- oldpath = append_suffix(path, options->suffix);
406
- if (oldpath == NULL)
407
- return -1;
408
- if (!options->to_stdout)
409
- newpath = (tchar *)path;
410
- } else if (!options->to_stdout) {
411
- warn("\"%"TS"\" does not end with the %"TS" suffix -- skipping",
412
- path, options->suffix);
413
- return -2;
414
- }
415
- } else if (!options->to_stdout) {
416
- /*
417
- * Input file is suffixed, and we're not writing to
418
- * stdout. Strip the suffix to get the path to the
419
- * output file.
420
- */
421
- newpath = xmalloc((suffix - oldpath + 1) *
422
- sizeof(tchar));
423
- if (newpath == NULL)
424
- return -1;
425
- tmemcpy(newpath, oldpath, suffix - oldpath);
426
- newpath[suffix - oldpath] = '\0';
427
- }
428
- }
429
-
430
- ret = xopen_for_read(oldpath, options->force || options->to_stdout,
431
- &in);
432
- if (ret != 0)
433
- goto out_free_paths;
434
-
435
- if (!options->force && isatty(in.fd)) {
436
- msg("Refusing to read compressed data from terminal. "
437
- "Use -f to override.\nFor help, use -h.");
438
- ret = -1;
439
- goto out_close_in;
440
- }
441
-
442
- ret = stat_file(&in, &stbuf, options->force || options->keep ||
443
- oldpath == NULL || newpath == NULL);
444
- if (ret != 0)
445
- goto out_close_in;
446
-
447
- ret = xopen_for_write(newpath, options->force, &out);
448
- if (ret != 0)
449
- goto out_close_in;
450
-
451
- /* TODO: need a streaming-friendly solution */
452
- ret = map_file_contents(&in, stbuf.st_size);
453
- if (ret != 0)
454
- goto out_close_out;
455
-
456
- ret = do_decompress(decompressor, &in, &out, options);
457
- if (ret != 0)
458
- goto out_close_out;
459
-
460
- if (oldpath != NULL && newpath != NULL)
461
- restore_metadata(&out, newpath, &stbuf);
462
- ret = 0;
463
- out_close_out:
464
- ret2 = xclose(&out);
465
- if (ret == 0)
466
- ret = ret2;
467
- if (ret != 0 && newpath != NULL)
468
- tunlink(newpath);
469
- out_close_in:
470
- xclose(&in);
471
- if (ret == 0 && oldpath != NULL && newpath != NULL && !options->keep)
472
- tunlink(oldpath);
473
- out_free_paths:
474
- if (newpath != path)
475
- free(newpath);
476
- if (oldpath != path)
477
- free(oldpath);
478
- return ret;
479
- }
480
-
481
- static int
482
- compress_file(struct libdeflate_compressor *compressor, const tchar *path,
483
- const struct options *options)
484
- {
485
- tchar *newpath = NULL;
486
- struct file_stream in;
487
- struct file_stream out;
488
- stat_t stbuf;
489
- int ret;
490
- int ret2;
491
-
492
- if (path != NULL && !options->to_stdout) {
493
- if (!options->force && has_suffix(path, options->suffix)) {
494
- msg("%"TS": already has %"TS" suffix -- skipping",
495
- path, options->suffix);
496
- return 0;
497
- }
498
- newpath = append_suffix(path, options->suffix);
499
- if (newpath == NULL)
500
- return -1;
501
- }
502
-
503
- ret = xopen_for_read(path, options->force || options->to_stdout, &in);
504
- if (ret != 0)
505
- goto out_free_newpath;
506
-
507
- ret = stat_file(&in, &stbuf, options->force || options->keep ||
508
- path == NULL || newpath == NULL);
509
- if (ret != 0)
510
- goto out_close_in;
511
-
512
- ret = xopen_for_write(newpath, options->force, &out);
513
- if (ret != 0)
514
- goto out_close_in;
515
-
516
- if (!options->force && isatty(out.fd)) {
517
- msg("Refusing to write compressed data to terminal. "
518
- "Use -f to override.\nFor help, use -h.");
519
- ret = -1;
520
- goto out_close_out;
521
- }
522
-
523
- /* TODO: need a streaming-friendly solution */
524
- ret = map_file_contents(&in, stbuf.st_size);
525
- if (ret != 0)
526
- goto out_close_out;
527
-
528
- ret = do_compress(compressor, &in, &out);
529
- if (ret != 0)
530
- goto out_close_out;
531
-
532
- if (path != NULL && newpath != NULL)
533
- restore_metadata(&out, newpath, &stbuf);
534
- ret = 0;
535
- out_close_out:
536
- ret2 = xclose(&out);
537
- if (ret == 0)
538
- ret = ret2;
539
- if (ret != 0 && newpath != NULL)
540
- tunlink(newpath);
541
- out_close_in:
542
- xclose(&in);
543
- if (ret == 0 && path != NULL && newpath != NULL && !options->keep)
544
- tunlink(path);
545
- out_free_newpath:
546
- free(newpath);
547
- return ret;
548
- }
549
-
550
- int
551
- tmain(int argc, tchar *argv[])
552
- {
553
- tchar *default_file_list[] = { NULL };
554
- struct options options;
555
- int opt_char;
556
- int i;
557
- int ret;
558
-
559
- begin_program(argv);
560
-
561
- options.to_stdout = false;
562
- options.decompress = is_gunzip();
563
- options.force = false;
564
- options.keep = false;
565
- options.test = false;
566
- options.compression_level = 6;
567
- options.suffix = T(".gz");
568
-
569
- while ((opt_char = tgetopt(argc, argv, optstring)) != -1) {
570
- switch (opt_char) {
571
- case '1':
572
- case '2':
573
- case '3':
574
- case '4':
575
- case '5':
576
- case '6':
577
- case '7':
578
- case '8':
579
- case '9':
580
- options.compression_level =
581
- parse_compression_level(opt_char, toptarg);
582
- if (options.compression_level < 0)
583
- return 1;
584
- break;
585
- case 'c':
586
- options.to_stdout = true;
587
- break;
588
- case 'd':
589
- options.decompress = true;
590
- break;
591
- case 'f':
592
- options.force = true;
593
- break;
594
- case 'h':
595
- show_usage(stdout);
596
- return 0;
597
- case 'k':
598
- options.keep = true;
599
- break;
600
- case 'n':
601
- /*
602
- * -n means don't save or restore the original filename
603
- * in the gzip header. Currently this implementation
604
- * already behaves this way by default, so accept the
605
- * option as a no-op.
606
- */
607
- break;
608
- case 'q':
609
- suppress_warnings = true;
610
- break;
611
- case 'S':
612
- options.suffix = toptarg;
613
- if (options.suffix[0] == T('\0')) {
614
- msg("invalid suffix");
615
- return 1;
616
- }
617
- break;
618
- case 't':
619
- options.test = true;
620
- options.decompress = true;
621
- options.to_stdout = true;
622
- /*
623
- * -t behaves just like the more commonly used -c
624
- * option, except that -t doesn't actually write
625
- * anything. For ease of implementation, just pretend
626
- * that -c was specified too.
627
- */
628
- break;
629
- case 'V':
630
- show_version();
631
- return 0;
632
- default:
633
- show_usage(stderr);
634
- return 1;
635
- }
636
- }
637
-
638
- argv += toptind;
639
- argc -= toptind;
640
-
641
- if (argc == 0) {
642
- argv = default_file_list;
643
- argc = ARRAY_LEN(default_file_list);
644
- } else {
645
- for (i = 0; i < argc; i++)
646
- if (argv[i][0] == '-' && argv[i][1] == '\0')
647
- argv[i] = NULL;
648
- }
649
-
650
- ret = 0;
651
- if (options.decompress) {
652
- struct libdeflate_decompressor *d;
653
-
654
- d = alloc_decompressor();
655
- if (d == NULL)
656
- return 1;
657
-
658
- for (i = 0; i < argc; i++)
659
- ret |= -decompress_file(d, argv[i], &options);
660
-
661
- libdeflate_free_decompressor(d);
662
- } else {
663
- struct libdeflate_compressor *c;
664
-
665
- c = alloc_compressor(options.compression_level);
666
- if (c == NULL)
667
- return 1;
668
-
669
- for (i = 0; i < argc; i++)
670
- ret |= -compress_file(c, argv[i], &options);
671
-
672
- libdeflate_free_compressor(c);
673
- }
674
-
675
- switch (ret) {
676
- case 0:
677
- /* No warnings or errors */
678
- return 0;
679
- case 2:
680
- /* At least one warning, but no errors */
681
- if (suppress_warnings)
682
- return 0;
683
- return 2;
684
- default:
685
- /* At least one error */
686
- return 1;
687
- }
688
- }