2 * Copyright (C) 2009-2010 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 (* Please read generator/README first. *)
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
30 open Generator_prepopts
33 (* Generate a lot of different functions for guestfish. *)
34 let generate_fish_cmds () =
35 generate_header CStyle GPLv2plus;
39 fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
41 let all_functions_sorted =
43 fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
44 ) all_functions_sorted in
46 let all_functions_and_fish_commands_sorted =
47 List.sort action_compare (all_functions_sorted @ fish_commands) in
49 pr "#include <config.h>\n";
51 pr "#include <stdio.h>\n";
52 pr "#include <stdlib.h>\n";
53 pr "#include <string.h>\n";
54 pr "#include <inttypes.h>\n";
56 pr "#include <guestfs.h>\n";
57 pr "#include \"c-ctype.h\"\n";
58 pr "#include \"full-write.h\"\n";
59 pr "#include \"xstrtol.h\"\n";
60 pr "#include \"fish.h\"\n";
62 pr "/* Valid suffixes allowed for numbers. See Gnulib xstrtol function. */\n";
63 pr "static const char *xstrtol_suffixes = \"0kKMGTPEZY\";\n";
66 (* list_commands function, which implements guestfish -h *)
67 pr "void list_commands (void)\n";
69 pr " printf (\" %%-16s %%s\\n\", _(\"Command\"), _(\"Description\"));\n";
70 pr " list_builtin_commands ();\n";
72 fun (name, _, _, flags, _, shortdesc, _) ->
73 let name = replace_char name '_' '-' in
74 pr " printf (\"%%-20s %%s\\n\", \"%s\", _(\"%s\"));\n"
76 ) all_functions_and_fish_commands_sorted;
77 pr " printf (\" %%s\\n\",";
78 pr " _(\"Use -h <cmd> / help <cmd> to show detailed help for a command.\"));\n";
82 (* display_command function, which implements guestfish -h cmd *)
83 pr "int display_command (const char *cmd)\n";
87 fun (name, _, _, flags, _, shortdesc, longdesc) ->
88 let name2 = replace_char name '_' '-' in
90 filter_map (function FishAlias n -> Some n | _ -> None) flags in
93 sprintf "\n\nYou can use %s as an alias for this command."
94 (String.concat " or " (List.map (fun s -> "'" ^ s ^ "'") aliases))
98 pr "STRCASEEQ (cmd, \"%s\")" name;
100 pr " || STRCASEEQ (cmd, \"%s\")" name2;
103 pr " || STRCASEEQ (cmd, \"%s\")" alias
106 pr " pod2text (\"%s\", _(\"%s\"), %S);\n"
108 ("=head1 DESCRIPTION\n\n" ^
109 longdesc ^ describe_alias);
116 fun (name, (_, args, optargs), _, flags, _, shortdesc, longdesc) ->
117 let name2 = replace_char name '_' '-' in
119 filter_map (function FishAlias n -> Some n | _ -> None) flags in
120 let longdesc = replace_str longdesc "C<guestfs_" "C<" in
125 let args = List.filter (function Key _ -> false | _ -> true) args in
129 (List.map (fun arg -> " " ^ name_of_argt arg) args))
131 (List.map (fun arg -> sprintf " [%s:..]" (name_of_argt arg)) optargs)) in
134 if List.exists (function Key _ -> true | _ -> false) args then
135 "\n\nThis command has one or more key or passphrase parameters.
136 Guestfish will prompt for these separately."
141 if List.mem ProtocolLimitWarning flags then
142 ("\n\n" ^ protocol_limit_warning)
145 (* For DangerWillRobinson commands, we should probably have
146 * guestfish prompt before allowing you to use them (especially
147 * in interactive mode). XXX
151 if List.mem DangerWillRobinson flags then
152 ("\n\n" ^ danger_will_robinson)
157 match deprecation_notice flags with
159 | Some txt -> "\n\n" ^ txt in
162 if aliases <> [] then
163 sprintf "\n\nYou can use %s as an alias for this command."
164 (String.concat " or " (List.map (fun s -> "'" ^ s ^ "'") aliases))
168 pr "STRCASEEQ (cmd, \"%s\")" name;
169 if name <> name2 then
170 pr " || STRCASEEQ (cmd, \"%s\")" name2;
173 pr " || STRCASEEQ (cmd, \"%s\")" alias
176 pr " pod2text (\"%s\", _(\"%s\"), %S);\n"
178 ("=head1 SYNOPSIS\n\n " ^ synopsis ^ "\n\n" ^
179 "=head1 DESCRIPTION\n\n" ^
180 longdesc ^ warnings ^ describe_alias);
186 pr " return display_builtin_command (cmd);\n";
190 let emit_print_list_function typ =
191 pr "static void print_%s_list (struct guestfs_%s_list *%ss)\n"
194 pr " unsigned int i;\n";
196 pr " for (i = 0; i < %ss->len; ++i) {\n" typ;
197 pr " printf (\"[%%d] = {\\n\", i);\n";
198 pr " print_%s_indent (&%ss->val[i], \" \");\n" typ typ;
199 pr " printf (\"}\\n\");\n";
205 (* print_* functions *)
209 List.exists (function (_, (FUUID|FBuffer)) -> true | _ -> false) cols in
211 pr "static void print_%s_indent (struct guestfs_%s *%s, const char *indent)\n" typ typ typ;
214 pr " unsigned int i;\n";
220 pr " printf (\"%%s%s: %%s\\n\", indent, %s->%s);\n" name typ name
222 pr " printf (\"%%s%s: \", indent);\n" name;
223 pr " for (i = 0; i < 32; ++i)\n";
224 pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
225 pr " printf (\"\\n\");\n"
227 pr " printf (\"%%s%s: \", indent);\n" name;
228 pr " for (i = 0; i < %s->%s_len; ++i)\n" typ name;
229 pr " if (c_isprint (%s->%s[i]))\n" typ name;
230 pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
232 pr " printf (\"\\\\x%%02x\", %s->%s[i]);\n" typ name;
233 pr " printf (\"\\n\");\n"
234 | name, (FUInt64|FBytes) ->
235 pr " printf (\"%%s%s: %%\" PRIu64 \"\\n\", indent, %s->%s);\n"
238 pr " printf (\"%%s%s: %%\" PRIi64 \"\\n\", indent, %s->%s);\n"
241 pr " printf (\"%%s%s: %%\" PRIu32 \"\\n\", indent, %s->%s);\n"
244 pr " printf (\"%%s%s: %%\" PRIi32 \"\\n\", indent, %s->%s);\n"
247 pr " printf (\"%%s%s: %%c\\n\", indent, %s->%s);\n"
249 | name, FOptPercent ->
250 pr " if (%s->%s >= 0) printf (\"%%s%s: %%g %%%%\\n\", indent, %s->%s);\n"
251 typ name name typ name;
252 pr " else printf (\"%%s%s: \\n\", indent);\n" name
258 (* Emit a print_TYPE_list function definition only if that function is used. *)
261 | typ, (RStructListOnly | RStructAndList) ->
262 (* generate the function for typ *)
263 emit_print_list_function typ
264 | typ, _ -> () (* empty *)
265 ) (rstructs_used_by all_functions);
267 (* Emit a print_TYPE function definition only if that function is used. *)
270 | typ, (RStructOnly | RStructAndList) ->
271 pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
273 pr " print_%s_indent (%s, \"\");\n" typ typ;
276 | typ, _ -> () (* empty *)
277 ) (rstructs_used_by all_functions);
279 (* run_<action> actions *)
281 fun (name, (ret, args, optargs as style), _, flags, _, _, _) ->
282 pr "static int run_%s (const char *cmd, size_t argc, char *argv[])\n" name;
287 | RBool _ -> pr " int r;\n"
288 | RInt64 _ -> pr " int64_t r;\n"
289 | RConstString _ | RConstOptString _ -> pr " const char *r;\n"
290 | RString _ -> pr " char *r;\n"
291 | RStringList _ | RHashtable _ -> pr " char **r;\n"
292 | RStruct (_, typ) -> pr " struct guestfs_%s *r;\n" typ
293 | RStructList (_, typ) -> pr " struct guestfs_%s_list *r;\n" typ
296 pr " size_t size;\n";
302 | OptString n -> pr " const char *%s;\n" n
307 | Key n -> pr " char *%s;\n" n
309 pr " const char *%s;\n" n;
310 pr " size_t %s_size;\n" n
311 | StringList n | DeviceList n -> pr " char **%s;\n" n
312 | Bool n -> pr " int %s;\n" n
313 | Int n -> pr " int %s;\n" n
314 | Int64 n -> pr " int64_t %s;\n" n
317 if optargs <> [] then (
318 pr " struct guestfs_%s_argv optargs_s = { .bitmask = 0 };\n" name;
319 pr " struct guestfs_%s_argv *optargs = &optargs_s;\n" name
322 if args <> [] || optargs <> [] then
323 pr " size_t i = 0;\n";
327 (* Check and convert parameters. *)
328 let argc_minimum, argc_maximum =
330 List.filter (function Key _ -> false | _ -> true) args in
331 let argc_minimum = List.length args_no_keys in
332 let argc_maximum = argc_minimum + List.length optargs in
333 argc_minimum, argc_maximum in
335 if argc_minimum = argc_maximum then (
336 pr " if (argc != %d) {\n" argc_minimum;
337 pr " fprintf (stderr, _(\"%%s should have %%d parameter(s)\\n\"), cmd, %d);\n"
340 pr " if (argc < %d || argc > %d) {\n" argc_minimum argc_maximum;
341 pr " fprintf (stderr, _(\"%%s should have %%d-%%d parameter(s)\\n\"), cmd, %d, %d);\n"
342 argc_minimum argc_maximum;
344 pr " fprintf (stderr, _(\"type 'help %%s' for help on %%s\\n\"), cmd, cmd);\n";
348 let parse_integer expr fn fntyp rtyp range name =
350 pr " strtol_error xerr;\n";
353 pr " xerr = %s (%s, NULL, 0, &r, xstrtol_suffixes);\n" fn expr;
354 pr " if (xerr != LONGINT_OK) {\n";
355 pr " fprintf (stderr,\n";
356 pr " _(\"%%s: %%s: invalid integer parameter (%%s returned %%d)\\n\"),\n";
357 pr " cmd, \"%s\", \"%s\", xerr);\n" name fn;
362 | Some (min, max, comment) ->
363 pr " /* %s */\n" comment;
364 pr " if (r < %s || r > %s) {\n" min max;
365 pr " fprintf (stderr, _(\"%%s: %%s: integer out of range\\n\"), cmd, \"%s\");\n"
369 pr " /* The check above should ensure this assignment does not overflow. */\n";
371 pr " %s = r;\n" name;
379 pr " %s = argv[i++];\n" name
381 | Dev_or_Path name ->
382 pr " %s = resolve_win_path (argv[i++]);\n" name;
383 pr " if (%s == NULL) return -1;\n" name
385 pr " %s = STRNEQ (argv[i], \"\") ? argv[i] : NULL;\n" name;
388 pr " %s = argv[i];\n" name;
389 pr " %s_size = strlen (argv[i]);\n" name;
392 pr " %s = file_in (argv[i++]);\n" name;
393 pr " if (%s == NULL) return -1;\n" name
395 pr " %s = file_out (argv[i++]);\n" name;
396 pr " if (%s == NULL) return -1;\n" name
397 | StringList name | DeviceList name ->
398 pr " %s = parse_string_list (argv[i++]);\n" name;
399 pr " if (%s == NULL) return -1;\n" name
401 pr " %s = read_key (\"%s\");\n" name name;
402 pr " if (%s == NULL) return -1;\n" name
404 pr " %s = is_true (argv[i++]) ? 1 : 0;\n" name
407 let min = "(-(2LL<<30))"
408 and max = "((2LL<<30)-1)"
410 "The Int type in the generator is a signed 31 bit int." in
411 Some (min, max, comment) in
412 parse_integer "argv[i++]" "xstrtoll" "long long" "int" range name
414 parse_integer "argv[i++]" "xstrtoll" "long long" "int64_t" None name
417 (* Optional arguments are prefixed with <argname>:<value> and
418 * may be missing, so we need to parse those until the end of
421 if optargs <> [] then (
422 let uc_name = String.uppercase name in
424 pr " for (; i < argc; ++i) {\n";
425 pr " uint64_t this_mask;\n";
426 pr " const char *this_arg;\n";
429 let n = name_of_argt argt in
430 let uc_n = String.uppercase n in
431 let len = String.length n in
432 pr " if (STRPREFIX (argv[i], \"%s:\")) {\n" n;
435 pr " optargs_s.%s = is_true (&argv[i][%d]) ? 1 : 0;\n"
439 let min = "(-(2LL<<30))"
440 and max = "((2LL<<30)-1)"
442 "The Int type in the generator is a signed 31 bit int." in
443 Some (min, max, comment) in
444 let expr = sprintf "&argv[i][%d]" (len+1) in
445 parse_integer expr "xstrtoll" "long long" "int" range name
447 let expr = sprintf "&argv[i][%d]" (len+1) in
448 parse_integer expr "xstrtoll" "long long" "int64_t" None name
450 pr " optargs_s.%s = &argv[i][%d];\n" n (len+1);
453 pr " this_mask = GUESTFS_%s_%s_BITMASK;\n" uc_name uc_n;
454 pr " this_arg = \"%s\";\n" n;
458 pr " if (optargs_s.bitmask & this_mask) {\n";
459 pr " fprintf (stderr, _(\"%%s: optional argument %%s given twice\\n\"),\n";
460 pr " cmd, this_arg);\n";
463 pr " optargs_s.bitmask |= this_mask;\n";
468 (* Call C API function. *)
470 pr " r = guestfs_%s " name
472 pr " r = guestfs_%s_argv " name;
473 generate_c_call_args ~handle:"g" style;
478 | Device _ | String _
479 | OptString _ | Bool _
482 | Pathname name | Dev_or_Path name | FileOut name
484 pr " free (%s);\n" name
486 pr " free_file_in (%s);\n" name
487 | StringList name | DeviceList name ->
488 pr " free_strings (%s);\n" name
491 (* Any output flags? *)
493 let flags = filter_map (
494 function FishOutput flag -> Some flag | _ -> None
500 failwithf "%s: more than one FishOutput flag is not allowed" name in
502 (* Check return value for errors and display command results. *)
504 | RErr -> pr " return r;\n"
506 pr " if (r == -1) return -1;\n";
507 (match fish_output with
509 pr " printf (\"%%d\\n\", r);\n";
510 | Some FishOutputOctal ->
511 pr " printf (\"%%s%%o\\n\", r != 0 ? \"0\" : \"\", r);\n";
512 | Some FishOutputHexadecimal ->
513 pr " printf (\"%%s%%x\\n\", r != 0 ? \"0x\" : \"\", r);\n");
516 pr " if (r == -1) return -1;\n";
517 (match fish_output with
519 pr " printf (\"%%\" PRIi64 \"\\n\", r);\n";
520 | Some FishOutputOctal ->
521 pr " printf (\"%%s%%\" PRIo64 \"\\n\", r != 0 ? \"0\" : \"\", r);\n";
522 | Some FishOutputHexadecimal ->
523 pr " printf (\"%%s%%\" PRIx64 \"\\n\", r != 0 ? \"0x\" : \"\", r);\n");
526 pr " if (r == -1) return -1;\n";
527 pr " if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
530 pr " if (r == NULL) return -1;\n";
531 pr " printf (\"%%s\\n\", r);\n";
533 | RConstOptString _ ->
534 pr " printf (\"%%s\\n\", r ? : \"(null)\");\n";
537 pr " if (r == NULL) return -1;\n";
538 pr " printf (\"%%s\\n\", r);\n";
542 pr " if (r == NULL) return -1;\n";
543 pr " print_strings (r);\n";
544 pr " free_strings (r);\n";
546 | RStruct (_, typ) ->
547 pr " if (r == NULL) return -1;\n";
548 pr " print_%s (r);\n" typ;
549 pr " guestfs_free_%s (r);\n" typ;
551 | RStructList (_, typ) ->
552 pr " if (r == NULL) return -1;\n";
553 pr " print_%s_list (r);\n" typ;
554 pr " guestfs_free_%s_list (r);\n" typ;
557 pr " if (r == NULL) return -1;\n";
558 pr " print_table (r);\n";
559 pr " free_strings (r);\n";
562 pr " if (r == NULL) return -1;\n";
563 pr " if (full_write (1, r, size) != size) {\n";
564 pr " perror (\"write\");\n";
575 (* run_action function *)
576 pr "int run_action (const char *cmd, size_t argc, char *argv[])\n";
580 fun (name, _, _, flags, _, _, _) ->
581 let name2 = replace_char name '_' '-' in
583 filter_map (function FishAlias n -> Some n | _ -> None) flags in
585 pr "STRCASEEQ (cmd, \"%s\")" name;
586 if name <> name2 then
587 pr " || STRCASEEQ (cmd, \"%s\")" name2;
590 pr " || STRCASEEQ (cmd, \"%s\")" alias;
593 pr " return run_%s (cmd, argc, argv);\n" name;
595 ) all_functions_and_fish_commands_sorted;
598 pr " fprintf (stderr, _(\"%%s: unknown command\\n\"), cmd);\n";
599 pr " if (command_num == 1)\n";
600 pr " extended_help_message ();\n";
607 (* Readline completion for guestfish. *)
608 and generate_fish_completion () =
609 generate_header CStyle GPLv2plus;
613 fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
623 #ifdef HAVE_LIBREADLINE
624 #include <readline/readline.h>
629 #ifdef HAVE_LIBREADLINE
631 static const char *const commands[] = {
632 BUILTIN_COMMANDS_FOR_COMPLETION,
635 (* Get the commands, including the aliases. They don't need to be
636 * sorted - the generator() function just does a dumb linear search.
640 fun (name, _, _, flags, _, _, _) ->
641 let name2 = replace_char name '_' '-' in
643 filter_map (function FishAlias n -> Some n | _ -> None) flags in
645 ) (all_functions @ fish_commands) in
646 let commands = List.flatten commands in
648 List.iter (pr " \"%s\",\n") commands;
654 generator (const char *text, int state)
656 static size_t index, len;
664 rl_attempted_completion_over = 1;
666 while ((name = commands[index]) != NULL) {
668 if (STRCASEEQLEN (name, text, len))
669 return strdup (name);
675 #endif /* HAVE_LIBREADLINE */
677 #ifdef HAVE_RL_COMPLETION_MATCHES
678 #define RL_COMPLETION_MATCHES rl_completion_matches
680 #ifdef HAVE_COMPLETION_MATCHES
681 #define RL_COMPLETION_MATCHES completion_matches
683 #endif /* else just fail if we don't have either symbol */
686 do_completion (const char *text, int start, int end)
688 char **matches = NULL;
690 #ifdef HAVE_LIBREADLINE
691 rl_completion_append_character = ' ';
694 matches = RL_COMPLETION_MATCHES (text, generator);
695 else if (complete_dest_paths)
696 matches = RL_COMPLETION_MATCHES (text, complete_dest_paths_generator);
703 (* Generate the POD documentation for guestfish. *)
704 and generate_fish_actions_pod () =
705 let all_functions_sorted =
707 fun (_, _, _, flags, _, _, _) ->
708 not (List.mem NotInFish flags || List.mem NotInDocs flags)
709 ) all_functions_sorted in
711 let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
714 fun (name, (_, args, optargs), _, flags, _, _, longdesc) ->
716 Str.global_substitute rex (
719 try Str.matched_group 1 s
721 failwithf "error substituting C<guestfs_...> in longdesc of function %s" name in
722 "L</" ^ replace_char sub '_' '-' ^ ">"
724 let name = replace_char name '_' '-' in
726 filter_map (function FishAlias n -> Some n | _ -> None) flags in
730 pr "=head2 %s\n\n" name
735 | Pathname n | Device n | Dev_or_Path n | String n ->
737 | OptString n -> pr " %s" n
738 | StringList n | DeviceList n -> pr " '%s ...'" n
739 | Bool _ -> pr " true|false"
740 | Int n -> pr " %s" n
741 | Int64 n -> pr " %s" n
742 | FileIn n | FileOut n -> pr " (%s|-)" n
743 | BufferIn n -> pr " %s" n
744 | Key _ -> () (* keys are entered at a prompt *)
748 | Bool n | Int n | Int64 n | String n -> pr " [%s:..]" n
753 pr "%s\n\n" longdesc;
755 if List.exists (function FileIn _ | FileOut _ -> true
756 | _ -> false) args then
757 pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
759 if List.exists (function Key _ -> true | _ -> false) args then
760 pr "This command has one or more key or passphrase parameters.
761 Guestfish will prompt for these separately.\n\n";
763 if optargs <> [] then
764 pr "This command has one or more optional arguments. See L</OPTIONAL ARGUMENTS>.\n\n";
766 if List.mem ProtocolLimitWarning flags then
767 pr "%s\n\n" protocol_limit_warning;
769 if List.mem DangerWillRobinson flags then
770 pr "%s\n\n" danger_will_robinson;
772 match deprecation_notice flags with
774 | Some txt -> pr "%s\n\n" txt
775 ) all_functions_sorted
777 (* Generate documentation for guestfish-only commands. *)
778 and generate_fish_commands_pod () =
780 fun (name, _, _, flags, _, _, longdesc) ->
781 let name = replace_char name '_' '-' in
783 filter_map (function FishAlias n -> Some n | _ -> None) flags in
787 pr "=head2 %s\n\n" name
789 pr "%s\n\n" longdesc;
792 and generate_fish_prep_options_h () =
793 generate_header CStyle GPLv2plus;
795 pr "#ifndef PREPOPTS_H\n";
800 const char *name; /* eg. \"fs\" */
802 size_t nr_params; /* optional parameters */
803 struct prep_param *params;
805 const char *shortdesc; /* short description */
806 const char *longdesc; /* long description */
808 /* functions to implement it */
809 void (*prelaunch) (const char *filename, prep_data *);
810 void (*postlaunch) (const char *filename, prep_data *, const char *device);
814 const char *pname; /* parameter name */
815 const char *pdefault; /* parameter default */
816 const char *pdesc; /* parameter description */
819 extern const struct prep preps[];
822 " (List.length prepopts);
825 fun (name, shortdesc, args, longdesc) ->
827 extern void prep_prelaunch_%s (const char *filename, prep_data *data);
828 extern void prep_postlaunch_%s (const char *filename, prep_data *data, const char *device);
834 pr "#endif /* PREPOPTS_H */\n"
836 and generate_fish_prep_options_c () =
837 generate_header CStyle GPLv2plus;
841 #include \"prepopts.h\"
846 fun (name, shortdesc, args, longdesc) ->
847 pr "static struct prep_param %s_args[] = {\n" name;
849 fun (n, default, desc) ->
850 pr " { \"%s\", \"%s\", \"%s\" },\n" n default desc
856 pr "const struct prep preps[] = {\n";
858 fun (name, shortdesc, args, longdesc) ->
859 pr " { \"%s\", %d, %s_args,
862 prep_prelaunch_%s, prep_postlaunch_%s },
864 name (List.length args) name
865 (c_quote shortdesc) (c_quote longdesc)