summaryrefslogtreecommitdiffstats
path: root/generator
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2010-09-18 09:38:05 +0100
committerRichard W.M. Jones <rjones@redhat.com>2010-09-18 09:38:05 +0100
commit0003ea2c3dbaa7e22f4f616539136821d80694b8 (patch)
tree1a5bcededb436fb9f1a0e8e046599fdd0d5ae7f1 /generator
parent585fceb3350c17a3d6ed7f2daffd5e2b3e5fc3e1 (diff)
downloadlibguestfs-0003ea2c3dbaa7e22f4f616539136821d80694b8.tar.gz
libguestfs-0003ea2c3dbaa7e22f4f616539136821d80694b8.tar.xz
libguestfs-0003ea2c3dbaa7e22f4f616539136821d80694b8.zip
generator: Generate guestfish-only commands.
The guestfish-only commands such as 'alloc' and 'edit' are now generated from one place in the generator instead of being spread around ad-hoc in the C code.
Diffstat (limited to 'generator')
-rw-r--r--generator/generator_actions.ml159
-rw-r--r--generator/generator_actions.mli3
-rw-r--r--generator/generator_checks.ml6
-rw-r--r--generator/generator_fish.ml106
-rw-r--r--generator/generator_main.ml1
-rw-r--r--generator/generator_utils.ml2
-rw-r--r--generator/generator_utils.mli3
7 files changed, 246 insertions, 34 deletions
diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index 2e015070..d01871f2 100644
--- a/generator/generator_actions.ml
+++ b/generator/generator_actions.ml
@@ -5124,9 +5124,7 @@ let all_functions = non_daemon_functions @ daemon_functions
(* In some places we want the functions to be displayed sorted
* alphabetically, so this is useful:
*)
-let all_functions_sorted =
- List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) ->
- compare n1 n2) all_functions
+let all_functions_sorted = List.sort action_compare all_functions
(* This is used to generate the src/MAX_PROC_NR file which
* contains the maximum procedure number, a surrogate for the
@@ -5137,3 +5135,158 @@ let max_proc_nr =
fun (_, _, proc_nr, _, _, _, _) -> proc_nr
) daemon_functions in
List.fold_left max 0 proc_nrs
+
+(* Non-API meta-commands available only in guestfish.
+ *
+ * Note (1): args/return value, proc_nr and tests fields are all
+ * meaningless. The only fields which are actually used are the
+ * shortname, FishAlias flags, shortdesc and longdesc.
+ *
+ * Note (2): to refer to other commands, use L</shortname>.
+ *
+ * Note (3): keep this list sorted by shortname.
+ *)
+let fish_commands = [
+ ("alloc", (RErr,[]), -1, [FishAlias "allocate"], [],
+ "allocate and add a disk file",
+ " alloc filename size
+
+This creates an empty (zeroed) file of the given size, and then adds
+so it can be further examined.
+
+For more advanced image creation, see L<qemu-img(1)> utility.
+
+Size can be specified using standard suffixes, eg. C<1M>.
+
+To create a sparse file, use L</sparse> instead. To create a
+prepared disk image, see L</PREPARED DISK IMAGES>.");
+
+ ("copy_in", (RErr,[]), -1, [], [],
+ "copy local files or directories into an image",
+ " copy-in local [local ...] /remotedir
+
+C<copy-in> copies local files or directories recursively into the disk
+image, placing them in the directory called C</remotedir> (which must
+exist). This guestfish meta-command turns into a sequence of
+L</tar-in> and other commands as necessary.
+
+Multiple local files and directories can be specified, but the last
+parameter must always be a remote directory. Wildcards cannot be
+used.");
+
+ ("copy_out", (RErr,[]), -1, [], [],
+ "copy remote files or directories out of an image",
+ " copy-out remote [remote ...] localdir
+
+C<copy-out> copies remote files or directories recursively out of the
+disk image, placing them on the host disk in a local directory called
+C<localdir> (which must exist). This guestfish meta-command turns
+into a sequence of L</download>, L</tar-out> and other commands as
+necessary.
+
+Multiple remote files and directories can be specified, but the last
+parameter must always be a local directory. To download to the
+current directory, use C<.> as in:
+
+ copy-out /home .
+
+Wildcards cannot be used in the ordinary command, but you can use
+them with the help of L</glob> like this:
+
+ glob copy-out /home/* .");
+
+ ("echo", (RErr,[]), -1, [], [],
+ "display a line of text",
+ " echo [params ...]
+
+This echos the parameters to the terminal.");
+
+ ("edit", (RErr,[]), -1, [FishAlias "vi"; FishAlias "emacs"], [],
+ "edit a file",
+ " edit filename
+
+This is used to edit a file. It downloads the file, edits it
+locally using your editor, then uploads the result.
+
+The editor is C<$EDITOR>. However if you use the alternate
+commands C<vi> or C<emacs> you will get those corresponding
+editors.");
+
+ ("glob", (RErr,[]), -1, [], [],
+ "expand wildcards in command",
+ " glob command args...
+
+Expand wildcards in any paths in the args list, and run C<command>
+repeatedly on each matching path.
+
+See L</WILDCARDS AND GLOBBING>.");
+
+ ("lcd", (RErr,[]), -1, [], [],
+ "change working directory",
+ " lcd directory
+
+Change the local directory, ie. the current directory of guestfish
+itself.
+
+Note that C<!cd> won't do what you might expect.");
+
+ ("man", (RErr,[]), -1, [FishAlias "manual"], [],
+ "open the manual",
+ " man
+
+Opens the manual page for guestfish.");
+
+ ("more", (RErr,[]), -1, [FishAlias "less"], [],
+ "view a file",
+ " more filename
+
+ less filename
+
+This is used to view a file.
+
+The default viewer is C<$PAGER>. However if you use the alternate
+command C<less> you will get the C<less> command specifically.");
+
+ ("reopen", (RErr,[]), -1, [], [],
+ "close and reopen libguestfs handle",
+ " reopen
+
+Close and reopen the libguestfs handle. It is not necessary to use
+this normally, because the handle is closed properly when guestfish
+exits. However this is occasionally useful for testing.");
+
+ ("sparse", (RErr,[]), -1, [], [],
+ "create a sparse disk image and add",
+ " sparse filename size
+
+This creates an empty sparse file of the given size, and then adds
+so it can be further examined.
+
+In all respects it works the same as the L</alloc> command, except that
+the image file is allocated sparsely, which means that disk blocks are
+not assigned to the file until they are needed. Sparse disk files
+only use space when written to, but they are slower and there is a
+danger you could run out of real disk space during a write operation.
+
+For more advanced image creation, see L<qemu-img(1)> utility.
+
+Size can be specified using standard suffixes, eg. C<1M>.");
+
+ ("supported", (RErr,[]), -1, [], [],
+ "list supported groups of commands",
+ " supported
+
+This command returns a list of the optional groups
+known to the daemon, and indicates which ones are
+supported by this build of the libguestfs appliance.
+
+See also L<guestfs(3)/AVAILABILITY>.");
+
+ ("time", (RErr,[]), -1, [], [],
+ "print elapsed time taken to run a command",
+ " time command args...
+
+Run the command as usual, but print the elapsed time afterwards. This
+can be useful for benchmarking operations.");
+
+]
diff --git a/generator/generator_actions.mli b/generator/generator_actions.mli
index 0ce6274f..711bf4fa 100644
--- a/generator/generator_actions.mli
+++ b/generator/generator_actions.mli
@@ -38,3 +38,6 @@ val test_functions : Generator_types.action list
val max_proc_nr : int
(** The largest procedure number used (also saved in [src/MAX_PROC_NR] and
used as the minor version number of the shared library). *)
+
+val fish_commands : Generator_types.action list
+(** Non-API meta-commands available only in guestfish. *)
diff --git a/generator/generator_checks.ml b/generator/generator_checks.ml
index f30a2868..98649e26 100644
--- a/generator/generator_checks.ml
+++ b/generator/generator_checks.ml
@@ -49,7 +49,7 @@ let () =
if String.contains name '-' then
failwithf "function name %s should not contain '-', use '_' instead."
name
- ) all_functions;
+ ) (all_functions @ fish_commands);
(* Check function parameter/return names. *)
List.iter (
@@ -120,14 +120,14 @@ let () =
let c = shortdesc.[String.length shortdesc-1] in
if c = '\n' || c = '.' then
failwithf "short description of %s should not end with . or \\n." name
- ) all_functions;
+ ) (all_functions @ fish_commands);
(* Check long descriptions. *)
List.iter (
fun (name, _, _, _, _, _, longdesc) ->
if longdesc.[String.length longdesc-1] = '\n' then
failwithf "long description of %s should not end with \\n." name
- ) all_functions;
+ ) (all_functions @ fish_commands);
(* Check proc_nrs. *)
List.iter (
diff --git a/generator/generator_fish.ml b/generator/generator_fish.ml
index ef675d35..930ef151 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 <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
- ) 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";
@@ -79,12 +82,41 @@ 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, style, _, 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) ->
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 =
match snd style with
@@ -123,16 +155,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 +178,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";
@@ -465,22 +501,25 @@ Guestfish will prompt for these separately."
(* run_action function *)
pr "int run_action (const char *cmd, int 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 +565,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;
@@ -611,15 +648,13 @@ and generate_fish_actions_pod () =
"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
@@ -657,6 +692,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, style, _, 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;
diff --git a/generator/generator_main.ml b/generator/generator_main.ml
index cdf7773f..401ae605 100644
--- a/generator/generator_main.ml
+++ b/generator/generator_main.ml
@@ -90,6 +90,7 @@ Run it from the top source directory using the command
output_to "capitests/tests.c" generate_tests;
output_to "fish/cmds.c" generate_fish_cmds;
output_to "fish/completion.c" generate_fish_completion;
+ output_to "fish/guestfish-commands.pod" generate_fish_commands_pod;
output_to "fish/guestfish-actions.pod" generate_fish_actions_pod;
output_to "fish/prepopts.c" generate_fish_prep_options_c;
output_to "fish/prepopts.h" generate_fish_prep_options_h;
diff --git a/generator/generator_utils.ml b/generator/generator_utils.ml
index 2bdcc0d8..b7401db0 100644
--- a/generator/generator_utils.ml
+++ b/generator/generator_utils.ml
@@ -303,3 +303,5 @@ let pod2text ~width name longdesc =
pod2text_memo_updated ();
lines
+(* Compare two actions (for sorting). *)
+let action_compare (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) = compare n1 n2
diff --git a/generator/generator_utils.mli b/generator/generator_utils.mli
index 7bc0dde1..f43a276d 100644
--- a/generator/generator_utils.mli
+++ b/generator/generator_utils.mli
@@ -96,3 +96,6 @@ val pod2text : width:int -> string -> string -> string list
plain ASCII lines of text. This is the slowest part of
autogeneration, so the results are memoized into a temporary
file. *)
+
+val action_compare : Generator_types.action -> Generator_types.action -> int
+(** Compare the names of two actions, for sorting. *)