english
version "1.0"
identify "xyz"

#: Copyright (c) 1998-2007 by Wayne C. Gramlich.
#, All rights reserved.

#: This module provides an input stream interface to files, network
#, streams, pipes, strings, and filters.
#,
#, Eventually, this module needs to be implementated via a co-type
#, (polymorphic) interface; until then, a simple variant is used.

module input_stream

define input_stream			#: Input stream of bytes/characters
    variant kind input_stream_kind
	closed null			#: This input_stream is closed
	file_input_stream file_input_stream #: A standard file input stream
    generate address_get, allocate, erase, identical, print

define input_stream_kind		#: The {input_stream} types.
    enumeration
	closed				#: A closed input stream
	file_input_stream		#: A file input stream
    generate equal, print, unsigned_convert

define file_input_stream		#: Input stream from a file
    record
	memory memory			#: Memory buffer
	done logical			#: {true} => no more data is available
	file_set file_set		#: {file_set} polling object (or ??)
	file_descriptor_number unsigned	#: File descriptor number
	file_name file_name		#: File name
	file_name_string string		#: Associated file name as a {string}
	offset unsigned			#: Offset to next valid byte
	remaining unsigned		#: Remaining valid bytes
    generate allocate, erase, print



#: {input_stream} routines:

procedure character_read@input_stream
    takes
	input_stream input_stream
    returns character

    #: This procedure will return the next character from {input_stream}.
    #, If there are no more characters to be had, the end-of-stream
    #, character is returned (see {end_of_stream_character}@{input_stream}.


procedure close@input_stream
    takes
	input_stream input_stream
    returns_nothing

    #: This procedure will close {input_stream}.


procedure create@input_stream
    takes_nothing
    returns input_stream

    #: This procedure will return a new closed {input_stream} object.
    #, It is primarily intended for use by other types, like
    #, {file_input_stream}, that have open procedures that will
    #, return an open {input_stream} object.


procedure input_available@input_stream
    takes
	input_stream input_stream
    returns logical

    #: This procedure will return {true}@{logical} if there is at least
    #, one byte of input avaiable from {input_stream} and {false}@{logical}
    #, otherwise.  This procedure does not do read ahead, so it will not
    #, block.


procedure end_of_stream_character@input_stream
    takes_nothing
    returns character

    #: This procedure returns the end-of-stream character that gets returned
    #, by {character_read}@{input_stream}() when there are no more characters
    #, to be had.


procedure is_done@input_stream
    takes
	input_stream input_stream
    returns logical

    #: This procedure will return {true}@{logical}() if no more
    #, bytes/characters can be read from {input_stream}; otherwise,
    #, {false} is returned.  Please note, this procedure will return
    #, {true} only until after at least one call is made to a read
    #, procedure returns 0 (or the end-of-stream character for
    #, {character_read}@{input_stream}().)


procedure is_open@input_stream
    takes
	input_stream input_stream
    returns logical

    #: This procedure will return {true}@{logical} if {input_stream} is
    #, still open and {false} otherwise.  Please note that having an
    #, open {input_stream} does not mean that any more data can be
    #, successfully read from it; use {is_done}@{input_stream}() for that
    #, purpose.


procedure memory_read@input_stream
    takes
	input_stream input_stream
	memory memory
	offset unsigned
	amount_requested unsigned
    returns unsigned

    #: This procedure will read up to {amount_requested} bytes from
    #, {input_stream} and store them into {memory} starting at
    #, {offset}.  The actual number of bytes read in is returned.
    #, A return value of 0 means that there are no more bytes/characters
    #, available from {input_stream} and it can be closed via
    #, {close}@{input_stream}().
    #,
    #, Please note that this procedure will only block if any internal
    #, buffer associated with {input_stream} is empty and the additional
    #, data has not yet been delivered by the operating system.  Thus,
    #, you should expect the actual number of bytes returned will frequently
    #, be less than {amount_requested} even though even though the
    #, operating system may eventually be able deliver more than
    #, {amount_requested} with subsequent calls.  With this procedure
    #, you should never assume that return value of less than
    #, {amount_requested} means that end of stream has been reached.


procedure memory_read_exact@input_stream
    takes
	input_stream input_stream
	memory memory
	offset unsigned
	amount_requested unsigned
    returns unsigned

    #: This procedure will attempt to read exactly {amount_requested}
    #, bytes/characters from {input_stream} and store the result into
    #, {memory} at starting it {offset}.  The actual number of bytes
    #, read is returned.  The only time the return value will not
    #, be equal to {amount_requested} is when an end of file/stream
    #, condition has occured on {input_stream}, in which case, the
    #, remaining number of bytes/characters is returned.


procedure string_append@input_stream
    takes
	input_stream input_stream
	buffer string
	amount_requested unsigned
    returns unsigned

    #: This procedure will append up to {amount_requested} characters
    #, from {input_stream} to {buffer}.  The actual number of characters
    #, appended is returned.  A return value of 0 means that there are
    #, no more characters available from {input_stream} and it can be
    #, closed.
    #,
    #, Please note that this procedure will only block if any internal
    #, buffer associated with {input_stream} is empty and the additional
    #, data has not yet been delivered by the operating system.  Thus,
    #, you should expect the actual number of bytes returned will frequently
    #, be less than {amount_requested} even though even though the
    #, operating system may eventually be able deliver more than
    #, {amount_requested} with subsequent calls.  With this procedure
    #, you should never assume that return value of less than
    #, {amount_requested} means that end of stream has been reached.


procedure string_append_exact@input_stream
    takes
	input_stream input_stream
	buffer string
	amount_requested unsigned
    returns unsigned

    #: This procedure will append exactly {amount_requested} bytes/characters
    #, from {input_stream} to {buffer}.  The total number of bytes/characters
    #, actually appended is returned.  The total number of bytes/characters
    #, returned will always equal {amount_requested} unless an end of
    #, stream condition occurs before {amount_requested} bytes/characters
    #, can be read.


#: {file_input_stream} routines:

procedure character_read@file_input_stream
    takes
	file_input_stream file_input_stream
    returns character

    #: This procedure will return the next character from {file_input_stream}.
    #, If there are no more characters to be had, the end-of-file character
    #, is returned (see {end_of_stream_character}@{file_input_stream}.


procedure close@file_input_stream
    takes
	file_input_stream file_input_stream
    returns_nothing

    #: This procedure will close {file_input_stream}.


procedure file_descriptor_bind@file_input_stream
    takes
	file_descriptor_number unsigned
	buffer_size unsigned
	file_name file_name
	file_name_string string
    returns input_stream

    #: This procedure will create and return an {file_input_stream}
    #, object connected to {file_descriptor_number} with a buffer that
    #, contains {buffer_size} bytes.  {file_name} and {file_name_string}
    #, are associated with the returned {file_input_stream} object for
    #, subsequent print out.


procedure fill@file_input_stream
    takes
	file_input_stream file_input_stream
	maximum_requested unsigned
    returns unsigned

    #: This procedure will fill the {file_input_stream} buffer with
    #, up to {maximum_requested} bytes/characters of data.  The amount
    #, of data remaining in the buffer is returned.


procedure input_available@file_input_stream
    takes
	file_input_stream file_input_stream
    returns logical

    #: This procedure will return {true}@{logical} if there is at least
    #, one byte of input avaiable from {file_input_stream} and
    #, {false}@{logical} otherwise.  This procedure does not do read
    #, ahead, so it will not block.


procedure is_done@file_input_stream
    takes
	file_input_stream file_input_stream
    returns logical

    #: This procedure will return {true} if no more characters can
    #, be read from {file_input_stream}.


procedure open@file_input_stream
    takes
	file_name file_name
    returns input_stream

    #: This procedure will return open {file_name} and return an
    #, associated {input_stream} object.


procedure open_buffer@file_input_stream
    takes
	file_name file_name
	buffer_size unsigned
    returns input_stream

    #: This procedure will return open {file_name} for reading and return
    #, an associated {input_stream} object with a backing buffer of size
    #, {buffer_size}.  ??@{input_stream} is returned if the open failed.
    #, An invocation to {status_get}@{unix_system} may give a more
    #, diagnostic reason.


procedure memory_read@file_input_stream
    takes
	file_input_stream file_input_stream
	to_memory memory
	to_offset unsigned
	amount_requested unsigned
    returns unsigned

    #: This procedure will read up to {amount_requested} bytes
    #, from {file_input_stream} and store them into {to_memory} starting
    #, at {to_offset}.  This procedure will fail if {to_offset}
    #, greater than or equal to the size of {to_memory}.
    #, The actual number of bytes read in is returned.  A return
    #, value of 0 means that there are no more bytes/characters available
    #, from {file_input_stream} and it can be closed.


procedure string_append@file_input_stream
    takes
	file_input_stream file_input_stream
	buffer string
	amount_requested unsigned
    returns unsigned

    #: This procedure will append up to {amount_requested} characters
    #, from {file_input_stream} to {buffer}.  The actual number of characters
    #, appended is returned.  A return value of 0 means that there are
    #, no more characters available from {file_input_stream} and it can be
    #, closed.

# Polymorphic implementation using co-types:
#
#define input_stream[buffer]
#    down_type input_stream
#    needs
#	procedure read
#	    takes buffer, unsigned, unsigned
#	    returns unsigned
#	procedure close
#	    takes buffer
#	    returns_nothing
#    record
#	buffer buffer
#	offset unsigned
#	unread unsigned
#    generate allocate, down_convert, erase, print, up_convert
#
#procedure create@input_stream[buffer]
#    takes
#	buffer buffer
#    returns input_stream
#
#    input_stream1 :@= new@input_stream1[buffer]()
#    input_stream1.buffer :@= buffer
#    input_stream :@= down_convert@(input_stream1)
#    return input_stream
#
#procedure read@input_stream
#    up_type input_stream[buffer]
#    takes
#	input_stream input_stream
#	amount unsigned
#    returns unsigned
#
#    input_stream1 :@= up_convert@(input_stream)
#    result :@= read@(input_stream1.buffer,
#      input_stream.offset + input_stream.unread, offset)
#    input_stream.unread :+= result
#    return result
#
#procedure close@input_stream
#    up_type input_stream[buffer]
#    takes
#	input_stream input_stream
#    returns_nothing
#
#    input_stream1 :@= up_convert@(input_stream)
#    close@(input_stream)