A complete example
passwd.fmli
(* An entry of "/etc/passwd" is represented by a record of
type [(!passwd_file, !password) entry] *)
type ('a, 'b) entry =
{ login: 'a string;
password: 'b string
}
(* Input from "/etc/passwd" *)
type noneq in_channel
val open_in: unit -{!passwd_file ||}-> in_channel
val input_entry: in_channel
-{!passwd_file | End_of_file: !passwd_file |}->
(!passwd_file, !password) entry
val close_in: in_channel -{!passwd_file ||}-> unit
passwd.ml
type entry =
{ login: string;
password: string
}
type in_channel = Pervasives.in_channel
let open_in () =
Pervasives.open_in "/etc/passwd"
let rec input_entry chan =
let line = input_line chan in
try
let i1 = String.index line ':' in
let i2 = String.index_from line (i1 + 1) ':' in
{ login = String.sub line 0 i1;
password = String.sub line (i1 + 1) (i2 - i1 - 1)
}
with
Not_found -> input_entry chan
let close_in chan =
Pervasives.close_in chan
shadow.fmli
(* An entry of "/etc/shadow" is represented by a record of
type [(!shadow_file, !shadow_password) entry] *)
type ('a, 'b) entry =
{ login: 'a string;
password: 'b string;
rem: 'b string;
}
(* Input from "/etc/shadow" *)
type noneq in_channel
val open_in: unit -{!shadow_file ||}-> in_channel
val input_entry: in_channel
-{!shadow_file | End_of_file: !shadow_file |}->
(!shadow_file, !shadow_password) entry
val close_in: in_channel -{!shadow_file ||}-> unit
(* Output to "/etc/shadow" *)
type noneq out_channel
val open_out: unit -{!shadow_file ||}-> out_channel
val output_entry:
out_channel -> (!shadow_file, !shadow_password) entry
-{!shadow_file ||}-> unit
val close_out: out_channel -{!shadow_file ||}-> unit
shadow.ml
type entry =
{ login: string;
password: string;
rem: string;
}
type in_channel = Pervasives.in_channel
let open_in () =
Pervasives.open_in "/etc/shadow"
let rec input_entry chan =
try
let line = input_line chan in
let i1 = String.index line ':' in
let i2 = String.index_from line (i1 + 1) ':' in
let ln = String.length line in
{ login = String.sub line 0 i1;
password = String.sub line (i1 + 1) (i2 - i1 - 1);
rem = String.sub line (i2 + 1) (ln - i2 - 1)
}
with
Not_found -> input_entry chan
let close_in chan =
Pervasives.close_in chan
type out_channel = Pervasives.out_channel
let open_out () =
Pervasives.open_out "/etc/shadow"
let output_entry chan e =
Printf.fprintf chan "%s:%s:%s\n" e.login e.password e.rem
let close_out chan =
Pervasives.close_out chan
verbose.fmli
flow !arg < !stderr
and !arg < !stdout
affects !arg
raises !arg
val message : !stdout string -{!stdout ||}-> unit
verbose.fml
flow !arg < !stderr, !stdout
(** [!verbose_mode] is true if the verbose mode is
active. *)
let verbose_mode : (!arg bool, _) ref = ref false
(** Parse command-line arguments. If the option "-v" if
found then [verbose_mode] is set to true. If any other
option is encountered then an error message is printed
and the exception [Exit] is raised. *)
let _ =
for i = 1 to Array.length Sys.argv - 1 do
match Sys.argv.(i) with
"-v" -> verbose_mode := true
| option ->
prerr_string "Invalid option ";
prerr_endline option;
raise Exit
done
(** [print message] print a message on the standard output
if the verbose mode is enabled. Otherwise, it does
nothing. *)
let message s =
if !verbose_mode then print_endline s
main.fml
flow !passwd_file < !shadow_file
and !passwd_file, !shadow_file < !stdout
and !passwd_file, !shadow_file < !shadow_password
and !password < !shadow_password
(** The module [StringMap] implements association tables
indexed by strings. *)
module StringMap = Map.Make (struct
type 'a t = 'a string
let compare = Pervasives.compare
end)
(** [read_shadow ()] reads the content of /etc/passwd
and returns a map associating each login to its
entry. *)
let read_shadow () =
let in_chan = Shadow.open_in () in
let rec loop accu =
try
let entry = Shadow.input_entry in_chan in
loop (StringMap.add entry.Shadow.login entry accu)
with End_of_file ->
Shadow.close_in in_chan;
accu
in
loop StringMap.empty
(** [read_passwd shadow_map] generates /etc/shadow from
/etc/passwd and the entries in [shadow_map] *)
let read_passwd shadow_map =
let in_chan = Passwd.open_in ()
and out_chan = Shadow.open_out () in
let rec loop () =
try
let passwd_entry = Passwd.input_entry in_chan in
Verbose.message passwd_entry.Passwd.login;
let shadow_entry =
try
StringMap.find passwd_entry.Passwd.login shadow_map
with
Not_found ->
Verbose.message " creating an entry";
{ Shadow.login = passwd_entry.Passwd.login;
Shadow.password = passwd_entry.Passwd.password;
Shadow.rem = ""
}
in
Shadow.output_entry out_chan shadow_entry
with
End_of_file -> ()
in
loop ();
Passwd.close_in in_chan;
Shadow.close_out out_chan
let _ =
let shadow_map = read_shadow () in
read_passwd shadow_map