Procházet zdrojové kódy

ncdconfig: move to ncd

ambrop7 před 14 roky
rodič
revize
854c3c6d5d

+ 1 - 1
generate_files

@@ -45,4 +45,4 @@ bproto protocol/addr.bproto addr
 do_flex predicate/BPredicate.l BPredicate
 do_bison predicate/BPredicate.y BPredicate
 "${PHP_CMD[@]}" blog_generator/blog.php --input-file blog_channels.txt --output-dir "${OUT_DIR}"
-do_lemon ncdconfig/NCDConfigParser_parse.y
+do_lemon ncd/NCDConfigParser_parse.y

+ 1 - 1
generated/NCDConfigParser_parse.c

@@ -11,7 +11,7 @@
 #include <stddef.h>
 
 #include <misc/debug.h>
-#include <ncdconfig/NCDConfig.h>
+#include <ncd/NCDConfig.h>
 
 struct parser_out {
     int out_of_memory;

+ 1 - 1
generated/NCDConfigParser_parse.y

@@ -26,7 +26,7 @@
 #include <stddef.h>
 
 #include <misc/debug.h>
-#include <ncdconfig/NCDConfig.h>
+#include <ncd/NCDConfig.h>
 
 struct parser_out {
     int out_of_memory;

+ 6 - 0
ncd/CMakeLists.txt

@@ -21,6 +21,12 @@ if (BADVPN_USE_INOTIFY)
     )
 endif ()
 
+add_library(ncdconfig
+    NCDConfig.c
+    NCDConfigTokenizer.c
+    NCDConfigParser.c
+)
+
 add_executable(badvpn-ncd
     ncd.c
     NCDValue.c

+ 279 - 0
ncd/NCDConfig.c

@@ -0,0 +1,279 @@
+/**
+ * @file NCDConfig.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/string_begins_with.h>
+#include <misc/expstring.h>
+
+#include <ncd/NCDConfig.h>
+
+void NCDConfig_free_interfaces (struct NCDConfig_interfaces *v)
+{
+    if (!v) {
+        return;
+    }
+    
+    free(v->name);
+    NCDConfig_free_statements(v->statements);
+    NCDConfig_free_interfaces(v->next);
+    
+    free(v);
+}
+
+void NCDConfig_free_statements (struct NCDConfig_statements *v)
+{
+    if (!v) {
+        return;
+    }
+    
+    NCDConfig_free_strings(v->objname);
+    NCDConfig_free_strings(v->names);
+    NCDConfig_free_arguments(v->args);
+    free(v->name);
+    NCDConfig_free_statements(v->next);
+    
+    free(v);
+}
+
+void NCDConfig_free_arguments (struct NCDConfig_arguments *v)
+{
+    if (!v) {
+        return;
+    }
+    
+    switch (v->type) {
+        case NCDCONFIG_ARG_STRING:
+            free(v->string);
+            break;
+        case NCDCONFIG_ARG_VAR:
+            NCDConfig_free_strings(v->var);
+            break;
+        default:
+            ASSERT(0);
+    }
+    
+    NCDConfig_free_arguments(v->next);
+    
+    free(v);
+}
+
+void NCDConfig_free_strings (struct NCDConfig_strings *v)
+{
+    if (!v) {
+        return;
+    }
+    
+    free(v->value);
+    NCDConfig_free_strings(v->next);
+    
+    free(v);
+}
+
+struct NCDConfig_interfaces * NCDConfig_make_interfaces (int is_template, char *name, struct NCDConfig_statements *statements, int need_next, struct NCDConfig_interfaces *next)
+{
+    if (!name || !statements || (need_next && !next)) {
+        goto fail;
+    }
+    
+    struct NCDConfig_interfaces *v = malloc(sizeof(*v));
+    if (!v) {
+        goto fail;
+    }
+    
+    v->is_template = is_template;
+    v->name = name;
+    v->statements = statements;
+    v->next = next;
+    
+    return v;
+    
+fail:
+    free(name);
+    NCDConfig_free_statements(statements);
+    NCDConfig_free_interfaces(next);
+    return NULL;
+}
+
+struct NCDConfig_statements * NCDConfig_make_statements (struct NCDConfig_strings *objname, struct NCDConfig_strings *names, struct NCDConfig_arguments *args, char *name, struct NCDConfig_statements *next)
+{
+    struct NCDConfig_statements *v = malloc(sizeof(*v));
+    if (!v) {
+        goto fail;
+    }
+    
+    v->objname = objname;
+    v->names = names;
+    v->args = args;
+    v->name = name;
+    v->next = next;
+
+    return v;
+    
+fail:
+    NCDConfig_free_strings(names);
+    NCDConfig_free_arguments(args);
+    free(name);
+    NCDConfig_free_statements(next);
+    return NULL;
+}
+
+struct NCDConfig_arguments * NCDConfig_make_arguments_string (char *str, struct NCDConfig_arguments *next)
+{
+    struct NCDConfig_arguments *v = malloc(sizeof(*v));
+    if (!v) {
+        goto fail;
+    }
+    
+    v->type = NCDCONFIG_ARG_STRING;
+    v->string = str;
+    v->next = next;
+    
+    return v;
+    
+fail:
+    free(str);
+    NCDConfig_free_arguments(next);
+    return NULL;
+}
+
+struct NCDConfig_arguments * NCDConfig_make_arguments_var (struct NCDConfig_strings *var, struct NCDConfig_arguments *next)
+{
+    struct NCDConfig_arguments *v = malloc(sizeof(*v));
+    if (!v) {
+        goto fail;
+    }
+    
+    v->type = NCDCONFIG_ARG_VAR;
+    v->var = var;
+    v->next = next;
+    
+    return v;
+    
+fail:
+    NCDConfig_free_strings(var);
+    NCDConfig_free_arguments(next);
+    return NULL;
+}
+
+struct NCDConfig_strings * NCDConfig_make_strings (char *value, int need_next, struct NCDConfig_strings *next)
+{
+    if (!value || (need_next && !next)) {
+        goto fail;
+    }
+    
+    struct NCDConfig_strings *v = malloc(sizeof(*v));
+    if (!v) {
+        goto fail;
+    }
+    
+    v->value = value;
+    v->next = next;
+    
+    return v;
+    
+fail:
+    free(value);
+    NCDConfig_free_strings(next);
+    return NULL;
+}
+
+int NCDConfig_statement_name_is (struct NCDConfig_statements *st, const char *needle)
+{
+    ASSERT(st->names)
+    
+    size_t l;
+    
+    struct NCDConfig_strings *name = st->names;
+    if (!(l = string_begins_with(needle, name->value))) {
+        return 0;
+    }
+    needle += l;
+    
+    name = name->next;
+    
+    while (name) {
+        if (!(l = string_begins_with(needle, "."))) {
+            return 0;
+        }
+        needle += l;
+        
+        if (!(l = string_begins_with(needle, name->value))) {
+            return 0;
+        }
+        needle += l;
+        
+        name = name->next;
+    }
+    
+    if (*needle) {
+        return 0;
+    }
+    
+    return 1;
+}
+
+struct NCDConfig_statements * NCDConfig_find_statement (struct NCDConfig_statements *st, const char *needle)
+{
+    while (st) {
+        if (NCDConfig_statement_name_is(st, needle)) {
+            return st;
+        }
+        
+        st = st->next;
+    }
+    
+    return NULL;
+}
+
+char * NCDConfig_concat_strings (struct NCDConfig_strings *s)
+{
+    ExpString str;
+    if (!ExpString_Init(&str)) {
+        goto fail0;
+    }
+    
+    if (!ExpString_Append(&str, s->value)) {
+        goto fail1;
+    }
+    
+    s = s->next;
+    
+    while (s) {
+        if (!ExpString_Append(&str, ".")) {
+            goto fail1;
+        }
+        if (!ExpString_Append(&str, s->value)) {
+            goto fail1;
+        }
+        
+        s = s->next;
+    }
+    
+    return ExpString_Get(&str);
+    
+fail1:
+    ExpString_Free(&str);
+fail0:
+    return NULL;
+}

+ 78 - 0
ncd/NCDConfig.h

@@ -0,0 +1,78 @@
+/**
+ * @file NCDConfig.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BADVPN_NCDCONFIG_NCDCONFIG_H
+#define BADVPN_NCDCONFIG_NCDCONFIG_H
+
+struct NCDConfig_interfaces;
+struct NCDConfig_statements;
+struct NCDConfig_arguments;
+struct NCDConfig_strings;
+
+struct NCDConfig_interfaces {
+    int is_template;
+    char *name;
+    struct NCDConfig_statements *statements;
+    struct NCDConfig_interfaces *next;
+};
+
+struct NCDConfig_statements {
+    struct NCDConfig_strings *objname;
+    struct NCDConfig_strings *names;
+    struct NCDConfig_arguments *args;
+    char *name;
+    struct NCDConfig_statements *next;
+};
+
+#define NCDCONFIG_ARG_STRING 1
+#define NCDCONFIG_ARG_VAR 2
+
+struct NCDConfig_arguments {
+    int type;
+    union {
+        char *string;
+        struct NCDConfig_strings *var;
+    };
+    struct NCDConfig_arguments *next;
+};
+
+struct NCDConfig_strings {
+    char *value;
+    struct NCDConfig_strings *next;
+};
+
+void NCDConfig_free_interfaces (struct NCDConfig_interfaces *v);
+void NCDConfig_free_statements (struct NCDConfig_statements *v);
+void NCDConfig_free_arguments (struct NCDConfig_arguments *v);
+void NCDConfig_free_strings (struct NCDConfig_strings *v);
+struct NCDConfig_interfaces * NCDConfig_make_interfaces (int is_template, char *name, struct NCDConfig_statements *statements, int have_next, struct NCDConfig_interfaces *next);
+struct NCDConfig_statements * NCDConfig_make_statements (struct NCDConfig_strings *objname, struct NCDConfig_strings *names, struct NCDConfig_arguments *args, char *name, struct NCDConfig_statements *next);
+struct NCDConfig_arguments * NCDConfig_make_arguments_string (char *str, struct NCDConfig_arguments *next);
+struct NCDConfig_arguments * NCDConfig_make_arguments_var (struct NCDConfig_strings *var, struct NCDConfig_arguments *next);
+struct NCDConfig_strings * NCDConfig_make_strings (char *value, int have_next, struct NCDConfig_strings *next);
+
+int NCDConfig_statement_name_is (struct NCDConfig_statements *st, const char *needle);
+struct NCDConfig_statements * NCDConfig_find_statement (struct NCDConfig_statements *st, const char *needle);
+
+char * NCDConfig_concat_strings (struct NCDConfig_strings *s);
+
+#endif

+ 169 - 0
ncd/NCDConfigParser.c

@@ -0,0 +1,169 @@
+/**
+ * @file NCDConfigParser.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/debug.h>
+#include <base/BLog.h>
+#include <ncd/NCDConfigTokenizer.h>
+
+#include <ncd/NCDConfigParser.h>
+
+#include <generated/blog_channel_NCDConfigParser.h>
+
+#include "../generated/NCDConfigParser_parse.c"
+#include "../generated/NCDConfigParser_parse.h"
+
+struct parser_state {
+    struct parser_out out;
+    int error;
+    void *parser;
+};
+
+static int tokenizer_output (void *user, int token, char *value, size_t position)
+{
+    struct parser_state *state = (struct parser_state *)user;
+    ASSERT(!state->out.out_of_memory)
+    ASSERT(!state->out.syntax_error)
+    ASSERT(!state->error)
+    
+    if (token == NCD_ERROR) {
+        BLog(BLOG_ERROR, "tokenizer error at %zu", position);
+        state->error = 1;
+        return 0;
+    }
+    
+    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_ROUND_OPEN: {
+            Parse(state->parser, ROUND_OPEN, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_ROUND_CLOSE: {
+            Parse(state->parser, ROUND_CLOSE, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_SEMICOLON: {
+            Parse(state->parser, SEMICOLON, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_DOT: {
+            Parse(state->parser, DOT, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_COMMA: {
+            Parse(state->parser, COMMA, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_ARROW: {
+            Parse(state->parser, ARROW, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_PROCESS: {
+            Parse(state->parser, PROCESS, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_TEMPLATE: {
+            Parse(state->parser, TEMPLATE, NULL, &state->out);
+        } break;
+        
+        case NCD_TOKEN_NAME: {
+            char *v = malloc(strlen(value) + 1);
+            if (!v) {
+                state->out.out_of_memory = 1;
+                break;
+            }
+            strcpy(v, value);
+            Parse(state->parser, NAME, v, &state->out);
+        } break;
+        
+        case NCD_TOKEN_STRING: {
+            char *v = malloc(strlen(value) + 1);
+            if (!v) {
+                state->out.out_of_memory = 1;
+                break;
+            }
+            strcpy(v, value);
+            Parse(state->parser, STRING, v, &state->out);
+        } break;
+        
+        default:
+            ASSERT(0);
+    }
+    
+    // if we got syntax error, stop parsing
+    if (state->out.syntax_error) {
+        BLog(BLOG_ERROR, "syntax error at %zu", position);
+        state->error = 1;
+        return 0;
+    }
+    
+    if (state->out.out_of_memory) {
+        BLog(BLOG_ERROR, "out of memory at %zu", position);
+        state->error = 1;
+        return 0;
+    }
+    
+    return 1;
+}
+
+int NCDConfigParser_Parse (char *config, size_t config_len, struct NCDConfig_interfaces **out_ast)
+{
+    struct parser_state state;
+    
+    state.out.out_of_memory = 0;
+    state.out.syntax_error = 0;
+    state.out.ast = NULL;
+    state.error = 0;
+    
+    if (!(state.parser = ParseAlloc(malloc))) {
+        BLog(BLOG_ERROR, "ParseAlloc failed");
+        return 0;
+    }
+    
+    // tokenize and parse
+    NCDConfigTokenizer_Tokenize(config, config_len, tokenizer_output, &state);
+    
+    if (state.error) {
+        ParseFree(state.parser, free);
+        NCDConfig_free_interfaces(state.out.ast);
+        return 0;
+    }
+    
+    ParseFree(state.parser, free);
+    
+    *out_ast = state.out.ast;
+    return 1;
+}

+ 32 - 0
ncd/NCDConfigParser.h

@@ -0,0 +1,32 @@
+/**
+ * @file NCDConfigParser.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BADVPN_NCDCONFIG_NCDCONFIGPARSER_H
+#define BADVPN_NCDCONFIG_NCDCONFIGPARSER_H
+
+#include <stddef.h>
+
+#include <ncd/NCDConfig.h>
+
+int NCDConfigParser_Parse (char *config, size_t config_len, struct NCDConfig_interfaces **out_ast);
+
+#endif

+ 187 - 0
ncd/NCDConfigParser_parse.y

@@ -0,0 +1,187 @@
+/**
+ * @file NCDConfigParser.y
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+%include {
+
+#include <string.h>
+#include <stddef.h>
+
+#include <misc/debug.h>
+#include <ncd/NCDConfig.h>
+
+struct parser_out {
+    int out_of_memory;
+    int syntax_error;
+    struct NCDConfig_interfaces *ast;
+};
+
+}
+
+%extra_argument {struct parser_out *parser_out}
+
+%token_type {void *}
+
+%token_destructor { free($$); }
+
+%type interfaces {struct NCDConfig_interfaces *}
+%type statements {struct NCDConfig_statements *}
+%type statement_names {struct NCDConfig_strings *}
+%type statement_args_maybe {struct NCDConfig_arguments *}
+%type statement_args {struct NCDConfig_arguments *}
+%type name_maybe {char *}
+%type process_or_template {int}
+
+%destructor interfaces { NCDConfig_free_interfaces($$); }
+%destructor statements { NCDConfig_free_statements($$); }
+%destructor statement_names { NCDConfig_free_strings($$); }
+%destructor statement_args_maybe { NCDConfig_free_arguments($$); }
+%destructor statement_args { NCDConfig_free_arguments($$); }
+%destructor name_maybe { free($$); }
+
+%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 ::= interfaces(A). {
+    parser_out->ast = A;
+
+    if (!A) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+interfaces(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE. {
+    R = NCDConfig_make_interfaces(T, A, B, 0, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+interfaces(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE interfaces(N). {
+    R = NCDConfig_make_interfaces(T, A, B, 1, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statements(R) ::= statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
+    R = NCDConfig_make_statements(NULL, A, B, C, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statements(R) ::= statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON statements(N). {
+    R = NCDConfig_make_statements(NULL, A, B, C, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statements(R) ::= statement_names(M) ARROW statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. {
+    R = NCDConfig_make_statements(M, A, B, C, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statements(R) ::= statement_names(M) ARROW statement_names(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON statements(N). {
+    R = NCDConfig_make_statements(M, A, B, C, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statement_names(R) ::= NAME(A). {
+    R = NCDConfig_make_strings(A, 0, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statement_names(R) ::= NAME(A) DOT statement_names(N). {
+    R = NCDConfig_make_strings(A, 1, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statement_args_maybe(R) ::= . {
+    R = NULL;
+}
+
+statement_args_maybe(R) ::= statement_args(A). {
+    R = A;
+}
+
+statement_args(R) ::= STRING(A). {
+    R = NCDConfig_make_arguments_string(A, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statement_args(R) ::= statement_names(A). {
+    R = NCDConfig_make_arguments_var(A, NULL);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statement_args(R) ::= STRING(A) COMMA statement_args(N). {
+    R = NCDConfig_make_arguments_string(A, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+statement_args(R) ::= statement_names(A) COMMA statement_args(N). {
+    R = NCDConfig_make_arguments_var(A, N);
+    if (!R) {
+        parser_out->out_of_memory = 1;
+    }
+}
+
+name_maybe(R) ::= . {
+    R = NULL;
+}
+
+name_maybe(R) ::= NAME(A). {
+    R = A;
+}
+
+process_or_template(R) ::= PROCESS. {
+    R = 0;
+}
+
+process_or_template(R) ::= TEMPLATE. {
+    R = 1;
+}

+ 202 - 0
ncd/NCDConfigTokenizer.c

@@ -0,0 +1,202 @@
+/**
+ * @file NCDConfigTokenizer.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <stddef.h>
+
+#include <misc/debug.h>
+#include <misc/string_begins_with.h>
+
+#include <ncd/NCDConfigTokenizer.h>
+
+static int is_name_char (char c)
+{
+    return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_');
+}
+
+static int is_name_first_char (char c)
+{
+    return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
+}
+
+static int is_space_char (char c)
+{
+    return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+}
+
+static int string_equals (char *str, int str_len, char *needle)
+{
+    return (str_len == strlen(needle) && !memcmp(str, needle, str_len));
+}
+
+void NCDConfigTokenizer_Tokenize (char *str, size_t left, NCDConfigTokenizer_output output, void *user)
+{
+    size_t position = 0;
+    
+    while (left > 0) {
+        size_t l;
+        int error = 0;
+        int token;
+        void *token_val = NULL;
+        char dec[NCD_MAX_SIZE + 1];
+        
+        if (*str == '#') {
+            l = 1;
+            while (l < left && str[l] != '\n') {
+                l++;
+            }
+            token = 0;
+        }
+        else if (l = data_begins_with(str, left, "{")) {
+            token = NCD_TOKEN_CURLY_OPEN;
+        }
+        else if (l = data_begins_with(str, left, "}")) {
+            token = NCD_TOKEN_CURLY_CLOSE;
+        }
+        else if (l = data_begins_with(str, left, "(")) {
+            token = NCD_TOKEN_ROUND_OPEN;
+        }
+        else if (l = data_begins_with(str, left, ")")) {
+            token = NCD_TOKEN_ROUND_CLOSE;
+        }
+        else if (l = data_begins_with(str, left, ";")) {
+            token = NCD_TOKEN_SEMICOLON;
+        }
+        else if (l = data_begins_with(str, left, ".")) {
+            token = NCD_TOKEN_DOT;
+        }
+        else if (l = data_begins_with(str, left, ",")) {
+            token = NCD_TOKEN_COMMA;
+        }
+        else if (l = data_begins_with(str, left, "->")) {
+            token = NCD_TOKEN_ARROW;
+        }
+        else if (is_name_first_char(*str)) {
+            l = 1;
+            while (l < left) {
+                if (!is_name_char(str[l])) {
+                    break;
+                }
+                l++;
+            }
+            
+            // check size
+            if (l > NCD_MAX_SIZE) {
+                error = 1;
+                goto out;
+            }
+            
+            // copy and terminate
+            memcpy(dec, str, l);
+            dec[l] = '\0';
+            
+            if (!strcmp(dec, "process")) {
+                token = NCD_TOKEN_PROCESS;
+            }
+            else if (!strcmp(dec, "template")) {
+                token = NCD_TOKEN_TEMPLATE;
+            }
+            else {
+                token = NCD_TOKEN_NAME;
+                token_val = dec;
+            }
+        }
+        else if (*str == '"') {
+            size_t dec_len = 0;
+            
+            // decode string on the fly
+            l = 1;
+            while (l < left) {
+                char dec_ch;
+                
+                if (str[l] == '\\') {
+                    if (!(l + 1 < left)) {
+                        error = 1;
+                        goto out;
+                    }
+                    
+                    dec_ch = str[l + 1];
+                    l += 2;
+                }
+                else if (str[l] == '"') {
+                    break;
+                }
+                else {
+                    dec_ch = str[l];
+                    l++;
+                }
+                
+                // check size
+                if (dec_len == NCD_MAX_SIZE) {
+                    error = 1;
+                    goto out;
+                }
+                
+                // append decoded char
+                dec[dec_len] = dec_ch;
+                dec_len++;
+            }
+            
+            // make sure closing quote was found
+            if (l == left) {
+                error = 1;
+                goto out;
+            }
+            
+            l++;
+            
+            // terminate
+            dec[dec_len] = '\0';
+            
+            token = NCD_TOKEN_STRING;
+            token_val = dec;
+        }
+        else if (is_space_char(*str)) {
+            token = 0;
+            l = 1;
+        }
+        else {
+            error = 1;
+        }
+        
+    out:
+        
+        // report error
+        if (error) {
+            output(user, NCD_ERROR, NULL, position);
+            return;
+        }
+        
+        // output token
+        if (token) {
+            if (!output(user, token, token_val, position)) {
+                return;
+            }
+        }
+        
+        str += l;
+        left -= l;
+        position += l;
+    }
+    
+    output(user, NCD_EOF, NULL, position);
+}

+ 47 - 0
ncd/NCDConfigTokenizer.h

@@ -0,0 +1,47 @@
+/**
+ * @file NCDConfigTokenizer.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BADVPN_NCDCONFIG_NCDCONFIGTOKENIZER_H
+#define BADVPN_NCDCONFIG_NCDCONFIGTOKENIZER_H
+
+#define NCD_ERROR -1
+#define NCD_EOF 0
+#define NCD_TOKEN_CURLY_OPEN 1
+#define NCD_TOKEN_CURLY_CLOSE 2
+#define NCD_TOKEN_ROUND_OPEN 3
+#define NCD_TOKEN_ROUND_CLOSE 4
+#define NCD_TOKEN_SEMICOLON 5
+#define NCD_TOKEN_DOT 6
+#define NCD_TOKEN_COMMA 7
+#define NCD_TOKEN_PROCESS 8
+#define NCD_TOKEN_NAME 9
+#define NCD_TOKEN_STRING 10
+#define NCD_TOKEN_ARROW 11
+#define NCD_TOKEN_TEMPLATE 12
+
+#define NCD_MAX_SIZE 128
+
+typedef int (*NCDConfigTokenizer_output) (void *user, int token, char *value, size_t position);
+
+void NCDConfigTokenizer_Tokenize (char *str, size_t str_len, NCDConfigTokenizer_output output, void *user);
+
+#endif

+ 1 - 1
ncd/ncd.c

@@ -42,7 +42,7 @@
 #include <system/BConnection.h>
 #include <system/BProcess.h>
 #include <udevmonitor/NCDUdevManager.h>
-#include <ncdconfig/NCDConfigParser.h>
+#include <ncd/NCDConfigParser.h>
 #include <ncd/NCDModule.h>
 #include <ncd/modules/modules.h>