12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451(*---------------------------------------------------------------------------
Copyright (c) 2011 Daniel C. Bünzli. All rights reserved.
Distributed under a BSD3 license, see license at the end of the file.
cmdliner release 0.9.8
---------------------------------------------------------------------------*)letstr=Printf.sprintf(* Invalid_arg strings *)leterr_argv="argv array must have at least one element"leterr_not_opt="Option argument without name"leterr_not_pos="Positional argument with a name"leterr_helps="Term error, help requested for unknown command "^sleterr_empty_list="Empty list"leterr_incomplete_enum="Incomplete enumeration for the type"leterr_doc_strings=str"Variable substitution failed on documentation fragment `%s'"s(* A few useful definitions. *)letrev_comparenn'=comparen'nletpr=Format.fprintfletpr_str=Format.pp_print_stringletpr_char=Format.pp_print_charletstr_of_ppppv=ppFormat.str_formatterv;Format.flush_str_formatter()letquotes=str"`%s'"sletalts_str?(quoted=true)alts=letquote=ifquotedthenquoteelse(funs->s)inmatchaltswith|[]->invalid_argerr_empty_list|[a]->(quotea)|[a;b]->str"either %s or %s"(quotea)(quoteb)|alts->letrev_alts=List.revaltsinstr"one of %s or %s"(String.concat", "(List.rev_mapquote(List.tlrev_alts)))(quote(List.hdrev_alts))letpr_white_strspacesppfs=(* spaces and new lines with Format's funs *)letleft=ref0andright=ref0andlen=String.lengthsinletflush()=Format.pp_print_stringppf(String.subs!left(!right-!left));incrright;left:=!right;inwhile(!right<>len)doifs.[!right]='\n'then(flush();Format.pp_force_newlineppf())elseifspaces&&s.[!right]=' 'then(flush();Format.pp_print_spaceppf())elseincrright;done;if!left<>lenthenflush()letpr_text=pr_white_strtrueletpr_lines=pr_white_strfalseletpr_to_temp_fileprv=tryletexec=Filename.basenameSys.argv.(0)inletfile,oc=Filename.open_temp_fileexec"out"inletppf=Format.formatter_of_out_channelocinprppfv;Format.pp_print_flushppf();close_outoc;at_exit(fun()->trySys.removefilewithSys_errore->());SomefilewithSys_error_->None(* Levenshtein distance, for making spelling suggestions in case of error. *)letlevenshtein_distancest=(* As found here http://rosettacode.org/wiki/Levenshtein_distance#OCaml *)letminimumabc=mina(minbc)inletm=String.lengthsinletn=String.lengthtin(* for all i and j, d.(i).(j) will hold the Levenshtein distance between
the first i characters of s and the first j characters of t *)letd=Array.make_matrix(m+1)(n+1)0infori=0tomdod.(i).(0)<-idone;forj=0tondod.(0).(j)<-jdone;forj=1tondofori=1tomdoifs.[i-1]=t.[j-1]thend.(i).(j)<-d.(i-1).(j-1)(* no operation required *)elsed.(i).(j)<-minimum(d.(i-1).(j)+1)(* a deletion *)(d.(i).(j-1)+1)(* an insertion *)(d.(i-1).(j-1)+1)(* a substitution *)done;done;d.(m).(n)letsuggestscandidates=letadd(min,acc)name=letd=levenshtein_distancesnameinifd=minthenmin,(name::acc)elseifd<minthend,[name]elsemin,accinletdist,suggs=List.fold_leftadd(max_int,[])candidatesinifdist<3(* suggest only if not too far *)thensuggselse[](* Tries. This implementation also maps any non ambiguous prefix of a
key to its value. *)moduleTrie:sigtype'atvalempty:'atvalis_empty:'at->boolvaladd:'at->string->'a->'atvalfind:'at->string->[`Okof'a|`Ambiguous|`Not_found]valambiguities:'at->string->stringlistvalof_list:(string*'a)list->'atend=structmoduleCmap=Map.Make(Char)(* character maps. *)type'avalue=(* type for holding a bound value. *)|Preof'a(* value is bound by the prefix of a key. *)|Keyof'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'at={v:'avalue;succs:'atCmap.t}letempty={v=Nil;succs=Cmap.empty}letis_emptyt=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. *)letaddtkd=letrecauxtklenidpre_d=ifi=lenthen{v=Keyd;succs=t.succs}elseletv=matcht.vwith|Amb|Pre_->Amb|Key_asv->v|Nil->pre_dinletsuccs=lett'=tryCmap.findk.[i]t.succswithNot_found->emptyinCmap.addk.[i](auxt'klen(i+1)dpre_d)t.succsin{v;succs}inauxtk(String.lengthk)0d(Pred(* allocate less *))letfind_nodetk=letrecauxtkleni=ifi=lenthentelseaux(Cmap.findk.[i]t.succs)klen(i+1)inauxtk(String.lengthk)0letfindtk=trymatch(find_nodetk).vwith|Keyv|Prev->`Okv|Amb->`Ambiguous|Nil->`Not_foundwithNot_found->`Not_foundletambiguitiestp=(* ambiguities of [p] in [t]. *)trylett=find_nodetpinmatcht.vwith|Key_|Pre_|Nil->[]|Amb->letadd_charsc=s^(String.make1c)inletrem_chars=String.subs0((String.lengths)-1)inletto_listm=Cmap.fold(funktacc->(k,t)::acc)m[]inletrecauxaccp=function|((c,t)::succs)::rest->letp'=add_charpcinletacc'=matcht.vwith|Pre_|Amb->acc|Key_->(p'::acc)|Nil->assertfalseinauxacc'p'((to_listt.succs)::succs::rest)|[]::[]->acc|[]::rest->auxacc(rem_charp)rest|[]->assertfalseinaux[]p(to_listt.succs::[])withNot_found->[]letof_listl=List.fold_left(funt(s,v)->addtsv)emptylend(* The following types keep untyped information about arguments and
terms. This data is used to parse the command line, report errors
and format man page information. *)typeenv_info=(* information about an environment variable. *){env_var:string;(* the variable. *)env_doc:string;(* help. *)env_docs:string;}(* title of help section where listed. *)typeabsence=(* what happens if the argument is absent from the cl. *)|Error(* an error is reported. *)|ValofstringLazy.t(* if <> "", takes the given default value. *)typeopt_kind=(* kinds of optional arguments. *)|Flag(* just a flag, without value. *)|Opt(* value is required. *)|Opt_voptofstring(* option value is optional, takes given default. *)typepos_kind=(* kinds of positional arguments. *)|All(* all positional arguments. *)|Nthofbool*int(* specific position. *)|Leftofbool*int(* all args on the left of a position. *)|Rightofbool*int(* all args on the right of a position. *)typearg_info=(* information about a command line argument. *){id:int;(* unique id for the argument. *)absent:absence;(* behaviour if absent. *)env_info:env_infooption;(* environment variable. *)doc:string;(* help. *)docv:string;(* variable name for the argument in help. *)docs:string;(* title of help section where listed. *)p_kind:pos_kind;(* positional arg kind. *)o_kind:opt_kind;(* optional arg kind. *)o_names:stringlist;(* names (for opt args). *)o_all:bool;}(* repeatable (for opt args). *)letarg_id=(* thread-safe UIDs, Oo.id (object end) was used before. *)letc=ref0infun()->letid=!cinincrc;ifid>!cthenassertfalse(* too many ids *)elseidletis_opta=a.o_names<>[]letis_posa=a.o_names=[]moduleAmap=Map.Make(* arg info maps. *)(structtypet=arg_infoletcompareaa'=comparea.ida'.idend)typearg=(* unconverted argument data as found on the command line. *)|Oof(int*string*(stringoption))list(* (pos, name, value) of opt. *)|Pofstringlisttypecmdline=argAmap.t(* command line, maps arg_infos to arg value. *)typeman_block=[(* block of manpage text. *)|`Sofstring|`Pofstring|`Preofstring|`Iofstring*string|`Noblank]typeterm_info={name:string;(* name of the term. *)version:stringoption;(* version (for --version). *)tdoc:string;(* one line description of term. *)tdocs:string;(* title of man section where listed (commands). *)sdocs:string;(* standard options, title of section where listed. *)man:man_blocklist;}(* man page text. *)typeeval_info=(* informatin about the evaluation context. *){term:term_info*arg_infolist;(* term being evaluated. *)main:term_info*arg_infolist;(* main term. *)choices:(term_info*arg_infolist)list;(* all term choices. *)env:string->stringoption}(* environment variable lookup. *)leteval_kindei=(* evaluation with multiple terms ? *)ifei.choices=[]then`Simpleelseif(fstei.term)==(fstei.main)then`M_mainelse`M_choicemoduleManpage=structtypetitle=string*int*string*string*stringtypeblock=man_blocktypet=title*blocklistletp_indent=7(* paragraph indentation. *)letl_indent=4(* label indentation. *)letescapesubstescbufs=letsubsts=letlen=String.lengthsinifnot(len>1&&s.[1]=',')then(substs)elseiflen=2then""elseescs.[0](String.subs2(len-2))intryBuffer.clearbuf;Buffer.add_substitutebufsubsts;lets=Buffer.contentsbufin(* twice for $(i,$(mname)). *)Buffer.clearbuf;Buffer.add_substitutebufsubsts;Buffer.contentsbufwithNot_found->invalid_arg(err_doc_strings)letpr_tokens?(groff=false)ppfs=letis_space=function' '|'\n'|'\r'|'\t'->true|_->falseinletlen=String.lengthsinleti=ref0intrywhile(true)dowhile(!i<len&&is_spaces.[!i])doincridone;letstart=!iinifstart=lenthenraiseExit;while(!i<len&¬(is_spaces.[!i])&¬(s.[!i]='-'))doincridone;pr_strppf(String.subsstart(!i-start));if!i=lenthenraiseExit;ifs.[!i]='-'then(incri;ifgroffthenpr_strppf"\\-"elsepr_charppf'-');if(!i<len&&is_spaces.[!i])then(ifgroffthenpr_charppf' 'elseFormat.pp_print_spaceppf())donewithExit->()(* Plain text output *)letplain_esccs=matchcwith'g'->""(* groff specific *)|_->sletpr_indentppfc=fori=1tocdopr_charppf' 'doneletpr_plain_blockssubstppfts=letbuf=Buffer.create1024inletescapet=escapesubstplain_escbuftinletpr_tokensppft=pr_tokensppf(escapet)inletrecaux=function|[]->()|t::ts->beginmatchtwith|`Noblank->()|`Ps->prppf"%a@[%a@]@,"pr_indentp_indentpr_tokenss|`Ss->prppf"@[%a@]"pr_tokenss|`Pres->prppf"%a@[%a@]@,"pr_indentp_indentpr_lines(escapes)|`I(label,s)->letlabel=escapelabelinletll=String.lengthlabelinprppf"@[%a@[%a@]"pr_indentp_indentpr_tokenslabel;ifs=""then()elseifll<l_indentthenprppf"%a@[%a@]@]@,"pr_indent(l_indent-ll)pr_tokensselseprppf"@\n%a@[%a@]@]@,"pr_indent(p_indent+l_indent)pr_tokenssend;beginmatchtswith|`Noblank::ts->auxts|ts->Format.pp_print_cutppf();auxtsendinauxtsletpr_plain_pagesubstppf(_,text)=prppf"@[<v>%a@]"(pr_plain_blockssubst)text(* Groff output *)letgroff_esccs=matchcwith|'i'->(str"\\fI%s\\fR"s)|'b'->(str"\\fB%s\\fR"s)|'p'->""(* plain text specific *)|_->sletpr_groff_linesppfs=letleft=ref0andright=ref0andlen=String.lengthsinletflush()=Format.pp_print_stringppf(String.subs!left(!right-!left));incrright;left:=!right;inwhile(!right<>len)doifs.[!right]='\n'then(flush();Format.pp_force_newlineppf())elseifs.[!right]='-'then(flush();pr_strppf"\\-")elseincrright;done;if!left<>lenthenflush()letpr_groff_blockssubstppftext=letbuf=Buffer.create1024inletescapet=escapesubstgroff_escbuftinletpr_tokensppft=pr_tokens~groff:trueppf(escapet)inletpr_block=function|`Ps->prppf"@\n.P@\n%a"pr_tokenss|`Pres->prppf"@\n.P@\n.nf@\n%a@\n.fi"pr_groff_lines(escapes)|`Ss->prppf"@\n.SH %a"pr_tokenss|`Noblank->prppf"@\n.sp -1"|`I(l,s)->prppf"@\n.TP 4@\n%a@\n%a"pr_tokenslpr_tokenssinList.iterpr_blocktextletpr_groff_pagesubstppf((n,s,a1,a2,a3),t)=prppf".\\\" Pipe this output to groff -man -Tutf8 | less@\n\
.\\\"@\n\
.TH \"%s\" %d \"%s\" \"%s\" \"%s\"@\n\
.\\\" Disable hyphenation and ragged-right@\n\
.nh@\n\
.ad l\
%a@?"nsa1a2a3(pr_groff_blockssubst)t(* Printing to a pager *)letfind_cmdcmds=lettest,null=matchSys.os_typewith|"Win32"->"where"," NUL"|_->"type","/dev/null"inletcmdc=Sys.command(str"%s %s 1>%s 2>%s"testcnullnull)=0intrySome(List.findcmdcmds)withNot_found->Noneletpr_to_pagerprintppfv=letpager=letcmds=["less";"more"]inletcmds=try(Sys.getenv"PAGER")::cmdswithNot_found->cmdsinletcmds=try(Sys.getenv"MANPAGER")::cmdswithNot_found->cmdsinfind_cmdcmdsinmatchpagerwith|None->print`Plainppfv|Somepager->letcmd=match(find_cmd["groff";"nroff"])with|None->beginmatchpr_to_temp_file(print`Plain)vwith|None->None|Somef->Some(str"%s < %s"pagerf)end|Somec->beginmatchpr_to_temp_file(print`Groff)vwith|None->None|Somef->(* TODO use -Tutf8, but annoyingly maps U+002D to U+2212. *)letxroff=ifc="groff"thenc^" -Tascii -P-c"elsecinSome(str"%s -man < %s | %s"xrofffpager)endinmatchcmdwith|None->print`Plainppfv|Somecmd->if(Sys.commandcmd)<>0thenprint`Plainppfvletrecprint?(subst=funx->x)fmtppfpage=matchfmtwith|`Pager->pr_to_pager(print~subst)ppfpage|`Plain->pr_plain_pagesubstppfpage|`Groff->pr_groff_pagesubstppfpageendmoduleHelp=structletinvocation?(sep=' ')ei=matcheval_kindeiwith|`Simple|`M_main->(fstei.main).name|`M_choice->str"%s%c%s"(fstei.main).namesep(fstei.term).namelettitleei=letprog=String.capitalize(fstei.main).nameinletname=String.uppercase(invocation~sep:'-'ei)inletleft_footer=prog^match(fstei.main).versionwith|None->""|Somev->str" %s"vinletcenter_header=str"%s Manual"proginname,1,"",left_footer,center_headerletname_sectionei=lettdocd=ifd=""then""else(str" - %s"d)in[`S"NAME";`P(str"%s%s"(invocation~sep:'-'ei)(tdoc(fstei.term).tdoc));]letsynopsisei=matcheval_kindeiwith|`M_main->str"$(b,%s) $(i,COMMAND) ..."(invocationei)|`Simple|`M_choice->letrev_cmp(p,_)(p',_)=matchp',pwith(* best effort. *)|p,All->-1|All,p->1|Left_,Right_->-1|Right_,Left_->1|Left(false,k),Nth(false,k')|Nth(false,k),Nth(false,k')|Nth(false,k),Right(false,k')->ifk<=k'then-1else1|Nth(false,k),Left(false,k')|Right(false,k),Nth(false,k')->ifk>=k'then1else-1|Left(true,k),Nth(true,k')|Nth(true,k),Nth(true,k')|Nth(true,k),Right(true,k')->ifk>=k'then-1else1|Nth(true,k),Left(true,k')|Right(true,k),Nth(true,k')->ifk<=k'then1else-1|p,p'->comparepp'inletrecformat_posacc=function|a::al->ifis_optathenformat_posaccalelseletv=ifa.docv=""then"$(i,ARG)"elsestr"$(i,%s)"a.docvinletv=ifa.absent=Errorthenstr"%s"velsestr"[%s]"vinletv=v^matcha.p_kindwithNth_->""|_->"..."informat_pos((a.p_kind,v)::acc)al|[]->accinletargs=List.sortrev_cmp(format_pos[](sndei.term))inletargs=String.concat" "(List.rev_mapsndargs)instr"$(b,%s) [$(i,OPTION)]... %s"(invocationei)argsletget_synopsis_sectionei=letrecextract_synopsissyn=function|`S_::_asman->List.revsyn,man|block::rest->extract_synopsis(block::syn)rest|[]->List.revsyn,[]inmatch(fstei.term).manwith|`S"SYNOPSIS"ass::rest->extract_synopsis[s]rest(* user-defined *)|man->[`S"SYNOPSIS";`P(synopsisei);],man(* automatic *)letor_enva=matcha.env_infowith|None->""|Somev->str" or $(i,%s) env"v.env_varletmake_arg_labela=ifis_posathenstr"$(i,%s)"a.docvelseletfmt_namevar=matcha.o_kindwith|Flag->funn->str"$(b,%s)%s"n(or_enva)|Opt->funn->ifString.lengthn>2thenstr"$(b,%s)=$(i,%s)"nvarelsestr"$(b,%s) $(i,%s)"nvar|Opt_vopt_->funn->ifString.lengthn>2thenstr"$(b,%s)[=$(i,%s)]"nvarelsestr"$(b,%s) [$(i,%s)]"nvarinletvar=ifa.docv=""then"VAL"elsea.docvinletnames=List.sortcomparea.o_namesinlets=String.concat", "(List.rev_map(fmt_namevar)names)insletarg_info_substs~bufadoc=letsubst=function|"docv"->str"$(i,%s)"a.docv|"opt"whenis_opta->letk=String.lowercase(List.hd(List.sortcomparea.o_names))instr"$(b,%s)"k|"env"whena.env_info<>None->beginmatcha.env_infowith|None->assertfalse|Somev->str"$(i,%s)"v.env_varend|s->str"$(%s)"sintryBuffer.clearbuf;Buffer.add_substitutebufsubstdoc;Buffer.contentsbufwithNot_found->invalid_arg(err_doc_stringdoc)letmake_arg_itemsei=letbuf=Buffer.create200inletcmpaa'=letc=comparea.docsa'.docsinifc<>0thencelsematchis_opta,is_opta'with|true,true->letkeynames=letk=String.lowercase(List.hd(List.sortrev_comparenames))inifk.[1]='-'thenString.subk1(String.lengthk-1)elsekincompare(keya.o_names)(keya'.o_names)|false,false->compare(String.lowercasea.docv)(String.lowercasea'.docv)|true,false->-1|false,true->1inletformata=letabsent=matcha.absentwith|Error->""|Valv->matchLazy.forcevwith|""->""|v->str"absent=%s%s"v(or_enva)inletoptvopt=matcha.o_kindwith|Opt_voptv->str"default=%s"v|_->""inletargvdoc=matchoptvopt,absentwith|"",""->""|s,""|"",s->str" (%s)"s|s,s'->str" (%s) (%s)"ss'in(a.docs,`I(make_arg_labela^argvdoc,(arg_info_substs~bufaa.doc)))inletis_arg_itema=not(is_posa&&(a.docv=""||a.doc=""))inletl=List.sortcmp(List.filteris_arg_item(sndei.term))inList.rev_mapformatlletmake_env_items_revei=letbuf=Buffer.create200inletcmpaa'=lete'=matcha'.env_infowithNone->assertfalse|Somea'->a'inlete=matcha.env_infowithNone->assertfalse|Somea->ainletc=comparee.env_docse'.env_docsinifc<>0thencelsecomparee.env_vare'.env_varinletformata=lete=matcha.env_infowithNone->assertfalse|Somea->ain(e.env_docs,`I(str"$(i,%s)"e.env_var,arg_info_substs~bufae.env_doc))inletis_env_itema=a.env_info<>Noneinletl=List.sortcmp(List.filteris_env_item(sndei.term))inList.rev_mapformatlletmake_cmd_itemsei=matcheval_kindeiwith|`Simple|`M_choice->[]|`M_main->letadd_cmdacc(ti,_)=(ti.tdocs,`I((str"$(b,%s)"ti.name),ti.tdoc))::accinList.sortrev_compare(List.fold_leftadd_cmd[]ei.choices)lettextei=(* man that code is particulary unreadable. *)letrecmerge_itemsaccto_insertmarkil=function|`Ssassec::ts->letacc=List.rev_appendto_insertaccinletacc=ifmarkthensec::`Orphan_mark::accelsesec::accinletto_insert,il=List.partition(fun(n,_)->n=s)ilinletto_insert=List.rev_map(fun(_,i)->i)to_insertinletto_insert=(to_insert:>[`Orphan_mark|Manpage.block]list)inmerge_itemsaccto_insert(s="DESCRIPTION")ilts|t::ts->lett=(t:>[`Orphan_mark|Manpage.block])inmerge_items(t::acc)to_insertmarkilts|[]->letacc=List.rev_appendto_insertaccin(ifmarkthen`Orphan_mark::accelseacc),ilinletrecmerge_orphansaccorphans=function|`Orphan_mark::ts->letrecmergeaccs=function|[]->(`Ss)::acc|(s',i)::ss->leti=(i:>Manpage.block)inifs=s'thenmerge(i::acc)ssselsemerge(i::(`Ss)::acc)s'ssinletacc=matchorphanswith|[]->acc|(s,_)::_->mergeaccsorphansinmerge_orphansacc[]ts|(#Manpage.blockase)::ts->merge_orphans(e::acc)orphansts|[]->accinletcmds=make_cmd_itemseiinletargs=make_arg_itemseiinletenvs_rev=make_env_items_reveiinletitems_rev=List.rev_appendcmds(List.rev_appendargsenvs_rev)inletcmp(s,_)(s',_)=matchs,swith|"ENVIRONMENT VARIABLES",_->1(* Put env vars at the end. *)|s,"ENVIRONMENT VARIABLES"->-1|s,s'->comparess'(* other predefined sec. names order correctly *)inletitems=List.rev(List.stable_sortcmpitems_rev)inletsynopsis,man=get_synopsis_sectioneiinletrev_text,orphans=merge_items[`Orphan_mark][]falseitemsmaninsynopsis@merge_orphans[]orphansrev_textletei_substei=function|"tname"->(fstei.term).name|"mname"->(fstei.main).name|s->str"$(%s)"sletmanei=titleei,(name_sectionei)@(textei)letprintfmtppfei=Manpage.print~subst:(ei_substei)fmtppf(manei)letpr_synopsisppfei=prppf"@[%s@]"(Manpage.escape(ei_substei)Manpage.plain_esc(Buffer.create100)(synopsisei))letpr_versionppfei=match(fstei.main).versionwith|None->assertfalse|Somev->prppf"@[%a@]@."pr_textvend(* Errors for the command line user *)moduleErr=structletinvalidkindsexp=str"invalid %s %s, %s"kind(quotes)expletinvalid_val=invalid"value"letnokinds=str"no %s %s"(quotes)kindletnot_dirs=str"%s is not a directory"(quotes)letis_dirs=str"%s is a directory"(quotes)letelementkindsexp=str"invalid element in %s (`%s'): %s"kindsexpletsep_missseps=invalid_vals(str"missing a `%c' separator"sep)letunknownkind?(hints=[])v=letdid_you_means=str", did you mean %s ?"sinlethints=matchhintswith[]->"."|hs->did_you_mean(alts_strhs)instr"unknown %s %s%s"kind(quotev)hintsletambiguouskindsambs=str"%s %s ambiguous and could be %s"kind(quotes)(alts_strambs)letpos_excessexcess=str"too many arguments, don't know what to do with %s"(String.concat", "(List.mapquoteexcess))letflag_valuefv=str"option %s is a flag, it cannot take the argument %s"(quotef)(quotev)letopt_value_missingf=str"option %s needs an argument"(quotef)letopt_parse_valuefe=str"option %s: %s"(quotef)eletenv_parse_valuevare=str"environment variable %s: %s"(quotevar)eletopt_repeatedff'=iff=f'thenstr"option %s cannot be repeated"(quotef)elsestr"options %s and %s cannot be present at the same time"(quotef)(quotef')letpos_parse_valueae=ifa.docv=""theneelsematcha.p_kindwith|Nth_->str"%s argument: %s"a.docve|_->str"%s... arguments: %s"a.docveletarg_missinga=ifis_optathenletreclong_name=function|n::l->if(String.lengthn)>2||l=[]thennelselong_namel|[]->assertfalseinstr"required option %s is missing"(long_namea.o_names)elseifa.docv=""thenstr"a required argument is missing"elsestr"required argument %s is missing"a.docv(* Error printers *)letprintppfeie=prppf"%s: @[%a@]@."(fstei.main).namepr_texteletpr_backtraceerreiebt=letbt=letlen=String.lengthbtiniflen>0thenString.subbt0(len-1)(* remove final '\n' *)elsebtinprerr"%s: @[internal error, uncaught exception:@\n%a@]@."(fstei.main).namepr_lines(str"%s\n%s"(Printexc.to_stringe)bt)letpr_try_helpppfei=letexec=Help.invocationeiinletmain=(fstei.main).nameinifexec=mainthenprppf"@[<2>Try `%s --help' for more information.@]"execelseprppf"@[<2>Try `%s --help' or `%s --help' for more information.@]"execmainletpr_usageppfeie=prppf"@[<v>%s: @[%a@]@,@[Usage: @[%a@]@]@,%a@]@."(fstei.main).namepr_texteHelp.pr_synopsiseipr_try_helpeiend(* Command lines. A command line stores pre-parsed information about
the command line's arguments in a more structured way. Given the
[arg_info] values mentionned in a term and Sys.argv (whithout exec
name) we parse the command line into a map of [arg_info] values to
[arg] values. This map is used by the term's closures to retrieve
and convert command line arguments (see the Arg module). *)moduleCmdline:sigexceptionErrorofstringvalchoose_term:term_info->(term_info*'a)list->stringlist->term_info*stringlistvalcreate:?peek_opts:bool->arg_infolist->stringlist->cmdlinevalopt_arg:cmdline->arg_info->(int*string*(stringoption))listvalpos_arg:cmdline->arg_info->stringlistend=structexceptionErrorofstringletopt_argcla=matchtryAmap.findaclwithNot_found->assertfalsewithOl->l|_->assertfalseletpos_argcla=matchtryAmap.findaclwithNot_found->assertfalsewithPl->l|_->assertfalseletchoose_termtichoices=function|[]->ti,[]|maybe::args'asargs->ifString.lengthmaybe>1&&maybe.[0]='-'thenti,argselseletindex=letaddacc(choice,_)=Trie.addaccchoice.namechoiceinList.fold_leftaddTrie.emptychoicesinmatchTrie.findindexmaybewith|`Okchoice->choice,args'|`Not_found->letall=Trie.ambiguitiesindex""inlethints=suggestmaybeallinraise(Error(Err.unknown"command"~hintsmaybe))|`Ambiguous->letambs=List.sortcompare(Trie.ambiguitiesindexmaybe)inraise(Error(Err.ambiguous"command"maybeambs))letarg_info_indexesal=(* from [al] 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]. *)letrecauxoptiposicl=function|a::l->ifis_posathenauxopti(a::posi)(Amap.adda(P[])cl)lelseletaddtname=Trie.addtnameainaux(List.fold_leftaddoptia.o_names)posi(Amap.adda(O[])cl)l|[]->opti,posi,clinauxTrie.empty[]Amap.emptyalletparse_opt_args=(* (name,value) of opt arg, assert len > 1. *)letl=String.lengthsinifs.[1]<>'-'thenifl=2thens,NoneelseString.subs02,Some(String.subs2(l-2))elsetryleti=String.indexs'='inString.subs0i,Some(String.subs(i+1)(l-i-1))withNot_found->s,Noneletparse_args~peek_optsopticlargs=(* returns an updated [cl] cmdline according to the options found in [args]
with the trie index [opti]. Positional arguments are returned in order
in a list. *)letrecauxkopticlpargs=function|[]->cl,(List.revpargs)|"--"::args->cl,(List.rev_appendpargsargs)|s::args->letis_opts=String.lengths>1&&s.[0]='-'inletis_short_opts=String.lengths=2&&s.[0]='-'inifnot(is_opts)thenaux(k+1)opticl(s::pargs)argselseletname,value=parse_opt_argsinmatchTrie.findoptinamewith|`Oka->letvalue,args=matchvalue,a.o_kindwith|Somev,Flagwhenis_short_optname->None,("-"^v)::args|Somev,_->value,args|None,Flag->value,args|None,_->matchargswith|v::rest->ifis_optvthenNone,argselseSomev,rest|[]->None,argsinletarg=O((k,name,value)::opt_argcla)inaux(k+1)opti(Amap.addaargcl)pargsargs|`Not_foundwhenpeek_opts->aux(k+1)opticlpargsargs(* skip *)|`Not_found->lethints=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=Trie.ambiguitiesopti"-"inmatchList.memshort_optall,suggestlong_optallwith|false,[]->[]|false,l->l|true,[]->[short_opt]|true,l->ifList.memshort_optlthenlelseshort_opt::linraise(Error(Err.unknown"option"~hintsname))|`Ambiguous->letambs=List.sortcompare(Trie.ambiguitiesoptiname)inraise(Error(Err.ambiguous"option"nameambs))inaux0opticl[]argsletprocess_pos_argsposiclpargs=(* returns an updated [cl] cmdline in which each positional arg mentionned
in the list index posi, is given a value according the list
of positional arguments values [pargs]. *)ifpargs=[]thenclelseletrectakenaccl=ifn=0thenList.revaccelsetake(n-1)(List.hdl::acc)(List.tll)inletrecauxpargslastclmax_spec=function|a::al->letarg,max_spec=matcha.p_kindwith|All->Ppargs,last|Nth(rev,k)->letk=ifrevthenlast-kelsekinletmax_spec=maxkmax_specinifk<0||k>lastthenP[],max_specelseP([List.nthpargsk]),max_spec|Left(rev,k)->letk=ifrevthenlast-kelsekinletmax_spec=maxkmax_specinifk<=0||k>lastthenP[],max_specelseP(takek[]pargs),max_spec|Right(rev,k)->letk=ifrevthenlast-kelsekinifk<0||k>=lastthenP[],lastelseP(List.rev(take(last-k)[](List.revpargs))),lastinauxpargslast(Amap.addaargcl)max_specal|[]->cl,max_specinletlast=List.lengthpargs-1inletcl,max_spec=auxpargslastcl(-1)posiiniflast<=max_specthenclelseletexcess=List.rev(take(last-max_spec)[](List.revpargs))inraise(Error(Err.pos_excessexcess))letcreate?(peek_opts=false)alargs=letopti,posi,cl=arg_info_indexesalinletcl,pargs=parse_args~peek_optsopticlargsinifpeek_optsthencl(* skip positional arguments *)elseprocess_pos_argsposiclpargsendmoduleArg=structtype'aparser=string->[`Okof'a|`Errorofstring]type'aprinter=Format.formatter->'a->unittype'aconverter='aparser*'aprintertypeenv=env_infotype'aarg_converter=(eval_info->cmdline->'a)type'at=arg_infolist*'aarg_convertertypeinfo=arg_infoletenv_var?(docs="ENVIRONMENT VARIABLES")?(doc="See option $(opt).")env_var={env_var=env_var;env_doc=doc;env_docs=docs}let(&)fx=fxletparse_errore=raise(Cmdline.Errore)letsome?(none="")(parse,print)=(funs->matchparseswith`Okv->`Ok(Somev)|`Error_ase->e),(funppfv->matchvwithNone->pr_strppfnone|Somev->printppfv)letinfo?docs?(docv="")?(doc="")?envnames=letdashn=ifString.lengthn=1then"-"^nelse"--"^ninletdocs=matchdocswith|None->ifnames=[]then"ARGUMENTS"else"OPTIONS"|Somes->sin{id=arg_id();absent=Val(lazy"");env_info=env;doc=doc;docv=docv;docs=docs;p_kind=All;o_kind=Flag;o_names=List.rev_mapdashnames;o_all=false;}letenv_bool_parses=matchString.lowercaseswith|""|"false"|"no"|"n"|"0"->`Okfalse|"true"|"yes"|"y"|"1"->`Oktrue|s->`Error(Err.invalid_vals(alts_str["true";"yes";"false";"no"]))letparse_to_listparsers=matchparserswith|`Okv->`Ok[v]|`Error_ase->elettry_enveiaparse~absent=matcha.env_infowith|None->absent|Someenv->matchei.envenv.env_varwith|None->absent|Somev->matchparsevwith|`Okv->v|`Errore->parse_error(Err.env_parse_valueenv.env_vare)letflaga=ifis_posatheninvalid_argerr_not_optelseletconverteicl=matchCmdline.opt_argclawith|[]->try_enveiaenv_bool_parse~absent:false|[_,_,None]->true|[_,f,Somev]->parse_error(Err.flag_valuefv)|(_,f,_)::(_,g,_)::_->parse_error(Err.opt_repeatedfg)in[a],convertletflag_alla=ifis_posatheninvalid_argerr_not_optelseleta={awitho_all=true}inletconverteicl=matchCmdline.opt_argclawith|[]->try_enveia(parse_to_listenv_bool_parse)~absent:[]|l->lettruth(_,f,v)=matchvwith|None->true|Somev->parse_error(Err.flag_valuefv)inList.rev_maptruthlin[a],convertletvflagvl=letconvert_cl=letrecauxfv=function|(v,a)::rest->beginmatchCmdline.opt_argclawith|[]->auxfvrest|[_,f,None]->beginmatchfvwith|None->aux(Some(f,v))rest|Some(g,_)->parse_error(Err.opt_repeatedgf)end|[_,f,Somev]->parse_error(Err.flag_valuefv)|(_,f,_)::(_,g,_)::_->parse_error(Err.opt_repeatedgf)end|[]->matchfvwithNone->v|Some(_,v)->vinauxNonelinletflag(_,a)=ifis_posatheninvalid_argerr_not_optelseainList.rev_mapflagl,convertletvflag_allvl=letconvert_cl=letrecauxacc=function|(fv,a)::rest->beginmatchCmdline.opt_argclawith|[]->auxaccrest|l->letfval(k,f,v)=matchvwith|None->(k,fv)|Somev->parse_error(Err.flag_valuefv)inaux(List.rev_append(List.rev_mapfvall)acc)restend|[]->ifacc=[]thenvelseList.rev_mapsnd(List.sortrev_compareacc)inaux[]linletflag(_,a)=ifis_posatheninvalid_argerr_not_optelse{awitho_all=true}inList.rev_mapflagl,convertletparse_opt_valueparsefv=matchparsevwith|`Okv->v|`Errore->parse_error(Err.opt_parse_valuefe)letopt?vopt(parse,print)va=ifis_posatheninvalid_argerr_not_optelseleta={awithabsent=Val(lazy(str_of_ppprintv));o_kind=matchvoptwith|None->Opt|Somedv->Opt_vopt(str_of_ppprintdv)}inletconverteicl=matchCmdline.opt_argclawith|[]->try_enveiaparse~absent:v|[_,f,Somev]->parse_opt_valueparsefv|[_,f,None]->beginmatchvoptwith|None->parse_error(Err.opt_value_missingf)|Someoptv->optvend|(_,f,_)::(_,g,_)::_->parse_error(Err.opt_repeatedgf)in[a],convertletopt_all?vopt(parse,print)va=ifis_posatheninvalid_argerr_not_optelseleta={awithabsent=Val(lazy"");o_all=true;o_kind=matchvoptwith|None->Opt|Somedv->Opt_vopt(str_of_ppprintdv)}inletconverteicl=matchCmdline.opt_argclawith|[]->try_enveia(parse_to_listparse)~absent:v|l->letparse(k,f,v)=matchvwith|Somev->(k,parse_opt_valueparsefv)|None->matchvoptwith|None->parse_error(Err.opt_value_missingf)|Somedv->(k,dv)inList.rev_mapsnd(List.sortrev_compare(List.rev_mapparsel))in[a],convert(* Positional arguments *)letparse_pos_valueparseav=matchparsevwith|`Okv->v|`Errore->parse_error(Err.pos_parse_valueae)letpos?(rev=false)k(parse,print)va=ifis_optatheninvalid_argerr_not_poselseleta={awithp_kind=Nth(rev,k);absent=Val(lazy(str_of_ppprintv))}inletconverteicl=matchCmdline.pos_argclawith|[]->try_enveiaparse~absent:v|[v]->parse_pos_valueparseav|_->assertfalsein[a],convertletpos_listkind(parse,_)va=ifis_optatheninvalid_argerr_not_poselseleta={awithp_kind=kind}inletconverteicl=matchCmdline.pos_argclawith|[]->try_enveia(parse_to_listparse)~absent:v|l->List.rev(List.rev_map(parse_pos_valueparsea)l)in[a],convertletpos_allcva=pos_listAllcvaletpos_left?(rev=false)k=pos_list(Left(rev,k))letpos_right?(rev=false)k=pos_list(Right(rev,k))(* Arguments as terms *)letabsent_erroral=List.rev_map(funa->{awithabsent=Error})alletvaluea=aletrequired(al,convert)=letal=absent_erroralinletconverteicl=matchconverteiclwith|Somev->v|None->parse_error(Err.arg_missing(List.hdal))inal,convertletnon_empty(al,convert)=letal=absent_erroralinletconverteicl=matchconverteiclwith|[]->parse_error(Err.arg_missing(List.hdal))|l->linal,convertletlast(al,convert)=letconverteicl=matchconverteiclwith|[]->parse_error(Err.arg_missing(List.hdal))|l->List.hd(List.revl)inal,convert(* Predefined converters. *)letbool=(funs->try`Ok(bool_of_strings)withInvalid_argument_->`Error(Err.invalid_vals(alts_str["true";"false"]))),Format.pp_print_boolletchar=(funs->ifString.lengths=1then`Oks.[0]else`Error(Err.invalid_vals"expected a character")),pr_charletparse_witht_of_strexps=try`Ok(t_of_strs)withFailure_->`Error(Err.invalid_valsexp)letint=parse_withint_of_string"expected an integer",Format.pp_print_intletint32=parse_withInt32.of_string"expected a 32-bit integer",(funppf->prppf"%ld")letint64=parse_withInt64.of_string"expected a 64-bit integer",(funppf->prppf"%Ld")letnativeint=parse_withNativeint.of_string"expected a processor-native integer",(funppf->prppf"%nd")letfloat=parse_withfloat_of_string"expected a floating point number",Format.pp_print_floatletstring=(funs->`Oks),pr_strletenumsl=ifsl=[]theninvalid_argerr_empty_listelselett=Trie.of_listslinletparses=matchTrie.findtswith|`Ok_asr->r|`Ambiguous->letambs=List.sortcompare(Trie.ambiguitiests)in`Error(Err.ambiguous"enum value"sambs)|`Not_found->letalts=List.rev(List.rev_map(fun(s,_)->s)sl)in`Error(Err.invalid_vals("expected "^(alts_stralts)))inletprintppfv=letsl_inv=List.rev_map(fun(s,v)->(v,s))slintrypr_strppf(List.assocvsl_inv)withNot_found->invalid_argerr_incomplete_enuminparse,printletfile=(funs->ifSys.file_existssthen`Okselse`Error(Err.no"file or directory"s)),pr_strletdir=(funs->ifSys.file_existssthenifSys.is_directorysthen`Okselse`Error(Err.not_dirs)else`Error(Err.no"directory"s)),pr_strletnon_dir_file=(funs->ifSys.file_existssthenifnot(Sys.is_directorys)then`Okselse`Error(Err.is_dirs)else`Error(Err.no"file"s)),pr_strletsplit_and_parsesepparses=letparsesub=matchparsesubwith|`Errore->failwithe|`Okv->vinletrecsplitaccumj=leti=tryString.rindex_fromsjsepwithNot_found->-1inif(i=-1)thenletp=String.subs0(j+1)inifp<>""thenparsep::accumelseaccumelseletp=String.subs(i+1)(j-i)inletaccum'=ifp<>""thenparsep::accumelseaccuminsplitaccum'(i-1)insplit[](String.lengths-1)letlist?(sep=',')(parse,pr_e)=letparses=try`Ok(split_and_parsesepparses)with|Failuree->`Error(Err.element"list"se)inletrecprintppf=function|v::l->pr_eppfv;if(l<>[])then(pr_charppfsep;printppfl)|[]->()inparse,printletarray?(sep=',')(parse,pr_e)=letparses=try`Ok(Array.of_list(split_and_parsesepparses))with|Failuree->`Error(Err.element"array"se)inletprintppfv=letmax=Array.lengthv-1infori=0tomaxdopr_eppfv.(i);ifi<>maxthenpr_charppfsepdoneinparse,printletsplit_leftseps=tryleti=String.indexssepinletlen=String.lengthsinSome((String.subs0i),(String.subs(i+1)(len-i-1)))withNot_found->Noneletpair?(sep=',')(pa0,pr0)(pa1,pr1)=letparsers=matchsplit_leftsepswith|None->`Error(Err.sep_missseps)|Some(v0,v1)->matchpa0v0,pa1v1with|`Okv0,`Okv1->`Ok(v0,v1)|`Errore,_|_,`Errore->`Error(Err.element"pair"se)inletprinterppf(v0,v1)=prppf"%a%c%a"pr0v0seppr1v1inparser,printerlett2=pairlett3?(sep=',')(pa0,pr0)(pa1,pr1)(pa2,pr2)=letparses=matchsplit_leftsepswith|None->`Error(Err.sep_missseps)|Some(v0,s)->matchsplit_leftsepswith|None->`Error(Err.sep_missseps)|Some(v1,v2)->matchpa0v0,pa1v1,pa2v2with|`Okv0,`Okv1,`Okv2->`Ok(v0,v1,v2)|`Errore,_,_|_,`Errore,_|_,_,`Errore->`Error(Err.element"triple"se)inletprintppf(v0,v1,v2)=prppf"%a%c%a%c%a"pr0v0seppr1v1seppr2v2inparse,printlett4?(sep=',')(pa0,pr0)(pa1,pr1)(pa2,pr2)(pa3,pr3)=letparses=matchsplit_leftsepswith|None->`Error(Err.sep_missseps)|Some(v0,s)->matchsplit_leftsepswith|None->`Error(Err.sep_missseps)|Some(v1,s)->matchsplit_leftsepswith|None->`Error(Err.sep_missseps)|Some(v2,v3)->matchpa0v0,pa1v1,pa2v2,pa3v3with|`Okv1,`Okv2,`Okv3,`Okv4->`Ok(v1,v2,v3,v4)|`Errore,_,_,_|_,`Errore,_,_|_,_,`Errore,_|_,_,_,`Errore->`Error(Err.element"quadruple"se)inletprintppf(v0,v1,v2,v3)=prppf"%a%c%a%c%a%c%a"pr0v0seppr1v1seppr2v2seppr3v3inparse,print(* Documentation formatting helpers *)letdoc_quote=quoteletdoc_alts=alts_strletdoc_alts_enum?quotedenum=alts_str?quoted(List.mapfstenum)endmoduleTerm=structtypeinfo=term_infotype+'at=arg_infolist*(eval_info->cmdline->'a)type'aresult=[|`Okof'a|`Errorof[`Parse|`Term|`Exn]|`Version|`Help]exceptionTermof[`Helpof[`Pager|`Plain|`Groff]*stringoption|`Errorofbool*string]letinfo?(sdocs="OPTIONS")?(man=[])?(docs="COMMANDS")?(doc="")?versionname={name=name;version=version;tdoc=doc;tdocs=docs;sdocs=sdocs;man=man}letnameti=ti.nameletconstv=[],(fun__->v)letpure(* deprecated *)=constletapp(al,f)(al',v)=List.rev_appendalal',funeicl->(feicl)(veicl)let($)=apptype'aret=[`Helpof[`Pager|`Plain|`Groff]*stringoption|`Errorof(bool*string)|`Okof'a]letret(al,v)=al,funeicl->matchveiclwith|`Okv->v|`Error(u,e)->raise(Term(`Error(u,e)))|`Helph->raise(Term(`Helph))letmain_name=[],(funei_->(fstei.main).name)letchoice_names=[],funei_->List.rev_map(fune->(fste).name)ei.choicesletman_format=letfmts=["pager",`Pager;"groff",`Groff;"plain",`Plain]inletdoc="Show output in format $(docv) (pager, plain or groff)."inArg.(value&opt(enumfmts)`Pager&info["man-format"]~docv:"FMT"~doc)(* Evaluation *)letremove_execargv=tryList.tl(Array.to_listargv)withFailure_->invalid_argerr_argvletadd_std_optsei=letdocs=(fstei.term).sdocsinletargs,v_lookup=if(fstei.main).version=Nonethen[],Noneelselet(a,lookup)=Arg.flag(Arg.info["version"]~docs~doc:"Show version information.")ina,Somelookupinletargs,h_lookup=let(a,lookup)=letfmt=Arg.enum["pager",`Pager;"groff",`Groff;"plain",`Plain]inletdoc="Show this help in format $(docv) (pager, plain or groff)."inleta=Arg.info["help"]~docv:"FMT"~docs~docinArg.opt~vopt:(Some`Pager)(Arg.somefmt)NoneainList.rev_appendaargs,lookupinh_lookup,v_lookup,{eiwithterm=(fstei.term),List.rev_appendargs(sndei.term)}leteval_termhelperreifargs=lethelp_arg,vers_arg,ei=add_std_optseiintryletcl=Cmdline.create(sndei.term)argsinmatchhelp_argeicl,vers_argwith|Somefmt,_->Help.printfmthelpei;`Help|None,Somev_argwhenv_argeicl->Help.pr_versionhelpei;`Version|_->`Ok(feicl)with|Cmdline.Errore->Err.pr_usageerreie;`Error`Parse|Term(`Error(usage,e))->ifusagethenErr.pr_usageerreieelseErr.printerreie;`Error`Term|Term(`Help(fmt,cmd))->letei=matchcmdwith|Somecmd->letcmd=tryList.find(fun(i,_)->i.name=cmd)ei.choiceswithNot_found->invalid_arg(err_helpcmd)in{eiwithterm=cmd}|None->{eiwithterm=ei.main}inlet_,_,ei=add_std_optseiinHelp.printfmthelpei;`Helpletenv_defaultv=trySome(Sys.getenvv)withNot_found->Noneleteval?(help=Format.std_formatter)?(err=Format.err_formatter)?(catch=true)?(env=env_default)?(argv=Sys.argv)((al,f),ti)=letterm=ti,alinletei={term=term;main=term;choices=[];env=env}intryeval_termhelperreif(remove_execargv)with|ewhencatch->Err.pr_backtraceerreie(Printexc.get_backtrace());`Error`Exnleteval_choice?(help=Format.std_formatter)?(err=Format.err_formatter)?(catch=true)?(env=env_default)?(argv=Sys.argv)(((al,f)ast),ti)choices=letei_choices=List.rev_map(fun((al,_),ti)->ti,al)choicesinletmain=(ti,al)inletei={term=main;main=main;choices=ei_choices;env=env}intryletchosen,args=Cmdline.choose_termtiei_choices(remove_execargv)inletfind_chosen(_,ti)=ti=choseninlet(al,f),_=List.findfind_chosen((t,ti)::choices)inletei={eiwithterm=(chosen,al)}ineval_termhelperreifargswith|Cmdline.Errore->(* may be raised by choose_term. *)Err.pr_usageerreie;`Error`Parse|ewhencatch->Err.pr_backtraceerreie(Printexc.get_backtrace());`Error`Exnleteval_peek_opts?(version_opt=false)?(env=env_default)?(argv=Sys.argv)(al,f)=letargs=remove_execargvinletversion=ifversion_optthenSome"dummy"elseNoneinletterm=info?version"dummy",alinletei={term=term;main=term;choices=[];env=env}inlethelp_arg,vers_arg,ei=add_std_optseiintryletcl=Cmdline.create~peek_opts:true(sndei.term)argsinmatchhelp_argeicl,vers_argwith|Somefmt,_->(try(Some(feicl),`Help)withe->None,`Help)|None,Somev_argwhenv_argeicl->(try(Some(feicl),`Version)withe->None,`Version)|_->letv=feiclinSomev,`Okvwith|Cmdline.Error_->None,(`Error`Parse)|Term_->None,(`Error`Term)|e->None,(`Error`Exn)end(*---------------------------------------------------------------------------
Copyright (c) 2011 Daniel C. Bünzli
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
3. Neither the name of Daniel C. Bünzli nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*)