(* You can supply zero or as many tests as you want per API call.
*
* Note that the test environment has 3 block devices, of size 500MB,
- * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc).
+ * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc), and
+ * a fourth squashfs block device with some known files on it (/dev/sdd).
+ *
* Note for partitioning purposes, the 500MB device has 63 cylinders.
*
+ * The squashfs block device (/dev/sdd) comes from images/test.sqsh.
+ *
* To be able to run the tests in a reasonable amount of time,
* the virtual machine and block devices are reused between tests.
* So don't try testing kill_subprocess :-x
*
* Between each test we blockdev-setrw, umount-all, lvm-remove-all.
*
- * If the appliance is running an older Linux kernel (eg. RHEL 5) then
- * devices are named /dev/hda etc. To cope with this, the test suite
- * adds some hairly logic to detect this case, and then automagically
- * replaces all strings which match "/dev/sd.*" with "/dev/hd.*".
- * When writing test cases you shouldn't have to worry about this
- * difference.
- *
* Don't assume anything about the previous contents of the block
* devices. Use 'Init*' to create some initial scenarios.
*
*)
| TestOutputList of seq * string list
(* Run the command sequence and expect the output of the final
+ * command to be the list of block devices (could be either
+ * "/dev/sd.." or "/dev/hd.." form - we don't check the 5th
+ * character of each string).
+ *)
+ | TestOutputListOfDevices of seq * string list
+ (* Run the command sequence and expect the output of the final
* command to be the integer.
*)
| TestOutputInt of seq * int
just want to read the image or write access if you want to modify the
image).
-This is equivalent to the qemu parameter C<-drive file=filename>.");
+This is equivalent to the qemu parameter C<-drive file=filename,cache=off>.
+
+Note that this call checks for the existence of C<filename>. This
+stops you from specifying other types of drive which are supported
+by qemu such as C<nbd:> and C<http:> URLs. To specify those, use
+the general C<guestfs_config> call instead.");
("add_cdrom", (RErr, [String "filename"]), -1, [FishAlias "cdrom"],
[],
"\
This function adds a virtual CD-ROM disk image to the guest.
-This is equivalent to the qemu parameter C<-cdrom filename>.");
+This is equivalent to the qemu parameter C<-cdrom filename>.
+
+Note that this call checks for the existence of C<filename>. This
+stops you from specifying other types of drive which are supported
+by qemu such as C<nbd:> and C<http:> URLs. To specify those, use
+the general C<guestfs_config> call instead.");
+
+ ("add_drive_ro", (RErr, [String "filename"]), -1, [FishAlias "add-ro"],
+ [],
+ "add a drive in snapshot mode (read-only)",
+ "\
+This adds a drive in snapshot mode, making it effectively
+read-only.
+
+Note that writes to the device are allowed, and will be seen for
+the duration of the guestfs handle, but they are written
+to a temporary file which is discarded as soon as the guestfs
+handle is closed. We don't currently have any method to enable
+changes to be committed, although qemu can support this.
+
+This is equivalent to the qemu parameter
+C<-drive file=filename,snapshot=on>.
+
+Note that this call checks for the existence of C<filename>. This
+stops you from specifying other types of drive which are supported
+by qemu such as C<nbd:> and C<http:> URLs. To specify those, use
+the general C<guestfs_config> call instead.");
("config", (RErr, [String "qemuparam"; OptString "qemuvalue"]), -1, [],
[],
should probably use C<guestfs_readdir> instead.");
("list_devices", (RStringList "devices", []), 7, [],
- [InitEmpty, Always, TestOutputList (
- [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"])],
+ [InitEmpty, Always, TestOutputListOfDevices (
+ [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"; "/dev/sdd"])],
"list the block devices",
"\
List all the block devices.
The full block device names are returned, eg. C</dev/sda>");
("list_partitions", (RStringList "partitions", []), 8, [],
- [InitBasicFS, Always, TestOutputList (
+ [InitBasicFS, Always, TestOutputListOfDevices (
[["list_partitions"]], ["/dev/sda1"]);
- InitEmpty, Always, TestOutputList (
+ InitEmpty, Always, TestOutputListOfDevices (
[["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
"list the partitions",
call C<guestfs_lvs>.");
("pvs", (RStringList "physvols", []), 9, [],
- [InitBasicFSonLVM, Always, TestOutputList (
+ [InitBasicFSonLVM, Always, TestOutputListOfDevices (
[["pvs"]], ["/dev/sda1"]);
- InitEmpty, Always, TestOutputList (
+ InitEmpty, Always, TestOutputListOfDevices (
[["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
["pvcreate"; "/dev/sda1"];
["pvcreate"; "/dev/sda2"];
See also C<guestfs_stat>.");
("pvcreate", (RErr, [String "device"]), 39, [],
- [InitEmpty, Always, TestOutputList (
+ [InitEmpty, Always, TestOutputListOfDevices (
[["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
["pvcreate"; "/dev/sda1"];
["pvcreate"; "/dev/sda2"];
use C<guestfs_upload>.");
("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
- [InitEmpty, Always, TestOutputList (
+ [InitEmpty, Always, TestOutputListOfDevices (
[["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
["mkfs"; "ext2"; "/dev/sda1"];
["mount"; "/dev/sda1"; "/"];
contains the filesystem.");
("mounts", (RStringList "devices", []), 46, [],
- [InitBasicFS, Always, TestOutputList (
+ [InitBasicFS, Always, TestOutputListOfDevices (
[["mounts"]], ["/dev/sda1"])],
"show mounted filesystems",
"\
The single parameter is an argv-style list of arguments.
The first element is the name of the program to run.
Subsequent elements are parameters. The list must be
-non-empty (ie. must contain a program name).
+non-empty (ie. must contain a program name). Note that
+the command runs directly, and is I<not> invoked via
+the shell (see C<guestfs_sh>).
The return value is anything printed to I<stdout> by
the command.
"run a command, returning lines",
"\
This is the same as C<guestfs_command>, but splits the
-result into a list of lines.");
+result into a list of lines.
+
+See also: C<guestfs_sh_lines>");
("stat", (RStat "statbuf", [String "path"]), 52, [],
[InitBasicFS, Always, TestOutputStruct (
["checksum"; "sha384"; "/new"]], "109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d");
InitBasicFS, Always, TestOutput (
[["write_file"; "/new"; "test\n"; "0"];
- ["checksum"; "sha512"; "/new"]], "0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123")],
+ ["checksum"; "sha512"; "/new"]], "0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123");
+ InitBasicFS, Always, TestOutput (
+ (* RHEL 5 thinks this is an HFS+ filesystem unless we give
+ * the type explicitly.
+ *)
+ [["mount_vfs"; "ro"; "squashfs"; "/dev/sdd"; "/"];
+ ["checksum"; "md5"; "/known-3"]], "46d6ca27ee07cdc6fa99c2e138cc522c")],
"compute MD5, SHAx or CRC checksum of file",
"\
This call computes the MD5, SHAx or CRC checksum of the
group (if any).");
("pvremove", (RErr, [String "device"]), 79, [],
- [InitEmpty, Always, TestOutputList (
+ [InitEmpty, Always, TestOutputListOfDevices (
[["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
["pvcreate"; "/dev/sda1"];
["vgcreate"; "VG"; "/dev/sda1"];
["vgremove"; "VG"];
["pvremove"; "/dev/sda1"];
["lvs"]], []);
- InitEmpty, Always, TestOutputList (
+ InitEmpty, Always, TestOutputListOfDevices (
[["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
["pvcreate"; "/dev/sda1"];
["vgcreate"; "VG"; "/dev/sda1"];
["vgremove"; "VG"];
["pvremove"; "/dev/sda1"];
["vgs"]], []);
- InitEmpty, Always, TestOutputList (
+ InitEmpty, Always, TestOutputListOfDevices (
[["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
["pvcreate"; "/dev/sda1"];
["vgcreate"; "VG"; "/dev/sda1"];
How many blocks are zeroed isn't specified (but it's I<not> enough
to securely wipe the device). It should be sufficient to remove
-any partition tables, filesystem superblocks and so on.");
+any partition tables, filesystem superblocks and so on.
+
+See also: C<guestfs_scrub_device>.");
("grub_install", (RErr, [String "root"; String "device"]), 86, [],
[InitBasicFS, Always, TestOutputTrue (
This command is only needed because of C<guestfs_resize2fs>
(q.v.). Normally you should use C<guestfs_fsck>.");
+ ("sleep", (RErr, [Int "secs"]), 109, [],
+ [InitNone, Always, TestRun (
+ [["sleep"; "1"]])],
+ "sleep for some seconds",
+ "\
+Sleep for C<secs> seconds.");
+
+ ("ntfs_3g_probe", (RInt "status", [Bool "rw"; String "device"]), 110, [],
+ [InitNone, Always, TestOutputInt (
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["mkfs"; "ntfs"; "/dev/sda1"];
+ ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 0);
+ InitNone, Always, TestOutputInt (
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["mkfs"; "ext2"; "/dev/sda1"];
+ ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 12)],
+ "probe NTFS volume",
+ "\
+This command runs the L<ntfs-3g.probe(8)> command which probes
+an NTFS C<device> for mountability. (Not all NTFS volumes can
+be mounted read-write, and some cannot be mounted at all).
+
+C<rw> is a boolean flag. Set it to true if you want to test
+if the volume can be mounted read-write. Set it to false if
+you want to test if the volume can be mounted read-only.
+
+The return value is an integer which C<0> if the operation
+would succeed, or some non-zero value documented in the
+L<ntfs-3g.probe(8)> manual page.");
+
+ ("sh", (RString "output", [String "command"]), 111, [],
+ [], (* XXX needs tests *)
+ "run a command via the shell",
+ "\
+This call runs a command from the guest filesystem via the
+guest's C</bin/sh>.
+
+This is like C<guestfs_command>, but passes the command to:
+
+ /bin/sh -c \"command\"
+
+Depending on the guest's shell, this usually results in
+wildcards being expanded, shell expressions being interpolated
+and so on.
+
+All the provisos about C<guestfs_command> apply to this call.");
+
+ ("sh_lines", (RStringList "lines", [String "command"]), 112, [],
+ [], (* XXX needs tests *)
+ "run a command via the shell returning lines",
+ "\
+This is the same as C<guestfs_sh>, but splits the result
+into a list of lines.
+
+See also: C<guestfs_command_lines>");
+
+ ("glob_expand", (RStringList "paths", [String "pattern"]), 113, [],
+ [InitBasicFS, Always, TestOutputList (
+ [["mkdir_p"; "/a/b/c"];
+ ["touch"; "/a/b/c/d"];
+ ["touch"; "/a/b/c/e"];
+ ["glob_expand"; "/a/b/c/*"]], ["/a/b/c/d"; "/a/b/c/e"]);
+ InitBasicFS, Always, TestOutputList (
+ [["mkdir_p"; "/a/b/c"];
+ ["touch"; "/a/b/c/d"];
+ ["touch"; "/a/b/c/e"];
+ ["glob_expand"; "/a/*/c/*"]], ["/a/b/c/d"; "/a/b/c/e"]);
+ InitBasicFS, Always, TestOutputList (
+ [["mkdir_p"; "/a/b/c"];
+ ["touch"; "/a/b/c/d"];
+ ["touch"; "/a/b/c/e"];
+ ["glob_expand"; "/a/*/x/*"]], [])],
+ "expand a wildcard path",
+ "\
+This command searches for all the pathnames matching
+C<pattern> according to the wildcard expansion rules
+used by the shell.
+
+If no paths match, then this returns an empty list
+(note: not an error).
+
+It is just a wrapper around the C L<glob(3)> function
+with flags C<GLOB_MARK|GLOB_BRACE>.
+See that manual page for more details.");
+
+ ("scrub_device", (RErr, [String "device"]), 114, [DangerWillRobinson],
+ [InitNone, Always, TestRun ( (* use /dev/sdc because it's smaller *)
+ [["scrub_device"; "/dev/sdc"]])],
+ "scrub (securely wipe) a device",
+ "\
+This command writes patterns over C<device> to make data retrieval
+more difficult.
+
+It is an interface to the L<scrub(1)> program. See that
+manual page for more details.");
+
+ ("scrub_file", (RErr, [String "file"]), 115, [],
+ [InitBasicFS, Always, TestRun (
+ [["write_file"; "/file"; "content"; "0"];
+ ["scrub_file"; "/file"]])],
+ "scrub (securely wipe) a file",
+ "\
+This command writes patterns over a file to make data retrieval
+more difficult.
+
+The file is I<removed> after scrubbing.
+
+It is an interface to the L<scrub(1)> program. See that
+manual page for more details.");
+
+ ("scrub_freespace", (RErr, [String "dir"]), 116, [],
+ [], (* XXX needs testing *)
+ "scrub (securely wipe) free space",
+ "\
+This command creates the directory C<dir> and then fills it
+with files until the filesystem is full, and scrubs the files
+as for C<guestfs_scrub_file>, and deletes them.
+The intention is to scrub any free space on the partition
+containing C<dir>.
+
+It is an interface to the L<scrub(1)> program. See that
+manual page for more details.");
+
+ ("mkdtemp", (RString "dir", [String "template"]), 117, [],
+ [InitBasicFS, Always, TestRun (
+ [["mkdir"; "/tmp"];
+ ["mkdtemp"; "/tmp/tmpXXXXXX"]])],
+ "create a temporary directory",
+ "\
+This command creates a temporary directory. The
+C<template> parameter should be a full pathname for the
+temporary directory name with the final six characters being
+\"XXXXXX\".
+
+For example: \"/tmp/myprogXXXXXX\" or \"/Temp/myprogXXXXXX\",
+the second one being suitable for Windows filesystems.
+
+The name of the temporary directory that was created
+is returned.
+
+The temporary directory is created with mode 0700
+and is owned by root.
+
+The caller is responsible for deleting the temporary
+directory and its contents after use.
+
+See also: L<mkdtemp(3)>");
+
]
let all_functions = non_daemon_functions @ daemon_functions
let seq_of_test = function
| TestRun s | TestOutput (s, _) | TestOutputList (s, _)
+ | TestOutputListOfDevices (s, _)
| TestOutputInt (s, _) | TestOutputTrue s | TestOutputFalse s
| TestOutputLength (s, _) | TestOutputStruct (s, _)
| TestLastFail s -> s
pr " struct guestfs_%s_args args;\n" name;
List.iter (
function
+ (* Note we allow the string to be writable, in order to
+ * allow device name translation. This is safe because
+ * we can modify the string (passed from RPC).
+ *)
| String n
- | OptString n -> pr " const char *%s;\n" n
+ | OptString n -> pr " char *%s;\n" n
| StringList n -> pr " char **%s;\n" n
| Bool n -> pr " int %s;\n" n
| Int n -> pr " int %s;\n" n
) daemon_functions;
pr " default:\n";
- pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
+ pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory\", proc_nr);\n";
pr " }\n";
pr "}\n";
pr "\n";
static guestfs_h *g;
static int suppress_error = 0;
-/* This will be 's' or 'h' depending on whether the guest kernel
- * names IDE devices /dev/sd* or /dev/hd*.
- */
-static char devchar = 's';
-
static void print_error (guestfs_h *g, void *data, const char *msg)
{
if (!suppress_error)
{
char c = 0;
int failed = 0;
- const char *srcdir;
const char *filename;
- int fd, i;
+ int fd;
int nr_tests, test_num = 0;
- char **devs;
no_test_warnings ();
exit (1);
}
+ if (guestfs_add_drive_ro (g, \"../images/test.sqsh\") == -1) {
+ printf (\"guestfs_add_drive_ro ../images/test.sqsh FAILED\\n\");
+ exit (1);
+ }
+
if (guestfs_launch (g) == -1) {
printf (\"guestfs_launch FAILED\\n\");
exit (1);
}
+
+ /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */
+ alarm (600);
+
if (guestfs_wait_ready (g) == -1) {
printf (\"guestfs_wait_ready FAILED\\n\");
exit (1);
}
- /* Detect if the appliance uses /dev/sd* or /dev/hd* in device
- * names. This changed between RHEL 5 and RHEL 6 so we have to
- * support both.
- */
- devs = guestfs_list_devices (g);
- if (devs == NULL || devs[0] == NULL) {
- printf (\"guestfs_list_devices FAILED\\n\");
- exit (1);
- }
- if (strncmp (devs[0], \"/dev/sd\", 7) == 0)
- devchar = 's';
- else if (strncmp (devs[0], \"/dev/hd\", 7) == 0)
- devchar = 'h';
- else {
- printf (\"guestfs_list_devices returned unexpected string '%%s'\\n\",
- devs[0]);
- exit (1);
- }
- for (i = 0; devs[i] != NULL; ++i)
- free (devs[i]);
- free (devs);
+ /* Cancel previous alarm. */
+ alarm (0);
nr_tests = %d;
| TestOutput (seq, expected) ->
pr " /* TestOutput for %s (%d) */\n" name i;
pr " char expected[] = \"%s\";\n" (c_quote expected);
- if String.length expected > 7 &&
- String.sub expected 0 7 = "/dev/sd" then
- pr " expected[5] = devchar;\n";
let seq, last = get_seq_last seq in
let test () =
pr " if (strcmp (r, expected) != 0) {\n";
pr " }\n";
pr " {\n";
pr " char expected[] = \"%s\";\n" (c_quote str);
- if String.length str > 7 && String.sub str 0 7 = "/dev/sd" then
- pr " expected[5] = devchar;\n";
+ pr " if (strcmp (r[%d], expected) != 0) {\n" i;
+ pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
+ pr " return -1;\n";
+ pr " }\n";
+ pr " }\n"
+ ) expected;
+ pr " if (r[%d] != NULL) {\n" (List.length expected);
+ pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
+ test_name;
+ pr " print_strings (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
+ | TestOutputListOfDevices (seq, expected) ->
+ pr " /* TestOutputListOfDevices for %s (%d) */\n" name i;
+ let seq, last = get_seq_last seq in
+ let test () =
+ iteri (
+ fun i str ->
+ pr " if (!r[%d]) {\n" i;
+ pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
+ pr " print_strings (r);\n";
+ pr " return -1;\n";
+ pr " }\n";
+ pr " {\n";
+ pr " char expected[] = \"%s\";\n" (c_quote str);
+ pr " r[%d][5] = 's';\n" i;
pr " if (strcmp (r[%d], expected) != 0) {\n" i;
pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
pr " return -1;\n";
| String n, arg
| OptString n, arg ->
pr " char %s[] = \"%s\";\n" n (c_quote arg);
- if String.length arg > 7 && String.sub arg 0 7 = "/dev/sd" then
- pr " %s[5] = devchar;\n" n
| Int _, _
| Bool _, _
| FileIn _, _ | FileOut _, _ -> ()
iteri (
fun i str ->
pr " char %s_%d[] = \"%s\";\n" n i (c_quote str);
- if String.length str > 7 && String.sub str 0 7 = "/dev/sd" then
- pr " %s_%d[5] = devchar;\n" n i
) strs;
pr " char *%s[] = {\n" n;
iteri (
len = strlen (text);
}
+ rl_attempted_completion_over = 1;
+
while ((name = commands[index]) != NULL) {
index++;
if (strncasecmp (name, text, len) == 0)
char **matches = NULL;
#ifdef HAVE_LIBREADLINE
+ rl_completion_append_character = ' ';
+
if (start == 0)
matches = rl_completion_matches (text, generator);
+ else if (complete_dest_paths)
+ matches = rl_completion_matches (text, complete_dest_paths_generator);
#endif
return matches;
List.iter (
function
| String n
- | OptString n -> next (); pr "const char *%s" n
- | StringList n -> next (); pr "char * const* const %s" n
+ | OptString n ->
+ next ();
+ if not in_daemon then pr "const char *%s" n
+ else pr "char *%s" n
+ | StringList n ->
+ next ();
+ if not in_daemon then pr "char * const* const %s" n
+ else pr "char **%s" n
| Bool n -> next (); pr "int %s" n
| Int n -> next (); pr "int %s" n
| FileIn n
List.iter (
function
| String n | FileIn n | FileOut n ->
+ pr " Check_Type (%sv, T_STRING);\n" n;
pr " const char *%s = StringValueCStr (%sv);\n" n n;
pr " if (!%s)\n" n;
pr " rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
| OptString n ->
pr " const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n
| StringList n ->
- pr " char **%s;" n;
+ pr " char **%s;\n" n;
+ pr " Check_Type (%sv, T_ARRAY);\n" n;
pr " {\n";
pr " int i, len;\n";
pr " len = RARRAY_LEN (%sv);\n" n;
*)
let can_generate style =
let check_no_bad_args =
- List.for_all (function Bool _ | Int _ -> false | _ -> true)
+ List.for_all (function Bool _ -> false | _ -> true)
in
match style with
| RErr, args -> check_no_bad_args args
) where
import Foreign
import Foreign.C
+import Foreign.C.Types
import IO
import Control.Exception
import Data.Typeable
pr "%s %s = do\n" name
(String.concat " " ("h" :: List.map name_of_argt (snd style)));
pr " r <- ";
+ (* Convert pointer arguments using with* functions. *)
List.iter (
function
| FileIn n
| String n -> pr "withCString %s $ \\%s -> " n n
| OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n
| StringList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n
- | Bool n ->
- (* XXX this doesn't work *)
- pr " let\n";
- pr " %s = case %s of\n" n n;
- pr " False -> 0\n";
- pr " True -> 1\n";
- pr " in fromIntegral %s $ \\%s ->\n" n n
- | Int n -> pr "fromIntegral %s $ \\%s -> " n n
+ | Bool _ | Int _ -> ()
) (snd style);
+ (* Convert integer arguments. *)
+ let args =
+ List.map (
+ function
+ | Bool n -> sprintf "(fromIntegral %s)" n
+ | Int n -> sprintf "(fromIntegral %s)" n
+ | FileIn n | FileOut n | String n | OptString n | StringList n -> n
+ ) (snd style) in
pr "withForeignPtr h (\\p -> c_%s %s)\n" name
- (String.concat " " ("p" :: List.map name_of_argt (snd style)));
+ (String.concat " " ("p" :: args));
(match fst style with
| RErr | RInt _ | RInt64 _ | RBool _ ->
pr " if (r == -1)\n";
(* XXX Add here tests of the return and error functions. *)
+(* This is used to generate the src/MAX_PROC_NR file which
+ * contains the maximum procedure number, a surrogate for the
+ * ABI version number. See src/Makefile.am for the details.
+ *)
+and generate_max_proc_nr () =
+ let proc_nrs = List.map (
+ fun (_, _, proc_nr, _, _, _, _) -> proc_nr
+ ) daemon_functions in
+
+ let max_proc_nr = List.fold_left max 0 proc_nrs in
+
+ pr "%d\n" max_proc_nr
+
let output_to filename =
let filename_new = filename ^ ".new" in
chan := open_out filename_new;
let close = output_to "haskell/bindtests.hs" in
generate_haskell_bindtests ();
close ();
+
+ let close = output_to "src/MAX_PROC_NR" in
+ generate_max_proc_nr ();
+ close ();