Blame src/optParse.ml

rpm-build 0f2925
(*
rpm-build 0f2925
 * optParse - Functions for parsing command line arguments.
rpm-build 0f2925
 * Copyright (C) 2004 Bardur Arantsson
rpm-build 0f2925
 *
rpm-build 0f2925
 * Heavily influenced by the optparse.py module from the Python
rpm-build 0f2925
 * standard library, but with lots of adaptation to the 'Ocaml Way'
rpm-build 0f2925
 *
rpm-build 0f2925
 *
rpm-build 0f2925
 * This library is free software; you can redistribute it and/or
rpm-build 0f2925
 * modify it under the terms of the GNU Lesser General Public
rpm-build 0f2925
 * License as published by the Free Software Foundation; either
rpm-build 0f2925
 * version 2.1 of the License, or (at your option) any later version,
rpm-build 0f2925
 * with the special exception on linking described in file LICENSE.
rpm-build 0f2925
 *
rpm-build 0f2925
 * This library is distributed in the hope that it will be useful,
rpm-build 0f2925
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rpm-build 0f2925
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
rpm-build 0f2925
 * Lesser General Public License for more details.
rpm-build 0f2925
 *
rpm-build 0f2925
 * You should have received a copy of the GNU Lesser General Public
rpm-build 0f2925
 * License along with this library; if not, write to the Free Software
rpm-build 0f2925
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
rpm-build 0f2925
 *)
rpm-build 0f2925
open Printf
rpm-build 0f2925
open ExtString
rpm-build 0f2925
open ExtList
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
let terminal_width =
rpm-build 0f2925
  try 
rpm-build 0f2925
    int_of_string (Sys.getenv "COLUMNS")    (* Might as well use it if it's there... *)
rpm-build 0f2925
  with
rpm-build 0f2925
    Failure _ -> 80
rpm-build 0f2925
  | Not_found -> 80
rpm-build 0f2925
rpm-build 0f2925
module GetOpt =
rpm-build 0f2925
  struct
rpm-build 0f2925
rpm-build 0f2925
    type action = string -> string list -> unit
rpm-build 0f2925
    type long_opt = string * int * action
rpm-build 0f2925
    type short_opt = char * int * action
rpm-build 0f2925
rpm-build 0f2925
    exception Error of (string * string)
rpm-build 0f2925
rpm-build 0f2925
    let split1 haystack needle =
rpm-build 0f2925
      try 
rpm-build 0f2925
        let (h, x) = String.split haystack needle in h, [x] 
rpm-build 0f2925
      with
rpm-build 0f2925
        Invalid_string -> haystack, []
rpm-build 0f2925
rpm-build 0f2925
    let find_opt format_name options s =
rpm-build 0f2925
      let rec loop l =
rpm-build 0f2925
        match l with
rpm-build 0f2925
          (x, y, z) :: t -> if x = s then x, y, z else loop t
rpm-build 0f2925
        | [] -> raise (Error (format_name s, "no such option"))
rpm-build 0f2925
      in
rpm-build 0f2925
      loop options
rpm-build 0f2925
rpm-build 0f2925
    let find_short_opt options = find_opt (fun c -> sprintf "-%c" c) options
rpm-build 0f2925
rpm-build 0f2925
    let find_long_opt options = find_opt (fun s -> "--" ^ s) options
rpm-build 0f2925
rpm-build 0f2925
    let parse other find_short_opt find_long_opt args =
rpm-build 0f2925
      let rec loop args =
rpm-build 0f2925
        let rec gather_args name n args =
rpm-build 0f2925
          try 
rpm-build 0f2925
            List.split_nth n args 
rpm-build 0f2925
          with
rpm-build 0f2925
            List.Invalid_index _ ->
rpm-build 0f2925
              raise (Error (name, "missing required arguments"))
rpm-build 0f2925
        in
rpm-build 0f2925
        let gather_long_opt s args =
rpm-build 0f2925
          let (h, t) = split1 s "=" in
rpm-build 0f2925
          let (_, nargs, action) = find_long_opt (String.slice ~first:2 h) in
rpm-build 0f2925
          let (accum, args') = gather_args h (nargs - List.length t) args in
rpm-build 0f2925
          action h (t @ accum); args'
rpm-build 0f2925
        in
rpm-build 0f2925
        let rec gather_short_opt_concat seen_args s k args =
rpm-build 0f2925
          if k < String.length s then
rpm-build 0f2925
            let ostr = sprintf "-%c" s.[k]
rpm-build 0f2925
            and (_, nargs, action) = find_short_opt s.[k] in
rpm-build 0f2925
            if nargs = 0 then
rpm-build 0f2925
              begin
rpm-build 0f2925
                action ostr [];
rpm-build 0f2925
                gather_short_opt_concat seen_args s (k + 1) args
rpm-build 0f2925
              end
rpm-build 0f2925
            else if not seen_args then
rpm-build 0f2925
              let (accum, args') = gather_args ostr nargs args in
rpm-build 0f2925
              action ostr accum; gather_short_opt_concat true s (k + 1) args'
rpm-build 0f2925
            else
rpm-build 0f2925
              raise
rpm-build 0f2925
                (Error
rpm-build 0f2925
                   (sprintf "-%c" s.[k],
rpm-build 0f2925
                    sprintf "option list '%s' already contains an option requiring an argument"
rpm-build 0f2925
                      s))
rpm-build 0f2925
          else args
rpm-build 0f2925
        in
rpm-build 0f2925
        let gather_short_opt s k args =
rpm-build 0f2925
          let ostr = sprintf "-%c" s.[k] in
rpm-build 0f2925
          let (_, nargs, action) = find_short_opt s.[k] in
rpm-build 0f2925
          if nargs = 0 then gather_short_opt_concat false s k args
rpm-build 0f2925
          else
rpm-build 0f2925
            let (accum, args') =
rpm-build 0f2925
              let h = String.slice ~first:(k+1) s in
rpm-build 0f2925
              if String.length h = 0 then gather_args ostr nargs args
rpm-build 0f2925
              else
rpm-build 0f2925
                let (t, args'') = gather_args ostr (nargs - 1) args in
rpm-build 0f2925
                h :: t, args''
rpm-build 0f2925
            in
rpm-build 0f2925
            action ostr accum; args'
rpm-build 0f2925
        in
rpm-build 0f2925
        match args with
rpm-build 0f2925
          [] -> []
rpm-build 0f2925
        | arg :: args' ->
rpm-build 0f2925
            if arg = "--" then args'
rpm-build 0f2925
            else if String.starts_with arg "--" then
rpm-build 0f2925
              loop (gather_long_opt arg args')
rpm-build 0f2925
            else if arg = "-" then begin other arg; loop args' end
rpm-build 0f2925
            else if String.starts_with arg "-" then
rpm-build 0f2925
              loop (gather_short_opt arg 1 args')
rpm-build 0f2925
            else begin other arg; loop args' end
rpm-build 0f2925
      in
rpm-build 0f2925
      let args' = loop args in List.iter other args'
rpm-build 0f2925
  end
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
module Opt =
rpm-build 0f2925
  struct
rpm-build 0f2925
rpm-build 0f2925
    exception No_value
rpm-build 0f2925
    exception Option_error of string * string
rpm-build 0f2925
    exception Option_help
rpm-build 0f2925
rpm-build 0f2925
    type 'a t = { 
rpm-build 0f2925
      option_set : string -> string list -> unit;
rpm-build 0f2925
      option_set_value : 'a -> unit;
rpm-build 0f2925
      option_get : unit -> 'a option;
rpm-build 0f2925
      option_metavars : string list;
rpm-build 0f2925
      option_defhelp : string option 
rpm-build 0f2925
    }
rpm-build 0f2925
rpm-build 0f2925
    let get opt =
rpm-build 0f2925
      match opt.option_get () with
rpm-build 0f2925
        Some x -> x
rpm-build 0f2925
      | None -> raise No_value
rpm-build 0f2925
rpm-build 0f2925
    let set opt v =
rpm-build 0f2925
      opt.option_set_value v
rpm-build 0f2925
rpm-build 0f2925
    let is_set opt = Option.is_some (opt.option_get ())
rpm-build 0f2925
rpm-build 0f2925
    let opt opt = opt.option_get ()
rpm-build 0f2925
rpm-build 0f2925
    let value_option metavar default coerce errfmt =
rpm-build 0f2925
      let data = ref default in
rpm-build 0f2925
      {
rpm-build 0f2925
        option_metavars = [metavar]; 
rpm-build 0f2925
        option_defhelp = None;
rpm-build 0f2925
        option_get = (fun _ -> !data);
rpm-build 0f2925
        option_set_value = (fun x -> data := Some x);
rpm-build 0f2925
        option_set =
rpm-build 0f2925
         (fun option args ->
rpm-build 0f2925
            let arg = List.hd args in
rpm-build 0f2925
              try 
rpm-build 0f2925
                data := Some (coerce arg)
rpm-build 0f2925
              with
rpm-build 0f2925
                  exn -> raise (Option_error (option, errfmt exn arg)))
rpm-build 0f2925
      }
rpm-build 0f2925
rpm-build 0f2925
    let callback_option metavar coerce errfmt f =
rpm-build 0f2925
      {
rpm-build 0f2925
        option_metavars = [metavar]; 
rpm-build 0f2925
        option_defhelp = None;
rpm-build 0f2925
        option_get = (fun _ -> Some ());
rpm-build 0f2925
        option_set_value = (fun () -> ());
rpm-build 0f2925
        option_set =
rpm-build 0f2925
         (fun option args ->
rpm-build 0f2925
            let arg = List.hd args in
rpm-build 0f2925
            let datum = ref None in
rpm-build 0f2925
              begin 
rpm-build 0f2925
              try 
rpm-build 0f2925
                datum := Some (coerce arg)
rpm-build 0f2925
              with
rpm-build 0f2925
                  exn -> raise (Option_error (option, errfmt exn arg))
rpm-build 0f2925
              end;
rpm-build 0f2925
rpm-build 0f2925
              Option.may f !datum)
rpm-build 0f2925
      }
rpm-build 0f2925
  end
rpm-build 0f2925
rpm-build 0f2925
module StdOpt =
rpm-build 0f2925
  struct
rpm-build 0f2925
rpm-build 0f2925
    open Opt
rpm-build 0f2925
rpm-build 0f2925
    let store_const ?default const =
rpm-build 0f2925
      let data = ref default in
rpm-build 0f2925
      {
rpm-build 0f2925
        option_metavars = []; 
rpm-build 0f2925
        option_defhelp = None;
rpm-build 0f2925
        option_get = (fun _ -> !data);
rpm-build 0f2925
        option_set_value = (fun x -> data := Some x);
rpm-build 0f2925
        option_set = fun _ _ -> data := Some const
rpm-build 0f2925
      }
rpm-build 0f2925
rpm-build 0f2925
    let store_true () = store_const ~default:false true
rpm-build 0f2925
rpm-build 0f2925
    let store_false () = store_const ~default:true false
rpm-build 0f2925
rpm-build 0f2925
    let int_option ?default ?(metavar = "INT") () =
rpm-build 0f2925
      value_option metavar default int_of_string
rpm-build 0f2925
        (fun _ s -> sprintf "invalid integer value '%s'" s)
rpm-build 0f2925
rpm-build 0f2925
    let int_callback ?(metavar = "INT") =
rpm-build 0f2925
      callback_option metavar int_of_string
rpm-build 0f2925
        (fun _ s -> sprintf "invalid integer value '%s'" s)
rpm-build 0f2925
rpm-build 0f2925
    let float_option ?default ?(metavar = "FLOAT") () =
rpm-build 0f2925
      value_option metavar default float_of_string
rpm-build 0f2925
        (fun _ s -> sprintf "invalid floating point value '%s'" s)
rpm-build 0f2925
rpm-build 0f2925
    let float_callback ?(metavar = "FLOAT") =
rpm-build 0f2925
      callback_option metavar float_of_string
rpm-build 0f2925
        (fun _ s -> sprintf "invalid floating point value '%s'" s)
rpm-build 0f2925
rpm-build 0f2925
    let str_option ?default ?(metavar = "STR") () =
rpm-build 0f2925
      value_option metavar default (fun s -> s) (fun _ _ -> "cannot happen")
rpm-build 0f2925
rpm-build 0f2925
    let str_callback ?(metavar = "STR") =
rpm-build 0f2925
      callback_option metavar (fun s -> s) (fun _ _ -> "cannot happen")
rpm-build 0f2925
rpm-build 0f2925
    let count_option ?(dest = ref 0) ?(increment = 1) () =
rpm-build 0f2925
      {
rpm-build 0f2925
        option_metavars = []; 
rpm-build 0f2925
        option_defhelp = None;
rpm-build 0f2925
        option_get = (fun _ -> Some !dest);
rpm-build 0f2925
        option_set_value = (fun x -> dest := x);
rpm-build 0f2925
        option_set = fun _ _ -> dest := !dest + increment
rpm-build 0f2925
      }
rpm-build 0f2925
rpm-build 0f2925
    let incr_option ?(dest = ref 0) = 
rpm-build 0f2925
      count_option ~dest ~increment:1
rpm-build 0f2925
rpm-build 0f2925
    let decr_option ?(dest = ref 0) = 
rpm-build 0f2925
      count_option ~dest ~increment:(-1)
rpm-build 0f2925
rpm-build 0f2925
    let help_option () =
rpm-build 0f2925
      {
rpm-build 0f2925
        option_metavars = [];
rpm-build 0f2925
        option_defhelp = Some "show this help message and exit";
rpm-build 0f2925
        option_get = (fun _ -> raise No_value);
rpm-build 0f2925
        option_set_value = (fun _ -> ());
rpm-build 0f2925
        option_set = fun _ _ -> raise Option_help
rpm-build 0f2925
      }
rpm-build 0f2925
rpm-build 0f2925
    let version_option vfunc =
rpm-build 0f2925
      {
rpm-build 0f2925
        option_metavars = [];
rpm-build 0f2925
        option_defhelp = Some "show program's version and exit";
rpm-build 0f2925
        option_get = (fun _ -> raise No_value);
rpm-build 0f2925
        option_set_value = (fun _ -> ());
rpm-build 0f2925
        option_set = fun _ _ -> print_endline (vfunc ()); exit 0
rpm-build 0f2925
      }
rpm-build 0f2925
  end
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
module Formatter =
rpm-build 0f2925
  struct
rpm-build 0f2925
rpm-build 0f2925
    (* Note that the whitespace regexps must NOT treat the non-breaking
rpm-build 0f2925
       space character as whitespace. *)
rpm-build 0f2925
    let whitespace = "\t\n\013\014\r "
rpm-build 0f2925
rpm-build 0f2925
    let split_into_chunks s =
rpm-build 0f2925
      let buf = Buffer.create (String.length s) in
rpm-build 0f2925
      let flush () =
rpm-build 0f2925
        let s = Buffer.contents buf
rpm-build 0f2925
        in
rpm-build 0f2925
          Buffer.clear buf;
rpm-build 0f2925
          s
rpm-build 0f2925
      in
rpm-build 0f2925
      let rec loop state accum i =
rpm-build 0f2925
        if (i
rpm-build 0f2925
          if ((state && not (String.contains whitespace s.[i])) || 
rpm-build 0f2925
              ((not state) && String.contains whitespace s.[i])) then
rpm-build 0f2925
            if Buffer.length buf > 0 then
rpm-build 0f2925
               loop (not state) (flush () :: accum) i 
rpm-build 0f2925
             else 
rpm-build 0f2925
               loop (not state) accum i
rpm-build 0f2925
          else
rpm-build 0f2925
            begin
rpm-build 0f2925
              Buffer.add_char buf s.[i];
rpm-build 0f2925
              loop state accum (i+1)
rpm-build 0f2925
            end
rpm-build 0f2925
        else
rpm-build 0f2925
          if Buffer.length buf > 0 then
rpm-build 0f2925
            flush () :: accum
rpm-build 0f2925
          else 
rpm-build 0f2925
            accum
rpm-build 0f2925
      in
rpm-build 0f2925
        List.rev (loop false [] 0)
rpm-build 0f2925
rpm-build 0f2925
    let is_whitespace s =
rpm-build 0f2925
      let rec loop i =
rpm-build 0f2925
        if i
rpm-build 0f2925
          if String.contains whitespace s.[i] then
rpm-build 0f2925
            loop (i+1)
rpm-build 0f2925
          else 
rpm-build 0f2925
            false
rpm-build 0f2925
        else 
rpm-build 0f2925
          true
rpm-build 0f2925
      in
rpm-build 0f2925
        loop 0
rpm-build 0f2925
rpm-build 0f2925
    let expand_tabs ?(tab_size = 8) s =
rpm-build 0f2925
      let len = String.length s in
rpm-build 0f2925
      let spaces n = String.make n ' '
rpm-build 0f2925
      and b = Buffer.create len in
rpm-build 0f2925
      let rec expand i col =
rpm-build 0f2925
        if i < len then
rpm-build 0f2925
          match s.[i] with
rpm-build 0f2925
            '\t' ->
rpm-build 0f2925
              let n = tab_size - col mod tab_size in
rpm-build 0f2925
              Buffer.add_string b (spaces n);
rpm-build 0f2925
              expand (i + 1) (col + n)
rpm-build 0f2925
          | '\n' -> 
rpm-build 0f2925
              Buffer.add_string b "\n";
rpm-build 0f2925
              expand (i + 1) 0
rpm-build 0f2925
          | c -> 
rpm-build 0f2925
              Buffer.add_char b c;
rpm-build 0f2925
              expand  (i + 1) (col + 1)
rpm-build 0f2925
      in
rpm-build 0f2925
      expand 0 0; 
rpm-build 0f2925
      Buffer.contents b
rpm-build 0f2925
rpm-build 0f2925
    let wrap ?(initial_indent = 0) ?(subsequent_indent = 0) text _width =
rpm-build 0f2925
      let wrap_chunks_line width acc =
rpm-build 0f2925
        let rec wrap (chunks, cur_line, cur_len) =
rpm-build 0f2925
          match chunks with
rpm-build 0f2925
            [] -> [], cur_line, cur_len
rpm-build 0f2925
          | hd :: tl ->
rpm-build 0f2925
              let l = String.length hd in
rpm-build 0f2925
              if cur_len + l <= width then
rpm-build 0f2925
                wrap (tl, hd :: cur_line, cur_len + l)
rpm-build 0f2925
              else chunks, cur_line, cur_len
rpm-build 0f2925
        in
rpm-build 0f2925
        wrap acc
rpm-build 0f2925
      in
rpm-build 0f2925
      let wrap_long_last_word width (chunks, cur_line, cur_len) =
rpm-build 0f2925
        match chunks with
rpm-build 0f2925
          [] -> [], cur_line, cur_len
rpm-build 0f2925
        | hd :: tl ->
rpm-build 0f2925
            let l = String.length hd in
rpm-build 0f2925
            if l > width then
rpm-build 0f2925
              match cur_line with
rpm-build 0f2925
                [] -> tl, [hd], cur_len + l
rpm-build 0f2925
              | _ -> chunks, cur_line, cur_len
rpm-build 0f2925
            else chunks, cur_line, cur_len
rpm-build 0f2925
      in
rpm-build 0f2925
      let wrap_remove_last_ws (chunks, cur_line, cur_len) =
rpm-build 0f2925
        match cur_line with
rpm-build 0f2925
          [] -> chunks, cur_line, cur_len
rpm-build 0f2925
        | hd :: tl ->
rpm-build 0f2925
            if is_whitespace hd then chunks, tl, cur_len - String.length hd
rpm-build 0f2925
            else chunks, cur_line, cur_len
rpm-build 0f2925
      in
rpm-build 0f2925
      let rec wrap_chunks_lines chunks lines =
rpm-build 0f2925
        let indent =
rpm-build 0f2925
          match lines with
rpm-build 0f2925
            [] -> initial_indent
rpm-build 0f2925
          | _ -> subsequent_indent
rpm-build 0f2925
        in
rpm-build 0f2925
        let width = _width - indent in
rpm-build 0f2925
        match chunks with
rpm-build 0f2925
          hd :: tl ->
rpm-build 0f2925
            if is_whitespace hd && lines <> [] then wrap_chunks_lines tl lines
rpm-build 0f2925
            else (* skip *)
rpm-build 0f2925
              let (chunks', cur_line, _) =
rpm-build 0f2925
                wrap_remove_last_ws
rpm-build 0f2925
                  (wrap_long_last_word width
rpm-build 0f2925
                     (wrap_chunks_line width (chunks, [], 0)))
rpm-build 0f2925
              in
rpm-build 0f2925
              wrap_chunks_lines chunks'
rpm-build 0f2925
                ((String.make indent ' ' ^
rpm-build 0f2925
                    String.concat "" (List.rev cur_line)) ::
rpm-build 0f2925
                   lines)
rpm-build 0f2925
        | [] -> List.rev lines
rpm-build 0f2925
      in
rpm-build 0f2925
      let chunks = split_into_chunks (expand_tabs text) in
rpm-build 0f2925
      wrap_chunks_lines chunks []
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
    let fill ?(initial_indent = 0) ?(subsequent_indent = 0) text width =
rpm-build 0f2925
      String.concat "\n" (wrap ~initial_indent ~subsequent_indent text width)
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
    type t = { 
rpm-build 0f2925
      indent : unit -> unit;
rpm-build 0f2925
      dedent : unit -> unit;
rpm-build 0f2925
      format_usage : string -> string;
rpm-build 0f2925
      format_heading : string -> string;
rpm-build 0f2925
      format_description : string -> string;
rpm-build 0f2925
      format_option : char list * string list -> string list -> 
rpm-build 0f2925
                                             string option -> string
rpm-build 0f2925
    }
rpm-build 0f2925
rpm-build 0f2925
    let format_option_strings short_first (snames, lnames) metavars =
rpm-build 0f2925
      let metavar = String.concat " " metavars in
rpm-build 0f2925
      let lopts =
rpm-build 0f2925
        List.map
rpm-build 0f2925
          (match metavar with
rpm-build 0f2925
             "" -> (fun z -> sprintf "--%s" z)
rpm-build 0f2925
           | _ -> fun z -> sprintf "--%s=%s" z metavar)
rpm-build 0f2925
          lnames
rpm-build 0f2925
      and sopts = List.map (fun x -> sprintf "-%c%s" x metavar) snames in
rpm-build 0f2925
      match short_first with
rpm-build 0f2925
        true -> String.concat ", " (sopts @ lopts)
rpm-build 0f2925
      | false -> String.concat ", " (lopts @ sopts)
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
    let indented_formatter ?level:(extlevel = ref 0)
rpm-build 0f2925
      ?indent:(extindent = ref 0) ?(indent_increment = 2) 
rpm-build 0f2925
      ?(max_help_position = 24) ?(width = terminal_width - 1) 
rpm-build 0f2925
      ?(short_first = true) () =
rpm-build 0f2925
      let indent = ref 0
rpm-build 0f2925
      and level = ref 0 in
rpm-build 0f2925
      let help_position = ref max_help_position
rpm-build 0f2925
      and help_width = ref (width - max_help_position) in
rpm-build 0f2925
      {
rpm-build 0f2925
        indent =
rpm-build 0f2925
         (fun () ->
rpm-build 0f2925
            indent := !indent + indent_increment;
rpm-build 0f2925
            level := !level + 1;
rpm-build 0f2925
            extindent := !indent;
rpm-build 0f2925
            extlevel := !level);
rpm-build 0f2925
rpm-build 0f2925
        dedent =
rpm-build 0f2925
         (fun () ->
rpm-build 0f2925
            indent := !indent - indent_increment;
rpm-build 0f2925
            level := !level - 1;
rpm-build 0f2925
            assert (!level >= 0);
rpm-build 0f2925
            extindent := !indent;
rpm-build 0f2925
            extlevel := !level);
rpm-build 0f2925
        
rpm-build 0f2925
        format_usage = (fun usage -> sprintf "usage: %s\n" usage);
rpm-build 0f2925
        
rpm-build 0f2925
        format_heading =
rpm-build 0f2925
         (fun heading -> sprintf "%*s%s:\n\n" !indent "" heading);
rpm-build 0f2925
        
rpm-build 0f2925
        format_description =
rpm-build 0f2925
         (fun description ->
rpm-build 0f2925
            let x =
rpm-build 0f2925
              fill ~initial_indent:(!indent) ~subsequent_indent:(!indent)
rpm-build 0f2925
                description (width - !indent)
rpm-build 0f2925
            in
rpm-build 0f2925
              if not (String.ends_with x "\n") then x ^ "\n\n" else x ^ "\n");
rpm-build 0f2925
        
rpm-build 0f2925
        format_option =
rpm-build 0f2925
         fun names metavars help ->
rpm-build 0f2925
           let opt_width = !help_position - !indent - 2 in
rpm-build 0f2925
           let opt_strings =
rpm-build 0f2925
             format_option_strings short_first names metavars
rpm-build 0f2925
           in
rpm-build 0f2925
           let buf = Buffer.create 256 in
rpm-build 0f2925
           let indent_first =
rpm-build 0f2925
             if String.length opt_strings > opt_width then
rpm-build 0f2925
               begin
rpm-build 0f2925
                 bprintf buf "%*s%s\n" !indent "" opt_strings; !help_position
rpm-build 0f2925
               end
rpm-build 0f2925
             else
rpm-build 0f2925
               begin
rpm-build 0f2925
                 bprintf buf "%*s%-*s  " !indent "" opt_width opt_strings; 0
rpm-build 0f2925
               end
rpm-build 0f2925
           in
rpm-build 0f2925
           Option.may
rpm-build 0f2925
             (fun option_help ->
rpm-build 0f2925
                let lines = wrap option_help !help_width in
rpm-build 0f2925
                match lines with
rpm-build 0f2925
                  h :: t ->
rpm-build 0f2925
                    bprintf buf "%*s%s\n" indent_first "" h;
rpm-build 0f2925
                    List.iter
rpm-build 0f2925
                      (fun x -> bprintf buf "%*s%s\n" !help_position "" x) t
rpm-build 0f2925
                | [] -> ())
rpm-build 0f2925
             help;
rpm-build 0f2925
rpm-build 0f2925
           let contents =
rpm-build 0f2925
             Buffer.contents buf
rpm-build 0f2925
           in
rpm-build 0f2925
             if String.length contents > 0 && not (String.ends_with contents "\n") then
rpm-build 0f2925
               contents ^ "\n"
rpm-build 0f2925
             else
rpm-build 0f2925
               contents
rpm-build 0f2925
      }
rpm-build 0f2925
rpm-build 0f2925
    let titled_formatter ?(level = ref 0) ?(indent = ref 0) 
rpm-build 0f2925
      ?(indent_increment = 0) ?(max_help_position = 24) 
rpm-build 0f2925
      ?(width = terminal_width - 1) ?(short_first = true) 
rpm-build 0f2925
      () =
rpm-build 0f2925
      let formatter =
rpm-build 0f2925
        indented_formatter ~level ~indent ~indent_increment ~max_help_position
rpm-build 0f2925
          ~width ~short_first ()
rpm-build 0f2925
      in
rpm-build 0f2925
      let format_heading h =
rpm-build 0f2925
        let c =
rpm-build 0f2925
          match !level with
rpm-build 0f2925
            0 -> '='
rpm-build 0f2925
          | 1 -> '-'
rpm-build 0f2925
          | _ -> failwith "titled_formatter: Too much indentation"
rpm-build 0f2925
        in
rpm-build 0f2925
        sprintf "%*s%s\n%*s%s\n\n" !indent "" (String.capitalize h) !indent ""
rpm-build 0f2925
          (String.make (String.length h) c)
rpm-build 0f2925
      in
rpm-build 0f2925
      let format_usage usage =
rpm-build 0f2925
        sprintf "%s  %s\n" (format_heading "Usage") usage
rpm-build 0f2925
      in
rpm-build 0f2925
      { formatter with 
rpm-build 0f2925
          format_usage = format_usage;
rpm-build 0f2925
          format_heading = format_heading
rpm-build 0f2925
      }
rpm-build 0f2925
  end
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
rpm-build 0f2925
open Opt
rpm-build 0f2925
open Formatter
rpm-build 0f2925
rpm-build 0f2925
module OptParser =
rpm-build 0f2925
  struct
rpm-build 0f2925
rpm-build 0f2925
    exception Option_conflict of string
rpm-build 0f2925
rpm-build 0f2925
    type group = { 
rpm-build 0f2925
      og_heading : string;
rpm-build 0f2925
      og_description : string option;
rpm-build 0f2925
      og_options :
rpm-build 0f2925
        ((char list * string list) * string list * string option) RefList.t;
rpm-build 0f2925
      og_children : group RefList.t 
rpm-build 0f2925
    }
rpm-build 0f2925
rpm-build 0f2925
    type t = { 
rpm-build 0f2925
      op_usage : string;
rpm-build 0f2925
      op_status : int;
rpm-build 0f2925
      op_suppress_usage : bool;
rpm-build 0f2925
      op_prog : string;
rpm-build 0f2925
rpm-build 0f2925
      op_formatter : Formatter.t;
rpm-build 0f2925
      
rpm-build 0f2925
      op_long_options : GetOpt.long_opt RefList.t;
rpm-build 0f2925
      op_short_options : GetOpt.short_opt RefList.t;
rpm-build 0f2925
      
rpm-build 0f2925
      op_groups : group 
rpm-build 0f2925
    }
rpm-build 0f2925
rpm-build 0f2925
    let unprogify optparser s =
rpm-build 0f2925
      (snd (String.replace ~str:s ~sub:"%prog" ~by:optparser.op_prog))
rpm-build 0f2925
rpm-build 0f2925
    let add optparser ?(group = optparser.op_groups) ?help ?(hide = false)
rpm-build 0f2925
      ?short_name ?(short_names = []) ?long_name ?(long_names = []) opt =
rpm-build 0f2925
      let lnames =
rpm-build 0f2925
        match long_name with
rpm-build 0f2925
            None -> long_names
rpm-build 0f2925
          | Some x -> x :: long_names
rpm-build 0f2925
      and snames =
rpm-build 0f2925
        match short_name with
rpm-build 0f2925
            None -> short_names
rpm-build 0f2925
          | Some x -> x :: short_names
rpm-build 0f2925
      in
rpm-build 0f2925
      if lnames = [] && snames = [] then
rpm-build 0f2925
        failwith "Options must have at least one name"
rpm-build 0f2925
      else
rpm-build 0f2925
        (* Checking for duplicates: *)
rpm-build 0f2925
        let snames' =
rpm-build 0f2925
          List.fold_left (fun r (x, _, _) -> x :: r) []
rpm-build 0f2925
            (RefList.to_list optparser.op_short_options)
rpm-build 0f2925
        and lnames' =
rpm-build 0f2925
          List.fold_left (fun r (x, _, _) -> x :: r) []
rpm-build 0f2925
            (RefList.to_list optparser.op_long_options)
rpm-build 0f2925
        in
rpm-build 0f2925
        let sconf =
rpm-build 0f2925
          List.filter (fun e -> List.exists (( = ) e) snames') snames
rpm-build 0f2925
        and lconf =
rpm-build 0f2925
          List.filter (fun e -> List.exists (( = ) e) lnames') lnames
rpm-build 0f2925
        in
rpm-build 0f2925
        if List.length sconf > 0 then
rpm-build 0f2925
          raise (Option_conflict (sprintf "-%c" (List.hd sconf)))
rpm-build 0f2925
        else if List.length lconf > 0 then
rpm-build 0f2925
          raise (Option_conflict (sprintf "--%s" (List.hd lconf)));
rpm-build 0f2925
          
rpm-build 0f2925
        (* Add to display list. *)
rpm-build 0f2925
        if not hide then
rpm-build 0f2925
          RefList.add group.og_options
rpm-build 0f2925
            ((snames, lnames), opt.option_metavars,
rpm-build 0f2925
             (match help with
rpm-build 0f2925
                  None -> opt.option_defhelp
rpm-build 0f2925
                | Some _ -> help));
rpm-build 0f2925
          
rpm-build 0f2925
        (* Getopt: *)
rpm-build 0f2925
        let nargs = List.length opt.option_metavars in
rpm-build 0f2925
          List.iter
rpm-build 0f2925
            (fun short ->
rpm-build 0f2925
               RefList.add optparser.op_short_options
rpm-build 0f2925
               (short, nargs, opt.option_set))
rpm-build 0f2925
            snames;
rpm-build 0f2925
          List.iter
rpm-build 0f2925
            (fun long ->
rpm-build 0f2925
               RefList.add optparser.op_long_options
rpm-build 0f2925
               (long, nargs, opt.option_set))
rpm-build 0f2925
            lnames
rpm-build 0f2925
            
rpm-build 0f2925
    let add_group optparser ?(parent = optparser.op_groups) ?description heading =
rpm-build 0f2925
      let g =
rpm-build 0f2925
        {
rpm-build 0f2925
          og_heading = heading; 
rpm-build 0f2925
          og_description = description;
rpm-build 0f2925
          og_options = RefList.empty (); 
rpm-build 0f2925
          og_children = RefList.empty ()
rpm-build 0f2925
        }
rpm-build 0f2925
      in
rpm-build 0f2925
      RefList.add parent.og_children g; g
rpm-build 0f2925
rpm-build 0f2925
    let make ?(usage = "%prog [options]") ?(status = 1) ?description ?version
rpm-build 0f2925
      ?(suppress_usage = false) ?(suppress_help = false) ?prog 
rpm-build 0f2925
      ?(formatter = Formatter.indented_formatter ()) () =
rpm-build 0f2925
      let optparser =
rpm-build 0f2925
        {
rpm-build 0f2925
          op_usage = usage; 
rpm-build 0f2925
          op_status = status;
rpm-build 0f2925
          op_suppress_usage = suppress_usage;
rpm-build 0f2925
          op_prog = Option.default (Filename.basename Sys.argv.(0)) prog;
rpm-build 0f2925
          op_formatter = formatter; 
rpm-build 0f2925
          op_short_options = RefList.empty ();
rpm-build 0f2925
          op_long_options = RefList.empty ();
rpm-build 0f2925
          op_groups = {
rpm-build 0f2925
            og_heading = "options"; 
rpm-build 0f2925
            og_options = RefList.empty ();
rpm-build 0f2925
            og_children = RefList.empty ();
rpm-build 0f2925
            og_description = description
rpm-build 0f2925
          }
rpm-build 0f2925
        }
rpm-build 0f2925
      in
rpm-build 0f2925
      Option.may                         (* Add version option? *)
rpm-build 0f2925
        (fun version ->
rpm-build 0f2925
           add optparser ~long_name:"version"
rpm-build 0f2925
             (StdOpt.version_option
rpm-build 0f2925
                (fun () -> unprogify optparser version)))
rpm-build 0f2925
        version;
rpm-build 0f2925
      if not suppress_help then              (* Add help option? *)
rpm-build 0f2925
        add optparser ~short_name:'h' ~long_name:"help"
rpm-build 0f2925
          (StdOpt.help_option ());
rpm-build 0f2925
rpm-build 0f2925
      optparser
rpm-build 0f2925
rpm-build 0f2925
    let format_usage optparser eol =
rpm-build 0f2925
      match optparser.op_suppress_usage with
rpm-build 0f2925
        true -> ""
rpm-build 0f2925
      | false ->
rpm-build 0f2925
          unprogify optparser
rpm-build 0f2925
            (optparser.op_formatter.format_usage optparser.op_usage) ^ eol
rpm-build 0f2925
rpm-build 0f2925
    let error optparser ?(chn = stderr) ?status message =
rpm-build 0f2925
      fprintf chn "%s%s: %s\n" (format_usage optparser "\n") optparser.op_prog
rpm-build 0f2925
        message;
rpm-build 0f2925
      flush chn;
rpm-build 0f2925
      exit (Option.default optparser.op_status status)
rpm-build 0f2925
rpm-build 0f2925
    let usage optparser ?(chn = stdout) () =
rpm-build 0f2925
      let rec loop g =
rpm-build 0f2925
        (* Heading: *)
rpm-build 0f2925
        output_string chn
rpm-build 0f2925
          (optparser.op_formatter.format_heading g.og_heading);
rpm-build 0f2925
rpm-build 0f2925
        optparser.op_formatter.indent ();
rpm-build 0f2925
        (* Description: *)
rpm-build 0f2925
        Option.may
rpm-build 0f2925
          (fun x ->
rpm-build 0f2925
             output_string chn (optparser.op_formatter.format_description x))
rpm-build 0f2925
          g.og_description;
rpm-build 0f2925
        (* Options: *)
rpm-build 0f2925
        RefList.iter
rpm-build 0f2925
          (fun (names, metavars, help) ->
rpm-build 0f2925
             output_string chn
rpm-build 0f2925
               (optparser.op_formatter.format_option names metavars help))
rpm-build 0f2925
          g.og_options;
rpm-build 0f2925
        (* Child groups: *)
rpm-build 0f2925
        output_string chn "\n";
rpm-build 0f2925
        RefList.iter loop g.og_children;
rpm-build 0f2925
rpm-build 0f2925
        optparser.op_formatter.dedent ()
rpm-build 0f2925
      in
rpm-build 0f2925
      output_string chn (format_usage optparser "\n");
rpm-build 0f2925
      loop optparser.op_groups;
rpm-build 0f2925
      flush chn
rpm-build 0f2925
rpm-build 0f2925
    let parse optparser ?(first = 0) ?last argv =
rpm-build 0f2925
      let args = RefList.empty ()
rpm-build 0f2925
      and n =
rpm-build 0f2925
        match last with
rpm-build 0f2925
          None -> Array.length argv - first
rpm-build 0f2925
        | Some m -> m - first + 1
rpm-build 0f2925
      in
rpm-build 0f2925
      begin 
rpm-build 0f2925
        try
rpm-build 0f2925
          GetOpt.parse (RefList.push args)
rpm-build 0f2925
            (GetOpt.find_short_opt
rpm-build 0f2925
               (RefList.to_list optparser.op_short_options))
rpm-build 0f2925
            (GetOpt.find_long_opt (RefList.to_list optparser.op_long_options))
rpm-build 0f2925
            (Array.to_list (Array.sub argv first n))
rpm-build 0f2925
        with
rpm-build 0f2925
            GetOpt.Error (opt, errmsg) ->
rpm-build 0f2925
              error optparser (sprintf "option '%s': %s" opt errmsg)
rpm-build 0f2925
          | Option_error (opt, errmsg) ->
rpm-build 0f2925
              error optparser (sprintf "option '%s': %s" opt errmsg)
rpm-build 0f2925
          | Option_help -> usage optparser (); exit 0
rpm-build 0f2925
      end;
rpm-build 0f2925
      List.rev (RefList.to_list args)
rpm-build 0f2925
rpm-build 0f2925
    let parse_argv optparser = 
rpm-build 0f2925
      parse optparser ~first:1 Sys.argv
rpm-build 0f2925
  end