49ddf1ffdd433cc4acd0e4827f32f1dadf8b5e72
[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       (match ret with
188        | RErr | RInt _ | RBool _ -> pr "  int r;\n"
189        | RInt64 _ -> pr "  int64_t r;\n"
190        | RConstString _ | RConstOptString _ ->
191            pr "  const char *r;\n"
192        | RString _ -> pr "  char *r;\n"
193        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
194        | RStruct (_, typ) -> pr "  struct guestfs_%s *r;\n" typ
195        | RStructList (_, typ) ->
196            pr "  struct guestfs_%s_list *r;\n" typ
197        | RBufferOut _ ->
198            pr "  char *r;\n";
199            pr "  size_t size;\n"
200       );
201       pr "\n";
202
203       if optargs = [] then
204         pr "  r = guestfs_%s " name
205       else
206         pr "  r = guestfs_%s_argv " name;
207       generate_c_call_args ~handle:"g" style;
208       pr ";\n";
209
210       List.iter (
211         function
212         | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
213         | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
214         | BufferIn _ | Pointer _ -> ()
215         | StringList n | DeviceList n ->
216             pr "  free (%s);\n" n
217       ) args;
218
219       (match errcode_of_ret ret with
220        | `CannotReturnError -> ()
221        | `ErrorIsMinusOne ->
222            pr "  if (r == -1)\n";
223            pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n"
224        | `ErrorIsNULL ->
225            pr "  if (r == NULL)\n";
226            pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n"
227       );
228       pr "\n";
229
230       (match ret with
231        | RErr ->
232            pr "  return Qnil;\n"
233        | RInt _ | RBool _ ->
234            pr "  return INT2NUM (r);\n"
235        | RInt64 _ ->
236            pr "  return ULL2NUM (r);\n"
237        | RConstString _ ->
238            pr "  return rb_str_new2 (r);\n";
239        | RConstOptString _ ->
240            pr "  if (r)\n";
241            pr "    return rb_str_new2 (r);\n";
242            pr "  else\n";
243            pr "    return Qnil;\n";
244        | RString _ ->
245            pr "  VALUE rv = rb_str_new2 (r);\n";
246            pr "  free (r);\n";
247            pr "  return rv;\n";
248        | RStringList _ ->
249            pr "  size_t i, len = 0;\n";
250            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
251            pr "  VALUE rv = rb_ary_new2 (len);\n";
252            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
253            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
254            pr "    free (r[i]);\n";
255            pr "  }\n";
256            pr "  free (r);\n";
257            pr "  return rv;\n"
258        | RStruct (_, typ) ->
259            let cols = cols_of_struct typ in
260            generate_ruby_struct_code typ cols
261        | RStructList (_, typ) ->
262            let cols = cols_of_struct typ in
263            generate_ruby_struct_list_code typ cols
264        | RHashtable _ ->
265            pr "  VALUE rv = rb_hash_new ();\n";
266            pr "  size_t i;\n";
267            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
268            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
269            pr "    free (r[i]);\n";
270            pr "    free (r[i+1]);\n";
271            pr "  }\n";
272            pr "  free (r);\n";
273            pr "  return rv;\n"
274        | RBufferOut _ ->
275            pr "  VALUE rv = rb_str_new (r, size);\n";
276            pr "  free (r);\n";
277            pr "  return rv;\n";
278       );
279
280       pr "}\n";
281       pr "\n"
282   ) all_functions;
283
284   pr "\
285 /* Initialize the module. */
286 void Init__guestfs ()
287 {
288   m_guestfs = rb_define_module (\"Guestfs\");
289   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
290   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
291
292 #ifdef HAVE_RB_DEFINE_ALLOC_FUNC
293   rb_define_alloc_func (c_guestfs, ruby_guestfs_create);
294 #endif
295
296   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
297   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
298
299 ";
300   (* Define the rest of the methods. *)
301   List.iter (
302     fun (name, (_, args, optargs), _, _, _, _, _) ->
303       let nr_args = List.length args + if optargs <> [] then 1 else 0 in
304       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
305       pr "        ruby_guestfs_%s, %d);\n" name nr_args
306   ) all_functions;
307
308   pr "}\n"
309
310 (* Ruby code to return a struct. *)
311 and generate_ruby_struct_code typ cols =
312   pr "  VALUE rv = rb_hash_new ();\n";
313   List.iter (
314     function
315     | name, FString ->
316         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name
317     | name, FBuffer ->
318         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name
319     | name, FUUID ->
320         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, 32));\n" name name
321     | name, (FBytes|FUInt64) ->
322         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
323     | name, FInt64 ->
324         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), LL2NUM (r->%s));\n" name name
325     | name, FUInt32 ->
326         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), UINT2NUM (r->%s));\n" name name
327     | name, FInt32 ->
328         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), INT2NUM (r->%s));\n" name name
329     | name, FOptPercent ->
330         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->%s));\n" name name
331     | name, FChar -> (* XXX wrong? *)
332         pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
333   ) cols;
334   pr "  guestfs_free_%s (r);\n" typ;
335   pr "  return rv;\n"
336
337 (* Ruby code to return a struct list. *)
338 and generate_ruby_struct_list_code typ cols =
339   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
340   pr "  size_t i;\n";
341   pr "  for (i = 0; i < r->len; ++i) {\n";
342   pr "    VALUE hv = rb_hash_new ();\n";
343   List.iter (
344     function
345     | name, FString ->
346         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
347     | name, FBuffer ->
348         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
349     | name, FUUID ->
350         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
351     | name, (FBytes|FUInt64) ->
352         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
353     | name, FInt64 ->
354         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), LL2NUM (r->val[i].%s));\n" name name
355     | name, FUInt32 ->
356         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), UINT2NUM (r->val[i].%s));\n" name name
357     | name, FInt32 ->
358         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), INT2NUM (r->val[i].%s));\n" name name
359     | name, FOptPercent ->
360         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
361     | name, FChar -> (* XXX wrong? *)
362         pr "    rb_hash_aset (hv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
363   ) cols;
364   pr "    rb_ary_push (rv, hv);\n";
365   pr "  }\n";
366   pr "  guestfs_free_%s_list (r);\n" typ;
367   pr "  return rv;\n"