/*******************************************************
 * Sieve (mail filter) structure creation/destuction.
 *
 * Filename:      sieve-struct.c
 * Author:        Randall Gellens
 * Last Modified: 4 November 1998
 * Version:       
 * Copyright:     1998, QUALCOMM Incorporated,
 *                all rights reserved
 *******************************************************/


/*-- standard header files --*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <malloc.h>

/*-- DAA trace utility --*/
#include "daa_trace.h"

/*-- local header files --*/
#include "sieve-struct.h"


/*--------------------------------------------------------------------------------
 *                v a l u e    a r r a y s
 *--------------------------------------------------------------------------------
*/

/*                                0     0     1     1     2     2     3     3     4     4     5     5     */
/*                                01234 56789 01234 56789 0123 456789 01234 56789 01234 56789 01234 56789 */
const char szActionTypeNames[] = "....\0Rejc\0Disc\0File\0Fwd\0.Keep\0Repl\0Mark\0Stop\0Void\0....\0",
           szOpTypeNames    [] = "....\0Cntn\0Exst\0Mtch\0Is\0..Over\0Undr\0....\0",
           szTestTypeNames  [] = "....\0Fals\0True\0Not\0.Any\0.All\0.Size\0Sppt\0Exst\0Hdr\0.Env\0.",
		   szStmtTypeNames  [] = "....\0If\0..Actn\0....\0";




/*---------------------------------------------------------------
 *    m a c r o s
 *---------------------------------------------------------------
 */
#define imda()           TRACEstamp("DELIVERY", psPF->stamp)
#define TRACE_IT1(L,X)   if (psPF->debug >= L) tracef(__LINE__, FALSE, "%.250s",X)
#define WARN(x)          syntax_error(TRUE,  X, psPF, __LINE__)
#define TILT(X)          syntax_error(FALSE, X, psPF, __LINE__)
#define OOPS(X)          syntax_error(FALSE, X, NULL, __LINE__)
#define MALLOC(S,W)      my_malloc(S, psPF, W, __LINE__)
#define FREE(P)          my_free(P, debug, __LINE__)

#define CRLF             "\x0D\x0A"  /* CRLF as string */



/*---------------------------------------------------------------
 *    g l o b a l   v a r i a b l e s
 *---------------------------------------------------------------
 */

int iAllocCount; /* number of items we've allocated */




/*---------------------------------------------------------------
 *    f u n c t i o n s
 *---------------------------------------------------------------
 */





/*--------------------------------------------------------------
 * Jacket for MALLOC.
 *
 * Parameters:
 *	  iSize:		size in bytes to be allocated.
 *    psPF:	    	pointer to strParseFile structure.
 *    what:         pointer to null-terminated string describing 
 *                      what it is we are allocating.
 *    ln:           line number whence called
 *
 * Result:
 *    Pointer to newly-allocated structure, or NULL.
 */

void *my_malloc ( size_t iSize, strParseFile *psPF, const char *what, int ln )
    {
    void *p;


    p = malloc ( iSize );
    if ( p )
        {
        if ( psPF->debug > 3 )
            tracef ( ln, FALSE, "allocated %u bytes for new %.50s (%p)", iSize, what, p );
        psPF->iHiMalloc ++;
        psPF->iHiSize   += iSize;

        if ( DO_ALLOC_COUNT )
            iAllocCount++;
        }
    else if ( psPF->debug > 0 )
        tracef ( ln, FALSE, "*** unable to allocate %u bytes ***", iSize );

    return ( p );
    } /* end my_malloc */



/*--------------------------------------------------------------
 * Jacket for FREE.
 *
 * Parameters:
 *	  ptr:		    pointer to item to be freed.
 *    debug:        desired debug level.
 *    ln:           line number whence called.
 *
 */

void my_free ( void *ptr, int debug, int ln )
    {
    if ( ptr )
        {
        if ( DO_ALLOC_COUNT )
            {
			if ( debug > 3)
				tracef ( ln, FALSE, "deallocating item %p [%d]", ptr, ln );
            iAllocCount--;
            }
        free ( ptr );
        }
    else if ( debug > 2 )
        tracef ( ln, FALSE, "null pointer passed to my_free (%d)", ln );
    } /* end my_free */


/*******************************************************************************/

/*
 * Allocate a new STMT structure.
 *
 * Parameters:
 *    psPF:     pointer to strParseFile structure.
 *
 * Returns:
 *    pointer to newly-allocated STMT structure, or NULL.
 *
 * Notes:
 *    caller must call free_stmt.
 *
 */
strStmt *new_stmt ( strParseFile *psPF )
    {
    strStmt *theStmt;


    theStmt = MALLOC ( sizeof(strStmt), "strStmt" );
    if ( theStmt )
        {
		theStmt->stmtType     = 0;
        theStmt->stmt.this_if = NULL; /* also NULLs theStmt->stmt.action */
        theStmt->next_stmt    = NULL;
		theStmt->iLine        = 0;
        }
    else
        TILT ("unable to allocate STMT");

    return ( theStmt );
    } /* end new_stmt */


/*******************************************************************************/

/*
 * Allocate a new IF structure.
 *
 * Parameters:
 *    psPF:     pointer to strParseFile structure.
 *
 * Returns:
 *    pointer to newly-allocated IF structure, or NULL.
 *
 * Notes:
 *    caller must call free_if.
 *
 */
strIf *new_if ( strParseFile *psPF )
    {
    strIf *theIf;


    theIf = MALLOC ( sizeof(strIf), "strIf" );
    if ( theIf )
        {
        theIf->test        = NULL;
        theIf->this_stmt   = NULL;
        theIf->else_stmt   = NULL;
        }
    else
        TILT ("unable to allocate IF");

    return ( theIf );
    } /* end new_if */


/*******************************************************************************/

/*
 * Allocate a new TEST structure.
 *
 * Parameters:
 *    type:     the (enum test_type) type of the new test.
 *    psPF:     pointer to strParseFile structure.
 *
 * Returns:
 *    pointer to newly-allocated TEST structure, or NULL.
 *
 * Notes:
 *    caller must call free_test.
 *
 */
strTest *new_test ( test_type type, strParseFile *psPF )
    {
    strTest *theTest;


    theTest = MALLOC ( sizeof(strTest), "strTest" );
    if ( theTest )
        {
        theTest->tstType    = type;
        theTest->compare    = NULL;
        theTest->this_test  = NULL;
        theTest->next_test  = NULL;
        }
    else
        TILT ( "unable to allocate TEST" );

    return ( theTest );
    } /* end new_test */



/*******************************************************************************/

/*
 * Allocate a new COMPARE structure.
 *
 * Parameters:
 *    hdr:      pointer to strText structure containing header names.
 *    op:       operator type.
 *    ctor:     comparator type.
 *    text:     pointer to strText structure containing text or match strings.
 *    num:      value (used in SIZE comparisons).
 *    psPF:     pointer to strParseFile structure.
 *
 * Returns:
 *    pointer to newly-allocated COMPARE structure, or NULL.
 *
 * Notes:
 *    caller must call free_compare.
 *
 */
strCompare *new_compare ( strText      *hdr, 
                          op_type       op, 
                          cmptr_type    ctor, 
                          strText      *text, 
                          long          num,
                          strParseFile *psPF )
    {
    strCompare   *cp = NULL;


    cp = MALLOC ( sizeof(strCompare), "strCompare" );
    if ( cp )
        {
        cp->hdr        = hdr;
        cp->op         = op;
        cp->comparator = ctor;
        cp->text       = text;
        cp->num        = num;
        }
    else
        TILT ( "unable to allocate COMPARE" );

    return ( cp );
    } /* end new_compare */


/*******************************************************************************/

/*
 * Allocate a new ACTION structure.
 *
 * Parameters:
 *    type:     desired type of new strAction structure.
 *    psPF:     pointer to strParseFile structure.
 *
 * Returns:
 *    pointer to newly-created strAction structure, or NULL.
 *
 * Notes:
 *    caller must call free_action.
 *
 */
strAction *new_action ( action_type type, strParseFile *psPF )
    {
    strAction *ap;


    ap = MALLOC ( sizeof(strAction), "strAction" );
    if ( ap )
        {
        ap->actnType    = type;
        ap->text        = NULL;
        ap->message     = NULL;
        }
    else
        TILT ( "unable to allocate ACTION" );

    return ( ap );
    } /* end new_action */


/*******************************************************************************/

/*
 * Allocate a new TEXT structure.
 *
 * Parameters:
 *    ptext:       pointer to null-terminated text, or NULL.
 *    psPF:        pointer to strParseFile structure.
 *
 * Returns:
 *    pointer to newly-created strText structure, or NULL.
 *
 * Notes:
 *    caller must call free_text;
 *
 */
strText *new_text ( char *ptext, strParseFile *psPF )
    {
    strText *tp;


    tp = MALLOC ( sizeof(strText), "strText" );
    if ( tp )
        {
        if ( ptext )
            tp->text = strdup(ptext);
        else
            tp->text = NULL;
        tp->next = NULL;
        }
    else
        TILT ( "unable to allocate TEXT" );

    return ( tp );
    } /* end new_text */


/*******************************************************************************/

/*
 * Allocate a new LINES structure.
 *
 * Parameters:
 *    line:     pointer to null-terminated text, or NULL.
 *    psPF:     pointer to strParseFile structure.
 *
 * Returns:
 *    pointer to newly-allocated strLines structure, or NULL.
 *
 * Notes:
 *    caller must call free_lines.
 *
 */
strLines *new_lines ( char *line, strParseFile *psPF )
    {
    strLines *theLines;

    theLines = MALLOC ( sizeof(strLines), "strLines" );
    if ( theLines )
        {
        if ( line )
            {
            theLines->lines     = strdup(line);
            theLines->length    = strlen(line);
            }
        else
            {
            theLines->lines     = NULL;
            theLines->length    = 0;
            }
        }
    else
        TILT ( "unable to allocate LINES" );

    return ( theLines );
    } /* end new_lines */



/*******************************************************************************/

/*
 * Deallocates a SCRIPT structure.
 *
 * Parameters:
 *    strPtr: pointer to structure to be freed.
 *    debug:  desired trace level.
 *
 */
void free_script ( strScript *strPtr, int debug )
    {
    if ( strPtr )
        {
        free_stmt ( strPtr->start, debug );
        FREE      ( strPtr               );
        }
    } /* end free_script */



/*******************************************************************************/

/*
 * Deallocates a STMT structure.
 *
 * Parameters:
 *    strPtr:   pointer to structure to be freed.
 *    debug:    desired trace level.
 *
 */
void free_stmt ( strStmt *strPtr, int debug )
    {
    strStmt *next;


    while ( strPtr )
        {
        next = strPtr->next_stmt;

		if ( strPtr->stmtType == stmtIf )
			free_if     ( strPtr->stmt.this_if, debug );
		else
			free_action ( strPtr->stmt.action,  debug );

        FREE            ( strPtr                      );

        strPtr = next;
        }
    } /* end free_stmt */



/*******************************************************************************/

/*
 * Deallocates an IF structure.
 *
 * Parameters:
 *    strPtr:   pointer to structure to be freed.
 *    debug:    desired trace level.
 *
 */
void free_if ( strIf *strPtr, int debug )
    {
    if ( strPtr )
        {
        free_test   ( strPtr->test      , debug );
        free_stmt   ( strPtr->this_stmt , debug );
        free_stmt   ( strPtr->else_stmt , debug );
        FREE        ( strPtr                    );
        }
    } /* end free_if */




/*******************************************************************************/

/*
 * Deallocates a TEST structure.
 *
 * Parameters:
 *    strPtr: pointer to structure to be freed.
 *    debug:  desired trace level.
 *
 */
void free_test ( strTest *strPtr, int debug )
    {
    strTest *next;


    while ( strPtr )
        {
        next = strPtr->next_test;

        free_compare ( strPtr->compare  , debug );
        free_test    ( strPtr->this_test, debug );
        FREE         ( strPtr                   );

        strPtr = next;
        }
    } /* end free_test */



/*******************************************************************************/

/*
 * Deallocates a COMPARE structure.
 *
 * Parameters:
 *    strPtr: pointer to structure to be freed.
 *    debug:  desired trace level.
 *
 */
void free_compare ( strCompare *strPtr, int debug )
    {
    if ( strPtr )
        {
        free_text ( strPtr->hdr , debug );
        free_text ( strPtr->text, debug );
        FREE      ( strPtr              );
        }
    } /* end free_compare */




/*******************************************************************************/

/*
 * Deallocates an ACTION structure.
 *
 * Parameters:
 *    strPtr: pointer to structure to be freed.
 *    debug:  desired trace level.
 *
 */
void free_action ( strAction *strPtr, int debug )
    {
	if ( strPtr )
		{
		FREE       ( strPtr->text           );
		free_lines ( strPtr->message, debug );
		FREE       ( strPtr                 );
		}
    } /* end free_action */


/*******************************************************************************/

/*
 * Frees a TEXT structure.
 *
 * Parameters:
 *    strPtr: pointer to structure to be freed.
 *    debug:  desired trace level.
 *
 */
void free_text ( strText *strPtr, int debug )
    {
    strText *next;


    while ( strPtr )
        {
        next = strPtr->next;

        FREE ( strPtr->text );
        FREE ( strPtr );

        strPtr = next;
        }
    } /* end free_text */


/*******************************************************************************/

/*
 * Free a LINES structure.
 *
 * Parameters:
 *    strPtr: pointer to structure to be freed.
 *    debug:  desired trace level.
 *
 */
void free_lines ( strLines *strPtr, int debug )
    {
    if ( strPtr )
        {
        FREE ( strPtr->lines );
        FREE ( strPtr );
        }
    } /* end free_lines */



/******************************************************************************/

/*
 * Informs user of syntax error or warning in script.
 *
 * Parameters:
 *   warn:  TRUE if this is a warning, not an error.
 *   psz:   pointer to error message.
 *   psPF:  pointer to strParseFile structure (may be NULL in some cases).
 *   ln:    line number of executable.
 *
 * Returns:
 *   TRUE if a token was found
 *   FALSE if end-of-file reached.
 *
 */
void syntax_error ( int warn, const char *psz, strParseFile *psPF, int ln )
    {
    char        dummy[2]  = "?";    /* dummy for uninitialized stuff */
    char       *pTokenS   = dummy;  
	int         iTokenOff = 0;
    int         iTokenLen = 0;      
    int         iLineNum  = -1;      
    char       *fname     = dummy;    
	char      **msg       = NULL;
	int        *msgSize   = NULL;
	int        *msgLeft   = NULL;
	char        aWarn[]   = "*warning";
	char        anErr[]   = "***error";
	char       *which     = anErr;
    int         debug     = 1;
    
    
    if ( psPF )
        {
        if ( warn )
			{
            psPF->warnCnt++;
			msg = &psPF->warnText;
			msgSize = &psPF->warnSize;
			msgLeft = &psPF->warnLeft;
			}
        else
			{
            psPF->errCnt++;
			msg = &psPF->errText;
			msgSize = &psPF->errSize;
			msgLeft = &psPF->errLeft;
			}

        if ( psPF->psPL->pTokenS )
            pTokenS = psPF->psPL->pTokenS;

        iLineNum = psPF->iLineNum;
        fname = psPF->fname;
        iTokenLen = psPF->psPL->iTokenLen;
		iTokenOff = psPF->psPL->pTokenS - psPF->szLine;
        debug = psPF->debug;
        }

    if ( debug )
        {
		if ( warn )
			which = aWarn;
			
        tracef( __LINE__, TRUE,
                "%.250s: %.250s at line %i of script %.255s; scanning (%u)\"%.*s\" {%u}*\n",
                which, psz, iLineNum, fname, iTokenLen,
                iTokenLen, pTokenS, ln );       
        }


	/* fill in the error or warning text */

	if ( msg != NULL )
		{
		int  x              = 0;
		char msgText [1024] = "";
		char msgLen         = 0;
		char *buf           = NULL;
		int  crlfLen        = 0;

		
		if ( *msg != NULL )
			crlfLen = 2;

        msgLen = sprintf ( msgText, "%.*sLine:%d; Offset:%d; Length:%d; Text:%.250s",
			                         crlfLen, CRLF, iLineNum, iTokenOff, iTokenLen, psz );

		if ( *msg == NULL )
			{
			x = ERR_WARN_CHUNK_SIZE;
			*msg     = malloc ( x );
			if ( *msg == NULL )
				{
				if ( debug )
					tracef ( __LINE__, FALSE, "***unable to allocate %d bytes for err/warn text", x );
				return;
				}

			**msg    = 0;
			*msgSize = x;
			*msgLeft = x - 1;

			if ( debug > 3 )
				tracef ( __LINE__, FALSE, "allocated %d bytes for err/warn text", *msgSize );
			}
		
		if ( *msgLeft < msgLen )
			{
			/* must enlarge buffer */

			x = *msgSize + ERR_WARN_CHUNK_SIZE;
			buf = *msg; /* save current buffer */

			*msg = malloc ( x );
			if ( *msg == NULL )
				{
				*msg = buf; /* restore original buffer */
				if ( debug )
					tracef ( __LINE__, FALSE, "***unable to allocate %d bytes for err/warn text", x );
				return;
				}

			*msgSize = x;
			*msgLeft += ERR_WARN_CHUNK_SIZE;

			**msg = 0;
			strcat ( *msg, buf ); /* copy current buffer */
			free ( buf );         /* deallocate current buffer */
			}

		strcat ( *msg, msgText );
		*msgLeft -= msgLen;

		if ( debug  > 3 )
			tracef ( __LINE__, FALSE, "added %d characters to err/warn text; buffer = %d; left = %d",
			                           msgLen, *msgSize, *msgLeft );
		}

    /*TBD: generate a mail message to inform user of error. */

    } /* end syntax_error */

