#load "unix.cma";;
#load "str.cma";;
-#directory "+xml-light";;
-#load "xml-light.cma";;
open Unix
open Printf
| AOpenFlags (* HIVEX_OPEN_* flags list. *)
| AUnusedFlags (* Flags arg that is always 0 *)
| ASetValues (* See hivex_node_set_values. *)
+ | ASetValue (* See hivex_node_set_value. *)
(* Hive types, from:
* https://secure.wikimedia.org/wikipedia/en/wiki/Windows_Registry#Keys_and_values
"set (key, value) pairs at a node",
"\
This call can be used to set all the (key, value) pairs
-stored in C<node>. Note that this library does not offer
-a way to modify just a single key at a node.
+stored in C<node>.
+
+C<node> is the node to modify.";
+
+ "node_set_value", (RErr, [AHive; ANode "node"; ASetValue; AUnusedFlags]),
+ "set a single (key, value) pair at a given node",
+ "\
+This call can be used to replace a single (key, value) pair
+stored in C<node>. If the key does not already exist, then a
+new key is added. Key matching is case insensitive.
C<node> is the node to modify.";
]
| ANode n | AValue n | AString n | AStringNullable n -> n
| AOpenFlags | AUnusedFlags -> "flags"
| ASetValues -> "values"
+ | ASetValue -> "val"
(* Check function names etc. for consistency. *)
let check_functions () =
| AString n | AStringNullable n -> pr "const char *%s" n
| AOpenFlags | AUnusedFlags -> pr "int flags"
| ASetValues -> pr "size_t nr_values, const hive_set_value *values"
+ | ASetValue -> pr "const hive_set_value *val"
) (snd style);
(match fst style with
| RLenType | RLenTypeVal -> pr ", hive_type *t, size_t *len"
C<hive_value_h> handles become invalid. Thus you can remove all
values stored at C<node> by passing C<nr_values = 0>.\n\n";
+ if List.mem ASetValue (snd style) then
+ pr "C<value> is a single (key, value) pair.
+
+Existing C<hive_value_h> handles become invalid.\n\n";
+
(match fst style with
| RErr ->
pr "\
| RLenType ->
pr "\
Returns 0 on success.
-On error this returns NULL and sets errno.\n\n"
+On error this returns -1 and sets errno.\n\n"
| RLenTypeVal ->
pr "\
The value is returned as an array of bytes (of length C<len>).
Lesser General Public License for more details.
"
+(* Generate the linker script which controls the visibility of
+ * symbols in the public ABI and ensures no other symbols get
+ * exported accidentally.
+ *)
+and generate_linker_script () =
+ generate_header HashStyle GPLv2plus;
+
+ let globals = [
+ "hivex_visit";
+ "hivex_visit_node"
+ ] in
+
+ let functions =
+ List.map (fun (name, _, _, _) -> "hivex_" ^ name)
+ functions in
+ let globals = List.sort compare (globals @ functions) in
+
+ pr "{\n";
+ pr " global:\n";
+ List.iter (pr " %s;\n") globals;
+ pr "\n";
+
+ pr " local:\n";
+ pr " *;\n";
+ pr "};\n"
+
and generate_ocaml_interface () =
generate_header OCamlStyle LGPLv2plus;
| AOpenFlags -> pr "open_flag list -> "
| AUnusedFlags -> ()
| ASetValues -> pr "set_value array -> "
+ | ASetValue -> pr "set_value -> "
) (snd style);
(match fst style with
| RErr -> pr "unit" (* all errors are turned into exceptions *)
#include <caml/memory.h>
#include <caml/mlvalues.h>
#include <caml/signals.h>
+
+#ifdef HAVE_CAML_UNIXSUPPORT_H
#include <caml/unixsupport.h>
+#else
+extern value unix_error_of_code (int errcode);
+#endif
+
+#ifndef HAVE_CAML_RAISE_WITH_ARGS
+static void
+caml_raise_with_args (value tag, int nargs, value args[])
+{
+ CAMLparam1 (tag);
+ CAMLxparamN (args, nargs);
+ value bucket;
+ int i;
+
+ bucket = caml_alloc_small (1 + nargs, 0);
+ Field(bucket, 0) = tag;
+ for (i = 0; i < nargs; i++) Field(bucket, 1 + i) = args[i];
+ caml_raise(bucket);
+ CAMLnoreturn;
+}
+#endif
#include <hivex.h>
#define Hiveh_val(v) (*((hive_h **)Data_custom_val(v)))
static value Val_hiveh (hive_h *);
static int HiveOpenFlags_val (value);
+static hive_set_value *HiveSetValue_val (value);
static hive_set_value *HiveSetValues_val (value);
static hive_type HiveType_val (value);
static value Val_hive_type (hive_type);
| ASetValues ->
pr " int nrvalues = Wosize_val (valuesv);\n";
pr " hive_set_value *values = HiveSetValues_val (valuesv);\n"
+ | ASetValue ->
+ pr " hive_set_value *val = HiveSetValue_val (valv);\n"
) (snd style);
pr "\n";
| ASetValues ->
pr " free (values);\n";
pr "\n";
+ | ASetValue ->
+ pr " free (val);\n";
+ pr "\n";
) (snd style);
(* Check for errors. *)
}
static hive_set_value *
+HiveSetValue_val (value v)
+{
+ hive_set_value *val = malloc (sizeof (hive_set_value));
+
+ val->key = String_val (Field (v, 0));
+ val->t = HiveType_val (Field (v, 1));
+ val->len = caml_string_length (Field (v, 2));
+ val->value = String_val (Field (v, 2));
+
+ return val;
+}
+
+static hive_set_value *
HiveSetValues_val (value v)
{
size_t nr_values = Wosize_val (v);
=item open
- $h = Win::Hivex::open ($filename,";
+ $h = Win::Hivex->open ($filename,";
List.iter (
fun (_, flag, _) ->
| AOpenFlags -> pr "[flags]"
| AUnusedFlags -> assert false
| ASetValues -> pr "\\@values"
+ | ASetValue -> pr "$val"
) args;
pr ")"
return ret;
}
+static hive_set_value *
+unpack_set_value (SV *sv)
+{
+ hive_set_value *ret;
+
+ if (!sv || !SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVHV)
+ croak (\"not a hash ref\");
+
+ ret = malloc (sizeof (hive_set_value));
+ if (ret == NULL)
+ croak (\"malloc failed\");
+
+ HV *hv = (HV *)SvRV(sv);
+
+ SV **svp;
+ svp = hv_fetch (hv, \"key\", 3, 0);
+ if (!svp || !*svp)
+ croak (\"missing 'key' in hash\");
+ ret->key = SvPV_nolen (*svp);
+
+ svp = hv_fetch (hv, \"t\", 1, 0);
+ if (!svp || !*svp)
+ croak (\"missing 't' in hash\");
+ ret->t = SvIV (*svp);
+
+ svp = hv_fetch (hv, \"value\", 5, 0);
+ if (!svp || !*svp)
+ croak (\"missing 'value' in hash\");
+ ret->value = SvPV (*svp, ret->len);
+
+ return ret;
+}
+
MODULE = Win::Hivex PACKAGE = Win::Hivex
PROTOTYPES: ENABLE
| AUnusedFlags -> ()
| ASetValues ->
pr " pl_set_values values = unpack_pl_set_values (ST(%d));\n" i
+ | ASetValue ->
+ pr " hive_set_value *val = unpack_set_value (ST(%d));\n" i
) (snd style);
let free_args () =
function
| ASetValues ->
pr " free (values.values);\n"
+ | ASetValue ->
+ pr " free (val);\n"
| AHive | ANode _ | AValue _ | AString _ | AStringNullable _
| AOpenFlags | AUnusedFlags -> ()
) (snd style)
| RErrDispose -> assert false
| RHive -> assert false
- | RInt32
| RNode
| RValue ->
pr "PREINIT:\n";
pr " size_t len;\n";
pr " hive_type type;\n";
pr " PPCODE:\n";
- pr " r = hivex_%s (%s, &len, &type);\n"
+ pr " r = hivex_%s (%s, &type, &len);\n"
name (String.concat ", " c_params);
free_args ();
pr " if (r == -1)\n";
pr " size_t len;\n";
pr " hive_type type;\n";
pr " PPCODE:\n";
- pr " r = hivex_%s (%s, &len, &type);\n"
+ pr " r = hivex_%s (%s, &type, &len);\n"
name (String.concat ", " c_params);
free_args ();
pr " if (r == NULL)\n";
name;
pr " EXTEND (SP, 2);\n";
pr " PUSHs (sv_2mortal (newSViv (type)));\n";
- pr " PUSHs (sv_2mortal (newSVpv (r, len)));\n";
+ pr " PUSHs (sv_2mortal (newSVpvn (r, len)));\n";
pr " free (r);\n";
+ | RInt32 ->
+ pr "PREINIT:\n";
+ pr " int32_t r;\n";
+ pr " CODE:\n";
+ pr " errno = 0;\n";
+ pr " r = hivex_%s (%s);\n"
+ name (String.concat ", " c_params);
+ free_args ();
+ pr " if (r == -1 && errno != 0)\n";
+ pr " croak (\"%%s: %%s\", \"%s\", strerror (errno));\n"
+ name;
+ pr " RETVAL = newSViv (r);\n";
+ pr " OUTPUT:\n";
+ pr " RETVAL\n"
+
| RInt64 ->
pr "PREINIT:\n";
pr " int64_t r;\n";
)
) functions
+and generate_python_c () =
+ generate_header CStyle LGPLv2plus;
+
+ pr "\
+#define PY_SSIZE_T_CLEAN 1
+#include <Python.h>
+
+#if PY_VERSION_HEX < 0x02050000
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include \"hivex.h\"
+
+#ifndef HAVE_PYCAPSULE_NEW
+typedef struct {
+ PyObject_HEAD
+ hive_h *h;
+} Pyhivex_Object;
+#endif
+
+static hive_h *
+get_handle (PyObject *obj)
+{
+ assert (obj);
+ assert (obj != Py_None);
+#ifndef HAVE_PYCAPSULE_NEW
+ return ((Pyhivex_Object *) obj)->h;
+#else
+ return (hive_h *) PyCapsule_GetPointer(obj, \"hive_h\");
+#endif
+}
+
+static PyObject *
+put_handle (hive_h *h)
+{
+ assert (h);
+#ifndef HAVE_PYCAPSULE_NEW
+ return
+ PyCObject_FromVoidPtrAndDesc ((void *) h, (char *) \"hive_h\", NULL);
+#else
+ return PyCapsule_New ((void *) h, \"hive_h\", NULL);
+#endif
+}
+
+/* This returns pointers into the Python objects, which should
+ * not be freed.
+ */
+static int
+get_value (PyObject *v, hive_set_value *ret)
+{
+ PyObject *obj;
+
+ obj = PyDict_GetItemString (v, \"key\");
+ if (!obj) {
+ PyErr_SetString (PyExc_RuntimeError, \"no 'key' element in dictionary\");
+ return -1;
+ }
+ if (!PyString_Check (obj)) {
+ PyErr_SetString (PyExc_RuntimeError, \"'key' element is not a string\");
+ return -1;
+ }
+ ret->key = PyString_AsString (obj);
+
+ obj = PyDict_GetItemString (v, \"t\");
+ if (!obj) {
+ PyErr_SetString (PyExc_RuntimeError, \"no 't' element in dictionary\");
+ return -1;
+ }
+ if (!PyInt_Check (obj)) {
+ PyErr_SetString (PyExc_RuntimeError, \"'t' element is not an integer\");
+ return -1;
+ }
+ ret->t = PyInt_AsLong (obj);
+
+ obj = PyDict_GetItemString (v, \"value\");
+ if (!obj) {
+ PyErr_SetString (PyExc_RuntimeError, \"no 'value' element in dictionary\");
+ return -1;
+ }
+ if (!PyString_Check (obj)) {
+ PyErr_SetString (PyExc_RuntimeError, \"'value' element is not a string\");
+ return -1;
+ }
+ ret->value = PyString_AsString (obj);
+ ret->len = PyString_Size (obj);
+
+ return 0;
+}
+
+typedef struct py_set_values {
+ size_t nr_values;
+ hive_set_value *values;
+} py_set_values;
+
+static int
+get_values (PyObject *v, py_set_values *ret)
+{
+ Py_ssize_t slen;
+ size_t len, i;
+
+ if (!PyList_Check (v)) {
+ PyErr_SetString (PyExc_RuntimeError, \"expecting a list parameter\");
+ return -1;
+ }
+
+ slen = PyList_Size (v);
+ if (slen < 0) {
+ PyErr_SetString (PyExc_RuntimeError, \"get_string_list: PyList_Size failure\");
+ return -1;
+ }
+ len = (size_t) slen;
+ ret->nr_values = len;
+ ret->values = malloc (len * sizeof (hive_set_value));
+ if (!ret->values) {
+ PyErr_SetString (PyExc_RuntimeError, strerror (errno));
+ return -1;
+ }
+
+ for (i = 0; i < len; ++i) {
+ if (get_value (PyList_GetItem (v, i), &(ret->values[i])) == -1) {
+ free (ret->values);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static PyObject *
+put_string_list (char * const * const argv)
+{
+ PyObject *list;
+ size_t argc, i;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ ;
+
+ list = PyList_New (argc);
+ for (i = 0; i < argc; ++i)
+ PyList_SetItem (list, i, PyString_FromString (argv[i]));
+
+ return list;
+}
+
+static void
+free_strings (char **argv)
+{
+ size_t argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ free (argv[argc]);
+ free (argv);
+}
+
+/* Since hive_node_t is the same as hive_value_t this also works for values. */
+static PyObject *
+put_node_list (hive_node_h *nodes)
+{
+ PyObject *list;
+ size_t argc, i;
+
+ for (argc = 0; nodes[argc] != 0; ++argc)
+ ;
+
+ list = PyList_New (argc);
+ for (i = 0; i < argc; ++i)
+ PyList_SetItem (list, i, PyLong_FromLongLong ((long) nodes[i]));
+
+ return list;
+}
+
+static PyObject *
+put_len_type (size_t len, hive_type t)
+{
+ PyObject *r = PyTuple_New (2);
+ PyTuple_SetItem (r, 0, PyInt_FromLong ((long) t));
+ PyTuple_SetItem (r, 1, PyLong_FromLongLong ((long) len));
+ return r;
+}
+
+static PyObject *
+put_val_type (char *val, size_t len, hive_type t)
+{
+ PyObject *r = PyTuple_New (2);
+ PyTuple_SetItem (r, 0, PyInt_FromLong ((long) t));
+ PyTuple_SetItem (r, 1, PyString_FromStringAndSize (val, len));
+ return r;
+}
+
+";
+
+ (* Generate functions. *)
+ List.iter (
+ fun (name, style, _, longdesc) ->
+ pr "static PyObject *\n";
+ pr "py_hivex_%s (PyObject *self, PyObject *args)\n" name;
+ pr "{\n";
+ pr " PyObject *py_r;\n";
+
+ let error_code =
+ match fst style with
+ | RErr -> pr " int r;\n"; "-1"
+ | RErrDispose -> pr " int r;\n"; "-1"
+ | RHive -> pr " hive_h *r;\n"; "NULL"
+ | RNode -> pr " hive_node_h r;\n"; "0"
+ | RNodeNotFound ->
+ pr " errno = 0;\n";
+ pr " hive_node_h r;\n";
+ "0 && errno != 0"
+ | RNodeList -> pr " hive_node_h *r;\n"; "NULL"
+ | RValue -> pr " hive_value_h r;\n"; "0"
+ | RValueList -> pr " hive_value_h *r;\n"; "NULL"
+ | RString -> pr " char *r;\n"; "NULL"
+ | RStringList -> pr " char **r;\n"; "NULL"
+ | RLenType ->
+ pr " int r;\n";
+ pr " size_t len;\n";
+ pr " hive_type t;\n";
+ "-1"
+ | RLenTypeVal ->
+ pr " char *r;\n";
+ pr " size_t len;\n";
+ pr " hive_type t;\n";
+ "NULL"
+ | RInt32 ->
+ pr " errno = 0;\n";
+ pr " int32_t r;\n";
+ "-1 && errno != 0"
+ | RInt64 ->
+ pr " errno = 0;\n";
+ pr " int64_t r;\n";
+ "-1 && errno != 0" in
+
+ (* Call and arguments. *)
+ let c_params =
+ List.map (function
+ | AUnusedFlags -> "0"
+ | ASetValues -> "values.nr_values, values.values"
+ | ASetValue -> "&val"
+ | arg -> name_of_argt arg) (snd style) in
+ let c_params =
+ match fst style with
+ | RLenType | RLenTypeVal -> c_params @ ["&t"; "&len"]
+ | _ -> c_params in
+
+ List.iter (
+ function
+ | AHive ->
+ pr " hive_h *h;\n";
+ pr " PyObject *py_h;\n"
+ | ANode n
+ | AValue n ->
+ pr " long %s;\n" n
+ | AString n
+ | AStringNullable n ->
+ pr " char *%s;\n" n
+ | AOpenFlags ->
+ pr " int flags;\n"
+ | AUnusedFlags -> ()
+ | ASetValues ->
+ pr " py_set_values values;\n";
+ pr " PyObject *py_values;\n"
+ | ASetValue ->
+ pr " hive_set_value val;\n";
+ pr " PyObject *py_val;\n"
+ ) (snd style);
+
+ pr "\n";
+
+ (* Convert the required parameters. *)
+ pr " if (!PyArg_ParseTuple (args, (char *) \"";
+ List.iter (
+ function
+ | AHive ->
+ pr "O"
+ | ANode n
+ | AValue n ->
+ pr "L"
+ | AString n ->
+ pr "s"
+ | AStringNullable n ->
+ pr "z"
+ | AOpenFlags ->
+ pr "i"
+ | AUnusedFlags -> ()
+ | ASetValues
+ | ASetValue ->
+ pr "O"
+ ) (snd style);
+
+ pr ":hivex_%s\"" name;
+
+ List.iter (
+ function
+ | AHive ->
+ pr ", &py_h"
+ | ANode n
+ | AValue n ->
+ pr ", &%s" n
+ | AString n
+ | AStringNullable n ->
+ pr ", &%s" n
+ | AOpenFlags ->
+ pr ", &flags"
+ | AUnusedFlags -> ()
+ | ASetValues ->
+ pr ", &py_values"
+ | ASetValue ->
+ pr ", &py_val"
+ ) (snd style);
+
+ pr "))\n";
+ pr " return NULL;\n";
+
+ (* Convert some Python argument types to C. *)
+ List.iter (
+ function
+ | AHive ->
+ pr " h = get_handle (py_h);\n"
+ | ANode _
+ | AValue _
+ | AString _
+ | AStringNullable _
+ | AOpenFlags
+ | AUnusedFlags -> ()
+ | ASetValues ->
+ pr " if (get_values (py_values, &values) == -1)\n";
+ pr " return NULL;\n"
+ | ASetValue ->
+ pr " if (get_value (py_val, &val) == -1)\n";
+ pr " return NULL;\n"
+ ) (snd style);
+
+ (* Call the C function. *)
+ pr " r = hivex_%s (%s);\n" name (String.concat ", " c_params);
+
+ (* Free up arguments. *)
+ List.iter (
+ function
+ | AHive | ANode _ | AValue _
+ | AString _ | AStringNullable _
+ | AOpenFlags | AUnusedFlags -> ()
+ | ASetValues ->
+ pr " free (values.values);\n"
+ | ASetValue -> ()
+ ) (snd style);
+
+ (* Check for errors from C library. *)
+ pr " if (r == %s) {\n" error_code;
+ pr " PyErr_SetString (PyExc_RuntimeError,\n";
+ pr " strerror (errno));\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr "\n";
+
+ (* Convert return value to Python. *)
+ (match fst style with
+ | RErr
+ | RErrDispose ->
+ pr " Py_INCREF (Py_None);\n";
+ pr " py_r = Py_None;\n"
+ | RHive ->
+ pr " py_r = put_handle (r);\n"
+ | RNode ->
+ pr " py_r = PyLong_FromLongLong (r);\n"
+ | RNodeNotFound ->
+ pr " if (r)\n";
+ pr " py_r = PyLong_FromLongLong (r);\n";
+ pr " else {\n";
+ pr " Py_INCREF (Py_None);\n";
+ pr " py_r = Py_None;\n";
+ pr " }\n";
+ | RNodeList
+ | RValueList ->
+ pr " py_r = put_node_list (r);\n";
+ pr " free (r);\n"
+ | RValue ->
+ pr " py_r = PyLong_FromLongLong (r);\n"
+ | RString ->
+ pr " py_r = PyString_FromString (r);\n";
+ pr " free (r);"
+ | RStringList ->
+ pr " py_r = put_string_list (r);\n";
+ pr " free_strings (r);\n"
+ | RLenType ->
+ pr " py_r = put_len_type (len, t);\n"
+ | RLenTypeVal ->
+ pr " py_r = put_val_type (r, len, t);\n";
+ pr " free (r);\n"
+ | RInt32 ->
+ pr " py_r = PyInt_FromLong ((long) r);\n"
+ | RInt64 ->
+ pr " py_r = PyLong_FromLongLong (r);\n"
+ );
+ pr " return py_r;\n";
+ pr "}\n";
+ pr "\n"
+ ) functions;
+
+ (* Table of functions. *)
+ pr "static PyMethodDef methods[] = {\n";
+ List.iter (
+ fun (name, _, _, _) ->
+ pr " { (char *) \"%s\", py_hivex_%s, METH_VARARGS, NULL },\n"
+ name name
+ ) functions;
+ pr " { NULL, NULL, 0, NULL }\n";
+ pr "};\n";
+ pr "\n";
+
+ (* Init function. *)
+ pr "\
+void
+initlibhivexmod (void)
+{
+ static int initialized = 0;
+
+ if (initialized) return;
+ Py_InitModule ((char *) \"libhivexmod\", methods);
+ initialized = 1;
+}
+"
+
and generate_python_py () =
- generate_header HashStyle LGPLv2plus
+ generate_header HashStyle LGPLv2plus;
-and generate_python_c () =
- generate_header CStyle LGPLv2plus
+ pr "\
+u\"\"\"Python bindings for hivex
+
+import hivex
+h = hivex.Hivex (filename)
+
+The hivex module provides Python bindings to the hivex API for
+examining and modifying Windows Registry 'hive' files.
+
+Read the hivex(3) man page to find out how to use the API.
+\"\"\"
+
+import libhivexmod
+
+class Hivex:
+ \"\"\"Instances of this class are hivex API handles.\"\"\"
+
+ def __init__ (self, filename";
+
+ List.iter (
+ fun (_, flag, _) -> pr ", %s = False" (String.lowercase flag)
+ ) open_flags;
+
+ pr "):
+ \"\"\"Create a new hivex handle.\"\"\"
+ flags = 0
+";
+
+ List.iter (
+ fun (n, flag, description) ->
+ pr " # %s\n" description;
+ pr " if %s: flags += %d\n" (String.lowercase flag) n
+ ) open_flags;
+
+ pr " self._o = libhivexmod.open (filename, flags)
+
+ def __del__ (self):
+ libhivexmod.close (self._o)
+
+";
+
+ List.iter (
+ fun (name, style, shortdesc, _) ->
+ (* The close and open calls are handled specially above. *)
+ if fst style <> RErrDispose && List.hd (snd style) = AHive then (
+ let args = List.tl (snd style) in
+ let args = List.filter (
+ function AOpenFlags | AUnusedFlags -> false
+ | _ -> true
+ ) args in
+
+ pr " def %s (self" name;
+ List.iter (fun arg -> pr ", %s" (name_of_argt arg)) args;
+ pr "):\n";
+ pr " u\"\"\"%s\"\"\"\n" shortdesc;
+ pr " return libhivexmod.%s (self._o" name;
+ List.iter (
+ fun arg ->
+ pr ", ";
+ match arg with
+ | AHive -> assert false
+ | ANode n | AValue n
+ | AString n | AStringNullable n -> pr "%s" n
+ | AOpenFlags
+ | AUnusedFlags -> assert false
+ | ASetValues -> pr "values"
+ | ASetValue -> pr "val"
+ ) args;
+ pr ")\n";
+ pr "\n"
+ )
+ ) functions
let output_to filename k =
let filename_new = filename ^ ".new" in
output_to "lib/hivex.h" generate_c_header;
output_to "lib/hivex.pod" generate_c_pod;
+ output_to "lib/hivex.syms" generate_linker_script;
+
output_to "ocaml/hivex.mli" generate_ocaml_interface;
output_to "ocaml/hivex.ml" generate_ocaml_implementation;
output_to "ocaml/hivex_c.c" generate_ocaml_c;
output_to "perl/lib/Win/Hivex.pm" generate_perl_pm;
output_to "perl/Hivex.xs" generate_perl_xs;
-(*
- We ran out of time before we could write the Python bindings.
output_to "python/hivex.py" generate_python_py;
output_to "python/hivex-py.c" generate_python_c;
-*)
(* Always generate this file last, and unconditionally. It's used
* by the Makefile to know when we must re-run the generator.