123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203(*---------------------------------------------------------------------------
Copyright (c) 2011 The cmdliner programmers. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)(* 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). *)leterr_multi_opt_name_defnameaa'=Cmdliner_base.err_multi_def~kind:"option name"nameCmdliner_info.Arg.docaa'moduleAmap=Map.Make(Cmdliner_info.Arg)typearg=(* unconverted argument data as found on the command line. *)|Oof(int*string*(stringoption))list(* (pos, name, value) of opt. *)|Pofstringlisttypet=argAmap.t(* command line, maps arg_infos to arg value. *)letget_argcla=tryAmap.findaclwithNot_found->assertfalseletopt_argcla=matchget_argclawithOl->l|_->assertfalseletpos_argcla=matchget_argclawithPl->l|_->assertfalseletactual_argscla=matchget_argclawith|Pargs->args|Ol->letextract_args(_pos,name,value)=name::(matchvaluewithNone->[]|Somev->[v])inList.concat(List.mapextract_argsl)letarg_info_indexesargs=(* 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]. *)letrecloopoptidxposidxcl=function|[]->optidx,posidx,cl|a::l->matchCmdliner_info.Arg.is_posawith|true->loopoptidx(a::posidx)(Amap.adda(P[])cl)l|false->letaddtname=matchCmdliner_trie.addtnameawith|`Newt->t|`Replaced(a',_)->invalid_arg(err_multi_opt_name_defnameaa')inletnames=Cmdliner_info.Arg.opt_namesainletoptidx=List.fold_leftaddoptidxnamesinloopoptidxposidx(Amap.adda(O[])cl)linloopCmdliner_trie.empty[]Amap.empty(Cmdliner_info.Arg.Set.elementsargs)(* Optional argument parsing *)letis_opts=String.lengths>1&&s.[0]='-'letis_short_opts=String.lengths=2&&s.[0]='-'letparse_opt_args=(* (name, value) of opt arg, assert len > 1. *)letl=String.lengthsinifs.[1]<>'-'then(* short opt *)ifl=2thens,NoneelseString.subs02,Some(String.subs2(l-2))(* with glued opt arg *)elsetry(* long opt *)leti=String.indexs'='inString.subs0i,Some(String.subs(i+1)(l-i-1))withNot_found->s,Nonelethint_matching_optoptidxs=(* hint options that could match [s] in [optidx]. FIXME explain this is
a bit obscure. *)ifString.lengths<=2then[]elseletshort_opt,long_opt=ifs.[1]<>'-'thens,Printf.sprintf"-%s"selseString.subs1(String.lengths-1),sinletshort_opt,_=parse_opt_argshort_optinletlong_opt,_=parse_opt_arglong_optinletall=Cmdliner_trie.ambiguitiesoptidx"-"inmatchList.memshort_optall,Cmdliner_base.suggestlong_optallwith|false,[]->[]|false,l->l|true,[]->[short_opt]|true,l->ifList.memshort_optlthenlelseshort_opt::lletparse_opt_args~peek_optsoptidxclargs=(* 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. *)letreclooperrskclpargs=function|[]->List.reverrs,cl,List.revpargs|"--"::args->List.reverrs,cl,(List.rev_appendpargsargs)|s::args->ifnot(is_opts)thenlooperrs(k+1)cl(s::pargs)argselseletname,value=parse_opt_argsinmatchCmdliner_trie.findoptidxnamewith|`Oka->letvalue,args=matchvalue,Cmdliner_info.Arg.opt_kindawith|Somev,Cmdliner_info.Arg.Flagwhenis_short_optname->None,("-"^v)::args|Some_,_->value,args|None,Cmdliner_info.Arg.Flag->value,args|None,_->matchargswith|[]->None,args|v::rest->ifis_optvthenNone,argselseSomev,restinletarg=O((k,name,value)::opt_argcla)inlooperrs(k+1)(Amap.addaargcl)pargsargs|`Not_foundwhenpeek_opts->looperrs(k+1)clpargsargs|`Not_found->lethints=hint_matching_optoptidxsinleterr=Cmdliner_base.err_unknown~kind:"option"~hintsnameinloop(err::errs)(k+1)clpargsargs|`Ambiguous->letambs=Cmdliner_trie.ambiguitiesoptidxnameinletambs=List.sortcompareambsinleterr=Cmdliner_base.err_ambiguous~kind:"option"name~ambsinloop(err::errs)(k+1)clpargsargsinleterrs,cl,pargs=loop[]0cl[]argsiniferrs=[]thenOk(cl,pargs)elseleterr=String.concat"\n"errsinError(err,cl,pargs)lettake_rangestartstopl=letrecloopiacc=function|[]->List.revacc|v::vs->ifi<startthenloop(i+1)accvselseifi<=stopthenloop(i+1)(v::acc)vselseList.revaccinloop0[]lletprocess_pos_argsposidxclpargs=(* 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]. *)ifpargs=[]thenletmisses=List.filterCmdliner_info.Arg.is_reqposidxinifmisses=[]thenOkclelseError(Cmdliner_msg.err_pos_missesmisses,cl)elseletlast=List.lengthpargs-1inletposrevk=ifrevthenlast-kelsekinletrecloopmissesclmax_spec=function|[]->misses,cl,max_spec|a::al->letapos=Cmdliner_info.Arg.pos_kindainletrev=Cmdliner_info.Arg.pos_revaposinletstart=posrev(Cmdliner_info.Arg.pos_startapos)inletstop=matchCmdliner_info.Arg.pos_lenaposwith|None->posrevlast|Somen->posrev(Cmdliner_info.Arg.pos_startapos+n-1)inletstart,stop=ifrevthenstop,startelsestart,stopinletargs=take_rangestartstoppargsinletmax_spec=maxstopmax_specinletcl=Amap.adda(Pargs)clinletmisses=matchCmdliner_info.Arg.is_reqa&&args=[]with|true->a::misses|false->missesinloopmissesclmax_specalinletmisses,cl,max_spec=loop[]cl(-1)posidxinifmisses<>[]thenError(Cmdliner_msg.err_pos_missesmisses,cl)elseiflast<=max_specthenOkclelseletexcess=take_range(max_spec+1)lastpargsinError(Cmdliner_msg.err_pos_excessexcess,cl)letcreate?(peek_opts=false)alargs=letoptidx,posidx,cl=arg_info_indexesalinmatchparse_opt_args~peek_optsoptidxclargswith|Ok(cl,_)whenpeek_opts->Okcl|Ok(cl,pargs)->process_pos_argsposidxclpargs|Error(errs,cl,_)->Error(errs,cl)letdeprecated_msgscl=letaddiargacc=matchCmdliner_info.Arg.deprecatediwith|None->acc|Somemsg->letplurall=ifList.lengthl>1then"s "else" "inmatchargwith|O[]|P[]->acc(* Should not happen *)|Oos->letplural=pluralosinletnames=List.map(fun(_,n,_)->n)osinletnames=String.concat" "(List.mapCmdliner_base.quotenames)inletmsg="option"::plural::names::": "::msg::[]inString.concat""msg::acc|Pargs->letplural=pluralargsinletargs=String.concat" "(List.mapCmdliner_base.quoteargs)inletmsg="argument"::plural::args::": "::msg::[]inString.concat""msg::accinAmap.foldaddcl[]