1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1999, 2002-2006, 2008, 2011, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or (at
8 // your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18
19 #include <common/mem.h>
20 #include <libaegis/output/filter/gzip.h>
21 #include <libaegis/sub.h>
22
23
24 #ifndef Z_BUFSIZE
25 #ifdef MAXSEG_64K
26 #define Z_BUFSIZE 4096 // minimize memory usage for 16-bit DOS
27 #else
28 #define Z_BUFSIZE 16384
29 #endif
30 #endif
31
32 static int gzip_magic[2] = {0x1f, 0x8b}; // gzip magic header
33
34
35 void
drop_dead(int err)36 output_filter_gzip::drop_dead(int err)
37 {
38 sub_context_ty sc;
39 sc.var_set_charstar("ERRNO", z_error(err));
40 sc.var_override("ERRNO");
41 sc.var_set_string("File_Name", output_filter::filename());
42 sc.fatal_intl(i18n("gzip $filename: $errno"));
43 }
44
45
46 //
47 // Outputs a long in LSB order to the given file
48 // (little endian)
49 //
50
51 void
output_long_le(unsigned long x)52 output_filter_gzip::output_long_le(unsigned long x)
53 {
54 for (int n = 0; n < 4; n++)
55 {
56 deeper_fputc((unsigned char)x);
57 x >>= 8;
58 }
59 }
60
61
~output_filter_gzip(void)62 output_filter_gzip::~output_filter_gzip(void)
63 {
64 //
65 // Make sure all buffered data has been passed to our write_inner
66 // method.
67 //
68 flush();
69
70 //
71 // finish sending the compressed stream
72 //
73 stream.avail_in = 0; // should be zero already anyway
74 if (stream.avail_out == 0)
75 {
76 deeper_write(outbuf, Z_BUFSIZE);
77 stream.next_out = outbuf;
78 stream.avail_out = Z_BUFSIZE;
79 }
80 for (;;)
81 {
82 int err = deflate(&stream, Z_FINISH);
83 if (err < 0)
84 drop_dead(err);
85 uInt len = Z_BUFSIZE - stream.avail_out;
86 if (!len)
87 break;
88 deeper_write(outbuf, len);
89 stream.next_out = outbuf;
90 stream.avail_out = Z_BUFSIZE;
91 }
92
93 //
94 // and the trailer
95 //
96 output_long_le(crc);
97 output_long_le(stream.total_in);
98
99 //
100 // Clean up any resources we were using.
101 //
102 if (stream.state != NULL)
103 deflateEnd(&stream);
104 delete [] outbuf;
105 outbuf = 0;
106 }
107
108
output_filter_gzip(const output::pointer & a_deeper)109 output_filter_gzip::output_filter_gzip(const output::pointer &a_deeper) :
110 output_filter(a_deeper),
111 outbuf(new Byte [Z_BUFSIZE]),
112 crc(0),
113 pos(0),
114 bol(true)
115 {
116 crc = crc32(0L, Z_NULL, 0);
117 stream.avail_in = 0;
118 stream.avail_out = 0;
119 stream.next_in = NULL;
120 stream.next_out = NULL;
121 stream.opaque = (voidpf)0;
122 stream.zalloc = (alloc_func)0;
123 stream.zfree = (free_func)0;
124
125 //
126 // Set the parameters for the compression.
127 // Note: windowBits is passed < 0 to suppress zlib header.
128 //
129 int err =
130 deflateInit2
131 (
132 &stream,
133 Z_BEST_COMPRESSION, // level
134 Z_DEFLATED, // method
135 -MAX_WBITS, // windowBits
136 DEF_MEM_LEVEL, // memLevel
137 Z_DEFAULT_STRATEGY // strategy
138 );
139 if (err != Z_OK)
140 drop_dead(err);
141
142 stream.next_out = outbuf;
143 stream.avail_out = Z_BUFSIZE;
144
145 //
146 // Write a very simple .gz header:
147 //
148 deeper_fputc(gzip_magic[0]);
149 deeper_fputc(gzip_magic[1]);
150 deeper_fputc(Z_DEFLATED);
151 deeper_fputc(0); // flags
152 output_long_le(0L); // time
153 deeper_fputc(0); // xflags
154 deeper_fputc(3); // always use unix OS_CODE
155 }
156
157
158 output::pointer
create(const output::pointer & a_deeper)159 output_filter_gzip::create(const output::pointer &a_deeper)
160 {
161 return pointer(new output_filter_gzip(a_deeper));
162 }
163
164
165 long
ftell_inner(void) const166 output_filter_gzip::ftell_inner(void)
167 const
168 {
169 return pos;
170 }
171
172
173 void
write_inner(const void * buf,size_t len)174 output_filter_gzip::write_inner(const void *buf, size_t len)
175 {
176 if (len > 0)
177 bol = (((const char *)buf)[len - 1] == '\n');
178 stream.next_in = (Bytef *)buf;
179 stream.avail_in = len;
180 while (stream.avail_in != 0)
181 {
182 if (stream.avail_out == 0)
183 {
184 deeper_write(outbuf, Z_BUFSIZE);
185 stream.next_out = outbuf;
186 stream.avail_out = Z_BUFSIZE;
187 }
188 int err = deflate(&stream, Z_NO_FLUSH);
189 if (err != Z_OK)
190 drop_dead(err);
191 }
192 crc = crc32(crc, (Bytef *)buf, len);
193 pos += len;
194 }
195
196
197 void
end_of_line_inner(void)198 output_filter_gzip::end_of_line_inner(void)
199 {
200 if (!bol)
201 write_inner("\n", 1);
202 }
203
204
205 nstring
type_name(void) const206 output_filter_gzip::type_name(void)
207 const
208 {
209 return ("gzip " + output_filter::type_name());
210 }
211
212
213 // vim: set ts=8 sw=4 et :
214