Remote protocol working.
[wrappi.git] / generator / wrappi_c.ml
index 3011ab1..5a73660 100644 (file)
@@ -23,10 +23,12 @@ open Wrappi_boilerplate
 
 open Printf
 
+let inputs = ["wrappi_c.ml"]
+
 let c_of_ptype ~param = function
   | TBool -> "int"
   | TBuffer -> assert false (* XXX not implemented *)
-  | TEnum name -> sprintf "enum wrap_%s" name
+  | TEnum name -> sprintf "wrap_%s_enum" name
   | TFile -> if param then "const char *" else "char *"
   | THash t -> if param then "char * const *" else "char **"
   | TInt -> "int" (* XXX not int, correct type depends on preconditions *)
@@ -36,7 +38,9 @@ let c_of_ptype ~param = function
   | TNullable TString -> if param then "const char *" else "char *"
   | TNullable _ -> assert false (* XXX may be implemented in future *)
   | TString -> if param then "const char *" else "char *"
-  | TStruct name -> sprintf "struct wrap_%s" name
+  | TStruct name ->
+    if param then sprintf "const struct wrap_%s *" name
+    else sprintf "struct wrap_%s *" name
   | TTypedef name -> assert false (* should never happen *)
   | TUInt32 -> "uint32_t"
   | TUInt64 -> "uint64_t"
@@ -44,8 +48,28 @@ let c_of_ptype ~param = function
 
 let c_of_rtype = function
   | RVoid -> "void"
+  | RStaticString -> "const char *"
   | Return t -> c_of_ptype ~param:false t
 
+let field_of_ptype = function
+  | TBool -> "int"
+  | TBuffer -> assert false (* XXX not implemented *)
+  | TEnum name -> sprintf "wrap_%s_enum" name
+  | TFile -> assert false (* cannot occur in a struct *)
+  | THash t -> "char **"
+  | TInt -> "int" (* XXX not int, correct type depends on preconditions *)
+  | TInt32 -> "int32_t"
+  | TInt64 -> "int64_t"
+  | TList t -> assert false (* XXX not implemented *)
+  | TNullable TString -> "char *"
+  | TNullable _ -> assert false (* XXX may be implemented in future *)
+  | TString -> "char *"
+  | TStruct name -> assert false (* we don't allow struct/union here *)
+  | TTypedef name -> assert false (* should never happen *)
+  | TUInt32 -> "uint32_t"
+  | TUInt64 -> "uint64_t"
+  | TUnion name ->  assert false (* we don't allow struct/union here *)
+
 (* Print the extern... declaration of a single entry point. *)
 let pr_extern_decl ep =
   let ret, req, opt = ep.ep_ftype in
@@ -68,7 +92,7 @@ let pr_extern_decl ep =
   pr ");\n"
 
 let generate_lib_wrappi_h api =
-  generate_header CStyle LGPLv2plus;
+  generate_header inputs CStyle LGPLv2plus;
 
   pr "\
 /* Please read the wrappi(1) man page for full documentation.  If you
@@ -88,6 +112,47 @@ extern \"C\" {
 /* The handle. */
 typedef struct wrap_h wrap_h;
 
+/* Types. */
+";
+
+  iter_enums api (
+    fun en ->
+      let name = en.en_name in
+
+      (* The C compiler may choose to declare the sizeof(enum) ==
+       * sizeof(char), and adding fields to such an enum later could
+       * cause ABI breakage.  (See the gcc --fshort-enums option for one
+       * example of this).  Therefore use the enum just to declare the
+       * values, and typedef the enum as an int.
+       *)
+      pr "enum {\n";
+
+      Array.iteri (
+        fun i id ->
+          pr "  WRAP_%s_%s = %d,\n"
+            (String.uppercase name) (String.uppercase id) i
+      ) en.en_identifiers;
+      pr "};\n";
+      pr "typedef int wrap_%s_enum;\n" name;
+      pr "\n";
+  );
+
+  iter_structs api (
+    fun sd ->
+      let name = sd.sd_name in
+
+      pr "struct wrap_%s {\n" name;
+
+      Array.iter (
+        fun (name, t) ->
+          pr "  %s %s;\n" (field_of_ptype t) name
+      ) sd.sd_fields;
+      pr "};\n";
+      pr "void wrap_free_%s (struct wrap_%s *);\n" name name;
+      pr "\n"
+  );
+
+  pr "\
 /* Connection management. */
 extern wrap_h *wrap_create (void);
 extern void wrap_close (wrap_h *w);
@@ -108,6 +173,50 @@ extern void wrap_close (wrap_h *w);
 
   pr "\
 
+/* C API introspection. */
+";
+  iter_entry_points api (
+    fun ep ->
+      let name = ep.ep_name in
+      let ret, req, opt = ep.ep_ftype in
+
+      pr "struct wrap_%s_args {\n" name;
+      List.iter (
+        fun (n, t, _) ->
+          let t = c_of_ptype ~param:true t in
+          let sep = (* "const char *" - omit space after asterisk *)
+            let len = String.length t in
+            if isalnum t.[len-1] then " " else "" in
+          pr "  %s%s%s;\n" t sep n
+      ) req;
+      pr "};\n";
+      pr "\n";
+
+      if opt <> [] then assert false; (* XXX not implemented *)
+
+      pr "struct wrap_%s_ret {\n" name;
+      (match ret with
+      | RVoid -> ()
+      | RStaticString -> pr "  const char *r;\n";
+      | Return t ->
+        let t = c_of_ptype ~param:false t in
+        let sep = (* "const char *" - omit space after asterisk *)
+          let len = String.length t in
+          if isalnum t.[len-1] then " " else "" in
+        pr "  %s%sr;\n" t sep
+      );
+
+      pr "};\n";
+      pr "\n";
+  );
+
+  pr "\
+extern void wrap_call (wrap_h *w, const char *name, const void *args, void *ret);
+extern size_t wrap_call_get_args_struct_size (wrap_h *w, const char *name);
+extern size_t wrap_call_get_ret_struct_size (wrap_h *w, const char *name);
+extern /* xdrproc_t */ void *wrap_call_get_args_xdrproc (wrap_h *w, const char *name);
+extern /* xdrproc_t */ void *wrap_call_get_ret_xdrproc (wrap_h *w, const char *name);
+
 #ifdef __cplusplus
 }
 #endif
@@ -115,5 +224,173 @@ extern void wrap_close (wrap_h *w);
 #endif /* WRAPPI_H_ */
 "
 
+let generate_lib_call_c api =
+  generate_header inputs CStyle LGPLv2plus;
+
+  pr "\
+#include <config.h>
+
+#include <stdio.h>
+
+#include \"wrappi.h\"
+#include \"internal.h\"
+
+void
+wrap_call (wrap_h *w, const char *name, const void *args, void *ret)
+{
+  int proc;
+
+  proc = wrap_int_lookup_proc_entry (name);
+  if (proc == -1) {
+    set_error (\"procedure not found: %%s\", name);
+    return;
+  }
+
+  /* This ends up calling wrap_int_call_<name>. */
+  wrap_int_proc_table[proc].call (w, args, ret);
+}
+
+size_t
+wrap_call_get_args_struct_size (wrap_h *w, const char *name)
+{
+  int proc;
+
+  proc = wrap_int_lookup_proc_entry (name);
+  if (proc == -1) {
+    set_error (\"procedure not found: %%s\", name);
+    return 0;
+  }
+
+  return wrap_int_proc_table[proc].args_struct_size;
+}
+
+size_t
+wrap_call_get_ret_struct_size (wrap_h *w, const char *name)
+{
+  int proc;
+
+  proc = wrap_int_lookup_proc_entry (name);
+  if (proc == -1) {
+    set_error (\"procedure not found: %%s\", name);
+    return 0;
+  }
+
+  return wrap_int_proc_table[proc].ret_struct_size;
+}
+
+/* Really this returns xdrproc_t but we don't want to have to include
+ * XDR headers in the public API.
+ */
+void *
+wrap_call_get_args_xdrproc (wrap_h *w, const char *name)
+{
+  int proc;
+
+  proc = wrap_int_lookup_proc_entry (name);
+  if (proc == -1) {
+    set_error (\"procedure not found: %%s\", name);
+    return 0;
+  }
+
+  return wrap_int_proc_table[proc].args_xdrproc;
+}
+
+/* Really this returns xdrproc_t but we don't want to have to include
+ * XDR headers in the public API.
+ */
+void *
+wrap_call_get_ret_xdrproc (wrap_h *w, const char *name)
+{
+  int proc;
+
+  proc = wrap_int_lookup_proc_entry (name);
+  if (proc == -1) {
+    set_error (\"procedure not found: %%s\", name);
+    return 0;
+  }
+
+  return wrap_int_proc_table[proc].ret_xdrproc;
+}
+";
+
+  iter_entry_points api (
+    fun ep ->
+      pr "\n";
+
+      let name = ep.ep_name in
+      let ret, req, opt = ep.ep_ftype in
+
+      pr "void\n";
+      pr "wrap_int_call_%s (wrap_h *w, const void *argsv, void *retv)\n" name;
+      pr "{\n";
+      if req <> [] || opt <> [] then
+        pr "  const struct wrap_%s_args *args = argsv;\n" name;
+      if ret <> RVoid then
+        pr "  struct wrap_%s_ret *ret = retv;\n" name;
+      pr "\n";
+
+      pr "  ";
+      (match ret with
+      | RVoid -> ()
+      | _ -> pr "ret->r = "
+      );
+
+      pr "wrap_%s (w" name;
+      List.iter (fun (n, _, _) -> pr ", args->%s" n) req;
+
+      if opt <> [] then assert false; (* XXX not implemented *)
+
+      pr ");\n";
+      pr "}\n";
+  )
+
+(* Functions for freeing structs are part of the C bindings.  We don't
+ * want them to be exposed in other languages, although they will be
+ * used by other bindings.
+ *)
+let generate_lib_free_structs_c api =
+  generate_header inputs CStyle LGPLv2plus;
+
+  pr "\
+#include <config.h>
+
+#include <stdlib.h>
+
+#include \"wrappi.h\"
+";
+
+  iter_structs api (
+    fun sd ->
+      pr "\n";
+
+      let name = sd.sd_name in
+
+      pr "void\n";
+      pr "wrap_free_%s (struct wrap_%s *v)\n" name name;
+      pr "{\n";
+
+      Array.iter (
+        fun (n, t) ->
+          match t with
+          | TBool | TEnum _ | TInt | TInt32 | TInt64 | TUInt32 | TUInt64 ->
+            () (* these don't need to be freed *)
+          | TBuffer -> assert false (* XXX not implemented *)
+          | TFile
+          | TNullable TString
+          | TString ->
+            pr "  free (v->%s);\n" n
+          | THash t -> assert false (* XXX not implemented *)
+          | TList t -> assert false (* XXX not implemented *)
+          | TNullable _ -> assert false (* XXX may be implemented in future *)
+          | TStruct name -> assert false (* cannot occur in structs *)
+          | TTypedef name -> assert false (* should never happen *)
+          | TUnion name -> assert false (* cannot occur in structs *)
+      ) sd.sd_fields;
+      pr "  free (v);\n";
+      pr "}\n"
+  )
+
 let generate api =
-  output_to "lib/wrappi.h" generate_lib_wrappi_h api
+  output_to "lib/wrappi.h" generate_lib_wrappi_h api;
+  output_to "lib/call.c" generate_lib_call_c api;
+  output_to "lib/free_structs.c" generate_lib_free_structs_c api