tools: Specify format of disks (RHBZ#642934,CVE-2010-3851).
[libguestfs.git] / generator / generator_fish.ml
index ef675d3..fdb4eff 100644 (file)
@@ -43,6 +43,9 @@ let generate_fish_cmds () =
       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
     ) all_functions_sorted in
 
       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
     ) all_functions_sorted in
 
+  let all_functions_and_fish_commands_sorted =
+    List.sort action_compare (all_functions_sorted @ fish_commands) in
+
   pr "#include <config.h>\n";
   pr "\n";
   pr "#include <stdio.h>\n";
   pr "#include <config.h>\n";
   pr "\n";
   pr "#include <stdio.h>\n";
@@ -70,7 +73,7 @@ let generate_fish_cmds () =
       let name = replace_char name '_' '-' in
       pr "  printf (\"%%-20s %%s\\n\", \"%s\", _(\"%s\"));\n"
         name shortdesc
       let name = replace_char name '_' '-' in
       pr "  printf (\"%%-20s %%s\\n\", \"%s\", _(\"%s\"));\n"
         name shortdesc
-  ) all_functions_sorted;
+  ) all_functions_and_fish_commands_sorted;
   pr "  printf (\"    %%s\\n\",";
   pr "          _(\"Use -h <cmd> / help <cmd> to show detailed help for a command.\"));\n";
   pr "}\n";
   pr "  printf (\"    %%s\\n\",";
   pr "          _(\"Use -h <cmd> / help <cmd> to show detailed help for a command.\"));\n";
   pr "}\n";
@@ -79,23 +82,56 @@ let generate_fish_cmds () =
   (* display_command function, which implements guestfish -h cmd *)
   pr "int display_command (const char *cmd)\n";
   pr "{\n";
   (* display_command function, which implements guestfish -h cmd *)
   pr "int display_command (const char *cmd)\n";
   pr "{\n";
+
+  List.iter (
+    fun (name, _, _, flags, _, shortdesc, longdesc) ->
+      let name2 = replace_char name '_' '-' in
+      let aliases =
+        filter_map (function FishAlias n -> Some n | _ -> None) flags in
+      let describe_alias =
+        if aliases <> [] then
+          sprintf "\n\nYou can use %s as an alias for this command."
+            (String.concat " or " (List.map (fun s -> "'" ^ s ^ "'") aliases))
+        else "" in
+
+      pr "  if (";
+      pr "STRCASEEQ (cmd, \"%s\")" name;
+      if name <> name2 then
+        pr " || STRCASEEQ (cmd, \"%s\")" name2;
+      List.iter (
+        fun alias ->
+          pr " || STRCASEEQ (cmd, \"%s\")" alias
+      ) aliases;
+      pr ") {\n";
+      pr "    pod2text (\"%s\", _(\"%s\"), %S);\n"
+        name2 shortdesc
+        ("=head1 DESCRIPTION\n\n" ^
+         longdesc ^ describe_alias);
+      pr "    return 0;\n";
+      pr "  }\n";
+      pr "  else\n"
+  ) fish_commands;
+
   List.iter (
   List.iter (
-    fun (name, style, _, flags, _, shortdesc, longdesc) ->
+    fun (name, (_, args, optargs), _, flags, _, shortdesc, longdesc) ->
       let name2 = replace_char name '_' '-' in
       let name2 = replace_char name '_' '-' in
-      let alias =
-        try find_map (function FishAlias n -> Some n | _ -> None) flags
-        with Not_found -> name in
+      let aliases =
+        filter_map (function FishAlias n -> Some n | _ -> None) flags in
       let longdesc = replace_str longdesc "C<guestfs_" "C<" in
       let synopsis =
       let longdesc = replace_str longdesc "C<guestfs_" "C<" in
       let synopsis =
-        match snd style with
+        match args with
         | [] -> name2
         | args ->
             let args = List.filter (function Key _ -> false | _ -> true) args in
         | [] -> name2
         | args ->
             let args = List.filter (function Key _ -> false | _ -> true) args in
-            sprintf "%s %s"
-              name2 (String.concat " " (List.map name_of_argt args)) in
+            sprintf "%s%s%s"
+              name2
+              (String.concat ""
+                 (List.map (fun arg -> " " ^ name_of_argt arg) args))
+              (String.concat ""
+                 (List.map (fun arg -> sprintf " [%s:..]" (name_of_argt arg)) optargs)) in
 
       let warnings =
 
       let warnings =
-        if List.exists (function Key _ -> true | _ -> false) (snd style) then
+        if List.exists (function Key _ -> true | _ -> false) args then
           "\n\nThis command has one or more key or passphrase parameters.
 Guestfish will prompt for these separately."
         else "" in
           "\n\nThis command has one or more key or passphrase parameters.
 Guestfish will prompt for these separately."
         else "" in
@@ -123,16 +159,19 @@ Guestfish will prompt for these separately."
           | Some txt -> "\n\n" ^ txt in
 
       let describe_alias =
           | Some txt -> "\n\n" ^ txt in
 
       let describe_alias =
-        if name <> alias then
-          sprintf "\n\nYou can use '%s' as an alias for this command." alias
+        if aliases <> [] then
+          sprintf "\n\nYou can use %s as an alias for this command."
+            (String.concat " or " (List.map (fun s -> "'" ^ s ^ "'") aliases))
         else "" in
 
       pr "  if (";
       pr "STRCASEEQ (cmd, \"%s\")" name;
       if name <> name2 then
         pr " || STRCASEEQ (cmd, \"%s\")" name2;
         else "" in
 
       pr "  if (";
       pr "STRCASEEQ (cmd, \"%s\")" name;
       if name <> name2 then
         pr " || STRCASEEQ (cmd, \"%s\")" name2;
-      if name <> alias then
-        pr " || STRCASEEQ (cmd, \"%s\")" alias;
+      List.iter (
+        fun alias ->
+          pr " || STRCASEEQ (cmd, \"%s\")" alias
+      ) aliases;
       pr ") {\n";
       pr "    pod2text (\"%s\", _(\"%s\"), %S);\n"
         name2 shortdesc
       pr ") {\n";
       pr "    pod2text (\"%s\", _(\"%s\"), %S);\n"
         name2 shortdesc
@@ -143,6 +182,7 @@ Guestfish will prompt for these separately."
       pr "  }\n";
       pr "  else\n"
   ) all_functions;
       pr "  }\n";
       pr "  else\n"
   ) all_functions;
+
   pr "    return display_builtin_command (cmd);\n";
   pr "}\n";
   pr "\n";
   pr "    return display_builtin_command (cmd);\n";
   pr "}\n";
   pr "\n";
@@ -238,10 +278,10 @@ Guestfish will prompt for these separately."
 
   (* run_<action> actions *)
   List.iter (
 
   (* run_<action> actions *)
   List.iter (
-    fun (name, style, _, flags, _, _, _) ->
-      pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
+    fun (name, (ret, args, optargs as style), _, flags, _, _, _) ->
+      pr "static int run_%s (const char *cmd, size_t argc, char *argv[])\n" name;
       pr "{\n";
       pr "{\n";
-      (match fst style with
+      (match ret with
        | RErr
        | RInt _
        | RBool _ -> pr "  int r;\n"
        | RErr
        | RInt _
        | RBool _ -> pr "  int r;\n"
@@ -272,26 +312,45 @@ Guestfish will prompt for these separately."
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
         | Int64 n -> pr "  int64_t %s;\n" n
         | Bool n -> pr "  int %s;\n" n
         | Int n -> pr "  int %s;\n" n
         | Int64 n -> pr "  int64_t %s;\n" n
-      ) (snd style);
+      ) args;
+
+      if optargs <> [] then (
+        pr "  struct guestfs_%s_argv optargs_s = { .bitmask = 0 };\n" name;
+        pr "  struct guestfs_%s_argv *optargs = &optargs_s;\n" name
+      );
+
+      if args <> [] || optargs <> [] then
+        pr "  size_t i = 0;\n";
+
+      pr "\n";
 
       (* Check and convert parameters. *)
 
       (* Check and convert parameters. *)
-      let argc_expected =
+      let argc_minimum, argc_maximum =
         let args_no_keys =
         let args_no_keys =
-          List.filter (function Key _ -> false | _ -> true) (snd style) in
-        List.length args_no_keys in
-      pr "  if (argc != %d) {\n" argc_expected;
-      pr "    fprintf (stderr, _(\"%%s should have %%d parameter(s)\\n\"), cmd, %d);\n"
-        argc_expected;
+          List.filter (function Key _ -> false | _ -> true) args in
+        let argc_minimum = List.length args_no_keys in
+        let argc_maximum = argc_minimum + List.length optargs in
+        argc_minimum, argc_maximum in
+
+      if argc_minimum = argc_maximum then (
+        pr "  if (argc != %d) {\n" argc_minimum;
+        pr "    fprintf (stderr, _(\"%%s should have %%d parameter(s)\\n\"), cmd, %d);\n"
+          argc_minimum;
+      ) else (
+        pr "  if (argc < %d || argc > %d) {\n" argc_minimum argc_maximum;
+        pr "    fprintf (stderr, _(\"%%s should have %%d-%%d parameter(s)\\n\"), cmd, %d, %d);\n"
+          argc_minimum argc_maximum;
+      );
       pr "    fprintf (stderr, _(\"type 'help %%s' for help on %%s\\n\"), cmd, cmd);\n";
       pr "    return -1;\n";
       pr "  }\n";
 
       pr "    fprintf (stderr, _(\"type 'help %%s' for help on %%s\\n\"), cmd, cmd);\n";
       pr "    return -1;\n";
       pr "  }\n";
 
-      let parse_integer fn fntyp rtyp range name =
+      let parse_integer expr fn fntyp rtyp range name =
         pr "  {\n";
         pr "    strtol_error xerr;\n";
         pr "    %s r;\n" fntyp;
         pr "\n";
         pr "  {\n";
         pr "    strtol_error xerr;\n";
         pr "    %s r;\n" fntyp;
         pr "\n";
-        pr "    xerr = %s (argv[i++], NULL, 0, &r, xstrtol_suffixes);\n" fn;
+        pr "    xerr = %s (%s, NULL, 0, &r, xstrtol_suffixes);\n" fn expr;
         pr "    if (xerr != LONGINT_OK) {\n";
         pr "      fprintf (stderr,\n";
         pr "               _(\"%%s: %%s: invalid integer parameter (%%s returned %%d)\\n\"),\n";
         pr "    if (xerr != LONGINT_OK) {\n";
         pr "      fprintf (stderr,\n";
         pr "               _(\"%%s: %%s: invalid integer parameter (%%s returned %%d)\\n\"),\n";
@@ -313,9 +372,6 @@ Guestfish will prompt for these separately."
         pr "  }\n";
       in
 
         pr "  }\n";
       in
 
-      if snd style <> [] then
-        pr "  size_t i = 0;\n";
-
       List.iter (
         function
         | Device name
       List.iter (
         function
         | Device name
@@ -353,13 +409,67 @@ Guestfish will prompt for these separately."
               and comment =
                 "The Int type in the generator is a signed 31 bit int." in
               Some (min, max, comment) in
               and comment =
                 "The Int type in the generator is a signed 31 bit int." in
               Some (min, max, comment) in
-            parse_integer "xstrtoll" "long long" "int" range name
+            parse_integer "argv[i++]" "xstrtoll" "long long" "int" range name
         | Int64 name ->
         | Int64 name ->
-            parse_integer "xstrtoll" "long long" "int64_t" None name
-      ) (snd style);
+            parse_integer "argv[i++]" "xstrtoll" "long long" "int64_t" None name
+      ) args;
+
+      (* Optional arguments are prefixed with <argname>:<value> and
+       * may be missing, so we need to parse those until the end of
+       * the argument list.
+       *)
+      if optargs <> [] then (
+        let uc_name = String.uppercase name in
+        pr "\n";
+        pr "  for (; i < argc; ++i) {\n";
+        pr "    uint64_t this_mask;\n";
+        pr "    const char *this_arg;\n";
+        List.iter (
+          fun argt ->
+            let n = name_of_argt argt in
+            let uc_n = String.uppercase n in
+            let len = String.length n in
+            pr "    if (STRPREFIX (argv[i], \"%s:\")) {\n" n;
+            (match argt with
+             | Bool n ->
+                 pr "      optargs_s.%s = is_true (&argv[i][%d]) ? 1 : 0;\n"
+                   n (len+1);
+             | Int n ->
+                 let range =
+                   let min = "(-(2LL<<30))"
+                   and max = "((2LL<<30)-1)"
+                   and comment =
+                     "The Int type in the generator is a signed 31 bit int." in
+                   Some (min, max, comment) in
+                 let expr = sprintf "&argv[i][%d]" (len+1) in
+                 parse_integer expr "xstrtoll" "long long" "int" range name
+             | Int64 n ->
+                 let expr = sprintf "&argv[i][%d]" (len+1) in
+                 parse_integer expr "xstrtoll" "long long" "int64_t" None name
+             | String n ->
+                 pr "      optargs_s.%s = &argv[i][%d];\n" n (len+1);
+             | _ -> assert false
+            );
+            pr "      this_mask = GUESTFS_%s_%s_BITMASK;\n" uc_name uc_n;
+            pr "      this_arg = \"%s\";\n" n;
+            pr "    }\n";
+        ) optargs;
+
+        pr "    if (optargs_s.bitmask & this_mask) {\n";
+        pr "      fprintf (stderr, _(\"%%s: optional argument %%s given twice\\n\"),\n";
+        pr "               cmd, this_arg);\n";
+        pr "      return -1;\n";
+        pr "    }\n";
+        pr "    optargs_s.bitmask |= this_mask;\n";
+        pr "  }\n";
+        pr "\n";
+      );
 
       (* Call C API function. *)
 
       (* Call C API function. *)
-      pr "  r = guestfs_%s " name;
+      if optargs = [] then
+        pr "  r = guestfs_%s " name
+      else
+        pr "  r = guestfs_%s_argv " name;
       generate_c_call_args ~handle:"g" style;
       pr ";\n";
 
       generate_c_call_args ~handle:"g" style;
       pr ";\n";
 
@@ -376,7 +486,7 @@ Guestfish will prompt for these separately."
             pr "  free_file_in (%s);\n" name
         | StringList name | DeviceList name ->
             pr "  free_strings (%s);\n" name
             pr "  free_file_in (%s);\n" name
         | StringList name | DeviceList name ->
             pr "  free_strings (%s);\n" name
-      ) (snd style);
+      ) args;
 
       (* Any output flags? *)
       let fish_output =
 
       (* Any output flags? *)
       let fish_output =
@@ -390,7 +500,7 @@ Guestfish will prompt for these separately."
             failwithf "%s: more than one FishOutput flag is not allowed" name in
 
       (* Check return value for errors and display command results. *)
             failwithf "%s: more than one FishOutput flag is not allowed" name in
 
       (* Check return value for errors and display command results. *)
-      (match fst style with
+      (match ret with
        | RErr -> pr "  return r;\n"
        | RInt _ ->
            pr "  if (r == -1) return -1;\n";
        | RErr -> pr "  return r;\n"
        | RInt _ ->
            pr "  if (r == -1) return -1;\n";
@@ -463,24 +573,27 @@ Guestfish will prompt for these separately."
   ) all_functions;
 
   (* run_action function *)
   ) all_functions;
 
   (* run_action function *)
-  pr "int run_action (const char *cmd, int argc, char *argv[])\n";
+  pr "int run_action (const char *cmd, size_t argc, char *argv[])\n";
   pr "{\n";
   pr "{\n";
+
   List.iter (
     fun (name, _, _, flags, _, _, _) ->
       let name2 = replace_char name '_' '-' in
   List.iter (
     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
+      let aliases =
+        filter_map (function FishAlias n -> Some n | _ -> None) flags in
       pr "  if (";
       pr "STRCASEEQ (cmd, \"%s\")" name;
       if name <> name2 then
         pr " || STRCASEEQ (cmd, \"%s\")" name2;
       pr "  if (";
       pr "STRCASEEQ (cmd, \"%s\")" name;
       if name <> name2 then
         pr " || STRCASEEQ (cmd, \"%s\")" name2;
-      if name <> alias then
-        pr " || STRCASEEQ (cmd, \"%s\")" alias;
+      List.iter (
+        fun alias ->
+          pr " || STRCASEEQ (cmd, \"%s\")" alias;
+      ) aliases;
       pr ")\n";
       pr "    return run_%s (cmd, argc, argv);\n" name;
       pr "  else\n";
       pr ")\n";
       pr "    return run_%s (cmd, argc, argv);\n" name;
       pr "  else\n";
-  ) all_functions;
+  ) all_functions_and_fish_commands_sorted;
+
   pr "    {\n";
   pr "      fprintf (stderr, _(\"%%s: unknown command\\n\"), cmd);\n";
   pr "      if (command_num == 1)\n";
   pr "    {\n";
   pr "      fprintf (stderr, _(\"%%s: unknown command\\n\"), cmd);\n";
   pr "      if (command_num == 1)\n";
@@ -526,12 +639,10 @@ static const char *const commands[] = {
     List.map (
       fun (name, _, _, flags, _, _, _) ->
         let name2 = replace_char name '_' '-' in
     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 aliases =
+          filter_map (function FishAlias n -> Some n | _ -> None) flags in
+        name2 :: aliases
+    ) (all_functions @ fish_commands) in
   let commands = List.flatten commands in
 
   List.iter (pr "  \"%s\",\n") commands;
   let commands = List.flatten commands in
 
   List.iter (pr "  \"%s\",\n") commands;
@@ -600,7 +711,7 @@ and generate_fish_actions_pod () =
   let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
 
   List.iter (
   let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
 
   List.iter (
-    fun (name, style, _, flags, _, _, longdesc) ->
+    fun (name, (_, args, optargs), _, flags, _, _, longdesc) ->
       let longdesc =
         Str.global_substitute rex (
           fun s ->
       let longdesc =
         Str.global_substitute rex (
           fun s ->
@@ -611,15 +722,13 @@ and generate_fish_actions_pod () =
             "L</" ^ replace_char sub '_' '-' ^ ">"
         ) longdesc in
       let name = replace_char name '_' '-' in
             "L</" ^ replace_char sub '_' '-' ^ ">"
         ) longdesc in
       let name = replace_char name '_' '-' in
-      let alias =
-        try find_map (function FishAlias n -> Some n | _ -> None) flags
-        with Not_found -> name in
+      let aliases =
+        filter_map (function FishAlias n -> Some n | _ -> None) flags in
 
 
-      pr "=head2 %s" name;
-      if name <> alias then
-        pr " | %s" alias;
-      pr "\n";
-      pr "\n";
+      List.iter (
+        fun name ->
+          pr "=head2 %s\n\n" name
+      ) (name :: aliases);
       pr " %s" name;
       List.iter (
         function
       pr " %s" name;
       List.iter (
         function
@@ -633,19 +742,27 @@ and generate_fish_actions_pod () =
         | FileIn n | FileOut n -> pr " (%s|-)" n
         | BufferIn n -> pr " %s" n
         | Key _ -> () (* keys are entered at a prompt *)
         | FileIn n | FileOut n -> pr " (%s|-)" n
         | BufferIn n -> pr " %s" n
         | Key _ -> () (* keys are entered at a prompt *)
-      ) (snd style);
+      ) args;
+      List.iter (
+        function
+        | Bool n | Int n | Int64 n | String n -> pr " [%s:..]" n
+        | _ -> assert false
+      ) optargs;
       pr "\n";
       pr "\n";
       pr "%s\n\n" longdesc;
 
       if List.exists (function FileIn _ | FileOut _ -> true
       pr "\n";
       pr "\n";
       pr "%s\n\n" longdesc;
 
       if List.exists (function FileIn _ | FileOut _ -> true
-                      | _ -> false) (snd style) then
+                      | _ -> false) args then
         pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
 
         pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
 
-      if List.exists (function Key _ -> true | _ -> false) (snd style) then
+      if List.exists (function Key _ -> true | _ -> false) args then
         pr "This command has one or more key or passphrase parameters.
 Guestfish will prompt for these separately.\n\n";
 
         pr "This command has one or more key or passphrase parameters.
 Guestfish will prompt for these separately.\n\n";
 
+      if optargs <> [] then
+        pr "This command has one or more optional arguments.  See L</OPTIONAL ARGUMENTS>.\n\n";
+
       if List.mem ProtocolLimitWarning flags then
         pr "%s\n\n" protocol_limit_warning;
 
       if List.mem ProtocolLimitWarning flags then
         pr "%s\n\n" protocol_limit_warning;
 
@@ -657,6 +774,21 @@ Guestfish will prompt for these separately.\n\n";
       | Some txt -> pr "%s\n\n" txt
   ) all_functions_sorted
 
       | Some txt -> pr "%s\n\n" txt
   ) all_functions_sorted
 
+(* Generate documentation for guestfish-only commands. *)
+and generate_fish_commands_pod () =
+  List.iter (
+    fun (name, _, _, flags, _, _, longdesc) ->
+      let name = replace_char name '_' '-' in
+      let aliases =
+        filter_map (function FishAlias n -> Some n | _ -> None) flags in
+
+      List.iter (
+        fun name ->
+          pr "=head2 %s\n\n" name
+      ) (name :: aliases);
+      pr "%s\n\n" longdesc;
+  ) fish_commands
+
 and generate_fish_prep_options_h () =
   generate_header CStyle GPLv2plus;
 
 and generate_fish_prep_options_h () =
   generate_header CStyle GPLv2plus;