%{

/***************************************************************************
(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
Business Machines Corporation and Siemens Rolm Communications Inc.

For purposes of this license notice, the term Licensors shall mean,
collectively, Apple Computer, Inc., AT&T Corp., International
Business Machines Corporation and Siemens Rolm Communications Inc.
The term Licensor shall mean any of the Licensors.

Subject to acceptance of the following conditions, permission is hereby
granted by Licensors without the need for written agreement and without
license or royalty fees, to use, copy, modify and distribute this
software for any purpose.

The above copyright notice and the following four paragraphs must be
reproduced in all copies of this software and any software including
this software.

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
MODIFICATIONS.

IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.

The software is provided with RESTRICTED RIGHTS.  Use, duplication, or
disclosure by the government are subject to restrictions set forth in
DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.

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

/*
 * src: vcc.c
 * doc: Parser for vCard and vCalendar. Note that this code is
 * generated by a yacc parser generator. Generally it should not
 * be edited by hand. The real source is vcc.y. The #line directives
 * can be commented out here to make it easier to trace through
 * in a debugger. However, if a bug is found it should
 * be fixed in vcc.y and this file regenerated.
 */


/* debugging utilities */
#if __DEBUG
#define DBG_(x) printf x
#else
#define DBG_(x)
#endif

/****  External Functions  ****/

/* assign local name to parser variables and functions so that
   we can use more than one yacc based parser.
*/

#undef yyparse
#define yyparse mimeO_parse
#undef yylex
#define yylex mimeO_lex
#undef yyerror
#define yyerror mimeO_error
#undef yychar
#define yychar mimeO_char
/* #define p_yyval p_mimeO_val */
#undef yyval
#define yyval mimeO_yyval
/* #define p_yylval p_mimeO_lval */
#undef yylval
#define yylval mimeO_yylval
#undef yydebug
#define yydebug mimeO_debug
#undef yynerrs
#define yynerrs mimeO_nerrs
#define yyerrflag mimeO_errflag
#define yyss mimeO_ss
#define yyssp mimeO_ssp
#define yyvs mimeO_vs
#define yyvsp mimeO_vsp
#define yylhs mimeO_lhs
#define yylen mimeO_len
#define yydefred mimeO_defred
#define yydgoto mimeO_dgoto
#define yysindex mimeO_sindex
#define yyrindex mimeO_rindex
#define yygindex mimeO_gindex
#define yytable mimeO_table
#define yycheck mimeO_check
#define yyname mimeO_name
#define yyrule mimeO_rule
#define YYPREFIX "mimeO_"


#ifndef _NO_LINE_FOLDING
#define _SUPPORT_LINE_FOLDING 1
#endif

/* undef below if compile with MFC */
/* #define INCLUDEMFC 1 */

#if defined(WIN32) || defined(_WIN32)
#ifdef INCLUDEMFC
#include <afx.h>
#endif
#endif

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "vcc.h"

static int yylex(void);
static void yyerror(char *s);

/****  Types, Constants  ****/

#define YYDEBUG		1	/* 1 to compile in some debugging code */
#define MAXTOKEN	256	/* maximum token (line) length */
#define YYSTACKSIZE 	50	/* ~unref ? */
#define MAXLEVEL	10	/* max # of nested objects parseable */
				/* (includes outermost) */


/****  Global Variables  ****/
int mimeO_lineNum, mimeO_numErrors; /* yyerror() can use these */
static VObjectO* vObjListO;
static VObjectO *curPropO;
static VObjectO *curObjO;
static VObjectO* ObjStackO[MAXLEVEL];
static int ObjStackTopO;


/* A helpful utility for the rest of the app. */
#if __CPLUSPLUS__
extern "C" {
#endif

    extern void Parse_DebugO(const char *s);
    extern void yyerror(char *s);

#if __CPLUSPLUS__
    };
#endif

int yyparse(void);

enum LexModeO {
	L_NORMAL,
	L_VCARD,
	L_VCAL,
	L_VEVENT,
	L_VTODO,
        L_VALARM,
	L_VALUES,
	L_BASE64,
	L_QUOTED_PRINTABLE
	};

/****  Private Forward Declarations  ****/
static int pushVObjectO(const char *prop);
static VObjectO* popVObjectO(void);
#ifndef _SUPPORT_LINE_FOLDING
static char* lexDataFromBase64O(void);
#endif
static void lexPopModeO(int top);
static int lexWithinModeO(enum LexModeO mode);
static void lexPushModeO(enum LexModeO mode);
static void enterPropsO(const char *s);
static void enterAttrO(const char *s1, const char *s2);
#if 0
static void enterValuesO(const char *value);
#endif
static void mimeO_error_(char *s);
 static void appendValueO(const char *value);

static int
ascii_tolower(const char i) {
	if ('A' <= i && i <= 'Z')
		return i - ('A' - 'a');
	return i;
}

static int
ascii_stricmp(const char* s1, const char* s2) {
	const char *p = s1, *q = s2;
	while (*p || *q) {
		char c1 = ascii_tolower(*p++);
		char c2 = ascii_tolower(*q++);
		if (c1 < c2)
			return -1;
		if (c1 > c2)
			return 1;
	}
	return 0;
}

%}

/***************************************************************************/
/***                           The grammar                              ****/
/***************************************************************************/

%union {
    char *str;
    VObjectO *vobj;
    }

%token
	EQ COLON DOT SEMICOLON SPACE HTAB LINESEP NEWLINE
	BEGIN_VCARD END_VCARD BEGIN_VCAL END_VCAL
	BEGIN_VEVENT END_VEVENT BEGIN_VTODO END_VTODO
	BEGIN_VALARM END_VALARM
	ID

/*
 * NEWLINE is the token that would occur outside a vCard,
 * while LINESEP is the token that would occur inside a vCard.
 */

%token <str>
	STRING ID

%type <str> name value

%type <vobj> vcard vcal vobject

%start mime

%%


mime: vobjects
	;

vobjects: vobject
	{ addListO(&vObjListO, $1); curObjO = 0; }
	vobjects
	| vobject
		{ addListO(&vObjListO, $1); curObjO = 0; }
	;

vobject: vcard
	| vcal
	;

vcard:
	BEGIN_VCARD
	{
	lexPushModeO(L_VCARD);
	if (!pushVObjectO(VCCardPropO)) YYERROR;
	}
	items END_VCARD
	{
	lexPopModeO(0);
	$$ = popVObjectO();
	}
	| BEGIN_VCARD
	{
	lexPushModeO(L_VCARD);
	if (!pushVObjectO(VCCardPropO)) YYERROR;
	}
	END_VCARD
	{
	lexPopModeO(0);
	$$ = popVObjectO();
	}
	;

items: item items
	| item
	;

item: prop COLON
	{
	lexPushModeO(L_VALUES);
	}
	values LINESEP
	{
	if (lexWithinModeO(L_BASE64) || lexWithinModeO(L_QUOTED_PRINTABLE))
	   lexPopModeO(0);
	lexPopModeO(0);
	}
	| error
	;

prop: name
	{
	enterPropsO($1);
	}
	attr_params
	| name
	{
	enterPropsO($1);
	}
	;

attr_params: attr_param attr_params
	| attr_param
	;

attr_param: SEMICOLON attr
	;

attr: name
	{
	enterAttrO($1,0);
	}
	| name EQ name
	{
	enterAttrO($1,$3);

	}
	;

name: ID
	;

values: value SEMICOLON { appendValueO($1); } values
	| value
	{ appendValueO($1); }
	;

value: STRING
	| { $$ = 0; }
	;

vcal:
	BEGIN_VCAL
	{ if (!pushVObjectO(VCCalPropO)) YYERROR; }
	calitems
	END_VCAL
	{ $$ = popVObjectO(); }
	| BEGIN_VCAL
	{ if (!pushVObjectO(VCCalPropO)) YYERROR; }
	END_VCAL
	{ $$ = popVObjectO(); }
	;

calitems: calitem calitems
	| calitem
	;

calitem:
	eventitem
	| todoitem
	| items
	;
        
eventitems:
	items
	| items alarmitem
	;        

eventitem:
	BEGIN_VEVENT
	{
	lexPushModeO(L_VEVENT);
	if (!pushVObjectO(VCEventPropO)) YYERROR;
	}
	eventitems
	END_VEVENT
	{
	lexPopModeO(0);
	popVObjectO();
	}
	| BEGIN_VEVENT
	{
	lexPushModeO(L_VEVENT);
	if (!pushVObjectO(VCEventPropO)) YYERROR;
	}
	END_VEVENT
	{
	lexPopModeO(0);
	popVObjectO();
	}
	;

todoitem:
	BEGIN_VTODO
	{
	lexPushModeO(L_VTODO);
	if (!pushVObjectO(VCTodoPropO)) YYERROR;
	}
	items
	END_VTODO
	{
	lexPopModeO(0);
	popVObjectO();
	}
	| BEGIN_VTODO
	{
	lexPushModeO(L_VTODO);
	if (!pushVObjectO(VCTodoPropO)) YYERROR;
	}
	END_VTODO
	{
	lexPopModeO(0);
	popVObjectO();
	}
	;
        
alarmitem:
	BEGIN_VALARM
	{
	lexPushModeO(L_VALARM);
	if (!pushVObjectO(VCAlarmPropO)) YYERROR;
	}
	items
	END_VALARM
	{
	lexPopModeO(0);
	popVObjectO();
	}
	| BEGIN_VALARM
	{
	lexPushModeO(L_VALARM);
	if (!pushVObjectO(VCAlarmPropO)) YYERROR;
	}
	END_VALARM
	{
	lexPopModeO(0);
	popVObjectO();
	}
	;

%%
/* ///////////////////////////////////////////////////////////////////////// */
static int pushVObjectO(const char *prop)
    {
    VObjectO *newObj;
    if (ObjStackTopO == MAXLEVEL)
	return FALSE;

    ObjStackO[++ObjStackTopO] = curObjO;

    if (curObjO) {
        newObj = addPropO(curObjO,prop);
        curObjO = newObj;
	}
    else
	curObjO = newVObjectO(prop);

    return TRUE;
    }


/* ///////////////////////////////////////////////////////////////////////// */
/* This pops the recently built vCard off the stack and returns it. */
static VObjectO* popVObjectO()
    {
    VObjectO *oldObj;
    if (ObjStackTopO < 0) {
	yyerror("pop on empty Object Stack\n");
	return 0;
	}
    oldObj = curObjO;
    curObjO = ObjStackO[ObjStackTopO--];

    return oldObj;
    }

static void appendValueO(const char *value)
{
  char *p1, *p2;
  wchar_t *p3;
  int i;

  if (fieldedPropO && *fieldedPropO) {
    if (value) {
      addPropValueO(curPropO, *fieldedPropO, value);
    }
    /* else this field is empty, advance to next field */
    fieldedPropO++;
  } else {
    if (value) {
      if (vObjectUStringZValueO(curPropO)) {
	p1 = fakeCStringO(vObjectUStringZValueO(curPropO));
	p2 = malloc(sizeof(char *) * (strlen(p1)+strlen(value)+1));
	strcpy(p2, p1);
	deleteStrO(p1);

	i = strlen(p2);
	p2[i] = ';';
	p2[i+1] = '\0';
	p2 = strcat(p2, value);
	p3 = (wchar_t *) vObjectUStringZValueO(curPropO);
	free(p3);
	setVObjectUStringZValue_O(curPropO,fakeUnicodeO(p2,0));
	deleteStrO(p2);
      } else {
	setVObjectUStringZValue_O(curPropO,fakeUnicodeO(value,0));
      }
    }
    else {
      setVObjectNullValueO(curPropO);
    }
  }
  deleteStrO(value);
}


static void enterPropsO(const char *s)
    {
    curPropO = addGroupO(curObjO,s);
    deleteStrO(s);
    }

static void enterAttrO(const char *s1, const char *s2)
    {
    const char *p1, *p2 = NULL;
    p1 = lookupProp_O(s1);
    if (s2) {
	VObjectO *a;
	p2 = lookupProp_O(s2);
	a = addPropO(curPropO,p1);
	setVObjectStringZValueO(a,p2);
	}
    else
	addPropO(curPropO,p1);
    if (ascii_stricmp(p1,VCBase64PropO) == 0 || (s2 && ascii_stricmp(p2,VCBase64PropO)==0))
	lexPushModeO(L_BASE64);
    else if (ascii_stricmp(p1,VCQuotedPrintablePropO) == 0
	    || (s2 && ascii_stricmp(p2,VCQuotedPrintablePropO)==0))
	lexPushModeO(L_QUOTED_PRINTABLE);
    deleteStrO(s1); deleteStrO(s2);
    }


/* #define MAX_LEX_LOOKAHEAD_0 64 */
#define MAX_LEX_LOOKAHEAD 256
#define MAX_LEX_MODE_STACK_SIZE 10
#define LEXMODE() (lexBufO.lexModeStack[lexBufO.lexModeStackTop])

struct LexBufO {
	/* input */
#ifdef INCLUDEMFC
    CFile *inputFile;
#else
    FILE *inputFile;
#endif
    char *inputString;
    unsigned long curPos;
    unsigned long inputLen;
	/* lookahead buffer */
	/*   -- lookahead buffer is int instead of char so that EOF
	 /      can be represented correctly.
	*/
    unsigned long len;
    int buf[MAX_LEX_LOOKAHEAD];
    unsigned long getPtr;
	/* context stack */
    unsigned long lexModeStackTop;
    enum LexModeO lexModeStack[MAX_LEX_MODE_STACK_SIZE];
	/* token buffer */
    unsigned long maxToken;
    char *strs;
    unsigned long strsLen;
    } lexBufO;

static void lexPushModeO(enum LexModeO mode)
    {
    if (lexBufO.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1))
	yyerror("lexical context stack overflow");
    else {
	lexBufO.lexModeStack[++lexBufO.lexModeStackTop] = mode;
	}
    }

static void lexPopModeO(int top)
    {
    /* special case of pop for ease of error recovery -- this
	version will never underflow */
    if (top)
	lexBufO.lexModeStackTop = 0;
    else
	if (lexBufO.lexModeStackTop > 0) lexBufO.lexModeStackTop--;
    }

static int lexWithinModeO(enum LexModeO mode) {
    unsigned long i;
    for (i=0;i<lexBufO.lexModeStackTop;i++)
	if (mode == lexBufO.lexModeStack[i]) return 1;
    return 0;
    }

static int lexGetc_O()
    {
    /* get next char from input, no buffering. */
    if (lexBufO.curPos == lexBufO.inputLen)
	return EOF;
    else if (lexBufO.inputString)
	return *(lexBufO.inputString + lexBufO.curPos++);
    else {
#ifdef INCLUDEMFC
	char result;
	return lexBufO.inputFile->Read(&result, 1) == 1 ? result : EOF;
#else
	return fgetc(lexBufO.inputFile);
#endif
	}
    }

static int lexGetaO()
    {
    ++lexBufO.len;
    return (lexBufO.buf[lexBufO.getPtr] = lexGetc_O());
    }

static int lexGeta_O(int i)
    {
    ++lexBufO.len;
    return (lexBufO.buf[(lexBufO.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_O());
    }

static void lexSkipLookaheadO() {
    if (lexBufO.len > 0 && lexBufO.buf[lexBufO.getPtr]!=EOF) {
	/* don't skip EOF. */
        lexBufO.getPtr = (lexBufO.getPtr + 1) % MAX_LEX_LOOKAHEAD;
	lexBufO.len--;
        }
    }

static int lexLookaheadO() {
    int c = (lexBufO.len)?
	lexBufO.buf[lexBufO.getPtr]:
	lexGetaO();
    /* do the \r\n -> \n or \r -> \n translation here */
    if (c == '\r') {
	int a = (lexBufO.len>1)?
	    lexBufO.buf[(lexBufO.getPtr+1)%MAX_LEX_LOOKAHEAD]:
	    lexGeta_O(1);
	if (a == '\n') {
	    lexSkipLookaheadO();
	    }
	lexBufO.buf[lexBufO.getPtr] = c = '\n';
	}
    else if (c == '\n') {
	int a = (lexBufO.len>1)?
	    lexBufO.buf[lexBufO.getPtr+1]:
	    lexGeta_O(1);
	if (a == '\r') {
	    lexSkipLookaheadO();
	    }
	lexBufO.buf[lexBufO.getPtr] = '\n';
	}
    return c;
    }

static int lexGetcO() {
    int c = lexLookaheadO();
    if (lexBufO.len > 0 && lexBufO.buf[lexBufO.getPtr]!=EOF) {
	/* EOF will remain in lookahead buffer */
        lexBufO.getPtr = (lexBufO.getPtr + 1) % MAX_LEX_LOOKAHEAD;
	lexBufO.len--;
        }
    return c;
    }

static void lexSkipLookaheadWordO() {
    if (lexBufO.strsLen <= lexBufO.len) {
	lexBufO.len -= lexBufO.strsLen;
	lexBufO.getPtr = (lexBufO.getPtr + lexBufO.strsLen) % MAX_LEX_LOOKAHEAD;
	}
    }

static void lexClearTokenO()
    {
    lexBufO.strsLen = 0;
    }

static void lexAppendcO(int c)
    {
    lexBufO.strs[lexBufO.strsLen] = c;
    /* append up to zero termination */
    if (c == 0) return;
    lexBufO.strsLen++;
    if (lexBufO.strsLen >= lexBufO.maxToken) {
	/* double the token string size */
	lexBufO.maxToken <<= 1;
	lexBufO.strs = (char*) realloc(lexBufO.strs,(size_t)lexBufO.maxToken);
	}
    }

static char* lexStrO() {
    return dupStrO(lexBufO.strs,(size_t)lexBufO.strsLen+1);
    }

static void lexSkipWhiteO() {
    int c = lexLookaheadO();
    while (c == ' ' || c == '\t') {
	lexSkipLookaheadO();
	c = lexLookaheadO();
	}
    }

static char* lexGetWordO() {
    int c;
    lexSkipWhiteO();
    lexClearTokenO();
    c = lexLookaheadO();
    while (c != EOF && !strchr("\t\n ;:=",c)) {
	lexAppendcO(c);
	lexSkipLookaheadO();
	c = lexLookaheadO();
	}
    lexAppendcO(0);
    return lexStrO();
    }


static void lexPushLookaheadcO(int c) {
    int putptr;
    /* can't putback EOF, because it never leaves lookahead buffer */
    if (c == EOF) return;
    putptr = (int)lexBufO.getPtr - 1;
    if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD;
    lexBufO.getPtr = putptr;
    lexBufO.buf[putptr] = c;
    lexBufO.len += 1;
    }

static char* lexLookaheadWordO() {
    /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD
     /  and thing bigger than that will stop the lookahead and return 0;
     / leading white spaces are not recoverable.
     */
    int c;
    int len = 0;
    int curgetptr = 0;
    lexSkipWhiteO();
    lexClearTokenO();
    curgetptr = (int)lexBufO.getPtr;	/* remember! */
    while (len < (MAX_LEX_LOOKAHEAD)) {
	c = lexGetcO();
	len++;
	if (c == EOF || strchr("\t\n ;:=", c)) {
	    lexAppendcO(0);
	    /* restore lookahead buf. */
	    lexBufO.len += len;
	    lexBufO.getPtr = curgetptr;
	    return lexStrO();
	    }
        else
	    lexAppendcO(c);
	}
    lexBufO.len += len;	/* char that has been moved to lookahead buffer */
    lexBufO.getPtr = curgetptr;
    return 0;
    }
    
static char* lexLookaheadUntilO(const char* lookfor)
{
  /* this function can lookahead until lookfor with max size of MAX_LEX_LOOKAHEAD
   * anything bigger than that will stop the lookahead and return 0;
   * leading white spaces are not recoverable.
   */
  int c;
  int len = 0;
  int curgetptr = 0;
  lexSkipWhiteO();
  lexClearTokenO();
  curgetptr = (int)lexBufO.getPtr;	/* remember! */
  while (len < (MAX_LEX_LOOKAHEAD)) 
  {
    c = lexGetcO();
    len++;
    if (c == EOF || strchr(lookfor, c)) 
    {
      lexAppendcO(0);
      /* restore lookahead buf. */
      lexBufO.len += len;
      lexBufO.getPtr = curgetptr;
      return lexStrO();
    }
    else
    {
      lexAppendcO(c);
    }
  }
  lexBufO.len += len;	/* char that has been moved to lookahead buffer */
  lexBufO.getPtr = curgetptr;
  return 0;
  
}    

#ifdef _SUPPORT_LINE_FOLDING
static void handleMoreRFC822LineBreakO(int c) {
    /* suport RFC 822 line break in cases like
     *	ADR: foo;
     *    morefoo;
     *    more foo;
     */
    if (c == ';') {
	int a;
	lexSkipLookaheadO();
	/* skip white spaces */
	a = lexLookaheadO();
	while (a == ' ' || a == '\t') {
	    lexSkipLookaheadO();
	    a = lexLookaheadO();
	    }
	if (a == '\n') {
	    lexSkipLookaheadO();
	    a = lexLookaheadO();
	    if (a == ' ' || a == '\t') {
		/* continuation, throw away all the \n and spaces read so
		 * far
		 */
		lexSkipWhiteO();
		lexPushLookaheadcO(';');
		}
	    else {
		lexPushLookaheadcO('\n');
		lexPushLookaheadcO(';');
		}
	    }
	else {
	    lexPushLookaheadcO(';');
	    }
	}
    }

static char* lexGet1ValueO() {
    int c;
    lexSkipWhiteO();
    c = lexLookaheadO();
    lexClearTokenO();
    while (c != EOF && c != ';') {
	if (c == '\n') {
	    int a;
	    lexSkipLookaheadO();
	    a  = lexLookaheadO();
	    if (a == ' ' || a == '\t') {
		lexAppendcO(' ');
		lexSkipLookaheadO();
		}
	    else {
		lexPushLookaheadcO('\n');
		break;
		}
	    }
	else {
	    lexAppendcO(c);
	    lexSkipLookaheadO();
	    }
	c = lexLookaheadO();
	}
    lexAppendcO(0);
    handleMoreRFC822LineBreakO(c);
    return c==EOF?0:lexStrO();
    }
#endif

#ifndef _SUPPORT_LINE_FOLDING
static char* lexGetStrUntilO(char *termset) {
    int c = lexLookaheadO();
    lexClearToken();
    while (c != EOF && !strchr(termset,c)) {
	lexAppendcO(c);
	lexSkipLookaheadO();
	c = lexLookaheadO();
	}
    lexAppendcO(0);
    return c==EOF?0:lexStr();
    }
#endif

static int match_begin_nameO(int end) {
    char *n = lexLookaheadWordO();
    int token = ID;
    
    if (n) {
	if (!ascii_stricmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD;
	else if (!ascii_stricmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL;
	else if (!ascii_stricmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT;
	else if (!ascii_stricmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO;
	else if (!ascii_stricmp(n,"valarm")) token = end?END_VALARM:BEGIN_VALARM;
	deleteStrO(n);
	return token;
	}
    return 0;
    }


#ifdef INCLUDEMFC
static void initLexO(const char *inputstring, unsigned long inputlen, CFile *inputfile)
#else
static void initLexO(const char *inputstring, unsigned long inputlen, FILE *inputfile)
#endif
    {
    /* initialize lex mode stack */
    lexBufO.lexModeStack[lexBufO.lexModeStackTop=0] = L_NORMAL;

    /* iniatialize lex buffer. */
    lexBufO.inputString = (char*) inputstring;
    lexBufO.inputLen = inputlen;
    lexBufO.curPos = 0;
    lexBufO.inputFile = inputfile;

    lexBufO.len = 0;
    lexBufO.getPtr = 0;

    lexBufO.maxToken = MAXTOKEN;
    lexBufO.strs = (char*)malloc(MAXTOKEN);
    lexBufO.strsLen = 0;

    }

static void finiLexO() {
    free(lexBufO.strs);
    }


/* ///////////////////////////////////////////////////////////////////////// */
/* This parses and converts the base64 format for binary encoding into
 * a decoded buffer (allocated with new).  See RFC 1521.
 */
static char * lexGetDataFromBase64O()
    {
    unsigned long bytesLen = 0, bytesMax = 0;
    int quadIx = 0, pad = 0;
    unsigned long trip = 0;
    unsigned char b;
    int c;
    unsigned char *bytes = NULL;
    unsigned char *oldBytes = NULL;

    DBG_(("db: lexGetDataFromBase64\n"));
    while (1) {
	c = lexGetcO();
	if (c == '\n') {
	    ++mimeO_lineNum;
	    if (lexLookaheadO() == '\n') {
		/* a '\n' character by itself means end of data */
		break;
		}
	    else continue; /* ignore '\n' */
	    }
	else {
	    if ((c >= 'A') && (c <= 'Z'))
		b = (unsigned char)(c - 'A');
	    else if ((c >= 'a') && (c <= 'z'))
		b = (unsigned char)(c - 'a') + 26;
	    else if ((c >= '0') && (c <= '9'))
		b = (unsigned char)(c - '0') + 52;
	    else if (c == '+')
		b = 62;
	    else if (c == '/')
		b = 63;
	    else if (c == '=') {
		b = 0;
		pad++;
	    } else if ((c == ' ') || (c == '\t')) {
		continue;
	    } else { /* error condition */
		if (bytes) free(bytes);
		else if (oldBytes) free(oldBytes);
		/* error recovery: skip until 2 adjacent newlines. */
		DBG_(("db: invalid character 0x%x '%c'\n", c,c));
		if (c != EOF)  {
		    c = lexGetcO();
		    while (c != EOF) {
			if (c == '\n' && lexLookaheadO() == '\n') {
			    ++mimeO_lineNum;
			    break;
			    }
			c = lexGetcO();
			}
		    }
		return NULL;
		}
	    trip = (trip << 6) | b;
	    if (++quadIx == 4) {
		unsigned char outBytes[3];
		int numOut;
		int i;
		for (i = 0; i < 3; i++) {
		    outBytes[2-i] = (unsigned char)(trip & 0xFF);
		    trip >>= 8;
		    }
		numOut = 3 - pad;
		if (bytesLen + numOut > bytesMax) {
		    if (!bytes) {
			bytesMax = 1024;
			bytes = (unsigned char*)malloc((size_t)bytesMax);
			}
		    else {
			bytesMax <<= 2;
			oldBytes = bytes;
			bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax);
			}
		    if (bytes == 0) {
			mimeO_error("out of memory while processing BASE64 data\n");
			}
		    }
		if (bytes) {
		    memcpy(bytes + bytesLen, outBytes, numOut);
		    bytesLen += numOut;
		    }
		trip = 0;
		quadIx = 0;
		}
	    }
	} /* while */
    DBG_(("db: bytesLen = %d\n",  bytesLen));
    /* kludge: all this won't be necessary if we have tree form
	representation */
    if (bytes) {
	setValueWithSizeO(curPropO,bytes,(unsigned int)bytesLen);
	free(bytes);
	}
    else if (oldBytes) {
	setValueWithSizeO(curPropO,oldBytes,(unsigned int)bytesLen);
	free(oldBytes);
	}
    return 0;
    }

static int match_begin_end_nameO(int end) {
    int token;
    lexSkipWhiteO();
    if (lexLookaheadO() != ':') return ID;
    lexSkipLookaheadO();
    lexSkipWhiteO();
    token = match_begin_nameO(end);
    if (token == ID) {
	lexPushLookaheadcO(':');
	DBG_(("db: ID '%s'\n", yylval.str));
	return ID;
	}
    else if (token != 0) {
	lexSkipLookaheadWordO();
	deleteStrO(yylval.str);
	DBG_(("db: begin/end %d\n", token));
	return token;
	}
    return 0;
    }

static char* lexGetQuotedPrintableO()
    {
    int cur;

    lexClearTokenO();
    do {
	cur = lexGetcO();
	switch (cur) {
	    case '=': {
		int c = 0;
		int next[2];
		int i;
		for (i = 0; i < 2; i++) {
		    next[i] = lexGetcO();
		    if (next[i] >= '0' && next[i] <= '9')
			c = c * 16 + next[i] - '0';
		    else if (next[i] >= 'A' && next[i] <= 'F')
			c = c * 16 + next[i] - 'A' + 10;
		    else
			break;
		    }
		if (i == 0) {
		    /* single '=' follow by LINESEP is continuation sign? */
		    if (next[0] == '\n') {
			++mimeO_lineNum;
			}
		    else {
			lexPushLookaheadcO('=');
			goto EndString;
			}
		    }
		else if (i == 1) {
		    lexPushLookaheadcO(next[1]);
		    lexPushLookaheadcO(next[0]);
		    lexAppendcO('=');
		} else {
		    lexAppendcO(c);
		    }
		break;
		} /* '=' */
	    case '\n':
	    case ';':
	      {
		lexPushLookaheadcO(cur);
		goto EndString;
		}
	    case EOF:
		break;
	    default:
		lexAppendcO(cur);
		break;
	    } /* switch */
	} while (cur != EOF);

EndString:
    lexAppendcO(0);
    return lexStrO();
    } /* LexQuotedPrintable */

static int yylex() {
    int lexmode = LEXMODE();
    if (lexmode == L_VALUES) {
	int c = lexGetcO();
	if (c == ';') {
	    DBG_(("db: SEMICOLON\n"));
	    lexPushLookaheadcO(c);
#ifdef _SUPPORT_LINE_FOLDING
	    handleMoreRFC822LineBreakO(c);
#endif
	    lexSkipLookaheadO();
	    return SEMICOLON;
	    }
	else if (strchr("\n",c)) {
	    ++mimeO_lineNum;
	    /* consume all line separator(s) adjacent to each other */
	    c = lexLookaheadO();
	    while (strchr("\n",c)) {
		lexSkipLookaheadO();
		c = lexLookaheadO();
		++mimeO_lineNum;
		}
	    DBG_(("db: LINESEP\n"));
	    return LINESEP;
	    }
	else {
	    char *p = 0;
	    lexPushLookaheadcO(c);
	    if (lexWithinModeO(L_BASE64)) {
		/* get each char and convert to bin on the fly... */
		p = lexGetDataFromBase64O();
		yylval.str = p;
		return STRING;
		}
	    else if (lexWithinModeO(L_QUOTED_PRINTABLE)) {
		p = lexGetQuotedPrintableO();
		}
	    else {
#ifdef _SUPPORT_LINE_FOLDING
		p = lexGet1ValueO();
#else
		p = lexGetStrUntilO(";\n");
#endif
		}
	    if (p) {
		DBG_(("db: STRING: '%s'\n", p));
		yylval.str = p;
		return STRING;
		}
	    else return 0;
	    }
	}
    else {
	/* normal mode */
	while (1) {
	    int c = lexGetcO();
	    switch(c) {
		case ':': {
                    /* consume a line separator after the colon if the next line
                     * does not contain a colon - i.e. a line wrap has occurred
                     */    
                    c = lexLookaheadO();
                    if(strchr("\n", c))
                    {
                      char *t = lexLookaheadUntilO(":");
                      if(t)
                      {
                        /* skip over the immediate newline */
                        ++t;
                        if(strstr(t, "\n"))
                        {
                          /* newline before the next colon - it was a line wrap */
                          /* consume the newline */
                          lexSkipLookaheadO();
                          c = lexLookaheadO();
                          ++mimeO_lineNum;
                        }
                      }
                    }    
		    DBG_(("db: COLON\n"));
		    return COLON;
		    }
		case ';':
		    DBG_(("db: SEMICOLON\n"));
		    return SEMICOLON;
		case '=':
		    DBG_(("db: EQ\n"));
		    return EQ;
		/* ignore whitespace in this mode */
		case '\t':
		case ' ': continue;
		case '\n': {
		    ++mimeO_lineNum;
		    continue;
		    }
		case EOF: return 0;
		    break;
		default: {
		    lexPushLookaheadcO(c);
		    if (isprint(c)) {
			char *t = lexGetWordO();
			yylval.str = t;
			if (!ascii_stricmp(t, "begin")) {
			    return match_begin_end_nameO(0);
			    }
			else if (!ascii_stricmp(t,"end")) {
			    return match_begin_end_nameO(1);
			    }
		        else {
			    DBG_(("db: ID '%s'\n", t));
			    return ID;
			    }
			}
		    else {
			/* unknow token */
			return 0;
			}
		    break;
		    }
		}
	    }
	}
    return 0;
    }


/***************************************************************************/
/***							Public Functions						****/
/***************************************************************************/

static VObjectO* Parse_MIMEHelperO()
    {
    ObjStackTopO = -1;
    mimeO_numErrors = 0;
    mimeO_lineNum = 1;
    vObjListO = 0;
    curObjO = 0;

    if (yyparse() != 0) {
	finiLexO();
	return 0;
    }

    finiLexO();
    return vObjListO;
    }

/* ///////////////////////////////////////////////////////////////////////// */
DLLEXPORT(VObjectO*) Parse_MIMEO(const char *input, unsigned long len)
    {
    initLexO(input, len, 0);
    return Parse_MIMEHelperO();
    }


#if INCLUDEMFC

DLLEXPORT(VObjectO*) Parse_mimeO_FromFileO(CFile *file)
    {
    unsigned long startPos;
    VObjectO *result;

    initLexO(0,-1,file);
    startPos = file->GetPosition();
    if (!(result = Parse_MIMEHelperO()))
	file->Seek(startPos, CFile::begin);
    return result;
    }

#else

VObjectO* Parse_MIME_FromFileO(FILE *file)
    {
    VObjectO *result;
    long startPos;

    initLexO(0,(unsigned long)-1,file);
    startPos = ftell(file);
    if (!(result = Parse_MIMEHelperO())) {
	fseek(file,startPos,SEEK_SET);
	}
    return result;
    }

DLLEXPORT(VObjectO*) Parse_MIME_FromFileNameO(char *fname)
    {
    FILE *fp = fopen(fname,"r");
    if (fp) {
	VObjectO* o = Parse_MIME_FromFileO(fp);
	fclose(fp);
	return o;
	}
    else {
	char msg[256];
	snprintf(msg, sizeof(msg), "can't open file '%s' for reading\n", fname);
	mimeO_error_(msg);
	return 0;
	}
    }

#endif

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

static MimeErrorHandlerO mimeErrorHandlerO;

DLLEXPORT(void) registerMimeErrorHandlerO(MimeErrorHandlerO me)
    {
    mimeErrorHandlerO = me;
    }

static void mimeO_error(char *s)
    {
    char msg[256];
    if (mimeErrorHandlerO) {
	sprintf(msg,"%s at line %d", s, mimeO_lineNum);
	mimeErrorHandlerO(msg);
	}
    }

static void mimeO_error_(char *s)
    {
    if (mimeErrorHandlerO) {
	mimeErrorHandlerO(s);
	}
    }


