jbe@79: jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@81: #include jbe@79: jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: jbe@80: #define MOONBR_IO_MAXSTRERRORLEN 80 jbe@80: #define MOONBR_IO_WRITEBUFLEN 4096 jbe@80: jbe@80: #define moonbr_io_errmsg() \ jbe@80: char errmsg[MOONBR_IO_MAXSTRERRORLEN]; \ jbe@80: strerror_r(errno, errmsg, MOONBR_IO_MAXSTRERRORLEN) jbe@80: jbe@80: jbe@79: #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle" jbe@79: #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public" jbe@79: jbe@79: typedef struct { jbe@79: int fd; jbe@84: int issocket; jbe@81: int nonblocking; jbe@81: int writeerr; jbe@83: size_t writeleft; jbe@83: #if LUA_VERSION_NUM >= 503 jbe@83: lua_Integer writeqin; jbe@83: lua_Integer writeqout; jbe@83: #else jbe@83: int writeqin; jbe@83: int writeqout; jbe@83: #endif jbe@83: size_t writeqoff; jbe@81: int writebufcnt; jbe@80: char writebuf[MOONBR_IO_WRITEBUFLEN]; jbe@79: } moonbr_io_handle_t; jbe@79: jbe@81: static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) { jbe@81: if (handle->nonblocking != nonblocking) { jbe@81: int flags; jbe@81: flags = fcntl(handle->fd, F_GETFL, 0); jbe@81: if (flags == -1) { jbe@81: moonbr_io_errmsg(); jbe@81: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@81: } jbe@81: if (nonblocking) flags |= O_NONBLOCK; jbe@81: else flags &= ~O_NONBLOCK; jbe@81: if (fcntl(handle->fd, F_SETFL, flags) == -1) { jbe@81: moonbr_io_errmsg(); jbe@81: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@81: } jbe@81: } jbe@81: } jbe@81: jbe@81: static int moonbr_io_write_impl(lua_State *L, int nonblocking, int flush) { jbe@80: moonbr_io_handle_t *handle; jbe@80: int i, top; jbe@80: const char *str; jbe@81: size_t strlen, strpos; jbe@80: size_t written; jbe@80: ssize_t result; jbe@80: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@83: if (handle->fd < 0) luaL_error(L, "Attempt to write to a closed I/O handle"); jbe@81: if (handle->writeerr) { jbe@80: lua_pushnil(L); jbe@80: lua_pushliteral(L, "Previous write error"); jbe@80: return 2; jbe@80: } jbe@81: moonbr_io_handle_set_nonblocking(L, handle, nonblocking); jbe@84: top = lua_gettop(L); jbe@81: lua_getuservalue(L, 1); jbe@81: lua_getfield(L, -1, "writebuf"); jbe@84: for (i=2; i<=top; i++) { jbe@84: luaL_checklstring(L, i, &strlen); jbe@84: lua_pushvalue(L, i); jbe@84: lua_rawseti(L, -2, handle->writeqin++); jbe@84: handle->writeleft += strlen; jbe@81: } jbe@83: while (handle->writeqout != handle->writeqin) { jbe@83: lua_rawgeti(L, -1, handle->writeqout); jbe@81: str = lua_tolstring(L, -1, &strlen); jbe@81: strpos = handle->writeqoff; jbe@81: while (strpos < strlen) { jbe@81: if (strlen - strpos < MOONBR_IO_WRITEBUFLEN - handle->writebufcnt) { jbe@81: memcpy(handle->writebuf + handle->writebufcnt, str + strpos, strlen - strpos); jbe@81: handle->writebufcnt += strlen - strpos; jbe@80: break; jbe@80: } else { jbe@80: written = 0; jbe@81: memcpy(handle->writebuf + handle->writebufcnt, str + strpos, MOONBR_IO_WRITEBUFLEN - handle->writebufcnt); jbe@81: strpos += MOONBR_IO_WRITEBUFLEN - handle->writebufcnt; jbe@80: while (written < MOONBR_IO_WRITEBUFLEN) { jbe@80: result = write(handle->fd, handle->writebuf + written, MOONBR_IO_WRITEBUFLEN - written); jbe@80: if (result < 0) { jbe@81: if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { jbe@81: if (written) { jbe@81: handle->writebufcnt -= written; jbe@81: memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt); jbe@81: } jbe@81: handle->writeqoff = strpos; jbe@81: goto moonbr_io_write_impl_block; jbe@81: } else if (errno != EINTR) { jbe@80: moonbr_io_errmsg(); jbe@81: handle->writeerr = 1; jbe@80: lua_pushnil(L); jbe@80: lua_pushstring(L, errmsg); jbe@80: return 2; jbe@80: } jbe@80: } else { jbe@80: written += result; jbe@81: handle->writeleft -= result; jbe@80: } jbe@80: } jbe@81: handle->writebufcnt = 0; jbe@80: } jbe@80: } jbe@81: handle->writeqoff = 0; jbe@81: lua_pop(L, 1); jbe@81: lua_pushnil(L); jbe@83: lua_rawseti(L, -2, handle->writeqout++); jbe@80: } jbe@81: if (flush) { jbe@81: written = 0; jbe@81: while (written < handle->writebufcnt) { jbe@81: result = write(handle->fd, handle->writebuf + written, handle->writebufcnt - written); jbe@81: if (result < 0) { jbe@81: if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { jbe@81: if (written) { jbe@81: handle->writebufcnt -= written; jbe@81: memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt); jbe@81: } jbe@81: goto moonbr_io_write_impl_block; jbe@81: } else if (errno != EINTR) { jbe@81: moonbr_io_errmsg(); jbe@81: handle->writeerr = -1; jbe@81: lua_pushnil(L); jbe@81: lua_pushstring(L, errmsg); jbe@81: return 2; jbe@81: } jbe@81: } else { jbe@81: written += result; jbe@81: handle->writeleft -= result; jbe@81: } jbe@81: } jbe@81: handle->writebufcnt = 0; jbe@82: if (nonblocking) lua_pushinteger(L, 0); jbe@81: } else { jbe@82: if (nonblocking) lua_pushinteger(L, handle->writeleft - handle->writebufcnt); jbe@81: } jbe@82: if (!nonblocking) lua_pushvalue(L, 1); jbe@80: return 1; jbe@81: moonbr_io_write_impl_block: jbe@81: if (flush) lua_pushinteger(L, handle->writeleft); jbe@81: else lua_pushinteger(L, handle->writeleft - handle->writebufcnt); jbe@81: return 1; jbe@81: } jbe@81: jbe@81: static int moonbr_io_write(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 0, 0); jbe@81: } jbe@81: jbe@81: static int moonbr_io_write_nb(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 1, 0); jbe@80: } jbe@80: jbe@80: static int moonbr_io_flush(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 0, 1); jbe@81: } jbe@81: jbe@81: static int moonbr_io_flush_nb(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 1, 1); jbe@80: } jbe@80: jbe@84: static int moonbr_io_close_impl(lua_State *L, int clean) { jbe@83: moonbr_io_handle_t *handle; jbe@83: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@83: if (handle->fd < 0) luaL_error(L, "Attempt to close a closed I/O handle"); jbe@84: if (clean && handle->writeleft) { jbe@83: lua_pushcfunction(L, moonbr_io_flush); jbe@83: lua_pushvalue(L, 1); jbe@83: lua_call(L, 1, 2); jbe@83: if (!lua_toboolean(L, -2)) { jbe@83: close(handle->fd); jbe@83: handle->fd = -1; jbe@83: return 2; jbe@83: } jbe@83: } jbe@83: if (close(handle->fd)) { jbe@83: moonbr_io_errmsg(); jbe@83: handle->fd = -1; jbe@83: lua_pushnil(L); jbe@83: lua_pushstring(L, errmsg); jbe@83: return 2; jbe@83: } jbe@83: handle->fd = -1; jbe@83: lua_pushboolean(L, 1); jbe@83: return 1; jbe@84: jbe@83: } jbe@83: jbe@84: static int moonbr_io_reset(lua_State *L) { jbe@84: return moonbr_io_close_impl(L, 0); jbe@84: } jbe@84: jbe@84: static int moonbr_io_close(lua_State *L) { jbe@84: return moonbr_io_close_impl(L, 1); jbe@84: } jbe@84: jbe@84: void moonbr_io_pushhandle(lua_State *L, int fd, int issocket) { jbe@79: moonbr_io_handle_t *handle; jbe@79: handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t)); jbe@79: handle->fd = fd; jbe@84: handle->issocket = issocket; jbe@81: handle->nonblocking = -1; jbe@81: handle->writeerr = 0; jbe@81: handle->writeleft = 0; jbe@83: handle->writeqin = 0; jbe@83: handle->writeqout = 0; jbe@81: handle->writeqoff = 0; jbe@81: handle->writebufcnt = 0; jbe@79: luaL_getmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: lua_setmetatable(L, -2); jbe@79: lua_newtable(L); // uservalue jbe@81: lua_newtable(L); jbe@81: lua_setfield(L, -2, "writebuf"); jbe@79: lua_newtable(L); // public jbe@79: luaL_getmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY); jbe@79: lua_setmetatable(L, -2); jbe@79: lua_setfield(L, -2, "public"); jbe@79: lua_setuservalue(L, -2); jbe@79: } jbe@79: jbe@79: static int moonbr_io_handleindex(lua_State *L) { jbe@80: luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: lua_getuservalue(L, 1); jbe@79: lua_getfield(L, -1, "public"); jbe@79: lua_pushvalue(L, 2); jbe@79: lua_gettable(L, -2); jbe@79: return 1; jbe@79: } jbe@79: jbe@79: static int moonbr_io_handlenewindex(lua_State *L) { jbe@80: luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: lua_getuservalue(L, 1); jbe@79: lua_getfield(L, -1, "public"); jbe@79: lua_pushvalue(L, 2); jbe@79: lua_pushvalue(L, 3); jbe@79: lua_settable(L, -3); jbe@79: return 0; jbe@79: } jbe@79: jbe@80: static int moonbr_io_handlegc(lua_State *L) { jbe@80: moonbr_io_handle_t *handle; jbe@80: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@80: if (handle->fd >= 0) { jbe@80: lua_getuservalue(L, 1); jbe@80: lua_getfield(L, -1, "gc"); jbe@80: lua_pushvalue(L, 1); jbe@80: lua_call(L, 1, 0); jbe@80: } jbe@80: return 0; jbe@80: } jbe@80: jbe@79: static int moonbr_io_getdummy(lua_State *L) { jbe@80: moonbr_io_pushhandle(L, 2, 0); jbe@79: return 1; jbe@79: } jbe@79: jbe@79: static const struct luaL_Reg moonbr_io_handle_methods[] = { jbe@84: {"reset", moonbr_io_reset}, jbe@80: {"close", moonbr_io_close}, jbe@80: {"write", moonbr_io_write}, jbe@81: {"write_nb", moonbr_io_write_nb}, jbe@80: {"flush", moonbr_io_flush}, jbe@81: {"flush_nb", moonbr_io_flush_nb}, jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@79: static const struct luaL_Reg moonbr_io_handle_metamethods[] = { jbe@79: {"__index", moonbr_io_handleindex}, jbe@79: {"__newindex", moonbr_io_handlenewindex}, jbe@80: {"__gc", moonbr_io_handlegc}, jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@79: static const struct luaL_Reg moonbr_io_module_funcs[] = { jbe@79: {"getdummy", moonbr_io_getdummy}, jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@79: int luaopen_moonbridge_io(lua_State *L) { jbe@79: jbe@80: lua_newtable(L); // module jbe@80: jbe@79: lua_newtable(L); // public metatable jbe@79: lua_newtable(L); // handle methods jbe@79: luaL_setfuncs(L, moonbr_io_handle_methods, 0); jbe@80: lua_pushvalue(L, -1); jbe@80: lua_setfield(L, -4, "handle"); jbe@79: lua_setfield(L, -2, "__index"); jbe@79: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY); jbe@79: jbe@79: lua_newtable(L); // handle metatable jbe@79: luaL_setfuncs(L, moonbr_io_handle_metamethods, 0); jbe@79: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: jbe@79: luaL_setfuncs(L, moonbr_io_module_funcs, 0); jbe@79: return 1; jbe@79: jbe@79: } jbe@79: