Implemented autosync, make it the default for guestfish.
[libguestfs.git] / src / generator.ml
index 29f7438..8fa5cbc 100755 (executable)
@@ -42,7 +42,7 @@ and argt =
 
 let functions = [
   ("mount", (Err, P2 (String "device", String "mountpoint")), 1,
-   "Mount a guest disk at a position in the filesystem",
+   "mount a guest disk at a position in the filesystem",
    "\
 Mount a guest disk at a position in the filesystem.  Block devices
 are named C</dev/sda>, C</dev/sdb> and so on, as they were added to
@@ -62,7 +62,7 @@ The filesystem options C<sync> and C<noatime> are set with this
 call, in order to improve reliability.");
 
   ("sync", (Err, P0), 2,
-   "Sync disks, writes are flushed through to the disk image",
+   "sync disks, writes are flushed through to the disk image",
    "\
 This syncs the disk, so that any writes are flushed through to the
 underlying disk image.
@@ -71,10 +71,10 @@ You should always call this if you have modified a disk image, before
 calling C<guestfs_close>.");
 
   ("touch", (Err, P1 (String "path")), 3,
-   "Update file timestamps or create a new file",
+   "update file timestamps or create a new file",
    "\
 Touch acts like the L<touch(1)> command.  It can be used to
-update the filesystems on a file, or, if the file does not exist,
+update the timestamps on a file, or, if the file does not exist,
 to create a new zero-length file.");
 ]
 
@@ -87,6 +87,18 @@ let iter_args f = function
   | P1 arg1 -> f arg1
   | P2 (arg1, arg2) -> f arg1; f arg2
 
+let iteri_args f = function
+  | P0 -> ()
+  | P1 arg1 -> f 0 arg1
+  | P2 (arg1, arg2) -> f 0 arg1; f 1 arg2
+
+let map_args f = function
+  | P0 -> []
+  | P1 arg1 -> [f arg1]
+  | P2 (arg1, arg2) -> [f arg1; f arg2]
+
+let nr_args = function | P0 -> 0 | P1 _ -> 1 | P2 _ -> 2
+
 type comment_style = CStyle | HashStyle | OCamlStyle
 type license = GPLv2 | LGPLv2
 
@@ -318,7 +330,6 @@ and generate_client_actions () =
           pr "  serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
             (String.uppercase shortname)
        | (_, args) ->
-          pr "\n";
           iter_args (
             function
             | String name -> pr "  args.%s = (char *) %s;\n" name name
@@ -447,7 +458,116 @@ and generate_daemon_actions () =
   pr "    default:\n";
   pr "      reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
   pr "  }\n";
+  pr "}\n"
+
+(* Generate a lot of different functions for guestfish. *)
+and generate_fish_cmds () =
+  generate_header CStyle GPLv2;
+
+  pr "#include <stdio.h>\n";
+  pr "#include <stdlib.h>\n";
+  pr "#include <string.h>\n";
+  pr "\n";
+  pr "#include \"fish.h\"\n";
+  pr "\n";
+
+  (* list_commands function, which implements guestfish -h *)
+  pr "void list_commands (void)\n";
+  pr "{\n";
+  pr "  printf (\"    %%-16s     %%s\\n\", \"Command\", \"Description\");\n";
+  pr "  list_builtin_commands ();\n";
+  List.iter (
+    fun (name, _, _, shortdesc, _) ->
+      pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
+       name shortdesc
+  ) functions;
+  pr "  printf (\"    Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
+  pr "}\n";
+  pr "\n";
+
+  (* display_command function, which implements guestfish -h cmd *)
+  pr "void display_command (const char *cmd)\n";
+  pr "{\n";
+  List.iter (
+    fun (name, style, _, shortdesc, longdesc) ->
+      let synopsis =
+       match style with
+       | (Err, P0) -> name
+       | (Err, args) ->
+           sprintf "%s <%s>"
+             name (
+               String.concat "> <" (
+                 map_args (function
+                           | String n -> n) args
+               )
+             ) in
+
+      pr "  if (strcasecmp (cmd, \"%s\") == 0)\n" name;
+      pr "    pod2text (\"%s - %s\", %S);\n"
+       name shortdesc
+       (" " ^ synopsis ^ "\n\n" ^ longdesc);
+      pr "  else\n"
+  ) functions;
+  pr "    display_builtin_command (cmd);\n";
   pr "}\n";
+  pr "\n";
+
+  (* run_<action> actions *)
+  List.iter (
+    fun (name, style, _, _, _) ->
+      pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
+      pr "{\n";
+      (match style with
+       | (Err, _) -> pr "  int r;\n"
+      );
+      iter_args (
+       function
+       | String name -> pr "  const char *%s;\n" name
+      ) (snd style);
+
+      (* Check and convert parameters. *)
+      let argc_expected = nr_args (snd style) in
+      pr "  if (argc != %d) {\n" argc_expected;
+      pr "    fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
+       argc_expected;
+      pr "    fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
+      pr "    return -1;\n";
+      pr "  }\n";
+      iteri_args (
+       fun i ->
+         function
+         | String name -> pr "  %s = argv[%d];\n" name i
+      ) (snd style);
+
+      (* Call C API function. *)
+      pr "  r = guestfs_%s " name;
+      generate_call_args ~handle:"g" style;
+      pr ";\n";
+
+      (* Check return value for errors. *)
+      (match style with
+       | (Err, _) -> pr "  return r;\n"
+      );
+      pr "}\n";
+      pr "\n"
+  ) functions;
+
+  (* run_action function *)
+  pr "int run_action (const char *cmd, int argc, char *argv[])\n";
+  pr "{\n";
+  List.iter (
+    fun (name, _, _, _, _) ->
+      pr "  if (strcasecmp (cmd, \"%s\") == 0)\n" name;
+      pr "    return run_%s (cmd, argc, argv);\n" name;
+      pr "  else\n";
+  ) functions;
+  pr "    {\n";
+  pr "      fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
+  pr "      return -1;\n";
+  pr "    }\n";
+  pr "  return 0;\n";
+  pr "}\n";
+  pr "\n"
 
 (* Generate a C function prototype. *)
 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
@@ -528,6 +648,10 @@ let () =
   generate_daemon_actions ();
   close ();
 
+  let close = output_to "fish/cmds.c" in
+  generate_fish_cmds ();
+  close ();
+
   let close = output_to "guestfs-actions.pod" in
   generate_pod ();
   close ()