123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242(* $Id$ *)(* Since Ocaml 3.11.2 there is also Printexc.register_printer allowing
the user to register a printer directly with the Ocaml stdlib.
Of course, we register our printer there, too, but we have to be
careful to avoid recursion
FORMAT OF EXCEPTIONS UNTIL 4.01:
- tag=0
- field(exn,0) is a block shared by all exceptions with the same
identity.
- field(field(exn,0),0) is the name as OCaml string
- field(exn,1): start of arguments
FORMAT OF EXCEPTIONS SINCE 4.02:
Exceptions without arguments: The exception is identical to the
exception descriptor:
- tag=object tag
- field(exn,0) is the name as OCaml string (NB. objects have in this
field a pointer to the method table)
- field(exn,1) is the OID (OCaml integer)
Exceptions with arguments:
- tag=0
- field(exn,0) is the exception descriptor object
- hence, field(field(exn,0),0) is the name
- hence, field(field(exn,0),1) is the OID
- field(exn,1): start of arguments
*)typeprinter=exn->stringletregistry=(Hashtbl.create50:(string,(Obj.t*printer)list)Hashtbl.t)(* The hashtable maps the name of the exception to an assoc list
mapping the exception anchor to the printer.
*)letscopys=(* we really want tomake here a copy! *)String.subs0(String.lengths)letregister_printeref=lete1=Obj.repreinletdescriptor=ifObj.tage1=0thenObj.fielde10elsee1inletname=scopy(Obj.obj(Obj.fielddescriptor0):string)inletdescr_is_object =(Obj.tagdescriptor=Obj.object_tag)inletalist=tryHashtbl.findregistrynamewithNot_found ->[]inletalist'=(descriptor,f)::(ifdescr_is_objectthenList.remove_assoc descriptoralistelseList.remove_assqdescriptoralist)inHashtbl.replace registrynamealist'(* to_string_opt: This is the function registered at Printexc.
It must not call Printexc.
*)letto_string_opt(e:exn):stringoption=lete1=Obj.repr einletdescriptor=ifObj.tage1=0thenObj.fielde10elsee1inletname=scopy(Obj.obj(Obj.fielddescriptor0):string)inletdescr_is_object =(Obj.tagdescriptor=Obj.object_tag)inletf_opt=tryletalist=Hashtbl.findregistrynameintryifdescr_is_objectthenSome(List.assoc descriptoralist)elseSome(List.assqdescriptoralist)with|Not_found ->(*
prerr_endline "Strange: exn by name found, but not by anchor";
prerr_endline ("name: " ^ name);
prerr_endline ("anchor: " ^
string_of_int(Obj.magic anchor : int));
let ea = fst(List.hd alist) in
prerr_endline ("expected anchor: " ^
string_of_int(Obj.magic ea : int));
*)Nonewith|Not_found->Noneinmatchf_optwith|None->None|Somef->Some(fe)(* to_string: This isthe function called by users. If there is
Printexc.register_printer, we simply call Printexc.to_string
- our printers are also registered with Printexc. If Ocaml does
not provide this feature, we can only use our own registry
*)letto_stringe=ifNetsys_conf.have_printexc_register_printerthenPrintexc.to_stringeelsematchto_string_opt ewith|None->Printexc.to_stringe|Somes->s(* If supported, register our registry: *)let()=ifNetsys_conf.have_printexc_register_printerthenNetsys_conf.printexc_register_printerto_string_opt(* Add printers for the core exceptions: *)letstring_of_unix_code=function|Unix.E2BIG->"E2BIG"|Unix.EACCES->"EACCES"|Unix.EAGAIN->"EAGAIN"|Unix.EBADF->"EBADF"|Unix.EBUSY->"EBUSY"|Unix.ECHILD->"ECHILD"|Unix.EDEADLK->"EDEADLK"|Unix.EDOM->"EDOM"|Unix.EEXIST->"EEXIST"|Unix.EFAULT->"EFAULT"|Unix.EFBIG->"EFBIG"|Unix.EINTR->"EINTR"|Unix.EINVAL->"EINVAL"|Unix.EIO->"EIO"|Unix.EISDIR->"EISDIR"|Unix.EMFILE->"EMFILE"|Unix.EMLINK->"EMLINK"|Unix.ENAMETOOLONG->"ENAMETOOLONG"|Unix.ENFILE->"ENFILE"|Unix.ENODEV->"ENODEV"|Unix.ENOENT->"ENOENT"|Unix.ENOEXEC->"ENOEXEC"|Unix.ENOLCK->"ENOLCK"|Unix.ENOMEM->"ENOMEM"|Unix.ENOSPC->"ENOSPC"|Unix.ENOSYS->"ENOSYS"|Unix.ENOTDIR->"ENOTDIR"|Unix.ENOTEMPTY->"ENOTEMPTY"|Unix.ENOTTY->"ENOTTY"|Unix.ENXIO->"ENXIO"|Unix.EPERM->"EPERM"|Unix.EPIPE->"EPIPE"|Unix.ERANGE->"ERANGE"|Unix.EROFS->"EROFS"|Unix.ESPIPE->"ESPIPE"|Unix.ESRCH->"ESRCH"|Unix.EXDEV->"EXDEV"|Unix.EWOULDBLOCK->"EWOULDBLOCK"|Unix.EINPROGRESS->"EINPROGRESS"|Unix.EALREADY->"EALREADY"|Unix.ENOTSOCK->"ENOTSOCK"|Unix.EDESTADDRREQ->"EDESTADDRREQ"|Unix.EMSGSIZE->"EMSGSIZE"|Unix.EPROTOTYPE->"EPROTOTYPE"|Unix.ENOPROTOOPT->"ENOPROTOOPT"|Unix.EPROTONOSUPPORT->"EPROTONOSUPPORT"|Unix.ESOCKTNOSUPPORT->"ESOCKTNOSUPPORT"|Unix.EOPNOTSUPP->"EOPNOTSUPP"|Unix.EPFNOSUPPORT->"EPFNOSUPPORT"|Unix.EAFNOSUPPORT->"EAFNOSUPPORT"|Unix.EADDRINUSE->"EADDRINUSE"|Unix.EADDRNOTAVAIL->"EADDRNOTAVAIL"|Unix.ENETDOWN->"ENETDOWN"|Unix.ENETUNREACH->"ENETUNREACH"|Unix.ENETRESET->"ENETRESET"|Unix.ECONNABORTED->"ECONNABORTED"|Unix.ECONNRESET->"ECONNRESET"|Unix.ENOBUFS->"ENOBUFS"|Unix.EISCONN->"EISCONN"|Unix.ENOTCONN->"ENOTCONN"|Unix.ESHUTDOWN->"ESHUTDOWN"|Unix.ETOOMANYREFS->"ETOOMANYREFS"|Unix.ETIMEDOUT->"ETIMEDOUT"|Unix.ECONNREFUSED->"ECONNREFUSED"|Unix.EHOSTDOWN->"EHOSTDOWN"|Unix.EHOSTUNREACH->"EHOSTUNREACH"|Unix.ELOOP->"ELOOP"|Unix.EOVERFLOW->"EOVERFLOW"|Unix.EUNKNOWNERRn->"EUNKNOWNERR "^string_of_intnletstring_literals="\""^String.escapeds^"\""letstring_of_unix_errore=matchewith|Unix.Unix_error(code,fname,arg)->"Unix.Unix_error("^string_of_unix_codecode^", "^string_literalfname^", "^string_literalarg^")"|_->assertfalseletrecstring_contains_ats1s2k=letl1=String.lengths1inletl2=String.lengths2ink+l2<=l1&&(String.subs1kl2=s2||string_contains_ats1 s2(k+1))letstring_containss1s2=(* Is s2 a substring of s1? *)string_contains_ats1s20lethave_nice_unix_error=(* maybe somebody enhances Printexc at some time so it can already
print nice Unix errors. Test for this.
*)lets=Printexc.to_string(Unix.Unix_error(Unix.ENOENT,"",""))instring_containss"ENOENT"let()=ifnothave_nice_unix_errorthen(register_printer(Unix.Unix_error(Unix.ENOENT,"",""))string_of_unix_error)