//-----------------------------------------------------------
// Copyright Christian Arnault LAL-Orsay CNRS
// arnault@lal.in2p3.fr
// Modified by garonne@lal.in2p3.fr
// See the complete license in cmt_license.txt "http://www.cecill.info". 
//-----------------------------------------------------------

#include "cmt_syntax.h"
#include "cmt.h"
#include "cmt_symbol.h"
#include "cmt_constituent.h"
#include "cmt_pattern.h"
#include "cmt_error.h"
#include "cmt_branch.h"
#include "cmt_error.h"
#include "cmt_script.h"
#include "cmt_language.h"
#include "cmt_project.h"
#include "cmt_cmtpath_pattern.h"
#include "cmt_log.h"


class KwdAction : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandAction, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdAlias : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandAlias, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdApplication : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use == &(Use::current ()))
      {
	Constituent::action (Application, words);
      }
  }
  
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdApplyPattern : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    ApplyPattern::action (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdApplyTag : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Tag::action_apply (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);    
  }

};

//----------------------------------------------------------
class KwdAuthor : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    use->author_action (words);

  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    cmt_string author;
    project->project_author_action (words);
  }
};
//----------------------------------------------------------


class KwdBranches : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use == &(Use::current ())) 
      {
	Branch::action (words);
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdBuildStrategy : public Kwd
{
public:
  bool decode (const cmt_string& w, cmt_string& strategy, cmt_string& value)
  {
    bool result = true;    
     
    value =  w;  
    
    /*Symbol* symbol = Symbol::find ("use_strategy");
      if (symbol != 0)
      {
      cmt_string s = symbol->build_macro_value ();
      Symbol::expand (s);
      cerr <<"# value s: "<<s<<endl;      
      } */ 

    Symbol::expand(value);
    //cerr <<"# value: "<<value<<endl;
    
    if (value == "prototypes")
      {
	strategy = "BuildPrototypes";
      }
    else if (value == "no_prototypes")
      {
	strategy = "BuildPrototypes";
      }
    else if ((value == "with_installarea") || (value == "with_install_area"))
      {
	value = "with_installarea";
	strategy = "InstallArea";
      }
    else if ((value == "without_installarea") || (value == "without_install_area"))
      {
	value = "without_installarea";
	strategy = "InstallArea";
      }
    else
      {
	result = false;
      }

    return (result);
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
        
    cmt_string cmtpath;
    cmt_string offset;

    use->get_cmtpath_and_offset (cmtpath, offset);

    Project* p = Project::find_by_cmtpath (cmtpath);

    for (int i = 1; i < words.size (); i++)
      {
	const cmt_string& w = words[i];

	cmt_string strategy;
	cmt_string value;

	bool in_error = false;

	if (decode (w, strategy, value))
	  {
	           
	    if (p != 0) p->set_strategy (strategy, value, use->get_package_name ());
	  }
	else
	  {
	    in_error = true;

	    CmtError::set (CmtError::syntax_error, "ParseRequirements> bad strategy keyword");
	  }
      }
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    // action (words, project->get_use(), file_name, line_number);
    // cerr << project->get_use()->get_strategy ("InstallArea") << endl;   
  
            
    for (int i = 1; i < words.size (); i++)
      {
	const cmt_string& w = words[i];

	cmt_string strategy;
	cmt_string value;

	bool in_error = false;

	if (decode (w, strategy, value))
	  {
	    if (project != 0) 
	      {
		// project->set_strategy (strategy, value, project->get_use()->get_package_name ());
		project->set_strategy (strategy, value, "");
		cmt_string s  = "build_strategy ";
		s += words[i];
		s += "\n";            
		bool no_found = true;	                 
		int size =  project->m_extra_lines.size ();
		for (int n = 0; n < size; n++)
		  {
		    if (s==project->m_extra_lines[n])
		      no_found = false;
		  }
		if (no_found)         
		  {  
		    cmt_string & buffer = project->m_extra_lines.add();           
		    buffer = s;
		  }
	      }  
	  }
	else
	  {
	    in_error = true;

	    CmtError::set (CmtError::syntax_error, "ParseRequirements> bad strategy keyword");
	  }
      }
  }
};

class KwdCleanupScript : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Script::action (words, CleanupScript, use);
    Symbol::action (words, CommandCleanupScript, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdCmtPathPattern : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& /*file_name*/,
	       int /*line_number*/)
  {
    CmtPathPattern::action (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdCmtPathPatternRevert : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& /*file_name*/,
	       int /*line_number*/)
  {
    CmtPathPattern::action (words, use, true);
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdContainer : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    project->container_action (words[1], words[2]);
  }
};

class KwdDocument : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use == &(Use::current ()))
      {
	Constituent::action (Document, words);
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdEndPrivate : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use != &(Use::current ()))
      {
	use->pop_scope_section ();
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdEndPublic : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use != &(Use::current ()))
      {
	use->pop_scope_section ();
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdIgnorePattern : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    IgnorePattern::action (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdIncludeDirs : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Include::action (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdIncludePath : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (words.size () > 1)
      {
	use->set_include_path (words[1]);
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdLanguage : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Language::action (words);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdLibrary : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use == &(Use::current ()))
      {
	Constituent::action (Library, words);
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdMacro : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandMacro, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);    
  }
};

class KwdMacroPrepend : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandMacroPrepend, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);
  }
};

class KwdMacroAppend : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandMacroAppend, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);
  }
};

class KwdMacroRemove : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandMacroRemove, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);
  }
};

class KwdMacroRemoveRegexp : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandMacroRemoveRegexp, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  
  {
    action (words, project->get_use(), file_name, line_number);
  }
};

class KwdMacroRemoveAll : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandMacroRemoveAll, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);
  }
};

class KwdMacroRemoveAllRegexp : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandMacroRemoveAllRegexp, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);
  }
};

class KwdMakeFragment : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Fragment::action (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdManager : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    use->manager_action (words);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdPackage : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    /*
      if (words.size () > 1)
      {
      if (use == &(Use::current()))
      {
      m_current_package = words[1];
      build_prefix (m_current_package, m_current_prefix);
	    
      if ((use->get_package_name () != "") &&
      (use->get_package_name () != m_current_package))
      {
      if (!m_quiet)
      {
      //  cerr << "#CMT> package name mismatch in requirements of " <<
      //  use->get_package_name () << " " <<
      //  use->version << " line #" << line_number;
      //  cerr << " : " << m_current_package << " versus " <<
      //  use->get_package_name () << endl;
      }
      }
	    
      use->set (m_current_package,
      m_current_version,
      m_current_path,
      "",
      "");
	    
      use->change_path (m_current_path);
      use->style = m_current_style;
      }
      }
    */
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdPath : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandPath, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}

};

class KwdPathAppend : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandPathAppend, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdPathPrepend : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandPathPrepend, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};
  
class KwdPathRemove : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandPathRemove, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}  
};

class KwdPathRemoveRegexp : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandPathRemoveRegexp, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}  
};

class KwdPattern : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Pattern::action (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}   
};

class KwdPrivate : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use != &(Use::current ()))
      {
	use->push_scope_section (ScopePrivate);
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}  
};

class KwdProject : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
  } 
};

class KwdPublic : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    if (use != &(Use::current ()))
      {
	use->push_scope_section (ScopePublic);
      }
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}   
};

class KwdSet : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandSet, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}    
};

class KwdSetAppend : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandSetAppend, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}    
};

class KwdSetPrepend : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandSetPrepend, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}    
};

class KwdSetRemove : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandSetRemove, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}    
};

class KwdSetRemoveRegexp : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Symbol::action (words, CommandSetRemoveRegexp, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}  
};

class KwdSetupScript : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Script::action (words, SetupScript, use);
    Symbol::action (words, CommandSetupScript, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdSetupStrategy : public Kwd
{
public:
  bool decode (const cmt_string& w, cmt_string& strategy, cmt_string& value)
  {
    bool result = true;

    value =  w; 
    Symbol::expand(value);


    if (value == "config")
      {
	strategy = "SetupConfig";
      }
    else if (value == "no_config")
      {
	strategy = "SetupConfig";
      }
    else if (value == "root")
      {
	strategy = "SetupRoot";
      }
    else if (value == "no_root")
      {
	strategy = "SetupRoot";
      }
    else if (value == "cleanup")
      {
	strategy = "SetupCleanup";
      }
    else if (value == "no_cleanup")
      {
	strategy = "SetupCleanup";
      }
    else
      {
	result = false;
      }

    return (result);
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    cmt_string cmtpath;
    cmt_string offset;

    use->get_cmtpath_and_offset (cmtpath, offset);

    Project* p = Project::find_by_cmtpath (cmtpath);

    for (int i = 1; i < words.size (); i++)
      {
	const cmt_string& w = words[i];

	cmt_string strategy;
	cmt_string value;

	bool in_error = false;

	if (decode (w, strategy, value))
	  {
	    if (p != 0) p->set_strategy (strategy, value, use->get_package_name ());
	  }
	else
	  {
	    in_error = true;

	    CmtError::set (CmtError::syntax_error, "ParseRequirements> bad strategy keyword");
	  }
      }
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    for (int i = 1; i < words.size (); i++)
      {
	const cmt_string& w = words[i];

	cmt_string strategy;
	cmt_string value;

	bool in_error = false;

	if (decode (w, strategy, value))
	  {
	    if (project != 0) project->set_strategy (strategy, value, "");
	    cmt_string s  = "setup_strategy ";
	    s += words[i];
	    s += "\n";

	    bool no_found = true;	                 
	    int size =  project->m_extra_lines.size ();
	    for (int n = 0; n < size; n++)
	      {
		if (s==project->m_extra_lines[n])
		  no_found = false;
	      }
	    if (no_found)         
	      {  
		cmt_string & buffer = project->m_extra_lines.add();           
		buffer = s;
	      }
	  }
	else
	  {
	    in_error = true;

	    CmtError::set (CmtError::syntax_error, "ParseRequirements> bad strategy keyword");
	  }
      }
  }
};

class KwdStructureStrategy : public Kwd
{
public:
  bool decode (const cmt_string& w, cmt_string& strategy, cmt_string& value)
  {
    bool result = true;

    value = w;
    Symbol::expand(value);

    if (value == "with_version_directory")
      {
	strategy = "VersionDirectory";
      }
    else if (value == "without_version_directory")
      {
	strategy = "VersionDirectory";
      }
    else
      {
	result = false;
      }

    return (result);
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    cmt_string cmtpath;
    cmt_string offset;

    use->get_cmtpath_and_offset (cmtpath, offset);

    Project* p = Project::find_by_cmtpath (cmtpath);

    for (int i = 1; i < words.size (); i++)
      {
	const cmt_string& w = words[i];

	cmt_string strategy;
	cmt_string value;

	bool in_error = false;

	if (decode (w, strategy, value))
	  {
	    if (p != 0) p->set_strategy (strategy, value, use->get_package_name ());
	  }
	else
	  {
	    in_error = true;

	    CmtError::set (CmtError::syntax_error, "ParseRequirements> bad strategy keyword");
	  }
      }
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    for (int i = 1; i < words.size (); i++)
      {
	const cmt_string& w = words[i];

	cmt_string strategy;
	cmt_string value;

	bool in_error = false;

	if (decode (w, strategy, value))
	  {
	    if (project != 0) project->set_strategy (strategy, value, "");
	    cmt_string s  = "structure_strategy ";
	    s += words[i];
	    s += "\n";            

	    bool no_found = true;	                 
	    int size =  project->m_extra_lines.size ();
	    for (int n = 0; n < size; n++)
	      {
		if (s == project->m_extra_lines[n])
		  no_found = false;
	      }

	    if (no_found)         
	      {  
		cmt_string & buffer = project->m_extra_lines.add();           
		buffer = s;
	      }	    
	  }
	else
	  {
	    in_error = true;

	    CmtError::set (CmtError::syntax_error, "ParseRequirements> bad strategy keyword");
	  }
      }
  }
};

class KwdTag : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Tag::action (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);    
  }  
};

class KwdTagExclude : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Tag::action_exclude (words, use);
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    action (words, project->get_use(), file_name, line_number);    
  }  
};

class KwdUse : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    Use::action (words, use);
  }

  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {
    project->use_action (words[1], words[2]);
  }
};

class KwdVersionStrategy : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    CmtMessage::warning ("Package " + use->get_package_name ()
			 + " sets obsolescent version strategy");
    //    cerr << "# Package " << use->get_package_name () <<
    //      " sets obsolescent version strategy" << endl;
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdVersion : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
  }
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {}
};

class KwdDefault : public Kwd
{
public:
  void action (const CmtSystem::cmt_string_vector& words,
	       Use* use,
	       const cmt_string& file_name,
	       int line_number)
  {
    /*
      Unknown keyword : just ignore the line
    */
    char num[32]; sprintf (num, "%d", line_number);
    CmtMessage::error ("bad syntax in requirements of " + use->get_package_name ()
		       + " " + use->version + " line #" + num
		       + " [" + words[0] + "...]");
    /*
    if (!Cmt::get_quiet ())
      {
	cerr << "#CMT> bad syntax in requirements of " << use->get_package_name ()
	     << " " << use->version << " line #" << line_number;
	cerr << " [" << words[0] << "...]" << endl;
      }
    */
    
    CmtError::set (CmtError::syntax_error, "ParseRequirements> ");
  }
  
  void action (const CmtSystem::cmt_string_vector& words,
	       Project* project,
	       const cmt_string& file_name,
	       int line_number)
  {action (words, project->get_use(), file_name, line_number);}
};

SyntaxParser& SyntaxParser::instance ()
{
  static SyntaxParser p;

  return (p);
}

/**
 *  Parse the input file, rejecting comments and
 * rebuilding complete lines (from sections separated by
 *  \\ characters.
 *
 *  Each reformatted line is parsed by filter_line
 */
void SyntaxParser::parse_requirements (const cmt_string& file_name, Use* use)
{
  SyntaxParser& me = instance ();

  if (use != 0)
    {
      cmt_string buffer;

      use->fill_standard_macros (buffer);

      AccessMode saved_current_access = Cmt::get_current_access ();
      Cmt::set_current_access (UserMode);
      me.do_parse_text (buffer, "", package_context, use, 0);
      Cmt::set_current_access (saved_current_access);
    }

  me.do_parse_requirements (file_name, use);
  
  
  if (use != 0)
    {
      //
      
      Project* p = use->get_project ();
      if (p != 0)
        {      
	  Use* p_use =  p->get_use();
	  if (p_use != 0 && p_use != use)
	    {
              
              //{
              //  int  size  = use->sub_uses.size ();
              //  for (int n = 0; n < size; n++)
              //  {
              //      Use* tuse = use->sub_uses[n];
              //      cerr << "\tpackage file [" << tuse->get_package_name() << "]" <<p_use->get_package_name()<< endl;         
              //  }
              //}
              
              // add the dependency
              use->add_sub_use (p_use);
              
              // add the right extra statements
              {
                int size =  p->m_extra_lines.size ();
                for (int n = 0; n < size; n++)
		  {
                    cmt_string& s = p->m_extra_lines[n];
                    SyntaxParser::parse_requirements_line (s, use);
		  }  
              } 
              
              // Fill other values                
              use->sub_use_scopes.push_back (use->get_current_scope ());
              use->sub_use_auto_imports.push_back (p_use->auto_imports);
              // cmt_string& request = use->requests.add ();                                          
              // request = package_name;
              // request += " ";
              // request += version;
              // request += " ";
              // request += path;
	      
	      // add at the uses level ?????
	      static Use::UsePtrVector& uses = Use::get_ordered_uses ();
	      //const Use& cu = Use::current ();
	      
	      bool found = false;
	      int  size  = uses.size ();
	      
	      // cerr << "\n size:"<<size<< ":" << endl;     
	      for (int n = 0; n < size; n++)
		{
		  Use* tuse = uses[n];
		  // cerr << "\tpackage file [" << tuse->get_package_name() << "]" <<p_use->get_package_name()<< endl;                    
		  if (tuse->get_package_name()==p_use->get_package_name())
		    found=true;
		}    
	      if (! found)
		{
                  uses.push_back (p_use);
		  p_use->m_index = uses.size () - 1;
		}
	      
	    }
        }                 
      use->close_scope_sections ();
    }
}

/**
 */
void SyntaxParser::parse_project_file_text (const cmt_string& text,
					    const cmt_string& file_name,
					    Project* project)
{
  SyntaxParser& me = instance ();
  me.do_parse_text (text, file_name, project_context, 0, project);
}

/**
 *  Parse a text, rejecting comments and
 * rebuilding complete lines (from sections separated by
 *  \\ characters.
 *
 *  Each reformatted line is parsed by filter_line
 */
void SyntaxParser::parse_requirements_text (const cmt_string& text,
					    const cmt_string& file_name,
					    Use* use)
{
  SyntaxParser& me = instance ();

  /**
   *
   *    We have to preserve m_current_access since it reflects whether
   *   the current cmt action is run in the context of the current package.
   *   (the opposite is when the cmt command specifies the current package
   *    in its arguments -use=... therefore the pwd is NOT the directory
   *    of the current package)
   *
   *   m_current_access is Developer when pwd =  current
   *                       User      when pwd != current
   *
   *    Therefore, as soon as we reach a used package, this must be switched to User
   *
   *   On the other hand, Cmt::scope reflects the status of the public/private 
   *  statements. By default, we are in public context when entering a new requirements
   *  file.
   *
   */

  AccessMode saved_current_access;

  saved_current_access = Cmt::get_current_access ();

  if (use == 0) use = &(Use::current ());

  if (use != &(Use::current ()))
    {
      if (Cmt::get_debug ())
	{
	  cout << "parse_requirements_text> set UserMode" << endl;
	}

      Cmt::set_current_access (UserMode);
    }
  else
    {
      if (Cmt::get_debug ())
	{
	  cout << "parse_requirements_text> set DeveloperMode" << endl;
	}

      Cmt::set_current_access (DeveloperMode);
    }

  me.do_parse_text (text, file_name, package_context, use, 0);

  Cmt::set_current_access (saved_current_access);
}

/**
 *  Apply the basic parser to one single line :
 *
 *   o Append to global text if previous back_slash
 *   o Split into words
 *   o Apply the generic Select operator
 */
void SyntaxParser::parse_requirements_line (const cmt_string& line,
					    Use* use,
					    const cmt_string& file_name,
					    int line_number)
{
  SyntaxParser& me = instance ();
  me.do_parse_line (line, file_name, line_number, package_context, use, 0);
}

SyntaxParser::SyntaxParser ()
{
  m_keywords.add ("action", new KwdAction ());
  m_keywords.add ("alias", new KwdAlias ());
  m_keywords.add ("application", new KwdApplication ());
  m_keywords.add ("apply_pattern", new KwdApplyPattern ());
  m_keywords.add ("apply_tag", new KwdApplyTag ());
  m_keywords.add ("author", new KwdAuthor ());
  m_keywords.add ("branches", new KwdBranches ());
  m_keywords.add ("build_strategy", new KwdBuildStrategy ());
  m_keywords.add ("cleanup_script", new KwdCleanupScript ());
  m_keywords.add ("cmtpath_pattern", new KwdCmtPathPattern ());
  m_keywords.add ("cmtpath_pattern_reverse", new KwdCmtPathPatternRevert ());
  m_keywords.add ("document", new KwdDocument ());
  m_keywords.add ("end_private", new KwdEndPrivate ());
  m_keywords.add ("end_public", new KwdEndPublic ());
  m_keywords.add ("ignore_pattern", new KwdIgnorePattern ());
  m_keywords.add ("include_dirs", new KwdIncludeDirs ());
  m_keywords.add ("include_path", new KwdIncludePath ());
  m_keywords.add ("language", new KwdLanguage ());
  m_keywords.add ("library", new KwdLibrary ());
  m_keywords.add ("macro", new KwdMacro ());
  m_keywords.add ("macro+", new KwdMacroAppend ());
  m_keywords.add ("macro_prepend", new KwdMacroPrepend ());
  m_keywords.add ("macro_append", new KwdMacroAppend ());
  m_keywords.add ("macro_remove", new KwdMacroRemove ());
  m_keywords.add ("macro_remove_regexp", new KwdMacroRemoveRegexp ());
  m_keywords.add ("macro_remove_all", new KwdMacroRemoveAll ());
  m_keywords.add ("macro_remove_all_regexp", new KwdMacroRemoveAllRegexp ());
  m_keywords.add ("make_fragment", new KwdMakeFragment ());
  m_keywords.add ("manager", new KwdManager ());
  m_keywords.add ("package", new KwdPackage ());
  m_keywords.add ("path", new KwdPath ());
  m_keywords.add ("path_append", new KwdPathAppend ());
  m_keywords.add ("path_prepend", new KwdPathPrepend ());
  m_keywords.add ("path_remove", new KwdPathRemove ());
  m_keywords.add ("path_remove_regexp", new KwdPathRemoveRegexp ());
  m_keywords.add ("pattern", new KwdPattern ());
  m_keywords.add ("public", new KwdPublic ());
  m_keywords.add ("private", new KwdPrivate ());
  m_keywords.add ("project", new KwdProject ());
  m_keywords.add ("set", new KwdSet ());
  m_keywords.add ("set_append", new KwdSetAppend ());
  m_keywords.add ("set_prepend", new KwdSetPrepend ());
  m_keywords.add ("set_remove", new KwdSetRemove ());
  m_keywords.add ("set_remove_regexp", new KwdSetRemoveRegexp ());
  m_keywords.add ("setup_script", new KwdSetupScript ());
  m_keywords.add ("setup_strategy", new KwdSetupStrategy ());
  m_keywords.add ("structure_strategy", new KwdStructureStrategy ());
  m_keywords.add ("tag", new KwdTag ());
  m_keywords.add ("tag_exclude", new KwdTagExclude ());
  m_keywords.add ("use", new KwdUse ());
  m_keywords.add ("version_strategy", new KwdVersionStrategy ());
  m_keywords.add ("version", new KwdVersion ());

  m_project_keywords.add ("author", new KwdAuthor());
  m_project_keywords.add ("apply_tag", new KwdApplyTag ());
  m_project_keywords.add ("build_strategy", new KwdBuildStrategy ());
  m_project_keywords.add ("container", new KwdContainer ());
  m_project_keywords.add ("macro", new KwdMacro ());
  // m_project_keywords.add ("macro+", new KwdMacroAppend ());
  // m_project_keywords.add ("macro_prepend", new KwdMacroPrepend ());
  // m_project_keywords.add ("macro_append", new KwdMacroAppend ());
  //m_project_keywords.add ("macro_remove", new KwdMacroRemove ());
  //m_project_keywords.add ("macro_remove_regexp", new KwdMacroRemoveRegexp ());
  //m_project_keywords.add ("macro_remove_all", new KwdMacroRemoveAll ());
  //m_project_keywords.add ("macro_remove_all_regexp", new KwdMacroRemoveAllRegexp ());

  m_project_keywords.add ("project", new KwdProject ());
  m_project_keywords.add ("setup_strategy", new KwdSetupStrategy ());
  
  m_project_keywords.add ("tag", new KwdTag ());
  //m_project_keywords.add ("tag_exclude", new KwdTagExclude ());

  
  m_project_keywords.add ("structure_strategy", new KwdStructureStrategy ());
  m_project_keywords.add ("use", new KwdUse ());
}

void SyntaxParser::do_parse_requirements (const cmt_string& file_name, Use* use)
{
  cmt_string actual_file_name = file_name;
  cmt_string text;

  CmtError::clear ();

  if (!CmtSystem::test_file (actual_file_name))
    {
      actual_file_name = "..";
      actual_file_name += CmtSystem::file_separator ();
      actual_file_name += "cmt";
      actual_file_name += CmtSystem::file_separator ();
      actual_file_name += file_name;

      if (!CmtSystem::test_file (actual_file_name))
        {
          actual_file_name = "..";
          actual_file_name += CmtSystem::file_separator ();
          actual_file_name += "mgr";
          actual_file_name += CmtSystem::file_separator ();
          actual_file_name += file_name;

          if (!CmtSystem::test_file (actual_file_name))
            {
              return;
            }
        }
    }

  text.read (actual_file_name);

  SyntaxParser::parse_requirements_text (text, actual_file_name, use);
}

/**
 *  Parse a text, rejecting comments and
 * rebuilding complete lines (from sections separated by
 *  \\ characters.
 *
 *  Each reformatted line is parsed by filter_line
 */
void SyntaxParser::do_parse_text (const cmt_string& text,
				  const cmt_string& file_name,
				  ContextType context,
				  Use* use,
				  Project* project)
{
  cmt_string line;
  int pos;
  int max_pos;
  int line_number = 1;

  if (context == package_context)
    {
      if (use == 0) use = &(Use::current ());
    }

  m_filtered_text.erase (0);

  pos = 0;
  max_pos = text.size ();

  for (pos = 0; pos < max_pos; )
    {
      int cr = text.find (pos, "\r\n");
      int nl = text.find (pos, '\n');
      int first = nl;
      int length = 1;

      if (cr != cmt_string::npos)
        {
          if (nl == cmt_string::npos)
            {
              first = cr;
              length = 2;
            }
          else
            {
              first = (nl < cr) ? nl : cr;
              length = (nl < cr) ? 1 : 2;
            }
        }

      if (first == cmt_string::npos)
        {
          text.substr (pos, line);
          pos = max_pos;
        }
      else if (first > pos)
        {
          text.substr (pos, first - pos, line);
          pos = first + length;
        }
      else
        {
          line.erase (0);
          pos += length;
        }

      do_parse_line (line, file_name, line_number, context, use, project);

      if ((Cmt::get_action () == action_check_configuration) && CmtError::has_pending_error ())
        {
          //break;
        }

      line_number++;
    }
}

void SyntaxParser::do_parse_line (const cmt_string& line,
				  const cmt_string& file_name,
				  int line_number,
				  ContextType context,
				  Use* use,
				  Project* project)
{
  int length;
  int nl;
  int back_slash;
  cmt_string temp_line = line;

  if (temp_line.size () == 0) return;
  if (temp_line[0] == '#') return;

  nl = temp_line.find_last_of ('\n');
  if (nl != cmt_string::npos) temp_line.erase (nl);

  length = temp_line.size ();
  if (length == 0) return;

  //
  // We scan the line for handling backslashes.
  //
  // o Really terminating backslashes (ie those only followed by spaces/tabs
  // mean continued line
  //
  //

  bool finished = true;

  length = temp_line.size ();

  back_slash = temp_line.find_last_of ('\\');

  if (back_slash != cmt_string::npos)
    {
      //
      // This is the last backslash
      // check if there are only space chars after it
      //
      
      bool at_end = true;

      for (int i = (back_slash + 1); i < length; i++)
        {
          char c = temp_line[i];
          if ((c != ' ') && (c != '\t'))
            {
              at_end = false;
              break;
            }
        }

      if (at_end)
        {
          temp_line.erase (back_slash);
          finished = false;
        }
      else
        {
          // This was not a trailing backslash.
          finished = true;
        }
    }

  m_filtered_text += temp_line;

  if (!finished)
    {
      // We still need to accumulate forthcoming lines
      // before parsing the resulting text.
      return;
    }

  /*
    Here a full line (possibly accumulating several lines
    ended by backslashes) is parsed :
    
    o Special characters are filtered now :
    
    <cmt:tab/>  \t
    <cmt:cr/>   \r
    <cmt:lf/>   \n
    
    o Split into words (a word is a string not containing
    spaces or enclosed in quotes)

    o Parse the word array (function Select)

  */

  m_filtered_text.replace_all ("<cmt:tab/>", "\t");
  m_filtered_text.replace_all ("<cmt:cr/>",  "\r");
  m_filtered_text.replace_all ("<cmt:lf/>",  "\n");

  if (Cmt::get_debug ())
    {
      cout << "parse_requirements_line [" << m_filtered_text << "]" << endl;
    }
  
  static CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (m_filtered_text, " \t", words);
  
  if (words.size () != 0)
    {
      switch (context)
	{
	case project_context:
	  do_parse_words (words, file_name, line_number, project);
	  break;
	case package_context:
	  do_parse_words (words, file_name, line_number, use);
	  break;
	}
    }
  
  m_filtered_text.erase (0);
}

void SyntaxParser::do_parse_words (const CmtSystem::cmt_string_vector& words,
				   const cmt_string& file_name,
				   int line_number,
				   Use* use)
{
  CmtError::clear ();

  if (words.size () == 0) return;

  const cmt_string& command = words[0];

  if (command.size () == 0) return;

  //
  // First analyze the syntax
  //

  Kwd* keyword = m_keywords.find (command);
  if (keyword == 0)
    {
      /*

      When the first word of the line is not a keyword, it may be an
      implicit pattern application.

      */

      Pattern* p = Pattern::find (command);
      if (p == 0)
	{
	  CmtError::set (CmtError::syntax_error, "ParseRequirements> ");
	}
      else
	{
	  keyword = m_keywords.find ("apply_pattern");
	}
    }

  if (CmtError::has_pending_error ())
    {
      char num[32]; sprintf (num, "%d", line_number);
      CmtMessage::error ("bad syntax in requirements of " + use->get_package_name ()
			 + " " + use->version
			 + " " + use->specified_path
			 + " line #" + num
			 + " [" + command + " ...]");
      /*
      if (!Cmt::get_quiet ())
        {
          cerr << "#CMT> bad syntax in requirements of " << use->get_package_name ()
               << " " << use->version 
               << " " << use->specified_path 
               << " line #" << line_number;
          cerr << " [" << command << " ...]" << endl;
        }
      */

      return;
    }

  //
  // Then interpret the action
  //

  keyword->action (words, use, file_name, line_number);
}

void SyntaxParser::do_parse_words (const CmtSystem::cmt_string_vector& words,
				   const cmt_string& file_name,
				   int line_number,
				   Project* project)
{
  CmtError::clear ();

  if (words.size () == 0) return;

  const cmt_string& command = words[0];

  if (command.size () == 0) return;

  //
  // First analyze the syntax
  //

  Kwd* keyword = m_project_keywords.find (command);
  if (keyword == 0)
    {
      CmtError::set (CmtError::syntax_error, "ParseRequirements> ");
    }

  if (CmtError::has_pending_error ())
    {
      char num[32]; sprintf (num, "%d", line_number);
      CmtMessage::error ("bad syntax in project file of " + project->get_name ()
			 + " line #" + num
			 + " [" + command + " ...]");
      /*
      if (!Cmt::get_quiet ())
        {
          cerr << "#CMT> bad syntax in project file of " << project->get_name ()
               << " line #" << line_number;
          cerr << " [" << command << " ...]" << endl;
        }
      */

      return;
    }

  //
  // Then interpret the action
  //

  keyword->action (words, project, file_name, line_number);
}
