/*###################################################################################
#
#   Embperl - Copyright (c) 1997-2001 Gerald Richter / ECOS
#
#   You may distribute under the terms of either the GNU General Public
#   License or the Artistic License, as specified in the Perl README file.
#   For use with Apache httpd and mod_perl, see also Apache copyright.
#
#   THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
#   IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
#   WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
#   $Id: epmain.c,v 1.75.4.71 2001/11/16 11:29:02 richter Exp $
#
###################################################################################*/


#include "ep.h"
#include "epmacro.h"


/* Version */

static char sVersion [] = VERSION ;


static int  bInitDone = 0 ; /* c part is already initialized */

static char sEnvHashName   [] = "ENV" ;
static char sFormHashName  [] = "HTML::Embperl::fdat" ;
static char sUserHashName  [] = "HTML::Embperl::udat" ;
static char sStateHashName [] = "HTML::Embperl::sdat" ;
static char sModHashName  []  = "HTML::Embperl::mdat" ;
static char sFormSplitHashName [] = "HTML::Embperl::fsplitdat" ;
static char sFormArrayName [] = "HTML::Embperl::ffld" ;
static char sInputHashName [] = "HTML::Embperl::idat" ;
static char sHeaderHashName  [] = "HTML::Embperl::http_headers_out" ;
static char sTabCountName  [] = "HTML::Embperl::cnt" ;
static char sTabRowName    [] = "HTML::Embperl::row" ;
static char sTabColName    [] = "HTML::Embperl::col" ;
static char sTabMaxRowName [] = "HTML::Embperl::maxrow" ;
static char sTabMaxColName [] = "HTML::Embperl::maxcol" ;
static char sTabModeName   [] = "HTML::Embperl::tabmode" ;
static char sEscModeName   [] = "HTML::Embperl::escmode" ;
#ifdef EP2
static char sCurrNodeName   [] = "HTML::Embperl::_ep_node" ;
static char sTokenHashName [] = "HTML::Embperl::Syntax::Default" ;
#endif

static char sDefaultPackageName [] = "HTML::Embperl::DOC::_%d" ;

static char sUIDName [] = "_ID" ;
static char sSetCookie [] = "Set-Cookie" ;
static char sCookieNameDefault [] = "EMBPERL_UID" ;


static int      nPackNo = 1 ;       /* Number for createing unique package names */
static tReq *   pReqFree = NULL ;   /* Chain of unused req structures */
tReq     InitialReq ;               /* Initial request - holds default values */
tReq * pCurrReq ;                   /* Set before every eval (NOT thread safe!!) */ 

static HV * pCacheHash ;            /* Hash which holds all cached data
				       (key=> filename or 
					      filename + packagename, 
				       value=>cache hash for file) */

static int nRequestCount = 1 ;


/* */
/* print error */
/* */

char * LogError (/*i/o*/ register req * r,
			/*in*/ int   rc)

    {
    const char * msg ;
    char * sText ;
    SV *   pSV ;
    SV *   pSVLine = NULL ;
    SV **  ppSV ;
    STRLEN l ;
    int    n ;

    
    EPENTRY (LogError) ;
    
    r -> errdat1 [sizeof (r -> errdat1) - 1] = '\0' ;
    r -> errdat2 [sizeof (r -> errdat2) - 1] = '\0' ;

    GetLineNo (r) ;
    
    if (rc != rcPerlWarn)
        r -> bError = 1 ;
    
    switch (rc)
        {
        case ok:                        msg ="[%d]ERR:  %d: %s ok%s%s" ; break ;
        case rcStackOverflow:           msg ="[%d]ERR:  %d: %s Stack Overflow%s%s" ; break ;
        case rcArgStackOverflow:        msg ="[%d]ERR:  %d: %s Argumnet Stack Overflow (%s)%s" ; break ;
        case rcStackUnderflow:          msg ="[%d]ERR:  %d: %s Stack Underflow%s%s" ; break ;
        case rcEndifWithoutIf:          msg ="[%d]ERR:  %d: %s endif without if%s%s" ; break ;
        case rcElseWithoutIf:           msg ="[%d]ERR:  %d: %s else without if%s%s" ; break ;
        case rcEndwhileWithoutWhile:    msg ="[%d]ERR:  %d: %s endwhile without while%s%s" ; break ;
        case rcEndtableWithoutTable:    msg ="[%d]ERR:  %d: %s blockend <%s> does not match blockstart <%s>" ; break ;
        case rcTablerowOutsideOfTable:  msg ="[%d]ERR:  %d: %s <tr> outside of table%s%s" ; break ;
        case rcCmdNotFound:             msg ="[%d]ERR:  %d: %s Unknown Command %s%s" ; break ;
        case rcOutOfMemory:             msg ="[%d]ERR:  %d: %s Out of memory%s%s" ; break ;
        case rcPerlVarError:            msg ="[%d]ERR:  %d: %s Perl variable error %s%s" ; break ;
        case rcHashError:               msg ="[%d]ERR:  %d: %s Perl hash error, %%%s does not exist%s" ; break ;
        case rcArrayError:              msg ="[%d]ERR:  %d: %s Perl array error , @%s does not exist%s" ; break ;
        case rcFileOpenErr:             msg ="[%d]ERR:  %d: %s File %s open error: %s" ; break ;    
        case rcLogFileOpenErr:          msg ="[%d]ERR:  %d: %s Logfile %s open error: %s" ; break ;    
        case rcMissingRight:            msg ="[%d]ERR:  %d: %s Missing right %s%s" ; break ;
        case rcNoRetFifo:               msg ="[%d]ERR:  %d: %s No Return Fifo%s%s" ; break ;
        case rcMagicError:              msg ="[%d]ERR:  %d: %s Perl Magic Error%s%s" ; break ;
        case rcWriteErr:                msg ="[%d]ERR:  %d: %s File write Error%s%s" ; break ;
        case rcUnknownNameSpace:        msg ="[%d]ERR:  %d: %s Namespace %s unknown%s" ; break ;
        case rcInputNotSupported:       msg ="[%d]ERR:  %d: %s Input not supported in mod_perl mode%s%s" ; break ;
        case rcCannotUsedRecursive:     msg ="[%d]ERR:  %d: %s Cannot be called recursivly in mod_perl mode%s%s" ; break ;
        case rcEndtableWithoutTablerow: msg ="[%d]ERR:  %d: %s </tr> without <tr>%s%s" ; break ;
        case rcEndtextareaWithoutTextarea: msg ="[%d]ERR:  %d: %s </textarea> without <textarea>%s%s" ; break ;
        case rcEvalErr:                 msg ="[%d]ERR:  %d: %s Error in Perl code: %s%s" ; break ;
	case rcNotCompiledForModPerl:   msg ="[%d]ERR:  %d: %s Embperl is not compiled for mod_perl. Rerun Makefile.PL and give the correct Apache source tree location %s%s" ; break ;
        case rcExecCGIMissing:          msg ="[%d]ERR:  %d: %s Forbidden %s: Options ExecCGI not set in your Apache configs%s" ; break ;
        case rcIsDir:                   msg ="[%d]ERR:  %d: %s Forbidden %s is a directory%s" ; break ;
        case rcXNotSet:                 msg ="[%d]ERR:  %d: %s Forbidden %s X Bit not set%s" ; break ;
        case rcNotFound:                msg ="[%d]ERR:  %d: %s Not found %s%s" ; break ;
        case rcUnknownVarType:          msg ="[%d]ERR:  %d: %s Type for Variable %s is unknown %s" ; break ;
        case rcPerlWarn:                msg ="[%d]ERR:  %d: %s Warning in Perl code: %s%s" ; break ;
        case rcVirtLogNotSet:           msg ="[%d]ERR:  %d: %s EMBPERL_VIRTLOG must be set, when dbgLogLink is set %s%s" ; break ;
        case rcMissingInput:            msg ="[%d]ERR:  %d: %s Sourcedata missing %s%s" ; break ;
        case rcUntilWithoutDo:          msg ="[%d]ERR:  %d: %s until without do%s%s" ; break ;
        case rcEndforeachWithoutForeach:msg ="[%d]ERR:  %d: %s endforeach without foreach%s%s" ; break ;
        case rcMissingArgs:             msg ="[%d]ERR:  %d: %s Too few arguments%s%s" ; break ;
        case rcNotAnArray:              msg ="[%d]ERR:  %d: %s Second Argument must be array/list%s%s" ; break ;
        case rcCallInputFuncFailed:     msg ="[%d]ERR:  %d: %s Call to Input Function failed: %s%s" ; break ;
        case rcCallOutputFuncFailed:    msg ="[%d]ERR:  %d: %s Call to Output Function failed: %s%s" ; break ;
        case rcSubNotFound:             msg ="[%d]ERR:  %d: %s Call to unknown Embperl macro %s%s" ; break ;
        case rcImportStashErr:          msg ="[%d]ERR:  %d: %s Package %s for import unknown%s" ; break ;
        case rcCGIError:                msg ="[%d]ERR:  %d: %s Setup of CGI.pm failed: %s%s" ; break ;
        case rcUnclosedHtml:            msg ="[%d]ERR:  %d: %s Unclosed HTML tag <%s> at end of file %s" ; break ;
        case rcUnclosedCmd:             msg ="[%d]ERR:  %d: %s Unclosed command [$ %s $] at end of file %s" ; break ;
	case rcNotAllowed:              msg ="[%d]ERR:  %d: %s Forbidden %s: Does not match EMBPERL_ALLOW %s" ; break ;
        case rcNotHashRef:              msg ="[%d]ERR:  %d: %s %s need hashref in '%s'" ; break ; 
	case rcTagMismatch:		msg ="[%d]ERR:  %d: %s Endtag '%s' doesn't match starttag '%s'" ; break ; 
	case rcCleanupErr:		msg ="[%d]ERR:  %d: %s Error in cleanup %s%s" ; break ; 
	case rcCryptoWrongHeader:	msg ="[%d]ERR:  %d: %s Decrypt-error: Not encrypted (%s)%s" ; break ; 
	case rcCryptoWrongSyntax:	msg ="[%d]ERR:  %d: %s Decrypt-error: Wrong syntax (%s)%s" ; break ; 
	case rcCryptoNotSupported:	msg ="[%d]ERR:  %d: %s Decrypt-error: Not supported (%s)%s" ; break ; 
	case rcCryptoBufferOverflow:	msg ="[%d]ERR:  %d: %s Decrypt-error: Buffer overflow (%s)%s" ; break ; 
	case rcCryptoErr:		msg ="[%d]ERR:  %d: %s Decrypt-error: OpenSSL error (%s)%s" ; break ; 
	case rcUnknownProvider:	        msg ="[%d]ERR:  %d: %s Unknown Provider %s %s" ; break ; 
	case rcXalanError:	        msg ="[%d]ERR:  %d: %s Xalan Error: %s: %s" ; break ; 
	case rcLibXSLTError:	        msg ="[%d]ERR:  %d: %s LibXSLT Error: %s: %s" ; break ; 
        case rcMissingParam:		msg ="[%d]ERR:  %d: %s Missing Parameter %s %s" ; break ; 
        case rcNotCodeRef:		msg ="[%d]ERR:  %d: %s %s need coderef in '%s'" ; break ; 
        case rcUnknownRecipe:		msg ="[%d]ERR:  %d: %s Unknown recipe '%s'" ; break ; 
        case rcTypeMismatch:		msg ="[%d]ERR:  %d: %s Unsupported Outputformat %s of %s" ; break ; 
        case rcChdirError:		msg ="[%d]ERR:  %d: %s Cannot change to directory %s %s" ; break ; 
	
	default:                        msg ="[%d]ERR:  %d: %s Error %s%s" ; break ; 
        }

    if ((rc != rcPerlWarn && rc != rcEvalErr) || r -> errdat1[0] == '\0')
        {
        char * p = NULL ;
        char buf[20] = "" ;
        if (!r -> Buf.pFile || !r -> Buf.pFile -> sSourcefile)
            p = "" ;
        if (!p)
            p = strrchr (r -> Buf.pFile -> sSourcefile, '/') ;
        if (p)
            p++ ;
        else
            {
            p = strrchr (r -> Buf.pFile -> sSourcefile, '\\') ;
            if (!p)
                p = r -> Buf.pFile -> sSourcefile ;
            else
                p++ ;
            }
        if (r -> Buf.nSourceline)
            sprintf (buf, "(%d)", r -> Buf.nSourceline) ;
        pSVLine = newSVpvf ("%s%s:", p, buf) ;
	newSVpvf2(pSVLine) ;
        }

   
    
    pSV = newSVpvf (msg, r -> nPid , rc, pSVLine?SvPV(pSVLine, l):"", r -> errdat1, r -> errdat2) ;
    newSVpvf2(pSV) ;

    if (r -> bOptions & optShowBacktrace)
        {
        req * l = r ;
        while (l && l != &InitialReq)
            {
            sv_catpvf(pSV, "\n    * %s", (!l -> Buf.pFile || !l -> Buf.pFile -> sSourcefile)?"<no filename available>":l -> Buf.pFile -> sSourcefile) ;
            l = l -> pLastReq ;
            }
        }


    if (pSVLine)
        SvREFCNT_dec(pSVLine) ;

    sText = SvPV (pSV, l) ;    
    
    lprintf (r, "%s\n", sText) ;

#ifdef APACHE
    if (r -> pApacheReq)
#ifdef APLOG_ERR
        if (rc != rcPerlWarn)
            aplog_error (APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r -> pApacheReq -> server, "%s", sText) ;
        else
            aplog_error (APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, r -> pApacheReq -> server, "%s", sText) ;
#else
        log_error (sText, r -> pApacheReq -> server) ;
#endif
    else
#endif
        {
#ifdef WIN32
        if (r -> nIOType != epIOCGI)
#endif
            {
            /*fprintf (stderr, "%s\n", sText) ;*/
            PerlIO_printf (PerlIO_stderr(), "%s\n", sText) ;
            fflush (stderr) ;
            }
        }
    
    if (rc == rcPerlWarn)
        strncpy (r -> lastwarn, r -> errdat1, sizeof (r -> lastwarn) - 1) ;

    if (r -> pErrArray)
        {
        /*lprintf (r, "DIS: in LogError AvFILL (pErrArray) = %d, nMarker = %d,  nLastErrFill= %d , bLastErrState = %d, bError = %d\n" , AvFILL (r -> pErrArray), r -> nMarker, r -> nLastErrFill, r -> bLastErrState, r -> bError) ;*/
        av_push (r -> pErrArray, pSV) ;
    
        av_store (r -> pErrFill, r -> nMarker, newSViv (AvFILL(r -> pErrArray))) ;
        av_store (r -> pErrState, r -> nMarker, newSViv (r -> bError)) ;
        n = r -> nMarker ;
        while (n-- > 0)
            {
            ppSV = av_fetch (r -> pErrFill, n, 0) ;
            if (ppSV && SvOK (*ppSV))
                break ;
            av_store (r -> pErrFill, n, newSViv (r -> nLastErrFill)) ;
            av_store (r -> pErrState, n, newSViv (r -> bLastErrState)) ;
            /*lprintf (r, "DIS: in LogError n=%d\n", n) ;*/
            }
        
        r -> nLastErrFill  = AvFILL(r -> pErrArray) ;
        r -> bLastErrState = r -> bError ;
        }
    else
	SvREFCNT_dec (pSV) ;

    r -> errdat1[0] = '\0' ;
    r -> errdat2[0] = '\0' ;

    return sText ;
    }


/* */
/* begin for error rollback */
/* */

void CommitError (/*i/o*/ register req * r)
    
    {
    int f = AvFILL(r -> pErrArray)  ;
    int n ;
    SV ** ppSV ;

    if (f == -1)
        return ; /* no errors -> nothing to do */

    /* lprintf (r, "DIS: Commit AvFILL (pErrArray) = %d, nMarker = %d,  nLastErrFill= %d , bLastErrState = %d\n" , AvFILL (r -> pErrArray), r -> nMarker, r -> nLastErrFill, r -> bLastErrState) ; */

    av_store (r -> pErrFill, r -> nMarker, newSViv (f)) ;
    av_store (r -> pErrState, r -> nMarker, newSViv (r -> bError)) ;
    n = r -> nMarker ;
    while (n-- > 0)
        {
        ppSV = av_fetch (r -> pErrFill, n, 0) ;
        if (ppSV && SvOK (*ppSV))
            break ;
        av_store (r -> pErrFill, n, newSViv (r -> nLastErrFill)) ;
        av_store (r -> pErrState, n, newSViv (r -> bLastErrState)) ;
        /* lprintf (r, "DIS: in LogError n=%d\n", n) ;*/
        }
    }

    
    
/* */
/* rollback error */
/* */

void RollbackError (/*i/o*/ register req * r)

    {
    SV *  pFill ;
    SV *  pState ;
    SV ** ppSV ;
    I32   f = AvFILL (r -> pErrFill) ;
    int   n ;
    int   i ;

    if (f < r -> nMarker)
        return ;
    
    /*lprintf (r, "DIS: AvFILL (pErrFill) = %d, nMarker = %d\n" , f, r -> nMarker) ;*/
    
    for (i = f; i > r -> nMarker; i--)
        {
        pFill  = av_pop(r -> pErrFill) ;
        pState = av_pop(r -> pErrState) ;
        SvREFCNT_dec (pFill) ;
        SvREFCNT_dec (pState) ;
        }
    ppSV   = av_fetch(r -> pErrFill, r -> nMarker, 0) ;
    if (ppSV)
        n = SvIV (*ppSV) ;
    else
        {
        n = 0 ;
        /*lprintf (r, "DIS: in Roolback set n=%d\n", n) ;*/
        }


    ppSV = av_fetch(r -> pErrState, r -> nMarker, 0) ;
    if (ppSV)
        r -> bError = SvIV (*ppSV) ;
    else
        r -> bError = 1 ;
    f = AvFILL (r -> pErrArray) ;
    /*lprintf (r, "DIS: AvFILL (pErrArray) = %d, n = %d\n" , f, n) ;*/
    if (f > n)
        lprintf (r, "[%d]ERR:  Discard the last %d errormessages, because they occured after the end of a table\n", r -> nPid, f - n) ;
    for (i = f; i > n; i--)
        {
        SvREFCNT_dec (av_pop(r -> pErrArray)) ;
        }

    r -> nLastErrFill  = AvFILL(r -> pErrArray) ;
    r -> bLastErrState = r -> bError ;

    /*lprintf (r, "DIS: in RollbackError AvFILL (pErrArray) = %d, nMarker = %d,  nLastErrFill= %d , bLastErrState = %d, bError = %d\n" , AvFILL (r -> pErrArray), r -> nMarker, r -> nLastErrFill, r -> bLastErrState, r -> bError) ;*/
    
    }

#if defined (_DEBUG) && defined (WIN32)

static int EmbperlCRTDebugOutput( int reportType, char *userMessage, int *retVal )
    {   
    lprintf (pCurrReq, "[%d]CRTDBG: %s\n", pCurrReq -> nPid, userMessage) ;  

    return TRUE ;
    }

#endif

/* */
/* Magic */
/* */

void NewEscMode (/*i/o*/ register req * r,
			           SV * pSV)

    {
    if (r -> nEscMode & escXML && !r -> bEscInUrl)
	r -> pNextEscape = Char2XML ;
    else if (r -> nEscMode & escHtml && !r -> bEscInUrl)
	r -> pNextEscape = Char2Html ;
    else if (r -> nEscMode & escUrl)
        r -> pNextEscape = Char2Url ;
    else 
        r -> pNextEscape = NULL ;

    if (r -> bEscModeSet < 1)
	{
        r -> pCurrEscape = r -> pNextEscape ;
        r -> nCurrEscMode = r -> nEscMode ;
	}

    if (r -> bEscModeSet < 0 && pSV && SvOK (pSV))
        r -> bEscModeSet = 1 ;
    }



static int notused ;

INTMG (TabCount, pCurrReq -> TableStack.State.nCount, pCurrReq -> TableStack.State.nCountUsed, ;) 
INTMG (TabRow, pCurrReq -> TableStack.State.nRow, pCurrReq -> TableStack.State.nRowUsed, ;) 
INTMG (TabCol, pCurrReq -> TableStack.State.nCol, pCurrReq -> TableStack.State.nColUsed, ;) 
INTMG (TabMaxRow, pCurrReq -> nTabMaxRow, notused,  ;) 
INTMG (TabMaxCol, pCurrReq -> nTabMaxCol, notused, ;) 
INTMG (TabMode, pCurrReq -> nTabMode, notused, ;) 
INTMG (EscMode, pCurrReq -> nEscMode, notused, NewEscMode (pCurrReq, pSV)) 
#ifdef EP2
INTMGshort (CurrNode, pCurrReq -> xCurrNode) 
#endif

OPTMGRD (optDisableVarCleanup      , pCurrReq -> bOptions) 
OPTMG   (optDisableEmbperlErrorPage, pCurrReq -> bOptions) 
OPTMG   (optReturnError            , pCurrReq -> bOptions) 
OPTMGRD (optSafeNamespace          , pCurrReq -> bOptions) 
OPTMGRD (optOpcodeMask             , pCurrReq -> bOptions) 
OPTMG   (optRawInput               , pCurrReq -> bOptions) 
OPTMG   (optSendHttpHeader         , pCurrReq -> bOptions) 
OPTMGRD (optDisableChdir           , pCurrReq -> bOptions) 
OPTMG   (optDisableHtmlScan        , pCurrReq -> bOptions) 
OPTMGRD (optEarlyHttpHeader        , pCurrReq -> bOptions) 
OPTMGRD (optDisableFormData        , pCurrReq -> bOptions) 
OPTMG   (optDisableInputScan       , pCurrReq -> bOptions) 
OPTMG   (optDisableTableScan       , pCurrReq -> bOptions) 
OPTMG   (optDisableMetaScan        , pCurrReq -> bOptions) 
OPTMGRD (optAllFormData            , pCurrReq -> bOptions) 
OPTMGRD (optRedirectStdout         , pCurrReq -> bOptions) 
OPTMG   (optUndefToEmptyValue      , pCurrReq -> bOptions) 
OPTMG   (optNoHiddenEmptyValue     , pCurrReq -> bOptions) 
OPTMGRD (optAllowZeroFilesize      , pCurrReq -> bOptions) 
OPTMGRD (optKeepSrcInMemory        , pCurrReq -> bOptions) 
OPTMG   (optKeepSpaces             , pCurrReq -> bOptions) 
OPTMG   (optOpenLogEarly           , pCurrReq -> bOptions) 
OPTMG   (optNoUncloseWarn          , pCurrReq -> bOptions) 


OPTMG   (dbgStd          , pCurrReq -> bDebug) 
OPTMG   (dbgMem          , pCurrReq -> bDebug) 
OPTMG   (dbgEval         , pCurrReq -> bDebug) 
OPTMG   (dbgCmd          , pCurrReq -> bDebug) 
OPTMG   (dbgEnv          , pCurrReq -> bDebug) 
OPTMG   (dbgForm         , pCurrReq -> bDebug) 
OPTMG   (dbgTab          , pCurrReq -> bDebug) 
OPTMG   (dbgInput        , pCurrReq -> bDebug) 
OPTMG   (dbgFlushOutput  , pCurrReq -> bDebug) 
OPTMG   (dbgFlushLog     , pCurrReq -> bDebug) 
OPTMG   (dbgAllCmds      , pCurrReq -> bDebug) 
OPTMG   (dbgSource       , pCurrReq -> bDebug) 
OPTMG   (dbgFunc         , pCurrReq -> bDebug) 
OPTMG   (dbgLogLink      , pCurrReq -> bDebug) 
OPTMG   (dbgDefEval      , pCurrReq -> bDebug) 
OPTMG   (dbgHeadersIn    , pCurrReq -> bDebug) 
OPTMG   (dbgShowCleanup  , pCurrReq -> bDebug) 
OPTMG   (dbgProfile      , pCurrReq -> bDebug) 
OPTMG   (dbgSession      , pCurrReq -> bDebug) 
OPTMG   (dbgImport       , pCurrReq -> bDebug) 

/* ---------------------------------------------------------------------------- */
/* read form input from http server... */
/* */

static int GetFormData (/*i/o*/ register req * r,
			/*in*/ char * pQueryString,
                        /*in*/ int    nLen)

    {
    int     num ;
    char *  p ;
    char *  pMem ;
    int     nVal ;
    int     nKey ;
    char *  pKey ;
    char *  pVal ;
    SV *    pSVV ;
    SV *    pSVK ;
    SV * *  ppSV ;

    EPENTRY (GetFormData) ;

    hv_clear (r -> pFormHash) ;
    hv_clear (r -> pFormSplitHash) ;
    
#ifdef HASHTEST
	pHash = perl_get_hv (sFormHashName, FALSE) ;
	if (pHash != r -> pFormHash)
	    {
	    strcpy (r -> errdat1, sFormHashName) ;
	    strcpy (r -> errdat2, " !!C-Adress differs from Perl-Adress!! in GetFormData" ) ;
	    LogError (r, rcHashError) ;
	    }
#endif

    if (nLen == 0)
        return ok ;
    
    if ((pMem = _malloc (r, nLen + 4)) == NULL)
        return rcOutOfMemory ;

    p = pMem ;


    nKey = nVal = 0 ;
    pKey = pVal = p ;
    while (1)
        {
        switch (nLen > 0?*pQueryString:'\0')
            {
            case '+':
                pQueryString++ ;
                nLen-- ;
                *p++ = ' ' ;
                break ;
            
            case '%':
                pQueryString++ ;
                nLen-- ;
                num = 0 ;
                if (*pQueryString)
                    {
                    if (toupper (*pQueryString) >= 'A')
                        num += (toupper (*pQueryString) - 'A' + 10) << 4 ;
                    else
                        num += ((*pQueryString) - '0') << 4 ;
                    pQueryString++ ;
                    }
                if (*pQueryString)
                    {
                    if (toupper (*pQueryString) >= 'A')
                        num += (toupper (*pQueryString) - 'A' + 10) ;
                    else
                        num += ((*pQueryString) - '0') ;
                    pQueryString++ ;
                    nLen-- ;
                    }
                *p++ = num ;
                break ;
            case '=':
                nKey = p - pKey ;
                *p++ = r -> pConf -> cMultFieldSep ;
                nVal = 0 ;
                pVal = p ;
                pQueryString++ ;
                nLen-- ;
                break ;
            case ';':
            case '&':
                pQueryString++ ;
                nLen-- ;
            case '\0':
                nVal = p - pVal ;
                *p++ = '\0' ;
            
                if (nKey > 0 && (nVal > 0 || (r -> bOptions & optAllFormData)))
                    {
                    char * sid = r -> pConf -> sCookieName ;
		    if (sid)
			{ /* remove session id  */
			if (strncmp (pKey, sid, nKey) != 0)
			    sid = NULL ;
			}

		    if (sid == NULL)
			{ /* field is not the session id */
			if (pVal > pKey)
			    pVal[-1] = '\0' ;
                    
			if ((ppSV = hv_fetch (r -> pFormHash, pKey, nKey, 0)))
			    { /* Field exists already -> append separator and field value */
			    sv_catpvn (*ppSV, &r ->  pConf -> cMultFieldSep, 1) ;
			    sv_catpvn (*ppSV, pVal, nVal) ;
			    }
			else
			    { /* New Field -> store it */
			    pSVV = newSVpv (pVal, nVal) ;
			    if (hv_store (r -> pFormHash, pKey, nKey, pSVV, 0) == NULL)
				{
				_free (r, pMem) ;
				return rcHashError ;
				}

			    pSVK = newSVpv (pKey, nKey) ;

			    av_push (r -> pFormArray, pSVK) ;
			    }

                
			if (r -> bDebug & dbgForm)
			    lprintf (r, "[%d]FORM: %s=%s\n", r -> nPid, pKey, pVal) ; 
			}
                    }
                pKey = pVal = p ;
                nKey = nVal = 0 ;
                
                if (*pQueryString == '\0')
                    {
                    _free (r, pMem) ;
                    return ok ;
                    }
                
                
                break ;
            default:
                *p++ = *pQueryString++ ;
                nLen-- ;
                break ;
            }
        }

    }

#ifdef comment

/* ---------------------------------------------------------------------------- */
/* read input from cgi process... */
/* */


static int GetInputData_CGIProcess (/*i/o*/ register req * r)

    {
    char *  p ;
    int     rc = ok ;
    int  state = 0 ;
    int  len   = 0 ;
    char sLine [1024] ;
    SV * pSVE ;
    int  savewarn = dowarn ;
    dowarn = 0 ; /* no warnings here */
    
    EPENTRY (GetInputData_CGIProcess) ;

    hv_clear (r -> pEnvHash) ;


    if (r -> bDebug)
        lprintf (r, "\n[%d]Waiting for Request... SVs: %d OBJs: %d\n", r -> nPid, sv_count, sv_objcount) ;

    if ((rc = OpenInput (r, sCmdFifo)) != ok)
        {
        dowarn = savewarn ;
        return rc ;
        }


    if (r -> bDebug)
        lprintf (r, "[%d]Processing Request...\n", r -> nPid) ;
    
    while (igets (sLine, sizeof (sLine)))
        {
        len = strlen (sLine) ; 
        while (len >= 0 && isspace (sLine [--len]))
            ;
        sLine [len + 1] = '\0' ;
        

        if (strcmp (sLine, "----") == 0)
            { state = 1 ; if (r -> bDebug) lprintf (r, "[%d]Environment...\n", r -> nPid) ;}
        else if (strcmp (sLine, "****") == 0)
            { state = 2 ;  if (r -> bDebug) lprintf (r,  "[%d]Formdata...\n", r -> nPid) ;}
        else if (state == 1)
            {
            p = strchr (sLine, '=') ;
            *p = '\0' ;
            p++ ;

            pSVE = newSVpv (p, strlen (p)) ;

            if (hv_store (r -> pEnvHash, sLine, strlen (sLine), pSVE, 0) == NULL)
                {
                dowarn = savewarn ;
                return rcHashError ;
                }
            if (r -> bDebug & dbgEnv)
                lprintf (r,  "[%d]ENV:  %s=%s\n", r -> nPid, sLine, p) ;
            }
        else if (state == 2)
            {
            len = atoi (sLine) ;
            if ((p = _malloc (len + 1)) == NULL)
                {
                dowarn = savewarn ;
                return rcOutOfMemory ;
                }
            iread (p, len) ;
            p[len] = '\0' ;
            rc = GetFormData (p, len) ;
            _free (p) ;
            break ;
            }
        else
            { if (r -> bDebug) lprintf (r, "[%d]Unknown Input: %s\n", r -> nPid, sLine) ;}

        }
        
    CloseInput () ;
    
    dowarn = savewarn ;
    return rc ;
    }
                        
#endif

/* ---------------------------------------------------------------------------- */
/* get form data when running as cgi script... */
/* */


static int GetInputData_CGIScript (/*i/o*/ register req * r)

    {
    char *  p = NULL ;
    char *  f ;
    int     rc = ok ;
    STRLEN  len   = 0 ;
    char    sLen [20] ;
    

    EPENTRY (GetInputData_CGIScript) ;

#ifdef APACHE
    if (r -> pApacheReq && (r -> bDebug & dbgHeadersIn))
        {
        int i;
        array_header *hdrs_arr;
        table_entry  *hdrs;

        hdrs_arr = table_elts (r -> pApacheReq->headers_in);
        hdrs = (table_entry *)hdrs_arr->elts;

        lprintf (r,  "[%d]HDR:  %d\n", r -> nPid, hdrs_arr->nelts) ; 
        for (i = 0; i < hdrs_arr->nelts; ++i)
	    if (hdrs[i].key)
                lprintf (r,  "[%d]HDR:  %s=%s\n", r -> nPid, hdrs[i].key, hdrs[i].val) ; 
        }
#endif

    if (r -> bDebug & dbgEnv)
        {
        SV *   psv ;
        HE *   pEntry ;
        char * pKey ;
        I32    l ;
        int  savewarn = dowarn ;
        dowarn = 0 ; /* no warnings here */
        
        hv_iterinit (r -> pEnvHash) ;
        while ((pEntry = hv_iternext (r -> pEnvHash)))
            {
            pKey = hv_iterkey (pEntry, &l) ;
            psv  = hv_iterval (r -> pEnvHash, pEntry) ;

                lprintf (r,  "[%d]ENV:  %s=%s\n", r -> nPid, pKey, SvPV (psv, na)) ; 
            }
        dowarn = savewarn ;
        }
    /* print out of env set tainted, so reset it now */
    tainted = 0 ;

#ifdef APACHE
    if (r -> pApacheReq)
        {
        const char * sLength = table_get(r -> pApacheReq->headers_in, "Content-Length") ;
	len = sLength?atoi (sLength):0 ;
	}
    else
#endif
	{
	sLen [0] = '\0' ;
	GetHashValue (r -> pEnvHash, "CONTENT_LENGTH", sizeof (sLen) - 1, sLen) ;
	len = atoi (sLen) ;
	}

    if (len == 0)
        {
        SV * * ppSV = hv_fetch(r -> pEnvHash, "QUERY_STRING", sizeof ("QUERY_STRING") - 1, 0) ;  
        if (ppSV != NULL)
            {
            p = SvPV (*ppSV ,len) ;
            }
        else
            len = 0 ;
        f = NULL ;
        }
    else
        {
        if ((p = _malloc (r, len + 1)) == NULL)
            return rcOutOfMemory ;

        if ((rc = OpenInput (r, NULL)) != ok)
            {
            _free (r, p) ;
            return rc ;
            }
        iread (r, p, len) ;
        CloseInput (r) ;
        
        p[len] = '\0' ;
        f = p ;
        }
        
    if (r -> bDebug)
        lprintf (r,  "[%d]Formdata... length = %d\n", r -> nPid, len) ;    

    rc = GetFormData (r, p, len) ;
    
#ifdef EP2
    if (!f && len > 0)
	{
        if ((f = _malloc (r, len + 1)) == NULL)
            return rcOutOfMemory ;

	memcpy (f, p, len) ;
        p[len] = '\0' ;
	}
    if (len > 0)
	{
	r -> sQueryInfo = f ;
	f[len] = '\0' ;
	}
#else
    if (f)
        _free (r, f) ;
#endif        
    
    tainted = 0 ;

    return rc ;
    }


/* ---------------------------------------------------------------------------- */
/* scan commands and evals ([x ... x] sequenz) ... */
/* */
/* p points to '[' */
/* */


    
static int ScanCmdEvals (/*i/o*/ register req * r,
			/*in*/ char *   p)
    
    
    { 
    int     rc ;
    char *  c ;
    char *  a ;
    char    nType ;
    SV *    pRet ;
    struct tCmd * pCmd ;
    char *  pAfterWS ;
    int     nFilepos = p - r -> Buf.pBuf ;
    SV **   ppSV ;
    AV *    pAV ;
    HV *    pHV ;
    STRLEN  l ;
    I32     li ;

    EPENTRY (ScanCmdEvals) ;
    
    p++ ;

    r -> Buf.pCurrPos = p ;

    if ((nType = *p++) == '\0')
        return ok ;

    r -> Buf.pCurrPos = p ;

    if (nType != '+' && nType != '-' && nType != '$' && nType != '!' && nType != '#')
        { /* escape (for [[ -> [) */
        if (r -> CmdStack.State.bProcessCmds == cmdAll)
            {
            if (nType != '[') 
                oputc (r, '[') ;
            oputc (r, nType) ;
            }
        return ok ;
        }

    /* end known ? */

    ppSV = hv_fetch(r -> Buf.pFile -> pCacheHash, (char *)&nFilepos, sizeof (nFilepos), 1) ;  
    if (ppSV == NULL)
        return rcHashError ;

    if (*ppSV != NULL && SvTYPE (*ppSV) == SVt_IV)
        {
	p = p + SvIV(*ppSV) ;
        }
    else
	{
	do
	    { /* search end  */
	    p++ ;
    
	    if ((p = strchr (p, ']')) == NULL)
		break ;
	    }   
	while (p[-1] != nType) ;
	if (p == NULL)
	    { /* end not found */
	    sprintf (r -> errdat1, "%c]", nType) ; 
	    return rcMissingRight ;
	    }
        SvREFCNT_dec (*ppSV) ;  
        *ppSV = newSViv (p - r -> Buf.pCurrPos) ;
        /*SvREFCNT_inc (*ppSV) ;  */
	}
	
    p [-1] = '\0' ;
    p++ ;

    pAfterWS = p;

    if ((r -> bOptions & optKeepSpaces) == 0)
	{	    
	/* skip trailing whitespaces */
	while (isspace(*pAfterWS))
	    pAfterWS++ ;

	if (nType == '+' && pAfterWS > p)
	    pAfterWS-- ;
	}

    switch (nType)
        {
        case '+':
            if (r -> CmdStack.State.bProcessCmds == cmdAll)
                {
                r -> bEscModeSet = -1 ;
                r -> pNextEscape = r -> pCurrEscape ;
                rc = EvalTrans (r, r -> Buf.pCurrPos, (r -> Buf.pCurrPos - r -> Buf.pBuf), &pRet) ;
                if (rc != ok && rc != rcEvalErr)
                    return rc ;

                if (pRet)
                    {
		    if (r -> bEscInUrl && SvTYPE(pRet) == SVt_RV && (pAV = (AV *)SvRV(pRet)))
			{			    
			if (SvTYPE(pAV) == SVt_PVAV)
			    { /* Array reference inside URL */
			    SV ** ppSV ;
			    int i ;
			    int f = AvFILL(pAV)  ;
			    for (i = 0; i <= f; i++)
				{
				ppSV = av_fetch (pAV, i, 0) ;
				if (ppSV && *ppSV)
				    {
				    OutputToHtml (r, SvPV (*ppSV, l)) ;
				    }
				if ((i & 1) == 0)
				    oputc (r, '=' ) ;
				else if (i < f)
				    oputs (r, "&amp;") ;
				}
			    }
			else if (SvTYPE(pAV) == SVt_PVHV)
			    { /* Hash reference inside URL */
			    int         i = 0 ;
			    HE *	    pEntry ;
			    char *	    pKey ;
			    SV * 	    pSVValue ;
			    pHV = (HV *)pAV ;

			    hv_iterinit (pHV) ;
			    while (pEntry = hv_iternext (pHV))
				{
				if (i++ > 0)
				    oputs (r, "&amp;") ;
				pKey     = hv_iterkey (pEntry, &li) ;
				OutputToHtml (r, pKey) ;
				oputc (r, '=' ) ;

				pSVValue = hv_iterval (pHV , pEntry) ;
				if (pSVValue)
				    OutputToHtml (r, SvPV (pSVValue, l)) ;
				}
			    }
			}
		    else
			{
			if (r -> pCurrEscape == NULL)
			    {
			    char * p = SvPV (pRet, l) ;
			    owrite (r, p, l) ;
			    }
			else
			    OutputToHtml (r, SvPV (pRet, l)) ;
			}
		    SvREFCNT_dec (pRet) ;
		    }
		r -> pCurrEscape = r -> pNextEscape ;
                r -> bEscModeSet = 0 ;
                }

            p [-2] = nType ;
            r -> Buf.pCurrPos = pAfterWS ;

        
            break ;
        case '-':
            if (r -> CmdStack.State.bProcessCmds == cmdAll)
                {
                rc = EvalTrans (r, r -> Buf.pCurrPos, (r -> Buf.pCurrPos - r -> Buf.pBuf), &pRet) ;
                if (rc != ok && rc != rcEvalErr)
                    return rc ;
                if (pRet)
                    SvREFCNT_dec (pRet) ;
                }

            p [-2] = nType ;
            r -> Buf.pCurrPos = pAfterWS ;

            break ;
        case '!':
            if (r -> CmdStack.State.bProcessCmds == cmdAll)
                {
                rc = EvalTransOnFirstCall (r, r -> Buf.pCurrPos, (r -> Buf.pCurrPos - r -> Buf.pBuf), &pRet) ;
                if (rc != ok && rc != rcEvalErr)
                    return rc ;
                if (pRet)
                    SvREFCNT_dec (pRet) ;
                }

            p [-2] = nType ;
            r -> Buf.pCurrPos = pAfterWS ;

            break ;
        case '#':
            /* just skip comments */
            p [-2] = nType ;
            r -> Buf.pCurrPos = pAfterWS ;

            break ;
        case '$':
            TransHtml (r, r -> Buf.pCurrPos, 0) ;

            /* skip spaces before command */
            while (*r -> Buf.pCurrPos != '\0' && isspace (*r -> Buf.pCurrPos))
                    r -> Buf.pCurrPos++ ;

            /* c holds the start of the command */
            a = c = r -> Buf.pCurrPos ;
            while (*a != '\0' && isalpha (*a))
                a++ ;

            /* a points to first char after command */

            r -> Buf.pCurrPos = p ;

            if ((rc = SearchCmd (r, c, a-c, a, FALSE, &pCmd)) != ok)
                return rc ;
        
        
            if ((rc = ProcessCmd (r, pCmd, a)) != ok)
		{
		p [-2] = nType ;
                return rc ;
		}

            p [-2] = nType ;
            if (r -> Buf.pCurrPos == p)
                r -> Buf.pCurrPos = pAfterWS ;

            break ;
        }

    return ok ;
    }

    
/* ---------------------------------------------------------------------------- */
/* process commands and evals in a string ... */
/* */
/* pIn   points to the string to process */
/* pOut  pointer to a pointer to a buffer for the output, maybe point to pIn at */
/*       exit if nothing to do or the buffer is filled with processed output */
/* nSize size of outputbuffer */
/* */


    
int ScanCmdEvalsInString (/*i/o*/ register req * r,
			  /*in*/  char *   pIn,
                          /*out*/ char * * pOut,
                          /*in*/  size_t   nSize,
                          /*out*/ char * * pFree)
    
    
    { 
    int    rc ;
    char * pSaveCurrPos  ;
    char * pSaveCurrStart ;
    char * pSaveEndPos ;
    char * pSaveLineNo ;
    char * p = strchr (pIn, '[');    


    EPENTRY (ScanCmdEvalsInString) ;

    *pFree = NULL ;
    if (p == NULL)
        {
        /* lprintf (r, "SCEV nothing sArg = %s\n", pIn) ; */
        *pOut = pIn ; /* Nothing to do */
        return ok ;
        }
    /* lprintf (r, "SCEV sArg = %s, p = %s\n", pIn, p) ; */

    /* save global vars */
    pSaveCurrPos   = r -> Buf.pCurrPos ;
    pSaveCurrStart = r -> Buf.pCurrStart ;
    pSaveEndPos    = r -> Buf.pEndPos ;
    pSaveLineNo    = r -> Buf.pLineNoCurrPos ;
    if (r -> Buf.pLineNoCurrPos == NULL)
        r -> Buf.pLineNoCurrPos = r -> Buf.pCurrPos ; /* save it for line no calculation */
    

    r -> Buf.pCurrPos = pIn ;
    r -> Buf.pEndPos  = pIn + strlen (pIn) ;

    *pOut = _malloc (r, nSize) ;
    if (*pOut == NULL)
        return rcOutOfMemory ;

    OutputToMemBuf (r, *pOut, nSize) ;

    rc = ok ;
    while (r -> Buf.pCurrPos < r -> Buf.pEndPos && rc == ok)
        {
        /* */
        /* execute [x ... x] and replace them if nessecary */
        /* */
        if (p == NULL || *p == '\0')
            { /* output the rest of html */
            owrite (r, r -> Buf.pCurrPos, r -> Buf.pEndPos - r -> Buf.pCurrPos) ;
            break ;
            }
        
        if (r -> CmdStack.State.bProcessCmds == cmdAll)
            {
            /* output until next cmd */
            owrite (r, r -> Buf.pCurrPos, p - r -> Buf.pCurrPos) ;
            }
        
        if (r -> bDebug & dbgSource)
            {
            char * s = p ;
            char * n ;

            while (*s && isspace (*s))
                s++ ;
            
            if (*s)
                {
                n = strchr (s, '\n') ;
#ifdef CLOCKS_PER_SEC
                if (r -> bDebug & dbgProfile)
                    if (n)
                        lprintf (r, "[%d]SRC: Time: %d ms  %*.*s\n", r -> nPid, ((clock () - r -> startclock) * 1000 / CLOCKS_PER_SEC), n-s, n-s, s) ;
                    else
                        lprintf (r, "[%d]SRC: Time: %d ms  %70.70s\n", r -> nPid, ((clock () - r -> startclock) * 1000 / CLOCKS_PER_SEC), s) ;
                else
#endif
                    if (n)
                        lprintf (r, "[%d]SRC: %*.*s\n", r -> nPid, n-s, n-s, s) ;
                    else
                        lprintf (r, "[%d]SRC: %70.70s\n", r -> nPid, s) ;

                }
            }        

        
        r -> Buf.pCurrStart = p ;
        rc = ScanCmdEvals (r, p) ;

        p = strchr (r -> Buf.pCurrPos, '[') ;
        }
    
    *pFree = *pOut = OutputToStd (r) ;

    r -> Buf.pCurrPos   = pSaveCurrPos ;
    r -> Buf.pCurrStart = pSaveCurrStart ;
    r -> Buf.pEndPos    = pSaveEndPos ;
    r -> Buf.pLineNoCurrPos = pSaveLineNo ;
    
    return rc ;
    }
            
/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* scan html tag ...                                                            */
/*                                                                              */
/* p points to '<'                                                              */
/*                                                                              */
/* ---------------------------------------------------------------------------- */

static int ScanHtmlTag (/*i/o*/ register req * r,
			/*in*/ char *   p)

    { 
    int  rc ;
    char ec ;
    char ea = 0 ;
    char * pec ;
    char * pea ;
    char * pCmd ;
    char * pArg ;
    char * pArgBuf  = NULL ;
    char * pFreeBuf = NULL ;
    struct tCmd * pCmdInfo ;



    EPENTRY (ScanHtmlTag) ;
    
    
    r -> Buf.pCurrTag = p ;     /* save start of html tag */

    /* skip space */
    p++ ;
    while (*p != '\0' && isspace (*p))
            p++ ;
    
    pCmd = p ;               /* start of tag name */
    while (*p != '\0' && !isspace (*p) && *p != '>')
        p++ ;

    ec = *p ;              /* save first char after tag name */
    pec = p ;
    *p++ = '\0' ;          /* set end of tag name to \0 */

    if ((rc = SearchCmd (r, pCmd, pec - pCmd, "", TRUE, &pCmdInfo)) != ok)
        {
        *pec = ec ;
        oputc (r, *r -> Buf.pCurrTag) ;
        r -> Buf.pCurrPos = r -> Buf.pCurrTag + 1 ;  
        if (rc == rcCmdNotFound)
            return ok ;    /* ignore this html tag */
        return rc ;
        }


    /* look if there are any arguments */    
    
    pArg = p ;             /* start of arguments */
    if (ec == '>')
        { /* No Arguments */
        pArg = p - 1 ;
        pea = NULL ;
        }
    else
        {
        /* get end of tag, skip everything inside [+/- ... -/+] */

        char nType = '\0';
        while ((*p != '>' || nType) && *p != '\0')
            {
            if (nType == '\0' && *p == '[' && (p[1] == '+' || p[1] == '-' || p[1] == '$' || p[1] == '!' || p[1] == '#'))
                nType = *++p ;
            else if (nType && *p == nType && p[1] == ']')
                {
                nType = '\0';
                p++ ;
                }

            p++;
            }

        if (*p == '>')
            {
            ea = *p ;
            pea = p ;
            *p = '\0' ;            /* set end of tag arguments to \0 */
            p++ ;
            }
        else
            {
            p = pArg + strlen (pArg) ;
            pea = NULL ;
            }
        }

    r -> Buf.pCurrPos = p ;    /* r -> Buf.pCurrPos = first char after whole tag */

    
    if (*pArg != '\0' && pCmdInfo -> bScanArg)
    	{
        if ((rc = ScanCmdEvalsInString (r, (char *)pArg, &pArgBuf, nInitialScanOutputSize, &pFreeBuf)) != ok)
            {
            if (pFreeBuf)
                _free (r, pFreeBuf) ;
            return rc ;
            }
    	}
    else
    	pArgBuf = pArg ;
    
    
    /* see if knwon html tag and execute */

    if ((rc = ProcessCmd (r, pCmdInfo, pArgBuf)) != ok)
        {
        if (rc == rcCmdNotFound)
            {
              /* only write html tag start char and */
            /*p = pCurrPos = pCurrTag + 1 ;   */    /* check if more to exceute within html tag */
            }
        else
            {
            if (pFreeBuf)
                _free (r, pFreeBuf) ;
            
            *pec = ec ;              /* restore first char after tag name */
            if (pea)
                *pea = ea ;              /* restore first char after tag arguments */

            return rc ;
            }
        }


    if (p == r -> Buf.pCurrPos && r -> Buf.pCurrPos) /* if CurrPos didn't change write out html tag as it is */
        {
        if (pArg == pArgBuf)
            { /* write unmodified tag */    
            *pec = ec ;              /* restore first char after tag name */
            if (pea)
                *pea = ea ;              /* restore first char after tag arguments */

            oputc (r, *r -> Buf.pCurrTag) ;
            r -> Buf.pCurrPos = r -> Buf.pCurrTag + 1 ;
            }
        else
            { /* write tag with interpreted args */
            oputs (r, r -> Buf.pCurrTag) ;
            oputc (r, ' ') ;
            oputs (r, pArgBuf) ;
            oputc (r, '>') ;
            *pec = ec ;              /* restore first char after tag name */
            if (pea)
                *pea = ea ;              /* restore first char after tag arguments */

            }
        }
    else
        {
        *pec = ec ;              /* restore first char after tag name */
        if (pea)
            *pea = ea ;              /* restore first char after tag arguments */
        }

    if (r -> Buf.pCurrPos == NULL)
        r -> Buf.pCurrPos = p ; /* html tag is written by command handler */

    if (pFreeBuf)
        _free (r, pFreeBuf) ;

    r -> Buf.pCurrTag = NULL ;

    return ok ;    
    }

    
/* ---------------------------------------------------------------------------- */
/* add magic to integer var */
/* */
/* in  sVarName = Name of varibale */
/* in  pVirtTab = pointer to virtual table */
/* */
/* ---------------------------------------------------------------------------- */

static int AddMagic (/*i/o*/ register req * r,
			/*in*/ char *     sVarName,
                     /*in*/ MGVTBL *   pVirtTab) 

    {
    SV * pSV ;
    struct magic * pMagic ;

    EPENTRY (AddMagic) ;

    
    pSV = perl_get_sv (sVarName, TRUE) ;
    sv_magic (pSV, NULL, 0, sVarName, strlen (sVarName)) ;
    sv_setiv (pSV, 0) ;
    pMagic = mg_find (pSV, 0) ;

    if (pMagic)
        pMagic -> mg_virtual = pVirtTab ;
    else
        {
        LogError (r, rcMagicError) ;
        return 1 ;
        }


    return ok ;
    }

    
/* ---------------------------------------------------------------------------- */
/* add magic to array								*/
/*										*/
/* in  sVarName = Name of varibale						*/
/* in  pVirtTab = pointer to virtual table					*/
/*										*/
/* ---------------------------------------------------------------------------- */

int AddMagicAV (/*i/o*/ register req * r,
		/*in*/ char *     sVarName,
                /*in*/ MGVTBL *   pVirtTab) 

    {
    SV * pSV ;
    struct magic * pMagic ;

    EPENTRY (AddMagicAV) ;

    
    pSV = (SV *)perl_get_av (sVarName, TRUE) ;
    sv_magic (pSV, NULL, 'P', sVarName, strlen (sVarName)) ;
    pMagic = mg_find (pSV, 0) ;

    if (pMagic)
        pMagic -> mg_virtual = pVirtTab ;
    else
        {
        LogError (r, rcMagicError) ;
        return 1 ;
        }


    return ok ;
    }


/* ---------------------------------------------------------------------------- */
/* init embperl module */
/* */
/* in  nIOType = type of requested i/o */
/* */
/* ---------------------------------------------------------------------------- */

int Init        (/*in*/ int           _nIOType,
                 /*in*/ const char *  sLogFile, 
                 /*in*/ int           nDebugDefault)

    {
    int     rc ;
#ifdef EP2
    HV *   pTokenHash ;
#endif

    req * r = &InitialReq ;
    
    pCurrReq = r ;

#if defined (_DEBUG) && defined (WIN32)
    _CrtSetReportHook( EmbperlCRTDebugOutput );
#endif

    r -> nIOType = _nIOType ;

#ifdef APACHE
    r -> pApacheReq = NULL ;
    if (_nIOType == epIOMod_Perl)
	{
	embperl_ApacheAddModule () ;
	}
#endif
    r -> bReqRunning = 0 ;
    
    r -> bDebug = nDebugDefault ;
    
    r -> nPid = getpid () ;

    r -> Buf.nSourceline = 1 ;
    r -> Buf.pSourcelinePos = NULL ;    
    r -> Buf.pLineNoCurrPos = NULL ;    

    r -> nEscMode = escStd ;
    r -> nCurrEscMode = escStd ;

    if ((rc = OpenLog (r, sLogFile, ((r -> bDebug & dbgFunc) || (r -> bOptions & optOpenLogEarly))?1:0)) != ok)
        { 
        r -> bDebug = 0 ; /* Turn debbuging off, only errors will go to stderr */
        LogError (r, rc) ;
        }

    EPENTRY (iembperl_init) ;

    if (r -> bDebug)
        {
        char * p ;

        switch (r -> nIOType)
            {
        #ifdef APACHE
            case epIOMod_Perl: p = "mod_perl"; break ;
        #else
            case epIOMod_Perl: p = "mod_perl UNSUPPORTED"; break ;
        #endif
            case epIOPerl:     p = "Offline"; break ;
            case epIOCGI:      p = "CGI-Script"; break ;
            case epIOProcess:  p = "Demon"; break ;
            default: p = "unknown" ; break ;
            }
        
        /* lprintf (r, "[%d]INIT: Embperl %s starting... mode = %s (%d)\n", nPid, sVersion, p, nIOType) ; */
        }


#ifndef APACHE
    if (r -> nIOType == epIOMod_Perl)
        {
        LogError (r, rcNotCompiledForModPerl) ;
        return 1 ;
        }
#endif

    if (bInitDone)
        return ok ; /* the rest was alreay done */

    /*
    if ((pSubArgsAV = perl_get_av ("_", FALSE)) == NULL)
        {
        LogError (r, rcPerlVarError) ;
        return 1 ;
        }
    */

    if ((r -> pFormHash = perl_get_hv (sFormHashName, TRUE)) == NULL)
        {
        LogError (r, rcHashError) ;
        return 1 ;
        }

    if ((r -> pUserHash = perl_get_hv (sUserHashName, TRUE)) == NULL)
        {
        LogError (r, rcHashError) ;
        return 1 ;
        }

    if ((r -> pStateHash = perl_get_hv (sStateHashName, TRUE)) == NULL)
        {
        LogError (r, rcHashError) ;
        return 1 ;
        }

    if ((r -> pModHash = perl_get_hv (sModHashName, TRUE)) == NULL)
        {
        LogError (r, rcHashError) ;
        return 1 ;
        }


    if ((r -> pFormSplitHash = perl_get_hv (sFormSplitHashName, TRUE)) == NULL)
        {
        LogError (r, rcHashError) ;
        return 1 ;
        }

    if ((r -> pFormArray = perl_get_av (sFormArrayName, TRUE)) == NULL)
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }

    /*
    if ((r -> pErrArray = perl_get_av (sErrArrayName, TRUE)) == NULL)
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }

    if ((r -> pErrFill = perl_get_av (sErrFillName, TRUE)) == NULL)
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }

    if ((r -> pErrState = perl_get_av (sErrStateName, TRUE)) == NULL)
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }

    */
    if ((r -> pHeaderHash = perl_get_hv (sHeaderHashName, TRUE)) == NULL)
        {
        LogError (r, rcHashError) ;
        return 1 ;
        }

    if ((r -> pInputHash = perl_get_hv (sInputHashName, TRUE)) == NULL)
        {
        LogError (r,  rcHashError) ;
        return 1 ;
        }

    if ((r -> pEnvHash = perl_get_hv (sEnvHashName, TRUE)) == NULL)
        {
        LogError (r,  rcHashError) ;
        return 1 ;
        }

    if (!(pCacheHash = newHV ()))
        {
        LogError (r,  rcHashError) ;
        return 1 ;
        }

    if (!(r -> pErrFill = newAV ()))
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }
    
    if (!(r -> pErrState = newAV ()))
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }
    
    if (!(r -> pErrArray = newAV ()))
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }
    
#ifdef EP2
    if (!(r -> pDomTreeAV = newAV ()))
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }

    if (!(r -> pCleanupAV = newAV ()))
        {
        LogError (r, rcArrayError) ;
        return 1 ;
        }

#endif    
    
    rc = 0 ;

    ADDINTMG (TabCount) 
    ADDINTMG (TabRow) 
    ADDINTMG (TabCol) 
    ADDINTMG (TabMaxRow) 
    ADDINTMG (TabMaxCol) 
    ADDINTMG (TabMode) 
    ADDINTMG (EscMode) 
#ifdef EP2
    ADDINTMG (CurrNode) 
#endif    
    
    ADDOPTMG (optDisableVarCleanup      ) 
    ADDOPTMG (optDisableEmbperlErrorPage) 
    ADDOPTMG (optReturnError) 
    ADDOPTMG (optSafeNamespace          ) 
    ADDOPTMG (optOpcodeMask             ) 
    ADDOPTMG (optRawInput               ) 
    ADDOPTMG (optSendHttpHeader         ) 
    ADDOPTMG (optDisableChdir           ) 
    ADDOPTMG (optDisableHtmlScan        ) 
    ADDOPTMG (optEarlyHttpHeader        ) 
    ADDOPTMG (optDisableFormData        ) 
    ADDOPTMG (optDisableInputScan       ) 
    ADDOPTMG (optDisableTableScan       ) 
    ADDOPTMG (optDisableMetaScan        ) 
    ADDOPTMG (optAllFormData            ) 
    ADDOPTMG (optRedirectStdout         ) 
    ADDOPTMG (optUndefToEmptyValue      ) 
    ADDOPTMG (optNoHiddenEmptyValue     ) 
    ADDOPTMG (optAllowZeroFilesize      ) 
    ADDOPTMG (optKeepSrcInMemory       ) 
    ADDOPTMG (optKeepSpaces            ) 
    ADDOPTMG (optOpenLogEarly          ) 
    ADDOPTMG (optNoUncloseWarn         ) 

    ADDOPTMG   (dbgStd         ) 
    ADDOPTMG   (dbgMem         ) 
    ADDOPTMG   (dbgEval        ) 
    ADDOPTMG   (dbgCmd         ) 
    ADDOPTMG   (dbgEnv         ) 
    ADDOPTMG   (dbgForm        ) 
    ADDOPTMG   (dbgTab         ) 
    ADDOPTMG   (dbgInput       ) 
    ADDOPTMG   (dbgFlushOutput ) 
    ADDOPTMG   (dbgFlushLog    ) 
    ADDOPTMG   (dbgAllCmds     ) 
    ADDOPTMG   (dbgSource      ) 
    ADDOPTMG   (dbgFunc        ) 
    ADDOPTMG   (dbgLogLink     ) 
    ADDOPTMG   (dbgDefEval     ) 
    ADDOPTMG   (dbgHeadersIn   ) 
    ADDOPTMG   (dbgShowCleanup ) 
    ADDOPTMG   (dbgProfile     ) 
    ADDOPTMG   (dbgSession     ) 
    ADDOPTMG   (dbgImport      ) 
   
#ifdef EP2
    DomInit () ;
    Cache_Init () ;
    Provider_Init () ;
#ifdef XALAN
    embperl_Xalan_Init () ;
#endif
#ifdef LIBXSLT
    embperl_LibXSLT_Init () ;
#endif

#endif    
    
    bInitDone = 1 ;

    return rc ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* clean up embperl module                                                      */
/*                                                                              */
/* ---------------------------------------------------------------------------- */

int Term ()

    {
    req * r = pCurrReq ;

    EPENTRY (iembperl_term) ;
    
    if (!bInitDone)
        return ok ; 

    CloseLog (r) ;
    CloseOutput (r) ;
    
    return ok ;
    }



int ResetHandler (/*in*/ SV * pApacheReqSV)
    {
#ifdef APACHE
    request_rec * pReq = (request_rec *)SvIV((SV*)SvRV(pApacheReqSV));
    pReq -> handler = NULL ;
#endif

    return ok ;
    }


/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Localise op_mask then opmask_add()                                           */
/*                                                                              */
/* Just copied from Opcode.xs                                                   */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


static void
opmask_addlocal(SV *   opset,
                char * op_mask_buf) 
    {
    char *orig_op_mask = op_mask;
    int i,j;
    char *bitmask;
    STRLEN len;
    int myopcode  = 0;
    int opset_len = (maxo + 7) / 8 ;

    SAVEPPTR(op_mask);
    op_mask = &op_mask_buf[0];
    if (orig_op_mask)
	Copy(orig_op_mask, op_mask, maxo, char);
    else
	Zero(op_mask, maxo, char);


    /* OPCODES ALREADY MASKED ARE NEVER UNMASKED. See opmask_addlocal()	*/

    bitmask = SvPV(opset, len);
    for (i=0; i < opset_len; i++)
        {
	U16 bits = bitmask[i];
	if (!bits)
            {	/* optimise for sparse masks */
	    myopcode += 8;
	    continue;
	    }
	for (j=0; j < 8 && myopcode < maxo; )
	    op_mask[myopcode++] |= bits & (1 << j++);
        }
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Setup Configuration specficy data                                            */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


tConf * SetupConfData   (/*in*/ HV *   pReqInfo,
                         /*in*/ SV *   pOpcodeMask)

    {
#ifdef EP2
    SV * *   ppSV ;
    SV *     pSV ;
    SV * *   ppCV ;
    int	     rc ;
#endif

    tConf *  pConf = malloc (sizeof (tConf)) ;
    
    tainted = 0 ;

    if (!pConf)
        return NULL ;

    pConf -> bDebug =	    GetHashValueUInt (pReqInfo, "debug", pCurrReq -> pConf?pCurrReq -> pConf -> bDebug:pCurrReq -> bDebug) ;	    /* Debugging options */
    pConf -> bOptions =	    GetHashValueUInt (pReqInfo, "options",  pCurrReq -> pConf?pCurrReq -> pConf -> bOptions:pCurrReq -> bOptions) ;  /* Options */
    pConf -> nEscMode =	    GetHashValueInt (pReqInfo, "escmode",  pCurrReq -> pConf?pCurrReq -> pConf -> nEscMode:escStd) ;  /* EscMode */
    pConf -> sPackage =	    sstrdup (GetHashValueStr (pReqInfo, "package", NULL)) ;         /* Packagename */
    pConf -> sLogFilename = sstrdup (GetHashValueStr (pReqInfo, "log",  NULL)) ;            /* name of logfile */
    pConf -> sVirtLogURI  = sstrdup (GetHashValueStr (pReqInfo, "virtlog",  pCurrReq -> pConf?pCurrReq -> pConf -> sVirtLogURI:NULL)) ;        /* name of logfile */
    pConf -> pOpcodeMask  = pOpcodeMask ;                                                   /* Opcode mask (if any) */
    pConf -> sCookieName  = sstrdup (GetHashValueStr (pReqInfo, "cookie_name",  sCookieNameDefault))  ;   /* Name to use for cookie */
    pConf -> sCookieExpires = sstrdup (GetHashValueStr (pReqInfo, "cookie_expires",  ""))  ; /* cookie expiration time */
    pConf -> sCookieDomain = sstrdup (GetHashValueStr (pReqInfo, "cookie_domain",  "")) ; ; /* domain patter for which the cookie should be returned */
    pConf -> sCookiePath   = sstrdup (GetHashValueStr (pReqInfo, "cookie_path",  "")) ; ;   /* path to which cookie should be returned */
    pConf -> cMultFieldSep = '\t' ;
    pConf -> pOpenBracket  = "[*" ;
    pConf -> pCloseBracket = "*]" ;
    pConf -> sPath         = sstrdup (GetHashValueStr (pReqInfo, "path",  pCurrReq -> pConf?pCurrReq -> pConf -> sPath:NULL)) ;        /* file search path */
    pConf -> sReqFilename  = sstrdup (GetHashValueStr (pReqInfo, "reqfilename",  pCurrReq -> pConf?pCurrReq -> pConf -> sReqFilename:NULL)) ;        /* filename of original request */
    pConf -> pReqParameter = pReqInfo ;

#ifdef EP2
    pConf -> sRecipe =	    sstrdup (GetHashValueStr (pReqInfo, "recipe", "Embperl")) ;         /* Recipe name */
    pConf -> bEP1Compat	    = GetHashValueInt (pReqInfo, "ep1compat",  pCurrReq -> pConf?pCurrReq -> pConf -> bEP1Compat:pCurrReq -> bEP1Compat) ;  /* EP1Compat */

    pConf -> sCacheKey	    = sstrdup (GetHashValueStr (pReqInfo, "cache_key",  pCurrReq -> pConf?pCurrReq -> pConf -> sCacheKey:NULL)) ; ;
    pConf -> bCacheKeyOptions = GetHashValueInt (pReqInfo, "cache_key_options",  pCurrReq -> pConf?pCurrReq -> pConf -> bCacheKeyOptions:ckoptDefault) ;  

    ppCV			    =     hv_fetch(pReqInfo, "expires_func", sizeof ("expires_func") - 1, 0) ;  
    if (ppCV && *ppCV && SvOK (*ppCV))
	{
	if ((rc = EvalConfig (pCurrReq, *ppCV, 0, NULL, &pConf -> pExpiresCV)) != ok)
	    LogError (pCurrReq, rc) ;
	}
    else
	pConf -> pExpiresCV     =  pCurrReq -> pConf?pCurrReq -> pConf -> pExpiresCV:NULL ;
    

    ppCV			    =     hv_fetch(pReqInfo, "cache_key_func", sizeof ("cache_key_func") - 1, 0) ;  
    if (ppCV && *ppCV && SvOK (*ppCV))
	{
	if ((rc = EvalConfig (pCurrReq, *ppCV, 0, NULL, &pConf -> pCacheKeyCV)) != ok)
	    LogError (pCurrReq, rc) ;
	}
    else
	pConf -> pCacheKeyCV     =  pCurrReq -> pConf?pCurrReq -> pConf -> pCacheKeyCV:NULL ;
    
    pConf -> nExpiresIn	    = GetHashValueInt (pReqInfo, "expires_in",  pCurrReq -> pConf?pCurrReq -> pConf -> nExpiresIn:0) ;  
#endif


    return pConf ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Free Configuration specficy data                                             */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


void FreeConfData       (/*in*/ tConf *   pConf)

    {
    if (!pConf)
        return ;

    if (pConf -> sPackage)
        free (pConf -> sPackage) ;
    
    if (pConf -> sLogFilename)
        free (pConf -> sLogFilename) ;

    if (pConf -> sVirtLogURI)
        free (pConf -> sVirtLogURI) ;

    if (pConf -> sCookieName)
	free (pConf -> sCookieName) ;

    if (pConf -> sCookieExpires)
	free (pConf -> sCookieExpires) ;

    if (pConf -> sCookieDomain)
	free (pConf -> sCookieDomain) ;

    if (pConf -> sCookiePath)
	free (pConf -> sCookiePath) ;

    if (pConf -> sPath)
	free (pConf -> sPath) ;

    if (pConf -> sReqFilename)
	free (pConf -> sReqFilename) ;

#ifdef EP2
    if (pConf -> sRecipe)
	free (pConf -> sRecipe) ;
 
    if (pConf -> sCacheKey)
	free (pConf -> sCacheKey) ;
 
    if (pConf -> pCacheKeyCV)
	SvREFCNT_dec (pConf -> pCacheKeyCV) ;

    if (pConf -> pExpiresCV)
	SvREFCNT_dec (pConf -> pExpiresCV) ;
#endif

    free (pConf) ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Setup File specficy data                                                     */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


tFile * SetupFileData   (/*i/o*/ register req * r,
                         /*in*/  char *  sSourcefile,
                         /*in*/  double  mtime,
                         /*in*/  long    nFilesize,
                         /*in*/  int     nFirstLine,
                         /*in*/  tConf * pConf)

    {
    SV * *      ppSV ;
    tFile *     f ;
    char	txt [sizeof (sDefaultPackageName) + 50] ;
    char *	cache_key;
    int		cache_key_len;
    char olddir[PATH_MAX] = "" ;
    char *      pNew ;

    EPENTRY (SetupFileData) ;

    tainted = 0 ;

    /* Have we seen this sourcefile/package already ? */
    cache_key_len = strlen( sSourcefile ) ;
    if ( pConf->sPackage )
	cache_key_len += strlen( pConf->sPackage );
    
    /* is it a relativ filename? -> append path */
    if (!(sSourcefile[0] == '/' || 
          sSourcefile[0] == '\\' || 
          (isalpha(sSourcefile[0]) && sSourcefile[1] == ':' && 
	          (sSourcefile[2] == '\\' || sSourcefile[2] == '/')) ||
	    (r -> pInData && SvROK(r -> pInData))))
        getcwd (olddir, sizeof (olddir) - 1) ;

    if ( olddir[0] )
	cache_key_len += strlen( olddir );
        
    cache_key = _malloc( r, cache_key_len + 3 );
    strcpy( cache_key, sSourcefile );
    if ( pConf->sPackage )
	strcat( cache_key, pConf->sPackage );

    if ( olddir[0] )
	strcat( cache_key, olddir );


#ifdef EP2
    if ( pConf->bEP1Compat && !pConf->sPackage )
	{
	strcat( cache_key, "-1" ); /* make sure Embperl 1.x compatible files get another namespace */
	cache_key_len += 2 ;
	}
#endif

    ppSV = hv_fetch(pCacheHash, cache_key, cache_key_len, 0);  
    
    if (ppSV && *ppSV)
        {
        f = (tFile *)SvIV((SV*)SvRV(*ppSV)) ;
        
        if (mtime == 0 || f -> mtime != mtime)
            {
            hv_clear (f -> pCacheHash) ;

#ifdef EP2	
	    UndefSub (r, EPMAINSUB, f -> sCurrPackage) ;
#endif
            if (r -> bDebug)
                lprintf (r, "[%d]MEM: Reload %s in %s\n", r -> nPid,  sSourcefile, f -> sCurrPackage) ;

            f -> mtime       = mtime ;	 /* last modification time of file */
            f -> nFilesize   = nFilesize ;	 /* size of File */
	    f -> bKeep       = (r -> bOptions & optKeepSrcInMemory) != 0 ;
	    f -> nFirstLine  = nFirstLine ;
	    if (f -> pExportHash)
		{
		SvREFCNT_dec (f -> pExportHash) ;
		f -> pExportHash = NULL ;
		}
	    if (f -> pBufSV)
		{
		SvREFCNT_dec (f -> pBufSV) ;
		f -> pBufSV = NULL ;
		}
	    }
        pNew = "Found" ;
        }
    else
        { /* create new file structure */
        if ((f = malloc (sizeof (*f))) == NULL)
	    {
	    _free(r,cache_key);
            return NULL ;
	    }

        f -> sSourcefile = sstrdup (sSourcefile) ; /* Name of sourcefile */
        f -> mtime       = mtime ;	 /* last modification time of file */
        f -> nFilesize   = nFilesize ;	 /* size of File */
	f -> pBufSV      = NULL ;
	f -> pNext2Free  = NULL ;
	f -> bKeep       = (r -> bOptions & optKeepSrcInMemory) != 0;
	f -> pExportHash = NULL ;
	f -> nFirstLine  = nFirstLine ;

        f -> pCacheHash  = newHV () ;    /* Hash containing CVs to precompiled subs */

        if (pConf -> sPackage)
            f -> sCurrPackage = strdup (pConf -> sPackage) ; /* Package of file  */
        else
            {
            sprintf (txt, sDefaultPackageName, nPackNo++ ) ;
            f -> sCurrPackage = strdup (txt) ; /* Package of file  */
            }
        f -> nCurrPackage = strlen (f -> sCurrPackage); /* Package of file (length) */

        hv_store(pCacheHash, cache_key, cache_key_len, newRV_noinc (newSViv ((IV)f)), 0) ;  
    
        if (r -> bDebug)
            lprintf (r, "[%d]MEM: Load %s in %s\n", r -> nPid,  sSourcefile, f -> sCurrPackage) ;
        pNew = "New" ;
        }
    if (r -> bDebug)
        lprintf (r, "[%d]CACHE: %s File for '%s' (%x) in '%s' hash cache-key '%s'\n", r -> nPid,  pNew, f -> sSourcefile, f, f -> sCurrPackage, cache_key) ;
    
    _free(r,cache_key);

    return f ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Get file data from filename; preset if not present                           */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


tFile * GetFileData     (/*in*/  char *  sSourcefile,
                         /*in*/  char *  sPackage,
			 /*in*/  double  mtime,
			 /*in*/  int     bEP1Compat)
                        
    {
    SV * *      ppSV ;
    tFile *     f ;
    char	txt [sizeof (sDefaultPackageName) + 50] ;
    char *	cache_key;
    int		cache_key_len;
    char olddir[PATH_MAX] = "" ;
    char *      pNew ;
    
    EPENTRY (GetFileData) ;

    /* Have we seen this sourcefile/package already ? */
    cache_key_len = strlen( sSourcefile ) ;
    if ( sPackage && *sPackage)
	cache_key_len += strlen( sPackage );
    
    /* is it a relativ filename? -> append path */
    if (!(sSourcefile[0] == '/' || 
        sSourcefile[0] == '\\' || 
        (isalpha(sSourcefile[0]) && sSourcefile[1] == ':' && 
            (sSourcefile[2] == '\\' || sSourcefile[2] == '/'))))
        getcwd (olddir, sizeof (olddir) - 1) ;

    if ( olddir[0] )
	cache_key_len += strlen( olddir );
        
    cache_key = malloc(cache_key_len + 3 );
    strcpy( cache_key, sSourcefile );
    if ( sPackage && *sPackage)
	strcat( cache_key, sPackage );

    if ( olddir[0] )
	strcat( cache_key, olddir );


#ifdef EP2
    if ( bEP1Compat )
	{
	strcat( cache_key, "-1" ); /* make sure Embperl 1.x compatible files get another namespace */
	cache_key_len += 2 ;
	}
#endif

    ppSV = hv_fetch(pCacheHash, cache_key, cache_key_len, 0);  
    
    if (ppSV && *ppSV)
        {
        f = (tFile *)SvIV((SV*)SvRV(*ppSV)) ;
        
        if (mtime == 0 || f -> mtime != mtime)
            {
            hv_clear (f -> pCacheHash) ;
#ifdef EP2	
	    UndefSub (pCurrReq, f -> sCurrPackage, EPMAINSUB) ;
#endif
        
            f -> mtime       = -1 ;	 /* reset last modification time of file */
	    if (f -> pExportHash)
		{
		SvREFCNT_dec (f -> pExportHash) ;
		f -> pExportHash = NULL ;
		}
	    }
        pNew = "Found " ;
        }
    else
        { /* create new file structure */
        if ((f = malloc (sizeof (*f))) == NULL)
	    {
	    free(cache_key);
            return NULL ;
	    }

        f -> sSourcefile = sstrdup (sSourcefile) ; /* Name of sourcefile */
        f -> mtime       = -1 ;	 /* last modification time of file */
        f -> nFilesize   = 0 ;	 /* size of File */
	f -> pBufSV      = NULL ;
	f -> pNext2Free  = NULL ;
	f -> bKeep       = 0 ;
	f -> pExportHash = NULL ;
	f -> nFirstLine  = 0 ;

        f -> pCacheHash  = newHV () ;    /* Hash containing CVs to precompiled subs */

	if ( sPackage && *sPackage)
            f -> sCurrPackage = strdup (sPackage) ; /* Package of file  */
        else
            {
            sprintf (txt, sDefaultPackageName, nPackNo++ ) ;
            f -> sCurrPackage = strdup (txt) ; /* Package of file  */
            }
        f -> nCurrPackage = strlen (f -> sCurrPackage); /* Package of file (length) */

        hv_store(pCacheHash, cache_key, cache_key_len, newRV_noinc (newSViv ((IV)f)), 0) ;  
    
        pNew = "New " ;
        }

    if (pCurrReq -> bDebug)
        lprintf (pCurrReq, "[%d]CACHE: %s File for %s (%x) in %s hash cache-key %s\n", pCurrReq -> nPid,  pNew, f -> sSourcefile, f, f -> sCurrPackage, cache_key) ;
    
    free(cache_key);

    return f ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Free File buffer								*/
/*                                                                              */
/* ---------------------------------------------------------------------------- */


static void FreeFileBuf     (/*i/o*/ register req * r,
			     /*i/o*/ tFile * f)


    {
    if (!f -> bKeep && f -> pBufSV)
	{
	SvREFCNT_dec (f -> pBufSV) ;
	f -> pBufSV = NULL ;
        if (r -> bDebug)
            lprintf (r, "[%d]MEM: Free buffer for %s in %s\n", r -> nPid,  f -> sSourcefile, f -> sCurrPackage) ;
	}
    else if (r -> bDebug && !f -> pBufSV)
        lprintf (r, "[%d]MEM: Warning! buffer for %s in %s is NULL\n", r -> nPid,  f -> sSourcefile, f -> sCurrPackage) ;
    }


/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Create Session cookie                                                        */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


static SV * CreateSessionCookie (/*i/o*/ register req * r,
				 /*in*/  HV * pSessionHash,
				 /*in*/  char type,
                                 /*in*/  int  bReturnCookie)
    
    {
    SV **   ppSVID ;
    SV *    pSVID = NULL ;
    SV *    pSVUID = NULL ;
    MAGIC * pMG ;
    char *  pUID = NULL ;
    char *  pInitialUID = NULL ;
    STRLEN  ulen = 0 ;
    STRLEN  ilen = 0 ;
    IV	    bModified ;
    SV *    pCookie = NULL ;
    STRLEN  ldummy ;

    if (r -> nSessionMgnt)
	{			
	SV * pUserHashObj = NULL ;
	if ((pMG = mg_find((SV *)pSessionHash,'P')))
	    {
	    dSP;                            /* initialize stack pointer      */
	    int n ;
	    pUserHashObj = pMG -> mg_obj ;

	    PUSHMARK(sp);                   /* remember the stack pointer    */
	    XPUSHs(pUserHashObj) ;            /* push pointer to obeject */
	    XPUSHs(sv_2mortal(newSViv(bReturnCookie?0:1))) ;       /* init session if not for cookie */
	    PUTBACK;
	    n = perl_call_method ("getids", G_ARRAY) ; /* call the function             */
	    SPAGAIN;
	    if (n > 2)
		{
		int  savewarn = dowarn ;
		dowarn = 0 ; /* no warnings here */
		bModified = POPi ;
		pSVUID = POPs;
		pUID = SvPV (pSVUID, ulen) ;
		pSVID = POPs;
		pInitialUID = SvPV (pSVID, ilen) ;
		dowarn = savewarn ;
		}
	    PUTBACK;
	    }
	
	if (r -> bDebug & dbgSession)  
	    lprintf (r, "[%d]SES:  Received Cookie ID: %s  New Cookie ID: %s  %s data is%s modified\n", r -> nPid, pInitialUID, pUID, type == 's'?"State":"User", bModified?"":" NOT") ; 

	if (ilen > 0 && (ulen == 0 || (!bModified && strcmp ("!DELETE", pInitialUID) == 0)))
	    { /* delete cookie */
            if (bReturnCookie)
                {                    
                pCookie = newSVpvf ("%s%s=; expires=Thu, 1-Jan-1970 00:00:01 GMT%s%s%s%s",  r -> pConf -> sCookieName, type == 's'?"s":"",
			    r -> pConf -> sCookieDomain[0]?"; domain=":""  , r -> pConf -> sCookieDomain, 
			    r -> pConf -> sCookiePath[0]?"; path=":""      , r -> pConf -> sCookiePath) ;
		newSVpvf2(pCookie) ;
                }

	    if (r -> bDebug & dbgSession)  
		lprintf (r, "[%d]SES:  Delete Cookie -> %s\n", r -> nPid, SvPV(pCookie, ldummy)) ;
	    }
	else if (ulen > 0 && 
		    ((bModified && (ilen == 0 || strcmp (pInitialUID, pUID) !=0)) ||
		     (r -> nSessionMgnt & 4) || !bReturnCookie))
	    {
            if (bReturnCookie)
                {                    
	        pCookie = newSVpvf ("%s%s=%s%s%s%s%s%s%s",  r -> pConf -> sCookieName, type == 's'?"s":"", pUID,
			    r -> pConf -> sCookieDomain[0]?"; domain=":""  , r -> pConf -> sCookieDomain, 
			    r -> pConf -> sCookiePath[0]?"; path=":""      , r -> pConf -> sCookiePath, 
			    r -> pConf -> sCookieExpires[0]?"; expires=":"", r -> pConf -> sCookieExpires) ;
		newSVpvf2(pCookie) ;
	        if (r -> bDebug & dbgSession)  
		    lprintf (r, "[%d]SES:  Send Cookie -> %s\n", r -> nPid, SvPV(pCookie, ldummy)) ; 
                }
            else
                {
                pCookie = pSVUID ;
                }
	    }
	}
    return pCookie ;
    }
    

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Setup Request                                                                */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


tReq * SetupRequest (/*in*/ SV *    pApacheReqSV,
                     /*in*/ char *  sSourcefile,
                     /*in*/ double  mtime,
                     /*in*/ long    nFilesize,
                     /*in*/ int     nFirstLine,
                     /*in*/ char *  sOutputfile,
                     /*in*/ tConf * pConf,
                     /*in*/ int     nIOType,
                     /*in*/ SV *    pIn,
                     /*in*/ SV *    pOut,
		     /*in*/ char *  sSubName,
		     /*in*/ char *  sImport,
		     /*in*/ int	    nSessionMgnt,
                     /*in*/ tTokenTable * pTokenTable)

    {
    int     rc ;
    tReq *  r = pCurrReq ;
    char *  sMode ;
    tFile * pFile ;
    HV * pReqHV  ;
#ifdef EP2
    SV * * ppSV ;
    STRLEN len ;
#endif

    dTHR ;

	
    tainted         = 0 ;

    if (pConf -> bDebug)
	if ((rc = OpenLog (pCurrReq, NULL, 2)) != ok)
	    { 
	    LogError (pCurrReq, rc) ;
	    }

    EPENTRY (SetupRequest) ;

    if (pReqFree)
        {
        r = pReqFree ;
        pReqFree = pReqFree -> pNext ;
        memcpy (r, pCurrReq, (char *)&r -> zeroend - (char *)r) ;
        r -> pNext = NULL ;
        }
    else
        {
        if ((r = malloc (sizeof (tReq))) == NULL)
            return NULL ;
        
        memcpy (r, pCurrReq, sizeof (*r)) ;
        }
    
#ifdef EP2
    r -> nPhase  = phInit ;
#endif
    r -> bSubReq = !(pCurrReq == &InitialReq) ;

    r -> pLastReq = pCurrReq ;
    pCurrReq = r ;

#if defined (_DEBUG) && defined (WIN32)
    _CrtMemCheckpoint(&r -> MemCheckpoint);    
#endif    
#ifdef DMALLOC
    r -> MemCheckpoint = dmalloc_mark () ;   
#endif    

#ifdef APACHE
    if (SvROK (pApacheReqSV))
        r -> pApacheReq = (request_rec *)SvIV((SV*)SvRV(pApacheReqSV));
    else
        r -> pApacheReq = NULL ;
    r -> pApacheReqSV = pApacheReqSV ;
#endif


    if (!r -> pLastReq -> pReqSV)
	pReqHV = newHV () ;
    else
	{
	pReqHV = (HV *)SvRV (r -> pLastReq -> pReqSV) ;
	SvREFCNT_inc (pReqHV) ;
	}

    sv_unmagic ((SV *)pReqHV, '~') ;
    sv_magic ((SV *)pReqHV, NULL, '~', (char *)&r, sizeof (r)) ;
    r -> pReqSV = newRV_noinc ((SV *)pReqHV) ;
    if (!r -> pLastReq -> pReqSV)
	sv_bless (r -> pReqSV, gv_stashpv ("HTML::Embperl::Req", 0)) ;

    r -> startclock      = clock () ;
    r -> stsv_count      = sv_count ;
    r -> stsv_objcount   = sv_objcount ;
    r -> lstsv_count     = sv_count ;
    r -> lstsv_objcount  = sv_objcount ;

    r -> nPid            = getpid () ; /* reget pid, because it could be chaned when loaded with PerlModule */
    r -> bDebug          = pConf -> bDebug ;
#ifdef EP2
    r -> bEP1Compat      = pConf -> bEP1Compat ;
    ppSV = hv_fetch(r -> pEnvHash, "PATH_INFO", sizeof ("PATH_INFO") - 1, 0) ;  
    if (ppSV)
        r -> sPathInfo = SvPV (*ppSV ,len) ;
    r -> pTokenTable = pTokenTable ;    
#else
    r -> pTokenTable = (void *)pTokenTable ;    
#endif
    if (rc != ok)
        r -> bDebug = 0 ; /* Turn debbuging off, only errors will go to stderr if logfile not open */
    r -> bOptions        = pConf -> bOptions ;
    /*r -> nIOType         = InitialReq.nIOType ;*/

    r -> sSubName        = sSubName ;
    r -> nSessionMgnt    = nSessionMgnt ;    
    r -> pConf           = pConf ;
    r -> nInsideSub	 = 0 ;
    r -> bExit  	 = 0 ;
    
    r -> nRequestCount   = nRequestCount++ ;
    r -> nRequestTime = time(NULL) ;
    getcwd (r->sCWD, sizeof (r->sCWD)-1) ;
    r -> sResetDir[0] = '\0' ;
    r -> pOutData        = pOut ;
    r -> pInData         = pIn ;

    r -> pFiles2Free	 = NULL ;
    if (r -> bSubReq && sSourcefile[0] == '?' && sSubName && sSubName[0] != '\0')
	{
	pFile = r -> pLastReq -> Buf.pFile ;
	r -> bOptions |= optDisableChdir ;
	}
    else
	{
	if ((pFile = SetupFileData (r, sSourcefile, mtime, nFilesize, nFirstLine, pConf)) == NULL)
            return NULL ;
	}

    if (r -> bSubReq && sOutputfile[0] == 1 && r -> pLastReq && !SvROK (pOut))
        {
        r -> sOutputfile      = r -> pLastReq -> sOutputfile ;
        r -> bAppendToMainReq = TRUE ;
        }
    else
        {
        if (sOutputfile[0] == 1)
            r -> sOutputfile      = "" ;
        else
            r -> sOutputfile      = sOutputfile ;
        r -> bAppendToMainReq = FALSE ;
        }
    
    r -> bReqRunning     = 0 ;

    r -> Buf.pFile = pFile ;

    r -> pOutData        = pOut ;
    r -> pInData         = pIn ;

    
    r -> CmdStack.State.nCmdType      = cmdNorm ;
    r -> CmdStack.State.bProcessCmds  = cmdAll ;
    r -> HtmlStack.State.nCmdType      = cmdNorm ;
    r -> HtmlStack.State.bProcessCmds  = cmdAll ;
    r -> nTabMode        = epTabRowDef | epTabColDef ;
    r -> nTabMaxRow      = 100 ;
    r -> nTabMaxCol      = 10 ;


    r -> nEscMode = pConf -> nEscMode ;
    NewEscMode (r, NULL) ;
    r -> bEscModeSet = 0 ;


    if (r -> bOptions & optSafeNamespace)
	{
        r -> Buf.sEvalPackage = "main" ;
        r -> Buf.nEvalPackage = sizeof ("main") - 1 ;
        }
    else
	{
        r -> Buf.sEvalPackage = r -> Buf.pFile -> sCurrPackage ;
        r -> Buf.nEvalPackage = r -> Buf.pFile -> nCurrPackage ;
        }
    
    if (sImport && *sImport)
	{

        if ((r -> pImportStash = gv_stashpv (sImport, 0)) == NULL)
	    {
	    strncpy (r -> errdat1, sImport, sizeof (r -> errdat1) - 1);
	    LogError (r, rcImportStashErr) ;
	    }
	r -> bOptions |= optDisableHtmlScan ;
	}
    else
	r -> pImportStash = NULL ;

    r -> Buf.nSourceline = r -> Buf.pFile -> nFirstLine ;
    r -> Buf.pSourcelinePos = NULL ;    
    r -> Buf.pLineNoCurrPos = NULL ;    

    r -> bStrict       = FALSE ;

    r -> errdat1 [0]  = '\0' ; /* Additional error information */
    r -> errdat2 [0]  = '\0' ;
    r -> lastwarn [0] = '\0' ; /* last warning */
    
    if (!r -> bSubReq)
        {
        r -> bError = FALSE ;
        av_clear (r -> pErrFill) ;
        av_clear (r -> pErrState) ;
        av_clear (r -> pErrArray) ;
        hv_clear (r -> pHeaderHash) ;
        r -> nLastErrFill  = AvFILL(r -> pErrArray) ;
        r -> bLastErrState = r -> bError ;
        r -> nLogFileStartPos = GetLogFilePos (r) ;
        }


    if (r -> bDebug)
        {
        time_t t ;
        struct tm * tm ;
        time (&t) ;        
        tm =localtime (&t) ;
        if (!r -> bSubReq)
	    lprintf (r, "[%d]REQ:  Embperl %s starting... %s\n", r -> nPid,  sVersion, asctime(tm)) ;
        r -> numEvals = 0  ;
        r -> numCacheHits = 0 ;
        }
    
    if (r -> bDebug)
        {
        switch (r -> nIOType)
            {
            case epIOMod_Perl: sMode = "mod_perl"; break ;
            case epIOPerl:     sMode = "Offline"; break ;
            case epIOCGI:      sMode = "CGI-Script"; break ;
            case epIOProcess:  sMode = "Demon"; break ;
            default: sMode = "unknown" ; break ;
            }
        
        lprintf (r, "[%d]REQ:  %s  %s  ", r -> nPid, (r -> bOptions & optSafeNamespace)?"SafeNamespace":"No Safe Eval", (r -> bOptions & optOpcodeMask)?"OpcodeMask":"All Opcode allowed") ;
#ifdef EP2
	lprintf (r, " mode = %s (%d) %s recipe = %s\n", sMode, r -> nIOType, r -> bEP1Compat?"EP 1.x":"EP 2.x", r -> pConf -> sRecipe) ;
#else
        lprintf (r, " mode = %s (%d)\n", sMode, r -> nIOType) ;
#endif
	lprintf (r, "[%d]REQ:  Package = %s\n", r -> nPid, r -> Buf.pFile -> sCurrPackage) ;
#ifdef DMALLOC
        dmalloc_message ("[%d]REQ:  Package = %s File = %s\n", r -> nPid, r -> Buf.pFile -> sCurrPackage, r -> Buf.pFile -> sSourcefile) ; 
#endif        
        }

    return r ;
    }


/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Free Request                                                                 */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


void FreeRequest (/*i/o*/ register req * r)

    {
    FreeConfData (r -> pConf) ;
    r -> pConf = NULL ;

    if (!r -> bAppendToMainReq && r -> ofd)
        CloseOutput (r) ; /* just to be sure */
    
    if (r -> bSubReq)
        {
        tReq * l = r -> pLastReq ;

        l -> bError      = r -> bError ;
        l -> nLastErrFill= r -> nLastErrFill ;
        l -> bLastErrState= r -> bLastErrState ;
        }
    else
        {
        tFile * pFile ;
        tFile * pNext ;
	int     i ;

#ifdef HASHTEST
	pHash = perl_get_hv (sFormHashName, FALSE) ;
	if (pHash != r -> pFormHash)
	    {
	    strcpy (r -> errdat1, sFormHashName) ;
	    strcpy (r -> errdat2, " !!C-Adress differs from Perl-Adress!! in FreeRequest") ;
	    LogError (r, rcHashError) ;
	    }
#endif

        hv_clear (r -> pHeaderHash) ;
        av_clear (r -> pFormArray) ;
        hv_clear (r -> pFormHash) ;
        hv_clear (r -> pInputHash) ;
        hv_clear (r -> pFormSplitHash) ;
#ifdef EP2 
        av_clear (r -> pDomTreeAV) ;
	for (i = 0 ; i < AvFILL (r -> pCleanupAV); i++)
	    {
	    sv_setsv (SvRV(*av_fetch (r -> pCleanupAV, i, 0)), &sv_undef) ;
	    }
        av_clear (r -> pCleanupAV) ;

        Cache_CleanupRequest (r) ;

#endif
	if ((pFile = r -> pFiles2Free))
	    {
	    do
		{
		FreeFileBuf (r, pFile) ;
		pNext = pFile -> pNext2Free ;
		pFile -> pNext2Free = NULL ;
		}
	    while (pFile != pNext && (pFile = pNext)) ;
	    }
#ifdef APACHE
	r -> pApacheReq   = NULL ;
	r -> pApacheReqSV = &sv_undef ;
#endif
	}

    if (r -> sSessionID)
	_free (r, r -> sSessionID) ;

    SvREFCNT_dec (r -> pReqSV) ;

    pCurrReq = r -> pLastReq ;
    if (pCurrReq && pCurrReq -> pReqSV)
	{
	SV * pReqHV = SvRV (pCurrReq -> pReqSV) ;
	sv_unmagic (pReqHV, '~') ;
	sv_magic (pReqHV, NULL, '~', (char *)&pCurrReq, sizeof (pCurrReq)) ;
	}

#if defined (_DEBUG) && defined (WIN32)
    _CrtMemDumpAllObjectsSince(&r -> MemCheckpoint);    
#endif    
#ifdef DMALLOC
			    /* unsigned long mark, int not_freed_b, int freed_b, int details_b */
    dmalloc_log_changed (r -> MemCheckpoint, 1, 0, 1) ;
    dmalloc_message ( "[%d]%sRequest freed. Entry-SVs: %d -OBJs: %d Exit-SVs: %d -OBJs: %d\n", r -> nPid,
	    (r -> bSubReq?"Sub-":""), r -> stsv_count, r -> stsv_objcount, sv_count, sv_objcount) ;
#endif    
    if (r -> bDebug)
	DomStats () ;

    r -> pNext = pReqFree ;
    pReqFree = r ;

    }


                     
/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Setup Safe Namespace                                                         */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


static void SetupSafeNamespace (/*i/o*/ register req * r)

    {                 
    GV *    gv;

    dTHR ;

	/* The following is borrowed from Opcode.xs */

    if (r -> bOptions & optOpcodeMask)
        opmask_addlocal(r -> pConf -> pOpcodeMask, r -> op_mask_buf);

        
    if (r -> bOptions & optSafeNamespace)
        {
        save_aptr(&endav);
        endav = (AV*)sv_2mortal((SV*)newAV()); /* ignore END blocks for now	*/

        save_hptr(&defstash);		/* save current default stack	*/
        /* the assignment to global defstash changes our sense of 'main'	*/
        defstash = gv_stashpv(r -> Buf.pFile -> sCurrPackage, GV_ADDWARN); /* should exist already	*/

        if (r -> bDebug)
            lprintf (r, "[%d]REQ:  switch to safe namespace %s\n", r -> nPid, r -> Buf.pFile -> sCurrPackage) ;


        /* defstash must itself contain a main:: so we'll add that now	*/
        /* take care with the ref counts (was cause of long standing bug)	*/
        /* XXX I'm still not sure if this is right, GV_ADDWARN should warn!	*/
        gv = gv_fetchpv("main::", GV_ADDWARN, SVt_PVHV);
        sv_free((SV*)GvHV(gv));
        GvHV(gv) = (HV*)SvREFCNT_inc(defstash);
        }
    }

    
/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Start the output stream                                                      */
/*                                                                              */
/* ---------------------------------------------------------------------------- */

    
static int StartOutput (/*i/o*/ register req * r)

    {
    int rc ;
    SV * pOutData  = r -> pOutData ;
    int  bOutToMem = SvROK (pOutData) ;
    

    if (r -> pImportStash)
	{ /* import does not generate any output */
	r -> bDisableOutput = 1 ;
	}
    else if (!bOutToMem)
        {
        if (!r -> bAppendToMainReq)
            {
            if ((rc = OpenOutput (r, r -> sOutputfile)) != ok)
                return rc ;
            }
        else
            OutputToStd (r) ;
        }
    else
        { /* only reset output buffers */
        r -> ofd = NULL ;
        OpenOutput (r, NULL) ;
        }


#ifdef APACHE
    if (r -> pApacheReq && r -> pApacheReq -> main)
    	r -> bOptions |= optEarlyHttpHeader ; /* do not direct output to memory on internal redirect */
#endif
    if (bOutToMem)
    	r -> bOptions &= ~optEarlyHttpHeader ;

    if (r -> bSubReq || r -> pImportStash)
    	r -> bOptions &= ~optSendHttpHeader ;


    if (r -> bOptions & optEarlyHttpHeader)
        {
#ifdef APACHE
        if (r -> pApacheReq == NULL)
            {
#endif
            if (r -> nIOType != epIOPerl && (r -> bOptions & optSendHttpHeader))
                oputs (r, "Content-type: text/html\n\n") ;

#ifdef APACHE
            }
        else
            {
            if (r -> pApacheReq -> main == NULL && (r -> bOptions & optSendHttpHeader))
            	send_http_header (r -> pApacheReq) ;
#ifndef WIN32
	    /* shouldn't be neccessary for newer mod_perl versions !? */
	    /* mod_perl_sent_header(r -> pApacheReq, 1) ; */
#endif
            if (r -> pApacheReq -> header_only)
            	{
	        if (!r -> bAppendToMainReq)
                    CloseOutput (r) ;
            	return ok ;
    	        }
            }
#endif
        }
    else
        {
        /*
	if (r -> nIOType == epIOCGI && (r -> bOptions & optSendHttpHeader))
            oputs (r, "Content-type: text/html\n\n") ;
        */
	
        oBegin (r) ;
        }


    if ((r -> bOptions & optAddStateSessionToLinks) && !r -> bSubReq)
	{
	SV * pCookie = CreateSessionCookie (r, r -> pStateHash, 's', 0) ; 
        STRLEN l ;
        lprintf (r, "opt %x optadd %x options %x cookie %s\n", optAddStateSessionToLinks, r -> bOptions & optAddStateSessionToLinks, r -> bOptions, SvPV(pCookie, l)) ;
	if (pCookie)
            {
            r -> sSessionID = _memstrcat  (r, r -> pConf -> sCookieName, "=", SvPV (pCookie, l), NULL) ;
            }
        }
    
    if ((r -> bOptions & optAddUserSessionToLinks) && !r -> bSubReq)
        {
	SV * pCookie = CreateSessionCookie (r, r -> pUserHash, 'u', 0) ; 
        if (pCookie)
            {
            STRLEN l ;
            if (r -> sSessionID)
                r -> sSessionID = _memstrcat (r, r -> sSessionID, ":", SvPV (pCookie, l), NULL) ;
            else
		r -> sSessionID = _memstrcat  (r, r -> pConf -> sCookieName, "=:", SvPV (pCookie, l), NULL) ;
            }
        }

    
    
    return ok ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* End the output stream                                                        */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


static int EndOutput (/*i/o*/ register req * r,
		      /*in*/ int    rc,
                      /*in*/ SV *   pOutData) 
                      

    {
    SV * pOut = NULL ;
    int  bOutToMem = SvROK (pOutData) ;
    SV * pCookie = NULL ;
    SV * pCookie2 = NULL ;
    int  bError = 0 ;
    STRLEN ldummy ;

#ifdef EP2OLDXSLT
    /* ### tmp ### */
    int bXSLT = 0 ;

    if (strcmp (r ->  pConf -> sRecipe + strlen (r ->  pConf -> sRecipe) - 4 , "XSLT") == 0)
        {
        bXSLT = 1 ;
        }
#endif
    
    r -> bEscModeSet = 0 ;

    if (rc != ok ||  r -> bError)
        { /* --- generate error page if necessary --- */
	dSP;                            /* initialize stack pointer      */
        
	/* --- check if error should be mailed --- */
	PUSHMARK(sp);                   /* remember the stack pointer    */
        XPUSHs(r -> pReqSV) ;            /* push pointer to obeject */
        PUTBACK;
        perl_call_method ("MailErrorsTo", G_DISCARD) ; /* call the function             */

        
	if (r -> bOptions & optReturnError)
	    {
    	    r -> bError = 1 ;
	    oRollbackOutput (r, NULL) ;
	    if (bOutToMem)
		{
		pOut = SvRV (pOutData) ;
		sv_setsv (pOut, &sv_undef) ;
		}
	    return ok ; /* No further output or header, this should be handle by the server */
	    }    
        else if (!(r -> bOptions & optDisableEmbperlErrorPage))
	    {
	    if (!r -> bAppendToMainReq)
		{

		oRollbackOutput (r, NULL) ; /* forget everything outputed so far */
		oBegin (r) ;

		PUSHMARK(sp);                   /* remember the stack pointer    */
		XPUSHs(r -> pReqSV) ;            /* push pointer to obeject */
		PUTBACK;
		perl_call_method ("SendErrorDoc", G_DISCARD) ; /* call the function             */
#ifdef APACHE
		if (r -> pApacheReq)
		    r -> pApacheReq -> status = 500 ;
#endif
		}
	    bError = 1 ;
	    }
	if (!r -> bAppendToMainReq)
   	    r -> bError = 0 ; /* error already handled */
	}
    

    if (!(r -> bOptions & optEarlyHttpHeader) && (r -> bOptions & optSendHttpHeader) && !bOutToMem)
        {  /* --- send http headers if not alreay done --- */
        if (!r -> bAppendToMainReq)
            {                    
            if (!(r -> bOptions & optNoSessionCookies))
                pCookie = CreateSessionCookie (r, r -> pUserHash, 'u', 1) ;
	    /* pCookie2 = CreateSessionCookie (r, r -> pStateHash, 's') ; */
		
#ifdef APACHE
	    if (r -> pApacheReq)
		{
		SV *   pHeader ;
		char * p ;
		HE *   pEntry ;
		char * pKey ;
		I32    l ;

 
 		I32	i;
 		I32	len;
 		AV	*arr;
 		SV	**svp;

		/* loc = 0  =>  no location header found
		 * loc = 1  =>  location header found
		 * loc = 2  =>  location header + value found
		 */
 		I32	loc;
        
		hv_iterinit (r -> pHeaderHash) ;
		while ((pEntry = hv_iternext (r -> pHeaderHash)))
		    {
		    pKey     = hv_iterkey (pEntry, &l) ;
		    pHeader  = hv_iterval (r -> pHeaderHash, pEntry) ;
 		    loc = 0;
		    if (pHeader && pKey)
			{			    

			if (stricmp (pKey, "location") == 0)
			    loc = 1;
 			if (stricmp (pKey, "content-type") == 0)  
 			    {
 			    p = NULL;
 			    if ( SvROK(pHeader) && SvTYPE(SvRV(pHeader)) == SVt_PVAV ) 
 				{
 				arr = (AV *)SvRV(pHeader);
 				if (av_len(arr) >= 0) 
 				    {
 				    svp = av_fetch(arr, 0, 0);
				    p = SvPV(*svp, ldummy);
			    	    }
 				} 
 			    else 
 		 		{
 				p = SvPV(pHeader, ldummy);
 				}
 			    if (p) 
				r->pApacheReq->content_type = pstrdup(r->pApacheReq->pool, p);
			    } 
  			else if (SvROK(pHeader)  && SvTYPE(SvRV(pHeader)) == SVt_PVAV ) 
 			    {
 			    arr = (AV *)SvRV(pHeader);
 			    len = av_len(arr);
 			    for (i = 0; i <= len; i++) 
 				{
 				svp = av_fetch(arr, i, 0);
 				p = SvPV(*svp, ldummy);
 				table_add( r->pApacheReq->headers_out, pstrdup(r->pApacheReq->pool, pKey),
 					   pstrdup(r->pApacheReq->pool, p ) );
 				if (loc == 1) 
				    {
				    loc = 2;
				    break;
 				    }
				}
 			    } 
 			else 
 			    {
 			    p = SvPV(pHeader, ldummy);
			    table_set(r -> pApacheReq->headers_out, pstrdup(r -> pApacheReq->pool, pKey), pstrdup(r -> pApacheReq->pool, p)) ;
			    if (loc == 1) loc = 2;
			    }

			if (loc == 2) r->pApacheReq->status = 301;
			}
		    }


		if (pCookie)
		    {
		    table_add(r -> pApacheReq->headers_out, sSetCookie, pstrdup(r -> pApacheReq->pool, SvPV(pCookie, ldummy))) ;
		    SvREFCNT_dec (pCookie) ;
		    }
		if (pCookie2)
		    {
		    table_add(r -> pApacheReq->headers_out, sSetCookie, pstrdup(r -> pApacheReq->pool, SvPV(pCookie2, ldummy))) ;
		    SvREFCNT_dec (pCookie2) ;
		    }
#ifdef EP2
	        if (r -> bEP1Compat)  /*  Embperl 2 currently cannot calc Content Length */
#endif
		set_content_length (r -> pApacheReq, GetContentLength (r) + (r -> pCurrEscape?2:0)) ;
		send_http_header (r -> pApacheReq) ;
#ifndef WIN32
		/* shouldn't be neccessary for newer mod_perl versions !? */
                /* mod_perl_sent_header(r -> pApacheReq, 1) ; */
#endif
                if (r -> bDebug & dbgHeadersIn)
        	    {
        	    int i;
        	    array_header *hdrs_arr;
        	    table_entry  *hdrs;

        	    hdrs_arr = table_elts (r -> pApacheReq->headers_out);
	            hdrs = (table_entry *)hdrs_arr->elts;

        	    lprintf (r,  "[%d]HDR:  %d\n", r -> nPid, hdrs_arr->nelts) ; 
	            for (i = 0; i < hdrs_arr->nelts; ++i)
		        if (hdrs[i].key)
                	    lprintf (r,  "[%d]HDR:  %s=%s\n", r -> nPid, hdrs[i].key, hdrs[i].val) ; 
        	    }
                }
	    else
#endif
		{ 
		if (r -> nIOType == epIOCGI)
		    {            
		    char txt[100] ;
		    int  save = r -> nMarker ;
		    SV *   pHeader ;
		    char * p ;
		    HE *   pEntry ;
		    char * pKey ;
		    I32    l ;
		    char * pContentType = "text/html";

		    r -> nMarker = 0 ; /* output directly */
        
		    hv_iterinit (r -> pHeaderHash) ;
		    while ((pEntry = hv_iternext (r -> pHeaderHash)))
			{
			pKey     = hv_iterkey (pEntry, &l) ;
			pHeader  = hv_iterval (r -> pHeaderHash, pEntry) ;

			if (pHeader && pKey)
			    {			    
 			    if (SvROK(pHeader)  && SvTYPE(SvRV(pHeader)) == SVt_PVAV ) 
 				{
 				AV * arr = (AV *)SvRV(pHeader);
 				I32 len = av_len(arr);
				int i ;

 				for (i = 0; i <= len; i++) 
 				    {
 				    SV ** svp = av_fetch(arr, i, 0);
 				    p = SvPV(*svp, ldummy);
				    oputs (r, pKey) ;
				    oputs (r, ": ") ;
				    oputs (r, p) ;
				    oputs (r, "\n") ;
				    if (r -> bDebug & dbgHeadersIn)
                			lprintf (r,  "[%d]HDR:  %s: %s\n", r -> nPid, pKey, p) ; 
				    }
 				} 
			    else
				{				    
				p = SvPV (pHeader, na) ;
				if (stricmp (pKey, "content-type") == 0)
				    pContentType = p ;
				else
				    {
				    oputs (r, pKey) ;
				    oputs (r, ": ") ;
				    oputs (r, p) ;
				    oputs (r, "\n") ;
				    }
				if (r -> bDebug & dbgHeadersIn)
                		    lprintf (r,  "[%d]HDR:  %s: %s\n", r -> nPid, pKey, p) ; 
				}
			    }
			}
		    
		    oputs (r, "Content-Type: ") ;
		    oputs (r, pContentType) ;
		    oputs (r, "\n") ;
		    sprintf (txt, "Content-Length: %d\n", GetContentLength (r) + (r -> pCurrEscape?2:0)) ;
		    oputs (r, txt) ;
		    if (pCookie)
			{
			oputs (r, sSetCookie) ;
			oputs (r, ": ") ;
			oputs (r, SvPV(pCookie, na)) ;
			oputs (r, "\n") ;
			SvREFCNT_dec (pCookie) ;
			}
		    if (pCookie2)
			{
			oputs (r, sSetCookie) ;
			oputs (r, ": ") ;
			oputs (r, SvPV(pCookie2, na)) ;
			oputs (r, "\n") ;
			SvREFCNT_dec (pCookie2) ;
			}

		    oputs (r, "\n") ;

		    r -> nMarker = save ;
		    }
		}
	    }
	}

    /* --- output the content if not alreay done --- */

    if (bOutToMem)
        pOut = SvRV (pOutData) ;


#ifdef APACHE
    if ((r -> pApacheReq == NULL || !r -> pApacheReq -> header_only) && 
	(!(r -> bOptions & optEarlyHttpHeader) || r -> bAppendToMainReq))
#else
    if (!(r -> bOptions & optEarlyHttpHeader) || r -> bAppendToMainReq)
#endif
        {
#ifdef EP2
	if (r -> bEP1Compat && r -> pCurrEscape)
#else
	if (r -> pCurrEscape)
#endif		
	    oputs (r, "\r\n") ;
#ifdef EP2OLDXSLT

        if (bXSLT && !bError && !r -> bEP1Compat)
            {
            char * pData ;
            int    l ;

	    tDomTree * pDomTree = DomTree_self (r -> xCurrDomTree) ;
	    Node_toString (r, pDomTree, pDomTree -> xDocument, 0) ;

            pOut = newSVpv ("", 0) ;
	    oputs (r, "\r\n") ;
            l = GetContentLength (r) + 1 ;
            
            SvGROW (pOut, l) ;
            pData = SvPVX (pOut) ;
            oCommitToMem (r, NULL, pData) ;
            oRollbackOutput (r, NULL) ;
            SvCUR_set (pOut, l - 1) ;

            if (r -> bAppendToMainReq)
                oBegin (r) ;

            
            if (strstr (r -> pConf -> sRecipe, "LibXSLT"))
                {
#ifdef LIBXSLT
                if (r -> bDebug & dbgXSLT)
                    lprintf (r, "[%d]XSLT: call libxslt\n", r -> nPid) ;

                if ((rc = embperl_LibXSLT_Text2Text   (r, r -> pConf -> pReqParameter, pOut)) != ok)
                    return rc ;
#else
                strcpy (r -> errdat1, "libxslt not supported") ;
                return 9999 ;
#endif
                }
            else if (strstr (r -> pConf -> sRecipe, "Xalan"))
                {
#ifdef XALAN
                if (r -> bDebug & dbgXSLT)
                    lprintf (r, "[%d]XSLT: call xalan\n", r -> nPid) ;

                if ((rc = embperl_Xalan_Text2Text   (r, r -> pConf -> pReqParameter, pOut)) != ok)
                    return rc ;
#else
                strcpy (r -> errdat1, "xalan not supported") ;
                return 9999 ;
#endif
                }
            }
#endif

        if (bOutToMem)
            {
            char * pData ;
            int    l ;
#ifdef EP2
            		
	    if (!bError && !r -> bEP1Compat && r -> pOutputSV)
		{
		sv_setsv (pOut, r -> pOutputSV) ;
		}
	    else
		{
		if (!bError && !r -> bEP1Compat)
		    {
		    tDomTree * pDomTree = DomTree_self (r -> xCurrDomTree) ;
		    Node_toString (r, pDomTree, pDomTree -> xDocument, 0) ;
		    }

		if (!r -> bEP1Compat)
		    oputs (r, "\r\n") ;
#endif		
		l = GetContentLength (r) + 1 ;
            
		sv_setpv (pOut, "") ;
		SvGROW (pOut, l) ;
		pData = SvPVX (pOut) ;
		oCommitToMem (r, NULL, pData) ;
		SvCUR_set (pOut, l - 1) ;
#ifdef EP2
		}
#endif		
            }
        else
            {
            if (r -> bAppendToMainReq)
                {
                tReq * l = r -> pLastReq ;


#ifdef EP2
		if (r -> bEP1Compat)
		    {
#endif
		    l -> pFirstBuf   = r -> pFirstBuf  ;
		    l -> pLastBuf    = r -> pLastBuf   ;
		    l -> pFreeBuf    = r -> pFreeBuf   ;
		    l -> pLastFreeBuf= r -> pLastFreeBuf ;
		    l -> nSessionMgnt= r -> nSessionMgnt ;
#ifdef EP2
		    }
		else
		    {
		    if (!bError && !r -> pImportStash)
			{
			tDomTree * pDomTree = DomTree_self (r -> xCurrDomTree) ;
#ifdef EP2OLDXSLT
                        if (bXSLT)
                            {
                            int len = GetContentLength (r) + 1 ;
                            char * pData = _malloc (r, len) ;
                            oCommitToMem (r, NULL, pData) ;
                            oRollbackOutput (r, NULL) ;
                            l -> xCurrNode =  Node_appendChild (DomTree_self (l -> xCurrDomTree),
								l -> xCurrNode, 
								l -> nCurrRepeatLevel, 
			                                            ntypCDATA,
                                                                    0,
                                                                    pData, len - 1, 0, 0, "XSLT Result") ;
                            }
                        else
#endif
                            {
                            if (r -> pOutputSV)
			        {
			        STRLEN len ;
			        char * p = SvPV (r -> pOutputSV, len) ;
			        l -> xCurrNode = Node_insertAfter_CDATA (p, len, 0, DomTree_self (l -> xCurrDomTree), l -> xCurrNode, l -> nCurrRepeatLevel) ;
			        }
		            else
                                {
                                l -> xCurrNode = Node_insertAfter (pDomTree, pDomTree -> xDocument, 0, DomTree_self (l -> xCurrDomTree), l -> xCurrNode, l -> nCurrRepeatLevel) ;
                                }
                            }
			}
		    }
#endif
		}
            else
		{
                oCommit (r, NULL) ;
#ifdef EP2
		if (!bError && !r -> bEP1Compat && !r -> pImportStash 
#ifdef EP2OLDXSLT
		    && !bXSLT
#endif
		    )
		    {
		    if (r -> pOutputSV)
			{
			STRLEN l ;
			char * p = SvPV (r -> pOutputSV, l) ;
			owrite (r, p, l) ;
			}
		    else
			{
			tDomTree * pDomTree = DomTree_self (r -> xCurrDomTree) ;
			Node_toString (r, pDomTree, pDomTree -> xDocument, 0) ;
			oputs (r, "\r\n") ;
			}
		    }
#endif
		}
	    }
        }
    else
        {
        oRollbackOutput (r, NULL) ;
        if (bOutToMem)
            sv_setsv (pOut, &sv_undef) ;
#ifdef EP2
	else if (!r -> bEP1Compat)
	    {
	    tDomTree * pDomTree = DomTree_self (r -> xCurrDomTree) ;
	    Node_toString (r, pDomTree, pDomTree -> xDocument, 0) ;
	    }
#endif
        }    

    if (!r -> bAppendToMainReq)
        CloseOutput (r) ;

    return ok ;
    }
    
/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Reset Request                                                                */
/*                                                                              */
/* ---------------------------------------------------------------------------- */


static int ResetRequest (/*i/o*/ register req * r,
			/*in*/ char *  sInputfile)

    {

    if (r -> bDebug)
        {
        clock_t cl = clock () ;
        time_t t ;
        struct tm * tm ;
        
        time (&t) ;        
        tm =localtime (&t) ;
        
        lprintf (r, "[%d]PERF: input = %s\n", r -> nPid, sInputfile) ;
#ifdef CLOCKS_PER_SEC
        lprintf (r, "[%d]PERF: Time: %d ms ", r -> nPid, ((cl - r -> startclock) * 1000 / CLOCKS_PER_SEC)) ;
#else
        lprintf (r, "[%d]PERF: ", r -> nPid) ;
#endif        
        lprintf (r, "Evals: %d ", r -> numEvals) ;
        if (r -> numEvals == 0)
            lprintf (r, "No Evals to cache") ;
        else
            lprintf (r, "Cache Hits: %d (%d%%)", r -> numCacheHits, r -> numCacheHits * 100 / r -> numEvals) ;

        lprintf (r, "\n") ;    
        lprintf (r, "[%d]%sRequest finished. %s. Entry-SVs: %d -OBJs: %d Exit-SVs: %d -OBJs: %d\n", r -> nPid,
	    (r -> bSubReq?"Sub-":""), asctime(tm), r -> stsv_count, r -> stsv_objcount, sv_count, sv_objcount) ;
#ifdef DMALLOC
        dmalloc_message ( "[%d]%sRequest finished. Entry-SVs: %d -OBJs: %d Exit-SVs: %d -OBJs: %d\n", r -> nPid,
	    (r -> bSubReq?"Sub-":""), r -> stsv_count, r -> stsv_objcount, sv_count, sv_objcount) ;
#endif        
        }

    
    r -> Buf.pCurrPos = NULL ;


    FlushLog (r) ;

    r -> Buf.nSourceline = 1 ;
    r -> Buf.pSourcelinePos = NULL ;    
    r -> Buf.pLineNoCurrPos = NULL ;    

    r -> bReqRunning = 0 ;

    av_clear (r -> pErrFill) ;
    av_clear (r -> pErrState) ;

#ifdef APACHE
    /* This must be the very very very last !!!!! */
    r -> pApacheReq = NULL ;
#endif
    return ok ;
    }


/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Process the file                                                             */
/*                                                                              */
/* ---------------------------------------------------------------------------- */

    

static int ProcessFile (/*i/o*/ register req * r,
			/*in*/ int     nFileSize)

    {
    int rc ;
    

    r -> Buf.pSourcelinePos = r -> Buf.pCurrPos = r -> Buf.pBuf ;
    r -> Buf.pEndPos  = r -> Buf.pBuf + nFileSize ;

#ifdef EP2
    if (!r -> bEP1Compat)
	{
#ifdef EP2OLD
	tConf * pConf = r -> pConf ;
	
	tProcessor p2 = {2, "Embperl", embperl_CompileProcessor, NULL, embperl_PreExecuteProcessor, embperl_ExecuteProcessor, "", 
	                   NULL, 0, 0, NULL,  NULL } ; 
	tProcessor p1 = {1, "Parser",  embperl_ParseProcessor,   NULL, NULL,                        NULL,                     "", NULL, 0, 0, NULL, NULL } ; 
	
	/* do this here to make stupid compiler happy (sun cc) */
	p2.pCacheKeyCV		= pConf -> pCacheKeyCV ;
	p2.bCacheKeyOptions	= pConf -> bCacheKeyOptions ;
	p2.nOutputExpiresIn	= pConf -> nExpiresIn ; 
	p2.pOutputExpiresCV	= pConf -> pExpiresCV ; 
	p1.pNext		= &p2 ;
	
	if (p2.pCacheKeyCV)
	    SvREFCNT_inc (p2.pCacheKeyCV) ;

	if (p2.pOutputExpiresCV)
	    SvREFCNT_inc (p2.pOutputExpiresCV) ;

	rc = embperl_CompileDocument (r, &p1) ;

	if (p2.pCacheKeyCV)
	    SvREFCNT_dec (p2.pCacheKeyCV) ;

	if (p2.pOutputExpiresCV)
	    SvREFCNT_dec (p2.pOutputExpiresCV) ;
#else
        HV * pParam ;
        SV * pParamRV ;
        SV * pImportParam ;
        tCacheItem * pCache ;
        HV * pRP = r -> pConf -> pReqParameter ;
        int i ;
        char * s ;
        SV * c ;
        SV * pRecipe ;
        STRLEN l ;
        int num ;

        dSP ;

	if (GetHashValueSV (pRP, "provider"))
            pParam = r -> pConf -> pReqParameter ;
        else 
            {
            if (!(pRecipe = GetHashValueSV (pRP, "recipe")))
                pRecipe = sv_2mortal(newSVpv ("Embperl", 7)) ;

            PUSHMARK(sp);
	    XPUSHs(r -> pReqSV); 
	    XPUSHs(sv_2mortal(newRV_inc((SV *)pRP))); 
	    XPUSHs(pRecipe);                
	    PUTBACK;                        
	    num = perl_call_pv ("HTML::Embperl::Recipe::GetRecipe", G_SCALAR /*| G_EVAL*/) ;
	    tainted = 0 ;
	    SPAGAIN;                        
	    if (num == 1)
		pParamRV = POPs ;
	    PUTBACK;
	    if (num != 1 || !SvROK (pParamRV) || !(pParam = (HV *)SvRV(pParamRV)) || SvTYPE((SV *)pParam) != SVt_PVHV)
		{
		strncpy (r -> errdat1, SvPV(pRecipe, l), sizeof (r -> errdat1) - 1) ;
		return rcUnknownRecipe ;
		}
	    }
#if 0
	if (GetHashValueSV (pRP, "provider"))
            pParam = r -> pConf -> pReqParameter ;
        else
            {
            char * sFn = NULL ;
            SV * pSrc ;
            
            if (SvROK(r -> pInData))
                {
                pSrc = CreateHashRef (
                                        "type", hashtstr, "memory",
                                        "name", hashtstr, r -> Buf.pFile -> sSourcefile,
                                        "source", hashtsv, SvREFCNT_inc(SvRV(r -> pInData)),
                                        "mtime", hashtint, (int)r -> Buf.pFile -> mtime,
                                        NULL) ;
                }
            else
                {
                                        
               if ((r -> bOptions & optDisableChdir) == 0)
                    {
                    sFn = strrchr (r -> Buf.pFile -> sSourcefile, '/') ;
#ifdef WIN32
                    if (!sFn)
                        sFn = strrchr (r -> Buf.pFile -> sSourcefile, '\\') ;
#endif
                    }
                if (sFn)
                    sFn++ ;
                else
                    sFn = r -> Buf.pFile -> sSourcefile ;

                pSrc = CreateHashRef (
                                        "type", hashtstr, "file",
                                        "filename", hashtstr, sFn,
                                        "cache",        hashtint, 0,
                                        NULL) ;
                }
                

            i = GetHashValueInt (pRP, "expires_in", 0) ;
            c = GetHashValueSVinc  (pRP, "expires_func", NULL) ;
            s = GetHashValueStrDup  (pRP, "expires_filename", NULL) ;

            pParam = (HV *)SvRV(sv_2mortal (CreateHashRef (
            "expires_in",       hashtint,   i,
            "expires_func",     hashtsv,    c,
            "expires_filename", hashtstr,   s,
            "cache",            hashtint,   GetHashValueInt   (pRP, "cache", i || c || s?1:0),
            "provider",         hashtsv,  CreateHashRef (
                "type",         hashtstr, "eprun",
                "source",       hashtsv,  pImportParam = CreateHashRef (
                    "provider", hashtsv, CreateHashRef (
                        "type", hashtstr, "epcompile",
                        "source",       hashtsv,  CreateHashRef (
                            "cache",        hashtint, 0,
                            "provider", hashtsv, CreateHashRef (
                                "type", hashtstr, "epparse",
                                "syntax",       hashtstr,  r -> pTokenTable -> sName,
                                "source",       hashtsv,  CreateHashRef (
                                    "cache",        hashtint, 0,
                                    "provider", hashtsv, pSrc,
                                    NULL),
                                NULL),
                            NULL),
                        NULL),
                    NULL),
                NULL),
            NULL))) ;
            }

        if (r -> pImportStash)
            pParam = (HV *)SvRV(pImportParam) ;
#endif


        if (SvTYPE(pParam) != SVt_PVHV)
            {
            strncpy (r -> errdat2, "provider", sizeof(r -> errdat2) - 1) ;
            return rcNotHashRef ; 
            }

        if ((rc = Cache_New (r, pParam, &r -> pOutputCache)) != ok)
            return rc ;

    
	if (strncmp (r -> pOutputCache -> pProvider -> sOutputType, "text/", 5) == 0)
	    {
	    if ((rc = Cache_GetContentSV (r, r -> pOutputCache, &r -> pOutputSV)) != ok)
		return rc ;
	    }
	else if (strcmp (r -> pOutputCache -> pProvider -> sOutputType, "X-Embperl/DomTree") == 0)
	    {
	    if ((rc = Cache_GetContentIndex (r, r -> pOutputCache, &r -> xCurrDomTree)) != ok)
		return rc ;
	    }
	else
	    {
	    sprintf (r -> errdat1, "'%s' (accpetable are 'text/*', 'X-Embperl/DomTree')", r -> pOutputCache -> pProvider -> sOutputType) ;
	    strncpy (r -> errdat2, r -> pOutputCache -> sKey, sizeof (r -> errdat2) - 1) ;
	    return rcTypeMismatch ;
	    }

#endif

	}
    else
        {
        clock_t cl1 = clock () ;
        clock_t cl2  ;

	rc = EvalMain (r) ; 
	
	cl2 = clock () ;
#ifdef CLOCKS_PER_SEC
        if (r -> bDebug)
	    {
	    lprintf (r, "[%d]PERF: Run Start Time: %d ms \n", r -> nPid, ((cl1 - r -> startclock) * 1000 / CLOCKS_PER_SEC)) ;
	    lprintf (r, "[%d]PERF: Run End Time:   %d ms \n", r -> nPid, ((cl2 - r -> startclock) * 1000 / CLOCKS_PER_SEC)) ;
	    lprintf (r, "[%d]PERF: Run Time:       %d ms \n", r -> nPid, ((cl2 - cl1) * 1000 / CLOCKS_PER_SEC)) ;
	    }
#endif    
        }
#else /* EP2 */
    rc = EvalMain (r) ; 
#endif /* EP2 */
    if ((r -> bOptions & optNoUncloseWarn) == 0)
	{
	if (!r -> bSubReq && r -> CmdStack.pStack)
	    {
	    if (r -> CmdStack.State.pCmd)
		strncpy (r -> errdat1, r -> CmdStack.State.pCmd -> sCmdName, sizeof (r -> errdat1) - 1) ;
	    LogError (r, rcUnclosedCmd) ;
	    }

	if (!r -> bSubReq && r -> HtmlStack.pStack)
	    {
	    if (r -> HtmlStack.State.pCmd)
		strncpy (r -> errdat1, r -> HtmlStack.State.pCmd -> sCmdName, sizeof (r -> errdat1) - 1) ;
	    LogError (r, rcUnclosedHtml) ;
	    }
	}

    return rc ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Process a block of the file  						*/
/*                                                                              */
/* ---------------------------------------------------------------------------- */

    

int ProcessBlock	(/*i/o*/ register req * r,
			 /*in*/  int	 nBlockStart,
			 /*in*/  int	 nBlockSize,
                         /*in*/  int     nBlockNo)

    {
    int     rc ;
    char *  p ;
    int     n ;


    r -> Buf.pCurrPos = r -> Buf.pBuf + nBlockStart ;
    r -> Buf.pEndPos  = r -> Buf.pCurrPos + nBlockSize ;
    r -> Buf.nBlockNo = nBlockNo ;


    if (r -> pTokenTable && strcmp ((char *)r -> pTokenTable, "Text") == 0)
	{ /* --- emulate Embperl 2  syntax => 'Text' --- */	    
	owrite (r, r -> Buf.pCurrPos, r -> Buf.pEndPos - r -> Buf.pCurrPos) ;
	return r -> Buf.nBlockNo ;
	}

    rc = ok ;
    p = r -> Buf.pCurrPos ;
    while (p && *p && p < r -> Buf.pEndPos && rc == ok)
        {
        if ((r -> bDebug & dbgMem) && (sv_count != r -> lstsv_count || sv_objcount != r -> lstsv_objcount))
            {
            lprintf (r, "[%d]SVs:  Entry-SVs: %d -OBJs: %d Curr-SVs: %d -OBJs: %d\n", r -> nPid, r -> stsv_count, r -> stsv_objcount, sv_count, sv_objcount) ;
            r -> lstsv_count = sv_count ;
            r -> lstsv_objcount = sv_objcount ;
            }
        
        /* */
        /* execute [x ... x] and special html tags and replace them if nessecary */
        /* */

        if (r -> CmdStack.State.bProcessCmds == cmdAll && !(r -> bOptions & optDisableHtmlScan))
            {
            n = strcspn (p, "[<") ;
            p += n ;
            }
        else
            p = strchr (p, '[') ;
            
            
        if (p == NULL)
            { /* output the rest of html */
            owrite (r, r -> Buf.pCurrPos, r -> Buf.pEndPos - r -> Buf.pCurrPos) ;
            break ;
            }
        
        if (r -> CmdStack.State.bProcessCmds == cmdAll)
            /* output until next cmd */
            owrite (r, r -> Buf.pCurrPos, p - r -> Buf.pCurrPos) ;
        
        if (*p == '\0')
            break ;

        if (r -> bDebug & dbgSource)
            {
            char * s = p ;
            char * n ;

            while (*s && isspace (*s))
                s++ ;
            
            if (*s)
                {
                GetLineNo (r) ;    
                n = strchr (s, '\n') ;
    
#ifdef CLOCKS_PER_SEC
                if (r -> bDebug & dbgProfile)
                    if (n)
                        lprintf (r, "[%d]SRC: Line %d: Time %d ms  %*.*s\n", r -> nPid, r -> Buf.nSourceline, ((clock () - r -> startclock) * 1000 / CLOCKS_PER_SEC), n-s, n-s, s) ;
                    else
                        lprintf (r, "[%d]SRC: Line %d: Time %d ms  %60.60s\n", r -> nPid, r -> Buf.nSourceline, ((clock () - r -> startclock) * 1000 / CLOCKS_PER_SEC), s) ;
                else
#endif
                    if (n)
                        lprintf (r, "[%d]SRC: Line %d: %*.*s\n", r -> nPid, r -> Buf.nSourceline, n-s, n-s, s) ;
                    else
                        lprintf (r, "[%d]SRC: Line %d: %60.60s\n", r -> nPid, r -> Buf.nSourceline, s) ;

                }
            }        

        
        r -> Buf.pCurrStart = p ;
        if (*p == '<')
            { /* HTML Tag */
            rc = ScanHtmlTag (r, p) ;
            }
         else
            { /* [x ... x] sequenz */
            if (p[1] == '*')
                break ;
            
            rc = ScanCmdEvals (r, p) ;
            }
        p = r -> Buf.pCurrPos ;
        }

    if (rc != ok)
        {
        if (rc != rcExit)
            LogError (r, rc) ;
        return 0 ;
        }
    
    return r -> Buf.nBlockNo ;
    }


	    
	    
	    
/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Read input file into memory  						*/
/*                                                                              */
/* ---------------------------------------------------------------------------- */

int ReadInputFile	(/*i/o*/ register req * r)

    {
    int	    rc = ok ;
    SV *    pBufSV = NULL ;
    req *   pMain = r ;

#ifdef EP2
    if (!r -> bEP1Compat)
    	{
    	SV * * ppSV ;
    	
    	ppSV = hv_fetch (r -> Buf.pFile -> pCacheHash, "P-1----", 7, 0) ;
    	if (ppSV && *ppSV)
    	    {
	    r -> Buf.pBuf		    = NULL ;
	    r -> Buf.pFile -> nFilesize     = 1 ;
	    return ok ;  /* source already parsed */
	    }
	}
#endif

    if ((pBufSV = r -> Buf.pFile -> pBufSV) == NULL || !SvPOK (pBufSV))
	{
	if (SvROK(r -> pInData))
	    { /* --- get input from memory --- */
	    STRLEN n ;
	    r -> Buf.pBuf = SvPV (pBufSV = SvRV(r -> pInData), n) ;
	    r -> Buf.pFile -> nFilesize = n ; 
	    }

	else
	    {
	    /* --- read input file --- */
    	    rc = ReadHTML (r, r -> Buf.pFile -> sSourcefile, &r -> Buf.pFile -> nFilesize, &pBufSV) ;
	    if (rc == ok)
		r -> Buf.pBuf = SvPVX (pBufSV) ;
	    }
	
	if (rc == ok)
	    {
	    SvREFCNT_inc (pBufSV) ;
	    r -> Buf.pFile -> pBufSV = pBufSV ;
	    r -> Buf.pEndPos  = r -> Buf.pBuf + r -> Buf.pFile -> nFilesize ;
	    
	    if (r -> Buf.pFile -> pNext2Free == NULL)
		{
		/* --- add to list for freeing --- */
	    
		while (pMain && pMain -> pLastReq != &InitialReq)
		    pMain = pMain -> pLastReq ;

		if ((r -> Buf.pFile -> pNext2Free = pMain -> pFiles2Free) == NULL)
		    r -> Buf.pFile -> pNext2Free  = r -> Buf.pFile ; /* last one points to itself !! */
		pMain -> pFiles2Free = r -> Buf.pFile ;
		}
	    
	    /* SetupDebugger (r) ; */
	    }
	}
    else
	{
	r -> Buf.pBuf		    = SvPVX (pBufSV) ;
	r -> Buf.pFile -> nFilesize = SvCUR (pBufSV) ;
	}

    return rc ;
    }

/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Process a block of the file  						*/
/*                                                                              */
/* ---------------------------------------------------------------------------- */

    

int ProcessSub		(/*i/o*/ register req * r,
			 /*in*/  tFile * pFile,
			 /*in*/  int	 nBlockStart,
                         /*in*/  int     nBlockNo)

    {
    int	    rc ;
    tSrcBuf Buf ;
    char *  sEvalPackage = r -> Buf.sEvalPackage ; 
    STRLEN  nEvalPackage = r -> Buf.nEvalPackage ;  
    SV *    pInData      = r -> pInData  ;  


    /*av_unshift (GvAV (PL_defgv), 1) ;
    av_store   (GvAV (PL_defgv), 0, r -> pReqSV) ; */
    
    memcpy (&Buf, &r -> Buf, sizeof (Buf)) ;

    
    if (pFile != r -> Buf.pFile)
	{ /* get other file */
	r -> Buf.pFile = pFile ;
	r -> pInData = &sv_undef ;

	if ((rc = ReadInputFile (r)) != ok)
	    {
	    LogError (r, rc) ;
	    return rc ;
	    }

	r -> Buf.pSourcelinePos =  r -> Buf.pBuf ;
	r -> Buf.nSourceline = r -> Buf.pFile -> nFirstLine ;
	r -> Buf.pLineNoCurrPos = NULL ;    
	r -> Buf.sEvalPackage   = r -> Buf.pFile -> sCurrPackage ; 
	r -> Buf.nEvalPackage   = r -> Buf.pFile -> nCurrPackage ; 
	}

    r -> nInsideSub++ ;
    rc = ProcessBlock (r, nBlockStart, r -> Buf.pFile -> nFilesize - nBlockStart, nBlockNo) ;
    r -> nInsideSub-- ;

    memcpy (&r -> Buf, &Buf, sizeof (Buf)) ;
    r -> Buf.sEvalPackage = sEvalPackage ; 
    r -> Buf.nEvalPackage = nEvalPackage ; 
    r -> pInData          = pInData ;  

    if (rc != ok)
	LogError (r, rc) ;

    return rc ;
    }


    
/* ---------------------------------------------------------------------------- */
/*                                                                              */
/* Request handler                                                              */
/*                                                                              */
/* ---------------------------------------------------------------------------- */



int ExecuteReq (/*i/o*/ register req * r,
                /*in*/  SV *           pReqSV) 

    {
    int     rc = ok ;
    char *  sInputfile = r -> Buf.pFile -> sSourcefile ;

    dTHR ;

    EPENTRY (ExecuteReq) ;

    
    /* r -> pReqSV = pReqSV ;  ep2?? */

    if (!r -> Buf.pFile -> pExportHash)
	r -> Buf.pFile -> pExportHash = newHV () ;
    
    ENTER;
    SAVETMPS ;
	
    SetupSafeNamespace (r) ;

    /* --- read form data from browser if not already read by perl part --- */
    if (rc == ok && !(r -> bOptions & optDisableFormData) && 
	            av_len (r -> pFormArray) == -1 && !r -> bSubReq && 
		    r -> pImportStash == NULL) 
        rc = GetInputData_CGIScript (r) ;
    
    /* --- open output and send http header if EarlyHttpHeaders --- */
    if (rc == ok)
        rc = StartOutput (r) ;

    /* --- read input file or get input file from memory --- */
#ifdef EP2
    if (rc == ok && r -> bEP1Compat)
#else
    if (rc == ok)
#endif
        {
        rc = ReadInputFile (r) ;

        if (rc == ok && r -> Buf.pBuf == NULL && r -> Buf.pFile -> nFilesize == 0)
            rc = rcMissingInput ;
        }
    
    /* --- ok so far? if not exit ---- */
#ifdef APACHE
    if (rc != ok || (r -> pApacheReq && r -> pApacheReq -> header_only && (r -> bOptions & optEarlyHttpHeader)))
#else
    if (rc != ok)
#endif
        {
        if (rc != ok)
            LogError (r, rc);
#ifdef APACHE
        r -> pApacheReq = NULL ;
#endif
        r -> bReqRunning = 0 ;
        FREETMPS ;
        LEAVE;
        return rc ;
        }

    /* --- change working directory --- */
    
    if (
#ifdef EP2
	 r -> bEP1Compat && 
#endif	
	((r -> bOptions & optDisableChdir) == 0 && sInputfile != NULL && sInputfile != '\0' && !SvROK(r -> pInData)))
	{
	char dir[PATH_MAX];
#ifdef WIN32
	    char drive[_MAX_DRIVE];
	    char fname[_MAX_FNAME];
	    char ext[_MAX_EXT];
	    char * c = sInputfile ;

	    while (*c)
		{ /* convert / to \ */
 		if (*c == '/')
		    *c = '\\' ;
		c++ ;
		}

	    r -> nResetDrive = _getdrive () ;
	    getcwd (r -> sResetDir, sizeof (r -> sResetDir) - 1) ;

	    _splitpath(sInputfile, drive, dir, fname, ext );
   	    _chdrive (drive[0] - 'A' + 1) ;
#else
	    Dirname (sInputfile, dir, sizeof (dir) - 1) ;
	    getcwd (r -> sResetDir, sizeof (r -> sResetDir) - 1) ;
#endif
	if (dir[0])
            {
            if (chdir (dir) < 0)
	       lprintf (r, "chdir error\n" ) ;
            else
                {
                if (!(dir[0] == '/'  
            #ifdef WIN32
                    ||
                    dir[0] == '\\' || 
                        (isalpha(dir[0]) && dir[1] == ':' && 
	                  (dir[2] == '\\' || dir[2] == '/')) 
            #endif                  
                    ))            
                    {
                    strcpy (r->sCWD,r -> sResetDir) ;
                    strcat (r->sCWD,"/") ;
                    strcat (r->sCWD,dir) ;
                    }
                else
                    strcpy (r->sCWD,dir) ;
                }
	    }
        else
	    r -> bOptions |= optDisableChdir ;
        }
    else
#ifdef EP2
	if (r -> bEP1Compat) 
#endif	
	    r -> bOptions |= optDisableChdir ;
        

    r -> bReqRunning     = 1 ;

    if ((rc = ProcessFile (r, r -> Buf.pFile -> nFilesize)) != ok)
        if (rc == rcExit)
            rc = ok ;
        else
            LogError (r, rc) ;


    /* --- Restore Operatormask and Package, destroy temp perl sv's --- */
    FREETMPS ;
    LEAVE;
    r -> bReqRunning = 0 ;

    /* --- send http header and data to the browser if not already done --- */
    if ((rc = EndOutput (r, rc, r -> pOutData)) != ok)
        LogError (r, rc) ;

#ifdef EP2
    if (r -> pOutputCache)
        Cache_ReleaseContent (r, r -> pOutputCache) ;
#endif    
    
    /* --- restore working directory --- */
    if (r -> sResetDir[0])
	{
#ifdef WIN32
   	_chdrive (r -> nResetDrive) ;
#endif
	chdir (r -> sResetDir) ;
	strcpy (r->sCWD,r -> sResetDir) ;
	r -> sResetDir[0] = '\0' ;
        }

    /* --- reset variables and log end of request --- */
    if ((rc = ResetRequest (r, sInputfile)) != ok)
        LogError (r, rc) ;

#if defined (_DEBUG) && defined (WIN32)
    _ASSERTE( _CrtCheckMemory( ) );
#endif

    return ok ;
    }
