930ef1511f0d463a1f2ce0f19a9a9730eee14061
[libguestfs.git] / generator / generator_fish.ml
1 (* libguestfs
2  * Copyright (C) 2009-2010 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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
17  *)
18
19 (* Please read generator/README first. *)
20
21 open Printf
22
23 open Generator_types
24 open Generator_utils
25 open Generator_pr
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
30 open Generator_prepopts
31 open Generator_c
32
33 (* Generate a lot of different functions for guestfish. *)
34 let generate_fish_cmds () =
35   generate_header CStyle GPLv2plus;
36
37   let all_functions =
38     List.filter (
39       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
40     ) all_functions in
41   let all_functions_sorted =
42     List.filter (
43       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
44     ) all_functions_sorted in
45
46   let all_functions_and_fish_commands_sorted =
47     List.sort action_compare (all_functions_sorted @ fish_commands) in
48
49   pr "#include <config.h>\n";
50   pr "\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";
55   pr "\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";
61   pr "\n";
62   pr "/* Valid suffixes allowed for numbers.  See Gnulib xstrtol function. */\n";
63   pr "static const char *xstrtol_suffixes = \"0kKMGTPEZY\";\n";
64   pr "\n";
65
66   (* list_commands function, which implements guestfish -h *)
67   pr "void list_commands (void)\n";
68   pr "{\n";
69   pr "  printf (\"    %%-16s     %%s\\n\", _(\"Command\"), _(\"Description\"));\n";
70   pr "  list_builtin_commands ();\n";
71   List.iter (
72     fun (name, _, _, flags, _, shortdesc, _) ->
73       let name = replace_char name '_' '-' in
74       pr "  printf (\"%%-20s %%s\\n\", \"%s\", _(\"%s\"));\n"
75         name shortdesc
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";
79   pr "}\n";
80   pr "\n";
81
82   (* display_command function, which implements guestfish -h cmd *)
83   pr "int display_command (const char *cmd)\n";
84   pr "{\n";
85
86   List.iter (
87     fun (name, style, _, flags, _, shortdesc, longdesc) ->
88       let name2 = replace_char name '_' '-' in
89       let aliases =
90         filter_map (function FishAlias n -> Some n | _ -> None) flags in
91       let describe_alias =
92         if aliases <> [] then
93           sprintf "\n\nYou can use %s as an alias for this command."
94             (String.concat " or " (List.map (fun s -> "'" ^ s ^ "'") aliases))
95         else "" in
96
97       pr "  if (";
98       pr "STRCASEEQ (cmd, \"%s\")" name;
99       if name <> name2 then
100         pr " || STRCASEEQ (cmd, \"%s\")" name2;
101       List.iter (
102         fun alias ->
103           pr " || STRCASEEQ (cmd, \"%s\")" alias
104       ) aliases;
105       pr ") {\n";
106       pr "    pod2text (\"%s\", _(\"%s\"), %S);\n"
107         name2 shortdesc
108         ("=head1 DESCRIPTION\n\n" ^
109          longdesc ^ describe_alias);
110       pr "    return 0;\n";
111       pr "  }\n";
112       pr "  else\n"
113   ) fish_commands;
114
115   List.iter (
116     fun (name, style, _, flags, _, shortdesc, longdesc) ->
117       let name2 = replace_char name '_' '-' in
118       let aliases =
119         filter_map (function FishAlias n -> Some n | _ -> None) flags in
120       let longdesc = replace_str longdesc "C<guestfs_" "C<" in
121       let synopsis =
122         match snd style with
123         | [] -> name2
124         | args ->
125             let args = List.filter (function Key _ -> false | _ -> true) args in
126             sprintf "%s %s"
127               name2 (String.concat " " (List.map name_of_argt args)) in
128
129       let warnings =
130         if List.exists (function Key _ -> true | _ -> false) (snd style) then
131           "\n\nThis command has one or more key or passphrase parameters.
132 Guestfish will prompt for these separately."
133         else "" in
134
135       let warnings =
136         warnings ^
137           if List.mem ProtocolLimitWarning flags then
138             ("\n\n" ^ protocol_limit_warning)
139           else "" in
140
141       (* For DangerWillRobinson commands, we should probably have
142        * guestfish prompt before allowing you to use them (especially
143        * in interactive mode). XXX
144        *)
145       let warnings =
146         warnings ^
147           if List.mem DangerWillRobinson flags then
148             ("\n\n" ^ danger_will_robinson)
149           else "" in
150
151       let warnings =
152         warnings ^
153           match deprecation_notice flags with
154           | None -> ""
155           | Some txt -> "\n\n" ^ txt in
156
157       let describe_alias =
158         if aliases <> [] then
159           sprintf "\n\nYou can use %s as an alias for this command."
160             (String.concat " or " (List.map (fun s -> "'" ^ s ^ "'") aliases))
161         else "" in
162
163       pr "  if (";
164       pr "STRCASEEQ (cmd, \"%s\")" name;
165       if name <> name2 then
166         pr " || STRCASEEQ (cmd, \"%s\")" name2;
167       List.iter (
168         fun alias ->
169           pr " || STRCASEEQ (cmd, \"%s\")" alias
170       ) aliases;
171       pr ") {\n";
172       pr "    pod2text (\"%s\", _(\"%s\"), %S);\n"
173         name2 shortdesc
174         ("=head1 SYNOPSIS\n\n " ^ synopsis ^ "\n\n" ^
175          "=head1 DESCRIPTION\n\n" ^
176          longdesc ^ warnings ^ describe_alias);
177       pr "    return 0;\n";
178       pr "  }\n";
179       pr "  else\n"
180   ) all_functions;
181
182   pr "    return display_builtin_command (cmd);\n";
183   pr "}\n";
184   pr "\n";
185
186   let emit_print_list_function typ =
187     pr "static void print_%s_list (struct guestfs_%s_list *%ss)\n"
188       typ typ typ;
189     pr "{\n";
190     pr "  unsigned int i;\n";
191     pr "\n";
192     pr "  for (i = 0; i < %ss->len; ++i) {\n" typ;
193     pr "    printf (\"[%%d] = {\\n\", i);\n";
194     pr "    print_%s_indent (&%ss->val[i], \"  \");\n" typ typ;
195     pr "    printf (\"}\\n\");\n";
196     pr "  }\n";
197     pr "}\n";
198     pr "\n";
199   in
200
201   (* print_* functions *)
202   List.iter (
203     fun (typ, cols) ->
204       let needs_i =
205         List.exists (function (_, (FUUID|FBuffer)) -> true | _ -> false) cols in
206
207       pr "static void print_%s_indent (struct guestfs_%s *%s, const char *indent)\n" typ typ typ;
208       pr "{\n";
209       if needs_i then (
210         pr "  unsigned int i;\n";
211         pr "\n"
212       );
213       List.iter (
214         function
215         | name, FString ->
216             pr "  printf (\"%%s%s: %%s\\n\", indent, %s->%s);\n" name typ name
217         | name, FUUID ->
218             pr "  printf (\"%%s%s: \", indent);\n" name;
219             pr "  for (i = 0; i < 32; ++i)\n";
220             pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
221             pr "  printf (\"\\n\");\n"
222         | name, FBuffer ->
223             pr "  printf (\"%%s%s: \", indent);\n" name;
224             pr "  for (i = 0; i < %s->%s_len; ++i)\n" typ name;
225             pr "    if (c_isprint (%s->%s[i]))\n" typ name;
226             pr "      printf (\"%%c\", %s->%s[i]);\n" typ name;
227             pr "    else\n";
228             pr "      printf (\"\\\\x%%02x\", %s->%s[i]);\n" typ name;
229             pr "  printf (\"\\n\");\n"
230         | name, (FUInt64|FBytes) ->
231             pr "  printf (\"%%s%s: %%\" PRIu64 \"\\n\", indent, %s->%s);\n"
232               name typ name
233         | name, FInt64 ->
234             pr "  printf (\"%%s%s: %%\" PRIi64 \"\\n\", indent, %s->%s);\n"
235               name typ name
236         | name, FUInt32 ->
237             pr "  printf (\"%%s%s: %%\" PRIu32 \"\\n\", indent, %s->%s);\n"
238               name typ name
239         | name, FInt32 ->
240             pr "  printf (\"%%s%s: %%\" PRIi32 \"\\n\", indent, %s->%s);\n"
241               name typ name
242         | name, FChar ->
243             pr "  printf (\"%%s%s: %%c\\n\", indent, %s->%s);\n"
244               name typ name
245         | name, FOptPercent ->
246             pr "  if (%s->%s >= 0) printf (\"%%s%s: %%g %%%%\\n\", indent, %s->%s);\n"
247               typ name name typ name;
248             pr "  else printf (\"%%s%s: \\n\", indent);\n" name
249       ) cols;
250       pr "}\n";
251       pr "\n";
252   ) structs;
253
254   (* Emit a print_TYPE_list function definition only if that function is used. *)
255   List.iter (
256     function
257     | typ, (RStructListOnly | RStructAndList) ->
258         (* generate the function for typ *)
259         emit_print_list_function typ
260     | typ, _ -> () (* empty *)
261   ) (rstructs_used_by all_functions);
262
263   (* Emit a print_TYPE function definition only if that function is used. *)
264   List.iter (
265     function
266     | typ, (RStructOnly | RStructAndList) ->
267         pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
268         pr "{\n";
269         pr "  print_%s_indent (%s, \"\");\n" typ typ;
270         pr "}\n";
271         pr "\n";
272     | typ, _ -> () (* empty *)
273   ) (rstructs_used_by all_functions);
274
275   (* run_<action> actions *)
276   List.iter (
277     fun (name, style, _, flags, _, _, _) ->
278       pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
279       pr "{\n";
280       (match fst style with
281        | RErr
282        | RInt _
283        | RBool _ -> pr "  int r;\n"
284        | RInt64 _ -> pr "  int64_t r;\n"
285        | RConstString _ | RConstOptString _ -> pr "  const char *r;\n"
286        | RString _ -> pr "  char *r;\n"
287        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
288        | RStruct (_, typ) -> pr "  struct guestfs_%s *r;\n" typ
289        | RStructList (_, typ) -> pr "  struct guestfs_%s_list *r;\n" typ
290        | RBufferOut _ ->
291            pr "  char *r;\n";
292            pr "  size_t size;\n";
293       );
294       List.iter (
295         function
296         | Device n
297         | String n
298         | OptString n -> pr "  const char *%s;\n" n
299         | Pathname n
300         | Dev_or_Path n
301         | FileIn n
302         | FileOut n
303         | Key n -> pr "  char *%s;\n" n
304         | BufferIn n ->
305             pr "  const char *%s;\n" n;
306             pr "  size_t %s_size;\n" n
307         | StringList n | DeviceList n -> pr "  char **%s;\n" n
308         | Bool n -> pr "  int %s;\n" n
309         | Int n -> pr "  int %s;\n" n
310         | Int64 n -> pr "  int64_t %s;\n" n
311       ) (snd style);
312
313       (* Check and convert parameters. *)
314       let argc_expected =
315         let args_no_keys =
316           List.filter (function Key _ -> false | _ -> true) (snd style) in
317         List.length args_no_keys in
318       pr "  if (argc != %d) {\n" argc_expected;
319       pr "    fprintf (stderr, _(\"%%s should have %%d parameter(s)\\n\"), cmd, %d);\n"
320         argc_expected;
321       pr "    fprintf (stderr, _(\"type 'help %%s' for help on %%s\\n\"), cmd, cmd);\n";
322       pr "    return -1;\n";
323       pr "  }\n";
324
325       let parse_integer fn fntyp rtyp range name =
326         pr "  {\n";
327         pr "    strtol_error xerr;\n";
328         pr "    %s r;\n" fntyp;
329         pr "\n";
330         pr "    xerr = %s (argv[i++], NULL, 0, &r, xstrtol_suffixes);\n" fn;
331         pr "    if (xerr != LONGINT_OK) {\n";
332         pr "      fprintf (stderr,\n";
333         pr "               _(\"%%s: %%s: invalid integer parameter (%%s returned %%d)\\n\"),\n";
334         pr "               cmd, \"%s\", \"%s\", xerr);\n" name fn;
335         pr "      return -1;\n";
336         pr "    }\n";
337         (match range with
338          | None -> ()
339          | Some (min, max, comment) ->
340              pr "    /* %s */\n" comment;
341              pr "    if (r < %s || r > %s) {\n" min max;
342              pr "      fprintf (stderr, _(\"%%s: %%s: integer out of range\\n\"), cmd, \"%s\");\n"
343                name;
344              pr "      return -1;\n";
345              pr "    }\n";
346              pr "    /* The check above should ensure this assignment does not overflow. */\n";
347         );
348         pr "    %s = r;\n" name;
349         pr "  }\n";
350       in
351
352       if snd style <> [] then
353         pr "  size_t i = 0;\n";
354
355       List.iter (
356         function
357         | Device name
358         | String name ->
359             pr "  %s = argv[i++];\n" name
360         | Pathname name
361         | Dev_or_Path name ->
362             pr "  %s = resolve_win_path (argv[i++]);\n" name;
363             pr "  if (%s == NULL) return -1;\n" name
364         | OptString name ->
365             pr "  %s = STRNEQ (argv[i], \"\") ? argv[i] : NULL;\n" name;
366             pr "  i++;\n"
367         | BufferIn name ->
368             pr "  %s = argv[i];\n" name;
369             pr "  %s_size = strlen (argv[i]);\n" name;
370             pr "  i++;\n"
371         | FileIn name ->
372             pr "  %s = file_in (argv[i++]);\n" name;
373             pr "  if (%s == NULL) return -1;\n" name
374         | FileOut name ->
375             pr "  %s = file_out (argv[i++]);\n" name;
376             pr "  if (%s == NULL) return -1;\n" name
377         | StringList name | DeviceList name ->
378             pr "  %s = parse_string_list (argv[i++]);\n" name;
379             pr "  if (%s == NULL) return -1;\n" name
380         | Key name ->
381             pr "  %s = read_key (\"%s\");\n" name name;
382             pr "  if (%s == NULL) return -1;\n" name
383         | Bool name ->
384             pr "  %s = is_true (argv[i++]) ? 1 : 0;\n" name
385         | Int name ->
386             let range =
387               let min = "(-(2LL<<30))"
388               and max = "((2LL<<30)-1)"
389               and comment =
390                 "The Int type in the generator is a signed 31 bit int." in
391               Some (min, max, comment) in
392             parse_integer "xstrtoll" "long long" "int" range name
393         | Int64 name ->
394             parse_integer "xstrtoll" "long long" "int64_t" None name
395       ) (snd style);
396
397       (* Call C API function. *)
398       pr "  r = guestfs_%s " name;
399       generate_c_call_args ~handle:"g" style;
400       pr ";\n";
401
402       List.iter (
403         function
404         | Device _ | String _
405         | OptString _ | Bool _
406         | Int _ | Int64 _
407         | BufferIn _ -> ()
408         | Pathname name | Dev_or_Path name | FileOut name
409         | Key name ->
410             pr "  free (%s);\n" name
411         | FileIn name ->
412             pr "  free_file_in (%s);\n" name
413         | StringList name | DeviceList name ->
414             pr "  free_strings (%s);\n" name
415       ) (snd style);
416
417       (* Any output flags? *)
418       let fish_output =
419         let flags = filter_map (
420           function FishOutput flag -> Some flag | _ -> None
421         ) flags in
422         match flags with
423         | [] -> None
424         | [f] -> Some f
425         | _ ->
426             failwithf "%s: more than one FishOutput flag is not allowed" name in
427
428       (* Check return value for errors and display command results. *)
429       (match fst style with
430        | RErr -> pr "  return r;\n"
431        | RInt _ ->
432            pr "  if (r == -1) return -1;\n";
433            (match fish_output with
434             | None ->
435                 pr "  printf (\"%%d\\n\", r);\n";
436             | Some FishOutputOctal ->
437                 pr "  printf (\"%%s%%o\\n\", r != 0 ? \"0\" : \"\", r);\n";
438             | Some FishOutputHexadecimal ->
439                 pr "  printf (\"%%s%%x\\n\", r != 0 ? \"0x\" : \"\", r);\n");
440            pr "  return 0;\n"
441        | RInt64 _ ->
442            pr "  if (r == -1) return -1;\n";
443            (match fish_output with
444             | None ->
445                 pr "  printf (\"%%\" PRIi64 \"\\n\", r);\n";
446             | Some FishOutputOctal ->
447                 pr "  printf (\"%%s%%\" PRIo64 \"\\n\", r != 0 ? \"0\" : \"\", r);\n";
448             | Some FishOutputHexadecimal ->
449                 pr "  printf (\"%%s%%\" PRIx64 \"\\n\", r != 0 ? \"0x\" : \"\", r);\n");
450            pr "  return 0;\n"
451        | RBool _ ->
452            pr "  if (r == -1) return -1;\n";
453            pr "  if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
454            pr "  return 0;\n"
455        | RConstString _ ->
456            pr "  if (r == NULL) return -1;\n";
457            pr "  printf (\"%%s\\n\", r);\n";
458            pr "  return 0;\n"
459        | RConstOptString _ ->
460            pr "  printf (\"%%s\\n\", r ? : \"(null)\");\n";
461            pr "  return 0;\n"
462        | RString _ ->
463            pr "  if (r == NULL) return -1;\n";
464            pr "  printf (\"%%s\\n\", r);\n";
465            pr "  free (r);\n";
466            pr "  return 0;\n"
467        | RStringList _ ->
468            pr "  if (r == NULL) return -1;\n";
469            pr "  print_strings (r);\n";
470            pr "  free_strings (r);\n";
471            pr "  return 0;\n"
472        | RStruct (_, typ) ->
473            pr "  if (r == NULL) return -1;\n";
474            pr "  print_%s (r);\n" typ;
475            pr "  guestfs_free_%s (r);\n" typ;
476            pr "  return 0;\n"
477        | RStructList (_, typ) ->
478            pr "  if (r == NULL) return -1;\n";
479            pr "  print_%s_list (r);\n" typ;
480            pr "  guestfs_free_%s_list (r);\n" typ;
481            pr "  return 0;\n"
482        | RHashtable _ ->
483            pr "  if (r == NULL) return -1;\n";
484            pr "  print_table (r);\n";
485            pr "  free_strings (r);\n";
486            pr "  return 0;\n"
487        | RBufferOut _ ->
488            pr "  if (r == NULL) return -1;\n";
489            pr "  if (full_write (1, r, size) != size) {\n";
490            pr "    perror (\"write\");\n";
491            pr "    free (r);\n";
492            pr "    return -1;\n";
493            pr "  }\n";
494            pr "  free (r);\n";
495            pr "  return 0;\n"
496       );
497       pr "}\n";
498       pr "\n"
499   ) all_functions;
500
501   (* run_action function *)
502   pr "int run_action (const char *cmd, int argc, char *argv[])\n";
503   pr "{\n";
504
505   List.iter (
506     fun (name, _, _, flags, _, _, _) ->
507       let name2 = replace_char name '_' '-' in
508       let aliases =
509         filter_map (function FishAlias n -> Some n | _ -> None) flags in
510       pr "  if (";
511       pr "STRCASEEQ (cmd, \"%s\")" name;
512       if name <> name2 then
513         pr " || STRCASEEQ (cmd, \"%s\")" name2;
514       List.iter (
515         fun alias ->
516           pr " || STRCASEEQ (cmd, \"%s\")" alias;
517       ) aliases;
518       pr ")\n";
519       pr "    return run_%s (cmd, argc, argv);\n" name;
520       pr "  else\n";
521   ) all_functions_and_fish_commands_sorted;
522
523   pr "    {\n";
524   pr "      fprintf (stderr, _(\"%%s: unknown command\\n\"), cmd);\n";
525   pr "      if (command_num == 1)\n";
526   pr "        extended_help_message ();\n";
527   pr "      return -1;\n";
528   pr "    }\n";
529   pr "  return 0;\n";
530   pr "}\n";
531   pr "\n"
532
533 (* Readline completion for guestfish. *)
534 and generate_fish_completion () =
535   generate_header CStyle GPLv2plus;
536
537   let all_functions =
538     List.filter (
539       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
540     ) all_functions in
541
542   pr "\
543 #include <config.h>
544
545 #include <stdio.h>
546 #include <stdlib.h>
547 #include <string.h>
548
549 #ifdef HAVE_LIBREADLINE
550 #include <readline/readline.h>
551 #endif
552
553 #include \"fish.h\"
554
555 #ifdef HAVE_LIBREADLINE
556
557 static const char *const commands[] = {
558   BUILTIN_COMMANDS_FOR_COMPLETION,
559 ";
560
561   (* Get the commands, including the aliases.  They don't need to be
562    * sorted - the generator() function just does a dumb linear search.
563    *)
564   let commands =
565     List.map (
566       fun (name, _, _, flags, _, _, _) ->
567         let name2 = replace_char name '_' '-' in
568         let aliases =
569           filter_map (function FishAlias n -> Some n | _ -> None) flags in
570         name2 :: aliases
571     ) (all_functions @ fish_commands) in
572   let commands = List.flatten commands in
573
574   List.iter (pr "  \"%s\",\n") commands;
575
576   pr "  NULL
577 };
578
579 static char *
580 generator (const char *text, int state)
581 {
582   static size_t index, len;
583   const char *name;
584
585   if (!state) {
586     index = 0;
587     len = strlen (text);
588   }
589
590   rl_attempted_completion_over = 1;
591
592   while ((name = commands[index]) != NULL) {
593     index++;
594     if (STRCASEEQLEN (name, text, len))
595       return strdup (name);
596   }
597
598   return NULL;
599 }
600
601 #endif /* HAVE_LIBREADLINE */
602
603 #ifdef HAVE_RL_COMPLETION_MATCHES
604 #define RL_COMPLETION_MATCHES rl_completion_matches
605 #else
606 #ifdef HAVE_COMPLETION_MATCHES
607 #define RL_COMPLETION_MATCHES completion_matches
608 #endif
609 #endif /* else just fail if we don't have either symbol */
610
611 char **
612 do_completion (const char *text, int start, int end)
613 {
614   char **matches = NULL;
615
616 #ifdef HAVE_LIBREADLINE
617   rl_completion_append_character = ' ';
618
619   if (start == 0)
620     matches = RL_COMPLETION_MATCHES (text, generator);
621   else if (complete_dest_paths)
622     matches = RL_COMPLETION_MATCHES (text, complete_dest_paths_generator);
623 #endif
624
625   return matches;
626 }
627 ";
628
629 (* Generate the POD documentation for guestfish. *)
630 and generate_fish_actions_pod () =
631   let all_functions_sorted =
632     List.filter (
633       fun (_, _, _, flags, _, _, _) ->
634         not (List.mem NotInFish flags || List.mem NotInDocs flags)
635     ) all_functions_sorted in
636
637   let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
638
639   List.iter (
640     fun (name, style, _, flags, _, _, longdesc) ->
641       let longdesc =
642         Str.global_substitute rex (
643           fun s ->
644             let sub =
645               try Str.matched_group 1 s
646               with Not_found ->
647                 failwithf "error substituting C<guestfs_...> in longdesc of function %s" name in
648             "L</" ^ replace_char sub '_' '-' ^ ">"
649         ) longdesc in
650       let name = replace_char name '_' '-' in
651       let aliases =
652         filter_map (function FishAlias n -> Some n | _ -> None) flags in
653
654       List.iter (
655         fun name ->
656           pr "=head2 %s\n\n" name
657       ) (name :: aliases);
658       pr " %s" name;
659       List.iter (
660         function
661         | Pathname n | Device n | Dev_or_Path n | String n ->
662             pr " %s" n
663         | OptString n -> pr " %s" n
664         | StringList n | DeviceList n -> pr " '%s ...'" n
665         | Bool _ -> pr " true|false"
666         | Int n -> pr " %s" n
667         | Int64 n -> pr " %s" n
668         | FileIn n | FileOut n -> pr " (%s|-)" n
669         | BufferIn n -> pr " %s" n
670         | Key _ -> () (* keys are entered at a prompt *)
671       ) (snd style);
672       pr "\n";
673       pr "\n";
674       pr "%s\n\n" longdesc;
675
676       if List.exists (function FileIn _ | FileOut _ -> true
677                       | _ -> false) (snd style) then
678         pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
679
680       if List.exists (function Key _ -> true | _ -> false) (snd style) then
681         pr "This command has one or more key or passphrase parameters.
682 Guestfish will prompt for these separately.\n\n";
683
684       if List.mem ProtocolLimitWarning flags then
685         pr "%s\n\n" protocol_limit_warning;
686
687       if List.mem DangerWillRobinson flags then
688         pr "%s\n\n" danger_will_robinson;
689
690       match deprecation_notice flags with
691       | None -> ()
692       | Some txt -> pr "%s\n\n" txt
693   ) all_functions_sorted
694
695 (* Generate documentation for guestfish-only commands. *)
696 and generate_fish_commands_pod () =
697   List.iter (
698     fun (name, style, _, flags, _, _, longdesc) ->
699       let name = replace_char name '_' '-' in
700       let aliases =
701         filter_map (function FishAlias n -> Some n | _ -> None) flags in
702
703       List.iter (
704         fun name ->
705           pr "=head2 %s\n\n" name
706       ) (name :: aliases);
707       pr "%s\n\n" longdesc;
708   ) fish_commands
709
710 and generate_fish_prep_options_h () =
711   generate_header CStyle GPLv2plus;
712
713   pr "#ifndef PREPOPTS_H\n";
714   pr "\n";
715
716   pr "\
717 struct prep {
718   const char *name;             /* eg. \"fs\" */
719
720   size_t nr_params;             /* optional parameters */
721   struct prep_param *params;
722
723   const char *shortdesc;        /* short description */
724   const char *longdesc;         /* long description */
725
726                                 /* functions to implement it */
727   void (*prelaunch) (const char *filename, prep_data *);
728   void (*postlaunch) (const char *filename, prep_data *, const char *device);
729 };
730
731 struct prep_param {
732   const char *pname;            /* parameter name */
733   const char *pdefault;         /* parameter default */
734   const char *pdesc;            /* parameter description */
735 };
736
737 extern const struct prep preps[];
738 #define NR_PREPS %d
739
740 " (List.length prepopts);
741
742   List.iter (
743     fun (name, shortdesc, args, longdesc) ->
744       pr "\
745 extern void prep_prelaunch_%s (const char *filename, prep_data *data);
746 extern void prep_postlaunch_%s (const char *filename, prep_data *data, const char *device);
747
748 " name name;
749   ) prepopts;
750
751   pr "\n";
752   pr "#endif /* PREPOPTS_H */\n"
753
754 and generate_fish_prep_options_c () =
755   generate_header CStyle GPLv2plus;
756
757   pr "\
758 #include \"fish.h\"
759 #include \"prepopts.h\"
760
761 ";
762
763   List.iter (
764     fun (name, shortdesc, args, longdesc) ->
765       pr "static struct prep_param %s_args[] = {\n" name;
766       List.iter (
767         fun (n, default, desc) ->
768           pr "  { \"%s\", \"%s\", \"%s\" },\n" n default desc
769       ) args;
770       pr "};\n";
771       pr "\n";
772   ) prepopts;
773
774   pr "const struct prep preps[] = {\n";
775   List.iter (
776     fun (name, shortdesc, args, longdesc) ->
777       pr "  { \"%s\", %d, %s_args,
778     \"%s\",
779     \"%s\",
780     prep_prelaunch_%s, prep_postlaunch_%s },
781 "
782         name (List.length args) name
783         (c_quote shortdesc) (c_quote longdesc)
784         name name;
785   ) prepopts;
786   pr "};\n"