X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fgenerator.ml;h=e2b96c846933137047822633b70c1dfdc1e43bff;hp=c9a99e1b1580b236dd062ac2d87e209ba5baecf2;hb=55b6e18f95950b1a2ec69d549c9e6c8a5758d166;hpb=55938405ccd3d5e5736f30c9bd78a51998cd7158 diff --git a/src/generator.ml b/src/generator.ml index c9a99e1..e2b96c8 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -1812,9 +1812,9 @@ See also C, C, C."); [["is_file"; "/known-1"]]); InitISOFS, Always, TestOutputFalse ( [["is_file"; "/directory"]])], - "test if file exists", + "test if a regular file", "\ -This returns C if and only if there is a file +This returns C if and only if there is a regular file with the given C name. Note that it returns false for other objects like directories. @@ -1825,7 +1825,7 @@ See also C."); [["is_dir"; "/known-3"]]); InitISOFS, Always, TestOutputTrue ( [["is_dir"; "/directory"]])], - "test if file exists", + "test if a directory", "\ This returns C if and only if there is a directory with the given C name. Note that it returns false for @@ -2338,7 +2338,7 @@ C can also be a named pipe. See also C."); - ("download", (RErr, [Dev_or_Path "remotefilename"; FileOut "filename"]), 67, [], + ("download", (RErr, [Dev_or_Path "remotefilename"; FileOut "filename"]), 67, [Progress], [InitBasicFS, Always, TestOutput ( (* Pick a file from cwd which isn't likely to change. *) [["upload"; "../COPYING.LIB"; "/COPYING.LIB"]; @@ -2698,7 +2698,7 @@ Checking or repairing NTFS volumes is not supported This command is entirely equivalent to running C."); - ("zero", (RErr, [Device "device"]), 85, [], + ("zero", (RErr, [Device "device"]), 85, [Progress], [InitBasicFS, Always, TestOutput ( [["umount"; "/dev/sda1"]; ["zero"; "/dev/sda1"]; @@ -4889,7 +4889,7 @@ or file C to another destination device or file C. Note this will fail if the source is too short or if the destination is not large enough."); - ("zero_device", (RErr, [Device "device"]), 228, [DangerWillRobinson], + ("zero_device", (RErr, [Device "device"]), 228, [DangerWillRobinson; Progress], [InitBasicFSonLVM, Always, TestRun ( [["zero_device"; "/dev/VG/LV"]])], "write zeroes to an entire device", @@ -5619,6 +5619,89 @@ type callt = | CallBool of bool | CallBuffer of string +(* Used for the guestfish -N (prepared disk images) option. + * Note that the longdescs are indented by 2 spaces. + *) +let prepopts = [ + ("disk", + "create a blank disk", + [ "size", "100M", "the size of the disk image" ], + " Create a blank disk, size 100MB (by default). + + The default size can be changed by supplying an optional parameter."); + + ("part", + "create a partitioned disk", + [ "size", "100M", "the size of the disk image"; + "partition", "mbr", "partition table type" ], + " Create a disk with a single partition. By default the size of the disk + is 100MB (the available space in the partition will be a tiny bit smaller) + and the partition table will be MBR (old DOS-style). + + These defaults can be changed by supplying optional parameters."); + + ("fs", + "create a filesystem", + [ "filesystem", "ext2", "the type of filesystem to use"; + "size", "100M", "the size of the disk image"; + "partition", "mbr", "partition table type" ], + " Create a disk with a single partition, with the partition containing + an empty filesystem. This defaults to creating a 100MB disk (the available + space in the filesystem will be a tiny bit smaller) with an MBR (old + DOS-style) partition table and an ext2 filesystem. + + These defaults can be changed by supplying optional parameters."); + + ("lv", + "create a disk with logical volume", + [ "name", "/dev/VG/LV", "the name of the VG and LV to use"; + "size", "100M", "the size of the disk image"; + "partition", "mbr", "partition table type" ], + " Create a disk with a single partition, set up the partition as an + LVM2 physical volume, and place a volume group and logical volume + on there. This defaults to creating a 100MB disk with the VG and + LV called /dev/VG/LV. You can change the name of the VG and LV + by supplying an alternate name as the first optional parameter. + + Note this does not create a filesystem. Use 'lvfs' to do that."); + + ("lvfs", + "create a disk with logical volume and filesystem", + [ "name", "/dev/VG/LV", "the name of the VG and LV to use"; + "filesystem", "ext2", "the type of filesystem to use"; + "size", "100M", "the size of the disk image"; + "partition", "mbr", "partition table type" ], + " Create a disk with a single partition, set up the partition as an + LVM2 physical volume, and place a volume group and logical volume + on there. Then format the LV with a filesystem. This defaults to + creating a 100MB disk with the VG and LV called /dev/VG/LV, with an + ext2 filesystem."); + + ("bootroot", + "create a boot and root filesystem", + [ "bootfs", "ext2", "the type of filesystem to use for boot"; + "rootfs", "ext2", "the type of filesystem to use for root"; + "size", "100M", "the size of the disk image"; + "bootsize", "32M", "the size of the boot filesystem"; + "partition", "mbr", "partition table type" ], + " Create a disk with two partitions, for boot and root filesystem. + Format the two filesystems independently. There are several optional + parameters which control the exact layout and filesystem types."); + + ("bootrootlv", + "create a boot and root filesystem using LVM", + [ "name", "/dev/VG/LV", "the name of the VG and LV for root"; + "bootfs", "ext2", "the type of filesystem to use for boot"; + "rootfs", "ext2", "the type of filesystem to use for root"; + "size", "100M", "the size of the disk image"; + "bootsize", "32M", "the size of the boot filesystem"; + "partition", "mbr", "partition table type" ], + " This is the same as 'bootroot' but the root filesystem (only) is + placed on a logical volume, named by default '/dev/VG/LV'. There are + several optional parameters which control the exact layout."); + +] + (* Used to memoize the result of pod2text. *) let pod2text_memo_filename = "src/.pod2text.data" let pod2text_memo : ((int * string * string), string list) Hashtbl.t = @@ -6451,11 +6534,21 @@ and generate_structs_h () = and generate_actions_h () = generate_header CStyle LGPLv2plus; List.iter ( - fun (shortname, style, _, _, _, _, _) -> + fun (shortname, style, _, flags, _, _, _) -> let name = "guestfs_" ^ shortname in + + let deprecated = + List.exists (function DeprecatedBy _ -> true | _ -> false) flags in + let test0 = + String.length shortname >= 5 && String.sub shortname 0 5 = "test0" in + let debug = + String.length shortname >= 5 && String.sub shortname 0 5 = "debug" in + if not deprecated && not test0 && not debug then + pr "#define LIBGUESTFS_HAVE_%s 1\n" (String.uppercase shortname); + generate_prototype ~single_line:true ~newline:true ~handle:"g" name style - ) all_functions + ) all_functions_sorted (* Generate the guestfs-internal-actions.h file. *) and generate_internal_actions_h () = @@ -6890,12 +6983,14 @@ and generate_linker_script () = "guestfs_close"; "guestfs_get_error_handler"; "guestfs_get_out_of_memory_handler"; + "guestfs_get_private"; "guestfs_last_error"; "guestfs_set_close_callback"; "guestfs_set_error_handler"; "guestfs_set_launch_done_callback"; "guestfs_set_log_message_callback"; "guestfs_set_out_of_memory_handler"; + "guestfs_set_private"; "guestfs_set_progress_callback"; "guestfs_set_subprocess_quit_callback"; @@ -8798,6 +8893,84 @@ Guestfish will prompt for these separately.\n\n"; | Some txt -> pr "%s\n\n" txt ) all_functions_sorted +and generate_fish_prep_options_h () = + generate_header CStyle GPLv2plus; + + pr "#ifndef PREPOPTS_H\n"; + pr "\n"; + + pr "\ +struct prep { + const char *name; /* eg. \"fs\" */ + + size_t nr_params; /* optional parameters */ + struct prep_param *params; + + const char *shortdesc; /* short description */ + const char *longdesc; /* long description */ + + /* functions to implement it */ + void (*prelaunch) (const char *filename, prep_data *); + void (*postlaunch) (const char *filename, prep_data *, const char *device); +}; + +struct prep_param { + const char *pname; /* parameter name */ + const char *pdefault; /* parameter default */ + const char *pdesc; /* parameter description */ +}; + +extern const struct prep preps[]; +#define NR_PREPS %d + +" (List.length prepopts); + + List.iter ( + fun (name, shortdesc, args, longdesc) -> + pr "\ +extern void prep_prelaunch_%s (const char *filename, prep_data *data); +extern void prep_postlaunch_%s (const char *filename, prep_data *data, const char *device); + +" name name; + ) prepopts; + + pr "\n"; + pr "#endif /* PREPOPTS_H */\n" + +and generate_fish_prep_options_c () = + generate_header CStyle GPLv2plus; + + pr "\ +#include \"fish.h\" +#include \"prepopts.h\" + +"; + + List.iter ( + fun (name, shortdesc, args, longdesc) -> + pr "static struct prep_param %s_args[] = {\n" name; + List.iter ( + fun (n, default, desc) -> + pr " { \"%s\", \"%s\", \"%s\" },\n" n default desc + ) args; + pr "};\n"; + pr "\n"; + ) prepopts; + + pr "const struct prep preps[] = {\n"; + List.iter ( + fun (name, shortdesc, args, longdesc) -> + pr " { \"%s\", %d, %s_args, + \"%s\", + \"%s\", + prep_prelaunch_%s, prep_postlaunch_%s }, +" + name (List.length args) name + (c_quote shortdesc) (c_quote longdesc) + name name; + ) prepopts; + pr "};\n" + (* Generate a C function prototype. *) and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true) ?(single_line = false) ?(newline = false) ?(in_daemon = false) @@ -8928,6 +9101,28 @@ val close : t -> unit unreferenced, but callers can call this in order to provide predictable cleanup. *) +type progress_cb = int -> int -> int64 -> int64 -> unit + +val set_progress_callback : t -> progress_cb -> unit +(** [set_progress_callback g f] sets [f] as the progress callback function. + For some long-running functions, [f] will be called repeatedly + during the function with progress updates. + + The callback is [f proc_nr serial position total]. See + the description of [guestfs_set_progress_callback] in guestfs(3) + for the meaning of these four numbers. + + Note that if the closure captures a reference to the handle, + this reference will prevent the handle from being + automatically closed by the garbage collector. There are + three ways to avoid this: be careful not to capture the handle + in the closure, or use a weak reference, or call + {!Guestfs.clear_progress_callback} to remove the reference. *) + +val clear_progress_callback : t -> unit +(** [clear_progress_callback g] removes any progress callback function + associated with the handle. See {!Guestfs.set_progress_callback}. *) + "; generate_ocaml_structure_decls (); @@ -8952,6 +9147,13 @@ exception Handle_closed of string external create : unit -> t = \"ocaml_guestfs_create\" external close : t -> unit = \"ocaml_guestfs_close\" +type progress_cb = int -> int -> int64 -> int64 -> unit + +external set_progress_callback : t -> progress_cb -> unit + = \"ocaml_guestfs_set_progress_callback\" +external clear_progress_callback : t -> unit + = \"ocaml_guestfs_clear_progress_callback\" + (* Give the exceptions names, so they can be raised from the C code. *) let () = Callback.register_exception \"ocaml_guestfs_error\" (Error \"\"); @@ -9399,6 +9601,46 @@ XS_unpack_charPtrPtr (SV *arg) { return ret; } +#define PROGRESS_KEY \"_perl_progress_cb\" + +static void +_clear_progress_callback (guestfs_h *g) +{ + guestfs_set_progress_callback (g, NULL, NULL); + SV *cb = guestfs_get_private (g, PROGRESS_KEY); + if (cb) { + guestfs_set_private (g, PROGRESS_KEY, NULL); + SvREFCNT_dec (cb); + } +} + +/* http://www.perlmonks.org/?node=338857 */ +static void +_progress_callback (guestfs_h *g, void *cb, + int proc_nr, int serial, uint64_t position, uint64_t total) +{ + dSP; + ENTER; + SAVETMPS; + PUSHMARK (SP); + XPUSHs (sv_2mortal (newSViv (proc_nr))); + XPUSHs (sv_2mortal (newSViv (serial))); + XPUSHs (sv_2mortal (my_newSVull (position))); + XPUSHs (sv_2mortal (my_newSVull (total))); + PUTBACK; + call_sv ((SV *) cb, G_VOID | G_DISCARD | G_EVAL); + FREETMPS; + LEAVE; +} + +static void +_close_handle (guestfs_h *g) +{ + assert (g != NULL); + _clear_progress_callback (g); + guestfs_close (g); +} + MODULE = Sys::Guestfs PACKAGE = Sys::Guestfs PROTOTYPES: ENABLE @@ -9426,19 +9668,34 @@ DESTROY (sv) SV **svp = hv_fetch (hv, \"_g\", 2, 0); if (svp != NULL) { guestfs_h *g = (guestfs_h *) SvIV (*svp); - assert (g != NULL); - guestfs_close (g); + _close_handle (g); } void close (g) guestfs_h *g; PPCODE: - guestfs_close (g); + _close_handle (g); /* Avoid double-free in DESTROY method. */ HV *hv = (HV *) SvRV (ST(0)); (void) hv_delete (hv, \"_g\", 2, G_DISCARD); +void +set_progress_callback (g, cb) + guestfs_h *g; + SV *cb; + PPCODE: + _clear_progress_callback (g); + SvREFCNT_inc (cb); + guestfs_set_private (g, PROGRESS_KEY, cb); + guestfs_set_progress_callback (g, _progress_callback, cb); + +void +clear_progress_callback (g) + guestfs_h *g; + PPCODE: + _clear_progress_callback (g); + "; List.iter ( @@ -9812,6 +10069,25 @@ C the program must not call any method (including C) on the handle (but the implicit call to C that happens when the final reference is cleaned up is OK). +=item $h->set_progress_callback (\\&cb); + +Set the progress notification callback for this handle +to the Perl closure C. + +C will be called whenever a long-running operation +generates a progress notification message. The 4 parameters +to the function are: C, C, C +and C. + +You should carefully read the documentation for +L before using +this function. + +=item $h->clear_progress_callback (); + +This removes any progress callback function associated with +the handle. + =cut " max_proc_nr; @@ -9845,6 +10121,55 @@ when the final reference is cleaned up is OK). =back +=head1 AVAILABILITY + +From time to time we add new libguestfs APIs. Also some libguestfs +APIs won't be available in all builds of libguestfs (the Fedora +build is full-featured, but other builds may disable features). +How do you test whether the APIs that your Perl program needs are +available in the version of C that you are using? + +To test if a particular function is available in the C +class, use the ordinary Perl UNIVERSAL method C +(see L). For example: + + use Sys::Guestfs; + if (defined (Sys::Guestfs->can (\"set_verbose\"))) { + print \"\\$h->set_verbose is available\\n\"; + } + +To test if particular features are supported by the current +build, use the L method like the example below. Note +that the appliance must be launched first. + + $h->available ( [\"augeas\"] ); + +Since the L method croaks if the feature is not supported, +you might also want to wrap this in an eval and return a boolean. +In fact this has already been done for you: use +L. + +For further discussion on this topic, refer to +L. + +=head1 STORING DATA IN THE HANDLE + +The handle returned from L is a hash reference. The hash +normally contains a single element: + + { + _g => [private data used by libguestfs] + } + +Callers can add other elements to this hash to store data for their own +purposes. The data lasts for the lifetime of the handle. + +Any fields whose names begin with an underscore are reserved +for private use by libguestfs. We may add more in future. + +It is recommended that callers prefix the name of their field(s) +with some unique string, to avoid conflicts with other users. + =head1 COPYRIGHT Copyright (C) %s Red Hat Inc. @@ -10681,6 +11006,10 @@ void Init__guestfs () c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject); e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError); +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func (c_guestfs, ruby_guestfs_create); +#endif + rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0); rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0); @@ -11810,6 +12139,431 @@ namespace Guestfs } " +and generate_php_h () = + generate_header CStyle LGPLv2plus; + + pr "\ +#ifndef PHP_GUESTFS_PHP_H +#define PHP_GUESTFS_PHP_H 1 + +#ifdef ZTS +#include \"TSRM.h\" +#endif + +#define PHP_GUESTFS_PHP_EXTNAME \"guestfs_php\" +#define PHP_GUESTFS_PHP_VERSION \"1.0\" + +PHP_MINIT_FUNCTION (guestfs_php); + +#define PHP_GUESTFS_HANDLE_RES_NAME \"guestfs_h\" + +PHP_FUNCTION (guestfs_create); +PHP_FUNCTION (guestfs_last_error); +"; + + List.iter ( + fun (shortname, style, _, _, _, _, _) -> + pr "PHP_FUNCTION (guestfs_%s);\n" shortname + ) all_functions_sorted; + + pr "\ + +extern zend_module_entry guestfs_php_module_entry; +#define phpext_guestfs_php_ptr &guestfs_php_module_entry + +#endif /* PHP_GUESTFS_PHP_H */ +" + +and generate_php_c () = + generate_header CStyle LGPLv2plus; + + pr "\ +/* NOTE: Be very careful with all macros in PHP header files. The + * morons who wrote them aren't good at making them safe for inclusion + * in arbitrary places in C code, eg. not using 'do ... while(0)' + * or parenthesizing any of the arguments. + */ + +/* NOTE (2): Some parts of the API can't be used on 32 bit platforms. + * Any 64 bit numbers will be truncated. There's no easy way around + * this in PHP. + */ + +#include + +#include +#include + +#include +#include + +#include \"guestfs.h\" + +static int res_guestfs_h; + +static void +guestfs_php_handle_dtor (zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + guestfs_h *g = (guestfs_h *) rsrc->ptr; + if (g != NULL) + guestfs_close (g); +} + +PHP_MINIT_FUNCTION (guestfs_php) +{ + res_guestfs_h = + zend_register_list_destructors_ex (guestfs_php_handle_dtor, + NULL, PHP_GUESTFS_HANDLE_RES_NAME, module_number); +} + +static function_entry guestfs_php_functions[] = { + PHP_FE (guestfs_create, NULL) + PHP_FE (guestfs_last_error, NULL) +"; + + List.iter ( + fun (shortname, style, _, _, _, _, _) -> + pr " PHP_FE (guestfs_%s, NULL)\n" shortname + ) all_functions_sorted; + + pr " { NULL, NULL, NULL } +}; + +zend_module_entry guestfs_php_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + PHP_GUESTFS_PHP_EXTNAME, + guestfs_php_functions, + PHP_MINIT (guestfs_php), + NULL, + NULL, + NULL, + NULL, +#if ZEND_MODULE_API_NO >= 20010901 + PHP_GUESTFS_PHP_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_GUESTFS_PHP +ZEND_GET_MODULE (guestfs_php) +#endif + +PHP_FUNCTION (guestfs_create) +{ + guestfs_h *g = guestfs_create (); + if (g == NULL) { + RETURN_FALSE; + } + + guestfs_set_error_handler (g, NULL, NULL); + + ZEND_REGISTER_RESOURCE (return_value, g, res_guestfs_h); +} + +PHP_FUNCTION (guestfs_last_error) +{ + zval *z_g; + guestfs_h *g; + + if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r\", + &z_g) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME, + res_guestfs_h); + if (g == NULL) { + RETURN_FALSE; + } + + const char *err = guestfs_last_error (g); + if (err) { + RETURN_STRING (err, 1); + } else { + RETURN_NULL (); + } +} + +"; + + (* Now generate the PHP bindings for each action. *) + List.iter ( + fun (shortname, style, _, _, _, _, _) -> + pr "PHP_FUNCTION (guestfs_%s)\n" shortname; + pr "{\n"; + pr " zval *z_g;\n"; + pr " guestfs_h *g;\n"; + + List.iter ( + function + | String n | Device n | Pathname n | Dev_or_Path n + | FileIn n | FileOut n | Key n + | OptString n + | BufferIn n -> + pr " char *%s;\n" n; + pr " int %s_size;\n" n + | StringList n + | DeviceList n -> + pr " zval *z_%s;\n" n; + pr " char **%s;\n" n; + | Bool n -> + pr " zend_bool %s;\n" n + | Int n | Int64 n -> + pr " long %s;\n" n + ) (snd style); + + pr "\n"; + + (* Parse the parameters. *) + let param_string = String.concat "" ( + List.map ( + function + | String n | Device n | Pathname n | Dev_or_Path n + | FileIn n | FileOut n | BufferIn n | Key n -> "s" + | OptString n -> "s!" + | StringList n | DeviceList n -> "a" + | Bool n -> "b" + | Int n | Int64 n -> "l" + ) (snd style) + ) in + + pr " if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r%s\",\n" + param_string; + pr " &z_g"; + List.iter ( + function + | String n | Device n | Pathname n | Dev_or_Path n + | FileIn n | FileOut n | BufferIn n | Key n + | OptString n -> + pr ", &%s, &%s_size" n n + | StringList n | DeviceList n -> + pr ", &z_%s" n + | Bool n -> + pr ", &%s" n + | Int n | Int64 n -> + pr ", &%s" n + ) (snd style); + pr ") == FAILURE) {\n"; + pr " RETURN_FALSE;\n"; + pr " }\n"; + pr "\n"; + pr " ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME,\n"; + pr " res_guestfs_h);\n"; + pr " if (g == NULL) {\n"; + pr " RETURN_FALSE;\n"; + pr " }\n"; + pr "\n"; + + List.iter ( + function + | String n | Device n | Pathname n | Dev_or_Path n + | FileIn n | FileOut n | Key n + | OptString n -> + (* Just need to check the string doesn't contain any ASCII + * NUL characters, which won't be supported by the C API. + *) + pr " if (strlen (%s) != %s_size) {\n" n n; + pr " fprintf (stderr, \"libguestfs: %s: parameter '%s' contains embedded ASCII NUL.\\n\");\n" shortname n; + pr " RETURN_FALSE;\n"; + pr " }\n"; + pr "\n" + | BufferIn n -> () + | StringList n + | DeviceList n -> + (* Convert array to list of strings. + * http://marc.info/?l=pecl-dev&m=112205192100631&w=2 + *) + pr " {\n"; + pr " HashTable *a;\n"; + pr " int n;\n"; + pr " HashPosition p;\n"; + pr " zval **d;\n"; + pr " size_t c = 0;\n"; + pr "\n"; + pr " a = Z_ARRVAL_P (z_%s);\n" n; + pr " n = zend_hash_num_elements (a);\n"; + pr " %s = safe_emalloc (n + 1, sizeof (char *), 0);\n" n; + pr " for (zend_hash_internal_pointer_reset_ex (a, &p);\n"; + pr " zend_hash_get_current_data_ex (a, (void **) &d, &p) == SUCCESS;\n"; + pr " zend_hash_move_forward_ex (a, &p)) {\n"; + pr " zval t = **d;\n"; + pr " zval_copy_ctor (&t);\n"; + pr " convert_to_string (&t);\n"; + pr " %s[c] = Z_STRVAL (t);\n" n; + pr " c++;\n"; + pr " }\n"; + pr " %s[c] = NULL;\n" n; + pr " }\n"; + pr "\n" + | Bool n | Int n | Int64 n -> () + ) (snd style); + + (* Return value. *) + let error_code = + match fst style with + | RErr -> pr " int r;\n"; "-1" + | RBool _ + | RInt _ -> pr " int r;\n"; "-1" + | RInt64 _ -> pr " int64_t r;\n"; "-1" + | RConstString _ -> pr " const char *r;\n"; "NULL" + | RConstOptString _ -> pr " const char *r;\n"; "NULL" + | RString _ -> + pr " char *r;\n"; "NULL" + | RStringList _ -> + pr " char **r;\n"; "NULL" + | RStruct (_, typ) -> + pr " struct guestfs_%s *r;\n" typ; "NULL" + | RStructList (_, typ) -> + pr " struct guestfs_%s_list *r;\n" typ; "NULL" + | RHashtable _ -> + pr " char **r;\n"; "NULL" + | RBufferOut _ -> + pr " char *r;\n"; + pr " size_t size;\n"; + "NULL" in + + (* Call the function. *) + pr " r = guestfs_%s " shortname; + generate_c_call_args ~handle:"g" style; + pr ";\n"; + pr "\n"; + + (* Free up parameters. *) + List.iter ( + function + | String n | Device n | Pathname n | Dev_or_Path n + | FileIn n | FileOut n | Key n + | OptString n -> () + | BufferIn n -> () + | StringList n + | DeviceList n -> + pr " {\n"; + pr " size_t c = 0;\n"; + pr "\n"; + pr " for (c = 0; %s[c] != NULL; ++c)\n" n; + pr " efree (%s[c]);\n" n; + pr " efree (%s);\n" n; + pr " }\n"; + pr "\n" + | Bool n | Int n | Int64 n -> () + ) (snd style); + + (* Check for errors. *) + pr " if (r == %s) {\n" error_code; + pr " RETURN_FALSE;\n"; + pr " }\n"; + pr "\n"; + + (* Convert the return value. *) + (match fst style with + | RErr -> + pr " RETURN_TRUE;\n" + | RBool _ -> + pr " RETURN_BOOL (r);\n" + | RInt _ -> + pr " RETURN_LONG (r);\n" + | RInt64 _ -> + pr " RETURN_LONG (r);\n" + | RConstString _ -> + pr " RETURN_STRING (r, 1);\n" + | RConstOptString _ -> + pr " if (r) { RETURN_STRING (r, 1); }\n"; + pr " else { RETURN_NULL (); }\n" + | RString _ -> + pr " char *r_copy = estrdup (r);\n"; + pr " free (r);\n"; + pr " RETURN_STRING (r_copy, 0);\n" + | RBufferOut _ -> + pr " char *r_copy = estrndup (r, size);\n"; + pr " free (r);\n"; + pr " RETURN_STRING (r_copy, 0);\n" + | RStringList _ -> + pr " size_t c = 0;\n"; + pr " array_init (return_value);\n"; + pr " for (c = 0; r[c] != NULL; ++c) {\n"; + pr " add_next_index_string (return_value, r[c], 1);\n"; + pr " free (r[c]);\n"; + pr " }\n"; + pr " free (r);\n"; + | RHashtable _ -> + pr " size_t c = 0;\n"; + pr " array_init (return_value);\n"; + pr " for (c = 0; r[c] != NULL; c += 2) {\n"; + pr " add_assoc_string (return_value, r[c], r[c+1], 1);\n"; + pr " free (r[c]);\n"; + pr " free (r[c+1]);\n"; + pr " }\n"; + pr " free (r);\n"; + | RStruct (_, typ) -> + let cols = cols_of_struct typ in + generate_php_struct_code typ cols + | RStructList (_, typ) -> + let cols = cols_of_struct typ in + generate_php_struct_list_code typ cols + ); + + pr "}\n"; + pr "\n" + ) all_functions_sorted + +and generate_php_struct_code typ cols = + pr " array_init (return_value);\n"; + List.iter ( + function + | name, FString -> + pr " add_assoc_string (return_value, \"%s\", r->%s, 1);\n" name name + | name, FBuffer -> + pr " add_assoc_stringl (return_value, \"%s\", r->%s, r->%s_len, 1);\n" + name name name + | name, FUUID -> + pr " add_assoc_stringl (return_value, \"%s\", r->%s, 32, 1);\n" + name name + | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) -> + pr " add_assoc_long (return_value, \"%s\", r->%s);\n" + name name + | name, FChar -> + pr " add_assoc_stringl (return_value, \"%s\", &r->%s, 1, 1);\n" + name name + | name, FOptPercent -> + pr " add_assoc_double (return_value, \"%s\", r->%s);\n" + name name + ) cols; + pr " guestfs_free_%s (r);\n" typ + +and generate_php_struct_list_code typ cols = + pr " array_init (return_value);\n"; + pr " size_t c = 0;\n"; + pr " for (c = 0; c < r->len; ++c) {\n"; + pr " zval *z_elem;\n"; + pr " ALLOC_INIT_ZVAL (z_elem);\n"; + pr " array_init (z_elem);\n"; + List.iter ( + function + | name, FString -> + pr " add_assoc_string (z_elem, \"%s\", r->val[c].%s, 1);\n" + name name + | name, FBuffer -> + pr " add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, r->val[c].%s_len, 1);\n" + name name name + | name, FUUID -> + pr " add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, 32, 1);\n" + name name + | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) -> + pr " add_assoc_long (z_elem, \"%s\", r->val[c].%s);\n" + name name + | name, FChar -> + pr " add_assoc_stringl (z_elem, \"%s\", &r->val[c].%s, 1, 1);\n" + name name + | name, FOptPercent -> + pr " add_assoc_double (z_elem, \"%s\", r->val[c].%s);\n" + name name + ) cols; + pr " add_next_index_zval (return_value, z_elem);\n"; + pr " }\n"; + pr " guestfs_free_%s_list (r);\n" typ + and generate_bindtests () = generate_header CStyle LGPLv2plus; @@ -12322,6 +13076,8 @@ Run it from the top source directory using the command output_to "fish/cmds.c" generate_fish_cmds; output_to "fish/completion.c" generate_fish_completion; output_to "fish/guestfish-actions.pod" generate_fish_actions_pod; + output_to "fish/prepopts.c" generate_fish_prep_options_c; + output_to "fish/prepopts.h" generate_fish_prep_options_h; output_to "ocaml/guestfs.mli" generate_ocaml_mli; output_to "ocaml/guestfs.ml" generate_ocaml_ml; output_to "ocaml/guestfs_c_actions.c" generate_ocaml_c; @@ -12349,6 +13105,8 @@ Run it from the top source directory using the command output_to "haskell/Guestfs.hs" generate_haskell_hs; output_to "haskell/Bindtests.hs" generate_haskell_bindtests; output_to "csharp/Libguestfs.cs" generate_csharp; + output_to "php/extension/php_guestfs_php.h" generate_php_h; + output_to "php/extension/guestfs_php.c" generate_php_c; (* Always generate this file last, and unconditionally. It's used * by the Makefile to know when we must re-run the generator.