/* Parser.cc

Copyright © 2003 David Reveman.

This file is part of Waimea.

Waimea 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 2, or (at your option) any later
version.

Waimea 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 Waimea; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */

#ifdef    HAVE_CONFIG_H
#  include "../config.h"
#endif // HAVE_CONFIG_H

extern "C" {
#include <X11/Xlib.h>

#ifdef    STDC_HEADERS
#  include <stdlib.h>
#  include <string.h>
#  include <stdarg.h>
#endif // STDC_HEADERS

#ifdef    HAVE_SYS_TYPES_H
#  include <sys/types.h>
#endif // HAVE_SYS_TYPES_H    
    
#ifdef    HAVE_SYS_STAT_H
#  include <sys/stat.h>
#endif // HAVE_SYS_STAT_H
    
#ifdef    HAVE_UNISTD_H
#  include <unistd.h>
#endif // HAVE_UNISTD_H

#ifdef    HAVE_SYS_WAIT_H
#  include <sys/wait.h>
#endif // HAVE_SYS_WAIT_H

#ifdef    HAVE_FCNTL_H
#  include <fcntl.h>
#endif // HAVE_FCNTL_H
    
}

#include "Parser.hh"
#include "Util.hh"
#include "Action.hh"
#include "Style.hh"
#include "Screen.hh"

Tst<int>           *win_state_tst;
Tst<long int>      *x11_modifier_tst;
Tst<long int>      *wa_modifier_tst;
Tst<int>           *dynamic_try_tst;
Tst<ParserElement> *cfg_element_tst;
Tst<ParserElement> *actionlist_element_tst;
Tst<ParserElement> *action_element_tst;
Tst<ParserElement> *menu_element_tst;
Tst<ParserElement> *menuitem_element_tst;
Tst<ParserElement> *rendergroup_element_tst;
Tst<ParserElement> *renderopdynamic_element_tst;
Tst<ParserElement> *renderpattern_element_tst;
Tst<ParserElement> *renderpath_element_tst;
Tst<ParserElement> *rendertext_element_tst;

static void parser_start_element(void *userdata, const XML_Char *name,
                                 const XML_Char **attr) {
    Parser *parser;
    ElementHandler *handler;
    ParserElement element;

    parser = (Parser *) userdata;

    if (parser->unknown_element_depth) {
        parser->unknown_element_depth++;
        return;
    }

    if (! parser->waimea_element_found) {
        if ( ! strcmp((char *) name, "waimea")) {
            Tst<char *> *attr_tst = new Tst<char *>(attr);
            char *version = parser->attrGetString(attr_tst, "version", NULL);
            if (version) {
                if (strcmp(version, VERSION) < 0)
                    parser->warning("detected an old configuration file "
                                    "version %s. waimea version is " VERSION
                                    "\n", version);
                else if (strcmp(version, VERSION) > 0)
                    parser->warning("detected an old version of waimea "
                                    VERSION ". configuration file version is "
                                    "%s\n", version);
            }
            parser->waimea_element_found = true;
            delete attr_tst;
            return;
        }
    }

    handler = parser->element_handler_stack.back();
    if (handler) {
        element = handler->elementMap(name);
        if (element == ParserElementNone) {
            parser->unknown_element_depth++;
        } else {
            Tst<char *> *attr_trie = new Tst<char *>(attr);
            if (! handler->startElement(element, name, attr_trie))
                parser->unknown_element_depth++;
            delete attr_trie;
        }
    }
}

static void parser_end_element(void *userdata, const XML_Char *name) {
    Parser *parser;
    ElementHandler *handler;

    parser = (Parser *) userdata;

    if (parser->unknown_element_depth) {
        parser->unknown_element_depth--;
        return;
    }

    handler = parser->element_handler_stack.back();
    if (handler) {
        handler->endElement(name);
        parser->popElementHandler();
    }
}

static void parser_characters(void *userdata, const XML_Char *chars, int len) {
    Parser *parser;
    ElementHandler *handler;

    parser = (Parser *) userdata;
    
    handler = parser->element_handler_stack.back();

    if (! len || ! handler) return;

    handler->characters(chars, len);
}

static void parser_start_doctype_decl(void *userdata,
                                      const XML_Char *doctypename,
                                      const XML_Char *,
                                      const XML_Char *, int) {
    Parser *parser = (Parser *) userdata;

    if (strcmp((char *) doctypename, "waimea") != 0)
        parser->warning("invalid doctype: %s", doctypename);
}

static void parser_end_doctype_decl(void *) {}

StringBuffer::~StringBuffer(void) {
    while (! strbuf.empty()) {
        delete [] strbuf.back();
        strbuf.pop_back();
    }
}

void StringBuffer::addString(char *chars, int len) {
    if (len < 1) return;
    strbuf_length += (len);
    char *substr = new char[len + 1];
    strncpy(substr, chars, len);
    substr[len] = '\0';
    strbuf.push_back(substr);
}

char *StringBuffer::getString(void) {
    char *str = new char[strbuf_length + 1];
    char *str_ptr = str;
    str[strbuf_length] = '\0';
    list<char *>::iterator it = strbuf.begin();
    for (; it != strbuf.end(); it++)
        str_ptr += sprintf(str_ptr, "%s", *it);
    return str;
}

Parser::Parser(WaScreen *_ws, int _include_depth, bool cr_xml_parser) {
    if (cr_xml_parser) {
        xml_parser = XML_ParserCreate("UTF-8");
        if (! xml_parser) {
            ERROR_FUNC << "failed to create xml parser context" << endl;
            exit(1);
        }
        XML_SetDoctypeDeclHandler(xml_parser,
                                  parser_start_doctype_decl,
                                  parser_end_doctype_decl);
        XML_SetElementHandler(xml_parser,
                              parser_start_element,
                              parser_end_element);
        XML_SetCharacterDataHandler(xml_parser, parser_characters);
        XML_SetUserData(xml_parser, this);
    } else
        xml_parser = NULL;
    
    include_depth = _include_depth;
    unknown_element_depth = 0;
    filename = WA_STRDUP("");
    waimea_element_found = false;
    ws = _ws;
}

Parser::~Parser(void) {
    char *warnings = strbuf.getString();
    if (*warnings != '\0')
        ws->showWarningMessage(__FUNCTION__, warnings);

    delete [] warnings;
    
    if (xml_parser)
        XML_ParserFree(xml_parser);
    
    while (! element_handler_stack.empty()) {
        element_handler_stack.back()->unref();
        element_handler_stack.pop_back();
    }
    delete [] filename;

    Tst<char *>::iterator it = constants.begin();
    for (; it != constants.end(); it++)
        delete [] *it;
}

void Parser::pushElementHandler(ElementHandler *handler) {
    element_handler_stack.push_back(handler);
}

void Parser::popElementHandler(void) {
    if (! element_handler_stack.empty()) {
        element_handler_stack.back()->unref();
        element_handler_stack.pop_back();
    }
}

void Parser::setFilename(const char *name) {
    delete [] filename;
    filename = WA_STRDUP((char *) name);
}

bool Parser::parseChunk(const char *data, int len) {
    if (! xml_parser) return false;    
    
    if (! XML_Parse(xml_parser, data, len, len == 0)) {
        warning("%s", XML_ErrorString(XML_GetErrorCode(xml_parser)));
        return false;
    }
        
    return true;
}

bool Parser::parseChunkEnd(void) {
    if (! xml_parser) return false;

    if (! XML_Parse(xml_parser, NULL, 0, true)) {
        warning("%s", XML_ErrorString(XML_GetErrorCode(xml_parser)));
        return false;
    }
    
    return true;
}

bool Parser::parseFile(const char *file, bool ignore_missing) {
    if (! xml_parser) return false;
    
    FILE *fd = fopen(file, "r");   
    if (fd == NULL) {
        if (! ignore_missing) {
            warning("%s: %s", file, strerror(errno));
        }
    } else {
        int len;
        delete [] filename;
        filename = WA_STRDUP((char *) file);
        do {
            void *buf = XML_GetBuffer(xml_parser, PARSE_BUFFER_SIZE);
            if (! buf) {
                WARNING_FUNC << "cannot get parse buffer" << endl;
                return false;
            }
            len = fread(buf, 1, PARSE_BUFFER_SIZE, fd);
            if (len < 0) {
                WARNING_FUNC << "failed reading config file" << endl;
                return false;
            }
            if (! XML_ParseBuffer(xml_parser, len, len == 0)) {
                warning("%s", XML_ErrorString(XML_GetErrorCode(xml_parser)));
                return false;
            }
        } while (len != 0);

        fclose(fd);
        
        return true;
    }
    return false;
}

bool Parser::parseCommand(const char *command, bool ignore_missing) {
    int m_pipe[2];
    int pid, waitstatus;
    struct sigaction action;
    FILE *fd;
    bool status = false;
    
    if (! xml_parser) return false;    

    if (pipe(m_pipe) < 0) {
        WARNING_FUNC; perror("pipe");
        goto bail;
    }
        
    action.sa_handler = SIG_DFL;
    action.sa_mask = sigset_t();
    action.sa_flags = 0;
    sigaction(SIGCHLD, &action, NULL);
    pid = fork();
    if (pid == 0) {
        dup2(m_pipe[1], STDOUT_FILENO);
        close(m_pipe[0]);
        close(m_pipe[1]);
        putenv(waimea_pathenv);
        if (execl("/bin/sh", "/bin/sh", "-c", command, NULL) < 0)
            WARNING_FUNC; perror("/bin/sh");
        close(STDOUT_FILENO);
        exit(127);
    }
    close(m_pipe[1]);

    fd = fdopen(m_pipe[0], "r");
        
    if (fd == NULL) {
        WARNING_FUNC; perror("fdopen");
    } else {
        int len;
            
        if (filename) delete [] filename;
        filename = WA_STRDUP("STDOUT");
        
        do {
            void *buf = XML_GetBuffer(xml_parser, PARSE_BUFFER_SIZE);
            if (! buf) {
                WARNING_FUNC << "cannot get parse buffer" << endl;
                goto bail;
            }
            len = fread(buf, 1, PARSE_BUFFER_SIZE, fd);
            if (len < 0) {
                WARNING_FUNC << "failed reading command output" << endl;
                goto bail;
            }
            if (! XML_ParseBuffer(xml_parser, len, len == 0)) {
                warning("%s", XML_ErrorString(XML_GetErrorCode(xml_parser)));
                goto bail;
            }
        } while (len != 0);
        
        action.sa_handler = signalhandler;
        action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
        sigaction(SIGCHLD, &action, NULL);
        
        if (waitpid(pid, &waitstatus, 0) == -1) {
            WARNING_FUNC; perror("waitpid");
        } else if (waitstatus) {
            if (! ignore_missing) {
                warning("execution failed");
            }
        }
        
        status = true;
    }
    
 bail:
    return status;
}

void Parser::warning(const char *msg, ...) {
    va_list args;
    char message[8192];
    char linenr[32];
    char *line;
    int len;
    
    va_start(args, msg);
    vsnprintf(message, 8192, msg, args);
    va_end(args);

    snprintf(linenr, 32, "%d", XML_GetCurrentLineNumber(xml_parser));
    len = strlen(filename) + strlen(linenr) + strlen(message) + 5;
    line = new char[len + 1];
    sprintf(line, "%s: %s: %s", filename, linenr, message);
    if (strbuf.strbuf.empty()) {
        char *error_head = "The following parsing errors occured:\n";
        strbuf.addString(error_head, strlen(error_head));
    }
    strbuf.addString(line, len);
    
    delete [] line;
}

char *Parser::attrGetString(Tst<char *> *attr, char *name,
                            char *default_value) {
    if (attr) {
        Tst<char *>::iterator it = attr->find(name);
        if (it != attr->end()) return attrValue(*it);
    }
    return default_value;
}

bool Parser::attrGetBool(Tst<char *> *attr, char *name, bool default_value) {
    if (attr) {
        Tst<char *>::iterator it = attr->find(name);
        if (it != attr->end()) {
            if (! strcasecmp(attrValue(*it), "true")) return true;
            else return false;
        }
    }
    return default_value;
}

double Parser::attrGetDouble(Tst<char *> *attr, char *name,
                             double default_value) {
    if (attr) {
        Tst<char *>::iterator it = attr->find(name);
        if (it != attr->end())
            return strtod(attrValue(*it), NULL);
    }
    return default_value;
}

int Parser::attrGetInt(Tst<char *> *attr, char *name, int default_value) {
    if (attr) {
        Tst<char *>::iterator it = attr->find(name);
        if (it != attr->end())
            return (int) strtol(attrValue(*it), NULL, 0);
    }
    return default_value;
}

unsigned int Parser::attrGetUint(Tst<char *> *attr, char *name,
                                 unsigned int default_value) {
    if (attr) {
        Tst<char *>::iterator it = attr->find(name);
        if (it != attr->end())    
            return (unsigned int) strtol(attrValue(*it), NULL, 0);
    }
    return default_value;
}

double Parser::attrGetLength(Tst<char *> *attr, char *name,
                             double default_value, LenghtUnitType *unit) {
    if (attr) {
        Tst<char *>::iterator it = attr->find(name);
        if (it != attr->end())
            return get_double_and_unit(attrValue(*it), unit);
    }
    return default_value;
}

void Parser::attrGetWindowregexContent(Tst<char *> *attr, WindowRegex *wreg) {
    char *value = attrGetString(attr, "name", NULL);
    if (value) wreg->addIDRegex(WindowIDName, value);
    value = attrGetString(attr, "class", NULL);
    if (value) wreg->addIDRegex(WindowIDClass, value);
    value = attrGetString(attr, "classname", NULL);
    if (value) wreg->addIDRegex(WindowIDClassName, value);
    value = attrGetString(attr, "pid", NULL);
    if (value) wreg->addIDRegex(WindowIDPID, value);
    value = attrGetString(attr, "host", NULL);
    if (value) wreg->addIDRegex(WindowIDHost, value);
    value = attrGetString(attr, "resourceid", NULL);
    if (value) wreg->addIDRegex(WindowIDWinID, value);
}

char *Parser::attrValue(char *value) {
    Tst<char *>::iterator it = constants.find(value);
    if (it != constants.end()) {
        return *it;
    } else {
        it = ws->constants.find(value);
        if (it != ws->constants.end())
            return *it;
    }
    return value;
}

ElementHandler::ElementHandler(Parser *_parser) :
    RefCounted<ElementHandler>(this) {
    parser = _parser;
}

IncludeElementHandler::IncludeElementHandler(Parser *_parser,
                                             ElementHandler *_handler,
                                             bool _ignore_missing) :
    ElementHandler(_parser) {
    previous_handler = _handler;
    ignore_missing = _ignore_missing;
}

void IncludeElementHandler::characters(const XML_Char *chars, int len) {
    strbuf.addString((char *) chars, len);
}

void IncludeElementHandler::endElement(const XML_Char *) {
    char *fname = strbuf.getString();

    if (parser->include_depth >= MAX_INCLUDE_DEPTH) {
        parser->warning("%s: cannot be included because maximum include "
                        "depth of %d reached", fname, MAX_INCLUDE_DEPTH);
        delete [] fname;
        return;
    }

    if (*fname == '|') {
        Parser *command_parser = new Parser(parser->ws,
                                            parser->include_depth + 1);
        previous_handler->parser = command_parser;
        command_parser->pushElementHandler(previous_handler->ref());
        command_parser->parseCommand(fname + 1, ignore_missing);
        previous_handler->parser = parser;
        
        delete command_parser;
    } else {
        char *filename = smartfile(fname, parser->filename, false);

        if (filename) {
            Parser *include_parser = new Parser(parser->ws,
                                                parser->include_depth + 1);
            previous_handler->parser = include_parser;
            include_parser->pushElementHandler(previous_handler->ref());
            include_parser->parseFile(filename, ignore_missing);
            previous_handler->parser = parser;
            
            delete include_parser;
            delete [] filename;
        } else {
            if (! ignore_missing)
                parser->warning("unable to open file: %s\n", fname);
        }
    }

    if (fname) delete [] fname;
}

InheritElementHandler::InheritElementHandler(CfgElementHandler *_cfg_handler,
                                             Tst<char *> *attr) :
    ElementHandler(_cfg_handler->parser) {
    ignore_missing = parser->attrGetBool(attr, "ignore_missing", false);
}

void InheritElementHandler::characters(const XML_Char *chars, int len) {
    strbuf.addString((char *) chars, len);
}

static const ParserElementMap cfg_element_map[] = {
    { "constant", ParserElementConstant },
    { "include", ParserElementInclude },
    { "do", ParserElementDo },
    { "actionlist", ParserElementActionList },
    { "match", ParserElementMatch },
    { "menu", ParserElementMenu },
    { "style", ParserElementStyle },
    { "group", ParserElementRenderGroup },
    { "path", ParserElementRenderPath },
    { "pattern", ParserElementRenderPattern },
    { "text", ParserElementRenderText }
};

ParserElement CfgElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it = cfg_element_tst->find((char *) name);
    if (it != cfg_element_tst->end()) return *it;
    else return ParserElementNone;
}

static const struct WindowStateMap {
    char *name;
    int state;
} win_state_map[] = {
    { "passive", WIN_STATE_PASSIVE },
    { "active", WIN_STATE_ACTIVE },
    { "hover", WIN_STATE_HOVER },
    { "pressed", WIN_STATE_PRESSED }
};

bool CfgElementHandler::startElement(ParserElement element,
                                     const XML_Char *, Tst<char *> *attr) {
    switch (element) {
        case ParserElementConstant: {
            char *cname = parser->attrGetString(attr, "name", NULL);
            char *cvalue = parser->attrGetString(attr, "value", NULL);
            if (cname && cvalue) {
                Tst<char *> *cons = &parser->ws->constants;
                char *storage = parser->attrGetString(attr, "scope", NULL);
                if (storage && ((! strcmp(storage, "local"))))
                    cons = &parser->constants;
                
                Tst<char *>::iterator it = cons->find(cname);
                if (it == cons->end())
                    cons->insert(cname, WA_STRDUP(cvalue));
                
            } else {
                parser->warning("required attribute=name or attribute=value "
                                "is missing\n");
            }
        } break;
        case ParserElementInclude: {
            bool ignore_missing = parser->attrGetBool(attr, "ignore_missing",
                                                      false);
            IncludeElementHandler *handler = new
                IncludeElementHandler(parser, this, ignore_missing);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementDo: {
            Doing *doing = new Doing(parser->ws);
            doing->applyAttributes(parser, attr);
            parser->ws->waimea->eh->doings.push_back(doing);
        } break;
        case ParserElementActionList: {
            ActionList *actionlist = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                actionlist = parser->ws->getActionListNamed(name, false);
                if (actionlist) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        actionlist->clear();
                } else {
                    actionlist = new ActionList(parser->ws, name);
                    parser->ws->actionlists.insert(actionlist->name,
                                                   actionlist);
                }
            } else {
                parser->warning("required attribute=name is missing\n");
                return false;
            }
            
            ActionListElementHandler *handler =
                new ActionListElementHandler(this, actionlist);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementMenu: {
            Menu *menu = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                menu = parser->ws->getMenuNamed(name, false);
                if (menu) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        menu->clear();
                } else {
                    menu = new Menu(parser->ws, name);
                    menu->commonStyleUpdate();
                    parser->ws->menus.insert(menu->name, menu);
                }
                menu->applyAttributes(parser, attr);
            } else {
                parser->warning("required attribute=name is missing\n");
                return false;
            }
            
            MenuElementHandler *handler = new MenuElementHandler(this, menu);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderGroup: {
            RenderGroup *rendergroup = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                rendergroup = parser->ws->getRenderGroupNamed(name, false);
                if (rendergroup) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        rendergroup->clear();
                } else {
                    rendergroup = new RenderGroup(parser->ws, name);
                    parser->ws->rendergroups.insert(rendergroup->name,
                                                    rendergroup);
                }
            } else {
                parser->warning("group without attribute=name outside of a "
                                "style tag does not make any sense\n");
                return false;
            }

            rendergroup->applyAttributes(parser, attr);
            
            RenderGroupElementHandler *handler =
                new RenderGroupElementHandler(this, rendergroup, NULL);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementStyle: {
            Style *style = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                style = parser->ws->getStyleNamed(name, false);
                if (style) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        style->clear();
                } else {
                    style = new Style(parser->ws, name);
                    parser->ws->styles.insert(style->name, style);
                    parser->ws->rendergroups.insert(style->name,
                                                    (Style *) style->ref());
                }
            } else {
                parser->warning("required attribute=name is missing\n");
                return false;
            }
            
            style->applyAttributes(parser, attr);
            
            RenderGroupElementHandler *handler =
                new RenderGroupElementHandler(this, (RenderGroup *) style,
                                              NULL);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderPattern: {
            RenderPattern *pattern = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                pattern = parser->ws->getPatternNamed(name, false);
                if (pattern) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        pattern->clear();
                } else {
                    pattern = new RenderPattern();
                    parser->ws->patterns.insert(name, pattern);
                }
            } else {
                parser->warning("path without attribute=name\n");
                return false;
            }

            pattern->applyAttributes(parser, attr);
            
            RenderPatternElementHandler *handler =
                new RenderPatternElementHandler(this, pattern);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderPath: {
            RenderOpPath *path = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                path = parser->ws->getPathNamed(name, false);
                if (path) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        path->clear();
                } else {
                    path = new RenderOpPath(name);
                    parser->ws->paths.insert(path->name, path);
                }
            } else {
                parser->warning("path without attribute=name outside of a "
                                "group or style tag does not make any "
                                "sense\n");
                return false;
            }

            path->applyAttributes(parser, attr);
            
            RenderPathElementHandler *handler =
                new RenderPathElementHandler(this, path, NULL);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderText: {
            RenderOpText *text = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                text = parser->ws->getTextNamed(name, false);
                if (text) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        text->clear();
                } else {
                    text = new RenderOpText(name);
                    parser->ws->texts.insert(text->name,
                                             (RenderOpText *) text->ref());
                }
            } else {
                parser->warning("text object without attribute=name "
                                "outside of a group or style tag does not "
                                "make any sense\n");
                return false;
            }
            
            text->applyAttributes(parser, attr);
            
            RenderOpTextElementHandler *handler =
                new RenderOpTextElementHandler(this, text, NULL);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementMatch: {
            char *a_name = parser->attrGetString(attr, "actionlist", NULL);
            char *s_name = parser->attrGetString(attr, "style", NULL);
            if (a_name || s_name) {
                list<ActionRegex *> *a_tolist = NULL;
                list<StyleRegex *> *s_tolist = NULL;
                char *t = parser->attrGetString(attr, "target", "window");
                if (t) {
                    if (a_name) a_tolist = parser->ws->getRegexActionList(t);
                    if (s_name) s_tolist = parser->ws->getRegexStyleList(t);
                    if (a_tolist || s_tolist) {
                        int state = WIN_STATE_PASSIVE;
                        char *s = parser->attrGetString(attr, "state", NULL);
                        if (s) {
                            Tst<int>::iterator it = win_state_tst->find(s);
                            if (it != win_state_tst->end()) state = *it;
                        }

                        char *w = parser->attrGetString(attr, "window", NULL);
                        
                        if (a_tolist) {
                            ActionRegex *areg = new ActionRegex(state, w);
                            parser->attrGetWindowregexContent(attr, areg);
                            areg->actionlist =
                                parser->ws->getActionListNamed(a_name, false);
                            if (! areg->actionlist) {
                                parser->warning("undefined actionlist: %s\n",
                                                a_name);
                                delete areg;
                            } else
                                a_tolist->push_front(areg);
                        }
                        if (s_tolist) {
                            StyleRegex *sreg = new StyleRegex(state, w);
                            parser->attrGetWindowregexContent(attr, sreg);
                            sreg->style =
                                parser->ws->getStyleNamed(s_name, false);
                            if (! sreg->style) {
                                parser->warning("undefined style: %s\n",
                                                s_name);
                                delete sreg;
                            } else
                                s_tolist->push_front(sreg);
                        }                        
                    }
                } else
                    parser->warning("missing required attribute=target\n");
            }
        } break;
        default:
            break;
    }
    
    return false;
}

ActionListElementHandler::ActionListElementHandler(
    CfgElementHandler *_cfg_handler, ActionList *_actionlist)
    : ElementHandler(_cfg_handler->parser) {
    actionlist = _actionlist;
    cfg_handler = _cfg_handler;
}

static const ParserElementMap actionlist_element_map[] = {
    { "inherit", ParserElementInherit },
    { "action", ParserElementAction }
};

ParserElement ActionListElementHandler::elementMap(const XML_Char *name) {
    if (! actionlist) return ParserElementNone;
    Tst<ParserElement>::iterator it =
        actionlist_element_tst->find((char *) name);
    if (it != actionlist_element_tst->end()) return *it;
    else return ParserElementNone;
}

bool ActionListElementHandler::startElement(ParserElement element,
                                            const XML_Char *,
                                            Tst<char *> *attr) {
    switch (element) {
        case ParserElementAction: {
            Action *action = new Action();
            action->applyAttributes(parser, attr);
            ActionElementHandler *handler =
                new ActionElementHandler(cfg_handler, actionlist, action);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementInherit: {
            ActionListInheritElementHandler *handler = new
                ActionListInheritElementHandler(cfg_handler, actionlist, attr);
            parser->pushElementHandler(handler);
            return true;
        } break;
        default:
            break;
    }
    return false;
}

void ActionListElementHandler::endElement(const XML_Char *) {
    parser->ws->propagateActionlistUpdate(actionlist);
}

ActionListInheritElementHandler::ActionListInheritElementHandler(
    CfgElementHandler *_cfg_handler, ActionList *_actionlist,
    Tst<char *> *attr) : InheritElementHandler(_cfg_handler, attr) {
    actionlist = _actionlist;
}

void ActionListInheritElementHandler::endElement(const XML_Char *) {
    char *actionlistname = strbuf.getString();
    
    ActionList *inherit_list = parser->ws->getActionListNamed(actionlistname,
                                                              false);

    if (inherit_list == actionlist) {
        parser->warning("actionlist %s cannot inherit itself\n",
                        actionlist->name);
    } else if (inherit_list) {
        actionlist->inheritContent(inherit_list);
    } else {
        if (! ignore_missing)
            parser->warning("undefined actionlist: %s\n", actionlistname);
    }
    
    delete [] actionlistname;
}

ActionElementHandler::ActionElementHandler(
    CfgElementHandler *_cfg_handler, ActionList *_actionlist,
    Action *_action) :
    ElementHandler(_cfg_handler->parser) {
    cfg_handler = _cfg_handler;
    actionlist = _actionlist;
    action = _action;
}

static const ParserElementMap action_element_map[] = {
    { "modifier", ParserElementModifier }
};

ParserElement ActionElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it = action_element_tst->find((char *) name);
    if (it != action_element_tst->end()) return *it;
    else return ParserElementNone;
}

static ModifierMap x11_modifier_map[] = {
    { "shift", ShiftMask },
    { "lock", LockMask },
    { "control", ControlMask },
    { "mod1", Mod1Mask },
    { "mod2", Mod2Mask },
    { "mod3", Mod3Mask },
    { "mod4", Mod4Mask },
    { "mod5", Mod5Mask },
    { "button1", Button1Mask },
    { "button2", Button2Mask },
    { "button3", Button3Mask },
    { "button4", Button4Mask },
    { "button5", Button5Mask }
};

static ModifierMap wa_modifier_map[] = {
    { "moveresize", MoveResizeMask },
    { "statetrue", StateTrueMask }
};

bool ActionElementHandler::startElement(ParserElement element,
                                        const XML_Char *,
                                        Tst<char *> *attr) {
    switch (element) {
        case ParserElementModifier: {
            unsigned int *x11_modifier_value = &action->x11mod;
            unsigned int *wa_modifier_value = &action->wamod;
            char *value = parser->attrGetString(attr, "constraint", NULL);
            if (value && (! strcasecmp(value, "must_not_exist"))) {
                x11_modifier_value = &action->x11nmod;
                wa_modifier_value = &action->wanmod;
            }

            value = parser->attrGetString(attr, "name", NULL);
            if (value) {
                bool mod_set = false;
                Tst<long int>::iterator it = x11_modifier_tst->find(value);
                if (it != x11_modifier_tst->end()) {
                    *x11_modifier_value = *it;
                    mod_set = true;
                }                    
                if (! mod_set) {
                    it = wa_modifier_tst->find(value);
                    if (it != wa_modifier_tst->end()) {
                        *wa_modifier_value = *it;
                        mod_set = true;
                    }
                }
                if (! mod_set) {
                    it = client_window_state_tst->find(value);
                    if (it != client_window_state_tst->end()) {
                        *wa_modifier_value = *it;
                        mod_set = true;
                    }
                }
                if (! mod_set) {
                    parser->warning("modifier=%s doesn't exist\n", value);
                }
            } else {
                value = parser->attrGetString(attr, "key", NULL);
                if (value) {
                    list<ModifierMap *>::iterator it =
                        parser->ws->waimea->modmaps.begin();
                    for (; it != parser->ws->waimea->modmaps.end(); it++) {
                        if (! strcasecmp(value, (*it)->name)) {
                            *x11_modifier_value |= (*it)->modifier;
                            break;
                        }
                    } if (it == parser->ws->waimea->modmaps.end())
                        parser->warning("no modifier mapping to key: %s\n",
                                        value);
                } else
                    parser->warning("required attribute missing, neither "
                                    "name or key specified\n");
            }
        } break;
        default:
            break;
    }
    return false;
}

void ActionElementHandler::endElement(const XML_Char *) {
    if (! action) return;
        
    if (action->func && action->type) {
        actionlist->actionlist.push_back(action);
    } else {
        delete action;
        action = NULL;
    }
}

MenuInheritElementHandler::MenuInheritElementHandler(
    CfgElementHandler *_cfg_handler, Menu *_menu, Tst<char *> *attr) :
    InheritElementHandler(_cfg_handler, attr) {
    menu = _menu;
}

void MenuInheritElementHandler::endElement(const XML_Char *) {
    char *menuname = strbuf.getString();
    
    Menu *inherit_menu = parser->ws->getMenuNamed(menuname, false);

    if (inherit_menu == menu) {
        parser->warning("menu %s cannot inherit itself\n", menu->name);
    } else if (inherit_menu) {
        menu->inheritContent(inherit_menu);
    } else {
        if (! ignore_missing)
            parser->warning("undefined menu: %s\n", menuname);
    }

    delete [] menuname;
}

MenuElementHandler::MenuElementHandler(
    CfgElementHandler *_cfg_handler, Menu *_menu)
    : ElementHandler(_cfg_handler->parser) {
    menu = _menu;
    cfg_handler = _cfg_handler;
}

static const ParserElementMap menu_element_map[] = {
    { "inherit", ParserElementInherit },
    { "item", ParserElementMenuItem }
};

ParserElement MenuElementHandler::elementMap(const XML_Char *name) {
    if (! menu) return ParserElementNone;
    Tst<ParserElement>::iterator it = menu_element_tst->find((char *) name);
    if (it != menu_element_tst->end()) return *it;
    else return ParserElementNone;
}

bool MenuElementHandler::startElement(ParserElement element,
                                      const XML_Char *,
                                      Tst<char *> *attr) {
    switch (element) {
        case ParserElementMenuItem: {
            char *name = parser->attrGetString(attr, "name", "item");
            MenuItem *menuitem = new MenuItem(parser->ws, menu, name);
            menuitem->commonStyleUpdate();
            menu->items.push_back(menuitem);
            menuitem->applyAttributes(parser, attr);

            MenuItemElementHandler *handler = new
                MenuItemElementHandler(cfg_handler, menuitem);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementInherit: {
            MenuInheritElementHandler *handler = new
                MenuInheritElementHandler(cfg_handler, menu, attr);
            parser->pushElementHandler(handler);
            return true;
        } break;
        default:
            break;
    }
    return false;
}

void MenuElementHandler::endElement(const XML_Char *) {
    if (menu) menu->update();
}

MenuItemElementHandler::MenuItemElementHandler(
    CfgElementHandler *_cfg_handler, MenuItem *_menuitem)
    : ElementHandler(_cfg_handler->parser) {
    menuitem = _menuitem;
    cfg_handler = _cfg_handler;
}

static const ParserElementMap menuitem_element_map[] = {
    { "menu", ParserElementMenu },
    { "itemaction", ParserElementMenuItemAction }
};

ParserElement MenuItemElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it =
        menuitem_element_tst->find((char *) name);
    if (it != menuitem_element_tst->end()) return *it;
    else return ParserElementNone;
}

bool MenuItemElementHandler::startElement(ParserElement element,
                                          const XML_Char *,
                                          Tst<char *> *attr) {
    switch (element) {
        case ParserElementMenu: {
            Menu *menu = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                menu = parser->ws->getMenuNamed(name, false);
                if (menu) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        menu->clear();
                } else {
                    menu = new Menu(parser->ws, name);
                    menu->commonStyleUpdate();
                    parser->ws->menus.insert(menu->name, menu);
                }
            } else {
                menu = new Menu(parser->ws, NULL);
                menu->commonStyleUpdate();
                parser->ws->menus.insert(menu->name, menu);
            }
            menu->applyAttributes(parser, attr);

            menuitem->submenu = (Menu *) menu->ref();
            
            MenuElementHandler *handler =
                new MenuElementHandler(cfg_handler, menu);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementMenuItemAction: {
            MenuItemAction *itemaction = new MenuItemAction();
            if (itemaction->applyAttributes(parser, attr))
                menuitem->actions.push_back(itemaction);
            else
                delete itemaction;
                
        } break;
        default:
            break;
    }
    return false;
}

RenderGroupElementHandler::RenderGroupElementHandler(
    CfgElementHandler *_cfg_handler, RenderGroup *_rendergroup,
    RenderGroup *_parentgroup) :
    ElementHandler(_cfg_handler->parser) {
    rendergroup = _rendergroup;
    parentgroup = _parentgroup;
    cfg_handler = _cfg_handler;
}

static const ParserElementMap rendergroup_element_map[] = {
    { "inherit", ParserElementInherit },
    { "group", ParserElementRenderGroup },
    { "dynamic", ParserElementRenderDynamic },
    { "shape_mask", ParserElementShapeMask },
    { "path", ParserElementRenderPath },
    { "line", ParserElementRenderLine },
    { "rectangle", ParserElementRenderRectangle },
    { "ellipse", ParserElementRenderEllipse },
    { "solid", ParserElementRenderSolid },
    { "image", ParserElementRenderImage },
    
#ifdef    SVG
    { "svg", ParserElementRenderSvg },
#endif // SVG
    
    { "text", ParserElementRenderText },
    { "window", ParserElementRenderWindow }
};

ParserElement RenderGroupElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it =
        rendergroup_element_tst->find((char *) name);
    if (it != rendergroup_element_tst->end()) {
        if (*it == ParserElementRenderWindow) {
            if (rendergroup->is_a_style) return *it;
            else return ParserElementNone;
        } else
            return *it;
    } else
        return ParserElementNone;
}

bool RenderGroupElementHandler::startElement(ParserElement element,
                                             const XML_Char *,
                                             Tst<char *> *attr) {
    switch (element) {
        case ParserElementInherit: {
            RenderGroupInheritElementHandler *handler = new
                RenderGroupInheritElementHandler(cfg_handler, rendergroup,
                                                 attr);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderWindow: {
            bool raised = parser->attrGetBool(attr, "raised", false);
            WindowElementHandler *handler =
                new WindowElementHandler(cfg_handler, (Style *) rendergroup,
                                         raised);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementShapeMask: {
            if (! rendergroup->is_a_style) return false;

            if (((Style *) rendergroup)->shapemask) {
                parser->warning("shape mask allready set\n");
                return false;
            }
            
            RenderGroup *subgroup = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                subgroup = parser->ws->getRenderGroupNamed(name, false);
                if (subgroup) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        subgroup->clear();
                } else {
                    subgroup = new RenderGroup(parser->ws, name);
                    parser->ws->rendergroups.insert(
                        subgroup->name, (RenderGroup *) subgroup->ref());
                }
            } else
                subgroup = new RenderGroup(parser->ws, NULL);

            subgroup->applyAttributes(parser, attr);
            
            ((Style *) rendergroup)->shapemask = subgroup;
            ((Style *) rendergroup)->shaped = true;
            
            RenderGroupElementHandler *handler =
                new RenderGroupElementHandler(cfg_handler, subgroup,
                                              rendergroup);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderGroup: {
            RenderGroup *subgroup = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                subgroup = parser->ws->getRenderGroupNamed(name, false);
                if (subgroup) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        subgroup->clear();
                } else {
                    subgroup = new RenderGroup(parser->ws, name);
                    parser->ws->rendergroups.insert(
                        subgroup->name, (RenderGroup *) subgroup->ref());
                }
            } else
                subgroup = new RenderGroup(parser->ws, NULL);

            subgroup->applyAttributes(parser, attr);
            
            rendergroup->operations.push_back(subgroup);
            
            RenderGroupElementHandler *handler =
                new RenderGroupElementHandler(cfg_handler, subgroup,
                                              rendergroup);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderDynamic: {
            RenderOpDynamic *dynamic = new RenderOpDynamic();
            dynamic->applyAttributes(parser, attr);
            RenderOpDynamicElementHandler *handler =
                new RenderOpDynamicElementHandler(cfg_handler, rendergroup,
                                                  dynamic);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderPath: {
            RenderOpPath *path = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                path = parser->ws->getPathNamed(name, false);
                if (path) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        path->clear();
                } else {
                    path = new RenderOpPath(name);
                    parser->ws->paths.insert(path->name,
                                             (RenderOpPath *) path->ref());
                }
            } else
                path = new RenderOpPath(NULL);

            path->applyAttributes(parser, attr);

            rendergroup->operations.push_back(path);
            
            RenderPathElementHandler *handler =
                new RenderPathElementHandler(cfg_handler, path, rendergroup);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderLine: {
            RenderOpLine *line = new RenderOpLine();            
            line->applyAttributes(parser, attr);
            rendergroup->operations.push_back(line);
        } break;
        case ParserElementRenderRectangle:
        case ParserElementRenderEllipse: {
            RenderOpRectangle *rectangle = new RenderOpRectangle();
            rectangle->applyAttributes(parser, attr);
            if (element == ParserElementRenderEllipse) {
                rectangle->nw_rx = rectangle->nw_ry = rectangle->ne_rx =
                    rectangle->ne_ry = rectangle->sw_rx = rectangle->sw_ry =
                    rectangle->se_rx = rectangle->se_ry = 50.0;
                rectangle->nw_rx_u = rectangle->nw_ry_u = rectangle->ne_rx_u =
                    rectangle->ne_ry_u = rectangle->sw_rx_u =
                    rectangle->sw_ry_u = rectangle->se_rx_u =
                    rectangle->se_ry_u = PCTLenghtUnitType;
            }
            rendergroup->operations.push_back(rectangle);
        } break;
        case ParserElementRenderSolid: {
            RenderOpSolid *solid = new RenderOpSolid();
            solid->applyAttributes(parser, attr);
            rendergroup->operations.push_back(solid);
        } break;
        case ParserElementRenderImage: {
            RenderOpImage *image = new RenderOpImage();
            if (image->applyAttributes(parser, attr)) {
                rendergroup->operations.push_back(image);
            } else
                delete image;
        } break;
        
#ifdef    SVG
        case ParserElementRenderSvg: {
            RenderOpSvg *svg = new RenderOpSvg();
            if (svg->applyAttributes(parser, attr)) {
                rendergroup->operations.push_back(svg);
            } else
                delete svg;
        } break;
#endif // SVG
        
        case ParserElementRenderText: {
            RenderOpText *text = NULL;
            char *name = parser->attrGetString(attr, "name", NULL);
            if (name) {
                text = parser->ws->getTextNamed(name, false);
                if (text) {
                    if (! parser->attrGetBool(attr, "extend", false))
                        text->clear();
                } else {
                    text = new RenderOpText(name);
                    parser->ws->texts.insert(text->name,
                                             (RenderOpText *) text->ref());
                }
            } else
                text = new RenderOpText(NULL);            

            text->applyAttributes(parser, attr);
            
            RenderOpTextElementHandler *handler =
                new RenderOpTextElementHandler(cfg_handler, text, rendergroup);
            parser->pushElementHandler(handler);
            return true;
        } break;
        default:
            break;
    }
    return false;
}

void RenderGroupElementHandler::endElement(const XML_Char *) {
    if (rendergroup->is_a_style) {
        ((Style *) rendergroup)->flattenTextOps(
            &((Style *) rendergroup)->textops);
        ((Style *) rendergroup)->serial++;
        parser->ws->propagateStyleUpdate((Style *) rendergroup);
    }
    if (parentgroup) {
        if (rendergroup->has_dynamic_op)
            parentgroup->has_dynamic_op = true;
        
        if (! rendergroup->cacheable)
            parentgroup->cacheable = false;
    }
}

RenderGroupInheritElementHandler::RenderGroupInheritElementHandler(
    CfgElementHandler *_cfg_handler, RenderGroup *_rendergroup,
    Tst<char *> *attr) : InheritElementHandler(_cfg_handler, attr) {
    rendergroup = _rendergroup;
}

void RenderGroupInheritElementHandler::endElement(const XML_Char *) {
    char *rendergroupname = strbuf.getString();
    
    RenderGroup *inherit_rendergroup =
        parser->ws->getRenderGroupNamed(rendergroupname, false);
    
    if (inherit_rendergroup == rendergroup) {
        parser->warning("group/style %s cannot inherit itself\n",
                        rendergroup->name);
    } else if (inherit_rendergroup) {
        if (inherit_rendergroup->is_a_style && rendergroup->is_a_style)
            ((Style *) rendergroup)->inheritContent(
                (Style *) inherit_rendergroup);
        else
            rendergroup->inheritContent(inherit_rendergroup);
    } else {
        if (! ignore_missing)
            parser->warning("undefined group/style: %s\n", rendergroupname);
    }
    
    delete [] rendergroupname;
}

WindowElementHandler::WindowElementHandler(
    CfgElementHandler *_cfg_handler, Style *_style, bool _raised) :
    ElementHandler(_cfg_handler->parser) {
    style = _style;
    cfg_handler = _cfg_handler;
    raised = _raised;
}

void WindowElementHandler::characters(const XML_Char *chars, int len) {
    strbuf.addString((char *) chars, len);
}

void WindowElementHandler::endElement(const XML_Char *) {
    char *windowname = strbuf.getString();
    
    if (*windowname != '\0') {
        if (! style->subs) style->subs = new list<SubwinInfo *>;
        SubwinInfo *sinfo =
            new SubwinInfo(parser->ws->getSubwindowId(windowname), raised);
        style->subs->push_back(sinfo);
    } else {
        parser->warning("missing character data for window name");
    }

    delete [] windowname;
}

static const ParserElementMap renderopdynamic_element_map[] = {
    { "try", ParserElementRenderDynamicTry }
};

RenderOpDynamicElementHandler::RenderOpDynamicElementHandler(
    CfgElementHandler *_cfg_handler, RenderGroup *_rendergroup,
    RenderOpDynamic *_dynamic) :
    ElementHandler(_cfg_handler->parser) {
    dynamic = _dynamic;
    cfg_handler = _cfg_handler;
    rendergroup = _rendergroup;
}

ParserElement RenderOpDynamicElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it =
        renderopdynamic_element_tst->find((char *) name);
    if (it != renderopdynamic_element_tst->end())
        return *it;
    else
        return ParserElementNone;
}

static const struct DynamicTryMap {
    char *name;
    int type;
} dynamic_try_map[] = {
    { "static", DynamicGroupStaticType },
    { "menuitemiconimage", DynamicGroupMenuItemIconImageType },
    { "menuitemiconsvg", DynamicGroupMenuItemIconSvgType },
    { "wmiconimage", DynamicGroupWmIconImageType },
    { "wmiconsvg", DynamicGroupWmIconSvgType }
};

bool RenderOpDynamicElementHandler::startElement(ParserElement element,
                                                 const XML_Char *,
                                                 Tst<char *> *attr) {
    switch (element) {
        case ParserElementRenderDynamicTry: {
            char *value = parser->attrGetString(attr, "type", NULL);
            if (value) {
                Tst<int>::iterator it = dynamic_try_tst->find((char *) value);
                if (it != dynamic_try_tst->end()) {
                    if (*it == DynamicGroupStaticType) {
                        value = parser->attrGetString(attr, "name", NULL);
                        if (value) {
                            RenderGroup *rendergroup =
                                parser->ws->getRenderGroupNamed(value, false);
                            if (rendergroup)
                                dynamic->dynamic_order.push_back(*it);
                            else
                                parser->warning("unknown static group: %s",
                                                value);
                        } else
                            parser->warning("static group name not defined");
                    } else
                        dynamic->dynamic_order.push_back(*it);
                }
            }
        }
        default:
            break;
    }

    return false;
}

void RenderOpDynamicElementHandler::endElement(const XML_Char *) {
    if (! dynamic->dynamic_order.empty()) {
        rendergroup->operations.push_back(dynamic);
        rendergroup->has_dynamic_op = true;
    } else
        cfg_handler->parser->warning("missing group in dynamic operation");
}

RenderPatternElementHandler::RenderPatternElementHandler(
    CfgElementHandler *_cfg_handler, RenderPattern *_pattern) :
    ElementHandler(_cfg_handler->parser) {
    pattern = _pattern;
    cfg_handler = _cfg_handler;
}

static const ParserElementMap renderpattern_element_map[] = {
    { "colorstop", ParserElementRenderColorStop }
};

ParserElement RenderPatternElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it =
        renderpattern_element_tst->find((char *) name);
    if (it != renderpattern_element_tst->end()) return *it;
    else return ParserElementNone;
}

bool RenderPatternElementHandler::startElement(ParserElement element,
                                               const XML_Char *,
                                               Tst<char *> *attr) {
    WaColor *color = new WaColor();
    double offset = 0.0;
    char *value = parser->attrGetString(attr, "color", NULL);
    if (value) {
        if (! color->parseColor(parser->ws, value))
            parser->warning("error parsing color: %s\n", value);
    }
    if (color)
        color->setOpacity(
            parser->attrGetDouble(attr, "opacity",
                                  color->getOpacity()));

    offset = parser->attrGetDouble(attr, "offset", offset);

    pattern->color_stops.push_back(new WaColorStop(offset, color));
    
    return false;
}

RenderPatternInheritElementHandler::RenderPatternInheritElementHandler(
    CfgElementHandler *_cfg_handler, RenderPattern *_pattern,
    Tst<char *> *attr) : InheritElementHandler(_cfg_handler, attr) {
    pattern = _pattern;
}

void RenderPatternInheritElementHandler::endElement(const XML_Char *) {
    char *patternname = strbuf.getString();
    
    RenderPattern *inherit_pattern =
        parser->ws->getPatternNamed(patternname, false);
    
    if (inherit_pattern == pattern) {
        parser->warning("pattern %s cannot inherit itself\n", patternname);
    } else if (inherit_pattern) {
        pattern->inheritContent(inherit_pattern);
    } else {
        if (! ignore_missing)
            parser->warning("undefined pattern: %s\n", patternname);
    }
    
    delete [] patternname;
}

RenderPathElementHandler::RenderPathElementHandler(
    CfgElementHandler *_cfg_handler, RenderOpPath *_path,
    RenderGroup *_rendergroup) : ElementHandler(_cfg_handler->parser) {
    path = _path;
    cfg_handler = _cfg_handler;
    rendergroup = _rendergroup;
}

static const ParserElementMap renderpath_element_map[] = {
    { "inherit", ParserElementInherit },
    { "moveto", ParserElementRenderPathMoveTo },
    { "relmoveto", ParserElementRenderPathRelMoveTo },
    { "lineto", ParserElementRenderPathLineTo },
    { "rellineto", ParserElementRenderPathRelLineTo },
    { "curveto", ParserElementRenderPathCurveTo },
    { "relcurveto", ParserElementRenderPathRelCurveTo },
    { "arcto", ParserElementRenderPathArcTo },
    { "close", ParserElementRenderPathClose }
};

ParserElement RenderPathElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it =
        renderpath_element_tst->find((char *) name);
    if (it != renderpath_element_tst->end()) return *it;
    else return ParserElementNone;
}

bool RenderPathElementHandler::startElement(ParserElement element,
                                            const XML_Char *,
                                            Tst<char *> *attr) {
    PathOperator *op = NULL;
    switch (element) {
        case ParserElementInherit: {
            RenderOpPathInheritElementHandler *handler = new
                RenderOpPathInheritElementHandler(cfg_handler, path, attr);
            parser->pushElementHandler(handler);
            return true;
        } break;
        case ParserElementRenderPathMoveTo:
            op = new PathOperator(PathOperatorMoveToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathRelMoveTo:
            op = new PathOperator(PathOperatorRelMoveToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathLineTo:
            op = new PathOperator(PathOperatorLineToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathRelLineTo:
            op = new PathOperator(PathOperatorRelLineToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathCurveTo:
            op = new PathOperator(PathOperatorCurveToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathRelCurveTo:
            op = new PathOperator(PathOperatorRelCurveToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathArcTo:
            op = new PathOperator(PathOperatorArcToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathRelArcTo:
            op = new PathOperator(PathOperatorRelArcToType);
            op->applyAttributes(parser, attr);
            break;
        case ParserElementRenderPathClose:
            op = new PathOperator(PathOperatorCloseType);
            op->applyAttributes(parser, attr);
            break;
        default:
            break;
    }
    if (op) path->ops.push_back(op);
    
    return false;
}

void RenderPathElementHandler::endElement(const XML_Char *) {
    if (path->ops.empty())
        parser->warning("path element does not contain any operations\n");
}

RenderOpPathInheritElementHandler::RenderOpPathInheritElementHandler(
    CfgElementHandler *_cfg_handler, RenderOpPath *_path,
    Tst<char *> *attr) : InheritElementHandler(_cfg_handler, attr) {
    path = _path;
}

void RenderOpPathInheritElementHandler::endElement(const XML_Char *) {
    char *pathname = strbuf.getString();
    
    RenderOpPath *inherit_path = parser->ws->getPathNamed(pathname, false);
    
    if (inherit_path == path) {
        parser->warning("path %s cannot inherit itself\n", path->name);
    } else if (inherit_path) {
        path->inheritContent(inherit_path);
    } else {
        if (! ignore_missing)
            parser->warning("undefined path: %s\n", pathname);
    }
    
    delete [] pathname;
}

static const ParserElementMap rendertext_element_map[] = {
    { "inherit", ParserElementInherit }
};

ParserElement RenderOpTextElementHandler::elementMap(const XML_Char *name) {
    Tst<ParserElement>::iterator it =
        rendertext_element_tst->find((char *) name);
    if (it != rendertext_element_tst->end()) return *it;
    else return ParserElementNone;
}

RenderOpTextElementHandler::RenderOpTextElementHandler(
    CfgElementHandler *_cfg_handler, RenderOpText *_text,
    RenderGroup *_rendergroup) : ElementHandler(_cfg_handler->parser) {
    text = _text;
    rendergroup = _rendergroup;
    cfg_handler = _cfg_handler;
}

bool RenderOpTextElementHandler::startElement(ParserElement element,
                                              const XML_Char *,
                                              Tst<char *> *attr) {
    switch (element) {
        case ParserElementInherit: {
            RenderOpTextInheritElementHandler *handler = new
                RenderOpTextInheritElementHandler(cfg_handler, &strbuf, attr);
            parser->pushElementHandler(handler);
            return true;
        } break;
        default:
            break;
    }
    return false;
}

void RenderOpTextElementHandler::characters(const XML_Char *chars, int len) {
    strbuf.addString((char *) chars, len);
}

void RenderOpTextElementHandler::endElement(const XML_Char *) {
    char *utf8 = strbuf.getString();
    bool dynamic;

    if (! text->is_static) {
        text->is_static = true;
        text->utf8 = preexpand(utf8, &dynamic);
        if (dynamic) {
            text->is_static = false;
            if (rendergroup) rendergroup->cacheable = false;
        }
    } else {
        text->is_static = true;
        text->utf8 = WA_STRDUP(utf8);
    }

    if (rendergroup) rendergroup->operations.push_back(text);
    
    delete [] utf8;
}

RenderOpTextInheritElementHandler::RenderOpTextInheritElementHandler(
    CfgElementHandler *_cfg_handler, StringBuffer *_textstrbuf,
    Tst<char *> *attr) : InheritElementHandler(_cfg_handler, attr) {
    textstrbuf = _textstrbuf;
}

void RenderOpTextInheritElementHandler::endElement(const XML_Char *) {
    char *textname = strbuf.getString();
    
    RenderOpText *inherit_text = parser->ws->getTextNamed(textname, false);
    
    if (inherit_text) {
        if (inherit_text->utf8)
            textstrbuf->addString((char *) inherit_text->utf8,
                                  strlen(inherit_text->utf8));
    } else {
        if (! ignore_missing)
            parser->warning("undefined text object: %s\n", textname);
    }
    
    delete [] textname;
}

double get_double_and_unit(char *s, LenghtUnitType *unit) {
    char *invalid_char;
    double value = strtod(s, &invalid_char);
    if (unit)
        if (invalid_char) {
            if (! strcasecmp(invalid_char, "%")) {
                *unit = PCTLenghtUnitType;
            } else if (! strcasecmp(invalid_char, "px")) {
                *unit = PXLenghtUnitType;
            } else if (! strcasecmp(invalid_char, "cm")) {
                *unit = CMLenghtUnitType;
            } else if (! strcasecmp(invalid_char, "mm")) {
                *unit = MMLenghtUnitType;
            } else if (! strcasecmp(invalid_char, "in")) {
                *unit = INLenghtUnitType;
            } else if (! strcasecmp(invalid_char, "pt")) {
                *unit = PTLenghtUnitType;
            } else if (! strcasecmp(invalid_char, "pc")) {
                *unit = PCLenghtUnitType;
            } else {
                *unit = PXLenghtUnitType;
            }
        } else
            *unit = PXLenghtUnitType;
    
    return value;
} 

void parser_create_tsts(void) {
    int size, i;

    win_state_tst = new Tst<int>;
    size = sizeof(win_state_map) / sizeof(WindowStateMap);
    for (i = 0; i < size; i++)
        win_state_tst->insert(win_state_map[i].name, win_state_map[i].state);
    
    x11_modifier_tst = new Tst<long int>;
    size = sizeof(x11_modifier_map) / sizeof(ModifierMap);
    for (i = 0; i < size; i++)
        x11_modifier_tst->insert(x11_modifier_map[i].name,
                                 x11_modifier_map[i].modifier);

    wa_modifier_tst = new Tst<long int>;
    size = sizeof(wa_modifier_map) / sizeof(ModifierMap);
    for (i = 0; i < size; i++)
        wa_modifier_tst->insert(wa_modifier_map[i].name,
                                wa_modifier_map[i].modifier);

    dynamic_try_tst = new Tst<int>;
    size = sizeof(dynamic_try_map) / sizeof(DynamicTryMap);
    for (i = 0; i < size; i++)
        dynamic_try_tst->insert(dynamic_try_map[i].name,
                                dynamic_try_map[i].type);
    
    cfg_element_tst = new Tst<ParserElement>;
    size = sizeof(cfg_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        cfg_element_tst->insert(cfg_element_map[i].name,
                                cfg_element_map[i].element);

    actionlist_element_tst = new Tst<ParserElement>;
    size = sizeof(actionlist_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        actionlist_element_tst->insert(actionlist_element_map[i].name,
                                       actionlist_element_map[i].element);

    action_element_tst = new Tst<ParserElement>;
    size = sizeof(action_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        action_element_tst->insert(action_element_map[i].name,
                                   action_element_map[i].element);

    menu_element_tst = new Tst<ParserElement>;
    size = sizeof(menu_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        menu_element_tst->insert(menu_element_map[i].name,
                                 menu_element_map[i].element);

    menuitem_element_tst = new Tst<ParserElement>;
    size = sizeof(menuitem_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        menuitem_element_tst->insert(menuitem_element_map[i].name,
                                     menuitem_element_map[i].element);

    rendergroup_element_tst = new Tst<ParserElement>;
    size = sizeof(rendergroup_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        rendergroup_element_tst->insert(rendergroup_element_map[i].name,
                                        rendergroup_element_map[i].element);

    renderopdynamic_element_tst = new Tst<ParserElement>;
    size = sizeof(renderopdynamic_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        renderopdynamic_element_tst->insert(
            renderopdynamic_element_map[i].name,
            renderopdynamic_element_map[i].element);

    renderpath_element_tst = new Tst<ParserElement>;
    size = sizeof(renderpath_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        renderpath_element_tst->insert(renderpath_element_map[i].name,
                                       renderpath_element_map[i].element);

    renderpattern_element_tst = new Tst<ParserElement>;
    size = sizeof(renderpattern_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        renderpattern_element_tst->insert
          (renderpattern_element_map[i].name,
           renderpattern_element_map[i].element);

    rendertext_element_tst = new Tst<ParserElement>;
    size = sizeof(rendertext_element_map) / sizeof(ParserElementMap);
    for (i = 0; i < size; i++)
        rendertext_element_tst->insert(rendertext_element_map[i].name,
                                       rendertext_element_map[i].element);
}


syntax highlighted by Code2HTML, v. 0.9.1