Sfoglia il codice sorgente

Adapt code to ISO C11. Buggy.

scossu 17 ore fa
parent
commit
828c297075

+ 1 - 1
Makefile

@@ -36,7 +36,7 @@ MASSIF_DUMP := $(TMPDIR)/volksdata_massif.out
 
 INCLUDE_BASE := . -Iinclude -Iext/hashmap -Iext/log/src
 INCLUDE := -I$(INCLUDE_BASE)
-_CFLAGS = -std=gnu11 -Wall -Wextra -fPIC $(INCLUDE)
+_CFLAGS = -std=c11 -Wall -Wextra -fPIC $(INCLUDE)
 CFLAGS = $(if $(DEBUG),\
 		 $(_CFLAGS) -Itest -O0 -ggdb -DDEBUG,\
 		 $(_CFLAGS) -O3 -g0)

+ 17 - 20
README.md

@@ -37,10 +37,6 @@ Test coverage is not sufficient. Documentation is fairly extensive but needs
 reformatting. This code is being integrated in higher-level projects and is
 being improved as issues arise.
 
-Portability is still under assessment. The goal is to make this code POSIX
-compatible. ANSI C compatibility is out of reach because of the LMDB
-dependency.
-
 This is my first stab at writing a C library (coming from Python) and an
 unpaid fun project, so don't be surprised if you find some gross stuff.
 
@@ -88,51 +84,52 @@ workable set of features as a standalone library:
 
 ## Building
 
-### Requirements
+### Required dependencies
 
-- It is recommended to build and run Volksdata on a Linux system. No other
-  OS has been tested so far.
-- A C compiler. This has been only tested with `gcc` so far.
+- A C compiler and standard library. This has been only tested with GCC and
+  GNU libc so far. Testing with musl libc is planned.
 - [LMDB](https://symas.com/lmdb/) libraries and headers.
 - [XXHash](https://github.com/Cyan4973/xxHash) >=0.8 libraries and headers.
 
 ### Optional dependencies
 - [re2c](https://re2c.org/) to build the RDF language lexers. Only required if
-  the codecs are changed. Otherwise, compiled lexers are included in this git
+  the lexers are changed. Otherwise, compiled lexers are included in this git
   repo.
 - [cinclude2dot](https://www.flourish.org/cinclude2dot) and
   [Graphviz](https://graphviz.org/) for generating visual dependency graph.
 
+### Conformance
+
+Volksdata is written in ISO C11. Note that LMDB uses by default POSIX-1.2011
+extensions, and may have to be compiled separately in non-POSIX systems.
 
 ### `make` commands
 
 The default `make` command (`make lib`) compiles the library. Enter `make help`
 to get an overview of the other available commands.
 
-`make install` and installs libraries and headers in the
-directories set by the environment variable `$PREFIX`. If this is unset, the
-default `/usr/local` prefix is used.
+`make install` installs libraries and headers in the directories set by the
+environment variable `$PREFIX`. If this is unset, the default `/usr/local`
+prefix is used.
 
-If `LOCAL` is set to anything else than `0`, the library will be installed in
-`$LOCAL_PREFIX` instead (by default,`$HOME/.local`.
+If `LOCAL` is set to `1`, the library will be installed in `$LOCAL_PREFIX`
+instead (by default,`$HOME/.local`).
 
-If `DEBUG` is set to anything else than `0`, the library will be compiled with
-debug symbols.
+If `DEBUG` is set to `1`, the library will be compiled with debug symbols.
 
 E.g.
 
 ```
+make DEBUG=1
 make install DEBUG=1 LOCAL=1
 ```
 
-Installs the library with debug symbols in `~/.local`.
+Builds and installs the library with debug symbols in `~/.local`.
 
+`make test` and similar always force `DEBUG=1`.
 
 ### Compile-Time defines (`-D[...]`)
 
-`DEBUG`: Set debug mode: memory map is at reduced size, logging is forced to
-TRACE level, etc.
-
 `VOLK_RDF_STREAM_CHUNK_SIZE`: Size of RDF decoding buffer, i.e., maximum size
 of a chunk of RDF data fed to the parser when decoding a RDF file into a graph.
 This should be larger than the maximum expected size of a single term in your

+ 1 - 1
include/volksdata/codec.h

@@ -257,7 +257,7 @@ typedef VOLK_rc (*term_decode_fn_t)(const char *rep, VOLK_Term **term);
  *  on parsing error.
  */
 typedef VOLK_rc (*gr_decode_fn_t)(
-        FILE *rep, VOLK_Graph **gr, size_t *ct, char **err);
+        FILE *fh, const char *sh, VOLK_Graph **gr, size_t *ct, char **err);
 
 
 /** @brief Codec structure.

+ 2 - 1
include/volksdata/codec/parser_nt.h

@@ -33,6 +33,7 @@ VOLK_nt_parse_term (const char *rep, VOLK_Term **term);
  *  encountered. On error, `err` will contain the error message.
  */
 VOLK_rc
-VOLK_nt_parse_doc (FILE *stream, VOLK_Graph **gr, size_t *ct, char **err);
+VOLK_nt_parse_doc (
+        FILE *fh, const char *sh, VOLK_Graph **gr, size_t *ct, char **err);
 
 #endif

+ 2 - 1
include/volksdata/codec/parser_ttl.h

@@ -20,6 +20,7 @@
  *  encountered. On error, `err` will contain the error message.
  */
 VOLK_rc
-VOLK_ttl_parse_doc (FILE *stream, VOLK_Graph **gr, size_t *ct, char **err);
+VOLK_ttl_parse_doc (
+        FILE *fh, const char *sh, VOLK_Graph **gr, size_t *ct, char **err);
 
 #endif

+ 21 - 0
include/volksdata/core.h

@@ -382,6 +382,27 @@ VOLK_strerror (VOLK_rc rc);
 */
 
 
+/** @brief Replacement for GNU strndup.
+ *
+ * param[in] src String to duplicate.
+ * param[in] max Max number of characters to duplicate. The length is capped
+ *  to the smaller value between this and the source string length (characters
+ *  up to the trailing `\0`).
+ *
+ * return Duplicated string. The caller is in charge of freeing it after use.
+ */
+char *strndup (const char *src, size_t max);
+
+
+/** @brief Replacement for GNU strdup.
+ *
+ * param[in] str String to duplicate.
+ *
+ * return Duplicated string. The caller is in charge of freeing it after use.
+ */
+char *strdup (const char *src);
+
+
 /** @brief Make recursive directories.
  *
  * Modified from

+ 17 - 20
src/codec/Makefile

@@ -10,30 +10,27 @@ BUILDDIR = $(BASEDIR)/build
 
 CODEC_SRC = $(wildcard codec_*.c)
 PARSER_SRC = $(CODEC_SRC:codec_%=parser_%)
-ifneq ($(DEBUG),)
-CODEC_OBJ := $(CODEC_SRC:%.c=$(BUILDDIR)/%_dbg.o)
-else
-CODEC_OBJ := $(CODEC_SRC:%.c=$(BUILDDIR)/%.o)
-endif
-PARSER_OBJ := $(subst codec,parser,$(CODEC_OBJ))
-GRAMMAR_OBJ := $(subst codec,grammar,$(CODEC_OBJ))
+CODEC_OBJ = $(if $(DEBUG),\
+		$(CODEC_SRC:%.c=$(BUILDDIR)/%_dbg.o),\
+		$(CODEC_SRC:%.c=$(BUILDDIR)/%.o))
+
+PARSER_OBJ = $(subst codec,parser,$(CODEC_OBJ))
+GRAMMAR_OBJ = $(subst codec,grammar,$(CODEC_OBJ))
 OBJ = $(GRAMMAR_OBJ) $(PARSER_OBJ) $(CODEC_OBJ)
 
 INCLUDE := -I$(INCLUDE_DIR) -I$(BASEDIR)/ext/tpl/src -I$(BASEDIR)/ext/hashmap \
 	-I$(BASEDIR)/ext/log/src
-_CFLAGS := -std=gnu11 -Wall -fPIC $(INCLUDE)
-
-ifneq ($(DEBUG),)
-CFLAGS = $(_CFLAGS) -I$(BASEDIR)/test -O0 -g3 -DDEBUG
-else
-CFLAGS = $(_CFLAGS) -O3 -g0
-endif
-
-$(info CODEC_OBJ: $(CODEC_OBJ))
-$(info GRAMMAR_OBJ: $(GRAMMAR_OBJ))
-$(info PARSER_OBJ: $(PARSER_OBJ))
-$(info OBJ: $(OBJ))
-$(info CFLAGS: $(CFLAGS))
+_CFLAGS := -std=c11 -Wall -fPIC $(INCLUDE)
+
+CFLAGS = $(if $(DEBUG),\
+		$(_CFLAGS) -I$(BASEDIR)/test -O0 -g3 -DDEBUG,\
+		$(_CFLAGS) -O3 -g0 -DNDEBUG)
+
+#$(info CODEC_OBJ: $(CODEC_OBJ))
+#$(info GRAMMAR_OBJ: $(GRAMMAR_OBJ))
+#$(info PARSER_OBJ: $(PARSER_OBJ))
+#$(info OBJ: $(OBJ))
+#$(info CFLAGS: $(CFLAGS))
 
 .DEFAULT_GOAL := codec
 

+ 36 - 23
src/codec/lexer_nt.re

@@ -10,19 +10,20 @@
 
 
 typedef struct {
-    FILE *          fh;                 // Input file handle.
-    YYCTYPE         buf[CHUNK_SIZE],    // Start of buffer.
-            *       lim,                // Position after the last available
-                                        //   input character (YYLIMIT).
-            *       cur,                // Next input character to be read
-                                        //   (YYCURSOR)
-            *       mar,                // Most recent match (YYMARKER)
-            *       tok,                // Start of current token.
-            *       bol;                // Address of the beginning of the
-                                        //   current line (for debugging).
-    unsigned        line;               // Current line no. (for debugging).
-    unsigned        ct;                 // Number of parsed triples.
-    bool            eof;                // if we have reached EOF.
+    FILE           *fh;                 ///< Input file handle.
+    const char     *sh;                 ///< Input string. Exclusive with fh.
+    YYCTYPE         buf[CHUNK_SIZE],    ///< Start of buffer.
+                   *lim,                ///< Position after the last available
+                                        ///<   input character (YYLIMIT).
+                   *cur,                ///< Next input character to be read
+                                        ///<   (YYCURSOR)
+                   *mar,                ///< Most recent match (YYMARKER)
+                   *tok,                ///< Start of current token.
+                   *bol;                ///< Address of the beginning of the
+                                        ///<   current line (for debugging).
+    unsigned        line;               ///< Current line no. (for debugging).
+    unsigned        ct;                 ///< Number of parsed triples.
+    bool            eof;                ///< if we have reached EOF.
     /*!stags:re2c format = "YYCTYPE *@@;"; */
 } ParseIterator;
 
@@ -37,12 +38,14 @@ static int fill(ParseIterator *it)
         return 2;
     }
     LOG_DEBUG("Shifting bytes: %lu", shift);
-    memmove(it->buf, it->tok, it->lim - it->tok);
+    memmove (it->buf, it->tok, it->lim - it->tok);
     it->lim -= shift;
     it->cur -= shift;
     it->mar -= shift;
     it->tok -= shift;
-    it->lim += fread(it->lim, 1, shift, it->fh);
+    if (it->fh) it->lim += fread (it->lim, 1, shift, it->fh);
+    // With a string handle, assume the whole input fits in CHUNK_SIZE.
+    else it->lim = memcpy (it->lim, it->sh, sizeof(it->buf));
     /*!stags:re2c format = "if (it->@@) it->@@ -= shift; "; */
     it->lim[0] = 0;
     it->eof |= it->lim < it->buf + CHUNK_SIZE - 1;
@@ -50,9 +53,19 @@ static int fill(ParseIterator *it)
 }
 
 
-static void parse_init(ParseIterator *it, FILE *fh)
+/** @brief Initialize parser.
+ *
+ * @param[in] it iterator handle to be initialized.
+ *
+ * @param[in] fh Open file handle to read from. This is exclusive with sh. If
+ *  both fh and sh are provided, fh has precedence.
+ *
+ * @param[in] sh String to read from. This is exclusive with fh.
+ */
+static void parse_init(ParseIterator *it, FILE *fh, const char *sh)
 {
     it->fh = fh;
+    it->sh = sh;
     it->cur = it->mar = it->tok = it->lim = it->buf + CHUNK_SIZE - 1;
     it->line = 1;
     it->bol = it->buf;
@@ -76,6 +89,9 @@ void NTParseTrace();
 static int lex (ParseIterator *it, VOLK_Term **term)
 {
     const YYCTYPE *lit_data_e, *dtype_s, *lang_s;
+    //(void) lit_data_e;
+    //(void) dtype_s;
+    //(void) lang_s;
 
 loop:
 
@@ -230,15 +246,11 @@ loop:
 VOLK_rc
 VOLK_nt_parse_term (const char *rep, VOLK_Term **term)
 {
-    FILE *fh = fmemopen ((void *)rep, strlen (rep), "r");
-
     ParseIterator it;
-    parse_init (&it, fh);
+    parse_init (&it, NULL, rep);
 
     int ttype = lex (&it, term);
 
-    fclose (fh);
-
     switch (ttype) {
         case T_IRIREF:
         case T_LITERAL:
@@ -250,13 +262,14 @@ VOLK_nt_parse_term (const char *rep, VOLK_Term **term)
 }
 
 VOLK_rc
-VOLK_nt_parse_doc (FILE *fh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
+VOLK_nt_parse_doc (
+        FILE *fh, const char *sh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
 {
     *err_p = NULL;
     *gr_p = NULL;
 
     ParseIterator parse_it;
-    parse_init (&parse_it, fh);
+    parse_init (&parse_it, fh, sh);
 
 #ifdef DEBUG
     NTParseTrace (stdout, "NT Parser > ");

+ 22 - 17
src/codec/lexer_ttl.re

@@ -17,19 +17,20 @@
 
 
 typedef struct {
-    FILE *          fh;                 // Input file handle.
-    YYCTYPE         buf[CHUNK_SIZE],    // Start of buffer.
-            *       lim,                // Position after the last available
-                                        //   input character (YYLIMIT).
-            *       cur,                // Next input character to be read
-                                        //   (YYCURSOR)
-            *       mar,                // Most recent match (YYMARKER)
-            *       tok,                // Start of current token.
-            *       bol;                // Address of the beginning of the
-                                        //   current line (for debugging).
-    unsigned        line;               // Current line no. (for debugging).
-    unsigned        stmt;               // Current statement.
-    bool            eof;                // if we have reached EOF.
+    FILE          * fh;                 ///< Input file handle.
+    const char    * sh;                 ///< Input string. Exclusive with fh.
+    YYCTYPE         buf[CHUNK_SIZE],    ///< Start of buffer.
+                  * lim,                ///< Position after the last available
+                                        ///<   input character (YYLIMIT).
+                  * cur,                ///< Next input character to be read
+                                        ///<   (YYCURSOR)
+                  * mar,                ///< Most recent match (YYMARKER)
+                  * tok,                ///< Start of current token.
+                  * bol;                ///< Address of the beginning of the
+                                        ///<   current line (for debugging).
+    unsigned        line;               ///< Current line no. (for debugging).
+    unsigned        stmt;               ///< Current statement.
+    bool            eof;                ///< if we have reached EOF.
     /*!stags:re2c format = "YYCTYPE *@@;"; */
 } ParseIterator;
 
@@ -54,7 +55,9 @@ static int fill (ParseIterator *it)
     it->cur -= shift;
     it->mar -= shift;
     it->tok -= shift;
-    it->lim += fread(it->lim, 1, shift, it->fh);
+    if (it->fh) it->lim += fread (it->lim, 1, shift, it->fh);
+    // With a string handle, assume the whole input fits in CHUNK_SIZE.
+    else it->lim = memcpy (it->lim, it->sh, sizeof(it->buf));
     /*!stags:re2c format = "if (it->@@) it->@@ -= shift; "; */
     it->lim[0] = 0;
     it->eof |= it->lim < it->buf + CHUNK_SIZE - 1;
@@ -62,9 +65,10 @@ static int fill (ParseIterator *it)
 }
 
 
-static void parse_init (ParseIterator *it, FILE *fh)
+static void parse_init (ParseIterator *it, FILE *fh, const char *sh)
 {
     it->fh = fh;
+    it->sh = sh;
     it->cur = it->mar = it->tok = it->lim = it->buf + CHUNK_SIZE - 1;
     it->line = 1;
     it->stmt = 1;
@@ -359,7 +363,8 @@ lchar:
 
 
 VOLK_rc
-VOLK_ttl_parse_doc (FILE *fh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
+VOLK_ttl_parse_doc (
+        FILE *fh, const char *sh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
 {
     *err_p = NULL;
     *gr_p = NULL;
@@ -373,7 +378,7 @@ VOLK_ttl_parse_doc (FILE *fh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
     state->rc = VOLK_NORESULT;
 
     ParseIterator parse_it;
-    parse_init (&parse_it, fh);
+    parse_init (&parse_it, fh, sh);
 
     void *parser = TTLParseAlloc (malloc);
 

+ 63 - 50
src/codec/parser_nt.c

@@ -1,4 +1,4 @@
-/* Generated by re2c 4.1 on Sun Aug 17 18:56:10 2025 */
+/* Generated by re2c 4.1 on Wed Aug 20 17:28:48 2025 */
 #line 1 "lexer_nt.re"
 #include "volksdata/codec/parser_nt.h"
 #include "volksdata/codec/tokens_nt.h"
@@ -12,23 +12,24 @@
 
 
 typedef struct {
-    FILE *          fh;                 // Input file handle.
-    YYCTYPE         buf[CHUNK_SIZE],    // Start of buffer.
-            *       lim,                // Position after the last available
-                                        //   input character (YYLIMIT).
-            *       cur,                // Next input character to be read
-                                        //   (YYCURSOR)
-            *       mar,                // Most recent match (YYMARKER)
-            *       tok,                // Start of current token.
-            *       bol;                // Address of the beginning of the
-                                        //   current line (for debugging).
-    unsigned        line;               // Current line no. (for debugging).
-    unsigned        ct;                 // Number of parsed triples.
-    bool            eof;                // if we have reached EOF.
+    FILE           *fh;                 ///< Input file handle.
+    const char     *sh;                 ///< Input string. Exclusive with fh.
+    YYCTYPE         buf[CHUNK_SIZE],    ///< Start of buffer.
+                   *lim,                ///< Position after the last available
+                                        ///<   input character (YYLIMIT).
+                   *cur,                ///< Next input character to be read
+                                        ///<   (YYCURSOR)
+                   *mar,                ///< Most recent match (YYMARKER)
+                   *tok,                ///< Start of current token.
+                   *bol;                ///< Address of the beginning of the
+                                        ///<   current line (for debugging).
+    unsigned        line;               ///< Current line no. (for debugging).
+    unsigned        ct;                 ///< Number of parsed triples.
+    bool            eof;                ///< if we have reached EOF.
     
-#line 30 "parser_nt.c"
+#line 31 "parser_nt.c"
 YYCTYPE *yyt1;YYCTYPE *yyt2;YYCTYPE *yyt3;
-#line 26 "lexer_nt.re"
+#line 27 "lexer_nt.re"
 
 } ParseIterator;
 
@@ -43,16 +44,18 @@ static int fill(ParseIterator *it)
         return 2;
     }
     LOG_DEBUG("Shifting bytes: %lu", shift);
-    memmove(it->buf, it->tok, it->lim - it->tok);
+    memmove (it->buf, it->tok, it->lim - it->tok);
     it->lim -= shift;
     it->cur -= shift;
     it->mar -= shift;
     it->tok -= shift;
-    it->lim += fread(it->lim, 1, shift, it->fh);
+    if (it->fh) it->lim += fread (it->lim, 1, shift, it->fh);
+    // With a string handle, assume the whole input fits in CHUNK_SIZE.
+    else it->lim = memcpy (it->lim, it->sh, sizeof(it->buf));
     
-#line 54 "parser_nt.c"
+#line 57 "parser_nt.c"
 if (it->yyt1) it->yyt1 -= shift; if (it->yyt2) it->yyt2 -= shift; if (it->yyt3) it->yyt3 -= shift; 
-#line 46 "lexer_nt.re"
+#line 49 "lexer_nt.re"
 
     it->lim[0] = 0;
     it->eof |= it->lim < it->buf + CHUNK_SIZE - 1;
@@ -60,18 +63,28 @@ if (it->yyt1) it->yyt1 -= shift; if (it->yyt2) it->yyt2 -= shift; if (it->yyt3)
 }
 
 
-static void parse_init(ParseIterator *it, FILE *fh)
+/** @brief Initialize parser.
+ *
+ * @param[in] it iterator handle to be initialized.
+ *
+ * @param[in] fh Open file handle to read from. This is exclusive with sh. If
+ *  both fh and sh are provided, fh has precedence.
+ *
+ * @param[in] sh String to read from. This is exclusive with fh.
+ */
+static void parse_init(ParseIterator *it, FILE *fh, const char *sh)
 {
     it->fh = fh;
+    it->sh = sh;
     it->cur = it->mar = it->tok = it->lim = it->buf + CHUNK_SIZE - 1;
     it->line = 1;
     it->bol = it->buf;
     it->ct = 0;
     it->eof = 0;
     
-#line 73 "parser_nt.c"
+#line 86 "parser_nt.c"
 it->yyt1 = NULL; it->yyt2 = NULL; it->yyt3 = NULL; 
-#line 61 "lexer_nt.re"
+#line 74 "lexer_nt.re"
 
     fill (it);
 }
@@ -90,6 +103,9 @@ void NTParseTrace();
 static int lex (ParseIterator *it, VOLK_Term **term)
 {
     const YYCTYPE *lit_data_e, *dtype_s, *lang_s;
+    //(void) lit_data_e;
+    //(void) dtype_s;
+    //(void) lang_s;
 
 loop:
 
@@ -98,7 +114,7 @@ loop:
     *term = NULL;
 
     
-#line 102 "parser_nt.c"
+#line 118 "parser_nt.c"
 {
 	YYCTYPE yych;
 	unsigned int yyaccept = 0;
@@ -124,7 +140,7 @@ yyFillLabel0:
 yy1:
 	++YYCURSOR;
 yy2:
-#line 218 "lexer_nt.re"
+#line 234 "lexer_nt.re"
 	{
         LOG_DEBUG(
             "Invalid token @ %lu: %s (\\x%x)",
@@ -132,7 +148,7 @@ yy2:
 
         return -1;
     }
-#line 136 "parser_nt.c"
+#line 152 "parser_nt.c"
 yy3:
 	++YYCURSOR;
 yyFillLabel1:
@@ -147,13 +163,13 @@ yyFillLabel1:
 			goto yy4;
 	}
 yy4:
-#line 201 "lexer_nt.re"
+#line 217 "lexer_nt.re"
 	{
         LOG_DEBUG("Separator.");
 
         return T_WS;
     }
-#line 157 "parser_nt.c"
+#line 173 "parser_nt.c"
 yy5:
 	++YYCURSOR;
 yyFillLabel2:
@@ -169,14 +185,14 @@ yyFillLabel2:
 			goto yy6;
 	}
 yy6:
-#line 117 "lexer_nt.re"
+#line 133 "lexer_nt.re"
 	{
         it->line ++;
         it->bol = YYCURSOR;
         LOG_DEBUG("New line: #%u.", it->line);
         return T_EOL;
     }
-#line 180 "parser_nt.c"
+#line 196 "parser_nt.c"
 yy7:
 	yyaccept = 0;
 	YYMARKER = ++YYCURSOR;
@@ -218,7 +234,7 @@ yyFillLabel4:
 		default: goto yy9;
 	}
 yy9:
-#line 207 "lexer_nt.re"
+#line 223 "lexer_nt.re"
 	{
         size_t size = YYCURSOR - it->tok + 1;
         YYCTYPE *data = malloc (size);
@@ -229,17 +245,17 @@ yy9:
 
         goto loop;
     }
-#line 233 "parser_nt.c"
+#line 249 "parser_nt.c"
 yy10:
 	++YYCURSOR;
-#line 194 "lexer_nt.re"
+#line 210 "lexer_nt.re"
 	{
         LOG_DEBUG("End of triple.");
         it->ct ++;
 
         return T_DOT;
     }
-#line 243 "parser_nt.c"
+#line 259 "parser_nt.c"
 yy11:
 	yyaccept = 0;
 	YYMARKER = ++YYCURSOR;
@@ -348,7 +364,7 @@ yy17:
 	lit_data_e = it->yyt1;
 	dtype_s = it->yyt2;
 	lang_s = it->yyt3;
-#line 141 "lexer_nt.re"
+#line 157 "lexer_nt.re"
 	{
         // Only unescape Unicode from data.
         size_t size = lit_data_e - it->tok - 2;
@@ -389,7 +405,7 @@ yy17:
         if (!UNLIKELY (term)) return -1;
         return T_LITERAL;
     }
-#line 393 "parser_nt.c"
+#line 409 "parser_nt.c"
 yy18:
 	++YYCURSOR;
 yyFillLabel9:
@@ -585,7 +601,7 @@ yy32:
 	}
 yy33:
 	++YYCURSOR;
-#line 129 "lexer_nt.re"
+#line 145 "lexer_nt.re"
 	{
         YYCTYPE *data = unescape_unicode (it->tok + 1, YYCURSOR - it->tok - 2);
 
@@ -597,7 +613,7 @@ yy33:
         if (!UNLIKELY (term)) return -1;
         return T_IRIREF;
     }
-#line 601 "parser_nt.c"
+#line 617 "parser_nt.c"
 yy34:
 	++YYCURSOR;
 yyFillLabel23:
@@ -840,7 +856,7 @@ yy50:
 			goto yy51;
 	}
 yy51:
-#line 182 "lexer_nt.re"
+#line 198 "lexer_nt.re"
 	{
         YYCTYPE *data = unescape_unicode (it->tok + 2, YYCURSOR - it->tok - 2);
 
@@ -852,7 +868,7 @@ yy51:
         if (!UNLIKELY (term)) return -1;
         return T_BNODE;
     }
-#line 856 "parser_nt.c"
+#line 872 "parser_nt.c"
 yy52:
 	++YYCURSOR;
 yyFillLabel39:
@@ -1654,14 +1670,14 @@ yyFillLabel95:
 			goto yy15;
 	}
 yy110:
-#line 124 "lexer_nt.re"
+#line 140 "lexer_nt.re"
 	{
         LOG_DEBUG("End of buffer.");
         return T_EOF;
     }
-#line 1663 "parser_nt.c"
+#line 1679 "parser_nt.c"
 }
-#line 226 "lexer_nt.re"
+#line 242 "lexer_nt.re"
 
 }
 
@@ -1669,15 +1685,11 @@ yy110:
 VOLK_rc
 VOLK_nt_parse_term (const char *rep, VOLK_Term **term)
 {
-    FILE *fh = fmemopen ((void *)rep, strlen (rep), "r");
-
     ParseIterator it;
-    parse_init (&it, fh);
+    parse_init (&it, NULL, rep);
 
     int ttype = lex (&it, term);
 
-    fclose (fh);
-
     switch (ttype) {
         case T_IRIREF:
         case T_LITERAL:
@@ -1689,13 +1701,14 @@ VOLK_nt_parse_term (const char *rep, VOLK_Term **term)
 }
 
 VOLK_rc
-VOLK_nt_parse_doc (FILE *fh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
+VOLK_nt_parse_doc (
+        FILE *fh, const char *sh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
 {
     *err_p = NULL;
     *gr_p = NULL;
 
     ParseIterator parse_it;
-    parse_init (&parse_it, fh);
+    parse_init (&parse_it, fh, sh);
 
 #ifdef DEBUG
     NTParseTrace (stdout, "NT Parser > ");

+ 104 - 99
src/codec/parser_ttl.c

@@ -1,4 +1,4 @@
-/* Generated by re2c 4.1 on Sun Aug 17 18:56:10 2025 */
+/* Generated by re2c 4.1 on Wed Aug 20 17:28:48 2025 */
 #line 1 "lexer_ttl.re"
 #include "volksdata/codec/parser_ttl.h"
 #include "volksdata/codec/tokens_ttl.h"
@@ -19,23 +19,24 @@
 
 
 typedef struct {
-    FILE *          fh;                 // Input file handle.
-    YYCTYPE         buf[CHUNK_SIZE],    // Start of buffer.
-            *       lim,                // Position after the last available
-                                        //   input character (YYLIMIT).
-            *       cur,                // Next input character to be read
-                                        //   (YYCURSOR)
-            *       mar,                // Most recent match (YYMARKER)
-            *       tok,                // Start of current token.
-            *       bol;                // Address of the beginning of the
-                                        //   current line (for debugging).
-    unsigned        line;               // Current line no. (for debugging).
-    unsigned        stmt;               // Current statement.
-    bool            eof;                // if we have reached EOF.
+    FILE          * fh;                 ///< Input file handle.
+    const char    * sh;                 ///< Input string. Exclusive with fh.
+    YYCTYPE         buf[CHUNK_SIZE],    ///< Start of buffer.
+                  * lim,                ///< Position after the last available
+                                        ///<   input character (YYLIMIT).
+                  * cur,                ///< Next input character to be read
+                                        ///<   (YYCURSOR)
+                  * mar,                ///< Most recent match (YYMARKER)
+                  * tok,                ///< Start of current token.
+                  * bol;                ///< Address of the beginning of the
+                                        ///<   current line (for debugging).
+    unsigned        line;               ///< Current line no. (for debugging).
+    unsigned        stmt;               ///< Current statement.
+    bool            eof;                ///< if we have reached EOF.
     
-#line 37 "parser_ttl.c"
+#line 38 "parser_ttl.c"
 YYCTYPE *yyt1;
-#line 33 "lexer_ttl.re"
+#line 34 "lexer_ttl.re"
 
 } ParseIterator;
 
@@ -60,11 +61,13 @@ static int fill (ParseIterator *it)
     it->cur -= shift;
     it->mar -= shift;
     it->tok -= shift;
-    it->lim += fread(it->lim, 1, shift, it->fh);
+    if (it->fh) it->lim += fread (it->lim, 1, shift, it->fh);
+    // With a string handle, assume the whole input fits in CHUNK_SIZE.
+    else it->lim = memcpy (it->lim, it->sh, sizeof(it->buf));
     
-#line 66 "parser_ttl.c"
+#line 69 "parser_ttl.c"
 if (it->yyt1) it->yyt1 -= shift; 
-#line 58 "lexer_ttl.re"
+#line 61 "lexer_ttl.re"
 
     it->lim[0] = 0;
     it->eof |= it->lim < it->buf + CHUNK_SIZE - 1;
@@ -72,18 +75,19 @@ if (it->yyt1) it->yyt1 -= shift;
 }
 
 
-static void parse_init (ParseIterator *it, FILE *fh)
+static void parse_init (ParseIterator *it, FILE *fh, const char *sh)
 {
     it->fh = fh;
+    it->sh = sh;
     it->cur = it->mar = it->tok = it->lim = it->buf + CHUNK_SIZE - 1;
     it->line = 1;
     it->stmt = 1;
     it->bol = it->buf;
     it->eof = 0;
     
-#line 85 "parser_ttl.c"
+#line 89 "parser_ttl.c"
 it->yyt1 = NULL; 
-#line 73 "lexer_ttl.re"
+#line 77 "lexer_ttl.re"
 
     fill (it);
 }
@@ -110,7 +114,7 @@ static int lex (ParseIterator *it, YYCTYPE **token_p)
 {
     const YYCTYPE *pfx;
 
-    #line 138 "lexer_ttl.re"
+    #line 142 "lexer_ttl.re"
 
 
 loop: // Start new token.
@@ -119,7 +123,7 @@ loop: // Start new token.
     *token_p = NULL;
 
     
-#line 123 "parser_ttl.c"
+#line 127 "parser_ttl.c"
 {
 	YYCTYPE yych;
 	unsigned int yyaccept = 0;
@@ -178,7 +182,7 @@ yyFillLabel0:
 yy1:
 	++YYCURSOR;
 yy2:
-#line 147 "lexer_ttl.re"
+#line 151 "lexer_ttl.re"
 	{
         log_warn (
             "Invalid token @ %lu: %s (\\x%x)",
@@ -186,7 +190,7 @@ yy2:
 
         return -1;
     }
-#line 190 "parser_ttl.c"
+#line 194 "parser_ttl.c"
 yy3:
 	++YYCURSOR;
 yyFillLabel1:
@@ -208,7 +212,7 @@ yyFillLabel1:
 			goto yy4;
 	}
 yy4:
-#line 215 "lexer_ttl.re"
+#line 219 "lexer_ttl.re"
 	{
         uint8_t *ws = uint8_ndup (it->tok, YYCURSOR - it->tok);
         LOG_TRACE("Whitespace: '%s'", ws);
@@ -221,7 +225,7 @@ yy4:
 
         return T_WS;
     }
-#line 225 "parser_ttl.c"
+#line 229 "parser_ttl.c"
 yy5:
 	++YYCURSOR;
 yyFillLabel2:
@@ -243,12 +247,12 @@ yyFillLabel2:
 			goto yy6;
 	}
 yy6:
-#line 160 "lexer_ttl.re"
+#line 164 "lexer_ttl.re"
 	{
         newline (it);
         goto loop;
     }
-#line 252 "parser_ttl.c"
+#line 256 "parser_ttl.c"
 yy7:
 	yyaccept = 0;
 	YYMARKER = ++YYCURSOR;
@@ -263,9 +267,9 @@ yyFillLabel3:
 			goto yy8;
 	}
 yy8:
-#line 167 "lexer_ttl.re"
+#line 171 "lexer_ttl.re"
 	{ goto schar; }
-#line 269 "parser_ttl.c"
+#line 273 "parser_ttl.c"
 yy9:
 	yyaccept = 1;
 	YYMARKER = ++YYCURSOR;
@@ -292,12 +296,12 @@ yyFillLabel4:
 		default: goto yy10;
 	}
 yy10:
-#line 210 "lexer_ttl.re"
+#line 214 "lexer_ttl.re"
 	{
         LOG_TRACE("Comment: `%s`", it->tok);
         goto loop;
     }
-#line 301 "parser_ttl.c"
+#line 305 "parser_ttl.c"
 yy11:
 	++YYCURSOR;
 yyFillLabel5:
@@ -314,14 +318,14 @@ yyFillLabel5:
 			goto yy12;
 	}
 yy12:
-#line 270 "lexer_ttl.re"
+#line 274 "lexer_ttl.re"
 	{ return T_LPAREN; }
-#line 320 "parser_ttl.c"
+#line 324 "parser_ttl.c"
 yy13:
 	++YYCURSOR;
-#line 272 "lexer_ttl.re"
+#line 276 "lexer_ttl.re"
 	{ return T_RPAREN; }
-#line 325 "parser_ttl.c"
+#line 329 "parser_ttl.c"
 yy14:
 	yyaccept = 2;
 	YYMARKER = ++YYCURSOR;
@@ -352,9 +356,9 @@ yyFillLabel7:
 			goto yy16;
 	}
 yy16:
-#line 286 "lexer_ttl.re"
+#line 290 "lexer_ttl.re"
 	{ return T_COMMA; }
-#line 358 "parser_ttl.c"
+#line 362 "parser_ttl.c"
 yy17:
 	++YYCURSOR;
 yyFillLabel8:
@@ -368,13 +372,13 @@ yyFillLabel8:
 			goto yy18;
 	}
 yy18:
-#line 288 "lexer_ttl.re"
+#line 292 "lexer_ttl.re"
 	{
         LOG_TRACE("End of statement #%u.", it->stmt);
         it->stmt++;
         return T_PERIOD;
     }
-#line 378 "parser_ttl.c"
+#line 382 "parser_ttl.c"
 yy19:
 	yyaccept = 3;
 	YYMARKER = ++YYCURSOR;
@@ -392,7 +396,7 @@ yyFillLabel9:
 			goto yy20;
 	}
 yy20:
-#line 235 "lexer_ttl.re"
+#line 239 "lexer_ttl.re"
 	{
         // Normalize sign.
         size_t offset = *it->tok == '+' ? 1 : 0;
@@ -402,7 +406,7 @@ yy20:
 
         return T_INTEGER;
     }
-#line 406 "parser_ttl.c"
+#line 410 "parser_ttl.c"
 yy21:
 	yyaccept = 4;
 	YYMARKER = ++YYCURSOR;
@@ -423,14 +427,14 @@ yyFillLabel10:
 		default: goto yy66;
 	}
 yy22:
-#line 196 "lexer_ttl.re"
+#line 200 "lexer_ttl.re"
 	{
         *token_p = uint8_ndup (it->tok, YYCURSOR - it->tok);
         LOG_TRACE("ID name: %s", *token_p);
 
         return T_QNAME;
     }
-#line 434 "parser_ttl.c"
+#line 438 "parser_ttl.c"
 yy23:
 	++YYCURSOR;
 yyFillLabel11:
@@ -447,13 +451,13 @@ yyFillLabel11:
 			goto yy24;
 	}
 yy24:
-#line 280 "lexer_ttl.re"
+#line 284 "lexer_ttl.re"
 	{
         LOG_TRACE("End of object list.");
 
         return T_SEMICOLON;
     }
-#line 457 "parser_ttl.c"
+#line 461 "parser_ttl.c"
 yy25:
 	yyaccept = 2;
 	YYMARKER = ++YYCURSOR;
@@ -522,14 +526,14 @@ yyFillLabel15:
 			goto yy29;
 	}
 yy29:
-#line 274 "lexer_ttl.re"
+#line 278 "lexer_ttl.re"
 	{ return T_LBRACKET; }
-#line 528 "parser_ttl.c"
+#line 532 "parser_ttl.c"
 yy30:
 	++YYCURSOR;
-#line 276 "lexer_ttl.re"
+#line 280 "lexer_ttl.re"
 	{ return T_RBRACKET; }
-#line 533 "parser_ttl.c"
+#line 537 "parser_ttl.c"
 yy31:
 	++YYCURSOR;
 yyFillLabel16:
@@ -575,12 +579,12 @@ yyFillLabel18:
 			goto yy34;
 	}
 yy34:
-#line 296 "lexer_ttl.re"
+#line 300 "lexer_ttl.re"
 	{
         LOG_TRACE("RDF type shorthand 'a'.");
         return T_RDF_TYPE;
     }
-#line 584 "parser_ttl.c"
+#line 588 "parser_ttl.c"
 yy35:
 	yyaccept = 2;
 	YYMARKER = ++YYCURSOR;
@@ -1002,7 +1006,7 @@ yyFillLabel44:
 			goto yy63;
 	}
 yy63:
-#line 255 "lexer_ttl.re"
+#line 259 "lexer_ttl.re"
 	{
         // Normalize sign.
         YYCTYPE offset = *it->tok == '+' ? 1 : 0;
@@ -1017,7 +1021,7 @@ yy63:
 
         return T_DECIMAL;
     }
-#line 1021 "parser_ttl.c"
+#line 1025 "parser_ttl.c"
 yy64:
 	++YYCURSOR;
 yyFillLabel45:
@@ -1272,14 +1276,14 @@ yy81:
 yy82:
 	++YYCURSOR;
 yy83:
-#line 176 "lexer_ttl.re"
+#line 180 "lexer_ttl.re"
 	{
         *token_p = uint8_ndup (it->tok + 1, YYCURSOR - it->tok - 2);
         LOG_TRACE("URI data: %s", *token_p);
 
         return T_IRIREF;
     }
-#line 1283 "parser_ttl.c"
+#line 1287 "parser_ttl.c"
 yy84:
 	++YYCURSOR;
 yyFillLabel61:
@@ -1416,14 +1420,14 @@ yy94:
 			goto yy95;
 	}
 yy95:
-#line 228 "lexer_ttl.re"
+#line 232 "lexer_ttl.re"
 	{
         *token_p = uint8_ndup (it->tok + 1, YYCURSOR - it->tok - 1);
         LOG_TRACE("Lang tag: '%s'", *token_p);
 
         return T_LANGTAG;
     }
-#line 1427 "parser_ttl.c"
+#line 1431 "parser_ttl.c"
 yy96:
 	yyaccept = 11;
 	YYMARKER = ++YYCURSOR;
@@ -1682,9 +1686,9 @@ yyFillLabel87:
 	}
 yy114:
 	++YYCURSOR;
-#line 294 "lexer_ttl.re"
+#line 298 "lexer_ttl.re"
 	{ return T_DTYPE_MARKER; }
-#line 1688 "parser_ttl.c"
+#line 1692 "parser_ttl.c"
 yy115:
 	++YYCURSOR;
 yyFillLabel88:
@@ -1890,9 +1894,9 @@ yyFillLabel103:
 	}
 yy131:
 	++YYCURSOR;
-#line 165 "lexer_ttl.re"
+#line 169 "lexer_ttl.re"
 	{ goto lchar; }
-#line 1896 "parser_ttl.c"
+#line 1900 "parser_ttl.c"
 yy132:
 	++YYCURSOR;
 yyFillLabel104:
@@ -2062,7 +2066,7 @@ yyFillLabel117:
 			goto yy146;
 	}
 yy146:
-#line 245 "lexer_ttl.re"
+#line 249 "lexer_ttl.re"
 	{
         // Normalize sign.
         size_t offset = *it->tok == '+' ? 1 : 0;
@@ -2072,7 +2076,7 @@ yy146:
 
         return T_DOUBLE;
     }
-#line 2076 "parser_ttl.c"
+#line 2080 "parser_ttl.c"
 yy147:
 	++YYCURSOR;
 yyFillLabel118:
@@ -2497,14 +2501,14 @@ yy178:
 			goto yy179;
 	}
 yy179:
-#line 203 "lexer_ttl.re"
+#line 207 "lexer_ttl.re"
 	{
         *token_p = uint8_ndup (it->tok + 2, YYCURSOR - it->tok - 2);
         LOG_TRACE("BNode name: %s", *token_p);
 
         return T_BNODE_ID;
     }
-#line 2508 "parser_ttl.c"
+#line 2512 "parser_ttl.c"
 yy180:
 	++YYCURSOR;
 yyFillLabel149:
@@ -2932,24 +2936,24 @@ yyFillLabel180:
 			goto yy212;
 	}
 yy212:
-#line 169 "lexer_ttl.re"
+#line 173 "lexer_ttl.re"
 	{
         *token_p = uint8_ndup (it->tok, YYCURSOR - it->tok);
         LOG_TRACE("Boolean: %s", *token_p);
 
         return T_BOOLEAN;
     }
-#line 2943 "parser_ttl.c"
+#line 2947 "parser_ttl.c"
 yy213:
 	++YYCURSOR;
 yy214:
-#line 190 "lexer_ttl.re"
+#line 194 "lexer_ttl.re"
 	{
         LOG_TRACE("'@base' keyword.");
 
         return T_BASE;
     }
-#line 2953 "parser_ttl.c"
+#line 2957 "parser_ttl.c"
 yy215:
 	++YYCURSOR;
 yyFillLabel181:
@@ -3170,14 +3174,14 @@ yy226:
 	++YYCURSOR;
 yy227:
 	pfx = it->yyt1;
-#line 183 "lexer_ttl.re"
+#line 187 "lexer_ttl.re"
 	{
         *token_p = uint8_ndup (pfx, YYCURSOR - pfx - 1);
         LOG_TRACE("Prefix declaration: '%s'", *token_p);
 
         return T_PREFIX;
     }
-#line 3181 "parser_ttl.c"
+#line 3185 "parser_ttl.c"
 yy228:
 	++YYCURSOR;
 yyFillLabel192:
@@ -3621,19 +3625,19 @@ yyFillLabel223:
 			goto yy52;
 	}
 yy260:
-#line 155 "lexer_ttl.re"
+#line 159 "lexer_ttl.re"
 	{
         LOG_TRACE("End of document.");
         return T_EOF;
     }
-#line 3630 "parser_ttl.c"
+#line 3634 "parser_ttl.c"
 }
-#line 301 "lexer_ttl.re"
+#line 305 "lexer_ttl.re"
 
 
 schar:
     
-#line 3637 "parser_ttl.c"
+#line 3641 "parser_ttl.c"
 {
 	YYCTYPE yych;
 	unsigned int yyaccept = 0;
@@ -3661,7 +3665,7 @@ yyFillLabel224:
 yy262:
 	++YYCURSOR;
 yy263:
-#line 306 "lexer_ttl.re"
+#line 310 "lexer_ttl.re"
 	{
         log_warn (
             "Invalid token in string @ %lu: %s (\\x%x)",
@@ -3669,23 +3673,23 @@ yy263:
 
         return -1;
     }
-#line 3673 "parser_ttl.c"
+#line 3677 "parser_ttl.c"
 yy264:
 	++YYCURSOR;
 yy265:
-#line 320 "lexer_ttl.re"
+#line 324 "lexer_ttl.re"
 	{ goto schar; }
-#line 3679 "parser_ttl.c"
+#line 3683 "parser_ttl.c"
 yy266:
 	++YYCURSOR;
-#line 322 "lexer_ttl.re"
+#line 326 "lexer_ttl.re"
 	{
         *token_p = unescape_unicode (it->tok + 1, YYCURSOR - it->tok - 2);
         LOG_TRACE("String: %s", *token_p);
 
         return T_STRING;
     }
-#line 3689 "parser_ttl.c"
+#line 3693 "parser_ttl.c"
 yy267:
 	yyaccept = 0;
 	YYMARKER = ++YYCURSOR;
@@ -3911,20 +3915,20 @@ yyFillLabel241:
 			goto yy275;
 	}
 yy285:
-#line 314 "lexer_ttl.re"
+#line 318 "lexer_ttl.re"
 	{
         log_warn ("Unterminated string!");
 
         return -1;
     }
-#line 3921 "parser_ttl.c"
+#line 3925 "parser_ttl.c"
 }
-#line 329 "lexer_ttl.re"
+#line 333 "lexer_ttl.re"
 
 
 lchar:
     
-#line 3928 "parser_ttl.c"
+#line 3932 "parser_ttl.c"
 {
 	YYCTYPE yych;
 	unsigned int yyaccept = 0;
@@ -3954,7 +3958,7 @@ yyFillLabel242:
 yy287:
 	++YYCURSOR;
 yy288:
-#line 349 "lexer_ttl.re"
+#line 353 "lexer_ttl.re"
 	{
         log_warn (
             "Invalid token in long string @ %lu: %s (\\x%x)",
@@ -3962,13 +3966,13 @@ yy288:
 
         return -1;
     }
-#line 3966 "parser_ttl.c"
+#line 3970 "parser_ttl.c"
 yy289:
 	++YYCURSOR;
 yy290:
-#line 340 "lexer_ttl.re"
+#line 344 "lexer_ttl.re"
 	{ goto lchar; }
-#line 3972 "parser_ttl.c"
+#line 3976 "parser_ttl.c"
 yy291:
 	yyaccept = 0;
 	YYMARKER = ++YYCURSOR;
@@ -4145,14 +4149,14 @@ yyFillLabel255:
 	}
 yy305:
 	++YYCURSOR;
-#line 342 "lexer_ttl.re"
+#line 346 "lexer_ttl.re"
 	{
         *token_p = unescape_unicode (it->tok + 3, YYCURSOR - it->tok - 6);
         LOG_TRACE("Long string: %s", it->tok);
 
         return T_STRING;
     }
-#line 4156 "parser_ttl.c"
+#line 4160 "parser_ttl.c"
 yy306:
 	++YYCURSOR;
 yyFillLabel256:
@@ -4232,21 +4236,22 @@ yyFillLabel261:
 			goto yy300;
 	}
 yy312:
-#line 334 "lexer_ttl.re"
+#line 338 "lexer_ttl.re"
 	{
         log_warn ("Unterminated long string!");
 
         return -1;
     }
-#line 4242 "parser_ttl.c"
+#line 4246 "parser_ttl.c"
 }
-#line 357 "lexer_ttl.re"
+#line 361 "lexer_ttl.re"
 
 }
 
 
 VOLK_rc
-VOLK_ttl_parse_doc (FILE *fh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
+VOLK_ttl_parse_doc (
+        FILE *fh, const char *sh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
 {
     *err_p = NULL;
     *gr_p = NULL;
@@ -4260,7 +4265,7 @@ VOLK_ttl_parse_doc (FILE *fh, VOLK_Graph **gr_p, size_t *ct, char **err_p)
     state->rc = VOLK_NORESULT;
 
     ParseIterator parse_it;
-    parse_init (&parse_it, fh);
+    parse_init (&parse_it, fh, sh);
 
     void *parser = TTLParseAlloc (malloc);
 

+ 94 - 16
src/core.c

@@ -1,6 +1,7 @@
-#define _XOPEN_SOURCE 500
 #include <errno.h>
-#include <ftw.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <string.h>
 
 #include "lmdb.h"
@@ -87,26 +88,103 @@ finally:
 }
 
 
-int
-unlink_cb(
-        const char *fpath, const struct stat *sb, int typeflag,
-        struct FTW *ftwbuf)
+char *strndup (const char *src, size_t max)
 {
-    (void) sb;
-    (void) typeflag;
-    (void) ftwbuf;
+    size_t len = strlen (src);
+    if (len > max) len = max;
 
-    LOG_DEBUG("Removing %s", fpath);
-    int rv = remove(fpath);
+    char *res = (char*)malloc (len + 1);
+    if (res) {
+        memcpy (res, src, len);
+        res[len] = '\0';
+    }
+
+    return res;
+}
 
-    if (rv)
-        perror(fpath);
 
-    return rv;
+char *strdup (const char *src)
+{
+   char *res = (char*)malloc (strlen (src) + 1);
+   if (res) strcpy(res, src);
+
+   return res;
 }
 
-int rm_r(const char *path)
-{ return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); }
+
+/** @brief Remove a directory recursively (POSIX compatible).
+ *
+ * Adapted from
+ * https://stackoverflow.com/questions/5467725/how-to-delete-a-directory-and-its-contents-in-posix-c/42596507#42596507
+ */
+VOLK_rc rm_r (const char *path)
+{
+    size_t path_len;
+    char *full_path;
+    DIR *dir;
+    struct stat stat_path, stat_entry;
+    struct dirent *entry;
+
+    // stat for the path
+    stat(path, &stat_path);
+
+    // if path does not exists or is not dir - exit with status -1
+    if (S_ISDIR(stat_path.st_mode) == 0) {
+        log_error ("%s: %s\n", "Is not directory", path);
+        return VOLK_IO_ERR;
+    }
+
+    // if not possible to read the directory for this user
+    if ((dir = opendir(path)) == NULL) {
+        log_error ("%s: %s\n", "Can`t open directory", path);
+        return VOLK_IO_ERR;
+    }
+
+    // the length of the path
+    path_len = strlen(path);
+
+    // iteration through entries in the directory
+    while ((entry = readdir(dir)) != NULL) {
+
+        // skip entries "." and ".."
+        if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+            continue;
+
+        // determine a full path of an entry
+        full_path = calloc(
+                path_len + 1 + strlen(entry->d_name) + 1, sizeof(char));
+        strcpy(full_path, path);
+        strcat(full_path, "/");
+        strcat(full_path, entry->d_name);
+
+        // stat for the entry
+        stat(full_path, &stat_entry);
+
+        // recursively remove a nested directory
+        if (S_ISDIR(stat_entry.st_mode) != 0) {
+            rm_r (full_path);
+            free (full_path);
+            continue;
+        }
+
+        // remove a file object
+        if (unlink(full_path) == 0)
+            LOG_DEBUG ("Removed a file:\t%s\n", full_path);
+        else
+            log_error ("Can't remove a file:\t%s\n", full_path);
+        free(full_path);
+    }
+
+    // remove the devastated directory and close the object of it
+    if (rmdir(path) == 0)
+        LOG_DEBUG ("Removed a directory:\t%s\n", path);
+    else
+        log_error ("Can't remove a directory:\t%s\n", path);
+
+    closedir(dir);
+
+    return VOLK_OK;
+}
 
 
 const char *

+ 0 - 2
test.c

@@ -13,9 +13,7 @@
 int main() {
 
     // Set env variable to test path.
-    putenv ("VOLK_MDB_STORE_PATH=" TEST_STORE_PATH);
     // Clear out database from previous test.
-    rm_r (TEST_STORE_PATH);
 
     clock_t start, end;
     double wallclock;

+ 2 - 7
test/test_codec_nt.c

@@ -210,14 +210,11 @@ test_decode_nt_term()
 int
 test_decode_nt_graph()
 {
-    FILE *input = fmemopen ((void *)start_nt_doc, strlen (start_nt_doc), "r");
 
     VOLK_Graph *gr;
     size_t ct;
     char *err;
-    EXPECT_PASS (codec.decode_graph (input, &gr, &ct, &err));
-
-    fclose (input);
+    EXPECT_PASS (codec.decode_graph (NULL, start_nt_doc, &gr, &ct, &err));
 
     ASSERT (err == NULL, "Error string is not NULL!");
 
@@ -254,12 +251,11 @@ int
 test_decode_nt_bad_graph()
 {
     log_info ("testing illegal NT document.");
-    FILE *input = fmemopen ((void *)bad_nt_doc, strlen (bad_nt_doc), "r");
 
     VOLK_Graph *gr;
     size_t ct;
     char *err;
-    VOLK_rc rc = codec.decode_graph (input, &gr, &ct, &err);
+    VOLK_rc rc = codec.decode_graph (NULL, bad_nt_doc, &gr, &ct, &err);
     EXPECT_INT_EQ (rc, VOLK_PARSE_ERR);
 
     log_info ("Error: %s", err);
@@ -268,7 +264,6 @@ test_decode_nt_bad_graph()
     ASSERT (strstr (err, "character 16") != NULL, "Wrong error char report!");
 
     free (err);
-    fclose (input);
     VOLK_graph_free (gr);
 
     return 0;

+ 2 - 2
test/test_codec_ttl.c

@@ -77,7 +77,7 @@ test_w3c_pos()
             if (ch == '\n') nt_ct++;
         }
 
-        EXPECT_PASS (codec.decode_graph (test_stream, &gr, &ct, &err));
+        EXPECT_PASS (codec.decode_graph (test_stream, NULL, &gr, &ct, &err));
         EXPECT_INT_EQ (VOLK_graph_size (gr), nt_ct); // Just count NT lines.
         VOLK_graph_free (gr);
         fclose (test_stream);
@@ -103,7 +103,7 @@ test_w3c_neg()
         FILE *test_stream = fopen (test_fname, "r");
         log_info ("Testing %s", test_fname);
 
-        VOLK_rc rc = codec.decode_graph (test_stream, &gr, &ct, &err);
+        VOLK_rc rc = codec.decode_graph (test_stream, NULL, &gr, &ct, &err);
         log_info ("rc: %d", rc);
         ASSERT (rc == VOLK_PARSE_ERR, "Bad test did not raise a parse error!");
         fclose (test_stream);

+ 1 - 1
test/test_store_mdb.c

@@ -18,7 +18,7 @@ static int test_ctx_switch()
 
     // Create enough triples to test a multi-page copy of triple data.
     // Add small buffer (4) to create a 3rd page.
-    size_t num_trp = (getpagesize() * 2 / TRP_KLEN) + 4;
+    size_t num_trp = (sysconf(_SC_PAGESIZE) * 2 / TRP_KLEN) + 4;
     VOLK_BufferTriple **tdata = malloc (num_trp * sizeof (*tdata));
     VOLK_Triple *trp = VOLK_triple_new (
         VOLK_iriref_new ("urn:s:1"),