--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/config/objconf.c Sun Jan 15 17:00:16 2012 +0100 @@ -0,0 +1,399 @@ +/* + * 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; +}