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

#ifdef WIN32
#include <direct.h>
#define chdir _chdir
#define rmdir _rmdir
//#define mkdir _mkdir
#define getcwd _getcwd
#define popen _popen
#define pclose _pclose
#define S_IFDIR _S_IFDIR
#define USE_GETCWD 1

#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <io.h>
#include <windows.h>

#define stat _stat

#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <dirent.h>
#endif

#ifdef __hpux__
#define USE_GETCWD 1
#endif

#ifdef __linux__
#define USE_GETCWD 1
#endif

#ifdef USE_GETCWD
char* getwd (const char* name)
{
  char dir[256];
  getcwd (dir, sizeof (dir));
  strcpy ((char*) name, dir);
  return ((char*) name);
}
#endif



#include "cmt_system.h"

//--------------------------------------------------
cmt_string CmtSystem::pwd ()
{
  char buffer[256] = "";
  char* ptr;

  ptr = getcwd (buffer, sizeof (buffer));

  const char* t = &buffer[0];
  return ((cmt_string) t);
}

//--------------------------------------------------
bool CmtSystem::cd (const cmt_string& dir)
{
  if ((dir.size () == 2) && (dir[1] == ':'))
    {
      cmt_string new_dir = dir;
      new_dir += file_separator ();
      if (chdir (new_dir.c_str ()) == 0) return (true);
      return (false);
    }
  else
    {
      if (chdir (dir.c_str ()) == 0) return (true);
      return (false);
    }
}

//--------------------------------------------------
void CmtSystem::basename (const cmt_string& file_name, cmt_string& result)
{
  int pos = file_name.find_last_of ('/');
  if (pos == cmt_string::npos)
    {
      pos = file_name.find_last_of ('\\');
    }

  if (pos == cmt_string::npos)
    {
      result = file_name;
    }
  else
    {
      file_name.substr (pos + 1, result);
    }
}

//--------------------------------------------------
void CmtSystem::basename (const cmt_string& file_name,
                          const cmt_string& /*suffix*/,
                          cmt_string& result)
{
  basename (file_name, result);

  int pos;

  pos = result.find_last_of ('.');

  if (pos != cmt_string::npos)
    {
      result.erase (pos);
    }
}

//--------------------------------------------------
void CmtSystem::dirname (const cmt_string& file_name, cmt_string& result)
{
  int pos = file_name.find_last_of ('/');
  if (pos == cmt_string::npos)
    {
      pos = file_name.find_last_of ('\\');
    }

  if (pos == cmt_string::npos)
    {
      result = "";
    }
  else
    {
      result = file_name;
      result.erase (pos);
    }
}

//--------------------------------------------------
void CmtSystem::name (const cmt_string& file_name, cmt_string& result)
{
  int pos;

  result = file_name;

  // remove the suffix

  pos = result.find_last_of ('.');

  if (pos != cmt_string::npos)
    {
      result.erase (pos);
    }

  // remove the directory name

  pos = result.find_last_of ('/');
  if (pos == cmt_string::npos)
    {
      pos = result.find_last_of ('\\');
    }

  if (pos != cmt_string::npos)
    {
      result.erase (0, pos + 1);
    }
}

//-------------------------------------------------
void CmtSystem::get_suffix (const cmt_string& file, cmt_string& result)
{
  int pos;

  pos = file.find_last_of ('.');

  if (pos == cmt_string::npos)
    {
      result = "";
    }
  else
    {
      file.substr (pos + 1, result);
    }
}

//-------------------------------------------------
void CmtSystem::get_dot_suffix (const cmt_string& file, cmt_string& result)
{
  int pos;

  pos = file.find_last_of ('.');

  if (pos == cmt_string::npos)
    {
      result = "";
    }
  else
    {
      file.substr (pos, result);
    }
}

//--------------------------------------------------
bool CmtSystem::has_prefix (const cmt_string& name)
{
  if ((name.find ('/') == cmt_string::npos) &&
      (name.find ('\\') == cmt_string::npos))
    {
      return (false);
    }

  return (true);
}

//--------------------------------------------------
bool CmtSystem::absolute_path (const cmt_string& name)
{
  if (name.size () == 0) return (false);

  if ((name[0] == '/') ||
      (name[0] == '\\')) return (true);

  if (name.size () >= 2)
    {
      if (name[1] == ':')
        {
          return (true);
        }
    }
  return (false);
}

//--------------------------------------------------
bool CmtSystem::has_device (const cmt_string& name)
//--------------------------------------------------
{
#ifdef WIN32
  if (name.size () == 0) return (false);

  if (name.size () >= 2)
    {
      if (name[1] == ':')
        {
          return (true);
        }
    }
#endif

  return (false);
}

//--------------------------------------------------
cmt_string CmtSystem::current_branch ()
{
  cmt_string result;

  basename (pwd (), result);

  return (result);
}

//--------------------------------------------------
bool CmtSystem::test_directory (const cmt_string& name)
{
  struct stat file_stat;
  int status;

  status = stat (name.c_str (), &file_stat);

  if (status == 0)
    {
      if ((file_stat.st_mode & S_IFDIR) == 0)
        {
          return (false);
        }
      else
        {
          return (true);
        }
    }
  else
    {
      return (false);
    }
}

//--------------------------------------------------
bool CmtSystem::test_file (const cmt_string& name)
{
  struct stat file_stat;
  int status;

  status = stat (name.c_str (), &file_stat);

  if (status == 0)
    {
      if ((file_stat.st_mode & S_IFDIR) == 0)
        {
          return (true);
        }
      else
        {
          return (false);
        }
    }
  else
    {
      return (false);
    }
}

//--------------------------------------------------
bool CmtSystem::compare_files (const cmt_string& name1,
                               const cmt_string& name2)
{
  struct stat file_stat1;
  struct stat file_stat2;
  int status;

  status = stat (name1.c_str (), &file_stat1);

  if (status == 0)
    {
      if ((file_stat1.st_mode & S_IFDIR) != 0)
        {
          return (false);
        }
    }
  else
    {
      return (false);
    }

  status = stat (name2.c_str (), &file_stat2);

  if (status == 0)
    {
      if ((file_stat2.st_mode & S_IFDIR) != 0)
        {
          return (false);
        }
    }
  else
    {
      return (false);
    }

  if (((int) file_stat1.st_size) != ((int) file_stat2.st_size))
    {
      return (false);
    }

  static cmt_string s1;
  static cmt_string s2;

  s1.read (name1);
  s2.read (name2);

  return ((s1 == s2));
}

//--------------------------------------------------
//
// Check if the file "name1" is identical to "name2"
// if they are identical, "name1" will be simply deleted
// otherwise "name1" will be copied to "name2" and deleted afterwards
//
//--------------------------------------------------
bool CmtSystem::compare_and_update_files (const cmt_string& name1,
                                          const cmt_string& name2)
{
  struct stat file_stat1;
  struct stat file_stat2;
  static cmt_string s1;
  static cmt_string s2;
  int status;

  status = stat (name1.c_str (), &file_stat1);

  if (status == 0)
    {
      if ((file_stat1.st_mode & S_IFDIR) != 0)
        {
          // name1 is a directory.
          return (false);
        }
    }
  else
    {
      // name1 does not exist
      return (false);
    }

  s1.read (name1);

  status = stat (name2.c_str (), &file_stat2);

  if (status == 0)
    {
      if ((file_stat2.st_mode & S_IFDIR) != 0)
        {
          // name2 is a directory
          return (false);
        }

      if (((int) file_stat1.st_size) == ((int) file_stat2.st_size))
        {
          s2.read (name2);
          if (s1 == s2)
            {
              unlink (name1);
              return (true);
            }
        }
    }

  FILE* f = fopen (name2, "wb");
  if (f != NULL)
    {
      s1.write (f);
      fclose (f);

      unlink (name1);

      return (true);
    }
  else
    {
      //
      // keep the new file "name1" since it cannot be
      // copied to "name2"
      //
      return (false);
    }
}

//--------------------------------------------------
int CmtSystem::file_size (const cmt_string& name)
{
  struct stat file_stat;
  int status;

  status = stat (name.c_str (), &file_stat);

  if (status == 0)
    {
      return ((int) file_stat.st_size);
    }
  else
    {
      return (0);
    }
}

//--------------------------------------------------
char CmtSystem::file_separator ()
{
#ifdef WIN32
  return ('\\');
#else
  return ('/');
#endif
}

//--------------------------------------------------
char CmtSystem::path_separator ()
{
#ifdef WIN32
  return (';');
#else
  return (':');
#endif
}

//--------------------------------------------------
const cmt_string& CmtSystem::ev_open ()
{
#ifdef WIN32
  static const cmt_string s = "%";
#else
  static const cmt_string s = "${";
#endif

  return (s);
}

//--------------------------------------------------
const cmt_string& CmtSystem::ev_close ()
{
#ifdef WIN32
  static const cmt_string s = "%";
#else
  static const cmt_string s = "}";
#endif

  return (s);
}

//-------------------------------------------------
bool CmtSystem::remove_file (const cmt_string& name)
{
  if (::unlink (name) != 0)
    {
      cout << "Cannot remove file " << name << endl;
      return (false);
    }

  return (true);
}

//-------------------------------------------------
bool CmtSystem::remove_directory (const cmt_string& name)
{
  //cout << "Try to remove directory " << name << endl;

  cmt_string_vector files;

  scan_dir (name, files);

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

      if (test_directory (file))
        {
          if (!remove_directory (file)) return (false);
        }
      else
        {
          if (!remove_file (file)) return (false);
        }
    }
 
  int status = ::rmdir (name);
  if (status != 0)
    {
      cout << "Cannot remove directory " << name << " errno=" << errno << endl;
      return (false);
    }

  return (true);
}

//-------------------------------------------------
bool CmtSystem::mkdir (const cmt_string& name)
{
  static cmt_string_vector path_vector;
  int i;
  static cmt_string full_path;
  char double_fs[] = "  ";

  double_fs[0] = file_separator ();
  double_fs[1] = file_separator ();

  full_path = name;

  if (file_separator () == '/')
    {
      full_path.replace_all ("\\", file_separator ());
    }
  else
    {
      full_path.replace_all ("/", file_separator ());
    }

  full_path.replace_all (double_fs, file_separator ());

  split (full_path, file_separator (), path_vector);

  full_path = "";

  if (absolute_path (name))
    {
      if (!has_device (name))
        {
          full_path = file_separator ();
        }
    }

  for (i = 0; i < path_vector.size (); i++)
    {
      const cmt_string& path = path_vector[i];

      if (i > 0) full_path += file_separator ();
      full_path += path;

      if (has_device (path)) continue;

      if (!test_directory (full_path))
        {
#ifdef WIN32
          if (::_mkdir (full_path.c_str ()) != 0)
            {
              cerr << "CMT> cannot create directory " << full_path << endl;
              return (false);
            }
#else
          if (::mkdir (full_path.c_str (), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) != 0)
            {
              cerr << "CMT> cannot create directory " << full_path << endl;
              return (false);
            }
#endif
        }
    }

  return (true);
}

//----------------------------------------------------------
void CmtSystem::scan_dir (const cmt_string& dir_name,
                          cmt_string_vector& list)
//----------------------------------------------------------
{
  static cmt_string dir_prefix;
  static cmt_string name_prefix;

  dir_prefix = dir_name;
  if (dir_name == "") dir_prefix = ".";

  if (!test_directory (dir_prefix))
    {
      dirname (dir_prefix, dir_prefix);
      basename (dir_name, name_prefix);
    }
  else
    {
    }

  bool need_filter = false;

  int wild_card;

  wild_card = name_prefix.find ('*');
  if (wild_card != cmt_string::npos)
    {
      name_prefix.erase (wild_card);
    }

  if (name_prefix.size () > 0)
    {
      need_filter = true;
    }

  list.clear ();

#ifdef WIN32

  long dir;
  struct _finddata_t entry;

  static cmt_string search;

  search = dir_prefix;
  search += file_separator ();
  search += "*";

  dir = _findfirst (search.c_str (), &entry);
  if (dir > 0)
    {
      for (;;)
        {
          if ((strcmp ((char*) entry.name, ".") != 0) &&
              (strcmp ((char*) entry.name, "..") != 0) &&
              (strncmp ((char*) entry.name, ".nfs", 4) != 0))
            {
              const char* name = entry.name;

              if (!need_filter ||
                  (strncmp (name, name_prefix.c_str (), name_prefix.size ()) == 0))
                {
                  cmt_string& name_entry = list.add ();

                  name_entry = dir_prefix;
                  name_entry += file_separator ();
                  name_entry += name;
                }
            }

          int status = _findnext (dir, &entry);
          if (status != 0)
            {
              break;
            }
        }

      _findclose (dir);
    }
#else

  //cout << "scan_dir> dir=" << dir_name << endl;

  DIR* dir = opendir (dir_prefix.c_str ());

  struct dirent* entry;

  if (dir != 0)
    {
      while ((entry = readdir (dir)) != 0)
        {
          //if (entry->d_name[0] == '.') continue;
          if (!strcmp ((char*) entry->d_name, ".")) continue;
          if (!strcmp ((char*) entry->d_name, "..")) continue;
          if (!strncmp ((char*) entry->d_name, ".nfs", 4)) continue;

          const char* name = entry->d_name;

          if (need_filter &&
              (strncmp (name, name_prefix.c_str (), name_prefix.size ()) != 0)) continue;

          //cout << "scan_dir> name=" << name << endl;

          cmt_string& name_entry = list.add ();

          name_entry = dir_prefix;
          name_entry += file_separator ();
          name_entry += name;
        }

      closedir (dir);
    }
#endif

}

//----------------------------------------------------------
void CmtSystem::scan_dir (const cmt_string& dir_name,
                          const cmt_regexp& expression,
                          cmt_string_vector& list)
//----------------------------------------------------------
{
  static cmt_string dir_prefix;

  dir_prefix = dir_name;
  if (dir_name == "") dir_prefix = ".";

  if (!test_directory (dir_prefix))
    {
      dirname (dir_prefix, dir_prefix);
    }

  list.clear ();

#ifdef WIN32

  long dir;
  struct _finddata_t entry;

  static cmt_string search;

  search = dir_prefix;
  search += file_separator ();
  search += "*";

  dir = _findfirst (search.c_str (), &entry);
  if (dir > 0)
    {
      for (;;)
        {
          if ((entry.name[0] != '.') &&
              (strcmp ((char*) entry.name, ".") != 0) &&
              (strcmp ((char*) entry.name, "..") != 0) &&
              (strncmp ((char*) entry.name, ".nfs", 4) != 0))
            {
              const char* name = entry.name;
              
              if (expression.match (name))
                {
                  cmt_string& name_entry = list.add ();

                  name_entry = dir_prefix;
                  name_entry += file_separator ();
                  name_entry += name;
                }
            }

          int status = _findnext (dir, &entry);
          if (status != 0)
            {
              break;
            }
        }
      _findclose (dir);
    }
#else

  //cout << "scan_dir> dir=" << dir_name << endl;

  DIR* dir = opendir (dir_prefix.c_str ());

  struct dirent* entry;

  if (dir != 0)
    {
      while ((entry = readdir (dir)) != 0)
        {
          //if (entry->d_name[0] == '.') continue;
          if (!strcmp ((char*) entry->d_name, ".")) continue;
          if (!strcmp ((char*) entry->d_name, "..")) continue;
          if (!strncmp ((char*) entry->d_name, ".nfs", 4)) continue;

          const char* name = entry->d_name;

          if (!expression.match (name)) continue;

          cmt_string& name_entry = list.add ();

          name_entry = dir_prefix;
          name_entry += file_separator ();
          name_entry += name;
        }

      closedir (dir);
    }
#endif

}

//----------------------------------------------------------
CmtSystem::cmt_string_vector& CmtSystem::scan_dir (const cmt_string& dir_name)
{
  static cmt_string_vector result;

  scan_dir (dir_name, result);

  return (result);
}

//----------------------------------------------------------
const cmt_string& CmtSystem::get_cmt_root ()
{
  static cmt_string root;

  root = "";

  const char* env = ::getenv ("CMTROOT");
  if (env != 0)
    {
      root = env;

      dirname (root, root);
      dirname (root, root);
      return (root);
    }

#ifdef WIN32
  LONG status;
  HKEY key = 0;

  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
                         0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      char temp[256];
      DWORD length = sizeof (temp) - 1;
      DWORD type;

      status = RegQueryValueEx (key, "root", 0, &type, (LPBYTE) temp, &length);
      if (status == ERROR_SUCCESS)
        {
          root = temp;
          return (root);
        }
    }
#endif

  return (root);
}

//----------------------------------------------------------
void CmtSystem::get_cmt_version (cmt_string& version)
{
  version = "";

  const char* env = ::getenv ("CMTROOT");
  if (env != 0)
    {
      cmt_string s = env;
      basename (s, version);
    }
  else
    {
#ifdef WIN32
      LONG status;
      HKEY key = 0;

      status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
                             0, KEY_READ, &key);
      if (status == ERROR_SUCCESS)
        {
          char temp[256];
          DWORD length = sizeof (temp) - 1;
          DWORD type;
          
          status = RegQueryValueEx (key, "version", 0, &type, 
                                    (LPBYTE) temp, &length);
          if (status == ERROR_SUCCESS)
            {
              version = temp;
            }
        }
#endif
    }
}

//----------------------------------------------------------
cmt_string CmtSystem::get_cmt_config ()
{
  const char* env = ::getenv ("CMTCONFIG");
  if (env != 0)
    {
      return (cmt_string (env));
    }

#ifdef WIN32
  LONG status;
  HKEY key = 0;

  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
                         0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      char temp[256];
      DWORD length = sizeof (temp) - 1;
      DWORD type;

      status = RegQueryValueEx (key, "config", 0, &type, 
                                (LPBYTE) temp, &length);
      if (status == ERROR_SUCCESS)
        {
          cmt_string config (temp);
          return (config);
        }
    }

  return ("VisualC");
#endif

  return ("");

}

//----------------------------------------------------------
cmt_string CmtSystem::get_cmt_site ()
{
  const char* env = ::getenv ("CMTSITE");
  if (env != 0)
    {
      return (cmt_string (env));
    }

#ifdef WIN32
  LONG status;
  HKEY key = 0;

  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
                         0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      char temp[256];
      DWORD length = sizeof (temp) - 1;
      DWORD type;

      status = RegQueryValueEx (key, "site", 0, &type, (LPBYTE) temp, &length);
      if (status == ERROR_SUCCESS)
        {
          cmt_string site (temp);
          return (site);
        }
    }
#endif

  return ("LAL");
}

//----------------------------------------------------------
void CmtSystem::get_uname (cmt_string& uname)
//----------------------------------------------------------
{
#ifdef WIN32
  uname = "WIN32";
#else

  uname = "";

  FILE* file;

  file = popen ("uname", "r");

  if (file != 0)
    {
      char line[1024];
      char* ptr;
      char* nl;

      line[0] = 0;
      ptr = fgets (line, sizeof (line), file);
      if (ptr != 0)
        {
          nl = strrchr (ptr, '\n');
          if (nl != 0) *nl = 0;

          uname = ptr;
        }
      pclose (file);
    }
#endif
}

//----------------------------------------------------------
void CmtSystem::get_hosttype (cmt_string& hosttype)
//----------------------------------------------------------
{
  hosttype = "";

  char* ptr;

  ptr = ::getenv ("HOSTTYPE");
  if (ptr != 0)
    {
      hosttype = ptr;
    }
}

//----------------------------------------------------------
cmt_string CmtSystem::get_temporary_name ()
//----------------------------------------------------------
{
  cmt_string name;

  name = ::tmpnam (NULL);

  return (name);
}

//----------------------------------------------------------
cmt_string CmtSystem::getenv (const cmt_string& name)
//----------------------------------------------------------
{
  cmt_string result;

  const char* env = ::getenv (name);
  if (env != 0)
    {
      result = env;
    }

  if (name == "CMTCONFIG")
  {
    return (get_cmt_config ());
  }

  /*
  if (name == "CMTROOT")
  {
          return (get_cmt_root ());
  }

  if (name == "CMTSITE")
  {
          return (get_cmt_site ());
  }
  */

  return (result);
}

//----------------------------------------------------------
void CmtSystem::add_cmt_path (const cmt_string& path,
                              const cmt_string& path_source,
                              cmt_string_vector& paths,
                              cmt_string_vector& path_sources)
//----------------------------------------------------------
{
  cmt_string npath = path;

  if (path == "") return;

  npath.replace_all ("\\", file_separator ());
  npath.replace_all ("/", file_separator ());
  
  for (int i = 0; i < paths.size (); i++)
    {
      const cmt_string& p = paths[i];
      
      if (p == npath) return;
    }
  
  cmt_string& new_path = paths.add ();
  new_path = npath;
  cmt_string& new_source = path_sources.add ();
  new_source = path_source;
}

//----------------------------------------------------------
static void add_cmt_paths (const cmt_string& file_name,
                           CmtSystem::cmt_string_vector& paths,
                           CmtSystem::cmt_string_vector& path_sources)
//----------------------------------------------------------
{
  if (!CmtSystem::test_file (file_name)) return;

  static cmt_string text;

  text.read (file_name);

  int pos = text.find ("CMTPATH");
  if (pos == cmt_string::npos) return;
  pos += strlen ("CMTPATH");
  pos = text.find (pos, "=");
  if (pos == cmt_string::npos) return;
  pos++;

  text.erase (0, pos);

  int nl = text.find (pos, "\n");
  if (nl != cmt_string::npos) text.erase (nl);

  static CmtSystem::cmt_string_vector path_vector;
  int i;

  CmtSystem::split (text, CmtSystem::path_separator (), path_vector);

  for (i = 0; i < path_vector.size (); i++)
    {
      const cmt_string& path = path_vector[i];

      CmtSystem::add_cmt_path (path, file_name, paths, path_sources);
    }
}

//----------------------------------------------------------
void CmtSystem::get_cmt_paths (cmt_string_vector& paths, 
                               cmt_string_vector& path_sources)
//----------------------------------------------------------
{
  paths.clear ();

#ifdef WIN32
  LONG status;
  HKEY key = 0;

  status = RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\CMT\\path",
                         0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      DWORD index = 0;
      char name[256];
      char temp[256];

      for (;;)
        {
          DWORD name_length = sizeof (name) - 1;
          DWORD length = sizeof (temp) - 1;
          DWORD type;
          status = RegEnumValue (key, index,
                                 name, &name_length, 0, &type,
                                 (LPBYTE) temp, &length);
          if ((status == ERROR_SUCCESS) ||
              (status == 234))
            {
              const cmt_string path = temp;
              add_cmt_path (path, "HKEY_CURRENT_USER", paths, path_sources);
            }

          if (status == 259)
            {
              break;
            }

          index++;
        }
    }

  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT\\path",
                         0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      DWORD index = 0;
      char name[256];
      char temp[256];

      for (;;)
        {
          DWORD type;
          DWORD name_length = sizeof (name) - 1;
          DWORD length = sizeof (temp) - 1;


          status = RegEnumValue (key, index,
                                 name, &name_length, 0, &type,
                                 (LPBYTE) temp, &length);
          if (status != ERROR_NO_MORE_ITEMS)
            {
              const cmt_string path = temp;
              add_cmt_path (path, "HKEY_LOCAL_MACHINE", paths, path_sources);
            }
          else
            {
              break;
            }
          index++;
        }
    }

#endif

  //-----------------------------------------
  // look for .cmtrc files :
  //  first look in ./
  //  then in "~/"
  //  then in ${CMTROOT}/mgr
  //-----------------------------------------
  cmt_string rc_name;

  const char* env = ::getenv ("CMTPATH");
  if (env != 0)
    {
      static cmt_string_vector path_vector;
      int i;

      split (env, path_separator (), path_vector);

      for (i = 0; i < path_vector.size (); i++)
        {
          const cmt_string& path = path_vector[i];

          add_cmt_path (path, "${CMTPATH}", paths, path_sources);
        }
    }

  add_cmt_paths (".cmtrc", paths, path_sources);

  const char* home_env = ::getenv ("HOME");
  if (home_env != 0)
    {
      rc_name = home_env;
      rc_name += file_separator ();
      rc_name += ".cmtrc";
      add_cmt_paths (rc_name, paths, path_sources);
    }

  rc_name = get_cmt_root ();
  rc_name += file_separator ();
  rc_name += "CMT";
  rc_name += file_separator ();
  cmt_string version;
  get_cmt_version (version);
  rc_name += version;
  rc_name += file_separator ();
  rc_name += "mgr";
  rc_name += file_separator ();
  rc_name += ".cmtrc";

  add_cmt_paths (rc_name, paths, path_sources);

  add_cmt_path (get_cmt_root (), "default path", paths, path_sources);

}

//----------------------------------------------------------
int CmtSystem::execute (const cmt_string& command)
{
  //cout << "CmtSystem::execute1> [" << command << "]" << endl;

  return (system (command.c_str ()));
}

//----------------------------------------------------------
int CmtSystem::execute (const cmt_string& command, cmt_string& output)
{
  output = "";

  //cout << "CmtSystem::execute2> [" << command << "]" << endl;

  FILE* f = popen (command.c_str (), "r"); 
  
  if (f != 0) 
    { 
      char line[256]; 
      char* ptr;

      while ((ptr = fgets (line, sizeof (line), f)) != NULL) 
        {
          output += ptr;
        } 
      pclose (f);

      return (1);
    }

  return (0);
}

//----------------------------------------------------------
bool CmtSystem::is_package_directory (const cmt_string& name)
{
  cmt_string_vector dirs;

  cmt_regexp exp ("^[a-zA-Z.][0-9]+([a-zA-Z.][0-9]+([a-zA-Z.][0-9]+)?)?");

  scan_dir (name, exp, dirs);

  if (dirs.size () == 0) return (false);

  cmt_string req;

  for (int i = 0; i < dirs.size (); i++)
      {
        const cmt_string& d = dirs[i];

        req = d;
        req += file_separator ();
        req += "mgr";
        req += file_separator ();
        req += "requirements";

        if (test_file (req)) return (true);

        req = d;
        req += file_separator ();
        req += "cmt";
        req += file_separator ();
        req += "requirements";

        if (test_file (req)) return (true);
      }

  return (false);
}

//----------------------------------------------------------
bool CmtSystem::is_version_directory (const cmt_string& name)
{
  int v;
  int r;
  int p;

  return (is_version_directory (name, v, r, p));
}

//----------------------------------------------------------
bool CmtSystem::is_version_directory (const cmt_string& name,
                                      int& v,
                                      int& r,
                                      int& p)
{
  static const cmt_string numbers = "0123456789";

  static const int id_version = 0;
  static const int id_release = 1;
  static const int id_patch   = 2;

  cmt_string buffer;

  enum 
  {
    starting,
    at_key,
    at_number
  } state;

  int id;
  int pos;
  int value;

  v = 0;
  r = 0;
  p = 0;

  //
  // version : v-field
  //         | v-field r-field
  //         | v-field r-field p-field
  //
  // v-field : field
  // r-field : field
  // p-field : field
  //
  // field   : key '*'
  //         | key number
  //
  // key     : letters
  //

  state = starting;
  id    = id_version;

  for (pos = 0; pos < name.size (); pos++)
    {
      char c = name[pos];

      if (c == '*')
        {
          // A wild card
          switch (state)
            {
            case starting:
              // cannot start with a wild card ??
              return (false);
            case at_key:
              // the numeric field is valued with a wild card
              return (true);
            case at_number:
              // Syntax error : number followed by a wild-card ??
              return (false);
            }
        }
      else if (numbers.find (c) == cmt_string::npos)
        {
          // A letter
          switch (state)
            {
            case starting:
              state = at_key;
              break;
            case at_key:
              // Multiple letter key (is it permitted??)
              break;
            case at_number:
              sscanf (buffer.c_str (), "%d", &value);
              switch (id)
                {
                case id_version:
                  v = value;
                  break;
                case id_release:
                  r = value;
                  break;
                case id_patch:
                  p = value;
                  break;
                }
              buffer = "";
              id++;
              state = at_key;
              break;
            }
        }
      else
        {
          // a number
          switch (state)
            {
            case starting:
              // not starting by a letter (syntax error)
              return (false);
            case at_key:
              // the numeric field for the current id is starting now
              buffer += c;
              state = at_number;
              break;
            case at_number:
              // continuing the current numeric field
              buffer += c;
              break;
            }
        }
    }

  switch (state)
    {
    case starting:
      // Empty version string
      return (false);
    case at_key:
      // Syntax error (only letters)
      return (false);
    case at_number:
      sscanf (buffer.c_str (), "%d", &value);
      switch (id)
        {
        case id_version:
          v = value;
          break;
        case id_release:
          r = value;
          break;
        case id_patch:
          p = value;
          break;
        }
      id++;
      state = at_key;
      return (true);
    }

  return (false);
}

//----------------------------------------------------------
//  Split a line into words. Separators are spaces and tabs
//  Text enclosed in double quotes is one word.
//----------------------------------------------------------
void CmtSystem::split (const cmt_string& text,
                       const cmt_string& separators,
                       cmt_string_vector& strings)
{
  static char* buffer = 0;
  static int allocated = 0;

  bool finished = false;

  strings.clear ();

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

  /*
    We are going to work in a copy of the text, since
    \0 will be inserted right after each found word.

    Then the vector of strings is iteratively filled by each found word.
  */

  if (buffer == 0)
    {
      allocated = text.size ();
      buffer = (char*) malloc (allocated + 1);
    }
  else
    {
      if (text.size () > allocated)
        {
          allocated = text.size ();
          buffer = (char*) realloc (buffer, allocated + 1);
        }
    }

  strcpy (buffer, text.c_str ());

  /*
    Algorithm :

    We look for words separated by <separators> which may be
    o spaces (' ' or '\t')
    o other characters such as ':'

    A word is a character string not containing any separator. A substring in
    this word my be enclosed between quotes (" or ') which permits separator
    inclusion within words.
  */

  char* current_word = buffer;

  while (*current_word != 0)
    {
      size_t prefix_length;
      size_t word_length;

      /*
        while ((*current_word == ' ') ||
        (*current_word == '\t'))
        {
        current_word++;
        }
      */

      // first skip all starting separators.

      prefix_length = strspn (current_word, separators.c_str ());
      if (prefix_length > 0)
        {
          // Move to the first non-separator character

          current_word += prefix_length;
        }

      /*
        Parse the next word.

        It may contain enclosures in quote characters or not.
        Quotes must be identical on both sides of each enclosure.
      */

      char* running_char = current_word;

      word_length = 0;

      for (;;)
        {
          size_t unquoted_length;
          size_t separator_offset;

          unquoted_length = strcspn (running_char, "\"\'");
          separator_offset = strcspn (running_char, separators.c_str ());

          if (separator_offset <= unquoted_length)
            {
              // no quote in this word -> we are finished for this one.
              running_char += separator_offset;
              break;
            }

          // We have found a quoted enclosure. Move to it.

          running_char += unquoted_length;

          char quote = running_char[0];

          // Remove it.
          {
            char* p = running_char;
            while (p[1] != 0)
              {
                *p = p[1];
                p++;
              }
            *p = 0;
          }

          // Look for the next occurence of this quote.
          {
            char* p = strchr (running_char, quote);
            if (p == 0)
              {
                // Unmatched quote : the rest of the line will be taken as a word...
                running_char += strlen (running_char);
                finished = true;
                break;
              }
            else
              {
                running_char = p;
              }
          }

          // Now we remove the ending quote from the word
          // (by shifting all remaining characters by one place to the left)

          {
            char* p = running_char;
            while (p[1] != 0)
              {
                *p = p[1];
                p++;
              }
            *p = 0;
          }
        }

      word_length = running_char - current_word;

      if (current_word[word_length] == 0)
        {
          finished = true;
        }
      else
        {
          current_word[word_length] = 0;
        }

      /*
        if ((t[0] == '"') ||
        (t[0] == '\'') ||
        (t[0] == ':'))
        {
        char* quote;

        t++;
        quote = strchr (t, sep);
        if (quote != 0) *quote = 0;
        else finished = true;
        }
        else
        {
        int offset;

        offset = strcspn (t, " \t:");
        if ((offset < 0) || (t[offset] == 0)) finished = true;
        if (!finished)
        {
        space = t + offset;
        *space = 0;
        }
        }
      */

      // Store the current word into the vector of strings

      {
        cmt_string& s = strings.add ();
        s = current_word;
      }

      if (finished) break;

      // Move to the next possible word.
      current_word += word_length + 1;
    }
}

//----------------------------------------------------------
void CmtSystem::compress_path (const cmt_string& dir, cmt_string& new_dir)
{
  new_dir = dir;

  compress_path (new_dir);
}

//----------------------------------------------------------
//
//  We try to detect the aaaa/xxxx/../bbbb patterns which should be 
// equivalent to aaaa/bbbb
//  this therefore consists in removing all /xxxx/../
//
//----------------------------------------------------------
void CmtSystem::compress_path (cmt_string& dir)
{
#ifdef WIN32
  static const char pattern[] = "\\..\\";
  static const char here[] = ".\\";
  static const char fs[] = "\\\\";
#else
  static const char pattern[] = "/../";
  static const char here[] = "./";
  static const char fs[] = "//";
#endif

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

    //
    // We first synchronize to using file_separator() in any case.
    //

  if (file_separator () == '/')
    {
      dir.replace_all ("\\", file_separator ());
    }
  else
    {
      dir.replace_all ("/", file_separator ());
    }

  dir.replace_all (fs, file_separator ());

  for (;;)
    {
      int pos1;
      int pos2;

      pos1 = dir.find (pattern);
      if (pos1 == cmt_string::npos) break;

        //
        // extract "aaaa/xxxx" from "aaaa/xxxx/../bbbb"
        //
      cmt_string p = dir.substr (0, pos1);
      
        //
        // Is "aaaa/xxxx" only made of "xxxx" ?
        // 
      pos2 = p.find_last_of (file_separator ());
      
      if (pos2 == cmt_string::npos) break;
      
        //    01234567890123456
        //    aaaa/xxxx/../bbbb
        //        2    1   3
        //
        // erase the "/xxxx/../" pattern
        // result will be "aaaa/bbbb"
        //
      dir.erase (pos2, pos1 + 4 - pos2 - 1);
    }

    //if (dir[dir.size () - 1] == file_separator ()) dir.erase (dir.size () - 1);
}

//----------------------------------------------------------
cmt_string CmtSystem::now ()
{
  cmt_string result;

  time_t ltime;
  time (&ltime);
  result = ctime (&ltime);

  result.replace_all ("\n", "");

  return (result);
}

//----------------------------------------------------------
cmt_string CmtSystem::user ()
{
#ifdef _WIN32
  cmt_string result = getenv ("USERNAME");
#else
  cmt_string result = getenv ("USER");
#endif

  return (result);
}

//----------------------------------------------------------
void CmtSystem::get_cvsroot (cmt_string& cvsroot)
{
  cvsroot = "";

  const char* env = ::getenv ("CVSROOT");
  if (env != 0)
    {
      cvsroot = env;
      return;
    }

#ifdef WIN32
  LONG status;
  HKEY key = 0;

  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
                         0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      char temp[256];
      DWORD length = sizeof (temp) - 1;
      DWORD type;

      status = RegQueryValueEx (key, "CVSROOT", 0, &type, 
                                (LPBYTE) temp, &length);
      if (status == ERROR_SUCCESS)
        {
          cvsroot = temp;
          return;
        }
    }
#endif
}

