/* This file is part of mhmake.
*
* Copyright (C) 2001-2009 Marc Haesen
*
* Mhmake 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 3 of the License, or
* (at your option) any later version.
*
* Mhmake 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 Mhmake. If not, see .
*/
/* $Rev$ */
#include "stdafx.h"
#include "util.h"
#include "mhmakefileparser.h"
#include "rule.h"
static const string s_QuoteString("\"");
funcdef mhmakefileparser::m_FunctionsDef[]= {
{"filter", &mhmakefileparser::f_filter}
,{"call", &mhmakefileparser::f_call}
,{"subst", &mhmakefileparser::f_subst}
,{"patsubst", &mhmakefileparser::f_patsubst}
,{"concat", &mhmakefileparser::f_concat}
,{"if", &mhmakefileparser::f_if}
,{"findstring", &mhmakefileparser::f_findstring}
,{"firstword", &mhmakefileparser::f_firstword}
,{"wildcard", &mhmakefileparser::f_wildcard}
,{"basename", &mhmakefileparser::f_basename}
,{"notdir", &mhmakefileparser::f_notdir}
,{"dir", &mhmakefileparser::f_dir}
,{"shell", &mhmakefileparser::f_shell}
,{"relpath", &mhmakefileparser::f_relpath}
,{"toupper", &mhmakefileparser::f_toupper}
,{"tolower", &mhmakefileparser::f_tolower}
,{"exist", &mhmakefileparser::f_exist}
,{"filesindirs",&mhmakefileparser::f_filesindirs}
,{"fullname" ,&mhmakefileparser::f_fullname}
,{"addprefix" ,&mhmakefileparser::f_addprefix}
,{"addsuffix" ,&mhmakefileparser::f_addsuffix}
,{"filter-out" ,&mhmakefileparser::f_filterout}
,{"word" ,&mhmakefileparser::f_word}
,{"words" ,&mhmakefileparser::f_words}
,{"strip" ,&mhmakefileparser::f_strip}
,{"which" ,&mhmakefileparser::f_which}
};
map mhmakefileparser::m_Functions;
bool mhmakefileparser::m_FunctionsInitialised;
///////////////////////////////////////////////////////////////////////////////
// Loop over a list of filenames
static string IterList(const string &List,string (*iterFunc)(const string &FileName,void *pArg), void *pArg=NULL)
{
const char *pTmp=List.c_str();
string Ret=g_EmptyString;
bool first=true;
while (*pTmp)
{
if (!first)
{
Ret+=g_SpaceString;
}
else
{
first=false;
}
string Item;
pTmp=NextItem(pTmp,Item);
Item=iterFunc(Item,pArg);
if (Item.empty())
first=true; // Do not add space the next iteration
else
Ret+=Item;
}
return Ret;
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::InitFuncs(void)
{
for (int i=0; i=Input.size())
return g_EmptyString;
size_t Stop=Input.size()-1;
while (strchr(" \t",Input[Stop])) Stop--;
return Input.substr(Start,Stop-Start+1);
}
///////////////////////////////////////////////////////////////////////////////
static string filter(const string &FileName, void *pvFilter)
{
string *pFilter=(string*)pvFilter;
if (PercentMatchList(UnquoteFileName(FileName),*pFilter))
return FileName;
else
return g_EmptyString;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_filter(const string & Arg) const
{
size_t ipos=Arg.find(',');
#ifdef _DEBUG
if (ipos==string::npos) {
throw string("filter func should have 2 arguments: ")+Arg;
}
#endif
string Str=TrimString(Arg.substr(0,ipos));
string List=Arg.substr(ipos+1);
if (Str.empty())
return Str;
return IterList(List,filter,(void*)&Str);
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_filterout(const string & Arg) const
{
size_t ipos=Arg.find(',');
#ifdef _DEBUG
if (ipos==string::npos) {
throw string("filter func should have 2 arguments: ")+Arg;
}
#endif
string Str=TrimString(Arg.substr(0,ipos));
string List=Arg.substr(ipos+1);
if (Str.empty())
return Str;
bool First=true;
string Ret;
char *pTok=strtok((char*)List.c_str()," \t"); // doing this is changing string, so this is very dangerous
while (pTok)
{
string Item(pTok);
if (!PercentMatchList(Item,Str))
{
if (First)
{
Ret=Item;
First=false;
}
else
{
Ret+=g_SpaceString;
Ret+=Item;
}
}
pTok=strtok(NULL," \t");
}
return Ret;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_call(const string & Arg) const
{
const char *pTmp=Arg.c_str();
bool LastCharIsComma=Arg[Arg.length()-1]==',';
string Func;
pTmp=NextCharItem(pTmp,Func,',');
map::const_iterator pFunc=m_Variables.find(Func);
#ifdef _DEBUG
if (pFunc==m_Variables.end())
{
throw string("call to non existing function ")+Func;
}
#endif
Func=pFunc->second;
int i=0;
while (*pTmp || LastCharIsComma) {
if (!*pTmp)
LastCharIsComma=false; /* To stop the loop */
string Repl;
pTmp=NextCharItem(pTmp,Repl,',');
i++;
char Tmp[10];
::sprintf(Tmp,"$(%d)",i);
size_t Len=::strlen(Tmp);
size_t Pos=Func.find(Tmp);
while (Func.npos!=Pos)
{
Func=Func.substr(0,Pos)+Repl+Func.substr(Pos+Len);
Pos=Func.find(Tmp,Pos+Len);
}
}
return ExpandExpression(Func);
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_subst(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string FromString;
pTmp=NextCharItem(pTmp,FromString,',');
#ifdef _DEBUG
if (!*pTmp) {
throw string("Wrong number of arguments in function subst");
}
#endif
string ToString;
pTmp=NextCharItem(pTmp,ToString,',');
string Text;
NextCharItem(pTmp,Text,',');
if (FromString.empty())
return Text;
string Ret;
size_t Pos=Text.find(FromString);
size_t PrevPos=0;
while (Pos!=string::npos)
{
Ret+=Text.substr(PrevPos,Pos-PrevPos);
Ret+=ToString;
PrevPos=Pos+FromString.length();
Pos=Text.find(FromString,PrevPos);
}
Ret+=Text.substr(PrevPos);
return Ret;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_patsubst(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string FromString;
pTmp=NextCharItem(pTmp,FromString,',');
#ifdef _DEBUG
if (!*pTmp) {
throw string("Wrong number of arguments in function subst");
}
#endif
string ToString;
pTmp=NextCharItem(pTmp,ToString,',');
string Text;
NextCharItem(pTmp,Text,',');
if (FromString.empty())
return Text;
return Substitute(Text,FromString,ToString);
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_concat(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string JoinString;
pTmp=NextCharItem(pTmp,JoinString,',');
string List;
pTmp=NextCharItem(pTmp,List,',');
if (JoinString.empty() && List.empty())
{
/* assume as $(concat ,,items) construct */
JoinString=",";
pTmp=NextCharItem(pTmp,List,',');
}
bool First=true;
string Ret;
char *pTok=strtok((char*)List.c_str()," \t"); // doing this is changing string, so this is very dangerous
while (pTok)
{
if (First)
{
First=false;
}
else
{
Ret+=JoinString;
}
Ret+=pTok;
pTok=strtok(NULL," \t");
}
return Ret;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_if(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string Cond;
pTmp=NextCharItem(pTmp,Cond,',');
string Ret;
if (Cond.empty())
{
pTmp=NextCharItem(pTmp,Ret,',');
}
NextCharItem(pTmp,Ret,',');
return Ret;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_findstring(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string find;
pTmp=NextCharItem(pTmp,find,',');
string instr;
NextCharItem(pTmp,instr,',');
if (instr.find(find) != instr.npos)
return find;
else
return g_EmptyString;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_firstword(const string & Arg) const
{
string FirstWord;
NextItem(Arg.c_str(),FirstWord);
return FirstWord;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_wildcard(const string & Arg) const
{
refptr FileSpec=GetFileInfo(TrimString(Arg),m_MakeDir); /* Use GetFileInfo to make the relative path absolute */
refptr Dir=FileSpec->GetDir();
#ifdef WIN32
struct _finddata_t FileInfo;
intptr_t hFile=_findfirst(FileSpec->GetFullFileName().c_str(),&FileInfo);
if (hFile==-1)
return g_EmptyString;
string Ret=g_EmptyString;
/* We have to verify with percentmatch since the find functions *.ext also matches the functions *.extbrol */
string CheckSpec=FileSpec->GetName();
if (PercentMatch(FileInfo.name,CheckSpec,NULL,'*'))
{
Ret=GetFileInfo(FileInfo.name,Dir)->GetQuotedFullFileName();
}
while (-1!=_findnext(hFile,&FileInfo))
{
if (PercentMatch(FileInfo.name,CheckSpec,NULL,'*'))
{
Ret+=g_SpaceString;
Ret+=GetFileInfo(FileInfo.name,Dir)->GetQuotedFullFileName();
}
}
_findclose(hFile);
#else
glob_t Res;
if (glob (FileSpec.c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_MARK, NULL, &Res))
return g_EmptyString;
string Ret=g_EmptyString;
string SepStr=g_EmptyString;
for (int i=0; i pFile=GetFileInfo(File,m_MakeDir);
if (pFile->Exists())
{
return string("1");
}
else
return g_EmptyString;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_filesindirs(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string strFiles;
pTmp=NextCharItem(pTmp,strFiles,',');
#ifdef _DEBUG
if (!*pTmp) {
throw string("Wrong number of arguments in function filesindirs");
}
#endif
string strDirs;
NextCharItem(pTmp,strDirs,',');
vector< refptr > Dirs;
SplitToItems(strDirs,Dirs);
pTmp=strFiles.c_str();
string Ret=g_EmptyString;
bool first=true;
while (*pTmp)
{
string File;
refptr pFile;
pTmp=NextItem(pTmp,File);
vector< refptr >::iterator It=Dirs.begin();
vector< refptr >::iterator ItEnd=Dirs.end();
while (It!=ItEnd)
{
pFile=GetFileInfo(File,*It++);
if (pFile->Exists())
{
break;
}
pFile=NullFileInfo;
}
if (!pFile)
continue;
if (!first)
{
Ret+=g_SpaceString;
}
else
{
first=false;
}
Ret+=pFile->GetQuotedFullFileName();
}
return Ret;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_fullname(const string & Arg) const
{
string File=TrimString(Arg);
refptr pFile=GetFileInfo(File,m_MakeDir);
return pFile->GetQuotedFullFileName();
}
///////////////////////////////////////////////////////////////////////////////
static string basename(const string &FileName,void*)
{
string Ret=UnquoteFileName(FileName);
Ret=Ret.substr(0,Ret.find_last_of('.'));
return QuoteFileName(Ret);
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_basename(const string & FileNames) const
{
return IterList(FileNames,basename);
}
///////////////////////////////////////////////////////////////////////////////
static string notdir(const string &FileName,void*)
{
string Ret=UnquoteFileName(FileName);
size_t Pos=Ret.find_last_of(OSPATHSEP);
if (Pos==string::npos)
{
return FileName;
}
else
{
Ret=Ret.substr(Pos+1);
return QuoteFileName(Ret);
}
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_notdir(const string & FileNames) const
{
return IterList(FileNames,notdir);
}
///////////////////////////////////////////////////////////////////////////////
static string addprefix(const string &FileName,void *pPrefix)
{
return *(const string *)pPrefix+FileName;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_addprefix(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string PreFix;
pTmp=NextCharItem(pTmp,PreFix,',');
#ifdef _DEBUG
if (!*pTmp) {
throw ("Wrong number of arguments in function addprefix");
}
#endif
string FileNames;
pTmp=NextCharItem(pTmp,FileNames,',');
return IterList(FileNames,addprefix,&PreFix);
}
///////////////////////////////////////////////////////////////////////////////
static string addsuffix(const string &FileName,void *pSuffix)
{
return FileName+*(const string *)pSuffix;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_addsuffix(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string SufFix;
pTmp=NextCharItem(pTmp,SufFix,',');
#ifdef _DEBUG
if (!*pTmp) {
throw string("Wrong number of arguments in function addsuffix");
}
#endif
string FileNames;
pTmp=NextCharItem(pTmp,FileNames,',');
return IterList(FileNames,addsuffix,&SufFix);
}
///////////////////////////////////////////////////////////////////////////////
// Returns the n-th word number
string mhmakefileparser::f_word(const string & Arg) const
{
const char *pTmp=Arg.c_str();
string strNum;
pTmp=NextCharItem(pTmp,strNum,',');
int WordNbr=atoi(strNum.c_str());
#ifdef _DEBUG
if (!WordNbr)
{
if (!WordNbr) {
throw string ("Expecting a number bigger then 0 for the word function");
}
}
#endif
int CurWord=0;
while (*pTmp)
{
string Word;
pTmp=NextItem(pTmp,Word);
CurWord++;
if (CurWord==WordNbr)
return Word;
}
return g_EmptyString;
}
///////////////////////////////////////////////////////////////////////////////
// Returns the number of words
string mhmakefileparser::f_words(const string & Arg) const
{
const char *pTmp=Arg.c_str();
int NrWords=0;
char szNumber[10];
while (*pTmp)
{
string Word;
pTmp=NextItem(pTmp,Word);
NrWords++;
}
sprintf(szNumber,"%d",NrWords);
return szNumber;
}
///////////////////////////////////////////////////////////////////////////////
// Search for a command in the enivornment path
string mhmakefileparser::f_which(const string & Arg) const
{
return SearchCommand(Arg);
}
///////////////////////////////////////////////////////////////////////////////
// Removes leading and trailing space
string mhmakefileparser::f_strip(const string & Arg) const
{
string::const_iterator pFirst=Arg.begin();
string::const_iterator pLast=Arg.end();
while (strchr(" \t\r\n",*pFirst) && pFirst!=pLast) pFirst++;
if (pFirst==pLast)
return "";
while (strchr(" \t\r\n",*(--pLast)));
pLast++;
return Arg.substr(pFirst-Arg.begin(),pLast-pFirst);
}
///////////////////////////////////////////////////////////////////////////////
static string dir(const string &FileName, void *)
{
size_t Pos=FileName.find_last_of(OSPATHSEP);
if (Pos==string::npos)
{
return g_EmptyString;
}
else
{
string Ret=g_EmptyString;
Ret+=FileName.substr(0,Pos+1);
if (FileName[0]=='"' && FileName.end()[-1]=='"')
Ret+=s_QuoteString;
return Ret;
}
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_dir(const string & FileNames) const
{
return IterList(FileNames,dir);
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::f_shell(const string & Command) const
{
string Output;
#ifdef _DEBUG
if (g_PrintAdditionalInfo)
cout << "shell: executing: Command '"<ExecuteCommand(string("@")+Command,&Output); // Make sure that the command is not echoed
#ifdef _DEBUG
if (g_PrintAdditionalInfo)
cout << "shell returned '"<