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

#include <stdlib.h>
#include <stdio.h>

#include "cmt.h"
#include "cmt_cvs.h"
#include "cmt_vcs.h"
#include "cmt_awk.h"
#include "cmt_symbol.h"
#include "cmt_project.h"
#include "cmt_log.h"
#include "cmt_error.h"

#include "cmt_syntax.h"

/**

Grep : perform a grep like operation onto a cmt_string

o All lines of the input string are collected when they contain
the specified pattern.
o The input string and the selector pattern are specified in
the constructor:

Grep (input_string, pattern)

o All selected lines are accumulated (appended) into the internal
variable m_result . 'space' is the separator.

o The accumulator is retrieved by the result () method.

*/
class Grep : public Awk
{
public:

  void begin ();
  void filter (const cmt_string& line);
  const cmt_string& result () const;

private:
  cmt_string m_result;
};

/**

Cut : perform a cut-like operation :

o collect the <field>'th field of every line into the m_result 
internal variable

o the field number is given in the constructor and starts at zero.

o selected fields are accumulated with a space as separator.

*/
class Cut : public Awk
{
public:
  Cut (int field);
  void begin ();
  void filter (const cmt_string& line);
  const cmt_string& result () const;

private:
  cmt_string m_result;
  int m_field;
};

/**

      History : maintains the history of checkouts during a 
                recursive checkout, so as to avoid double checkouts.

 */
class History
{
public:
  static History& instance ();
  void clear ();
  void install (const cmt_string& line);
  bool is_installed (const cmt_string& line);

private:
  History ();

  cmt_string m_installed;
};

//--------------------------------------------------------------------
static void show_packages ()
{
  Package::PackageVector& vector = Package::packages ();

  int i;
  int j;

  cout << "### Packages: ";
  for (i = 0; i < vector.size (); i++)
    {
      Package& p = vector[i];
      cout << p.get_name () << "[";
      Use::UsePtrVector& uses = p.get_uses ();
      for (j = 0; j < uses.size (); j++)
	{
	  Use* u = uses[j];
	  cout << u << ",";
	}

      cout << "] ";
    }
  cout << endl;

  {
    Use::UsePtrVector& uses = Use::get_ordered_uses ();
    //    static Use::UsePtrVector& uses = Use::get_ordered_uses ();

    cout << "### Uses: ";
    for (i = 0; i < uses.size (); i++)
      {
	Use* u = uses[i];
	cout << "[" << u << "]" << u->get_package_name () << " ";
      }
  }

  cout << endl;
}

//--------------------------------------------------------------------

class GlobalProjectAction : public IProjectAction
{
public:
  GlobalProjectAction (const cmt_string& package,
		       const cmt_string& version = "",
			const cmt_string& path = "")
    : m_package (package), m_version (version), m_path (path)
  {
    m_home_dir = CmtSystem::pwd ();
    m_current = Use::current();
    m_current_name = m_current.get_package_name ();
    m_current.set_package_name ("m_current");
  }

  bool run (const Project& project)
  {
    if (project.is_current ()) return true;
    CmtMessage::verbose ("run: " + project.get_cmtpath ());
    const Use* use = &(project.get_container ());
    if (use->get_package_name () != "" && use->located ())
      {
	CmtMessage::verbose ("container " + use->get_info () +
			     " (" + use->real_path + ")");
	//	Use* cuse = Use::add (use->real_path,
	Use* cuse = Use::add (use->path,
			      use->get_package_name (),
			      use->version,
			      "", "", "", 0); // 0 == NOT used by current
	if (cuse)
	  {
	    cmt_string cmt (use->get_full_path ());
	    cmt += CmtSystem::file_separator ();
	    cmt += "cmt";
	    if (!CmtSystem::cd (cmt))
	      {
		CmtMessage::error ("Cannot move to directory " + cmt);
		CmtError::set (CmtError::file_access_error, cmt);
		if (!CmtSystem::cd (m_home_dir))
		  {
		    CmtMessage::error ("Cannot move to directory " + m_home_dir);
		    CmtError::set (CmtError::file_access_error, m_home_dir);
		  }
		return true;
	      }
	    //cerr << "parsing: " << "requirements" << endl; 
	    SyntaxParser::parse_requirements ("requirements", cuse);
	    //cerr << "parsed: " << "requirements" << endl; 
	    //show_packages ();
	  }
	else
	  {
	    CmtMessage::error ("Cannot add container " + use->get_package_name () +
			       " " + use->version + " " + use->real_path);
	  }
	if (!CmtSystem::cd (m_home_dir))
	  {
	    CmtMessage::error ("Cannot move to directory " + m_home_dir);
	    CmtError::set (CmtError::file_access_error, m_home_dir);
	  }
      }
    return true;
  }

private:
  cmt_string m_home_dir;
  cmt_string m_package;
  cmt_string m_version;
  cmt_string m_path;
  cmt_string m_cmtpath;
  Use m_current;
  cmt_string m_current_name;
};

//--------------------------------------------------------------------
/**
 *
 *   Packages: keep info on all packages that can be found
 *   in current context - CMTPATHs.
 *
 */

class Packages
{
public:
  static Packages& instance ();
  //  void clear ();
  //  void install (const cmt_string& line);
  //  bool is_installed (const cmt_string& line);

  Use* find (const cmt_string& name,
	     const cmt_string& version = "",
	     const cmt_string& path = "");

  void exclude_current_project ();
  void restore_current_project ();

private:
  Packages ();

  int m_i;
  Project* m_cur;
  Project m_null;
  bool m_initialized;
};

//--------------------------------------------------------------------
Packages::Packages ()
  : m_i (-1), m_cur (0), m_initialized (false)
{
  m_null.set_cmtpath (CmtSystem::get_temporary_name ());
}

//--------------------------------------------------------------------
Packages& Packages::instance ()
{
  static Packages pkgs;
  return (pkgs);
}

//--------------------------------------------------------------------
Use* Packages::find (const cmt_string& name,
		     const cmt_string& version,
		     const cmt_string& path)
{
  if (name == "") return 0;
  //  cerr << "find: " << name << " " << version << " " << path << endl;
  exclude_current_project ();
  if (path != "")
    {
      //cerr << "find: No need to find path: " << name << " " << path << endl;
      //      Use use_obj (name, "", path);
      Use use_obj (name, version, path);
      Use* use = &use_obj;
      //use->set (name, version, path);
      use->get_package ()->remove_use (use);
      if (use->move_to ())
	{
	  CmtSystem::cd (Cmt::get_current_dir ());
	  /*
	  cerr << "real_path: " << use->real_path
	       << " style: " << use->style
	       << " structuring_style: " << use->structuring_style
	       << " full_path: " << use->get_full_path () << endl;
	  */
	  Use::UseVector& instances = Use::get_instances ();
	  Use& use_object = instances.add ();
	  use_object = *use;
	  /*
	  use_object.set (use->get_package_name (), use->version, use->path,
			  use->version_alias, use->path_alias);
	  use_object.get_package ()->remove_use (&use_object);
	  */
	  restore_current_project ();
	  return (&use_object);
	}
      else
	{
	  CmtSystem::cd (Cmt::get_current_dir ());
	  CmtMessage::warning (CmtError::get_error_name (CmtError::package_not_found)
			       + ": " + use->get_info ());
	}
    }
  //
  // Second try is among the CMTPATHs
  //
  if (!m_initialized)
    {
      m_initialized = true;

      GlobalProjectAction pa (name, version, path);
      
      Project::reverse_broadcast (pa);
  //  Project::broadcast (pa);
    }
  //  cerr << "find: " << name << " " << version << " " << path << " Done." << endl;
  //show_packages ();

  Package* p = Package::find (name);
  if (p == 0)
    {
      restore_current_project ();
      return 0;
    }
    //return PackageInfo (name, version, path, "");
  Use::UsePtrVector& uses = p->get_uses ();
  Use* p_use (0);
  //  cerr << "find for: " << name << endl;
  for (int use_index = 0; use_index < uses.size (); use_index++)
    {
      Use* use = uses[use_index];
      char index[8];
      sprintf (index, "%d", use->m_index);
      CmtMessage::verbose ("find: " + use->get_info () +
			   " (" + use->real_path + ")" + 
			   " m_index: " + index + 
			   " located: " + (use->located () ? "1" : "0"));
      if (use->located ()) p_use = use;
      //      if (use->m_index >= 0) return use;
      if (use->m_index >= 0) break;
    }
  if (p_use)
    {
//       cerr << "find: Try to validate: " << p_use->get_info ()
// 	   << ": " << p_use->real_path << endl;
      //      Use use_obj (name, "", path);
      Use use_obj (name, version, p_use->path);
      Use* use = &use_obj;
      use->get_package ()->remove_use (use);
      if (use->move_to ())
	{
	  CmtSystem::cd (Cmt::get_current_dir ());
	  if (use->version == p_use->version)
	    {
	      CmtMessage::verbose ("Validated: " + p_use->get_info () +
				   " (" + p_use->real_path + ")");
	      restore_current_project ();
	      return p_use;
	    }
	  //	  Use* n_use = Use::add (use->real_path,
	  Use* n_use = Use::add (use->path,
				 use->get_package_name (),
				 use->version,
				 use->version_alias,
				 use->path_alias,
				 use->native_version,
				 0); // 0 == NOT used by current
	  CmtSystem::cd (Cmt::get_current_dir ());
	  restore_current_project ();
	  return n_use;
	}
      else
	{
	  CmtSystem::cd (Cmt::get_current_dir ());
	  CmtMessage::verbose (CmtError::get_error_name (CmtError::package_not_found)
			       + ": " + use->get_info ());
	  restore_current_project ();
	  return 0;
	}
    }

  restore_current_project ();
  return p_use;
  //  return PackageInfo (name, version, path, "");
  //  return pa.info ();
}

//--------------------------------------------------------------------
void Packages::exclude_current_project ()
{
  if (m_i != -1 || m_cur != 0) return;
  //  cerr << "exclude_current_project: " << m_i << ": " << m_cur << endl;
  Project::ProjectPtrVector& Ordered = Project::ordered_projects ();
  int i = -1;
  //  m_cur = 0;
  //  Project null;
  for (i = 0; i < Ordered.size (); i++)
    if (Ordered[i]->is_current ()) break;
  if (i >= 0 && i < Ordered.size ())
    {
      m_i = i;
      m_cur = Ordered[i];
      Ordered[i] = &m_null;
    }
}

//--------------------------------------------------------------------
void Packages::restore_current_project ()
{
  Project::ProjectPtrVector& Ordered = Project::ordered_projects ();
  if (m_i >= 0 && m_i < Ordered.size ())
    {
      Ordered[m_i] = m_cur;
      m_i = -1;
      m_cur = 0;
    }
}

//--------------------------------------------------------------------
/*
void Packages::clear ()
{
  m_installed = "";
}

void Packages::install (const cmt_string& line)
{
  m_installed += "|";
  m_installed += line;
  m_installed += "|";
}

bool Packages::is_installed (const cmt_string& line)
{
  if (m_installed.find (line) != cmt_string::npos)
    {
      return (true);
    }
  
  return (false);
}
*/

//--------------------------------------------------------------------
/**

     RecursivePass1 : simply validate use statements in a requirements file
                      and echo those that really need to be handled.

 */
class RecursivePass1 : public Awk
{
public:

  void begin ();
  void filter (const cmt_string& line);
  const cmt_string& result () const;

private:
  cmt_string m_result;
  bool m_first;
};

/**

     RecursivePass2 : after filtering the use statements really perform the
                      checkouts.

 */
class CvsImplementation;
class RecursivePass2 : public Awk
{
public:
  RecursivePass2 (CvsImplementation& cvs);
  void begin ();
  void filter (const cmt_string& line);

private:
  CvsImplementation& m_cvs;
};

//--------------------------------------------------------------------
/**
 *
 * Helper classes
 *
 */

/*
class SelectSubUses : public Awk
{
public:

  void begin ();
  void filter (const cmt_string& line);
  //  const cmt_string& result () const;
  bool has (const cmt_string& package) const;

private:
  void append (const cmt_string& package);
  cmt_string m_result;
  bool m_first;
};

void SelectSubUses::begin ()
{
  m_first = true;
  m_result = "";
}

void SelectSubUses::filter (const cmt_string& line)
{
  CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (line, " \t", words);

  enum
    {
      need_package,
      need_version,
      need_path,
      no_need
    } state = need_package;

  cmt_string package;
  cmt_string version;
  cmt_string path;
  cerr << line;
  for (int i = 2; i < words.size (); i++)
    {
      const cmt_string& s = words[i];
      cerr << " `" + s + "'"; 
      if (s[0] == '-') continue;

      switch (state)
        {
	case need_package:
	  package = s;
	  state = need_version;
	  break;
	case need_version:
	  version = s;
	  state = need_path;
	  break;
	case need_path:
	  if (s.find ("(") != 0)
	    path = s;
	  state = no_need;
	  break;
        }
    }
  cerr << endl;

  if (package == "" ||
      package == "CMT" ||
      package == "cmt")
    return;
  append (package);
}

void SelectSubUses::append (const cmt_string& package)
{
  if (m_first)
    {
      m_first = false;
      m_result = "|" + package + "|";
    }
  else
    {
      if (!has (package))
	m_result += package + "|";
    }
  cerr << "m_result: " << m_result << endl;
}

bool SelectSubUses::has (const cmt_string& package) const
{
  return (m_result.find ("|" + package + "|") != cmt_string::npos);
}
*/

//--------------------------------------------------------------------
class Walkthru : public Awk
{
public:
  Walkthru (IUseVisitor& visitor);
  //  RecursivePass2 (CvsImplementation& cvs);
  void begin ();
  void filter (const cmt_string& line);

private:
  //  CvsImplementation& m_cvs;
  IUseVisitor& m_visitor;
};

//--------------------------------------------------------------------
Walkthru::Walkthru (IUseVisitor& visitor)
  : m_visitor (visitor)
{
}

//--------------------------------------------------------------------
void Walkthru::begin ()
{
}

//--------------------------------------------------------------------
void Walkthru::filter (const cmt_string& line)
{
  CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (line, " \t", words);

  enum
    {
      need_package,
      need_version,
      need_path,
      no_need
    } state = need_package;

  cmt_string package;
  cmt_string version;
  cmt_string path;
  //  cerr << line;
  for (int i = 1; i < words.size (); i++)
    {
      const cmt_string& s = words[i];
      //      cerr << " `" + s + "'"; 
      if (s[0] == '-') continue;

      if (s.find ("$(") != cmt_string::npos
	  || s.find ("${") != cmt_string::npos)
	{
	  CmtMessage::warning (line +
			       ": Skipped due to macro reference(s)."
			       " Consider using -C option.");
	  return;
	}

      switch (state)
        {
	case need_package:
	  package = s;
	  state = need_version;
	  break;
	case need_version:
	  version = s;
	  state = need_path;
	  break;
	case need_path:
	  if (s.find ("(") != 0 && s.find ("#") != 0)
	    path = s;
	  state = no_need;
	  break;
        default:
	  break;
        }
    }
  //  cerr << endl;
  
  if (package == "" ||
      package == "CMT" ||
      package == "cmt" ||
      package == CmtSystem::get_home_package () ||
      package == CmtSystem::get_user_context_package ()
      )
    return;
  /*
  if (version == "")
    version = "*";
  */

  /*
  if (version.find ("*") != cmt_string::npos)
    {
// 	cerr << "# ================= Package " << package 
// 	<< " version " << version << " " << path 
// 	<< " has wild cards and will not be considered." << endl;
      CmtMessage::warning (package + " " + version +
			   (path != "" ? " " + path : "") +
			   ": Skipped due to wildcards in version."
			   " Consider using -C option.");
    }
  else
    {
  */
      Use use_obj (package, version, path);
      Use* use = &use_obj;
      //use->set (name, version, path);
      use->get_package ()->remove_use (use);
      m_visitor.in (use);
      //      static const cmt_string empty;
      //      m_cvs.do_checkout_phase2 (path, package, version, empty);
//       cmt_string module ((path != "") ? path + "/" + package : package);
//       Vcs::checkout_module (m_checkout,
// 			    m_config, m_with_version_directory,
// 			    module, version,
// 			    m_type);
//    }

}

//--------------------------------------------------------------------

/**

     RecursivePass3 : simply validate use statements in a project file
                      and echo those that really need to be handled.

 */
class RecursivePass3 : public Awk
{
public:

  void begin ();
  void filter (const cmt_string& line);
  const cmt_string& result () const;

private:
  cmt_string m_result;
  bool m_first;
};

/**

     RecursivePass4 : after filtering the use statements really perform the
                      project checkouts.

 */
class CvsImplementation;
class RecursivePass4 : public Awk
{
public:
  RecursivePass4 (CvsImplementation& cvs);
  void begin ();
  void filter (const cmt_string& line);

private:
  CvsImplementation& m_cvs;
};

/**

   Internal implementation of CVS to CMT operations.
   The Cvs class only provides abstract interface.

 */
class CvsImplementation
{
public:

  CvsImplementation ()
  {
    clear ();
  }

  void clear ()
  {
    m_recursive  = false;
    m_head       = false;
    m_verbose    = false;
    m_simulation = false;
    no_config    = false;

    m_home_dir     = "";
    m_checkout_dir = "";
    m_version_dir  = "";
    m_cvs_offset   = "";

    m_last_module        = "";
    m_last_cvs_infos     = "";
    structure_info       = "";
    error_info           = "";
    tags_top_info        = "";
    tags_info            = "";
    cvsversions_top_info = "";
    cvsversions_info     = "";
    branches_info        = "";
    subpackages_info     = "";
    subprojects_info     = "";

    m_protocol_level     = "";

    Symbol* symbol = Symbol::find ("cmt_cvs_protocol_level");
    if (symbol != 0)
      {
	m_protocol_level = symbol->build_macro_value ();
	Symbol::expand (m_protocol_level);
      }
  }

  CvsImplementation& operator = (const CvsImplementation& other)
  {
    m_recursive  = other.m_recursive;
    m_head       = other.m_head;
    m_verbose    = other.m_verbose;
    m_simulation = other.m_simulation;
    no_config    = other.no_config;

    m_home_dir       = other.m_home_dir;
    m_checkout_dir   = other.m_checkout_dir;
    m_version_dir    = other.m_version_dir;
    m_cvs_offset     = other.m_cvs_offset;
    m_protocol_level = other.m_protocol_level;
    m_last_module    = other.m_last_module;
    m_last_cvs_infos = other.m_last_cvs_infos;

    structure_info       = other.structure_info;
    error_info           = other.error_info;
    tags_top_info        = other.tags_top_info;
    tags_info            = other.tags_info;
    cvsversions_top_info = other.cvsversions_top_info;
    cvsversions_info     = other.cvsversions_info;
    branches_info        = other.branches_info;
    subpackages_info     = other.subpackages_info;
    subprojects_info     = other.subprojects_info;

    return (*this);
  }

  /**
     Filter out the space-separated words of a text that don't match a regexp.
  */
  void filter_list (cmt_string& text, const cmt_regexp& exp)
  {
    CmtSystem::cmt_string_vector list;

    CmtSystem::split (text, " ", list);

    int i;

    text = "";

    for (i = 0; i < list.size (); i++)
      {
	const cmt_string& s = list[i];
	if (exp.match (s))
	  {
	    if (i > 0) text += " ";
	    text += s;
	  }
      }
  }

  int execute (const cmt_string& command)
  {
    int status = 0;

    if (m_verbose || m_simulation)
      {
	CmtMessage::info ("Executing [" + command + "]");
	//	cerr << "#CMT> Executing [" << command << "]" << endl;
      }
    
    if (!m_simulation)
      {
	status = CmtSystem::execute (command);
      }

    return (status);
  }

  void execute_and_retry (const cmt_string& command, const cmt_string& message)
  {
    int status;
    int retry = 0;

    for (;;)
      {
	status = execute (command);
        
	if (status != 0)
	  {
	    retry++;
	    
	    char stat[8];
	    //sprintf (stat, "%d", WEXITSTATUS(status));
	    sprintf (stat, "%d", status);
	    char ret[8];
	    sprintf (ret, "%d", retry);
	    CmtMessage::error ("# " + message + ": status=" + stat);
	    CmtMessage::error ("#---------------------------------------------------------");
	    //	    cerr << "# " << message << ": status=" << status << endl;
	    //	    cerr << "#---------------------------------------------------------" << endl;
	    
	    if (retry > 5)
	      {
		exit(status);
		//		exit(0);
	      }
	    else
	      {
		CmtMessage::info (command + " [retry: " + ret + "]");
	      }
	  }
	else
	  {
	    break;
	  }
      }
  }

  int execute (const cmt_string& command, cmt_string& out)
  {
    int status = 0;

    if (m_verbose || m_simulation)
      {
	CmtMessage::info ("Executing [" + command + "]");
	//	cerr << "#CMT> Executing [" << command << "]" << endl;
      }
    
    if (!m_simulation)
      {
	status = CmtSystem::execute (command, out);
	
      }
      
    //if (m_verbose || m_simulation)
    //  {
    //       cerr << out << endl;
    //  }

    return (status);
  }


  /**
     This function will check the protocol level with the CVS pluggin.
     The expected level is defined in a macro (in the requirements file of CMT)
     The protocol level characterizes the structure of the message received from the CVS pluggin
     and depends on its version.

     In order to know if the pluggin is installed AND if the support for this version is
     installed, we checkout CVSROOT/loginfo which should contain entries with selection patterns

     .cmtcvsinfos/<protocol_level>

     However old versions only offer as pattern:

     .cmtcvsinfos

     In this function we'll detect if the effective protocol level satisifies the 
     level expected for this release of CMT

     In simulation mode we suppose the expected protocol has been found.
  */
  bool check_protocol ()
  {
    static bool done = false;
    static bool found = true;

    if (done) return (found);
    done = true;

    cmt_string cvsroot;
	
    CmtSystem::get_cvsroot (cvsroot);
	
    cmt_string command;
	
    command = "cvs";
    if (cvsroot != "") 
      {
	command += " -d ";
	command += cvsroot;
      }
    command += " -Q co -p CVSROOT/loginfo ";

    found = false;

    cmt_string pattern = ".cmtcvsinfos";

    if (m_protocol_level != "")
      {
	pattern += "/";
    	pattern += m_protocol_level;
      }

    cmt_string loginfo;

    if (m_simulation)
      {
	loginfo = pattern;
      }

    execute (command, loginfo);

    int pos = loginfo.find (pattern);

    if (pos != cmt_string::npos)
      {
	found = true;
      }

    if (m_verbose)
      {
	if (found)
	  {
	    CmtMessage::info ("Protocol level " + m_protocol_level);
	    //	    cerr << "#CMT> Protocol level " << m_protocol_level << endl;
	  }
	else
	  {
	    CmtMessage::error ("The CVS pluggin is not installed or is not at protocol level " + m_protocol_level);
	    //	    cerr << "#CMT> The CVS pluggin is not installed or is not at protocol level " << m_protocol_level << endl;
	  }
      }

    if (!found)
      {
	CmtMessage::error ("The CVS pluggin is not installed or is not at protocol level " + m_protocol_level);
	exit(1);
      }

    return (found);
  }

  /**
     Execute the CVS command that activates the CVS pluggin, ie this is a cvs import 
     using the conventional module .cmtcvsinfos/<protocol-level>/<module>

     We create a temporary directory just to lauch the command. However nothing
     should change in this temporary directory since the pluggin returns an error status.
  */
  void retreive_cvs_infos (const cmt_string& module)
  {
    static const cmt_string cmtcvsinfos = ".cmtcvsinfos";

    cmt_string home_dir = CmtSystem::pwd ();
	
    //
    // Activities related with .cmtcvsinfos will occur in a temporary directory
    //
    cmt_string tmp_dir = CmtSystem::getenv ("TMPDIR");
    if (tmp_dir == "")
      {
	tmp_dir = CmtSystem::file_separator ();
	tmp_dir += "tmp";
      }
    
    if (!CmtSystem::cd (tmp_dir))
      {
	tmp_dir = home_dir;
      }
    
    tmp_dir += CmtSystem::file_separator ();
    tmp_dir += "cmtcvs";
    {
      cmt_string temp = CmtSystem::get_temporary_name ();
      CmtSystem::basename (temp, temp);
      // Suppress dots for Windows
      temp.replace_all (".", "");
      tmp_dir += temp;
    }
    
    if (!CmtSystem::test_directory (tmp_dir))
      {
	if (!CmtSystem::mkdir (tmp_dir))
	  {
	    CmtMessage::error ("Cannot create the temporary directory [" + tmp_dir + "]");
	    //	    cerr << "#CMT> Cannot create the temporary directory [" << tmp_dir << "]" << endl;
	    return;
	  }
      }
    
    //trap "rm -rf ${tmp_dir}" 0 1 2 15
    
    if (!CmtSystem::cd (tmp_dir))
      {
	CmtMessage::error ("Cannot move to the temporary directory " + tmp_dir);
	//	cerr << "#CMT> Cannot move to the temporary directory " << tmp_dir << endl;
	
	if (m_verbose)
	  {
	    CmtMessage::info ("now removing tmp_dir " + tmp_dir + " home=" + home_dir);
	    //	    cerr << "#CMT> now removing tmp_dir " << tmp_dir << " home=" << home_dir << endl;
	  }
	
	CmtSystem::remove_directory (tmp_dir);
	
	return;
      }
    
    if (m_verbose)
      {
	CmtMessage::info ("cvs infos are now obtained from the temporary directory " + CmtSystem::pwd ());  
	//	cerr << "#CMT> cvs infos are now obtained from the temporary directory " << CmtSystem::pwd () << endl;  
      }
    
    /**
       The script associated to such entries is supposed to :
       1) extract the set of <infos> from ${module}/cmt/requirements
       or ${module}/cmt/project.cmt
       2) build an output of the form :
       <infos>=info1 info2 info3 ...
      
       Currently this script can be found in
      
       ${CMTROOT}/cmt/cmt_buildcvsinfos2.sh
       %CMTROOT%/cmt/cmt_buildcvsinfos.py
       There is a C++ implementation as cmtcvs.exe
      
    */
    
    if (!CmtSystem::test_directory (cmtcvsinfos))
      {
	CmtSystem::mkdir (cmtcvsinfos);
      }
    
    CmtSystem::cd (cmtcvsinfos);
    
    cmt_string cvsroot;
    
    CmtSystem::get_cvsroot (cvsroot);
    
    cmt_string command;
    
    command = "cvs";
    if (cvsroot != "") 
      {
	command += " -d ";
	command += cvsroot;
      }
    command += " -Q import -m cmt ";
    
    command += cmtcvsinfos;
    
    if (m_protocol_level != "")
      {
	command += "/";
	command += m_protocol_level;
      }
    command += "/";
    command += module;
    command += " CMT v1";
    
    m_last_cvs_infos = "";

    execute (command, m_last_cvs_infos);
    
    if (m_verbose)
      {
	CmtMessage::info ("now removing tmp_dir " + tmp_dir + " home=" + home_dir);
	//	cerr << "#CMT> now removing tmp_dir " << tmp_dir << " home=" << home_dir << endl;
      }
    
    CmtSystem::cd (home_dir);
    CmtSystem::remove_directory (tmp_dir);	
  }

  /**
     This method exploits the hook installed into the loginfo script.
     A communication is setup with a dummy CVS module named .cmtcvsinfos/<module>
    
     At import time, the contents of the file will be used to parameterize
     the script named cmt_buildcvsinfos2.sh (referenced in the loginfo script)
    
     This script performs a scan in the CVS repository for the following types of
     information :
    
     the recognized structure below this module (none, project, package)
     all top symbolic tags installed for the module
     all symbolic tags installed for the module
     all branches available below this module
     all subpackages installed below the module.
     all subprojects installed below the module.
    
     In principle, only modules corresponding to true CMT products (packages or projects) are considered.
     o tags are obtained from the requirements or the project file
     o branches are sub-directories which are not themselves packages
     o subpackages are sub-directories which are CMT packages
     (a subdirectory is always either a branch or a subpackage)
  */
  void get_cvs_infos_with_offset (const cmt_string& module)
  {
    if (!check_protocol ())
      {
	CmtMessage::error ("The CVS pluggin is not installed or is not at protocol level " + m_protocol_level);
	//	cerr << "#CMT> The CVS pluggin is not installed or is not at protocol level " << m_protocol_level << endl;
	return;
      }

    if (module == "")
      {
	CmtMessage::error ("cmt cvs needs a module name");
	//	cerr << "#CMT> cmt cvs needs a module name" << endl;
	return;
      }

    if (module == m_last_module)
      {
	if (m_verbose)
	  {
	    CmtMessage::info ("cvs infos for module " + module + " already there");
	    //	    cerr << "#CMT> cvs infos for module " << module << " already there" << endl;
	  }
      }
    else
      {
	m_last_module = module;
        
	retreive_cvs_infos (module);
      }
    
    /**
       Now retrieve all info fields :
    */

    Grep grep;

    grep.run (m_last_cvs_infos, "structure=");
    
    if (grep.result () != "")
      {
	structure_info = grep.result ();
	structure_info.replace ("structure=", "");
      }
    else
      {
	// This may happen for old protocol level < v1r1
	structure_info = "package";
      }
    
    grep.run (m_last_cvs_infos, "error=");
    
    if (grep.result () != "")
      {
	error_info = grep.result ();
	error_info.replace ("error=", "");
      }
    else
      {
	error_info = "";
      }
    
    grep.run (m_last_cvs_infos, "tags_top=");
    
    if (grep.result () != "")
      {
	tags_top_info = grep.result ();
	tags_top_info.replace ("tags_top=", "");
      }
    else
      {
	tags_top_info = "";
      }
    
    grep.run (m_last_cvs_infos, "tags=");
    
    if (grep.result () != "")
      {
	tags_info = grep.result ();
	tags_info.replace ("tags=", "");
      }
    else
      {
	tags_info = "";
      }
    
    grep.run (m_last_cvs_infos, "cvsversions_top=");
    
    if (grep.result () != "")
      {
	cvsversions_top_info = grep.result ();
	cvsversions_top_info.replace ("cvsversions_top=", "");
      }
    else
      {
	cvsversions_top_info = "";
      }
    
    grep.run (m_last_cvs_infos, "cvsversions=");
    
    if (grep.result () != "")
      {
	cvsversions_info = grep.result ();
	cvsversions_info.replace ("cvsversions=", "");
      }
    else
      {
	cvsversions_info = "";
      }
    
    grep.run (m_last_cvs_infos, "branches=");
    
    if (grep.result () != "")
      {
	branches_info = grep.result ();
	branches_info.replace ("branches=", "");
      }
    else
      {
	branches_info = "";
      }
    
    grep.run (m_last_cvs_infos, "subpackages=");
    
    if (grep.result () != "")
      {
	subpackages_info = grep.result ();
	subpackages_info.replace ("subpackages=", "");
      }
    else
      {
	subpackages_info = "";
      }
    
    grep.run (m_last_cvs_infos, "subprojects=");

    if (grep.result () != "")
      {
	subprojects_info = grep.result ();
	subprojects_info.replace ("subprojects=", "");
      }
    else
      {
	subprojects_info = "";
      }

    /**
       The CMTCVSTAGFILTER env. var. may contain a regexp that will exclude some tags
       from the answr of the CVS pluggin.
       The pattern is a regexp but it may also contain the <package> template
    */
    cmt_string tag_filter = CmtSystem::getenv ("CMTCVSTAGFILTER");
    
    if (tag_filter != "")
      {
	cmt_string package;
	CmtSystem::basename (module, package);
	
	cmt_string pattern = "<package>";
	
	tag_filter.replace_all (pattern, package);
	
	cmt_regexp exp (tag_filter);
	
	cmt_string text;
	
	filter_list (tags_top_info, exp);
	filter_list (tags_info, exp);
	filter_list (cvsversions_top_info, exp);
	filter_list (cvsversions_info, exp);
      }
  }
  
  void get_cvs_infos (const cmt_string& cvs_offset, const cmt_string& module)
  {
    cmt_string full_name;

    if (cvs_offset != "") 
      {
	full_name = cvs_offset;
	full_name += "/";
	while (full_name.find ("//") != cmt_string::npos)
	  {
	    full_name.replace_all ("//", "/");
	  }
      }

    full_name += module;

    get_cvs_infos_with_offset (full_name);
  }

  /**
     From a space-separated list of version tags, try to find one tag
     matching a given regular expression.

     o The first matching tag is returned into 'version'
     o Success is returned as function value.

  */
  bool match_version_request (const cmt_string& text, 
                              const cmt_regexp& version_exp,
                              cmt_string& version)
  {
    CmtSystem::cmt_string_vector vs;
      
    CmtSystem::split (text, " \t", vs);

    version = "";

    for (int i = 0; i < vs.size (); i++)
      {
	const cmt_string& vv = "^"+ vs[i] + "$";
        
	if (version_exp.match (vv))
	  {
	    version = vv;
	    return (true);
	  }
      }

    return (false);
  }

  void get_module (const cmt_string& offset,
		   const cmt_string& product,
		   cmt_string& module)
  {
    module = "";

    if (offset != "")
      {
	module = offset;
	module += "/"; // This is for CVS only thus we don't use the real separator.
	while (module.find ("//") != cmt_string::npos)
	  {
	    module.replace_all ("//", "/");
	  }
      }

    module += product;
  }

  bool get_version (const cmt_string& offset,
                    const cmt_string& product,
                    const cmt_string& version_request,
                    const cmt_string& module,
                    cmt_string& version,
                    bool& at_head)
  {
    Grep grep;
    cmt_string topversions;
    cmt_string versions;
    cmt_string requested_version = version_request;
    
    at_head = false;
            
    /**
     *   Try to figure out what is the effective version tag available
     *   for the requested version expressions (which may contain
     *   wild card)
     *
     *     the requested version may either be in the top tags (ie those
     *     corresponding to the same most recent CVS version number before
     *     the HEAD) or not. The returned at_head flag will show this.
     *
     *     then the requested product may either be located in the CVS repository
     *     under the offset or not, the returned module will contain the effective
     *     location where the requested version has been found.
     */
    
    if (m_verbose)
      {
	CmtMessage::info ("requesting cvs infos onto module " + module);
	//	cerr << "#CMT> requesting cvs infos onto module " << module << endl;
      }
    
    get_cvs_infos_with_offset (module);
    
    if (error_info != "")
      {
	versions = "";
	CmtError::set (CmtError::configuration_error, "Product " + product + " not found in ${CVSROOT}");
	//	CmtMessage::error ("Product " + product + " not found in ${CVSROOT}");
	//	cerr << "#CMT> Product " << product << " not found in ${CVSROOT}" << endl;
	return (false);
      }

    versions = tags_top_info;

    cmt_string v = version_request;
    
    if (version_request.find ("*") != cmt_string::npos)
      {
	v.replace_all ("*", ".*");
      }
    else
      {
	// this is an exact match to the end of the word since there is no wild card
	v += "$";
      }
    
    cmt_regexp version_exp (v);
    
    if (!match_version_request (versions, version_exp, version))
      {
	// We try on non-top versions

	versions = tags_info;

	if (!match_version_request (versions, version_exp, version))
	  {
	    version = requested_version;
	    int pos = 0;
	    if ((pos = version.find ("*")) != cmt_string::npos)
	      {
		//
		//  There was a wild card but the expression does not match
		// any of the existing tags in CVS.
		//  Things will be retreived from HEAD but we have to build
		// a reasonable version tag from the wild card expression.
		//  If the letter before the * was a digit, then simply remove
		// the * (v5* -> v5) otherwise add a zero (v5r* -> v5r0)
		//
		if (pos > 0)
		  {
		    char letter = version[pos-1];
		    
		    static const cmt_string digits = "0123456789";
		    
		    if (digits.find (letter) == cmt_string::npos)
		      {
			// "v5r*" -> "v5r0"
			version.replace ("*", "0");
		      }
		    else
		      {
			// "v5*" -> "v5"
			version.replace ("*", "");
		      }
		  }
		else
		  {
		    // The expression was simply "*" !!!
		    version = "v0";
		  }
	      }
	    at_head = true;
	  }
	else
	  {
	    at_head = false;
	  }
      }
    else
      {
	at_head = true;
      }
    
    /**
     *   Here we have at least one version matching the requested expression.
     */
    
    return (true);
  }

  bool do_need_version ()
  {
    bool need_version = false;

    if (structure_info == "project")
      {
	need_version = true;
	//CmtStructuringStyle style = Cmt::get_current_structuring_style ();
	//if (style == default_structuring_style)
	//{
	//   Use& current_use = Use::current ();
	//   if (current_use.get_strategy ("VersionDirectory"))
	//      need_version = true;
	//}
	//else if (style == with_version_directory)
	//    need_version = true;	  	       
	// cerr<<"need version"<<need_version<<endl;
      }
    else
      {
	CmtStructuringStyle style = Cmt::get_current_structuring_style ();
	if (style == default_structuring_style)
	  {
	    Use& current_use = Use::current ();
	    if (current_use.get_strategy ("VersionDirectory"))
	      need_version = true;
	  }
	else if (style == with_version_directory)
	  need_version = true;	        
      }

    return (need_version);
  }

  /**
     Take care of structuring style for packages and of project vs package conventions
  */
  cmt_string build_version_directory (const cmt_string& offset,
                                      const cmt_string& product,
                                      const cmt_string& version)
  {
    cmt_string dir = m_home_dir;
    
    if (m_checkout_dir != "")
      {
	// consider the usual -d option 
 
	dir += CmtSystem::file_separator ();
	dir += m_checkout_dir;
      }
    
    dir += CmtSystem::file_separator ();
    dir += offset;
    dir += CmtSystem::file_separator ();
    dir += product;

    if (do_need_version ())
      {
	dir += CmtSystem::file_separator ();
	dir += version;
      }
    
    CmtSystem::reduce_file_separators (dir);
    
    return (dir);
  }

  /**
     Wrapper to mkdir handling simulation and verbose options.
  */
  bool mkdir (const cmt_string& dir)
  {
    if (m_simulation)
      {
	CmtMessage::info ("Would create the " + dir + " directory");
	//	cerr << "#CMT> Would create the " << dir << " directory" << endl;
      }
    else
      {
	if (!CmtSystem::cd (dir))
	  {
	    if (m_verbose)
	      {
		CmtMessage::info ("About to mkdir " + dir);
		//		cerr << "#CMT> About to mkdir " << dir << endl;
	      }

	    CmtSystem::mkdir (dir);
	    if (!CmtSystem::cd (dir))
	      {
		CmtMessage::error ("# Cannot cd into the directory: " + dir);
		CmtMessage::error ("#---------------------------------------------------------");
		//		cerr << "# Error creating the directory :" << dir << endl;
		//		cerr << "#---------------------------------------------------------" << endl;
		return (false);
	      }
	  }
      }
    return (true);
  }

  /**
     When running cmt cvs commands, we stand by definition outside of any existing 
     package context. Thus it's likely that CMTPATH are not completely defined.
     This function manually prepends CMTPATH entries to the environment variable.
  */
  void add_cmtpath (const cmt_string& dir)
  {
    return;
    static cmt_string CMTPATH;

    cmt_string cmtpath = Symbol::get_env_value ("CMTPATH");

    if (cmtpath.find (dir) == cmt_string::npos)
      {
	CMTPATH = dir;
	CMTPATH += ":";
	CMTPATH += cmtpath;

	CmtSystem::putenv ("CMTPATH", CMTPATH);
      }

    if (m_verbose)
      {
	CmtMessage::info ("CMTPATH=" + CmtSystem::getenv ("CMTPATH"));
	//	cerr << "#CMT> CMTPATH=" << CmtSystem::getenv ("CMTPATH") << endl;
      }
  }

  /**
     Specific checkout of one project
  */
  bool really_checkout_project_contents (const cmt_string& offset,
					 const cmt_string& project,
					 const cmt_string& version,
					 const cmt_string& tag,
					 const cmt_string& module,
					 const cmt_string& basedir,
					 bool at_head,
					 const cmt_string& currentdir)
  {
    cmt_string dir = currentdir;

    CmtMessage::info ("get project files into " + dir);
    //    cerr << "  # get project files into " << dir << endl;


    cmt_string version_dir = version;
    if (!mkdir (version_dir))
    {
      CmtError::set (CmtError::execution_error, "Cannot create " + version_dir);
      return (false);	
    }

    dir += CmtSystem::file_separator ();
    dir += version_dir;
    
    cmt_string command = "cvs -f -Q co -P ";
    if (!at_head)
      {
	command += "-r ";
	command += (tag != "") ? tag : version;
      }

    command += " -d cmt ";

    command += " ";
    command += module;
    command += "/cmt";

    command += CmtSystem::command_separator ();

    command += " cvs -f update -l .";

    execute_and_retry (command, "Error getting project CMT contents");

    return (true);
  }
  /**
     Construct CVS management files in the top directory. This is needed
     if the top directory of a product is empty. (In this case the
     co -l results in nothing)
  */
  void make_management_files (const cmt_string& module,
			      const cmt_string& entries_text)
  {
    if (!CmtSystem::test_directory ("CVS"))
      {
	/**
	 * The CVS repository had not been created (this is generally
	 * due to the lack of top files)
	 */

	if (!mkdir ("CVS")) return;

	CmtSystem::cd ("..");

	cmt_string s;
	
	// Let's create first the CVS/Root file.
	
	CmtSystem::get_cvsroot (s);
	s += "\n";
	
	cmt_string f;
	
	f = "CVS";
	f += CmtSystem::file_separator ();
	f += "Root";
	
	if (m_simulation)
	  {
	    CmtMessage::info ("Would fill in the CVS/Root file with\n" + s);
	    //	    cerr << "#CMT> Would fill in the CVS/Root file with " << endl;
	    //	    cerr << s << endl;
	  }
	else
	  {
	    if (m_verbose)
	      {
		CmtMessage::info ("Fill in the CVS/Root file with\n" + s);
		//		cerr << "#CMT> Fill in the CVS/Root file with " << endl;
		//		cerr << s << endl;
	      }
	    s.write (f);
	  }
	
	// Now we create the CVS/Repository file
	
	f = "CVS";
	f += CmtSystem::file_separator ();
	f += "Repository";
	
	CmtSystem::get_cvsroot (s);
	if (s[0] == ':')
	  {
	    int pos = s.find (1, ":");
	    s.erase (0, pos+1);
	    pos = s.find (0, ":");
	    s.erase (0, pos+1);
	  }
	s += "/";
	s += module;
	s += "\n";
	
	if (m_simulation)
	  {
	    CmtMessage::info ("Would fill in the CVS/Repository file with\n" + s);
	    //	    cerr << "#CMT> Would fill in the CVS/Repository file with " << endl;
	    //	    cerr << s << endl;
	  }
	else
	  {
	    if (m_verbose)
	      {
		CmtMessage::info ("Fill in the CVS/Repository file with\n" + s);
		//		cerr << "#CMT> Fill in the CVS/Repository file with " << endl;
		//		cerr << s << endl;
	      }
	    s.write (f);
	  }
      }
    
    if (m_simulation)
      {
	CmtMessage::info ("Would write the top CVS/Entries file with\n" + entries_text);
	//	cerr << "#CMT> Would write the top CVS/Entries file with " << endl;
	//	cerr << entries_text << endl;
      }
    else
      {
	cmt_string entries_file_name;

	entries_file_name = "CVS";
	entries_file_name += CmtSystem::file_separator ();
	entries_file_name += "Entries";
    
	cmt_string text;

	if (!text.read (entries_file_name))
	  {
	    // This happens when there were no top files
	  }

	text += entries_text;

	// Now the CVS/Entries is ready to be created.
	if (m_verbose)
	  {
	    CmtMessage::info ("Fill in the top CVS/Entries file with\n" + text);
	    //	    cerr << "#CMT> Fill in the top CVS/Entries file with " << endl;
	    //	    cerr << text << endl;
	  }

	text.write (entries_file_name);
      }
  }


  /**
     Specific checkout of one package

     1) get top files (no directories)
     2) construct the directory structure (with or without version directory)
     3) build the CVS/Entries file for subdirs and co individual subdirs
     4) write the CVS management files if CVS did not do it.
  */
  bool really_checkout_package_contents (const cmt_string& offset,
					 const cmt_string& package,
					 const cmt_string& version,
					 const cmt_string& module,
					 const cmt_string& basedir,
					 bool at_head,
					 const cmt_string& currentdir)
  {
    cmt_string dir = currentdir;

    CmtMessage::info ("get top files ");
    //    cerr << "  # get top files " << endl;
            
    cmt_string command = "cvs -f -Q co -l ";
    if (!at_head)
      {
	command += "-r ";
	command += version;
      }

    bool need_version = do_need_version ();

    if (need_version)
      {
	command += " -d ";
	command += version;
      }
    else
      {
	command += " -d ";
	command += package;

	// Must stand just above the package directory
	CmtSystem::cd ("..");
	CmtSystem::dirname (dir, dir);
      }
    
    command += " ";
    command += module;
    
    execute_and_retry (command, "Error getting package CMT contents");

    if (need_version)
      {
	if (!mkdir (version))
	  {
	    CmtError::set (CmtError::execution_error, "Cannot create " + version);
	    return (false);	
	  }
	dir += CmtSystem::file_separator ();
	dir += version;
      }
    else
      {
	if (!mkdir (package))
	{
	  CmtError::set (CmtError::execution_error, "Cannot create " + package);
	  return (false);	
	}
	
	dir += CmtSystem::file_separator ();
	dir += package;
      }
    
    if (m_verbose)
      {
	CmtMessage::info ("Now getting subdirectories pwd=" + CmtSystem::pwd ()
			  + " dir=" + dir);
	//	cerr << "#CMT> Now getting subdirectories pwd=" << CmtSystem::pwd () << " dir=" << dir << endl;
      }
    
    cmt_string branches = CmtSystem::getenv ("CMTCVSBRANCHES");
    
    if (branches == "")
      {
	branches = branches_info;
      }
    
    CmtSystem::cmt_string_vector branch_vector;
    
    CmtSystem::split (branches, " \t", branch_vector);
      
    cmt_string text = "";
    
    command = "";

    if (!CmtSystem::test_directory ("CVS"))
      {
	int i;
	for (i = 0; i < branch_vector.size (); i++)
	  {
	    cmt_string& branch = branch_vector[i];	
	    if (i > 0)
	      {
		command += CmtSystem::command_separator ();
	      }
	
	    command += "cvs -f -Q co ";
	
	    if (!at_head)
	      {
		command += "-r ";
		command += version;
	      }
	
	    command += " -d ";
	    command += branch;
	    command += " ";
	    command += module;
	    command += "/";    // CVS uses the '/' notation on all platforms!!
	    command += branch;
	
	    text += "D/";
	    text += branch;
	    text += "////\n";
	  } 
        execute_and_retry (command, "Error getting package contents");

        make_management_files (module, text);

        if (need_touch_files)
	  {  
    
            CmtMessage::info ("udapte the file timestamps");                 
	    //            cerr << "# --> udapte the file timestamps" << endl;                 
            for (i = 0; i < branch_vector.size (); i++)
	      {	
		cmt_string& branch = branch_vector[i];    
                CmtSystem::cmt_string_vector& list = CmtSystem::scan_dir (branch);
                int j;
                for (j = 0; j < list.size (); j++)
		  if (CmtSystem::test_file(list[j]))                
		    CmtSystem::touch_file (list[j]);
	      }
	  }        
        return (true);   
      }
 
    command += "cvs -Q update -d ";

    if (!at_head)
      {
        command += "-r ";
        command += version;
      }

    int i;
    
    for (i = 0; i < branch_vector.size (); i++)
      {
	cmt_string& branch = branch_vector[i];
	if (branch != "CVS")
	  {   
	    command += " ";
	    command += branch;
	  }
      }

    command += CmtSystem::command_separator ();

    command += "cvs update -l .";
    
    execute_and_retry (command, "Error getting package contents");

    if (need_touch_files)
      {  
    
	CmtMessage::info ("udapte the file timestamps");                 
	//        cerr << "# --> udapte the file timestamps" << endl;                 
        for (i = 0; i < branch_vector.size (); i++)
	  {	
	    cmt_string& branch = branch_vector[i];    
            CmtSystem::cmt_string_vector& list = CmtSystem::scan_dir (branch);
            int j;
            for (j = 0; j < list.size (); j++)
	      if (CmtSystem::test_file(list[j]))                
		CmtSystem::touch_file (list[j]);
	  }
      }        
    return (true);
  }

  /**
     Effective checkout of a package or a project
  */
  bool really_checkout (const cmt_string& offset,
			const cmt_string& product,
			const cmt_string& version,
			const cmt_string& tag,
			const cmt_string& module,
			const cmt_string& basedir,
			bool at_head)
  {
    cmt_string dir = basedir;
    cmt_string out;
    
    cerr << "# ================= working on " << structure_info << " " << product 
	 << " version " << version;

    if (at_head) cerr << " (At head) ";

    cmt_string full_offset;

    full_offset = m_cvs_offset;
    full_offset += offset;

    cmt_string echo_ppath;
        
    if (offset != "")
      {
	echo_ppath = " path ";
	echo_ppath += offset;
      }
    
    cerr << echo_ppath << " in " << dir << endl;

    if (do_need_version ())
      {
	// Move back to the product name.
	CmtSystem::dirname (dir, dir);
      }

    if (!mkdir (dir))
      {
	CmtError::set (CmtError::execution_error, "Cannot create " + dir);
	return (false);
      }
	
    if (structure_info == "package")
      {
	really_checkout_package_contents (offset,
					  product,
					  version,
					  module,
					  basedir,
					  at_head,
					  dir);
      }
    else if (structure_info == "project")
      {
	really_checkout_project_contents (offset,
					  product,
					  version,
					  tag,
					  module,
					  basedir,
					  at_head,
					  dir);
      }

    return (true);
  }

  /**
     Find the most appropriate effective version directory corresponding to the 
     specified version expression.
     The specified expression may contain wildcards (in the file manager sense). This
     FME is first converted into a RE then a directory search is performed.
     An empty string is returned if no match is found.
  */
  cmt_string find_matching_version (const cmt_string& expression)
  {
      
    cmt_string result;     
    
    //
    // Here expression takes the form
    //   <some path>/<expression with wild-card>
    //

    cmt_string dir;
    CmtSystem::dirname (expression, dir);
    dir += CmtSystem::file_separator ();
    
    cmt_string version;
    CmtSystem::basename (expression, version);

    if (version.find ("*") == cmt_string::npos)
      {
	// there is no wildcarding here. A simple test is enough.
	if (CmtSystem::test_directory (expression))
	  {
	    result = version;
	  }
      }
    else
      {
	version.replace ("*", ".*");
	
	cmt_regexp exp (version);
	
	CmtSystem::cmt_string_vector list;
	
	CmtSystem::scan_dir (dir, exp, list);
	
	if (list.size () > 0)
	  {
	    result = list[0];
	    
	    CmtSystem::basename (result, result);
	  }
      }
    
    return (result);
  }
  
  /**
   *   We provide a path to a requirements file. From it we read the use 
   *  statements, and we try to checkout the corresponding packages.
   *
   *   A boolean return tells if any recursion occurred.
   */
  bool checkout_from_requirements (const cmt_string& requirements_path)
  {
    static cmt_regexp expression ("^[ \t]*use[ \t]");

    cmt_string text;

    text.read (requirements_path);

    RecursivePass1 p1;
    p1.run (text, expression);

    bool result = (p1.result () != "");
    RecursivePass2 p2 (*this);
    p2.run (p1.result ());

    return (result);
  }

  /**
   *   We provide a path to a project file. From it we read the use 
   *  statements, and we try to checkout the corresponding projects.
   */
  void checkout_from_project_file (const cmt_string& file_name)
  {
    static cmt_regexp expression ("^[ \t]*use[ \t]");

    cmt_string text;

    text.read (file_name);

    CvsImplementation& me = *this;
    CvsImplementation saved;
    saved = me;
    cmt_string here = CmtSystem::pwd ();

    RecursivePass3 p3;
    p3.run (text, expression);

    RecursivePass4 p4 (*this);
    p4.run (p3.result ());

    Grep grep;

    grep.run (text, "container");
    cmt_string container = grep.result ();

    if (container != "")
      {
	static cmt_regexp container_expression ("^[ \t]*container[ \t]");

	add_cmtpath (here);
	
	CmtMessage::info ("now getting project packages from the " + container + " " + here);
	//	cerr << "  # --> now getting project packages from the " << container << " " << here << endl;

	CmtSystem::cd (here);

	RecursivePass1 p1;
	p1.run (text, container_expression);

	RecursivePass2 p2 (*this);

	m_home_dir = CmtSystem::pwd ();
	p2.run (p1.result ());
      }

    CmtSystem::cd (here);
    me = saved;
  }

  /**
     Check whether a given directory structure matches an expected product structure
     given by the structure info obtained from the most recent request to the CVS pluggin
  */
  bool check_structure (const cmt_string& dir, const cmt_string& version)
  {
    bool result = false;
    if (!CmtSystem::test_directory (dir))
      {
	return (false);
      }

    if (structure_info == "package")
      {
	// Check if it is a true CMT package.	
	cmt_string file_name;
	
	file_name = dir;
	file_name += CmtSystem::file_separator ();
	file_name += "cmt";
	file_name += CmtSystem::file_separator ();
	file_name += "requirements";
	
	if (CmtSystem::test_file (file_name))
	  {	  
	    if (!do_need_version ())
	      {
		// open the version.cmt file and compare the version
		cmt_string current_version;
		cmt_string version_file = dir;
		version_file += CmtSystem::file_separator ();
		version_file += "CVS";
		version_file += CmtSystem::file_separator ();
		version_file += "Tag"; 
		current_version.read (version_file);               
		//cerr <<version_file<<", "<<current_version<<", "<<"N"+version+"\n"<<endl;
		return current_version=="N"+version+"\n";	            
	      }   
	    else
	      result = true;
	  }
	else
	  {

	    file_name = dir;
	    file_name += CmtSystem::file_separator ();
	    file_name += "mgr";
	    file_name += CmtSystem::file_separator ();
	    file_name += "requirements";
	    
	    if (CmtSystem::test_file (file_name))
	      {
		if (!do_need_version ())
		  {
		    // open the version.cmt file and compare the version
		    cmt_string current_version;
		    cmt_string version_file = dir;
		    version_file += CmtSystem::file_separator ();
		    version_file += "CVS";
		    version_file += CmtSystem::file_separator ();
		    version_file += "Tag"; 
		    current_version.read (version_file);               
		    //cerr <<version_file<<", "<<current_version<<", "<<"N"+version+"\n"<<endl;
		    return current_version=="N"+version+"\n";	            
		  }   
	       	else
		  result = true;
	      }
	  }
      }
    else if (structure_info == "project")
      {
	cmt_string file_name;
	
	file_name = dir;
	file_name += CmtSystem::file_separator ();
	file_name += "cmt";
	file_name += CmtSystem::file_separator ();
	file_name += "project.cmt";
	
	if (CmtSystem::test_file (file_name))
	  {
	    result = true;
	  }
      }

    return (result);
  }

  /**
     Internal call from the initial do_checkout or from recursive passes
     Prepare the directory structure for the checkout
     Do the checkout
     Check if everything was done properly, if a package or a project has been created
     If needed recurse from the requirements or project file

     For projects there may be two different specifications for
     - the version directory
     - the CVS tag
  */
  void do_checkout_phase2 (const cmt_string& offset,
			   const cmt_string& product,
			   const cmt_string& specified_version,
			   const cmt_string& tag)
  {
    if (m_verbose)
      {
	CmtMessage::info ("do_checkout_phase2> offset=" + offset
			  + " " + structure_info + "=" + product
			  + " specified_version=" + specified_version
			  + " tag=" + tag
			  + " pwd=" + CmtSystem::pwd ());
	/*
	cerr << "#CMT> do_checkout_phase2> offset=" << offset
	     << " " << structure_info << "=" << product
	     << " specified_version=" << specified_version
	     << " tag=" << tag
	     << " pwd=" << CmtSystem::pwd ()
	     << endl;
	*/
      }
    
    cmt_string version = specified_version;
    cmt_string empty;
    cmt_string full_offset;

    full_offset = m_cvs_offset;
    full_offset += offset;
    
    cmt_string echo_ppath;
    
    if (offset != "")
      {
	echo_ppath = " path ";
	echo_ppath += offset;
      }
    
    if (version == "")
      {
	CmtMessage::warning ("================= No version specified for " + structure_info + " " + product);
	//	cerr << "# ================= No version specified for " << structure_info << " " << product << endl;
	return;
      }
    
    //
    //  First make an attempt to locate the specified version of
    //  this product "as-it-is" in the work area.
    //   Since 'version' may contain wild-card, it's likely that
    //  we should not simply use CmtSystem::test_directory but
    //  use the wild-card search.
    //
    
    cmt_string dir;
    
    dir = build_version_directory (offset, product, version);
    
    bool recursive = m_recursive;
    
    cmt_string effective_version = "";
    if (do_need_version ())
      {
	/* Important this part should be enhanced ASAP 
	   and deal with many other cases.... arghhhh !!
	*/    
        effective_version = find_matching_version (dir);         
      }


         
    cmt_string module;
    get_module (full_offset, product, module);

    cmt_string cvs_tag = (tag != "") ? tag : version;
    bool at_head = false;

    if (effective_version != "")
      {
	version = effective_version;
      }
    else
      {
	//
	// get_version attempts to find the most appropriate version
	// tag matching the specification FROM the repository. However,
	// we should take into account situations where some versions have
	// already been checked out, in which case they might be sufficient
	// (or preferred?)
	//
	
	if (cvs_tag.find ("*") != cmt_string::npos)
	  {
	    CmtMessage::warning ("================= " + structure_info + " " + product 
				 + " version " + cvs_tag + echo_ppath 
				 + " has wild cards and will not be considered.");
	    /*
	    cerr << "# ================= " << structure_info << " " << product 
		 << " version " << cvs_tag << echo_ppath 
		 << " has wild cards and will not be considered." << endl;
	    */
	    return;
	  }
	
	if (!get_version (full_offset, product, cvs_tag, module,
			  cvs_tag, at_head))
	  {
	    return;
	  }
	
	if (m_head)
	  {
	    m_head = false;
	    
	    at_head = true;
	  }
	else
	  {
	    at_head = false;
	  }
	
	//
	// Make a second try after having selected a CVS tag from all the
	// available tags compatible with the specified version
	//

	if (tag == "")
	  {
	    // If tag was not specified, then the version directory has to match the CVS tag
	    // Otherwise the original version specification is kept for the directory.
	    version = cvs_tag;
	    dir = build_version_directory (offset, product, version);
	  }
      }
    
    if (check_structure (dir, version))
      {
	CmtMessage::info ("================= " + structure_info + " " + product 
			  + " version " + version + echo_ppath 
			  + " already installed.");	
	/*
	cerr << "# ================= " << structure_info << " " << product 
	     << " version "            << version        << echo_ppath 
	     << " already installed."  << endl;	
	*/
	recursive = false;
      }
    else
      {
	//
	// Now we can say that we have to perform the real checkout.
	// 
	
	if (!really_checkout (offset, product, version, cvs_tag, module, dir, at_head))
	  {
	    CmtMessage::error ("bad return from really_checkout_product");
	    //	    cerr << "# bad return from really_checkout_product" << endl;
	    return;
	  }
      }

    //
    //  Now reach the newly checked out product.
    //
    
    if (m_simulation)
      {
	CmtMessage::info (structure_info + " directory not really created " + dir);
	//	cerr << "#CMT> " << structure_info << " directory not really created " << dir << endl;
      }
    else if (structure_info == "package")
      {
	if (!CmtSystem::cd (dir))
	  {
	    CmtError::set (CmtError::configuration_error,
			   "Package directory not created " + dir);
	    //	    CmtMessage::error ("Package directory not created " + dir);
	    //	    cerr << "#CMT> Package directory not created " << dir << endl;
	    return;
	  }
	
	// Check if it is a true CMT package.
	
	cmt_string file_name;
	
	file_name = "cmt";
	file_name += CmtSystem::file_separator ();
	file_name += "requirements";
	
	if (CmtSystem::test_file (file_name))
	  {
	    dir += CmtSystem::file_separator ();
	    dir += "cmt";
	    CmtSystem::cd ("cmt");
	    
	    if (!do_need_version ())
	      {
		cmt_string text = version;
		text += "\n";
		//		text.write ("version.cmt");
		text.write (Package::get_version_file_name ());
	      }
	  }
	else
	  {
	    file_name = "mgr";
	    file_name += CmtSystem::file_separator ();
	    file_name += "requirements";
	    
	    if (CmtSystem::test_file (file_name))
	      {
		dir += CmtSystem::file_separator ();
		dir += "mgr";
		CmtSystem::cd ("mgr");
	      }
	    else
	      {
		CmtMessage::error (product + " not a CMT package");
		//		cerr << "# " << product << " not a CMT package" << endl;
		return;
	      }
	  }
	
	if (recursive)
	  {
	    cmt_string here = CmtSystem::pwd ();

	    bool did_recurse = checkout_from_requirements ("requirements");

	    CmtSystem::cd (here);

	    if (did_recurse) 
	      {
		if (no_config==false)	          
		  {
		    if (do_need_version ())
		      execute ("cmt -quiet broadcast cmt -with_version_directory -quiet config");
		    else
		      execute ("cmt -quiet broadcast cmt -without_version_directory -quiet config");
		  }
	      }
	    else
	      {
		if (no_config==false)
		  {
		    if (do_need_version ())
		      execute ("cmt -with_version_directory -quiet config");
		    else
		      execute ("cmt -without_version_directory -quiet config");
		  }
	      }
	  }
	else
	  {
	    if (no_config==false)
	      {
		if (do_need_version ())
		  execute ("cmt -with_version_directory -quiet config");
		else
		  execute ("cmt -without_version_directory -quiet config");
	      }
	  }
      }
    else if (structure_info == "project")
      {
	
	if (m_verbose)
	  {
	    CmtMessage::info ("dir=" + dir);
	    //	    cerr << "#CMT> dir=" << dir << endl;
	  }
	
	if (!CmtSystem::cd (dir))
	  {
	    CmtError::set (CmtError::configuration_error,
			   "Project directory not created " + dir);
	    //	    CmtMessage::error ("Project directory not created " + dir);
	    //	    cerr << "#CMT> Project directory not created " << dir << endl;
	    return;
	  }
	
	cmt_string file_name;
	
	file_name = "cmt";
	file_name += CmtSystem::file_separator ();
	file_name += "project.cmt";
	
	if (!CmtSystem::test_file (file_name))
	  {
	    CmtMessage::error (product + " not a CMT project");
	    //	    cerr << "# " << product << " not a CMT project" << endl;
	    return;
	  }
	
	if (recursive)
	  {
	    checkout_from_project_file (file_name);
	  }

	CmtMessage::info ("================= Project " + product + " completed");
	//	cerr << "# ================= Project " << product << " completed" << endl;

      }
  }
  
  /**
     Top level of the checkout operation, initiated from the command line arguments

     Construct an history of the checkouts to avoid duplicating the
     checkouts during the recursivity

     Eventually 
     o perform the cmt config for packages.

  */
  void do_checkout_phase1 (const cmt_string& module, 
			   const cmt_string& version_dir, 
			   const cmt_string& version_tag)
  {
    if (m_verbose)
      {
	CmtMessage::info ("checkout phase1 module=" + module
			  + " version_dir=" + version_dir + " version_tag=" + version_tag);
	//	cerr << "#CMT> checkout phase1 module=" << module
	//	     << " version_dir=" << version_dir << " version_tag=" << version_tag << endl;
      }

    add_cmtpath (m_home_dir);

    History& h = History::instance ();

    h.clear ();

    if (module == "")
      {
	if (m_verbose)
	  {
	    CmtMessage::info ("Missing module name");
	    //	    cerr << "#CMT> Missing module name" << endl;
	  }
	return;
      }

    cmt_string offset;
    cmt_string product;
    cmt_string version;
    cmt_string tag;
    
    {
      cmt_string m;
      m = m_cvs_offset;
      m += module;
        
      get_cvs_infos_with_offset (m);
        
      if (error_info != "")
	{
	  CmtError::set (CmtError::configuration_error, error_info);
	  //CmtMessage::error (error_info);
	  //	  cerr << error_info << endl;
	  return;
	}
    }

    if (version_tag == "")
      {
	// No version tag is specified
	// we need to discard all CVS branches
	//
	cmt_string versions = "";

	if (cvsversions_top_info != "")
	  {
	    versions = cvsversions_top_info;
	    versions += " ";
	  }

	versions += cvsversions_info;

	if (m_verbose)
	  {
	    CmtMessage::info ("cumulated versions " + versions);
	    //	    cerr << "#CMT> cumulated versions " << versions << endl;
	  }

	static CmtSystem::cmt_string_vector words;

	// Get all <tag>:<version> items

	CmtSystem::split (versions, " \t", words);

	for (int i = 0; i < words.size (); i++)
	  {
	    // split to get <tag> and <version> separately

	    static CmtSystem::cmt_string_vector vts;
	    CmtSystem::split (words[i], ":", vts);

	    cmt_string t = vts[0];
	    cmt_string v = vts[1];

	    // Split to count the number of items for the CVS version

	    static CmtSystem::cmt_string_vector ds;
	    CmtSystem::split (v, ".", ds);

	    if ((ds.size () == 2) || (v == "1.1.1.1"))
	      {
		tag = t;
		break;
	      }
	  }
      }
    else
      {
	tag = version_tag;
      }

    version = (version_dir == "") ? tag : version_dir;

    CmtSystem::dirname (module, offset);
    CmtSystem::basename (module, product);
    
    cmt_string top_dir;
        
    top_dir = m_home_dir;
    top_dir += CmtSystem::file_separator ();
    top_dir += m_checkout_dir;
    top_dir += CmtSystem::file_separator ();
    top_dir += offset;
    top_dir += CmtSystem::file_separator ();
    top_dir += product;
    top_dir += CmtSystem::file_separator ();
    top_dir += version;

    CmtSystem::reduce_file_separators (top_dir);

    if (m_verbose)
      {
	CmtMessage::info ("about to checkout " + structure_info
			  + " " + product + " version " + version + " into " + top_dir);
	//	cerr << "#CMT> about to checkout " << structure_info
	//	     << " " << product << " version " << version << " into " << top_dir << endl;
      }

    static const cmt_string empty;
    do_checkout_phase2 (offset, product, version, tag);

    if (m_simulation) return;

    if (!CmtSystem::cd (top_dir)) return;

    if (structure_info == "project")
      {
	cmt_string file_name;
    
	file_name = "cmt";
	file_name += CmtSystem::file_separator ();
	file_name += "project.cmt";
	
	if (!CmtSystem::test_file (file_name))
	  {
	    CmtError::set (CmtError::configuration_error, product +
			   " was not properly checked out and is missing its cmt/project.cmt file");
	    //CmtMessage::error (product + " was not properly checked out and is missing its cmt/project.cmt file");
	    //	    cerr << "# " << product << " was not properly checked out and is missing its cmt/project.cmt file" << endl;
	    return;
	  }
      }
    else
      {
	cmt_string file_name;
    
	file_name = "cmt";
	file_name += CmtSystem::file_separator ();
	file_name += "requirements";
	
	if (CmtSystem::test_file (file_name))
	  {
	    top_dir += CmtSystem::file_separator ();
	    top_dir += "cmt";
	    CmtSystem::cd ("cmt");
	  }
	else
	  {
	    file_name = "mgr";
	    file_name += CmtSystem::file_separator ();
	    file_name += "requirements";
	    
	    if (CmtSystem::test_file (file_name))
	      {
		top_dir += CmtSystem::file_separator ();
		top_dir += "mgr";
		CmtSystem::cd ("mgr");
	      }
	    else
	      {
		CmtError::set (CmtError::configuration_error, product +
			       " was not properly checked out and is missing its cmt/requirements file");
		//		CmtMessage::error (product + " was not properly checked out and is missing its cmt/requirements file");
		//		cerr << "# " << product << " was not properly checked out and is missing its cmt/requirements file" << endl;
		return;
	      }
	  }
	
	if (m_verbose)
	  {
	    CmtMessage::info ("product " + product + " has been checked out");
	    //	    cerr << "#CMT> product " << product << " has been checked out" << endl;
	  }
	
	/*
	 * This is already done in do_checkout_phase2 () 
	 *
	if (!m_recursive)
	  {
	    if (no_config==false)
	      execute ("cmt -quiet config");
	  }
	*/
      }
  }

  void help ()
  {
    cerr << "Usage:" << endl;
    cerr << "> cd <some work area>" << endl;
    cerr << "> cmt co | checkout [modifier]... <package|project>..." << endl;
    cerr << "" << endl;
    cerr << "   modifier :" << endl;
    cerr << "   -l        Do not process used packages (default)." << endl;
    cerr << "   -R        Process used products recursively." << endl;
    cerr << "   -r rev    Check out version tag. (is sticky)" << endl;
    cerr << "   -vd dir   Use this version directory instead of CVS tag." << endl;
    cerr << "   -d dir    Check out into dir instead of module name." << endl;
    cerr << "   -o offset Offset in the CVS repository" << endl;
    cerr << "   -requirements <requirements file path>  Check out packages referenced in this requirements file" << endl;
    cerr << "   -t        Change file timestamps to the date of checkout" << endl;
    cerr << "   -n        simulation mode on" << endl;
    cerr << "   -no_config  Disable config step after checkout" << endl;    
    cerr << "   -v        verbose mode on" << endl;
    cerr << "   -rs suffix  Same as -r <packagename|projectname>suffix" << endl;
    cerr << "   --help    print this help" << endl;
    cerr << "" << endl;
    cerr << "(Modifiers are recognised anywhere on the command line.)" << endl;
    cerr << "" << endl;
    cerr << "> cmt cvstags <package|project>" << endl;
    cerr << "> cmt cvssubpackages <directory>" << endl;
    cerr << "> cmt cvssubprojects <directory>" << endl;
    cerr << "" << endl;
  }

  /**
     Implementation of the cmt cvstags
     Get the CVS tags of a module
  */
  void tags (const CmtSystem::cmt_string_vector& arguments)
  {
    if (arguments.size () < 1)
      {
	help ();
	return;
      }
    
    if (CmtSystem::getenv ("CVSROOT") == "")
      {
	CmtMessage::error ("Please set CVSROOT first !");
	//	cerr << "# Please set CVSROOT first !" << endl;
	return;
      }
    
    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
    if (m_cvs_offset != "") 
      {
	m_cvs_offset += "/";
	m_cvs_offset.replace_all ("//", "/");
      }
    
    bool all = false;
    
    for (int arg = 0; arg < arguments.size (); arg++)
      {
	const cmt_string& option = arguments[arg];
	
	if (option == "-all")
	  {
	    all = true;
	  }
	else
	  {
	    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), option);
	    
	    if (error_info != "")
	      {
		CmtMessage::error (error_info);
		//		cerr << error_info << endl;
	      }
	    else
	      {
		cmt_string tags;
		
		if (all)
		  {
		    tags = cvsversions_top_info;
		    tags += " ";
		    tags += cvsversions_info;
		  }
		else
		  {
		    tags = tags_top_info;
		    tags += " ";
		    tags += tags_info;
		  }
		
		CmtSystem::cmt_string_vector v;
		
		CmtSystem::split (tags, " \t", v);
		for (int i = 0; i < v.size (); i++)
		  {
		    const cmt_string& s = v[i];
		    cout << s << endl;
		    //		    cerr << s << endl;
		  }
	      }
	  }
      }
  }

  /**
     Implementation of the cmt cvsbranches
     Get the subdirs of a module that are not subackages
  */
  void branches (const cmt_string& module)
  {
    cmt_string out;
    
    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), module);
    
    if (error_info != "")
      {
	CmtMessage::error (error_info);
	//	cerr << error_info << endl;
      }
    else
      {
	cout << branches_info << endl;
	//	cerr << branches_info << endl;
      }
  }

  /**
     Implementation of the cmt cvssubpackages
     Get the subdirs of a module that ARE CMT subpackages
  */
  void subpackages (const cmt_string& module)
  {
    cmt_string out;
    
    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
    
    if (error_info != "")
      {
	CmtMessage::error (error_info);
	//	cerr << error_info << endl;
      }
    else
      {
	cout << subpackages_info << endl;
	//	cerr << subpackages_info << endl;
      }
  }
  
  /**
     Implementation of the cmt cvssubrojects
     Get the subdirs of a module that ARE CMT projects
  */
  void subprojects (const cmt_string& module)
  {
    cmt_string out;
    
    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
    
    if (error_info != "")
      {
	CmtMessage::error (error_info);
	//	cerr << error_info << endl;
      }
    else
      {
	cout << subprojects_info << endl;
	//	cerr << subprojects_info << endl;
      }
  }

  /**
     Implementation of the cmt checkout
     Parse the arguments
     Then call do_checkout for each argument
  */
  void checkout (const CmtSystem::cmt_string_vector& arguments)
  {
    if (arguments.size () < 1)
      {
	help ();
	return;
      }
    
    if (CmtSystem::getenv ("CVSROOT") == "")
      {
	CmtMessage::error ("Please set CVSROOT first !");
	//	cerr << "# Please set CVSROOT first !" << endl;
	return;
      }
    
    m_home_dir = CmtSystem::pwd ();
    m_checkout_dir = "";
    m_version_dir = "";
    m_cvs_offset = "";

    //cmt_string module;
    
    m_recursive      = false;
    need_touch_files = false;
    no_config        = false;
    
    bool need_version_tag = false;
    cmt_string version_tag;

    bool need_version_tag_suffix = false;
    cmt_string version_tag_suffix;

    CmtSystem::cmt_string_vector modules;
    
    
    bool need_checkout_dir = false;
    bool need_cvs_offset = false;
    bool need_requirements_file = false;
    bool need_version_dir = false;
    
    m_simulation = false;
    //m_verbose = true;
    m_verbose = false;
    
    m_head = true;
    
    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
    if (m_cvs_offset != "") 
      {
	m_cvs_offset += "/";
	m_cvs_offset.replace_all ("//", "/");
      }
    
    for (int arg = 0; arg < arguments.size (); arg++)
      {
	const cmt_string& option = arguments[arg];
	
	if (need_version_tag)
	  {
	    need_version_tag = false;
	    
	    if (option == "HEAD")
	      {
		m_head = true;
	      }
	    else
	      {
		version_tag = option;
	      }
	  }
	else if (need_checkout_dir)
	  {
	    need_checkout_dir = false;
	    m_checkout_dir = option;
	  }
	else if (need_version_dir)
	  {
	    need_version_dir = false;
	    m_version_dir = option;
	  }
	else if (need_cvs_offset)
	  {
	    need_cvs_offset = false;
	    m_cvs_offset = option;
	    m_cvs_offset += '/';
	    m_cvs_offset.replace_all ("//", "/");
	  }
	else if (need_requirements_file)
	  {
	    need_requirements_file = false;
	    m_head = false;
	    checkout_from_requirements (option);
	  }
	else if (need_version_tag_suffix)
	  {
	    need_version_tag_suffix = false;
	    version_tag_suffix = option;
	  }
	else
	  {
	    if (option == "-R")
	      {
		m_recursive = true;
	      }
	    else if (option == "-t")
	      {   
		need_touch_files = true;
	      }  
	    else if (option == "-l")
	      {
		m_recursive = false;
	      }
	    else if (option == "-r")
	      {
		need_version_tag = true;
		m_head = false;
	      }
	    else if (option == "-d")
	      {
		need_checkout_dir = true;
	      }
	    else if (option == "-o")
	      {
		need_cvs_offset = true;
	      }
	    else if (option == "-n")
	      {
		m_simulation = true;
	      }
	    else if (option == "-no_config")
	      {
		no_config = true;
	      }	      
	    else if (option == "-v")
	      {
		m_verbose = true;
	      }
	    else if (option == "-vd")
	      {
		need_version_dir = true;
	      }
	    else if (option == "-requirements")
	      {
		need_requirements_file = true;
	      }
	    else if (option == "-rs")
	      {
		need_version_tag_suffix = true;
		m_head = false;
	      }
	    else if (option == "--help")
	      {
		help ();
		return;
	      }
	    else if (option[0] == '-')
	      {
		help ();
		return;
	      }
	    else
	      {
		modules.push_back (option);
		//do_checkout_phase1 (option, m_version_dir, version_tag);
	      }
	  }
      }
    if ( modules.size () < 1)
      {
	help ();
	return;
      }
    for (int arg = 0; arg < modules.size (); arg++)
      {
	if (version_tag_suffix != "")
	  {
	    cmt_string name;
	    CmtSystem::basename (modules[arg], name);
	    version_tag = name + version_tag_suffix;
	  }
	do_checkout_phase1 (modules[arg], m_version_dir, version_tag);
      }
  }

private:

  bool m_recursive;
  bool need_touch_files;
  bool m_head;
  bool m_verbose;
  bool m_simulation;
  bool no_config;

  cmt_string m_home_dir;
  cmt_string m_checkout_dir;
  cmt_string m_version_dir;
  cmt_string m_cvs_offset;

  cmt_string m_protocol_level;
  cmt_string m_last_module;
  cmt_string m_last_cvs_infos;
  cmt_string structure_info;
  cmt_string error_info;
  cmt_string tags_top_info;
  cmt_string tags_info;
  cmt_string cvsversions_top_info;
  cmt_string cvsversions_info;
  cmt_string branches_info;
  cmt_string subpackages_info;
  cmt_string subprojects_info;
};

//--------------------------------------------------------------------

void Grep::begin ()
{
  m_result = "";
}

void Grep::filter (const cmt_string& line)
{
  //if (CmtSystem::testenv ("CMTTESTAWK")) cerr << "Grep::filter" << endl;

  if (m_result != "") m_result += " ";
  m_result += line;
}

const cmt_string& Grep::result () const
{
  return (m_result);
}

//--------------------------------------------------------------------

Cut::Cut (int field)
{
  m_field = field;
}

void Cut::begin ()
{
  //if (CmtSystem::testenv ("CMTTESTAWK")) cerr << "Cut::begin" << endl;
  m_result = "";
}

void Cut::filter (const cmt_string& line)
{
  //if (CmtSystem::testenv ("CMTTESTAWK")) cerr << "Cut::filter" << endl;

  static CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (line, " \t", words);
  
  if (words.size () <= m_field) return;
  
  if (m_result != "") m_result += " ";
  m_result += words[m_field];
}

const cmt_string& Cut::result () const
{
  return (m_result);
}

//--------------------------------------------------------------------
//--------------------------------------------------------------------

History& History::instance ()
{
  static History h;
  return (h);
}

void History::clear ()
{
  m_installed = "";
}

void History::install (const cmt_string& line)
{
  m_installed += "|";
  m_installed += line;
  m_installed += "|";
}

bool History::is_installed (const cmt_string& line)
{
  if (m_installed.find (line) != cmt_string::npos)
    {
      return (true);
    }
  
  return (false);
}

History::History ()
{
}


//--------------------------------------------------------------------

void RecursivePass1::begin ()
{
  m_first = true;
  m_result = "";
}

void RecursivePass1::filter (const cmt_string& line)
{
  //if (CmtSystem::testenv ("CMTTESTAWK")) cerr << "RecursivePass1::filter> " 
  //                                          << "line=[" << line << "]" << endl;

  if (line.find ("use CMT") != cmt_string::npos) return;
  if (line.find ("use cmt") != cmt_string::npos) return;
  
  History& h = History::instance ();
  
  if (h.is_installed (line)) return;
  
  CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (line, " \t", words);

  enum
    {
      need_package,
      need_version,
      need_path,
      no_need
    } state = need_package;

  cmt_string package;
  cmt_string version;
  cmt_string path;

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

      if (s[0] == '-') continue;

      switch (state)
        {
	case need_package:
	  package = s;
	  state = need_version;
	  break;
	case need_version:
	  version = s;
	  state = need_path;
	  break;
	case need_path:
	  path = s;
	  state = no_need;
	  break;
        default:
	  break;
        }
    }
  
  if (version.find ("*") != cmt_string::npos)
    {
      /*
	cerr << "# ================= Package " << package 
	<< " version " << version << " " << path 
	<< " has wild cards and will not be considered." << endl;
      */
      return;
    }

  /**
   *  At the first pass, we simply accumulate the not-yet handled
   *  use statements.
   */
  
  m_result += line;
  m_result += "\n";
  
  if (m_first)
    {
      m_first = false;
      cerr << "  # --> now propagate cmt checkout to :" << endl;
    }
  
  cerr << "  #     " << package << " " << version << " " << path << endl;
}

const cmt_string& RecursivePass1::result () const
{
  return (m_result);
}

//--------------------------------------------------------------------

RecursivePass2::RecursivePass2 (CvsImplementation& cvs) : m_cvs (cvs)
{
}

void RecursivePass2::begin ()
{
}

void RecursivePass2::filter (const cmt_string& line)
{
  //if (CmtSystem::testenv ("CMTTESTAWK")) cerr << "RecursivePass2::filter> " 
  //                                          << "line=[" << line << "]" << endl;

  /**
   *   At the second pass, the lines are really handled. Thus
   *   the lines are stored into m_installed so as to avoid
   *   later on re-installation.
   */
  
  History& h = History::instance ();
  
  if (h.is_installed (line)) return;
  
  h.install (line);
  
  CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (line, " \t", words);

  enum
    {
      need_package,
      need_version,
      need_path,
      no_need
    } state = need_package;

  cmt_string package;
  cmt_string version;
  cmt_string path;

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

      if (s[0] == '-') continue;

      switch (state)
        {
	case need_package:
	  package = s;
	  state = need_version;
	  break;
	case need_version:
	  version = s;
	  state = need_path;
	  break;
	case need_path:
	  path = s;
	  state = no_need;
	  break;
        default:
	  break;
        }
    }

  if (version.find ("*") != cmt_string::npos)
    {
      /*
	cerr << "# ================= Package " << package 
	<< " version " << version << " " << path 
	<< " has wild cards and will not be considered." << endl;
      */
    }
  else
    {
      static const cmt_string empty;
      m_cvs.do_checkout_phase2 (path, package, version, empty);
    }
}

//--------------------------------------------------------------------

void RecursivePass3::begin ()
{
  m_first = true;
  m_result = "";
}

void RecursivePass3::filter (const cmt_string& line)
{
  History& h = History::instance ();
  
  if (h.is_installed (line)) return;
  
  CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (line, " \t", words);

  enum
    {
      need_project,
      need_version,
      need_tag,
      no_need
    } state = need_project;

  cmt_string project;
  cmt_string version;
  cmt_string tag;

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

      switch (state)
        {
	case need_project:
	  project = s;
	  state = need_version;
	  break;
	case need_version:
	  version = s;
	  state = need_tag;
	  break;
	case need_tag:
	  tag = s;
	  state = no_need;
	  break;
        default:
	  break;
        }
    }
  
  if (version.find ("*") != cmt_string::npos)
    {
      /*
	cerr << "# ================= Project " << project 
	<< " version " << version << " " << path 
	<< " has wild cards and will not be considered." << endl;
      */
      return;
    }

  /**
   *  At the first pass, we simply accumulate the not-yet handled
   *  use statements.
   */
  
  m_result += line;
  m_result += "\n";
  
  if (m_first)
    {
      m_first = false;
      cerr << "  # --> now propagate cmt checkout to :" << endl;
    }
  
  cerr << "  #     " << project << " " << version << " " << tag << endl;
}

const cmt_string& RecursivePass3::result () const
{
  return (m_result);
}

//--------------------------------------------------------------------

RecursivePass4::RecursivePass4 (CvsImplementation& cvs) : m_cvs (cvs)
{
}

void RecursivePass4::begin ()
{
}

void RecursivePass4::filter (const cmt_string& line)
{
  //if (CmtSystem::testenv ("CMTTESTAWK")) cerr << "RecursivePass4::filter> " 
  //                                          << "line=[" << line << "]" << endl;

  /**
   *   At the second pass, the lines are really handled. Thus
   *   the lines are stored into m_installed so as to avoid
   *   later on re-installation.
   */
  
  History& h = History::instance ();
  
  if (h.is_installed (line)) return;
  
  h.install (line);
  
  CmtSystem::cmt_string_vector words;
  
  CmtSystem::split (line, " \t", words);

  enum
    {
      need_project,
      need_version,
      need_tag,
      no_need
    } state = need_project;

  cmt_string project;
  cmt_string version;
  cmt_string tag;

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

      switch (state)
        {
	case need_project:
	  project = s;
	  state = need_version;
	  break;
	case need_version:
	  version = s;
	  state = need_tag;
	  break;
	case need_tag:
	  tag = s;
	  state = no_need;
	  break;
        default:
	  break;
        }
    }

  if (version.find ("*") != cmt_string::npos)
    {
      /*
	cerr << "# ================= Project " << project 
	<< " version " << version
	<< " has wild cards and will not be considered." << endl;
      */
    }
  else
    {
      static const cmt_string empty;
      m_cvs.do_checkout_phase2 (empty, project, version, tag);
    }
}

//--------------------------------------------------------------------

void Cvs::tags (const CmtSystem::cmt_string_vector& arguments)
{
  CvsImplementation cvs;

  cvs.tags (arguments);
}

void Cvs::branches (const cmt_string& module)
{
  CvsImplementation cvs;

  cvs.branches (module);
}

void Cvs::subpackages (const cmt_string& module)
{
  CvsImplementation cvs;

  cvs.subpackages (module);
}

void Cvs::subprojects (const cmt_string& module)
{
  CvsImplementation cvs;

  cvs.subprojects (module);
}

void Cvs::checkout (const CmtSystem::cmt_string_vector& arguments)
{
  CvsImplementation cvs;

  cvs.checkout (arguments);
}

//--------------------------------------------------------------------
void Vcs::help ()
{
  cout << "Usage:" << endl;
  cout << "> cd <some work area>" << endl;
  cout << "> cmt co | checkout [modifier]... <package|project>..." << endl;
  cout << "" << endl;
  cout << "   modifier :" << endl;
  cout << "   -l          Do not process used packages (default)." << endl;
  cout << "   -R          Process used products recursively." << endl;
  cout << "   -r rev      Check out version tag rev (may be HEAD)." << endl;
  cout << "   -C          Determine version tag and/or package container from context." << endl;
  cout << "   -i          Do not check out, show package(s) information only." << endl;
  cout << "   -vd dir     Use this version directory instead of version tag." << endl;
  cout << "   -d dir      Check out into dir instead of module name." << endl;
  cout << "   -o offset   Offset in the repository" << endl;
  cout << "   -requirements <requirements file path>  Check out packages referenced in this requirements file" << endl;
  //cerr << "   -t        Change file timestamps to the date of checkout" << endl;
  //cerr << "   -n        simulation mode on" << endl;
  cout << "   -no_config  Disable config step after checkout" << endl;    
  //cerr << "   -v        verbose mode on" << endl;
  cout << "   -rs suffix  Same as -r <packagename|projectname>suffix" << endl;
  cout << "   --help      Print this help" << endl;
  cout << "" << endl;
  cout << "(Modifiers are recognised anywhere on the command line.)" << endl;
//   cerr << "" << endl;
//   cerr << "> cmt cvstags <package|project>" << endl;
//   cerr << "> cmt cvssubpackages <directory>" << endl;
//   cerr << "> cmt cvssubprojects <directory>" << endl;
  cout << "" << endl;
}

//--------------------------------------------------------------------
/**
 * When running cmt cvs commands, we stand by definition outside of any existing 
 * package context. Thus it's likely that CMTPATH are not completely defined.
 * This function manually prepends CMTPATH entries to the environment variable.
 */
//--------------------------------------------------------------------
void Vcs::add_cmtpath (const cmt_string& dir)
{
  static cmt_string CMTPATH;
  bool add (true);
  CmtSystem::cmt_string_vector paths;

  cmt_string cmtpath = Symbol::get_env_value ("CMTPATH");
  
  CmtSystem::split (cmtpath, CmtSystem::path_separator (), paths);
  for (int j = 0; j < paths.size (); j++)
    {
      if (dir == paths[j])
	{
	  add = false;
	  break;
	}
    }
  if (add)
    {
      CMTPATH = dir;
      if (cmtpath != "")
	{
	  CMTPATH += CmtSystem::path_separator ();
	  CMTPATH += cmtpath;
	}
      CmtSystem::putenv ("CMTPATH", CMTPATH);
    }
  //  if (m_verbose)
  //      CmtMessage::info ("CMTPATH=" + CmtSystem::getenv ("CMTPATH"));
}

//--------------------------------------------------------------------
bool Vcs::need_version_directory ()
{
  bool need_version = false;

  {
    CmtStructuringStyle style = Cmt::get_current_structuring_style ();
    if (style == default_structuring_style)
      {
	Use& current_use = Use::current ();
	if (current_use.get_strategy ("VersionDirectory"))
	  need_version = true;
      }
    else if (style == with_version_directory)
      need_version = true;	        
  }

  return need_version;
}

//--------------------------------------------------------------------
int Vcs::parse_arguments (const CmtSystem::cmt_string_vector& arguments,
			  Checkout& checkout, bool& config)
{
  if (arguments.size () < 1)
    {
      CmtError::set (CmtError::syntax_error, "Too few arguments");
      help ();
      return 2;
    }

  /**
   * Parse the arguments
   */
  bool need_version_tag = false;
  bool need_checkout_dir = false;
  bool need_version_dir = false;
  bool need_offset = false;
  bool need_requirements_file = false;
  cmt_string version_tag_suffix;
  bool need_version_tag_suffix = false;

  for (int arg = 0; arg < arguments.size (); arg++)
    {
      const cmt_string& option = arguments[arg];
      
      if (need_version_tag
	  || need_checkout_dir
	  || need_version_dir
	  || need_offset
	  || need_requirements_file)
	{
	  if (option[0] == '-')
	    {
	      CmtError::set (CmtError::syntax_error,
			     "Option argument expected. Option " + option
			     + " provided");
	      //	      CmtMessage::error ("Option argument expected. Option " + option + " provided");
	      help ();
	      return 2;
	    }
	}

      if (need_version_tag)
	{
	  need_version_tag = false;
	  
// 	    if (option == "HEAD")
// 	      {
// 		m_head = true;
// 	      }
// 	    else
// 	      {
	  checkout.m_version_tag = option;
// 	      }
	}
      else if (need_checkout_dir)
	{
	  need_checkout_dir = false;
	  checkout.m_checkout_dir = option;
	}
      else if (need_version_dir)
	{
	  need_version_dir = false;
	  checkout.m_version_dir = option;
	}
      else if (need_offset)
	{
	  need_offset = false;
	  checkout.m_offset = option;
	  //m_offset += '/';
	  //m_offset.replace_all ("//", "/");
	}
      else if (need_requirements_file)
	{
	  need_requirements_file = false;
	  //m_head = false;
	  //checkout_from_requirements (option);
	  checkout.m_requirements = option;
	}
      else if (need_version_tag_suffix)
	{
	  need_version_tag_suffix = false;
	  version_tag_suffix = option;
	}
      else
	{
	  if (option == "-R")
	    {
	      //CmtMessage::warning ("Option " + option + " not implemented");
	      //CmtMessage::warning ("Option " + option + " under testing");
	      //m_recursive = true;
	      checkout.m_recursive = true;
	    }
	  else if (option == "-t")
	    {   
	      CmtMessage::warning ("Option " + option + " not implemented");
	      //need_touch_files = true;
	    }  
	  else if (option == "-l")
	    {
	      checkout.m_recursive = false;
	      //m_recursive = false;
	    }
	  else if (option == "-r")
	    {
	      need_version_tag = true;
	      //m_head = false;
	    }
	  else if (option == "-C")
	    {
	      checkout.m_context = true;
	    }
	  else if (option == "-i")
	    {
	      checkout.m_info = true;
	    }
	  else if (option == "-d")
	    {
	      need_checkout_dir = true;
	    }
	  else if (option == "-o")
	    {
	      need_offset = true;
	    }
	  else if (option == "-n")
	    {
	      CmtMessage::warning ("Option " + option + " not implemented");
	      //m_simulation = true;
	    }
	  else if (option == "-no_config")
	    {
	      config = false;
	    }	      
	  else if (option == "-v")
	    {
	      CmtMessage::warning ("Option " + option + " not implemented");
	      //m_verbose = true;
	    }
	  else if (option == "-vd")
	    {
	      need_version_dir = true;
	    }
	  else if (option == "-requirements")
	    {
	      //CmtMessage::warning ("Option " + option + " under testing");
	      need_requirements_file = true;
	    }
	  else if (option == "-rs")
	    {
	      need_version_tag_suffix = true;
	      //m_head = false;
	    }
	  else if (option == "--help")
	    {
	      help ();
	      return 0;
	    }
	  else if (option[0] == '-')
	    {
	      CmtError::set (CmtError::syntax_error, "Invalid option " + option);
	      //	      CmtMessage::error ("Invalid option " + option);
	      help ();
	      return 2;
	    }
	  else
	    {
	      if (option.size () == 0)
		{
		  CmtMessage::warning ("Skipping bad module `" +
				       option + "'");
		  continue;
		}
	      checkout.m_modules.push_back (option);
	    }
	}
    }
  
  for (int arg = 0; arg < checkout.m_modules.size (); arg++)
    {
      if (version_tag_suffix != "")
	{
	  cmt_string name;
	  CmtSystem::basename (checkout.m_modules[arg], name);
	  checkout.m_version_tag = name + version_tag_suffix;
	}
    }

  if (checkout.m_modules.size () < 1 &&
      checkout.m_requirements == "")
    {
      CmtError::set (CmtError::syntax_error, "No module to checkout specified");
      //      CmtMessage::error ("No module to checkout specified");
      help ();
      return 2;
    }
  //checkout.print ();
  return 0;
}

//--------------------------------------------------------------------
class Modules
{
public:
  //  Use::UsePtrVector m_packages;
  //  cmt_vector<PackageInfo> m_packages;
  Use::UseVector m_packages;
  cmt_string m_uses;
};

//--------------------------------------------------------------------
int Vcs::parse_checkout (Checkout& checkout,
			 Modules& modules)
  //			 cmt_string& uses)
{
  int retval (0);

  if (checkout.m_requirements != "")
    {
      if (!CmtSystem::test_file (checkout.m_requirements))
	{
	  CmtError::set (CmtError::path_not_found, checkout.m_requirements);
	  ++retval;
	}
      else
	{
	  cmt_string text;
	  if (!text.read (checkout.m_requirements))
	    {
	      CmtError::set (CmtError::file_access_error, checkout.m_requirements);
	      ++retval;
	    }
	  else
	    {
	      modules.m_uses = text;
	    }
	}
    }

  const cmt_string& version_tag = checkout.m_version_tag;
  if (version_tag == "HEAD" || version_tag == "head" ||
      version_tag == "TRUNK" || version_tag == "trunk")
    checkout.m_head = true;
  
  cmt_string uses;
  for (int arg = 0; arg < checkout.m_modules.size (); arg++)
    {
      cmt_string use ("use");
      cmt_string name = checkout.m_modules[arg];
      cmt_string package;
      cmt_string version;
      cmt_string path;
      /*
	if (checkout.m_context)
	{
	name.replace_all ("//", "/");
	}
      */
      while (name.size () > 0 && name[name.size () - 1] == '/')
	name.erase (name.size () - 1);
      if (name.size () == 0)
	{
	  CmtError::set (CmtError::syntax_error, "Bad module name `" +
			 checkout.m_modules[arg] + "'");
	  CmtError::print ();
	  continue;
	}
      
      int pos = name.find_last_of ('/');
      if (pos != cmt_string::npos)
	{
	  package = name.substr (pos + 1);
	  path = name.substr (0, pos);
	}
      else
	{
	  package = name;
	}
      if (checkout.m_context)
	{
	  if (version_tag == "" || path == "")
	    {
	      Packages& pkgs = Packages::instance ();
	      //if (version_tag == "") - want to find 1) version
	      //                                      2) path, if (path == "")
	      // args: package, version_tag, path
	      //if (path == "") - want to find 1) path
	      //                               2) version, if (version_tag == "")
	      // args: package, "", path
	      // package, "", path
	      Use* p_use = pkgs.find (package, "", path);
	      if (p_use == 0)
		{
		  CmtError::set (CmtError::package_not_found, package +
				 ": Context");
		  CmtError::print ();
		  ++retval;
		  continue;
		}
// 	      cerr << "Obtained: " << p_use->get_info ()
// 		   << " " << p_use->real_path << endl;
	      if (version_tag == "" )
		version = p_use->version;
	      else
		version = version_tag;

	      path = p_use->path;
	    }
	  if (!checkout.m_head)
	    {
	      use += " " + package + " " + version + " " + path + "\n";
	      CmtMessage::verbose (use);
	      uses += use;
	    }
	} // end of checkout.m_context
      else
	{
	  version = version_tag;
	}
      {
	Use use_obj (package, version, path);
	Use* use = &use_obj;
	use->get_package ()->remove_use (use);
	modules.m_packages.push_back (use_obj);
      }
    } // end of for-loop over checkout.m_modules

  if (checkout.m_context
      && !checkout.m_head)
    {
      resolve_uses (checkout, uses + modules.m_uses);
    }

  return retval;
}

//--------------------------------------------------------------------
void Vcs::checkout (const CmtSystem::cmt_string_vector& arguments)
{
  cmt_string cvsroot = CmtSystem::getenv ("CVSROOT");
  cmt_string svnroot = CmtSystem::getenv ("SVNROOT");

  if (cvsroot != "" && svnroot != "")
    {
      CmtError::set (CmtError::configuration_error,
		     "Both CVSROOT and SVNROOT set in environment");
      return;
    }

  if (cvsroot == "" && svnroot == "")
    {
      CmtError::set (CmtError::configuration_error,
		     "Neither CVSROOT nor SVNROOT set in environment");
      return;
    }

  if (cvsroot != "")
    {
      Cvs::checkout (arguments);
      return;
    }

  bool config (true);
  Checkout checkout;
  parse_arguments (arguments, checkout, config);

  if (CmtError::has_pending_error ()) return;

  //  cmt_string uses;
  //  parse_checkout (checkout, uses);
  Modules modules;
  parse_checkout (checkout, modules);
  //  add_cmtpath (CmtSystem::pwd ());
  bool with_version_directory (need_version_directory ());
      /*
      checkout.print ();
      cerr << "config: " << config << endl;
      cerr << "with_version_directory: " << with_version_directory << endl;
      */
  if (svnroot != "")
    {
      Svn::checkout (checkout, modules, config, with_version_directory);
      return;
    }
}

//--------------------------------------------------------------------
class VisitorForInfo : public IUseVisitor
{
public:
  VisitorForInfo (const Vcs::Checkout& checkout)
    : m_checkout (checkout)
  {
  }

  void in (Use* use)
  {
    cmt_string version (use->version);
    if (m_checkout.m_head)
      {
	version = "HEAD";
      }
    else if (version.find ("*") != cmt_string::npos
	     && !m_checkout.m_context)
      {
	CmtMessage::warning (use->get_info () +
			     ": Skipped due to wildcards in version."
			     " Consider using -C or -r HEAD option.");
	return;
      }

    cmt_string p = use->real_path;
    if (use->path != "")
      {
	int pos = p.find_last_of (use->path);
	if (pos != cmt_string::npos)
	  {
	    p.erase (pos);
	  }
      }

    cout << use->get_package_name ()
	 << " " << version; //use->version;
    
    if (CmtSystem::absolute_path (use->path))
      {
	if (!Cmt::get_quiet ()) 
	  {
	    cout << " (" << use->path << ")";
	  }
      }
    else
      {
	if (use->path != "") cout << " " << use->path;
      }
    
    if (!Cmt::get_quiet ()) 
      {
	if (p != "") cout << " (" << p << ")";
      }
    
    cout << endl;
  }

private:
  const Vcs::Checkout& m_checkout;
};

//--------------------------------------------------------------------
class VisitorForCheckout : public IUseVisitor
{
public:
  VisitorForCheckout (const Vcs::Checkout& checkout,
		      bool config, bool with_version_directory,
		      Vcs::VcsType type)
    : m_checkout (checkout),
      m_config (config), m_with_version_directory (with_version_directory),
      m_type (type)
  {
  }
  
  void in (Use* use)
  {
    cmt_string version (use->version);
    if (m_checkout.m_head)
      {
	version = "HEAD";
      }
    else if (version.find ("*") != cmt_string::npos
	     && !m_checkout.m_context)
      {
	CmtMessage::warning (use->get_info () +
			     ": Skipped due to wildcards in version."
			     " Consider using -C or -r HEAD option.");
	return;
      }

    cmt_string module ((use->path != "") ?
		       use->path + "/" + use->get_package_name () :
		       use->get_package_name ());
    Vcs::checkout_module (m_checkout,
			  m_config, m_with_version_directory,
			  //			  module, version,
			  module,
			  version,
			  m_type);
  }

private:
  const Vcs::Checkout& m_checkout;
  bool m_config;
  bool m_with_version_directory;
  Vcs::VcsType m_type;
};

//--------------------------------------------------------------------
void Svn::checkout (Vcs::Checkout checkout, Modules modules,
		   bool config, bool with_version_directory)
{
  cmt_string checkout_command;
  Symbol* checkout_command_macro = Symbol::find ("svn_checkout_command");
  if (checkout_command_macro != 0)
    {
      checkout_command = checkout_command_macro->resolve_macro_value ();
    }
  if (checkout_command == "")
    {
      CmtError::set (CmtError::symbol_not_found,
		     "Macro svn_checkout_command not defined");
      return;
    }
  checkout.m_command = checkout_command;

  /**
   * Checkout packages found in context
   */
  if (checkout.m_context
      && !checkout.m_head)
    {
      Vcs::checkout_context (checkout, modules,
			     config, with_version_directory,
			     Vcs::Svn);
      return;
    }

  /**
   * Call checkout for each command line argument
   */
  VisitorForInfo visitor_i (checkout);
  VisitorForCheckout visitor_co (checkout,
				 config, with_version_directory,
				 Vcs::Svn);
  for (int i = 0; i < modules.m_packages.size (); i++)
    {
      Use* use = &(modules.m_packages[i]);
      if (checkout.m_info)
	visitor_i.in (use);
      else
	visitor_co.in (use);
    }
  /*
  for (int arg = 0; arg < checkout.m_modules.size (); arg++)
    {
      const cmt_string& module = checkout.m_modules[arg];
      const cmt_string& version_tag = checkout.m_version_tag;
      Vcs::checkout_module (checkout,
			    config, with_version_directory,
			    module, version_tag,
			    Vcs::Svn);
      //      do_checkout_phase1 (modules[arg], m_version_dir, version_tag);
    }
  */

  /**
   * Call checkout for used package in requirements file
   */
  if (checkout.m_requirements != "" &&
      modules.m_uses != "")
    Vcs::checkout_from_requirements (checkout,
				     config, with_version_directory,
				     checkout.m_requirements,
				     Vcs::Svn);
}

//--------------------------------------------------------------------
void Vcs::checkout_module (const Vcs::Checkout& checkout,
			   bool config, bool with_version_directory,
			   cmt_string module, cmt_string version_tag,
			   Vcs::VcsType type)
{
  History& h = History::instance ();
  cmt_string line (module + " " + version_tag);
  //cerr << "get: " << line << endl;  
  if (h.is_installed (line)) return;
  h.install (line);
  //cerr << "install: " << line << endl;  

  cmt_string cmd (checkout.m_command);
  cmt_string msg;

  if (!config)
    cmd += " --no_config";

  if (with_version_directory)
    cmd += " --with_version_directory";
  else
    cmd += " --without_version_directory";

  if (version_tag == "" ||
      version_tag == "HEAD" || version_tag == "head" ||
      version_tag == "TRUNK" || version_tag == "trunk"
      )
    {
      msg += (version_tag == "" ? " (HEAD)" : " (" + version_tag + ")");
      version_tag = "";
    }
  else
    {
      cmd += " -r " + version_tag;
      msg += " (" + version_tag + ")";
    }
  if (checkout.m_offset != "")
    {
      cmd += " -o " + checkout.m_offset;
      msg += " offset " + checkout.m_offset;
    }
  if (checkout.m_checkout_dir != "")
    {
      cmd += " -d " + checkout.m_checkout_dir;
      msg += " in " + checkout.m_checkout_dir;
    }
  if (checkout.m_version_dir != "")
    cmd += " --version-dir " + checkout.m_version_dir;

  /**
   * Then call checkout for the module
   */
  CmtMessage::info ("Working on " + module + msg);
  //      cerr << cmd + " " + module << endl;
  int status = CmtSystem::execute (cmd + " " + module);
  if (0 == status)
    {
      CmtMessage::info (module + " done.");
      if (checkout.m_recursive
	  && (!checkout.m_context || checkout.m_head))
	//      if (checkout.m_recursive && !checkout.m_context)
	//      if (checkout.m_recursive)
	{
	  cmt_string requirements;
	  if (checkout.m_checkout_dir != "")
	    {
	      requirements = checkout.m_checkout_dir;
	    }
	  else
	    {
	      requirements = module;
	      requirements.replace_all ("/", CmtSystem::file_separator ());
	    }
	  if (with_version_directory)
	    {
	      if (checkout.m_version_dir != "")
		{
		  requirements += CmtSystem::file_separator ();
		  requirements += checkout.m_version_dir;
		}
	      else
		{
		  if (version_tag != "")
		    {
		      requirements += CmtSystem::file_separator ();
		      requirements += version_tag;
		    }
		  else
		    { // checkout of trunk
		      //
		      CmtSystem::cmt_string_vector versions;
		      cmt_string name;
		      
		      name = requirements;
		      name += CmtSystem::file_separator ();
		      name += "*";
		      CmtSystem::scan_dir (name, versions);

		      int n (0);
		      for (int i = 0; i < versions.size (); i++)
			{
			  const cmt_string& vers = versions[i];
			  
			  if (Cmt::get_debug ())
			    {
			      cout << "     ... version " << vers << " exists" << endl;
			    }
			  /*
			    This check is not sufficient !! We need to check in addition
			    that the selected directory is really the start of a true CMT
			    package (ie with either /mgr/requirements or /cmt/requirements below)
			  */
			  
			  cmt_string req;
			  req = vers;
			  //req = name;
			  req += CmtSystem::file_separator ();
			  req += "mgr";
			  req += CmtSystem::file_separator ();
			  req += "requirements";
			  
			  if (!CmtSystem::test_file (req))
			    {
			      req = vers;
			      //req = name;
			      req += CmtSystem::file_separator ();
			      req += "cmt";
			      req += CmtSystem::file_separator ();
			      req += "requirements";
			      
			      if (!CmtSystem::test_file (req)) continue;
			    }
			  n += 1;
			  if (n > 1)
			    {
			      CmtMessage::warning (requirements +
						   ": Ambiguous choice"
						   " for recursion");
			      break;
			    }
			  CmtSystem::basename (vers, name);
			  requirements += CmtSystem::file_separator ();
			  requirements += name;
			} // end of for versions loop
		      if (n < 1)
			{
			  CmtMessage::error ("Cannot find package for recursion"
					     " in " + requirements);
			  CmtError::set (CmtError::package_not_found,
					 "in " + requirements);
			  return;
			}
		    } // end of checkout of trunk
		}
	    } // end of with_version_directory
	  cmt_string
	    cmt_req = requirements +
	    CmtSystem::file_separator () +
	    "cmt" +
	    CmtSystem::file_separator () +
	    "requirements";
	  cmt_string
	    mgr_req = requirements +
	    CmtSystem::file_separator () +
	    "mgr" +
	    CmtSystem::file_separator () +
	    "requirements";
	  if (CmtSystem::test_file (cmt_req))
	    {
	      requirements = cmt_req;
	    }
	  else if (CmtSystem::test_file (mgr_req))
	    {
	      requirements = mgr_req;
	    }
	  else
	    {
	      CmtError::set (CmtError::path_not_found,
			     cmt_req + " or " + mgr_req);
	      return;
	    }
	  Vcs::checkout_from_requirements (checkout,
					   config, with_version_directory,
					   requirements,
					   type);
	} // end of checkout.m_recursive
    }
  else
    {
      CmtError::set (CmtError::execution_failed, cmd + " " + module, status);
    }
  //      do_checkout_phase1 (modules[arg], m_version_dir, version_tag);
}

//--------------------------------------------------------------------
static void visit (const Use::UsePtrVector& uses, IUseVisitor& visitor)
{
  for (int i = 0; i < uses.size (); i++)
    {
      Use* use = uses[i];
      if (use == 0) continue;
      if (use->selected) continue;
      use->selected = true;
      if (use->discarded) continue;
      if (use->m_hidden) continue;
      if (!use->located ()) continue;
      if (use->get_package_name () == "CMT") continue;
      if (use->get_package_name () == "cmt_standalone") continue;
      if (use->get_package_name () == CmtSystem::get_home_package ()) continue;
      if (use->get_package_name () == CmtSystem::get_user_context_package ()) continue;

      //      visitor.in (use);
      visit (use->sub_uses, visitor);
      visitor.in (use);
    }
}

//--------------------------------------------------------------------
void Vcs::checkout_context (const Vcs::Checkout& checkout, Modules modules,
			    bool config, bool with_version_directory,
			    Vcs::VcsType type)
{
  VisitorForInfo visitor_i (checkout);
  VisitorForCheckout visitor_co (checkout,
				 config, with_version_directory,
				 type);
  
  Use* current = &(Use::current ());
  if (checkout.m_info)
    visit (current->sub_uses, visitor_i);
  else
    visit (current->sub_uses, visitor_co);

}

//--------------------------------------------------------------------
int Vcs::resolve_uses (const cmt_string& text, cmt_string& uses)
{
  cerr << "Vcs::resolve_uses" << endl;
  cmt_string home_dir = CmtSystem::pwd ();
  //
  // Resolve uses by creating a package in a temporary directory
  //
  cmt_string tmp_dir = CmtSystem::getenv ("TMPDIR");
  if (tmp_dir == "")
    {
      tmp_dir = CmtSystem::file_separator ();
      tmp_dir += "tmp";
    }
  
  if (!CmtSystem::test_directory (tmp_dir))
    //if (!CmtSystem::cd (tmp_dir))
    {
      tmp_dir = home_dir;
    }
  
  tmp_dir += CmtSystem::file_separator ();
  tmp_dir += "cmtvcs";
  cmt_string temp = CmtSystem::get_temporary_name ();
  CmtSystem::basename (temp, temp);
  // Suppress dots for Windows
  temp.replace_all (".", "");
  tmp_dir += temp;

  cerr << "tmp_dir: " << tmp_dir << endl;
  //  tmp_dir = "/tmp/cmtvcsfileEvmagO";

  if (CmtSystem::test_directory (tmp_dir))
    {
      if (!CmtSystem::remove_directory (tmp_dir))
	{
	  // CmtMessage::error ("Cannot remove temporary directory " + tmp_dir);
	  // error message printed by CmtSystem::remove_directory
	  return 1;
	}
    }
  if (!CmtSystem::mkdir (tmp_dir))
    {
      CmtMessage::error ("Cannot create temporary directory " + tmp_dir);
      return 1;
    }

  cmt_string pkgspec (temp + " v1 " + tmp_dir);
  if (0 != CmtSystem::execute ("cmt -without_version_directory create " + pkgspec))
    //  if (0 != CmtSystem::execute ("cmt -with_version_directory create " + pkg))
    {
      CmtMessage::error ("Cannot create package " + pkgspec);
      CmtSystem::remove_directory (tmp_dir);
      return 1;
    }

  cmt_string cmt_dir (tmp_dir +
		      CmtSystem::file_separator () + temp +
		      //		      CmtSystem::file_separator () + "v1" + 
		      CmtSystem::file_separator () + "cmt");
  
  cmt_string reqtext;
  cmt_string requirements (cmt_dir +
			   CmtSystem::file_separator () + "requirements");
  if (!reqtext.read (requirements))
    {
      CmtError::set (CmtError::file_access_error, requirements);
      CmtSystem::remove_directory (tmp_dir);
      return 1;
    }

  FILE* file = fopen (requirements, "wb");
  if (file != NULL)
    {
      (reqtext + text).write (file);
      CmtSystem::close_ostream (file, requirements);
      //      fclose (file);
    }
  else
    {
      CmtError::set (CmtError::file_access_error, requirements);
      CmtSystem::remove_directory (tmp_dir);
      return 1;
    }

  if (!CmtSystem::cd (cmt_dir))
    {
      CmtMessage::error ("Cannot move to temporary package directory " + cmt_dir);
      CmtSystem::remove_directory (tmp_dir);
      return 1;
    }

  int status = CmtSystem::execute ("cmt show uses", uses);
  //  cerr << "status: " << status << endl;
  if (status != 0)
    {
      CmtMessage::error ("Cannot show uses for package " + pkgspec);
    }
  //  cerr << "uses:\n" + uses << endl;
  CmtSystem::cd (home_dir);
  CmtSystem::remove_directory (tmp_dir);
  return  status == 0 ? 0 : -1;
}

//--------------------------------------------------------------------
int Vcs::resolve_uses (const cmt_string& text, Use::UsePtrVector& uses)
{
  cerr << "Vcs::resolve_uses NEW" << endl;
  cmt_string home_dir = CmtSystem::pwd ();

  Use* current = &(Use::current());

  SyntaxParser::parse_requirements_text (text, "", current);

  cerr <<  "recursive: " <<  Cmt::get_recursive () << endl;

  Use::show_all ();

  if (CmtError::get_last_error_code () == CmtError::package_not_found)
    CmtError::print ();

  return -1;
}

//--------------------------------------------------------------------
int Vcs::resolve_uses (const Checkout& checkout, const cmt_string& uses)
  //int Vcs::resolve_uses (const Checkout& checkout, const Modules& modules)
{
  Packages& pkgs = Packages::instance ();
  pkgs.exclude_current_project ();

  bool cur_recursive (Cmt::get_recursive ());
  CmtScopeFilteringMode
    cur_scope_filtering_mode (Cmt::get_scope_filtering_mode ());

  if (checkout.m_recursive)
    {
      Cmt::set_recursive (true);
      Cmt::set_scope_filtering_mode (reach_private_uses);
    }

  SyntaxParser::parse_requirements_text (uses, "", &(Use::current()));

  if (checkout.m_recursive)
    {
      Cmt::set_recursive (cur_recursive);
      Cmt::set_scope_filtering_mode (cur_scope_filtering_mode);
    }

  pkgs.restore_current_project ();

  if (CmtError::get_last_error_code () == CmtError::package_not_found)
    {
      CmtError::print ();
      return -1;
    }

  return 0;
}
/*
int Vcs::resolve_uses (const Checkout& checkout, const cmt_string& uses)
  //int Vcs::resolve_uses (const Checkout& checkout, const Modules& modules)
{
  //  if (!checkout.m_context) return 0;

  //  cerr << "Vcs::resolve_uses NEW+" << endl;
  cmt_string home_dir = CmtSystem::pwd ();
  //
  // Resolve uses by creating a package in a temporary directory
  //
  cmt_string tmp_dir = CmtSystem::getenv ("TMPDIR");
  if (tmp_dir == "")
    {
      tmp_dir = CmtSystem::file_separator ();
      tmp_dir += "tmp";
    }
  
  if (!CmtSystem::test_directory (tmp_dir))
    //if (!CmtSystem::cd (tmp_dir))
    {
      tmp_dir = home_dir;
    }
  
  tmp_dir += CmtSystem::file_separator ();
  tmp_dir += "cmtvcs";
  cmt_string temp = CmtSystem::get_temporary_name ();
  CmtSystem::basename (temp, temp);
  // Suppress dots for Windows
  temp.replace_all (".", "");
  tmp_dir += temp;

  if (CmtSystem::test_directory (tmp_dir))
    {
      if (!CmtSystem::remove_directory (tmp_dir))
	{
	  CmtError::set (CmtError::file_access_error, tmp_dir);
	  // CmtMessage::error ("Cannot remove temporary directory " + tmp_dir);
	  // error message printed by CmtSystem::remove_directory
	  return 1;
	}
    }
  if (!CmtSystem::mkdir (tmp_dir))
    {
      CmtError::set (CmtError::file_access_error,
		     "Cannot create temporary directory " + tmp_dir);
      CmtError::print ();
      //CmtMessage::error ("Cannot create temporary directory " + tmp_dir);
      return 1;
    }

    if (!CmtSystem::cd (tmp_dir))
    {
      CmtError::set (CmtError::file_access_error,
		     "Cannot move to temporary package directory " + tmp_dir);
      CmtError::print ();
     //CmtMessage::error ("Cannot move to temporary package directory " + tmp_dir);
      CmtSystem::remove_directory (tmp_dir);
      return 1;
    }

  Project::ProjectPtrVector& Ordered = Project::ordered_projects ();
  int i (-1);
  Project* cur (0);
  Project null;
  for (i = 0; i < Ordered.size (); i++)
    if (Ordered[i]->is_current ()) break;
  if (i >= 0 && i < Ordered.size ())
    {
      cur = Ordered[i];
      Ordered[i] = &null;
    }

  bool cur_recursive (Cmt::get_recursive ());
  CmtScopeFilteringMode
    cur_scope_filtering_mode (Cmt::get_scope_filtering_mode ());
  if (checkout.m_recursive)
    {
      Cmt::set_recursive (true);
      Cmt::set_scope_filtering_mode (reach_private_uses);
    }

  SyntaxParser::parse_requirements_text (uses, "", &(Use::current()));

  if (checkout.m_recursive)
    {
      Cmt::set_recursive (cur_recursive);
      Cmt::set_scope_filtering_mode (cur_scope_filtering_mode);
    }

  if (i >= 0 && i < Ordered.size ())
    {
      Ordered[i] = cur;
    }

  CmtSystem::cd (home_dir);
  CmtSystem::remove_directory (tmp_dir);

  if (CmtError::get_last_error_code () == CmtError::package_not_found)
    {
      CmtError::print ();
      return -1;
    }

  return 0;
}
*/
//--------------------------------------------------------------------
void Vcs::checkout_from_requirements (const Vcs::Checkout& checkout,
				      bool config,
				      bool with_version_directory,
				      const cmt_string& requirements,
				      Vcs::VcsType type)
{
  CmtMessage::info ("Processing " + requirements);

  cmt_string text;
  if (!text.read (requirements))
    {
      CmtError::set (CmtError::file_access_error, requirements);
      return;
    }

  static cmt_regexp expression ("^[ \t]*use[ \t]");
  if (checkout.m_info)
    {
      VisitorForInfo visitor_i (checkout);
      Walkthru wth (visitor_i);
      wth.run (text, expression);
    }
  else
    {
      VisitorForCheckout visitor_co (checkout,
				     config, with_version_directory,
				     type);
      Walkthru wth (visitor_co);
      wth.run (text, expression);
    }
}
//--------------------------------------------------------------------
