void plainconf::addModuleWithParam(XmlNode *pCurNode,
                                   const char *moduleName, const char *param)
{
    XmlNode *pModuleNode = NULL;
    XmlNodeList::const_iterator iter;
    const XmlNodeList *pModuleList = pCurNode->getChildren("module");

    if (pModuleList)
    {
        for (iter = pModuleList->begin(); iter != pModuleList->end(); ++iter)
        {
            if (strcasecmp((*iter)->getChildValue("name", 1), moduleName) == 0)
            {
                pModuleNode = (*iter);
                break;
            }
        }
    }

    if (!pModuleNode)
    {
        pModuleNode = new XmlNode;
        //XmlNode *pParamNode = new XmlNode;
        const char *attr = NULL;
        pModuleNode->init("module", &attr);
        pModuleNode->setValue(moduleName, strlen(moduleName));
        pCurNode->addChild(pModuleNode->getName(), pModuleNode);
        logToMem(LOG_LEVEL_INFO, "[%s:%s]addModuleWithParam ADD module %s",
                 pCurNode->getName(), pCurNode->getValue(), moduleName);
    }

    appendModuleParam(pModuleNode, param);
}
void plainconf::appendModuleParam(XmlNode *pModuleNode, const char *param)
{
    XmlNode *pParamNode = pModuleNode->getChild("param");

    if (pParamNode == NULL)
    {
        pParamNode = new XmlNode;
        const char *attr = NULL;
        pParamNode->init("param", &attr);
        pParamNode->setValue(param, strlen(param));
        pModuleNode->addChild(pParamNode->getName(), pParamNode);
    }
    else
    {
        AutoStr2 totalValue = pParamNode->getValue();
        totalValue.append("\n", 1);
        totalValue.append(param, strlen(param));
        pParamNode->setValue(totalValue.c_str(), totalValue.len());
    }

    logToMem(LOG_LEVEL_INFO, "[%s:%s] module [%s] add param [%s]",
             pModuleNode->getParent()->getName(),
             ((pModuleNode->getParent()->getValue()) ?
              pModuleNode->getParent()->getValue() : ""),
             pModuleNode->getValue(), param);
}
//return the root node of the tree
XmlNode *plainconf::parseFile(const char *configFilePath,
                              const char *rootTag)
{
#ifdef TEST_OUTPUT_PLAIN_CONF
    char *tmp = (char *)new char[5 * 1024 * 1024];

    if (tmp)
        delete []tmp;

#endif
    XmlNode *rootNode = new XmlNode;
    const char *attr = NULL;
    rootNode->init(rootTag, &attr);
    gModuleList.push_back(rootNode);

    loadConfFile(configFilePath);

    if (gModuleList.size() != 1)
        logToMem(LOG_LEVEL_ERR,
                 "parseFile find '{' and '}' do not match in the end of file %s, rootTag %s.",
                 configFilePath, rootTag);

    gModuleList.clear();

    handleSpecialCaseLoop(rootNode);


#ifdef TEST_OUTPUT_PLAIN_CONF
    char sPlainFile[512] = {0};
    strcpy(sPlainFile, configFilePath);
    strcat(sPlainFile, ".txt");
    plainconf::testOutputConfigFile(rootNode, sPlainFile);
#endif
    return rootNode;
}
void plainconf::saveUnknownItems(const char *fileName, int lineNumber,
                                 XmlNode *pCurNode, const char *name, const char *value)
{
    //if not inside a "module" and without a "::", treated as error
    if (strcasecmp(pCurNode->getName(), "module") != 0 &&
        strstr(name, "::") == NULL)
    {
        logToMem(LOG_LEVEL_ERR, "Not support [%s %s] in file %s:%d", name, value,
                 fileName, lineNumber);
        return ;
    }

    char newvalue[4096] = {0};
    XmlNode *pParamNode = new XmlNode;
    const char *attr = NULL;
    pParamNode->init(UNKNOWN_KEYWORDS, &attr);
    strcpy(newvalue, name);
    strcat(newvalue, " ");
    strcat(newvalue, value);
    pParamNode->setValue(newvalue, strlen(newvalue));
    pCurNode->addChild(pParamNode->getName(), pParamNode);
}
void plainconf::parseLine(const char *fileName, int lineNumber,
                          const char *sLine)
{
    const int MAX_NAME_LENGTH = 4096;
    char name[MAX_NAME_LENGTH] = {0};
    char value[MAX_NAME_LENGTH] = {0};
    const char *attr = NULL;

    XmlNode *pNode = NULL;
    XmlNode *pCurNode = (XmlNode *)gModuleList.back();
    const char *p = sLine;
    const char *pEnd = sLine + strlen(sLine);

    bool bNameSet = false;

    for (; p < pEnd; ++p)
    {
        //"{" is a beginning of a block only if it is the last char of a line
        if (*p == '{' && pEnd - p == 1)
        {
            if (strlen(name) > 0)
            {
                const char *pRealname = getRealName(name);

                if (pRealname)
                {
                    pNode = new XmlNode;
                    pNode->init(pRealname, &attr);

                    //Remove space in the end of the value such as "module cache  {", value will be "cache"
                    removeSpace(value, 1);

                    if (strlen(value) > 0)
                        pNode->setValue(value, strlen(value));

                    pCurNode->addChild(pNode->getName(), pNode);
                    gModuleList.push_back(pNode);
                    pCurNode = pNode;
                    clearNameAndValue(name, value);
                    break;
                }
                else
                {
                    logToMem(LOG_LEVEL_ERR,
                             "parseline find block name [%s] is NOT keyword in %s:%d", name, fileName,
                             lineNumber);
                    break;
                }
            }
            else
            {
                logToMem(LOG_LEVEL_ERR,
                         "parseline found '{' without a block name in %s:%d", fileName, lineNumber);
                break;
            }
        }

        else if (*p == '}' && p == sLine)
        {
            if (gModuleList.size() > 1)
            {
                gModuleList.pop_back();
                clearNameAndValue(name, value);

                if (*(p + 1))
                {
                    ++p;
                    trimWhiteSpace(&p);
                    parseLine(fileName, lineNumber, p);
                    break;
                }
            }
            else
            {
                logToMem(LOG_LEVEL_ERR, "parseline found more '}' in %s:%d", fileName,
                         lineNumber);
                clearNameAndValue(name, value);
                break;
            }
        }
        else if ((*p == ' ' || *p == '\t') && value[0] == 0)
        {
            bNameSet = true;
            continue;
        }
        else
        {
            if (!bNameSet)
                strcatchr(name, *p, MAX_NAME_LENGTH);
            else
                strcatchr(value, *p, MAX_NAME_LENGTH);
        }
    }

    if (name[0] != 0)
    {
        const char *pRealname = getRealName(name);

        if (pRealname)
        {
            assert(pNode == NULL);
            pNode = new XmlNode;
            pNode->init(pRealname, &attr);

            if (strlen(value) > 0)
                pNode->setValue(value, strlen(value));

            pCurNode->addChild(pNode->getName(), pNode);
        }
        else
        {
            //There is no special case in server level
            //if (memcmp(pCurNode->getName(), SERVER_ROOT_XML_NAME, sizeof(SERVER_ROOT_XML_NAME) - 1) != 0)
            saveUnknownItems(fileName, lineNumber, pCurNode, name, value);
            //else
            //    logToMem(LOG_LEVEL_ERR, "%s Server level find unknown keyword [%s], ignored.", SERVER_ROOT_XML_NAME, name );
        }
    }
}