/* * Copyright 1990 by Rayan S. Zachariassen, all rights reserved. * This will be free software, but only when it is finished. * * Copyright Matti Aarnio 1992-2000 */ #include "hostenv.h" #include "mailer.h" #include "prototypes.h" #undef STATIC #define STATIC static #undef ATHACK /* 'user at host' == 'user@host' */ #include "rfc822.sst.h" STATIC /* This will apply to the array in the .sst file */ #include "rfc822.sst.c" extern void EmitToken __((token822 *t, ComponentClass ac, struct address *ap)); STATIC const char * Input822TokenName __((int)); /* private semantics mechanism variables: */ STATIC ComponentClass currentTokenType; STATIC token822 *ptrNextInputToken, *ptrAcceptedToken; STATIC token822 *headPending, **pending; STATIC token822 *headDeferred, **deferred; /* Table Walker State */ STATIC int processing; /* are we running the table walker? */ STATIC int inAddress; /* are we collecting an address? */ STATIC int sslPointer; /* index into S/SL table */ STATIC TableOperation operation; /* the current operation */ /* convenience */ STATIC struct address nullAddr = { NULL, NULL, newAddress, 0, NULL, NULL }; #ifdef ATHACK STATIC token822 atsigntoken = { "@", 1, Special, NULL }; #endif /* Tracing Control */ STATIC FILE *tracefp; /* if non-null, trace output here */ /* Abort flag */ STATIC int aborted; /* * The Rule Call Stack implements Syntax/Semantic Language rule call and return. * Each time an oCall operation is executed, the table return address is pushed * onto the Rule Call Stack. When an oReturn is executed, the return address * is popped from the stack. An oReturn executed when the Rule Call Stack is * empty terminates table execution. */ STATIC int sslStack[127]; /* The S/SL Rule Call Stack */ #define sslStackSize (sizeof sslStack/sizeof sslStack[0]) STATIC int sslTop; /* * Set by the Choice Handler to indicate whether a match was made or the * otherwise path was taken. Set to true if a match was made and false * otherwise. This flag is used in input choices to indicate whether the * choice input token should be accepted or not. */ STATIC int choiceTagMatched; /* Choice Match Flag */ /* * These are used to hold the decoded parameter value to a parameterized * semantic operation and the result value returned by a choice semantic * operation or rule respectively. */ STATIC int parameterValue; /* Parameter for Semantic Operation */ STATIC int resultValue; /* Result from Semantic Operation */ /* S/SL System Failure Codes */ typedef enum { fSemanticChoiceFailed, /* 0 */ fChoiceRuleFailed /* 1 */ } FailureCodes; /* S/SL System Failure Code Type */ #define maxErrors 20 STATIC int noErrors; /* Error Counter */ STATIC ErrorCodes firstFatalErrorCode = eSslStackOverflow; /* fix */ /* Input Interface */ STATIC InputTokens nextInputToken = tNewLine; STATIC InputTokens acceptedToken; struct receivedtag { const char *name; int len; InputTokens value; }; STATIC struct receivedtag rt[] = { { "from", 4, tFrom }, { "by", 2, tBy }, { "id", 2, tId }, { "with", 4, tWith }, { "via", 3, tVia }, { "for", 3, tFor }, { "convert", 7, tConvert }, { 0, 0, tSyntaxError } }; /* Variables Used in Syntax Error Recovery */ STATIC InputTokens savedInputToken; STATIC int RunningSslRecovery = 0; STATIC struct { ErrorCodes ec; const char *msg; } ear[] = { { eSyntaxError, "Syntax error" }, { ePrematureEndOfFile, "Unexpected end of file" }, { eExtraneousProgramText, "Extraneous program text" }, { eSslStackOverflow, "Nesting too deep" }, { eExtraneousTokensInAddress, "extraneous tokens in address" }, { eExtraneousTokensInMailbox, "extraneous tokens in mailbox" }, { eMissingSemicolonToEndGroup, "missing semicolon to end mail group" }, { eMissingSemicolonInReceived, "missing semicolon before timestamp" }, { eMissingEndOfAddress, "missing end of address" }, { eMissingEndOfMailbox, "missing end of mailbox" }, { eIllegalWordInPhrase, "illegal word in phrase" }, { eIllegalSpecialInPhrase, "illegal special character in phrase" }, { eIllegalPeriodInPhrase, "illegal period in phrase" }, { eIllegalPhraseMustBeQuoted, "phrases containing '.' must be quoted" }, { eIllegalSubdomainInDomain, "illegal subdomain in domain, probably extra '.' at the end of the address" }, { eIllegalTokenInRoute, "illegal token in route" }, { eIllegalWordInLocalPart, "illegal word in localpart, probably extra '.' at the end of the address" }, { eIllegalStartOfMessageId, "illegal start of message identification"}, { eIllegalEndOfMessageId, "illegal end of message identification" }, { eIllegalEncryptionIdentifier, "illegal encryption Identifier" }, { eIllegalAddressSeparator, "illegal address separator" }, { eIllegalMailboxSeparator, "illegal mailbox separator" }, { eIllegalMessageIDSeparator, "illegal message-id separator" }, { eExpectedWord, "expected word" }, { eIllegalStartOfRouteAddress, "illegal start of route address" }, { eIllegalEndOfRouteAddress, "illegal end of route address" }, { eIllegalSpecialInValue, "illegal special in value" }, { eIllegalReferencesSeparator, "illegal reference separator" }, }; /* This procedure emits the error message associated with errCode */ STATIC void SslError __((ErrorCodes, struct address *)); STATIC void SslError(errCode, ap) ErrorCodes errCode; struct address *ap; { token822 *t; int i; if (errCode == eNoError) abort(); for (t = NULL, i = 0; i < (sizeof ear/sizeof ear[0]); ++i) { if (ear[i].ec == errCode) { t = makeToken(ear[i].msg, strlen(ear[i].msg)); break; } } if (t == NULL) t = makeToken("Unknown", sizeof("Unknown")-1); t->t_type = Error; EmitToken(t, cError, ap); ++noErrors; if ((int)(errCode) >= (int)(firstFatalErrorCode) || noErrors == maxErrors) { aborted = 1; processing = 0; } } /* * This procedure provides the interface to the previous pass. * It is reponsible for handling all input including line number * indicators and the values and text associated with input tokens. */ STATIC void AcceptInputToken __((token822 **, struct address *)); STATIC void AcceptInputToken(tlistp, ap) token822 **tlistp; struct address *ap; { if (acceptedToken == tEndOfHeader && nextInputToken == tEndOfHeader) abort(); if (nextInputToken == tSyntaxError) { acceptedToken = tSyntaxError; /* ptrAcceptedToken = NULL; */ nextInputToken = savedInputToken; goto ok; } else { /* Accept Token */ acceptedToken = nextInputToken; ptrAcceptedToken = ptrNextInputToken; } /* Read Next Input Token */ do { next: if (*tlistp == NULL) { nextInputToken = tEndOfHeader; ptrNextInputToken = NULL; } else { ptrNextInputToken = *tlistp; switch ((*tlistp)->t_type) { case Error: case Comment: if (ptrAcceptedToken == NULL && ap != NULL) { EmitToken(*tlistp, (*tlistp)->t_type == Error ? cError : cComment, ap); } else { (*pending) = copyToken(*tlistp); pending = &((*pending)->t_next); } (*tlistp) = (*tlistp)->t_next; goto next; case Space: #if 0 # if 0 if (RunningSslRecovery) { /* Copy it only during SslRecovery */ if (ptrAcceptedToken == NULL && ap != NULL) { EmitToken(*tlistp, cSpace, ap); } else { (*pending) = copyToken(*tlistp); pending = &((*pending)->t_next); } } # else nextInputToken = tSpace; (*tlistp) = (*tlistp)->t_next; break; # endif #endif /* fall through */ case Fold: (*tlistp) = (*tlistp)->t_next; goto next; case Special: switch ((*tlistp)->t_pname[0]) { case ';': nextInputToken = tSemicolon; break; case ',': nextInputToken = tComma; break; case '.': nextInputToken = tPeriod; break; case ':': if ((*tlistp)->t_pname[1] == ':') nextInputToken = tDoubleColon; else nextInputToken = tColon; break; case '<': nextInputToken = tLeftAngle; break; case '>': nextInputToken = tRightAngle; break; case '@': nextInputToken = tAtSign; break; default: nextInputToken = tOtherSpecial; break; } *tlistp = (*tlistp)->t_next; break; case String: nextInputToken = tQuotedString; *tlistp = (*tlistp)->t_next; break; case Atom: nextInputToken = tAtom; *tlistp = (*tlistp)->t_next; break; case DomainLiteral: nextInputToken = tDomainLiteral; *tlistp = (*tlistp)->t_next; break; case Word: case Empty: abort(); default: break; } } } while (nextInputToken == tNewLine); ok: /* Trace Input */ if (tracefp) printf("Input token accepted %s (%d) Next input token %s (%d)\n", Input822TokenName((int)acceptedToken), acceptedToken, Input822TokenName((int)nextInputToken), nextInputToken); } /* * The constants, variables, types, modules and procedures used in * implementing the Semantic Mechanisms of the pass go here. These * implement the facilities used in the semantic operations. */ /* Syntax Error Handling */ /* * This procedure handles syntax errors in the input to the Parser pass, * * Syntax error recovery: * When a mismatch occurs between the the next input token and the syntax * table, the following recovery is employed. * * If the expected token is tEndOfHeader then if there has been no previous * syntax error on the line, ignore the error. (A missing logical end of * header is not a real error.) * * If the expected token is tComma or tSemicolon or tEndOfHeader and a syntax * error has already been detected in the current logical address (flagged * by nextInputToken == tSyntaxError), then flush the input and exit when * any of these tokens are found. * * Otherwise, if this is the first syntax error detected on the line * (flagged by nextInputToken != tSyntaxError), then if the input token is * tEndOfHeader then emit the ePrematureEndOfFile error code and terminate * execution. Otherwise, emit the eSyntaxError error code and set the * nextInputToken to tSyntaxError to prevent further input, and exit when the * expected ( ?? ) input is tSemicolon or tNewLine. * * If the expected token is not tSemicolon nor tNewLine and a syntax error * has already been detected on the current line (flagged by nextInputToken == * tSyntaxError), then do nothing and continue as if the expected token had * been matched. */ STATIC void SslSyntaxError __((token822 **, struct address *)); STATIC void SslSyntaxError(tlistp, ap) token822 **tlistp; struct address *ap; { if (operation != oInput && operation != oInputAny) abort(); if (nextInputToken == tSyntaxError) { /* Currently recovering from syntax error */ /* Complete recovery by synchronizing input to a new address */ nextInputToken = savedInputToken; RunningSslRecovery = 1; while (nextInputToken != tSemicolon && nextInputToken != tComma && nextInputToken != tEndOfHeader) { AcceptInputToken(tlistp, ap); EmitToken(ptrAcceptedToken, cResync, ap); } RunningSslRecovery = 0; if (sslTop == 0) /* Return from main S/SL procedure */ processing = 0; else sslPointer = sslStack[sslTop--]; } else { /* First syntax error on the line */ if (sslTable[sslPointer] == (int)(tEndOfHeader)) { /* Ignore missing logical newlines */ } else { /* mark syntax error, gobble tokens, jump to resync */ savedInputToken = nextInputToken; nextInputToken = tSyntaxError; } } } STATIC const char * Input822TokenName(in) int in; { switch ((InputTokens)in) { case tSyntaxError: return "tSyntaxError"; case tIdent: return "tIdent"; case tString: return "tString"; case tInteger: return "tInteger"; case tSemicolon: return "tSemicolon"; case tComma: return "tComma"; case tPeriod: return "tPeriod"; case tDoubleColon: return "tDoubleColon"; case tColon: return "tColon"; case tLeftAngle: return "tLeftAngle"; case tRightAngle: return "tRightAngle"; case tAtSign: return "tAtSign"; case tOtherSpecial: return "tOtherSpecial"; case tAtom: return "tAtom"; case tBy: return "tBy"; case tDomainLiteral: return "tDomainLiteral"; case tFor: return "tFor"; case tFrom: return "tFrom"; case tId: return "tId"; case tQuotedString: return "tQuotedString"; case tVia: return "tVia"; case tWith: return "tWith"; case tNewLine: return "tNewLine"; case tEndOfHeader: return "tEndOfHeader"; default: break; } return "unknown input token"; } STATIC const char * TableOperationName __((TableOperation)); STATIC const char * TableOperationName(op) TableOperation op; { switch (op) { case oCall: return "oCall"; case oReturn: return "oReturn"; case oRuleEnd: return "oRuleEnd"; case oJump: return "oJump"; case oInput: return "oInput"; case oInputAny: return "oInputAny"; case oInputChoice: return "oInputChoice"; case oEmit: return "oEmit"; case oError: return "oError"; case oChoice: return "oChoice"; case oChoiceEnd: return "oChoiceEnd"; case oSetParameter: return "oSetParameter"; case oSetResult: return "oSetResult"; case oSetResultFromInput: return "oSetResultFromInput"; case oSetComponentType: return "oSetComponentType"; case oEmitToken: return "oEmitToken"; case oEmitTokenCurType: return "oEmitTokenCurType"; case oOpenAddress: return "oOpenAddress"; case oAppendAddress: return "oAppendAddress"; case oCloseAddress: return "oCloseAddress"; case oDateTime: return "oDateTime"; case oEnterReceived: return "oEnterReceived"; case oExitReceived: return "oExitReceived"; case oSetReturnType: return "oSetReturnType"; case oRewind: return "oRewind"; default: break; } return "unknown operation"; } STATIC void SslTrace __((void)); STATIC void SslTrace() { printf("Table index %d Operation %d (%s) Argument %d\n", sslPointer-1, operation, TableOperationName(operation), sslTable[sslPointer]); } STATIC void SslFailure __((FailureCodes)); STATIC void SslFailure(failCode) FailureCodes failCode; { printf("### S/SL program failure: "); switch (failCode) { case fSemanticChoiceFailed: printf("Semantic choice failed\n"); break; case fChoiceRuleFailed: printf("Choice rule returned without a value\n"); break; } SslTrace(); abort(); } /* * This procedure performs both input and semantic choices. It sequentially * tests each alternative value against the tag value, and when a match is * found, performs a branch to the corresponding alternative path. If none * of the alternative values matches the tag value, sslTable interpretation * proceeds to the operation immediately following the list of alternatives * (normally the otherwise path). The flag choiceTagMatched is set to true * if a match is found and false otherwise. */ STATIC void SslChoice __((int)); STATIC void SslChoice(choiceTag) int choiceTag; { int numberOfChoices, choicePointer; choicePointer = sslTable[sslPointer]; choiceTagMatched = 0; for (numberOfChoices = sslTable[choicePointer++]; numberOfChoices > 0; choicePointer += 2, --numberOfChoices) { if (tracefp) printf("Matching %s (%d) with %s (%d)\n", Input822TokenName(choiceTag), choiceTag, Input822TokenName(sslTable[choicePointer]), sslTable[choicePointer]); if (sslTable[choicePointer] == choiceTag #ifdef ATHACK || (choiceTag == (int)tAtom && sslTable[choicePointer] == (int)tAtSign && ptrNextInputToken != NULL && ptrNextInputToken->t_len == 2 && (ptrNextInputToken->t_pname[0] == 'a' || ptrNextInputToken->t_pname[0] == 'A') && (ptrNextInputToken->t_pname[1] == 't' || ptrNextInputToken->t_pname[1] == 'T') && (ptrNextInputToken = &atsigntoken)) #endif /* ATHACK */ ) { sslPointer = sslTable[choicePointer+1]; choiceTagMatched = 1; if (tracefp) printf("Matched!\n"); return; } } if (tracefp) printf("No match!\n"); sslPointer = choicePointer; } void EmitToken(t, ac, ap) token822 *t; ComponentClass ac; struct address *ap; { token822 *nt, **ptp; struct addr *na; AddrComponent type; if (deferred != NULL) { if (ac == cError) { /* prepend errors to deferred tokens */ nt = copyToken(t); ptp = &headDeferred; for(t = headDeferred; t ; ptp = &t->t_next, t = *ptp) if (t->t_type != Error) break; nt->t_next = *ptp; *ptp = nt; if (deferred == &headDeferred) deferred = &nt->t_next; } else { (*deferred) = copyToken(t); deferred = &((*deferred)->t_next); } if (headPending) { /* printf("append pending\n"); */ (*deferred) = headPending; deferred = pending; (*pending) = NULL; pending = &headPending; headPending = 0; } return; } switch (ac) { case cPhrase: type = aPhrase; break; case cComment: type = aComment; break; case cSpecial: type = aSpecial; break; case cGroup: type = aGroup; break; case cAddress: type = anAddress; break; case cDomain: type = aDomain; break; case cWord: type = aWord; break; #if 0 case cSpace: type = aSpace; break; #endif case cResync: type = reSync; ap->a_stamp = BadAddress; break; case cError: default: type = anError; ap->a_stamp = BadAddress; break; } /* * For efficiency, we just keep prepending tokens. The * reverseComponent() function will eventually do the reversal. */ if (ap->a_tokens != NULL && ap->a_tokens->p_type == type) { /* prepend now -- reverse later */ nt = copyToken(t); nt->t_next = ap->a_tokens->p_tokens; ap->a_tokens->p_tokens = nt; } else { na = (struct addr *)tmalloc(sizeof (struct addr)); /* prepend now -- reverse later */ nt = copyToken(t); nt->t_next = NULL; na->p_tokens = nt; na->p_next = ap->a_tokens; na->p_type = type; ap->a_tokens = na; } if (headPending) { (*pending) = NULL; for (t = headPending, headPending = NULL; t != NULL; t = t->t_next) EmitToken(t, t->t_type==Error ? cError : cComment, ap); pending = &headPending; } } struct address * revaddress(ap) struct address *ap; { register token822 *rprev, *rnext, *r; register struct addr *pprev, *pnext, *p; /* reverse order of address components and tokens */ if (ap == NULL) return NULL; /* reverse the various linked lists */ pprev = NULL; for (p = ap->a_tokens; p != NULL; p = pnext) { pnext = p->p_next; p->p_next = pprev; pprev = p; rprev = NULL; for (r = p->p_tokens; r != NULL; r = rnext) { rnext = r->t_next; r->t_next = rprev; rprev = r; } p->p_tokens = rprev; } ap->a_tokens = pprev; return ap; } STATIC void revappend __((token822 **, struct address *)); STATIC void revappend(tprev,ap) token822 **tprev; struct address *ap; { struct addr *p; token822 *t; ap = revaddress(ap); /* append all the tokens together */ if (ap == NULL || ap->a_tokens == NULL || ap->a_stamp == BadAddress) return; for (p = ap->a_tokens; p ; p = p->p_next) { *tprev = p->p_tokens; for (t = p->p_tokens; t ; t = t->t_next) tprev = &(t->t_next); } *tprev = NULL; } /* * The Parser. * * entry - where in the S/SL table to start executing. * tlistp - pointer to raw token list as returned by the scanner. * tfp - trace file pointer (for debugging output) * * The parser can return any of the union misc type values, however the * usual case is when trying to recognize an address or mailbox of some kind. * In this case, the parser can return a list of address structures, each * of which describes an address or mailbox as appropriate. This description * consists of a categorization of which scanner tokens correspond to which * RFC822 tokens. Certain non-terminals in the RFC822 grammar can correspond * to simple word lists, for example phrases or comments. These appear as * token classes (the AddrComponent enum type) alongside Atoms and Specials * in what the parser returns. */ union misc parse822(entry, tlistp, ltmp, tfp) HeaderSemantics entry; token822 **tlistp; struct tm *ltmp; FILE *tfp; { int inReceived, i; token822 *t, *torig; struct address *ap, *na, *pap; struct received rcvd; union misc retval; ReturnValue returnType; tracefp = tfp; torig = *tlistp; pending = &headPending; deferred = NULL; inReceived = 0; inAddress = 0; ap = NULL; processing = 1; aborted = 0; sslTop = 0; noErrors = 0; nextInputToken = tNewLine; acceptedToken = tNewLine; returnType = rAddress; currentTokenType = cAddress; if (tracefp) { sslPointer = ((int)entry) + 1; operation = (TableOperation)-1; /* Invalid value! */ SslTrace(); } /* Initialize Input/Output */ AcceptInputToken(tlistp, ap); if (nextInputToken == tEndOfHeader) { retval.a = NULL; return retval; } /* Walk the S/SL Table */ sslPointer = (int)entry; while (processing || inAddress) { if (processing) operation = (TableOperation)(sslTable[sslPointer++]); else operation = oCloseAddress; /* Trace Execution */ if (tracefp) SslTrace(); switch (operation) { case oCall: if (sslTop < sslStackSize) { sslStack[++sslTop] = sslPointer + 1; sslPointer = sslTable[sslPointer]; } else { if (ap == NULL) { ap = (struct address *) tmalloc(sizeof (struct address)); *ap = nullAddr; inAddress = 1; } SslError(eSslStackOverflow, ap); processing = 0; } break; case oReturn: if (sslTop == 0) /* Return from main S/SL procedure */ processing = 0; else sslPointer = sslStack[sslTop--]; break; case oRuleEnd: SslFailure(fChoiceRuleFailed); break; case oJump: sslPointer = sslTable[sslPointer]; break; case oInput: if (sslTable[sslPointer] == (int)(nextInputToken) #ifdef ATHACK || (nextInputToken == tAtom && sslTable[sslPointer] == (int)tAtSign && ptrNextInputToken != NULL && ptrNextInputToken->t_len == 2 && (ptrNextInputToken->t_pname[0] == 'a' || ptrNextInputToken->t_pname[0] == 'A') && (ptrNextInputToken->t_pname[1] == 't' || ptrNextInputToken->t_pname[1] == 'T') && (ptrNextInputToken = &atsigntoken)) #endif /* ATHACK */ ) { AcceptInputToken(tlistp, ap); ++sslPointer; } else { ++sslPointer; /* Syntax error in input */ if (ap == NULL) { ap = (struct address *) tmalloc(sizeof (struct address)); *ap = nullAddr; inAddress = 1; } SslSyntaxError(tlistp, ap); } break; case oInputAny: if (nextInputToken != tEndOfHeader) AcceptInputToken(tlistp, ap); else { /* Premature end of file */ if (ap == NULL) { ap = (struct address *) tmalloc(sizeof (struct address)); *ap = nullAddr; inAddress = 1; } SslSyntaxError(tlistp, ap); } break; case oInputChoice: if (inReceived && nextInputToken == tAtom) { t = ptrNextInputToken; for (i = 0; rt[i].name != NULL; ++i) { if (TOKENLEN(t) == strlen(rt[i].name) && CISTREQN(t->t_pname, rt[i].name, rt[i].len)) { nextInputToken = rt[i].value; break; } } } SslChoice((int)nextInputToken); if (choiceTagMatched) AcceptInputToken(tlistp, ap); break; case oEmit: ++sslPointer; break; case oError: ++sslPointer; if (ap == NULL) { ap = (struct address *) tmalloc(sizeof (struct address)); *ap = nullAddr; inAddress = 1; } SslError((ErrorCodes)sslTable[sslPointer-1], ap); break; case oChoice: if (inReceived && resultValue == (int)tAtom) { t = ptrNextInputToken; for (i = 0; rt[i].name != NULL; ++i) { if (TOKENLEN(t) == strlen(rt[i].name) && CISTREQN(t->t_pname, rt[i].name, rt[i].len)) { resultValue = (int)rt[i].value; break; } } } SslChoice(resultValue); break; case oChoiceEnd: SslFailure(fSemanticChoiceFailed); break; case oSetParameter: parameterValue = sslTable[sslPointer++]; break; case oSetResult: resultValue = sslTable[sslPointer++]; break; case oSetResultFromInput: resultValue = (int)nextInputToken; break; /* The Following Are Pass Dependent Semantic Mechanisms */ case oSetComponentType: currentTokenType = (ComponentClass)parameterValue; break; case oEmitTokenCurType: parameterValue = (int)currentTokenType; /* fall through */ case oEmitToken: if (ap == NULL) { ap = (struct address *) tmalloc(sizeof (struct address)); *ap = nullAddr; inAddress = 1; } EmitToken(ptrAcceptedToken, (ComponentClass)parameterValue, ap); break; case oDeferEmitToken: if (deferred != NULL) abort(); deferred = &headDeferred; if (headPending) { if (ap == NULL) { ap = (struct address *) tmalloc(sizeof (struct address)); *ap = nullAddr; inAddress = 1; } (*pending) = NULL; for (t = headPending, headPending = NULL; t != NULL; t = t->t_next) EmitToken(t, t->t_type == Error ? cError : cComment, ap); pending = &headPending; } break; case oReleaseEmitToken: /* comments... */ if (ap == NULL) { ap = (struct address *) tmalloc(sizeof (struct address)); *ap = nullAddr; inAddress = 1; } t = headDeferred; headDeferred = NULL; (*deferred) = NULL; deferred = NULL; for (; t != NULL; t = t->t_next) if (t->t_type == Comment) EmitToken(t, cComment, ap); else if (t->t_type == Error) EmitToken(t, cError, ap); else EmitToken(t, (ComponentClass)parameterValue, ap); break; case oOpenAddress: if (inAddress) break; na = (struct address *)tmalloc(sizeof (struct address)); *na = nullAddr; na->a_next = ap; ap = na; inAddress = 1; break; case oAppendAddress: if (inAddress) break; ap = revaddress(ap); inAddress = 1; break; case oCloseAddress: if (!inAddress) break; ap = revaddress(ap); inAddress = 0; break; case oDateTime: if (ptrNextInputToken != NULL) { retval.d = dateParse(ltmp, ptrNextInputToken); } else retval.d = 0; break; case oEnterReceived: inReceived = 1; rcvd.r_from = rcvd.r_by = rcvd.r_id = rcvd.r_for = NULL; rcvd.r_via = rcvd.r_with = rcvd.r_convert = NULL; rcvd.r_time = 0L; break; case oExitReceived: inReceived = 0; break; case oSaveReceivedComponent: switch ((ReceivedComponent)parameterValue) { case rcFrom: rcvd.r_from = revaddress(ap); break; case rcBy: rcvd.r_by = revaddress(ap); break; case rcVia: /* grab a single token */ if (ap != NULL && ap->a_stamp != BadAddress && ap->a_tokens != NULL && ap->a_tokens->p_tokens != NULL) { rcvd.r_via = ap->a_tokens->p_tokens; rcvd.r_via->t_next = NULL; /* in case */ } break; case rcWith: revappend(&rcvd.r_with,ap); break; case rcConvert: revappend(&rcvd.r_convert,ap); break; case rcId: rcvd.r_id = revaddress(ap); break; case rcFor: rcvd.r_for = revaddress(ap); break; case rcDate: rcvd.r_time = retval.d; break; default: break; } ap = NULL; break; case oSetReturnType: returnType = (ReturnValue)parameterValue; break; case oRewind: if (ap == NULL || (ap->a_tokens == NULL && ap->a_next == NULL)) { *tlistp = torig; pending = &headPending; deferred = NULL; nextInputToken = tNewLine; acceptedToken = tNewLine; AcceptInputToken(tlistp, ap); resultValue = (int)Success; } else resultValue = (int)Failure; break; default: printf("Unknown operation %d\n", (int)operation); abort(); } } if (returnType == rAddress) { if (nextInputToken != tEndOfHeader && !aborted && ap != NULL) SslError(eExtraneousProgramText, ap); /* reverse order of addresses */ for (na = NULL; ap != NULL; na = ap, ap = pap) { pap = ap->a_next; ap->a_next = na; } retval.a = na; } else if (returnType == rReceived) { retval.r = (struct received *)tmalloc(sizeof (struct received)); *(retval.r) = rcvd; } return retval; }