/*  This file is part of mhmake.
 *
 *  Copyright (C) 2001-2010 marha@sourceforge.net
 *
 *  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 "md5.h"
#include "mhmakefileparser.h"
#include "rule.h"
#include "flexlexer.h"
#include "mhmakeparser.hpp"
commandqueue             mhmakefileparser::sm_CommandQueue;
stack mhmakefileparser::sm_ParserStack; // Keeps track of the currently active parser
string mhmakefileparser::GetFileNameLineNo(void)
{
  if (!sm_ParserStack.empty())
  {
    yy::mhmakeparser* pParser=sm_ParserStack.top();
    yy::position *pPos=pParser->GetCurPos();
    return pParser->GetInputFilename()+":"+stringify(pPos->line)+" ";
  }
  else
    return g_EmptyString; // Currently not parsing, this is a run-time error after parsing
}
///////////////////////////////////////////////////////////////////////////////
int mhmakefileparser::ParseFile(const fileinfo *pFileInfo, const fileinfo *pMakeDir)
{
  if (pMakeDir)
  {
    m_MakeDir=pMakeDir;
    m_Variables[CURDIR]=m_MakeDir->GetQuotedFullFileName();
  }
  ifstream yyin(pFileInfo->GetFullFileName().c_str(),ios_base::in);
  if (yyin.fail())
  {
    cerr << "Error opening makefile: "<GetQuotedFullFileName()<GetFullFileName();
  theLexer.m_pMakefileParser=this;
  yy::mhmakeparser Parser(this,&theLexer);
  sm_ParserStack.push(&Parser);
  int Ret=Parser.parse();
  sm_ParserStack.pop();
  return Ret;
}
///////////////////////////////////////////////////////////////////////////////
int mhmakefileparser::ParseString(const string &StringIn)
{
  basic_istringstream yyin(StringIn.c_str());
  mhmakeFlexLexer theLexer(&yyin);
  theLexer.m_InputFileName=string("eval(")+StringIn+")";
  theLexer.m_pMakefileParser=this;
  yy::mhmakeparser Parser(this,&theLexer);
  sm_ParserStack.push(&Parser);
  int Ret=Parser.parse();
  sm_ParserStack.pop();
  return Ret;
}
///////////////////////////////////////////////////////////////////////////////
bool mhmakefileparser::IsDefined(const string &Var) const
{
  bool Ret = m_Variables.find(Var)!=m_Variables.end();
  if (!Ret)
  {
    GetFromEnv(Var, &Ret);
  }
  return Ret;
}
///////////////////////////////////////////////////////////////////////////////
static inline size_t SkipUntilQuote(const string &Expr,size_t i,char Char)
{
  while (Expr[i++]!=Char) ;
  return i;
}
///////////////////////////////////////////////////////////////////////////////
// Splits expression on the Item, but the item may not occur within
// a macro or quoted string
static pair SplitExpr(const string &Expr,char Item)
{
  size_t i=0;
  char Char=Expr[i++];
  while (Char!=Item)
  {
    if (Char=='"' || Char=='\'')
    {
      i=SkipUntilQuote(Expr,i,Char);
    }
    else if (Char=='$')
    {
      i=SkipMakeExpr(Expr,i);
    }
    Char=Expr[i++];
  }
  return pair(Expr.substr(0,i-1),Expr.substr(i));
}
///////////////////////////////////////////////////////////////////////////////
bool mhmakefileparser::IsEqual(const string &EqualExpr) const
{
  string Expr=ExpandExpression(EqualExpr);
  const char *pStr=Expr.c_str();
  const char *pTmp=pStr;
  while (*pTmp && *pTmp!=',') pTmp++;
  ptrdiff_t Pos=pTmp-pStr;
  size_t Size=Expr.size();
  pTmp=pStr+Size-1;
  while (pTmp>pStr && strchr(" \t",*pTmp))
  {
    pTmp--;
  }
  if (2*Pos != pTmp-pStr)
  {
    return false;
  }
  pTmp=pStr;
  const char *pTmp2=pTmp+Pos+1;
  if (*pTmp=='(')
  {
    pTmp++;
    Pos--;
  }
  for (int i=0; ii)
        {
          ToAdd=ExpandMacro(Expr.substr(i,inew-i-1));
          i=inew;
        }
        else
        {
          // This is a single character expression
          ToAdd=ExpandMacro(string(1,Expr[i-1]));
        }
        if (ToAdd.find('$')!=string::npos && ToAdd.length()!=1)
        {
          Recurse=true;
        }
        Ret+=ToAdd;
      }
      else
      {
        Ret+=Char;
      }
    }
  }
  return Ret;
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::ExpandMacro(const string &Expr) const
{
  const char *pTmp=Expr.c_str();
  /* First remove leading spaces */
  while (*pTmp==' ' || *pTmp=='\t') pTmp++;
  const char *pVar=pTmp;
  while (*pTmp && *pTmp!=' ' && *pTmp!='\t' && *pTmp!=':')
  {
    if (*pTmp=='$' && pTmp[1]) // We have a macro expansion inside a macro expansion, so recurse Do not consider isolated $ (This was a $$)
    {
      return ExpandMacro(ExpandExpression(Expr));
    }
    pTmp++;
  }
  const char *pVarEnd=pTmp;
  char Type=*pTmp++;
  while (*pTmp && (*pTmp==' ' || *pTmp=='\t')) pTmp++;
  if (Type&&*pTmp)
  { // We have a match for the regular expression ^([^ \\t:]+)([: \\t])[ \\t]*(.+)
    if (Type==':')
    {
      #ifdef WIN32
      bool IsFileName=false;
      if (pVarEnd-pVar == 1 && (*pVar=='<' || *pVar =='@'))
        IsFileName=true;
      #endif
      string ToSubst=ExpandExpression(ExpandVar(string(pVar,pVarEnd)));
      const char *pSrc=pTmp;
      const char *pStop=pSrc;
      while (*pStop!='=') pStop++;
      const char *pTo=pStop+1;
      string SrcStr(ExpandExpression(string(pSrc,pStop)));
      string ToStr(pTo);  // Do not expand yet to be able to use % inside a macro
      #ifdef WIN32
      if (IsFileName)
      {
        matchres Res;
        string FileName(UnquoteFileName(ToSubst));
        if (PercentMatch(FileName,UnquoteFileName(SrcStr),&Res))
        {
          FileName=ReplaceWithStem(UnquoteFileName(ToStr),Res.m_Stem);
        }
        return QuoteFileName(FileName);
      }
      #endif
      return Substitute(ToSubst,SrcStr,ToStr);
    }
    else if (Type==' ' || Type == '\t')
    {
      string Arg(pTmp);
      string Func(pVar,pVarEnd);
      function_f pFunc=m_Functions[Func];
      #ifdef _DEBUG
      if (pFunc)
      {
        return (this->*pFunc)(Arg);
      }
      else
      {
        throw GetFileNameLineNo() + "Unknown function specified in macro: "+Func;
      }
      #else
      return (this->*pFunc)(Arg);
      #endif
    }
    else
    {
    #ifdef _DEBUG
      throw string("Fatal error in ExpandMacro (bug in mhmake ? ? ?)");
    #else
      return g_EmptyString;
    #endif
    }
  }
  else
  {
    #ifdef _DEBUG
    if (Expr.find('$')!=string::npos && Expr.length()!=1)
      throw(string("Bug in mhmake: wasn't expecting a $ sign in: ")+Expr);
    #endif
    string Ret=ExpandVar(Expr);
    if (Ret.length()>1)
      return ExpandExpression(Ret);
    else
      return Ret;
  }
}
///////////////////////////////////////////////////////////////////////////////
string mhmakefileparser::ExpandVar(const string &Var) const
{
  map::const_iterator pIt=m_Variables.find(Var);
  if (pIt==m_Variables.end())
  {
    if (Var.size()==1)
    {
      char Char=Var[0];
      if (m_RuleThatIsBuild)
      {
        switch (Char)
        {
          case '<': // return first prerequisit
#ifdef _DEBUG
            if (!m_RuleThatIsBuild->GetDeps().size())
            {
              return "";
            }
#endif
            return m_RuleThatIsBuild->GetDeps()[0]->GetQuotedFullFileName();
          case '@': // return full target file name
            return m_RuleThatIsBuild->GetQuotedFullFileName();
          case '*': // return stem
            return m_RuleThatIsBuild->GetRule()->GetStem();
          case '^': // return all prerequisits
            return m_RuleThatIsBuild->GetPrerequisits();
          case '/':
            return OSPATHSEPSTR;
          case '$':
            return Var;
          case 'n':
            return "\n";
          default:
            break;
        }
      }
      else
      {
        switch (Char)
        {
          case '<': // return first prerequisit
          case '@': // return full target file name
          case '*': // return stem
          case '^': // return all prerequisits
          case '$': // $$ expansion
            return Var;  // To make comparing of rules more accurate
          case '/':
            return OSPATHSEPSTR;
          case 'n':
            return "\n";
          default:
            break;
        }
      }
    }
    string Env=GetFromEnv(Var);
    if (Env.empty())
    {
      #ifdef _DEBUG
      if (g_PrintAdditionalInfo)
      {
        cout<<"Warning: Variable "<second;
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::SplitToItems(const string &String,vector &Items) const
{
  const char *pTmp=String.c_str();
  while (*pTmp)
  {
    string Item;
    pTmp=NextItem(pTmp,Item);
    if (!Item.empty())
    {
      Items.push_back(GetFileInfo(Item,m_MakeDir));
    }
  }
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::ParseBuildedIncludeFiles()
{
  vector::iterator It=m_ToBeIncludeAfterBuild.begin();
  while (It!=m_ToBeIncludeAfterBuild.end())
  {
    int result=ParseFile(GetFileInfo(*It,m_MakeDir));
    if (result)
    {
      throw string("Error parsing ")+*It;
    }
    It++;
  }
}
#ifdef _DEBUG
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::PrintVariables(bool Expand) const
{
  map::const_iterator It=m_Variables.begin();
  while (It!=m_Variables.end())
  {
    if (Expand)
    {
      try
      {
        cout<first<<" : "<second)<first<<" : "<second<begin();
  while (pIt!=m_pCurrentItems->end())
  {
    if ((*pIt)->GetFullFileName().find('%')!=string::npos)
    {
      IMPLICITRULE::AddImplicitRule(*pIt,*m_pCurrentDeps,m_pCurrentRule);
    }
    else
    {
      // If we had a double colon we must make sure that the target is always build
      if (m_DoubleColonRule)
      {
        (*pIt)->SetNotExist();
      }
      (*pIt)->SetRuleIfNotExist(m_pCurrentRule);
      if (!m_pCurrentRule)
        (*pIt)->AddDeps(*m_pCurrentDeps);
      else
        (*pIt)->InsertDeps(*m_pCurrentDeps);
      if (!m_FirstTarget)
      { // Only check this if the rule is not an implicit rule
        m_FirstTarget=(*m_pCurrentItems)[0];
      }
    }
    pIt++;
  }
  m_pCurrentItems=NULL;
  m_pCurrentRule=NullRule;
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::GetAutoDeps(const fileinfo *pFirstDep, deps_t &Autodeps)
{
  /* Here we have to scan only c/c++ headers so skip certain extensions */
  const char *pFullName=pFirstDep->GetFullFileName().c_str();
  const char *pExt=strrchr(pFullName,'.');
  bool bPython=false;
  if (pExt)
  {
    if (!_stricmp(pExt+1,"py"))
      bPython=true;
  }
  FILE *pIn=fopen(pFullName,"r");
  if (!pIn)
    return;
  char IncludeList[255];
  int PrevRet=0;
  int Ret=fgetc(pIn);
  while(Ret!=EOF)
  {
    char Type[2];
    bool bFound=false;
    if (bPython)
    {
      Type[0]='"';
      if (Ret=='i')
      {
        Ret=fscanf(pIn,"mport%*[ \t]%254[^\"\n]",IncludeList);
        if (Ret==1)
        {
          if (IncludeList[0]!='*')
            bFound=true;
        }
      }
    }
    else
    {
      if (PrevRet=='/')
      {
        if (Ret=='/')
        {
          /* This is a C++ command so read until the next line-feed */
          do
          {
            Ret=fgetc(pIn);
          } while (Ret!='\n' && Ret!=EOF);
        }
        else if (Ret=='*')
        {
          /* This is a standard C comment, so read until then end of the command */
          do
          {
            PrevRet=Ret;
            Ret=fgetc(pIn);
          } while ((PrevRet!='*' || Ret!='/') && Ret!=EOF);
        }
      }
      else if (Ret=='#' || Ret=='.')
      {
        if (Ret=='#')
        {
          Ret=fscanf(pIn,"%*[ \t]");
          Ret=fscanf(pIn,"include%*[ \t]%1[\"<]%254[^>\"]%*[\">]",(char*)&Type,IncludeList);
        }
        else
          Ret=fscanf(pIn,"import%*[ \t]%1[\"<]%254[^>\"]%*[\">]",(char*)&Type,IncludeList);
        if (Ret==2)
        {
          bFound=true;
        }
      }
    }
    if (bFound)
    {
      const char *pTmp=IncludeList;
      while (*pTmp)
      {
        string IncludeFile;
        pTmp=NextItem(pTmp,IncludeFile," \t,");
        if (bPython)
          IncludeFile+=".py";
        if (SkipHeaderFile(IncludeFile))
          continue;
        #ifdef _DEBUG
        m_ImplicitSearch++; // This is to avoid warnings of targets that does not exist
        #endif
        if (Type[0]=='"')
        {
          fileinfo *pInclude=GetFileInfo(IncludeFile,pFirstDep->GetDir());
          /* Add the dependency when the file alrady exist or there is a rule available to be build */
          mh_time_t Date=BuildTarget(pInclude);
          if (Date.DoesExist())  // Try to build the target, and add it if it exists after building
          {
            deps_t::const_iterator pFind=Autodeps.find(pInclude);
            if (pFind==Autodeps.end())
            {
              Autodeps.insert(pInclude);
              GetAutoDepsIfNeeded(pInclude,pInclude);
            }
          }
        }
        const refptr IncludeDirs=GetIncludeDirs();
        fileinfoarray::const_iterator It=IncludeDirs->begin();
        while (Itend())
        {
          fileinfo *pInclude=GetFileInfo(IncludeFile,*It);
          mh_time_t Date=BuildTarget(pInclude);
          if (Date.DoesExist()) // Try to build the target, and add it if it exists after building
          {
            deps_t::const_iterator pFind=Autodeps.find(pInclude);
            if (pFind==Autodeps.end())
            {
              Autodeps.insert(pInclude);
              GetAutoDepsIfNeeded(pInclude,pInclude);
            }
            break;
          }
          It++;
        }
        #ifdef _DEBUG
        m_ImplicitSearch--;
        #endif
      }
    }
    PrevRet=Ret;
    Ret=fgetc(pIn);
  }
  fclose(pIn);
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::GetAutoDepsIfNeeded(fileinfo *pTarget, const fileinfo *pFirstDep)
{
  autodeps_entry_t &Autodeps=m_AutoDeps[pTarget];
  if (!Autodeps.first)
  {
    Autodeps.first=true;
    /* We are going to rescan, so throw away the old. */
    Autodeps.second.clear();
    GetAutoDeps(pFirstDep,Autodeps.second);
    // Now add these dependencies also to the rules
    deps_t::iterator It=Autodeps.second.begin();
    while (It!=Autodeps.second.end())
    {
      pTarget->AddDep(*It);
      It++;
    }
  }
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::UpdateAutomaticDependencies(fileinfo *pTarget)
{
  if (pTarget->IsAutoDepExtention())
  {
    // we have to search for the include files in the first dependency of Target
    vector &Deps=pTarget->GetDeps();
    if (!Deps.size())
      return; // There is no first dep
    fileinfo *pFirstDep=Deps[0];
    GetAutoDepsIfNeeded(pTarget,pFirstDep);
    SetAutoDepsDirty();
  }
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::UpdateNoRuleAutomaticDependencies(fileinfo *pTarget)
{
  // we have to search for the include files in the Target
  deps_t Autodeps;
  GetAutoDeps(pTarget,Autodeps);
  // Now add these dependencies also to the rules
  deps_t::iterator It=Autodeps.begin();
  while (It!=Autodeps.end())
  {
    pTarget->AddDep(*It);
    It++;
  }
}
///////////////////////////////////////////////////////////////////////////////
const refptr mhmakefileparser::GetIncludeDirs() const
{
  string Includes=ExpandExpression("$(INCLUDES)");
  if (!m_pIncludeDirs || Includes != m_IncludeDirs)
  {
    ((mhmakefileparser*)this)->m_IncludeDirs=Includes;
    ((mhmakefileparser*)this)->m_pIncludeDirs=refptr(new fileinfoarray);
    if (Includes.empty())  // If not defined try a default path
      Includes="include inc .." OSPATHSEPSTR "include .." OSPATHSEPSTR "inc";
    const char *pTmp=Includes.c_str();
    while (*pTmp)
    {
      string Item;
      pTmp=NextItem(pTmp,Item);
      fileinfo *pIncDir=GetFileInfo(Item,m_MakeDir);
      if (pIncDir->Exists() || pIncDir->GetRule())
        ((mhmakefileparser*)this)->m_pIncludeDirs->push_back(pIncDir);
    }
  }
  return m_pIncludeDirs;
}
static void ReadStr(FILE *pFile,char *Str)
{
  int i=0;
  while (1)
  {
    Str[i]=fgetc(pFile);
#ifdef _DEBUG
    if (Str[i]==EOF)
    {
      cout<<"Premature end of depency file.\n";
      Str[i]=0;
      return;
    }
#endif
    if (Str[i]=='\n')
      break;
    i++;
  }
  Str[i]='\0';
}
void mhmakefileparser::LoadAutoDepsFile(fileinfo *pDepFile)
{
  if (m_AutoDepFileLoaded && m_AutoDepFileLoaded==pDepFile)
    return;  /* This autodep file is already loaded. */
  m_AutoDepFileLoaded=pDepFile;
  FILE *pIn=fopen(pDepFile->GetFullFileName().c_str(),"rb");
  if (!pIn)
  {
    cerr << "Error opening autodep file "<GetQuotedFullFileName()<GetQuotedFullFileName()<GetQuotedFullFileName()<<": "<AddDep(pDep);
      }
      ReadStr(pIn,FileName);
    }
    ReadStr(pIn,FileName);
  }
  uint32 Md5_32;
  bool MakeNotDirty=true;
  while (fread(&Md5_32,sizeof(Md5_32),1,pIn))
  {
    ReadStr(pIn,FileName);
    fileinfo *pTarget=GetAbsFileInfo(FileName);
    if (!pTarget->CompareMd5_32(0) && !pTarget->CompareMd5_32(Md5_32))
    {
      MakeNotDirty=false; /* BuildTarget had set the dirty flag, but since the md5 did not change it was a false dirty. This is for BuildTargets called before this routine */
      #ifdef _DEBUG
      cout << "Warning: trying to set to different md5's for Target "<GetQuotedFullFileName()<<" Old: "<GetCommandsMd5_32()<<" New: "<GetQuotedFullFileName()<<" to "<SetCommandsMd5_32(Md5_32);  // If it was already there, just update the md5 value
    AddTarget(pTarget);
  }
  if (MakeNotDirty)
    ClearAutoDepsDirty();
  fclose(pIn);
}
void mhmakefileparser::SaveAutoDepsFile()
{
  if (!IsAutoDepsDirty())
    return;
  if (g_Clean
#ifdef _DEBUG
      || g_DoNotExecute || g_GenProjectTree
#endif
     )
    return;   // do not save on clean or if no commands are executed
  string DepFile=ExpandVar(AUTODEPFILE);
  if (!DepFile.size())
  {
    return;
  }
  fileinfo *pDepFile=GetFileInfo(DepFile,m_MakeDir);
#ifdef _DEBUG
  if (g_PrintAdditionalInfo)
    cout<<"Saving automatic dependency file "<GetFullFileName().c_str(),"wb");
  if (!pOut)
  {
    /* Maybe it is because the directory does not exist, so try to create this first */
    if (!MakeDirs(pDepFile->GetDir()))
    {
      #ifdef _DEBUG
      if (!g_DoNotExecute)
      #endif
        cerr << "Error creating dir "<GetDir()->GetFullFileName()<GetFullFileName().c_str(),"wb");
    if (!pOut)
    {
      #ifdef _DEBUG
      if (!g_DoNotExecute)
      #endif
        cerr << "Error creating file "<second.second.empty())
    {
      fprintf(pOut,"%s\n",It->first->GetFullFileName().c_str());
      deps_t::const_iterator DepIt=It->second.second.begin();
      while (DepIt!=It->second.second.end())
      {
        fprintf(pOut,"%s\n",(*DepIt)->GetFullFileName().c_str());
        DepIt++;
      }
      fprintf(pOut,"\n");
    }
    It++;
  }
  /* Now save the Md5 strings */
  fprintf(pOut,"\n");
  set::iterator pIt=m_Targets.begin();
  while (pIt!=m_Targets.end())
  {
    if (!(*pIt)->CompareMd5_32(0))
    {
      (*pIt)->WriteMd5_32(pOut);
      string FileName=(*pIt)->GetFullFileName();
      fwrite(FileName.c_str(),FileName.size(),1,pOut);
      fputc('\n',pOut);
    }
    pIt++;
  }
  fclose(pOut);
}
///////////////////////////////////////////////////////////////////////////////
// Uses the SKIPHEADERS variable to check if we have to skip the header in
// the automatic dependency scanning
bool mhmakefileparser::SkipHeaderFile(const string &FileName)
{
  if (!m_SkipHeadersInitialized)
  {
    m_SkipHeadersInitialized=true;
    string HeadersToSkip=ExpandVar(SKIPHEADERS);
    const char *pTmp=HeadersToSkip.c_str();
    while (*pTmp)
    {
      string Item;
      pTmp=NextItem(pTmp,Item);
      if (Item.find('%')==string::npos)
      {
        m_SkipHeaders.insert(Item);
      }
      else
      {
        m_PercentHeaders.push_back(Item);
      }
    }
  }
  if (m_SkipHeaders.find(FileName)!=m_SkipHeaders.end())
    return true;
  vector::const_iterator It=m_PercentHeaders.begin();
  vector::const_iterator ItEnd=m_PercentHeaders.end();
  while (It!=ItEnd)
  {
    if (PercentMatch(FileName,*It++))
      return true;
  }
  return false;
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::SetExport(const string &Var, const string &Val)
{
  m_Exports.insert(Var);
#ifdef WIN32
  if (!m_pEnv)
  {
    /* Environment not created yet, so create one */
    char *pEnv=GetEnvironmentStrings();
    char *pEnd=pEnv;
    while (*pEnd)
    {
      while (*pEnd++);
    }
    size_t Len=pEnd-pEnv+1;
    m_pEnv=(char*)malloc(Len);
    memcpy(m_pEnv,pEnv,Len);
    m_EnvLen=Len;
    FreeEnvironmentStrings(pEnv);
  }
  /* First check if the variable is in the environment, if so remove it first */
  char *pEnv=m_pEnv;
  while (*pEnv)
  {
    const char *pVar=Var.c_str();
    char *pStart=pEnv;
    while (*pEnv!='=' && tolower(*pEnv)==tolower(*pVar))
    {
      pEnv++; pVar++;
    }
    if (*pEnv=='=' && !*pVar)
    {
      /* Variable found, remove it */
      while (*pEnv++);
      m_EnvLen-=pEnv-pStart;
      while (*pEnv)
      {
        while (*pEnv)
        {
          *pStart=*pEnv++;
          pStart++;
        }
        *pStart=*pEnv++;
        pStart++;
      }
      *pStart=*pEnv;
      break;
    }
    while (*pEnv++);
  }
  size_t VarLen=Var.length();
  size_t ValLen=Val.length();
  size_t Extra=VarLen+ValLen+2;
  /* Add the variable at the end */
  m_pEnv=(char*)realloc(m_pEnv,m_EnvLen+Extra);
  pEnv=m_pEnv+m_EnvLen-1;
  memcpy(pEnv,Var.c_str(),VarLen);
  pEnv+=VarLen;
  *pEnv++='=';
  memcpy(pEnv,Val.c_str(),ValLen);
  pEnv+=ValLen;
  *pEnv++='\0';
  *pEnv++='\0';
  m_EnvLen+=Extra;
#else
  if (!m_pEnv)
  {
    /* Environment not created yet, so create one */
    char **pEnv=environ;
    char **pEnd=pEnv;
    int Len=1;
    while (*pEnd)
    {
      Len++;
      pEnd++;
    }
    m_EnvLen=Len;
    m_pEnv=(char**)malloc(Len*sizeof(pEnv));
    int i=0;
    while (*pEnv)
    {
      m_pEnv[i]=strdup(*pEnv);
      i++;
      pEnv++;
    }
    m_pEnv[i]=NULL;
  }
  /* First check if the variable is in the environment, if so replace it */
  char **pEnv=m_pEnv;
  while (*pEnv)
  {
    const char *pVar=Var.c_str();
    char *pStart=*pEnv;
    char *pTmp=pStart;
    while (*pTmp!='=' && *pTmp==*pVar)
    {
      pTmp++; pVar++;
    }
    if (*pTmp=='=' && !*pVar)
    {
      free(*pEnv);
      *pEnv=strdup((Var+"="+Val).c_str());
      break;
    }
    pEnv++;
  }
  if (!*pEnv)
  {
    // Add it at the end of the list
    m_pEnv=(char**)realloc(m_pEnv,(m_EnvLen+1)*sizeof(*pEnv));
    m_pEnv[m_EnvLen-1]=strdup((Var+"="+Val).c_str());
    m_pEnv[m_EnvLen++]=NULL;
  }
#endif
}
///////////////////////////////////////////////////////////////////////////////
void mhmakefileparser::SetvPath(const string &Pattern, const string &Path)
{
  // First create the array of directory from the Path parameter. For now all entries need to
  // be valid directories (extra checking). This could be improved by checking if there
  // is a rule for creating the directory entry, but like first said, not for now.
  refptr pDirArray=new fileinfoarray;
  const char *pTmp=Path.c_str();
  while (*pTmp)
  {
    string Item;
    pTmp=NextItem(pTmp,Item," \t:;");
    if (!Item.empty())
    {
      fileinfo *pDir=GetFileInfo(Item,m_MakeDir);
      if (!pDir->IsDir())
      {
        #ifdef WIN32
        // On windows this could happen if a full pathname was specified, so we try together
        // with the next item to check if this is not a valid directories
        // but only if the the length of the item was 1 namely the driver letter
        if (Item.length()==1)
        {
          string Rest;
          pTmp=NextItem(pTmp,Rest," \t:;");
          pDir=GetFileInfo(Item+":"+Rest,m_MakeDir);
          #ifdef _DEBUG
          if (g_PrintAdditionalInfo && !pDir->IsDir())
            throw(pDir->GetFullFileName()+" is not a valid directory.");
          #endif
        }
        #ifdef _DEBUG
        else if (g_PrintAdditionalInfo)
          cout << pDir->GetFullFileName() << " is not a valid directory.\n";
        #endif
        #else
        #ifdef _DEBUG
        if (g_PrintAdditionalInfo)
          cout << pDir->GetFullFileName() << " is not a valid directory.\n";
        #endif
        #endif
      }
      pDirArray->push_back(pDir);
    }
  }
  m_vPath.push_back(pair >(Pattern,pDirArray));
}
///////////////////////////////////////////////////////////////////////////////
//
// Search for the target using the vPath
fileinfo* mhmakefileparser::SearchvPath(const fileinfo* pTarget)
{
  string TargetName=pTarget->GetName();
  vector< pair< string, refptr > >::iterator vPathIt=m_vPath.begin();
  while (vPathIt!=m_vPath.end())
  {
    matchres Res;
    if (PercentMatch(TargetName,vPathIt->first,&Res))
    {
      fileinfoarray::iterator pIt=vPathIt->second->begin();
      while (pIt!=vPathIt->second->end())
      {
        fileinfo* pNewTarget=GetFileInfo(TargetName,*pIt);
        mh_time_t TargetDate=StartBuildTarget(pNewTarget,false);
        if (!TargetDate.IsDateValid())
          TargetDate=WaitBuildTarget(pNewTarget);
        if (pNewTarget->GetDate().DoesExist())
          return pNewTarget;
        pIt++;
      }
    }
    vPathIt++;
  }
  return NULL;
}
///////////////////////////////////////////////////////////////////////////////
//
//Checks if the variables retreived from the environment or command-line have been
//changed. Do this at late as possible because they can also be changed in theLexer
//makefiles.
//
void mhmakefileparser::CheckEnv(void)
{
  if (CompareEnv())
  {
    #ifdef _DEBUG
    if (!g_GenProjectTree)
      cout << "Rebuilding everything of "<< m_MakeDir->GetQuotedFullFileName() <<" because environment and/or command-line variables have been changed.\n";
    #endif
    SetRebuildAll();
  }
}
///////////////////////////////////////////////////////////////////////////////
//
//Create a Md5 string from m_GlobalCommandLineVars and USED_ENVVARS
//
#define SkipVar(Var) (m_EnvVarsToIgnore.find(Var)!=m_EnvVarsToIgnore.end())
#define DBGOUT(stuff)
uint32 mhmakefileparser::CreateEnvMd5_32() const
{
  md5_context ctx;
  string Md5;
  string EnvVars=ExpandVar(USED_ENVVARS);
  const char *pTmp=EnvVars.c_str();
  // Now create the md5 string
  md5_starts( &ctx );
  DBGOUT(cout << "MD5 of " << m_MakeDir->GetQuotedFullFileName() << endl);
  while (*pTmp)
  {
    string Var;
    pTmp=NextItem(pTmp,Var,";");
    if (!SkipVar(Var))
    {
      string Val=ExpandVar(Var);
      transform(Val.begin(),Val.end(),Val.begin(),(int(__CDECL *)(int))toupper);
      DBGOUT(cout << GetMakeDir()->GetQuotedFullFileName() << " -> Setting GetFromEnv var " << Var << " to " << Val << endl);
      md5_update( &ctx, (uint8 *) Var.c_str(), (unsigned long)Var.size());
      md5_update( &ctx, (uint8 *) "=", 1);
      md5_update( &ctx, (uint8 *) Val.c_str(), (unsigned long)Val.size());
    }
  }
  return md5_finish32( &ctx);
}
///////////////////////////////////////////////////////////////////////////////
// Makes sure that the makefile exports are set in the environment
bool mhmakefileparser::CompareEnv() const
{
  uint32 Md5_32=CreateEnvMd5_32();
#ifdef _DEBUG
  if (!g_GenProjectTree && Md5_32!=m_EnvMd5_32)
    cout << "Environment has been changed: "<::const_iterator pLineFind=m_CommandLineVars.find(Var);
  if (pLineFind!=m_CommandLineVars.end())
  {
    ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
    ((mhmakefileparser*)this)->m_Variables[Var]=pLineFind->second;
    if (pDefined) *pDefined=true;
    return pLineFind->second;
  }
  const char *pEnv=getenv(Var.c_str());
  if (!pEnv)
  {
    if (pDefined) *pDefined=false;
    return g_EmptyString;
  }
  ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
  ((mhmakefileparser*)this)->m_Variables[Var]=pEnv;
  if (pDefined) *pDefined=true;
  return pEnv;
}
///////////////////////////////////////////////////////////////////////////////
// Creates the variable USED_ENVVARS to be saved in the autodeps file
//
void mhmakefileparser::CreateUSED_ENVVARS()
{
  set Variables;
  set::const_iterator It=m_UsedEnvVars.begin();
  set::const_iterator ItEnd=m_UsedEnvVars.end();
  while (It!=ItEnd)
  {
    string Var=*It;
    if (!SkipVar(Var))
    {
      transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
      Variables.insert(Var);
    }
    It++;
  }
  It=m_Exports.begin();
  ItEnd=m_Exports.end();
  while (It!=ItEnd)
  {
    string Var=*It;
    if (!SkipVar(Var))
    {
      transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
      Variables.insert(Var);
    }
    It++;
  }
  map::const_iterator CLItEnd=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.end();
  map::const_iterator CLIt=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.begin();
  while (CLIt!=CLItEnd)
  {
    string Var=CLIt->first;
    if (!SkipVar(Var))
    {
      transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
      Variables.insert(Var);
    }
    CLIt++;
  }
  It=Variables.begin();
  ItEnd=Variables.end();
  string Val;
  while (It!=ItEnd)
  {
    Val+=*It;
    Val+=";";
    It++;
  }
  m_Variables[USED_ENVVARS]=Val;
}
static string s_TrueString("1");
static string s_FalseString("0");
static string AndExpression(const string &First, const string &Second)
{
  if (First.empty() || First==s_FalseString)
  {
    return g_EmptyString;
  }
  else
  {
    return Second.empty() || Second==s_FalseString ? g_EmptyString : s_TrueString;
  }
}
static string OrExpression(const string &First, const string &Second)
{
  if (First.empty() || First==s_FalseString)
  {
    return Second.empty() || Second==s_FalseString ? g_EmptyString : s_TrueString;
  }
  else
  {
    return s_TrueString;
  }
}
string mhmakefileparser::ResolveExpression(const string &InExpr,string &Rest) const
{
  unsigned i=0;
  string Ret;
  string Expr=InExpr;
  Rest=g_EmptyString;
  while (i