// This may look like C code, but it is really -*- C++ -*-
// RTSS --- Railway Total System Simulator
// (c) TAKAGI Ryo (at Kogakuin University)
// rtss_path.cc --- implementation of class RTSS_Path_String
// -----
// ChangeLog:
// 2007. 11. 20
//  File created. Mainly moving from main.cc.
// -----


#include "rtss_path.hh"
#include <iostream>
#include <cstdlib>
#include <pcre.h>
#include <unistd.h>
#include <vector>

using std :: cerr ;
using std :: endl ;
using std :: vector ;



// -----
// Constructors
// コンストラクタ
// -----

RTSS_Path_String :: RTSS_Path_String
()
  : string ()
{
  ovector = new int [ RP_OVECCOUNT ] ;
}

RTSS_Path_String :: RTSS_Path_String
( const char * x )
  : string ( x )
{
  ovector = new int [ RP_OVECCOUNT ] ;
}

RTSS_Path_String :: RTSS_Path_String
( const string & x )
  : string ( x )
{
  ovector = new int [ RP_OVECCOUNT ] ;
}

RTSS_Path_String :: RTSS_Path_String
( const RTSS_Path_String & x )
  : string ( x )
{
  ovector = new int [ RP_OVECCOUNT ] ;
}


// -----
// Substitution operator
// 代入演算子
// -----

RTSS_Path_String &
RTSS_Path_String :: operator=
( const RTSS_Path_String & x )
{
  if ( & x == this ) return * this ;
  string :: operator= ( x ) ;
  return * this ;
}



// -----
// Returns true if the full path name is of Windows style. If true, the
// drive name will be set to the argument string variable, and the drive
// name will be taken away from the path name contained in the class.
// ウィンドウズ・スタイルのフルパス名である場合 true を返す. True なら，
// そのさい string 引数にドライブ名がセットされ，クラスに格納されている
// ファイル名はドライブ名を取り去られる.
// -----
bool
RTSS_Path_String :: isWindowsStyleFullPath
( string & drive_out )
{
  const char * error ;
  int erroffset ;
  string pcre_in = "^([A-z]:)(.*)$" ;
  pcre * re
    = pcre_compile ( pcre_in . c_str () , PCRE_UTF8 & PCRE_DOLLAR_ENDONLY ,
                     & error , & erroffset , NULL ) ;
  if ( re == NULL )
  {
    cerr << "ERROR: in RTSS_Path_String::isWindowsStyleFullPath(...): "
         << "PCRE compilation failed at offset " << erroffset
         << ": " << error << endl ;
    exit ( 1 ) ;
  }
  int rc = pcre_exec ( re , NULL , c_str () , length () , 0 , 0 ,
                       ovector , RP_OVECCOUNT ) ;
  pcre_free ( re ) ;
  if ( rc == PCRE_ERROR_NOMATCH )
  {
    if ( string ( c_str () ) == "/" )
    {
      string :: operator= ( "" ) ;
    }
    return false ;
  }
  else if ( rc < 0 )
  {
    cerr << "ERROR: in RTSS_Path_String::isWindowsStyleFullPath(...): "
         << "PCRE match error." << endl ;
    exit ( 1 ) ;
  }
  else
  {
    // -----
    // Substring 1 = drive name, 2 = the rest. 1 will be set to string
    // drive_out.
    // Substring 1 = ドライブ名, 2 = のこり。1 は drive_out 変数に格納する。
    // -----
    char buf [ PATH_MAX ] ;
    int subs_l
      = pcre_copy_substring ( c_str () , ovector , rc , 1 , buf , PATH_MAX ) ;
    drive_out = string ( buf ) ;
    subs_l
      = pcre_copy_substring ( c_str () , ovector , rc , 2 , buf , PATH_MAX ) ;
    string current_directory ( buf ) ;

    // -----
    // Check if directory name part starts with a (back)slash.
    // ディレクトリ名部分が(バック)スラッシュから始まることを確認。
    // -----
    pcre_in = "^([/\\\\])(.*)$" ;
    re = pcre_compile ( pcre_in . c_str () , PCRE_UTF8 & PCRE_DOLLAR_ENDONLY ,
                        & error , & erroffset , NULL ) ;
    if ( re == NULL )
    {
      cerr << "ERROR: in RTSS_Path_String::isWindowsStyleFullPath(...): "
           << "PCRE compilation failed at offset " << erroffset
           << ": " << error << endl ;
      exit ( 2 ) ;
    }
    rc = pcre_exec ( re , NULL , current_directory . c_str () ,
                         current_directory . length () , 0 , 0 ,
                         ovector , RP_OVECCOUNT ) ;
    pcre_free ( re ) ;
    if ( rc == PCRE_ERROR_NOMATCH )
    {
      cerr << "Error: There must be a (back)slash after the drive name."
           << endl ;
      exit ( 1 ) ;
    }
    else if ( rc < 0 )
    {
      cerr << "ERROR: in RTSS_Path_String::isWindowsStyleFullPath(...): "
           << "PCRE match error." << endl ;
      exit ( 2 ) ;
    }

    // -----
    // current_directory will be substituted to "this".
    // ディレクトリ名部分を "this" に代入.
    // -----
    if ( current_directory == "\\" )
    {
      current_directory = "" ;
    }
    string :: operator= ( current_directory ) ;
    return true ;
  }
}




// -----
// Returns true if the name has .xml extension.
// .xml 拡張子を持つ場合 true を返す.
// -----
bool
RTSS_Path_String :: hasXmlExtension
()
  const
{
  const char * error ;
  int erroffset ;
  pcre * re = pcre_compile ( "[.]xml$" , PCRE_UTF8 & PCRE_DOLLAR_ENDONLY ,
                             & error , & erroffset , NULL ) ;
  if ( re == NULL )
  {
    cerr << "ERROR: in RTSS_Path_String::hasXmlExtension(): "
         << "PCRE compilation failed at offset " << erroffset
         << ": " << error << endl ;
    exit ( 1 ) ;
  }
  int rc = pcre_exec ( re , NULL , c_str () , length () ,
                       0 , 0 , ovector , RP_OVECCOUNT ) ;
  pcre_free ( re ) ;
  if ( rc == PCRE_ERROR_NOMATCH )
  {
    return false ;
  }
  else if ( rc < 0 )
  {
    cerr << "Error: in RTSS_Path_String::hasXmlExtension(): PCRE match error."
         << endl ;
    exit ( 1 ) ;
  }
  else
  {
    return true ;
  }
}



// -----
// Process Path. Arguments: whether it is windows-style (bool), current
// drive (const string &), current directory (const string &), resulting
// file drive name (string &), resulting file directory name (string &),
// resulting file name (string &). Returns true if successful.
// パス名を処理する. 引数: ウィンドウズ・スタイルか否か (bool), カレント
// ドライブ (const string &), カレントディレクトリ (const string &),
// 結果的なファイルのドライブ名 (string &), 同ディレクトリ名 (string &),
// 同ファイル名 (string &). 成功すれば true.
// -----
bool
RTSS_Path_String :: processPath
( bool is_backslash_in ,
  const string & current_drive_in ,
  const string & current_dir_in ,
  string & filedrive_out ,
  string & filedir_out ,
  string & filename_out )
{
  // -----
  // Filename check (2) ... if it starts with either "[A-z]:" or
  // a slash or a backslash, then it is assumed as the full path.
  // Otherwise, it is assumed as relative to the current working
  // directory. Uses PCRE. If in windows and full path, separate
  // the drive name.
  // ファイル名チェック(2)… もし "[A-z]:" またはスラッシュ・バック
  // スラッシュで始まる名称なら，フルパスと仮定する。そうでなければ
  // カレントディレクトリからの相対パスであると仮定する。PCRE 使用.
  // もしウィンドウズ環境下でフルパスなら，ドライブ名を分離。
  // -----
  bool is_relative_path = false ;
  string pcre_in = "^[/\\\\].*$" ;
  if ( is_backslash_in )
  {
    pcre_in = "^([A-z]:)(.*)$" ;
  }
  const char * error ;
  int erroffset ;
  pcre * re = pcre_compile ( pcre_in . c_str () ,
                             PCRE_UTF8 & PCRE_DOLLAR_ENDONLY ,
                             & error , & erroffset , NULL ) ;
  if ( re == NULL )
  {
    cerr << "Error: in RTSS_Path_String::processPath(...): "
         << "PCRE compilation failed at offset " << erroffset
         << ": " << error << endl ;
    exit ( 1 ) ;
  }
  int rc = pcre_exec ( re , NULL , c_str () , length () , 0 , 0 , ovector ,
                       RP_OVECCOUNT ) ;
  pcre_free ( re ) ;
  if ( rc == PCRE_ERROR_NOMATCH )
  {
    is_relative_path = true ;
  }
  else if ( rc < 0 )
  {
    cerr << "Error: in RTSS_Path_String::processPath(...): "
         << "PCRE match error." << endl ;
    exit ( 1 ) ;
  }
  else
  {
    is_relative_path = false ;
    if ( is_backslash_in )
    {
      // -----
      // Substring 1 = drive name, 2 = the rest.
      // Substring 1 = ドライブ名, 2 = のこり。
      // -----
      char buf [ PATH_MAX ] ;
      int subs_l = pcre_copy_substring
        ( c_str () , ovector , rc , 1 , buf , PATH_MAX ) ;
      filedrive_out = buf ;
      subs_l = pcre_copy_substring
        ( c_str () , ovector , rc , 2 , buf , PATH_MAX ) ;
      string :: operator= ( buf ) ;
    }
    else
    {
      filedrive_out = "" ;
    }
    if ( ( ! is_relative_path ) || is_backslash_in )
    {
      // -----
      // Check if directory name part starts with a (back)slash.
      // ディレクトリ名部分が(バック)スラッシュから始まることを確認。
      // -----
      pcre_in = "^([/\\\\])(.*)$" ;
      re = pcre_compile ( pcre_in . c_str () ,
                          PCRE_UTF8 & PCRE_DOLLAR_ENDONLY ,
                          & error , & erroffset , NULL ) ;
      if ( re == NULL )
      {
        cerr << "Error: in RTSS_Path_String::processPath(...): "
             << "PCRE compilation failed at offset " << erroffset
             << ": " << error << endl ;
        exit ( 2 ) ;
      }
      int rc = pcre_exec ( re , NULL , c_str () , length () , 0 , 0 ,
                           ovector , RP_OVECCOUNT ) ;
      pcre_free ( re ) ;
      if ( rc == PCRE_ERROR_NOMATCH )
      {
        if ( ! is_relative_path )
        {
          cerr << "Error: There must be a (back)slash after the drive name."
               << endl ;
          exit ( 2 ) ;
        }
      }
      else if ( rc < 0 )
      {
        cerr << "Error: in RTSS_Path_String::processPath(...): "
             << "PCRE match error." << endl ;
        exit ( 2 ) ;
      }
      else
      {
        filedrive_out = current_drive_in ;
        is_relative_path = false ;
      }
    }
  }
  // -----
  // Filename check (3) ... separate file name and directory name.
  // Uses PCRE.
  // ファイル名チェック(3)… ディレクトリ名とファイル名を分離。
  // PCRE 使用.
  // -----
  string fname_t ;
  if ( is_relative_path )
  {
    filedrive_out = current_drive_in ;
    fname_t = current_dir_in ;
    if ( is_backslash_in )
    {
      fname_t += "\\" ;
    }
    else
    {
      fname_t += "/" ;
    }
    fname_t += * this ;
  }
  else
  {
    fname_t = * this ;
  }
  bool not_finished = true ;
  vector < string > dstr_vec ;
  while ( not_finished )
  {
    pcre_in = "^[/\\\\]([^/\\\\]+)([/\\\\].*)$" ;
    re = pcre_compile ( pcre_in . c_str () ,
                        PCRE_UTF8 & PCRE_DOLLAR_ENDONLY ,
                        & error , & erroffset , NULL ) ;
    if ( re == NULL )
    {
      cerr << "Error: in RTSS_Path_String::processPath(...): "
           << "PCRE compilation failed at offset " << erroffset
           << ": " << error << endl ;
      exit ( 3 ) ;
    }
    int rc = pcre_exec
      ( re , NULL , fname_t . c_str () , fname_t . length () ,
        0 , 0 , ovector , RP_OVECCOUNT ) ;
    pcre_free ( re ) ;
    if ( rc == PCRE_ERROR_NOMATCH )
    {
      break ;
    }
    else if ( rc < 0 )
    {
      cerr << "Error: in RTSS_Path_String::processPath(...): "
           << "PCRE match error." << endl ;
      exit ( 3 ) ;
    }
    else
    {
      // -----
      // Substring 1 = dir entry, 2 = the rest.
      // Substring 1 = ディレクトリエントリ, 2 = のこり。
      // -----
      char buf [ PATH_MAX ] ;
      int subs_l = pcre_copy_substring
        ( fname_t . c_str () , ovector , rc , 1 , buf , PATH_MAX ) ;
      string dstr_pb = buf ;
      if ( dstr_pb == ".." )
      {
        dstr_vec . pop_back () ;
      }
      else if ( dstr_pb != "." )
      {
        dstr_vec . push_back ( dstr_pb ) ;
      }
      subs_l = pcre_copy_substring
        ( fname_t . c_str () , ovector , rc , 2 , buf , PATH_MAX ) ;
      fname_t = buf ;
    }
  }
  // -----
  // Stripping the first (back)slash character from fname_t, which
  // is the final filename.
  // fname_t 変数の最初にある(バック)スラッシュをとれば，これがファ
  // イル名となる。
  // -----
  pcre_in = "^[/\\\\]([^/\\\\]*)$" ;
  re = pcre_compile ( pcre_in . c_str () ,
                      PCRE_UTF8 & PCRE_DOLLAR_ENDONLY ,
                      & error , & erroffset , NULL ) ;
  if ( re == NULL )
  {
    cerr << "Error: in RTSS_Path_String::processPath(...): "
         << "PCRE compilation failed at offset " << erroffset
         << ": " << error << endl ;
    exit ( 4 ) ;
  }
  rc = pcre_exec ( re , NULL , fname_t . c_str () ,
                   fname_t . length () , 0 , 0 , ovector , RP_OVECCOUNT ) ;
  pcre_free ( re ) ;
  if ( rc == PCRE_ERROR_NOMATCH )
  {
    cerr << "Error: in RTSS_Path_String::processPath(...): "
         << "unknown error." << endl ;
    exit ( 4 ) ;
  }
  else if ( rc < 0 )
  {
    cerr << "Error: in RTSS_Path_String::processPath(...): "
         << "PCRE match error." << endl ;
    exit ( 4 ) ;
  }
  else
  {
    // -----
    // Substring 1 = file name.
    // Substring 1 = ファイル名。
    // -----
    char buf [ PATH_MAX ] ;
    int subs_l = pcre_copy_substring
      ( fname_t . c_str () , ovector , rc , 1 , buf , PATH_MAX ) ;
    filename_out = buf ;
  }
  // -----
  // Re-constructing the contents of dstr_vec into filedir.
  // dstr_vec 内の文字列を filedir 変数に再構築する。
  // -----
  filedir_out = "" ;
  for ( vector < string > :: const_iterator ii = dstr_vec . begin () ;
        ii < dstr_vec . end () ; ++ ii )
  {
    if ( is_backslash_in )
    {
      filedir_out += "\\" ;
    }
    else
    {
      filedir_out += "/" ;
    }
    filedir_out += * ii ;
  }

  return true ;
}
