summaryrefslogtreecommitdiff
path: root/src/liolib.js
blob: f5813237222e86dfd829a35964488f7bb888e870 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
"use strict";

const fs      = require('fs');

const lua     = require('./lua.js');
const lauxlib = require('./lauxlib.js');

const IO_PREFIX = "_IO_";
const IOPREF_LEN = IO_PREFIX.length;
const IO_INPUT = lua.to_luastring(IO_PREFIX + "input");
const IO_OUTPUT = lua.to_luastring(IO_PREFIX + "output");

const tolstream = function(L) {
    return lauxlib.luaL_checkudata(L, 1, lauxlib.LUA_FILEHANDLE);
};

const isclosed = function(p) {
    return p.closef === null;
};

const f_tostring = function(L) {
    let p = tolstream(L);
    if (isclosed(p))
        lua.lua_pushliteral(L, "file (closed)");
    else
        lua.lua_pushstring(L, lua.to_luastring(`file (${p.f.toString()})`));
    return 1;
};

const newprefile = function(L) {
    let p = lua.lua_newuserdata(L);
    p.f = null;
    p.closef = null;
    lauxlib.luaL_setmetatable(L, lauxlib.LUA_FILEHANDLE);
    return p;
};

const getiofile = function(L, findex) {
    lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, findex);
    let p = lua.lua_touserdata(L, -1);
    if (isclosed(p))
        lauxlib.luaL_error(L, lua.to_luastring(`standard ${lua.to_jsstring(findex.slice(IOPREF_LEN))} file is closed`));
    return p.f;
};

const g_write = function(L, f, arg) {
    let nargs = lua.lua_gettop(L) - arg;
    let status = true;
    let err;
    for (; nargs--; arg++) {
        let s = lauxlib.luaL_checklstring(L, arg);
        try {
            status = status && (fs.writeSync(f.fd, Uint8Array.from(s)) === s.length);
        } catch (e) {
            status = false;
            err = e;
        }
    }
    if (status) return 1;  /* file handle already on stack top */
    else return lauxlib.luaL_fileresult(L, status, null, err);
};

const io_write = function(L) {
    return g_write(L, getiofile(L, IO_OUTPUT), 1);
};

const iolib = {
    "write": io_write
};

const flib = {
    "__tostring": f_tostring
};

const createmeta = function(L) {
    lauxlib.luaL_newmetatable(L, lauxlib.LUA_FILEHANDLE);  /* create metatable for file handles */
    lua.lua_pushvalue(L, -1);  /* push metatable */
    lua.lua_setfield(L, -2, lua.to_luastring("__index", true));  /* metatable.__index = metatable */
    lauxlib.luaL_setfuncs(L, flib, 0);  /* add file methods to new metatable */
    lua.lua_pop(L, 1);  /* pop new metatable */
};

const io_noclose = function(L) {
    let p = tolstream(L);
    p.closef = io_noclose;
    lua.lua_pushnil(L);
    lua.lua_pushliteral(L, "cannot close standard file");
    return 2;
};

const createstdfile = function(L, f, k, fname) {
    let p = newprefile(L);
    p.f = f;
    p.closef = io_noclose;
    if (k !== null) {
        lua.lua_pushvalue(L, -1);
        lua.lua_setfield(L, lua.LUA_REGISTRYINDEX, k);  /* add file to registry */
    }
    lua.lua_setfield(L, -2, fname);  /* add file to module */
};

const luaopen_io = function(L) {
    lauxlib.luaL_newlib(L, iolib);
    createmeta(L);
    /* create (and set) default files */
    createstdfile(L, process.stdin, IO_INPUT, lua.to_luastring("stdin"));
    createstdfile(L, process.stdout, IO_OUTPUT, lua.to_luastring("stdout"));
    createstdfile(L, process.stderr, null, lua.to_luastring("stderr"));
    return 1;
};

module.exports.luaopen_io = luaopen_io;