More implementation.
authorRichard W.M. Jones <rjones@redhat.com>
Sun, 1 Jan 2012 16:45:14 +0000 (16:45 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Sun, 1 Jan 2012 18:03:04 +0000 (18:03 +0000)
17 files changed:
.gitignore
APIs/error.api
APIs/mknod.api
configure.ac
examples/Makefile.am
generator-lib/wrappi_accumulator.ml
generator-lib/wrappi_types.ml
generator-lib/wrappi_types.mli
generator-macros/pa_wrap.ml
generator/wrappi_c.ml
generator/wrappi_c_impl.ml
lib/.gitignore
lib/Makefile.am
lib/connect.c [new file with mode: 0644]
lib/implementation_files.mk
lib/internal.h
lib/wrappi.c

index 545b80a..ec4a33c 100644 (file)
@@ -22,12 +22,14 @@ Makefile.in
 /config.sub
 /configure
 /depcomp
+/examples/remote
 /generator-lib/config.ml
 /generator/generator
 /generator/stamp-generator
 /install-sh
 /lib/wrappi.h
 /libtool
+/local*
 /ltmain.sh
 /m4/libtool.m4
 /m4/ltoptions.m4
index e315338..2dd8652 100644 (file)
 entry_point local
 bool error ()
 <<
-  return w->error_flag;
+  return w->internal.error != NULL;
 >>
 
 entry_point local
 void clear_error ()
 <<
-  w->error_flag = 0;
+  free (w->internal.error);
+  w->internal.error = NULL;
+  w->internal.errnum = 0;
+  w->internal.error_func = NULL;
+>>
+
+entry_point local
+static_string get_error ()
+<<
+  if (!w->internal.error) {
+    set_error ("no error on handle: do not call this function unless 'wrap_error' returns true");
+    return NULL;
+  }
+  return w->internal.error;
+>>
+
+entry_point local
+int get_errno ()
+<<
+  if (!w->internal.error) {
+    set_error ("no error on handle: do not call this function unless 'wrap_error' returns true");
+    return -1;
+  }
+  return w->internal.errnum;
+>>
+
+entry_point local
+static_string get_error_func ()
+<<
+  if (!w->internal.error) {
+    set_error ("no error on handle: do not call this function unless 'wrap_error' returns true");
+    return NULL;
+  }
+  return w->internal.error_func;
 >>
 
 (*
index 07d2cca..a6bc8ef 100644 (file)
 entry_point
 void mknod_char (pathname path, fileperm perm, uint64 major, uint64 minor)
 <<
-  if (mknod (path, S_IFCHR | perm, makedev (major, minor)) == -1) {
+  if (mknod (path, S_IFCHR | perm, makedev (major, minor)) == -1)
     set_error_errno ("mknod: %s", path);
-    return -1;
-  }
-  return 0;
 >>
 includes [ "sys/types.h"; "sys/stat.h"; "fcntl.h"; "unistd.h" ]
 
index 79ed9c9..3cad79e 100644 (file)
@@ -20,6 +20,10 @@ AM_INIT_AUTOMAKE([foreign])
 
 AC_CONFIG_MACRO_DIR([m4])
 
+dnl Allow all GNU/Linux functions.
+dnl autoconf complains unless this is very early in the file.
+AC_USE_SYSTEM_EXTENSIONS
+
 AC_PROG_LIBTOOL
 
 AC_PROG_SED
@@ -34,6 +38,25 @@ test "x$U" != "x" && AC_MSG_ERROR([Compiler not ANSI compliant])
 
 AM_PROG_CC_C_O
 
+AC_ARG_ENABLE([gcc-warnings],
+    [AS_HELP_STRING([--enable-gcc-warnings],
+                    [turn on lots of GCC warnings (for developers)])],
+    [case $enableval in
+       yes|no) ;;
+       *)      AC_MSG_ERROR([bad value $enableval for gcc-warnings option]) ;;
+     esac
+     gcc_warnings=$enableval],
+    [gcc_warnings=no]
+)
+
+if test "$gcc_warnings" = yes; then
+    # XXX With gnulib we can improve this in future.
+    WARN_CFLAGS="-Wall"
+    AC_SUBST([WARN_CFLAGS])
+    WERROR_CFLAGS="-Werror"
+    AC_SUBST([WERROR_CFLAGS])
+fi
+
 dnl Check support for 64 bit file offsets.
 AC_SYS_LARGEFILE
 
index 7fd04c1..15b68bd 100644 (file)
@@ -20,3 +20,5 @@ noinst_PROGRAMS = remote
 remote_SOURCES = remote.c
 remote_CFLAGS = -I../lib
 remote_LDADD = ../lib/libwrappi.la
+
+CLEANFILES = *~
index ac09b99..3713d35 100644 (file)
@@ -101,7 +101,8 @@ let rec resolve_typedefs thing name loc = function
       exit 1
 
 let resolve_typedefs_in_ret thing name loc = function
-  | RVoid as t -> t
+  | (RVoid
+        | RStaticString) as t -> t
   | Return t -> Return (resolve_typedefs thing name loc t)
 
 let get_api () =
index f738f95..998e911 100644 (file)
@@ -44,11 +44,14 @@ type prec
 
 type parameter = string * ptype * prec option
 
-type rtype = RVoid | Return of ptype
+type rtype = RVoid | RStaticString | Return of ptype
 
 type ftype = rtype * parameter list * parameter list
 
-type c_code = string
+type c_code = {
+  cc_loc : Camlp4.PreCast.Loc.t;
+  cc_code : string;
+}
 
 type entry_point = {
   ep_loc : Camlp4.PreCast.Loc.t;
@@ -124,6 +127,7 @@ let rec string_of_ptype = function
   | TUnion name -> sprintf "union %s" name
 let string_of_rtype = function
   | RVoid -> "void"
+  | RStaticString -> "static_string"
   | Return t -> string_of_ptype t
 let string_of_parameter (name, t, _) =
   sprintf "%s %s" (string_of_ptype t) name
@@ -132,7 +136,7 @@ let string_of_parameters params =
 let string_of_ftype (ret, req, opt) =
   sprintf "%s %s %s"
     (string_of_rtype ret) (string_of_parameters req) (string_of_parameters opt)
-let string_of_c_code code = code
+let string_of_c_code code = code.cc_code
 
 let string_of_typedef td =
   sprintf "typedef %s %s" td.td_name (string_of_ptype td.td_type)
index 94cf371..72840fd 100644 (file)
@@ -41,15 +41,18 @@ type prec
 type parameter = string * ptype * prec option
 (** API parameter (argument name, type, optional precondition). *)
 
-type rtype = RVoid | Return of ptype
+type rtype = RVoid | RStaticString | Return of ptype
 (** API return type.  A superset of {!ptype} because we allow the
-    special value [RVoid] for functions that don't return any value. *)
+    some special return-only types. *)
 
 type ftype = rtype * parameter list * parameter list
 (** A function type.  Return type, list of required parameters, list
     of optional parameters. *)
 
-type c_code = string
+type c_code = {
+  cc_loc : Camlp4.PreCast.Loc.t;
+  cc_code : string;
+}
 (** C code. *)
 
 type entry_point = {
index c176fe2..5ce6d08 100644 (file)
@@ -89,9 +89,13 @@ let add_typedef _loc name t =
 let () =
   (* Quotation expander for C code. *)
   let c_quotation_expander _loc _ code =
+    let loc = expr_of_loc _loc _loc in
+
     (* XXX Expand %- or $- expressions in code. *)
     (* XXX Escape >> in code. *)
-    ExStr (_loc, code)
+
+    <:expr< { Wrappi_types.cc_loc = $loc$;
+              cc_code = $str:code$ } >>
   in
   Quotation.add "c" Quotation.DynAst.expr_tag c_quotation_expander;
 
@@ -127,6 +131,7 @@ EXTEND Gram
   (* A return type. *)
   rtype: [
     [ "void" -> <:expr< Wrappi_types.RVoid >> ]
+  | [ "static_string" -> <:expr< Wrappi_types.RStaticString >> ]
   | [ t = ptype -> <:expr< Wrappi_types.Return $t$ >> ]
   ];
 
index 3011ab1..7f9c1b4 100644 (file)
@@ -44,6 +44,7 @@ let c_of_ptype ~param = function
 
 let c_of_rtype = function
   | RVoid -> "void"
+  | RStaticString -> "const char *"
   | Return t -> c_of_ptype ~param:false t
 
 (* Print the extern... declaration of a single entry point. *)
index 8742fad..c3e2ed7 100644 (file)
@@ -46,12 +46,19 @@ 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 pr_decl ep =
+let pr_defn ?(impl = false) ep =
   let ret, req, opt = ep.ep_ftype in
-  pr "%s\n" (c_of_rtype ret);
-  pr "wrap_%s (wrap_h *w" ep.ep_name;
+
+  if not impl then (
+    pr "%s\n" (c_of_rtype ret);
+    pr "wrap_%s (wrap_h *w" ep.ep_name
+  ) else (
+    pr "static %s\n" (c_of_rtype ret);
+    pr "impl_%s (struct wrap_internal_h *w" ep.ep_name
+  );
 
   (* Required parameters. *)
   List.iter (
@@ -73,43 +80,81 @@ let generate_implementation ep =
   generate_header CStyle LGPLv2plus;
 
   pr "\
+/* Automatically generated implementation of '%s'.
+ * This API was defined in '%s' at line %d.
+ */
+
+" ep.ep_name (Loc.file_name ep.ep_loc) (Loc.start_line ep.ep_loc);
+
+  pr "\
 #include <config.h>
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
 ";
   List.iter (pr "#include <%s>\n") ep.ep_includes;
 
 pr "\
 
 #include \"wrappi.h\"
-
 #include \"internal.h\"
 
-/* Automatically generated implementation of '%s'.
- * This API was defined in '%s' at line %d.
- */
+";
 
-" ep.ep_name (Loc.file_name ep.ep_loc) (Loc.start_line ep.ep_loc);
+  pr_defn ~impl:(not ep.ep_local) ep;
 
-  (* Depending on whether this is a local or remote function, include
-   * different definitions here.
+  pr "{\n";
+
+  (match ep.ep_code with
+  | None -> () (* XXX implicit code *)
+  | Some { cc_loc = loc; cc_code = code } ->
+    pr "#line %d \"%s\"\n" (Loc.start_line loc) (Loc.file_name loc);
+    pr "%s" code
+  );
+
+  pr "}\n";
+
+  (* For remote functions only, we now need to generate the
+   * local binding code.
    *)
-  (*if ep.ep_local then ... *)
+  if not ep.ep_local then (
+    pr "\n";
 
-  pr_decl ep;
+    pr_defn ep;
 
-  pr "\
-{
-#line %d \"%s\"
-" (Loc.start_line ep.ep_loc) (Loc.file_name ep.ep_loc);
+    pr "{\n";
+    pr "  assert (w->scheme == NULL); /* XXX */;\n";
+    pr "\n";
+    pr "  ";
 
-  (match ep.ep_code with
-  | None -> ()
-  | Some code -> pr "%s" code
-  );
+    let ret, req, opt = ep.ep_ftype in
+
+    (match ret with
+    | RVoid -> ()
+    | _ -> pr "return "
+    );
+
+    pr "impl_%s ((struct wrap_internal_h *)w" ep.ep_name;
+
+    (* Required parameters. *)
+    List.iter (fun (name, _, _) -> pr ", %s" name) req;
+
+    (* Optional parameters. *)
+    if opt <> [] then
+      assert false; (* XXX not implemented *)
+
+    pr ");\n";
+
+    (match ret with
+    | RVoid -> pr "  return;\n"
+    | _ -> ()
+    );
 
-  pr "}\n"
+    pr "}\n"
+  )
 
 (* Make a unique, reproducible filename for each entry point. *)
 let filename_of_ep ep =
@@ -137,7 +182,7 @@ let generate_lib_implementation_files_mk api =
 
   pr "local_implementation_files := \\\n";
 
-  loop (List.filter (fun ep -> ep.ep_local) eps);
+  loop (List.filter (fun ep -> ep.ep_local && ep.ep_code <> None) eps);
 
   pr "\n";
   pr "remote_implementation_files := \\\n";
@@ -149,11 +194,17 @@ let generate api =
 
   iter_entry_points api (
     fun ep ->
-      let filename = filename_of_ep ep in
+      (* Local entry points which don't have associated code are
+       * assumed to be implemented in hand-written code elsewhere under
+       * lib/.
+       *)
+      if not ep.ep_local || ep.ep_code <> None then (
+        let filename = filename_of_ep ep in
 
-      gitignores := ("/" ^ filename) :: !gitignores;
+        gitignores := ("/" ^ filename) :: !gitignores;
 
-      output_to ("lib/" ^ filename) generate_implementation ep
+        output_to ("lib/" ^ filename) generate_implementation ep
+      )
   );
 
   let gitignores = List.rev !gitignores in
index 51ea85c..76d4e3e 100644 (file)
@@ -1,8 +1,8 @@
 /error-clear_error.c
-/handle-connect.c
 /error-error.c
 /filesize-filesize.c
+/error-get_errno.c
+/error-get_error.c
+/error-get_error_func.c
 /mkdir-mkdir.c
 /mknod-mknod_char.c
-/handle-set_hostname.c
-/handle-set_scheme.c
index d634a20..41f197d 100644 (file)
@@ -29,5 +29,9 @@ libwrappi_la_LDFLAGS = -version-info $(LIB_MINOR):0:$(LIB_MINOR)
 libwrappi_la_SOURCES = \
        wrappi.h \
        wrappi.c \
+       connect.c \
        $(local_implementation_files) \
        $(remote_implementation_files)
+libwrappi_la_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
+
+CLEANFILES = *~
diff --git a/lib/connect.c b/lib/connect.c
new file mode 100644 (file)
index 0000000..a57fcfa
--- /dev/null
@@ -0,0 +1,45 @@
+/* wrappi
+ * Copyright (C) 2011-2012 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "wrappi.h"
+#include "internal.h"
+
+void
+wrap_connect (wrap_h *w)
+{
+  /* Remote not implemented yet. */
+  assert (w->scheme == NULL);
+}
+
+void
+wrap_set_scheme (wrap_h *w, const char *scheme)
+{
+  /* XXX */
+}
+
+void
+wrap_set_hostname (wrap_h *w, const char *hostname)
+{
+  /* XXX */
+}
index 4631ce5..04c4a17 100644 (file)
 
 local_implementation_files := \
        error-clear_error.c \
-       handle-connect.c \
        error-error.c \
-       handle-set_hostname.c \
-       handle-set_scheme.c
+       error-get_errno.c \
+       error-get_error.c \
+       error-get_error_func.c
 
 remote_implementation_files := \
        filesize-filesize.c \
index 2213d7b..b9cc57c 100644 (file)
 #ifndef WRAPPI_INTERNAL_H_
 #define WRAPPI_INTERNAL_H_
 
+#include <string.h>
+#include <errno.h>
+
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
+#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
+#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
+#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
+#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
+#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
+#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
+#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
+
+struct wrap_internal_h {
+  char *error;          /* Last error on this handle, NULL is none. */
+  int errnum;           /* errno, or 0 if there was no errno set. */
+  const char *error_func; /* Function where the error occurred, if known. */
+};
+
 struct wrap_h {
-  int error_flag;
+  /* The "internal" part of the handle must always appear at the
+   * beginning of the handle structure.  For the remote case, this
+   * contains a cut-down handle which is what non-local entry points
+   * are permitted to touch.  For the local case, the full handle is
+   * cast to (struct wrap_internal_h *).
+   */
+  struct wrap_internal_h internal;
+
+  /* Fields that follow the 'internal' structure only exist
+   * on the local side of a connection.
+   */
+
+  /* Connection URL.  If scheme = NULL, it means we're using the local
+   * code.
+   */
+  const char *scheme;
+  const char *hostname;
 };
 
+/* Declare an error, setting the error field in the handle. */
+#define set_error(fs...) \
+  wrap_int_set_error ((struct wrap_internal_h *)w, __func__, fs)
+#define set_error_errno(fs...) \
+  wrap_int_set_error_errno ((struct wrap_internal_h *)w, (errno), __func__, fs)
+
+extern void wrap_int_set_error (struct wrap_internal_h *w, const char *func, const char *fs, ...);
+extern void wrap_int_set_error_errno (struct wrap_internal_h *w, int errnum, const char *func, const char *fs, ...);
+
 #endif /* WRAPPI_INTERNAL_H_ */
index ff223b3..0e45890 100644 (file)
@@ -20,9 +20,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 
 #include "wrappi.h"
-
 #include "internal.h"
 
 wrap_h *
@@ -40,3 +40,61 @@ wrap_close (wrap_h *w)
 {
   free (w);
 }
+
+static void
+set_error_buf (struct wrap_internal_h *w,
+               char *buf, int errnum, const char *func)
+{
+  free (w->error);
+  w->error = buf;
+  w->errnum = errnum;
+
+  /* 'func' is always statically allocated, since it comes from
+   * C99 __func__, so this is safe.
+   */
+  if (STRPREFIX (func, "impl_"))
+    func += 5;
+  else if (STRPREFIX (func, "wrap_"))
+    func += 5;
+  w->error_func = func;
+
+  /* XXX This will be conditional on the user setting an error handler. */
+  fprintf (stderr, "wrappi: %s: %s\n", func, buf);
+}
+
+void
+wrap_int_set_error (struct wrap_internal_h *w,
+                    const char *func,
+                    const char *fs, ...)
+{
+  va_list args;
+  char *msg;
+  int err;
+
+  va_start (args, fs);
+  err = vasprintf (&msg, fs, args);
+  va_end (args);
+
+  if (err < 0) return;
+
+  set_error_buf (w, msg, 0, func);
+}
+
+void
+wrap_int_set_error_errno (struct wrap_internal_h *w,
+                          int errnum,
+                          const char *func,
+                          const char *fs, ...)
+{
+  va_list args;
+  char *msg;
+  int err;
+
+  va_start (args, fs);
+  err = vasprintf (&msg, fs, args);
+  va_end (args);
+
+  if (err < 0) return;
+
+  set_error_buf (w, msg, errnum, func);
+}