libdeflate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +1 -0
  6. data/.rubocop_todo.yml +9 -0
  7. data/.travis.yml +5 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +52 -0
  11. data/Rakefile +15 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/ext/libdeflate/extconf.rb +14 -0
  15. data/ext/libdeflate/libdeflate/.gitignore +19 -0
  16. data/ext/libdeflate/libdeflate/COPYING +21 -0
  17. data/ext/libdeflate/libdeflate/Makefile +231 -0
  18. data/ext/libdeflate/libdeflate/Makefile.msc +64 -0
  19. data/ext/libdeflate/libdeflate/NEWS +57 -0
  20. data/ext/libdeflate/libdeflate/README.md +170 -0
  21. data/ext/libdeflate/libdeflate/common/common_defs.h +351 -0
  22. data/ext/libdeflate/libdeflate/common/compiler_gcc.h +134 -0
  23. data/ext/libdeflate/libdeflate/common/compiler_msc.h +95 -0
  24. data/ext/libdeflate/libdeflate/lib/adler32.c +213 -0
  25. data/ext/libdeflate/libdeflate/lib/adler32_impl.h +281 -0
  26. data/ext/libdeflate/libdeflate/lib/aligned_malloc.c +57 -0
  27. data/ext/libdeflate/libdeflate/lib/aligned_malloc.h +13 -0
  28. data/ext/libdeflate/libdeflate/lib/bt_matchfinder.h +357 -0
  29. data/ext/libdeflate/libdeflate/lib/crc32.c +368 -0
  30. data/ext/libdeflate/libdeflate/lib/crc32_impl.h +286 -0
  31. data/ext/libdeflate/libdeflate/lib/crc32_table.h +526 -0
  32. data/ext/libdeflate/libdeflate/lib/decompress_impl.h +404 -0
  33. data/ext/libdeflate/libdeflate/lib/deflate_compress.c +2817 -0
  34. data/ext/libdeflate/libdeflate/lib/deflate_compress.h +14 -0
  35. data/ext/libdeflate/libdeflate/lib/deflate_constants.h +66 -0
  36. data/ext/libdeflate/libdeflate/lib/deflate_decompress.c +889 -0
  37. data/ext/libdeflate/libdeflate/lib/gzip_compress.c +95 -0
  38. data/ext/libdeflate/libdeflate/lib/gzip_constants.h +45 -0
  39. data/ext/libdeflate/libdeflate/lib/gzip_decompress.c +130 -0
  40. data/ext/libdeflate/libdeflate/lib/hc_matchfinder.h +405 -0
  41. data/ext/libdeflate/libdeflate/lib/lib_common.h +35 -0
  42. data/ext/libdeflate/libdeflate/lib/matchfinder_avx2.h +53 -0
  43. data/ext/libdeflate/libdeflate/lib/matchfinder_common.h +205 -0
  44. data/ext/libdeflate/libdeflate/lib/matchfinder_neon.h +61 -0
  45. data/ext/libdeflate/libdeflate/lib/matchfinder_sse2.h +53 -0
  46. data/ext/libdeflate/libdeflate/lib/unaligned.h +202 -0
  47. data/ext/libdeflate/libdeflate/lib/x86_cpu_features.c +169 -0
  48. data/ext/libdeflate/libdeflate/lib/x86_cpu_features.h +48 -0
  49. data/ext/libdeflate/libdeflate/lib/zlib_compress.c +87 -0
  50. data/ext/libdeflate/libdeflate/lib/zlib_constants.h +21 -0
  51. data/ext/libdeflate/libdeflate/lib/zlib_decompress.c +91 -0
  52. data/ext/libdeflate/libdeflate/libdeflate.h +274 -0
  53. data/ext/libdeflate/libdeflate/programs/benchmark.c +558 -0
  54. data/ext/libdeflate/libdeflate/programs/checksum.c +197 -0
  55. data/ext/libdeflate/libdeflate/programs/detect.sh +62 -0
  56. data/ext/libdeflate/libdeflate/programs/gzip.c +603 -0
  57. data/ext/libdeflate/libdeflate/programs/prog_util.c +530 -0
  58. data/ext/libdeflate/libdeflate/programs/prog_util.h +162 -0
  59. data/ext/libdeflate/libdeflate/programs/test_checksums.c +135 -0
  60. data/ext/libdeflate/libdeflate/programs/tgetopt.c +118 -0
  61. data/ext/libdeflate/libdeflate/tools/afl-fuzz/Makefile +12 -0
  62. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_compress/fuzz.c +40 -0
  63. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_compress/inputs/0 +0 -0
  64. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_decompress/fuzz.c +28 -0
  65. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_decompress/inputs/0 +3 -0
  66. data/ext/libdeflate/libdeflate/tools/afl-fuzz/gzip_decompress/fuzz.c +28 -0
  67. data/ext/libdeflate/libdeflate/tools/afl-fuzz/gzip_decompress/inputs/0 +0 -0
  68. data/ext/libdeflate/libdeflate/tools/afl-fuzz/prepare_for_fuzz.sh +14 -0
  69. data/ext/libdeflate/libdeflate/tools/afl-fuzz/zlib_decompress/fuzz.c +28 -0
  70. data/ext/libdeflate/libdeflate/tools/afl-fuzz/zlib_decompress/inputs/0 +3 -0
  71. data/ext/libdeflate/libdeflate/tools/android_build.sh +104 -0
  72. data/ext/libdeflate/libdeflate/tools/checksum_benchmarks.sh +76 -0
  73. data/ext/libdeflate/libdeflate/tools/exec_tests.sh +30 -0
  74. data/ext/libdeflate/libdeflate/tools/gen_crc32_multipliers.c +108 -0
  75. data/ext/libdeflate/libdeflate/tools/gen_crc32_table.c +100 -0
  76. data/ext/libdeflate/libdeflate/tools/gzip_tests.sh +412 -0
  77. data/ext/libdeflate/libdeflate/tools/make-windows-releases +21 -0
  78. data/ext/libdeflate/libdeflate/tools/mips_build.sh +9 -0
  79. data/ext/libdeflate/libdeflate/tools/msc_test.bat +3 -0
  80. data/ext/libdeflate/libdeflate/tools/pgo_build.sh +23 -0
  81. data/ext/libdeflate/libdeflate/tools/produce_gzip_benchmark_table.sh +37 -0
  82. data/ext/libdeflate/libdeflate/tools/run_tests.sh +305 -0
  83. data/ext/libdeflate/libdeflate/tools/windows_build.sh +10 -0
  84. data/ext/libdeflate/libdeflate_ext.c +389 -0
  85. data/ext/libdeflate/libdeflate_ext.h +8 -0
  86. data/lib/libdeflate.rb +2 -0
  87. data/lib/libdeflate/version.rb +3 -0
  88. data/libdeflate.gemspec +33 -0
  89. metadata +230 -0
@@ -0,0 +1,197 @@
1
+ /*
2
+ * checksum.c - Adler-32 and CRC-32 checksumming 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 <zlib.h>
29
+
30
+ #include "prog_util.h"
31
+
32
+ static const tchar *const optstring = T("Ahs:tZ");
33
+
34
+ static void
35
+ show_usage(FILE *fp)
36
+ {
37
+ fprintf(fp,
38
+ "Usage: %"TS" [-A] [-h] [-s SIZE] [-t] [-Z] [FILE]...\n"
39
+ "Calculate Adler-32 or CRC-32 checksums of the specified FILEs.\n"
40
+ "\n"
41
+ "Options:\n"
42
+ " -A use Adler-32 (default is CRC-32)\n"
43
+ " -h print this help\n"
44
+ " -s SIZE chunk size\n"
45
+ " -t show checksum speed, excluding I/O\n"
46
+ " -Z use zlib implementation instead of libdeflate\n",
47
+ program_invocation_name);
48
+ }
49
+
50
+ static u32
51
+ zlib_adler32(u32 adler, const void *buf, size_t len)
52
+ {
53
+ return adler32(adler, buf, len);
54
+ }
55
+
56
+ static u32
57
+ zlib_crc32(u32 crc, const void *buf, size_t len)
58
+ {
59
+ return crc32(crc, buf, len);
60
+ }
61
+
62
+ typedef u32 (*cksum_fn_t)(u32, const void *, size_t);
63
+
64
+ static int
65
+ checksum_stream(struct file_stream *in, cksum_fn_t cksum, u32 *sum,
66
+ void *buf, size_t bufsize, u64 *size_ret, u64 *elapsed_ret)
67
+ {
68
+ u64 size = 0;
69
+ u64 elapsed = 0;
70
+
71
+ for (;;) {
72
+ ssize_t ret;
73
+ u64 start_time;
74
+
75
+ ret = xread(in, buf, bufsize);
76
+ if (ret < 0)
77
+ return ret;
78
+ if (ret == 0)
79
+ break;
80
+
81
+ size += ret;
82
+ start_time = timer_ticks();
83
+ *sum = cksum(*sum, buf, ret);
84
+ elapsed += timer_ticks() - start_time;
85
+ }
86
+
87
+ if (elapsed == 0)
88
+ elapsed = 1;
89
+ *size_ret = size;
90
+ *elapsed_ret = elapsed;
91
+ return 0;
92
+ }
93
+
94
+ int
95
+ tmain(int argc, tchar *argv[])
96
+ {
97
+ bool use_adler32 = false;
98
+ bool use_zlib_impl = false;
99
+ bool do_timing = false;
100
+ void *buf;
101
+ size_t bufsize = 131072;
102
+ tchar *default_file_list[] = { NULL };
103
+ cksum_fn_t cksum;
104
+ int opt_char;
105
+ int i;
106
+ int ret;
107
+
108
+ program_invocation_name = get_filename(argv[0]);
109
+
110
+ while ((opt_char = tgetopt(argc, argv, optstring)) != -1) {
111
+ switch (opt_char) {
112
+ case 'A':
113
+ use_adler32 = true;
114
+ break;
115
+ case 'h':
116
+ show_usage(stdout);
117
+ return 0;
118
+ case 's':
119
+ bufsize = tstrtoul(toptarg, NULL, 10);
120
+ if (bufsize == 0) {
121
+ msg("invalid chunk size: \"%"TS"\"", toptarg);
122
+ return 1;
123
+ }
124
+ break;
125
+ case 't':
126
+ do_timing = true;
127
+ break;
128
+ case 'Z':
129
+ use_zlib_impl = true;
130
+ break;
131
+ default:
132
+ show_usage(stderr);
133
+ return 1;
134
+ }
135
+ }
136
+
137
+ argc -= toptind;
138
+ argv += toptind;
139
+
140
+ if (use_adler32) {
141
+ if (use_zlib_impl)
142
+ cksum = zlib_adler32;
143
+ else
144
+ cksum = libdeflate_adler32;
145
+ } else {
146
+ if (use_zlib_impl)
147
+ cksum = zlib_crc32;
148
+ else
149
+ cksum = libdeflate_crc32;
150
+ }
151
+
152
+ buf = xmalloc(bufsize);
153
+ if (buf == NULL)
154
+ return 1;
155
+
156
+ if (argc == 0) {
157
+ argv = default_file_list;
158
+ argc = ARRAY_LEN(default_file_list);
159
+ } else {
160
+ for (i = 0; i < argc; i++)
161
+ if (argv[i][0] == '-' && argv[i][1] == '\0')
162
+ argv[i] = NULL;
163
+ }
164
+
165
+ for (i = 0; i < argc; i++) {
166
+ struct file_stream in;
167
+ u32 sum = cksum(0, NULL, 0);
168
+ u64 size = 0;
169
+ u64 elapsed = 0;
170
+
171
+ ret = xopen_for_read(argv[i], true, &in);
172
+ if (ret != 0)
173
+ goto out;
174
+
175
+ ret = checksum_stream(&in, cksum, &sum, buf, bufsize,
176
+ &size, &elapsed);
177
+ if (ret == 0) {
178
+ if (do_timing) {
179
+ printf("%08"PRIx32"\t%"TS"\t"
180
+ "%"PRIu64" ms\t%"PRIu64" MB/s\n",
181
+ sum, in.name, timer_ticks_to_ms(elapsed),
182
+ timer_MB_per_s(size, elapsed));
183
+ } else {
184
+ printf("%08"PRIx32"\t%"TS"\t\n", sum, in.name);
185
+ }
186
+ }
187
+
188
+ xclose(&in);
189
+
190
+ if (ret != 0)
191
+ goto out;
192
+ }
193
+ ret = 0;
194
+ out:
195
+ free(buf);
196
+ return -ret;
197
+ }
@@ -0,0 +1,62 @@
1
+ #!/bin/sh
2
+
3
+ if [ -z "$CC" ]; then
4
+ CC=cc
5
+ fi
6
+
7
+ echo "/* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT. */"
8
+ echo "#ifndef CONFIG_H"
9
+ echo "#define CONFIG_H"
10
+
11
+ tmpfile="$(mktemp -t libdeflate_config.XXXXXXXX)"
12
+ trap "rm -f \"$tmpfile\"" EXIT
13
+
14
+ program_compiles() {
15
+ echo "$1" > "$tmpfile"
16
+ $CC $CFLAGS -x c "$tmpfile" -o /dev/null > /dev/null 2>&1
17
+ }
18
+
19
+ check_function() {
20
+ funcname="$1"
21
+ macro="HAVE_$(echo $funcname | tr a-z A-Z)"
22
+ echo
23
+ echo "/* Is the $funcname() function available? */"
24
+ if program_compiles "int main() { $funcname(); }"; then
25
+ echo "#define $macro 1"
26
+ else
27
+ echo "/* $macro is not set */"
28
+ fi
29
+ }
30
+
31
+ have_stat_field() {
32
+ program_compiles "#include <sys/types.h>
33
+ #include <sys/stat.h>
34
+ int main() { struct stat st; st.$1; }"
35
+ }
36
+
37
+ check_stat_nanosecond_precision() {
38
+ echo
39
+ echo "/* Does stat() provide nanosecond-precision timestamps? */"
40
+ if have_stat_field st_atim; then
41
+ echo "#define HAVE_STAT_NANOSECOND_PRECISION 1"
42
+ elif have_stat_field st_atimespec; then
43
+ # Nonstandard field names used by OS X and older BSDs
44
+ echo "#define HAVE_STAT_NANOSECOND_PRECISION 1"
45
+ echo "#define st_atim st_atimespec"
46
+ echo "#define st_mtim st_mtimespec"
47
+ echo "#define st_ctim st_ctimespec"
48
+ else
49
+ echo "/* HAVE_STAT_NANOSECOND_PRECISION is not set */"
50
+ fi
51
+ }
52
+
53
+ check_function clock_gettime
54
+ check_function futimens
55
+ check_function futimes
56
+ check_function posix_fadvise
57
+ check_function posix_madvise
58
+
59
+ check_stat_nanosecond_precision
60
+
61
+ echo
62
+ echo "#endif /* CONFIG_H */"
@@ -0,0 +1,603 @@
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/types.h>
32
+ #include <sys/stat.h>
33
+ #ifdef _WIN32
34
+ # include <sys/utime.h>
35
+ #else
36
+ # include <sys/time.h>
37
+ # include <unistd.h>
38
+ # include <utime.h>
39
+ #endif
40
+
41
+ struct options {
42
+ bool to_stdout;
43
+ bool decompress;
44
+ bool force;
45
+ bool keep;
46
+ int compression_level;
47
+ const tchar *suffix;
48
+ };
49
+
50
+ static const tchar *const optstring = T("1::2::3::4::5::6::7::8::9::cdfhknS:V");
51
+
52
+ static void
53
+ show_usage(FILE *fp)
54
+ {
55
+ fprintf(fp,
56
+ "Usage: %"TS" [-LEVEL] [-cdfhkV] [-S SUF] FILE...\n"
57
+ "Compress or decompress the specified FILEs.\n"
58
+ "\n"
59
+ "Options:\n"
60
+ " -1 fastest (worst) compression\n"
61
+ " -6 medium compression (default)\n"
62
+ " -12 slowest (best) compression\n"
63
+ " -c write to standard output\n"
64
+ " -d decompress\n"
65
+ " -f overwrite existing output files\n"
66
+ " -h print this help\n"
67
+ " -k don't delete input files\n"
68
+ " -S SUF use suffix SUF instead of .gz\n"
69
+ " -V show version and legal information\n",
70
+ program_invocation_name);
71
+ }
72
+
73
+ static void
74
+ show_version(void)
75
+ {
76
+ printf(
77
+ "gzip compression program v" LIBDEFLATE_VERSION_STRING "\n"
78
+ "Copyright 2016 Eric Biggers\n"
79
+ "\n"
80
+ "This program is free software which may be modified and/or redistributed\n"
81
+ "under the terms of the MIT license. There is NO WARRANTY, to the extent\n"
82
+ "permitted by law. See the COPYING file for details.\n"
83
+ );
84
+ }
85
+
86
+ /* Was the program invoked in decompression mode? */
87
+ static bool
88
+ is_gunzip(void)
89
+ {
90
+ if (tstrxcmp(program_invocation_name, T("gunzip")) == 0)
91
+ return true;
92
+ if (tstrxcmp(program_invocation_name, T("libdeflate-gunzip")) == 0)
93
+ return true;
94
+ #ifdef _WIN32
95
+ if (tstrxcmp(program_invocation_name, T("gunzip.exe")) == 0)
96
+ return true;
97
+ if (tstrxcmp(program_invocation_name, T("libdeflate-gunzip.exe")) == 0)
98
+ return true;
99
+ #endif
100
+ return false;
101
+ }
102
+
103
+ static const tchar *
104
+ get_suffix(const tchar *path, const tchar *suffix)
105
+ {
106
+ size_t path_len = tstrlen(path);
107
+ size_t suffix_len = tstrlen(suffix);
108
+ const tchar *p;
109
+
110
+ if (path_len <= suffix_len)
111
+ return NULL;
112
+ p = &path[path_len - suffix_len];
113
+ if (tstrxcmp(p, suffix) == 0)
114
+ return p;
115
+ return NULL;
116
+ }
117
+
118
+ static bool
119
+ has_suffix(const tchar *path, const tchar *suffix)
120
+ {
121
+ return get_suffix(path, suffix) != NULL;
122
+ }
123
+
124
+ static tchar *
125
+ append_suffix(const tchar *path, const tchar *suffix)
126
+ {
127
+ size_t path_len = tstrlen(path);
128
+ size_t suffix_len = tstrlen(suffix);
129
+ tchar *suffixed_path;
130
+
131
+ suffixed_path = xmalloc((path_len + suffix_len + 1) * sizeof(tchar));
132
+ if (suffixed_path == NULL)
133
+ return NULL;
134
+ tmemcpy(suffixed_path, path, path_len);
135
+ tmemcpy(&suffixed_path[path_len], suffix, suffix_len + 1);
136
+ return suffixed_path;
137
+ }
138
+
139
+ static int
140
+ do_compress(struct libdeflate_compressor *compressor,
141
+ struct file_stream *in, struct file_stream *out)
142
+ {
143
+ const void *uncompressed_data = in->mmap_mem;
144
+ size_t uncompressed_size = in->mmap_size;
145
+ void *compressed_data;
146
+ size_t actual_compressed_size;
147
+ size_t max_compressed_size;
148
+ int ret;
149
+
150
+ max_compressed_size = libdeflate_gzip_compress_bound(compressor,
151
+ uncompressed_size);
152
+ compressed_data = xmalloc(max_compressed_size);
153
+ if (compressed_data == NULL) {
154
+ msg("%"TS": file is probably too large to be processed by this "
155
+ "program", in->name);
156
+ ret = -1;
157
+ goto out;
158
+ }
159
+
160
+ actual_compressed_size = libdeflate_gzip_compress(compressor,
161
+ uncompressed_data,
162
+ uncompressed_size,
163
+ compressed_data,
164
+ max_compressed_size);
165
+ if (actual_compressed_size == 0) {
166
+ msg("Bug in libdeflate_gzip_compress_bound()!");
167
+ ret = -1;
168
+ goto out;
169
+ }
170
+
171
+ ret = full_write(out, compressed_data, actual_compressed_size);
172
+ out:
173
+ free(compressed_data);
174
+ return ret;
175
+ }
176
+
177
+ static u32
178
+ load_u32_gzip(const u8 *p)
179
+ {
180
+ return ((u32)p[0] << 0) | ((u32)p[1] << 8) |
181
+ ((u32)p[2] << 16) | ((u32)p[3] << 24);
182
+ }
183
+
184
+ static int
185
+ do_decompress(struct libdeflate_decompressor *decompressor,
186
+ struct file_stream *in, struct file_stream *out)
187
+ {
188
+ const u8 *compressed_data = in->mmap_mem;
189
+ size_t compressed_size = in->mmap_size;
190
+ void *uncompressed_data = NULL;
191
+ size_t uncompressed_size;
192
+ enum libdeflate_result result;
193
+ int ret;
194
+
195
+ if (compressed_size < sizeof(u32)) {
196
+ msg("%"TS": not in gzip format", in->name);
197
+ ret = -1;
198
+ goto out;
199
+ }
200
+
201
+ uncompressed_size = load_u32_gzip(&compressed_data[compressed_size - 4]);
202
+
203
+ uncompressed_data = xmalloc(uncompressed_size);
204
+ if (uncompressed_data == NULL) {
205
+ msg("%"TS": file is probably too large to be processed by this "
206
+ "program", in->name);
207
+ ret = -1;
208
+ goto out;
209
+ }
210
+
211
+ result = libdeflate_gzip_decompress(decompressor,
212
+ compressed_data,
213
+ compressed_size,
214
+ uncompressed_data,
215
+ uncompressed_size, NULL);
216
+
217
+ if (result == LIBDEFLATE_INSUFFICIENT_SPACE) {
218
+ msg("%"TS": file corrupt or too large to be processed by this "
219
+ "program", in->name);
220
+ ret = -1;
221
+ goto out;
222
+ }
223
+
224
+ if (result != LIBDEFLATE_SUCCESS) {
225
+ msg("%"TS": file corrupt or not in gzip format", in->name);
226
+ ret = -1;
227
+ goto out;
228
+ }
229
+
230
+ ret = full_write(out, uncompressed_data, uncompressed_size);
231
+ out:
232
+ free(uncompressed_data);
233
+ return ret;
234
+ }
235
+
236
+ static int
237
+ stat_file(struct file_stream *in, stat_t *stbuf, bool allow_hard_links)
238
+ {
239
+ if (tfstat(in->fd, stbuf) != 0) {
240
+ msg("%"TS": unable to stat file", in->name);
241
+ return -1;
242
+ }
243
+
244
+ if (!S_ISREG(stbuf->st_mode) && !in->is_standard_stream) {
245
+ msg("%"TS" is %s -- skipping",
246
+ in->name, S_ISDIR(stbuf->st_mode) ? "a directory" :
247
+ "not a regular file");
248
+ return -2;
249
+ }
250
+
251
+ if (stbuf->st_nlink > 1 && !allow_hard_links) {
252
+ msg("%"TS" has multiple hard links -- skipping "
253
+ "(use -f to process anyway)", in->name);
254
+ return -2;
255
+ }
256
+
257
+ return 0;
258
+ }
259
+
260
+ static void
261
+ restore_mode(struct file_stream *out, const stat_t *stbuf)
262
+ {
263
+ #ifndef _WIN32
264
+ if (fchmod(out->fd, stbuf->st_mode) != 0)
265
+ msg_errno("%"TS": unable to preserve mode", out->name);
266
+ #endif
267
+ }
268
+
269
+ static void
270
+ restore_owner_and_group(struct file_stream *out, const stat_t *stbuf)
271
+ {
272
+ #ifndef _WIN32
273
+ if (fchown(out->fd, stbuf->st_uid, stbuf->st_gid) != 0) {
274
+ msg_errno("%"TS": unable to preserve owner and group",
275
+ out->name);
276
+ }
277
+ #endif
278
+ }
279
+
280
+ static void
281
+ restore_timestamps(struct file_stream *out, const tchar *newpath,
282
+ const stat_t *stbuf)
283
+ {
284
+ int ret;
285
+ #if defined(HAVE_FUTIMENS) && defined(HAVE_STAT_NANOSECOND_PRECISION)
286
+ struct timespec times[2] = {
287
+ stbuf->st_atim, stbuf->st_mtim,
288
+ };
289
+ ret = futimens(out->fd, times);
290
+ #elif defined(HAVE_FUTIMES) && defined(HAVE_STAT_NANOSECOND_PRECISION)
291
+ struct timeval times[2] = {
292
+ { stbuf->st_atim.tv_sec, stbuf->st_atim.tv_nsec / 1000, },
293
+ { stbuf->st_mtim.tv_sec, stbuf->st_mtim.tv_nsec / 1000, },
294
+ };
295
+ ret = futimes(out->fd, times);
296
+ #else
297
+ struct tutimbuf times = {
298
+ stbuf->st_atime, stbuf->st_mtime,
299
+ };
300
+ ret = tutime(newpath, &times);
301
+ #endif
302
+ if (ret != 0)
303
+ msg_errno("%"TS": unable to preserve timestamps", out->name);
304
+ }
305
+
306
+ static void
307
+ restore_metadata(struct file_stream *out, const tchar *newpath,
308
+ const stat_t *stbuf)
309
+ {
310
+ restore_mode(out, stbuf);
311
+ restore_owner_and_group(out, stbuf);
312
+ restore_timestamps(out, newpath, stbuf);
313
+ }
314
+
315
+ static int
316
+ decompress_file(struct libdeflate_decompressor *decompressor, const tchar *path,
317
+ const struct options *options)
318
+ {
319
+ tchar *oldpath = (tchar *)path;
320
+ tchar *newpath = NULL;
321
+ struct file_stream in;
322
+ struct file_stream out;
323
+ stat_t stbuf;
324
+ int ret;
325
+ int ret2;
326
+
327
+ if (path != NULL) {
328
+ const tchar *suffix = get_suffix(path, options->suffix);
329
+ if (suffix == NULL) {
330
+ /*
331
+ * Input file is unsuffixed. If the file doesn't exist,
332
+ * then try it suffixed. Otherwise, if we're not
333
+ * writing to stdout, skip the file with warning status.
334
+ * Otherwise, go ahead and try to open the file anyway
335
+ * (which will very likely fail).
336
+ */
337
+ if (tstat(path, &stbuf) != 0 && errno == ENOENT) {
338
+ oldpath = append_suffix(path, options->suffix);
339
+ if (oldpath == NULL)
340
+ return -1;
341
+ if (!options->to_stdout)
342
+ newpath = (tchar *)path;
343
+ } else if (!options->to_stdout) {
344
+ msg("\"%"TS"\" does not end with the %"TS" "
345
+ "suffix -- skipping",
346
+ path, options->suffix);
347
+ return -2;
348
+ }
349
+ } else if (!options->to_stdout) {
350
+ /*
351
+ * Input file is suffixed, and we're not writing to
352
+ * stdout. Strip the suffix to get the path to the
353
+ * output file.
354
+ */
355
+ newpath = xmalloc((suffix - oldpath + 1) *
356
+ sizeof(tchar));
357
+ if (newpath == NULL)
358
+ return -1;
359
+ tmemcpy(newpath, oldpath, suffix - oldpath);
360
+ newpath[suffix - oldpath] = '\0';
361
+ }
362
+ }
363
+
364
+ ret = xopen_for_read(oldpath, options->force || options->to_stdout,
365
+ &in);
366
+ if (ret != 0)
367
+ goto out_free_paths;
368
+
369
+ if (!options->force && isatty(in.fd)) {
370
+ msg("Refusing to read compressed data from terminal. "
371
+ "Use -f to override.\nFor help, use -h.");
372
+ ret = -1;
373
+ goto out_close_in;
374
+ }
375
+
376
+ ret = stat_file(&in, &stbuf, options->force || options->keep ||
377
+ oldpath == NULL || newpath == NULL);
378
+ if (ret != 0)
379
+ goto out_close_in;
380
+
381
+ ret = xopen_for_write(newpath, options->force, &out);
382
+ if (ret != 0)
383
+ goto out_close_in;
384
+
385
+ /* TODO: need a streaming-friendly solution */
386
+ ret = map_file_contents(&in, stbuf.st_size);
387
+ if (ret != 0)
388
+ goto out_close_out;
389
+
390
+ ret = do_decompress(decompressor, &in, &out);
391
+ if (ret != 0)
392
+ goto out_close_out;
393
+
394
+ if (oldpath != NULL && newpath != NULL)
395
+ restore_metadata(&out, newpath, &stbuf);
396
+ ret = 0;
397
+ out_close_out:
398
+ ret2 = xclose(&out);
399
+ if (ret == 0)
400
+ ret = ret2;
401
+ if (ret != 0 && newpath != NULL)
402
+ tunlink(newpath);
403
+ out_close_in:
404
+ xclose(&in);
405
+ if (ret == 0 && oldpath != NULL && newpath != NULL && !options->keep)
406
+ tunlink(oldpath);
407
+ out_free_paths:
408
+ if (newpath != path)
409
+ free(newpath);
410
+ if (oldpath != path)
411
+ free(oldpath);
412
+ return ret;
413
+ }
414
+
415
+ static int
416
+ compress_file(struct libdeflate_compressor *compressor, const tchar *path,
417
+ const struct options *options)
418
+ {
419
+ tchar *newpath = NULL;
420
+ struct file_stream in;
421
+ struct file_stream out;
422
+ stat_t stbuf;
423
+ int ret;
424
+ int ret2;
425
+
426
+ if (path != NULL && !options->to_stdout) {
427
+ if (!options->force && has_suffix(path, options->suffix)) {
428
+ msg("%"TS": already has %"TS" suffix -- skipping",
429
+ path, options->suffix);
430
+ return 0;
431
+ }
432
+ newpath = append_suffix(path, options->suffix);
433
+ if (newpath == NULL)
434
+ return -1;
435
+ }
436
+
437
+ ret = xopen_for_read(path, options->force || options->to_stdout, &in);
438
+ if (ret != 0)
439
+ goto out_free_newpath;
440
+
441
+ ret = stat_file(&in, &stbuf, options->force || options->keep ||
442
+ path == NULL || newpath == NULL);
443
+ if (ret != 0)
444
+ goto out_close_in;
445
+
446
+ ret = xopen_for_write(newpath, options->force, &out);
447
+ if (ret != 0)
448
+ goto out_close_in;
449
+
450
+ if (!options->force && isatty(out.fd)) {
451
+ msg("Refusing to write compressed data to terminal. "
452
+ "Use -f to override.\nFor help, use -h.");
453
+ ret = -1;
454
+ goto out_close_out;
455
+ }
456
+
457
+ /* TODO: need a streaming-friendly solution */
458
+ ret = map_file_contents(&in, stbuf.st_size);
459
+ if (ret != 0)
460
+ goto out_close_out;
461
+
462
+ ret = do_compress(compressor, &in, &out);
463
+ if (ret != 0)
464
+ goto out_close_out;
465
+
466
+ if (path != NULL && newpath != NULL)
467
+ restore_metadata(&out, newpath, &stbuf);
468
+ ret = 0;
469
+ out_close_out:
470
+ ret2 = xclose(&out);
471
+ if (ret == 0)
472
+ ret = ret2;
473
+ if (ret != 0 && newpath != NULL)
474
+ tunlink(newpath);
475
+ out_close_in:
476
+ xclose(&in);
477
+ if (ret == 0 && path != NULL && newpath != NULL && !options->keep)
478
+ tunlink(path);
479
+ out_free_newpath:
480
+ free(newpath);
481
+ return ret;
482
+ }
483
+
484
+ int
485
+ tmain(int argc, tchar *argv[])
486
+ {
487
+ tchar *default_file_list[] = { NULL };
488
+ struct options options;
489
+ int opt_char;
490
+ int i;
491
+ int ret;
492
+
493
+ program_invocation_name = get_filename(argv[0]);
494
+
495
+ options.to_stdout = false;
496
+ options.decompress = is_gunzip();
497
+ options.force = false;
498
+ options.keep = false;
499
+ options.compression_level = 6;
500
+ options.suffix = T(".gz");
501
+
502
+ while ((opt_char = tgetopt(argc, argv, optstring)) != -1) {
503
+ switch (opt_char) {
504
+ case '1':
505
+ case '2':
506
+ case '3':
507
+ case '4':
508
+ case '5':
509
+ case '6':
510
+ case '7':
511
+ case '8':
512
+ case '9':
513
+ options.compression_level =
514
+ parse_compression_level(opt_char, toptarg);
515
+ if (options.compression_level == 0)
516
+ return 1;
517
+ break;
518
+ case 'c':
519
+ options.to_stdout = true;
520
+ break;
521
+ case 'd':
522
+ options.decompress = true;
523
+ break;
524
+ case 'f':
525
+ options.force = true;
526
+ break;
527
+ case 'h':
528
+ show_usage(stdout);
529
+ return 0;
530
+ case 'k':
531
+ options.keep = true;
532
+ break;
533
+ case 'n':
534
+ /*
535
+ * -n means don't save or restore the original filename
536
+ * in the gzip header. Currently this implementation
537
+ * already behaves this way by default, so accept the
538
+ * option as a no-op.
539
+ */
540
+ break;
541
+ case 'S':
542
+ options.suffix = toptarg;
543
+ if (options.suffix[0] == T('\0')) {
544
+ msg("invalid suffix");
545
+ return 1;
546
+ }
547
+ break;
548
+ case 'V':
549
+ show_version();
550
+ return 0;
551
+ default:
552
+ show_usage(stderr);
553
+ return 1;
554
+ }
555
+ }
556
+
557
+ argv += toptind;
558
+ argc -= toptind;
559
+
560
+ if (argc == 0) {
561
+ argv = default_file_list;
562
+ argc = ARRAY_LEN(default_file_list);
563
+ } else {
564
+ for (i = 0; i < argc; i++)
565
+ if (argv[i][0] == '-' && argv[i][1] == '\0')
566
+ argv[i] = NULL;
567
+ }
568
+
569
+ ret = 0;
570
+ if (options.decompress) {
571
+ struct libdeflate_decompressor *d;
572
+
573
+ d = alloc_decompressor();
574
+ if (d == NULL)
575
+ return 1;
576
+
577
+ for (i = 0; i < argc; i++)
578
+ ret |= -decompress_file(d, argv[i], &options);
579
+
580
+ libdeflate_free_decompressor(d);
581
+ } else {
582
+ struct libdeflate_compressor *c;
583
+
584
+ c = alloc_compressor(options.compression_level);
585
+ if (c == NULL)
586
+ return 1;
587
+
588
+ for (i = 0; i < argc; i++)
589
+ ret |= -compress_file(c, argv[i], &options);
590
+
591
+ libdeflate_free_compressor(c);
592
+ }
593
+
594
+ /*
595
+ * If ret=0, there were no warnings or errors. Exit with status 0.
596
+ * If ret=2, there was at least one warning. Exit with status 2.
597
+ * Else, there was at least one error. Exit with status 1.
598
+ */
599
+ if (ret != 0 && ret != 2)
600
+ ret = 1;
601
+
602
+ return ret;
603
+ }