
/*  W3 Copyright statement 
Copyright 1995 by: Massachusetts Institute of Technology (MIT), INRIA</H2>

This W3C software is being provided by the copyright holders under the
following license. By obtaining, using and/or copying this software,
you agree that you have read, understood, and will comply with the
following terms and conditions: 

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee or royalty is hereby
granted, provided that the full text of this NOTICE appears on
<EM>ALL</EM> copies of the software and documentation or portions
thereof, including modifications, that you make. 

<B>THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF EXAMPLE,
BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
COPYRIGHT HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE
OR DOCUMENTATION.

The name and trademarks of copyright holders may NOT be used
in advertising or publicity pertaining to the software without
specific, written prior permission.  Title to copyright in this
software and any associated documentation will at all times remain
with copyright holders. 
*/
/*								      CSParse.c
**	PICS CONFIGURATION MANAGER FOR CLIENTS AND SERVERS
**
**	(c) COPYRIGHT MIT 1995.
**	Please first read the full copyright statement in the file COPYRIGHT.
**
**	This module converts application/xpics streams (files or network) to PICS_ class data
**
** History:
**	 4 Dec 95  EGP  start
**	15 Feb 96  EGP  alpha 1
**
** BUGS: no code yet; doesn't actually do anything
*/

/* Library include files */
/* --- BEGIN added by mharmsen@netscape.com on 7/9/97 --- */
#include "xp.h"
/* --- END added by mharmsen@netscape.com on 7/9/97 --- */
/* #include "sysdep.h" jhines 7/9/97 */
#include "htchunk.h"
#include "htstring.h"
#include "cslutils.h"
#include "csparse.h"

PUBLIC int ParseDebug = 0;	/* For use with LablPars and RatPars */

PUBLIC BOOL BVal_readVal(BVal_t * pBVal, const char * valueStr)
{
    /* --- BEGIN converted by mharmsen@netscape.com on 7/9/97 --- */
    if (!XP_STRCASECMP(valueStr, "true") || 
        !XP_STRCASECMP(valueStr, "yes"))
        pBVal->state = BVal_YES;
    else if (XP_STRCASECMP(valueStr, "false") && 
             XP_STRCASECMP(valueStr, "no"))
        return NO;;
    /* --- END converted by mharmsen@netscape.com on 7/9/97 --- */
    pBVal->state |= BVal_INITIALIZED;
    return YES;
}

PUBLIC BOOL BVal_initialized(const BVal_t * pBVal)
{
    return (pBVal->state & BVal_INITIALIZED);
}

PUBLIC BOOL BVal_value(const BVal_t * pBVal)
{
    return ((pBVal->state & BVal_YES) ? 1 : 0);
}

PUBLIC void BVal_set(BVal_t * pBVal, BOOL value)
{
	if (value)
		pBVal->state = BVal_YES;
    pBVal->state |= BVal_INITIALIZED;
    return;
}

PUBLIC void BVal_clear(BVal_t * pBVal)
{
    if (pBVal)
      pBVal->state = BVal_UNINITIALIZED;
    return;
}

PUBLIC BOOL FVal_readVal(FVal_t * pFVal, const char * valueStr)
{
    /* --- BEGIN converted by mharmsen@netscape.com on 7/9/97 --- */
    if (!XP_STRCASECMP(valueStr, "+INF")) {
        pFVal->stat = FVal_POSITIVE_INF;
        return YES;
    }
    if (!XP_STRCASECMP(valueStr, "-INF")) {
        pFVal->stat = FVal_NEGATIVE_INF;
        return YES;
    }
    /* --- END converted by mharmsen@netscape.com on 7/9/97 --- */
    pFVal->stat = FVal_VALUE;
    sscanf(valueStr, "%f", &pFVal->value);
    return YES;
}

PUBLIC BOOL FVal_initialized(const FVal_t * pFVal)
{
    return (pFVal->stat != FVal_UNINITIALIZED);
}

PUBLIC float FVal_value(const FVal_t * pFVal)
{
    return (pFVal->value);
}

/* C U T T I N G   E D G E   M A T H   T E C H N O L O G Y   H E R E */
PRIVATE BOOL FVal_lessThan(const FVal_t * pSmall, const FVal_t * pBig)
{
    if (pBig->stat == FVal_UNINITIALIZED || pSmall->stat == FVal_UNINITIALIZED)
        return FALSE;
    if (pBig->stat == FVal_POSITIVE_INF || pSmall->stat == FVal_NEGATIVE_INF) {
        if (pSmall->stat == FVal_POSITIVE_INF)
            return FALSE;
        return TRUE;
    }
    if (pBig->stat == FVal_NEGATIVE_INF || pSmall->stat == FVal_POSITIVE_INF) {
        return FALSE;
    }
    return pSmall->value < pBig->value;
}

PUBLIC FVal_t FVal_minus(const FVal_t * pBig, const FVal_t * pSmall)
{
    FVal_t ret = FVal_NEW_UNINITIALIZED;
    /* no notion of 2 time infinity so please keep your limits to a minimum */
    if (pBig->stat == FVal_UNINITIALIZED || pSmall->stat == FVal_UNINITIALIZED)
        return ret;
    FVal_set(&ret, (float)0.0);
    if (pBig->stat == FVal_POSITIVE_INF || pSmall->stat == FVal_NEGATIVE_INF) {
        if (pSmall->stat != FVal_POSITIVE_INF)
            FVal_setInfinite(&ret, 0);
        return ret;
    }
    if (pBig->stat == FVal_NEGATIVE_INF || pSmall->stat == FVal_POSITIVE_INF) {
        if (pSmall->stat != FVal_NEGATIVE_INF)
            FVal_setInfinite(&ret, 0);
        return ret;
    }
    ret.value = pBig->value - pSmall->value;
    return (ret);
}

PUBLIC BOOL FVal_nearerZero(const FVal_t * pRef, const FVal_t * pCheck)
{
    if (pRef->stat == FVal_UNINITIALIZED || pCheck->stat == FVal_UNINITIALIZED || 
        pCheck->stat == FVal_POSITIVE_INF || pCheck->stat == FVal_NEGATIVE_INF)
        return NO;
    if (pRef->stat == FVal_POSITIVE_INF || pRef->stat == FVal_NEGATIVE_INF)
        return YES;
    if (pRef->value < 0.0) {
        if (pCheck->value < 0.0)
            return pCheck->value > pRef->value;
        return pCheck->value < -pRef->value;
    }
    if (pCheck->value < 0.0)
        return pCheck->value > -pRef->value;
    return pCheck->value < pRef->value;
}

PUBLIC BOOL FVal_isZero(const FVal_t * pFVal)
{
    if (pFVal->stat == FVal_VALUE && pFVal->value == 0.0)
        return YES;
    return NO;
}

PUBLIC void FVal_set(FVal_t * pFVal, float value)
{
    pFVal->value = value;
    pFVal->stat = FVal_VALUE;
}

PUBLIC void FVal_setInfinite(FVal_t * pFVal, BOOL negative)
{
    pFVal->stat = negative ? FVal_NEGATIVE_INF : FVal_POSITIVE_INF;
}

PUBLIC int FVal_isInfinite(const FVal_t * pFVal)
{
    return (pFVal->stat == FVal_POSITIVE_INF ? 1 : pFVal->stat == FVal_NEGATIVE_INF ? -1 : 0);
}

PUBLIC void FVal_clear(FVal_t * pFVal)
{
    if (pFVal)
      pFVal->stat = FVal_UNINITIALIZED;
    return;
}

PUBLIC char * FVal_toStr(FVal_t * pFVal)
{
    char * ptr;
    if ((ptr = (char *)HT_MALLOC(40)) == NULL)
	HT_OUTOFMEM("FVal buffer");
    sprintf(ptr, "%.1f", FVal_value(pFVal));
    return ptr;
}

PUBLIC BOOL SVal_readVal(SVal_t * pSVal, const char * valueStr)
{
    pSVal->initialized = YES;
    StrAllocCopy(pSVal->value, valueStr);
    return YES;
}

PUBLIC BOOL SVal_initialized(const SVal_t * pSVal)
{
    return (pSVal->initialized != NO);
}

PUBLIC char * SVal_value(const SVal_t * pSVal)
{
    return (pSVal->value);
}

PUBLIC void SVal_clear(SVal_t * pSVal)
{
    if (SVal_initialized(pSVal)) {
      HT_FREE(pSVal->value);
      pSVal->initialized = NO;
      }
    return;
}

#if 0
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int timeZoneHours;
    int timeZoneMinutes;
#endif
PUBLIC BOOL DVal_readVal(DVal_t * pDVal, const char * valueStr)
{
    char space[] = "1994.11.05T08:15-0500";
    char timeZoneSign;
    char timeZoneMinutesMSB;
    /* --- BEGIN converted by mharmsen@netscape.com on 7/9/97 --- */
    if (XP_STRLEN(valueStr) != 0x15)
        return NO;
    XP_MEMCPY(space, valueStr, 0x15);
    timeZoneSign = space[16];
    timeZoneMinutesMSB = space[19];
    space[4] = space[7] = space[10] = space[13] = space[16] = space[19] = 0;
    pDVal->year = XP_ATOI(space);
    pDVal->month = XP_ATOI(space+5);
    pDVal->day = XP_ATOI(space+8);
    pDVal->hour = XP_ATOI(space+11);
    pDVal->minute = XP_ATOI(space+14);
    pDVal->timeZoneHours = XP_ATOI(space+17);
    space[19] = timeZoneMinutesMSB;
    pDVal->timeZoneMinutes = XP_ATOI(space+19);
    /* --- END converted by mharmsen@netscape.com on 7/9/97 --- */
    if (timeZoneSign == '-') {
        pDVal->timeZoneHours = -pDVal->timeZoneHours;
        pDVal->timeZoneMinutes = -pDVal->timeZoneMinutes;
    }
    StrAllocCopy(pDVal->value, valueStr);
    pDVal->initialized = YES;
    return YES;
}

PUBLIC BOOL DVal_initialized(const DVal_t * pDVal)
{
    return (pDVal->initialized != NO);
}

PUBLIC int DVal_compare(const DVal_t * a, const DVal_t * b)
{
    if (a->year > b->year) return 1;
    if (a->year < b->year) return -1;
    if (a->month > b->month) return 1;
    if (a->month < b->month) return -1;
    if (a->day > b->day) return 1;
    if (a->day < b->day) return -1;
    if (a->hour+a->timeZoneHours > b->hour+b->timeZoneHours) return 1;
    if (a->hour+a->timeZoneHours < b->hour+b->timeZoneHours) return -1;
    if (a->minute+a->timeZoneMinutes > b->minute+b->timeZoneMinutes) return 1;
    if (a->minute+a->timeZoneMinutes < b->minute+b->timeZoneMinutes) return -1;
    return 0;
}

PUBLIC char * DVal_value(const DVal_t * pDVal)
{
    return (pDVal->value);
}

PUBLIC void DVal_clear(DVal_t * pDVal)
{
    if (DVal_initialized(pDVal)) {
        HT_FREE(pDVal->value);
	pDVal->initialized = NO;
    }
    return;
}

PUBLIC char * Range_toStr(Range_t * pRange)
{
    HTChunk * pChunk;
    char * ptr;
    pChunk = HTChunk_new(20);
    ptr = FVal_toStr(&pRange->min);
    HTChunk_puts(pChunk, ptr);
    HT_FREE(ptr);
    if (FVal_initialized(&pRange->max)) {
        ptr = FVal_toStr(&pRange->max);
	HTChunk_puts(pChunk, ":");
	HTChunk_puts(pChunk, ptr);
	HT_FREE(ptr);
    }
    return HTChunk_toCString(pChunk);
}

/* Range_gap - find gap between 2 ranges. Either of these ranges may be a 
 * single value (in the min)
 * negative vector indicates that ref is greater than test
 */
PUBLIC FVal_t Range_gap(Range_t * a, Range_t * b)
{
    Range_t aN = *a;
    Range_t bN = *b;
    FVal_t ret = FVal_NEW_UNINITIALIZED;
    if (!FVal_initialized(&a->min) || !FVal_initialized(&b->min))
        return (ret);

    /* set ret for successful 0 returns */
    FVal_set(&ret, (float)0.0);

    /* normalize our ranges */
    if (FVal_lessThan(&aN.max, &aN.min)) {
        aN.max = a->min;
        aN.min = a->max;
    }
    if (FVal_lessThan(&bN.max, &bN.min)) {
        bN.max = b->min;
        bN.min = b->max;
    }

    /* check partial ranges (just a min, no max) */
    if (!FVal_initialized(&aN.max)) {
        if (!FVal_initialized(&bN.max))
            return FVal_minus(&aN.min, &bN.min);
        if (FVal_lessThan(&aN.min, &bN.min))
            return FVal_minus(&bN.min, &aN.min);
        if (FVal_lessThan(&bN.max, &aN.min))
            return FVal_minus(&bN.max, &aN.min);
        return ret;
    }
    /* we have four values to compare */
    {
        FVal_t minDif = FVal_minus(&bN.min, &aN.min);
        FVal_t maxDif = FVal_minus(&bN.max, &aN.max);
        Range_t common;
        common.min = FVal_lessThan(&bN.min, &aN.min) ? aN.min : bN.min;
        common.max = FVal_lessThan(&bN.max, &aN.max) ? bN.max : aN.max;
        if (!FVal_lessThan(&common.max, &common.min))
            return ret;
        /* failure - indicate how far we are off */
        return FVal_nearerZero(&minDif, &maxDif) ? minDif : maxDif;
    }
}

/* ------------------------------------------------------------------------- */

/* C O N S T R U C T O R S */
PUBLIC CSParse_t * CSParse_new(void)
{
    CSParse_t * me;
	if ((me = (CSParse_t *) HT_CALLOC(1, sizeof(CSParse_t))) == NULL)
	    HT_OUTOFMEM("CSParse");
    me->nowIn = NowIn_NEEDOPEN;
    me->token = HTChunk_new(0x10);
	if ((me->pParseContext = (ParseContext_t *) HT_CALLOC(1, sizeof(ParseContext_t))) == NULL)
	    HT_OUTOFMEM("ParseContext_t");
    return me;
}

PUBLIC void CSParse_delete(CSParse_t * me)
{
	HT_FREE(me->pParseContext);
	HTChunk_delete(me->token);
	HT_FREE(me);
}

/* L A B E L   P A R S E R S */
PRIVATE StateRet_t callErrorHandler(CSParse_t * pCSParse, 
				    const char * errorLocation,
				    char demark, StateRet_t errorCode)
{
    char * token = HTChunk_data(pCSParse->token);
    /* --- BEGIN removed compiler warning by mharmsen@netscape.com on 7/9/97 --- */
    pCSParse->pParseContext->pTokenError = (char *)errorLocation;
    /* --- END removed compiler warning by mharmsen@netscape.com on 7/9/97 --- */
    return (*pCSParse->pParseContext->pParseErrorHandler)(pCSParse, token, 
					      demark, StateRet_ERROR_BAD_CHAR);
}

/* CSParse_parseChunk - elemental parse engine for all pics nowIns. This passes 
 * tokenized data into the handler functions in the CSParse_t.handlersOf. These 
 * handlers are responsibel for placing the data in the appropriate target.
 * The text is broken into nowIns and passed a SubParser based on the current 
 * nowIn which is one of:
 *  NowIn_NEEDOPEN - get paren and go to NowIn_ENGINE, text is an error
 *  NowIn_ENGINE - in a containing structure, text goes to engineOf_
 *  NowIn_NEEDCLOSE - get paren and go to NowIn_ENGINE, text is an error
 *  NowIn_END - expect no more text or parens
 *  NowIn_ERROR - 
 */
PUBLIC CSDoMore_t CSParse_parseChunk (CSParse_t * pCSParse, const char * ptr, int len, void * pVoid)
{
    int i;
    if (!len || !ptr)
        return CSDoMore_error;
    for (i = 0; i < len; i++) {
        pCSParse->offset++;
        if (pCSParse->quoteState) {
            if (pCSParse->quoteState == ptr[i]) {
                pCSParse->quoteState = 0;
                pCSParse->demark = ' ';
            }
            else
                HTChunk_putb(pCSParse->token, ptr+i, 1);
            continue;
        }
        if (ptr[i] == SQUOTE || ptr[i] == DQUOTE) {
            if (pCSParse->demark) {
                while ((pCSParse->nowIn = (*pCSParse->pParseContext->engineOf)(pCSParse, ' ', pVoid)) == NowIn_CHAIN)
                	; /* */
                HTChunk_clear(pCSParse->token);
                pCSParse->demark = 0;
            } else if (HTChunk_size(pCSParse->token) && 
/*                  && warn(pCSParse, message_UNEXPECTED_CHARACTER, ptr[i])) */
		       callErrorHandler(pCSParse, ptr+i, ptr[i], 
					StateRet_ERROR_BAD_CHAR) !=StateRet_OK)
		    pCSParse->nowIn = NowIn_ERROR;
            pCSParse->quoteState = ptr[i];
            pCSParse->pParseContext->observedQuotes = YES;
            continue;
        }
        switch (pCSParse->nowIn) {
            case NowIn_NEEDOPEN:
                if (ptr[i] == LPAREN) {
                    pCSParse->nowIn = NowIn_ENGINE;
                    continue;
                }
                if (XP_IS_SPACE(ptr[i]))
                    continue;
/*                if (warn(pCSParse, message_UNEXPECTED_CHARACTER, ptr[i])) pCSParse->nowIn = NowIn_ERROR; */
	        if (callErrorHandler(pCSParse, ptr+i, ptr[i], 
				     StateRet_ERROR_BAD_CHAR) !=StateRet_OK)
		    pCSParse->nowIn = NowIn_ERROR;
                continue;
            case NowIn_ENGINE:
                if (XP_IS_SPACE(ptr[i])) {
                    if (HTChunk_size(pCSParse->token))
                        pCSParse->demark = ' ';
                    continue;
                }
                if (ptr[i] == LPAREN || ptr[i] == RPAREN || pCSParse->demark) {
		    /* parens override space demarkation */
		    if (ptr[i] == LPAREN) pCSParse->demark = LPAREN;
		    if (ptr[i] == RPAREN) pCSParse->demark = RPAREN;
		    /* call the engine as long as it wants re-entrance */
                    while ((pCSParse->nowIn = (*pCSParse->pParseContext->engineOf)(pCSParse, pCSParse->demark, pVoid)) == NowIn_CHAIN)
                    	; /* */
                    HTChunk_clear(pCSParse->token);
                    pCSParse->demark = 0;
                    if (ptr[i] == LPAREN || ptr[i] == RPAREN)
                        continue;
                    /* continue with next token */
                }
                HTChunk_putb(pCSParse->token, ptr+i, 1);
                continue;
            case NowIn_NEEDCLOSE:
                if (ptr[i] == RPAREN) {
                    pCSParse->nowIn = NowIn_ENGINE;
                    continue;
                }
                if (XP_IS_SPACE(ptr[i]))
                    continue;
		if (callErrorHandler(pCSParse, ptr+i, ptr[i], 
				     StateRet_ERROR_BAD_CHAR) !=StateRet_OK)
		    pCSParse->nowIn = NowIn_ERROR;
/*                if (warn(pCSParse, message_UNEXPECTED_CHARACTER, ptr[i])) pCSParse->nowIn = NowIn_ERROR; */
                continue;
            case NowIn_END:
#if 0 /* enable this to tell the parser to check the remainder of 
		 the stream after the parsed object thinks it is done */
                if (XP_IS_SPACE(ptr[i]))
                    continue;
/*                if (warn(pCSParse, message_UNEXPECTED_CHARACTER, ptr[i])) pCSParse->nowIn = NowIn_ERROR; */
		if (callErrorHandler(pCSParse, ptr+i, ptr[i], 
				     StateRet_ERROR_BAD_CHAR) !=StateRet_OK)
		    pCSParse->nowIn = NowIn_ERROR;
                continue;
#else
                return CSDoMore_done;
#endif
            case NowIn_MATCHCLOSE:
                if (ptr[i] == RPAREN) {
                    if (!pCSParse->depth)
                        pCSParse->nowIn = NowIn_ENGINE;
                    else
                        pCSParse->depth--;
                }
                if (ptr[i] == LPAREN)
                    pCSParse->depth++;
                continue;
            case NowIn_ERROR:
                return CSDoMore_error;
                break;
            default:
/*                if (warn(pCSParse, message_INTERNAL_ERROR, "bad nowIn")) pCSParse->nowIn = NowIn_ERROR; */
		HTTrace("PICS: Internal error in parser - bad nowIn:%d.\n", 
			pCSParse->nowIn);
		return CSDoMore_error;
        }
    }
    /* check completion */
    return pCSParse->nowIn == NowIn_END ? CSDoMore_done : CSDoMore_more;
}

PUBLIC BOOL Punct_badDemark(Punct_t validPunctuation, char demark)
{
    switch (demark) {
		case ' ': return (!(validPunctuation & Punct_WHITE));
		case LPAREN: return (!(validPunctuation & Punct_LPAREN));
		case RPAREN: return (!(validPunctuation & Punct_RPAREN));
	}
    return YES;
}

#if 0
PRIVATE void Input_dump(char * token, char demark)
{
    char space[256];
    sprintf(space, " %s |%c|\n", token, demark);
    HTTrace(space);
}
#endif
PRIVATE char * CSParse_subState2str(SubState_t subState)
{
    static char space[33];
    space[0] = 0;
    /* --- BEGIN converted by mharmsen@netscape.com on 7/9/97 --- */
    if (subState == SubState_N)
        XP_STRCPY(space, "N");
    else if (subState == SubState_X)
        XP_STRCPY(space, "X");
    else {
        int i;
	SubState_t comp;
	char ch[] = "A";
	for (i = 1, comp = SubState_A; i < (sizeof(SubState_t)*8 - 1); i++, (*ch)++, comp<<=1)
	    if (comp & subState)
	        XP_STRCAT(space, ch);
    }
    /* --- END converted by mharmsen@netscape.com on 7/9/97 --- */
    return space;
}

PRIVATE int ParseTrace(const char * fmt, ...)
{
    va_list pArgs;

    va_start(pArgs, fmt);

    if (!ParseDebug)
        return 0;
    return (vfprintf(stderr, fmt, pArgs));
}

PUBLIC NowIn_t CSParse_targetParser(CSParse_t * pCSParse, char demark, void * pVoid)
{
/*    ParseContext_t * pParseContext = pCSParse->pParseContext; */
    TargetObject_t * pTargetObject = pCSParse->pTargetObject;
    BOOL failedOnPunct = NO;
    char * token = 0;
    StateRet_t ret = StateRet_OK;
    int i;
static NowIn_t lastRet = NowIn_END;

	/* changed by montulli@netscape.com 11/29/97 
     * if (HTChunk_size(pCSParse->token)) {
     *   HTChunk_terminate(pCSParse->token);
     *   token = HTChunk_data(pCSParse->token);
     * }
	 */
	if(HTChunk_data(pCSParse->token))
	{
		HTChunk_terminate(pCSParse->token);
		token = HTChunk_data(pCSParse->token);
	}

	/*Input_dump(token, demark);*/
    for (i = 0; i < pTargetObject->stateTokenCount; i++) {
        StateToken_t * pStateToken = pTargetObject->stateTokens + i;
        pCSParse->pStateToken = pStateToken;

        if (!(pCSParse->currentSubState & pStateToken->validSubStates))
            continue;
        if (pStateToken->pCheck) {  /* use check function */
            StateRet_t checkRes;
            checkRes = (*pStateToken->pCheck)(pCSParse, pStateToken, token, demark);
            switch (checkRes) {
                case StateRet_WARN_BAD_PUNCT:
                    failedOnPunct = YES;
                case StateRet_WARN_NO_MATCH:
                    continue;
                case StateRet_ERROR_BAD_CHAR:
		    (*pCSParse->pParseContext->pParseErrorHandler)(pCSParse, token, demark, StateRet_ERROR_BAD_CHAR);
		    /*                    if (pTargetObject->pDestroy)
		        (*pTargetObject->pDestroy)(pCSParse); */
                    return NowIn_ERROR;
	        default:
		    break;
            }
        } else {                    /* or match by name[s] */
            if (!(pStateToken->command & Command_MATCHANY)) {
	        if (token && pStateToken->name1) {
                    /* --- BEGIN converted by mharmsen@netscape.com on 7/9/97 --- */
        	    if (XP_STRCASECMP(token, pStateToken->name1) && (!pStateToken->name2 || XP_STRCASECMP(token, pStateToken->name2)))
       		        continue;
                    /* --- END converted by mharmsen@netscape.com on 7/9/97 --- */
	        } else {
		    if (token != pStateToken->name1)
	                continue;
	        }
	    }
            if (Punct_badDemark(pStateToken->validPunctuation, demark)) {
                failedOnPunct = YES;
                continue;
            }
        }
/* open or close and do the appropriate callbacks */
	if (lastRet != NowIn_CHAIN)
	    ParseTrace("%30s %c ", token ? token : "", demark);
        ParseTrace("%10s - %s:%10s => ", pCSParse->pTargetObject->note, CSParse_subState2str(pCSParse->currentSubState), pStateToken->note);
	if (pStateToken->command & Command_NOTOKEN) {
	    HTChunk_clear(pCSParse->token);
		token = 0;
	}
	if (pStateToken->command & Command_OPEN && pTargetObject->pOpen)
	    if ((*pTargetObject->pOpen)(pCSParse, token, demark) == StateRet_ERROR)
			return NowIn_ERROR;
    
	if (pStateToken->command & (Command_OPEN|Command_CLOSE) && pCSParse->pParseContext->pTargetChangeCallback) {
	    ParseTrace("%3d", pStateToken->command & Command_CLOSE ? -(int)pTargetObject->targetChange : pTargetObject->targetChange);
	    if ((*pCSParse->pParseContext->pTargetChangeCallback)(pCSParse, pTargetObject, pTargetObject->targetChange, 
		(BOOL)(pStateToken->command & Command_CLOSE), pVoid) == StateRet_ERROR)
		return NowIn_ERROR;
	} else
	    ParseTrace("   ");
        if (pStateToken->command & Command_CLOSE && pTargetObject->pClose)
            ret = (*pTargetObject->pClose)(pCSParse, token, demark);

        if (pStateToken->pPrep && ret != NowIn_ERROR)
            ret = (*pStateToken->pPrep)(pCSParse, token, demark);
        if (pStateToken->pNextTargetObject)
            pCSParse->pTargetObject = pStateToken->pNextTargetObject;
        if (pStateToken->nextSubState != SubState_X)
            pCSParse->currentSubState = pStateToken->nextSubState;
/*
CSLabel_dump(pCSLabel);
HTTrace(pCSParse->pTargetObject->note);
*/
        ParseTrace("%10s - %s", pCSParse->pTargetObject->note, CSParse_subState2str(pCSParse->currentSubState));
        if (pStateToken->command & Command_CHAIN) {
	    ParseTrace(" -O-O-");
            return lastRet = NowIn_CHAIN;
	}
	ParseTrace("\n");
        return lastRet = ret == StateRet_ERROR_BAD_CHAR ? NowIn_ERROR : ret == StateRet_DONE ? NowIn_END : NowIn_ENGINE;
    }
    (*pCSParse->pParseContext->pParseErrorHandler)(pCSParse, token, demark, failedOnPunct ? StateRet_WARN_BAD_PUNCT : StateRet_WARN_NO_MATCH);
    if (pTargetObject->pDestroy)
	    (*pTargetObject->pDestroy)(pCSParse);
    return NowIn_ERROR;
}

