#include	<stdio.h>
#include	"rtf.h"
#include	"rtftohtml.h"
#include	"string.h"

#define MAXNEST 8

/* Attributes of output stream */


/* Destinations can be files or strings.  */


struct SDest

 HName = {
    NULL, 0, 0
},
HHref = {
    NULL, 0, 0
},
Title = {
    NULL, 0, 0
},
BkName = {
    NULL, 0, 0
},
Item = {
    NULL, 0, 0
};

char * (badstyles[256]);	
char contextb[256];
int cend,wrap;

struct FDest {
    char filename[256];		/* Name of output file */
    char wbuff[82];		/* Output buffer */
    FILE *outfd;		/* Output file - (file destinations only) */
    int wrap;		    /* should we wrap text or not */
    int HPTags;			/* Highest Paragraph Tag */
    int HTTags;			/* Highest Text Tag */
    int NeedPar;		/* Output Paragraph before next text */
    int InCol2;			/* We are past Column 1 */
    int PTidx;			/* Paragraph Tag Index */
    int TSpecial;		/* Special Tag */
    int cpos;			/* character position */
    int TStack[MAXNEST];	/* Stack of tags */
}
 EmptyFD = {
    "", "",NULL,1, -1, -1, 0, 0,0, 0
},
 File1 = {
    "","", NULL,1, -1, -1, 0, 0,0, 0
},
Footnote = {
    "","", NULL,1, -1, -1, 0, 0,0, 0
};

#define ConvertCaps	((TStyle&(styleAllCaps|styleSmallCaps))!=0)
int outfuniq = 0;
extern char PFileExt[16];	/* extension to be used for links to pictures */
extern char FPrefix[256];	/* Base name of output files */
extern char FPrefixR[256];	/* Relative name of output files */
extern int linkself;

char LineBuffer[512];

extern char OutfileName[256];	/* output file name */
extern char *InfileName;	/* input file name */

extern int IStyle_Chg;		/* Has input style changed? */

/* Translator Options */
extern int debug;
extern int IMG;

/* Input State Variables */
extern char *ParStyle;		/* Paragraph Style Name */
extern TStyle_typ TStyle;	/* Bitmask of Text Styles */
extern char *TFont;		/* Points to Font String */
extern int TSize;		/* Pointsize of Font */
extern int inTable;			/* In a table? */
extern int cellno;
extern int firstcell;
extern int lastcell;
extern int destination;		/* The current input destination */
extern RTFFont *fp;		/* Actual Font Pointer */

extern InStateStack *ISS;


extern char *outMap[rtfSC_MaxChar];

FILE *PictFile;
int uniqnum = 0;

#define MAXOBUF	1024
int
nextfield(cp, w,  ob,  adj,  EntIsOne)
char *cp;int w; char *ob; int adj; int EntIsOne;
{
	int i,j,br=-1,lp,rp,nb=-1,ow=w;
	for(i=0;i<w&&cp[i]!='\0';i++){
		if(cp[i]==' '||cp[i]=='\t'){
			if(nb==i-1)br=i;
		}	else {
			if(EntIsOne&&cp[i]=='&'){
				while(cp[i]!=';'){
					w++;
					i++;
				}
			}
			nb=i;
		}
	}
	if(i==0){
		ob[0]='\0';
		return(0);
	}
	if(cp[i]=='\0'&&nb==i-1)br=i;
	if(br==-1)br=w;
	if(w!=ow){ /* now subtract off any entities not used */
		for(i=br,j=0;i<w&&cp[i]!='\0';i++){
			if(cp[i]=='&'){
				while(cp[i]!=';'){
					j++;
					i++;
				}
			}
		}
		w-=j;
	}
	switch(adj){
	case AdjL:			/* left justify - no pad */
		lp=0;
		rp=br;
		break;
	case AdjLPad:			/* left justify */
		lp=0;
		rp=w;
		break;
	case AdjRPad:			/* right justify */
		lp=w-br;
		rp=w;
		break;
	case AdjCpad:			/* center */
		lp=(w-br)/2;
		rp=w;
		break;
	}
	if(rp>=MAXOBUF){
		fprintf(stderr,"Overflowed MAXOBUF too many Entities in a table cell\n");
		exit(1);
	}
	for(i=0;i<lp;i++)ob[i]=' ';
	for(j=0;j<br;i++,j++)ob[i]=cp[j];
	for(;i<rp;i++)ob[i]=' ';
	ob[i]='\0';
	return(br);
}

void
putwrap( s,  OD, wrap)
char *s;struct FDest *OD;int wrap;
{
	int i;
	int n;
	char obuff[133];
	/* If writing to a table, just save up the text */
	if(!wrap){
		if(OD->cpos>0){
			OD->wbuff[OD->cpos]='\0';
			fputs(OD->wbuff,OD->outfd);
			fputc('\n',OD->outfd);
		}
		fputs(s,OD->outfd);
		OD->cpos=-1;
		return;
	}
	i=OD->cpos;
	while( *s != '\0'){
		if(i==81){
			n=nextfield(OD->wbuff, 80,  obuff, AdjL,0);
			fputs(obuff,OD->outfd);
			if(n<80){	/* only if we have a break */
				fputc('\n',OD->outfd);
			    for(;n<81;n++)
					if(OD->wbuff[n]!=' '&&OD->wbuff[n]!='\t')
						break;
			    for(i=0;n<81;i++,n++)
			    	OD->wbuff[i]=OD->wbuff[n];
			} else  { /* force break at next opportunity */
				i=-1;
			}
		}
		if(i<0){
			if(strchr(" \t\n",*s)!=NULL){ /*found break */
				fputc('\n',OD->outfd);
				i=0;
				s++;
			} else{
				fputc(*(s++),OD->outfd);
			}
		} else if(*s=='\n'){
			OD->wbuff[i]='\0';
			fputs(OD->wbuff,OD->outfd);
			fputc(*(s++),OD->outfd);
			i=0;
			continue;
		} else
			OD->wbuff[i++]=*(s++);
	}
	OD->cpos=i;
}

HTMLInit()
{
	int i;
	for(i=0;i<256;i++)
		badstyles[i]=NULL;
	HName.used=0;
	HHref.used=0;
	Title.used=0;
	Item.used=0;
	File1=EmptyFD;
	Footnote=EmptyFD;
	outfuniq=0;
	uniqnum=0;
	wrap=0;
	cend=0;
	
    strcpy(File1.filename, OutfileName);
    if (OutfileName[0] != '\0') {
	File1.outfd = fopen(OutfileName, "w");
	if (File1.outfd == NULL) {
	    fprintf(stderr, "Open of %s Failed\n", OutfileName);
	    exit(1);
	}
    } else {
	File1.outfd = stdout;
    }
    /* open a file for footnotes */
    Footnote.outfd = tmpfile();
    if (Footnote.outfd == NULL) {
	fprintf(stderr, "Open of %s Failed\n", "Temporary File");
	exit(1);
    }
    putwrap("<html><head>", &File1,File1.wrap);
    putwrap("<!-- This document was created from RTF source by rtftohtml version ", &File1,File1.wrap);
    putwrap(PVers, &File1,File1.wrap);
    putwrap(" -->", &File1,File1.wrap);
}
static void
TestStyle();

HTMLCleanup()
{
    char buff[256];
    int anyfoot = 0;		
    /* Fake some plain text so that test-style will undo all current markup. */
    TFont="--Not a Font--";
    TStyle=0;
    TSize=9999;
    ParStyle=PMatchArr[0].PStyle;
    TestStyle(&File1);
	putwrap("",&Footnote,0);
    rewind(Footnote.outfd);
    while (fgets(buff, 256, Footnote.outfd)) {
	if (anyfoot == 0) {
	    putwrap("<hr>", &File1,File1.wrap);
	    anyfoot = 1;
	}
	putwrap(buff,&File1,0);
    }
    putwrap("</body></html>", &File1,0);
    if (File1.outfd != stdout)
	fclose(Footnote.outfd);
	fclose(File1.outfd);
}

HTMLStartDivert(){
    struct FDest *OD;
	OD = &File1;
	switch (destination) {
    case rtfObjResult:
    	if(linkself&&Item.used>0&&!inTable){
    		sprintf(LineBuffer,"<a href=\"#%s\">",Item.ptr);
    		putwrap(LineBuffer,OD,0);
	    	Item.used=0;
	    }
		break;
	}
}

int BkNum=0;
HTMLEndDivert()
{
    struct FDest *OD;
	OD = &File1;
    if (ISS->destination != destination&&!inTable) {
		switch (destination) {
		case rtfPict:
		    fclose(PictFile);
		    RTFSetClassCallback(rtfText, PutHTML);
		    break;
		case rtfFootnote:
		    Footnote.NeedPar = 1;
		    Footnote.InCol2 = 0;
		    break;
		case rtfObjResult:
	    	if(linkself){putwrap("</a>",OD,OD->wrap);}
			break;
	    case rtfBookmarkStart:
	    	if(BkName.used>0){
	    		sprintf(LineBuffer,"<a name=\"%s\">",BkName.ptr);
	    		putwrap(LineBuffer,OD,0);
	    		BkName.used=0;
	    	}else {
	    		sprintf(LineBuffer,"<a name=\"RTFtohtmlxx%d\">",BkNum++);
	    		putwrap(LineBuffer,OD,OD->wrap);
	    	}
			break;
	    case rtfBookmarkEnd:
	    	putwrap("</a>",OD,OD->wrap);
			break;
	    }
	}
}
char twochar[3];
int charnum = 0;

/*
 * This function "cheats" by using rtfMajor (raw input character) rather
 * than rtfMinor (corresponding standard character code).  This is based on
 * the assumption than the function won't see anything other than
 * '0'..'9' and that the result of mapping the standard codes for such chars
 * would be the same chars.
 */

static void
PutHex()
{
    int xval;
    twochar[charnum] = rtfMajor;
    if (++charnum == 2) {
	sscanf(twochar, "%2x", &xval);
	fputc((char) xval, PictFile);
	charnum = 0;
    }
}

char zeros[512];
void

NabPicture(extn, ftype)
    char *extn;
    long ftype;
{
    struct FDest *OD;
    /*
     * First create the reference to the picture, and then change the class
     * callback so that the contents of the picture are written to a file. If
     * we are already gathering a link, or the -i option was specified,
     * change the format of the link to IMG.
     */
    char filename[256];
    char rfilename[256];
    char reftext[512];
    if(inTable){
    	RTFSkipGroup ();
		PopIState();
    }
    sprintf(filename, "%s%d.%s", FPrefix, ++outfuniq, extn);
    sprintf(rfilename, "%s%d.%s", FPrefixR, outfuniq, PFileExt);

    /* find the previous destination */
    if (ISS == NULL) {
	fprintf(stderr, "Error, State Stack is null\n");
	    context();
	exit(1);
    }
    switch (ISS->destination) {
	/* string destinations */
    case rtfFootnote:
	OD = &Footnote;
	break;
    default:
	OD = &File1;
	break;
    }
    if (IStyle_Chg)	TestStyle(OD);
    if (OD->TSpecial == MTDiscard){
		RTFSkipGroup ();
		PopIState();
    }
    if (IMG || OD->TSpecial == MTHot) {
    	    		sprintf(LineBuffer,"<IMG SRC=\"%s\">", rfilename);
    	    		putwrap(LineBuffer,OD,0);
    } else {
	sprintf(LineBuffer,"<A href=\"%s\">Click here for Picture </A>", rfilename);
	putwrap(LineBuffer,OD,0);
    }
#ifdef THINK_C
    _ftype = ftype;
#endif
    PictFile = fopen(filename, "wb");
    if (PictFile == NULL) {
	fprintf(stderr, "Open of %s Failed\n", filename);
	exit(1);
    }
    RTFSetClassCallback(rtfText, PutHex);
    if (ftype == FTPICT) {
	fwrite(zeros, 512, 1, PictFile);
    }
}



int InBody = 0;

static void
TestStyle(OD)
    struct FDest *OD;
{
/*
	Change the style of the output text to the new style.
	If the style changes, force correct nesting of HTML tags.
*/
    int PMidx, i, j;
    int OldPMidx;
    int OldTSpecial = OD->TSpecial;
    if(OD->HPTags<0)
    	OldPMidx = -1;
    else
    	OldPMidx = OD->TStack[OD->HPTags];
    /* Look for Special Text Style Matches - these override normal processing */
    IStyle_Chg = 0;
    OD->TSpecial = 0;
    /* Find a match on the new paragraph and text styles */
    for (PMidx = 0; PMidx < PMatchLen; PMidx++) {
		if (strcmp(PMatchArr[PMidx].PStyle, ParStyle) == 0) {
		    break;
		}
    }
    if (PMidx == PMatchLen){
		PMidx = 0;		/* The first entry is the default */
		if(strcmp(ParStyle,"")!=0){
			for(i=0;i<256&&badstyles[i]!=NULL;i++) /* Save off mismatches */
				if(strcmp(badstyles[i],ParStyle)==0)
					break;
			if(badstyles[i]==NULL&&i<256){
				badstyles[i]=ParStyle;
				fprintf(stderr,"Unknown Paragraph style: %s\n",ParStyle);
			}
		}
	}
	if(PMatchArr[PMidx].PTidx>MTSPECIAL&&
    	(TMatchArr[i].TTidx != MTHref || OD != &Footnote)){
    	OD->TSpecial=PMatchArr[PMidx].PTidx;
    } else {
	    for (i = 0; i < TMatchLen; i++) {
			if ((TMatchArr[i].TStyle == (TMatchArr[i].TMask & TStyle)) &&
			    (TMatchArr[i].Font[0] == '\0' || strcmp(TMatchArr[i].Font, TFont) == 0) &&
			    (TMatchArr[i].FSize == 0 || TMatchArr[i].FSize == TSize)) {
			    /* can't have hot text in footnote */
			    if (TMatchArr[i].TTidx == MTHref && OD == &Footnote)
					continue;
			    if (TMatchArr[i].TTidx > MTSPECIAL) {
					OD->TSpecial = TMatchArr[i].TTidx;
					break;
			    }
			}
	    }
    }
    if (inTable){ /* force specials off */
    	OD->TSpecial=0;
    	ParStyle="_Table";
    }
    if (OldTSpecial != OD->TSpecial) {
	    switch(OldTSpecial){
	    case MTName:
		    putwrap("\"> </a>", OD,0);
			OD->wrap=PTagArr[OD->PTidx].DoFold;
	    	break;
	    case MTHref:
		    putwrap("\">", OD,0);
		    if(OD->TSpecial!=MTHot){
		    	fprintf(stderr, "Error, at end of href, hot text not found\n");
	    context();
	  			  exit(1);
		    }
			OD->wrap=PTagArr[OD->PTidx].DoFold;
	    	break;
	    case MTHot:
			putwrap("</a>",OD,OD->wrap);
	    	break;
	    case MTFootNote:
			if (OD == &File1) {
				putwrap("]</a>", OD,OD->wrap);
			} else {
				putwrap("]</a>",OD,OD->wrap);
			}
	    	break;
	    }
    }
	if (OD->TSpecial != 0){
  		if (OldTSpecial != OD->TSpecial) {
		    switch(OD->TSpecial){
		    case MTName:
		    	if (OD->NeedPar) {
					putwrap(PTagArr[OD->PTidx].ParTag, OD,OD->wrap);
					OD->NeedPar = 0;
		   		}
				putwrap("<a name=\"", OD,0);
				OD->wrap=0;
		   		break;
		    case MTHref:
		    	if (OD->NeedPar) {
					putwrap(PTagArr[OD->PTidx].ParTag, OD,OD->wrap);
					OD->NeedPar = 0;
		   		}
				putwrap("<a href=\"", OD,0);
				OD->wrap=0;
		   		break;
		    case MTHot:
			    if(OldTSpecial!=MTHref){
			    	fprintf(stderr, "Error, hot text found without preceding href\n");
	   				context();
		  			exit(1);
			    }		    	
				break;
		    case MTFootNote:
				if (OD->NeedPar) {
					putwrap(PTagArr[OD->PTidx].ParTag, OD,OD->wrap);
					OD->NeedPar = 0;
				}
				if (OD == &File1) {
					sprintf(LineBuffer,"<a href=\"#fn%d\">[", uniqnum);
					putwrap(LineBuffer,OD,OD->wrap);
				} else {
					sprintf(LineBuffer,"<a name=\"fn%d\">[", uniqnum++);
					putwrap(LineBuffer,OD,OD->wrap);
				}
				break;
		    }
		}
		return;
	}

    OD->PTidx = PMatchArr[PMidx].PTidx;
    if (PMidx != OldPMidx) { /* Paragraph Style changed */
    	int pushpop=0;
		/* Pop all of the Text Styles */
		while (OD->HTTags > OD->HPTags) {
		    putwrap(TTagArr[TMatchArr[OD->TStack[OD->HTTags]].TTidx].EndTag,
			OD,OD->wrap);
		    OD->HTTags--;
		}
		if (PMatchArr[PMidx].NestLev > OD->HPTags + 1) {
		    fprintf(stderr, "Error, found style:%s at a nesting level of %d\n"
			,ParStyle, OD->HPTags);
		    context();
		    exit(1);
		}
		while (OD->HPTags >= PMatchArr[PMidx].NestLev) {
		    /* Pop Paragraph Tags  */
		    if(OD->TStack[OD->HPTags] == PMidx){
		    	pushpop=1;
		    	break;
		    } else {
			    if (strcmp(PTagArr[PMatchArr[OD->TStack[OD->HPTags]].PTidx].EndTag, "") != 0) {
				/* if there was a closing tag, we no longer need a paragraph */
				OD->NeedPar = 0;
			    }
			    putwrap(PTagArr[PMatchArr[OD->TStack[OD->HPTags]].PTidx].EndTag, OD,
			    PTagArr[PMatchArr[OD->TStack[OD->HPTags]].PTidx].DoFold);
			    OD->HPTags--;
			}
		}
		if(!pushpop){
			if(OD->HPTags >= 0&&!PTagArr[PMatchArr[OD->TStack[OD->HPTags]].PTidx].CanNest) {
			    /* Nesting Error */
			    fprintf(stderr, "Error, can't use style:%s within %s\n",
				ParStyle,
				PMatchArr[OD->TStack[OD->HPTags]].PStyle);
			    context();
			    exit(1);
			}
			/* Now push the new style */
			OD->HPTags++;
			OD->TStack[OD->HPTags] = PMidx;
			if (strcmp(PTagArr[OD->PTidx].StartTag, "") != 0) {
			    /* if there is a Starting tag, we no longer need a paragraph */
			    putwrap(PTagArr[OD->PTidx].StartTag, OD,PTagArr[OD->PTidx].DoFold);
			    OD->NeedPar = 0;
			}
		}
		OD->HTTags = OD->HPTags;
  		OD->wrap=PTagArr[OD->PTidx].DoFold;
    }
    /* Check remaining Text Styles to see if they should be popped */
    for (i = OD->HPTags + 1; i <= OD->HTTags; i++) {
	if ((TMatchArr[OD->TStack[i]].TStyle != (TMatchArr[OD->TStack[i]].TMask & TStyle)) ||
	    (TMatchArr[OD->TStack[i]].Font[0] != '\0' && strcmp(TMatchArr[OD->TStack[i]].Font, TFont) != 0) ||
	    (TMatchArr[OD->TStack[i]].FSize != 0 && TMatchArr[OD->TStack[i]].FSize != TSize)) {
	    while (OD->HTTags >= i) {
		putwrap(TTagArr[TMatchArr[OD->TStack[OD->HTTags]].TTidx].EndTag,
		    OD,OD->wrap);
		OD->HTTags--;
	    }
	}
    }
    /* Now check all Text Styles and see if anything else must be pushed */
    if (PTagArr[PMatchArr[OD->TStack[OD->HPTags]].PTidx].AllowText) {
	for (i = 0; i < TMatchLen; i++) {
	    if ((TMatchArr[i].TStyle == (TMatchArr[i].TMask & TStyle)) &&
		(TMatchArr[i].Font[0] == '\0' || strcmp(TMatchArr[i].Font, TFont) == 0) &&
		(TMatchArr[i].FSize == 0 || TMatchArr[i].FSize == TSize)) {
		for (j = OD->HPTags + 1; j <= OD->HTTags; j++) {
		    if (TMatchArr[OD->TStack[j]].TTidx == TMatchArr[i].TTidx)
			break;
		}
		if (j > OD->HTTags) {
		    /* didn't find it - push it on */
		    OD->HTTags++;
		    OD->TStack[OD->HTTags] = i;
		    if (TMatchArr[i].TTidx >= TTagLen) {
		    	fprintf(stderr,"Internal trap - tried to push special: %d\n",TMatchArr[i].TTidx);
	    		context();
		    	exit(1);
		    }
		    putwrap(TTagArr[TMatchArr[i].TTidx].StartTag,
			OD,OD->wrap);
		}
	    }
	}
    }			
}


PutSpecial(Special)
    int Special;
{
    int i,j,n,opos,dataleft;
    char orec[MAXOBUF];
    struct FDest *OD;
    switch (destination) {
	/* string destinations */
    case rtfITitle:
    case rtfInfo:
    case rtfISubject:
    case rtfIAuthor:
    case rtfIOperator:
    case rtfIKeywords:
    case rtfIComment:
    case rtfIVersion:
    case rtfIDoccomm:
	return;
	/* file destinations */
    case rtfHeader:
    case rtfHeaderLeft:
    case rtfHeaderRight:
    case rtfHeaderFirst:
    case rtfFooter:
    case rtfFooterLeft:
    case rtfFooterRight:
    case rtfFooterFirst:
	return;
    case rtfFootnote:
	OD = &Footnote;
	break;
    default:
	if (!InBody) {
	    /*
	     * If we get here, we have text destined for the body. close out
	     * the head section.
	     */
	    if (Title.used) {
		sprintf(LineBuffer,"<title>%s</title>", Title.ptr);
		putwrap(LineBuffer, &File1,1);
	    }
	    putwrap("</head><body>", &File1,1);
	    InBody = 1;
	}
	OD = &File1;
	break;
    }
    if (IStyle_Chg){
	    if(inTable){
			TFont="--Not a Font--";
	    	TStyle=0;
	   		TSize=9999;
	   		ParStyle="_Table";
	    }
    	TestStyle(OD);
    }
    switch (Special) {
    case rtfRow:	/* Clear out all saved cell data */
    	dataleft=1;
	    for(i=firstcell;i<lastcell;i++)
	    	cell[i].cpos=0;
    	while(dataleft){
    		dataleft=0;
    		opos=0;
	    	for(i=firstcell;i<lastcell;i++){
	    		if(cell[i].merge||cell[i].cpos>=cell[i].cbuff.used){
	    			for(j=0;j<=cell[i].width;j++){
	    				orec[opos++]=' ';
	    			}
	    			continue;
	    			}
	    		cell[i].cpos+=nextfield(&(cell[i].cbuff.ptr[cell[i].cpos]), 
	    			cell[i].width-1,  &(orec[opos]), cell[i].just,1);
	    		/* skip leading blanks */
	    		while(cell[i].cbuff.ptr[cell[i].cpos]==' '||cell[i].cbuff.ptr[cell[i].cpos]=='\t')
	    			cell[i].cpos++;
	    		n=strlen(&(orec[opos]));
	    		orec[opos+n]=' ';
	    		orec[opos+n+1]=' ';
	    		opos+=n+2; 		
	    		if(cell[i].cpos<cell[i].cbuff.used)
	    			dataleft=1;
	    	}
    		orec[opos++]='\n';
    		orec[opos]='\0';
    		putwrap(orec, OD,0);
    	}
	    for(i=firstcell;i<lastcell;i++)
	    	cell[i].cbuff.used=0;
		break;
    case rtfPar:
    	if(inTable){
    		AppendText(&(cell[cellno].cbuff), " ");
			return;
    	}   		
    	if (OD->NeedPar&&OD->TSpecial == 0) {
			putwrap(PTagArr[OD->PTidx].ParTag, OD,OD->wrap);
 		}
		OD->NeedPar = 1;
		OD->InCol2 = 0;
		break;
    case rtfTab:
    	if(inTable){
    		AppendText(&(cell[cellno].cbuff), " ");
			return;
    	}   		
		if (OD->TSpecial == MTDiscard)
		    return;
		if (!OD->InCol2) {
		    if (OD->NeedPar) {
				putwrap(PTagArr[OD->PTidx].ParTag, OD,OD->wrap);
				OD->NeedPar = 0;
		    }
		    putwrap(PTagArr[OD->PTidx].Col2Tag, OD,OD->wrap);
		    OD->InCol2 = 1;
		} else {
		    if (OD->NeedPar) {
				putwrap(PTagArr[OD->PTidx].ParTag, OD,OD->wrap);
				OD->NeedPar = 0;
		    }
		    putwrap(PTagArr[OD->PTidx].TabTag, OD,OD->wrap);
		}
		break;
    }
}



context(){
	int cptr;
	fprintf(stderr,"Preceding Context is:\n");
	if(wrap){
		cptr=cend;
	} else {
		cptr=0;
		if(cend==0)
			return;
	}
	while(1){
		if(cptr>=256)
			cptr=0;
		fputc(contextb[cptr++],stderr);
		if(cptr==cend)
			return;
	}
}

void
context_add(str)
char *str;
{
	while(*str!='\0'){
		if(cend>=256){
			wrap=1;
			cend=0;
		}
		contextb[cend++]= *str;
		str++;
	}
}
void
PutHTMLString(p)
    char *p;
{
    int i, j;
    struct FDest *OD;
    context_add(p);
    switch (destination) {
	/* string destinations */
    case rtfITitle:
	AppendText(&Title, p);
	return;
    case rtfInfo:
    case rtfISubject:
    case rtfIAuthor:
    case rtfIOperator:
    case rtfIKeywords:
    case rtfIComment:
    case rtfIVersion:
    case rtfIDoccomm:
	return;
    case rtfObjItem:
	AppendText(&Item, p);
	return;
    case rtfBookmarkStart:
	AppendText(&BkName, p);
	return;
    case rtfBookmarkEnd:
	return;
	/* file destinations */
    case rtfHeader:
    case rtfHeaderLeft:
    case rtfHeaderRight:
    case rtfHeaderFirst:
    case rtfFooter:
    case rtfFooterLeft:
    case rtfFooterRight:
    case rtfFooterFirst:
	return;
    case rtfFootnote:
	OD = &Footnote;
	break;
    default:
	if (!InBody) {
	    /*
	     * If we get here, we have text destined for the body. close out
	     * the head section.
	     */
	    if (Title.used) {
		sprintf(LineBuffer,"<title>%s</title>", Title.ptr);
		putwrap(LineBuffer, &File1,1);
	    }
	    putwrap("</head><body>", &File1,1);
	    InBody = 1;
	}
	OD = &File1;
	break;
    }	
    if(inTable){
		AppendText(&(cell[cellno].cbuff), p);
		return;
	}
    if (IStyle_Chg)TestStyle(OD);
    if (OD->TSpecial == MTDiscard)
	return;
    if (OD->InCol2==0 && PTagArr[OD->PTidx].DeleteCol1==1)
		return;
    if (OD->NeedPar&&OD->TSpecial == 0) {
		putwrap(PTagArr[OD->PTidx].ParTag, OD,OD->wrap);
		OD->NeedPar = 0;
    }
    /* If we are looking at <,> or & that have been translated
    	and we are in MHLiteral, untranslated them.
    */
    if (OD->TSpecial == MTLiteral){
    	if(strcmp(p,"&lt;")==0)p="<";
    	else if(strcmp(p,"&gt;")==0)p=">";
    	else if(strcmp(p,"&amp;")==0)p="&";
    } else if ((ConvertCaps) && p[0] != '&' && p[0] != '[') {
    /* do uppercasing if string doesn't begin with '&' or '[' */
	char *s = p;

	while (*s != '\0') {
	    if (islower(*s))
		*s = toupper(*s);
	    ++s;
	}
    }
    putwrap(p, OD,OD->wrap);
}

void
PutHTML()
{
    char *p;
    p = MapChar(rtfMinor);
    PutHTMLString(p);
}



/*
 * Map a standard character code to an output string.
 * Returns pointer to static string.
 */

static char *
MapChar(stdCode)
    int stdCode;
{
    static char buf[rtfBufSiz];
    char *oStr;

    if (stdCode == rtfSC_nothing)
	(void) strcpy(buf, "[[??]]");
    else {
	oStr = outMap[stdCode];
	if (oStr == (char *) NULL) {	/* no output sequence in map */
	    sprintf(buf, "[[%s]]", RTFStdCharName(stdCode));
	} else
	    (void) strcpy(buf, oStr);
    }
    return (buf);
}


static void
AppendText(OD, text)
    struct SDest *OD;
    char *text;
{
    int need;
    char *ptr;

    need = OD->used + 1 + strlen(text);	/* need space for next char + '\0' */
    if (OD->alloc < need) {
	OD->alloc += DSALLOC * (1 + (need / DSALLOC));
	ptr = (char *) RTFAlloc(
	    (int) sizeof(char) * OD->alloc);
	if (ptr == NULL) {
	    fprintf(stderr, "Memory Allocation Failed\n");
	    context();
	    exit(1);
	}
	if (OD->ptr != NULL) {
	    (void) strcpy(ptr, OD->ptr);
	    RTFFree(OD->ptr);
	}
	OD->ptr = ptr;
    }
    strcpy(&(OD->ptr[(OD->used)]), text);
    OD->used += strlen(text);
}
