+ pr " if (g->state != READY) {\n";
+ pr " error (g, \"%s called from the wrong state, %%d != READY\",\n"
+ name;
+ pr " g->state);\n";
+ pr " return %s;\n" error_code;
+ pr " }\n";
+ pr "\n";
+ pr " memset (&rv, 0, sizeof rv);\n";
+ pr "\n";
+
+ (match snd style with
+ | P0 ->
+ pr " serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
+ (String.uppercase shortname)
+ | args ->
+ iter_args (
+ function
+ | String n ->
+ pr " args.%s = (char *) %s;\n" n n
+ | OptString n ->
+ pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n
+ | Bool n ->
+ pr " args.%s = %s;\n" n n
+ | Int n ->
+ pr " args.%s = %s;\n" n n
+ ) args;
+ pr " serial = dispatch (g, GUESTFS_PROC_%s,\n"
+ (String.uppercase shortname);
+ pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n"
+ name;
+ );
+ pr " if (serial == -1)\n";
+ pr " return %s;\n" error_code;
+ pr "\n";
+
+ pr " rv.cb_done = 0;\n";
+ pr " g->reply_cb_internal = %s_cb;\n" shortname;
+ pr " g->reply_cb_internal_data = &rv;\n";
+ pr " main_loop.main_loop_run (g);\n";
+ pr " g->reply_cb_internal = NULL;\n";
+ pr " g->reply_cb_internal_data = NULL;\n";
+ pr " if (!rv.cb_done) {\n";
+ pr " error (g, \"%s failed, see earlier error messages\");\n" name;
+ pr " return %s;\n" error_code;
+ pr " }\n";
+ pr "\n";
+
+ pr " if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_%s, serial) == -1)\n"
+ (String.uppercase shortname);
+ pr " return %s;\n" error_code;
+ pr "\n";
+
+ pr " if (rv.hdr.status == GUESTFS_STATUS_ERROR) {\n";
+ pr " error (g, \"%%s\", rv.err.error);\n";
+ pr " return %s;\n" error_code;
+ pr " }\n";
+ pr "\n";
+
+ (match fst style with
+ | Err -> pr " return 0;\n"
+ | RInt n
+ | RBool n -> pr " return rv.ret.%s;\n" n
+ | RConstString _ ->
+ failwithf "RConstString cannot be returned from a daemon function"
+ | RString n ->
+ pr " return rv.ret.%s; /* caller will free */\n" n
+ | RStringList n ->
+ pr " /* caller will free this, but we need to add a NULL entry */\n";
+ pr " rv.ret.%s.%s_val =" n n;
+ pr " safe_realloc (g, rv.ret.%s.%s_val,\n" n n;
+ pr " sizeof (char *) * (rv.ret.%s.%s_len + 1));\n"
+ n n;
+ pr " rv.ret.%s.%s_val[rv.ret.%s.%s_len] = NULL;\n" n n n n;
+ pr " return rv.ret.%s.%s_val;\n" n n
+ | RIntBool _ ->
+ pr " /* caller with free this */\n";
+ pr " return safe_memdup (g, &rv.ret, sizeof (rv.ret));\n"
+ | RPVList n ->
+ pr " /* caller will free this */\n";
+ pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
+ | RVGList n ->
+ pr " /* caller will free this */\n";
+ pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
+ | RLVList n ->
+ pr " /* caller will free this */\n";
+ pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
+ );
+
+ pr "}\n\n"
+ ) daemon_functions
+
+(* Generate daemon/actions.h. *)
+and generate_daemon_actions_h () =
+ generate_header CStyle GPLv2;
+
+ pr "#include \"../src/guestfs_protocol.h\"\n";
+ pr "\n";
+
+ List.iter (
+ fun (name, style, _, _, _, _) ->
+ generate_prototype
+ ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
+ name style;
+ ) daemon_functions
+
+(* Generate the server-side stubs. *)
+and generate_daemon_actions () =
+ generate_header CStyle GPLv2;
+
+ pr "#define _GNU_SOURCE // for strchrnul\n";
+ pr "\n";
+ pr "#include <stdio.h>\n";
+ pr "#include <stdlib.h>\n";
+ pr "#include <string.h>\n";
+ pr "#include <inttypes.h>\n";
+ pr "#include <ctype.h>\n";
+ pr "#include <rpc/types.h>\n";
+ pr "#include <rpc/xdr.h>\n";
+ pr "\n";
+ pr "#include \"daemon.h\"\n";
+ pr "#include \"../src/guestfs_protocol.h\"\n";
+ pr "#include \"actions.h\"\n";
+ pr "\n";
+
+ List.iter (
+ fun (name, style, _, _, _, _) ->
+ (* Generate server-side stubs. *)
+ pr "static void %s_stub (XDR *xdr_in)\n" name;
+ pr "{\n";
+ let error_code =
+ match fst style with
+ | Err | RInt _ -> pr " int r;\n"; "-1"
+ | RBool _ -> pr " int r;\n"; "-1"
+ | RConstString _ ->
+ failwithf "RConstString cannot be returned from a daemon function"
+ | RString _ -> pr " char *r;\n"; "NULL"
+ | RStringList _ -> pr " char **r;\n"; "NULL"
+ | RIntBool _ -> pr " guestfs_%s_ret *r;\n" name; "NULL"
+ | RPVList _ -> pr " guestfs_lvm_int_pv_list *r;\n"; "NULL"
+ | RVGList _ -> pr " guestfs_lvm_int_vg_list *r;\n"; "NULL"
+ | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL" in
+
+ (match snd style with
+ | P0 -> ()
+ | args ->
+ pr " struct guestfs_%s_args args;\n" name;
+ iter_args (
+ function
+ | String n
+ | OptString n -> pr " const char *%s;\n" n
+ | Bool n -> pr " int %s;\n" n
+ | Int n -> pr " int %s;\n" n
+ ) args
+ );
+ pr "\n";
+
+ (match snd style with
+ | P0 -> ()
+ | args ->
+ pr " memset (&args, 0, sizeof args);\n";
+ pr "\n";
+ pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
+ pr " reply_with_error (\"%%s: daemon failed to decode procedure arguments\", \"%s\");\n" name;
+ pr " return;\n";
+ pr " }\n";
+ iter_args (
+ function
+ | String n -> pr " %s = args.%s;\n" n n
+ | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n
+ | Bool n -> pr " %s = args.%s;\n" n n
+ | Int n -> pr " %s = args.%s;\n" n n
+ ) args;
+ pr "\n"
+ );
+
+ pr " r = do_%s " name;
+ generate_call_args style;
+ pr ";\n";
+
+ pr " if (r == %s)\n" error_code;
+ pr " /* do_%s has already called reply_with_error, so just return */\n" name;
+ pr " return;\n";
+ pr "\n";
+
+ (match fst style with
+ | Err -> pr " reply (NULL, NULL);\n"
+ | RInt n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s = r;\n" n;
+ pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name
+ | RBool n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s = r;\n" n;
+ pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name
+ | RConstString _ ->
+ failwithf "RConstString cannot be returned from a daemon function"
+ | RString n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s = r;\n" n;
+ pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+ pr " free (r);\n"
+ | RStringList n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s.%s_len = count_strings (r);\n" n n;
+ pr " ret.%s.%s_val = r;\n" n n;
+ pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+ pr " free_strings (r);\n"
+ | RIntBool _ ->
+ pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name;
+ pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name
+ | RPVList n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s = *r;\n" n;
+ pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+ pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
+ | RVGList n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s = *r;\n" n;
+ pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+ pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
+ | RLVList n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s = *r;\n" n;
+ pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+ pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
+ );
+
+ pr "}\n\n";
+ ) daemon_functions;
+
+ (* Dispatch function. *)
+ pr "void dispatch_incoming_message (XDR *xdr_in)\n";
+ pr "{\n";
+ pr " switch (proc_nr) {\n";
+
+ List.iter (
+ fun (name, style, _, _, _, _) ->
+ pr " case GUESTFS_PROC_%s:\n" (String.uppercase name);
+ pr " %s_stub (xdr_in);\n" name;
+ pr " break;\n"
+ ) daemon_functions;
+
+ pr " default:\n";
+ pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
+ pr " }\n";
+ pr "}\n";
+ pr "\n";
+
+ (* LVM columns and tokenization functions. *)
+ (* XXX This generates crap code. We should rethink how we
+ * do this parsing.
+ *)
+ List.iter (
+ function
+ | typ, cols ->
+ pr "static const char *lvm_%s_cols = \"%s\";\n"
+ typ (String.concat "," (List.map fst cols));
+ pr "\n";
+
+ pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
+ pr "{\n";
+ pr " char *tok, *p, *next;\n";
+ pr " int i, j;\n";
+ pr "\n";
+ (*
+ pr " fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
+ pr "\n";
+ *)
+ pr " if (!str) {\n";
+ pr " fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
+ pr " return -1;\n";
+ pr " }\n";
+ pr " if (!*str || isspace (*str)) {\n";
+ pr " fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
+ pr " return -1;\n";
+ pr " }\n";
+ pr " tok = str;\n";
+ List.iter (
+ fun (name, coltype) ->
+ pr " if (!tok) {\n";
+ pr " fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
+ pr " return -1;\n";
+ pr " }\n";
+ pr " p = strchrnul (tok, ',');\n";
+ pr " if (*p) next = p+1; else next = NULL;\n";
+ pr " *p = '\\0';\n";
+ (match coltype with
+ | `String ->
+ pr " r->%s = strdup (tok);\n" name;
+ pr " if (r->%s == NULL) {\n" name;
+ pr " perror (\"strdup\");\n";
+ pr " return -1;\n";
+ pr " }\n"
+ | `UUID ->
+ pr " for (i = j = 0; i < 32; ++j) {\n";
+ pr " if (tok[j] == '\\0') {\n";
+ pr " fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
+ pr " return -1;\n";
+ pr " } else if (tok[j] != '-')\n";
+ pr " r->%s[i++] = tok[j];\n" name;
+ pr " }\n";
+ | `Bytes ->
+ pr " if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
+ pr " fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
+ pr " return -1;\n";
+ pr " }\n";
+ | `Int ->
+ pr " if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
+ pr " fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
+ pr " return -1;\n";
+ pr " }\n";
+ | `OptPercent ->
+ pr " if (tok[0] == '\\0')\n";
+ pr " r->%s = -1;\n" name;
+ pr " else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
+ pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
+ pr " return -1;\n";
+ pr " }\n";
+ );
+ pr " tok = next;\n";
+ ) cols;
+
+ pr " if (tok != NULL) {\n";
+ pr " fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
+ pr " return -1;\n";
+ pr " }\n";
+ pr " return 0;\n";
+ pr "}\n";
+ pr "\n";
+
+ pr "guestfs_lvm_int_%s_list *\n" typ;
+ pr "parse_command_line_%ss (void)\n" typ;
+ pr "{\n";
+ pr " char *out, *err;\n";
+ pr " char *p, *pend;\n";
+ pr " int r, i;\n";
+ pr " guestfs_lvm_int_%s_list *ret;\n" typ;
+ pr " void *newp;\n";
+ pr "\n";
+ pr " ret = malloc (sizeof *ret);\n";
+ pr " if (!ret) {\n";
+ pr " reply_with_perror (\"malloc\");\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr "\n";
+ pr " ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
+ pr " ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
+ pr "\n";
+ pr " r = command (&out, &err,\n";
+ pr " \"/sbin/lvm\", \"%ss\",\n" typ;
+ pr " \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
+ pr " \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
+ pr " if (r == -1) {\n";
+ pr " reply_with_error (\"%%s\", err);\n";
+ pr " free (out);\n";
+ pr " free (err);\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr "\n";
+ pr " free (err);\n";
+ pr "\n";
+ pr " /* Tokenize each line of the output. */\n";
+ pr " p = out;\n";
+ pr " i = 0;\n";
+ pr " while (p) {\n";
+ pr " pend = strchr (p, '\\n'); /* Get the next line of output. */\n";
+ pr " if (pend) {\n";
+ pr " *pend = '\\0';\n";
+ pr " pend++;\n";
+ pr " }\n";
+ pr "\n";
+ pr " while (*p && isspace (*p)) /* Skip any leading whitespace. */\n";
+ pr " p++;\n";
+ pr "\n";
+ pr " if (!*p) { /* Empty line? Skip it. */\n";
+ pr " p = pend;\n";
+ pr " continue;\n";
+ pr " }\n";
+ pr "\n";
+ pr " /* Allocate some space to store this next entry. */\n";
+ pr " newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
+ pr " sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
+ pr " if (newp == NULL) {\n";
+ pr " reply_with_perror (\"realloc\");\n";
+ pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
+ pr " free (ret);\n";
+ pr " free (out);\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr " ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
+ pr "\n";
+ pr " /* Tokenize the next entry. */\n";
+ pr " r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
+ pr " if (r == -1) {\n";
+ pr " reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
+ pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
+ pr " free (ret);\n";
+ pr " free (out);\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr "\n";
+ pr " ++i;\n";
+ pr " p = pend;\n";
+ pr " }\n";
+ pr "\n";
+ pr " ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
+ pr "\n";
+ pr " free (out);\n";
+ pr " return ret;\n";
+ pr "}\n"
+
+ ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
+
+(* Generate a lot of different functions for guestfish. *)
+and generate_fish_cmds () =
+ generate_header CStyle GPLv2;
+
+ let all_functions =
+ List.filter (
+ fun (_, _, _, flags, _, _) -> not (List.mem NotInFish flags)
+ ) all_functions in
+ let all_functions_sorted =
+ List.filter (
+ fun (_, _, _, flags, _, _) -> not (List.mem NotInFish flags)
+ ) all_functions_sorted in
+
+ pr "#include <stdio.h>\n";
+ pr "#include <stdlib.h>\n";
+ pr "#include <string.h>\n";
+ pr "#include <inttypes.h>\n";
+ pr "\n";
+ pr "#include <guestfs.h>\n";
+ pr "#include \"fish.h\"\n";
+ pr "\n";
+
+ (* list_commands function, which implements guestfish -h *)
+ pr "void list_commands (void)\n";
+ pr "{\n";
+ pr " printf (\" %%-16s %%s\\n\", \"Command\", \"Description\");\n";
+ pr " list_builtin_commands ();\n";
+ List.iter (
+ fun (name, _, _, flags, shortdesc, _) ->
+ let name = replace_char name '_' '-' in
+ pr " printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
+ name shortdesc
+ ) all_functions_sorted;
+ pr " printf (\" Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
+ pr "}\n";
+ pr "\n";
+
+ (* display_command function, which implements guestfish -h cmd *)
+ pr "void display_command (const char *cmd)\n";
+ pr "{\n";
+ 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 longdesc = replace_str longdesc "C<guestfs_" "C<" in
+ let synopsis =
+ match snd style with
+ | P0 -> name2
+ | args ->
+ sprintf "%s <%s>"
+ name2 (String.concat "> <" (map_args name_of_argt args)) in
+
+ let warnings =
+ if List.mem ProtocolLimitWarning flags then
+ "\n\nBecause of the message protocol, there is a transfer limit
+of somewhere between 2MB and 4MB. To transfer large files you should use
+FTP."
+ else "" in
+
+ let describe_alias =
+ if name <> alias then
+ sprintf "\n\nYou can use '%s' as an alias for this command." alias
+ else "" in
+
+ pr " if (";
+ pr "strcasecmp (cmd, \"%s\") == 0" name;
+ if name <> name2 then
+ pr " || strcasecmp (cmd, \"%s\") == 0" name2;
+ if name <> alias then
+ pr " || strcasecmp (cmd, \"%s\") == 0" alias;
+ pr ")\n";
+ pr " pod2text (\"%s - %s\", %S);\n"
+ name2 shortdesc
+ (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings ^ describe_alias);
+ pr " else\n"
+ ) all_functions;
+ pr " display_builtin_command (cmd);\n";
+ pr "}\n";
+ pr "\n";
+
+ (* print_{pv,vg,lv}_list functions *)
+ List.iter (
+ function
+ | typ, cols ->
+ pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
+ pr "{\n";
+ pr " int i;\n";
+ pr "\n";
+ List.iter (
+ function
+ | name, `String ->
+ pr " printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
+ | name, `UUID ->
+ pr " printf (\"%s: \");\n" name;
+ pr " for (i = 0; i < 32; ++i)\n";
+ pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
+ pr " printf (\"\\n\");\n"
+ | name, `Bytes ->
+ pr " printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
+ | name, `Int ->
+ pr " printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
+ | name, `OptPercent ->
+ pr " if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
+ typ name name typ name;
+ pr " else printf (\"%s: \\n\");\n" name
+ ) cols;
+ pr "}\n";
+ pr "\n";
+ pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
+ typ typ typ;
+ pr "{\n";
+ pr " int i;\n";
+ pr "\n";
+ pr " for (i = 0; i < %ss->len; ++i)\n" typ;
+ pr " print_%s (&%ss->val[i]);\n" typ typ;
+ pr "}\n";
+ pr "\n";
+ ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
+
+ (* run_<action> actions *)
+ List.iter (
+ fun (name, style, _, flags, _, _) ->
+ pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
+ pr "{\n";
+ (match fst style with
+ | Err
+ | RInt _
+ | RBool _ -> pr " int r;\n"
+ | RConstString _ -> pr " const char *r;\n"
+ | RString _ -> pr " char *r;\n"
+ | RStringList _ -> pr " char **r;\n"
+ | RIntBool _ -> pr " struct guestfs_int_bool *r;\n"
+ | RPVList _ -> pr " struct guestfs_lvm_pv_list *r;\n"
+ | RVGList _ -> pr " struct guestfs_lvm_vg_list *r;\n"
+ | RLVList _ -> pr " struct guestfs_lvm_lv_list *r;\n"
+ );
+ iter_args (
+ function
+ | String n -> pr " const char *%s;\n" n
+ | OptString n -> pr " const char *%s;\n" n
+ | Bool n -> pr " int %s;\n" n
+ | Int n -> pr " int %s;\n" n
+ ) (snd style);
+
+ (* Check and convert parameters. *)
+ let argc_expected = nr_args (snd style) in
+ pr " if (argc != %d) {\n" argc_expected;
+ pr " fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
+ argc_expected;
+ pr " fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
+ pr " return -1;\n";
+ pr " }\n";
+ iteri_args (
+ fun i ->
+ function
+ | String name -> pr " %s = argv[%d];\n" name i
+ | OptString name ->
+ pr " %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
+ name i i
+ | Bool name ->
+ pr " %s = is_true (argv[%d]) ? 1 : 0;\n" name i
+ | Int name ->
+ pr " %s = atoi (argv[%d]);\n" name i
+ ) (snd style);
+
+ (* Call C API function. *)
+ let fn =
+ try find_map (function FishAction n -> Some n | _ -> None) flags
+ with Not_found -> sprintf "guestfs_%s" name in
+ pr " r = %s " fn;
+ generate_call_args ~handle:"g" style;
+ pr ";\n";
+
+ (* Check return value for errors and display command results. *)
+ (match fst style with
+ | Err -> pr " return r;\n"
+ | RInt _ ->
+ pr " if (r == -1) return -1;\n";
+ pr " if (r) printf (\"%%d\\n\", r);\n";
+ pr " return 0;\n"
+ | RBool _ ->
+ pr " if (r == -1) return -1;\n";
+ pr " if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
+ pr " return 0;\n"
+ | RConstString _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " printf (\"%%s\\n\", r);\n";
+ pr " return 0;\n"
+ | RString _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " printf (\"%%s\\n\", r);\n";
+ pr " free (r);\n";
+ pr " return 0;\n"
+ | RStringList _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " print_strings (r);\n";
+ pr " free_strings (r);\n";
+ pr " return 0;\n"
+ | RIntBool _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " printf (\"%%d, %%s\\n\", r->i,\n";
+ pr " r->b ? \"true\" : \"false\");\n";
+ pr " guestfs_free_int_bool (r);\n";
+ pr " return 0;\n"
+ | RPVList _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " print_pv_list (r);\n";
+ pr " guestfs_free_lvm_pv_list (r);\n";
+ pr " return 0;\n"
+ | RVGList _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " print_vg_list (r);\n";
+ pr " guestfs_free_lvm_vg_list (r);\n";
+ pr " return 0;\n"
+ | RLVList _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " print_lv_list (r);\n";
+ pr " guestfs_free_lvm_lv_list (r);\n";
+ pr " return 0;\n"
+ );
+ pr "}\n";
+ pr "\n"
+ ) all_functions;
+
+ (* 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
+ pr " if (";
+ pr "strcasecmp (cmd, \"%s\") == 0" name;
+ if name <> name2 then
+ pr " || strcasecmp (cmd, \"%s\") == 0" name2;
+ if name <> alias then
+ pr " || strcasecmp (cmd, \"%s\") == 0" alias;
+ pr ")\n";
+ pr " return run_%s (cmd, argc, argv);\n" name;
+ pr " else\n";
+ ) all_functions;
+ pr " {\n";
+ pr " fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
+ pr " return -1;\n";
+ pr " }\n";
+ pr " return 0;\n";
+ pr "}\n";
+ pr "\n"
+
+(* Generate the POD documentation for guestfish. *)
+and generate_fish_actions_pod () =
+ let all_functions_sorted =
+ List.filter (
+ fun (_, _, _, flags, _, _) -> not (List.mem NotInFish flags)
+ ) all_functions_sorted in
+
+ List.iter (
+ fun (name, style, _, flags, _, longdesc) ->
+ let longdesc = replace_str longdesc "C<guestfs_" "C<" in
+ let name = replace_char name '_' '-' in
+ let alias =
+ try find_map (function FishAlias n -> Some n | _ -> None) flags
+ with Not_found -> name in
+
+ pr "=head2 %s" name;
+ if name <> alias then
+ pr " | %s" alias;
+ pr "\n";
+ pr "\n";
+ pr " %s" name;
+ iter_args (
+ function
+ | String n -> pr " %s" n
+ | OptString n -> pr " %s" n
+ | Bool _ -> pr " true|false"
+ | Int n -> pr " %s" n
+ ) (snd style);
+ pr "\n";
+ pr "\n";
+ pr "%s\n\n" longdesc
+ ) all_functions_sorted