* this one to describe the interface (see the big table below), and
* daemon/<somefile>.c to write the implementation.
*
- * After editing this file, run it (./src/generator.ml) to regenerate
- * all the output files.
+ * After editing this file, run it (./src/generator.ml) to regenerate all the
+ * output files. Note that if you are using a separate build directory you must
+ * run generator.ml from your top level build directory. You must also have run
+ * configure before generator.ml will run.
*
* IMPORTANT: This script should NOT print any warnings. If it prints
* warnings, you should treat them as errors.
* inefficient. Keys should be unique. NULLs are not permitted.
*)
| RHashtable of string
+(* Not implemented:
+ (* "RBufferOut" is handled almost exactly like RString, but
+ * it allows the string to contain arbitrary 8 bit data including
+ * ASCII NUL. In the C API this causes an implicit extra parameter
+ * to be added of type <size_t *size_r>. Other programming languages
+ * support strings with arbitrary 8 bit data. At the RPC layer
+ * we have to use the opaque<> type instead of string<>.
+ *)
+ | RBufferOut of string
+*)
and args = argt list (* Function parameters, guestfs handle is implicit. *)
*)
| FileIn of string
| FileOut of string
+(* Not implemented:
+ (* Opaque buffer which can contain arbitrary 8 bit data.
+ * In the C API, this is expressed as <char *, int> pair.
+ * Most other languages have a string type which can contain
+ * ASCII NUL. We use whatever type is appropriate for each
+ * language.
+ * Buffers are limited by the total message size. To transfer
+ * large blocks of data, use FileIn/FileOut parameters instead.
+ * To return an arbitrary buffer, use RBufferOut.
+ *)
+ | BufferIn of string
+*)
type flags =
| ProtocolLimitWarning (* display warning about protocol size limits *)
*)
| TestOutputInt of seq * int
(* Run the command sequence and expect the output of the final
+ * command to be <op> <int>, eg. ">=", "1".
+ *)
+ | TestOutputIntOp of seq * string * int
+ (* Run the command sequence and expect the output of the final
* command to be a true value (!= 0 or != NULL).
*)
| TestOutputTrue of seq
and test_field_compare =
| CompareWithInt of string * int
+ | CompareWithIntOp of string * string * int
| CompareWithString of string * string
| CompareFieldsIntEq of string * string
| CompareFieldsStrEq of string * string
Setting C<qemu> to C<NULL> restores the default qemu binary.");
("get_qemu", (RConstString "qemu", []), -1, [],
- [],
+ [InitNone, Always, TestRun (
+ [["get_qemu"]])],
"get the qemu binary",
"\
Return the current qemu binary.
Setting C<path> to C<NULL> restores the default path.");
("get_path", (RConstString "path", []), -1, [],
- [],
+ [InitNone, Always, TestRun (
+ [["get_path"]])],
"get the search path",
"\
Return the current search path.
are passed (libguestfs always adds a few of its own).");
("get_append", (RConstString "append", []), -1, [],
+ (* This cannot be tested with the current framework. The
+ * function can return NULL in normal operations, which the
+ * test framework interprets as an error.
+ *)
[],
"get the additional kernel options",
"\
enabled by default).");
("get_autosync", (RBool "autosync", []), -1, [],
- [],
+ [InitNone, Always, TestRun (
+ [["get_autosync"]])],
"get autosync mode",
"\
Get the autosync flag.");
This returns the verbose messages flag.");
("is_ready", (RBool "ready", []), -1, [],
- [],
+ [InitNone, Always, TestOutputTrue (
+ [["is_ready"]])],
"is ready to accept commands",
"\
This returns true iff this handle is ready to accept commands
For more information on states, see L<guestfs(3)>.");
("is_config", (RBool "config", []), -1, [],
- [],
+ [InitNone, Always, TestOutputFalse (
+ [["is_config"]])],
"is in configuration state",
"\
This returns true iff this handle is being configured
For more information on states, see L<guestfs(3)>.");
("is_launching", (RBool "launching", []), -1, [],
- [],
+ [InitNone, Always, TestOutputFalse (
+ [["is_launching"]])],
"is launching subprocess",
"\
This returns true iff this handle is launching the subprocess
For more information on states, see L<guestfs(3)>.");
("is_busy", (RBool "busy", []), -1, [],
- [],
+ [InitNone, Always, TestOutputFalse (
+ [["is_busy"]])],
"is busy processing a command",
"\
This returns true iff this handle is busy processing a command
For more information on states, see L<guestfs(3)>.");
("set_memsize", (RErr, [Int "memsize"]), -1, [FishAlias "memsize"],
- [],
+ [InitNone, Always, TestOutputInt (
+ [["set_memsize"; "500"];
+ ["get_memsize"]], 500)],
"set memory allocated to the qemu subprocess",
"\
This sets the memory size in megabytes allocated to the
see L<guestfs(3)>.");
("get_memsize", (RInt "memsize", []), -1, [],
- [],
+ [InitNone, Always, TestOutputIntOp (
+ [["get_memsize"]], ">=", 256)],
"get memory allocated to the qemu subprocess",
"\
This gets the memory size in megabytes allocated to the
see L<guestfs(3)>.");
("get_pid", (RInt "pid", []), -1, [FishAlias "pid"],
- [],
+ [InitNone, Always, TestOutputIntOp (
+ [["get_pid"]], ">=", 1)],
"get PID of qemu subprocess",
"\
Return the process ID of the qemu subprocess. If there is no
This is an internal call used for debugging and testing.");
("version", (RStruct ("version", "version"), []), -1, [],
- [InitBasicFS, Always, TestOutputStruct (
+ [InitNone, Always, TestOutputStruct (
[["version"]], [CompareWithInt ("major", 1)])],
"get the library version number",
"\
This returns the list of currently mounted filesystems. It returns
the list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).
-Some internal mounts are not shown.");
+Some internal mounts are not shown.
+
+See also: C<guestfs_mountpoints>");
("umount_all", (RErr, []), 47, [FishAlias "unmount-all"],
[InitBasicFS, Always, TestOutputList (
("upload", (RErr, [FileIn "filename"; String "remotefilename"]), 66, [],
[InitBasicFS, Always, TestOutput (
(* Pick a file from cwd which isn't likely to change. *)
- [["upload"; "../COPYING.LIB"; "/COPYING.LIB"];
- ["checksum"; "md5"; "/COPYING.LIB"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
+ [["upload"; "../COPYING.LIB"; "/COPYING.LIB"];
+ ["checksum"; "md5"; "/COPYING.LIB"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
"upload a file from the local machine",
"\
Upload local file C<filename> to C<remotefilename> on the
("download", (RErr, [String "remotefilename"; FileOut "filename"]), 67, [],
[InitBasicFS, Always, TestOutput (
(* Pick a file from cwd which isn't likely to change. *)
- [["upload"; "../COPYING.LIB"; "/COPYING.LIB"];
- ["download"; "/COPYING.LIB"; "testdownload.tmp"];
- ["upload"; "testdownload.tmp"; "/upload"];
- ["checksum"; "md5"; "/upload"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
+ [["upload"; "../COPYING.LIB"; "/COPYING.LIB"];
+ ["download"; "/COPYING.LIB"; "testdownload.tmp"];
+ ["upload"; "testdownload.tmp"; "/upload"];
+ ["checksum"; "md5"; "/upload"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
"download a file to the local machine",
"\
Download file C<remotefilename> and save it as C<filename>
("lvresize", (RErr, [String "device"; Int "mbytes"]), 105, [],
[InitNone, Always, TestOutput (
- [["sfdiskM"; "/dev/sda"; ","];
- ["pvcreate"; "/dev/sda1"];
- ["vgcreate"; "VG"; "/dev/sda1"];
- ["lvcreate"; "LV"; "VG"; "10"];
- ["mkfs"; "ext2"; "/dev/VG/LV"];
- ["mount"; "/dev/VG/LV"; "/"];
- ["write_file"; "/new"; "test content"; "0"];
- ["umount"; "/"];
- ["lvresize"; "/dev/VG/LV"; "20"];
- ["e2fsck_f"; "/dev/VG/LV"];
- ["resize2fs"; "/dev/VG/LV"];
- ["mount"; "/dev/VG/LV"; "/"];
- ["cat"; "/new"]], "test content")],
+ [["sfdiskM"; "/dev/sda"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
+ ["lvcreate"; "LV"; "VG"; "10"];
+ ["mkfs"; "ext2"; "/dev/VG/LV"];
+ ["mount"; "/dev/VG/LV"; "/"];
+ ["write_file"; "/new"; "test content"; "0"];
+ ["umount"; "/"];
+ ["lvresize"; "/dev/VG/LV"; "20"];
+ ["e2fsck_f"; "/dev/VG/LV"];
+ ["resize2fs"; "/dev/VG/LV"];
+ ["mount"; "/dev/VG/LV"; "/"];
+ ["cat"; "/new"]], "test content")],
"resize an LVM logical volume",
"\
This resizes (expands or shrinks) an existing LVM logical
("sleep", (RErr, [Int "secs"]), 109, [],
[InitNone, Always, TestRun (
- [["sleep"; "1"]])],
+ [["sleep"; "1"]])],
"sleep for some seconds",
"\
Sleep for C<secs> seconds.");
See also C<guestfs_sfdisk> and the L<sfdisk(8)> manpage.");
+ ("zfile", (RString "description", [String "method"; String "path"]), 140, [],
+ [],
+ "determine file type inside a compressed file",
+ "\
+This command runs C<file> after first decompressing C<path>
+using C<method>.
+
+C<method> must be one of C<gzip>, C<compress> or C<bzip2>.
+
+See also: C<guestfs_file>");
+
+ ("getxattrs", (RStructList ("xattrs", "xattr"), [String "path"]), 141, [],
+ [],
+ "list extended attributes of a file or directory",
+ "\
+This call lists the extended attributes of the file or directory
+C<path>.
+
+At the system call level, this is a combination of the
+L<listxattr(2)> and L<getxattr(2)> calls.
+
+See also: C<guestfs_lgetxattrs>, L<attr(5)>.");
+
+ ("lgetxattrs", (RStructList ("xattrs", "xattr"), [String "path"]), 142, [],
+ [],
+ "list extended attributes of a file or directory",
+ "\
+This is the same as C<guestfs_getxattrs>, but if C<path>
+is a symbolic link, then it returns the extended attributes
+of the link itself.");
+
+ ("setxattr", (RErr, [String "xattr";
+ String "val"; Int "vallen"; (* will be BufferIn *)
+ String "path"]), 143, [],
+ [],
+ "set extended attribute of a file or directory",
+ "\
+This call sets the extended attribute named C<xattr>
+of the file C<path> to the value C<val> (of length C<vallen>).
+The value is arbitrary 8 bit data.
+
+See also: C<guestfs_lsetxattr>, L<attr(5)>.");
+
+ ("lsetxattr", (RErr, [String "xattr";
+ String "val"; Int "vallen"; (* will be BufferIn *)
+ String "path"]), 144, [],
+ [],
+ "set extended attribute of a file or directory",
+ "\
+This is the same as C<guestfs_setxattr>, but if C<path>
+is a symbolic link, then it sets an extended attribute
+of the link itself.");
+
+ ("removexattr", (RErr, [String "xattr"; String "path"]), 145, [],
+ [],
+ "remove extended attribute of a file or directory",
+ "\
+This call removes the extended attribute named C<xattr>
+of the file C<path>.
+
+See also: C<guestfs_lremovexattr>, L<attr(5)>.");
+
+ ("lremovexattr", (RErr, [String "xattr"; String "path"]), 146, [],
+ [],
+ "remove extended attribute of a file or directory",
+ "\
+This is the same as C<guestfs_removexattr>, but if C<path>
+is a symbolic link, then it removes an extended attribute
+of the link itself.");
+
+ ("mountpoints", (RHashtable "mps", []), 147, [],
+ [],
+ "show mountpoints",
+ "\
+This call is similar to C<guestfs_mounts>. That call returns
+a list of devices. This one returns a hash table (map) of
+device name to directory where the device is mounted.");
+
+ ("mkmountpoint", (RErr, [String "path"]), 148, [],
+ [],
+ "create a mountpoint",
+ "\
+C<guestfs_mkmountpoint> and C<guestfs_rmmountpoint> are
+specialized calls that can be used to create extra mountpoints
+before mounting the first filesystem.
+
+These calls are I<only> necessary in some very limited circumstances,
+mainly the case where you want to mount a mix of unrelated and/or
+read-only filesystems together.
+
+For example, live CDs often contain a \"Russian doll\" nest of
+filesystems, an ISO outer layer, with a squashfs image inside, with
+an ext2/3 image inside that. You can unpack this as follows
+in guestfish:
+
+ add-ro Fedora-11-i686-Live.iso
+ run
+ mkmountpoint /cd
+ mkmountpoint /squash
+ mkmountpoint /ext3
+ mount /dev/sda /cd
+ mount-loop /cd/LiveOS/squashfs.img /squash
+ mount-loop /squash/LiveOS/ext3fs.img /ext3
+
+The inner filesystem is now unpacked under the /ext3 mountpoint.");
+
+ ("rmmountpoint", (RErr, [String "path"]), 149, [],
+ [],
+ "remove a mountpoint",
+ "\
+This calls removes a mountpoint that was previously created
+with C<guestfs_mkmountpoint>. See C<guestfs_mkmountpoint>
+for full details.");
+
]
let all_functions = non_daemon_functions @ daemon_functions
type field =
| FChar (* C 'char' (really, a 7 bit byte). *)
| FString (* nul-terminated ASCII string. *)
+ | FBuffer (* opaque buffer of bytes, (char *, int) pair *)
| FUInt32
| FInt32
| FUInt64
"release", FInt64;
"extra", FString;
];
+
+ (* Extended attribute. *)
+ "xattr", [
+ "attrname", FString;
+ "attrval", FBuffer;
+ ];
] (* end of structs *)
(* Ugh, Java has to be different ..
"statvfs", "StatVFS";
"dirent", "Dirent";
"version", "Version";
+ "xattr", "XAttr";
]
(* Used for testing language bindings. *)
let seq_of_test = function
| TestRun s | TestOutput (s, _) | TestOutputList (s, _)
| TestOutputListOfDevices (s, _)
- | TestOutputInt (s, _) | TestOutputTrue s | TestOutputFalse s
+ | TestOutputInt (s, _) | TestOutputIntOp (s, _, _)
+ | TestOutputTrue s | TestOutputFalse s
| TestOutputLength (s, _) | TestOutputStruct (s, _)
| TestLastFail s -> s
| name, (FUInt64|FBytes) -> pr " uint64_t %s;\n" name
| name, FInt64 -> pr " int64_t %s;\n" name
| name, FString -> pr " char *%s;\n" name
+ | name, FBuffer ->
+ pr " /* The next two fields describe a byte array. */\n";
+ pr " uint32_t %s_len;\n" name;
+ pr " char *%s;\n" name
| name, FUUID ->
pr " /* The next field is NOT nul-terminated, be careful when printing it: */\n";
pr " char %s[32];\n" name
List.iter (function
| name, FChar -> pr " char %s;\n" name
| name, FString -> pr " string %s<>;\n" name
+ | name, FBuffer -> pr " opaque %s<>;\n" name
| name, FUUID -> pr " opaque %s[32];\n" name
| name, (FInt32|FUInt32) -> pr " int %s;\n" name
| name, (FInt64|FUInt64|FBytes) -> pr " hyper %s;\n" name
function
| name, FChar -> pr " char %s;\n" name
| name, FString -> pr " char *%s;\n" name
+ | name, FBuffer ->
+ pr " uint32_t %s_len;\n" name;
+ pr " char *%s;\n" name
| name, FUUID -> pr " char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
| name, FUInt32 -> pr " uint32_t %s;\n" name
| name, FInt32 -> pr " int32_t %s;\n" name
{
if (!guestfs_is_ready (g)) {
if (guestfs_is_config (g))
- error (g, \"%%s: call launch() before using this function\",
+ error (g, \"%%s: call launch before using this function\\n(in guestfish, don't forget to use the 'run' command)\",
caller);
else if (guestfs_is_launching (g))
error (g, \"%%s: call wait_ready() before using this function\",
pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
pr " return -1;\n";
pr " }\n";
- | FInt32 | FUInt32 | FUInt64 | FChar ->
+ | FBuffer | FInt32 | FUInt32 | FUInt64 | FChar ->
assert false (* can never be an LVM column *)
);
pr " tok = next;\n";
and generate_one_test_body name i test_name init test =
(match init with
- | InitNone
+ | InitNone (* XXX at some point, InitNone and InitEmpty became
+ * folded together as the same thing. Really we should
+ * make InitNone do nothing at all, but the tests may
+ * need to be checked to make sure this is OK.
+ *)
| InitEmpty ->
pr " /* InitNone|InitEmpty for %s */\n" test_name;
List.iter (generate_test_command_call test_name)
in
List.iter (generate_test_command_call test_name) seq;
generate_test_command_call ~test test_name last
+ | TestOutputIntOp (seq, op, expected) ->
+ pr " /* TestOutputIntOp for %s (%d) */\n" name i;
+ let seq, last = get_seq_last seq in
+ let test () =
+ pr " if (! (r %s %d)) {\n" op expected;
+ pr " fprintf (stderr, \"%s: expected %s %d but got %%d\\n\","
+ test_name op expected;
+ pr " (int) r);\n";
+ pr " return -1;\n";
+ pr " }\n"
+ in
+ List.iter (generate_test_command_call test_name) seq;
+ generate_test_command_call ~test test_name last
| TestOutputTrue seq ->
pr " /* TestOutputTrue for %s (%d) */\n" name i;
let seq, last = get_seq_last seq in
pr " (int) r->%s);\n" field;
pr " return -1;\n";
pr " }\n"
+ | CompareWithIntOp (field, op, expected) ->
+ pr " if (!(r->%s %s %d)) {\n" field op expected;
+ pr " fprintf (stderr, \"%s: %s was %%d, expected %s %d\\n\",\n"
+ test_name field op expected;
+ pr " (int) r->%s);\n" field;
+ pr " return -1;\n";
+ pr " }\n"
| CompareWithString (field, expected) ->
pr " if (strcmp (r->%s, \"%s\") != 0) {\n" field expected;
pr " fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
pr "#include <stdlib.h>\n";
pr "#include <string.h>\n";
pr "#include <inttypes.h>\n";
+ pr "#include <ctype.h>\n";
pr "\n";
pr "#include <guestfs.h>\n";
pr "#include \"fish.h\"\n";
List.iter (
fun (typ, cols) ->
let needs_i =
- List.exists (function (_, FUUID) -> true | _ -> false) cols in
+ List.exists (function (_, (FUUID|FBuffer)) -> true | _ -> false) cols in
pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
pr "{\n";
pr " for (i = 0; i < 32; ++i)\n";
pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
pr " printf (\"\\n\");\n"
+ | name, FBuffer ->
+ pr " printf (\"%s: \");\n" name;
+ pr " for (i = 0; i < %s->%s_len; ++i)\n" typ name;
+ pr " if (isprint (%s->%s[i]))\n" typ name;
+ pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
+ pr " else\n";
+ pr " printf (\"\\\\x%%02x\", %s->%s[i]);\n" typ name;
+ pr " printf (\"\\n\");\n"
| name, (FUInt64|FBytes) ->
pr " printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
| name, FInt64 ->
(match col with
| name, FString ->
pr " v = caml_copy_string (%s->%s);\n" typ name
+ | name, FBuffer ->
+ pr " v = caml_alloc_string (%s->%s_len);\n" typ name;
+ pr " memcpy (String_val (v), %s->%s, %s->%s_len);\n"
+ typ name typ name
| name, FUUID ->
pr " v = caml_alloc_string (32);\n";
pr " memcpy (String_val (v), %s->%s, 32);\n" typ name
List.iter (
function
| name, FString -> pr " %s : string;\n" name
+ | name, FBuffer -> pr " %s : string;\n" name
| name, FUUID -> pr " %s : string;\n" name
| name, (FBytes|FInt64|FUInt64) -> pr " %s : int64;\n" name
| name, (FInt32|FUInt32) -> pr " %s : int32;\n" name
| name, FUUID ->
pr " (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 32), 0);\n"
name (String.length name) n name
+ | name, FBuffer ->
+ pr " (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, %s->val[i].%s_len), 0);\n"
+ name (String.length name) n name n name
| name, (FBytes|FUInt64) ->
pr " (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n"
name (String.length name) n name
| name, FString ->
pr " PUSHs (sv_2mortal (newSVpv (%s->%s, 0)));\n"
n name
+ | name, FBuffer ->
+ pr " PUSHs (sv_2mortal (newSVpv (%s->%s, %s->%s_len)));\n"
+ n name n name
| name, FUUID ->
pr " PUSHs (sv_2mortal (newSVpv (%s->%s, 32)));\n"
n name
pr " PyDict_SetItemString (dict, \"%s\",\n" name;
pr " PyString_FromString (%s->%s));\n"
typ name
+ | name, FBuffer ->
+ pr " PyDict_SetItemString (dict, \"%s\",\n" name;
+ pr " PyString_FromStringAndSize (%s->%s, %s->%s_len));\n"
+ typ name typ name
| name, FUUID ->
pr " PyDict_SetItemString (dict, \"%s\",\n" name;
pr " PyString_FromStringAndSize (%s->%s, 32));\n"
function
| name, FString ->
pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name
+ | name, FBuffer ->
+ pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name
| name, FUUID ->
pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, 32));\n" name name
| name, (FBytes|FUInt64) ->
function
| name, FString ->
pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
+ | name, FBuffer ->
+ pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, r->val[i].%s_len));\n" name name name
| name, FUUID ->
pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
| name, (FBytes|FUInt64) ->
List.iter (
function
| name, FString
- | name, FUUID -> pr " public String %s;\n" name
+ | name, FUUID
+ | name, FBuffer -> pr " public String %s;\n" name
| name, (FBytes|FUInt64|FInt64) -> pr " public long %s;\n" name
| name, (FUInt32|FInt32) -> pr " public int %s;\n" name
| name, FChar -> pr " public char %s;\n" name
pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
pr " (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n";
pr " }\n";
+ | name, FBuffer ->
+ pr " {\n";
+ pr " int len = r->%s_len;\n" name;
+ pr " char s[len+1];\n";
+ pr " memcpy (s, r->%s, len);\n" name;
+ pr " s[len] = 0;\n";
+ pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
+ pr " (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n";
+ pr " }\n";
| name, (FBytes|FUInt64|FInt64) ->
pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
pr " (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
pr " (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
pr " }\n";
+ | name, FBuffer ->
+ pr " {\n";
+ pr " int len = r->val[i].%s_len;\n" name;
+ pr " char s[len+1];\n";
+ pr " memcpy (s, r->val[i].%s, len);\n" name;
+ pr " s[len] = 0;\n";
+ pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
+ pr " (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
+ pr " }\n";
| name, (FBytes|FUInt64|FInt64) ->
pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
pr " (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
close ();
) java_structs;
+ let close = output_to "java/Makefile.inc" in
+ pr "java_built_sources =";
+ List.iter (
+ fun (typ, jtyp) ->
+ pr " com/redhat/et/libguestfs/%s.java" jtyp;
+ ) java_structs;
+ pr " com/redhat/et/libguestfs/GuestFS.java\n";
+ close ();
+
let close = output_to "java/com_redhat_et_libguestfs_GuestFS.c" in
generate_java_c ();
close ();