(* $Id: extract_args.ml 163 2012-05-07 13:03:54Z gerd $ *)

(* Runs ocamlc -help and extract the command-line signature *)

open Printf


let read_lines file =
  let f = open_in file in
  let lines = ref [] in
  try
    while true do
      lines := input_line f :: !lines
    done;
    assert false
  with
    | End_of_file ->
	close_in f;
	List.rev !lines
    | error ->
	close_in f; 
	raise error
;;
      

let get_help cmd =
  let temp_file = 
    Filename.temp_file "findlib" ".txt" in
  let help_out =
    try
      let code =
	Sys.command (sprintf "%s -help >%s 2>&1"
		       cmd
		       (Filename.quote temp_file)) in
      if code <> 0 then 
	raise Not_found;  (* Assume command does not exist! *)
      let lines = read_lines temp_file in
      Sys.remove temp_file;
      lines
    with error ->
      Sys.remove temp_file; raise error in
  help_out
;;


let switch1_re = Str.regexp "[ \t]*\\(-[-a-zA-Z0-9_,]+\\)[ \t]?\\(.*\\)$";;
let switch2_re = Str.regexp "[ \t]*\\(-\\)[ \t]+\\(.*\\)$";;

let argument_re = Str.regexp "[ \t]*[<[]";;

let rec extract_signature lines =
  match lines with
    | [] ->
	[]
    | line :: lines' ->
	if Str.string_match switch1_re line 0 || Str.string_match switch2_re line 0 then (
	  let switch_name = Str.matched_group 1 line in
	  let help_text = Str.matched_group 2 line in
	  let has_arg = Str.string_match argument_re help_text 0 in
	  let help_lines, lines'' = extract_help_continuation lines' in
	  let help_text' = String.concat "\n" (help_text :: help_lines) in
	  let r = 
	    (switch_name, has_arg, help_text') in
	  r :: extract_signature lines''
	)
	else
	  extract_signature lines'
  
and extract_help_continuation lines =
  match lines with
    | [] ->
	( [], [] )
    | line :: lines' ->
	if Str.string_match switch1_re line 0 || Str.string_match switch2_re line 0 then
	  ( [], lines )
	else
	  let help_lines, lines'' = extract_help_continuation lines' in
	  (line :: help_lines, lines'')
;;


let rm_help_switch switches =
  List.filter
    (fun (name, _, _) ->
       name <> "-help" && name <> "--help")
    switches
;;


let output_some_signature f name switches =
  fprintf f "let %s_spec = Some [\n" name;
  List.iter
    (fun (switch_name, has_arg, help_text) ->
       fprintf f "  \"%s\",\n" (String.escaped switch_name);
       fprintf f "  %b,\n" has_arg;
       fprintf f "  \"%s\";\n\n" (String.escaped help_text)
    )
    switches;
  fprintf f "];;\n\n"
;;


let output_none f name =
  fprintf f "let %s_spec = None;;\n\n" name
;;


let main() =
  let f = ref stdout in
  let progs = ref [] in
  Arg.parse
    [ "-o", Arg.String (fun s -> f := open_out s),
      "<file>  Save generated module to this file";
    ]
    (fun arg -> progs := arg :: !progs)
    "usage: extract_args <options> <command> ...";

  progs := List.rev !progs;

  List.iter
    (fun prog ->
       try
	 let help_lines = get_help prog in  (* or Not_found *)
	 let switches = rm_help_switch (extract_signature help_lines) in
	 output_some_signature !f prog switches
       with
	   Not_found ->
	     output_none !f prog
    )
    !progs;

  close_out !f
;;


main();;

