--          This file is part of SmallEiffel The GNU Eiffel Compiler.
--          Copyright (C) 1994-98 LORIA - UHP - CRIN - INRIA - FRANCE
--            Dominique COLNET and Suzanne COLLIN - colnet@loria.fr 
--                       http://www.loria.fr/SmallEiffel
-- SmallEiffel is  free  software;  you can  redistribute it and/or modify it 
-- under the terms of the GNU General Public License as published by the Free
-- Software  Foundation;  either  version  2, or (at your option)  any  later 
-- version. SmallEiffel is distributed in the hope that it will be useful,but
-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-- or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU General Public License 
-- for  more  details.  You  should  have  received a copy of the GNU General 
-- Public  License  along  with  SmallEiffel;  see the file COPYING.  If not,
-- write to the  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-- Boston, MA 02111-1307, USA.
--
class SYSTEM_TOOLS
   --
   -- Singleton object to handle system dependant information.
   -- This singleton is shared via the GLOBALS.`system_tools' once function.
   --
   -- Only this object is supposed to handle contents of the `SmallEiffel' 
   -- system environment variable.
   --
   -- You may also want to customize this class in order to support a 
   -- new operating system (please let us know).
   --

inherit GLOBALS;

creation make

feature {NONE} -- Currently handled system List :
   
   amiga_system:       STRING is "Amiga";
   beos_system:        STRING is "BeOS";
   dos_system:         STRING is "DOS";
   macintosh_system:   STRING is "Macintosh";
   os2_system:         STRING is "OS2";
   unix_system:        STRING is "UNIX";
   vms_system:         STRING is "VMS";
   windows_system:     STRING is "Windows";

feature {NONE}

   system_list: ARRAY[STRING] is
      once
	 Result := << amiga_system,
		      beos_system, 
		      dos_system,
		      macintosh_system, 
		      os2_system, 
		      unix_system,
		      vms_system, 
		      windows_system
		      >>;
      end;

feature {NONE}

   system_name: STRING;

   sys_directory: STRING;
	 -- The SmallEiffel/sys directory computed with the value of
	 -- the environment variable `SmallEiffel'.
	 -- For example, under UNIX: "/usr/lib/SmallEiffel/sys/"

   bin_directory: STRING;
	 -- For example, under UNIX: "/usr/lib/SmallEiffel/bin/"
   
feature {NONE}
   
   make is
      local
	 system_se_path: STRING;
	 i: INTEGER;
      do
	 system_se_path := get_environment_variable(fz_se);
	 if system_se_path = Void then
	    system_se_path := fz_se.twin;
	    system_se_path.to_upper;
	    system_se_path := get_environment_variable(system_se_path);
	    if system_se_path = Void then
	       echo.put_string(
                  "System environment variable %"SmallEiffel%" not set.%N%
                  %Trying default value: %"");
	       system_se_path := "/usr/lib/SmallEiffel/sys/system.se";
	       echo.put_string(system_se_path);
	       echo.put_string(fz_03);
	    end;
	 end;
	 if system_se_path.has_suffix(fz_system_se) then
	    echo.sfr_connect(tmp_file_read,system_se_path);
	 else
	    echo.put_string(
               "You should update the value of the %"SmallEiffel%" %
               %system environment variable.%N%
               %Since release -0.79, the %"SmallEiffel%" system %
               %environment variable must be the absolute path of %
               %the %"system.se%" file.%N%
               %For example %"/usr/lib/SmallEiffel/sys/system.se%" %
               %under Unix like system.%N");
	    if system_se_path.has('/') then
	       echo.put_string("%"Hope this is a Unix like system.%N");
	       tmp_path.copy(system_se_path);
	       tmp_path.set_last('/');
	       tmp_path.append(fz_sys);
	       tmp_path.extend('/');
	       tmp_path.append(fz_system_se);
	       echo.sfr_connect(tmp_file_read,tmp_path);
	    end;
	    if not tmp_file_read.is_connected then
	       if system_se_path.has('\') then
		  echo.put_string("%"Hope this is a DOS or Windows like system.%N");
		  tmp_path.copy(system_se_path);
		  tmp_path.set_last('\');
		  tmp_path.append(fz_sys);
		  tmp_path.extend('\');
		  tmp_path.append(fz_system_se);
		  echo.sfr_connect(tmp_file_read,tmp_path);
	       end;
	    end;
	    if not tmp_file_read.is_connected then
	       if system_se_path.has(':') then
		  echo.put_string("%"Hope this is a Macintosh like system.%N");
		  tmp_path.copy(system_se_path);
		  tmp_path.set_last(':');
		  tmp_path.append(fz_sys);
		  tmp_path.extend(':');
		  tmp_path.append(fz_system_se);
		  echo.sfr_connect(tmp_file_read,tmp_path);
	       end;
	    end;
	    if not tmp_file_read.is_connected then
	       if system_se_path.has(']') then
		  echo.put_string("%"Hope this is a VMS system.%N");
		  tmp_path.copy(system_se_path);
		  tmp_path.set_last(']');
		  tmp_path.remove_last(1);
		  tmp_path.extend('.');
		  tmp_path.append(fz_sys);
		  tmp_path.extend(']');
		  tmp_path.append(fz_system_se);
		  echo.sfr_connect(tmp_file_read,tmp_path);
	       end;
	    end;
	    if not tmp_file_read.is_connected then
	       echo.put_string("%"Last chance.%N");
	       tmp_path.copy(system_se_path);
	       tmp_path.append(fz_system_se);
	       echo.sfr_connect(tmp_file_read,tmp_path);
	    end;
	 end;
	 if not tmp_file_read.is_connected then
	    echo.w_put_string(
               "Unable to find file %"system.se%".%N%
               %Please, set the environment variable %"SmallEiffel%" %
	       %with the appropriate absolute path to this file.%N%
	       %Example for Unix: %"/usr/lib/SmallEiffel/sys/system.se%"%N%
               %Example for DOS/Windows: %"C:\SmallEiffel\sys\system.se%"%N");
	    die_with_code(exit_failure_code);
	 end;
	 tmp_file_read.read_line;
	 system_name := tmp_file_read.last_string;
	 i := system_list.index_of(system_name);
	 if i > system_list.upper then
	    echo.w_put_string("Unknown system name in file%N%"");
	    echo.w_put_string(tmp_file_read.path);
	    echo.w_put_string("%".%NCurrently handled system names :%N");
	    from
	       i := 1;
	    until
	       i > system_list.upper
	    loop
	       echo.w_put_string(system_list.item(i));
	       echo.w_put_character('%N');
	       i := i + 1;
	    end;
	    die_with_code(exit_failure_code);
	 else
	    system_name := system_list.item(i);
	    echo.put_string("System is %"");
	    echo.put_string(system_name);
	    echo.put_string(fz_b0);
	 end;
	 sys_directory := tmp_file_read.path.twin;
	 sys_directory.remove_suffix(fz_system_se);
	 tmp_file_read.disconnect;
	 bin_directory := sys_directory.twin;
	 parent_directory(bin_directory);
	 add_directory(bin_directory,fz_bin);
      end;

feature {COMPILE}

   compile_to_c_in(command: STRING) is
	 -- Append the system command call
      do
	 if windows_system /= system_name then
	    command.append(bin_directory);
         end;
	 command.append(us_compile_to_c);
	 command.append(x_suffix);
      end;

feature {COMPILE}

   clean_in(command: STRING) is
      do
	 if windows_system /= system_name then
	    command.append(bin_directory);
	 end;
	 command.append(fz_clean);
	 command.append(x_suffix);
      end;

feature {C_PRETTY_PRINTER}

   first_line_of_sys_file(name: STRING): STRING is
	 -- Create a new STRING which is the contents of the first 
	 -- line of the corresponding SmallEiffel/sys/ directory.
      do
	 tmp_path.copy(sys_directory);
	 tmp_path.append(name);
	 tmp_path.append(system_name);
	 echo.sfr_connect_or_exit(tmp_file_read,tmp_path);
	 tmp_file_read.read_line;
	 Result := tmp_file_read.last_string.twin;
	 tmp_file_read.disconnect;
      end;

feature {C_PRETTY_PRINTER}

   sys_runtime(name: STRING; suffix: CHARACTER) is
	 -- Prepare `tmp_file_read' to access the corresponding file 
	 -- in the SmallEiffel/sys/runtime directory.
      require
	 name /= Void;
	 suffix = 'c' or suffix = 'h'
      do
	 tmp_path.copy(sys_directory);
	 add_directory(tmp_path,fz_runtime);
	 tmp_path.append(name);
	 tmp_path.extend('.');
	 tmp_path.extend(suffix);
	 echo.sfr_connect_or_exit(tmp_file_read,tmp_path);
      ensure
	 tmp_file_read.is_connected
      end;
   
feature {COMPILE,CLEAN}

   remove_make_script: STRING is
	 -- Compute the corresponding make file script and remove
	 -- the old one if any.
      do
	 Result := path_h;
	 Result.remove_suffix(h_suffix);
	 Result.append(make_suffix);
         echo.file_removing(Result);
      end;

feature {C_PRETTY_PRINTER}

   path_h: STRING is
	 -- Create a new STRING which is the name of the
	 -- main *.h file.
      do
	 Result := run_control.root_class.twin;
	 Result.to_lower;
	 if dos_system = system_name then
	    from
	    until
	       Result.count <= 4
	    loop
	       Result.remove_last(1);
	    end;
	 end;
	 Result.append(h_suffix);
      end;

feature {SMALL_EIFFEL}

   read_loading_path_in(lp: ARRAY[STRING]) is
      do
	 loading_path_add(lp,fz_loadpath_se,1);
	 tmp_path.copy(sys_directory);
	 tmp_path.append("loadpath.");
	 tmp_path.append(system_name);
	 loading_path_add(lp,tmp_path,1);
      end;

feature {NONE}

   fz_loadpath_se: STRING is "loadpath.se";

   loading_path_add(lp: ARRAY[STRING]; path: STRING; level: INTEGER) is
      local
	 file: STD_FILE_READ;
	 line: STRING;
      do
	 if level > 5 or else lp.count > 1024 then
	    echo.w_put_string(
               "Eiffel source loading path too long or infinite %
	       %loadpath.se includes.%N");
	    !!line.make(1024);
	    append_lp_in(line,lp);
	    echo.w_put_string(line);
	    die_with_code(exit_failure_code);
	 end;
	 !!file.make;
	 echo.sfr_connect(file,path);
	 if file.is_connected then
	    from
	       echo.put_string("Append contents of  %"");
	       echo.put_string(path);
	       echo.put_string("%" to loading path.%N");
	    until
	       file.end_of_input
	    loop
	       file.read_line;
	       line := file.last_string.twin;
	       if line.has_suffix(fz_loadpath_se) then
		  loading_path_add(lp,line,level + 1);
	       elseif not line.empty then
		  lp.add_last(line);
	       end;
	    end;
	    file.disconnect;
	 end;
      end;

feature {SMALL_EIFFEL}
   
   append_lp_in(str: STRING; lp: ARRAY[STRING]) is
      local      
	 i: INTEGER;
	 sed: STRING;
      do
	 str.append("%NLoading path is:%N[%N");
	 from  
	    i := lp.lower;
	 until
	    i > lp.upper 
	 loop
	    str.extend(' ');
	    str.extend('%"');
	    str.append(lp.item(i));
	    str.extend('%"');
	    str.extend('%N');
	    i := i + 1;
	 end;
	 str.append("]%NEnvironment Variable %"SmallEiffel%" is:%N");
	 sed := get_environment_variable(fz_se);
	 if sed = Void then
	    str.append("not set.%N");
	 else
	    str.append(" %"");
	    str.append(sed);
	    str.append("%".%N");
	 end;
      end;

feature {GC_HANDLER}

   put_mark_stack_and_registers is
	 -- Add customized assembly code to mark registers.
      local
	 architecture: STRING;
      do
	 tmp_path.copy(sys_directory);
	 tmp_path.append(fz_gc);
	 echo.sfr_connect_or_exit(tmp_file_read,tmp_path);
	 architecture := echo.read_word_in(tmp_file_read);
	 tmp_file_read.disconnect;
	 if us_none.is_equal(architecture) then
	    eh.append(
	       "Assembly Code for Garbage Collector not selected in %"");
	    eh.append(tmp_path);
	    eh.append(
               "%". Default generic (hazardous) C code is provided.");
	    eh.print_as_warning;
	    architecture := "generic.c";
	 end;
	 tmp_path.copy(sys_directory);
	 add_directory(tmp_path,fz_gc_lib);
	 tmp_path.append(architecture);
	 echo.sfr_connect_or_exit(tmp_file_read,tmp_path);
	 cpp.put_c_file(tmp_file_read);
      end;

feature {NONE}

   add_directory(path, dir: STRING) is
      require
	 path.count > 0;
	 dir.count > 0
      do
	 if slash_separator then
	    path.set_last('/');
	    path.append(dir);
	    path.set_last('/');
	 elseif backslash_separator then
	    path.set_last('\');
	    path.append(dir);
	    path.set_last('\');
	 elseif colon_separator then
	    path.set_last(':');
	    path.append(dir);
	    path.set_last(':');
	 elseif vms_system = system_name then
	    path.set_last(']');
	    path.remove_last(1);
	    path.set_last('.');
	    path.append(dir);
	    path.set_last(']');
	 end;
      end;

feature {NONE}

   parent_directory(path: STRING) is
	 -- Remove the last sub-directory of `path' (assume `path' is 
	 -- a combination of more than one directory). 
      require
	 path.count > 0
      do
	 if slash_separator then
	    from
	       path.remove_last(1);
	    until
	       path.last = '/'
	    loop
	       path.remove_last(1);
	    end;
	 elseif backslash_separator then
	    from
	       path.remove_last(1);
	    until
	       path.last = '\'
	    loop
	       path.remove_last(1);
	    end;
	 elseif colon_separator then
	    from
	       path.remove_last(1);
	    until
	       path.last = ':'
	    loop
	       path.remove_last(1);
	    end;
	 elseif vms_system = system_name then
	    from
	       path.remove_last(1);
	    until
	       path.last = '.'
	    loop
	       path.remove_last(1);
	    end;
	    path.remove_last(1);
	    path.extend(']');
	 end;
      ensure
	 path.count > 0;
	 path.count < (old path.count)
      end;

feature {NONE}

   slash_separator: BOOLEAN is
      do
	 if unix_system = system_name then
	    Result := true;
	 elseif amiga_system = system_name then
	    Result := true;
	 end;
      end;

feature {NONE}

   backslash_separator: BOOLEAN is
      do
	 if windows_system = system_name then
	    Result := true;
	 elseif dos_system = system_name then
	    Result := true;
	 elseif os2_system = system_name then
	    Result := true;
	 end;
      end;
      
feature {NONE}

   colon_separator: BOOLEAN is
      do
	 if macintosh_system = system_name then
	    Result := true;
	 elseif beos_system = system_name then
	    Result := true;
	 end;
      end;
      
feature {SHORT_PRINT}

   format_directory(format: STRING): STRING is
      require
	 format /= Void
      do
	 !!Result.make(sys_directory.count + 10);
	 Result.copy(sys_directory);
	 parent_directory(Result);
	 add_directory(Result,"short");
	 add_directory(Result,format);
      end;

feature

   bad_use_exit(command_name: STRING) is
      require
	 command_name /= Void
      do
	 echo.w_put_string("Bad use of command `");
	 echo.w_put_string(command_name);
	 echo.w_put_string("'.%N");
	 tmp_path.copy(sys_directory);
	 parent_directory(tmp_path);
	 add_directory(tmp_path,"man");
	 tmp_path.append(command_name);
	 tmp_path.append(help_suffix);
	 echo.w_put_string("See docmentation in file:%N   ");
	 echo.w_put_string(tmp_path);
	 echo.w_put_character('%N');
	 die_with_code(exit_failure_code);
      end;

feature {JVM}

   class_file_path(path, directory, class_file_name: STRING) is
	 -- Prepare `path' using `directory' and `class_file_name'.
      do
	 path.clear;
	 if vms_system = system_name then
	    if directory.first /= '[' then
	       path.extend('[');
	    end;
	 end;
	 path.append(directory);
	 if slash_separator then
	    path.set_last('/');
	 elseif backslash_separator then
	    path.set_last('\');
	 elseif colon_separator then
	    path.set_last(':');
	 elseif vms_system = system_name then
	    path.set_last(']');
	 end;
	 path.append(class_file_name);
	 path.append(class_suffix);
      end;

feature {C_PRETTY_PRINTER}

   put_c_main_function_type(out_c: STD_FILE_WRITE) is
      do
	 if vms_system = system_name then
	    out_c.put_string(fz_void);
	 else
	    out_c.put_string(fz_int);
	 end;
      end;

feature {C_PRETTY_PRINTER}

   strip_executable(command: STRING): BOOLEAN is
      require
	 command /= Void
      do
	 if unix_system = system_name then
	    Result := true;
	    command.copy("strip ");
	 elseif os2_system = system_name then
	    Result := true;
	    command.copy("emxbind -qs ");
	 end;	 
      end;
      
feature

   make_suffix: STRING is
      -- Suffix for make file produced by `compile_to_c'.
      once
	 if dos_system = system_name then
	    Result := ".BAT";
	 elseif windows_system = system_name then
	    Result := ".bat";
	 elseif vms_system = system_name then
	    Result := ".COM";
	 elseif os2_system = system_name then
	    Result := ".CMD";
	 else
	    Result := ".make";
	 end;
      end;

feature 

   x_suffix: STRING is 
	 -- Executable files suffix.
      once
	 if dos_system = system_name or else
	    vms_system = system_name
	  then
	    Result := ".EXE";
	 elseif os2_system = system_name then
	    Result := ".exe";
	 elseif windows_system = system_name then
	    Result := ".exe";
	 else
	    Result := "";
	 end;
      end;

feature

   o_suffix_memory: STRING is 
	 -- Object File produced by the C Compiler.
      once
	 !!Result.make(4);
	 tmp_path.copy(sys_directory);
	 tmp_path.append("o_suffix.");
	 tmp_path.append(system_name);
	 echo.sfr_connect_or_exit(tmp_file_read,tmp_path);
	 Result := echo.read_word_in(tmp_file_read);
	 tmp_file_read.disconnect;
      end;

feature {NONE}

   singleton_memory: SYSTEM_TOOLS is
      once
	 Result := Current;
      end;

invariant

   is_real_singleton: Current = singleton_memory
   
end -- SYSTEM_TOOLS
