![]() |
![]() |
||||||
![]() |
|||||||
|
/*
* 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"
const char Version[] = "SQLMeta meta parser version 1.1.2b";
const char Author[] = "Written by Daan Vreeken";
const char Copyright[] = "Copyright (C) 2001 Daan Vreeken";
const char License[] = "Published under the terms of the GNU GPL";
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;
int SeenCode = 0;
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 our 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 (LineLen>0)
if (!MyCache->Feed(Code,LineBuf,0))
return 0;
LineLen=0;
SeenCode=0;
break;
}
/* FALLTHROUGH */
case '"':
if (Buf[Pos]=='"')
Quoted=!Quoted;
/* FALLTHROUGH */
case ' ':
case '\t':
if (!SeenCode)
break;
/* FALLTHROUGH */
default:
if (LineLen<sizeof(LineBuf)-1)
{
SeenCode=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);
}
while ((Pos=strstr(Line,"\\n"))!=NULL)
{
//Make the trailing line null-terminated
*Pos=0;
RestPos=Pos+2;
//Substitute the variable into our code line...
snprintf(Temp,MaxLineLen+1,"%s\n%s",Line,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 what 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("%s<br>\n",Version);
Print("%s<br>\n",Author);
Print("<br>\n");
Print("%s<br>\n",Copyright);
Print("%s<br>\n",License);
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;
//DUMP the Result
if (!Conn->DumpResult())
return 0;
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 '"':
Quoted=!Quoted;
if (!Quoted)
{
//Remove trailing '"'
*Ptr=0;
InSpace=1;
}
break;
case ' ':
case '\t':
if (!Quoted)
{
//Turn space into null-terminator of last
//argument
*Ptr=0;
InSpace=1;
break;
}
/* FALLTHROUGH */
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 <Danovitsch @ Vitsch . net> |