/* 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 */ #include "wwwsys.h" #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) { if (!strcasecomp(valueStr, "true") || !strcasecomp(valueStr, "yes")) pBVal->state = BVal_YES; else if (strcasecomp(valueStr, "false") && strcasecomp(valueStr, "no")) return NO;; 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) { if (!strcasecomp(valueStr, "+INF")) { pFVal->stat = FVal_POSITIVE_INF; return YES; } if (!strcasecomp(valueStr, "-INF")) { pFVal->stat = FVal_NEGATIVE_INF; return YES; } 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 */ PUBLIC 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; if (strlen(valueStr) != 0x15) return NO; 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 = atoi(space); pDVal->month = atoi(space+5); pDVal->day = atoi(space+8); pDVal->hour = atoi(space+11); pDVal->minute = atoi(space+14); pDVal->timeZoneHours = atoi(space+17); space[19] = timeZoneMinutesMSB; pDVal->timeZoneMinutes = atoi(space+19); 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); pCSParse->pParseContext->pTokenError = errorLocation; 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 (isspace((int) 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 (isspace((int) 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 (isspace((int) 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 (isspace(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: HTTRACE(PICS_TRACE, "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; } PUBLIC char * CSParse_subState2str(SubState_t subState) { static char space[33]; space[0] = 0; if (subState == SubState_N) strcpy(space, "N"); else if (subState == SubState_X) 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) strcat(space, ch); } 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; if (HTChunk_size(pCSParse->token)) { HTChunk_terminate(pCSParse->token); token = HTChunk_data(pCSParse->token); } 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) { if (strcasecomp(token, pStateToken->name1) && (!pStateToken->name2 || strcasecomp(token, pStateToken->name2))) continue; } 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 ? -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; 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; }