X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=generator%2Fgenerator_fish.ml;h=fdb4eff39057c830884f40bc6403d027307f8e1a;hp=ef675d35202fd9b9d4a5cfcadd94aad21dbcb03a;hb=641ccab6c3b17f1c94676eab99e8baa9cddf5a0b;hpb=585fceb3350c17a3d6ed7f2daffd5e2b3e5fc3e1 diff --git a/generator/generator_fish.ml b/generator/generator_fish.ml index ef675d3..fdb4eff 100644 --- a/generator/generator_fish.ml +++ b/generator/generator_fish.ml @@ -43,6 +43,9 @@ let generate_fish_cmds () = 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 \n"; pr "\n"; pr "#include \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 - ) all_functions_sorted; + ) all_functions_and_fish_commands_sorted; pr " printf (\" %%s\\n\","; pr " _(\"Use -h / help 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"; + + 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 ( - fun (name, style, _, flags, _, shortdesc, longdesc) -> + fun (name, (_, args, optargs), _, flags, _, shortdesc, longdesc) -> 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 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 = - 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 @@ -123,16 +159,19 @@ Guestfish will prompt for these separately." | 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; - 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 @@ -143,6 +182,7 @@ Guestfish will prompt for these separately." pr " }\n"; pr " else\n" ) all_functions; + pr " return display_builtin_command (cmd);\n"; pr "}\n"; pr "\n"; @@ -238,10 +278,10 @@ Guestfish will prompt for these separately." (* run_ 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"; - (match fst style with + (match ret with | 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 - ) (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. *) - let argc_expected = + let argc_minimum, argc_maximum = 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"; - 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 " 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"; @@ -313,9 +372,6 @@ Guestfish will prompt for these separately." pr " }\n"; in - if snd style <> [] then - pr " size_t i = 0;\n"; - 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 - parse_integer "xstrtoll" "long long" "int" range name + parse_integer "argv[i++]" "xstrtoll" "long long" "int" range 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 : 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. *) - 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"; @@ -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 - ) (snd style); + ) args; (* 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. *) - (match fst style with + (match ret with | 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 *) - 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"; + 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; - 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"; - ) 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"; @@ -526,12 +639,10 @@ static const char *const 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 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; @@ -600,7 +711,7 @@ and generate_fish_actions_pod () = let rex = Str.regexp "C]+\\)>" in List.iter ( - fun (name, style, _, flags, _, _, longdesc) -> + fun (name, (_, args, optargs), _, flags, _, _, longdesc) -> let longdesc = Str.global_substitute rex ( fun s -> @@ -611,15 +722,13 @@ and generate_fish_actions_pod () = "L" ) 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 @@ -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 *) - ) (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 - | _ -> false) (snd style) then + | _ -> false) args then 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"; + if optargs <> [] then + pr "This command has one or more optional arguments. See L.\n\n"; + 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 +(* 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;