diff --git a/Makefile b/Makefile index 749bb19..cfd391e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-g -std=c99 -Isrc/lib/lua -Iinclude +CFLAGS=-g -std=gnu99 -Isrc/lib/lua -Iinclude BUILD = bin/ SOURCE = src/c/ diff --git a/include/luares.h b/include/luares.h index a19e3b5..d75a063 100644 --- a/include/luares.h +++ b/include/luares.h @@ -5,6 +5,7 @@ extern char lua_component[]; extern char lua_computer[]; extern char lua_eepromDefault[]; extern char lua_eeprom[]; +extern char lua_filesystem[]; extern char lua_init[]; extern char lua_sandbox[]; extern char lua_textgpu[]; diff --git a/src/c/lnative.c b/src/c/lnative.c index ddd0d05..32df7be 100644 --- a/src/c/lnative.c +++ b/src/c/lnative.c @@ -1,19 +1,279 @@ +#define _XOPEN_SOURCE 500 + #include "lupi.h" #include #include #include +#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include static int l_sleep (lua_State *L) { - int t = lua_tonumber(L, 1); + unsigned int t = lua_tonumber(L, 1); usleep(t); return 0; } +// Filesystem methods +static int l_fs_exists (lua_State *L) { + const char* fname = lua_tostring(L, 1); + if( access( fname, F_OK ) != -1 ) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 1; +} + +static int l_fs_mkdir (lua_State *L) { + const char* fname = lua_tostring(L, 1); + if( mkdir( fname, 0755 ) != -1 ) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 1; +} + +static int l_fs_isdir (lua_State *L) { + const char* fname = lua_tostring(L, 1); + struct stat s; + int err = stat(fname, &s); + if(-1 == err) { + lua_pushboolean(L, 0); + } else { + if(S_ISDIR(s.st_mode)) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + } + return 1; +} + +static int l_fs_spaceUsed (lua_State *L) { + const char* fname = lua_tostring(L, 1); + struct statvfs s; + if( statvfs(fname, &s) != -1 ) { + lua_pushnumber(L, s.f_bsize * s.f_bfree); + } else { + lua_pushnumber(L, -1); + } + return 1; +} + +static int l_fs_open (lua_State *L) { + const char* fname = lua_tostring(L, 1); + const char* mode = lua_tostring(L, 2); + int m = 0; + if(mode[0] == 'r') m = O_RDONLY; + else if(mode[0] == 'w') m = O_WRONLY | O_CREAT /*| O_DIRECT*/; + else if(mode[0] == 'a') m = O_WRONLY | O_APPEND | O_CREAT /*| O_DIRECT*/; + else return 0; + int fd = open(fname, m, 644); + if(fd == -1) return 0; + + lua_pushnumber(L, fd); + return 1; +} + +static int l_fs_seek (lua_State *L) { + int fd = lua_tonumber(L, 1); + int whence = lua_tonumber(L, 2); + long offset = lua_tonumber(L, 3); + + int w = 0; + if(whence == 0) w = SEEK_CUR; + else if(whence == 1) w = SEEK_SET; + else if(whence == 2) w = SEEK_END; + else return 0; + int res = lseek(fd, w, offset); + lua_pushnumber(L, res); + return 1; +} + +static int l_fs_write (lua_State *L) { + int fd = lua_tonumber(L, 1); + size_t len = 0; + const char* data = lua_tolstring(L, 2, &len); + + //TODO: May not all data be written? + if(write(fd, data, len) == -1) { + lua_pushboolean(L, 0); + } else { + lua_pushboolean(L, 1); + } + return 1; +} + +static int l_fs_spaceTotal (lua_State *L) { + const char* fname = lua_tostring(L, 1); + struct statvfs s; + if( statvfs(fname, &s) != -1 ) { + lua_pushnumber(L, s.f_frsize * s.f_blocks); + } else { + lua_pushnumber(L, -1); + } + return 1; +} + +static int l_fs_rename (lua_State *L) { + const char* from = lua_tostring(L, 1); + const char* to = lua_tostring(L, 1); + if( rename( from, to ) != -1 ) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 1; +} + +static int l_fs_list (lua_State *L) { + const char* path = lua_tostring(L, 1); + + DIR *dir; + struct dirent *ent; + if ((dir = opendir(path)) != NULL) { + lua_newtable(L); + int n = 1; + while ((ent = readdir(dir)) != NULL) { //TODO: Check if it should be freed + lua_pushstring(L, ent->d_name); + lua_rawseti(L, -2, n++); + } + closedir(dir); + } else { + return 0; + } + return 1; +} + +static int l_fs_lastModified (lua_State *L) { + const char* path = lua_tostring(L, 1); + struct stat s; + if( stat(path, &s) != -1 ) { + lua_pushnumber(L, s.st_mtime); + } else { + return 0; //TODO: No error? + } + return 1; +} + +static int rm(const char *path, const struct stat *s, int flag, struct FTW *f) { + int status; + int (*rm_func)(const char *); + + switch(flag) { + default: rm_func = unlink; break; + case FTW_DP: rm_func = rmdir; + } + if(status = rm_func(path), status != 0) + perror(path); + return status; +} + +static int l_fs_remove (lua_State *L) { + const char* path = lua_tostring(L, 1); + + if( nftw( path, rm, FOPEN_MAX, FTW_DEPTH )) { + lua_pushboolean(L, 0); + } else { + lua_pushboolean(L, 1); + } + return 1; +} + +static int l_fs_close (lua_State *L) { + int fd = lua_tonumber(L, 1); + + if(close(fd) == -1) { + lua_pushboolean(L, 0); + } else { + lua_pushboolean(L, 1); + } + return 1; +} + +static int l_fs_size (lua_State *L) { + const char* path = lua_tostring(L, 1); + struct stat s; + if( stat(path, &s) != -1 ) { + lua_pushnumber(L, s.st_size); + } else { + return 0; //TODO: No error? + } + return 1; +} + +static int l_fs_read (lua_State *L) { + int fd = lua_tonumber(L, 1); + int count = lua_tonumber(L, 2); + void* buf = malloc(count); + size_t res = read(fd, buf, count); + if(res != -1) { + lua_pushlstring(L, buf, res); + free(buf); + return 1; + } + free(buf); + return 0; +} + +#ifndef CLOCK_TICK_RATE +#define CLOCK_TICK_RATE 1193180 +#endif + +//Filesystem end +static int l_beep (lua_State *L) { + int freq = lua_tonumber(L, 1); + int btime = lua_tonumber(L, 2); + int console_fd = -1; + + if((console_fd = open("/dev/console", O_WRONLY)) == -1) { + //fprintf(stderr, "Could not open /dev/console for writing.\n"); + printf("\a"); + return 0; + } + + if(ioctl(console_fd, KIOCSOUND, (int)(CLOCK_TICK_RATE/freq)) < 0) { + printf("\a"); + return 0; + } + + usleep(1000 * btime); + ioctl(console_fd, KIOCSOUND, 0); + close(console_fd); + return 0; +} + void luanative_start(lua_State *L) { lua_createtable (L, 0, 1); - pushctuple(L, "sleep", l_sleep); + pushctuple(L, "sleep", l_sleep); + + pushctuple(L, "fs_exists", l_fs_exists); + pushctuple(L, "fs_mkdir", l_fs_mkdir); + pushctuple(L, "fs_isdir", l_fs_isdir); + pushctuple(L, "fs_spaceUsed", l_fs_spaceUsed); + pushctuple(L, "fs_open", l_fs_open); + pushctuple(L, "fs_seek", l_fs_seek); + pushctuple(L, "fs_write", l_fs_write); + pushctuple(L, "fs_spaceTotal", l_fs_spaceTotal); + pushctuple(L, "fs_rename", l_fs_rename); + pushctuple(L, "fs_lastModified", l_fs_lastModified); + pushctuple(L, "fs_remove", l_fs_remove); + pushctuple(L, "fs_close", l_fs_close); + pushctuple(L, "fs_size", l_fs_size); + pushctuple(L, "fs_read", l_fs_read); + + pushctuple(L, "beep", l_beep); + lua_setglobal(L, "native"); } \ No newline at end of file diff --git a/src/c/modules.c b/src/c/modules.c index b7fa8db..c890d16 100644 --- a/src/c/modules.c +++ b/src/c/modules.c @@ -13,6 +13,7 @@ void setup_modules(lua_State *L) { pushstuple(L, "computer", lua_computer); pushstuple(L, "eeprom", lua_eeprom); pushstuple(L, "eepromDefault", lua_eepromDefault); + pushstuple(L, "filesystem", lua_filesystem); pushstuple(L, "sandbox", lua_sandbox); pushstuple(L, "textgpu", lua_textgpu); pushstuple(L, "color", lua_util_color); diff --git a/src/c/run.c b/src/c/run.c index 39867c3..0282967 100644 --- a/src/c/run.c +++ b/src/c/run.c @@ -5,6 +5,15 @@ #include #include + +#include +#include +#include +#include +#include +#include +#include + void run_init() { lua_State *L; L = luaL_newstate(); @@ -14,7 +23,8 @@ void run_init() { luanative_start (L); termutils_start (L); - int status = luaL_loadstring(L, lua_init); + //int status = luaL_loadstring(L, lua_init); + int status = luaL_loadbuffer(L, lua_init, strlen(lua_init), "=INIT"); if (status) { fprintf(stderr, "Couldn't load init: %s\n", lua_tostring(L, -1)); exit(1); diff --git a/src/lua/core/boot.lua b/src/lua/core/boot.lua index 28e4ff4..de326be 100644 --- a/src/lua/core/boot.lua +++ b/src/lua/core/boot.lua @@ -4,39 +4,41 @@ function boot.boot() local gpu = modules.component.api.proxy(modules.component.api.list("gpu", true)()) local w, h = gpu.getResolution() - local function bsod(err) + local function bsod(...) gpu.setBackground(0x0000FF) gpu.setForeground(0xFFFFFF) - gpu.fill(0, 0, w, h, " ") + gpu.fill(1, 1, w, h, " ") gpu.set(2, 2, "CRITICAL ERROR OCCURED") - gpu.set(2, 3, "Lua BIOS Has failed:") - gpu.set(2, 5, tostring(err)) - io.flush() - native.sleep(2000000) + gpu.set(2, 3, "Lua BIOS has failed:") + for n, v in pairs({...}) do + gpu.set(2, 4 + n, tostring(v)) + end + gpu.set(2, h-1, "SYSTEM WILL STOP") gpu.setForeground(0xFFFFFF) gpu.setBackground(0x000000) + + native.sleep(4000000) + os.exit(1) end - print("r= " .. tostring(w) .. " " .. tostring(h)) - gpu.fill(0, 0, w, h, " ") - gpu.set(10, 5, "HHHHHHHHHHHHH") - gpu.set(11, 11, "VVVVVVVVVVVVVVVVV", true) - print("LuPI L2 INIT") - + gpu.fill(1, 1, w, h, " ") + gpu.set(1, h - 2, "LuPI L2 INIT") local code = modules.component.api.invoke(modules.component.api.list("eeprom", true)(), "get") if not code then - print("No bootcode") - error("No bootcode") + bsod("No bootcode") end - local f, reason = load(code, "=BIOS", nil, modules.sandbox) + local f, reason = load(code, "=USERBIOS", nil, modules.sandbox) if not f then - print(reason) + bsod(reason) else - local e, reason = pcall(f) - if not e then - bsod(reason) - end - print("System quit, Panic") + xpcall(f, function(e) + local trace = {} + for s in string.gmatch(debug.traceback(e, 2), "[^\r\n]+") do + trace[#trace + 1] = s + end + bsod("System crashed", "Stack traceback:", table.unpack(trace)) + end) + bsod("System quit") end end diff --git a/src/lua/core/component.lua b/src/lua/core/component.lua index f48387d..bd7b91c 100644 --- a/src/lua/core/component.lua +++ b/src/lua/core/component.lua @@ -35,7 +35,7 @@ function api.register(address, ctype, proxy, doc) end end modules.computer.api.pushSignal("component_added", address, ctype) - return true + return address end function api.unregister(address) @@ -65,7 +65,7 @@ function api.invoke(address, method, ...) error("No such component") end if not components[address].rawproxy[method] then - error("No such method") + error("No such method: " .. tostring(components[address].type) .. "." .. tostring(method)) end return components[address].rawproxy[method](...) end diff --git a/src/lua/core/computer.lua b/src/lua/core/computer.lua index 8ddae3e..9510301 100644 --- a/src/lua/core/computer.lua +++ b/src/lua/core/computer.lua @@ -10,4 +10,10 @@ function api.pushSignal(...) --FIXME: ASAP: Implement end +function api.beep(freq, time) + if not freq then freq = 1000 end + if not time then time = 0.2 end + native.beep(freq, time * 1000) +end + return computer \ No newline at end of file diff --git a/src/lua/core/filesystem.lua b/src/lua/core/filesystem.lua new file mode 100644 index 0000000..9da216c --- /dev/null +++ b/src/lua/core/filesystem.lua @@ -0,0 +1,158 @@ +--Native side at lnative.c + +local filesystem = {} + +local function segments(path) + path = path:gsub("\\", "/") + repeat local n; path, n = path:gsub("//", "/") until n == 0 + local parts = {} + for part in path:gmatch("[^/]+") do + table.insert(parts, part) + end + local i = 1 + while i <= #parts do + if parts[i] == "." then + table.remove(parts, i) + elseif parts[i] == ".." then + table.remove(parts, i) + i = i - 1 + if i > 0 then + table.remove(parts, i) + else + i = 1 + end + else + i = i + 1 + end + end + return parts +end + +local function canonical(path) + local result = table.concat(segments(path), "/") + if string.sub(path, 1, 1) == "/" then + return "/" .. result + else + return result + end +end + +local function concat(pathA, pathB, ...) + checkArg(1, pathA, "string") + checkArg(2, pathB, "string", "nil") + local function _concat(n, a, b, ...) + if not b then + return a + end + checkArg(n, b, "string") + return _concat(n + 1, a .. "/" .. b, ...) + end + return canonical(_concat(2, pathA, pathB, ...)) +end + +function filesystem.register(basePath) + checkArg(1, basePath, "string") + + if not native.fs_exists(basePath) then + native.fs_mkdir(basePath) + end + if not native.fs_isdir(basePath) then + error("Filesystem root is not a directory!") + end + local function realpath(path) + checkArg(1, path, "string") + return concat(basePath, path) + end + + --TODO: checkArg all + local fs = {} + function fs.spaceUsed() + return native.fs_spaceUsed(basePath) + end + function fs.open(path, mode) + checkArg(1, path, "string") + checkArg(2, path, "string", "nil") + local m = "r" + if mode == "a" then m = "a" + elseif mode == "w" then m = "w" end + local fd = native.fs_open(realpath(path), m) + if not fd then + return nil, path + end + return fd + end + function fs.seek(handle, whence, offset) --TODO: Test + checkArg(1, handle, "number") + checkArg(2, whence, "string") + checkArg(3, offset, "number") + local w = 0 + if whence == "cur" then w = 0 + elseif whence == "set" then w = 1 + elseif whence == "end" then w = 2 + else error("Invalid whence") end + return native.fs_seek(handle, w, offset or 0) + end + function fs.makeDirectory(path) --TODO: check if creates parrent dirs + checkArg(1, path, "string") + return native.fs_mkdir(realpath(path)) + end + function fs.exists(path) + checkArg(1, path, "string") + return native.fs_exists(realpath(path)) + end + function fs.isReadOnly() + return false --TODO: Implement + end + function fs.write(handle, value) --TODO: check behaviour on invalid FDs + checkArg(1, handle, "number") + checkArg(2, value, "string") + return native.fs_write(handle, value) + end + function fs.spaceTotal() + return native.fs_spaceTotal(basePath) + end + function fs.isDirectory(path) + checkArg(1, path, "string") + return native.fs_isdir(realpath(path)) + end + function fs.rename(from, to) + checkArg(1, from, "string") + checkArg(2, to, "string") + return native.fs_rename(realpath(from), realpath(to)) + end + function fs.list(path) + checkArg(1, path, "string") + return native.fs_list(realpath(path)) --TODO: Test, check if dirs get / at end + end + function fs.lastModified(path) + checkArg(1, path, "string") + return native.fs_lastModified(realpath(path)) + end + function fs.getLabel() + return --TODO: Implement, use real labels + end + function fs.remove(path) --TODO: TEST!! + checkArg(1, path, "string") + return native.fs_remove(realpath(path)) + end + function fs.close(handle) + checkArg(1, handle, "number") + return native.fs_close() + end + function fs.size(path) + checkArg(1, path, "string") + return native.fs_size(realpath(path)) + end + function fs.read(handle, count) --FIXME: Hudgeread, fix in general + checkArg(1, handle, "number") + checkArg(2, count, "number") + return native.fs_read(handle, count) + end + function fs.setLabel(value) + checkArg(1, value, "number") + return value --TODO: Implement, use real labels + end + return modules.component.api.register(nil, "filesystem", fs) +end + +return filesystem \ No newline at end of file diff --git a/src/lua/core/init.lua b/src/lua/core/init.lua index 3a2bf42..24dfadd 100644 --- a/src/lua/core/init.lua +++ b/src/lua/core/init.lua @@ -18,6 +18,7 @@ end print("LuPI L1 INIT") modules = {} +deadhooks = {} local function loadModule(name) print("LuPI L1 INIT > Load module > " .. name) @@ -26,7 +27,7 @@ local function loadModule(name) if not moduleCode[name] then error("No code for module " .. tostring(name)) end - local code, reason = load(moduleCode[name]) + local code, reason = load(moduleCode[name], "=Module "..name) if not code then print("Failed loading module " .. name .. ": " .. reason) else @@ -34,28 +35,47 @@ local function loadModule(name) end end ---Load modules ---Utils -loadModule("random") -loadModule("color") +function main() + --Load modules + --Utils + loadModule("random") + loadModule("color") ---Core -loadModule("component") -loadModule("computer") + --Core + loadModule("component") + loadModule("computer") ---Components -loadModule("eeprom") -loadModule("textgpu") + --Components + loadModule("eeprom") + loadModule("textgpu") + loadModule("filesystem") ---Userspace -loadModule("sandbox") -loadModule("boot") + --Userspace + loadModule("sandbox") + loadModule("boot") ---Setup core modules -modules.component.prepare() -modules.computer.prepare() + --Setup core modules + modules.component.prepare() + modules.computer.prepare() -modules.eeprom.register() -modules.textgpu.start() + modules.eeprom.register() + modules.filesystem.register("root") + modules.textgpu.start() -modules.boot.boot() + modules.boot.boot() +end + +local state, cause = pcall(main) +if not state then + print("LuPI finished with following error:") + print(cause) +end + +print("Running shutdown hooks") +for k, hook in ipairs(deadhooks) do + local state, cause = pcall(hook) + if not state then + print("Shutdown hook with following error:") + print(cause) + end +end \ No newline at end of file diff --git a/src/lua/core/sandbox.lua b/src/lua/core/sandbox.lua index 360ac46..30377c8 100644 --- a/src/lua/core/sandbox.lua +++ b/src/lua/core/sandbox.lua @@ -160,7 +160,8 @@ sandbox = { len = utf8.len, offset = utf8.offset }, - checkArg = checkArg + checkArg = checkArg, + og = _G } sandbox._G = sandbox diff --git a/src/lua/core/textgpu.lua b/src/lua/core/textgpu.lua index 5c55386..ff44433 100644 --- a/src/lua/core/textgpu.lua +++ b/src/lua/core/textgpu.lua @@ -25,7 +25,8 @@ function textgpu.start() return --TODO: Maybe? end background = modules.color.nearest(color, mapping) - io.write("\x1b[4" .. background .. "m") + io.write("\x1b[4" .. math.floor(background) .. "m") + io.flush() end function gpu.setForeground(color, isPaletteIndex) checkArg(1, color, "number") @@ -33,8 +34,9 @@ function textgpu.start() if isPaletteIndex then return --TODO: Maybe? end - background = modules.color.nearest(color, mapping) - io.write("\x1b[3" .. background .. "m") + foreground = modules.color.nearest(color, mapping) + io.write("\x1b[3" .. math.floor(foreground) .. "m") + io.flush() end function gpu.getBackground() return mapping[background], false @@ -79,6 +81,8 @@ function textgpu.start() checkArg(2, y, "number") checkArg(3, value, "string") checkArg(4, vertical, "boolean", "nil") + x = math.floor(x) + y = math.floor(y) if not vertical then io.write("\x1b[" .. y .. ";" .. x .. "H" .. value) else @@ -87,6 +91,7 @@ function textgpu.start() io.write(c .. "\x1b[D\x1b[B") end) end + io.flush() return true end function gpu.copy(x, y, w, h, tx, ty) @@ -112,10 +117,15 @@ function textgpu.start() return true end + io.write("\x1b[?25l") --Disable cursor gpu.setForeground(0xFFFFFF) gpu.setBackground(0x000000) modules.component.api.register(nil, "gpu", gpu) + + deadhooks[#deadhooks + 1] = function() + io.write("\x1b[?25h") --Enable cursor on quit + end end return textgpu \ No newline at end of file