X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=generator%2Fgenerator.ml;h=dd13d6ae1b957395444ee44f16d8592f2a1182dd;hb=584fc6fc9857cf493120aebe514f73f3a143234b;hp=d8efc041c4fd8c617a9a84c4bdc5ea8058148c70;hpb=422d46d06a84d0b8194a53de4ad242dbeea85c52;p=hivex.git diff --git a/generator/generator.ml b/generator/generator.ml index d8efc04..dd13d6a 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -45,6 +45,7 @@ and ret = | RErr (* 0 = ok, -1 = error *) | RErrDispose (* Disposes handle, see hivex_close. *) | RHive (* Returns a hive_h or NULL. *) + | RSize (* Returns size_t or 0. *) | RNode (* Returns hive_node_h or 0. *) | RNodeNotFound (* See hivex_node_get_child. *) | RNodeList (* Returns hive_node_h* or NULL. *) @@ -228,6 +229,17 @@ string C<\"\"> here. The default key is often written C<\"@\">, but inside hives that has no meaning and won't give you the default key."; + "value_key_len", (RSize, [AHive; AValue "val"]), + "return the length of a value's key", + "\ +Return the length of the key (name) of a (key, value) pair. The +length can legitimately be 0, so errno is the necesary mechanism +to check for errors. + +In the context of Windows Registries, a zero-length name means +that this value is the default key for this node in the tree. +This is usually written as C<\"@\">."; + "value_key", (RString, [AHive; AValue "val"]), "return the key of a (key, value) pair", "\ @@ -250,6 +262,16 @@ information, and the value itself. Also, C functions below which can be used to return the value in a more useful form when you know the type in advance."; + "node_struct_length", (RSize, [AHive; ANode "node"]), + "return the length of a node", + "\ +Return the length of the node data structure."; + + "value_struct_length", (RSize, [AHive; AValue "val"]), + "return the length of a value data structure", + "\ +Return the length of the value data structure."; + "value_value", (RLenTypeVal, [AHive; AValue "val"]), "return data length, data type and data of a value", "\ @@ -334,21 +356,6 @@ new key is added. Key matching is case insensitive. C is the node to modify."; ] -(* Used to memoize the result of pod2text. *) -let pod2text_memo_filename = "generator/.pod2text.data" -let pod2text_memo : ((int * string * string), string list) Hashtbl.t = - try - let chan = open_in pod2text_memo_filename in - let v = input_value chan in - close_in chan; - v - with - _ -> Hashtbl.create 13 -let pod2text_memo_updated () = - let chan = open_out pod2text_memo_filename in - output_value chan pod2text_memo; - close_out chan - (* Useful functions. * Note we don't want to use any external OCaml libraries which * makes this a bit harder than it should be. @@ -395,6 +402,64 @@ let trimr ?(test = isspace) str = let trim ?(test = isspace) str = trimr ~test (triml ~test str) +(* Used to memoize the result of pod2text. *) +let pod2text_memo_filename = "generator/.pod2text.data.version.2" +let pod2text_memo : ((int option * bool * bool * string * string), string list) Hashtbl.t = + try + let chan = open_in pod2text_memo_filename in + let v = input_value chan in + close_in chan; + v + with + _ -> Hashtbl.create 13 +let pod2text_memo_updated () = + let chan = open_out pod2text_memo_filename in + output_value chan pod2text_memo; + close_out chan + +(* Useful if you need the longdesc POD text as plain text. Returns a + * list of lines. + * + * Because this is very slow (the slowest part of autogeneration), + * we memoize the results. + *) +let pod2text ?width ?(trim = true) ?(discard = true) name longdesc = + let key = width, trim, discard, name, longdesc in + try Hashtbl.find pod2text_memo key + with Not_found -> + let filename, chan = Filename.open_temp_file "gen" ".tmp" in + fprintf chan "=head1 %s\n\n%s\n" name longdesc; + close_out chan; + let cmd = + match width with + | Some width -> + sprintf "pod2text -w %d %s" width (Filename.quote filename) + | None -> + sprintf "pod2text %s" (Filename.quote filename) in + let chan = open_process_in cmd in + let lines = ref [] in + let rec loop i = + let line = input_line chan in + if i = 1 && discard then (* discard the first line of output *) + loop (i+1) + else ( + let line = if trim then triml line else line in + lines := line :: !lines; + loop (i+1) + ) in + let lines = try loop 1 with End_of_file -> List.rev !lines in + unlink filename; + (match close_process_in chan with + | WEXITED 0 -> () + | WEXITED i -> + failwithf "pod2text: process exited with non-zero status (%d)" i + | WSIGNALED i | WSTOPPED i -> + failwithf "pod2text: process signalled or stopped by signal %d" i + ); + Hashtbl.add pod2text_memo key lines; + pod2text_memo_updated (); + lines + let rec find s sub = let len = String.length s in let sublen = String.length sub in @@ -815,6 +880,7 @@ and generate_c_prototype ?(extern = false) name style = | RErr -> pr "int " | RErrDispose -> pr "int " | RHive -> pr "hive_h *" + | RSize -> pr "size_t " | RNode -> pr "hive_node_h " | RNodeNotFound -> pr "hive_node_h " | RNodeList -> pr "hive_node_h *" @@ -894,7 +960,7 @@ If you just want to export or modify the Registry of a Windows virtual machine, you should look at L. Hivex is also comes with language bindings for -OCaml, Perl and Python. +OCaml, Perl, Python and Ruby. =head1 TYPES @@ -1003,6 +1069,10 @@ The hive handle must not be used again after calling this function.\n\n" pr "\ Returns a new hive handle. On error this returns NULL and sets errno.\n\n" + | RSize -> + pr "\ +Returns a size. +On error this returns 0 and sets errno.\n\n" | RNode -> pr "\ Returns a node handle. @@ -1543,6 +1613,7 @@ and generate_ocaml_prototype ?(is_external = false) name style = | RErr -> pr "unit" (* all errors are turned into exceptions *) | RErrDispose -> pr "unit" | RHive -> pr "t" + | RSize -> pr "int64" | RNode -> pr "node" | RNodeNotFound -> pr "node" | RNodeList -> pr "node array" @@ -1692,6 +1763,7 @@ static void raise_closed (const char *) Noreturn; | RErr -> pr " int r;\n"; "-1" | RErrDispose -> pr " int r;\n"; "-1" | RHive -> pr " hive_h *r;\n"; "NULL" + | RSize -> pr " size_t r;\n"; "0" | RNode -> pr " hive_node_h r;\n"; "0" | RNodeNotFound -> pr " errno = 0;\n"; @@ -1765,6 +1837,7 @@ static void raise_closed (const char *) Noreturn; | RErr -> pr " rv = Val_unit;\n" | RErrDispose -> pr " rv = Val_unit;\n" | RHive -> pr " rv = Val_hiveh (r);\n" + | RSize -> pr " rv = caml_copy_int64 (r);\n" | RNode -> pr " rv = Val_int (r);\n" | RNodeNotFound -> pr " if (r == 0)\n"; @@ -1903,7 +1976,7 @@ copy_type_len (size_t len, hive_type t) v = Val_hive_type (t); Store_field (rv, 0, v); v = Val_int (len); - Store_field (rv, 1, len); + Store_field (rv, 1, v); CAMLreturn (rv); } @@ -2100,6 +2173,9 @@ sub open { | RLenTypeVal | RInt32 | RInt64 -> () + | RSize -> + pr "\ +This returns a size.\n\n" | RNode -> pr "\ This returns a node handle.\n\n" @@ -2159,6 +2235,7 @@ and generate_perl_prototype name style = | RErr | RErrDispose -> () | RHive -> pr "$h = " + | RSize -> pr "$size = " | RNode | RNodeNotFound -> pr "$node = " | RNodeList -> pr "@nodes = " @@ -2381,6 +2458,7 @@ DESTROY (h) | RErr -> pr "void\n" | RErrDispose -> failwith "perl bindings cannot handle a call which disposes of the handle" | RHive -> failwith "perl bindings cannot handle a call which returns a handle" + | RSize | RNode | RNodeNotFound | RValue @@ -2457,6 +2535,7 @@ DESTROY (h) | RErrDispose -> assert false | RHive -> assert false + | RSize | RNode | RValue -> pr "PREINIT:\n"; @@ -2615,6 +2694,8 @@ and generate_python_c () = generate_header CStyle LGPLv2plus; pr "\ +#include + #define PY_SSIZE_T_CLEAN 1 #include @@ -2668,40 +2749,42 @@ static int get_value (PyObject *v, hive_set_value *ret) { PyObject *obj; +#ifndef HAVE_PYSTRING_ASSTRING + PyObject *bytes; +#endif 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; - } +#ifdef HAVE_PYSTRING_ASSTRING ret->key = PyString_AsString (obj); +#else + bytes = PyUnicode_AsUTF8String (obj); + ret->key = PyBytes_AS_STRING (bytes); +#endif 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); + ret->t = PyLong_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; - } +#ifdef HAVE_PYSTRING_ASSTRING ret->value = PyString_AsString (obj); ret->len = PyString_Size (obj); +#else + bytes = PyUnicode_AsUTF8String (obj); + ret->value = PyBytes_AS_STRING (bytes); + ret->len = PyBytes_GET_SIZE (bytes); +#endif return 0; } @@ -2755,8 +2838,13 @@ put_string_list (char * const * const argv) ; list = PyList_New (argc); - for (i = 0; i < argc; ++i) + for (i = 0; i < argc; ++i) { +#ifdef HAVE_PYSTRING_ASSTRING PyList_SetItem (list, i, PyString_FromString (argv[i])); +#else + PyList_SetItem (list, i, PyUnicode_FromString (argv[i])); +#endif + } return list; } @@ -2792,7 +2880,7 @@ 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, 0, PyLong_FromLong ((long) t)); PyTuple_SetItem (r, 1, PyLong_FromLongLong ((long) len)); return r; } @@ -2801,8 +2889,12 @@ 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, 0, PyLong_FromLong ((long) t)); +#ifdef HAVE_PYSTRING_ASSTRING PyTuple_SetItem (r, 1, PyString_FromStringAndSize (val, len)); +#else + PyTuple_SetItem (r, 1, PyBytes_FromStringAndSize (val, len)); +#endif return r; } @@ -2821,6 +2913,7 @@ put_val_type (char *val, size_t len, hive_type t) | RErr -> pr " int r;\n"; "-1" | RErrDispose -> pr " int r;\n"; "-1" | RHive -> pr " hive_h *r;\n"; "NULL" + | RSize -> pr " size_t r;\n"; "0" | RNode -> pr " hive_node_h r;\n"; "0" | RNodeNotFound -> pr " errno = 0;\n"; @@ -2980,6 +3073,7 @@ put_val_type (char *val, size_t len, hive_type t) pr " py_r = Py_None;\n" | RHive -> pr " py_r = put_handle (r);\n" + | RSize | RNode -> pr " py_r = PyLong_FromLongLong (r);\n" | RNodeNotFound -> @@ -2996,7 +3090,11 @@ put_val_type (char *val, size_t len, hive_type t) | RValue -> pr " py_r = PyLong_FromLongLong (r);\n" | RString -> + pr "#ifdef HAVE_PYSTRING_ASSTRING\n"; pr " py_r = PyString_FromString (r);\n"; + pr "#else\n"; + pr " py_r = PyUnicode_FromString (r);\n"; + pr "#endif\n"; pr " free (r);" | RStringList -> pr " py_r = put_string_list (r);\n"; @@ -3007,7 +3105,7 @@ put_val_type (char *val, size_t len, hive_type t) pr " py_r = put_val_type (r, len, t);\n"; pr " free (r);\n" | RInt32 -> - pr " py_r = PyInt_FromLong ((long) r);\n" + pr " py_r = PyLong_FromLong ((long) r);\n" | RInt64 -> pr " py_r = PyLong_FromLongLong (r);\n" ); @@ -3029,22 +3127,54 @@ put_val_type (char *val, size_t len, hive_type t) (* Init function. *) pr "\ +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + \"libhivexmod\", /* m_name */ + \"hivex module\", /* m_doc */ + -1, /* m_size */ + methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + +static PyObject * +moduleinit (void) +{ + PyObject *m; + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create (&moduledef); +#else + m = Py_InitModule ((char *) \"libhivexmod\", methods); +#endif + + return m; /* m might be NULL if module init failed */ +} + +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit_libhivexmod (void) +{ + return moduleinit (); +} +#else void initlibhivexmod (void) { - static int initialized = 0; - - if (initialized) return; - Py_InitModule ((char *) \"libhivexmod\", methods); - initialized = 1; + (void) moduleinit (); } +#endif " and generate_python_py () = generate_header HashStyle LGPLv2plus; pr "\ -u\"\"\"Python bindings for hivex +\"\"\"Python bindings for hivex import hivex h = hivex.Hivex (filename) @@ -3097,7 +3227,7 @@ class Hivex: pr " def %s (self" name; List.iter (fun arg -> pr ", %s" (name_of_argt arg)) args; pr "):\n"; - pr " u\"\"\"%s\"\"\"\n" shortdesc; + pr " \"\"\"%s\"\"\"\n" shortdesc; pr " return libhivexmod.%s (self._o" name; List.iter ( fun arg -> @@ -3116,6 +3246,364 @@ class Hivex: ) ) functions +and generate_ruby_c () = + generate_header CStyle LGPLv2plus; + + pr "\ +#include +#include +#include + +#include + +#include \"hivex.h\" + +#include \"extconf.h\" + +/* For Ruby < 1.9 */ +#ifndef RARRAY_LEN +#define RARRAY_LEN(r) (RARRAY((r))->len) +#endif + +#ifndef RSTRING_LEN +#define RSTRING_LEN(r) (RSTRING((r))->len) +#endif + +#ifndef RSTRING_PTR +#define RSTRING_PTR(r) (RSTRING((r))->ptr) +#endif + +static VALUE m_hivex; /* hivex module */ +static VALUE c_hivex; /* hive_h handle */ +static VALUE e_Error; /* used for all errors */ + +static void +ruby_hivex_free (void *hvp) +{ + hive_h *h = hvp; + + if (h) + hivex_close (h); +} + +static void +get_value (VALUE valv, hive_set_value *val) +{ + VALUE key = rb_hash_lookup (valv, ID2SYM (rb_intern (\"key\"))); + VALUE type = rb_hash_lookup (valv, ID2SYM (rb_intern (\"type\"))); + VALUE value = rb_hash_lookup (valv, ID2SYM (rb_intern (\"value\"))); + + val->key = StringValueCStr (key); + val->t = NUM2ULL (type); + val->len = RSTRING_LEN (value); + val->value = RSTRING_PTR (value); +} + +static hive_set_value * +get_values (VALUE valuesv, size_t *nr_values) +{ + size_t i; + hive_set_value *ret; + + *nr_values = RARRAY_LEN (valuesv); + ret = malloc (sizeof (*ret) * *nr_values); + if (ret == NULL) + abort (); + + for (i = 0; i < *nr_values; ++i) { + VALUE v = rb_ary_entry (valuesv, i); + get_value (v, &ret[i]); + } + + return ret; +} + +"; + + List.iter ( + fun (name, (ret, args), shortdesc, longdesc) -> + let () = + (* Generate rdoc. *) + let doc = replace_str longdesc "C "h." ^ name, args + | args -> "Hivex::" ^ name, args in + let args = filter_map ( + function + | AUnusedFlags -> None + | args -> Some (name_of_argt args) + ) args in + let args = String.concat ", " args in + + let ret = + match ret with + | RErr | RErrDispose -> "nil" + | RHive -> "Hivex::Hivex" + | RSize | RNode | RNodeNotFound -> "integer" + | RNodeList -> "list" + | RValue -> "integer" + | RValueList -> "list" + | RString -> "string" + | RStringList -> "list" + | RLenType -> "hash" + | RLenTypeVal -> "hash" + | RInt32 -> "integer" + | RInt64 -> "integer" in + + pr "\ +/* + * call-seq: + * %s(%s) -> %s + * + * %s + * + * %s + * + * (For the C API documentation for this function, see + * +hivex_%s+[http://libguestfs.org/hivex.3.html#hivex_%s]). + */ +" call args ret shortdesc doc name name in + + (* Generate the function. *) + pr "static VALUE\n"; + pr "ruby_hivex_%s (" name; + + let () = + (* If the first argument is not AHive, then this is a module-level + * function, and Ruby passes an implicit module argument which we + * must ignore. Otherwise the first argument is the hive handle. + *) + let args = + match args with + | AHive :: args -> pr "VALUE hv"; args + | args -> pr "VALUE modulev"; args in + List.iter ( + function + | AUnusedFlags -> () + | arg -> + pr ", VALUE %sv" (name_of_argt arg) + ) args; + pr ")\n" in + + pr "{\n"; + + List.iter ( + function + | AHive -> + pr " hive_h *h;\n"; + pr " Data_Get_Struct (hv, hive_h, h);\n"; + pr " if (!h)\n"; + pr " rb_raise (rb_eArgError, \"%%s: used handle after closing it\",\n"; + pr " \"%s\");\n" name; + | ANode n -> + pr " hive_node_h %s = NUM2ULL (%sv);\n" n n + | AValue n -> + pr " hive_value_h %s = NUM2ULL (%sv);\n" n n + | AString n -> + pr " const char *%s = StringValueCStr (%sv);\n" n n; + | AStringNullable n -> + pr " const char *%s =\n" n; + pr " !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n + | AOpenFlags -> + pr " int flags = 0;\n"; + List.iter ( + fun (n, flag, _) -> + pr " if (RTEST (rb_hash_lookup (flagsv, ID2SYM (rb_intern (\"%s\")))))\n" + (String.lowercase flag); + pr " flags += %d;\n" n + ) open_flags + | AUnusedFlags -> () + | ASetValues -> + pr " size_t nr_values;\n"; + pr " hive_set_value *values;\n"; + pr " values = get_values (valuesv, &nr_values);\n" + | ASetValue -> + pr " hive_set_value val;\n"; + pr " get_value (valv, &val);\n" + ) args; + pr "\n"; + + let error_code = + match ret with + | RErr -> pr " int r;\n"; "-1" + | RErrDispose -> pr " int r;\n"; "-1" + | RHive -> pr " hive_h *r;\n"; "NULL" + | RSize -> pr " size_t r;\n"; "0" + | 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 + pr "\n"; + + let c_params = + List.map (function + | ASetValues -> ["nr_values"; "values"] + | ASetValue -> ["&val"] + | AUnusedFlags -> ["0"] + | arg -> [name_of_argt arg]) args in + let c_params = + match ret with + | RLenType | RLenTypeVal -> c_params @ [["&t"; "&len"]] + | _ -> c_params in + let c_params = List.concat c_params in + + pr " r = hivex_%s (%s" name (List.hd c_params); + List.iter (pr ", %s") (List.tl c_params); + pr ");\n"; + pr "\n"; + + (* Dispose of the hive handle (even if hivex_close returns error). *) + (match ret with + | RErrDispose -> + pr " /* So we don't double-free in the finalizer. */\n"; + pr " DATA_PTR (hv) = NULL;\n"; + pr "\n"; + | _ -> () + ); + + List.iter ( + function + | AHive + | ANode _ + | AValue _ + | AString _ + | AStringNullable _ + | AOpenFlags + | AUnusedFlags -> () + | ASetValues -> + pr " free (values);\n" + | ASetValue -> () + ) args; + + (* Check for errors from C library. *) + pr " if (r == %s)\n" error_code; + pr " rb_raise (e_Error, \"%%s\", strerror (errno));\n"; + pr "\n"; + + (match ret with + | RErr | RErrDispose -> + pr " return Qnil;\n" + | RHive -> + pr " return Data_Wrap_Struct (c_hivex, NULL, ruby_hivex_free, r);\n" + | RSize + | RNode + | RValue + | RInt64 -> + pr " return ULL2NUM (r);\n" + | RInt32 -> + pr " return INT2NUM (r);\n" + | RNodeNotFound -> + pr " if (r)\n"; + pr " return ULL2NUM (r);\n"; + pr " else\n"; + pr " return Qnil;\n" + | RNodeList + | RValueList -> + pr " size_t i, len = 0;\n"; + pr " for (i = 0; r[i] != 0; ++i) len++;\n"; + pr " VALUE rv = rb_ary_new2 (len);\n"; + pr " for (i = 0; r[i] != 0; ++i)\n"; + pr " rb_ary_push (rv, ULL2NUM (r[i]));\n"; + pr " free (r);\n"; + pr " return rv;\n" + | RString -> + pr " VALUE rv = rb_str_new2 (r);\n"; + pr " free (r);\n"; + pr " return rv;\n" + | RStringList -> + pr " size_t i, len = 0;\n"; + pr " for (i = 0; r[i] != NULL; ++i) len++;\n"; + pr " VALUE rv = rb_ary_new2 (len);\n"; + pr " for (i = 0; r[i] != NULL; ++i) {\n"; + pr " rb_ary_push (rv, rb_str_new2 (r[i]));\n"; + pr " free (r[i]);\n"; + pr " }\n"; + pr " free (r);\n"; + pr " return rv;\n" + | RLenType -> + pr " VALUE rv = rb_hash_new ();\n"; + pr " rb_hash_aset (rv, ID2SYM (rb_intern (\"len\")), INT2NUM (len));\n"; + pr " rb_hash_aset (rv, ID2SYM (rb_intern (\"type\")), INT2NUM (t));\n"; + pr " return rv;\n" + | RLenTypeVal -> + pr " VALUE rv = rb_hash_new ();\n"; + pr " rb_hash_aset (rv, ID2SYM (rb_intern (\"len\")), INT2NUM (len));\n"; + pr " rb_hash_aset (rv, ID2SYM (rb_intern (\"type\")), INT2NUM (t));\n"; + pr " rb_hash_aset (rv, ID2SYM (rb_intern (\"value\")), rb_str_new (r, len));\n"; + pr " free (r);\n"; + pr " return rv;\n" + ); + + pr "}\n"; + pr "\n" + ) functions; + + pr "\ +/* Initialize the module. */ +void Init__hivex () +{ + m_hivex = rb_define_module (\"Hivex\"); + c_hivex = rb_define_class_under (m_hivex, \"Hivex\", rb_cObject); + e_Error = rb_define_class_under (m_hivex, \"Error\", rb_eStandardError); + + /* XXX How to pass arguments? */ +#if 0 +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func (c_hivex, ruby_hivex_open); +#endif +#endif + +"; + + (* Methods. *) + List.iter ( + fun (name, (_, args), _, _) -> + let args = List.filter ( + function + | AUnusedFlags -> false + | _ -> true + ) args in + let nr_args = List.length args in + match args with + | AHive :: _ -> + pr " rb_define_method (c_hivex, \"%s\",\n" name; + pr " ruby_hivex_%s, %d);\n" name (nr_args-1) + | args -> (* class function *) + pr " rb_define_module_function (m_hivex, \"%s\",\n" name; + pr " ruby_hivex_%s, %d);\n" name nr_args + ) functions; + + pr "}\n" + let output_to filename k = let filename_new = filename ^ ".new" in chan := open_out filename_new; @@ -3183,6 +3671,8 @@ Run it from the top source directory using the command output_to "python/hivex.py" generate_python_py; output_to "python/hivex-py.c" generate_python_c; + output_to "ruby/ext/hivex/_hivex.c" generate_ruby_c; + (* Always generate this file last, and unconditionally. It's used * by the Makefile to know when we must re-run the generator. *)