# 1 "src/ctypes-foreign/dl.ml.unix"
(*
 * Copyright (c) 2013 Jeremy Yallop.
 *
 * This file is distributed under the terms of the MIT License.
 * See the file LICENSE for details.
 *)

[@@@ocaml.warning "-16"]

type library

type flag = 
    RTLD_LAZY
  | RTLD_NOW
  | RTLD_GLOBAL
  | RTLD_LOCAL
  | RTLD_NODELETE
  | RTLD_NOLOAD
  | RTLD_DEEPBIND

exception DL_error of string

(* void *dlopen(const char *filename, int flag); *)
external _dlopen : ?filename:string -> flags:int -> library option
  = "ctypes_dlopen"
    
(* void *dlsym(void *handle, const char *symbol); *)
external _dlsym : ?handle:library -> symbol:string -> nativeint option
  = "ctypes_dlsym"

(* int dlclose(void *handle); *)
external _dlclose : handle:library -> int
  = "ctypes_dlclose"

(* char *dlerror(void); *)
external _dlerror : unit -> string option
  = "ctypes_dlerror"

external resolve_flag : flag -> int
  = "ctypes_resolve_dl_flag"

let _report_dl_error noload =
  match _dlerror () with
    | Some error -> raise (DL_error (error))
    | None       ->
      if noload then
        raise (DL_error "library not loaded")
      else
        failwith "dl_error: expected error, but no error reported"

let crush_flags f : 'a list -> int = List.fold_left (fun i o -> i lor (f o)) 0

[@@@warning "-16"]
let dlopen ?filename ~flags =
  match _dlopen ?filename ~flags:(crush_flags resolve_flag flags) with
    | Some library -> library
    | None         -> _report_dl_error (List.mem RTLD_NOLOAD flags)

let dlclose ~handle =
  match _dlclose ~handle with
    | 0 -> ()
    | _ -> _report_dl_error false

let dlsym ?handle ~symbol =
  match _dlsym ?handle ~symbol with
    | Some symbol -> symbol
    | None        -> _report_dl_error false
