Improved IO

David Teller <[email protected]>
Newsgroups gmane.comp.lang.ocaml.lib.devel
Message-ID <1219927355.7195.7.camel@Blefuscu>
    Hi,
 Here's a second patch, this time adding plenty of stuff to module IO:
* input stdin
* outputs stdout, stderr, stdnull
* outputting to a Buffer
* a printf that works correctly with %a and %t 
* a full reimplementation of module Printf for use with outputs
* reading/writing single precision floats
* reading enumerations of chars, lines, strings and all sorts of numbers
* writing enumerations of chars, lines, strings and all sorts of
numbers.

 Enclosed a new test case. Additional features are waiting for a word on
my improvements to Enum. Should I resend it?

Cheers and thanks to Richard for his nice piece on "how to get your code
into an open-source project",
 David


-- 
David Teller-Rajchenbach
 Security of Distributed Systems
  http://www.univ-orleans.fr/lifo/Members/David.Teller
 Angry researcher: French Universities need reforms, but the LRU act brings liquidations.

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

_______________________________________________
ocaml-lib-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ocaml-lib-devel
improved_io.patch (text/x-patch, 57 KB)
diff -Nur ocaml-extlib-read-only.orig/extlib/IO.ml ocaml-extlib-read-only/extlib/IO.ml
--- ocaml-extlib-read-only.orig/extlib/IO.ml	2008-08-28 14:33:26.000000000 +0200
+++ ocaml-extlib-read-only/extlib/IO.ml	2008-08-28 14:30:47.000000000 +0200
@@ -1,6 +1,7 @@
 (*
  * IO - Abstract input/output
  * Copyright (C) 2003 Nicolas Cannasse
+ *               2008 David Teller (contributor)
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -18,6 +19,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *)
 
+open ExtString
+
+
 type input = {
 	mutable in_read : unit -> char;
 	mutable in_input : string -> int -> int -> int;
@@ -35,8 +39,21 @@
 exception Input_closed
 exception Output_closed
 
-(* -------------------------------------------------------------- *)
-(* API *)
+(**
+   {6 Exception management}
+*)
+(** [apply_enum f x] applies [f] to [x] and converts exceptions
+    [No_more_input] and [Input_closed] to [Enum.No_more_elements]*)
+let apply_enum f x =
+  try f x
+  with No_more_input 
+    |  Input_closed  -> raise Enum.No_more_elements
+
+
+(**
+   {6 API}
+*)
+
 
 let default_close = (fun () -> ())
 
@@ -145,9 +162,6 @@
 	if p + l > sl || p < 0 || l < 0 then invalid_arg "IO.output";
 	o.out_output s p l
 
-let printf o fmt =
-	Printf.kprintf (fun s -> nwrite o s) fmt
-
 let flush o = o.out_flush()
 
 let close_out o =
@@ -211,8 +225,10 @@
 		out_flush = o.out_flush;
 	} , (fun () -> !p)
 
-(* -------------------------------------------------------------- *)
-(* Standard IO *)
+(**
+   {6 Standard IO}
+*)
+
 
 let input_string s =
 	let pos = ref 0 in
@@ -248,6 +264,13 @@
 		out_flush = (fun () -> ());
 	}
 
+let output_buffer buf =
+  {
+    out_write = Buffer.add_char buf;
+    out_output= (fun s p l -> Buffer.add_substring buf s p l; l);
+    out_close = (fun () -> Buffer.contents buf);
+    out_flush = (fun () -> ());
+  }
 let input_channel ch =
 	{
 		in_read = (fun () ->
@@ -316,6 +339,19 @@
 		);
 		out_flush = (fun () -> ());
 	}
+let comb (a,b) =
+  create_out ~write:(fun c -> 
+		       write a c;
+		       write b c)
+    ~output:(fun s i j ->
+	       let _ = output a s i j in
+		 output b s i j)
+    ~flush:(fun () ->
+	      flush a;
+	      flush b)
+    ~close:(fun () ->
+	      let _ = close_out a in
+	      close_out b)
 
 let pipe() =
 	let input = ref "" in
@@ -362,8 +398,10 @@
 
 external cast_output : 'a output -> unit output = "%identity"
 
-(* -------------------------------------------------------------- *)
-(* BINARY APIs *)
+(**
+   {6 Binary APIs}
+*)
+
 
 exception Overflow of string
 
@@ -375,7 +413,7 @@
 		c - 256
 	else
 		c
-
+  
 let read_string i =
 	let b = Buffer.create 8 in
 	let rec loop() =
@@ -467,6 +505,9 @@
 let read_double ch =
 	Int64.float_of_bits (read_i64 ch)
 
+let read_float ch =
+	Int32.float_of_bits (read_real_i32 ch)
+
 let write_byte o n =
 	(* doesn't test bounds of n in order to keep semantics of Pervasives.output_byte *)
 	write o (Char.unsafe_chr (n land 0xFF))
@@ -512,8 +553,12 @@
 let write_double ch f =
 	write_i64 ch (Int64.bits_of_float f)
 
-(* -------------------------------------------------------------- *)
-(* Big Endians *)
+let write_float ch f =
+        write_real_i32 ch (Int32.bits_of_float f)
+
+(**
+   {6 Big Endians}
+*)
 
 module BigEndian = struct
 
@@ -565,6 +610,9 @@
 let read_double ch =
 	Int64.float_of_bits (read_i64 ch)
 
+let read_float ch =
+	Int32.float_of_bits (read_real_i32 ch)
+
 let write_ui16 ch n =
 	if n < 0 || n > 0xFFFF then raise (Overflow "write_ui16");
 	write_byte ch (n lsr 8);
@@ -598,10 +646,59 @@
 let write_double ch f =
 	write_i64 ch (Int64.bits_of_float f)
 
+let write_float ch f =
+	write_real_i32 ch (Int32.bits_of_float f)
+
+let ui16s_of input     = Enum.from (fun () -> apply_enum read_ui16 input)
+
+let i16s_of input      = Enum.from (fun () -> apply_enum read_i16 input)
+
+let i32s_of input      = Enum.from (fun () -> apply_enum read_i32 input)
+
+let real_i32s_of input = Enum.from (fun () -> apply_enum read_real_i32 input)
+
+let i64s_of input      = Enum.from (fun () -> apply_enum read_i64 input)
+
+let doubles_of input   = Enum.from (fun () -> apply_enum read_double input)
+
+let floats_of input    = Enum.from (fun () -> apply_enum read_float input)
+
+let write_byte_enum output enum =
+  Enum.iter (write_byte output) enum
+
+let write_ui16_enum output enum =
+  Enum.iter (write_ui16 output) enum
+
+let write_i16_enum output enum =
+  Enum.iter (write_i16 output) enum
+
+let write_i32_enum output enum =
+  Enum.iter (write_i32 output) enum
+
+let write_real_i32_enum output enum =
+  Enum.iter (write_real_i32 output) enum
+
+let write_i64_enum output enum =
+  Enum.iter (write_i64 output) enum
+
+let write_double_enum output enum =
+  Enum.iter (write_double output) enum
+
+let write_float_enum output enum =
+  Enum.iter (write_float output) enum
+
+let write_string_enum output enum =
+  Enum.iter (write_string output) enum
+
+let write_line_enum output enum =
+  Enum.iter (write_line output) enum
+
+
 end
 
-(* -------------------------------------------------------------- *)
-(* Bits API *)
+(**
+   {6 Bits API}
+*)
 
 type 'a bc = {
 	ch : 'a;
@@ -676,8 +773,10 @@
 let flush_bits b =
 	if b.nbits > 0 then write_bits b (8 - b.nbits) 0
 
-(* -------------------------------------------------------------- *)
-(* Generic IO *)
+(**
+   {6 Generic IO}
+*)
+
 
 class in_channel ch =
   object
@@ -767,3 +866,696 @@
 		~output
 		~flush:ch#flush
 		~close:ch#close_out
+(**
+   {6 Enumerations}
+*)
+
+let bytes_of input        = Enum.from (fun () -> apply_enum read_byte input)
+
+let signed_bytes_of input = Enum.from (fun () -> apply_enum read_signed_byte input)
+
+let ui16s_of input        = Enum.from (fun () -> apply_enum read_ui16 input)
+
+let i16s_of input         = Enum.from (fun () -> apply_enum read_i16 input)
+
+let i32s_of input         = Enum.from (fun () -> apply_enum read_i32 input)
+
+let real_i32s_of input    = Enum.from (fun () -> apply_enum read_real_i32 input)
+
+let i64s_of input         = Enum.from (fun () -> apply_enum read_i64 input)
+
+let doubles_of input      = Enum.from (fun () -> apply_enum read_double input)
+
+let floats_of input       = Enum.from (fun () -> apply_enum read_float input)
+
+let strings_of input      = Enum.from (fun () -> apply_enum read_string input)
+
+let lines_of input        = Enum.from (fun () -> apply_enum read_line input)
+
+
+let buffer_size = 1024
+
+let chars_of the_input = Enum.concat (Enum.from (fun () -> 
+   apply_enum (fun source -> String.enum (nread source buffer_size)) the_input))
+
+let bits_of in_bits = Enum.from (fun () -> apply_enum read_bits in_bits 1)
+
+let write_byte_enum output enum =
+  Enum.iter (write_byte output) enum
+
+let write_ui16_enum output enum =
+  Enum.iter (write_ui16 output) enum
+
+let write_i16_enum output enum =
+  Enum.iter (write_i16 output) enum
+
+let write_i32_enum output enum =
+  Enum.iter (write_i32 output) enum
+
+let write_real_i32_enum output enum =
+  Enum.iter (write_real_i32 output) enum
+
+let write_i64_enum output enum =
+  Enum.iter (write_i64 output) enum
+
+let write_double_enum output enum =
+  Enum.iter (write_double output) enum
+
+let write_float_enum output enum =
+  Enum.iter (write_float output) enum
+
+let write_string_enum output enum =
+  Enum.iter (write_string output) enum
+
+let write_line_enum output enum =
+  Enum.iter (write_line output) enum
+
+let write_bits_enum ~nbits output enum =
+  Enum.iter (write_bits ~nbits output) enum
+
+(**
+   {6 Standard IO}
+*)
+
+
+let stdin = input_channel Pervasives.stdin
+let stdout= output_channel Pervasives.stdout
+let stderr= output_channel Pervasives.stderr
+let stdnull= create_out
+  ~write:ignore 
+  ~output:(fun _ _ l -> l)
+  ~flush:ignore
+  ~close:ignore
+
+(**
+   {6 Printf}
+
+   A reimplementation of Printf (with a few additional functions) based
+   on [output].    We provide an internal signature to limit the dangers
+   of {!Obj.magic}.
+
+   {b Note} this module is inlined because of circular dependencies (themselves
+   caused by the legacy definition of a function {!printf} in module {!IO}).
+*)
+
+module Printf :
+sig
+  val printf: ('b, 'a output, unit) format -> 'b
+    (**The usual [printf] function, prints to
+       [stdout].*)
+
+  val eprintf: ('b, 'a output, unit) format -> 'b
+    (**The usual [eprintf] function, prints to
+       [stderr].*)
+
+  val sprintf:  ('a, unit, string) format -> 'a
+    (** As [fprintf] but outputs are replaced with
+	strings. In particular, any function called with 
+	[%a] should have type [unit -> string].*)
+
+  val sprintf2: ('a, 'b output, unit, string) format4 -> 'a
+    (**As [printf] but produces a string instead
+       of printing to the output. By opposition to
+       [sprintf], only the result is changed with
+       respect to [printf], not the inner workings.*)
+
+
+  val fprintf: 'a output -> ('b, 'a output, unit) format -> 'b
+    (**General printf, prints to any output.*)
+
+  val ifprintf: _        -> ('b, 'a output, unit) format -> 'b
+    (**As [fprintf] but doesn't actually print anything.
+       Sometimes useful for debugging.*)
+
+  val bprintf: Buffer.t  -> ('a, Buffer.t, unit) format -> 'a
+    (**As [fprintf], but with buffers instead of outputs.*)
+
+  val bprintf2: Buffer.t  -> ('b, 'a output, unit) format -> 'b
+    (**As [printf] but writes to a buffer instead
+       of printing to the output. By opposition to
+       [bprintf], only the result is changed with
+       respect to [printf], not the inner workings.*)
+
+  val kfprintf : ('a output -> 'b) -> 'a output -> ('c, 'a output, unit, 'b) format4 -> 'c
+    (**Same as [fprintf], but instead of returning immediately, passes the [output] to its first
+       argument at the end of printing.*)
+
+  val ksprintf: (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b
+    (** Same as [sprintf] above, but instead of returning the string,
+	passes it to the first argument. *)
+  val ksprintf2: (string -> 'b) -> ('c, 'a output, unit, 'b) format4 -> 'c
+    (** Same as [sprintf2] above, but instead of returning the string,
+	passes it to the first argument. *)
+
+  val kbprintf : (Buffer.t -> 'a) ->
+       Buffer.t -> ('b, Buffer.t, unit, 'a) format4 -> 'b
+    (** Same as [bprintf], but instead of returning immediately,
+	passes the buffer to its first argument at the end of printing. *)
+  val kbprintf2 : (Buffer.t -> 'b) ->  Buffer.t -> ('c, 'a output, unit, 'b) format4 -> 'c
+    (** Same as [bprintf2], but instead of returning immediately,
+	passes the buffer to its first argument at the end of printing.*)
+
+  val kprintf : (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b
+
+  val mkprintf: ('a output -> 'b) -> 'a output -> ('c, 'a output, unit, 'b) format4 -> 'c
+end
+=
+struct
+external format_float: string -> float -> string
+  = "caml_format_float"
+external format_int: string -> int -> string
+  = "caml_format_int"
+external format_int32: string -> int32 -> string
+  = "caml_int32_format"
+external format_nativeint: string -> nativeint -> string
+  = "caml_nativeint_format"
+external format_int64: string -> int64 -> string
+  = "caml_int64_format"
+
+module Sformat = struct
+
+  type index;;
+
+  external unsafe_index_of_int : int -> index = "%identity";;
+  let index_of_int i =
+    if i >= 0 then unsafe_index_of_int i
+    else failwith ("index_of_int: negative argument " ^ string_of_int i);;
+  external int_of_index : index -> int = "%identity";;
+
+  let add_int_index i idx = index_of_int (i + int_of_index idx);;
+  let succ_index = add_int_index 1;;
+
+  external length : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> int
+    = "%string_length";;
+  external get : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> int -> char
+    = "%string_safe_get";;
+  external unsafe_get : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> int -> char
+    = "%string_unsafe_get";;
+  external unsafe_to_string : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> string
+    = "%identity";;
+  let sub fmt idx len =
+    String.sub (unsafe_to_string fmt) (int_of_index idx) len;;
+  let to_string fmt = sub fmt (unsafe_index_of_int 0) (length fmt);;
+
+end;;
+
+let bad_conversion sfmt i c =
+  invalid_arg
+    ("printf: bad conversion %" ^ String.make 1 c ^ ", at char number " ^
+     string_of_int i ^ " in format string ``" ^ sfmt ^ "''");;
+
+let bad_conversion_format fmt i c =
+  bad_conversion (Sformat.to_string fmt) i c;;
+
+let incomplete_format fmt =
+  invalid_arg
+    ("printf: premature end of format string ``" ^
+     Sformat.to_string fmt ^ "''");;
+
+(* Parses a string conversion to return the specified length and the padding direction. *)
+let parse_string_conversion sfmt =
+  let rec parse neg i =
+    if i >= String.length sfmt then (0, neg) else
+    match String.unsafe_get sfmt i with
+    | '1'..'9' ->
+      (int_of_string
+         (String.sub sfmt i (String.length sfmt - i - 1)),
+       neg)
+    | '-' -> parse true (succ i)
+    | _   -> parse neg  (succ i) in
+  try parse false 1 with Failure _ -> bad_conversion sfmt 0 's'
+
+(* Pad a (sub) string into a blank string of length [p],
+   on the right if [neg] is true, on the left otherwise. *)
+let pad_string pad_char p neg s i len =
+  if p = len && i = 0 then s else
+  if p <= len then String.sub s i len else
+  let res = String.make p pad_char in
+  if neg
+  then String.blit s i res 0 len
+  else String.blit s i res (p - len) len;
+  res
+
+(* Format a string given a %s format, e.g. %40s or %-20s.
+   To do: ignore other flags (#, +, etc)? *)
+let format_string sfmt s =
+  let (p, neg) = parse_string_conversion sfmt in
+  pad_string ' ' p neg s 0 (String.length s);;
+
+(* Extract a format string out of [fmt] between [start] and [stop] inclusive.
+   '*' in the format are replaced by integers taken from the [widths] list.
+   extract_format returns a string. *)
+let extract_format fmt start stop widths =
+  let start = succ start in
+  let b = Buffer.create (stop - start + 10) in
+  Buffer.add_char b '%';
+  let rec fill_format i widths =
+    if i <= stop then
+      match (Sformat.unsafe_get fmt i, widths) with
+      | ('*', h :: t) ->
+        Buffer.add_string b (string_of_int h);
+        let i = succ i in
+        fill_format i t
+      | ('*', []) ->
+        assert false (* should not happen *)
+      | (c, _) ->
+        Buffer.add_char b c; fill_format (succ i) widths in
+  fill_format start (List.rev widths);
+  Buffer.contents b;;
+
+let extract_format_int conv fmt start stop widths =
+   let sfmt = extract_format fmt start stop widths in
+   match conv with
+   | 'n' | 'N' ->
+     sfmt.[String.length sfmt - 1] <- 'u';
+     sfmt
+   | _ -> sfmt;;
+
+(* Returns the position of the next character following the meta format
+   string, starting from position [i], inside a given format [fmt].
+   According to the character [conv], the meta format string is
+   enclosed by the delimitors %{ and %} (when [conv = '{']) or %( and
+   %) (when [conv = '(']). Hence, [sub_format] returns the index of
+   the character following the [')'] or ['}'] that ends the meta format,
+   according to the character [conv]. *)
+let sub_format incomplete_format bad_conversion_format conv fmt i =
+  let len = Sformat.length fmt in
+  let rec sub_fmt c i =
+    let close = if c = '(' then ')' else (* '{' *) '}' in
+    let rec sub j =
+       if j >= len then incomplete_format fmt else
+       match Sformat.get fmt j with
+       | '%' -> sub_sub (succ j)
+       | _ -> sub (succ j)
+    and sub_sub j =
+       if j >= len then incomplete_format fmt else
+       match Sformat.get fmt j with
+       | '(' | '{' as c ->
+         let j = sub_fmt c (succ j) in sub (succ j)
+       | '}' | ')' as c ->
+         if c = close then succ j else bad_conversion_format fmt i c
+       | _ -> sub (succ j) in
+    sub i in
+  sub_fmt conv i;;
+
+let sub_format_for_printf conv =
+  sub_format incomplete_format bad_conversion_format conv;;
+
+let iter_on_format_args fmt add_conv add_char =
+
+  let lim = Sformat.length fmt - 1 in
+
+  let rec scan_flags skip i =
+    if i > lim then incomplete_format fmt else
+    match Sformat.unsafe_get fmt i with
+    | '*' -> scan_flags skip (add_conv skip i 'i')
+    | '#' | '-' | ' ' | '+' -> scan_flags skip (succ i)
+    | '_' -> scan_flags true (succ i)
+    | '0'..'9'
+    | '.' -> scan_flags skip (succ i)
+    | _ -> scan_conv skip i
+  and scan_conv skip i =
+    if i > lim then incomplete_format fmt else
+    match Sformat.unsafe_get fmt i with
+    | '%' | '!' -> succ i
+    | 's' | 'S' | '[' -> add_conv skip i 's'
+    | 'c' | 'C' -> add_conv skip i 'c'
+    | 'd' | 'i' |'o' | 'u' | 'x' | 'X' | 'N' -> add_conv skip i 'i'
+    | 'f' | 'e' | 'E' | 'g' | 'G' | 'F' -> add_conv skip i 'f'
+    | 'B' | 'b' -> add_conv skip i 'B'
+    | 'a' | 'r' | 't' as conv -> add_conv skip i conv
+    | 'l' | 'n' | 'L' as conv ->
+      let j = succ i in
+      if j > lim then add_conv skip i 'i' else begin
+        match Sformat.get fmt j with
+        | 'd' | 'i' | 'o' | 'u' | 'x' | 'X' ->
+          add_char (add_conv skip i conv) 'i'
+        | c -> add_conv skip i 'i' end
+    | '{' as conv ->
+      (* Just get a regular argument, skipping the specification. *)
+      let i = add_conv skip i conv in
+      (* To go on, find the index of the next char after the meta format. *)
+      let j = sub_format_for_printf conv fmt i in
+      (* Add the meta specification to the summary anyway. *)
+      let rec loop i =
+        if i < j - 2 then loop (add_char i (Sformat.get fmt i)) in
+      loop i;
+      (* Go on, starting at the closing brace to properly close the meta
+         specification in the summary. *)
+      scan_conv skip (j - 1)
+    | '(' as conv ->
+      (* Use the static format argument specification instead of
+         the runtime format argument value: they must have the same type
+         anyway. *)
+      scan_fmt (add_conv skip i conv)
+    | '}' | ')' as conv -> add_conv skip i conv
+    | conv -> bad_conversion_format fmt i conv
+
+  and scan_fmt i =
+    if i < lim then
+     if Sformat.get fmt i = '%'
+     then scan_fmt (scan_flags false (succ i))
+     else scan_fmt (succ i)
+    else i in
+
+  ignore (scan_fmt 0);;
+
+(* Returns a string that summarizes the typing information that a given
+   format string contains.
+   For instance, [summarize_format_type "A number %d\n"] is "%i".
+   It also checks the well-formedness of the format string. *)
+let summarize_format_type fmt =
+  let len = Sformat.length fmt in
+  let b = Buffer.create len in
+  let add_char i c = Buffer.add_char b c; succ i in
+  let add_conv skip i c =
+    if skip then Buffer.add_string b "%_" else Buffer.add_char b '%';
+    add_char i c in
+  iter_on_format_args fmt add_conv add_char;
+  Buffer.contents b;;
+
+module Ac = struct
+  type ac = {
+    mutable ac_rglr : int;
+    mutable ac_skip : int;
+    mutable ac_rdrs : int;
+  }
+end;;
+
+open Ac;;
+
+(* Computes the number of arguments of a format (including flag
+   arguments if any). *)
+let ac_of_format fmt =
+  let ac = { ac_rglr = 0; ac_skip = 0; ac_rdrs = 0; } in
+  let incr_ac skip c =
+    let inc = if c = 'a' then 2 else 1 in
+    if c = 'r' then ac.ac_rdrs <- ac.ac_rdrs + 1;
+    if skip
+    then ac.ac_skip <- ac.ac_skip + inc
+    else ac.ac_rglr <- ac.ac_rglr + inc in
+  let add_conv skip i c =
+    (* Just finishing a meta format: no additional argument to record. *)
+    if c <> ')' && c <> '}' then incr_ac skip c;
+    succ i
+  and add_char i c = succ i in
+
+  iter_on_format_args fmt add_conv add_char;
+  ac;;
+
+let count_arguments_of_format fmt =
+  let ac = ac_of_format fmt in
+  ac.ac_rglr + ac.ac_skip + ac.ac_rdrs;;
+
+let list_iter_i f l =
+  let rec loop i = function
+  | [] -> ()
+  | [x] -> f i x (* Tail calling [f] *)
+  | x :: xs -> f i x; loop (succ i) xs in
+  loop 0 l;;
+
+(* ``Abstracting'' version of kprintf: returns a (curried) function that
+   will print when totally applied.
+   Note: in the following, we are careful not to be badly caught
+   by the compiler optimizations on the representation of arrays. *)
+let kapr kpr fmt =
+  match count_arguments_of_format fmt with
+  | 0 -> kpr fmt [||]
+  | 1 -> Obj.magic (fun x ->
+      let a = Array.make 1 (Obj.repr 0) in
+      a.(0) <- x;
+      kpr fmt a)
+  | 2 -> Obj.magic (fun x y ->
+      let a = Array.make 2 (Obj.repr 0) in
+      a.(0) <- x; a.(1) <- y;
+      kpr fmt a)
+  | 3 -> Obj.magic (fun x y z ->
+      let a = Array.make 3 (Obj.repr 0) in
+      a.(0) <- x; a.(1) <- y; a.(2) <- z;
+      kpr fmt a)
+  | 4 -> Obj.magic (fun x y z t ->
+      let a = Array.make 4 (Obj.repr 0) in
+      a.(0) <- x; a.(1) <- y; a.(2) <- z;
+      a.(3) <- t;
+      kpr fmt a)
+  | 5 -> Obj.magic (fun x y z t u ->
+      let a = Array.make 5 (Obj.repr 0) in
+      a.(0) <- x; a.(1) <- y; a.(2) <- z;
+      a.(3) <- t; a.(4) <- u;
+      kpr fmt a)
+  | 6 -> Obj.magic (fun x y z t u v ->
+      let a = Array.make 6 (Obj.repr 0) in
+      a.(0) <- x; a.(1) <- y; a.(2) <- z;
+      a.(3) <- t; a.(4) <- u; a.(5) <- v;
+      kpr fmt a)
+  | nargs ->
+    let rec loop i args =
+      if i >= nargs then
+        let a = Array.make nargs (Obj.repr 0) in
+        list_iter_i (fun i arg -> a.(nargs - i - 1) <- arg) args;
+        kpr fmt a
+      else Obj.magic (fun x -> loop (succ i) (x :: args)) in
+    loop 0 [];;
+
+(* Get the index of the next argument to printf. *)
+let next_index n = Sformat.succ_index n;;
+
+(* Decode a format string and act on it.
+   [fmt] is the printf format string, and [pos] points to a [%] character.
+   After consuming the appropriate number of arguments and formatting
+   them, one of the five continuations is called:
+   [cont_s] for outputting a string (args: arg num, string, next pos)
+   [cont_a] for performing a %a action (args: arg num, fn, arg, next pos)
+   [cont_t] for performing a %t action (args: arg num, fn, next pos)
+   [cont_f] for performing a flush action (args: arg num, next pos)
+   [cont_m] for performing a %( action (args: arg num, sfmt, next pos)
+
+   "arg num" is the index in array args of the next argument to printf.
+   "next pos" is the position in [fmt] of the first character following
+   the %conversion specification in [fmt]. *)
+
+(* Note: here, rather than test explicitly against [Sformat.length fmt]
+   to detect the end of the format, we use [Sformat.unsafe_get] and
+   rely on the fact that we'll get a "nul" character if we access
+   one past the end of the string.  These "nul" characters are then
+   caught by the [_ -> bad_conversion] clauses below.
+   Don't do this at home, kids. *)
+let scan_format fmt args n pos cont_s cont_a cont_t cont_f cont_m =
+
+  let get_arg n =
+    Obj.magic (args.(Sformat.int_of_index n)) in
+
+  let rec scan_flags n widths i =
+    match Sformat.unsafe_get fmt i with
+    | '*' ->
+      let (width : int) = get_arg n in
+      scan_flags (next_index n) (width :: widths) (succ i)
+    | '0'..'9'
+    | '.' | '#' | '-' | ' ' | '+' -> scan_flags n widths (succ i)
+    | _ -> scan_conv n widths i
+
+  and scan_conv n widths i =
+    match Sformat.unsafe_get fmt i with
+    | '%' ->
+      cont_s n "%" (succ i)
+    | 's' | 'S' as conv ->
+      let (x : string) = get_arg n in
+      let x = if conv = 's' then x else "\"" ^ String.escaped x ^ "\"" in
+      let s =
+        (* optimize for common case %s *)
+        if i = succ pos then x else
+        format_string (extract_format fmt pos i widths) x in
+      cont_s (next_index n) s (succ i)
+    | 'c' | 'C' as conv ->
+      let (x : char) = get_arg n in
+      let s =
+        if conv = 'c' then String.make 1 x else "'" ^ Char.escaped x ^ "'" in
+      cont_s (next_index n) s (succ i)
+    | 'd' | 'i' | 'o' | 'u' | 'x' | 'X' | 'N' as conv ->
+      let (x : int) = get_arg n in
+      let s =
+        format_int (extract_format_int conv fmt pos i widths) x in
+      cont_s (next_index n) s (succ i)
+    | 'f' | 'e' | 'E' | 'g' | 'G' ->
+      let (x : float) = get_arg n in
+      let s = format_float (extract_format fmt pos i widths) x in
+      cont_s (next_index n) s (succ i)
+    | 'F' ->
+      let (x : float) = get_arg n in
+      cont_s (next_index n) (string_of_float x) (succ i)
+    | 'B' | 'b' ->
+      let (x : bool) = get_arg n in
+      cont_s (next_index n) (string_of_bool x) (succ i)
+    | 'a' ->
+      let printer = get_arg n in
+      let n = Sformat.succ_index n in
+      let arg = get_arg n in
+      cont_a (next_index n) printer arg (succ i)
+    | 't' ->
+      let printer = get_arg n in
+      cont_t (next_index n) printer (succ i)
+    | 'l' | 'n' | 'L' as conv ->
+      begin match Sformat.unsafe_get fmt (succ i) with
+      | 'd' | 'i' | 'o' | 'u' | 'x' | 'X' ->
+        let i = succ i in
+        let s =
+          match conv with
+          | 'l' ->
+            let (x : int32) = get_arg n in
+            format_int32 (extract_format fmt pos i widths) x
+          | 'n' ->
+            let (x : nativeint) = get_arg n in
+            format_nativeint (extract_format fmt pos i widths) x
+          | _ ->
+            let (x : int64) = get_arg n in
+            format_int64 (extract_format fmt pos i widths) x in
+        cont_s (next_index n) s (succ i)
+      | _ ->
+        let (x : int) = get_arg n in
+        let s = format_int (extract_format_int 'n' fmt pos i widths) x in
+        cont_s (next_index n) s (succ i)
+      end
+    | '!' -> cont_f n (succ i)
+    | '{' | '(' as conv (* ')' '}' *) ->
+      let (xf : ('a, 'b, 'c, 'd, 'e, 'f) format6) = get_arg n in
+      let i = succ i in
+      let j = sub_format_for_printf conv fmt i in
+      if conv = '{' (* '}' *) then
+        (* Just print the format argument as a specification. *)
+        cont_s
+          (next_index n)
+          (summarize_format_type xf)
+          j else
+        (* Use the format argument instead of the format specification. *)
+        cont_m (next_index n) xf j
+    | (* '(' *) ')' ->
+      cont_s n "" (succ i)
+    | conv ->
+      bad_conversion_format fmt i conv in
+
+  scan_flags n [] (succ pos);;
+
+(*Trimmed-down version of the legacy lib's [mkprintf]. Most of the generality
+  is lifted to [output] rather than [mkprintf] itself.*)
+let mkprintf k out fmt =
+
+  let rec pr k n fmt v =
+
+    let len = Sformat.length fmt in
+
+    let rec doprn n i =
+       if i >= len then Obj.magic (k out)
+       else             match Sformat.unsafe_get fmt i with
+	 | '%' -> scan_format fmt v n i cont_s cont_a cont_t cont_f cont_m
+	 |  c  -> write out c; doprn n (succ i)
+    and cont_s n s i =
+      nwrite out s; 
+      doprn n i
+    and cont_a n printer arg i =
+      printer out arg;
+      doprn n i
+    and cont_t n printer i =
+      printer out;
+      doprn n i
+    and cont_f n i =
+      flush out; 
+      doprn n i
+    and cont_m n xf i =
+      let m = Sformat.add_int_index (count_arguments_of_format xf) n in
+	pr (Obj.magic (fun _ -> doprn m i)) n xf v
+
+    in doprn n 0 
+  in let kpr = pr k (Sformat.index_of_int 0) in
+    kapr kpr fmt;;
+
+let fprintf out fmt = mkprintf ignore out fmt
+let printf      fmt = fprintf stdout fmt
+let eprintf     fmt = fprintf stderr fmt
+let ifprintf _  fmt = fprintf stdnull fmt
+let ksprintf2 k fmt =
+  let out = output_string () in
+    mkprintf (fun out -> k (close_out out)) out fmt
+let kbprintf2 k buf fmt =
+  let out = output_buffer buf in
+    mkprintf (fun out -> k buf) out fmt
+let sprintf2 fmt = ksprintf2 (Std.identity) fmt
+let bprintf2 buf fmt = kbprintf2 ignore buf fmt
+(**
+   Other possible implementation of [sprintf2],
+   left as example:
+
+[
+let sprintf2    fmt = 
+  let out = output_string () in
+    mkprintf (fun out -> close_out out) out fmt
+]
+*)
+(**
+   Other possible implementation of [bprintf2],
+   left as example:
+[
+let bprintf2 buf fmt = 
+  let out = output_buffer buf in
+    mkprintf ignore out fmt
+]*)
+
+let kfprintf        = mkprintf
+let bprintf         = Printf.bprintf
+let sprintf         = Printf.sprintf
+let ksprintf        = Printf.ksprintf
+let kbprintf        = Printf.kbprintf
+let kprintf         = Printf.kprintf
+end
+
+let printf = Printf.fprintf
+
+(**
+   {6 Utilities}
+*)
+
+let make_list_printer (p:('a output -> 'b -> unit)) 
+                      (a:string)
+		      (b:string)
+		      (s:string)
+		      (out:'a output)
+		      (l:'b list) = 
+  let rec aux out l = match l with
+  | []    -> ()
+  | [h]   -> p out h
+  | h::t  -> printf out "%a%s%a" p h s aux t
+  in printf out "%s%a%s" a aux l b
+
+
+
+(*
+  (**Commented out until changes to [Enum] and [ExtChar] are accepted.*)
+let tab_out n out =
+  let spaces   = String.make n ' ' in
+  create_out 
+    ~write: (fun c     -> 
+	       printf stderr "Attempting to write %C\n" c;
+	       out.out_write c;
+	       if Char.is_newline c then (
+		 printf stderr "Adding %S\n" spaces;
+		 nwrite out spaces)
+	    )
+    ~output:(fun s p l -> (*Replace each newline within the segment with newline^spaces*)
+	       printf stderr "Attempting to output %S\n" s;
+	       let length = String.length s                 in
+	       let buffer = Buffer.create (String.length s) in
+		 for i = p to min (length - 1) l do
+		   let c = String.unsafe_get s i in
+		     Buffer.add_char buffer c;
+		     if Char.is_newline c then
+		       Buffer.add_string buffer spaces
+		 done;
+		 let s' = Buffer.contents buffer                  in
+		 let _  = printf stderr "Replacing with %S\n" s' in
+		 output out s' 0 (String.length s'))
+    ~flush:out.out_flush
+    ~close:out.out_close
+
+let lmargin n p out x =
+  p (tab_out n out) x*)
+
+let combine = comb
diff -Nur ocaml-extlib-read-only.orig/extlib/IO.mli ocaml-extlib-read-only/extlib/IO.mli
--- ocaml-extlib-read-only.orig/extlib/IO.mli	2008-08-28 14:33:26.000000000 +0200
+++ ocaml-extlib-read-only/extlib/IO.mli	2008-08-28 14:30:47.000000000 +0200
@@ -1,6 +1,8 @@
 (* 
  * IO - Abstract input/output
  * Copyright (C) 2003 Nicolas Cannasse
+ *               2008 David Teller (contributor)
+ *               2008 Philippe Strauss (contributor)
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -43,6 +45,19 @@
 exception Output_closed
 (** This exception is raised when reading on a closed output. *)
 
+(** {6 Standard inputs/outputs} *)
+val stdin : input
+(** Standard input, as per Unix/Windows conventions (by default, keyboard).*)
+
+val stdout: unit output
+(** Standard output, as per Unix/Windows conventions (by default, console).*)
+
+val stderr: unit output
+(** Standard error output, as per Unix/Windows conventions.*)
+
+val stdnull: unit output
+(** An output which discards everything written to it.*)
+
 (** {6 Standard API} *)
 
 val read : input -> char
@@ -100,7 +115,10 @@
 (** Close the output and return its accumulator data.
   It can no longer be written. *)
 
-(** {6 Creation of IO Inputs/Outputs} *)
+(** {6 Creation of IO Inputs/Outputs} 
+
+    To open a file for reading/writing, see {!File.open_file_in}
+    and {!File.open_file_out}*)
 
 val input_string : string -> input
 (** Create an input that will read from a string. *)
@@ -109,11 +127,11 @@
 (** Create an output that will write into a string in an efficient way.
   When closed, the output returns all the data written into it. *)
 
-val input_channel : in_channel -> input
-(** Create an input that will read from a channel. *)
-
-val output_channel : out_channel -> unit output
-(** Create an output that will write into a channel. *) 
+val output_buffer : Buffer.t -> string output
+(** Create an output that will append its results at the end of a buffer
+    in an efficient way. Closing  returns the whole contents of the buffer
+    -- the buffer remains usable.*)
+    
 
 val input_enum : char Enum.t -> input
 (** Create an input that will read from an [enum]. *)
@@ -122,6 +140,10 @@
 (** Create an output that will write into an [enum]. The 
   final enum is returned when the output is closed. *)
 
+val combine : ('a output * 'a output) -> 'a output
+(** [combine (a,b)] creates a new [output] [c] such that
+    writing to [c] will actually write to both [a] and [b] *)
+
 val create_in :
   read:(unit -> char) ->
   input:(string -> int -> int -> int) -> close:(unit -> unit) -> input
@@ -133,10 +155,16 @@
   flush:(unit -> unit) -> close:(unit -> 'a) -> 'a output
 (** Fully create an output by giving all the needed functions. *)
 
-(** {6 Utilities} *)
+(*val tab_out : int -> 'a output -> 'a output
+(** Create an output shifted to the right by a number of white spaces.
+
+    [tab_out n out] produces a new output for writing into [out], in
+    which every new line starts with [n] white spaces.
+    Raises [Invalid_argument] if [n] < 0.*)*)
+
 
-val printf : 'a output -> ('b, unit, string, unit) format4 -> 'b
-(** The printf function works for any output. *)
+
+(** {6 Utilities} *)
 
 val read_all : input -> string
 (** read all the contents of the input until [No_more_input] is raised. *)
@@ -157,6 +185,8 @@
 (** You can safely transform any output to an unit output in a safe way 
   by using this function. *)
 
+
+
 (** {6 Binary files API}
 
 	Here is some API useful for working with binary files, in particular
@@ -190,6 +220,9 @@
 val read_i64 : input -> int64
 (** Read a signed 64-bit integer as an OCaml int64. *)
 
+val read_float : input -> float
+(** Read an IEEE single precision floating point value. *)
+
 val read_double : input -> float
 (** Read an IEEE double precision floating point value. *)
 
@@ -220,6 +253,9 @@
 val write_double : 'a output -> float -> unit
 (** Write an IEEE double precision floating point value. *)
 
+val write_float : 'a output -> float -> unit
+(** Write an IEEE single precision floating point value. *)
+
 val write_string : 'a output -> string -> unit
 (** Write a string and append an null character. *)
 
@@ -237,16 +273,35 @@
 	val read_real_i32 : input -> int32
 	val read_i64 : input -> int64
 	val read_double : input -> float
-	
+	val read_float: input -> float
 	val write_ui16 : 'a output -> int -> unit
 	val write_i16 : 'a output -> int -> unit
 	val write_i32 : 'a output -> int -> unit
 	val write_real_i32 : 'a output -> int32 -> unit
 	val write_i64 : 'a output -> int64 -> unit
 	val write_double : 'a output -> float -> unit
+	val write_float  : 'a output -> float -> unit
+
+	val ui16s_of : input -> int Enum.t
+	val i16s_of : input -> int Enum.t
+	val i32s_of : input -> int Enum.t
+	val real_i32s_of : input -> int32 Enum.t
+	val i64s_of : input -> int64 Enum.t
+	val doubles_of : input -> float Enum.t
+
+	val write_byte_enum : 'a output -> int Enum.t -> unit
+	val write_ui16_enum : 'a output -> int Enum.t -> unit
+	val write_i16_enum : 'a output -> int Enum.t -> unit
+	val write_i32_enum : 'a output -> int Enum.t -> unit
+	val write_real_i32_enum : 'a output -> int32 Enum.t -> unit
+	val write_i64_enum : 'a output -> int64 Enum.t -> unit
+	val write_double_enum : 'a output -> float Enum.t -> unit
+	val write_string_enum : 'a output -> string Enum.t -> unit
+	val write_line_enum : 'a output -> string Enum.t -> unit
 
 end
 
+
 (** {6 Bits API}
 
 	This enable you to read and write from an IO bit-by-bit or several bits
@@ -277,6 +332,17 @@
 val drop_bits : in_bits -> unit
 (** Drop up to 7 buffered bits and restart to next input character. *)
 
+(**
+   {6 For compatibility purposes}
+*)
+val input_channel : in_channel -> input
+(** Create an input that will read from a channel. *)
+
+val output_channel : out_channel -> unit output
+(** Create an output that will write into a channel. *) 
+
+
+
 (** {6 Generic IO Object Wrappers}
 
 	Theses OO Wrappers have been written to provide easy support of ExtLib
@@ -319,3 +385,226 @@
 val from_out_channel : #out_channel -> unit output
 val from_in_chars : #in_chars -> input
 val from_out_chars : #out_chars -> unit output
+
+(** {6 Enumeration API}*)
+
+val bytes_of : input -> int Enum.t
+(** Read an enumeration of unsigned 8-bit integers. *)
+
+val signed_bytes_of : input -> int Enum.t
+(** Read an enumeration of signed 8-bit integers. *)
+
+val ui16s_of : input -> int Enum.t
+(** Read an enumeration of unsigned 16-bit words. *)
+
+val i16s_of : input -> int Enum.t
+(** Read an enumartion of signed 16-bit words. *)
+
+val i32s_of : input -> int Enum.t
+(** Read an enumeration of signed 32-bit integers. Raise [Overflow] if the
+  read integer cannot be represented as a Caml 31-bit integer. *)
+
+val real_i32s_of : input -> int32 Enum.t
+(** Read an enumeration of signed 32-bit integers as OCaml [int32]s. *)
+
+val i64s_of : input -> int64 Enum.t
+(** Read an enumeration of signed 64-bit integers as OCaml [int64]s. *)
+
+val doubles_of : input -> float Enum.t
+(** Read an enumeration of IEEE double precision floating point values. *)
+
+val strings_of : input -> string Enum.t
+(** Read an enumeration of null-terminated strings. *)
+
+val lines_of : input -> string Enum.t
+(** Read an enumeration of LF or CRLF terminated strings. *)
+
+val chars_of : input -> char Enum.t
+(** Read an enumeration of Latin-1 characters. 
+
+    {b Note} Usually faster than calling [read] several times.*)
+
+val bits_of : in_bits -> int Enum.t
+(** Read an enumeration of bits *)
+
+val write_byte_enum : 'a output -> int Enum.t -> unit
+(** Write an enumeration of unsigned 8-bit bytes. *)
+
+val write_ui16_enum : 'a output -> int Enum.t -> unit
+(** Write an enumeration of unsigned 16-bit words. *)
+
+val write_i16_enum : 'a output -> int Enum.t -> unit
+(** Write an enumeration of signed 16-bit words. *)
+
+val write_i32_enum : 'a output -> int Enum.t -> unit
+(** Write an enumeration of signed 32-bit integers. *) 
+
+val write_real_i32_enum : 'a output -> int32 Enum.t -> unit
+(** Write an enumeration of OCaml int32s. *)
+
+val write_i64_enum : 'a output -> int64 Enum.t -> unit
+(** Write an enumeration of OCaml int64s. *)
+
+val write_double_enum : 'a output -> float Enum.t -> unit
+(** Write an enumeration of IEEE double precision floating point value. *)
+
+val write_string_enum : 'a output -> string Enum.t -> unit
+(** Write an enumeration of strings, appending null characters. *)
+
+val write_line_enum : 'a output -> string Enum.t -> unit
+(** Write an enumeration of lines, appending a LF (it might be converted
+    to CRLF on some systems depending on the underlying IO). *)
+
+val write_bits_enum : nbits:int -> out_bits -> int Enum.t -> unit
+(** Write an enumeration of bits*)
+
+(** {6 Printing} *)
+
+(** / *)
+val printf : 'a output -> ('b, 'a output, unit) format -> 'b
+(** A [fprintf]-style unparser. For more information
+    about printing, see the documentation of {!Printf}.
+
+    Obsoleted because of name clash: that's a [fprintf],
+    not a [printf].*)
+(** / *)
+(**
+   Usual [printf]-style functions, adapted for working with
+   [output]
+*)
+module Printf : sig
+  (** {6 Common functions}*)
+
+  val printf: ('b, 'a output, unit) format -> 'b
+    (**The usual [printf] function, prints to
+       [stdout].*)
+
+  val eprintf: ('b, 'a output, unit) format -> 'b
+    (**The usual [eprintf] function, prints to
+       [stderr].*)
+
+  val sprintf:  ('a, unit, string) format -> 'a
+    (** As [fprintf] but outputs are replaced with
+	strings. In particular, any function called with 
+	[%a] should have type [unit -> string].*)
+
+  val sprintf2: ('a, 'b output, unit, string) format4 -> 'a
+    (**As [printf] but produces a string instead
+       of printing to the output. By opposition to
+       [sprintf], only the result is changed with
+       respect to [printf], not the inner workings.*)
+
+  (** {6 General functions}*)
+  val fprintf: 'a output -> ('b, 'a output, unit) format -> 'b
+    (**General printf, prints to any output.*)
+
+  val ifprintf: _        -> ('b, 'a output, unit) format -> 'b
+    (**As [fprintf] but doesn't actually print anything.
+       Sometimes useful for debugging.*)
+
+  val bprintf: Buffer.t  -> ('a, Buffer.t, unit) format -> 'a
+    (**As [fprintf], but with buffers instead of outputs.
+       In particular, any unparser called with [%a] should
+       write to a buffer rather than to an output*)
+
+  val bprintf2: Buffer.t  -> ('b, 'a output, unit) format -> 'b
+    (**As [printf] but writes to a buffer instead
+       of printing to the output. By opposition to
+       [bprintf], only the result is changed with
+       respect to [printf], not the inner workings.*)
+
+  (**{6 Functions with continuations}*)
+    
+  val kfprintf : ('a output -> 'b) -> 'a output -> ('c, 'a output, unit, 'b) format4 -> 'c
+    (**Same as [fprintf], but instead of returning immediately, passes the [output] to its first
+       argument at the end of printing.*)
+
+  val ksprintf: (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b
+    (** Same as [sprintf] above, but instead of returning the string,
+	passes it to the first argument. *)
+  val ksprintf2: (string -> 'b) -> ('c, 'a output, unit, 'b) format4 -> 'c
+    (** Same as [sprintf2] above, but instead of returning the string,
+	passes it to the first argument. *)
+
+  val kbprintf : (Buffer.t -> 'a) ->
+       Buffer.t -> ('b, Buffer.t, unit, 'a) format4 -> 'b
+    (** Same as [bprintf], but instead of returning immediately,
+	passes the buffer to its first argument at the end of printing. *)
+  val kbprintf2 : (Buffer.t -> 'b) ->  Buffer.t -> ('c, 'a output, unit, 'b) format4 -> 'c
+    (** Same as [bprintf2], but instead of returning immediately,
+	passes the buffer to its first argument at the end of printing.*)
+
+    (**/**)
+  val kprintf : (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b
+    (** A deprecated synonym for [ksprintf]. *)
+    (**/**)
+
+    (**
+       {6 About formats}
+
+       Only read this if you intend to toy  with [mkprintf].
+
+      
+       {7 Format4}
+       [('a, 'b, 'c, 'd) format4] = is the type of arguments for
+       [printf]-style functions such that
+       - ['a] is the type of arguments, with a return type of ['d]
+         {ul
+         {- if your format looks like ["%s"], ['a] is [string -> 'd]}
+         {- if your format looks like ["%s%s"], ['a] is [string -> string -> 'd]}
+         {- ...}
+         }
+       - ['b] is the type of the first argument given to unparsers
+         (i.e. functions introduced with [%a] or [%t])
+         {ul
+         {- if your unparsers take a [unit] argument, ['b] should be 
+            [unit]}
+         {- if your unparsers take a [string output], ['b] should be 
+            [string output]}
+         {- ...}
+         }
+       - ['c] is the {b final} return type of unparsers
+         {ul
+         {- if you have an unparser introduced with [%t] and its result
+            has type [unit], ['c] should be [unit]
+         {- if you have an unparser introduced with [%a] and its type is
+            [string output -> string -> unit], ['c] should be [unit]}
+         {- ...}
+         }
+       - ['d] is the final return value of the function once all
+         arguments have been applied.
+
+       {7 Format}
+       [('a, 'b, c) format] is just a shortcut for [('a, 'b, 'c, 'd) format4].
+
+       {7 Important}
+       Note that [Obj.magic] is involved behind this, so be careful.
+    *)
+
+  val mkprintf: ('a output -> 'b) -> 'a output -> ('c, 'a output, unit, 'b) format4 -> 'c
+    (**Generic builder for [printf]-style functions.
+
+      [mkprintf k] builds a [fprintf]-style function which calls [k] upon the
+       channel once the evaluation of all arguments is complete.
+
+      Obj.magic is involved, {b Here Be Dragons}.
+*)
+end
+
+val make_list_printer: ('a output -> 'b -> unit) -> string -> string -> string -> ('a output -> 'b list -> unit)
+(** Make a list printer
+
+    [make_list_printer printer start_symbol end_symbol separator] creates a printer for
+    lists, which prints [start_symbol] at the beginning of the list, [end_symbol] at
+    the end, uses [printer] for each element of the contents and separates these
+    elements with [separator]
+*)
+
+(*val lmargin : int -> ('b output -> 'a -> unit) -> 'b output -> 'a -> unit
+(** [lmargin n p] behaves as [p], with the exception that every new line from this
+    point will be shifted to the right by [n] white spaces*)*)
+
+
+(**/**)
+val comb : ('a output * 'a output) -> 'a output
+(** Old name of [combine]*)
diff -Nur ocaml-extlib-read-only.orig/extlib/.svn/entries ocaml-extlib-read-only/extlib/.svn/entries
--- ocaml-extlib-read-only.orig/extlib/.svn/entries	2008-08-28 14:33:26.000000000 +0200
+++ ocaml-extlib-read-only/extlib/.svn/entries	2008-08-28 14:08:56.000000000 +0200
@@ -32,7 +32,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 99ee33dfaca5b476b251612af369253d
 2007-12-28T20:23:32.408536Z
 361
@@ -44,7 +44,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 35a8275bb8888c83d5c07899aa7cd718
 2007-12-28T20:23:32.408536Z
 361
@@ -56,7 +56,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 cd6f16b4f39c9c8e28377a9ed6f38d2c
 2004-07-26T19:11:14.000000Z
 231
@@ -68,7 +68,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 87f07e547cd66bfac4928e074ccc808d
 2007-12-28T20:23:32.408536Z
 361
@@ -80,7 +80,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 337baf3ae6550941e5e9c8b6c0f9373c
 2007-12-28T20:23:32.408536Z
 361
@@ -92,7 +92,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 890ecca9fa4a02cbfce25324827463b4
 2007-12-28T20:23:32.408536Z
 361
@@ -104,7 +104,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 3726001556e37089a57cce789671b045
 2007-12-28T20:23:32.408536Z
 361
@@ -116,7 +116,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 c6b23c4d7f4d327a4f04f6f4280816c1
 2006-03-07T11:07:22.000000Z
 346
@@ -128,7 +128,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 295efb5994c0045f0b2db8342ce742f1
 2005-05-07T08:51:04.000000Z
 317
@@ -140,7 +140,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 c722b2a6827a7fe53dbb2f607ef94125
 2007-04-08T17:23:41.000000Z
 351
@@ -152,7 +152,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 28f666c5cb1d03a34e7306f1cf44103c
 2005-02-08T10:04:19.000000Z
 308
@@ -164,7 +164,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 ef9e13836bc4ac05ac3170d9c8de592c
 2005-12-06T20:47:38.000000Z
 342
@@ -176,7 +176,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 2164faa5f23827a771429c96303cea51
 2008-04-23T12:03:57.089785Z
 380
@@ -188,7 +188,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 593d007dd9f2c22e6bd0bbea2d0d4bbf
 2004-09-20T14:09:33.000000Z
 257
@@ -200,7 +200,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 68e1fb912a508c90d5ce62578ece4a11
 2005-05-19T08:01:04.000000Z
 321
@@ -212,7 +212,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 08d15fdba87860e6879ace4101846003
 2006-02-20T16:04:42.000000Z
 344
@@ -224,7 +224,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 8e050d62b1bc6a3f6fdab6c0fb0b662d
 2004-05-06T09:44:41.000000Z
 199
@@ -236,7 +236,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 1236bbeadb622950524e504d01e75143
 2004-05-06T09:44:41.000000Z
 199
@@ -248,7 +248,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 364041e3fdd415c7d7d7fd4502b27b8e
 2007-12-28T20:23:32.408536Z
 361
@@ -260,7 +260,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 b6ff2f96d0de2d3001c304d13eb6135b
 2004-05-06T09:44:41.000000Z
 199
@@ -272,7 +272,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 6f609ef2adc95047b9c2cd07050394a0
 2005-05-07T08:51:04.000000Z
 317
@@ -284,7 +284,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 24766e36ac2fc85bdf70256e819e076a
 2004-10-26T06:58:20.000000Z
 262
@@ -296,7 +296,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 898b8daad72357979be000180f6b9a52
 2004-05-06T09:44:41.000000Z
 199
@@ -308,7 +308,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 d5c84916a0118eefacae63f44ba285ff
 2005-11-24T15:20:07.000000Z
 331
@@ -320,7 +320,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 bf250921a42c0cc75c2cb97758e8cdfb
 2005-02-17T23:51:06.000000Z
 312
@@ -332,7 +332,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 4150da91e7254bc321bb206ef240f881
 2004-05-06T09:44:41.000000Z
 199
@@ -344,7 +344,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 d7e7a3956a079c9dc0c63eb965d60231
 2006-03-06T20:53:18.000000Z
 345
@@ -356,7 +356,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 d4d740323afde7f715e0a65f280f1f03
 2007-11-27T19:46:53.000000Z
 352
@@ -368,7 +368,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 2cb67f54588406f885ca9d1e7aa14535
 2004-05-06T09:44:41.000000Z
 199
@@ -380,7 +380,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 b8ee9fc8e266faa70a62f2c7f1c1cce5
 2004-10-29T06:51:19.000000Z
 264
@@ -392,7 +392,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 c978a2326e4157eab96c905f0edbb2bf
 2004-05-06T09:44:41.000000Z
 199
@@ -404,7 +404,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 64b587ebdb7881dee0a8c95486b0d8b4
 2004-05-06T09:44:41.000000Z
 199
@@ -416,7 +416,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 d2b4287875082f6c7db677815efaa30a
 2008-02-02T09:32:49.231726Z
 377
@@ -428,7 +428,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 470d1ea88b0525e67b22081faebac6ab
 2005-05-19T10:48:33.000000Z
 322
@@ -440,7 +440,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 1fea2302f5132fdf00f1be95a470dd22
 2005-05-19T08:01:04.000000Z
 321
@@ -452,7 +452,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 e3c145db93ca187a345a825d8a771102
 2004-07-18T18:55:03.000000Z
 227
@@ -464,7 +464,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 b0e5cce623c9edfe7be9dcfa5345feda
 2004-05-06T09:44:41.000000Z
 199
@@ -476,7 +476,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 e175f675b4803db7f7c00f0e2173545b
 2006-10-11T16:24:42.000000Z
 348
@@ -488,7 +488,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 18eee13484de3553fe50c1a18c676459
 2004-05-06T09:44:41.000000Z
 199
@@ -500,7 +500,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 56fb3575c3cdb1c6b1b347a3280f1f6f
 2006-03-06T20:53:18.000000Z
 345
@@ -512,7 +512,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 abc642714df74739f43a3bf7b397f5fb
 2004-12-22T07:59:46.000000Z
 273
@@ -524,7 +524,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 4e700f79a816a7e85c755977da4a6b0e
 2004-05-06T09:44:41.000000Z
 199
@@ -536,7 +536,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 f32671fc0fa94dcc7afdf04ebe195e90
 2004-05-22T07:03:36.000000Z
 203
@@ -548,7 +548,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 95aba1b949f632b25fa7adb23b85f434
 2008-04-23T12:03:57.089785Z
 380
@@ -560,7 +560,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 1d9d64297b26eb729f95dc1fd3d43586
 2007-12-25T11:32:35.000000Z
 355
@@ -572,7 +572,7 @@
 
 
 
-2008-08-28T12:33:26.000000Z
+2008-08-28T12:08:56.000000Z
 63b9e3f062ed924f877f47bb73a0e7a9
 2005-12-07T13:47:19.000000Z
 343
Binary files ocaml-extlib-read-only.orig/test/mktest.cmi and ocaml-extlib-read-only/test/mktest.cmi differ
Binary files ocaml-extlib-read-only.orig/test/mktest.cmo and ocaml-extlib-read-only/test/mktest.cmo differ
diff -Nur ocaml-extlib-read-only.orig/test/.svn/entries ocaml-extlib-read-only/test/.svn/entries
--- ocaml-extlib-read-only.orig/test/.svn/entries	2008-08-28 14:33:24.000000000 +0200
+++ ocaml-extlib-read-only/test/.svn/entries	2008-08-28 14:08:54.000000000 +0200
@@ -32,7 +32,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 02f4ced36486a952b32f40a1318d1241
 2005-11-25T10:22:19.000000Z
 341
@@ -44,7 +44,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 76da4f9021b2620a91978c15c58ec90c
 2004-12-23T22:18:45.000000Z
 285
@@ -56,7 +56,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 7a65ec4cd15861fbddfd3752e01a566b
 2007-12-30T19:53:02.568798Z
 375
@@ -68,7 +68,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 4045f1e0d443cf020e6e414a515e4318
 2007-04-08T11:57:44.000000Z
 349
@@ -80,7 +80,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 a8595307fa012a4f3b988f5185059e3a
 2004-12-28T12:12:17.000000Z
 302
@@ -92,7 +92,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 8d3d4b7d013a4197c7e0dc4e41b498ad
 2004-12-27T21:51:20.000000Z
 298
@@ -104,7 +104,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 f40e7d33885a0fa12a73d7f6c69892a9
 2005-03-02T19:25:44.000000Z
 315
@@ -116,7 +116,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 24c7132b9c1fee2b5070a1a880dec428
 2004-12-27T19:16:17.000000Z
 296
@@ -128,7 +128,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 8b1ef9caf456924251711880f17b3ba8
 2005-02-15T10:11:27.000000Z
 310
@@ -140,7 +140,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 95f574a8bd1756a9a410994dff4fecc9
 2005-01-15T13:10:34.000000Z
 306
@@ -152,7 +152,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 5a987c0aa0de3883ff3415978c1d6414
 2007-04-08T11:57:44.000000Z
 349
@@ -164,7 +164,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 4f74305685d74ea7eb4095d21c03977c
 2007-04-08T17:18:47.000000Z
 350
@@ -176,7 +176,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 18083c760fe60825217ff15fddbd77e7
 2004-12-21T06:30:55.000000Z
 272
@@ -188,7 +188,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 100e2fcbf5cafabc63241e8a5b67e7fd
 2007-12-25T11:29:56.000000Z
 354
@@ -203,7 +203,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 845b53b567667c6aa9ca4e1e20c41128
 2004-12-24T10:49:00.000000Z
 287
@@ -215,7 +215,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 1d9ace098b2a2b02be7908ecbeae79d0
 2007-12-30T19:53:02.568798Z
 375
@@ -227,7 +227,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 d69aceb57a22dbbe2f0e586b21cca669
 2007-12-25T12:02:18.000000Z
 357
@@ -239,7 +239,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 14aed34e8eb25c7e9fdbeae72b904b35
 2008-04-23T18:09:27.770031Z
 381
@@ -251,7 +251,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 38e25db1b067e5f5749e74c94b00be31
 2007-12-30T19:53:02.568798Z
 375
@@ -263,7 +263,7 @@
 
 
 
-2008-08-28T12:33:24.000000Z
+2008-08-28T12:08:54.000000Z
 945e851a2d06d0a92e430ac75a6ae871
 2007-11-27T19:46:53.000000Z
 352
diff -Nur ocaml-extlib-read-only.orig/test/test_dt_IO_printing001.ml ocaml-extlib-read-only/test/test_dt_IO_printing001.ml
--- ocaml-extlib-read-only.orig/test/test_dt_IO_printing001.ml	1970-01-01 01:00:00.000000000 +0100
+++ ocaml-extlib-read-only/test/test_dt_IO_printing001.ml	2008-08-28 14:24:13.000000000 +0200
@@ -0,0 +1,56 @@
+(**
+   A few tests for the new Printf-style functions
+   of module [IO.Printf].
+*)
+
+open IO
+open IO.Printf
+
+let counter = ref 0
+
+let count out r = 
+  incr r;
+  fprintf out "%i" !r
+
+let test_printf () = 
+  printf  "Counting %-*s %a %S\n" 15 "printf"  count counter "[complete]"
+
+let test_eprintf () =
+  eprintf "Counting %-*s %a %S\n" 15 "eprintf" count counter "[complete]"
+
+let test_sprintf2 () =
+  let result = sprintf2 "Counting %-*s %a %S\n" 15 "sprintf2" count counter "[complete]" in
+    assert (result = "Testing sprintf2 3 \"[complete]\"\n");
+    printf  "%s" result
+
+
+let test_bprintf2 () =
+  let buf = Buffer.create 10 in
+    bprintf2 buf "Counting %-*s %a %S\n" 15 "bprintf2" count counter "[complete]";
+    let result = Buffer.contents buf in
+    assert (result = "Testing bprintf2 4 \"[complete]\"\n");
+    printf "%s" result
+
+let test_kfprintf () =
+    kfprintf (fun out -> fprintf stdout "\n") stdout "Counting %-*s %a %S" 15 "kfprintf" count counter "[complete]"
+
+let test_ksprintf2 () =
+    ksprintf2 (fun result -> 
+		 assert (result = "Testing ksprintf2 6 \"[complete]\"\n");
+		 fprintf stdout "%s\n" result) "Counting %-*s %a %S" 15 "ksprintf2" count counter "[complete]"
+
+let test_kbprintf2 () =
+  let buf = Buffer.create 10 in
+    kbprintf2 (fun buf -> 
+		 let result = Buffer.contents buf in
+		 assert (result = "Testing kbprintf2 7 \"[complete]\"\n");
+		 fprintf stdout "%s\n" result) buf "Counting %-*s %a %S" 15 "kbprintf7" count counter "[complete]"
+
+let test () = 
+  Util.run_test ~test_name:"dt_IO.Printf.printf" test_printf;
+  Util.run_test ~test_name:"dt_IO.Printf.eprintf" test_eprintf;
+  Util.run_test ~test_name:"dt_IO.Printf.sprintf2" test_sprintf2;
+  Util.run_test ~test_name:"dt_IO.Printf.bprintf2" test_bprintf2;
+  Util.run_test ~test_name:"dt_IO.Printf.kfprintf" test_kfprintf;
+  Util.run_test ~test_name:"dt_IO.Printf.ksprintf2" test_ksprintf2;
+  Util.run_test ~test_name:"dt_IO.Printf.kbprintf2" test_kbprintf2
diff -Nur ocaml-extlib-read-only.orig/test/util/zlib-test/.svn/entries ocaml-extlib-read-only/test/util/zlib-test/.svn/entries
--- ocaml-extlib-read-only.orig/test/util/zlib-test/.svn/entries	2008-08-28 14:33:23.000000000 +0200
+++ ocaml-extlib-read-only/test/util/zlib-test/.svn/entries	2008-08-28 14:08:54.000000000 +0200
@@ -32,7 +32,7 @@
 
 
 
-2008-08-28T12:33:23.000000Z
+2008-08-28T12:08:54.000000Z
 e466f98334d72d431e31a5d313667dfa
 2007-12-25T11:55:54.000000Z
 356
@@ -45,7 +45,7 @@
 
 
 
-2008-08-28T12:33:23.000000Z
+2008-08-28T12:08:54.000000Z
 93cb00f8763621d80d39b54902fdab3b
 2007-12-25T12:04:42.000000Z
 358
@@ -57,7 +57,7 @@
 
 
 
-2008-08-28T12:33:23.000000Z
+2008-08-28T12:08:54.000000Z
 ff9873126cd57adcc14c992c7d27a733
 2007-12-25T11:55:54.000000Z
 356
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.