ocaml: Fix bindings when a function takes more than 10 parameters.
[libguestfs.git] / generator / generator_ocaml.ml
index ced6fb4..4f7548c 100644 (file)
@@ -1,5 +1,5 @@
 (* libguestfs
- * Copyright (C) 2009-2010 Red Hat Inc.
+ * Copyright (C) 2009-2011 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,15 +28,28 @@ open Generator_optgroups
 open Generator_actions
 open Generator_structs
 open Generator_c
+open Generator_events
 
 (* Generate the OCaml bindings interface. *)
 let rec generate_ocaml_mli () =
   generate_header OCamlStyle LGPLv2plus;
 
   pr "\
-(** For API documentation you should refer to the C API
-    in the guestfs(3) manual page.  The OCaml API uses almost
-    exactly the same calls. *)
+(** libguestfs bindings for OCaml.
+
+    For API documentation, the canonical reference is the
+    {{:http://libguestfs.org/guestfs.3.html}guestfs(3)} man page.
+    The OCaml API uses almost exactly the same calls.
+
+    For examples written in OCaml see the
+    {{:http://libguestfs.org/guestfs-ocaml.3.html}guestfs-ocaml(3)} man page.
+    *)
+
+(** {2 Module style API}
+
+    This is the module-style API.  There is also an object-oriented API
+    (see the end of this file and {!guestfs})
+    which is functionally completely equivalent, but is more compact. *)
 
 type t
 (** A [guestfs_h] handle. *)
@@ -45,51 +58,90 @@ exception Error of string
 (** This exception is raised when there is an error. *)
 
 exception Handle_closed of string
-(** This exception is raised if you use a {!Guestfs.t} handle
+(** This exception is raised if you use a {!t} handle
     after calling {!close} on it.  The string is the name of
     the function. *)
 
 val create : unit -> t
-(** Create a {!Guestfs.t} handle. *)
+(** Create a {!t} handle. *)
 
 val close : t -> unit
-(** Close the {!Guestfs.t} handle and free up all resources used
+(** Close the {!t} handle and free up all resources used
     by it immediately.
 
     Handles are closed by the garbage collector when they become
     unreferenced, but callers can call this in order to provide
     predictable cleanup. *)
 
-type progress_cb = int -> int -> int64 -> int64 -> unit
+type event =
+";
+  List.iter (
+    fun (name, _) ->
+      pr "  | EVENT_%s\n" (String.uppercase name)
+  ) events;
+  pr "\n";
+
+  pr "\
+val event_all : event list
+(** A list containing all event types. *)
 
-val set_progress_callback : t -> progress_cb -> unit
-(** [set_progress_callback g f] sets [f] as the progress callback function.
-    For some long-running functions, [f] will be called repeatedly
-    during the function with progress updates.
+type event_handle
+(** The opaque event handle which can be used to delete event callbacks. *)
 
-    The callback is [f proc_nr serial position total].  See
-    the description of [guestfs_set_progress_callback] in guestfs(3)
-    for the meaning of these four numbers.
+type event_callback =
+  t -> event -> event_handle -> string -> int64 array -> unit
+(** The event callback. *)
+
+val set_event_callback : t -> event_callback -> event list -> event_handle
+(** [set_event_callback g f es] sets [f] as the event callback function
+    for all events in the set [es].
 
     Note that if the closure captures a reference to the handle,
     this reference will prevent the handle from being
-    automatically closed by the garbage collector.  There are
-    three ways to avoid this: be careful not to capture the handle
-    in the closure, or use a weak reference, or call
-    {!Guestfs.clear_progress_callback} to remove the reference. *)
+    automatically closed by the garbage collector.  Since the
+    handle is passed to the event callback, with careful programming
+    it should be possible to avoid capturing the handle in the closure. *)
+
+val delete_event_callback : t -> event_handle -> unit
+(** [delete_event_callback g eh] removes a previously registered
+    event callback.  See {!set_event_callback}. *)
+
+val last_errno : t -> int
+(** [last_errno g] returns the last errno that happened on the handle [g]
+    (or [0] if there was no errno).  Note that the returned integer is the
+    raw errno number, and it is {i not} related to the {!Unix.error} type.
+
+    [last_errno] can be overwritten by subsequent operations on a handle,
+    so if you want to capture the errno correctly, you must call this
+    in the {!Error} exception handler, before any other operation on [g]. *)
 
-val clear_progress_callback : t -> unit
-(** [clear_progress_callback g] removes any progress callback function
-    associated with the handle.  See {!Guestfs.set_progress_callback}. *)
+val user_cancel : t -> unit
+(** Cancel current transfer.  This is safe to call from OCaml signal
+    handlers and threads. *)
 
 ";
   generate_ocaml_structure_decls ();
 
   (* The actions. *)
   List.iter (
-    fun (name, style, _, _, _, shortdesc, _) ->
+    fun (name, style, _, flags, _, shortdesc, _) ->
+      let deprecated =
+        try Some (find_map (function DeprecatedBy fn -> Some fn | _ -> None)
+                    flags)
+        with Not_found -> None in
+      let in_docs = not (List.mem NotInDocs flags) in
+
       generate_ocaml_prototype name style;
-      pr "(** %s *)\n" shortdesc;
+
+      if in_docs then (
+        pr "(** %s" shortdesc;
+        (match deprecated with
+         | None -> ()
+         | Some replacement ->
+             pr "\n\n    @deprecated Use {!%s} instead\n" replacement
+        );
+        pr " *)\n";
+      );
       pr "\n"
   ) all_functions_sorted;
 
@@ -97,26 +149,31 @@ val clear_progress_callback : t -> unit
 (** {2 Object-oriented API}
 
     This is an alternate way of calling the API using an object-oriented
-    style, so you can use [g#add_drive_opts filename] instead of
-    [Guestfs.add_drive_opts g filename].  Apart from the different style,
-    it offers exactly the same functionality.
+    style, so you can use
+    [g#]{{!guestfs.add_drive_opts}add_drive_opts} [filename]
+    instead of [Guestfs.add_drive_opts g filename].
+    Apart from the different style, it offers exactly the same functionality.
 
     Calling [new guestfs ()] creates both the object and the handle.
     The object and handle are closed either implicitly when the
-    object is garbage collected, or explicitly by calling the [g#close ()]
-    method.
+    object is garbage collected, or explicitly by calling the
+    [g#]{{!guestfs.close}close} [()] method.
 
-    You can get the {!Guestfs.t} handle by calling [g#ocaml_handle].
+    You can get the {!t} handle by calling
+    [g#]{{!guestfs.ocaml_handle}ocaml_handle}.
 
     Note that methods that take no parameters (except the implicit handle)
     get an extra unit [()] parameter.  This is so you can create a
-    closure from the method easily.  For example [g#get_verbose ()]
+    closure from the method easily.  For example
+    [g#]{{!guestfs.get_verbose}get_verbose} [()]
     calls the method, whereas [g#get_verbose] is a function. *)
 
 class guestfs : unit -> object
   method close : unit -> unit
-  method set_progress_callback : progress_cb -> unit
-  method clear_progress_callback : unit -> unit
+  method set_event_callback : event_callback -> event list -> event_handle
+  method delete_event_callback : event_handle -> unit
+  method last_errno : unit -> int
+  method user_cancel : unit -> unit
   method ocaml_handle : t
 ";
 
@@ -147,12 +204,38 @@ exception Handle_closed of string
 external create : unit -> t = \"ocaml_guestfs_create\"
 external close : t -> unit = \"ocaml_guestfs_close\"
 
-type progress_cb = int -> int -> int64 -> int64 -> unit
+type event =
+";
+  List.iter (
+    fun (name, _) ->
+      pr "  | EVENT_%s\n" (String.uppercase name)
+  ) events;
+  pr "\n";
+
+  pr "\
+let event_all = [
+";
+  List.iter (
+    fun (name, _) ->
+      pr "  EVENT_%s;\n" (String.uppercase name)
+  ) events;
+
+  pr "\
+]
+
+type event_handle = int
+
+type event_callback =
+  t -> event -> event_handle -> string -> int64 array -> unit
 
-external set_progress_callback : t -> progress_cb -> unit
-  = \"ocaml_guestfs_set_progress_callback\"
-external clear_progress_callback : t -> unit
-  = \"ocaml_guestfs_clear_progress_callback\"
+external set_event_callback : t -> event_callback -> event list -> event_handle
+  = \"ocaml_guestfs_set_event_callback\"
+external delete_event_callback : t -> event_handle -> unit
+  = \"ocaml_guestfs_delete_event_callback\"
+
+external last_errno : t -> int = \"ocaml_guestfs_last_errno\"
+
+external user_cancel : t -> unit = \"ocaml_guestfs_user_cancel\" \"noalloc\"
 
 (* Give the exceptions names, so they can be raised from the C code. *)
 let () =
@@ -175,8 +258,10 @@ class guestfs () =
   let g = create () in
   object
     method close () = close g
-    method set_progress_callback = set_progress_callback g
-    method clear_progress_callback () = clear_progress_callback g
+    method set_event_callback = set_event_callback g
+    method delete_event_callback = delete_event_callback g
+    method last_errno () = last_errno g
+    method user_cancel () = user_cancel g
     method ocaml_handle = g
 ";
 
@@ -355,13 +440,23 @@ copy_table (char * const * argv)
       pr ")\n";
       pr "{\n";
 
+      (* CAMLparam<N> can only take up to 5 parameters.  Further parameters
+       * have to be passed in groups of 5 to CAMLxparam<N> calls.
+       *)
       (match params with
-       | [p1; p2; p3; p4; p5] ->
-           pr "  CAMLparam5 (%s);\n" (String.concat ", " params)
        | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
            pr "  CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
-           pr "  CAMLxparam%d (%s);\n"
-             (List.length rest) (String.concat ", " rest)
+           let rec loop = function
+             | [] -> ()
+             | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
+               pr "  CAMLxparam5 (%s);\n"
+                 (String.concat ", " [p1; p2; p3; p4; p5]);
+               loop rest
+             | rest ->
+               pr "  CAMLxparam%d (%s);\n"
+                 (List.length rest) (String.concat ", " rest)
+           in
+           loop rest
        | ps ->
            pr "  CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
       );