ocaml: General improvements to generated code.
[libguestfs.git] / src / generator.ml
index cea5178..8b83496 100755 (executable)
@@ -142,6 +142,7 @@ and argt =
   | DeviceList of string(* list of Device names (each cannot be NULL) *)
   | Bool of string     (* boolean *)
   | Int of string      (* int (smallish ints, signed, <= 31 bits) *)
+  | Int64 of string    (* any 64 bit int *)
     (* These are treated as filenames (simple string parameters) in
      * the C API and bindings.  But in the RPC protocol, we transfer
      * the actual file content up to or down from the daemon.
@@ -364,6 +365,7 @@ let test_all_args = [
   StringList "strlist";
   Bool "b";
   Int "integer";
+  Int64 "integer64";
   FileIn "filein";
   FileOut "fileout";
 ]
@@ -833,6 +835,32 @@ The default is disabled.");
    "\
 Return the direct appliance mode flag.");
 
+  ("set_recovery_proc", (RErr, [Bool "recoveryproc"]), -1, [FishAlias "recovery-proc"],
+   [InitNone, Always, TestOutputTrue (
+      [["set_recovery_proc"; "true"];
+       ["get_recovery_proc"]])],
+   "enable or disable the recovery process",
+   "\
+If this is called with the parameter C<false> then
+C<guestfs_launch> does not create a recovery process.  The
+purpose of the recovery process is to stop runaway qemu
+processes in the case where the main program aborts abruptly.
+
+This only has any effect if called before C<guestfs_launch>,
+and the default is true.
+
+About the only time when you would want to disable this is
+if the main process will fork itself into the background
+(\"daemonize\" itself).  In this case the recovery process
+thinks that the main program has disappeared and so kills
+qemu, which is not very helpful.");
+
+  ("get_recovery_proc", (RBool "recoveryproc", []), -1, [],
+   [],
+   "get recovery process enabled flag",
+   "\
+Return the recovery process enabled flag.");
+
 ]
 
 (* daemon_functions are any functions which cause some action
@@ -3645,6 +3673,226 @@ The result list is not sorted.
 
 =back");
 
+  ("case_sensitive_path", (RString "rpath", [Pathname "path"]), 197, [],
+   [InitISOFS, Always, TestOutput (
+      [["case_sensitive_path"; "/DIRECTORY"]], "/directory");
+    InitISOFS, Always, TestOutput (
+      [["case_sensitive_path"; "/DIRECTORY/"]], "/directory");
+    InitISOFS, Always, TestOutput (
+      [["case_sensitive_path"; "/Known-1"]], "/known-1");
+    InitISOFS, Always, TestLastFail (
+      [["case_sensitive_path"; "/Known-1/"]]);
+    InitBasicFS, Always, TestOutput (
+      [["mkdir"; "/a"];
+       ["mkdir"; "/a/bbb"];
+       ["touch"; "/a/bbb/c"];
+       ["case_sensitive_path"; "/A/bbB/C"]], "/a/bbb/c");
+    InitBasicFS, Always, TestOutput (
+      [["mkdir"; "/a"];
+       ["mkdir"; "/a/bbb"];
+       ["touch"; "/a/bbb/c"];
+       ["case_sensitive_path"; "/A////bbB/C"]], "/a/bbb/c");
+    InitBasicFS, Always, TestLastFail (
+      [["mkdir"; "/a"];
+       ["mkdir"; "/a/bbb"];
+       ["touch"; "/a/bbb/c"];
+       ["case_sensitive_path"; "/A/bbb/../bbb/C"]])],
+   "return true path on case-insensitive filesystem",
+   "\
+This can be used to resolve case insensitive paths on
+a filesystem which is case sensitive.  The use case is
+to resolve paths which you have read from Windows configuration
+files or the Windows Registry, to the true path.
+
+The command handles a peculiarity of the Linux ntfs-3g
+filesystem driver (and probably others), which is that although
+the underlying filesystem is case-insensitive, the driver
+exports the filesystem to Linux as case-sensitive.
+
+One consequence of this is that special directories such
+as C<c:\\windows> may appear as C</WINDOWS> or C</windows>
+(or other things) depending on the precise details of how
+they were created.  In Windows itself this would not be
+a problem.
+
+Bug or feature?  You decide:
+L<http://www.tuxera.com/community/ntfs-3g-faq/#posixfilenames1>
+
+This function resolves the true case of each element in the
+path and returns the case-sensitive path.
+
+Thus C<guestfs_case_sensitive_path> (\"/Windows/System32\")
+might return C<\"/WINDOWS/system32\"> (the exact return value
+would depend on details of how the directories were originally
+created under Windows).
+
+I<Note>:
+This function does not handle drive names, backslashes etc.
+
+See also C<guestfs_realpath>.");
+
+  ("vfs_type", (RString "fstype", [Device "device"]), 198, [],
+   [InitBasicFS, Always, TestOutput (
+      [["vfs_type"; "/dev/sda1"]], "ext2")],
+   "get the Linux VFS type corresponding to a mounted device",
+   "\
+This command gets the block device type corresponding to
+a mounted device called C<device>.
+
+Usually the result is the name of the Linux VFS module that
+is used to mount this device (probably determined automatically
+if you used the C<guestfs_mount> call).");
+
+  ("truncate", (RErr, [Pathname "path"]), 199, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["write_file"; "/test"; "some stuff so size is not zero"; "0"];
+       ["truncate"; "/test"];
+       ["stat"; "/test"]], [CompareWithInt ("size", 0)])],
+   "truncate a file to zero size",
+   "\
+This command truncates C<path> to a zero-length file.  The
+file must exist already.");
+
+  ("truncate_size", (RErr, [Pathname "path"; Int64 "size"]), 200, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["touch"; "/test"];
+       ["truncate_size"; "/test"; "1000"];
+       ["stat"; "/test"]], [CompareWithInt ("size", 1000)])],
+   "truncate a file to a particular size",
+   "\
+This command truncates C<path> to size C<size> bytes.  The file
+must exist already.  If the file is smaller than C<size> then
+the file is extended to the required size with null bytes.");
+
+  ("utimens", (RErr, [Pathname "path"; Int64 "atsecs"; Int64 "atnsecs"; Int64 "mtsecs"; Int64 "mtnsecs"]), 201, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["touch"; "/test"];
+       ["utimens"; "/test"; "12345"; "67890"; "9876"; "5432"];
+       ["stat"; "/test"]], [CompareWithInt ("mtime", 9876)])],
+   "set timestamp of a file with nanosecond precision",
+   "\
+This command sets the timestamps of a file with nanosecond
+precision.
+
+C<atsecs, atnsecs> are the last access time (atime) in secs and
+nanoseconds from the epoch.
+
+C<mtsecs, mtnsecs> are the last modification time (mtime) in
+secs and nanoseconds from the epoch.
+
+If the C<*nsecs> field contains the special value C<-1> then
+the corresponding timestamp is set to the current time.  (The
+C<*secs> field is ignored in this case).
+
+If the C<*nsecs> field contains the special value C<-2> then
+the corresponding timestamp is left unchanged.  (The
+C<*secs> field is ignored in this case).");
+
+  ("mkdir_mode", (RErr, [Pathname "path"; Int "mode"]), 202, [],
+   [InitBasicFS, Always, TestOutputStruct (
+      [["mkdir_mode"; "/test"; "0o111"];
+       ["stat"; "/test"]], [CompareWithInt ("mode", 0o40111)])],
+   "create a directory with a particular mode",
+   "\
+This command creates a directory, setting the initial permissions
+of the directory to C<mode>.  See also C<guestfs_mkdir>.");
+
+  ("lchown", (RErr, [Int "owner"; Int "group"; Pathname "path"]), 203, [],
+   [], (* XXX *)
+   "change file owner and group",
+   "\
+Change the file owner to C<owner> and group to C<group>.
+This is like C<guestfs_chown> but if C<path> is a symlink then
+the link itself is changed, not the target.
+
+Only numeric uid and gid are supported.  If you want to use
+names, you will need to locate and parse the password file
+yourself (Augeas support makes this relatively easy).");
+
+  ("lstatlist", (RStructList ("statbufs", "stat"), [Pathname "path"; StringList "names"]), 204, [],
+   [], (* XXX *)
+   "lstat on multiple files",
+   "\
+This call allows you to perform the C<guestfs_lstat> operation
+on multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a list of stat structs, with a one-to-one
+correspondence to the C<names> list.  If any name did not exist
+or could not be lstat'd, then the C<ino> field of that structure
+is set to C<-1>.
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+See also C<guestfs_lxattrlist> for a similarly efficient call
+for getting extended attributes.  Very long directory listings
+might cause the protocol message size to be exceeded, causing
+this call to fail.  The caller must split up such requests
+into smaller groups of names.");
+
+  ("lxattrlist", (RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"]), 205, [],
+   [], (* XXX *)
+   "lgetxattr on multiple files",
+   "\
+This call allows you to get the extended attributes
+of multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a flat list of xattr structs which must be
+interpreted sequentially.  The first xattr struct always has a zero-length
+C<attrname>.  C<attrval> in this struct is zero-length
+to indicate there was an error doing C<lgetxattr> for this
+file, I<or> is a C string which is a decimal number
+(the number of following attributes for this file, which could
+be C<\"0\">).  Then after the first xattr struct are the
+zero or more attributes for the first named file.
+This repeats for the second and subsequent files.
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+See also C<guestfs_lstatlist> for a similarly efficient call
+for getting standard stats.  Very long directory listings
+might cause the protocol message size to be exceeded, causing
+this call to fail.  The caller must split up such requests
+into smaller groups of names.");
+
+  ("readlinklist", (RStringList "links", [Pathname "path"; StringList "names"]), 206, [],
+   [], (* XXX *)
+   "readlink on multiple files",
+   "\
+This call allows you to do a C<readlink> operation
+on multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a list of strings, with a one-to-one
+correspondence to the C<names> list.  Each string is the
+value of the symbol link.
+
+If the C<readlink(2)> operation fails on any name, then
+the corresponding result string is the empty string C<\"\">.
+However the whole operation is completed even if there
+were C<readlink(2)> errors, and so you can call this
+function with names where you don't know if they are
+symbolic links already (albeit slightly less efficient).
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+Very long directory listings might cause the protocol
+message size to be exceeded, causing
+this call to fail.  The caller must split up such requests
+into smaller groups of names.");
+
+  ("pread", (RBufferOut "content", [Pathname "path"; Int "count"; Int64 "offset"]), 207, [ProtocolLimitWarning],
+   [InitISOFS, Always, TestOutputBuffer (
+      [["pread"; "/known-4"; "1"; "3"]], "\n")],
+   "read part of a file",
+   "\
+This command lets you read part of a file.  It reads C<count>
+bytes of the file, starting at C<offset>, from file C<path>.
+
+This may read fewer bytes than requested.  For further details
+see the L<pread(2)> system call.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -3845,7 +4093,7 @@ type rstructs_used_t = RStructOnly | RStructListOnly | RStructAndList
  *    == there are functions returning both RStruct (_, structname)
  *                                      and RStructList (_, structname)
  *)
-let rstructs_used =
+let rstructs_used_by functions =
   (* ||| is a "logical OR" for rstructs_used_t *)
   let (|||) a b =
     match a, b with
@@ -3874,27 +4122,18 @@ let rstructs_used =
       | RStruct (_, structname) -> update structname RStructOnly
       | RStructList (_, structname) -> update structname RStructListOnly
       | _ -> ()
-  ) all_functions;
+  ) functions;
 
   (* return key->values as a list of (key,value) *)
   Hashtbl.fold (fun key value xs -> (key, value) :: xs) h []
 
-(* debug:
-let () =
-  List.iter (
-    function
-    | sn, RStructOnly -> printf "%s RStructOnly\n" sn
-    | sn, RStructListOnly -> printf "%s RStructListOnly\n" sn
-    | sn, RStructAndList -> printf "%s RStructAndList\n" sn
-  ) rstructs_used
-*)
-
 (* Used for testing language bindings. *)
 type callt =
   | CallString of string
   | CallOptString of string option
   | CallStringList of string list
   | CallInt of int
+  | CallInt64 of int64
   | CallBool of bool
 
 (* Used to memoize the result of pod2text. *)
@@ -4032,7 +4271,7 @@ let mapi f xs =
 
 let name_of_argt = function
   | Pathname n | Device n | Dev_or_Path n | String n | OptString n
-  | StringList n | DeviceList n | Bool n | Int n
+  | StringList n | DeviceList n | Bool n | Int n | Int64 n
   | FileIn n | FileOut n -> n
 
 let java_name_of_struct typ =
@@ -4448,11 +4687,13 @@ and generate_xdr () =
            pr "struct %s_args {\n" name;
            List.iter (
              function
-             | Pathname n | Device n | Dev_or_Path n | String n -> pr "  string %s<>;\n" n
+             | Pathname n | Device n | Dev_or_Path n | String n ->
+                pr "  string %s<>;\n" n
              | OptString n -> pr "  str *%s;\n" n
              | StringList n | DeviceList n -> pr "  str %s<>;\n" n
              | Bool n -> pr "  bool %s;\n" n
              | Int n -> pr "  int %s;\n" n
+             | Int64 n -> pr "  hyper %s;\n" n
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "};\n\n"
@@ -4641,6 +4882,8 @@ and generate_client_actions () =
   pr "\
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
 
 #include \"guestfs.h\"
 #include \"guestfs-internal-actions.h\"
@@ -4743,6 +4986,8 @@ check_state (guestfs_h *g, const char *caller)
           pr "    fputs (%s ? \" true\" : \" false\", stdout);\n" n
       | Int n ->                       (* int *)
           pr "    printf (\" %%d\", %s);\n" n
+      | Int64 n ->
+          pr "    printf (\" %%\" PRIi64, %s);\n" n
     ) (snd style);
     pr "    putchar ('\\n');\n";
     pr "  }\n";
@@ -4832,6 +5077,8 @@ check_state (guestfs_h *g, const char *caller)
                  pr "  args.%s = %s;\n" n n
              | Int n ->
                  pr "  args.%s = %s;\n" n n
+             | Int64 n ->
+                 pr "  args.%s = %s;\n" n n
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "  serial = guestfs___send (g, GUESTFS_PROC_%s,\n"
@@ -5034,6 +5281,7 @@ and generate_daemon_actions () =
              | StringList n | DeviceList n -> pr "  char **%s;\n" n
              | Bool n -> pr "  int %s;\n" n
              | Int n -> pr "  int %s;\n" n
+             | Int64 n -> pr "  int64_t %s;\n" n
              | FileIn _ | FileOut _ -> ()
            ) args
       );
@@ -5085,6 +5333,7 @@ and generate_daemon_actions () =
                  pr "  }\n";
              | Bool n -> pr "  %s = args.%s;\n" n n
              | Int n -> pr "  %s = args.%s;\n" n n
+             | Int64 n -> pr "  %s = args.%s;\n" n n
              | FileIn _ | FileOut _ -> ()
            ) args;
            pr "\n"
@@ -5987,6 +6236,7 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
         | OptString n, arg ->
             pr "    const char *%s = \"%s\";\n" n (c_quote arg);
         | Int _, _
+        | Int64 _, _
         | Bool _, _
         | FileIn _, _ | FileOut _, _ -> ()
         | StringList n, arg | DeviceList n, arg ->
@@ -6045,6 +6295,12 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
               with Failure "int_of_string" ->
                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
             pr ", %d" i
+        | Int64 _, arg ->
+            let i =
+              try Int64.of_string arg
+              with Failure "int_of_string" ->
+                failwithf "%s: expecting an int64, but got '%s'" test_name arg in
+            pr ", %Ld" i
         | Bool _, arg ->
             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
       ) (List.combine (snd style) args);
@@ -6221,17 +6477,17 @@ and generate_fish_cmds () =
         | name, FString ->
             pr "  printf (\"%%s%s: %%s\\n\", indent, %s->%s);\n" name typ name
         | name, FUUID ->
-            pr "  printf (\"%s: \");\n" name;
+            pr "  printf (\"%%s%s: \", indent);\n" name;
             pr "  for (i = 0; i < 32; ++i)\n";
-            pr "    printf (\"%%s%%c\", indent, %s->%s[i]);\n" typ name;
+            pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
             pr "  printf (\"\\n\");\n"
         | name, FBuffer ->
             pr "  printf (\"%%s%s: \", indent);\n" name;
             pr "  for (i = 0; i < %s->%s_len; ++i)\n" typ name;
             pr "    if (c_isprint (%s->%s[i]))\n" typ name;
-            pr "      printf (\"%%s%%c\", indent, %s->%s[i]);\n" typ name;
+            pr "      printf (\"%%c\", %s->%s[i]);\n" typ name;
             pr "    else\n";
-            pr "      printf (\"%%s\\\\x%%02x\", indent, %s->%s[i]);\n" typ name;
+            pr "      printf (\"\\\\x%%02x\", %s->%s[i]);\n" typ name;
             pr "  printf (\"\\n\");\n"
         | name, (FUInt64|FBytes) ->
             pr "  printf (\"%%s%s: %%\" PRIu64 \"\\n\", indent, %s->%s);\n"
@@ -6264,19 +6520,19 @@ and generate_fish_cmds () =
         (* generate the function for typ *)
         emit_print_list_function typ
     | typ, _ -> () (* empty *)
-  ) rstructs_used;
+  ) (rstructs_used_by all_functions);
 
   (* Emit a print_TYPE function definition only if that function is used. *)
   List.iter (
     function
-    | typ, RStructOnly ->
+    | typ, (RStructOnly | RStructAndList) ->
         pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
         pr "{\n";
         pr "  print_%s_indent (%s, \"\");\n" typ typ;
         pr "}\n";
         pr "\n";
     | typ, _ -> () (* empty *)
-  ) rstructs_used;
+  ) (rstructs_used_by all_functions);
 
   (* run_<action> actions *)
   List.iter (
@@ -6299,15 +6555,17 @@ and generate_fish_cmds () =
       );
       List.iter (
         function
-        | Pathname n
-        | Device n | Dev_or_Path n
+        | Device n
         | String n
         | OptString n
         | FileIn n
         | FileOut n -> pr "  const char *%s;\n" n
+        | Pathname n
+        | Dev_or_Path n -> pr "  char *%s;\n" n
         | StringList n | DeviceList n -> pr "  char **%s;\n" n
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
+        | Int64 n -> pr "  int64_t %s;\n" n
       ) (snd style);
 
       (* Check and convert parameters. *)
@@ -6321,8 +6579,13 @@ and generate_fish_cmds () =
       iteri (
         fun i ->
           function
+          | Device name
+          | String name ->
+             pr "  %s = argv[%d];\n" name i
           | Pathname name
-          | Device name | Dev_or_Path name | String name -> pr "  %s = argv[%d];\n" name i
+          | Dev_or_Path name ->
+             pr "  %s = resolve_win_path (argv[%d]);\n" name i;
+             pr "  if (%s == NULL) return -1;\n" name
           | OptString name ->
               pr "  %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
                 name i i
@@ -6339,6 +6602,8 @@ and generate_fish_cmds () =
               pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
           | Int name ->
               pr "  %s = atoi (argv[%d]);\n" name i
+          | Int64 name ->
+              pr "  %s = atoll (argv[%d]);\n" name i
       ) (snd style);
 
       (* Call C API function. *)
@@ -6351,9 +6616,11 @@ and generate_fish_cmds () =
 
       List.iter (
         function
-        | Pathname name | Device name | Dev_or_Path name | String name
+        | Device name | String name
         | OptString name | FileIn name | FileOut name | Bool name
-        | Int name -> ()
+        | Int name | Int64 name -> ()
+        | Pathname name | Dev_or_Path name ->
+            pr "  free (%s);\n" name
         | StringList name | DeviceList name ->
             pr "  free_strings (%s);\n" name
       ) (snd style);
@@ -6570,6 +6837,7 @@ and generate_fish_actions_pod () =
         | StringList n | DeviceList n -> pr " '%s ...'" n
         | Bool _ -> pr " true|false"
         | Int n -> pr " %s" n
+        | Int64 n -> pr " %s" n
         | FileIn n | FileOut n -> pr " (%s|-)" n
       ) (snd style);
       pr "\n";
@@ -6642,6 +6910,7 @@ and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
           pr "char *const *%s" n
       | Bool n -> next (); pr "int %s" n
       | Int n -> next (); pr "int %s" n
+      | Int64 n -> next (); pr "int64_t %s" n
       | FileIn n
       | FileOut n ->
           if not in_daemon then (next (); pr "const char *%s" n)
@@ -6694,12 +6963,21 @@ type t
 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
+    after calling {!close} on it.  The string is the name of
+    the function. *)
+
 val create : unit -> t
+(** Create a {!Guestfs.t} handle. *)
 
 val close : t -> unit
-(** Handles are closed by the garbage collector when they become
-    unreferenced, but callers can also call this in order to
-    provide predictable cleanup. *)
+(** Close the {!Guestfs.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. *)
 
 ";
   generate_ocaml_structure_decls ();
@@ -6710,7 +6988,7 @@ val close : t -> unit
       generate_ocaml_prototype name style;
       pr "(** %s *)\n" shortdesc;
       pr "\n"
-  ) all_functions
+  ) all_functions_sorted
 
 (* Generate the OCaml bindings implementation. *)
 and generate_ocaml_ml () =
@@ -6718,12 +6996,17 @@ and generate_ocaml_ml () =
 
   pr "\
 type t
+
 exception Error of string
+exception Handle_closed of string
+
 external create : unit -> t = \"ocaml_guestfs_create\"
 external close : t -> unit = \"ocaml_guestfs_close\"
 
+(* Give the exceptions names, so they can be raised from the C code. *)
 let () =
-  Callback.register_exception \"ocaml_guestfs_error\" (Error \"\")
+  Callback.register_exception \"ocaml_guestfs_error\" (Error \"\");
+  Callback.register_exception \"ocaml_guestfs_closed\" (Handle_closed \"\")
 
 ";
 
@@ -6733,7 +7016,7 @@ let () =
   List.iter (
     fun (name, style, _, _, _, shortdesc, _) ->
       generate_ocaml_prototype ~is_external:true name style;
-  ) all_functions
+  ) all_functions_sorted
 
 (* Generate the OCaml bindings C implementation. *)
 and generate_ocaml_c () =
@@ -6864,11 +7147,17 @@ copy_table (char * const * argv)
         (* generate the function for typ *)
         emit_ocaml_copy_list_function typ
     | typ, _ -> () (* empty *)
-  ) rstructs_used;
+  ) (rstructs_used_by all_functions);
 
   (* The wrappers. *)
   List.iter (
     fun (name, style, _, _, _, _, _) ->
+      pr "/* Automatically generated wrapper for function\n";
+      pr " * ";
+      generate_ocaml_prototype name style;
+      pr " */\n";
+      pr "\n";
+
       let params =
         "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
 
@@ -6878,6 +7167,7 @@ copy_table (char * const * argv)
       pr "/* Emit prototype to appease gcc's -Wmissing-prototypes. */\n";
       pr "CAMLprim value ocaml_guestfs_%s (value %s" name (List.hd params);
       List.iter (pr ", value %s") (List.tl params); pr ");\n";
+      pr "\n";
 
       pr "CAMLprim value\n";
       pr "ocaml_guestfs_%s (value %s" name (List.hd params);
@@ -6903,7 +7193,7 @@ copy_table (char * const * argv)
 
       pr "  guestfs_h *g = Guestfs_val (gv);\n";
       pr "  if (g == NULL)\n";
-      pr "    caml_failwith (\"%s: used handle after closing it\");\n" name;
+      pr "    ocaml_guestfs_raise_closed (\"%s\");\n" name;
       pr "\n";
 
       List.iter (
@@ -6924,6 +7214,8 @@ copy_table (char * const * argv)
             pr "  int %s = Bool_val (%sv);\n" n n
         | Int n ->
             pr "  int %s = Int_val (%sv);\n" n n
+        | Int64 n ->
+            pr "  int64_t %s = Int64_val (%sv);\n" n n
       ) (snd style);
       let error_code =
         match fst style with
@@ -6962,7 +7254,8 @@ copy_table (char * const * argv)
         function
         | StringList n | DeviceList n ->
             pr "  ocaml_guestfs_free_strings (%s);\n" n;
-        | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _
+        | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _
+       | Bool _ | Int _ | Int64 _
         | FileIn _ | FileOut _ -> ()
       ) (snd style);
 
@@ -7024,7 +7317,7 @@ copy_table (char * const * argv)
         pr "}\n";
         pr "\n"
       )
-  ) all_functions
+  ) all_functions_sorted
 
 and generate_ocaml_structure_decls () =
   List.iter (
@@ -7054,6 +7347,7 @@ and generate_ocaml_prototype ?(is_external = false) name style =
     | StringList _ | DeviceList _ -> pr "string array -> "
     | Bool _ -> pr "bool -> "
     | Int _ -> pr "int -> "
+    | Int64 _ -> pr "int64 -> "
   ) (snd style);
   (match fst style with
    | RErr -> pr "unit" (* all errors are turned into exceptions *)
@@ -7205,12 +7499,14 @@ DESTROY (g)
           | StringList n | DeviceList n -> pr "      char **%s;\n" n
           | Bool n -> pr "      int %s;\n" n
           | Int n -> pr "      int %s;\n" n
+          | Int64 n -> pr "      int64_t %s;\n" n
       ) (snd style);
 
       let do_cleanups () =
         List.iter (
           function
-          | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _
+          | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _
+         | Bool _ | Int _ | Int64 _
           | FileIn _ | FileOut _ -> ()
           | StringList n | DeviceList n -> pr "      free (%s);\n" n
         ) (snd style)
@@ -7582,7 +7878,7 @@ and generate_perl_prototype name style =
       comma := true;
       match arg with
       | Pathname n | Device n | Dev_or_Path n | String n
-      | OptString n | Bool n | Int n | FileIn n | FileOut n ->
+      | OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n ->
           pr "$%s" n
       | StringList n | DeviceList n ->
           pr "\\@%s" n
@@ -7810,7 +8106,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
         (* generate the function for typ *)
         emit_put_list_function typ
     | typ, _ -> () (* empty *)
-  ) rstructs_used;
+  ) (rstructs_used_by all_functions);
 
   (* Python wrapper functions. *)
   List.iter (
@@ -7849,6 +8145,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
             pr "  char **%s;\n" n
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
+        | Int64 n -> pr "  long long %s;\n" n
       ) (snd style);
 
       pr "\n";
@@ -7862,6 +8159,9 @@ py_guestfs_close (PyObject *self, PyObject *args)
         | StringList _ | DeviceList _ -> pr "O"
         | Bool _ -> pr "i" (* XXX Python has booleans? *)
         | Int _ -> pr "i"
+        | Int64 _ -> pr "L" (* XXX Whoever thought it was a good idea to
+                            * emulate C's int/long/long long in Python?
+                            *)
       ) (snd style);
       pr ":guestfs_%s\",\n" name;
       pr "                         &py_g";
@@ -7872,6 +8172,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
         | StringList n | DeviceList n -> pr ", &py_%s" n
         | Bool n -> pr ", &%s" n
         | Int n -> pr ", &%s" n
+        | Int64 n -> pr ", &%s" n
       ) (snd style);
 
       pr "))\n";
@@ -7881,7 +8182,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> ()
         | StringList n | DeviceList n ->
             pr "  %s = get_string_list (py_%s);\n" n n;
             pr "  if (!%s) return NULL;\n" n
@@ -7896,7 +8197,7 @@ py_guestfs_close (PyObject *self, PyObject *args)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> ()
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
@@ -8226,6 +8527,8 @@ static VALUE ruby_guestfs_close (VALUE gv)
             pr "  int %s = RTEST (%sv);\n" n n
         | Int n ->
             pr "  int %s = NUM2INT (%sv);\n" n n
+        | Int64 n ->
+            pr "  long long %s = NUM2LL (%sv);\n" n n
       ) (snd style);
       pr "\n";
 
@@ -8253,7 +8556,7 @@ static VALUE ruby_guestfs_close (VALUE gv)
       List.iter (
         function
         | Pathname _ | Device _ | Dev_or_Path _ | String _
-        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
+        | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ -> ()
         | StringList n | DeviceList n ->
             pr "  free (%s);\n" n
       ) (snd style);
@@ -8576,6 +8879,8 @@ and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false)
           pr "boolean %s" n
       | Int n ->
           pr "int %s" n
+      | Int64 n ->
+          pr "long %s" n
   ) (snd style);
 
   pr ")\n";
@@ -8695,6 +9000,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
             pr ", jboolean j%s" n
         | Int n ->
             pr ", jint j%s" n
+        | Int64 n ->
+            pr ", jlong j%s" n
       ) (snd style);
       pr ")\n";
       pr "{\n";
@@ -8748,6 +9055,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
         | Bool n
         | Int n ->
             pr "  int %s;\n" n
+        | Int64 n ->
+            pr "  int64_t %s;\n" n
       ) (snd style);
 
       let needs_i =
@@ -8789,7 +9098,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
             pr "  }\n";
             pr "  %s[%s_len] = NULL;\n" n n;
         | Bool n
-        | Int n ->
+        | Int n
+        | Int64 n ->
             pr "  %s = j%s;\n" n n
       ) (snd style);
 
@@ -8818,7 +9128,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close
             pr "  }\n";
             pr "  free (%s);\n" n
         | Bool n
-        | Int n -> ()
+        | Int n
+       | Int64 n -> ()
       ) (snd style);
 
       (* Check for errors. *)
@@ -9080,7 +9391,7 @@ last_error h = do
           | Pathname n | Device n | Dev_or_Path n | String n -> pr "withCString %s $ \\%s -> " n n
           | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n
           | StringList n | DeviceList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n
-          | Bool _ | Int _ -> ()
+          | Bool _ | Int _ | Int64 _ -> ()
         ) (snd style);
         (* Convert integer arguments. *)
         let args =
@@ -9088,6 +9399,7 @@ last_error h = do
             function
             | Bool n -> sprintf "(fromBool %s)" n
             | Int n -> sprintf "(fromIntegral %s)" n
+            | Int64 n -> sprintf "(fromIntegral %s)" n
             | FileIn n | FileOut n
             | Pathname n | Device n | Dev_or_Path n | String n | OptString n | StringList n | DeviceList n -> n
           ) (snd style) in
@@ -9144,6 +9456,7 @@ and generate_haskell_prototype ~handle ?(hs = false) style =
        | StringList _ | DeviceList _ -> if hs then pr "[String]" else pr "Ptr CString"
        | Bool _ -> pr "%s" bool
        | Int _ -> pr "%s" int
+       | Int64 _ -> pr "%s" int
        | FileIn _ -> pr "%s" string
        | FileOut _ -> pr "%s" string
       );
@@ -9224,6 +9537,7 @@ print_strings (char *const *argv)
       | StringList n | DeviceList n -> pr "  print_strings (%s);\n" n
       | Bool n -> pr "  printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n
       | Int n -> pr "  printf (\"%%d\\n\", %s);\n" n
+      | Int64 n -> pr "  printf (\"%%\" PRIi64 \"\\n\", %s);\n" n
     ) (snd style);
     pr "  /* Java changes stdout line buffering so we need this: */\n";
     pr "  fflush (stdout);\n";
@@ -9339,6 +9653,8 @@ let () =
             "[|" ^ String.concat ";" (List.map (sprintf "\"%s\"") xs) ^ "|]"
         | CallInt i when i >= 0 -> string_of_int i
         | CallInt i (* when i < 0 *) -> "(" ^ string_of_int i ^ ")"
+        | CallInt64 i when i >= 0L -> Int64.to_string i ^ "L"
+        | CallInt64 i (* when i < 0L *) -> "(" ^ Int64.to_string i ^ "L)"
         | CallBool b -> string_of_bool b
       ) args
     )
@@ -9372,6 +9688,7 @@ my $g = Sys::Guestfs->new ();
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> if b then "1" else "0"
       ) args
     )
@@ -9402,6 +9719,7 @@ g = guestfs.GuestFS ()
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> if b then "1" else "0"
       ) args
     )
@@ -9432,6 +9750,7 @@ g = Guestfs::create()
         | CallStringList xs ->
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> string_of_bool b
       ) args
     )
@@ -9467,6 +9786,7 @@ public class Bindtests {
             "new String[]{" ^
               String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "}"
         | CallInt i -> string_of_int i
+        | CallInt64 i -> Int64.to_string i
         | CallBool b -> string_of_bool b
       ) args
     )
@@ -9509,6 +9829,8 @@ main = do
             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
         | CallInt i when i < 0 -> "(" ^ string_of_int i ^ ")"
         | CallInt i -> string_of_int i
+        | CallInt64 i when i < 0L -> "(" ^ Int64.to_string i ^ ")"
+        | CallInt64 i -> Int64.to_string i
         | CallBool true -> "True"
         | CallBool false -> "False"
       ) args
@@ -9527,43 +9849,43 @@ main = do
 and generate_lang_bindtests call =
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString None;
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString ""; CallOptString (Some "def");
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString ""; CallOptString (Some "");
                 CallStringList []; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"; "2"]; CallBool false;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool true;
-                CallInt 0; CallString "123"; CallString "456"];
+                CallInt 0; CallInt64 0L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt (-1); CallString "123"; CallString "456"];
+                CallInt (-1); CallInt64 (-1L); CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt (-2); CallString "123"; CallString "456"];
+                CallInt (-2); CallInt64 (-2L); CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 1; CallString "123"; CallString "456"];
+                CallInt 1; CallInt64 1L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 2; CallString "123"; CallString "456"];
+                CallInt 2; CallInt64 2L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 4095; CallString "123"; CallString "456"];
+                CallInt 4095; CallInt64 4095L; CallString "123"; CallString "456"];
   call "test0" [CallString "abc"; CallOptString (Some "def");
                 CallStringList ["1"]; CallBool false;
-                CallInt 0; CallString ""; CallString ""]
+                CallInt 0; CallInt64 0L; CallString ""; CallString ""]
 
 (* XXX Add here tests of the return and error functions. *)