about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-12-11 00:30:42 -0800
committerKartik K. Agaram <vc@akkartik.com>2021-12-11 00:43:26 -0800
commit0b0a58da066ba1da2d74348af66e85a7f7a95bbf (patch)
tree81c4b7bb3b7dca41d1b83118c7457f0f780be18c /src
parent469ad4e5469b5cda4c61d39cc042852467691a82 (diff)
downloadteliva-0b0a58da066ba1da2d74348af66e85a7f7a95bbf.tar.gz
snapshot: start reading a new format
I really wanted to avoid getting into defining or parsing new file
formats. However, using the entire power of Lua is not ideal, as
described earlier in Konrad Hinsen's bug. In addition to everything
else, it's a vector for arbitrary code execution when someone loads an
untrusted image.

I could use JSON, but it requires ugly string escaping. Seems cleaner to
just use YAML. But YAML is complex and needs its own dependencies. If
I'm going to do my own, might as well make the multi-line string format
really clear.

I can't yet write the new format.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile2
-rw-r--r--src/lua.c29
-rw-r--r--src/tlv.c73
-rw-r--r--src/x.tlv42
4 files changed, 119 insertions, 27 deletions
diff --git a/src/Makefile b/src/Makefile
index 65580bb..d9ef9b1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -26,7 +26,7 @@ LUA_A=	liblua.a
 CORE_O=	lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
 	lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o  \
 	lundump.o lvm.o lzio.o \
-	kilo.o
+	kilo.o tlv.o
 LIB_O=	lauxlib.o lbaselib.o menu.o ldblib.o liolib.o lmathlib.o \
 	loslib.o ltablib.o lstrlib.o loadlib.o linit.o
 
diff --git a/src/lua.c b/src/lua.c
index c245d24..eb16b51 100644
--- a/src/lua.c
+++ b/src/lua.c
@@ -168,25 +168,6 @@ static int docall (lua_State *L, int narg, int clear) {
 }
 
 
-/* pushes commandline args to the stack, then an array of all commandline args */
-static int getargs (lua_State *L, char **argv, int n) {
-  int narg;
-  int i;
-  int argc = 0;
-  while (argv[argc]) argc++;  /* count total number of arguments */
-  narg = argc - (n + 1);  /* number of arguments to the script */
-  luaL_checkstack(L, narg + 3, "too many arguments to script");
-  for (i=n+1; i < argc; i++)
-    lua_pushstring(L, argv[i]);
-  lua_createtable(L, narg, n + 1);
-  for (i=0; i < argc; i++) {
-    lua_pushstring(L, argv[i]);
-    lua_rawseti(L, -2, i - n);
-  }
-  return narg;
-}
-
-
 static int dofile (lua_State *L, const char *name) {
   int status = luaL_loadfile(L, name) || docall(L, 0, 1);
   return report_in_developer_mode(L, status);
@@ -326,17 +307,13 @@ int load_definitions(lua_State *L) {
 
 
 char *Image_name = NULL;
+void load_tlv (lua_State *L, char *filename);
 static int handle_image (lua_State *L, char **argv, int n) {
   int status;
-  int narg = getargs(L, argv, n);  /* collect arguments */
-  lua_setglobal(L, "arg");
+  /* TODO: pass args in */
   /* parse and load file contents (teliva_program array) */
   Image_name = argv[n];
-  status = luaL_loadfile(L, Image_name);
-  lua_insert(L, -(narg+1));
-  if (status != 0) return report(L, status);  /* can't recover within teliva */
-  status = docall(L, narg, 0);
-  if (status != 0) return report(L, status);  /* can't recover within teliva */
+  load_tlv(L, Image_name);
   status = load_definitions(L);
   if (status != 0) return 0;
   /* call main() */
diff --git a/src/tlv.c b/src/tlv.c
new file mode 100644
index 0000000..bf23da8
--- /dev/null
+++ b/src/tlv.c
@@ -0,0 +1,73 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+void teliva_load_multiline_string(lua_State* L, FILE* in) {
+  luaL_Buffer b;
+  luaL_buffinit(L, &b);
+  char line[1024] = {'\0'};
+  int indent = -1;
+  while (!feof(in)) {
+    char c = fgetc(in);
+    ungetc(c, in);
+    if (c != ' ') break;
+    assert(fgets(line, 1024, in));
+    assert(line[strlen(line)-1] == '\n');
+    char* start = strchr(line, '>');
+    if (indent == -1)
+      indent = start - line;
+    else
+      assert(indent == start - line);
+    ++start;  /* skip '>' */
+    luaL_addstring(&b, start);
+  }
+  luaL_pushresult(&b);
+}
+
+/* leave a single table on stack containing the next top-level definition from the file */
+void teliva_load_definition(lua_State* L, FILE* in) {
+  lua_newtable(L);
+  int def_idx = lua_gettop(L);
+  char line[1024] = {'\0'};
+  char key[512] = {'\0'};
+  char value[1024] = {'\0'};
+  while (!feof(in)) {
+    assert(fgets(line, 1024, in));
+    assert(line[strlen(line)-1] == '\n');
+    if (line[0] == '#') continue;  /* comment */
+    assert(line[0] == '-' || line[0] == ' ');
+    assert(line[1] == ' ');
+    memset(key, 0, 512);
+    memset(value, 0, 1024);
+    sscanf(line+2, "%s%s", key, value);
+    assert(key[strlen(key)-1] == ':');
+    key[strlen(key)-1] = '\0';
+    lua_pushstring(L, key);
+    if (value[0] != '\0')
+      lua_pushstring(L, value);  /* value string on same line */
+    else
+      teliva_load_multiline_string(L, in);  /* load from later lines */
+    lua_settable(L, def_idx);
+    /* done with this definition? */
+    char c = fgetc(in);
+    ungetc(c, in);
+    if (c != ' ') break;
+  }
+}
+
+void load_tlv(lua_State* L, char* filename) {
+  lua_newtable(L);
+  int history_array = lua_gettop(L);
+  FILE* in = fopen(filename, "r");
+  for (int i = 1; !feof(in); ++i) {
+    teliva_load_definition(L, in);
+    if (lua_isnil(L, -1)) break;
+    lua_rawseti(L, history_array, i);
+  }
+  fclose(in);
+  lua_setglobal(L, "teliva_program");
+}
diff --git a/src/x.tlv b/src/x.tlv
new file mode 100644
index 0000000..4204c72
--- /dev/null
+++ b/src/x.tlv
@@ -0,0 +1,42 @@
+- __teliva_timestamp: original
+  window:
+    >window = curses.stdscr()
+- __teliva_timestamp: original
+  n:
+    >n = 0
+- __teliva_timestamp: original
+  render:
+    >function render(window)
+    >  window:clear()
+    >  window:attron(curses.A_BOLD)
+    >  window:attron(curses.color_pair(6))
+    >  window:mvaddstr(10, 10, "     ")
+    >  window:mvaddstr(10, 11, n)
+    >  window:attroff(curses.color_pair(6))
+    >  window:attroff(curses.A_BOLD)
+    >  curses.refresh()
+    >end
+- __teliva_timestamp: original
+  menu:
+    >menu = {Enter="increment"}
+- __teliva_timestamp: original
+  update:
+    >function update(window)
+    >  local key = curses.getch()
+    >  if key == 10 then
+    >    n = n+1
+    >  end
+    >end
+- __teliva_timestamp: original
+  main:
+    >function main()
+    >  for i=1,7 do
+    >    curses.init_pair(i, 0, i)
+    >  end
+    >  curses.init_pair(255, 15, 1)  -- reserved for Teliva error messages
+    >
+    >  while true do
+    >    render(window)
+    >    update(window)
+    >  end
+    >end