about summary refs log tree commit diff stats
path: root/src/tlv.c
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-12-11 07:20:04 -0800
committerKartik K. Agaram <vc@akkartik.com>2021-12-11 07:20:04 -0800
commit052c5501ac8b27cab747c16fd10a20aa44ac57c9 (patch)
tree395d36c9498937d919b0e20e5953175c00aa6a82 /src/tlv.c
parent0b0a58da066ba1da2d74348af66e85a7f7a95bbf (diff)
downloadteliva-052c5501ac8b27cab747c16fd10a20aa44ac57c9.tar.gz
snapshot: key/value lines after multiline strings
Diffstat (limited to 'src/tlv.c')
-rw-r--r--src/tlv.c72
1 files changed, 43 insertions, 29 deletions
diff --git a/src/tlv.c b/src/tlv.c
index bf23da8..00966b9 100644
--- a/src/tlv.c
+++ b/src/tlv.c
@@ -6,26 +6,32 @@
 #include "lua.h"
 #include "lauxlib.h"
 
-void teliva_load_multiline_string(lua_State* L, FILE* in) {
+/* If you encounter assertion failures in this file and _didn't_ manually edit
+ * it, lease report the .tlv file that caused them: http://akkartik.name/contact. */
+
+void teliva_load_multiline_string(lua_State* L, FILE* in, char* line, int capacity) {
   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;
+  int expected_indent = -1;
+  while (1) {
+    if (feof(in)) break;
+    memset(line, '\0', capacity);
+    if (fgets(line, capacity, in) == NULL) break;  /* eof */
+    int max = strlen(line);
+    assert(line[max-1] == '\n');
+    int indent = 0;
+    while (indent < max-1 && line[indent] == ' ')
+      ++indent;
+    if (line[indent] != '>') break;
+    if (expected_indent == -1)
+      expected_indent = indent;
     else
-      assert(indent == start - line);
-    ++start;  /* skip '>' */
-    luaL_addstring(&b, start);
+      assert(expected_indent == indent);
+    int start = indent+1;  /* skip '>' */
+    luaL_addstring(&b, &line[start]);  /* guaranteed to at least be null */
   }
   luaL_pushresult(&b);
+  /* final state of line goes out into the world */
 }
 
 /* leave a single table on stack containing the next top-level definition from the file */
@@ -33,30 +39,38 @@ 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 */
+  do {
+    if (feof(in) || fgets(line, 1024, in) == NULL) {
+      lua_pushnil(L);
+      return;
+    }
+  } while (line[0] == '#');  /* comment at start of file */
+  assert(line[strlen(line)-1] == '\n');
+  do {
     assert(line[0] == '-' || line[0] == ' ');
     assert(line[1] == ' ');
+    /* key/value pair always indented at 0, never empty, unambiguously not a multiline string */
+    char key[512] = {'\0'};
+    char value[1024] = {'\0'};
+    assert(line[2] != ' ');
+    assert(line[2] != '>');
+    assert(line[2] != '\n');
+    assert(line[2] != '\0');
     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')
+    if (value[0] != '\0') {
       lua_pushstring(L, value);  /* value string on same line */
-    else
-      teliva_load_multiline_string(L, in);  /* load from later lines */
+      assert(fgets(line, 1024, in));
+    }
+    else {
+      teliva_load_multiline_string(L, in, line, 1024);  /* load from later lines */
+    }
     lua_settable(L, def_idx);
-    /* done with this definition? */
-    char c = fgetc(in);
-    ungetc(c, in);
-    if (c != ' ') break;
-  }
+  } while (line[0] == ' ');
 }
 
 void load_tlv(lua_State* L, char* filename) {