Added bindings for GNU readline.
[libguestfs.git] / src / generator.ml
index 84ee90f..6ef8e1b 100755 (executable)
@@ -950,6 +950,57 @@ Some internal mounts are not unmounted by this call.");
 This command removes all LVM logical volumes, volume groups
 and physical volumes.");
 
+  ("file", (RString "description", [String "path"]), 49, [],
+   [InitBasicFS, TestOutput (
+      [["touch"; "/new"];
+       ["file"; "/new"]], "empty");
+    InitBasicFS, TestOutput (
+      [["write_file"; "/new"; "some content\n"; "0"];
+       ["file"; "/new"]], "ASCII text");
+    InitBasicFS, TestLastFail (
+      [["file"; "/nofile"]])],
+   "determine file type",
+   "\
+This call uses the standard L<file(1)> command to determine
+the type or contents of the file.  This also works on devices,
+for example to find out whether a partition contains a filesystem.
+
+The exact command which runs is C<file -bsL path>.  Note in
+particular that the filename is not prepended to the output
+(the C<-b> option).");
+
+  ("command", (RString "output", [StringList "arguments"]), 50, [],
+   [], (* XXX how to test? *)
+   "run a command from the guest filesystem",
+   "\
+This call runs a command from the guest filesystem.  The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+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).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>.  If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places.  It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.");
+
+  ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [],
+   [], (* XXX how to test? *)
+   "run a command, returning lines",
+   "\
+This is the same as C<guestfs_command>, but splits the
+result into a list of lines.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -1144,7 +1195,9 @@ let check_functions () =
          failwithf "%s param/ret %s should not contain '-' or '_'"
            name n;
        if n = "value" then
-         failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n
+         failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n;
+       if n = "argv" || n = "args" then
+         failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
       in
 
       (match fst style with
@@ -1282,10 +1335,10 @@ let rec generate_actions_pod () =
        | RBool _ ->
           pr "This function returns a C truth value on success or -1 on error.\n\n"
        | RConstString _ ->
-          pr "This function returns a string or NULL on error.
+          pr "This function returns a string, or NULL on error.
 The string is owned by the guest handle and must I<not> be freed.\n\n"
        | RString _ ->
-          pr "This function returns a string or NULL on error.
+          pr "This function returns a string, or NULL on error.
 I<The caller must free the returned string after use>.\n\n"
        | RStringList _ ->
           pr "This function returns a NULL-terminated array of strings
@@ -2776,6 +2829,87 @@ and generate_fish_cmds () =
   pr "}\n";
   pr "\n"
 
+(* Readline completion for guestfish. *)
+and generate_fish_completion () =
+  generate_header CStyle GPLv2;
+
+  let all_functions =
+    List.filter (
+      fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
+    ) all_functions in
+
+  pr "\
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_LIBREADLINE
+#include <readline/readline.h>
+#endif
+
+#include \"fish.h\"
+
+#ifdef HAVE_LIBREADLINE
+
+static const char *commands[] = {
+";
+
+  (* Get the commands and sort them, including the aliases. *)
+  let commands =
+    List.map (
+      fun (name, _, _, flags, _, _, _) ->
+       let name2 = replace_char name '_' '-' in
+       let alias =
+         try find_map (function FishAlias n -> Some n | _ -> None) flags
+         with Not_found -> name in
+
+       if name <> alias then [name2; alias] else [name2]
+    ) all_functions in
+  let commands = List.flatten commands in
+  let commands = List.sort compare commands in
+
+  List.iter (pr "  \"%s\",\n") commands;
+
+  pr "  NULL
+};
+
+static char *
+generator (const char *text, int state)
+{
+  static int index, len;
+  const char *name;
+
+  if (!state) {
+    index = 0;
+    len = strlen (text);
+  }
+
+  while ((name = commands[index]) != NULL) {
+    index++;
+    if (strncasecmp (name, text, len) == 0)
+      return strdup (name);
+  }
+
+  return NULL;
+}
+
+#endif /* HAVE_LIBREADLINE */
+
+char **do_completion (const char *text, int start, int end)
+{
+  char **matches = NULL;
+
+#ifdef HAVE_LIBREADLINE
+  if (start == 0)
+    matches = rl_completion_matches (text, generator);
+#endif
+
+  return matches;
+}
+";
+
 (* Generate the POD documentation for guestfish. *)
 and generate_fish_actions_pod () =
   let all_functions_sorted =
@@ -3332,20 +3466,22 @@ DESTROY (g)
          | OptString _
          | Bool _
          | Int _ -> ()
-         | StringList n -> pr "        free (%s);\n" n
+         | StringList n -> pr "      free (%s);\n" n
        ) (snd style)
       in
 
       (* Code. *)
       (match fst style with
        | RErr ->
+          pr "PREINIT:\n";
+          pr "      int r;\n";
           pr " PPCODE:\n";
-          pr "      if (guestfs_%s " name;
+          pr "      r = guestfs_%s " name;
           generate_call_args ~handle:"g" style;
-          pr " == -1) {\n";
+          pr ";\n";
           do_cleanups ();
+          pr "      if (r == -1)\n";
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n"
        | RInt n
        | RBool n ->
           pr "PREINIT:\n";
@@ -3354,10 +3490,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == -1) {\n" n;
           do_cleanups ();
+          pr "      if (%s == -1)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      RETVAL = newSViv (%s);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
@@ -3368,10 +3503,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL) {\n" n;
           do_cleanups ();
+          pr "      if (%s == NULL)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr " OUTPUT:\n";
           pr "      RETVAL\n"
@@ -3382,10 +3516,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL) {\n" n;
           do_cleanups ();
+          pr "      if (%s == NULL)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      RETVAL = newSVpv (%s, 0);\n" n;
           pr "      free (%s);\n" n;
           pr " OUTPUT:\n";
@@ -3398,10 +3531,9 @@ DESTROY (g)
           pr "      %s = guestfs_%s " n name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (%s == NULL) {\n" n;
           do_cleanups ();
+          pr "      if (%s == NULL)\n" n;
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
           pr "      EXTEND (SP, n);\n";
           pr "      for (i = 0; i < n; ++i) {\n";
@@ -3416,28 +3548,25 @@ DESTROY (g)
           pr "      r = guestfs_%s " name;
           generate_call_args ~handle:"g" style;
           pr ";\n";
-          pr "      if (r == NULL) {\n";
           do_cleanups ();
+          pr "      if (r == NULL)\n";
           pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
-          pr "      }\n";
           pr "      EXTEND (SP, 2);\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
           pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
           pr "      guestfs_free_int_bool (r);\n";
        | RPVList n ->
-          generate_perl_lvm_code "pv" pv_cols name style n;
+          generate_perl_lvm_code "pv" pv_cols name style n do_cleanups;
        | RVGList n ->
-          generate_perl_lvm_code "vg" vg_cols name style n;
+          generate_perl_lvm_code "vg" vg_cols name style n do_cleanups;
        | RLVList n ->
-          generate_perl_lvm_code "lv" lv_cols name style n;
+          generate_perl_lvm_code "lv" lv_cols name style n do_cleanups;
       );
 
-      do_cleanups ();
-
       pr "\n"
   ) all_functions
 
-and generate_perl_lvm_code typ cols name style n =
+and generate_perl_lvm_code typ cols name style n do_cleanups =
   pr "PREINIT:\n";
   pr "      struct guestfs_lvm_%s_list *%s;\n" typ n;
   pr "      int i;\n";
@@ -3446,6 +3575,7 @@ and generate_perl_lvm_code typ cols name style n =
   pr "      %s = guestfs_%s " n name;
   generate_call_args ~handle:"g" style;
   pr ";\n";
+  do_cleanups ();
   pr "      if (%s == NULL)\n" n;
   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
   pr "      EXTEND (SP, %s->len);\n" n;
@@ -4023,6 +4153,10 @@ Run it from the top source directory using the command
   generate_fish_cmds ();
   close ();
 
+  let close = output_to "fish/completion.c" in
+  generate_fish_completion ();
+  close ();
+
   let close = output_to "guestfs-structs.pod" in
   generate_structs_pod ();
   close ();