note
	description: "[
					Objects representing a path, i.e. a way to identify a file or a directory for the
					current underlying platform. A path is made of two components:
					1 - an optional root which can either be:
						a - a drive letter followed by colon on Windows, i.e. "C:" or "C:\"
						b - "/" for UNIX root directory.
						c - "\" for Windows root directory.
						d - "\\server\share" or "\\server\share\" for Microsoft UNC path.
					2 - a sequence of zero or more names.
					
					A path is absolute if it has a root, and on windows if the root is a drive, then it should
					be followed by "\". Otherwise a path is relative.
		
		
					Validity
					========
					
					The current class will not check the validity of filenames. Check your file
					system for your operating system manual for the list of invalid characters.
		
		
					Windows consideration
					=====================
					
					When the root of a Windows path is a drive, be aware of the following behavior:
					1 - "C:filename.txt" refers to the file name "filename.txt" in the current directory 
					    on drive "C:".
					2 - "C:sub\filename.txt" refers to the file name "filename.txt" in a subdirectory "sub"
					    of the current directory on drive "C:".
					3 - "C:\sub\filename.txt" refers to the file name "filename.txt" in a subdirectory "sub"
					    located at the root of the drive "C:".
					
					Both forward and backslashes are accepted, but forward slashes are internally converted
					to backward slashes whenever they are used to construct a path.
					
					On Windows, there is a limit of 259 characters for a path. If you need to create a larger
					path, you can do so by using the following conventions which will let you have paths of
					about 32,767 characters:
					1 - Use \\?\ for non-UNC path and let the rest unchanged.
					2 - Use \\?\UNC\server\share for UNC path and let the rest unchanged.
					The above path cannot be used to specify a relative path.
					
					To know more about Windows paths, read the "Naming Files, Paths, and Namespaces"
					document located at:
					  http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
		
		
					Unicode consideration
					=====================
					
					The PATH class treats strings as sequence of Unicode characters, i.e. an instance of 
					a READABLE_STRING_8 or descendant will be treated as if characters in the range
					128 .. 255 were Unicode code points.
					This contrasts to the FILE/DIRECTORY classes where to preserve backward compatibility, those
					characters are treated as is.
					
					
					Mixed-encoding consideration
					============================
					
					Most operating systems have conventions for paths that are incompatible with Unicode.
					On UNIX, in a sequence of names, each name is just a null-terminated byte sequence, it
					does not follow any specific encoding. Usually the locale setting enables you to see
					the filename the way you expect.
					On Windows, the sequence of names is made of null-terminated UTF-16 code unit sequence. Windows
					does not guarantee that the sequence is actually a valid UTF-16 sequence.
					
					In other words, when there is an invalid UTF-8 encoding on UNIX, or an invalid UTF-16 encoding
					on Windows, the filename is not directly representable as a Unicode string. To make it possible
					to create and store paths in a textually representable form, the query name will create
					an encoded representation that can be then later used in make_from_string to create a PATH
					equivalent to the original path. The encoding is described in UTF_CONVERTER's note clause
					and is a fourth variant of the recommended practice for replacement characters in Unicode
					(see http://www.unicode.org/review/pr-121.html).
		
								
					Immutability
					============
					
					Instances of the current class are immutable.
	]"
	library: "Free implementation of ELKS library"
	status: "See notice at end of class."
	date: "$Date: 2020-05-19 14:32:38 +0000 (Tue, 19 May 2020) $"
	revision: "$Revision: 104260 $"

class interface
	PATH

create {NATIVE_STRING_HANDLER}
	make_from_pointer


create {PATH}
	make_from_storage,
	make_from_normalized_storage


create 
	make_empty,
	make_current,
	make_from_string,
	make_from_separate

feature -- Status report

	is_current_symbol: BOOLEAN
			-- Is Current a representation of "."?

	is_parent_symbol: BOOLEAN
			-- Is Current Representation of ".."?

	has_root: BOOLEAN
			-- Does current have a root?
		ensure
			defintion: Result implies not is_empty

	is_empty: BOOLEAN
			-- Is current empty, i.e. no root and no sequence of names?

	is_relative: BOOLEAN
			-- Is current path relative?

	is_absolute: BOOLEAN
			-- Is current path absolute?

	is_simple: BOOLEAN
			-- Is current path made of only one name and no root?
			-- I.e. readme.txt, usr or the empty path.

	is_same_file_as (a_path: PATH): BOOLEAN
			-- Does Current and a_path points to the same file on disk? It is different from path equality
			-- as it will take into account symbolic links.
			-- If Current or/and a_path do not exists, it will yield false, otherwise it will compare
			-- the file at the file system level.
		require
			a_path_not_void: a_path /= Void

	has_extension (a_ext: READABLE_STRING_GENERAL): BOOLEAN
			-- Does Current has an extension a_ext compared in a case insensitive manner?
		require
			a_ext_not_void: a_ext /= Void
			a_ext_not_empty: not a_ext.is_empty
			a_ext_has_no_dot: not a_ext.has ('.'.to_character_32)
	
feature -- Access

	root: detachable PATH
			-- Root if any of current path.
		ensure
			has_root_implies_not_void: has_root implies Result /= Void

	parent: PATH
			-- Parent directory if any, otherwise current working path.
			-- The parent of a path consists of root if any, and of each
			-- simple names in the current sequence except for the last.

	entry: detachable PATH
			-- Name of file or directory denoted by Current if any.
			-- This is the last name in the current sequence.
		ensure
			not_empty: Result /= Void implies not Result.is_empty

	extension: detachable IMMUTABLE_STRING_32
			-- Extension if any of current entry.
		ensure
			not_empty: attached Result implies not Result.is_empty
			no_dot: attached Result implies not Result.has ('.'.to_character_32)

	components: ARRAYED_LIST [PATH]
			-- Sequence of simple paths making up Current, including root if any.

	absolute_path: PATH
			-- Absolute path of Current.
			-- If Current is already absolute, then return Current.
			-- If Current is empty, then return the current working directory.
			-- Otherwise resolve the path in a platform specific way:
			-- * On UNIX, resolve against the current working directory
			-- * On Windows:
			--    a) if current has a drive letter which is not followed by "\"
			--       resolve against the current working directory for that drive letter,
			--       otherwise resolve against the current working directory.
			--    b) if current path starts with "\", not a double "\\", then resolve
			--       against the root of the current working directory (i.e. a drive
			--       letter "C:\" or a UNC path "\\server\share\".)

	absolute_path_in (a_current_directory: PATH): PATH
			-- Absolute path of Current in the context of a_current_directory.
			-- If Current is already absolute, then return Current.
			-- If Current is empty, then return a_current_directory.
			-- Otherwise resolve the path in a platform specific way:
			-- * On UNIX, resolve against a_current_directory
			-- * On Windows:
			--    a) if current has a drive letter which is not followed by "\"
			--       resolve against a_current_directory for that drive letter,
			--       otherwise resolve against a_current_directory.
			--    b) if current path starts with "\", not a double "\\", then resolve
			--       against the root of `a_current_directory; (i.e. a drive
			--       letter "C:\" or a UNC path "\\server\share\".)
		require
			a_current_directory_not_void: a_current_directory /= Void
			a_current_directory_absolute: a_current_directory.is_absolute
		ensure
			has_root: Result.has_root

	canonical_path: PATH
			-- Canonical path of Current.
			-- Similar to absolute_path except that sequences containing "." or ".." are
			-- resolved.

	hash_code: INTEGER_32
			-- Hash code value

	native_string: NATIVE_STRING
			-- Convert current into an instance of NATIVE_STRING
		ensure
			set: Result.raw_string.same_string (storage)

	Unix_separator: CHARACTER_8 = '/'

	Windows_separator: CHARACTER_8 = '\'
			-- Platform specific directory separator.

	directory_separator: CHARACTER_8
			-- Default directory separator for the current platform.
	
feature -- Status setting

	extended (a_name: READABLE_STRING_GENERAL): PATH
			-- New path instance of current extended with path a_name.
			-- If current is not empty, then a_name cannot have a root.
			-- A directory separator is added between two entries except
			-- 1 - a_name starts with a directory separator (i.e. it has a root)
			-- 2 - if current is empty or is just a root.
			-- Note that a_name can be an encoding of a mixed-encoding simple name and it will
			-- be decoded accordingly (see note clause for the class for more details.)
		require
			a_name_not_void: a_name /= Void
			a_name_not_empty: not a_name.is_empty
			a_name_has_no_root: not is_empty implies not (create {PATH}.make_from_string (a_name)).has_root
		ensure
			associated_path_of_name: attached (create {PATH}.make_from_string (a_name)) as l_path
			not_empty: not Result.is_empty
			extended_with_only_empty_or_root: (same_as (root) or is_empty) implies Result.name.same_string (name + l_path.name)
			extended_with_more_than_root_or_not_empty: (not same_as (root) and not is_empty) implies Result.name.same_string (name + Directory_separator_string + l_path.name)

	extended_path alias "+" (a_path: PATH): PATH
			-- New path instance of current extended with path a_path.
			-- If current is not empty, then a_path cannot have a root.
			-- A directory separator is added between two entries except
			-- 1 - a_path starts with a directory separator (i.e. it has a root)
			-- 2 - if current is empty or is just a root.
		require
			a_path_not_void: a_path /= Void
			a_path_not_empty: not a_path.is_empty
			a_path_has_no_root: not is_empty implies not a_path.has_root
		ensure
			not_empty: not Result.is_empty
			extended_with_only_empty_or_root: (same_as (root) or is_empty) implies Result.name.same_string (name + a_path.name)
			extended_with_more_than_root_or_not_empty: (not same_as (root) and not is_empty) implies Result.name.same_string (name + Directory_separator_string + a_path.name)

	appended (a_extra: READABLE_STRING_GENERAL): PATH
			-- New path instance of current where entry is appended with a_extra without
			-- adding a directory separator.
			-- For example if Current path is "C:" and a_path is "path\file.txt", it will yield
			-- "C:path\file.txt".
			-- Note that a_extra can be an encoding of a mixed-encoding simple name and it will
			-- be decoded accordingly (see note clause for the class for more details.)
		require
			a_extra_not_void: a_extra /= Void
			a_extra_not_empty: not a_extra.is_empty
		ensure
			not_empty: not Result.is_empty
			appended: Result.name.same_string (name + (create {PATH}.make_from_string (a_extra)).name)

	appended_with_extension (a_ext: READABLE_STRING_GENERAL): PATH
			-- New path instance of current where entry is extended with a dot followed by a_ext.
			-- If Current already has a dot, no dot is added.
			-- Note that a_ext can be an encoding of a mixed-encoding simple name and it will
			-- be decoded accordingly (see note clause for the class for more details.)
		require
			a_ext_not_void: a_ext /= Void
			a_ext_not_empty: not a_ext.is_empty
			a_ext_has_no_dot: not a_ext.has ('.'.to_character_32)
			a_ext_has_no_directory_separator: not a_ext.has (Windows_separator.to_character_32) and not a_ext.has (Unix_separator.to_character_32)
			has_entry: entry /= Void
		ensure
			not_empty: not Result.is_empty
			extension_set: attached Result.extension as l_ext and then l_ext.same_string_general (a_ext)
			components_stable: Result.components.count = components.count
	
feature -- Comparison

	same_as (other: detachable PATH): BOOLEAN
			-- Is Current the same path as other?
			-- Note that no canonicalization is being performed to compare paths,
			-- paths are compared using the OS-specific convention for letter case.

	is_less alias "<" (other: like Current): BOOLEAN
			-- Is current object less than other?

	is_equal (other: like Current): BOOLEAN
			-- Is other attached to an object considered
			-- equal to current object?

	is_case_sensitive_equal (other: PATH): BOOLEAN
			-- Compare path and paying attention to case.
		require
			other_not_void: other /= Void

	is_case_insensitive_equal (other: PATH): BOOLEAN
			-- Compare path without paying attention to case. If the path is containing some mixed-encoding
			-- we might ignore many characters when doing the case comparison.
		require
			other_not_void: other /= Void
	
feature -- Duplication

	copy (other: like Current)
			-- Update current object using fields of object attached
			-- to other, so as to yield equal objects.
	
feature -- Output

	out: STRING_8
			-- Unicode representation of the underlying filename if representable,
			-- otherwise a UTF-8 encoded version.
			-- Use utf_8_name to have a printable representation whose format is not going
			-- to be changed in the future.

	utf_8_name: STRING_8
			-- UTF-8 representation of the underlying filename.

	name: IMMUTABLE_STRING_32
			-- If current is representable in Unicode, the Unicode representation.
			-- Otherwise all non-valid sequences for the current platform in the path are escaped
			-- as mentioned in the note clause of the class.
			-- To ensure roundtrip, you cannot use name directly to create a FILE, you have to
			-- create a PATH instance using make_from_string before passing it to the creation
			-- procedure of FILE taking an instance of PATH.
		ensure
			roundtrip: same_as (create {PATH}.make_from_string (Result))
	
feature -- Output

	debug_output: READABLE_STRING_32
			-- String that should be displayed in debugger to represent Current.
	
invariant
	little_endian_windows: {PLATFORM}.is_windows implies Platform.Is_little_endian
	even_count_on_windows: {PLATFORM}.is_windows implies storage.count \\ unit_size = 0
	no_forward_slash_on_windows: {PLATFORM}.is_windows implies not storage.has_substring ("/%U")

note
	copyright: "Copyright (c) 1984-2020, Eiffel Software and others"
	license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
	source: "[
		Eiffel Software
		5949 Hollister Ave., Goleta, CA 93117 USA
		Telephone 805-685-1006, Fax 805-685-6869
		Website http://www.eiffel.com
		Customer support http://support.eiffel.com
	]"

end -- class PATH

Generated by ISE EiffelStudio