|
|
/*
* --- T2-COPYRIGHT-NOTE-BEGIN --- * This copyright note is auto-generated by ./scripts/Create-CopyPatch. * * T2 SDE: misc/lua/lzlib/lzlib.c * Copyright (C) 2004 - 2006 The T2 SDE Project * * More information can be found in the files COPYING and README. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. A copy of the * GNU General Public License can be found in the file COPYING. * --- T2-COPYRIGHT-NOTE-END --- */ // compilation: gcc --shared lzlib.c -o lzlib.so -lz
// contains some slightly addapted code pieces from lua's liolib.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <zlib.h>
#include <lua.h>
#include <lauxlib.h>
#define ZLIB_HANDLE "gzFile"
// ------------------- helper functions ------------------------
// pushes gzFile operation result string on lua stack
// first value is either true or nil (== operation failed),
// in the latter case a string containing the error message and
// the nummerical error codes (gzerror / errno) are returned
// as additional values
static int pushresult (lua_State *L, gzFile* zf, const char *filename) { int zlib_result; const char* zmessage; if ((zf) == NULL) zlib_result = Z_ERRNO; else zmessage = gzerror (*zf, &zlib_result);
if (zlib_result == Z_OK) { lua_pushboolean(L, 1); return 1; } else {
if (zlib_result == Z_ERRNO) // error is a file io error
zmessage = strerror(errno);
lua_pushnil(L);
if (filename) lua_pushfstring(L, "%s: %s", filename, zmessage); else lua_pushfstring(L, "%s", zmessage);
lua_pushinteger(L, zlib_result); lua_pushinteger(L, errno);
return 4; } }
// ensures first argument is an open zlib handle and returns it
static gzFile *tozfile (lua_State *L) { gzFile *zf = ((gzFile *) luaL_checkudata(L, 1, ZLIB_HANDLE));
if (*zf == NULL) luaL_error(L, "attempt to use a closed gz file"); return zf; }
// ----------------- poor man buffer in C ---------------------
char* content_buffer = NULL; size_t content_buffer_length = 0; size_t content_length = 0;
static inline void ResetBuffer () { content_length = 0; }
static inline void ExtendBufferBySize (size_t min_size) { if (min_size > content_buffer_length) { if (content_buffer_length == 0) content_buffer = malloc (min_size); else content_buffer = realloc (content_buffer, min_size); content_buffer_length = min_size; } }
static inline void ExtendBuffer () { ExtendBufferBySize ((content_buffer_length < 256) ? 256 : 2 * content_buffer_length); // for testing: ExtendBufferBySize ((content_buffer_length < 10) ? 10 : 2 + content_buffer_length);
}
static inline void AddToBuffer (char c) { if (content_buffer_length == content_length) ExtendBuffer (); content_buffer [content_length++] = c; }
static inline const char* FinishBuffer () { AddToBuffer ('\0'); return content_buffer; }
static inline int BufferFill () { return content_length; }
static inline int BufferFree () { return (content_buffer_length - content_length); }
// -------------------------- API ------------------------------
static int gz_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r");
gzFile* zf = (gzFile *) lua_newuserdata(L, sizeof(gzFile)); luaL_getmetatable(L, ZLIB_HANDLE); lua_setmetatable(L, -2);
*zf = gzopen(filename, mode);
return (*zf == NULL) ? pushresult(L, NULL, filename) : 1; }
static int gz_close (lua_State *L) { gzFile *zf = tozfile(L); int result = gzclose(*zf); *zf = NULL;
// need to emulate pushresult behavior, because gzerror
// does not work anymore after closing stream *grrrr*
if (result == Z_OK) { lua_pushboolean (L, 1); return 1; } else { const char* zmessage; if (result == Z_ERRNO) // error is a file io error
zmessage = strerror(errno); else zmessage = zError(result);
lua_pushnil(L); lua_pushfstring(L, zmessage); lua_pushinteger(L, result); lua_pushinteger(L, errno); return 4; } }
static int __gz_lines_iterator (lua_State *L) { gzFile zf = (gzFile) lua_topointer (L, lua_upvalueindex(1)); int ch = '\0'; char* ret;
if (content_buffer_length == 0) ExtendBuffer (); else ResetBuffer ();
#ifdef READ_LINE_ONE_BY_ONE
while ( (ch = gzgetc(zf)) != -1 && ch != '\n') { AddToBuffer ((char) ch); }
#else
do { ret = gzgets (zf, content_buffer + BufferFill (), BufferFree ()); if (ret == Z_NULL) break; int l = strlen (content_buffer); content_length = l; if (l > 0) { ch = content_buffer[l - 1]; if (ch != '\n') ExtendBuffer (); else content_buffer[l-1] = '\0'; } } while (ret && ch != '\n');
#endif
if (ch == '\n' || BufferFill () > 0) { if (ch == '\n') lua_pushstring (L, FinishBuffer ()); return 1; } else { return pushresult(L, &zf, NULL); } }
static int gz_lines (lua_State *L) { gzFile *zf = tozfile(L); lua_pushlightuserdata (L, *zf); lua_pushcclosure (L, __gz_lines_iterator, 1); return 1; }
static int gz_status (lua_State *L) { gzFile *zf = tozfile(L); return pushresult (L, zf, NULL); }
static int gz_eof (lua_State *L) { gzFile *zf = tozfile(L); int eof = gzeof (*zf); lua_pushboolean(L, eof); if (eof == 1) { const char* eof_reason; int eof_reason_code; eof_reason = gzerror (*zf, &eof_reason_code); lua_pushboolean (L, eof_reason_code == Z_STREAM_END); lua_pushstring (L, eof_reason); return 3; } return 0; }
// ------------------- init and registering --------------------
static const luaL_reg R[] = { {"open", gz_open}, {"close", gz_close}, {"lines", gz_lines}, {"status", gz_status}, {"eof", gz_eof}, {NULL, NULL} };
static const luaL_Reg zlib[] = { {"close", gz_close}, {"lines", gz_lines}, {"status", gz_status}, {"eof", gz_eof}, {"__gc", gz_close}, {NULL, NULL} };
static void createmeta (lua_State *L) { luaL_newmetatable(L, ZLIB_HANDLE); /* create metatable for zlib handles */ lua_pushvalue(L, -1); /* push metatable */ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ luaL_register(L, NULL, zlib); /* gz file methods */ }
LUALIB_API int luaopen_lzlib (lua_State *L) { createmeta(L); luaL_openlib(L, "lzlib", R, 0); lua_pushliteral(L,"version"); /** version */ lua_pushliteral(L,"pre-alpha"); lua_settable(L,-3); return 1; }
|