lua-users home
lua-l archive

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


Hi,

As many of you have most probably stumbled upon the problem with
fdopen from the luaposix package on luajit and some of you have
most probably solved it one way or another, I'd like to post my
solution to this problem just for the purpose of archiving it and
hopefully helping others.

But first a bit of a disclaimer: I do not consider myself an
experienced Lua programmer and perhaps there are better solutions
to this problem (-;

local stdio = require("posix.stdio")
local unistd = require("posix.unistd")

function mikes_fdopen(fd)
	local fh, rv

	fh = io.tmpfile()
	if (not fh) then
		return nil
	end
	fh:flush()
	rv = unistd.dup2(fd, stdio.fileno(fh))
	if (not rv) then
		fh:close()
		return nil
	end
	unistd.close(fd)
	return fh
end

What happens here is that we obtain a Lua userdata object that
includes a 'FILE *' from io.tmpfile() and then substitute the
underlying file descriptor (stdio.fileno(fh)) with the provided
one (returned from the call to posix.fcntl.open, posix.unistd.pipe,
etc.)  The actual temporary file is unlinked before tmpfile()
returns.  I've skipped the "mode" argument to the fdopen since
tmpfile() always opens a read-write stream and there's no real
way to update the mode on an already opened file descriptor.

It's important to let the VM create a wrapped 'FILE *' object
since luajit needs to create a GC reference for the object as
well as set the type of the object to UDTYPE_IO_FILE (something
PUC-Rio Lua doesn't need to do):

  static IOFileUD *io_file_new(lua_State *L)
  {
    IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
    GCudata *ud = udataV(L->top-1);
    ud->udtype = UDTYPE_IO_FILE;
    /* NOBARRIER: The GCudata is new (marked white). */
    setgcrefr(ud->metatable, curr_func(L)->c.env);
    iof->fp = NULL;
    iof->type = IOFILE_TYPE_FILE;
    return iof;
  }

I'm open to any public criticism or a public discussion
regarding this.  Let me also say sorry in advance if this has
already been presented before, that would just mean that my
google-fu wasn't strong enough.

Cheers,
Mike