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

#include <errno.h>
#include <stdio.h>

#ifndef WIN32
#include <unistd.h>
#endif

#include "cmt_generator.h"
#include "cmt_use.h"
#include "cmt_symbol.h"

#include "cmt_generators.h"
#include "cmt_log.h"
#include "cmt_error.h"

#include "cmt_install_area.h"

//------------------------------------------------------------------------
void SourceFile::set (const cmt_string name, Language& language, const cmt_string output)
{
  m_name = name;
  m_language = &language;
  m_output = output;
  
  CmtSystem::reduce_file_separators (m_name);
}

cmt_string SourceFile::name () const
{
  return (m_name);
}

Language& SourceFile::language () const
{
  return (*m_language);
}

cmt_string SourceFile::output () const
{
  return (m_output);
}
//------------------------------------------------------------------------

//--------------------------------------------------
CmtGenerator::CmtGenerator ()
{
  m_CONSTITUENT.set ("CONSTITUENT");
  m_LINKMACRO.set ("LINKMACRO");
  m_DEPENDENCIESOPTS.set ("DEPENDENCIESOPTS");
  m_DOCPATH.set ("DOCPATH");
  m_PACKAGEPATH.set ("PACKAGEPATH");
  m_PACKAGEPREFIX.set ("PACKAGEPREFIX");
  m_PACKAGE.set ("PACKAGE");
  m_VERSION.set ("VERSION");
  m_MGRSTYLE.set ("MGRSTYLE");
  m_TITLE.set ("TITLE");
  m_GROUP.set ("GROUP");
  m_CONSTITUENTSUFFIX.set ("CONSTITUENTSUFFIX");
  m_LIBRARYSUFFIX.set ("LIBRARYSUFFIX");
  m_USER.set ("USER");
  m_DATE.set ("DATE");
  m_PROTOTARGET.set ("PROTOTARGET");
  m_OBJS.set ("OBJS");
  m_CLASSES.set ("CLASSES");
  m_PROTOSTAMPS.set ("PROTOSTAMPS");
  m_NAME.set ("NAME");
  m_FILEPATH.set ("FILEPATH");
  m_FILESUFFIX.set ("FILESUFFIX");
  m_SUFFIX.set ("SUFFIX");
  m_FILENAME.set ("FILENAME");
  m_LINE.set ("LINE");
  m_ADDINCLUDE.set ("ADDINCLUDE");
  m_FULLNAME.set ("FULLNAME");
  m_DIRNAME.set ("DIRNAME");
  m_OUTPUTNAME.set ("OUTPUTNAME");
  m_ALLOS9SOURCES.set ("ALLOS9SOURCES");
  m_NODEBUGUSELINKOPTS.set ("NODEBUGUSELINKOPTS");
  m_DEBUGUSELINKOPTS.set ("DEBUGUSELINKOPTS");
  m_USEINCLUDES.set ("USEINCLUDES");
  m_HASTARGETTAG.set ("HASTARGETTAG");
  m_HASDEPENDENCIES.set ("HASDEPENDENCIES");
  m_HASPROTOTYPES.set ("HASPROTOTYPES");
  m_ISCHECKGROUP.set ("ISCHECKGROUP");
}

//--------------------------------------------------
void CmtGenerator::reset ()
{
  m_CONSTITUENT = "";
  m_LINKMACRO = "";
  m_DEPENDENCIESOPTS = "";
  m_DOCPATH = "";
  m_PACKAGEPATH = "";
  m_PACKAGEPREFIX = "";
  m_PACKAGE = "";
  m_VERSION = "";
  m_MGRSTYLE = "";
  m_TITLE = "";
  m_GROUP = "";
  m_CONSTITUENTSUFFIX = "";
  m_LIBRARYSUFFIX = "";
  m_USER = "";
  m_DATE = "";
  m_PROTOTARGET = "";
  m_OBJS = "";
  m_CLASSES = "";
  m_PROTOSTAMPS = "";
  m_NAME = "";
  m_FILEPATH = "";
  m_FILESUFFIX = "";
  m_SUFFIX = "";
  m_FILENAME = "";
  m_LINE = "";
  m_ADDINCLUDE = "";
  m_FULLNAME = "";
  m_OUTPUTNAME = "";
  m_ALLOS9SOURCES = "";
  m_NODEBUGUSELINKOPTS = "";
  m_DEBUGUSELINKOPTS = "";
  m_USEINCLUDES = "";
  m_HASTARGETTAG = "";
  m_HASDEPENDENCIES = "";
  m_HASPROTOTYPES = "";
  m_ISCHECKGROUP = "";
  m_PACKINCLUDES = "";
  m_PACKOS9      = false;
  m_GENERATOR    = "";

  is_library     = false;
  is_application = false;
  srcdir       = "";
  docdir       = "";
  cmtdir       = "";
  incdir       = "";
  src          = "$(src)";
  doc          = "$(doc)";
  inc          = "$(inc)";
  mgr          = "$(mgr)";
  cmt          = "$(cmt)";
  protos       = "";
  protonames   = "";
  os9sources   = "";

  m_source_files.clear ();
  m_bin = "";
  m_output_file_name = "";
  m_output_file = 0;
  m_constituent = 0;

  Language::setup_all_fragments ();

  CmtSystem::cd (Cmt::get_current_dir ());

  cmt_string branch = CmtSystem::current_branch ();

  if ((branch == "mgr") || (branch == "cmt"))
    {
      if (CmtSystem::test_directory ("../src"))
        {
          srcdir = "..";
	  srcdir += CmtSystem::file_separator ();
          srcdir += "src";
	  srcdir += CmtSystem::file_separator ();
        }
      else
        {
          srcdir = "";
        }

      if (CmtSystem::test_directory ("../doc"))
        {
          docdir = "..";
	  docdir += CmtSystem::file_separator ();
          docdir += "doc";
	  docdir += CmtSystem::file_separator ();
        }
      else
        {
          docdir = "";
        }

      if (CmtSystem::test_directory ("../cmt"))
        {
          cmtdir = "..";
	  cmtdir += CmtSystem::file_separator ();
          cmtdir += "cmt";
	  cmtdir += CmtSystem::file_separator ();
        }
      else if (CmtSystem::test_directory ("../mgr"))
        {
          cmtdir = "..";
	  cmtdir += CmtSystem::file_separator ();
          cmtdir += "mgr";
	  cmtdir += CmtSystem::file_separator ();
        }
      else
        {
          cmtdir = CmtSystem::pwd ();
          cmtdir += CmtSystem::file_separator ();
        }

      if (CmtSystem::test_directory ("../src"))
        {
          incdir = "..";
	  incdir += CmtSystem::file_separator ();
          incdir += "src";
	  incdir += CmtSystem::file_separator ();
        }
      else
        {
          incdir = "";
        }
    }
  else
    {
      srcdir = ".";
      srcdir += CmtSystem::file_separator ();
      docdir = ".";
      docdir += CmtSystem::file_separator ();
      cmtdir = CmtSystem::pwd ();
      cmtdir += CmtSystem::file_separator ();
      incdir = ".";
      incdir += CmtSystem::file_separator ();
    }
}

//--------------------------------------------------
bool CmtGenerator::prepare_output (const cmt_string& package,
				   const Constituent& constituent,
				   const cmt_string& file)
{
  m_PACKAGE = package;
  m_CONSTITUENT = constituent.name;
  m_CONSTITUENTSUFFIX = constituent.suffix;

  m_PACKOS9 = constituent.need_OS9;

  m_output_file_name = cmtdir + m_CONSTITUENT + ".";

  if (Cmt::build_nmake ())
    {
      m_output_file_name += "nmake";
    }
  else
    {
      m_output_file_name += "make";
    }
  
  if (file != "") m_output_file_name = file;

  m_output_file_name += "new";


  m_output_file = fopen (m_output_file_name.c_str (), "wb");
  if (m_output_file != NULL)
    {
      return (true);
    }
  else
    {
      return (false);
    }
}

//--------------------------------------------------
void CmtGenerator::check (const cmt_string& name)
{
  static cmt_string old;
  static cmt_string backup;

  old = name;

  int pos = old.find_last_of ("new");
  old.erase (pos);

  if (!CmtSystem::compare_files (old, name))
    {
      backup = old;
      backup += "sav";

      unlink (backup.c_str ());
      rename (old.c_str (), backup.c_str ());
      rename (name.c_str (), old.c_str ());
    }
  else
    {
      unlink (name);
    }
}

//--------------------------------------------------
void CmtGenerator::commit (const cmt_string& name)
{
  static cmt_string old;
  static cmt_string backup;

  old = name;

  int pos = old.find_last_of ("new");
  old.erase (pos);

  if (CmtSystem::test_file (old))
    {
      backup = old;
      backup += "sav";

      unlink (backup.c_str ());
      rename (old.c_str (), backup.c_str ());
    }

  rename (name.c_str (), old.c_str ());
}

//--------------------------------------------------
void CmtGenerator::terminate ()
{
  CmtSystem::close_ostream (m_output_file, m_output_file_name);
  //  fclose (m_output_file);

  //--- Complete the operation --------------

  commit (m_output_file_name);
}

//--------------------------------------------------
void CmtGenerator::fill_names_outputs ()
{
  bool first = true;

  m_LINE = "";
  m_OBJS = "";

  for (int i = 0; i < m_source_files.size (); i++)
    {
      const SourceFile& file = m_source_files[i];
      const cmt_string name = file.name ();
      const cmt_string output = file.output ();

      if (output != "")
        {
          if (first)
            {
              first = false;
            }
          else
            {
	      m_LINE += " ";
              m_OBJS += " ";
            }
	  m_LINE += name;
          m_OBJS += output;
        }

      if (Cmt::get_debug ())
        {
          cout << "CmtGenerator::fill_names_outputs>" << endl;
	  cout << "name=" << name << " LINE=" << m_LINE << endl;
	  cout << "output=" << output << " OBJS=" << m_OBJS << endl;
        }
    }

  filter_path (m_LINE.value);
}

//--------------------------------------------------
void CmtGenerator::fill_outputs ()
{
  bool first = true;

  m_OBJS = "";

  for (int i = 0; i < m_source_files.size (); i++)
    {
      const SourceFile& file = m_source_files[i];
      const cmt_string output = file.output ();

      if (output != "")
        {
          if (first)
            {
              first = false;
            }
          else
            {
              m_OBJS += " ";
            }

          m_OBJS += output;
        }

      if (Cmt::get_debug ())
        {
          cout << "CmtGenerator::fill_outputs> output=" << output << " OBJS=" << m_OBJS << endl;
        }

    }

  if (Cmt::get_debug ())
    {
      cout << "CmtGenerator::fill_outputs> OBJS=" << m_OBJS << endl;
    }

}

/*
//--------------------------------------------------
void CmtGenerator::prepare_use_context ()
{
  cmt_string path;
  cmt_string substitution;

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

  m_deps_builder.clear ();

  if (use->include_path != "none")
    {
      if (use->include_path == "")
	{
	  m_deps_builder.add (incdir, "$(src)");
	}
      else
	{
	  substitution = use->include_path;
	  
	  path = substitution;
	  Symbol::expand (path);
	  
	  CmtSystem::reduce_file_separators (path);

          m_deps_builder.add (path, substitution);
	}
    }

  m_deps_builder.add_includes (*use);

  Use::UsePtrVector& uses = Use::get_ordered_uses ();

  if (uses.size () > 0)
    {
      int number;

      for (number = 0; number < uses.size (); number++)
        {
          use = uses[number];
          if (use->discarded) continue;

          if (use->real_path != "")
            {
	      if (use->include_path != "none")
		{
		  if (use->include_path == "")
		    {
		      use->get_full_path (path);
                      path += CmtSystem::file_separator ();
		      path += "src";

		      substitution = "$(";
		      substitution += use->prefix;
		      substitution += "ROOT)";
		      substitution += CmtSystem::file_separator ();
		      substitution += "src";
		      substitution += CmtSystem::file_separator ();
		    }
		  else
		    {
		      substitution = use->include_path;

		      path = substitution;
		      Symbol::expand (path);

                      CmtSystem::reduce_file_separators (path);
		    }

		  m_deps_builder.add (path, substitution);
		}

              m_deps_builder.add_includes (*use);
            }
        }
    }
}
*/
//--------------------------------------------------
void CmtGenerator::filter_path (cmt_string& text)
{
  CmtSystem::compress_path (text);

  text.replace_all ("./../src/../", "../");
  text.replace_all ("./../src/", "$(src)");

  text.replace_all (".\\..\\src\\..\\", "..\\");
  text.replace_all (".\\..\\src\\", "$(src)");

  text.replace_all ("../src/../", "../");
  text.replace_all ("../src/", "$(src)");

  text.replace_all ("..\\src\\..\\", "..\\");
  text.replace_all ("..\\src\\", "$(src)");

  text.replace_all ("../doc/../", "../");
  text.replace_all ("../doc/", "$(doc)");

  text.replace_all ("..\\doc\\..\\", "..\\");
  text.replace_all ("..\\doc\\", "$(doc)");

  text.replace_all ("$(src)$(src)", "$(src)");
}

/**
   Scan a complete file spec (with possibly wild cards and directory)
   given in full_name ad fill in a vector of found file names.

   Result of the scan is filtered against matching suffixes

   Returns the count of non empty file names really found.

*/
int CmtGenerator::get_all_files (const cmt_string& full_name,
				 const cmt_vector<cmt_regexp>& exclude_exprs,
				 const cmt_vector<cmt_regexp>& select_exprs,
				 CmtSystem::cmt_string_vector& files)
{
  static cmt_string suffix;
  static cmt_string name;

  bool has_excludes = false;
  bool has_selects = false;

  suffix = "";
  name = "";

  files.clear ();

  has_excludes = (exclude_exprs.size () > 0);
  has_selects = (select_exprs.size () > 0);

  CmtSystem::get_dot_suffix (full_name, suffix);

  bool wilcarded_suffix = false;

  if (suffix == ".*") wilcarded_suffix = true;

  int count = 0;

  if (full_name.find ('*') != cmt_string::npos)
    {
      CmtSystem::scan_dir (full_name, files);

      if (Cmt::get_debug ())
        {
          cout << "CMT::get_all_files> full_name=" << full_name <<
	    " pwd=" << CmtSystem::pwd () << endl;
          cout << "CMT::get_all_files> files.size=" <<  files.size () << endl;
        }

      /**

      We have to treat patterns of the form *.xxx (ie with a
      suffix) thus we filter out everything that could have been
      collected with a different suffix because the
      CmtSystem::scan_dir function only handles patterns of the
      form xxx* (ie with trailing *)

      [If the original suffix was empty (ie files specified using
      xx*) this means getting files without any dot-suffix. This
      may be incorrect??]

      */

      for (int j = 0; j < files.size (); j++)
        {
          cmt_string& n = files[j];

	  bool rejected = false;

          if (n == "")
            {
	      rejected = true;
	    }

	  if (!rejected && has_selects)
	    {
	      rejected = true;

	      for (int k = 0; k < select_exprs.size (); k++)
		{
		  const cmt_regexp& exp = select_exprs[k];
		  if (exp.match (n))
		    {
		      rejected = false;
		      break;
		    }
		}
	    }

	  if (!rejected && has_excludes)
	    {
	      for (int k = 0; k < exclude_exprs.size (); k++)
		{
		  const cmt_regexp& exp = exclude_exprs[k];
		  if (exp.match (n))
		    {
		      rejected = true;
		      break;
		    }
		}
	    }

	  if (!rejected)
	    {
              static cmt_string s;

              CmtSystem::get_dot_suffix (n, s);
              if (!wilcarded_suffix && (s != suffix)) 
                {
		  rejected = true;
                }
              else
                {
                  count++;
                }
            }

	  if (Cmt::get_debug ())
	    {
	      if (rejected)
		{
		  cout << "CMT::get_all_files> reject " <<  n << endl;
		}
	      else
		{
		  cout << "CMT::get_all_files> keep " <<  n << endl;
		}
	    }

	  if (rejected)
	    {
	      n = "";
	    }
        }
    }
  else
    {
      if (full_name != "")
        {
	  bool rejected = false;

	  if (has_excludes)
	    {
	      for (int k = 0; k < exclude_exprs.size (); k++)
		{
		  const cmt_regexp& exp = exclude_exprs[k];
		  if (exp.match (full_name))
		    {
		      rejected = true;
		      break;
		    }
		}
	    }

	  if (!rejected)
	    {
	      cmt_string& n = files.add ();

	      n = full_name;

	      count++;
	    }
        }
    }

  return (count);
}

//--------------------------------------------------
void CmtGenerator::set_full_name (cmt_string& full_name, cmt_string& file)
{
  full_name = "";

  Symbol::expand (file);

  if (file == "") return;
  
  if (!CmtSystem::absolute_path (file))
    {
      full_name = srcdir;
      if (full_name != "") full_name += CmtSystem::file_separator ();
    }
  
  full_name += file;

  CmtSystem::reduce_file_separators (full_name);
}

//------------------------------------------------------------------------
//static ApplicationGenerator ApplicationContext;
static LibraryGenerator LibraryContext;
static DocumentGenerator DocumentContext;
static ReadmeGenerator ReadmeContext;
static PrototypeGenerator PrototypeContext;
static DefaultMakefileGenerator DefaultMakefileContext;
static MSDEVGenerator MSDEVContext;
static VSNETGenerator VSNETContext;
static MakeSetupGenerator MakeSetupContext;
static ConstituentsMakefileGenerator ConstituentsMakefileContext;
static PackagesMakefileGenerator PackagesMakefileContext;
static DependencyGenerator DependencyContext;

//--------------------------------------------------
int Generator::build_msdev_workspace (const Constituent::ConstituentVector& constituents)
{
  return (MSDEVContext.build_workspace (constituents));
}

//--------------------------------------------------
int Generator::build_msdev (const Constituent& constituent)
{
  return (MSDEVContext.build_project (constituent));
}

//--------------------------------------------------
int Generator::build_vsnet_workspace (const Constituent::ConstituentVector& constituents)
{
  return (VSNETContext.build_workspace (constituents));
}

//--------------------------------------------------   
int Generator::build_vsnet (const Constituent& constituent)
{
  return (VSNETContext.build_project (constituent));
}

//--------------------------------------------------
void Generator::build_make_setup (const cmt_string& package)
{
  MakeSetupContext.build (package);
}

//--------------------------------------------------
void Generator::build_constituents_makefile (const cmt_string& package,
					     const cmt_string& file)
//					     const CmtSystem::cmt_string_vector& arguments)
{
  ConstituentsMakefileContext.build (package, file);
  //  ConstituentsMakefileContext.build (package, arguments);
}

//--------------------------------------------------
int Generator::build_constituent_infile (const Constituent& constituent,
					 const cmt_string& outdir,
					 bool usecmt)
{
  cmt_string file (constituent.name + ".in");
  if (outdir != "")
    {
      if (outdir [outdir.size () - 1] != CmtSystem::file_separator ())
	file = outdir + CmtSystem::file_separator () + file;
      else
	file = outdir + file;
    }

  int retval (0);
  static InGenerator InGen (usecmt, false);

  bool gen (true);
  ostringstream os;
  ofstream s;
  //  s.open (file);
  s.open (file, ios::in);
  if (s) // file already exists
    {
      retval = InGen.build (constituent, os);
      ostringstream osn;
      osn << s.rdbuf ();
      if (os.str () == osn.str ())
	{
	  //	  cerr << file << " up-to-date" << endl;
	  gen = false;
	}
    }
  s.clear ();
  s.close ();
  s.clear ();
  if (0 != retval) return retval;
  if (gen)
    {
      s.open (file);
      if (!s)
	{
	  CmtError::set (CmtError::file_access_error, file);
	  return -1;
	}
      s.exceptions (ios::failbit | ios::badbit);
      try
	{
	  if (os.str ().size () != 0)
	    {
	      //  cerr << file << " contents already generated" << endl;
	      s << os.str ();
	    }
	  else
	    {
	      retval = InGen.build (constituent, s);
	    }
	  s.close (); // ios_base::failbit
	}
      catch (const ios::failure& e)
	{
	  CmtSystem::close_ostream (NULL, file + ": " + cmt_string (e.what ()));
	  return -1;
	}
    }

      //  InGen.build (constituent, s);

  //  s.close (); // ios_base::failbit

  return retval;
}

//--------------------------------------------------
int Generator::build_library_links_infile (const cmt_string& outdir)
{
#ifndef WIN32
  cmt_string file ("library_links.in");
  if (outdir != "")
    {
      if (outdir [outdir.size () - 1] != CmtSystem::file_separator ())
	file = outdir + CmtSystem::file_separator () + file;
      else
	file = outdir + file;
    }

  bool wrote (false);
  ostringstream ls;

  Use::UsePtrVector& Uses = Use::get_ordered_uses ();
  Use& current_use = Use::current ();
  Use::UsePtrVector uses (Uses);
  uses.push_back (&Use::current ());

  cmt_string shlibsuffix;
  {
    Symbol* macro = Symbol::find ("shlibsuffix");
    if (macro == 0)
      {
	CmtError::set(CmtError::configuration_error, "shlibsuffix undefined");
	return -1;
      }
    shlibsuffix = macro->build_macro_value ();
    Symbol::expand (shlibsuffix);
  }

  for (int i = 0; i < uses.size (); i++)
    {
      Use* use = uses[i];

      if (use == 0) continue;
      if (use->discarded) continue;
      if (use->m_hidden) continue;

      if (use->get_package_name () == "CMT") continue;
      if (!use->located ()) continue;
      //use->remove_library_links (cmtinstallarea, tag, shlibsuffix, symunlinkcmd);
      cmt_string s (use->get_package_name ());
      s += "_libraries";
      const Symbol* libraries_macro = Symbol::find (s);
      if (libraries_macro == 0) continue;
      cmt_string libraries = libraries_macro->build_macro_value ();
      Symbol::expand (libraries);
      static CmtSystem::cmt_string_vector values;
      
      CmtSystem::split (libraries, " \t", values);
      
      for (int j = 0; j < values.size (); j++)
	{
	  const cmt_string& library = values[j];
	  
	  static cmt_string libname;
	  static cmt_string name;
	  
	  // Is it a simple name or a complete path?
	  
	  libname = library;
	  Symbol::expand (libname);
	  if (0 == libname.size ()) continue;
	  
	  CmtSystem::cmt_string_vector paths;
	  use->absolute_library_path (libname,
				      shlibsuffix, 
				      paths);
	  for (int k = 0; k < paths.size (); k++)
	    {
	      cmt_string path (paths[k]);
	      Symbol::expand (path);
	      if (0 == path.size ()) continue;
	      if (wrote) ls << " ";
	      else wrote = true;
	      ls << CmtSystem::quote (path, " \t");
	    }
	}
    }
  ostringstream os;
  if (wrote)
    {
      os << "macro ";
      os << current_use.get_package_name () + "_libraries ";
      os << CmtSystem::quote (ls.str ().c_str (), " \t");
      os << "\n";

      os << "macro shlibsuffix ";
      os << CmtSystem::quote (shlibsuffix, " \t");
      os << "\n";

      cmt_string symlinkcmd;
      {
	Symbol* macro = Symbol::find ("library_install_command");
	if (macro != 0)
	  {
	    symlinkcmd = macro->build_macro_value ();
	    Symbol::expand (symlinkcmd);
	  }
      }
      os << "macro library_install_command ";
      os << CmtSystem::quote (symlinkcmd, " \t");
      os << "\n";

      cmt_string symunlinkcmd;
      {
	Symbol* macro = Symbol::find ("symunlink");
	if (macro != 0)
	  {
	    symunlinkcmd = macro->build_macro_value ();
	    Symbol::expand (symunlinkcmd);
	  }
      }
      os << "macro symunlink ";
      os << CmtSystem::quote (symunlinkcmd, " \t");
      os << "\n";

      if (current_use.get_strategy ("InstallArea"))
	{
	  os << "build_strategy with_installarea\n";

	  const CmtInstallAreaMgr& ia_mgr = CmtInstallAreaMgr::instance ();
	  
	  cmt_string cmtinstallarea = ia_mgr.get_installarea ();
	  {
	    Symbol* symbol = Symbol::find ("CMTINSTALLAREA");
	    if (symbol != 0)
	      {
		cmtinstallarea = symbol->build_macro_value ();
		Symbol::expand (cmtinstallarea);
	      }
	  }
	  os << "macro CMTINSTALLAREA ";
	  os << CmtSystem::quote (cmtinstallarea, " \t");
	  os << "\n";

	  cmt_string tag;
	  {
	    Symbol* macro = Symbol::find ("tag");
	    if (macro != 0)
	      {
		tag = macro->build_macro_value ();
		Symbol::expand (tag);
	      }
	  }
	  os << "macro tag ";
	  os << CmtSystem::quote (tag, " \t");
	  os << "\n";
	}
      //      cerr << os.str ();
    }

  // Write input requirements file

  //  static InGenerator InGen (usecmt);

  bool gen (true);
  //  ostringstream os;
  ofstream s;
  //  s.open (file);
  s.open (file, ios::in);
  if (s) // file already exists
    {
      //      InGen.build (constituent, os);
      ostringstream osn;
      osn << s.rdbuf ();
      if (os.str () == osn.str ())
	{
	  //	  cerr << file << " up-to-date" << endl;
	  gen = false;
	}
    }
  s.clear ();
  s.close ();
  s.clear ();
  if (gen)
    {
      s.open (file);
      if (!s)
	{
	  CmtError::set (CmtError::file_access_error, file);
	  return -1;
	}
      s.exceptions (ios::failbit | ios::badbit);
      try
	{
	  //	  if (os.str ().size () != 0)
	  //	    {
	      //  cerr << file << " contents already generated" << endl;
	      s << os.str ();
	      //	    }
	      //	  else
	      //	    {
	      //	      InGen.build (constituent, s);
	      //	    }
	  s.close (); // ios_base::failbit
	}
      catch (const ios::failure& e)
	{
	  CmtSystem::close_ostream (NULL, file + ": " + cmt_string (e.what ()));
	  return -1;
	}
    }

#endif
  return 0;
}

//--------------------------------------------------
int Generator::build_dependencies_infile (const Constituent* pconstituent,
					  const cmt_string& outdir,
					  bool usecmt)
{
  cmt_string file ("dependencies");
  if (0 != pconstituent)
    file += "_" + pconstituent->name;
  file += ".in";

  if (outdir != "")
    {
      if (outdir [outdir.size () - 1] != CmtSystem::file_separator ())
	file = outdir + CmtSystem::file_separator () + file;
      else
	file = outdir + file;
    }

  ostringstream os;
  
  cmt_string preprocessor;
  Symbol* macro = Symbol::find ("preprocessor_command");
  if (macro != 0)
    {
      preprocessor = macro->resolve_macro_value ();
    }

  if (preprocessor == "")
    {
      const Use* current_use = &Use::current ();
      Use::UsePtrVector uses (Use::get_ordered_uses ());
      uses.push_back (&Use::current ());
      
      if (current_use->include_path == "none")
	os << "include_path none\n";

      for (int i = uses.size () - 1; i >= 0; i--)
	{
	  const Use* use = uses[i];
	  if (use->discarded) continue;
	  if (use->m_hidden) continue;
	  if (!use->located ()) continue;
	  
	  cmt_string package_name = use->get_package_name ();
	  if (package_name == "CMT") continue;
	  
	  const Symbol* filter_macro = Symbol::find (package_name + "_header_file_filter");
	  if (filter_macro == 0) continue;
	  const cmt_string filter_expr (filter_macro->resolve_macro_value ());
	  if (filter_expr.size () == 0) continue;
	  
	  const Symbol* stamp_macro = Symbol::find (package_name + "_header_file_stamp");
	  cmt_string stamp;
	  if (stamp_macro != 0)
	    {
	      stamp = stamp_macro->resolve_macro_value ();
	    }
	  else
	    {
	      use->get_full_path (stamp);
	      switch (use->style)
		{
		case cmt_style:
		  stamp += CmtSystem::file_separator ();
		  stamp += "cmt";
		  break;
		case mgr_style:
		  stamp += CmtSystem::file_separator ();
		  stamp += "mgr";
		  break;
                default:
		  break;
		}
	      stamp += CmtSystem::file_separator ();
	      stamp += "cmt_header_file.stamp";
	    }
	  if (!CmtSystem::test_file (stamp)) continue;

	  os << "macro " + package_name + "_header_file_filter ";
	  os << CmtSystem::quote (filter_expr, " \t");
	  //os << CmtSystem::quote (filter_macro->resolve_macro_value (), " \t");
	  os << "\n";
	  os << "macro " + package_name + "_header_file_stamp ";
	  os << CmtSystem::quote (stamp, " \t");
	  os << "\n";
	}
    }
  else
    {
      os << "macro preprocessor_command ";
      os << CmtSystem::quote (preprocessor, " \t");
      os << "\n";
      macro = Symbol::find ("includes");
      if (0 != macro)
	{
	  os << "macro includes ";
	  os << CmtSystem::quote (macro->resolve_macro_value (), " \t");
	  os << "\n";
	}
      if (0 == pconstituent)
	{
	  const Constituent::ConstituentVector& constituents =
	    Constituent::constituents ();
	  for (int n = 0; n < constituents.size (); n++)
	    {
	      const Constituent& constituent = constituents[n];
	      if (constituent.has_target_tag) continue;
	      if (constituent.type == Document) continue;
	      cmt_string prefix;
	      switch (constituent.type)
		{
		case Application:
		  prefix = "app_";
		  break;
		case Library:
		  prefix = "lib_";
		  break;
                default:
		  break;
		}
	      cmt_string macro_name (prefix + constituent.name + "_cppflags");
	      macro = Symbol::find (macro_name);
	      if (0 != macro)
		{
		  os << "macro_append " + macro_name + " ";
		  os << CmtSystem::quote (macro->resolve_macro_value (), " \t");
		  os << "\n";
		}
	    }
	}
      else if (pconstituent->type != Document)
	{
	  const Constituent& constituent = *pconstituent;
	  cmt_string prefix;
	  switch (constituent.type)
	    {
	    case Application:
	      prefix = "app_";
	      break;
	    case Library:
	      prefix = "lib_";
	      break;
            default:
	      break;
	    }
	  cmt_string macro_name (prefix + constituent.name + "_cppflags");
	  macro = Symbol::find (macro_name);
	  if (0 != macro)
	    {
	      os << "macro_append " + macro_name + " ";
	      os << CmtSystem::quote (macro->resolve_macro_value (), " \t");
	      os << "\n";
	    }
	}
    }

  // Write input requirements file

  bool gen (true);
  //  ostringstream os;
  ofstream s;
  //  s.open (file);
  s.open (file, ios::in);
  if (s) // file already exists
    {
      //      InGen.build (constituent, os);
      ostringstream osn;
      osn << s.rdbuf ();
      if (os.str () == osn.str ())
	{
	  //	  cerr << file << " up-to-date" << endl;
	  gen = false;
	}
    }
  s.clear ();
  s.close ();
  s.clear ();
  if (gen)
    {
      s.open (file);
      if (!s)
	{
	  CmtError::set (CmtError::file_access_error, file);
	  return -1;
	}
      s.exceptions (ios::failbit | ios::badbit);
      try
	{
	  //	  if (os.str ().size () != 0)
	  //	    {
	      //  cerr << file << " contents already generated" << endl;
	      s << os.str ();
	      //	    }
	      //	  else
	      //	    {
	      //	      InGen.build (constituent, s);
	      //	    }
	  s.close (); // ios_base::failbit
	}
      catch (const ios::failure& e)
	{
	  CmtSystem::close_ostream (NULL, file + ": " + cmt_string (e.what ()));
	  return -1;
	}
    }

  return 0;
}

//--------------------------------------------------
int Generator::build_constituent_makefile (const Constituent& constituent,
					   bool& dependencies,
					   const cmt_string& file)
{
  const cmt_string& package = Cmt::get_current_package ();

  switch (constituent.type)
    {
    case Application:
      //ApplicationContext.build (package, constituent, file);
      //break;
    case Library:
      LibraryContext.build (package, constituent, dependencies, file);
      break;
    case Document:
      DocumentContext.build (package, constituent, dependencies, file);
      break;
    }

  return (0);
}

//--------------------------------------------------
void Generator::build_constituent_makefile (const CmtSystem::cmt_string_vector& arguments)
{
  cmt_string name;
  cmt_string file;

  if (arguments.size () == 1)
    {
      file = "";
      name = arguments[0];
    }
  else if (arguments.size () == 2) // arguments[0].substr (0, 5) == "-out="
    {
      cmt_string arg = arguments[0];
      arg.erase (0, 5);
      file = arg;
      name = arguments[1];
    }
  else
    {
      CmtMessage::error ("build constituent_makefile : wrong arguments");
      //      cerr << "#CMT> build constituent_makefile : wrong arguments" << endl;
      return;
    }

  const Constituent* constituent = Constituent::find (name);
  if (constituent != 0)
    {
      bool dependencies (false);
      build_constituent_makefile (*constituent, dependencies, file);
    }
}

//--------------------------------------------------
void Generator::build_default_makefile ()
{
  DefaultMakefileContext.build ();
}

//--------------------------------------------------
void Generator::build_packages_makefile (const cmt_string& package,
					 const cmt_string& file)
{
  PackagesMakefileContext.build (package, file);
  //  UsesMakefileContext.build (package, file);
}

//--------------------------------------------------
void Generator::build_dependencies (const CmtSystem::cmt_string_vector& arguments)
{
  DependencyContext.build (arguments);
}

//--------------------------------------------------
void Generator::build_prototype (const cmt_string& file_name)
{
  PrototypeContext.build (file_name);
}

//--------------------------------------------------
void Generator::build_readme (const CmtSystem::cmt_string_vector& arguments)
{
  ReadmeContext.build (arguments);
}

class WinDefAwk : public PAwk
{
public :
  WinDefAwk (const cmt_string& library_name)
  {
    m_name = library_name;
  }

  void begin ()
  {
    cout << "LIBRARY " << m_name << endl;
    cout << "EXPORTS" << endl;
  }

  void filter (const cmt_string& line)
  {
    if (line.find ("External") == cmt_string::npos) return;
    if (line.find ("??_") != cmt_string::npos)
      {
	if (line.find ("operator/=") == cmt_string::npos) return;
	// Keep operator /= .
      }

    CmtSystem::cmt_string_vector words;
    CmtSystem::split (line, " \t", words);
    if (words.size () >= 7)
      {
	int pos = 7;

	cmt_string& fifth_word = words[4];
	if (fifth_word == "()") pos = 7;
	else if (fifth_word == "External") pos = 6;
	else return;

	cmt_string& symbol = words[pos];
	if (symbol[0] == '_') symbol.erase (0, 1);
	symbol.replace_all ("\r", "");
	symbol.replace_all ("\n", "");

	if ((pos == 6) && 
	    ((line.find(": static") != cmt_string::npos) ||
	     (line.find("(class") != cmt_string::npos)) )
	  {
	    // static data members are not DATA :
	    // extern objects are not DATA :
	    cout << " " << symbol << " " << endl;
	  } 
	else if (pos == 6)
	  {
	    // DATA :
	    cout << " " << symbol << "\tDATA" << endl;
	  } 
	else
	  {
	    // code :
	    cout << " " << symbol << " " << endl;
	  } 
      }
  }

  void end ()
  {
  }

private:
  cmt_string m_name;
};

//--------------------------------------------------
//void Generator::build_windefs (const cmt_string& library_name)
void Generator::build_windefs (const CmtSystem::cmt_string_vector& arguments)
{
  cmt_string name;
  //  CmtSystem::cmt_string_vector files;
  cmt_string files;

  for (int i = 0; i < arguments.size (); i++)
    {
      const cmt_string& w = arguments[i];
      if (w.substr (0, 6) == "-name=" || w.substr (0, 6) == "-name:" ||
	  w.substr (0, 6) == "/name:" || w.substr (0, 6) == "/name=")
	w.substr (6, name);
      else if (w.substr (0, 1) == "@" && w.size () > 1)
	{
	  cmt_string commandfile;
	  w.substr (1, commandfile);
	  if (!CmtSystem::test_file (commandfile))
	    {
	      CmtMessage::warning ("No such file `" + commandfile + "'.");
	      continue;
	    }
	  cmt_string text;
	  if (!text.read (commandfile))
	    {
	      CmtMessage::warning ("Could not read `" + commandfile + "'.");
	      continue;
	    }
	  text.replace_all ("\r", " ");
	  text.replace_all ("\n", " ");
	  files += " " + text;
	  /*
	  CmtSystem::cmt_string_vector words;
          CmtSystem::split (text, " \t", words);
	  for (int i = 0; i < words.size (); i++)
	    {
	      files.push_back (words[i]);
	    }
	  */
	}
      else
	files += " " + w;
      //	files.push_back (w);
    }

  if (files.size () == 0)
    {
      CmtMessage::error ("build_windefs: no files specified");
      return;
    }
  if (name == "")
    {
      CmtSystem::cmt_string_vector words;
      CmtSystem::split (files, " \t", words);
      if (words.size () == 0)
	{
	  CmtMessage::error ("build_windefs: no files specified");
	  return;
	}
      cmt_string suffix;
      CmtSystem::get_dot_suffix (words[0], suffix);
      CmtSystem::basename (words[0], suffix, name);
    }
  if (name == "")
    {
      CmtMessage::error ("build_windefs: cannot determine library name");
      return;
    }

  //  cmt_string bin;

  //  CmtSystem::dirname (library_name, bin);
  //  CmtSystem::get_dot_suffix (library_name, suffix);
  //  CmtSystem::basename (library_name, suffix, name);
  
  //  if (!CmtSystem::cd (bin)) return;
  
  //  cmt_string command;
  
  cmt_string command ("dumpbin /symbols");
  //  command += library_name;
  /*
  for (int i = 0; i < files.size (); i++)
    {
      command += " " + files[i];
    }
  */
  command += " " + files;
	
  WinDefAwk filter (name);
  
  filter.run (command, "SECT");
}

//--------------------------------------------------
void Packager::begin ()
{
  m_package_name = "";
}

void Packager::filter (const cmt_string& line)
{
  CmtSystem::cmt_string_vector words;

  CmtSystem::split (line, " ", words);
  if (words.size () > 1)
    {
      cmt_string& w = words[0];

      if (w == "package")
        {
          m_package_name = words[1];

	  int pos = m_package_name.find (";");
          if (pos != cmt_string::npos) m_package_name.erase (pos);
          m_package_name.replace_all (".", CmtSystem::file_separator ());
        }
    }
}

cmt_string& Packager::package_name ()
{
  return (m_package_name);
}

//------------------------------------------------------------------------
InGenerator::Buffer::Buffer ()
  : m_initialized (false), m_usecmt (true)
{ }

//------------------------------------------------------------------------
void InGenerator::Buffer::set_names (const CmtSystem::cmt_string_vector& names)
{
  m_names = names;
}

//------------------------------------------------------------------------
int InGenerator::Buffer::print (ostream& s)
{
  int retval (0);
  retval = initialize ();
  if (0 > retval) return retval;
  s << m_buffer.str ();
  return 0;
}

//------------------------------------------------------------------------
void InGenerator::Buffer::set_uses (bool usecmt)
{
  m_usecmt = usecmt;
}

//------------------------------------------------------------------------
void InGenerator::Buffer::set_pedantic (bool pedantic)
{
  m_pedantic = pedantic;
}

//------------------------------------------------------------------------
int InGenerator::Languages::initialize ()
{
  int errors (0);
  if (m_initialized) return - errors;
  m_initialized = true;
  for (int i = 0; i < m_names.size (); i++)
    {
      const cmt_string& name = m_names[i];
      Language& p (Language::find (name));
      if (Language::null () == p)
	{
	  CmtError::set (CmtError::language_not_found, name);
	  errors += 1;
	  continue;
	}
      p.show (Requirements, m_buffer);
      /*
      if (!p->use->get_package ()->is_cmt ())
	p->print (Requirements, m_buffer);
      */
    }
  //  cerr << "Languages::initialize: " << m_buffer.str ();
  return - errors;
}

//------------------------------------------------------------------------
int InGenerator::Fragments::initialize ()
{
  int errors (0);
  if (m_initialized) return - errors;
  m_initialized = true;
  for (int i = 0; i < m_names.size (); i++)
    {
      const cmt_string& name = m_names[i];
      if (Cmt::get_debug ())
	{
	  cout << "InGenerator::Fragments::initialize> " << name << endl;
	}
      Fragment* fragment (Fragment::find (name));
      if (0 == fragment)
	{
	  if (m_pedantic)
	    {
	      CmtError::set (CmtError::fragment_not_found, name + " (standard)");
	      errors += 1;
	    }
	  else
	    {
	      CmtMessage::warning
		(CmtError::get_error_name (CmtError::fragment_not_found)
		 + ": " + name + " (standard)");
	    }
	  continue;
	}
      if (m_usecmt || !fragment->use->get_package ()->is_cmt ())
	if (1 != fragment->print (Requirements, m_buffer))
	  {
	    if (m_pedantic)
	      {
		CmtError::set (CmtError::fragment_not_found, name + " (standard)");
		errors += 1;
	      }
	    else
	      {
		CmtMessage::warning
		  (CmtError::get_error_name (CmtError::fragment_not_found)
		   + ": " + name + " (standard)");
	      }
	    continue;
	  }
    }
  //  cerr << "initialize: " << m_buffer.str ();
  return - errors;
}

//------------------------------------------------------------------------
InGenerator::InGenerator (bool usecmt, bool pedantic)
  : m_usecmt (usecmt), m_pedantic (pedantic)
{
  m_pedantic = (0 != CmtSystem::getenv ("CMTPEDANTIC").size () ||
		0 != CmtSystem::getenv ("PEDANTIC").size ());

  m_common.set_pedantic (m_pedantic);
  m_application.set_pedantic (m_pedantic);
  m_application_library.set_pedantic (m_pedantic);
  m_library.set_pedantic (m_pedantic);
  m_document.set_pedantic (m_pedantic);
  m_languages.set_pedantic (m_pedantic);

  CmtSystem::cmt_string_vector common;
  common.push_back ("make_header");
  common.push_back ("dependencies");
  common.push_back ("cleanup_header");
  m_common.set_names (common);
  m_common.set_uses (usecmt);

  CmtSystem::cmt_string_vector application;
  application.push_back ("java_header");
  application.push_back ("application_header");
  application.push_back ("application");
  application.push_back ("check_java");
  application.push_back ("cleanup_application");
  application.push_back ("cleanup_objects");
  application.push_back ("check_application");

  CmtSystem::cmt_string_vector application_library;
  application_library.push_back ("protos_header");
  application_library.push_back ("buildproto");
  application_library.push_back ("dependencies_and_triggers");
  application_library.push_back ("java");
  application_library.push_back ("java_copy");
  application_library.push_back ("cleanup");
  application_library.push_back ("cleanup_java");
  m_application_library.set_names (application_library);
  m_application_library.set_uses (usecmt);

  CmtSystem::cmt_string_vector library;
  library.push_back ("jar_header");
  library.push_back ("library_header");
  library.push_back ("jar");
  library.push_back ("library_no_share");
  library.push_back ("library_no_static");
  library.push_back ("library");
  library.push_back ("cleanup_library");

  CmtSystem::cmt_string_vector languages;
  Language::LanguageVector& Languages = Language::languages ();
  for (int n = 0; n < Languages.size (); n++)
    {
      Language& language = Languages[n];
      //language.setup_fragments ();
      application.push_back (language.fragment_name); //application.name ();
      if (!(language == "java"))
	library.push_back (language.fragment_name + "_library"); //library.name ();
      if (m_usecmt || !language.m_use->get_package ()->is_cmt ())
//       if (!language.m_use->get_package ()->is_cmt ())
 	{
	  languages.push_back (language.m_name);
 	}
      else if (language.dependencies_options () !=
	  language.dependencies_options_expanded ())
	{
	  languages.push_back (language.m_name);
	}
    }
  m_languages.set_names (languages);
  m_languages.set_uses (usecmt);
  m_application.set_names (application);
  m_application.set_uses (usecmt);
  m_library.set_names (library);
  m_library.set_uses (usecmt);
  
  CmtSystem::cmt_string_vector document;
  document.push_back ("document_header");
  document.push_back ("dependency");
  m_document.set_names (document);
  m_document.set_uses (usecmt);
}

//------------------------------------------------------------------------
int InGenerator::build (const Constituent& constituent, ostream& s)
{
  int retval (0);

  constituent.show (s);
  retval += m_common.print (s);

  switch (constituent.type)
    {
    case Application:
      retval += m_application.print (s);
      retval += m_application_library.print (s);
      retval += m_languages.print (s);
      break;
    case Library:
      retval += m_application_library.print (s);
      retval += m_library.print (s);
      retval += m_languages.print (s);
      break;
    case Document:
      Fragment* fragment (Fragment::find (constituent.generator));
      if (Cmt::get_debug ())
	{
	  cout << "InGenerator::build> " << constituent.generator
	       << " |name> " << constituent.name << endl;
	}
      if (0 == fragment)
	{
	  CmtError::set (CmtError::fragment_not_found, constituent.generator
			 + " (document " + constituent.name + ")");
	  return retval -= 1;
	}
      if (m_usecmt || !fragment->use->get_package ()->is_cmt ())
	if (1 != fragment->print (Requirements, s))
	  {
	    CmtError::set (CmtError::fragment_not_found, constituent.generator
			   + " (document " + constituent.name + ")");
	    return retval -= 1;
	  }
      if (0 != fragment->header.size ())
	{
	  if (Cmt::get_debug ())
	    {
	      cout << "InGenerator::build> " << fragment->header
		   << " |name> " << constituent.name << endl;
	    }
	  Fragment* header (Fragment::find (fragment->header));
	  if (0 == header)
	    {
	      CmtError::set (CmtError::fragment_not_found, fragment->header
			     + " (document " + constituent.name + ")");
	      return retval -= 1;
	    }
	  //	  if (header->use != fragment->use &&
	  if (m_usecmt || !header->use->get_package ()->is_cmt ())
	    if (1 != header->print (Requirements, s))
	      {
		CmtError::set (CmtError::fragment_not_found, fragment->header
			       + " (document " + constituent.name + ")");
		return retval -= 1;
	      }
	}
      if (0 != fragment->trailer.size ())
	{
	  if (Cmt::get_debug ())
	    {
	      cout << "InGenerator::build> " << fragment->trailer
		   << " |name> " << constituent.name << endl;
	    }
	  Fragment* trailer (Fragment::find (fragment->trailer));
	  if (0 == trailer)
	    {
	      CmtError::set (CmtError::fragment_not_found, fragment->trailer
			     + " (document " + constituent.name + ")");
	      return retval -= 1;
	    }
	  //	  if (trailer->use != fragment->use &&
	  if (m_usecmt || !trailer->use->get_package ()->is_cmt ())
	    if (1 != trailer->print (Requirements, s))
	      {
		CmtError::set (CmtError::fragment_not_found, fragment->trailer
			       + " (document " + constituent.name + ")");
		return retval -= 1;
	      }
	}
      retval += m_document.print (s);
      break;
    }

  if (m_pedantic)
    return retval;
  else
    return 0;
}
//------------------------------------------------------------------------
