src/server/config/objconf.c

Sun, 15 Jan 2012 17:00:16 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 15 Jan 2012 17:00:16 +0100
changeset 16
a9bbd82d2dce
child 17
d2a97bbeb57d
permissions
-rw-r--r--

New configuration file parser

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2011 Olaf Wintermann. All rights reserved.
 *
 * 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.
 *
 * 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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "objconf.h"

#include <string.h>

/* dev notes:
 *
 * to free ObjectConfig, free:
 *    line dlist
 *    mempool
 *    object
 */

ObjectConfig *load_object_config(char *file) {
    FILE *in = fopen(file, "r");
    if(in == NULL) {
        return NULL;
    }

    ObjectConfig *conf = malloc(sizeof(ObjectConfig));
    conf->parser.parse = objconf_parse;
    conf->file = file;

    int r = cfg_parse_basic_file((ConfigParser*)conf, in);
    if(r != 0) {
        // TODO: free
        return NULL;
    }

    return conf;
}



int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) {
    ObjectConfig *conf = p;

    begin->type = objconf_get_line_type(line);
    switch(begin->type) {
        case LINE_BEGIN_TAG: {
            ConfigTag *tag = objconf_parse_begin_tag(line, conf->parser.mp);
            if(tag == NULL) {
                fprintf(stderr, "Parse error!\n");
                exit(-1); // TODO: better error handling
            }
            tag->begin = begin;
            tag->end = end;
            tag->type_num = objconf_get_tag_type(tag->name);
            //printf("line {%s}\n", sstrdub(ll).ptr);
            if(objconf_on_begin_tag(conf, tag) != 0) {
                fprintf(stderr, "1error!\n");
                exit(-1);
            }
            break;
        }
        case LINE_END_TAG: {
            sstr_t tag = objconf_get_end_tag_name(line);
            if(objconf_on_end_tag(conf, tag) != 0) {
                fprintf(stderr, "2error!\n");
                exit(-1);
            }

            break;
        }
        case LINE_DIRECTIVE: {
            ConfigDirective *dir = objconf_parse_directive(
                    line,
                    conf->parser.mp);
            dir->begin = begin;
            dir->end = end;
            if(objconf_on_directive(conf, dir) != 0) {
                fprintf(stderr, "3error!\n");
                exit(-1);
            }
        }
    }
    return 0;
}

int objconf_on_begin_tag(ObjectConfig *conf, ConfigTag *tag) {
    if(tag->type_num != TAG_OBJECT) {
        ConfigParserLevel *l = conf->levels->data;
        if(l->tag->type_num != TAG_OBJECT) {
            tag->parent = l->tag;
        }
    }
    

    switch(tag->type_num) {
        case TAG_OBJECT: {
            ConfigObject *obj = OBJ_NEW_N(conf->parser.mp, ConfigObject);
            obj->begin = tag->begin;
            obj->end = tag->end;

            obj->name = cfg_param_get(tag->param, sstr("name"));
            obj->ppath = cfg_param_get(tag->param, sstr("ppath"));

            conf->obj = obj;
            conf->objects = ucx_dlist_append(conf->objects, obj);

            // create tree level object
            ConfigParserLevel *lvl = OBJ_NEW(conf->parser.mp, ConfigParserLevel);
            lvl->iftag = NULL;
            lvl->levelnum = 1;
            lvl->tag = tag;
            conf->levels = ucx_list_prepend(conf->levels, lvl);

            break;
        }
        case TAG_IF: {
            // create tree level object
            ConfigParserLevel *last_lvl = conf->levels->data;

            ConfigParserLevel *lvl = OBJ_NEW(conf->parser.mp, ConfigParserLevel);
            lvl->iftag = NULL;
            lvl->levelnum = last_lvl->levelnum + 1;
            lvl->tag = tag;
            conf->levels = ucx_list_prepend(conf->levels, lvl);
            last_lvl->iftag = tag;

            break;
        }
        case TAG_ELSEIF: {
        }
        case TAG_ELSE: {
            // create tree level object
            ConfigParserLevel *last_lvl = conf->levels->data;
            tag->iftag = last_lvl->iftag;

            ConfigParserLevel *lvl = OBJ_NEW(
                    conf->parser.mp,
                    ConfigParserLevel);
            
            lvl->iftag = last_lvl->tag;
            lvl->levelnum = last_lvl->levelnum + 1;
            lvl->tag = tag;
            conf->levels = ucx_list_prepend(conf->levels, lvl);

            break;
        }
        case TAG_CLIENT: {
            // create tree level object
            
            // TODO
            
            break;
        }
        default: {
            printf("unknown tag\n");
            return 1;
        }
    }

    return 0;
}

int objconf_on_end_tag(ObjectConfig *conf, sstr_t tagname) {
    int type = objconf_get_tag_type(tagname);
    if(type == -1) {
        fprintf(stderr, "unknown tag\n");
        return 1;
    } else {
        if(type == TAG_OBJECT) {
            conf->obj = NULL;
        }

        // remove level
        conf->levels = ucx_list_remove(conf->levels, conf->levels);
    }
    
    return 0;
}

int objconf_on_directive(ObjectConfig *conf, ConfigDirective *dir) {
    ConfigParserLevel *lvl = conf->levels->data;

    // check if we have a condition for the directive
    // if the level tag is not an object tag, use it as condition
    if(lvl->tag->type_num != TAG_OBJECT) {
        dir->condition = lvl->tag;
    }

    // add directive to current object
    conf->obj->directives[dir->type_num] = ucx_dlist_append(
            conf->obj->directives[dir->type_num],
            dir);

    return 0;
}

/*
 * checks if the line contains a begin/end tag or a directive
 */
int objconf_get_line_type(sstr_t line) {
    if(line.length < 3) {
        // this line is to short to be correct
        return LINE_ERROR;
    }
    
    if(line.ptr[0] == '<') {
        // start or end tag
        // TODO: check for space between '<' and '/'
        if(line.ptr[1] == '/') {
            return LINE_END_TAG;
        } else {
            return LINE_BEGIN_TAG;
        }
    } else {
        return LINE_DIRECTIVE;
    }
}

ConfigTag* objconf_parse_begin_tag(sstr_t line, UcxMempool *mp) {
    if(line.length < 4) {
        return NULL; // this line can't contain a valid tag
    }

    if(line.ptr[0] != '<' || line.ptr[line.length - 1] != '>') {
        return NULL; // syntax error
    }

    sstr_t name;
    name.ptr = line.ptr + 1;
    int i;
    for(i=1;i<line.length - 1;i++) {
        if(line.ptr[i] < 33) { // char is space
            name.length = i - 1;
            break;
        }
    }
    if(name.length < 1) {
        return NULL; // syntax error
    }

    // create tag object
    ConfigTag *tag = OBJ_NEW(mp, ConfigTag);
    tag->name = sstrdub_mp(mp, name);
    tag->param = NULL;

    // parse parameters
    sstr_t param_str;
    param_str.ptr = line.ptr + i;
    param_str.length = line.length - name.length - 2;
    param_str = sstrtrim(param_str);
    if(param_str.length <= 0) {
        return tag; // no parameters
    }

    sstr_t pname;
    sstr_t pvalue;
    for(;;) {
        param_str = cfg_param(param_str, &pname, &pvalue);
        if(pname.length <= 0) {
            break;
        }

        // create param object
        ConfigParam *param = OBJ_NEW(mp, ConfigParam);
        param->name = sstrdub_mp(mp, pname);
        if(pvalue.length > 0) {
            param->value = sstrdub_mp(mp, pvalue);
        } else {
            param->value.ptr = NULL;
            param->value.length = 0;
        }

        // add param to list
        tag->param = ucx_list_append(tag->param, param);
    }

    return tag;
}

/*
 * returns the name of the ending tag
 * on error, this functions returns a zero length string
 */
sstr_t objconf_get_end_tag_name(sstr_t line) {
    sstr_t ns;
    ns.ptr = NULL;
    ns.length = 0;

    if(line.length < 4) {
        // minimum of 4 chars: </a>
        return ns;
    }

    sstr_t name;
    name.ptr = line.ptr + 2;
    name.length = line.length - 3;

    // check for </ > frame
    if(line.ptr[0] != '<'
            || line.ptr[1] != '/'
            || line.ptr[line.length - 1] != '>')
    {
        return ns;
    }

    return sstrtrim(name);
}

/*
 * parses a line containing a directive and returns a ConfigDirective object
 * or NULL if an error occurs
 */
ConfigDirective* objconf_parse_directive(sstr_t line, UcxMempool *mp) {
    if(line.length < 6) {
        return NULL; // line too short
    }

    sstr_t name;

    int i;
    for(i=0;i<line.length;i++) {
        if(line.ptr[i] < 33) {
            break;
        }
    }
    name.ptr = line.ptr;
    name.length = i;

    // create directive object
    ConfigDirective *directive = OBJ_NEW(mp, ConfigDirective);
    directive->directive_type = sstrdub_mp(mp, name);
    directive->type_num = cfg_get_directive_type_num(name);
    directive->condition = NULL; // set later by main parsing function
    directive->param = NULL;

    sstr_t param_str;
    param_str.ptr = name.ptr + i;
    param_str.length = line.length - i;
    param_str = sstrtrim(param_str);
    sstr_t pname;
    sstr_t pvalue;
    for(;;) {
        param_str = cfg_param(param_str, &pname, &pvalue);
        if(pname.length <= 0) {
            break;
        }

        // create param object
        ConfigParam *param = OBJ_NEW(mp, ConfigParam);
        param->name = sstrdub_mp(mp, pname);
        if(pvalue.length > 0) {
            param->value = sstrdub_mp(mp, pvalue);
        } else {
            param->value.ptr = NULL;
            param->value.length = 0;
        }

        // add param to list
        directive->param = ucx_list_append(directive->param, param);
    }

    return directive;
}

int objconf_get_tag_type(sstr_t tag) {
    if(!sstrcmp(tag, sstr("Object"))) {
        return TAG_OBJECT;
    } else if(!sstrcmp(tag, sstr("If"))) {
        return TAG_IF;
    } else if(!sstrcmp(tag, sstr("ElseIf"))) {
        return TAG_ELSEIF;
    } else if(!sstrcmp(tag, sstr("Else"))) {
        return TAG_ELSE;
    } else if(!sstrcmp(tag, sstr("Client"))) {
        return TAG_CLIENT;
    }
    return -1;
}

mercurial