|
16 | 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
17 | 17 | */
|
18 | 18 |
|
19 |
| -#include <bzlib.h> |
20 |
| -#include <stdio.h> |
21 |
| -#include <stdlib.h> |
22 |
| -#include <string.h> |
23 | 19 | #include <lua.h>
|
24 | 20 | #include <lauxlib.h>
|
25 | 21 |
|
26 |
| -#include <assert.h> |
27 |
| - |
| 22 | +#include "lbz2_file_reader.h" |
28 | 23 | #include "lbz2_file_writer.h"
|
29 | 24 | #include "lbz2_stream.h"
|
30 | 25 |
|
31 |
| -#define LBZ_STATE_META "LuaBook.bz2" |
32 |
| - |
33 |
| -typedef struct { |
34 |
| - BZFILE *bz_stream; |
35 |
| - FILE *f; |
36 |
| - |
37 |
| - /* getline related stuff */ |
38 |
| - char *buf; |
39 |
| - size_t buf_size; /* max == LUAL_BUFFERSIZE */ |
40 |
| -} lbz_state; |
41 |
| - |
42 |
| -/* Forward declarations */ |
43 |
| -static lbz_state *lbz_check_state(lua_State *L, int index); |
44 |
| - |
45 |
| -static int lbz_read_open(lua_State *L); |
46 |
| -static int lbz_read(lua_State *L); |
47 |
| -static void lbz_perform_close(lbz_state *state, int keep_extra_buf); |
48 |
| -static int lbz_read_close(lua_State *L); |
49 |
| -static int lbz_getline(lua_State *L); |
50 |
| -static int lbz_getline_read(lua_State *L, luaL_Buffer *b, lbz_state *state, int keep_eol); |
51 |
| - |
52 |
| -static int lbz_lines(lua_State *L); |
53 |
| - |
54 |
| -static void lbz_buffer_init(lbz_state *state); |
55 |
| -static void lbz_buffer_free(lbz_state *state); |
56 |
| - |
57 |
| -static void lbz_buffer_append(lbz_state *state, const char *data, size_t data_len); |
58 |
| -static void lbz_buffer_drain(lbz_state *state, size_t amount); |
59 |
| -static void lbz_buffer_drain_all(lbz_state *state); |
60 |
| - |
61 |
| -static const struct luaL_reg bzlib_f [] = { |
62 |
| - {"open", lbz_read_open}, |
63 |
| - {NULL, NULL} /* Sentinel */ |
| 26 | +static luaL_Reg lbz2_global[] = { |
| 27 | + { NULL, NULL } |
64 | 28 | };
|
65 | 29 |
|
66 |
| -static const struct luaL_reg bzlib_m [] = { |
67 |
| - {"read", lbz_read}, |
68 |
| - {"getline", lbz_getline}, |
69 |
| - {"close", lbz_read_close}, |
70 |
| - {"lines", lbz_lines}, |
71 |
| - {NULL, NULL} /* Sentinel */ |
72 |
| -}; |
73 |
| - |
74 |
| -lbz_state *lbz_check_state(lua_State *L, int index) { |
75 |
| - return (lbz_state *)luaL_checkudata(L, index, LBZ_STATE_META); |
76 |
| -} |
77 |
| - |
78 |
| -/* Binding to libbzip2's BZ2_bzReadOpen method */ |
79 |
| -int lbz_read_open(lua_State *L) { |
80 |
| - size_t len; |
81 |
| - const char *fname = lua_tolstring(L, 1, &len); |
82 |
| - FILE *f = fopen(fname, "rb"); |
83 |
| - |
84 |
| - if (f == NULL) |
85 |
| - return luaL_error(L, "Failed to fopen %s", fname); |
86 |
| - |
87 |
| - int bzerror; |
88 |
| - lbz_state *state = (lbz_state *) lua_newuserdata(L, sizeof(lbz_state)); |
89 |
| - state->bz_stream = BZ2_bzReadOpen(&bzerror, f, 0, 0, NULL, 0); |
90 |
| - state->f = f; |
91 |
| - |
92 |
| - lbz_buffer_init(state); |
93 |
| - |
94 |
| - luaL_getmetatable(L, LBZ_STATE_META); |
95 |
| - lua_setmetatable(L, -2); |
96 |
| - |
97 |
| - if (bzerror != BZ_OK) |
98 |
| - lua_pushnil(L); |
99 |
| - |
100 |
| - return 1; |
101 |
| -} |
102 |
| - |
103 |
| -void lbz_buffer_init(lbz_state *state) { |
104 |
| - state->buf = malloc(LUAL_BUFFERSIZE); |
105 |
| - state->buf_size = 0; |
106 |
| -} |
107 |
| - |
108 |
| -void lbz_buffer_free(lbz_state *state) { |
109 |
| - if(!state->buf) return; |
110 |
| - state->buf_size = 0; |
111 |
| - free(state->buf); |
112 |
| - state->buf = NULL; |
113 |
| -} |
114 |
| - |
115 |
| -void lbz_buffer_append(lbz_state *state, const char *data, size_t data_size) { |
116 |
| - assert(state->buf_size + data_size < LUAL_BUFFERSIZE); |
117 |
| - memmove(state->buf + state->buf_size, data, data_size); |
118 |
| - state->buf_size += data_size; |
119 |
| -} |
120 |
| - |
121 |
| -void lbz_buffer_drain(lbz_state *state, size_t amount) { |
122 |
| - memmove(state->buf, state->buf + amount, state->buf_size - amount); |
123 |
| - state->buf_size -= amount; |
124 |
| -} |
125 |
| - |
126 |
| -void lbz_buffer_drain_all(lbz_state *state) { |
127 |
| - state->buf_size = 0; |
128 |
| -} |
129 |
| - |
130 |
| -/* Binding to libbzip2's BZ2_bzReadOpen method */ |
131 |
| -static int lbz_read(lua_State *L) { |
132 |
| - int bzerror = BZ_OK; |
133 |
| - int len; |
134 |
| - luaL_Buffer b; |
135 |
| - lbz_state *state = lbz_check_state(L, 1); |
136 |
| - len = luaL_checkint(L, 2); |
137 |
| - |
138 |
| - if (!state->bz_stream && !state->buf) { |
139 |
| - /* The logical end of file has been reached -- there's no more data to |
140 |
| - * return, and the user should call the read_close method. */ |
141 |
| - lua_pushnil(L); |
142 |
| - lua_pushstring(L, "CLOSED"); |
143 |
| - return 2; |
144 |
| - } |
145 |
| - luaL_buffinit(L, &b); |
146 |
| - |
147 |
| - /* In case this function is being used alongsize the getline method, we |
148 |
| - * should use the buffers that getline is using */ |
149 |
| - if (state->buf_size) { |
150 |
| - int used_len = (state->buf_size < len) ? state->buf_size : len; |
151 |
| - luaL_addlstring(&b, state->buf, used_len); |
152 |
| - lbz_buffer_drain(state, used_len); |
153 |
| - len -= used_len; |
154 |
| - } |
155 |
| - |
156 |
| - /* Pull in chunks until all data read */ |
157 |
| - while(len > 0) { |
158 |
| - char *buf = luaL_prepbuffer(&b); |
159 |
| - int nextRead = len > LUAL_BUFFERSIZE ? LUAL_BUFFERSIZE : len; |
160 |
| - int read = BZ2_bzRead(&bzerror, state->bz_stream, buf, nextRead); |
161 |
| - if (read > 0) { |
162 |
| - luaL_addsize(&b, read); |
163 |
| - len -= read; |
164 |
| - } |
165 |
| - if (bzerror != BZ_OK) |
166 |
| - goto handle_error; |
167 |
| - } |
168 |
| - luaL_pushresult(&b); |
169 |
| - return 1; |
170 |
| -handle_error: |
171 |
| - if(BZ_STREAM_END == bzerror) { |
172 |
| - /* Push the data read already and mark the stream done */ |
173 |
| - luaL_pushresult(&b); |
174 |
| - lbz_perform_close(state, 0); |
175 |
| - return 1; |
176 |
| - } else { |
177 |
| - lua_pushnil(L); |
178 |
| - lua_pushstring(L, BZ2_bzerror(state->bz_stream, &bzerror)); |
179 |
| - return 2; |
180 |
| - } |
181 |
| -} |
182 |
| - |
183 |
| -void lbz_perform_close(lbz_state *state, int keep_extra_buf) { |
184 |
| - int bzerror; |
185 |
| - if(!keep_extra_buf) |
186 |
| - lbz_buffer_free(state); |
187 |
| - if(!state->bz_stream) |
188 |
| - return; |
189 |
| - BZ2_bzReadClose(&bzerror, state->bz_stream); |
190 |
| - fclose(state->f); |
191 |
| - state->bz_stream = NULL; |
192 |
| - state->f = NULL; |
193 |
| -} |
194 |
| - |
195 |
| -/* Binding to libbzip2's BZ2_bzReadClose method */ |
196 |
| -static int lbz_read_close(lua_State *L) { |
197 |
| - lbz_state *state = lbz_check_state(L, 1); |
198 |
| - lbz_perform_close(state, 0); |
199 |
| - return 0; |
200 |
| -} |
201 |
| - |
202 |
| -/* |
203 |
| - * GETLINE STUFF |
204 |
| - * This code is considerably more complicated... if you know of a simpler way |
205 |
| - * to do it that doesn't sacrifice speed, please let me know. |
206 |
| - */ |
207 |
| - |
208 |
| -static int lbz_handle_eol(luaL_Buffer *b, char *buf, size_t buf_len, lbz_state *state, int in_buffer, int keep_eol) { |
209 |
| - char *eol = memchr(buf, '\n', buf_len); |
210 |
| - size_t chars_to_return; |
211 |
| - |
212 |
| - /* If a newline hasn't been found, keep iterating while building up |
213 |
| - * the buffer */ |
214 |
| - if(eol == NULL) { |
215 |
| - if(in_buffer) |
216 |
| - luaL_addsize(b, buf_len); |
217 |
| - else |
218 |
| - luaL_addlstring(b, buf, buf_len); |
219 |
| - return 0; |
220 |
| - } |
221 |
| - chars_to_return = eol - buf; |
222 |
| - eol++; |
223 |
| - if(keep_eol) |
224 |
| - chars_to_return++; |
225 |
| - if(in_buffer) |
226 |
| - luaL_addsize(b, chars_to_return); |
227 |
| - else |
228 |
| - luaL_addlstring(b, buf, chars_to_return); |
229 |
| - /* Save the remaining data end of data - position of beginning */ |
230 |
| - lbz_buffer_append(state, eol, buf_len - (eol - buf)); |
231 |
| - luaL_pushresult(b); |
232 |
| - return 1; |
233 |
| -} |
234 |
| -/* This is an auxilliary function that lbz_getline calls when it needs to |
235 |
| - * actually use the BZ2_bzRead method to read more data from the bzipped file. |
236 |
| - **/ |
237 |
| -static int lbz_getline_read(lua_State *L, luaL_Buffer *b, lbz_state *state, int keep_eol) { |
238 |
| - int bzerror; |
239 |
| - |
240 |
| - /* The entire 'extra_buf' buffer is needed */ |
241 |
| - luaL_addlstring(b, state->buf, state->buf_size); |
242 |
| - lbz_buffer_drain_all(state); |
243 |
| - |
244 |
| - if (!state->bz_stream) { /* No more data left at all - return data is 'success' */ |
245 |
| - lbz_perform_close(state, 0); // Completely close it out now |
246 |
| - luaL_pushresult(b); |
247 |
| - return 1; |
248 |
| - } |
249 |
| - while(1) { |
250 |
| - char *buf = luaL_prepbuffer(b); |
251 |
| - int len = BZ2_bzRead(&bzerror, state->bz_stream, buf, LUAL_BUFFERSIZE); |
252 |
| - |
253 |
| - if ((bzerror != BZ_OK) && (bzerror != BZ_STREAM_END)) { |
254 |
| - /* Error happened, data thrown */ |
255 |
| - lua_pushnil(L); |
256 |
| - lua_pushstring(L, BZ2_bzerror(state->bz_stream, &bzerror)); |
257 |
| - return 2; |
258 |
| - } |
259 |
| - if (!lbz_handle_eol(b, buf, len, state, 1, keep_eol)) |
260 |
| - continue; |
261 |
| - |
262 |
| - /* Kill the stream, keep the remaining buffer */ |
263 |
| - if (bzerror == BZ_STREAM_END) |
264 |
| - lbz_perform_close(state, state->buf_size ? 1 : 0); |
265 |
| - return 1; |
266 |
| - } |
267 |
| - return 0; |
268 |
| -} |
269 |
| - |
270 |
| -static int lbz_getline(lua_State *L) { |
271 |
| - lbz_state *state = lbz_check_state(L, 1); |
272 |
| - int skip_eol = lua_toboolean(L, 2); |
273 |
| - luaL_Buffer b; |
274 |
| - |
275 |
| - if (!state->bz_stream && !state->buf) { |
276 |
| - lua_pushnil(L); |
277 |
| - lua_pushstring(L, "CLOSED"); |
278 |
| - return 2; |
279 |
| - } |
280 |
| - |
281 |
| - luaL_buffinit(L, &b); |
282 |
| - if (state->buf_size) { |
283 |
| - size_t data_size = state->buf_size; |
284 |
| - lbz_buffer_drain_all(state); |
285 |
| - /* Drain entire buffer so that remaining data can be appropriately added */ |
286 |
| - if (!lbz_handle_eol(&b, state->buf, data_size, state, 0, !skip_eol)) |
287 |
| - return lbz_getline_read(L, &b, state, !skip_eol); |
288 |
| - return 1; |
289 |
| - } |
290 |
| - |
291 |
| - /* If there was no extra data from the last pass then we need to call |
292 |
| - * lbz_getline_read directly to get more data and find the newline. */ |
293 |
| - return lbz_getline_read(L, &b, state, !skip_eol); |
294 |
| -} |
295 |
| - |
296 |
| -static int lbz_line_iter(lua_State *L) { |
297 |
| - lua_settop(L, 0); |
298 |
| - lua_pushvalue(L, lua_upvalueindex(1)); |
299 |
| - lua_pushvalue(L, lua_upvalueindex(2)); |
300 |
| - return lbz_getline(L); |
301 |
| -} |
302 |
| - |
303 |
| -/* (bz):lines(keep_eol) */ |
304 |
| -int lbz_lines(lua_State *L) { |
305 |
| - int skip_eol = !lua_toboolean(L, 2); |
306 |
| - lua_pushvalue(L, 1); |
307 |
| - lua_pushboolean(L, skip_eol); |
308 |
| - lua_pushcclosure(L, lbz_line_iter, 2); |
309 |
| - return 1; |
310 |
| -} |
311 |
| - |
312 |
| -static int lbz_gc(lua_State *L) { |
313 |
| - lbz_read_close(L); |
314 |
| - return 0; |
315 |
| -} |
316 |
| - |
317 | 30 | int luaopen_bz2(lua_State *L) {
|
318 |
| - luaL_newmetatable(L, LBZ_STATE_META); |
319 |
| - lua_newtable(L); |
320 |
| - luaL_register(L, NULL, bzlib_m); |
321 |
| - lua_setfield(L, -2, "__index"); |
322 |
| - |
323 |
| - lua_pushcfunction(L, lbz_gc); |
324 |
| - lua_setfield(L, -2, "__gc"); |
325 |
| - lua_pop(L, 1); |
| 31 | + luaL_register(L, "bz2", lbz2_global); |
326 | 32 |
|
327 | 33 | lua_pushliteral(L, "bz2");
|
328 | 34 | lua_setfield(L, -2, "_NAME");
|
329 | 35 | lua_pushliteral(L, "0.1");
|
330 | 36 | lua_setfield(L, -2, "_VERSION");
|
331 |
| - luaL_register(L, "bz2", bzlib_f); |
332 | 37 |
|
| 38 | + register_lbz2_file_reader(L); |
333 | 39 | register_lbz2_file_writer(L);
|
334 | 40 | register_lbz2_stream(L);
|
335 | 41 |
|
|
0 commit comments