libdeflate 0.1.0

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 (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
+ }