open Generator_utils
open Generator_pr
open Generator_docstrings
+open Generator_api_versions
open Generator_optgroups
open Generator_actions
open Generator_structs
(* Generate a C function prototype. *)
let rec generate_prototype ?(extern = true) ?(static = false)
?(semicolon = true)
- ?(single_line = false) ?(newline = false) ?(in_daemon = false)
+ ?(single_line = false) ?(indent = "") ?(newline = false)
+ ?(in_daemon = false)
?(prefix = "") ?(suffix = "")
?handle
?(optarg_proto = Dots)
name (ret, args, optargs) =
+ pr "%s" indent;
if extern then pr "extern ";
if static then pr "static ";
(match ret with
- | RErr -> pr "int "
- | RInt _ -> pr "int "
- | RInt64 _ -> pr "int64_t "
- | RBool _ -> pr "int "
- | RConstString _ | RConstOptString _ -> pr "const char *"
- | RString _ | RBufferOut _ -> pr "char *"
- | RStringList _ | RHashtable _ -> pr "char **"
+ | RErr
+ | RInt _
+ | RBool _ ->
+ pr "int";
+ if single_line then pr " " else pr "\n%s" indent
+ | RInt64 _ ->
+ pr "int64_t";
+ if single_line then pr " " else pr "\n%s" indent
+ | RConstString _ | RConstOptString _ ->
+ pr "const char *";
+ if not single_line then pr "\n%s" indent
+ | RString _ | RBufferOut _ ->
+ pr "char *";
+ if not single_line then pr "\n%s" indent
+ | RStringList _ | RHashtable _ ->
+ pr "char **";
+ if not single_line then pr "\n%s" indent
| RStruct (_, typ) ->
if not in_daemon then pr "struct guestfs_%s *" typ
- else pr "guestfs_int_%s *" typ
+ else pr "guestfs_int_%s *" typ;
+ if not single_line then pr "\n%s" indent
| RStructList (_, typ) ->
if not in_daemon then pr "struct guestfs_%s_list *" typ
- else pr "guestfs_int_%s_list *" typ
+ else pr "guestfs_int_%s_list *" typ;
+ if not single_line then pr "\n%s" indent
);
let is_RBufferOut = match ret with RBufferOut _ -> true | _ -> false in
pr "%s%s%s (" prefix name suffix;
);
let next () =
if !comma then (
- if single_line then pr ", " else pr ",\n\t\t"
+ if single_line then pr ", "
+ else (
+ let namelen = String.length prefix + String.length name +
+ String.length suffix + 2 in
+ pr ",\n%s%s" indent (spaces namelen)
+ )
);
comma := true
in
pr "const char *%s" n;
next ();
pr "size_t %s_size" n
+ | Pointer (t, n) ->
+ next ();
+ pr "%s %s" t n
) args;
if is_RBufferOut then (next (); pr "size_t *size_r");
if optargs <> [] then (
if not (List.mem NotInDocs flags) then (
let name = "guestfs_" ^ shortname in
pr "=head2 %s\n\n" name;
- pr " ";
- generate_prototype ~extern:false ~handle:"g" name style;
+ generate_prototype ~extern:false ~indent:" " ~handle:"g" name style;
pr "\n\n";
let uc_shortname = String.uppercase shortname in
or NULL if there was an error.
I<The caller must call C<guestfs_free_%s> after use>.\n\n" typ typ
| RStructList (_, typ) ->
- pr "This function returns a C<struct guestfs_%s_list *>
-(see E<lt>guestfs-structs.hE<gt>),
+ pr "This function returns a C<struct guestfs_%s_list *>,
or NULL if there was an error.
I<The caller must call C<guestfs_free_%s_list> after use>.\n\n" typ typ
| RHashtable _ ->
| None -> ()
| Some txt -> pr "%s\n\n" txt
);
+ (match lookup_api_version name with
+ | Some version -> pr "(Added in %s)\n\n" version
+ | None -> ()
+ );
(* Handling of optional argument variants. *)
if optargs <> [] then (
pr "=head2 %s_va\n\n" name;
- pr " ";
- generate_prototype ~extern:false ~handle:"g"
+ generate_prototype ~extern:false ~indent:" " ~handle:"g"
~prefix:"guestfs_" ~suffix:"_va" ~optarg_proto:VA
shortname style;
pr "\n\n";
pr "This is the \"va_list variant\" of L</%s>.\n\n" name;
pr "See L</CALLS WITH OPTIONAL ARGUMENTS>.\n\n";
pr "=head2 %s_argv\n\n" name;
- pr " ";
- generate_prototype ~extern:false ~handle:"g"
+ generate_prototype ~extern:false ~indent:" " ~handle:"g"
~prefix:"guestfs_" ~suffix:"_argv" ~optarg_proto:Argv
shortname style;
pr "\n\n";
pr "=back\n";
pr "\n"
-(* Generate the guestfs-structs.h file. *)
-and generate_structs_h () =
+(* Generate the guestfs.h file. *)
+and generate_guestfs_h () =
generate_header CStyle LGPLv2plus;
- (* This is a public exported header file containing various
- * structures. The structures are carefully written to have
- * exactly the same in-memory format as the XDR structures that
- * we use on the wire to the daemon. The reason for creating
- * copies of these structures here is just so we don't have to
- * export the whole of guestfs_protocol.h (which includes much
- * unrelated and XDR-dependent stuff that we don't want to be
- * public, or required by clients).
- *
- * To reiterate, we will pass these structures to and from the
- * client with a simple assignment or memcpy, so the format
- * must be identical to what rpcgen / the RFC defines.
+ pr "\
+/* ---------- IMPORTANT NOTE ----------
+ *
+ * All API documentation is in the manpage, 'guestfs(3)'.
+ * To read it, type: man 3 guestfs
+ * Or read it online here: http://libguestfs.org/guestfs.3.html
+ *
+ * Go and read it now, I'll be right here waiting for you
+ * when you come back.
+ *
+ * ------------------------------------
+ */
+
+#ifndef GUESTFS_H_
+#define GUESTFS_H_
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+/* The handle. */
+#ifndef GUESTFS_TYPEDEF_H
+#define GUESTFS_TYPEDEF_H 1
+typedef struct guestfs_h guestfs_h;
+#endif
+
+/* Connection management. */
+extern guestfs_h *guestfs_create (void);
+extern void guestfs_close (guestfs_h *g);
+
+/* Error handling. */
+extern const char *guestfs_last_error (guestfs_h *g);
+#define LIBGUESTFS_HAVE_LAST_ERRNO 1
+extern int guestfs_last_errno (guestfs_h *g);
+
+#ifndef GUESTFS_TYPEDEF_ERROR_HANDLER_CB
+#define GUESTFS_TYPEDEF_ERROR_HANDLER_CB 1
+typedef void (*guestfs_error_handler_cb) (guestfs_h *g, void *opaque, const char *msg);
+#endif
+
+#ifndef GUESTFS_TYPEDEF_ABORT_CB
+#define GUESTFS_TYPEDEF_ABORT_CB 1
+typedef void (*guestfs_abort_cb) (void) __attribute__((__noreturn__));
+#endif
+
+extern void guestfs_set_error_handler (guestfs_h *g, guestfs_error_handler_cb cb, void *opaque);
+extern guestfs_error_handler_cb guestfs_get_error_handler (guestfs_h *g, void **opaque_rtn);
+
+extern void guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_cb);
+extern guestfs_abort_cb guestfs_get_out_of_memory_handler (guestfs_h *g);
+
+/* Events. */
+#ifndef GUESTFS_TYPEDEF_LOG_MESSAGE_CB
+#define GUESTFS_TYPEDEF_LOG_MESSAGE_CB 1
+typedef void (*guestfs_log_message_cb) (guestfs_h *g, void *opaque, char *buf, int len);
+#endif
+
+#ifndef GUESTFS_TYPEDEF_SUBPROCESS_QUIT_CB
+#define GUESTFS_TYPEDEF_SUBPROCESS_QUIT_CB 1
+typedef void (*guestfs_subprocess_quit_cb) (guestfs_h *g, void *opaque);
+#endif
+
+#ifndef GUESTFS_TYPEDEF_LAUNCH_DONE_CB
+#define GUESTFS_TYPEDEF_LAUNCH_DONE_CB 1
+typedef void (*guestfs_launch_done_cb) (guestfs_h *g, void *opaque);
+#endif
+
+#ifndef GUESTFS_TYPEDEF_CLOSE_CB
+#define GUESTFS_TYPEDEF_CLOSE_CB 1
+typedef void (*guestfs_close_cb) (guestfs_h *g, void *opaque);
+#endif
+
+#ifndef GUESTFS_TYPEDEF_PROGRESS_CB
+#define GUESTFS_TYPEDEF_PROGRESS_CB 1
+typedef void (*guestfs_progress_cb) (guestfs_h *g, void *opaque, int proc_nr, int serial, uint64_t position, uint64_t total);
+#endif
+
+extern void guestfs_set_log_message_callback (guestfs_h *g, guestfs_log_message_cb cb, void *opaque);
+extern void guestfs_set_subprocess_quit_callback (guestfs_h *g, guestfs_subprocess_quit_cb cb, void *opaque);
+extern void guestfs_set_launch_done_callback (guestfs_h *g, guestfs_launch_done_cb cb, void *opaque);
+#define LIBGUESTFS_HAVE_SET_CLOSE_CALLBACK 1
+extern void guestfs_set_close_callback (guestfs_h *g, guestfs_close_cb cb, void *opaque);
+#define LIBGUESTFS_HAVE_SET_PROGRESS_CALLBACK 1
+extern void guestfs_set_progress_callback (guestfs_h *g, guestfs_progress_cb cb, void *opaque);
+
+/* Private data area. */
+#define LIBGUESTFS_HAVE_SET_PRIVATE 1
+extern void guestfs_set_private (guestfs_h *g, const char *key, void *data);
+#define LIBGUESTFS_HAVE_GET_PRIVATE 1
+extern void *guestfs_get_private (guestfs_h *g, const char *key);
+
+/* Structures. */
+";
+
+ (* The structures are carefully written to have exactly the same
+ * in-memory format as the XDR structures that we use on the wire to
+ * the daemon. The reason for creating copies of these structures
+ * here is just so we don't have to export the whole of
+ * guestfs_protocol.h (which includes much unrelated and
+ * XDR-dependent stuff that we don't want to be public, or required
+ * by clients).
+ *
+ * To reiterate, we will pass these structures to and from the client
+ * with a simple assignment or memcpy, so the format must be
+ * identical to what rpcgen / the RFC defines.
*)
(* Public structures. *)
pr "extern void guestfs_free_%s (struct guestfs_%s *);\n" typ typ;
pr "extern void guestfs_free_%s_list (struct guestfs_%s_list *);\n" typ typ;
pr "\n"
- ) structs
+ ) structs;
+
+ pr "\
+/* Actions. */
+";
-(* Generate the guestfs-actions.h file. *)
-and generate_actions_h () =
- generate_header CStyle LGPLv2plus;
List.iter (
fun (shortname, (ret, args, optargs as style), _, flags, _, _, _) ->
let deprecated =
~prefix:"guestfs_" ~suffix:"_argv" ~optarg_proto:Argv
shortname style;
);
- ) all_functions_sorted
+ ) all_functions_sorted;
+
+ pr "\
+
+/* Private functions.
+ *
+ * These are NOT part of the public, stable API, and can change at any
+ * time! We export them because they are used by some of the language
+ * bindings.
+ */
+extern void *guestfs_safe_malloc (guestfs_h *g, size_t nbytes);
+extern void *guestfs_safe_calloc (guestfs_h *g, size_t n, size_t s);
+extern const char *guestfs_tmpdir (void);
+/* End of private functions. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GUESTFS_H_ */
+"
(* Generate the guestfs-internal-actions.h file. *)
and generate_internal_actions_h () =
#include \"guestfs-internal.h\"
#include \"guestfs-internal-actions.h\"
#include \"guestfs_protocol.h\"
+#include \"errnostring.h\"
/* Check the return message from a call for validity. */
static int
| BufferIn n
| StringList n
| DeviceList n
- | Key n ->
+ | Key n
+ | Pointer (_, n) ->
pr " if (%s == NULL) {\n" n;
pr " error (g, \"%%s: %%s: parameter cannot be NULL\",\n";
pr " \"%s\", \"%s\");\n" shortname n;
(* Generate code to generate guestfish call traces. *)
let trace_call shortname (ret, args, optargs) =
- pr " if (guestfs__get_trace (g)) {\n";
+ pr " if (trace_flag) {\n";
let needs_i =
List.exists (function
| Pathname n
| Dev_or_Path n
| FileIn n
- | FileOut n
- | Key n ->
+ | FileOut n ->
(* guestfish doesn't support string escaping, so neither do we *)
pr " fprintf (stderr, \" \\\"%%s\\\"\", %s);\n" n
+ | Key n ->
+ (* don't print keys *)
+ pr " fprintf (stderr, \" \\\"***\\\"\");\n"
| OptString n -> (* string option *)
pr " if (%s) fprintf (stderr, \" \\\"%%s\\\"\", %s);\n" n n;
pr " else fprintf (stderr, \" null\");\n"
| BufferIn n -> (* RHBZ#646822 *)
pr " fputc (' ', stderr);\n";
pr " guestfs___print_BufferIn (stderr, %s, %s_size);\n" n n
+ | Pointer (t, n) ->
+ pr " fprintf (stderr, \" (%s)%%p\", %s);\n" t n
) args;
(* Optional arguments. *)
~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" ~optarg_proto:Argv
shortname style;
pr "{\n";
+ pr " int trace_flag = g->trace;\n";
+ pr "\n";
check_null_strings shortname style;
reject_unknown_optargs shortname style;
trace_call shortname style;
pr " int serial;\n";
pr " int r;\n";
+ pr " int trace_flag = g->trace;\n";
pr "\n";
check_null_strings shortname style;
reject_unknown_optargs shortname style;
pr " }\n";
pr " args.%s.%s_val = (char *) %s;\n" n n n;
pr " args.%s.%s_len = %s_size;\n" n n n
+ | Pointer _ -> assert false
) args;
pr " serial = guestfs___send (g, GUESTFS_PROC_%s,\n"
(String.uppercase shortname);
pr "\n";
pr " if (hdr.status == GUESTFS_STATUS_ERROR) {\n";
- pr " error (g, \"%%s: %%s\", \"%s\", err.error_message);\n" shortname;
+ pr " int errnum = 0;\n";
+ pr " if (err.errno_string[0] != '\\0')\n";
+ pr " errnum = guestfs___string_to_errno (err.errno_string);\n";
+ pr " if (errnum <= 0)\n";
+ pr " error (g, \"%%s: %%s\", \"%s\", err.error_message);\n"
+ shortname;
+ pr " else\n";
+ pr " guestfs_error_errno (g, errnum, \"%%s: %%s\", \"%s\",\n"
+ shortname;
+ pr " err.error_message);\n";
pr " free (err.error_message);\n";
pr " free (err.errno_string);\n";
pr " guestfs___end_busy (g);\n";
"guestfs_get_error_handler";
"guestfs_get_out_of_memory_handler";
"guestfs_get_private";
+ "guestfs_last_errno";
"guestfs_last_error";
"guestfs_set_close_callback";
"guestfs_set_error_handler";