/*
** DATATYPE: IXML
** MODULE: Funcs.c
** $VER: IXML iX Datatype Funcs.c 23.03
** SHORT: Library functions for IXML iX datatype
**					  v     ,
** Copyright (c) 1997 Ivan Sturlic
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/memory.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <clib/alib_protos.h>

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <dtex/dtex.h>

/*
** Every iX Datatype has 5 functions:
**
** DTF_Identify() which performs some tests on given file
** to determine if that file should be read by this datatype.
** Function returns an integer value between 0 and 9.
** Value of 9 means that file is recogized 100%.
** Value of 0 means that file is not recognized.
** Value of e.g. 5 means that datatype is about 50% sure that
** file is of required type.
** Value of e.g. 8 means that datatype is almost 100% sure that
** file is of required type etc.
**
** DTF_FileStruct() which is used to determine begin-end of
** some logical parts of file.
** For example, AmigaGuide database is made up of nodes, and this
** function should find begin, end and name of every node.
** 
** DTF_FreeStruct() should free the list of nodes allocated
** by DTF_FileStruct().
**
** DTF_ProcessChunk() which reads one part of file and returns
** information on that part in iX_ChunkInfo form readable by iX-Guide
**
** DTF_FreeChunk() is used to deallocate stuff allocated
** by DTF_ProcessChunk().
*/

//---------------------------------------------------------
//	DTF_IDENTIFY
//---------------------------------------------------------
ULONG __saveds __asm DTF_Identify(register __a0 STRPTR name)
{
 BPTR fh;
 char buff[7];
 ULONG retval = 0L;
 STRPTR lastc;

 /* we'll check the extension first */
 lastc = name+strlen(name)-1;
 while(lastc>=name)
 {
	if (*lastc=='.')
	{
		if (stricmp(lastc,".ixml")==0)
			return 9L;
		else
			break;
	}
	lastc--;
 }

 // Check the first 6 characters. They should
 // be "<IXML>" if file is IXML (may be lowercase, btw).

 if (fh = Open(name,MODE_OLDFILE))
 {
	Read(fh,buff,6);
	buff[6] = '\0';
	if (stricmp(buff,"<IXML>")==0)
		retval = 9L;
	Close(fh);
 }

 return retval;
}

//=== SUPPORT ROUTINE =====================================
//	check if tag is broken
//	- returns TRUE if tag is valid
//---------------------------------------------------------
BOOL TagValid(STRPTR tagp,STRPTR buffend,STRPTR *cp)
{
 BOOL valid = FALSE;
 
 if (*tagp=='<') // this is an IXML tag
 {
	// find the end of tag
	while((tagp<=buffend) && (*tagp!='>'))
	{
		if (*tagp=='"')
		{
			if (tagp<buffend)
			{
				tagp++;
				while((tagp<=buffend) && (*tagp!='"')) tagp++;
			}
		} // END if double quotes
		else if (*tagp=='\'')
		{
			if (tagp<buffend)
			{
				tagp++;
				while((tagp<=buffend) && (*tagp!='\'')) tagp++;
			}
		} // END if single quotes
					
		if (tagp<=buffend) tagp++;
	} // END while!='>'
	if (tagp<=buffend) // end of tag has been found
		valid = TRUE;				
 } // if '<'

 *cp = tagp;

 return valid;
}

//=== SUPPORT ROUTINE =====================================
//=== Extract argument from tag ===========================
// - moreargs will be TRUE if there are some args left
// - returns pointer to chars after processed argument
//   or pointer to the end of tag (NULL character, not '>')
//	if no more args found
// - argname points to the arg name and argval to the arg value
//   (both strings are NULL terminated)
//=========================================================
STRPTR ExtractArg(STRPTR tagptr, STRPTR *argname, STRPTR *argval, BOOL *moreargs)
{
 STRPTR endargname,endargval;

 *moreargs = FALSE;

 while(*tagptr==' ' || *tagptr==9 || *tagptr=='\n') tagptr++; 	// skip blanks
	
 if (*tagptr=='\0' || *tagptr=='>')		// end of tag
 {
	*argname = NULL;
	*argval = NULL;
	return tagptr;
 }

 *argname = tagptr;	// start of argument name
 tagptr = strpbrk(tagptr," \t=\n>\0");
 endargname = tagptr;
 
 while(*tagptr==' ' || *tagptr==9 || *tagptr=='\n') tagptr++; 	// skip blanks
 
 if (*tagptr!='=')	// no argument value
 {
	*argval = NULL;
	if (*tagptr && *tagptr!='>') *moreargs = TRUE;
	*endargname = '\0';
	return tagptr;
 }

 // we probably have an argument value
 tagptr++;
 while(*tagptr==' ' || *tagptr==9 || *tagptr=='\n') tagptr++; 	// skip blanks
 
 if (*tagptr == '"')	// double quotes
 {
	tagptr++;
	*argval = tagptr;
	while(*tagptr && *tagptr!='"') tagptr++;
	endargval = tagptr;
	if (*tagptr=='"') tagptr++;
 }
 else if (*tagptr == '\'')	// single quotes
 {
	tagptr++;
	*argval = tagptr;
	while(*tagptr && *tagptr!='\'') tagptr++;
	endargval = tagptr;
	if (*tagptr=='\'') tagptr++;
 }
 else	// no quotes
 {
	*argval = tagptr;
	tagptr = strpbrk(tagptr," \t\n>\0");
	endargval = tagptr;
 }

 while(*tagptr==' ' || *tagptr==9 || *tagptr=='\n') tagptr++; 	// skip blanks
 if (*tagptr && *tagptr!='>') *moreargs = TRUE;

 *endargname = '\0';
 *endargval = '\0';

 return tagptr;
}

//=== SUPPORT ROUTINE =====================================
//	skip to the end of tag
//---------------------------------------------------------
STRPTR SkipToEOT(STRPTR cptr)
{
 while(*cptr && *cptr!='>')
 {
	if (*cptr=='"')
	{
		cptr++;
		while(*cptr && *cptr!='"')
			cptr++;
	}
	else if (*cptr=='\'')
	{
		cptr++;
		while(*cptr && *cptr!='\'')
			cptr++;
	}

	if (*cptr) cptr++;
 }

 return cptr;
}

//=== SUPPORT ROUTINE =====================================
//	skip to the end of comment
//---------------------------------------------------------
STRPTR SkipToEOC(STRPTR cptr)
{
 while(*cptr)
 {
	if (*cptr=='-' && cptr[1]=='-' && cptr[2]=='>')
		return cptr+2;
	cptr++;
 }

 return cptr;
}

#define FS_BUFFERSIZE	8192	 // buffer size used in DTF_FileStruct()

//---------------------------------------------------------
//	DTF_FILESTRUCT
// return number of nodes if everything OK, otherwise return
// some negative number which should represent some error code
//---------------------------------------------------------
LONG __saveds __asm DTF_FileStruct(register __a0 BPTR fh,
							register __a1 APTR readcallback,
							register __a2 struct List *nodelist,
							register __a3 BOOL *saveindex)
{
 struct IXML_Node *nd=NULL;
 LONG numnodes=0,ftotal,buffsize,numr,buffpos,
 	readpos,extraread,tdist;
 void (*rcb)(ULONG,ULONG);
 STRPTR buffer,cp,buffend,btstart,newbuff,nodename;
 BOOL broken,endblock,inNode=FALSE;

 rcb = (void (*)(ULONG,ULONG))readcallback;
 *saveindex = TRUE;		// save node index
 NewList(nodelist);		// init list of nodes

 // build the list of nodes...
 if (NULL==(buffer = AllocVec(FS_BUFFERSIZE,MEMF_PUBLIC)))
	return -1;

 Seek(fh,0L,OFFSET_END);
 ftotal = Seek(fh,0L,OFFSET_BEGINNING);

 buffsize = FS_BUFFERSIZE;
 while(numr = Read(fh,buffer,FS_BUFFERSIZE))
 {
	// process the buffer
	// first make sure that no tag is broken
	// (that the end of buffer is not inside tag)
	buffpos = Seek(fh,0L,OFFSET_CURRENT);
	
	if (readcallback)
		(*rcb)(buffpos,ftotal);
	
	buffpos -= numr;
	cp = buffer;
	buffend = cp + numr - 1;
	broken = FALSE;
	readpos = Seek(fh,0L,OFFSET_CURRENT);
	while(cp <= buffend)
	{
		if (*cp=='<') // this is the IXML tag
		{
			btstart = cp;	// beginning of tag
			broken = TRUE;
			// is this tag broken?
			if (TagValid(btstart,buffend,&cp))
				broken = FALSE;
			else
				broken = TRUE;
		} // if '<'
		
		if (cp<=buffend) cp++;
	} // while cp <= buffend
	extraread = 0;
	while (broken && (numr==FS_BUFFERSIZE))	// is some tag broken?
	{
		// yes, we need to read in some more data
		tdist = btstart - buffer; // position of broken tag
		// reallocate the buffer
		if (NULL==(newbuff = AllocVec(buffsize+FS_BUFFERSIZE,MEMF_PUBLIC)))
			{FreeVec(buffer); return -1;}
		CopyMem(buffer,newbuff,buffsize);
		FreeVec(buffer);
		buffer = newbuff;
		numr = Read(fh,buffer+buffsize,FS_BUFFERSIZE);
		if (numr)
		{
			btstart = buffer + tdist;	// position at the tag beginning
			buffend = buffer + buffsize + numr - 1;
			buffsize+=FS_BUFFERSIZE;
			// check again if tag is broken
			if (TagValid(btstart,buffend,&cp))
				broken = FALSE;
			extraread = cp - buffer - FS_BUFFERSIZE + 1;
		}
	}

	if (broken)	// still broken?
	{
		/* this tag is not valid (doesn't have '>' at the end) */
		buffend = btstart;
	}
	else
		buffend = cp;
	
	// find node or endnode in buffer
	// now, we are sure that there are no more broken tags
	cp = buffer;
	while(cp <= buffend)
	{
		if (*cp=='<') // this is IXML tag
		{
			btstart = cp;
			endblock = FALSE;
			cp++;
			if (*cp=='!' && cp[1]=='-' && cp[2]=='-') // comment?
			{
				cp = SkipToEOC(cp);
				continue;
			}
			while((*cp==' ') || (*cp==9) || (*cp=='\n')) cp++; // skip blanks
			if (*cp=='/') // end block tag
			{
				endblock = TRUE;
				cp++;
			}
			if (*cp!='>')
			{
				while((*cp==' ') || (*cp==9) || (*cp=='\n')) cp++; // skip blanks
				if (*cp!='>')
				{
					char stor,*nm;
					STRPTR argname,argval;
					BOOL moreargs;
							
					nm = cp;
					while((*cp!='>') && (*cp!=' ') && (*cp!=9) && (*cp!='\n')) cp++;
					stor = *cp;
					*cp = '\0';
					if (stricmp(nm,"NODE")==0) // node tag found
					{
						nodename = NULL;
						*cp = stor;
						if (!endblock)
						// extract attributes
						do
						{
							cp = ExtractArg(cp,&argname,&argval,&moreargs);
							if (argname)
								if (stricmp(argname,"NAME")==0)
									if (argval)
									{
										if (NULL==(nodename = AllocVec(strlen(argval)+1,MEMF_ANY)))
											{FreeVec(buffer); return -1;}
										strcpy(nodename,argval);
										break;
									}
						} while(moreargs);
								
						if (!endblock && nodename && !inNode) // we got the beginning of node
						{
							// allocate new node struct
							inNode = TRUE;
							if (NULL==(nd = AllocVec(sizeof(struct IXML_Node),MEMF_CLEAR)))
								{FreeVec(buffer); return -1;}
							nd->nd_Node.ln_Name = nodename;
							nd->nd_Begin = buffpos + (btstart - buffer);
						}
								
						if (endblock && inNode)	// end of node
						{
							cp = SkipToEOT(cp);
							nd->nd_End = buffpos + (cp - buffer);
							AddTail(nodelist,(struct Node *)nd);
							numnodes++;
							inNode = FALSE;
							nd = NULL;
						}
					} // if "NODE"
					else if(stricmp(nm,"HEAD")==0) // head element found
					{
						*cp = stor;
							
						if (NULL==(nodename = AllocVec(20,MEMF_ANY)))
							{FreeVec(buffer); return -1;}
						strcpy(nodename,"___ixml_head");
								
						if (!endblock && nodename && !inNode) // we got the beginning of header
						{
							// allocate new node struct
							inNode = TRUE;
							if (NULL==(nd = AllocVec(sizeof(struct IXML_Node),MEMF_CLEAR)))
								{FreeVec(buffer); return -1;}
							nd->nd_Node.ln_Name = nodename;
							nd->nd_Begin = buffpos + (btstart-buffer);
						}
								
						if (endblock && inNode)	// end of header
						{
							cp = SkipToEOT(cp);
							nd->nd_End = buffpos + (cp - buffer);
							AddTail(nodelist,(struct Node *)nd);
							numnodes++;
							inNode = FALSE;
							nd = NULL;
						}
					} // if "HEAD"
					else *cp = stor;
				} // if *cp!='>'
			} // if *cp!='>'
			cp = SkipToEOT(cp); // skip to the end of tag
		} // if IXML tag
		else cp++;
	}
	// correct file position and buffer size
	if (extraread)
	{
		readpos += extraread;
		Seek(fh,readpos,OFFSET_BEGINNING);
		FreeVec(buffer);
		if (NULL==(buffer = AllocVec(FS_BUFFERSIZE,MEMF_ANY)))
 			return -1;
		buffsize = FS_BUFFERSIZE;
	}
 } // while have data from file

 FreeVec(buffer);

 return numnodes;
}

//---------------------------------------------------------
//	DTF_FreeStruct
//---------------------------------------------------------
void __saveds __asm DTF_FreeStruct(register __a0 struct List *nodelist)
{
 struct IXML_Node *nd,*pnd;

 // free the list of nodes
 nd = (struct IXML_Node *)nodelist->lh_Head;
 while(nd->nd_Node.ln_Succ)
 {
	pnd = nd;
	nd = (struct IXML_Node *)nd->nd_Node.ln_Succ;
	FreeVec(pnd->nd_Node.ln_Name);
	FreeVec(pnd);
 }

 NewList(nodelist);
}

//=== SUPPORT ROUTINES ====================================
//=== custom realloc function =============================
void *Realloc(void *oldptr,long newsz,long oldsz)
{
 void *mempt;
 long copysize;
 
 mempt = AllocVec(newsz,MEMF_PUBLIC);
 if (oldsz>newsz) copysize=newsz; else copysize=oldsz;
 memcpy(mempt,oldptr,copysize);
 FreeVec(oldptr);

 return(mempt);
}

//=== CHECK for the entry overflow ========================
BOOL CheckMemory(struct iX_ChunkInfo *ci,long *chunk_c,long entry_c,LONG ParserECS)
{
	if (*chunk_c==ParserECS)
     {
		*chunk_c=0;
		ci->ci_EI = Realloc(ci->ci_EI,(entry_c+ParserECS)*sizeof(struct iX_EntryInfo),entry_c*sizeof(struct iX_EntryInfo));
	}

	if (!ci->ci_EI)
		return FALSE;
}

//=== find out the tab size ===============================
LONG NextMultipleDiff(LONG factor,LONG num)
{
 LONG multiple;

 multiple=num+1;
 while(multiple % factor) multiple++;

 return(multiple-num);
}

//=== FREE iX_ChunkInfo ===================================
void FreeChunkInfo(struct iX_ChunkInfo *ci)
{
	if (ci)
  	{
  		if (ci->ci_Data) FreeVec(ci->ci_Data);
  		if (ci->ci_EI) FreeVec(ci->ci_EI);
		FreeVec(ci);
	}
}

//---------------------------------------------------------
// strings

// IXML tags
STRPTR IXMLTags[] = {
	"IXML","HEAD","NODE","DATABASE","DOCUMENT","BASE","SCREEN",
	"MACRO","ALIAS","REXX","SYSTEM","ANIMSCRIPT","MAP","H1","H2",
	"H3","H4","H5","H6","F1","F2","F3","F4","F5","F6","X1","X2",
	"X3","X4","X5","X6","CODE","DEFF","FONT","B","I","U","S","D",
	"DIV","CENTER","A","IMG","ASEQUENCE","AREA","HR","BR","P",
	"PRE","UL","LI","RECT","INDENT","TABLE","COLGROUP","COL","TBODY",
	"TR","TD","FRAMESET","FRAME","NOBR",
	NULL};

// IXML attributes
STRPTR IXMLAttrs[] = {
	"NAME","TITLE","PREV","NEXT","AUTHOR","VERSION","COPYRIGHT",
	"WORDWRAP","INDEX","HELP","TOC","VSPACING","LEFT","TOP","WIDTH",
	"HEIGHT","TABSIZE","OUTPUT","ALIGN","VALIGN","BACKGROUND","BGCOLOR",
	"TEXTFG","TEXTBG","TEXTSC","CODESET","USEBLANKS","LINKPADDING",
	"LINKFGCOL","LINKBG","LINKBGCOL","LINKFGCOLSEL","LINKBGSEL",
	"LINKBGCOLSEL","LINKFRAME","LINKFCOL","LINKFCOLSEL","LINKBORDER",
	"BGSOUND","BGSOUNDLOOP","HREF","MODE","DEPTH","PUBNAME","USEPUBLIC",
	"DEFAULT","REAL","ALIAS","PROGRAM","INPUT","LOOP","BACKWARD","PINGPONG",
	"SIZE","COLOR","BCOLOR","SCOLOR","SPACE","DEFALIGN","ACTION","TARGET",
	"SRC","TRANS","BORDER","ALT","ISANIM","ANIMSCRIPT","ANIMSPEED","USEMAP",
	"HSPACE","VSPACE","FRAME","FCOLOR","FCOLSEL","FROM","TO","COORDS",
	"NOSHADE","CLEAR","CELLSPACING","CELLPADDING","RULES","SPAN","COLS",
	"ROWS","NORESIZE","COLSPAN","ROWSPAN","CELLBORDER",
	NULL};

//---------------------------------------------------------

//=== these are IXML special character IDs
#define CHR_OPENBRACE		0
#define CHR_CLOSEBRACE		1
#define CHR_DOT			2
#define CHR_AMP			3
#define CHR_DOUBLEQUOTE		4
#define CHR_LESSTHAN		5
#define CHR_GREATERTHAN		6
#define CHR_SINGLEQUOTE		7

//---------------------------------------------------------
//	DTF_ProcessChunk
//---------------------------------------------------------
struct iX_ChunkInfo * __saveds __asm DTF_ProcessChunk(register __a0 BPTR src,
										register __a1 Object *guide,
										register __d0 LONG b_point,
										register __d1 LONG e_point,
										register __d2 BOOL head)
{
 struct iX_ChunkInfo		*ci;
 LONG					ParserECS,cc,ec=0,ecc=0,chrproc=0,tagc,attrc;
 STRPTR					dp,endp,olddp,tdp,tagend;
 char					stor;
 UBYTE					asciival;
 STRPTR					argname,argval;
 struct iX_Alias			*macro;
 BOOL					neg,moreargs;
 STRPTR					macroargname[25],macroargval[25]; // maximum of 25 args for macro tag allowed
 APTR					MemPool;
 STRPTR					tmpbuff;
 LONG					tabsize=4;
 struct MinList			*macros=NULL,emptylist;
 STRPTR					*CharNames = NULL;

 if (guide)
 {
 	GetAttr(XGDA_TabSize,guide,(ULONG *)&tabsize);
 	GetAttr(XGDA_MacroList,guide,(ULONG *)&macros);
	GetAttr(XGDA_CharNames,guide,(ULONG *)&CharNames);
 }
 else
 {
	NewList((struct List *)&emptylist);
	macros = &emptylist;
 }
 
 if (!src)
 	return NULL;

 ParserECS = 1000;
 if ((e_point - b_point) > 50000)
 	ParserECS = 5000;
 else
 if ((e_point - b_point) > 20000)
 	ParserECS = 3000;

 if (NULL==(ci = AllocVec(sizeof(struct iX_ChunkInfo),MEMF_CLEAR)))
	return NULL;

 if (NULL==(ci->ci_EI = AllocVec(ParserECS*sizeof(struct iX_EntryInfo),MEMF_CLEAR)))
 	goto ERROR;
 if (NULL==(MemPool = CreatePool(MEMF_PUBLIC|MEMF_CLEAR,8192,4096)))
	goto ERROR;
 if (NULL==(ci->ci_Data = AllocVec(e_point-b_point+2,MEMF_PUBLIC)))
 	goto ERROR;
 if (CharNames)
 	if (NULL==(tmpbuff = AllocVec(e_point-b_point+2,MEMF_PUBLIC)))
 		goto ERROR;

 // fill the source buffer
 Seek(src, b_point, OFFSET_BEGINNING);
 if (0==(ci->ci_DataLen = Read(src, ci->ci_Data, e_point-b_point+1)))
 	goto ERROR;
 ci->ci_Data[ci->ci_DataLen] = 0;

 if (!CharNames)
 	goto PASS2;	// no charnames array
 // ### pass 1 ############################################
 // - replace all character names (&...;) with appropriate characters
 // - special IXML chars like <, >, & etc. replace with unused low chars
 // like chr 1, 2, 3 ... and later (in pass 2) replace them with real chars

 dp = ci->ci_Data;
 tdp = tmpbuff;

 while(*dp)
 {
	if (*dp == '&')
	{
		endp = strpbrk(dp," \t;\0");
		if (endp)
		{
			stor = *endp;
			*endp = '\0';
			asciival = 0;
			if(dp[1]=='#') // ascii decimal value
				asciival = atol(dp+2);
			else			// name of character
			{
				cc = 0;
				while(CharNames[cc])
				{
					if (strcmp(CharNames[cc],dp+1)==0)
						break;
					cc++;
				}
				if (CharNames[cc])	// if character name found
				switch(cc)
				{
					// special characters
					case CHR_OPENBRACE:
					case CHR_CLOSEBRACE:
					case CHR_DOT:
					case CHR_AMP:
					case CHR_DOUBLEQUOTE:
					case CHR_LESSTHAN:
					case CHR_GREATERTHAN:
					case CHR_SINGLEQUOTE:
					 asciival = cc+1;
					break;
					// other characters
					default:
					 asciival = 152+cc;
					break;
				}
			} // if name of character
			
			// replace with ascii value
			*endp = stor;
			if (asciival)
			{
				*tdp = asciival;
				if (*endp==';') endp++;
				ci->ci_DataLen -= endp-dp-1;
			}
			else *tdp = '&';
		} // if endp
		dp = endp; tdp++;
	} // if *dp=='&'
	else
		{*tdp = *dp; tdp++; dp++;}
 } // while dp

 *tdp = '\0';
 FreeVec(ci->ci_Data);
 ci->ci_Data = tmpbuff;

PASS2:
 // ### pass 2 ############################################
 // - extract tags, attributes, words etc.
 // - replace chars 1-8 with real characters

 dp = ci->ci_Data;	// reset

 while(*dp)
 {
	olddp = dp;
	switch(*dp)
	{
		case '<':		// tag
		 neg = FALSE;
		 // this is an IXML tag
		 // find out the tag name
		 dp+=1;
		 while(*dp==' ' || *dp==9 || *dp=='\n') dp++; // skip blanks
		 
		 if (*dp=='!' && dp[1]=='-' && dp[2]=='-')	// this is comment - skip it
		 {
			dp = SkipToEOC(dp);
			break;
		 }
		 
		 if (*dp == '/')	// this is block end tag (e.g. /NODE,/B...)
		 {
			neg = TRUE;
			dp++;
			while(*dp==' ' || *dp==9 || *dp=='\n') dp++; // skip blanks
		 }
		 
		 endp = strpbrk(dp," \t\n>\0");
		 stor = *endp;
		 *endp = '\0';
		 for(tdp=dp;tdp<endp;tdp++) *tdp = toupper(*tdp); // tag name to uppercase
		 // search macro list first
		 macro = (struct iX_Alias *)macros->mlh_Head;
		 while(macro->al_Node.mln_Succ)
		 {
			if (strcmp(macro->al_Alias,dp)==0) // NOTE: macro->Alias MUST be in uppercase
				break;			// this tag is macro
			macro = (struct iX_Alias *)macro->al_Node.mln_Succ;
		 }
		 tagc = 0;
		 // search IXMLTags
		 while(IXMLTags[tagc])
		 {
			if (strcmp(dp,IXMLTags[tagc])==0) // NOTE: tags MUST be in uppercase
				break;
			tagc++;
		 }
		 *endp = stor;
		 if ((IXMLTags[tagc]==NULL) && (macro->al_Node.mln_Succ==NULL))
		 {
			// unknown tag, skip this
			dp = SkipToEOT(dp);
			break;
		 }

		 dp = endp; // just after tag name
		 tagend = SkipToEOT(dp); /* where's the tag end? */

		 //--------------------------------------------------------
		 if (macro->al_Node.mln_Succ) // we have to replace this tag
		 {
			LONG nsize,mc,dist,asize;
			STRPTR newstr;
			
			// build new tag string
			// find out the approx. size of new string
			nsize = strlen(macro->al_Real);
			nsize += (tagend-olddp+1);
			
			if (NULL==(newstr = AllocPooled(MemPool,nsize)))
				goto ERROR;
						
			// build the argument list
			mc = 0;
			do
			{
			  dp = ExtractArg(dp, &macroargname[mc], &macroargval[mc], &moreargs);
			  mc++;
			} while(moreargs && (mc<25));
			
			// build the new string / insert args
			tdp = macro->al_Real;
			cc = 0;
			while(*tdp)
			{
				if (*tdp == '$')
				{	
					STRPTR ep;
					LONG argnum;
					
					tdp++;
					// get arg number
					ep = tdp;
					while(isdigit(*ep)) ep++;
					stor = *ep;
					*ep = '\0';
					argnum = atol(tdp);
					*ep = stor;
					tdp = ep;
					if (argnum <= mc)
						if (macroargval[argnum-1]) // insert this arg into newstr
						{
							strcpy(&newstr[cc],macroargval[argnum-1]);
							cc += strlen(&newstr[cc]); 
						}
				} // end if $
				else
				{
					newstr[cc] = *tdp;
					cc++; tdp++;
				}
			} // end while *tdp
			
			// we got the new string
			// replace old tag with this string
			
			// what is the size of new string
			asize = strlen(newstr);
			// substract the size of old tag
			asize -= (tagend-olddp+1);
			// new size of chunk buffer
			asize = ci->ci_DataLen + 1 + asize;
			// tag distance from the beginning of chunk
			dist = olddp - ci->ci_Data;
			// remove the old tag
			strcpy(olddp,tagend+1);		
			// reallocate the chunk buffer
			ci->ci_Data = Realloc(ci->ci_Data, asize, ci->ci_DataLen+1);
			ci->ci_DataLen = asize - 1; // new size
			// update pointers
			olddp = dp = ci->ci_Data + dist;
			// insert the new string
			strins(dp, newstr);
			
			FreePooled(MemPool,newstr,nsize);
			break; // try again
		 } // end if macro
		 //------------------------------------------------------
		
		 // this is not macro tag
		 eID(ec) = ET_TAG;
		 ePOS(ec) = tagc+1;	// tag ID
		 eFLG(ec) = 0;
		 if (neg) eFLG(ec) |= EF_ENDBLOCK;
		 ec++;ecc++;
		 if (!CheckMemory(ci,&ecc,ec,ParserECS)) goto ERROR;

		 // extract arguments
		 do
		 {
			dp = ExtractArg(dp, &argname, &argval, &moreargs);
		 	if (argname)
			{
				for(tdp=argname;*tdp;tdp++) *tdp = toupper(*tdp);
				// argument ID
				attrc = 0;
				while(IXMLAttrs[attrc])
				{
					if (strcmp(argname,IXMLAttrs[attrc])==0)
						break;	// arg name found
					attrc++;
				}
				if (IXMLAttrs[attrc])
				{
					eID(ec) = ET_ATTR;
					eFLG(ec) = 0;
					eLEN(ec) = attrc+1;	// this is arg. ID
					if (argval)
					{
						// check if it is a number
						tdp = argval;
						while(isdigit(*tdp)) tdp++;
						if ((*tdp=='\0') || ((*tdp=='%') && (tdp[1]=='\0')))
						{ // this is number
							eFLG(ec) |= EF_NUMBER;
							if (*tdp=='%')
							{
								*tdp = '\0';
								eFLG(ec) |= EF_PERCENT;
							}
							ePOS(ec) = atol(argval);
						}
						else
						{ // string
							ePOS(ec) = argval - ci->ci_Data;
							eFLG(ec) = EF_STRING;
						}
					}
					else eFLG(ec) |= EF_NOVALUE;
					
					ec++;ecc++;
		 			if (!CheckMemory(ci,&ecc,ec,ParserECS)) goto ERROR;
				}
			} // end if argname
		 } while(moreargs);
		
		 eID(ec) = ET_ENDTAG;
		 ec++;ecc++;
		 if (!CheckMemory(ci,&ecc,ec,ParserECS)) goto ERROR;

		 dp = tagend+1; // position after tag
		break;
       	case '\n':	// new line
		 chrproc=0;
		 eID(ec) = ET_RTRN;
		 *dp = ' ';
		 dp++;
		 ec++;ecc++;
		 if (!CheckMemory(ci,&ecc,ec,ParserECS)) goto ERROR;
		break;
		case ' ':		// blank
		 eID(ec) = ET_SPC;
		 eLEN(ec) = 0;
		 eFLG(ec) = 0;
		 while(*dp && (*dp==' '))
		 {
			dp++;
			eLEN(ec)++;
			chrproc++;
		 }
		 ec++;ecc++;
		 if (!CheckMemory(ci,&ecc,ec,ParserECS)) goto ERROR;
		break;
		case 9:		// tab
		 eID(ec) = ET_SPC;
           eLEN(ec) = NextMultipleDiff(tabsize,chrproc);
		 eFLG(ec) = EF_TAB;
		 dp++;
		 chrproc += eLEN(ec);
		 ec++;ecc++;
           if (!CheckMemory(ci,&ecc,ec,ParserECS)) goto ERROR;
		break;
		default:		// word
		 eID(ec) = ET_WORD;
		 ePOS(ec) = dp - ci->ci_Data;
		 eLEN(ec) = 0;
		 while(*dp)
		 {
			if (*dp=='<') break;
			if (*dp==' ') {dp++;chrproc++;eLEN(ec)++;break;}
			if (*dp=='\n') break;
			if (*dp==9) break;
			dp++;chrproc++;eLEN(ec)++;
		 }
		 ec++;ecc++;
		 if (!CheckMemory(ci,&ecc,ec,ParserECS)) goto ERROR;
          break;
	} // switch *dp
	
	// replace chars 1-8
	while(olddp<dp)
	{
		if (*olddp<8) // this should be replaced
			switch(*olddp)
			{
				case 1:
				 *olddp = '{';
				break;
				case 2:
				 *olddp = '}';
				break;
				case 3:
				 *olddp = '.';
				break;
				case 4:
				 *olddp = '&';
				break;
				case 5:
				 *olddp = '"';
				break;
				case 6:
				 *olddp = '<';
				break;
				case 7:
				 *olddp = '>';
				break;
				case 8:
				 *olddp = '\'';
				break;
			}
		olddp++;
	}
	if (*dp=='>') dp++;
 } // while *dp

 eID(ec) = ET_END;
 ci->ci_EntryNum = ec;

 DeletePool(MemPool);

 return ci;

ERROR: 
 if (MemPool) DeletePool(MemPool);
 // free chunk info
 FreeChunkInfo(ci);

 return NULL;
}

//---------------------------------------------------------
//	DTF_FreeChunk
//---------------------------------------------------------
void __saveds __asm DTF_FreeChunk(register __a0 struct iX_ChunkInfo *ci)
{
 FreeChunkInfo(ci);
}

