diff options
-rw-r--r-- | generator/README | 4 | ||||
-rw-r--r-- | generator/generator_actions.ml | 20 | ||||
-rw-r--r-- | generator/generator_c.ml | 256 | ||||
-rw-r--r-- | generator/generator_checks.ml | 21 | ||||
-rw-r--r-- | generator/generator_types.ml | 7 |
5 files changed, 196 insertions, 112 deletions
diff --git a/generator/README b/generator/README index f7dde116..afc58ebc 100644 --- a/generator/README +++ b/generator/README @@ -48,3 +48,7 @@ backwards compatibility. You may add another optional argument, *if* the function has >= 1 optional arguments already. Add it at the end of the list. + +You may add optional arguments to a function that doesn't have any. +However you *must* set the once_had_no_optargs flag to true, so that +the relevant backwards compatibility bindings can be added. diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index c83bf703..97b02904 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -31,7 +31,8 @@ let defaults = { name = ""; style = RErr, [], []; proc_nr = None; deprecated_by = None; optional = None; progress = false; camel_name = ""; cancellable = false; config_only = false; - c_function = ""; c_optarg_prefix = "" } + once_had_no_optargs = false; + c_name = ""; c_function = ""; c_optarg_prefix = "" } (* These test functions are used in the language binding tests. *) @@ -9296,22 +9297,29 @@ Remove C<VAR> from the environment." }; (* Some post-processing of the basic lists of actions. *) -(* Add the name of the C function that non-C language bindings should - * call. Currently this is simply guestfs_<name> or - * guestfs_<name>_argv depending on whether the function has no - * optional args or some optional args. +(* Add the name of the C function: + * c_name = short name, used by C bindings so we know what to export + * c_function = full name that non-C bindings should call + * c_optarg_prefix = prefix for optarg / bitmask names *) let non_daemon_functions, daemon_functions = let make_c_function f = match f with | { style = _, _, [] } -> { f with + c_name = f.name; c_function = "guestfs_" ^ f.name; c_optarg_prefix = "GUESTFS_" ^ String.uppercase f.name } - | { style = _, _, (_::_) } -> + | { style = _, _, (_::_); once_had_no_optargs = false } -> { f with + c_name = f.name; c_function = "guestfs_" ^ f.name ^ "_argv"; c_optarg_prefix = "GUESTFS_" ^ String.uppercase f.name } + | { style = _, _, (_::_); once_had_no_optargs = true } -> + { f with + c_name = f.name ^ "_opts"; + c_function = "guestfs_" ^ f.name ^ "_opts_argv"; + c_optarg_prefix = "GUESTFS_" ^ String.uppercase f.name ^ "_OPTS" } in let non_daemon_functions = List.map make_c_function non_daemon_functions in let daemon_functions = List.map make_c_function daemon_functions in diff --git a/generator/generator_c.ml b/generator/generator_c.ml index 6f24bee4..ebd252f2 100644 --- a/generator/generator_c.ml +++ b/generator/generator_c.ml @@ -177,14 +177,18 @@ and generate_actions_pod () = List.iter ( function | { in_docs = false } -> () - | ({ in_docs = true } as f) -> generate_actions_pod_entry f + | ({ in_docs = true; once_had_no_optargs = false } as f) -> + generate_actions_pod_entry f + | ({ in_docs = true; once_had_no_optargs = true } as f) -> + generate_actions_pod_back_compat_entry f; + generate_actions_pod_entry f ) all_functions_sorted -and generate_actions_pod_entry ({ name = shortname; +and generate_actions_pod_entry ({ c_name = c_name; style = ret, args, optargs as style } as f) = - let name = "guestfs_" ^ shortname in - pr "=head2 %s\n\n" name; - generate_prototype ~extern:false ~indent:" " ~handle:"g" name style; + pr "=head2 guestfs_%s\n\n" c_name; + generate_prototype ~extern:false ~indent:" " ~handle:"g" + ~prefix:"guestfs_" c_name style; pr "\n\n"; (match deprecation_notice ~prefix:"guestfs_" f with @@ -192,7 +196,6 @@ and generate_actions_pod_entry ({ name = shortname; | Some txt -> pr "%s\n\n" txt ); - let uc_shortname = String.uppercase shortname in if optargs <> [] then ( pr "You may supply a list of optional arguments to this call.\n"; pr "Use zero or more of the following pairs of parameters,\n"; @@ -201,8 +204,7 @@ and generate_actions_pod_entry ({ name = shortname; List.iter ( fun argt -> let n = name_of_optargt argt in - let uc_n = String.uppercase n in - pr " GUESTFS_%s_%s, " uc_shortname uc_n; + pr " GUESTFS_%s_%s, " (String.uppercase c_name) (String.uppercase n); match argt with | OBool n -> pr "int %s,\n" n | OInt n -> pr "int %s,\n" n @@ -264,29 +266,47 @@ I<The caller must free the returned buffer after use>.\n\n" pr "This function takes a key or passphrase parameter which could contain sensitive material. Read the section L</KEYS AND PASSPHRASES> for more information.\n\n"; - (match lookup_api_version name with + (match lookup_api_version ("guestfs_" ^ c_name) with | Some version -> pr "(Added in %s)\n\n" version | None -> () ); (* Handling of optional argument variants. *) if optargs <> [] then ( - pr "=head2 %s_va\n\n" name; + pr "=head2 guestfs_%s_va\n\n" c_name; generate_prototype ~extern:false ~indent:" " ~handle:"g" ~prefix:"guestfs_" ~suffix:"_va" ~optarg_proto:VA - shortname style; + c_name style; pr "\n\n"; - pr "This is the \"va_list variant\" of L</%s>.\n\n" name; + pr "This is the \"va_list variant\" of L</guestfs_%s>.\n\n" c_name; pr "See L</CALLS WITH OPTIONAL ARGUMENTS>.\n\n"; - pr "=head2 %s_argv\n\n" name; + pr "=head2 guestfs_%s_argv\n\n" c_name; generate_prototype ~extern:false ~indent:" " ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" ~optarg_proto:Argv - shortname style; + c_name style; pr "\n\n"; - pr "This is the \"argv variant\" of L</%s>.\n\n" name; + pr "This is the \"argv variant\" of L</guestfs_%s>.\n\n" c_name; pr "See L</CALLS WITH OPTIONAL ARGUMENTS>.\n\n"; ) +and generate_actions_pod_back_compat_entry { name = name; + style = ret, args, _ } = + pr "= head2 guestfs_%s\n\n" name; + generate_prototype ~extern:false ~indent:" " ~handle:"g" + ~prefix:"guestfs_" name (ret, args, []); + pr "\n\n"; + + pr "This function is provided for backwards compatibility\n"; + pr "with earlier versions of libguestfs. It simply calls\n"; + pr "L</guestfs_%s_opts> with no optional arguments.\n" name; + pr "\n"; + + (match lookup_api_version ("guestfs_" ^ name) with + | Some version -> pr "(Added in %s)\n\n" version + | None -> () + ); + pr "\n" + and generate_structs_pod () = (* Structs documentation. *) List.iter ( @@ -619,7 +639,17 @@ extern GUESTFS_DLL_PUBLIC void *guestfs_next_private (guestfs_h *g, const char * in List.iter ( - fun f -> generate_action_header f + fun ({ name = name; style = ret, args, _ } as f) -> + (* If once_had_no_optargs is set, then we need to generate a + * <name>_opts variant, plus a backwards-compatible wrapper + * called just <name> with no optargs. + *) + if f.once_had_no_optargs then ( + generate_action_header { f with style = ret, args, [] }; + generate_action_header { f with name = f.name ^ "_opts" }; + ) + else + generate_action_header f ) all_functions_sorted; pr "\ @@ -651,10 +681,10 @@ extern GUESTFS_DLL_PUBLIC int guestfs___for_each_disk (guestfs_h *g, virDomainPt and generate_internal_actions_h () = generate_header CStyle LGPLv2plus; List.iter ( - fun { name = shortname; style = style } -> + fun { c_name = c_name; style = style } -> generate_prototype ~single_line:true ~newline:true ~handle:"g" ~prefix:"guestfs__" ~optarg_proto:Argv - shortname style + c_name style ) non_daemon_functions (* Generate the client-side dispatch stubs. *) @@ -772,7 +802,7 @@ trace_send_line (guestfs_h *g) (* Generate code to check String-like parameters are not passed in * as NULL (returning an error if they are). *) - let check_null_strings shortname (ret, args, optargs) = + let check_null_strings c_name (ret, args, optargs) = let pr_newline = ref false in List.iter ( function @@ -790,17 +820,17 @@ trace_send_line (guestfs_h *g) | Pointer (_, n) -> pr " if (%s == NULL) {\n" n; pr " error (g, \"%%s: %%s: parameter cannot be NULL\",\n"; - pr " \"%s\", \"%s\");\n" shortname n; + pr " \"%s\", \"%s\");\n" c_name n; let errcode = match errcode_of_ret ret with | `CannotReturnError -> (* XXX hack *) - if shortname = "internal_test_rconstoptstring" then + if c_name = "internal_test_rconstoptstring" then `ErrorIsNULL else failwithf "%s: RConstOptString function has invalid parameter '%s'" - shortname n + c_name n | (`ErrorIsMinusOne |`ErrorIsNULL) as e -> e in pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; @@ -820,10 +850,10 @@ trace_send_line (guestfs_h *g) function | OString n -> pr " if ((optargs->bitmask & GUESTFS_%s_%s_BITMASK) &&\n" - (String.uppercase shortname) (String.uppercase n); + (String.uppercase c_name) (String.uppercase n); pr " optargs->%s == NULL) {\n" n; pr " error (g, \"%%s: %%s: optional parameter cannot be NULL\",\n"; - pr " \"%s\", \"%s\");\n" shortname n; + pr " \"%s\", \"%s\");\n" c_name n; let errcode = match errcode_of_ret ret with | `CannotReturnError -> assert false @@ -840,14 +870,14 @@ trace_send_line (guestfs_h *g) in (* Generate code to reject optargs we don't know about. *) - let reject_unknown_optargs shortname = function + let reject_unknown_optargs c_name = function | _, _, [] -> () | ret, _, optargs -> let len = List.length optargs in let mask = Int64.lognot (Int64.pred (Int64.shift_left 1L len)) in pr " if (optargs->bitmask & UINT64_C(0x%Lx)) {\n" mask; pr " error (g, \"%%s: unknown option in guestfs_%%s_argv->bitmask (this can happen if a program is compiled against a newer version of libguestfs, then dynamically linked to an older version)\",\n"; - pr " \"%s\", \"%s\");\n" shortname shortname; + pr " \"%s\", \"%s\");\n" c_name c_name; let errcode = match errcode_of_ret ret with | `CannotReturnError -> assert false @@ -858,7 +888,7 @@ trace_send_line (guestfs_h *g) in (* Generate code to generate guestfish call traces. *) - let trace_call shortname (ret, args, optargs) = + let trace_call name c_name (ret, args, optargs) = pr " if (trace_flag) {\n"; let needs_i = @@ -872,7 +902,7 @@ trace_send_line (guestfs_h *g) pr " trace_fp = trace_open (g);\n"; - pr " fprintf (trace_fp, \"%%s\", \"%s\");\n" shortname; + pr " fprintf (trace_fp, \"%%s\", \"%s\");\n" name; (* Required arguments. *) List.iter ( @@ -917,10 +947,8 @@ trace_send_line (guestfs_h *g) List.iter ( fun argt -> let n = name_of_optargt argt in - let uc_shortname = String.uppercase shortname in - let uc_n = String.uppercase n in pr " if (optargs->bitmask & GUESTFS_%s_%s_BITMASK)\n" - uc_shortname uc_n; + (String.uppercase c_name) (String.uppercase n); (match argt with | OString n -> pr " fprintf (trace_fp, \" \\\"%%s:%%s\\\"\", \"%s\", optargs->%s);\n" n n @@ -938,7 +966,7 @@ trace_send_line (guestfs_h *g) pr "\n"; in - let trace_return ?(indent = 2) shortname (ret, _, _) rv = + let trace_return ?(indent = 2) name (ret, _, _) rv = let indent = spaces indent in pr "%sif (trace_flag) {\n" indent; @@ -954,7 +982,7 @@ trace_send_line (guestfs_h *g) pr "%s trace_fp = trace_open (g);\n" indent; - pr "%s fprintf (trace_fp, \"%%s = \", \"%s\");\n" indent shortname; + pr "%s fprintf (trace_fp, \"%%s = \", \"%s\");\n" indent name; (match ret with | RErr | RInt _ | RBool _ -> @@ -993,19 +1021,19 @@ trace_send_line (guestfs_h *g) pr "\n"; in - let trace_return_error ?(indent = 2) shortname (ret, _, _) errcode = + let trace_return_error ?(indent = 2) name (ret, _, _) errcode = let indent = spaces indent in pr "%sif (trace_flag)\n" indent; pr "%s guestfs___trace (g, \"%%s = %%s (error)\",\n" indent; pr "%s \"%s\", \"%s\");\n" - indent shortname (string_of_errcode errcode) + indent name (string_of_errcode errcode) in - let handle_null_optargs optargs shortname = + let handle_null_optargs optargs c_name = if optargs <> [] then ( - pr " struct guestfs_%s_argv optargs_null;\n" shortname; + pr " struct guestfs_%s_argv optargs_null;\n" c_name; pr " if (!optargs) {\n"; pr " optargs_null.bitmask = 0;\n"; pr " optargs = &optargs_null;\n"; @@ -1014,20 +1042,20 @@ trace_send_line (guestfs_h *g) in (* For non-daemon functions, generate a wrapper around each function. *) - let generate_non_daemon_wrapper { name = shortname; + let generate_non_daemon_wrapper { name = name; c_name = c_name; style = ret, _, optargs as style; config_only = config_only } = if optargs = [] then generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs_" - shortname style + c_name style else generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" ~optarg_proto:Argv - shortname style; + c_name style; pr "{\n"; - handle_null_optargs optargs shortname; + handle_null_optargs optargs c_name; pr " int trace_flag = g->trace;\n"; pr " FILE *trace_fp;\n"; @@ -1053,27 +1081,27 @@ trace_send_line (guestfs_h *g) if config_only then ( pr " if (g->state != CONFIG) {\n"; pr " error (g, \"%%s: this function can only be called in the config state\",\n"; - pr " \"%s\");\n" shortname; + pr " \"%s\");\n" c_name; pr " return -1;\n"; pr " }\n"; ); - enter_event shortname; - check_null_strings shortname style; - reject_unknown_optargs shortname style; - trace_call shortname style; - pr " r = guestfs__%s " shortname; + enter_event name; + check_null_strings c_name style; + reject_unknown_optargs c_name style; + trace_call name c_name style; + pr " r = guestfs__%s " c_name; generate_c_call_args ~handle:"g" ~implicit_size_ptr:"size_r" style; pr ";\n"; pr "\n"; (match errcode_of_ret ret with | (`ErrorIsMinusOne | `ErrorIsNULL) as errcode -> pr " if (r != %s) {\n" (string_of_errcode errcode); - trace_return ~indent:4 shortname style "r"; + trace_return ~indent:4 name style "r"; pr " } else {\n"; - trace_return_error ~indent:4 shortname style errcode; + trace_return_error ~indent:4 name style errcode; pr " }\n"; | `CannotReturnError -> - trace_return shortname style "r"; + trace_return name style "r"; ); pr "\n"; pr " return r;\n"; @@ -1086,9 +1114,8 @@ trace_send_line (guestfs_h *g) ) non_daemon_functions; (* Client-side stubs for each function. *) - let generate_daemon_stub { name = shortname; + let generate_daemon_stub { name = name; c_name = c_name; style = ret, args, optargs as style } = - let name = "guestfs_" ^ shortname in let errcode = match errcode_of_ret ret with | `CannotReturnError -> assert false @@ -1097,19 +1124,19 @@ trace_send_line (guestfs_h *g) (* Generate the action stub. *) if optargs = [] then generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" ~prefix:"guestfs_" shortname style + ~handle:"g" ~prefix:"guestfs_" c_name style else generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs_" ~suffix:"_argv" - ~optarg_proto:Argv shortname style; + ~optarg_proto:Argv c_name style; pr "{\n"; - handle_null_optargs optargs shortname; + handle_null_optargs optargs c_name; (match args with | [] -> () - | _ -> pr " struct %s_args args;\n" name + | _ -> pr " struct guestfs_%s_args args;\n" name ); pr " guestfs_message_header hdr;\n"; @@ -1123,7 +1150,7 @@ trace_send_line (guestfs_h *g) | RBool _ | RString _ | RStringList _ | RStruct _ | RStructList _ | RHashtable _ | RBufferOut _ -> - pr " struct %s_ret ret;\n" name; + pr " struct guestfs_%s_ret ret;\n" name; true in pr " int serial;\n"; @@ -1149,10 +1176,10 @@ trace_send_line (guestfs_h *g) pr " const uint64_t progress_hint = 0;\n"; pr "\n"; - enter_event shortname; - check_null_strings shortname style; - reject_unknown_optargs shortname style; - trace_call shortname style; + enter_event name; + check_null_strings c_name style; + reject_unknown_optargs c_name style; + trace_call name c_name style; (* Calculate the total size of all FileIn arguments to pass * as a progress bar hint. @@ -1168,8 +1195,8 @@ trace_send_line (guestfs_h *g) ) args; (* This is a daemon_function so check the appliance is up. *) - pr " if (check_appliance_up (g, \"%s\") == -1) {\n" shortname; - trace_return_error ~indent:4 shortname style errcode; + pr " if (check_appliance_up (g, \"%s\") == -1) {\n" name; + trace_return_error ~indent:4 name style errcode; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr "\n"; @@ -1177,7 +1204,7 @@ trace_send_line (guestfs_h *g) (* Send the main header and arguments. *) if args = [] && optargs = [] then ( pr " serial = guestfs___send (g, GUESTFS_PROC_%s, progress_hint, 0,\n" - (String.uppercase shortname); + (String.uppercase name); pr " NULL, NULL);\n" ) else ( List.iter ( @@ -1199,9 +1226,9 @@ trace_send_line (guestfs_h *g) | BufferIn n -> pr " /* Just catch grossly large sizes. XDR encoding will make this precise. */\n"; pr " if (%s_size >= GUESTFS_MESSAGE_MAX) {\n" n; - trace_return_error ~indent:4 shortname style errcode; + trace_return_error ~indent:4 name style errcode; pr " error (g, \"%%s: size of input buffer too large\", \"%s\");\n" - shortname; + name; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr " args.%s.%s_val = (char *) %s;\n" n n n; @@ -1212,10 +1239,8 @@ trace_send_line (guestfs_h *g) List.iter ( fun argt -> let n = name_of_optargt argt in - let uc_shortname = String.uppercase shortname in - let uc_n = String.uppercase n in pr " if (optargs->bitmask & GUESTFS_%s_%s_BITMASK)\n" - uc_shortname uc_n; + (String.uppercase c_name) (String.uppercase n); (match argt with | OBool n | OInt n @@ -1231,14 +1256,14 @@ trace_send_line (guestfs_h *g) ) optargs; pr " serial = guestfs___send (g, GUESTFS_PROC_%s,\n" - (String.uppercase shortname); + (String.uppercase name); pr " progress_hint, %s,\n" (if optargs <> [] then "optargs->bitmask" else "0"); - pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n" + pr " (xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n" name; ); pr " if (serial == -1) {\n"; - trace_return_error ~indent:4 shortname style errcode; + trace_return_error ~indent:4 name style errcode; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr "\n"; @@ -1250,9 +1275,9 @@ trace_send_line (guestfs_h *g) | FileIn n -> pr " r = guestfs___send_file (g, %s);\n" n; pr " if (r == -1) {\n"; - trace_return_error ~indent:4 shortname style errcode; + trace_return_error ~indent:4 name style errcode; pr " /* daemon will send an error reply which we discard */\n"; - pr " guestfs___recv_discard (g, \"%s\");\n" shortname; + pr " guestfs___recv_discard (g, \"%s\");\n" name; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr " if (r == -2) /* daemon cancelled */\n"; @@ -1268,37 +1293,37 @@ trace_send_line (guestfs_h *g) pr " memset (&err, 0, sizeof err);\n"; if has_ret then pr " memset (&ret, 0, sizeof ret);\n"; pr "\n"; - pr " r = guestfs___recv (g, \"%s\", &hdr, &err,\n " shortname; + pr " r = guestfs___recv (g, \"%s\", &hdr, &err,\n " name; if not has_ret then pr "NULL, NULL" else - pr "(xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret" shortname; + pr "(xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret" name; pr ");\n"; pr " if (r == -1) {\n"; - trace_return_error ~indent:4 shortname style errcode; + trace_return_error ~indent:4 name style errcode; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr "\n"; pr " if (check_reply_header (g, &hdr, GUESTFS_PROC_%s, serial) == -1) {\n" - (String.uppercase shortname); - trace_return_error ~indent:4 shortname style errcode; + (String.uppercase name); + trace_return_error ~indent:4 name style errcode; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr "\n"; pr " if (hdr.status == GUESTFS_STATUS_ERROR) {\n"; - trace_return_error ~indent:4 shortname style errcode; + trace_return_error ~indent:4 name style errcode; pr " int errnum = 0;\n"; pr " if (err.errno_string[0] != '\\0')\n"; pr " errnum = guestfs___string_to_errno (err.errno_string);\n"; pr " if (errnum <= 0)\n"; pr " error (g, \"%%s: %%s\", \"%s\", err.error_message);\n" - shortname; + name; pr " else\n"; pr " guestfs_error_errno (g, errnum, \"%%s: %%s\", \"%s\",\n" - shortname; + name; pr " err.error_message);\n"; pr " free (err.error_message);\n"; pr " free (err.errno_string);\n"; @@ -1311,7 +1336,7 @@ trace_send_line (guestfs_h *g) function | FileOut n -> pr " if (guestfs___recv_file (g, %s) == -1) {\n" n; - trace_return_error ~indent:4 shortname style errcode; + trace_return_error ~indent:4 name style errcode; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr "\n"; @@ -1357,7 +1382,7 @@ trace_send_line (guestfs_h *g) pr " ret_v = p;\n"; pr " }\n"; ); - trace_return shortname style "ret_v"; + trace_return name style "ret_v"; pr " return ret_v;\n"; pr "}\n\n" in @@ -1393,13 +1418,13 @@ trace_send_line (guestfs_h *g) ) structs; - (* Functions which have optional arguments have two generated variants. *) - let generate_va_variants { name = shortname; + (* Functions which have optional arguments have two or three + * generated variants. + *) + let generate_va_variants { name = name; c_name = c_name; style = ret, args, optargs as style } = assert (optargs <> []); (* checked by caller *) - let uc_shortname = String.uppercase shortname in - (* Get the name of the last regular argument. *) let last_arg = match ret with @@ -1428,12 +1453,12 @@ trace_send_line (guestfs_h *g) (* The regular variable args function, just calls the _va variant. *) generate_prototype ~extern:false ~semicolon:false ~newline:true - ~handle:"g" ~prefix:"guestfs_" shortname style; + ~handle:"g" ~prefix:"guestfs_" c_name style; pr "{\n"; pr " va_list optargs;\n"; pr "\n"; pr " va_start (optargs, %s);\n" last_arg; - pr " %sr = guestfs_%s_va " rtype shortname; + pr " %sr = guestfs_%s_va " rtype c_name; generate_c_call_args ~handle:"g" ~implicit_size_ptr:"size_r" style; pr ";\n"; pr " va_end (optargs);\n"; @@ -1443,10 +1468,10 @@ trace_send_line (guestfs_h *g) generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs_" ~suffix:"_va" ~optarg_proto:VA - shortname style; + c_name style; pr "{\n"; - pr " struct guestfs_%s_argv optargs_s;\n" shortname; - pr " struct guestfs_%s_argv *optargs = &optargs_s;\n" shortname; + pr " struct guestfs_%s_argv optargs_s;\n" c_name; + pr " struct guestfs_%s_argv *optargs = &optargs_s;\n" c_name; pr " int i;\n"; pr "\n"; pr " optargs_s.bitmask = 0;\n"; @@ -1457,8 +1482,8 @@ trace_send_line (guestfs_h *g) List.iter ( fun argt -> let n = name_of_optargt argt in - let uc_n = String.uppercase n in - pr " case GUESTFS_%s_%s:\n" uc_shortname uc_n; + pr " case GUESTFS_%s_%s:\n" + (String.uppercase c_name) (String.uppercase n); pr " optargs_s.%s = va_arg (args, " n; (match argt with | OBool _ | OInt _ -> pr "int" @@ -1476,30 +1501,48 @@ trace_send_line (guestfs_h *g) pr " default:\n"; pr " error (g, \"%%s: unknown option %%d (this can happen if a program is compiled against a newer version of libguestfs, then dynamically linked to an older version)\",\n"; - pr " \"%s\", i);\n" shortname; + pr " \"%s\", i);\n" name; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr "\n"; pr " uint64_t i_mask = UINT64_C(1) << i;\n"; pr " if (optargs_s.bitmask & i_mask) {\n"; pr " error (g, \"%%s: same optional argument specified more than once\",\n"; - pr " \"%s\");\n" shortname; + pr " \"%s\");\n" name; pr " return %s;\n" (string_of_errcode errcode); pr " }\n"; pr " optargs_s.bitmask |= i_mask;\n"; pr " }\n"; pr "\n"; - pr " return guestfs_%s_argv " shortname; + pr " return guestfs_%s_argv " c_name; generate_c_call_args ~handle:"g" ~implicit_size_ptr:"size_r" style; pr ";\n"; pr "}\n\n" + + and generate_back_compat_wrapper { name = name; + style = ret, args, _ as style } = + generate_prototype ~extern:false ~semicolon:false ~newline:true + ~handle:"g" ~prefix:"guestfs_" + name (ret, args, []); + pr "{\n"; + pr " struct guestfs_%s_opts_argv optargs_s = { .bitmask = 0 };\n" name; + pr " struct guestfs_%s_opts_argv *optargs = &optargs_s;\n" name; + pr "\n"; + pr " return guestfs_%s_opts_argv " name; + generate_c_call_args ~handle:"g" ~implicit_size_ptr:"size_r" style; + pr ";\n"; + pr "}\n"; + pr "\n" in List.iter ( function | { style = _, _, [] } -> () - | ({ style = _, _, (_::_) } as f) -> + | ({ style = _, _, (_::_); once_had_no_optargs = false } as f) -> generate_va_variants f + | ({ style = _, _, (_::_); once_had_no_optargs = true } as f) -> + generate_va_variants f; + generate_back_compat_wrapper f ) all_functions_sorted (* Generate the linker script which controls the visibility of @@ -1545,11 +1588,18 @@ and generate_linker_script () = List.flatten ( List.map ( function - | { name = name; style = _, _, [] } -> ["guestfs_" ^ name] - | { name = name } -> + | { c_name = c_name; style = _, _, [] } -> ["guestfs_" ^ c_name] + | { c_name = c_name; style = _, _, (_::_); + once_had_no_optargs = false } -> + ["guestfs_" ^ c_name; + "guestfs_" ^ c_name ^ "_va"; + "guestfs_" ^ c_name ^ "_argv"] + | { name = name; c_name = c_name; style = _, _, (_::_); + once_had_no_optargs = true } -> ["guestfs_" ^ name; - "guestfs_" ^ name ^ "_va"; - "guestfs_" ^ name ^ "_argv"] + "guestfs_" ^ c_name; + "guestfs_" ^ c_name ^ "_va"; + "guestfs_" ^ c_name ^ "_argv"] ) all_functions ) in let structs = diff --git a/generator/generator_checks.ml b/generator/generator_checks.ml index b48a598f..b40aab42 100644 --- a/generator/generator_checks.ml +++ b/generator/generator_checks.ml @@ -40,7 +40,9 @@ let () = (* Check function names. *) List.iter ( fun { name = name } -> - if String.length name >= 7 && String.sub name 0 7 = "guestfs" then + let len = String.length name in + + if len >= 7 && String.sub name 0 7 = "guestfs" then failwithf "function name %s does not need 'guestfs' prefix" name; if name = "" then failwithf "function name is empty"; @@ -48,7 +50,14 @@ let () = failwithf "function name %s must start with lowercase a-z" name; if String.contains name '-' then failwithf "function name %s should not contain '-', use '_' instead." - name + name; +(* + (* Functions mustn't be named '_opts' since that is reserved for + * backwards compatibility functions. + *) + if len >= 5 && String.sub name (len-5) 5 = "_opts" then + failwithf "function name %s cannot end with _opts" name +*) ) (all_functions @ fish_commands); (* Check function parameter/return names. *) @@ -243,6 +252,14 @@ let () = | { config_only = false } -> () ) (daemon_functions @ fish_commands); + (* once_had_no_optargs can only apply if the function now has optargs. *) + List.iter ( + function + | { name = name; once_had_no_optargs = true; style = _, _, [] } -> + failwithf "%s cannot have once_had_no_optargs flag and no optargs" name + | { once_had_no_optargs = false } | { style = _, _, (_::_) } -> () + ) all_functions; + (* Check tests. *) List.iter ( function diff --git a/generator/generator_types.ml b/generator/generator_types.ml index bd1d759b..ce7aea3b 100644 --- a/generator/generator_types.ml +++ b/generator/generator_types.ml @@ -408,11 +408,16 @@ type action = { function *) config_only : bool; (* non-daemon-function which can only be used while in CONFIG state *) + once_had_no_optargs : bool; (* mark functions that once had no optargs + but now do, so we can generate the + required back-compat machinery *) (* "Internal" data attached by the generator at various stages. This * doesn't need to (and shouldn't) be set when defining actions. *) - c_function : string; (* name of C API function *) + c_name : string; (* shortname exposed by C API *) + c_function : string; (* full name of C API function called by + non-C bindings *) c_optarg_prefix : string; (* prefix for optarg names/bitmask names *) } |