prepare serverconfig parser to be also used for obj.conf and init.conf

Sun, 06 Nov 2022 17:41:39 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 06 Nov 2022 17:41:39 +0100
changeset 417
90805bb9fbd6
parent 416
e2093ca0ef90
child 418
b7dcc9c4f270

prepare serverconfig parser to be also used for obj.conf and init.conf

src/server/config/serverconfig.c file | annotate | diff | comparison | revisions
src/server/config/serverconfig.h file | annotate | diff | comparison | revisions
--- a/src/server/config/serverconfig.c	Sun Nov 06 16:59:39 2022 +0100
+++ b/src/server/config/serverconfig.c	Sun Nov 06 17:41:39 2022 +0100
@@ -44,8 +44,18 @@
         return NULL;
     }
     
+    CxMempool *mp = cxBasicMempoolCreate(512);
+    if(!mp) {
+        fclose(in);
+        return NULL;
+    }
+    
     CxBuffer buf;
-    cxBufferInit(&buf, NULL, 16384, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
+    if(cxBufferInit(&buf, NULL, 16384, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
+        fclose(in);
+        cxMempoolDestroy(mp);
+        return NULL;
+    }
     
     //ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write);
     char readbuf[2048];
@@ -55,7 +65,11 @@
     }
     fclose(in);
     
-    ServerConfig *scfg = serverconfig_parse(cx_strn(buf.space, buf.size));
+    ConfigParser2 parser;
+    ZERO(&parser, sizeof(ConfigParser2));
+    parser.mp = mp;
+    parser.allow_hierarchy = true;
+    ServerConfig *scfg = serverconfig_parse(&parser, cx_strn(buf.space, buf.size));
     
     cxBufferDestroy(&buf);
     return scfg;
@@ -190,9 +204,8 @@
     return 0;
 }
 
-ServerConfig* serverconfig_parse(cxstring content) {
-    CxMempool *mp = cxBasicMempoolCreate(512);
-    if(!mp) return NULL;
+ServerConfig* serverconfig_parse(ConfigParser2 *parser, cxstring content) {
+    CxMempool *mp = parser->mp;
     CxAllocator *a = (CxAllocator*)mp->allocator;
     
     ServerConfig *config = cxMalloc(a, sizeof(ServerConfig));
@@ -248,6 +261,12 @@
                     // this is a newline after a object is closed with '}'
                     // the line containing "}\n" should be added to the object
                     parent->text_end = line_cp;
+                    // validate
+                    if(parser->validateObjEnd && parser->validateObjEnd(parser, parent)) {
+                        // validation failed
+                        err = 1;
+                        break;
+                    }
                     // done with this object, remove it from the stack
                     ConfigNodeStack *remove_item = node_stack;
                     node_stack = node_stack->next;
@@ -261,6 +280,10 @@
                     obj->text_begin = new_textbegin;
                 } else {
                     // normal line containing a directive, space or comment
+                    if(parser->validateDirective && parser->validateDirective(parser, current)) {
+                        err = 1;
+                        break;
+                    }
                     // add it to parent node
                     current->text_begin = line_cp;
                     CFG_NODE_ADD(&parent->children_begin, &parent->children_end, current);
@@ -270,7 +293,14 @@
                 // the type is set to CONFIG_NODE_OBECT if it was followed by
                 // a '{' character
                 if(obj && obj->type == CONFIG_NODE_OBJECT) {
-                    // new object started, add it to the stack
+                    // new object started
+                    if(parser->validateObjBegin && parser->validateObjBegin(parser, obj)) {
+                        // validation callback failed
+                        err = 1;
+                        break;
+                    }
+                    
+                    // add it to the stack
                     nodestack_prepend(a, &node_stack, obj);
                     obj = NULL;
                 }
@@ -286,6 +316,13 @@
                 // either a directive/obj name, parameter or { }
                 
                 if(!cx_strcmp(token.content, cx_str("{"))) {
+                    // check if the parser allows an object hierarchy
+                    if(!parser->allow_hierarchy) {
+                        parser->error = CONFIG_PARSER_SYNTAX_ERROR;
+                        err = 1;
+                        break;
+                    }
+                    
                     // obj is pointing to the previous node that started
                     // a directive
                     if(!obj) {
@@ -297,6 +334,13 @@
                         current->type = CONFIG_NODE_OPEN_OBJECT;
                     }
                 } else if(!cx_strcmp(token.content, cx_str("}"))) {
+                    // check if the parser allows an object hierarchy
+                    if(!parser->allow_hierarchy) {
+                        parser->error = CONFIG_PARSER_SYNTAX_ERROR;
+                        err = 1;
+                        break;
+                    }
+                    
                     obj_closed = 1; // force newline before next directive
                     obj = NULL;
                     current->type = CONFIG_NODE_CLOSE_OBJECT;
--- a/src/server/config/serverconfig.h	Sun Nov 06 16:59:39 2022 +0100
+++ b/src/server/config/serverconfig.h	Sun Nov 06 17:41:39 2022 +0100
@@ -34,6 +34,8 @@
 #include <cx/mempool.h>
 #include <cx/string.h>
 
+#include <stdbool.h>
+
 #include "conf.h"
 
 #ifdef __cplusplus
@@ -46,6 +48,7 @@
 typedef struct ServerConfig    ServerConfig;
 typedef struct ConfigNode      ConfigNode;
 typedef struct CFGToken        CFGToken;
+typedef struct ConfigParser2   ConfigParser2; // TODO: rename to ConfigParser after old ConfigParser is removed
 
 typedef enum ConfigNodeType    ConfigNodeType;
 typedef enum CFGTokenType      CFGTokenType;
@@ -98,9 +101,50 @@
     CFGTokenType type;
 };
 
+enum ConfigParserError {
+    CONFIG_PARSER_OOM = 0,
+    CONFIG_PARSER_SYNTAX_ERROR
+};
+
+/*
+ * CfgValidateNodeFunc functions are called by the parser to validate
+ * objects and directives.
+ * 
+ * The function must return 0 if the validation was successful. If the
+ * functions returns a value != 0, the parser aborts.
+ */
+typedef int(*CfgValidateNodeFunc)(ConfigParser2 *parser, ConfigNode *node);
+
+struct ConfigParser2 {
+    CxMempool *mp;
+    
+    int error;
+    
+    /*
+     * called when an object is started
+     */
+    CfgValidateNodeFunc validateObjBegin;
+    
+    /*
+     * called when an object is closed
+     */
+    CfgValidateNodeFunc validateObjEnd;
+    
+    /*
+     * called before a directive is added
+     */
+    CfgValidateNodeFunc validateDirective;
+    
+    /*
+     * true: store result in a tree
+     * false: flat list of directives, don't allow '{' and  '}'
+     */
+    bool allow_hierarchy;
+};
+
 ServerConfig* serverconfig_load(const char *file);
 
-ServerConfig* serverconfig_parse(cxstring content);
+ServerConfig* serverconfig_parse(ConfigParser2 *parser, cxstring content);
 
 void serverconfig_free(ServerConfig *cfg);
 

mercurial