inspect: Add support for Linux Mint and Mandriva.
[libguestfs.git] / generator / generator_ruby.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 (* Generate ruby bindings. *)
33 let rec generate_ruby_c () =
34   generate_header CStyle LGPLv2plus;
35
36   pr "\
37 #include <stdio.h>
38 #include <stdlib.h>
39
40 #include <ruby.h>
41
42 #include \"guestfs.h\"
43
44 #include \"extconf.h\"
45
46 /* For Ruby < 1.9 */
47 #ifndef RARRAY_LEN
48 #define RARRAY_LEN(r) (RARRAY((r))->len)
49 #endif
50
51 static VALUE m_guestfs;                 /* guestfs module */
52 static VALUE c_guestfs;                 /* guestfs_h handle */
53 static VALUE e_Error;                   /* used for all errors */
54
55 static void ruby_guestfs_free (void *p)
56 {
57   if (!p) return;
58   guestfs_close ((guestfs_h *) p);
59 }
60
61 static VALUE ruby_guestfs_create (VALUE m)
62 {
63   guestfs_h *g;
64
65   g = guestfs_create ();
66   if (!g)
67     rb_raise (e_Error, \"failed to create guestfs handle\");
68
69   /* Don't print error messages to stderr by default. */
70   guestfs_set_error_handler (g, NULL, NULL);
71
72   /* Wrap it, and make sure the close function is called when the
73    * handle goes away.
74    */
75   return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g);
76 }
77
78 static VALUE ruby_guestfs_close (VALUE gv)
79 {
80   guestfs_h *g;
81   Data_Get_Struct (gv, guestfs_h, g);
82
83   ruby_guestfs_free (g);
84   DATA_PTR (gv) = NULL;
85
86   return Qnil;
87 }
88
89 ";
90
91   List.iter (
92     fun (name, (ret, args, optargs as style), _, _, _, _, _) ->
93       pr "static VALUE ruby_guestfs_%s (VALUE gv" name;
94       List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) args;
95       (* XXX This makes the hash mandatory, meaning that you have
96        * to specify {} for no arguments.  We could make it so this
97        * can be omitted.  However that is a load of hassle because
98        * you have to completely change the way that arguments are
99        * passed in.  See:
100        * http://www.redhat.com/archives/libvir-list/2008-April/msg00004.html
101        *)
102       if optargs <> [] then
103         pr ", VALUE optargsv";
104       pr ")\n";
105       pr "{\n";
106       pr "  guestfs_h *g;\n";
107       pr "  Data_Get_Struct (gv, guestfs_h, g);\n";
108       pr "  if (!g)\n";
109       pr "    rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n"
110         name;
111       pr "\n";
112
113       List.iter (
114         function
115         | Pathname n | Device n | Dev_or_Path n | String n | Key n
116         | FileIn n | FileOut n ->
117             pr "  Check_Type (%sv, T_STRING);\n" n;
118             pr "  const char *%s = StringValueCStr (%sv);\n" n n;
119             pr "  if (!%s)\n" n;
120             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
121             pr "              \"%s\", \"%s\");\n" n name
122         | BufferIn n ->
123             pr "  Check_Type (%sv, T_STRING);\n" n;
124             pr "  const char *%s = RSTRING (%sv)->ptr;\n" n n;
125             pr "  if (!%s)\n" n;
126             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
127             pr "              \"%s\", \"%s\");\n" n name;
128             pr "  size_t %s_size = RSTRING (%sv)->len;\n" n n
129         | OptString n ->
130             pr "  const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n
131         | StringList n | DeviceList n ->
132             pr "  char **%s;\n" n;
133             pr "  Check_Type (%sv, T_ARRAY);\n" n;
134             pr "  {\n";
135             pr "    size_t i, len;\n";
136             pr "    len = RARRAY_LEN (%sv);\n" n;
137             pr "    %s = guestfs_safe_malloc (g, sizeof (char *) * (len+1));\n"
138               n;
139             pr "    for (i = 0; i < len; ++i) {\n";
140             pr "      VALUE v = rb_ary_entry (%sv, i);\n" n;
141             pr "      %s[i] = StringValueCStr (v);\n" n;
142             pr "    }\n";
143             pr "    %s[len] = NULL;\n" n;
144             pr "  }\n";
145         | Bool n ->
146             pr "  int %s = RTEST (%sv);\n" n n
147         | Int n ->
148             pr "  int %s = NUM2INT (%sv);\n" n n
149         | Int64 n ->
150             pr "  long long %s = NUM2LL (%sv);\n" n n
151         | Pointer (t, n) ->
152             pr "  %s %s = (%s) (intptr_t) NUM2LL (%sv);\n" t n t n
153       ) args;
154       pr "\n";
155
156       (* Optional arguments are passed in a final hash parameter. *)
157       if optargs <> [] then (
158         let uc_name = String.uppercase name in
159         pr "  Check_Type (optargsv, T_HASH);\n";
160         pr "  struct guestfs_%s_argv optargs_s = { .bitmask = 0 };\n" name;
161         pr "  struct guestfs_%s_argv *optargs = &optargs_s;\n" name;
162         pr "  VALUE v;\n";
163         List.iter (
164           fun argt ->
165             let n = name_of_argt argt in
166             let uc_n = String.uppercase n in
167             pr "  v = rb_hash_lookup (optargsv, ID2SYM (rb_intern (\"%s\")));\n" n;
168             pr "  if (v != Qnil) {\n";
169             (match argt with
170              | Bool n ->
171                  pr "    optargs_s.%s = RTEST (v);\n" n;
172              | Int n ->
173                  pr "    optargs_s.%s = NUM2INT (v);\n" n;
174              | Int64 n ->
175                  pr "    optargs_s.%s = NUM2LL (v);\n" n;
176              | String _ ->
177                  pr "    Check_Type (v, T_STRING);\n";
178                  pr "    optargs_s.%s = StringValueCStr (v);\n" n
179              | _ -> assert false
180             );
181             pr "    optargs_s.bitmask |= GUESTFS_%s_%s_BITMASK;\n" uc_name uc_n;
182             pr "  }\n";
183         ) optargs;
184         pr "\n";
185       );
186
187       let error_code =
188         match ret with
189         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
190         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
191         | RConstString _ | RConstOptString _ ->
192             pr "  const char *r;\n"; "NULL"
193         | RString _ -> pr "  char *r;\n"; "NULL"
194         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
195         | RStruct (_, typ) -> pr "  struct guestfs_%s *r;\n" typ; "NULL"
196         | RStructList (_, typ) ->
197             pr "  struct guestfs_%s_list *r;\n" typ; "NULL"
198         | RBufferOut _ ->
199             pr "  char *r;\n";
200             pr "  size_t size;\n";
201             "NULL" in
202       pr "\n";
203
204       if optargs = [] then
205         pr "  r = guestfs_%s " name
206       else
207         pr "  r = guestfs_%s_argv " name;
208       generate_c_call_args ~handle:"g" style;
209       pr ";\n";
210
211       List.iter (
212         function
213         | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
214         | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
215         | BufferIn _ | Pointer _ -> ()
216         | StringList n | DeviceList n ->
217             pr "  free (%s);\n" n
218       ) args;
219
220       pr "  if (r == %s)\n" error_code;
221       pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n";
222       pr "\n";
223
224       (match ret with
225        | RErr ->
226            pr "  return Qnil;\n"
227        | RInt _ | RBool _ ->
228            pr "  return INT2NUM (r);\n"
229        | RInt64 _ ->
230            pr "  return ULL2NUM (r);\n"
231        | RConstString _ ->
232            pr "  return rb_str_new2 (r);\n";
233        | RConstOptString _ ->
234            pr "  if (r)\n";
235            pr "    return rb_str_new2 (r);\n";
236            pr "  else\n";
237            pr "    return Qnil;\n";
238        | RString _ ->
239            pr "  VALUE rv = rb_str_new2 (r);\n";
240            pr "  free (r);\n";
241            pr "  return rv;\n";
242        | RStringList _ ->
243            pr "  size_t i, len = 0;\n";
244            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
245            pr "  VALUE rv = rb_ary_new2 (len);\n";
246            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
247            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
248            pr "    free (r[i]);\n";
249            pr "  }\n";
250            pr "  free (r);\n";
251            pr "  return rv;\n"
252        | RStruct (_, typ) ->
253            let cols = cols_of_struct typ in
254            generate_ruby_struct_code typ cols
255        | RStructList (_, typ) ->
256            let cols = cols_of_struct typ in
257            generate_ruby_struct_list_code typ cols
258        | RHashtable _ ->
259            pr "  VALUE rv = rb_hash_new ();\n";
260            pr "  size_t i;\n";
261            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
262            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
263            pr "    free (r[i]);\n";
264            pr "    free (r[i+1]);\n";
265            pr "  }\n";
266            pr "  free (r);\n";
267            pr "  return rv;\n"
268        | RBufferOut _ ->
269            pr "  VALUE rv = rb_str_new (r, size);\n";
270            pr "  free (r);\n";
271            pr "  return rv;\n";
272       );
273
274       pr "}\n";
275       pr "\n"
276   ) all_functions;
277
278   pr "\
279 /* Initialize the module. */
280 void Init__guestfs ()
281 {
282   m_guestfs = rb_define_module (\"Guestfs\");
283   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
284   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
285
286 #ifdef HAVE_RB_DEFINE_ALLOC_FUNC
287   rb_define_alloc_func (c_guestfs, ruby_guestfs_create);
288 #endif
289
290   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
291   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
292
293 ";
294   (* Define the rest of the methods. *)
295   List.iter (
296     fun (name, (_, args, optargs), _, _, _, _, _) ->
297       let nr_args = List.length args + if optargs <> [] then 1 else 0 in
298       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
299       pr "        ruby_guestfs_%s, %d);\n" name nr_args
300   ) all_functions;
301
302   pr "}\n"
303
304 (* Ruby code to return a struct. *)
305 and generate_ruby_struct_code typ cols =
306   pr "  VALUE rv = rb_hash_new ();\n";
307   List.iter (
308     function
309     | name, FString ->
310         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name
311     | name, FBuffer ->
312         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name
313     | name, FUUID ->
314         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, 32));\n" name name
315     | name, (FBytes|FUInt64) ->
316         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
317     | name, FInt64 ->
318         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), LL2NUM (r->%s));\n" name name
319     | name, FUInt32 ->
320         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), UINT2NUM (r->%s));\n" name name
321     | name, FInt32 ->
322         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), INT2NUM (r->%s));\n" name name
323     | name, FOptPercent ->
324         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->%s));\n" name name
325     | name, FChar -> (* XXX wrong? *)
326         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
327   ) cols;
328   pr "  guestfs_free_%s (r);\n" typ;
329   pr "  return rv;\n"
330
331 (* Ruby code to return a struct list. *)
332 and generate_ruby_struct_list_code typ cols =
333   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
334   pr "  size_t i;\n";
335   pr "  for (i = 0; i < r->len; ++i) {\n";
336   pr "    VALUE hv = rb_hash_new ();\n";
337   List.iter (
338     function
339     | name, FString ->
340         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
341     | name, FBuffer ->
342         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, r->val[i].%s_len));\n" name name name
343     | name, FUUID ->
344         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
345     | name, (FBytes|FUInt64) ->
346         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
347     | name, FInt64 ->
348         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), LL2NUM (r->val[i].%s));\n" name name
349     | name, FUInt32 ->
350         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), UINT2NUM (r->val[i].%s));\n" name name
351     | name, FInt32 ->
352         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), INT2NUM (r->val[i].%s));\n" name name
353     | name, FOptPercent ->
354         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
355     | name, FChar -> (* XXX wrong? *)
356         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
357   ) cols;
358   pr "    rb_ary_push (rv, hv);\n";
359   pr "  }\n";
360   pr "  guestfs_free_%s_list (r);\n" typ;
361   pr "  return rv;\n"