Combine generator subdirectories into one.
[wrappi.git] / generator / wrappi_c.ml
1 (* wrappi
2  * Copyright (C) 2011 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 open Wrappi_types
20 open Wrappi_utils
21 open Wrappi_pr
22 open Wrappi_boilerplate
23
24 open Printf
25
26 let inputs = ["wrappi_c.ml"]
27
28 let c_of_ptype ~param = function
29   | TBool -> "int"
30   | TBuffer -> assert false (* XXX not implemented *)
31   | TEnum name -> sprintf "wrap_%s_enum" name
32   | TFile -> if param then "const char *" else "char *"
33   | THash t -> if param then "char * const *" else "char **"
34   | TInt -> "int" (* XXX not int, correct type depends on preconditions *)
35   | TInt32 -> "int32_t"
36   | TInt64 -> "int64_t"
37   | TList t -> assert false (* XXX not implemented *)
38   | TNullable TString -> if param then "const char *" else "char *"
39   | TNullable _ -> assert false (* XXX may be implemented in future *)
40   | TString -> if param then "const char *" else "char *"
41   | TStruct name ->
42     if param then sprintf "const struct wrap_%s *" name
43     else sprintf "struct wrap_%s *" name
44   | TTypedef name -> assert false (* should never happen *)
45   | TUInt32 -> "uint32_t"
46   | TUInt64 -> "uint64_t"
47   | TUnion name -> sprintf "union wrap_%s" name
48
49 let c_of_rtype = function
50   | RVoid -> "void"
51   | RStaticString -> "const char *"
52   | Return t -> c_of_ptype ~param:false t
53
54 let field_of_ptype = function
55   | TBool -> "int"
56   | TBuffer -> assert false (* XXX not implemented *)
57   | TEnum name -> sprintf "wrap_%s_enum" name
58   | TFile -> assert false (* cannot occur in a struct *)
59   | THash t -> "char **"
60   | TInt -> "int" (* XXX not int, correct type depends on preconditions *)
61   | TInt32 -> "int32_t"
62   | TInt64 -> "int64_t"
63   | TList t -> assert false (* XXX not implemented *)
64   | TNullable TString -> "char *"
65   | TNullable _ -> assert false (* XXX may be implemented in future *)
66   | TString -> "char *"
67   | TStruct name -> assert false (* we don't allow struct/union here *)
68   | TTypedef name -> assert false (* should never happen *)
69   | TUInt32 -> "uint32_t"
70   | TUInt64 -> "uint64_t"
71   | TUnion name ->  assert false (* we don't allow struct/union here *)
72
73 (* Print the extern... declaration of a single entry point. *)
74 let pr_extern_decl ep =
75   let ret, req, opt = ep.ep_ftype in
76   pr "extern %s wrap_%s (wrap_h *w" (c_of_rtype ret) ep.ep_name;
77
78   (* Required parameters. *)
79   List.iter (
80     fun (name, t, _) ->
81       let t = c_of_ptype ~param:true t in
82       let sep = (* "const char *" - omit space after asterisk *)
83         let len = String.length t in
84         if isalnum t.[len-1] then " " else "" in
85       pr ", %s%s%s" t sep name
86   ) req;
87
88   (* Optional parameters. *)
89   if opt <> [] then
90     pr ", ...";
91
92   pr ");\n"
93
94 let generate_lib_wrappi_h api =
95   generate_header inputs CStyle LGPLv2plus;
96
97   pr "\
98 /* Please read the wrappi(1) man page for full documentation.  If you
99  * are not familiar with man pages or don't have the documentation
100  * installed, it is also available online at http://wrappi.org/
101  */
102
103 #ifndef WRAPPI_H_
104 #define WRAPPI_H_
105
106 #ifdef __cplusplus
107 extern \"C\" {
108 #endif
109
110 #include <stdint.h>
111
112 /* The handle. */
113 typedef struct wrap_h wrap_h;
114
115 /* Types. */
116 ";
117
118   iter_enums api (
119     fun en ->
120       let name = en.en_name in
121
122       (* The C compiler may choose to declare the sizeof(enum) ==
123        * sizeof(char), and adding fields to such an enum later could
124        * cause ABI breakage.  (See the gcc --fshort-enums option for one
125        * example of this).  Therefore use the enum just to declare the
126        * values, and typedef the enum as an int.
127        *)
128       pr "enum {\n";
129
130       Array.iteri (
131         fun i id ->
132           pr "  WRAP_%s_%s = %d,\n"
133             (String.uppercase name) (String.uppercase id) i
134       ) en.en_identifiers;
135       pr "};\n";
136       pr "typedef int wrap_%s_enum;\n" name;
137       pr "\n";
138   );
139
140   iter_structs api (
141     fun sd ->
142       let name = sd.sd_name in
143
144       pr "struct wrap_%s {\n" name;
145
146       Array.iter (
147         fun (name, t) ->
148           pr "  %s %s;\n" (field_of_ptype t) name
149       ) sd.sd_fields;
150       pr "};\n";
151       pr "void wrap_free_%s (struct wrap_%s *);\n" name name;
152       pr "\n"
153   );
154
155   pr "\
156 /* Connection management. */
157 extern wrap_h *wrap_create (void);
158 extern void wrap_close (wrap_h *w);
159
160 ";
161
162   (* Separate the local and remote functions. *)
163   pr "\
164 /* Handle functions. */
165 ";
166   iter_entry_points api (fun ep -> if ep.ep_local then pr_extern_decl ep);
167
168   pr "\
169
170 /* API entry points. */
171 ";
172   iter_entry_points api (fun ep -> if not ep.ep_local then pr_extern_decl ep);
173
174   pr "\
175
176 /* C API introspection. */
177 ";
178   iter_entry_points api (
179     fun ep ->
180       let name = ep.ep_name in
181       let ret, req, opt = ep.ep_ftype in
182
183       pr "struct wrap_%s_args {\n" name;
184       List.iter (
185         fun (n, t, _) ->
186           let t = c_of_ptype ~param:true t in
187           let sep = (* "const char *" - omit space after asterisk *)
188             let len = String.length t in
189             if isalnum t.[len-1] then " " else "" in
190           pr "  %s%s%s;\n" t sep n
191       ) req;
192       pr "};\n";
193       pr "\n";
194
195       if opt <> [] then assert false; (* XXX not implemented *)
196
197       pr "struct wrap_%s_ret {\n" name;
198       (match ret with
199       | RVoid -> ()
200       | RStaticString -> pr "  const char *r;\n";
201       | Return t ->
202         let t = c_of_ptype ~param:false t in
203         let sep = (* "const char *" - omit space after asterisk *)
204           let len = String.length t in
205           if isalnum t.[len-1] then " " else "" in
206         pr "  %s%sr;\n" t sep
207       );
208
209       pr "};\n";
210       pr "\n";
211   );
212
213   pr "\
214 extern void wrap_call (wrap_h *w, const char *name, const void *args, void *ret);
215 extern size_t wrap_call_get_args_struct_size (wrap_h *w, const char *name);
216 extern size_t wrap_call_get_ret_struct_size (wrap_h *w, const char *name);
217 extern /* xdrproc_t */ void *wrap_call_get_args_xdrproc (wrap_h *w, const char *name);
218 extern /* xdrproc_t */ void *wrap_call_get_ret_xdrproc (wrap_h *w, const char *name);
219
220 #ifdef __cplusplus
221 }
222 #endif
223
224 #endif /* WRAPPI_H_ */
225 "
226
227 let generate_lib_call_c api =
228   generate_header inputs CStyle LGPLv2plus;
229
230   pr "\
231 #include <config.h>
232
233 #include <stdio.h>
234
235 #include \"wrappi.h\"
236 #include \"internal.h\"
237
238 void
239 wrap_call (wrap_h *w, const char *name, const void *args, void *ret)
240 {
241   int proc;
242
243   proc = wrap_int_lookup_proc_entry (name);
244   if (proc == -1) {
245     set_error (\"procedure not found: %%s\", name);
246     return;
247   }
248
249   /* This ends up calling wrap_int_call_<name>. */
250   wrap_int_proc_table[proc].call (w, args, ret);
251 }
252
253 size_t
254 wrap_call_get_args_struct_size (wrap_h *w, const char *name)
255 {
256   int proc;
257
258   proc = wrap_int_lookup_proc_entry (name);
259   if (proc == -1) {
260     set_error (\"procedure not found: %%s\", name);
261     return 0;
262   }
263
264   return wrap_int_proc_table[proc].args_struct_size;
265 }
266
267 size_t
268 wrap_call_get_ret_struct_size (wrap_h *w, const char *name)
269 {
270   int proc;
271
272   proc = wrap_int_lookup_proc_entry (name);
273   if (proc == -1) {
274     set_error (\"procedure not found: %%s\", name);
275     return 0;
276   }
277
278   return wrap_int_proc_table[proc].ret_struct_size;
279 }
280
281 /* Really this returns xdrproc_t but we don't want to have to include
282  * XDR headers in the public API.
283  */
284 void *
285 wrap_call_get_args_xdrproc (wrap_h *w, const char *name)
286 {
287   int proc;
288
289   proc = wrap_int_lookup_proc_entry (name);
290   if (proc == -1) {
291     set_error (\"procedure not found: %%s\", name);
292     return 0;
293   }
294
295   return wrap_int_proc_table[proc].args_xdrproc;
296 }
297
298 /* Really this returns xdrproc_t but we don't want to have to include
299  * XDR headers in the public API.
300  */
301 void *
302 wrap_call_get_ret_xdrproc (wrap_h *w, const char *name)
303 {
304   int proc;
305
306   proc = wrap_int_lookup_proc_entry (name);
307   if (proc == -1) {
308     set_error (\"procedure not found: %%s\", name);
309     return 0;
310   }
311
312   return wrap_int_proc_table[proc].ret_xdrproc;
313 }
314 ";
315
316   iter_entry_points api (
317     fun ep ->
318       pr "\n";
319
320       let name = ep.ep_name in
321       let ret, req, opt = ep.ep_ftype in
322
323       pr "void\n";
324       pr "wrap_int_call_%s (wrap_h *w, const void *argsv, void *retv)\n" name;
325       pr "{\n";
326       if req <> [] || opt <> [] then
327         pr "  const struct wrap_%s_args *args = argsv;\n" name;
328       if ret <> RVoid then
329         pr "  struct wrap_%s_ret *ret = retv;\n" name;
330       pr "\n";
331
332       pr "  ";
333       (match ret with
334       | RVoid -> ()
335       | _ -> pr "ret->r = "
336       );
337
338       pr "wrap_%s (w" name;
339       List.iter (fun (n, _, _) -> pr ", args->%s" n) req;
340
341       if opt <> [] then assert false; (* XXX not implemented *)
342
343       pr ");\n";
344       pr "}\n";
345   )
346
347 (* Functions for freeing structs are part of the C bindings.  We don't
348  * want them to be exposed in other languages, although they will be
349  * used by other bindings.
350  *)
351 let generate_lib_free_structs_c api =
352   generate_header inputs CStyle LGPLv2plus;
353
354   pr "\
355 #include <config.h>
356
357 #include <stdlib.h>
358
359 #include \"wrappi.h\"
360 ";
361
362   iter_structs api (
363     fun sd ->
364       pr "\n";
365
366       let name = sd.sd_name in
367
368       pr "void\n";
369       pr "wrap_free_%s (struct wrap_%s *v)\n" name name;
370       pr "{\n";
371
372       Array.iter (
373         fun (n, t) ->
374           match t with
375           | TBool | TEnum _ | TInt | TInt32 | TInt64 | TUInt32 | TUInt64 ->
376             () (* these don't need to be freed *)
377           | TBuffer -> assert false (* XXX not implemented *)
378           | TFile
379           | TNullable TString
380           | TString ->
381             pr "  free (v->%s);\n" n
382           | THash t -> assert false (* XXX not implemented *)
383           | TList t -> assert false (* XXX not implemented *)
384           | TNullable _ -> assert false (* XXX may be implemented in future *)
385           | TStruct name -> assert false (* cannot occur in structs *)
386           | TTypedef name -> assert false (* should never happen *)
387           | TUnion name -> assert false (* cannot occur in structs *)
388       ) sd.sd_fields;
389       pr "  free (v);\n";
390       pr "}\n"
391   )
392
393 let generate api =
394   output_to "lib/wrappi.h" generate_lib_wrappi_h api;
395   output_to "lib/call.c" generate_lib_call_c api;
396   output_to "lib/free_structs.c" generate_lib_free_structs_c api