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