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

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

#include "cmt_use.h"
#include "cmt_symbol.h"
#include "cmt_system.h"
#include "cmt_database.h"

//-------------------------------------------------------------
class SetBuilder : public ValueBuilder
{
public:
  const cmt_string build (const Symbol& symbol,
                          const cmt_string& tag_name = "");
  const cmt_string clean (const Symbol& symbol,
                          const cmt_string& tag_name = "")
      {
        static const cmt_string empty = "";
        return (empty);
      }
};
//-------------------------------------------------------------

//-------------------------------------------------------------
class PathBuilder : public ValueBuilder
{
public:
  const cmt_string build (const Symbol& symbol,
                          const cmt_string& tag_name = "");
  const cmt_string clean (const Symbol& symbol,
                          const cmt_string& tag_name = "");
};
//-------------------------------------------------------------

//-------------------------------------------------------------
class MacroBuilder : public ValueBuilder
{
public:
  const cmt_string build (const Symbol& symbol,
                          const cmt_string& tag_name = "");
  const cmt_string clean (const Symbol& symbol,
                          const cmt_string& tag_name = "")
      {
        static const cmt_string empty = "";
        return (empty);
      }
};
//-------------------------------------------------------------

//-------------------------------------------------------------
class ScriptBuilder : public ValueBuilder
{
public:
  const cmt_string build (const Symbol& symbol,
                          const cmt_string& tag_name = "");
  const cmt_string clean (const Symbol& symbol,
                          const cmt_string& tag_name = "")
      {
        static const cmt_string empty = "";
        return (empty);
      }
};
//-------------------------------------------------------------

//-------------------------------------------------------------
class ActionBuilder : public ValueBuilder
{
public:
  const cmt_string build (const Symbol& symbol,
                          const cmt_string& tag_name = "");
  const cmt_string clean (const Symbol& symbol,
                          const cmt_string& tag_name = "")
      {
        static const cmt_string empty = "";
        return (empty);
      }
};
//-------------------------------------------------------------

//-------------------------------------------------------------
class symbol_marker
{
public:
  symbol_marker ()
  {
    ptr = cmt_string::npos;
    pattern = 0;
    intro = 0;
  }

  symbol_marker (int a_ptr, char a_pattern, int a_intro)
  {
    ptr = a_ptr;
    pattern = a_pattern;
    intro = a_intro;
  }

  symbol_marker (const symbol_marker& other)
  {
    ptr = other.ptr;
    pattern = other.pattern;
    intro = other.intro;
  }

  void set (int a_ptr, char a_pattern, int a_intro)
  {
    ptr = a_ptr;
    pattern = a_pattern;
    intro = a_intro;
  }

  static symbol_marker& get_lowest (symbol_marker markers[], int count)
  {
    static symbol_marker result;
    int real_count = 0;
    int i;

      // Check that at least one marker has result

    for (i = 0; i < count; i++)
      {
        if (markers[i].ptr != cmt_string::npos) real_count++;
      }

    if (real_count == 0) return (result);

    // since we've passed the previous test, 
    // at least one entry is not npos.
    // Now discards other npos by moving them to the end
    
    for (i = 0; i < count;)
      {
        if (markers[i].ptr == cmt_string::npos)
          {
            markers[i] = markers[count-1];
            count--;
            if (count == 0) break;
          }
        else
          {
            i++;
          }
      }
    
    if (count == 0) return (result);
    
    // now all entries in [0, count-1] are not npos
    // let's sort the lowest one in [0]
    
    for (i = 1; i < count;)
      {
        if (markers[0].ptr > markers[i].ptr)
          {
            symbol_marker temp = markers[0];
            markers[0] = markers[i];
            markers[i] = temp;
            i = 1;
          }
        else
          {
            i++;
          }
      }
    
    return (markers[0]);
  }

  int ptr;
  char pattern;
  int intro;
};
//-------------------------------------------------------------

/**

   Attempt to substitute all occurrences of

      ${<symbol_name>}
      $(<symbol_name>)
      %<symbol_name>%    [on Windows only]

      by the specified value in the given text.

    This is for one symbol only

 */
static void resolve_value (cmt_string& text,
                           const cmt_string& symbol_name,
                           const cmt_string& value)
{
  static cmt_string pattern;

  pattern = "${";
  pattern += symbol_name;
  pattern += "}";

  text.replace_all (pattern, value);

  pattern = "$(";
  pattern += symbol_name;
  pattern += ")";

  text.replace_all (pattern, value);

#ifdef WIN32
  pattern = "%";
  pattern += symbol_name;
  pattern += "%";

  text.replace_all (pattern, value);
#endif
}

/**

   Attempt to substitute into the specified text all occurrences of

      ${xxx}
      $(xxx)
      `xxx`
      %xxx%    [on Windows only]

      by the appropriate value:

        for `xxx` : 

            xxx is considered as a shell command. Value is the result of its execution

        for other patterns:

            if xxx is a symbol name, its value is substituted to the pattern
            otherwise, xxx is tried as an environment variable


     ===> In all cases, the pattern is filtered away.


 */
static void resolve_value (cmt_string& text)
{
  //static cmt_regexp reg ("[$%`]");

  //if (!reg.match (text)) return;

  cmt_string pattern;
  cmt_string symbol_name;
  char end_pattern;

  int start = 0;

  for (;;)
    {
      int begin;
      int end;

      symbol_marker markers[4];
      int num = 0;

      markers[num].set (text.find (start, "$("), ')', 2); num++;
      markers[num].set (text.find (start, "${"), '}', 2); num++;
      markers[num].set (text.find (start, "`"), '`', 1); num++;

#ifdef WIN32
      markers[num].set (text.find (start, "%"), '%', 1); num++;
#endif

      // Find the first matching pattern

      symbol_marker& marker = symbol_marker::get_lowest (markers, num);

      begin = marker.ptr;

      if (begin == cmt_string::npos) break;

      end_pattern = marker.pattern;
      start = begin + marker.intro;

      end = text.find (start, end_pattern);
      if (end == cmt_string::npos)
        {
          // The pattern is a fake one (no ending!)
          start++;
          continue;
        }

      // This should never happen...
      if (end < begin) break;

      // Extract the complete pattern
      text.substr (begin, end - begin + 1, pattern);

      // Then only the symbol name
      text.substr (begin + marker.intro, end - begin - marker.intro, symbol_name);

      if (text[begin] == '`')
        {
          cmt_string command = symbol_name;
          resolve_value (command);

            // The value is a shell command that first needs
            // to be applied. The output of the command is then substituted

          cmt_string result;

          Symbol::all_set ();
          CmtSystem::execute (command, result);
          
          int pos;
          pos = result.find ('\n');
          if (pos != cmt_string::npos) result.erase (pos);
          pos = result.find ('\r');
          if (pos != cmt_string::npos) result.erase (pos);
          
          if (Cmt::get_debug ())
            {
              cout << "   Executing [" << command << "] to expand a symbol value =>[" 
                   << result << "]" << endl;
            }
          
          text.replace_all (pattern, result);

          // The substitution will restart from the same place
          // allowing for recursive replacements
          start = begin;
        }
      else
        {
          Symbol* symbol = Symbol::find (symbol_name);
          if (symbol != 0)
            {
                // Symbol found
              cmt_string value = symbol->resolve_macro_value ();
              text.replace_all (pattern, value);
              
                // The substitution will restart from the same place
                // allowing for recursive replacements
              start = begin;
            }
          else
            {
                // Symbol not found. Look for env. variable
              cmt_string value = CmtSystem::getenv (symbol_name);
              
                // When the env. variable is not defined, the replacement is empty
                // thus all $(xxx) ${xxx} %xxx% patterns are always filtered away.
              text.replace_all (pattern, value);
              
                // The substitution will restart from the same place
                // allowing for recursive replacements
              start = begin;
            }
        }
    }
}

/**

   Attempt to substitute all occurrences of

      ${xxx}
      $(xxx)
      `xxx`
      %xxx%    [on Windows only]

      by the appropriate value:

        for `xxx` : 

            xxx is considered as a shell command. Value is the result of its execution

        for other patterns:

            if xxx is a macro name, its value is substituted to the pattern

            if xxx is a set or a path, it is kept in place (after fixing the pattern
            according to the OS style)

            otherwise it is simply kept in place

 */
static void resolve_value_for_macros (cmt_string& text)
{
  cmt_string pattern;
  cmt_string symbol_name;
  char end_pattern;

  int start = 0;

  for (;;)
    {
      //
      // Try and substitute all ${xxx} $(xxx) or %xxx% patterns
      // using symbol values, only when the symbol is a macro.
      //

      int begin;
      int end;

      symbol_marker markers[4];
      int num = 0;

      markers[num].set (text.find (start, "$("), ')', 2); num++;
      markers[num].set (text.find (start, "${"), '}', 2); num++;

#ifdef WIN32
      markers[num].set (text.find (start, "%"), '%', 1); num++;
#endif

      markers[num].set (text.find (start, "`"), '`', 1); num++;

      // Find the first of three patterns

      symbol_marker& marker = symbol_marker::get_lowest (markers, num);

      begin = marker.ptr;

      if (begin == cmt_string::npos) break;

      end_pattern = marker.pattern;
      start = begin + marker.intro;

      end = text.find (start, end_pattern);
      if (end == cmt_string::npos)
        {
          // The pattern is a fake one (no ending!)
          start++;
          continue;
        }

      // This should never happen...
      if (end < begin) break;

      // Extract the complete pattern
      text.substr (begin, end - begin + 1, pattern);

      // Then only the macro name
      text.substr (begin + marker.intro, end - begin - marker.intro, symbol_name);

      if (text[begin] == '`')
        {
          cmt_string command = symbol_name;
          resolve_value (command);

            // The macro value is a shell command that first needs
            // to be applied. The output of the command is substituted

          cmt_string result;

          Symbol::all_set ();
          CmtSystem::execute (command, result);
          
          int pos;
          pos = result.find ('\n');
          if (pos != cmt_string::npos) result.erase (pos);
          pos = result.find ('\r');
          if (pos != cmt_string::npos) result.erase (pos);
          
          if (Cmt::get_debug ())
            {
              cout << "   Executing [" << command << "] to expand a macro value =>[" 
                   << result << "]" << endl;
            }
          
          text.replace_all (pattern, result);

          // The substitution will restart from the same place
          // allowing for recursive replacements
          start = begin;
        }
      else
        {
          Symbol* macro = Symbol::find (symbol_name);
          if ((macro != 0) && 
              (macro->type == Symbol::SymbolMacro))
            {
                // Macro found
              cmt_string value = macro->resolve_macro_value ();
              text.replace_all (pattern, value);

                // The substitution will restart from the same place
                // allowing for recursive replacements
              start = begin;
            }
          else if ((macro == 0) || 
                   ((macro->type == Symbol::SymbolSet) || (macro->type == Symbol::SymbolPath)))
            {
                // Set found
                // ensure that the delimiters will match the OS dependent
                // delimiters.
              
              cmt_string pattern_close = marker.pattern;
              
              if (pattern_close != CmtSystem::ev_close ())
                {
                  cmt_string new_pattern;
                  
                  new_pattern = CmtSystem::ev_open ();
                  new_pattern += symbol_name;
                  new_pattern += CmtSystem::ev_close ();
                  
                  text.replace (pattern, new_pattern);
                }
              
              start = end + 1;
            }
          else
            {
              start = end + 1;
            }
        }
    }
}

/**

   This function suppress all OS delimiters ie ${ } or % % and replaces them
   by the pure macro delimiters $( )

   `xxx` pattern is replaced by the result of the execution of the command

 */
static void suppress_OS_delimiters (cmt_string& text)
{
  cmt_string pattern;
  cmt_string symbol_name;
  char end_pattern;

  int start = 0;

  for (;;)
    {
      int begin;
      int end;

      symbol_marker markers[3];
      int num = 0;

      markers[num].set (text.find (start, "${"), '}', 2); num++;
      markers[num].set (text.find (start, "`"), '`', 1); num++;

#ifdef WIN32
      markers[num].set (text.find (start, "%"), '%', 1); num++;
#endif

      // Find the first of three patterns

      symbol_marker& marker = symbol_marker::get_lowest (markers, num);

      begin = marker.ptr;

      if (begin == cmt_string::npos) break;

      end_pattern = marker.pattern;
      start = begin + marker.intro;

      end = text.find (start, end_pattern);
      if (end == cmt_string::npos)
        {
          // The pattern is a fake one (no ending!)
          start++;
          continue;
        }

      // This should never happen...
      if (end < begin) break;

      // Extract the complete pattern
      text.substr (begin, end - begin + 1, pattern);

      // Then only the macro name
      text.substr (begin + marker.intro, end - begin - marker.intro, symbol_name);

      if (text[begin] == '`')
        {
          cmt_string command = symbol_name;
          resolve_value (command);

            // The macro value is a shell command that first needs
            // to be applied. The output of the command is substituted

          cmt_string result;

          Symbol::all_set ();
          CmtSystem::execute (command, result);
          
          int pos;
          pos = result.find ('\n');
          if (pos != cmt_string::npos) result.erase (pos);
          pos = result.find ('\r');
          if (pos != cmt_string::npos) result.erase (pos);
          
          if (Cmt::get_debug ())
            {
              cout << "   Executing [" << command << "] to expand a macro value =>[" 
                   << result << "]" << endl;
            }
          
          text.replace_all (pattern, result);

          // The substitution will restart from the same place
          // allowing for recursive replacements
          start = begin;
        }
      else
        {
          cmt_string new_pattern;

          new_pattern = "$(";
          new_pattern += symbol_name;
          new_pattern += ")";

          text.replace (pattern, new_pattern);

          start = begin;
        }
    }
}

//-------------------------------------------------------------
/*                                                          */
/*  Operations on SymbolValues                              */
/*                                                          */
//-------------------------------------------------------------

//-------------------------------------------------------------
SymbolValue::SymbolValue ()
{
  tag = 0;
}

//-------------------------------------------------------------
SymbolValue::~SymbolValue ()
{
  tag = 0;
}

//-------------------------------------------------------------
/*                                                          */
/*  Operations on Symbols                                   */
/*                                                          */
//-------------------------------------------------------------

//-------------------------------------------------------------
Symbol* Symbol::create (const cmt_string& name,
                        CommandType command,
                        Use* use)
{
  static SetBuilder Set;
  static PathBuilder Path;
  static MacroBuilder Macro;
  static ScriptBuilder Script;
  static ActionBuilder Action;

  static SymbolVector& Symbols = symbols ();
  static SymbolMap& SymbolMap = symbol_map ();

  SymbolType type = SymbolUndefined;

  switch (command)
    {
    case CommandSet:
    case CommandSetAppend:
    case CommandSetPrepend:
    case CommandSetRemove:
    case CommandSetRemoveRegexp:
      type = SymbolSet;
      break;
    case CommandPath:
    case CommandPathAppend:
    case CommandPathPrepend:
    case CommandPathRemove:
    case CommandPathRemoveRegexp:
      type = SymbolPath;
      break;
    case CommandMacro:
    case CommandMacroAppend:
    case CommandMacroPrepend:
    case CommandMacroRemove:
    case CommandMacroRemoveRegexp:
    case CommandMacroRemoveAll:
    case CommandMacroRemoveAllRegexp:
      type = SymbolMacro;
      break;
    case CommandAction:
      type = SymbolAction;
      break;
    case CommandAlias:
      type = SymbolAlias;
      break;
    case CommandSetupScript:
      type = SymbolSetupScript;
      break;
    case CommandCleanupScript:
      type = SymbolCleanupScript;
      break;
    }

  {
    Symbol* symbol;

    symbol = find (name);
    if (symbol != 0) 
      {
        if (symbol->type != type)
          {
            ActionType action = Cmt::get_action ();

            if ((!Cmt::get_quiet ()) &&
                (action != action_build_tag_makefile))
              {
                cmt_string s1;
                cmt_string s2;

                switch (symbol->type)
                  {
                  case SymbolSet:
                    s1 = "set";
                    break;
                  case SymbolPath:
                    s1 = "path";
                    break;
                  case SymbolMacro:
                    s1 = "macro";
                    break;
                  case SymbolSetupScript:
                    s1 = "setup_script";
                    break;
                  case SymbolCleanupScript:
                    s1 = "cleanup_script";
                    break;
                  case SymbolAction:
                    s1 = "action";
                    break;
                  case SymbolAlias:
                    s1 = "alias";
                    break;
                  } 

                switch (type)
                  {
                  case SymbolSet:
                    s2 = "set";   
                    break;
                  case SymbolPath:
                    s2 = "path";   
                    break;
                  case SymbolMacro:
                    s2 = "macro";   
                    break;
                  case SymbolSetupScript:
                    s2 = "setup_script";  
                    break;
                  case SymbolCleanupScript:
                    s2 = "cleanup_script";  
                    break;
                  case SymbolAction:
                    s2 = "action";
                    break;
                  case SymbolAlias:
                    s2 = "alias";
                    break;
                  } 

                cerr << "#CMT> Warning: Symbol " << name 
                     << " inconsistently redeclared from " << s1 << " to " << s2;
                if (use != 0) cerr << " in package " << use->get_package_name ();
                cerr << endl;
              }
          }

        return (symbol);
      }
  }

  Symbol& symbol = Symbols.add ();
  SymbolMap.add (name, symbol);

  symbol.name  = name;
  symbol.scope = use->get_current_scope ();
  symbol.type  = type;

  symbol.value_lists.clear ();

  switch (type)
    {
    case SymbolSet:
      symbol.builder = &Set;
      break;
    case SymbolPath:
      symbol.builder = &Path;
      break;
    case SymbolAlias:
      symbol.builder = &Set;
      break;
    case SymbolMacro:
      symbol.builder = &Macro;
      break;
    case SymbolSetupScript:
    case SymbolCleanupScript:
      symbol.builder = &Script;
      break;
    case SymbolAction:
      symbol.builder = &Action;
      break;
    }

  symbol.selected_value = -1;

  return (&symbol);
}

//-------------------------------------------------------------
Symbol* Symbol::find (const cmt_string& name)
{
  static SymbolMap& SymbolMap = symbol_map ();

  Symbol* result = 0;

  result = SymbolMap.find (name);

  return (result);
}

//-------------------------------------------------------------
int Symbol::symbol_number ()
{
  static SymbolVector& Symbols = symbols ();

  return (Symbols.size ());
}

//-------------------------------------------------------------
Symbol::SymbolVector& Symbol::symbols ()
{
  static Database& db = Database::instance ();
  static SymbolVector& Symbols = db.symbols ();

  return (Symbols);
}

//-------------------------------------------------------------
Symbol::SymbolMap& Symbol::symbol_map ()
{
  static Database& db = Database::instance ();
  static SymbolMap& SymbolMap = db.symbol_map ();

  return (SymbolMap);
}

/**
   Filter out faulty items of a path-like symbol value
 */
void Symbol::filter_path_value (const cmt_string& name, cmt_string& text)
{
  CmtSystem::cmt_string_vector paths;
                  
  CmtSystem::split (text, CmtSystem::path_separator (), paths);
                  
  for (int j = 0; j < paths.size (); ++j)
    {
      cmt_string& t = paths[j];

      if (!CmtSystem::test_directory (t))
	{
	  if (Cmt::get_warnings ())
	    {
	      cerr << "#CMT> Warning: Wrong " << name << " item " << t << endl;
	    }
	    
	  t = "";
	}
    }
  
  Cmt::vector_to_string (paths, CmtSystem::path_separator (), text);

  for (;;)
    {
      int sz = text.size ();

      if (sz == 0) break;

      if ((text[0] == ';') || (text[0] == ':'))
	{
	  text.erase (0, 1);
	}
      else if ((text[sz-1] == ';') || (text[sz-1] == ':'))
	{
	  text.erase (sz-1, 1);
	}
      else
	{
	  break;
	}
    }

  text.replace_all ("::", ":");
  text.replace_all (";;", ";");
}


//-------------------------------------------------------------
Symbol& Symbol::symbol (int index)
{
  static SymbolVector& Symbols = symbols ();

  return (Symbols[index]);
}

//-------------------------------------------------------------
void Symbol::action (const CmtSystem::cmt_string_vector& words,
                     CommandType command_type,
                     Use* use)
{
  int number;
  Symbol* symbol;
  Tag* tag;

  if (words.size () < 1) return;
  cmt_string name = words[1];

  if ((command_type == CommandSetupScript) ||
      (command_type == CommandCleanupScript))
    {
      cmt_string full_name;

      Symbol::expand (name);

      if (name != "")
        {
          if (CmtSystem::absolute_path (name)) 
            {
              full_name = name;
            }
          else
            {
#ifdef WIN32
              full_name = "%";
#else
              full_name = "${";
#endif
              full_name += use->prefix;
              full_name += "ROOT";
#ifdef WIN32
              full_name += "%";
#else
              full_name += "}";
#endif
              full_name += CmtSystem::file_separator ();
              if (use->style == cmt_style) full_name += "cmt";
              else if (use->style == no_version_style) full_name += "cmt";
              else full_name += "mgr";
              full_name += CmtSystem::file_separator ();
              full_name += name;
            }

          symbol = create (full_name, command_type, use);
          symbol->add_value_to_list (command_type, use,
                                     Tag::get_default (), full_name);
        }
    }
  else
    {
      if (words.size () < 2) return;
      const cmt_string& default_value = words[2];

      if (Cmt::get_debug ())
	{
	  cout << "Symbol::action> name:" << name 
	       << " access:" << Cmt::get_current_access () 
	       << " scope:" << use->get_current_scope () << endl;
	}

      if (Cmt::get_current_access () == UserMode)
	{
	  if (name == "constituents") return;
	  if (name == "constituentscclean") return;

          if (use->get_current_scope () == ScopePrivate) return;
	}

      symbol = create (name, command_type, use);

      /*
        Parse the default value.
      */
      
      symbol->add_value_to_list (command_type, use,
                                 Tag::get_default (), default_value);
      
      /*
        Then parse all specific values
        
        <tag> <value>
        ...
      */
      
      number = 3;
      while (number < (words.size () - 1))
        {
          cmt_string tag_name = words[number];
          const cmt_string& value = words[number + 1];

	  expand (tag_name);

	  if (Cmt::get_debug ())
	    {
	      cout << "Symbol::action> tag_name=" << tag_name << endl;
	    }

	  tag = Tag::find (tag_name);
	  if (tag == 0)
	    {
	      tag = Tag::add (tag_name, PriorityUserTag, "use", use);
	    }

          symbol->add_value_to_list (command_type, use, tag, value);
          
          number += 2;
        }

      if (name == "CMTPATH")
        {
          Cmt::configure_cmt_path (use);
        }
      else if (name == "CMTSITE")
        {
          Cmt::configure_site_tag (use);
        }
      else if (name == "CMTCONFIG")
        {
            //cerr << "redefining CMTCONFIG" << endl;
          Cmt::configure_tags (use);
        }
      else if (name == "CMTHOME")
        {
          Cmt::configure_home (use);
        }
      else if (name == "CMTUSERCONTEXT")
        {
          Cmt::configure_user_context (use);
        }
      else if (name.find ("_native_version") != cmt_string::npos)
        {
          cmt_string n = use->get_package_name ();
          n += "_native_version";

          if (name == n)
            {
              use->set_native_version (true);
            }
        }
    }
}

//-------------------------------------------------------------
int Symbol::is_selected (const cmt_string& name)
{
  Symbol* symbol;
  int number;
  int value_number;

  symbol = find (name);
  if (symbol == 0) return (0);

  if (symbol->value_lists.size () == 0) return (0);

  for (number = 0;
       number < symbol->value_lists.size ();
       number++)
    {
      const SymbolValueList& value_list = symbol->value_lists[number];

      if (value_list.discarded) continue;

      if ((value_list.command_type == CommandMacro) ||
          (value_list.command_type == CommandSet) ||
          (value_list.command_type == CommandSetAppend) ||
          (value_list.command_type == CommandSetPrepend) ||
          (value_list.command_type == CommandSetRemove) ||
          (value_list.command_type == CommandSetRemoveRegexp) ||
          (value_list.command_type == CommandAlias) ||
          (value_list.command_type == CommandAction))
        {
          for (value_number = 0;
               value_number < value_list.values.size ();
               value_number++)
            {
              Tag* tag;

              SymbolValue& value = value_list.values[value_number];

              tag = value.tag;
              if ((tag == 0) ||
                  (tag == Tag::get_default ()) ||
                  (tag->is_selected () != 0))
                {
                  return (1);
                }
            }
        }
    }

  return (0);
}

//-------------------------------------------------------------
Symbol::Symbol ()
{
  name = "";
}

//-------------------------------------------------------------
Symbol::~Symbol ()
{
}

/**
 *  Characterizes if a value is provided as the reference to itself. 
 */
bool Symbol::value_is_reflexive (const cmt_string& text) const
{
  bool result = false;
  int text_length = text.size ();

  if (text_length == (name.size () + 3))
    {
      static cmt_string temp;
              
      if (text[0] == '$')
        {
          if (text[1] == '(')
            {
              temp = "$(";
              temp += name;
              temp += ")";
              
              if (text == temp)
                {
                  result = true;
                }
            }
          else if (text[1] == '{')
            {
              temp = "${";
              temp += name;
              temp += "}";
              
              if (text == temp)
                {
                  result = true;
                }
            }
        }
    }
  else if (text_length == (name.size () + 2))
    {
      static cmt_string temp;

      temp = "%";
      temp += name;
      temp += "%";

      if (text == temp)
        {
          result = true;
        }
    }

  return (result);
}

//-------------------------------------------------------------
void Symbol::add_value_to_list (CommandType command_type,
                                Use* use,
				Tag* tag,
                                const cmt_string& text)
{
  SymbolValueList* value_list = 0;
  bool is_reflexive = false;

    //
    // First pickup the most recent value_list
    //
  if (value_lists.size () > 0) value_list = &(value_lists.back ());

    //
    //  Create a new value list is we switch to another use or
    //  if we switch to a new command_type (eg. switching from
    //  macro to macro_append).
    //
  if ((value_list == 0) ||
      (use != value_list->use) ||
      (command_type != value_list->command_type) ||
      (tag == Tag::get_default ()))
    {
      value_list = &(value_lists.add ());
      value_list->use = use;
      value_list->command_type = command_type;
      value_list->values.clear ();
      value_list->discarded = false;
      value_list->is_reflexive = false;
    }

/*
  else
    {
        value_list = &(value_lists[value_lists.size () - 1]);
    }
*/

  is_reflexive = value_list->is_reflexive;

    //
    //  If the command_type is command_macro or command_set, 
    // this is considered as a full re-set of this symbol 
    //   In this case, we have to discard all previous values
    //
    //  However, we'd like to exclude from this logic the cases where
    //  the value is **exactly* 
    //
    //     $(<symbol>)
    //     ${<symbol>}
    //     %<symbol>%
    //
    //   which would then mean that we do not reset the value but rather
    //  override it.
    //

    //
    // Inside this value_list, we add this new tag-value pair.
    //

  if ((command_type == CommandMacro) ||
      (command_type == CommandSet) ||
      (command_type == CommandPath) ||
      (command_type == CommandAction))
    {
        //
        // Check whether we have to hide previous settings by this new definition
        //  (of course this is only useful if there WERE previous settings)
        //
      if ((value_lists.size () >= 1) && (!is_reflexive))
        {
          if (value_is_reflexive (text))
            {
              value_list->is_reflexive = true;
              is_reflexive = true;
            }
          else
            {
	      //cout << "...discarding old values for symbol " << name << " text=[" << text << "]" << endl;
                  
              for (int i = 0; i < (value_lists.size () - 1); i++)
                {
                  SymbolValueList& vl = value_lists[i];
                  
                  if ((vl.use != 0) &&
                      (vl.use->discarded))
		    {
                      //vl.discarded = true;
		    }
                }
            }
        }
    }

  SymbolValue& value = value_list->values.add ();

  value.tag = tag;
  value.text = text;
  value.selected = 0;
}

/**
   Compute the current value of all environment variables and set them to the
   shell
 */
void Symbol::all_set ()
{
  //cerr << "all_set" << endl;

  if (Cmt::get_debug ())
    {
      cout << "Symbol::all_set> done" << Cmt::get_all_sets_done () << endl;
    }

  if (Cmt::get_all_sets_done ()) return;

  Cmt::set_all_sets_done ();

  static SymbolVector& Symbols = symbols ();
  Use::UsePtrVector& Uses = Use::get_ordered_uses ();

  static CmtSystem::cmt_string_vector envs;

  envs.clear ();

  int number;

  if (Symbols.size () == 0) 
    {
        //running = false;
      return;
    }

  cmt_string value;

  for (number = 0; number < Symbol::symbol_number (); number++)
    {
      Symbol& symbol = Symbol::symbol (number);

      if (symbol.type != SymbolSet) continue;

      value = symbol.build_macro_value ();
      if (value != "")
	{
	  cmt_string& temp = envs.add ();

	  temp = symbol.name;
	  temp += '=';
	  temp += value;
	}
    }

  cmt_string cmtconfig = CmtSystem::get_cmt_config ();

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

      for (number = 0; number < Uses.size (); number++)
        {
          Use& use = *(Uses[number]);

          if (use.discarded) continue;

	  if (use.get_package_name () == "cmt_standalone") continue;

	  if (use.get_strategy ("SetupConfig"))
	    {
	      cmt_string& temp = envs.add ();

	      temp = use.prefix;
	      temp += "CONFIG=";
	      temp += cmtconfig;
	    }

	  if (use.get_strategy ("SetupRoot"))
	    {
	      cmt_string& temp = envs.add ();

	      temp = use.prefix;
	      temp += "ROOT=";
	      temp += use.get_full_path ();
	    }
        }
    }

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

    if (use.get_package_name () != "cmt_standalone")
      {
	if (use.get_strategy ("SetupConfig"))
	  {
	    cmt_string& temp = envs.add ();
	    
	    temp = use.prefix;
	    temp += "CONFIG=";
	    temp += cmtconfig;
	  }
	
	if (use.get_strategy ("SetupRoot"))
	  {
	    cmt_string& temp = envs.add ();
	    
	    temp = use.prefix;
	    temp += "ROOT=";
	    temp += use.get_full_path ();
	  }
      }
  }

  for (number = 0; number < Symbol::symbol_number (); number++)
    {
      Symbol& symbol = Symbol::symbol (number);

      if ((symbol.type != SymbolPath)) continue;

      value = symbol.build_macro_value ();
      if (value != "")
	{
	  Symbol::expand (value);
	  filter_path_value (symbol.name, value);

	  cmt_string& temp = envs.add ();

	  temp = symbol.name;
	  temp += '=';
	  temp += value;
	}
    }

  for (number = 0; number < envs.size (); number++)
    {
      cmt_string& env = envs[number];
      Symbol::expand (env);

#ifdef WIN32
      env.replace_all ("/", "\\");
#endif

      if (Cmt::get_debug ())
        {
          cout << "Symbol::all_set> " << env << endl;
        }
      CmtSystem::putenv (env);
    }

    //running = false;
}

//-------------------------------------------------------------
void Symbol::all_print (PrintMode mode)
{
  static SymbolVector& Symbols = symbols ();

  int number;

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

  for (number = 0; number < Symbol::symbol_number (); number++)
    {
      Symbol& symbol = Symbol::symbol (number);

      if ((symbol.type == SymbolSet) ||
          (symbol.type == SymbolAlias) ||
          (symbol.type == SymbolSetupScript))
        {
          if (symbol.print (mode))
            {
              if (mode == Bat)
                {
                  cout << endl;
                }
              else
                {
                  //cout << "; ";
                  cout << endl;
                }
            }
        }
    }

  for (number = 0; number < Symbol::symbol_number (); number++)
    {
      Symbol& symbol = Symbol::symbol (number);

      if ((symbol.type != SymbolPath)) continue;

      if (symbol.print (mode))
        {
          if (mode == Bat)
            {
              cout << endl;
            }
          else
            {
              //cout << "; ";
              cout << endl;
            }
        }
    }
}

//-------------------------------------------------------------
void Symbol::all_print_clean (PrintMode mode)
{
  static SymbolVector& Symbols = symbols ();

  int number;

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

  for (number = Symbols.size () - 1; number >= 0; number--)
    {
      Symbol& symbol = Symbols[number];

      if ((symbol.type == SymbolSet) ||
          (symbol.type == SymbolAlias) ||
          (symbol.type == SymbolCleanupScript))
        {
          if (symbol.print_clean (mode))
            {
              cout << endl;
            }
        }
    }

  for (number = Symbols.size () - 1; number >= 0; number--)
    {
      Symbol& symbol = Symbols[number];

      if ((symbol.type != SymbolPath)) continue;

      if (symbol.print_clean (mode))
        {
          cout << endl;
        }
    }
}

//-------------------------------------------------------------
int Symbol::print_clean (PrintMode mode)
{
  int result = 0;
  static cmt_string temp;

  if (name == "CMTCONFIG") return (0);

  switch (type)
    {
    case SymbolSet :
      switch (mode)
        {
        case Csh :
          cout << "unsetenv " << name;
          result = 1;
          break;
        case Sh :
          cout << "unset " << name;
          result = 1;
          break;
        case Bat :
          cout << "set " << name << "=";
          result = 1;
          break;
        }
      break;
    case SymbolAlias :
      switch (mode)
        {
          case Csh :
            cout << "unalias " << name;
            result = 1;
            break;
          case Sh :
            cout << "unset " << name;
            result = 1;
            break;
        }
      break;
    case SymbolPath :
      temp = clean_macro_value ();
      switch (mode)
        {
        case Csh :
	  if (temp == "")
	    {
	      cout << "unsetenv " << name;
	    }
	  else
	    {
	      cout << "setenv " << name << " " << temp;
	    }
          result = 1;
          break;
        case Sh :
          cout << name << "=" << temp << "; export " << name;
          result = 1;
          break;
        case Bat :
          cout << "set " << name << "=" << temp;
          result = 1;
          break;
        }
      break;
    case SymbolCleanupScript :
      switch (mode)
        {
        case Csh :
          cout << "if ( -f " << name << ".csh ) then" << endl;
          cout << "  source " << name << ".csh" << endl;
          cout << "endif" << endl;
          result = 1;
          break;
        case Sh :
          cout << "if test -f " << name << ".sh; then" << endl;
          cout << "  . " << name << ".sh" << endl;
          cout << "fi" << endl;
          result = 1;
          break;
        case Bat :
          cout << "call " << name;
          result = 1;
          break;
        }
      break;
    }

  return (result);
}

//-------------------------------------------------------------
int Symbol::print (PrintMode mode)
{
  int result = 0;
  cmt_string temp;

  temp = build_macro_value ();

  bool empty = (temp.size () == 0) ? true : false;

  if (type == SymbolPath)
    {
      expand (temp);
      Symbol::filter_path_value (name, temp);
    }

  switch (type)
    {
      case SymbolSet :
      case SymbolPath :
        switch (mode)
          {
            case Csh :
              if (empty) cout << "unsetenv " << name;
              else cout << "setenv " << name << " \"" << temp << "\"";

              result = 1;
              break;
            case Sh :
              if (empty) cout << "unset " << name;
              else cout << name << "=\"" << temp << "\"; export " << name;

              result = 1;
              break;
            case Bat :
              temp.replace_all ("/", "\\");
              cout << "set " << name << "=" << temp;
              result = 1;
              break;
          }
        break;
      case SymbolAlias :
        switch (mode)
          {
            case Csh :
              cout << "alias " << name <<
                  " \"" << temp << "\"";
              result = 1;
              break;
            case Sh :
              cout << "alias " << name <<
                  "=\"" << temp << "\"";
              result = 1;
              break;
            case Bat :
              cout << "set " << name <<
                  "=" << temp;
              result = 1;
              break;
          }
        break;
      default :
        break;
    }

  if (temp != "")
    {
      switch (type)
        {
          case SymbolSetupScript :
            switch (mode)
              {
                case Csh :
                  cout << "if ( -f " << name << ".csh ) then" << endl;
                  cout << "  source " << name << ".csh" << endl;
                  cout << "endif" << endl;
                  result = 1;
                  break;
                case Sh :
                  cout << "if test -f " << name << ".sh; then" << endl;
                  cout << "  . " << name << ".sh" << endl;
                  cout << "fi" << endl;
                  result = 1;
                  break;
                case Bat :
                  cout << "call " << name;
                  result = 1;
                  break;
              }
            break;
          default:
            break;
        }
    }

  return (result);
}

//-------------------------------------------------------------
cmt_string Symbol::build_macro_value (bool display_it) const
{
  cmt_string temp;

  if (display_it)
    {
      temp = builder->build_and_display (*this);
    }
  else
    {
      temp = builder->build (*this);
    }

  return (temp);
}

//-------------------------------------------------------------
cmt_string Symbol::clean_macro_value () const
{
  cmt_string temp;

  temp = builder->clean (*this);

  return (temp);
}

/**

   Attempt to substitute into the symbol value all occurrences of

      ${xxx}
      $(xxx)
      `xxx`
      %xxx%    [on Windows only]

      by the appropriate value:

        for `xxx` : 

            xxx is considered as a shell command. Value is the result of its execution

        for other patterns:

            if xxx is a symbol name, its value is substituted to the pattern
            otherwise, xxx is tried as an environment variable


     ===> In all cases, the pattern is filtered away.

  */
cmt_string Symbol::resolve_macro_value (const cmt_string& tag_name)
{
  cmt_string temp = builder->build (*this, tag_name);

  resolve_value (temp);

  return (temp);
}

//-------------------------------------------------------------
void Symbol::show_macro (PrintMode mode)
{
  if (Cmt::get_debug ())
    {
      cout << "Symbol::show_macro> " << name << endl;
    }

  ActionType action = Cmt::get_action ();

  cmt_string value = build_macro_value (true);

  if ((!Cmt::get_quiet ()) &&
      (action != action_build_tag_makefile) &&
      (action != action_show_macros) &&
      (action != action_show_actions) &&
      (action != action_show_sets))
    {
      cout << "#" << endl;
      cout << "# Selection : " << endl;
    }

  if (value.size () > 0)
    {
      if ((action == action_show_macro) ||
          (action == action_show_macros) ||
          (action == action_show_sets) ||
          (action == action_show_set) ||
          (action == action_show_actions) ||
          (action == action_show_action) ||
          (action == action_build_tag_makefile) ||
          (action == action_load) ||
          (!Cmt::get_quiet ()))
        {
          if (mode == Make)
            {
              cout << name << "=";
            }
          else
            {
              cout << name << "='";
            }
        }

      if ((action == action_show_macro_value) ||
          (action == action_show_set_value) ||
          (action == action_show_action_value))
        {
          expand (value);
        }
      else if (action == action_build_tag_makefile)
        {
            /* 
               Unfortunately, the %xxx% pattern has to be kept on Unix. Therefore
               we cannot force all patterns to become $(xxx)

               This was useful on Windows so as to only keep $(xxx)
             */
#ifdef WIN32
	  suppress_OS_delimiters (value);
#endif
        }

      cout << value;

      if ((action == action_show_macro) ||
          (action == action_show_macros) ||
          (action == action_show_sets) ||
          (action == action_show_set) ||
          (action == action_show_actions) ||
          (action == action_show_action) ||
          (action == action_build_tag_makefile) ||
          (action == action_load) ||
          (!Cmt::get_quiet ()))
        {
          if (mode != Make)
            {
              cout << "'";
            }
#ifdef WIN32
	  else
	    {
              cout << " ";
	    }
#endif
        }

      cout << endl;
    }
}

//-------------------------------------------------------------
void Symbol::clear_all ()
{
  static SymbolVector& Symbols = symbols ();
  static SymbolMap& SymbolMap = symbol_map ();

  SymbolMap.clear ();
  Symbols.clear ();
}

//-------------------------------------------------------------
void Symbol::expand (cmt_string& text)
{
  static cmt_regexp reg ("[$%`]");

  if (!reg.match (text)) return;

  resolve_value (text);
}

//-------------------------------------------------------------
ValueBuilder::ValueBuilder ()
{
  m_display_it = false;
}

//-------------------------------------------------------------
const cmt_string ValueBuilder::build_and_display (const Symbol& symbol)
{
  cmt_string temp;

  m_display_it = true;
  temp = build (symbol);
  m_display_it = false;

  return (temp);
}

//-------------------------------------------------------------
const cmt_string SetBuilder::build (const Symbol& symbol,
                                    const cmt_string& /*tag_name*/)
{
    // Control of recursivity
  static int level = 0;

  bool show_it = false;

  cmt_string temp;
  cmt_string previous_temp;
  cmt_string new_value;
  static const cmt_string empty;

  ActionType action = Cmt::get_action ();

  if (action == action_show_set)
    {
      if (symbol.name == Cmt::get_current_target ())
        {
             // Should not display on recursive calls
          if (level == 0) show_it = m_display_it;
        }
    }

  level++;

  temp = "";

  bool first_definition = true;
  bool defined = false;

  for (int i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      const int selected = value_list.select_first ();

      if (selected < 0) continue;

      SymbolValue& value = value_list.values[selected];

      if (show_it)
        {
          value_list.show (symbol, value, first_definition);
        }
      
      if (value_list.discarded) continue;

        //
        // One should accumulate values if it refers to
        // itself.
        //
      
      new_value = value.text;
      
      resolve_value_for_macros (new_value);
      
      switch (value_list.command_type)
        {
          case CommandSet :

            if (!value_list.is_reflexive || 
                !symbol.value_is_reflexive (value.text))
              {
                resolve_value (new_value, symbol.name, temp);
                temp = new_value;
              }
            else if (temp == "")
              {
                temp = CmtSystem::getenv (symbol.name);
              }

            if (!defined)
              {
                defined = true;
              }
            else
              {
                if ((!Cmt::get_quiet ()) &&
                    ((action == action_show_macro) ||
		     (action == action_show_set) ||
		     (action == action_show_action) ||
		     (action == action_show_macros) ||
		     (action == action_show_sets) ||
		     (action == action_show_actions)))
                  {
                    cerr << "#CMT> Warning: Symbol " << symbol.name << " overridden";

                    if (value_list.use != 0)
                      {
                        cerr << " in package " << value_list.use->get_package_name ();
                      }

                    cerr << endl;
                  }
              }

            break;
          case CommandSetAppend :
            
            if (new_value != "")
              {
                temp += new_value;
              }
            
            break;
          case CommandSetPrepend :
            
            if (new_value != "")
              {
                previous_temp = temp;
                temp = new_value;
                temp += previous_temp;
              }
            
            break;
          case CommandSetRemove :
            
            if (new_value != "")
              {
                temp.replace_all (new_value, empty);
              }
            
            break;
          case CommandSetRemoveRegexp :
            
            if (new_value != "")
              {
		cmt_regexp e (new_value);
		cmt_regexp::iterator it;

		for (;;)
		  {
		    it = e.begin (temp);
		    if (it == e.end ()) break;

		    temp.erase (it._pos, it._length);
		  }
              }
            
            break;
          case CommandAlias :
            
            resolve_value (new_value, symbol.name, temp);
            temp = new_value;
            
            break;
        }
    }

  level--;

  return (temp);
}

static bool find_path_entry (const cmt_string& paths, const cmt_string& value)
{
  static const cmt_string path_separator = CmtSystem::path_separator ();

  cmt_string here = CmtSystem::pwd ();
  cmt_string rvalue = value;

  if (CmtSystem::cd (value))
    {
      rvalue = CmtSystem::pwd ();
    }
  else
    {
      CmtSystem::compress_path (rvalue);
    }

  CmtSystem::cmt_string_vector items;
  CmtSystem::split (paths, path_separator, items);

  bool found = false;

  for (int i = 0; i < items.size (); i++)
    {
      const cmt_string& item = items[i];
      cmt_string ritem = item;
      if (CmtSystem::cd (item))
	{
	  ritem = CmtSystem::pwd ();
	}
      else
	{
	  CmtSystem::compress_path (ritem);
	}

      if (ritem == rvalue)
	{
	  found = true;
	  break;
	}
    }

  CmtSystem::cd (here);
  return (found);
}

//-------------------------------------------------------------
const cmt_string PathBuilder::build (const Symbol& symbol,
                                     const cmt_string& /*tag_name*/)
{
    // Control of recursivity
  static int level = 0;

  bool show_it = false;

  cmt_string temp;
  cmt_string previous_temp;
  cmt_string new_value;
  static const cmt_string empty;

  static cmt_string path_separator = CmtSystem::path_separator ();

  ActionType action = Cmt::get_action ();

  if (action == action_show_set)
    {
      if (symbol.name == Cmt::get_current_target ())
        {
            // Should not display on recursive calls
          if (level == 0) show_it = m_display_it;
        }
    }

  level++;

  temp = CmtSystem::getenv (symbol.name);

  bool first_definition = true;
  bool defined = false;

  for (int i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      const int selected = value_list.select_first ();
      
      if (selected < 0) continue;
 
      SymbolValue& value = value_list.values[selected];
          
      if (show_it)
        {
          value_list.show (symbol, value, first_definition);
        }
          
      if (value_list.discarded) continue;

      new_value = value.text;
          
          //resolve_value (new_value);
      resolve_value_for_macros (new_value);
          
      switch (value_list.command_type)
        {
          case CommandPath :
            
            if (!value_list.is_reflexive || 
                !symbol.value_is_reflexive (value.text))
              {
                resolve_value (new_value, symbol.name, temp);
                temp = new_value;

                if (!defined)
                  {
                    defined = true;
                  }
                else
                  {
		    if ((!Cmt::get_quiet ()) &&
			((action == action_show_macro) ||
			 (action == action_show_set) ||
			 (action == action_show_action) ||
			 (action == action_show_macros) ||
			 (action == action_show_sets) ||
			 (action == action_show_actions)))
                      {
                        cerr << "#CMT> Warning: Symbol " << symbol.name << " overridden";
                        
                        if (value_list.use != 0)
                          {
                            cerr << " in package " << value_list.use->get_package_name ();
                          }
                        
                        cerr << endl;
                      }
                  }
              }

            break;
          case CommandPathAppend :
              
            if (new_value != "")
              {
                if (!find_path_entry (temp, new_value))
                  {
                    if (temp != "") temp += path_separator;
                      
                    temp += new_value;
                  }
              }
                  
            break;
          case CommandPathPrepend :
              
            if (new_value != "")
              {
                if (!find_path_entry (temp, new_value))
                  {
                    previous_temp = temp;
                    temp = new_value;
                    if (previous_temp != "") temp += path_separator;
                    temp += previous_temp;
                  }
              }
                  
            break;
          case CommandPathRemove :
              
            if (new_value != "")
              {
                CmtSystem::cmt_string_vector paths;
                  
                CmtSystem::split (temp, path_separator, paths);
                  
                for (int j = 0; j < paths.size (); ++j)
                  {
                    cmt_string& s = paths[j];
                      
                    if (s.find (new_value) != cmt_string::npos)
                      {
                        s = "";
                      }
                  }

                Cmt::vector_to_string (paths, path_separator, temp);
              }
              
            break;
          case CommandPathRemoveRegexp :

            if (new_value != "")
              {
		cmt_regexp e (new_value);

                CmtSystem::cmt_string_vector paths;
                  
                CmtSystem::split (temp, path_separator, paths);
                  
                for (int j = 0; j < paths.size (); ++j)
                  {
                    cmt_string& s = paths[j];

		    if (CmtSystem::getenv ("TESTPRR") != "")
		      {
			cout << "PRR> s=[" << s << "]";
		      }

                    if (e.match (s))
                      {
                        s = "";

			if (CmtSystem::getenv ("TESTPRR") != "")
			  {
			    cout << " match ";
			  }
                      }
		    else
                      {
			if (CmtSystem::getenv ("TESTPRR") != "")
			  {
			    cout << " no match ";
			  }
                      }

		    if (CmtSystem::getenv ("TESTPRR") != "")
		      {
			cout << endl;
		      }
                  }

                Cmt::vector_to_string (paths, path_separator, temp);
              }
              
            break;
        }

    }

  level--;

  for (;;)
    {
      int sz = temp.size ();

      if (sz == 0) break;

      if ((temp[0] == ';') || (temp[0] == ':'))
	{
	  temp.erase (0, 1);
	}
      else if ((temp[sz-1] == ';') || (temp[sz-1] == ':'))
	{
	  temp.erase (sz-1, 1);
	}
      else
	{
	  break;
	}
    }

  temp.replace_all ("::", ":");
  temp.replace_all (";;", ";");
  
  return (temp);
}

//-------------------------------------------------------------
const cmt_string PathBuilder::clean (const Symbol& symbol,
                                     const cmt_string& /*tag_name*/)
{
    // Control of recursivity
  static int level = 0;

  cmt_string temp;
  cmt_string new_value;
  static const cmt_string empty;

  static cmt_string path_separator = CmtSystem::path_separator ();

  temp = CmtSystem::getenv (symbol.name);

    //cerr << "#####1 temp=" << temp << endl;

  for (int i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if (value_list.discarded) continue;

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      const int selected = value_list.select_first ();
      
      if (selected < 0) continue;
 
      SymbolValue& value = value_list.values[selected];
          
      new_value = value.text;
          
          //resolve_value (new_value);

        //cerr << "#####1 new_value=" << new_value << endl;

      resolve_value_for_macros (new_value);
      resolve_value (new_value);
          
        //cerr << "#####2 new_value=" << new_value << endl;

      switch (value_list.command_type)
        {
          case CommandPath :
            
            temp = "";
            
            break;
          case CommandPathAppend :
          case CommandPathPrepend :
          case CommandPathRemove :

            if (new_value != "")
              {
                CmtSystem::cmt_string_vector paths;
                  
                CmtSystem::split (temp, path_separator, paths);
                  
                for (int j = 0; j < paths.size (); ++j)
                  {
                    cmt_string& s = paths[j];
                      
                    if (s.find (new_value) != cmt_string::npos)
                      {
                        s = "";
                      }

                    if (j > 0)
                      {
                        cmt_string& s2 = paths[j-1];
                        if (s2 == s)
                          {
                            s2 = "";
                          }
                      }
                  }

                Cmt::vector_to_string (paths, path_separator, temp);
                temp.replace_all ("::", ":");
                temp.replace_all (";;", ";");
              }
              
            break;
          case CommandPathRemoveRegexp :

            if (new_value != "")
              {
		cmt_regexp e (new_value);

                CmtSystem::cmt_string_vector paths;
                  
                CmtSystem::split (temp, path_separator, paths);
                  
                for (int j = 0; j < paths.size (); ++j)
                  {
                    cmt_string& s = paths[j];
                      
                    if (e.match (s))
                      {
                        s = "";
                      }

                    if (j > 0)
                      {
                        cmt_string& s2 = paths[j-1];
                        if (s2 == s)
                          {
                            s2 = "";
                          }
                      }
                  }

                Cmt::vector_to_string (paths, path_separator, temp);
                temp.replace_all ("::", ":");
                temp.replace_all (";;", ";");
              }
              
            break;
        }
    }

    //cerr << "#####2 temp=" << temp << endl;
  
  return (temp);
}

//-------------------------------------------------------------
const cmt_string MacroBuilder::build (const Symbol& symbol,
                                      const cmt_string& tag_name)
{
    // Control of recursivity
  static int level = 0;

  cmt_string temp;
  cmt_string previous_temp;
  static const cmt_string empty;
  bool show_it = false;

  ActionType action = Cmt::get_action ();

  if (action == action_show_macro)
    {
      if (symbol.name == Cmt::get_current_target ())
        {
             // Should not display on recursive calls
          if (level == 0) show_it = m_display_it;
        }
    }

  level++;

  temp = "";

  int i;

  bool first_definition = true;
  bool defined = false;

  for (i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      if (value_list.command_type != CommandMacroPrepend) continue;

      const int selected = value_list.select_first (tag_name);

      if (selected < 0) continue;

      SymbolValue& value = value_list.values[selected];

      if (show_it)
        {
          value_list.show (symbol, value, first_definition);
        }

      if (value_list.discarded) continue;

      previous_temp = temp;
      temp = value.text;
      temp += previous_temp;
    }

  previous_temp = temp;
  temp = "";

  first_definition = true;

  for (i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      if (value_list.command_type != CommandMacro) continue;

      const int selected = value_list.select_first (tag_name);

      if (selected < 0) continue;

      SymbolValue& value = value_list.values[selected];

      if (show_it)
        {
          value_list.show (symbol, value, first_definition);
        }

      // WARNING:
      // Commented just for a test : should be uncommented after the test
      if (value_list.discarded) continue;
      
      if (!value_list.is_reflexive || 
          !symbol.value_is_reflexive (value.text))
        {
          temp = value.text;

          if (!defined)
            {
              defined = true;
            }
          else
            {
	      /*
	      if ((!Cmt::get_quiet ()) &&
		  ((action == action_show_macro) ||
		   (action == action_show_set) ||
		   (action == action_show_action) ||
		   (action == action_show_macros) ||
		   (action == action_show_sets) ||
		   (action == action_show_actions)))
                {
                  cerr << "#CMT> Warning: Symbol " << symbol.name << " overridden";
                  
                  if (value_list.use != 0)
                    {
                      cerr << " in package " << value_list.use->get_package_name ();
                    }

                  cerr << endl;
                }
	      */
            }
        }
    }

  previous_temp += temp;
  temp = previous_temp;

  for (i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      if (value_list.command_type != CommandMacroAppend) continue;

      const int selected = value_list.select_first (tag_name);

      if (selected < 0) continue;

      SymbolValue& value = value_list.values[selected];

      if (show_it)
        {
          value_list.show (symbol, value, first_definition);
        }

      if (value_list.discarded) continue;

      temp += value.text;
    }

  for (i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      if ((value_list.command_type != CommandMacroRemove) &&
          (value_list.command_type != CommandMacroRemoveRegexp) &&
          (value_list.command_type != CommandMacroRemoveAll) &&
          (value_list.command_type != CommandMacroRemoveAllRegexp)) continue;

      const int selected = value_list.select_first (tag_name);

      if (selected < 0) continue;

      SymbolValue& value = value_list.values[selected];

      if (show_it)
        {
          value_list.show (symbol, value, first_definition);
        }

      if (value_list.discarded) continue;

      switch (value_list.command_type)
        {
          case CommandMacroRemove :
            temp.replace (value.text, empty);
            break;
          case CommandMacroRemoveRegexp :
            if (value.text != "")
              {
		cmt_regexp e (value.text);
		cmt_regexp::iterator it;

		it = e.begin (temp);
		if (it != e.end ())
		  {
		    temp.erase (it._pos, it._length);
		  }
              }
            break;
          case CommandMacroRemoveAll :
            temp.replace_all (value.text, empty);
            break;
          case CommandMacroRemoveAllRegexp :
            if (value.text != "")
              {
		cmt_regexp e (value.text);
		cmt_regexp::iterator it;

		for (;;)
		  {
		    it = e.begin (temp);
		    if (it != e.end ())
		      {
			temp.erase (it._pos, it._length);
		      }
		    else
		      {
			break;
		      }
		  }
              }
            break;
        }
    }

  level--;

  return (temp);
}

//-------------------------------------------------------------
const cmt_string ScriptBuilder::build (const Symbol& symbol,
                                       const cmt_string& tag_name)
{
    // Control of recursivity
  static int level = 0;

  static const cmt_string empty = "";

  if (symbol.value_lists.size () > 0)
    {
      const SymbolValueList& value_list = symbol.value_lists[0];

      if (value_list.discarded) return (empty);

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) return (empty);
    }

  return (symbol.name);
}

//-------------------------------------------------------------
const cmt_string ActionBuilder::build (const Symbol& symbol,
				       const cmt_string& tag_name)
{
    // Control of recursivity
  static int level = 0;

  cmt_string temp;
  cmt_string previous_temp;
  static const cmt_string empty;
  bool show_it = false;

  ActionType action = Cmt::get_action ();

  if (action == action_show_action)
    {
      if (symbol.name == Cmt::get_current_target ())
        {
             // Should not display on recursive calls
          if (level == 0) show_it = m_display_it;
        }
    }

  level++;

  int i;

  bool first_definition = true;
  bool defined = false;

  temp = "";

  for (i = 0; i < symbol.value_lists.size (); i++)
    {
      const SymbolValueList& value_list = symbol.value_lists[i];

      if ((value_list.use != 0) &&
          (value_list.use->discarded)) continue;

      if (value_list.command_type != CommandAction) continue;

      const int selected = value_list.select_first (tag_name);

      if (selected < 0) continue;

      SymbolValue& value = value_list.values[selected];

      if (show_it)
        {
          value_list.show (symbol, value, first_definition);
        }

      // WARNING:
      // Commented just for a test : should be uncommented after the test
      if (value_list.discarded) continue;
      
      if (!value_list.is_reflexive || 
          !symbol.value_is_reflexive (value.text))
        {
          temp = value.text;

          if (!defined)
            {
              defined = true;
            }
          else
            {
	      if ((!Cmt::get_quiet ()) &&
		  ((action == action_show_macro) ||
		   (action == action_show_set) ||
		   (action == action_show_action) ||
		   (action == action_show_macros) ||
		   (action == action_show_sets) ||
		   (action == action_show_actions)))
                {
                  cerr << "#CMT> Warning: Symbol " << symbol.name << " overridden";
                  
                  if (value_list.use != 0)
                    {
                      cerr << " in package " << value_list.use->get_package_name ();
                    }

                  cerr << endl;
                }
            }
        }
    }

  level--;

  return (temp);
}

//-------------------------------------------------------------
int SymbolValueList::select_first (const cmt_string& tag_name) const
{
  int priority = 0;
  int value_number;
  int selected = -1;

  Tag* the_tag = 0;

  if (tag_name != "") the_tag = Tag::find (tag_name);

  for (value_number = 0;
       value_number < values.size ();
       value_number++)
    {
      const SymbolValue& value = values[value_number];

      const Tag* tag = value.tag;

      if (the_tag == 0)
        {
          if (!tag->is_selected ()) continue;
        }
      else
        {
          if (tag != the_tag) continue;
          selected = value_number;
        }

      //
      // Only the first value at a given priority is
      // selected (which implies the '>' test instead
      // of '>=')
      //

      if (tag->get_priority () > priority)
        {
          priority = tag->get_priority ();
          selected = value_number;
        }
    }

  return (selected);
}

//-------------------------------------------------------------
int SymbolValueList::select_last () const
{
  int priority = 0;
  int value_number;
  int selected = -1;

  for (value_number = 0;
       value_number < values.size ();
       value_number++)
    {
      SymbolValue& value = values[value_number];

      const Tag* tag = value.tag;

      if (tag->is_selected ())
        {
          //
          // The last value at a given priority is
          // selected (which implies the '>=' test instead
          // of '>')
          //

          if (tag->get_priority () >= priority)
            {
              priority = tag->get_priority ();
              selected = value_number;
            }
        }
    }

  return (selected);
}

//-------------------------------------------------------------
void SymbolValueList::show (const Symbol& symbol, 
                            const SymbolValue& value, 
                            bool& first_definition) const
{
  cmt_string discarded_text;
  cmt_string define_text;
  ActionType action = Cmt::get_action ();

  if (value.text == "") return;

  if (discarded) discarded_text = " (discarded by override)";
  else discarded_text = "";

  if (first_definition) define_text = "defines";
  else define_text = "overrides";

  cout << "# Package ";
  if (use != 0)
    {
      cout << use->get_package_name () << " " << use->version;
    }

  switch (command_type)
    {
      case CommandSet :
        cout << " " << define_text << " set " << symbol.name << " as ";
        first_definition = false;
        break;
      case CommandSetAppend :
        cout << " appends to set " << symbol.name << " : ";
        break;
      case CommandSetPrepend :
        cout << " prepends to set " << symbol.name << " : ";
        break;
      case CommandSetRemove :
        cout << " removes from set " << symbol.name << " : ";
        break;
      case CommandSetRemoveRegexp :
        cout << " removes RE from set " << symbol.name << " : ";
        break;
      case CommandAlias :
        cout << " " << define_text << " alias " << symbol.name << " as ";
        first_definition = false;
        break;
      case CommandPath :
        cout << " " << define_text << " path " << symbol.name << " as ";
        first_definition = false;
        break;
      case CommandPathAppend :
        cout << " appends to path " << symbol.name << " : ";
        break;
      case CommandPathPrepend :
        cout << " prepends to path " << symbol.name << " : ";
        break;
      case CommandPathRemove :
        cout << " removes from path " << symbol.name << " : ";
        break;
      case CommandPathRemoveRegexp :
        cout << " removes RE from path " << symbol.name << " : ";
        break;
      case CommandMacroPrepend :
        cout << " prepends to macro " << symbol.name << " : ";
        break;
      case CommandMacro :
        cout << " " << define_text << " macro " << symbol.name << " as ";
        break;
      case CommandMacroAppend :
        cout << " appends to macro " << symbol.name << " : ";
        break;
      case CommandMacroRemove :
        cout << " remove from macro " << symbol.name << " : ";
        break;
      case CommandMacroRemoveRegexp :
        cout << " remove RE from macro " << symbol.name << " : ";
        break;
      case CommandMacroRemoveAll :
        cout << " remove all from macro " << symbol.name << " : ";
        break;
      case CommandMacroRemoveAllRegexp :
        cout << " remove all RE from macro " << symbol.name << " : ";
        break;
      case CommandAction :
        cout << " " << define_text << " action " << symbol.name << " as ";
        first_definition = false;
        break;
    }

  cout << "'" << value.text << "'";
          
  Tag* selected_tag = value.tag;
          
  if ((selected_tag == 0) ||
      (selected_tag == Tag::get_default ()))
    {
      cout << " for default tag";
    }
  else
    {
      cout << " for tag '" << selected_tag->get_name () << "'";
    }
  
  cout << discarded_text << endl;
}

//-------------------------------------------------------------
bool Symbol::check_tag_used (Tag* tag)
{
  if (tag == 0) return (false);

  static SymbolVector& Symbols = symbols ();

  if (Symbols.size () == 0) 
    {
      return (false);
    }

  for (int number = 0; number < Symbol::symbol_number (); number++)
    {
      Symbol& symbol = Symbol::symbol (number);

      for (int i = 0; i < symbol.value_lists.size (); i++)
	{
	  const SymbolValueList& value_list = symbol.value_lists[i];

	  for (int j = 0; j < value_list.values.size (); j++)
	    {
	      const SymbolValue& value = value_list.values[j];
	      Tag* t = value.tag;

	      if (t != 0)
		{
		  if (t->use_operand (tag)) return (true);
		}
	    }
	}
    }

  return (false);
}

