int SubstFormat::parse( const char * pCurLine, const char * pFormatStr, const char * pEnd, int isSSI, char varChar )
{
    while(( pFormatStr < pEnd )&&( isspace( *pFormatStr )))
        ++pFormatStr;
    int err = 0;
    char achVarChar[2];
    LinkedObj * pLast = head();
    SubstItem * pItem;
    achVarChar[0] = varChar;
    achVarChar[1] = 0;
    while( pFormatStr < pEnd )
    {
        pItem = new SubstItem();
        if ( !pItem )
        {
            ERR_NO_MEM( "new SubstItem()" );
            return -1;
        }
        
        if (  *pFormatStr == varChar )
        {
            if ( pFormatStr + 1 == pEnd )
            {
                HttpLog::parse_error( pCurLine ,  "Line ended with '$'" );
                err = 1;
            }
            if ( isdigit( *( pFormatStr + 1 ) ) )
            {
                pItem->setType( REF_RULE_SUBSTR );
                pItem->setIndex( *(pFormatStr + 1) - '0' );
                pFormatStr += 2;
            }
            else 
            {
                ++pFormatStr;
                if ( pItem->parseServerVar( pCurLine, pFormatStr, pEnd, isSSI ) )
                {
                    err = 1;
                }

            }
        }
        else
        {
            pItem->parseString( pFormatStr, pEnd, achVarChar );
        }
        if ( err )
        {
            delete pItem;
            return -1;
        }
        else
        {
            pLast->addNext( pItem );
            pLast = pItem;
        }
    }
    return 0;
}
int RewriteEngine::parseRules( char * &pRules, RewriteRuleList * pRuleList,
                        const RewriteMapList * pMapList )
{
    LinkedObj * pLast = pRuleList->tail();
    if ( !pLast )
        pLast = pRuleList->head();
    while( *pRules )
    {
        while( isspace( *pRules ) )
            ++pRules;
        if ( !*pRules )
            break;
        if ((( strncasecmp( pRules, "RewriteCond", 11 ) == 0 )&&
             ( isspace( *(pRules + 11)) ))||
            (( strncasecmp( pRules, "RewriteRule", 11 ) == 0 )&&
             ( isspace( *(pRules + 11)) )))
        {
            RewriteRule * pRule = new RewriteRule();
            if ( !pRule )
            {
                ERR_NO_MEM( "new RewriteRule()" );
                return -1;
            }
            int ret = pRule->parse( pRules, pMapList );
            if ( ret )
            {
                delete pRule;
                return 0;
            }
            pLast->addNext( pRule );
            pLast = pRule;
        }
        else
        {
            char * pLineEnd = strchr( pRules, '\n' );
            if ( pLineEnd )
                *pLineEnd = 0;
            if ( *pRules != '#' )
            {
                LOG_ERR(( "Invalid rewrite directive: %s", pRules ));
            }
            if ( pLineEnd )
                *pLineEnd = '\n';
            else
                break;
            pRules = pLineEnd + 1;
        }
    }
    return 0;
}
int RewriteSubstFormat::parse( const char * pFormatStr, const char * pEnd, const RewriteMapList * pMaps )
{
    while(( pFormatStr < pEnd )&&( isspace( *pFormatStr )))
        ++pFormatStr;
    int err = 0;
    LinkedObj * pLast = head();
    RewriteSubstItem * pItem;
    while( pFormatStr < pEnd )
    {
        pItem = new RewriteSubstItem();
        if ( !pItem )
        {
            ERR_NO_MEM( "new SubstItem()" );
            return -1;
        }
        
        switch( *pFormatStr )
        {
        case '$':
            if ( pFormatStr + 1 == pEnd )
            {
                HttpLog::parse_error( s_pCurLine,  "Line ended with '$'" );
                err = 1;
                break;
            }
            if ( *( pFormatStr + 1 ) == '{' )
            {
                if ( !pMaps )
                {
                    HttpLog::parse_error( s_pCurLine,  "No rewrite map defined" );
                    err = 1;
                    break;
                }
                pItem->setType( REF_MAP );
                MapRefItem * pMapRef = new MapRefItem();
                if ( !pMapRef )
                {
                    ERR_NO_MEM( "new MapRefItem()" );
                    err = 1;
                    break;
                }
                if ( pMapRef->parse( pFormatStr, pEnd, pMaps ) )
                {
                    delete pMapRef;
                    err = 1;
                    break;
                }
                pItem->setMapRef( pMapRef );
            }
            else if ( isdigit( *( pFormatStr + 1 ) ) )
            {
                pItem->setType( REF_RULE_SUBSTR );
                pItem->setIndex( *(pFormatStr + 1) - '0' );
                pFormatStr += 2;
            }
            else
            {
                HttpLog::parse_error( s_pCurLine,  "'$' should be followed by a digit for a RewriteRule backreference." );
                pItem->setType( REF_STRING );
                pItem->setStr( pFormatStr, 1 );
                ++pFormatStr;
            }
            break;
        case '%':
            if ( pFormatStr + 1 == pEnd )
            {
                HttpLog::parse_error( s_pCurLine,  "Line ended with '%'" );
                err = 1;
                break;
            }
            if ( *( pFormatStr + 1 ) == '{' )
            {
                ++pFormatStr;
                if ( pItem->parseServerVar( s_pCurLine, pFormatStr, pEnd ) )
                {
                    err = 1;
                    break;
                }
            }
            else if ( isdigit( *( pFormatStr + 1 ) ) )
            {
                pItem->setType( REF_COND_SUBSTR );
                pItem->setIndex( *(pFormatStr + 1) - '0' );
                pFormatStr += 2;
            }
            else
            {
                HttpLog::parse_error( s_pCurLine,  "'%' should be followed by a digit for a RewriteCond backreference." );
                pItem->setType( REF_STRING );
                pItem->setStr( pFormatStr, 1 );
                ++pFormatStr;
            }
            break;
        case '\\':
        default:
            pItem->parseString( pFormatStr, pEnd, "$%" );
            break;
        }
        if ( err )
        {
            delete pItem;
            return -1;
        }
        else
        {
            pLast->addNext( pItem );
            pLast = pItem;
        }
    }
    return 0;
}
int RewriteRule::parse( char * &pRule, const RewriteMapList * pMaps )
{
    char * pCur;
    char * pLineEnd;
    LinkedObj * pLast = m_conds.head();
    assert( pLast->next() == NULL );
    while( *pRule )
    {
        while( isspace( *pRule ) )
            ++pRule;
        if ( !*pRule )
            break;
        pCur = pRule;
        pLineEnd = strchr( pCur, '\n' );
        if ( !pLineEnd )
        {
            pLineEnd = pCur + strlen( pCur );
            pRule = pLineEnd;
        }
        else
        {
            pRule = pLineEnd + 1;
            *pLineEnd = 0;
        }
        s_pCurLine = pCur;
        if ( *pCur != '#' )
        {
            if (( strncasecmp( pCur, "RewriteCond", 11 ) == 0 )&&
                ( isspace( *(pCur + 11)) ))
            {
                RewriteCond * pCond = new RewriteCond();
                if ( !pCond )
                {
                    ERR_NO_MEM( "new RewriteCond()" );
                    return -1;
                }
                
                if ( pCond->parse( pCur+12, pLineEnd, pMaps ) )
                {
                    delete pCond;
                    HttpLog::parse_error( s_pCurLine,  "invalid rewrite condition" );
                    return -1;
                }
                pLast->addNext( pCond );
                pLast = pCond;
            }
            else if (( strncasecmp( pCur, "RewriteRule", 11 ) == 0 )&&
                     ( isspace( *(pCur + 11)) ))
            {
                int ret = parseRule( pCur+12, pLineEnd, pMaps );
                pCur = pLineEnd+1;
                return ret;
            }
            else
            {
                HttpLog::parse_error( s_pCurLine,  "invalid rewrite directive " );
                return -1;
            }
        }
    }

    return -1;
}