configure: Add --with-extra flag to allow setting the extra version string.
[libguestfs.git] / generator / generator_php.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_c
31
32 let rec generate_php_h () =
33   generate_header CStyle LGPLv2plus;
34
35   pr "\
36 #ifndef PHP_GUESTFS_PHP_H
37 #define PHP_GUESTFS_PHP_H 1
38
39 #ifdef ZTS
40 #include \"TSRM.h\"
41 #endif
42
43 #define PHP_GUESTFS_PHP_EXTNAME \"guestfs_php\"
44 #define PHP_GUESTFS_PHP_VERSION \"1.0\"
45
46 PHP_MINIT_FUNCTION (guestfs_php);
47
48 #define PHP_GUESTFS_HANDLE_RES_NAME \"guestfs_h\"
49
50 PHP_FUNCTION (guestfs_create);
51 PHP_FUNCTION (guestfs_last_error);
52 ";
53
54   List.iter (
55     fun (shortname, _, _, _, _, _, _) ->
56       pr "PHP_FUNCTION (guestfs_%s);\n" shortname
57   ) all_functions_sorted;
58
59   pr "\
60
61 extern zend_module_entry guestfs_php_module_entry;
62 #define phpext_guestfs_php_ptr &guestfs_php_module_entry
63
64 #endif /* PHP_GUESTFS_PHP_H */
65 "
66
67 and generate_php_c () =
68   generate_header CStyle LGPLv2plus;
69
70   pr "\
71 /* NOTE: Be very careful with all macros in PHP header files.  The
72  * morons who wrote them aren't good at making them safe for inclusion
73  * in arbitrary places in C code, eg. not using 'do ... while(0)'
74  * or parenthesizing any of the arguments.
75  */
76
77 /* NOTE (2): Some parts of the API can't be used on 32 bit platforms.
78  * Any 64 bit numbers will be truncated.  There's no easy way around
79  * this in PHP.
80  */
81
82 #include <config.h>
83
84 #include <stdio.h>
85 #include <stdlib.h>
86
87 #include <php.h>
88 #include <php_guestfs_php.h>
89
90 #include \"guestfs.h\"
91
92 static int res_guestfs_h;
93
94 static void
95 guestfs_php_handle_dtor (zend_rsrc_list_entry *rsrc TSRMLS_DC)
96 {
97   guestfs_h *g = (guestfs_h *) rsrc->ptr;
98   if (g != NULL)
99     guestfs_close (g);
100 }
101
102 PHP_MINIT_FUNCTION (guestfs_php)
103 {
104   res_guestfs_h =
105     zend_register_list_destructors_ex (guestfs_php_handle_dtor,
106     NULL, PHP_GUESTFS_HANDLE_RES_NAME, module_number);
107 }
108
109 static function_entry guestfs_php_functions[] = {
110   PHP_FE (guestfs_create, NULL)
111   PHP_FE (guestfs_last_error, NULL)
112 ";
113
114   List.iter (
115     fun (shortname, _, _, _, _, _, _) ->
116       pr "  PHP_FE (guestfs_%s, NULL)\n" shortname
117   ) all_functions_sorted;
118
119   pr "  { NULL, NULL, NULL }
120 };
121
122 zend_module_entry guestfs_php_module_entry = {
123 #if ZEND_MODULE_API_NO >= 20010901
124   STANDARD_MODULE_HEADER,
125 #endif
126   PHP_GUESTFS_PHP_EXTNAME,
127   guestfs_php_functions,
128   PHP_MINIT (guestfs_php),
129   NULL,
130   NULL,
131   NULL,
132   NULL,
133 #if ZEND_MODULE_API_NO >= 20010901
134   PHP_GUESTFS_PHP_VERSION,
135 #endif
136   STANDARD_MODULE_PROPERTIES
137 };
138
139 #ifdef COMPILE_DL_GUESTFS_PHP
140 ZEND_GET_MODULE (guestfs_php)
141 #endif
142
143 PHP_FUNCTION (guestfs_create)
144 {
145   guestfs_h *g = guestfs_create ();
146   if (g == NULL) {
147     RETURN_FALSE;
148   }
149
150   guestfs_set_error_handler (g, NULL, NULL);
151
152   ZEND_REGISTER_RESOURCE (return_value, g, res_guestfs_h);
153 }
154
155 PHP_FUNCTION (guestfs_last_error)
156 {
157   zval *z_g;
158   guestfs_h *g;
159
160   if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r\",
161                              &z_g) == FAILURE) {
162     RETURN_FALSE;
163   }
164
165   ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME,
166                        res_guestfs_h);
167   if (g == NULL) {
168     RETURN_FALSE;
169   }
170
171   const char *err = guestfs_last_error (g);
172   if (err) {
173     RETURN_STRING (err, 1);
174   } else {
175     RETURN_NULL ();
176   }
177 }
178
179 ";
180
181   (* Now generate the PHP bindings for each action. *)
182   List.iter (
183     fun (shortname, (ret, args, optargs as style), _, _, _, _, _) ->
184       pr "PHP_FUNCTION (guestfs_%s)\n" shortname;
185       pr "{\n";
186       pr "  zval *z_g;\n";
187       pr "  guestfs_h *g;\n";
188
189       List.iter (
190         function
191         | String n | Device n | Pathname n | Dev_or_Path n
192         | FileIn n | FileOut n | Key n
193         | OptString n
194         | BufferIn n ->
195             pr "  char *%s;\n" n;
196             pr "  int %s_size;\n" n
197         | StringList n
198         | DeviceList n ->
199             pr "  zval *z_%s;\n" n;
200             pr "  char **%s;\n" n;
201         | Bool n ->
202             pr "  zend_bool %s;\n" n
203         | Int n | Int64 n | Pointer (_, n) ->
204             pr "  long %s;\n" n
205         ) args;
206
207       if optargs <> [] then (
208         pr "  struct guestfs_%s_argv optargs_s = { .bitmask = 0 };\n" shortname;
209         pr "  struct guestfs_%s_argv *optargs = &optargs_s;\n" shortname;
210
211         (* XXX Ugh PHP doesn't have proper optional arguments, so we
212          * have to use sentinel values.
213          *)
214         (* Since we don't know if PHP types will exactly match structure
215          * types, declare some local variables here.
216          *)
217         List.iter (
218           function
219           | Bool n -> pr "  zend_bool optargs_t_%s = -1;\n" n
220           | Int n | Int64 n -> pr "  long optargs_t_%s = -1;\n" n
221           | String n ->
222               pr "  char *optargs_t_%s = NULL;\n" n;
223               pr "  int optargs_t_%s_size = -1;\n" n
224           | _ -> assert false
225         ) optargs
226       );
227
228       pr "\n";
229
230       (* Parse the parameters. *)
231       let param_string = String.concat "" (
232         List.map (
233           function
234           | String n | Device n | Pathname n | Dev_or_Path n
235           | FileIn n | FileOut n | BufferIn n | Key n -> "s"
236           | OptString n -> "s!"
237           | StringList n | DeviceList n -> "a"
238           | Bool n -> "b"
239           | Int n | Int64 n | Pointer (_, n) -> "l"
240         ) args
241       ) in
242
243       let param_string =
244         if optargs <> [] then
245           param_string ^ "|" ^
246             String.concat "" (
247               List.map (
248                 function
249                 | Bool _ -> "b"
250                 | Int _ | Int64 _ -> "l"
251                 | String _ -> "s"
252                 | _ -> assert false
253               ) optargs
254             )
255         else param_string in
256
257       pr "  if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r%s\",\n"
258         param_string;
259       pr "        &z_g";
260       List.iter (
261         function
262         | String n | Device n | Pathname n | Dev_or_Path n
263         | FileIn n | FileOut n | BufferIn n | Key n
264         | OptString n ->
265             pr ", &%s, &%s_size" n n
266         | StringList n | DeviceList n ->
267             pr ", &z_%s" n
268         | Bool n ->
269             pr ", &%s" n
270         | Int n | Int64 n | Pointer (_, n) ->
271             pr ", &%s" n
272       ) args;
273       List.iter (
274         function
275         | Bool n | Int n | Int64 n ->
276             pr ", &optargs_t_%s" n
277         | String n ->
278             pr ", &optargs_t_%s, &optargs_t_%s_size" n n
279         | _ -> assert false
280       ) optargs;
281       pr ") == FAILURE) {\n";
282       pr "    RETURN_FALSE;\n";
283       pr "  }\n";
284       pr "\n";
285       pr "  ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME,\n";
286       pr "                       res_guestfs_h);\n";
287       pr "  if (g == NULL) {\n";
288       pr "    RETURN_FALSE;\n";
289       pr "  }\n";
290       pr "\n";
291
292       List.iter (
293         function
294         | String n | Device n | Pathname n | Dev_or_Path n
295         | FileIn n | FileOut n | Key n
296         | OptString n ->
297             (* Just need to check the string doesn't contain any ASCII
298              * NUL characters, which won't be supported by the C API.
299              *)
300             pr "  if (strlen (%s) != %s_size) {\n" n n;
301             pr "    fprintf (stderr, \"libguestfs: %s: parameter '%s' contains embedded ASCII NUL.\\n\");\n" shortname n;
302             pr "    RETURN_FALSE;\n";
303             pr "  }\n";
304             pr "\n"
305         | BufferIn n -> ()
306         | StringList n
307         | DeviceList n ->
308             (* Convert array to list of strings.
309              * http://marc.info/?l=pecl-dev&m=112205192100631&w=2
310              *)
311             pr "  {\n";
312             pr "    HashTable *a;\n";
313             pr "    int n;\n";
314             pr "    HashPosition p;\n";
315             pr "    zval **d;\n";
316             pr "    size_t c = 0;\n";
317             pr "\n";
318             pr "    a = Z_ARRVAL_P (z_%s);\n" n;
319             pr "    n = zend_hash_num_elements (a);\n";
320             pr "    %s = safe_emalloc (n + 1, sizeof (char *), 0);\n" n;
321             pr "    for (zend_hash_internal_pointer_reset_ex (a, &p);\n";
322             pr "         zend_hash_get_current_data_ex (a, (void **) &d, &p) == SUCCESS;\n";
323             pr "         zend_hash_move_forward_ex (a, &p)) {\n";
324             pr "      zval t = **d;\n";
325             pr "      zval_copy_ctor (&t);\n";
326             pr "      convert_to_string (&t);\n";
327             pr "      %s[c] = Z_STRVAL (t);\n" n;
328             pr "      c++;\n";
329             pr "    }\n";
330             pr "    %s[c] = NULL;\n" n;
331             pr "  }\n";
332             pr "\n"
333         | Bool _ | Int _ | Int64 _ | Pointer _ -> ()
334         ) args;
335
336       (* Optional arguments. *)
337       if optargs <> [] then (
338         let uc_shortname = String.uppercase shortname in
339         List.iter (
340           fun argt ->
341             let n = name_of_argt argt in
342             let uc_n = String.uppercase n in
343             pr "  if (optargs_t_%s != " n;
344             (match argt with
345              | Bool _ -> pr "((zend_bool)-1)"
346              | Int _ | Int64 _ -> pr "-1"
347              | String _ -> pr "NULL"
348              | _ -> assert false
349             );
350             pr ") {\n";
351             pr "    optargs_s.%s = optargs_t_%s;\n" n n;
352             pr "    optargs_s.bitmask |= GUESTFS_%s_%s_BITMASK;\n"
353               uc_shortname uc_n;
354             pr "  }\n"
355         ) optargs;
356         pr "\n"
357       );
358
359       (* Return value. *)
360       (match ret with
361        | RErr -> pr "  int r;\n"
362        | RBool _
363        | RInt _ -> pr "  int r;\n"
364        | RInt64 _ -> pr "  int64_t r;\n"
365        | RConstString _ -> pr "  const char *r;\n"
366        | RConstOptString _ -> pr "  const char *r;\n"
367        | RString _ ->
368            pr "  char *r;\n"
369        | RStringList _ ->
370            pr "  char **r;\n"
371        | RStruct (_, typ) ->
372            pr "  struct guestfs_%s *r;\n" typ
373        | RStructList (_, typ) ->
374            pr "  struct guestfs_%s_list *r;\n" typ
375        | RHashtable _ ->
376            pr "  char **r;\n"
377        | RBufferOut _ ->
378            pr "  char *r;\n";
379            pr "  size_t size;\n"
380       );
381
382       (* Call the function. *)
383       if optargs = [] then
384         pr "  r = guestfs_%s " shortname
385       else
386         pr "  r = guestfs_%s_argv " shortname;
387       generate_c_call_args ~handle:"g" style;
388       pr ";\n";
389       pr "\n";
390
391       (* Free up parameters. *)
392       List.iter (
393         function
394         | String n | Device n | Pathname n | Dev_or_Path n
395         | FileIn n | FileOut n | Key n
396         | OptString n -> ()
397         | BufferIn n -> ()
398         | StringList n
399         | DeviceList n ->
400             pr "  {\n";
401             pr "    size_t c = 0;\n";
402             pr "\n";
403             pr "    for (c = 0; %s[c] != NULL; ++c)\n" n;
404             pr "      efree (%s[c]);\n" n;
405             pr "    efree (%s);\n" n;
406             pr "  }\n";
407             pr "\n"
408         | Bool _ | Int _ | Int64 _ | Pointer _ -> ()
409         ) args;
410
411       (* Check for errors. *)
412       (match errcode_of_ret ret with
413        | `CannotReturnError -> ()
414        | `ErrorIsMinusOne ->
415            pr "  if (r == -1) {\n";
416            pr "    RETURN_FALSE;\n";
417            pr "  }\n"
418        | `ErrorIsNULL ->
419            pr "  if (r == NULL) {\n";
420            pr "    RETURN_FALSE;\n";
421            pr "  }\n"
422       );
423       pr "\n";
424
425       (* Convert the return value. *)
426       (match ret with
427        | RErr ->
428            pr "  RETURN_TRUE;\n"
429        | RBool _ ->
430            pr "  RETURN_BOOL (r);\n"
431        | RInt _ ->
432            pr "  RETURN_LONG (r);\n"
433        | RInt64 _ ->
434            pr "  RETURN_LONG (r);\n"
435        | RConstString _ ->
436            pr "  RETURN_STRING (r, 1);\n"
437        | RConstOptString _ ->
438            pr "  if (r) { RETURN_STRING (r, 1); }\n";
439            pr "  else { RETURN_NULL (); }\n"
440        | RString _ ->
441            pr "  char *r_copy = estrdup (r);\n";
442            pr "  free (r);\n";
443            pr "  RETURN_STRING (r_copy, 0);\n"
444        | RBufferOut _ ->
445            pr "  char *r_copy = estrndup (r, size);\n";
446            pr "  free (r);\n";
447            pr "  RETURN_STRING (r_copy, 0);\n"
448        | RStringList _ ->
449            pr "  size_t c = 0;\n";
450            pr "  array_init (return_value);\n";
451            pr "  for (c = 0; r[c] != NULL; ++c) {\n";
452            pr "    add_next_index_string (return_value, r[c], 1);\n";
453            pr "    free (r[c]);\n";
454            pr "  }\n";
455            pr "  free (r);\n";
456        | RHashtable _ ->
457            pr "  size_t c = 0;\n";
458            pr "  array_init (return_value);\n";
459            pr "  for (c = 0; r[c] != NULL; c += 2) {\n";
460            pr "    add_assoc_string (return_value, r[c], r[c+1], 1);\n";
461            pr "    free (r[c]);\n";
462            pr "    free (r[c+1]);\n";
463            pr "  }\n";
464            pr "  free (r);\n";
465        | RStruct (_, typ) ->
466            let cols = cols_of_struct typ in
467            generate_php_struct_code typ cols
468        | RStructList (_, typ) ->
469            let cols = cols_of_struct typ in
470            generate_php_struct_list_code typ cols
471       );
472
473       pr "}\n";
474       pr "\n"
475   ) all_functions_sorted
476
477 and generate_php_struct_code typ cols =
478   pr "  array_init (return_value);\n";
479   List.iter (
480     function
481     | name, FString ->
482         pr "  add_assoc_string (return_value, \"%s\", r->%s, 1);\n" name name
483     | name, FBuffer ->
484         pr "  add_assoc_stringl (return_value, \"%s\", r->%s, r->%s_len, 1);\n"
485           name name name
486     | name, FUUID ->
487         pr "  add_assoc_stringl (return_value, \"%s\", r->%s, 32, 1);\n"
488           name name
489     | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) ->
490         pr "  add_assoc_long (return_value, \"%s\", r->%s);\n"
491           name name
492     | name, FChar ->
493         pr "  add_assoc_stringl (return_value, \"%s\", &r->%s, 1, 1);\n"
494           name name
495     | name, FOptPercent ->
496         pr "  add_assoc_double (return_value, \"%s\", r->%s);\n"
497           name name
498   ) cols;
499   pr "  guestfs_free_%s (r);\n" typ
500
501 and generate_php_struct_list_code typ cols =
502   pr "  array_init (return_value);\n";
503   pr "  size_t c = 0;\n";
504   pr "  for (c = 0; c < r->len; ++c) {\n";
505   pr "    zval *z_elem;\n";
506   pr "    ALLOC_INIT_ZVAL (z_elem);\n";
507   pr "    array_init (z_elem);\n";
508   List.iter (
509     function
510     | name, FString ->
511         pr "    add_assoc_string (z_elem, \"%s\", r->val[c].%s, 1);\n"
512           name name
513     | name, FBuffer ->
514         pr "    add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, r->val[c].%s_len, 1);\n"
515           name name name
516     | name, FUUID ->
517         pr "    add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, 32, 1);\n"
518           name name
519     | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) ->
520         pr "    add_assoc_long (z_elem, \"%s\", r->val[c].%s);\n"
521           name name
522     | name, FChar ->
523         pr "    add_assoc_stringl (z_elem, \"%s\", &r->val[c].%s, 1, 1);\n"
524           name name
525     | name, FOptPercent ->
526         pr "    add_assoc_double (z_elem, \"%s\", r->val[c].%s);\n"
527           name name
528   ) cols;
529   pr "    add_next_index_zval (return_value, z_elem);\n";
530   pr "  }\n";
531   pr "  guestfs_free_%s_list (r);\n" typ