Sun, 15 Jan 2012 17:00:16 +0100
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; }