added new self-made config parser with new config style
[NeonServV5.git] / src / ConfigParser.c
diff --git a/src/ConfigParser.c b/src/ConfigParser.c
new file mode 100644 (file)
index 0000000..775e062
--- /dev/null
@@ -0,0 +1,316 @@
+/* ConfigParser.c - NeonServ v5.2
+ * Copyright (C) 2011  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program 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, see <http://www.gnu.org/licenses/>. 
+ */
+
+#include "ConfigParser.h"
+
+#define ENTRYTYPE_BLOCK   1
+#define ENTRYTYPE_STRING  2
+#define ENTRYTYPE_INTEGER 3
+
+struct ConfigEntry {
+    char *name;
+    int type;
+    void *value;
+    struct ConfigEntry *next;
+};
+
+static struct ConfigEntry *root_entry = NULL;
+
+static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element);
+static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry);
+
+int loadConfig(const char *filename) {
+    FILE *f;
+    f = fopen(filename, "rb");
+    if(!f) return 0;
+    
+    // obtain file size:
+    long lSize;
+    fseek (f, 0, SEEK_END);
+    lSize = ftell(f);
+    rewind(f);
+    
+    // allocate memory to contain the whole file:
+    char *buffer = (char*) malloc (sizeof(char)*lSize + 1);
+    if (buffer == NULL) return 0;
+    
+    // copy the file into the buffer:
+    size_t result = fread (buffer, 1, lSize, f);
+    if (result != lSize) return 0;
+    buffer[lSize] = '\0';
+    
+    // now parse the config file...
+    if(root_entry) {
+        free_loaded_config();
+    }
+    root_entry = malloc(sizeof(*root_entry));
+    if (!root_entry) return 0;
+    parse_config_recursive(root_entry, buffer, 1);
+    
+    // free the buffer...
+    free(buffer);
+    return 1;
+}
+
+#define PARSER_FLAG_ESCAPED    0x01
+#define PARSER_FLAG_BLOCK      0x02
+#define PARSER_FLAG_STRING     0x04
+#define PARSER_FLAG_EXPECT_END 0x08
+#define PARSER_FLAG_NEXTCHAR   0x10
+#define PARSER_FLAG_INTEGER    0x20
+#define PARSER_FLAG_EOBN       0x40 /* End of Block name */
+#define PARSER_FLAG_COMMAND    0x80
+static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element) {
+    int flags = 0;
+    int type = (root_element ? ENTRYTYPE_BLOCK : 0);
+    char cbuf[1024];
+    int cbufpos = 0;
+    struct ConfigEntry *sub_entrys = NULL;
+    while(*buffer) {
+        if(flags & PARSER_FLAG_NEXTCHAR) {
+            buffer++;
+            if(*buffer == '\0')
+                break;
+        }
+        flags |= PARSER_FLAG_NEXTCHAR;
+        if(flags & PARSER_FLAG_EOBN) {
+            flags &= ~(PARSER_FLAG_EOBN | PARSER_FLAG_NEXTCHAR);
+            struct ConfigEntry *new_entry = malloc(sizeof(*new_entry));
+            if (!new_entry) return buffer;
+            new_entry->name = strdup(cbuf);
+            buffer = parse_config_recursive(new_entry, buffer, 0);
+            if(sub_entrys)
+                new_entry->next = sub_entrys;
+            else
+                new_entry->next = NULL;
+            sub_entrys = new_entry;
+            centry->value = sub_entrys;
+        }
+        if(flags & PARSER_FLAG_ESCAPED) {
+            cbuf[cbufpos++] = *buffer;
+            flags &= ~PARSER_FLAG_ESCAPED;
+            continue;
+        }
+        if(flags & PARSER_FLAG_EXPECT_END) {
+            if(*buffer == ';') {
+                centry->type = type;
+                return (buffer+1);
+            }
+            continue;
+        }
+        if(flags & PARSER_FLAG_STRING) {
+            if(*buffer == '"') {
+                cbuf[cbufpos] = '\0';
+                flags &= ~PARSER_FLAG_STRING;
+                if(type == ENTRYTYPE_STRING) {
+                    flags |= PARSER_FLAG_EXPECT_END;
+                    centry->value = strdup(cbuf);
+                } else if(type == ENTRYTYPE_BLOCK) {
+                    flags |= PARSER_FLAG_EOBN;
+                }
+            } else if(*buffer == '\\')
+                flags |= PARSER_FLAG_ESCAPED;
+            else
+                cbuf[cbufpos++] = *buffer;
+            continue;
+        }
+        if(flags & PARSER_FLAG_INTEGER) {
+            if(!isdigit(*buffer)) {
+                cbuf[cbufpos] = '\0';
+                flags &= ~PARSER_FLAG_INTEGER;
+                if(type == ENTRYTYPE_INTEGER) {
+                    flags |= PARSER_FLAG_EXPECT_END;
+                    int *intbuf = malloc(sizeof(int));
+                    *intbuf = atoi(cbuf);
+                    centry->value = intbuf;
+                }
+                if(*buffer == ';') {
+                    centry->type = type;
+                    return (buffer+1);
+                }
+            } else
+                cbuf[cbufpos++] = *buffer;
+            continue;
+        }
+        if(flags & PARSER_FLAG_COMMAND) {
+            int found_command = 0;
+            switch(*buffer) {
+                case '/':
+                    buffer = strstr(buffer, "\n");
+                    found_command = 1;
+                    break;
+                case '*':
+                    //simple search for the next */
+                    buffer = strstr(buffer, "*/")+1;
+                    found_command = 1;
+            }
+            flags &= ~PARSER_FLAG_COMMAND;
+            if(found_command)
+                continue;
+        }
+        switch(*buffer) {
+            case '\\':
+                flags |= PARSER_FLAG_ESCAPED;
+                break;
+            case '/':
+                if(!(flags & PARSER_FLAG_STRING)) {
+                    flags |= PARSER_FLAG_COMMAND;
+                }
+                break;
+            case '{':
+                flags |= PARSER_FLAG_BLOCK;
+                type = ENTRYTYPE_BLOCK;
+                break;
+            case '}':
+                if(flags & PARSER_FLAG_BLOCK)
+                    flags &= ~PARSER_FLAG_BLOCK;
+                flags |= PARSER_FLAG_EXPECT_END;
+                break;
+            case '"':
+                flags |= PARSER_FLAG_STRING;
+                if(!type)
+                    type = ENTRYTYPE_STRING;
+                cbufpos = 0;
+                break;
+            case ';':
+                centry->type = type;
+                return (buffer+1);
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if(!type)
+                    type = ENTRYTYPE_INTEGER;
+                flags |= PARSER_FLAG_INTEGER;
+                cbufpos = 0;
+                cbuf[cbufpos++] = *buffer;
+                break;
+            default:
+                break;
+        }
+    }
+    centry->type = type;
+    return buffer; //end of the buffer
+}
+
+int get_int_field(char *field_path) {
+    struct ConfigEntry *centry = root_entry;
+    char *a, *b = field_path;
+    struct ConfigEntry *subentry;
+    while((a = strstr(b, ".")) && centry) {
+        if(centry->type == ENTRYTYPE_BLOCK) {
+            int found = 0;
+            for(subentry = centry->value; subentry; subentry = subentry->next) {
+                if(!stricmplen(subentry->name, b, a-b)) {
+                    centry = subentry;
+                    found = 1;
+                    break;
+                }
+            }
+            if(!found)
+                return 0;
+        } else
+            return 0;
+        b = a+1;
+    }
+    if(centry->type == ENTRYTYPE_BLOCK) {
+        int found = 0;
+        for(subentry = centry->value; subentry; subentry = subentry->next) {
+            if(!stricmp(subentry->name, b)) {
+                centry = subentry;
+                found = 1;
+                break;
+            }
+        }
+        if(!found)
+            return 0;
+    } else
+        return 0;
+    if(centry->type == ENTRYTYPE_INTEGER)
+        return ((int *)centry->value)[0];
+    else
+        return 0;
+}
+
+char *get_string_field(char *field_path) {
+    struct ConfigEntry *centry = root_entry;
+    char *a, *b = field_path;
+    struct ConfigEntry *subentry;
+    while((a = strstr(b, ".")) && centry) {
+        if(centry->type == ENTRYTYPE_BLOCK) {
+            int found = 0;
+            for(subentry = centry->value; subentry; subentry = subentry->next) {
+                if(!stricmplen(subentry->name, b, a-b)) {
+                    centry = subentry;
+                    found = 1;
+                    break;
+                }
+            }
+            if(!found)
+                return NULL;
+        } else
+            return NULL;
+        b = a+1;
+    }
+    if(centry->type == ENTRYTYPE_BLOCK) {
+        int found = 0;
+        for(subentry = centry->value; subentry; subentry = subentry->next) {
+            if(!stricmp(subentry->name, b)) {
+                centry = subentry;
+                found = 1;
+                break;
+            }
+        }
+        if(!found)
+            return NULL;
+    } else
+        return NULL;
+    if(centry->type == ENTRYTYPE_STRING)
+        return centry->value;
+    else
+        return NULL;
+}
+
+void free_loaded_config() {
+    if(root_entry) {
+        free_entry_rekursiv(root_entry, 1);
+        root_entry = NULL;
+    }
+}
+
+static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry) {
+    if(centry->type == ENTRYTYPE_BLOCK) {
+        struct ConfigEntry *subentry, *nextentry;
+        for(subentry = centry->value; subentry; subentry = nextentry) {
+            nextentry = subentry->next;
+            free_entry_rekursiv(subentry, 0);
+        }
+    } else if(centry->type == ENTRYTYPE_STRING) {
+        free(centry->value);
+    } else if(centry->type == ENTRYTYPE_INTEGER) {
+        free(centry->value);
+    }
+    if(!is_root_entry)
+        free(centry->name);
+    free(centry);
+}