Saturday, September 21, 2013

Real world OCaml

Real world OCaml

For many tasks, I find OCaml as useful as Python and like "a better C".

This program matches files in a directory with a provided suffix and invokes a program on them.
(*e.g.
  Match files in directory with given suffix and invoke a program on
  them.

$ ./build/bin/filesin.exe -d \
    c:/project/examples -p \
    c:/project/someprog.exe
*)

(*Globals*)
let version,root,prog,suffix=ref false,ref "",ref "",ref ""

let (read_args:unit -> unit) = fun () ->
  let specification =
    [("-v", Arg.Set version, "Print the version number") ;
     ("-d", Arg.String (fun s -> root := s), "Directory") ;
     ("-s", Arg.String (fun s -> suffix := s), "Suffix") ;
     ("-p", Arg.String (fun s -> prog := s), "Path to the prog") ;
    ]
  in Arg.parse specification
  (fun s -> Printf.printf "Warning : Ignoring unrecognized argument \"%s\"\n" s)
  ("Usage : "^(Sys.argv.(0))^" options")

let (filesin:string -> string array option) = fun s ->
  if not (Sys.file_exists s)
       || not (Sys.is_directory s) then
    None
  else (Some (Sys.readdir s))

let (read_and_close:Unix.file_descr -> string)  = fun fdr ->
  let buf = Buffer.create 1024 in
  let bytes_to_read = ref true in
  while !bytes_to_read do
    let s  = String.create 1024 in
    let num_bytes_read = Unix.read fdr s 0 124 in
    Buffer.add_string buf (String.sub s 0 num_bytes_read) ;
    if num_bytes_read < 1024 then bytes_to_read := false ;
    ()
  done ;
  Unix.close fdr ;
  Buffer.contents buf

let (process_file:string -> unit) = fun f ->
  let p = Filename.concat (!root) f in
  let cmd = Printf.sprintf "%s -l -e %s" (!prog) p in
  (*I tried to use anonymous pipes here but that seems broken on
    Windows when [Unix.system] is involved*)
  let tf=Filename.temp_file "tmp" "" in
  let fdw = Unix.openfile tf [Unix.O_WRONLY; Unix.O_TRUNC; Unix.O_CREAT] 0o666 in
  let tmp = Unix.dup Unix.stdout in
  let stat = (
    Unix.dup2 fdw Unix.stdout ;
    Printf.printf "\n*** - %s:\n" p; flush stdout;
    Unix.system cmd  (*Invoke the intpreter on f*)
  )
  in
  Unix.dup2 tmp Unix.stdout ; Unix.close tmp; Unix.close fdw;
  match stat with
  | Unix.WEXITED 127 ->
    Printf.printf "The command \"%s\" could not be executed" cmd
  | _ ->
    let fdr = Unix.openfile tf [Unix.O_RDONLY] 0o666 in
    Printf.printf "%s" (read_and_close fdr) ; flush stdout (*; Sys.remove tf*)

let (process_files:string list -> unit) = fun fs ->
  List.iter (fun f -> process_file f) fs

let (main:unit) =
  read_args () ; Printf.printf "%s" !prog ;
  if !version then print_string "0.0.0\n"
  else
    if !suffix = "" then
    raise (Failure
      "Missing argument : suffix") ;
   if not (Sys.file_exists (!prog)) then
      raise (Failure (Printf.sprintf
        "Bad argument : The file '%s' does not exist" !prog)) ;
   try
     match (filesin !root) with
     | Some names ->
       let n=String.length !suffix in
       let pred e =
         let i = (String.length e - n) in
         (i >= 0) && (String.sub e i n) = !suffix
       in process_files (List.filter pred (Array.to_list names) )
     | None -> ()
   with
   | _  as e -> Printf.printf "Exception : %s\n" (Printexc.to_string e)