Navigation
Home
gpl
sqlmeta
v1.1b
metaparser.cpp








































metaparser.cpp
   
   /*
    * MetaParser.cpp
    * Part of SQLMeta, a language to use sql-queries in html pages.
    *
    * Copyright (C) 2001  Daan Vreeken
    *
    * This program is free software; you can redistribute it and/or
    * modify it under the terms of the GNU General Public License
    * as published by the Free Software Foundation; either version 2
    * of the License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    * GNU General Public License for more details.
    *
    * You should have received a copy of the GNU General Public License
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    *
    */
   
   #include <string.h>
   
   #include <new>
   #include <CgiDefs.h>
   #include <Cgicc.h>
   #include <HTMLClasses.h>
   
   
   
   #include "sqlmeta.h"
   
   
   
   
   MetaParser::MetaParser(SQLMeta *MyParent)
   {
   	Parent=MyParent;
   
   	DB=NULL;
   	DBIsMine=1;
   	Query=NULL;
   
   	LineCount=1;
   	FileName=NULL;
   	NestCount=0;
   }
   
   
   
   int MetaParser::Parse(void)
   {
   	char		Buf[1000];
   	int		Readed = 0;
   	int		Pos = 0;
   	int		LastMetaPos = -1;
   
   	const char	*MetaMarker = "<sql ";
   	const int	MarkerLen = strlen(MetaMarker);
   	int		MarkerPos = 0;
   
   	char		LineBuf[MaxLineLen+1];
   	int		LineLen = 0;
   	int		Parseing = 0;
   
   	int		NonMetaBytes;
   	int		Quoted = 0;
   	int		FirstLine = 1;
   	int		Result;
   
   	MyCache=(Cache *)new Cache(this);
   	if (MyCache==NULL)
   	{
   		Error(ErrMalloc,"MetaParser::Parse");
   		exit(0);
   	}
   
   	do
   	{
   		/// Read data (if we are out of it)
   		//
   		if (Pos==Readed)
   		{
   			if (!FirstLine)
   				Readed=fread(Buf,1,sizeof(Buf),Input);
   			else
   			{
   				Readed=fread(Buf,1,2,Input);
   				if ((Readed==2) && (Buf[0]=='#') && (Buf[1]=='!'))
   				{
   					//Don't forget this one in the linecounter
   					LineCount++;
   
   					//Skip all until we see a '\n'
   					while ((Readed>0) && (Buf[0]!='\n'))
   						Readed=fread(Buf,1,1,Input);
   
   					//Fill the buffer
   					Readed=fread(Buf,1,sizeof(Buf),Input);
   				}
   				FirstLine=0;
   			}
   
   			LastMetaPos=-1;
   			Pos=0;
   		}
   
   		/// Search for meta tags ('<sql ')
   		//
   		while ((Pos<Readed) && (!Parseing))
   		{
   			//Just to keep the linecounter in line
   			if (Buf[Pos]=='\n') LineCount++;
   
   			//Search for a match of the MetaMarker
   			// '<sql '
   			//
   			if (Buf[Pos]==MetaMarker[MarkerPos])
   				MarkerPos++;
   			else
   				if (MarkerPos>0)
   				{
   					//Whoops.. we swallowed a peace of a non-meta
   					//header.. (if it was '<html>', we now have
   					//swallowed '<'
   
   					//Check if it is still in our bufffer,
   					//we could have dumped the rest if
   					//the tag started on the end of the
   					//last buffer...
   					if (Pos<=MarkerPos)
   					{
   						//It's gone for good...
   						//Now we have to cut it in
   						//two different parts:
   						//1. tag part we lost
   						//2. everything after that
   
   						//Spit out the piece of tag we swallowed
   						////fwrite(MetaMarker,MarkerPos,1,Parent->OutputStream);
   						if (!MyCache->Feed(HTML,(char *)MetaMarker,MarkerPos))
   							return 0;
   
   						//Make out spit-routine spit
   						//out all from here on
   						LastMetaPos=Pos-1;
   					}
   					//Reset search marker
   					MarkerPos=0;
   				}
   
   			if (MarkerPos==MarkerLen)
   			{
   				//We found one, let's start parseing...
   				//
   				Parseing=1;
   				MarkerPos=0;
   				Quoted=0;
   
   				//Don't forget to spit out the non-meta text
   				//before the tag
   				//(if there is any)
   				if (Pos>MarkerLen)
   				{
   					NonMetaBytes=Pos-LastMetaPos;
   					NonMetaBytes-=MarkerLen;
   
   					if (NonMetaBytes>0)
   						////fwrite(&Buf[LastMetaPos+1],NonMetaBytes,1,Parent->OutputStream);
   						if (!MyCache->Feed(HTML,&Buf[LastMetaPos+1],NonMetaBytes))
   							return 0;
   					LastMetaPos=Pos;
   				}
   			}
   
   			Pos++;
   		}
   
   		//If we are here because we rolled over the edge of the buffer,
   		//we still have some non-meta text in our buffer.
   		//Let's spit it out
   		//
   		if ((Pos==Readed) && (!Parseing))
   		{
   			NonMetaBytes=Pos-LastMetaPos-1;
   			NonMetaBytes-=MarkerPos;
   
   			if (NonMetaBytes>0)
   				////fwrite(&Buf[LastMetaPos+1],NonMetaBytes,1,Parent->OutputStream);
   				if (!MyCache->Feed(HTML,&Buf[LastMetaPos+1],NonMetaBytes))
   					return 0;
   		}
   
   		/// Parse meta block
   		//
   		while ((Pos<Readed) && (Parseing))
   		{
   			switch (Buf[Pos])
   			{
   			case '>':
   				//Found a tag-end marker, are we in quoted text?
   				if (!Quoted)
   				{
   					//Real tag ending is here
   					//stop parseing...
   					//
   					Parseing=0;
   					LastMetaPos=Pos;
   				}
   				/* FALLTHROUGH */
   
   			case '\n':
   			case ';':
   				if (Buf[Pos]=='\n') LineCount++;
   
   				//End of command found, unless we're quoted.
   				if (!Quoted)
   				{
   					//Command really did end here...
   					//
   					LineBuf[LineLen]=0;
   					////ExecCommand(LineBuf);
   					if (!MyCache->Feed(Code,LineBuf,0))
   						return 0;
   					LineLen=0;
   
   					break;
   				}
   				/* FALLTHROUGH */
   
   			case '"':
   				if (Buf[Pos]=='"')
   					Quoted=!Quoted;
   				/* FALLTHROUGH */
   
   			default:
   				if (LineLen<sizeof(LineBuf)-1)
   				{
   					LineBuf[LineLen++]=Buf[Pos];
   				}
   				else
   					return Error(ErrLineTooLong);
   			}
   			Pos++;
   		}
   
   		/// Repeat...
   
   	} while (Readed>0);
   
   
   	//Flush cache and get out of here
   	Result=MyCache->Flush();
   	delete MyCache;
   	MyCache=NULL;
   
   	return Result;
   }
   
   
   
   int MetaParser::PreProcess(char *Line)
   {
   	char		*Temp;
   	int		Len;
   	char		*Pos;
   	char		*NextPos;
   	char		*VarName;
   	char		*RestPos;
   	char		*Var;
   	int		IsEnvVar;
   	int		Nr;
   
   	//Create a copy of the line
   	Len=strlen(Line);
   	Temp=(char *)malloc(MaxLineLen);
   	if (Temp==NULL)
   		return Error(ErrMalloc,"MetaParser::PreProcess");
   
   	while ((Pos=strstr(Line,"${"))!=NULL)
   	{
   		IsEnvVar=1;
   
   		//Search for the last occurance of '${'
   		NextPos=Pos+1;
   		while ((NextPos=strstr(NextPos,"${"))!=NULL)
   			Pos=NextPos++;
   
   		//Make the trailing line null-terminated
   		*Pos=0;
   
   		VarName=Pos+2;
   		RestPos=index(VarName,'}');
   
   		//Check variable termination
   		if (RestPos==NULL)
   			return Error(ErrVarUnterminated);
   
   		*RestPos++=0;
   
   		//Does it start with 'col'?
   		if (strstr(VarName,"col")==VarName)
   		{
   			//Don't substitute ${cola}, just ${col1} etc..
   			if (sscanf(VarName+3,"%d",&Nr))
   			{
   				Nr--;
   
   				//It's a column out of a query...
   				IsEnvVar=0;
   
   				if (Query==NULL)
   					return Error(ErrVarSubst,VarName,"don't have a query!");
   				if (Query->Result==NULL)
   					return Error(ErrVarSubst,VarName,"don't have query results");
   				if (Nr<0)
   					return Error(ErrVarSubst,VarName,"column number too small");
   				if (Nr>=Query->Fields)
   					return Error(ErrVarSubst,VarName,"column number too big");
   
   				Var=Query->Row[Nr];
   			}
   		}
   
   		//Does it start with 'form_'?
   		if (strstr(VarName,"form_")==VarName)
   		{
   			Cgicc			*Cgi = Parent->Cgi;
   			const_form_iterator	Iter;
   
   			//It's a field out of a form
   			IsEnvVar=0;
   
   			if (Cgi==NULL)
   				return Error(ErrVarSubst,VarName,"No cgi query set");
   
   			Iter=Cgi->getElement(VarName+5);
   			if ((Iter==Cgi->getElements().end()) || (Iter->isEmpty()))
   				return Error(ErrVarSubst,VarName,"Variable not found in form data");
   
   			Var=(char *)Iter->getValue().data();
   		}
   
   		if (strstr(VarName,"rownr")==VarName)
   		{
   			char	Temp[10];
   
   			//No environment string here...
   			IsEnvVar=0;
   
   			if (Query==NULL)
   				return Error(ErrVarSubst,VarName,"don't have a query!");
   			if (Query->Result==NULL)
   				return Error(ErrVarSubst,VarName,"don't have query results");
   
   			snprintf(Temp,sizeof(Temp),"%d",Query->RowNr);
   			Var=Temp;
   		}
   
   		if (IsEnvVar)
   		{
   			//It's a environment variable (since there was no
   			//other match)
   			//Try to fetch the environment variable
   			Var=getenv(VarName);
   			if (Var==NULL)
   				return Error(ErrEnvNotSet,VarName);
   		}
   
   		//Substitute the variable into our code line...		
   		snprintf(Temp,MaxLineLen+1,"%s%s%s",Line,Var,RestPos);
   		strcpy(Line,Temp);
   	}
   
   	free(Temp);
   }
   
   
   
   int MetaParser::ExecCommand(char *Line)
   {
   	int		Args = 0;
   	int		Cnt;
   
   	//Pre-process line...
   	if (!PreProcess(Line))
   		return 0;
   
   	//Split it into arguments
   	Args=SplitArgs(Line,Arg);
   	if (Args==-1)
   		return 0;
   
   	//Skip empty commands
   	if (Args==0)
   		return 1;
   
   	if (!strcmp(Arg[0],"include"))
   	{
   		//Parse include file...
   		//
   		if (Args!=2)
   			return Error(ErrArgNr,"include [filename]");
   		if (NestCount==MaxNesting)
   		{
   			//stop right now...
   			Error(ErrNesting);
   			exit(0);
   		}
   
   		//Open the include file
   		FILE		*IncludeFile = fopen(Arg[1],"r");
   		if (IncludeFile==NULL)
   			return Error(ErrOpenFile,Arg[1]);
   
   		//Create a new parser
   		MetaParser	*Temp = new MetaParser(Parent);
   		if (Temp==NULL)
   			return Error("Could not create MetaParser!");
   
   		//Set some vars in the parser
   		Temp->Input=IncludeFile;
   		Temp->NestCount=NestCount+1;
   		Temp->FileName=Arg[1];
   		Temp->DB=DB;
   		Temp->DBIsMine=0;
   		//Parse the include file now...
   		Temp->Parse();
   
   		//Close wat needs to be closed
   		delete Temp;
   		fclose(IncludeFile);
   
   		//Done
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"print"))
   	{
   		//Print argument 2
   		//
   		if (Args!=2)
   			return Error(ErrArgNr,"print [text]");
   
   		fwrite(Arg[1],strlen(Arg[1]),1,Parent->OutputStream);
   
   		//Done
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"host"))
   	{
   		//Set SQL host
   		//
   		if (Args!=2)
   			return Error(ErrArgNr,"host [hostname]");
   
   		return DB->SetHost(Arg[1]);
   	}
   
   	if (!strcmp(Arg[0],"user"))
   	{
   		//Set SQL username and password
   		//
   		if (Args!=3)
   			return Error(ErrArgNr,"user [username] [password]");
   
   		return DB->SetUser(Arg[1],Arg[2]);
   	}
   
   	if (!strcmp(Arg[0],"db"))
   	{
   		//Set SQL host
   		//
   		if (Args!=2)
   			return Error(ErrArgNr,"db [database]");
   
   		return DB->SetDB(Arg[1]);
   	}
   
   	if (!strcmp(Arg[0],"info"))
   	{
   		//Spit out some info
   		//
   		Print(Version);
   
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"set"))
   	{
   		//Store or remove a environment string
   		//
   		if ((Args!=2) && (Args!=3))
   			return Error(ErrArgNr,"set [varname] {value}");
   
   		if (Args==2)
   		{
   			//Remove var
   			unsetenv(Arg[1]);
   		}
   		else
   		{
   			//Set variable
   			if (setenv(Arg[1],Arg[2],1))
   				return Error(ErrEnvCouldNotSet);
   		}
   
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"query"))
   	{
   		DBConnection	*Conn;
   
   		//Fire a query at the server, without using the results
   		//
   		if (Args!=2)
   			return Error(ErrArgNr,"query [sql query]");
   
   		//Create a connection to the database
   		Conn=(DBConnection *)new DBConnection(this,DB);
   		if (Conn==NULL)
   			return Error(ErrMalloc,"MetaParser::ExecCommand");
   
   		if (!Conn->TryConnect())
   			return 0;
   
   		if (!Conn->Query(Arg[1]))
   			return 0;
   
   		//DUMMY: GetResult
   		if (!Conn->GetResult())
   			return 0;
   
   		//And flush those results...
   		Conn->FreeResult();
   
   		delete Conn;
   
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"table"))
   	{
   		DBConnection	*Conn;
   
   		//Create a table of results from a query
   		//
   		if (Args!=2)
   			return Error(ErrArgNr,"table [sql query]");
   
   		//Create a connection to the database
   		Conn=(DBConnection *)new DBConnection(this,DB);
   		if (Conn==NULL)
   			return Error(ErrMalloc,"MetaParser::ExecCommand");
   
   		if (!Conn->TryConnect())
   			return 0;
   
   		if (!Conn->Query(Arg[1]))
   			return 0;
   
   		if (!Conn->GetResult())
   			return 0;
   
   		//Spit out header
   		Print("<table bgcolor=\"#e0e0e0\" border=1>\n");
   
   		//Table title
   		printf(" <tr bgcolor=\"#c0c0c0\">");
   		for (Cnt=0; Cnt<Conn->Fields; Cnt++)
   			printf("<td>%s</td>",Conn->FieldName[Cnt]);
   		printf("</tr>\n");
   
   		//Table data
   		while (Conn->NextRow())
   		{
   			printf(" <tr>");
   			for (Cnt=0; Cnt<Conn->Fields; Cnt++)
   				printf("<td>%s</td>",Conn->Row[Cnt]);
   			printf("</tr>\n");
   		}
   
   		//Table footer
   		printf("</table>\n");
   
   		Conn->FreeResult();
   
   		delete Conn;
   
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"with"))
   	{
   		LoopWith	*Loop;
   		int		Result;
   
   		if (Args!=2)
   			return Error(ErrArgNr,"with [sql query]; ...; loop");
   
   		Loop=(LoopWith *)new LoopWith(this,MyCache,&Result,Arg[1]);
   		if (Loop==NULL)
   			return Error(ErrMalloc,"MetaParser::ExecCommand");
   		if (!Result)
   			return 0;
   
   		//Add the loop to the loop cache
   		MyCache->AddLoop(Loop);
   
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"for"))
   	{
   		LoopFor		*Loop;
   		int		Result;
   
   		if ((Args!=4) || (strcmp(Arg[2],"in")))
   			return Error(ErrArgNr,"for [variable] in \"for array.. foo etc\"");
   
   		Loop=(LoopFor *)new LoopFor(this,MyCache,&Result,Arg[1],Arg[3]);
   		if (Loop==NULL)
   			return Error(ErrMalloc,"MetaParser::ExecCommand");
   		if (!Result)
   			return 0;
   
   		//Add the loop to the loop cache
   		MyCache->AddLoop(Loop);
   
   		return 1;
   	}
   
   	if (!strcmp(Arg[0],"loop"))
   	{
   		return Error(ErrLoop,"loop","with");
   	}
   	if (!strcmp(Arg[0],"next"))
   	{
   		return Error(ErrLoop,"next","for");
   	}
   
   	Error("Unknown command '%s'!",Arg[0]);
   	return 0;
   }
   
   
   
   void MetaParser::SpitoutHTML(char *Data, int Size)
   {
   	fwrite(Data,Size,1,Parent->OutputStream);
   }
   
   
   
   int MetaParser::SplitArgs(char *Cmd, ArgPtr *Dst)
   {
   	int		Args = 0;
   	char		*Ptr = Cmd;
   	int		InSpace = 1;
   	int		Quoted = 0;
   
   	while (*Ptr)
   	{
   		switch (*Ptr)
   		{
   		case 0:
   			//Turn 0 bytes into space
   			*Ptr=' ';
   			/* FALLTHROUGH */
   
   		case ' ':
   		case '\t':
   			if (!Quoted)
   			{
   				//Turn space into null-terminator of last
   				//argument
   				*Ptr=0;
   				InSpace=1;
   			}
   			break;
   
   		case '"':
   			Quoted=!Quoted;
   			if (!Quoted)
   				//Remove trailing '"'
   				*Ptr=0;
   			break;
   
   		default:
   			if (InSpace)
   			{
   				if (Args==MaxArgs)
   				{
   					Error("Too many arguments!");
   					return -1;
   				}
   
   				Dst[Args]=Ptr;
   				Args++;
   				InSpace=0;
   			}
   		}
   
   		Ptr++;
   	}
   
   	return Args;
   }
   
   
   
   int MetaParser::Error(char *Str, ...)
   {
   	va_list		List;
   
   	if (FileName)
   		fprintf(Parent->ErrorStream,"sqlmeta: Error in file [%s] at line %d:\n",FileName,LineCount);
   	else
   		fprintf(Parent->ErrorStream,"sqlmeta: Error at line %d:\n",LineCount);
   
   	va_start(List,Str);
   	vfprintf(Parent->ErrorStream,Str,List);
   	va_end(List);
   
   	fprintf(Parent->ErrorStream,"\n");
   	return 0;
   }
   
   
   
   void MetaParser::Print(char *Str, ...)
   {
   	va_list		List;
   
   	va_start(List,Str);
   	vfprintf(Parent->OutputStream,Str,List);
   	va_end(List);
   }
   
   
   
   MetaParser::~MetaParser(void)
   {
   	if ((DBIsMine) && (DB!=NULL))
   	{
   		delete DB;
   	}
   
   	if (MyCache!=NULL)
   		delete MyCache;
   }
   
   
   
   
   

syntax highlighted by Code2HTML, v. 0.9.1


Email me with questions/comments : Daan <Daan @ pa4dan . nl>