--          This file is part of SmartEiffel The GNU Eiffel Compiler.
--       Copyright (C) 1994-2002 LORIA - INRIA - U.H.P. Nancy 1 - FRANCE
--          Dominique COLNET and Suzanne COLLIN - SmartEiffel@loria.fr
--                       http://SmartEiffel.loria.fr
-- SmartEiffel 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. SmartEiffel 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  SmartEiffel;  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 `SmartEiffel'
   -- 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, install

feature {NONE}

   -- Currently handled system list:
   unix_system:        STRING is "UNIX"
   windows_system:     STRING is "Windows"
   cygwin_system:      STRING is "Cygwin"
   beos_system:        STRING is "BeOS"
   macintosh_system:   STRING is "Macintosh"
   amiga_system:       STRING is "Amiga"
   dos_system:         STRING is "DOS"
   os2_system:         STRING is "OS2"
   open_vms_system:    STRING is "OpenVMS"
   elate_system:       STRING is "Elate"

   -- Currently handled C compiler list:
   gcc:                STRING is "gcc"
   gpp:                STRING is "g++"
   lcc_win32:          STRING is "lcc-win32"
   cc:                 STRING is "cc"
   wcl386:             STRING is "wcl386"
   bcc32:              STRING is "bcc32"
   cl:                 STRING is "cl"
   sas_c:              STRING is "sc"
   dice:               STRING is "dice"
   vbcc:               STRING is "vbcc"
   ccc:                STRING is "ccc"
   vpcc:               STRING is "vpcc"
   open_vms_cc:        STRING is "OpenVMS_CC"
   tcc:                STRING is "tcc"

feature {INSTALL}

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

   compiler_list: ARRAY[STRING] is
      once
         Result := <<
                     gcc,
                     lcc_win32,
                     cc,
                     wcl386,
                     bcc32,
                     cl,
                     sas_c,
                     dice,
                     vbcc,
                     ccc,
                     vpcc,
                     open_vms_cc,
		     tcc
                     >>
      end

   add_x_suffix(cmd: STRING) is
      local
         suffix: STRING
      do
         suffix := x_suffix
         if not cmd.has_suffix(suffix) then
            cmd.append(suffix)
         end
      end

   make is
      local
         system_se_path: STRING
         i: INTEGER
      do
         system_se_path := echo.getenv(fz_smarteiffel,Void)
         if system_se_path = Void then
            system_se_path := fz_smarteiffel.twin
            system_se_path.to_upper
            system_se_path := echo.getenv(system_se_path,Void)
            if system_se_path = Void then
               echo.put_string(
                  once "System environment variable %"SmartEiffel%" not set.%N%
                  %Trying default value: %"")
               system_se_path := "/usr/lib/SmartEiffel/sys/system.se"
               echo.put_string(system_se_path)
               echo.put_string(fz_03)
            end
         else
            echo.put_string(once "SmartEiffel=%"")
            echo.put_string(system_se_path)
            echo.put_string(fz_b0)
         end
         if system_se_path.has_suffix(fz_system_se) then
            echo.tfr_connect(tmp_file_read,system_se_path)
         else
            echo.put_string(
               "You should update the value of the %"SmartEiffel%" %
               %system environment variable.%N%
               %Since release -0.79, the %"SmartEiffel%" system %
               %environment variable must be the absolute path of %
               %the %"system.se%" file.%N%
               %For example %"/usr/lib/SmartEiffel/sys/system.se%" %
               %under Unix like system.%N")
            if system_se_path.has('/') then
               echo.put_string(once "Hope this is a Unix like system.%N")
               tmp_path.copy(system_se_path)
               tmp_path.extend_unless('/')
               tmp_path.append(fz_sys)
               tmp_path.extend('/')
               tmp_path.append(fz_system_se)
               echo.tfr_connect(tmp_file_read,tmp_path)
            end
            if not tmp_file_read.is_connected then
               if system_se_path.has('\') then
                  echo.put_string(once "Hope this is a Windows like system.%N")
                  tmp_path.copy(system_se_path)
                  tmp_path.extend_unless('\')
                  tmp_path.append(fz_sys)
                  tmp_path.extend('\')
                  tmp_path.append(fz_system_se)
                  echo.tfr_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(once "Hope this is a Macintosh like system.%N")
                  tmp_path.copy(system_se_path)
                  tmp_path.extend_unless(':')
                  tmp_path.append(fz_sys)
                  tmp_path.extend(':')
                  tmp_path.append(fz_system_se)
                  echo.tfr_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(once "Hope this is a VMS system.%N")
                  tmp_path.copy(system_se_path)
                  tmp_path.extend_unless(']')
                  tmp_path.remove_last(1)
                  tmp_path.extend('.')
                  tmp_path.append(fz_sys)
                  tmp_path.extend(']')
                  tmp_path.append(fz_system_se)
                  echo.tfr_connect(tmp_file_read,tmp_path)
               end
            end
            if not tmp_file_read.is_connected then
               echo.put_string(once "Last chance.%N")
               tmp_path.copy(system_se_path)
               tmp_path.append(fz_system_se)
               echo.tfr_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 %"SmartEiffel%" %
               %with the appropriate absolute path to this file.%N%
               %Example for Unix: %"/usr/lib/SmartEiffel/sys/system.se%"%N%
               %Example for DOS/Windows: %"C:\SmartEiffel\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(once "Unknown system name in file%N%"")
            echo.w_put_string(tmp_file_read.path)
            echo.w_put_string(once "%".%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(once "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)
         subdirectory(bin_directory,fz_bin)
      end

   system_name: STRING

feature {NONE}

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

   bin_directory: STRING
         -- For example, under UNIX: "/usr/lib/SmartEiffel/bin/"

   install is
      do
      end

feature {COMPILE,INSTALL}

   command_path_in(command, command_name: STRING) is
         -- Append in `command' the correct path for `command_name'.
      do
         if system_name = open_vms_system then
            command.append(once "mcr ")
         end
         command.append(bin_directory)
         command.append(command_name)
         command.append(x_suffix)
      end

   remove_other_extra_files(name: STRING) is
         -- Remove some other extra file which may be created while
         -- compiling `name' root class in split mode.
      local
         n: STRING
      do
         if c_compiler = lcc_win32 or else c_compiler = sas_c then
            n := name + lnk_suffix
            echo.file_removing(n)
         end
      end

feature {COMPILE}

   remove_make_script_and_other_extra_files is
         -- Remove the *.make file script and some other extra files.
      local
         name: STRING
      do
         name := new_make_script
         name.remove_suffix(make_suffix)
         remove_other_extra_files(name)
      end

   cygnus_bug(make_file: TEXT_FILE_READ; make_script_name: STRING) is
         -- Because of a bug in cygnus on windows 95/NT.
      local
         time_out: INTEGER
      do
         make_file.connect_to(make_script_name)
         from
            time_out := 2000
         until
            time_out = 0 or else make_file.is_connected
         loop
            make_file.connect_to(make_script_name)
            time_out := time_out - 1
         end
      end

feature {COMPILE,CLEAN}

   new_make_script: STRING is
         -- Compute the corresponding make file script name and remove
         -- the old existing one if any.
      do
         Result := path_h
         Result.remove_last(2)
         Result.append(make_suffix)
         echo.file_removing(Result)
      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.tfr_connect_or_exit(tmp_file_read,tmp_path)
         architecture := echo.read_word_in(tmp_file_read)
         tmp_file_read.disconnect
         if as_none.is_equal(architecture) then
            error_handler.append(
               "Assembly Code for Garbage Collector not selected in %"")
            error_handler.append(tmp_path)
            error_handler.append(
               "%". Default generic (hazardous) C code is provided. %
                % Have a look in the SmartEiffel FAQ.")
            error_handler.print_as_warning
            architecture := "generic.c"
         end
         tmp_path.copy(sys_directory)
         subdirectory(tmp_path,fz_gc_lib)
         tmp_path.append(architecture)
         echo.tfr_connect_or_exit(tmp_file_read,tmp_path)
         cpp.put_c_file(tmp_file_read)
      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)
         subdirectory(Result,once "short")
         subdirectory(Result,format)
      end

feature

   is_c_plus_plus_file_path(path: STRING): BOOLEAN is
         -- True when there `path' has one of the following
         -- suffix: ".cpp", ".cc", or ".C".
      do
         if path.has_suffix(c_plus_plus_suffix) then
            Result := true
         elseif path.has_suffix(once ".cc") then
            Result := true
         elseif path.has_suffix(once ".C") then
            Result := true
         end
      end

feature {CLUSTER,JVM,ACE}

   file_path(parent_path, file_name: STRING) is
         -- Use the `system_name' knowledge to call feature `compute_file_path_with'
         -- of BASIC_DIRECTORY. The `parent_path' is modified accordingly.
      do
         set_basic_directory_notation
         basic_directory.compute_file_path_with(parent_path,file_name)
         parent_path.copy(basic_directory.last_entry)
      end

feature

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

   x_suffix: STRING is
         -- Executable files suffix.
      once
         if dos_system = system_name then
            Result := exe_suffix
            Result.to_upper
         elseif open_vms_system = system_name then
            Result := exe_suffix
            Result.to_upper
         elseif os2_system = system_name then
            Result := exe_suffix
         elseif windows_system = system_name then
            Result := exe_suffix
         elseif cygwin_system = system_name then
            Result := exe_suffix
         elseif elate_system = system_name then
            Result := once ".00"
         else
            create Result.make(4)
         end
      ensure
         Result /= Void
      end

   object_suffix: STRING is
         -- Of object File produced by the C Compiler.
      once
         if c_compiler = gcc then
            Result := o_suffix
         elseif c_compiler = lcc_win32 then
            Result := obj_suffix
         elseif c_compiler = cc then
            Result := o_suffix
         elseif c_compiler = wcl386 then
            Result := obj_suffix
         elseif c_compiler = bcc32 then
            Result := obj_suffix
         elseif c_compiler = cl then
            Result := obj_suffix
         elseif c_compiler = sas_c then
            Result := o_suffix
         elseif c_compiler = dice then
            Result := o_suffix
         elseif c_compiler = vbcc then
            Result := o_suffix
         elseif c_compiler = ccc then
            Result := o_suffix
         elseif c_compiler = vpcc then
            Result := o_suffix
         elseif c_compiler = open_vms_cc then
            Result := obj_suffix
            Result.to_upper
         elseif c_compiler = tcc then
            Result := o_suffix
         end
      end

feature {NATIVE_SMART_EIFFEL}

   add_lib_math is
      once
         if beos_system = system_name then
         elseif c_compiler = gcc then
            add_external_lib(libm)
         --elseif c_compiler = bcc32 then
         --   add_external_lib(libm)
         elseif c_compiler = cl then
         elseif c_compiler = sas_c then
            -- math library is included automatically if
            -- "Math=..." was specified (as it is in
            -- default `sas_c_compiler_options')
         elseif c_compiler = dice then
            add_external_lib(libm)
         elseif c_compiler = vbcc then
            if amiga_system = system_name then
               add_external_lib(once "mieee")
            else
               add_external_lib(libm)
            end
         elseif c_compiler = ccc then
            add_external_lib(libcpml)
         elseif c_compiler = vpcc then
         elseif c_compiler = open_vms_cc then
         elseif c_compiler = tcc then
            add_external_lib(libm)
         end
      end

feature {COMPILE,COMPILE_TO_C}

   extra_arg(arg: STRING; argi: INTEGER; next_arg: STRING): INTEGER is
      require
         arg /= Void
         argi >= 1
      do
         if arg.item(1) /= '-' then
            if arg.has_suffix(object_suffix) then
               append_token(external_object_files,arg)
               Result := argi + 1
            elseif arg.has_suffix(c_suffix) then
               append_token(external_c_files,arg)
               Result := argi + 1
            elseif is_c_plus_plus_file_path(arg) then
               append_token(external_c_plus_plus_files,arg)
               Result := argi + 1
            elseif arg.has_suffix(once ".a") then
               add_external_lib(arg)
               Result := argi + 1
            elseif arg.has_suffix(once ".lib") then
               add_external_lib(arg)
               Result := argi + 1
            elseif arg.has_suffix(once ".res") then
               -- For lcc-win32 resource files:
               add_external_lib(arg)
               Result := argi + 1
            elseif ace.root_class_name = Void then
               ace.set_root_class_name_using(arg)
               Result := argi + 1
               if next_arg /= Void then
                  if next_arg.item(1) /= '-' then
                     if next_arg.has_suffix(object_suffix) then
                     elseif next_arg.has_suffix(c_suffix) then
                     elseif is_c_plus_plus_file_path(next_arg) then
                     else
                        ace.set_root_procedure_name(next_arg)
                        Result := argi + 2
                     end
                  end
               end
            else
               append_token(c_compiler_options,arg)
               Result := argi + 1
            end
         elseif arg.has_prefix(once "-l") then
            do_add_external_lib(arg)
            Result := argi + 1
         elseif arg.has_prefix(l_flag) then
            append_token(external_lib_path,arg)
            if (l_flag).is_equal(arg) then
               if next_arg /= Void then
                  append_token(external_lib_path,next_arg)
                  Result := argi + 2
               end
            else
               Result := argi + 1
            end
         elseif (once "-subsystem").is_equal(arg) then
            append_token(linker_options,arg)
            if next_arg /= Void then
               append_token(linker_options,next_arg)
               Result := argi + 2
            else
               Result := argi + 1
            end
         elseif (once "-include").is_equal(arg) then
            if next_arg /= Void then
               append_token(c_compiler_options,arg)
               append_token(c_compiler_options,next_arg)
               Result := argi + 2
            else
               Result := argi + 1
            end
         else
            append_token(c_compiler_options,arg)
            Result := argi + 1
         end
      ensure
         Result > old argi
      end

feature {ACE, COMMAND_LINE_TOOLS}

   bad_use_exit(command: COMMAND_LINE_TOOLS) is
         -- Print the traditional `command_line_help_summary' as well as some extra
         -- information to find more help.
      require
         command /= Void
      local
         command_name: STRING
      do
         echo.w_put_string("Bad use of command `")
         command_name := command.command_name
         echo.w_put_string(command_name)
         echo.w_put_string("'.%N")
         echo.w_put_string(command.command_line_help_summary)
         tmp_path.copy(sys_directory)
         parent_directory(tmp_path)
         subdirectory(tmp_path,"man")
         tmp_path.append(command_name)
         tmp_path.append(".txt")
         echo.w_put_string(
            "If SmartEiffel is correctly installed, you should find%N%
            %more information in file: %"")
         echo.w_put_string(tmp_path)
         echo.w_put_string(
            "%".%NYou can also have a look at http://SmartEiffel.loria.fr%N")
         die_with_code(exit_failure_code)
      end

   set_c_compiler(cc_arg: STRING) is
         -- If `cc_arg' is not Void, this `cc_arg' is used as the C compiler.
         -- Otherwise, when `cc_arg' is Void, read the selected one in the
         -- "SmartEiffel/sys/compiler.se" file.
      local
         i, j: INTEGER; lib_name: STRING;
      do
         if cc_arg /= Void then
            i := compiler_list.index_of(cc_arg)
            if i > compiler_list.upper then
               echo.w_put_string(once "compile_to_c: %"")
               echo.w_put_string(cc_arg)
               echo.w_put_string(
               once "%": unknown C compiler name after -cc flag or in the ACE file.%N")
               show_compiler_list_then_exit
            end
            c_compiler := compiler_list.item(i)
         else
            tmp_path.copy(sys_directory)
            tmp_path.append(once "compiler.se")
            echo.tfr_connect_or_exit(tmp_file_read,tmp_path)
            c_compiler := echo.read_word_in(tmp_file_read)
            i := compiler_list.index_of(c_compiler)
            if i > compiler_list.upper then
               echo.w_put_string(once "Unknown compiler name in file%N%"")
               echo.w_put_string(tmp_file_read.path)
               echo.w_put_string(fz_b0)
               show_compiler_list_then_exit
            end
            c_compiler := compiler_list.item(i)
            if not tmp_file_read.end_of_input then
	       tmp_file_read.read_line_in(c_compiler_options)
            end
            tmp_file_read.disconnect
            from
            until c_compiler_options.is_empty or else
               not c_compiler_options.first.is_separator
            loop
               c_compiler_options.remove_first(1)
            end

	    -- Extract additionnal libs from c_compiler_options
	    if c_compiler = lcc_win32 or else c_compiler = cl 
	       or else c_compiler = bcc32 then
	       -- detect ".lib"
	       from
		  i := c_compiler_options.substring_index(once ".lib", 1)
	       until
		  i = 0
	       loop
		  if (i + 4 > c_compiler_options.count) or else
		     c_compiler_options.item(i + 4).is_separator then
		     from
			j := i
		     until
			j=1 or else c_compiler_options.item(j-1).is_separator
		     loop
			j := j - 1
		     end
		     lib_name := c_compiler_options.twin
		     lib_name.shrink(j, i + 3)
		     add_external_lib(lib_name)
		     c_compiler_options.remove_between(j, i + 3)
		     i := j
		  else
		     i := i + 4
		  end
		  i := c_compiler_options.substring_index(once ".lib", i)
	       end
	    else
	       -- detect "-l"
	       from
		  i := c_compiler_options.substring_index(once "-l", 1)
	       until
		  i = 0
	       loop
		  if (i = 1) or else
		     c_compiler_options.item(i - 1).is_separator then
		     from
			j := i
		     until
			j = c_compiler_options.count or else
			c_compiler_options.item(j + 1).is_separator
		     loop
			j := j + 1
		     end
		     lib_name := c_compiler_options.twin
		     lib_name.shrink(i, j)
		     add_external_lib(lib_name)
		     c_compiler_options.remove_between(i, j)
		  else
		     i := i + 2
		  end
		  i := c_compiler_options.substring_index(once ".lib", i)
	       end
	    end
	 end
      ensure
         compiler_list.fast_has(c_compiler)
      end

feature {ACE, COMPILE_TO_C}

   set_no_strip is
      do
         no_strip := true
      end

feature {ACE,C_PRETTY_PRINTER}

   c_compiler_options: STRING is ""
         -- C compiler options including extra include path,
         -- optimization flags, etc.

feature {C_PRETTY_PRINTER}

   put_c_main_function_type(out_c: TEXT_FILE_WRITE) is
      do
         if open_vms_system = system_name then
            out_c.put_string(fz_void)
         else
            out_c.put_string(fz_int)
         end
      end

   put_c_main_function_exit(out_c: TEXT_FILE_WRITE) is
      do
         out_c.put_string(once "}%Nexit(0);%N")
         if open_vms_system = system_name then
            out_c.put_string(once "return;}%N")
         else
            out_c.put_string(once "return 0;}%N")
         end
      end

   sys_runtime(name: STRING; suffix: CHARACTER) is
         -- Prepare `tmp_file_read' to access the corresponding file of the
         -- SmartEiffel sys/runtime directory. The complete path is
         -- always available in `tmp_path' to the caller in order to emit an
         -- error message when the corresponding file is not found. (This
         -- routine does not emit errors message itself just because this
         -- may be normal. See also `mandatory_sys_runtime'.)
      require
         name /= Void
         suffix = 'c' or suffix = 'h'
      do
         sys_runtime_(name,suffix)
         echo.tfr_connect(tmp_file_read,tmp_path)
      end

   mandatory_sys_runtime(name: STRING; suffix: CHARACTER) is
         -- Same as `sys_runtime', but emit an error message when the
         -- corresponding file does not exists.
      do
         sys_runtime_(name,suffix)
         echo.tfr_connect_or_exit(tmp_file_read,tmp_path)
      ensure
         tmp_file_read.is_connected
      end

   path_h: STRING is
         -- Create a new STRING which is the name of the main *.h file.
      do
         Result := ace.root_class_name.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

   strip_executable(cmd: STRING): BOOLEAN is
      local
         executable_name: STRING
      do
         cmd.clear
         if not no_strip
	    and c_compiler /= tcc then -- *** Probably not the good way to exclude strip when tcc. How should it be done ?
            executable_name := ace.executable_name
            if unix_system = system_name then
               Result := true
               cmd.append(once "strip ")
               if executable_name = Void then
                  cmd.append(once "a.out")
               else
                  cmd.append(executable_name)
               end
            elseif os2_system = system_name then
               Result := true
               cmd.append(once "emxbind -qs ")
               if executable_name = Void then
                  cmd.append(once "a.exe")
               else
                  cmd.append(executable_name)
               end
            end
         end
      end

   add_c_plus_plus_file(f: STRING) is
      require
         is_c_plus_plus_file_path(f)
      do
         append_token(external_c_plus_plus_files,f)
      end

   is_linking_mandatory: BOOLEAN is
         -- Is it mandatory to link again this executable even when
         -- nothing has changed in the generated C code ?
      do
         Result := not external_object_files.is_empty
         if not Result then
            Result := not external_c_files.is_empty
         end
         if not Result then
            Result := not external_c_plus_plus_files.is_empty
         end
      end

   add_lib_basic_picasso is
      once
         if system_name = unix_system then
            add_external_lib_path("/usr/X11R6/lib")
            add_external_lib_path("/usr/X/lib")
            add_external_lib("X11")
	 elseif system_name = windows_system then
	    if c_compiler = bcc32 then
	       add_external_lib("import32.lib")
	    end
            add_external_lib("user32.lib")
            add_external_lib("gdi32.lib")
            add_external_lib("wsock32.lib")
            add_external_lib("winmm.lib")
	 elseif system_name = cygwin_system and then c_compiler = gcc then
	    add_external_lib_path("/usr/w32api")
	    add_external_lib("user32")
	    add_external_lib("gdi32")
	    add_external_lib("wsock32")
	    add_external_lib("winmm")
         end
      end

feature {SMART_EIFFEL}

   add_lib_scoop is
      once
         add_external_lib(once "pthread")
      end

feature {C_PRETTY_PRINTER,INSTALL}

   split_mode_c_compiler_command(cmd, c_file_name: STRING) is
         -- Where c_file_name is the name of one slice.
      do
         cmd.clear
         if c_compiler = gcc then
            if is_c_plus_plus_file_path(c_file_name) then
               cmd.append(gpp)
            else
               cmd.append(gcc)
            end
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = lcc_win32 then
            cmd.append(lcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_file_name)
         elseif c_compiler = cc then
            cmd.append(cc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = wcl386 then
            cmd.append(once "wcc386")
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_file_name)
         elseif c_compiler = bcc32 then
            cmd.append(bcc32)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = cl then
            cmd.append(cl)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = sas_c then
            cmd.append(sas_c)
            append_token(cmd,sas_c_compiler_options(true))
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_file_name)
         elseif c_compiler = dice then
            cmd.append(dcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = vbcc then
            cmd.append(vc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = ccc then
            cmd.append(ccc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = vpcc then
            cmd.append(vpcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
            append_token(cmd,c_file_name)
         elseif c_compiler = open_vms_cc then
            -- BdB: external_header_path to be added here somehow
            cmd.append(once "cc/warning=disable=(embedcomment,longextern) ")
            cmd.append(c_file_name)
            cmd.remove_last(2)
         elseif c_compiler = tcc then
            cmd.append(tcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_flag)
	    append_token(cmd,o_flag)
	    append_token(cmd,c_file_name)
	    cmd.remove_last(2) -- *** BAD STYLE
	    cmd.append(object_suffix)
            append_token(cmd,c_file_name)
         end
      end

   split_mode_linker_command(cmd, c_name: STRING; max: INTEGER) is
         -- Where `c_name' is only the prefix name (i.e. "compile_to_c").
      local
         script, lst: TEXT_FILE_WRITE; i: INTEGER; name: STRING
      do
         cmd.clear
         if c_compiler = gcc then
            if external_c_plus_plus_files.is_empty then
               cmd.append(gcc)
            else
               cmd.append(gpp)
            end
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = lcc_win32 then
            external_c_files_for_lcc_win32(cmd)
            cmd.append(lcclnk)
            if not no_strip then
               append_token(cmd,s_flag)
            end
            append_token(cmd,linker_options)
            add_executable_name(cmd)
            if max < 4 then
               -- Object files are passed in the command line:
               add_objects(cmd,c_name,max)
            else
               -- Object files are listed in a *.lnk file:
               name := c_name
               name.append(lnk_suffix)
               append_token(cmd, "@" + name)
               create lst.make
               echo.tfw_connect(lst,name)
               from
                  name.remove_last(4)
                  i := 1
               until
                  i > max
               loop
                  lst.put_string(name)
                  lst.put_integer(i)
                  lst.put_string(obj_suffix)
                  lst.put_new_line
                  i := i + 1
               end
               lst.disconnect
            end
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = cc then
            cmd.append(cc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = wcl386 then
            cmd.append(once "wlink")
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = bcc32 then
            cmd.append(bcc32)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
            add_lib_math
         elseif c_compiler = cl then
            cmd.append(cl)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,once " /link")
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            append_token(cmd,external_lib)
            add_lib_math
         elseif c_compiler = sas_c then
            cmd.append(sas_c)
            append_token(cmd,sas_c_compiler_options(true))
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,c_name)
            cmd.append(once "#1#2#3#4#5#6#7#8#9#?.o")
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
            add_executable_name(cmd)
            if not no_strip then
               cmd.append(once " StripDebug")
            end
         elseif c_compiler = dice then
            cmd.append(dcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
            if no_strip then
               -- no typo; "-s" means "include symbol table",
               -- not "strip debug information"
               append_token(cmd,once "-s -d1")
            end
            add_lib_math
         elseif c_compiler = vbcc then
            cmd.append(vc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_lib_math
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = ccc then
            cmd.append(ccc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = vpcc then
            cmd.append(vpcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = open_vms_cc then
            create script.make
            echo.tfw_connect(script,once "linkit.com")
            script.put_string(once "$ link/exe=")
            cmd.clear
            add_executable_name(cmd)
            script.put_string(cmd)
            script.put_character(' ')
            from
               i := 1
            until
               i >= max
            loop
               script.put_string(c_name)
               script.put_integer(i)
               script.put_string(once ",-%N")
               i := i + 1
            end
            script.put_string(c_name)
            script.put_integer(i)
            script.put_character('%N')
            script.disconnect
            cmd.clear
            cmd.append(once "@linkit.com%Ndelete linkit.com;")
         elseif c_compiler = tcc then
	    cmd.append(tcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,external_lib)
            add_objects(cmd,c_name,max)
            append_token(cmd,external_c_files)
            append_token(cmd,external_object_files)
         end
      end

   no_split_mode_command(cmd, c_file_name: STRING) is
      require
         c_file_name.has_suffix(once ".c")
      do
         cmd.clear
         if c_compiler = gcc then
            if external_c_plus_plus_files.is_empty then
               cmd.append(gcc)
            else
               cmd.append(gpp)
            end
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = lcc_win32 then
            cmd.append(lcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,c_file_name)
            cmd.extend('%N')
            external_c_files_for_lcc_win32(cmd)
            cmd.append(lcclnk)
            if not no_strip then
               append_token(cmd,s_flag)
            end
            append_token(cmd,linker_options)
            add_executable_name(cmd)
            c_file_name.remove_suffix(c_suffix)
            c_file_name.append(object_suffix)
            append_token(cmd,c_file_name)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = cc then
            cmd.append(cc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = wcl386 then
            cmd.append(wcl386)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = bcc32 then
            cmd.append(bcc32)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
            add_lib_math
         elseif c_compiler = cl then
            cmd.append(cl)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,once " /link")
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            append_token(cmd,external_lib)
            add_lib_math
         elseif c_compiler = sas_c then
            cmd.append(sas_c)
            append_token(cmd,sas_c_compiler_options(false))
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
            add_lib_math
            add_executable_name(cmd)
         elseif c_compiler = dice then
            cmd.append(dcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
            add_lib_math
            if no_strip then
               append_token(cmd,once "-s")
            end
         elseif c_compiler = vbcc then
            cmd.append(vc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            add_lib_math
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = ccc then
            cmd.append(ccc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = vpcc then
            cmd.append(vpcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_c_plus_plus_files)
            append_token(cmd,external_object_files)
            append_token(cmd,external_lib)
         elseif c_compiler = open_vms_cc then
            cmd.append(once "cc/warning=disable=(embedcomment,longextern) ")
            cmd.append(c_file_name)
            cmd.remove_last(2)
            cmd.append(once "%Nlink/exe=")
            add_executable_name(cmd)
            cmd.extend(' ')
            cmd.append(c_file_name)
            cmd.remove_last(2)
         elseif c_compiler = tcc then
	    cmd.append(tcc)
            append_token(cmd,c_compiler_options)
            append_token(cmd,external_header_path)
            append_token(cmd,linker_options)
            append_token(cmd,external_lib_path)
            add_executable_name(cmd)
            append_token(cmd,external_lib)
            append_token(cmd,c_file_name)
            append_token(cmd,external_c_files)
            append_token(cmd,external_object_files)
         end
      end

feature {NONE} -- SAS/c support functions:

   Scoptions_exists: BOOLEAN is
         -- Is there a file "SCOPTIONS" in the current directory?
      once
         Result := file_tools.is_readable(once "SCOPTIONS")
      end

   sas_c_compiler_options(split: BOOLEAN): STRING is
         -- C compiler options or "" if no SCOPTIONS exists.
         -- If `split' is True, "Data=Far" is used, otherwise
         -- "Data=Auto".
      do
         if Scoptions_exists then
            Result := once ""
         else
            !!Result.make(0)
            Result.append(once "Math=IEEE Parameters=Both Code=Far")
            -- cause bloat, but avoid linker errors
            if split then
               Result.append(once " Data=Far")
            else
               Result.append(once " Data=Auto")
            end
            Result.append(once " Ignore=93,194,304")
            -- ignore the following warnings:
            --  93: no reference to identifier X
            -- 194: too much local data for NEAR reference,
            --      some changed to FAR (only with Data=Auto)
            -- 304: dead assignment eliminated
            Result.append(once " NoVersion NoIcons")
            -- avoid cluttering the display with messages and
            -- the current directory with icon files
         end
      end

feature {ACE}

   read_loadpath_files is
         -- For command line mode only (no ACE file used). Read the good
         -- old "loadpath.se" file as well as the "loadpath.<system>" file
         -- to drive class file searching while in plain command line mode.
      require
         ace.file_path = Void
      do
         loadpath_read(fz_loadpath_se,1)
         tmp_path.copy(sys_directory)
         tmp_path.append(once "loadpath.")
         tmp_path.append(system_name)
         loadpath_read(tmp_path,1)
      end

   system_name_in(msg: STRING) is
         -- Append in `msg' the current `system_name'.
      do
         if system_name /= Void then
            msg.append(once "The selected system name is %"")
            msg.append(system_name)
            msg.append(fz_b0)
         else
            msg.append(once "No information available about the system used %
                       %(check your%NSmartEiffel installation).%N")
         end
      end

   set_external_header_path(a_paths: STRING) is
         -- Set `external_header_path' to `a_paths', but format it according to rules
         -- for current C compiler.
      local
         paths: ARRAY[STRING]; i: INTEGER
      do
         paths := a_paths.split
         from
            i := paths.lower
         until
            i > paths.upper
         loop
            add_external_header_path(paths.item(i))
            i := i + 1
         end
      end

   set_external_lib_path(a_paths: STRING) is
         -- Set `external_lib_path' to `a_paths', but format it according to rules for
         -- current C compiler and linker.
      local
         lib_paths: ARRAY[STRING]; i: INTEGER
      do
         lib_paths := a_paths.split
         from
            i := lib_paths.lower
         until
            i > lib_paths.upper
         loop
            add_external_lib_path(lib_paths.item(i))
            i := i + 1
         end
      end

   set_external_lib(a_libs: STRING) is
         -- Set `external_lib' to `a_libs', but format it according to rules for
         -- current C compiler and linker.
      local
         lib_files: ARRAY[STRING]; i: INTEGER
      do
         lib_files := a_libs.split
         from
            i := lib_files.lower
         until
            i > lib_files.upper
         loop
            add_external_lib(lib_files.item(i))
            i := i + 1
         end
      end

   external_object_files: STRING is ""
         -- External object files.

   external_c_files: STRING is ""
         -- External C files.

   linker_options: STRING is ""
         -- Those options are only to be passed to the linker.

   external_c_plus_plus_files: STRING is ""
         -- External C++ files.

   no_strip: BOOLEAN

feature {ECHO}

   parent_directory(some_path: STRING) is
         -- Use the `system_name' knowledge to call feature `compute_parent_directory_of'
         -- of BASIC_DIRECTORY. The `some_path' is modified accordingly.
      require
         some_path.count > 0
      do
         set_basic_directory_notation
         basic_directory.compute_parent_directory_of(some_path)
         some_path.copy(basic_directory.last_entry)
      end

feature {CLUSTER}

   cluster_path(directory_path: STRING) is
      require
         directory_path /= Void
      local
         s: STRING
      do
         s := system_name
         check
            s /= Void
         end
         if s = unix_system then
            directory_path.replace_all('\','/')
         elseif s = windows_system or else s = dos_system or else s = os2_system
          then
            directory_path.replace_all('/','\')
         else
            -- Do nothing for other operating systems :-(
         end
      end

feature {NONE}

   basic_directory: BASIC_DIRECTORY

   set_basic_directory_notation is
      local
         s: STRING; letter_code: CHARACTER
      once
         s := system_name
         if s = Void then
         elseif s = unix_system then
            letter_code := 'U'
         elseif s = windows_system or else s = dos_system or else s = os2_system then
            letter_code := 'W'
         elseif s = beos_system then
            letter_code := 'U'
         elseif s = macintosh_system then
            letter_code := 'M'
         elseif s = amiga_system then
            letter_code := 'A'
         elseif s = open_vms_system then
            letter_code := 'V'
         elseif s = cygwin_system then
            letter_code := 'C'
         elseif s = elate_system then
            letter_code := 'U'
         else
            check false end
         end
         if s /= Void then
            basic_directory.notation.put(letter_code,1)
         end
      end

   c_compiler: STRING
         -- One item of `compiler_list'.

   subdirectory(parent_path, entry_name: STRING) is
         -- Use the `system_name' knowledge to call feature `compute_subdirectory_with'
         -- of BASIC_DIRECTORY. The `parent_path' is modified accordingly.
      require
         not parent_path.is_empty
         not entry_name.is_empty
      do
         set_basic_directory_notation
         basic_directory.compute_subdirectory_with(parent_path,entry_name)
         parent_path.copy(basic_directory.last_entry)
      end

   append_token(line, token: STRING) is
      do
         if not token.is_empty then
            if token.first /= ' ' then
               if not line.is_empty then
                  line.extend_unless(' ')
               end
            end
            line.append(token)
         end
      end

   loadpath_read(path: STRING; level: INTEGER) is
      local
         file: TEXT_FILE_READ
         buffer: STRING
      do
         if level > 5 or else ace.cluster_count > 1024 then
            echo.w_put_string(
               "Eiffel source loading path too long or infinite %
               %loadpath.se includes.%N")
            !!buffer.make(5000)
            ace.view_in(buffer)
            echo.w_put_string(buffer)
            die_with_code(exit_failure_code)
         end
         !!file.make
         echo.tfr_connect(file,path)
         if file.is_connected then
            from
               echo.put_string(once "Append contents of  %"")
               echo.put_string(path)
               echo.put_string(once "%" to loading path.%N")
            until
               file.end_of_input
            loop
               file.read_line
               buffer := file.last_string.twin
               environment_variable_substitution(path,buffer)
               if buffer.has_suffix(fz_loadpath_se) then
                  loadpath_read(buffer,level + 1)
               elseif buffer.is_empty then
                  if not file.end_of_input then
                     ace.cluster_add_last(buffer)
                  end
               else
                  ace.cluster_add_last(buffer)
               end
            end
            file.disconnect
         end
      end

   external_c_files_for_lcc_win32(cmd: STRING) is
         -- Because lcc_win32 does not accept *.c file while linking as
         -- other C compiler do :-(
      local
         c_files: ARRAY[STRING]; c_file: STRING; i: INTEGER
      do
         if not external_c_files.is_empty then
            c_files := external_c_files.split
            external_c_files.clear
            if c_files /= Void then
               from
                  i := c_files.lower
               until
                  i > c_files.upper
               loop
                  c_file := c_files.item(i)
                  cmd.append(lcc)
                  append_token(cmd,c_compiler_options)
                  append_token(cmd,c_file)
                  cmd.extend('%N')
                  c_file.remove_suffix(c_suffix)
                  c_file.append(object_suffix)
                  append_token(external_object_files,c_file)
                  i := i + 1
               end
            end
         end
      end

feature {ID_PROVIDER}

   id_file_path: STRING is
      once
         Result := path_h
         Result.remove_suffix(h_suffix)
         Result.append(once ".id")
      end

feature {C_PRETTY_PRINTER, INSTALL}

   default_c_compiler_options(running_install:  BOOLEAN) is
	 -- Setting of the default `c_compiler_options'.
	 -- The  `running_install' flag means that we are C compiling 
	 -- tools of SmartEiffel (compile, compile_to_c, etc).
      do
         check
            c_compiler /= Void
            -- This ought to be in a require clause, but `c_compiler' has 
            -- access restrictions
         end
	 if gcc = c_compiler then
	    if running_install then
	       append_token(c_compiler_options, o2)
	    end
	 elseif gpp = c_compiler then
	    if running_install then
	       append_token(c_compiler_options, o2)
	    end
	 elseif lcc_win32 = c_compiler then
	    if not c_compiler_options.has('O') then
	       append_token(c_compiler_options, once "-O")
	    end
	 elseif cc = c_compiler then
	 elseif wcl386 = c_compiler then
	 elseif bcc32 = c_compiler then
	    if c_compiler_options.is_empty then
	       c_compiler_options.copy(
                  once "-5 -w-aus -w-par -w-rvl -O2 -O-v")
	    end
	 elseif cl = c_compiler then
	    if c_compiler_options.is_empty then
	       c_compiler_options.copy(once "-O2 -nologo")
	    end
	 elseif sas_c = c_compiler then
	    if running_install then
	       if not Scoptions_exists then
		  append_token(c_compiler_options,
                               once "Optimize OptimizerTime")
	       end
	    else
	       c_compiler_options.clear
	       linker_options.copy(once "Link")
	       if not Scoptions_exists then
		  append_token(linker_options, once "SmallCode SmallData")
	       end
	    end
	 elseif dice = c_compiler then
	    if c_compiler_options.is_empty then
	       c_compiler_options.copy(once "-mD -mC")
	    end
	 elseif vbcc = c_compiler then
	    if c_compiler_options.is_empty then
	       if amiga_system = system_name then
		  c_compiler_options.copy(once "-DAMIGA")
	       end
	    end
	 elseif ccc = c_compiler then
	    if c_compiler_options.is_empty then
	       c_compiler_options.copy(o2)
	    end
	 elseif vpcc = c_compiler then
	 elseif open_vms_cc = c_compiler then
	 elseif tcc = c_compiler then
	 else
	    check false end
	 end
      end

feature {NONE}

   external_header_path: STRING is ""
         -- Additional paths where .h files can be found.

   external_lib_path: STRING is ""
         -- External libraries path to be added at link time.

   external_lib: STRING is ""
         -- External libraries to be added at link time.

   do_add_external_lib(a_lib: STRING) is
         -- Low level function which appends a library to
         -- `external_lib' if it is not already in
         -- `external_lib'. `a_lib' should be formatted for the
         -- current linker.
      do
         if not external_lib.has_substring(a_lib) then
            append_token(external_lib,a_lib)
         end
      end

   sys_runtime_(name: STRING; suffix: CHARACTER) is
         -- Common part to implement `sys_runtime' and `mandatory_sys_runtime'.
      require
         name /= Void
         suffix = 'c' or suffix = 'h'
      do
         tmp_path.copy(sys_directory)
         subdirectory(tmp_path,fz_runtime)
         tmp_path.append(name)
         tmp_path.extend('.')
         tmp_path.extend(suffix)
      end

   environment_variable_substitution(path, line: STRING) is
         -- If any, substitute some environment variable by it's value.
         -- The only one accepted notation is :
         --                                        ${...}
      local
         i, state, mem1, mem2: INTEGER
         c: CHARACTER
         value, variable: STRING
      do
         from
            i := 1
         until
            i > line.count
         loop
            c := line.item(i)
            inspect
               state
            when 0 then -- Initial state.
               if c = '$' then
                  state := 1
                  mem1 := i
               end
            when 1 then -- "$" read.
               if c = '{' then
                  state := 2
                  !!variable.make(8)
               else
                  state := 0
               end
            when 2 then -- "${" read.
               if c = '}' then
                  state := 3
                  mem2 := i
               else
                  variable.extend(c)
               end
            else -- First correct variable found.
            end
            i := i + 1
         end
         if state = 3 then
            value := echo.getenv(variable,path)
            if value /= Void then
               variable.copy(line)
               line.keep_head(mem1 - 1)
               line.append(value)
               variable.remove_first(mem2)
               line.append(variable)
               environment_variable_substitution(path,line)
            end
         end
      end

   show_compiler_list_then_exit is
      local
         i: INTEGER
      do
         echo.w_put_string(once "Currently handled compiler names:%N")
         from
            i := 1
         until
            i > compiler_list.upper
         loop
            echo.w_put_string(compiler_list.item(i))
            echo.w_put_character('%N')
            i := i + 1
         end
         die_with_code(exit_failure_code)
      end

   add_executable_name(cmd: STRING) is
      local
         executable_name: STRING
      do
         executable_name := ace.executable_name
         if executable_name = Void then
            executable_name := ace.root_class_name.twin
            executable_name.to_lower
	    -- *** Why not end the if here ?
            if c_compiler = lcc_win32 then
               append_token(cmd,o_flag)
               append_token(cmd,executable_name)
               add_x_suffix(cmd)
            elseif c_compiler = bcc32 then
               append_token(cmd,e_flag)
               cmd.append(executable_name)
               add_x_suffix(cmd)
            elseif c_compiler = wcl386 then
               append_token(cmd,o_flag)
               cmd.append(executable_name)
               add_x_suffix(cmd)
            elseif c_compiler = sas_c then
               executable_name := ace.root_class_name.twin
               executable_name.to_lower
               cmd.append(fz_to_)
               cmd.append(executable_name)
            elseif c_compiler = dice then
               append_token(cmd,o_flag)
               cmd.append(executable_name)
            elseif c_compiler = vbcc then
               append_token(cmd,o_flag)
               cmd.append(executable_name)
            elseif c_compiler = vpcc then
               append_token(cmd,o_flag)
               cmd.append(executable_name)
            elseif c_compiler = open_vms_cc then
               cmd.append(executable_name)
            elseif c_compiler = tcc then
	       if system_name = unix_system then
		  append_token(cmd,o_flag)
		  append_token(cmd,once "a.out")
	       else
		  append_token(cmd,o_flag)
		  append_token(cmd,executable_name)
		  add_x_suffix(cmd)
	       end
            end
         elseif c_compiler = gcc then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = lcc_win32 then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = cc then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = wcl386 then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = bcc32 then
            append_token(cmd,e_flag)
            cmd.append(executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = cl then
            append_token(cmd,o_flag)
            cmd.append(executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = sas_c then
            cmd.append(fz_to_)
            append_token(cmd,executable_name)
         elseif c_compiler = dice then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = vbcc then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = ccc then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = vpcc then
            append_token(cmd,o_flag)
            append_token(cmd,executable_name)
            add_x_suffix(cmd)
         elseif c_compiler = open_vms_cc then
            cmd.append(executable_name)
	 elseif c_compiler = tcc then
	    append_token(cmd,o_flag)
	    append_token(cmd,executable_name)
	    add_x_suffix(cmd)
         end
      end

   add_objects(cmd, c_name: STRING; max: INTEGER) is
         -- Add objects files numbered from 1 to `max' included.
      local
         i: INTEGER; short_command: BOOLEAN
      do
         if max.in_range(3,99) then
            if unix_system = system_name then
               short_command := true
            elseif cygwin_system = system_name then
               short_command := true
            end
         end
         if short_command then
            add_objects_(cmd,c_name,1,max)
         else
            from
               i := 1 until i > max
            loop
               append_token(cmd,c_name)
               i.append_in(cmd)
               cmd.append(object_suffix)
               i := i + 1
            end
         end
      end

   add_objects_(cmd, c_name: STRING; min, max: INTEGER) is
         -- This is a good recursive example ;-)
      require
         max.in_range(1,99)
         min.in_range(1,max + 1)
      local
         c, i1, i2, new_min: INTEGER
      do
         if min > max then
            check min = max + 1 end
         elseif min < 9 then
            append_token(cmd,c_name)
            cmd.append(once "[1-")
            i1 := (9).min(max)
            i1.append_in(cmd)
            cmd.extend(']')
            cmd.append(object_suffix)
            add_objects_(cmd,c_name,i1 + 1,max)
         elseif min = max then
            append_token(cmd,c_name)
            min.append_in(cmd)
            cmd.append(object_suffix)
         else
            check (min \\ 10) = 0 end
            c := max - min + 1
            check c >= 2 end
            if c <= 10 then
               i1 := min // 10
               i2 := max \\ 10
               append_token(cmd,c_name)
               i1.append_in(cmd)
               cmd.extend('[')
               cmd.extend('0')
               cmd.extend('-')
               i2.append_in(cmd)
               cmd.extend(']')
               cmd.append(object_suffix)
            elseif c < 20 then
               add_objects_(cmd,c_name,min,min + 9)
               add_objects_(cmd,c_name,min + 10,max)
            else
               i1 := min // 10
               i2 := (max - 9) // 10
               check (i2 * 10) + 9 <= max end
               append_token(cmd,c_name)
               cmd.extend('[')
               i1.append_in(cmd)
               cmd.extend('-')
               i2.append_in(cmd)
               cmd.append(once "][0-9]")
               cmd.append(object_suffix)
               new_min := (i2 * 10) + 10
               add_objects_(cmd,c_name,new_min,max)
            end
         end
      ensure
         (min < max) implies (cmd.count > old cmd.count)
         c_name.is_equal(old c_name.twin)
         max = old max
      end

   add_external_header_path(path: STRING) is
         -- Append `path' to `external_header_path' taking into account
         -- how the current linker likes it to get this path.
         -- If `path' appears to be already formatted, no formatting is
         -- added by this routine.
      require
         not path.is_empty
      do
         token_buffer.clear
         -- everybody likes the Unix way
         if path.item (1) /= '-' then
            token_buffer.copy(once "-I")
         end
         token_buffer.append_string(path)
         token_buffer.extend_unless(' ')
         if not external_header_path.has_substring(token_buffer) then
            append_token(external_header_path,token_buffer)
         end
      end

   add_external_lib(lib: STRING) is
         -- Append `lib' to `external_lib' taking into account
         -- how the current linker likes it to get an additional library.
         -- If `lib' appears to be already formatted, no formatting is
         -- added by this routine.
      require
         not lib.is_empty
      do
         token_buffer.clear
         if c_compiler = lcc_win32 or else c_compiler = cl 
	    or else c_compiler = bcc32 then
            -- they don't need an option
         else
            -- the Unix way
            if lib.item (1) /= '-' then
	       if not lib.has('/') and then not lib.has('\') then
		  token_buffer.copy(once "-l")
	       end
	    end
         end
         token_buffer.append_string(lib)
         token_buffer.extend_unless(' ')
         do_add_external_lib(token_buffer)
      end

   add_external_lib_path(path: STRING) is
         -- Append `path' to `external_lib_path' taking into account
         -- how the current linker likes it to get this path.
         -- If `path' appears to be already formatted, no formatting is
         -- added by this routine.
      require
         not path.is_empty
      do
	 -- lcc_win32 doesn't support paths, do nothing in that case
         if c_compiler /= lcc_win32 then
	    token_buffer.clear
	    if c_compiler = cl then
	       -- Microsoft's own way of library searching
	       if path.item (1) /= '/' then
		  token_buffer.copy(once "/LIBPATH:")
	       end
	    else
	       -- the Unix way
	       if path.item (1) /= '-' then
		  token_buffer.copy(l_flag)
	       end
	    end
	    token_buffer.append_string(path)
	    token_buffer.extend_unless(' ')
	    if not external_lib_path.has_substring(token_buffer) then
	       append_token(external_lib_path,token_buffer)
	    end
	 end
      end

   token_buffer: STRING is
      once
         !!Result.make(128)
      end

   exe_suffix: STRING is ".exe"

   o_suffix: STRING is ".o"

   obj_suffix: STRING is ".obj"

   c_flag: STRING is "-c"

   o_flag: STRING is "-o"

   e_flag: STRING is "-e"

   s_flag: STRING is "-s"

   lcc: STRING is "lcc"

   vc: STRING is "vc"

   dcc: STRING is "dcc"

   lcclnk: STRING is "lcclnk"

   lnk_suffix: STRING is ".lnk"

   libm: STRING is "m"

   l_flag: STRING is "-L"

   o2: STRING is "-O2"

   libcpml: STRING is "cpml"

   fz_to_: STRING is " To "

   fz_loadpath_se: STRING is "loadpath.se"

   singleton_memory: SYSTEM_TOOLS is
      once
         Result := Current
      end

invariant

   is_real_singleton: Current = singleton_memory

end -- SYSTEM_TOOLS