This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,13 @@
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -0,0 +1,33 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
module Manpage = Cmdliner_manpage
module Term = struct
include Cmdliner_term
include Cmdliner_term_deprecated
end
module Cmd = struct
module Exit = Cmdliner_info.Exit
module Env = Cmdliner_info.Env
include Cmdliner_cmd
include Cmdliner_eval
end
module Arg = Cmdliner_arg
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,406 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
let rev_compare n0 n1 = compare n1 n0
(* Invalid_argument strings **)
let err_not_opt = "Option argument without name"
let err_not_pos = "Positional argument with a name"
(* Documentation formatting helpers *)
let strf = Printf.sprintf
let doc_quote = Cmdliner_base.quote
let doc_alts = Cmdliner_base.alts_str
let doc_alts_enum ?quoted enum = doc_alts ?quoted (List.map fst enum)
let str_of_pp pp v = pp Format.str_formatter v; Format.flush_str_formatter ()
(* Argument converters *)
type 'a parser = string -> [ `Ok of 'a | `Error of string ]
type 'a printer = Format.formatter -> 'a -> unit
type 'a conv = 'a parser * 'a printer
type 'a converter = 'a conv
let default_docv = "VALUE"
let conv ?docv (parse, print) =
let parse s = match parse s with Ok v -> `Ok v | Error (`Msg e) -> `Error e in
parse, print
let conv' ?docv (parse, print) =
let parse s = match parse s with Ok v -> `Ok v | Error e -> `Error e in
parse, print
let pconv ?docv conv = conv
let conv_parser (parse, _) =
fun s -> match parse s with `Ok v -> Ok v | `Error e -> Error (`Msg e)
let conv_printer (_, print) = print
let conv_docv _ = default_docv
let err_invalid s kind = `Msg (strf "invalid value '%s', expected %s" s kind)
let parser_of_kind_of_string ~kind k_of_string =
fun s -> match k_of_string s with
| None -> Error (err_invalid s kind)
| Some v -> Ok v
let some = Cmdliner_base.some
let some' = Cmdliner_base.some'
(* Argument information *)
type env = Cmdliner_info.Env.info
let env_var = Cmdliner_info.Env.info
type 'a t = 'a Cmdliner_term.t
type info = Cmdliner_info.Arg.t
let info = Cmdliner_info.Arg.v
(* Arguments *)
let ( & ) f x = f x
let err e = Error (`Parse e)
let parse_to_list parser s = match parser s with
| `Ok v -> `Ok [v]
| `Error _ as e -> e
let report_deprecated_env ei e = match Cmdliner_info.Env.info_deprecated e with
| None -> ()
| Some msg ->
let var = Cmdliner_info.Env.info_var e in
let msg = String.concat "" ["environment variable "; var; ": "; msg ] in
let err_fmt = Cmdliner_info.Eval.err_ppf ei in
Cmdliner_msg.pp_err err_fmt ei ~err:msg
let try_env ei a parse ~absent = match Cmdliner_info.Arg.env a with
| None -> Ok absent
| Some env ->
let var = Cmdliner_info.Env.info_var env in
match Cmdliner_info.Eval.env_var ei var with
| None -> Ok absent
| Some v ->
match parse v with
| `Error e -> err (Cmdliner_msg.err_env_parse env ~err:e)
| `Ok v -> report_deprecated_env ei env; Ok v
let arg_to_args = Cmdliner_info.Arg.Set.singleton
let list_to_args f l =
let add acc v = Cmdliner_info.Arg.Set.add (f v) acc in
List.fold_left add Cmdliner_info.Arg.Set.empty l
let alias_opt aliases a =
let a = Cmdliner_info.Arg.make_opt ~absent:Err ~kind:Opt a in
let aliases = (fun f -> function
| None -> Error (Cmdliner_msg.err_opt_value_missing f)
| Some o -> Ok (aliases o)) in
let a = Cmdliner_info.Arg.aliases ~aliases a in
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else
let convert ei cl = match Cmdliner_cline.opt_arg cl a with
| [] -> try_env ei a Cmdliner_base.env_bool_parse ~absent:false
| [_, _, None] -> Ok true
| [_, f, Some v] -> Ok true
| (_, f, _) :: (_ ,g, _) :: _ -> err (Cmdliner_msg.err_opt_repeated f g)
in
arg_to_args a, convert
let alias aliases a =
let aliases = (fun f -> function
| Some v -> Error (Cmdliner_msg.err_flag_value f v)
| None -> Ok aliases) in
let a = Cmdliner_info.Arg.aliases ~aliases a in
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else
let convert ei cl = match Cmdliner_cline.opt_arg cl a with
| [] -> try_env ei a Cmdliner_base.env_bool_parse ~absent:false
| [_, _, None] -> Ok true
| [_, f, Some v] -> err (Cmdliner_msg.err_flag_value f v)
| (_, f, _) :: (_ ,g, _) :: _ -> err (Cmdliner_msg.err_opt_repeated f g)
in
arg_to_args a, convert
let flag a =
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else
let convert ei cl = match Cmdliner_cline.opt_arg cl a with
| [] -> try_env ei a Cmdliner_base.env_bool_parse ~absent:false
| [_, _, None] -> Ok true
| [_, f, Some v] -> err (Cmdliner_msg.err_flag_value f v)
| (_, f, _) :: (_ ,g, _) :: _ -> err (Cmdliner_msg.err_opt_repeated f g)
in
arg_to_args a, convert
let flag_all a =
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else
let a = Cmdliner_info.Arg.make_all_opts a in
let convert ei cl = match Cmdliner_cline.opt_arg cl a with
| [] ->
try_env ei a (parse_to_list Cmdliner_base.env_bool_parse) ~absent:[]
| l ->
try
let truth (_, f, v) = match v with
| None -> true
| Some v -> failwith (Cmdliner_msg.err_flag_value f v)
in
Ok (List.rev_map truth l)
with Failure e -> err e
in
arg_to_args a, convert
let vflag v l =
let convert _ cl =
let rec aux fv = function
| (v, a) :: rest ->
begin match Cmdliner_cline.opt_arg cl a with
| [] -> aux fv rest
| [_, f, None] ->
begin match fv with
| None -> aux (Some (f, v)) rest
| Some (g, _) -> failwith (Cmdliner_msg.err_opt_repeated g f)
end
| [_, f, Some v] -> failwith (Cmdliner_msg.err_flag_value f v)
| (_, f, _) :: (_, g, _) :: _ ->
failwith (Cmdliner_msg.err_opt_repeated g f)
end
| [] -> match fv with None -> v | Some (_, v) -> v
in
try Ok (aux None l) with Failure e -> err e
in
let flag (_, a) =
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else a
in
list_to_args flag l, convert
let vflag_all v l =
let convert _ cl =
let rec aux acc = function
| (fv, a) :: rest ->
begin match Cmdliner_cline.opt_arg cl a with
| [] -> aux acc rest
| l ->
let fval (k, f, v) = match v with
| None -> (k, fv)
| Some v -> failwith (Cmdliner_msg.err_flag_value f v)
in
aux (List.rev_append (List.rev_map fval l) acc) rest
end
| [] ->
if acc = [] then v else List.rev_map snd (List.sort rev_compare acc)
in
try Ok (aux [] l) with Failure e -> err e
in
let flag (_, a) =
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else
Cmdliner_info.Arg.make_all_opts a
in
list_to_args flag l, convert
let parse_opt_value parse f v = match parse v with
| `Ok v -> v
| `Error err -> failwith (Cmdliner_msg.err_opt_parse f ~err)
let opt ?vopt (parse, print) v a =
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else
let absent = match Cmdliner_info.Arg.absent a with
| Cmdliner_info.Arg.Doc d as a when d <> "" -> a
| _ -> Cmdliner_info.Arg.Val (lazy (str_of_pp print v))
in
let kind = match vopt with
| None -> Cmdliner_info.Arg.Opt
| Some dv -> Cmdliner_info.Arg.Opt_vopt (str_of_pp print dv)
in
let a = Cmdliner_info.Arg.make_opt ~absent ~kind a in
let convert ei cl = match Cmdliner_cline.opt_arg cl a with
| [] -> try_env ei a parse ~absent:v
| [_, f, Some v] ->
(try Ok (parse_opt_value parse f v) with Failure e -> err e)
| [_, f, None] ->
begin match vopt with
| None -> err (Cmdliner_msg.err_opt_value_missing f)
| Some optv -> Ok optv
end
| (_, f, _) :: (_, g, _) :: _ -> err (Cmdliner_msg.err_opt_repeated g f)
in
arg_to_args a, convert
let opt_all ?vopt (parse, print) v a =
if Cmdliner_info.Arg.is_pos a then invalid_arg err_not_opt else
let absent = match Cmdliner_info.Arg.absent a with
| Cmdliner_info.Arg.Doc d as a when d <> "" -> a
| _ -> Cmdliner_info.Arg.Val (lazy "")
in
let kind = match vopt with
| None -> Cmdliner_info.Arg.Opt
| Some dv -> Cmdliner_info.Arg.Opt_vopt (str_of_pp print dv)
in
let a = Cmdliner_info.Arg.make_opt_all ~absent ~kind a in
let convert ei cl = match Cmdliner_cline.opt_arg cl a with
| [] -> try_env ei a (parse_to_list parse) ~absent:v
| l ->
let parse (k, f, v) = match v with
| Some v -> (k, parse_opt_value parse f v)
| None -> match vopt with
| None -> failwith (Cmdliner_msg.err_opt_value_missing f)
| Some dv -> (k, dv)
in
try Ok (List.rev_map snd
(List.sort rev_compare (List.rev_map parse l))) with
| Failure e -> err e
in
arg_to_args a, convert
(* Positional arguments *)
let parse_pos_value parse a v = match parse v with
| `Ok v -> v
| `Error err -> failwith (Cmdliner_msg.err_pos_parse a ~err)
let pos ?(rev = false) k (parse, print) v a =
if Cmdliner_info.Arg.is_opt a then invalid_arg err_not_pos else
let absent = match Cmdliner_info.Arg.absent a with
| Cmdliner_info.Arg.Doc d as a when d <> "" -> a
| _ -> Cmdliner_info.Arg.Val (lazy (str_of_pp print v))
in
let pos = Cmdliner_info.Arg.pos ~rev ~start:k ~len:(Some 1) in
let a = Cmdliner_info.Arg.make_pos_abs ~absent ~pos a in
let convert ei cl = match Cmdliner_cline.pos_arg cl a with
| [] -> try_env ei a parse ~absent:v
| [v] ->
(try Ok (parse_pos_value parse a v) with Failure e -> err e)
| _ -> assert false
in
arg_to_args a, convert
let pos_list pos (parse, _) v a =
if Cmdliner_info.Arg.is_opt a then invalid_arg err_not_pos else
let a = Cmdliner_info.Arg.make_pos ~pos a in
let convert ei cl = match Cmdliner_cline.pos_arg cl a with
| [] -> try_env ei a (parse_to_list parse) ~absent:v
| l ->
try Ok (List.rev (List.rev_map (parse_pos_value parse a) l)) with
| Failure e -> err e
in
arg_to_args a, convert
let all = Cmdliner_info.Arg.pos ~rev:false ~start:0 ~len:None
let pos_all c v a = pos_list all c v a
let pos_left ?(rev = false) k =
let start = if rev then k + 1 else 0 in
let len = if rev then None else Some k in
pos_list (Cmdliner_info.Arg.pos ~rev ~start ~len)
let pos_right ?(rev = false) k =
let start = if rev then 0 else k + 1 in
let len = if rev then Some k else None in
pos_list (Cmdliner_info.Arg.pos ~rev ~start ~len)
(* Arguments as terms *)
let absent_error args =
let make_req a acc =
let req_a = Cmdliner_info.Arg.make_req a in
Cmdliner_info.Arg.Set.add req_a acc
in
Cmdliner_info.Arg.Set.fold make_req args Cmdliner_info.Arg.Set.empty
let value a = a
let err_arg_missing args =
err @@ Cmdliner_msg.err_arg_missing (Cmdliner_info.Arg.Set.choose args)
let required (args, convert) =
let args = absent_error args in
let convert ei cl = match convert ei cl with
| Ok (Some v) -> Ok v
| Ok None -> err_arg_missing args
| Error _ as e -> e
in
args, convert
let non_empty (al, convert) =
let args = absent_error al in
let convert ei cl = match convert ei cl with
| Ok [] -> err_arg_missing args
| Ok l -> Ok l
| Error _ as e -> e
in
args, convert
let last (args, convert) =
let convert ei cl = match convert ei cl with
| Ok [] -> err_arg_missing args
| Ok l -> Ok (List.hd (List.rev l))
| Error _ as e -> e
in
args, convert
(* Predefined arguments *)
let man_fmts =
["auto", `Auto; "pager", `Pager; "groff", `Groff; "plain", `Plain]
let man_fmt_docv = "FMT"
let man_fmts_enum = Cmdliner_base.enum man_fmts
let man_fmts_alts = doc_alts_enum man_fmts
let man_fmts_doc kind =
strf "Show %s in format $(docv). The value $(docv) must be %s. \
With $(b,auto), the format is $(b,pager) or $(b,plain) whenever \
the $(b,TERM) env var is $(b,dumb) or undefined."
kind man_fmts_alts
let man_format =
let doc = man_fmts_doc "output" in
let docv = man_fmt_docv in
value & opt man_fmts_enum `Pager & info ["man-format"] ~docv ~doc
let stdopt_version ~docs =
value & flag & info ["version"] ~docs ~doc:"Show version information."
let stdopt_help ~docs =
let doc = man_fmts_doc "this help" in
let docv = man_fmt_docv in
value & opt ~vopt:(Some `Auto) (some man_fmts_enum) None &
info ["help"] ~docv ~docs ~doc
(* Predefined converters. *)
let bool = Cmdliner_base.bool
let char = Cmdliner_base.char
let int = Cmdliner_base.int
let nativeint = Cmdliner_base.nativeint
let int32 = Cmdliner_base.int32
let int64 = Cmdliner_base.int64
let float = Cmdliner_base.float
let string = Cmdliner_base.string
let enum = Cmdliner_base.enum
let file = Cmdliner_base.file
let dir = Cmdliner_base.dir
let non_dir_file = Cmdliner_base.non_dir_file
let list = Cmdliner_base.list
let array = Cmdliner_base.array
let pair = Cmdliner_base.pair
let t2 = Cmdliner_base.t2
let t3 = Cmdliner_base.t3
let t4 = Cmdliner_base.t4
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,117 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Command line arguments as terms. *)
type 'a parser = string -> [ `Ok of 'a | `Error of string ]
type 'a printer = Format.formatter -> 'a -> unit
type 'a conv = 'a parser * 'a printer
type 'a converter = 'a conv
val conv :
?docv:string -> (string -> ('a, [`Msg of string]) result) * 'a printer ->
'a conv
val conv' :
?docv:string -> (string -> ('a, string) result) * 'a printer -> 'a conv
val pconv : ?docv:string -> 'a parser * 'a printer -> 'a conv
val conv_parser : 'a conv -> (string -> ('a, [`Msg of string]) result)
val conv_printer : 'a conv -> 'a printer
val conv_docv : 'a conv -> string
val parser_of_kind_of_string :
kind:string -> (string -> 'a option) ->
(string -> ('a, [`Msg of string]) result)
val some : ?none:string -> 'a converter -> 'a option converter
val some' : ?none:'a -> 'a converter -> 'a option converter
type env = Cmdliner_info.Env.info
val env_var : ?deprecated:string -> ?docs:string -> ?doc:string -> string -> env
type 'a t = 'a Cmdliner_term.t
type info
val info :
?deprecated:string -> ?absent:string -> ?docs:string -> ?docv:string ->
?doc:string -> ?env:env -> string list -> info
val ( & ) : ('a -> 'b) -> 'a -> 'b
val flag : info -> bool t
val flag_all : info -> bool list t
val vflag : 'a -> ('a * info) list -> 'a t
val vflag_all : 'a list -> ('a * info) list -> 'a list t
val alias : string list -> info -> bool t
val alias_opt : (string -> string list) -> info -> bool t
val opt : ?vopt:'a -> 'a converter -> 'a -> info -> 'a t
val opt_all : ?vopt:'a -> 'a converter -> 'a list -> info -> 'a list t
val pos : ?rev:bool -> int -> 'a converter -> 'a -> info -> 'a t
val pos_all : 'a converter -> 'a list -> info -> 'a list t
val pos_left : ?rev:bool -> int -> 'a converter -> 'a list -> info -> 'a list t
val pos_right : ?rev:bool -> int -> 'a converter -> 'a list -> info -> 'a list t
(** {1 As terms} *)
val value : 'a t -> 'a Cmdliner_term.t
val required : 'a option t -> 'a Cmdliner_term.t
val non_empty : 'a list t -> 'a list Cmdliner_term.t
val last : 'a list t -> 'a Cmdliner_term.t
(** {1 Predefined arguments} *)
val man_format : Cmdliner_manpage.format Cmdliner_term.t
val stdopt_version : docs:string -> bool Cmdliner_term.t
val stdopt_help : docs:string -> Cmdliner_manpage.format option Cmdliner_term.t
(** {1 Converters} *)
val bool : bool converter
val char : char converter
val int : int converter
val nativeint : nativeint converter
val int32 : int32 converter
val int64 : int64 converter
val float : float converter
val string : string converter
val enum : (string * 'a) list -> 'a converter
val file : string converter
val dir : string converter
val non_dir_file : string converter
val list : ?sep:char -> 'a converter -> 'a list converter
val array : ?sep:char -> 'a converter -> 'a array converter
val pair : ?sep:char -> 'a converter -> 'b converter -> ('a * 'b) converter
val t2 : ?sep:char -> 'a converter -> 'b converter -> ('a * 'b) converter
val t3 :
?sep:char -> 'a converter ->'b converter -> 'c converter ->
('a * 'b * 'c) converter
val t4 :
?sep:char -> 'a converter ->'b converter -> 'c converter -> 'd converter ->
('a * 'b * 'c * 'd) converter
val doc_quote : string -> string
val doc_alts : ?quoted:bool -> string list -> string
val doc_alts_enum : ?quoted:bool -> (string * 'a) list -> string
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,357 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
let strf = Printf.sprintf
(* Unique ids *)
let uid =
(* Thread-safe UIDs, Oo.id (object end) was used before.
Note this won't be thread-safe in multicore, we should use
Atomic but this is >= 4.12 and we have 4.08 for now. *)
let c = ref 0 in
fun () ->
let id = !c in
incr c; if id > !c then assert false (* too many ids *) else id
(* Edit distance *)
let edit_distance s0 s1 =
let minimum (a : int) (b : int) (c : int) : int = min a (min b c) in
let s0,s1 = if String.length s0 <= String.length s1 then s0,s1 else s1,s0 in
let m = String.length s0 and n = String.length s1 in
let rec rows row0 row i = match i > n with
| true -> row0.(m)
| false ->
row.(0) <- i;
for j = 1 to m do
if s0.[j - 1] = s1.[i - 1] then row.(j) <- row0.(j - 1) else
row.(j) <- minimum (row0.(j - 1) + 1) (row0.(j) + 1) (row.(j - 1) + 1)
done;
rows row row0 (i + 1)
in
rows (Array.init (m + 1) (fun x -> x)) (Array.make (m + 1) 0) 1
let suggest s candidates =
let add (min, acc) name =
let d = edit_distance s name in
if d = min then min, (name :: acc) else
if d < min then d, [name] else
min, acc
in
let dist, suggs = List.fold_left add (max_int, []) candidates in
if dist < 3 (* suggest only if not too far *) then suggs else []
(* Invalid argument strings *)
let err_empty_list = "empty list"
let err_incomplete_enum ss =
strf "Arg.enum: missing printable string for a value, other strings are: %s"
(String.concat ", " ss)
(* Formatting tools *)
let pp = Format.fprintf
let pp_sp = Format.pp_print_space
let pp_str = Format.pp_print_string
let pp_char = Format.pp_print_char
let pp_text = Format.pp_print_text
let pp_lines ppf s =
let rec stop_at sat ~start ~max s =
if start > max then start else
if sat s.[start] then start else
stop_at sat ~start:(start + 1) ~max s
in
let sub s start stop ~max =
if start = stop then "" else
if start = 0 && stop > max then s else
String.sub s start (stop - start)
in
let is_nl c = c = '\n' in
let max = String.length s - 1 in
let rec loop start s = match stop_at is_nl ~start ~max s with
| stop when stop > max -> Format.pp_print_string ppf (sub s start stop ~max)
| stop ->
Format.pp_print_string ppf (sub s start stop ~max);
Format.pp_force_newline ppf ();
loop (stop + 1) s
in
loop 0 s
let pp_tokens ~spaces ppf s = (* collapse white and hint spaces (maybe) *)
let is_space = function ' ' | '\n' | '\r' | '\t' -> true | _ -> false in
let i_max = String.length s - 1 in
let flush start stop = pp_str ppf (String.sub s start (stop - start + 1)) in
let rec skip_white i =
if i > i_max then i else
if is_space s.[i] then skip_white (i + 1) else i
in
let rec loop start i =
if i > i_max then flush start i_max else
if not (is_space s.[i]) then loop start (i + 1) else
let next_start = skip_white i in
(flush start (i - 1); if spaces then pp_sp ppf () else pp_char ppf ' ';
if next_start > i_max then () else loop next_start next_start)
in
loop 0 0
(* Converter (end-user) error messages *)
let quote s = strf "'%s'" s
let alts_str ?quoted alts =
let quote = match quoted with
| None -> strf "$(b,%s)"
| Some quoted -> if quoted then quote else (fun s -> s)
in
match alts with
| [] -> invalid_arg err_empty_list
| [a] -> (quote a)
| [a; b] -> strf "either %s or %s" (quote a) (quote b)
| alts ->
let rev_alts = List.rev alts in
strf "one of %s or %s"
(String.concat ", " (List.rev_map quote (List.tl rev_alts)))
(quote (List.hd rev_alts))
let err_multi_def ~kind name doc v v' =
strf "%s %s defined twice (doc strings are '%s' and '%s')"
kind name (doc v) (doc v')
let err_ambiguous ~kind s ~ambs =
strf "%s %s ambiguous and could be %s" kind (quote s)
(alts_str ~quoted:true ambs)
let err_unknown ?(dom = []) ?(hints = []) ~kind v =
let hints = match hints, dom with
| [], [] -> "."
| [], dom -> strf ", must be %s." (alts_str ~quoted:true dom)
| hints, _ -> strf ", did you mean %s?" (alts_str ~quoted:true hints)
in
strf "unknown %s %s%s" kind (quote v) hints
let err_no kind s = strf "no %s %s" (quote s) kind
let err_not_dir s = strf "%s is not a directory" (quote s)
let err_is_dir s = strf "%s is a directory" (quote s)
let err_element kind s exp =
strf "invalid element in %s ('%s'): %s" kind s exp
let err_invalid kind s exp = strf "invalid %s %s, %s" kind (quote s) exp
let err_invalid_val = err_invalid "value"
let err_sep_miss sep s =
err_invalid_val s (strf "missing a '%c' separator" sep)
(* Converters *)
type 'a parser = string -> [ `Ok of 'a | `Error of string ]
type 'a printer = Format.formatter -> 'a -> unit
type 'a conv = 'a parser * 'a printer
let some ?(none = "") (parse, print) =
let parse s = match parse s with `Ok v -> `Ok (Some v) | `Error _ as e -> e in
let print ppf v = match v with
| None -> Format.pp_print_string ppf none
| Some v -> print ppf v
in
parse, print
let some' ?none (parse, print) =
let parse s = match parse s with `Ok v -> `Ok (Some v) | `Error _ as e -> e in
let print ppf = function
| None -> (match none with None -> () | Some v -> print ppf v)
| Some v -> print ppf v
in
parse, print
let bool =
let parse s = try `Ok (bool_of_string s) with
| Invalid_argument _ ->
`Error (err_invalid_val s (alts_str ~quoted:true ["true"; "false"]))
in
parse, Format.pp_print_bool
let char =
let parse s = match String.length s = 1 with
| true -> `Ok s.[0]
| false -> `Error (err_invalid_val s "expected a character")
in
parse, pp_char
let parse_with t_of_str exp s =
try `Ok (t_of_str s) with Failure _ -> `Error (err_invalid_val s exp)
let int =
parse_with int_of_string "expected an integer", Format.pp_print_int
let int32 =
parse_with Int32.of_string "expected a 32-bit integer",
(fun ppf -> pp ppf "%ld")
let int64 =
parse_with Int64.of_string "expected a 64-bit integer",
(fun ppf -> pp ppf "%Ld")
let nativeint =
parse_with Nativeint.of_string "expected a processor-native integer",
(fun ppf -> pp ppf "%nd")
let float =
parse_with float_of_string "expected a floating point number",
Format.pp_print_float
let string = (fun s -> `Ok s), pp_str
let enum sl =
if sl = [] then invalid_arg err_empty_list else
let t = Cmdliner_trie.of_list sl in
let parse s = match Cmdliner_trie.find t s with
| `Ok _ as r -> r
| `Ambiguous ->
let ambs = List.sort compare (Cmdliner_trie.ambiguities t s) in
`Error (err_ambiguous ~kind:"enum value" s ~ambs)
| `Not_found ->
let alts = List.rev (List.rev_map (fun (s, _) -> s) sl) in
`Error (err_invalid_val s ("expected " ^ (alts_str ~quoted:true alts)))
in
let print ppf v =
let sl_inv = List.rev_map (fun (s,v) -> (v,s)) sl in
try pp_str ppf (List.assoc v sl_inv)
with Not_found -> invalid_arg (err_incomplete_enum (List.map fst sl))
in
parse, print
let file =
let parse s = match Sys.file_exists s with
| true -> `Ok s
| false -> `Error (err_no "file or directory" s)
in
parse, pp_str
let dir =
let parse s = match Sys.file_exists s with
| true -> if Sys.is_directory s then `Ok s else `Error (err_not_dir s)
| false -> `Error (err_no "directory" s)
in
parse, pp_str
let non_dir_file =
let parse s = match Sys.file_exists s with
| true -> if not (Sys.is_directory s) then `Ok s else `Error (err_is_dir s)
| false -> `Error (err_no "file" s)
in
parse, pp_str
let split_and_parse sep parse s = (* raises [Failure] *)
let parse sub = match parse sub with
| `Error e -> failwith e | `Ok v -> v
in
let rec split accum j =
let i = try String.rindex_from s j sep with Not_found -> -1 in
if (i = -1) then
let p = String.sub s 0 (j + 1) in
if p <> "" then parse p :: accum else accum
else
let p = String.sub s (i + 1) (j - i) in
let accum' = if p <> "" then parse p :: accum else accum in
split accum' (i - 1)
in
split [] (String.length s - 1)
let list ?(sep = ',') (parse, pp_e) =
let parse s = try `Ok (split_and_parse sep parse s) with
| Failure e -> `Error (err_element "list" s e)
in
let rec print ppf = function
| v :: l -> pp_e ppf v; if (l <> []) then (pp_char ppf sep; print ppf l)
| [] -> ()
in
parse, print
let array ?(sep = ',') (parse, pp_e) =
let parse s = try `Ok (Array.of_list (split_and_parse sep parse s)) with
| Failure e -> `Error (err_element "array" s e)
in
let print ppf v =
let max = Array.length v - 1 in
for i = 0 to max do pp_e ppf v.(i); if i <> max then pp_char ppf sep done
in
parse, print
let split_left sep s =
try
let i = String.index s sep in
let len = String.length s in
Some ((String.sub s 0 i), (String.sub s (i + 1) (len - i - 1)))
with Not_found -> None
let pair ?(sep = ',') (pa0, pr0) (pa1, pr1) =
let parser s = match split_left sep s with
| None -> `Error (err_sep_miss sep s)
| Some (v0, v1) ->
match pa0 v0, pa1 v1 with
| `Ok v0, `Ok v1 -> `Ok (v0, v1)
| `Error e, _ | _, `Error e -> `Error (err_element "pair" s e)
in
let printer ppf (v0, v1) = pp ppf "%a%c%a" pr0 v0 sep pr1 v1 in
parser, printer
let t2 = pair
let t3 ?(sep = ',') (pa0, pr0) (pa1, pr1) (pa2, pr2) =
let parse s = match split_left sep s with
| None -> `Error (err_sep_miss sep s)
| Some (v0, s) ->
match split_left sep s with
| None -> `Error (err_sep_miss sep s)
| Some (v1, v2) ->
match pa0 v0, pa1 v1, pa2 v2 with
| `Ok v0, `Ok v1, `Ok v2 -> `Ok (v0, v1, v2)
| `Error e, _, _ | _, `Error e, _ | _, _, `Error e ->
`Error (err_element "triple" s e)
in
let print ppf (v0, v1, v2) =
pp ppf "%a%c%a%c%a" pr0 v0 sep pr1 v1 sep pr2 v2
in
parse, print
let t4 ?(sep = ',') (pa0, pr0) (pa1, pr1) (pa2, pr2) (pa3, pr3) =
let parse s = match split_left sep s with
| None -> `Error (err_sep_miss sep s)
| Some(v0, s) ->
match split_left sep s with
| None -> `Error (err_sep_miss sep s)
| Some (v1, s) ->
match split_left sep s with
| None -> `Error (err_sep_miss sep s)
| Some (v2, v3) ->
match pa0 v0, pa1 v1, pa2 v2, pa3 v3 with
| `Ok v1, `Ok v2, `Ok v3, `Ok v4 -> `Ok (v1, v2, v3, v4)
| `Error e, _, _, _ | _, `Error e, _, _ | _, _, `Error e, _
| _, _, _, `Error e -> `Error (err_element "quadruple" s e)
in
let print ppf (v0, v1, v2, v3) =
pp ppf "%a%c%a%c%a%c%a" pr0 v0 sep pr1 v1 sep pr2 v2 sep pr3 v3
in
parse, print
let env_bool_parse s = match String.lowercase_ascii s with
| "" | "false" | "no" | "n" | "0" -> `Ok false
| "true" | "yes" | "y" | "1" -> `Ok true
| s ->
let alts = alts_str ~quoted:true ["true"; "yes"; "false"; "no" ] in
`Error (err_invalid_val s alts)
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,76 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** A few helpful base definitions. *)
val uid : unit -> int
(** [uid ()] is new unique for the program run. *)
val suggest : string -> string list -> string list
(** [suggest near candidates] suggest values from [candidates]
not too far from [near]. *)
(** {1:fmt Formatting helpers} *)
val pp_text : Format.formatter -> string -> unit
val pp_lines : Format.formatter -> string -> unit
val pp_tokens : spaces:bool -> Format.formatter -> string -> unit
(** {1:err Error message helpers} *)
val quote : string -> string
val alts_str : ?quoted:bool -> string list -> string
val err_ambiguous : kind:string -> string -> ambs:string list -> string
val err_unknown :
?dom:string list -> ?hints:string list -> kind:string -> string -> string
val err_multi_def :
kind:string -> string -> ('b -> string) -> 'b -> 'b -> string
(** {1:conv Textual OCaml value converters} *)
type 'a parser = string -> [ `Ok of 'a | `Error of string ]
type 'a printer = Format.formatter -> 'a -> unit
type 'a conv = 'a parser * 'a printer
val some : ?none:string -> 'a conv -> 'a option conv
val some' : ?none:'a -> 'a conv -> 'a option conv
val bool : bool conv
val char : char conv
val int : int conv
val nativeint : nativeint conv
val int32 : int32 conv
val int64 : int64 conv
val float : float conv
val string : string conv
val enum : (string * 'a) list -> 'a conv
val file : string conv
val dir : string conv
val non_dir_file : string conv
val list : ?sep:char -> 'a conv -> 'a list conv
val array : ?sep:char -> 'a conv -> 'a array conv
val pair : ?sep:char -> 'a conv -> 'b conv -> ('a * 'b) conv
val t2 : ?sep:char -> 'a conv -> 'b conv -> ('a * 'b) conv
val t3 : ?sep:char -> 'a conv ->'b conv -> 'c conv -> ('a * 'b * 'c) conv
val t4 :
?sep:char -> 'a conv -> 'b conv -> 'c conv -> 'd conv ->
('a * 'b * 'c * 'd) conv
val env_bool_parse : bool parser
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,224 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(* A command line stores pre-parsed information about the command
line's arguments in a more structured way. Given the
Cmdliner_info.arg values mentioned in a term and Sys.argv
(without exec name) we parse the command line into a map of
Cmdliner_info.arg values to [arg] values (see below). This map is used by
the term's closures to retrieve and convert command line arguments
(see the Cmdliner_arg module). *)
let err_multi_opt_name_def name a a' =
Cmdliner_base.err_multi_def
~kind:"option name" name Cmdliner_info.Arg.doc a a'
module Amap = Map.Make (Cmdliner_info.Arg)
type arg = (* unconverted argument data as found on the command line. *)
| O of (int * string * (string option)) list (* (pos, name, value) of opt. *)
| P of string list
type t = arg Amap.t (* command line, maps arg_infos to arg value. *)
let get_arg cl a = try Amap.find a cl with Not_found -> assert false
let opt_arg cl a = match get_arg cl a with O l -> l | _ -> assert false
let pos_arg cl a = match get_arg cl a with P l -> l | _ -> assert false
let actual_args cl a = match get_arg cl a with
| P args -> args
| O l ->
let extract_args (_pos, name, value) =
name :: (match value with None -> [] | Some v -> [v])
in
List.concat (List.map extract_args l)
let arg_info_indexes args =
(* from [args] returns a trie mapping the names of optional arguments to
their arg_info, a list with all arg_info for positional arguments and
a cmdline mapping each arg_info to an empty [arg]. *)
let rec loop optidx posidx cl = function
| [] -> optidx, posidx, cl
| a :: l ->
match Cmdliner_info.Arg.is_pos a with
| true -> loop optidx (a :: posidx) (Amap.add a (P []) cl) l
| false ->
let add t name = match Cmdliner_trie.add t name a with
| `New t -> t
| `Replaced (a', _) -> invalid_arg (err_multi_opt_name_def name a a')
in
let names = Cmdliner_info.Arg.opt_names a in
let optidx = List.fold_left add optidx names in
loop optidx posidx (Amap.add a (O []) cl) l
in
loop Cmdliner_trie.empty [] Amap.empty (Cmdliner_info.Arg.Set.elements args)
(* Optional argument parsing *)
let is_opt s = String.length s > 1 && s.[0] = '-'
let is_short_opt s = String.length s = 2 && s.[0] = '-'
let parse_opt_arg s = (* (name, value) of opt arg, assert len > 1. *)
let l = String.length s in
if s.[1] <> '-' then (* short opt *)
if l = 2 then s, None else
String.sub s 0 2, Some (String.sub s 2 (l - 2)) (* with glued opt arg *)
else try (* long opt *)
let i = String.index s '=' in
String.sub s 0 i, Some (String.sub s (i + 1) (l - i - 1))
with Not_found -> s, None
let hint_matching_opt optidx s =
(* hint options that could match [s] in [optidx]. FIXME explain this is
a bit obscure. *)
if String.length s <= 2 then [] else
let short_opt, long_opt =
if s.[1] <> '-'
then s, Printf.sprintf "-%s" s
else String.sub s 1 (String.length s - 1), s
in
let short_opt, _ = parse_opt_arg short_opt in
let long_opt, _ = parse_opt_arg long_opt in
let all = Cmdliner_trie.ambiguities optidx "-" in
match List.mem short_opt all, Cmdliner_base.suggest long_opt all with
| false, [] -> []
| false, l -> l
| true, [] -> [short_opt]
| true, l -> if List.mem short_opt l then l else short_opt :: l
let parse_opt_args ~peek_opts optidx cl args =
(* returns an updated [cl] cmdline according to the options found in [args]
with the trie index [optidx]. Positional arguments are returned in order
in a list. *)
let rec loop errs k cl pargs = function
| [] -> List.rev errs, cl, List.rev pargs
| "--" :: args -> List.rev errs, cl, (List.rev_append pargs args)
| s :: args ->
if not (is_opt s) then loop errs (k + 1) cl (s :: pargs) args else
let name, value = parse_opt_arg s in
match Cmdliner_trie.find optidx name with
| `Ok a ->
let value, args = match value, Cmdliner_info.Arg.opt_kind a with
| Some v, Cmdliner_info.Arg.Flag when is_short_opt name ->
None, ("-" ^ v) :: args
| Some _, _ -> value, args
| None, Cmdliner_info.Arg.Flag -> value, args
| None, _ ->
match args with
| [] -> None, args
| v :: rest -> if is_opt v then None, args else Some v, rest
in
let arg = O ((k, name, value) :: opt_arg cl a) in
let errs,args =
match Cmdliner_info.Arg.alias a name value with
| Ok l -> errs,l@args
| Error err -> err::errs,args
in
loop errs (k + 1) (Amap.add a arg cl) pargs args
| `Not_found when peek_opts -> loop errs (k + 1) cl pargs args
| `Not_found ->
let hints = hint_matching_opt optidx s in
let err = Cmdliner_base.err_unknown ~kind:"option" ~hints name in
loop (err :: errs) (k + 1) cl pargs args
| `Ambiguous ->
let ambs = Cmdliner_trie.ambiguities optidx name in
let ambs = List.sort compare ambs in
let err = Cmdliner_base.err_ambiguous ~kind:"option" name ~ambs in
loop (err :: errs) (k + 1) cl pargs args
in
let errs, cl, pargs = loop [] 0 cl [] args in
if errs = [] then Ok (cl, pargs) else
let err = String.concat "\n" errs in
Error (err, cl, pargs)
let take_range start stop l =
let rec loop i acc = function
| [] -> List.rev acc
| v :: vs ->
if i < start then loop (i + 1) acc vs else
if i <= stop then loop (i + 1) (v :: acc) vs else
List.rev acc
in
loop 0 [] l
let process_pos_args posidx cl pargs =
(* returns an updated [cl] cmdline in which each positional arg mentioned
in the list index posidx, is given a value according the list
of positional arguments values [pargs]. *)
if pargs = [] then
let misses = List.filter Cmdliner_info.Arg.is_req posidx in
if misses = [] then Ok cl else
Error (Cmdliner_msg.err_pos_misses misses, cl)
else
let last = List.length pargs - 1 in
let pos rev k = if rev then last - k else k in
let rec loop misses cl max_spec = function
| [] -> misses, cl, max_spec
| a :: al ->
let apos = Cmdliner_info.Arg.pos_kind a in
let rev = Cmdliner_info.Arg.pos_rev apos in
let start = pos rev (Cmdliner_info.Arg.pos_start apos) in
let stop = match Cmdliner_info.Arg.pos_len apos with
| None -> pos rev last
| Some n -> pos rev (Cmdliner_info.Arg.pos_start apos + n - 1)
in
let start, stop = if rev then stop, start else start, stop in
let args = take_range start stop pargs in
let max_spec = max stop max_spec in
let cl = Amap.add a (P args) cl in
let misses = match Cmdliner_info.Arg.is_req a && args = [] with
| true -> a :: misses
| false -> misses
in
loop misses cl max_spec al
in
let misses, cl, max_spec = loop [] cl (-1) posidx in
if misses <> [] then Error (Cmdliner_msg.err_pos_misses misses, cl) else
if last <= max_spec then Ok cl else
let excess = take_range (max_spec + 1) last pargs in
Error (Cmdliner_msg.err_pos_excess excess, cl)
let create ?(peek_opts = false) al args =
let optidx, posidx, cl = arg_info_indexes al in
match parse_opt_args ~peek_opts optidx cl args with
| Ok (cl, _) when peek_opts -> Ok cl
| Ok (cl, pargs) -> process_pos_args posidx cl pargs
| Error (errs, cl, _) -> Error (errs, cl)
let deprecated_msgs cl =
let add i arg acc = match Cmdliner_info.Arg.deprecated i with
| None -> acc
| Some msg ->
let plural l = if List.length l > 1 then "s " else " " in
match arg with
| O [] | P [] -> acc (* Should not happen *)
| O os ->
let plural = plural os in
let names = List.map (fun (_, n, _) -> n) os in
let names = String.concat " " (List.map Cmdliner_base.quote names) in
let msg = "option" :: plural :: names :: ": " :: msg :: [] in
String.concat "" msg :: acc
| P args ->
let plural = plural args in
let args = String.concat " " (List.map Cmdliner_base.quote args) in
let msg = "argument" :: plural :: args :: ": " :: msg :: [] in
String.concat "" msg :: acc
in
Amap.fold add cl []
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,36 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Command lines. *)
type t
val create :
?peek_opts:bool -> Cmdliner_info.Arg.Set.t -> string list ->
(t, string * t) result
val opt_arg : t -> Cmdliner_info.Arg.t -> (int * string * (string option)) list
val pos_arg : t -> Cmdliner_info.Arg.t -> string list
val actual_args : t -> Cmdliner_info.Arg.t -> string list
(** Actual command line arguments from the command line *)
val is_opt : string -> bool
val deprecated_msgs : t -> string list
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,46 @@
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(* Commands *)
(* Command info *)
type info = Cmdliner_info.Cmd.t
let info = Cmdliner_info.Cmd.v
type 'a t =
| Cmd of info * 'a Cmdliner_term.parser
| Group of info * ('a Cmdliner_term.parser option * 'a t list)
let get_info = function Cmd (i, _) | Group (i, _) -> i
let children_infos = function
| Cmd _ -> [] | Group (_, (_, cs)) -> List.map get_info cs
let v i (args, p) = Cmd (Cmdliner_info.Cmd.add_args i args, p)
let group ?default i cmds =
let args, parser = match default with
| None -> None, None | Some (args, p) -> Some args, Some p
in
let children = List.map get_info cmds in
let i = Cmdliner_info.Cmd.with_children i ~args ~children in
Group (i, (parser, cmds))
let name c = Cmdliner_info.Cmd.name (get_info c)
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,40 @@
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Commands and their information. *)
type info = Cmdliner_info.Cmd.t
val info :
?deprecated:string ->
?man_xrefs:Cmdliner_manpage.xref list -> ?man:Cmdliner_manpage.block list ->
?envs:Cmdliner_info.Env.info list -> ?exits:Cmdliner_info.Exit.info list ->
?sdocs:string -> ?docs:string -> ?doc:string -> ?version:string ->
string -> info
type 'a t =
| Cmd of info * 'a Cmdliner_term.parser
| Group of info * ('a Cmdliner_term.parser option * 'a t list)
val v : info -> 'a Cmdliner_term.t -> 'a t
val group : ?default:'a Cmdliner_term.t -> info -> 'a t list -> 'a t
val name : 'a t -> string
val get_info : 'a t -> info
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,411 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
let rev_compare n0 n1 = compare n1 n0
let strf = Printf.sprintf
let order_args a0 a1 =
match Cmdliner_info.Arg.is_opt a0, Cmdliner_info.Arg.is_opt a1 with
| true, true -> (* optional by name *)
let key names =
let k = List.hd (List.sort rev_compare names) in
let k = String.lowercase_ascii k in
if k.[1] = '-' then String.sub k 1 (String.length k - 1) else k
in
compare
(key @@ Cmdliner_info.Arg.opt_names a0)
(key @@ Cmdliner_info.Arg.opt_names a1)
| false, false -> (* positional by variable *)
compare
(String.lowercase_ascii @@ Cmdliner_info.Arg.docv a0)
(String.lowercase_ascii @@ Cmdliner_info.Arg.docv a1)
| true, false -> -1 (* positional first *)
| false, true -> 1 (* optional after *)
let esc = Cmdliner_manpage.escape
let cmd_name t = esc @@ Cmdliner_info.Cmd.name t
let sorted_items_to_blocks ~boilerplate:b items =
(* Items are sorted by section and then rev. sorted by appearance.
We gather them by section in correct order in a `Block and prefix
them with optional boilerplate *)
let boilerplate = match b with None -> (fun _ -> None) | Some b -> b in
let mk_block sec acc = match boilerplate sec with
| None -> (sec, `Blocks acc)
| Some b -> (sec, `Blocks (b :: acc))
in
let rec loop secs sec acc = function
| (sec', it) :: its when sec' = sec -> loop secs sec (it :: acc) its
| (sec', it) :: its -> loop (mk_block sec acc :: secs) sec' [it] its
| [] -> (mk_block sec acc) :: secs
in
match items with
| [] -> []
| (sec, it) :: its -> loop [] sec [it] its
(* Doc string variables substitutions. *)
let env_info_subst ~subst e = function
| "env" -> Some (strf "$(b,%s)" @@ esc (Cmdliner_info.Env.info_var e))
| id -> subst id
let exit_info_subst ~subst e = function
| "status" -> Some (strf "%d" (fst @@ Cmdliner_info.Exit.info_codes e))
| "status_max" -> Some (strf "%d" (snd @@ Cmdliner_info.Exit.info_codes e))
| id -> subst id
let arg_info_subst ~subst a = function
| "docv" ->
Some (strf "$(i,%s)" @@ esc (Cmdliner_info.Arg.docv a))
| "opt" when Cmdliner_info.Arg.is_opt a ->
Some (strf "$(b,%s)" @@ esc (Cmdliner_info.Arg.opt_name_sample a))
| "env" as id ->
begin match Cmdliner_info.Arg.env a with
| Some e -> env_info_subst ~subst e id
| None -> subst id
end
| id -> subst id
let cmd_info_subst ei = function
| "tname" -> Some (strf "$(b,%s)" @@ cmd_name (Cmdliner_info.Eval.cmd ei))
| "mname" -> Some (strf "$(b,%s)" @@ cmd_name (Cmdliner_info.Eval.main ei))
| "iname" ->
let cmd = Cmdliner_info.Eval.cmd ei :: Cmdliner_info.Eval.parents ei in
let cmd = String.concat " " (List.rev_map Cmdliner_info.Cmd.name cmd) in
Some (strf "$(b,%s)" cmd)
| _ -> None
(* Command docs *)
let invocation ?(sep = " ") ?(parents = []) cmd =
let names = List.rev_map Cmdliner_info.Cmd.name (cmd :: parents) in
esc @@ String.concat sep names
let synopsis_pos_arg a =
let v = match Cmdliner_info.Arg.docv a with "" -> "ARG" | v -> v in
let v = strf "$(i,%s)" (esc v) in
let v = (if Cmdliner_info.Arg.is_req a then strf "%s" else strf "[%s]") v in
match Cmdliner_info.Arg.(pos_len @@ pos_kind a) with
| None -> v ^ ""
| Some 1 -> v
| Some n ->
let rec loop n acc = if n <= 0 then acc else loop (n - 1) (v :: acc) in
String.concat " " (loop n [])
let synopsis_opt_arg a n =
let var = match Cmdliner_info.Arg.docv a with "" -> "VAL" | v -> v in
match Cmdliner_info.Arg.opt_kind a with
| Cmdliner_info.Arg.Flag -> strf "$(b,%s)" (esc n)
| Cmdliner_info.Arg.Opt ->
if String.length n > 2
then strf "$(b,%s)=$(i,%s)" (esc n) (esc var)
else strf "$(b,%s) $(i,%s)" (esc n) (esc var)
| Cmdliner_info.Arg.Opt_vopt _ ->
if String.length n > 2
then strf "$(b,%s)[=$(i,%s)]" (esc n) (esc var)
else strf "$(b,%s) [$(i,%s)]" (esc n) (esc var)
let deprecated cmd = match Cmdliner_info.Cmd.deprecated cmd with
| None -> "" | Some _ -> "(Deprecated) "
let synopsis ?parents cmd = match Cmdliner_info.Cmd.children cmd with
| [] ->
let rev_cli_order (a0, _) (a1, _) =
Cmdliner_info.Arg.rev_pos_cli_order a0 a1
in
let args = Cmdliner_info.Cmd.args cmd in
let oargs, pargs = Cmdliner_info.Arg.(Set.partition is_opt args) in
let oargs =
(* Keep only those that are listed in the s_options section and
that are not [--version] or [--help]. * *)
let keep a =
let drop_names n = n = "--help" || n = "--version" in
Cmdliner_info.Arg.docs a = Cmdliner_manpage.s_options &&
not (List.exists drop_names (Cmdliner_info.Arg.opt_names a))
in
let oargs = Cmdliner_info.Arg.Set.(elements (filter keep oargs)) in
let count = List.length oargs in
let any_option = "[$(i,OPTION)]…" in
if count = 0 || count > 3 then any_option else
let syn a =
strf "[%s]" (synopsis_opt_arg a (Cmdliner_info.Arg.opt_name_sample a))
in
let oargs = List.sort order_args oargs in
let oargs = String.concat " " (List.map syn oargs) in
String.concat " " [oargs; any_option]
in
let pargs =
let pargs = Cmdliner_info.Arg.Set.elements pargs in
if pargs = [] then "" else
let pargs = List.map (fun a -> a, synopsis_pos_arg a) pargs in
let pargs = List.sort rev_cli_order pargs in
String.concat " " ("" (* add a space *) :: List.rev_map snd pargs)
in
strf "%s$(b,%s) %s%s"
(deprecated cmd) (invocation ?parents cmd) oargs pargs
| _cmds ->
let subcmd = match Cmdliner_info.Cmd.has_args cmd with
| false -> "$(i,COMMAND)" | true -> "[$(i,COMMAND)]"
in
strf "%s$(b,%s) %s …" (deprecated cmd) (invocation ?parents cmd) subcmd
let cmd_docs ei = match Cmdliner_info.(Cmd.children (Eval.cmd ei)) with
| [] -> []
| cmds ->
let add_cmd acc cmd =
let syn = synopsis cmd in
(Cmdliner_info.Cmd.docs cmd, `I (syn, Cmdliner_info.Cmd.doc cmd)) :: acc
in
let by_sec_by_rev_name (s0, `I (c0, _)) (s1, `I (c1, _)) =
let c = compare s0 s1 in
if c <> 0 then c else compare c1 c0 (* N.B. reverse *)
in
let cmds = List.fold_left add_cmd [] cmds in
let cmds = List.sort by_sec_by_rev_name cmds in
let cmds = (cmds :> (string * Cmdliner_manpage.block) list) in
sorted_items_to_blocks ~boilerplate:None cmds
(* Argument docs *)
let arg_man_item_label a =
let s = match Cmdliner_info.Arg.is_pos a with
| true -> strf "$(i,%s)" (esc @@ Cmdliner_info.Arg.docv a)
| false ->
let names = List.sort compare (Cmdliner_info.Arg.opt_names a) in
String.concat ", " (List.rev_map (synopsis_opt_arg a) names)
in
match Cmdliner_info.Arg.deprecated a with
| None -> s | Some _ -> "(Deprecated) " ^ s
let arg_to_man_item ~errs ~subst ~buf a =
let subst = arg_info_subst ~subst a in
let or_env ~value a = match Cmdliner_info.Arg.env a with
| None -> ""
| Some e ->
let value = if value then " or" else "absent " in
strf "%s $(b,%s) env" value (esc @@ Cmdliner_info.Env.info_var e)
in
let absent = match Cmdliner_info.Arg.absent a with
| Cmdliner_info.Arg.Err -> "required"
| Cmdliner_info.Arg.Doc "" -> strf "%s" (or_env ~value:false a)
| Cmdliner_info.Arg.Doc s ->
let s = Cmdliner_manpage.subst_vars ~errs ~subst buf s in
strf "absent=%s%s" s (or_env ~value:true a)
| Cmdliner_info.Arg.Val v ->
match Lazy.force v with
| "" -> strf "%s" (or_env ~value:false a)
| v -> strf "absent=$(b,%s)%s" (esc v) (or_env ~value:true a)
in
let optvopt = match Cmdliner_info.Arg.opt_kind a with
| Cmdliner_info.Arg.Opt_vopt v -> strf "default=$(b,%s)" (esc v)
| _ -> ""
in
let argvdoc = match optvopt, absent with
| "", "" -> ""
| s, "" | "", s -> strf " (%s)" s
| s, s' -> strf " (%s) (%s)" s s'
in
let doc = Cmdliner_info.Arg.doc a in
let doc = Cmdliner_manpage.subst_vars ~errs ~subst buf doc in
(Cmdliner_info.Arg.docs a, `I (arg_man_item_label a ^ argvdoc, doc))
let arg_docs ~errs ~subst ~buf ei =
let by_sec_by_arg a0 a1 =
let c = compare (Cmdliner_info.Arg.docs a0) (Cmdliner_info.Arg.docs a1) in
if c <> 0 then c else
let c =
match Cmdliner_info.Arg.deprecated a0, Cmdliner_info.Arg.deprecated a1
with
| None, None | Some _, Some _ -> 0
| None, Some _ -> -1 | Some _, None -> 1
in
if c <> 0 then c else order_args a0 a1
in
let keep_arg a acc =
if not Cmdliner_info.Arg.(is_pos a && (docv a = "" || doc a = ""))
then (a :: acc) else acc
in
let args = Cmdliner_info.Cmd.args @@ Cmdliner_info.Eval.cmd ei in
let args = Cmdliner_info.Arg.Set.fold keep_arg args [] in
let args = List.sort by_sec_by_arg args in
let args = List.rev_map (arg_to_man_item ~errs ~subst ~buf) args in
sorted_items_to_blocks ~boilerplate:None args
(* Exit statuses doc *)
let exit_boilerplate sec = match sec = Cmdliner_manpage.s_exit_status with
| false -> None
| true -> Some (Cmdliner_manpage.s_exit_status_intro)
let exit_docs ~errs ~subst ~buf ~has_sexit ei =
let by_sec (s0, _) (s1, _) = compare s0 s1 in
let add_exit_item acc e =
let subst = exit_info_subst ~subst e in
let min, max = Cmdliner_info.Exit.info_codes e in
let doc = Cmdliner_info.Exit.info_doc e in
let label = if min = max then strf "%d" min else strf "%d-%d" min max in
let item = `I (label, Cmdliner_manpage.subst_vars ~errs ~subst buf doc) in
(Cmdliner_info.Exit.info_docs e, item) :: acc
in
let exits = Cmdliner_info.Cmd.exits @@ Cmdliner_info.Eval.cmd ei in
let exits = List.sort Cmdliner_info.Exit.info_order exits in
let exits = List.fold_left add_exit_item [] exits in
let exits = List.stable_sort by_sec (* sort by section *) exits in
let boilerplate = if has_sexit then None else Some exit_boilerplate in
sorted_items_to_blocks ~boilerplate exits
(* Environment doc *)
let env_boilerplate sec = match sec = Cmdliner_manpage.s_environment with
| false -> None
| true -> Some (Cmdliner_manpage.s_environment_intro)
let env_docs ~errs ~subst ~buf ~has_senv ei =
let add_env_item ~subst (seen, envs as acc) e =
if Cmdliner_info.Env.Set.mem e seen then acc else
let seen = Cmdliner_info.Env.Set.add e seen in
let var = strf "$(b,%s)" @@ esc (Cmdliner_info.Env.info_var e) in
let var = match Cmdliner_info.Env.info_deprecated e with
| None -> var | Some _ -> "(Deprecated) " ^ var in
let doc = Cmdliner_info.Env.info_doc e in
let doc = Cmdliner_manpage.subst_vars ~errs ~subst buf doc in
let envs = (Cmdliner_info.Env.info_docs e, `I (var, doc)) :: envs in
seen, envs
in
let add_arg_env a acc = match Cmdliner_info.Arg.env a with
| None -> acc
| Some e -> add_env_item ~subst:(arg_info_subst ~subst a) acc e
in
let add_env acc e = add_env_item ~subst:(env_info_subst ~subst e) acc e in
let by_sec_by_rev_name (s0, `I (v0, _)) (s1, `I (v1, _)) =
let c = compare s0 s1 in
if c <> 0 then c else compare v1 v0 (* N.B. reverse *)
in
(* Arg envs before term envs is important here: if the same is mentioned
both in an arg and in a term the substs of the arg are allowed. *)
let args = Cmdliner_info.Cmd.args @@ Cmdliner_info.Eval.cmd ei in
let tenvs = Cmdliner_info.Cmd.envs @@ Cmdliner_info.Eval.cmd ei in
let init = Cmdliner_info.Env.Set.empty, [] in
let acc = Cmdliner_info.Arg.Set.fold add_arg_env args init in
let _, envs = List.fold_left add_env acc tenvs in
let envs = List.sort by_sec_by_rev_name envs in
let envs = (envs :> (string * Cmdliner_manpage.block) list) in
let boilerplate = if has_senv then None else Some env_boilerplate in
sorted_items_to_blocks ~boilerplate envs
(* xref doc *)
let xref_docs ~errs ei =
let main = Cmdliner_info.Eval.main ei in
let to_xref = function
| `Main -> Cmdliner_info.Cmd.name main, 1
| `Tool tool -> tool, 1
| `Page (name, sec) -> name, sec
| `Cmd c ->
(* N.B. we are handling only the first subcommand level here *)
let cmds = Cmdliner_info.Cmd.children main in
let mname = Cmdliner_info.Cmd.name main in
let is_cmd cmd = Cmdliner_info.Cmd.name cmd = c in
if List.exists is_cmd cmds then strf "%s-%s" mname c, 1 else
(Format.fprintf errs "xref %s: no such command name@." c; "doc-err", 0)
in
let xref_str (name, sec) = strf "%s(%d)" (esc name) sec in
let xrefs = Cmdliner_info.Cmd.man_xrefs @@ Cmdliner_info.Eval.cmd ei in
let xrefs = match main == Cmdliner_info.Eval.cmd ei with
| true -> List.filter (fun x -> x <> `Main) xrefs (* filter out default *)
| false -> xrefs
in
let xrefs = List.fold_left (fun acc x -> to_xref x :: acc) [] xrefs in
let xrefs = List.(rev_map xref_str (sort rev_compare xrefs)) in
if xrefs = [] then [] else
[Cmdliner_manpage.s_see_also, `P (String.concat ", " xrefs)]
(* Man page construction *)
let ensure_s_name ei sm =
if Cmdliner_manpage.(smap_has_section sm ~sec:s_name) then sm else
let cmd = Cmdliner_info.Eval.cmd ei in
let parents = Cmdliner_info.Eval.parents ei in
let tname = (deprecated cmd) ^ invocation ~sep:"-" ~parents cmd in
let tdoc = Cmdliner_info.Cmd.doc cmd in
let tagline = if tdoc = "" then "" else strf " - %s" tdoc in
let tagline = `P (strf "%s%s" tname tagline) in
Cmdliner_manpage.(smap_append_block sm ~sec:s_name tagline)
let ensure_s_synopsis ei sm =
if Cmdliner_manpage.(smap_has_section sm ~sec:s_synopsis) then sm else
let cmd = Cmdliner_info.Eval.cmd ei in
let parents = Cmdliner_info.Eval.parents ei in
let synopsis = `P (synopsis ~parents cmd) in
Cmdliner_manpage.(smap_append_block sm ~sec:s_synopsis synopsis)
let insert_cmd_man_docs ~errs ei sm =
let buf = Buffer.create 200 in
let subst = cmd_info_subst ei in
let ins sm (sec, b) = Cmdliner_manpage.smap_append_block sm ~sec b in
let has_senv = Cmdliner_manpage.(smap_has_section sm ~sec:s_environment) in
let has_sexit = Cmdliner_manpage.(smap_has_section sm ~sec:s_exit_status) in
let sm = List.fold_left ins sm (cmd_docs ei) in
let sm = List.fold_left ins sm (arg_docs ~errs ~subst ~buf ei) in
let sm = List.fold_left ins sm (exit_docs ~errs ~subst ~buf ~has_sexit ei)in
let sm = List.fold_left ins sm (env_docs ~errs ~subst ~buf ~has_senv ei) in
let sm = List.fold_left ins sm (xref_docs ~errs ei) in
sm
let text ~errs ei =
let man = Cmdliner_info.Cmd.man @@ Cmdliner_info.Eval.cmd ei in
let sm = Cmdliner_manpage.smap_of_blocks man in
let sm = ensure_s_name ei sm in
let sm = ensure_s_synopsis ei sm in
let sm = insert_cmd_man_docs ei ~errs sm in
Cmdliner_manpage.smap_to_blocks sm
let title ei =
let main = Cmdliner_info.Eval.main ei in
let exec = String.capitalize_ascii (Cmdliner_info.Cmd.name main) in
let cmd = Cmdliner_info.Eval.cmd ei in
let parents = Cmdliner_info.Eval.parents ei in
let name = String.uppercase_ascii (invocation ~sep:"-" ~parents cmd) in
let center_header = esc @@ strf "%s Manual" exec in
let left_footer =
let version = match Cmdliner_info.Cmd.version main with
| None -> "" | Some v -> " " ^ v
in
esc @@ strf "%s%s" exec version
in
name, 1, "", left_footer, center_header
let man ~errs ei = title ei, text ~errs ei
let pp_man ~errs fmt ppf ei =
Cmdliner_manpage.print
~errs ~subst:(cmd_info_subst ei) fmt ppf (man ~errs ei)
(* Plain synopsis for usage *)
let pp_plain_synopsis ~errs ppf ei =
let buf = Buffer.create 100 in
let subst = cmd_info_subst ei in
let cmd = Cmdliner_info.Eval.cmd ei in
let parents = Cmdliner_info.Eval.parents ei in
let synopsis = synopsis ~parents cmd in
let syn = Cmdliner_manpage.doc_to_plain ~errs ~subst buf synopsis in
Format.fprintf ppf "@[%s@]" syn
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,27 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
val pp_man :
errs:Format.formatter -> Cmdliner_manpage.format -> Format.formatter ->
Cmdliner_info.Eval.t -> unit
val pp_plain_synopsis :
errs:Format.formatter -> Format.formatter -> Cmdliner_info.Eval.t -> unit
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,292 @@
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
type 'a eval_ok = [ `Ok of 'a | `Version | `Help ]
type eval_error = [ `Parse | `Term | `Exn ]
let err_help s = "Term error, help requested for unknown command " ^ s
let err_argv = "argv array must have at least one element"
let add_stdopts ei =
let docs = Cmdliner_info.Cmd.stdopts_docs @@ Cmdliner_info.Eval.cmd ei in
let vargs, vers =
match Cmdliner_info.Cmd.version @@ Cmdliner_info.Eval.main ei with
| None -> Cmdliner_info.Arg.Set.empty, None
| Some _ ->
let args, _ as vers = Cmdliner_arg.stdopt_version ~docs in
args, Some vers
in
let help = Cmdliner_arg.stdopt_help ~docs in
let args = Cmdliner_info.Arg.Set.union vargs (fst help) in
let cmd = Cmdliner_info.Cmd.add_args (Cmdliner_info.Eval.cmd ei) args in
help, vers, Cmdliner_info.Eval.with_cmd ei cmd
let parse_error_term err ei cl = Error (`Parse err)
type 'a eval_result =
('a, [ Cmdliner_term.term_escape
| `Exn of exn * Printexc.raw_backtrace
| `Parse of string
| `Std_help of Cmdliner_manpage.format | `Std_version ]) result
let run_parser ~catch ei cl f = try (f ei cl :> 'a eval_result) with
| exn when catch ->
let bt = Printexc.get_raw_backtrace () in
Error (`Exn (exn, bt))
let try_eval_stdopts ~catch ei cl help version =
match run_parser ~catch ei cl (snd help) with
| Ok (Some fmt) -> Some (Error (`Std_help fmt))
| Error _ as err -> Some err
| Ok None ->
match version with
| None -> None
| Some version ->
match run_parser ~catch ei cl (snd version) with
| Ok false -> None
| Ok true -> Some (Error (`Std_version))
| Error _ as err -> Some err
let do_help help_ppf err_ppf ei fmt cmd =
let ei = match cmd with
| None (* help of main command requested *) ->
let env _ = assert false in
let cmd = Cmdliner_info.Eval.main ei in
let ei' = Cmdliner_info.Eval.v ~cmd ~parents:[] ~env ~err_ppf in
begin match Cmdliner_info.Eval.parents ei with
| [] -> (* [ei] is an evaluation of main, [cmd] has stdopts *) ei'
| _ -> let _, _, ei = add_stdopts ei' in ei
end
| Some cmd ->
try
(* For now we simply keep backward compat. [cmd] should be
a name from main's children. *)
let main = Cmdliner_info.Eval.main ei in
let is_cmd t = Cmdliner_info.Cmd.name t = cmd in
let children = Cmdliner_info.Cmd.children main in
let cmd = List.find is_cmd children in
let _, _, ei = add_stdopts (Cmdliner_info.Eval.with_cmd ei cmd) in
ei
with Not_found -> invalid_arg (err_help cmd)
in
Cmdliner_docgen.pp_man ~errs:err_ppf fmt help_ppf ei
let do_result help_ppf err_ppf ei = function
| Ok v -> Ok (`Ok v)
| Error res ->
match res with
| `Std_help fmt ->
Cmdliner_docgen.pp_man ~errs:err_ppf fmt help_ppf ei; Ok `Help
| `Std_version ->
Cmdliner_msg.pp_version help_ppf ei; Ok `Version
| `Parse err ->
Cmdliner_msg.pp_err_usage err_ppf ei ~err_lines:false ~err;
Error `Parse
| `Help (fmt, cmd) -> do_help help_ppf err_ppf ei fmt cmd; Ok `Help
| `Exn (e, bt) -> Cmdliner_msg.pp_backtrace err_ppf ei e bt; (Error `Exn)
| `Error (usage, err) ->
(if usage
then Cmdliner_msg.pp_err_usage err_ppf ei ~err_lines:true ~err
else Cmdliner_msg.pp_err err_ppf ei ~err);
(Error `Term)
let cmd_name_trie cmds =
let add acc cmd =
let i = Cmdliner_cmd.get_info cmd in
let name = Cmdliner_info.Cmd.name i in
match Cmdliner_trie.add acc name cmd with
| `New t -> t
| `Replaced (cmd', _) ->
let i' = Cmdliner_cmd.get_info cmd' and kind = "command" in
invalid_arg @@
Cmdliner_base.err_multi_def ~kind name Cmdliner_info.Cmd.doc i i'
in
List.fold_left add Cmdliner_trie.empty cmds
let cmd_name_dom cmds =
let cmd_name c = Cmdliner_info.Cmd.name (Cmdliner_cmd.get_info c) in
List.sort String.compare (List.rev_map cmd_name cmds)
let find_term args cmd =
let never_term _ _ = assert false in
let stop args_rest args_rev parents cmd =
let args = List.rev_append args_rev args_rest in
match (cmd : 'a Cmdliner_cmd.t) with
| Cmd (i, t) ->
args, t, i, parents, Ok ()
| Group (i, (Some t, children)) ->
args, t, i, parents, Ok ()
| Group (i, (None, children)) ->
let dom = cmd_name_dom children in
let err = Cmdliner_msg.err_cmd_missing ~dom in
args, never_term, i, parents, Error err
in
let rec loop args_rev parents cmd = function
| ("--" :: _ | [] as rest) -> stop rest args_rev parents cmd
| (arg :: _ as rest) when Cmdliner_cline.is_opt arg ->
stop rest args_rev parents cmd
| arg :: args ->
match cmd with
| Cmd (i, t) ->
let args = List.rev_append args_rev (arg :: args) in
args, t, i, parents, Ok ()
| Group (i, (t, children)) ->
let index = cmd_name_trie children in
match Cmdliner_trie.find index arg with
| `Ok cmd -> loop args_rev (i :: parents) cmd args
| `Not_found ->
let args = List.rev_append args_rev (arg :: args) in
let all = Cmdliner_trie.ambiguities index "" in
let hints = Cmdliner_base.suggest arg all in
let dom = cmd_name_dom children in
let kind = "command" in
let err = Cmdliner_base.err_unknown ~kind ~dom ~hints arg in
args, never_term, i, parents, Error err
| `Ambiguous ->
let args = List.rev_append args_rev (arg :: args) in
let ambs = Cmdliner_trie.ambiguities index arg in
let ambs = List.sort compare ambs in
let err = Cmdliner_base.err_ambiguous ~kind:"command" arg ~ambs in
args, never_term, i, parents, Error err
in
loop [] [] cmd args
let env_default v = try Some (Sys.getenv v) with Not_found -> None
let remove_exec argv =
try List.tl (Array.to_list argv) with Failure _ -> invalid_arg err_argv
let do_deprecated_msgs err_ppf cl ei =
let cmd = Cmdliner_info.Eval.cmd ei in
let msgs = Cmdliner_cline.deprecated_msgs cl in
let msgs = match Cmdliner_info.Cmd.deprecated cmd with
| None -> msgs
| Some msg ->
let name = Cmdliner_base.quote (Cmdliner_info.Cmd.name cmd) in
String.concat "" ("command " :: name :: ": " :: msg :: []) :: msgs
in
if msgs <> []
then Cmdliner_msg.pp_err err_ppf ei ~err:(String.concat "\n" msgs)
let eval_value
?help:(help_ppf = Format.std_formatter)
?err:(err_ppf = Format.err_formatter)
?(catch = true) ?(env = env_default) ?(argv = Sys.argv) cmd
=
let args, f, cmd, parents, res = find_term (remove_exec argv) cmd in
let ei = Cmdliner_info.Eval.v ~cmd ~parents ~env ~err_ppf in
let help, version, ei = add_stdopts ei in
let term_args = Cmdliner_info.Cmd.args @@ Cmdliner_info.Eval.cmd ei in
let res = match res with
| Error msg -> (* Command lookup error, we still prioritize stdargs *)
let cl = match Cmdliner_cline.create term_args args with
| Error (_, cl) -> cl | Ok cl -> cl
in
begin match try_eval_stdopts ~catch ei cl help version with
| Some e -> e
| None -> Error (`Error (true, msg))
end
| Ok () ->
match Cmdliner_cline.create term_args args with
| Error (e, cl) ->
begin match try_eval_stdopts ~catch ei cl help version with
| Some e -> e
| None -> Error (`Error (true, e))
end
| Ok cl ->
match try_eval_stdopts ~catch ei cl help version with
| Some e -> e
| None ->
do_deprecated_msgs err_ppf cl ei;
run_parser ~catch ei cl f
in
do_result help_ppf err_ppf ei res
let eval_peek_opts
?(version_opt = false) ?(env = env_default) ?(argv = Sys.argv) t
: 'a option * ('a eval_ok, eval_error) result
=
let args, f = t in
let version = if version_opt then Some "dummy" else None in
let cmd = Cmdliner_info.Cmd.v ?version "dummy" in
let cmd = Cmdliner_info.Cmd.add_args cmd args in
let null_ppf = Format.make_formatter (fun _ _ _ -> ()) (fun () -> ()) in
let ei = Cmdliner_info.Eval.v ~cmd ~parents:[] ~env ~err_ppf:null_ppf in
let help, version, ei = add_stdopts ei in
let term_args = Cmdliner_info.Cmd.args @@ Cmdliner_info.Eval.cmd ei in
let cli_args = remove_exec argv in
let v, ret =
match Cmdliner_cline.create ~peek_opts:true term_args cli_args with
| Error (e, cl) ->
begin match try_eval_stdopts ~catch:true ei cl help version with
| Some e -> None, e
| None -> None, Error (`Error (true, e))
end
| Ok cl ->
let ret = run_parser ~catch:true ei cl f in
let v = match ret with Ok v -> Some v | Error _ -> None in
match try_eval_stdopts ~catch:true ei cl help version with
| Some e -> v, e
| None -> v, ret
in
let ret = match ret with
| Ok v -> Ok (`Ok v)
| Error `Std_help _ -> Ok `Help
| Error `Std_version -> Ok `Version
| Error `Parse _ -> Error `Parse
| Error `Help _ -> Ok `Help
| Error `Exn _ -> Error `Exn
| Error `Error _ -> Error `Term
in
(v, ret)
let exit_status_of_result ?(term_err = Cmdliner_info.Exit.cli_error) = function
| Ok (`Ok _ | `Help | `Version) -> Cmdliner_info.Exit.ok
| Error `Term -> term_err
| Error `Parse -> Cmdliner_info.Exit.cli_error
| Error `Exn -> Cmdliner_info.Exit.internal_error
let eval ?help ?err ?catch ?env ?argv ?term_err cmd =
exit_status_of_result ?term_err @@
eval_value ?help ?err ?catch ?env ?argv cmd
let eval' ?help ?err ?catch ?env ?argv ?term_err cmd =
match eval_value ?help ?err ?catch ?env ?argv cmd with
| Ok (`Ok c) -> c
| r -> exit_status_of_result ?term_err r
let pp_err ppf cmd ~msg = (* FIXME move that to Cmdliner_msgs *)
let name = Cmdliner_cmd.name cmd in
Format.fprintf ppf "%s: @[%a@]@." name Cmdliner_base.pp_lines msg
let eval_result
?help ?(err = Format.err_formatter) ?catch ?env ?argv ?term_err cmd
=
match eval_value ?help ~err ?catch ?env ?argv cmd with
| Ok (`Ok (Error msg)) -> pp_err err cmd ~msg; Cmdliner_info.Exit.some_error
| r -> exit_status_of_result ?term_err r
let eval_result'
?help ?(err = Format.err_formatter) ?catch ?env ?argv ?term_err cmd
=
match eval_value ?help ~err ?catch ?env ?argv cmd with
| Ok (`Ok (Ok c)) -> c
| Ok (`Ok (Error msg)) -> pp_err err cmd ~msg; Cmdliner_info.Exit.some_error
| r -> exit_status_of_result ?term_err r
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,60 @@
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Command evaluation *)
(** {1:eval Evaluating commands} *)
type 'a eval_ok = [ `Ok of 'a | `Version | `Help ]
type eval_error = [ `Parse | `Term | `Exn ]
val eval_value :
?help:Format.formatter -> ?err:Format.formatter -> ?catch:bool ->
?env:(string -> string option) -> ?argv:string array -> 'a Cmdliner_cmd.t ->
('a eval_ok, eval_error) result
val eval_peek_opts :
?version_opt:bool -> ?env:(string -> string option) ->
?argv:string array -> 'a Cmdliner_term.t ->
'a option * ('a eval_ok, eval_error) result
val eval :
?help:Format.formatter -> ?err:Format.formatter -> ?catch:bool ->
?env:(string -> string option) -> ?argv:string array ->
?term_err:int -> unit Cmdliner_cmd.t -> Cmdliner_info.Exit.code
val eval' :
?help:Format.formatter -> ?err:Format.formatter -> ?catch:bool ->
?env:(string -> string option) -> ?argv:string array ->
?term_err:int -> int Cmdliner_cmd.t -> Cmdliner_info.Exit.code
val eval_result :
?help:Format.formatter -> ?err:Format.formatter -> ?catch:bool ->
?env:(string -> string option) -> ?argv:string array ->
?term_err:Cmdliner_info.Exit.code -> (unit, string) result Cmdliner_cmd.t ->
Cmdliner_info.Exit.code
val eval_result' :
?help:Format.formatter -> ?err:Format.formatter -> ?catch:bool ->
?env:(string -> string option) -> ?argv:string array ->
?term_err:Cmdliner_info.Exit.code ->
(Cmdliner_info.Exit.code, string) result Cmdliner_cmd.t ->
Cmdliner_info.Exit.code
(*---------------------------------------------------------------------------
Copyright (c) 2022 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,21 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,21 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,247 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(* Exit codes *)
module Exit = struct
type code = int
let ok = 0
let some_error = 123
let cli_error = 124
let internal_error = 125
type info =
{ codes : code * code; (* min, max *)
doc : string; (* help. *)
docs : string; } (* title of help section where listed. *)
let info
?(docs = Cmdliner_manpage.s_exit_status) ?(doc = "undocumented") ?max min
=
let max = match max with None -> min | Some max -> max in
{ codes = (min, max); doc; docs }
let info_codes i = i.codes
let info_code i = fst i.codes
let info_doc i = i.doc
let info_docs i = i.docs
let info_order i0 i1 = compare i0.codes i1.codes
let defaults =
[ info ok ~doc:"on success.";
info some_error
~doc:"on indiscriminate errors reported on standard error.";
info cli_error ~doc:"on command line parsing errors.";
info internal_error ~doc:"on unexpected internal errors (bugs)."; ]
end
(* Environment variables *)
module Env = struct
type var = string
type info = (* information about an environment variable. *)
{ id : int; (* unique id for the env var. *)
deprecated : string option;
var : string; (* the variable. *)
doc : string; (* help. *)
docs : string; } (* title of help section where listed. *)
let info
?deprecated
?(docs = Cmdliner_manpage.s_environment) ?(doc = "See option $(opt).") var
=
{ id = Cmdliner_base.uid (); deprecated; var; doc; docs }
let info_deprecated i = i.deprecated
let info_var i = i.var
let info_doc i = i.doc
let info_docs i = i.docs
let info_compare i0 i1 = Int.compare i0.id i1.id
module Set = Set.Make (struct type t = info let compare = info_compare end)
end
(* Arguments *)
module Arg = struct
type absence = Err | Val of string Lazy.t | Doc of string
type opt_kind = Flag | Opt | Opt_vopt of string
type pos_kind = (* information about a positional argument. *)
{ pos_rev : bool; (* if [true] positions are counted from the end. *)
pos_start : int; (* start positional argument. *)
pos_len : int option } (* number of arguments or [None] if unbounded. *)
let pos ~rev:pos_rev ~start:pos_start ~len:pos_len =
{ pos_rev; pos_start; pos_len}
let pos_rev p = p.pos_rev
let pos_start p = p.pos_start
let pos_len p = p.pos_len
type t = (* information about a command line argument. *)
{ id : int; (* unique id for the argument. *)
deprecated : string option; (* deprecation message *)
absent : absence; (* behaviour if absent. *)
env : Env.info option; (* environment variable for default value. *)
doc : string; (* help. *)
docv : string; (* variable name for the argument in help. *)
docs : string; (* title of help section where listed. *)
pos : pos_kind; (* positional arg kind. *)
opt_kind : opt_kind; (* optional arg kind. *)
opt_names : string list; (* names (for opt args). *)
opt_all : bool; (* repeatable (for opt args). *)
opt_alias: string -> string option -> (string list, string) Result.t; (* [opt_alias arg value], [arg] is the name of the argument,
and [value] is the possible value *)
}
let dumb_pos = pos ~rev:false ~start:(-1) ~len:None
let v ?deprecated ?(absent = "") ?docs ?(docv = "") ?(doc = "") ?env names =
let dash n = if String.length n = 1 then "-" ^ n else "--" ^ n in
let opt_names = List.map dash names in
let docs = match docs with
| Some s -> s
| None ->
match names with
| [] -> Cmdliner_manpage.s_arguments
| _ -> Cmdliner_manpage.s_options
in
{ id = Cmdliner_base.uid (); deprecated; absent = Doc absent;
env; doc; docv; docs; pos = dumb_pos; opt_kind = Flag; opt_names;
opt_all = false;
opt_alias = fun _ _ -> Ok [] }
let id a = a.id
let deprecated a = a.deprecated
let absent a = a.absent
let env a = a.env
let doc a = a.doc
let docv a = a.docv
let docs a = a.docs
let pos_kind a = a.pos
let opt_kind a = a.opt_kind
let opt_names a = a.opt_names
let opt_all a = a.opt_all
let opt_name_sample a =
(* First long or short name (in that order) in the list; this
allows the client to control which name is shown *)
let rec find = function
| [] -> List.hd a.opt_names
| n :: ns -> if (String.length n) > 2 then n else find ns
in
find a.opt_names
let alias a = a.opt_alias
let make_req a = { a with absent = Err }
let make_all_opts a = { a with opt_all = true }
let make_opt ~absent ~kind:opt_kind a = { a with absent; opt_kind }
let make_opt_all ~absent ~kind:opt_kind a =
{ a with absent; opt_kind; opt_all = true }
let make_pos ~pos a = { a with pos }
let make_pos_abs ~absent ~pos a = { a with absent; pos }
let aliases ~aliases a = { a with opt_alias = aliases }
let is_opt a = a.opt_names <> []
let is_pos a = a.opt_names = []
let is_req a = a.absent = Err
let pos_cli_order a0 a1 = (* best-effort order on the cli. *)
let c = compare (a0.pos.pos_rev) (a1.pos.pos_rev) in
if c <> 0 then c else
if a0.pos.pos_rev
then compare a1.pos.pos_start a0.pos.pos_start
else compare a0.pos.pos_start a1.pos.pos_start
let rev_pos_cli_order a0 a1 = pos_cli_order a1 a0
let compare a0 a1 = Int.compare a0.id a1.id
module Set = Set.Make (struct type nonrec t = t let compare = compare end)
end
(* Commands *)
module Cmd = struct
type t =
{ name : string; (* name of the cmd. *)
version : string option; (* version (for --version). *)
deprecated : string option; (* deprecation message *)
doc : string; (* one line description of cmd. *)
docs : string; (* title of man section where listed (commands). *)
sdocs : string; (* standard options, title of section where listed. *)
exits : Exit.info list; (* exit codes for the cmd. *)
envs : Env.info list; (* env vars that influence the cmd. *)
man : Cmdliner_manpage.block list; (* man page text. *)
man_xrefs : Cmdliner_manpage.xref list; (* man cross-refs. *)
args : Arg.Set.t; (* Command arguments. *)
has_args : bool; (* [true] if has own parsing term. *)
children : t list; } (* Children, if any. *)
let v
?deprecated ?(man_xrefs = [`Main]) ?(man = []) ?(envs = [])
?(exits = Exit.defaults) ?(sdocs = Cmdliner_manpage.s_common_options)
?(docs = Cmdliner_manpage.s_commands) ?(doc = "") ?version name
=
{ name; version; deprecated; doc; docs; sdocs; exits;
envs; man; man_xrefs; args = Arg.Set.empty;
has_args = true; children = [] }
let name t = t.name
let version t = t.version
let deprecated t = t.deprecated
let doc t = t.doc
let docs t = t.docs
let stdopts_docs t = t.sdocs
let exits t = t.exits
let envs t = t.envs
let man t = t.man
let man_xrefs t = t.man_xrefs
let args t = t.args
let has_args t = t.has_args
let children t = t.children
let add_args t args = { t with args = Arg.Set.union args t.args }
let with_children cmd ~args ~children =
let has_args, args = match args with
| None -> false, cmd.args
| Some args -> true, Arg.Set.union args cmd.args
in
{ cmd with has_args; args; children }
end
(* Evaluation *)
module Eval = struct
type t = (* information about the evaluation context. *)
{ cmd : Cmd.t; (* cmd being evaluated. *)
parents : Cmd.t list; (* parents of cmd, root is last. *)
env : string -> string option; (* environment variable lookup. *)
err_ppf : Format.formatter (* error formatter *) }
let v ~cmd ~parents ~env ~err_ppf = { cmd; parents; env; err_ppf }
let cmd e = e.cmd
let parents e = e.parents
let env_var e v = e.env v
let err_ppf e = e.err_ppf
let main e = match List.rev e.parents with [] -> e.cmd | m :: _ -> m
let with_cmd ei cmd = { ei with cmd }
end
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,157 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Exit codes, environment variables, arguments, commands and eval information.
These information types gathers untyped data used to parse command
lines report errors and format man pages. *)
(** Exit codes. *)
module Exit : sig
type code = int
val ok : code
val some_error : code
val cli_error : code
val internal_error : code
type info
val info : ?docs:string -> ?doc:string -> ?max:code -> code -> info
val info_code : info -> code
val info_codes : info -> code * code
val info_doc : info -> string
val info_docs : info -> string
val info_order : info -> info -> int
val defaults : info list
end
(** Environment variables. *)
module Env : sig
type var = string
type info
val info : ?deprecated:string -> ?docs:string -> ?doc:string -> var -> info
val info_var : info -> string
val info_doc : info -> string
val info_docs : info -> string
val info_deprecated : info -> string option
module Set : Set.S with type elt = info
end
(** Arguments *)
module Arg : sig
type absence =
| Err (** an error is reported. *)
| Val of string Lazy.t (** if <> "", takes the given default value. *)
| Doc of string
(** if <> "", a doc string interpreted in the doc markup language. *)
(** The type for what happens if the argument is absent from the cli. *)
type opt_kind =
| Flag (** without value, just a flag. *)
| Opt (** with required value. *)
| Opt_vopt of string (** with optional value, takes given default. *)
(** The type for optional argument kinds. *)
type pos_kind
val pos : rev:bool -> start:int -> len:int option -> pos_kind
val pos_rev : pos_kind -> bool
val pos_start : pos_kind -> int
val pos_len : pos_kind -> int option
type t
val v :
?deprecated:string -> ?absent:string -> ?docs:string -> ?docv:string ->
?doc:string -> ?env:Env.info -> string list -> t
val id : t -> int
val deprecated : t -> string option
val absent : t -> absence
val env : t -> Env.info option
val doc : t -> string
val docv : t -> string
val docs : t -> string
val opt_names : t -> string list (* has dashes *)
val opt_name_sample : t -> string (* warning must be an opt arg *)
val opt_kind : t -> opt_kind
val pos_kind : t -> pos_kind
val alias : t -> string -> string option -> (string list, string) Result.t
val make_req : t -> t
val make_all_opts : t -> t
val make_opt : absent:absence -> kind:opt_kind -> t -> t
val make_opt_all : absent:absence -> kind:opt_kind -> t -> t
val make_pos : pos:pos_kind -> t -> t
val make_pos_abs : absent:absence -> pos:pos_kind -> t -> t
val aliases : aliases:(string -> string option -> (string list, string) Result.t) -> t -> t
val is_opt : t -> bool
val is_pos : t -> bool
val is_req : t -> bool
val pos_cli_order : t -> t -> int
val rev_pos_cli_order : t -> t -> int
val compare : t -> t -> int
module Set : Set.S with type elt = t
end
(** Commands. *)
module Cmd : sig
type t
val v :
?deprecated:string ->
?man_xrefs:Cmdliner_manpage.xref list -> ?man:Cmdliner_manpage.block list ->
?envs:Env.info list -> ?exits:Exit.info list ->
?sdocs:string -> ?docs:string -> ?doc:string -> ?version:string ->
string -> t
val name : t -> string
val version : t -> string option
val deprecated : t -> string option
val doc : t -> string
val docs : t -> string
val stdopts_docs : t -> string
val exits : t -> Exit.info list
val envs : t -> Env.info list
val man : t -> Cmdliner_manpage.block list
val man_xrefs : t -> Cmdliner_manpage.xref list
val args : t -> Arg.Set.t
val has_args : t -> bool
val children : t -> t list
val add_args : t -> Arg.Set.t -> t
val with_children : t -> args:Arg.Set.t option -> children:t list -> t
end
(** Evaluation. *)
module Eval : sig
type t
val v :
cmd:Cmd.t -> parents:Cmd.t list -> env:(string -> string option) ->
err_ppf:Format.formatter -> t
val cmd : t -> Cmd.t
val main : t -> Cmd.t
val parents : t -> Cmd.t list
val env_var : t -> string -> string option
val err_ppf : t -> Format.formatter
val with_cmd : t -> Cmd.t -> t
end
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,534 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(* Manpages *)
type block =
[ `S of string | `P of string | `Pre of string | `I of string * string
| `Noblank | `Blocks of block list ]
type title = string * int * string * string * string
type t = title * block list
type xref =
[ `Main | `Cmd of string | `Tool of string | `Page of string * int ]
(* Standard sections *)
let s_name = "NAME"
let s_synopsis = "SYNOPSIS"
let s_description = "DESCRIPTION"
let s_commands = "COMMANDS"
let s_command_aliases = "COMMAND ALIASES"
let s_arguments = "ARGUMENTS"
let s_options = "OPTIONS"
let s_common_options = "COMMON OPTIONS"
let s_exit_status = "EXIT STATUS"
let s_exit_status_intro = `P "$(iname) exits with:"
let s_environment = "ENVIRONMENT"
let s_environment_intro =
`P "These environment variables affect the execution of $(tname):"
let s_files = "FILES"
let s_examples = "EXAMPLES"
let s_bugs = "BUGS"
let s_authors = "AUTHORS"
let s_see_also = "SEE ALSO"
let s_none = "cmdliner-none"
(* Section order *)
let s_created = ""
let order =
[| s_name; s_synopsis; s_description; s_created; s_commands;
s_arguments; s_options; s_common_options; s_exit_status;
s_environment; s_files; s_examples; s_bugs; s_authors; s_see_also;
s_none; |]
let order_synopsis = 1
let order_created = 3
let section_of_order i = order.(i)
let section_to_order ~on_unknown s =
let max = Array.length order - 1 in
let rec loop i = match i > max with
| true -> on_unknown
| false -> if order.(i) = s then i else loop (i + 1)
in
loop 0
(* Section maps
Section maps, maps section names to their section order and reversed
content blocks (content is not reversed in `Block blocks). The sections
are listed in reversed order. Unknown sections get the order of the last
known section. *)
type smap = (string * (int * block list)) list
let smap_of_blocks bs = (* N.B. this flattens `Blocks, not t.r. *)
let rec loop s s_o rbs smap = function
| [] -> s, s_o, rbs, smap
| `S new_sec :: bs ->
let new_o = section_to_order ~on_unknown:s_o new_sec in
loop new_sec new_o [] ((s, (s_o, rbs)):: smap) bs
| `Blocks blist :: bs ->
let s, s_o, rbs, rmap = loop s s_o rbs smap blist (* not t.r. *) in
loop s s_o rbs rmap bs
| (`P _ | `Pre _ | `I _ | `Noblank as c) :: bs ->
loop s s_o (c :: rbs) smap bs
in
let first, (bs : block list) = match bs with
| `S s :: bs -> s, bs
| `Blocks (`S s :: blist) :: bs -> s, (`Blocks blist) :: bs
| _ -> "", bs
in
let first_o = section_to_order ~on_unknown:order_synopsis first in
let s, s_o, rc, smap = loop first first_o [] [] bs in
(s, (s_o, rc)) :: smap
let smap_to_blocks smap = (* N.B. this leaves `Blocks content untouched. *)
let rec loop acc smap s = function
| b :: rbs -> loop (b :: acc) smap s rbs
| [] ->
let acc = if s = "" then acc else `S s :: acc in
match smap with
| [] -> acc
| (_, (_, [])) :: smap -> loop acc smap "" [] (* skip empty section *)
| (s, (_, rbs)) :: smap ->
if s = s_none
then loop acc smap "" [] (* skip *)
else loop acc smap s rbs
in
loop [] smap "" []
let smap_has_section smap ~sec = List.exists (fun (s, _) -> sec = s) smap
let smap_append_block smap ~sec b =
let o = section_to_order ~on_unknown:order_created sec in
let try_insert =
let rec loop max_lt_o left = function
| (s', (o, rbs)) :: right when s' = sec ->
Ok (List.rev_append ((sec, (o, b :: rbs)) :: left) right)
| (_, (o', _) as s) :: right ->
let max_lt_o = if o' < o then max o' max_lt_o else max_lt_o in
loop max_lt_o (s :: left) right
| [] ->
if max_lt_o <> -1 then Error max_lt_o else
Ok (List.rev ((sec, (o, [b])) :: left))
in
loop (-1) [] smap
in
match try_insert with
| Ok smap -> smap
| Error insert_before ->
let rec loop left = function
| (s', (o', _)) :: _ as right when o' = insert_before ->
List.rev_append ((sec, (o, [b])) :: left) right
| s :: ss -> loop (s :: left) ss
| [] -> assert false
in
loop [] smap
(* Formatting tools *)
let strf = Printf.sprintf
let pf = Format.fprintf
let pp_str = Format.pp_print_string
let pp_char = Format.pp_print_char
let pp_indent ppf c = for i = 1 to c do pp_char ppf ' ' done
let pp_lines = Cmdliner_base.pp_lines
let pp_tokens = Cmdliner_base.pp_tokens
(* Cmdliner markup handling *)
let err e fmt = pf e ("cmdliner error: " ^^ fmt ^^ "@.")
let err_unescaped ~errs c s = err errs "unescaped %C in %S" c s
let err_malformed ~errs s = err errs "Malformed $(…) in %S" s
let err_unclosed ~errs s = err errs "Unclosed $(…) in %S" s
let err_undef ~errs id s = err errs "Undefined variable $(%s) in %S" id s
let err_illegal_esc ~errs c s = err errs "Illegal escape char %C in %S" c s
let err_markup ~errs dir s =
err errs "Unknown cmdliner markup $(%c,…) in %S" dir s
let is_markup_dir = function 'i' | 'b' -> true | _ -> false
let is_markup_esc = function '$' | '\\' | '(' | ')' -> true | _ -> false
let markup_need_esc = function '\\' | '$' -> true | _ -> false
let markup_text_need_esc = function '\\' | '$' | ')' -> true | _ -> false
let escape s = (* escapes [s] from doc language. *)
let max_i = String.length s - 1 in
let rec escaped_len i l =
if i > max_i then l else
if markup_text_need_esc s.[i] then escaped_len (i + 1) (l + 2) else
escaped_len (i + 1) (l + 1)
in
let escaped_len = escaped_len 0 0 in
if escaped_len = String.length s then s else
let b = Bytes.create escaped_len in
let rec loop i k =
if i > max_i then Bytes.unsafe_to_string b else
let c = String.unsafe_get s i in
if not (markup_text_need_esc c)
then (Bytes.unsafe_set b k c; loop (i + 1) (k + 1))
else (Bytes.unsafe_set b k '\\'; Bytes.unsafe_set b (k + 1) c;
loop (i + 1) (k + 2))
in
loop 0 0
let subst_vars ~errs ~subst b s =
let max_i = String.length s - 1 in
let flush start stop = match start > max_i with
| true -> ()
| false -> Buffer.add_substring b s start (stop - start + 1)
in
let skip_escape k start i =
if i > max_i then err_unescaped ~errs '\\' s else k start (i + 1)
in
let rec skip_markup k start i =
if i > max_i then (err_unclosed ~errs s; k start i) else
match s.[i] with
| '\\' -> skip_escape (skip_markup k) start (i + 1)
| ')' -> k start (i + 1)
| c -> skip_markup k start (i + 1)
in
let rec add_subst start i =
if i > max_i then (err_unclosed ~errs s; loop start i) else
if s.[i] <> ')' then add_subst start (i + 1) else
let id = String.sub s start (i - start) in
let next = i + 1 in
begin match subst id with
| None -> err_undef ~errs id s; Buffer.add_string b "undefined";
| Some v -> Buffer.add_string b v
end;
loop next next
and loop start i =
if i > max_i then flush start max_i else
let next = i + 1 in
match s.[i] with
| '\\' -> skip_escape loop start next
| '$' ->
if next > max_i then err_unescaped ~errs '$' s else
begin match s.[next] with
| '(' ->
let min = next + 2 in
if min > max_i then (err_unclosed ~errs s; loop start next) else
begin match s.[min] with
| ',' -> skip_markup loop start (min + 1)
| _ ->
let start_id = next + 1 in
flush start (i - 1); add_subst start_id start_id
end
| _ -> err_unescaped ~errs '$' s; loop start next
end;
| c -> loop start next
in
(Buffer.clear b; loop 0 0; Buffer.contents b)
let add_markup_esc ~errs k b s start next target_need_escape target_escape =
let max_i = String.length s - 1 in
if next > max_i then err_unescaped ~errs '\\' s else
match s.[next] with
| c when not (is_markup_esc s.[next]) ->
err_illegal_esc ~errs c s;
k (next + 1) (next + 1)
| c ->
(if target_need_escape c then target_escape b c else Buffer.add_char b c);
k (next + 1) (next + 1)
let add_markup_text ~errs k b s start target_need_escape target_escape =
let max_i = String.length s - 1 in
let flush start stop = match start > max_i with
| true -> ()
| false -> Buffer.add_substring b s start (stop - start + 1)
in
let rec loop start i =
if i > max_i then (err_unclosed ~errs s; flush start max_i) else
let next = i + 1 in
match s.[i] with
| '\\' -> (* unescape *)
flush start (i - 1);
add_markup_esc ~errs loop b s start next
target_need_escape target_escape
| ')' -> flush start (i - 1); k next next
| c when markup_text_need_esc c ->
err_unescaped ~errs c s; flush start (i - 1); loop next next
| c when target_need_escape c ->
flush start (i - 1); target_escape b c; loop next next
| c -> loop start next
in
loop start start
(* Plain text output *)
let markup_to_plain ~errs b s =
let max_i = String.length s - 1 in
let flush start stop = match start > max_i with
| true -> ()
| false -> Buffer.add_substring b s start (stop - start + 1)
in
let need_escape _ = false in
let escape _ _ = assert false in
let rec loop start i =
if i > max_i then flush start max_i else
let next = i + 1 in
match s.[i] with
| '\\' ->
flush start (i - 1);
add_markup_esc ~errs loop b s start next need_escape escape
| '$' ->
if next > max_i then err_unescaped ~errs '$' s else
begin match s.[next] with
| '(' ->
let min = next + 2 in
if min > max_i then (err_unclosed ~errs s; loop start next) else
begin match s.[min] with
| ',' ->
let markup = s.[min - 1] in
if not (is_markup_dir markup)
then (err_markup ~errs markup s; loop start next) else
let start_data = min + 1 in
(flush start (i - 1);
add_markup_text ~errs loop b s start_data need_escape escape)
| _ ->
err_malformed ~errs s; loop start next
end
| _ -> err_unescaped ~errs '$' s; loop start next
end
| c when markup_need_esc c ->
err_unescaped ~errs c s; flush start (i - 1); loop next next
| c -> loop start next
in
(Buffer.clear b; loop 0 0; Buffer.contents b)
let doc_to_plain ~errs ~subst b s =
markup_to_plain ~errs b (subst_vars ~errs ~subst b s)
let p_indent = 7 (* paragraph indentation. *)
let l_indent = 4 (* label indentation. *)
let pp_plain_blocks ~errs subst ppf ts =
let b = Buffer.create 1024 in
let markup t = doc_to_plain ~errs b ~subst t in
let pp_tokens ppf t = pp_tokens ~spaces:true ppf t in
let rec blank_line = function
| `Noblank :: ts -> loop ts
| ts -> Format.pp_print_cut ppf (); loop ts
and loop = function
| [] -> ()
| t :: ts ->
match t with
| `Noblank -> loop ts
| `Blocks bs -> loop (bs @ ts)
| `P s ->
pf ppf "%a@[%a@]@," pp_indent p_indent pp_tokens (markup s);
blank_line ts
| `S s -> pf ppf "@[%a@]@," pp_tokens (markup s); loop ts
| `Pre s ->
pf ppf "%a@[%a@]@," pp_indent p_indent pp_lines (markup s);
blank_line ts
| `I (label, s) ->
let label = markup label and s = markup s in
pf ppf "@[%a@[%a@]" pp_indent p_indent pp_tokens label;
begin match s with
| "" -> pf ppf "@]@,"
| s ->
let ll = String.length label in
if ll < l_indent
then (pf ppf "%a@[%a@]@]@," pp_indent (l_indent - ll) pp_tokens s)
else (pf ppf "@\n%a@[%a@]@]@,"
pp_indent (p_indent + l_indent) pp_tokens s)
end;
blank_line ts
in
loop ts
let pp_plain_page ~errs subst ppf (_, text) =
pf ppf "@[<v>%a@]" (pp_plain_blocks ~errs subst) text
(* Groff output *)
let markup_to_groff ~errs b s =
let max_i = String.length s - 1 in
let flush start stop = match start > max_i with
| true -> ()
| false -> Buffer.add_substring b s start (stop - start + 1)
in
let need_escape = function '.' | '\'' | '-' | '\\' -> true | _ -> false in
let escape b c = Printf.bprintf b "\\N'%d'" (Char.code c) in
let rec end_text start i = Buffer.add_string b "\\fR"; loop start i
and loop start i =
if i > max_i then flush start max_i else
let next = i + 1 in
match s.[i] with
| '\\' ->
flush start (i - 1);
add_markup_esc ~errs loop b s start next need_escape escape
| '$' ->
if next > max_i then err_unescaped ~errs '$' s else
begin match s.[next] with
| '(' ->
let min = next + 2 in
if min > max_i then (err_unclosed ~errs s; loop start next) else
begin match s.[min] with
| ',' ->
let start_data = min + 1 in
flush start (i - 1);
begin match s.[min - 1] with
| 'i' -> Buffer.add_string b "\\fI"
| 'b' -> Buffer.add_string b "\\fB"
| markup -> err_markup ~errs markup s
end;
add_markup_text ~errs end_text b s start_data need_escape escape
| _ -> err_malformed ~errs s; loop start next
end
| _ -> err_unescaped ~errs '$' s; flush start (i - 1); loop next next
end
| c when markup_need_esc c ->
err_unescaped ~errs c s; flush start (i - 1); loop next next
| c when need_escape c ->
flush start (i - 1); escape b c; loop next next
| c -> loop start next
in
(Buffer.clear b; loop 0 0; Buffer.contents b)
let doc_to_groff ~errs ~subst b s =
markup_to_groff ~errs b (subst_vars ~errs ~subst b s)
let pp_groff_blocks ~errs subst ppf text =
let buf = Buffer.create 1024 in
let markup t = doc_to_groff ~errs ~subst buf t in
let pp_tokens ppf t = pp_tokens ~spaces:false ppf t in
let rec pp_block = function
| `Blocks bs -> List.iter pp_block bs (* not T.R. *)
| `P s -> pf ppf "@\n.P@\n%a" pp_tokens (markup s)
| `Pre s -> pf ppf "@\n.P@\n.nf@\n%a@\n.fi" pp_lines (markup s)
| `S s -> pf ppf "@\n.SH %a" pp_tokens (markup s)
| `Noblank -> pf ppf "@\n.sp -1"
| `I (l, s) ->
pf ppf "@\n.TP 4@\n%a@\n%a" pp_tokens (markup l) pp_tokens (markup s)
in
List.iter pp_block text
let pp_groff_page ~errs subst ppf ((n, s, a1, a2, a3), t) =
pf ppf ".\\\" Pipe this output to groff -m man -K utf8 -T utf8 | less -R@\n\
.\\\"@\n\
.mso an.tmac@\n\
.TH \"%s\" %d \"%s\" \"%s\" \"%s\"@\n\
.\\\" Disable hyphenation and ragged-right@\n\
.nh@\n\
.ad l\
%a@?"
n s a1 a2 a3 (pp_groff_blocks ~errs subst) t
(* Printing to a pager *)
let pp_to_temp_file pp_v v =
try
let exec = Filename.basename Sys.argv.(0) in
let file, oc = Filename.open_temp_file exec "out" in
let ppf = Format.formatter_of_out_channel oc in
pp_v ppf v; Format.pp_print_flush ppf (); close_out oc;
at_exit (fun () -> try Sys.remove file with Sys_error e -> ());
Some file
with Sys_error _ -> None
let tmp_file_for_pager () =
try
let exec = Filename.basename Sys.argv.(0) in
let file = Filename.temp_file exec "tty" in
at_exit (fun () -> try Sys.remove file with Sys_error e -> ());
Some file
with Sys_error _ -> None
let find_cmd cmds =
let test, null = match Sys.os_type with
| "Win32" -> "where", " NUL"
| _ -> "command -v", "/dev/null"
in
let cmd (c, _) = Sys.command (strf "%s %s 1>%s 2>%s" test c null null) = 0 in
try Some (List.find cmd cmds) with Not_found -> None
let pp_to_pager print ppf v =
let pager =
let cmds = ["less", ""; "more", ""] in
let cmds = try (Sys.getenv "PAGER", "") :: cmds with Not_found -> cmds in
let cmds = try (Sys.getenv "MANPAGER", "") :: cmds with Not_found -> cmds in
find_cmd cmds
in
match pager with
| None -> print `Plain ppf v
| Some (pager, opts) ->
let pager = match Sys.win32 with
| false -> "LESS=FRX " ^ pager ^ opts
| true -> "set LESS=FRX && " ^ pager ^ opts
in
let groffer =
let cmds =
["mandoc", " -m man -K utf-8 -T utf8";
"groff", " -m man -K utf8 -T utf8";
"nroff", ""]
in
find_cmd cmds
in
let cmd = match groffer with
| None ->
begin match pp_to_temp_file (print `Plain) v with
| None -> None
| Some f -> Some (strf "%s < %s" pager f)
end
| Some (groffer, opts) ->
let groffer = groffer ^ opts in
begin match pp_to_temp_file (print `Groff) v with
| None -> None
| Some f when Sys.win32 ->
(* For some obscure reason the pipe below does not
work. We need to use a temporary file.
https://github.com/dbuenzli/cmdliner/issues/166 *)
begin match tmp_file_for_pager () with
| None -> None
| Some tmp ->
Some (strf "%s <%s >%s && %s <%s" groffer f tmp pager tmp)
end
| Some f ->
Some (strf "%s < %s | %s" groffer f pager)
end
in
match cmd with
| None -> print `Plain ppf v
| Some cmd -> if (Sys.command cmd) <> 0 then print `Plain ppf v
(* Output *)
type format = [ `Auto | `Pager | `Plain | `Groff ]
let rec print
?(errs = Format.err_formatter)
?(subst = fun x -> None) fmt ppf page =
match fmt with
| `Pager -> pp_to_pager (print ~errs ~subst) ppf page
| `Plain -> pp_plain_page ~errs subst ppf page
| `Groff -> pp_groff_page ~errs subst ppf page
| `Auto ->
match try (Some (Sys.getenv "TERM")) with Not_found -> None with
| None | Some "dumb" -> print ~errs ~subst `Plain ppf page
| Some _ -> print ~errs ~subst `Pager ppf page
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,100 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Manpages.
See {!Cmdliner.Manpage}. *)
type block =
[ `S of string | `P of string | `Pre of string | `I of string * string
| `Noblank | `Blocks of block list ]
val escape : string -> string
(** [escape s] escapes [s] from the doc language. *)
type title = string * int * string * string * string
type t = title * block list
type xref =
[ `Main | `Cmd of string | `Tool of string | `Page of string * int ]
(** {1 Standard section names} *)
val s_name : string
val s_synopsis : string
val s_description : string
val s_commands : string
val s_arguments : string
val s_options : string
val s_common_options : string
val s_exit_status : string
val s_environment : string
val s_files : string
val s_bugs : string
val s_examples : string
val s_authors : string
val s_see_also : string
val s_none : string
(** {1 Section maps}
Used for handling the merging of metadata doc strings. *)
type smap
val smap_of_blocks : block list -> smap
val smap_to_blocks : smap -> block list
val smap_has_section : smap -> sec:string -> bool
val smap_append_block : smap -> sec:string -> block -> smap
(** [smap_append_block smap sec b] appends [b] at the end of section
[sec] creating it at the right place if needed. *)
(** {1 Content boilerplate} *)
val s_exit_status_intro : block
val s_environment_intro : block
(** {1 Output} *)
type format = [ `Auto | `Pager | `Plain | `Groff ]
val print :
?errs:Format.formatter -> ?subst:(string -> string option) -> format ->
Format.formatter -> t -> unit
(** {1 Printers and escapes used by Cmdliner module} *)
val subst_vars :
errs:Format.formatter -> subst:(string -> string option) -> Buffer.t ->
string -> string
(** [subst b ~subst s], using [b], substitutes in [s] variables of the form
"$(doc)" by their [subst] definition. This leaves escapes and markup
directives $(markup,) intact.
@raise Invalid_argument in case of illegal syntax. *)
val doc_to_plain :
errs:Format.formatter -> subst:(string -> string option) -> Buffer.t ->
string -> string
(** [doc_to_plain b ~subst s] using [b], substitutes in [s] variables by
their [subst] definition and renders cmdliner directives to plain
text.
@raise Invalid_argument in case of illegal syntax. *)
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,122 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
let strf = Printf.sprintf
let quote = Cmdliner_base.quote
let pp = Format.fprintf
let pp_text = Cmdliner_base.pp_text
let pp_lines = Cmdliner_base.pp_lines
(* Environment variable errors *)
let err_env_parse env ~err =
let var = Cmdliner_info.Env.info_var env in
strf "environment variable %s: %s" (quote var) err
(* Positional argument errors *)
let err_pos_excess excess =
strf "too many arguments, don't know what to do with %s"
(String.concat ", " (List.map quote excess))
let err_pos_miss a = match Cmdliner_info.Arg.docv a with
| "" -> "a required argument is missing"
| v -> strf "required argument %s is missing" v
let err_pos_misses = function
| [] -> assert false
| [a] -> err_pos_miss a
| args ->
let add_arg acc a = match Cmdliner_info.Arg.docv a with
| "" -> "ARG" :: acc
| argv -> argv :: acc
in
let rev_args = List.sort Cmdliner_info.Arg.rev_pos_cli_order args in
let args = List.fold_left add_arg [] rev_args in
let args = String.concat ", " args in
strf "required arguments %s are missing" args
let err_pos_parse a ~err = match Cmdliner_info.Arg.docv a with
| "" -> err
| argv ->
match Cmdliner_info.Arg.(pos_len @@ pos_kind a) with
| Some 1 -> strf "%s argument: %s" argv err
| None | Some _ -> strf "%s… arguments: %s" argv err
(* Optional argument errors *)
let err_flag_value flag v =
strf "option %s is a flag, it cannot take the argument %s"
(quote flag) (quote v)
let err_opt_value_missing f = strf "option %s needs an argument" (quote f)
let err_opt_parse f ~err = strf "option %s: %s" (quote f) err
let err_opt_repeated f f' =
if f = f' then strf "option %s cannot be repeated" (quote f) else
strf "options %s and %s cannot be present at the same time"
(quote f) (quote f')
(* Argument errors *)
let err_arg_missing a =
if Cmdliner_info.Arg.is_pos a then err_pos_miss a else
strf "required option %s is missing" (Cmdliner_info.Arg.opt_name_sample a)
let err_cmd_missing ~dom =
strf "required COMMAND name is missing, must be %s."
(Cmdliner_base.alts_str ~quoted:true dom)
(* Other messages *)
let exec_name ei = Cmdliner_info.Cmd.name @@ Cmdliner_info.Eval.main ei
let pp_version ppf ei =
match Cmdliner_info.Cmd.version @@ Cmdliner_info.Eval.main ei with
| None -> assert false
| Some v -> pp ppf "@[%a@]@." Cmdliner_base.pp_text v
let pp_try_help ppf ei =
let rcmds = Cmdliner_info.Eval.(cmd ei :: parents ei) in
match List.rev_map Cmdliner_info.Cmd.name rcmds with
| [] -> assert false
| [n] -> pp ppf "@[<2>Try '%s --help' for more information.@]" n
| n :: _ as cmds ->
let cmds = String.concat " " cmds in
pp ppf "@[<2>Try '%s --help' or '%s --help' for more information.@]"
cmds n
let pp_err ppf ei ~err = pp ppf "%s: @[%a@]@." (exec_name ei) pp_lines err
let pp_err_usage ppf ei ~err_lines ~err =
let pp_err = if err_lines then pp_lines else pp_text in
pp ppf "@[<v>%s: @[%a@]@,@[Usage: @[%a@]@]@,%a@]@."
(exec_name ei) pp_err err (Cmdliner_docgen.pp_plain_synopsis ~errs:ppf) ei
pp_try_help ei
let pp_backtrace ppf ei e bt =
let bt = Printexc.raw_backtrace_to_string bt in
let bt =
let len = String.length bt in
if len > 0 then String.sub bt 0 (len - 1) (* remove final '\n' *) else bt
in
pp ppf "%s: @[internal error, uncaught exception:@\n%a@]@."
(exec_name ei) pp_lines (strf "%s\n%s" (Printexc.to_string e) bt)
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,56 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Messages for the end-user. *)
(** {1:env_err Environment variable errors} *)
val err_env_parse : Cmdliner_info.Env.info -> err:string -> string
(** {1:pos_err Positional argument errors} *)
val err_pos_excess : string list -> string
val err_pos_misses : Cmdliner_info.Arg.t list -> string
val err_pos_parse : Cmdliner_info.Arg.t -> err:string -> string
(** {1:opt_err Optional argument errors} *)
val err_flag_value : string -> string -> string
val err_opt_value_missing : string -> string
val err_opt_parse : string -> err:string -> string
val err_opt_repeated : string -> string -> string
(** {1:arg_err Argument errors} *)
val err_arg_missing : Cmdliner_info.Arg.t -> string
val err_cmd_missing : dom:string list -> string
(** {1:msgs Other messages} *)
val pp_version : Format.formatter -> Cmdliner_info.Eval.t -> unit
val pp_try_help : Format.formatter -> Cmdliner_info.Eval.t -> unit
val pp_err : Format.formatter -> Cmdliner_info.Eval.t -> err:string -> unit
val pp_err_usage :
Format.formatter -> Cmdliner_info.Eval.t -> err_lines:bool -> err:string -> unit
val pp_backtrace :
Format.formatter ->
Cmdliner_info.Eval.t -> exn -> Printexc.raw_backtrace -> unit
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,98 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
type term_escape =
[ `Error of bool * string
| `Help of Cmdliner_manpage.format * string option ]
type 'a parser =
Cmdliner_info.Eval.t -> Cmdliner_cline.t ->
('a, [ `Parse of string | term_escape ]) result
type 'a t = Cmdliner_info.Arg.Set.t * 'a parser
let const v = Cmdliner_info.Arg.Set.empty, (fun _ _ -> Ok v)
let app (args_f, f) (args_v, v) =
Cmdliner_info.Arg.Set.union args_f args_v,
fun ei cl -> match (f ei cl) with
| Error _ as e -> e
| Ok f ->
match v ei cl with
| Error _ as e -> e
| Ok v -> Ok (f v)
(* Terms *)
let ( $ ) = app
type 'a ret = [ `Ok of 'a | term_escape ]
let ret (al, v) =
al, fun ei cl -> match v ei cl with
| Ok (`Ok v) -> Ok v
| Ok (`Error _ as err) -> Error err
| Ok (`Help _ as help) -> Error help
| Error _ as e -> e
let term_result ?(usage = false) (al, v) =
al, fun ei cl -> match v ei cl with
| Ok (Ok _ as ok) -> ok
| Ok (Error (`Msg e)) -> Error (`Error (usage, e))
| Error _ as e -> e
let term_result' ?usage t =
let wrap = app (const (Result.map_error (fun e -> `Msg e))) t in
term_result ?usage wrap
let cli_parse_result (al, v) =
al, fun ei cl -> match v ei cl with
| Ok (Ok _ as ok) -> ok
| Ok (Error (`Msg e)) -> Error (`Parse e)
| Error _ as e -> e
let cli_parse_result' t =
let wrap = app (const (Result.map_error (fun e -> `Msg e))) t in
cli_parse_result wrap
let main_name =
Cmdliner_info.Arg.Set.empty,
(fun ei _ -> Ok (Cmdliner_info.Cmd.name @@ Cmdliner_info.Eval.main ei))
let choice_names =
Cmdliner_info.Arg.Set.empty,
(fun ei _ ->
(* N.B. this keeps everything backward compatible. We return the command
names of main's children *)
let name t = Cmdliner_info.Cmd.name t in
let choices = Cmdliner_info.Cmd.children (Cmdliner_info.Eval.main ei) in
Ok (List.rev_map name choices))
let with_used_args (al, v) : (_ * string list) t =
al, fun ei cl ->
match v ei cl with
| Ok x ->
let actual_args arg_info acc =
let args = Cmdliner_cline.actual_args cl arg_info in
List.rev_append args acc
in
let used = List.rev (Cmdliner_info.Arg.Set.fold actual_args al []) in
Ok (x, used)
| Error _ as e -> e
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,51 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Terms *)
type term_escape =
[ `Error of bool * string
| `Help of Cmdliner_manpage.format * string option ]
type 'a parser =
Cmdliner_info.Eval.t -> Cmdliner_cline.t ->
('a, [ `Parse of string | term_escape ]) result
(** Type type for command line parser. given static information about
the command line and a command line to parse returns an OCaml value. *)
type 'a t = Cmdliner_info.Arg.Set.t * 'a parser
(** The type for terms. The list of arguments it can parse and the parsing
function that does so. *)
val const : 'a -> 'a t
val app : ('a -> 'b) t -> 'a t -> 'b t
val ( $ ) : ('a -> 'b) t -> 'a t -> 'b t
type 'a ret = [ `Ok of 'a | term_escape ]
val ret : 'a ret t -> 'a t
val term_result : ?usage:bool -> ('a, [`Msg of string]) result t -> 'a t
val term_result' : ?usage:bool -> ('a, string) result t -> 'a t
val cli_parse_result : ('a, [`Msg of string]) result t -> 'a t
val cli_parse_result' : ('a, string) result t -> 'a t
val main_name : string t
val choice_names : string list t
val with_used_args : 'a t -> ('a * string list) t
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,93 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(* Term combinators *)
let man_format = Cmdliner_arg.man_format
let pure = Cmdliner_term.const
(* Term information *)
type exit_info = Cmdliner_info.Exit.info
let exit_info = Cmdliner_info.Exit.info
let exit_status_success = Cmdliner_info.Exit.ok
let exit_status_cli_error = Cmdliner_info.Exit.cli_error
let exit_status_internal_error = Cmdliner_info.Exit.internal_error
let default_error_exits =
[ exit_info exit_status_cli_error ~doc:"on command line parsing errors.";
exit_info exit_status_internal_error
~doc:"on unexpected internal errors (bugs)."; ]
let default_exits =
(exit_info exit_status_success ~doc:"on success.") :: default_error_exits
type env_info = Cmdliner_info.Env.info
let env_info = Cmdliner_info.Env.info ?deprecated:None
type info = Cmdliner_info.Cmd.t
let info
?(man_xrefs = []) ?man ?envs ?(exits = [])
?(sdocs = Cmdliner_manpage.s_options) ?docs ?doc ?version name
=
Cmdliner_info.Cmd.v
~man_xrefs ?man ?envs ~exits ~sdocs ?docs ?doc ?version name
let name ti = Cmdliner_info.Cmd.name ti
(* Evaluation *)
type 'a result =
[ `Ok of 'a | `Error of [`Parse | `Term | `Exn ] | `Version | `Help ]
let to_legacy_result = function
| Ok (#Cmdliner_eval.eval_ok as r) -> (r : 'a result)
| Error e -> `Error e
let eval ?help ?err ?catch ?env ?argv (t, i) =
let cmd = Cmdliner_cmd.v i t in
to_legacy_result (Cmdliner_eval.eval_value ?help ?err ?catch ?env ?argv cmd)
let eval_choice ?help ?err ?catch ?env ?argv (t, i) choices =
let sub (t, i) = Cmdliner_cmd.v i t in
let cmd = Cmdliner_cmd.group i ~default:t (List.map sub choices) in
to_legacy_result (Cmdliner_eval.eval_value ?help ?err ?catch ?env ?argv cmd)
let eval_peek_opts ?version_opt ?env ?argv t =
let o, r = Cmdliner_eval.eval_peek_opts ?version_opt ?env ?argv t in
o, to_legacy_result r
(* Exits *)
let exit_status_of_result ?(term_err = 1) = function
| `Ok () | `Help | `Version -> exit_status_success
| `Error `Term -> term_err
| `Error `Exn -> exit_status_internal_error
| `Error `Parse -> exit_status_cli_error
let exit_status_of_status_result ?term_err = function
| `Ok n -> n
| `Help | `Version | `Error _ as r -> exit_status_of_result ?term_err r
let stdlib_exit = exit
let exit ?term_err r = stdlib_exit (exit_status_of_result ?term_err r)
let exit_status ?term_err r =
stdlib_exit (exit_status_of_status_result ?term_err r)
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,96 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
module Cmap = Map.Make (Char) (* character maps. *)
type 'a value = (* type for holding a bound value. *)
| Pre of 'a (* value is bound by the prefix of a key. *)
| Key of 'a (* value is bound by an entire key. *)
| Amb (* no value bound because of ambiguous prefix. *)
| Nil (* not bound (only for the empty trie). *)
type 'a t = { v : 'a value; succs : 'a t Cmap.t }
let empty = { v = Nil; succs = Cmap.empty }
let is_empty t = t = empty
(* N.B. If we replace a non-ambiguous key, it becomes ambiguous but it's
not important for our use. Also the following is not tail recursive but
the stack is bounded by key length. *)
let add t k d =
let rec loop t k len i d pre_d = match i = len with
| true ->
let t' = { v = Key d; succs = t.succs } in
begin match t.v with
| Key old -> `Replaced (old, t')
| _ -> `New t'
end
| false ->
let v = match t.v with
| Amb | Pre _ -> Amb | Key _ as v -> v | Nil -> pre_d
in
let t' = try Cmap.find k.[i] t.succs with Not_found -> empty in
match loop t' k len (i + 1) d pre_d with
| `New n -> `New { v; succs = Cmap.add k.[i] n t.succs }
| `Replaced (o, n) ->
`Replaced (o, { v; succs = Cmap.add k.[i] n t.succs })
in
loop t k (String.length k) 0 d (Pre d (* allocate less *))
let find_node t k =
let rec aux t k len i =
if i = len then t else
aux (Cmap.find k.[i] t.succs) k len (i + 1)
in
aux t k (String.length k) 0
let find t k =
try match (find_node t k).v with
| Key v | Pre v -> `Ok v | Amb -> `Ambiguous | Nil -> `Not_found
with Not_found -> `Not_found
let ambiguities t p = (* ambiguities of [p] in [t]. *)
try
let t = find_node t p in
match t.v with
| Key _ | Pre _ | Nil -> []
| Amb ->
let add_char s c = s ^ (String.make 1 c) in
let rem_char s = String.sub s 0 ((String.length s) - 1) in
let to_list m = Cmap.fold (fun k t acc -> (k,t) :: acc) m [] in
let rec aux acc p = function
| ((c, t) :: succs) :: rest ->
let p' = add_char p c in
let acc' = match t.v with
| Pre _ | Amb -> acc
| Key _ -> (p' :: acc)
| Nil -> assert false
in
aux acc' p' ((to_list t.succs) :: succs :: rest)
| [] :: [] -> acc
| [] :: rest -> aux acc (rem_char p) rest
| [] -> assert false
in
aux [] p (to_list t.succs :: [])
with Not_found -> []
let of_list l =
let add t (s, v) = match add t s v with `New t -> t | `Replaced (_, t) -> t in
List.fold_left add empty l
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,34 @@
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)
(** Tries.
This implementation also maps any non ambiguous prefix of a
key to its value. *)
type 'a t
val empty : 'a t
val is_empty : 'a t -> bool
val add : 'a t -> string -> 'a -> [ `New of 'a t | `Replaced of 'a * 'a t ]
val find : 'a t -> string -> [ `Ok of 'a | `Ambiguous | `Not_found ]
val ambiguities : 'a t -> string -> string list
val of_list : (string * 'a) list -> 'a t
(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)

View file

@ -0,0 +1,4 @@
(library
(name cmdliner)
(wrapped false)
(flags (-w -3-6-27-32-33-35-50)))