Ver código fonte

ncd: add NCDValueParser

ambrop7 14 anos atrás
pai
commit
3db25274ae

+ 1 - 0
blog_channels.txt

@@ -83,6 +83,7 @@ BTap 4
 lwip 4
 NCDConfigTokenizer 4
 NCDConfigParser 4
+NCDValueParser 4
 nsskey 4
 addr 4
 PasswordListener 4

+ 3 - 0
examples/CMakeLists.txt

@@ -31,6 +31,9 @@ if (BUILD_NCD)
 
     add_executable(ncd_parser_test ncd_parser_test.c)
     target_link_libraries(ncd_parser_test ncdconfig)
+
+    add_executable(ncd_value_parser_test ncd_value_parser_test.c)
+    target_link_libraries(ncd_value_parser_test ncdvalue)
 endif ()
 
 if (BUILDING_UDEVMONITOR)

+ 87 - 0
examples/ncd_value_parser_test.c

@@ -0,0 +1,87 @@
+/**
+ * @file ncd_value_parser_test.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <misc/debug.h>
+#include <base/BLog.h>
+#include <ncd/NCDValueParser.h>
+
+static void print_indent (unsigned int indent)
+{
+    while (indent > 0) {
+        printf("  ");
+        indent--;
+    }
+}
+
+static void print_value (NCDValue *val, unsigned int indent)
+{
+    switch (NCDValue_Type(val)) {
+        case NCDVALUE_STRING: {
+            print_indent(indent);
+            printf("string: '%s'\n", NCDValue_StringValue(val));
+        } break;
+        case NCDVALUE_LIST: {
+            print_indent(indent);
+            printf("list:\n");
+            
+            for (NCDValue *e = NCDValue_ListFirst(val); e; e = NCDValue_ListNext(val, e)) {
+                print_value(e, indent + 1);
+            }
+        } break;
+        default: ASSERT(0);
+    }
+}
+
+int main (int argc, char **argv)
+{
+    if (argc != 2) {
+        printf("Usage: %s <string>\n", (argc > 0 ? argv[0] : ""));
+        return 1;
+    }
+    
+    BLog_InitStdout();
+    
+    // parse
+    NCDValue val;
+    if (!NCDValueParser_Parse(argv[1], strlen(argv[1]), &val)) {
+        DEBUG("NCDConfigParser_Parse failed");
+        return 1;
+    }
+    
+    // print
+    print_value(&val, 0);
+    
+    NCDValue_Free(&val);
+    
+    return 0;
+}

+ 4 - 0
generated/blog_channel_NCDValueParser.h

@@ -0,0 +1,4 @@
+#ifdef BLOG_CURRENT_CHANNEL
+#undef BLOG_CURRENT_CHANNEL
+#endif
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDValueParser

+ 17 - 16
generated/blog_channels_defines.h

@@ -83,19 +83,20 @@
 #define BLOG_CHANNEL_lwip 82
 #define BLOG_CHANNEL_NCDConfigTokenizer 83
 #define BLOG_CHANNEL_NCDConfigParser 84
-#define BLOG_CHANNEL_nsskey 85
-#define BLOG_CHANNEL_addr 86
-#define BLOG_CHANNEL_PasswordListener 87
-#define BLOG_CHANNEL_NCDInterfaceMonitor 88
-#define BLOG_CHANNEL_NCDRfkillMonitor 89
-#define BLOG_CHANNEL_udpgw 90
-#define BLOG_CHANNEL_UdpGwClient 91
-#define BLOG_CHANNEL_SocksUdpGwClient 92
-#define BLOG_CHANNEL_BNetwork 93
-#define BLOG_CHANNEL_BConnection 94
-#define BLOG_CHANNEL_BSSLConnection 95
-#define BLOG_CHANNEL_BDatagram 96
-#define BLOG_CHANNEL_PeerChat 97
-#define BLOG_CHANNEL_BArpProbe 98
-#define BLOG_CHANNEL_NCDModuleIndex 99
-#define BLOG_NUM_CHANNELS 100
+#define BLOG_CHANNEL_NCDValueParser 85
+#define BLOG_CHANNEL_nsskey 86
+#define BLOG_CHANNEL_addr 87
+#define BLOG_CHANNEL_PasswordListener 88
+#define BLOG_CHANNEL_NCDInterfaceMonitor 89
+#define BLOG_CHANNEL_NCDRfkillMonitor 90
+#define BLOG_CHANNEL_udpgw 91
+#define BLOG_CHANNEL_UdpGwClient 92
+#define BLOG_CHANNEL_SocksUdpGwClient 93
+#define BLOG_CHANNEL_BNetwork 94
+#define BLOG_CHANNEL_BConnection 95
+#define BLOG_CHANNEL_BSSLConnection 96
+#define BLOG_CHANNEL_BDatagram 97
+#define BLOG_CHANNEL_PeerChat 98
+#define BLOG_CHANNEL_BArpProbe 99
+#define BLOG_CHANNEL_NCDModuleIndex 100
+#define BLOG_NUM_CHANNELS 101

+ 1 - 0
generated/blog_channels_list.h

@@ -83,6 +83,7 @@
 {.name = "lwip", .loglevel = 4},
 {.name = "NCDConfigTokenizer", .loglevel = 4},
 {.name = "NCDConfigParser", .loglevel = 4},
+{.name = "NCDValueParser", .loglevel = 4},
 {.name = "nsskey", .loglevel = 4},
 {.name = "addr", .loglevel = 4},
 {.name = "PasswordListener", .loglevel = 4},

+ 7 - 2
ncd/CMakeLists.txt

@@ -28,9 +28,14 @@ add_library(ncdconfig
 )
 target_link_libraries(ncdconfig base)
 
+add_library(ncdvalue
+    NCDValue.c
+    NCDValueParser.c
+)
+target_link_libraries(ncdvalue ncdconfig)
+
 add_executable(badvpn-ncd
     ncd.c
-    NCDValue.c
     NCDModule.c
     NCDModuleIndex.c
     NCDIfConfig.c
@@ -82,7 +87,7 @@ add_executable(badvpn-ncd
     modules/sys_watch_usb.c
     ${NCD_ADDITIONAL_SOURCES}
 )
-target_link_libraries(badvpn-ncd system flow flowextra dhcpclient arpprobe ncdconfig udevmonitor)
+target_link_libraries(badvpn-ncd system flow flowextra dhcpclient arpprobe ncdconfig ncdvalue udevmonitor)
 
 if (BADVPN_USE_LINUX_INPUT)
     string(REPLACE " " ";" FLAGS_LIST "${CMAKE_C_FLAGS}")

+ 217 - 0
ncd/NCDValueParser.c

@@ -0,0 +1,217 @@
+/**
+ * @file NCDValueParser.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/debug.h>
+#include <base/BLog.h>
+#include <ncd/NCDConfigTokenizer.h>
+
+#include "NCDValueParser.h"
+
+#include <generated/blog_channel_NCDValueParser.h>
+
+// rename non-static functions defined by our Lemon parser
+// to avoid clashes with other Lemon parsers
+#define ParseTrace ParseTrace_NCDValueParser
+#define ParseAlloc ParseAlloc_NCDValueParser
+#define ParseFree ParseFree_NCDValueParser
+#define Parse Parse_NCDValueParser
+
+#include "../generated/NCDValueParser_parse.c"
+#include "../generated/NCDValueParser_parse.h"
+#include <sys/stat.h>
+
+struct parser_state {
+    struct parser_out out;
+    int error;
+    void *parser;
+};
+
+static int tokenizer_output (void *user, int token, char *value, size_t line, size_t line_char)
+{
+    struct parser_state *state = (struct parser_state *)user;
+    ASSERT(!state->out.out_of_memory)
+    ASSERT(!state->out.syntax_error)
+    ASSERT(!state->error)
+    
+    // some tokens are only valid in NCD scripts and not value strings
+    switch (token) {
+        case NCD_TOKEN_NAME:
+        case NCD_TOKEN_ROUND_OPEN:
+        case NCD_TOKEN_ROUND_CLOSE:
+        case NCD_TOKEN_SEMICOLON:
+        case NCD_TOKEN_DOT:
+        case NCD_TOKEN_ARROW:
+        case NCD_TOKEN_PROCESS:
+        case NCD_TOKEN_TEMPLATE:
+            token = NCD_ERROR;
+            free(value);
+    }
+    
+    if (token == NCD_ERROR) {
+        BLog(BLOG_ERROR, "line %zu, character %zu: tokenizer error", line, line_char);
+        goto fail;
+    }
+    
+    switch (token) {
+        case NCD_EOF: {
+            Parse(state->parser, 0, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_CURLY_OPEN: {
+            Parse(state->parser, CURLY_OPEN, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_CURLY_CLOSE: {
+            Parse(state->parser, CURLY_CLOSE, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_COMMA: {
+            Parse(state->parser, COMMA, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_STRING: {
+            Parse(state->parser, STRING, value, &state->out);
+        } break;
+        
+        default:
+            ASSERT(0);
+    }
+    
+    if (state->out.syntax_error) {
+        BLog(BLOG_ERROR, "line %zu, character %zu: syntax error", line, line_char);
+        goto fail;
+    }
+    
+    if (state->out.out_of_memory) {
+        BLog(BLOG_ERROR, "line %zu, character %zu: out of memory", line, line_char);
+        goto fail;
+    }
+    
+    return 1;
+    
+fail:
+    state->error = 1;
+    return 0;
+}
+
+static int build_list_value (struct NCDConfig_list *list, NCDValue *out)
+{
+    NCDValue list_val;
+    NCDValue_InitList(&list_val);
+    
+    for (struct NCDConfig_list *elem = list; elem; elem = elem->next) {
+        NCDValue v;
+        
+        if (elem->type == NCDCONFIG_ARG_STRING) {
+            if (!NCDValue_InitString(&v, elem->string)) {
+                BLog(BLOG_ERROR, "NCDValue_InitString failed");
+                goto fail;
+            }
+        }
+        else if (elem->type == NCDCONFIG_ARG_LIST) {
+            if (!build_list_value(elem->list, &v)) {
+                goto fail;
+            }
+        }
+        else {
+            ASSERT(0)
+        }
+        
+        if (!NCDValue_ListAppend(&list_val, v)) {
+            BLog(BLOG_ERROR, "NCDValue_ListAppend failed");
+            NCDValue_Free(&v);
+            goto fail;
+        }
+    }
+    
+    *out = list_val;
+    return 1;
+    
+fail:
+    NCDValue_Free(&list_val);
+    return 0;
+}
+
+int NCDValueParser_Parse (const char *str, size_t str_len, NCDValue *out_value)
+{
+    int res = 0;
+    
+    // init parse state
+    struct parser_state state;
+    state.out.out_of_memory = 0;
+    state.out.syntax_error = 0;
+    state.out.ast_type = AST_TYPE_NONE;
+    state.out.ast_string = NULL;
+    state.out.ast_list = NULL;
+    state.error = 0;
+    
+    // allocate parser
+    if (!(state.parser = ParseAlloc(malloc))) {
+        BLog(BLOG_ERROR, "ParseAlloc failed");
+        goto out;
+    }
+    
+    // tokenize and parse
+    NCDConfigTokenizer_Tokenize((char *)str, str_len, tokenizer_output, &state);
+    
+    // check for errors
+    if (state.error) {
+        goto out;
+    }
+    
+    ASSERT(state.out.ast_type == AST_TYPE_STRING || state.out.ast_type == AST_TYPE_LIST)
+    
+    // convert AST to value
+    NCDValue val;
+    if (state.out.ast_type == AST_TYPE_STRING) {
+        if (!NCDValue_InitString(&val, state.out.ast_string)) {
+            BLog(BLOG_ERROR, "NCDValue_InitString failed");
+            goto out;
+        }
+    } else {
+        if (!build_list_value(state.out.ast_list, &val)) {
+            goto out;
+        }
+    }
+    
+    *out_value = val;
+    res = 1;
+    
+out:
+    if (state.parser) {
+        ParseFree(state.parser, free);
+    }
+    free(state.out.ast_string);
+    NCDConfig_free_list(state.out.ast_list);
+    return res;
+}

+ 40 - 0
ncd/NCDValueParser.h

@@ -0,0 +1,40 @@
+/**
+ * @file NCDValueParser.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BADVPN_NCDVALUEPARSER_H
+#define BADVPN_NCDVALUEPARSER_H
+
+#include <stddef.h>
+
+#include <misc/debug.h>
+#include <ncd/NCDValue.h>
+
+int NCDValueParser_Parse (const char *str, size_t str_len, NCDValue *out_value) WARN_UNUSED;
+
+#endif

+ 125 - 0
ncd/NCDValueParser_parse.y

@@ -0,0 +1,125 @@
+/**
+ * @file NCDConfigParser_parse.y
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+%include {
+
+#include <string.h>
+#include <stddef.h>
+
+#include <misc/debug.h>
+#include <ncd/NCDConfig.h>
+
+#define AST_TYPE_NONE 0
+#define AST_TYPE_STRING 1
+#define AST_TYPE_LIST 2
+
+struct parser_out {
+    int out_of_memory;
+    int syntax_error;
+    int ast_type;
+    char *ast_string;
+    struct NCDConfig_list *ast_list;
+};
+
+}
+
+%extra_argument {struct parser_out *parser_out}
+
+%token_type {void *}
+
+%token_destructor { free($$); }
+
+%type list_contents {struct NCDConfig_list *}
+%type list {struct NCDConfig_list *}
+
+%destructor list_contents { NCDConfig_free_list($$); }
+%destructor list { NCDConfig_free_list($$); }
+
+%stack_size 0
+
+%syntax_error {
+    parser_out->syntax_error = 1;
+}
+
+// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked
+%stack_overflow {
+    if (yypMinor) {
+        free(yypMinor->yy0);
+    }
+}
+
+input ::= STRING(A). {
+    ASSERT(parser_out->ast_type == AST_TYPE_NONE)
+
+    parser_out->ast_string = A;
+    parser_out->ast_type = AST_TYPE_STRING;
+}
+
+input ::= list(A). {
+    ASSERT(parser_out->ast_type == AST_TYPE_NONE)
+
+    parser_out->ast_list = A;
+    parser_out->ast_type = AST_TYPE_LIST;
+}
+
+list_contents(R) ::= STRING(A). {
+    R = NCDConfig_make_list_string(A, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list_contents(R) ::= list(A). {
+    R = NCDConfig_make_list_list(A, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list_contents(R) ::= STRING(A) COMMA list_contents(N). {
+    R = NCDConfig_make_list_string(A, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list_contents(R) ::= list(A) COMMA list_contents(N). {
+    R = NCDConfig_make_list_list(A, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+list(R) ::= CURLY_OPEN CURLY_CLOSE. {
+    R = NULL;
+}
+
+list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. {
+    R = A;
+}