/*
 * This code is part of Screws project.
 * Copylefted by pancake@phreaker.net at 2003
 */

#include "Exec.h"

/*
 * Needs a '\0' to be present at the end of the buffer, or
 * strstr is pontentially going to give a segfault
 */
void
execHsml(b,len)
    char **b; /* DoublePtr for reallocs */
	long len;
{
    group_list grps;
    char *fpos = *b, 
    	 *fend = *b+len, 
	 *bstrt= 0,
	 *bend = 0, 
	 *faux;
	long diff; /* FUXOR CODE */
    int  sz,block=0;
    bool noecho=false, res;

    /* First scan number of code blocks, as use of realloc needs grps.[l]gr or
     * grps.cb to be allocated once, as they cross reference themselves
     * (will allocate grps.cb just once)
     */
    faux=fpos;
    while (faux < fend)
    {
		faux=strstr(faux,"<#");
		if (!faux) break; /* no <# found */
		if (	(faux-fpos>=1 && *(faux-1)!='\\') || 
			(faux-fpos>=2 && *(faux-2)=='\\')  ) { block++; }
			faux++; /* XXX enhace speed ? */
    }

    if (groupingCreate(&grps,block)<0) {
	printf("Internal error while executing script (groupingCreate).");
	return;
    }

    block=0;
    
    /* Parse all blocks and group them */
    fend[0]=0;
    do
    {
	bstrt=strchr(fpos,'<');
	if (!bstrt)             /* no more tags */
	{
		fpos=fend;
		break;
	}
	switch(bstrt[1])
	{
	case '#': /* HSMLv1 tags ( External Execution ) */
		if ( bstrt!=fpos && 
		    ( (bstrt-fpos>=1 && *(bstrt-1)!='\\') || 
		      (bstrt-fpos>=2 && *(bstrt-2)=='\\')) ) { /* no escaping */
		    block++;
		    faux=bstrt+1;
		    do {
				bend=strstr(faux,"#>");
				if (!bend) 
					{ /* missing close tag */
				    printf("Missing close tag on block %d.",block);
				    groupingDelete(&grps);
				    return;
					}
				if (*(bend-1)!='\\' || *(bend-2)=='\\') 
				{
				    /* got start/end tags, now check format */
			   	 bend++;
			   	 if (checkBlock(&grps,bstrt,bend,block)<0) 
					{
						groupingDelete(&grps);
						return;
			    	}
				} else { /* escape it */
			    faux=bend+1;
			    bend=0;
				}
		    } while (bend==0);
		    fpos=bend+1;
		}
		break;
	case '*': /* HSMLv2 tag (XML programming) */
		/* TODO Must be unified using XmlProg.c */
		if (!memcmp(fpos+2,"comment",7))
		{
			faux=strstr(fpos+10,"</*comment>");
			if (!faux) {
				printf("*Comment tag not closed\n");
				return;
			}
			faux+=10;
			strcpy(fpos,faux+1);
			fend-=strlen(faux+1);
		} else
		if (!memcmp(fpos+2,"include",7))
		{
			char *files,*f,*f2;
			long fsize;
			FILE *fd;

			faux=strstr(fpos,"/>");
			if (!faux) {
				printf("*include tag not closed\n");
				return;
			}

			/* clear include tag */
			faux[0]=0; files=strdup(fpos+10); faux[0]='/';

			/* append */
			strcpy(fpos,faux+2);
			fend-=(fpos-faux);

			/* include files */
			f=files;
			while(1) 
			{
				f=strstr(f,"file=\"");
				if (!f) break;
				f=f+6;
				f2=strchr(f,'\"');
				if (!f2)
				{
						printf("Quote not closed in include.\n");
						exit(1); /* XXX continue? */
				}
				f2[0]=0;
				fd=fopen(f,"r");
				if (!fd)
				{
						printf("Cannot open include file: '%s'\n",f);
						exit(1);
				}
				f2[0]='\"';

				/* get file size and append */
				fseek(fd,0,SEEK_END); fsize=ftell(fd); fseek(fd,0,SEEK_SET);
				diff=(long)*b;
				//fend=*b; diff=fpos-*b; /* safe ptr */
				*b=(char *)realloc(*b,fsize+len+1);
				diff=(long)
					 *b-diff;
				fpos=*b+diff;
				fend=*b+diff;
				faux=*b+diff;
				//fpos=*b+diff; fend=*b+diff; /* safe ptr */
				memcpy(fpos+fsize,fpos,fsize);
				fread(fpos,fsize,1,fd);
				fend[0]=0;
				fclose(fd);
			}
			free(files);
		}
		break;
	default:
		//fpos++;
//		fpos=strchr(fpos+1,'>');
//		if (!fpos)fpos=fend;
		//fpos=fpos+2; /* XXX XXX THIS IS VERY UGLY AND SLOWLY!!!!!!!!!!! */
		break;
		}
	fpos++;
    } while(fpos+5 <fend);

    /* Parse OK, now echo text and execute blocks */
    fpos=*b;
    faux=blockStrt(groupingBlockFirst(&grps));
    while(fpos<fend)
    {
	if (noecho == false)
   	{
		if (!faux) 	sz=strlen(fpos);
		else 		sz=faux-fpos;

	    if (write(1,fpos,sz)<0) 
		{
			printf("Internal error while executing script (write).");
			groupingDelete(&grps);
			return;
	    }
	    if (!faux) break;
	}
	if (faux != NULL) 
	{
	    res=parsetoLang((_cblock_t*)groupingBlockCurrent(&grps),noecho);
	    if (res!=ignore)
			noecho=res;
	    fpos=blockEnd(groupingBlockCurrent(&grps))+1;
	    faux=blockStrt(groupingBlockNext(&grps));
	} 
	if (faux == NULL) 
		{
		if (res) /* XXX It works OK. But could fail */
			break;
	    if (noecho == false) 
		{
		if (write(1,fpos,fend-fpos)<0) 
			{
		    printf("Internal error while executing script (write).");
		    groupingDelete(&grps);
		    return;
			}
	    }
	    fpos=fend;
		}
    } 

    groupingDelete(&grps);
}

/*
 * Check format of a code block and inserts it on the
 * group list
 * Return 0 if all goes OK, <0 otherwise
 */
int
checkBlock (grps, bstrt, bend, block)
    group_list *grps; /* Groups                    */
    char *bstrt;      /* Starting address of block */
    char *bend;       /* Ending address of block   */
    int block;        /* Current block number      */
{
    char *cstrt, *cend;
    char *lang, *subgrp;
    bool exitv, exec;

    lang=bstrt+2;
    cend=bend-2;
    cstrt=searchCh(' ',lang,cend);
    if (!cstrt) 
	{
		printf("Missing space on open tag %d.",block);
		return -1;
    }
    *cstrt='\0';
    subgrp=searchCh('+',lang,cstrt);
    if (!subgrp) 
	{
		subgrp=cstrt;
		exec=true;
    }
    else {
		*subgrp='\0';
		subgrp++;
		if (*(cstrt-1)=='.') 
		{
		    exec=true;
		    *(cstrt-1)='\0';
		} else exec=false;
    }
    cstrt++;
    if (*cend != ' ')
   	{
	  switch (*cend) 
	  {
	    case '\n':
	    case ' ':
	    case '\t':
	    case '?':
				exitv=ignore;
				break;
	    case '0':
				exitv=false;
				break;
	    case '1':
				exitv=true;
				break;
	    default:
				exitv=ignore;
				break;
	  }
	  if (*(cend-1)!=' '&&*(cend)!='\t'&&*(cend)!='\n') {
	    printf("Missing space on close tag %d (%c).",block,*(cend));
	    return -3;
	  }
		cend--;
    } else {
		exitv=script;
	}

    if (blockNew(grps,bstrt,bend,cstrt,cend,lang,subgrp,exec,exitv)<0) 
	{
		printf("Internal error while executing script (newBlock).");
		return -4;
    }
    return 0;
}

/*
 * Searches a character on a string between two addresses
 * Returns 0 if not found, its address otherwise
 */
char*
searchCh (c, s, e)
    char c;  /* Charater to search                  */
    char *s; /* Srtart address (included on search) */
    char *e; /* End address (included on search)    */
{
    while (*s!=c && s<=e)
	s++;
    if (s>e) s=0;
    return s;
}

