/* * 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 #include #include #include #include #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 = "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 ('0) { //Whoops.. we swallowed a peace of a non-meta //header.. (if it was '', 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': //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 (LineLen0); //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
\n",Version); Print("%s
\n",Author); Print("
\n"); Print("%s
\n",Copyright); Print("%s
\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("\n"); //Table title printf(" "); for (Cnt=0; CntFields; Cnt++) printf("",Conn->FieldName[Cnt]); printf("\n"); //Table data while (Conn->NextRow()) { printf(" "); for (Cnt=0; CntFields; Cnt++) printf("",Conn->Row[Cnt]); printf("\n"); } //Table footer printf("
%s
%s
\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; }