ocaml/otherlibs/win32unix/unix.ml

555 lines
15 KiB
OCaml

(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy and Pascal Cuoq, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Initialization *)
external startup: unit -> unit = "win_startup"
external cleanup: unit -> unit = "win_cleanup"
let _ = startup(); at_exit cleanup
(* Errors *)
type error =
E2BIG
| EACCESS
| EAGAIN
| EBADF
| EBUSY
| ECHILD
| EDEADLK
| EDOM
| EEXIST
| EFAULT
| EFBIG
| EINTR
| EINVAL
| EIO
| EISDIR
| EMFILE
| EMLINK
| ENAMETOOLONG
| ENFILE
| ENODEV
| ENOENT
| ENOEXEC
| ENOLCK
| ENOMEM
| ENOSPC
| ENOSYS
| ENOTDIR
| ENOTEMPTY
| ENOTTY
| ENXIO
| EPERM
| EPIPE
| ERANGE
| EROFS
| ESPIPE
| ESRCH
| EXDEV
| EWOULDBLOCK
| EINPROGRESS
| EALREADY
| ENOTSOCK
| EDESTADDRREQ
| EMSGSIZE
| EPROTOTYPE
| ENOPROTOOPT
| EPROTONOSUPPORT
| ESOCKTNOSUPPORT
| EOPNOTSUPP
| EPFNOSUPPORT
| EAFNOSUPPORT
| EADDRINUSE
| EADDRNOTAVAIL
| ENETDOWN
| ENETUNREACH
| ENETRESET
| ECONNABORTED
| ECONNRESET
| ENOBUFS
| EISCONN
| ENOTCONN
| ESHUTDOWN
| ETOOMANYREFS
| ETIMEDOUT
| ECONNREFUSED
| EHOSTDOWN
| EHOSTUNREACH
| ELOOP
| EUNKNOWNERR
exception Unix_error of error * string * string
let _ = Callback.register_exception "Unix.Unix_error"
(Unix_error(EUNKNOWNERR, "", ""))
external error_message : error -> string = "unix_error_message"
let handle_unix_error f arg =
try
f arg
with Unix_error(err, fun_name, arg) ->
prerr_string Sys.argv.(0);
prerr_string ": \"";
prerr_string fun_name;
prerr_string "\" failed";
if String.length arg > 0 then begin
prerr_string " on \"";
prerr_string arg;
prerr_string "\""
end;
prerr_string ": ";
prerr_endline (error_message err);
exit 2
external environment : unit -> string array = "unix_environment"
type process_status =
WEXITED of int
| WSIGNALED of int
| WSTOPPED of int
type wait_flag =
WNOHANG
| WUNTRACED
type file_descr
external execv : string -> string array -> unit = "unix_execv"
external execve : string -> string array -> string array -> unit = "unix_execve"
external execvp : string -> string array -> unit = "unix_execvp"
external waitpid : wait_flag list -> int -> int * process_status
= "win_waitpid"
external getpid : unit -> int = "unix_getpid"
let wait () = invalid_arg("Unix.wait not implemented")
type standard_handle = STD_INPUT | STD_OUTPUT | STD_ERROR
external stdhandle : standard_handle -> file_descr = "win_stdhandle"
let stdin = stdhandle STD_INPUT
let stdout = stdhandle STD_OUTPUT
let stderr = stdhandle STD_ERROR
type open_flag =
O_RDONLY
| O_WRONLY
| O_RDWR
| O_NONBLOCK
| O_APPEND
| O_CREAT
| O_TRUNC
| O_EXCL
| O_BINARY
| O_TEXT
type file_perm = int
external openfile : string -> open_flag list -> file_perm -> file_descr
= "unix_open"
external close : file_descr -> unit = "unix_close"
external unsafe_read : file_descr -> string -> int -> int -> int
= "unix_read"
external unsafe_write : file_descr -> string -> int -> int -> int
= "unix_write"
let read fd buf ofs len =
if len < 0 or ofs + len > String.length buf
then invalid_arg "Unix.read"
else unsafe_read fd buf ofs len
let write fd buf ofs len =
if len < 0 or ofs + len > String.length buf
then invalid_arg "Unix.write"
else unsafe_write fd buf ofs len
external open_read_descriptor : int -> in_channel = "caml_open_descriptor"
external open_write_descriptor : int -> out_channel = "caml_open_descriptor"
external fd_of_in_channel : in_channel -> int = "channel_descriptor"
external fd_of_out_channel : out_channel -> int = "channel_descriptor"
external open_handle : file_descr -> open_flag list -> int = "win_fd_handle"
external filedescr_of_fd : int -> file_descr = "win_handle_fd"
let in_channel_of_descr_gen flags handle =
open_read_descriptor(open_handle handle flags)
let in_channel_of_descr handle =
in_channel_of_descr_gen [O_BINARY] handle
let out_channel_of_descr_gen flags handle =
open_write_descriptor(open_handle handle flags)
let out_channel_of_descr handle =
out_channel_of_descr_gen [O_BINARY] handle
let descr_of_in_channel inchan =
filedescr_of_fd(fd_of_in_channel inchan)
let descr_of_out_channel outchan =
filedescr_of_fd(fd_of_out_channel outchan)
type seek_command =
SEEK_SET
| SEEK_CUR
| SEEK_END
external lseek : file_descr -> int -> seek_command -> int = "unix_lseek"
type file_kind =
S_REG
| S_DIR
| S_CHR
| S_BLK
| S_LNK
| S_FIFO
| S_SOCK
type stats =
{ st_dev : int;
st_ino : int;
st_kind : file_kind;
st_perm : file_perm;
st_nlink : int;
st_uid : int;
st_gid : int;
st_rdev : int;
st_size : int;
st_atime : int;
st_mtime : int;
st_ctime : int }
external stat : string -> stats = "unix_stat"
external unlink : string -> unit = "unix_unlink"
external rename : string -> string -> unit = "unix_rename"
type access_permission =
R_OK
| W_OK
| X_OK
| F_OK
external access : string -> access_permission list -> unit = "unix_access"
external dup : file_descr -> file_descr = "unix_dup"
external dup2 : file_descr -> file_descr -> unit = "unix_dup2"
external set_close_on_exec : file_descr -> unit = "win_set_close_on_exec"
external clear_close_on_exec : file_descr -> unit = "win_clear_close_on_exec"
external mkdir : string -> file_perm -> unit = "unix_mkdir"
external rmdir : string -> unit = "unix_rmdir"
external chdir : string -> unit = "unix_chdir"
external getcwd : unit -> string = "unix_getcwd"
type dir_entry =
Dir_empty
| Dir_read of string
| Dir_toread
type dir_handle = { handle: int; mutable entry_read: dir_entry }
external findfirst : string -> string * int = "win_findfirst"
external findnext : int -> string= "win_findnext"
let opendir dirname =
try
let (first_entry, handle) = findfirst (dirname ^ "\\*.*") in
{ handle = handle; entry_read = Dir_read first_entry }
with End_of_file ->
{ handle = 0; entry_read = Dir_empty }
let readdir d =
match d.entry_read with
Dir_empty -> raise End_of_file
| Dir_read name -> d.entry_read <- Dir_toread; name
| Dir_toread -> findnext d.handle
external win_findclose : int -> unit = "win_findclose"
let closedir d =
match d.entry_read with
Dir_empty -> ()
| _ -> win_findclose d.handle
external pipe : unit -> file_descr * file_descr = "unix_pipe"
type tm =
{ tm_sec : int;
tm_min : int;
tm_hour : int;
tm_mday : int;
tm_mon : int;
tm_year : int;
tm_wday : int;
tm_yday : int;
tm_isdst : bool }
external time : unit -> int = "unix_time"
external gettimeofday : unit -> float = "unix_gettimeofday"
external gmtime : int -> tm = "unix_gmtime"
external localtime : int -> tm = "unix_localtime"
external mktime : tm -> int * tm = "unix_mktime"
external sleep : int -> unit = "unix_sleep"
external utimes : string -> int -> int -> unit = "unix_utimes"
let getlogin () =
try Sys.getenv "USERNAME" with Not_found -> ""
type inet_addr
external inet_addr_of_string : string -> inet_addr
= "unix_inet_addr_of_string"
external string_of_inet_addr : inet_addr -> string
= "unix_string_of_inet_addr"
let inet_addr_any = inet_addr_of_string "0.0.0.0"
type socket_domain =
PF_UNIX
| PF_INET
type socket_type =
SOCK_STREAM
| SOCK_DGRAM
| SOCK_RAW
| SOCK_SEQPACKET
type sockaddr =
ADDR_UNIX of string
| ADDR_INET of inet_addr * int
type shutdown_command =
SHUTDOWN_RECEIVE
| SHUTDOWN_SEND
| SHUTDOWN_ALL
type msg_flag =
MSG_OOB
| MSG_DONTROUTE
| MSG_PEEK
type socket_option =
SO_DEBUG
| SO_BROADCAST
| SO_REUSEADDR
| SO_KEEPALIVE
| SO_DONTROUTE
| SO_OOBINLINE
external socket : socket_domain -> socket_type -> int -> file_descr
= "unix_socket"
external accept : file_descr -> file_descr * sockaddr = "unix_accept"
external bind : file_descr -> sockaddr -> unit = "unix_bind"
external connect : file_descr -> sockaddr -> unit = "unix_connect"
external listen : file_descr -> int -> unit = "unix_listen"
external shutdown : file_descr -> shutdown_command -> unit = "unix_shutdown"
external getsockname : file_descr -> sockaddr = "unix_getsockname"
external getpeername : file_descr -> sockaddr = "unix_getpeername"
external unsafe_recv :
file_descr -> string -> int -> int -> msg_flag list -> int
= "unix_recv"
external unsafe_recvfrom :
file_descr -> string -> int -> int -> msg_flag list -> int * sockaddr
= "unix_recvfrom"
external unsafe_send :
file_descr -> string -> int -> int -> msg_flag list -> int
= "unix_send"
external unsafe_sendto :
file_descr -> string -> int -> int -> msg_flag list -> sockaddr -> int
= "unix_sendto" "unix_sendto_native"
let recv fd buf ofs len flags =
if len < 0 or ofs + len > String.length buf
then invalid_arg "Unix.recv"
else unsafe_recv fd buf ofs len flags
let recvfrom fd buf ofs len flags =
if len < 0 or ofs + len > String.length buf
then invalid_arg "Unix.recvfrom"
else unsafe_recvfrom fd buf ofs len flags
let send fd buf ofs len flags =
if len < 0 or ofs + len > String.length buf
then invalid_arg "Unix.send"
else unsafe_send fd buf ofs len flags
let sendto fd buf ofs len flags addr =
if len < 0 or ofs + len > String.length buf
then invalid_arg "Unix.sendto"
else unsafe_sendto fd buf ofs len flags addr
external getsockopt : file_descr -> socket_option -> bool = "unix_getsockopt"
external setsockopt : file_descr -> socket_option -> bool -> unit
= "unix_setsockopt"
type host_entry =
{ h_name : string;
h_aliases : string array;
h_addrtype : socket_domain;
h_addr_list : inet_addr array }
type protocol_entry =
{ p_name : string;
p_aliases : string array;
p_proto : int }
type service_entry =
{ s_name : string;
s_aliases : string array;
s_port : int;
s_proto : string }
external gethostname : unit -> string = "unix_gethostname"
external gethostbyname : string -> host_entry = "unix_gethostbyname"
external gethostbyaddr : inet_addr -> host_entry = "unix_gethostbyaddr"
external getprotobyname : string -> protocol_entry
= "unix_getprotobyname"
external getprotobynumber : int -> protocol_entry
= "unix_getprotobynumber"
external getservbyname : string -> string -> service_entry
= "unix_getservbyname"
external getservbyport : int -> string -> service_entry
= "unix_getservbyport"
(* High-level process management (system, popen) *)
external win_create_process : string -> string -> string option ->
file_descr -> file_descr -> file_descr -> int
= "win_create_process" "win_create_process_native"
let create_process prog args fd1 fd2 fd3 =
win_create_process prog (String.concat " " (Array.to_list args)) None
fd1 fd2 fd3
let create_process_env prog args env fd1 fd2 fd3 =
win_create_process prog (String.concat " " (Array.to_list args))
(Some(String.concat "\000" (Array.to_list env) ^ "\000"))
fd1 fd2 fd3
external system: string -> process_status = "win_system"
type popen_process =
Process of in_channel * out_channel
| Process_in of in_channel
| Process_out of out_channel
let popen_processes = (Hashtbl.create 7 : (popen_process, int) Hashtbl.t)
let open_proc cmd proc input output =
let shell =
try Sys.getenv "COMSPEC"
with Not_found -> raise(Unix_error(ENOEXEC, "open_proc", cmd)) in
let pid =
create_process shell [|shell; "/c"; cmd|] input output stderr in
Hashtbl.add popen_processes proc pid
let open_process_in cmd =
let (in_read, in_write) = pipe() in
let inchan = in_channel_of_descr in_read in
set_close_on_exec in_read;
open_proc cmd (Process_in inchan) stdin in_write;
close in_write;
inchan
let open_process_out cmd =
let (out_read, out_write) = pipe() in
let outchan = out_channel_of_descr out_write in
set_close_on_exec out_write;
open_proc cmd (Process_out outchan) out_read stdout;
close out_read;
outchan
let open_process cmd =
let (in_read, in_write) = pipe() in
let (out_read, out_write) = pipe() in
let inchan = in_channel_of_descr in_read in
let outchan = out_channel_of_descr out_write in
set_close_on_exec in_read;
set_close_on_exec out_write;
open_proc cmd (Process(inchan, outchan)) out_read in_write;
(inchan, outchan)
let find_proc_id fun_name proc =
try
let pid = Hashtbl.find popen_processes proc in
Hashtbl.remove popen_processes proc;
pid
with Not_found ->
raise(Unix_error(EBADF, fun_name, ""))
let close_process_in inchan =
let pid = find_proc_id "close_process_in" (Process_in inchan) in
close_in inchan;
snd(waitpid [] pid)
let close_process_out outchan =
let pid = find_proc_id "close_process_out" (Process_out outchan) in
close_out outchan;
snd(waitpid [] pid)
let close_process (inchan, outchan) =
let pid = find_proc_id "close_process" (Process(inchan, outchan)) in
close_in inchan; close_out outchan;
snd(waitpid [] pid)
(* Polling *)
external select :
file_descr list -> file_descr list -> file_descr list -> float ->
file_descr list * file_descr list * file_descr list = "unix_select"
(* High-level network functions *)
let open_connection sockaddr =
let domain =
match sockaddr with ADDR_UNIX _ -> PF_UNIX | ADDR_INET(_,_) -> PF_INET in
let sock =
socket domain SOCK_STREAM 0 in
connect sock sockaddr;
(in_channel_of_descr sock, out_channel_of_descr sock)
let shutdown_connection inchan =
shutdown (descr_of_in_channel inchan) SHUTDOWN_SEND
(* Dummy functions *)
let set_nonblock fd = ()
let clear_nonblock fd = ()
type passwd_entry =
{ pw_name : string;
pw_passwd : string;
pw_uid : int;
pw_gid : int;
pw_gecos : string;
pw_dir : string;
pw_shell : string }
type group_entry =
{ gr_name : string;
gr_passwd : string;
gr_gid : int;
gr_mem : string array }
let getpwnam x = raise Not_found
let getgrnam = getpwnam
let getpwuid = getpwnam
let getgrgid = getpwnam
let getuid () = 1
let getgid () = 1