#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 = "");

private:
  cmt_string temp;
};
//-------------------------------------------------------------

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

private:
  cmt_string temp;
};
//-------------------------------------------------------------

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

private:
  cmt_string temp;
};
//-------------------------------------------------------------

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

private:
  cmt_string temp;
};
//-------------------------------------------------------------

//-------------------------------------------------------------
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 last)
  {
    static symbol_marker result;

    // discards trailing npos
    while ((last >= 0) && (markers[last].ptr == cmt_string::npos)) last--;

    if (last < 0) return (result);

    int i;

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

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

//-------------------------------------------------------------
static SetBuilder Set;
static PathBuilder Path;
static MacroBuilder Macro;
static ScriptBuilder Script;
//-------------------------------------------------------------

//-------------------------------------------------------------
static void resolve_value (cmt_string& text,
                           const cmt_string& macro_name,
                           const cmt_string& value)
{
  static cmt_string pattern;

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

  text.replace_all (pattern, value);

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

  text.replace_all (pattern, value);

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

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

//-------------------------------------------------------------
static void resolve_value (cmt_string& text)
{
  cmt_string pattern;
  cmt_string macro_name;
  char end_pattern;

  int start = 0;

  for (;;)
    {
      //
      // Try and substitute all ${xxx} $(xxx) or %xxx% patterns
      // using symbol values.
      //

      int begin;
      int end;

      symbol_marker markers[3];

      markers[0].set (text.find (start, "$("), ')', 2);
      markers[1].set (text.find (start, "${"), '}', 2);
      markers[2].set (text.find (start, "%"), '%', 1);

      // Find the first of three patterns

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

      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, macro_name);

      Symbol* macro = Symbol::find (macro_name);
      if (macro != 0)
        {
          // 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
        {
          // Macro not found. Look for env. variable
          cmt_string value = CmtSystem::getenv (macro_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;
        }
    }
}

//-------------------------------------------------------------
static void resolve_value_for_macros (cmt_string& text)
{
  cmt_string pattern;
  cmt_string macro_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[3];

      markers[0].set (text.find (start, "$("), ')', 2);
      markers[1].set (text.find (start, "${"), '}', 2);
      markers[2].set (text.find (start, "%"), '%', 1);

      // Find the first of three patterns

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

      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, macro_name);

      Symbol* macro = Symbol::find (macro_name);
      if ((macro != 0) && 
          (macro->command == CommandMacro))
        {
          // 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->command == CommandSet) || (macro->command == CommandPath)))
        {
          // 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 += macro_name;
              new_pattern += CmtSystem::ev_close ();

              text.replace (pattern, new_pattern);
            }

          start = end + 1;
        }
      else
        {
          start = end + 1;
        }
    }
}

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

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

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

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

//-------------------------------------------------------------
Symbol* Symbol::create (const cmt_string& name,
                        CommandType command,
                        ScopeType scope)
{
  static SymbolVector& Symbols = symbols ();

  {
    Symbol* symbol;

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

  Symbol& symbol = Symbols.add ();

  symbol.name    = name;
  symbol.scope   = scope;
  symbol.command = command;

  symbol.value_lists.clear ();

  switch (command)
    {
    case CommandSet:
    case CommandSetAppend:
    case CommandSetPrepend:
    case CommandSetRemove:
      symbol.builder = &Set;
      symbol.command = CommandSet;
      break;
    case CommandPathAppend:
    case CommandPathPrepend:
    case CommandPathRemove:
      symbol.builder = &Path;
      break;
    case CommandAlias:
      symbol.builder = &Set;
      break;
    case CommandMacro:
    case CommandMacroAppend:
    case CommandMacroPrepend:
    case CommandMacroRemove:
    case CommandMacroRemoveAll:
      symbol.builder = &Macro;
      symbol.command = CommandMacro;
      break;
    case CommandSetupScript:
    case CommandCleanupScript:
      symbol.builder = &Script;
      break;
    }

  symbol.selected_value = -1;

  return (&symbol);
}

//-------------------------------------------------------------
Symbol* Symbol::find (const cmt_string& name)
{
  static SymbolVector& Symbols = symbols ();

  int number;
  Symbol* result = 0;

  for (number = 0; number < Symbols.size (); number++)
    {
      Symbol& s = Symbols[number];

      if (s.name == name)
        {
          result = &s;
        }
    }

  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& 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;
  Selector* selector;

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

  if ((command_type == CommandSetupScript) ||
      (command_type == CommandCleanupScript))
    {
      cmt_string full_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 full_name += "mgr";
          full_name += CmtSystem::file_separator ();
          full_name += name;
        }

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

      if ((Cmt::current_access == UserMode) &&
          (Cmt::scope == ScopePrivate)) return;

      symbol = create (name, command_type, Cmt::scope);

      /*
        Parse the default value.
      */
      
      symbol->add_value_to_list (command_type, use,
                                 Cmt::default_selector, default_value);
      
      /*
        Then parse all specific values
        
        <selector> <value>
        ...
      */
      
      number = 3;
      while (number < (words.size () - 1))
        {
          const cmt_string& selector_name = words[number];
          const cmt_string& value = words[number + 1];
          
          selector = Selector::create (selector_name);
          symbol->add_value_to_list (command_type, use, selector, value);
          
          number += 2;
        }
    }
}

//-------------------------------------------------------------
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 == CommandAlias))
        {
          for (value_number = 0;
               value_number < value_list.values.size ();
               value_number++)
            {
              Selector* selector;

              SymbolValue& value = value_list.values[value_number];

              selector = value.selector;
              if ((selector == 0) ||
                  (selector == Cmt::default_selector) ||
                  (selector->selected != 0))
                {
                  return (1);
                }
            }
        }
    }

  return (0);
}

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

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

//-------------------------------------------------------------
void Symbol::add_value_to_list (CommandType command_type,
                                Use* use,
                                Selector* selector,
                                const cmt_string& text)
{
  SymbolValueList* value_list = 0;

    //
    // 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) ||
      (selector == Cmt::default_selector))
    {
      value_list = &(value_lists.add ());
      value_list->use = use;
      value_list->command_type = command_type;
      value_list->values.clear ();
      value_list->discarded = false;
    }

    //
    // Inside this value_list, we add this new selector-value pair.
    //
  SymbolValue& value = value_list->values.add ();

  value.selector = selector;
  value.text = text;
  value.selected = 0;

    //
    //  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
    //

  if ((command_type == CommandMacro) ||
      (command_type == CommandSet))
    {
      for (int i = 0; i < (value_lists.size () - 1); i++)
        {
          SymbolValueList& value_list = value_lists[i];
          
          if ((value_list.use != 0) &&
              (value_list.use->discarded)) continue;

          value_list.discarded = true;
        }
    }
}

//-------------------------------------------------------------
//  Check symbol's scope against current scope and symbol
// type.
//-------------------------------------------------------------
int Symbol::valid (PrintMode mode)
{
  int result = 0;

  if (command == CommandMacro) return (0);
  if (command == CommandMacroPrepend) return (0);
  if (command == CommandMacroAppend) return (0);
  if (command == CommandMacroRemove) return (0);
  if (command == CommandMacroRemoveAll) return (0);
  if (scope != Cmt::scope) return (0);

  return (1);
}

//-------------------------------------------------------------
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++)
    {
      Selector* selected_selector = 0;

      Symbol& symbol = Symbol::symbol (number);

      if ((symbol.command == CommandSet) ||
          (symbol.command == CommandAlias) ||
          (symbol.command == CommandSetupScript))
        {
          if (symbol.print (1, mode))
            {
              if (mode == Bat)
                {
                  cout << endl;
                }
              else
                {
                  //cout << "; ";
                  cout << endl;
                }
            }
        }
    }

  for (number = 0; number < Symbol::symbol_number (); number++)
    {
      Selector* selected_selector = 0;

      Symbol& symbol = Symbol::symbol (number);

      if ((symbol.command != CommandPathAppend) &&
          (symbol.command != CommandPathPrepend) &&
          (symbol.command != CommandPathRemove)) continue;

      if (symbol.print (1, 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.print_clean (0, mode))
        {
          cout << "; ";
        }
    }
}

//-------------------------------------------------------------
int Symbol::print_clean (int tabs, PrintMode mode)
{
  int result = 0;

  switch (command)
    {
    case CommandSet :
      switch (mode)
        {
        case Csh :
          Cmt::print_tabs (tabs);
          cout << "unsetenv " << name;
          result = 1;
          break;
        case Sh :
          Cmt::print_tabs (tabs);
          cout << "unset " << name;
          result = 1;
          break;
        }
      break;
    case CommandAlias :
      Cmt::print_tabs (tabs);
      cout << "unalias " << name;
      result = 1;
      break;
    case CommandSetupScript :
      switch (mode)
        {
        case Csh :
          Cmt::print_tabs (tabs);
          cout << "if ( -f " << name << ".csh ) then" << endl;
          cout << "  source " << name << ".csh" << endl;
          cout << "endif" << endl;
          result = 1;
          break;
        case Sh :
          Cmt::print_tabs (tabs);
          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 (int tabs, PrintMode mode)
{
  int result = 0;
  static cmt_string temp;

  temp = build_macro_value ();
  if (temp == "") return (0);

  switch (command)
    {
    case CommandSet :
    case CommandPathAppend :
    case CommandPathPrepend :
    case CommandPathRemove :
      switch (mode)
        {
        case Csh :
          //Cmt::print_tabs (tabs);
          cout << "setenv " << name <<
            " \"" << temp << "\"";
          result = 1;
          break;
        case Sh :
          //Cmt::print_tabs (tabs);
          cout << name <<
            "=\"" << temp <<
            "\"; export " << name;
          result = 1;
          break;
        case Bat :
          temp.replace_all ("/", "\\");
          cout << "set " << name <<
            "=" << temp;
          result = 1;
          break;
        }
      break;
    case CommandAlias :
      switch (mode)
        {
        case Csh :
          //Cmt::print_tabs (tabs);
          cout << "alias " << name <<
            " \"" << temp << "\"";
          result = 1;
          break;
        case Sh :
          //Cmt::print_tabs (tabs);
          cout << "alias " << name <<
            "=\"" << temp <<
            "\"; export " << name;
          result = 1;
          break;
        case Bat :
          cout << "set " << name <<
            "=" << temp;
          result = 1;
          break;
        }
      break;
    case CommandSetupScript :
      switch (mode)
        {
        case Csh :
          //Cmt::print_tabs (tabs);
          cout << "if ( -f " << name << ".csh ) then" << endl;
          cout << "  source " << name << ".csh" << endl;
          cout << "endif" << endl;
          result = 1;
          break;
        case Sh :
          //Cmt::print_tabs (tabs);
          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);
}

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

  temp = builder->build (*this);

  return (temp);
}

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

  int start = 0;

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

      symbol_marker markers[3];

      markers[0].set (temp.find (start, "$("), ')', 2);
      markers[1].set (temp.find (start, "${"), '}', 2);
      markers[2].set (temp.find (start, "%"), '%', 1);

      // Find the first of three patterns

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

      begin = marker.ptr;

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

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

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

      if (end < begin) break;

      temp.substr (begin, end - begin + 1, pattern);
      temp.substr (begin + marker.intro, end - begin - marker.intro, macro_name);
      Symbol* macro = find (macro_name);
      if (macro != 0)
        {
          // Macro found
          cmt_string value = macro->resolve_macro_value (tag_name);
          temp.replace_all (pattern, value);
          start = begin;
        }
      else
        {
          // Macro not found. Look for env. variable
          cmt_string value = CmtSystem::getenv (macro_name);
          temp.replace_all (pattern, value);
          start = begin;
        }
    }

  return (temp);
}

//-------------------------------------------------------------
void Symbol::show_macro (PrintMode mode)
{
  cmt_string value = build_macro_value ();

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

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

      if ((Cmt::action == action_show_macro_value) ||
          (Cmt::action == action_show_set_value))
        {
          expand (value);
        }

      cout << value;

      if ((Cmt::action == action_show_macro) ||
          (Cmt::action == action_show_macros) ||
          (Cmt::action == action_show_sets) ||
          (Cmt::action == action_show_set) ||
          (Cmt::action == action_build_tag_makefile) ||
          (Cmt::action == action_load) ||
          (!Cmt::quiet))
        {
          if (mode != Make)
            {
              cout << "'";
            }
        }

      cout << endl;
    }
}

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

  Symbols.clear ();
}

//-------------------------------------------------------------
void Symbol::expand (cmt_string& text)
{
  resolve_value (text);
}

//-------------------------------------------------------------
const cmt_string& SetBuilder::build (const Symbol& symbol,
                                     const cmt_string& /*tag_name*/)
{
  int show_it = 0;

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

  if (Cmt::action == action_show_set)
    {
      if (symbol.name == Cmt::current_target)
        {
          show_it = 1;
        }
    }

  temp = "";

  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];

      if (show_it)
        {
          cout << "# Package ";
          if (value_list.use != 0)
            {
              cout << value_list.use->package << " " << value_list.use->version;
            }
        }
      
        //
        // 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 (show_it)
              {
                cout << " defines set " << symbol.name << " as ";
              }
            
            resolve_value (new_value, symbol.name, temp);
            temp = new_value;
            
            break;
          case CommandSetAppend :
            
            if (show_it)
              {
                cout << " appends to set " << symbol.name << " : ";
              }
            
            if (new_value != "")
              {
                temp += new_value;
              }
            
            break;
          case CommandSetPrepend :
            
            if (show_it)
              {
                cout << " prepends to set " << symbol.name << " : ";
              }
            
            if (new_value != "")
              {
                previous_temp = temp;
                temp = new_value;
                temp += previous_temp;
              }
            
            break;
          case CommandPathRemove :
            
            if (show_it)
              {
                cout << " removes from set " << symbol.name << " : ";
              }
            
            if (new_value != "")
              {
                temp.replace_all (new_value, empty);
              }
            
            break;
          case CommandAlias :
            
            if (show_it)
              {
                cout << " defines alias " << symbol.name << " as ";
              }
            
            resolve_value (new_value, symbol.name, temp);
            temp = new_value;
            
            break;
        }
      
      if (show_it)
        {
          cout << "'" << value.text << "'";
          
          Selector* selected_selector = value.selector;
          
          if ((selected_selector == 0) ||
              (selected_selector == Cmt::default_selector))
            {
              cout << " for default selector";
            }
          else
            {
              cout << " for selector '" << selected_selector->name << "'";
            }
          
          cout << endl;
        }
    }

  return (temp);
}

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

  cmt_string complete_paths;

  complete_paths = path_separator;
  complete_paths += paths;
  complete_paths += path_separator;

  complete_paths.replace_all (double_path_separator, path_separator);

  cmt_string complete_value;
  complete_value = path_separator;
  complete_value += value;
  complete_value += path_separator;

  if (complete_paths.find (complete_value) == cmt_string::npos) return (false);
  else return (true);
}

//-------------------------------------------------------------
const cmt_string& PathBuilder::build (const Symbol& symbol,
                                      const cmt_string& /*tag_name*/)
{
  int show_it = 0;

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

  static cmt_string path_separator = CmtSystem::path_separator ();

  if (Cmt::action == action_show_set)
    {
      if (symbol.name == Cmt::current_target)
        {
          show_it = 1;
        }
    }

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

  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];
          
      if (show_it)
        {
          cout << "# Package ";
          if (value_list.use != 0)
            {
              cout << value_list.use->package << " " << value_list.use->version;
            }
        }
          
      new_value = value.text;
          
          //resolve_value (new_value);
      resolve_value_for_macros (new_value);
          
      switch (value_list.command_type)
        {
          case CommandPathAppend :
              
            if (show_it)
              {
                cout << " appends to path " << symbol.name << " : ";
              }
              
            if (new_value != "")
              {
                if (!find_path_entry (temp, new_value))
                  {
                    if (temp != "") temp += path_separator;
                      
                    temp += new_value;
                  }
              }
                  
            break;
          case CommandPathPrepend :
                  
            if (show_it)
              {
                cout << " prepends to path " << symbol.name << " : ";
              }
              
            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 (show_it)
              {
                cout << " removes from path " << symbol.name << " : ";
              }
              
            if (new_value != "")
              {
                static 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);
                temp.replace_all ("::", ":");
                temp.replace_all (";;", ";");
              }
              
            break;
        }

      if (show_it)
        {
          cout << "'" << value.text << "'";
              
          Selector* selected_selector = value.selector;
              
          if ((selected_selector == 0) ||
              (selected_selector == Cmt::default_selector))
            {
              cout << " for default selector";
            }
          else
            {
              cout << " for selector '" << selected_selector->name << "'";
            }
              
          cout << endl;
        }
    }
  
  return (temp);
}

//-------------------------------------------------------------
const cmt_string& MacroBuilder::build (const Symbol& symbol,
                                       const cmt_string& tag_name)
{
  static cmt_string previous_temp;
  static const cmt_string empty;
  int show_it = 0;

  if (Cmt::action == action_show_macro)
    {
      if (symbol.name == Cmt::current_target)
        {
          show_it = 1;
        }
    }

  temp = "";

  int i;

  for (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;

      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)
        {
          cout << "# Package ";
          if (value_list.use != 0)
            {
              cout << value_list.use->package << " " << value_list.use->version;
            }
          cout << " adds to macro " << symbol.name << " : ";
        }
      
      temp += value.text;
      
      if (show_it)
        {
          cout << "'" << value.text << "'";
          
          Selector* selected_selector = value.selector;
          
          if ((selected_selector == 0) ||
              (selected_selector == Cmt::default_selector))
            {
              cout << " for default selector";
            }
          else
            {
              cout << " for selector '" << selected_selector->name << "'";
            }
          
          cout << endl;
        }
    }

  previous_temp = temp;
  temp = "";

  for (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;

      if (value_list.command_type != CommandMacro) continue;

      const int selected = value_list.select_first (tag_name);

      if (selected >= 0)
        {
          SymbolValue& value = value_list.values[selected];

          if (show_it)
            {
              cout << "# Package ";
              if (value_list.use != 0)
                {
                  cout << value_list.use->package << " " << value_list.use->version;
                }
              cout << " defines macro " << symbol.name << " as ";
            }

          temp = value.text;

          if (show_it)
            {
              cout << "'" << value.text << "'";

              Selector* selected_selector = value.selector;

              if ((selected_selector == 0) ||
                  (selected_selector == Cmt::default_selector))
                {
                  cout << " for default selector";
                }
              else
                {
                  cout << " for selector '" << selected_selector->name << "'";
                }

              cout << 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.discarded) continue;

      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)
        {
          cout << "# Package ";
          if (value_list.use != 0)
            {
              cout << value_list.use->package << " " << value_list.use->version;
            }
          cout << " adds to macro " << symbol.name << " : ";
        }
      
      temp += value.text;
      
      if (show_it)
        {
          cout << "'" << value.text << "'";
          
          Selector* selected_selector = value.selector;
          
          if ((selected_selector == 0) ||
              (selected_selector == Cmt::default_selector))
            {
              cout << " for default selector";
            }
          else
            {
              cout << " for selector '" << selected_selector->name << "'";
            }
          
          cout << endl;
        }
    }

  for (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;

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

      const int selected = value_list.select_first (tag_name);

      if (selected < 0) continue;

      SymbolValue& value = value_list.values[selected];

      if (show_it)
        {
          cout << "# Package ";
          if (value_list.use != 0)
            {
              cout << value_list.use->package << " " << value_list.use->version;
            }
        }
      
      switch (value_list.command_type)
        {
          case CommandMacroRemove :
            if (show_it)
              {
                cout << " remove from macro " << symbol.name << " : ";
              }
            temp.replace (value.text, empty);
            break;
          case CommandMacroRemoveAll :
            if (show_it)
              {
                cout << " remove all from macro " << symbol.name << " : ";
              }
            temp.replace_all (value.text, empty);
            break;
        }
      
      if (show_it)
        {
          cout << "'" << value.text << "'";
          
          Selector* selected_selector = value.selector;
          
          if ((selected_selector == 0) ||
              (selected_selector == Cmt::default_selector))
            {
              cout << " for default selector";
            }
          else
            {
              cout << " for selector '" << selected_selector->name << "'";
            }
          
          cout << endl;
        }
    }

  return (temp);
}

//-------------------------------------------------------------
const cmt_string& ScriptBuilder::build (const Symbol& symbol,
                                        const cmt_string& tag_name)
{
  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);
}

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

  Selector* the_selector = 0;

  if (tag_name != "") the_selector = Selector::find (tag_name);

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

      const Selector* selector = value.selector;

      if (the_selector == 0)
        {
          if (!selector->selected) continue;
        }
      else
        {
          if (selector != the_selector) continue;
          selected = value_number;
        }

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

      if (selector->priority > priority)
        {
          priority = selector->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 Selector* selector = value.selector;

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

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

  return (selected);
}
