note
	description: "Files viewed as persistent sequences of ASCII characters"
	library: "Free implementation of ELKS library"
	status: "See notice at end of class."
	legal: "See notice at end of class."
	date: "$Date: 2020-05-28 16:02:59 +0000 (Thu, 28 May 2020) $"
	revision: "$Revision: 104378 $"

class 
	PLAIN_TEXT_FILE

inherit
	FILE
		rename
			index as position
		redefine
			make_with_name,
			make_with_path,
			is_plain_text
		end

create 
	make,
	make_with_name,
	make_with_path,
	make_open_read,
	make_open_write,
	make_open_append,
	make_open_read_write,
	make_create_read_write,
	make_open_read_append,
	make_open_temporary,
	make_open_temporary_with_prefix

feature -- Initialization	

	make_with_name (fn: READABLE_STRING_GENERAL)
			-- Create file object with fn as file name.
		do
			Precursor (fn)
			create last_string_32.make_empty
		end

	make_with_path (a_path: PATH)
			-- Create file object with a_path as path.
		do
			Precursor (a_path)
			create last_string_32.make_empty
		end
	
feature -- Status report

	is_plain_text: BOOLEAN
			-- Is file reserved for text (character sequences)? (Yes)
		do
			Result := True
		end

	Support_storable: BOOLEAN = False
			-- Can medium be used to store an Eiffel structure?
	
feature -- Output

	put_integer (i: INTEGER_32)
			-- Write ASCII value of i at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of putint and put_integer_32.
		do
			put_string_general (i.out)
		end

	putint (i: INTEGER_32)
			-- Write ASCII value of i at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_integer and put_integer_32.
		do
			put_string_general (i.out)
		end

	put_integer_32 (i: INTEGER_32)
			-- Write ASCII value of i at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_integer and putint.
		do
			put_string_general (i.out)
		end

	put_integer_64 (i: INTEGER_64)
			-- Write ASCII value of i at current position.
		do
			put_string_general (i.out)
		end

	put_integer_16 (i: INTEGER_16)
			-- Write ASCII value of i at current position.
		do
			put_string_general (i.out)
		end

	put_integer_8 (i: INTEGER_8)
			-- Write ASCII value of i at current position.
		do
			put_string_general (i.out)
		end

	put_natural_64 (i: NATURAL_64)
			-- Write ASCII value of i at current position.
		do
			put_string_general (i.out)
		end

	put_natural (i: NATURAL_32)
			-- Write ASCII value of i at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_natural_32.
		do
			put_string_general (i.out)
		end

	put_natural_32 (i: NATURAL_32)
			-- Write ASCII value of i at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_natural.
		do
			put_string_general (i.out)
		end

	put_natural_16 (i: NATURAL_16)
			-- Write ASCII value of i at current position.
		do
			put_string_general (i.out)
		end

	put_natural_8 (i: NATURAL_8)
			-- Write ASCII value of i at current position.
		do
			put_string_general (i.out)
		end

	put_boolean (b: BOOLEAN)
			-- Write ASCII value of b at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of putbool.
		do
			put_string_general (if b then
					True_string
				else
					False_string
				end)
		end

	putbool (b: BOOLEAN)
			-- Write ASCII value of b at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_boolean.
		do
			put_string_general (if b then
					True_string
				else
					False_string
				end)
		end

	put_real (r: REAL_32)
			-- Write ASCII value of r at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of putreal and put_real_32.
		do
			file_pr (file_pointer, r)
		end

	putreal (r: REAL_32)
			-- Write ASCII value of r at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_real and put_real_32.
		do
			file_pr (file_pointer, r)
		end

	put_real_32 (r: REAL_32)
			-- Write ASCII value of r at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_real and putreal.
		do
			file_pr (file_pointer, r)
		end

	put_double (d: REAL_64)
			-- Write ASCII value d at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of putdouble and put_real_64.
		do
			file_pd (file_pointer, d)
		end

	putdouble (d: REAL_64)
			-- Write ASCII value d at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_double and put_real_64.
		do
			file_pd (file_pointer, d)
		end

	put_real_64 (d: REAL_64)
			-- Write ASCII value d at current position.
			-- Was declared in PLAIN_TEXT_FILE as synonym of put_double and putdouble.
		do
			file_pd (file_pointer, d)
		end

	put_string_32 (s: READABLE_STRING_32)
			-- Write string s at current position.
		do
			put_string_general (s)
		end

	put_string_general (s: READABLE_STRING_GENERAL)
			-- Write string s at current position.
		require
			extendible: extendible
			non_void: s /= Void
		local
			str: STRING_8
			utf32, utf8: ENCODING
			l_encoding: like encoding
		do
			l_encoding := encoding
			utf32 := {SYSTEM_ENCODINGS}.utf32;
			utf32.convert_to (l_encoding, s)
			if utf32.last_conversion_successful then
				str := utf32.last_converted_string_8
			else
				utf8 := {SYSTEM_ENCODINGS}.utf8;
				utf32.convert_to (utf8, s)
				if utf32.last_conversion_successful then
					str := utf32.last_converted_string_8
					if not utf8.same_as (l_encoding) then
						utf8.convert_to (l_encoding, str)
						if utf8.last_conversion_successful then
							str := utf8.last_converted_string_8
						end
					end
				elseif s.is_valid_as_string_8 then
					str := s.to_string_8
				else
					str := {UTF_CONVERTER}.string_32_to_utf_8_string_8 (s.as_string_32)
				end
			end
			put_string (str)
		end
	
feature -- Input

	read_integer_64
			-- Read the ASCII representation of a new 64-bit integer
			-- from file. Make result available in last_integer_64.
		do
			read_integer_with_no_type
			last_integer_64 := Ctoi_convertor.parsed_integer_64
		end

	read_integer
			-- Read the ASCII representation of a new 32-bit integer
			-- from file. Make result available in last_integer.
			-- Was declared in PLAIN_TEXT_FILE as synonym of readint and read_integer_32.
		do
			read_integer_with_no_type
			last_integer := Ctoi_convertor.parsed_integer_32
		end

	readint
			-- Read the ASCII representation of a new 32-bit integer
			-- from file. Make result available in last_integer.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_integer and read_integer_32.
		do
			read_integer_with_no_type
			last_integer := Ctoi_convertor.parsed_integer_32
		end

	read_integer_32
			-- Read the ASCII representation of a new 32-bit integer
			-- from file. Make result available in last_integer.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_integer and readint.
		do
			read_integer_with_no_type
			last_integer := Ctoi_convertor.parsed_integer_32
		end

	read_integer_16
			-- Read the ASCII representation of a new 16-bit integer
			-- from file. Make result available in last_integer_16.
		do
			read_integer_with_no_type
			last_integer_16 := Ctoi_convertor.parsed_integer_16
		end

	read_integer_8
			-- Read the ASCII representation of a new 8-bit integer
			-- from file. Make result available in last_integer_8.
		do
			read_integer_with_no_type
			last_integer_8 := Ctoi_convertor.parsed_integer_8
		end

	read_natural_64
			-- Read the ASCII representation of a new 64-bit natural
			-- from file. Make result available in last_natural_64.
		do
			read_integer_with_no_type
			last_natural_64 := Ctoi_convertor.parsed_natural_64
		end

	read_natural
			-- Read the ASCII representation of a new 32-bit natural
			-- from file. Make result available in last_natural.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_natural_32.
		do
			read_integer_with_no_type
			last_natural := Ctoi_convertor.parsed_natural_32
		end

	read_natural_32
			-- Read the ASCII representation of a new 32-bit natural
			-- from file. Make result available in last_natural.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_natural.
		do
			read_integer_with_no_type
			last_natural := Ctoi_convertor.parsed_natural_32
		end

	read_natural_16
			-- Read the ASCII representation of a new 16-bit natural
			-- from file. Make result available in last_natural_16.
		do
			read_integer_with_no_type
			last_natural_16 := Ctoi_convertor.parsed_natural_16
		end

	read_natural_8
			-- Read the ASCII representation of a new 8-bit natural
			-- from file. Make result available in last_natural_8.
		do
			read_integer_with_no_type
			last_natural_8 := Ctoi_convertor.parsed_natural_8
		end

	read_real
			-- Read the ASCII representation of a new real
			-- from file. Make result available in last_real.
			-- Was declared in PLAIN_TEXT_FILE as synonym of readreal and read_real_32.
		do
			last_real := file_gr (file_pointer)
		end

	readreal
			-- Read the ASCII representation of a new real
			-- from file. Make result available in last_real.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_real and read_real_32.
		do
			last_real := file_gr (file_pointer)
		end

	read_real_32
			-- Read the ASCII representation of a new real
			-- from file. Make result available in last_real.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_real and readreal.
		do
			last_real := file_gr (file_pointer)
		end

	read_double
			-- Read the ASCII representation of a new double
			-- from file. Make result available in last_double.
			-- Was declared in PLAIN_TEXT_FILE as synonym of readdouble and read_real_64.
		do
			last_double := file_gd (file_pointer)
		end

	readdouble
			-- Read the ASCII representation of a new double
			-- from file. Make result available in last_double.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_double and read_real_64.
		do
			last_double := file_gd (file_pointer)
		end

	read_real_64
			-- Read the ASCII representation of a new double
			-- from file. Make result available in last_double.
			-- Was declared in PLAIN_TEXT_FILE as synonym of read_double and readdouble.
		do
			last_double := file_gd (file_pointer)
		end

	read_to_string (a_string: STRING_8; pos, nb: INTEGER_32): INTEGER_32
			-- Fill a_string, starting at position pos with at
			-- most nb characters read from current file.
			-- Return the number of characters actually read.
		do
			Result := file_gss (file_pointer, a_string.area.item_address (pos - 1), nb);
			a_string.reset_hash_codes
		end
	
feature -- Unicode input

	last_string_32: STRING_32
			-- Last unicode string read.

	read_unicode_line
			-- Read a line as STRING_32.
		local
			utf32: ENCODING
		do
			read_line
			utf32 := {SYSTEM_ENCODINGS}.utf32;
			encoding.convert_to (utf32, last_string)
			if encoding.last_conversion_successful then
				last_string_32.wipe_out;
				last_string_32.append (encoding.last_converted_string_32)
			end
		end
	
feature -- Encoding

	encoding: ENCODING
			-- Associated encoding.
		do
			if attached internal_encoding as l_encoding then
				Result := l_encoding
			else
				Result := Default_encoding
				internal_encoding := Result
			end
		ensure
			encoding_not_void: Result /= Void
		end

	Default_encoding: ENCODING
			-- Default value for encoding.
		once
			Result := {SYSTEM_ENCODINGS}.utf8
		ensure
			default_encoding_not_void: Result /= Void
		end

	set_encoding (enc: like encoding)
			-- Set associated encoding with enc.
		require
			enc_not_void: enc /= Void
		do
			internal_encoding := enc
		ensure
			encoding_set: encoding = enc
		end

	set_utf8_encoding
			-- Set encoding to UTF-8.
		do
			set_encoding ({SYSTEM_ENCODINGS}.utf8)
		ensure
			encoding_set: encoding = {SYSTEM_ENCODINGS}.utf8
		end

	detect_encoding
			-- Detect and update encoding according to BOM detection.
		require
				is_open_read
		local
			pos: like position
			c1, c2, c3, c4: CHARACTER_8
		do
			pos := position
			start
			if readable then
				read_character
				c1 := last_character
				inspect c1
				when 'ï' then
					if readable then
						read_character
						c2 := last_character
						if c2 = '»' then
							if readable then
								read_character
								c3 := last_character
								if c3 = '¿' then
									set_utf8_encoding
								else
									back
								end
							end
						else
							back
						end
					end
				when 'ÿ' then
					if readable then
						read_character
						c2 := last_character
						if c2 = 'þ' then
							if readable then
								read_character
								c3 := last_character
								if readable then
									read_character
									c4 := last_character
								end
								if c3 = '%U' and c4 = '%U' then
									set_encoding (create {ENCODING}.make ({CODE_PAGE_CONSTANTS}.utf32_le))
								else
									set_encoding (create {ENCODING}.make ({CODE_PAGE_CONSTANTS}.utf16_le))
									back
									back
								end
							end
						else
							back
						end
					end
				when 'þ' then
					if readable then
						read_character
						c2 := last_character
						if c2 = 'ÿ' then
							set_encoding (create {ENCODING}.make ({CODE_PAGE_CONSTANTS}.utf16_be))
						else
							back
						end
					end
				when '%U' then
					if readable then
						read_character
						c2 := last_character
						if c2 = '%U' then
							if readable then
								read_character
								c3 := last_character
								if c3 = 'þ' then
									if readable then
										read_character
										c4 := last_character
										if c4 = 'ÿ' then
											set_encoding (create {ENCODING}.make ({CODE_PAGE_CONSTANTS}.utf32_be))
										else
											back
										end
									end
								else
									back
								end
							end
						else
							back
						end
					end
				else
					back
				end
			end
			if pos /= 0 then
				go (pos)
			end
		end

	put_encoding_bom
			-- Put Byte Order Mark (BOM) related to encoding.
		require
			is_open_write: is_open_write
			at_beginning: position = 0
		local
			cp: READABLE_STRING_8
		do
			cp := encoding.code_page
			if cp.is_case_insensitive_equal_general ({CODE_PAGE_CONSTANTS}.utf8) then
				put_character ('ï')
				put_character ('»')
				put_character ('¿')
			elseif cp.is_case_insensitive_equal_general ({CODE_PAGE_CONSTANTS}.utf16_le) then
				put_character ('ÿ')
				put_character ('þ')
			elseif cp.is_case_insensitive_equal_general ({CODE_PAGE_CONSTANTS}.utf16_be) then
				put_character ('þ')
				put_character ('ÿ')
			elseif cp.is_case_insensitive_equal_general ({CODE_PAGE_CONSTANTS}.utf32_le) then
				put_character ('ÿ')
				put_character ('þ')
				put_character ('%U')
				put_character ('%U')
			elseif cp.is_case_insensitive_equal_general ({CODE_PAGE_CONSTANTS}.utf32_be) then
				put_character ('%U')
				put_character ('%U')
				put_character ('þ')
				put_character ('ÿ')
			end
		end
	
feature {NONE} -- Encoding

	internal_encoding: detachable ENCODING
			-- Internal value for encoding.
	
feature {NONE} -- Implementation

	Ctoi_convertor: STRING_TO_INTEGER_CONVERTOR
			-- Convertor used to parse string to integer or natural
		once
			create Result.make;
			Result.set_leading_separators (Internal_leading_separators);
			Result.set_leading_separators_acceptable (True);
			Result.set_trailing_separators_acceptable (False)
		end

	Internal_leading_separators: STRING_8 = " %N%R%T"
			-- Characters that are considered as leading separators

	is_sequence_an_expected_numeric: BOOLEAN
			-- Is last number sequence read by read_number_sequence an expected numeric?

	read_number_sequence (convertor: STRING_TO_NUMERIC_CONVERTOR; conversion_type: INTEGER_32)
			-- Read a number sequence from current position and parse this
			-- sequence using convertor to see if it is a valid numeric.
			-- Set is_sequence_an_expected_numeric with True if it is valid.
		do
			convertor.reset (conversion_type)
			from
				is_sequence_an_expected_numeric := True
			until
				end_of_file or else not is_sequence_an_expected_numeric
			loop
				read_character
				if not end_of_file then
					convertor.parse_character (last_character)
					is_sequence_an_expected_numeric := convertor.parse_successful
				end
			end
		end

	read_integer_with_no_type
			-- Read a ASCII representation of number of type
			-- at current position.
		do
			read_number_sequence (Ctoi_convertor, {NUMERIC_INFORMATION}.type_no_limitation)
			if not is_sequence_an_expected_numeric then
				return_characters
			end
		end

	return_characters
			-- Return character(s)
		do
			if last_character = '%N' and {PLATFORM}.is_windows then
				back
			end
			back
		end

	file_gi (file: POINTER): INTEGER_32
			-- Get an integer from file
		external
			"C signature (FILE *): EIF_INTEGER use %"eif_file.h%""
		alias
			"eif_file_gi"
		end

	file_gr (file: POINTER): REAL_32
			-- Read a real from file
		external
			"C signature (FILE *): EIF_REAL_32 use %"eif_file.h%""
		alias
			"eif_file_gr"
		end

	file_gd (file: POINTER): REAL_64
			-- Read a double from file
		external
			"C signature (FILE *): EIF_REAL_64 use %"eif_file.h%""
		alias
			"eif_file_gd"
		end

	file_pi (file: POINTER; n: INTEGER_32)
			-- Put n to end of file.
		external
			"C signature (FILE *, EIF_INTEGER) use %"eif_file.h%""
		alias
			"eif_file_pi"
		end

	file_pr (file: POINTER; r: REAL_32)
			-- Put r to end of file.
		external
			"C signature (FILE *, EIF_REAL_32) use %"eif_file.h%""
		alias
			"eif_file_pr"
		end

	file_pd (file: POINTER; d: REAL_64)
			-- Put d to end of file.
		external
			"C signature (FILE *, EIF_REAL_64) use %"eif_file.h%""
		alias
			"eif_file_pd"
		end
	
invariant
	plain_text: is_plain_text

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 PLAIN_TEXT_FILE

Generated by ISE EiffelStudio