/*******************************************************************************/////  >>>	Class name:		TextBuffer	//// 	>>>	Project:		Mailstrom		//	>>>	Date:			February 14, 1992// 	>>>	By:				Adam Treister////	>>>	SUPERCLASS		CObject///*******************************************************************************///	Sponsorship:	Biomedical Research Tech. Program of the NIH, Grant LM05208		//	Copyright © 	1992, The Trustees of the Leland Stanford Junior University.////  Distribution permissions, copyrights and disclaimers are discussed//  in Legal.Notice available with this code.  NO WARRANTY OF ANY KIND IS MADE./*******************************************************************************//*  Abstract:   My general purpose text buffer, smart enough to overflow its* allocated memory, and able to do munging, line wrapping, some parsing etc.A very convenient device.  It has mutated within Mailstrom to do some mail specifictasks, and probably should become a subclass of its former self.*//*******************************************************************************///  Modification history:///*******************************************************************************/#include "TextBuffer.h"#include "TBUtilities.h"#include "string.h"#include "CError.h"Handle 	MakeNewHandle(Size nBytes);void	DisposeTheHandle (Handle *h);#include "IMailDefines.h"void TextBuffer :: ITextBuffer(long size){	h = MakeNewHandle(size);	ASSERT(!MemError());	CheckAllocation(h);	physSize = size;	logiSize = 0;	}/*------------------------------------------------------------------*/void TextBuffer :: Dispose(){	if (h)		DisposeTheHandle(&h);	inherited::Dispose();}/*--------------------------------------------------------------------------*/void TextBuffer :: AdjustSize(long extraBytes){	if (physSize - logiSize < extraBytes)	{		SetHandleSize(h,logiSize + extraBytes);		ASSERT(!MemError());		CheckAllocation(h);	}	physSize = GetHandleSize(h);	}/*--------------------------------------------------------------------------*/void TextBuffer :: DoublePhysSize(){	SetHandleSize(h,physSize = 2*physSize);	ASSERT(!MemError());	CheckAllocation(h);					/* I'm hoping this won't happen cuz its generally a small buffer (uh-oh!) */}/*--------------------------------------------------------------------------*/void TextBuffer :: SetLogicalToPhysical(){	physSize = GetHandleSize(h);	logiSize = physSize;}/*--------------------------------------------------------------------------*/void TextBuffer :: SetPhysicalToLogical(){	SetHandleSize(h, logiSize);	ASSERT(!MemError());	physSize = logiSize;}/*------------------------------------------------------------------*/void TextBuffer :: SetText(Handle incoming){	long len,mem;	OSErr err;		mem = FreeMem();	len = GetHandleSize(incoming);	SetHandleSize(h,len);	mem = FreeMem();	err = MemError();	if (err == noErr)	{		physSize = logiSize = len;		HLock(incoming);	HLock(h);		BlockMove(*incoming,*h,len);		HUnlock(incoming);	HUnlock(h);	}}/*------------------------------------------------------------------*/void TextBuffer :: AppendLine(Str255 str){	AddStr(str);	if (str[*str] != CR) AddChar(CR);}/*------------------------------------------------------------------*/extern char gListSep;void TextBuffer :: AddText(Handle incoming){	long len = GetHandleSize(incoming);			AdjustSize(len);	HLock(h);	BlockMove(*incoming, (*h)+logiSize, len);	ASSERT(!MemError());	HUnlock(h);	logiSize += len;}/*------------------------------------------------------------------*/void TextBuffer :: AddStr(Str255 str){	long len = *str;		AdjustSize(len);	HLock(h);	BlockMove(str+1, (*h)+logiSize, len);	ASSERT(!MemError());	HUnlock(h);	logiSize += len;}/*------------------------------------------------------------------*/void TextBuffer :: AddCStr(char* cstr){	long len=0;	char *counter;	len = strlen(cstr);	AdjustSize(len);		HLock(h);	BlockMove(cstr, (*h)+logiSize, len);	ASSERT(!MemError());	HUnlock(h);	logiSize += len;}/*------------------------------------------------------------------*/void TextBuffer :: AddBlock(char* start, long length){	AdjustSize(length);	HLock(h);	BlockMove(start, (*h)+logiSize, length);	ASSERT(!MemError());	HUnlock(h);	logiSize += length;}/*------------------------------------------------------------------*/void TextBuffer :: AddChar(char c){	AdjustSize(1);	*((*h)+logiSize) = c;	logiSize++;}/*------------------------------------------------------------------*/void TextBuffer :: AddFloat(float f,int decPlaces, int pad){	Str255 str;		Float2String(f,str,decPlaces);	if (pad)	PadString(str, pad, TRUE);	AdjustSize(*str);	AppendChar(str,TAB);	AddStr(str);}/*------------------------------------------------------------------*/void TextBuffer :: AddLong(long num,int pad){	Str255 str;		NumToString(num,str);	if (pad)	PadString(str, pad, TRUE);	AdjustSize(*str);	AppendChar(str,TAB);	AddStr(str);}/*------------------------------------------------------------------*/void TextBuffer :: AddPoint(Point pt){	Str255 str,a,b;		StrCopy((unsigned char *)"\p(\t", str);	NumToString((long) pt.h,a);	AppendChar(a,TAB);	AppendChar(a,gListSep);	AppendChar(a,TAB);	NumToString((long) pt.v,b);	AppendChar(b,TAB);	AppendChar(b,CLOSEPAREN);	AppendChar(b,TAB);	PStrCat(3,str,a,b);	AddStr(str);}/*------------------------------------------------------------------*/void TextBuffer :: AddRealPoint(float x, float y){	Str255 str,a,b;		StrCopy((unsigned char *)"\p(\t", str);	Float2String(x,a,3);	AppendChar(a,TAB);	AppendChar(a,gListSep);	AppendChar(a,TAB);	Float2String(y,b,3);	AppendChar(a,TAB);	AppendChar(b,CLOSEPAREN);	AppendChar(b,TAB);	PStrCat(3,str,a,b);	AddStr(str);}/*------------------------------------------------------------------*/extern char gDecPoint;#define IsGoodChar(c) 	(IsDecimalPoint(c) OR IsDigit(c) OR ((c) == CR) OR ((c) == '-'))Handle TextBuffer :: ConvertToTabText(Handle srcText){	register int ct,i;	Handle destText;	register Ptr destP,srcP;	Boolean wasGood = FALSE;	long srcLen;		destText = NewHandleClear(srcLen = GetHandleSize(srcText));	CheckAllocation(destText);	HLock(destText);		destP = *destText;	HLock(srcText);			srcP = *srcText;	ct = 0;	for (i = 0; i < srcLen; i++)	{		if (IsGoodChar(srcP[i]))		{			destP[ct++] = srcP[i];			wasGood = (srcP[i] != CR);		/* return sets wasGood to F so tabs don't go in at front of line */		}		else 		{				if (wasGood)	destP[ct++] = TAB;			wasGood = FALSE;		}	}	HUnlock(srcText);	HUnlock(destText);	SetHandleSize(destText,ct);	ASSERT(!MemError());	CheckAllocation(destText);	physSize = logiSize = ct;	return(destText);}/*--------------------------------------------------------------------------*/Handle TextBuffer :: CopyHandle(){	Handle dup;		dup = NewHandleClear(logiSize);		// force even size	ASSERT(!MemError());	HLock(h);		HLock(dup);	BlockMove(*h,*dup,logiSize);	HUnlock(h);		HUnlock(dup);	return(dup);}/*--------------------------------------------------------------------------*/Handle TextBuffer :: CopyEvenLengthHandle(){	Handle dup;		dup = NewHandleClear(logiSize + (ODD(logiSize) ? 1 : 0));		// force even size	ASSERT(!MemError());	HLock(h);		HLock(dup);	BlockMove(*h,*dup,logiSize);	if (ODD(logiSize))	{		*(*dup+logiSize) = '\0';				// NULL Pad	}	HUnlock(h);		HUnlock(dup);	return(dup);}/*--------------------------------------------------------------------------*/#include "string.h"void 	SpinCursor(short num);short TextBuffer :: Munge(char* s, char* r)		/* returns the number of subst made */{	long len1,len2,offset = 0;	short ct = -1;	SetPhysicalToLogical();	len1 = strlen(s);	len2 = strlen(r);	TurnWatchOn();	while (offset >= 0)	{		offset = Munger (h,offset,s,len1,r,len2);		ASSERT(!MemError());		ct++;		if (ct %25 == 0)		SpinCursor(12);	}	SetLogicalToPhysical();	ASSERT(!MemError());	return(ct);}/*--------------------------------------------------------------------------*/void TextBuffer :: MungeCR2CRLF(){	Munge("\r","\r\n");}/*--------------------------------------------------------------------------*/void TextBuffer :: MungeCR2Space(){	Munge("\r"," ");}/*--------------------------------------------------------------------------*/void TextBuffer :: MungeCRLF2CR(){//	Munge("\r\n","\r");		register char *start, *reader, *writer, *eoText;		HLock(h);		start = reader = writer = *h;	eoText = start + logiSize;	TurnWatchOn();		while (reader < eoText)	{		if (*reader != LF)			*writer++ = *reader;		reader++;	}	logiSize = writer-start;	HUnlock(h);	SetPhysicalToLogical();	TurnArrowOn();}/*--------------------------------------------------------------------------*/void TextBuffer :: Caret(short startCol){	Boolean endsInCR;	long len;	Str255 prefix;	extern short gCaretWrap;		GetIndString(prefix, IMAILSTRS,23);				// get the caret string from resource fork	len = *prefix;	if (len <= 0)								// if not found default backe to ">   "	{		StrCopy("\p>   ",prefix);		len = 4;	}	endsInCR = (*h)[logiSize-1] == CR;	if (endsInCR)	logiSize--;	SetPhysicalToLogical();		Unwrap(gCaretWrap,startCol);										// new	WrapTo(gCaretWrap-len, F);									// new	SetPhysicalToLogical();		(void) Munger (h,0L,NULL,0L,prefix+1,len);			/* insert leading caret */	physSize = GetHandleSize(h);	SetLogicalToPhysical();	endsInCR = (*h)[logiSize-1] == CR;	if (endsInCR)	{		SetHandleSize(h,GetHandleSize(h)-1);		physSize--;		logiSize--;	}	*prefix = '\r';				// this does a p2c, while inserting CR	prefix[len+1] = '\0';			// in front.	Munge("\r",(char *) prefix);	ASSERT(!MemError());	physSize = GetHandleSize(h);	AddChar(CR);}	/*--------------------------------------------------------------------------*/void TextBuffer :: Indent(){	Munge("\r","\r    ");}/*--------------------------------------------------------------------------*/// adding the parameter startCol to deal with the fact that the first line// might not start at col = 0, and if not l don't want to read that short line// as intention (ie, and leave the CR in).  startCol maintains its value until// a CR is perceived to be intentionalvoid TextBuffer :: Unwrap(int threshold, short startCol){#ifdef OLDWRAP	Munge("\r\r","ÆÆ");						/*replace cr-cr with ÆÆ */	Munge("ÆÆ\r","ÆÆÆ");					/*replace ÆÆcr with ÆÆÆ */	Munge("\r","");							/*remove crs  */	Munge("Æ","\r");						/*replace Æ with cr */#else	{		char *p,*block,*eoText,*lookahead,*crInQuestion,*startLine;				HLock(h);			block = *h;				p = block;		eoText = block + logiSize;		while ( p < eoText )		{			startLine = p;			while (p < eoText AND *p != CR ) p++;			if (p >= eoText)	break;			lookahead = p+1;			crInQuestion = p;			// never remove double CRs or crs before lines that start in punctuation			if (*lookahead != CR AND !IsPunct(*lookahead) )				{		 		while (!IsSpace(*lookahead))		lookahead++;		/*advance to end of first word in next line */				if ((lookahead - startLine + startCol) >= threshold)				{					*crInQuestion = SPACE;			/* the CR was due to wrapping, replace it with SPACE */				}				else startCol = 0;			}			else startCol = 0;			p++;		}				HUnlock(h);	}		#endif}/*--------------------------------------------------------------------------*/enum { HYPH1 = 0xD0, HYPH2,  QUOTE1, QUOTE2, APOST1, APOST2 };#define LAST_ASCII 127void TextBuffer :: StripBadChars(){	register long i;	register unsigned char  *array;	register unsigned char c;		HLock(h);	array = (unsigned char  *) *h;		for (i=0; i < logiSize; i++)	{		if ((c = array[i]) > LAST_ASCII)		{			switch (c)			{				case HYPH1:		array[i] = '-';			break;				case HYPH2:		array[i] = '-';			break;				case QUOTE1:	array[i] = '"';			break;				case QUOTE2:	array[i] = '"';			break;				case APOST1:	array[i] = '\'';		break;				case APOST2:	array[i] = '\'';		break;								default:		array[i] = '?';			}		}	}	HUnlock(h);}/*--------------------------------------------------------------------------*/void TextBuffer :: RemoveConseqCRs()			/* search and replace repeatedly until none are replaced */{	short ct;	do 	{		ct = Munge("\r\r","\r");	}	while (ct > 0);}/*--------------------------------------------------------------------------*/char *ScanLine(char *start, short maxLen, short *actualLen, char *eoBuffer, Boolean *wasWrapped);void TextBuffer :: WrapTo(short lineLen,Boolean InHeader){	Boolean done = F;	Handle tmpH;	Ptr curReader,writer,reader;	short actualLen;	Ptr eoBuffer;	long newSize,newLen;	Boolean wasWrapped;		newSize = MAX((logiSize + (logiSize >> 6)),physSize);		/* increase size by 1/64 if nec*/	tmpH = MakeNewHandle(newSize);		HLock(tmpH);	writer = *tmpH;		HLock(h);		reader = *h;	eoBuffer = reader + logiSize;		while (!done)	{		curReader = ScanLine(reader,lineLen,&actualLen,eoBuffer,&wasWrapped);			done = curReader >= eoBuffer;		BlockMove(reader,writer, (long) actualLen);		writer += (long) actualLen;		*writer++ = CR;		if (wasWrapped AND InHeader)				*writer++ = SPACE;		/* if we're wrapping the header, stick a space as the first char */		reader = curReader;	}		newLen = writer - *tmpH;	logiSize = newLen;	physSize = newSize;	HUnlock(tmpH);	DisposeTheHandle(&h);	h = tmpH;	ASSERT(!MemError());}/*--------------------------------------------------------------------------*//*	scan the stream looking for one of three conditions:  EOF, maxLen chars w/o CR, CR *//*	fill in the actual length, ignoring trailing and leading white space */char *ScanLine(char *start, short maxLen, short *actualLen, char *eoBuffer, Boolean *needsWrapP){	char *charP, *eoPrev, *boNext;	short ct=0;	char *lastWhite = NULL;		charP = start;	while (charP < eoBuffer AND ct <= maxLen AND *charP != CR)		/* count chars and remember last white */	{		if (IS_WHITE(*charP))				lastWhite = charP;		charP++;		ct++;	}	if (lastWhite == NULL)	lastWhite = charP;				/* handle lines over 76 chars w/o whitespace */	if (ct >= maxLen)	{				eoPrev = lastWhite;		while (IS_WHITE(*eoPrev))	eoPrev--;			/* remove white space at end */		*actualLen = eoPrev-start+1;				boNext = lastWhite;		while (IS_WHITE(*boNext))	boNext++;;			/* ignore white space at start of next line */						if (boNext == eoPrev)	boNext++;			// break isn't made at white space, one word lines		*needsWrapP = T;		return(boNext);	}		if (*charP == CR)	{		boNext = charP+1;							/* skip the return, cuz it'll be added */				*actualLen = charP-start;		*needsWrapP = F;		return(boNext);	}		if (charP >= eoBuffer)	{		*actualLen = eoBuffer-start;		*needsWrapP = F;		return(eoBuffer);	}}