lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Okay, so I'm a bit wound and I'm not terribly good at writing emails to mailing lists, but here goes:

Some introduction:

Lua represents a file handle as userdata with a metatable containing things like :seek(), :lines(), :flush(), :setvbuf(), :write(), :read(), etc...  this table is referenced in the Lua registry *table* (people forget the registry is just a Lua table like any other).

When a function in the io 'library' receives a file handle it does type-checking on that parameter like by [probably] calling luaL_checkudata(lua_state, index, LUA_FILEHANDLE)[1];

LUA_FILEHANDLE is a preprocessor definition in lauxlib.h, which is just "FILE*"

So what this means is luaL_checkudata() gets the metatable of the userdata object (if it has one), and checks to see that it matches the table referenced by the key "FILE*" in the registry table.

Lua file handles themselves are called luaL_Stream's or just LStream's (there's a typedef in liolib.c[2]).

typedef struct luaL_Stream {
  FILE *f;  /* stream (NULL for incompletely created streams) */
  lua_CFunction closef;  /* to close stream (NULL for closed streams) */
} luaL_Stream;

^ found in lauxlib.h[3]

Lua file handles, in their C form are just ANSI C FILE *'s and a pointer to the function to close them (io_fclose() in liolib.c[4]).

To create a Lua file handle you would need to call newfile() in liolib.c[5], but this function is static and therefore not exposed.

----------------------------------------------------

Other stuff/thoughts/info:

There are many situations where it would be advantageous to be able to create file handles that could use both your own I/O functions and the standard io.read()/io.write().

One example is that I have created my own unfinished/shoddy binding[6] to BSD sockets because I could not get "low enough" with luasocket[7].  luasocket represents sockets as its own userdata type, not as a Lua file handle -- but I wanted the ability to call recv() on my socket type and also be able to io.write() to it (I managed to get this working! \o/).

luasec[8] wraps luasocket objects to provide encryption on network connections (basic gist -- I'm not a security person).  It calls a method (socket:getfd()) to retrieve the file descriptor from the luasocket C object and then returns a dummy object after it has "wrapped" the socket.  In this way, luasec doesn't have to know the implementation details of the socket object in luasocket.  I, however, think it would be great if luasec could assume it were a standard Lua file handle.

luaposix[9] is another well-known project with the ability to retrieve the file descriptor number from a Lua file handle (fileno()[10]).

----------------------------------------------------

What I want:

In Lua 5.3 I would love to have the ability to create and manipulate Lua file handles without creating my own copies of internal functions from liolib.c and lauxlib.h.  They are very simple objects (as shown above), and some projects would benefit from having an official way to create this type of userdata so that they become more interoperable with other third-party projects.  In my [unfinished] lsock project I have shown that by representing sockets as Lua file handles I can make use of my network bindings and use what is available in the io library (shameless promotion, maybe).

What I'll be doing:

A few projects have code for converting from Lua file handles to file descriptors and back, what I hope to do over the next few days is to collect this and my own code from lsock to create a library that will make it easy for third parties to retrieve the FD from a Lua file handle, or the HANDLE associated with the FD (on Windows) -- and then do this process in reverse.

It's pretty simple... to go from FILE * to fd on Windows or Linux you call _fileno()/fileno() on the luaL_Stream.f member.  To go back you _fdopen()/fdopen() with mode "r+b" (I believe).

To get a HANDLE for Windows file operations you call _get_osfhandle(fd), and you use _open_osfhandle(HANDLE) to go back.

My point is that this should be something available to everyone as minimal library we call all make use of to cut down on duplicated code and even stranger file handle types -- especially if it means gaining functionality by having an interoperable Lua file handle object.

fileno() is part of C89 and C99, but fdopen() is POSIX, _set/get_osfhandle() is obviously Windows-specific so none of this future minimal library will probably ever be accepted upstream.

Anyway, that is what I am thinking about.  No more :getfd() -- file descriptors should not be known from the Lua side of things (imo) -- and then everyone could benefit from the common Lua file handle.

Thoughts?

Sources:
1: http://www.lua.org/manual/5.2/manual.html#luaL_checkudata
2: http://www.lua.org/source/5.2/liolib.c.html
3: http://www.lua.org/source/5.2/lauxlib.h.html#luaL_Stream
4: http://www.lua.org/source/5.2/liolib.c.html#io_fclose
5: http://www.lua.org/source/5.2/liolib.c.html#newfile
6: https://github.com/Pogs/lsock
7: http://w3.impa.br/~diego/software/luasocket/
8: https://github.com/brunoos/luasec/wiki
9: https://github.com/luaposix/luaposix
10: https://github.com/luaposix/luaposix/blob/master/ext/posix/posix.c#L848

PS: I'm going to be so mad if there's a typo in here somewhere...