(* $Id: netchannels.mli,v 1.12 2002/10/26 20:47:37 stolpmann Exp $
 * ----------------------------------------------------------------------
 *
 *)


(****************************** Types *********************************)

exception Closed_channel

exception Buffer_underrun
  (* Raised by input methods if the internal buffer of the channel is too
   * empty to read even one byte of data.
   * This exception is only used by certain implementations of channel
   * classes.
   *)

exception Command_failure of Unix.process_status
  (* Raised by [close_in] or [close_out] if the channel is connected with
   * another process, and the execution of that process fails.
   *)

class type raw_in_channel = object
  method input : string -> int -> int -> int
  method close_in : unit -> unit
  method pos_in : int             (* number of read characters *)
end

class type raw_out_channel = object
  method output : string -> int -> int -> int
  method close_out : unit -> unit
  method pos_out : int             (* number of written characters *)
  method flush : unit -> unit
end

class type raw_io_channel = object
  inherit raw_in_channel
  inherit raw_out_channel
end

class type compl_in_channel = object
  (* Classic operations: *)
  method really_input : string -> int -> int -> unit
  method input_char : unit -> char
  method input_line : unit -> string
  method input_byte : unit -> int

  (* Transitional method: *)
  method _rep_in : [ `Chan of in_channel | `Other ]
     (* THIS METHOD IS DEPRECATED AND WILL BE REMOVED SOON *)
end

class type in_obj_channel = object
  inherit raw_in_channel
  inherit compl_in_channel
end

class type compl_out_channel = object
  (* Classic operations: *)
  method really_output : string -> int -> int -> unit
  method output_char : char -> unit
  method output_string : string -> unit
  method output_byte : int -> unit
  method output_buffer : Buffer.t -> unit
  method output_channel : ?len:int -> in_obj_channel -> unit
      (* ~len: optionally limit the number of bytes *)

  (* Transitional method: *)
  method _rep_out : [ `Chan of out_channel | `Other ]
     (* THIS METHOD IS DEPRECATED AND WILL BE REMOVED SOON *)
end


class type out_obj_channel = object
  inherit raw_out_channel
  inherit compl_out_channel
end


class type io_obj_channel = object
  inherit in_obj_channel
  inherit out_obj_channel
end


class type trans_out_obj_channel = object
  inherit out_obj_channel

  (* Buffered output channels may have transaction semantics: *)
  method commit_work : unit -> unit
  method rollback_work : unit -> unit
  (* A transaction buffer collects the strings written into it. When
   * commit_work is called, the contents are processed. When rollback_work
   * is called, the contents are dropped, and the buffer is empty again.
   * The method close_out implies either commit_work or rollback_work
   * at the user's choice.
   * If a system error occurs while writing data to commit, the
   * error flag will be set, the buffer will be emptied, and the exception
   * will be raised.
   * The method flush has no effect for transaction buffers. The internal
   * buffers are all flushed when commit_work is called.
   *
   * Note that if the underlying descriptor is a network descriptor, 
   * the other side of the connection normally does _not_ acknowledge
   * that data have been received and processed. The method commit_work
   * simply writes the data to the network connection, and does not check
   * whether the data have been successfully processed (which would
   * require a special protocol).
   *)
end


(****************************** Input channels ************************)


class input_channel :
  in_channel ->
    in_obj_channel


class input_command : 
  string ->
    in_obj_channel
  (* Runs the command with /bin/sh, and reads from stdout. When [close_in]
   * is invoked, and the process did not exit with a code 0, the exception
   * Command_failure is raised.
   *)


class input_string :
  ?pos:int -> ?len:int -> string ->
    in_obj_channel
  (* Note: The passed string is not modified. The data of the string are read,
   * and after the whole string has been read an EOF condition is signaled.
   *
   * ~pos: The data of the channel begins at this position of the string.
   *   default: 0
   * ~len: The data of the channel consists of this number of bytes.
   *   default: until the end of the string
   *)


val create_input_netbuffer :
  Netbuffer.t ->
    in_obj_channel   *   (* shutdown: *) (unit -> unit)
  (* The data are read from the beginning of the netbuffer, and read data is
   * removed from the netbuffer.
   * The user of this class may add new data to the netbuffer at any time.
   * If the netbuffer becomes empty, the input methods raise Buffer_underrun.
   * If the returned function is called the channel will record the EOF
   * condition, and the input methods will indicate EOF if the netbuffer
   * becomes empty.
   *)

val lexbuf_of_in_obj_channel : in_obj_channel -> Lexing.lexbuf

val string_of_in_obj_channel : in_obj_channel -> string
  (* Reads from the in_obj_channel until EOF and returns the characters
   * as string.
   *)

val with_in_obj_channel : 
  (#in_obj_channel as 'a) -> ('a -> 'b) -> 'b
  (* with_in_obj_channel ch f:
   * Computes f ch and closes ch. If an exception happens, the channel is
   * closed, too.
   *)


(**************************** Output channels *************************)

class output_channel :
  ?onclose:(unit -> unit) ->              (* default: fun _ -> () *)
  out_channel ->
    out_obj_channel
  (* This out_obj_channel redirects the methods to the corresponding 
   * channel functions with the same name.
   * ~onclose: this function is called when the close_out method is invoked
   *)


class output_command : 
  ?onclose:(unit -> unit) ->              (* default: fun _ -> () *)
  string ->
    out_obj_channel
  (* Runs the command with /bin/sh, and writes to stdin. When [close_out]
   * is invoked, and the process did not exit with a code 0, the exception
   * Command_failure is raised.
   * ~onclose: this function is called when the close_out method is invoked
   *)


class output_buffer :
  ?onclose:(unit -> unit) ->              (* default: fun _ -> () *)
  Buffer.t ->
    out_obj_channel
  (* This out_obj_channel writes the data into the passed buffer.
   * ~onclose: this function is called when the close_out method is invoked
   *)

class output_netbuffer :
  ?onclose:(unit -> unit) ->              (* default: fun _ -> () *)
  Netbuffer.t ->
    out_obj_channel
  (* This out_obj_channel writes the data into the passed netbuffer.
   * ~onclose: this function is called when the close_out method is invoked
   *)

class output_null :
  ?onclose:(unit -> unit) ->              (* default: fun _ -> () *)
  unit ->
    out_obj_channel
  (* This out_obj_channel discards all written data. The method [pos_out]
   * returns the number of discarded bytes.
   * ~onclose: this function is called when the close_out method is invoked
   *)

val with_out_obj_channel : 
  (#out_obj_channel as 'a) -> ('a -> 'b) -> 'b
  (* with_out_obj_channel ch f:
   * Computes f ch and closes ch. If an exception happens, the channel is
   * closed, too.
   *)


(********************** Raw channels **********************************)

class virtual augment_raw_in_channel :
object
  inherit compl_in_channel
  method virtual input : string -> int -> int -> int
  method virtual close_in : unit -> unit
  method virtual pos_in : int
end
  (* This class implements the methods from compl_in_channel by calling
   * the methods of raw_in_channel. There is no additional buffering.
   * The performance of the method input_line is very bad. 
   *)


class virtual augment_raw_out_channel :
object
  inherit compl_out_channel
  method virtual output : string -> int -> int -> int
  method virtual close_out : unit -> unit
  method virtual flush : unit -> unit
  method virtual pos_out : int
end
  (* This class implements the methods from compl_out_channel by calling
   * the methods of raw_out_channel. There is no additional buffering.
   *)


class type enhanced_raw_in_channel =
object 
  inherit raw_in_channel
  method private enhanced_input_line : unit -> string
end


class buffered_raw_in_channel : 
        ?buffer_size:int ->     (* default: 4096 *)
	raw_in_channel ->
	  enhanced_raw_in_channel
  (* This class adds a buffer to the underlying raw_in_channel.
   * As additional feature, the method enhanced_input_line is a fast
   * version of input_line that profits from the buffer.
   *)

class buffered_raw_out_channel : 
        ?buffer_size:int ->     (* default: 4096 *)
	raw_out_channel ->
	  raw_out_channel
  (* This class adds a buffer to the underlying raw_out_channel. *)

(*********************** Channels over descriptors ********************)

class input_descr :
  ?start_pos_in:int ->
  Unix.file_descr ->
    raw_in_channel

class output_descr :
  ?start_pos_out:int ->
  Unix.file_descr ->
    raw_out_channel

class socket_descr :
  ?start_pos_in:int ->
  ?start_pos_out:int ->
  Unix.file_descr ->
    raw_io_channel


(********************** Transactional output channels *****************)

type close_mode = [ `Commit | `Rollback ];;

class buffered_trans_channel :
  ?close_mode:close_mode ->
  out_obj_channel ->
    trans_out_obj_channel
  (* Has a transaction buffer implemented as Buffer.t *)

val make_temporary_file : 
  ?mode:int -> ?limit:int -> ?tmp_directory:string -> ?tmp_prefix:string -> 
  unit ->
    (string * in_channel * out_channel)

  (* make_temporary_file():
   * Creates a temporary file in the directory ~tmp_directory with a name prefix
   * ~tmp_prefix and a unique suffix. The function returns the file name
   * and the opened file as out_channel and as in_channel. 
   * ~tmp_directory: By default the current directory
   * ~tmp_prefix: By default "netstring". It is better to have a prefix that
   *        is likely to be unique, e.g. the process ID, or the current time.
   * ~mode: The creation mask of the file; defaults to 0o600, i.e. the file
   *        is private for the current user
   * ~limit: Limits the number of trials to find the unique suffix. Defaults
   *        to 1000.
   *)

class tempfile_trans_channel :
  ?close_mode:close_mode ->
  ?tmp_directory:string ->
  ?tmp_prefix:string ->
  out_obj_channel ->
    trans_out_obj_channel
  (* Has a transaction buffer implemented as temporary file *)



(************************* Pipes and filters **************************)


class pipe :
  ?conv:(Netbuffer.t -> bool -> Netbuffer.t -> unit) ->
  unit ->
    io_obj_channel
  
  (* A pipe has two internal buffers (realized by Netbuffer). The
   * output methods of the class write to the incoming buffer. When
   * new data are appended to the incoming buffer, the conversion function
   * ~conv is called; the arguments are the incoming buffer and the outgoing
   * buffer. The conversion function must convert the data available in the
   * incoming buffer and append the result to the outgoing buffer. Finally,
   * the input methods of the class return the data found in the outgoing
   * buffer.
   *
   * The conversion function is called as follows:
   * conv incoming_buffer at_eof outgoing_buffer
   *
   * The conversion function is allowed to do nothing if the incoming data
   * are not complete enough to be converted. It is also allowed to convert
   * only the beginning of the incoming buffer.
   *
   * If the outgoing buffer is empty, the input methods will raise
   * Buffer_underrun.
   *
   * If close_out is invoked, the end of the data stream will be recorded.
   * In this case, the conversion function is called with at_eof = true,
   * and it is expected that this function converts the whole data found
   * in the incoming buffer.
   *
   * close_in implies close_out.
   *
   * The conversion function may raise exceptions. The exceptions will
   * fall through to the caller of the input methods. (The output methods
   * and close_in, close_out never fail because of such exceptions.)
   *
   * The default conversion function copies everything from the incoming
   * buffer to the outgoing buffer without modification.
   *)

class output_filter : io_obj_channel -> out_obj_channel -> out_obj_channel
  (* An [output_filter] filters the data written to it through the
   * [io_obj_channel] (usually a [pipe]), and writes the filtered data
   * to the passed [out_obj_channel].
   *
   * If the filter is closed, the [io_obj_channel] will be closed, too,
   * but not the destination [out_obj_channel].
   *)

class input_filter : in_obj_channel -> io_obj_channel -> in_obj_channel
  (* An [input_filter] filters the data read from it through the
   * [io_obj_channel] (usually a [pipe] after the data have been 
   * retrieved from the passed [in_obj_channel].
   *
   * An [input_filter] object never generates [Buffer_underrun] exceptions.
   * However, if the passed [in_obj_channel] or [io_obj_channel] raises such
   * an exception, the exception will fall through the calling chain.
   *
   * If the filter is closed, the [io_obj_channel] will be closed, too,
   * but not the source [in_obj_channel].
   *)

(* Note: If you have the choice, prefer [output_filter] over [input_filter].
 * The latter is slower.
 *
 * The primary application of filters is to encode or decode a channel
 * on the fly. For example, the following lines write a BASE64-encoded file:
 *
 * let ch = new output_channel (open_out "file.b64") in
 * let encoder = new Netencoding.Base64.encoding_pipe ~linelength:76 () in
 * let ch' = new output_filter encoder ch in
 * ... (* write to ch' *)
 * ch' # close_out();
 * ch  # close_out();  (* you must close both channels! *)
 *
 * All bytes written to ch' are BASE64-encoded and the encoded bytes are
 * written to ch.
 *
 * There are also pipes to decode BASE64, and to encode and decode the
 * "Quoted printable" format. Encoding and decoding work even if the
 * data is delivered in disadvantageous chunks, because the data is
 * "re-chunked" if needed. For example, BASE64 would require that data
 * arrive in multiples of three bytes, and to cope with that, the BASE64 pipe
 * only processes the prefix of the input buffer that is a multiple of three,
 * and defers the encoding of the extra bytes till the next opportunity.
 *)

(* ======================================================================
 * History:
 * 
 * $Log: netchannels.mli,v $
 * Revision 1.12  2002/10/26 20:47:37  stolpmann
 * 	Change: [flush] is now part of [raw_out_channel].
 * 	New: [augment_raw_in_channel], [augment_raw_out-channel],
 * [buffered_raw_in_channel], [buffered_raw_out_channel].
 * 	Fixed: [input_descr], [output_descr], [socket_descr] no longer
 * seek to find out the position.
 *
 * Revision 1.11  2002/10/24 23:33:23  stolpmann
 * 	New class output_null that simply discards any output
 *
 * Revision 1.10  2002/03/15 16:09:31  stolpmann
 * 	Added an example for the filter classes.
 *
 * Revision 1.9  2002/02/02 23:53:06  stolpmann
 * 	New: input_command, output_command
 *
 * Revision 1.8  2002/01/12 18:35:17  stolpmann
 * 	Added ?onclose for some output classes.
 *
 * Revision 1.7  2002/01/06 02:20:13  stolpmann
 * 	New optional arguments ?pos and ?len for class [input_string].
 *
 * Revision 1.6  2002/01/05 22:43:54  stolpmann
 * 	New: classes [input_filter], [output_filter]
 *
 * Revision 1.5  2002/01/02 22:55:09  stolpmann
 * 	Added: [input_netbuffer], [output_netbuffer], [pipe]. New
 * exception [Buffer_underrun].
 *
 * Revision 1.4  2001/12/22 09:17:15  pdoane
 * 	Added raw_*_channel types
 * 	Changed output to really_output for out_obj_channel
 * 	Added file_descr/socket_descr implementations
 * 	Renamed buffered_output_channel and tempfile_output_channel
 *
 * Revision 1.3  2001/09/30 00:01:33  stolpmann
 * 	New function: make_temporary_file.
 * 	For the class tempfile_output_channel: the default tmp_directory
 * is now "." (it is difficult to have a reasonable automatic tmp_directory
 * for the scope of netchannels only)
 * 	with_in/out_obj_channel: It is ok if the channel is already
 * closed.
 *
 * Revision 1.2  2001/09/28 21:20:05  stolpmann
 * 	New functions:
 * 	- string_of_in_obj_channel
 * 	- with_in_obj_channel, with_out_obj_channel
 *
 * Revision 1.1  2001/09/24 21:23:17  stolpmann
 * 	Initial revision.
 *
 * 
 *)
