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